Use entity class attributes for Bond (#53055)

This commit is contained in:
Robert Hillis 2021-07-16 17:06:18 -04:00 committed by GitHub
parent b13119884c
commit 4fceac00b1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 37 additions and 136 deletions

View file

@ -38,27 +38,19 @@ async def async_setup_entry(
class BondCover(BondEntity, CoverEntity): class BondCover(BondEntity, CoverEntity):
"""Representation of a Bond cover.""" """Representation of a Bond cover."""
_attr_device_class = DEVICE_CLASS_SHADE
def __init__( def __init__(
self, hub: BondHub, device: BondDevice, bpup_subs: BPUPSubscriptions self, hub: BondHub, device: BondDevice, bpup_subs: BPUPSubscriptions
) -> None: ) -> None:
"""Create HA entity representing Bond cover.""" """Create HA entity representing Bond cover."""
super().__init__(hub, device, bpup_subs) super().__init__(hub, device, bpup_subs)
self._closed: bool | None = None
def _apply_state(self, state: dict) -> None: def _apply_state(self, state: dict) -> None:
cover_open = state.get("open") cover_open = state.get("open")
self._closed = True if cover_open == 0 else False if cover_open == 1 else None self._attr_is_closed = (
True if cover_open == 0 else False if cover_open == 1 else None
@property )
def device_class(self) -> str | None:
"""Get device class."""
return DEVICE_CLASS_SHADE
@property
def is_closed(self) -> bool | None:
"""Return if the cover is closed or not."""
return self._closed
async def async_open_cover(self, **kwargs: Any) -> None: async def async_open_cover(self, **kwargs: Any) -> None:
"""Open the cover.""" """Open the cover."""

View file

@ -25,6 +25,8 @@ _FALLBACK_SCAN_INTERVAL = timedelta(seconds=10)
class BondEntity(Entity): class BondEntity(Entity):
"""Generic Bond entity encapsulating common features of any Bond controlled device.""" """Generic Bond entity encapsulating common features of any Bond controlled device."""
_attr_should_poll = False
def __init__( def __init__(
self, self,
hub: BondHub, hub: BondHub,
@ -37,31 +39,17 @@ class BondEntity(Entity):
self._device = device self._device = device
self._device_id = device.device_id self._device_id = device.device_id
self._sub_device = sub_device self._sub_device = sub_device
self._available = True self._attr_available = True
self._bpup_subs = bpup_subs self._bpup_subs = bpup_subs
self._update_lock: Lock | None = None self._update_lock: Lock | None = None
self._initialized = False self._initialized = False
sub_device_id: str = f"_{sub_device}" if sub_device else ""
@property self._attr_unique_id = f"{hub.bond_id}_{device.device_id}{sub_device_id}"
def unique_id(self) -> str | None: if sub_device:
"""Get unique ID for the entity.""" sub_device_name = sub_device.replace("_", " ").title()
hub_id = self._hub.bond_id self._attr_name = f"{device.name} {sub_device_name}"
device_id = self._device_id else:
sub_device_id: str = f"_{self._sub_device}" if self._sub_device else "" self._attr_name = device.name
return f"{hub_id}_{device_id}{sub_device_id}"
@property
def name(self) -> str | None:
"""Get entity name."""
if self._sub_device:
sub_device_name = self._sub_device.replace("_", " ").title()
return f"{self._device.name} {sub_device_name}"
return self._device.name
@property
def should_poll(self) -> bool:
"""No polling needed."""
return False
@property @property
def device_info(self) -> DeviceInfo: def device_info(self) -> DeviceInfo:
@ -93,16 +81,6 @@ class BondEntity(Entity):
return device_info return device_info
@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) -> None: async def async_update(self) -> None:
"""Fetch assumed state of the cover from the hub using API.""" """Fetch assumed state of the cover from the hub using API."""
await self._async_update_from_api() await self._async_update_from_api()
@ -113,7 +91,7 @@ class BondEntity(Entity):
self.hass.is_stopping self.hass.is_stopping
or self._bpup_subs.alive or self._bpup_subs.alive
and self._initialized and self._initialized
and self._available and self.available
): ):
return return
@ -135,13 +113,14 @@ class BondEntity(Entity):
try: try:
state: dict = await self._hub.bond.device_state(self._device_id) state: dict = await self._hub.bond.device_state(self._device_id)
except (ClientError, AsyncIOTimeoutError, OSError) as error: except (ClientError, AsyncIOTimeoutError, OSError) as error:
if self._available: if self.available:
_LOGGER.warning( _LOGGER.warning(
"Entity %s has become unavailable", self.entity_id, exc_info=error "Entity %s has become unavailable", self.entity_id, exc_info=error
) )
self._available = False self._attr_available = False
else: else:
self._async_state_callback(state) self._async_state_callback(state)
self._attr_assumed_state = self._hub.is_bridge and not self._device.trust_state
@abstractmethod @abstractmethod
def _apply_state(self, state: dict) -> None: def _apply_state(self, state: dict) -> None:
@ -151,9 +130,9 @@ class BondEntity(Entity):
def _async_state_callback(self, state: dict) -> None: def _async_state_callback(self, state: dict) -> None:
"""Process a state change.""" """Process a state change."""
self._initialized = True self._initialized = True
if not self._available: if not self.available:
_LOGGER.info("Entity %s has come back", self.entity_id) _LOGGER.info("Entity %s has come back", self.entity_id)
self._available = True self._attr_available = True
_LOGGER.debug( _LOGGER.debug(
"Device state for %s (%s) is:\n%s", self.name, self.entity_id, state "Device state for %s (%s) is:\n%s", self.name, self.entity_id, state
) )

View file

@ -81,26 +81,7 @@ async def async_setup_entry(
class BondBaseLight(BondEntity, LightEntity): class BondBaseLight(BondEntity, LightEntity):
"""Representation of a Bond light.""" """Representation of a Bond light."""
def __init__( _attr_supported_features = 0
self,
hub: BondHub,
device: BondDevice,
bpup_subs: BPUPSubscriptions,
sub_device: str | None = None,
) -> None:
"""Create HA entity representing Bond light."""
super().__init__(hub, device, bpup_subs, sub_device)
self._light: int | None = None
@property
def is_on(self) -> bool:
"""Return if light is currently on."""
return self._light == 1
@property
def supported_features(self) -> int:
"""Flag supported features."""
return 0
class BondLight(BondBaseLight, BondEntity, LightEntity): class BondLight(BondBaseLight, BondEntity, LightEntity):
@ -115,26 +96,13 @@ class BondLight(BondBaseLight, BondEntity, LightEntity):
) -> None: ) -> None:
"""Create HA entity representing Bond light.""" """Create HA entity representing Bond light."""
super().__init__(hub, device, bpup_subs, sub_device) super().__init__(hub, device, bpup_subs, sub_device)
self._brightness: int | None = None if device.supports_set_brightness():
self._attr_supported_features = SUPPORT_BRIGHTNESS
def _apply_state(self, state: dict) -> None: def _apply_state(self, state: dict) -> None:
self._light = state.get("light") self._attr_is_on = state.get("light") == 1
self._brightness = state.get("brightness") brightness = state.get("brightness")
self._attr_brightness = round(brightness * 255 / 100) if brightness else None
@property
def supported_features(self) -> int:
"""Flag supported features."""
if self._device.supports_set_brightness():
return SUPPORT_BRIGHTNESS
return 0
@property
def brightness(self) -> int | None:
"""Return the brightness of this light between 1..255."""
brightness_value = (
round(self._brightness * 255 / 100) if self._brightness else None
)
return brightness_value
async def async_turn_on(self, **kwargs: Any) -> None: async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn on the light.""" """Turn on the light."""
@ -156,7 +124,7 @@ class BondDownLight(BondBaseLight, BondEntity, LightEntity):
"""Representation of a Bond light.""" """Representation of a Bond light."""
def _apply_state(self, state: dict) -> None: def _apply_state(self, state: dict) -> None:
self._light = state.get("down_light") and state.get("light") self._attr_is_on = bool(state.get("down_light") and state.get("light"))
async def async_turn_on(self, **kwargs: Any) -> None: async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn on the light.""" """Turn on the light."""
@ -175,7 +143,7 @@ class BondUpLight(BondBaseLight, BondEntity, LightEntity):
"""Representation of a Bond light.""" """Representation of a Bond light."""
def _apply_state(self, state: dict) -> None: def _apply_state(self, state: dict) -> None:
self._light = state.get("up_light") and state.get("light") self._attr_is_on = bool(state.get("up_light") and state.get("light"))
async def async_turn_on(self, **kwargs: Any) -> None: async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn on the light.""" """Turn on the light."""
@ -193,29 +161,14 @@ class BondUpLight(BondBaseLight, BondEntity, LightEntity):
class BondFireplace(BondEntity, LightEntity): class BondFireplace(BondEntity, LightEntity):
"""Representation of a Bond-controlled fireplace.""" """Representation of a Bond-controlled fireplace."""
def __init__( _attr_supported_features = SUPPORT_BRIGHTNESS
self, hub: BondHub, device: BondDevice, bpup_subs: BPUPSubscriptions
) -> None:
"""Create HA entity representing Bond fireplace."""
super().__init__(hub, device, bpup_subs)
self._power: bool | None = None
# Bond flame level, 0-100
self._flame: int | None = None
def _apply_state(self, state: dict) -> None: def _apply_state(self, state: dict) -> None:
self._power = state.get("power") power = state.get("power")
self._flame = state.get("flame") flame = state.get("flame")
self._attr_is_on = power == 1
@property self._attr_brightness = round(flame * 255 / 100) if flame else None
def supported_features(self) -> int: self._attr_icon = "mdi:fireplace" if power == 1 else "mdi:fireplace-off"
"""Flag brightness as supported feature to represent flame level."""
return SUPPORT_BRIGHTNESS
@property
def is_on(self) -> bool:
"""Return True if power is on."""
return self._power == 1
async def async_turn_on(self, **kwargs: Any) -> None: async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the fireplace on.""" """Turn the fireplace on."""
@ -233,13 +186,3 @@ class BondFireplace(BondEntity, LightEntity):
_LOGGER.debug("Fireplace async_turn_off called with: %s", kwargs) _LOGGER.debug("Fireplace async_turn_off called with: %s", kwargs)
await self._hub.bond.action(self._device.device_id, Action.turn_off()) await self._hub.bond.action(self._device.device_id, Action.turn_off())
@property
def brightness(self) -> int | None:
"""Return the flame of this fireplace converted to HA brightness between 0..255."""
return round(self._flame * 255 / 100) if self._flame else None
@property
def icon(self) -> str | None:
"""Show fireplace icon for the entity."""
return "mdi:fireplace" if self._power == 1 else "mdi:fireplace-off"

View file

@ -13,7 +13,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import BPUP_SUBS, DOMAIN, HUB from .const import BPUP_SUBS, DOMAIN, HUB
from .entity import BondEntity from .entity import BondEntity
from .utils import BondDevice, BondHub from .utils import BondHub
async def async_setup_entry( async def async_setup_entry(
@ -38,21 +38,8 @@ async def async_setup_entry(
class BondSwitch(BondEntity, SwitchEntity): class BondSwitch(BondEntity, SwitchEntity):
"""Representation of a Bond generic device.""" """Representation of a Bond generic device."""
def __init__(
self, hub: BondHub, device: BondDevice, bpup_subs: BPUPSubscriptions
) -> None:
"""Create HA entity representing Bond generic device (switch)."""
super().__init__(hub, device, bpup_subs)
self._power: bool | None = None
def _apply_state(self, state: dict) -> None: def _apply_state(self, state: dict) -> None:
self._power = state.get("power") self._attr_is_on = state.get("power") == 1
@property
def is_on(self) -> bool:
"""Return True if power is on."""
return self._power == 1
async def async_turn_on(self, **kwargs: Any) -> None: async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the device on.""" """Turn the device on."""