Entity Cleanup on Z-Wave node removal (#23633)
* Initial groundwork for entity cleanup on node removal * Connect node_removed to dispatcher * update docstring * Add node_removal test * Address review comments * Use hass.add_job instead of run_coroutine_threadsafepull/23991/head
parent
eebd094423
commit
1282370ccb
|
@ -376,6 +376,25 @@ async def async_setup_entry(hass, config_entry):
|
|||
hass.add_job(check_has_unique_id, entity, _on_ready, _on_timeout,
|
||||
hass.loop)
|
||||
|
||||
def node_removed(node):
|
||||
node_id = node.node_id
|
||||
node_key = 'node-{}'.format(node_id)
|
||||
_LOGGER.info("Node Removed: %s",
|
||||
hass.data[DATA_DEVICES][node_key])
|
||||
for key in list(hass.data[DATA_DEVICES]):
|
||||
if not key.startswith('{}-'.format(node_id)):
|
||||
continue
|
||||
|
||||
entity = hass.data[DATA_DEVICES][key]
|
||||
_LOGGER.info('Removing Entity - value: %s - entity_id: %s',
|
||||
key, entity.entity_id)
|
||||
hass.add_job(entity.node_removed())
|
||||
del hass.data[DATA_DEVICES][key]
|
||||
|
||||
entity = hass.data[DATA_DEVICES][node_key]
|
||||
hass.add_job(entity.node_removed())
|
||||
del hass.data[DATA_DEVICES][node_key]
|
||||
|
||||
def network_ready():
|
||||
"""Handle the query of all awake nodes."""
|
||||
_LOGGER.info("Z-Wave network is ready for use. All awake nodes "
|
||||
|
@ -399,6 +418,8 @@ async def async_setup_entry(hass, config_entry):
|
|||
value_added, ZWaveNetwork.SIGNAL_VALUE_ADDED, weak=False)
|
||||
dispatcher.connect(
|
||||
node_added, ZWaveNetwork.SIGNAL_NODE_ADDED, weak=False)
|
||||
dispatcher.connect(
|
||||
node_removed, ZWaveNetwork.SIGNAL_NODE_REMOVED, weak=False)
|
||||
dispatcher.connect(
|
||||
network_ready, ZWaveNetwork.SIGNAL_AWAKE_NODES_QUERIED, weak=False)
|
||||
dispatcher.connect(
|
||||
|
|
|
@ -3,6 +3,7 @@ import logging
|
|||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.const import ATTR_BATTERY_LEVEL, ATTR_WAKEUP, ATTR_ENTITY_ID
|
||||
from homeassistant.helpers.entity_registry import async_get_registry
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
from .const import (
|
||||
|
@ -74,6 +75,16 @@ class ZWaveBaseEntity(Entity):
|
|||
if self.hass and self.platform:
|
||||
self.hass.add_job(_async_remove_and_add)
|
||||
|
||||
async def node_removed(self):
|
||||
"""Call when a node is removed from the Z-Wave network."""
|
||||
await self.async_remove()
|
||||
|
||||
registry = await async_get_registry(self.hass)
|
||||
if self.entity_id not in registry.entities:
|
||||
return
|
||||
|
||||
registry.async_remove(self.entity_id)
|
||||
|
||||
|
||||
class ZWaveNodeEntity(ZWaveBaseEntity):
|
||||
"""Representation of a Z-Wave node."""
|
||||
|
|
|
@ -226,6 +226,48 @@ async def test_device_entity(hass, mock_openzwave):
|
|||
assert device.device_state_attributes[zwave.ATTR_POWER] == 50.123
|
||||
|
||||
|
||||
async def test_node_removed(hass, mock_openzwave):
|
||||
"""Test node removed in base class."""
|
||||
# Create a mock node & node entity
|
||||
node = MockNode(node_id='10', name='Mock Node')
|
||||
value = MockValue(data=False, node=node, instance=2, object_id='11',
|
||||
label='Sensor',
|
||||
command_class=const.COMMAND_CLASS_SENSOR_BINARY)
|
||||
power_value = MockValue(data=50.123456, node=node, precision=3,
|
||||
command_class=const.COMMAND_CLASS_METER)
|
||||
values = MockEntityValues(primary=value, power=power_value)
|
||||
device = zwave.ZWaveDeviceEntity(values, 'zwave')
|
||||
device.hass = hass
|
||||
device.entity_id = 'zwave.mock_node'
|
||||
device.value_added()
|
||||
device.update_properties()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Save it to the entity registry
|
||||
registry = mock_registry(hass)
|
||||
registry.async_get_or_create('zwave', 'zwave', device.unique_id)
|
||||
device.entity_id = registry.async_get_entity_id(
|
||||
'zwave', 'zwave', device.unique_id)
|
||||
|
||||
# Create dummy entity registry entries for other integrations
|
||||
hue_entity = registry.async_get_or_create('light', 'hue', 1234)
|
||||
zha_entity = registry.async_get_or_create('sensor', 'zha', 5678)
|
||||
|
||||
# Verify our Z-Wave entity is registered
|
||||
assert registry.async_is_registered(device.entity_id)
|
||||
|
||||
# Remove it
|
||||
entity_id = device.entity_id
|
||||
await device.node_removed()
|
||||
|
||||
# Verify registry entry for our Z-Wave node is gone
|
||||
assert not registry.async_is_registered(entity_id)
|
||||
|
||||
# Verify registry entries for our other entities remain
|
||||
assert registry.async_is_registered(hue_entity.entity_id)
|
||||
assert registry.async_is_registered(zha_entity.entity_id)
|
||||
|
||||
|
||||
async def test_node_discovery(hass, mock_openzwave):
|
||||
"""Test discovery of a node."""
|
||||
mock_receivers = []
|
||||
|
|
Loading…
Reference in New Issue