Commit Graph
100 Commits
Author SHA1 Message Date
Mathieu Moran 2817930bdc feat: re-add trip ID column on the server portal
Restore the 'ID' column (and showId attribute) on the server trip list.
2026-08-15 17:29:49 -04:00
Mathieu Moran a05d4a9a13 style: bake in 90% zoom scale via root font-size
Set html { font-size: 90% } so the whole UI (sidebar, typography, spacing)
renders at the ~90% browser-zoom look the owner prefers, consistently on
both the Pi and the remote server.
2026-08-15 17:21:50 -04:00
Mathieu Moran 36076034c4 style: compact data-list table rows so Pi and server match
Add an explicit .375rem vertical cell padding for .data-list tables. The
CSS/templates were already byte-identical across Pi and server, so this
makes the compact row height deterministic on both and rules out any
Bootstrap-default drift or stale-cache difference.
2026-08-15 17:13:40 -04:00
Mathieu Moran dcca46ec68 style: drop the ID column from the server trip table to match the Pi
The portal's trip list had an extra 'ID' column the Pi's list doesn't
have. Remove it (and its showId data attribute) so both tables render the
same columns: checkbox, Started, km, Time, Max, Odometer, delete.
2026-08-15 17:02:41 -04:00
Mathieu Moran ff49609f2a docs: note Pi checkbox batch trip delete in Phase 29 2026-08-15 16:56:28 -04:00
Mathieu Moran 99035e86ee feat: checkbox batch trip delete on the Pi (local-only)
- Pi history page gets a select checkbox column + 'Delete selected' button
  (mirrors the server UI)
- POST /api/trips/delete batch endpoint
- app_sql.delete_trips_batch() + deleted_trip_markers table so a later trip
  rebuild can't resurrect locally-deleted trips
- Deletion is local-only: raw tracking rows are kept and the remote server
  is never notified (it keeps its archival copy recomputed from uploaded rows)
2026-08-15 16:55:30 -04:00
Mathieu Moran c9d36cca33 docs: close BUG-23 + record fix in Phase 29
Move BUG-23 to Resolved with commit 78aea2d and verification (Pi trips
57 -> 49 after rebuild). Add matching entry to PLAN.md Phase 29.
2026-08-15 16:08:29 -04:00
Mathieu Moran 78aea2dc88 fix: Pi trip builder drops GPS lock-in drift trips (BUG-23)
Add the server's minimum-trip filter to build_trip_summaries() — duration
< 30s OR distance < 0.05km is rejected — so GPS lock-in drift no longer
spawns false trips on the Pi. Also sort normalized rows by datetime (the
server already orders by recorded_at ASC), fixing clock-rollback trip
boundaries. Bump TRIP_BUILD_VERSION to force a Pi-side rebuild.
2026-08-15 16:05:27 -04:00
Mathieu Moran 94dcffa2cb docs: add BUG-23 — Pi/server trip counts diverge (57 vs 10)
Root-caused the discrepancy by replicating the server's trip builder over
live data: 58 raw groups -> 51 after the server's duration/distance floor
(7 short trips dropped that the Pi keeps) -> 10 after skipping 41 deleted
trip markers. Documents the min-trip filter mismatch and the row-set
mismatch as the actual bugs.
2026-08-15 15:56:32 -04:00
Mathieu Moran 47993a876f fix: diagnose.sh DB checks — wrong path, column, and tool
- DB lives under db/sql/<account>_<car>.db, not the repo root (was -maxdepth 1)
- Pick the most recently modified DB (active account) instead of the first
  found, which was the stale TestUser_SubaruWRX.db
- Query UPLOADED=0 instead of sent=0 (column was renamed long ago)
- Use the venv Python's sqlite3 for queries — the sqlite3 CLI isn't installed
  on the Pi, so integrity/row-count checks were silently failing
