docs: close BUG-03/05, add Phase 27 features + renumber Android to Phase 28
This commit is contained in:
+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
|
||||
- **Status**: `IDEA`
|
||||
- **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
|
||||
explicitly by the owner.
|
||||
- **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
|
||||
- **Status**: `IDEA`
|
||||
- **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
|
||||
provisioning.
|
||||
- **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
|
||||
- **Status**: `IDEA`
|
||||
- **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
|
||||
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
|
||||
@@ -64,7 +64,7 @@ dedicated PLAN.md phase (or fold it into the relevant existing work) and mark it
|
||||
- **Status**: `IDEA`
|
||||
- **Area**: modems / uplink (Pi `modules/app_cellular.py`, `modules/app_gps.py`, uplink
|
||||
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
|
||||
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.
|
||||
@@ -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
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user