Skip to content

fix: auto-clear stale encryption key after repeated update failures#442

Open
Dzhuneyt wants to merge 1 commit intoRobHofmann:masterfrom
Dzhuneyt:fix/stale-encryption-key-auto-recovery
Open

fix: auto-clear stale encryption key after repeated update failures#442
Dzhuneyt wants to merge 1 commit intoRobHofmann:masterfrom
Dzhuneyt:fix/stale-encryption-key-auto-recovery

Conversation

@Dzhuneyt
Copy link
Copy Markdown

Closes #441.

Summary

When a Gree AC rotates its session key (reproduced by putting the unit into Wi-Fi re-pairing mode via MODE + WIFI), the integration kept encrypting requests with the stale cached key forever — the climate entity stayed Unavailable until the user manually reloaded the integration.

This patch tracks consecutive update failures and, after a threshold, discards the cached key so the existing async_update re-bind path auto-recovers on the next poll tick. User-configured keys are preserved.

See #441 for full symptoms, logs, root-cause analysis, and repro steps.

Change

Single-file change (~35 lines added, 0 removed) in custom_components/gree/climate.py:

  • STALE_KEY_THRESHOLD = 3 module-level constant
  • Two new attrs in __init__:
    • _user_provided_key = bool(encryption_key) — remembers whether the user supplied a static key at setup
    • _consecutive_update_failures = 0
  • New _handle_comms_failure() helper:
    • Increments the counter
    • At threshold, if key was auto-acquired → clears _encryption_key + CIPHER, logs warning, resets counter
    • At threshold, if key was user-configured → logs warning only (no silent override)
  • SyncState resets the counter on successful GreeGetValues, calls _handle_comms_failure() in the existing except blocks for both GreeGetValues and SendStateToAc

No changes to the happy path — only triggers when comms are already failing.

Why the _user_provided_key guard

CONF_ENCRYPTION_KEY is an optional user-editable field in the config flow. Users who deliberately set a static key would otherwise see it silently replaced by whatever bind returns after a transient network blip. With the guard, blank-key setups (the common case, auto-bind) get auto-healing; users who typed a key get a warning but no silent override.

Recovery mechanism (no new code path needed)

Existing async_update already re-binds when _encryption_key is None:

if not self._encryption_key:
    key = await GetDeviceKey(...)  # or GetDeviceKeyGCM
    if key:
        self._encryption_key = key
        self.CIPHER = ...
        await self.SyncState()

This PR just triggers that path after repeated failures instead of only at first boot.

Scope

  • Fix only applied in climate.py. Other platforms (switch.py, sensor.py, number.py, select.py) share the climate instance via hass.data[DOMAIN][entry.entry_id]["device"] (see entity.py) — they don't cache their own keys, so no change needed there.
  • No test suite exists in the repo, so no tests added. Happy to add a small pytest harness if you'd like one.

Test plan

When the AC rotates its session key (e.g. after Wi-Fi re-pairing via
MODE+WIFI on the unit), the integration kept encrypting with the stale
cached key forever, leaving the entity Unavailable until a manual reload.

Track consecutive update failures; after STALE_KEY_THRESHOLD (default 3),
discard the cached key so the existing async_update re-bind path
auto-recovers on the next tick. User-configured keys
(CONF_ENCRYPTION_KEY) are preserved and only logged — silently
overriding a static key the user explicitly set would be surprising.

Refs RobHofmann#441
@RobHofmann
Copy link
Copy Markdown
Owner

Seems promising. Would be nice if people could help test this one!

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.

Device stays Unavailable forever after Wi-Fi re-pairing — stale cached encryption key

2 participants