diff --git a/PLAN.md b/PLAN.md index d6bea94..21155bd 100644 --- a/PLAN.md +++ b/PLAN.md @@ -1482,6 +1482,47 @@ 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 — 🚧 PLANNED (local config mgmt) +**Unstarted.** Replace hardcoded `account.name`, `car.model`, and avatar with managed configuration editable on the server and synced to the Pi. Enables multi-device/multi-user scenarios and eliminates manual code edits. + +#### Scope +- **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, for display on the portal and Pi topbar) + - Car model/year (already in DB via Phase 24's `device_id` + `car_model`, but allow user override) + - Profile picture upload (stored server-side, served to both portal and Pi via API endpoint) + - Owner name / username (replaces hardcoded `account.name`) +- **Pi-side config sync**: + - Add a new `device_config` table to local SQLite (device_name, owner_name, car_model, avatar_url, sync_timestamp, hash) + - On Pi startup and every 30 minutes, fetch the latest config from the remote server via a new `/api/device-config` endpoint (authenticated with device_key, not user login) + - If hash differs, update local config table and restart the web app (or hot-reload via settings API) + - Fall back gracefully to hardcoded defaults if server is unreachable +- **UI updates**: + - Pi topbar shows the synced `device_name` instead of hardcoded car model (or car model from synced config) + - Pi sidebar shows the synced owner_name instead of hardcoded `account.name` + - Avatar image sourced from synced `avatar_url` (served from server, fallback to local placeholder) + - All trip/tracking records use synced values at capture time (no retroactive changes to history) +- **Device authentication**: use an existing `device_key` (generated on first Pi provisioning, stored in `/home/yaris/app/yaris-obd/.device-key` or env var) to authenticate the `/api/device-config` request. Server validates device_key against the Postgres `devices` table. + +#### Implementation phases +1. **Server (Phase 25.5a)**: add `device_name`, `owner_name`, `avatar_blob` columns to Postgres `devices` table; create `/api/device-config` read endpoint and `/api/device-config` POST endpoint for edits; add Settings UI to portal for these fields. +2. **Pi config table & sync (Phase 25.5b)**: add `device_config` SQLite table; implement 30-min sync fetch on Pi; hot-update app globals if config changes. +3. **UI updates (Phase 25.5c)**: update topbar, sidebar, avatar rendering to use synced config; test full cycle (edit on server → Pi syncs → UI reflects change). +4. **Acceptance (Phase 25.5d)**: multi-sync cycle test, network loss recovery, avatar upload/display, retroactive trip behavior. + +#### Benefits +- No code edit needed to rebrand a deployed Pi +- Server becomes the source of truth for device identity (enables future multi-Pi/multi-car scenarios) +- Avatar management centralized +- Graceful fallback if server unavailable (Pi uses last-synced config) +- Enables future features: device rename, device status on portal, bulk config across devices + +#### Trade-offs +- Adds config sync complexity and latency (eventual consistency) +- Requires device authentication key rotation strategy (future) +- Metadata storage (avatar blob) requires server-side image handling + +--- + ### Phase 26: Native Android companion app — 🚧 PLANNED (low priority, always last) 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