diff --git a/homeassistant/helpers/config_validation.py b/homeassistant/helpers/config_validation.py index 6b0737ae346..422f940e98e 100644 --- a/homeassistant/helpers/config_validation.py +++ b/homeassistant/helpers/config_validation.py @@ -847,11 +847,18 @@ PLATFORM_SCHEMA = vol.Schema( PLATFORM_SCHEMA_BASE = PLATFORM_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA) ENTITY_SERVICE_FIELDS = { - vol.Optional(ATTR_ENTITY_ID): comp_entity_ids, - vol.Optional(ATTR_DEVICE_ID): vol.Any( - ENTITY_MATCH_NONE, vol.All(ensure_list, [str]) + # Either accept static entity IDs, a single dynamic template or a mixed list + # of static and dynamic templates. While this could be solved with a single + # complex template, handling it like this, keeps config validation useful. + vol.Optional(ATTR_ENTITY_ID): vol.Any( + comp_entity_ids, dynamic_template, vol.All(list, template_complex) + ), + vol.Optional(ATTR_DEVICE_ID): vol.Any( + ENTITY_MATCH_NONE, vol.All(ensure_list, [vol.Any(dynamic_template, str)]) + ), + vol.Optional(ATTR_AREA_ID): vol.Any( + ENTITY_MATCH_NONE, vol.All(ensure_list, [vol.Any(dynamic_template, str)]) ), - vol.Optional(ATTR_AREA_ID): vol.Any(ENTITY_MATCH_NONE, vol.All(ensure_list, [str])), } diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index 7983190dbe8..932384493f3 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -28,6 +28,7 @@ from homeassistant.const import ( ATTR_AREA_ID, ATTR_DEVICE_ID, ATTR_ENTITY_ID, + CONF_ENTITY_ID, CONF_SERVICE, CONF_SERVICE_DATA, CONF_SERVICE_TEMPLATE, @@ -189,7 +190,22 @@ def async_prepare_call_from_config( domain, service = domain_service.split(".", 1) - target = config.get(CONF_TARGET) + target = {} + if CONF_TARGET in config: + conf = config.get(CONF_TARGET) + try: + template.attach(hass, conf) + target.update(template.render_complex(conf, variables)) + if CONF_ENTITY_ID in target: + target[CONF_ENTITY_ID] = cv.comp_entity_ids(target[CONF_ENTITY_ID]) + except TemplateError as ex: + raise HomeAssistantError( + f"Error rendering service target template: {ex}" + ) from ex + except vol.Invalid as ex: + raise HomeAssistantError( + f"Template rendered invalid entity IDs: {target[CONF_ENTITY_ID]}" + ) from ex service_data = {} diff --git a/tests/helpers/test_service.py b/tests/helpers/test_service.py index 95ccdc84395..92cbd5514e6 100644 --- a/tests/helpers/test_service.py +++ b/tests/helpers/test_service.py @@ -195,6 +195,24 @@ class TestServiceHelpers(unittest.TestCase): "area_id": ["test-area-id"], } + config = { + "service": "{{ 'test_domain.test_service' }}", + "target": { + "area_id": ["area-42", "{{ 'area-51' }}"], + "device_id": ["abcdef", "{{ 'fedcba' }}"], + "entity_id": ["light.static", "{{ 'light.dynamic' }}"], + }, + } + + service.call_from_config(self.hass, config) + self.hass.block_till_done() + + assert dict(self.calls[1].data) == { + "area_id": ["area-42", "area-51"], + "device_id": ["abcdef", "fedcba"], + "entity_id": ["light.static", "light.dynamic"], + } + def test_service_template_service_call(self): """Test legacy service_template call with templating.""" config = {