Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 24 additions & 9 deletions homeassistant/helpers/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,15 +332,6 @@ def async_prepare_call_from_config(
target.update(conf.async_render(variables))
else:
target.update(template.render_complex(conf, variables))

if CONF_ENTITY_ID in target:
registry = entity_registry.async_get(hass)
entity_ids = cv.comp_entity_ids_or_uuids(target[CONF_ENTITY_ID])
if entity_ids not in (ENTITY_MATCH_ALL, ENTITY_MATCH_NONE):
entity_ids = entity_registry.async_validate_entity_ids(
registry, entity_ids
)
target[CONF_ENTITY_ID] = entity_ids
except TemplateError as ex:
raise HomeAssistantError(
f"Error rendering service target template: {ex}"
Expand All @@ -350,6 +341,30 @@ def async_prepare_call_from_config(
f"Template rendered invalid entity IDs: {target[CONF_ENTITY_ID]}"
) from ex

# All config entries but CONF_ENTITY_ID use ensure_list.
# Using ensure_list transforms a template value to a singleton list
# Since we expect a list of strings, a template that evaluates to
# a list will lead to a nested list.
# We cannot easily fix this in schema without breaking old configs.
# This block flattens nested lists but keeps non-list members as is.
for entry, value in target.items():
if entry == CONF_ENTITY_ID:
registry = entity_registry.async_get(hass)
entity_ids = cv.comp_entity_ids_or_uuids(value)
if entity_ids not in (ENTITY_MATCH_ALL, ENTITY_MATCH_NONE):
entity_ids = entity_registry.async_validate_entity_ids(
registry, entity_ids
)
target[entry] = entity_ids
else:
ret = []
for elem in value:
if isinstance(elem, list):
ret.extend(elem)
else:
ret.append(elem)
target[entry] = ret

service_data = {}

for conf in (CONF_SERVICE_DATA, CONF_SERVICE_DATA_TEMPLATE):
Expand Down
20 changes: 20 additions & 0 deletions tests/helpers/test_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,26 @@ async def test_service_call(hass: HomeAssistant) -> None:
"entity_id": ["light.static"],
}

config = {
"action": "{{ 'test_domain.test_service' }}",
"target": {
"area_id": ["area-42", "{{ 'area-51' }}"],
"device_id": "{{ ['abcdef', 'fedcba'] }}",
"entity_id": ["light.static", "{{ 'light.dynamic' }}"],
"floor_id": ["floor-first", "{{ 'floor-second' }}"],
},
}

await service.async_call_from_config(hass, config)
await hass.async_block_till_done()

assert dict(calls[3].data) == {
"area_id": ["area-42", "area-51"],
"device_id": ["abcdef", "fedcba"],
"entity_id": ["light.static", "light.dynamic"],
"floor_id": ["floor-first", "floor-second"],
}


async def test_service_template_service_call(hass: HomeAssistant) -> None:
"""Test legacy service_template call with templating."""
Expand Down