Make air-traffic refresh interval configurable, resolved from plan

Add a refresh-interval control to the Map settings popover: Auto (follows
the OpenSky plan) or a fixed 15/30/60/120s. The states endpoint now
resolves a recommended interval from the resolved plan (anonymous 60s,
standard 30s, contributor 15s) and returns it alongside the aircraft, so
"Auto" tracks the plan's credit budget and update rate. Polling reschedules
live when the effective interval changes; the map caption reflects it. New
airTrafficInterval preference (default 'auto', persisted + synced).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
tajniak81
2026-07-13 22:58:19 +02:00
co-authored by Claude Opus 4.8
parent 2509fb9c07
commit 125d0ccc7d
9 changed files with 116 additions and 38 deletions
+28 -2
View File
@@ -564,8 +564,15 @@ func (s *Server) handleOpenSkyStates(w http.ResponseWriter, r *http.Request) {
who := callerFromRecord(rec)
res := s.resolveOpenSky(r.Context(), who, rec.Record["pluginSettings"])
// The plan drives the recommended refresh cadence (its daily credit budget and
// upstream update rate). Surfaced on every response so the "Auto" interval in the
// Map settings popover resolves even before the overlay is enabled.
plan := res.eff.Plan
recInterval := recommendedOpenSkyInterval(plan)
disabled := func(detail string) {
writeJSON(w, http.StatusOK, map[string]any{"states": []osAircraft{}, "unavailable": true, "detail": detail})
writeJSON(w, http.StatusOK, map[string]any{
"states": []osAircraft{}, "unavailable": true, "detail": detail,
"plan": plan, "recommendedInterval": recInterval})
}
switch {
case !res.available:
@@ -630,7 +637,26 @@ func (s *Server) handleOpenSkyStates(w http.ResponseWriter, r *http.Request) {
}
aircraft = append(aircraft, a)
}
writeJSON(w, http.StatusOK, map[string]any{"time": osResp.Time, "states": aircraft})
writeJSON(w, http.StatusOK, map[string]any{
"time": osResp.Time, "states": aircraft,
"plan": plan, "recommendedInterval": recInterval})
}
// recommendedOpenSkyInterval maps a resolved plan to a sensible default poll
// interval (seconds) for the Live map. It balances the plan's daily credit budget
// (anonymous 400 · standard 4000 · contributor 8000) against how often OpenSky
// actually refreshes state vectors (~10s anonymous, ~5s authenticated), so the map
// stays fresh without draining credits. An unset plan resolves as standard (the
// runtime default). See planDailyCredits in the opensky plugin.
func recommendedOpenSkyInterval(plan string) int {
switch plan {
case "anonymous":
return 60
case "contributor":
return 15
default: // standard or unset
return 30
}
}
// rawFloat reads element i of an OpenSky state array as a float, reporting ok=false