fix(ha-integration): ausgeschaltete Lampe -> "unavailable" statt Fehlerzustand
War eine Lampe stromlos, wurde der Verbindungsfehler als UpdateFailed gemeldet; HA schob den Config-Entry in SETUP_RETRY und die Integration fiel in den Fehlerzustand. Jetzt wird Connection-Refused/Timeout als NinlightUnavailable gefuehrt: der Coordinator liefert den letzten bekannten State mit available=False, der Entry bleibt geladen und die Entity zeigt "unavailable". Protokollfehler (400, kaputtes JSON) und Auth (401 -> Reauth) verhalten sich unveraendert. Light-Faehigkeiten (RGB/CT, Kelvin-Bereich) sind deshalb dynamische Properties — ein Entry, der mit stromloser Lampe angelegt wurde, erkennt sie, sobald die Lampe antwortet.
This commit is contained in:
@@ -17,6 +17,7 @@ from .client import (
|
||||
NinlightClient,
|
||||
NinlightError,
|
||||
NinlightState,
|
||||
NinlightUnavailable,
|
||||
)
|
||||
from .const import CONF_TOKEN, DEFAULT_PORT, DOMAIN, SCAN_INTERVAL
|
||||
|
||||
@@ -63,6 +64,18 @@ class NinlightCoordinator(DataUpdateCoordinator[NinlightState]):
|
||||
# Only possible when the bulb has `auth_all` set; a wrong token will not fix
|
||||
# itself, so surface a re-auth flow instead of retrying every interval.
|
||||
raise ConfigEntryAuthFailed(str(err)) from err
|
||||
except NinlightUnavailable as err:
|
||||
# A bulb that is switched off or off the WLAN is an expected state, not a
|
||||
# fault. Keep the last known data with `available=False` so the entity shows
|
||||
# "unavailable" and the config entry stays loaded - no error log, no retry
|
||||
# backoff, no reload. The next poll picks the bulb up when it answers again.
|
||||
_LOGGER.debug("%s unreachable (assumed powered off): %s", self.name, err)
|
||||
if isinstance(self.data, NinlightState):
|
||||
return NinlightState.unavailable_copy(self.data)
|
||||
# No successful poll yet (e.g. HA started while the bulb was off): a default
|
||||
# state carries no identity, but the entry still loads cleanly and the
|
||||
# identity fetch below reruns on the first poll that reaches the bulb.
|
||||
return NinlightState.unavailable_copy(NinlightState.from_api({}))
|
||||
except NinlightBadRequest as err:
|
||||
# A poll sends no body, so this can only mean a firmware we do not match.
|
||||
raise UpdateFailed(str(err)) from err
|
||||
|
||||
Reference in New Issue
Block a user