Compare commits

...

1 Commits

Author SHA1 Message Date
abmantis
7011280517 Support target conditions in script relation extraction 2026-01-20 19:30:02 +00:00
4 changed files with 125 additions and 44 deletions

View File

@@ -604,7 +604,7 @@ class AutomationEntity(BaseAutomationEntity, RestoreEntity):
if self._cond_func is not None:
for conf in self._cond_func.config:
referenced |= condition.async_extract_labels(conf)
referenced |= condition.async_extract_targets(conf, ATTR_LABEL_ID)
for conf in self._trigger_config:
referenced |= set(_get_targets_from_trigger_config(conf, ATTR_LABEL_ID))
@@ -617,7 +617,7 @@ class AutomationEntity(BaseAutomationEntity, RestoreEntity):
if self._cond_func is not None:
for conf in self._cond_func.config:
referenced |= condition.async_extract_floors(conf)
referenced |= condition.async_extract_targets(conf, ATTR_FLOOR_ID)
for conf in self._trigger_config:
referenced |= set(_get_targets_from_trigger_config(conf, ATTR_FLOOR_ID))
@@ -630,7 +630,7 @@ class AutomationEntity(BaseAutomationEntity, RestoreEntity):
if self._cond_func is not None:
for conf in self._cond_func.config:
referenced |= condition.async_extract_areas(conf)
referenced |= condition.async_extract_targets(conf, ATTR_AREA_ID)
for conf in self._trigger_config:
referenced |= set(_get_targets_from_trigger_config(conf, ATTR_AREA_ID))

View File

