mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 05:07:41 +00:00
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 check
This commit is contained in:
parent
bf67458d83
commit
6a801fc058
@ -31,7 +31,7 @@ from .const import (
|
|||||||
DOMAIN,
|
DOMAIN,
|
||||||
LOGGER,
|
LOGGER,
|
||||||
)
|
)
|
||||||
from .coordinator import InvalidApiKeyMonitor, OpenUvCoordinator
|
from .coordinator import OpenUvCoordinator
|
||||||
|
|
||||||
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
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),
|
entry.data.get(CONF_LONGITUDE, hass.config.longitude),
|
||||||
altitude=entry.data.get(CONF_ELEVATION, hass.config.elevation),
|
altitude=entry.data.get(CONF_ELEVATION, hass.config.elevation),
|
||||||
session=websession,
|
session=websession,
|
||||||
|
check_status_before_request=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_update_protection_data() -> dict[str, Any]:
|
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)
|
high = entry.options.get(CONF_TO_WINDOW, DEFAULT_TO_WINDOW)
|
||||||
return await client.uv_protection_window(low=low, high=high)
|
return await client.uv_protection_window(low=low, high=high)
|
||||||
|
|
||||||
invalid_api_key_monitor = InvalidApiKeyMonitor(hass, entry)
|
|
||||||
|
|
||||||
coordinators: dict[str, OpenUvCoordinator] = {
|
coordinators: dict[str, OpenUvCoordinator] = {
|
||||||
coordinator_name: OpenUvCoordinator(
|
coordinator_name: OpenUvCoordinator(
|
||||||
hass,
|
hass,
|
||||||
|
entry=entry,
|
||||||
name=coordinator_name,
|
name=coordinator_name,
|
||||||
latitude=client.latitude,
|
latitude=client.latitude,
|
||||||
longitude=client.longitude,
|
longitude=client.longitude,
|
||||||
update_method=update_method,
|
update_method=update_method,
|
||||||
invalid_api_key_monitor=invalid_api_key_monitor,
|
|
||||||
)
|
)
|
||||||
for coordinator_name, update_method in (
|
for coordinator_name, update_method in (
|
||||||
(DATA_UV, client.uv_index),
|
(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 = [
|
init_tasks = [
|
||||||
coordinator.async_config_entry_first_refresh()
|
coordinator.async_config_entry_first_refresh()
|
||||||
for coordinator in coordinators.values()
|
for coordinator in coordinators.values()
|
||||||
]
|
]
|
||||||
await asyncio.gather(*init_tasks)
|
await asyncio.gather(*init_tasks)
|
||||||
client.enable_request_retries()
|
|
||||||
|
|
||||||
hass.data.setdefault(DOMAIN, {})
|
hass.data.setdefault(DOMAIN, {})
|
||||||
hass.data[DOMAIN][entry.entry_id] = coordinators
|
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."""
|
"""Verify the credentials and create/re-auth the entry."""
|
||||||
websession = aiohttp_client.async_get_clientsession(self.hass)
|
websession = aiohttp_client.async_get_clientsession(self.hass)
|
||||||
client = Client(data.api_key, 0, 0, session=websession)
|
client = Client(data.api_key, 0, 0, session=websession)
|
||||||
client.disable_request_retries()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await client.uv_index()
|
await client.uv_index()
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
"""Define an update coordinator for OpenUV."""
|
"""Define an update coordinator for OpenUV."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
|
||||||
from collections.abc import Awaitable, Callable
|
from collections.abc import Awaitable, Callable
|
||||||
from typing import Any, cast
|
from typing import Any, cast
|
||||||
|
|
||||||
from pyopenuv.errors import InvalidApiKeyError, OpenUvError
|
from pyopenuv.errors import InvalidApiKeyError, OpenUvError
|
||||||
|
|
||||||
from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntry
|
from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.data_entry_flow import FlowResult
|
from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||||
from homeassistant.helpers.debounce import Debouncer
|
from homeassistant.helpers.debounce import Debouncer
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||||
|
|
||||||
@ -18,64 +17,6 @@ from .const import LOGGER
|
|||||||
DEFAULT_DEBOUNCER_COOLDOWN_SECONDS = 15 * 60
|
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):
|
class OpenUvCoordinator(DataUpdateCoordinator):
|
||||||
"""Define an OpenUV data coordinator."""
|
"""Define an OpenUV data coordinator."""
|
||||||
|
|
||||||
@ -86,11 +27,11 @@ class OpenUvCoordinator(DataUpdateCoordinator):
|
|||||||
self,
|
self,
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
*,
|
*,
|
||||||
|
entry: ConfigEntry,
|
||||||
name: str,
|
name: str,
|
||||||
latitude: str,
|
latitude: str,
|
||||||
longitude: str,
|
longitude: str,
|
||||||
update_method: Callable[[], Awaitable[dict[str, Any]]],
|
update_method: Callable[[], Awaitable[dict[str, Any]]],
|
||||||
invalid_api_key_monitor: InvalidApiKeyMonitor,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize."""
|
"""Initialize."""
|
||||||
super().__init__(
|
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.latitude = latitude
|
||||||
self.longitude = longitude
|
self.longitude = longitude
|
||||||
|
|
||||||
@ -115,10 +56,18 @@ class OpenUvCoordinator(DataUpdateCoordinator):
|
|||||||
try:
|
try:
|
||||||
data = await self.update_method()
|
data = await self.update_method()
|
||||||
except InvalidApiKeyError as err:
|
except InvalidApiKeyError as err:
|
||||||
await self._invalid_api_key_monitor.async_increment()
|
raise ConfigEntryAuthFailed("Invalid API key") from err
|
||||||
raise UpdateFailed(str(err)) from err
|
|
||||||
except OpenUvError as err:
|
except OpenUvError as err:
|
||||||
raise UpdateFailed(str(err)) from 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"])
|
return cast(dict[str, Any], data["result"])
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "OpenUV",
|
"name": "OpenUV",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/openuv",
|
"documentation": "https://www.home-assistant.io/integrations/openuv",
|
||||||
"requirements": ["pyopenuv==2022.04.0"],
|
"requirements": ["pyopenuv==2023.01.0"],
|
||||||
"codeowners": ["@bachya"],
|
"codeowners": ["@bachya"],
|
||||||
"iot_class": "cloud_polling",
|
"iot_class": "cloud_polling",
|
||||||
"loggers": ["pyopenuv"],
|
"loggers": ["pyopenuv"],
|
||||||
|
@ -1822,7 +1822,7 @@ pyoctoprintapi==0.1.9
|
|||||||
pyombi==0.1.10
|
pyombi==0.1.10
|
||||||
|
|
||||||
# homeassistant.components.openuv
|
# homeassistant.components.openuv
|
||||||
pyopenuv==2022.04.0
|
pyopenuv==2023.01.0
|
||||||
|
|
||||||
# homeassistant.components.opnsense
|
# homeassistant.components.opnsense
|
||||||
pyopnsense==0.2.0
|
pyopnsense==0.2.0
|
||||||
|
@ -1305,7 +1305,7 @@ pynzbgetapi==0.2.0
|
|||||||
pyoctoprintapi==0.1.9
|
pyoctoprintapi==0.1.9
|
||||||
|
|
||||||
# homeassistant.components.openuv
|
# homeassistant.components.openuv
|
||||||
pyopenuv==2022.04.0
|
pyopenuv==2023.01.0
|
||||||
|
|
||||||
# homeassistant.components.opnsense
|
# homeassistant.components.opnsense
|
||||||
pyopnsense==0.2.0
|
pyopnsense==0.2.0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user