"""An abstract class common to all Bond entities.""" from abc import abstractmethod from asyncio import TimeoutError as AsyncIOTimeoutError import logging from typing import Any, Dict, Optional from aiohttp import ClientError from homeassistant.const import ATTR_NAME from homeassistant.helpers.entity import Entity from .const import DOMAIN from .utils import BondDevice, BondHub _LOGGER = logging.getLogger(__name__) class BondEntity(Entity): """Generic Bond entity encapsulating common features of any Bond controlled device.""" def __init__(self, hub: BondHub, device: BondDevice): """Initialize entity with API and device info.""" self._hub = hub self._device = device self._available = True @property def unique_id(self) -> Optional[str]: """Get unique ID for the entity.""" hub_id = self._hub.bond_id device_id = self._device.device_id return f"{hub_id}_{device_id}" @property def name(self) -> Optional[str]: """Get entity name.""" return self._device.name @property def device_info(self) -> Optional[Dict[str, Any]]: """Get a an HA device representing this Bond controlled device.""" return { ATTR_NAME: self.name, "identifiers": {(DOMAIN, self._device.device_id)}, "via_device": (DOMAIN, self._hub.bond_id), } @property def assumed_state(self) -> bool: """Let HA know this entity relies on an assumed state tracked by Bond.""" return self._hub.is_bridge and not self._device.trust_state @property def available(self) -> bool: """Report availability of this entity based on last API call results.""" return self._available async def async_update(self): """Fetch assumed state of the cover from the hub using API.""" try: state: dict = await self._hub.bond.device_state(self._device.device_id) except (ClientError, AsyncIOTimeoutError, OSError) as error: if self._available: _LOGGER.warning( "Entity %s has become unavailable", self.entity_id, exc_info=error ) self._available = False else: _LOGGER.debug("Device state for %s is:\n%s", self.entity_id, state) if not self._available: _LOGGER.info("Entity %s has come back", self.entity_id) self._available = True self._apply_state(state) @abstractmethod def _apply_state(self, state: dict): raise NotImplementedError