diff --git a/homeassistant/components/duotecno/cover.py b/homeassistant/components/duotecno/cover.py index a6fb49c30e0..0be9daf572b 100644 --- a/homeassistant/components/duotecno/cover.py +++ b/homeassistant/components/duotecno/cover.py @@ -8,11 +8,10 @@ from duotecno.unit import DuoswitchUnit from homeassistant.components.cover import CoverEntity, CoverEntityFeature from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant -from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN -from .entity import DuotecnoEntity +from .entity import DuotecnoEntity, api_call async def async_setup_entry( @@ -54,29 +53,17 @@ class DuotecnoCover(DuotecnoEntity, CoverEntity): """Return if the cover is closing.""" return self._unit.is_closing() + @api_call async def async_open_cover(self, **kwargs: Any) -> None: """Open the cover.""" - try: - await self._unit.open() - except OSError as err: - raise HomeAssistantError( - "Transmit for the open_cover packet failed" - ) from err + await self._unit.open() + @api_call async def async_close_cover(self, **kwargs: Any) -> None: """Close the cover.""" - try: - await self._unit.close() - except OSError as err: - raise HomeAssistantError( - "Transmit for the close_cover packet failed" - ) from err + await self._unit.close() + @api_call async def async_stop_cover(self, **kwargs: Any) -> None: """Stop the cover.""" - try: - await self._unit.stop() - except OSError as err: - raise HomeAssistantError( - "Transmit for the stop_cover packet failed" - ) from err + await self._unit.stop() diff --git a/homeassistant/components/duotecno/entity.py b/homeassistant/components/duotecno/entity.py index 5715593ad2d..d38d52a0d26 100644 --- a/homeassistant/components/duotecno/entity.py +++ b/homeassistant/components/duotecno/entity.py @@ -1,8 +1,13 @@ """Support for Velbus devices.""" from __future__ import annotations +from collections.abc import Awaitable, Callable, Coroutine +from functools import wraps +from typing import Any, Concatenate, ParamSpec, TypeVar + from duotecno.unit import BaseUnit +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity import Entity @@ -35,3 +40,25 @@ class DuotecnoEntity(Entity): async def _on_update(self) -> None: """When a unit has an update.""" self.async_write_ha_state() + + +_T = TypeVar("_T", bound="DuotecnoEntity") +_P = ParamSpec("_P") + + +def api_call( + func: Callable[Concatenate[_T, _P], Awaitable[None]] +) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, None]]: + """Catch command exceptions.""" + + @wraps(func) + async def cmd_wrapper(self: _T, *args: _P.args, **kwargs: _P.kwargs) -> None: + """Wrap all command methods.""" + try: + await func(self, *args, **kwargs) + except OSError as exc: + raise HomeAssistantError( + f"Error calling {func.__name__} on entity {self.entity_id}" + ) from exc + + return cmd_wrapper diff --git a/homeassistant/components/duotecno/light.py b/homeassistant/components/duotecno/light.py index da288b6cbe0..9aee4513fca 100644 --- a/homeassistant/components/duotecno/light.py +++ b/homeassistant/components/duotecno/light.py @@ -6,11 +6,10 @@ from duotecno.unit import DimUnit from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant -from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN -from .entity import DuotecnoEntity +from .entity import DuotecnoEntity, api_call async def async_setup_entry( @@ -40,6 +39,7 @@ class DuotecnoLight(DuotecnoEntity, LightEntity): """Return the brightness of the light.""" return int((self._unit.get_dimmer_state() * 255) / 100) + @api_call async def async_turn_on(self, **kwargs: Any) -> None: """Instruct the light to turn on.""" if (val := kwargs.get(ATTR_BRIGHTNESS)) is not None: @@ -48,18 +48,9 @@ class DuotecnoLight(DuotecnoEntity, LightEntity): else: # restore state val = None - try: - await self._unit.set_dimmer_state(val) - except OSError as err: - raise HomeAssistantError( - "Transmit for the set_dimmer_state packet failed" - ) from err + await self._unit.set_dimmer_state(val) + @api_call async def async_turn_off(self, **kwargs: Any) -> None: """Instruct the light to turn off.""" - try: - await self._unit.set_dimmer_state(0) - except OSError as err: - raise HomeAssistantError( - "Transmit for the set_dimmer_state packet failed" - ) from err + await self._unit.set_dimmer_state(0) diff --git a/homeassistant/components/duotecno/switch.py b/homeassistant/components/duotecno/switch.py index a9921de85d3..63bab750543 100644 --- a/homeassistant/components/duotecno/switch.py +++ b/homeassistant/components/duotecno/switch.py @@ -6,11 +6,10 @@ from duotecno.unit import SwitchUnit from homeassistant.components.switch import SwitchEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant -from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN -from .entity import DuotecnoEntity +from .entity import DuotecnoEntity, api_call async def async_setup_entry( @@ -35,16 +34,12 @@ class DuotecnoSwitch(DuotecnoEntity, SwitchEntity): """Return true if the switch is on.""" return self._unit.is_on() + @api_call async def async_turn_on(self, **kwargs: Any) -> None: """Instruct the switch to turn on.""" - try: - await self._unit.turn_on() - except OSError as err: - raise HomeAssistantError("Transmit for the turn_on packet failed") from err + await self._unit.turn_on() + @api_call async def async_turn_off(self, **kwargs: Any) -> None: """Instruct the switch to turn off.""" - try: - await self._unit.turn_off() - except OSError as err: - raise HomeAssistantError("Transmit for the turn_off packet failed") from err + await self._unit.turn_off()