Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions homeassistant/helpers/template/extensions/state.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""State functions for Home Assistant templates."""

import collections.abc
from collections.abc import Iterable
import logging
from typing import TYPE_CHECKING, Any
Expand Down Expand Up @@ -162,7 +161,7 @@ def expand(self, *args: Any) -> Iterable[State]:
continue
elif isinstance(entity, State):
entity_id = entity.entity_id
elif isinstance(entity, collections.abc.Iterable):
elif isinstance(entity, Iterable):
search += entity
continue
else:
Expand Down
95 changes: 0 additions & 95 deletions tests/helpers/template/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,6 @@ def test_invalid_template(hass: HomeAssistant) -> None:
tmpl.async_render()


def test_invalid_entity_id(hass: HomeAssistant) -> None:
"""Test referring states by entity id."""
with pytest.raises(TemplateError):
render(hass, '{{ states["big.fat..."] }}')
with pytest.raises(TemplateError):
render(hass, '{{ states.test["big.fat..."] }}')
with pytest.raises(TemplateError):
render(hass, '{{ states["invalid/domain"] }}')


def test_raise_exception_on_error(hass: HomeAssistant) -> None:
"""Test raising an exception on error."""
with pytest.raises(TemplateError):
Expand Down Expand Up @@ -599,19 +589,6 @@ def test_state_with_unit_and_rounding_options(
assert tpl2.async_render() == output2_2


def test_length_of_states(hass: HomeAssistant) -> None:
"""Test fetching the length of states."""
hass.states.async_set("sensor.test", "23")
hass.states.async_set("sensor.test2", "wow")
hass.states.async_set("climate.test2", "cooling")

result = render(hass, "{{ states | length }}")
assert result == 3

result = render(hass, "{{ states.sensor | length }}")
assert result == 2


def test_render_complex_handling_non_template_values(hass: HomeAssistant) -> None:
"""Test that we can render non-template fields."""
assert template.render_complex(
Expand Down Expand Up @@ -707,21 +684,6 @@ async def test_demo_template(hass: HomeAssistant) -> None:
assert "sun" in result


async def test_slice_states(hass: HomeAssistant) -> None:
"""Test iterating states with a slice."""
hass.states.async_set("sensor.test", "23")

result = render(
hass,
(
"{% for states in states | slice(1) -%}{% set state = states | first %}"
"{{ state.entity_id }}"
"{%- endfor %}"
),
)
assert result == "sensor.test"


async def test_lifecycle(hass: HomeAssistant) -> None:
"""Test that we limit template render info for lifecycle events."""
hass.states.async_set("sun.sun", "above", {"elevation": 50, "next_rising": "later"})
Expand Down Expand Up @@ -833,63 +795,6 @@ async def test_template_errors(hass: HomeAssistant) -> None:
render(hass, "{{ utcnow() | random }}")


async def test_state_attributes(hass: HomeAssistant) -> None:
"""Test state attributes."""
hass.states.async_set("sensor.test", "23")

result = render(hass, "{{ states.sensor.test.last_changed }}")
assert result == str(hass.states.get("sensor.test").last_changed)

result = render(hass, "{{ states.sensor.test.object_id }}")
assert result == hass.states.get("sensor.test").object_id

result = render(hass, "{{ states.sensor.test.domain }}")
assert result == hass.states.get("sensor.test").domain

result = render(hass, "{{ states.sensor.test.context.id }}")
assert result == hass.states.get("sensor.test").context.id

result = render(hass, "{{ states.sensor.test.state_with_unit }}")
assert result == 23

result = render(hass, "{{ states.sensor.test.invalid_prop }}")
assert result == ""

with pytest.raises(TemplateError):
render(hass, "{{ states.sensor.test.invalid_prop.xx }}")


async def test_unavailable_states(hass: HomeAssistant) -> None:
"""Test watching unavailable states."""

for i in range(10):
hass.states.async_set(f"light.sensor{i}", "on")

hass.states.async_set("light.unavailable", "unavailable")
hass.states.async_set("light.unknown", "unknown")
hass.states.async_set("light.none", "none")

result = render(
hass,
(
"{{ states | selectattr('state', 'in', ['unavailable','unknown','none']) "
"| sort(attribute='entity_id') | map(attribute='entity_id') | list | join(', ') }}"
),
)
assert result == "light.none, light.unavailable, light.unknown"

result = render(
hass,
(
"{{ states.light "
"| selectattr('state', 'in', ['unavailable','unknown','none']) "
"| sort(attribute='entity_id') | map(attribute='entity_id') | list "
"| join(', ') }}"
),
)
assert result == "light.none, light.unavailable, light.unknown"


async def test_no_result_parsing(hass: HomeAssistant) -> None:
"""Test if templates results are not parsed."""
hass.states.async_set("sensor.temperature", "12")
Expand Down
98 changes: 98 additions & 0 deletions tests/helpers/template/test_states.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@

from homeassistant.const import STATE_ON
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import TemplateError
from homeassistant.helpers import template
from homeassistant.helpers.json import json_dumps
from homeassistant.helpers.template import states as template_states
from homeassistant.util import dt as dt_util

from .helpers import render

from tests.common import async_fire_time_changed


Expand Down Expand Up @@ -76,3 +79,98 @@ async def test_lru_increases_with_many_entities(hass: HomeAssistant) -> None:
assert template_states.CACHED_TEMPLATE_NO_COLLECT_LRU.get_size() == int(
round(mock_entity_count * template_states.ENTITY_COUNT_GROWTH_FACTOR)
)


def test_invalid_entity_id(hass: HomeAssistant) -> None:
"""Test referring states by entity id."""
with pytest.raises(TemplateError):
render(hass, '{{ states["big.fat..."] }}')
with pytest.raises(TemplateError):
render(hass, '{{ states.test["big.fat..."] }}')
with pytest.raises(TemplateError):
render(hass, '{{ states["invalid/domain"] }}')


def test_length_of_states(hass: HomeAssistant) -> None:
"""Test fetching the length of states."""
hass.states.async_set("sensor.test", "23")
hass.states.async_set("sensor.test2", "wow")
hass.states.async_set("climate.test2", "cooling")

result = render(hass, "{{ states | length }}")
assert result == 3

result = render(hass, "{{ states.sensor | length }}")
assert result == 2


async def test_slice_states(hass: HomeAssistant) -> None:
"""Test iterating states with a slice."""
hass.states.async_set("sensor.test", "23")

result = render(
hass,
(
"{% for states in states | slice(1) -%}{% set state = states | first %}"
"{{ state.entity_id }}"
"{%- endfor %}"
),
)
assert result == "sensor.test"


async def test_state_attributes(hass: HomeAssistant) -> None:
"""Test state attributes."""
hass.states.async_set("sensor.test", "23")

result = render(hass, "{{ states.sensor.test.last_changed }}")
assert result == str(hass.states.get("sensor.test").last_changed)

result = render(hass, "{{ states.sensor.test.object_id }}")
assert result == hass.states.get("sensor.test").object_id

result = render(hass, "{{ states.sensor.test.domain }}")
assert result == hass.states.get("sensor.test").domain

result = render(hass, "{{ states.sensor.test.context.id }}")
assert result == hass.states.get("sensor.test").context.id

result = render(hass, "{{ states.sensor.test.state_with_unit }}")
assert result == 23

result = render(hass, "{{ states.sensor.test.invalid_prop }}")
assert result == ""

with pytest.raises(TemplateError):
render(hass, "{{ states.sensor.test.invalid_prop.xx }}")


async def test_unavailable_states(hass: HomeAssistant) -> None:
"""Test watching unavailable states."""

for i in range(10):
hass.states.async_set(f"light.sensor{i}", "on")

hass.states.async_set("light.unavailable", "unavailable")
hass.states.async_set("light.unknown", "unknown")
hass.states.async_set("light.none", "none")

result = render(
hass,
(
"{{ states | selectattr('state', 'in', ['unavailable','unknown','none']) "
"| sort(attribute='entity_id') | map(attribute='entity_id') | list | join(', ') }}"
),
)
assert result == "light.none, light.unavailable, light.unknown"

result = render(
hass,
(
"{{ states.light "
"| selectattr('state', 'in', ['unavailable','unknown','none']) "
"| sort(attribute='entity_id') | map(attribute='entity_id') | list "
"| join(', ') }}"
),
)
assert result == "light.none, light.unavailable, light.unknown"