Avoid sorting domain/all states in templates (#90608)

This commit is contained in:
J. Nick Koston 2023-03-31 11:27:55 -10:00 committed by Paulus Schoutsen
parent 03f085d7be
commit 6242dd2214
3 changed files with 33 additions and 24 deletions

View File

@ -13,7 +13,7 @@ from functools import cache, lru_cache, partial, wraps
import json import json
import logging import logging
import math import math
from operator import attrgetter, contains from operator import contains
import pathlib import pathlib
import random import random
import re import re
@ -983,7 +983,7 @@ def _state_generator(
hass: HomeAssistant, domain: str | None hass: HomeAssistant, domain: str | None
) -> Generator[TemplateState, None, None]: ) -> Generator[TemplateState, None, None]:
"""State generator for a domain or all states.""" """State generator for a domain or all states."""
for state in sorted(hass.states.async_all(domain), key=attrgetter("entity_id")): for state in hass.states.async_all(domain):
yield _template_state_no_collect(hass, state) yield _template_state_no_collect(hass, state)
@ -1097,7 +1097,7 @@ def expand(hass: HomeAssistant, *args: Any) -> Iterable[State]:
_collect_state(hass, entity_id) _collect_state(hass, entity_id)
found[entity_id] = entity found[entity_id] = entity
return sorted(found.values(), key=lambda a: a.entity_id) return list(found.values())
def device_entities(hass: HomeAssistant, _device_id: str) -> Iterable[str]: def device_entities(hass: HomeAssistant, _device_id: str) -> Iterable[str]:

View File

