mirror of
https://github.com/home-assistant/core.git
synced 2025-07-17 10:17:09 +00:00
Add RGB, RGBW and RGBWW capability to template.light (#86047)
* Add RGB, RGBW and RGBWW capability to template.light Add the required unit test Mute 'LightTemplate.async_turn_on' is too complex Rename all HS color mode from a generic "Color" name to a specific "HS" name * Bring back legacy "color" keyword * Cleanup unrequested commented test * Increase code coverage to 100% * Remove confusing if that should never be false * Apply suggestions from code review --------- Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
parent
edf18df0e6
commit
3929b0163c
@ -11,6 +11,9 @@ from homeassistant.components.light import (
|
|||||||
ATTR_COLOR_TEMP,
|
ATTR_COLOR_TEMP,
|
||||||
ATTR_EFFECT,
|
ATTR_EFFECT,
|
||||||
ATTR_HS_COLOR,
|
ATTR_HS_COLOR,
|
||||||
|
ATTR_RGB_COLOR,
|
||||||
|
ATTR_RGBW_COLOR,
|
||||||
|
ATTR_RGBWW_COLOR,
|
||||||
ATTR_TRANSITION,
|
ATTR_TRANSITION,
|
||||||
ENTITY_ID_FORMAT,
|
ENTITY_ID_FORMAT,
|
||||||
ColorMode,
|
ColorMode,
|
||||||
@ -46,8 +49,18 @@ from .template_entity import (
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
_VALID_STATES = [STATE_ON, STATE_OFF, "true", "false"]
|
_VALID_STATES = [STATE_ON, STATE_OFF, "true", "false"]
|
||||||
|
|
||||||
|
# Legacy
|
||||||
CONF_COLOR_ACTION = "set_color"
|
CONF_COLOR_ACTION = "set_color"
|
||||||
CONF_COLOR_TEMPLATE = "color_template"
|
CONF_COLOR_TEMPLATE = "color_template"
|
||||||
|
|
||||||
|
CONF_HS_ACTION = "set_hs"
|
||||||
|
CONF_HS_TEMPLATE = "hs_template"
|
||||||
|
CONF_RGB_ACTION = "set_rgb"
|
||||||
|
CONF_RGB_TEMPLATE = "rgb_template"
|
||||||
|
CONF_RGBW_ACTION = "set_rgbw"
|
||||||
|
CONF_RGBW_TEMPLATE = "rgbw_template"
|
||||||
|
CONF_RGBWW_ACTION = "set_rgbww"
|
||||||
|
CONF_RGBWW_TEMPLATE = "rgbww_template"
|
||||||
CONF_EFFECT_ACTION = "set_effect"
|
CONF_EFFECT_ACTION = "set_effect"
|
||||||
CONF_EFFECT_LIST_TEMPLATE = "effect_list_template"
|
CONF_EFFECT_LIST_TEMPLATE = "effect_list_template"
|
||||||
CONF_EFFECT_TEMPLATE = "effect_template"
|
CONF_EFFECT_TEMPLATE = "effect_template"
|
||||||
@ -67,8 +80,16 @@ LIGHT_SCHEMA = vol.All(
|
|||||||
cv.deprecated(CONF_ENTITY_ID),
|
cv.deprecated(CONF_ENTITY_ID),
|
||||||
vol.Schema(
|
vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Optional(CONF_COLOR_ACTION): cv.SCRIPT_SCHEMA,
|
vol.Exclusive(CONF_COLOR_ACTION, "hs_legacy_action"): cv.SCRIPT_SCHEMA,
|
||||||
vol.Optional(CONF_COLOR_TEMPLATE): cv.template,
|
vol.Exclusive(CONF_COLOR_TEMPLATE, "hs_legacy_template"): cv.template,
|
||||||
|
vol.Exclusive(CONF_HS_ACTION, "hs_legacy_action"): cv.SCRIPT_SCHEMA,
|
||||||
|
vol.Exclusive(CONF_HS_TEMPLATE, "hs_legacy_template"): cv.template,
|
||||||
|
vol.Optional(CONF_RGB_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
|
vol.Optional(CONF_RGB_TEMPLATE): cv.template,
|
||||||
|
vol.Optional(CONF_RGBW_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
|
vol.Optional(CONF_RGBW_TEMPLATE): cv.template,
|
||||||
|
vol.Optional(CONF_RGBWW_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
|
vol.Optional(CONF_RGBWW_TEMPLATE): cv.template,
|
||||||
vol.Inclusive(CONF_EFFECT_ACTION, "effect"): cv.SCRIPT_SCHEMA,
|
vol.Inclusive(CONF_EFFECT_ACTION, "effect"): cv.SCRIPT_SCHEMA,
|
||||||
vol.Inclusive(CONF_EFFECT_LIST_TEMPLATE, "effect"): cv.template,
|
vol.Inclusive(CONF_EFFECT_LIST_TEMPLATE, "effect"): cv.template,
|
||||||
vol.Inclusive(CONF_EFFECT_TEMPLATE, "effect"): cv.template,
|
vol.Inclusive(CONF_EFFECT_TEMPLATE, "effect"): cv.template,
|
||||||
@ -166,6 +187,22 @@ class LightTemplate(TemplateEntity, LightEntity):
|
|||||||
if (color_action := config.get(CONF_COLOR_ACTION)) is not None:
|
if (color_action := config.get(CONF_COLOR_ACTION)) is not None:
|
||||||
self._color_script = Script(hass, color_action, friendly_name, DOMAIN)
|
self._color_script = Script(hass, color_action, friendly_name, DOMAIN)
|
||||||
self._color_template = config.get(CONF_COLOR_TEMPLATE)
|
self._color_template = config.get(CONF_COLOR_TEMPLATE)
|
||||||
|
self._hs_script = None
|
||||||
|
if (hs_action := config.get(CONF_HS_ACTION)) is not None:
|
||||||
|
self._hs_script = Script(hass, hs_action, friendly_name, DOMAIN)
|
||||||
|
self._hs_template = config.get(CONF_HS_TEMPLATE)
|
||||||
|
self._rgb_script = None
|
||||||
|
if (rgb_action := config.get(CONF_RGB_ACTION)) is not None:
|
||||||
|
self._rgb_script = Script(hass, rgb_action, friendly_name, DOMAIN)
|
||||||
|
self._rgb_template = config.get(CONF_RGB_TEMPLATE)
|
||||||
|
self._rgbw_script = None
|
||||||
|
if (rgbw_action := config.get(CONF_RGBW_ACTION)) is not None:
|
||||||
|
self._rgbw_script = Script(hass, rgbw_action, friendly_name, DOMAIN)
|
||||||
|
self._rgbw_template = config.get(CONF_RGBW_TEMPLATE)
|
||||||
|
self._rgbww_script = None
|
||||||
|
if (rgbww_action := config.get(CONF_RGBWW_ACTION)) is not None:
|
||||||
|
self._rgbww_script = Script(hass, rgbww_action, friendly_name, DOMAIN)
|
||||||
|
self._rgbww_template = config.get(CONF_RGBWW_TEMPLATE)
|
||||||
self._effect_script = None
|
self._effect_script = None
|
||||||
if (effect_action := config.get(CONF_EFFECT_ACTION)) is not None:
|
if (effect_action := config.get(CONF_EFFECT_ACTION)) is not None:
|
||||||
self._effect_script = Script(hass, effect_action, friendly_name, DOMAIN)
|
self._effect_script = Script(hass, effect_action, friendly_name, DOMAIN)
|
||||||
@ -178,24 +215,39 @@ class LightTemplate(TemplateEntity, LightEntity):
|
|||||||
self._state = False
|
self._state = False
|
||||||
self._brightness = None
|
self._brightness = None
|
||||||
self._temperature = None
|
self._temperature = None
|
||||||
self._color = None
|
self._hs_color = None
|
||||||
|
self._rgb_color = None
|
||||||
|
self._rgbw_color = None
|
||||||
|
self._rgbww_color = None
|
||||||
self._effect = None
|
self._effect = None
|
||||||
self._effect_list = None
|
self._effect_list = None
|
||||||
self._fixed_color_mode = None
|
self._color_mode = None
|
||||||
self._max_mireds = None
|
self._max_mireds = None
|
||||||
self._min_mireds = None
|
self._min_mireds = None
|
||||||
self._supports_transition = False
|
self._supports_transition = False
|
||||||
|
self._supported_color_modes = None
|
||||||
|
|
||||||
color_modes = {ColorMode.ONOFF}
|
color_modes = {ColorMode.ONOFF}
|
||||||
if self._level_script is not None:
|
if self._level_script is not None:
|
||||||
color_modes.add(ColorMode.BRIGHTNESS)
|
color_modes.add(ColorMode.BRIGHTNESS)
|
||||||
if self._temperature_script is not None:
|
if self._temperature_script is not None:
|
||||||
color_modes.add(ColorMode.COLOR_TEMP)
|
color_modes.add(ColorMode.COLOR_TEMP)
|
||||||
|
if self._hs_script is not None:
|
||||||
|
color_modes.add(ColorMode.HS)
|
||||||
if self._color_script is not None:
|
if self._color_script is not None:
|
||||||
color_modes.add(ColorMode.HS)
|
color_modes.add(ColorMode.HS)
|
||||||
|
if self._rgb_script is not None:
|
||||||
|
color_modes.add(ColorMode.RGB)
|
||||||
|
if self._rgbw_script is not None:
|
||||||
|
color_modes.add(ColorMode.RGBW)
|
||||||
|
if self._rgbww_script is not None:
|
||||||
|
color_modes.add(ColorMode.RGBWW)
|
||||||
|
|
||||||
self._supported_color_modes = filter_supported_color_modes(color_modes)
|
self._supported_color_modes = filter_supported_color_modes(color_modes)
|
||||||
|
if len(self._supported_color_modes) > 1:
|
||||||
|
self._color_mode = ColorMode.UNKNOWN
|
||||||
if len(self._supported_color_modes) == 1:
|
if len(self._supported_color_modes) == 1:
|
||||||
self._fixed_color_mode = next(iter(self._supported_color_modes))
|
self._color_mode = next(iter(self._supported_color_modes))
|
||||||
|
|
||||||
self._attr_supported_features = LightEntityFeature(0)
|
self._attr_supported_features = LightEntityFeature(0)
|
||||||
if self._effect_script is not None:
|
if self._effect_script is not None:
|
||||||
@ -232,7 +284,22 @@ class LightTemplate(TemplateEntity, LightEntity):
|
|||||||
@property
|
@property
|
||||||
def hs_color(self) -> tuple[float, float] | None:
|
def hs_color(self) -> tuple[float, float] | None:
|
||||||
"""Return the hue and saturation color value [float, float]."""
|
"""Return the hue and saturation color value [float, float]."""
|
||||||
return self._color
|
return self._hs_color
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rgb_color(self) -> tuple[int, int, int] | None:
|
||||||
|
"""Return the rgb color value."""
|
||||||
|
return self._rgb_color
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rgbw_color(self) -> tuple[int, int, int, int] | None:
|
||||||
|
"""Return the rgbw color value."""
|
||||||
|
return self._rgbw_color
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rgbww_color(self) -> tuple[int, int, int, int, int] | None:
|
||||||
|
"""Return the rgbww color value."""
|
||||||
|
return self._rgbww_color
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def effect(self) -> str | None:
|
def effect(self) -> str | None:
|
||||||
@ -247,12 +314,7 @@ class LightTemplate(TemplateEntity, LightEntity):
|
|||||||
@property
|
@property
|
||||||
def color_mode(self):
|
def color_mode(self):
|
||||||
"""Return current color mode."""
|
"""Return current color mode."""
|
||||||
if self._fixed_color_mode:
|
return self._color_mode
|
||||||
return self._fixed_color_mode
|
|
||||||
# Support for ct + hs, prioritize hs
|
|
||||||
if self._color is not None:
|
|
||||||
return ColorMode.HS
|
|
||||||
return ColorMode.COLOR_TEMP
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_color_modes(self):
|
def supported_color_modes(self):
|
||||||
@ -305,10 +367,42 @@ class LightTemplate(TemplateEntity, LightEntity):
|
|||||||
)
|
)
|
||||||
if self._color_template:
|
if self._color_template:
|
||||||
self.add_template_attribute(
|
self.add_template_attribute(
|
||||||
"_color",
|
"_hs_color",
|
||||||
self._color_template,
|
self._color_template,
|
||||||
None,
|
None,
|
||||||
self._update_color,
|
self._update_hs,
|
||||||
|
none_on_template_error=True,
|
||||||
|
)
|
||||||
|
if self._hs_template:
|
||||||
|
self.add_template_attribute(
|
||||||
|
"_hs_color",
|
||||||
|
self._hs_template,
|
||||||
|
None,
|
||||||
|
self._update_hs,
|
||||||
|
none_on_template_error=True,
|
||||||
|
)
|
||||||
|
if self._rgb_template:
|
||||||
|
self.add_template_attribute(
|
||||||
|
"_rgb_color",
|
||||||
|
self._rgb_template,
|
||||||
|
None,
|
||||||
|
self._update_rgb,
|
||||||
|
none_on_template_error=True,
|
||||||
|
)
|
||||||
|
if self._rgbw_template:
|
||||||
|
self.add_template_attribute(
|
||||||
|
"_rgbw_color",
|
||||||
|
self._rgbw_template,
|
||||||
|
None,
|
||||||
|
self._update_rgbw,
|
||||||
|
none_on_template_error=True,
|
||||||
|
)
|
||||||
|
if self._rgbww_template:
|
||||||
|
self.add_template_attribute(
|
||||||
|
"_rgbww_color",
|
||||||
|
self._rgbww_template,
|
||||||
|
None,
|
||||||
|
self._update_rgbww,
|
||||||
none_on_template_error=True,
|
none_on_template_error=True,
|
||||||
)
|
)
|
||||||
if self._effect_list_template:
|
if self._effect_list_template:
|
||||||
@ -337,7 +431,7 @@ class LightTemplate(TemplateEntity, LightEntity):
|
|||||||
)
|
)
|
||||||
super()._async_setup_templates()
|
super()._async_setup_templates()
|
||||||
|
|
||||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
async def async_turn_on(self, **kwargs: Any) -> None: # noqa: C901
|
||||||
"""Turn the light on."""
|
"""Turn the light on."""
|
||||||
optimistic_set = False
|
optimistic_set = False
|
||||||
# set optimistic states
|
# set optimistic states
|
||||||
@ -357,19 +451,88 @@ class LightTemplate(TemplateEntity, LightEntity):
|
|||||||
"Optimistically setting color temperature to %s",
|
"Optimistically setting color temperature to %s",
|
||||||
kwargs[ATTR_COLOR_TEMP],
|
kwargs[ATTR_COLOR_TEMP],
|
||||||
)
|
)
|
||||||
|
self._color_mode = ColorMode.COLOR_TEMP
|
||||||
self._temperature = kwargs[ATTR_COLOR_TEMP]
|
self._temperature = kwargs[ATTR_COLOR_TEMP]
|
||||||
if self._color_template is None:
|
if self._hs_template is None and self._color_template is None:
|
||||||
self._color = None
|
self._hs_color = None
|
||||||
|
if self._rgb_template is None:
|
||||||
|
self._rgb_color = None
|
||||||
|
if self._rgbw_template is None:
|
||||||
|
self._rgbw_color = None
|
||||||
|
if self._rgbww_template is None:
|
||||||
|
self._rgbww_color = None
|
||||||
optimistic_set = True
|
optimistic_set = True
|
||||||
|
|
||||||
if self._color_template is None and ATTR_HS_COLOR in kwargs:
|
if (
|
||||||
|
self._hs_template is None
|
||||||
|
and self._color_template is None
|
||||||
|
and ATTR_HS_COLOR in kwargs
|
||||||
|
):
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"Optimistically setting color to %s",
|
"Optimistically setting hs color to %s",
|
||||||
kwargs[ATTR_HS_COLOR],
|
kwargs[ATTR_HS_COLOR],
|
||||||
)
|
)
|
||||||
self._color = kwargs[ATTR_HS_COLOR]
|
self._color_mode = ColorMode.HS
|
||||||
|
self._hs_color = kwargs[ATTR_HS_COLOR]
|
||||||
if self._temperature_template is None:
|
if self._temperature_template is None:
|
||||||
self._temperature = None
|
self._temperature = None
|
||||||
|
if self._rgb_template is None:
|
||||||
|
self._rgb_color = None
|
||||||
|
if self._rgbw_template is None:
|
||||||
|
self._rgbw_color = None
|
||||||
|
if self._rgbww_template is None:
|
||||||
|
self._rgbww_color = None
|
||||||
|
optimistic_set = True
|
||||||
|
|
||||||
|
if self._rgb_template is None and ATTR_RGB_COLOR in kwargs:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Optimistically setting rgb color to %s",
|
||||||
|
kwargs[ATTR_RGB_COLOR],
|
||||||
|
)
|
||||||
|
self._color_mode = ColorMode.RGB
|
||||||
|
self._rgb_color = kwargs[ATTR_RGB_COLOR]
|
||||||
|
if self._temperature_template is None:
|
||||||
|
self._temperature = None
|
||||||
|
if self._hs_template is None and self._color_template is None:
|
||||||
|
self._hs_color = None
|
||||||
|
if self._rgbw_template is None:
|
||||||
|
self._rgbw_color = None
|
||||||
|
if self._rgbww_template is None:
|
||||||
|
self._rgbww_color = None
|
||||||
|
optimistic_set = True
|
||||||
|
|
||||||
|
if self._rgbw_template is None and ATTR_RGBW_COLOR in kwargs:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Optimistically setting rgbw color to %s",
|
||||||
|
kwargs[ATTR_RGBW_COLOR],
|
||||||
|
)
|
||||||
|
self._color_mode = ColorMode.RGBW
|
||||||
|
self._rgbw_color = kwargs[ATTR_RGBW_COLOR]
|
||||||
|
if self._temperature_template is None:
|
||||||
|
self._temperature = None
|
||||||
|
if self._hs_template is None and self._color_template is None:
|
||||||
|
self._hs_color = None
|
||||||
|
if self._rgb_template is None:
|
||||||
|
self._rgb_color = None
|
||||||
|
if self._rgbww_template is None:
|
||||||
|
self._rgbww_color = None
|
||||||
|
optimistic_set = True
|
||||||
|
|
||||||
|
if self._rgbww_template is None and ATTR_RGBWW_COLOR in kwargs:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Optimistically setting rgbww color to %s",
|
||||||
|
kwargs[ATTR_RGBWW_COLOR],
|
||||||
|
)
|
||||||
|
self._color_mode = ColorMode.RGBWW
|
||||||
|
self._rgbww_color = kwargs[ATTR_RGBWW_COLOR]
|
||||||
|
if self._temperature_template is None:
|
||||||
|
self._temperature = None
|
||||||
|
if self._hs_template is None and self._color_template is None:
|
||||||
|
self._hs_color = None
|
||||||
|
if self._rgb_template is None:
|
||||||
|
self._rgb_color = None
|
||||||
|
if self._rgbw_template is None:
|
||||||
|
self._rgbw_color = None
|
||||||
optimistic_set = True
|
optimistic_set = True
|
||||||
|
|
||||||
common_params = {}
|
common_params = {}
|
||||||
@ -413,6 +576,58 @@ class LightTemplate(TemplateEntity, LightEntity):
|
|||||||
await self.async_run_script(
|
await self.async_run_script(
|
||||||
self._color_script, run_variables=common_params, context=self._context
|
self._color_script, run_variables=common_params, context=self._context
|
||||||
)
|
)
|
||||||
|
elif ATTR_HS_COLOR in kwargs and self._hs_script:
|
||||||
|
hs_value = kwargs[ATTR_HS_COLOR]
|
||||||
|
common_params["hs"] = hs_value
|
||||||
|
common_params["h"] = int(hs_value[0])
|
||||||
|
common_params["s"] = int(hs_value[1])
|
||||||
|
|
||||||
|
await self.async_run_script(
|
||||||
|
self._hs_script, run_variables=common_params, context=self._context
|
||||||
|
)
|
||||||
|
elif ATTR_RGBWW_COLOR in kwargs and self._rgbww_script:
|
||||||
|
rgbww_value = kwargs[ATTR_RGBWW_COLOR]
|
||||||
|
common_params["rgbww"] = rgbww_value
|
||||||
|
common_params["rgb"] = (
|
||||||
|
int(rgbww_value[0]),
|
||||||
|
int(rgbww_value[1]),
|
||||||
|
int(rgbww_value[2]),
|
||||||
|
)
|
||||||
|
common_params["r"] = int(rgbww_value[0])
|
||||||
|
common_params["g"] = int(rgbww_value[1])
|
||||||
|
common_params["b"] = int(rgbww_value[2])
|
||||||
|
common_params["cw"] = int(rgbww_value[3])
|
||||||
|
common_params["ww"] = int(rgbww_value[4])
|
||||||
|
|
||||||
|
await self.async_run_script(
|
||||||
|
self._rgbww_script, run_variables=common_params, context=self._context
|
||||||
|
)
|
||||||
|
elif ATTR_RGBW_COLOR in kwargs and self._rgbw_script:
|
||||||
|
rgbw_value = kwargs[ATTR_RGBW_COLOR]
|
||||||
|
common_params["rgbw"] = rgbw_value
|
||||||
|
common_params["rgb"] = (
|
||||||
|
int(rgbw_value[0]),
|
||||||
|
int(rgbw_value[1]),
|
||||||
|
int(rgbw_value[2]),
|
||||||
|
)
|
||||||
|
common_params["r"] = int(rgbw_value[0])
|
||||||
|
common_params["g"] = int(rgbw_value[1])
|
||||||
|
common_params["b"] = int(rgbw_value[2])
|
||||||
|
common_params["w"] = int(rgbw_value[3])
|
||||||
|
|
||||||
|
await self.async_run_script(
|
||||||
|
self._rgbw_script, run_variables=common_params, context=self._context
|
||||||
|
)
|
||||||
|
elif ATTR_RGB_COLOR in kwargs and self._rgb_script:
|
||||||
|
rgb_value = kwargs[ATTR_RGB_COLOR]
|
||||||
|
common_params["rgb"] = rgb_value
|
||||||
|
common_params["r"] = int(rgb_value[0])
|
||||||
|
common_params["g"] = int(rgb_value[1])
|
||||||
|
common_params["b"] = int(rgb_value[2])
|
||||||
|
|
||||||
|
await self.async_run_script(
|
||||||
|
self._rgb_script, run_variables=common_params, context=self._context
|
||||||
|
)
|
||||||
elif ATTR_BRIGHTNESS in kwargs and self._level_script:
|
elif ATTR_BRIGHTNESS in kwargs and self._level_script:
|
||||||
await self.async_run_script(
|
await self.async_run_script(
|
||||||
self._level_script, run_variables=common_params, context=self._context
|
self._level_script, run_variables=common_params, context=self._context
|
||||||
@ -560,18 +775,19 @@ class LightTemplate(TemplateEntity, LightEntity):
|
|||||||
" this light, or 'None'"
|
" this light, or 'None'"
|
||||||
)
|
)
|
||||||
self._temperature = None
|
self._temperature = None
|
||||||
|
self._color_mode = ColorMode.COLOR_TEMP
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _update_color(self, render):
|
def _update_hs(self, render):
|
||||||
"""Update the hs_color from the template."""
|
"""Update the color from the template."""
|
||||||
if render is None:
|
if render is None:
|
||||||
self._color = None
|
self._hs_color = None
|
||||||
return
|
return
|
||||||
|
|
||||||
h_str = s_str = None
|
h_str = s_str = None
|
||||||
if isinstance(render, str):
|
if isinstance(render, str):
|
||||||
if render in ("None", ""):
|
if render in ("None", ""):
|
||||||
self._color = None
|
self._hs_color = None
|
||||||
return
|
return
|
||||||
h_str, s_str = map(
|
h_str, s_str = map(
|
||||||
float, render.replace("(", "").replace(")", "").split(",", 1)
|
float, render.replace("(", "").replace(")", "").split(",", 1)
|
||||||
@ -582,10 +798,12 @@ class LightTemplate(TemplateEntity, LightEntity):
|
|||||||
if (
|
if (
|
||||||
h_str is not None
|
h_str is not None
|
||||||
and s_str is not None
|
and s_str is not None
|
||||||
|
and isinstance(h_str, (int, float))
|
||||||
|
and isinstance(s_str, (int, float))
|
||||||
and 0 <= h_str <= 360
|
and 0 <= h_str <= 360
|
||||||
and 0 <= s_str <= 100
|
and 0 <= s_str <= 100
|
||||||
):
|
):
|
||||||
self._color = (h_str, s_str)
|
self._hs_color = (h_str, s_str)
|
||||||
elif h_str is not None and s_str is not None:
|
elif h_str is not None and s_str is not None:
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
(
|
(
|
||||||
@ -596,12 +814,151 @@ class LightTemplate(TemplateEntity, LightEntity):
|
|||||||
s_str,
|
s_str,
|
||||||
self.entity_id,
|
self.entity_id,
|
||||||
)
|
)
|
||||||
self._color = None
|
self._hs_color = None
|
||||||
else:
|
else:
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
"Received invalid hs_color : (%s) for entity %s", render, self.entity_id
|
"Received invalid hs_color : (%s) for entity %s", render, self.entity_id
|
||||||
)
|
)
|
||||||
self._color = None
|
self._hs_color = None
|
||||||
|
self._color_mode = ColorMode.HS
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _update_rgb(self, render):
|
||||||
|
"""Update the color from the template."""
|
||||||
|
if render is None:
|
||||||
|
self._rgb_color = None
|
||||||
|
return
|
||||||
|
|
||||||
|
r_int = g_int = b_int = None
|
||||||
|
if isinstance(render, str):
|
||||||
|
if render in ("None", ""):
|
||||||
|
self._rgb_color = None
|
||||||
|
return
|
||||||
|
cleanup_char = ["(", ")", "[", "]", " "]
|
||||||
|
for char in cleanup_char:
|
||||||
|
render = render.replace(char, "")
|
||||||
|
r_int, g_int, b_int = map(int, render.split(",", 3))
|
||||||
|
elif isinstance(render, (list, tuple)) and len(render) == 3:
|
||||||
|
r_int, g_int, b_int = render
|
||||||
|
|
||||||
|
if all(
|
||||||
|
value is not None and isinstance(value, (int, float)) and 0 <= value <= 255
|
||||||
|
for value in (r_int, g_int, b_int)
|
||||||
|
):
|
||||||
|
self._rgb_color = (r_int, g_int, b_int)
|
||||||
|
elif any(
|
||||||
|
isinstance(value, (int, float)) and not 0 <= value <= 255
|
||||||
|
for value in (r_int, g_int, b_int)
|
||||||
|
):
|
||||||
|
_LOGGER.error(
|
||||||
|
"Received invalid rgb_color : (%s, %s, %s) for entity %s. Expected: (0-255, 0-255, 0-255)",
|
||||||
|
r_int,
|
||||||
|
g_int,
|
||||||
|
b_int,
|
||||||
|
self.entity_id,
|
||||||
|
)
|
||||||
|
self._rgb_color = None
|
||||||
|
else:
|
||||||
|
_LOGGER.error(
|
||||||
|
"Received invalid rgb_color : (%s) for entity %s",
|
||||||
|
render,
|
||||||
|
self.entity_id,
|
||||||
|
)
|
||||||
|
self._rgb_color = None
|
||||||
|
self._color_mode = ColorMode.RGB
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _update_rgbw(self, render):
|
||||||
|
"""Update the color from the template."""
|
||||||
|
if render is None:
|
||||||
|
self._rgbw_color = None
|
||||||
|
return
|
||||||
|
|
||||||
|
r_int = g_int = b_int = w_int = None
|
||||||
|
if isinstance(render, str):
|
||||||
|
if render in ("None", ""):
|
||||||
|
self._rgb_color = None
|
||||||
|
return
|
||||||
|
cleanup_char = ["(", ")", "[", "]", " "]
|
||||||
|
for char in cleanup_char:
|
||||||
|
render = render.replace(char, "")
|
||||||
|
r_int, g_int, b_int, w_int = map(int, render.split(",", 4))
|
||||||
|
elif isinstance(render, (list, tuple)) and len(render) == 4:
|
||||||
|
r_int, g_int, b_int, w_int = render
|
||||||
|
|
||||||
|
if all(
|
||||||
|
value is not None and isinstance(value, (int, float)) and 0 <= value <= 255
|
||||||
|
for value in (r_int, g_int, b_int, w_int)
|
||||||
|
):
|
||||||
|
self._rgbw_color = (r_int, g_int, b_int, w_int)
|
||||||
|
elif any(
|
||||||
|
isinstance(value, (int, float)) and not 0 <= value <= 255
|
||||||
|
for value in (r_int, g_int, b_int, w_int)
|
||||||
|
):
|
||||||
|
_LOGGER.error(
|
||||||
|
"Received invalid rgb_color : (%s, %s, %s, %s) for entity %s. Expected: (0-255, 0-255, 0-255, 0-255)",
|
||||||
|
r_int,
|
||||||
|
g_int,
|
||||||
|
b_int,
|
||||||
|
w_int,
|
||||||
|
self.entity_id,
|
||||||
|
)
|
||||||
|
self._rgbw_color = None
|
||||||
|
else:
|
||||||
|
_LOGGER.error(
|
||||||
|
"Received invalid rgb_color : (%s) for entity %s",
|
||||||
|
render,
|
||||||
|
self.entity_id,
|
||||||
|
)
|
||||||
|
self._rgbw_color = None
|
||||||
|
self._color_mode = ColorMode.RGBW
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _update_rgbww(self, render):
|
||||||
|
"""Update the color from the template."""
|
||||||
|
if render is None:
|
||||||
|
self._rgbww_color = None
|
||||||
|
return
|
||||||
|
|
||||||
|
r_int = g_int = b_int = cw_int = ww_int = None
|
||||||
|
if isinstance(render, str):
|
||||||
|
if render in ("None", ""):
|
||||||
|
self._rgb_color = None
|
||||||
|
return
|
||||||
|
cleanup_char = ["(", ")", "[", "]", " "]
|
||||||
|
for char in cleanup_char:
|
||||||
|
render = render.replace(char, "")
|
||||||
|
r_int, g_int, b_int, cw_int, ww_int = map(int, render.split(",", 5))
|
||||||
|
elif isinstance(render, (list, tuple)) and len(render) == 5:
|
||||||
|
r_int, g_int, b_int, cw_int, ww_int = render
|
||||||
|
|
||||||
|
if all(
|
||||||
|
value is not None and isinstance(value, (int, float)) and 0 <= value <= 255
|
||||||
|
for value in (r_int, g_int, b_int, cw_int, ww_int)
|
||||||
|
):
|
||||||
|
self._rgbww_color = (r_int, g_int, b_int, cw_int, ww_int)
|
||||||
|
elif any(
|
||||||
|
isinstance(value, (int, float)) and not 0 <= value <= 255
|
||||||
|
for value in (r_int, g_int, b_int, cw_int, ww_int)
|
||||||
|
):
|
||||||
|
_LOGGER.error(
|
||||||
|
"Received invalid rgb_color : (%s, %s, %s, %s, %s) for entity %s. Expected: (0-255, 0-255, 0-255, 0-255)",
|
||||||
|
r_int,
|
||||||
|
g_int,
|
||||||
|
b_int,
|
||||||
|
cw_int,
|
||||||
|
ww_int,
|
||||||
|
self.entity_id,
|
||||||
|
)
|
||||||
|
self._rgbww_color = None
|
||||||
|
else:
|
||||||
|
_LOGGER.error(
|
||||||
|
"Received invalid rgb_color : (%s) for entity %s",
|
||||||
|
render,
|
||||||
|
self.entity_id,
|
||||||
|
)
|
||||||
|
self._rgbww_color = None
|
||||||
|
self._color_mode = ColorMode.RGBWW
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _update_max_mireds(self, render):
|
def _update_max_mireds(self, render):
|
||||||
|
@ -7,6 +7,9 @@ from homeassistant.components.light import (
|
|||||||
ATTR_COLOR_TEMP,
|
ATTR_COLOR_TEMP,
|
||||||
ATTR_EFFECT,
|
ATTR_EFFECT,
|
||||||
ATTR_HS_COLOR,
|
ATTR_HS_COLOR,
|
||||||
|
ATTR_RGB_COLOR,
|
||||||
|
ATTR_RGBW_COLOR,
|
||||||
|
ATTR_RGBWW_COLOR,
|
||||||
ATTR_TRANSITION,
|
ATTR_TRANSITION,
|
||||||
ColorMode,
|
ColorMode,
|
||||||
LightEntityFeature,
|
LightEntityFeature,
|
||||||
@ -72,7 +75,7 @@ OPTIMISTIC_COLOR_TEMP_LIGHT_CONFIG = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
OPTIMISTIC_HS_COLOR_LIGHT_CONFIG = {
|
OPTIMISTIC_LEGACY_COLOR_LIGHT_CONFIG = {
|
||||||
**OPTIMISTIC_ON_OFF_LIGHT_CONFIG,
|
**OPTIMISTIC_ON_OFF_LIGHT_CONFIG,
|
||||||
"set_color": {
|
"set_color": {
|
||||||
"service": "test.automation",
|
"service": "test.automation",
|
||||||
@ -86,6 +89,68 @@ OPTIMISTIC_HS_COLOR_LIGHT_CONFIG = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
OPTIMISTIC_HS_COLOR_LIGHT_CONFIG = {
|
||||||
|
**OPTIMISTIC_ON_OFF_LIGHT_CONFIG,
|
||||||
|
"set_hs": {
|
||||||
|
"service": "test.automation",
|
||||||
|
"data_template": {
|
||||||
|
"action": "set_hs",
|
||||||
|
"caller": "{{ this.entity_id }}",
|
||||||
|
"s": "{{s}}",
|
||||||
|
"h": "{{h}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
OPTIMISTIC_RGB_COLOR_LIGHT_CONFIG = {
|
||||||
|
**OPTIMISTIC_ON_OFF_LIGHT_CONFIG,
|
||||||
|
"set_rgb": {
|
||||||
|
"service": "test.automation",
|
||||||
|
"data_template": {
|
||||||
|
"action": "set_rgb",
|
||||||
|
"caller": "{{ this.entity_id }}",
|
||||||
|
"r": "{{r}}",
|
||||||
|
"g": "{{g}}",
|
||||||
|
"b": "{{b}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
OPTIMISTIC_RGBW_COLOR_LIGHT_CONFIG = {
|
||||||
|
**OPTIMISTIC_ON_OFF_LIGHT_CONFIG,
|
||||||
|
"set_rgbw": {
|
||||||
|
"service": "test.automation",
|
||||||
|
"data_template": {
|
||||||
|
"action": "set_rgbw",
|
||||||
|
"caller": "{{ this.entity_id }}",
|
||||||
|
"r": "{{r}}",
|
||||||
|
"g": "{{g}}",
|
||||||
|
"b": "{{b}}",
|
||||||
|
"w": "{{w}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
OPTIMISTIC_RGBWW_COLOR_LIGHT_CONFIG = {
|
||||||
|
**OPTIMISTIC_ON_OFF_LIGHT_CONFIG,
|
||||||
|
"set_rgbww": {
|
||||||
|
"service": "test.automation",
|
||||||
|
"data_template": {
|
||||||
|
"action": "set_rgbww",
|
||||||
|
"caller": "{{ this.entity_id }}",
|
||||||
|
"r": "{{r}}",
|
||||||
|
"g": "{{g}}",
|
||||||
|
"b": "{{b}}",
|
||||||
|
"cw": "{{cw}}",
|
||||||
|
"ww": "{{ww}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_light(hass, count, light_config):
|
async def async_setup_light(hass, count, light_config):
|
||||||
"""Do setup of light integration."""
|
"""Do setup of light integration."""
|
||||||
config = {"light": {"platform": "template", "lights": light_config}}
|
config = {"light": {"platform": "template", "lights": light_config}}
|
||||||
@ -607,6 +672,7 @@ async def test_level_action_no_template(
|
|||||||
"{{ state_attr('light.nolight', 'brightness') }}",
|
"{{ state_attr('light.nolight', 'brightness') }}",
|
||||||
ColorMode.BRIGHTNESS,
|
ColorMode.BRIGHTNESS,
|
||||||
),
|
),
|
||||||
|
(None, "{{'one'}}", ColorMode.BRIGHTNESS),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_level_template(
|
async def test_level_template(
|
||||||
@ -643,6 +709,7 @@ async def test_level_template(
|
|||||||
(None, "None", ColorMode.COLOR_TEMP),
|
(None, "None", ColorMode.COLOR_TEMP),
|
||||||
(None, "{{ none }}", ColorMode.COLOR_TEMP),
|
(None, "{{ none }}", ColorMode.COLOR_TEMP),
|
||||||
(None, "", ColorMode.COLOR_TEMP),
|
(None, "", ColorMode.COLOR_TEMP),
|
||||||
|
(None, "{{ 'one' }}", ColorMode.COLOR_TEMP),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_temperature_template(
|
async def test_temperature_template(
|
||||||
@ -797,17 +864,17 @@ async def test_entity_picture_template(hass: HomeAssistant, setup_light) -> None
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"test_template_light": {
|
"test_template_light": {
|
||||||
**OPTIMISTIC_HS_COLOR_LIGHT_CONFIG,
|
**OPTIMISTIC_LEGACY_COLOR_LIGHT_CONFIG,
|
||||||
"value_template": "{{1 == 1}}",
|
"value_template": "{{1 == 1}}",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_color_action_no_template(
|
async def test_legacy_color_action_no_template(
|
||||||
hass: HomeAssistant,
|
hass,
|
||||||
setup_light,
|
setup_light,
|
||||||
calls,
|
calls,
|
||||||
) -> None:
|
):
|
||||||
"""Test setting color with optimistic template."""
|
"""Test setting color with optimistic template."""
|
||||||
state = hass.states.get("light.test_template_light")
|
state = hass.states.get("light.test_template_light")
|
||||||
assert state.attributes.get("hs_color") is None
|
assert state.attributes.get("hs_color") is None
|
||||||
@ -833,6 +900,186 @@ async def test_color_action_no_template(
|
|||||||
assert state.attributes["supported_features"] == 0
|
assert state.attributes["supported_features"] == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("count", [1])
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"light_config",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"test_template_light": {
|
||||||
|
**OPTIMISTIC_HS_COLOR_LIGHT_CONFIG,
|
||||||
|
"value_template": "{{1 == 1}}",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_hs_color_action_no_template(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
setup_light,
|
||||||
|
calls,
|
||||||
|
) -> None:
|
||||||
|
"""Test setting hs color with optimistic template."""
|
||||||
|
state = hass.states.get("light.test_template_light")
|
||||||
|
assert state.attributes.get("hs_color") is None
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
light.DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: "light.test_template_light", ATTR_HS_COLOR: (40, 50)},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(calls) == 1
|
||||||
|
assert calls[-1].data["action"] == "set_hs"
|
||||||
|
assert calls[-1].data["caller"] == "light.test_template_light"
|
||||||
|
assert calls[-1].data["h"] == 40
|
||||||
|
assert calls[-1].data["s"] == 50
|
||||||
|
|
||||||
|
state = hass.states.get("light.test_template_light")
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert state.attributes["color_mode"] == ColorMode.HS
|
||||||
|
assert state.attributes.get("hs_color") == (40, 50)
|
||||||
|
assert state.attributes["supported_color_modes"] == [ColorMode.HS]
|
||||||
|
assert state.attributes["supported_features"] == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("count", [1])
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"light_config",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"test_template_light": {
|
||||||
|
**OPTIMISTIC_RGB_COLOR_LIGHT_CONFIG,
|
||||||
|
"value_template": "{{1 == 1}}",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_rgb_color_action_no_template(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
setup_light,
|
||||||
|
calls,
|
||||||
|
) -> None:
|
||||||
|
"""Test setting rgb color with optimistic template."""
|
||||||
|
state = hass.states.get("light.test_template_light")
|
||||||
|
assert state.attributes.get("rgb_color") is None
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
light.DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: "light.test_template_light", ATTR_RGB_COLOR: (160, 78, 192)},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(calls) == 1
|
||||||
|
assert calls[-1].data["action"] == "set_rgb"
|
||||||
|
assert calls[-1].data["caller"] == "light.test_template_light"
|
||||||
|
assert calls[-1].data["r"] == 160
|
||||||
|
assert calls[-1].data["g"] == 78
|
||||||
|
assert calls[-1].data["b"] == 192
|
||||||
|
|
||||||
|
state = hass.states.get("light.test_template_light")
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert state.attributes["color_mode"] == ColorMode.RGB
|
||||||
|
assert state.attributes.get("rgb_color") == (160, 78, 192)
|
||||||
|
assert state.attributes["supported_color_modes"] == [ColorMode.RGB]
|
||||||
|
assert state.attributes["supported_features"] == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("count", [1])
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"light_config",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"test_template_light": {
|
||||||
|
**OPTIMISTIC_RGBW_COLOR_LIGHT_CONFIG,
|
||||||
|
"value_template": "{{1 == 1}}",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_rgbw_color_action_no_template(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
setup_light,
|
||||||
|
calls,
|
||||||
|
) -> None:
|
||||||
|
"""Test setting rgbw color with optimistic template."""
|
||||||
|
state = hass.states.get("light.test_template_light")
|
||||||
|
assert state.attributes.get("rgbw_color") is None
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
light.DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: "light.test_template_light",
|
||||||
|
ATTR_RGBW_COLOR: (160, 78, 192, 25),
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(calls) == 1
|
||||||
|
assert calls[-1].data["action"] == "set_rgbw"
|
||||||
|
assert calls[-1].data["caller"] == "light.test_template_light"
|
||||||
|
assert calls[-1].data["r"] == 160
|
||||||
|
assert calls[-1].data["g"] == 78
|
||||||
|
assert calls[-1].data["b"] == 192
|
||||||
|
assert calls[-1].data["w"] == 25
|
||||||
|
|
||||||
|
state = hass.states.get("light.test_template_light")
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert state.attributes["color_mode"] == ColorMode.RGBW
|
||||||
|
assert state.attributes.get("rgbw_color") == (160, 78, 192, 25)
|
||||||
|
assert state.attributes["supported_color_modes"] == [ColorMode.RGBW]
|
||||||
|
assert state.attributes["supported_features"] == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("count", [1])
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"light_config",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"test_template_light": {
|
||||||
|
**OPTIMISTIC_RGBWW_COLOR_LIGHT_CONFIG,
|
||||||
|
"value_template": "{{1 == 1}}",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_rgbww_color_action_no_template(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
setup_light,
|
||||||
|
calls,
|
||||||
|
) -> None:
|
||||||
|
"""Test setting rgbww color with optimistic template."""
|
||||||
|
state = hass.states.get("light.test_template_light")
|
||||||
|
assert state.attributes.get("rgbww_color") is None
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
light.DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: "light.test_template_light",
|
||||||
|
ATTR_RGBWW_COLOR: (160, 78, 192, 25, 55),
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(calls) == 1
|
||||||
|
assert calls[-1].data["action"] == "set_rgbww"
|
||||||
|
assert calls[-1].data["caller"] == "light.test_template_light"
|
||||||
|
assert calls[-1].data["r"] == 160
|
||||||
|
assert calls[-1].data["g"] == 78
|
||||||
|
assert calls[-1].data["b"] == 192
|
||||||
|
assert calls[-1].data["cw"] == 25
|
||||||
|
assert calls[-1].data["ww"] == 55
|
||||||
|
|
||||||
|
state = hass.states.get("light.test_template_light")
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert state.attributes["color_mode"] == ColorMode.RGBWW
|
||||||
|
assert state.attributes.get("rgbww_color") == (160, 78, 192, 25, 55)
|
||||||
|
assert state.attributes["supported_color_modes"] == [ColorMode.RGBWW]
|
||||||
|
assert state.attributes["supported_features"] == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("count", [1])
|
@pytest.mark.parametrize("count", [1])
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("expected_hs", "color_template", "expected_color_mode"),
|
("expected_hs", "color_template", "expected_color_mode"),
|
||||||
@ -845,19 +1092,20 @@ async def test_color_action_no_template(
|
|||||||
(None, "{{x - 12}}", ColorMode.HS),
|
(None, "{{x - 12}}", ColorMode.HS),
|
||||||
(None, "", ColorMode.HS),
|
(None, "", ColorMode.HS),
|
||||||
(None, "{{ none }}", ColorMode.HS),
|
(None, "{{ none }}", ColorMode.HS),
|
||||||
|
(None, "{{('one','two')}}", ColorMode.HS),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_color_template(
|
async def test_legacy_color_template(
|
||||||
hass: HomeAssistant,
|
hass,
|
||||||
expected_hs,
|
expected_hs,
|
||||||
expected_color_mode,
|
expected_color_mode,
|
||||||
count,
|
count,
|
||||||
color_template,
|
color_template,
|
||||||
) -> None:
|
):
|
||||||
"""Test the template for the color."""
|
"""Test the template for the color."""
|
||||||
light_config = {
|
light_config = {
|
||||||
"test_template_light": {
|
"test_template_light": {
|
||||||
**OPTIMISTIC_HS_COLOR_LIGHT_CONFIG,
|
**OPTIMISTIC_LEGACY_COLOR_LIGHT_CONFIG,
|
||||||
"value_template": "{{ 1 == 1 }}",
|
"value_template": "{{ 1 == 1 }}",
|
||||||
"color_template": color_template,
|
"color_template": color_template,
|
||||||
}
|
}
|
||||||
@ -871,6 +1119,176 @@ async def test_color_template(
|
|||||||
assert state.attributes["supported_features"] == 0
|
assert state.attributes["supported_features"] == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("count", [1])
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("expected_hs", "hs_template", "expected_color_mode"),
|
||||||
|
[
|
||||||
|
((360, 100), "{{(360, 100)}}", ColorMode.HS),
|
||||||
|
((360, 100), "(360, 100)", ColorMode.HS),
|
||||||
|
((359.9, 99.9), "{{(359.9, 99.9)}}", ColorMode.HS),
|
||||||
|
(None, "{{(361, 100)}}", ColorMode.HS),
|
||||||
|
(None, "{{(360, 101)}}", ColorMode.HS),
|
||||||
|
(None, "[{{(360)}},{{null}}]", ColorMode.HS),
|
||||||
|
(None, "{{x - 12}}", ColorMode.HS),
|
||||||
|
(None, "", ColorMode.HS),
|
||||||
|
(None, "{{ none }}", ColorMode.HS),
|
||||||
|
(None, "{{('one','two')}}", ColorMode.HS),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_hs_template(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
expected_hs,
|
||||||
|
expected_color_mode,
|
||||||
|
count,
|
||||||
|
hs_template,
|
||||||
|
) -> None:
|
||||||
|
"""Test the template for the color."""
|
||||||
|
light_config = {
|
||||||
|
"test_template_light": {
|
||||||
|
**OPTIMISTIC_HS_COLOR_LIGHT_CONFIG,
|
||||||
|
"value_template": "{{ 1 == 1 }}",
|
||||||
|
"hs_template": hs_template,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await async_setup_light(hass, count, light_config)
|
||||||
|
state = hass.states.get("light.test_template_light")
|
||||||
|
assert state.attributes.get("hs_color") == expected_hs
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert state.attributes["color_mode"] == expected_color_mode
|
||||||
|
assert state.attributes["supported_color_modes"] == [ColorMode.HS]
|
||||||
|
assert state.attributes["supported_features"] == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("count", [1])
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("expected_rgb", "rgb_template", "expected_color_mode"),
|
||||||
|
[
|
||||||
|
((160, 78, 192), "{{(160, 78, 192)}}", ColorMode.RGB),
|
||||||
|
((160, 78, 192), "{{[160, 78, 192]}}", ColorMode.RGB),
|
||||||
|
((160, 78, 192), "(160, 78, 192)", ColorMode.RGB),
|
||||||
|
((159, 77, 191), "{{(159.9, 77.9, 191.9)}}", ColorMode.RGB),
|
||||||
|
(None, "{{(256, 100, 100)}}", ColorMode.RGB),
|
||||||
|
(None, "{{(100, 256, 100)}}", ColorMode.RGB),
|
||||||
|
(None, "{{(100, 100, 256)}}", ColorMode.RGB),
|
||||||
|
(None, "{{x - 12}}", ColorMode.RGB),
|
||||||
|
(None, "", ColorMode.RGB),
|
||||||
|
(None, "{{ none }}", ColorMode.RGB),
|
||||||
|
(None, "{{('one','two','tree')}}", ColorMode.RGB),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_rgb_template(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
expected_rgb,
|
||||||
|
expected_color_mode,
|
||||||
|
count,
|
||||||
|
rgb_template,
|
||||||
|
) -> None:
|
||||||
|
"""Test the template for the color."""
|
||||||
|
light_config = {
|
||||||
|
"test_template_light": {
|
||||||
|
**OPTIMISTIC_RGB_COLOR_LIGHT_CONFIG,
|
||||||
|
"value_template": "{{ 1 == 1 }}",
|
||||||
|
"rgb_template": rgb_template,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await async_setup_light(hass, count, light_config)
|
||||||
|
state = hass.states.get("light.test_template_light")
|
||||||
|
assert state.attributes.get("rgb_color") == expected_rgb
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert state.attributes["color_mode"] == expected_color_mode
|
||||||
|
assert state.attributes["supported_color_modes"] == [ColorMode.RGB]
|
||||||
|
assert state.attributes["supported_features"] == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("count", [1])
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("expected_rgbw", "rgbw_template", "expected_color_mode"),
|
||||||
|
[
|
||||||
|
((160, 78, 192, 25), "{{(160, 78, 192, 25)}}", ColorMode.RGBW),
|
||||||
|
((160, 78, 192, 25), "{{[160, 78, 192, 25]}}", ColorMode.RGBW),
|
||||||
|
((160, 78, 192, 25), "(160, 78, 192, 25)", ColorMode.RGBW),
|
||||||
|
((159, 77, 191, 24), "{{(159.9, 77.9, 191.9, 24.9)}}", ColorMode.RGBW),
|
||||||
|
(None, "{{(256, 100, 100, 100)}}", ColorMode.RGBW),
|
||||||
|
(None, "{{(100, 256, 100, 100)}}", ColorMode.RGBW),
|
||||||
|
(None, "{{(100, 100, 256, 100)}}", ColorMode.RGBW),
|
||||||
|
(None, "{{(100, 100, 100, 256)}}", ColorMode.RGBW),
|
||||||
|
(None, "{{x - 12}}", ColorMode.RGBW),
|
||||||
|
(None, "", ColorMode.RGBW),
|
||||||
|
(None, "{{ none }}", ColorMode.RGBW),
|
||||||
|
(None, "{{('one','two','tree','four')}}", ColorMode.RGBW),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_rgbw_template(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
expected_rgbw,
|
||||||
|
expected_color_mode,
|
||||||
|
count,
|
||||||
|
rgbw_template,
|
||||||
|
) -> None:
|
||||||
|
"""Test the template for the color."""
|
||||||
|
light_config = {
|
||||||
|
"test_template_light": {
|
||||||
|
**OPTIMISTIC_RGBW_COLOR_LIGHT_CONFIG,
|
||||||
|
"value_template": "{{ 1 == 1 }}",
|
||||||
|
"rgbw_template": rgbw_template,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await async_setup_light(hass, count, light_config)
|
||||||
|
state = hass.states.get("light.test_template_light")
|
||||||
|
assert state.attributes.get("rgbw_color") == expected_rgbw
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert state.attributes["color_mode"] == expected_color_mode
|
||||||
|
assert state.attributes["supported_color_modes"] == [ColorMode.RGBW]
|
||||||
|
assert state.attributes["supported_features"] == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("count", [1])
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("expected_rgbww", "rgbww_template", "expected_color_mode"),
|
||||||
|
[
|
||||||
|
((160, 78, 192, 25, 55), "{{(160, 78, 192, 25, 55)}}", ColorMode.RGBWW),
|
||||||
|
((160, 78, 192, 25, 55), "(160, 78, 192, 25, 55)", ColorMode.RGBWW),
|
||||||
|
((160, 78, 192, 25, 55), "{{[160, 78, 192, 25, 55]}}", ColorMode.RGBWW),
|
||||||
|
(
|
||||||
|
(159, 77, 191, 24, 54),
|
||||||
|
"{{(159.9, 77.9, 191.9, 24.9, 54.9)}}",
|
||||||
|
ColorMode.RGBWW,
|
||||||
|
),
|
||||||
|
(None, "{{(256, 100, 100, 100, 100)}}", ColorMode.RGBWW),
|
||||||
|
(None, "{{(100, 256, 100, 100, 100)}}", ColorMode.RGBWW),
|
||||||
|
(None, "{{(100, 100, 256, 100, 100)}}", ColorMode.RGBWW),
|
||||||
|
(None, "{{(100, 100, 100, 256, 100)}}", ColorMode.RGBWW),
|
||||||
|
(None, "{{(100, 100, 100, 100, 256)}}", ColorMode.RGBWW),
|
||||||
|
(None, "{{x - 12}}", ColorMode.RGBWW),
|
||||||
|
(None, "", ColorMode.RGBWW),
|
||||||
|
(None, "{{ none }}", ColorMode.RGBWW),
|
||||||
|
(None, "{{('one','two','tree','four','five')}}", ColorMode.RGBWW),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_rgbww_template(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
expected_rgbww,
|
||||||
|
expected_color_mode,
|
||||||
|
count,
|
||||||
|
rgbww_template,
|
||||||
|
) -> None:
|
||||||
|
"""Test the template for the color."""
|
||||||
|
light_config = {
|
||||||
|
"test_template_light": {
|
||||||
|
**OPTIMISTIC_RGBWW_COLOR_LIGHT_CONFIG,
|
||||||
|
"value_template": "{{ 1 == 1 }}",
|
||||||
|
"rgbww_template": rgbww_template,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await async_setup_light(hass, count, light_config)
|
||||||
|
state = hass.states.get("light.test_template_light")
|
||||||
|
assert state.attributes.get("rgbww_color") == expected_rgbww
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert state.attributes["color_mode"] == expected_color_mode
|
||||||
|
assert state.attributes["supported_color_modes"] == [ColorMode.RGBWW]
|
||||||
|
assert state.attributes["supported_features"] == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("count", [1])
|
@pytest.mark.parametrize("count", [1])
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"light_config",
|
"light_config",
|
||||||
@ -879,8 +1297,7 @@ async def test_color_template(
|
|||||||
"test_template_light": {
|
"test_template_light": {
|
||||||
**OPTIMISTIC_ON_OFF_LIGHT_CONFIG,
|
**OPTIMISTIC_ON_OFF_LIGHT_CONFIG,
|
||||||
"value_template": "{{1 == 1}}",
|
"value_template": "{{1 == 1}}",
|
||||||
"set_color": [
|
"set_hs": {
|
||||||
{
|
|
||||||
"service": "test.automation",
|
"service": "test.automation",
|
||||||
"data_template": {
|
"data_template": {
|
||||||
"entity_id": "test.test_state",
|
"entity_id": "test.test_state",
|
||||||
@ -888,7 +1305,6 @@ async def test_color_template(
|
|||||||
"s": "{{s}}",
|
"s": "{{s}}",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
|
||||||
"set_temperature": {
|
"set_temperature": {
|
||||||
"service": "test.automation",
|
"service": "test.automation",
|
||||||
"data_template": {
|
"data_template": {
|
||||||
@ -896,18 +1312,48 @@ async def test_color_template(
|
|||||||
"color_temp": "{{color_temp}}",
|
"color_temp": "{{color_temp}}",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"set_rgb": {
|
||||||
|
"service": "test.automation",
|
||||||
|
"data_template": {
|
||||||
|
"entity_id": "test.test_state",
|
||||||
|
"r": "{{r}}",
|
||||||
|
"g": "{{g}}",
|
||||||
|
"b": "{{b}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"set_rgbw": {
|
||||||
|
"service": "test.automation",
|
||||||
|
"data_template": {
|
||||||
|
"entity_id": "test.test_state",
|
||||||
|
"r": "{{r}}",
|
||||||
|
"g": "{{g}}",
|
||||||
|
"b": "{{b}}",
|
||||||
|
"w": "{{w}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"set_rgbww": {
|
||||||
|
"service": "test.automation",
|
||||||
|
"data_template": {
|
||||||
|
"entity_id": "test.test_state",
|
||||||
|
"r": "{{r}}",
|
||||||
|
"g": "{{g}}",
|
||||||
|
"b": "{{b}}",
|
||||||
|
"cw": "{{cw}}",
|
||||||
|
"ww": "{{ww}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_color_and_temperature_actions_no_template(
|
async def test_all_colors_mode_no_template(
|
||||||
hass: HomeAssistant, setup_light, calls
|
hass: HomeAssistant, setup_light, calls
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test setting color and color temperature with optimistic template."""
|
"""Test setting color and color temperature with optimistic template."""
|
||||||
state = hass.states.get("light.test_template_light")
|
state = hass.states.get("light.test_template_light")
|
||||||
assert state.attributes.get("hs_color") is None
|
assert state.attributes.get("hs_color") is None
|
||||||
|
|
||||||
# Optimistically set color, light should be in hs_color mode
|
# Optimistically set hs color, light should be in hs_color mode
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
light.DOMAIN,
|
light.DOMAIN,
|
||||||
SERVICE_TURN_ON,
|
SERVICE_TURN_ON,
|
||||||
@ -926,6 +1372,9 @@ async def test_color_and_temperature_actions_no_template(
|
|||||||
assert state.attributes["supported_color_modes"] == [
|
assert state.attributes["supported_color_modes"] == [
|
||||||
ColorMode.COLOR_TEMP,
|
ColorMode.COLOR_TEMP,
|
||||||
ColorMode.HS,
|
ColorMode.HS,
|
||||||
|
ColorMode.RGB,
|
||||||
|
ColorMode.RGBW,
|
||||||
|
ColorMode.RGBWW,
|
||||||
]
|
]
|
||||||
assert state.attributes["supported_features"] == 0
|
assert state.attributes["supported_features"] == 0
|
||||||
|
|
||||||
@ -947,10 +1396,100 @@ async def test_color_and_temperature_actions_no_template(
|
|||||||
assert state.attributes["supported_color_modes"] == [
|
assert state.attributes["supported_color_modes"] == [
|
||||||
ColorMode.COLOR_TEMP,
|
ColorMode.COLOR_TEMP,
|
||||||
ColorMode.HS,
|
ColorMode.HS,
|
||||||
|
ColorMode.RGB,
|
||||||
|
ColorMode.RGBW,
|
||||||
|
ColorMode.RGBWW,
|
||||||
]
|
]
|
||||||
assert state.attributes["supported_features"] == 0
|
assert state.attributes["supported_features"] == 0
|
||||||
|
|
||||||
# Optimistically set color, light should again be in hs_color mode
|
# Optimistically set rgb color, light should be in rgb_color mode
|
||||||
|
await hass.services.async_call(
|
||||||
|
light.DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: "light.test_template_light", ATTR_RGB_COLOR: (160, 78, 192)},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(calls) == 3
|
||||||
|
assert calls[-1].data["r"] == 160
|
||||||
|
assert calls[-1].data["g"] == 78
|
||||||
|
assert calls[-1].data["b"] == 192
|
||||||
|
|
||||||
|
state = hass.states.get("light.test_template_light")
|
||||||
|
assert state.attributes["color_mode"] == ColorMode.RGB
|
||||||
|
assert state.attributes["color_temp"] is None
|
||||||
|
assert state.attributes["rgb_color"] == (160, 78, 192)
|
||||||
|
assert state.attributes["supported_color_modes"] == [
|
||||||
|
ColorMode.COLOR_TEMP,
|
||||||
|
ColorMode.HS,
|
||||||
|
ColorMode.RGB,
|
||||||
|
ColorMode.RGBW,
|
||||||
|
ColorMode.RGBWW,
|
||||||
|
]
|
||||||
|
assert state.attributes["supported_features"] == 0
|
||||||
|
|
||||||
|
# Optimistically set rgbw color, light should be in rgb_color mode
|
||||||
|
await hass.services.async_call(
|
||||||
|
light.DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: "light.test_template_light",
|
||||||
|
ATTR_RGBW_COLOR: (160, 78, 192, 25),
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(calls) == 4
|
||||||
|
assert calls[-1].data["r"] == 160
|
||||||
|
assert calls[-1].data["g"] == 78
|
||||||
|
assert calls[-1].data["b"] == 192
|
||||||
|
assert calls[-1].data["w"] == 25
|
||||||
|
|
||||||
|
state = hass.states.get("light.test_template_light")
|
||||||
|
assert state.attributes["color_mode"] == ColorMode.RGBW
|
||||||
|
assert state.attributes["color_temp"] is None
|
||||||
|
assert state.attributes["rgbw_color"] == (160, 78, 192, 25)
|
||||||
|
assert state.attributes["supported_color_modes"] == [
|
||||||
|
ColorMode.COLOR_TEMP,
|
||||||
|
ColorMode.HS,
|
||||||
|
ColorMode.RGB,
|
||||||
|
ColorMode.RGBW,
|
||||||
|
ColorMode.RGBWW,
|
||||||
|
]
|
||||||
|
assert state.attributes["supported_features"] == 0
|
||||||
|
|
||||||
|
# Optimistically set rgbww color, light should be in rgb_color mode
|
||||||
|
await hass.services.async_call(
|
||||||
|
light.DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: "light.test_template_light",
|
||||||
|
ATTR_RGBWW_COLOR: (160, 78, 192, 25, 55),
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(calls) == 5
|
||||||
|
assert calls[-1].data["r"] == 160
|
||||||
|
assert calls[-1].data["g"] == 78
|
||||||
|
assert calls[-1].data["b"] == 192
|
||||||
|
assert calls[-1].data["cw"] == 25
|
||||||
|
assert calls[-1].data["ww"] == 55
|
||||||
|
|
||||||
|
state = hass.states.get("light.test_template_light")
|
||||||
|
assert state.attributes["color_mode"] == ColorMode.RGBWW
|
||||||
|
assert state.attributes["color_temp"] is None
|
||||||
|
assert state.attributes["rgbww_color"] == (160, 78, 192, 25, 55)
|
||||||
|
assert state.attributes["supported_color_modes"] == [
|
||||||
|
ColorMode.COLOR_TEMP,
|
||||||
|
ColorMode.HS,
|
||||||
|
ColorMode.RGB,
|
||||||
|
ColorMode.RGBW,
|
||||||
|
ColorMode.RGBWW,
|
||||||
|
]
|
||||||
|
assert state.attributes["supported_features"] == 0
|
||||||
|
|
||||||
|
# Optimistically set hs color, light should again be in hs_color mode
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
light.DOMAIN,
|
light.DOMAIN,
|
||||||
SERVICE_TURN_ON,
|
SERVICE_TURN_ON,
|
||||||
@ -958,7 +1497,7 @@ async def test_color_and_temperature_actions_no_template(
|
|||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert len(calls) == 3
|
assert len(calls) == 6
|
||||||
assert calls[-1].data["h"] == 10
|
assert calls[-1].data["h"] == 10
|
||||||
assert calls[-1].data["s"] == 20
|
assert calls[-1].data["s"] == 20
|
||||||
|
|
||||||
@ -969,6 +1508,9 @@ async def test_color_and_temperature_actions_no_template(
|
|||||||
assert state.attributes["supported_color_modes"] == [
|
assert state.attributes["supported_color_modes"] == [
|
||||||
ColorMode.COLOR_TEMP,
|
ColorMode.COLOR_TEMP,
|
||||||
ColorMode.HS,
|
ColorMode.HS,
|
||||||
|
ColorMode.RGB,
|
||||||
|
ColorMode.RGBW,
|
||||||
|
ColorMode.RGBWW,
|
||||||
]
|
]
|
||||||
assert state.attributes["supported_features"] == 0
|
assert state.attributes["supported_features"] == 0
|
||||||
|
|
||||||
@ -980,7 +1522,7 @@ async def test_color_and_temperature_actions_no_template(
|
|||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert len(calls) == 4
|
assert len(calls) == 7
|
||||||
assert calls[-1].data["color_temp"] == 234
|
assert calls[-1].data["color_temp"] == 234
|
||||||
|
|
||||||
state = hass.states.get("light.test_template_light")
|
state = hass.states.get("light.test_template_light")
|
||||||
@ -990,6 +1532,9 @@ async def test_color_and_temperature_actions_no_template(
|
|||||||
assert state.attributes["supported_color_modes"] == [
|
assert state.attributes["supported_color_modes"] == [
|
||||||
ColorMode.COLOR_TEMP,
|
ColorMode.COLOR_TEMP,
|
||||||
ColorMode.HS,
|
ColorMode.HS,
|
||||||
|
ColorMode.RGB,
|
||||||
|
ColorMode.RGBW,
|
||||||
|
ColorMode.RGBWW,
|
||||||
]
|
]
|
||||||
assert state.attributes["supported_features"] == 0
|
assert state.attributes["supported_features"] == 0
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user