From a6c189b450ea268486e84b1a2310d1f7d20c6f24 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Mon, 14 Mar 2022 01:30:37 +0100 Subject: [PATCH] Add Pure devices to Sensibo (#67695) --- homeassistant/components/sensibo/climate.py | 2 - .../components/sensibo/coordinator.py | 9 ++ homeassistant/components/sensibo/number.py | 1 - homeassistant/components/sensibo/select.py | 2 +- homeassistant/components/sensibo/sensor.py | 92 ++++++++++++++++--- 5 files changed, 90 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/sensibo/climate.py b/homeassistant/components/sensibo/climate.py index 9299952024c..cec25d2a918 100644 --- a/homeassistant/components/sensibo/climate.py +++ b/homeassistant/components/sensibo/climate.py @@ -103,8 +103,6 @@ async def async_setup_entry( entities = [ SensiboClimate(coordinator, device_id) for device_id, device_data in coordinator.data.parsed.items() - # Remove none climate devices - if device_data["hvac_modes"] and device_data["temp"] ] async_add_entities(entities) diff --git a/homeassistant/components/sensibo/coordinator.py b/homeassistant/components/sensibo/coordinator.py index 7d37ae7f235..b79ae9d0923 100644 --- a/homeassistant/components/sensibo/coordinator.py +++ b/homeassistant/components/sensibo/coordinator.py @@ -167,6 +167,12 @@ class SensiboDataUpdateCoordinator(DataUpdateCoordinator): rssi=measurement.get("rssi"), ) + # Add information for pure devices + pure_conf = dev["pureBoostConfig"] + pure_sensitivity = pure_conf.get("sensitivity") if pure_conf else None + pure_boost_enabled = pure_conf.get("enabled") if pure_conf else None + pm25 = dev["measurements"].get("pm25") + device_data[unique_id] = { "id": unique_id, "mac": mac, @@ -200,6 +206,9 @@ class SensiboDataUpdateCoordinator(DataUpdateCoordinator): "calibration_hum": calibration_hum, "full_capabilities": capabilities, "motion_sensors": motion_sensors, + "pure_sensitivity": pure_sensitivity, + "pure_boost_enabled": pure_boost_enabled, + "pm25": pm25, } return SensiboData(raw=data, parsed=device_data) diff --git a/homeassistant/components/sensibo/number.py b/homeassistant/components/sensibo/number.py index d3cc4d57830..001269ae168 100644 --- a/homeassistant/components/sensibo/number.py +++ b/homeassistant/components/sensibo/number.py @@ -66,7 +66,6 @@ async def async_setup_entry( SensiboNumber(coordinator, device_id, description) for device_id, device_data in coordinator.data.parsed.items() for description in NUMBER_TYPES - if device_data["hvac_modes"] and device_data["temp"] ) diff --git a/homeassistant/components/sensibo/select.py b/homeassistant/components/sensibo/select.py index cb569620cca..d211a9dd223 100644 --- a/homeassistant/components/sensibo/select.py +++ b/homeassistant/components/sensibo/select.py @@ -58,7 +58,7 @@ async def async_setup_entry( SensiboSelect(coordinator, device_id, description) for device_id, device_data in coordinator.data.parsed.items() for description in SELECT_TYPES - if device_data["hvac_modes"] and description.key in device_data["full_features"] + if description.key in device_data["full_features"] ) diff --git a/homeassistant/components/sensibo/sensor.py b/homeassistant/components/sensibo/sensor.py index cb5833ee985..21f2bf2b5da 100644 --- a/homeassistant/components/sensibo/sensor.py +++ b/homeassistant/components/sensibo/sensor.py @@ -3,6 +3,7 @@ from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass +from typing import Any from homeassistant.components.sensor import ( SensorDeviceClass, @@ -12,6 +13,7 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( + CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, ELECTRIC_POTENTIAL_VOLT, PERCENTAGE, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, @@ -24,25 +26,39 @@ from homeassistant.helpers.typing import StateType from .const import DOMAIN from .coordinator import MotionSensor, SensiboDataUpdateCoordinator -from .entity import SensiboMotionBaseEntity +from .entity import SensiboDeviceBaseEntity, SensiboMotionBaseEntity @dataclass -class BaseEntityDescriptionMixin: +class MotionBaseEntityDescriptionMixin: """Mixin for required Sensibo base description keys.""" value_fn: Callable[[MotionSensor], StateType] @dataclass -class SensiboSensorEntityDescription( - SensorEntityDescription, BaseEntityDescriptionMixin +class DeviceBaseEntityDescriptionMixin: + """Mixin for required Sensibo base description keys.""" + + value_fn: Callable[[dict[str, Any]], StateType] + + +@dataclass +class SensiboMotionSensorEntityDescription( + SensorEntityDescription, MotionBaseEntityDescriptionMixin ): """Describes Sensibo Motion sensor entity.""" -MOTION_SENSOR_TYPES: tuple[SensiboSensorEntityDescription, ...] = ( - SensiboSensorEntityDescription( +@dataclass +class SensiboDeviceSensorEntityDescription( + SensorEntityDescription, DeviceBaseEntityDescriptionMixin +): + """Describes Sensibo Motion sensor entity.""" + + +MOTION_SENSOR_TYPES: tuple[SensiboMotionSensorEntityDescription, ...] = ( + SensiboMotionSensorEntityDescription( key="rssi", device_class=SensorDeviceClass.SIGNAL_STRENGTH, entity_category=EntityCategory.DIAGNOSTIC, @@ -53,7 +69,7 @@ MOTION_SENSOR_TYPES: tuple[SensiboSensorEntityDescription, ...] = ( value_fn=lambda data: data.rssi, entity_registry_enabled_default=False, ), - SensiboSensorEntityDescription( + SensiboMotionSensorEntityDescription( key="battery_voltage", device_class=SensorDeviceClass.VOLTAGE, entity_category=EntityCategory.DIAGNOSTIC, @@ -63,7 +79,7 @@ MOTION_SENSOR_TYPES: tuple[SensiboSensorEntityDescription, ...] = ( icon="mdi:battery", value_fn=lambda data: data.battery_voltage, ), - SensiboSensorEntityDescription( + SensiboMotionSensorEntityDescription( key="humidity", device_class=SensorDeviceClass.HUMIDITY, native_unit_of_measurement=PERCENTAGE, @@ -72,7 +88,7 @@ MOTION_SENSOR_TYPES: tuple[SensiboSensorEntityDescription, ...] = ( icon="mdi:water", value_fn=lambda data: data.humidity, ), - SensiboSensorEntityDescription( + SensiboMotionSensorEntityDescription( key="temperature", device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=TEMP_CELSIUS, @@ -82,6 +98,23 @@ MOTION_SENSOR_TYPES: tuple[SensiboSensorEntityDescription, ...] = ( value_fn=lambda data: data.temperature, ), ) +DEVICE_SENSOR_TYPES: tuple[SensiboDeviceSensorEntityDescription, ...] = ( + SensiboDeviceSensorEntityDescription( + key="pm25", + device_class=SensorDeviceClass.PM25, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + name="PM2.5", + icon="mdi:air-filter", + value_fn=lambda data: data["pm25"], + ), + SensiboDeviceSensorEntityDescription( + key="pure_sensitivity", + name="Pure Sensitivity", + icon="mdi:air-filter", + value_fn=lambda data: data["pure_sensitivity"], + ), +) async def async_setup_entry( @@ -91,19 +124,28 @@ async def async_setup_entry( coordinator: SensiboDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] - async_add_entities( + entities: list[SensiboMotionSensor | SensiboDeviceSensor] = [] + + entities.extend( SensiboMotionSensor(coordinator, device_id, sensor_id, sensor_data, description) for device_id, device_data in coordinator.data.parsed.items() for sensor_id, sensor_data in device_data["motion_sensors"].items() for description in MOTION_SENSOR_TYPES if device_data["motion_sensors"] ) + entities.extend( + SensiboDeviceSensor(coordinator, device_id, description) + for device_id, device_data in coordinator.data.parsed.items() + for description in DEVICE_SENSOR_TYPES + if device_data[description.key] is not None + ) + async_add_entities(entities) class SensiboMotionSensor(SensiboMotionBaseEntity, SensorEntity): """Representation of a Sensibo Motion Sensor.""" - entity_description: SensiboSensorEntityDescription + entity_description: SensiboMotionSensorEntityDescription def __init__( self, @@ -111,7 +153,7 @@ class SensiboMotionSensor(SensiboMotionBaseEntity, SensorEntity): device_id: str, sensor_id: str, sensor_data: MotionSensor, - entity_description: SensiboSensorEntityDescription, + entity_description: SensiboMotionSensorEntityDescription, ) -> None: """Initiate Sensibo Motion Sensor.""" super().__init__( @@ -131,3 +173,29 @@ class SensiboMotionSensor(SensiboMotionBaseEntity, SensorEntity): def native_value(self) -> StateType: """Return value of sensor.""" return self.entity_description.value_fn(self.sensor_data) + + +class SensiboDeviceSensor(SensiboDeviceBaseEntity, SensorEntity): + """Representation of a Sensibo Device Sensor.""" + + entity_description: SensiboDeviceSensorEntityDescription + + def __init__( + self, + coordinator: SensiboDataUpdateCoordinator, + device_id: str, + entity_description: SensiboDeviceSensorEntityDescription, + ) -> None: + """Initiate Sensibo Device Sensor.""" + super().__init__( + coordinator, + device_id, + ) + self.entity_description = entity_description + self._attr_unique_id = f"{device_id}-{entity_description.key}" + self._attr_name = f"{self.device_data['name']} {entity_description.name}" + + @property + def native_value(self) -> StateType: + """Return value of sensor.""" + return self.entity_description.value_fn(self.device_data)