Add MIN_TIME_BETWEEN_UPDATES to dsmr integration (#43057)
parent
11a9aa3956
commit
930866bad5
|
@ -5,7 +5,7 @@ from asyncio import CancelledError
|
|||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import DATA_TASK, DOMAIN, PLATFORMS
|
||||
from .const import DATA_LISTENER, DATA_TASK, DOMAIN, PLATFORMS
|
||||
|
||||
|
||||
async def async_setup(hass, config: dict):
|
||||
|
@ -23,12 +23,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
|||
hass.config_entries.async_forward_entry_setup(entry, platform)
|
||||
)
|
||||
|
||||
listener = entry.add_update_listener(async_update_options)
|
||||
hass.data[DOMAIN][entry.entry_id][DATA_LISTENER] = listener
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
"""Unload a config entry."""
|
||||
task = hass.data[DOMAIN][entry.entry_id][DATA_TASK]
|
||||
listener = hass.data[DOMAIN][entry.entry_id][DATA_LISTENER]
|
||||
|
||||
# Cancel the reconnect task
|
||||
task.cancel()
|
||||
|
@ -46,6 +50,13 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
|||
)
|
||||
)
|
||||
if unload_ok:
|
||||
listener()
|
||||
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
|
||||
|
||||
async def async_update_options(hass: HomeAssistant, config_entry: ConfigEntry):
|
||||
"""Update options."""
|
||||
await hass.config_entries.async_reload(config_entry.entry_id)
|
||||
|
|
|
@ -8,14 +8,18 @@ from async_timeout import timeout
|
|||
from dsmr_parser import obis_references as obis_ref
|
||||
from dsmr_parser.clients.protocol import create_dsmr_reader, create_tcp_dsmr_reader
|
||||
import serial
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries, core, exceptions
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT
|
||||
from homeassistant.core import callback
|
||||
|
||||
from .const import ( # pylint:disable=unused-import
|
||||
CONF_DSMR_VERSION,
|
||||
CONF_SERIAL_ID,
|
||||
CONF_SERIAL_ID_GAS,
|
||||
CONF_TIME_BETWEEN_UPDATE,
|
||||
DEFAULT_TIME_BETWEEN_UPDATE,
|
||||
DOMAIN,
|
||||
)
|
||||
|
||||
|
@ -115,6 +119,12 @@ class DSMRFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
VERSION = 1
|
||||
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH
|
||||
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(config_entry):
|
||||
"""Get the options flow for this handler."""
|
||||
return DSMROptionFlowHandler(config_entry)
|
||||
|
||||
def _abort_if_host_port_configured(
|
||||
self,
|
||||
port: str,
|
||||
|
@ -174,6 +184,33 @@ class DSMRFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
return self.async_create_entry(title=name, data=data)
|
||||
|
||||
|
||||
class DSMROptionFlowHandler(config_entries.OptionsFlow):
|
||||
"""Handle options."""
|
||||
|
||||
def __init__(self, config_entry):
|
||||
"""Initialize options flow."""
|
||||
self.config_entry = config_entry
|
||||
|
||||
async def async_step_init(self, user_input=None):
|
||||
"""Manage the options."""
|
||||
if user_input is not None:
|
||||
return self.async_create_entry(title="", data=user_input)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="init",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Optional(
|
||||
CONF_TIME_BETWEEN_UPDATE,
|
||||
default=self.config_entry.options.get(
|
||||
CONF_TIME_BETWEEN_UPDATE, DEFAULT_TIME_BETWEEN_UPDATE
|
||||
),
|
||||
): vol.All(vol.Coerce(int), vol.Range(min=0)),
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class CannotConnect(exceptions.HomeAssistantError):
|
||||
"""Error to indicate we cannot connect."""
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ PLATFORMS = ["sensor"]
|
|||
CONF_DSMR_VERSION = "dsmr_version"
|
||||
CONF_RECONNECT_INTERVAL = "reconnect_interval"
|
||||
CONF_PRECISION = "precision"
|
||||
CONF_TIME_BETWEEN_UPDATE = "time_between_update"
|
||||
|
||||
CONF_SERIAL_ID = "serial_id"
|
||||
CONF_SERIAL_ID_GAS = "serial_id_gas"
|
||||
|
@ -15,7 +16,9 @@ DEFAULT_DSMR_VERSION = "2.2"
|
|||
DEFAULT_PORT = "/dev/ttyUSB0"
|
||||
DEFAULT_PRECISION = 3
|
||||
DEFAULT_RECONNECT_INTERVAL = 30
|
||||
DEFAULT_TIME_BETWEEN_UPDATE = 30
|
||||
|
||||
DATA_LISTENER = "listener"
|
||||
DATA_TASK = "task"
|
||||
|
||||
DEVICE_NAME_ENERGY = "Energy Meter"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
"""Support for Dutch Smart Meter (also known as Smartmeter or P1 port)."""
|
||||
import asyncio
|
||||
from asyncio import CancelledError
|
||||
from datetime import timedelta
|
||||
from functools import partial
|
||||
import logging
|
||||
from typing import Dict
|
||||
|
@ -22,6 +23,7 @@ from homeassistant.core import CoreState, callback
|
|||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.typing import HomeAssistantType
|
||||
from homeassistant.util import Throttle
|
||||
|
||||
from .const import (
|
||||
CONF_DSMR_VERSION,
|
||||
|
@ -29,11 +31,13 @@ from .const import (
|
|||
CONF_RECONNECT_INTERVAL,
|
||||
CONF_SERIAL_ID,
|
||||
CONF_SERIAL_ID_GAS,
|
||||
CONF_TIME_BETWEEN_UPDATE,
|
||||
DATA_TASK,
|
||||
DEFAULT_DSMR_VERSION,
|
||||
DEFAULT_PORT,
|
||||
DEFAULT_PRECISION,
|
||||
DEFAULT_RECONNECT_INTERVAL,
|
||||
DEFAULT_TIME_BETWEEN_UPDATE,
|
||||
DEVICE_NAME_ENERGY,
|
||||
DEVICE_NAME_GAS,
|
||||
DOMAIN,
|
||||
|
@ -72,6 +76,7 @@ async def async_setup_entry(
|
|||
) -> None:
|
||||
"""Set up the DSMR sensor."""
|
||||
config = entry.data
|
||||
options = entry.options
|
||||
|
||||
dsmr_version = config[CONF_DSMR_VERSION]
|
||||
|
||||
|
@ -142,6 +147,11 @@ async def async_setup_entry(
|
|||
|
||||
async_add_entities(devices)
|
||||
|
||||
min_time_between_updates = timedelta(
|
||||
seconds=options.get(CONF_TIME_BETWEEN_UPDATE, DEFAULT_TIME_BETWEEN_UPDATE)
|
||||
)
|
||||
|
||||
@Throttle(min_time_between_updates)
|
||||
def update_entities_telegram(telegram):
|
||||
"""Update entities with latest telegram and trigger state update."""
|
||||
# Make all device entities aware of new telegram
|
||||
|
@ -242,7 +252,7 @@ class DSMREntity(Entity):
|
|||
def update_data(self, telegram):
|
||||
"""Update data."""
|
||||
self.telegram = telegram
|
||||
if self.hass:
|
||||
if self.hass and self._obis in self.telegram:
|
||||
self.async_write_ha_state()
|
||||
|
||||
def get_dsmr_object_attr(self, attribute):
|
||||
|
|
|
@ -5,5 +5,15 @@
|
|||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"time_between_update": "Minimum time between entity updates [s]"
|
||||
},
|
||||
"title": "DSMR Options"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,5 +3,15 @@
|
|||
"abort": {
|
||||
"already_configured": "Device is already configured"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"time_between_update": "Minimum time between entity updates [s]"
|
||||
},
|
||||
"title": "DSMR Options"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ from itertools import chain, repeat
|
|||
|
||||
import serial
|
||||
|
||||
from homeassistant import config_entries, setup
|
||||
from homeassistant import config_entries, data_entry_flow, setup
|
||||
from homeassistant.components.dsmr import DOMAIN
|
||||
|
||||
from tests.async_mock import DEFAULT, AsyncMock, patch
|
||||
|
@ -196,9 +196,49 @@ async def test_import_update(hass, dsmr_connection_send_validate_fixture):
|
|||
data=new_entry_data,
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == "abort"
|
||||
assert result["reason"] == "already_configured"
|
||||
|
||||
assert entry.data["precision"] == 3
|
||||
|
||||
|
||||
async def test_options_flow(hass):
|
||||
"""Test options flow."""
|
||||
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||
|
||||
entry_data = {
|
||||
"port": "/dev/ttyUSB0",
|
||||
"dsmr_version": "2.2",
|
||||
"precision": 4,
|
||||
"reconnect_interval": 30,
|
||||
}
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=entry_data,
|
||||
unique_id="/dev/ttyUSB0",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.options.async_init(entry.entry_id)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "init"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
"time_between_update": 15,
|
||||
},
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.dsmr.async_setup_entry", return_value=True
|
||||
), patch("homeassistant.components.dsmr.async_unload_entry", return_value=True):
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.options == {"time_between_update": 15}
|
||||
|
|
|
@ -81,6 +81,9 @@ async def test_default_setup(hass, dsmr_connection_fixture):
|
|||
"serial_id": "1234",
|
||||
"serial_id_gas": "5678",
|
||||
}
|
||||
entry_options = {
|
||||
"time_between_update": 0,
|
||||
}
|
||||
|
||||
telegram = {
|
||||
CURRENT_ELECTRICITY_USAGE: CosemObject(
|
||||
|
@ -96,7 +99,7 @@ async def test_default_setup(hass, dsmr_connection_fixture):
|
|||
}
|
||||
|
||||
mock_entry = MockConfigEntry(
|
||||
domain="dsmr", unique_id="/dev/ttyUSB0", data=entry_data
|
||||
domain="dsmr", unique_id="/dev/ttyUSB0", data=entry_data, options=entry_options
|
||||
)
|
||||
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
@ -232,6 +235,9 @@ async def test_v4_meter(hass, dsmr_connection_fixture):
|
|||
"serial_id": "1234",
|
||||
"serial_id_gas": "5678",
|
||||
}
|
||||
entry_options = {
|
||||
"time_between_update": 0,
|
||||
}
|
||||
|
||||
telegram = {
|
||||
HOURLY_GAS_METER_READING: MBusObject(
|
||||
|
@ -244,7 +250,7 @@ async def test_v4_meter(hass, dsmr_connection_fixture):
|
|||
}
|
||||
|
||||
mock_entry = MockConfigEntry(
|
||||
domain="dsmr", unique_id="/dev/ttyUSB0", data=entry_data
|
||||
domain="dsmr", unique_id="/dev/ttyUSB0", data=entry_data, options=entry_options
|
||||
)
|
||||
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
@ -289,6 +295,9 @@ async def test_v5_meter(hass, dsmr_connection_fixture):
|
|||
"serial_id": "1234",
|
||||
"serial_id_gas": "5678",
|
||||
}
|
||||
entry_options = {
|
||||
"time_between_update": 0,
|
||||
}
|
||||
|
||||
telegram = {
|
||||
HOURLY_GAS_METER_READING: MBusObject(
|
||||
|
@ -301,7 +310,7 @@ async def test_v5_meter(hass, dsmr_connection_fixture):
|
|||
}
|
||||
|
||||
mock_entry = MockConfigEntry(
|
||||
domain="dsmr", unique_id="/dev/ttyUSB0", data=entry_data
|
||||
domain="dsmr", unique_id="/dev/ttyUSB0", data=entry_data, options=entry_options
|
||||
)
|
||||
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
@ -346,6 +355,9 @@ async def test_belgian_meter(hass, dsmr_connection_fixture):
|
|||
"serial_id": "1234",
|
||||
"serial_id_gas": "5678",
|
||||
}
|
||||
entry_options = {
|
||||
"time_between_update": 0,
|
||||
}
|
||||
|
||||
telegram = {
|
||||
BELGIUM_HOURLY_GAS_METER_READING: MBusObject(
|
||||
|
@ -358,7 +370,7 @@ async def test_belgian_meter(hass, dsmr_connection_fixture):
|
|||
}
|
||||
|
||||
mock_entry = MockConfigEntry(
|
||||
domain="dsmr", unique_id="/dev/ttyUSB0", data=entry_data
|
||||
domain="dsmr", unique_id="/dev/ttyUSB0", data=entry_data, options=entry_options
|
||||
)
|
||||
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
@ -400,11 +412,14 @@ async def test_belgian_meter_low(hass, dsmr_connection_fixture):
|
|||
"serial_id": "1234",
|
||||
"serial_id_gas": "5678",
|
||||
}
|
||||
entry_options = {
|
||||
"time_between_update": 0,
|
||||
}
|
||||
|
||||
telegram = {ELECTRICITY_ACTIVE_TARIFF: CosemObject([{"value": "0002", "unit": ""}])}
|
||||
|
||||
mock_entry = MockConfigEntry(
|
||||
domain="dsmr", unique_id="/dev/ttyUSB0", data=entry_data
|
||||
domain="dsmr", unique_id="/dev/ttyUSB0", data=entry_data, options=entry_options
|
||||
)
|
||||
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
|
Loading…
Reference in New Issue