What's Changed
Integration
-
Backup is offered only where the system can create one. The
"Create backup" button and the Home Assistant backup agent now follow
the central's backup capability. Visible change for existing users:
on a CCU that is not an OpenCCU (original CCU2/CCU3, debmatic) the
button and the backup agent are no longer offered, because such a
system cannot create a backup. An OpenCCU keeps both, as before. The
firmware update entity offers its "back up before installing" option
on the same condition and is unchanged for the direct-CCU backend. -
Creating a backup is refused where the system cannot create one. The
create_ccu_backupaction and the configuration panel's backup command
now follow the same backup capability as the button and the backup
agent: on such a system they answer with a clear "does not support
backups" error instead of asking the CCU or daemon for a backup it cannot
produce. -
openccu-loom (Beta): setup, reauthentication and reconfigure follow
what the daemon offers — pairing instead of pasting a token, connecting
through an openccu-lite box, and backup / system-update capabilities taken
from the daemon. Loom details stay out of this changelog while the backend
is Beta. -
Fix: diagnostics no longer carry the openccu-loom API token or the
openccu-lite box token; both are redacted now. -
Fix: a
connection_failedrepair stayed after the connection was healthy again. Moving the CCU to a different host is the ordinary way there — the interfaces fail, their repairs appear, the entry is reconfigured onto the new address — and afterwards the repairs stayed visible next to connection sensors readingon, with every device operating normally. Nothing short of deleting and re-adding the integration took them back.The repair is raised from a
connection_stateevent and withdrawn by the opposite one, and that one is published only for an interface the central's connection state tracker actually holds (CentralConnectionState.remove_issue). That tracker belongs to the central, so it is rebuilt empty with every setup of the config entry — a reload, a reconfigure, a restart. Whatever a previous session left in the issue registry, which does survive all three, therefore had nobody left to withdraw it.The startup cleanup that already handled the other transient repair types covers
connectionandcallbacknow. It runs before the central starts, so an interface that is still down raises its repair again within seconds.clientis deliberately left out of it: a fresh client always transitions to CONNECTED, and that transition withdraws the repair on its own. -
Callback repairs are withdrawn by sweeping the issue registry instead of rebuilding every id from
{instance_name}-{interface}. That composition is the aiohomematic interface id; on the openccu-loom backend the daemon names the leading component itself, so a callback repair raised there was never addressed by the id the integration built for it. Both halves go through one helper now —support.get_issue_idcomposes the id, and the sweep matches the prefix that helper produces — so the two cannot drift apart -
Four issue types the startup cleanup carried as legacy (
pending_pong_mismatch,unknown_pong_mismatch,interface_not_reachable,xmlrpc_server_receives_no_events) are gone from it. They could never have matched anything: repairs of that generation were keyed{interface_event_type}-{interface_id}, so the id carries no entry id in front — which the cleanup requires as a prefix — a hyphen where it looks for an underscore, and the interface event type where the list names the translation key -
Fix: the button blueprints asked the CCU for direct links on every keypress, even with the warning switched off.
Warn if direct connections exist in CCUdefaulted to on, so an automation that had never stored a value for that input inherited it — the option looked off in the automation editor while the CCU round trip ran ahead of every action. That made the guard added in 2.11.0 ineffective for exactly the automations that never touched the option, and it is the delay reported in #3402: the action can never be faster than the CCU answers, and that answer time swings from milliseconds to seconds.The option defaults to off now in all five blueprints that carry it (2-, 6-, 8-button, key ring remote control, and the 6-button one in
blueprints/community), and its description says what enabling it costs. Re-import the blueprints to pick this up. An automation that has an explicittruestored keeps it — the default only applies where nothing was ever saved — so if you want the warning gone there, switch the option off and save -
Fix: adding a delayed device reported success when it failed. The "Add delayed device" repair swallowed every error from adding the device, closed the issue and finished as if the device had been added. It now ends with a "could not be added" message carrying the backend's error.
-
Breaking: Home Assistant 2026.9 or newer is required (was 2026.8). The integration builds its schemas with probatio, the validation library Home Assistant ships from 2026.9 on and types its own helpers against from 2026.10. Validation behaves as before: since 2026.9 Home Assistant aliases
voluptuousto probatio's compatibility shim at startup, so the integration's schemas andInvalidwere already probatio objects at runtime — importing probatio directly makes the integration independent of that alias and type-checks its schemas against the types Home Assistant declares. The repair flow's steps are typed withRepairsFlowResult, the result typeRepairsFlowdeclares
Config Panel
- The signal quality table shows "RSSI Device" and "RSSI Peer" as two sortable columns instead of a single "RSSI" column filled from
RSSI_DEVICEonly (frontend#112, reported in frontend#111). Classic BidCos-RF devices often report no validRSSI_DEVICEwhileRSSI_PEERis valid, so the column stayed at "—" although the device detail view showed a value. The two values measure opposite directions of the radio link and are shown side by side
Dependencies
Bump openccu-loom-client to 2026.10.8
- For the openccu-loom backend (Beta) only; no runtime effect on the direct-CCU backend, where the client is not loaded
Bump aiohomematic-config to 2026.10.1
- Raises its dependency floors to the aiohomematic and openccu-data versions pinned here; no functional change
Bump aiohomematic to 2026.10.4
-
Fix: classic BidCos-RF devices showed no battery state. Devices such as HM-Sec-SCo or HM-CC-RT-DN report their battery as
LOWBATinstead ofLOW_BAT, so the device's low-battery state stayed unknown and the config panel's signal quality view showed none. Both spellings are read now -
Fix: a reload left a command throttle worker running. Stopping an interface client now also stops its command throttle, so reloading the integration no longer leaves a
CommandThrottle-*task pending -
A backend that reports no product is identified as an unknown system instead of an original CCU. Neither enables backup or system update, so no feature appears or disappears
Bump openccu-data to 2026.9.1
- The release adds CCU WebUI device images to the repository tree only — they are not part of the Python package — so nothing reaches this integration at runtime. Bumped so the manifest pin and the test requirement name the same version
Bump aiohomematic to 2026.10.2
-
Fix: a device no longer loses every channel after a firmware update or a re-pairing. When the CCU announced a changed device (
updateDevice,readdedDevice,replaceDevice), aiohomematic dropped the device and its channels from its caches and fetched only the device level back. The device was then built without channels: in Home Assistant it kept itsupdateentity and lost every state — window contacts, switches and heating groups alike. The loss was persisted and survived every restart, so a CCU update that announces changed device descriptions could strip a large part of an installation at once. The device is now fetched together with its channels -
An already damaged cache repairs itself. Before devices are created from the cache, every device whose channels are not fully cached is fetched again from the CCU and the repaired cache is saved, so an installation that already lost channels recovers on the next start without deleting any cache file
-
aiohomematic develops and tests against the godevccu simulator instead of pydevccu now; its backend enum value for the simulator changed from
PyDevCCUtoGoDevCCU. The integration does not reference that value, so nothing changes at runtime
Bump aiohomematic to 2026.9.4
-
Fix: BidCos-RF data points stayed on
restoredafter a start. The ReGa bulk fetch is the only source of an initial value on the interfaces without a per-parametergetValuefallback (BidCos-RF, VirtualDevices, CUxD, CCU-Jack), and its snapshot is taken once duringstart_clients()and expires afterMAX_CACHE_AGE. Its consumer for any channel but 0 is the integration adding its entities, which happens after the platforms have been forwarded — in a real installation reliably later than that, so the snapshot was gone by then and the data point stayed unset for good. Covers were the visible case, because a shutter reports nothing until it is moved:HM-LC-Bl1PBU-FMblinds sat atvalue_state=restoredwithcurrent_position: 0until they were operated by hand. The init path refreshes an expired snapshot now instead of giving up; thegetValuefallback stays disabled -
Fix: battery and diagnostic data points stayed on
restoredafter a start. Parameters on the init ignore list (LOW_BAT,LOWBAT,OPERATING_VOLTAGE,DUTY_CYCLE,DUTYCYCLE, theERROR_*,RSSI_*and*_ERRORpatterns, and every data point ofHmIP-SWSD*/HmIP-SWD) skip the per-parametergetValueon purpose so a battery-powered device is not woken, which leaves the bulk snapshot as their only source — and this path read it without refreshing it first. Unlike a cover'sLEVELthese do not recover on their own:LOW_BATis sent only when it changes, so a device that reported a low battery before the start kept showing a normal one until the battery was replaced. This affected every interface, not only those without the fallback -
Fix: a fresh snapshot for one interface skipped the others.
CentralDataCache.load()left the loop over all clients withreturninstead ofcontinuewhen it hit a recently refreshed interface, so every client behind it was never loaded -
Fix:
changed_within_seconds()ignored whole days. It readtimedelta.seconds, which drops the day part, so a change from exactly 24 h ago counted as recent -
MAX_CACHE_AGEis 15 s instead of 10 s. It governs the lifetime of the central data cache, the device details cache refresh guard (MAX_CACHE_AGE / 3) and the default staleness window ofchanged_within_seconds()
Bump aiohomematic to 2026.9.3
-
Fix: devices stayed unavailable after a reconnect. A device that became reachable again while the connection to the CCU was down — the CCU restarts and resets the flag, or the device recovers while the proxy is gone — stayed unavailable in Home Assistant for as long as the central ran. Events flowed and commands worked; the entities of that device remained greyed out until the integration was reloaded or
homematicip_local.force_device_availabilitywas applied by hand.The CCU announces
UNREACHonly when the value changes, so that recovery transition is never delivered after such an outage. Nothing closed the gap afterwards:UN_REACHandSTICKY_UN_REACHare hidden parameters and carryDataPointUsage.NO_CREATE, which is exactly what the recovery data load skips — it iterates the readable generic data points — while the one path that does read them over RPC runs only for newly created devices.The connection recovery re-reads the channel 0
VALUESparamset of every device on the interface now and appliesUN_REACH,STICKY_UN_REACHandCONFIG_PENDINGthrough the regular event path, in the staged data load as well as in the circuit-breaker recovery. It reads them withgetParamsetrather than the per-parametergetValuefallback, because that fallback is skipped on BidCos-RF, VirtualDevices, CUxD and CCU-Jack — building on it would have produced a fix that does nothing on four of six interfaces. A read that fails, or a paramset that does not carry the parameter, leaves the data point untouched instead of defaulting it: a boolean without a value falls back tofalse, so a swallowed read error would have reported every unreachable device as reachable