|
2 | 2 |
|
3 | 3 | **Project:** Crow/AAP Alarm IP Module for Home Assistant |
4 | 4 | **Integration domain:** `crowipmodule` |
5 | | -**Branch:** `HA2026_09_dev` |
6 | | -**Version:** `2.1.0-beta.1` |
7 | | -**Status:** Pre-release, implementation complete |
8 | | -**Last updated:** 2026-09-21 |
| 5 | +**Branch:** `master` |
| 6 | +**Version:** `2.2.0` |
| 7 | +**Status:** Released |
| 8 | +**Last updated:** 2026-10-10 |
9 | 9 |
|
10 | 10 | --- |
11 | 11 |
|
@@ -49,7 +49,7 @@ Out of scope: |
49 | 49 | | G3 | Stay usable when the panel or network is unavailable | Availability driven by real connection state; supervised reconnect with backoff | |
50 | 50 | | G4 | Report every panel signal the protocol exposes | 16 zones, 2 areas, 8 outputs, 21 system statuses | |
51 | 51 | | G5 | Be diagnosable from a user's Home Assistant instance | Downloadable diagnostics with redaction, plus a "System Status" text sensor | |
52 | | -| G6 | Present as a first-class integration | Local `brand/` assets, UI config flow, 5 translations, device registry grouping | |
| 52 | +| G6 | Present as a first-class integration | Local `brand/` assets, UI config flow, 5 translations, device registry (hub + one zone device) | |
53 | 53 |
|
54 | 54 | ### 2.2 Non-goals |
55 | 55 |
|
@@ -184,15 +184,17 @@ untyped, unnamespaced, and leaks if a platform fails to unload. |
184 | 184 | @dataclass |
185 | 185 | class CrowRuntimeData: |
186 | 186 | controller: CrowIPAlarmPanel |
| 187 | + entry_id: str | None = None # config entry id, to scope registry lookups |
187 | 188 | device_id: str | None = None # device registry id of the hub, for via_device_id |
188 | 189 | firmware: str | None = None # "<version> (<date>)", for sw_version |
189 | 190 | ``` |
190 | 191 |
|
191 | 192 | **Consequences.** |
192 | 193 |
|
193 | | -* ✔ Lifetime is tied to the entry; unload cannot leak. |
194 | | -* ✔ One object carries every shared dependency, so platform setup is a single attribute read. |
195 | | -* ✔ `device_id` gives the zone sub-devices a valid `via_device_id` (see DEC-5). |
| 194 | +* ✔ Lifetime is tied to the entry; unload cannot leak. |
| 195 | +* ✔ One object carries every shared dependency, so platform setup is a single attribute read. |
| 196 | +* ✔ `device_id` gives the zone sub-devices a valid `via_device_id` (see DEC-5). |
| 197 | +* ✔ `entry_id` scopes the entity-registry lookups of the zone alignment (see DEC-7). |
196 | 198 |
|
197 | 199 | ### 5.5 DEC-5 — Register the hub device before forwarding platforms |
198 | 200 |
|
@@ -226,7 +228,33 @@ drops and recover on reconnect. |
226 | 228 | * ✔ Users and automations can distinguish "disarmed" from "unknown". |
227 | 229 | * ✔ `Diagnostics` and the System Status sensor expose `connected` explicitly. |
228 | 230 | * ✘ A brief network blip marks entities unavailable, which some automations treat as a state change. |
| 231 | +### 5.7 DEC-7 — One device for all zone entities |
| 232 | + |
| 233 | +**Context.** Releases 2.1.x gave every zone type its own device (*Crow Alarm Windows*, *Crow Alarm |
| 234 | +Doors*, *Crow Alarm Motion*, ...) so that the type became part of the entity name and entity id. |
| 235 | +That turned out to be a trap: Home Assistant links an entity to a device **only when the entity is |
| 236 | +registered for the very first time**. An entity created while a zone still had another type stayed on |
| 237 | +the device of that former type, and re-configuring the zone in the UI did not move it - the device |
| 238 | +list kept showing a door zone under *Windows* with no way to correct it from the UI. |
| 239 | + |
| 240 | +**Decision.** All zone entities are registered on the single *Crow Alarm Sensors* device |
| 241 | +(`const.IDENTIFIER_ZONE`). The zone type still selects the binary-sensor **device class** (the icon: |
| 242 | +door, window, motion, smoke, ...), but never the device. `binary_sensor._async_align_zone_entities()` |
| 243 | +re-links existing zone entities to that device on every setup, so an installation that has been |
| 244 | +through the split layout is corrected by a restart, and |
| 245 | +`_remove_retired_zone_devices()` deletes the retired per-type devices once nothing is registered on |
| 246 | +them. |
229 | 247 |
|
| 248 | +**Consequences.** |
| 249 | + |
| 250 | +* ✔ There is no second device left for a zone to be stuck on, whatever order zones were configured |
| 251 | + or re-typed in. |
| 252 | +* ✔ Entity ids are never rewritten by the integration: they are stable handles for dashboards and |
| 253 | + automations, and a zone that keeps its id keeps working after the move. |
| 254 | +* ✘ An entity id may still name a device the entity no longer hangs off |
| 255 | + (`binary_sensor.crow_alarm_windows_terrassentur` on *Crow Alarm Sensors*). That is cosmetic; the |
| 256 | + diagnostics `zone_registry` report shows the real device link. |
| 257 | +* ✘ The type is no longer visible in the device name, so the icon and the entity name carry it. |
230 | 258 | --- |
231 | 259 |
|
232 | 260 | ## 6. Runtime view |
@@ -347,7 +375,7 @@ Verified against the real 2026.9.3 sources; each row is asserted by |
347 | 375 | ``` |
348 | 376 | <config>/custom_components/crowipmodule/ |
349 | 377 | ``` |
350 | | -Installed by HACS from GitHub release archive `2.1.0-beta.1` (or the branch), or copied manually. |
| 378 | +Installed by HACS from GitHub release archive `2.2.0` (or the branch), or copied manually. |
351 | 379 | Home Assistant imports the package at startup; the config entry lives in |
352 | 380 | `.storage/core.config_entries`, not in YAML. Per-entry runtime objects are constructed at setup and |
353 | 381 | released at unload. |
|
0 commit comments