From fee89d8d16cd1b67842a888b10b86f100867dc1e Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Sun, 20 Aug 2017 20:29:54 +0200 Subject: [PATCH] LIFX: avoid rare NoneType errors (#9054) * Get full multizone state during registration We used to rely on the periodic update to get the state of each zone, only establishing the number of zones during registration. This resulted in errors if the current state was needed for a partial color change before the first async_update happened. Now we do a full update before adding the light. Thus async_update can no longer assume device.color_zones to be defined and must instead use the response message to decide the total number of zones. * Insist on getting the initial state If a response to the initial state query is lost we used to just carry on. This resulted in type errors when we next tried to access the undefined state. After this commit the light is not added before we have the full state. This scenario mostly happens when something is misbehaving and the type errors were actually useful in figuring out what happend. So an error message is logged in their place. * Remove lint --- homeassistant/components/light/lifx.py | 53 +++++++++++++++----------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/light/lifx.py b/homeassistant/components/light/lifx.py index a7301c24a88..6b57a1c5146 100644 --- a/homeassistant/components/light/lifx.py +++ b/homeassistant/components/light/lifx.py @@ -325,29 +325,33 @@ class LIFXManager(object): entity = self.entities[device.mac_addr] entity.registered = True _LOGGER.debug("%s register AGAIN", entity.who) - yield from entity.async_update() - yield from entity.async_update_ha_state() + yield from entity.update_hass() else: _LOGGER.debug("%s register NEW", device.ip_addr) - device.timeout = MESSAGE_TIMEOUT - device.retry_count = MESSAGE_RETRIES - device.unregister_timeout = UNAVAILABLE_GRACE + # Read initial state ack = AwaitAioLIFX().wait - yield from ack(device.get_version) - yield from ack(device.get_color) + version_resp = yield from ack(device.get_version) + if version_resp: + color_resp = yield from ack(device.get_color) - if lifxwhite(device): - entity = LIFXWhite(device, self.effects_conductor) - elif lifxmultizone(device): - yield from ack(partial(device.get_color_zones, start_index=0)) - entity = LIFXStrip(device, self.effects_conductor) + if version_resp is None or color_resp is None: + _LOGGER.error("Failed to initialize %s", device.ip_addr) else: - entity = LIFXColor(device, self.effects_conductor) + device.timeout = MESSAGE_TIMEOUT + device.retry_count = MESSAGE_RETRIES + device.unregister_timeout = UNAVAILABLE_GRACE - _LOGGER.debug("%s register READY", entity.who) - self.entities[device.mac_addr] = entity - self.async_add_devices([entity]) + if lifxwhite(device): + entity = LIFXWhite(device, self.effects_conductor) + elif lifxmultizone(device): + entity = LIFXStrip(device, self.effects_conductor) + else: + entity = LIFXColor(device, self.effects_conductor) + + _LOGGER.debug("%s register READY", entity.who) + self.entities[device.mac_addr] = entity + self.async_add_devices([entity], True) @callback def unregister(self, device): @@ -674,9 +678,14 @@ class LIFXStrip(LIFXColor): @asyncio.coroutine def update_color_zones(self): """Get updated color information for each zone.""" - ack = AwaitAioLIFX().wait - bulb = self.device - - # Each get_color_zones returns the next 8 zones - for zone in range(0, len(bulb.color_zones), 8): - yield from ack(partial(bulb.get_color_zones, start_index=zone)) + zone = 0 + top = 1 + while self.available and zone < top: + # Each get_color_zones can update 8 zones at once + resp = yield from AwaitAioLIFX().wait(partial( + self.device.get_color_zones, + start_index=zone, + end_index=zone+7)) + if resp: + zone += 8 + top = resp.count