From 6ddc2193d6aceb1db143ad5785e2a9b313162c60 Mon Sep 17 00:00:00 2001 From: Sid <27780930+autinerd@users.noreply.github.com> Date: Mon, 26 May 2025 15:05:11 +0200 Subject: [PATCH] Add exception handler and exception translations to eheimdigital (#145476) * Add exception handler and exception translations to eheimdigital * Fix --------- Co-authored-by: Joostlek --- .../components/eheimdigital/climate.py | 39 ++++++++----------- .../components/eheimdigital/entity.py | 26 ++++++++++++- .../components/eheimdigital/light.py | 23 +++++------ .../components/eheimdigital/number.py | 3 +- .../eheimdigital/quality_scale.yaml | 2 +- .../components/eheimdigital/select.py | 3 +- .../components/eheimdigital/strings.json | 5 +++ .../components/eheimdigital/switch.py | 4 +- homeassistant/components/eheimdigital/time.py | 3 +- 9 files changed, 65 insertions(+), 43 deletions(-) diff --git a/homeassistant/components/eheimdigital/climate.py b/homeassistant/components/eheimdigital/climate.py index 3cde9e758cd..7ac0b897507 100644 --- a/homeassistant/components/eheimdigital/climate.py +++ b/homeassistant/components/eheimdigital/climate.py @@ -4,7 +4,7 @@ from typing import Any from eheimdigital.device import EheimDigitalDevice from eheimdigital.heater import EheimDigitalHeater -from eheimdigital.types import EheimDigitalClientError, HeaterMode, HeaterUnit +from eheimdigital.types import HeaterMode, HeaterUnit from homeassistant.components.climate import ( PRESET_NONE, @@ -20,12 +20,11 @@ from homeassistant.const import ( UnitOfTemperature, ) from homeassistant.core import HomeAssistant -from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .const import HEATER_BIO_MODE, HEATER_PRESET_TO_HEATER_MODE, HEATER_SMART_MODE from .coordinator import EheimDigitalConfigEntry, EheimDigitalUpdateCoordinator -from .entity import EheimDigitalEntity +from .entity import EheimDigitalEntity, exception_handler # Coordinator is used to centralize the data updates PARALLEL_UPDATES = 0 @@ -83,34 +82,28 @@ class EheimDigitalHeaterClimate(EheimDigitalEntity[EheimDigitalHeater], ClimateE self._attr_unique_id = self._device_address self._async_update_attrs() + @exception_handler async def async_set_preset_mode(self, preset_mode: str) -> None: """Set the preset mode.""" - try: - if preset_mode in HEATER_PRESET_TO_HEATER_MODE: - await self._device.set_operation_mode( - HEATER_PRESET_TO_HEATER_MODE[preset_mode] - ) - except EheimDigitalClientError as err: - raise HomeAssistantError from err + if preset_mode in HEATER_PRESET_TO_HEATER_MODE: + await self._device.set_operation_mode( + HEATER_PRESET_TO_HEATER_MODE[preset_mode] + ) + @exception_handler async def async_set_temperature(self, **kwargs: Any) -> None: """Set a new temperature.""" - try: - if ATTR_TEMPERATURE in kwargs: - await self._device.set_target_temperature(kwargs[ATTR_TEMPERATURE]) - except EheimDigitalClientError as err: - raise HomeAssistantError from err + if ATTR_TEMPERATURE in kwargs: + await self._device.set_target_temperature(kwargs[ATTR_TEMPERATURE]) + @exception_handler async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: """Set the heating mode.""" - try: - match hvac_mode: - case HVACMode.OFF: - await self._device.set_active(active=False) - case HVACMode.AUTO: - await self._device.set_active(active=True) - except EheimDigitalClientError as err: - raise HomeAssistantError from err + match hvac_mode: + case HVACMode.OFF: + await self._device.set_active(active=False) + case HVACMode.AUTO: + await self._device.set_active(active=True) def _async_update_attrs(self) -> None: if self._device.temperature_unit == HeaterUnit.CELSIUS: diff --git a/homeassistant/components/eheimdigital/entity.py b/homeassistant/components/eheimdigital/entity.py index c0f91a4b798..d28087ef82e 100644 --- a/homeassistant/components/eheimdigital/entity.py +++ b/homeassistant/components/eheimdigital/entity.py @@ -1,12 +1,15 @@ """Base entity for EHEIM Digital.""" from abc import ABC, abstractmethod -from typing import TYPE_CHECKING +from collections.abc import Callable, Coroutine +from typing import TYPE_CHECKING, Any, Concatenate from eheimdigital.device import EheimDigitalDevice +from eheimdigital.types import EheimDigitalClientError from homeassistant.const import CONF_HOST from homeassistant.core import callback +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -51,3 +54,24 @@ class EheimDigitalEntity[_DeviceT: EheimDigitalDevice]( """Update attributes when the coordinator updates.""" self._async_update_attrs() super()._handle_coordinator_update() + + +def exception_handler[_EntityT: EheimDigitalEntity[EheimDigitalDevice], **_P]( + func: Callable[Concatenate[_EntityT, _P], Coroutine[Any, Any, Any]], +) -> Callable[Concatenate[_EntityT, _P], Coroutine[Any, Any, None]]: + """Decorate AirGradient calls to handle exceptions. + + A decorator that wraps the passed in function, catches AirGradient errors. + """ + + async def handler(self: _EntityT, *args: _P.args, **kwargs: _P.kwargs) -> None: + try: + await func(self, *args, **kwargs) + except EheimDigitalClientError as error: + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="communication_error", + translation_placeholders={"error": str(error)}, + ) from error + + return handler diff --git a/homeassistant/components/eheimdigital/light.py b/homeassistant/components/eheimdigital/light.py index 2725315befd..7960e956859 100644 --- a/homeassistant/components/eheimdigital/light.py +++ b/homeassistant/components/eheimdigital/light.py @@ -4,7 +4,7 @@ from typing import Any from eheimdigital.classic_led_ctrl import EheimDigitalClassicLEDControl from eheimdigital.device import EheimDigitalDevice -from eheimdigital.types import EheimDigitalClientError, LightMode +from eheimdigital.types import LightMode from homeassistant.components.light import ( ATTR_BRIGHTNESS, @@ -15,13 +15,12 @@ from homeassistant.components.light import ( LightEntityFeature, ) from homeassistant.core import HomeAssistant -from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.util.color import brightness_to_value, value_to_brightness from .const import EFFECT_DAYCL_MODE, EFFECT_TO_LIGHT_MODE from .coordinator import EheimDigitalConfigEntry, EheimDigitalUpdateCoordinator -from .entity import EheimDigitalEntity +from .entity import EheimDigitalEntity, exception_handler BRIGHTNESS_SCALE = (1, 100) @@ -88,6 +87,7 @@ class EheimDigitalClassicLEDControlLight( """Return whether the entity is available.""" return super().available and self._device.light_level[self._channel] is not None + @exception_handler async def async_turn_on(self, **kwargs: Any) -> None: """Turn on the light.""" if ATTR_EFFECT in kwargs: @@ -96,22 +96,17 @@ class EheimDigitalClassicLEDControlLight( if ATTR_BRIGHTNESS in kwargs: if self._device.light_mode == LightMode.DAYCL_MODE: await self._device.set_light_mode(LightMode.MAN_MODE) - try: - await self._device.turn_on( - int(brightness_to_value(BRIGHTNESS_SCALE, kwargs[ATTR_BRIGHTNESS])), - self._channel, - ) - except EheimDigitalClientError as err: - raise HomeAssistantError from err + await self._device.turn_on( + int(brightness_to_value(BRIGHTNESS_SCALE, kwargs[ATTR_BRIGHTNESS])), + self._channel, + ) + @exception_handler async def async_turn_off(self, **kwargs: Any) -> None: """Turn off the light.""" if self._device.light_mode == LightMode.DAYCL_MODE: await self._device.set_light_mode(LightMode.MAN_MODE) - try: - await self._device.turn_off(self._channel) - except EheimDigitalClientError as err: - raise HomeAssistantError from err + await self._device.turn_off(self._channel) def _async_update_attrs(self) -> None: light_level = self._device.light_level[self._channel] diff --git a/homeassistant/components/eheimdigital/number.py b/homeassistant/components/eheimdigital/number.py index 7fd0c6b6de7..03f27aa82df 100644 --- a/homeassistant/components/eheimdigital/number.py +++ b/homeassistant/components/eheimdigital/number.py @@ -26,7 +26,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .coordinator import EheimDigitalConfigEntry, EheimDigitalUpdateCoordinator -from .entity import EheimDigitalEntity +from .entity import EheimDigitalEntity, exception_handler PARALLEL_UPDATES = 0 @@ -182,6 +182,7 @@ class EheimDigitalNumber( self._attr_unique_id = f"{self._device_address}_{description.key}" @override + @exception_handler async def async_set_native_value(self, value: float) -> None: return await self.entity_description.set_value_fn(self._device, value) diff --git a/homeassistant/components/eheimdigital/quality_scale.yaml b/homeassistant/components/eheimdigital/quality_scale.yaml index a56551a14f6..fa13c9bf4ca 100644 --- a/homeassistant/components/eheimdigital/quality_scale.yaml +++ b/homeassistant/components/eheimdigital/quality_scale.yaml @@ -58,7 +58,7 @@ rules: entity-device-class: done entity-disabled-by-default: done entity-translations: done - exception-translations: todo + exception-translations: done icon-translations: todo reconfiguration-flow: todo repair-issues: todo diff --git a/homeassistant/components/eheimdigital/select.py b/homeassistant/components/eheimdigital/select.py index 9311eb01ecc..41ab13e3bd4 100644 --- a/homeassistant/components/eheimdigital/select.py +++ b/homeassistant/components/eheimdigital/select.py @@ -13,7 +13,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .coordinator import EheimDigitalConfigEntry, EheimDigitalUpdateCoordinator -from .entity import EheimDigitalEntity +from .entity import EheimDigitalEntity, exception_handler PARALLEL_UPDATES = 0 @@ -94,6 +94,7 @@ class EheimDigitalSelect( self._attr_unique_id = f"{self._device_address}_{description.key}" @override + @exception_handler async def async_select_option(self, option: str) -> None: return await self.entity_description.set_value_fn(self._device, option) diff --git a/homeassistant/components/eheimdigital/strings.json b/homeassistant/components/eheimdigital/strings.json index 89f802c9d6d..77cffb4a709 100644 --- a/homeassistant/components/eheimdigital/strings.json +++ b/homeassistant/components/eheimdigital/strings.json @@ -101,5 +101,10 @@ "name": "Night start time" } } + }, + "exceptions": { + "communication_error": { + "message": "An error occurred while communicating with the EHEIM Digital hub: {error}" + } } } diff --git a/homeassistant/components/eheimdigital/switch.py b/homeassistant/components/eheimdigital/switch.py index de23feff322..2a4f3df3861 100644 --- a/homeassistant/components/eheimdigital/switch.py +++ b/homeassistant/components/eheimdigital/switch.py @@ -10,7 +10,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .coordinator import EheimDigitalConfigEntry, EheimDigitalUpdateCoordinator -from .entity import EheimDigitalEntity +from .entity import EheimDigitalEntity, exception_handler # Coordinator is used to centralize the data updates PARALLEL_UPDATES = 0 @@ -58,10 +58,12 @@ class EheimDigitalClassicVarioSwitch( self._async_update_attrs() @override + @exception_handler async def async_turn_off(self, **kwargs: Any) -> None: await self._device.set_active(active=False) @override + @exception_handler async def async_turn_on(self, **kwargs: Any) -> None: await self._device.set_active(active=True) diff --git a/homeassistant/components/eheimdigital/time.py b/homeassistant/components/eheimdigital/time.py index ae64fad0c92..49834c827b9 100644 --- a/homeassistant/components/eheimdigital/time.py +++ b/homeassistant/components/eheimdigital/time.py @@ -15,7 +15,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .coordinator import EheimDigitalConfigEntry, EheimDigitalUpdateCoordinator -from .entity import EheimDigitalEntity +from .entity import EheimDigitalEntity, exception_handler PARALLEL_UPDATES = 0 @@ -122,6 +122,7 @@ class EheimDigitalTime( self._attr_unique_id = f"{device.mac_address}_{description.key}" @override + @exception_handler async def async_set_value(self, value: time) -> None: """Change the time.""" return await self.entity_description.set_value_fn(self._device, value)