1 """Support for LIFX lights."""
3 from __future__
import annotations
6 from datetime
import datetime, timedelta
9 import aiolifx_effects
as aiolifx_effects_module
10 import voluptuous
as vol
39 LIFX_CEILING_PRODUCT_IDS,
41 from .coordinator
import FirmwareEffect, LIFXUpdateCoordinator
42 from .entity
import LIFXEntity
43 from .manager
import (
44 SERVICE_EFFECT_COLORLOOP,
53 from .util
import convert_8_to_16, convert_16_to_8, find_hsbk, lifx_features, merge_hsbk
55 LIFX_STATE_SETTLE_DELAY = 0.3
57 SERVICE_LIFX_SET_STATE =
"set_state"
59 LIFX_SET_STATE_SCHEMA: VolDictType = {
60 **LIGHT_TURN_ON_SCHEMA,
61 ATTR_INFRARED: vol.All(vol.Coerce(int), vol.Clamp(min=0, max=255)),
62 ATTR_ZONES: vol.All(cv.ensure_list, [cv.positive_int]),
63 ATTR_POWER: cv.boolean,
67 SERVICE_LIFX_SET_HEV_CYCLE_STATE =
"set_hev_cycle_state"
69 LIFX_SET_HEV_CYCLE_STATE_SCHEMA: VolDictType = {
70 ATTR_POWER: vol.Required(cv.boolean),
71 ATTR_DURATION: vol.All(vol.Coerce(float), vol.Clamp(min=0, max=86400)),
83 async_add_entities: AddEntitiesCallback,
85 """Set up LIFX from a config entry."""
86 domain_data = hass.data[DOMAIN]
87 coordinator: LIFXUpdateCoordinator = domain_data[entry.entry_id]
88 manager: LIFXManager = domain_data[DATA_LIFX_MANAGER]
89 device = coordinator.device
90 platform = entity_platform.async_get_current_platform()
91 platform.async_register_entity_service(
92 SERVICE_LIFX_SET_STATE,
93 LIFX_SET_STATE_SCHEMA,
96 platform.async_register_entity_service(
97 SERVICE_LIFX_SET_HEV_CYCLE_STATE,
98 LIFX_SET_HEV_CYCLE_STATE_SCHEMA,
99 "set_hev_cycle_state",
102 if device.product
in LIFX_CEILING_PRODUCT_IDS:
103 entity: LIFXLight =
LIFXCeiling(coordinator, manager, entry)
105 entity =
LIFXMatrix(coordinator, manager, entry)
111 entity =
LIFXColor(coordinator, manager, entry)
113 entity =
LIFXWhite(coordinator, manager, entry)
118 """Representation of a LIFX light."""
120 _attr_supported_features = LightEntityFeature.TRANSITION | LightEntityFeature.EFFECT
125 coordinator: LIFXUpdateCoordinator,
126 manager: LIFXManager,
129 """Initialize the light."""
135 self.effects_conductor: aiolifx_effects_module.Conductor = (
136 manager.effects_conductor
143 if bulb_features[
"min_kelvin"] != bulb_features[
"max_kelvin"]:
144 color_mode = ColorMode.COLOR_TEMP
146 color_mode = ColorMode.BRIGHTNESS
154 """Return the brightness of this light between 0..255."""
155 fade = self.
bulbbulb.power_level / 65535
160 """Return the color temperature of this light in kelvin."""
161 return int(self.
bulbbulb.color[HSBK_KELVIN])
165 """Return true if light is on."""
166 return bool(self.
bulbbulb.power_level != 0)
170 """Return the name of the currently running effect."""
171 if effect := self.effects_conductor.
effect(self.
bulbbulb):
172 return f
"effect_{effect.name}"
174 return f
"effect_{FirmwareEffect(effect).name.lower()}"
178 """Update state at the start and end of a transition."""
193 """Refresh the state."""
203 """Turn the light on."""
204 await self.
set_stateset_state(**{**kwargs, ATTR_POWER:
True})
207 """Turn the light off."""
208 await self.
set_stateset_state(**{**kwargs, ATTR_POWER:
False})
211 """Set a color on the light and turn it on/off."""
216 await self.effects_conductor.stop([bulb])
218 if ATTR_EFFECT
in kwargs:
222 if ATTR_INFRARED
in kwargs:
224 Platform.SELECT, INFRARED_BRIGHTNESS
228 "The 'infrared' attribute of 'lifx.set_state' is deprecated:"
229 " call 'select.select_option' targeting '%s' instead"
235 if ATTR_TRANSITION
in kwargs:
236 fade =
int(kwargs[ATTR_TRANSITION] * 1000)
241 power_on = kwargs.get(ATTR_POWER,
False)
242 power_off =
not kwargs.get(ATTR_POWER,
True)
250 if hsbk
and power_on:
251 await self.
set_colorset_color(hsbk, kwargs)
252 await self.
set_powerset_power(
True, duration=fade)
254 await self.
set_colorset_color(hsbk, kwargs, duration=fade)
256 await self.
set_powerset_power(
True, duration=fade)
261 await self.
set_colorset_color(hsbk, kwargs, duration=fade)
263 await self.
set_powerset_power(
False, duration=fade)
266 await asyncio.sleep(LIFX_STATE_SETTLE_DELAY)
272 self, power: bool, duration: int |
None =
None
274 """Set the state of the HEV LEDs on a LIFX Clean bulb."""
277 "This device does not support setting HEV cycle state"
288 """Send a power change to the bulb."""
291 except TimeoutError
as ex:
296 hsbk: list[float | int |
None],
297 kwargs: dict[str, Any],
300 """Send a color change to the bulb."""
304 except TimeoutError
as ex:
310 """Send a get color message to the bulb."""
313 except TimeoutError
as ex:
315 f
"Timeout setting getting color for {self.name}"
319 """Start an effect with default parameters."""
320 await self.
hasshasshass.services.async_call(
323 {ATTR_ENTITY_ID: self.
entity_identity_id},
328 """Register callbacks."""
335 """Cancel postponed update, if applicable."""
341 """Run when entity will be removed from hass."""
347 """Representation of a white-only LIFX light."""
349 _attr_effect_list = [SERVICE_EFFECT_PULSE, SERVICE_EFFECT_STOP]
353 """Representation of a color LIFX light."""
355 _attr_effect_list = [
356 SERVICE_EFFECT_COLORLOOP,
357 SERVICE_EFFECT_PULSE,
363 """Return the supported color modes."""
364 return {ColorMode.COLOR_TEMP, ColorMode.HS}
368 """Return the color mode of the light."""
369 has_sat = self.
bulbbulb.color[HSBK_SATURATION]
370 return ColorMode.HS
if has_sat
else ColorMode.COLOR_TEMP
374 """Return the hs value."""
375 hue, sat, _, _ = self.
bulbbulb.color
376 hue = hue / 65535 * 360
377 sat = sat / 65535 * 100
378 return (hue, sat)
if sat
else None
382 """Representation of a legacy LIFX multizone device."""
384 _attr_effect_list = [
385 SERVICE_EFFECT_COLORLOOP,
386 SERVICE_EFFECT_PULSE,
393 hsbk: list[float | int |
None],
394 kwargs: dict[str, Any],
397 """Send a color change to the bulb."""
399 color_zones = bulb.color_zones
403 if not self.
is_onis_onis_on
and hsbk[HSBK_BRIGHTNESS]
is None:
405 await asyncio.sleep(LIFX_STATE_SETTLE_DELAY)
409 if (zones := kwargs.get(ATTR_ZONES))
is None:
412 first_zone = color_zones[0]
413 first_zone_brightness = first_zone[HSBK_BRIGHTNESS]
414 all_zones_have_same_brightness = all(
415 color_zones[zone][HSBK_BRIGHTNESS] == first_zone_brightness
416 for zone
in range(num_zones)
418 all_zones_are_the_same = all(
419 color_zones[zone] == first_zone
for zone
in range(num_zones)
422 all_zones_have_same_brightness
or hsbk[HSBK_BRIGHTNESS]
is not None
423 )
and (all_zones_are_the_same
or hsbk[HSBK_KELVIN]
is not None):
424 await super().
set_color(hsbk, kwargs, duration)
427 zones =
list(range(num_zones))
429 zones = [x
for x
in set(zones)
if x < num_zones]
432 for index, zone
in enumerate(zones):
433 zone_hsbk =
merge_hsbk(color_zones[zone], hsbk)
434 apply = 1
if (index == len(zones) - 1)
else 0
437 zone, zone, zone_hsbk, duration, apply
439 except TimeoutError
as ex:
441 f
"Timeout setting color zones for {self.name}"
451 """Send a get color zones message to the device."""
454 except TimeoutError
as ex:
456 f
"Timeout getting color zones from {self.name}"
461 """Representation of a LIFX device that supports extended multizone messages."""
464 self, hsbk: list[float | int |
None], kwargs: dict[str, Any], duration: int = 0
466 """Set colors on all zones of the device."""
471 color_zones = self.
bulbbulb.color_zones
472 if (zones := kwargs.get(ATTR_ZONES))
is None:
474 for index, zone
in enumerate(color_zones):
478 for index, zone
in enumerate(color_zones):
485 color_zones, duration=duration
487 except TimeoutError
as ex:
489 f
"Timeout setting color zones on {self.name}"
498 """Representation of a LIFX matrix device."""
500 _attr_effect_list = [
501 SERVICE_EFFECT_COLORLOOP,
502 SERVICE_EFFECT_FLAME,
503 SERVICE_EFFECT_PULSE,
504 SERVICE_EFFECT_MORPH,
510 """Representation of a LIFX Ceiling device."""
512 _attr_effect_list = [
513 SERVICE_EFFECT_COLORLOOP,
514 SERVICE_EFFECT_FLAME,
515 SERVICE_EFFECT_PULSE,
516 SERVICE_EFFECT_MORPH,
None async_get_color(self)
str|None async_get_entity_id(self, Platform platform, str key)
None async_set_color_zones(self, int start_index, int end_index, list[float|int|None] hsbk, int|None duration, int apply)
int async_get_active_effect(self)
None async_get_extended_color_zones(self)
None async_set_extended_color_zones(self, list[tuple[int|float, int|float, int|float, int|float]] colors, int|None colors_count=None, int duration=0, int apply=1)
None async_set_color(self, list[float|int|None] hsbk, int|None duration)
None async_set_hev_cycle_state(self, bool enable, int duration=0)
None async_set_power(self, bool state, int|None duration)
None async_get_color_zones(self)
int get_number_of_zones(self)
set[ColorMode] supported_color_modes(self)
tuple[float, float]|None hs_color(self)
ColorMode color_mode(self)
None set_color(self, list[float|int|None] hsbk, dict[str, Any] kwargs, int duration=0)
None set_power(self, bool pwr, int duration=0)
None default_effect(self, **Any kwargs)
int|None color_temp_kelvin(self)
None _cancel_postponed_update(self)
None set_state(self, **Any kwargs)
None async_added_to_hass(self)
_attr_supported_color_modes
_attr_max_color_temp_kelvin
None set_color(self, list[float|int|None] hsbk, dict[str, Any] kwargs, int duration=0)
None set_hev_cycle_state(self, bool power, int|None duration=None)
None __init__(self, LIFXUpdateCoordinator coordinator, LIFXManager manager, ConfigEntry entry)
None async_turn_on(self, **Any kwargs)
_attr_min_color_temp_kelvin
None async_will_remove_from_hass(self)
None async_turn_off(self, **Any kwargs)
None update_during_transition(self, int when)
None update_color_zones(self)
None set_color(self, list[float|int|None] hsbk, dict[str, Any] kwargs, int duration=0)
None async_write_ha_state(self)
None async_on_remove(self, CALLBACK_TYPE func)
None _async_refresh(self, bool log_failures=True, bool raise_on_auth_failed=False, bool scheduled=False, bool raise_on_entry_error=False)
None async_set_updated_data(self, _DataT data)
None async_request_refresh(self)
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
int convert_16_to_8(int value)
list[float|int|None] merge_hsbk(list[float|int|None] base, list[float|int|None] change)
list[float|int|None]|None find_hsbk(HomeAssistant hass, **Any kwargs)
int convert_8_to_16(int value)
dict[str, Any] lifx_features(Light bulb)
CALLBACK_TYPE async_call_later(HomeAssistant hass, float|timedelta delay, HassJob[[datetime], Coroutine[Any, Any, None]|None]|Callable[[datetime], Coroutine[Any, Any, None]|None] action)