feat(bluetooth): add idle BLE keepalive#63
Merged
Conversation
added 2 commits
July 10, 2026 21:01
An idle held BLE link to the vehicle drops at ~42s mean lifetime. Add a keepalive_interval constructor knob (default 20s, None/0 disables) that issues a bounded passive GATT read after that many seconds of GATT idleness, resetting the link supervision timer and extending link lifetime ~10x. - Idle-triggered: _last_activity is bumped on every send and received frame, so an active session gets no extra traffic; the read fires only during genuine idleness. - Passive and safe: reads the version characteristic only, never a signed/mutating command, and never wakes the car. - Bounded and best-effort: each read carries a short timeout and swallows all failures, so a sleeping car can never hang the loop and a failed keepalive never raises into caller code or forces a reconnect. - One asyncio task tied to the connection lifecycle: started after start_notify in connect(), cancelled-and-awaited in disconnect(). Threaded through Vehicles/VehiclesBluetooth factories. Tests in tests/test_ble_keepalive.py.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Add a keepalive_interval constructor parameter to VehicleBluetooth (captain-directed feature) that keeps an otherwise idle held BLE link to the vehicle from dropping. Measured problem: an idle held BLE link dies at ~42s mean lifetime (link supervision timeout ~720ms underneath, bluetoothd-verified on macOS CoreBluetooth); a single trivial passive GATT read every ~20s extends lifetime ~10x (~400s observed).
Design decisions (captain-approved, refined to idle-triggered):
Constraints followed: KISS (one asyncio task + a monotonic timestamp, no scheduling framework); one issue per PR; based on latest origin main (v1.6.2, 56ea111); code comments state single WHY constraints only, with the measured evidence summary living in AGENTS.md; live car verification was NOT required and the car was not touched. Added tests/test_ble_keepalive.py (11 tests): idle timer fires the read, activity resets the timer, disabled = zero reads, failure swallowed + loop survives, hanging read bounded by timeout, task cleanup on disconnect. Full suite (209) passes, pyright strict and ruff clean.
What Changed
VehicleBluetooth, with a configurablekeepalive_intervalthat performs bounded passive GATT reads only after connection inactivity and cleans up with the connection lifecycle.keepalive_intervalthrough Bluetooth vehicle factory methods while preserving Teslemetry/Tessie signature parity for unsupported Bluetooth creation paths.Risk Assessment
✅ Low: The change is well-bounded to BLE keepalive lifecycle plumbing with documented behavior and focused coverage, and I did not find a substantiated merge-blocking or follow-up risk in the reviewed diff.
Testing
Focused keepalive tests passed, the evidence transcript demonstrates default/factory interval handling, passive VERSION_UUID reads after idle time, no reads during simulated activity, disabled keepalive behavior, notify-based timer reset, and bounded timeout behavior; the full test suite also passed.
Evidence: BLE keepalive mock evidence
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.
uv run pytest tests/test_ble_keepalive.py -quv run python - <<'PY' > /tmp/no-mistakes-evidence/01KX5V3P8XYAANMSA7RGN0AFP7/ble_keepalive_mock_session.txt ...uv run pytest tests -q✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.