mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Add support for multiple mbus devices in dsmr (#84097)
* Add support for multiple mbus devices in dsmr A dsmr meter can have 4 mbus devices. Support them all and also add support for a water meter on the mbus device. * Apply suggestions from code review Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com> * Rewrite old gas sensor to new mbus sensor * No force updates + fix mbus entity unique_id * Remove old gas device * Add additional tests * Fix remarks from last review + move migrated 5b gas meter to new device_id * Fix ruff error * Last fixes --------- Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
This commit is contained in:
parent
36eb858d0a
commit
ba481001c3
@ -29,6 +29,7 @@ DATA_TASK = "task"
|
|||||||
|
|
||||||
DEVICE_NAME_ELECTRICITY = "Electricity Meter"
|
DEVICE_NAME_ELECTRICITY = "Electricity Meter"
|
||||||
DEVICE_NAME_GAS = "Gas Meter"
|
DEVICE_NAME_GAS = "Gas Meter"
|
||||||
|
DEVICE_NAME_WATER = "Water Meter"
|
||||||
|
|
||||||
DSMR_VERSIONS = {"2.2", "4", "5", "5B", "5L", "5S", "Q3D"}
|
DSMR_VERSIONS = {"2.2", "4", "5", "5B", "5L", "5S", "Q3D"}
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ from homeassistant.const import (
|
|||||||
UnitOfVolume,
|
UnitOfVolume,
|
||||||
)
|
)
|
||||||
from homeassistant.core import CoreState, Event, HomeAssistant, callback
|
from homeassistant.core import CoreState, Event, HomeAssistant, callback
|
||||||
|
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||||
from homeassistant.helpers.device_registry import DeviceInfo
|
from homeassistant.helpers.device_registry import DeviceInfo
|
||||||
from homeassistant.helpers.dispatcher import (
|
from homeassistant.helpers.dispatcher import (
|
||||||
async_dispatcher_connect,
|
async_dispatcher_connect,
|
||||||
@ -57,6 +58,7 @@ from .const import (
|
|||||||
DEFAULT_TIME_BETWEEN_UPDATE,
|
DEFAULT_TIME_BETWEEN_UPDATE,
|
||||||
DEVICE_NAME_ELECTRICITY,
|
DEVICE_NAME_ELECTRICITY,
|
||||||
DEVICE_NAME_GAS,
|
DEVICE_NAME_GAS,
|
||||||
|
DEVICE_NAME_WATER,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
DSMR_PROTOCOL,
|
DSMR_PROTOCOL,
|
||||||
LOGGER,
|
LOGGER,
|
||||||
@ -73,6 +75,7 @@ class DSMRSensorEntityDescription(SensorEntityDescription):
|
|||||||
|
|
||||||
dsmr_versions: set[str] | None = None
|
dsmr_versions: set[str] | None = None
|
||||||
is_gas: bool = False
|
is_gas: bool = False
|
||||||
|
is_water: bool = False
|
||||||
obis_reference: str
|
obis_reference: str
|
||||||
|
|
||||||
|
|
||||||
@ -374,28 +377,138 @@ SENSORS: tuple[DSMRSensorEntityDescription, ...] = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def add_gas_sensor_5B(telegram: dict[str, DSMRObject]) -> DSMRSensorEntityDescription:
|
def create_mbus_entity(
|
||||||
"""Return correct entity for 5B Gas meter."""
|
mbus: int, mtype: int, telegram: dict[str, DSMRObject]
|
||||||
ref = None
|
) -> DSMRSensorEntityDescription | None:
|
||||||
if obis_references.BELGIUM_MBUS1_METER_READING2 in telegram:
|
"""Create a new MBUS Entity."""
|
||||||
ref = obis_references.BELGIUM_MBUS1_METER_READING2
|
if (
|
||||||
elif obis_references.BELGIUM_MBUS2_METER_READING2 in telegram:
|
mtype == 3
|
||||||
ref = obis_references.BELGIUM_MBUS2_METER_READING2
|
and (
|
||||||
elif obis_references.BELGIUM_MBUS3_METER_READING2 in telegram:
|
obis_reference := getattr(
|
||||||
ref = obis_references.BELGIUM_MBUS3_METER_READING2
|
obis_references, f"BELGIUM_MBUS{mbus}_METER_READING2"
|
||||||
elif obis_references.BELGIUM_MBUS4_METER_READING2 in telegram:
|
)
|
||||||
ref = obis_references.BELGIUM_MBUS4_METER_READING2
|
)
|
||||||
elif ref is None:
|
in telegram
|
||||||
ref = obis_references.BELGIUM_MBUS1_METER_READING2
|
):
|
||||||
return DSMRSensorEntityDescription(
|
return DSMRSensorEntityDescription(
|
||||||
key="belgium_5min_gas_meter_reading",
|
key=f"mbus{mbus}_gas_reading",
|
||||||
translation_key="gas_meter_reading",
|
translation_key="gas_meter_reading",
|
||||||
obis_reference=ref,
|
obis_reference=obis_reference,
|
||||||
dsmr_versions={"5B"},
|
is_gas=True,
|
||||||
is_gas=True,
|
device_class=SensorDeviceClass.GAS,
|
||||||
device_class=SensorDeviceClass.GAS,
|
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
)
|
||||||
)
|
if (
|
||||||
|
mtype == 7
|
||||||
|
and (
|
||||||
|
obis_reference := getattr(
|
||||||
|
obis_references, f"BELGIUM_MBUS{mbus}_METER_READING1"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
in telegram
|
||||||
|
):
|
||||||
|
return DSMRSensorEntityDescription(
|
||||||
|
key=f"mbus{mbus}_water_reading",
|
||||||
|
translation_key="water_meter_reading",
|
||||||
|
obis_reference=obis_reference,
|
||||||
|
is_water=True,
|
||||||
|
device_class=SensorDeviceClass.WATER,
|
||||||
|
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def device_class_and_uom(
|
||||||
|
telegram: dict[str, DSMRObject],
|
||||||
|
entity_description: DSMRSensorEntityDescription,
|
||||||
|
) -> tuple[SensorDeviceClass | None, str | None]:
|
||||||
|
"""Get native unit of measurement from telegram,."""
|
||||||
|
dsmr_object = telegram[entity_description.obis_reference]
|
||||||
|
uom: str | None = getattr(dsmr_object, "unit") or None
|
||||||
|
with suppress(ValueError):
|
||||||
|
if entity_description.device_class == SensorDeviceClass.GAS and (
|
||||||
|
enery_uom := UnitOfEnergy(str(uom))
|
||||||
|
):
|
||||||
|
return (SensorDeviceClass.ENERGY, enery_uom)
|
||||||
|
if uom in UNIT_CONVERSION:
|
||||||
|
return (entity_description.device_class, UNIT_CONVERSION[uom])
|
||||||
|
return (entity_description.device_class, uom)
|
||||||
|
|
||||||
|
|
||||||
|
def rename_old_gas_to_mbus(
|
||||||
|
hass: HomeAssistant, entry: ConfigEntry, mbus_device_id: str
|
||||||
|
) -> None:
|
||||||
|
"""Rename old gas sensor to mbus variant."""
|
||||||
|
dev_reg = dr.async_get(hass)
|
||||||
|
device_entry_v1 = dev_reg.async_get_device(identifiers={(DOMAIN, entry.entry_id)})
|
||||||
|
if device_entry_v1 is not None:
|
||||||
|
device_id = device_entry_v1.id
|
||||||
|
|
||||||
|
ent_reg = er.async_get(hass)
|
||||||
|
entries = er.async_entries_for_device(ent_reg, device_id)
|
||||||
|
|
||||||
|
for entity in entries:
|
||||||
|
if entity.unique_id.endswith("belgium_5min_gas_meter_reading"):
|
||||||
|
try:
|
||||||
|
ent_reg.async_update_entity(
|
||||||
|
entity.entity_id,
|
||||||
|
new_unique_id=mbus_device_id,
|
||||||
|
device_id=mbus_device_id,
|
||||||
|
)
|
||||||
|
except ValueError:
|
||||||
|
LOGGER.warning(
|
||||||
|
"Skip migration of %s because it already exists",
|
||||||
|
entity.entity_id,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
LOGGER.info(
|
||||||
|
"Migrated entity %s from unique id %s to %s",
|
||||||
|
entity.entity_id,
|
||||||
|
entity.unique_id,
|
||||||
|
mbus_device_id,
|
||||||
|
)
|
||||||
|
# Cleanup old device
|
||||||
|
dev_entities = er.async_entries_for_device(
|
||||||
|
ent_reg, device_id, include_disabled_entities=True
|
||||||
|
)
|
||||||
|
if not dev_entities:
|
||||||
|
dev_reg.async_remove_device(device_id)
|
||||||
|
|
||||||
|
|
||||||
|
def create_mbus_entities(
|
||||||
|
hass: HomeAssistant, telegram: dict[str, DSMRObject], entry: ConfigEntry
|
||||||
|
) -> list[DSMREntity]:
|
||||||
|
"""Create MBUS Entities."""
|
||||||
|
entities = []
|
||||||
|
for idx in range(1, 5):
|
||||||
|
if (
|
||||||
|
device_type := getattr(obis_references, f"BELGIUM_MBUS{idx}_DEVICE_TYPE")
|
||||||
|
) not in telegram:
|
||||||
|
continue
|
||||||
|
if (type_ := int(telegram[device_type].value)) not in (3, 7):
|
||||||
|
continue
|
||||||
|
if (
|
||||||
|
identifier := getattr(
|
||||||
|
obis_references,
|
||||||
|
f"BELGIUM_MBUS{idx}_EQUIPMENT_IDENTIFIER",
|
||||||
|
)
|
||||||
|
) in telegram:
|
||||||
|
serial_ = telegram[identifier].value
|
||||||
|
rename_old_gas_to_mbus(hass, entry, serial_)
|
||||||
|
else:
|
||||||
|
serial_ = ""
|
||||||
|
if description := create_mbus_entity(idx, type_, telegram):
|
||||||
|
entities.append(
|
||||||
|
DSMREntity(
|
||||||
|
description,
|
||||||
|
entry,
|
||||||
|
telegram,
|
||||||
|
*device_class_and_uom(telegram, description), # type: ignore[arg-type]
|
||||||
|
serial_,
|
||||||
|
idx,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return entities
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
@ -415,25 +528,10 @@ async def async_setup_entry(
|
|||||||
add_entities_handler()
|
add_entities_handler()
|
||||||
add_entities_handler = None
|
add_entities_handler = None
|
||||||
|
|
||||||
def device_class_and_uom(
|
|
||||||
telegram: dict[str, DSMRObject],
|
|
||||||
entity_description: DSMRSensorEntityDescription,
|
|
||||||
) -> tuple[SensorDeviceClass | None, str | None]:
|
|
||||||
"""Get native unit of measurement from telegram,."""
|
|
||||||
dsmr_object = telegram[entity_description.obis_reference]
|
|
||||||
uom: str | None = getattr(dsmr_object, "unit") or None
|
|
||||||
with suppress(ValueError):
|
|
||||||
if entity_description.device_class == SensorDeviceClass.GAS and (
|
|
||||||
enery_uom := UnitOfEnergy(str(uom))
|
|
||||||
):
|
|
||||||
return (SensorDeviceClass.ENERGY, enery_uom)
|
|
||||||
if uom in UNIT_CONVERSION:
|
|
||||||
return (entity_description.device_class, UNIT_CONVERSION[uom])
|
|
||||||
return (entity_description.device_class, uom)
|
|
||||||
|
|
||||||
all_sensors = SENSORS
|
|
||||||
if dsmr_version == "5B":
|
if dsmr_version == "5B":
|
||||||
all_sensors += (add_gas_sensor_5B(telegram),)
|
mbus_entities = create_mbus_entities(hass, telegram, entry)
|
||||||
|
for mbus_entity in mbus_entities:
|
||||||
|
entities.append(mbus_entity)
|
||||||
|
|
||||||
entities.extend(
|
entities.extend(
|
||||||
[
|
[
|
||||||
@ -443,7 +541,7 @@ async def async_setup_entry(
|
|||||||
telegram,
|
telegram,
|
||||||
*device_class_and_uom(telegram, description), # type: ignore[arg-type]
|
*device_class_and_uom(telegram, description), # type: ignore[arg-type]
|
||||||
)
|
)
|
||||||
for description in all_sensors
|
for description in SENSORS
|
||||||
if (
|
if (
|
||||||
description.dsmr_versions is None
|
description.dsmr_versions is None
|
||||||
or dsmr_version in description.dsmr_versions
|
or dsmr_version in description.dsmr_versions
|
||||||
@ -618,6 +716,8 @@ class DSMREntity(SensorEntity):
|
|||||||
telegram: dict[str, DSMRObject],
|
telegram: dict[str, DSMRObject],
|
||||||
device_class: SensorDeviceClass,
|
device_class: SensorDeviceClass,
|
||||||
native_unit_of_measurement: str | None,
|
native_unit_of_measurement: str | None,
|
||||||
|
serial_id: str = "",
|
||||||
|
mbus_id: int = 0,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize entity."""
|
"""Initialize entity."""
|
||||||
self.entity_description = entity_description
|
self.entity_description = entity_description
|
||||||
@ -629,8 +729,15 @@ class DSMREntity(SensorEntity):
|
|||||||
device_serial = entry.data[CONF_SERIAL_ID]
|
device_serial = entry.data[CONF_SERIAL_ID]
|
||||||
device_name = DEVICE_NAME_ELECTRICITY
|
device_name = DEVICE_NAME_ELECTRICITY
|
||||||
if entity_description.is_gas:
|
if entity_description.is_gas:
|
||||||
device_serial = entry.data[CONF_SERIAL_ID_GAS]
|
if serial_id:
|
||||||
|
device_serial = serial_id
|
||||||
|
else:
|
||||||
|
device_serial = entry.data[CONF_SERIAL_ID_GAS]
|
||||||
device_name = DEVICE_NAME_GAS
|
device_name = DEVICE_NAME_GAS
|
||||||
|
if entity_description.is_water:
|
||||||
|
if serial_id:
|
||||||
|
device_serial = serial_id
|
||||||
|
device_name = DEVICE_NAME_WATER
|
||||||
if device_serial is None:
|
if device_serial is None:
|
||||||
device_serial = entry.entry_id
|
device_serial = entry.entry_id
|
||||||
|
|
||||||
@ -638,7 +745,13 @@ class DSMREntity(SensorEntity):
|
|||||||
identifiers={(DOMAIN, device_serial)},
|
identifiers={(DOMAIN, device_serial)},
|
||||||
name=device_name,
|
name=device_name,
|
||||||
)
|
)
|
||||||
self._attr_unique_id = f"{device_serial}_{entity_description.key}"
|
if mbus_id != 0:
|
||||||
|
if serial_id:
|
||||||
|
self._attr_unique_id = f"{device_serial}"
|
||||||
|
else:
|
||||||
|
self._attr_unique_id = f"{device_serial}_{mbus_id}"
|
||||||
|
else:
|
||||||
|
self._attr_unique_id = f"{device_serial}_{entity_description.key}"
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def update_data(self, telegram: dict[str, DSMRObject] | None) -> None:
|
def update_data(self, telegram: dict[str, DSMRObject] | None) -> None:
|
||||||
|
@ -147,6 +147,9 @@
|
|||||||
},
|
},
|
||||||
"voltage_swell_l3_count": {
|
"voltage_swell_l3_count": {
|
||||||
"name": "Voltage swells phase L3"
|
"name": "Voltage swells phase L3"
|
||||||
|
},
|
||||||
|
"water_meter_reading": {
|
||||||
|
"name": "Water consumption"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
212
tests/components/dsmr/test_mbus_migration.py
Normal file
212
tests/components/dsmr/test_mbus_migration.py
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
"""Tests for the DSMR integration."""
|
||||||
|
import datetime
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
from homeassistant.components.dsmr.const import DOMAIN
|
||||||
|
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
|
async def test_migrate_gas_to_mbus(
|
||||||
|
hass: HomeAssistant, entity_registry: er.EntityRegistry, dsmr_connection_fixture
|
||||||
|
) -> None:
|
||||||
|
"""Test migration of unique_id."""
|
||||||
|
(connection_factory, transport, protocol) = dsmr_connection_fixture
|
||||||
|
|
||||||
|
from dsmr_parser.obis_references import (
|
||||||
|
BELGIUM_MBUS1_DEVICE_TYPE,
|
||||||
|
BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER,
|
||||||
|
BELGIUM_MBUS1_METER_READING2,
|
||||||
|
)
|
||||||
|
from dsmr_parser.objects import CosemObject, MBusObject
|
||||||
|
|
||||||
|
mock_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
unique_id="/dev/ttyUSB0",
|
||||||
|
data={
|
||||||
|
"port": "/dev/ttyUSB0",
|
||||||
|
"dsmr_version": "5B",
|
||||||
|
"precision": 4,
|
||||||
|
"reconnect_interval": 30,
|
||||||
|
"serial_id": "1234",
|
||||||
|
"serial_id_gas": "37464C4F32313139303333373331",
|
||||||
|
},
|
||||||
|
options={
|
||||||
|
"time_between_update": 0,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
old_unique_id = "37464C4F32313139303333373331_belgium_5min_gas_meter_reading"
|
||||||
|
|
||||||
|
device_registry = hass.helpers.device_registry.async_get(hass)
|
||||||
|
device = device_registry.async_get_or_create(
|
||||||
|
config_entry_id=mock_entry.entry_id,
|
||||||
|
identifiers={(DOMAIN, mock_entry.entry_id)},
|
||||||
|
name="Gas Meter",
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
entity: er.RegistryEntry = entity_registry.async_get_or_create(
|
||||||
|
suggested_object_id="gas_meter_reading",
|
||||||
|
disabled_by=None,
|
||||||
|
domain=SENSOR_DOMAIN,
|
||||||
|
platform=DOMAIN,
|
||||||
|
device_id=device.id,
|
||||||
|
unique_id=old_unique_id,
|
||||||
|
config_entry=mock_entry,
|
||||||
|
)
|
||||||
|
assert entity.unique_id == old_unique_id
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
telegram = {
|
||||||
|
BELGIUM_MBUS1_DEVICE_TYPE: CosemObject(
|
||||||
|
BELGIUM_MBUS1_DEVICE_TYPE, [{"value": "003", "unit": ""}]
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER: CosemObject(
|
||||||
|
BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER,
|
||||||
|
[{"value": "37464C4F32313139303333373331", "unit": ""}],
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS1_METER_READING2: MBusObject(
|
||||||
|
BELGIUM_MBUS1_METER_READING2,
|
||||||
|
[
|
||||||
|
{"value": datetime.datetime.fromtimestamp(1551642213)},
|
||||||
|
{"value": Decimal(745.695), "unit": "m3"},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
assert await hass.config_entries.async_setup(mock_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
telegram_callback = connection_factory.call_args_list[0][0][2]
|
||||||
|
|
||||||
|
# simulate a telegram pushed from the smartmeter and parsed by dsmr_parser
|
||||||
|
telegram_callback(telegram)
|
||||||
|
|
||||||
|
# after receiving telegram entities need to have the chance to be created
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
dev_entities = er.async_entries_for_device(
|
||||||
|
entity_registry, device.id, include_disabled_entities=True
|
||||||
|
)
|
||||||
|
assert not dev_entities
|
||||||
|
|
||||||
|
assert (
|
||||||
|
entity_registry.async_get_entity_id(SENSOR_DOMAIN, DOMAIN, old_unique_id)
|
||||||
|
is None
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
entity_registry.async_get_entity_id(
|
||||||
|
SENSOR_DOMAIN, DOMAIN, "37464C4F32313139303333373331"
|
||||||
|
)
|
||||||
|
== "sensor.gas_meter_reading"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_migrate_gas_to_mbus_exists(
|
||||||
|
hass: HomeAssistant, entity_registry: er.EntityRegistry, dsmr_connection_fixture
|
||||||
|
) -> None:
|
||||||
|
"""Test migration of unique_id."""
|
||||||
|
(connection_factory, transport, protocol) = dsmr_connection_fixture
|
||||||
|
|
||||||
|
from dsmr_parser.obis_references import (
|
||||||
|
BELGIUM_MBUS1_DEVICE_TYPE,
|
||||||
|
BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER,
|
||||||
|
BELGIUM_MBUS1_METER_READING2,
|
||||||
|
)
|
||||||
|
from dsmr_parser.objects import CosemObject, MBusObject
|
||||||
|
|
||||||
|
mock_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
unique_id="/dev/ttyUSB0",
|
||||||
|
data={
|
||||||
|
"port": "/dev/ttyUSB0",
|
||||||
|
"dsmr_version": "5B",
|
||||||
|
"precision": 4,
|
||||||
|
"reconnect_interval": 30,
|
||||||
|
"serial_id": "1234",
|
||||||
|
"serial_id_gas": "37464C4F32313139303333373331",
|
||||||
|
},
|
||||||
|
options={
|
||||||
|
"time_between_update": 0,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
old_unique_id = "37464C4F32313139303333373331_belgium_5min_gas_meter_reading"
|
||||||
|
|
||||||
|
device_registry = hass.helpers.device_registry.async_get(hass)
|
||||||
|
device = device_registry.async_get_or_create(
|
||||||
|
config_entry_id=mock_entry.entry_id,
|
||||||
|
identifiers={(DOMAIN, mock_entry.entry_id)},
|
||||||
|
name="Gas Meter",
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
entity: er.RegistryEntry = entity_registry.async_get_or_create(
|
||||||
|
suggested_object_id="gas_meter_reading",
|
||||||
|
disabled_by=None,
|
||||||
|
domain=SENSOR_DOMAIN,
|
||||||
|
platform=DOMAIN,
|
||||||
|
device_id=device.id,
|
||||||
|
unique_id=old_unique_id,
|
||||||
|
config_entry=mock_entry,
|
||||||
|
)
|
||||||
|
assert entity.unique_id == old_unique_id
|
||||||
|
|
||||||
|
device2 = device_registry.async_get_or_create(
|
||||||
|
config_entry_id=mock_entry.entry_id,
|
||||||
|
identifiers={(DOMAIN, "37464C4F32313139303333373331")},
|
||||||
|
name="Gas Meter",
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
entity_registry.async_get_or_create(
|
||||||
|
suggested_object_id="gas_meter_reading_alt",
|
||||||
|
disabled_by=None,
|
||||||
|
domain=SENSOR_DOMAIN,
|
||||||
|
platform=DOMAIN,
|
||||||
|
device_id=device2.id,
|
||||||
|
unique_id="37464C4F32313139303333373331",
|
||||||
|
config_entry=mock_entry,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
telegram = {
|
||||||
|
BELGIUM_MBUS1_DEVICE_TYPE: CosemObject(
|
||||||
|
BELGIUM_MBUS1_DEVICE_TYPE, [{"value": "003", "unit": ""}]
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER: CosemObject(
|
||||||
|
BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER,
|
||||||
|
[{"value": "37464C4F32313139303333373331", "unit": ""}],
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS1_METER_READING2: MBusObject(
|
||||||
|
BELGIUM_MBUS1_METER_READING2,
|
||||||
|
[
|
||||||
|
{"value": datetime.datetime.fromtimestamp(1551642213)},
|
||||||
|
{"value": Decimal(745.695), "unit": "m3"},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
assert await hass.config_entries.async_setup(mock_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
telegram_callback = connection_factory.call_args_list[0][0][2]
|
||||||
|
|
||||||
|
# simulate a telegram pushed from the smartmeter and parsed by dsmr_parser
|
||||||
|
telegram_callback(telegram)
|
||||||
|
|
||||||
|
# after receiving telegram entities need to have the chance to be created
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert (
|
||||||
|
entity_registry.async_get_entity_id(SENSOR_DOMAIN, DOMAIN, old_unique_id)
|
||||||
|
== "sensor.gas_meter_reading"
|
||||||
|
)
|
@ -8,21 +8,8 @@ import asyncio
|
|||||||
import datetime
|
import datetime
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from itertools import chain, repeat
|
from itertools import chain, repeat
|
||||||
from typing import Literal
|
|
||||||
from unittest.mock import DEFAULT, MagicMock
|
from unittest.mock import DEFAULT, MagicMock
|
||||||
|
|
||||||
from dsmr_parser.obis_references import (
|
|
||||||
BELGIUM_MBUS1_METER_READING1,
|
|
||||||
BELGIUM_MBUS1_METER_READING2,
|
|
||||||
BELGIUM_MBUS2_METER_READING1,
|
|
||||||
BELGIUM_MBUS2_METER_READING2,
|
|
||||||
BELGIUM_MBUS3_METER_READING1,
|
|
||||||
BELGIUM_MBUS3_METER_READING2,
|
|
||||||
BELGIUM_MBUS4_METER_READING1,
|
|
||||||
BELGIUM_MBUS4_METER_READING2,
|
|
||||||
)
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
ATTR_OPTIONS,
|
ATTR_OPTIONS,
|
||||||
@ -145,8 +132,8 @@ async def test_default_setup(
|
|||||||
# simulate a telegram pushed from the smartmeter and parsed by dsmr_parser
|
# simulate a telegram pushed from the smartmeter and parsed by dsmr_parser
|
||||||
telegram_callback(telegram)
|
telegram_callback(telegram)
|
||||||
|
|
||||||
# after receiving telegram entities need to have the chance to update
|
# after receiving telegram entities need to have the chance to be created
|
||||||
await asyncio.sleep(0)
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
# ensure entities have new state value after incoming telegram
|
# ensure entities have new state value after incoming telegram
|
||||||
power_consumption = hass.states.get("sensor.electricity_meter_power_consumption")
|
power_consumption = hass.states.get("sensor.electricity_meter_power_consumption")
|
||||||
@ -495,10 +482,18 @@ async def test_belgian_meter(hass: HomeAssistant, dsmr_connection_fixture) -> No
|
|||||||
from dsmr_parser.obis_references import (
|
from dsmr_parser.obis_references import (
|
||||||
BELGIUM_CURRENT_AVERAGE_DEMAND,
|
BELGIUM_CURRENT_AVERAGE_DEMAND,
|
||||||
BELGIUM_MAXIMUM_DEMAND_MONTH,
|
BELGIUM_MAXIMUM_DEMAND_MONTH,
|
||||||
|
BELGIUM_MBUS1_DEVICE_TYPE,
|
||||||
|
BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER,
|
||||||
BELGIUM_MBUS1_METER_READING2,
|
BELGIUM_MBUS1_METER_READING2,
|
||||||
BELGIUM_MBUS2_METER_READING2,
|
BELGIUM_MBUS2_DEVICE_TYPE,
|
||||||
|
BELGIUM_MBUS2_EQUIPMENT_IDENTIFIER,
|
||||||
|
BELGIUM_MBUS2_METER_READING1,
|
||||||
|
BELGIUM_MBUS3_DEVICE_TYPE,
|
||||||
|
BELGIUM_MBUS3_EQUIPMENT_IDENTIFIER,
|
||||||
BELGIUM_MBUS3_METER_READING2,
|
BELGIUM_MBUS3_METER_READING2,
|
||||||
BELGIUM_MBUS4_METER_READING2,
|
BELGIUM_MBUS4_DEVICE_TYPE,
|
||||||
|
BELGIUM_MBUS4_EQUIPMENT_IDENTIFIER,
|
||||||
|
BELGIUM_MBUS4_METER_READING1,
|
||||||
ELECTRICITY_ACTIVE_TARIFF,
|
ELECTRICITY_ACTIVE_TARIFF,
|
||||||
)
|
)
|
||||||
from dsmr_parser.objects import CosemObject, MBusObject
|
from dsmr_parser.objects import CosemObject, MBusObject
|
||||||
@ -509,41 +504,13 @@ async def test_belgian_meter(hass: HomeAssistant, dsmr_connection_fixture) -> No
|
|||||||
"precision": 4,
|
"precision": 4,
|
||||||
"reconnect_interval": 30,
|
"reconnect_interval": 30,
|
||||||
"serial_id": "1234",
|
"serial_id": "1234",
|
||||||
"serial_id_gas": "5678",
|
"serial_id_gas": None,
|
||||||
}
|
}
|
||||||
entry_options = {
|
entry_options = {
|
||||||
"time_between_update": 0,
|
"time_between_update": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
telegram = {
|
telegram = {
|
||||||
BELGIUM_MBUS1_METER_READING2: MBusObject(
|
|
||||||
BELGIUM_MBUS1_METER_READING2,
|
|
||||||
[
|
|
||||||
{"value": datetime.datetime.fromtimestamp(1551642213)},
|
|
||||||
{"value": Decimal(745.695), "unit": "m3"},
|
|
||||||
],
|
|
||||||
),
|
|
||||||
BELGIUM_MBUS2_METER_READING2: MBusObject(
|
|
||||||
BELGIUM_MBUS2_METER_READING2,
|
|
||||||
[
|
|
||||||
{"value": datetime.datetime.fromtimestamp(1551642214)},
|
|
||||||
{"value": Decimal(745.696), "unit": "m3"},
|
|
||||||
],
|
|
||||||
),
|
|
||||||
BELGIUM_MBUS3_METER_READING2: MBusObject(
|
|
||||||
BELGIUM_MBUS3_METER_READING2,
|
|
||||||
[
|
|
||||||
{"value": datetime.datetime.fromtimestamp(1551642215)},
|
|
||||||
{"value": Decimal(745.697), "unit": "m3"},
|
|
||||||
],
|
|
||||||
),
|
|
||||||
BELGIUM_MBUS4_METER_READING2: MBusObject(
|
|
||||||
BELGIUM_MBUS4_METER_READING2,
|
|
||||||
[
|
|
||||||
{"value": datetime.datetime.fromtimestamp(1551642216)},
|
|
||||||
{"value": Decimal(745.698), "unit": "m3"},
|
|
||||||
],
|
|
||||||
),
|
|
||||||
BELGIUM_CURRENT_AVERAGE_DEMAND: CosemObject(
|
BELGIUM_CURRENT_AVERAGE_DEMAND: CosemObject(
|
||||||
BELGIUM_CURRENT_AVERAGE_DEMAND,
|
BELGIUM_CURRENT_AVERAGE_DEMAND,
|
||||||
[{"value": Decimal(1.75), "unit": "kW"}],
|
[{"value": Decimal(1.75), "unit": "kW"}],
|
||||||
@ -555,6 +522,62 @@ async def test_belgian_meter(hass: HomeAssistant, dsmr_connection_fixture) -> No
|
|||||||
{"value": Decimal(4.11), "unit": "kW"},
|
{"value": Decimal(4.11), "unit": "kW"},
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
BELGIUM_MBUS1_DEVICE_TYPE: CosemObject(
|
||||||
|
BELGIUM_MBUS1_DEVICE_TYPE, [{"value": "003", "unit": ""}]
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER: CosemObject(
|
||||||
|
BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER,
|
||||||
|
[{"value": "37464C4F32313139303333373331", "unit": ""}],
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS1_METER_READING2: MBusObject(
|
||||||
|
BELGIUM_MBUS1_METER_READING2,
|
||||||
|
[
|
||||||
|
{"value": datetime.datetime.fromtimestamp(1551642213)},
|
||||||
|
{"value": Decimal(745.695), "unit": "m3"},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS2_DEVICE_TYPE: CosemObject(
|
||||||
|
BELGIUM_MBUS2_DEVICE_TYPE, [{"value": "007", "unit": ""}]
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS2_EQUIPMENT_IDENTIFIER: CosemObject(
|
||||||
|
BELGIUM_MBUS2_EQUIPMENT_IDENTIFIER,
|
||||||
|
[{"value": "37464C4F32313139303333373332", "unit": ""}],
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS2_METER_READING1: MBusObject(
|
||||||
|
BELGIUM_MBUS2_METER_READING1,
|
||||||
|
[
|
||||||
|
{"value": datetime.datetime.fromtimestamp(1551642214)},
|
||||||
|
{"value": Decimal(678.695), "unit": "m3"},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS3_DEVICE_TYPE: CosemObject(
|
||||||
|
BELGIUM_MBUS3_DEVICE_TYPE, [{"value": "003", "unit": ""}]
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS3_EQUIPMENT_IDENTIFIER: CosemObject(
|
||||||
|
BELGIUM_MBUS3_EQUIPMENT_IDENTIFIER,
|
||||||
|
[{"value": "37464C4F32313139303333373333", "unit": ""}],
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS3_METER_READING2: MBusObject(
|
||||||
|
BELGIUM_MBUS3_METER_READING2,
|
||||||
|
[
|
||||||
|
{"value": datetime.datetime.fromtimestamp(1551642215)},
|
||||||
|
{"value": Decimal(12.12), "unit": "m3"},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS4_DEVICE_TYPE: CosemObject(
|
||||||
|
BELGIUM_MBUS4_DEVICE_TYPE, [{"value": "007", "unit": ""}]
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS4_EQUIPMENT_IDENTIFIER: CosemObject(
|
||||||
|
BELGIUM_MBUS4_EQUIPMENT_IDENTIFIER,
|
||||||
|
[{"value": "37464C4F32313139303333373334", "unit": ""}],
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS4_METER_READING1: MBusObject(
|
||||||
|
BELGIUM_MBUS4_METER_READING1,
|
||||||
|
[
|
||||||
|
{"value": datetime.datetime.fromtimestamp(1551642216)},
|
||||||
|
{"value": Decimal(13.13), "unit": "m3"},
|
||||||
|
],
|
||||||
|
),
|
||||||
ELECTRICITY_ACTIVE_TARIFF: CosemObject(
|
ELECTRICITY_ACTIVE_TARIFF: CosemObject(
|
||||||
ELECTRICITY_ACTIVE_TARIFF, [{"value": "0001", "unit": ""}]
|
ELECTRICITY_ACTIVE_TARIFF, [{"value": "0001", "unit": ""}]
|
||||||
),
|
),
|
||||||
@ -600,7 +623,7 @@ async def test_belgian_meter(hass: HomeAssistant, dsmr_connection_fixture) -> No
|
|||||||
assert max_demand.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfPower.KILO_WATT
|
assert max_demand.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfPower.KILO_WATT
|
||||||
assert max_demand.attributes.get(ATTR_STATE_CLASS) is None
|
assert max_demand.attributes.get(ATTR_STATE_CLASS) is None
|
||||||
|
|
||||||
# check if gas consumption is parsed correctly
|
# check if gas consumption mbus1 is parsed correctly
|
||||||
gas_consumption = hass.states.get("sensor.gas_meter_gas_consumption")
|
gas_consumption = hass.states.get("sensor.gas_meter_gas_consumption")
|
||||||
assert gas_consumption.state == "745.695"
|
assert gas_consumption.state == "745.695"
|
||||||
assert gas_consumption.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.GAS
|
assert gas_consumption.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.GAS
|
||||||
@ -613,48 +636,69 @@ async def test_belgian_meter(hass: HomeAssistant, dsmr_connection_fixture) -> No
|
|||||||
== UnitOfVolume.CUBIC_METERS
|
== UnitOfVolume.CUBIC_METERS
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# check if water usage mbus2 is parsed correctly
|
||||||
|
water_consumption = hass.states.get("sensor.water_meter_water_consumption")
|
||||||
|
assert water_consumption.state == "678.695"
|
||||||
|
assert (
|
||||||
|
water_consumption.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.WATER
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
water_consumption.attributes.get(ATTR_STATE_CLASS)
|
||||||
|
== SensorStateClass.TOTAL_INCREASING
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
water_consumption.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||||
|
== UnitOfVolume.CUBIC_METERS
|
||||||
|
)
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
# check if gas consumption mbus1 is parsed correctly
|
||||||
("key1", "key2", "key3", "gas_value"),
|
gas_consumption = hass.states.get("sensor.gas_meter_gas_consumption_2")
|
||||||
[
|
assert gas_consumption.state == "12.12"
|
||||||
(
|
assert gas_consumption.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.GAS
|
||||||
BELGIUM_MBUS1_METER_READING1,
|
assert (
|
||||||
BELGIUM_MBUS2_METER_READING2,
|
gas_consumption.attributes.get(ATTR_STATE_CLASS)
|
||||||
BELGIUM_MBUS3_METER_READING1,
|
== SensorStateClass.TOTAL_INCREASING
|
||||||
"745.696",
|
)
|
||||||
),
|
assert (
|
||||||
(
|
gas_consumption.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||||
BELGIUM_MBUS1_METER_READING2,
|
== UnitOfVolume.CUBIC_METERS
|
||||||
BELGIUM_MBUS2_METER_READING1,
|
)
|
||||||
BELGIUM_MBUS3_METER_READING2,
|
|
||||||
"745.695",
|
# check if water usage mbus2 is parsed correctly
|
||||||
),
|
water_consumption = hass.states.get("sensor.water_meter_water_consumption_2")
|
||||||
(
|
assert water_consumption.state == "13.13"
|
||||||
BELGIUM_MBUS4_METER_READING2,
|
assert (
|
||||||
BELGIUM_MBUS2_METER_READING1,
|
water_consumption.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.WATER
|
||||||
BELGIUM_MBUS3_METER_READING1,
|
)
|
||||||
"745.695",
|
assert (
|
||||||
),
|
water_consumption.attributes.get(ATTR_STATE_CLASS)
|
||||||
(
|
== SensorStateClass.TOTAL_INCREASING
|
||||||
BELGIUM_MBUS4_METER_READING1,
|
)
|
||||||
BELGIUM_MBUS2_METER_READING1,
|
assert (
|
||||||
BELGIUM_MBUS3_METER_READING2,
|
water_consumption.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||||
"745.697",
|
== UnitOfVolume.CUBIC_METERS
|
||||||
),
|
)
|
||||||
],
|
|
||||||
)
|
|
||||||
async def test_belgian_meter_alt(
|
async def test_belgian_meter_alt(hass: HomeAssistant, dsmr_connection_fixture) -> None:
|
||||||
hass: HomeAssistant,
|
|
||||||
dsmr_connection_fixture,
|
|
||||||
key1: Literal,
|
|
||||||
key2: Literal,
|
|
||||||
key3: Literal,
|
|
||||||
gas_value: str,
|
|
||||||
) -> None:
|
|
||||||
"""Test if Belgian meter is correctly parsed."""
|
"""Test if Belgian meter is correctly parsed."""
|
||||||
(connection_factory, transport, protocol) = dsmr_connection_fixture
|
(connection_factory, transport, protocol) = dsmr_connection_fixture
|
||||||
|
|
||||||
from dsmr_parser.objects import MBusObject
|
from dsmr_parser.obis_references import (
|
||||||
|
BELGIUM_MBUS1_DEVICE_TYPE,
|
||||||
|
BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER,
|
||||||
|
BELGIUM_MBUS1_METER_READING1,
|
||||||
|
BELGIUM_MBUS2_DEVICE_TYPE,
|
||||||
|
BELGIUM_MBUS2_EQUIPMENT_IDENTIFIER,
|
||||||
|
BELGIUM_MBUS2_METER_READING2,
|
||||||
|
BELGIUM_MBUS3_DEVICE_TYPE,
|
||||||
|
BELGIUM_MBUS3_EQUIPMENT_IDENTIFIER,
|
||||||
|
BELGIUM_MBUS3_METER_READING1,
|
||||||
|
BELGIUM_MBUS4_DEVICE_TYPE,
|
||||||
|
BELGIUM_MBUS4_EQUIPMENT_IDENTIFIER,
|
||||||
|
BELGIUM_MBUS4_METER_READING2,
|
||||||
|
)
|
||||||
|
from dsmr_parser.objects import CosemObject, MBusObject
|
||||||
|
|
||||||
entry_data = {
|
entry_data = {
|
||||||
"port": "/dev/ttyUSB0",
|
"port": "/dev/ttyUSB0",
|
||||||
@ -662,32 +706,67 @@ async def test_belgian_meter_alt(
|
|||||||
"precision": 4,
|
"precision": 4,
|
||||||
"reconnect_interval": 30,
|
"reconnect_interval": 30,
|
||||||
"serial_id": "1234",
|
"serial_id": "1234",
|
||||||
"serial_id_gas": "5678",
|
"serial_id_gas": None,
|
||||||
}
|
}
|
||||||
entry_options = {
|
entry_options = {
|
||||||
"time_between_update": 0,
|
"time_between_update": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
telegram = {
|
telegram = {
|
||||||
key1: MBusObject(
|
BELGIUM_MBUS1_DEVICE_TYPE: CosemObject(
|
||||||
key1,
|
BELGIUM_MBUS1_DEVICE_TYPE, [{"value": "007", "unit": ""}]
|
||||||
[
|
|
||||||
{"value": datetime.datetime.fromtimestamp(1551642213)},
|
|
||||||
{"value": Decimal(745.695), "unit": "m3"},
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
key2: MBusObject(
|
BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER: CosemObject(
|
||||||
key2,
|
BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER,
|
||||||
[
|
[{"value": "37464C4F32313139303333373331", "unit": ""}],
|
||||||
{"value": datetime.datetime.fromtimestamp(1551642214)},
|
|
||||||
{"value": Decimal(745.696), "unit": "m3"},
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
key3: MBusObject(
|
BELGIUM_MBUS1_METER_READING1: MBusObject(
|
||||||
key3,
|
BELGIUM_MBUS1_METER_READING1,
|
||||||
[
|
[
|
||||||
{"value": datetime.datetime.fromtimestamp(1551642215)},
|
{"value": datetime.datetime.fromtimestamp(1551642215)},
|
||||||
{"value": Decimal(745.697), "unit": "m3"},
|
{"value": Decimal(123.456), "unit": "m3"},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS2_DEVICE_TYPE: CosemObject(
|
||||||
|
BELGIUM_MBUS2_DEVICE_TYPE, [{"value": "003", "unit": ""}]
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS2_EQUIPMENT_IDENTIFIER: CosemObject(
|
||||||
|
BELGIUM_MBUS2_EQUIPMENT_IDENTIFIER,
|
||||||
|
[{"value": "37464C4F32313139303333373332", "unit": ""}],
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS2_METER_READING2: MBusObject(
|
||||||
|
BELGIUM_MBUS2_METER_READING2,
|
||||||
|
[
|
||||||
|
{"value": datetime.datetime.fromtimestamp(1551642216)},
|
||||||
|
{"value": Decimal(678.901), "unit": "m3"},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS3_DEVICE_TYPE: CosemObject(
|
||||||
|
BELGIUM_MBUS3_DEVICE_TYPE, [{"value": "007", "unit": ""}]
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS3_EQUIPMENT_IDENTIFIER: CosemObject(
|
||||||
|
BELGIUM_MBUS3_EQUIPMENT_IDENTIFIER,
|
||||||
|
[{"value": "37464C4F32313139303333373333", "unit": ""}],
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS3_METER_READING1: MBusObject(
|
||||||
|
BELGIUM_MBUS3_METER_READING1,
|
||||||
|
[
|
||||||
|
{"value": datetime.datetime.fromtimestamp(1551642217)},
|
||||||
|
{"value": Decimal(12.12), "unit": "m3"},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS4_DEVICE_TYPE: CosemObject(
|
||||||
|
BELGIUM_MBUS4_DEVICE_TYPE, [{"value": "003", "unit": ""}]
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS4_EQUIPMENT_IDENTIFIER: CosemObject(
|
||||||
|
BELGIUM_MBUS4_EQUIPMENT_IDENTIFIER,
|
||||||
|
[{"value": "37464C4F32313139303333373334", "unit": ""}],
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS4_METER_READING2: MBusObject(
|
||||||
|
BELGIUM_MBUS4_METER_READING2,
|
||||||
|
[
|
||||||
|
{"value": datetime.datetime.fromtimestamp(1551642218)},
|
||||||
|
{"value": Decimal(13.13), "unit": "m3"},
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@ -709,9 +788,24 @@ async def test_belgian_meter_alt(
|
|||||||
# after receiving telegram entities need to have the chance to be created
|
# after receiving telegram entities need to have the chance to be created
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
# check if gas consumption is parsed correctly
|
# check if water usage mbus1 is parsed correctly
|
||||||
|
water_consumption = hass.states.get("sensor.water_meter_water_consumption")
|
||||||
|
assert water_consumption.state == "123.456"
|
||||||
|
assert (
|
||||||
|
water_consumption.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.WATER
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
water_consumption.attributes.get(ATTR_STATE_CLASS)
|
||||||
|
== SensorStateClass.TOTAL_INCREASING
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
water_consumption.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||||
|
== UnitOfVolume.CUBIC_METERS
|
||||||
|
)
|
||||||
|
|
||||||
|
# check if gas consumption mbus2 is parsed correctly
|
||||||
gas_consumption = hass.states.get("sensor.gas_meter_gas_consumption")
|
gas_consumption = hass.states.get("sensor.gas_meter_gas_consumption")
|
||||||
assert gas_consumption.state == gas_value
|
assert gas_consumption.state == "678.901"
|
||||||
assert gas_consumption.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.GAS
|
assert gas_consumption.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.GAS
|
||||||
assert (
|
assert (
|
||||||
gas_consumption.attributes.get(ATTR_STATE_CLASS)
|
gas_consumption.attributes.get(ATTR_STATE_CLASS)
|
||||||
@ -722,6 +816,157 @@ async def test_belgian_meter_alt(
|
|||||||
== UnitOfVolume.CUBIC_METERS
|
== UnitOfVolume.CUBIC_METERS
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# check if water usage mbus3 is parsed correctly
|
||||||
|
water_consumption = hass.states.get("sensor.water_meter_water_consumption_2")
|
||||||
|
assert water_consumption.state == "12.12"
|
||||||
|
assert (
|
||||||
|
water_consumption.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.WATER
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
water_consumption.attributes.get(ATTR_STATE_CLASS)
|
||||||
|
== SensorStateClass.TOTAL_INCREASING
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
water_consumption.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||||
|
== UnitOfVolume.CUBIC_METERS
|
||||||
|
)
|
||||||
|
|
||||||
|
# check if gas consumption mbus4 is parsed correctly
|
||||||
|
gas_consumption = hass.states.get("sensor.gas_meter_gas_consumption_2")
|
||||||
|
assert gas_consumption.state == "13.13"
|
||||||
|
assert gas_consumption.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.GAS
|
||||||
|
assert (
|
||||||
|
gas_consumption.attributes.get(ATTR_STATE_CLASS)
|
||||||
|
== SensorStateClass.TOTAL_INCREASING
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
gas_consumption.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||||
|
== UnitOfVolume.CUBIC_METERS
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_belgian_meter_mbus(hass: HomeAssistant, dsmr_connection_fixture) -> None:
|
||||||
|
"""Test if Belgian meter is correctly parsed."""
|
||||||
|
(connection_factory, transport, protocol) = dsmr_connection_fixture
|
||||||
|
|
||||||
|
from dsmr_parser.obis_references import (
|
||||||
|
BELGIUM_MBUS1_DEVICE_TYPE,
|
||||||
|
BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER,
|
||||||
|
BELGIUM_MBUS2_DEVICE_TYPE,
|
||||||
|
BELGIUM_MBUS2_EQUIPMENT_IDENTIFIER,
|
||||||
|
BELGIUM_MBUS3_DEVICE_TYPE,
|
||||||
|
BELGIUM_MBUS3_EQUIPMENT_IDENTIFIER,
|
||||||
|
BELGIUM_MBUS3_METER_READING2,
|
||||||
|
BELGIUM_MBUS4_DEVICE_TYPE,
|
||||||
|
BELGIUM_MBUS4_METER_READING1,
|
||||||
|
ELECTRICITY_ACTIVE_TARIFF,
|
||||||
|
)
|
||||||
|
from dsmr_parser.objects import CosemObject, MBusObject
|
||||||
|
|
||||||
|
entry_data = {
|
||||||
|
"port": "/dev/ttyUSB0",
|
||||||
|
"dsmr_version": "5B",
|
||||||
|
"precision": 4,
|
||||||
|
"reconnect_interval": 30,
|
||||||
|
"serial_id": "1234",
|
||||||
|
"serial_id_gas": None,
|
||||||
|
}
|
||||||
|
entry_options = {
|
||||||
|
"time_between_update": 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
telegram = {
|
||||||
|
ELECTRICITY_ACTIVE_TARIFF: CosemObject(
|
||||||
|
ELECTRICITY_ACTIVE_TARIFF, [{"value": "0003", "unit": ""}]
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS1_DEVICE_TYPE: CosemObject(
|
||||||
|
BELGIUM_MBUS1_DEVICE_TYPE, [{"value": "006", "unit": ""}]
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER: CosemObject(
|
||||||
|
BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER,
|
||||||
|
[{"value": "37464C4F32313139303333373331", "unit": ""}],
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS2_DEVICE_TYPE: CosemObject(
|
||||||
|
BELGIUM_MBUS2_DEVICE_TYPE, [{"value": "003", "unit": ""}]
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS2_EQUIPMENT_IDENTIFIER: CosemObject(
|
||||||
|
BELGIUM_MBUS2_EQUIPMENT_IDENTIFIER,
|
||||||
|
[{"value": "37464C4F32313139303333373332", "unit": ""}],
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS3_DEVICE_TYPE: CosemObject(
|
||||||
|
BELGIUM_MBUS3_DEVICE_TYPE, [{"value": "007", "unit": ""}]
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS3_EQUIPMENT_IDENTIFIER: CosemObject(
|
||||||
|
BELGIUM_MBUS3_EQUIPMENT_IDENTIFIER,
|
||||||
|
[{"value": "37464C4F32313139303333373333", "unit": ""}],
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS3_METER_READING2: MBusObject(
|
||||||
|
BELGIUM_MBUS3_METER_READING2,
|
||||||
|
[
|
||||||
|
{"value": datetime.datetime.fromtimestamp(1551642217)},
|
||||||
|
{"value": Decimal(12.12), "unit": "m3"},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS4_DEVICE_TYPE: CosemObject(
|
||||||
|
BELGIUM_MBUS4_DEVICE_TYPE, [{"value": "007", "unit": ""}]
|
||||||
|
),
|
||||||
|
BELGIUM_MBUS4_METER_READING1: MBusObject(
|
||||||
|
BELGIUM_MBUS4_METER_READING1,
|
||||||
|
[
|
||||||
|
{"value": datetime.datetime.fromtimestamp(1551642218)},
|
||||||
|
{"value": Decimal(13.13), "unit": "m3"},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
mock_entry = MockConfigEntry(
|
||||||
|
domain="dsmr", unique_id="/dev/ttyUSB0", data=entry_data, options=entry_options
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
await hass.config_entries.async_setup(mock_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
telegram_callback = connection_factory.call_args_list[0][0][2]
|
||||||
|
|
||||||
|
# simulate a telegram pushed from the smartmeter and parsed by dsmr_parser
|
||||||
|
telegram_callback(telegram)
|
||||||
|
|
||||||
|
# after receiving telegram entities need to have the chance to be created
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
# tariff should be translated in human readable and have no unit
|
||||||
|
active_tariff = hass.states.get("sensor.electricity_meter_active_tariff")
|
||||||
|
assert active_tariff.state == "unknown"
|
||||||
|
|
||||||
|
# check if gas consumption mbus2 is parsed correctly
|
||||||
|
gas_consumption = hass.states.get("sensor.gas_meter_gas_consumption")
|
||||||
|
assert gas_consumption is None
|
||||||
|
|
||||||
|
# check if water usage mbus3 is parsed correctly
|
||||||
|
water_consumption = hass.states.get("sensor.water_meter_water_consumption_2")
|
||||||
|
assert water_consumption is None
|
||||||
|
|
||||||
|
# check if gas consumption mbus4 is parsed correctly
|
||||||
|
gas_consumption = hass.states.get("sensor.gas_meter_gas_consumption_2")
|
||||||
|
assert gas_consumption is None
|
||||||
|
|
||||||
|
# check if gas consumption mbus4 is parsed correctly
|
||||||
|
water_consumption = hass.states.get("sensor.water_meter_water_consumption")
|
||||||
|
assert water_consumption.state == "13.13"
|
||||||
|
assert (
|
||||||
|
water_consumption.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.WATER
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
water_consumption.attributes.get(ATTR_STATE_CLASS)
|
||||||
|
== SensorStateClass.TOTAL_INCREASING
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
water_consumption.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||||
|
== UnitOfVolume.CUBIC_METERS
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_belgian_meter_low(hass: HomeAssistant, dsmr_connection_fixture) -> None:
|
async def test_belgian_meter_low(hass: HomeAssistant, dsmr_connection_fixture) -> None:
|
||||||
"""Test if Belgian meter is correctly parsed."""
|
"""Test if Belgian meter is correctly parsed."""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user