mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Add trigger vacuum entities to template integration (#145534)
* Add trigger vacuum entities to template integration * remove comment --------- Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
parent
7bb9936e81
commit
7eaa60b17c
@ -159,7 +159,6 @@ CONFIG_SECTION_SCHEMA = vol.All(
|
|||||||
ensure_domains_do_not_have_trigger_or_action(
|
ensure_domains_do_not_have_trigger_or_action(
|
||||||
DOMAIN_BUTTON,
|
DOMAIN_BUTTON,
|
||||||
DOMAIN_FAN,
|
DOMAIN_FAN,
|
||||||
DOMAIN_VACUUM,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
|
|
||||||
from .const import CONF_OBJECT_ID, DOMAIN
|
from .const import CONF_OBJECT_ID, DOMAIN
|
||||||
|
from .coordinator import TriggerUpdateCoordinator
|
||||||
from .entity import AbstractTemplateEntity
|
from .entity import AbstractTemplateEntity
|
||||||
from .template_entity import (
|
from .template_entity import (
|
||||||
LEGACY_FIELDS as TEMPLATE_ENTITY_LEGACY_FIELDS,
|
LEGACY_FIELDS as TEMPLATE_ENTITY_LEGACY_FIELDS,
|
||||||
@ -48,6 +49,7 @@ from .template_entity import (
|
|||||||
make_template_entity_common_modern_attributes_schema,
|
make_template_entity_common_modern_attributes_schema,
|
||||||
rewrite_common_legacy_to_modern_conf,
|
rewrite_common_legacy_to_modern_conf,
|
||||||
)
|
)
|
||||||
|
from .trigger_entity import TriggerEntity
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -187,6 +189,13 @@ async def async_setup_platform(
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if "coordinator" in discovery_info:
|
||||||
|
async_add_entities(
|
||||||
|
TriggerVacuumEntity(hass, discovery_info["coordinator"], config)
|
||||||
|
for config in discovery_info["entities"]
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
_async_create_template_tracking_entities(
|
_async_create_template_tracking_entities(
|
||||||
async_add_entities,
|
async_add_entities,
|
||||||
hass,
|
hass,
|
||||||
@ -213,7 +222,14 @@ class AbstractTemplateVacuum(AbstractTemplateEntity, StateVacuumEntity):
|
|||||||
# List of valid fan speeds
|
# List of valid fan speeds
|
||||||
self._attr_fan_speed_list = config[CONF_FAN_SPEED_LIST]
|
self._attr_fan_speed_list = config[CONF_FAN_SPEED_LIST]
|
||||||
|
|
||||||
def _register_scripts(
|
self._attr_supported_features = (
|
||||||
|
VacuumEntityFeature.START | VacuumEntityFeature.STATE
|
||||||
|
)
|
||||||
|
|
||||||
|
if self._battery_level_template:
|
||||||
|
self._attr_supported_features |= VacuumEntityFeature.BATTERY
|
||||||
|
|
||||||
|
def _iterate_scripts(
|
||||||
self, config: dict[str, Any]
|
self, config: dict[str, Any]
|
||||||
) -> Generator[tuple[str, Sequence[dict[str, Any]], VacuumEntityFeature | int]]:
|
) -> Generator[tuple[str, Sequence[dict[str, Any]], VacuumEntityFeature | int]]:
|
||||||
for action_id, supported_feature in (
|
for action_id, supported_feature in (
|
||||||
@ -356,18 +372,12 @@ class TemplateVacuum(TemplateEntity, AbstractTemplateVacuum):
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
assert name is not None
|
assert name is not None
|
||||||
|
|
||||||
self._attr_supported_features = (
|
for action_id, action_config, supported_feature in self._iterate_scripts(
|
||||||
VacuumEntityFeature.START | VacuumEntityFeature.STATE
|
|
||||||
)
|
|
||||||
for action_id, action_config, supported_feature in self._register_scripts(
|
|
||||||
config
|
config
|
||||||
):
|
):
|
||||||
self.add_script(action_id, action_config, name, DOMAIN)
|
self.add_script(action_id, action_config, name, DOMAIN)
|
||||||
self._attr_supported_features |= supported_feature
|
self._attr_supported_features |= supported_feature
|
||||||
|
|
||||||
if self._battery_level_template:
|
|
||||||
self._attr_supported_features |= VacuumEntityFeature.BATTERY
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_setup_templates(self) -> None:
|
def _async_setup_templates(self) -> None:
|
||||||
"""Set up templates."""
|
"""Set up templates."""
|
||||||
@ -403,3 +413,59 @@ class TemplateVacuum(TemplateEntity, AbstractTemplateVacuum):
|
|||||||
return
|
return
|
||||||
|
|
||||||
self._handle_state(result)
|
self._handle_state(result)
|
||||||
|
|
||||||
|
|
||||||
|
class TriggerVacuumEntity(TriggerEntity, AbstractTemplateVacuum):
|
||||||
|
"""Vacuum entity based on trigger data."""
|
||||||
|
|
||||||
|
domain = VACUUM_DOMAIN
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
coordinator: TriggerUpdateCoordinator,
|
||||||
|
config: ConfigType,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the entity."""
|
||||||
|
TriggerEntity.__init__(self, hass, coordinator, config)
|
||||||
|
AbstractTemplateVacuum.__init__(self, config)
|
||||||
|
|
||||||
|
self._attr_name = name = self._rendered.get(CONF_NAME, DEFAULT_NAME)
|
||||||
|
|
||||||
|
for action_id, action_config, supported_feature in self._iterate_scripts(
|
||||||
|
config
|
||||||
|
):
|
||||||
|
self.add_script(action_id, action_config, name, DOMAIN)
|
||||||
|
self._attr_supported_features |= supported_feature
|
||||||
|
|
||||||
|
for key in (CONF_STATE, CONF_FAN_SPEED, CONF_BATTERY_LEVEL):
|
||||||
|
if isinstance(config.get(key), template.Template):
|
||||||
|
self._to_render_simple.append(key)
|
||||||
|
self._parse_result.add(key)
|
||||||
|
|
||||||
|
@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_STATE, self._handle_state),
|
||||||
|
(CONF_FAN_SPEED, self._update_fan_speed),
|
||||||
|
(CONF_BATTERY_LEVEL, self._update_battery_level),
|
||||||
|
):
|
||||||
|
if (rendered := self._rendered.get(key)) is not None:
|
||||||
|
updater(rendered)
|
||||||
|
write_ha_state = True
|
||||||
|
|
||||||
|
if len(self._rendered) > 0:
|
||||||
|
# In case any non optimistic template
|
||||||
|
write_ha_state = True
|
||||||
|
|
||||||
|
if write_ha_state:
|
||||||
|
self.async_set_context(self.coordinator.data["context"])
|
||||||
|
self.async_write_ha_state()
|
||||||
|
@ -26,8 +26,26 @@ from tests.components.vacuum import common
|
|||||||
TEST_OBJECT_ID = "test_vacuum"
|
TEST_OBJECT_ID = "test_vacuum"
|
||||||
TEST_ENTITY_ID = f"vacuum.{TEST_OBJECT_ID}"
|
TEST_ENTITY_ID = f"vacuum.{TEST_OBJECT_ID}"
|
||||||
|
|
||||||
STATE_INPUT_SELECT = "input_select.state"
|
TEST_STATE_SENSOR = "sensor.test_state"
|
||||||
BATTERY_LEVEL_INPUT_NUMBER = "input_number.battery_level"
|
TEST_SPEED_SENSOR = "sensor.test_fan_speed"
|
||||||
|
TEST_BATTERY_LEVEL_SENSOR = "sensor.test_battery_level"
|
||||||
|
TEST_AVAILABILITY_ENTITY = "availability_state.state"
|
||||||
|
|
||||||
|
TEST_STATE_TRIGGER = {
|
||||||
|
"trigger": {
|
||||||
|
"trigger": "state",
|
||||||
|
"entity_id": [
|
||||||
|
TEST_STATE_SENSOR,
|
||||||
|
TEST_SPEED_SENSOR,
|
||||||
|
TEST_BATTERY_LEVEL_SENSOR,
|
||||||
|
TEST_AVAILABILITY_ENTITY,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"variables": {"triggering_entity": "{{ trigger.entity_id }}"},
|
||||||
|
"action": [
|
||||||
|
{"event": "action_event", "event_data": {"what": "{{ triggering_entity }}"}}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
START_ACTION = {
|
START_ACTION = {
|
||||||
"start": {
|
"start": {
|
||||||
@ -140,6 +158,24 @@ async def async_setup_modern_format(
|
|||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_trigger_format(
|
||||||
|
hass: HomeAssistant, count: int, vacuum_config: dict[str, Any]
|
||||||
|
) -> None:
|
||||||
|
"""Do setup of vacuum integration via trigger format."""
|
||||||
|
config = {"template": {"vacuum": vacuum_config, **TEST_STATE_TRIGGER}}
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
async def setup_vacuum(
|
async def setup_vacuum(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
@ -152,6 +188,8 @@ async def setup_vacuum(
|
|||||||
await async_setup_legacy_format(hass, count, vacuum_config)
|
await async_setup_legacy_format(hass, count, vacuum_config)
|
||||||
elif style == ConfigurationStyle.MODERN:
|
elif style == ConfigurationStyle.MODERN:
|
||||||
await async_setup_modern_format(hass, count, vacuum_config)
|
await async_setup_modern_format(hass, count, vacuum_config)
|
||||||
|
elif style == ConfigurationStyle.TRIGGER:
|
||||||
|
await async_setup_trigger_format(hass, count, vacuum_config)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -171,6 +209,10 @@ async def setup_test_vacuum_with_extra_config(
|
|||||||
await async_setup_modern_format(
|
await async_setup_modern_format(
|
||||||
hass, count, {"name": TEST_OBJECT_ID, **vacuum_config, **extra_config}
|
hass, count, {"name": TEST_OBJECT_ID, **vacuum_config, **extra_config}
|
||||||
)
|
)
|
||||||
|
elif style == ConfigurationStyle.TRIGGER:
|
||||||
|
await async_setup_trigger_format(
|
||||||
|
hass, count, {"name": TEST_OBJECT_ID, **vacuum_config, **extra_config}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -202,6 +244,16 @@ async def setup_state_vacuum(
|
|||||||
**TEMPLATE_VACUUM_ACTIONS,
|
**TEMPLATE_VACUUM_ACTIONS,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
elif style == ConfigurationStyle.TRIGGER:
|
||||||
|
await async_setup_trigger_format(
|
||||||
|
hass,
|
||||||
|
count,
|
||||||
|
{
|
||||||
|
"name": TEST_OBJECT_ID,
|
||||||
|
"state": state_template,
|
||||||
|
**TEMPLATE_VACUUM_ACTIONS,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -236,6 +288,17 @@ async def setup_base_vacuum(
|
|||||||
**extra_config,
|
**extra_config,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
elif style == ConfigurationStyle.TRIGGER:
|
||||||
|
state_config = {"state": state_template} if state_template else {}
|
||||||
|
await async_setup_trigger_format(
|
||||||
|
hass,
|
||||||
|
count,
|
||||||
|
{
|
||||||
|
"name": TEST_OBJECT_ID,
|
||||||
|
**state_config,
|
||||||
|
**extra_config,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -277,6 +340,19 @@ async def setup_single_attribute_state_vacuum(
|
|||||||
**extra_config,
|
**extra_config,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
elif style == ConfigurationStyle.TRIGGER:
|
||||||
|
state_config = {"state": state_template} if state_template else {}
|
||||||
|
await async_setup_trigger_format(
|
||||||
|
hass,
|
||||||
|
count,
|
||||||
|
{
|
||||||
|
"name": TEST_OBJECT_ID,
|
||||||
|
**state_config,
|
||||||
|
**TEMPLATE_VACUUM_ACTIONS,
|
||||||
|
**extra,
|
||||||
|
**extra_config,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -313,6 +389,18 @@ async def setup_attributes_state_vacuum(
|
|||||||
**TEMPLATE_VACUUM_ACTIONS,
|
**TEMPLATE_VACUUM_ACTIONS,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
elif style == ConfigurationStyle.TRIGGER:
|
||||||
|
state_config = {"state": state_template} if state_template else {}
|
||||||
|
await async_setup_trigger_format(
|
||||||
|
hass,
|
||||||
|
count,
|
||||||
|
{
|
||||||
|
"name": TEST_OBJECT_ID,
|
||||||
|
"attributes": attributes,
|
||||||
|
**state_config,
|
||||||
|
**TEMPLATE_VACUUM_ACTIONS,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("count", [1])
|
@pytest.mark.parametrize("count", [1])
|
||||||
@ -333,6 +421,13 @@ async def setup_attributes_state_vacuum(
|
|||||||
STATE_UNKNOWN,
|
STATE_UNKNOWN,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
ConfigurationStyle.TRIGGER,
|
||||||
|
None,
|
||||||
|
{"start": {"service": "script.vacuum_start"}},
|
||||||
|
STATE_UNKNOWN,
|
||||||
|
None,
|
||||||
|
),
|
||||||
(
|
(
|
||||||
ConfigurationStyle.LEGACY,
|
ConfigurationStyle.LEGACY,
|
||||||
"{{ 'cleaning' }}",
|
"{{ 'cleaning' }}",
|
||||||
@ -353,6 +448,16 @@ async def setup_attributes_state_vacuum(
|
|||||||
VacuumActivity.CLEANING,
|
VacuumActivity.CLEANING,
|
||||||
100,
|
100,
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
ConfigurationStyle.TRIGGER,
|
||||||
|
"{{ 'cleaning' }}",
|
||||||
|
{
|
||||||
|
"battery_level": "{{ 100 }}",
|
||||||
|
"start": {"service": "script.vacuum_start"},
|
||||||
|
},
|
||||||
|
VacuumActivity.CLEANING,
|
||||||
|
100,
|
||||||
|
),
|
||||||
(
|
(
|
||||||
ConfigurationStyle.LEGACY,
|
ConfigurationStyle.LEGACY,
|
||||||
"{{ 'abc' }}",
|
"{{ 'abc' }}",
|
||||||
@ -373,6 +478,16 @@ async def setup_attributes_state_vacuum(
|
|||||||
STATE_UNKNOWN,
|
STATE_UNKNOWN,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
ConfigurationStyle.TRIGGER,
|
||||||
|
"{{ 'abc' }}",
|
||||||
|
{
|
||||||
|
"battery_level": "{{ 101 }}",
|
||||||
|
"start": {"service": "script.vacuum_start"},
|
||||||
|
},
|
||||||
|
STATE_UNKNOWN,
|
||||||
|
None,
|
||||||
|
),
|
||||||
(
|
(
|
||||||
ConfigurationStyle.LEGACY,
|
ConfigurationStyle.LEGACY,
|
||||||
"{{ this_function_does_not_exist() }}",
|
"{{ this_function_does_not_exist() }}",
|
||||||
@ -395,18 +510,35 @@ async def setup_attributes_state_vacuum(
|
|||||||
STATE_UNKNOWN,
|
STATE_UNKNOWN,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
ConfigurationStyle.TRIGGER,
|
||||||
|
"{{ this_function_does_not_exist() }}",
|
||||||
|
{
|
||||||
|
"battery_level": "{{ this_function_does_not_exist() }}",
|
||||||
|
"fan_speed": "{{ this_function_does_not_exist() }}",
|
||||||
|
"start": {"service": "script.vacuum_start"},
|
||||||
|
},
|
||||||
|
STATE_UNAVAILABLE,
|
||||||
|
None,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.usefixtures("setup_base_vacuum")
|
@pytest.mark.usefixtures("setup_base_vacuum")
|
||||||
async def test_valid_legacy_configs(hass: HomeAssistant, count, parm1, parm2) -> None:
|
async def test_valid_legacy_configs(hass: HomeAssistant, count, parm1, parm2) -> None:
|
||||||
"""Test: configs."""
|
"""Test: configs."""
|
||||||
|
|
||||||
|
# Ensure trigger entity templates are rendered
|
||||||
|
hass.states.async_set(TEST_STATE_SENSOR, None)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert len(hass.states.async_all("vacuum")) == count
|
assert len(hass.states.async_all("vacuum")) == count
|
||||||
_verify(hass, parm1, parm2)
|
_verify(hass, parm1, parm2)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("count", [0])
|
@pytest.mark.parametrize("count", [0])
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
|
"style",
|
||||||
|
[ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER],
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("state_template", "extra_config"),
|
("state_template", "extra_config"),
|
||||||
@ -423,13 +555,14 @@ async def test_invalid_configs(hass: HomeAssistant, count) -> None:
|
|||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("count", "state_template", "extra_config"),
|
("count", "state_template", "extra_config"),
|
||||||
[(1, "{{ states('input_select.state') }}", {})],
|
[(1, "{{ states('sensor.test_state') }}", {})],
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("style", "attribute"),
|
("style", "attribute"),
|
||||||
[
|
[
|
||||||
(ConfigurationStyle.LEGACY, "battery_level_template"),
|
(ConfigurationStyle.LEGACY, "battery_level_template"),
|
||||||
(ConfigurationStyle.MODERN, "battery_level"),
|
(ConfigurationStyle.MODERN, "battery_level"),
|
||||||
|
(ConfigurationStyle.TRIGGER, "battery_level"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@ -447,6 +580,10 @@ async def test_battery_level_template(
|
|||||||
hass: HomeAssistant, expected: int | None
|
hass: HomeAssistant, expected: int | None
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test templates with values from other entities."""
|
"""Test templates with values from other entities."""
|
||||||
|
# Ensure trigger entity templates are rendered
|
||||||
|
hass.states.async_set(TEST_STATE_SENSOR, None)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
_verify(hass, STATE_UNKNOWN, expected)
|
_verify(hass, STATE_UNKNOWN, expected)
|
||||||
|
|
||||||
|
|
||||||
@ -455,7 +592,7 @@ async def test_battery_level_template(
|
|||||||
[
|
[
|
||||||
(
|
(
|
||||||
1,
|
1,
|
||||||
"{{ states('input_select.state') }}",
|
"{{ states('sensor.test_state') }}",
|
||||||
{
|
{
|
||||||
"fan_speeds": ["low", "medium", "high"],
|
"fan_speeds": ["low", "medium", "high"],
|
||||||
},
|
},
|
||||||
@ -467,6 +604,7 @@ async def test_battery_level_template(
|
|||||||
[
|
[
|
||||||
(ConfigurationStyle.LEGACY, "fan_speed_template"),
|
(ConfigurationStyle.LEGACY, "fan_speed_template"),
|
||||||
(ConfigurationStyle.MODERN, "fan_speed"),
|
(ConfigurationStyle.MODERN, "fan_speed"),
|
||||||
|
(ConfigurationStyle.TRIGGER, "fan_speed"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@ -481,33 +619,39 @@ async def test_battery_level_template(
|
|||||||
@pytest.mark.usefixtures("setup_single_attribute_state_vacuum")
|
@pytest.mark.usefixtures("setup_single_attribute_state_vacuum")
|
||||||
async def test_fan_speed_template(hass: HomeAssistant, expected: str | None) -> None:
|
async def test_fan_speed_template(hass: HomeAssistant, expected: str | None) -> None:
|
||||||
"""Test templates with values from other entities."""
|
"""Test templates with values from other entities."""
|
||||||
|
# Ensure trigger entity templates are rendered
|
||||||
|
hass.states.async_set(TEST_STATE_SENSOR, None)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
_verify(hass, STATE_UNKNOWN, None, expected)
|
_verify(hass, STATE_UNKNOWN, None, expected)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("count", "state_template", "attribute_template", "extra_config"),
|
("count", "state_template", "attribute_template", "extra_config", "attribute"),
|
||||||
[
|
[
|
||||||
(
|
(
|
||||||
1,
|
1,
|
||||||
"{{ 'on' }}",
|
"{{ 'on' }}",
|
||||||
"{% if states.switch.test_state.state %}mdi:check{% endif %}",
|
"{% if states.sensor.test_state.state %}mdi:check{% endif %}",
|
||||||
{},
|
{},
|
||||||
|
"icon",
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("style", "attribute"),
|
("style", "expected"),
|
||||||
[
|
[
|
||||||
(ConfigurationStyle.MODERN, "icon"),
|
(ConfigurationStyle.MODERN, ""),
|
||||||
|
(ConfigurationStyle.TRIGGER, None),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.usefixtures("setup_single_attribute_state_vacuum")
|
@pytest.mark.usefixtures("setup_single_attribute_state_vacuum")
|
||||||
async def test_icon_template(hass: HomeAssistant) -> None:
|
async def test_icon_template(hass: HomeAssistant, expected: int) -> None:
|
||||||
"""Test icon template."""
|
"""Test icon template."""
|
||||||
state = hass.states.get(TEST_ENTITY_ID)
|
state = hass.states.get(TEST_ENTITY_ID)
|
||||||
assert state.attributes.get("icon") in ("", None)
|
assert state.attributes.get("icon") == expected
|
||||||
|
|
||||||
hass.states.async_set("switch.test_state", STATE_ON)
|
hass.states.async_set(TEST_STATE_SENSOR, STATE_ON)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
state = hass.states.get(TEST_ENTITY_ID)
|
state = hass.states.get(TEST_ENTITY_ID)
|
||||||
@ -515,29 +659,31 @@ async def test_icon_template(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("count", "state_template", "attribute_template", "extra_config"),
|
("count", "state_template", "attribute_template", "extra_config", "attribute"),
|
||||||
[
|
[
|
||||||
(
|
(
|
||||||
1,
|
1,
|
||||||
"{{ 'on' }}",
|
"{{ 'on' }}",
|
||||||
"{% if states.switch.test_state.state %}local/vacuum.png{% endif %}",
|
"{% if states.sensor.test_state.state %}local/vacuum.png{% endif %}",
|
||||||
{},
|
{},
|
||||||
|
"picture",
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("style", "attribute"),
|
("style", "expected"),
|
||||||
[
|
[
|
||||||
(ConfigurationStyle.MODERN, "picture"),
|
(ConfigurationStyle.MODERN, ""),
|
||||||
|
(ConfigurationStyle.TRIGGER, None),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.usefixtures("setup_single_attribute_state_vacuum")
|
@pytest.mark.usefixtures("setup_single_attribute_state_vacuum")
|
||||||
async def test_picture_template(hass: HomeAssistant) -> None:
|
async def test_picture_template(hass: HomeAssistant, expected: int) -> None:
|
||||||
"""Test picture template."""
|
"""Test picture template."""
|
||||||
state = hass.states.get(TEST_ENTITY_ID)
|
state = hass.states.get(TEST_ENTITY_ID)
|
||||||
assert state.attributes.get("entity_picture") in ("", None)
|
assert state.attributes.get("entity_picture") == expected
|
||||||
|
|
||||||
hass.states.async_set("switch.test_state", STATE_ON)
|
hass.states.async_set(TEST_STATE_SENSOR, STATE_ON)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
state = hass.states.get(TEST_ENTITY_ID)
|
state = hass.states.get(TEST_ENTITY_ID)
|
||||||
@ -560,6 +706,7 @@ async def test_picture_template(hass: HomeAssistant) -> None:
|
|||||||
[
|
[
|
||||||
(ConfigurationStyle.LEGACY, "availability_template"),
|
(ConfigurationStyle.LEGACY, "availability_template"),
|
||||||
(ConfigurationStyle.MODERN, "availability"),
|
(ConfigurationStyle.MODERN, "availability"),
|
||||||
|
(ConfigurationStyle.TRIGGER, "availability"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.usefixtures("setup_single_attribute_state_vacuum")
|
@pytest.mark.usefixtures("setup_single_attribute_state_vacuum")
|
||||||
@ -567,14 +714,14 @@ async def test_available_template_with_entities(hass: HomeAssistant) -> None:
|
|||||||
"""Test availability templates with values from other entities."""
|
"""Test availability templates with values from other entities."""
|
||||||
|
|
||||||
# When template returns true..
|
# When template returns true..
|
||||||
hass.states.async_set("availability_state.state", STATE_ON)
|
hass.states.async_set(TEST_AVAILABILITY_ENTITY, STATE_ON)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
# Device State should not be unavailable
|
# Device State should not be unavailable
|
||||||
assert hass.states.get(TEST_ENTITY_ID).state != STATE_UNAVAILABLE
|
assert hass.states.get(TEST_ENTITY_ID).state != STATE_UNAVAILABLE
|
||||||
|
|
||||||
# When Availability template returns false
|
# When Availability template returns false
|
||||||
hass.states.async_set("availability_state.state", STATE_OFF)
|
hass.states.async_set(TEST_AVAILABILITY_ENTITY, STATE_OFF)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
# device state should be unavailable
|
# device state should be unavailable
|
||||||
@ -597,15 +744,22 @@ async def test_available_template_with_entities(hass: HomeAssistant) -> None:
|
|||||||
[
|
[
|
||||||
(ConfigurationStyle.LEGACY, "availability_template"),
|
(ConfigurationStyle.LEGACY, "availability_template"),
|
||||||
(ConfigurationStyle.MODERN, "availability"),
|
(ConfigurationStyle.MODERN, "availability"),
|
||||||
|
(ConfigurationStyle.TRIGGER, "availability"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.usefixtures("setup_single_attribute_state_vacuum")
|
@pytest.mark.usefixtures("setup_single_attribute_state_vacuum")
|
||||||
async def test_invalid_availability_template_keeps_component_available(
|
async def test_invalid_availability_template_keeps_component_available(
|
||||||
hass: HomeAssistant, caplog_setup_text
|
hass: HomeAssistant, caplog_setup_text, caplog: pytest.LogCaptureFixture
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test that an invalid availability keeps the device available."""
|
"""Test that an invalid availability keeps the device available."""
|
||||||
|
|
||||||
|
# Ensure state change triggers trigger entity.
|
||||||
|
hass.states.async_set(TEST_STATE_SENSOR, None)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert hass.states.get(TEST_ENTITY_ID) != STATE_UNAVAILABLE
|
assert hass.states.get(TEST_ENTITY_ID) != STATE_UNAVAILABLE
|
||||||
assert "UndefinedError: 'x' is undefined" in caplog_setup_text
|
err = "'x' is undefined"
|
||||||
|
assert err in caplog_setup_text or err in caplog.text
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@ -627,7 +781,7 @@ async def test_attribute_templates(hass: HomeAssistant) -> None:
|
|||||||
state = hass.states.get(TEST_ENTITY_ID)
|
state = hass.states.get(TEST_ENTITY_ID)
|
||||||
assert state.attributes["test_attribute"] == "It ."
|
assert state.attributes["test_attribute"] == "It ."
|
||||||
|
|
||||||
hass.states.async_set("sensor.test_state", "Works")
|
hass.states.async_set(TEST_STATE_SENSOR, "Works")
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
await async_update_entity(hass, TEST_ENTITY_ID)
|
await async_update_entity(hass, TEST_ENTITY_ID)
|
||||||
state = hass.states.get(TEST_ENTITY_ID)
|
state = hass.states.get(TEST_ENTITY_ID)
|
||||||
@ -635,26 +789,31 @@ async def test_attribute_templates(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
|
"style",
|
||||||
|
[ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER],
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("count", "state_template", "attributes"),
|
("count", "state_template", "attributes"),
|
||||||
[
|
[
|
||||||
(
|
(
|
||||||
1,
|
1,
|
||||||
"{{ states('input_select.state') }}",
|
"{{ states('sensor.test_state') }}",
|
||||||
{"test_attribute": "{{ this_function_does_not_exist() }}"},
|
{"test_attribute": "{{ this_function_does_not_exist() }}"},
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.usefixtures("setup_attributes_state_vacuum")
|
@pytest.mark.usefixtures("setup_attributes_state_vacuum")
|
||||||
async def test_invalid_attribute_template(
|
async def test_invalid_attribute_template(
|
||||||
hass: HomeAssistant, caplog_setup_text
|
hass: HomeAssistant, caplog_setup_text, caplog: pytest.LogCaptureFixture
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test that errors are logged if rendering template fails."""
|
"""Test that errors are logged if rendering template fails."""
|
||||||
|
|
||||||
|
hass.states.async_set(TEST_STATE_SENSOR, "Works")
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert len(hass.states.async_all("vacuum")) == 1
|
assert len(hass.states.async_all("vacuum")) == 1
|
||||||
assert "test_attribute" in caplog_setup_text
|
err = "'this_function_does_not_exist' is undefined"
|
||||||
assert "TemplateError" in caplog_setup_text
|
assert err in caplog_setup_text or err in caplog.text
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("count", [1])
|
@pytest.mark.parametrize("count", [1])
|
||||||
@ -689,6 +848,21 @@ async def test_invalid_attribute_template(
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
ConfigurationStyle.TRIGGER,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "test_template_vacuum_01",
|
||||||
|
"state": "{{ true }}",
|
||||||
|
**UNIQUE_ID_CONFIG,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "test_template_vacuum_02",
|
||||||
|
"state": "{{ false }}",
|
||||||
|
**UNIQUE_ID_CONFIG,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.usefixtures("setup_vacuum")
|
@pytest.mark.usefixtures("setup_vacuum")
|
||||||
@ -701,7 +875,8 @@ async def test_unique_id(hass: HomeAssistant) -> None:
|
|||||||
("count", "state_template", "extra_config"), [(1, None, START_ACTION)]
|
("count", "state_template", "extra_config"), [(1, None, START_ACTION)]
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
|
"style",
|
||||||
|
[ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER],
|
||||||
)
|
)
|
||||||
@pytest.mark.usefixtures("setup_base_vacuum")
|
@pytest.mark.usefixtures("setup_base_vacuum")
|
||||||
async def test_unused_services(hass: HomeAssistant) -> None:
|
async def test_unused_services(hass: HomeAssistant) -> None:
|
||||||
@ -741,10 +916,11 @@ async def test_unused_services(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("count", "state_template"),
|
("count", "state_template"),
|
||||||
[(1, "{{ states('input_select.state') }}")],
|
[(1, "{{ states('sensor.test_state') }}")],
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
|
"style",
|
||||||
|
[ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER],
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"action",
|
"action",
|
||||||
@ -782,8 +958,8 @@ async def test_state_services(
|
|||||||
[
|
[
|
||||||
(
|
(
|
||||||
1,
|
1,
|
||||||
"{{ states('input_select.state') }}",
|
"{{ states('sensor.test_state') }}",
|
||||||
"{{ states('input_select.fan_speed') }}",
|
"{{ states('sensor.test_fan_speed') }}",
|
||||||
{
|
{
|
||||||
"fan_speeds": ["low", "medium", "high"],
|
"fan_speeds": ["low", "medium", "high"],
|
||||||
},
|
},
|
||||||
@ -795,6 +971,7 @@ async def test_state_services(
|
|||||||
[
|
[
|
||||||
(ConfigurationStyle.LEGACY, "fan_speed_template"),
|
(ConfigurationStyle.LEGACY, "fan_speed_template"),
|
||||||
(ConfigurationStyle.MODERN, "fan_speed"),
|
(ConfigurationStyle.MODERN, "fan_speed"),
|
||||||
|
(ConfigurationStyle.TRIGGER, "fan_speed"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.usefixtures("setup_single_attribute_state_vacuum")
|
@pytest.mark.usefixtures("setup_single_attribute_state_vacuum")
|
||||||
@ -835,8 +1012,8 @@ async def test_set_fan_speed(hass: HomeAssistant, calls: list[ServiceCall]) -> N
|
|||||||
[
|
[
|
||||||
(
|
(
|
||||||
1,
|
1,
|
||||||
"{{ states('input_select.state') }}",
|
"{{ states('sensor.test_state') }}",
|
||||||
"{{ states('input_select.fan_speed') }}",
|
"{{ states('sensor.test_fan_speed') }}",
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@ -845,6 +1022,7 @@ async def test_set_fan_speed(hass: HomeAssistant, calls: list[ServiceCall]) -> N
|
|||||||
[
|
[
|
||||||
(ConfigurationStyle.LEGACY, "fan_speed_template"),
|
(ConfigurationStyle.LEGACY, "fan_speed_template"),
|
||||||
(ConfigurationStyle.MODERN, "fan_speed"),
|
(ConfigurationStyle.MODERN, "fan_speed"),
|
||||||
|
(ConfigurationStyle.TRIGGER, "fan_speed"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.usefixtures("setup_single_attribute_state_vacuum")
|
@pytest.mark.usefixtures("setup_single_attribute_state_vacuum")
|
||||||
@ -918,7 +1096,8 @@ async def test_nested_unique_id(
|
|||||||
|
|
||||||
@pytest.mark.parametrize(("count", "vacuum_config"), [(1, {"start": []})])
|
@pytest.mark.parametrize(("count", "vacuum_config"), [(1, {"start": []})])
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
|
"style",
|
||||||
|
[ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER],
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("extra_config", "supported_features"),
|
("extra_config", "supported_features"),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user