refactor(companion): shared shutdown steps, SD channel key by secret, fetch race, small UI fixes

- UITaskBase holds what both UIs' shutdown() and low-battery check share:
  the saves before the goodbye sound, the power-down (GPS through its
  provider) and the battery EMA and threshold.
- L2 SD archive: a channel's file is keyed by its secret only, so renaming
  keeps the history; old files are renamed once at boot.
- L2 fetch: abandoning a request and the task finishing it are one critical
  section (it could stay DONE for good); TileCache gets the read-side barrier.
- Text from a repeater or sensor no longer rings on the device (nothing to
  open); a settings switch reads any non-zero value as on; the message
  toast never cuts a letter in half; ui-new's GPS helpers ask UiCore.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-10-12 18:08:31 +02:00
co-authored by Claude Opus 5.5
parent 28d8291e33
commit 01a068be7d
8 changed files with 82 additions and 65 deletions
+31
View File
@@ -47,6 +47,37 @@ protected:
// of its loop() (MyMesh::loop() used to push it at the end of its own).
void pollConnection() { setHasConnection(_serial->isBLEConnected()); }
// ── The parts of shutdown() and the low-battery check every UI shares ──
// Before the goodbye sound: whatever a screen not yet left still holds in
// NodePrefs (they save on exit), the clock, the contacts' lazy write.
static void saveBeforeShutdown() {
the_mesh.savePrefs();
the_mesh.saveRTCTime();
the_mesh.flushDirtyContacts();
}
// After it: reboot, or the display, radio and GPS off and the board down.
// The GPS goes through its provider: pins keep their state in nRF52
// SYSTEMOFF, so the module would go on draining the battery.
void powerDown(bool restart, DisplayDriver* display, SensorManager* sensors) {
if (restart) { _board->reboot(); return; }
if (display) display->turnOff();
radio_driver.powerOff();
if (sensors) {
LocationProvider* loc = sensors->getLocationProvider();
if (loc) loc->stop();
}
_board->powerOff();
}
// Settings > Power > Battery shutdown: `mv` smoothed with battEma() every
// 8 s, under the threshold and not on USB power (charging would loop).
static uint16_t battEma(uint16_t prev, uint16_t raw) {
return prev == 0 ? raw : (uint16_t)((prev * 4u + raw) / 5u); // alpha 0.2: the ADC jitters under load
}
bool lowBatteryShutdownDue(uint16_t mv, const NodePrefs* prefs) const {
uint16_t low = prefs ? prefs->low_batt_mv : 0;
return low > 0 && mv > 0 && mv < low && !_board->isExternalPowered();
}
public:
void setHasConnection(bool connected) {
bool prev = _connected;