Files
core/tests/helpers/test_trigger_template_entity.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

332 lines
11 KiB
Python
Raw Normal View History

"""Test template trigger entity."""
from typing import Any
import pytest
from homeassistant.components.sensor import SensorDeviceClass
from homeassistant.components.sensor.helpers import ( # pylint: disable=hass-component-root-import
async_parse_date_datetime,
)
from homeassistant.const import (
CONF_DEVICE_CLASS,
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,
ManualTriggerSensorEntity,
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"}
async def test_manual_trigger_sensor_entity_with_date(
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_STATE: template.Template("{{ as_datetime(value) }}", hass),
CONF_DEVICE_CLASS: SensorDeviceClass.TIMESTAMP,
}
class TestEntity(ManualTriggerSensorEntity):
"""Test entity class."""
extra_template_keys = (CONF_STATE,)
@property
def state(self) -> bool | None:
"""Return extra attributes."""
return "2025-01-01T00:00:00+00:00"
entity = TestEntity(hass, config)
entity.entity_id = "test.entity"
variables = entity._template_variables_with_value("2025-01-01T00:00:00+00:00")
assert entity._render_availability_template(variables) is True
assert entity.available is True
entity._set_native_value_with_possible_timestamp(entity.state)
await hass.async_block_till_done()
assert entity.native_value == async_parse_date_datetime(
"2025-01-01T00:00:00+00:00", entity.entity_id, entity.device_class
)
assert entity.state == "2025-01-01T00:00:00+00:00"
assert entity.device_class == SensorDeviceClass.TIMESTAMP