Add offline and ntfy alerts

Co-authored-by: Copilot <[email protected]>
This commit is contained in:
Mathieu Moran
2026-08-09 20:28:42 -04:00
co-authored by Copilot
parent 8b664667e5
commit 96332adab6
12 changed files with 1098 additions and 21 deletions
+43 -15
View File
@@ -1009,21 +1009,49 @@ the remaining readings. The endpoint exposes structured current and historical f
without overflow or new console errors. Also fixed disconnected OBD speed showing `undefined`
and deferred offline map tiles until a real GPS fix, avoiding irrelevant placeholder requests.
### Phase 20: Alerts/notifications — 🚧 PLANNED
**New phase - depends on Phases 13/17/19 existing first**, since it needs real data sources to
alert on. Right now, finding out about a problem (check-engine light on, dashcam SD card nearly
full, upload backlog growing unbounded, Pi overheating, GPS fix lost for a long stretch) requires
manually opening the web UI. Add a lightweight push-notification integration - no new
infrastructure needed, just an outbound webhook to something like `ntfy.sh` or a Telegram bot -
fired on a handful of conditions:
- DTC/MIL status changes to "on" (from Phase 13's expanded OBD data).
- Dashcam USB storage or local `dash/videos/` disk usage crossing a configurable threshold.
- Upload backlog (pending tracking rows or dashcam files) growing past a configurable size,
suggesting the Pi's been offline/uploading too slowly for a while.
- Pi CPU temperature crossing a configurable threshold (from Phase 19).
- GPS fix lost for longer than a configurable duration while the car appears to be moving
(OBD speed > 0 with no fix - needs Phase 13's data).
Webhook target/thresholds configurable via the Phase 21 Settings page rather than hardcoded.
### Phase 20: Alerts/notifications — ✅ DONE
Added an offline-first notification system. A shared bell in the top bar on every web page shows
active local alerts with no modem, SIM card, or internet connection. Optional ntfy delivery mirrors
the same conditions only when explicitly enabled and configured; it defaults off until Phase 21
exposes its topic URL, optional access token, and thresholds.
Monitored conditions:
- MIL/check-engine state and DTC codes.
- Pi filesystem pressure and the physical dashcam USB filesystem, checked read-only and cached for
five minutes to avoid repeated mounts.
- Pending tracking-row or local dashcam-video upload backlog.
- Pi CPU temperature and Raspberry Pi firmware undervoltage/throttling flags.
- GPS fix loss for a configured duration while fresh OBD wheel speed confirms movement.
The alert engine runs independently every minute and persists active state, first-active time,
last attempt/success, delivery errors, recovery state, and last value in SQLite. It sends once on a
transition, repeats only after the configured cooldown, retries failed deliveries after five
minutes, and sends a recovery notification after a delivered condition clears. GPS cold starts
must remain lost for the full duration before alerting, and stale MIL/DTC values are suppressed
after OBD communication stops. Firmware current flags alert immediately; new historical bits
remain visible for five minutes, and cleared bits are reconciled so the same fault can alert again
after reboot. Failed transient deliveries retry every minute during that window. The bell
explicitly reports a dead/stale monitor instead of silently showing old conditions.
`/api/alerts/status` exposes sanitized diagnostics without the ntfy URL, token, or request errors
that could contain a private topic URL.
**Implemented and verified 2026-08-09:**
- Thirteen focused alert tests cover ntfy request shape/authentication/error redaction, transition
deduplication, cooldown repeats, recovery, failed-delivery retry, disabled delivery, GPS
duration/reset, firmware-event recurrence, diagnostic freshness, and dashcam-usage caching. The
combined targeted suite is 33 tests.
- Deployed uncommitted code and verified the alert thread, state table, status API, and service on
the Pi. `enabled=false` and `configured=false` produced no external attempt while local alerts
continued working.
- Tested the ntfy transport against a temporary HTTP receiver bound only to `127.0.0.1` on the Pi:
path, UTF-8 body, title, priority, tags, and bearer Authorization header all arrived correctly;
no third-party request or real token was used.
- The real read-only physical-dashcam check found 96.1% usage/1.23GB free, unmounted afterward, and
the original library remained exactly 87 files/29,615,111,758 bytes. The live bell correctly
showed that storage alert alongside the real Pi undervoltage/throttling alert.
- Browser-tested the real local bell/dropdown at 390x844 on the dashboard and trip-history page:
badge counts, alert titles/details, no-alert state, synthetic monitor-failure state, shared-shell
availability, and no-horizontal-overflow behavior passed with no new console errors.
### Phase 21: Settings page — 🚧 PLANNED
**New phase - depends on Phase 15's shared components, and benefits from Phases 17/20 existing**