LIFX: add multiple modes to pulse effect (#8016)

* blink: the existing 50/50 flashing between base color and effect color
* breathe: a lifx_effect_breathe replacement
* ping: mostly base color with a short flash at the end of the cycle
* strobe: dark base color and short cycles by default
* solid: temporary color change, base color never visible

Adding a service call for each mode is a bit extravagant so instead
lifx_effect_breathe has been folded in as an option and that service
call is deprecated.
This commit is contained in:
Anders Melchiorsen 2017-06-15 07:59:11 +02:00 committed by Pascal Vizeli
parent ae3973144c
commit 8eb29787a5
2 changed files with 73 additions and 54 deletions

View File

@ -22,9 +22,18 @@ SERVICE_EFFECT_STOP = 'lifx_effect_stop'
ATTR_POWER_ON = 'power_on'
ATTR_PERIOD = 'period'
ATTR_CYCLES = 'cycles'
ATTR_MODE = 'mode'
ATTR_SPREAD = 'spread'
ATTR_CHANGE = 'change'
MODE_BLINK = 'blink'
MODE_BREATHE = 'breathe'
MODE_PING = 'ping'
MODE_STROBE = 'strobe'
MODE_SOLID = 'solid'
MODES = [MODE_BLINK, MODE_BREATHE, MODE_PING, MODE_STROBE, MODE_SOLID]
# aiolifx waveform modes
WAVEFORM_SINE = 1
WAVEFORM_PULSE = 4
@ -44,13 +53,13 @@ LIFX_EFFECT_BREATHE_SCHEMA = LIFX_EFFECT_SCHEMA.extend({
vol.Coerce(tuple)),
ATTR_COLOR_TEMP: vol.All(vol.Coerce(int), vol.Range(min=1)),
ATTR_KELVIN: vol.All(vol.Coerce(int), vol.Range(min=0)),
vol.Optional(ATTR_PERIOD, default=1.0):
vol.All(vol.Coerce(float), vol.Range(min=0.05)),
vol.Optional(ATTR_CYCLES, default=1.0):
vol.All(vol.Coerce(float), vol.Range(min=1)),
ATTR_PERIOD: vol.All(vol.Coerce(float), vol.Range(min=0.05)),
ATTR_CYCLES: vol.All(vol.Coerce(float), vol.Range(min=1)),
})
LIFX_EFFECT_PULSE_SCHEMA = LIFX_EFFECT_BREATHE_SCHEMA
LIFX_EFFECT_PULSE_SCHEMA = LIFX_EFFECT_BREATHE_SCHEMA.extend({
vol.Optional(ATTR_MODE, default=MODE_BLINK): vol.In(MODES),
})
LIFX_EFFECT_COLORLOOP_SCHEMA = LIFX_EFFECT_SCHEMA.extend({
ATTR_BRIGHTNESS: VALID_BRIGHTNESS,
@ -217,14 +226,13 @@ class LIFXEffect(object):
return [random.randint(0, 65535), 65535, 0, NEUTRAL_WHITE]
class LIFXEffectBreathe(LIFXEffect):
"""Representation of a breathe effect."""
class LIFXEffectPulse(LIFXEffect):
"""Representation of a pulse effect."""
def __init__(self, hass, lights):
"""Initialize the breathe effect."""
super(LIFXEffectBreathe, self).__init__(hass, lights)
self.name = SERVICE_EFFECT_BREATHE
self.waveform = WAVEFORM_SINE
"""Initialize the pulse effect."""
super().__init__(hass, lights)
self.name = SERVICE_EFFECT_PULSE
@asyncio.coroutine
def async_play(self, **kwargs):
@ -235,13 +243,42 @@ class LIFXEffectBreathe(LIFXEffect):
@asyncio.coroutine
def async_light_play(self, light, **kwargs):
"""Play a light effect on the bulb."""
period = kwargs[ATTR_PERIOD]
cycles = kwargs[ATTR_CYCLES]
hsbk, color_changed = light.find_hsbk(**kwargs)
if kwargs[ATTR_MODE] == MODE_STROBE:
# Strobe must flash from a dark color
light.device.set_color([0, 0, 0, NEUTRAL_WHITE])
yield from asyncio.sleep(0.1)
default_period = 0.1
default_cycles = 10
else:
default_period = 1.0
default_cycles = 1
period = kwargs.get(ATTR_PERIOD, default_period)
cycles = kwargs.get(ATTR_CYCLES, default_cycles)
# Breathe has a special waveform
if kwargs[ATTR_MODE] == MODE_BREATHE:
waveform = WAVEFORM_SINE
else:
waveform = WAVEFORM_PULSE
# Ping and solid have special duty cycles
if kwargs[ATTR_MODE] == MODE_PING:
ping_duration = int(5000 - min(2500, 300*period))
duty_cycle = 2**15 - ping_duration
elif kwargs[ATTR_MODE] == MODE_SOLID:
duty_cycle = -2**15
else:
duty_cycle = 0
# Set default effect color based on current setting
if not color_changed:
if light.lifxwhite or hsbk[1] < 65536/2:
if kwargs[ATTR_MODE] == MODE_STROBE:
# Strobe: cold white
hsbk = [hsbk[0], 0, 65535, 5600]
elif light.lifxwhite or hsbk[1] < 65536/2:
# White: toggle brightness
hsbk[2] = 65535 if hsbk[2] < 65536/2 else 0
else:
@ -254,8 +291,8 @@ class LIFXEffectBreathe(LIFXEffect):
'color': hsbk,
'period': int(period*1000),
'cycles': cycles,
'duty_cycle': 0,
'waveform': self.waveform,
'duty_cycle': duty_cycle,
'waveform': waveform,
}
light.device.set_waveform(args)
@ -269,14 +306,21 @@ class LIFXEffectBreathe(LIFXEffect):
return [hsbk[0], hsbk[1], 0, hsbk[2]]
class LIFXEffectPulse(LIFXEffectBreathe):
"""Representation of a pulse effect."""
class LIFXEffectBreathe(LIFXEffectPulse):
"""Representation of a breathe effect."""
def __init__(self, hass, lights):
"""Initialize the pulse effect."""
super(LIFXEffectPulse, self).__init__(hass, lights)
self.name = SERVICE_EFFECT_PULSE
self.waveform = WAVEFORM_PULSE
"""Initialize the breathe effect."""
super().__init__(hass, lights)
self.name = SERVICE_EFFECT_BREATHE
_LOGGER.warning("'lifx_effect_breathe' is deprecated. Please use "
"'lifx_effect_pulse' with 'mode: breathe'")
@asyncio.coroutine
def async_perform(self, **kwargs):
"""Prepare all lights for the effect."""
kwargs[ATTR_MODE] = MODE_BREATHE
yield from super().async_perform(**kwargs)
class LIFXEffectColorloop(LIFXEffect):
@ -284,7 +328,7 @@ class LIFXEffectColorloop(LIFXEffect):
def __init__(self, hass, lights):
"""Initialize the colorloop effect."""
super(LIFXEffectColorloop, self).__init__(hass, lights)
super().__init__(hass, lights)
self.name = SERVICE_EFFECT_COLORLOOP
@asyncio.coroutine
@ -335,7 +379,7 @@ class LIFXEffectStop(LIFXEffect):
def __init__(self, hass, lights):
"""Initialize the stop effect."""
super(LIFXEffectStop, self).__init__(hass, lights)
super().__init__(hass, lights)
self.name = SERVICE_EFFECT_STOP
@asyncio.coroutine

View File

@ -23,36 +23,7 @@ lifx_set_state:
lifx_effect_breathe:
description: Run a breathe effect by fading to a color and back.
fields:
entity_id:
description: Name(s) of entities to run the effect on
example: 'light.kitchen'
brightness:
description: Number between 0..255 indicating brightness when the effect peaks
example: 120
color_name:
description: A human readable color name
example: 'red'
rgb_color:
description: Color for the fade in RGB-format
example: '[255, 100, 100]'
period:
description: Duration of the effect in seconds (default 1.0)
example: 3
cycles:
description: Number of times the effect should run (default 1.0)
example: 2
power_on:
description: Powered off lights are temporarily turned on during the effect (default True)
example: False
description: Deprecated, use lifx_effect_pulse
lifx_effect_pulse:
description: Run a flash effect by changing to a color and back.
@ -62,6 +33,10 @@ lifx_effect_pulse:
description: Name(s) of entities to run the effect on
example: 'light.kitchen'
mode:
description: 'Decides how colors are changed. Possible values: blink, breathe, ping, strobe, solid'
example: strobe
brightness:
description: Number between 0..255 indicating brightness of the temporary color
example: 120