Skip to content

Fix fan turn-on behavior to ensure AUTO mode is correctly set#130

Open
laurensdehoorne wants to merge 21 commits into
michaelarnauts:masterfrom
laurensdehoorne:master
Open

Fix fan turn-on behavior to ensure AUTO mode is correctly set#130
laurensdehoorne wants to merge 21 commits into
michaelarnauts:masterfrom
laurensdehoorne:master

Conversation

@laurensdehoorne

Copy link
Copy Markdown

Ensures ComfoConnect fan switches to AUTO when turned on from off by forcing a Manual → Auto mode sequence, fixing sync issues with the official app.

Updated fan control logic to ensure AUTO mode is set before turning on the fan.
Refactor fan control logic to ensure AUTO mode is set correctly before turning on the fan. Removed debug logging statements for cleaner code.
@laurensdehoorne

Copy link
Copy Markdown
Author
  • boost mode option OFF

laurensdehoorne and others added 17 commits April 28, 2026 12:43
README updated with two additions:

Boost mode control listed as a feature (it was implemented but not documented)
Availability tracking mentioned (entities go unavailable on connection loss and recover automatically)
The internal bug fixes (deprecated hass.loop, @callback misuse, boost option type mismatch, analog inputs disabled by default) are implementation details not worth surfacing in user-facing docs.
get_boost() in aiocomfoconnect can raise AttributeError when the bridge
returns an unexpected/empty response (e.g. device temporarily
unavailable), crashing async_update for the boost mode select entity.
Catch it and report None so the state goes unknown instead of spamming
the log.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
balance_mode and boost_timeout have no sensor, so they poll the bridge
directly via RMI requests. When the bridge fails to answer in time these
raised AioComfoConnectTimeout (and occasionally AttributeError),
crashing async_update and flooding the log.

Catch both in async_update and keep the last known value instead.
Replaces the narrower AttributeError handling in _get_boost_option.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The four device-info requests (version, model, firmware, name) issued
right after connect were unguarded. On a flaky bridge a timeout there
crashed setup entirely instead of letting HA retry. Wrap them and raise
ConfigEntryNotReady on timeout/disconnect, cleaning up the stored bridge.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Previously a polled select kept its last value indefinitely on timeout,
showing a stale state. Track consecutive failures and set the entity
unavailable after MAX_UPDATE_FAILURES (3) in a row; reset and restore
availability on the next successful poll.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
calculate_airflow_constraints returns None or a (possibly empty) list of
constraint names. The old mapping turned the common "no constraints"
case into an empty string (blank in HA) and showed only the first
constraint. Join all active constraints and render "None" when there are
none, so the sensor always has a visible value.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The bridge device used its serial number (e.g. DEM0125281474) as the
display name, which reads like a demo unit. Use "ComfoConnect LAN C
Bridge" as the name and store the serial in the dedicated serial_number
field.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
get_comfocool_mode() returns a bool, so the ComfoCool select's
get_value_fn set current_option to True/False (an invalid option) on the
forced startup update before the push sensor corrected it. Map the bool
to ComfoCoolMode.AUTO/OFF via a helper, mirroring _get_boost_option.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The ComfoControl app shows the ventilation level (1/2/3) and airflow,
which the integration hid. Add a "Fan speed level" sensor from
SENSOR_FAN_SPEED_MODE (0=away, 1/2/3) and enable the existing supply/
exhaust airflow (m3/h) and fan speed (rpm) sensors by default.

Registering PDO 65 in both the fan and the new sensor is safe:
register_sensor is idempotent and the update dispatcher fans out to all
listeners.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The operating-mode PDO is only pushed on change and occasionally carries
a transient value that is neither -1 (auto) nor 1 (manual). _handle_mode_
update defaulted those to manual, so the preset flickered auto->manual->
auto, and after a restart it stayed None/stale until the next push.

- Ignore unrecognized operating-mode values instead of forcing manual.
- Read the authoritative mode via get_mode() once at startup
  (update_before_add already forces a single async_update), guarded
  against bridge timeouts.

Verified on the live unit: supply fan runs steadily ~1667 rpm while the
preset/percentage were flapping, confirming these were reporting
artifacts, not real state.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two errors seen on the live bridge:

1. "Task was destroyed but it is pending!" (85x): ComfoConnect.connect()
   already runs its own internal reconnect loop, but send_keepalive
   called connect() again on every timeout, spawning duplicate reconnect
   loops and read tasks that got orphaned. The keepalive now only probes
   the bridge and reports availability; the library reconnects itself.

2. ComfoConnectNotAllowed during a polled get_boost() crashed the select
   update once (transient NOT_ALLOWED reply during a reconnect). Catch
   the base ComfoConnectError in select and fan async_update so any
   transient protocol error skips the update instead of erroring.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ports two fixes from upstream (michaelarnauts), adapted to the pinned
aiocomfoconnect==0.1.15 (the latest release):

- fan: wrap set_speed()/set_mode() so a disconnect/timeout or protocol
  error (e.g. RMI_ERROR) raised while the user changes speed or preset is
  surfaced as a HomeAssistantError notification instead of an unhandled
  exception. Upstream imported ComfoConnectNotConnected, which does not
  exist in 0.1.15; use AioComfoConnectNotConnected/ComfoConnectError.
- config_flow: add the missing "invalid_host" error string to
  strings.json and translations/en.json so the "no bridge found" error is
  shown to the user instead of being swallowed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sensors and binary sensors are push-only: they get a value only when the
bridge pushes the PDO. Rarely-changing PDOs (energy totals, filter days,
analog inputs, season flags) aren't re-sent until they actually change,
so at a fresh boot those entities stayed "unknown" for a long time, and
most have no RMI getter to read on demand.

Use RestoreSensor / RestoreEntity to restore the last known value on
startup, so entities show their previous value immediately and the next
push refreshes it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
After a bridge reconnect, _unhold_sensors emits the bridge's cached
initial values, which can be stale (observed: temperature profile
reverting from "cool" to "normal", corrected only ~3 min later on the
next push). The keepalive does not always observe these short reconnects,
so an availability-triggered refresh alone is not enough.

- Poll all selects via their authoritative RMI getter on a slow interval
  (SCAN_INTERVAL = 5 min). Sensor-backed selects keep their push updates
  for responsiveness; the poll corrects any stale pushed value.
- Also force an immediate RMI refresh on the unavailable->available
  transition, for faster recovery when a reconnect is noticed.

This also lowers RMI load for the previously 30s-polled boost/balance
selects.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The bridge emits a spurious 0 for many sensors right after a reconnect
(observed: fan duty 43%->0, ventilation power 35W->0, outside temp
dipping to 0 every hour and skewing a statistics average). Sensors have
no RMI getter so they cannot self-correct like the selects do.

Add an ignore_zero flag on the sensor description: drop a pushed 0
(keeping the last value) where 0 is implausible. Auto-enabled for
temperature/humidity device classes; set explicitly for fan duty,
ventilation power (current + total) and days-to-replace-filter. Preheater
power is deliberately excluded (0 W is correct in summer).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

1 participant