fix(climate): write mode-correct setpoints and stop REST reverting live state #5

Merged
thatguygriff merged 1 commits from fix/setpoint-key-and-stale-rest-state into main 2026-09-19 12:53:51 +00:00
Collaborator

Two independent bugs made a second change (e.g. adjusting the target temperature right after switching cool -> heat) fail to take, and made Home Assistant settle back on a previous state while DKN Cloud NA showed the correct one.

Bug 1 — temperature writes went to the wrong setpoint key

_writable_temperature_property_for_mode() ignored its hvac_mode argument and resolved the key from real_mode/mode in the cached device payload. That payload still describes the pre-change state, so a heat setpoint was emitted as setpoint_air_cool: the unit switched to heat but the heat setpoint never moved.

Reproduced against the real payload shape:

key no mode (old path): setpoint_air_cool   <- bug, while HA requested HEAT
key HEAT(3):            setpoint_air_heat   <- after fix

The same defect broke AUTO in steady state: with mode=1, real_mode=2 the write targeted setpoint_air_cool while the read used setpoint_air_auto, so the value could never appear to change.

  • target_temperature() and writable_target_temperature_key() now accept the mode the caller intends, and that mode takes priority over anything in the device payload. Device-reported modes remain a fallback only for payloads that do not expose the requested mode's setpoint.
  • async_set_temperature() no longer re-sends power+modeasync_set_hvac_mode() already sent them, and the stale-cache condition guarding the resend was always true. It now waits (bounded, 8s) for the cloud to echo mode before sending the setpoint, matching the ensure_mode ordering smoke-test/smoke_test.py already uses.

Bug 2 — state reverting to a previous value minutes later

The coordinator merged {**existing, **device}, letting the lagging REST /installations snapshot win over live Socket.IO pushes. Once the socket confirmed a write, _reconcile_optimistic dropped the optimistic overlay, leaving nothing to stop the next poll from reverting the state — so HA settled on stale "cool at the old target".

  • Socket-pushed values are now tracked per device (DknCoordinator._live_state) and applied after the REST payload.
  • DknCloudNaClient.socket_session is incremented on each connect; the coordinator discards _live_state when the session changes, since updates may have been missed while the socket was down and REST becomes authoritative again.

Also

  • hvac_action and target_temperature are reported against the effective (optimistic-aware) mode, so the action cannot contradict the reported mode while a change propagates.
  • New DknEntity._async_wait_for_device_value() helper; DEVICE_ECHO_TIMEOUT_SEC / DEVICE_ECHO_POLL_SEC constants.
  • manifest bumped to 0.2.1.

Verification

  • python3 -m py_compile clean across the component.
  • Setpoint key selection exercised directly against representative payloads (results above), including the fallback path for payloads missing the requested mode's setpoint.

Not verified against live hardware: the Bug 2 fix rests on the REST snapshot being the source of the revert. It is the only remaining explanation given the unit physically switched to heat while HA reported cool, but running smoke-test/run.sh with credentials would confirm it directly.

