mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 19:27:45 +00:00
Add trigger based template lights (#140631)
* Add abstract template light class in preparation for trigger based template lights * add base for trigger entity * Update more tests * revert trigger template entity changes and light trigger tests. * fix merge conflicts * address comments * change function name * nitpick * fix merge conflict issue --------- Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
parent
5da57271b2
commit
95552e9a5b
@ -134,9 +134,7 @@ CONFIG_SECTION_SCHEMA = vol.All(
|
||||
),
|
||||
},
|
||||
),
|
||||
ensure_domains_do_not_have_trigger_or_action(
|
||||
BUTTON_DOMAIN, COVER_DOMAIN, LIGHT_DOMAIN
|
||||
),
|
||||
ensure_domains_do_not_have_trigger_or_action(BUTTON_DOMAIN, COVER_DOMAIN),
|
||||
)
|
||||
|
||||
TEMPLATE_BLUEPRINT_SCHEMA = vol.All(
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Generator, Sequence
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
@ -18,6 +19,7 @@ from homeassistant.components.light import (
|
||||
ATTR_TRANSITION,
|
||||
DEFAULT_MAX_KELVIN,
|
||||
DEFAULT_MIN_KELVIN,
|
||||
DOMAIN as LIGHT_DOMAIN,
|
||||
ENTITY_ID_FORMAT,
|
||||
PLATFORM_SCHEMA as LIGHT_PLATFORM_SCHEMA,
|
||||
ColorMode,
|
||||
@ -46,6 +48,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
from homeassistant.util import color as color_util
|
||||
|
||||
from . import TriggerUpdateCoordinator
|
||||
from .const import CONF_OBJECT_ID, CONF_PICTURE, DOMAIN
|
||||
from .template_entity import (
|
||||
LEGACY_FIELDS as TEMPLATE_ENTITY_LEGACY_FIELDS,
|
||||
@ -55,6 +58,7 @@ from .template_entity import (
|
||||
TemplateEntity,
|
||||
rewrite_common_legacy_to_modern_conf,
|
||||
)
|
||||
from .trigger_entity import TriggerEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_VALID_STATES = [STATE_ON, STATE_OFF, "true", "false"]
|
||||
@ -253,6 +257,13 @@ async def async_setup_platform(
|
||||
)
|
||||
return
|
||||
|
||||
if "coordinator" in discovery_info:
|
||||
async_add_entities(
|
||||
TriggerLightEntity(hass, discovery_info["coordinator"], config)
|
||||
for config in discovery_info["entities"]
|
||||
)
|
||||
return
|
||||
|
||||
_async_create_template_tracking_entities(
|
||||
async_add_entities,
|
||||
hass,
|
||||
@ -261,27 +272,17 @@ async def async_setup_platform(
|
||||
)
|
||||
|
||||
|
||||
class LightTemplate(TemplateEntity, LightEntity):
|
||||
"""Representation of a templated Light, including dimmable."""
|
||||
|
||||
_attr_should_poll = False
|
||||
class AbstractTemplateLight(LightEntity):
|
||||
"""Representation of a template lights features."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config: dict[str, Any],
|
||||
unique_id: str | None,
|
||||
self, config: dict[str, Any], initial_state: bool | None = False
|
||||
) -> None:
|
||||
"""Initialize the light."""
|
||||
super().__init__(hass, config=config, fallback_name=None, unique_id=unique_id)
|
||||
if (object_id := config.get(CONF_OBJECT_ID)) is not None:
|
||||
self.entity_id = async_generate_entity_id(
|
||||
ENTITY_ID_FORMAT, object_id, hass=hass
|
||||
)
|
||||
name = self._attr_name
|
||||
if TYPE_CHECKING:
|
||||
assert name is not None
|
||||
"""Initialize the features."""
|
||||
|
||||
self._registered_scripts: list[str] = []
|
||||
|
||||
# Template attributes
|
||||
self._template = config.get(CONF_STATE)
|
||||
self._level_template = config.get(CONF_LEVEL)
|
||||
self._temperature_template = config.get(CONF_TEMPERATURE)
|
||||
@ -295,12 +296,8 @@ class LightTemplate(TemplateEntity, LightEntity):
|
||||
self._min_mireds_template = config.get(CONF_MIN_MIREDS)
|
||||
self._supports_transition_template = config.get(CONF_SUPPORTS_TRANSITION)
|
||||
|
||||
for action_id in (CONF_ON_ACTION, CONF_OFF_ACTION, CONF_EFFECT_ACTION):
|
||||
# Scripts can be an empty list, therefore we need to check for None
|
||||
if (action_config := config.get(action_id)) is not None:
|
||||
self.add_script(action_id, action_config, name, DOMAIN)
|
||||
|
||||
self._state = False
|
||||
# Stored values for template attributes
|
||||
self._state = initial_state
|
||||
self._brightness = None
|
||||
self._temperature: int | None = None
|
||||
self._hs_color = None
|
||||
@ -309,14 +306,19 @@ class LightTemplate(TemplateEntity, LightEntity):
|
||||
self._rgbww_color = None
|
||||
self._effect = None
|
||||
self._effect_list = None
|
||||
self._color_mode = None
|
||||
self._max_mireds = None
|
||||
self._min_mireds = None
|
||||
self._supports_transition = False
|
||||
self._supported_color_modes = None
|
||||
self._color_mode: ColorMode | None = None
|
||||
self._supported_color_modes: set[ColorMode] | None = None
|
||||
|
||||
color_modes = {ColorMode.ONOFF}
|
||||
def _register_scripts(
|
||||
self, config: dict[str, Any]
|
||||
) -> Generator[tuple[str, Sequence[dict[str, Any]], ColorMode | None]]:
|
||||
for action_id, color_mode in (
|
||||
(CONF_ON_ACTION, None),
|
||||
(CONF_OFF_ACTION, None),
|
||||
(CONF_EFFECT_ACTION, None),
|
||||
(CONF_TEMPERATURE_ACTION, ColorMode.COLOR_TEMP),
|
||||
(CONF_LEVEL_ACTION, ColorMode.BRIGHTNESS),
|
||||
(CONF_HS_ACTION, ColorMode.HS),
|
||||
@ -324,21 +326,9 @@ class LightTemplate(TemplateEntity, LightEntity):
|
||||
(CONF_RGBW_ACTION, ColorMode.RGBW),
|
||||
(CONF_RGBWW_ACTION, ColorMode.RGBWW),
|
||||
):
|
||||
# Scripts can be an empty list, therefore we need to check for None
|
||||
if (action_config := config.get(action_id)) is not None:
|
||||
self.add_script(action_id, action_config, name, DOMAIN)
|
||||
color_modes.add(color_mode)
|
||||
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:
|
||||
self._color_mode = next(iter(self._supported_color_modes))
|
||||
|
||||
self._attr_supported_features = LightEntityFeature(0)
|
||||
if (self._action_scripts.get(CONF_EFFECT_ACTION)) is not None:
|
||||
self._attr_supported_features |= LightEntityFeature.EFFECT
|
||||
if self._supports_transition is True:
|
||||
self._attr_supported_features |= LightEntityFeature.TRANSITION
|
||||
self._registered_scripts.append(action_id)
|
||||
yield (action_id, action_config, color_mode)
|
||||
|
||||
@property
|
||||
def brightness(self) -> int | None:
|
||||
@ -413,107 +403,12 @@ class LightTemplate(TemplateEntity, LightEntity):
|
||||
"""Return true if device is on."""
|
||||
return self._state
|
||||
|
||||
@callback
|
||||
def _async_setup_templates(self) -> None:
|
||||
"""Set up templates."""
|
||||
if self._template:
|
||||
self.add_template_attribute(
|
||||
"_state", self._template, None, self._update_state
|
||||
)
|
||||
if self._level_template:
|
||||
self.add_template_attribute(
|
||||
"_brightness",
|
||||
self._level_template,
|
||||
None,
|
||||
self._update_brightness,
|
||||
none_on_template_error=True,
|
||||
)
|
||||
if self._max_mireds_template:
|
||||
self.add_template_attribute(
|
||||
"_max_mireds_template",
|
||||
self._max_mireds_template,
|
||||
None,
|
||||
self._update_max_mireds,
|
||||
none_on_template_error=True,
|
||||
)
|
||||
if self._min_mireds_template:
|
||||
self.add_template_attribute(
|
||||
"_min_mireds_template",
|
||||
self._min_mireds_template,
|
||||
None,
|
||||
self._update_min_mireds,
|
||||
none_on_template_error=True,
|
||||
)
|
||||
if self._temperature_template:
|
||||
self.add_template_attribute(
|
||||
"_temperature",
|
||||
self._temperature_template,
|
||||
None,
|
||||
self._update_temperature,
|
||||
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,
|
||||
)
|
||||
if self._effect_list_template:
|
||||
self.add_template_attribute(
|
||||
"_effect_list",
|
||||
self._effect_list_template,
|
||||
None,
|
||||
self._update_effect_list,
|
||||
none_on_template_error=True,
|
||||
)
|
||||
if self._effect_template:
|
||||
self.add_template_attribute(
|
||||
"_effect",
|
||||
self._effect_template,
|
||||
None,
|
||||
self._update_effect,
|
||||
none_on_template_error=True,
|
||||
)
|
||||
if self._supports_transition_template:
|
||||
self.add_template_attribute(
|
||||
"_supports_transition_template",
|
||||
self._supports_transition_template,
|
||||
None,
|
||||
self._update_supports_transition,
|
||||
none_on_template_error=True,
|
||||
)
|
||||
super()._async_setup_templates()
|
||||
def set_optimistic_attributes(self, **kwargs) -> bool: # noqa: C901
|
||||
"""Update attributes which should be set optimistically.
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None: # noqa: C901
|
||||
"""Turn the light on."""
|
||||
Returns True if any attribute was updated.
|
||||
"""
|
||||
optimistic_set = False
|
||||
# set optimistic states
|
||||
if self._template is None:
|
||||
self._state = True
|
||||
optimistic_set = True
|
||||
@ -613,6 +508,10 @@ class LightTemplate(TemplateEntity, LightEntity):
|
||||
self._rgbw_color = None
|
||||
optimistic_set = True
|
||||
|
||||
return optimistic_set
|
||||
|
||||
def get_registered_script(self, **kwargs) -> tuple[str, dict]:
|
||||
"""Get registered script for turn_on."""
|
||||
common_params = {}
|
||||
|
||||
if ATTR_BRIGHTNESS in kwargs:
|
||||
@ -621,24 +520,23 @@ class LightTemplate(TemplateEntity, LightEntity):
|
||||
if ATTR_TRANSITION in kwargs and self._supports_transition is True:
|
||||
common_params["transition"] = kwargs[ATTR_TRANSITION]
|
||||
|
||||
if ATTR_COLOR_TEMP_KELVIN in kwargs and (
|
||||
temperature_script := self._action_scripts.get(CONF_TEMPERATURE_ACTION)
|
||||
if (
|
||||
ATTR_COLOR_TEMP_KELVIN in kwargs
|
||||
and (script := CONF_TEMPERATURE_ACTION) in self._registered_scripts
|
||||
):
|
||||
common_params["color_temp"] = color_util.color_temperature_kelvin_to_mired(
|
||||
kwargs[ATTR_COLOR_TEMP_KELVIN]
|
||||
)
|
||||
|
||||
await self.async_run_script(
|
||||
temperature_script,
|
||||
run_variables=common_params,
|
||||
context=self._context,
|
||||
)
|
||||
elif ATTR_EFFECT in kwargs and (
|
||||
effect_script := self._action_scripts.get(CONF_EFFECT_ACTION)
|
||||
return (script, common_params)
|
||||
|
||||
if (
|
||||
ATTR_EFFECT in kwargs
|
||||
and (script := CONF_EFFECT_ACTION) in self._registered_scripts
|
||||
):
|
||||
assert self._effect_list is not None
|
||||
effect = kwargs[ATTR_EFFECT]
|
||||
if effect not in self._effect_list:
|
||||
if self._effect_list is not None and effect not in self._effect_list:
|
||||
_LOGGER.error(
|
||||
"Received invalid effect: %s for entity %s. Expected one of: %s",
|
||||
effect,
|
||||
@ -649,22 +547,22 @@ class LightTemplate(TemplateEntity, LightEntity):
|
||||
|
||||
common_params["effect"] = effect
|
||||
|
||||
await self.async_run_script(
|
||||
effect_script, run_variables=common_params, context=self._context
|
||||
)
|
||||
elif ATTR_HS_COLOR in kwargs and (
|
||||
hs_script := self._action_scripts.get(CONF_HS_ACTION)
|
||||
return (script, common_params)
|
||||
|
||||
if (
|
||||
ATTR_HS_COLOR in kwargs
|
||||
and (script := CONF_HS_ACTION) in self._registered_scripts
|
||||
):
|
||||
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(
|
||||
hs_script, run_variables=common_params, context=self._context
|
||||
)
|
||||
elif ATTR_RGBWW_COLOR in kwargs and (
|
||||
rgbww_script := self._action_scripts.get(CONF_RGBWW_ACTION)
|
||||
return (script, common_params)
|
||||
|
||||
if (
|
||||
ATTR_RGBWW_COLOR in kwargs
|
||||
and (script := CONF_RGBWW_ACTION) in self._registered_scripts
|
||||
):
|
||||
rgbww_value = kwargs[ATTR_RGBWW_COLOR]
|
||||
common_params["rgbww"] = rgbww_value
|
||||
@ -679,11 +577,11 @@ class LightTemplate(TemplateEntity, LightEntity):
|
||||
common_params["cw"] = int(rgbww_value[3])
|
||||
common_params["ww"] = int(rgbww_value[4])
|
||||
|
||||
await self.async_run_script(
|
||||
rgbww_script, run_variables=common_params, context=self._context
|
||||
)
|
||||
elif ATTR_RGBW_COLOR in kwargs and (
|
||||
rgbw_script := self._action_scripts.get(CONF_RGBW_ACTION)
|
||||
return (script, common_params)
|
||||
|
||||
if (
|
||||
ATTR_RGBW_COLOR in kwargs
|
||||
and (script := CONF_RGBW_ACTION) in self._registered_scripts
|
||||
):
|
||||
rgbw_value = kwargs[ATTR_RGBW_COLOR]
|
||||
common_params["rgbw"] = rgbw_value
|
||||
@ -697,11 +595,11 @@ class LightTemplate(TemplateEntity, LightEntity):
|
||||
common_params["b"] = int(rgbw_value[2])
|
||||
common_params["w"] = int(rgbw_value[3])
|
||||
|
||||
await self.async_run_script(
|
||||
rgbw_script, run_variables=common_params, context=self._context
|
||||
)
|
||||
elif ATTR_RGB_COLOR in kwargs and (
|
||||
rgb_script := self._action_scripts.get(CONF_RGB_ACTION)
|
||||
return (script, common_params)
|
||||
|
||||
if (
|
||||
ATTR_RGB_COLOR in kwargs
|
||||
and (script := CONF_RGB_ACTION) in self._registered_scripts
|
||||
):
|
||||
rgb_value = kwargs[ATTR_RGB_COLOR]
|
||||
common_params["rgb"] = rgb_value
|
||||
@ -709,39 +607,15 @@ class LightTemplate(TemplateEntity, LightEntity):
|
||||
common_params["g"] = int(rgb_value[1])
|
||||
common_params["b"] = int(rgb_value[2])
|
||||
|
||||
await self.async_run_script(
|
||||
rgb_script, run_variables=common_params, context=self._context
|
||||
)
|
||||
elif ATTR_BRIGHTNESS in kwargs and (
|
||||
level_script := self._action_scripts.get(CONF_LEVEL_ACTION)
|
||||
return (script, common_params)
|
||||
|
||||
if (
|
||||
ATTR_BRIGHTNESS in kwargs
|
||||
and (script := CONF_LEVEL_ACTION) in self._registered_scripts
|
||||
):
|
||||
await self.async_run_script(
|
||||
level_script, run_variables=common_params, context=self._context
|
||||
)
|
||||
else:
|
||||
await self.async_run_script(
|
||||
self._action_scripts[CONF_ON_ACTION],
|
||||
run_variables=common_params,
|
||||
context=self._context,
|
||||
)
|
||||
return (script, common_params)
|
||||
|
||||
if optimistic_set:
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the light off."""
|
||||
off_script = self._action_scripts[CONF_OFF_ACTION]
|
||||
if ATTR_TRANSITION in kwargs and self._supports_transition is True:
|
||||
await self.async_run_script(
|
||||
off_script,
|
||||
run_variables={"transition": kwargs[ATTR_TRANSITION]},
|
||||
context=self._context,
|
||||
)
|
||||
else:
|
||||
await self.async_run_script(off_script, context=self._context)
|
||||
if self._template is None:
|
||||
self._state = False
|
||||
self.async_write_ha_state()
|
||||
return (CONF_ON_ACTION, common_params)
|
||||
|
||||
@callback
|
||||
def _update_brightness(self, brightness):
|
||||
@ -809,33 +683,6 @@ class LightTemplate(TemplateEntity, LightEntity):
|
||||
|
||||
self._effect = effect
|
||||
|
||||
@callback
|
||||
def _update_state(self, result):
|
||||
"""Update the state from the template."""
|
||||
if isinstance(result, TemplateError):
|
||||
# This behavior is legacy
|
||||
self._state = False
|
||||
if not self._availability_template:
|
||||
self._attr_available = True
|
||||
return
|
||||
|
||||
if isinstance(result, bool):
|
||||
self._state = result
|
||||
return
|
||||
|
||||
state = str(result).lower()
|
||||
if state in _VALID_STATES:
|
||||
self._state = state in ("true", STATE_ON)
|
||||
return
|
||||
|
||||
_LOGGER.error(
|
||||
"Received invalid light is_on state: %s for entity %s. Expected: %s",
|
||||
state,
|
||||
self.entity_id,
|
||||
", ".join(_VALID_STATES),
|
||||
)
|
||||
self._state = None
|
||||
|
||||
@callback
|
||||
def _update_temperature(self, render):
|
||||
"""Update the temperature from the template."""
|
||||
@ -1092,3 +939,338 @@ class LightTemplate(TemplateEntity, LightEntity):
|
||||
self._supports_transition = bool(render)
|
||||
if self._supports_transition:
|
||||
self._attr_supported_features |= LightEntityFeature.TRANSITION
|
||||
|
||||
|
||||
class LightTemplate(TemplateEntity, AbstractTemplateLight):
|
||||
"""Representation of a templated Light, including dimmable."""
|
||||
|
||||
_attr_should_poll = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config: dict[str, Any],
|
||||
unique_id: str | None,
|
||||
) -> None:
|
||||
"""Initialize the light."""
|
||||
TemplateEntity.__init__(
|
||||
self, hass, config=config, fallback_name=None, unique_id=unique_id
|
||||
)
|
||||
AbstractTemplateLight.__init__(self, config)
|
||||
if (object_id := config.get(CONF_OBJECT_ID)) is not None:
|
||||
self.entity_id = async_generate_entity_id(
|
||||
ENTITY_ID_FORMAT, object_id, hass=hass
|
||||
)
|
||||
name = self._attr_name
|
||||
if TYPE_CHECKING:
|
||||
assert name is not None
|
||||
|
||||
color_modes = {ColorMode.ONOFF}
|
||||
for action_id, action_config, color_mode in self._register_scripts(config):
|
||||
self.add_script(action_id, action_config, name, DOMAIN)
|
||||
if color_mode:
|
||||
color_modes.add(color_mode)
|
||||
|
||||
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:
|
||||
self._color_mode = next(iter(self._supported_color_modes))
|
||||
|
||||
self._attr_supported_features = LightEntityFeature(0)
|
||||
if self._action_scripts.get(CONF_EFFECT_ACTION):
|
||||
self._attr_supported_features |= LightEntityFeature.EFFECT
|
||||
if self._supports_transition is True:
|
||||
self._attr_supported_features |= LightEntityFeature.TRANSITION
|
||||
|
||||
@callback
|
||||
def _async_setup_templates(self) -> None:
|
||||
"""Set up templates."""
|
||||
if self._template:
|
||||
self.add_template_attribute(
|
||||
"_state", self._template, None, self._update_state
|
||||
)
|
||||
if self._level_template:
|
||||
self.add_template_attribute(
|
||||
"_brightness",
|
||||
self._level_template,
|
||||
None,
|
||||
self._update_brightness,
|
||||
none_on_template_error=True,
|
||||
)
|
||||
if self._max_mireds_template:
|
||||
self.add_template_attribute(
|
||||
"_max_mireds_template",
|
||||
self._max_mireds_template,
|
||||
None,
|
||||
self._update_max_mireds,
|
||||
none_on_template_error=True,
|
||||
)
|
||||
if self._min_mireds_template:
|
||||
self.add_template_attribute(
|
||||
"_min_mireds_template",
|
||||
self._min_mireds_template,
|
||||
None,
|
||||
self._update_min_mireds,
|
||||
none_on_template_error=True,
|
||||
)
|
||||
if self._temperature_template:
|
||||
self.add_template_attribute(
|
||||
"_temperature",
|
||||
self._temperature_template,
|
||||
None,
|
||||
self._update_temperature,
|
||||
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,
|
||||
)
|
||||
if self._effect_list_template:
|
||||
self.add_template_attribute(
|
||||
"_effect_list",
|
||||
self._effect_list_template,
|
||||
None,
|
||||
self._update_effect_list,
|
||||
none_on_template_error=True,
|
||||
)
|
||||
if self._effect_template:
|
||||
self.add_template_attribute(
|
||||
"_effect",
|
||||
self._effect_template,
|
||||
None,
|
||||
self._update_effect,
|
||||
none_on_template_error=True,
|
||||
)
|
||||
if self._supports_transition_template:
|
||||
self.add_template_attribute(
|
||||
"_supports_transition_template",
|
||||
self._supports_transition_template,
|
||||
None,
|
||||
self._update_supports_transition,
|
||||
none_on_template_error=True,
|
||||
)
|
||||
super()._async_setup_templates()
|
||||
|
||||
@callback
|
||||
def _update_state(self, result):
|
||||
"""Update the state from the template."""
|
||||
if isinstance(result, TemplateError):
|
||||
# This behavior is legacy
|
||||
self._state = False
|
||||
if not self._availability_template:
|
||||
self._attr_available = True
|
||||
return
|
||||
|
||||
if isinstance(result, bool):
|
||||
self._state = result
|
||||
return
|
||||
|
||||
state = str(result).lower()
|
||||
if state in _VALID_STATES:
|
||||
self._state = state in ("true", STATE_ON)
|
||||
return
|
||||
|
||||
_LOGGER.error(
|
||||
"Received invalid light is_on state: %s for entity %s. Expected: %s",
|
||||
state,
|
||||
self.entity_id,
|
||||
", ".join(_VALID_STATES),
|
||||
)
|
||||
self._state = None
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the light on."""
|
||||
optimistic_set = self.set_optimistic_attributes(**kwargs)
|
||||
script_id, script_params = self.get_registered_script(**kwargs)
|
||||
await self.async_run_script(
|
||||
self._action_scripts[script_id],
|
||||
run_variables=script_params,
|
||||
context=self._context,
|
||||
)
|
||||
|
||||
if optimistic_set:
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the light off."""
|
||||
off_script = self._action_scripts[CONF_OFF_ACTION]
|
||||
if ATTR_TRANSITION in kwargs and self._supports_transition is True:
|
||||
await self.async_run_script(
|
||||
off_script,
|
||||
run_variables={"transition": kwargs[ATTR_TRANSITION]},
|
||||
context=self._context,
|
||||
)
|
||||
else:
|
||||
await self.async_run_script(off_script, context=self._context)
|
||||
if self._template is None:
|
||||
self._state = False
|
||||
self.async_write_ha_state()
|
||||
|
||||
|
||||
class TriggerLightEntity(TriggerEntity, AbstractTemplateLight):
|
||||
"""Light entity based on trigger data."""
|
||||
|
||||
domain = LIGHT_DOMAIN
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
coordinator: TriggerUpdateCoordinator,
|
||||
config: ConfigType,
|
||||
) -> None:
|
||||
"""Initialize the entity."""
|
||||
TriggerEntity.__init__(self, hass, coordinator, config)
|
||||
AbstractTemplateLight.__init__(self, config, None)
|
||||
|
||||
# Render the _attr_name before initializing TemplateLightEntity
|
||||
self._attr_name = name = self._rendered.get(CONF_NAME, DEFAULT_NAME)
|
||||
|
||||
self._optimistic_attrs: dict[str, str] = {}
|
||||
self._optimistic = True
|
||||
for key in (
|
||||
CONF_STATE,
|
||||
CONF_LEVEL,
|
||||
CONF_TEMPERATURE,
|
||||
CONF_RGB,
|
||||
CONF_RGBW,
|
||||
CONF_RGBWW,
|
||||
CONF_EFFECT,
|
||||
CONF_MAX_MIREDS,
|
||||
CONF_MIN_MIREDS,
|
||||
CONF_SUPPORTS_TRANSITION,
|
||||
):
|
||||
if isinstance(config.get(key), template.Template):
|
||||
if key == CONF_STATE:
|
||||
self._optimistic = False
|
||||
self._to_render_simple.append(key)
|
||||
self._parse_result.add(key)
|
||||
|
||||
for key in (CONF_EFFECT_LIST, CONF_HS):
|
||||
if isinstance(config.get(key), template.Template):
|
||||
self._to_render_complex.append(key)
|
||||
self._parse_result.add(key)
|
||||
|
||||
color_modes = {ColorMode.ONOFF}
|
||||
for action_id, action_config, color_mode in self._register_scripts(config):
|
||||
self.add_script(action_id, action_config, name, DOMAIN)
|
||||
if color_mode:
|
||||
color_modes.add(color_mode)
|
||||
|
||||
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:
|
||||
self._color_mode = next(iter(self._supported_color_modes))
|
||||
|
||||
self._attr_supported_features = LightEntityFeature(0)
|
||||
if self._action_scripts.get(CONF_EFFECT_ACTION):
|
||||
self._attr_supported_features |= LightEntityFeature.EFFECT
|
||||
if self._supports_transition is True:
|
||||
self._attr_supported_features |= LightEntityFeature.TRANSITION
|
||||
|
||||
@callback
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
"""Handle update of the data."""
|
||||
self._process_data()
|
||||
|
||||
if not self.available:
|
||||
self.async_write_ha_state()
|
||||
return
|
||||
|
||||
write_ha_state = False
|
||||
for key, updater in (
|
||||
(CONF_LEVEL, self._update_brightness),
|
||||
(CONF_EFFECT_LIST, self._update_effect_list),
|
||||
(CONF_EFFECT, self._update_effect),
|
||||
(CONF_TEMPERATURE, self._update_temperature),
|
||||
(CONF_HS, self._update_hs),
|
||||
(CONF_RGB, self._update_rgb),
|
||||
(CONF_RGBW, self._update_rgbw),
|
||||
(CONF_RGBWW, self._update_rgbww),
|
||||
(CONF_MAX_MIREDS, self._update_max_mireds),
|
||||
(CONF_MIN_MIREDS, self._update_min_mireds),
|
||||
):
|
||||
if (rendered := self._rendered.get(key)) is not None:
|
||||
updater(rendered)
|
||||
write_ha_state = True
|
||||
|
||||
if (rendered := self._rendered.get(CONF_SUPPORTS_TRANSITION)) is not None:
|
||||
self._update_supports_transition(rendered)
|
||||
write_ha_state = True
|
||||
|
||||
if not self._optimistic:
|
||||
raw = self._rendered.get(CONF_STATE)
|
||||
self._state = template.result_as_boolean(raw)
|
||||
|
||||
self.async_set_context(self.coordinator.data["context"])
|
||||
write_ha_state = True
|
||||
elif self._optimistic and len(self._rendered) > 0:
|
||||
# In case any non optimistic template
|
||||
write_ha_state = True
|
||||
|
||||
if write_ha_state:
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the light on."""
|
||||
optimistic_set = self.set_optimistic_attributes(**kwargs)
|
||||
script_id, script_params = self.get_registered_script(**kwargs)
|
||||
if self._template and self._state is None:
|
||||
# Ensure an optimistic state is set on the entity when turn_on
|
||||
# is called and the main state hasn't rendered. This will only
|
||||
# occur when the state is unknown, the template hasn't triggered,
|
||||
# and turn_on is called.
|
||||
self._state = True
|
||||
|
||||
await self.async_run_script(
|
||||
self._action_scripts[script_id],
|
||||
run_variables=script_params,
|
||||
context=self._context,
|
||||
)
|
||||
|
||||
if optimistic_set:
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the light off."""
|
||||
off_script = self._action_scripts[CONF_OFF_ACTION]
|
||||
if ATTR_TRANSITION in kwargs and self._supports_transition is True:
|
||||
await self.async_run_script(
|
||||
off_script,
|
||||
run_variables={"transition": kwargs[ATTR_TRANSITION]},
|
||||
context=self._context,
|
||||
)
|
||||
else:
|
||||
await self.async_run_script(off_script, context=self._context)
|
||||
if self._template is None:
|
||||
self._state = False
|
||||
self.async_write_ha_state()
|
||||
|
@ -25,6 +25,7 @@ from homeassistant.const import (
|
||||
STATE_OFF,
|
||||
STATE_ON,
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, ServiceCall
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
@ -159,6 +160,20 @@ OPTIMISTIC_RGBWW_COLOR_LIGHT_CONFIG = {
|
||||
}
|
||||
|
||||
|
||||
TEST_STATE_TRIGGER = {
|
||||
"trigger": {"trigger": "state", "entity_id": "light.test_state"},
|
||||
"variables": {"triggering_entity": "{{ trigger.entity_id }}"},
|
||||
"action": [{"event": "action_event", "event_data": {"what": "triggering_entity"}}],
|
||||
}
|
||||
|
||||
|
||||
TEST_EVENT_TRIGGER = {
|
||||
"trigger": {"platform": "event", "event_type": "test_event"},
|
||||
"variables": {"type": "{{ trigger.event.data.type }}"},
|
||||
"action": [{"event": "action_event", "event_data": {"type": "{{ type }}"}}],
|
||||
}
|
||||
|
||||
|
||||
TEST_MISSING_KEY_CONFIG = {
|
||||
"turn_on": {
|
||||
"service": "light.turn_on",
|
||||
@ -434,7 +449,7 @@ async def async_setup_legacy_format_with_attribute(
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_new_format(
|
||||
async def async_setup_modern_format(
|
||||
hass: HomeAssistant, count: int, light_config: dict[str, Any]
|
||||
) -> None:
|
||||
"""Do setup of light integration via new format."""
|
||||
@ -461,7 +476,51 @@ async def async_setup_modern_format_with_attribute(
|
||||
) -> None:
|
||||
"""Do setup of a legacy light that has a single templated attribute."""
|
||||
extra = {attribute: attribute_template} if attribute and attribute_template else {}
|
||||
await async_setup_new_format(
|
||||
await async_setup_modern_format(
|
||||
hass,
|
||||
count,
|
||||
{
|
||||
"name": "test_template_light",
|
||||
**extra_config,
|
||||
"state": "{{ 1 == 1 }}",
|
||||
**extra,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_trigger_format(
|
||||
hass: HomeAssistant, count: int, light_config: dict[str, Any]
|
||||
) -> None:
|
||||
"""Do setup of light integration via new format."""
|
||||
config = {
|
||||
"template": {
|
||||
**TEST_STATE_TRIGGER,
|
||||
"light": light_config,
|
||||
}
|
||||
}
|
||||
|
||||
with assert_setup_component(count, template.DOMAIN):
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
template.DOMAIN,
|
||||
config,
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_start()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def async_setup_trigger_format_with_attribute(
|
||||
hass: HomeAssistant,
|
||||
count: int,
|
||||
attribute: str,
|
||||
attribute_template: str,
|
||||
extra_config: dict,
|
||||
) -> None:
|
||||
"""Do setup of a legacy light that has a single templated attribute."""
|
||||
extra = {attribute: attribute_template} if attribute and attribute_template else {}
|
||||
await async_setup_trigger_format(
|
||||
hass,
|
||||
count,
|
||||
{
|
||||
@ -484,7 +543,9 @@ async def setup_light(
|
||||
if style == ConfigurationStyle.LEGACY:
|
||||
await async_setup_legacy_format(hass, count, light_config)
|
||||
elif style == ConfigurationStyle.MODERN:
|
||||
await async_setup_new_format(hass, count, light_config)
|
||||
await async_setup_modern_format(hass, count, light_config)
|
||||
elif style == ConfigurationStyle.TRIGGER:
|
||||
await async_setup_trigger_format(hass, count, light_config)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -507,7 +568,17 @@ async def setup_state_light(
|
||||
},
|
||||
)
|
||||
elif style == ConfigurationStyle.MODERN:
|
||||
await async_setup_new_format(
|
||||
await async_setup_modern_format(
|
||||
hass,
|
||||
count,
|
||||
{
|
||||
**OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG,
|
||||
"name": "test_template_light",
|
||||
"state": state_template,
|
||||
},
|
||||
)
|
||||
elif style == ConfigurationStyle.TRIGGER:
|
||||
await async_setup_trigger_format(
|
||||
hass,
|
||||
count,
|
||||
{
|
||||
@ -536,6 +607,10 @@ async def setup_single_attribute_light(
|
||||
await async_setup_modern_format_with_attribute(
|
||||
hass, count, attribute, attribute_template, extra_config
|
||||
)
|
||||
elif style == ConfigurationStyle.TRIGGER:
|
||||
await async_setup_trigger_format_with_attribute(
|
||||
hass, count, attribute, attribute_template, extra_config
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -554,6 +629,10 @@ async def setup_single_action_light(
|
||||
await async_setup_modern_format_with_attribute(
|
||||
hass, count, "", "", extra_config
|
||||
)
|
||||
elif style == ConfigurationStyle.TRIGGER:
|
||||
await async_setup_trigger_format_with_attribute(
|
||||
hass, count, "", "", extra_config
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -579,7 +658,7 @@ async def setup_empty_action_light(
|
||||
},
|
||||
)
|
||||
elif style == ConfigurationStyle.MODERN:
|
||||
await async_setup_new_format(
|
||||
await async_setup_modern_format(
|
||||
hass,
|
||||
count,
|
||||
{
|
||||
@ -627,7 +706,20 @@ async def setup_light_with_effects(
|
||||
},
|
||||
)
|
||||
elif style == ConfigurationStyle.MODERN:
|
||||
await async_setup_new_format(
|
||||
await async_setup_modern_format(
|
||||
hass,
|
||||
count,
|
||||
{
|
||||
"name": "test_template_light",
|
||||
**OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG,
|
||||
"state": "{{true}}",
|
||||
**common,
|
||||
"effect_list": effect_list_template,
|
||||
"effect": effect_template,
|
||||
},
|
||||
)
|
||||
elif style == ConfigurationStyle.TRIGGER:
|
||||
await async_setup_trigger_format(
|
||||
hass,
|
||||
count,
|
||||
{
|
||||
@ -674,7 +766,19 @@ async def setup_light_with_mireds(
|
||||
},
|
||||
)
|
||||
elif style == ConfigurationStyle.MODERN:
|
||||
await async_setup_new_format(
|
||||
await async_setup_modern_format(
|
||||
hass,
|
||||
count,
|
||||
{
|
||||
"name": "test_template_light",
|
||||
**OPTIMISTIC_ON_OFF_LIGHT_CONFIG,
|
||||
"state": "{{ 1 == 1 }}",
|
||||
**common,
|
||||
"temperature": "{{200}}",
|
||||
},
|
||||
)
|
||||
elif style == ConfigurationStyle.TRIGGER:
|
||||
await async_setup_trigger_format(
|
||||
hass,
|
||||
count,
|
||||
{
|
||||
@ -720,7 +824,21 @@ async def setup_light_with_transition_template(
|
||||
},
|
||||
)
|
||||
elif style == ConfigurationStyle.MODERN:
|
||||
await async_setup_new_format(
|
||||
await async_setup_modern_format(
|
||||
hass,
|
||||
count,
|
||||
{
|
||||
"name": "test_template_light",
|
||||
**OPTIMISTIC_COLOR_TEMP_LIGHT_CONFIG,
|
||||
"state": "{{ 1 == 1 }}",
|
||||
**common,
|
||||
"effect_list": "{{ ['Disco', 'Police'] }}",
|
||||
"effect": "{{ None }}",
|
||||
"supports_transition": transition_template,
|
||||
},
|
||||
)
|
||||
elif style == ConfigurationStyle.TRIGGER:
|
||||
await async_setup_trigger_format(
|
||||
hass,
|
||||
count,
|
||||
{
|
||||
@ -741,19 +859,24 @@ async def setup_light_with_transition_template(
|
||||
[(0, [ColorMode.BRIGHTNESS])],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"style",
|
||||
("style", "expected_state"),
|
||||
[
|
||||
ConfigurationStyle.LEGACY,
|
||||
ConfigurationStyle.MODERN,
|
||||
(ConfigurationStyle.LEGACY, STATE_OFF),
|
||||
(ConfigurationStyle.MODERN, STATE_OFF),
|
||||
(ConfigurationStyle.TRIGGER, STATE_UNKNOWN),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize("state_template", ["{{states.test['big.fat...']}}"])
|
||||
async def test_template_state_invalid(
|
||||
hass: HomeAssistant, supported_features, supported_color_modes, setup_state_light
|
||||
hass: HomeAssistant,
|
||||
supported_features,
|
||||
supported_color_modes,
|
||||
expected_state,
|
||||
setup_state_light,
|
||||
) -> None:
|
||||
"""Test template state with render error."""
|
||||
state = hass.states.get("light.test_template_light")
|
||||
assert state.state == STATE_OFF
|
||||
assert state.state == expected_state
|
||||
assert state.attributes["color_mode"] is None
|
||||
assert state.attributes["supported_color_modes"] == supported_color_modes
|
||||
assert state.attributes["supported_features"] == supported_features
|
||||
@ -765,6 +888,7 @@ async def test_template_state_invalid(
|
||||
[
|
||||
ConfigurationStyle.LEGACY,
|
||||
ConfigurationStyle.MODERN,
|
||||
ConfigurationStyle.TRIGGER,
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize("state_template", ["{{ states.light.test_state.state }}"])
|
||||
@ -795,6 +919,7 @@ async def test_template_state_text(hass: HomeAssistant, setup_state_light) -> No
|
||||
[
|
||||
ConfigurationStyle.LEGACY,
|
||||
ConfigurationStyle.MODERN,
|
||||
ConfigurationStyle.TRIGGER,
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
@ -812,13 +937,18 @@ async def test_template_state_text(hass: HomeAssistant, setup_state_light) -> No
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_legacy_template_state_boolean(
|
||||
async def test_template_state_boolean(
|
||||
hass: HomeAssistant,
|
||||
expected_color_mode,
|
||||
expected_state,
|
||||
style,
|
||||
setup_state_light,
|
||||
) -> None:
|
||||
"""Test the setting of the state with boolean on."""
|
||||
if style == ConfigurationStyle.TRIGGER:
|
||||
hass.states.async_set("light.test_state", expected_state)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("light.test_template_light")
|
||||
assert state.state == expected_state
|
||||
assert state.attributes.get("color_mode") == expected_color_mode
|
||||
@ -860,6 +990,14 @@ async def test_legacy_template_state_boolean(
|
||||
},
|
||||
ConfigurationStyle.MODERN,
|
||||
),
|
||||
(
|
||||
{
|
||||
**OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG,
|
||||
"name": "test_template_light",
|
||||
"state": "{%- if false -%}",
|
||||
},
|
||||
ConfigurationStyle.TRIGGER,
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_template_config_errors(hass: HomeAssistant, setup_light) -> None:
|
||||
@ -880,6 +1018,11 @@ async def test_template_config_errors(hass: HomeAssistant, setup_light) -> None:
|
||||
ConfigurationStyle.MODERN,
|
||||
0,
|
||||
),
|
||||
(
|
||||
{"name": "light_one", "state": "{{ 1== 1}}", **TEST_MISSING_KEY_CONFIG},
|
||||
ConfigurationStyle.TRIGGER,
|
||||
0,
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_missing_key(hass: HomeAssistant, count, setup_light) -> None:
|
||||
@ -896,6 +1039,7 @@ async def test_missing_key(hass: HomeAssistant, count, setup_light) -> None:
|
||||
[
|
||||
ConfigurationStyle.LEGACY,
|
||||
ConfigurationStyle.MODERN,
|
||||
ConfigurationStyle.TRIGGER,
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize("state_template", ["{{ states.light.test_state.state }}"])
|
||||
@ -946,11 +1090,21 @@ async def test_on_action(
|
||||
(
|
||||
{
|
||||
"name": "test_template_light",
|
||||
"state": "{{states.light.test_state.state}}",
|
||||
**TEST_ON_ACTION_WITH_TRANSITION_CONFIG,
|
||||
"supports_transition": "{{true}}",
|
||||
},
|
||||
ConfigurationStyle.MODERN,
|
||||
),
|
||||
(
|
||||
{
|
||||
"name": "test_template_light",
|
||||
"state": "{{states.light.test_state.state}}",
|
||||
**TEST_ON_ACTION_WITH_TRANSITION_CONFIG,
|
||||
"supports_transition": "{{true}}",
|
||||
},
|
||||
ConfigurationStyle.TRIGGER,
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_on_action_with_transition(
|
||||
@ -984,7 +1138,7 @@ async def test_on_action_with_transition(
|
||||
|
||||
@pytest.mark.parametrize("count", [1])
|
||||
@pytest.mark.parametrize(
|
||||
("light_config", "style"),
|
||||
("light_config", "style", "initial_state"),
|
||||
[
|
||||
(
|
||||
{
|
||||
@ -993,6 +1147,7 @@ async def test_on_action_with_transition(
|
||||
}
|
||||
},
|
||||
ConfigurationStyle.LEGACY,
|
||||
STATE_OFF,
|
||||
),
|
||||
(
|
||||
{
|
||||
@ -1000,11 +1155,21 @@ async def test_on_action_with_transition(
|
||||
**OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG,
|
||||
},
|
||||
ConfigurationStyle.MODERN,
|
||||
STATE_OFF,
|
||||
),
|
||||
(
|
||||
{
|
||||
"name": "test_template_light",
|
||||
**OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG,
|
||||
},
|
||||
ConfigurationStyle.TRIGGER,
|
||||
STATE_UNKNOWN,
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_on_action_optimistic(
|
||||
hass: HomeAssistant,
|
||||
initial_state: str,
|
||||
setup_light,
|
||||
calls: list[ServiceCall],
|
||||
) -> None:
|
||||
@ -1013,7 +1178,7 @@ async def test_on_action_optimistic(
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("light.test_template_light")
|
||||
assert state.state == STATE_OFF
|
||||
assert state.state == initial_state
|
||||
assert state.attributes["color_mode"] is None
|
||||
assert state.attributes["supported_color_modes"] == [ColorMode.BRIGHTNESS]
|
||||
assert state.attributes["supported_features"] == 0
|
||||
@ -1058,6 +1223,7 @@ async def test_on_action_optimistic(
|
||||
[
|
||||
ConfigurationStyle.LEGACY,
|
||||
ConfigurationStyle.MODERN,
|
||||
ConfigurationStyle.TRIGGER,
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize("state_template", ["{{ states.light.test_state.state }}"])
|
||||
@ -1113,6 +1279,15 @@ async def test_off_action(
|
||||
},
|
||||
ConfigurationStyle.MODERN,
|
||||
),
|
||||
(
|
||||
{
|
||||
"name": "test_template_light",
|
||||
"state": "{{states.light.test_state.state}}",
|
||||
**TEST_OFF_ACTION_WITH_TRANSITION_CONFIG,
|
||||
"supports_transition": "{{true}}",
|
||||
},
|
||||
ConfigurationStyle.TRIGGER,
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_off_action_with_transition(
|
||||
@ -1145,7 +1320,7 @@ async def test_off_action_with_transition(
|
||||
|
||||
@pytest.mark.parametrize("count", [1])
|
||||
@pytest.mark.parametrize(
|
||||
("light_config", "style"),
|
||||
("light_config", "style", "initial_state"),
|
||||
[
|
||||
(
|
||||
{
|
||||
@ -1154,6 +1329,7 @@ async def test_off_action_with_transition(
|
||||
}
|
||||
},
|
||||
ConfigurationStyle.LEGACY,
|
||||
STATE_OFF,
|
||||
),
|
||||
(
|
||||
{
|
||||
@ -1161,15 +1337,24 @@ async def test_off_action_with_transition(
|
||||
**OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG,
|
||||
},
|
||||
ConfigurationStyle.MODERN,
|
||||
STATE_OFF,
|
||||
),
|
||||
(
|
||||
{
|
||||
"name": "test_template_light",
|
||||
**OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG,
|
||||
},
|
||||
ConfigurationStyle.TRIGGER,
|
||||
STATE_UNKNOWN,
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_off_action_optimistic(
|
||||
hass: HomeAssistant, setup_light, calls: list[ServiceCall]
|
||||
hass: HomeAssistant, initial_state, setup_light, calls: list[ServiceCall]
|
||||
) -> None:
|
||||
"""Test off action with optimistic state."""
|
||||
state = hass.states.get("light.test_template_light")
|
||||
assert state.state == STATE_OFF
|
||||
assert state.state == initial_state
|
||||
assert state.attributes["color_mode"] is None
|
||||
assert state.attributes["supported_color_modes"] == [ColorMode.BRIGHTNESS]
|
||||
assert state.attributes["supported_features"] == 0
|
||||
@ -1195,6 +1380,7 @@ async def test_off_action_optimistic(
|
||||
[
|
||||
ConfigurationStyle.LEGACY,
|
||||
ConfigurationStyle.MODERN,
|
||||
ConfigurationStyle.TRIGGER,
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize("state_template", ["{{1 == 1}}"])
|
||||
@ -1235,6 +1421,7 @@ async def test_level_action_no_template(
|
||||
[
|
||||
(ConfigurationStyle.LEGACY, "level_template"),
|
||||
(ConfigurationStyle.MODERN, "level"),
|
||||
(ConfigurationStyle.TRIGGER, "level"),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
@ -1255,14 +1442,20 @@ async def test_level_action_no_template(
|
||||
)
|
||||
async def test_level_template(
|
||||
hass: HomeAssistant,
|
||||
style: ConfigurationStyle,
|
||||
expected_level: Any,
|
||||
expected_color_mode: ColorMode,
|
||||
setup_single_attribute_light,
|
||||
) -> None:
|
||||
"""Test the template for the level."""
|
||||
if style == ConfigurationStyle.TRIGGER:
|
||||
hass.states.async_set("light.test_state", STATE_ON)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("light.test_template_light")
|
||||
assert state.attributes.get("brightness") == expected_level
|
||||
assert state.state == STATE_ON
|
||||
|
||||
assert state.attributes["color_mode"] == expected_color_mode
|
||||
assert state.attributes["supported_color_modes"] == [ColorMode.BRIGHTNESS]
|
||||
assert state.attributes["supported_features"] == 0
|
||||
@ -1276,6 +1469,7 @@ async def test_level_template(
|
||||
[
|
||||
(ConfigurationStyle.LEGACY, "temperature_template"),
|
||||
(ConfigurationStyle.MODERN, "temperature"),
|
||||
(ConfigurationStyle.TRIGGER, "temperature"),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
@ -1292,15 +1486,20 @@ async def test_level_template(
|
||||
)
|
||||
async def test_temperature_template(
|
||||
hass: HomeAssistant,
|
||||
style: ConfigurationStyle,
|
||||
expected_temp: Any,
|
||||
expected_color_mode: ColorMode,
|
||||
setup_single_attribute_light,
|
||||
) -> None:
|
||||
"""Test the template for the temperature."""
|
||||
if style == ConfigurationStyle.TRIGGER:
|
||||
hass.states.async_set("light.test_state", STATE_ON)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("light.test_template_light")
|
||||
assert state.attributes.get("color_temp") == expected_temp
|
||||
assert state.state == STATE_ON
|
||||
assert state.attributes["color_mode"] == expected_color_mode
|
||||
assert state.attributes.get("color_mode") == expected_color_mode
|
||||
assert state.attributes["supported_color_modes"] == [ColorMode.COLOR_TEMP]
|
||||
assert state.attributes["supported_features"] == 0
|
||||
|
||||
@ -1313,6 +1512,7 @@ async def test_temperature_template(
|
||||
[
|
||||
ConfigurationStyle.LEGACY,
|
||||
ConfigurationStyle.MODERN,
|
||||
ConfigurationStyle.TRIGGER,
|
||||
],
|
||||
)
|
||||
async def test_temperature_action_no_template(
|
||||
@ -1369,6 +1569,15 @@ async def test_temperature_action_no_template(
|
||||
ConfigurationStyle.MODERN,
|
||||
"light.template_light",
|
||||
),
|
||||
(
|
||||
{
|
||||
**OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG,
|
||||
"name": "Template light",
|
||||
"state": "{{ 1 == 1 }}",
|
||||
},
|
||||
ConfigurationStyle.TRIGGER,
|
||||
"light.template_light",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_friendly_name(hass: HomeAssistant, entity_id: str, setup_light) -> None:
|
||||
@ -1388,6 +1597,7 @@ async def test_friendly_name(hass: HomeAssistant, entity_id: str, setup_light) -
|
||||
[
|
||||
(ConfigurationStyle.LEGACY, "icon_template"),
|
||||
(ConfigurationStyle.MODERN, "icon"),
|
||||
(ConfigurationStyle.TRIGGER, "icon"),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
@ -1396,7 +1606,7 @@ async def test_friendly_name(hass: HomeAssistant, entity_id: str, setup_light) -
|
||||
async def test_icon_template(hass: HomeAssistant, setup_single_attribute_light) -> None:
|
||||
"""Test icon template."""
|
||||
state = hass.states.get("light.test_template_light")
|
||||
assert state.attributes.get("icon") == ""
|
||||
assert state.attributes.get("icon") in ("", None)
|
||||
|
||||
state = hass.states.async_set("light.test_state", STATE_ON)
|
||||
await hass.async_block_till_done()
|
||||
@ -1414,6 +1624,7 @@ async def test_icon_template(hass: HomeAssistant, setup_single_attribute_light)
|
||||
[
|
||||
(ConfigurationStyle.LEGACY, "entity_picture_template"),
|
||||
(ConfigurationStyle.MODERN, "picture"),
|
||||
(ConfigurationStyle.TRIGGER, "picture"),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
@ -1425,7 +1636,7 @@ async def test_entity_picture_template(
|
||||
) -> None:
|
||||
"""Test entity_picture template."""
|
||||
state = hass.states.get("light.test_template_light")
|
||||
assert state.attributes.get("entity_picture") == ""
|
||||
assert state.attributes.get("entity_picture") in ("", None)
|
||||
|
||||
state = hass.states.async_set("light.test_state", STATE_ON)
|
||||
await hass.async_block_till_done()
|
||||
@ -1488,6 +1699,7 @@ async def test_legacy_color_action_no_template(
|
||||
[
|
||||
ConfigurationStyle.LEGACY,
|
||||
ConfigurationStyle.MODERN,
|
||||
ConfigurationStyle.TRIGGER,
|
||||
],
|
||||
)
|
||||
async def test_hs_color_action_no_template(
|
||||
@ -1529,6 +1741,7 @@ async def test_hs_color_action_no_template(
|
||||
[
|
||||
ConfigurationStyle.LEGACY,
|
||||
ConfigurationStyle.MODERN,
|
||||
ConfigurationStyle.TRIGGER,
|
||||
],
|
||||
)
|
||||
async def test_rgb_color_action_no_template(
|
||||
@ -1571,6 +1784,7 @@ async def test_rgb_color_action_no_template(
|
||||
[
|
||||
ConfigurationStyle.LEGACY,
|
||||
ConfigurationStyle.MODERN,
|
||||
ConfigurationStyle.TRIGGER,
|
||||
],
|
||||
)
|
||||
async def test_rgbw_color_action_no_template(
|
||||
@ -1617,6 +1831,7 @@ async def test_rgbw_color_action_no_template(
|
||||
[
|
||||
ConfigurationStyle.LEGACY,
|
||||
ConfigurationStyle.MODERN,
|
||||
ConfigurationStyle.TRIGGER,
|
||||
],
|
||||
)
|
||||
async def test_rgbww_color_action_no_template(
|
||||
@ -1702,6 +1917,7 @@ async def test_legacy_color_template(
|
||||
[
|
||||
(ConfigurationStyle.LEGACY, "hs_template"),
|
||||
(ConfigurationStyle.MODERN, "hs"),
|
||||
(ConfigurationStyle.TRIGGER, "hs"),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
@ -1723,9 +1939,14 @@ async def test_hs_template(
|
||||
hass: HomeAssistant,
|
||||
expected_hs,
|
||||
expected_color_mode,
|
||||
style: ConfigurationStyle,
|
||||
setup_single_attribute_light,
|
||||
) -> None:
|
||||
"""Test the template for the color."""
|
||||
if style == ConfigurationStyle.TRIGGER:
|
||||
hass.states.async_set("light.test_state", STATE_ON)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("light.test_template_light")
|
||||
assert state.attributes.get("hs_color") == expected_hs
|
||||
assert state.state == STATE_ON
|
||||
@ -1742,6 +1963,7 @@ async def test_hs_template(
|
||||
[
|
||||
(ConfigurationStyle.LEGACY, "rgb_template"),
|
||||
(ConfigurationStyle.MODERN, "rgb"),
|
||||
(ConfigurationStyle.TRIGGER, "rgb"),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
@ -1764,9 +1986,14 @@ async def test_rgb_template(
|
||||
hass: HomeAssistant,
|
||||
expected_rgb,
|
||||
expected_color_mode,
|
||||
style: ConfigurationStyle,
|
||||
setup_single_attribute_light,
|
||||
) -> None:
|
||||
"""Test the template for the color."""
|
||||
if style == ConfigurationStyle.TRIGGER:
|
||||
hass.states.async_set("light.test_state", STATE_ON)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("light.test_template_light")
|
||||
assert state.attributes.get("rgb_color") == expected_rgb
|
||||
assert state.state == STATE_ON
|
||||
@ -1783,6 +2010,7 @@ async def test_rgb_template(
|
||||
[
|
||||
(ConfigurationStyle.LEGACY, "rgbw_template"),
|
||||
(ConfigurationStyle.MODERN, "rgbw"),
|
||||
(ConfigurationStyle.TRIGGER, "rgbw"),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
@ -1806,9 +2034,14 @@ async def test_rgbw_template(
|
||||
hass: HomeAssistant,
|
||||
expected_rgbw,
|
||||
expected_color_mode,
|
||||
style: ConfigurationStyle,
|
||||
setup_single_attribute_light,
|
||||
) -> None:
|
||||
"""Test the template for the color."""
|
||||
if style == ConfigurationStyle.TRIGGER:
|
||||
hass.states.async_set("light.test_state", STATE_ON)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("light.test_template_light")
|
||||
assert state.attributes.get("rgbw_color") == expected_rgbw
|
||||
assert state.state == STATE_ON
|
||||
@ -1825,6 +2058,7 @@ async def test_rgbw_template(
|
||||
[
|
||||
(ConfigurationStyle.LEGACY, "rgbww_template"),
|
||||
(ConfigurationStyle.MODERN, "rgbww"),
|
||||
(ConfigurationStyle.TRIGGER, "rgbww"),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
@ -1853,9 +2087,14 @@ async def test_rgbww_template(
|
||||
hass: HomeAssistant,
|
||||
expected_rgbww,
|
||||
expected_color_mode,
|
||||
style: ConfigurationStyle,
|
||||
setup_single_attribute_light,
|
||||
) -> None:
|
||||
"""Test the template for the color."""
|
||||
if style == ConfigurationStyle.TRIGGER:
|
||||
hass.states.async_set("light.test_state", STATE_ON)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("light.test_template_light")
|
||||
assert state.attributes.get("rgbww_color") == expected_rgbww
|
||||
assert state.state == STATE_ON
|
||||
@ -1887,6 +2126,15 @@ async def test_rgbww_template(
|
||||
},
|
||||
ConfigurationStyle.MODERN,
|
||||
),
|
||||
(
|
||||
{
|
||||
"name": "test_template_light",
|
||||
**OPTIMISTIC_ON_OFF_LIGHT_CONFIG,
|
||||
"state": "{{1 == 1}}",
|
||||
**TEST_ALL_COLORS_NO_TEMPLATE_CONFIG,
|
||||
},
|
||||
ConfigurationStyle.TRIGGER,
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_all_colors_mode_no_template(
|
||||
@ -2084,7 +2332,8 @@ async def test_all_colors_mode_no_template(
|
||||
|
||||
@pytest.mark.parametrize("count", [1])
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
|
||||
"style",
|
||||
[ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
("effect_list_template", "effect_template", "effect", "expected"),
|
||||
@ -2097,10 +2346,17 @@ async def test_effect_action(
|
||||
hass: HomeAssistant,
|
||||
effect: str,
|
||||
expected: Any,
|
||||
style: ConfigurationStyle,
|
||||
setup_light_with_effects,
|
||||
calls: list[ServiceCall],
|
||||
) -> None:
|
||||
"""Test setting valid effect with template."""
|
||||
|
||||
if style == ConfigurationStyle.TRIGGER:
|
||||
# Ensures the trigger template entity updates
|
||||
hass.states.async_set("light.test_state", STATE_ON)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("light.test_template_light")
|
||||
assert state is not None
|
||||
|
||||
@ -2123,7 +2379,8 @@ async def test_effect_action(
|
||||
|
||||
@pytest.mark.parametrize(("count", "effect_template"), [(1, "{{ None }}")])
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
|
||||
"style",
|
||||
[ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
("expected_effect_list", "effect_list_template"),
|
||||
@ -2145,9 +2402,16 @@ async def test_effect_action(
|
||||
],
|
||||
)
|
||||
async def test_effect_list_template(
|
||||
hass: HomeAssistant, expected_effect_list, setup_light_with_effects
|
||||
hass: HomeAssistant,
|
||||
expected_effect_list,
|
||||
style: ConfigurationStyle,
|
||||
setup_light_with_effects,
|
||||
) -> None:
|
||||
"""Test the template for the effect list."""
|
||||
if style == ConfigurationStyle.TRIGGER:
|
||||
hass.states.async_set("light.test_state", STATE_ON)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("light.test_template_light")
|
||||
assert state is not None
|
||||
assert state.attributes.get("effect_list") == expected_effect_list
|
||||
@ -2158,7 +2422,8 @@ async def test_effect_list_template(
|
||||
[(1, "{{ ['Strobe color', 'Police', 'Christmas', 'RGB', 'Random Loop'] }}")],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
|
||||
"style",
|
||||
[ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
("expected_effect", "effect_template"),
|
||||
@ -2171,9 +2436,16 @@ async def test_effect_list_template(
|
||||
],
|
||||
)
|
||||
async def test_effect_template(
|
||||
hass: HomeAssistant, expected_effect, setup_light_with_effects
|
||||
hass: HomeAssistant,
|
||||
expected_effect,
|
||||
style: ConfigurationStyle,
|
||||
setup_light_with_effects,
|
||||
) -> None:
|
||||
"""Test the template for the effect."""
|
||||
if style == ConfigurationStyle.TRIGGER:
|
||||
hass.states.async_set("light.test_state", STATE_ON)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("light.test_template_light")
|
||||
assert state is not None
|
||||
assert state.attributes.get("effect") == expected_effect
|
||||
@ -2185,6 +2457,7 @@ async def test_effect_template(
|
||||
[
|
||||
(ConfigurationStyle.LEGACY, "min_mireds_template"),
|
||||
(ConfigurationStyle.MODERN, "min_mireds"),
|
||||
(ConfigurationStyle.TRIGGER, "min_mireds"),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
@ -2199,9 +2472,16 @@ async def test_effect_template(
|
||||
],
|
||||
)
|
||||
async def test_min_mireds_template(
|
||||
hass: HomeAssistant, expected_min_mireds, setup_light_with_mireds
|
||||
hass: HomeAssistant,
|
||||
expected_min_mireds,
|
||||
style: ConfigurationStyle,
|
||||
setup_light_with_mireds,
|
||||
) -> None:
|
||||
"""Test the template for the min mireds."""
|
||||
if style == ConfigurationStyle.TRIGGER:
|
||||
hass.states.async_set("light.test_state", STATE_ON)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("light.test_template_light")
|
||||
assert state is not None
|
||||
assert state.attributes.get("min_mireds") == expected_min_mireds
|
||||
@ -2213,6 +2493,7 @@ async def test_min_mireds_template(
|
||||
[
|
||||
(ConfigurationStyle.LEGACY, "max_mireds_template"),
|
||||
(ConfigurationStyle.MODERN, "max_mireds"),
|
||||
(ConfigurationStyle.TRIGGER, "max_mireds"),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
@ -2227,9 +2508,16 @@ async def test_min_mireds_template(
|
||||
],
|
||||
)
|
||||
async def test_max_mireds_template(
|
||||
hass: HomeAssistant, expected_max_mireds, setup_light_with_mireds
|
||||
hass: HomeAssistant,
|
||||
expected_max_mireds,
|
||||
style: ConfigurationStyle,
|
||||
setup_light_with_mireds,
|
||||
) -> None:
|
||||
"""Test the template for the max mireds."""
|
||||
if style == ConfigurationStyle.TRIGGER:
|
||||
hass.states.async_set("light.test_state", STATE_ON)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("light.test_template_light")
|
||||
assert state is not None
|
||||
assert state.attributes.get("max_mireds") == expected_max_mireds
|
||||
@ -2243,6 +2531,7 @@ async def test_max_mireds_template(
|
||||
[
|
||||
(ConfigurationStyle.LEGACY, "supports_transition_template"),
|
||||
(ConfigurationStyle.MODERN, "supports_transition"),
|
||||
(ConfigurationStyle.TRIGGER, "supports_transition"),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
@ -2257,9 +2546,17 @@ async def test_max_mireds_template(
|
||||
],
|
||||
)
|
||||
async def test_supports_transition_template(
|
||||
hass: HomeAssistant, expected_supports_transition, setup_single_attribute_light
|
||||
hass: HomeAssistant,
|
||||
style: ConfigurationStyle,
|
||||
expected_supports_transition,
|
||||
setup_single_attribute_light,
|
||||
) -> None:
|
||||
"""Test the template for the supports transition."""
|
||||
if style == ConfigurationStyle.TRIGGER:
|
||||
# Ensures the trigger template entity updates
|
||||
hass.states.async_set("light.test_state", STATE_ON)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("light.test_template_light")
|
||||
|
||||
expected_value = 1
|
||||
@ -2277,10 +2574,11 @@ async def test_supports_transition_template(
|
||||
("count", "transition_template"), [(1, "{{ states('sensor.test') }}")]
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
|
||||
"style",
|
||||
[ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER],
|
||||
)
|
||||
async def test_supports_transition_template_updates(
|
||||
hass: HomeAssistant, setup_light_with_transition_template
|
||||
hass: HomeAssistant, style: ConfigurationStyle, setup_light_with_transition_template
|
||||
) -> None:
|
||||
"""Test the template for the supports transition dynamically."""
|
||||
state = hass.states.get("light.test_template_light")
|
||||
@ -2288,12 +2586,24 @@ async def test_supports_transition_template_updates(
|
||||
|
||||
hass.states.async_set("sensor.test", 0)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
if style == ConfigurationStyle.TRIGGER:
|
||||
# Ensures the trigger template entity updates
|
||||
hass.states.async_set("light.test_state", STATE_ON)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("light.test_template_light")
|
||||
supported_features = state.attributes.get("supported_features")
|
||||
assert supported_features == LightEntityFeature.EFFECT
|
||||
|
||||
hass.states.async_set("sensor.test", 1)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
if style == ConfigurationStyle.TRIGGER:
|
||||
# Ensures the trigger template entity updates
|
||||
hass.states.async_set("light.test_state", STATE_OFF)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("light.test_template_light")
|
||||
supported_features = state.attributes.get("supported_features")
|
||||
assert (
|
||||
@ -2302,6 +2612,12 @@ async def test_supports_transition_template_updates(
|
||||
|
||||
hass.states.async_set("sensor.test", 0)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
if style == ConfigurationStyle.TRIGGER:
|
||||
# Ensures the trigger template entity updates
|
||||
hass.states.async_set("light.test_state", STATE_ON)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("light.test_template_light")
|
||||
supported_features = state.attributes.get("supported_features")
|
||||
assert supported_features == LightEntityFeature.EFFECT
|
||||
@ -2322,16 +2638,22 @@ async def test_supports_transition_template_updates(
|
||||
[
|
||||
(ConfigurationStyle.LEGACY, "availability_template"),
|
||||
(ConfigurationStyle.MODERN, "availability"),
|
||||
(ConfigurationStyle.TRIGGER, "availability"),
|
||||
],
|
||||
)
|
||||
async def test_available_template_with_entities(
|
||||
hass: HomeAssistant, setup_single_attribute_light
|
||||
hass: HomeAssistant, style: ConfigurationStyle, setup_single_attribute_light
|
||||
) -> None:
|
||||
"""Test availability templates with values from other entities."""
|
||||
# When template returns true..
|
||||
hass.states.async_set(_STATE_AVAILABILITY_BOOLEAN, STATE_ON)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
if style == ConfigurationStyle.TRIGGER:
|
||||
# Ensures the trigger template entity updates
|
||||
hass.states.async_set("light.test_state", STATE_ON)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Device State should not be unavailable
|
||||
assert hass.states.get("light.test_template_light").state != STATE_UNAVAILABLE
|
||||
|
||||
@ -2339,6 +2661,11 @@ async def test_available_template_with_entities(
|
||||
hass.states.async_set(_STATE_AVAILABILITY_BOOLEAN, STATE_OFF)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
if style == ConfigurationStyle.TRIGGER:
|
||||
# Ensures the trigger template entity updates
|
||||
hass.states.async_set("light.test_state", STATE_OFF)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# device state should be unavailable
|
||||
assert hass.states.get("light.test_template_light").state == STATE_UNAVAILABLE
|
||||
|
||||
@ -2361,7 +2688,9 @@ async def test_available_template_with_entities(
|
||||
],
|
||||
)
|
||||
async def test_invalid_availability_template_keeps_component_available(
|
||||
hass: HomeAssistant, setup_single_attribute_light, caplog_setup_text
|
||||
hass: HomeAssistant,
|
||||
setup_single_attribute_light,
|
||||
caplog_setup_text,
|
||||
) -> None:
|
||||
"""Test that an invalid availability keeps the device available."""
|
||||
assert hass.states.get("light.test_template_light").state != STATE_UNAVAILABLE
|
||||
@ -2392,6 +2721,19 @@ async def test_invalid_availability_template_keeps_component_available(
|
||||
],
|
||||
ConfigurationStyle.MODERN,
|
||||
),
|
||||
(
|
||||
[
|
||||
{
|
||||
"name": "test_template_light_01",
|
||||
**TEST_UNIQUE_ID_CONFIG,
|
||||
},
|
||||
{
|
||||
"name": "test_template_light_02",
|
||||
**TEST_UNIQUE_ID_CONFIG,
|
||||
},
|
||||
],
|
||||
ConfigurationStyle.TRIGGER,
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_unique_id(hass: HomeAssistant, setup_light) -> None:
|
||||
|
Loading…
x
Reference in New Issue
Block a user