Remove no-longer-needed invalid API key monitor for OpenUV (#85573)
* Remove no-longer-needed invalid API key monitor for OpenUV * Handle re-auth cancellation * Use automatic API status checkpull/85645/head
parent
e7babb4266
commit
ebab2bd0f9
|
@ -31,7 +31,7 @@ from .const import (
|
|||
DOMAIN,
|
||||
LOGGER,
|
||||
)
|
||||
from .coordinator import InvalidApiKeyMonitor, OpenUvCoordinator
|
||||
from .coordinator import OpenUvCoordinator
|
||||
|
||||
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
||||
|
||||
|
@ -45,6 +45,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
entry.data.get(CONF_LONGITUDE, hass.config.longitude),
|
||||
altitude=entry.data.get(CONF_ELEVATION, hass.config.elevation),
|
||||
session=websession,
|
||||
check_status_before_request=True,
|
||||
)
|
||||
|
||||
async def async_update_protection_data() -> dict[str, Any]:
|
||||
|
@ -53,16 +54,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
high = entry.options.get(CONF_TO_WINDOW, DEFAULT_TO_WINDOW)
|
||||
return await client.uv_protection_window(low=low, high=high)
|
||||
|
||||
invalid_api_key_monitor = InvalidApiKeyMonitor(hass, entry)
|
||||
|
||||
coordinators: dict[str, OpenUvCoordinator] = {
|
||||
coordinator_name: OpenUvCoordinator(
|
||||
hass,
|
||||
entry=entry,
|
||||
name=coordinator_name,
|
||||
latitude=client.latitude,
|
||||
longitude=client.longitude,
|
||||
update_method=update_method,
|
||||
invalid_api_key_monitor=invalid_api_key_monitor,
|
||||
)
|
||||
for coordinator_name, update_method in (
|
||||
(DATA_UV, client.uv_index),
|
||||
|
@ -70,16 +69,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
)
|
||||
}
|
||||
|
||||
# We disable the client's request retry abilities here to avoid a lengthy (and
|
||||
# blocking) startup; then, if the initial update is successful, we re-enable client
|
||||
# request retries:
|
||||
client.disable_request_retries()
|
||||
init_tasks = [
|
||||
coordinator.async_config_entry_first_refresh()
|
||||
for coordinator in coordinators.values()
|
||||
]
|
||||
await asyncio.gather(*init_tasks)
|
||||
client.enable_request_retries()
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = coordinators
|
||||
|
|
|
@ -103,7 +103,6 @@ class OpenUvFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
"""Verify the credentials and create/re-auth the entry."""
|
||||
websession = aiohttp_client.async_get_clientsession(self.hass)
|
||||
client = Client(data.api_key, 0, 0, session=websession)
|
||||
client.disable_request_retries()
|
||||
|
||||
try:
|
||||
await client.uv_index()
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
"""Define an update coordinator for OpenUV."""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Awaitable, Callable
|
||||
from typing import Any, cast
|
||||
|
||||
from pyopenuv.errors import InvalidApiKeyError, OpenUvError
|
||||
|
||||
from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||
from homeassistant.helpers.debounce import Debouncer
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
|
@ -18,64 +17,6 @@ from .const import LOGGER
|
|||
DEFAULT_DEBOUNCER_COOLDOWN_SECONDS = 15 * 60
|
||||
|
||||
|
||||
class InvalidApiKeyMonitor:
|
||||
"""Define a monitor for failed API calls (due to bad keys) across coordinators."""
|
||||
|
||||
DEFAULT_FAILED_API_CALL_THRESHOLD = 5
|
||||
|
||||
def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Initialize."""
|
||||
self._count = 1
|
||||
self._lock = asyncio.Lock()
|
||||
self._reauth_flow_manager = ReauthFlowManager(hass, entry)
|
||||
self.entry = entry
|
||||
|
||||
async def async_increment(self) -> None:
|
||||
"""Increment the counter."""
|
||||
async with self._lock:
|
||||
self._count += 1
|
||||
if self._count > self.DEFAULT_FAILED_API_CALL_THRESHOLD:
|
||||
LOGGER.info("Starting reauth after multiple failed API calls")
|
||||
self._reauth_flow_manager.start_reauth()
|
||||
|
||||
async def async_reset(self) -> None:
|
||||
"""Reset the counter."""
|
||||
async with self._lock:
|
||||
self._count = 0
|
||||
self._reauth_flow_manager.cancel_reauth()
|
||||
|
||||
|
||||
class ReauthFlowManager:
|
||||
"""Define an OpenUV reauth flow manager."""
|
||||
|
||||
def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Initialize."""
|
||||
self.entry = entry
|
||||
self.hass = hass
|
||||
|
||||
@callback
|
||||
def _get_active_reauth_flow(self) -> FlowResult | None:
|
||||
"""Get an active reauth flow (if it exists)."""
|
||||
return next(
|
||||
iter(self.entry.async_get_active_flows(self.hass, {SOURCE_REAUTH})),
|
||||
None,
|
||||
)
|
||||
|
||||
@callback
|
||||
def cancel_reauth(self) -> None:
|
||||
"""Cancel a reauth flow (if appropriate)."""
|
||||
if reauth_flow := self._get_active_reauth_flow():
|
||||
LOGGER.debug("API seems to have recovered; canceling reauth flow")
|
||||
self.hass.config_entries.flow.async_abort(reauth_flow["flow_id"])
|
||||
|
||||
@callback
|
||||
def start_reauth(self) -> None:
|
||||
"""Start a reauth flow (if appropriate)."""
|
||||
if not self._get_active_reauth_flow():
|
||||
LOGGER.debug("Multiple API failures in a row; starting reauth flow")
|
||||
self.entry.async_start_reauth(self.hass)
|
||||
|
||||
|
||||
class OpenUvCoordinator(DataUpdateCoordinator):
|
||||
"""Define an OpenUV data coordinator."""
|
||||
|
||||
|
@ -86,11 +27,11 @@ class OpenUvCoordinator(DataUpdateCoordinator):
|
|||
self,
|
||||
hass: HomeAssistant,
|
||||
*,
|
||||
entry: ConfigEntry,
|
||||
name: str,
|
||||
latitude: str,
|
||||
longitude: str,
|
||||
update_method: Callable[[], Awaitable[dict[str, Any]]],
|
||||
invalid_api_key_monitor: InvalidApiKeyMonitor,
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
super().__init__(
|
||||
|
@ -106,7 +47,7 @@ class OpenUvCoordinator(DataUpdateCoordinator):
|
|||
),
|
||||
)
|
||||
|
||||
self._invalid_api_key_monitor = invalid_api_key_monitor
|
||||
self._entry = entry
|
||||
self.latitude = latitude
|
||||
self.longitude = longitude
|
||||
|
||||
|
@ -115,10 +56,18 @@ class OpenUvCoordinator(DataUpdateCoordinator):
|
|||
try:
|
||||
data = await self.update_method()
|
||||
except InvalidApiKeyError as err:
|
||||
await self._invalid_api_key_monitor.async_increment()
|
||||
raise UpdateFailed(str(err)) from err
|
||||
raise ConfigEntryAuthFailed("Invalid API key") from err
|
||||
except OpenUvError as err:
|
||||
raise UpdateFailed(str(err)) from err
|
||||
|
||||
await self._invalid_api_key_monitor.async_reset()
|
||||
# OpenUV uses HTTP 403 to indicate both an invalid API key and an API key that
|
||||
# has hit its daily/monthly limit; both cases will result in a reauth flow. If
|
||||
# coordinator update succeeds after a reauth flow has been started, terminate
|
||||
# it:
|
||||
if reauth_flow := next(
|
||||
iter(self._entry.async_get_active_flows(self.hass, {SOURCE_REAUTH})),
|
||||
None,
|
||||
):
|
||||
self.hass.config_entries.flow.async_abort(reauth_flow["flow_id"])
|
||||
|
||||
return cast(dict[str, Any], data["result"])
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"name": "OpenUV",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/openuv",
|
||||
"requirements": ["pyopenuv==2022.04.0"],
|
||||
"requirements": ["pyopenuv==2023.01.0"],
|
||||
"codeowners": ["@bachya"],
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["pyopenuv"],
|
||||
|
|
|
@ -1803,7 +1803,7 @@ pyoctoprintapi==0.1.9
|
|||
pyombi==0.1.10
|
||||
|
||||
# homeassistant.components.openuv
|
||||
pyopenuv==2022.04.0
|
||||
pyopenuv==2023.01.0
|
||||
|
||||
# homeassistant.components.opnsense
|
||||
pyopnsense==0.2.0
|
||||
|
|
|
@ -1283,7 +1283,7 @@ pynzbgetapi==0.2.0
|
|||
pyoctoprintapi==0.1.9
|
||||
|
||||
# homeassistant.components.openuv
|
||||
pyopenuv==2022.04.0
|
||||
pyopenuv==2023.01.0
|
||||
|
||||
# homeassistant.components.opnsense
|
||||
pyopnsense==0.2.0
|
||||
|
|
Loading…
Reference in New Issue