Skip to content

Commit b157afa

Browse files
authored
Remove templates from schemas for service fields validation (#150063)
1 parent edaf5c8 commit b157afa

File tree

3 files changed

+32
-25
lines changed

3 files changed

+32
-25
lines changed

homeassistant/components/websocket_api/commands.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -260,11 +260,6 @@ async def handle_call_service(
260260
hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any]
261261
) -> None:
262262
"""Handle call service command."""
263-
# We do not support templates.
264-
target = msg.get("target")
265-
if template.is_complex(target):
266-
raise vol.Invalid("Templates are not supported here")
267-
268263
try:
269264
context = connection.context(msg)
270265
response = await hass.services.async_call(
@@ -273,7 +268,7 @@ async def handle_call_service(
273268
service_data=msg.get("service_data"),
274269
blocking=True,
275270
context=context,
276-
target=target,
271+
target=msg.get("target"),
277272
return_response=msg["return_response"],
278273
)
279274
result: dict[str, Context | ServiceResponse] = {"context": context}

homeassistant/helpers/config_validation.py

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -732,7 +732,7 @@ def temperature_unit(value: Any) -> UnitOfTemperature:
732732
raise vol.Invalid("invalid temperature unit (expected C or F)")
733733

734734

735-
def template(value: Any | None) -> template_helper.Template:
735+
def template(value: Any) -> template_helper.Template:
736736
"""Validate a jinja2 template."""
737737
if value is None:
738738
raise vol.Invalid("template value is None")
@@ -750,7 +750,7 @@ def template(value: Any | None) -> template_helper.Template:
750750
return template_value
751751

752752

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

13211321
ENTITY_SERVICE_FIELDS: VolDictType = {
1322-
# Either accept static entity IDs, a single dynamic template or a mixed list
1323-
# of static and dynamic templates. While this could be solved with a single
1324-
# complex template, handling it like this, keeps config validation useful.
1325-
vol.Optional(ATTR_ENTITY_ID): vol.Any(
1326-
comp_entity_ids, dynamic_template, vol.All(list, template_complex)
1327-
),
1322+
vol.Optional(ATTR_ENTITY_ID): comp_entity_ids,
13281323
vol.Optional(ATTR_DEVICE_ID): vol.Any(
1329-
ENTITY_MATCH_NONE, vol.All(ensure_list, [vol.Any(dynamic_template, str)])
1324+
ENTITY_MATCH_NONE,
1325+
vol.All(ensure_list, [str]),
13301326
),
13311327
vol.Optional(ATTR_AREA_ID): vol.Any(
1332-
ENTITY_MATCH_NONE, vol.All(ensure_list, [vol.Any(dynamic_template, str)])
1328+
ENTITY_MATCH_NONE,
1329+
vol.All(ensure_list, [str]),
13331330
),
13341331
vol.Optional(ATTR_FLOOR_ID): vol.Any(
1335-
ENTITY_MATCH_NONE, vol.All(ensure_list, [vol.Any(dynamic_template, str)])
1332+
ENTITY_MATCH_NONE,
1333+
vol.All(ensure_list, [str]),
13361334
),
13371335
vol.Optional(ATTR_LABEL_ID): vol.Any(
1338-
ENTITY_MATCH_NONE, vol.All(ensure_list, [vol.Any(dynamic_template, str)])
1336+
ENTITY_MATCH_NONE,
1337+
vol.All(ensure_list, [str]),
13391338
),
13401339
}
13411340

1342-
TARGET_SERVICE_FIELDS = {
1343-
# Same as ENTITY_SERVICE_FIELDS but supports specifying entity by entity registry
1344-
# ID.
1341+
TARGET_SERVICE_FIELDS: VolDictType = {
1342+
# Same as ENTITY_SERVICE_FIELDS but supports specifying entity
1343+
# by entity registry ID.
1344+
**ENTITY_SERVICE_FIELDS,
1345+
vol.Optional(ATTR_ENTITY_ID): comp_entity_ids_or_uuids,
1346+
}
1347+
1348+
_TARGET_SERVICE_FIELDS_TEMPLATED: VolDictType = {
13451349
# Either accept static entity IDs, a single dynamic template or a mixed list
13461350
# of static and dynamic templates. While this could be solved with a single
13471351
# complex template, handling it like this, keeps config validation useful.
1352+
# Entity ID can be specified as either a user visible one or by entity registry ID.
1353+
#
1354+
# The schema supports templates as it is meant to be used in the initial validation
1355+
# before templates are automatically rendered by the core logic.
13481356
vol.Optional(ATTR_ENTITY_ID): vol.Any(
1349-
comp_entity_ids_or_uuids, dynamic_template, vol.All(list, template_complex)
1357+
comp_entity_ids_or_uuids,
1358+
dynamic_template,
1359+
vol.All(list, template_complex),
13501360
),
13511361
vol.Optional(ATTR_DEVICE_ID): vol.Any(
13521362
ENTITY_MATCH_NONE, vol.All(ensure_list, [vol.Any(dynamic_template, str)])
@@ -1362,7 +1372,6 @@ def platform_only_config_schema(domain: str) -> Callable[[dict], dict]:
13621372
),
13631373
}
13641374

1365-
13661375
_HAS_ENTITY_SERVICE_FIELD = has_at_least_one_key(*ENTITY_SERVICE_FIELDS)
13671376

13681377

@@ -1494,7 +1503,9 @@ def _backward_compat_service_schema(value: Any | None) -> Any:
14941503
template, vol.All(dict, template_complex)
14951504
),
14961505
vol.Optional(CONF_ENTITY_ID): comp_entity_ids,
1497-
vol.Optional(CONF_TARGET): vol.Any(TARGET_SERVICE_FIELDS, dynamic_template),
1506+
vol.Optional(CONF_TARGET): vol.Any(
1507+
_TARGET_SERVICE_FIELDS_TEMPLATED, dynamic_template
1508+
),
14981509
vol.Optional(CONF_RESPONSE_VARIABLE): str,
14991510
# The frontend stores data here. Don't use in core.
15001511
vol.Remove("metadata"): dict,

homeassistant/helpers/selector.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1463,7 +1463,8 @@ class TargetSelector(Selector[TargetSelectorConfig]):
14631463
}
14641464
)
14651465

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

14681469
def __init__(self, config: TargetSelectorConfig | None = None) -> None:
14691470
"""Instantiate a selector."""

0 commit comments

Comments
 (0)