"""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.device_registry import DeviceInfo from homeassistant.helpers.entity import Entity from .const import DOMAIN class VelbusEntity(Entity): """Representation of a Velbus entity.""" _attr_should_poll: bool = False def __init__(self, channel: VelbusChannel) -> None: """Initialize a Velbus entity.""" self._channel = channel self._attr_name = channel.get_name() self._attr_device_info = DeviceInfo( identifiers={ (DOMAIN, str(channel.get_module_address())), }, manufacturer="Velleman", model=channel.get_module_type_name(), name=channel.get_full_name(), sw_version=channel.get_module_sw_version(), ) serial = channel.get_module_serial() or str(channel.get_module_address()) self._attr_unique_id = f"{serial}-{channel.get_channel_number()}" async def async_added_to_hass(self) -> None: """Add listener for state changes.""" self._channel.on_status_update(self._on_update) 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