Add support for Swedish smart electricity meters to DSMR (#54630)
* Add support for Swedish smart electricity meters to DSMR * Use Swedish protocol support from dsmr_parser * Update tests * Bump dsmr_parser to 0.30 * Remove last_reset attribute from Swedish energy sensors
This commit is contained in:
parent
0688aaa2b6
commit
32a2c5d5db
9 changed files with 149 additions and 13 deletions
|
@ -28,6 +28,7 @@ from .const import (
|
||||||
CONF_TIME_BETWEEN_UPDATE,
|
CONF_TIME_BETWEEN_UPDATE,
|
||||||
DEFAULT_TIME_BETWEEN_UPDATE,
|
DEFAULT_TIME_BETWEEN_UPDATE,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
|
DSMR_VERSIONS,
|
||||||
LOGGER,
|
LOGGER,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -70,6 +71,10 @@ class DSMRConnection:
|
||||||
if self._equipment_identifier in telegram:
|
if self._equipment_identifier in telegram:
|
||||||
self._telegram = telegram
|
self._telegram = telegram
|
||||||
transport.close()
|
transport.close()
|
||||||
|
# Swedish meters have no equipment identifier
|
||||||
|
if self._dsmr_version == "5S" and obis_ref.P1_MESSAGE_TIMESTAMP in telegram:
|
||||||
|
self._telegram = telegram
|
||||||
|
transport.close()
|
||||||
|
|
||||||
if self._host is None:
|
if self._host is None:
|
||||||
reader_factory = partial(
|
reader_factory = partial(
|
||||||
|
@ -119,7 +124,7 @@ async def _validate_dsmr_connection(
|
||||||
equipment_identifier_gas = conn.equipment_identifier_gas()
|
equipment_identifier_gas = conn.equipment_identifier_gas()
|
||||||
|
|
||||||
# Check only for equipment identifier in case no gas meter is connected
|
# Check only for equipment identifier in case no gas meter is connected
|
||||||
if equipment_identifier is None:
|
if equipment_identifier is None and data[CONF_DSMR_VERSION] != "5S":
|
||||||
raise CannotCommunicate
|
raise CannotCommunicate
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -203,7 +208,7 @@ class DSMRFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
{
|
{
|
||||||
vol.Required(CONF_HOST): str,
|
vol.Required(CONF_HOST): str,
|
||||||
vol.Required(CONF_PORT): int,
|
vol.Required(CONF_PORT): int,
|
||||||
vol.Required(CONF_DSMR_VERSION): vol.In(["2.2", "4", "5", "5B", "5L"]),
|
vol.Required(CONF_DSMR_VERSION): vol.In(DSMR_VERSIONS),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
|
@ -247,7 +252,7 @@ class DSMRFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
schema = vol.Schema(
|
schema = vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_PORT): vol.In(list_of_ports),
|
vol.Required(CONF_PORT): vol.In(list_of_ports),
|
||||||
vol.Required(CONF_DSMR_VERSION): vol.In(["2.2", "4", "5", "5B", "5L"]),
|
vol.Required(CONF_DSMR_VERSION): vol.In(DSMR_VERSIONS),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
|
@ -288,6 +293,7 @@ class DSMRFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
|
|
||||||
data = {**data, **info}
|
data = {**data, **info}
|
||||||
|
|
||||||
|
if info[CONF_SERIAL_ID]:
|
||||||
await self.async_set_unique_id(info[CONF_SERIAL_ID])
|
await self.async_set_unique_id(info[CONF_SERIAL_ID])
|
||||||
self._abort_if_unique_id_configured()
|
self._abort_if_unique_id_configured()
|
||||||
except CannotConnect:
|
except CannotConnect:
|
||||||
|
@ -316,6 +322,7 @@ class DSMRFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
name = f"{host}:{port}" if host is not None else port
|
name = f"{host}:{port}" if host is not None else port
|
||||||
data = {**import_config, **info}
|
data = {**import_config, **info}
|
||||||
|
|
||||||
|
if info[CONF_SERIAL_ID]:
|
||||||
await self.async_set_unique_id(info[CONF_SERIAL_ID])
|
await self.async_set_unique_id(info[CONF_SERIAL_ID])
|
||||||
self._abort_if_unique_id_configured(data)
|
self._abort_if_unique_id_configured(data)
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,8 @@ DATA_TASK = "task"
|
||||||
DEVICE_NAME_ENERGY = "Energy Meter"
|
DEVICE_NAME_ENERGY = "Energy Meter"
|
||||||
DEVICE_NAME_GAS = "Gas Meter"
|
DEVICE_NAME_GAS = "Gas Meter"
|
||||||
|
|
||||||
|
DSMR_VERSIONS = {"2.2", "4", "5", "5B", "5L", "5S"}
|
||||||
|
|
||||||
SENSORS: tuple[DSMRSensorEntityDescription, ...] = (
|
SENSORS: tuple[DSMRSensorEntityDescription, ...] = (
|
||||||
DSMRSensorEntityDescription(
|
DSMRSensorEntityDescription(
|
||||||
key=obis_references.CURRENT_ELECTRICITY_USAGE,
|
key=obis_references.CURRENT_ELECTRICITY_USAGE,
|
||||||
|
@ -62,11 +64,13 @@ SENSORS: tuple[DSMRSensorEntityDescription, ...] = (
|
||||||
DSMRSensorEntityDescription(
|
DSMRSensorEntityDescription(
|
||||||
key=obis_references.ELECTRICITY_ACTIVE_TARIFF,
|
key=obis_references.ELECTRICITY_ACTIVE_TARIFF,
|
||||||
name="Power Tariff",
|
name="Power Tariff",
|
||||||
|
dsmr_versions={"2.2", "4", "5", "5B", "5L"},
|
||||||
icon="mdi:flash",
|
icon="mdi:flash",
|
||||||
),
|
),
|
||||||
DSMRSensorEntityDescription(
|
DSMRSensorEntityDescription(
|
||||||
key=obis_references.ELECTRICITY_USED_TARIFF_1,
|
key=obis_references.ELECTRICITY_USED_TARIFF_1,
|
||||||
name="Energy Consumption (tarif 1)",
|
name="Energy Consumption (tarif 1)",
|
||||||
|
dsmr_versions={"2.2", "4", "5", "5B", "5L"},
|
||||||
device_class=DEVICE_CLASS_ENERGY,
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
force_update=True,
|
force_update=True,
|
||||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
@ -74,6 +78,7 @@ SENSORS: tuple[DSMRSensorEntityDescription, ...] = (
|
||||||
DSMRSensorEntityDescription(
|
DSMRSensorEntityDescription(
|
||||||
key=obis_references.ELECTRICITY_USED_TARIFF_2,
|
key=obis_references.ELECTRICITY_USED_TARIFF_2,
|
||||||
name="Energy Consumption (tarif 2)",
|
name="Energy Consumption (tarif 2)",
|
||||||
|
dsmr_versions={"2.2", "4", "5", "5B", "5L"},
|
||||||
force_update=True,
|
force_update=True,
|
||||||
device_class=DEVICE_CLASS_ENERGY,
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
@ -81,6 +86,7 @@ SENSORS: tuple[DSMRSensorEntityDescription, ...] = (
|
||||||
DSMRSensorEntityDescription(
|
DSMRSensorEntityDescription(
|
||||||
key=obis_references.ELECTRICITY_DELIVERED_TARIFF_1,
|
key=obis_references.ELECTRICITY_DELIVERED_TARIFF_1,
|
||||||
name="Energy Production (tarif 1)",
|
name="Energy Production (tarif 1)",
|
||||||
|
dsmr_versions={"2.2", "4", "5", "5B", "5L"},
|
||||||
force_update=True,
|
force_update=True,
|
||||||
device_class=DEVICE_CLASS_ENERGY,
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
@ -88,6 +94,7 @@ SENSORS: tuple[DSMRSensorEntityDescription, ...] = (
|
||||||
DSMRSensorEntityDescription(
|
DSMRSensorEntityDescription(
|
||||||
key=obis_references.ELECTRICITY_DELIVERED_TARIFF_2,
|
key=obis_references.ELECTRICITY_DELIVERED_TARIFF_2,
|
||||||
name="Energy Production (tarif 2)",
|
name="Energy Production (tarif 2)",
|
||||||
|
dsmr_versions={"2.2", "4", "5", "5B", "5L"},
|
||||||
force_update=True,
|
force_update=True,
|
||||||
device_class=DEVICE_CLASS_ENERGY,
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
@ -137,45 +144,53 @@ SENSORS: tuple[DSMRSensorEntityDescription, ...] = (
|
||||||
DSMRSensorEntityDescription(
|
DSMRSensorEntityDescription(
|
||||||
key=obis_references.SHORT_POWER_FAILURE_COUNT,
|
key=obis_references.SHORT_POWER_FAILURE_COUNT,
|
||||||
name="Short Power Failure Count",
|
name="Short Power Failure Count",
|
||||||
|
dsmr_versions={"2.2", "4", "5", "5B", "5L"},
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
icon="mdi:flash-off",
|
icon="mdi:flash-off",
|
||||||
),
|
),
|
||||||
DSMRSensorEntityDescription(
|
DSMRSensorEntityDescription(
|
||||||
key=obis_references.LONG_POWER_FAILURE_COUNT,
|
key=obis_references.LONG_POWER_FAILURE_COUNT,
|
||||||
name="Long Power Failure Count",
|
name="Long Power Failure Count",
|
||||||
|
dsmr_versions={"2.2", "4", "5", "5B", "5L"},
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
icon="mdi:flash-off",
|
icon="mdi:flash-off",
|
||||||
),
|
),
|
||||||
DSMRSensorEntityDescription(
|
DSMRSensorEntityDescription(
|
||||||
key=obis_references.VOLTAGE_SAG_L1_COUNT,
|
key=obis_references.VOLTAGE_SAG_L1_COUNT,
|
||||||
name="Voltage Sags Phase L1",
|
name="Voltage Sags Phase L1",
|
||||||
|
dsmr_versions={"2.2", "4", "5", "5B", "5L"},
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
),
|
),
|
||||||
DSMRSensorEntityDescription(
|
DSMRSensorEntityDescription(
|
||||||
key=obis_references.VOLTAGE_SAG_L2_COUNT,
|
key=obis_references.VOLTAGE_SAG_L2_COUNT,
|
||||||
name="Voltage Sags Phase L2",
|
name="Voltage Sags Phase L2",
|
||||||
|
dsmr_versions={"2.2", "4", "5", "5B", "5L"},
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
),
|
),
|
||||||
DSMRSensorEntityDescription(
|
DSMRSensorEntityDescription(
|
||||||
key=obis_references.VOLTAGE_SAG_L3_COUNT,
|
key=obis_references.VOLTAGE_SAG_L3_COUNT,
|
||||||
name="Voltage Sags Phase L3",
|
name="Voltage Sags Phase L3",
|
||||||
|
dsmr_versions={"2.2", "4", "5", "5B", "5L"},
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
),
|
),
|
||||||
DSMRSensorEntityDescription(
|
DSMRSensorEntityDescription(
|
||||||
key=obis_references.VOLTAGE_SWELL_L1_COUNT,
|
key=obis_references.VOLTAGE_SWELL_L1_COUNT,
|
||||||
name="Voltage Swells Phase L1",
|
name="Voltage Swells Phase L1",
|
||||||
|
dsmr_versions={"2.2", "4", "5", "5B", "5L"},
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
icon="mdi:pulse",
|
icon="mdi:pulse",
|
||||||
),
|
),
|
||||||
DSMRSensorEntityDescription(
|
DSMRSensorEntityDescription(
|
||||||
key=obis_references.VOLTAGE_SWELL_L2_COUNT,
|
key=obis_references.VOLTAGE_SWELL_L2_COUNT,
|
||||||
name="Voltage Swells Phase L2",
|
name="Voltage Swells Phase L2",
|
||||||
|
dsmr_versions={"2.2", "4", "5", "5B", "5L"},
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
icon="mdi:pulse",
|
icon="mdi:pulse",
|
||||||
),
|
),
|
||||||
DSMRSensorEntityDescription(
|
DSMRSensorEntityDescription(
|
||||||
key=obis_references.VOLTAGE_SWELL_L3_COUNT,
|
key=obis_references.VOLTAGE_SWELL_L3_COUNT,
|
||||||
name="Voltage Swells Phase L3",
|
name="Voltage Swells Phase L3",
|
||||||
|
dsmr_versions={"2.2", "4", "5", "5B", "5L"},
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
icon="mdi:pulse",
|
icon="mdi:pulse",
|
||||||
),
|
),
|
||||||
|
@ -237,6 +252,22 @@ SENSORS: tuple[DSMRSensorEntityDescription, ...] = (
|
||||||
device_class=DEVICE_CLASS_ENERGY,
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
),
|
),
|
||||||
|
DSMRSensorEntityDescription(
|
||||||
|
key=obis_references.SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL,
|
||||||
|
name="Energy Consumption (total)",
|
||||||
|
dsmr_versions={"5S"},
|
||||||
|
force_update=True,
|
||||||
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
),
|
||||||
|
DSMRSensorEntityDescription(
|
||||||
|
key=obis_references.SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL,
|
||||||
|
name="Energy Production (total)",
|
||||||
|
dsmr_versions={"5S"},
|
||||||
|
force_update=True,
|
||||||
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
),
|
||||||
DSMRSensorEntityDescription(
|
DSMRSensorEntityDescription(
|
||||||
key=obis_references.ELECTRICITY_IMPORTED_TOTAL,
|
key=obis_references.ELECTRICITY_IMPORTED_TOTAL,
|
||||||
name="Energy Consumption (total)",
|
name="Energy Consumption (total)",
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"domain": "dsmr",
|
"domain": "dsmr",
|
||||||
"name": "DSMR Slimme Meter",
|
"name": "DSMR Slimme Meter",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/dsmr",
|
"documentation": "https://www.home-assistant.io/integrations/dsmr",
|
||||||
"requirements": ["dsmr_parser==0.29"],
|
"requirements": ["dsmr_parser==0.30"],
|
||||||
"codeowners": ["@Robbie1221", "@frenck"],
|
"codeowners": ["@Robbie1221", "@frenck"],
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"iot_class": "local_push"
|
"iot_class": "local_push"
|
||||||
|
|
|
@ -44,6 +44,7 @@ from .const import (
|
||||||
DEVICE_NAME_ENERGY,
|
DEVICE_NAME_ENERGY,
|
||||||
DEVICE_NAME_GAS,
|
DEVICE_NAME_GAS,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
|
DSMR_VERSIONS,
|
||||||
LOGGER,
|
LOGGER,
|
||||||
SENSORS,
|
SENSORS,
|
||||||
)
|
)
|
||||||
|
@ -54,7 +55,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||||
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.string,
|
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.string,
|
||||||
vol.Optional(CONF_HOST): cv.string,
|
vol.Optional(CONF_HOST): cv.string,
|
||||||
vol.Optional(CONF_DSMR_VERSION, default=DEFAULT_DSMR_VERSION): vol.All(
|
vol.Optional(CONF_DSMR_VERSION, default=DEFAULT_DSMR_VERSION): vol.All(
|
||||||
cv.string, vol.In(["5L", "5B", "5", "4", "2.2"])
|
cv.string, vol.In(DSMR_VERSIONS)
|
||||||
),
|
),
|
||||||
vol.Optional(CONF_RECONNECT_INTERVAL, default=DEFAULT_RECONNECT_INTERVAL): int,
|
vol.Optional(CONF_RECONNECT_INTERVAL, default=DEFAULT_RECONNECT_INTERVAL): int,
|
||||||
vol.Optional(CONF_PRECISION, default=DEFAULT_PRECISION): vol.Coerce(int),
|
vol.Optional(CONF_PRECISION, default=DEFAULT_PRECISION): vol.Coerce(int),
|
||||||
|
@ -118,7 +119,7 @@ async def async_setup_entry(
|
||||||
create_tcp_dsmr_reader,
|
create_tcp_dsmr_reader,
|
||||||
entry.data[CONF_HOST],
|
entry.data[CONF_HOST],
|
||||||
entry.data[CONF_PORT],
|
entry.data[CONF_PORT],
|
||||||
entry.data[CONF_DSMR_VERSION],
|
dsmr_version,
|
||||||
update_entities_telegram,
|
update_entities_telegram,
|
||||||
loop=hass.loop,
|
loop=hass.loop,
|
||||||
keep_alive_interval=60,
|
keep_alive_interval=60,
|
||||||
|
@ -127,7 +128,7 @@ async def async_setup_entry(
|
||||||
reader_factory = partial(
|
reader_factory = partial(
|
||||||
create_dsmr_reader,
|
create_dsmr_reader,
|
||||||
entry.data[CONF_PORT],
|
entry.data[CONF_PORT],
|
||||||
entry.data[CONF_DSMR_VERSION],
|
dsmr_version,
|
||||||
update_entities_telegram,
|
update_entities_telegram,
|
||||||
loop=hass.loop,
|
loop=hass.loop,
|
||||||
)
|
)
|
||||||
|
@ -217,6 +218,8 @@ class DSMREntity(SensorEntity):
|
||||||
if entity_description.is_gas:
|
if entity_description.is_gas:
|
||||||
device_serial = entry.data[CONF_SERIAL_ID_GAS]
|
device_serial = entry.data[CONF_SERIAL_ID_GAS]
|
||||||
device_name = DEVICE_NAME_GAS
|
device_name = DEVICE_NAME_GAS
|
||||||
|
if device_serial is None:
|
||||||
|
device_serial = entry.entry_id
|
||||||
|
|
||||||
self._attr_device_info = {
|
self._attr_device_info = {
|
||||||
"identifiers": {(DOMAIN, device_serial)},
|
"identifiers": {(DOMAIN, device_serial)},
|
||||||
|
|
|
@ -532,7 +532,7 @@ doorbirdpy==2.1.0
|
||||||
dovado==0.4.1
|
dovado==0.4.1
|
||||||
|
|
||||||
# homeassistant.components.dsmr
|
# homeassistant.components.dsmr
|
||||||
dsmr_parser==0.29
|
dsmr_parser==0.30
|
||||||
|
|
||||||
# homeassistant.components.dwd_weather_warnings
|
# homeassistant.components.dwd_weather_warnings
|
||||||
dwdwfsapi==1.0.4
|
dwdwfsapi==1.0.4
|
||||||
|
|
|
@ -307,7 +307,7 @@ directv==0.4.0
|
||||||
doorbirdpy==2.1.0
|
doorbirdpy==2.1.0
|
||||||
|
|
||||||
# homeassistant.components.dsmr
|
# homeassistant.components.dsmr
|
||||||
dsmr_parser==0.29
|
dsmr_parser==0.30
|
||||||
|
|
||||||
# homeassistant.components.dynalite
|
# homeassistant.components.dynalite
|
||||||
dynalite_devices==0.1.46
|
dynalite_devices==0.1.46
|
||||||
|
|
|
@ -7,6 +7,7 @@ from dsmr_parser.obis_references import (
|
||||||
EQUIPMENT_IDENTIFIER,
|
EQUIPMENT_IDENTIFIER,
|
||||||
EQUIPMENT_IDENTIFIER_GAS,
|
EQUIPMENT_IDENTIFIER_GAS,
|
||||||
LUXEMBOURG_EQUIPMENT_IDENTIFIER,
|
LUXEMBOURG_EQUIPMENT_IDENTIFIER,
|
||||||
|
P1_MESSAGE_TIMESTAMP,
|
||||||
)
|
)
|
||||||
from dsmr_parser.objects import CosemObject
|
from dsmr_parser.objects import CosemObject
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -44,6 +45,7 @@ async def dsmr_connection_send_validate_fixture(hass):
|
||||||
protocol.telegram = {
|
protocol.telegram = {
|
||||||
EQUIPMENT_IDENTIFIER: CosemObject([{"value": "12345678", "unit": ""}]),
|
EQUIPMENT_IDENTIFIER: CosemObject([{"value": "12345678", "unit": ""}]),
|
||||||
EQUIPMENT_IDENTIFIER_GAS: CosemObject([{"value": "123456789", "unit": ""}]),
|
EQUIPMENT_IDENTIFIER_GAS: CosemObject([{"value": "123456789", "unit": ""}]),
|
||||||
|
P1_MESSAGE_TIMESTAMP: CosemObject([{"value": "12345678", "unit": ""}]),
|
||||||
}
|
}
|
||||||
|
|
||||||
async def connection_factory(*args, **kwargs):
|
async def connection_factory(*args, **kwargs):
|
||||||
|
@ -57,6 +59,10 @@ async def dsmr_connection_send_validate_fixture(hass):
|
||||||
[{"value": "123456789", "unit": ""}]
|
[{"value": "123456789", "unit": ""}]
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
if args[1] == "5S":
|
||||||
|
protocol.telegram = {
|
||||||
|
P1_MESSAGE_TIMESTAMP: CosemObject([{"value": "12345678", "unit": ""}]),
|
||||||
|
}
|
||||||
|
|
||||||
return (transport, protocol)
|
return (transport, protocol)
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ from homeassistant.components.dsmr import DOMAIN, config_flow
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
SERIAL_DATA = {"serial_id": "12345678", "serial_id_gas": "123456789"}
|
SERIAL_DATA = {"serial_id": "12345678", "serial_id_gas": "123456789"}
|
||||||
|
SERIAL_DATA_SWEDEN = {"serial_id": None, "serial_id_gas": None}
|
||||||
|
|
||||||
|
|
||||||
def com_port():
|
def com_port():
|
||||||
|
@ -482,6 +483,29 @@ async def test_import_luxembourg(hass, dsmr_connection_send_validate_fixture):
|
||||||
assert result["data"] == {**entry_data, **SERIAL_DATA}
|
assert result["data"] == {**entry_data, **SERIAL_DATA}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_import_sweden(hass, dsmr_connection_send_validate_fixture):
|
||||||
|
"""Test we can import."""
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
|
entry_data = {
|
||||||
|
"port": "/dev/ttyUSB0",
|
||||||
|
"dsmr_version": "5S",
|
||||||
|
"precision": 4,
|
||||||
|
"reconnect_interval": 30,
|
||||||
|
}
|
||||||
|
|
||||||
|
with patch("homeassistant.components.dsmr.async_setup_entry", return_value=True):
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": config_entries.SOURCE_IMPORT},
|
||||||
|
data=entry_data,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "create_entry"
|
||||||
|
assert result["title"] == "/dev/ttyUSB0"
|
||||||
|
assert result["data"] == {**entry_data, **SERIAL_DATA_SWEDEN}
|
||||||
|
|
||||||
|
|
||||||
def test_get_serial_by_id_no_dir():
|
def test_get_serial_by_id_no_dir():
|
||||||
"""Test serial by id conversion if there's no /dev/serial/by-id."""
|
"""Test serial by id conversion if there's no /dev/serial/by-id."""
|
||||||
p1 = patch("os.path.isdir", MagicMock(return_value=False))
|
p1 = patch("os.path.isdir", MagicMock(return_value=False))
|
||||||
|
|
|
@ -536,6 +536,71 @@ async def test_belgian_meter_low(hass, dsmr_connection_fixture):
|
||||||
assert power_tariff.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ""
|
assert power_tariff.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ""
|
||||||
|
|
||||||
|
|
||||||
|
async def test_swedish_meter(hass, dsmr_connection_fixture):
|
||||||
|
"""Test if v5 meter is correctly parsed."""
|
||||||
|
(connection_factory, transport, protocol) = dsmr_connection_fixture
|
||||||
|
|
||||||
|
from dsmr_parser.obis_references import (
|
||||||
|
SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL,
|
||||||
|
SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL,
|
||||||
|
)
|
||||||
|
from dsmr_parser.objects import CosemObject
|
||||||
|
|
||||||
|
entry_data = {
|
||||||
|
"port": "/dev/ttyUSB0",
|
||||||
|
"dsmr_version": "5S",
|
||||||
|
"precision": 4,
|
||||||
|
"reconnect_interval": 30,
|
||||||
|
"serial_id": None,
|
||||||
|
"serial_id_gas": None,
|
||||||
|
}
|
||||||
|
entry_options = {
|
||||||
|
"time_between_update": 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
telegram = {
|
||||||
|
SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL: CosemObject(
|
||||||
|
[{"value": Decimal(123.456), "unit": ENERGY_KILO_WATT_HOUR}]
|
||||||
|
),
|
||||||
|
SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: CosemObject(
|
||||||
|
[{"value": Decimal(654.321), "unit": ENERGY_KILO_WATT_HOUR}]
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
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 update
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
|
||||||
|
power_tariff = hass.states.get("sensor.energy_consumption_total")
|
||||||
|
assert power_tariff.state == "123.456"
|
||||||
|
assert power_tariff.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_ENERGY
|
||||||
|
assert power_tariff.attributes.get(ATTR_ICON) is None
|
||||||
|
assert power_tariff.attributes.get(ATTR_STATE_CLASS) == STATE_CLASS_TOTAL_INCREASING
|
||||||
|
assert (
|
||||||
|
power_tariff.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ENERGY_KILO_WATT_HOUR
|
||||||
|
)
|
||||||
|
|
||||||
|
power_tariff = hass.states.get("sensor.energy_production_total")
|
||||||
|
assert power_tariff.state == "654.321"
|
||||||
|
assert power_tariff.attributes.get(ATTR_STATE_CLASS) == STATE_CLASS_TOTAL_INCREASING
|
||||||
|
assert (
|
||||||
|
power_tariff.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ENERGY_KILO_WATT_HOUR
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_tcp(hass, dsmr_connection_fixture):
|
async def test_tcp(hass, dsmr_connection_fixture):
|
||||||
"""If proper config provided TCP connection should be made."""
|
"""If proper config provided TCP connection should be made."""
|
||||||
(connection_factory, transport, protocol) = dsmr_connection_fixture
|
(connection_factory, transport, protocol) = dsmr_connection_fixture
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue