From 6b7f2eb9fe0919f4f8bd3b34847fc877a1b94723 Mon Sep 17 00:00:00 2001 From: rmassch <1083135+rmassch@users.noreply.github.com> Date: Sun, 24 Nov 2024 22:58:24 +0000 Subject: [PATCH 1/2] feat: add boost time remaining and status --- custom_components/healthbox/binary_sensor.py | 125 +++++++++++++++++++ custom_components/healthbox/const.py | 2 +- custom_components/healthbox/sensor.py | 14 ++- 3 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 custom_components/healthbox/binary_sensor.py diff --git a/custom_components/healthbox/binary_sensor.py b/custom_components/healthbox/binary_sensor.py new file mode 100644 index 0000000..8487ebc --- /dev/null +++ b/custom_components/healthbox/binary_sensor.py @@ -0,0 +1,125 @@ +"""Sensor platform for healthbox.""" +from __future__ import annotations + +from decimal import Decimal +from collections.abc import Callable +from dataclasses import dataclass + +from homeassistant.core import HomeAssistant +from homeassistant.config_entries import ConfigEntry +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from homeassistant.components.binary_sensor import ( + BinarySensorEntity, + BinarySensorEntityDescription, +) + +from .const import DOMAIN, HealthboxRoom, LOGGER +from .coordinator import HealthboxDataUpdateCoordinator + + +@dataclass +class HealthboxRoomEntityDescriptionMixin: + """Mixin values for Healthbox Room entities.""" + + room: HealthboxRoom + is_on: bool + + +@dataclass +class HealthboxRoomBinarySensorEntityDescription( + BinarySensorEntityDescription, HealthboxRoomEntityDescriptionMixin +): + """Class describing Healthbox Room binary sensor entities.""" + + +def generate_binary_room_sensors_for_healthbox( + coordinator: HealthboxDataUpdateCoordinator, +) -> list[HealthboxRoomBinarySensorEntityDescription]: + """Generate binary sensors for each room.""" + room_binary_sensors: list[HealthboxRoomBinarySensorEntityDescription] = [] + + for room in coordinator.api.rooms: + if room.boost is not None: + room_binary_sensors.append( + HealthboxRoomBinarySensorEntityDescription( + key=f"{room.room_id}_boost_status", + name=f"{room.name} Boost Status", + room=room, + is_on=lambda x: x.boost.enabled + ) + ) + + return room_binary_sensors + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the sensor platform.""" + coordinator: HealthboxDataUpdateCoordinator = hass.data[DOMAIN][ + config_entry.entry_id + ] + + room_binary_sensors = generate_binary_room_sensors_for_healthbox( + coordinator=coordinator) + + entities = [] + + for description in room_binary_sensors: + entities.append(HealthboxRoomBinarySensor(coordinator, description)) + + async_add_entities(entities) + + +class HealthboxRoomBinarySensor( + CoordinatorEntity[HealthboxDataUpdateCoordinator], BinarySensorEntity +): + """Representation of a Healthbox Room Sensor.""" + + entity_description: HealthboxRoomBinarySensorEntityDescription + + def __init__( + self, + coordinator: HealthboxDataUpdateCoordinator, + description: HealthboxRoomBinarySensorEntityDescription, + ) -> None: + """Initialize Binary Sensor Domain.""" + super().__init__(coordinator) + + self.entity_description = description + self._attr_unique_id = f"{ + coordinator.config_entry.entry_id}-{description.room.room_id}-{description.key}" + self._attr_name = f"{description.name}" + self._attr_device_info = DeviceInfo( + name=self.entity_description.room.name, + identifiers={ + ( + DOMAIN, + f"{coordinator.config_entry.unique_id}_{ + self.entity_description.room.room_id}", + ) + }, + manufacturer="Renson", + model="Healthbox Room", + ) + + @property + def is_on(self) -> bool: + """Binary Sensor native value.""" + room_id: int = int(self.entity_description.room.room_id) + + matching_room = [ + room for room in self.coordinator.api.rooms if int(room.room_id) == room_id + ] + + if len(matching_room) != 1: + error_msg: str = f"No matching room found for id {room_id}" + LOGGER.error(error_msg) + else: + matching_room = matching_room[0] + return self.entity_description.is_on(matching_room) diff --git a/custom_components/healthbox/const.py b/custom_components/healthbox/const.py index 024372d..d89c756 100644 --- a/custom_components/healthbox/const.py +++ b/custom_components/healthbox/const.py @@ -17,7 +17,7 @@ ATTRIBUTION = "" SCAN_INTERVAL = timedelta(seconds=5) -PLATFORMS = [Platform.SENSOR] +PLATFORMS = [Platform.SENSOR, Platform.BINARY_SENSOR] SERVICE_START_ROOM_BOOST = "start_room_boost" SERVICE_START_ROOM_BOOST_SCHEMA = vol.Schema( diff --git a/custom_components/healthbox/sensor.py b/custom_components/healthbox/sensor.py index 9561386..98e3552 100644 --- a/custom_components/healthbox/sensor.py +++ b/custom_components/healthbox/sensor.py @@ -19,7 +19,8 @@ UnitOfPower, UnitOfPressure, UnitOfVolumeFlowRate, - UnitOfElectricPotential + UnitOfElectricPotential, + UnitOfTime ) @@ -160,6 +161,17 @@ def generate_room_sensors_for_healthbox( suggested_display_precision=2, ), ) + room_sensors.append( + HealthboxRoomSensorEntityDescription( + key=f"{room.room_id}_boost_remaining", + name=f"{room.name} Boost Remaining", + native_unit_of_measurement=UnitOfTime.SECONDS, + icon="mdi:clock-time-five-outline", + state_class=SensorStateClass.MEASUREMENT, + room=room, + value_fn=lambda x: x.boost.remaining + ), + ) if room.airflow_ventilation_rate is not None: room_sensors.append( HealthboxRoomSensorEntityDescription( From 01ed6309a13c032db3d8677a128b8c3249102011 Mon Sep 17 00:00:00 2001 From: rmassch <1083135+rmassch@users.noreply.github.com> Date: Sun, 24 Nov 2024 23:00:52 +0000 Subject: [PATCH 2/2] fix: ruff errors --- custom_components/healthbox/binary_sensor.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/custom_components/healthbox/binary_sensor.py b/custom_components/healthbox/binary_sensor.py index 8487ebc..eb9396f 100644 --- a/custom_components/healthbox/binary_sensor.py +++ b/custom_components/healthbox/binary_sensor.py @@ -1,8 +1,6 @@ """Sensor platform for healthbox.""" from __future__ import annotations -from decimal import Decimal -from collections.abc import Callable from dataclasses import dataclass from homeassistant.core import HomeAssistant