1
0
mirror of https://github.com/home-assistant/core.git synced 2025-12-20 02:48:57 +00:00
Files
core/homeassistant/components/qbus/sensor.py
Thomas D 755864f9f3 Add sensor platform to Qbus integration (#149389)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-08-02 20:01:58 +02:00

379 lines
12 KiB
Python

"""Support for Qbus sensor."""
from dataclasses import dataclass
from qbusmqttapi.discovery import QbusMqttOutput
from qbusmqttapi.state import (
GaugeStateProperty,
QbusMqttGaugeState,
QbusMqttHumidityState,
QbusMqttThermoState,
QbusMqttVentilationState,
QbusMqttWeatherState,
)
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_PARTS_PER_MILLION,
LIGHT_LUX,
PERCENTAGE,
UnitOfElectricCurrent,
UnitOfElectricPotential,
UnitOfEnergy,
UnitOfLength,
UnitOfPower,
UnitOfPressure,
UnitOfSoundPressure,
UnitOfSpeed,
UnitOfTemperature,
UnitOfVolume,
UnitOfVolumeFlowRate,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .coordinator import QbusConfigEntry
from .entity import QbusEntity, create_new_entities, determine_new_outputs
PARALLEL_UPDATES = 0
@dataclass(frozen=True, kw_only=True)
class QbusWeatherDescription(SensorEntityDescription):
"""Description for Qbus weather entities."""
property: str
_WEATHER_DESCRIPTIONS = (
QbusWeatherDescription(
key="daylight",
property="dayLight",
translation_key="daylight",
device_class=SensorDeviceClass.ILLUMINANCE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=LIGHT_LUX,
),
QbusWeatherDescription(
key="light",
property="light",
device_class=SensorDeviceClass.ILLUMINANCE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=LIGHT_LUX,
),
QbusWeatherDescription(
key="light_east",
property="lightEast",
translation_key="light_east",
device_class=SensorDeviceClass.ILLUMINANCE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=LIGHT_LUX,
),
QbusWeatherDescription(
key="light_south",
property="lightSouth",
translation_key="light_south",
device_class=SensorDeviceClass.ILLUMINANCE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=LIGHT_LUX,
),
QbusWeatherDescription(
key="light_west",
property="lightWest",
translation_key="light_west",
device_class=SensorDeviceClass.ILLUMINANCE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=LIGHT_LUX,
),
QbusWeatherDescription(
key="temperature",
property="temperature",
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
),
QbusWeatherDescription(
key="wind",
property="wind",
device_class=SensorDeviceClass.WIND_SPEED,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
),
)
_GAUGE_VARIANT_DESCRIPTIONS = {
"AIRPRESSURE": SensorEntityDescription(
key="airpressure",
device_class=SensorDeviceClass.PRESSURE,
native_unit_of_measurement=UnitOfPressure.MBAR,
state_class=SensorStateClass.MEASUREMENT,
),
"AIRQUALITY": SensorEntityDescription(
key="airquality",
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
),
"CURRENT": SensorEntityDescription(
key="current",
device_class=SensorDeviceClass.CURRENT,
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
state_class=SensorStateClass.MEASUREMENT,
),
"ENERGY": SensorEntityDescription(
key="energy",
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
state_class=SensorStateClass.TOTAL,
),
"GAS": SensorEntityDescription(
key="gas",
device_class=SensorDeviceClass.VOLUME_FLOW_RATE,
native_unit_of_measurement=UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR,
state_class=SensorStateClass.MEASUREMENT,
),
"GASFLOW": SensorEntityDescription(
key="gasflow",
device_class=SensorDeviceClass.VOLUME_FLOW_RATE,
native_unit_of_measurement=UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR,
state_class=SensorStateClass.MEASUREMENT,
),
"HUMIDITY": SensorEntityDescription(
key="humidity",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
"LIGHT": SensorEntityDescription(
key="light",
device_class=SensorDeviceClass.ILLUMINANCE,
native_unit_of_measurement=LIGHT_LUX,
state_class=SensorStateClass.MEASUREMENT,
),
"LOUDNESS": SensorEntityDescription(
key="loudness",
device_class=SensorDeviceClass.SOUND_PRESSURE,
native_unit_of_measurement=UnitOfSoundPressure.DECIBEL,
state_class=SensorStateClass.MEASUREMENT,
),
"POWER": SensorEntityDescription(
key="power",
device_class=SensorDeviceClass.POWER,
native_unit_of_measurement=UnitOfPower.KILO_WATT,
state_class=SensorStateClass.MEASUREMENT,
),
"PRESSURE": SensorEntityDescription(
key="pressure",
device_class=SensorDeviceClass.PRESSURE,
native_unit_of_measurement=UnitOfPressure.KPA,
state_class=SensorStateClass.MEASUREMENT,
),
"TEMPERATURE": SensorEntityDescription(
key="temperature",
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
state_class=SensorStateClass.MEASUREMENT,
),
"VOLTAGE": SensorEntityDescription(
key="voltage",
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
state_class=SensorStateClass.MEASUREMENT,
),
"VOLUME": SensorEntityDescription(
key="volume",
device_class=SensorDeviceClass.VOLUME_STORAGE,
native_unit_of_measurement=UnitOfVolume.LITERS,
state_class=SensorStateClass.MEASUREMENT,
),
"WATER": SensorEntityDescription(
key="water",
device_class=SensorDeviceClass.WATER,
native_unit_of_measurement=UnitOfVolume.LITERS,
state_class=SensorStateClass.TOTAL,
),
"WATERFLOW": SensorEntityDescription(
key="waterflow",
device_class=SensorDeviceClass.VOLUME_FLOW_RATE,
native_unit_of_measurement=UnitOfVolumeFlowRate.LITERS_PER_HOUR,
state_class=SensorStateClass.MEASUREMENT,
),
"WATERLEVEL": SensorEntityDescription(
key="waterlevel",
device_class=SensorDeviceClass.DISTANCE,
native_unit_of_measurement=UnitOfLength.METERS,
state_class=SensorStateClass.MEASUREMENT,
),
"WATERPRESSURE": SensorEntityDescription(
key="waterpressure",
device_class=SensorDeviceClass.PRESSURE,
native_unit_of_measurement=UnitOfPressure.MBAR,
state_class=SensorStateClass.MEASUREMENT,
),
"WIND": SensorEntityDescription(
key="wind",
device_class=SensorDeviceClass.WIND_SPEED,
native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
state_class=SensorStateClass.MEASUREMENT,
),
}
def _is_gauge_with_variant(output: QbusMqttOutput) -> bool:
return (
output.type == "gauge"
and isinstance(output.variant, str)
and _GAUGE_VARIANT_DESCRIPTIONS.get(output.variant.upper()) is not None
)
def _is_ventilation_with_co2(output: QbusMqttOutput) -> bool:
return output.type == "ventilation" and output.properties.get("co2") is not None
async def async_setup_entry(
hass: HomeAssistant,
entry: QbusConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up sensor entities."""
coordinator = entry.runtime_data
added_outputs: list[QbusMqttOutput] = []
def _create_weather_entities() -> list[QbusEntity]:
new_outputs = determine_new_outputs(
coordinator, added_outputs, lambda output: output.type == "weatherstation"
)
return [
QbusWeatherSensor(output, description)
for output in new_outputs
for description in _WEATHER_DESCRIPTIONS
]
def _check_outputs() -> None:
entities: list[QbusEntity] = [
*create_new_entities(
coordinator,
added_outputs,
_is_gauge_with_variant,
QbusGaugeVariantSensor,
),
*create_new_entities(
coordinator,
added_outputs,
lambda output: output.type == "humidity",
QbusHumiditySensor,
),
*create_new_entities(
coordinator,
added_outputs,
lambda output: output.type == "thermo",
QbusThermoSensor,
),
*create_new_entities(
coordinator,
added_outputs,
_is_ventilation_with_co2,
QbusVentilationSensor,
),
*_create_weather_entities(),
]
async_add_entities(entities)
_check_outputs()
entry.async_on_unload(coordinator.async_add_listener(_check_outputs))
class QbusGaugeVariantSensor(QbusEntity, SensorEntity):
"""Representation of a Qbus sensor entity for gauges with variant."""
_state_cls = QbusMqttGaugeState
_attr_name = None
_attr_suggested_display_precision = 2
def __init__(self, mqtt_output: QbusMqttOutput) -> None:
"""Initialize sensor entity."""
super().__init__(mqtt_output)
variant = str(mqtt_output.variant)
self.entity_description = _GAUGE_VARIANT_DESCRIPTIONS[variant.upper()]
async def _handle_state_received(self, state: QbusMqttGaugeState) -> None:
self._attr_native_value = state.read_value(GaugeStateProperty.CURRENT_VALUE)
class QbusHumiditySensor(QbusEntity, SensorEntity):
"""Representation of a Qbus sensor entity for humidity modules."""
_state_cls = QbusMqttHumidityState
_attr_device_class = SensorDeviceClass.HUMIDITY
_attr_name = None
_attr_native_unit_of_measurement = PERCENTAGE
_attr_state_class = SensorStateClass.MEASUREMENT
async def _handle_state_received(self, state: QbusMqttHumidityState) -> None:
self._attr_native_value = state.read_value()
class QbusThermoSensor(QbusEntity, SensorEntity):
"""Representation of a Qbus sensor entity for thermostats."""
_state_cls = QbusMqttThermoState
_attr_device_class = SensorDeviceClass.TEMPERATURE
_attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS
_attr_state_class = SensorStateClass.MEASUREMENT
async def _handle_state_received(self, state: QbusMqttThermoState) -> None:
self._attr_native_value = state.read_current_temperature()
class QbusVentilationSensor(QbusEntity, SensorEntity):
"""Representation of a Qbus sensor entity for ventilations."""
_state_cls = QbusMqttVentilationState
_attr_device_class = SensorDeviceClass.CO2
_attr_name = None
_attr_native_unit_of_measurement = CONCENTRATION_PARTS_PER_MILLION
_attr_state_class = SensorStateClass.MEASUREMENT
_attr_suggested_display_precision = 0
async def _handle_state_received(self, state: QbusMqttVentilationState) -> None:
self._attr_native_value = state.read_co2()
class QbusWeatherSensor(QbusEntity, SensorEntity):
"""Representation of a Qbus weather sensor."""
_state_cls = QbusMqttWeatherState
entity_description: QbusWeatherDescription
def __init__(
self, mqtt_output: QbusMqttOutput, description: QbusWeatherDescription
) -> None:
"""Initialize sensor entity."""
super().__init__(mqtt_output, id_suffix=description.key)
self.entity_description = description
if description.key == "temperature":
self._attr_name = None
async def _handle_state_received(self, state: QbusMqttWeatherState) -> None:
if value := state.read_property(self.entity_description.property, None):
self.native_value = value