2026-08-15 14:22:35 -04:00
Mathieu Moran 5170dbce62 chore: update diagnose.sh for dual-T3U WiFi and Android app
- Network section now checks wlan1 (in-car AP, 10.42.0.1), wlan2 (uplink),
  and reports wlan0 as retired fallback (was: first-wlan guess)
- Header + manual commands reflect current layout: Android companion app,
  remote server at yaris.ceaf.dev, AP/uplink nmcli profiles
2026-08-15 14:16:10 -04:00
Mathieu Moran 3808054577 docs: add BUG-22 commit ref 2026-08-14 23:05:12 -04:00
Mathieu Moran 90cfd7bd04 docs: close BUG-22 (smoothed GPS max verified) 2026-08-14 23:04:42 -04:00
Mathieu Moran be3b196c09 fix: BUG-22 bump TRIP_BUILD_VERSION to force Pi trip rebuild 2026-08-14 23:03:28 -04:00
Mathieu Moran a0be96dfaa fix: BUG-22 smooth GPS-derived speeds in trip max (trailing window) 2026-08-14 23:00:53 -04:00
Mathieu Moran d9314969d8 fix: BUG-22 include GPS-derived speeds in trip max (Pi + server) 2026-08-14 22:56:37 -04:00
Mathieu Moran aaa0bdabc4 docs: add BUG-21 commit ref 2026-08-14 22:37:33 -04:00
Mathieu Moran 7c8f27b84d docs: close BUG-21 (GPS-speed fallback verified) 2026-08-14 22:37:21 -04:00
Mathieu Moran 2fee2de357 fix: BUG-21 GPS-speed fallback in server route replay 2026-08-14 22:34:29 -04:00
Mathieu Moran 0fa6cc4988 docs: add BUG-21 — OBD speed dropout mid-drive, GPS-only replay 2026-08-14 22:22:16 -04:00
Mathieu Moran 09eca4d39f docs: add BUG-20 commit ref 2026-08-14 22:05:21 -04:00
Mathieu Moran 9af71e2395 docs: close BUG-20 (trip IDs renumbered 1..10, sequence clamped) 2026-08-14 22:05:09 -04:00
Mathieu Moran 01c7fdf0c4 fix: BUG-20 clamp trips sequence after recompute + renumber migration 2026-08-14 21:45:26 -04:00
Mathieu Moran 25f57603c6 feat: FEAT-14 dual T3U — wlan2 uplink, wlan1 AP, onboard wlan0 fallback 2026-08-14 21:32:30 -04:00
Mathieu Moran c7329186f4 docs: note SIM868 RTC set via AT+CCLK (BUG-18 follow-up) 2026-08-14 19:03:09 -04:00
Mathieu Moran dc3a3d866d docs: add commit refs for BUG-17/18 2026-08-14 18:35:21 -04:00
Mathieu Moran bf4f3d377e docs: close BUG-17 + BUG-18 — DS3231 RTC verified across power loss 2026-08-14 18:35:10 -04:00
Mathieu Moran bfdd1c2285 docs: BUG-18 DS3231 installed + working (I²C 0x68), pending power-cycle test 2026-08-14 18:05:17 -04:00
Mathieu Moran c2462356e1 Fix slow OBD value load on dashboard (BUG-19) 2026-08-13 16:57:16 -04:00
Mathieu Moran e873db918d Add clear DTC codes button to Pi DTC page (FEAT-15) 2026-08-13 16:40:15 -04:00
Mathieu Moran b39faadf58 docs: correct BUG-15 commit ref 2026-08-13 16:24:14 -04:00
Mathieu Moran ab798205a4 docs: close BUG-15 — phone-OS limitation (dual-role), workaround documented 2026-08-13 16:23:14 -04:00
Mathieu Moran c44026e511 docs: BUG-18 plan — DS3231 (I²C) + SIM868 CR1220 RTC backup 2026-08-13 16:13:51 -04:00
Mathieu Moran f94c50b2ee docs: close BUG-16 (fixed + verified), update Phase 29 status 2026-08-13 15:53:52 -04:00
Mathieu Moran f5f20c315b fix: trip builder handles NULL-GPS speed-only rows (BUG-16 follow-up)
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).
2026-08-13 15:52:07 -04:00
Mathieu Moran e5ac14e38b fix: persist car-connected flag so trip building works across reboots (BUG-16)
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().
2026-08-13 15:41:30 -04:00
Mathieu Moran e0ae179c38 docs: BUG-16/17/18 — Phase 29 road-test findings (trip missing, clock drift, no RTC) 2026-08-13 15:22:00 -04:00
Mathieu Moran 2172de6055 docs: add Phase 29 — round-2 road-test bug fixes (2026-08-13) 2026-08-13 15:11:53 -04:00
Mathieu Moran 87418ba3f8 docs: FEAT-14 — second T3U to replace onboard wlan0 (dual AC radios) 2026-08-13 15:04:11 -04:00
Mathieu Moran 4ee768174c docs: FEAT-13 — flush uploaded tracking rows from local DB (idea) 2026-08-13 15:03:00 -04:00
Mathieu Moran 3d49764855 feat: log OBD speed before GPS fix so rolling starts aren't lost
- _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
2026-08-13 02:02:45 -04:00
Mathieu Moran fa8ae0001a ui: use redacted dashboard screenshots + restore GitHub footer link
- pics/ui.png regenerated from redacted desktop + mobile screenshots
- restore GitHub footer link in Pi + server base.html (accidentally removed)
2026-08-12 23:44:40 -04:00
Mathieu Moran 495e0a99f9 docs: refresh readme status + dashboard screenshots
- Project status -> Phase 27 done, Phase 28 (Android app) in progress
- Added Android companion app to feature list
- Regenerated pics/ui.png (mobile 390px + desktop 1280px dashboards)
2026-08-12 22:58:59 -04:00
Mathieu Moran 7f6d6c3a89 fix: show correct 'reach the Pi' URL per uplink mode + BUG-15
- Settings: when the Pi is on the phone's hotspot (mode phone), show the
  Pi's hotspot IP (http://<ip>) with an LTE-only hint instead of the AP URL
- BUG-15 documents the phone dual-role routing loop (host hotspot + connect
  to AP simultaneously breaks the uplink)
2026-08-12 22:38:31 -04:00
Mathieu Moran 51417abf75 docs: phone-hotspot requires LTE-only phone (no simultaneous AP client)
- FEAT-07: documented the WiFi-sharing/routing loop that breaks the Pi's
  uplink when the phone hosts the hotspot and is also on the Pi's AP
2026-08-12 22:32:07 -04:00
Mathieu Moran 26597888ba fix: use public DNS on phone-hotspot (carrier resolver fails .dev tunnel)
- 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
2026-08-12 21:14:46 -04:00
Mathieu Moran 81c7ee25fd test: align suite with shipped behavior (WiFi swap, BUG-10 guard, odometer_baseline)
- test_app_trip + test_trip_sql: mock was_car_ever_connected() so trip
  building runs (BUG-10 guard suppresses trips until car ECU reached)
- test_app_settings: include odometer_baseline in valid payload + assert save
- test_yaris_uplink: Hotspot -> Hotspot-local; restore_fallback no longer
  activates AP (reports no_connection); wait timeout writes fallback_restored
- test_app_network: local_hotspot mode removed by WiFi swap -> wifi/no-internet
2026-08-12 19:14:07 -04:00
Mathieu Moran 2e8e42a165 android: byte-level upload progress + debug logging; document 28.3
- UploadWorker: ProgressRequestBody (okio ForwardingSink) tracks real bytes
  sent including already-received chunks, instead of chunk count
