Rework Phases 12-17 for efficiency, add QOL phases (power resilience, watchdog, trip history, Pi health, alerts, settings)

Reordered/renumbered the roadmap based on dependency analysis:
- Phase 12 (remove relay) stays first - pure cleanup, no deps.
- New Phase 13: power-loss resilience + hardware watchdog - OS-level,
  independent, should land before the Pi lives in the car full-time
  (cars cut power abruptly at every key-off, a real SD-corruption risk
  nothing in the project currently addresses).
- New Phase 14: UI design-system foundation - pulls forward a small
  slice of the old end-of-roadmap redesign (shared layout + reusable
  list/table component) so the dashcam/video browsers, trip history,
  and settings pages are all built on it once instead of being built
  in the old template and then rebuilt during the redesign later.
- Phases 15/16 (dashcam + local video browsers) now explicitly build
  on Phase 14's foundation. Folded two more QOL ideas directly into
  Phase 16: ffmpeg thumbnails per video, and an optional
  upload-confirmed local retention/auto-cleanup policy.
- Phase 17 (OBD hardening) explicitly marked independent/parallel-
  frins to other phases - backend-only work, no UI dependency.
- New Phase 18: trip history + map replay (trips table + GPS-gap
  boundary detection + list/replay UI reusing the offline Leaflet map).
- New Phase 19: Pi health monitoring card (CPU temp/disk/uptime/RAM).
- New Phase 20: alerts/notifications via a simple outbound webhook
  (ntfy.sh/Telegram) for DTC-on, storage full, backlog growing,
  overheating, lost GPS fix.
- New Phase 21: consolidated Settings page (upload toggle, cellular
  APN, retention policy, alert config) - reduces routine SSH-editing.
- Phase 22: renamed 'Finish web UI redesign' - now scoped as the
  remaining cleanup after Phase 14's foundation + all functional
  phases land, rather than one big redesign at the very end.
- Phase 23: native Android app, unchanged content, still last/low
  priority per explicit user instruction.

Updated the session's todo tracking table to match the new phase
IDs/order and added todo_deps reflecting the same dependency chain.

