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 <[email protected]>
This commit is contained in:
Mathieu Moran
2026-08-10 16:44:03 -04:00
co-authored by Copilot
parent d7b9bb634f
commit ed78700005
+39 -26
View File
@@ -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 and **ready-to-install** sign-off. Afterward, the owner handles permanent installation outside the
phase roadmap. phase roadmap.
### Phase 25.5: Device profile & configuration sync — 🚧 IN PROGRESS (config file + server sync planning) ### Phase 25.5: Device profile & configuration sync — 🚧 IN PROGRESS (config file + settings UI + 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. **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) #### Current state (implemented)
- **`config/device.json`** (new): stores device identity fields: - **`config/device.json`** (new): stores device identity fields:
- `owner_name`: "quoije" (displayed in topbar, replaces `account.name` hardcode) - `owner_name`: "quoije" (displayed in topbar)
- `car_model`: "Yaris 2013" (displayed in sidebar, replaces `car.model` hardcode) - `car_model`: "Yaris 2013" (displayed in sidebar)
- `device_name`: "Yaris OBD" (displayed on portal, not yet used on Pi) - `device_name`: "Yaris OBD" (reserved for portal)
- `avatar_path`: "img/yaris.png" (sourced by template, replaces hardcoded path) - `avatar_path`: "img/yaris.png" (sourced by template)
- **Code changes**: - **`modules/app_device_config.py`** (new): manages device config via SQLite:
- `modules/app_account.py`: loads `owner_name`, `avatar_path` from JSON on startup with fallback to hardcoded defaults - `get_device_config()`: returns current config + sync_enabled flag
- `modules/app_car.py`: loads `car_model`, `device_name` from JSON on startup with fallback to hardcoded defaults - `update_device_config()`: persists changes to SQLite, enforces read-only when sync_enabled
- `templates/base.html`: avatar image now sources from `account.avatar_path` (loaded from config) instead of hardcoded path - Fallback to JSON file values if database empty
- **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. - **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+) #### Tested behaviors
- **Server-side config editor**: add a simple "Device Settings" page on the remote portal (login-protected, device-scoped) with editable fields: - ✅ Load device config from API
- Device nickname (device_name) - ✅ Update device config (name, model, avatar, device name)
- Car model/year (car_model) - ✅ Sync toggle blocks field edits (returns error if trying to edit while sync_enabled=true)
- Profile picture upload (avatar stored on server) - ✅ Fields properly disabled/read-only in Settings UI when sync enabled
- Owner name / username (owner_name) - ✅ 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-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 - 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 `config/device.json` → restarts web app or hot-reloads globals - 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 local JSON (already loaded at startup) - 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. - **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 #### 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 - ✅ 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) - 🚧 Server becomes source of truth (after Phase 25.5a)
- 🚧 Enables multi-Pi/multi-car scenarios (after Phase 25.5a) - 🚧 Enables multi-Pi/multi-car scenarios (after Phase 25.5a)
#### Trade-offs #### Trade-offs
- Sync is eventual-consistency (changes on server may take hours to reach offline car) - 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 (before Phase 25.5a server UI is built) - 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) - Avatar must be placed in `static/img/` directory (no upload yet; Phase 25.5a adds server upload)
--- ---