Skip to content
Merged
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
7 changes: 1 addition & 6 deletions homeassistant/components/websocket_api/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,11 +260,6 @@ async def handle_call_service(
hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any]
) -> None:
"""Handle call service command."""
# We do not support templates.
target = msg.get("target")
if template.is_complex(target):
raise vol.Invalid("Templates are not supported here")

try:
context = connection.context(msg)
response = await hass.services.async_call(
Expand All @@ -273,7 +268,7 @@ async def handle_call_service(
service_data=msg.get("service_data"),
blocking=True,
context=context,
target=target,
target=msg.get("target"),
return_response=msg["return_response"],
)
result: dict[str, Context | ServiceResponse] = {"context": context}
Expand Down
47 changes: 29 additions & 18 deletions homeassistant/helpers/config_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,7 @@ def temperature_unit(value: Any) -> UnitOfTemperature:
raise vol.Invalid("invalid temperature unit (expected C or F)")


def template(value: Any | None) -> template_helper.Template:
def template(value: Any) -> template_helper.Template:
"""Validate a jinja2 template."""
if value is None:
raise vol.Invalid("template value is None")
Expand All @@ -750,7 +750,7 @@ def template(value: Any | None) -> template_helper.Template:
return template_value


def dynamic_template(value: Any | None) -> template_helper.Template:
def dynamic_template(value: Any) -> template_helper.Template:
"""Validate a dynamic (non static) jinja2 template."""
if value is None:
raise vol.Invalid("template value is None")
Expand Down Expand Up @@ -1319,34 +1319,44 @@ def platform_only_config_schema(domain: str) -> Callable[[dict], dict]:
}

ENTITY_SERVICE_FIELDS: VolDictType = {
# 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_ENTITY_ID): comp_entity_ids,
vol.Optional(ATTR_DEVICE_ID): vol.Any(
ENTITY_MATCH_NONE, vol.All(ensure_list, [vol.Any(dynamic_template, str)])
ENTITY_MATCH_NONE,
vol.All(ensure_list, [str]),
),
vol.Optional(ATTR_AREA_ID): vol.Any(
ENTITY_MATCH_NONE, vol.All(ensure_list, [vol.Any(dynamic_template, str)])
ENTITY_MATCH_NONE,
vol.All(ensure_list, [str]),
),
vol.Optional(ATTR_FLOOR_ID): vol.Any(
ENTITY_MATCH_NONE, vol.All(ensure_list, [vol.Any(dynamic_template, str)])
ENTITY_MATCH_NONE,
vol.All(ensure_list, [str]),
),
vol.Optional(ATTR_LABEL_ID): vol.Any(
ENTITY_MATCH_NONE, vol.All(ensure_list, [vol.Any(dynamic_template, str)])
ENTITY_MATCH_NONE,
vol.All(ensure_list, [str]),
),
}

TARGET_SERVICE_FIELDS = {
# Same as ENTITY_SERVICE_FIELDS but supports specifying entity by entity registry
# ID.
TARGET_SERVICE_FIELDS: VolDictType = {
Comment thread
arturpragacz marked this conversation as resolved.
# Same as ENTITY_SERVICE_FIELDS but supports specifying entity
# by entity registry ID.
**ENTITY_SERVICE_FIELDS,
vol.Optional(ATTR_ENTITY_ID): comp_entity_ids_or_uuids,
}

_TARGET_SERVICE_FIELDS_TEMPLATED: VolDictType = {
# 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.
# Entity ID can be specified as either a user visible one or by entity registry ID.
#
# The schema supports templates as it is meant to be used in the initial validation
# before templates are automatically rendered by the core logic.
vol.Optional(ATTR_ENTITY_ID): vol.Any(
comp_entity_ids_or_uuids, dynamic_template, vol.All(list, template_complex)
comp_entity_ids_or_uuids,
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)])
Expand All @@ -1362,7 +1372,6 @@ def platform_only_config_schema(domain: str) -> Callable[[dict], dict]:
),
}


_HAS_ENTITY_SERVICE_FIELD = has_at_least_one_key(*ENTITY_SERVICE_FIELDS)


Expand Down Expand Up @@ -1494,7 +1503,9 @@ def _backward_compat_service_schema(value: Any | None) -> Any:
template, vol.All(dict, template_complex)
),
vol.Optional(CONF_ENTITY_ID): comp_entity_ids,
vol.Optional(CONF_TARGET): vol.Any(TARGET_SERVICE_FIELDS, dynamic_template),
vol.Optional(CONF_TARGET): vol.Any(
_TARGET_SERVICE_FIELDS_TEMPLATED, dynamic_template
),
vol.Optional(CONF_RESPONSE_VARIABLE): str,
# The frontend stores data here. Don't use in core.
vol.Remove("metadata"): dict,
Expand Down
3 changes: 2 additions & 1 deletion homeassistant/helpers/selector.py
Original file line number Diff line number Diff line change
Expand Up @@ -1463,7 +1463,8 @@ class TargetSelector(Selector[TargetSelectorConfig]):
}
)

TARGET_SELECTION_SCHEMA = vol.Schema(cv.TARGET_SERVICE_FIELDS)
# We want to transition to not including templates in the target selector.
TARGET_SELECTION_SCHEMA = vol.Schema(cv._TARGET_SERVICE_FIELDS_TEMPLATED) # noqa: SLF001

def __init__(self, config: TargetSelectorConfig | None = None) -> None:
"""Instantiate a selector."""
Expand Down
Loading