Files
flyradar/src/settings_store.cpp
Eiswolf-BG 7137e50d66 v2.7.3: Flight logbook safety net, radar/clock/timezone fixes
New:
- Flight logbook safety net: disabled by default, requires confirming
  a full-screen warning before it can be turned on, and automatically
  switches off again after exactly 24 hours - survives restarts and
  power cycles (no valid enable timestamp = guaranteed off). Each
  activation now writes its own CSV file instead of endlessly
  appending to one growing file, and every file can be deleted
  individually from Logbook files.
- Adjustable display brightness (10-100% in 10% steps, Menu -> System
  -> "Brightness") with a live preview.
- About screen (Menu -> System -> "About") showing project name,
  description, and firmware version.

Fixed:
- Radar's red altitude band (>9100m/30000ft) no longer gets dimmed at
  night - the previous dim tone looked orange-brown and was hard to
  tell apart from yellow. Legend and markers are consistent again.
- Radius change no longer leaves a stale "tail" trail behind each
  aircraft between the old and new position.
- Splash screen no longer shows a time-of-day greeting that appeared
  too late in the boot sequence to be useful; the star animation now
  starts immediately instead of only near the end of boot.
- Clock stayed on raw UTC after the first boot instead of local time
  (missing DST handling in some timezones) because the location wait
  loop skipped the timezone lookup once a location was already saved.
- Header clock no longer leaves stale digit fragments behind the
  current time, most visible on the last minute digit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 14:02:01 +02:00

247 lines
7.4 KiB
C++

