# Per-Integration Gap Audit: `nut` (Network UPS Tools) > Status legend, method, and sizing: see [`docs/03-quality-scale-audit-rubric.md`](../03-quality-scale-audit-rubric.md). > Snapshot findings; πŸ” items must be re-verified against `dev` before implementation. ## 1. Snapshot | Field | Value | |---|---| | Domain | `nut` | | Category | UPS monitoring / power management | | IoT class | `local_polling` | | Backing library | `aionut` (async NUT client) | | Platforms | `sensor`, `button` (device commands), `switch`/outlet control where supported πŸ” | | Declared `quality_scale` | none (unrated) πŸ” | | Code owners | Multiple active owners listed in `CODEOWNERS` (this integration has seen sustained maintainer investment) πŸ” | | Popularity band (M1 dataset) | High β€” UPS monitoring is a staple of self-hosted deployments | | Issue-volume band (M1 dataset) | Medium β€” themes: sensor availability flapping when UPS drops off NUT server, credential errors, missing/unknown NUT variables | **Why selected:** very widely deployed, credential-bearing, and already structurally modern (it has had recent maintainer attention), which means our work is mostly *finishing* the quality-scale checklist rather than re-architecting β€” exactly the kind of low-risk, high-completion-rate target the strategy favors. It is also a strong candidate to push past Bronze toward **Silver/Gold** within this program. ## 2. Architecture summary The integration connects to a NUT server (`upsd`) and maps NUT variables (`battery.charge`, `ups.status`, `input.voltage`, …) to sensors via a static variableβ†’description mapping, using a `DataUpdateCoordinator`. Device commands (`beeper.disable`, `test.battery.start`, outlet on/off) are exposed as buttons/switches where the UPS advertises support. Username/password are optional, depending on `upsd` configuration. ## 3. Gap audit | # | Dimension | Status | Evidence / notes | |---|---|---|---| | D1 | Config flow completeness | βœ… Met πŸ” | Config flow with host/port/credentials and UPS alias selection when the server exposes multiple UPSes; duplicate prevention present. No discovery β€” N/A (NUT is not advertised; some NAS devices run it but there is no reliable beacon). Verify a `reconfigure` step exists; if not, it is a cheap Gold-track add. | | D2 | Reauth & credentials | 🟑 Partial πŸ” | Credentials optional. Verify reauth flow exists and that runtime auth errors from `aionut` raise `ConfigEntryAuthFailed`. M1 sampling suggested reauth exists for this integration; confirm coverage of the no-credentials β†’ credentials-now-required transition (common after users harden `upsd.users`). | | D3 | Runtime data & typing | 🟑 Partial πŸ” | Verify `runtime_data` adoption; if still `hass.data[DOMAIN]`, migrate. Annotations are good; candidate for `.strict-typing` inclusion with modest cleanup. | | D4 | Update strategy | βœ… Met | Coordinator-based polling with sensible interval. Confirm `PARALLEL_UPDATES = 0` on sensor platform πŸ” | | D5 | Diagnostics & repairs | 🟑 Partial πŸ” | Diagnostics support was added historically β€” verify it still covers: redacted entry data, full NUT variable dump, supported command list. If present, audit redaction (serial numbers are fine to keep; credentials must be redacted). No repair issues; consider one: "UPS no longer reported by NUT server" instead of silent unavailability. | | D6 | Entity naming & device registry | 🟑 Partial | Device info populated from `device.mfr`/`device.model`/`device.serial`. Verify `has_entity_name` + `translation_key` across all sensor descriptions; the sensor description table is large and historically mixed translated and literal names πŸ”. Buttons for device commands should use translated names keyed by command. | | D7 | Error handling & availability | 🟑 Partial | Setup failure paths are mapped to `ConfigEntryNotReady`. Gap: when a single UPS disappears from a multi-UPS server, entities should go unavailable with a clear reason; M1 issues show "Unknown" states instead. Exception translations not in place for button/switch command failures. | | D8 | Test coverage | 🟑 Partial | Config-flow coverage is strong; sensor mapping covered through fixture dumps for several UPS models. Gaps: snapshot tests not universal across platforms, command (button/switch) failure-path tests, reauth-trigger test. | | D9 | Documentation & strings | 🟑 Partial | `data_description` coverage incomplete; docs lack a "which variables become which entities" reference table and a troubleshooting section for `upsd` ACL errors (top issue theme). | | D10 | Quality-scale declaration | ❌ Gap πŸ” | No declared tier at snapshot. Given the integration's structural health, declare Bronze immediately after verification pass, with a realistic path to **Silver** inside this program and Gold items enumerated as `todo`. | ## 4. Detailed findings ### D7 β€” Availability semantics (highest-impact gap) The NUT data model makes partial failure common: the NUT server is reachable, but a given UPS is stale (`Data stale` error) or removed. Today this surfaces as unknown states or update errors that flap the whole coordinator. Plan: catch per-UPS stale/ not-found errors in `_async_update_data`, mark the coordinator failed with a translation-backed `UpdateFailed` carrying the NUT error string, and document the behavior. This addresses the largest recurring issue cluster. ### D5 β€” Diagnostics audit Even if `diagnostics.py` exists, our rubric requires verifying: (a) `async_redact_data` used with an explicit `TO_REDACT` set including `CONF_PASSWORD`, `CONF_USERNAME`; (b) the raw variable dump is included (it is the single most useful triage artifact for "missing sensor" reports); (c) a diagnostics test snapshot exists. ## 5. Work items | ID | Title | Dimensions | Effort | Depends on | |---|---|---|---|---| | NUT-01 | Verification pass: reauth, diagnostics, runtime_data, parallel updates (audit-fix PR for whichever sub-items fail) | D2, D3, D4, D5 | S–M | β€” | | NUT-02 | Per-UPS stale/not-found handling with translation-backed `UpdateFailed`; availability tests | D7, D8 | M | NUT-01 | | NUT-03 | Entity naming/translation_key sweep across sensor description table + buttons (no unique_id changes) | D6 | M | β€” | | NUT-04 | Snapshot tests for all platforms from at least 3 real UPS fixture dumps; command failure-path tests | D8 | M | NUT-03 | | NUT-05 | Translation-backed exceptions for command entities (`HomeAssistantError` + keys) | D7, D9 | S | β€” | | NUT-06 | `data_description` strings + docs: variableβ†’entity table, `upsd` ACL troubleshooting (docs repo PR) | D9 | S | β€” | | NUT-07 | Declare `quality_scale: bronze` (then `silver` once NUT-02/05 merge) + `quality_scale.yaml` | D10 | S | NUT-01, NUT-02, NUT-04, NUT-05 | | NUT-08 | (Stretch, Gold-track) `reconfigure` flow for host/port if absent | D1 | S | NUT-01 | Target tier: **Silver declared**, Gold checklist filed with honest `todo`s. ## 6. Risks & coordination notes - This integration has active, engaged code owners. Per the engagement plan (docs/04): open a tracking issue *first* proposing the checklist, link the audit, and let owners veto/re-order items before any PR. - The sensor description table is the merge-conflict hot zone (owners add new NUT variables regularly). Keep NUT-03 isolated and rebased promptly. - `aionut` exception taxonomy must be confirmed against the pinned version πŸ”.