mirror of
https://github.com/home-assistant/core.git
synced 2026-05-08 17:49:37 +01:00
Extract area template functions into an areas Jinja2 extension (#156629)
This commit is contained in:
@@ -56,8 +56,6 @@ from homeassistant.core import (
|
||||
)
|
||||
from homeassistant.exceptions import TemplateError
|
||||
from homeassistant.helpers import (
|
||||
area_registry as ar,
|
||||
device_registry as dr,
|
||||
entity_registry as er,
|
||||
issue_registry as ir,
|
||||
location as loc_helper,
|
||||
@@ -78,7 +76,7 @@ from .context import (
|
||||
template_context_manager,
|
||||
template_cv,
|
||||
)
|
||||
from .helpers import raise_no_default, resolve_area_id
|
||||
from .helpers import raise_no_default
|
||||
from .render_info import RenderInfo, render_info_cv
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -1244,103 +1242,6 @@ def issue(hass: HomeAssistant, domain: str, issue_id: str) -> dict[str, Any] | N
|
||||
return None
|
||||
|
||||
|
||||
def areas(hass: HomeAssistant) -> Iterable[str | None]:
|
||||
"""Return all areas."""
|
||||
return list(ar.async_get(hass).areas)
|
||||
|
||||
|
||||
def area_id(hass: HomeAssistant, lookup_value: str) -> str | None:
|
||||
"""Get the area ID from an area name, alias, device id, or entity id."""
|
||||
return resolve_area_id(hass, lookup_value)
|
||||
|
||||
|
||||
def _get_area_name(area_reg: ar.AreaRegistry, valid_area_id: str) -> str:
|
||||
"""Get area name from valid area ID."""
|
||||
area = area_reg.async_get_area(valid_area_id)
|
||||
assert area
|
||||
return area.name
|
||||
|
||||
|
||||
def area_name(hass: HomeAssistant, lookup_value: str) -> str | None:
|
||||
"""Get the area name from an area id, device id, or entity id."""
|
||||
area_reg = ar.async_get(hass)
|
||||
if area := area_reg.async_get_area(lookup_value):
|
||||
return area.name
|
||||
|
||||
dev_reg = dr.async_get(hass)
|
||||
ent_reg = er.async_get(hass)
|
||||
# Import here, not at top-level to avoid circular import
|
||||
from homeassistant.helpers import config_validation as cv # noqa: PLC0415
|
||||
|
||||
try:
|
||||
cv.entity_id(lookup_value)
|
||||
except vol.Invalid:
|
||||
pass
|
||||
else:
|
||||
if entity := ent_reg.async_get(lookup_value):
|
||||
# If entity has an area ID, get the area name for that
|
||||
if entity.area_id:
|
||||
return _get_area_name(area_reg, entity.area_id)
|
||||
# If entity has a device ID and the device exists with an area ID, get the
|
||||
# area name for that
|
||||
if (
|
||||
entity.device_id
|
||||
and (device := dev_reg.async_get(entity.device_id))
|
||||
and device.area_id
|
||||
):
|
||||
return _get_area_name(area_reg, device.area_id)
|
||||
|
||||
if (device := dev_reg.async_get(lookup_value)) and device.area_id:
|
||||
return _get_area_name(area_reg, device.area_id)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def area_entities(hass: HomeAssistant, area_id_or_name: str) -> Iterable[str]:
|
||||
"""Return entities for a given area ID or name."""
|
||||
_area_id: str | None
|
||||
# if area_name returns a value, we know the input was an ID, otherwise we
|
||||
# assume it's a name, and if it's neither, we return early
|
||||
if area_name(hass, area_id_or_name) is None:
|
||||
_area_id = area_id(hass, area_id_or_name)
|
||||
else:
|
||||
_area_id = area_id_or_name
|
||||
if _area_id is None:
|
||||
return []
|
||||
ent_reg = er.async_get(hass)
|
||||
entity_ids = [
|
||||
entry.entity_id for entry in er.async_entries_for_area(ent_reg, _area_id)
|
||||
]
|
||||
dev_reg = dr.async_get(hass)
|
||||
# We also need to add entities tied to a device in the area that don't themselves
|
||||
# have an area specified since they inherit the area from the device.
|
||||
entity_ids.extend(
|
||||
[
|
||||
entity.entity_id
|
||||
for device in dr.async_entries_for_area(dev_reg, _area_id)
|
||||
for entity in er.async_entries_for_device(ent_reg, device.id)
|
||||
if entity.area_id is None
|
||||
]
|
||||
)
|
||||
return entity_ids
|
||||
|
||||
|
||||
def area_devices(hass: HomeAssistant, area_id_or_name: str) -> Iterable[str]:
|
||||
"""Return device IDs for a given area ID or name."""
|
||||
_area_id: str | None
|
||||
# if area_name returns a value, we know the input was an ID, otherwise we
|
||||
# assume it's a name, and if it's neither, we return early
|
||||
if area_name(hass, area_id_or_name) is not None:
|
||||
_area_id = area_id_or_name
|
||||
else:
|
||||
_area_id = area_id(hass, area_id_or_name)
|
||||
if _area_id is None:
|
||||
return []
|
||||
dev_reg = dr.async_get(hass)
|
||||
entries = dr.async_entries_for_area(dev_reg, _area_id)
|
||||
return [entry.id for entry in entries]
|
||||
|
||||
|
||||
def closest(hass: HomeAssistant, *args: Any) -> State | None:
|
||||
"""Find closest entity.
|
||||
|
||||
@@ -2182,6 +2083,7 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment):
|
||||
] = weakref.WeakValueDictionary()
|
||||
self.add_extension("jinja2.ext.loopcontrols")
|
||||
self.add_extension("jinja2.ext.do")
|
||||
self.add_extension("homeassistant.helpers.template.extensions.AreaExtension")
|
||||
self.add_extension("homeassistant.helpers.template.extensions.Base64Extension")
|
||||
self.add_extension(
|
||||
"homeassistant.helpers.template.extensions.CollectionExtension"
|
||||
@@ -2276,22 +2178,6 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment):
|
||||
|
||||
return jinja_context(wrapper)
|
||||
|
||||
# Area extensions
|
||||
|
||||
self.globals["areas"] = hassfunction(areas)
|
||||
|
||||
self.globals["area_id"] = hassfunction(area_id)
|
||||
self.filters["area_id"] = self.globals["area_id"]
|
||||
|
||||
self.globals["area_name"] = hassfunction(area_name)
|
||||
self.filters["area_name"] = self.globals["area_name"]
|
||||
|
||||
self.globals["area_entities"] = hassfunction(area_entities)
|
||||
self.filters["area_entities"] = self.globals["area_entities"]
|
||||
|
||||
self.globals["area_devices"] = hassfunction(area_devices)
|
||||
self.filters["area_devices"] = self.globals["area_devices"]
|
||||
|
||||
# Integration extensions
|
||||
|
||||
self.globals["integration_entities"] = hassfunction(integration_entities)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""Home Assistant template extensions."""
|
||||
|
||||
from .areas import AreaExtension
|
||||
from .base64 import Base64Extension
|
||||
from .collection import CollectionExtension
|
||||
from .crypto import CryptoExtension
|
||||
@@ -11,6 +12,7 @@ from .regex import RegexExtension
|
||||
from .string import StringExtension
|
||||
|
||||
__all__ = [
|
||||
"AreaExtension",
|
||||
"Base64Extension",
|
||||
"CollectionExtension",
|
||||
"CryptoExtension",
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
"""Area functions for Home Assistant templates."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Iterable
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.helpers import (
|
||||
area_registry as ar,
|
||||
device_registry as dr,
|
||||
entity_registry as er,
|
||||
)
|
||||
from homeassistant.helpers.template.helpers import resolve_area_id
|
||||
|
||||
from .base import BaseTemplateExtension, TemplateFunction
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from homeassistant.helpers.template import TemplateEnvironment
|
||||
|
||||
|
||||
class AreaExtension(BaseTemplateExtension):
|
||||
"""Extension for area-related template functions."""
|
||||
|
||||
def __init__(self, environment: TemplateEnvironment) -> None:
|
||||
"""Initialize the area extension."""
|
||||
super().__init__(
|
||||
environment,
|
||||
functions=[
|
||||
TemplateFunction(
|
||||
"areas",
|
||||
self.areas,
|
||||
as_global=True,
|
||||
requires_hass=True,
|
||||
),
|
||||
TemplateFunction(
|
||||
"area_id",
|
||||
self.area_id,
|
||||
as_global=True,
|
||||
as_filter=True,
|
||||
requires_hass=True,
|
||||
limited_ok=False,
|
||||
),
|
||||
TemplateFunction(
|
||||
"area_name",
|
||||
self.area_name,
|
||||
as_global=True,
|
||||
as_filter=True,
|
||||
requires_hass=True,
|
||||
limited_ok=False,
|
||||
),
|
||||
TemplateFunction(
|
||||
"area_entities",
|
||||
self.area_entities,
|
||||
as_global=True,
|
||||
as_filter=True,
|
||||
requires_hass=True,
|
||||
),
|
||||
TemplateFunction(
|
||||
"area_devices",
|
||||
self.area_devices,
|
||||
as_global=True,
|
||||
as_filter=True,
|
||||
requires_hass=True,
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
def areas(self) -> Iterable[str | None]:
|
||||
"""Return all areas."""
|
||||
return list(ar.async_get(self.hass).areas)
|
||||
|
||||
def area_id(self, lookup_value: str) -> str | None:
|
||||
"""Get the area ID from an area name, alias, device id, or entity id."""
|
||||
return resolve_area_id(self.hass, lookup_value)
|
||||
|
||||
def _get_area_name(self, area_reg: ar.AreaRegistry, valid_area_id: str) -> str:
|
||||
"""Get area name from valid area ID."""
|
||||
area = area_reg.async_get_area(valid_area_id)
|
||||
assert area
|
||||
return area.name
|
||||
|
||||
def area_name(self, lookup_value: str) -> str | None:
|
||||
"""Get the area name from an area id, device id, or entity id."""
|
||||
area_reg = ar.async_get(self.hass)
|
||||
if area := area_reg.async_get_area(lookup_value):
|
||||
return area.name
|
||||
|
||||
dev_reg = dr.async_get(self.hass)
|
||||
ent_reg = er.async_get(self.hass)
|
||||
# Import here, not at top-level to avoid circular import
|
||||
from homeassistant.helpers import config_validation as cv # noqa: PLC0415
|
||||
|
||||
try:
|
||||
cv.entity_id(lookup_value)
|
||||
except vol.Invalid:
|
||||
pass
|
||||
else:
|
||||
if entity := ent_reg.async_get(lookup_value):
|
||||
# If entity has an area ID, get the area name for that
|
||||
if entity.area_id:
|
||||
return self._get_area_name(area_reg, entity.area_id)
|
||||
# If entity has a device ID and the device exists with an area ID, get the
|
||||
# area name for that
|
||||
if (
|
||||
entity.device_id
|
||||
and (device := dev_reg.async_get(entity.device_id))
|
||||
and device.area_id
|
||||
):
|
||||
return self._get_area_name(area_reg, device.area_id)
|
||||
|
||||
if (device := dev_reg.async_get(lookup_value)) and device.area_id:
|
||||
return self._get_area_name(area_reg, device.area_id)
|
||||
|
||||
return None
|
||||
|
||||
def area_entities(self, area_id_or_name: str) -> Iterable[str]:
|
||||
"""Return entities for a given area ID or name."""
|
||||
_area_id: str | None
|
||||
# if area_name returns a value, we know the input was an ID, otherwise we
|
||||
# assume it's a name, and if it's neither, we return early
|
||||
if self.area_name(area_id_or_name) is None:
|
||||
_area_id = self.area_id(area_id_or_name)
|
||||
else:
|
||||
_area_id = area_id_or_name
|
||||
if _area_id is None:
|
||||
return []
|
||||
ent_reg = er.async_get(self.hass)
|
||||
entity_ids = [
|
||||
entry.entity_id for entry in er.async_entries_for_area(ent_reg, _area_id)
|
||||
]
|
||||
dev_reg = dr.async_get(self.hass)
|
||||
# We also need to add entities tied to a device in the area that don't themselves
|
||||
# have an area specified since they inherit the area from the device.
|
||||
entity_ids.extend(
|
||||
[
|
||||
entity.entity_id
|
||||
for device in dr.async_entries_for_area(dev_reg, _area_id)
|
||||
for entity in er.async_entries_for_device(ent_reg, device.id)
|
||||
if entity.area_id is None
|
||||
]
|
||||
)
|
||||
return entity_ids
|
||||
|
||||
def area_devices(self, area_id_or_name: str) -> Iterable[str]:
|
||||
"""Return device IDs for a given area ID or name."""
|
||||
_area_id: str | None
|
||||
# if area_name returns a value, we know the input was an ID, otherwise we
|
||||
# assume it's a name, and if it's neither, we return early
|
||||
if self.area_name(area_id_or_name) is not None:
|
||||
_area_id = area_id_or_name
|
||||
else:
|
||||
_area_id = self.area_id(area_id_or_name)
|
||||
if _area_id is None:
|
||||
return []
|
||||
dev_reg = dr.async_get(self.hass)
|
||||
entries = dr.async_entries_for_area(dev_reg, _area_id)
|
||||
return [entry.id for entry in entries]
|
||||
@@ -0,0 +1,313 @@
|
||||
"""Test area template functions."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import (
|
||||
area_registry as ar,
|
||||
device_registry as dr,
|
||||
entity_registry as er,
|
||||
)
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.helpers.template.helpers import assert_result_info, render_to_info
|
||||
|
||||
|
||||
async def test_areas(hass: HomeAssistant, area_registry: ar.AreaRegistry) -> None:
|
||||
"""Test areas function."""
|
||||
# Test no areas
|
||||
info = render_to_info(hass, "{{ areas() }}")
|
||||
assert_result_info(info, [])
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test one area
|
||||
area1 = area_registry.async_get_or_create("area1")
|
||||
info = render_to_info(hass, "{{ areas() }}")
|
||||
assert_result_info(info, [area1.id])
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test multiple areas
|
||||
area2 = area_registry.async_get_or_create("area2")
|
||||
info = render_to_info(hass, "{{ areas() }}")
|
||||
assert_result_info(info, [area1.id, area2.id])
|
||||
assert info.rate_limit is None
|
||||
|
||||
|
||||
async def test_area_id(
|
||||
hass: HomeAssistant,
|
||||
area_registry: ar.AreaRegistry,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test area_id function."""
|
||||
config_entry = MockConfigEntry(domain="light")
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
# Test non existing entity id
|
||||
info = render_to_info(hass, "{{ area_id('sensor.fake') }}")
|
||||
assert_result_info(info, None)
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test non existing device id (hex value)
|
||||
info = render_to_info(hass, "{{ area_id('123abc') }}")
|
||||
assert_result_info(info, None)
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test non existing area name
|
||||
info = render_to_info(hass, "{{ area_id('fake area name') }}")
|
||||
assert_result_info(info, None)
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test wrong value type
|
||||
info = render_to_info(hass, "{{ area_id(56) }}")
|
||||
assert_result_info(info, None)
|
||||
assert info.rate_limit is None
|
||||
|
||||
area_registry.async_get_or_create("sensor.fake")
|
||||
|
||||
# Test device with single entity, which has no area
|
||||
device_entry = device_registry.async_get_or_create(
|
||||
config_entry_id=config_entry.entry_id,
|
||||
connections={(dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
||||
)
|
||||
entity_entry = entity_registry.async_get_or_create(
|
||||
"light",
|
||||
"hue",
|
||||
"5678",
|
||||
config_entry=config_entry,
|
||||
device_id=device_entry.id,
|
||||
)
|
||||
info = render_to_info(hass, f"{{{{ area_id('{device_entry.id}') }}}}")
|
||||
assert_result_info(info, None)
|
||||
assert info.rate_limit is None
|
||||
|
||||
info = render_to_info(hass, f"{{{{ area_id('{entity_entry.entity_id}') }}}}")
|
||||
assert_result_info(info, None)
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test device ID, entity ID and area name as input with area name that looks like
|
||||
# a device ID. Try a filter too
|
||||
area_entry_hex = area_registry.async_get_or_create("123abc")
|
||||
device_entry = device_registry.async_update_device(
|
||||
device_entry.id, area_id=area_entry_hex.id
|
||||
)
|
||||
entity_entry = entity_registry.async_update_entity(
|
||||
entity_entry.entity_id, area_id=area_entry_hex.id
|
||||
)
|
||||
|
||||
info = render_to_info(hass, f"{{{{ '{device_entry.id}' | area_id }}}}")
|
||||
assert_result_info(info, area_entry_hex.id)
|
||||
assert info.rate_limit is None
|
||||
|
||||
info = render_to_info(hass, f"{{{{ area_id('{entity_entry.entity_id}') }}}}")
|
||||
assert_result_info(info, area_entry_hex.id)
|
||||
assert info.rate_limit is None
|
||||
|
||||
info = render_to_info(hass, f"{{{{ area_id('{area_entry_hex.name}') }}}}")
|
||||
assert_result_info(info, area_entry_hex.id)
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test device ID, entity ID and area name as input with area name that looks like an
|
||||
# entity ID
|
||||
area_entry_entity_id = area_registry.async_get_or_create("sensor.fake")
|
||||
device_entry = device_registry.async_update_device(
|
||||
device_entry.id, area_id=area_entry_entity_id.id
|
||||
)
|
||||
entity_entry = entity_registry.async_update_entity(
|
||||
entity_entry.entity_id, area_id=area_entry_entity_id.id
|
||||
)
|
||||
|
||||
info = render_to_info(hass, f"{{{{ area_id('{device_entry.id}') }}}}")
|
||||
assert_result_info(info, area_entry_entity_id.id)
|
||||
assert info.rate_limit is None
|
||||
|
||||
info = render_to_info(hass, f"{{{{ area_id('{entity_entry.entity_id}') }}}}")
|
||||
assert_result_info(info, area_entry_entity_id.id)
|
||||
assert info.rate_limit is None
|
||||
|
||||
info = render_to_info(hass, f"{{{{ area_id('{area_entry_entity_id.name}') }}}}")
|
||||
assert_result_info(info, area_entry_entity_id.id)
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Make sure that when entity doesn't have an area but its device does, that's what
|
||||
# gets returned
|
||||
entity_entry = entity_registry.async_update_entity(
|
||||
entity_entry.entity_id, area_id=area_entry_entity_id.id
|
||||
)
|
||||
|
||||
info = render_to_info(hass, f"{{{{ area_id('{entity_entry.entity_id}') }}}}")
|
||||
assert_result_info(info, area_entry_entity_id.id)
|
||||
assert info.rate_limit is None
|
||||
|
||||
|
||||
async def test_area_name(
|
||||
hass: HomeAssistant,
|
||||
area_registry: ar.AreaRegistry,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test area_name function."""
|
||||
config_entry = MockConfigEntry(domain="light")
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
# Test non existing entity id
|
||||
info = render_to_info(hass, "{{ area_name('sensor.fake') }}")
|
||||
assert_result_info(info, None)
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test non existing device id (hex value)
|
||||
info = render_to_info(hass, "{{ area_name('123abc') }}")
|
||||
assert_result_info(info, None)
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test non existing area id
|
||||
info = render_to_info(hass, "{{ area_name('1234567890') }}")
|
||||
assert_result_info(info, None)
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test wrong value type
|
||||
info = render_to_info(hass, "{{ area_name(56) }}")
|
||||
assert_result_info(info, None)
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test device with single entity, which has no area
|
||||
device_entry = device_registry.async_get_or_create(
|
||||
config_entry_id=config_entry.entry_id,
|
||||
connections={(dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
||||
)
|
||||
entity_entry = entity_registry.async_get_or_create(
|
||||
"light",
|
||||
"hue",
|
||||
"5678",
|
||||
config_entry=config_entry,
|
||||
device_id=device_entry.id,
|
||||
)
|
||||
info = render_to_info(hass, f"{{{{ area_name('{device_entry.id}') }}}}")
|
||||
assert_result_info(info, None)
|
||||
assert info.rate_limit is None
|
||||
|
||||
info = render_to_info(hass, f"{{{{ area_name('{entity_entry.entity_id}') }}}}")
|
||||
assert_result_info(info, None)
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test device ID, entity ID and area id as input. Try a filter too
|
||||
area_entry = area_registry.async_get_or_create("123abc")
|
||||
device_entry = device_registry.async_update_device(
|
||||
device_entry.id, area_id=area_entry.id
|
||||
)
|
||||
entity_entry = entity_registry.async_update_entity(
|
||||
entity_entry.entity_id, area_id=area_entry.id
|
||||
)
|
||||
|
||||
info = render_to_info(hass, f"{{{{ '{device_entry.id}' | area_name }}}}")
|
||||
assert_result_info(info, area_entry.name)
|
||||
assert info.rate_limit is None
|
||||
|
||||
info = render_to_info(hass, f"{{{{ area_name('{entity_entry.entity_id}') }}}}")
|
||||
assert_result_info(info, area_entry.name)
|
||||
assert info.rate_limit is None
|
||||
|
||||
info = render_to_info(hass, f"{{{{ area_name('{area_entry.id}') }}}}")
|
||||
assert_result_info(info, area_entry.name)
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Make sure that when entity doesn't have an area but its device does, that's what
|
||||
# gets returned
|
||||
entity_entry = entity_registry.async_update_entity(
|
||||
entity_entry.entity_id, area_id=None
|
||||
)
|
||||
|
||||
info = render_to_info(hass, f"{{{{ area_name('{entity_entry.entity_id}') }}}}")
|
||||
assert_result_info(info, area_entry.name)
|
||||
assert info.rate_limit is None
|
||||
|
||||
|
||||
async def test_area_entities(
|
||||
hass: HomeAssistant,
|
||||
area_registry: ar.AreaRegistry,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test area_entities function."""
|
||||
config_entry = MockConfigEntry(domain="light")
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
# Test non existing device id
|
||||
info = render_to_info(hass, "{{ area_entities('deadbeef') }}")
|
||||
assert_result_info(info, [])
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test wrong value type
|
||||
info = render_to_info(hass, "{{ area_entities(56) }}")
|
||||
assert_result_info(info, [])
|
||||
assert info.rate_limit is None
|
||||
|
||||
area_entry = area_registry.async_get_or_create("sensor.fake")
|
||||
entity_entry = entity_registry.async_get_or_create(
|
||||
"light",
|
||||
"hue",
|
||||
"5678",
|
||||
config_entry=config_entry,
|
||||
)
|
||||
entity_registry.async_update_entity(entity_entry.entity_id, area_id=area_entry.id)
|
||||
|
||||
info = render_to_info(hass, f"{{{{ area_entities('{area_entry.id}') }}}}")
|
||||
assert_result_info(info, ["light.hue_5678"])
|
||||
assert info.rate_limit is None
|
||||
|
||||
info = render_to_info(hass, f"{{{{ '{area_entry.name}' | area_entities }}}}")
|
||||
assert_result_info(info, ["light.hue_5678"])
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test for entities that inherit area from device
|
||||
device_entry = device_registry.async_get_or_create(
|
||||
connections={(dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
||||
config_entry_id=config_entry.entry_id,
|
||||
suggested_area="sensor.fake",
|
||||
)
|
||||
entity_registry.async_get_or_create(
|
||||
"light",
|
||||
"hue_light",
|
||||
"5678",
|
||||
config_entry=config_entry,
|
||||
device_id=device_entry.id,
|
||||
)
|
||||
|
||||
info = render_to_info(hass, f"{{{{ '{area_entry.name}' | area_entities }}}}")
|
||||
assert_result_info(info, ["light.hue_5678", "light.hue_light_5678"])
|
||||
assert info.rate_limit is None
|
||||
|
||||
|
||||
async def test_area_devices(
|
||||
hass: HomeAssistant,
|
||||
area_registry: ar.AreaRegistry,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
) -> None:
|
||||
"""Test area_devices function."""
|
||||
config_entry = MockConfigEntry(domain="light")
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
# Test non existing device id
|
||||
info = render_to_info(hass, "{{ area_devices('deadbeef') }}")
|
||||
assert_result_info(info, [])
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test wrong value type
|
||||
info = render_to_info(hass, "{{ area_devices(56) }}")
|
||||
assert_result_info(info, [])
|
||||
assert info.rate_limit is None
|
||||
|
||||
area_entry = area_registry.async_get_or_create("sensor.fake")
|
||||
device_entry = device_registry.async_get_or_create(
|
||||
config_entry_id=config_entry.entry_id,
|
||||
connections={(dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
||||
suggested_area=area_entry.name,
|
||||
)
|
||||
|
||||
info = render_to_info(hass, f"{{{{ area_devices('{area_entry.id}') }}}}")
|
||||
assert_result_info(info, [device_entry.id])
|
||||
assert info.rate_limit is None
|
||||
|
||||
info = render_to_info(hass, f"{{{{ '{area_entry.name}' | area_devices }}}}")
|
||||
assert_result_info(info, [device_entry.id])
|
||||
assert info.rate_limit is None
|
||||
@@ -36,8 +36,6 @@ from homeassistant.const import (
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import TemplateError
|
||||
from homeassistant.helpers import (
|
||||
area_registry as ar,
|
||||
device_registry as dr,
|
||||
entity,
|
||||
entity_registry as er,
|
||||
issue_registry as ir,
|
||||
@@ -2636,306 +2634,6 @@ async def test_issue(hass: HomeAssistant, issue_registry: ir.IssueRegistry) -> N
|
||||
assert info.rate_limit is None
|
||||
|
||||
|
||||
async def test_areas(hass: HomeAssistant, area_registry: ar.AreaRegistry) -> None:
|
||||
"""Test areas function."""
|
||||
# Test no areas
|
||||
info = render_to_info(hass, "{{ areas() }}")
|
||||
assert_result_info(info, [])
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test one area
|
||||
area1 = area_registry.async_get_or_create("area1")
|
||||
info = render_to_info(hass, "{{ areas() }}")
|
||||
assert_result_info(info, [area1.id])
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test multiple areas
|
||||
area2 = area_registry.async_get_or_create("area2")
|
||||
info = render_to_info(hass, "{{ areas() }}")
|
||||
assert_result_info(info, [area1.id, area2.id])
|
||||
assert info.rate_limit is None
|
||||
|
||||
|
||||
async def test_area_id(
|
||||
hass: HomeAssistant,
|
||||
area_registry: ar.AreaRegistry,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test area_id function."""
|
||||
config_entry = MockConfigEntry(domain="light")
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
# Test non existing entity id
|
||||
info = render_to_info(hass, "{{ area_id('sensor.fake') }}")
|
||||
assert_result_info(info, None)
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test non existing device id (hex value)
|
||||
info = render_to_info(hass, "{{ area_id('123abc') }}")
|
||||
assert_result_info(info, None)
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test non existing area name
|
||||
info = render_to_info(hass, "{{ area_id('fake area name') }}")
|
||||
assert_result_info(info, None)
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test wrong value type
|
||||
info = render_to_info(hass, "{{ area_id(56) }}")
|
||||
assert_result_info(info, None)
|
||||
assert info.rate_limit is None
|
||||
|
||||
area_entry_entity_id = area_registry.async_get_or_create("sensor.fake")
|
||||
|
||||
# Test device with single entity, which has no area
|
||||
device_entry = device_registry.async_get_or_create(
|
||||
config_entry_id=config_entry.entry_id,
|
||||
connections={(dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
||||
)
|
||||
entity_entry = entity_registry.async_get_or_create(
|
||||
"light",
|
||||
"hue",
|
||||
"5678",
|
||||
config_entry=config_entry,
|
||||
device_id=device_entry.id,
|
||||
)
|
||||
info = render_to_info(hass, f"{{{{ area_id('{device_entry.id}') }}}}")
|
||||
assert_result_info(info, None)
|
||||
assert info.rate_limit is None
|
||||
|
||||
info = render_to_info(hass, f"{{{{ area_id('{entity_entry.entity_id}') }}}}")
|
||||
assert_result_info(info, None)
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test device ID, entity ID and area name as input with area name that looks like
|
||||
# a device ID. Try a filter too
|
||||
area_entry_hex = area_registry.async_get_or_create("123abc")
|
||||
device_entry = device_registry.async_update_device(
|
||||
device_entry.id, area_id=area_entry_hex.id
|
||||
)
|
||||
entity_entry = entity_registry.async_update_entity(
|
||||
entity_entry.entity_id, area_id=area_entry_hex.id
|
||||
)
|
||||
|
||||
info = render_to_info(hass, f"{{{{ '{device_entry.id}' | area_id }}}}")
|
||||
assert_result_info(info, area_entry_hex.id)
|
||||
assert info.rate_limit is None
|
||||
|
||||
info = render_to_info(hass, f"{{{{ area_id('{entity_entry.entity_id}') }}}}")
|
||||
assert_result_info(info, area_entry_hex.id)
|
||||
assert info.rate_limit is None
|
||||
|
||||
info = render_to_info(hass, f"{{{{ area_id('{area_entry_hex.name}') }}}}")
|
||||
assert_result_info(info, area_entry_hex.id)
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test device ID, entity ID and area name as input with area name that looks like an
|
||||
# entity ID
|
||||
area_entry_entity_id = area_registry.async_get_or_create("sensor.fake")
|
||||
device_entry = device_registry.async_update_device(
|
||||
device_entry.id, area_id=area_entry_entity_id.id
|
||||
)
|
||||
entity_entry = entity_registry.async_update_entity(
|
||||
entity_entry.entity_id, area_id=area_entry_entity_id.id
|
||||
)
|
||||
|
||||
info = render_to_info(hass, f"{{{{ area_id('{device_entry.id}') }}}}")
|
||||
assert_result_info(info, area_entry_entity_id.id)
|
||||
assert info.rate_limit is None
|
||||
|
||||
info = render_to_info(hass, f"{{{{ area_id('{entity_entry.entity_id}') }}}}")
|
||||
assert_result_info(info, area_entry_entity_id.id)
|
||||
assert info.rate_limit is None
|
||||
|
||||
info = render_to_info(hass, f"{{{{ area_id('{area_entry_entity_id.name}') }}}}")
|
||||
assert_result_info(info, area_entry_entity_id.id)
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Make sure that when entity doesn't have an area but its device does, that's what
|
||||
# gets returned
|
||||
entity_entry = entity_registry.async_update_entity(
|
||||
entity_entry.entity_id, area_id=area_entry_entity_id.id
|
||||
)
|
||||
|
||||
info = render_to_info(hass, f"{{{{ area_id('{entity_entry.entity_id}') }}}}")
|
||||
assert_result_info(info, area_entry_entity_id.id)
|
||||
assert info.rate_limit is None
|
||||
|
||||
|
||||
async def test_area_name(
|
||||
hass: HomeAssistant,
|
||||
area_registry: ar.AreaRegistry,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test area_name function."""
|
||||
config_entry = MockConfigEntry(domain="light")
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
# Test non existing entity id
|
||||
info = render_to_info(hass, "{{ area_name('sensor.fake') }}")
|
||||
assert_result_info(info, None)
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test non existing device id (hex value)
|
||||
info = render_to_info(hass, "{{ area_name('123abc') }}")
|
||||
assert_result_info(info, None)
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test non existing area id
|
||||
info = render_to_info(hass, "{{ area_name('1234567890') }}")
|
||||
assert_result_info(info, None)
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test wrong value type
|
||||
info = render_to_info(hass, "{{ area_name(56) }}")
|
||||
assert_result_info(info, None)
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test device with single entity, which has no area
|
||||
device_entry = device_registry.async_get_or_create(
|
||||
config_entry_id=config_entry.entry_id,
|
||||
connections={(dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
||||
)
|
||||
entity_entry = entity_registry.async_get_or_create(
|
||||
"light",
|
||||
"hue",
|
||||
"5678",
|
||||
config_entry=config_entry,
|
||||
device_id=device_entry.id,
|
||||
)
|
||||
info = render_to_info(hass, f"{{{{ area_name('{device_entry.id}') }}}}")
|
||||
assert_result_info(info, None)
|
||||
assert info.rate_limit is None
|
||||
|
||||
info = render_to_info(hass, f"{{{{ area_name('{entity_entry.entity_id}') }}}}")
|
||||
assert_result_info(info, None)
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test device ID, entity ID and area id as input. Try a filter too
|
||||
area_entry = area_registry.async_get_or_create("123abc")
|
||||
device_entry = device_registry.async_update_device(
|
||||
device_entry.id, area_id=area_entry.id
|
||||
)
|
||||
entity_entry = entity_registry.async_update_entity(
|
||||
entity_entry.entity_id, area_id=area_entry.id
|
||||
)
|
||||
|
||||
info = render_to_info(hass, f"{{{{ '{device_entry.id}' | area_name }}}}")
|
||||
assert_result_info(info, area_entry.name)
|
||||
assert info.rate_limit is None
|
||||
|
||||
info = render_to_info(hass, f"{{{{ area_name('{entity_entry.entity_id}') }}}}")
|
||||
assert_result_info(info, area_entry.name)
|
||||
assert info.rate_limit is None
|
||||
|
||||
info = render_to_info(hass, f"{{{{ area_name('{area_entry.id}') }}}}")
|
||||
assert_result_info(info, area_entry.name)
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Make sure that when entity doesn't have an area but its device does, that's what
|
||||
# gets returned
|
||||
entity_entry = entity_registry.async_update_entity(
|
||||
entity_entry.entity_id, area_id=None
|
||||
)
|
||||
|
||||
info = render_to_info(hass, f"{{{{ area_name('{entity_entry.entity_id}') }}}}")
|
||||
assert_result_info(info, area_entry.name)
|
||||
assert info.rate_limit is None
|
||||
|
||||
|
||||
async def test_area_entities(
|
||||
hass: HomeAssistant,
|
||||
area_registry: ar.AreaRegistry,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test area_entities function."""
|
||||
config_entry = MockConfigEntry(domain="light")
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
# Test non existing device id
|
||||
info = render_to_info(hass, "{{ area_entities('deadbeef') }}")
|
||||
assert_result_info(info, [])
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test wrong value type
|
||||
info = render_to_info(hass, "{{ area_entities(56) }}")
|
||||
assert_result_info(info, [])
|
||||
assert info.rate_limit is None
|
||||
|
||||
area_entry = area_registry.async_get_or_create("sensor.fake")
|
||||
entity_entry = entity_registry.async_get_or_create(
|
||||
"light",
|
||||
"hue",
|
||||
"5678",
|
||||
config_entry=config_entry,
|
||||
)
|
||||
entity_registry.async_update_entity(entity_entry.entity_id, area_id=area_entry.id)
|
||||
|
||||
info = render_to_info(hass, f"{{{{ area_entities('{area_entry.id}') }}}}")
|
||||
assert_result_info(info, ["light.hue_5678"])
|
||||
assert info.rate_limit is None
|
||||
|
||||
info = render_to_info(hass, f"{{{{ '{area_entry.name}' | area_entities }}}}")
|
||||
assert_result_info(info, ["light.hue_5678"])
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test for entities that inherit area from device
|
||||
device_entry = device_registry.async_get_or_create(
|
||||
connections={(dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
||||
config_entry_id=config_entry.entry_id,
|
||||
suggested_area="sensor.fake",
|
||||
)
|
||||
entity_registry.async_get_or_create(
|
||||
"light",
|
||||
"hue_light",
|
||||
"5678",
|
||||
config_entry=config_entry,
|
||||
device_id=device_entry.id,
|
||||
)
|
||||
|
||||
info = render_to_info(hass, f"{{{{ '{area_entry.name}' | area_entities }}}}")
|
||||
assert_result_info(info, ["light.hue_5678", "light.hue_light_5678"])
|
||||
assert info.rate_limit is None
|
||||
|
||||
|
||||
async def test_area_devices(
|
||||
hass: HomeAssistant,
|
||||
area_registry: ar.AreaRegistry,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
) -> None:
|
||||
"""Test area_devices function."""
|
||||
config_entry = MockConfigEntry(domain="light")
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
# Test non existing device id
|
||||
info = render_to_info(hass, "{{ area_devices('deadbeef') }}")
|
||||
assert_result_info(info, [])
|
||||
assert info.rate_limit is None
|
||||
|
||||
# Test wrong value type
|
||||
info = render_to_info(hass, "{{ area_devices(56) }}")
|
||||
assert_result_info(info, [])
|
||||
assert info.rate_limit is None
|
||||
|
||||
area_entry = area_registry.async_get_or_create("sensor.fake")
|
||||
device_entry = device_registry.async_get_or_create(
|
||||
config_entry_id=config_entry.entry_id,
|
||||
connections={(dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
||||
suggested_area=area_entry.name,
|
||||
)
|
||||
|
||||
info = render_to_info(hass, f"{{{{ area_devices('{area_entry.id}') }}}}")
|
||||
assert_result_info(info, [device_entry.id])
|
||||
assert info.rate_limit is None
|
||||
|
||||
info = render_to_info(hass, f"{{{{ '{area_entry.name}' | area_devices }}}}")
|
||||
assert_result_info(info, [device_entry.id])
|
||||
assert info.rate_limit is None
|
||||
|
||||
|
||||
def test_closest_function_to_coord(hass: HomeAssistant) -> None:
|
||||
"""Test closest function to coord."""
|
||||
hass.states.async_set(
|
||||
|
||||
Reference in New Issue
Block a user