Treat 404 as auth error and persist refreshed tokens
Validate / Hassfest validation (pull_request) Failing after 20s
Validate / HACS validation (pull_request) Failing after 0s

The DKN Cloud NA API returns 404 (not 401) when tokens are expired,
which caused the integration to silently fail with UpdateFailed
indefinitely instead of attempting a token refresh or triggering
reauth. Refreshed tokens were also only held in memory, so they
were lost on Home Assistant restart.

- Add 404 to auth_error_statuses on is_logged_in, refresh_access_token,
  and fetch_installations.
- Persist refreshed access + refresh tokens back to the config entry
  after each successful coordinator update.
- Skip the entry reload listener for token-only option updates to
  avoid a reload loop on every refresh.
- Log API 4xx responses at WARNING with the body so failures are
  visible in HA logs without enabling debug logging.
This commit is contained in:
2026-05-26 09:58:57 -03:00
parent aa81851243
commit dd1cc76580
3 changed files with 53 additions and 8 deletions
+17 -3
View File
@@ -9,9 +9,18 @@ from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .api import DknCloudNaClient
from .const import CONF_REFRESH_TOKEN, CONF_USER_TOKEN, DOMAIN, LOGGER
from .const import (
CONF_EXPOSE_PII,
CONF_REFRESH_TOKEN,
CONF_SCAN_INTERVAL,
CONF_USER_TOKEN,
DOMAIN,
LOGGER,
)
from .coordinator import DknCoordinator
_RELOAD_KEYS = {CONF_SCAN_INTERVAL, CONF_EXPOSE_PII}
PLATFORMS: list[Platform] = [
Platform.CLIMATE,
Platform.SENSOR,
@@ -44,6 +53,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.data[DOMAIN][entry.entry_id] = {
"coordinator": coordinator,
"client": client,
"_prev_options": dict(entry.options),
}
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
@@ -74,5 +84,9 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def _async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Reload entry when options change."""
await hass.config_entries.async_reload(entry.entry_id)
"""Reload entry when user-facing options change (not token refreshes)."""
domain_data = hass.data.get(DOMAIN, {}).get(entry.entry_id, {})
prev_opts = domain_data.get("_prev_options", {})
if any(entry.options.get(k) != prev_opts.get(k) for k in _RELOAD_KEYS):
await hass.config_entries.async_reload(entry.entry_id)
domain_data["_prev_options"] = dict(entry.options)