From 78a3f259d6eac2e080ebab3daf59a7e78f99c77e Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Wed, 3 May 2017 21:26:04 +0200 Subject: [PATCH] LIFX: handle unavailable lights gracefully Recent aiolifx allow sending messages to unregistered devices (as a no-op). This is handy because bulbs can disappear anytime we yield and constantly testing for availability is both error-prone and annoying. So keep the aiolifx device around until a new one registers on the same mac_addr. --- .../components/light/lifx/__init__.py | 19 +++++++------ .../components/light/lifx/effects.py | 27 +++++++++---------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/light/lifx/__init__.py b/homeassistant/components/light/lifx/__init__.py index 01038814f51..f13934011e9 100644 --- a/homeassistant/components/light/lifx/__init__.py +++ b/homeassistant/components/light/lifx/__init__.py @@ -93,6 +93,7 @@ class LIFXManager(object): if device.mac_addr in self.entities: entity = self.entities[device.mac_addr] entity.device = device + entity.registered = True _LOGGER.debug("%s register AGAIN", entity.who) self.hass.async_add_job(entity.async_update_ha_state()) else: @@ -118,7 +119,7 @@ class LIFXManager(object): if device.mac_addr in self.entities: entity = self.entities[device.mac_addr] _LOGGER.debug("%s unregister", entity.who) - entity.device = None + entity.registered = False self.hass.async_add_job(entity.async_update_ha_state()) @@ -172,6 +173,7 @@ class LIFXLight(Light): def __init__(self, device): """Initialize the light.""" self.device = device + self.registered = True self.product = device.product self.blocker = None self.effect_data = None @@ -183,7 +185,7 @@ class LIFXLight(Light): @property def available(self): """Return the availability of the device.""" - return self.device is not None + return self.registered @property def name(self): @@ -345,7 +347,7 @@ class LIFXLight(Light): def async_update(self): """Update bulb status (if it is available).""" _LOGGER.debug("%s async_update", self.who) - if self.available and self.blocker is None: + if self.blocker is None: yield from self.refresh_state() @asyncio.coroutine @@ -357,11 +359,12 @@ class LIFXLight(Light): @asyncio.coroutine def refresh_state(self): """Ask the device about its current state and update our copy.""" - msg = yield from AwaitAioLIFX(self).wait(self.device.get_color) - if msg is not None: - self.set_power(self.device.power_level) - self.set_color(*self.device.color) - self._name = self.device.label + if self.available: + msg = yield from AwaitAioLIFX(self).wait(self.device.get_color) + if msg is not None: + self.set_power(self.device.power_level) + self.set_color(*self.device.color) + self._name = self.device.label def find_hsbk(self, **kwargs): """Find the desired color from a number of possible inputs.""" diff --git a/homeassistant/components/light/lifx/effects.py b/homeassistant/components/light/lifx/effects.py index 2dc56443723..a15360df33e 100644 --- a/homeassistant/components/light/lifx/effects.py +++ b/homeassistant/components/light/lifx/effects.py @@ -176,18 +176,16 @@ class LIFXEffect(object): def async_setup(self, **kwargs): """Prepare all lights for the effect.""" for light in self.lights: + # Remember the current state (as far as we know it) yield from light.refresh_state() - if not light.device: - self.lights.remove(light) - else: - light.effect_data = LIFXEffectData( - self, light.is_on, light.device.color) + light.effect_data = LIFXEffectData( + self, light.is_on, light.device.color) - # Temporarily turn on power for the effect to be visible - if kwargs[ATTR_POWER_ON] and not light.is_on: - hsbk = self.from_poweroff_hsbk(light, **kwargs) - light.device.set_color(hsbk) - light.device.set_power(True) + # Temporarily turn on power for the effect to be visible + if kwargs[ATTR_POWER_ON] and not light.is_on: + hsbk = self.from_poweroff_hsbk(light, **kwargs) + light.device.set_color(hsbk) + light.device.set_power(True) # pylint: disable=no-self-use @asyncio.coroutine @@ -202,12 +200,13 @@ class LIFXEffect(object): self.lights.remove(light) if light.effect_data and light.effect_data.effect == self: - if light.device and not light.effect_data.power: + if not light.effect_data.power: light.device.set_power(False) yield from asyncio.sleep(0.5) - if light.device: - light.device.set_color(light.effect_data.color) - yield from asyncio.sleep(0.5) + + light.device.set_color(light.effect_data.color) + yield from asyncio.sleep(0.5) + light.effect_data = None yield from light.refresh_state()