mirror of
https://github.com/home-assistant/core.git
synced 2025-12-01 21:48:03 +00:00
Extract floor template functions into a floors Jinja2 extension (#156589)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -59,7 +59,6 @@ from homeassistant.helpers import (
|
|||||||
area_registry as ar,
|
area_registry as ar,
|
||||||
device_registry as dr,
|
device_registry as dr,
|
||||||
entity_registry as er,
|
entity_registry as er,
|
||||||
floor_registry as fr,
|
|
||||||
issue_registry as ir,
|
issue_registry as ir,
|
||||||
location as loc_helper,
|
location as loc_helper,
|
||||||
)
|
)
|
||||||
@@ -79,7 +78,7 @@ from .context import (
|
|||||||
template_context_manager,
|
template_context_manager,
|
||||||
template_cv,
|
template_cv,
|
||||||
)
|
)
|
||||||
from .helpers import raise_no_default
|
from .helpers import raise_no_default, resolve_area_id
|
||||||
from .render_info import RenderInfo, render_info_cv
|
from .render_info import RenderInfo, render_info_cv
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -1318,74 +1317,6 @@ def issue(hass: HomeAssistant, domain: str, issue_id: str) -> dict[str, Any] | N
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def floors(hass: HomeAssistant) -> Iterable[str | None]:
|
|
||||||
"""Return all floors."""
|
|
||||||
floor_registry = fr.async_get(hass)
|
|
||||||
return [floor.floor_id for floor in floor_registry.async_list_floors()]
|
|
||||||
|
|
||||||
|
|
||||||
def floor_id(hass: HomeAssistant, lookup_value: Any) -> str | None:
|
|
||||||
"""Get the floor ID from a floor or area name, alias, device id, or entity id."""
|
|
||||||
floor_registry = fr.async_get(hass)
|
|
||||||
lookup_str = str(lookup_value)
|
|
||||||
if floor := floor_registry.async_get_floor_by_name(lookup_str):
|
|
||||||
return floor.floor_id
|
|
||||||
floors_list = floor_registry.async_get_floors_by_alias(lookup_str)
|
|
||||||
if floors_list:
|
|
||||||
return floors_list[0].floor_id
|
|
||||||
|
|
||||||
if aid := area_id(hass, lookup_value):
|
|
||||||
area_reg = ar.async_get(hass)
|
|
||||||
if area := area_reg.async_get_area(aid):
|
|
||||||
return area.floor_id
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def floor_name(hass: HomeAssistant, lookup_value: str) -> str | None:
|
|
||||||
"""Get the floor name from a floor id."""
|
|
||||||
floor_registry = fr.async_get(hass)
|
|
||||||
if floor := floor_registry.async_get_floor(lookup_value):
|
|
||||||
return floor.name
|
|
||||||
|
|
||||||
if aid := area_id(hass, lookup_value):
|
|
||||||
area_reg = ar.async_get(hass)
|
|
||||||
if (
|
|
||||||
(area := area_reg.async_get_area(aid))
|
|
||||||
and area.floor_id
|
|
||||||
and (floor := floor_registry.async_get_floor(area.floor_id))
|
|
||||||
):
|
|
||||||
return floor.name
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def floor_areas(hass: HomeAssistant, floor_id_or_name: str) -> Iterable[str]:
|
|
||||||
"""Return area IDs for a given floor ID or name."""
|
|
||||||
_floor_id: str | None
|
|
||||||
# If floor_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 floor_name(hass, floor_id_or_name) is not None:
|
|
||||||
_floor_id = floor_id_or_name
|
|
||||||
else:
|
|
||||||
_floor_id = floor_id(hass, floor_id_or_name)
|
|
||||||
if _floor_id is None:
|
|
||||||
return []
|
|
||||||
|
|
||||||
area_reg = ar.async_get(hass)
|
|
||||||
entries = ar.async_entries_for_floor(area_reg, _floor_id)
|
|
||||||
return [entry.id for entry in entries if entry.id]
|
|
||||||
|
|
||||||
|
|
||||||
def floor_entities(hass: HomeAssistant, floor_id_or_name: str) -> Iterable[str]:
|
|
||||||
"""Return entity_ids for a given floor ID or name."""
|
|
||||||
return [
|
|
||||||
entity_id
|
|
||||||
for area_id in floor_areas(hass, floor_id_or_name)
|
|
||||||
for entity_id in area_entities(hass, area_id)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def areas(hass: HomeAssistant) -> Iterable[str | None]:
|
def areas(hass: HomeAssistant) -> Iterable[str | None]:
|
||||||
"""Return all areas."""
|
"""Return all areas."""
|
||||||
return list(ar.async_get(hass).areas)
|
return list(ar.async_get(hass).areas)
|
||||||
@@ -1393,37 +1324,7 @@ def areas(hass: HomeAssistant) -> Iterable[str | None]:
|
|||||||
|
|
||||||
def area_id(hass: HomeAssistant, lookup_value: str) -> str | None:
|
def area_id(hass: HomeAssistant, lookup_value: str) -> str | None:
|
||||||
"""Get the area ID from an area name, alias, device id, or entity id."""
|
"""Get the area ID from an area name, alias, device id, or entity id."""
|
||||||
area_reg = ar.async_get(hass)
|
return resolve_area_id(hass, lookup_value)
|
||||||
lookup_str = str(lookup_value)
|
|
||||||
if area := area_reg.async_get_area_by_name(lookup_str):
|
|
||||||
return area.id
|
|
||||||
areas_list = area_reg.async_get_areas_by_alias(lookup_str)
|
|
||||||
if areas_list:
|
|
||||||
return areas_list[0].id
|
|
||||||
|
|
||||||
ent_reg = er.async_get(hass)
|
|
||||||
dev_reg = dr.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, return that
|
|
||||||
if entity.area_id:
|
|
||||||
return entity.area_id
|
|
||||||
# If entity has a device ID, return the area ID for the device
|
|
||||||
if entity.device_id and (device := dev_reg.async_get(entity.device_id)):
|
|
||||||
return device.area_id
|
|
||||||
|
|
||||||
# Check if this could be a device ID
|
|
||||||
if device := dev_reg.async_get(lookup_value):
|
|
||||||
return device.area_id
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def _get_area_name(area_reg: ar.AreaRegistry, valid_area_id: str) -> str:
|
def _get_area_name(area_reg: ar.AreaRegistry, valid_area_id: str) -> str:
|
||||||
@@ -2359,6 +2260,7 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment):
|
|||||||
"homeassistant.helpers.template.extensions.CollectionExtension"
|
"homeassistant.helpers.template.extensions.CollectionExtension"
|
||||||
)
|
)
|
||||||
self.add_extension("homeassistant.helpers.template.extensions.CryptoExtension")
|
self.add_extension("homeassistant.helpers.template.extensions.CryptoExtension")
|
||||||
|
self.add_extension("homeassistant.helpers.template.extensions.FloorExtension")
|
||||||
self.add_extension("homeassistant.helpers.template.extensions.LabelExtension")
|
self.add_extension("homeassistant.helpers.template.extensions.LabelExtension")
|
||||||
self.add_extension("homeassistant.helpers.template.extensions.MathExtension")
|
self.add_extension("homeassistant.helpers.template.extensions.MathExtension")
|
||||||
self.add_extension("homeassistant.helpers.template.extensions.RegexExtension")
|
self.add_extension("homeassistant.helpers.template.extensions.RegexExtension")
|
||||||
@@ -2462,23 +2364,6 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment):
|
|||||||
self.globals["area_devices"] = hassfunction(area_devices)
|
self.globals["area_devices"] = hassfunction(area_devices)
|
||||||
self.filters["area_devices"] = self.globals["area_devices"]
|
self.filters["area_devices"] = self.globals["area_devices"]
|
||||||
|
|
||||||
# Floor extensions
|
|
||||||
|
|
||||||
self.globals["floors"] = hassfunction(floors)
|
|
||||||
self.filters["floors"] = self.globals["floors"]
|
|
||||||
|
|
||||||
self.globals["floor_id"] = hassfunction(floor_id)
|
|
||||||
self.filters["floor_id"] = self.globals["floor_id"]
|
|
||||||
|
|
||||||
self.globals["floor_name"] = hassfunction(floor_name)
|
|
||||||
self.filters["floor_name"] = self.globals["floor_name"]
|
|
||||||
|
|
||||||
self.globals["floor_areas"] = hassfunction(floor_areas)
|
|
||||||
self.filters["floor_areas"] = self.globals["floor_areas"]
|
|
||||||
|
|
||||||
self.globals["floor_entities"] = hassfunction(floor_entities)
|
|
||||||
self.filters["floor_entities"] = self.globals["floor_entities"]
|
|
||||||
|
|
||||||
# Integration extensions
|
# Integration extensions
|
||||||
|
|
||||||
self.globals["integration_entities"] = hassfunction(integration_entities)
|
self.globals["integration_entities"] = hassfunction(integration_entities)
|
||||||
@@ -2534,8 +2419,6 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment):
|
|||||||
"device_id",
|
"device_id",
|
||||||
"distance",
|
"distance",
|
||||||
"expand",
|
"expand",
|
||||||
"floor_id",
|
|
||||||
"floor_name",
|
|
||||||
"has_value",
|
"has_value",
|
||||||
"is_device_attr",
|
"is_device_attr",
|
||||||
"is_hidden_entity",
|
"is_hidden_entity",
|
||||||
@@ -2557,8 +2440,6 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment):
|
|||||||
"closest",
|
"closest",
|
||||||
"device_id",
|
"device_id",
|
||||||
"expand",
|
"expand",
|
||||||
"floor_id",
|
|
||||||
"floor_name",
|
|
||||||
"has_value",
|
"has_value",
|
||||||
]
|
]
|
||||||
hass_tests = [
|
hass_tests = [
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
from .base64 import Base64Extension
|
from .base64 import Base64Extension
|
||||||
from .collection import CollectionExtension
|
from .collection import CollectionExtension
|
||||||
from .crypto import CryptoExtension
|
from .crypto import CryptoExtension
|
||||||
|
from .floors import FloorExtension
|
||||||
from .labels import LabelExtension
|
from .labels import LabelExtension
|
||||||
from .math import MathExtension
|
from .math import MathExtension
|
||||||
from .regex import RegexExtension
|
from .regex import RegexExtension
|
||||||
@@ -12,6 +13,7 @@ __all__ = [
|
|||||||
"Base64Extension",
|
"Base64Extension",
|
||||||
"CollectionExtension",
|
"CollectionExtension",
|
||||||
"CryptoExtension",
|
"CryptoExtension",
|
||||||
|
"FloorExtension",
|
||||||
"LabelExtension",
|
"LabelExtension",
|
||||||
"MathExtension",
|
"MathExtension",
|
||||||
"RegexExtension",
|
"RegexExtension",
|
||||||
|
|||||||
157
homeassistant/helpers/template/extensions/floors.py
Normal file
157
homeassistant/helpers/template/extensions/floors.py
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
"""Floor functions for Home Assistant templates."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Iterable
|
||||||
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
|
from homeassistant.helpers import (
|
||||||
|
area_registry as ar,
|
||||||
|
device_registry as dr,
|
||||||
|
entity_registry as er,
|
||||||
|
floor_registry as fr,
|
||||||
|
)
|
||||||
|
from homeassistant.helpers.template.helpers import resolve_area_id
|
||||||
|
|
||||||
|
from .base import BaseTemplateExtension, TemplateFunction
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from homeassistant.helpers.template import TemplateEnvironment
|
||||||
|
|
||||||
|
|
||||||
|
class FloorExtension(BaseTemplateExtension):
|
||||||
|
"""Extension for floor-related template functions."""
|
||||||
|
|
||||||
|
def __init__(self, environment: TemplateEnvironment) -> None:
|
||||||
|
"""Initialize the floor extension."""
|
||||||
|
super().__init__(
|
||||||
|
environment,
|
||||||
|
functions=[
|
||||||
|
TemplateFunction(
|
||||||
|
"floors",
|
||||||
|
self.floors,
|
||||||
|
as_global=True,
|
||||||
|
requires_hass=True,
|
||||||
|
),
|
||||||
|
TemplateFunction(
|
||||||
|
"floor_id",
|
||||||
|
self.floor_id,
|
||||||
|
as_global=True,
|
||||||
|
as_filter=True,
|
||||||
|
requires_hass=True,
|
||||||
|
limited_ok=False,
|
||||||
|
),
|
||||||
|
TemplateFunction(
|
||||||
|
"floor_name",
|
||||||
|
self.floor_name,
|
||||||
|
as_global=True,
|
||||||
|
as_filter=True,
|
||||||
|
requires_hass=True,
|
||||||
|
limited_ok=False,
|
||||||
|
),
|
||||||
|
TemplateFunction(
|
||||||
|
"floor_areas",
|
||||||
|
self.floor_areas,
|
||||||
|
as_global=True,
|
||||||
|
as_filter=True,
|
||||||
|
requires_hass=True,
|
||||||
|
),
|
||||||
|
TemplateFunction(
|
||||||
|
"floor_entities",
|
||||||
|
self.floor_entities,
|
||||||
|
as_global=True,
|
||||||
|
as_filter=True,
|
||||||
|
requires_hass=True,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
def floors(self) -> Iterable[str | None]:
|
||||||
|
"""Return all floors."""
|
||||||
|
floor_registry = fr.async_get(self.hass)
|
||||||
|
return [floor.floor_id for floor in floor_registry.async_list_floors()]
|
||||||
|
|
||||||
|
def floor_id(self, lookup_value: Any) -> str | None:
|
||||||
|
"""Get the floor ID from a floor or area name, alias, device id, or entity id."""
|
||||||
|
floor_registry = fr.async_get(self.hass)
|
||||||
|
lookup_str = str(lookup_value)
|
||||||
|
|
||||||
|
# Check if it's a floor name or alias
|
||||||
|
if floor := floor_registry.async_get_floor_by_name(lookup_str):
|
||||||
|
return floor.floor_id
|
||||||
|
floors_list = floor_registry.async_get_floors_by_alias(lookup_str)
|
||||||
|
if floors_list:
|
||||||
|
return floors_list[0].floor_id
|
||||||
|
|
||||||
|
# Resolve to area ID and get floor from area
|
||||||
|
if aid := resolve_area_id(self.hass, lookup_value):
|
||||||
|
area_reg = ar.async_get(self.hass)
|
||||||
|
if area := area_reg.async_get_area(aid):
|
||||||
|
return area.floor_id
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def floor_name(self, lookup_value: str) -> str | None:
|
||||||
|
"""Get the floor name from a floor id."""
|
||||||
|
floor_registry = fr.async_get(self.hass)
|
||||||
|
|
||||||
|
# Check if it's a floor ID
|
||||||
|
if floor := floor_registry.async_get_floor(lookup_value):
|
||||||
|
return floor.name
|
||||||
|
|
||||||
|
# Resolve to area ID and get floor name from area's floor
|
||||||
|
if aid := resolve_area_id(self.hass, lookup_value):
|
||||||
|
area_reg = ar.async_get(self.hass)
|
||||||
|
if (
|
||||||
|
(area := area_reg.async_get_area(aid))
|
||||||
|
and area.floor_id
|
||||||
|
and (floor := floor_registry.async_get_floor(area.floor_id))
|
||||||
|
):
|
||||||
|
return floor.name
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _floor_id_or_name(self, floor_id_or_name: str) -> str | None:
|
||||||
|
"""Get the floor ID from a floor name or ID."""
|
||||||
|
# If floor_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.floor_name(floor_id_or_name) is not None:
|
||||||
|
return floor_id_or_name
|
||||||
|
return self.floor_id(floor_id_or_name)
|
||||||
|
|
||||||
|
def floor_areas(self, floor_id_or_name: str) -> Iterable[str]:
|
||||||
|
"""Return area IDs for a given floor ID or name."""
|
||||||
|
if (_floor_id := self._floor_id_or_name(floor_id_or_name)) is None:
|
||||||
|
return []
|
||||||
|
|
||||||
|
area_reg = ar.async_get(self.hass)
|
||||||
|
entries = ar.async_entries_for_floor(area_reg, _floor_id)
|
||||||
|
return [entry.id for entry in entries if entry.id]
|
||||||
|
|
||||||
|
def floor_entities(self, floor_id_or_name: str) -> Iterable[str]:
|
||||||
|
"""Return entity_ids for a given floor ID or name."""
|
||||||
|
ent_reg = er.async_get(self.hass)
|
||||||
|
dev_reg = dr.async_get(self.hass)
|
||||||
|
entity_ids = []
|
||||||
|
|
||||||
|
for area_id in self.floor_areas(floor_id_or_name):
|
||||||
|
# Get entities directly assigned to the area
|
||||||
|
entity_ids.extend(
|
||||||
|
[
|
||||||
|
entry.entity_id
|
||||||
|
for entry in er.async_entries_for_area(ent_reg, area_id)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Also 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
|
||||||
@@ -2,10 +2,21 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any, NoReturn
|
from typing import TYPE_CHECKING, Any, NoReturn
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.helpers import (
|
||||||
|
area_registry as ar,
|
||||||
|
device_registry as dr,
|
||||||
|
entity_registry as er,
|
||||||
|
)
|
||||||
|
|
||||||
from .context import template_cv
|
from .context import template_cv
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
|
|
||||||
def raise_no_default(function: str, value: Any) -> NoReturn:
|
def raise_no_default(function: str, value: Any) -> NoReturn:
|
||||||
"""Raise ValueError when no default is specified for template functions."""
|
"""Raise ValueError when no default is specified for template functions."""
|
||||||
@@ -14,3 +25,47 @@ def raise_no_default(function: str, value: Any) -> NoReturn:
|
|||||||
f"Template error: {function} got invalid input '{value}' when {action} template"
|
f"Template error: {function} got invalid input '{value}' when {action} template"
|
||||||
f" '{template}' but no default was specified"
|
f" '{template}' but no default was specified"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_area_id(hass: HomeAssistant, lookup_value: Any) -> str | None:
|
||||||
|
"""Resolve lookup value to an area ID.
|
||||||
|
|
||||||
|
Accepts area name, area alias, device ID, or entity ID.
|
||||||
|
Returns the area ID or None if not found.
|
||||||
|
"""
|
||||||
|
area_reg = ar.async_get(hass)
|
||||||
|
dev_reg = dr.async_get(hass)
|
||||||
|
ent_reg = er.async_get(hass)
|
||||||
|
lookup_str = str(lookup_value)
|
||||||
|
|
||||||
|
# Check if it's an area name
|
||||||
|
if area := area_reg.async_get_area_by_name(lookup_str):
|
||||||
|
return area.id
|
||||||
|
|
||||||
|
# Check if it's an area alias
|
||||||
|
areas_list = area_reg.async_get_areas_by_alias(lookup_str)
|
||||||
|
if areas_list:
|
||||||
|
return areas_list[0].id
|
||||||
|
|
||||||
|
# Import here, not at top-level to avoid circular import
|
||||||
|
from homeassistant.helpers import config_validation as cv # noqa: PLC0415
|
||||||
|
|
||||||
|
# Check if it's an entity ID
|
||||||
|
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, return that
|
||||||
|
if entity.area_id:
|
||||||
|
return entity.area_id
|
||||||
|
# If entity has a device ID, return the area ID for the device
|
||||||
|
if entity.device_id and (device := dev_reg.async_get(entity.device_id)):
|
||||||
|
return device.area_id
|
||||||
|
|
||||||
|
# Check if it's a device ID
|
||||||
|
if device := dev_reg.async_get(lookup_value):
|
||||||
|
return device.area_id
|
||||||
|
|
||||||
|
return None
|
||||||
|
|||||||
297
tests/helpers/template/extensions/test_floors.py
Normal file
297
tests/helpers/template/extensions/test_floors.py
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
"""Test floor 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,
|
||||||
|
floor_registry as fr,
|
||||||
|
)
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
from tests.helpers.template.helpers import assert_result_info, render_to_info
|
||||||
|
|
||||||
|
|
||||||
|
async def test_floors(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
floor_registry: fr.FloorRegistry,
|
||||||
|
) -> None:
|
||||||
|
"""Test floors function."""
|
||||||
|
|
||||||
|
# Test no floors
|
||||||
|
info = render_to_info(hass, "{{ floors() }}")
|
||||||
|
assert_result_info(info, [])
|
||||||
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
# Test one floor
|
||||||
|
floor1 = floor_registry.async_create("First floor")
|
||||||
|
info = render_to_info(hass, "{{ floors() }}")
|
||||||
|
assert_result_info(info, [floor1.floor_id])
|
||||||
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
# Test multiple floors
|
||||||
|
floor2 = floor_registry.async_create("Second floor")
|
||||||
|
info = render_to_info(hass, "{{ floors() }}")
|
||||||
|
assert_result_info(info, [floor1.floor_id, floor2.floor_id])
|
||||||
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
|
||||||
|
async def test_floor_id(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
floor_registry: fr.FloorRegistry,
|
||||||
|
area_registry: ar.AreaRegistry,
|
||||||
|
device_registry: dr.DeviceRegistry,
|
||||||
|
entity_registry: er.EntityRegistry,
|
||||||
|
) -> None:
|
||||||
|
"""Test floor_id function."""
|
||||||
|
|
||||||
|
def test(value: str, expected: str | None) -> None:
|
||||||
|
info = render_to_info(hass, f"{{{{ floor_id('{value}') }}}}")
|
||||||
|
assert_result_info(info, expected)
|
||||||
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
info = render_to_info(hass, f"{{{{ '{value}' | floor_id }}}}")
|
||||||
|
assert_result_info(info, expected)
|
||||||
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
# Test non existing floor name
|
||||||
|
test("Third floor", None)
|
||||||
|
|
||||||
|
# Test wrong value type
|
||||||
|
info = render_to_info(hass, "{{ floor_id(42) }}")
|
||||||
|
assert_result_info(info, None)
|
||||||
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
info = render_to_info(hass, "{{ 42 | floor_id }}")
|
||||||
|
assert_result_info(info, None)
|
||||||
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
# Test with an actual floor
|
||||||
|
floor = floor_registry.async_create("First floor")
|
||||||
|
test("First floor", floor.floor_id)
|
||||||
|
|
||||||
|
config_entry = MockConfigEntry(domain="light")
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
area_entry_hex = area_registry.async_get_or_create("123abc")
|
||||||
|
|
||||||
|
# Create area, device, entity and assign area to device and entity
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
test(area_entry_hex.id, None)
|
||||||
|
test(device_entry.id, None)
|
||||||
|
test(entity_entry.entity_id, None)
|
||||||
|
|
||||||
|
# Add floor to area
|
||||||
|
area_entry_hex = area_registry.async_update(
|
||||||
|
area_entry_hex.id, floor_id=floor.floor_id
|
||||||
|
)
|
||||||
|
|
||||||
|
test(area_entry_hex.id, floor.floor_id)
|
||||||
|
test(device_entry.id, floor.floor_id)
|
||||||
|
test(entity_entry.entity_id, floor.floor_id)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_floor_name(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
floor_registry: fr.FloorRegistry,
|
||||||
|
area_registry: ar.AreaRegistry,
|
||||||
|
device_registry: dr.DeviceRegistry,
|
||||||
|
entity_registry: er.EntityRegistry,
|
||||||
|
) -> None:
|
||||||
|
"""Test floor_name function."""
|
||||||
|
|
||||||
|
def test(value: str, expected: str | None) -> None:
|
||||||
|
info = render_to_info(hass, f"{{{{ floor_name('{value}') }}}}")
|
||||||
|
assert_result_info(info, expected)
|
||||||
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
info = render_to_info(hass, f"{{{{ '{value}' | floor_name }}}}")
|
||||||
|
assert_result_info(info, expected)
|
||||||
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
# Test non existing floor name
|
||||||
|
test("Third floor", None)
|
||||||
|
|
||||||
|
# Test wrong value type
|
||||||
|
info = render_to_info(hass, "{{ floor_name(42) }}")
|
||||||
|
assert_result_info(info, None)
|
||||||
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
info = render_to_info(hass, "{{ 42 | floor_name }}")
|
||||||
|
assert_result_info(info, None)
|
||||||
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
# Test existing floor ID
|
||||||
|
floor = floor_registry.async_create("First floor")
|
||||||
|
test(floor.floor_id, floor.name)
|
||||||
|
|
||||||
|
config_entry = MockConfigEntry(domain="light")
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
area_entry_hex = area_registry.async_get_or_create("123abc")
|
||||||
|
|
||||||
|
# Create area, device, entity and assign area to device and entity
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
test(area_entry_hex.id, None)
|
||||||
|
test(device_entry.id, None)
|
||||||
|
test(entity_entry.entity_id, None)
|
||||||
|
|
||||||
|
# Add floor to area
|
||||||
|
area_entry_hex = area_registry.async_update(
|
||||||
|
area_entry_hex.id, floor_id=floor.floor_id
|
||||||
|
)
|
||||||
|
|
||||||
|
test(area_entry_hex.id, floor.name)
|
||||||
|
test(device_entry.id, floor.name)
|
||||||
|
test(entity_entry.entity_id, floor.name)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_floor_areas(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
floor_registry: fr.FloorRegistry,
|
||||||
|
area_registry: ar.AreaRegistry,
|
||||||
|
) -> None:
|
||||||
|
"""Test floor_areas function."""
|
||||||
|
|
||||||
|
# Test non existing floor ID
|
||||||
|
info = render_to_info(hass, "{{ floor_areas('skyring') }}")
|
||||||
|
assert_result_info(info, [])
|
||||||
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
info = render_to_info(hass, "{{ 'skyring' | floor_areas }}")
|
||||||
|
assert_result_info(info, [])
|
||||||
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
# Test wrong value type
|
||||||
|
info = render_to_info(hass, "{{ floor_areas(42) }}")
|
||||||
|
assert_result_info(info, [])
|
||||||
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
info = render_to_info(hass, "{{ 42 | floor_areas }}")
|
||||||
|
assert_result_info(info, [])
|
||||||
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
floor = floor_registry.async_create("First floor")
|
||||||
|
area = area_registry.async_create("Living room")
|
||||||
|
area_registry.async_update(area.id, floor_id=floor.floor_id)
|
||||||
|
|
||||||
|
# Get areas by floor ID
|
||||||
|
info = render_to_info(hass, f"{{{{ floor_areas('{floor.floor_id}') }}}}")
|
||||||
|
assert_result_info(info, [area.id])
|
||||||
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
info = render_to_info(hass, f"{{{{ '{floor.floor_id}' | floor_areas }}}}")
|
||||||
|
assert_result_info(info, [area.id])
|
||||||
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
# Get areas by floor name
|
||||||
|
info = render_to_info(hass, f"{{{{ floor_areas('{floor.name}') }}}}")
|
||||||
|
assert_result_info(info, [area.id])
|
||||||
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
info = render_to_info(hass, f"{{{{ '{floor.name}' | floor_areas }}}}")
|
||||||
|
assert_result_info(info, [area.id])
|
||||||
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
|
||||||
|
async def test_floor_entities(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
floor_registry: fr.FloorRegistry,
|
||||||
|
area_registry: ar.AreaRegistry,
|
||||||
|
entity_registry: er.EntityRegistry,
|
||||||
|
) -> None:
|
||||||
|
"""Test floor_entities function."""
|
||||||
|
|
||||||
|
# Test non existing floor ID
|
||||||
|
info = render_to_info(hass, "{{ floor_entities('skyring') }}")
|
||||||
|
assert_result_info(info, [])
|
||||||
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
info = render_to_info(hass, "{{ 'skyring' | floor_entities }}")
|
||||||
|
assert_result_info(info, [])
|
||||||
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
# Test wrong value type
|
||||||
|
info = render_to_info(hass, "{{ floor_entities(42) }}")
|
||||||
|
assert_result_info(info, [])
|
||||||
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
info = render_to_info(hass, "{{ 42 | floor_entities }}")
|
||||||
|
assert_result_info(info, [])
|
||||||
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
floor = floor_registry.async_create("First floor")
|
||||||
|
area1 = area_registry.async_create("Living room")
|
||||||
|
area2 = area_registry.async_create("Dining room")
|
||||||
|
area_registry.async_update(area1.id, floor_id=floor.floor_id)
|
||||||
|
area_registry.async_update(area2.id, floor_id=floor.floor_id)
|
||||||
|
|
||||||
|
config_entry = MockConfigEntry(domain="light")
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
entity_entry = entity_registry.async_get_or_create(
|
||||||
|
"light",
|
||||||
|
"hue",
|
||||||
|
"living_room",
|
||||||
|
config_entry=config_entry,
|
||||||
|
)
|
||||||
|
entity_registry.async_update_entity(entity_entry.entity_id, area_id=area1.id)
|
||||||
|
entity_entry = entity_registry.async_get_or_create(
|
||||||
|
"light",
|
||||||
|
"hue",
|
||||||
|
"dining_room",
|
||||||
|
config_entry=config_entry,
|
||||||
|
)
|
||||||
|
entity_registry.async_update_entity(entity_entry.entity_id, area_id=area2.id)
|
||||||
|
|
||||||
|
# Get entities by floor ID
|
||||||
|
expected = ["light.hue_living_room", "light.hue_dining_room"]
|
||||||
|
info = render_to_info(hass, f"{{{{ floor_entities('{floor.floor_id}') }}}}")
|
||||||
|
assert_result_info(info, expected)
|
||||||
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
info = render_to_info(hass, f"{{{{ '{floor.floor_id}' | floor_entities }}}}")
|
||||||
|
assert_result_info(info, expected)
|
||||||
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
# Get entities by floor name
|
||||||
|
info = render_to_info(hass, f"{{{{ floor_entities('{floor.name}') }}}}")
|
||||||
|
assert_result_info(info, expected)
|
||||||
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
info = render_to_info(hass, f"{{{{ '{floor.name}' | floor_entities }}}}")
|
||||||
|
assert_result_info(info, expected)
|
||||||
|
assert info.rate_limit is None
|
||||||
@@ -2,7 +2,15 @@
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.helpers.template.helpers import raise_no_default
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import (
|
||||||
|
area_registry as ar,
|
||||||
|
device_registry as dr,
|
||||||
|
entity_registry as er,
|
||||||
|
)
|
||||||
|
from homeassistant.helpers.template.helpers import raise_no_default, resolve_area_id
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
def test_raise_no_default() -> None:
|
def test_raise_no_default() -> None:
|
||||||
@@ -12,3 +20,87 @@ def test_raise_no_default() -> None:
|
|||||||
match="Template error: test got invalid input 'invalid' when rendering or compiling template '' but no default was specified",
|
match="Template error: test got invalid input 'invalid' when rendering or compiling template '' but no default was specified",
|
||||||
):
|
):
|
||||||
raise_no_default("test", "invalid")
|
raise_no_default("test", "invalid")
|
||||||
|
|
||||||
|
|
||||||
|
async def test_resolve_area_id(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
area_registry: ar.AreaRegistry,
|
||||||
|
device_registry: dr.DeviceRegistry,
|
||||||
|
entity_registry: er.EntityRegistry,
|
||||||
|
) -> None:
|
||||||
|
"""Test resolve_area_id function."""
|
||||||
|
config_entry = MockConfigEntry(domain="light")
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
# Test non existing entity id
|
||||||
|
assert resolve_area_id(hass, "sensor.fake") is None
|
||||||
|
|
||||||
|
# Test non existing device id (hex value)
|
||||||
|
assert resolve_area_id(hass, "123abc") is None
|
||||||
|
|
||||||
|
# Test non existing area name
|
||||||
|
assert resolve_area_id(hass, "fake area name") is None
|
||||||
|
|
||||||
|
# Test wrong value type
|
||||||
|
assert resolve_area_id(hass, 56) 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,
|
||||||
|
)
|
||||||
|
assert resolve_area_id(hass, device_entry.id) is None
|
||||||
|
assert resolve_area_id(hass, entity_entry.entity_id) is None
|
||||||
|
|
||||||
|
# Test device ID, entity ID and area name as input with area name that looks like
|
||||||
|
# a device ID
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
assert resolve_area_id(hass, device_entry.id) == area_entry_hex.id
|
||||||
|
assert resolve_area_id(hass, entity_entry.entity_id) == area_entry_hex.id
|
||||||
|
assert resolve_area_id(hass, area_entry_hex.name) == area_entry_hex.id
|
||||||
|
|
||||||
|
# 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
|
||||||
|
)
|
||||||
|
|
||||||
|
assert resolve_area_id(hass, device_entry.id) == area_entry_entity_id.id
|
||||||
|
assert resolve_area_id(hass, entity_entry.entity_id) == area_entry_entity_id.id
|
||||||
|
assert resolve_area_id(hass, area_entry_entity_id.name) == area_entry_entity_id.id
|
||||||
|
|
||||||
|
# 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
|
||||||
|
)
|
||||||
|
|
||||||
|
assert resolve_area_id(hass, entity_entry.entity_id) == area_entry_entity_id.id
|
||||||
|
|
||||||
|
# Test area alias
|
||||||
|
area_with_alias = area_registry.async_get_or_create("Living Room")
|
||||||
|
area_registry.async_update(area_with_alias.id, aliases={"lounge", "family room"})
|
||||||
|
|
||||||
|
assert resolve_area_id(hass, "Living Room") == area_with_alias.id
|
||||||
|
assert resolve_area_id(hass, "lounge") == area_with_alias.id
|
||||||
|
assert resolve_area_id(hass, "family room") == area_with_alias.id
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ from homeassistant.helpers import (
|
|||||||
device_registry as dr,
|
device_registry as dr,
|
||||||
entity,
|
entity,
|
||||||
entity_registry as er,
|
entity_registry as er,
|
||||||
floor_registry as fr,
|
|
||||||
issue_registry as ir,
|
issue_registry as ir,
|
||||||
template,
|
template,
|
||||||
translation,
|
translation,
|
||||||
@@ -4419,289 +4418,6 @@ async def test_lru_increases_with_many_entities(hass: HomeAssistant) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_floors(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
floor_registry: fr.FloorRegistry,
|
|
||||||
) -> None:
|
|
||||||
"""Test floors function."""
|
|
||||||
|
|
||||||
# Test no floors
|
|
||||||
info = render_to_info(hass, "{{ floors() }}")
|
|
||||||
assert_result_info(info, [])
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
# Test one floor
|
|
||||||
floor1 = floor_registry.async_create("First floor")
|
|
||||||
info = render_to_info(hass, "{{ floors() }}")
|
|
||||||
assert_result_info(info, [floor1.floor_id])
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
# Test multiple floors
|
|
||||||
floor2 = floor_registry.async_create("Second floor")
|
|
||||||
info = render_to_info(hass, "{{ floors() }}")
|
|
||||||
assert_result_info(info, [floor1.floor_id, floor2.floor_id])
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
|
|
||||||
async def test_floor_id(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
floor_registry: fr.FloorRegistry,
|
|
||||||
area_registry: ar.AreaRegistry,
|
|
||||||
device_registry: dr.DeviceRegistry,
|
|
||||||
entity_registry: er.EntityRegistry,
|
|
||||||
) -> None:
|
|
||||||
"""Test floor_id function."""
|
|
||||||
|
|
||||||
def test(value: str, expected: str | None) -> None:
|
|
||||||
info = render_to_info(hass, f"{{{{ floor_id('{value}') }}}}")
|
|
||||||
assert_result_info(info, expected)
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
info = render_to_info(hass, f"{{{{ '{value}' | floor_id }}}}")
|
|
||||||
assert_result_info(info, expected)
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
# Test non existing floor name
|
|
||||||
test("Third floor", None)
|
|
||||||
|
|
||||||
# Test wrong value type
|
|
||||||
info = render_to_info(hass, "{{ floor_id(42) }}")
|
|
||||||
assert_result_info(info, None)
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
info = render_to_info(hass, "{{ 42 | floor_id }}")
|
|
||||||
assert_result_info(info, None)
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
# Test with an actual floor
|
|
||||||
floor = floor_registry.async_create("First floor")
|
|
||||||
test("First floor", floor.floor_id)
|
|
||||||
|
|
||||||
config_entry = MockConfigEntry(domain="light")
|
|
||||||
config_entry.add_to_hass(hass)
|
|
||||||
area_entry_hex = area_registry.async_get_or_create("123abc")
|
|
||||||
|
|
||||||
# Create area, device, entity and assign area to device and entity
|
|
||||||
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,
|
|
||||||
)
|
|
||||||
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
|
|
||||||
)
|
|
||||||
|
|
||||||
test(area_entry_hex.id, None)
|
|
||||||
test(device_entry.id, None)
|
|
||||||
test(entity_entry.entity_id, None)
|
|
||||||
|
|
||||||
# Add floor to area
|
|
||||||
area_entry_hex = area_registry.async_update(
|
|
||||||
area_entry_hex.id, floor_id=floor.floor_id
|
|
||||||
)
|
|
||||||
|
|
||||||
test(area_entry_hex.id, floor.floor_id)
|
|
||||||
test(device_entry.id, floor.floor_id)
|
|
||||||
test(entity_entry.entity_id, floor.floor_id)
|
|
||||||
|
|
||||||
|
|
||||||
async def test_floor_name(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
floor_registry: fr.FloorRegistry,
|
|
||||||
area_registry: ar.AreaRegistry,
|
|
||||||
device_registry: dr.DeviceRegistry,
|
|
||||||
entity_registry: er.EntityRegistry,
|
|
||||||
) -> None:
|
|
||||||
"""Test floor_name function."""
|
|
||||||
|
|
||||||
def test(value: str, expected: str | None) -> None:
|
|
||||||
info = render_to_info(hass, f"{{{{ floor_name('{value}') }}}}")
|
|
||||||
assert_result_info(info, expected)
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
info = render_to_info(hass, f"{{{{ '{value}' | floor_name }}}}")
|
|
||||||
assert_result_info(info, expected)
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
# Test non existing floor name
|
|
||||||
test("Third floor", None)
|
|
||||||
|
|
||||||
# Test wrong value type
|
|
||||||
info = render_to_info(hass, "{{ floor_name(42) }}")
|
|
||||||
assert_result_info(info, None)
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
info = render_to_info(hass, "{{ 42 | floor_name }}")
|
|
||||||
assert_result_info(info, None)
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
# Test existing floor ID
|
|
||||||
floor = floor_registry.async_create("First floor")
|
|
||||||
test(floor.floor_id, floor.name)
|
|
||||||
|
|
||||||
config_entry = MockConfigEntry(domain="light")
|
|
||||||
config_entry.add_to_hass(hass)
|
|
||||||
area_entry_hex = area_registry.async_get_or_create("123abc")
|
|
||||||
|
|
||||||
# Create area, device, entity and assign area to device and entity
|
|
||||||
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,
|
|
||||||
)
|
|
||||||
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
|
|
||||||
)
|
|
||||||
|
|
||||||
test(area_entry_hex.id, None)
|
|
||||||
test(device_entry.id, None)
|
|
||||||
test(entity_entry.entity_id, None)
|
|
||||||
|
|
||||||
# Add floor to area
|
|
||||||
area_entry_hex = area_registry.async_update(
|
|
||||||
area_entry_hex.id, floor_id=floor.floor_id
|
|
||||||
)
|
|
||||||
|
|
||||||
test(area_entry_hex.id, floor.name)
|
|
||||||
test(device_entry.id, floor.name)
|
|
||||||
test(entity_entry.entity_id, floor.name)
|
|
||||||
|
|
||||||
|
|
||||||
async def test_floor_areas(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
floor_registry: fr.FloorRegistry,
|
|
||||||
area_registry: ar.AreaRegistry,
|
|
||||||
) -> None:
|
|
||||||
"""Test floor_areas function."""
|
|
||||||
|
|
||||||
# Test non existing floor ID
|
|
||||||
info = render_to_info(hass, "{{ floor_areas('skyring') }}")
|
|
||||||
assert_result_info(info, [])
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
info = render_to_info(hass, "{{ 'skyring' | floor_areas }}")
|
|
||||||
assert_result_info(info, [])
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
# Test wrong value type
|
|
||||||
info = render_to_info(hass, "{{ floor_areas(42) }}")
|
|
||||||
assert_result_info(info, [])
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
info = render_to_info(hass, "{{ 42 | floor_areas }}")
|
|
||||||
assert_result_info(info, [])
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
floor = floor_registry.async_create("First floor")
|
|
||||||
area = area_registry.async_create("Living room")
|
|
||||||
area_registry.async_update(area.id, floor_id=floor.floor_id)
|
|
||||||
|
|
||||||
# Get areas by floor ID
|
|
||||||
info = render_to_info(hass, f"{{{{ floor_areas('{floor.floor_id}') }}}}")
|
|
||||||
assert_result_info(info, [area.id])
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
info = render_to_info(hass, f"{{{{ '{floor.floor_id}' | floor_areas }}}}")
|
|
||||||
assert_result_info(info, [area.id])
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
# Get entities by floor name
|
|
||||||
info = render_to_info(hass, f"{{{{ floor_areas('{floor.name}') }}}}")
|
|
||||||
assert_result_info(info, [area.id])
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
info = render_to_info(hass, f"{{{{ '{floor.name}' | floor_areas }}}}")
|
|
||||||
assert_result_info(info, [area.id])
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
|
|
||||||
async def test_floor_entities(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
floor_registry: fr.FloorRegistry,
|
|
||||||
area_registry: ar.AreaRegistry,
|
|
||||||
entity_registry: er.EntityRegistry,
|
|
||||||
) -> None:
|
|
||||||
"""Test floor_entities function."""
|
|
||||||
|
|
||||||
# Test non existing floor ID
|
|
||||||
info = render_to_info(hass, "{{ floor_entities('skyring') }}")
|
|
||||||
assert_result_info(info, [])
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
info = render_to_info(hass, "{{ 'skyring' | floor_entities }}")
|
|
||||||
assert_result_info(info, [])
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
# Test wrong value type
|
|
||||||
info = render_to_info(hass, "{{ floor_entities(42) }}")
|
|
||||||
assert_result_info(info, [])
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
info = render_to_info(hass, "{{ 42 | floor_entities }}")
|
|
||||||
assert_result_info(info, [])
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
floor = floor_registry.async_create("First floor")
|
|
||||||
area1 = area_registry.async_create("Living room")
|
|
||||||
area2 = area_registry.async_create("Dining room")
|
|
||||||
area_registry.async_update(area1.id, floor_id=floor.floor_id)
|
|
||||||
area_registry.async_update(area2.id, floor_id=floor.floor_id)
|
|
||||||
|
|
||||||
config_entry = MockConfigEntry(domain="light")
|
|
||||||
config_entry.add_to_hass(hass)
|
|
||||||
entity_entry = entity_registry.async_get_or_create(
|
|
||||||
"light",
|
|
||||||
"hue",
|
|
||||||
"living_room",
|
|
||||||
config_entry=config_entry,
|
|
||||||
)
|
|
||||||
entity_registry.async_update_entity(entity_entry.entity_id, area_id=area1.id)
|
|
||||||
entity_entry = entity_registry.async_get_or_create(
|
|
||||||
"light",
|
|
||||||
"hue",
|
|
||||||
"dining_room",
|
|
||||||
config_entry=config_entry,
|
|
||||||
)
|
|
||||||
entity_registry.async_update_entity(entity_entry.entity_id, area_id=area2.id)
|
|
||||||
|
|
||||||
# Get entities by floor ID
|
|
||||||
expected = ["light.hue_living_room", "light.hue_dining_room"]
|
|
||||||
info = render_to_info(hass, f"{{{{ floor_entities('{floor.floor_id}') }}}}")
|
|
||||||
assert_result_info(info, expected)
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
info = render_to_info(hass, f"{{{{ '{floor.floor_id}' | floor_entities }}}}")
|
|
||||||
assert_result_info(info, expected)
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
# Get entities by floor name
|
|
||||||
info = render_to_info(hass, f"{{{{ floor_entities('{floor.name}') }}}}")
|
|
||||||
assert_result_info(info, expected)
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
info = render_to_info(hass, f"{{{{ '{floor.name}' | floor_entities }}}}")
|
|
||||||
assert_result_info(info, expected)
|
|
||||||
assert info.rate_limit is None
|
|
||||||
|
|
||||||
|
|
||||||
async def test_template_thread_safety_checks(hass: HomeAssistant) -> None:
|
async def test_template_thread_safety_checks(hass: HomeAssistant) -> None:
|
||||||
"""Test template thread safety checks."""
|
"""Test template thread safety checks."""
|
||||||
hass.states.async_set("sensor.test", "23")
|
hass.states.async_set("sensor.test", "23")
|
||||||
|
|||||||
Reference in New Issue
Block a user