@@ -29,10 +29,7 @@ from typing import (
import voluptuous as vol
from homeassistant.const import (
ATTR_AREA_ID,
ATTR_DEVICE_CLASS,
ATTR_FLOOR_ID,
ATTR_LABEL_ID,
CONF_ABOVE,
CONF_AFTER,
CONF_ATTRIBUTE,
@@ -1387,27 +1384,9 @@ def async_extract_devices(config: ConfigType | Template) -> set[str]:
@callback
def async_extract_areas(config: ConfigType | Template) -> set[str]:
"""Extract areas from a condition."""
return _async_extract_targets(config, ATTR_AREA_ID)
@callback
def async_extract_floors(config: ConfigType | Template) -> set[str]:
"""Extract floors from a condition."""
return _async_extract_targets(config, ATTR_FLOOR_ID)
@callback
def async_extract_labels(config: ConfigType | Template) -> set[str]:
"""Extract labels from a condition."""
return _async_extract_targets(config, ATTR_LABEL_ID)
@callback
def _async_extract_targets(
def async_extract_targets(
config: ConfigType | Template,
target_type: Literal["entity_id", "device_id", "area_id", "floor_id", "label_id"],
target_type: Literal["area_id", "floor_id", "label_id"],
) -> set[str]:
"""Extract targets from a condition."""
referenced: set[str] = set()

View File

@@ -1601,8 +1601,13 @@ class Script:
):
_referenced_extract_ids(data, target, referenced)
elif action == cv.SCRIPT_ACTION_CHECK_CONDITION:
referenced |= condition.async_extract_targets(step, target)
elif action == cv.SCRIPT_ACTION_CHOOSE:
for choice in step[CONF_CHOOSE]:
for cond in choice[CONF_CONDITIONS]:
referenced |= condition.async_extract_targets(cond, target)
Script._find_referenced_target(
target, referenced, choice[CONF_SEQUENCE]
)
@@ -1612,6 +1617,8 @@ class Script:
)
elif action == cv.SCRIPT_ACTION_IF:
for cond in step[CONF_IF]:
referenced |= condition.async_extract_targets(cond, target)
Script._find_referenced_target(target, referenced, step[CONF_THEN])
if CONF_ELSE in step:
Script._find_referenced_target(target, referenced, step[CONF_ELSE])

View File

@@ -4209,6 +4209,16 @@ async def test_referenced_labels(hass: HomeAssistant) -> None:
"data_template": {"label_id": "label_in_data_template"},
},
{"action": "test.script", "data": {"without": "label_id"}},
{
"condition": "light.is_on",
"target": {"label_id": "label_condition_target"},
},
{
"condition": "light.is_on",
"target": {
"label_id": ["label_condition_list_1", "label_condition_list_2"]
},
},
{
"choose": [
{
@@ -4221,7 +4231,10 @@ async def test_referenced_labels(hass: HomeAssistant) -> None:
],
},
{
"conditions": "{{ true == false }}",
"conditions": {
"condition": "light.is_on",
"target": {"label_id": "label_choice_2_cond"},
},
"sequence": [
{
"action": "test.script",
@@ -4240,7 +4253,10 @@ async def test_referenced_labels(hass: HomeAssistant) -> None:
{"event": "test_event"},
{"delay": "{{ delay_period }}"},
{
"if": [],
"if": {
"condition": "light.is_on",
"target": {"label_id": "label_if_cond"},
},
"then": [
{
"action": "test.script",
@@ -4277,17 +4293,22 @@ async def test_referenced_labels(hass: HomeAssistant) -> None:
)
assert script_obj.referenced_labels == {
"label_choice_1_seq",
"label_choice_2_cond",
"label_choice_2_seq",
"label_condition_list_1",
"label_condition_list_2",
"label_condition_target",
"label_default_seq",
"label_if_cond",
"label_if_else",
"label_if_then",
"label_in_data_template",
"label_in_target",
"label_parallel",
"label_sequence",
"label_service_list_1",
"label_service_list_2",
"label_service_not_list",
"label_if_then",
"label_if_else",
"label_parallel",
"label_sequence",
}
# Test we cache results.
assert script_obj.referenced_labels is script_obj.referenced_labels
@@ -4320,6 +4341,16 @@ async def test_referenced_floors(hass: HomeAssistant) -> None:
"data_template": {"floor_id": "floor_in_data_template"},
},
{"action": "test.script", "data": {"without": "floor_id"}},
{
"condition": "light.is_on",
"target": {"floor_id": "floor_condition_target"},
},
{
"condition": "light.is_on",
"target": {
"floor_id": ["floor_condition_list_1", "floor_condition_list_2"]
},
},
{
"choose": [
{
@@ -4332,7 +4363,10 @@ async def test_referenced_floors(hass: HomeAssistant) -> None:
],
},
{
"conditions": "{{ true == false }}",
"conditions": {
"condition": "light.is_on",
"target": {"floor_id": "floor_choice_2_cond"},
},
"sequence": [
{
"action": "test.script",
@@ -4351,7 +4385,10 @@ async def test_referenced_floors(hass: HomeAssistant) -> None:
{"event": "test_event"},
{"delay": "{{ delay_period }}"},
{
"if": [],
"if": {
"condition": "light.is_on",
"target": {"floor_id": "floor_if_cond"},
},
"then": [
{
"action": "test.script",
@@ -4388,16 +4425,21 @@ async def test_referenced_floors(hass: HomeAssistant) -> None:
)
assert script_obj.referenced_floors == {
"floor_choice_1_seq",
"floor_choice_2_cond",
"floor_choice_2_seq",
"floor_condition_list_1",
"floor_condition_list_2",
"floor_condition_target",
"floor_default_seq",
"floor_if_cond",
"floor_if_else",
"floor_if_then",
"floor_in_data_template",
"floor_in_target",
"floor_service_list",
"floor_service_not_list",
"floor_if_then",
"floor_if_else",
"floor_parallel",
"floor_sequence",
"floor_service_list",
"floor_service_not_list",
}
# Test we cache results.
assert script_obj.referenced_floors is script_obj.referenced_floors
@@ -4430,6 +4472,16 @@ async def test_referenced_areas(hass: HomeAssistant) -> None:
"data_template": {"area_id": "area_in_data_template"},
},
{"action": "test.script", "data": {"without": "area_id"}},
{
"condition": "light.is_on",
"target": {"area_id": "area_condition_target"},
},
{
"condition": "light.is_on",
"target": {
"area_id": ["area_condition_list_1", "area_condition_list_2"]
},
},
{
"choose": [
{
@@ -4442,7 +4494,10 @@ async def test_referenced_areas(hass: HomeAssistant) -> None:
],
},
{
"conditions": "{{ true == false }}",
"conditions": {
"condition": "light.is_on",
"target": {"area_id": "area_choice_2_cond"},
},
"sequence": [
{
"action": "test.script",
@@ -4461,7 +4516,10 @@ async def test_referenced_areas(hass: HomeAssistant) -> None:
{"event": "test_event"},
{"delay": "{{ delay_period }}"},
{
"if": [],
"if": {
"condition": "light.is_on",
"target": {"area_id": "area_if_cond"},
},
"then": [
{
"action": "test.script",
@@ -4498,16 +4556,21 @@ async def test_referenced_areas(hass: HomeAssistant) -> None:
)
assert script_obj.referenced_areas == {
"area_choice_1_seq",
"area_choice_2_cond",
"area_choice_2_seq",
"area_condition_list_1",
"area_condition_list_2",
"area_condition_target",
"area_default_seq",
"area_if_cond",
"area_if_else",
"area_if_then",
"area_in_data_template",
"area_in_target",
"area_service_list",
"area_service_not_list",
"area_if_then",
"area_if_else",
"area_parallel",
"area_sequence",
"area_service_list",
"area_service_not_list",
# 'area_service_template', # no area extraction from template
}
# Test we cache results.
@@ -4608,6 +4671,19 @@ async def test_referenced_entities(hass: HomeAssistant) -> None:
}
],
},
{
"condition": "light.is_on",
"target": {"entity_id": "light.condition_target"},
},
{
"condition": "light.is_on",
"target": {
"entity_id": [
"light.condition_list_1",
"light.condition_list_2",
]
},
},
{
"sequence": [
{
@@ -4626,6 +4702,9 @@ async def test_referenced_entities(hass: HomeAssistant) -> None:
"light.choice_1_seq",
"light.choice_2_cond",
"light.choice_2_seq",
"light.condition_list_1",
"light.condition_list_2",
"light.condition_target",
"light.default_seq",
"light.direct_entity_referenced",
"light.entity_in_data_template",
@@ -4656,6 +4735,19 @@ async def test_referenced_devices(hass: HomeAssistant) -> None:
"device_id": "condition-dev-id",
"domain": "switch",
},
{
"condition": "light.is_on",
"target": {"device_id": "condition-target-dev-id"},
},
{
"condition": "light.is_on",
"target": {
"device_id": [
"condition-target-list-1",
"condition-target-list-2",
]
},
},
{
"action": "test.script",
"data": {"device_id": "data-string-id"},
@@ -4753,6 +4845,9 @@ async def test_referenced_devices(hass: HomeAssistant) -> None:
"choice-2-cond-dev-id",
"choice-2-seq-device-target",
"condition-dev-id",
"condition-target-dev-id",
"condition-target-list-1",
"condition-target-list-2",
"data-string-id",
"data-template-string-id",
"default-device-target",