Skip to content

Commit

Permalink
Fix traceback during ZHA device removal (home-assistant#24882)
Browse files Browse the repository at this point in the history
* fix device remove lifecycle
* clean up remove signal
* add guard
  • Loading branch information
dmulcahey authored and Adminiuga committed Jul 1, 2019
1 parent 40c424e commit 7d651e2
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 31 deletions.
2 changes: 1 addition & 1 deletion homeassistant/components/zha/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def async_get_device_info(hass, device, ha_device_registry=None):
ret_device = {}
ret_device.update(device.device_info)
ret_device['entities'] = [{
'entity_id': entity_ref.reference_id,
'entity_id': entity_ref.entity.entity_id,
NAME: entity_ref.device_info[NAME]
} for entity_ref in zha_gateway.device_registry[device.ieee]]

Expand Down
1 change: 0 additions & 1 deletion homeassistant/components/zha/core/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@
SIGNAL_SET_LEVEL = "set_level"
SIGNAL_STATE_ATTR = "update_state_attribute"
SIGNAL_AVAILABLE = 'available'
SIGNAL_REMOVE = 'remove'

QUIRK_APPLIED = 'quirk_applied'
QUIRK_CLASS = 'quirk_class'
Expand Down
39 changes: 19 additions & 20 deletions homeassistant/components/zha/core/gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
DEBUG_LEVELS, DEFAULT_BAUDRATE, DEFAULT_DATABASE_NAME, DEVICE_FULL_INIT,
DEVICE_INFO, DEVICE_JOINED, DEVICE_REMOVED, DOMAIN, IEEE, LOG_ENTRY,
LOG_OUTPUT, MODEL, NWK, ORIGINAL, RADIO, RADIO_DESCRIPTION, RAW_INIT,
SIGNAL_REMOVE, SIGNATURE, TYPE, UNKNOWN_MANUFACTURER, UNKNOWN_MODEL, ZHA,
ZHA_GW_MSG, ZIGPY, ZIGPY_DECONZ, ZIGPY_XBEE)
SIGNATURE, TYPE, UNKNOWN_MANUFACTURER, UNKNOWN_MODEL, ZHA, ZHA_GW_MSG,
ZIGPY, ZIGPY_DECONZ, ZIGPY_XBEE)
from .device import DeviceStatus, ZHADevice
from .discovery import (
async_create_device_entity, async_dispatch_discovery_info,
Expand All @@ -40,7 +40,9 @@
_LOGGER = logging.getLogger(__name__)

EntityReference = collections.namedtuple(
'EntityReference', 'reference_id zha_device cluster_channels device_info')
'EntityReference',
'entity device_info'
)


class ZHAGateway:
Expand Down Expand Up @@ -143,23 +145,27 @@ def device_left(self, device):
"""Handle device leaving the network."""
pass

async def _async_remove_device(self, device):
async def _async_remove_device(self, device, entity_refs):
if entity_refs is not None:
remove_tasks = []
for entity_ref in entity_refs:
remove_tasks.append(entity_ref.entity.async_remove())
await asyncio.gather(*remove_tasks)
ha_device_registry = await get_dev_reg(self._hass)
reg_device = ha_device_registry.async_get_device(
{(DOMAIN, str(device.ieee))}, set())
ha_device_registry.async_remove_device(reg_device.id)
if reg_device is not None:
ha_device_registry.async_remove_device(reg_device.id)

def device_removed(self, device):
"""Handle device being removed from the network."""
zha_device = self._devices.pop(device.ieee, None)
self._device_registry.pop(device.ieee, None)
entity_refs = self._device_registry.pop(device.ieee, None)
if zha_device is not None:
device_info = async_get_device_info(self._hass, zha_device)
zha_device.async_unsub_dispatcher()
asyncio.ensure_future(self._async_remove_device(zha_device))
async_dispatcher_send(
self._hass,
"{}_{}".format(SIGNAL_REMOVE, str(zha_device.ieee))
asyncio.ensure_future(
self._async_remove_device(zha_device, entity_refs)
)
if device_info is not None:
async_dispatcher_send(
Expand All @@ -179,7 +185,7 @@ def get_entity_reference(self, entity_id):
"""Return entity reference for given entity_id if found."""
for entity_reference in itertools.chain.from_iterable(
self.device_registry.values()):
if entity_id == entity_reference.reference_id:
if entity_id == entity_reference.entity.entity_id:
return entity_reference

@property
Expand All @@ -192,17 +198,10 @@ def device_registry(self):
"""Return entities by ieee."""
return self._device_registry

def register_entity_reference(
self, ieee, reference_id, zha_device, cluster_channels,
device_info):
def register_entity_reference(self, ieee, entity, device_info):
"""Record the creation of a hass entity associated with ieee."""
self._device_registry[ieee].append(
EntityReference(
reference_id=reference_id,
zha_device=zha_device,
cluster_channels=cluster_channels,
device_info=device_info
)
EntityReference(entity=entity, device_info=device_info)
)

@callback
Expand Down
12 changes: 3 additions & 9 deletions homeassistant/components/zha/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
from homeassistant.util import slugify

from .core.const import (
ATTR_MANUFACTURER, DATA_ZHA, DATA_ZHA_BRIDGE_ID, DOMAIN, MODEL, NAME,
SIGNAL_REMOVE)
ATTR_MANUFACTURER, DATA_ZHA, DATA_ZHA_BRIDGE_ID, DOMAIN, MODEL, NAME)

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -126,14 +125,9 @@ async def async_added_to_hass(self):
None, "{}_{}".format(self.zha_device.available_signal, 'entity'),
self.async_set_available,
signal_override=True)
await self.async_accept_signal(
None, "{}_{}".format(SIGNAL_REMOVE, str(self.zha_device.ieee)),
self.async_remove,
signal_override=True
)
self._zha_device.gateway.register_entity_reference(
self._zha_device.ieee, self.entity_id, self._zha_device,
self.cluster_channels, self.device_info)
self._zha_device.ieee, self, self.device_info
)

async def async_check_recently_seen(self):
"""Check if the device was seen within the last 2 hours."""
Expand Down

0 comments on commit 7d651e2

Please sign in to comment.