Streamline exception handling in Guardian (#107053)

This commit is contained in:
Aaron Bach 2024-01-06 15:56:19 -07:00 committed by GitHub
parent fce869248c
commit 50fbcaf20f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 45 deletions

View File

@ -2,9 +2,9 @@
from __future__ import annotations
import asyncio
from collections.abc import Awaitable, Callable, Coroutine
from collections.abc import Callable, Coroutine
from dataclasses import dataclass
from typing import Any, cast
from typing import Any
from aioguardian import Client
from aioguardian.errors import GuardianError
@ -302,9 +302,7 @@ class PairedSensorManager:
entry=self._entry,
client=self._client,
api_name=f"{API_SENSOR_PAIRED_SENSOR_STATUS}_{uid}",
api_coro=lambda: cast(
Awaitable, self._client.sensor.paired_sensor_status(uid)
),
api_coro=lambda: self._client.sensor.paired_sensor_status(uid),
api_lock=self._api_lock,
valve_controller_uid=self._entry.data[CONF_UID],
)

View File

@ -5,7 +5,6 @@ from collections.abc import Awaitable, Callable
from dataclasses import dataclass
from aioguardian import Client
from aioguardian.errors import GuardianError
from homeassistant.components.button import (
ButtonDeviceClass,
@ -15,12 +14,12 @@ from homeassistant.components.button import (
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import GuardianData, ValveControllerEntity, ValveControllerEntityDescription
from .const import API_SYSTEM_DIAGNOSTICS, DOMAIN
from .util import convert_exceptions_to_homeassistant_error
@dataclass(frozen=True, kw_only=True)
@ -96,14 +95,10 @@ class GuardianButton(ValveControllerEntity, ButtonEntity):
self._client = data.client
@convert_exceptions_to_homeassistant_error
async def async_press(self) -> None:
"""Send out a restart command."""
try:
async with self._client:
await self.entity_description.push_action(self._client)
except GuardianError as err:
raise HomeAssistantError(
f'Error while pressing button "{self.entity_id}": {err}'
) from err
async with self._client:
await self.entity_description.push_action(self._client)
async_dispatcher_send(self.hass, self.coordinator.signal_reboot_requested)

View File

@ -6,17 +6,16 @@ from dataclasses import dataclass
from typing import Any
from aioguardian import Client
from aioguardian.errors import GuardianError
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import GuardianData, ValveControllerEntity, ValveControllerEntityDescription
from .const import API_VALVE_STATUS, API_WIFI_STATUS, DOMAIN
from .util import convert_exceptions_to_homeassistant_error
ATTR_AVG_CURRENT = "average_current"
ATTR_CONNECTED_CLIENTS = "connected_clients"
@ -139,34 +138,16 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
"""Return True if entity is on."""
return self.entity_description.is_on_fn(self.coordinator.data)
@convert_exceptions_to_homeassistant_error
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the switch off."""
if not self._attr_is_on:
return
try:
async with self._client:
await self.entity_description.off_fn(self._client)
except GuardianError as err:
raise HomeAssistantError(
f'Error while turning "{self.entity_id}" off: {err}'
) from err
self._attr_is_on = False
self.async_write_ha_state()
async with self._client:
await self.entity_description.off_fn(self._client)
await self.coordinator.async_request_refresh()
@convert_exceptions_to_homeassistant_error
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the switch on."""
if self._attr_is_on:
return
try:
async with self._client:
await self.entity_description.on_fn(self._client)
except GuardianError as err:
raise HomeAssistantError(
f'Error while turning "{self.entity_id}" on: {err}'
) from err
self._attr_is_on = True
self.async_write_ha_state()
async with self._client:
await self.entity_description.on_fn(self._client)
await self.coordinator.async_request_refresh()

View File

@ -2,22 +2,29 @@
from __future__ import annotations
import asyncio
from collections.abc import Awaitable, Callable, Iterable
from collections.abc import Callable, Coroutine, Iterable
from dataclasses import dataclass
from datetime import timedelta
from typing import Any, cast
from functools import wraps
from typing import TYPE_CHECKING, Any, Concatenate, ParamSpec, TypeVar, cast
from aioguardian import Client
from aioguardian.errors import GuardianError
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import LOGGER
if TYPE_CHECKING:
from . import GuardianEntity
_GuardianEntityT = TypeVar("_GuardianEntityT", bound=GuardianEntity)
DEFAULT_UPDATE_INTERVAL = timedelta(seconds=30)
SIGNAL_REBOOT_REQUESTED = "guardian_reboot_requested_{0}"
@ -68,7 +75,7 @@ class GuardianDataUpdateCoordinator(DataUpdateCoordinator[dict]):
entry: ConfigEntry,
client: Client,
api_name: str,
api_coro: Callable[..., Awaitable],
api_coro: Callable[..., Coroutine[Any, Any, dict[str, Any]]],
api_lock: asyncio.Lock,
valve_controller_uid: str,
) -> None:
@ -112,3 +119,27 @@ class GuardianDataUpdateCoordinator(DataUpdateCoordinator[dict]):
self.hass, self.signal_reboot_requested, async_reboot_requested
)
)
_P = ParamSpec("_P")
@callback
def convert_exceptions_to_homeassistant_error(
func: Callable[Concatenate[_GuardianEntityT, _P], Coroutine[Any, Any, Any]],
) -> Callable[Concatenate[_GuardianEntityT, _P], Coroutine[Any, Any, None]]:
"""Decorate to handle exceptions from the Guardian API."""
@wraps(func)
async def wrapper(
entity: _GuardianEntityT, *args: _P.args, **kwargs: _P.kwargs
) -> None:
"""Wrap the provided function."""
try:
await func(entity, *args, **kwargs)
except GuardianError as err:
raise HomeAssistantError(
f"Error while calling {func.__name__}: {err}"
) from err
return wrapper