Skip to content

fix(vehicle): document BLE read limits and cover state readers#49

Merged
Bre77 merged 3 commits into
mainfrom
fm/tfa-ble-pr2-reads-r6
Jul 9, 2026
Merged

fix(vehicle): document BLE read limits and cover state readers#49
Bre77 merged 3 commits into
mainfrom
fm/tfa-ble-pr2-reads-r6

Conversation

@Bre77

@Bre77 Bre77 commented Jul 9, 2026

Copy link
Copy Markdown
Member

Intent

PR-2 of the BLE production-readiness program (read-path completeness + vehicle_data ergonomics), scoped SAFE-TO-SELF-MERGE (read-only + additive + docs, no actuation). Added mocked-transport regression tests for all 14 BLE state readers (vehicle_state, charge_state, climate_state, drive_state, location_state, closures_state, charge_schedule_state, preconditioning_schedule_state, tire_pressure_state, media_state, media_detail_state, software_update_state, parental_controls_state, vehicle_data) in tests/test_ble_mocked_state_readers.py, plus a new vcsec_vehicle_status_reply() helper in tests/ble_mocked_transport.py. Live-verified all 13 individual readers read-only against the real car (m5-btproxy.local, VIN LRW3F7EK4NC716336) with well-formed protos - no actuation command was ever sent.

Initially made BLE vehicle_data(endpoints=None) default to 'all BLE endpoints' to mirror the cloud vehicle_data() ergonomics, per the master plan's PR-2 scope. Live testing then surfaced a real vehicle-side constraint: the vehicle's signed-command implementation enforces its own response-size cap independent of BLE transport chunking - a composite vehicle_data() call requesting 2+ endpoints together (including the all-endpoints default) reliably raises TeslaFleetMessageFaultResponseSizeExceedsMTU, while single-endpoint calls and the individual per-substate readers succeed fine. The user reviewed this finding and explicitly rejected the all-endpoints default as a footgun for a public method that would always fault when called with no args. Per their direction, I reverted vehicle_data() to require endpoints as before (no default), kept the MTU-cap docstring documentation, and noted multi-endpoint auto-chunking (split under the cap, merge replies) as a possible future enhancement without implementing it. AGENTS.md was updated with both the response-size-cap gotcha and a new infotainment-boot-delay gotcha (wake_up() acks before infotainment is ready; first INFO read after waking may need a short wait/retry) discovered during live verification.

Deliberate scope boundaries: no actuation commands were touched or tested; no transport/chunking behavior changes were implemented (documented as future work only); the git-excluded scripts/ble-harness/ directory (restored from a snapshot, extended with a read-only 'readall' subcommand) is intentionally out of the PR diff.

What Changed

  • Added mocked BLE transport coverage for the BLE state read path, including all individual state readers and single-endpoint vehicle_data() decoding.
  • Kept BLE vehicle_data() endpoints explicit and documented the vehicle-side response-size cap that makes multi-endpoint BLE reads fail with TeslaFleetMessageFaultResponseSizeExceedsMTU.
  • Expanded Bluetooth vehicle docs with the supported read methods, INFO-domain wake timing guidance, and the single-endpoint recommendation for BLE vehicle data reads.

Risk Assessment

✅ Low: The final branch diff is narrowly scoped to BLE vehicle_data documentation, AGENTS notes, and mocked read-path tests, with no production behavior change introduced in this range.

Testing

Inspected the BLE/doc/test diff, ran the focused 14-reader mocked BLE regression suite, generated reviewer-visible JSON evidence from mocked end-to-end reader calls showing domains/request fields/reply types, then ran the full test suite; all tests passed, and transient worktree artifacts from testing were cleaned up.

Evidence: BLE state-reader request/response evidence

JSON evidence records 14 BLE read paths. It shows each substate reader sent one mocked signed read request, with INFO-domain GetVehicleData fields for individual readers, VCSEC GET_STATUS for vehicle_state, and explicit single-endpoint vehicle_data([BluetoothVehicleData.CHARGE_STATE]).

{
  "ble_readers_exercised": 14,
  "rows": [
    {
      "reader": "charge_state",
      "request_domain": "DOMAIN_INFOTAINMENT",
      "request_fields": [
        "getChargeState"
      ],
      "decoded_reply_type": "ChargeState",
      "send_calls": 1
    },
    {
      "reader": "climate_state",
      "request_domain": "DOMAIN_INFOTAINMENT",
      "request_fields": [
        "getClimateState"
      ],
      "decoded_reply_type": "ClimateState",
      "send_calls": 1
    },
    {
      "reader": "drive_state",
      "request_domain": "DOMAIN_INFOTAINMENT",
      "request_fields": [
        "getDriveState"
      ],
      "decoded_reply_type": "DriveState",
      "send_calls": 1
    },
    {
      "reader": "location_state",
      "request_domain": "DOMAIN_INFOTAINMENT",
      "request_fields": [
        "getLocationState"
      ],
      "decoded_reply_type": "LocationState",
      "send_calls": 1
    },
    {
      "reader": "closures_state",
      "request_domain": "DOMAIN_INFOTAINMENT",
      "request_fields": [
        "getClosuresState"
      ],
      "decoded_reply_type": "ClosuresState",
      "send_calls": 1
    },
    {
      "reader": "charge_schedule_state",
      "request_domain": "DOMAIN_INFOTAINMENT",
      "request_fields": [
        "getChargeScheduleState"
      ],
      "decoded_reply_type": "ChargeScheduleState",
      "send_calls": 1
    },
    {
      "reader": "preconditioning_schedule_state",
      "request_domain": "DOMAIN_INFOTAINMENT",
      "request_fields": [
        "getPreconditioningScheduleState"
      ],
      "decoded_reply_type": "PreconditioningScheduleState",
      "send_calls": 1
    },
    {
      "reader": "tire_pressure_state",
      "request_domain": "DOMAIN_INFOTAINMENT",
      "request_fields": [
        "getTirePressureState"
      ],
      "decoded_reply_type": "TirePressureState",
      "send_calls": 1
    },
    {
      "reader": "media_state",
      "request_domain": "DOMAIN_INFOTAINMENT",
      "request_fields": [
        "getMediaState"
      ],
      "decoded_reply_type": "MediaState",
      "send_calls": 1
    },
    {
      "reader": "media_detail_state",
      "request_domain": "DOMAIN_INFOTAINMENT",
      "request_fields": [
        "getMediaDetailState"
      ],
      "decoded_reply_type": "MediaDetailState",
      "send_calls": 1
    },
    {
      "reader": "software_update_state",
      "request_domain": "DOMAIN_INFOTAINMENT",
      "request_fields": [
        "getSoftwareUpdateState"
      ],
      "decoded_reply_type": "SoftwareUpdateState",
      "send_calls": 1
    },
    {
      "reader": "parental_controls_state",
      "request_domain": "DOMAIN_INFOTAINMENT",
      "request_fields": [
        "getParentalControlsState"
      ],
      "decoded_reply_type": "ParentalControlsState",
      "send_calls": 1
    },
    {
      "reader": "vehicle_state",
      "request_domain": "DOMAIN_VEHICLE_SECURITY",
      "request_type": "INFORMATION_REQUEST_TYPE_GET_STATUS",
      "decoded_reply_type": "VehicleStatus",
      "vehicle_lock_state": "VEHICLELOCKSTATE_LOCKED",
      "send_calls": 1
    },
    {
      "reader": "vehicle_data",
      "call": "vehicle_data([BluetoothVehicleData.CHARGE_STATE])",
      "request_domain": "DOMAIN_INFOTAINMENT",
      "request_fields": [
        "getChargeState"
      ],
      "decoded_reply_type": "VehicleData",
      "returned_charge_level": 77,
      "send_calls": 1
    }
  ]
}

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

✅ **Review** - passed

✅ No issues found.

✅ **Test** - passed

✅ No issues found.

  • git diff --stat c28d86eccc27f03f5335b3c5243a42043ef44b83..d10ca630acb7bf1581cf4b0dfd27c1c32a3976e3
  • git diff c28d86eccc27f03f5335b3c5243a42043ef44b83..d10ca630acb7bf1581cf4b0dfd27c1c32a3976e3 -- tesla_fleet_api/tesla/vehicle/bluetooth.py tests/ble_mocked_transport.py tests/test_ble_mocked_state_readers.py AGENTS.md
  • uv run pytest tests/test_ble_mocked_state_readers.py -vv
  • uv run python /tmp/no-mistakes-evidence/01KX31WFX8ABB4FQ17RSPW776E/ble_state_reader_evidence.py > /tmp/no-mistakes-evidence/01KX31WFX8ABB4FQ17RSPW776E/ble_state_reader_evidence.json
  • uv run pytest tests
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

firstmate crewmate added 3 commits July 9, 2026 18:57
…icle_data() to all endpoints

Covers the PR-2 read-path gap: every BLE state reader now has a
mocked-transport regression test, and vehicle_data(endpoints=None)
defaults to all BLE endpoints (matching the cloud vehicle_data()
ergonomics) with the proto-vs-REST divergence and the live-verified
response-size cap documented in the docstring.
…default

A no-arg vehicle_data() defaulting to every endpoint reliably raises
TeslaFleetMessageFaultResponseSizeExceedsMTU on real hardware (a
multi-endpoint composite call overflows the vehicle's own
signed-command response-size cap) - a footgun for a public method.
endpoints stays required; the cap and the per-substate-reader/narrow-
subset workaround remain documented, with auto-chunking noted as a
possible future enhancement rather than implemented here.
@Bre77 Bre77 merged commit 29a9246 into main Jul 9, 2026
5 checks passed
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