mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 12:47:08 +00:00
Add support for effects, transition/brightness parameters to template light, min_mireds and max_mireds templates (#43850)
* Add support for effects, transition/brightness parameters to template light, min_mireds and max_mireds templates * code fixes * min_mireds, max_mireds fixes * test fixes * more fixes * format fix * style fix * _update_effect_list change * style fix * Fixes after review * additional fixes * duplicated lines removed * fixes after CI run * test fixes * code and test fixes * supports transition change, added test cases
This commit is contained in:
parent
c497c0eadd
commit
85495c08b0
@ -6,12 +6,16 @@ import voluptuous as vol
|
|||||||
from homeassistant.components.light import (
|
from homeassistant.components.light import (
|
||||||
ATTR_BRIGHTNESS,
|
ATTR_BRIGHTNESS,
|
||||||
ATTR_COLOR_TEMP,
|
ATTR_COLOR_TEMP,
|
||||||
|
ATTR_EFFECT,
|
||||||
ATTR_HS_COLOR,
|
ATTR_HS_COLOR,
|
||||||
|
ATTR_TRANSITION,
|
||||||
ATTR_WHITE_VALUE,
|
ATTR_WHITE_VALUE,
|
||||||
ENTITY_ID_FORMAT,
|
ENTITY_ID_FORMAT,
|
||||||
SUPPORT_BRIGHTNESS,
|
SUPPORT_BRIGHTNESS,
|
||||||
SUPPORT_COLOR,
|
SUPPORT_COLOR,
|
||||||
SUPPORT_COLOR_TEMP,
|
SUPPORT_COLOR_TEMP,
|
||||||
|
SUPPORT_EFFECT,
|
||||||
|
SUPPORT_TRANSITION,
|
||||||
SUPPORT_WHITE_VALUE,
|
SUPPORT_WHITE_VALUE,
|
||||||
LightEntity,
|
LightEntity,
|
||||||
)
|
)
|
||||||
@ -49,6 +53,12 @@ CONF_COLOR_TEMPLATE = "color_template"
|
|||||||
CONF_COLOR_ACTION = "set_color"
|
CONF_COLOR_ACTION = "set_color"
|
||||||
CONF_WHITE_VALUE_TEMPLATE = "white_value_template"
|
CONF_WHITE_VALUE_TEMPLATE = "white_value_template"
|
||||||
CONF_WHITE_VALUE_ACTION = "set_white_value"
|
CONF_WHITE_VALUE_ACTION = "set_white_value"
|
||||||
|
CONF_EFFECT_ACTION = "set_effect"
|
||||||
|
CONF_EFFECT_LIST_TEMPLATE = "effect_list_template"
|
||||||
|
CONF_EFFECT_TEMPLATE = "effect_template"
|
||||||
|
CONF_MAX_MIREDS_TEMPLATE = "max_mireds_template"
|
||||||
|
CONF_MIN_MIREDS_TEMPLATE = "min_mireds_template"
|
||||||
|
CONF_SUPPORTS_TRANSITION = "supports_transition_template"
|
||||||
|
|
||||||
LIGHT_SCHEMA = vol.All(
|
LIGHT_SCHEMA = vol.All(
|
||||||
cv.deprecated(CONF_ENTITY_ID),
|
cv.deprecated(CONF_ENTITY_ID),
|
||||||
@ -70,6 +80,12 @@ LIGHT_SCHEMA = vol.All(
|
|||||||
vol.Optional(CONF_COLOR_ACTION): cv.SCRIPT_SCHEMA,
|
vol.Optional(CONF_COLOR_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
vol.Optional(CONF_WHITE_VALUE_TEMPLATE): cv.template,
|
vol.Optional(CONF_WHITE_VALUE_TEMPLATE): cv.template,
|
||||||
vol.Optional(CONF_WHITE_VALUE_ACTION): cv.SCRIPT_SCHEMA,
|
vol.Optional(CONF_WHITE_VALUE_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
|
vol.Inclusive(CONF_EFFECT_LIST_TEMPLATE, "effect"): cv.template,
|
||||||
|
vol.Inclusive(CONF_EFFECT_TEMPLATE, "effect"): cv.template,
|
||||||
|
vol.Inclusive(CONF_EFFECT_ACTION, "effect"): cv.SCRIPT_SCHEMA,
|
||||||
|
vol.Optional(CONF_MAX_MIREDS_TEMPLATE): cv.template,
|
||||||
|
vol.Optional(CONF_MIN_MIREDS_TEMPLATE): cv.template,
|
||||||
|
vol.Optional(CONF_SUPPORTS_TRANSITION): cv.template,
|
||||||
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
@ -108,6 +124,15 @@ async def _async_create_entities(hass, config):
|
|||||||
white_value_action = device_config.get(CONF_WHITE_VALUE_ACTION)
|
white_value_action = device_config.get(CONF_WHITE_VALUE_ACTION)
|
||||||
white_value_template = device_config.get(CONF_WHITE_VALUE_TEMPLATE)
|
white_value_template = device_config.get(CONF_WHITE_VALUE_TEMPLATE)
|
||||||
|
|
||||||
|
effect_action = device_config.get(CONF_EFFECT_ACTION)
|
||||||
|
effect_list_template = device_config.get(CONF_EFFECT_LIST_TEMPLATE)
|
||||||
|
effect_template = device_config.get(CONF_EFFECT_TEMPLATE)
|
||||||
|
|
||||||
|
max_mireds_template = device_config.get(CONF_MAX_MIREDS_TEMPLATE)
|
||||||
|
min_mireds_template = device_config.get(CONF_MIN_MIREDS_TEMPLATE)
|
||||||
|
|
||||||
|
supports_transition_template = device_config.get(CONF_SUPPORTS_TRANSITION)
|
||||||
|
|
||||||
lights.append(
|
lights.append(
|
||||||
LightTemplate(
|
LightTemplate(
|
||||||
hass,
|
hass,
|
||||||
@ -127,6 +152,12 @@ async def _async_create_entities(hass, config):
|
|||||||
color_template,
|
color_template,
|
||||||
white_value_action,
|
white_value_action,
|
||||||
white_value_template,
|
white_value_template,
|
||||||
|
effect_action,
|
||||||
|
effect_list_template,
|
||||||
|
effect_template,
|
||||||
|
max_mireds_template,
|
||||||
|
min_mireds_template,
|
||||||
|
supports_transition_template,
|
||||||
unique_id,
|
unique_id,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -161,6 +192,12 @@ class LightTemplate(TemplateEntity, LightEntity):
|
|||||||
color_template,
|
color_template,
|
||||||
white_value_action,
|
white_value_action,
|
||||||
white_value_template,
|
white_value_template,
|
||||||
|
effect_action,
|
||||||
|
effect_list_template,
|
||||||
|
effect_template,
|
||||||
|
max_mireds_template,
|
||||||
|
min_mireds_template,
|
||||||
|
supports_transition_template,
|
||||||
unique_id,
|
unique_id,
|
||||||
):
|
):
|
||||||
"""Initialize the light."""
|
"""Initialize the light."""
|
||||||
@ -197,12 +234,25 @@ class LightTemplate(TemplateEntity, LightEntity):
|
|||||||
hass, white_value_action, friendly_name, domain
|
hass, white_value_action, friendly_name, domain
|
||||||
)
|
)
|
||||||
self._white_value_template = white_value_template
|
self._white_value_template = white_value_template
|
||||||
|
self._effect_script = None
|
||||||
|
if effect_action is not None:
|
||||||
|
self._effect_script = Script(hass, effect_action, friendly_name, domain)
|
||||||
|
self._effect_list_template = effect_list_template
|
||||||
|
self._effect_template = effect_template
|
||||||
|
self._max_mireds_template = max_mireds_template
|
||||||
|
self._min_mireds_template = min_mireds_template
|
||||||
|
self._supports_transition_template = supports_transition_template
|
||||||
|
|
||||||
self._state = False
|
self._state = False
|
||||||
self._brightness = None
|
self._brightness = None
|
||||||
self._temperature = None
|
self._temperature = None
|
||||||
self._color = None
|
self._color = None
|
||||||
self._white_value = None
|
self._white_value = None
|
||||||
|
self._effect = None
|
||||||
|
self._effect_list = None
|
||||||
|
self._max_mireds = None
|
||||||
|
self._min_mireds = None
|
||||||
|
self._supports_transition = False
|
||||||
self._unique_id = unique_id
|
self._unique_id = unique_id
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -215,6 +265,22 @@ class LightTemplate(TemplateEntity, LightEntity):
|
|||||||
"""Return the CT color value in mireds."""
|
"""Return the CT color value in mireds."""
|
||||||
return self._temperature
|
return self._temperature
|
||||||
|
|
||||||
|
@property
|
||||||
|
def max_mireds(self):
|
||||||
|
"""Return the max mireds value in mireds."""
|
||||||
|
if self._max_mireds is not None:
|
||||||
|
return self._max_mireds
|
||||||
|
|
||||||
|
return super().max_mireds
|
||||||
|
|
||||||
|
@property
|
||||||
|
def min_mireds(self):
|
||||||
|
"""Return the min mireds value in mireds."""
|
||||||
|
if self._min_mireds is not None:
|
||||||
|
return self._min_mireds
|
||||||
|
|
||||||
|
return super().min_mireds
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def white_value(self):
|
def white_value(self):
|
||||||
"""Return the white value."""
|
"""Return the white value."""
|
||||||
@ -225,6 +291,16 @@ class LightTemplate(TemplateEntity, LightEntity):
|
|||||||
"""Return the hue and saturation color value [float, float]."""
|
"""Return the hue and saturation color value [float, float]."""
|
||||||
return self._color
|
return self._color
|
||||||
|
|
||||||
|
@property
|
||||||
|
def effect(self):
|
||||||
|
"""Return the effect."""
|
||||||
|
return self._effect
|
||||||
|
|
||||||
|
@property
|
||||||
|
def effect_list(self):
|
||||||
|
"""Return the effect list."""
|
||||||
|
return self._effect_list
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""Return the display name of this light."""
|
"""Return the display name of this light."""
|
||||||
@ -247,6 +323,10 @@ class LightTemplate(TemplateEntity, LightEntity):
|
|||||||
supported_features |= SUPPORT_COLOR
|
supported_features |= SUPPORT_COLOR
|
||||||
if self._white_value_script is not None:
|
if self._white_value_script is not None:
|
||||||
supported_features |= SUPPORT_WHITE_VALUE
|
supported_features |= SUPPORT_WHITE_VALUE
|
||||||
|
if self._effect_script is not None:
|
||||||
|
supported_features |= SUPPORT_EFFECT
|
||||||
|
if self._supports_transition is True:
|
||||||
|
supported_features |= SUPPORT_TRANSITION
|
||||||
return supported_features
|
return supported_features
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -268,6 +348,22 @@ class LightTemplate(TemplateEntity, LightEntity):
|
|||||||
self._update_brightness,
|
self._update_brightness,
|
||||||
none_on_template_error=True,
|
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:
|
if self._temperature_template:
|
||||||
self.add_template_attribute(
|
self.add_template_attribute(
|
||||||
"_temperature",
|
"_temperature",
|
||||||
@ -292,6 +388,30 @@ class LightTemplate(TemplateEntity, LightEntity):
|
|||||||
self._update_white_value,
|
self._update_white_value,
|
||||||
none_on_template_error=True,
|
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,
|
||||||
|
)
|
||||||
await super().async_added_to_hass()
|
await super().async_added_to_hass()
|
||||||
|
|
||||||
async def async_turn_on(self, **kwargs):
|
async def async_turn_on(self, **kwargs):
|
||||||
@ -324,33 +444,65 @@ class LightTemplate(TemplateEntity, LightEntity):
|
|||||||
self._temperature = kwargs[ATTR_COLOR_TEMP]
|
self._temperature = kwargs[ATTR_COLOR_TEMP]
|
||||||
optimistic_set = True
|
optimistic_set = True
|
||||||
|
|
||||||
if ATTR_BRIGHTNESS in kwargs and self._level_script:
|
common_params = {}
|
||||||
await self._level_script.async_run(
|
|
||||||
{"brightness": kwargs[ATTR_BRIGHTNESS]}, context=self._context
|
if ATTR_BRIGHTNESS in kwargs:
|
||||||
)
|
common_params["brightness"] = kwargs[ATTR_BRIGHTNESS]
|
||||||
elif ATTR_COLOR_TEMP in kwargs and self._temperature_script:
|
|
||||||
|
if ATTR_TRANSITION in kwargs and self._supports_transition is True:
|
||||||
|
common_params["transition"] = kwargs[ATTR_TRANSITION]
|
||||||
|
|
||||||
|
if ATTR_COLOR_TEMP in kwargs and self._temperature_script:
|
||||||
|
common_params["color_temp"] = kwargs[ATTR_COLOR_TEMP]
|
||||||
|
|
||||||
await self._temperature_script.async_run(
|
await self._temperature_script.async_run(
|
||||||
{"color_temp": kwargs[ATTR_COLOR_TEMP]}, context=self._context
|
common_params, context=self._context
|
||||||
)
|
)
|
||||||
elif ATTR_WHITE_VALUE in kwargs and self._white_value_script:
|
elif ATTR_WHITE_VALUE in kwargs and self._white_value_script:
|
||||||
|
common_params["white_value"] = kwargs[ATTR_WHITE_VALUE]
|
||||||
|
|
||||||
await self._white_value_script.async_run(
|
await self._white_value_script.async_run(
|
||||||
{"white_value": kwargs[ATTR_WHITE_VALUE]}, context=self._context
|
common_params, context=self._context
|
||||||
)
|
)
|
||||||
|
elif ATTR_EFFECT in kwargs and self._effect_script:
|
||||||
|
effect = kwargs[ATTR_EFFECT]
|
||||||
|
if effect not in self._effect_list:
|
||||||
|
_LOGGER.error(
|
||||||
|
"Received invalid effect: %s. Expected one of: %s",
|
||||||
|
effect,
|
||||||
|
self._effect_list,
|
||||||
|
exc_info=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
common_params["effect"] = effect
|
||||||
|
|
||||||
|
await self._effect_script.async_run(common_params, context=self._context)
|
||||||
elif ATTR_HS_COLOR in kwargs and self._color_script:
|
elif ATTR_HS_COLOR in kwargs and self._color_script:
|
||||||
hs_value = kwargs[ATTR_HS_COLOR]
|
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._color_script.async_run(
|
await self._color_script.async_run(
|
||||||
{"hs": hs_value, "h": int(hs_value[0]), "s": int(hs_value[1])},
|
common_params,
|
||||||
context=self._context,
|
context=self._context,
|
||||||
)
|
)
|
||||||
|
elif ATTR_BRIGHTNESS in kwargs and self._level_script:
|
||||||
|
await self._level_script.async_run(common_params, context=self._context)
|
||||||
else:
|
else:
|
||||||
await self._on_script.async_run(context=self._context)
|
await self._on_script.async_run(common_params, context=self._context)
|
||||||
|
|
||||||
if optimistic_set:
|
if optimistic_set:
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs):
|
async def async_turn_off(self, **kwargs):
|
||||||
"""Turn the light off."""
|
"""Turn the light off."""
|
||||||
await self._off_script.async_run(context=self._context)
|
if ATTR_TRANSITION in kwargs and self._supports_transition is True:
|
||||||
|
await self._off_script.async_run(
|
||||||
|
{"transition": kwargs[ATTR_TRANSITION]}, context=self._context
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
await self._off_script.async_run(context=self._context)
|
||||||
if self._template is None:
|
if self._template is None:
|
||||||
self._state = False
|
self._state = False
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
@ -397,6 +549,45 @@ class LightTemplate(TemplateEntity, LightEntity):
|
|||||||
)
|
)
|
||||||
self._white_value = None
|
self._white_value = None
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _update_effect_list(self, effect_list):
|
||||||
|
"""Update the effect list from the template."""
|
||||||
|
if effect_list in ("None", ""):
|
||||||
|
self._effect_list = None
|
||||||
|
return
|
||||||
|
|
||||||
|
if not isinstance(effect_list, list):
|
||||||
|
_LOGGER.error(
|
||||||
|
"Received invalid effect list: %s. Expected list of strings",
|
||||||
|
effect_list,
|
||||||
|
)
|
||||||
|
self._effect_list = None
|
||||||
|
return
|
||||||
|
|
||||||
|
if len(effect_list) == 0:
|
||||||
|
self._effect_list = None
|
||||||
|
return
|
||||||
|
|
||||||
|
self._effect_list = effect_list
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _update_effect(self, effect):
|
||||||
|
"""Update the effect from the template."""
|
||||||
|
if effect in ("None", ""):
|
||||||
|
self._effect = None
|
||||||
|
return
|
||||||
|
|
||||||
|
if effect not in self._effect_list:
|
||||||
|
_LOGGER.error(
|
||||||
|
"Received invalid effect: %s. Expected one of: %s",
|
||||||
|
effect,
|
||||||
|
self._effect_list,
|
||||||
|
)
|
||||||
|
self._effect = None
|
||||||
|
return
|
||||||
|
|
||||||
|
self._effect = effect
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _update_state(self, result):
|
def _update_state(self, result):
|
||||||
"""Update the state from the template."""
|
"""Update the state from the template."""
|
||||||
@ -479,3 +670,42 @@ class LightTemplate(TemplateEntity, LightEntity):
|
|||||||
else:
|
else:
|
||||||
_LOGGER.error("Received invalid hs_color : (%s)", render)
|
_LOGGER.error("Received invalid hs_color : (%s)", render)
|
||||||
self._color = None
|
self._color = None
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _update_max_mireds(self, render):
|
||||||
|
"""Update the max mireds from the template."""
|
||||||
|
|
||||||
|
try:
|
||||||
|
if render in ("None", ""):
|
||||||
|
self._max_mireds = None
|
||||||
|
return
|
||||||
|
self._max_mireds = int(render)
|
||||||
|
except ValueError:
|
||||||
|
_LOGGER.error(
|
||||||
|
"Template must supply an integer temperature within the range for this light, or 'None'",
|
||||||
|
exc_info=True,
|
||||||
|
)
|
||||||
|
self._max_mireds = None
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _update_min_mireds(self, render):
|
||||||
|
"""Update the min mireds from the template."""
|
||||||
|
try:
|
||||||
|
if render in ("None", ""):
|
||||||
|
self._min_mireds = None
|
||||||
|
return
|
||||||
|
self._min_mireds = int(render)
|
||||||
|
except ValueError:
|
||||||
|
_LOGGER.error(
|
||||||
|
"Template must supply an integer temperature within the range for this light, or 'None'",
|
||||||
|
exc_info=True,
|
||||||
|
)
|
||||||
|
self._min_mireds = None
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _update_supports_transition(self, render):
|
||||||
|
"""Update the supports transition from the template."""
|
||||||
|
if render in ("None", ""):
|
||||||
|
self._supports_transition = False
|
||||||
|
return
|
||||||
|
self._supports_transition = bool(render)
|
||||||
|
@ -8,8 +8,11 @@ import homeassistant.components.light as light
|
|||||||
from homeassistant.components.light import (
|
from homeassistant.components.light import (
|
||||||
ATTR_BRIGHTNESS,
|
ATTR_BRIGHTNESS,
|
||||||
ATTR_COLOR_TEMP,
|
ATTR_COLOR_TEMP,
|
||||||
|
ATTR_EFFECT,
|
||||||
ATTR_HS_COLOR,
|
ATTR_HS_COLOR,
|
||||||
|
ATTR_TRANSITION,
|
||||||
ATTR_WHITE_VALUE,
|
ATTR_WHITE_VALUE,
|
||||||
|
SUPPORT_TRANSITION,
|
||||||
)
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID,
|
ATTR_ENTITY_ID,
|
||||||
@ -378,6 +381,64 @@ async def test_on_action(hass, calls):
|
|||||||
assert len(calls) == 1
|
assert len(calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_on_action_with_transition(hass, calls):
|
||||||
|
"""Test on action with transition."""
|
||||||
|
assert await setup.async_setup_component(
|
||||||
|
hass,
|
||||||
|
light.DOMAIN,
|
||||||
|
{
|
||||||
|
"light": {
|
||||||
|
"platform": "template",
|
||||||
|
"lights": {
|
||||||
|
"test_template_light": {
|
||||||
|
"value_template": "{{states.light.test_state.state}}",
|
||||||
|
"turn_on": {
|
||||||
|
"service": "test.automation",
|
||||||
|
"data_template": {
|
||||||
|
"transition": "{{transition}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"turn_off": {
|
||||||
|
"service": "light.turn_off",
|
||||||
|
"entity_id": "light.test_state",
|
||||||
|
},
|
||||||
|
"supports_transition_template": "{{true}}",
|
||||||
|
"set_level": {
|
||||||
|
"service": "light.turn_on",
|
||||||
|
"data_template": {
|
||||||
|
"entity_id": "light.test_state",
|
||||||
|
"brightness": "{{brightness}}",
|
||||||
|
"transition": "{{transition}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await hass.async_start()
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
hass.states.async_set("light.test_state", STATE_OFF)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("light.test_template_light")
|
||||||
|
assert state.state == STATE_OFF
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
light.DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: "light.test_template_light", ATTR_TRANSITION: 5},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(calls) == 1
|
||||||
|
print(calls[0].data)
|
||||||
|
assert calls[0].data["transition"] == 5
|
||||||
|
|
||||||
|
|
||||||
async def test_on_action_optimistic(hass, calls):
|
async def test_on_action_optimistic(hass, calls):
|
||||||
"""Test on action with optimistic state."""
|
"""Test on action with optimistic state."""
|
||||||
assert await setup.async_setup_component(
|
assert await setup.async_setup_component(
|
||||||
@ -443,7 +504,9 @@ async def test_off_action(hass, calls):
|
|||||||
"service": "light.turn_on",
|
"service": "light.turn_on",
|
||||||
"entity_id": "light.test_state",
|
"entity_id": "light.test_state",
|
||||||
},
|
},
|
||||||
"turn_off": {"service": "test.automation"},
|
"turn_off": {
|
||||||
|
"service": "test.automation",
|
||||||
|
},
|
||||||
"set_level": {
|
"set_level": {
|
||||||
"service": "light.turn_on",
|
"service": "light.turn_on",
|
||||||
"data_template": {
|
"data_template": {
|
||||||
@ -477,6 +540,63 @@ async def test_off_action(hass, calls):
|
|||||||
assert len(calls) == 1
|
assert len(calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_off_action_with_transition(hass, calls):
|
||||||
|
"""Test off action with transition."""
|
||||||
|
assert await setup.async_setup_component(
|
||||||
|
hass,
|
||||||
|
light.DOMAIN,
|
||||||
|
{
|
||||||
|
"light": {
|
||||||
|
"platform": "template",
|
||||||
|
"lights": {
|
||||||
|
"test_template_light": {
|
||||||
|
"value_template": "{{states.light.test_state.state}}",
|
||||||
|
"turn_on": {
|
||||||
|
"service": "light.turn_on",
|
||||||
|
"entity_id": "light.test_state",
|
||||||
|
},
|
||||||
|
"turn_off": {
|
||||||
|
"service": "test.automation",
|
||||||
|
"data_template": {
|
||||||
|
"transition": "{{transition}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"supports_transition_template": "{{true}}",
|
||||||
|
"set_level": {
|
||||||
|
"service": "light.turn_on",
|
||||||
|
"data_template": {
|
||||||
|
"entity_id": "light.test_state",
|
||||||
|
"brightness": "{{brightness}}",
|
||||||
|
"transition": "{{transition}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await hass.async_start()
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
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.state == STATE_ON
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
light.DOMAIN,
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
{ATTR_ENTITY_ID: "light.test_template_light", ATTR_TRANSITION: 2},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(calls) == 1
|
||||||
|
assert calls[0].data["transition"] == 2
|
||||||
|
|
||||||
|
|
||||||
async def test_off_action_optimistic(hass, calls):
|
async def test_off_action_optimistic(hass, calls):
|
||||||
"""Test off action with optimistic state."""
|
"""Test off action with optimistic state."""
|
||||||
assert await setup.async_setup_component(
|
assert await setup.async_setup_component(
|
||||||
@ -1119,6 +1239,417 @@ async def test_color_template(hass, expected_hs, template):
|
|||||||
assert state.attributes.get("hs_color") == expected_hs
|
assert state.attributes.get("hs_color") == expected_hs
|
||||||
|
|
||||||
|
|
||||||
|
async def test_effect_action_valid_effect(hass, calls):
|
||||||
|
"""Test setting valid effect with template."""
|
||||||
|
assert await setup.async_setup_component(
|
||||||
|
hass,
|
||||||
|
light.DOMAIN,
|
||||||
|
{
|
||||||
|
"light": {
|
||||||
|
"platform": "template",
|
||||||
|
"lights": {
|
||||||
|
"test_template_light": {
|
||||||
|
"value_template": "{{true}}",
|
||||||
|
"turn_on": {"service": "test.automation"},
|
||||||
|
"turn_off": {
|
||||||
|
"service": "light.turn_off",
|
||||||
|
"entity_id": "light.test_state",
|
||||||
|
},
|
||||||
|
"set_level": {
|
||||||
|
"service": "light.turn_on",
|
||||||
|
"data_template": {
|
||||||
|
"entity_id": "light.test_state",
|
||||||
|
"brightness": "{{brightness}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"set_effect": {
|
||||||
|
"service": "test.automation",
|
||||||
|
"data_template": {
|
||||||
|
"entity_id": "test.test_state",
|
||||||
|
"effect": "{{effect}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"effect_list_template": "{{ ['Disco', 'Police'] }}",
|
||||||
|
"effect_template": "{{ 'Disco' }}",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await hass.async_start()
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("light.test_template_light")
|
||||||
|
assert state is not None
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
light.DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: "light.test_template_light", ATTR_EFFECT: "Disco"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(calls) == 1
|
||||||
|
assert calls[0].data["effect"] == "Disco"
|
||||||
|
|
||||||
|
state = hass.states.get("light.test_template_light")
|
||||||
|
assert state is not None
|
||||||
|
assert state.attributes.get("effect") == "Disco"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_effect_action_invalid_effect(hass, calls):
|
||||||
|
"""Test setting invalid effect with template."""
|
||||||
|
assert await setup.async_setup_component(
|
||||||
|
hass,
|
||||||
|
light.DOMAIN,
|
||||||
|
{
|
||||||
|
"light": {
|
||||||
|
"platform": "template",
|
||||||
|
"lights": {
|
||||||
|
"test_template_light": {
|
||||||
|
"value_template": "{{true}}",
|
||||||
|
"turn_on": {"service": "test.automation"},
|
||||||
|
"turn_off": {
|
||||||
|
"service": "light.turn_off",
|
||||||
|
"entity_id": "light.test_state",
|
||||||
|
},
|
||||||
|
"set_level": {
|
||||||
|
"service": "light.turn_on",
|
||||||
|
"data_template": {
|
||||||
|
"entity_id": "light.test_state",
|
||||||
|
"brightness": "{{brightness}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"set_effect": {
|
||||||
|
"service": "test.automation",
|
||||||
|
"data_template": {
|
||||||
|
"entity_id": "test.test_state",
|
||||||
|
"effect": "{{effect}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"effect_list_template": "{{ ['Disco', 'Police'] }}",
|
||||||
|
"effect_template": "{{ None }}",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await hass.async_start()
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("light.test_template_light")
|
||||||
|
assert state is not None
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
light.DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: "light.test_template_light", ATTR_EFFECT: "RGB"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(calls) == 1
|
||||||
|
assert calls[0].data["effect"] == "RGB"
|
||||||
|
|
||||||
|
state = hass.states.get("light.test_template_light")
|
||||||
|
assert state is not None
|
||||||
|
assert state.attributes.get("effect") is None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"expected_effect_list,template",
|
||||||
|
[
|
||||||
|
(
|
||||||
|
["Strobe color", "Police", "Christmas", "RGB", "Random Loop"],
|
||||||
|
"{{ ['Strobe color', 'Police', 'Christmas', 'RGB', 'Random Loop'] }}",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
["Police", "RGB", "Random Loop"],
|
||||||
|
"{{ ['Police', 'RGB', 'Random Loop'] }}",
|
||||||
|
),
|
||||||
|
(None, "{{ [] }}"),
|
||||||
|
(None, "{{ '[]' }}"),
|
||||||
|
(None, "{{ 124 }}"),
|
||||||
|
(None, "{{ '124' }}"),
|
||||||
|
(None, "{{ none }}"),
|
||||||
|
(None, ""),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_effect_list_template(hass, expected_effect_list, template):
|
||||||
|
"""Test the template for the effect list."""
|
||||||
|
with assert_setup_component(1, light.DOMAIN):
|
||||||
|
assert await setup.async_setup_component(
|
||||||
|
hass,
|
||||||
|
light.DOMAIN,
|
||||||
|
{
|
||||||
|
"light": {
|
||||||
|
"platform": "template",
|
||||||
|
"lights": {
|
||||||
|
"test_template_light": {
|
||||||
|
"value_template": "{{ 1 == 1 }}",
|
||||||
|
"turn_on": {
|
||||||
|
"service": "light.turn_on",
|
||||||
|
"entity_id": "light.test_state",
|
||||||
|
},
|
||||||
|
"turn_off": {
|
||||||
|
"service": "light.turn_off",
|
||||||
|
"entity_id": "light.test_state",
|
||||||
|
},
|
||||||
|
"set_effect": {
|
||||||
|
"service": "test.automation",
|
||||||
|
"data_template": {
|
||||||
|
"entity_id": "test.test_state",
|
||||||
|
"effect": "{{effect}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"effect_list_template": template,
|
||||||
|
"effect_template": "{{ None }}",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await hass.async_start()
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"expected_effect,template",
|
||||||
|
[
|
||||||
|
(None, "Disco"),
|
||||||
|
(None, "None"),
|
||||||
|
(None, "{{ None }}"),
|
||||||
|
("Police", "Police"),
|
||||||
|
("Strobe color", "{{ 'Strobe color' }}"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_effect_template(hass, expected_effect, template):
|
||||||
|
"""Test the template for the effect."""
|
||||||
|
with assert_setup_component(1, light.DOMAIN):
|
||||||
|
assert await setup.async_setup_component(
|
||||||
|
hass,
|
||||||
|
light.DOMAIN,
|
||||||
|
{
|
||||||
|
"light": {
|
||||||
|
"platform": "template",
|
||||||
|
"lights": {
|
||||||
|
"test_template_light": {
|
||||||
|
"value_template": "{{ 1 == 1 }}",
|
||||||
|
"turn_on": {
|
||||||
|
"service": "light.turn_on",
|
||||||
|
"entity_id": "light.test_state",
|
||||||
|
},
|
||||||
|
"turn_off": {
|
||||||
|
"service": "light.turn_off",
|
||||||
|
"entity_id": "light.test_state",
|
||||||
|
},
|
||||||
|
"set_effect": {
|
||||||
|
"service": "test.automation",
|
||||||
|
"data_template": {
|
||||||
|
"entity_id": "test.test_state",
|
||||||
|
"effect": "{{effect}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"effect_list_template": "{{ ['Strobe color', 'Police', 'Christmas', 'RGB', 'Random Loop'] }}",
|
||||||
|
"effect_template": template,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await hass.async_start()
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"expected_min_mireds,template",
|
||||||
|
[
|
||||||
|
(118, "{{118}}"),
|
||||||
|
(153, "{{x - 12}}"),
|
||||||
|
(153, "None"),
|
||||||
|
(153, "{{ none }}"),
|
||||||
|
(153, ""),
|
||||||
|
(153, "{{ 'a' }}"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_min_mireds_template(hass, expected_min_mireds, template):
|
||||||
|
"""Test the template for the min mireds."""
|
||||||
|
with assert_setup_component(1, light.DOMAIN):
|
||||||
|
assert await setup.async_setup_component(
|
||||||
|
hass,
|
||||||
|
"light",
|
||||||
|
{
|
||||||
|
"light": {
|
||||||
|
"platform": "template",
|
||||||
|
"lights": {
|
||||||
|
"test_template_light": {
|
||||||
|
"value_template": "{{ 1 == 1 }}",
|
||||||
|
"turn_on": {
|
||||||
|
"service": "light.turn_on",
|
||||||
|
"entity_id": "light.test_state",
|
||||||
|
},
|
||||||
|
"turn_off": {
|
||||||
|
"service": "light.turn_off",
|
||||||
|
"entity_id": "light.test_state",
|
||||||
|
},
|
||||||
|
"set_temperature": {
|
||||||
|
"service": "light.turn_on",
|
||||||
|
"data_template": {
|
||||||
|
"entity_id": "light.test_state",
|
||||||
|
"color_temp": "{{color_temp}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"temperature_template": "{{200}}",
|
||||||
|
"min_mireds_template": template,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await hass.async_start()
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"expected_max_mireds,template",
|
||||||
|
[
|
||||||
|
(488, "{{488}}"),
|
||||||
|
(500, "{{x - 12}}"),
|
||||||
|
(500, "None"),
|
||||||
|
(500, "{{ none }}"),
|
||||||
|
(500, ""),
|
||||||
|
(500, "{{ 'a' }}"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_max_mireds_template(hass, expected_max_mireds, template):
|
||||||
|
"""Test the template for the max mireds."""
|
||||||
|
with assert_setup_component(1, light.DOMAIN):
|
||||||
|
assert await setup.async_setup_component(
|
||||||
|
hass,
|
||||||
|
"light",
|
||||||
|
{
|
||||||
|
"light": {
|
||||||
|
"platform": "template",
|
||||||
|
"lights": {
|
||||||
|
"test_template_light": {
|
||||||
|
"value_template": "{{ 1 == 1 }}",
|
||||||
|
"turn_on": {
|
||||||
|
"service": "light.turn_on",
|
||||||
|
"entity_id": "light.test_state",
|
||||||
|
},
|
||||||
|
"turn_off": {
|
||||||
|
"service": "light.turn_off",
|
||||||
|
"entity_id": "light.test_state",
|
||||||
|
},
|
||||||
|
"set_temperature": {
|
||||||
|
"service": "light.turn_on",
|
||||||
|
"data_template": {
|
||||||
|
"entity_id": "light.test_state",
|
||||||
|
"color_temp": "{{color_temp}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"temperature_template": "{{200}}",
|
||||||
|
"max_mireds_template": template,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await hass.async_start()
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"expected_supports_transition,template",
|
||||||
|
[
|
||||||
|
(True, "{{true}}"),
|
||||||
|
(True, "{{1 == 1}}"),
|
||||||
|
(False, "{{false}}"),
|
||||||
|
(False, "{{ none }}"),
|
||||||
|
(False, ""),
|
||||||
|
(False, "None"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_supports_transition_template(
|
||||||
|
hass, expected_supports_transition, template
|
||||||
|
):
|
||||||
|
"""Test the template for the supports transition."""
|
||||||
|
with assert_setup_component(1, light.DOMAIN):
|
||||||
|
assert await setup.async_setup_component(
|
||||||
|
hass,
|
||||||
|
"light",
|
||||||
|
{
|
||||||
|
"light": {
|
||||||
|
"platform": "template",
|
||||||
|
"lights": {
|
||||||
|
"test_template_light": {
|
||||||
|
"value_template": "{{ 1 == 1 }}",
|
||||||
|
"turn_on": {
|
||||||
|
"service": "light.turn_on",
|
||||||
|
"entity_id": "light.test_state",
|
||||||
|
},
|
||||||
|
"turn_off": {
|
||||||
|
"service": "light.turn_off",
|
||||||
|
"entity_id": "light.test_state",
|
||||||
|
},
|
||||||
|
"set_temperature": {
|
||||||
|
"service": "light.turn_on",
|
||||||
|
"data_template": {
|
||||||
|
"entity_id": "light.test_state",
|
||||||
|
"color_temp": "{{color_temp}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"supports_transition_template": template,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await hass.async_start()
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("light.test_template_light")
|
||||||
|
|
||||||
|
expected_value = 1
|
||||||
|
|
||||||
|
if expected_supports_transition is True:
|
||||||
|
expected_value = 0
|
||||||
|
|
||||||
|
assert state is not None
|
||||||
|
assert (
|
||||||
|
int(state.attributes.get("supported_features")) & SUPPORT_TRANSITION
|
||||||
|
) != expected_value
|
||||||
|
|
||||||
|
|
||||||
async def test_available_template_with_entities(hass):
|
async def test_available_template_with_entities(hass):
|
||||||
"""Test availability templates with values from other entities."""
|
"""Test availability templates with values from other entities."""
|
||||||
await setup.async_setup_component(
|
await setup.async_setup_component(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user