Fix support for Heat meters to DSMR integration (#125523)
* Fix support for Heat meters to DSMR integration * Fixed test
This commit is contained in:
parent
2a1df2063d
commit
17ab45da43
3 changed files with 94 additions and 1 deletions
|
@ -26,6 +26,7 @@ DEFAULT_TIME_BETWEEN_UPDATE = 30
|
||||||
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"
|
DEVICE_NAME_WATER = "Water Meter"
|
||||||
|
DEVICE_NAME_HEAT = "Heat Meter"
|
||||||
|
|
||||||
DSMR_VERSIONS = {"2.2", "4", "5", "5B", "5L", "5S", "Q3D"}
|
DSMR_VERSIONS = {"2.2", "4", "5", "5B", "5L", "5S", "Q3D"}
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,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_HEAT,
|
||||||
DEVICE_NAME_WATER,
|
DEVICE_NAME_WATER,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
DSMR_PROTOCOL,
|
DSMR_PROTOCOL,
|
||||||
|
@ -75,6 +76,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
|
is_water: bool = False
|
||||||
|
is_heat: bool = False
|
||||||
obis_reference: str
|
obis_reference: str
|
||||||
|
|
||||||
|
|
||||||
|
@ -82,6 +84,7 @@ class MbusDeviceType(IntEnum):
|
||||||
"""Types of mbus devices (13757-3:2013)."""
|
"""Types of mbus devices (13757-3:2013)."""
|
||||||
|
|
||||||
GAS = 3
|
GAS = 3
|
||||||
|
HEAT = 4
|
||||||
WATER = 7
|
WATER = 7
|
||||||
|
|
||||||
|
|
||||||
|
@ -396,6 +399,16 @@ SENSORS_MBUS_DEVICE_TYPE: dict[int, tuple[DSMRSensorEntityDescription, ...]] = {
|
||||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
MbusDeviceType.HEAT: (
|
||||||
|
DSMRSensorEntityDescription(
|
||||||
|
key="heat_reading",
|
||||||
|
translation_key="heat_meter_reading",
|
||||||
|
obis_reference="MBUS_METER_READING",
|
||||||
|
is_heat=True,
|
||||||
|
device_class=SensorDeviceClass.ENERGY,
|
||||||
|
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||||
|
),
|
||||||
|
),
|
||||||
MbusDeviceType.WATER: (
|
MbusDeviceType.WATER: (
|
||||||
DSMRSensorEntityDescription(
|
DSMRSensorEntityDescription(
|
||||||
key="water_reading",
|
key="water_reading",
|
||||||
|
@ -490,6 +503,10 @@ def create_mbus_entities(
|
||||||
continue
|
continue
|
||||||
type_ = int(device_type.value)
|
type_ = int(device_type.value)
|
||||||
|
|
||||||
|
if type_ not in SENSORS_MBUS_DEVICE_TYPE:
|
||||||
|
LOGGER.warning("Unsupported MBUS_DEVICE_TYPE (%d)", type_)
|
||||||
|
continue
|
||||||
|
|
||||||
if identifier := getattr(device, "MBUS_EQUIPMENT_IDENTIFIER", None):
|
if identifier := getattr(device, "MBUS_EQUIPMENT_IDENTIFIER", None):
|
||||||
serial_ = identifier.value
|
serial_ = identifier.value
|
||||||
rename_old_gas_to_mbus(hass, entry, serial_)
|
rename_old_gas_to_mbus(hass, entry, serial_)
|
||||||
|
@ -554,7 +571,10 @@ async def async_setup_entry(
|
||||||
)
|
)
|
||||||
for description in SENSORS
|
for description in SENSORS
|
||||||
if is_supported_description(telegram, description, dsmr_version)
|
if is_supported_description(telegram, description, dsmr_version)
|
||||||
and (not description.is_gas or CONF_SERIAL_ID_GAS in entry.data)
|
and (
|
||||||
|
(not description.is_gas and not description.is_heat)
|
||||||
|
or CONF_SERIAL_ID_GAS in entry.data
|
||||||
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
@ -743,6 +763,10 @@ class DSMREntity(SensorEntity):
|
||||||
if serial_id:
|
if serial_id:
|
||||||
device_serial = serial_id
|
device_serial = serial_id
|
||||||
device_name = DEVICE_NAME_WATER
|
device_name = DEVICE_NAME_WATER
|
||||||
|
if entity_description.is_heat:
|
||||||
|
if serial_id:
|
||||||
|
device_serial = serial_id
|
||||||
|
device_name = DEVICE_NAME_HEAT
|
||||||
if device_serial is None:
|
if device_serial is None:
|
||||||
device_serial = entry.entry_id
|
device_serial = entry.entry_id
|
||||||
|
|
||||||
|
|
|
@ -1521,6 +1521,74 @@ async def test_gas_meter_providing_energy_reading(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_heat_meter_mbus(
|
||||||
|
hass: HomeAssistant, dsmr_connection_fixture: tuple[MagicMock, MagicMock, MagicMock]
|
||||||
|
) -> None:
|
||||||
|
"""Test if heat meter reading is correctly parsed."""
|
||||||
|
(connection_factory, transport, protocol) = dsmr_connection_fixture
|
||||||
|
|
||||||
|
entry_data = {
|
||||||
|
"port": "/dev/ttyUSB0",
|
||||||
|
"dsmr_version": "5",
|
||||||
|
"serial_id": "1234",
|
||||||
|
"serial_id_gas": None,
|
||||||
|
}
|
||||||
|
entry_options = {
|
||||||
|
"time_between_update": 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
telegram = Telegram()
|
||||||
|
telegram.add(
|
||||||
|
MBUS_DEVICE_TYPE,
|
||||||
|
CosemObject((0, 1), [{"value": "004", "unit": ""}]),
|
||||||
|
"MBUS_DEVICE_TYPE",
|
||||||
|
)
|
||||||
|
telegram.add(
|
||||||
|
MBUS_METER_READING,
|
||||||
|
MBusObject(
|
||||||
|
(0, 1),
|
||||||
|
[
|
||||||
|
{"value": datetime.datetime.fromtimestamp(1551642213)},
|
||||||
|
{"value": Decimal(745.695), "unit": "GJ"},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
"MBUS_METER_READING",
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_entry = MockConfigEntry(
|
||||||
|
domain="dsmr", unique_id="/dev/ttyUSB0", data=entry_data, options=entry_options
|
||||||
|
)
|
||||||
|
|
||||||
|
hass.loop.set_debug(True)
|
||||||
|
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()
|
||||||
|
|
||||||
|
# check if gas consumption is parsed correctly
|
||||||
|
heat_consumption = hass.states.get("sensor.heat_meter_energy")
|
||||||
|
assert heat_consumption.state == "745.695"
|
||||||
|
assert (
|
||||||
|
heat_consumption.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.ENERGY
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
heat_consumption.attributes.get("unit_of_measurement")
|
||||||
|
== UnitOfEnergy.GIGA_JOULE
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
heat_consumption.attributes.get(ATTR_STATE_CLASS)
|
||||||
|
== SensorStateClass.TOTAL_INCREASING
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_all_obis_references_exists() -> None:
|
def test_all_obis_references_exists() -> None:
|
||||||
"""Verify that all attributes exist by name in database."""
|
"""Verify that all attributes exist by name in database."""
|
||||||
for sensor in SENSORS:
|
for sensor in SENSORS:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue