# Quality-Scale Audit: `yeelight` > Audit snapshot per rubric; re-verify against `dev` before execution. ## 1. Integration profile | Attribute | Value | |---|---| | Domain | `yeelight` | | IoT class | `local_push` (music-mode/listener) with polling fallback | | Primary libraries | `yeelight` (python-yeelight, has asyncio support), `async-upnp-client` (SSDP-style discovery on the Yeelight custom protocol) | | Declared `quality_scale` | none declared | | Install base | Top-50 lights integration; large install base in CN/EU | | Issue volume | Medium-high — bulbs flapping unavailable (LAN-control quirks, connection limit of the bulb firmware), effects/flow failures, nightlight-mode entity confusion, discovery misses across VLANs | | CODEOWNERS | Listed; activity intermittent — plan for core-team review fallback (engagement plan §4.2) | **Why selected:** classic "old but loved" integration: heavily used, conceptually simple, but predates modern conventions and has a persistent availability complaint class with a known root cause (bulb firmware allows ~1 concurrent TCP connection and silently drops). ## 2. Rule-by-rule audit ### Bronze | Rule | Status | Evidence / Gap | |---|---|---| | `config-flow` | ✅ | UI flow; discovery via SSDP-like multicast, zeroconf, DHCP, plus homekit-adjacent triggers for some models. | | `test-before-configure` | ✅ | Capabilities fetch before entry creation. | | `unique-config-entry` | ✅ | Bulb ID-based. | | `config-flow-test-coverage` | 🟡 | Wide matrix exists; verify discovery-source branches (DHCP-update path) coverage. | | `entity-unique-id` | ✅ | Bulb-ID derived; nightlight and ambilight sub-entities suffixed. | | `has-entity-name` | 🟡 | Light entities mostly fine; nightlight/ambilight naming uses suffixes — verify `has_entity_name` with proper translated names rather than f-string suffixes. Binary sensor (nightlight) needs check. | | `runtime-data` | 🟡 | Verify `entry.runtime_data`; older `hass.data[DOMAIN][DATA_*]` indirection layer may persist. | | `appropriate-polling` | 🟡 | Listener push + polling fallback; document the interval rationale. Verify polling backs off when the push listener is healthy. | | `entity-event-setup` | ✅ | Listener callbacks bound in `async_added_to_hass`. | | `action-setup` | 🟡 | Custom services (`set_mode`, `start_flow`, `set_color_scene`, …) are entity services on the light platform — correct pattern; verify schema validation errors are translated. | | `common-modules` | 🟡 | Device abstraction in `device.py`; no `coordinator.py` (listener-based — acceptable, document in `quality_scale.yaml` exemption notes). | | `dependency-transparency` | ✅ | PyPI, public source. | | `docs-*` (Bronze set) | 🟡 | Docs cover LAN-control prerequisite well; removal instructions + actions table to template. | | `brands` | ✅ | Present. | ### Silver | Rule | Status | Evidence / Gap | |---|---|---| | `config-entry-unloading` | ✅ | Listener torn down on unload; verify socket close on all paths (reported FD-leak class). | | `reauthentication-flow` | ➖ | No credentials (LAN control toggle is on-device). **Exempt** — record justification in `quality_scale.yaml`. | | `action-exceptions` | 🟡 | `BulbException` on commands sometimes logged not raised; raise translated `HomeAssistantError` ("bulb refused connection — check LAN control / connection limit"). | | `entity-unavailable` | 🟡 | **Key gap.** Availability flaps when the bulb's single-connection limit is hit (e.g., another app connected). Needs: single persistent connection ownership, debounced unavailability (don't flap on one failed poll while listener is alive), tests for the listener-dead+poll-ok matrix. | | `log-when-unavailable` | 🟡 | Reconnect loop logs repeatedly; implement log-once. | | `parallel-updates` | ❌ | Not declared; `1` for the light platform (bulb serializes commands), `0` for listener-driven sensors. | | `integration-owner` | 🟡 | Listed but intermittently active — risk noted. | | `test-coverage` | 🟡 | Large existing suite; gaps around reconnect logic and nightlight-mode switching. | | `docs-configuration-parameters` | 🟡 | Options (model override, nightlight, transitions, save-on-change) partially documented. | ### Gold (selected) | Rule | Status | Notes | |---|---|---| | `diagnostics` | ❌ | **Pulled into scope** (YL-4): model, fw version, capabilities list, listener state, reconnect counters — redact bulb ID/host. Cheap and aids the flapping-availability triage. | | `discovery` / `discovery-update-info` | ✅/🟡 | Multiple sources; verify IP update on rediscovery (bulbs are DHCP-churn-prone — common complaint). | | `entity-category` / `entity-device-class` | 🟡 | Nightlight binary sensor category check; trivial, fold into YL-2. | | `strict-typing` | ❌ | Out of scope. | ## 3. Gap summary and proposed work items | ID | Work item | Rule(s) | Size | Risk | |---|---|---|---|---| | YL-1 | Availability hardening: debounced unavailability, listener-health-aware polling, reconnect log-once, socket-close audit + tests | `entity-unavailable`, `log-when-unavailable`, `appropriate-polling` | M | Medium — behavior change; needs fixture-driven listener simulation | | YL-2 | Naming sweep (nightlight/ambilight via `has_entity_name` + translation keys), entity-category fixes | `has-entity-name` | S | Medium — user-visible names; follow migration guidance | | YL-3 | Translated action exceptions; `PARALLEL_UPDATES` declarations; `runtime_data` migration | `action-exceptions`, `parallel-updates`, `runtime-data` | S | Low | | YL-4 | Add `diagnostics.py` with redaction | `diagnostics` | S | Low | | YL-5 | Verify/fix discovery IP-update on rediscovery + tests | `discovery-update-info` | S | Low | | YL-6 | Coverage to ≥95 % on touched modules; docs (removal, parameters, exemption rationale) | `test-coverage`, `docs-*` | M | Low | | YL-7 | Add `quality_scale.yaml` with reauth exemption; declare Bronze→Silver after YL-1…YL-3, YL-6 | meta | XS | Low | **Target outcome:** unscored → **Silver** + diagnostics, with YL-1 directly targeting the dominant complaint (availability flapping). ## 4. Maintainer-coordination notes - Owner activity is intermittent: open the tracking issue, tag the owner, and after the waiting period defined in engagement plan §4.2 (14 days), request core-team review while keeping the owner cc'd on every PR. Never merge availability-behavior changes without at least one maintainer with Yeelight hardware confirming. - python-yeelight is community-maintained; if YL-1 needs library changes (connection ownership), file upstream first — integration PRs must not vendor workarounds.