From 0535578440275d7b7b44734d76a9a180f6f6f95b Mon Sep 17 00:00:00 2001 From: Maikel Punie Date: Sun, 6 Aug 2023 19:12:19 +0200 Subject: [PATCH] Velbus code cleanup (#97584) * Some cleanup and code improvements for the velbus integration * Comments * More comments * Update homeassistant/components/velbus/entity.py Co-authored-by: G Johansson * More comments --------- Co-authored-by: G Johansson --- .../components/velbus/binary_sensor.py | 7 +++-- homeassistant/components/velbus/button.py | 8 +++--- homeassistant/components/velbus/climate.py | 9 +++---- homeassistant/components/velbus/cover.py | 6 ++++- homeassistant/components/velbus/entity.py | 27 +++++++++++++++++++ homeassistant/components/velbus/light.py | 6 ++++- homeassistant/components/velbus/select.py | 3 ++- homeassistant/components/velbus/switch.py | 4 ++- 8 files changed, 52 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/velbus/binary_sensor.py b/homeassistant/components/velbus/binary_sensor.py index ef0cef938b1..25591cc1cb0 100644 --- a/homeassistant/components/velbus/binary_sensor.py +++ b/homeassistant/components/velbus/binary_sensor.py @@ -18,10 +18,9 @@ async def async_setup_entry( """Set up Velbus switch based on config_entry.""" await hass.data[DOMAIN][entry.entry_id]["tsk"] cntrl = hass.data[DOMAIN][entry.entry_id]["cntrl"] - entities = [] - for channel in cntrl.get_all("binary_sensor"): - entities.append(VelbusBinarySensor(channel)) - async_add_entities(entities) + async_add_entities( + VelbusBinarySensor(channel) for channel in cntrl.get_all("binary_sensor") + ) class VelbusBinarySensor(VelbusEntity, BinarySensorEntity): diff --git a/homeassistant/components/velbus/button.py b/homeassistant/components/velbus/button.py index d75486bab7a..2a0392c48cb 100644 --- a/homeassistant/components/velbus/button.py +++ b/homeassistant/components/velbus/button.py @@ -13,7 +13,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN -from .entity import VelbusEntity +from .entity import VelbusEntity, api_call async def async_setup_entry( @@ -24,10 +24,7 @@ async def async_setup_entry( """Set up Velbus switch based on config_entry.""" await hass.data[DOMAIN][entry.entry_id]["tsk"] cntrl = hass.data[DOMAIN][entry.entry_id]["cntrl"] - entities = [] - for channel in cntrl.get_all("button"): - entities.append(VelbusButton(channel)) - async_add_entities(entities) + async_add_entities(VelbusButton(channel) for channel in cntrl.get_all("button")) class VelbusButton(VelbusEntity, ButtonEntity): @@ -37,6 +34,7 @@ class VelbusButton(VelbusEntity, ButtonEntity): _attr_entity_registry_enabled_default = False _attr_entity_category = EntityCategory.CONFIG + @api_call async def async_press(self) -> None: """Handle the button press.""" await self._channel.press() diff --git a/homeassistant/components/velbus/climate.py b/homeassistant/components/velbus/climate.py index ccdfb3b073b..ecdddd19289 100644 --- a/homeassistant/components/velbus/climate.py +++ b/homeassistant/components/velbus/climate.py @@ -16,7 +16,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN, PRESET_MODES -from .entity import VelbusEntity +from .entity import VelbusEntity, api_call async def async_setup_entry( @@ -27,10 +27,7 @@ async def async_setup_entry( """Set up Velbus switch based on config_entry.""" await hass.data[DOMAIN][entry.entry_id]["tsk"] cntrl = hass.data[DOMAIN][entry.entry_id]["cntrl"] - entities = [] - for channel in cntrl.get_all("climate"): - entities.append(VelbusClimate(channel)) - async_add_entities(entities) + async_add_entities(VelbusClimate(channel) for channel in cntrl.get_all("climate")) class VelbusClimate(VelbusEntity, ClimateEntity): @@ -67,6 +64,7 @@ class VelbusClimate(VelbusEntity, ClimateEntity): """Return the current temperature.""" return self._channel.get_state() + @api_call async def async_set_temperature(self, **kwargs: Any) -> None: """Set new target temperatures.""" if (temp := kwargs.get(ATTR_TEMPERATURE)) is None: @@ -74,6 +72,7 @@ class VelbusClimate(VelbusEntity, ClimateEntity): await self._channel.set_temp(temp) self.async_write_ha_state() + @api_call async def async_set_preset_mode(self, preset_mode: str) -> None: """Set the new preset mode.""" await self._channel.set_preset(PRESET_MODES[preset_mode]) diff --git a/homeassistant/components/velbus/cover.py b/homeassistant/components/velbus/cover.py index 009c4fadfb9..46881fcdcaf 100644 --- a/homeassistant/components/velbus/cover.py +++ b/homeassistant/components/velbus/cover.py @@ -15,7 +15,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN -from .entity import VelbusEntity +from .entity import VelbusEntity, api_call async def async_setup_entry( @@ -81,18 +81,22 @@ class VelbusCover(VelbusEntity, CoverEntity): return 100 - pos return None + @api_call async def async_open_cover(self, **kwargs: Any) -> None: """Open the cover.""" await self._channel.open() + @api_call async def async_close_cover(self, **kwargs: Any) -> None: """Close the cover.""" await self._channel.close() + @api_call async def async_stop_cover(self, **kwargs: Any) -> None: """Stop the cover.""" await self._channel.stop() + @api_call async def async_set_cover_position(self, **kwargs: Any) -> None: """Move the cover to a specific position.""" await self._channel.set_position(100 - kwargs[ATTR_POSITION]) diff --git a/homeassistant/components/velbus/entity.py b/homeassistant/components/velbus/entity.py index 13ecb7febab..46d9f03b4fb 100644 --- a/homeassistant/components/velbus/entity.py +++ b/homeassistant/components/velbus/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 velbusaio.channels import Channel as VelbusChannel +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity import DeviceInfo, Entity from .const import DOMAIN @@ -35,3 +40,25 @@ class VelbusEntity(Entity): async def _on_update(self) -> None: self.async_write_ha_state() + + +_T = TypeVar("_T", bound="VelbusEntity") +_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"Could not execute {func.__name__} service for {self.name}" + ) from exc + + return cmd_wrapper diff --git a/homeassistant/components/velbus/light.py b/homeassistant/components/velbus/light.py index ca00a3134ce..1806c2905e9 100644 --- a/homeassistant/components/velbus/light.py +++ b/homeassistant/components/velbus/light.py @@ -26,7 +26,7 @@ from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN -from .entity import VelbusEntity +from .entity import VelbusEntity, api_call async def async_setup_entry( @@ -63,6 +63,7 @@ class VelbusLight(VelbusEntity, LightEntity): """Return the brightness of the light.""" return int((self._channel.get_dimmer_state() * 255) / 100) + @api_call async def async_turn_on(self, **kwargs: Any) -> None: """Instruct the Velbus light to turn on.""" if ATTR_BRIGHTNESS in kwargs: @@ -83,6 +84,7 @@ class VelbusLight(VelbusEntity, LightEntity): ) await getattr(self._channel, attr)(*args) + @api_call async def async_turn_off(self, **kwargs: Any) -> None: """Instruct the velbus light to turn off.""" attr, *args = ( @@ -113,6 +115,7 @@ class VelbusButtonLight(VelbusEntity, LightEntity): """Return true if the light is on.""" return self._channel.is_on() + @api_call async def async_turn_on(self, **kwargs: Any) -> None: """Instruct the Velbus light to turn on.""" if ATTR_FLASH in kwargs: @@ -126,6 +129,7 @@ class VelbusButtonLight(VelbusEntity, LightEntity): attr, *args = "set_led_state", "on" await getattr(self._channel, attr)(*args) + @api_call async def async_turn_off(self, **kwargs: Any) -> None: """Instruct the velbus light to turn off.""" attr, *args = "set_led_state", "off" diff --git a/homeassistant/components/velbus/select.py b/homeassistant/components/velbus/select.py index af79b5d1276..6e2b4d1a746 100644 --- a/homeassistant/components/velbus/select.py +++ b/homeassistant/components/velbus/select.py @@ -8,7 +8,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN -from .entity import VelbusEntity +from .entity import VelbusEntity, api_call async def async_setup_entry( @@ -37,6 +37,7 @@ class VelbusSelect(VelbusEntity, SelectEntity): self._attr_options = self._channel.get_options() self._attr_unique_id = f"{self._attr_unique_id}-program_select" + @api_call async def async_select_option(self, option: str) -> None: """Update the program on the module.""" await self._channel.set_selected_program(option) diff --git a/homeassistant/components/velbus/switch.py b/homeassistant/components/velbus/switch.py index 6de8373d3fc..db7c165840e 100644 --- a/homeassistant/components/velbus/switch.py +++ b/homeassistant/components/velbus/switch.py @@ -9,7 +9,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN -from .entity import VelbusEntity +from .entity import VelbusEntity, api_call async def async_setup_entry( @@ -36,10 +36,12 @@ class VelbusSwitch(VelbusEntity, SwitchEntity): """Return true if the switch is on.""" return self._channel.is_on() + @api_call async def async_turn_on(self, **kwargs: Any) -> None: """Instruct the switch to turn on.""" await self._channel.turn_on() + @api_call async def async_turn_off(self, **kwargs: Any) -> None: """Instruct the switch to turn off.""" await self._channel.turn_off()