1 """Support for LimitlessLED bulbs."""
3 from __future__
import annotations
5 from collections.abc
import Callable
7 from typing
import Any, Concatenate, cast
9 from limitlessled
import Color
10 from limitlessled.bridge
import Bridge
11 from limitlessled.group
import Group
12 from limitlessled.group.dimmer
import DimmerGroup
13 from limitlessled.group.rgbw
import RgbwGroup
14 from limitlessled.group.rgbww
import RgbwwGroup
15 from limitlessled.group.white
import WhiteGroup
16 from limitlessled.pipeline
import Pipeline
17 from limitlessled.presets
import COLORLOOP
18 import voluptuous
as vol
30 PLATFORM_SCHEMA
as LIGHT_PLATFORM_SCHEMA,
43 _LOGGER = logging.getLogger(__name__)
45 CONF_BRIDGES =
"bridges"
46 CONF_GROUPS =
"groups"
47 CONF_NUMBER =
"number"
48 CONF_VERSION =
"version"
51 DEFAULT_LED_TYPE =
"rgbw"
53 DEFAULT_TRANSITION = 0
57 LED_TYPE = [
"rgbw",
"rgbww",
"white",
"bridge-led",
"dimmer"]
59 EFFECT_NIGHT =
"night"
65 COLOR_MODES_LIMITLESS_WHITE = {ColorMode.COLOR_TEMP}
66 SUPPORT_LIMITLESSLED_WHITE = LightEntityFeature.EFFECT | LightEntityFeature.TRANSITION
67 COLOR_MODES_LIMITLESS_DIMMER = {ColorMode.BRIGHTNESS}
68 SUPPORT_LIMITLESSLED_DIMMER = LightEntityFeature.TRANSITION
69 COLOR_MODES_LIMITLESS_RGB = {ColorMode.HS}
70 SUPPORT_LIMITLESSLED_RGB = (
71 LightEntityFeature.EFFECT | LightEntityFeature.FLASH | LightEntityFeature.TRANSITION
73 COLOR_MODES_LIMITLESS_RGBWW = {ColorMode.COLOR_TEMP, ColorMode.HS}
74 SUPPORT_LIMITLESSLED_RGBWW = (
75 LightEntityFeature.EFFECT | LightEntityFeature.FLASH | LightEntityFeature.TRANSITION
78 PLATFORM_SCHEMA = LIGHT_PLATFORM_SCHEMA.extend(
80 vol.Required(CONF_BRIDGES): vol.All(
84 vol.Required(CONF_HOST): cv.string,
86 CONF_VERSION, default=DEFAULT_VERSION
88 vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
89 vol.Required(CONF_GROUPS): vol.All(
93 vol.Required(CONF_NAME): cv.string,
95 CONF_TYPE, default=DEFAULT_LED_TYPE
97 vol.Required(CONF_NUMBER): cv.positive_int,
99 CONF_FADE, default=DEFAULT_FADE
112 """Rewrite legacy configuration to new format."""
113 bridges = config.get(CONF_BRIDGES, [config])
115 for bridge_conf
in bridges:
117 if "groups" in bridge_conf:
118 groups = bridge_conf[
"groups"]
120 _LOGGER.warning(
"Legacy configuration format detected")
121 for i
in range(1, 5):
122 name_key = f
"group_{i}_name"
123 if name_key
in bridge_conf:
127 "type": bridge_conf.get(
128 f
"group_{i}_type", DEFAULT_LED_TYPE
130 "name": bridge_conf.get(name_key),
135 "host": bridge_conf.get(CONF_HOST),
136 "version": bridge_conf.get(CONF_VERSION),
137 "port": bridge_conf.get(CONF_PORT),
141 return {
"bridges": new_bridges}
147 add_entities: AddEntitiesCallback,
148 discovery_info: DiscoveryInfoType |
None =
None,
150 """Set up the LimitlessLED lights."""
158 bridge_conf: dict[str, Any]
159 group_conf: dict[str, Any]
160 for bridge_conf
in config[CONF_BRIDGES]:
162 bridge_conf.get(CONF_HOST),
163 port=bridge_conf.get(CONF_PORT, DEFAULT_PORT),
164 version=bridge_conf.get(CONF_VERSION, DEFAULT_VERSION),
166 for group_conf
in bridge_conf[CONF_GROUPS]:
167 group = bridge.add_group(
168 group_conf.get(CONF_NUMBER),
169 group_conf.get(CONF_NAME),
170 group_conf.get(CONF_TYPE, DEFAULT_LED_TYPE),
176 def state[_LimitlessLEDGroupT: LimitlessLEDGroup, **_P](
179 [Callable[Concatenate[_LimitlessLEDGroupT, int, Pipeline, _P], Any]],
180 Callable[Concatenate[_LimitlessLEDGroupT, _P],
None],
184 Specify True (turn on) or False (turn off).
188 function: Callable[Concatenate[_LimitlessLEDGroupT, int, Pipeline, _P], Any],
189 ) -> Callable[Concatenate[_LimitlessLEDGroupT, _P],
None]:
190 """Set up the decorator function."""
193 self: _LimitlessLEDGroupT, *args: _P.args, **kwargs: _P.kwargs
195 """Wrap a group state change."""
196 pipeline = Pipeline()
197 transition_time = DEFAULT_TRANSITION
198 if self.effect == EFFECT_COLORLOOP:
200 self._attr_effect =
None
202 if ATTR_TRANSITION
in kwargs:
203 transition_time =
int(cast(float, kwargs[ATTR_TRANSITION]))
205 function(self, transition_time, pipeline, *args, **kwargs)
207 self._attr_is_on = new_state
208 self.group.enqueue(pipeline)
209 self.schedule_update_ha_state()
217 """Representation of a LimitessLED group."""
219 _attr_assumed_state =
True
220 _attr_max_mireds = 370
221 _attr_min_mireds = 154
222 _attr_should_poll =
False
224 def __init__(self, group: Group, config: dict[str, Any]) ->
None:
225 """Initialize a group."""
227 if isinstance(group, WhiteGroup):
231 elif isinstance(group, DimmerGroup):
235 elif isinstance(group, RgbwGroup):
238 self.
_attr_effect_list_attr_effect_list = [EFFECT_COLORLOOP, EFFECT_NIGHT, EFFECT_WHITE]
239 elif isinstance(group, RgbwwGroup):
242 self.
_attr_effect_list_attr_effect_list = [EFFECT_COLORLOOP, EFFECT_NIGHT, EFFECT_WHITE]
249 ColorMode.COLOR_TEMP,
259 """Handle entity about to be added to hass event."""
262 self.
_attr_is_on_attr_is_on = last_state.state == STATE_ON
269 """Return the brightness property."""
277 """Return the color mode of the light."""
287 return ColorMode.COLOR_TEMP
291 def turn_off(self, transition_time: int, pipeline: Pipeline, **kwargs: Any) ->
None:
292 """Turn off a group."""
293 if self.
configconfig[CONF_FADE]:
294 pipeline.transition(transition_time, brightness=0.0)
298 def turn_on(self, transition_time: int, pipeline: Pipeline, **kwargs: Any) ->
None:
299 """Turn on (or adjust property of) a group."""
301 if kwargs.get(ATTR_EFFECT) == EFFECT_NIGHT:
303 pipeline.night_light()
314 if ATTR_BRIGHTNESS
in kwargs:
318 if ATTR_HS_COLOR
in kwargs:
321 assert self.
hs_colorhs_color
is not None
322 if self.
hs_colorhs_color[1] < MIN_SATURATION:
328 if ATTR_COLOR_TEMP
in kwargs:
337 pipeline.transition(transition_time, **args)
342 if kwargs[ATTR_FLASH] == FLASH_LONG:
344 pipeline.flash(duration=duration)
347 if ATTR_EFFECT
in kwargs
and self.
effect_listeffect_list:
348 if kwargs[ATTR_EFFECT] == EFFECT_COLORLOOP:
350 pipeline.append(COLORLOOP)
351 if kwargs[ATTR_EFFECT] == EFFECT_WHITE:
356 """Convert Home Assistant color temperature units to percentage."""
359 width = max_kelvin - min_kelvin
362 temperature = (kelvin - min_kelvin) / width
363 return max(0,
min(1, temperature))
366 """Convert Home Assistant brightness units to percentage."""
371 """Convert Home Assistant HS list to RGB Color tuple."""
372 assert self.
hs_colorhs_color
is not None
list[str]|None effect_list(self)
int|None brightness(self)
int|None color_temp(self)
set[ColorMode]|set[str]|None supported_color_modes(self)
tuple[float, float]|None hs_color(self)
LightEntityFeature supported_features(self)
float limitlessled_brightness(self)
int|None brightness(self)
float limitlessled_temperature(self)
str|None color_mode(self)
None async_added_to_hass(self)
_attr_supported_color_modes
None __init__(self, Group group, dict[str, Any] config)
Color limitlessled_color(self)
None turn_off(self, int transition_time, Pipeline pipeline, **Any kwargs)
None turn_on(self, int transition_time, Pipeline pipeline, **Any kwargs)
int|None supported_features(self)
State|None async_get_last_state(self)
None add_entities(AsusWrtRouter router, AddEntitiesCallback async_add_entities, set[str] tracked)
None setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback add_entities, DiscoveryInfoType|None discovery_info=None)
ConfigType rewrite_legacy(ConfigType config)
bool state(HomeAssistant hass, str|State|None entity, Any req_state, timedelta|None for_period=None, str|None attribute=None, TemplateVarsType variables=None)
int color_temperature_mired_to_kelvin(float mired_temperature)
tuple[int, int, int] color_hs_to_RGB(float iH, float iS)