You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix: smart-strategy resilience for refresh-status 5xx (counters, cache, recovery)
When the Toyota gateway returns persistent HTTP 500 on
POST /refresh-status (some Lexus / Aygo / Yaris vehicles per
ha_toyota#291 + ha_toyota#293), pytoyoda's controller exhausts its
4-attempt retry sequence and raises ToyotaApiError. The previous
implementation called vehicle.refresh_status() without a try/except,
which meant the exception propagated out of _refresh_one_vehicle
with three consequences:
1. on_post_layer1_failure() never ran (it sits inside the
`return_code != "000000"` branch, which a raised POST never
reaches), so consecutive_post_rejections never advanced and the
_AUTO_DISABLE_REJECTION_THRESHOLD soft/hard-disable mechanism
never fired. status_refresh_state stayed `active` forever.
2. _refresh_one_vehicle's post-decision bookkeeping was skipped:
trips manager refresh, movement detection, diag state persistence,
and the caller's `last_good_per_vin[vin] = vehicle_data` line.
Phase 1 had already fetched fresh telemetry/location/etc., but
that fresh data was never promoted to the cache layer; entities
served from the prior cycle's cached VehicleData. Reported as
parking location frozen at home, lock state stale, etc.
3. Once auto-disable did fire (via the existing returnCode-rejection
branch on cars where that path could trip), the only documented
recovery was the user toggling enable_status_refresh OFF then ON.
No way to retry without disabling the feature first.
This commit:
- Wraps the POST in contextlib.suppress for the same exception set
the Layer 2 poll loop already catches. Collapses the exception
path and the non-"000000" returnCode path into a single Layer 1
failure branch.
- Adds a bare GET fallback (`vehicle.update(only=["status"])`) on
Layer 1 failure so /status entities still refresh this cycle,
even before auto-disable kicks in.
- Lets explicit service calls bypass BOTH HARD_DISABLED_AUTO and
HARD_DISABLED_USER. Matches the HA convention that polling
toggles stop the cadence but explicit invocations still go
through; users can disable the automatic strategy and drive POSTs
from their own automations (geofence, garage door, time-of-day).
- After a successful POST clears auto_disabled_status_refresh,
the strategy goes back to ACTIVE on the next cycle without manual
toggling. Users can now recover from auto-disable by simply
pressing the refresh button instead of toggling options OFF/ON.
- Three new tests in test_refresh_strategy.py covering the
service-call bypass behaviour for both AUTO and USER disable +
blocking when no service call is pending.
- Updates const.py docstring for CONF_ENABLE_STATUS_REFRESH and
services.yaml description for refresh_vehicle_status to reflect
the cadence-vs-capability distinction.
All 31 existing tests still pass; ruff clean.
Closes ha_toyota#293.
0 commit comments