#include "settings_store.h"
#include "config.h"
#include "sd_mutex.h"
#include <SD.h>
#include <cstring>
namespace SettingsStore {
namespace {
uint8_t rangeIdx = Config::DEFAULT_RANGE_INDEX;
bool inverted = true; // Dieses Board braucht invertDisplay(true) fuer korrekte Farben (siehe main.cpp)
uint8_t brightnessPct = Config::BRIGHTNESS_MAX_PERCENT;
bool emergencyAlertOn = true;
bool proximityAlertOn = true;
bool watchlistAlertOn = true;
// MUSS bei einer frischen Installation aus sein - sonst schreibt sich
// die SD-Karte unbemerkt voll (siehe Bestaetigungsdialog beim
// Einschalten in menu_screen.cpp + 24h-Auto-Aus in flight_logbook.cpp).
bool flightLogbookOn = false;
uint32_t logbookEnabledAtEpoch = 0;
char logbookSessionFile[16] = {0};
bool ledHeartbeatOn = true;
uint8_t screenTimeoutMin = 0;
bool nightDimmingOn = true;
bool hideGroundVehiclesOn = true;
uint8_t languageIdx = 0;
uint8_t unitsModeVal = 0;
void applyKeyValue(const String& key, const String& value) {
if (key == "range_index") {
int v = value.toInt();
if (v >= 0 && v < Config::RANGE_STEP_COUNT) {
rangeIdx = (uint8_t)v;
}
} else if (key == "invert") {
inverted = (value.toInt() != 0);
} else if (key == "brightness_percent") {
int v = value.toInt();
if (v >= Config::BRIGHTNESS_MIN_PERCENT && v <= Config::BRIGHTNESS_MAX_PERCENT) {
brightnessPct = (uint8_t)v;
}
} else if (key == "emergency_alert") {
emergencyAlertOn = (value.toInt() != 0);
} else if (key == "proximity_alert") {
proximityAlertOn = (value.toInt() != 0);
} else if (key == "watchlist_alert") {
watchlistAlertOn = (value.toInt() != 0);
} else if (key == "flight_logbook") {
flightLogbookOn = (value.toInt() != 0);
} else if (key == "logbook_enabled_at") {
logbookEnabledAtEpoch = (uint32_t)value.toInt();
} else if (key == "logbook_session_file") {
strncpy(logbookSessionFile, value.c_str(), sizeof(logbookSessionFile) - 1);
logbookSessionFile[sizeof(logbookSessionFile) - 1] = 0;
} else if (key == "led_heartbeat") {
ledHeartbeatOn = (value.toInt() != 0);
} else if (key == "screen_timeout_min") {
int v = value.toInt();
if (v >= 0 && v <= 10) screenTimeoutMin = (uint8_t)v;
} else if (key == "night_dimming") {
nightDimmingOn = (value.toInt() != 0);
} else if (key == "hide_ground_vehicles") {
hideGroundVehiclesOn = (value.toInt() != 0);
} else if (key == "language") {
int v = value.toInt();
if (v >= 0 && v <= 5) languageIdx = (uint8_t)v;
} else if (key == "units_mode") {
int v = value.toInt();
if (v >= 0 && v <= 2) unitsModeVal = (uint8_t)v;
}
}
}
void load() {
SdMutex::Guard guard;
if (!SD.exists(Config::SD_SETTINGS_FILE)) return;
File f = SD.open(Config::SD_SETTINGS_FILE, FILE_READ);
if (!f) return;
while (f.available()) {
String line = f.readStringUntil('\n');
line.trim();
if (line.length() == 0 || line.startsWith("#")) continue;
int eq = line.indexOf('=');
if (eq < 0) continue;
String key = line.substring(0, eq);
String value = line.substring(eq + 1);
key.trim();
value.trim();
applyKeyValue(key, value);
}
f.close();
// Sicherheitsregel: das Flugbuch darf nach einem Neustart nur dann
// aktiv bleiben, wenn auch ein gueltiger Einschalt-Zeitstempel
// vorhanden ist - kein Zeitstempel bedeutet garantiert AUS, egal was in
// "flight_logbook" steht (verhindert unbemerktes Weiterlaufen z.B. nach
// einem Firmware-Update oder einer manuell bearbeiteten Datei).
if (flightLogbookOn && logbookEnabledAtEpoch == 0) {
flightLogbookOn = false;
}
}
void save() {
SdMutex::Guard guard;
File f = SD.open(Config::SD_SETTINGS_FILE, FILE_WRITE);
if (!f) return;
f.printf("range_index=%d\n", rangeIdx);
f.printf("invert=%d\n", inverted ? 1 : 0);
f.printf("brightness_percent=%d\n", brightnessPct);
f.printf("emergency_alert=%d\n", emergencyAlertOn ? 1 : 0);
f.printf("proximity_alert=%d\n", proximityAlertOn ? 1 : 0);
f.printf("watchlist_alert=%d\n", watchlistAlertOn ? 1 : 0);
f.printf("flight_logbook=%d\n", flightLogbookOn ? 1 : 0);
f.printf("logbook_enabled_at=%lu\n", (unsigned long)logbookEnabledAtEpoch);
f.printf("logbook_session_file=%s\n", logbookSessionFile);
f.printf("led_heartbeat=%d\n", ledHeartbeatOn ? 1 : 0);
f.printf("screen_timeout_min=%d\n", screenTimeoutMin);
f.printf("night_dimming=%d\n", nightDimmingOn ? 1 : 0);
f.printf("hide_ground_vehicles=%d\n", hideGroundVehiclesOn ? 1 : 0);
f.printf("language=%d\n", languageIdx);
f.printf("units_mode=%d\n", unitsModeVal);
f.close();
}
uint8_t rangeIndex() { return rangeIdx; }
void setRangeIndex(uint8_t idx) {
if (idx < Config::RANGE_STEP_COUNT) {
rangeIdx = idx;
save();
}
}
bool displayInverted() { return inverted; }
void setDisplayInverted(bool inv) {
inverted = inv;
save();
}
uint8_t brightnessPercent() { return brightnessPct; }
void setBrightnessPercent(uint8_t percent) {
if (percent >= Config::BRIGHTNESS_MIN_PERCENT && percent <= Config::BRIGHTNESS_MAX_PERCENT) {
brightnessPct = percent;
save();
}
}
bool emergencyAlertEnabled() { return emergencyAlertOn; }
void setEmergencyAlertEnabled(bool on) {
emergencyAlertOn = on;
save();
}
bool proximityAlertEnabled() { return proximityAlertOn; }
void setProximityAlertEnabled(bool on) {
proximityAlertOn = on;
save();
}
bool watchlistAlertEnabled() { return watchlistAlertOn; }
void setWatchlistAlertEnabled(bool on) {
watchlistAlertOn = on;
save();
}
bool flightLogbookEnabled() { return flightLogbookOn; }
void setFlightLogbookEnabled(bool on) {
flightLogbookOn = on;
save();
}
uint32_t flightLogbookEnabledAtEpoch() { return logbookEnabledAtEpoch; }
void setFlightLogbookEnabledAtEpoch(uint32_t epoch) {
logbookEnabledAtEpoch = epoch;
save();
}
String flightLogbookSessionFile() { return String(logbookSessionFile); }
void setFlightLogbookSessionFile(const String& label) {
strncpy(logbookSessionFile, label.c_str(), sizeof(logbookSessionFile) - 1);
logbookSessionFile[sizeof(logbookSessionFile) - 1] = 0;
save();
}
bool ledHeartbeatEnabled() { return ledHeartbeatOn; }
void setLedHeartbeatEnabled(bool on) {
ledHeartbeatOn = on;
save();
}
uint8_t screenTimeoutMinutes() { return screenTimeoutMin; }
void setScreenTimeoutMinutes(uint8_t minutes) {
if (minutes <= 10) {
screenTimeoutMin = minutes;
save();
}
}
bool nightDimmingEnabled() { return nightDimmingOn; }
void setNightDimmingEnabled(bool on) {
nightDimmingOn = on;
save();
}
bool hideGroundVehicles() { return hideGroundVehiclesOn; }
void setHideGroundVehicles(bool on) {
hideGroundVehiclesOn = on;
save();
}
uint8_t language() { return languageIdx; }
void setLanguage(uint8_t lang) {
if (lang <= 5) {
languageIdx = lang;
save();
}
}
uint8_t unitsMode() { return unitsModeVal; }
void setUnitsMode(uint8_t mode) {
if (mode <= 2) {
unitsModeVal = mode;
save();
}
}
}