From 755864f9f3b0d0465f283b82393db7d14b38b397 Mon Sep 17 00:00:00 2001 From: Thomas D <11554546+thomasddn@users.noreply.github.com> Date: Sat, 2 Aug 2025 20:01:58 +0200 Subject: [PATCH] Add sensor platform to Qbus integration (#149389) Co-authored-by: Joost Lekkerkerker --- homeassistant/components/qbus/climate.py | 6 +- homeassistant/components/qbus/const.py | 1 + homeassistant/components/qbus/cover.py | 6 +- homeassistant/components/qbus/entity.py | 56 +- homeassistant/components/qbus/light.py | 6 +- homeassistant/components/qbus/manifest.json | 1 + homeassistant/components/qbus/scene.py | 13 +- homeassistant/components/qbus/sensor.py | 378 ++++++ homeassistant/components/qbus/strings.json | 16 + homeassistant/components/qbus/switch.py | 6 +- tests/components/qbus/conftest.py | 21 +- .../qbus/fixtures/payload_config.json | 338 +++++- .../qbus/snapshots/test_sensor.ambr | 1047 +++++++++++++++++ tests/components/qbus/test_sensor.py | 27 + 14 files changed, 1882 insertions(+), 40 deletions(-) create mode 100644 homeassistant/components/qbus/sensor.py create mode 100644 tests/components/qbus/snapshots/test_sensor.ambr create mode 100644 tests/components/qbus/test_sensor.py diff --git a/homeassistant/components/qbus/climate.py b/homeassistant/components/qbus/climate.py index caaec2f95d7..a19ec4d0156 100644 --- a/homeassistant/components/qbus/climate.py +++ b/homeassistant/components/qbus/climate.py @@ -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)) diff --git a/homeassistant/components/qbus/const.py b/homeassistant/components/qbus/const.py index 73819d2a11b..133a3b8fea9 100644 --- a/homeassistant/components/qbus/const.py +++ b/homeassistant/components/qbus/const.py @@ -10,6 +10,7 @@ PLATFORMS: list[Platform] = [ Platform.COVER, Platform.LIGHT, Platform.SCENE, + Platform.SENSOR, Platform.SWITCH, ] diff --git a/homeassistant/components/qbus/cover.py b/homeassistant/components/qbus/cover.py index 2adb8253551..3fc1b20602a 100644 --- a/homeassistant/components/qbus/cover.py +++ b/homeassistant/components/qbus/cover.py @@ -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)) diff --git a/homeassistant/components/qbus/entity.py b/homeassistant/components/qbus/entity.py index 91e4d83b548..9fb481d4515 100644 --- a/homeassistant/components/qbus/entity.py +++ b/homeassistant/components/qbus/entity.py @@ -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.""" diff --git a/homeassistant/components/qbus/light.py b/homeassistant/components/qbus/light.py index 4385cfe60f0..61225f11243 100644 --- a/homeassistant/components/qbus/light.py +++ b/homeassistant/components/qbus/light.py @@ -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)) diff --git a/homeassistant/components/qbus/manifest.json b/homeassistant/components/qbus/manifest.json index feffa6e492c..15392f6cc97 100644 --- a/homeassistant/components/qbus/manifest.json +++ b/homeassistant/components/qbus/manifest.json @@ -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", diff --git a/homeassistant/components/qbus/scene.py b/homeassistant/components/qbus/scene.py index 8d18feb26d3..706fb089dde 100644 --- a/homeassistant/components/qbus/scene.py +++ b/homeassistant/components/qbus/scene.py @@ -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: diff --git a/homeassistant/components/qbus/sensor.py b/homeassistant/components/qbus/sensor.py new file mode 100644 index 00000000000..e983e0a8cbb --- /dev/null +++ b/homeassistant/components/qbus/sensor.py @@ -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 diff --git a/homeassistant/components/qbus/strings.json b/homeassistant/components/qbus/strings.json index f308c5b3519..f3a0d108476 100644 --- a/homeassistant/components/qbus/strings.json +++ b/homeassistant/components/qbus/strings.json @@ -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}." diff --git a/homeassistant/components/qbus/switch.py b/homeassistant/components/qbus/switch.py index 05283a44cfc..3c4d280fa30 100644 --- a/homeassistant/components/qbus/switch.py +++ b/homeassistant/components/qbus/switch.py @@ -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)) diff --git a/tests/components/qbus/conftest.py b/tests/components/qbus/conftest.py index 9b42a6a3de8..e1febea524b 100644 --- a/tests/components/qbus/conftest.py +++ b/tests/components/qbus/conftest.py @@ -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 diff --git a/tests/components/qbus/fixtures/payload_config.json b/tests/components/qbus/fixtures/payload_config.json index 2cad6c623db..883eca19276 100644 --- a/tests/components/qbus/fixtures/payload_config.json +++ b/tests/components/qbus/fixtures/payload_config.json @@ -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" } ] } diff --git a/tests/components/qbus/snapshots/test_sensor.ambr b/tests/components/qbus/snapshots/test_sensor.ambr new file mode 100644 index 00000000000..fe665057b1e --- /dev/null +++ b/tests/components/qbus/snapshots/test_sensor.ambr @@ -0,0 +1,1047 @@ +# serializer version: 1 +# name: test_sensor[sensor.energie-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.energie', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': None, + 'platform': 'qbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': 'ctd_000001_84', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor[sensor.energie-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'energy', + 'friendly_name': 'Energie', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.energie', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_sensor[sensor.gas-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.gas', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': None, + 'platform': 'qbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': 'ctd_000001_85', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor[sensor.gas-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'volume_flow_rate', + 'friendly_name': 'Gas', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.gas', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_sensor[sensor.gas_flow-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.gas_flow', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': None, + 'platform': 'qbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': 'ctd_000001_86', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor[sensor.gas_flow-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'volume_flow_rate', + 'friendly_name': 'Gas Flow', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.gas_flow', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_sensor[sensor.lichtsterkte_tuin-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.lichtsterkte_tuin', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': None, + 'platform': 'qbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': 'ctd_000001_88', + 'unit_of_measurement': 'lx', + }) +# --- +# name: test_sensor[sensor.lichtsterkte_tuin-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'illuminance', + 'friendly_name': 'Lichtsterkte Tuin', + 'state_class': , + 'unit_of_measurement': 'lx', + }), + 'context': , + 'entity_id': 'sensor.lichtsterkte_tuin', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_sensor[sensor.living_th_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.living_th_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'qbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': 'ctd_000001_120', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor[sensor.living_th_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'Living Th Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.living_th_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_sensor[sensor.luchtdruk-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.luchtdruk', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': None, + 'platform': 'qbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': 'ctd_000001_81', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor[sensor.luchtdruk-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'pressure', + 'friendly_name': 'Luchtdruk', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.luchtdruk', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_sensor[sensor.luchtkwaliteit-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.luchtkwaliteit', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': None, + 'platform': 'qbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': 'ctd_000001_82', + 'unit_of_measurement': 'ppm', + }) +# --- +# name: test_sensor[sensor.luchtkwaliteit-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'carbon_dioxide', + 'friendly_name': 'Luchtkwaliteit', + 'state_class': , + 'unit_of_measurement': 'ppm', + }), + 'context': , + 'entity_id': 'sensor.luchtkwaliteit', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_sensor[sensor.luchtsensor-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.luchtsensor', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': None, + 'platform': 'qbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': 'ctd_000001_224', + 'unit_of_measurement': 'ppm', + }) +# --- +# name: test_sensor[sensor.luchtsensor-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'carbon_dioxide', + 'friendly_name': 'Luchtsensor', + 'state_class': , + 'unit_of_measurement': 'ppm', + }), + 'context': , + 'entity_id': 'sensor.luchtsensor', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_sensor[sensor.regenput-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.regenput', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': None, + 'platform': 'qbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': 'ctd_000001_40', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor[sensor.regenput-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'distance', + 'friendly_name': 'Regenput', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.regenput', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_sensor[sensor.stroom-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.stroom', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': None, + 'platform': 'qbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': 'ctd_000001_83', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor[sensor.stroom-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'current', + 'friendly_name': 'Stroom', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.stroom', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_sensor[sensor.vochtigheid_keuken-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.vochtigheid_keuken', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': None, + 'platform': 'qbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': 'ctd_000001_94-1', + 'unit_of_measurement': '%', + }) +# --- +# name: test_sensor[sensor.vochtigheid_keuken-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'humidity', + 'friendly_name': 'Vochtigheid Keuken', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.vochtigheid_keuken', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_sensor[sensor.vochtigheid_living-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.vochtigheid_living', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': None, + 'platform': 'qbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': 'ctd_000001_87', + 'unit_of_measurement': '%', + }) +# --- +# name: test_sensor[sensor.vochtigheid_living-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'humidity', + 'friendly_name': 'Vochtigheid Living', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.vochtigheid_living', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_sensor[sensor.weersensor-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.weersensor', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': None, + 'platform': 'qbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': 'ctd_000001_21007_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor[sensor.weersensor-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'Weersensor', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.weersensor', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_sensor[sensor.weersensor_daylight-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.weersensor_daylight', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Daylight', + 'platform': 'qbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'daylight', + 'unique_id': 'ctd_000001_21007_daylight', + 'unit_of_measurement': 'lx', + }) +# --- +# name: test_sensor[sensor.weersensor_daylight-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'illuminance', + 'friendly_name': 'Weersensor Daylight', + 'state_class': , + 'unit_of_measurement': 'lx', + }), + 'context': , + 'entity_id': 'sensor.weersensor_daylight', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_sensor[sensor.weersensor_illuminance-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.weersensor_illuminance', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Illuminance', + 'platform': 'qbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': 'ctd_000001_21007_light', + 'unit_of_measurement': 'lx', + }) +# --- +# name: test_sensor[sensor.weersensor_illuminance-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'illuminance', + 'friendly_name': 'Weersensor Illuminance', + 'state_class': , + 'unit_of_measurement': 'lx', + }), + 'context': , + 'entity_id': 'sensor.weersensor_illuminance', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_sensor[sensor.weersensor_illuminance_east-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.weersensor_illuminance_east', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Illuminance east', + 'platform': 'qbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'light_east', + 'unique_id': 'ctd_000001_21007_light_east', + 'unit_of_measurement': 'lx', + }) +# --- +# name: test_sensor[sensor.weersensor_illuminance_east-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'illuminance', + 'friendly_name': 'Weersensor Illuminance east', + 'state_class': , + 'unit_of_measurement': 'lx', + }), + 'context': , + 'entity_id': 'sensor.weersensor_illuminance_east', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_sensor[sensor.weersensor_illuminance_south-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.weersensor_illuminance_south', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Illuminance south', + 'platform': 'qbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'light_south', + 'unique_id': 'ctd_000001_21007_light_south', + 'unit_of_measurement': 'lx', + }) +# --- +# name: test_sensor[sensor.weersensor_illuminance_south-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'illuminance', + 'friendly_name': 'Weersensor Illuminance south', + 'state_class': , + 'unit_of_measurement': 'lx', + }), + 'context': , + 'entity_id': 'sensor.weersensor_illuminance_south', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_sensor[sensor.weersensor_illuminance_west-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.weersensor_illuminance_west', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Illuminance west', + 'platform': 'qbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'light_west', + 'unique_id': 'ctd_000001_21007_light_west', + 'unit_of_measurement': 'lx', + }) +# --- +# name: test_sensor[sensor.weersensor_illuminance_west-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'illuminance', + 'friendly_name': 'Weersensor Illuminance west', + 'state_class': , + 'unit_of_measurement': 'lx', + }), + 'context': , + 'entity_id': 'sensor.weersensor_illuminance_west', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_sensor[sensor.weersensor_wind_speed-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.weersensor_wind_speed', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Wind speed', + 'platform': 'qbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': 'ctd_000001_21007_wind', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor[sensor.weersensor_wind_speed-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'wind_speed', + 'friendly_name': 'Weersensor Wind speed', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.weersensor_wind_speed', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- diff --git a/tests/components/qbus/test_sensor.py b/tests/components/qbus/test_sensor.py new file mode 100644 index 00000000000..255b29eb7f0 --- /dev/null +++ b/tests/components/qbus/test_sensor.py @@ -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)