# DDR-0041: Home Assistant Integration Runtime Split (Hub + Coordinator) and CoordinatorEntity Adoption

Status: Accepted  
Date: 2026-01-13  
Related ADRs: ADR-0121, ADR-0120, ADR-0123, DDR-0038, DDR-0039

## Context

The E27 integration is event-driven and must avoid duplicated refresh loops and
direct device I/O from entities. Home Assistant’s DataUpdateCoordinator (DUC)
provides a standard fan-out pattern for many entities consuming a single shared
snapshot.

Earlier HA code concentrated multiple responsibilities in hub.py, including
event dispatch and snapshot update mechanics, and entities sometimes consulted
hub/client internals.

## Decision

Implement a two-layer HA runtime model per config entry (per panel):

1) `hub.py` (thin integration-owned facade)
- Owns Elke27Client lifecycle: connect, disconnect, reconnect orchestration.
- Exposes stable async methods for coordinator/entities:
  - `get_snapshot()`
  - `refresh_csm()`
  - `refresh_domain_config(domain)`
  - `subscribe_typed(callback)` / `unsubscribe_typed(callback)` (wrappers)
- Does not implement polling loops or entity fan-out.
- Does not call `async_set_updated_data`.

2) `coordinator.py` (DataUpdateCoordinator)
- Subscribes to library typed events via hub.
- Maintains `coordinator.data` as the single authoritative snapshot tree.
- On CSM-driven invalidation events, schedules selective refreshes by calling
  hub refresh primitives.
- Uses debounced/coalesced domain refresh:
  - merges multiple triggers into a pending domain set
  - avoids redundant refresh calls under event storms
- Pushes updates to entities via `async_set_updated_data(snapshot)`.

Entities:
- Subclass `CoordinatorEntity`.
- Read exclusively from `coordinator.data` (including definition metadata per
  DDR-0039).
- Never call the library client directly.
- Call hub methods only for user-initiated actions/services.

## Rationale

- Aligns with HA best practices for fan-out and availability handling.
- Keeps protocol complexity out of entities and out of DUC polling loops.
- Ensures all outbound traffic respects library throttling and single in-flight
  enforcement (ADR-0120, ADR-0111, DDR-0037).
- Provides a clean test boundary: coordinator behavior can be unit-tested with
  event injection and mocked hub refresh methods.

## Consequences

- New module `coordinator.py` exists as the only place that calls
  `async_set_updated_data`.
- Platforms/entities must be rewired to use coordinator data and no longer read
  hub/client internal state.
- Coordinator tests must validate:
  - typed event consumption (DDR-0038)
  - debounced/coalesced selective refresh behavior
  - snapshot propagation to entities
- Diagnostics and entity base helpers must use coordinator snapshot rather than
  hub internals.