- Robust chunk reads (loop skip/read) + Log.d/Log.e debug logging
- DashcamViewModel: upload state/progress debug logging
- android/plan.md + PLAN.md: 28.3 marked in progress with built items
2026-08-12 19:00:00 -04:00
Mathieu Moran 6398baaa6b feat: in-app test file generator for upload testing
adb-pushed files are invisible to the app due to scoped storage, so add a
'Create 120 MB test file' button in the Downloads tab that writes the file
through the app's own uid (visible in its contribution set).
2026-08-12 18:23:37 -04:00
Mathieu Moran 6f112ab622 fix: upload progress stuck at 0%
setProgressAsync (fire-and-forget) was dropping/coalescing updates before
they were persisted. Switch to awaited suspend setProgress() in both
UploadWorker and DownloadWorker so each progress write lands and the
LiveData observer actually sees pct advance.
2026-08-12 18:15:42 -04:00
Mathieu Moran 30b919bc03 feat: block re-upload of files already on server + visible upload progress
- Hide upload button when file is in uploadedNames (already on server)
- UploadWorker still guards via /status already_uploaded check
- Indeterminate progress bar during Hashing/Checking phases
- Upload label always shows percentage
2026-08-12 18:08:26 -04:00
Mathieu Moran 2012370cef ui: stack odometer under speed in replay timeline + GitHub footer link (Pi + server) 2026-08-12 17:24:34 -04:00
Mathieu Moran e8746e5d69 docs: FEAT-03 extended — simulated odometer in trip replay + trip list 2026-08-12 17:12:45 -04:00
Mathieu Moran 0d5aea5947 feat: simulated odometer in trip replay + trip list (continuous across trips)
- Pi route_points: emit per-point odometer (raw OBD value or baseline+prior+within-trip distance)
- Pi get_trip_route: SELECT ODOMETER, compute prior-trips distance + baseline
- Pi list_trips: fallback now starts from baseline (was 0)
- Server get_trip_route: per-point odometer (raw or simulated) with haversine accumulation
- Server list_trips: odo_start/end from baseline + cumulative (was hardcoded None)
- history.js: Odometer line in replay scrubber below speed
- Both history templates: trip-replay-odometer element
2026-08-12 16:59:00 -04:00
Mathieu Moran cfd9d03e81 ui: rework Pi settings page — grouped sections, icons, clearer labels
- Four logical sections: Car & identity, Connectivity, Server & uploads, Alerts
- FontAwesome icons on every card header for visual scanning
- Reordered cards (identity → in-car AP → phone hotspot → server → uploads → alerts)
- settings-section + settings-card styles added via block styles
- Improved helper text (device name, odometer, hotspot password)
- All element IDs preserved — JS behavior unchanged
2026-08-12 16:43:04 -04:00
Mathieu Moran 2cfbe67a86 ui: move Test connection + Sync now buttons to header, left of Save settings
- Header now has Test connection, Sync now, Save settings in a row
- remote-config-message alert moved below header for inline feedback
- Removed buttons from Remote server credentials card
2026-08-12 16:35:09 -04:00
Mathieu Moran 14981c066b docs: WiFi role swap + hotspot settings feature (FEAT-11)
- PLAN.md: Hardware section + Architecture #2/#5 updated for wlan1=AP(5GHz), wlan0=uplink
- FEATURES.md: FEAT-11 SHIPPED (hotspot SSID/password UI + 5GHz role swap)
- readme.md: hardware table + hotspot feature line updated
- deploy/readme.md: profile names/bands updated (home-wifi on wlan0, Hotspot-local 5GHz on wlan1)
2026-08-12 16:25:16 -04:00
Mathieu Moran e222dbdba7 net: AP on wlan1 uses 5GHz band for faster in-car access 2026-08-12 16:18:15 -04:00
Mathieu Moran b4b3643201 net: app_network.py reflects WiFi swap — uplink on wlan0, AP on wlan1 2026-08-12 16:17:21 -04:00
Mathieu Moran 4827b73e68 net: swap WiFi roles — fast T3U (wlan1) hosts AP, onboard (wlan0) carries uplink
- yaris_uplink.py: PRIMARY_INTERFACE=wlan0, AP_INTERFACE=wlan1, AP_CONNECTION=Hotspot-local
- install_netwatch.sh: WLAN=wlan0, WLAN_FB=wlan1, AP on wlan1
- _ap_client_count() reads the AP interface instead of hardcoded wlan0
2026-08-12 15:55:23 -04:00
Mathieu Moran 03358587ca fix: upload progress no longer stuck at 0%
UploadWorker now promotes to foreground + reports phase immediately:
- 'Hashing…' while computing SHA256
- 'Checking server…' during resume status check
- 'Uploading…' with per-chunk percentage
Downloads tab renders the phase label for uploads
2026-08-12 15:44:53 -04:00
Mathieu Moran 5b3c04b83d feat: server-presence indicator on Downloads tab
- CredentialStore tracks uploaded filenames in prefs
- UploadWorker success marks file as uploaded
- Downloads tab shows green CloudDone icon next to files already on server
- Upload button skipped for files already on server (dedup)
2026-08-12 15:42:51 -04:00
Mathieu Moran 0c81f1835d fix: FGS type for upload worker + cloud icon for server status
- DownloadWorker.createForegroundNotification now passes FOREGROUND_SERVICE_TYPE_DATA_SYNC
  (was defaulting to 'none' → InvalidForegroundServiceTypeException crash on upload)
