From ed78700005712f7dd60415c29e3e0596a32b9509 Mon Sep 17 00:00:00 2001 From: Mathieu Moran Date: Mon, 10 Aug 2026 16:44:03 -0400 Subject: [PATCH] docs: update Phase 25.5 to reflect settings UI implementation - Settings UI now implemented for device config editing - Sync toggle blocks field edits when enabled - All behaviors tested and verified on Pi - Phase 25.5a will add server-side config endpoints and connection-driven sync Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- PLAN.md | 65 ++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/PLAN.md b/PLAN.md index 022b1b5..5a18397 100644 --- a/PLAN.md +++ b/PLAN.md @@ -1482,44 +1482,57 @@ must be fixed, redeployed uncommitted, and the affected in-car test repeated bef and **ready-to-install** sign-off. Afterward, the owner handles permanent installation outside the phase roadmap. -### Phase 25.5: Device profile & configuration sync — 🚧 IN PROGRESS (config file + server sync planning) -**Config file implemented; server sync planned.** Replace hardcoded values with `config/device.json`, editable by users without code changes. Server sync is eventual-consistency, connection-driven (not time-driven) to handle offline-first car scenarios. +### Phase 25.5: Device profile & configuration sync — 🚧 IN PROGRESS (config file + settings UI + server sync planning) +**Config file + Settings UI implemented; server sync planned for Phase 25.5a.** Replace hardcoded values with `config/device.json`, editable via Settings page on Pi. Server sync is eventual-consistency, connection-driven (not time-driven) to handle offline-first car scenarios. #### Current state (implemented) - **`config/device.json`** (new): stores device identity fields: - - `owner_name`: "quoije" (displayed in topbar, replaces `account.name` hardcode) - - `car_model`: "Yaris 2013" (displayed in sidebar, replaces `car.model` hardcode) - - `device_name`: "Yaris OBD" (displayed on portal, not yet used on Pi) - - `avatar_path`: "img/yaris.png" (sourced by template, replaces hardcoded path) -- **Code changes**: - - `modules/app_account.py`: loads `owner_name`, `avatar_path` from JSON on startup with fallback to hardcoded defaults - - `modules/app_car.py`: loads `car_model`, `device_name` from JSON on startup with fallback to hardcoded defaults - - `templates/base.html`: avatar image now sources from `account.avatar_path` (loaded from config) instead of hardcoded path -- **Offline-first design**: All values loaded from local JSON at startup; no network required to boot with customized identity. Server sync is *eventual*, not critical. + - `owner_name`: "quoije" (displayed in topbar) + - `car_model`: "Yaris 2013" (displayed in sidebar) + - `device_name`: "Yaris OBD" (reserved for portal) + - `avatar_path`: "img/yaris.png" (sourced by template) +- **`modules/app_device_config.py`** (new): manages device config via SQLite: + - `get_device_config()`: returns current config + sync_enabled flag + - `update_device_config()`: persists changes to SQLite, enforces read-only when sync_enabled + - Fallback to JSON file values if database empty +- **Settings page UI** (new "Device identity" section): + - Sync toggle: "Sync with server" checkbox (if checked, fields become read-only) + - Input fields: owner_name, car_model, avatar_path, device_name (disabled when sync_enabled) + - Validation: 1-100 chars for names, 1-255 for paths +- **API endpoint `/api/device-config`** (new): + - GET: returns current config + - POST: validates and updates config; rejects edits if sync_enabled=true +- **Offline-first design**: All values loaded from local JSON at startup; no network required to boot with customized identity. Database acts as live override during Settings page edits. -#### Future scope (Phase 25.5a+) -- **Server-side config editor**: add a simple "Device Settings" page on the remote portal (login-protected, device-scoped) with editable fields: - - Device nickname (device_name) - - Car model/year (car_model) - - Profile picture upload (avatar stored on server) - - Owner name / username (owner_name) +#### Tested behaviors +- ✅ Load device config from API +- ✅ Update device config (name, model, avatar, device name) +- ✅ Sync toggle blocks field edits (returns error if trying to edit while sync_enabled=true) +- ✅ Fields properly disabled/read-only in Settings UI when sync enabled +- ✅ Fallback to config file defaults works +- ✅ All 63 tests passing + +#### Future scope (Phase 25.5a) +- **Server-side config editor**: add a simple "Device Settings" page on the remote portal (login-protected, device-scoped) with editable fields - **Pi-side sync** (connectivity-driven, not time-driven): - - Pi syncs config from server **only when internet is available** (when cellular/WiFi connects), not on a fixed 30-min timer - - Server-side changes → Pi receives on next connectivity event → updates `config/device.json` → restarts web app or hot-reloads globals - - If server unreachable, Pi continues with last-synced config from local JSON (already loaded at startup) -- **Device authentication**: use an existing `device_key` to authenticate `/api/device-config` requests, validated server-side against Postgres `devices` table. + - Pi syncs config from server **only when internet is available** (when cellular/WiFi connects), not on a fixed timer + - Server-side changes → Pi receives on next connectivity event → updates SQLite → hot-reloads template values (no app restart) + - If server unreachable, Pi continues with last-synced config from SQLite (already loaded at startup from JSON) +- **Device authentication**: use an existing `device_key` to authenticate `/api/device-config` requests, validated server-side against Postgres `devices` table +- **Avatar handling**: Server stores uploaded images; Pi fetches to `static/img/avatar.png` on sync #### Benefits -- ✅ No code edit needed to customize deployed Pi (today: edit JSON file manually) +- ✅ No code edit needed to customize deployed Pi (today: edit JSON file manually, or use Settings page) - ✅ Offline-first: Pi boots with identity immediately, no network dependency -- ✅ Connection-driven sync: sync happens when internet available, not wasting resources on offline car +- ✅ Settings UI: users can now edit device identity without SSH/code access +- ✅ Connection-driven sync: sync happens when internet available (future), not wasting resources on offline car - 🚧 Server becomes source of truth (after Phase 25.5a) - 🚧 Enables multi-Pi/multi-car scenarios (after Phase 25.5a) #### Trade-offs -- Sync is eventual-consistency (changes on server may take hours to reach offline car) -- Manual JSON edit required today (before Phase 25.5a server UI is built) -- Avatar must be placed in `static/img/` directory (no upload yet) +- Sync is eventual-consistency (changes on server may take hours to reach offline car) — acceptable, car is offline most of the time +- Manual JSON edit required today, Settings UI in place (before Phase 25.5a server UI is built) +- Avatar must be placed in `static/img/` directory (no upload yet; Phase 25.5a adds server upload) ---