You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
`_expand_entity()` resolves `light.*` groups and `group.*` helper groups to individual `light.` entities. Uses `_get_state()` directly for attribute access.
120
120
121
-
### Service Registration Loop
121
+
### Service Registration
122
122
123
-
Services registered via loop with lambda capture for cleanup:
123
+
Services are registered individually in `async_setup()` (not `async_setup_entry()`). This ensures they persist across config entry reloads. Each handler resolves the active entry at call time via `_get_loaded_entry()`:
| Sensor Platform |`sensor.py`|196| Preset status sensor entities |
163
163
164
164
### 3.3 Dependency Graph
165
165
@@ -213,6 +213,10 @@ class LightControllerData:
213
213
type LightControllerConfigEntry = ConfigEntry[LightControllerData]
214
214
```
215
215
216
+
**Response Building** — The `_service_response()` helper constructs standardized response dicts for all non-controller service responses (errors, preset operations, create/delete results). It mirrors the `OperationResult.to_dict()` structure, ensuring a consistent response schema regardless of whether the response originates from the controller or a service handler.
217
+
218
+
**Optional String Helper** — `_get_optional_str()` retrieves optional string parameters, treating empty strings as `None`. Used for parameters where the absence of a value is semantically different from an empty string (e.g., effect names).
219
+
216
220
#### Service Schema Validation
217
221
All service inputs are validated through Voluptuous schemas at the HA service layer boundary. Once data passes schema validation, internal code trusts it without redundant checks.
218
222
@@ -271,7 +275,9 @@ The `ensure_state()` method implements a linear pipeline:
271
275
│ ├─ _send_commands_per_target() (transition on first attempt only)
272
276
│ ├─ await asyncio.sleep(delay)
273
277
│ ├─ _verify_light() for each target
274
-
│ └─ Filter: keep only targets not yet SUCCESS or UNAVAILABLE
│ └─ Filter at batch level: if ANY target in a batch failed,
280
+
│ retry the ENTIRE batch (minus unavailable entities)
275
281
│
276
282
6. Result Assembly
277
283
│
@@ -294,6 +300,14 @@ Result: 2 service calls instead of 3
294
300
295
301
For ON targets, grouping uses a composite key of `(brightness_pct, rgb_color, color_temp_kelvin, effect, transition)`. For OFF targets, all entities are batched into a single `turn_off` call regardless of original settings.
296
302
303
+
#### Batch-Level Retry
304
+
305
+
During the retry loop, `_build_dispatch_batches()` reconstructs the batch groupings used during dispatch. Verification is then applied at batch granularity: if **any** target in a batch fails verification, the **entire batch** is re-sent (excluding unavailable entities). This avoids sending partial groups, which could cause visual inconsistencies when lights in the same batch should have identical settings.
306
+
307
+
#### Logbook Integration
308
+
309
+
`_log_to_logbook()` writes operation results to Home Assistant's logbook service. Logbook entries are written for failures (always) and successes (when `log_success` is enabled). Calls are non-blocking (`blocking=False`) to avoid delaying the response.
310
+
297
311
#### Verification Logic
298
312
299
313
Verification checks are layered:
@@ -312,7 +326,7 @@ This prevents false failures for lights that don't support the requested color m
312
326
### 4.3 Preset Manager (`preset_manager.py`)
313
327
314
328
#### Purpose
315
-
Manages the full lifecycle of presets: creation, storage, retrieval, activation, and deletion.
329
+
Manages presets: creation, storage, retrieval, activation, and deletion. Lookup by ID or name is provided via `find_preset()`, which first tries by ID then falls back to case-insensitive name matching.
0 commit comments