Skip to content

Tesla BLE onboarding: app-driven discovery, VIN provisioning, staging beat (Android) - #201

Closed
dkneeland wants to merge 4 commits into
mainfrom
feat/tesla-ble-phase4
Closed

Tesla BLE onboarding: app-driven discovery, VIN provisioning, staging beat (Android)#201
dkneeland wants to merge 4 commits into
mainfrom
feat/tesla-ble-phase4

Conversation

@dkneeland

@dkneeland dkneeland commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Summary

Phase 4 Tesla BLE app-channel UX for Android, plus two GATT-busy subscribe fixes. On-car validated end-to-end (enrollment via key card + touchscreen confirm, live status, rebrand UX). Supersedes #200 - the CAN retry fix from that PR is included here (as its own commit) and extended to the Tesla status subscription.

Three commits:

  1. feat(android): Phase 4 Tesla BLE app-channel UX - TeslaStatus model/parser, TeslaClient (opcodes 0x01/0x02/0x03 on the CADA0200 service), TeslaStatusSource -> StateFlow, one-line Home tile, standalone enroll flow (DevicePuck-based), Settings section with two-phase reset. Review findings folded in (parser unit tests, subscribe-time status read, recompose-stability) and dead tesla_enroll_* strings dropped.
  2. fix(android): retry GATT subscribes dropped by the GATT-busy race - Android's GATT client allows one op in flight; back-to-back CCCD writes (CAN + Tesla status on service discovery) could be silently refused because writeDescriptor()'s return value was ignored. Now checked and retried (3 attempts, 250 ms) via a shared DashKitBleManager.subscribeWithRetry() used by both call sites. Includes and generalizes the fix(android): retry CAN subscribe dropped by GATT-busy race #200 fix.
  3. feat(android): Phase 4 Tesla Connection UX rebrand + tile alignment - user-facing copy rebranded from "key / enroll / set up" to "Connection / connect / remove"; single-line captions; tile status right-justified with 16 dp chevron spacing; negative statuses (not present / unlocked / awake) and a Connection-failed fault state now render. Internal identifiers (tesla_enroll_*, TeslaKeySection, NeverEnrolled) intentionally unchanged; "key card" (physical card) wording kept.

Testing

  • :app:assembleDebug -x :app:syncDashApps clean; unit tests pass (TeslaStatusTest).
  • On-car: full enrollment flow (tap card -> confirm on touchscreen -> Connected), live presence/lock/sleep tile updates, remove/reconnect from Settings.
  • Subagent copy audit: zero "enrollment"/"set up"/"Enrolled" strings remain on-screen.

