A major update — recovery now tracks and reports its own result, the guard shows an honest blind state and surfaces config problems in Repairs, plus a handful of edge-case fixes. Read the breaking changes before updating.
⚠️ Breaking changes
- Status attribute
target→recover_driver. Any dashboard or template readingstate_attr('sensor.<guard>_status', 'target')must switch torecover_driver. - Health Check off is no longer a blind "assume success". With the check off, the recovery driver's own result now decides the attempt: a PoE port that doesn't come back online, or a recovery action that sets
recover_failed, is a failed attempt (retry → escalate). Existinghealth_check-off guards behave more strictly.
✨ New
blindstatus. When a guard can't read its health (source unavailable, template render error) it now showsblindinstead of a staleok— matching the Health entity. No recovery is triggered (unknown is never a fault); it returns took/suspectonce health reads again.- Recovery result tracking. New status attributes:
fail_count,last_fail,last_recover_driver_result(the driver's owngood/failedverdict, distinct from the guard state) andlast_recover_driver_time. A recovery action can set therecover_failedvariable to report a failed repair. - Config-health in Repairs. Misconfigured guards/ports now surface in Settings → Repairs, event-driven and self-clearing: a blind guard (missing/disabled health entity), a health template that reads only missing entities or references a missing one, an invalid recovery action, a PoE port with no id, or a PoE port whose actuator/status entity is missing.
🐛 Fixes
- Manual recover during a snooze now lifts the snooze instead of leaving the guard stranded.
- Manual recover while following a linked partner's repair is ignored (no competing double-cycle).
- A PoE port whose actuator raises now ends
failedinstead of stranded onrecovering.
🧹 Internals
RecoveryDriver.recover()returns a verdict. README, architecture and the regression checklist fully aligned; hassfest + ruff clean.