From 6f5507670f55ed9094fac4fa3e034e475946ad54 Mon Sep 17 00:00:00 2001 From: Petro31 <35082313+Petro31@users.noreply.github.com> Date: Mon, 8 Dec 2025 13:19:52 -0500 Subject: [PATCH] Fix multiple top-level support for template integration (#158244) --- homeassistant/components/template/config.py | 14 ++++- tests/components/template/test_config.py | 69 +++++++++++++++++++++ 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/template/config.py b/homeassistant/components/template/config.py index bc343a8d145..7b4038375bf 100644 --- a/homeassistant/components/template/config.py +++ b/homeassistant/components/template/config.py @@ -2,6 +2,7 @@ from collections.abc import Callable from contextlib import suppress +import itertools import logging from typing import Any @@ -346,12 +347,21 @@ async def async_validate_config_section( async def async_validate_config(hass: HomeAssistant, config: ConfigType) -> ConfigType: """Validate config.""" - if DOMAIN not in config: + + configs = [] + for key in config: + if DOMAIN not in key: + continue + + if key == DOMAIN or (key.startswith(DOMAIN) and len(key.split()) > 1): + configs.append(cv.ensure_list(config[key])) + + if not configs: return config config_sections = [] - for cfg in cv.ensure_list(config[DOMAIN]): + for cfg in itertools.chain(*configs): try: template_config: TemplateConfig = await async_validate_config_section( hass, cfg diff --git a/tests/components/template/test_config.py b/tests/components/template/test_config.py index 5764fd6c5d4..7cfb43cafc8 100644 --- a/tests/components/template/test_config.py +++ b/tests/components/template/test_config.py @@ -475,3 +475,72 @@ async def test_invalid_schema_raises_issue( assert issue.domain == "template" assert issue.severity == ir.IssueSeverity.WARNING + + +async def test_multiple_configuration_keys( + hass: HomeAssistant, +) -> None: + """Test multiple configurations keys create entities.""" + await async_setup_component( + hass, + "template", + { + "template": [{"binary_sensor": [{"name": "Foo", "state": "{{ True }}"}]}], + "template mytemplates": [ + { + "sensor": [ + {"name": "Foo", "state": "{{ 'bar' }}"}, + {"name": "Bar", "state": "{{ 'foo' }}"}, + ] + } + ], + "template y": [ + { + "cover": [ + { + "name": "Shades Curtain", + "unique_id": "shades_curtain", + "open_cover": [], + "close_cover": [], + "stop_cover": [], + } + ] + }, + { + "cover": [ + { + "open_cover": { + "target": {"entity_id": ["cover.shades_curtain"]}, + "action": "cover.close_cover", + }, + "close_cover": { + "target": {"entity_id": ["cover.shades_curtain"]}, + "action": "cover.open_cover", + }, + "stop_cover": { + "target": {"entity_id": ["cover.shades_curtain"]}, + "action": "cover.stop_cover", + }, + "default_entity_id": "cover.shades_reversed", + "icon": "{% set s = states('cover.shades_curtain') %}\n{% if s == 'open' %}\n mdi:curtains-closed\n{% else %}\n mdi:curtains\n{% endif %}", + "name": "Shades Reversed", + "unique_id": "c0223bcb-32c6-430e-a2c1-3545f8031796", + "state": "{% set s = states('cover.shades_curtain') %}\n{% if s == 'open' %}\n closed\n{% elif s == 'closed' %}\n open\n{% elif s == 'opening' %}\n closing\n{% elif s == 'closing' %}\n opening\n{% else %}\n unknown\n{% endif %}", + } + ] + }, + ], + }, + ) + await hass.async_block_till_done() + + for entity_id, expected in ( + ("binary_sensor.foo", "on"), + ("sensor.foo", "bar"), + ("sensor.bar", "foo"), + ("cover.shades_curtain", "unknown"), + ("cover.shades_reversed", "unknown"), + ): + state = hass.states.get(entity_id) + assert state + assert state.state == expected