diff --git a/aiolifx_effects/aiolifx_effects.py b/aiolifx_effects/aiolifx_effects.py index 97a25b4..96ecd05 100644 --- a/aiolifx_effects/aiolifx_effects.py +++ b/aiolifx_effects/aiolifx_effects.py @@ -1,5 +1,7 @@ import asyncio import random +import logging +from aiolifx.aiolifx import features_map from functools import partial @@ -9,8 +11,18 @@ NEUTRAL_WHITE = 3500 +_LOGGER = logging.getLogger(__name__) + def lifx_white(device): - return device.product and device.product in [10, 11, 18] + """Return true if the device supports neither color or variable temperature and is not a switch.""" + return bool( + features_map[device.product]["color"] is False + and features_map[device.product]["temperature_range"] is None + ) + +def extended_multizone(device): + """Return try if the device supports extended multizone messages.""" + return features_map[device.product]["extended_multizone"] class PreState: """Structure describing a power/color state.""" @@ -75,6 +87,7 @@ async def start(self, effect, participants): async with self.lock: effect.conductor = self + _LOGGER.debug(f"Starting {effect} on {', '.join(device.label for device in participants)}") # Restore previous state await self._stop_nolock(participants, effect) @@ -83,10 +96,16 @@ async def start(self, effect, participants): tasks = [] for device in participants: if not self.running.get(device.mac_addr): + _LOGGER.debug(f"Storing current light state for {device.label}") tasks.append(AwaitAioLIFX().wait(device.get_color)) if device.color_zones: - for zone in range(0, len(device.color_zones), 8): - tasks.append(AwaitAioLIFX().wait(partial(device.get_color_zones, start_index=zone))) + if extended_multizone(device): + _LOGGER.debug(f"Storing current zone state for {device.label} using get_extended_color_zones") + tasks.append(AwaitAioLIFX().wait(device.get_extended_color_zones)) + else: + _LOGGER.debug(f"Storing current zone state for {device.label} using get_color_zones") + for zone in range(0, len(device.color_zones), 8): + tasks.append(AwaitAioLIFX().wait(partial(device.get_color_zones, start_index=zone))) if tasks: await asyncio.wait(tasks) @@ -95,8 +114,9 @@ async def start(self, effect, participants): pre_state = running.pre_state if running else PreState(device) self.running[device.mac_addr] = RunningEffect(effect, pre_state) - # Powered off zones report zero brightness. Get the real values. - await self._fixup_multizone(participants) + # Powered off zones report zero brightness on older multizone devices. Get the real values. + if extended_multizone(device) is False: + await self._fixup_multizone(participants) self.loop.create_task(effect.async_perform(participants)) @@ -196,6 +216,7 @@ async def async_perform(self, participants): tasks = [] for device in self.participants: if self.power_on and not device.power_level: + _LOGGER.debug(f"Powering on {device.label} before starting effect.") tasks.append(self.conductor.loop.create_task(self.poweron(device))) if tasks: await asyncio.wait(tasks) @@ -321,7 +342,7 @@ async def effect_color(self, device): class EffectColorloop(LIFXEffect): """Representation of a colorloop effect.""" - def __init__(self, power_on=True, period=None, change=None, spread=None, brightness=None, transition=None): + def __init__(self, power_on=True, period=None, change=None, spread=None, brightness=None, saturation=None, transition=None): """Initialize the colorloop effect.""" super().__init__(power_on) self.name = 'colorloop' @@ -330,6 +351,7 @@ def __init__(self, power_on=True, period=None, change=None, spread=None, brightn self.change = change if change else 20 self.spread = spread if spread else 30 self.brightness = brightness + self.saturation = saturation self.transition = transition def inherit_prestate(self, other): @@ -345,10 +367,11 @@ async def async_play(self, **kwargs): while self.participants: hue = (hue + direction*self.change) % 360 lhue = hue - + _LOGGER.debug(f"Starting color loop on {', '.join(device.label for device in self.participants)}") random.shuffle(self.participants) for device in self.participants: + if self.transition is not None: transition = int(1000*self.transition) elif device == self.participants[0] or self.spread > 0: @@ -359,13 +382,19 @@ async def async_play(self, **kwargs): else: brightness = self.running(device).pre_state.color[2] + if self.saturation is not None: + saturation = self.saturation + else: + saturation = int(random.uniform(0.8, 1.0)*65535) + hsbk = [ int(65535/360*lhue), - int(random.uniform(0.8, 1.0)*65535), + saturation, brightness, NEUTRAL_WHITE, ] device.set_color(hsbk, None, transition) + _LOGGER.debug(f"Send set_color({int(65535/360*lhue)}, {saturation}, {brightness}, {NEUTRAL_WHITE}, transition={transition}) to {device.label}") # Adjust the next light so the full spread is used if len(self.participants) > 1: