mirror of
https://github.com/home-assistant/core.git
synced 2025-11-10 03:19:34 +00:00
478 lines
15 KiB
Python
478 lines
15 KiB
Python
"""Test Template config."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
import voluptuous as vol
|
|
|
|
from homeassistant.components.template import DOMAIN
|
|
from homeassistant.components.template.config import (
|
|
CONFIG_SECTION_SCHEMA,
|
|
async_validate_config_section,
|
|
)
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers import issue_registry as ir
|
|
from homeassistant.helpers.script_variables import ScriptVariables
|
|
from homeassistant.helpers.template import Template
|
|
from homeassistant.setup import async_setup_component
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"config",
|
|
[
|
|
{
|
|
"trigger": {"trigger": "event", "event_type": "my_event"},
|
|
"button": {
|
|
"press": {
|
|
"service": "test.automation",
|
|
"data_template": {"caller": "{{ this.entity_id }}"},
|
|
},
|
|
"device_class": "restart",
|
|
"unique_id": "test",
|
|
"name": "test",
|
|
"icon": "mdi:test",
|
|
},
|
|
},
|
|
{
|
|
"trigger": {"trigger": "event", "event_type": "my_event"},
|
|
"action": {
|
|
"service": "test.automation",
|
|
"data_template": {"caller": "{{ this.entity_id }}"},
|
|
},
|
|
"button": {
|
|
"press": {
|
|
"service": "test.automation",
|
|
"data_template": {"caller": "{{ this.entity_id }}"},
|
|
},
|
|
"device_class": "restart",
|
|
"unique_id": "test",
|
|
"name": "test",
|
|
"icon": "mdi:test",
|
|
},
|
|
},
|
|
],
|
|
)
|
|
async def test_invalid_schema(hass: HomeAssistant, config: dict) -> None:
|
|
"""Test invalid config schemas."""
|
|
with pytest.raises(vol.Invalid):
|
|
CONFIG_SECTION_SCHEMA(config)
|
|
|
|
|
|
async def test_valid_default_entity_id(hass: HomeAssistant) -> None:
|
|
"""Test valid default_entity_id schemas."""
|
|
config = {
|
|
"button": {
|
|
"press": [],
|
|
"default_entity_id": "button.test",
|
|
},
|
|
}
|
|
assert CONFIG_SECTION_SCHEMA(config) == {
|
|
"button": [
|
|
{
|
|
"press": [],
|
|
"name": Template("Template Button", hass),
|
|
"default_entity_id": "button.test",
|
|
}
|
|
]
|
|
}
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"default_entity_id",
|
|
[
|
|
"foo",
|
|
"{{ 'my_template' }}",
|
|
"SJLIVan as dfkaj;heafha faass00",
|
|
48,
|
|
None,
|
|
"bttn.test",
|
|
],
|
|
)
|
|
async def test_invalid_default_entity_id(
|
|
hass: HomeAssistant, default_entity_id: dict
|
|
) -> None:
|
|
"""Test invalid default_entity_id schemas."""
|
|
config = {
|
|
"button": {
|
|
"press": [],
|
|
"default_entity_id": default_entity_id,
|
|
},
|
|
}
|
|
with pytest.raises(vol.Invalid):
|
|
CONFIG_SECTION_SCHEMA(config)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("config", "expected_error"),
|
|
[
|
|
(
|
|
{
|
|
"trigger": {"trigger": "event", "event_type": "my_event"},
|
|
"binary_sensor": {
|
|
"state": "{{ states('binary_sensor.test') }}",
|
|
"unique_id": "test",
|
|
"name": "test",
|
|
"auto_off": "00:00:01",
|
|
},
|
|
},
|
|
None,
|
|
),
|
|
(
|
|
{
|
|
"binary_sensor": {
|
|
"state": "{{ states('binary_sensor.test') }}",
|
|
"name": "test",
|
|
},
|
|
},
|
|
None,
|
|
),
|
|
(
|
|
{
|
|
"binary_sensor": {
|
|
"state": "{{ states('binary_sensor.test') }}",
|
|
"auto_off": "00:00:01",
|
|
},
|
|
},
|
|
"The auto_off option for template binary sensor: name: Template Binary Sensor",
|
|
),
|
|
(
|
|
{
|
|
"binary_sensor": {
|
|
"state": "{{ states('binary_sensor.test') }}",
|
|
"name": "test",
|
|
"auto_off": "00:00:01",
|
|
},
|
|
},
|
|
"The auto_off option for template binary sensor: name: test",
|
|
),
|
|
(
|
|
{
|
|
"binary_sensor": {
|
|
"state": "{{ states('binary_sensor.test') }}",
|
|
"unique_id": "test_unique_id",
|
|
"auto_off": "00:00:01",
|
|
},
|
|
},
|
|
"The auto_off option for template binary sensor: unique_id: test_unique_id",
|
|
),
|
|
(
|
|
{
|
|
"binary_sensor": {
|
|
"state": "{{ states('binary_sensor.test') }}",
|
|
"name": "test",
|
|
"unique_id": "test_unique_id",
|
|
"auto_off": "00:00:01",
|
|
},
|
|
},
|
|
"The auto_off option for template binary sensor: name: test",
|
|
),
|
|
(
|
|
{
|
|
"binary_sensor": {
|
|
"state": "{{ states('binary_sensor.test') }}",
|
|
"default_entity_id": "binary_sensor.test_entity_id",
|
|
"auto_off": "00:00:01",
|
|
},
|
|
},
|
|
"The auto_off option for template binary sensor: default_entity_id: binary_sensor.test_entity_id",
|
|
),
|
|
(
|
|
{
|
|
"binary_sensor": {
|
|
"state": "{{ states('binary_sensor.test') }}",
|
|
"name": "test",
|
|
"unique_id": "test_unique_id",
|
|
"default_entity_id": "binary_sensor.test_entity_id",
|
|
"auto_off": "00:00:01",
|
|
},
|
|
},
|
|
"The auto_off option for template binary sensor: name: test",
|
|
),
|
|
(
|
|
{
|
|
"binary_sensor": {
|
|
"state": "{{ states('binary_sensor.test') }}",
|
|
"unique_id": "test_unique_id",
|
|
"default_entity_id": "binary_sensor.test_entity_id",
|
|
"auto_off": "00:00:01",
|
|
},
|
|
},
|
|
"The auto_off option for template binary sensor: default_entity_id: binary_sensor.test_entity_id",
|
|
),
|
|
],
|
|
)
|
|
async def test_invalid_binary_sensor_schema_with_auto_off(
|
|
hass: HomeAssistant,
|
|
config: dict,
|
|
expected_error: str | None,
|
|
caplog: pytest.LogCaptureFixture,
|
|
) -> None:
|
|
"""Test invalid config schemas create issue and log warning."""
|
|
|
|
await async_setup_component(hass, "template", {"template": [config]})
|
|
|
|
assert (
|
|
expected_error is None and "ERROR" not in caplog.text
|
|
) or expected_error in caplog.text
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("config", "expected"),
|
|
[
|
|
(
|
|
{
|
|
"variables": {"a": 1},
|
|
"button": {
|
|
"press": {
|
|
"service": "test.automation",
|
|
"data_template": {"caller": "{{ this.entity_id }}"},
|
|
},
|
|
"variables": {"b": 2},
|
|
"device_class": "restart",
|
|
"unique_id": "test",
|
|
"name": "test",
|
|
"icon": "mdi:test",
|
|
},
|
|
},
|
|
{"a": 1, "b": 2},
|
|
),
|
|
(
|
|
{
|
|
"variables": {"a": 1},
|
|
"button": [
|
|
{
|
|
"press": {
|
|
"service": "test.automation",
|
|
"data_template": {"caller": "{{ this.entity_id }}"},
|
|
},
|
|
"variables": {"b": 2},
|
|
"device_class": "restart",
|
|
"unique_id": "test",
|
|
"name": "test",
|
|
"icon": "mdi:test",
|
|
}
|
|
],
|
|
},
|
|
{"a": 1, "b": 2},
|
|
),
|
|
(
|
|
{
|
|
"variables": {"a": 1},
|
|
"button": [
|
|
{
|
|
"press": {
|
|
"service": "test.automation",
|
|
"data_template": {"caller": "{{ this.entity_id }}"},
|
|
},
|
|
"variables": {"a": 2, "b": 2},
|
|
"device_class": "restart",
|
|
"unique_id": "test",
|
|
"name": "test",
|
|
"icon": "mdi:test",
|
|
}
|
|
],
|
|
},
|
|
{"a": 2, "b": 2},
|
|
),
|
|
(
|
|
{
|
|
"variables": {"a": 1},
|
|
"button": {
|
|
"press": {
|
|
"service": "test.automation",
|
|
"data_template": {"caller": "{{ this.entity_id }}"},
|
|
},
|
|
"device_class": "restart",
|
|
"unique_id": "test",
|
|
"name": "test",
|
|
"icon": "mdi:test",
|
|
},
|
|
},
|
|
{"a": 1},
|
|
),
|
|
(
|
|
{
|
|
"button": {
|
|
"press": {
|
|
"service": "test.automation",
|
|
"data_template": {"caller": "{{ this.entity_id }}"},
|
|
},
|
|
"variables": {"b": 2},
|
|
"device_class": "restart",
|
|
"unique_id": "test",
|
|
"name": "test",
|
|
"icon": "mdi:test",
|
|
},
|
|
},
|
|
{"b": 2},
|
|
),
|
|
],
|
|
)
|
|
async def test_combined_state_variables(
|
|
hass: HomeAssistant, config: dict, expected: dict
|
|
) -> None:
|
|
"""Tests combining variables for state based template entities."""
|
|
validated = await async_validate_config_section(hass, config)
|
|
assert "variables" not in validated
|
|
variables: ScriptVariables = validated["button"][0]["variables"]
|
|
assert variables.as_dict() == expected
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("config", "expected_root", "expected_entity"),
|
|
[
|
|
(
|
|
{
|
|
"trigger": {"trigger": "event", "event_type": "my_event"},
|
|
"variables": {"a": 1},
|
|
"binary_sensor": {
|
|
"name": "test",
|
|
"state": "{{ trigger.event.event_type }}",
|
|
"variables": {"b": 2},
|
|
},
|
|
},
|
|
{"a": 1},
|
|
{"b": 2},
|
|
),
|
|
(
|
|
{
|
|
"triggers": {"trigger": "event", "event_type": "my_event"},
|
|
"variables": {"a": 1},
|
|
"binary_sensor": {
|
|
"name": "test",
|
|
"state": "{{ trigger.event.event_type }}",
|
|
},
|
|
},
|
|
{"a": 1},
|
|
{},
|
|
),
|
|
(
|
|
{
|
|
"trigger": {"trigger": "event", "event_type": "my_event"},
|
|
"binary_sensor": {
|
|
"name": "test",
|
|
"state": "{{ trigger.event.event_type }}",
|
|
"variables": {"b": 2},
|
|
},
|
|
},
|
|
{},
|
|
{"b": 2},
|
|
),
|
|
],
|
|
)
|
|
async def test_combined_trigger_variables(
|
|
hass: HomeAssistant,
|
|
config: dict,
|
|
expected_root: dict,
|
|
expected_entity: dict,
|
|
) -> None:
|
|
"""Tests variable are not combined for trigger based template entities."""
|
|
empty = ScriptVariables({})
|
|
validated = await async_validate_config_section(hass, config)
|
|
root_variables: ScriptVariables = validated.get("variables", empty)
|
|
assert root_variables.as_dict() == expected_root
|
|
variables: ScriptVariables = validated["binary_sensor"][0].get("variables", empty)
|
|
assert variables.as_dict() == expected_entity
|
|
|
|
|
|
async def test_state_init_attribute_variables(
|
|
hass: HomeAssistant,
|
|
) -> None:
|
|
"""Test a state based template entity initializes icon, name, and picture with variables."""
|
|
source = "switch.foo"
|
|
entity_id = "sensor.foo"
|
|
|
|
hass.states.async_set(source, "on", {"friendly_name": "Foo"})
|
|
config = {
|
|
"template": [
|
|
{
|
|
"variables": {
|
|
"switch": "switch.foo",
|
|
"on_icon": "mdi:lightbulb",
|
|
"on_picture": "on.png",
|
|
},
|
|
"sensor": {
|
|
"variables": {
|
|
"off_icon": "mdi:lightbulb-off",
|
|
"off_picture": "off.png",
|
|
},
|
|
"name": "{{ state_attr(switch, 'friendly_name') }}",
|
|
"icon": "{{ on_icon if is_state(switch, 'on') else off_icon }}",
|
|
"picture": "{{ on_picture if is_state(switch, 'on') else off_picture }}",
|
|
"state": "{{ is_state(switch, 'on') }}",
|
|
},
|
|
}
|
|
],
|
|
}
|
|
assert await async_setup_component(
|
|
hass,
|
|
DOMAIN,
|
|
config,
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
# Check initial state
|
|
sensor = hass.states.get(entity_id)
|
|
assert sensor
|
|
assert sensor.state == "True"
|
|
assert sensor.attributes["icon"] == "mdi:lightbulb"
|
|
assert sensor.attributes["entity_picture"] == "on.png"
|
|
assert sensor.attributes["friendly_name"] == "Foo"
|
|
|
|
hass.states.async_set(source, "off", {"friendly_name": "Foo"})
|
|
await hass.async_block_till_done()
|
|
|
|
# Check to see that the template light works
|
|
sensor = hass.states.get(entity_id)
|
|
assert sensor
|
|
assert sensor.state == "False"
|
|
assert sensor.attributes["icon"] == "mdi:lightbulb-off"
|
|
assert sensor.attributes["entity_picture"] == "off.png"
|
|
assert sensor.attributes["friendly_name"] == "Foo"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("config", "expected_warning"),
|
|
[
|
|
(
|
|
{
|
|
"trigger": {"trigger": "event", "event_type": "my_event"},
|
|
},
|
|
"Invalid template configuration found, trigger option is missing matching domain",
|
|
),
|
|
(
|
|
{
|
|
"action": {
|
|
"service": "test.automation",
|
|
"data_template": {"caller": "{{ this.entity_id }}"},
|
|
},
|
|
"sensor": {
|
|
"state": "{{ states('sensor.test') }}",
|
|
"unique_id": "test",
|
|
"name": "test",
|
|
"icon": "mdi:test",
|
|
},
|
|
},
|
|
"Invalid template configuration found, action option requires a trigger",
|
|
),
|
|
],
|
|
)
|
|
async def test_invalid_schema_raises_issue(
|
|
hass: HomeAssistant,
|
|
config: dict,
|
|
expected_warning: str,
|
|
issue_registry: ir.IssueRegistry,
|
|
caplog: pytest.LogCaptureFixture,
|
|
) -> None:
|
|
"""Test invalid config schemas create issue and log warning."""
|
|
|
|
await async_setup_component(hass, "template", {"template": [config]})
|
|
|
|
assert expected_warning in caplog.text
|
|
|
|
assert len(issue_registry.issues) == 1
|
|
issue = next(iter(issue_registry.issues.values()))
|
|
|
|
assert issue.domain == "template"
|
|
assert issue.severity == ir.IssueSeverity.WARNING
|