- Dashcam file rows show CloudDone (green, on server) / CloudQueue (grey, not uploaded)
2026-08-12 15:33:32 -04:00
Mathieu Moran 8101663dd0 fix: plain SharedPreferences for credentials (EncryptedSharedPreferences crashed on Pixel)
- CredentialStore now uses getSharedPreferences instead of EncryptedSharedPreferences
- Still sandboxed by Android app isolation (other apps can't read it)
2026-08-12 15:23:53 -04:00
Mathieu Moran f4b93006e3 docs: finalize UI branding section — sidebar, logo, mobile dark theme 2026-08-12 05:39:30 -04:00
Mathieu Moran db971ab0e3 style: red underline on mobile topbar logo 2026-08-12 05:37:26 -04:00
Mathieu Moran 4a919b55a1 style: dark mobile UI — bottom nav + topbar match sidebar
- Mobile bottom nav: white → dark (#1c1c1e) bg, white icons/text
- Mobile topbar: white → dark bg on small screens, white text/controls
- Mobile logo color: dark → white for dark bg readability
- Desktop topbar remains white (contrasts with dark sidebar)
2026-08-12 05:34:24 -04:00
Mathieu Moran 6ac750f53b fix: hotspot save now restarts AP immediately (down + 1s wait + up) 2026-08-12 05:28:28 -04:00
Mathieu Moran a7ea97259d fix: remove trailing comma in hotspot POST — was returning (Response, None) tuple 2026-08-12 05:24:02 -04:00
Mathieu Moran 336815b49c feat: Pi hotspot SSID/password configurable from Settings page
- Settings page: SSID + password fields in In-car access card
- GET /api/hotspot reads current SSID (password never returned)
- POST /api/hotspot updates via nmcli (Hotspot-local profile on wlan0)
- deploy/yaris-hotspot.sudoers for privileged nmcli access
- Password is write-only with placeholder hint
2026-08-12 05:15:55 -04:00
Mathieu Moran c0aed4a997 fix: dashcam auto-loads + compact card style for both sections
- LaunchedEffect auto-refreshes on tab open (no more manual refresh needed)
- Imported Videos card matches USB Dashcam compact style
- Removed bulky Refresh button from imported videos
2026-08-12 05:10:55 -04:00
Mathieu Moran bc1516257a feat: USB dashcam file browser on Dashcam tab
- PiApi extended: listDashcamFiles, deleteDashcamFile, importDashcamFiles
- DTO: DashcamFilesResponse, DashcamDevice, DashcamFile
- DashcamScreen: USB Dashcam card with status, file count, Import All + Refresh
- ViewModel: loadDashcamFiles, deleteDashcamFile, importDashcamFiles
2026-08-12 05:06:39 -04:00
Mathieu Moran ffa84073ea fix: Settings header height matches other tabs + 48dp red underline
- Settings Column padding removed from around header
- Cards use horizontal padding individually
- YarisHeader underline: full-width → 48dp
2026-08-12 04:43:38 -04:00
Mathieu Moran e9309f4cdf style: YARIS logo with red underline + #1c1c1e dark theme
- Shared YarisHeader composable: bold YARIS + red accent bar + subtitle
- Dark theme: #1c1c1e background, #2a2a2c surface (matching web UI sidebar)
- Red accent #e63946 matches web logo underline
- Applied to Dashcam, Downloads, Settings screens
2026-08-12 04:36:55 -04:00
Mathieu Moran b69ae3a229 docs: UI branding — dark sidebar + YARIS-OBD Racing Sans One logo 2026-08-12 04:33:46 -04:00
Mathieu Moran 2989f60f9c style: minimalist Material icon buttons (download, upload, delete)
- DashcamScreen: Download + Delete as IconButton with Icons.Filled
- DownloadsScreen: CloudUpload + Delete as IconButton
- No text labels, just clean Material icons
2026-08-12 04:27:51 -04:00
Mathieu Moran 6b47dafdb8 style: yaris-obd lowercase logo 2026-08-12 04:23:42 -04:00
Mathieu Moran bb4ac8c490 feat: Material icons in nav bar + yaris-obd header on all screens
- Tab icons: Icons.Filled.Videocam / FileDownload / Settings
- Branding header (yaris-obd + Phase 28 subtitle) on Dashcam, Downloads, Settings
2026-08-12 04:20:27 -04:00
Mathieu Moran eab91956c9 fix: ASCII-safe tab icons + yaris-obd branding on Settings
- Tab icons: Cam / DL / Set (Unicode emoji kept corrupting in the source file)
- Settings top: yaris-obd title + Phase 28 subtitle restored
2026-08-12 04:18:11 -04:00
Mathieu Moran d613252155 style: dark sidebar + Racing Sans One YARIS wordmark logo
- Sidebar bg: navy (#1a1a2e) → dark gray (#1c1c1e)
- Logo: replaced fa-car icon + text with styled YARIS wordmark
  using Racing Sans One (free Google Font, OFL licensed)
- Red accent underline (#e63946) on logo
- Applied to both server (server/templates/base.html) and
  Pi (templates/base.html) + mobile topbar
2026-08-12 04:15:51 -04:00
Mathieu Moran 67dc9b897e refactor: merge Test tab into Settings, 3 tabs total
- Settings tab now has credential fields + network test (Test Pi, Test Server)
- Removed standalone Test tab
- Bottom nav: Dashcam, Downloads, Settings
2026-08-12 04:04:21 -04:00
Mathieu Moran 19942dc114 feat: Phase 28.3 — cellular upload relay + Settings tab
- UploadWorker: 40MB chunked upload to yaris.ceaf.dev (SHA256 check,
  /status resume, WorkManager foreground notification, progress)
- ServerApi extended: uploadStatus + uploadChunk endpoints
- CredentialStore: EncryptedSharedPreferences for server URL/device name/API key
- Settings tab: credential entry (server URL, device name, API key)
- Downloads tab: ↑ Upload button per file, upload progress with cancel
- Upload only shown when credentials configured
2026-08-12 04:00:45 -04:00
Mathieu Moran c8e26dde26 feat: Downloads tab UI polish — deleting spinner, progress redesign
- Deleting files show CircularProgressIndicator instead of delete button
- Active download card redesigned: spinner + filename + %, rounded progress bar
- AnimatedVisibility for progress section
- Empty state with icon
- Track _deletingNames in ViewModel for reactive UI
2026-08-12 03:46:33 -04:00
Mathieu Moran b523d77e16 feat: cancel button on active downloads
- ActiveDownload now carries workerId (UUID string) for cancellation
- cancelDownload() calls WorkManager.cancelWorkById()
- Downloads tab shows ✕ button on in-progress download cards
2026-08-12 03:35:03 -04:00
Mathieu Moran 78cd05c4cb docs: add BUG-14 (Nominatim slowness), update PLAN.md Phase 26 (14 bugs)
- BUG-14: Nominatim replaced with offline reverse_geocoder — instant, no network
- PLAN.md Phase 26: 13 → 14 bugs resolved, updated result summary
2026-08-12 03:31:50 -04:00
Mathieu Moran 0ddfe39901 refactor: ditch Nominatim, use reverse_geocoder exclusively for geocoding
Nominatim was slow (rate-limited, 1 req/sec, ~4s timeouts) and unreliable
from Docker/Cloudflare (blocked or throttled). It was the root cause of
location labels failing to appear during trip scrubbing.

- server/app.py: _geocode_cached now uses reverse_geocoder exclusively
  (offline, instant, city/province level). Removed Nominatim HTTP calls,
  ThreadPoolExecutor, concurrent.futures import.
- app.py (Pi): same — reverse_geocoder imported at module level,
  Nominatim + urllib.request removed.
- Batch endpoint simplified to sequential loop with lru_cache + dedup.
  No network dependency at all — geocoding is instant and always works.

reverse_geocoder provides city/province labels (e.g. 'Montreal, Quebec')
which is the right granularity for trip scrubbing.
2026-08-12 03:27:00 -04:00
Mathieu Moran e60b7d7d36 perf: geocode pipeline — batch POST + parallel server, 200→1 roundtrip
JS preload(): replaced N individual GET /api/geocode (concurrency 2) with
a single POST /api/geocode/batch — one HTTP roundtrip instead of up to
N/2 sequential ones. schedLoc debounce reduced 600→200ms.

Server geocode_batch(): ThreadPoolExecutor(max_workers=4) parallelises
Nominatim/reverse_geocoder calls instead of sequential loop. Deduplicates
coords before submitting futures. lru_cache absorbs duplicates within batch.

Pi geocode_batch_route(): deduplicate coords, removed stale 'if label:'
guard (label always non-empty after BUG-13 fix).
2026-08-12 03:15:30 -04:00
Mathieu Moran ad10a873b4 docs: close BUG-13, update PLAN.md Phase 26 (13 bugs resolved)
- BUG-13 moved from Open to Resolved with fix summary
- PLAN.md Phase 26: 12 → 13 bugs, added BUG-13 entry
- deploy memory: Unraid SSH deploy key + updated rebuild command
2026-08-12 03:08:57 -04:00
Mathieu Moran 830981934f fix: BUG-13 — server geocode returns raw coords instead of location names
Root cause: server _geocode_cached() returned None → /api/geocode
returned HTTP 204 with no body. JS frontend treated 204 as success,
r.json() on empty body threw → showed raw-coordinate fallback.

Server fixes:
- _geocode_cached() now always returns a non-empty string (coordinate
  fallback when both Nominatim and reverse_geocoder fail)
- /api/geocode always returns HTTP 200 with {label: '...'} (never 204)
- /api/geocode/batch always includes every coord key in result dict
- Nominatim call: jsonv2 + accept-language=en (aligned with Pi)
- reverse_geocoder: mode=1 (aligned with Pi)

Pi alignment:
- _geocode_cached() now checks for empty reverse_geocoder label before
  returning, falling through to coordinate string (same guard as server)
2026-08-12 02:53:44 -04:00
Mathieu Moran 81a0bfebd5 docs: add BUG-13 — server web UI shows raw coords instead of geocoded location
- Trips 18517, 18516 show (x,y) instead of address/street name
- Likely regression in geocoding pipeline or cache lookup
- Plan: rework server-side geocoding, backport to Pi if simple
2026-08-12 02:49:04 -04:00
Mathieu Moran b670fc5480 folder: delete github agent temp folder 2026-08-12 02:35:44 -04:00
Mathieu Moran 555af3694e docs: reformat FEATURES.md — summary table with dates, numerical order
- All 10 FEATs now in strict numerical order under single Feature ideas section
- Added summary table (ID, Status, Created, Title, Phase) for quick scanning
- Created dates sourced from git history (ef3a951 through bc29a01)
- Removed orphan Ideas that landed section (FEAT-06/07 merged into main flow)
2026-08-12 02:34:36 -04:00
Mathieu Moran 7a6c1750c9 docs: update Phase 28.1/28.2 status in PLAN.md + android/plan.md
- PLAN.md: Phase 28 header now shows 28.1 ✅, 28.2 ✅, 28.3–28.7 pending
- PLAN.md: added 28.1 gate-pass + 28.2 dashcam browser/download summaries
- android/plan.md: 28.2 rewritten from NOT STARTED to DONE with real
  implementation details (PiApi, DashcamScreen, DashcamViewModel,
  WorkManager DownloadWorker, Coil thumbnails, bottom nav)
2026-08-12 02:29:22 -04:00
Mathieu Moran e19511fc67 fix: downloads use WorkManager foreground service + inline progress
- DownloadWorker: foreground service with notification + setProgressAsync
- ActiveDownload state tracked via WorkManager LiveData observation
- Downloads tab shows progress bar + % for in-flight downloads
- Survives screen lock / background
- Fixed FGS type for Android 14+ (FOREGROUND_SERVICE_TYPE_DATA_SYNC)
2026-08-12 02:27:33 -04:00
Mathieu Moran bc29a01337 docs: FEAT-05 DTC sync shipped — multi-device selector, portal DTC page, Pi snapshot upload 2026-08-12 02:20:48 -04:00
Mathieu Moran ed2b3aacfc feat: downloads tab + public Movies/yaris-obd/ folder
- Downloads now go to Movies/yaris-obd/ (visible in file managers)
- New Downloads tab: list downloaded files, tap to play (system player),
  delete button
- FileProvider added for video playback intent
- Downloaded files auto-refresh after each download
2026-08-12 01:56:18 -04:00
Mathieu Moran e30b85d218 feat: portal-wide device selector — session-based, all pages now use selected device
- _portal_device_id() reads from session, falls back to DEVICE_NAME env var
- GET/POST /api/v1/portal/select-device for selector data + set
- Dropdown in server base.html topbar, JS populates from API, page reloads on change
- All portal pages (overview, trips, dashcam, DTC, device settings) now use selected device
2026-08-12 01:40:51 -04:00
Mathieu Moran 1556c65131 fix: delete uses @HTTP hasBody + JsonObject return type
- Retrofit @DELETE doesn't support @Body — switched to @HTTP(method=DELETE, hasBody=true)
- Return type changed from Map<String,Any?> to JsonObject (kotlinx-serialization
  can't deserialize untyped maps)
- Fixed double-slash in download URL (baseUrl trailing slash)
- Cleaned up download progress tracking (emit outside withContext(IO))
2026-08-12 01:35:11 -04:00
Mathieu Moran 3916f9879e feat: FEAT-05 — DTC history sync to remote server
- Server: dtc_events table (migrate_phase27_dtc.sql), db.py helpers
  (upsert_dtc_snapshot, list_dtc_events, delete_dtc_event)
- Server API: POST /api/v1/dtc/batch (device auth), GET/DELETE /api/v1/portal/dtc
- Server portal: /dtc page with code, description, MIL, status, timestamps, delete
- Server nav: DTC history in sidebar + mobile bottom nav
- Pi: _upload_dtc_snapshot() in app_upload.py — uploads when snapshot changes,
  skips when OBD disconnected or unchanged
2026-08-12 01:23:23 -04:00
Mathieu Moran 13cb8b4361 docs: Phase 27 DONE (3 shipped) — odometer sync, trip ID, hotspot docs; FEAT-08 shipped, FEAT-09 wontfix 2026-08-12 01:16:53 -04:00