Conversation
…lently When one ESP32 proxies two BRC1H pairs over BLE, the back-to-back SET_OPERATION_MODE (600 ms) and SET_SETTING_STATUS (200 ms) we emit from control() occasionally have the second chunk silently dropped by one of the pulses. SET_OPERATION_MODE goes through (the LCD switches to the requested mode icon), but SET_SETTING_STATUS does not, so the unit stays physically off. Polling 5–15 s later reports status=0 and flips climate::mode to OFF in HA, surprising the user who just turned it on. The other paired BRC1H on the same ESP works fine in the same window. The existing BLE_SEND_MAX_RETRIES loop in query_() only retries the raw chunk write; if all 5 writes succeed at the link layer but the pulse-side parser silently rejects, we never know. Add a higher-level status retry: stash the last requested status byte in last_set_status_byte_ on every control() that emits SET_SETTING_STATUS. In parse_cb_, on CMD_GET_SETTING_STATUS readback within STATUS_RETRY_WINDOW_MS (30 s after control()) — if cur_status_.status disagrees with our intent, re-send SET_SETTING_STATUS once more, up to MAX_STATUS_RETRIES (2). Patch the local cur_status_.status so the downstream switch doesn't flip climate to OFF based on the stale readback. Caveats: - If the user toggled the unit on the BRC1H buttons within the same 30 s window, we'd fight back briefly. Acceptable: the user usually notices and either the second retry settles or a fresh control() resets the guard. - 30 s window is conservative; in practice a single retry resolves it within the same poll cycle.
…lently Symmetric counterpart to the SET_SETTING_STATUS retry: when one ESP32 proxies two BRC1H pairs over BLE, the back-to-back SET_OPERATION_MODE (600 ms) emitted from control() occasionally has its chunk silently dropped by one of the pulses. The unit stays in the previous mode (typically FAN_ONLY left over from an earlier scene), and 5-15 s later the next CMD_GET_OPERATION_MODE poll reports the stale byte and flips climate::mode in HA back to that value — surprising the user who just picked COOL/HEAT/AUTO in HomeKit or HA. Concrete trigger seen here: scene-restore at the end of an airing routine left both BRC1H pairs in FAN_ONLY. Forty minutes later, a HomeKit COOL command went through to the active climate entity (state flipped to cool in HA), but the SET_OPERATION_MODE chunk for that device was dropped at the pulse, while SET_SETTING_STATUS landed. The next poll round read mode=0 (FAN_ONLY) from the BRC1H and parse_cb_ snapped HA back to fan_only within 24 s. The existing sticky-FAN_ONLY guard only protects against the inverse (pulse reverting FAN_ONLY back to a main mode), so it didn't help here. We already remember last_set_status_byte_ / last_set_ms_ from the SETTING_STATUS retry path. Stash the requested raw mode byte too (255 = unset, used for the OFF case where no SET_OPERATION_MODE is sent), and on a CMD_GET_OPERATION_MODE readback mismatch within STATUS_RETRY_WINDOW_MS, re-issue SET_OPERATION_MODE up to MAX_STATUS_RETRIES times. Pretend the readback matched what we requested so the switch below does not snap climate::mode to the stale value while we wait for the retry to land — the next poll round will confirm or trigger another retry. Byte 0 and 5 are treated as equivalent by madoka_mode_byte_equiv — both decode to CLIMATE_MODE_FAN_ONLY downstream, and the BRC1H is free to switch between them on its own. Reuses the same window and retry cap as the SETTING_STATUS guard.
|
To use the changes from this PR as an external component, add the following to your ESPHome configuration YAML file: external_components:
- source: github://Petapton/esphome@pull/15/head
components: [daikin_madoka]
refresh: 1h(Added by the PR bot) |
|
Can't reproduce. Please at least provide logs. |
|
Same situation as #14 (this is its symmetric counterpart): the rig it reproduced on — two BRC1H on one ESP32 — is no longer assembled. I moved each BRC1H onto its own ESP32 next to the panel to get past chronic Concrete case I saw at the time: both pulses left in How to reproduce / catch it on a two-on-one-ESP rig: I'll revisit single-ESP-two-devices later and capture it then. Keeping it open — close if you'd prefer. |
Summary
Symmetric counterpart to the SETTING_STATUS retry from #14: re-issues
SET_OPERATION_MODEwhen the BRC1H reports a stale mode byte after a recent HA-side mode change, instead of lettingparse_cb_flipclimate::modeback to the stale value.Stacks on #14 — please merge #14 first (or treat this as
#14 + this commit). The diff itself is+46 / -1and only touchesdaikin_madoka.{cpp,h}.Why
When one ESP32 proxies two BRC1H pairs over BLE, the back-to-back
SET_OPERATION_MODE(600 ms) emitted fromcontrol()occasionally has its chunk silently dropped by one of the pulses. The unit stays in its previous mode, and 5–15 s later the nextCMD_GET_OPERATION_MODEpoll reports the stale byte.parse_cb_then flipsclimate::modein HA back to that value — surprising the user who just picked COOL/HEAT/AUTO.Concrete repro seen today:
COOLcommand went through;SET_SETTING_STATUSlanded (unit physically on), butSET_OPERATION_MODEchunk was silently dropped at the pulse.coolfor ~24 s, then the next poll returnedmode=0(FAN_ONLY) andparse_cb_snapped HA back tofan_only.The existing sticky-FAN_ONLY guard only protects against the inverse (pulse reverting FAN_ONLY back to a main mode), so it didn't help here.
BLE_SEND_MAX_RETRIESonly retries the link-layer chunk write — if the link accepts but the pulse parser silently rejects, we never know.How
last_set_mode_byte_on everycontrol()call that emittedSET_OPERATION_MODE(255 means "unset", used for the OFF case where we don't send a mode byte).parse_cb_forCMD_GET_OPERATION_MODE, after readingcur_status_.mode, compare it tolast_set_mode_byte_withinSTATUS_RETRY_WINDOW_MS.SET_OPERATION_MODE(up toMAX_STATUS_RETRIES), then setcur_status_.mode = last_set_mode_byte_so the downstream switch doesn't snapclimate::modeto the stale value while the retry is in flight. The next poll round confirms or triggers another retry.madoka_mode_byte_equivtreats byte 0 and byte 5 as equivalent — both decode toCLIMATE_MODE_FAN_ONLYdownstream, and the BRC1H is free to switch between them.Reuses the same
STATUS_RETRY_WINDOW_MS = 30000/MAX_STATUS_RETRIES = 2constants as #14.Test plan
Verified on a dual-BRC1H ESP32 proxy that reproduced the original bug:
fan_only → cool, observed for 41 s —coolstable, no rollback (pre-fix: rolled back tofan_onlyafter 24 s).cool → heat, observed for 31 s —heatstable.heat → fan_only, observed for 31 s —fan_onlystable.fan_only → cool, observed for 30 s —coolstable.control()paths still go through to the BRC1H (LCD mode icon flips immediately, fan setpoints land).last_set_mode_byte_ = 255keeps the guard disabled, no spurious retries.🤖 Generated with Claude Code