1
0
mirror of https://github.com/home-assistant/core.git synced 2026-05-08 09:38:58 +01:00

Fix optimistic set to false for template entities (#150421)

This commit is contained in:
Petro31
2025-08-15 07:53:48 -04:00
committed by GitHub
parent 7bd126dc8e
commit 792bb5781d
11 changed files with 322 additions and 5 deletions
+7 -3
View File
@@ -34,16 +34,20 @@ class AbstractTemplateEntity(Entity):
self._action_scripts: dict[str, Script] = {}
if self._optimistic_entity:
optimistic = config.get(CONF_OPTIMISTIC)
self._template = config.get(CONF_STATE)
optimistic = self._template is None
assumed_optimistic = self._template is None
if self._extra_optimistic_options:
optimistic = optimistic and all(
assumed_optimistic = assumed_optimistic and all(
config.get(option) is None
for option in self._extra_optimistic_options
)
self._attr_assumed_state = optimistic or config.get(CONF_OPTIMISTIC, False)
self._attr_assumed_state = optimistic or (
optimistic is None and assumed_optimistic
)
if (object_id := config.get(CONF_OBJECT_ID)) is not None:
self.entity_id = async_generate_entity_id(
@@ -102,7 +102,7 @@ TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA = vol.Schema(
TEMPLATE_ENTITY_OPTIMISTIC_SCHEMA = {
vol.Optional(CONF_OPTIMISTIC, default=False): cv.boolean,
vol.Optional(CONF_OPTIMISTIC): cv.boolean,
}
@@ -973,3 +973,35 @@ async def test_optimistic(hass: HomeAssistant) -> None:
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == AlarmControlPanelState.ARMED_HOME
@pytest.mark.parametrize(
("count", "panel_config"),
[
(
1,
{
"name": TEST_OBJECT_ID,
"state": "{{ states('alarm_control_panel.test') }}",
**OPTIMISTIC_TEMPLATE_ALARM_CONFIG,
"optimistic": False,
},
)
],
)
@pytest.mark.parametrize(
"style",
[ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER],
)
@pytest.mark.usefixtures("setup_panel")
async def test_not_optimistic(hass: HomeAssistant) -> None:
"""Test optimistic yaml option set to false."""
await hass.services.async_call(
ALARM_DOMAIN,
"alarm_arm_away",
{"entity_id": TEST_ENTITY_ID, "code": "1234"},
blocking=True,
)
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == STATE_UNKNOWN
+28 -1
View File
@@ -628,11 +628,38 @@ async def test_template_position(
],
)
@pytest.mark.usefixtures("setup_cover")
async def test_template_not_optimistic(hass: HomeAssistant) -> None:
async def test_template_not_optimistic(
hass: HomeAssistant,
calls: list[ServiceCall],
) -> None:
"""Test the is_closed attribute."""
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == STATE_UNKNOWN
# Test to make sure optimistic is not set with only a position template.
await hass.services.async_call(
COVER_DOMAIN,
SERVICE_OPEN_COVER,
{ATTR_ENTITY_ID: TEST_ENTITY_ID},
blocking=True,
)
await hass.async_block_till_done()
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == STATE_UNKNOWN
# Test to make sure optimistic is not set with only a position template.
await hass.services.async_call(
COVER_DOMAIN,
SERVICE_CLOSE_COVER,
{ATTR_ENTITY_ID: TEST_ENTITY_ID},
blocking=True,
)
await hass.async_block_till_done()
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == STATE_UNKNOWN
@pytest.mark.parametrize(("count", "state_template"), [(1, "{{ 1 == 1 }}")])
@pytest.mark.parametrize(
+33
View File
@@ -1885,6 +1885,39 @@ async def test_optimistic_option(hass: HomeAssistant) -> None:
assert state.state == STATE_OFF
@pytest.mark.parametrize(
("count", "fan_config"),
[
(
1,
{
"name": TEST_OBJECT_ID,
"state": "{{ is_state('sensor.test_sensor', 'on') }}",
"turn_on": [],
"turn_off": [],
"optimistic": False,
},
)
],
)
@pytest.mark.parametrize(
"style",
[ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER],
)
@pytest.mark.usefixtures("setup_fan")
async def test_not_optimistic(hass: HomeAssistant) -> None:
"""Test optimistic yaml option set to false."""
await hass.services.async_call(
fan.DOMAIN,
"turn_on",
{"entity_id": TEST_ENTITY_ID},
blocking=True,
)
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == STATE_OFF
async def test_setup_config_entry(
hass: HomeAssistant,
snapshot: SnapshotAssertion,
+36
View File
@@ -2795,6 +2795,42 @@ async def test_optimistic_option(hass: HomeAssistant) -> None:
assert state.state == STATE_OFF
@pytest.mark.parametrize(
("count", "light_config"),
[
(
1,
{
"name": TEST_OBJECT_ID,
"state": "{{ is_state('light.test_state', 'on') }}",
"turn_on": [],
"turn_off": [],
"optimistic": False,
},
)
],
)
@pytest.mark.parametrize(
("style", "expected"),
[
(ConfigurationStyle.MODERN, STATE_OFF),
(ConfigurationStyle.TRIGGER, STATE_UNKNOWN),
],
)
@pytest.mark.usefixtures("setup_light")
async def test_not_optimistic(hass: HomeAssistant, expected: str) -> None:
"""Test optimistic yaml option set to false."""
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 == expected
async def test_setup_config_entry(
hass: HomeAssistant,
snapshot: SnapshotAssertion,
+33
View File
@@ -1190,6 +1190,39 @@ async def test_optimistic(hass: HomeAssistant) -> None:
assert state.state == LockState.UNLOCKED
@pytest.mark.parametrize(
("count", "lock_config"),
[
(
1,
{
"name": TEST_OBJECT_ID,
"state": "{{ is_state('sensor.test_state', 'on') }}",
"lock": [],
"unlock": [],
"optimistic": False,
},
)
],
)
@pytest.mark.parametrize(
"style",
[ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER],
)
@pytest.mark.usefixtures("setup_lock")
async def test_not_optimistic(hass: HomeAssistant) -> None:
"""Test optimistic yaml option set to false."""
await hass.services.async_call(
lock.DOMAIN,
lock.SERVICE_LOCK,
{ATTR_ENTITY_ID: TEST_ENTITY_ID},
blocking=True,
)
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == LockState.UNLOCKED
async def test_setup_config_entry(
hass: HomeAssistant,
snapshot: SnapshotAssertion,
+31
View File
@@ -605,6 +605,37 @@ async def test_optimistic(hass: HomeAssistant) -> None:
assert float(state.state) == 2
@pytest.mark.parametrize(
("count", "number_config"),
[
(
1,
{
"state": "{{ states('sensor.test_state') }}",
"optimistic": False,
"set_value": [],
},
)
],
)
@pytest.mark.parametrize(
"style",
[ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER],
)
@pytest.mark.usefixtures("setup_number")
async def test_not_optimistic(hass: HomeAssistant) -> None:
"""Test optimistic yaml option set to false."""
await hass.services.async_call(
number.DOMAIN,
number.SERVICE_SET_VALUE,
{ATTR_ENTITY_ID: _TEST_NUMBER, "value": 4},
blocking=True,
)
state = hass.states.get(_TEST_NUMBER)
assert state.state == STATE_UNKNOWN
@pytest.mark.parametrize(
("count", "number_config"),
[
+36
View File
@@ -601,6 +601,42 @@ async def test_optimistic(hass: HomeAssistant) -> None:
assert state.state == "yes"
@pytest.mark.parametrize(
("count", "select_config"),
[
(
1,
{
"state": "{{ states('select.test_state') }}",
"optimistic": False,
"options": "{{ ['test', 'yes', 'no'] }}",
"select_option": [],
},
)
],
)
@pytest.mark.parametrize(
"style",
[ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER],
)
@pytest.mark.usefixtures("setup_select")
async def test_not_optimistic(hass: HomeAssistant) -> None:
"""Test optimistic yaml option set to false."""
# Ensure Trigger template entities update the options list
hass.states.async_set(TEST_STATE_ENTITY_ID, "anything")
await hass.async_block_till_done()
await hass.services.async_call(
select.DOMAIN,
select.SERVICE_SELECT_OPTION,
{ATTR_ENTITY_ID: _TEST_SELECT, "option": "test"},
blocking=True,
)
state = hass.states.get(_TEST_SELECT)
assert state.state == STATE_UNKNOWN
@pytest.mark.parametrize(
("count", "select_config"),
[
+37
View File
@@ -14,6 +14,7 @@ from homeassistant.const import (
STATE_OFF,
STATE_ON,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from homeassistant.core import CoreState, HomeAssistant, ServiceCall, State
from homeassistant.helpers import device_registry as dr, entity_registry as er
@@ -1267,3 +1268,39 @@ async def test_optimistic_option(hass: HomeAssistant) -> None:
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == STATE_OFF
@pytest.mark.parametrize(
("count", "switch_config"),
[
(
1,
{
"name": TEST_OBJECT_ID,
"state": "{{ is_state('switch.test_state', 'on') }}",
"turn_on": [],
"turn_off": [],
"optimistic": False,
},
)
],
)
@pytest.mark.parametrize(
("style", "expected"),
[
(ConfigurationStyle.MODERN, STATE_OFF),
(ConfigurationStyle.TRIGGER, STATE_UNKNOWN),
],
)
@pytest.mark.usefixtures("setup_switch")
async def test_not_optimistic(hass: HomeAssistant, expected: str) -> None:
"""Test optimistic yaml option set to false."""
await hass.services.async_call(
switch.DOMAIN,
"turn_on",
{"entity_id": TEST_ENTITY_ID},
blocking=True,
)
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == expected
+48
View File
@@ -1299,6 +1299,54 @@ async def test_optimistic_option(
assert state.state == VacuumActivity.DOCKED
@pytest.mark.parametrize(
("count", "vacuum_config"),
[
(
1,
{
"name": TEST_OBJECT_ID,
"state": "{{ states('sensor.test_state') }}",
"start": [],
**TEMPLATE_VACUUM_ACTIONS,
"optimistic": False,
},
)
],
)
@pytest.mark.parametrize(
"style",
[ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER],
)
@pytest.mark.parametrize(
"service",
[
vacuum.SERVICE_START,
vacuum.SERVICE_PAUSE,
vacuum.SERVICE_STOP,
vacuum.SERVICE_RETURN_TO_BASE,
vacuum.SERVICE_CLEAN_SPOT,
],
)
@pytest.mark.usefixtures("setup_vacuum")
async def test_not_optimistic(
hass: HomeAssistant,
service: str,
calls: list[ServiceCall],
) -> None:
"""Test optimistic yaml option set to false."""
await hass.services.async_call(
vacuum.DOMAIN,
service,
{"entity_id": TEST_ENTITY_ID},
blocking=True,
)
await hass.async_block_till_done()
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == STATE_UNKNOWN
async def test_setup_config_entry(
hass: HomeAssistant,
snapshot: SnapshotAssertion,