Trips starting/ending on speed-only rows violated the trips.*GPSX NOT NULL
constraint. Use the first/last GPS-bearing row for start/end coordinates, and
skip a trip that never got any GPS fix (no location to record).
connect_obd() set _car_ever_connected without a global declaration, so the
module-level flag was never set and was_car_ever_connected() always returned
False — local trip building has been silently blocked since BUG-10 landed.
Also persist the flag to the settings table so a drive followed by a reboot
still builds trips, and clean up dead code in was_car_ever_connected().
- _log_loop: when GPS has no fix but OBD speed > 1, write speed-only rows
(NULL coordinates) instead of dropping the motion
- insert_tracking: accept None gps_x/gps_y -> NULL GPSX/GPSY
- app_trip: keep NULL-coord rows through normalize; _segment returns 0 for
missing coords; route_points drops them (no position to draw)
- tests: speed-only rows build a trip with max speed + zero distance
- docs: FEAT-12 + readme note; server already filters NULL-GPS safely
- yaris_uplink configure() keyfile: dns=8.8.8.8;1.1.1.1 + ignore-auto-dns=true
so the Pi doesn't rely on the phone's DHCP DNS, which failed to resolve
yaris.ceaf.dev (NameResolutionError) and broke uploads on the hotspot
- test asserts the DNS lines are written to the keyfile
BUG-03: DTC/MIL codes were never persisted; lost on OBD disconnect or reboot.
- Add new dtc table schema (code, description, MIL, first/last seen, IS_SET)
and a migration that rebuilds the original unused NAME/STATUS/DTC_ID table.
- Persist MIL + codes to SQLite on each successful OBD poll (_persist_dtc_state).
- /api/dtc falls back to the last-persisted snapshot when OBD is offline/stale.
- Fix get_overview_stats to count the real dtc table (was querying non-existent
dtc_codes table).
- Add cleared/since tracking via IS_SET + FIRST_SEEN_AT/LAST_SEEN_AT.
BUG-05: server trip replay hardcoded speed_source:'gps'; mirror Pi's
'obd if speed>0 else gps' logic in server get_trip_route.
Adds tests/test_dtc_persistence.py (7 tests).
- add /api/gps/status for thread/fix-age visibility
- surface GPS status in Pi health and live GPS cards
- add tests for app_gps status snapshots
Co-authored-by: Copilot <[email protected]>
- add multi-device portal device management, key rotation, and sync request APIs
- add Pi settings UI for remote server URL, device name, and API key
- add local test-connection and sync-now actions
- make upload and config sync reuse dynamic shared credentials
- add Phase 25.5c migration and docs updates
Co-authored-by: Copilot <[email protected]>
- load server sync creds from db/upload_creds.env like uploads do
- fall back from YARIS_DEVICE_KEY to existing YARIS_API_KEY
- document shared credential flow for server-synced identity
- add test coverage for sync credential fallback
Co-authored-by: Copilot <[email protected]>
- app_alerts.py: null guard on list_local_videos() result (.get('files', []))
- app_cellular.py: remove erroneous .encode(errors='replace') on str response body
- app_settings.py: initialize apn='' when isinstance check fails (NameError guard)
- app_upload.py: move json import to top of file instead of inside except handler
- server/app.py: wrap int(request.args.get('limit')) in try/except ValueError
- server/app.py: wrap os.path.commonpath() in try/except ValueError (cross-root paths)
- tests/test_app_settings.py: add missing trips_upload_enabled/video_convert_enabled
to valid_payload() (fields added in Phase 23.5 but test not updated)
All 63 tests pass.
Co-authored-by: Copilot <[email protected]>
Prefer a configured phone hotspot for outbound synchronization, preserve secure write-only credentials, and recover through home WiFi or the local Pi hotspot.
Co-authored-by: Copilot <[email protected]>