Notes for reviewers/testers

  • Requires the matching Phase 4 firmware (Tesla BLE vehicle-command integration (Phases 0-4) dashkit-firmware#58) - the app speaks the new app-channel protocol (CADA0200 service, link_state byte). Against older firmware the tile simply shows not-connected.
  • iOS port of the same contract is a follow-up (needs Mac/Xcode validation per the integration plan).

Update 2026-08-23 — full onboarding flow + audit remediation

The branch now carries the complete app-driven onboarding experience and the fixes from the firmware/Android audit (companion to dashkit-firmware#58):

New since this PR opened

  • Discovery: live scan of nearby Tesla adverts (legacy hash + modern tail formats), deduped by address with 10-second expiry — stale sightings can no longer be selected or provisioned against.
  • VIN entry hardened: ISO 3779 check-digit validation in addition to charset/WMI. A failing digit (almost always a typo) requires an explicit per-VIN override before staging.
  • Explicit staging beat: the decoded identity card (model/year/plant/drive) renders with a "Stage this car" CTA — TESLA_CMD_PROVISION fires only on tap, never automatically.
  • Identity carried through: the confirmed car's "Model · year · plant" line persists on the start/connecting/tap-window/success/fault screens.
  • Match wording reflects evidence strength: modern adverts prove only a VIN tail ("VIN tail matches ✓"); legacy hash-prefix matches keep "MATCH ✓".
  • Tile rewording: Home/Settings show "Car seen nearby" (recent-past) matching the latch-on-first-sighting scan design.
  • Status-frame version rejection with test coverage.
  • Adds ble/TeslaVinDecoder.kt + unit tests (previously untracked; required for build).

Validation: compileDebugKotlin + full testDebugUnitTest green on this exact tree; on-car enrollment validated end-to-end (tap recognized within seconds of keycard confirm).

@dkneeland dkneeland changed the title Phase 4 Tesla BLE app-channel UX (Android) + GATT-busy subscribe fixes Tesla BLE onboarding: app-driven discovery, VIN provisioning, staging beat (Android) Aug 23, 2026
@dkneeland
dkneeland force-pushed the feat/tesla-ble-phase4 branch from 8706de9 to efc24e9 Compare August 24, 2026 01:35
- Add TeslaStatus model + parser and TeslaClient with app-channel opcodes
  (0x01 start / 0x02 reset / 0x03 cancel) on the new CADA0200 service.
- Add TeslaStatusSource (GattListener) subscribing to CADA0202 -> StateFlow.
- Add one-line TeslaTile on Home (key-not-set-up / staged / connected /
  not-connected; first two open the enroll flow).
- Add standalone TeslaEnrollFlow (Start -> tap-window countdown + Cancel ->
  success/fault) driven by the status flow; DevicePuck gains an accent param.
- Add Settings > DashKit > Tesla key section: status, enroll, two-phase reset.
- Wire nav route TeslaEnrollRoute + status source lifecycle in MainActivity;
  cyan #4FD1FF in Color.kt; strings in strings.xml.

Review findings folded in: read current status on subscribe (fresh
subscribers don't wait for a reconnect backoff) + handle
onCharacteristicRead; proper HomeTile text for PairingWindow/EnrollmentFault;
ErrorStep maps TeslaFaultDetail.None to a neutral message; hoisted
recreate-on-recompose fallback StateFlows to stable remember values; added
TeslaStatusTest covering the 7-byte frame parser + link-state mapping.

Also drops dead tesla_enroll_confirm/_card_detected/_already_enrolled
strings - the firmware exposes no such discrete states (ponytail-audit).
Android's GATT client allows only one operation in flight, and the app
subscribes to two characteristics back-to-back when services become
ready (CAN + Tesla status). The second CCCD write could be refused while
the first was pending, and writeDescriptor()'s return value was ignored,
so the dropped subscribe looked like success: no CAN frames arrived
(Connected is gated on the first CAN frame, so onboarding stalled at
Pairing), or the status tile went stale with no error.

Check writeDescriptor()'s return value and retry (3 attempts, 250 ms
apart) via a shared DashKitBleManager.subscribeWithRetry(), now used by
both the CAN datasource and the Tesla status source.

Supersedes #200.
Rebrand on-screen Tesla copy from key/enroll/set up key to Connection/connect/remove: Settings section 'Tesla key' -> 'Tesla Connection' with caption; actions 'Connect Tesla' / 'Remove Tesla connection' with remove/reconnect dialog and toasts; enroll flow 'Connect to your Tesla' / 'Connect' / 'Connected' / 'Connection failed'; pair caption reflects the separate connection.

Tile: right-justify status text (TextAlign.End), single-line ellipsis, 16 dp spacer before the chevron; 'Tesla' label stays left. Show negative statuses (not present/unlocked/awake) and a 'Connection failed' fault state on the tile.

Internal identifiers (tesla_enroll_*, TeslaKeySection, NeverEnrolled) and 'key card' wording left as-is; user-facing copy only.
App-driven onboarding for the Tesla flow:

- live scan of nearby Tesla adverts, deduped and expired after 10 s
  without an advert; both the rendered list and the auto-match derive
  from that live window
- VIN entry validated by charset/WMI plus the ISO 3779 check digit; a
  failing digit requires a per-VIN "Stage this car anyway" override
- explicit staging beat: decoded identity card (model/year/plant/drive)
  with a "Stage this car" CTA; TESLA_CMD_PROVISION fires only on tap
- confirmed car identity carried through start/connecting/tap-window/
  success/fault screens as a muted one-liner
- Home/Settings tile reworded to recent-past ("Car seen nearby") to
  match its latch-on-first-sighting design
- TeslaStatusTest covers frame-version rejection
- adds ble/TeslaVinDecoder.kt + its unit tests (previously untracked;
  clean checkouts could not build without them)
@dkneeland

Copy link
Copy Markdown
Collaborator Author

Superseded by a stacked series that carries this work forward with the post-review audit fixes applied and the manual-VIN / nearby-car approach removed:

Merge order: 206 -> 207 -> 208 (squash each; retarget + Update branch the next as bases collapse onto main). Context: docs/android-vin-onboarding-plan.md. The \pr/201\ branch is left intact for reference.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant