mirror of
https://github.com/home-assistant/core.git
synced 2025-08-03 02:28:21 +00:00
Add sensor platform to Qbus integration (#149389)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
parent
fa476d4e34
commit
755864f9f3
@ -22,7 +22,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import QbusConfigEntry
|
||||
from .entity import QbusEntity, add_new_outputs
|
||||
from .entity import QbusEntity, create_new_entities
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
@ -42,13 +42,13 @@ async def async_setup_entry(
|
||||
added_outputs: list[QbusMqttOutput] = []
|
||||
|
||||
def _check_outputs() -> None:
|
||||
add_new_outputs(
|
||||
entities = create_new_entities(
|
||||
coordinator,
|
||||
added_outputs,
|
||||
lambda output: output.type == "thermo",
|
||||
QbusClimate,
|
||||
async_add_entities,
|
||||
)
|
||||
async_add_entities(entities)
|
||||
|
||||
_check_outputs()
|
||||
entry.async_on_unload(coordinator.async_add_listener(_check_outputs))
|
||||
|
@ -10,6 +10,7 @@ PLATFORMS: list[Platform] = [
|
||||
Platform.COVER,
|
||||
Platform.LIGHT,
|
||||
Platform.SCENE,
|
||||
Platform.SENSOR,
|
||||
Platform.SWITCH,
|
||||
]
|
||||
|
||||
|
@ -20,7 +20,7 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .coordinator import QbusConfigEntry
|
||||
from .entity import QbusEntity, add_new_outputs
|
||||
from .entity import QbusEntity, create_new_entities
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
@ -36,13 +36,13 @@ async def async_setup_entry(
|
||||
added_outputs: list[QbusMqttOutput] = []
|
||||
|
||||
def _check_outputs() -> None:
|
||||
add_new_outputs(
|
||||
entities = create_new_entities(
|
||||
coordinator,
|
||||
added_outputs,
|
||||
lambda output: output.type == "shutter",
|
||||
QbusCover,
|
||||
async_add_entities,
|
||||
)
|
||||
async_add_entities(entities)
|
||||
|
||||
_check_outputs()
|
||||
entry.async_on_unload(coordinator.async_add_listener(_check_outputs))
|
||||
|
@ -14,7 +14,6 @@ from qbusmqttapi.state import QbusMqttState
|
||||
from homeassistant.components.mqtt import ReceiveMessage, client as mqtt
|
||||
from homeassistant.helpers.device_registry import DeviceInfo, format_mac
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN, MANUFACTURER
|
||||
from .coordinator import QbusControllerCoordinator
|
||||
@ -24,14 +23,24 @@ _REFID_REGEX = re.compile(r"^\d+\/(\d+(?:\/\d+)?)$")
|
||||
StateT = TypeVar("StateT", bound=QbusMqttState)
|
||||
|
||||
|
||||
def add_new_outputs(
|
||||
def create_new_entities(
|
||||
coordinator: QbusControllerCoordinator,
|
||||
added_outputs: list[QbusMqttOutput],
|
||||
filter_fn: Callable[[QbusMqttOutput], bool],
|
||||
entity_type: type[QbusEntity],
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Call async_add_entities for new outputs."""
|
||||
) -> list[QbusEntity]:
|
||||
"""Create entities for new outputs."""
|
||||
|
||||
new_outputs = determine_new_outputs(coordinator, added_outputs, filter_fn)
|
||||
return [entity_type(output) for output in new_outputs]
|
||||
|
||||
|
||||
def determine_new_outputs(
|
||||
coordinator: QbusControllerCoordinator,
|
||||
added_outputs: list[QbusMqttOutput],
|
||||
filter_fn: Callable[[QbusMqttOutput], bool],
|
||||
) -> list[QbusMqttOutput]:
|
||||
"""Determine new outputs."""
|
||||
|
||||
added_ref_ids = {k.ref_id for k in added_outputs}
|
||||
|
||||
@ -43,7 +52,8 @@ def add_new_outputs(
|
||||
|
||||
if new_outputs:
|
||||
added_outputs.extend(new_outputs)
|
||||
async_add_entities([entity_type(output) for output in new_outputs])
|
||||
|
||||
return new_outputs
|
||||
|
||||
|
||||
def format_ref_id(ref_id: str) -> str | None:
|
||||
@ -67,7 +77,13 @@ class QbusEntity(Entity, Generic[StateT], ABC):
|
||||
_attr_has_entity_name = True
|
||||
_attr_should_poll = False
|
||||
|
||||
def __init__(self, mqtt_output: QbusMqttOutput) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
mqtt_output: QbusMqttOutput,
|
||||
*,
|
||||
id_suffix: str = "",
|
||||
link_to_main_device: bool = False,
|
||||
) -> None:
|
||||
"""Initialize the Qbus entity."""
|
||||
|
||||
self._mqtt_output = mqtt_output
|
||||
@ -79,17 +95,25 @@ class QbusEntity(Entity, Generic[StateT], ABC):
|
||||
)
|
||||
|
||||
ref_id = format_ref_id(mqtt_output.ref_id)
|
||||
unique_id = f"ctd_{mqtt_output.device.serial_number}_{ref_id}"
|
||||
|
||||
self._attr_unique_id = f"ctd_{mqtt_output.device.serial_number}_{ref_id}"
|
||||
if id_suffix:
|
||||
unique_id += f"_{id_suffix}"
|
||||
|
||||
# Create linked device
|
||||
self._attr_device_info = DeviceInfo(
|
||||
name=mqtt_output.name.title(),
|
||||
manufacturer=MANUFACTURER,
|
||||
identifiers={(DOMAIN, f"{mqtt_output.device.serial_number}_{ref_id}")},
|
||||
suggested_area=mqtt_output.location.title(),
|
||||
via_device=create_main_device_identifier(mqtt_output),
|
||||
)
|
||||
self._attr_unique_id = unique_id
|
||||
|
||||
if link_to_main_device:
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={create_main_device_identifier(mqtt_output)}
|
||||
)
|
||||
else:
|
||||
self._attr_device_info = DeviceInfo(
|
||||
name=mqtt_output.name.title(),
|
||||
manufacturer=MANUFACTURER,
|
||||
identifiers={(DOMAIN, f"{mqtt_output.device.serial_number}_{ref_id}")},
|
||||
suggested_area=mqtt_output.location.title(),
|
||||
via_device=create_main_device_identifier(mqtt_output),
|
||||
)
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Run when entity about to be added to hass."""
|
||||
|
@ -11,7 +11,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.util.color import brightness_to_value, value_to_brightness
|
||||
|
||||
from .coordinator import QbusConfigEntry
|
||||
from .entity import QbusEntity, add_new_outputs
|
||||
from .entity import QbusEntity, create_new_entities
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
@ -27,13 +27,13 @@ async def async_setup_entry(
|
||||
added_outputs: list[QbusMqttOutput] = []
|
||||
|
||||
def _check_outputs() -> None:
|
||||
add_new_outputs(
|
||||
entities = create_new_entities(
|
||||
coordinator,
|
||||
added_outputs,
|
||||
lambda output: output.type == "analog",
|
||||
QbusLight,
|
||||
async_add_entities,
|
||||
)
|
||||
async_add_entities(entities)
|
||||
|
||||
_check_outputs()
|
||||
entry.async_on_unload(coordinator.async_add_listener(_check_outputs))
|
||||
|
@ -7,6 +7,7 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/qbus",
|
||||
"integration_type": "hub",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["qbusmqttapi"],
|
||||
"mqtt": [
|
||||
"cloudapp/QBUSMQTTGW/state",
|
||||
"cloudapp/QBUSMQTTGW/config",
|
||||
|
@ -7,11 +7,10 @@ from qbusmqttapi.state import QbusMqttState, StateAction, StateType
|
||||
|
||||
from homeassistant.components.scene import Scene
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .coordinator import QbusConfigEntry
|
||||
from .entity import QbusEntity, add_new_outputs, create_main_device_identifier
|
||||
from .entity import QbusEntity, create_new_entities
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
@ -27,13 +26,13 @@ async def async_setup_entry(
|
||||
added_outputs: list[QbusMqttOutput] = []
|
||||
|
||||
def _check_outputs() -> None:
|
||||
add_new_outputs(
|
||||
entities = create_new_entities(
|
||||
coordinator,
|
||||
added_outputs,
|
||||
lambda output: output.type == "scene",
|
||||
QbusScene,
|
||||
async_add_entities,
|
||||
)
|
||||
async_add_entities(entities)
|
||||
|
||||
_check_outputs()
|
||||
entry.async_on_unload(coordinator.async_add_listener(_check_outputs))
|
||||
@ -45,12 +44,8 @@ class QbusScene(QbusEntity, Scene):
|
||||
def __init__(self, mqtt_output: QbusMqttOutput) -> None:
|
||||
"""Initialize scene entity."""
|
||||
|
||||
super().__init__(mqtt_output)
|
||||
super().__init__(mqtt_output, link_to_main_device=True)
|
||||
|
||||
# Add to main controller device
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={create_main_device_identifier(mqtt_output)}
|
||||
)
|
||||
self._attr_name = mqtt_output.name.title()
|
||||
|
||||
async def async_activate(self, **kwargs: Any) -> None:
|
||||
|
378
homeassistant/components/qbus/sensor.py
Normal file
378
homeassistant/components/qbus/sensor.py
Normal file
@ -0,0 +1,378 @@
|
||||
"""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
|
@ -16,6 +16,22 @@
|
||||
"no_controller": "No controllers were found"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"daylight": {
|
||||
"name": "Daylight"
|
||||
},
|
||||
"light_east": {
|
||||
"name": "Illuminance east"
|
||||
},
|
||||
"light_south": {
|
||||
"name": "Illuminance south"
|
||||
},
|
||||
"light_west": {
|
||||
"name": "Illuminance west"
|
||||
}
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
"invalid_preset": {
|
||||
"message": "Preset mode \"{preset}\" is not valid. Valid preset modes are: {options}."
|
||||
|
@ -10,7 +10,7 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .coordinator import QbusConfigEntry
|
||||
from .entity import QbusEntity, add_new_outputs
|
||||
from .entity import QbusEntity, create_new_entities
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
@ -26,13 +26,13 @@ async def async_setup_entry(
|
||||
added_outputs: list[QbusMqttOutput] = []
|
||||
|
||||
def _check_outputs() -> None:
|
||||
add_new_outputs(
|
||||
entities = create_new_entities(
|
||||
coordinator,
|
||||
added_outputs,
|
||||
lambda output: output.type == "onoff",
|
||||
QbusSwitch,
|
||||
async_add_entities,
|
||||
)
|
||||
async_add_entities(entities)
|
||||
|
||||
_check_outputs()
|
||||
entry.async_on_unload(coordinator.async_add_listener(_check_outputs))
|
||||
|
@ -1,6 +1,6 @@
|
||||
"""Test fixtures for qbus."""
|
||||
|
||||
from collections.abc import Generator
|
||||
from collections.abc import Awaitable, Callable, Generator
|
||||
import json
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
@ -64,3 +64,22 @@ async def setup_integration(
|
||||
|
||||
async_fire_mqtt_message(hass, TOPIC_CONFIG, json.dumps(payload_config))
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def setup_integration_deferred(
|
||||
hass: HomeAssistant,
|
||||
mqtt_mock: MqttMockHAClient,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
payload_config: JsonObjectType,
|
||||
) -> Callable[[], Awaitable]:
|
||||
"""Set up the integration."""
|
||||
|
||||
async def run() -> None:
|
||||
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
async_fire_mqtt_message(hass, TOPIC_CONFIG, json.dumps(payload_config))
|
||||
await hass.async_block_till_done()
|
||||
|
||||
return run
|
||||
|
@ -116,7 +116,7 @@
|
||||
{
|
||||
"id": "UL30",
|
||||
"location": "Guest bedroom",
|
||||
"locationId": 0,
|
||||
"locationId": 3,
|
||||
"name": "CURTAINS",
|
||||
"originalName": "CURTAINS",
|
||||
"refId": "000001/108",
|
||||
@ -144,7 +144,7 @@
|
||||
},
|
||||
"id": "UL31",
|
||||
"location": "Living",
|
||||
"locationId": 8,
|
||||
"locationId": 0,
|
||||
"name": "SLATS",
|
||||
"originalName": "SLATS",
|
||||
"properties": {
|
||||
@ -183,6 +183,340 @@
|
||||
},
|
||||
"refId": "000001/4",
|
||||
"type": "shutter"
|
||||
},
|
||||
{
|
||||
"id": "UL40",
|
||||
"location": "Tuin",
|
||||
"locationId": 12,
|
||||
"name": "Luchtdruk",
|
||||
"originalName": "Luchtdruk",
|
||||
"refId": "000001/81",
|
||||
"type": "gauge",
|
||||
"variant": "AirPressure",
|
||||
"actions": {},
|
||||
"properties": {
|
||||
"currentValue": {
|
||||
"max": 1500,
|
||||
"min": 0,
|
||||
"read": true,
|
||||
"step": 0.1,
|
||||
"type": "number",
|
||||
"unit": "mbar",
|
||||
"write": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "UL41",
|
||||
"location": "Tuin",
|
||||
"locationId": 12,
|
||||
"name": "Luchtkwaliteit",
|
||||
"originalName": "Luchtkwaliteit",
|
||||
"refId": "000001/82",
|
||||
"type": "gauge",
|
||||
"variant": "AirQuality",
|
||||
"actions": {},
|
||||
"properties": {
|
||||
"currentValue": {
|
||||
"max": 1500,
|
||||
"min": 0,
|
||||
"read": true,
|
||||
"step": 0.1,
|
||||
"type": "number",
|
||||
"unit": "ppm",
|
||||
"write": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "UL42",
|
||||
"location": "Garage",
|
||||
"locationId": 27,
|
||||
"name": "Stroom",
|
||||
"originalName": "Stroom",
|
||||
"refId": "000001/83",
|
||||
"type": "gauge",
|
||||
"variant": "Current",
|
||||
"actions": {},
|
||||
"properties": {
|
||||
"currentValue": {
|
||||
"max": 100,
|
||||
"min": 0,
|
||||
"read": true,
|
||||
"step": 0.1,
|
||||
"type": "number",
|
||||
"unit": "kWh",
|
||||
"write": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "UL43",
|
||||
"location": "Garage",
|
||||
"locationId": 27,
|
||||
"name": "Energie",
|
||||
"originalName": "Energie",
|
||||
"refId": "000001/84",
|
||||
"type": "gauge",
|
||||
"variant": "Energy",
|
||||
"actions": {},
|
||||
"properties": {
|
||||
"currentValue": {
|
||||
"read": true,
|
||||
"step": 0.1,
|
||||
"type": "number",
|
||||
"unit": "A",
|
||||
"write": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "UL44",
|
||||
"location": "Garage",
|
||||
"locationId": 27,
|
||||
"name": "Gas",
|
||||
"originalName": "Gas",
|
||||
"refId": "000001/85",
|
||||
"type": "gauge",
|
||||
"variant": "Gas",
|
||||
"actions": {},
|
||||
"properties": {
|
||||
"currentValue": {
|
||||
"max": 5,
|
||||
"min": 0,
|
||||
"read": true,
|
||||
"step": 0.001,
|
||||
"type": "number",
|
||||
"unit": "m³/h",
|
||||
"write": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "UL45",
|
||||
"location": "Garage",
|
||||
"locationId": 27,
|
||||
"name": "Gas flow",
|
||||
"originalName": "Gas flow",
|
||||
"refId": "000001/86",
|
||||
"type": "gauge",
|
||||
"variant": "GasFlow",
|
||||
"actions": {},
|
||||
"properties": {
|
||||
"currentValue": {
|
||||
"max": 10,
|
||||
"min": 0,
|
||||
"read": true,
|
||||
"step": 0.1,
|
||||
"type": "number",
|
||||
"unit": "m³/h",
|
||||
"write": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "UL46",
|
||||
"location": "Living",
|
||||
"locationId": 0,
|
||||
"name": "Vochtigheid living",
|
||||
"originalName": "Vochtigheid living",
|
||||
"refId": "000001/87",
|
||||
"type": "gauge",
|
||||
"variant": "Humidity",
|
||||
"actions": {},
|
||||
"properties": {
|
||||
"currentValue": {
|
||||
"max": 100,
|
||||
"min": 0,
|
||||
"read": true,
|
||||
"step": 0.1,
|
||||
"type": "number",
|
||||
"unit": "%",
|
||||
"write": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "UL47",
|
||||
"location": "Tuin",
|
||||
"locationId": 12,
|
||||
"name": "Lichtsterkte tuin",
|
||||
"originalName": "Lichtsterkte tuin",
|
||||
"refId": "000001/88",
|
||||
"type": "gauge",
|
||||
"variant": "Light",
|
||||
"actions": {},
|
||||
"properties": {
|
||||
"currentValue": {
|
||||
"max": 100000,
|
||||
"min": 0,
|
||||
"read": true,
|
||||
"step": 0.1,
|
||||
"type": "number",
|
||||
"unit": "lx",
|
||||
"write": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "UL40",
|
||||
"location": "Tuin",
|
||||
"locationId": 12,
|
||||
"name": "Regenput",
|
||||
"originalName": "Regenput",
|
||||
"refId": "000001/40",
|
||||
"type": "gauge",
|
||||
"variant": "WaterLevel",
|
||||
"actions": {},
|
||||
"properties": {
|
||||
"currentValue": {
|
||||
"max": 100,
|
||||
"min": 0,
|
||||
"read": true,
|
||||
"step": 0.1,
|
||||
"type": "number",
|
||||
"unit": "m",
|
||||
"write": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "UL60",
|
||||
"location": "Tuin",
|
||||
"locationId": 12,
|
||||
"name": "Weersensor",
|
||||
"originalName": "Weersensor",
|
||||
"refId": "000001/21007",
|
||||
"type": "weatherstation",
|
||||
"variant": [null],
|
||||
"actions": {},
|
||||
"properties": {
|
||||
"dayLight": {
|
||||
"max": 1000,
|
||||
"min": 0,
|
||||
"read": true,
|
||||
"step": 0.1,
|
||||
"type": "number",
|
||||
"write": false
|
||||
},
|
||||
"light": {
|
||||
"max": 100000,
|
||||
"min": 0,
|
||||
"read": true,
|
||||
"step": 0.1,
|
||||
"type": "number",
|
||||
"write": false
|
||||
},
|
||||
"lightEast": {
|
||||
"max": 100000,
|
||||
"min": 0,
|
||||
"read": true,
|
||||
"step": 0.1,
|
||||
"type": "number",
|
||||
"write": false
|
||||
},
|
||||
"lightSouth": {
|
||||
"max": 100000,
|
||||
"min": 0,
|
||||
"read": true,
|
||||
"step": 0.1,
|
||||
"type": "number",
|
||||
"write": false
|
||||
},
|
||||
"lightWest": {
|
||||
"max": 100000,
|
||||
"min": 0,
|
||||
"read": true,
|
||||
"step": 0.1,
|
||||
"type": "number",
|
||||
"write": false
|
||||
},
|
||||
"raining": {
|
||||
"read": true,
|
||||
"type": "boolean",
|
||||
"write": false
|
||||
},
|
||||
"temperature": {
|
||||
"max": 100,
|
||||
"min": -100,
|
||||
"read": true,
|
||||
"step": 0.1,
|
||||
"type": "number",
|
||||
"write": false
|
||||
},
|
||||
"twilight": {
|
||||
"read": true,
|
||||
"type": "boolean",
|
||||
"write": false
|
||||
},
|
||||
"wind": {
|
||||
"max": 1000,
|
||||
"min": 0,
|
||||
"read": true,
|
||||
"step": 0.1,
|
||||
"type": "number",
|
||||
"write": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "UL70",
|
||||
"location": "",
|
||||
"locationId": 0,
|
||||
"name": "Luchtsensor",
|
||||
"originalName": "Luchtsensor",
|
||||
"refId": "000001/224",
|
||||
"type": "ventilation",
|
||||
"variant": [null],
|
||||
"actions": {},
|
||||
"properties": {
|
||||
"co2": {
|
||||
"max": 5000,
|
||||
"min": 0,
|
||||
"read": true,
|
||||
"step": 16,
|
||||
"type": "number",
|
||||
"unit": "ppm",
|
||||
"write": false
|
||||
},
|
||||
"currRegime": {
|
||||
"enumValues": ["Manueel", "Nacht", "Boost", "Uit", "Auto"],
|
||||
"read": true,
|
||||
"type": "enumString",
|
||||
"write": true
|
||||
},
|
||||
"refresh": {
|
||||
"max": 100,
|
||||
"min": 0,
|
||||
"read": true,
|
||||
"step": 1,
|
||||
"type": "number",
|
||||
"write": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "UL80",
|
||||
"location": "Kitchen",
|
||||
"locationId": 8,
|
||||
"name": "Vochtigheid keuken",
|
||||
"originalName": "Vochtigheid keuken",
|
||||
"properties": {
|
||||
"currRegime": {
|
||||
"enumValues": ["Manual", "Cook", "Boost", "Off", "Auto"],
|
||||
"read": true,
|
||||
"type": "enumString",
|
||||
"write": true
|
||||
},
|
||||
"value": {
|
||||
"read": true,
|
||||
"step": 1,
|
||||
"type": "percent",
|
||||
"write": false
|
||||
}
|
||||
},
|
||||
"refId": "000001/94/1",
|
||||
"type": "humidity"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
1047
tests/components/qbus/snapshots/test_sensor.ambr
Normal file
1047
tests/components/qbus/snapshots/test_sensor.ambr
Normal file
File diff suppressed because it is too large
Load Diff
27
tests/components/qbus/test_sensor.py
Normal file
27
tests/components/qbus/test_sensor.py
Normal file
@ -0,0 +1,27 @@
|
||||
"""Test Qbus sensors."""
|
||||
|
||||
from collections.abc import Awaitable, Callable
|
||||
from unittest.mock import patch
|
||||
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from tests.common import MockConfigEntry, snapshot_platform
|
||||
|
||||
|
||||
async def test_sensor(
|
||||
hass: HomeAssistant,
|
||||
setup_integration_deferred: Callable[[], Awaitable],
|
||||
entity_registry: er.EntityRegistry,
|
||||
snapshot: SnapshotAssertion,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test sensor."""
|
||||
|
||||
with patch("homeassistant.components.qbus.PLATFORMS", [Platform.SENSOR]):
|
||||
await setup_integration_deferred()
|
||||
|
||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
Loading…
x
Reference in New Issue
Block a user