11"""Helpers for template integration."""
22
33from collections .abc import Callable
4+ from enum import Enum
5+ import hashlib
46import itertools
57import logging
68from typing import Any
2224)
2325from homeassistant .core import HomeAssistant , callback
2426from homeassistant .exceptions import PlatformNotReady
25- from homeassistant .helpers import template
27+ from homeassistant .helpers import issue_registry as ir , template
2628from homeassistant .helpers .entity import Entity
2729from homeassistant .helpers .entity_platform import (
2830 AddConfigEntryEntitiesCallback ,
2931 AddEntitiesCallback ,
3032 async_get_platforms ,
3133)
34+ from homeassistant .helpers .issue_registry import IssueSeverity
3235from homeassistant .helpers .singleton import singleton
3336from homeassistant .helpers .typing import ConfigType , DiscoveryInfoType
37+ from homeassistant .util import yaml as yaml_util
38+ from homeassistant .util .hass_dict import HassKey
3439
3540from .const import (
3641 CONF_ADVANCED_OPTIONS ,
4651from .template_entity import TemplateEntity
4752from .trigger_entity import TriggerEntity
4853
54+ LEGACY_TEMPLATE_DEPRECATION_KEY = "deprecate_legacy_templates"
55+
4956DATA_BLUEPRINTS = "template_blueprints"
57+ DATA_DEPRECATION : HassKey [list [str ]] = HassKey (LEGACY_TEMPLATE_DEPRECATION_KEY )
5058
5159LEGACY_FIELDS = {
5260 CONF_ICON_TEMPLATE : CONF_ICON ,
@@ -180,6 +188,95 @@ def async_create_template_tracking_entities(
180188 async_add_entities (entities )
181189
182190
191+ def _format_template (value : Any ) -> Any :
192+ if isinstance (value , template .Template ):
193+ return value .template
194+
195+ if isinstance (value , Enum ):
196+ return value .name
197+
198+ if isinstance (value , (int , float , str , bool )):
199+ return value
200+
201+ return str (value )
202+
203+
204+ def format_migration_config (
205+ config : ConfigType | list [ConfigType ], depth : int = 0
206+ ) -> ConfigType | list [ConfigType ]:
207+ """Recursive method to format templates as strings from ConfigType."""
208+ types = (dict , list )
209+ if depth > 9 :
210+ raise RecursionError
211+
212+ if isinstance (config , list ):
213+ items = []
214+ for item in config :
215+ if isinstance (item , types ):
216+ if len (item ) > 0 :
217+ items .append (format_migration_config (item , depth + 1 ))
218+ else :
219+ items .append (_format_template (item ))
220+ return items # type: ignore[return-value]
221+
222+ formatted_config = {}
223+ for field , value in config .items ():
224+ if isinstance (value , types ):
225+ if len (value ) > 0 :
226+ formatted_config [field ] = format_migration_config (value , depth + 1 )
227+ else :
228+ formatted_config [field ] = _format_template (value )
229+
230+ return formatted_config
231+
232+
233+ def create_legacy_template_issue (
234+ hass : HomeAssistant , config : ConfigType , domain : str
235+ ) -> None :
236+ """Create a repair for legacy template entities."""
237+
238+ breadcrumb = "Template Entity"
239+ # Default entity id should be in most legacy configuration because
240+ # it's created from the legacy slug. Vacuum and Lock do not have a
241+ # slug, therefore we need to use the name or unique_id.
242+ if (default_entity_id := config .get (CONF_DEFAULT_ENTITY_ID )) is not None :
243+ breadcrumb = default_entity_id .split ("." )[- 1 ]
244+ elif (unique_id := config .get (CONF_UNIQUE_ID )) is not None :
245+ breadcrumb = f"unique_id: { unique_id } "
246+ elif (name := config .get (CONF_NAME )) and isinstance (name , template .Template ):
247+ breadcrumb = name .template
248+
249+ issue_id = f"{ LEGACY_TEMPLATE_DEPRECATION_KEY } _{ domain } _{ breadcrumb } _{ hashlib .md5 (',' .join (config .keys ()).encode ()).hexdigest ()} "
250+
251+ if (deprecation_list := hass .data .get (DATA_DEPRECATION )) is None :
252+ hass .data [DATA_DEPRECATION ] = deprecation_list = []
253+
254+ deprecation_list .append (issue_id )
255+
256+ try :
257+ modified_yaml = format_migration_config (config )
258+ yaml_config = yaml_util .dump ({DOMAIN : [{domain : [modified_yaml ]}]})
259+ # Format to show up properly in a numbered bullet on the repair.
260+ yaml_config = " ```\n " + yaml_config .replace ("\n " , "\n " ) + "```"
261+ except RecursionError :
262+ yaml_config = f"{ DOMAIN } :\n - { domain } : - ..."
263+
264+ ir .async_create_issue (
265+ hass ,
266+ DOMAIN ,
267+ issue_id ,
268+ breaks_in_ha_version = "2026.6" ,
269+ is_fixable = False ,
270+ severity = IssueSeverity .WARNING ,
271+ translation_key = "deprecated_legacy_templates" ,
272+ translation_placeholders = {
273+ "domain" : domain ,
274+ "breadcrumb" : breadcrumb ,
275+ "config" : yaml_config ,
276+ },
277+ )
278+
279+
183280async def async_setup_template_platform (
184281 hass : HomeAssistant ,
185282 domain : str ,
@@ -201,6 +298,10 @@ async def async_setup_template_platform(
201298 )
202299 else :
203300 configs = [rewrite_legacy_to_modern_config (hass , config , legacy_fields )]
301+
302+ for definition in configs :
303+ create_legacy_template_issue (hass , definition , domain )
304+
204305 async_create_template_tracking_entities (
205306 state_entity_cls ,
206307 async_add_entities ,
0 commit comments