From 85495c08b01ea36850ec67e7e52f490fa7abe023 Mon Sep 17 00:00:00 2001 From: jacekpaszkowski Date: Mon, 24 May 2021 13:31:57 +0200 Subject: [PATCH] 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 --- homeassistant/components/template/light.py | 250 +++++++++- tests/components/template/test_light.py | 533 ++++++++++++++++++++- 2 files changed, 772 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/template/light.py b/homeassistant/components/template/light.py index 2479388eaaf..f546c5dc4da 100644 --- a/homeassistant/components/template/light.py +++ b/homeassistant/components/template/light.py @@ -6,12 +6,16 @@ import voluptuous as vol from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, + ATTR_EFFECT, ATTR_HS_COLOR, + ATTR_TRANSITION, ATTR_WHITE_VALUE, ENTITY_ID_FORMAT, SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, + SUPPORT_EFFECT, + SUPPORT_TRANSITION, SUPPORT_WHITE_VALUE, LightEntity, ) @@ -49,6 +53,12 @@ CONF_COLOR_TEMPLATE = "color_template" CONF_COLOR_ACTION = "set_color" CONF_WHITE_VALUE_TEMPLATE = "white_value_template" 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( cv.deprecated(CONF_ENTITY_ID), @@ -70,6 +80,12 @@ LIGHT_SCHEMA = vol.All( vol.Optional(CONF_COLOR_ACTION): cv.SCRIPT_SCHEMA, vol.Optional(CONF_WHITE_VALUE_TEMPLATE): cv.template, 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, } ), @@ -108,6 +124,15 @@ async def _async_create_entities(hass, config): white_value_action = device_config.get(CONF_WHITE_VALUE_ACTION) 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( LightTemplate( hass, @@ -127,6 +152,12 @@ async def _async_create_entities(hass, config): color_template, white_value_action, white_value_template, + effect_action, + effect_list_template, + effect_template, + max_mireds_template, + min_mireds_template, + supports_transition_template, unique_id, ) ) @@ -161,6 +192,12 @@ class LightTemplate(TemplateEntity, LightEntity): color_template, white_value_action, white_value_template, + effect_action, + effect_list_template, + effect_template, + max_mireds_template, + min_mireds_template, + supports_transition_template, unique_id, ): """Initialize the light.""" @@ -197,12 +234,25 @@ class LightTemplate(TemplateEntity, LightEntity): hass, white_value_action, friendly_name, domain ) 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._brightness = None self._temperature = None self._color = 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 @property @@ -215,6 +265,22 @@ class LightTemplate(TemplateEntity, LightEntity): """Return the CT color value in mireds.""" 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 def white_value(self): """Return the white value.""" @@ -225,6 +291,16 @@ class LightTemplate(TemplateEntity, LightEntity): """Return the hue and saturation color value [float, float].""" 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 def name(self): """Return the display name of this light.""" @@ -247,6 +323,10 @@ class LightTemplate(TemplateEntity, LightEntity): supported_features |= SUPPORT_COLOR if self._white_value_script is not None: 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 @property @@ -268,6 +348,22 @@ class LightTemplate(TemplateEntity, LightEntity): 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", @@ -292,6 +388,30 @@ class LightTemplate(TemplateEntity, LightEntity): self._update_white_value, 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() async def async_turn_on(self, **kwargs): @@ -324,33 +444,65 @@ class LightTemplate(TemplateEntity, LightEntity): self._temperature = kwargs[ATTR_COLOR_TEMP] optimistic_set = True - if ATTR_BRIGHTNESS in kwargs and self._level_script: - await self._level_script.async_run( - {"brightness": kwargs[ATTR_BRIGHTNESS]}, context=self._context - ) - elif ATTR_COLOR_TEMP in kwargs and self._temperature_script: + common_params = {} + + if ATTR_BRIGHTNESS in kwargs: + common_params["brightness"] = kwargs[ATTR_BRIGHTNESS] + + 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( - {"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: + common_params["white_value"] = kwargs[ATTR_WHITE_VALUE] + 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: 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( - {"hs": hs_value, "h": int(hs_value[0]), "s": int(hs_value[1])}, + common_params, context=self._context, ) + elif ATTR_BRIGHTNESS in kwargs and self._level_script: + await self._level_script.async_run(common_params, context=self._context) else: - await self._on_script.async_run(context=self._context) + await self._on_script.async_run(common_params, context=self._context) if optimistic_set: self.async_write_ha_state() async def async_turn_off(self, **kwargs): """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: self._state = False self.async_write_ha_state() @@ -397,6 +549,45 @@ class LightTemplate(TemplateEntity, LightEntity): ) 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 def _update_state(self, result): """Update the state from the template.""" @@ -479,3 +670,42 @@ class LightTemplate(TemplateEntity, LightEntity): else: _LOGGER.error("Received invalid hs_color : (%s)", render) 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) diff --git a/tests/components/template/test_light.py b/tests/components/template/test_light.py index b3a4b2a1aa4..ec0347b8470 100644 --- a/tests/components/template/test_light.py +++ b/tests/components/template/test_light.py @@ -8,8 +8,11 @@ import homeassistant.components.light as light from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, + ATTR_EFFECT, ATTR_HS_COLOR, + ATTR_TRANSITION, ATTR_WHITE_VALUE, + SUPPORT_TRANSITION, ) from homeassistant.const import ( ATTR_ENTITY_ID, @@ -378,6 +381,64 @@ async def test_on_action(hass, calls): 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): """Test on action with optimistic state.""" assert await setup.async_setup_component( @@ -443,7 +504,9 @@ async def test_off_action(hass, calls): "service": "light.turn_on", "entity_id": "light.test_state", }, - "turn_off": {"service": "test.automation"}, + "turn_off": { + "service": "test.automation", + }, "set_level": { "service": "light.turn_on", "data_template": { @@ -477,6 +540,63 @@ async def test_off_action(hass, calls): 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): """Test off action with optimistic state.""" 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 +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): """Test availability templates with values from other entities.""" await setup.async_setup_component(