@ -3043,7 +3043,9 @@ async def test_async_track_template_result_multiple_templates_mixing_domain(
template_1 = Template("{{ states.switch.test.state == 'on' }}") template_1 = Template("{{ states.switch.test.state == 'on' }}")
template_2 = Template("{{ states.switch.test.state == 'on' }}") template_2 = Template("{{ states.switch.test.state == 'on' }}")
template_3 = Template("{{ states.switch.test.state == 'off' }}") template_3 = Template("{{ states.switch.test.state == 'off' }}")
template_4 = Template("{{ states.switch | map(attribute='entity_id') | list }}") template_4 = Template(
"{{ states.switch | sort(attribute='entity_id') | map(attribute='entity_id') | list }}"
)
refresh_runs = [] refresh_runs = []

View File

@ -185,7 +185,7 @@ def test_raise_exception_on_error(hass: HomeAssistant) -> None:
def test_iterating_all_states(hass: HomeAssistant) -> None: def test_iterating_all_states(hass: HomeAssistant) -> None:
"""Test iterating all states.""" """Test iterating all states."""
tmpl_str = "{% for state in states %}{{ state.state }}{% endfor %}" tmpl_str = "{% for state in states | sort(attribute='entity_id') %}{{ state.state }}{% endfor %}"
info = render_to_info(hass, tmpl_str) info = render_to_info(hass, tmpl_str)
assert_result_info(info, "", all_states=True) assert_result_info(info, "", all_states=True)
@ -2511,20 +2511,22 @@ async def test_expand(hass: HomeAssistant) -> None:
hass.states.async_set("test.object", "happy") hass.states.async_set("test.object", "happy")
info = render_to_info( info = render_to_info(
hass, "{{ expand('test.object') | map(attribute='entity_id') | join(', ') }}" hass,
"{{ expand('test.object') | sort(attribute='entity_id') | map(attribute='entity_id') | join(', ') }}",
) )
assert_result_info(info, "test.object", ["test.object"]) assert_result_info(info, "test.object", ["test.object"])
assert info.rate_limit is None assert info.rate_limit is None
info = render_to_info( info = render_to_info(
hass, hass,
"{{ expand('group.new_group') | map(attribute='entity_id') | join(', ') }}", "{{ expand('group.new_group') | sort(attribute='entity_id') | map(attribute='entity_id') | join(', ') }}",
) )
assert_result_info(info, "", ["group.new_group"]) assert_result_info(info, "", ["group.new_group"])
assert info.rate_limit is None assert info.rate_limit is None
info = render_to_info( info = render_to_info(
hass, "{{ expand(states.group) | map(attribute='entity_id') | join(', ') }}" hass,
"{{ expand(states.group) | sort(attribute='entity_id') | map(attribute='entity_id') | join(', ') }}",
) )
assert_result_info(info, "", [], ["group"]) assert_result_info(info, "", [], ["group"])
assert info.rate_limit == template.DOMAIN_STATES_RATE_LIMIT assert info.rate_limit == template.DOMAIN_STATES_RATE_LIMIT
@ -2535,13 +2537,14 @@ async def test_expand(hass: HomeAssistant) -> None:
info = render_to_info( info = render_to_info(
hass, hass,
"{{ expand('group.new_group') | map(attribute='entity_id') | join(', ') }}", "{{ expand('group.new_group') | sort(attribute='entity_id') | map(attribute='entity_id') | join(', ') }}",
) )
assert_result_info(info, "test.object", {"group.new_group", "test.object"}) assert_result_info(info, "test.object", {"group.new_group", "test.object"})
assert info.rate_limit is None assert info.rate_limit is None
info = render_to_info( info = render_to_info(
hass, "{{ expand(states.group) | map(attribute='entity_id') | join(', ') }}" hass,
"{{ expand(states.group) | sort(attribute='entity_id') | map(attribute='entity_id') | join(', ') }}",
) )
assert_result_info(info, "test.object", {"test.object"}, ["group"]) assert_result_info(info, "test.object", {"test.object"}, ["group"])
assert info.rate_limit == template.DOMAIN_STATES_RATE_LIMIT assert info.rate_limit == template.DOMAIN_STATES_RATE_LIMIT
@ -2550,7 +2553,7 @@ async def test_expand(hass: HomeAssistant) -> None:
hass, hass,
( (
"{{ expand('group.new_group', 'test.object')" "{{ expand('group.new_group', 'test.object')"
" | map(attribute='entity_id') | join(', ') }}" " | sort(attribute='entity_id') | map(attribute='entity_id') | join(', ') }}"
), ),
) )
assert_result_info(info, "test.object", {"test.object", "group.new_group"}) assert_result_info(info, "test.object", {"test.object", "group.new_group"})
@ -2559,7 +2562,7 @@ async def test_expand(hass: HomeAssistant) -> None:
hass, hass,
( (
"{{ ['group.new_group', 'test.object'] | expand" "{{ ['group.new_group', 'test.object'] | expand"
" | map(attribute='entity_id') | join(', ') }}" " | sort(attribute='entity_id') | map(attribute='entity_id') | join(', ') }}"
), ),
) )
assert_result_info(info, "test.object", {"test.object", "group.new_group"}) assert_result_info(info, "test.object", {"test.object", "group.new_group"})
@ -2579,7 +2582,7 @@ async def test_expand(hass: HomeAssistant) -> None:
hass, hass,
( (
"{{ states.group.power_sensors.attributes.entity_id | expand " "{{ states.group.power_sensors.attributes.entity_id | expand "
"| map(attribute='state')|map('float')|sum }}" "| sort(attribute='entity_id') | map(attribute='state')|map('float')|sum }}"
), ),
) )
assert_result_info( assert_result_info(
@ -2607,7 +2610,8 @@ async def test_expand(hass: HomeAssistant) -> None:
await hass.async_block_till_done() await hass.async_block_till_done()
info = render_to_info( info = render_to_info(
hass, "{{ expand('light.grouped') | map(attribute='entity_id') | join(', ') }}" hass,
"{{ expand('light.grouped') | sort(attribute='entity_id') | map(attribute='entity_id') | join(', ') }}",
) )
assert_result_info( assert_result_info(
info, info,
@ -2629,7 +2633,8 @@ async def test_expand(hass: HomeAssistant) -> None:
}, },
) )
info = render_to_info( info = render_to_info(
hass, "{{ expand('zone.test') | map(attribute='entity_id') | join(', ') }}" hass,
"{{ expand('zone.test') | sort(attribute='entity_id') | map(attribute='entity_id') | join(', ') }}",
) )
assert_result_info( assert_result_info(
info, info,
@ -2644,7 +2649,8 @@ async def test_expand(hass: HomeAssistant) -> None:
await hass.async_block_till_done() await hass.async_block_till_done()
info = render_to_info( info = render_to_info(
hass, "{{ expand('zone.test') | map(attribute='entity_id') | join(', ') }}" hass,
"{{ expand('zone.test') | sort(attribute='entity_id') | map(attribute='entity_id') | join(', ') }}",
) )
assert_result_info( assert_result_info(
info, info,
@ -2659,7 +2665,8 @@ async def test_expand(hass: HomeAssistant) -> None:
await hass.async_block_till_done() await hass.async_block_till_done()
info = render_to_info( info = render_to_info(
hass, "{{ expand('zone.test') | map(attribute='entity_id') | join(', ') }}" hass,
"{{ expand('zone.test') | sort(attribute='entity_id') | map(attribute='entity_id') | join(', ') }}",
) )
assert_result_info( assert_result_info(
info, info,
@ -2709,7 +2716,7 @@ async def test_device_entities(
hass, hass,
( (
f"{{{{ device_entities('{device_entry.id}') | expand " f"{{{{ device_entities('{device_entry.id}') | expand "
"| map(attribute='entity_id') | join(', ') }}" "| sort(attribute='entity_id') | map(attribute='entity_id') | join(', ') }}"
), ),
) )
assert_result_info(info, "", ["light.hue_5678"]) assert_result_info(info, "", ["light.hue_5678"])
@ -2721,7 +2728,7 @@ async def test_device_entities(
hass, hass,
( (
f"{{{{ device_entities('{device_entry.id}') | expand " f"{{{{ device_entities('{device_entry.id}') | expand "
"| map(attribute='entity_id') | join(', ') }}" "| sort(attribute='entity_id') | map(attribute='entity_id') | join(', ') }}"
), ),
) )
assert_result_info(info, "light.hue_5678", ["light.hue_5678"]) assert_result_info(info, "light.hue_5678", ["light.hue_5678"])
@ -2743,7 +2750,7 @@ async def test_device_entities(
hass, hass,
( (
f"{{{{ device_entities('{device_entry.id}') | expand " f"{{{{ device_entities('{device_entry.id}') | expand "
"| map(attribute='entity_id') | join(', ') }}" "| sort(attribute='entity_id') | map(attribute='entity_id') | join(', ') }}"
), ),
) )
assert_result_info( assert_result_info(
@ -3384,7 +3391,7 @@ def test_async_render_to_info_with_complex_branching(hass: HomeAssistant) -> Non
{% elif states.light.a == "on" %} {% elif states.light.a == "on" %}
{{ states[domain] | list }} {{ states[domain] | list }}
{% elif states('light.b') == "on" %} {% elif states('light.b') == "on" %}
{{ states[otherdomain] | map(attribute='entity_id') | list }} {{ states[otherdomain] | sort(attribute='entity_id') | map(attribute='entity_id') | list }}
{% elif states.light.a == "on" %} {% elif states.light.a == "on" %}
{{ states["nonexist"] | list }} {{ states["nonexist"] | list }}
{% else %} {% else %}
@ -4205,7 +4212,7 @@ async def test_lights(hass: HomeAssistant) -> None:
"""Test we can sort lights.""" """Test we can sort lights."""
tmpl = """ tmpl = """
{% set lights_on = states.light|selectattr('state','eq','on')|map(attribute='name')|list %} {% set lights_on = states.light|selectattr('state','eq','on')|sort(attribute='entity_id')|map(attribute='name')|list %}
{% if lights_on|length == 0 %} {% if lights_on|length == 0 %}
No lights on. Sleep well.. No lights on. Sleep well..
{% elif lights_on|length == 1 %} {% elif lights_on|length == 1 %}
@ -4308,7 +4315,7 @@ async def test_unavailable_states(hass: HomeAssistant) -> None:
tpl = template.Template( tpl = template.Template(
( (
"{{ states | selectattr('state', 'in', ['unavailable','unknown','none']) " "{{ states | selectattr('state', 'in', ['unavailable','unknown','none']) "
"| map(attribute='entity_id') | list | join(', ') }}" "| sort(attribute='entity_id') | map(attribute='entity_id') | list | join(', ') }}"
), ),
hass, hass,
) )
@ -4318,7 +4325,7 @@ async def test_unavailable_states(hass: HomeAssistant) -> None:
( (
"{{ states.light " "{{ states.light "
"| selectattr('state', 'in', ['unavailable','unknown','none']) " "| selectattr('state', 'in', ['unavailable','unknown','none']) "
"| map(attribute='entity_id') | list " "| sort(attribute='entity_id') | map(attribute='entity_id') | list "
"| join(', ') }}" "| join(', ') }}"
), ),
hass, hass,