Co-authored-by: Copilot <[email protected]>
This commit is contained in:
Mathieu Moran
2026-08-09 16:57:56 -04:00
co-authored by Copilot
parent 5877f16c9a
commit 0b729adb28
+127 -34
View File
@@ -661,9 +661,41 @@ not fixable). Remove:
avoid rehashing its 20GB+ backlog every loop), `DASHCAM_CHUNK_SIZE`/Cloudflare 100MB avoid rehashing its 20GB+ backlog every loop), `DASHCAM_CHUNK_SIZE`/Cloudflare 100MB
workaround - none of that is relay-specific. workaround - none of that is relay-specific.
### Phase 13: Dashcam USB file browser (list + delete) — 🚧 PLANNED ### Phase 13: Power-loss resilience & watchdog — 🚧 PLANNED
**New phase, not code-dependent on anything else — OS/hardware-level, can be done anytime,
ideally before the Pi lives in the car full-time.** A car cuts power abruptly at every key-off;
nothing in the project currently protects against that, and repeated unclean power loss is a
classic cause of SD card corruption on embedded Pi projects. Two complementary angles:
- **Filesystem hardening**: convert the Pi's root filesystem to read-only with a writable overlay
(`overlayroot`/`raspi-config`'s built-in overlay FS option), so an abrupt power cut can no longer
corrupt the OS itself. App data that must persist (`db/sql/`, `dash/videos/`, logs) needs to
live on an explicitly writable partition/bind-mount carved out of the overlay, since those
still need real writes.
- **Clean shutdown before power loss**: either (a) a small delayed-shutdown power module/circuit
(holds 5V for a few seconds after ignition-off so the Pi can run `shutdown -h now` cleanly), or
(b) if the SIM868 HAT or another ADC/GPIO source can sense ignition/voltage state, a software
voltage-drop watcher that triggers a clean shutdown proactively. Exact approach TBD - depends
on what's easiest to wire into the existing power setup.
- **Hardware watchdog**: enable the Pi's built-in `bcm2835_wdt` watchdog (via `systemd`'s
`RuntimeWatchdogSec`/`watchdog-device` support) so a fully hung app or kernel gets a hard reboot
automatically instead of sitting dead in the car until someone notices.
### Phase 14: UI design-system foundation — 🚧 PLANNED
**New phase, pulled forward from the old "do the whole redesign at the end" plan.** Building the
Phase 15/16 file browsers (and later the trip-history/Pi-health/settings pages) against the
current old admin-template style just means rebuilding the same UI twice once the full redesign
(Phase 22) happens. Instead, do a *small, scoped* slice of the redesign right now: a shared page
layout/shell plus one reusable list/table component (sortable columns, per-row action buttons,
delete-with-confirmation pattern, empty-state messaging) that every subsequent list-based feature
in this plan (dashcam browser, video browser, trip history, settings) can be built on directly,
instead of writing bespoke list/delete UI code from scratch each time. Not a full redesign of
every existing page yet - just establishes the pattern once so nothing built after this point
needs to be redone in Phase 22.
### Phase 15: Dashcam USB file browser (list + delete) — 🚧 PLANNED
Replace the current all-or-nothing "Import videos" flow's blind spot (no visibility into what's Replace the current all-or-nothing "Import videos" flow's blind spot (no visibility into what's
actually on the camera) with a real file browser for the mounted USB dashcam device: actually on the camera) with a real file browser for the mounted USB dashcam device, built on the
Phase 14 shared list component:
- New endpoint(s) in `modules/app_dash.py` to list every video file on the mounted dashcam - New endpoint(s) in `modules/app_dash.py` to list every video file on the mounted dashcam
(relative path, size, modified time) without copying anything - reuses the existing (relative path, size, modified time) without copying anything - reuses the existing
`find_video_files()` walk, pointed at the dashcam's mount point instead of the local `find_video_files()` walk, pointed at the dashcam's mount point instead of the local
@@ -676,13 +708,13 @@ actually on the camera) with a real file browser for the mounted USB dashcam dev
list with a delete button per row, a "Delete all" button behind a confirmation dialog (real list with a delete button per row, a "Delete all" button behind a confirmation dialog (real
data loss risk - no undo). data loss risk - no undo).
- Explicitly deferred/out of scope: no in-browser preview/playback of files still on the - Explicitly deferred/out of scope: no in-browser preview/playback of files still on the
dashcam itself (only after import - see Phase 14). No changes to the USB dashcam itself (only after import - see Phase 16). No changes to the USB
select-mode/data-communication toggle behavior - the user replugs power manually to return the select-mode/data-communication toggle behavior - the user replugs power manually to return the
camera to recording mode, as they already do. camera to recording mode, as they already do.
### Phase 14: Local imported-video browser + player — 🚧 PLANNED ### Phase 16: Local imported-video browser + player — 🚧 PLANNED
Same file-browser treatment for videos already imported to the Pi's own disk Same file-browser treatment for videos already imported to the Pi's own disk
(`dash/videos/`, populated by `import_dashcam()`): (`dash/videos/`, populated by `import_dashcam()`), built on the Phase 14 shared list component:
- List endpoint (reuses `find_video_files()` against `app_dash.DEFAULT_DESTINATION`) showing - List endpoint (reuses `find_video_files()` against `app_dash.DEFAULT_DESTINATION`) showing
relative path, size, modified time, and upload status (cross-check against relative path, size, modified time, and upload status (cross-check against
`app_sql.dashcam_file_marked_uploaded()` - so it's visible at a glance which imported clips `app_sql.dashcam_file_marked_uploaded()` - so it's visible at a glance which imported clips
@@ -698,8 +730,18 @@ Same file-browser treatment for videos already imported to the Pi's own disk
- UI: a `/dash` card (or a dedicated sub-page if the list gets long) with a file list, a - UI: a `/dash` card (or a dedicated sub-page if the list gets long) with a file list, a
"Play" action that opens the video inline, and a delete button per row with a confirmation "Play" action that opens the video inline, and a delete button per row with a confirmation
prompt. prompt.
- **QOL: video thumbnails.** Scrolling a list of `VID_00234.MOV`-style filenames to find a
specific clip is rough. Grab one low-res frame per file via `ffmpeg` (cheap, done once per file
and cached, not on every page load) so the browser shows an actual thumbnail per row instead of
just a filename.
- **QOL: retention/auto-cleanup policy.** Once a file is confirmed uploaded to the remote server,
it's safe to free local space. Add an optional automatic cleanup (configurable via the Phase 21
Settings page - e.g. "delete local copies older than N days once uploaded", off by default)
so the Pi's limited storage doesn't need to be babysat manually as the dashcam backlog grows.
### Phase 15: OBD real-hardware readiness (code hardening) — 🚧 PLANNED ### Phase 17: OBD real-hardware readiness (code hardening) — 🚧 PLANNED
**Independent of everything else in this plan — only touches `app_obd.py`/`app_events.py`, no UI
dependency for the backend part, so this can be done anytime/in parallel with the phases above.**
Nothing has been installed or tested in the actual 2013 Yaris yet - `modules/app_obd.py` has Nothing has been installed or tested in the actual 2013 Yaris yet - `modules/app_obd.py` has
only ever been tested standalone on the Pi with nothing plugged into the OBD-II port (correctly only ever been tested standalone on the Pi with nothing plugged into the OBD-II port (correctly
reports "no connection to car"). Per explicit user decision, this phase is **code hardening reports "no connection to car"). Per explicit user decision, this phase is **code hardening
@@ -729,53 +771,104 @@ all in `modules/app_obd.py` / `modules/app_events.py`:
- **Add a small connect-time diagnostic log** (ELM327 firmware version + negotiated protocol, - **Add a small connect-time diagnostic log** (ELM327 firmware version + negotiated protocol,
both available from `python-obd`'s connection object) written once per successful connect - both available from `python-obd`'s connection object) written once per successful connect -
useful evidence to sanity-check against Toyota's expected protocol on the very first real test. useful evidence to sanity-check against Toyota's expected protocol on the very first real test.
- **Expand the data pulled from the car beyond speed/RPM**, to feed the richer dashboard planned - **Expand the data pulled from the car beyond speed/RPM** (backend only - the dashboard cards
in Phase 17: coolant temperature, fuel level, throttle position, and DTC/MIL status (check- displaying this land in Phase 22 once the redesign reaches them): coolant temperature, fuel
engine light on/off + stored trouble codes, reusing/wiring up the already-scaffolded but level, throttle position, and DTC/MIL status (check-engine light on/off + stored trouble codes,
currently-empty `/dtc` page and `GET_DTC` command). **Odometer/mileage is not guaranteed** - reusing/wiring up the already-scaffolded but currently-empty `/dtc` page and `GET_DTC` command).
the standard OBD-II mode 1 PID for total odometer (0xA6) was only added to the SAE J1979 spec **Odometer/mileage is not guaranteed** - the standard OBD-II mode 1 PID for total odometer
in later revisions and is inconsistently supported across manufacturers/years; whether the (0xA6) was only added to the SAE J1979 spec in later revisions and is inconsistently supported
2013 Yaris's ECU exposes it at all can only be confirmed once tested against the real car across manufacturers/years; whether the 2013 Yaris's ECU exposes it at all can only be
(falls under "code hardening only" for now - add the query with graceful "unsupported" handling confirmed once tested against the real car (falls under "code hardening only" for now - add
rather than assuming it will work). the query with graceful "unsupported" handling rather than assuming it will work).
### Phase 16: Complete web UI redesign — 🚧 PLANNED ### Phase 18: Trip history & replay — 🚧 PLANNED
**New phase.** Right now the `tracking` table (Phase 7) is just a raw stream of GPS/speed points
with no concept of a discrete "trip" - there's no way to look back and see "trip #47, left home
8:03am, arrived work 8:31am, 22km." Add:
- Trip boundary detection: group existing tracking rows into discrete trips based on speed>0
gaps (a simple, dependency-free heuristic - e.g. a gap of N minutes with no motion ends a trip),
computed either live as rows are logged or in a batch pass over existing data. Does not need to
wait on Phase 17's OBD hardening - can ship with GPS-only boundary detection first, refine with
ignition-on/off signals from OBD later if useful.
- A `trips` table (start/end time, start/end coordinates, distance, duration) in
`modules/app_sql.py`.
- A new history page/list (built on the Phase 14 shared list component) showing past trips, and
a map replay view (reusing the existing offline-tile Leaflet setup from Phase 3/4) that draws
the recorded route for a selected trip.
### Phase 19: Pi health monitoring card — 🚧 PLANNED
**New phase, small and standalone - no dependencies.** CPU temperature, free disk space, uptime,
and RAM usage are all cheap to read (`vcgencmd measure_temp`, `psutil`/`/proc` stats) and cheap to
display, but currently invisible - the only way to notice a problem (thermal throttling in a hot
car, an SD card filling up) today is to SSH in and check manually. Add a small backend endpoint
and a dashboard card (built on the Phase 14 component set) surfacing these at a glance. Also
feeds directly into Phase 20's alerting.
### Phase 20: Alerts/notifications — 🚧 PLANNED
**New phase - depends on Phases 16/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 17'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 17's data).
Webhook target/thresholds configurable via the Phase 21 Settings page rather than hardcoded.
### Phase 21: Settings page — 🚧 PLANNED
**New phase - depends on Phase 14's shared components, and benefits from Phases 16/20 existing**
so there's real configuration to consolidate. Several settings currently only exist as
SSH-edited files or scattered individual toggles (dashcam upload pause switch on `/dash`,
cellular APN in `db/upload_creds.env`, hotspot SSID/password via `nmcli` directly) or don't exist
yet (retention days from Phase 16, alert webhook/thresholds from Phase 20). Add one dedicated
Settings page consolidating anything reasonably safe to expose over the local-only web UI:
upload toggle, cellular APN, local video retention policy, alert webhook URL + thresholds. WiFi
SSID/password changes likely stay out of scope (higher-risk to expose over a UI that could lock
the Pi out of its own network if fat-fingered) unless it turns out to be easy/safe to add later.
### Phase 22: Finish web UI redesign — 🚧 PLANNED
The current `templates/`/`static/` UI is an old, mostly-unmodified admin dashboard template The current `templates/`/`static/` UI is an old, mostly-unmodified admin dashboard template
(SB Admin 2-style Bootstrap theme) with leftover placeholder cruft never replaced with real (SB Admin 2-style Bootstrap theme) with leftover placeholder cruft never replaced with real
content - e.g. `index.html`'s "Placeholder button", dropdown menus full of "Action" / "Another content - e.g. `index.html`'s "Placeholder button", dropdown menus full of "Action" / "Another
action" / "Something else here" stub links, and a `/dtc` page that's just an empty "Output" card action" / "Something else here" stub links, and a `/dtc` page that's just an empty "Output" card
with nothing wired up to it at all. Given how much is changing anyway (phone-relay removal, with nothing wired up to it at all. Phase 14 already pulled the *foundational* piece of this
new dashcam/video browsers, richer OBD data), do a proper pass over the whole UI rather than work forward (shared layout/list component, used by every phase since); this phase is the
keep bolting new cards onto the old template: remaining cleanup once all the functional phases above have landed:
- Replace/remove all placeholder buttons, dropdown menus, and stub links that don't do anything. - Replace/remove all remaining placeholder buttons, dropdown menus, and stub links that don't do
- Real design pass for the new features rather than reusing the generic admin-template card style anything.
as-is: the dashcam/video file browsers (Phases 13-14) and the expanded OBD data (Phase 15) all - Wire up the actually-empty `/dtc` page to show real DTC/MIL data from Phase 17.
need list/table and status-display patterns that don't really exist in the current template. - Add the new dashboard cards for Phase 17's expanded OBD data (coolant temp, fuel level,
- Wire up the actually-empty `/dtc` page to show real DTC/MIL data once Phase 15 exposes it. throttle) and Phase 19's Pi health card, using the Phase 14 component patterns.
- Not yet scoped: whether to keep Bootstrap 5 + the current theme framework and just clean it up, - Final visual polish pass across every page now that the full feature set (dashcam/video
or replace the frontend stack entirely. To be decided once the functional phases above land, so browsers, trip history, settings, alerts) exists, rather than redesigning against a moving
the redesign is done once against the final feature set rather than repeatedly against a moving
target. target.
- Not yet scoped: whether to keep Bootstrap 5 + the current theme framework and just clean it up,
or replace the frontend stack entirely - decide once everything above has landed.
### Phase 17: Native Android companion app — 🚧 PLANNED (low priority, do last) ### Phase 23: Native Android companion app — 🚧 PLANNED (low priority, do last)
Revives the phone-relay goal (Phase 11/12) properly this time, using a real native app instead of Revives the phone-relay goal (Phase 11/12) properly this time, using a real native app instead of
a web page - the only way to actually hold a WiFi connection to the Pi's hotspot and a cellular a web page - the only way to actually hold a WiFi connection to the Pi's hotspot and a cellular
connection to the internet open simultaneously (`ConnectivityManager.bindProcessToNetwork()`, connection to the internet open simultaneously (`ConnectivityManager.bindProcessToNetwork()`,
unavailable to browser JS - see the Phase 11 update). Explicitly **not urgent** - deprioritized to unavailable to browser JS - see the Phase 11 update). Explicitly **not urgent** - deprioritized to
the end of the roadmap, after all web-side work (Phases 12-16) is done. Scope, at a high level the end of the roadmap, after all web-side work (Phases 12-22) is done. Scope, at a high level
(not yet designed in detail): (not yet designed in detail):
- **Dashcam file transfer**: browse/select/delete dashcam clips already imported to the Pi (same - **Dashcam file transfer**: browse/select/delete dashcam clips already imported to the Pi (same
data as the Phase 14 local video browser, just a native client instead of/alongside the web UI), data as the Phase 16 local video browser, just a native client instead of/alongside the web UI),
download them to the phone, and upload to the remote server over cellular while the phone stays download them to the phone, and upload to the remote server over cellular while the phone stays
on the Pi's hotspot for the download leg - the true simultaneous dual-network relay the web on the Pi's hotspot for the download leg - the true simultaneous dual-network relay the web
version could never deliver. version could never deliver.
- **Trip/location info**: surface live GPS position, speed, and trip history pulled from the Pi - **Trip/location info**: surface live GPS position, speed, and trip history (Phase 18) pulled
while connected to its hotspot (reusing the existing `/api/gps`-style data feeds), so the app is from the Pi while connected to its hotspot (reusing the existing `/api/gps`-style data feeds),
useful as a real "check on the car" companion, not just a file-transfer tool. so the app is useful as a real "check on the car" companion, not just a file-transfer tool.
- Not yet scoped: distribution (sideload vs Play Store), whether it talks to the Pi directly over - Not yet scoped: distribution (sideload vs Play Store), whether it talks to the Pi directly over
the hotspot only or also to the remote server directly for trip history when away from the car, the hotspot only or also to the remote server directly for trip history when away from the car,
and how much of the existing REST API surface can be reused as-is vs needs new and how much of the existing REST API surface can be reused as-is vs needs new
mobile-friendly endpoints. To be designed once Phases 12-16 (web-side cleanup + hardening + mobile-friendly endpoints. To be designed once Phases 12-22 (web-side cleanup + hardening +
redesign) are done. redesign) are done.
## Working Notes / Environment Gotchas ## Working Notes / Environment Gotchas