Fix so that entities are properly unloaded with config entry (#16281)

This commit is contained in:
Robert Svensson 2018-08-29 23:18:20 +02:00 committed by Paulus Schoutsen
parent 88f72a654a
commit 645c3a67d8
8 changed files with 62 additions and 20 deletions

View file

@ -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.

View file

@ -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."""

View file

@ -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."""

View file

@ -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({})

View file

@ -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,
}

View file

@ -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."""

View file

@ -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

View file

@ -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