From 645c3a67d818f6103bd125b6886d6c1a740a9d53 Mon Sep 17 00:00:00 2001 From: Robert Svensson <Kane610@users.noreply.github.com> Date: Wed, 29 Aug 2018 23:18:20 +0200 Subject: [PATCH] Fix so that entities are properly unloaded with config entry (#16281) --- .../components/binary_sensor/deconz.py | 5 +++ homeassistant/components/deconz/__init__.py | 19 +++++++-- homeassistant/components/light/deconz.py | 5 +++ homeassistant/components/scene/deconz.py | 4 ++ homeassistant/components/sensor/deconz.py | 40 ++++++++++++------- homeassistant/components/switch/deconz.py | 5 +++ requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 8 files changed, 62 insertions(+), 20 deletions(-) diff --git a/homeassistant/components/binary_sensor/deconz.py b/homeassistant/components/binary_sensor/deconz.py index 1fb62124407..d2ca9e7c5e8 100644 --- a/homeassistant/components/binary_sensor/deconz.py +++ b/homeassistant/components/binary_sensor/deconz.py @@ -54,6 +54,11 @@ class DeconzBinarySensor(BinarySensorDevice): self._sensor.register_async_callback(self.async_update_callback) self.hass.data[DATA_DECONZ_ID][self.entity_id] = self._sensor.deconz_id + async def async_will_remove_from_hass(self) -> None: + """Disconnect sensor object when removed.""" + self._sensor.remove_callback(self.async_update_callback) + self._sensor = None + @callback def async_update_callback(self, reason): """Update the sensor's state. diff --git a/homeassistant/components/deconz/__init__.py b/homeassistant/components/deconz/__init__.py index a4edc009ea1..e9f797d95f9 100644 --- a/homeassistant/components/deconz/__init__.py +++ b/homeassistant/components/deconz/__init__.py @@ -24,7 +24,7 @@ from .const import ( CONF_ALLOW_CLIP_SENSOR, CONFIG_FILE, DATA_DECONZ_EVENT, DATA_DECONZ_ID, DATA_DECONZ_UNSUB, DOMAIN, _LOGGER) -REQUIREMENTS = ['pydeconz==44'] +REQUIREMENTS = ['pydeconz==45'] CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ @@ -179,15 +179,22 @@ async def async_unload_entry(hass, config_entry): deconz = hass.data.pop(DOMAIN) hass.services.async_remove(DOMAIN, SERVICE_DECONZ) deconz.close() - for component in ['binary_sensor', 'light', 'scene', 'sensor']: + + for component in ['binary_sensor', 'light', 'scene', 'sensor', 'switch']: await hass.config_entries.async_forward_entry_unload( config_entry, component) + dispatchers = hass.data[DATA_DECONZ_UNSUB] for unsub_dispatcher in dispatchers: unsub_dispatcher() hass.data[DATA_DECONZ_UNSUB] = [] - hass.data[DATA_DECONZ_EVENT] = [] + + for event in hass.data[DATA_DECONZ_EVENT]: + event.async_will_remove_from_hass() + hass.data[DATA_DECONZ_EVENT].remove(event) + hass.data[DATA_DECONZ_ID] = [] + return True @@ -206,6 +213,12 @@ class DeconzEvent: self._event = 'deconz_{}'.format(CONF_EVENT) self._id = slugify(self._device.name) + @callback + def async_will_remove_from_hass(self) -> None: + """Disconnect event object when removed.""" + self._device.remove_callback(self.async_update_callback) + self._device = None + @callback def async_update_callback(self, reason): """Fire the event if reason is that state is updated.""" diff --git a/homeassistant/components/light/deconz.py b/homeassistant/components/light/deconz.py index 412cf8693e5..ff3fe609924 100644 --- a/homeassistant/components/light/deconz.py +++ b/homeassistant/components/light/deconz.py @@ -82,6 +82,11 @@ class DeconzLight(Light): self._light.register_async_callback(self.async_update_callback) self.hass.data[DATA_DECONZ_ID][self.entity_id] = self._light.deconz_id + async def async_will_remove_from_hass(self) -> None: + """Disconnect light object when removed.""" + self._light.remove_callback(self.async_update_callback) + self._light = None + @callback def async_update_callback(self, reason): """Update the light's state.""" diff --git a/homeassistant/components/scene/deconz.py b/homeassistant/components/scene/deconz.py index dde78dadc49..5af8f657206 100644 --- a/homeassistant/components/scene/deconz.py +++ b/homeassistant/components/scene/deconz.py @@ -38,6 +38,10 @@ class DeconzScene(Scene): """Subscribe to sensors events.""" self.hass.data[DATA_DECONZ_ID][self.entity_id] = self._scene.deconz_id + async def async_will_remove_from_hass(self) -> None: + """Disconnect scene object when removed.""" + self._scene = None + async def async_activate(self): """Activate the scene.""" await self._scene.async_set_state({}) diff --git a/homeassistant/components/sensor/deconz.py b/homeassistant/components/sensor/deconz.py index 8cb3915dc46..37fab727299 100644 --- a/homeassistant/components/sensor/deconz.py +++ b/homeassistant/components/sensor/deconz.py @@ -64,6 +64,11 @@ class DeconzSensor(Entity): self._sensor.register_async_callback(self.async_update_callback) self.hass.data[DATA_DECONZ_ID][self.entity_id] = self._sensor.deconz_id + async def async_will_remove_from_hass(self) -> None: + """Disconnect sensor object when removed.""" + self._sensor.remove_callback(self.async_update_callback) + self._sensor = None + @callback def async_update_callback(self, reason): """Update the sensor's state. @@ -155,16 +160,21 @@ class DeconzSensor(Entity): class DeconzBattery(Entity): """Battery class for when a device is only represented as an event.""" - def __init__(self, device): + def __init__(self, sensor): """Register dispatcher callback for update of battery state.""" - self._device = device - self._name = '{} {}'.format(self._device.name, 'Battery Level') + self._sensor = sensor + self._name = '{} {}'.format(self._sensor.name, 'Battery Level') self._unit_of_measurement = "%" async def async_added_to_hass(self): """Subscribe to sensors events.""" - self._device.register_async_callback(self.async_update_callback) - self.hass.data[DATA_DECONZ_ID][self.entity_id] = self._device.deconz_id + self._sensor.register_async_callback(self.async_update_callback) + self.hass.data[DATA_DECONZ_ID][self.entity_id] = self._sensor.deconz_id + + async def async_will_remove_from_hass(self) -> None: + """Disconnect sensor object when removed.""" + self._sensor.remove_callback(self.async_update_callback) + self._sensor = None @callback def async_update_callback(self, reason): @@ -175,7 +185,7 @@ class DeconzBattery(Entity): @property def state(self): """Return the state of the battery.""" - return self._device.battery + return self._sensor.battery @property def name(self): @@ -185,7 +195,7 @@ class DeconzBattery(Entity): @property def unique_id(self): """Return a unique identifier for the device.""" - return self._device.uniqueid + return self._sensor.uniqueid @property def device_class(self): @@ -206,22 +216,22 @@ class DeconzBattery(Entity): def device_state_attributes(self): """Return the state attributes of the battery.""" attr = { - ATTR_EVENT_ID: slugify(self._device.name), + ATTR_EVENT_ID: slugify(self._sensor.name), } return attr @property def device_info(self): """Return a device description for device registry.""" - if (self._device.uniqueid is None or - self._device.uniqueid.count(':') != 7): + if (self._sensor.uniqueid is None or + self._sensor.uniqueid.count(':') != 7): return None - serial = self._device.uniqueid.split('-', 1)[0] + serial = self._sensor.uniqueid.split('-', 1)[0] return { 'connections': {(CONNECTION_ZIGBEE, serial)}, 'identifiers': {(DECONZ_DOMAIN, serial)}, - 'manufacturer': self._device.manufacturer, - 'model': self._device.modelid, - 'name': self._device.name, - 'sw_version': self._device.swversion, + 'manufacturer': self._sensor.manufacturer, + 'model': self._sensor.modelid, + 'name': self._sensor.name, + 'sw_version': self._sensor.swversion, } diff --git a/homeassistant/components/switch/deconz.py b/homeassistant/components/switch/deconz.py index 35dbc3ef782..bd8167d89a0 100644 --- a/homeassistant/components/switch/deconz.py +++ b/homeassistant/components/switch/deconz.py @@ -55,6 +55,11 @@ class DeconzSwitch(SwitchDevice): self._switch.register_async_callback(self.async_update_callback) self.hass.data[DATA_DECONZ_ID][self.entity_id] = self._switch.deconz_id + async def async_will_remove_from_hass(self) -> None: + """Disconnect switch object when removed.""" + self._switch.remove_callback(self.async_update_callback) + self._switch = None + @callback def async_update_callback(self, reason): """Update the switch's state.""" diff --git a/requirements_all.txt b/requirements_all.txt index c788a242834..54d902f4260 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -820,7 +820,7 @@ pycsspeechtts==1.0.2 pydaikin==0.4 # homeassistant.components.deconz -pydeconz==44 +pydeconz==45 # homeassistant.components.zwave pydispatcher==2.0.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8e10876cc06..e0d1596db3f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -139,7 +139,7 @@ py-canary==0.5.0 pyblackbird==0.5 # homeassistant.components.deconz -pydeconz==44 +pydeconz==45 # homeassistant.components.zwave pydispatcher==2.0.5 -- GitLab