11"""template conftest."""
22
3+ from dataclasses import dataclass
34from enum import Enum
45
56import pytest
89from homeassistant .config_entries import SOURCE_USER
910from homeassistant .core import HomeAssistant , ServiceCall
1011from homeassistant .data_entry_flow import FlowResultType
12+ from homeassistant .helpers import entity_registry as er
1113from homeassistant .helpers .typing import ConfigType
1214from homeassistant .setup import async_setup_component
1315
@@ -40,19 +42,37 @@ def make_test_trigger(*entities: str) -> dict:
4042 }
4143
4244
45+ async def async_trigger (
46+ hass : HomeAssistant , entity_id : str , state : str | None = None
47+ ) -> None :
48+ """Trigger a state change."""
49+ hass .states .async_set (entity_id , state )
50+ await hass .async_block_till_done ()
51+
52+
4353async def async_setup_legacy_platforms (
4454 hass : HomeAssistant ,
4555 domain : str ,
46- slug : str ,
56+ slug : str | None ,
4757 count : int ,
48- config : ConfigType ,
58+ config : ConfigType | list [ ConfigType ] ,
4959) -> None :
5060 """Do setup of any legacy platform that supports a keyed dictionary of template entities."""
61+ if slug is None :
62+ # Lock and Weather platforms do not use a slug
63+ if isinstance (config , list ):
64+ config = {domain : [{"platform" : "template" , ** item } for item in config ]}
65+ else :
66+ config = {domain : {"platform" : "template" , ** config }}
67+ else :
68+ assert isinstance (config , dict )
69+ config = {domain : {"platform" : "template" , slug : config }}
70+
5171 with assert_setup_component (count , domain ):
5272 assert await async_setup_component (
5373 hass ,
5474 domain ,
55- { domain : { "platform" : "template" , slug : config }} ,
75+ config ,
5676 )
5777
5878 await hass .async_block_till_done ()
@@ -64,16 +84,15 @@ async def async_setup_modern_state_format(
6484 hass : HomeAssistant ,
6585 domain : str ,
6686 count : int ,
67- config : ConfigType ,
68- extra_config : ConfigType | None = None ,
87+ config : ConfigType | list [ ConfigType ] ,
88+ extra_section_config : ConfigType | None = None ,
6989) -> None :
7090 """Do setup of template integration via modern format."""
71- extra = extra_config or {}
7291 with assert_setup_component (count , template .DOMAIN ):
7392 assert await async_setup_component (
7493 hass ,
7594 template .DOMAIN ,
76- {"template" : {domain : config , ** extra }},
95+ {"template" : {domain : config , ** ( extra_section_config or {}) }},
7796 )
7897
7998 await hass .async_block_till_done ()
@@ -86,12 +105,11 @@ async def async_setup_modern_trigger_format(
86105 domain : str ,
87106 trigger : dict ,
88107 count : int ,
89- config : ConfigType ,
90- extra_config : ConfigType | None = None ,
108+ config : ConfigType | list [ ConfigType ] ,
109+ extra_section_config : ConfigType | None = None ,
91110) -> None :
92111 """Do setup of template integration via trigger format."""
93- extra = extra_config or {}
94- config = {"template" : {domain : config , ** trigger , ** extra }}
112+ config = {"template" : {domain : config , ** trigger , ** (extra_section_config or {})}}
95113
96114 with assert_setup_component (count , template .DOMAIN ):
97115 assert await async_setup_component (
@@ -105,6 +123,164 @@ async def async_setup_modern_trigger_format(
105123 await hass .async_block_till_done ()
106124
107125
126+ @dataclass (frozen = True )
127+ class TemplatePlatformSetup :
128+ """Template Platform Setup Information."""
129+
130+ domain : str
131+ legacy_slug : str | None
132+ object_id : str
133+ trigger : ConfigType
134+
135+ @property
136+ def entity_id (self ) -> str :
137+ """Return test entity ID."""
138+ return f"{ self .domain } .{ self .object_id } "
139+
140+
141+ async def setup_entity (
142+ hass : HomeAssistant ,
143+ platform_setup : TemplatePlatformSetup ,
144+ style : ConfigurationStyle ,
145+ count : int ,
146+ config : ConfigType ,
147+ state_template : str | None = None ,
148+ extra_config : ConfigType | None = None ,
149+ attributes : ConfigType | None = None ,
150+ extra_section_config : ConfigType | None = None ,
151+ ) -> None :
152+ """Do setup of a template entity based on the configuration style."""
153+ if style == ConfigurationStyle .LEGACY :
154+ await async_setup_legacy_platforms (
155+ hass ,
156+ platform_setup .domain ,
157+ platform_setup .legacy_slug ,
158+ count ,
159+ {
160+ platform_setup .object_id : {
161+ ** ({"value_template" : state_template } if state_template else {}),
162+ ** config ,
163+ ** (extra_config or {}),
164+ ** ({"attribute_templates" : attributes } if attributes else {}),
165+ }
166+ },
167+ )
168+ return
169+
170+ entity_config = {
171+ "name" : platform_setup .object_id ,
172+ ** ({"state" : state_template } if state_template else {}),
173+ ** config ,
174+ ** ({"attributes" : attributes } if attributes else {}),
175+ ** (extra_config or {}),
176+ }
177+ if style == ConfigurationStyle .MODERN :
178+ await async_setup_modern_state_format (
179+ hass , platform_setup .domain , count , entity_config , extra_section_config
180+ )
181+ elif style == ConfigurationStyle .TRIGGER :
182+ await async_setup_modern_trigger_format (
183+ hass ,
184+ platform_setup .domain ,
185+ platform_setup .trigger ,
186+ count ,
187+ entity_config ,
188+ extra_section_config ,
189+ )
190+
191+
192+ async def setup_and_test_unique_id (
193+ hass : HomeAssistant ,
194+ platform_setup : TemplatePlatformSetup ,
195+ style : ConfigurationStyle ,
196+ entity_config : ConfigType | None ,
197+ ) -> None :
198+ """Setup 2 entities with the same unique_id and verify only 1 entity is created.
199+
200+ The entity_config not provide name or unique_id, those are added automatically.
201+ """
202+ entity_config = {"unique_id" : "not-so_-unique-anymore" , ** (entity_config or {})}
203+ if style == ConfigurationStyle .LEGACY :
204+ if platform_setup .legacy_slug is None :
205+ config = [
206+ {"name" : "template_entity_1" , ** entity_config },
207+ {"name" : "template_entity_2" , ** entity_config },
208+ ]
209+ else :
210+ config = {
211+ "template_entity_1" : entity_config ,
212+ "template_entity_2" : entity_config ,
213+ }
214+ await async_setup_legacy_platforms (
215+ hass , platform_setup .domain , platform_setup .legacy_slug , 1 , config
216+ )
217+ elif style == ConfigurationStyle .MODERN :
218+ await async_setup_modern_state_format (
219+ hass ,
220+ platform_setup .domain ,
221+ 1 ,
222+ [
223+ {"name" : "template_entity_1" , ** entity_config },
224+ {"name" : "template_entity_2" , ** entity_config },
225+ ],
226+ )
227+ elif style == ConfigurationStyle .TRIGGER :
228+ await async_setup_modern_trigger_format (
229+ hass ,
230+ platform_setup .domain ,
231+ platform_setup .trigger ,
232+ 1 ,
233+ [
234+ {"name" : "template_entity_1" , ** entity_config },
235+ {"name" : "template_entity_2" , ** entity_config },
236+ ],
237+ )
238+
239+ assert len (hass .states .async_all (platform_setup .domain )) == 1
240+
241+
242+ async def setup_and_test_nested_unique_id (
243+ hass : HomeAssistant ,
244+ platform_setup : TemplatePlatformSetup ,
245+ style : ConfigurationStyle ,
246+ entity_registry : er .EntityRegistry ,
247+ entity_config : ConfigType | None ,
248+ ) -> None :
249+ """Setup 2 entities with unique unique_ids in a template section that contains a unique_id.
250+
251+ The test will verify that 2 entities are created where the unique_id appends the
252+ section unique_id to each entity unique_id.
253+
254+ The entity_config should not provide name or unique_id, those are added automatically.
255+ """
256+ entities = [
257+ {"name" : "test_a" , "unique_id" : "a" , ** (entity_config or {})},
258+ {"name" : "test_b" , "unique_id" : "b" , ** (entity_config or {})},
259+ ]
260+ extra_section_config = {"unique_id" : "x" }
261+ if style == ConfigurationStyle .MODERN :
262+ await async_setup_modern_state_format (
263+ hass , platform_setup .domain , 1 , entities , extra_section_config
264+ )
265+ elif style == ConfigurationStyle .TRIGGER :
266+ await async_setup_modern_trigger_format (
267+ hass ,
268+ platform_setup .domain ,
269+ platform_setup .trigger ,
270+ 1 ,
271+ entities ,
272+ extra_section_config ,
273+ )
274+
275+ assert len (hass .states .async_all (platform_setup .domain )) == 2
276+
277+ entry = entity_registry .async_get (f"{ platform_setup .domain } .test_a" )
278+ assert entry .unique_id == "x-a"
279+
280+ entry = entity_registry .async_get (f"{ platform_setup .domain } .test_b" )
281+ assert entry .unique_id == "x-b"
282+
283+
108284@pytest .fixture
109285def calls (hass : HomeAssistant ) -> list [ServiceCall ]:
110286 """Track calls to a mock service."""
0 commit comments