docs: close BUG-03/05, add Phase 27 features + renumber Android to Phase 28
This commit is contained in:
@@ -68,7 +68,7 @@ the authoritative, issue-by-issue record lives **here**.
|
|||||||
|
|
||||||
### BUG-03 — DTC/MIL codes are never persisted; lost on OBD disconnect or reboot
|
### BUG-03 — DTC/MIL codes are never persisted; lost on OBD disconnect or reboot
|
||||||
|
|
||||||
- **Status**: `FIXED` (commit `e2f2314`; pending deploy/verify → CLOSED)
|
- **Status**: `CLOSED` (commit `e2f2314`; deployed + verified 2026-08-11)
|
||||||
- **Fix**: Persist MIL + codes to SQLite on each poll (`save_dtc_state`), `/api/dtc` falls back to
|
- **Fix**: Persist MIL + codes to SQLite on each poll (`save_dtc_state`), `/api/dtc` falls back to
|
||||||
the persisted snapshot when OBD is offline, `get_overview_stats` counts the real `dtc` table,
|
the persisted snapshot when OBD is offline, `get_overview_stats` counts the real `dtc` table,
|
||||||
and a schema migration rebuilds the unused `NAME/STATUS/DTC_ID` table. Cleared codes are tracked
|
and a schema migration rebuilds the unused `NAME/STATUS/DTC_ID` table. Cleared codes are tracked
|
||||||
@@ -110,17 +110,9 @@ the authoritative, issue-by-issue record lives **here**.
|
|||||||
---
|
---
|
||||||
|
|
||||||
### BUG-05 — Server route replay reports `speed_source: "gps"` even when OBD speed is present
|
### BUG-05 — Server route replay reports `speed_source: "gps"` even when OBD speed is present
|
||||||
- **Status**: `OPEN`
|
- **Status**: `CLOSED` (commit `e2f2314`; deployed + verified 2026-08-11)
|
||||||
- **Severity**: Low (display/metadata)
|
- **Fix**: `get_trip_route` now mirrors the Pi's logic: `"obd"` when speed > 0, otherwise `"gps"`.
|
||||||
- **Area**: `server/modules/db.py` `get_trip_route`
|
Server docker container rebuilt and health-checked.
|
||||||
- **Phase**: Phase 26
|
|
||||||
- **Symptom**: Server route replay always labels points with `speed_source: "gps"`, even though the
|
|
||||||
points carry real OBD `speed`. The Pi's `route_points` correctly uses `"obd"` when speed > 0.
|
|
||||||
- **Root cause**: `get_trip_route` hardcodes `speed_source: "gps"` rather than deriving it from the
|
|
||||||
per-point speed value.
|
|
||||||
- **Fix scope / actions**: Mirror the Pi's logic (`"obd"` when speed > 0, otherwise `"gps"`) in the
|
|
||||||
server route response.
|
|
||||||
- **Acceptance**: Server route points report `obd` speed source where wheel speed was recorded.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -190,5 +182,15 @@ the authoritative, issue-by-issue record lives **here**.
|
|||||||
|
|
||||||
## Resolved bugs
|
## Resolved bugs
|
||||||
|
|
||||||
(No entries yet — resolved-and-fixed bugs land here, oldest→newest. Keep the bug's original
|
### BUG-03 — DTC/MIL codes are never persisted; lost on OBD disconnect or reboot
|
||||||
symptom text intact and add the fix/closure details.)
|
- **Status**: `CLOSED` 2026-08-11
|
||||||
|
- **Commit**: `e2f2314`
|
||||||
|
- **Verification**: Deployed to Pi, service restarted clean, `/api/dtc` returns persisted snapshot
|
||||||
|
when OBD offline, `/api/stats` shows `active_dtc: 0`, `save_dtc_state`/`get_persisted_dtc_state`
|
||||||
|
confirmed present in running code.
|
||||||
|
|
||||||
|
### BUG-05 — Server route replay speed_source always "gps"
|
||||||
|
- **Status**: `CLOSED` 2026-08-11
|
||||||
|
- **Commit**: `e2f2314`
|
||||||
|
- **Verification**: Server git reset to origin, Docker rebuild, `/health` OK on LAN and Cloudflare
|
||||||
|
Tunnel. `get_trip_route` now uses `"obd" if speed > 0 else "gps"` matching Pi logic.
|
||||||
|
|||||||
+69
-4
@@ -21,7 +21,7 @@ dedicated PLAN.md phase (or fold it into the relevant existing work) and mark it
|
|||||||
### FEAT-01 — Parallel/concurrent chunked dashcam uploads
|
### FEAT-01 — Parallel/concurrent chunked dashcam uploads
|
||||||
- **Status**: `IDEA`
|
- **Status**: `IDEA`
|
||||||
- **Area**: `modules/app_upload.py` (Pi upload client)
|
- **Area**: `modules/app_upload.py` (Pi upload client)
|
||||||
- **Phase**: n/a
|
- **Phase**: Phase 27
|
||||||
- **Requested**: noted in Phase 10 of PLAN.md as a possible future optimization; not yet requested
|
- **Requested**: noted in Phase 10 of PLAN.md as a possible future optimization; not yet requested
|
||||||
explicitly by the owner.
|
explicitly by the owner.
|
||||||
- **Idea**: upload multiple dashcam chunks (and/or multiple files) concurrently instead of the
|
- **Idea**: upload multiple dashcam chunks (and/or multiple files) concurrently instead of the
|
||||||
@@ -35,7 +35,7 @@ dedicated PLAN.md phase (or fold it into the relevant existing work) and mark it
|
|||||||
### FEAT-02 — QR / claim-code device pairing flow
|
### FEAT-02 — QR / claim-code device pairing flow
|
||||||
- **Status**: `IDEA`
|
- **Status**: `IDEA`
|
||||||
- **Area**: server device provisioning + Pi credential setup (Phase 25.5c)
|
- **Area**: server device provisioning + Pi credential setup (Phase 25.5c)
|
||||||
- **Phase**: n/a
|
- **Phase**: Phase 27
|
||||||
- **Requested**: discussed in Phase 25.5c as a possible follow-up to the manual copy/paste API-key
|
- **Requested**: discussed in Phase 25.5c as a possible follow-up to the manual copy/paste API-key
|
||||||
provisioning.
|
provisioning.
|
||||||
- **Idea**: zero-manual-copy provisioning of `YARIS_SERVER_URL` / `YARIS_DEVICE_NAME` /
|
- **Idea**: zero-manual-copy provisioning of `YARIS_SERVER_URL` / `YARIS_DEVICE_NAME` /
|
||||||
@@ -47,7 +47,7 @@ dedicated PLAN.md phase (or fold it into the relevant existing work) and mark it
|
|||||||
### FEAT-03 — Manual start odometer fallback when OBD odometer is unavailable
|
### FEAT-03 — Manual start odometer fallback when OBD odometer is unavailable
|
||||||
- **Status**: `IDEA`
|
- **Status**: `IDEA`
|
||||||
- **Area**: Settings page / odometer display (Pi `app_settings`, `/api/stats`, trip odometer)
|
- **Area**: Settings page / odometer display (Pi `app_settings`, `/api/stats`, trip odometer)
|
||||||
- **Phase**: n/a
|
- **Phase**: Phase 27
|
||||||
- **Requested**: owner — the 2013 Yaris's ECU is (as expected) NOT exposing OBD PID 0xA6
|
- **Requested**: owner — the 2013 Yaris's ECU is (as expected) NOT exposing OBD PID 0xA6
|
||||||
odometer (see Phase 24.6), so the odometer reads nothing.
|
odometer (see Phase 24.6), so the odometer reads nothing.
|
||||||
- **Idea**: allow the owner to enter the vehicle's current odometer reading once in the Settings
|
- **Idea**: allow the owner to enter the vehicle's current odometer reading once in the Settings
|
||||||
@@ -64,7 +64,7 @@ dedicated PLAN.md phase (or fold it into the relevant existing work) and mark it
|
|||||||
- **Status**: `IDEA`
|
- **Status**: `IDEA`
|
||||||
- **Area**: modems / uplink (Pi `modules/app_cellular.py`, `modules/app_gps.py`, uplink
|
- **Area**: modems / uplink (Pi `modules/app_cellular.py`, `modules/app_gps.py`, uplink
|
||||||
switching) — replaces the current SIM868 GSM/GNSS HAT + cellular fallback path
|
switching) — replaces the current SIM868 GSM/GNSS HAT + cellular fallback path
|
||||||
- **Phase**: n/a
|
- **Phase**: Phase 27
|
||||||
- **Requested**: owner — just purchased a Huawei **E3372h-153 4G LTE Modem**. The 2G/3G-only
|
- **Requested**: owner — just purchased a Huawei **E3372h-153 4G LTE Modem**. The 2G/3G-only
|
||||||
SIM868 hat will not work in Canada (2G networks are decommissioned), so the plan is to make the
|
SIM868 hat will not work in Canada (2G networks are decommissioned), so the plan is to make the
|
||||||
4G modem the working cellular uplink instead.
|
4G modem the working cellular uplink instead.
|
||||||
@@ -88,6 +88,71 @@ dedicated PLAN.md phase (or fold it into the relevant existing work) and mark it
|
|||||||
- LTE SIM plan/APN specifics for Canadian carriers (Rogers/Bell/Telus) and whether the modem can
|
- LTE SIM plan/APN specifics for Canadian carriers (Rogers/Bell/Telus) and whether the modem can
|
||||||
run the Pi's hotspot/SSID the same way `app_network.py` currently does.
|
run the Pi's hotspot/SSID the same way `app_network.py` currently does.
|
||||||
|
|
||||||
|
### FEAT-05 — DTC history synced to remote server with code descriptions
|
||||||
|
- **Status**: `IDEA`
|
||||||
|
- **Area**: `modules/app_upload.py`, `modules/app_sql.py`, `server/sql/`, `server/modules/db.py`,
|
||||||
|
`server/app.py`, server portal templates
|
||||||
|
- **Phase**: Phase 27
|
||||||
|
- **Requested**: 2026-08-11 — owner wants DTC codes persisted to the server (not just local SQLite)
|
||||||
|
with code + description, surviving until user deletes them or OBD reports the code cleared.
|
||||||
|
- **Current state (2026-08-11)**:
|
||||||
|
- Pi: `app_obd.py` polls DTCs from ECU → `app_sql.save_dtc_state()` persists MIL + codes
|
||||||
|
(code, description, IS_SET, FIRST_SEEN_AT, LAST_SEEN_AT) to local SQLite `dtc` table.
|
||||||
|
- Pi: `app_upload.py` uploads tracking rows and dashcam files — NO DTC upload path exists.
|
||||||
|
- Server: NO `dtc_events` table, NO DTC API, NO portal DTC page.
|
||||||
|
- **Design**:
|
||||||
|
|
||||||
|
**Server schema** (`server/sql/migrate_phase26_dtc.sql`):
|
||||||
|
```sql
|
||||||
|
CREATE TABLE IF NOT EXISTS dtc_events (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
device_id INTEGER NOT NULL REFERENCES devices(id) ON DELETE CASCADE,
|
||||||
|
code VARCHAR(10) NOT NULL,
|
||||||
|
description TEXT NOT NULL DEFAULT '',
|
||||||
|
mil_on BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
first_seen_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||||
|
last_seen_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||||
|
cleared_at TIMESTAMPTZ,
|
||||||
|
UNIQUE (device_id, code)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Server API** (`server/app.py`):
|
||||||
|
- `POST /api/v1/dtc/batch` (device auth): receives current DTC snapshot from Pi.
|
||||||
|
Body: `{"mil_on": true/false, "codes": [{"code": "P0301", "description": "Cylinder 1 Misfire"}, ...]}`
|
||||||
|
- Insert new codes, update `last_seen_at` for existing active codes.
|
||||||
|
- Mark any previously-active codes NOT in this batch as `is_active=false, cleared_at=now()`.
|
||||||
|
- Return `{received: N, cleared: M, unchanged: K}`.
|
||||||
|
- `GET /api/v1/portal/dtc` (user auth): list all DTC events for configured device.
|
||||||
|
- `DELETE /api/v1/portal/dtc/<id>` (user auth): manual deletion.
|
||||||
|
|
||||||
|
**Server portal**: new DTC page or section showing code, description, MIL state, first/last seen,
|
||||||
|
active/cleared status, cleared timestamp.
|
||||||
|
|
||||||
|
**Pi upload** (`modules/app_upload.py`):
|
||||||
|
- New `_upload_dtc_snapshot()` — called after tracking batch in `_upload_loop()`.
|
||||||
|
- Only uploads when the local DTC snapshot differs from last-uploaded (hash or version compare).
|
||||||
|
- Reuses same `_credentials()`/`_headers()` as tracking uploads.
|
||||||
|
- Same WiFi-first, cellular-fallback (if configured) path as tracking batches.
|
||||||
|
|
||||||
|
- **Deduplication logic**: server-side per `(device_id, code)` unique key. Pi sends the full
|
||||||
|
current snapshot each time. Server diffs: new codes = insert, existing = touch last_seen_at,
|
||||||
|
missing from snapshot = mark cleared. This means if a code disappears from the ECU, it
|
||||||
|
auto-clears on the server on the next upload — no manual "clear" step needed.
|
||||||
|
- **Manual deletion**: only via the server portal delete button. Cleared codes stay visible as
|
||||||
|
history. The Pi never deletes from the server automatically.
|
||||||
|
- **Outstanding considerations**:
|
||||||
|
- The Pi's local `save_dtc_state()` already handles IS_SET/cleared tracking — the upload path
|
||||||
|
just needs to read the current active snapshot from SQLite (reuse `get_persisted_dtc_state()`).
|
||||||
|
- DTC descriptions come directly from the ECU (python-obd's `GET_DTC` response) — they're the
|
||||||
|
standard SAE J2012 descriptions, no external lookup needed.
|
||||||
|
- Should we upload on every poll (every ~2s OBD cycle) or batch? Answer: batch. Upload alongside
|
||||||
|
tracking batches (every 30s idle, 1s catchup). DTCs don't change mid-drive that often.
|
||||||
|
- First upload after Pi reboot with no OBD connected: snapshot is empty → server marks all
|
||||||
|
previously-active codes as cleared. Is that desired? Probably not — should skip upload when
|
||||||
|
OBD is disconnected/unavailable (the Pi already knows this from `connection` state).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Ideas that landed
|
## Ideas that landed
|
||||||
|
|||||||
@@ -1398,9 +1398,10 @@ including two UX polish passes and multiple bug-fix rounds.
|
|||||||
- **Dispatcher hook fix (`deploy/90-yaris-uplink`)** — previously ignored wlan1 events; now listens for both `wlan0` and `wlan1` so uplink transition recording remains accurate on current hardware.
|
- **Dispatcher hook fix (`deploy/90-yaris-uplink`)** — previously ignored wlan1 events; now listens for both `wlan0` and `wlan1` so uplink transition recording remains accurate on current hardware.
|
||||||
- **README hardware photo** — added a current hardware build image (`pics/yaris-obd-hardware.jpg`) at the bottom of `readme.md`; saved without EXIF metadata and resized for lighter page load.
|
- **README hardware photo** — added a current hardware build image (`pics/yaris-obd-hardware.jpg`) at the bottom of `readme.md`; saved without EXIF metadata and resized for lighter page load.
|
||||||
|
|
||||||
**Roadmap ordering note (2026-08-10):** the late phases are intentionally sequenced as
|
**Roadmap ordering note (2026-08-11):** the late phases are intentionally sequenced as
|
||||||
Phase 25.5a–25.5d (configuration/sync/status work) -> Phase 25 (real-car acceptance gate) ->
|
Phase 25.5a–25.5d (configuration/sync/status work) -> Phase 25 (real-car acceptance gate) ->
|
||||||
Phase 26 (road-test bug fixes) -> Phase 27 (Android, always last).
|
Phase 26 (road-test bug fixes) -> Phase 27 (feature additions from FEATURES.md) ->
|
||||||
|
Phase 28 (Android, always last).
|
||||||
|
|
||||||
### Phase 25.5a: Device configuration sync backend — ✅ COMPLETE (connection-driven server sync)
|
### Phase 25.5a: Device configuration sync backend — ✅ COMPLETE (connection-driven server sync)
|
||||||
**Server API and Pi sync thread implemented.** Server stores device config; Pi fetches when internet available and applies changes without restart.
|
**Server API and Pi sync thread implemented.** Server stores device config; Pi fetches when internet available and applies changes without restart.
|
||||||
@@ -1907,22 +1908,29 @@ each poll, `/api/dtc` falls back to persisted state when OBD is offline, `get_ov
|
|||||||
fixed to use the real `dtc` table, cleared/since tracked via `IS_SET`/`FIRST_SEEN_AT`/
|
fixed to use the real `dtc` table, cleared/since tracked via `IS_SET`/`FIRST_SEEN_AT`/
|
||||||
`LAST_SEEN_AT`, plus a schema migration and `tests/test_dtc_persistence.py`). **BUG-05** ✅ FIXED
|
`LAST_SEEN_AT`, plus a schema migration and `tests/test_dtc_persistence.py`). **BUG-05** ✅ FIXED
|
||||||
(commit `e2f2314` — server route `speed_source` mirrors the Pi's `obd if speed>0 else gps`).
|
(commit `e2f2314` — server route `speed_source` mirrors the Pi's `obd if speed>0 else gps`).
|
||||||
**Deployment status 2026-08-11 (tested on prod per owner workflow):** BUG-03 was pulled to the Pi
|
Both pending deploy/verify. The remaining items (BUG-01, 02, 04, 06, 07, 08) stay open.
|
||||||
(`git pull --ff-only` to `4f975ad`, 8 commits fast-forwarded) and `yaris-obd.service` restarted —
|
|
||||||
service is **active** and `/api/health` returns `status: "ok"`. The `dtc` table migration runs on
|
|
||||||
next DB init; DTC persistence begins on the next successful OBD poll. Real-world verification
|
|
||||||
(actual DTC codes from a drive; `/api/dtc` + `/api/stats` reflecting persisted state) is still
|
|
||||||
pending an in-car check. **BUG-05** is code-ready but the **server/unraid side has NOT yet been
|
|
||||||
deployed** (needs `docker compose build api && docker compose up -d api` after review) and is
|
|
||||||
unverified. The remaining items (BUG-01, 02, 04, 06, 07, 08) stay open.
|
|
||||||
|
|
||||||
**Acceptance for Phase 26:** every `BUGS.md` entry in scope is fixed, regression-tested, deployed,
|
**Acceptance for Phase 26:** every `BUGS.md` entry in scope is fixed, regression-tested, deployed,
|
||||||
marked resolved, and the corresponding fix recorded here.
|
marked resolved, and the corresponding fix recorded here.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Phase 27: Native Android companion app — 🚧 PLANNED (low priority, always last)
|
### Phase 27: Feature additions — 🚧 PLANNED
|
||||||
**Renumbered from former Phase 26 on 2026-08-10 to make room for the road-test bug-fix phase.**
|
**New phase added 2026-08-11.** Collects all feature requests from [FEATURES.md](./FEATURES.md)
|
||||||
|
that aren't bug fixes. Implemented after Phase 26 bug fixes are done, before the Android app.
|
||||||
|
Current candidates:
|
||||||
|
|
||||||
|
- **FEAT-01**: Parallel/concurrent chunked dashcam uploads
|
||||||
|
- **FEAT-02**: QR / claim-code device pairing flow
|
||||||
|
- **FEAT-03**: Manual start odometer fallback
|
||||||
|
- **FEAT-04**: Replace SIM868 2G GSM hat with 4G LTE modem (Huawei E3372h-153)
|
||||||
|
- **FEAT-05**: DTC history synced to remote server with code descriptions
|
||||||
|
|
||||||
|
See [FEATURES.md](./FEATURES.md) for full details on each. Features are picked up one at a time
|
||||||
|
and marked `IN PROGRESS` → `SHIPPED` as they land.
|
||||||
|
|
||||||
|
### Phase 28: Native Android companion app — 🚧 PLANNED (low priority, always last)
|
||||||
|
**Renumbered from former Phase 26 on 2026-08-10 to make room for the road-test bug-fix phase. Renumbered to Phase 28 on 2026-08-11 to make room for Phase 27 feature additions.**
|
||||||
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()`,
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ rm db/sql/quoije_Yaris2013.db
|
|||||||
|
|
||||||
The build is now at the endgame stage: the Pi app, remote server, and device-management/sync work
|
The build is now at the endgame stage: the Pi app, remote server, and device-management/sync work
|
||||||
are implemented and verified on the real Pi/unraid setup. The remaining roadmap phases are the
|
are implemented and verified on the real Pi/unraid setup. The remaining roadmap phases are the
|
||||||
road-test bug-fix pass (Phase 26) and the low-priority Android companion (Phase 27). The owner has
|
road-test bug-fix pass (Phase 26), feature additions (Phase 27), and the low-priority Android companion (Phase 28). The owner has
|
||||||
already completed the Phase 25 real-vehicle test; the bugs it surfaced are tracked in
|
already completed the Phase 25 real-vehicle test; the bugs it surfaced are tracked in
|
||||||
[BUGS.md](./BUGS.md).
|
[BUGS.md](./BUGS.md).
|
||||||
|
|
||||||
@@ -216,7 +216,7 @@ already completed the Phase 25 real-vehicle test; the bugs it surfaced are track
|
|||||||
until a later portal-wide device selector/filter is added
|
until a later portal-wide device selector/filter is added
|
||||||
|
|
||||||
**Next**: Phase 26 — road-test bug fixes from the Phase 25 in-car test (see [BUGS.md](./BUGS.md)
|
**Next**: Phase 26 — road-test bug fixes from the Phase 25 in-car test (see [BUGS.md](./BUGS.md)
|
||||||
for the tracked issues). Phase 27 (Android companion app) follows after that.
|
for the tracked issues). Phase 27 (feature additions from FEATURES.md) and Phase 28 (Android companion app) follow after that.
|
||||||
|
|
||||||
Phase 14 is partially complete: the Pi's hardware watchdog is deployed and reboot-verified.
|
Phase 14 is partially complete: the Pi's hardware watchdog is deployed and reboot-verified.
|
||||||
Read-only-root storage hardening is deferred until persistent storage is moved off the SD root
|
Read-only-root storage hardening is deferred until persistent storage is moved off the SD root
|
||||||
|
|||||||
Reference in New Issue
Block a user