291 lines
9.1 KiB
Python
291 lines
9.1 KiB
Python
"""Test template trigger entity."""
|
|
|
|
from typing import Any
|
|
|
|
import pytest
|
|
|
|
from homeassistant.const import (
|
|
CONF_ICON,
|
|
CONF_NAME,
|
|
CONF_STATE,
|
|
CONF_UNIQUE_ID,
|
|
STATE_OFF,
|
|
STATE_ON,
|
|
STATE_UNKNOWN,
|
|
)
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers import template
|
|
from homeassistant.helpers.trigger_template_entity import (
|
|
CONF_ATTRIBUTES,
|
|
CONF_AVAILABILITY,
|
|
CONF_PICTURE,
|
|
ManualTriggerEntity,
|
|
ValueTemplate,
|
|
)
|
|
|
|
_ICON_TEMPLATE = 'mdi:o{{ "n" if value=="on" else "ff" }}'
|
|
_PICTURE_TEMPLATE = '/local/picture_o{{ "n" if value=="on" else "ff" }}'
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("value", "test_template", "error_value", "expected", "error"),
|
|
[
|
|
(1, "{{ value == 1 }}", None, "True", None),
|
|
(1, "1", None, "1", None),
|
|
(
|
|
1,
|
|
"{{ x - 4 }}",
|
|
None,
|
|
None,
|
|
"",
|
|
),
|
|
(
|
|
1,
|
|
"{{ x - 4 }}",
|
|
template._SENTINEL,
|
|
template._SENTINEL,
|
|
"Error parsing value for test.entity: 'x' is undefined (value: 1, template: {{ x - 4 }})",
|
|
),
|
|
],
|
|
)
|
|
async def test_value_template_object(
|
|
hass: HomeAssistant,
|
|
value: Any,
|
|
test_template: str,
|
|
error_value: Any,
|
|
expected: Any,
|
|
error: str | None,
|
|
caplog: pytest.LogCaptureFixture,
|
|
) -> None:
|
|
"""Test ValueTemplate object."""
|
|
entity = ManualTriggerEntity(
|
|
hass,
|
|
{
|
|
CONF_NAME: template.Template("test_entity", hass),
|
|
},
|
|
)
|
|
entity.entity_id = "test.entity"
|
|
|
|
value_template = ValueTemplate.from_template(template.Template(test_template, hass))
|
|
|
|
variables = entity._template_variables_with_value(value)
|
|
result = value_template.async_render_as_value_template(
|
|
entity.entity_id, variables, error_value
|
|
)
|
|
|
|
assert result == expected
|
|
|
|
if error is not None:
|
|
assert error in caplog.text
|
|
|
|
|
|
async def test_template_entity_requires_hass_set(hass: HomeAssistant) -> None:
|
|
"""Test manual trigger template entity."""
|
|
config = {
|
|
"name": template.Template("test_entity", hass),
|
|
"icon": template.Template(
|
|
'{% if value=="on" %} mdi:on {% else %} mdi:off {% endif %}', hass
|
|
),
|
|
"picture": template.Template(
|
|
'{% if value=="on" %} /local/picture_on {% else %} /local/picture_off {% endif %}',
|
|
hass,
|
|
),
|
|
}
|
|
|
|
entity = ManualTriggerEntity(hass, config)
|
|
entity.entity_id = "test.entity"
|
|
hass.states.async_set("test.entity", STATE_ON)
|
|
await entity.async_added_to_hass()
|
|
|
|
variables = entity._template_variables_with_value(STATE_ON)
|
|
entity._process_manual_data(variables)
|
|
await hass.async_block_till_done()
|
|
|
|
assert entity.name == "test_entity"
|
|
assert entity.icon == "mdi:on"
|
|
assert entity.entity_picture == "/local/picture_on"
|
|
|
|
hass.states.async_set("test.entity", STATE_OFF)
|
|
await entity.async_added_to_hass()
|
|
|
|
variables = entity._template_variables_with_value(STATE_OFF)
|
|
entity._process_manual_data(variables)
|
|
await hass.async_block_till_done()
|
|
|
|
assert entity.name == "test_entity"
|
|
assert entity.icon == "mdi:off"
|
|
assert entity.entity_picture == "/local/picture_off"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("test_template", "test_entity_state", "expected"),
|
|
[
|
|
('{{ has_value("test.entity") }}', STATE_ON, True),
|
|
('{{ has_value("test.entity") }}', STATE_OFF, True),
|
|
('{{ has_value("test.entity") }}', STATE_UNKNOWN, False),
|
|
('{{ "a" if has_value("test.entity") else "b" }}', STATE_ON, False),
|
|
('{{ "something_not_boolean" }}', STATE_OFF, False),
|
|
("{{ 1 }}", STATE_OFF, True),
|
|
("{{ 0 }}", STATE_OFF, False),
|
|
],
|
|
)
|
|
async def test_trigger_template_availability(
|
|
hass: HomeAssistant,
|
|
test_template: str,
|
|
test_entity_state: str,
|
|
expected: bool,
|
|
) -> None:
|
|
"""Test manual trigger template entity availability template."""
|
|
config = {
|
|
CONF_NAME: template.Template("test_entity", hass),
|
|
CONF_AVAILABILITY: template.Template(test_template, hass),
|
|
CONF_UNIQUE_ID: "9961786c-f8c8-4ea0-ab1d-b9e922c39088",
|
|
}
|
|
|
|
entity = ManualTriggerEntity(hass, config)
|
|
entity.entity_id = "test.entity"
|
|
hass.states.async_set("test.entity", test_entity_state)
|
|
await entity.async_added_to_hass()
|
|
|
|
variables = entity._template_variables()
|
|
assert entity._render_availability_template(variables) is expected
|
|
await hass.async_block_till_done()
|
|
|
|
assert entity.unique_id == "9961786c-f8c8-4ea0-ab1d-b9e922c39088"
|
|
assert entity.available is expected
|
|
|
|
|
|
async def test_trigger_no_availability_template(
|
|
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
|
) -> None:
|
|
"""Test manual trigger template entity when availability template isn't used."""
|
|
config = {
|
|
CONF_NAME: template.Template("test_entity", hass),
|
|
CONF_ICON: template.Template(_ICON_TEMPLATE, hass),
|
|
CONF_PICTURE: template.Template(_PICTURE_TEMPLATE, hass),
|
|
CONF_STATE: template.Template("{{ value == 'on' }}", hass),
|
|
}
|
|
|
|
class TestEntity(ManualTriggerEntity):
|
|
"""Test entity class."""
|
|
|
|
extra_template_keys = (CONF_STATE,)
|
|
|
|
@property
|
|
def state(self) -> bool | None:
|
|
"""Return extra attributes."""
|
|
return self._rendered.get(CONF_STATE)
|
|
|
|
entity = TestEntity(hass, config)
|
|
entity.entity_id = "test.entity"
|
|
variables = entity._template_variables_with_value(STATE_ON)
|
|
assert entity._render_availability_template(variables) is True
|
|
assert entity.available is True
|
|
entity._process_manual_data(variables)
|
|
await hass.async_block_till_done()
|
|
|
|
assert entity.state == "True"
|
|
assert entity.icon == "mdi:on"
|
|
assert entity.entity_picture == "/local/picture_on"
|
|
|
|
variables = entity._template_variables_with_value(STATE_OFF)
|
|
assert entity._render_availability_template(variables) is True
|
|
assert entity.available is True
|
|
entity._process_manual_data(variables)
|
|
await hass.async_block_till_done()
|
|
|
|
assert entity.state == "False"
|
|
assert entity.icon == "mdi:off"
|
|
assert entity.entity_picture == "/local/picture_off"
|
|
|
|
|
|
async def test_trigger_template_availability_with_syntax_error(
|
|
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
|
) -> None:
|
|
"""Test manual trigger template entity when availability render fails."""
|
|
config = {
|
|
CONF_NAME: template.Template("test_entity", hass),
|
|
CONF_AVAILABILITY: template.Template("{{ incorrect ", hass),
|
|
}
|
|
|
|
entity = ManualTriggerEntity(hass, config)
|
|
entity.entity_id = "test.entity"
|
|
|
|
variables = entity._template_variables()
|
|
entity._render_availability_template(variables)
|
|
assert entity.available is True
|
|
|
|
assert "Error rendering availability template for test.entity" in caplog.text
|
|
|
|
|
|
async def test_attribute_order(
|
|
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
|
) -> None:
|
|
"""Test manual trigger template entity when availability render fails."""
|
|
config = {
|
|
CONF_NAME: template.Template("test_entity", hass),
|
|
CONF_ATTRIBUTES: {
|
|
"beer": template.Template("{{ value }}", hass),
|
|
"no_beer": template.Template("{{ sad - 1 }}", hass),
|
|
"more_beer": template.Template("{{ beer + 1 }}", hass),
|
|
},
|
|
}
|
|
|
|
entity = ManualTriggerEntity(hass, config)
|
|
entity.entity_id = "test.entity"
|
|
hass.states.async_set("test.entity", STATE_ON)
|
|
await entity.async_added_to_hass()
|
|
|
|
variables = entity._template_variables_with_value(1)
|
|
entity._process_manual_data(variables)
|
|
await hass.async_block_till_done()
|
|
|
|
assert entity.extra_state_attributes == {"beer": 1, "more_beer": 2}
|
|
|
|
assert (
|
|
"Error rendering attributes.no_beer template for test.entity: UndefinedError: 'sad' is undefined"
|
|
in caplog.text
|
|
)
|
|
|
|
|
|
async def test_trigger_template_complex(hass: HomeAssistant) -> None:
|
|
"""Test manual trigger template entity complex template."""
|
|
complex_template = """
|
|
{% set d = {'test_key':'test_data'} %}
|
|
{{ dict(d) }}
|
|
|
|
"""
|
|
config = {
|
|
CONF_NAME: template.Template("test_entity", hass),
|
|
CONF_ICON: template.Template(
|
|
'{% if value=="on" %} mdi:on {% else %} mdi:off {% endif %}', hass
|
|
),
|
|
CONF_PICTURE: template.Template(
|
|
'{% if value=="on" %} /local/picture_on {% else %} /local/picture_off {% endif %}',
|
|
hass,
|
|
),
|
|
CONF_AVAILABILITY: template.Template('{{ has_value("test.entity") }}', hass),
|
|
"other_key": template.Template(complex_template, hass),
|
|
}
|
|
|
|
class TestEntity(ManualTriggerEntity):
|
|
"""Test entity class."""
|
|
|
|
extra_template_keys_complex = ("other_key",)
|
|
|
|
@property
|
|
def some_other_key(self) -> dict[str, Any] | None:
|
|
"""Return extra attributes."""
|
|
return self._rendered.get("other_key")
|
|
|
|
entity = TestEntity(hass, config)
|
|
entity.entity_id = "test.entity"
|
|
hass.states.async_set("test.entity", STATE_ON)
|
|
await entity.async_added_to_hass()
|
|
|
|
variables = entity._template_variables_with_value(STATE_ON)
|
|
entity._process_manual_data(variables)
|
|
await hass.async_block_till_done()
|
|
|
|
assert entity.some_other_key == {"test_key": "test_data"}
|