Commit Graph
340 Commits
Author SHA1 Message Date
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
Mathieu Moran 22bbee3b30 feat: Phase 28.2 — dashcam video browser + WiFi download
- DashcamScreen: LazyColumn of imported Pi videos with thumbnails (Coil),
  file size, date, upload status, converting progress
- DashcamViewModel: Retrofit-backed video list + download to phone storage
  + delete local copy
- PiApi extended: listVideos, getVideoFile (streaming), getThumbnail, deleteVideo
- DTOs: VideoListResponse, VideoFile, ConvertProgress
- Bottom nav: Dashcam tab + Test tab
- Verified: video list loads from real Pi over yaris 911 hotspot
2026-08-12 01:14:20 -04:00
Mathieu Moran 348bfdc48c feat: Phase 28.1 gate passed — dual-network test works
- Simplified to single OkHttpClient — Android OS routes correctly:
  10.42.0.1 → WiFi (Pi hotspot subnet)
  yaris.ceaf.dev → cellular (WiFi has no internet)
- Removed NetworkBinder-based per-socket binding (EPERM on Pixel,
  OS-routed approach works better for this use case)
- Verified on real Pixel: Pi GPS + server health both return 200
  simultaneously while connected to "yaris 911" hotspot
2026-08-12 01:09:13 -04:00
Mathieu Moran f1606ad1f6 fix: remove runBlocking(Dispatchers.Main) deadlock — app now starts
- NetworkBinder functions were suspend but never actually suspended
  (ConnectivityManager calls are synchronous) — made them plain fun
- MainActivity.discoverNetworks() now calls them directly, no coroutine
- Wrapped in try/catch so network discovery failure doesn't crash Activity
- Removed unused awaitNetwork() extension + coroutine imports
2026-08-12 00:54:27 -04:00
Mathieu Moran 40f92d8703 feat: Phase 28.1 — gradle wrapper + debug APK builds successfully
- gradlew + gradle-wrapper.jar (Gradle 8.11.1)
- Fixed manifest icon to @android:mipmap/sym_def_app_icon (no custom asset needed yet)
- Added adaptive icon XML + colors for future custom icon
- Removed deprecated buildfeatures.buildconfig from gradle.properties
- 17MB debug APK builds: ./gradlew assembleDebug
- local.properties auto-ignored (contains SDK path)
2026-08-12 00:48:05 -04:00
Mathieu Moran 8d64b1e8b0 feat: Phase 28.1.2 — dual-network binding + OkHttp clients
- NetworkBinder: discovers Pi hotspot WiFi (no-internet) and cellular networks
  via ConnectivityManager, creates per-network socket-bound OkHttpClients
- DualNetworkTestScreen: real OkHttp calls to 10.42.0.1/api/gps/status and
  yaris.ceaf.dev/health, simultaneous "Test Both" gate button
- MainActivity: discovers networks at startup, passes bound clients to screen
- Retrofit interfaces (PiApi, ServerApi) + DTOs (GpsStatus, HealthResponse)
- Uncommented OkHttp/Retrofit/kotlinx-serialization deps in build.gradle.kts
2026-08-12 00:34:41 -04:00
Mathieu Moran f1c83a2ee7 init: android project scaffold (Phase 28.1)
- Kotlin DSL build with version catalog (AGP 8.7, Kotlin 2.1, Compose BOM 2025.01)
- app module: Compose + Material 3, dark theme, network security config
- DualNetworkTestScreen — Phase 28.1 gate screen (buttons wired in 28.1.2)
- YarisApp, MainActivity, YarisOBDTheme
- ProGuard rules for Retrofit/OkHttp/Kotlinx Serialization/Coil/MapLibre/Room
- cleartext allowed for 10.42.0.1 + yaris.local
- gradle wrapper properties (needs Android Studio to generate wrapper jar)
2026-08-12 00:26:54 -04:00