Only reflect unavailable state in DSMR when disconnected (#84862)

* Only reflect unavailable state in DSMR when disonnected

* Addressreview comment
This commit is contained in:
Franck Nijhof 2022-12-30 19:08:11 +01:00 committed by GitHub
parent f8fa676ac8
commit 02f64ada2d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 31 additions and 8 deletions

View file

@ -401,7 +401,7 @@ async def async_setup_entry(
) )
@Throttle(min_time_between_updates) @Throttle(min_time_between_updates)
def update_entities_telegram(telegram: dict[str, DSMRObject]) -> None: def update_entities_telegram(telegram: dict[str, DSMRObject] | None) -> None:
"""Update entities with latest telegram and trigger state update.""" """Update entities with latest telegram and trigger state update."""
# Make all device entities aware of new telegram # Make all device entities aware of new telegram
for entity in entities: for entity in entities:
@ -445,6 +445,11 @@ async def async_setup_entry(
while hass.state == CoreState.not_running or hass.is_running: while hass.state == CoreState.not_running or hass.is_running:
# Start DSMR asyncio.Protocol reader # Start DSMR asyncio.Protocol reader
# Reflect connected state in devices state by setting an
# empty telegram resulting in `unknown` states
update_entities_telegram({})
try: try:
transport, protocol = await hass.loop.create_task(reader_factory()) transport, protocol = await hass.loop.create_task(reader_factory())
@ -472,8 +477,8 @@ async def async_setup_entry(
protocol = None protocol = None
# Reflect disconnect state in devices state by setting an # Reflect disconnect state in devices state by setting an
# empty telegram resulting in `unknown` states # None telegram resulting in `unavailable` states
update_entities_telegram({}) update_entities_telegram(None)
# throttle reconnect attempts # throttle reconnect attempts
await asyncio.sleep( await asyncio.sleep(
@ -487,11 +492,19 @@ async def async_setup_entry(
transport = None transport = None
protocol = None protocol = None
# Reflect disconnect state in devices state by setting an
# None telegram resulting in `unavailable` states
update_entities_telegram(None)
# throttle reconnect attempts # throttle reconnect attempts
await asyncio.sleep( await asyncio.sleep(
entry.data.get(CONF_RECONNECT_INTERVAL, DEFAULT_RECONNECT_INTERVAL) entry.data.get(CONF_RECONNECT_INTERVAL, DEFAULT_RECONNECT_INTERVAL)
) )
except CancelledError: except CancelledError:
# Reflect disconnect state in devices state by setting an
# None telegram resulting in `unavailable` states
update_entities_telegram(None)
if stop_listener and ( if stop_listener and (
hass.state == CoreState.not_running or hass.is_running hass.state == CoreState.not_running or hass.is_running
): ):
@ -534,7 +547,7 @@ class DSMREntity(SensorEntity):
"""Initialize entity.""" """Initialize entity."""
self.entity_description = entity_description self.entity_description = entity_description
self._entry = entry self._entry = entry
self.telegram: dict[str, DSMRObject] = {} self.telegram: dict[str, DSMRObject] | None = {}
device_serial = entry.data[CONF_SERIAL_ID] device_serial = entry.data[CONF_SERIAL_ID]
device_name = DEVICE_NAME_ELECTRICITY device_name = DEVICE_NAME_ELECTRICITY
@ -551,16 +564,21 @@ class DSMREntity(SensorEntity):
self._attr_unique_id = f"{device_serial}_{entity_description.key}" self._attr_unique_id = f"{device_serial}_{entity_description.key}"
@callback @callback
def update_data(self, telegram: dict[str, DSMRObject]) -> None: def update_data(self, telegram: dict[str, DSMRObject] | None) -> None:
"""Update data.""" """Update data."""
self.telegram = telegram self.telegram = telegram
if self.hass and self.entity_description.obis_reference in self.telegram: if self.hass and (
telegram is None or self.entity_description.obis_reference in telegram
):
self.async_write_ha_state() self.async_write_ha_state()
def get_dsmr_object_attr(self, attribute: str) -> str | None: def get_dsmr_object_attr(self, attribute: str) -> str | None:
"""Read attribute from last received telegram for this DSMR object.""" """Read attribute from last received telegram for this DSMR object."""
# Make sure telegram contains an object for this entities obis # Make sure telegram contains an object for this entities obis
if self.entity_description.obis_reference not in self.telegram: if (
self.telegram is None
or self.entity_description.obis_reference not in self.telegram
):
return None return None
# Get the attribute value if the object has it # Get the attribute value if the object has it
@ -571,7 +589,7 @@ class DSMREntity(SensorEntity):
@property @property
def available(self) -> bool: def available(self) -> bool:
"""Entity is only available if there is a telegram.""" """Entity is only available if there is a telegram."""
return bool(self.telegram) return self.telegram is not None
@property @property
def native_value(self) -> StateType: def native_value(self) -> StateType:

View file

@ -24,6 +24,7 @@ from homeassistant.const import (
ATTR_UNIT_OF_MEASUREMENT, ATTR_UNIT_OF_MEASUREMENT,
ENERGY_KILO_WATT_HOUR, ENERGY_KILO_WATT_HOUR,
STATE_UNAVAILABLE, STATE_UNAVAILABLE,
STATE_UNKNOWN,
VOLUME_CUBIC_METERS, VOLUME_CUBIC_METERS,
UnitOfPower, UnitOfPower,
) )
@ -783,6 +784,10 @@ async def test_reconnect(hass, dsmr_connection_fixture):
assert connection_factory.call_count == 1 assert connection_factory.call_count == 1
state = hass.states.get("sensor.electricity_meter_power_consumption")
assert state
assert state.state == STATE_UNKNOWN
# indicate disconnect, release wait lock and allow reconnect to happen # indicate disconnect, release wait lock and allow reconnect to happen
closed.set() closed.set()
# wait for lock set to resolve # wait for lock set to resolve