Fix LIFX effects (#16309)

This commit is contained in:
Anders Melchiorsen 2018-08-31 10:17:11 +02:00 committed by Paulus Schoutsen
parent 8be7a0a9b9
commit 16a58bd1cf
2 changed files with 64 additions and 64 deletions

View file

@ -52,6 +52,8 @@ homeassistant/components/cover/template.py @PhracturedBlue
homeassistant/components/device_tracker/automatic.py @armills homeassistant/components/device_tracker/automatic.py @armills
homeassistant/components/device_tracker/tile.py @bachya homeassistant/components/device_tracker/tile.py @bachya
homeassistant/components/history_graph.py @andrey-git homeassistant/components/history_graph.py @andrey-git
homeassistant/components/light/lifx.py @amelchio
homeassistant/components/light/lifx_legacy.py @amelchio
homeassistant/components/light/tplink.py @rytilahti homeassistant/components/light/tplink.py @rytilahti
homeassistant/components/light/yeelight.py @rytilahti homeassistant/components/light/yeelight.py @rytilahti
homeassistant/components/lock/nello.py @pschmitt homeassistant/components/lock/nello.py @pschmitt
@ -65,6 +67,7 @@ homeassistant/components/media_player/sonos.py @amelchio
homeassistant/components/media_player/xiaomi_tv.py @fattdev homeassistant/components/media_player/xiaomi_tv.py @fattdev
homeassistant/components/media_player/yamaha_musiccast.py @jalmeroth homeassistant/components/media_player/yamaha_musiccast.py @jalmeroth
homeassistant/components/plant.py @ChristianKuehnel homeassistant/components/plant.py @ChristianKuehnel
homeassistant/components/scene/lifx_cloud.py @amelchio
homeassistant/components/sensor/airvisual.py @bachya homeassistant/components/sensor/airvisual.py @bachya
homeassistant/components/sensor/filter.py @dgomes homeassistant/components/sensor/filter.py @dgomes
homeassistant/components/sensor/gearbest.py @HerrHofrat homeassistant/components/sensor/gearbest.py @HerrHofrat

View file

@ -167,9 +167,9 @@ async def async_setup_platform(hass,
return True return True
def lifx_features(device): def lifx_features(bulb):
"""Return a feature map for this device, or a default map if unknown.""" """Return a feature map for this bulb, or a default map if unknown."""
return aiolifx().products.features_map.get(device.product) or \ return aiolifx().products.features_map.get(bulb.product) or \
aiolifx().products.features_map.get(1) aiolifx().products.features_map.get(1)
@ -256,7 +256,7 @@ class LIFXManager:
async def start_effect(self, entities, service, **kwargs): async def start_effect(self, entities, service, **kwargs):
"""Start a light effect on entities.""" """Start a light effect on entities."""
devices = [light.device for light in entities] bulbs = [light.bulb for light in entities]
if service == SERVICE_EFFECT_PULSE: if service == SERVICE_EFFECT_PULSE:
effect = aiolifx_effects().EffectPulse( effect = aiolifx_effects().EffectPulse(
@ -266,7 +266,7 @@ class LIFXManager:
mode=kwargs.get(ATTR_MODE), mode=kwargs.get(ATTR_MODE),
hsbk=find_hsbk(**kwargs), hsbk=find_hsbk(**kwargs),
) )
await self.effects_conductor.start(effect, devices) await self.effects_conductor.start(effect, bulbs)
elif service == SERVICE_EFFECT_COLORLOOP: elif service == SERVICE_EFFECT_COLORLOOP:
preprocess_turn_on_alternatives(kwargs) preprocess_turn_on_alternatives(kwargs)
@ -282,12 +282,12 @@ class LIFXManager:
transition=kwargs.get(ATTR_TRANSITION), transition=kwargs.get(ATTR_TRANSITION),
brightness=brightness, brightness=brightness,
) )
await self.effects_conductor.start(effect, devices) await self.effects_conductor.start(effect, bulbs)
elif service == SERVICE_EFFECT_STOP: elif service == SERVICE_EFFECT_STOP:
await self.effects_conductor.stop(devices) await self.effects_conductor.stop(bulbs)
def service_to_entities(self, service): def service_to_entities(self, service):
"""Return the known devices that a service call mentions.""" """Return the known entities that a service call mentions."""
entity_ids = extract_entity_ids(self.hass, service) entity_ids = extract_entity_ids(self.hass, service)
if entity_ids: if entity_ids:
entities = [entity for entity in self.entities.values() entities = [entity for entity in self.entities.values()
@ -298,50 +298,50 @@ class LIFXManager:
return entities return entities
@callback @callback
def register(self, device): def register(self, bulb):
"""Handle aiolifx detected bulb.""" """Handle aiolifx detected bulb."""
self.hass.async_add_job(self.register_new_device(device)) self.hass.async_add_job(self.register_new_bulb(bulb))
async def register_new_device(self, device): async def register_new_bulb(self, bulb):
"""Handle newly detected bulb.""" """Handle newly detected bulb."""
if device.mac_addr in self.entities: if bulb.mac_addr in self.entities:
entity = self.entities[device.mac_addr] entity = self.entities[bulb.mac_addr]
entity.registered = True entity.registered = True
_LOGGER.debug("%s register AGAIN", entity.who) _LOGGER.debug("%s register AGAIN", entity.who)
await entity.update_hass() await entity.update_hass()
else: else:
_LOGGER.debug("%s register NEW", device.ip_addr) _LOGGER.debug("%s register NEW", bulb.ip_addr)
# Read initial state # Read initial state
ack = AwaitAioLIFX().wait ack = AwaitAioLIFX().wait
color_resp = await ack(device.get_color) color_resp = await ack(bulb.get_color)
if color_resp: if color_resp:
version_resp = await ack(device.get_version) version_resp = await ack(bulb.get_version)
if color_resp is None or version_resp is None: if color_resp is None or version_resp is None:
_LOGGER.error("Failed to initialize %s", device.ip_addr) _LOGGER.error("Failed to initialize %s", bulb.ip_addr)
device.registered = False bulb.registered = False
else: else:
device.timeout = MESSAGE_TIMEOUT bulb.timeout = MESSAGE_TIMEOUT
device.retry_count = MESSAGE_RETRIES bulb.retry_count = MESSAGE_RETRIES
device.unregister_timeout = UNAVAILABLE_GRACE bulb.unregister_timeout = UNAVAILABLE_GRACE
if lifx_features(device)["multizone"]: if lifx_features(bulb)["multizone"]:
entity = LIFXStrip(device, self.effects_conductor) entity = LIFXStrip(bulb, self.effects_conductor)
elif lifx_features(device)["color"]: elif lifx_features(bulb)["color"]:
entity = LIFXColor(device, self.effects_conductor) entity = LIFXColor(bulb, self.effects_conductor)
else: else:
entity = LIFXWhite(device, self.effects_conductor) entity = LIFXWhite(bulb, self.effects_conductor)
_LOGGER.debug("%s register READY", entity.who) _LOGGER.debug("%s register READY", entity.who)
self.entities[device.mac_addr] = entity self.entities[bulb.mac_addr] = entity
self.async_add_entities([entity], True) self.async_add_entities([entity], True)
@callback @callback
def unregister(self, device): def unregister(self, bulb):
"""Handle aiolifx disappearing bulbs.""" """Handle aiolifx disappearing bulbs."""
if device.mac_addr in self.entities: if bulb.mac_addr in self.entities:
entity = self.entities[device.mac_addr] entity = self.entities[bulb.mac_addr]
_LOGGER.debug("%s unregister", entity.who) _LOGGER.debug("%s unregister", entity.who)
entity.registered = False entity.registered = False
self.hass.async_add_job(entity.async_update_ha_state()) self.hass.async_add_job(entity.async_update_ha_state())
@ -352,20 +352,17 @@ class AwaitAioLIFX:
def __init__(self): def __init__(self):
"""Initialize the wrapper.""" """Initialize the wrapper."""
self.device = None
self.message = None self.message = None
self.event = asyncio.Event() self.event = asyncio.Event()
@callback @callback
def callback(self, device, message): def callback(self, bulb, message):
"""Handle responses.""" """Handle responses."""
self.device = device
self.message = message self.message = message
self.event.set() self.event.set()
async def wait(self, method): async def wait(self, method):
"""Call an aiolifx method and wait for its response.""" """Call an aiolifx method and wait for its response."""
self.device = None
self.message = None self.message = None
self.event.clear() self.event.clear()
method(callb=self.callback) method(callb=self.callback)
@ -387,9 +384,9 @@ def convert_16_to_8(value):
class LIFXLight(Light): class LIFXLight(Light):
"""Representation of a LIFX light.""" """Representation of a LIFX light."""
def __init__(self, device, effects_conductor): def __init__(self, bulb, effects_conductor):
"""Initialize the light.""" """Initialize the light."""
self.light = device self.bulb = bulb
self.effects_conductor = effects_conductor self.effects_conductor = effects_conductor
self.registered = True self.registered = True
self.postponed_update = None self.postponed_update = None
@ -397,34 +394,34 @@ class LIFXLight(Light):
@property @property
def available(self): def available(self):
"""Return the availability of the device.""" """Return the availability of the bulb."""
return self.registered return self.registered
@property @property
def unique_id(self): def unique_id(self):
"""Return a unique ID.""" """Return a unique ID."""
return self.light.mac_addr return self.bulb.mac_addr
@property @property
def name(self): def name(self):
"""Return the name of the device.""" """Return the name of the bulb."""
return self.light.label return self.bulb.label
@property @property
def who(self): def who(self):
"""Return a string identifying the device.""" """Return a string identifying the bulb."""
return "%s (%s)" % (self.light.ip_addr, self.name) return "%s (%s)" % (self.bulb.ip_addr, self.name)
@property @property
def min_mireds(self): def min_mireds(self):
"""Return the coldest color_temp that this light supports.""" """Return the coldest color_temp that this light supports."""
kelvin = lifx_features(self.light)['max_kelvin'] kelvin = lifx_features(self.bulb)['max_kelvin']
return math.floor(color_util.color_temperature_kelvin_to_mired(kelvin)) return math.floor(color_util.color_temperature_kelvin_to_mired(kelvin))
@property @property
def max_mireds(self): def max_mireds(self):
"""Return the warmest color_temp that this light supports.""" """Return the warmest color_temp that this light supports."""
kelvin = lifx_features(self.light)['min_kelvin'] kelvin = lifx_features(self.bulb)['min_kelvin']
return math.ceil(color_util.color_temperature_kelvin_to_mired(kelvin)) return math.ceil(color_util.color_temperature_kelvin_to_mired(kelvin))
@property @property
@ -432,8 +429,8 @@ class LIFXLight(Light):
"""Flag supported features.""" """Flag supported features."""
support = SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION | SUPPORT_EFFECT support = SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION | SUPPORT_EFFECT
device_features = lifx_features(self.light) bulb_features = lifx_features(self.bulb)
if device_features['min_kelvin'] != device_features['max_kelvin']: if bulb_features['min_kelvin'] != bulb_features['max_kelvin']:
support |= SUPPORT_COLOR_TEMP support |= SUPPORT_COLOR_TEMP
return support return support
@ -441,25 +438,25 @@ class LIFXLight(Light):
@property @property
def brightness(self): def brightness(self):
"""Return the brightness of this light between 0..255.""" """Return the brightness of this light between 0..255."""
return convert_16_to_8(self.light.color[2]) return convert_16_to_8(self.bulb.color[2])
@property @property
def color_temp(self): def color_temp(self):
"""Return the color temperature.""" """Return the color temperature."""
_, sat, _, kelvin = self.light.color _, sat, _, kelvin = self.bulb.color
if sat: if sat:
return None return None
return color_util.color_temperature_kelvin_to_mired(kelvin) return color_util.color_temperature_kelvin_to_mired(kelvin)
@property @property
def is_on(self): def is_on(self):
"""Return true if device is on.""" """Return true if light is on."""
return self.light.power_level != 0 return self.bulb.power_level != 0
@property @property
def effect(self): def effect(self):
"""Return the name of the currently running effect.""" """Return the name of the currently running effect."""
effect = self.effects_conductor.effect(self.light) effect = self.effects_conductor.effect(self.bulb)
if effect: if effect:
return 'lifx_effect_' + effect.name return 'lifx_effect_' + effect.name
return None return None
@ -485,19 +482,19 @@ class LIFXLight(Light):
util.dt.utcnow() + timedelta(milliseconds=when)) util.dt.utcnow() + timedelta(milliseconds=when))
async def async_turn_on(self, **kwargs): async def async_turn_on(self, **kwargs):
"""Turn the device on.""" """Turn the light on."""
kwargs[ATTR_POWER] = True kwargs[ATTR_POWER] = True
self.hass.async_add_job(self.set_state(**kwargs)) self.hass.async_add_job(self.set_state(**kwargs))
async def async_turn_off(self, **kwargs): async def async_turn_off(self, **kwargs):
"""Turn the device off.""" """Turn the light off."""
kwargs[ATTR_POWER] = False kwargs[ATTR_POWER] = False
self.hass.async_add_job(self.set_state(**kwargs)) self.hass.async_add_job(self.set_state(**kwargs))
async def set_state(self, **kwargs): async def set_state(self, **kwargs):
"""Set a color on the light and turn it on/off.""" """Set a color on the light and turn it on/off."""
async with self.lock: async with self.lock:
bulb = self.light bulb = self.bulb
await self.effects_conductor.stop([bulb]) await self.effects_conductor.stop([bulb])
@ -544,13 +541,13 @@ class LIFXLight(Light):
await self.update_during_transition(fade) await self.update_during_transition(fade)
async def set_power(self, ack, pwr, duration=0): async def set_power(self, ack, pwr, duration=0):
"""Send a power change to the device.""" """Send a power change to the bulb."""
await ack(partial(self.light.set_power, pwr, duration=duration)) await ack(partial(self.bulb.set_power, pwr, duration=duration))
async def set_color(self, ack, hsbk, kwargs, duration=0): async def set_color(self, ack, hsbk, kwargs, duration=0):
"""Send a color change to the device.""" """Send a color change to the bulb."""
hsbk = merge_hsbk(self.light.color, hsbk) hsbk = merge_hsbk(self.bulb.color, hsbk)
await ack(partial(self.light.set_color, hsbk, duration=duration)) await ack(partial(self.bulb.set_color, hsbk, duration=duration))
async def default_effect(self, **kwargs): async def default_effect(self, **kwargs):
"""Start an effect with default parameters.""" """Start an effect with default parameters."""
@ -563,7 +560,7 @@ class LIFXLight(Light):
async def async_update(self): async def async_update(self):
"""Update bulb status.""" """Update bulb status."""
if self.available and not self.lock.locked(): if self.available and not self.lock.locked():
await AwaitAioLIFX().wait(self.light.get_color) await AwaitAioLIFX().wait(self.bulb.get_color)
class LIFXWhite(LIFXLight): class LIFXWhite(LIFXLight):
@ -600,7 +597,7 @@ class LIFXColor(LIFXLight):
@property @property
def hs_color(self): def hs_color(self):
"""Return the hs value.""" """Return the hs value."""
hue, sat, _, _ = self.light.color hue, sat, _, _ = self.bulb.color
hue = hue / 65535 * 360 hue = hue / 65535 * 360
sat = sat / 65535 * 100 sat = sat / 65535 * 100
return (hue, sat) if sat else None return (hue, sat) if sat else None
@ -610,8 +607,8 @@ class LIFXStrip(LIFXColor):
"""Representation of a LIFX light strip with multiple zones.""" """Representation of a LIFX light strip with multiple zones."""
async def set_color(self, ack, hsbk, kwargs, duration=0): async def set_color(self, ack, hsbk, kwargs, duration=0):
"""Send a color change to the device.""" """Send a color change to the bulb."""
bulb = self.light bulb = self.bulb
num_zones = len(bulb.color_zones) num_zones = len(bulb.color_zones)
zones = kwargs.get(ATTR_ZONES) zones = kwargs.get(ATTR_ZONES)
@ -659,7 +656,7 @@ class LIFXStrip(LIFXColor):
while self.available and zone < top: while self.available and zone < top:
# Each get_color_zones can update 8 zones at once # Each get_color_zones can update 8 zones at once
resp = await AwaitAioLIFX().wait(partial( resp = await AwaitAioLIFX().wait(partial(
self.light.get_color_zones, self.bulb.get_color_zones,
start_index=zone)) start_index=zone))
if resp: if resp:
zone += 8 zone += 8