1 """Support for Magic Home lights."""
3 from __future__
import annotations
7 from typing
import Any, Final
9 from flux_led.const
import MultiColorEffects
10 from flux_led.protocol
import MusicMode
11 from flux_led.utils
import rgbcw_brightness, rgbcw_to_rgbwc, rgbw_brightness
12 import voluptuous
as vol
14 from homeassistant
import config_entries
34 color_temperature_kelvin_to_mired,
35 color_temperature_mired_to_kelvin,
40 CONF_CUSTOM_EFFECT_COLORS,
41 CONF_CUSTOM_EFFECT_SPEED_PCT,
42 CONF_CUSTOM_EFFECT_TRANSITION,
49 MULTI_BRIGHTNESS_COLOR_MODES,
54 from .coordinator
import FluxLedUpdateCoordinator
55 from .entity
import FluxOnOffEntity
58 _flux_color_mode_to_hass,
62 _min_rgbwc_brightness,
63 _str_to_multi_color_effect,
66 _LOGGER = logging.getLogger(__name__)
77 ATTR_FOREGROUND_COLOR: Final =
"foreground_color"
78 ATTR_BACKGROUND_COLOR: Final =
"background_color"
79 ATTR_SENSITIVITY: Final =
"sensitivity"
80 ATTR_LIGHT_SCREEN: Final =
"light_screen"
84 COLOR_TEMP_WARM_VS_COLD_WHITE_CUT_OFF: Final = 285
86 EFFECT_CUSTOM: Final =
"custom"
88 SERVICE_CUSTOM_EFFECT: Final =
"set_custom_effect"
89 SERVICE_SET_ZONES: Final =
"set_zones"
90 SERVICE_SET_MUSIC_MODE: Final =
"set_music_mode"
92 CUSTOM_EFFECT_DICT: VolDictType = {
93 vol.Required(CONF_COLORS): vol.All(
95 vol.Length(min=1, max=16),
96 [vol.All(vol.Coerce(tuple), vol.ExactSequence((cv.byte, cv.byte, cv.byte)))],
98 vol.Optional(CONF_SPEED_PCT, default=50): vol.All(
99 vol.Coerce(int), vol.Range(min=0, max=100)
101 vol.Optional(CONF_TRANSITION, default=TRANSITION_GRADUAL): vol.All(
102 cv.string, vol.In([TRANSITION_GRADUAL, TRANSITION_JUMP, TRANSITION_STROBE])
106 SET_MUSIC_MODE_DICT: VolDictType = {
107 vol.Optional(ATTR_SENSITIVITY, default=100): vol.All(
108 vol.Coerce(int), vol.Range(min=0, max=100)
110 vol.Optional(ATTR_BRIGHTNESS, default=100): vol.All(
111 vol.Coerce(int), vol.Range(min=0, max=100)
113 vol.Optional(ATTR_EFFECT, default=1): vol.All(
114 vol.Coerce(int), vol.Range(min=0, max=16)
116 vol.Optional(ATTR_LIGHT_SCREEN, default=
False): bool,
117 vol.Optional(ATTR_FOREGROUND_COLOR): vol.All(
118 vol.Coerce(tuple), vol.ExactSequence((cv.byte,) * 3)
120 vol.Optional(ATTR_BACKGROUND_COLOR): vol.All(
121 vol.Coerce(tuple), vol.ExactSequence((cv.byte,) * 3)
125 SET_ZONES_DICT: VolDictType = {
126 vol.Required(CONF_COLORS): vol.All(
128 vol.Length(min=1, max=2048),
129 [vol.All(vol.Coerce(tuple), vol.ExactSequence((cv.byte, cv.byte, cv.byte)))],
131 vol.Optional(CONF_SPEED_PCT, default=50): vol.All(
132 vol.Coerce(int), vol.Range(min=0, max=100)
134 vol.Optional(CONF_EFFECT, default=MultiColorEffects.STATIC.name.lower()): vol.All(
135 cv.string, vol.In([effect.name.lower()
for effect
in MultiColorEffects])
143 async_add_entities: AddEntitiesCallback,
145 """Set up the Flux lights."""
146 coordinator: FluxLedUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
148 platform = entity_platform.async_get_current_platform()
149 platform.async_register_entity_service(
150 SERVICE_CUSTOM_EFFECT,
152 "async_set_custom_effect",
154 platform.async_register_entity_service(
159 platform.async_register_entity_service(
160 SERVICE_SET_MUSIC_MODE,
162 "async_set_music_mode",
164 options = entry.options
167 custom_effect_colors = ast.literal_eval(
168 options.get(CONF_CUSTOM_EFFECT_COLORS)
or "[]"
170 except (ValueError, TypeError, SyntaxError, MemoryError)
as ex:
172 "Could not parse custom effect colors for %s: %s", entry.unique_id, ex
174 custom_effect_colors = []
180 entry.unique_id
or entry.entry_id,
181 list(custom_effect_colors),
182 options.get(CONF_CUSTOM_EFFECT_SPEED_PCT, DEFAULT_EFFECT_SPEED),
183 options.get(CONF_CUSTOM_EFFECT_TRANSITION, TRANSITION_GRADUAL),
190 FluxOnOffEntity, CoordinatorEntity[FluxLedUpdateCoordinator], LightEntity
192 """Representation of a Flux light."""
196 _attr_supported_features = LightEntityFeature.TRANSITION | LightEntityFeature.EFFECT
200 coordinator: FluxLedUpdateCoordinator,
202 custom_effect_colors: list[tuple[int, int, int]],
203 custom_effect_speed_pct: int,
204 custom_effect_transition: str,
206 """Initialize the light."""
207 super().
__init__(coordinator, base_unique_id,
None)
211 custom_effects: list[str] = []
212 if custom_effect_colors:
213 custom_effects.append(EFFECT_CUSTOM)
221 """Return the brightness of this light between 0..255."""
222 return self._device.brightness
226 """Return the kelvin value of this light in mired."""
231 """Return the rgb color value."""
232 return self._device.rgb_unscaled
236 """Return the rgbw color value."""
237 return self._device.rgbw
241 """Return the rgbww aka rgbcw color value."""
242 return self._device.rgbcw
246 """Return the color mode of the light."""
248 self._device.color_mode, self._device.color_modes
253 """Return the current effect."""
254 return self._device.effect
257 """Turn the specified or all lights on."""
258 if self._device.requires_turn_on
or not kwargs:
264 if MODE_ATTRS.intersection(kwargs):
267 await self._device.async_set_brightness(self.
_async_brightness_async_brightness(**kwargs))
272 if effect == EFFECT_CUSTOM:
274 await self._device.async_set_custom_pattern(
280 await self._device.async_set_effect(
282 self._device.speed
or DEFAULT_EFFECT_SPEED,
288 """Determine brightness from kwargs or current value."""
289 if (brightness := kwargs.get(ATTR_BRIGHTNESS))
is None:
297 return max(MIN_RGB_BRIGHTNESS, brightness)
300 """Set an effect or color mode."""
303 if effect := kwargs.get(ATTR_EFFECT):
307 if color_temp_mired := kwargs.get(ATTR_COLOR_TEMP):
310 ATTR_BRIGHTNESS
not in kwargs
315 brightness =
max(MIN_CCT_BRIGHTNESS, *self._device.rgb)
316 await self._device.async_set_white_temp(color_temp_kelvin, brightness)
319 if rgb := kwargs.get(ATTR_RGB_COLOR):
320 if not self._device.requires_turn_on:
322 red, green, blue = rgb
323 await self._device.async_set_levels(red, green, blue, brightness=brightness)
326 if rgbw := kwargs.get(ATTR_RGBW_COLOR):
327 if ATTR_BRIGHTNESS
in kwargs:
328 rgbw = rgbw_brightness(rgbw, brightness)
330 await self._device.async_set_levels(*rgbw)
333 if rgbcw := kwargs.get(ATTR_RGBWW_COLOR):
334 if ATTR_BRIGHTNESS
in kwargs:
335 rgbcw = rgbcw_brightness(kwargs[ATTR_RGBWW_COLOR], brightness)
336 rgbwc = rgbcw_to_rgbwc(rgbcw)
338 await self._device.async_set_levels(*rgbwc)
340 if (white := kwargs.get(ATTR_WHITE))
is not None:
341 await self._device.async_set_levels(w=white)
345 self, colors: list[tuple[int, int, int]], speed_pct: int, transition: str
347 """Set a custom effect on the bulb."""
348 await self._device.async_set_custom_pattern(
355 self, colors: list[tuple[int, int, int]], speed_pct: int, effect: str
357 """Set a colors for zones."""
370 foreground_color: tuple[int, int, int] |
None =
None,
371 background_color: tuple[int, int, int] |
None =
None,
373 """Configure music mode."""
376 sensitivity=sensitivity,
377 brightness=brightness,
378 mode=MusicMode.LIGHT_SCREEN.value
if light_screen
else None,
380 foreground_color=foreground_color,
381 background_color=background_color,
None _async_ensure_device_on(self)
None async_turn_on(self, **Any kwargs)
tuple[int, int, int] rgb_color(self)
int _async_brightness(self, **Any kwargs)
None async_set_custom_effect(self, list[tuple[int, int, int]] colors, int speed_pct, str transition)
_custom_effect_transition
None _async_set_mode(self, **Any kwargs)
None async_set_music_mode(self, int sensitivity, int brightness, int effect, bool light_screen, tuple[int, int, int]|None foreground_color=None, tuple[int, int, int]|None background_color=None)
tuple[int, int, int, int] rgbw_color(self)
_attr_supported_color_modes
None __init__(self, FluxLedUpdateCoordinator coordinator, str base_unique_id, list[tuple[int, int, int]] custom_effect_colors, int custom_effect_speed_pct, str custom_effect_transition)
None _async_set_effect(self, str effect, int brightness)
tuple[int, int, int, int, int] rgbww_color(self)
None _async_turn_on(self, **Any kwargs)
None async_set_zones(self, list[tuple[int, int, int]] colors, int speed_pct, str effect)
int|None brightness(self)
ColorMode|str|None color_mode(self)
None async_setup_entry(HomeAssistant hass, config_entries.ConfigEntry entry, AddEntitiesCallback async_add_entities)
int _effect_brightness(int brightness)
ColorMode _flux_color_mode_to_hass(str|None flux_color_mode, set[str] flux_color_modes)
tuple[int, int, int] _min_rgb_brightness(tuple[int, int, int] rgb)
tuple[int, int, int, int] _min_rgbw_brightness(tuple[int, int, int, int] rgbw, tuple[int, int, int, int] current_rgbw)
tuple[int, int, int, int, int] _min_rgbwc_brightness(tuple[int, int, int, int, int] rgbwc, tuple[int, int, int, int, int] current_rgbwc)
MultiColorEffects _str_to_multi_color_effect(str effect_str)
set[str] _hass_color_modes(AIOWifiLedBulb device)
int color_temperature_mired_to_kelvin(float mired_temperature)
int color_temperature_kelvin_to_mired(float kelvin_temperature)