Repository navigation
Stop Infotainment polling from keeping the car awake - #161
Conversation
Decides when an Infotainment read is due: 10 s while charging or driving, 30 s for a 660 s window after activity or a trigger, then every 660 s; one read on wake; none while asleep. Modelled on yoziru/esphome-tesla-ble PRs #198 and #213. Not wired into the controller yet (waits for #158). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0129RzhbEE2cboMcYyZMuxqX
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0129RzhbEE2cboMcYyZMuxqX
Any Infotainment request restarts the car's sleep countdown, so a 30 s window or idle reads keep it awake. Poll every 10 s only while charging or driving; otherwise read once on connect, wake, unlock, a closure change or a user request, plus one follow-up 60 s later. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0129RzhbEE2cboMcYyZMuxqX
A reconnect after a dropped link no longer reads or schedules a follow-up, and the transition memory survives it so a wake, unlock or closure change during the outage still counts. A closure that is neither closed nor unknown, ajar included, counts as open. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0129RzhbEE2cboMcYyZMuxqX
The status handler asks the per-link policy instead of requesting the charge state on every awake status. The charge and drive replies feed it, the refresh button counts as a user request, and the first READY link and a completed pairing are fresh starts. Evidence moves to docs/research; README and master plan updated. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0129RzhbEE2cboMcYyZMuxqX
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0129RzhbEE2cboMcYyZMuxqX
ScreenshotsCaptured from the demo build at 3fec744. Debug APK: zip from Android CI (GitHub sign-in needed) |
| } | ||
| historyStore.record(bleName, charge, acquiredAt, latestRssi(address)) | ||
| infotainmentPolicy.onChargeReading(charge.chargingStateKind) | ||
| if (previous?.battery_level != charge.battery_level || |
There was a problem hiding this comment.
why are we checking battery level
| * [onFreshStart]), VCSEC asleep to awake, locked to unlocked, any closure changing between open and | ||
| * not open (a closure that is neither CLOSED nor UNKNOWN counts as open, so ajar is open), and an | ||
| * explicit user request. A plain reconnect after a dropped link is not an event. Each event also | ||
| * schedules one follow-up read [FOLLOW_UP_DELAY_MS] later, to catch a shift into D or charging |
There was a problem hiding this comment.
what happens if driver shifts to D after 60s ?
| } | ||
|
|
||
| /** The latest drive reading. Shift state D, R or N is activity. */ | ||
| fun onDriveReading(shiftState: ShiftStateKind?) { |
There was a problem hiding this comment.
https://github.com/yoziru/esphome-tesla-ble/blob/0932a1c04f0f7e853db688bd80b02e28df28a9d4/components/tesla_ble_vehicle/tesla_ble_vehicle.cpp#L206
sentry mode also is on. Maybe keep AC on as well? - Or at least make staying awake non-issue due to their energy sue magnitude.
Per the owner's review on #161: read every 10 s while charging or driving and for a hold after each status change (asleep to awake, any lock change, any closure change, fresh start, user request): 1 min, or 10 min while the car reports presence. A presence change alone is not a change. The single read plus 60 s follow-up is gone. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0129RzhbEE2cboMcYyZMuxqX
ScreenshotsCaptured from the demo build at 22bc763. Debug APK: zip from Android CI (GitHub sign-in needed) |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0129RzhbEE2cboMcYyZMuxqX
ScreenshotsCaptured from the demo build at ca2396a. Debug APK: zip from Android CI (GitHub sign-in needed) |
Catches an event the status misses (remote climate, Sentry, a remote wake). 20 minutes is longer than the car's own sleep countdown, so a car that would sleep is already asleep and is never kept awake by it. It does not start a hold. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0129RzhbEE2cboMcYyZMuxqX
ScreenshotsCaptured from the demo build at 640f278. Debug APK: zip from Android CI (GitHub sign-in needed) |




Summary
Part of #112 (real-car verification pending).
Any Infotainment request restarts the car's own sleep countdown, so reading charge and drive state every 10 s whenever VCSEC says "awake" can keep a parked car from ever sleeping. A new pure policy in
coredecides when the read is due; the controller asks it from the VCSEC status handler. VCSEC status stays at 10 s.Infotainment is read every 10 s while either of these holds:
isActive()): sentry and climate join it in a later PR.HOLD_PRESENT_MS) if the latest status shows user presence (someone in the car), otherwise 1 min (HOLD_ABSENT_MS). Presence is re-evaluated on every status, so when the person leaves, the remaining hold drops to 1 min after the last change.Status changes that start or restart the hold:
A change in user presence alone is not a change, so it never starts a hold and cannot keep resetting the car's sleep countdown.
Idle safety read: while the car is awake, not active and not in a hold, one read once 20 min (
IDLE_SAFETY_INTERVAL_MS) have passed since the last read. It catches an event the status misses (remote climate, Sentry, a remote wake) when the car stays awake anyway. 20 min is deliberately longer than the car's own sleep countdown (about 10-15 min), so a car that would sleep is already asleep when the read comes due and its sleep is never extended. It does not start a hold. The value is tuned from the real-car check.Asleep: no reads, and the hold is dropped. Never wakes the car (awake VCSEC status and a session are still required). A reconnect after a dropped link is not a change, and the transition memory (asleep, locked, closures) survives it, so a wake, lock change or door change during an outage still starts the hold on the first status after.
Changes:
core/.../protocol/InfotainmentPollPolicy.ktplusInfotainmentPollPolicyTest(every rule, change and non-change, and the safety read: due at 20 min, not before, none while asleep, no hold started).TeslaBleController.kt: one policy perVehicleLink; the status handler asksonStatusinstead of requesting the charge state unconditionally; charge and drive replies feed the readings; the refresh button counts as a user request and still sends at once; the first READY (onLinkReady) and a completed pairing are fresh starts.docs/research/infotainment-polling-and-sleep.md(evidence and citations), README note, master-plan lines (including unchecked items for sentry/climate as active, activity recognition, and an opt-in "stay awake while the phone is near and moving").Note: the wake button does not call
onUserRequest. It sends no Infotainment read (the car is asleep); the resulting asleep to awake step is itself a change.Type
Testing
./gradlew :core:test :app:assembleDebug :app:testDebugUnitTest :app:lintDebug ktlintCheck detektpasses locally.IDLE_SAFETY_INTERVAL_MSfrom what you see.docs/research/infotainment-polling-and-sleep.md.Checklist
🤖 Generated with Claude Code
https://claude.ai/code/session_0129RzhbEE2cboMcYyZMuxqX
Generated by Claude Code