Add sensor platform to Qbus integration (#149389)

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
Thomas D 2025-08-02 20:01:58 +02:00 committed by GitHub
parent fa476d4e34
commit 755864f9f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 1882 additions and 40 deletions

View File

@ -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))

View File

@ -10,6 +10,7 @@ PLATFORMS: list[Platform] = [
Platform.COVER,
Platform.LIGHT,
Platform.SCENE,
Platform.SENSOR,
Platform.SWITCH,
]

View File

@ -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))

View File

@ -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,10 +95,18 @@ 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_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,

View File

@ -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))

View File

@ -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",

View File

@ -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:

View 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

View File

@ -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}."

View File

@ -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))

View File

@ -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

View File

@ -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"
}
]
}

File diff suppressed because it is too large Load Diff

View 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)