Skip to content

Commit

Permalink
Streamline exception handling in Guardian (#107053)
Browse files Browse the repository at this point in the history
  • Loading branch information
bachya committed Jan 6, 2024
1 parent fce8692 commit 50fbcaf
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 45 deletions.
8 changes: 3 additions & 5 deletions homeassistant/components/guardian/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -302,9 +302,7 @@ async def async_pair_sensor(self, uid: str) -> None:
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],
)
Expand Down
13 changes: 4 additions & 9 deletions homeassistant/components/guardian/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from dataclasses import dataclass

from aioguardian import Client
from aioguardian.errors import GuardianError

from homeassistant.components.button import (
ButtonDeviceClass,
Expand All @@ -15,12 +14,12 @@
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)
Expand Down Expand Up @@ -96,14 +95,10 @@ def __init__(

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)
37 changes: 9 additions & 28 deletions homeassistant/components/guardian/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,16 @@
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"
Expand Down Expand Up @@ -139,34 +138,16 @@ def is_on(self) -> bool:
"""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()
37 changes: 34 additions & 3 deletions homeassistant/components/guardian/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down Expand Up @@ -68,7 +75,7 @@ def __init__(
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:
Expand Down Expand Up @@ -112,3 +119,27 @@ def async_reboot_requested() -> None:
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

0 comments on commit 50fbcaf

Please sign in to comment.