Add area_entities and area_devices template functions/filters (#55228)
This commit is contained in:
parent
806dc51125
commit
e5255cf21f
2 changed files with 137 additions and 0 deletions
|
@ -1038,6 +1038,52 @@ def area_name(hass: HomeAssistant, lookup_value: str) -> str | None:
|
||||||
return None
|
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 = entity_registry.async_get(hass)
|
||||||
|
entity_ids = [
|
||||||
|
entry.entity_id
|
||||||
|
for entry in entity_registry.async_entries_for_area(ent_reg, _area_id)
|
||||||
|
]
|
||||||
|
dev_reg = device_registry.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 device_registry.async_entries_for_area(dev_reg, _area_id)
|
||||||
|
for entity in entity_registry.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 = device_registry.async_get(hass)
|
||||||
|
entries = device_registry.async_entries_for_area(dev_reg, _area_id)
|
||||||
|
return [entry.id for entry in entries]
|
||||||
|
|
||||||
|
|
||||||
def closest(hass, *args):
|
def closest(hass, *args):
|
||||||
"""Find closest entity.
|
"""Find closest entity.
|
||||||
|
|
||||||
|
@ -1783,6 +1829,12 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment):
|
||||||
self.globals["area_name"] = hassfunction(area_name)
|
self.globals["area_name"] = hassfunction(area_name)
|
||||||
self.filters["area_name"] = pass_context(self.globals["area_name"])
|
self.filters["area_name"] = pass_context(self.globals["area_name"])
|
||||||
|
|
||||||
|
self.globals["area_entities"] = hassfunction(area_entities)
|
||||||
|
self.filters["area_entities"] = pass_context(self.globals["area_entities"])
|
||||||
|
|
||||||
|
self.globals["area_devices"] = hassfunction(area_devices)
|
||||||
|
self.filters["area_devices"] = pass_context(self.globals["area_devices"])
|
||||||
|
|
||||||
if limited:
|
if limited:
|
||||||
# Only device_entities is available to limited templates, mark other
|
# Only device_entities is available to limited templates, mark other
|
||||||
# functions and filters as unsupported.
|
# functions and filters as unsupported.
|
||||||
|
|
|
@ -2143,6 +2143,91 @@ async def test_area_name(hass):
|
||||||
assert info.rate_limit is None
|
assert info.rate_limit is None
|
||||||
|
|
||||||
|
|
||||||
|
async def test_area_entities(hass):
|
||||||
|
"""Test area_entities function."""
|
||||||
|
config_entry = MockConfigEntry(domain="light")
|
||||||
|
entity_registry = mock_registry(hass)
|
||||||
|
device_registry = mock_device_registry(hass)
|
||||||
|
area_registry = mock_area_registry(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_registry.async_get_or_create(
|
||||||
|
"light",
|
||||||
|
"hue",
|
||||||
|
"5678",
|
||||||
|
config_entry=config_entry,
|
||||||
|
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):
|
||||||
|
"""Test area_devices function."""
|
||||||
|
config_entry = MockConfigEntry(domain="light")
|
||||||
|
device_registry = mock_device_registry(hass)
|
||||||
|
area_registry = mock_area_registry(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):
|
def test_closest_function_to_coord(hass):
|
||||||
"""Test closest function to coord."""
|
"""Test closest function to coord."""
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
|
|
Loading…
Add table
Reference in a new issue