Skip to content

Commit

Permalink
Prepare for new dsmr_reader
Browse files Browse the repository at this point in the history
  • Loading branch information
dupondje committed Dec 22, 2022
1 parent cfa08c5 commit 6dbd691
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 21 deletions.
2 changes: 1 addition & 1 deletion homeassistant/components/dsmr/manifest.json
Expand Up @@ -2,7 +2,7 @@
"domain": "dsmr",
"name": "DSMR Slimme Meter",
"documentation": "https://www.home-assistant.io/integrations/dsmr",
"requirements": ["dsmr_parser==0.33"],
"requirements": ["dsmr_parser==1.0.0"],
"codeowners": ["@Robbie1221", "@frenck"],
"config_flow": true,
"iot_class": "local_push",
Expand Down
120 changes: 105 additions & 15 deletions homeassistant/components/dsmr/sensor.py
Expand Up @@ -187,7 +187,7 @@ class DSMRSensorEntityDescription(
key="short_power_failure_count",
name="Short power failure count",
obis_reference=obis_references.SHORT_POWER_FAILURE_COUNT,
dsmr_versions={"2.2", "4", "5", "5B", "5L"},
dsmr_versions={"2.2", "4", "5", "5L"},
entity_registry_enabled_default=False,
icon="mdi:flash-off",
entity_category=EntityCategory.DIAGNOSTIC,
Expand All @@ -196,7 +196,7 @@ class DSMRSensorEntityDescription(
key="long_power_failure_count",
name="Long power failure count",
obis_reference=obis_references.LONG_POWER_FAILURE_COUNT,
dsmr_versions={"2.2", "4", "5", "5B", "5L"},
dsmr_versions={"2.2", "4", "5", "5L"},
entity_registry_enabled_default=False,
icon="mdi:flash-off",
entity_category=EntityCategory.DIAGNOSTIC,
Expand All @@ -205,31 +205,31 @@ class DSMRSensorEntityDescription(
key="voltage_sag_l1_count",
name="Voltage sags phase L1",
obis_reference=obis_references.VOLTAGE_SAG_L1_COUNT,
dsmr_versions={"2.2", "4", "5", "5B", "5L"},
dsmr_versions={"2.2", "4", "5", "5L"},
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
DSMRSensorEntityDescription(
key="voltage_sag_l2_count",
name="Voltage sags phase L2",
obis_reference=obis_references.VOLTAGE_SAG_L2_COUNT,
dsmr_versions={"2.2", "4", "5", "5B", "5L"},
dsmr_versions={"2.2", "4", "5", "5L"},
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
DSMRSensorEntityDescription(
key="voltage_sag_l3_count",
name="Voltage sags phase L3",
obis_reference=obis_references.VOLTAGE_SAG_L3_COUNT,
dsmr_versions={"2.2", "4", "5", "5B", "5L"},
dsmr_versions={"2.2", "4", "5", "5L"},
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
DSMRSensorEntityDescription(
key="voltage_swell_l1_count",
name="Voltage swells phase L1",
obis_reference=obis_references.VOLTAGE_SWELL_L1_COUNT,
dsmr_versions={"2.2", "4", "5", "5B", "5L"},
dsmr_versions={"2.2", "4", "5", "5L"},
entity_registry_enabled_default=False,
icon="mdi:pulse",
entity_category=EntityCategory.DIAGNOSTIC,
Expand All @@ -238,7 +238,7 @@ class DSMRSensorEntityDescription(
key="voltage_swell_l2_count",
name="Voltage swells phase L2",
obis_reference=obis_references.VOLTAGE_SWELL_L2_COUNT,
dsmr_versions={"2.2", "4", "5", "5B", "5L"},
dsmr_versions={"2.2", "4", "5", "5L"},
entity_registry_enabled_default=False,
icon="mdi:pulse",
entity_category=EntityCategory.DIAGNOSTIC,
Expand All @@ -247,7 +247,7 @@ class DSMRSensorEntityDescription(
key="voltage_swell_l3_count",
name="Voltage swells phase L3",
obis_reference=obis_references.VOLTAGE_SWELL_L3_COUNT,
dsmr_versions={"2.2", "4", "5", "5B", "5L"},
dsmr_versions={"2.2", "4", "5", "5L"},
entity_registry_enabled_default=False,
icon="mdi:pulse",
entity_category=EntityCategory.DIAGNOSTIC,
Expand Down Expand Up @@ -355,14 +355,22 @@ class DSMRSensorEntityDescription(
state_class=SensorStateClass.TOTAL_INCREASING,
),
DSMRSensorEntityDescription(
key="belgium_5min_gas_meter_reading",
name="Gas consumption",
obis_reference=obis_references.BELGIUM_5MIN_GAS_METER_READING,
key="belgium_current_average_demand",
name="Current average demand",
obis_reference=obis_references.BELGIUM_CURRENT_AVERAGE_DEMAND,
dsmr_versions={"5B"},
is_gas=True,
force_update=True,
device_class=SensorDeviceClass.GAS,
state_class=SensorStateClass.TOTAL_INCREASING,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.MEASUREMENT,
),
DSMRSensorEntityDescription(
key="belgium_maximum_demand_month",
name="Maximum demand current month",
obis_reference=obis_references.BELGIUM_MAXIMUM_DEMAND_MONTH,
dsmr_versions={"5B"},
force_update=True,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.MEASUREMENT,
),
DSMRSensorEntityDescription(
key="gas_meter_reading",
Expand Down Expand Up @@ -397,9 +405,91 @@ async def async_setup_entry(
seconds=entry.options.get(CONF_TIME_BETWEEN_UPDATE, DEFAULT_TIME_BETWEEN_UPDATE)
)

def create_mbus_entity(
mbus: int, mtype: int, telegram: dict[str, DSMRObject]
) -> DSMRSensorEntityDescription:
if mbus == 1:
if mtype == 3:
obis_reference = obis_references.BELGIUM_MBUS1_METER_READING2
elif mtype == 7:
obis_reference = obis_references.BELGIUM_MBUS1_METER_READING1
elif mbus == 2:
if mtype == 3:
obis_reference = obis_references.BELGIUM_MBUS2_METER_READING2
elif mtype == 7:
obis_reference = obis_references.BELGIUM_MBUS2_METER_READING1
elif mbus == 3:
if mtype == 3:
obis_reference = obis_references.BELGIUM_MBUS3_METER_READING2
elif mtype == 7:
obis_reference = obis_references.BELGIUM_MBUS3_METER_READING1
if mbus == 4:
if mtype == 3:
obis_reference = obis_references.BELGIUM_MBUS4_METER_READING2
elif mtype == 7:
obis_reference = obis_references.BELGIUM_MBUS4_METER_READING1

if mtype == 3:
sensor = DSMRSensorEntityDescription(
key=f"mbus{mbus}_gas_reading",
name=f"Gas consumption (mbus{mbus}",
obis_reference=obis_reference,
is_gas=True,
force_update=True,
device_class=SensorDeviceClass.GAS,
state_class=SensorStateClass.TOTAL_INCREASING,
)
elif mtype == 7:
sensor = DSMRSensorEntityDescription(
key=f"mbus{mbus}_water_reading",
name=f"Water consumption (mbus{mbus}",
obis_reference=obis_reference,
force_update=True,
device_class=SensorDeviceClass.WATER,
state_class=SensorStateClass.TOTAL_INCREASING,
)
return sensor

@Throttle(min_time_between_updates)
def update_entities_telegram(telegram: dict[str, DSMRObject]) -> None:
"""Update entities with latest telegram and trigger state update."""
"""Add and update entities depending on the DSMR data we received."""
if obis_references.BELGIUM_MBUS1_DEVICE_TYPE in telegram:
if (
telegram[obis_references.BELGIUM_MBUS1_DEVICE_TYPE] == 3
or telegram[obis_references.BELGIUM_MBUS1_DEVICE_TYPE] == 7
):
description = create_mbus_entity(
1, telegram[obis_references.BELGIUM_MBUS1_DEVICE_TYPE], telegram
)
async_add_entities([DSMREntity(description, entry)])
if obis_references.BELGIUM_MBUS2_DEVICE_TYPE in telegram:
if (
telegram[obis_references.BELGIUM_MBUS2_DEVICE_TYPE] == 3
or telegram[obis_references.BELGIUM_MBUS2_DEVICE_TYPE] == 7
):
description = create_mbus_entity(
2, telegram[obis_references.BELGIUM_MBUS2_DEVICE_TYPE], telegram
)
async_add_entities([DSMREntity(description, entry)])
if obis_references.BELGIUM_MBUS3_DEVICE_TYPE in telegram:
if (
telegram[obis_references.BELGIUM_MBUS3_DEVICE_TYPE] == 3
or telegram[obis_references.BELGIUM_MBUS3_DEVICE_TYPE] == 7
):
description = create_mbus_entity(
3, telegram[obis_references.BELGIUM_MBUS3_DEVICE_TYPE], telegram
)
async_add_entities([DSMREntity(description, entry)])
if obis_references.BELGIUM_MBUS4_DEVICE_TYPE in telegram:
if (
telegram[obis_references.BELGIUM_MBUS4_DEVICE_TYPE] == 3
or telegram[obis_references.BELGIUM_MBUS4_DEVICE_TYPE] == 7
):
description = create_mbus_entity(
4, telegram[obis_references.BELGIUM_MBUS4_DEVICE_TYPE], telegram
)
async_add_entities([DSMREntity(description, entry)])

# Make all device entities aware of new telegram
for entity in entities:
entity.update_data(telegram)
Expand Down
2 changes: 1 addition & 1 deletion requirements_all.txt
Expand Up @@ -606,7 +606,7 @@ doorbirdpy==2.1.0
dovado==0.4.1

# homeassistant.components.dsmr
dsmr_parser==0.33
dsmr_parser==1.0.0

# homeassistant.components.dwd_weather_warnings
dwdwfsapi==1.0.5
Expand Down
2 changes: 1 addition & 1 deletion requirements_test_all.txt
Expand Up @@ -474,7 +474,7 @@ discovery30303==0.2.1
doorbirdpy==2.1.0

# homeassistant.components.dsmr
dsmr_parser==0.33
dsmr_parser==1.0.0

# homeassistant.components.dynalite
dynalite_devices==0.1.47
Expand Down
42 changes: 42 additions & 0 deletions tests/components/dsmr/test_config_flow.py
Expand Up @@ -210,6 +210,48 @@ async def test_setup_serial_rfxtrx(
assert result["data"] == {**entry_data, **SERIAL_DATA}


@patch("serial.tools.list_ports.comports", return_value=[com_port()])
async def test_setup_5B(com_mock, hass, dsmr_connection_send_validate_fixture):
"""Test we can setup serial."""
port = com_port()

result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)

assert result["type"] == "form"
assert result["step_id"] == "user"
assert result["errors"] is None

result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"type": "Serial"},
)

assert result["type"] == "form"
assert result["step_id"] == "setup_serial"
assert result["errors"] == {}

with patch("homeassistant.components.dsmr.async_setup_entry", return_value=True):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"port": port.device, "dsmr_version": "5B"},
)
await hass.async_block_till_done()

entry_data = {
"port": port.device,
"dsmr_version": "5B",
"protocol": "dsmr_protocol",
"serial_id": "12345678",
"serial_id_gas": "123456789",
}

assert result["type"] == "create_entry"
assert result["title"] == port.device
assert result["data"] == entry_data


@patch("serial.tools.list_ports.comports", return_value=[com_port()])
async def test_setup_5L(com_mock, hass, dsmr_connection_send_validate_fixture):
"""Test we can setup serial."""
Expand Down
29 changes: 26 additions & 3 deletions tests/components/dsmr/test_sensor.py
Expand Up @@ -393,7 +393,10 @@ async def test_belgian_meter(hass, dsmr_connection_fixture):
(connection_factory, transport, protocol) = dsmr_connection_fixture

from dsmr_parser.obis_references import (
BELGIUM_5MIN_GAS_METER_READING,
BELGIUM_MBUS1_DEVICE_TYPE,
BELGIUM_MBUS1_METER_READING2,
BELGIUM_MBUS2_DEVICE_TYPE,
BELGIUM_MBUS2_METER_READING1,
ELECTRICITY_ACTIVE_TARIFF,
)
from dsmr_parser.objects import CosemObject, MBusObject
Expand All @@ -411,12 +414,20 @@ async def test_belgian_meter(hass, dsmr_connection_fixture):
}

telegram = {
BELGIUM_5MIN_GAS_METER_READING: MBusObject(
BELGIUM_MBUS1_DEVICE_TYPE: CosemObject([{"value": "003", "unit": ""}]),
BELGIUM_MBUS1_METER_READING2: MBusObject(
[
{"value": datetime.datetime.fromtimestamp(1551642213)},
{"value": Decimal(745.695), "unit": "m3"},
]
),
BELGIUM_MBUS2_DEVICE_TYPE: CosemObject([{"value": "007", "unit": ""}]),
BELGIUM_MBUS2_METER_READING1: MBusObject(
[
{"value": datetime.datetime.fromtimestamp(1551642214)},
{"value": Decimal(678.695), "unit": "m3"},
]
),
ELECTRICITY_ACTIVE_TARIFF: CosemObject([{"value": "0001", "unit": ""}]),
}

Expand Down Expand Up @@ -446,7 +457,7 @@ async def test_belgian_meter(hass, dsmr_connection_fixture):
assert active_tariff.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ""

# check if gas consumption is parsed correctly
gas_consumption = hass.states.get("sensor.gas_meter_gas_consumption")
gas_consumption = hass.states.get("sensor.mbus1_gas_reading")
assert gas_consumption.state == "745.695"
assert gas_consumption.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.GAS
assert (
Expand All @@ -457,6 +468,18 @@ async def test_belgian_meter(hass, dsmr_connection_fixture):
gas_consumption.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == VOLUME_CUBIC_METERS
)

# check if water usage is parsed correctly
gas_consumption = hass.states.get("sensor.mbus2_water_reading")
assert gas_consumption.state == "678.695"
assert gas_consumption.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.WATER
assert (
gas_consumption.attributes.get(ATTR_STATE_CLASS)
== SensorStateClass.TOTAL_INCREASING
)
assert (
gas_consumption.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == VOLUME_CUBIC_METERS
)


async def test_belgian_meter_low(hass, dsmr_connection_fixture):
"""Test if Belgian meter is correctly parsed."""
Expand Down

0 comments on commit 6dbd691

Please sign in to comment.