diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index 7f3937d41c1..4c6888cbf2f 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -916,6 +916,38 @@ def device_entities(hass: HomeAssistant, _device_id: str) -> Iterable[str]: return [entry.entity_id for entry in entries] +def integration_entities(hass: HomeAssistant, entry_name: str) -> Iterable[str]: + """ + Get entity ids for entities tied to an integration/domain. + + Provide entry_name as domain to get all entity id's for a integration/domain + or provide a config entry title for filtering between instances of the same integration. + """ + # first try if this is a config entry match + conf_entry = next( + ( + entry.entry_id + for entry in hass.config_entries.async_entries() + if entry.title == entry_name + ), + None, + ) + if conf_entry is not None: + ent_reg = entity_registry.async_get(hass) + entries = entity_registry.async_entries_for_config_entry(ent_reg, conf_entry) + return [entry.entity_id for entry in entries] + + # fallback to just returning all entities for a domain + # pylint: disable=import-outside-toplevel + from homeassistant.helpers.entity import entity_sources + + return [ + entity_id + for entity_id, info in entity_sources(hass).items() + if info["domain"] == entry_name + ] + + def device_id(hass: HomeAssistant, entity_id_or_device_name: str) -> str | None: """Get a device ID from an entity ID or device name.""" entity_reg = entity_registry.async_get(hass) @@ -1853,6 +1885,11 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment): self.globals["area_devices"] = hassfunction(area_devices) self.filters["area_devices"] = pass_context(self.globals["area_devices"]) + self.globals["integration_entities"] = hassfunction(integration_entities) + self.filters["integration_entities"] = pass_context( + self.globals["integration_entities"] + ) + if limited: # Only device_entities is available to limited templates, mark other # functions and filters as unsupported. diff --git a/tests/helpers/test_template.py b/tests/helpers/test_template.py index 0e1d96b8dac..f1dc740a068 100644 --- a/tests/helpers/test_template.py +++ b/tests/helpers/test_template.py @@ -1,5 +1,6 @@ """Test Home Assistant template helper methods.""" -from datetime import datetime +from datetime import datetime, timedelta +import logging import math import random from unittest.mock import patch @@ -20,7 +21,8 @@ from homeassistant.const import ( VOLUME_LITERS, ) from homeassistant.exceptions import TemplateError -from homeassistant.helpers import device_registry as dr, template +from homeassistant.helpers import device_registry as dr, entity, template +from homeassistant.helpers.entity_platform import EntityPlatform from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util from homeassistant.util.unit_system import UnitSystem @@ -1849,6 +1851,44 @@ async def test_device_entities(hass): assert info.rate_limit is None +async def test_integration_entities(hass): + """Test integration_entities function.""" + entity_registry = mock_registry(hass) + + # test entities for given config entry title + config_entry = MockConfigEntry(domain="mock", title="Mock bridge 2") + config_entry.add_to_hass(hass) + entity_entry = entity_registry.async_get_or_create( + "sensor", "mock", "test", config_entry=config_entry + ) + info = render_to_info(hass, "{{ integration_entities('Mock bridge 2') }}") + assert_result_info(info, [entity_entry.entity_id]) + assert info.rate_limit is None + + # test integration entities not in entity registry + mock_entity = entity.Entity() + mock_entity.hass = hass + mock_entity.entity_id = "light.test_entity" + mock_entity.platform = EntityPlatform( + hass=hass, + logger=logging.getLogger(__name__), + domain="light", + platform_name="entryless_integration", + platform=None, + scan_interval=timedelta(seconds=30), + entity_namespace=None, + ) + await mock_entity.async_internal_added_to_hass() + info = render_to_info(hass, "{{ integration_entities('entryless_integration') }}") + assert_result_info(info, ["light.test_entity"]) + assert info.rate_limit is None + + # Test non existing integration/entry title + info = render_to_info(hass, "{{ integration_entities('abc123') }}") + assert_result_info(info, []) + assert info.rate_limit is None + + async def test_device_id(hass): """Test device_id function.""" config_entry = MockConfigEntry(domain="light")