Add optimistic option to light yaml (#149395)

This commit is contained in:
Petro31 2025-07-28 10:59:57 -04:00 committed by GitHub
parent 49bd15718c
commit d823b574c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 63 additions and 7 deletions

View File

@ -53,6 +53,7 @@ from .entity import AbstractTemplateEntity
from .helpers import async_setup_template_platform
from .template_entity import (
TEMPLATE_ENTITY_COMMON_SCHEMA_LEGACY,
TEMPLATE_ENTITY_OPTIMISTIC_SCHEMA,
TemplateEntity,
make_template_entity_common_modern_schema,
)
@ -121,7 +122,7 @@ LEGACY_FIELDS = {
DEFAULT_NAME = "Template Light"
LIGHT_YAML_SCHEMA = vol.Schema(
LIGHT_COMMON_SCHEMA = vol.Schema(
{
vol.Inclusive(CONF_EFFECT_ACTION, "effect"): cv.SCRIPT_SCHEMA,
vol.Inclusive(CONF_EFFECT_LIST, "effect"): cv.template,
@ -132,6 +133,8 @@ LIGHT_YAML_SCHEMA = vol.Schema(
vol.Optional(CONF_LEVEL): cv.template,
vol.Optional(CONF_MAX_MIREDS): cv.template,
vol.Optional(CONF_MIN_MIREDS): cv.template,
vol.Required(CONF_OFF_ACTION): cv.SCRIPT_SCHEMA,
vol.Required(CONF_ON_ACTION): cv.SCRIPT_SCHEMA,
vol.Optional(CONF_RGB_ACTION): cv.SCRIPT_SCHEMA,
vol.Optional(CONF_RGB): cv.template,
vol.Optional(CONF_RGBW_ACTION): cv.SCRIPT_SCHEMA,
@ -142,9 +145,11 @@ LIGHT_YAML_SCHEMA = vol.Schema(
vol.Optional(CONF_SUPPORTS_TRANSITION): cv.template,
vol.Optional(CONF_TEMPERATURE_ACTION): cv.SCRIPT_SCHEMA,
vol.Optional(CONF_TEMPERATURE): cv.template,
vol.Required(CONF_OFF_ACTION): cv.SCRIPT_SCHEMA,
vol.Required(CONF_ON_ACTION): cv.SCRIPT_SCHEMA,
}
)
LIGHT_YAML_SCHEMA = LIGHT_COMMON_SCHEMA.extend(
TEMPLATE_ENTITY_OPTIMISTIC_SCHEMA
).extend(make_template_entity_common_modern_schema(DEFAULT_NAME).schema)
LIGHT_LEGACY_YAML_SCHEMA = vol.All(
@ -215,6 +220,7 @@ class AbstractTemplateLight(AbstractTemplateEntity, LightEntity):
"""Representation of a template lights features."""
_entity_id_format = ENTITY_ID_FORMAT
_optimistic_entity = True
# The super init is not called because TemplateEntity and TriggerEntity will call AbstractTemplateEntity.__init__.
# This ensures that the __init__ on AbstractTemplateEntity is not called twice.
@ -224,7 +230,6 @@ class AbstractTemplateLight(AbstractTemplateEntity, LightEntity):
"""Initialize the features."""
# Template attributes
self._template = config.get(CONF_STATE)
self._level_template = config.get(CONF_LEVEL)
self._temperature_template = config.get(CONF_TEMPERATURE)
self._hs_template = config.get(CONF_HS)
@ -349,7 +354,7 @@ class AbstractTemplateLight(AbstractTemplateEntity, LightEntity):
Returns True if any attribute was updated.
"""
optimistic_set = False
if self._template is None:
if self._attr_assumed_state:
self._state = True
optimistic_set = True
@ -1066,7 +1071,7 @@ class StateLightEntity(TemplateEntity, AbstractTemplateLight):
)
else:
await self.async_run_script(off_script, context=self._context)
if self._template is None:
if self._attr_assumed_state:
self._state = False
self.async_write_ha_state()
@ -1205,6 +1210,6 @@ class TriggerLightEntity(TriggerEntity, AbstractTemplateLight):
)
else:
await self.async_run_script(off_script, context=self._context)
if self._template is None:
if self._attr_assumed_state:
self._state = False
self.async_write_ha_state()

View File

@ -37,6 +37,9 @@ from tests.common import assert_setup_component
# Represent for light's availability
_STATE_AVAILABILITY_BOOLEAN = "availability_boolean.state"
TEST_OBJECT_ID = "test_light"
TEST_ENTITY_ID = f"light.{TEST_OBJECT_ID}"
TEST_STATE_ENTITY_ID = "light.test_state"
OPTIMISTIC_ON_OFF_LIGHT_CONFIG = {
"turn_on": {
@ -2740,3 +2743,51 @@ async def test_effect_with_empty_action(
"""Test empty set_effect action."""
state = hass.states.get("light.test_template_light")
assert state.attributes["supported_features"] == LightEntityFeature.EFFECT
@pytest.mark.parametrize(
("count", "light_config"),
[
(
1,
{
"name": TEST_OBJECT_ID,
"state": "{{ is_state('light.test_state', 'on') }}",
"turn_on": [],
"turn_off": [],
"optimistic": True,
},
)
],
)
@pytest.mark.parametrize(
"style",
[ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER],
)
@pytest.mark.usefixtures("setup_light")
async def test_optimistic_option(hass: HomeAssistant) -> None:
"""Test optimistic yaml option."""
hass.states.async_set(TEST_STATE_ENTITY_ID, STATE_OFF)
await hass.async_block_till_done()
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == STATE_OFF
await hass.services.async_call(
light.DOMAIN,
"turn_on",
{"entity_id": TEST_ENTITY_ID},
blocking=True,
)
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == STATE_ON
hass.states.async_set(TEST_STATE_ENTITY_ID, STATE_ON)
await hass.async_block_till_done()
hass.states.async_set(TEST_STATE_ENTITY_ID, STATE_OFF)
await hass.async_block_till_done()
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == STATE_OFF