mirror of
https://github.com/home-assistant/core.git
synced 2025-07-24 13:47:35 +00:00
Teach call service script action about entity registry ids (#61172)
This commit is contained in:
parent
d5d8eefded
commit
fc2025509e
@ -311,6 +311,12 @@ comp_entity_ids = vol.Any(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
comp_entity_ids_or_uuids = vol.Any(
|
||||||
|
vol.All(vol.Lower, vol.Any(ENTITY_MATCH_ALL, ENTITY_MATCH_NONE)),
|
||||||
|
entity_ids_or_uuids,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def entity_domain(domain: str | list[str]) -> Callable[[Any], str]:
|
def entity_domain(domain: str | list[str]) -> Callable[[Any], str]:
|
||||||
"""Validate that entity belong to domain."""
|
"""Validate that entity belong to domain."""
|
||||||
ent_domain = entities_domain(domain)
|
ent_domain = entities_domain(domain)
|
||||||
@ -972,6 +978,23 @@ ENTITY_SERVICE_FIELDS = {
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TARGET_SERVICE_FIELDS = {
|
||||||
|
# Same as ENTITY_SERVICE_FIELDS but supports specifying entity by entity registry
|
||||||
|
# ID.
|
||||||
|
# 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_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)])
|
||||||
|
),
|
||||||
|
vol.Optional(ATTR_AREA_ID): vol.Any(
|
||||||
|
ENTITY_MATCH_NONE, vol.All(ensure_list, [vol.Any(dynamic_template, str)])
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def make_entity_service_schema(
|
def make_entity_service_schema(
|
||||||
schema: dict, *, extra: int = vol.PREVENT_EXTRA
|
schema: dict, *, extra: int = vol.PREVENT_EXTRA
|
||||||
@ -1034,7 +1057,7 @@ SERVICE_SCHEMA = vol.All(
|
|||||||
template, vol.All(dict, template_complex)
|
template, vol.All(dict, template_complex)
|
||||||
),
|
),
|
||||||
vol.Optional(CONF_ENTITY_ID): comp_entity_ids,
|
vol.Optional(CONF_ENTITY_ID): comp_entity_ids,
|
||||||
vol.Optional(CONF_TARGET): vol.Any(ENTITY_SERVICE_FIELDS, dynamic_template),
|
vol.Optional(CONF_TARGET): vol.Any(TARGET_SERVICE_FIELDS, dynamic_template),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
has_at_least_one_key(CONF_SERVICE, CONF_SERVICE_TEMPLATE),
|
has_at_least_one_key(CONF_SERVICE, CONF_SERVICE_TEMPLATE),
|
||||||
|
@ -218,7 +218,10 @@ def async_prepare_call_from_config(
|
|||||||
target.update(template.render_complex(conf, variables))
|
target.update(template.render_complex(conf, variables))
|
||||||
|
|
||||||
if CONF_ENTITY_ID in target:
|
if CONF_ENTITY_ID in target:
|
||||||
target[CONF_ENTITY_ID] = cv.comp_entity_ids(target[CONF_ENTITY_ID])
|
registry = entity_registry.async_get(hass)
|
||||||
|
target[CONF_ENTITY_ID] = entity_registry.async_resolve_entity_ids(
|
||||||
|
registry, cv.comp_entity_ids_or_uuids(target[CONF_ENTITY_ID])
|
||||||
|
)
|
||||||
except TemplateError as ex:
|
except TemplateError as ex:
|
||||||
raise HomeAssistantError(
|
raise HomeAssistantError(
|
||||||
f"Error rendering service target template: {ex}"
|
f"Error rendering service target template: {ex}"
|
||||||
|
@ -29,6 +29,7 @@ from homeassistant.setup import async_setup_component
|
|||||||
|
|
||||||
from tests.common import (
|
from tests.common import (
|
||||||
MockEntity,
|
MockEntity,
|
||||||
|
async_mock_service,
|
||||||
get_test_home_assistant,
|
get_test_home_assistant,
|
||||||
mock_device_registry,
|
mock_device_registry,
|
||||||
mock_registry,
|
mock_registry,
|
||||||
@ -375,6 +376,27 @@ class TestServiceHelpers(unittest.TestCase):
|
|||||||
assert mock_log.call_count == 3
|
assert mock_log.call_count == 3
|
||||||
|
|
||||||
|
|
||||||
|
async def test_service_call_entry_id(hass):
|
||||||
|
"""Test service call with entity specified by entity registry ID."""
|
||||||
|
registry = ent_reg.async_get(hass)
|
||||||
|
calls = async_mock_service(hass, "test_domain", "test_service")
|
||||||
|
entry = registry.async_get_or_create(
|
||||||
|
"hello", "hue", "1234", suggested_object_id="world"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert entry.entity_id == "hello.world"
|
||||||
|
|
||||||
|
config = {
|
||||||
|
"service": "test_domain.test_service",
|
||||||
|
"target": {"entity_id": entry.id},
|
||||||
|
}
|
||||||
|
|
||||||
|
await service.async_call_from_config(hass, config)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert dict(calls[0].data) == {"entity_id": ["hello.world"]}
|
||||||
|
|
||||||
|
|
||||||
async def test_extract_entity_ids(hass):
|
async def test_extract_entity_ids(hass):
|
||||||
"""Test extract_entity_ids method."""
|
"""Test extract_entity_ids method."""
|
||||||
hass.states.async_set("light.Bowl", STATE_ON)
|
hass.states.async_set("light.Bowl", STATE_ON)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user