Two independent bugs made a second change (e.g. adjusting the target temperature right after switching cool -> heat) fail to take, and made Home Assistant settle back on a previous state while DKN Cloud NA showed the correct one. ## Bug 1 — temperature writes went to the wrong setpoint key `_writable_temperature_property_for_mode()` ignored its `hvac_mode` argument and resolved the key from `real_mode`/`mode` in the **cached** device payload. That payload still describes the pre-change state, so a heat setpoint was emitted as `setpoint_air_cool`: the unit switched to heat but the heat setpoint never moved. Reproduced against the real payload shape: ``` key no mode (old path): setpoint_air_cool <- bug, while HA requested HEAT key HEAT(3): setpoint_air_heat <- after fix ``` The same defect broke AUTO in steady state: with `mode=1, real_mode=2` the write targeted `setpoint_air_cool` while the read used `setpoint_air_auto`, so the value could never appear to change. - `target_temperature()` and `writable_target_temperature_key()` now accept the mode the caller intends, and that mode takes priority over anything in the device payload. Device-reported modes remain a fallback only for payloads that do not expose the requested mode's setpoint. - `async_set_temperature()` no longer re-sends `power`+`mode` — `async_set_hvac_mode()` already sent them, and the stale-cache condition guarding the resend was always true. It now waits (bounded, 8s) for the cloud to echo `mode` before sending the setpoint, matching the `ensure_mode` ordering `smoke-test/smoke_test.py` already uses. ## Bug 2 — state reverting to a previous value minutes later The coordinator merged `{**existing, **device}`, letting the lagging REST `/installations` snapshot win over live Socket.IO pushes. Once the socket confirmed a write, `_reconcile_optimistic` dropped the optimistic overlay, leaving nothing to stop the next poll from reverting the state — so HA settled on stale "cool at the old target". - Socket-pushed values are now tracked per device (`DknCoordinator._live_state`) and applied **after** the REST payload. - `DknCloudNaClient.socket_session` is incremented on each connect; the coordinator discards `_live_state` when the session changes, since updates may have been missed while the socket was down and REST becomes authoritative again. ## Also - `hvac_action` and `target_temperature` are reported against the effective (optimistic-aware) mode, so the action cannot contradict the reported mode while a change propagates. - New `DknEntity._async_wait_for_device_value()` helper; `DEVICE_ECHO_TIMEOUT_SEC` / `DEVICE_ECHO_POLL_SEC` constants. - manifest bumped to `0.2.1`. ## Verification - `python3 -m py_compile` clean across the component. - Setpoint key selection exercised directly against representative payloads (results above), including the fallback path for payloads missing the requested mode's setpoint. Not verified against live hardware: the Bug 2 fix rests on the REST snapshot being the source of the revert. It is the only remaining explanation given the unit physically switched to heat while HA reported cool, but running `smoke-test/run.sh` with credentials would confirm it directly.
Kydoimos added 1 commit 2026-09-19 12:52:38 +00:00
fix(climate): write mode-correct setpoints and stop REST reverting live state
Validate / HACS validation (pull_request) Skipped
Validate / Hassfest validation (pull_request) Skipped
3929546ea5
Two independent bugs made a second change (e.g. adjusting the target
temperature right after switching cool -> heat) fail to take, and made
Home Assistant settle back on a previous state while DKN Cloud NA showed
the correct one.

Wrong setpoint key on write:

_writable_temperature_property_for_mode() ignored its hvac_mode argument
and resolved the key from real_mode/mode in the cached device payload.
That payload still describes the pre-change state, so a heat setpoint was
emitted as setpoint_air_cool: the unit switched to heat but the heat
setpoint never moved. The same defect broke AUTO in steady state, where
real_mode=cool made the write target setpoint_air_cool while the read
used setpoint_air_auto, so the value could never appear to change.

target_temperature() and writable_target_temperature_key() now accept the
mode the caller intends, and that mode takes priority over anything in the
device payload. Device-reported modes remain a fallback only for payloads
that do not expose the requested mode's setpoint.

async_set_temperature() no longer re-sends power+mode; async_set_hvac_mode()
already sent them, and the stale-cache condition guarding the resend was
always true. It now waits (bounded) for the cloud to echo the mode before
sending the setpoint, matching the ensure_mode ordering the smoke test uses.

REST snapshot reverting live state:

The coordinator merged {**existing, **device}, letting the lagging REST
/installations snapshot win over live Socket.IO pushes. Once the socket
confirmed a write, _reconcile_optimistic dropped the overlay, leaving
nothing to stop the next poll from reverting the state.

Socket-pushed values are now tracked per device and applied after the REST
payload. They are discarded when the socket session changes, since updates
may have been missed while it was down and REST becomes authoritative again.

Also report hvac_action and target_temperature against the effective
(optimistic-aware) mode so the action cannot contradict the reported mode
while a change propagates.

Co-authored-by: anthropic/claude-opus-5
thatguygriff merged commit d6e935e8d7 into main 2026-09-19 12:53:51 +00:00
Sign in to join this conversation.