@@ -469,7 +469,7 @@ def is_template_string(value: str) -> bool:
469
469
470
470
471
471
async def async_extract_entities_from_template_string (
472
- template_str : str ,
472
+ hass : HomeAssistant , template_str : str
473
473
) -> set [str ]:
474
474
"""Extract entity IDs from a template string using regex analysis.
475
475
@@ -483,7 +483,7 @@ async def async_extract_entities_from_template_string(
483
483
484
484
# Use regex patterns to find entities
485
485
try :
486
- regex_entities = extract_entities_from_template_regex (template_str )
486
+ regex_entities = extract_entities_from_template_regex (hass , template_str )
487
487
entities .update (regex_entities )
488
488
# pylint: disable-next=broad-exception-caught
489
489
except Exception as exc : # noqa: BLE001 - Keep broad for unexpected regex issues
@@ -496,7 +496,9 @@ async def async_extract_entities_from_template_string(
496
496
return entities
497
497
498
498
499
- def extract_entities_from_template_regex (template_str : str ) -> set [str ]:
499
+ def extract_entities_from_template_regex (
500
+ hass : HomeAssistant , template_str : str
501
+ ) -> set [str ]:
500
502
"""Extract entity IDs from template string using regex patterns.
501
503
502
504
This function uses regex patterns based on Home Assistant's core validation
@@ -525,19 +527,26 @@ def extract_entities_from_template_regex(template_str: str) -> set[str]:
525
527
if valid_entity_id (individual_id ):
526
528
entities .add (individual_id )
527
529
528
- return entities
530
+ # Filter out known services to avoid false positives
531
+ known_services = async_get_all_services (hass )
532
+ return entities - known_services
529
533
530
534
531
535
async def _process_template_object (
532
- template : Template , known_entity_ids : set [str ], unknown_entities : set [str ]
536
+ hass : HomeAssistant ,
537
+ template : Template ,
538
+ known_entity_ids : set [str ],
539
+ unknown_entities : set [str ],
533
540
) -> None :
534
541
"""Process a Template object and add unknown entities to the set."""
535
542
template_entities = set ()
536
543
537
544
# Use regex patterns on the template string
538
545
try :
539
546
if hasattr (template , "template" ) and template .template :
540
- regex_entities = extract_entities_from_template_regex (template .template )
547
+ regex_entities = extract_entities_from_template_regex (
548
+ hass , template .template
549
+ )
541
550
template_entities .update (regex_entities )
542
551
# pylint: disable-next=broad-exception-caught
543
552
except Exception : # noqa: BLE001
@@ -550,12 +559,15 @@ async def _process_template_object(
550
559
551
560
552
561
async def _process_template_string (
562
+ hass : HomeAssistant ,
553
563
template_str : str ,
554
564
known_entity_ids : set [str ],
555
565
unknown_entities : set [str ],
556
566
) -> None :
557
567
"""Process a template string and add unknown entities to the set."""
558
- template_entities = await async_extract_entities_from_template_string (template_str )
568
+ template_entities = await async_extract_entities_from_template_string (
569
+ hass , template_str
570
+ )
559
571
# Check if any of the template entities are unknown
560
572
for template_entity in template_entities :
561
573
# Handle comma-separated entity lists
@@ -587,7 +599,7 @@ async def async_filter_known_entity_ids_with_templates(
587
599
# Handle Template objects
588
600
if isinstance (entity_id_raw , Template ):
589
601
await _process_template_object (
590
- entity_id_raw , known_entity_ids , unknown_entities
602
+ hass , entity_id_raw , known_entity_ids , unknown_entities
591
603
)
592
604
continue
593
605
@@ -597,7 +609,7 @@ async def async_filter_known_entity_ids_with_templates(
597
609
# Check if this looks like a template string
598
610
if is_template_string (entity_id_raw ):
599
611
await _process_template_string (
600
- entity_id_raw , known_entity_ids , unknown_entities
612
+ hass , entity_id_raw , known_entity_ids , unknown_entities
601
613
)
602
614
else :
603
615
# Process as regular entity ID(s), handling comma-separated lists
@@ -655,8 +667,10 @@ def extract_template_strings_from_config(
655
667
return strings
656
668
657
669
658
- async def async_extract_entities_from_config (config : Any ) -> set [str ]:
659
- """Extract all entity IDs referenced in templates within a configuration structure."""
670
+ async def async_extract_entities_from_config (
671
+ hass : HomeAssistant , config : Any
672
+ ) -> set [str ]:
673
+ """Extract entity IDs referenced in templates within a configuration structure."""
660
674
entities = set ()
661
675
if not config :
662
676
return entities
@@ -667,7 +681,7 @@ async def async_extract_entities_from_config(config: Any) -> set[str]:
667
681
# async_extract_entities_from_template_string already handles
668
682
# TemplateError and other exceptions internally, logging them.
669
683
referenced_entities = await async_extract_entities_from_template_string (
670
- template_str
684
+ hass , template_str
671
685
)
672
686
entities .update (referenced_entities )
673
687
# pylint: disable-next=broad-exception-caught
0 commit comments