Gracefully handle climate-settings HTTP 500 (fixes #294)#309
Conversation
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 5 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
There was a problem hiding this comment.
Code Review
This pull request improves the robustness of the Toyota integration by catching API errors during vehicle updates and gracefully handling missing or null values in climate settings (such as when the climate-settings endpoint returns an HTTP 500). The review feedback correctly identifies a potential AttributeError in _create_climate_settings if self.vehicle.climate_settings itself is None, and provides a safer implementation using getattr.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| # Start with existing operations (None when climate-settings 500'd) | ||
| ac_operations = (self.vehicle.climate_settings.operations or []).copy() |
There was a problem hiding this comment.
If self.vehicle.climate_settings is None (which can happen if the climate-settings endpoint completely failed or returned 500), accessing self.vehicle.climate_settings.operations directly will raise an AttributeError and crash the climate entity when a user attempts to control it. Use getattr to safely retrieve the climate_settings object and its operations attribute.
| # Start with existing operations (None when climate-settings 500'd) | |
| ac_operations = (self.vehicle.climate_settings.operations or []).copy() | |
| # Start with existing operations (None when climate-settings 500'd) | |
| climate_settings = getattr(self.vehicle, "climate_settings", None) | |
| ac_operations = (getattr(climate_settings, "operations", None) or []).copy() |
There was a problem hiding this comment.
Good catch — fixed in 76f8e05. climate_settings itself (not just .operations) can be None when the endpoint 500s, so I now getattr through both on the control path, matching the load-path guards elsewhere in this PR.
Addresses review feedback on pytoyoda#309: _create_climate_settings accessed self.vehicle.climate_settings.operations directly, which AttributeErrors when climate_settings itself is None (full climate-settings 500), crashing the entity on a set-temperature/defrost action. getattr through both.
|
Heads-up — this overlaps #310: both wrap the same try:
await asyncio.wait_for(
_call_tagged("vehicle.update", vin, vehicle.update(skip=["status"])),
STATUS_FETCH_BUDGET_S,
)
except (ToyotaApiError, ToyotaInternalError) as ex:
...Happy to fold them into a single PR instead if you'd prefer. |
When the /v1/global/remote/climate-settings endpoint returns HTTP 500 (seen on some HEV/PHEV vehicles, e.g. issue pytoyoda#294), the unguarded vehicle.update() call aborts the whole update flow. Wrap it in a try/except for ToyotaApiError / ToyotaInternalError and log a warning so a single failing endpoint no longer breaks the refresh.
A climate-settings HTTP 500 makes climate_settings.operations (and min/max temp) come back None, so stock filter(..., None) raises TypeError every cycle and defrost/temperature handling silently fails. Add None-guards in climate.py so the climate entity degrades gracefully instead of erroring. Refs pytoyoda#294.
Addresses review feedback on pytoyoda#309: _create_climate_settings accessed self.vehicle.climate_settings.operations directly, which AttributeErrors when climate_settings itself is None (full climate-settings 500), crashing the entity on a set-temperature/defrost action. getattr through both.
76f8e05 to
da7de24
Compare
for more information, see https://pre-commit.ci
|
Rebased onto |
Problem
On some HEV/PHEV vehicles the
/v1/global/remote/climate-settingsendpoint returns HTTP 500 even though remote climate works in the MyToyota app (#294). Two failure modes result:vehicle.update()call propagates the error and aborts the entire refresh cycle, taking otherwise-healthy endpoints down with it.climate_settingsdoes come back partially populated,operations/min_temp/max_tempareNone, sofilter(operation, None)raisesTypeErrorevery update cycle and HA-core'sset_temperaturevalidation doesfloat < None→TypeError. Logs fill with retries/tracebacks and the climate entity never settles.Fix (integration-layer, minimal)
vehicle.update(skip=["status"])intry/except (ToyotaApiError, ToyotaInternalError)and log a warning, so one failing endpoint no longer aborts the refresh.climate_settingsinclimate.py: fall back to the existing__init__defaults (18/29/1) for min/max/step and treatoperations=Noneas[]. The climate entity degrades gracefully instead of erroring.This is the auto-disable-style graceful handling #294's reporter explicitly asked for. The 500 originates Toyota-side, so the fix is independent of the pytoyoda version/provider; it also complements the recent BEV climate enablement (pytoyoda
f7f67eb+ea73031), which makes more vehicles reach this endpoint.Testing
Running in production for ~1 week (since 2026-06-15) on a Corolla Touring Sports (v2.3.0, HA 2026.5.x) whose climate-settings endpoint 500s consistently. Before: refresh aborted + per-cycle
TypeErrorspam. After: climate entity loads with sane defaults, the spam stops, and the rest of the refresh completes normally.ruff+ruff-formatclean.Compatibility
No config/schema changes. Healthy vehicles (valid
climate_settings) are unaffected — guards only change behaviour on theNone/500 path.