Fix zwave_js device re-interview (#78046)

* Handle stale node and entity info on re-interview

* Add test

* Unsubscribe on config entry unload
This commit is contained in:
Martin Hjelmare 2022-09-08 20:15:27 +02:00 committed by GitHub
parent be064bfeef
commit f11b51e12b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 103 additions and 9 deletions

View file

@ -313,19 +313,24 @@ class ControllerEvents:
node,
)
LOGGER.debug("Node added: %s", node.node_id)
# Listen for ready node events, both new and re-interview.
self.config_entry.async_on_unload(
node.on(
"ready",
lambda event: self.hass.async_create_task(
self.node_events.async_on_node_ready(event["node"])
),
)
)
# we only want to run discovery when the node has reached ready state,
# otherwise we'll have all kinds of missing info issues.
if node.ready:
await self.node_events.async_on_node_ready(node)
return
# if node is not yet ready, register one-time callback for ready state
LOGGER.debug("Node added: %s - waiting for it to become ready", node.node_id)
node.once(
"ready",
lambda event: self.hass.async_create_task(
self.node_events.async_on_node_ready(event["node"])
),
)
# we do submit the node to device registry so user has
# some visual feedback that something is (in the process of) being added
self.register_node_in_dev_reg(node)
@ -414,12 +419,25 @@ class NodeEvents:
async def async_on_node_ready(self, node: ZwaveNode) -> None:
"""Handle node ready event."""
LOGGER.debug("Processing node %s", node)
driver = self.controller_events.driver_events.driver
# register (or update) node in device registry
device = self.controller_events.register_node_in_dev_reg(node)
# We only want to create the defaultdict once, even on reinterviews
if device.id not in self.controller_events.registered_unique_ids:
self.controller_events.registered_unique_ids[device.id] = defaultdict(set)
# Remove any old value ids if this is a reinterview.
self.controller_events.discovered_value_ids.pop(device.id, None)
# Remove stale entities that may exist from a previous interview.
async_dispatcher_send(
self.hass,
(
f"{DOMAIN}_"
f"{get_valueless_base_unique_id(driver, node)}_"
"remove_entity_on_ready_node"
),
)
value_updates_disc_info: dict[str, ZwaveDiscoveryInfo] = {}
# run discovery on all node values and create/update entities

View file

@ -12,7 +12,7 @@ from homeassistant.helpers.entity import DeviceInfo, Entity
from .const import DOMAIN, LOGGER
from .discovery import ZwaveDiscoveryInfo
from .helpers import get_device_id, get_unique_id
from .helpers import get_device_id, get_unique_id, get_valueless_base_unique_id
EVENT_VALUE_UPDATED = "value updated"
EVENT_VALUE_REMOVED = "value removed"
@ -96,6 +96,17 @@ class ZWaveBaseEntity(Entity):
self.async_on_remove(
self.info.node.on(EVENT_VALUE_REMOVED, self._value_removed)
)
self.async_on_remove(
async_dispatcher_connect(
self.hass,
(
f"{DOMAIN}_"
f"{get_valueless_base_unique_id(self.driver, self.info.node)}_"
"remove_entity_on_ready_node"
),
self.async_remove,
)
)
for status_event in (EVENT_ALIVE, EVENT_DEAD):
self.async_on_remove(

View file

@ -189,6 +189,14 @@ class ZWaveNodeFirmwareUpdate(UpdateEntity):
)
)
self.async_on_remove(
async_dispatcher_connect(
self.hass,
f"{DOMAIN}_{self._base_unique_id}_remove_entity_on_ready_node",
self.async_remove,
)
)
self.async_on_remove(async_at_start(self.hass, self._async_update))
async def async_will_remove_from_hass(self) -> None: