Rename 'service' to 'action' in automations and scripts (#122845)

This commit is contained in:
Franck Nijhof 2024-07-31 14:36:53 +02:00 committed by GitHub
parent f14471112d
commit 8b96c7873f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 414 additions and 275 deletions

View file

@ -113,6 +113,7 @@ SUN_EVENT_SUNRISE: Final = "sunrise"
# #### CONFIG ####
CONF_ABOVE: Final = "above"
CONF_ACCESS_TOKEN: Final = "access_token"
CONF_ACTION: Final = "action"
CONF_ADDRESS: Final = "address"
CONF_AFTER: Final = "after"
CONF_ALIAS: Final = "alias"

View file

@ -34,6 +34,7 @@ from homeassistant.const import (
ATTR_FLOOR_ID,
ATTR_LABEL_ID,
CONF_ABOVE,
CONF_ACTION,
CONF_ALIAS,
CONF_ATTRIBUTE,
CONF_BELOW,
@ -1325,11 +1326,30 @@ EVENT_SCHEMA = vol.Schema(
}
)
def _backward_compat_service_schema(value: Any | None) -> Any:
"""Backward compatibility for service schemas."""
if not isinstance(value, dict):
return value
# `service` has been renamed to `action`
if CONF_SERVICE in value:
if CONF_ACTION in value:
raise vol.Invalid(
"Cannot specify both 'service' and 'action'. Please use 'action' only."
)
value[CONF_ACTION] = value.pop(CONF_SERVICE)
return value
SERVICE_SCHEMA = vol.All(
_backward_compat_service_schema,
vol.Schema(
{
**SCRIPT_ACTION_BASE_SCHEMA,
vol.Exclusive(CONF_SERVICE, "service name"): vol.Any(
vol.Exclusive(CONF_ACTION, "service name"): vol.Any(
service, dynamic_template
),
vol.Exclusive(CONF_SERVICE_TEMPLATE, "service name"): vol.Any(
@ -1348,7 +1368,7 @@ SERVICE_SCHEMA = vol.All(
vol.Remove("metadata"): dict,
}
),
has_at_least_one_key(CONF_SERVICE, CONF_SERVICE_TEMPLATE),
has_at_least_one_key(CONF_ACTION, CONF_SERVICE_TEMPLATE),
)
NUMERIC_STATE_THRESHOLD_SCHEMA = vol.Any(
@ -1844,6 +1864,7 @@ ACTIONS_MAP = {
CONF_WAIT_FOR_TRIGGER: SCRIPT_ACTION_WAIT_FOR_TRIGGER,
CONF_VARIABLES: SCRIPT_ACTION_VARIABLES,
CONF_IF: SCRIPT_ACTION_IF,
CONF_ACTION: SCRIPT_ACTION_CALL_SERVICE,
CONF_SERVICE: SCRIPT_ACTION_CALL_SERVICE,
CONF_SERVICE_TEMPLATE: SCRIPT_ACTION_CALL_SERVICE,
CONF_STOP: SCRIPT_ACTION_STOP,

View file

@ -20,8 +20,8 @@ from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_FLOOR_ID,
ATTR_LABEL_ID,
CONF_ACTION,
CONF_ENTITY_ID,
CONF_SERVICE,
CONF_SERVICE_DATA,
CONF_SERVICE_DATA_TEMPLATE,
CONF_SERVICE_TEMPLATE,
@ -358,8 +358,8 @@ def async_prepare_call_from_config(
f"Invalid config for calling service: {ex}"
) from ex
if CONF_SERVICE in config:
domain_service = config[CONF_SERVICE]
if CONF_ACTION in config:
domain_service = config[CONF_ACTION]
else:
domain_service = config[CONF_SERVICE_TEMPLATE]

View file

@ -88,7 +88,7 @@ async def test_service_data_not_a_dict(
{
automation.DOMAIN: {
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {"service": "test.automation", "data": 100},
"action": {"action": "test.automation", "data": 100},
}
},
)
@ -111,7 +111,7 @@ async def test_service_data_single_template(
automation.DOMAIN: {
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {
"service": "test.automation",
"action": "test.automation",
"data": "{{ { 'foo': 'bar' } }}",
},
}
@ -136,7 +136,7 @@ async def test_service_specify_data(
"alias": "hello",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {
"service": "test.automation",
"action": "test.automation",
"data_template": {
"some": (
"{{ trigger.platform }} - {{ trigger.event.event_type }}"
@ -170,7 +170,7 @@ async def test_service_specify_entity_id(
{
automation.DOMAIN: {
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {"service": "test.automation", "entity_id": "hello.world"},
"action": {"action": "test.automation", "entity_id": "hello.world"},
}
},
)
@ -192,7 +192,7 @@ async def test_service_specify_entity_id_list(
automation.DOMAIN: {
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {
"service": "test.automation",
"action": "test.automation",
"entity_id": ["hello.world", "hello.world2"],
},
}
@ -216,7 +216,7 @@ async def test_two_triggers(hass: HomeAssistant, calls: list[ServiceCall]) -> No
{"platform": "event", "event_type": "test_event"},
{"platform": "state", "entity_id": "test.entity"},
],
"action": {"service": "test.automation"},
"action": {"action": "test.automation"},
}
},
)
@ -245,7 +245,7 @@ async def test_trigger_service_ignoring_condition(
"entity_id": "non.existing",
"above": "1",
},
"action": {"service": "test.automation"},
"action": {"action": "test.automation"},
}
},
)
@ -301,7 +301,7 @@ async def test_two_conditions_with_and(
"below": 150,
},
],
"action": {"service": "test.automation"},
"action": {"action": "test.automation"},
}
},
)
@ -333,7 +333,7 @@ async def test_shorthand_conditions_template(
automation.DOMAIN: {
"trigger": [{"platform": "event", "event_type": "test_event"}],
"condition": "{{ is_state('test.entity', 'hello') }}",
"action": {"service": "test.automation"},
"action": {"action": "test.automation"},
}
},
)
@ -360,11 +360,11 @@ async def test_automation_list_setting(
automation.DOMAIN: [
{
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {"service": "test.automation"},
"action": {"action": "test.automation"},
},
{
"trigger": {"platform": "event", "event_type": "test_event_2"},
"action": {"service": "test.automation"},
"action": {"action": "test.automation"},
},
]
},
@ -390,8 +390,8 @@ async def test_automation_calling_two_actions(
automation.DOMAIN: {
"trigger": {"platform": "event", "event_type": "test_event"},
"action": [
{"service": "test.automation", "data": {"position": 0}},
{"service": "test.automation", "data": {"position": 1}},
{"action": "test.automation", "data": {"position": 0}},
{"action": "test.automation", "data": {"position": 1}},
],
}
},
@ -420,7 +420,7 @@ async def test_shared_context(hass: HomeAssistant, calls: list[ServiceCall]) ->
{
"alias": "bye",
"trigger": {"platform": "event", "event_type": "test_event2"},
"action": {"service": "test.automation"},
"action": {"action": "test.automation"},
},
]
},
@ -486,7 +486,7 @@ async def test_services(hass: HomeAssistant, calls: list[ServiceCall]) -> None:
automation.DOMAIN: {
"alias": "hello",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {"service": "test.automation"},
"action": {"action": "test.automation"},
}
},
)
@ -569,7 +569,7 @@ async def test_reload_config_service(
"alias": "hello",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {
"service": "test.automation",
"action": "test.automation",
"data_template": {"event": "{{ trigger.event.event_type }}"},
},
}
@ -597,7 +597,7 @@ async def test_reload_config_service(
"alias": "bye",
"trigger": {"platform": "event", "event_type": "test_event2"},
"action": {
"service": "test.automation",
"action": "test.automation",
"data_template": {"event": "{{ trigger.event.event_type }}"},
},
}
@ -650,7 +650,7 @@ async def test_reload_config_when_invalid_config(
"alias": "hello",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {
"service": "test.automation",
"action": "test.automation",
"data_template": {"event": "{{ trigger.event.event_type }}"},
},
}
@ -690,7 +690,7 @@ async def test_reload_config_handles_load_fails(
"alias": "hello",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {
"service": "test.automation",
"action": "test.automation",
"data_template": {"event": "{{ trigger.event.event_type }}"},
},
}
@ -735,7 +735,7 @@ async def test_automation_stops(
"action": [
{"event": "running"},
{"wait_template": "{{ is_state('test.entity', 'goodbye') }}"},
{"service": "test.automation"},
{"action": "test.automation"},
],
}
}
@ -811,7 +811,7 @@ async def test_reload_unchanged_does_not_stop(
"action": [
{"event": "running"},
{"wait_template": "{{ is_state('test.entity', 'goodbye') }}"},
{"service": "test.automation"},
{"action": "test.automation"},
],
}
}
@ -858,7 +858,7 @@ async def test_reload_single_unchanged_does_not_stop(
"action": [
{"event": "running"},
{"wait_template": "{{ is_state('test.entity', 'goodbye') }}"},
{"service": "test.automation"},
{"action": "test.automation"},
],
}
}
@ -905,7 +905,7 @@ async def test_reload_single_add_automation(
"id": "sun",
"alias": "hello",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": [{"service": "test.automation"}],
"action": [{"action": "test.automation"}],
}
}
assert await async_setup_component(hass, automation.DOMAIN, config1)
@ -942,25 +942,25 @@ async def test_reload_single_parallel_calls(
"id": "sun",
"alias": "hello",
"trigger": {"platform": "event", "event_type": "test_event_sun"},
"action": [{"service": "test.automation"}],
"action": [{"action": "test.automation"}],
},
{
"id": "moon",
"alias": "goodbye",
"trigger": {"platform": "event", "event_type": "test_event_moon"},
"action": [{"service": "test.automation"}],
"action": [{"action": "test.automation"}],
},
{
"id": "mars",
"alias": "goodbye",
"trigger": {"platform": "event", "event_type": "test_event_mars"},
"action": [{"service": "test.automation"}],
"action": [{"action": "test.automation"}],
},
{
"id": "venus",
"alias": "goodbye",
"trigger": {"platform": "event", "event_type": "test_event_venus"},
"action": [{"service": "test.automation"}],
"action": [{"action": "test.automation"}],
},
]
}
@ -1055,7 +1055,7 @@ async def test_reload_single_remove_automation(
"id": "sun",
"alias": "hello",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": [{"service": "test.automation"}],
"action": [{"action": "test.automation"}],
}
}
config2 = {automation.DOMAIN: {}}
@ -1093,12 +1093,12 @@ async def test_reload_moved_automation_without_alias(
automation.DOMAIN: [
{
"trigger": {"platform": "event", "event_type": "test_event"},
"action": [{"service": "test.automation"}],
"action": [{"action": "test.automation"}],
},
{
"alias": "automation_with_alias",
"trigger": {"platform": "event", "event_type": "test_event2"},
"action": [{"service": "test.automation"}],
"action": [{"action": "test.automation"}],
},
]
}
@ -1149,17 +1149,17 @@ async def test_reload_identical_automations_without_id(
{
"alias": "dolly",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": [{"service": "test.automation"}],
"action": [{"action": "test.automation"}],
},
{
"alias": "dolly",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": [{"service": "test.automation"}],
"action": [{"action": "test.automation"}],
},
{
"alias": "dolly",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": [{"service": "test.automation"}],
"action": [{"action": "test.automation"}],
},
]
}
@ -1246,12 +1246,12 @@ async def test_reload_identical_automations_without_id(
[
{
"trigger": {"platform": "event", "event_type": "test_event"},
"action": [{"service": "test.automation"}],
"action": [{"action": "test.automation"}],
},
# An automation using templates
{
"trigger": {"platform": "event", "event_type": "test_event"},
"action": [{"service": "{{ 'test.automation' }}"}],
"action": [{"action": "{{ 'test.automation' }}"}],
},
# An automation using blueprint
{
@ -1278,13 +1278,13 @@ async def test_reload_identical_automations_without_id(
{
"id": "sun",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": [{"service": "test.automation"}],
"action": [{"action": "test.automation"}],
},
# An automation using templates
{
"id": "sun",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": [{"service": "{{ 'test.automation' }}"}],
"action": [{"action": "{{ 'test.automation' }}"}],
},
# An automation using blueprint
{
@ -1424,12 +1424,12 @@ async def test_automation_restore_state(hass: HomeAssistant) -> None:
{
"alias": "hello",
"trigger": {"platform": "event", "event_type": "test_event_hello"},
"action": {"service": "test.automation"},
"action": {"action": "test.automation"},
},
{
"alias": "bye",
"trigger": {"platform": "event", "event_type": "test_event_bye"},
"action": {"service": "test.automation"},
"action": {"action": "test.automation"},
},
]
}
@ -1474,7 +1474,7 @@ async def test_initial_value_off(hass: HomeAssistant) -> None:
"alias": "hello",
"initial_state": "off",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {"service": "test.automation", "entity_id": "hello.world"},
"action": {"action": "test.automation", "entity_id": "hello.world"},
}
},
)
@ -1499,7 +1499,7 @@ async def test_initial_value_on(hass: HomeAssistant) -> None:
"initial_state": "on",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {
"service": "test.automation",
"action": "test.automation",
"entity_id": ["hello.world", "hello.world2"],
},
}
@ -1528,7 +1528,7 @@ async def test_initial_value_off_but_restore_on(hass: HomeAssistant) -> None:
"alias": "hello",
"initial_state": "off",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {"service": "test.automation", "entity_id": "hello.world"},
"action": {"action": "test.automation", "entity_id": "hello.world"},
}
},
)
@ -1553,7 +1553,7 @@ async def test_initial_value_on_but_restore_off(hass: HomeAssistant) -> None:
"alias": "hello",
"initial_state": "on",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {"service": "test.automation", "entity_id": "hello.world"},
"action": {"action": "test.automation", "entity_id": "hello.world"},
}
},
)
@ -1576,7 +1576,7 @@ async def test_no_initial_value_and_restore_off(hass: HomeAssistant) -> None:
automation.DOMAIN: {
"alias": "hello",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {"service": "test.automation", "entity_id": "hello.world"},
"action": {"action": "test.automation", "entity_id": "hello.world"},
}
},
)
@ -1600,7 +1600,7 @@ async def test_automation_is_on_if_no_initial_state_or_restore(
automation.DOMAIN: {
"alias": "hello",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {"service": "test.automation", "entity_id": "hello.world"},
"action": {"action": "test.automation", "entity_id": "hello.world"},
}
},
)
@ -1623,7 +1623,7 @@ async def test_automation_not_trigger_on_bootstrap(hass: HomeAssistant) -> None:
automation.DOMAIN: {
"alias": "hello",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {"service": "test.automation", "entity_id": "hello.world"},
"action": {"action": "test.automation", "entity_id": "hello.world"},
}
},
)
@ -1714,7 +1714,7 @@ async def test_automation_bad_config_validation(
"alias": "good_automation",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {
"service": "test.automation",
"action": "test.automation",
"entity_id": "hello.world",
},
},
@ -1756,7 +1756,7 @@ async def test_automation_bad_config_validation(
"alias": "bad_automation",
"trigger": {"platform": "event", "event_type": "test_event2"},
"action": {
"service": "test.automation",
"action": "test.automation",
"data_template": {"event": "{{ trigger.event.event_type }}"},
},
}
@ -1785,7 +1785,7 @@ async def test_automation_with_error_in_script(
automation.DOMAIN: {
"alias": "hello",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {"service": "test.automation", "entity_id": "hello.world"},
"action": {"action": "test.automation", "entity_id": "hello.world"},
}
},
)
@ -1811,7 +1811,7 @@ async def test_automation_with_error_in_script_2(
automation.DOMAIN: {
"alias": "hello",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {"service": None, "entity_id": "hello.world"},
"action": {"action": None, "entity_id": "hello.world"},
}
},
)
@ -1842,19 +1842,19 @@ async def test_automation_restore_last_triggered_with_initial_state(
"alias": "hello",
"initial_state": "off",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {"service": "test.automation"},
"action": {"action": "test.automation"},
},
{
"alias": "bye",
"initial_state": "off",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {"service": "test.automation"},
"action": {"action": "test.automation"},
},
{
"alias": "solong",
"initial_state": "on",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {"service": "test.automation"},
"action": {"action": "test.automation"},
},
]
}
@ -2013,11 +2013,11 @@ async def test_extraction_functions(
},
"action": [
{
"service": "test.script",
"action": "test.script",
"data": {"entity_id": "light.in_both"},
},
{
"service": "test.script",
"action": "test.script",
"data": {"entity_id": "light.in_first"},
},
{
@ -2027,15 +2027,15 @@ async def test_extraction_functions(
"type": "turn_on",
},
{
"service": "test.test",
"action": "test.test",
"target": {"area_id": "area-in-both"},
},
{
"service": "test.test",
"action": "test.test",
"target": {"floor_id": "floor-in-both"},
},
{
"service": "test.test",
"action": "test.test",
"target": {"label_id": "label-in-both"},
},
],
@ -2087,7 +2087,7 @@ async def test_extraction_functions(
},
"action": [
{
"service": "test.script",
"action": "test.script",
"data": {"entity_id": "light.in_both"},
},
{
@ -2140,7 +2140,7 @@ async def test_extraction_functions(
},
"action": [
{
"service": "test.script",
"action": "test.script",
"data": {"entity_id": "light.in_both"},
},
{
@ -2150,27 +2150,27 @@ async def test_extraction_functions(
},
{"scene": "scene.hello"},
{
"service": "test.test",
"action": "test.test",
"target": {"area_id": "area-in-both"},
},
{
"service": "test.test",
"action": "test.test",
"target": {"area_id": "area-in-last"},
},
{
"service": "test.test",
"action": "test.test",
"target": {"floor_id": "floor-in-both"},
},
{
"service": "test.test",
"action": "test.test",
"target": {"floor_id": "floor-in-last"},
},
{
"service": "test.test",
"action": "test.test",
"target": {"label_id": "label-in-both"},
},
{
"service": "test.test",
"action": "test.test",
"target": {"label_id": "label-in-last"},
},
],
@ -2289,7 +2289,7 @@ async def test_automation_variables(
},
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {
"service": "test.automation",
"action": "test.automation",
"data": {
"value": "{{ test_var }}",
"event_type": "{{ event_type }}",
@ -2308,7 +2308,7 @@ async def test_automation_variables(
"value_template": "{{ trigger.event.data.pass_condition }}",
},
"action": {
"service": "test.automation",
"action": "test.automation",
},
},
{
@ -2317,7 +2317,7 @@ async def test_automation_variables(
},
"trigger": {"platform": "event", "event_type": "test_event_3"},
"action": {
"service": "test.automation",
"action": "test.automation",
},
},
]
@ -2373,7 +2373,7 @@ async def test_automation_trigger_variables(
},
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {
"service": "test.automation",
"action": "test.automation",
"data": {
"value": "{{ test_var }}",
"event_type": "{{ event_type }}",
@ -2391,7 +2391,7 @@ async def test_automation_trigger_variables(
},
"trigger": {"platform": "event", "event_type": "test_event_2"},
"action": {
"service": "test.automation",
"action": "test.automation",
"data": {
"value": "{{ test_var }}",
"event_type": "{{ event_type }}",
@ -2438,7 +2438,7 @@ async def test_automation_bad_trigger_variables(
},
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {
"service": "test.automation",
"action": "test.automation",
},
},
]
@ -2465,7 +2465,7 @@ async def test_automation_this_var_always(
{
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {
"service": "test.automation",
"action": "test.automation",
"data": {
"this_template": "{{this.entity_id}}",
},
@ -2542,7 +2542,7 @@ async def test_blueprint_automation(
"Blueprint 'Call service based on event' generated invalid automation",
(
"value should be a string for dictionary value @"
" data['action'][0]['service']"
" data['action'][0]['action']"
),
),
],
@ -2640,7 +2640,7 @@ async def test_trigger_service(hass: HomeAssistant, calls: list[ServiceCall]) ->
"alias": "hello",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {
"service": "test.automation",
"action": "test.automation",
"data_template": {"trigger": "{{ trigger }}"},
},
}
@ -2679,14 +2679,14 @@ async def test_trigger_condition_implicit_id(
{
"conditions": {"condition": "trigger", "id": [0, "2"]},
"sequence": {
"service": "test.automation",
"action": "test.automation",
"data": {"param": "one"},
},
},
{
"conditions": {"condition": "trigger", "id": "1"},
"sequence": {
"service": "test.automation",
"action": "test.automation",
"data": {"param": "two"},
},
},
@ -2730,14 +2730,14 @@ async def test_trigger_condition_explicit_id(
{
"conditions": {"condition": "trigger", "id": "one"},
"sequence": {
"service": "test.automation",
"action": "test.automation",
"data": {"param": "one"},
},
},
{
"conditions": {"condition": "trigger", "id": "two"},
"sequence": {
"service": "test.automation",
"action": "test.automation",
"data": {"param": "two"},
},
},
@ -2822,8 +2822,8 @@ async def test_recursive_automation_starting_script(
f" {automation_runs} }}}}"
)
},
{"service": "script.script1"},
{"service": "test.script_done"},
{"action": "script.script1"},
{"action": "test.script_done"},
],
},
}
@ -2840,9 +2840,9 @@ async def test_recursive_automation_starting_script(
{"platform": "event", "event_type": "trigger_automation"},
],
"action": [
{"service": "test.automation_started"},
{"action": "test.automation_started"},
{"delay": 0.001},
{"service": "script.script1"},
{"action": "script.script1"},
],
}
},
@ -2923,7 +2923,7 @@ async def test_recursive_automation(
],
"action": [
{"event": "trigger_automation"},
{"service": "test.automation_done"},
{"action": "test.automation_done"},
],
}
},
@ -2985,7 +2985,7 @@ async def test_recursive_automation_restart_mode(
],
"action": [
{"event": "trigger_automation"},
{"service": "test.automation_done"},
{"action": "test.automation_done"},
],
}
},
@ -3021,7 +3021,7 @@ async def test_websocket_config(
config = {
"alias": "hello",
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {"service": "test.automation", "data": 100},
"action": {"action": "test.automation", "data": 100},
}
assert await async_setup_component(
hass, automation.DOMAIN, {automation.DOMAIN: config}
@ -3095,7 +3095,7 @@ async def test_automation_turns_off_other_automation(hass: HomeAssistant) -> Non
"from": "on",
},
"action": {
"service": "automation.turn_off",
"action": "automation.turn_off",
"target": {
"entity_id": "automation.automation_1",
},
@ -3118,7 +3118,7 @@ async def test_automation_turns_off_other_automation(hass: HomeAssistant) -> Non
},
},
"action": {
"service": "persistent_notification.create",
"action": "persistent_notification.create",
"metadata": {},
"data": {
"message": "Test race",
@ -3185,7 +3185,7 @@ async def test_two_automations_call_restart_script_same_time(
"fire_toggle": {
"sequence": [
{
"service": "input_boolean.toggle",
"action": "input_boolean.toggle",
"target": {"entity_id": "input_boolean.test_1"},
}
]
@ -3206,7 +3206,7 @@ async def test_two_automations_call_restart_script_same_time(
"to": "on",
},
"action": {
"service": "script.fire_toggle",
"action": "script.fire_toggle",
},
"id": "automation_0",
"mode": "single",
@ -3218,7 +3218,7 @@ async def test_two_automations_call_restart_script_same_time(
"to": "on",
},
"action": {
"service": "script.fire_toggle",
"action": "script.fire_toggle",
},
"id": "automation_1",
"mode": "single",
@ -3301,3 +3301,29 @@ async def test_two_automation_call_restart_script_right_after_each_other(
hass.states.async_set("input_boolean.test_2", "on")
await hass.async_block_till_done()
assert len(events) == 1
async def test_action_service_backward_compatibility(
hass: HomeAssistant, calls: list[ServiceCall]
) -> None:
"""Test we can still use the service call method."""
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {
"service": "test.automation",
"entity_id": "hello.world",
"data": {"event": "{{ trigger.event.event_type }}"},
},
}
},
)
hass.bus.async_fire("test_event")
await hass.async_block_till_done()
assert len(calls) == 1
assert calls[0].data.get(ATTR_ENTITY_ID) == ["hello.world"]
assert calls[0].data.get("event") == "test_event"

View file

@ -40,7 +40,7 @@ async def test_exclude_attributes(
{
automation.DOMAIN: {
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {"service": "test.automation", "entity_id": "hello.world"},
"action": {"action": "test.automation", "entity_id": "hello.world"},
}
},
)

View file

@ -74,7 +74,7 @@ async def test_confirmable_notification(
"message": "Throw ring in mountain?",
"confirm_action": [
{
"service": "homeassistant.turn_on",
"action": "homeassistant.turn_on",
"target": {"entity_id": "mount.doom"},
}
],

View file

@ -85,7 +85,7 @@ async def test_passing_variables(hass: HomeAssistant) -> None:
"script": {
"test": {
"sequence": {
"service": "test.script",
"action": "test.script",
"data_template": {"hello": "{{ greeting }}"},
}
}
@ -115,8 +115,14 @@ async def test_passing_variables(hass: HomeAssistant) -> None:
@pytest.mark.parametrize("toggle", [False, True])
async def test_turn_on_off_toggle(hass: HomeAssistant, toggle) -> None:
"""Verify turn_on, turn_off & toggle services."""
@pytest.mark.parametrize("action_schema_variations", ["action", "service"])
async def test_turn_on_off_toggle(
hass: HomeAssistant, toggle: bool, action_schema_variations: str
) -> None:
"""Verify turn_on, turn_off & toggle services.
Ensures backward compatibility with the old service action schema is maintained.
"""
event = "test_event"
event_mock = Mock()
@ -132,9 +138,15 @@ async def test_turn_on_off_toggle(hass: HomeAssistant, toggle) -> None:
async_track_state_change(hass, ENTITY_ID, state_listener, to_state="on")
if toggle:
turn_off_step = {"service": "script.toggle", "entity_id": ENTITY_ID}
turn_off_step = {
action_schema_variations: "script.toggle",
"entity_id": ENTITY_ID,
}
else:
turn_off_step = {"service": "script.turn_off", "entity_id": ENTITY_ID}
turn_off_step = {
action_schema_variations: "script.turn_off",
"entity_id": ENTITY_ID,
}
assert await async_setup_component(
hass,
"script",
@ -165,7 +177,7 @@ async def test_turn_on_off_toggle(hass: HomeAssistant, toggle) -> None:
invalid_configs = [
{"test": {}},
{"test hello world": {"sequence": [{"event": "bla"}]}},
{"test": {"sequence": {"event": "test_event", "service": "homeassistant.turn_on"}}},
{"test": {"sequence": {"event": "test_event", "action": "homeassistant.turn_on"}}},
]
@ -180,7 +192,7 @@ invalid_configs = [
"test": {
"sequence": {
"event": "test_event",
"service": "homeassistant.turn_on",
"action": "homeassistant.turn_on",
}
}
},
@ -235,7 +247,7 @@ async def test_bad_config_validation_critical(
"good_script": {
"alias": "good_script",
"sequence": {
"service": "test.automation",
"action": "test.automation",
"entity_id": "hello.world",
},
},
@ -300,7 +312,7 @@ async def test_bad_config_validation(
"good_script": {
"alias": "good_script",
"sequence": {
"service": "test.automation",
"action": "test.automation",
"entity_id": "hello.world",
},
},
@ -342,7 +354,7 @@ async def test_bad_config_validation(
object_id: {
"alias": "bad_script",
"sequence": {
"service": "test.automation",
"action": "test.automation",
"entity_id": "hello.world",
},
},
@ -430,7 +442,7 @@ async def test_reload_unchanged_does_not_stop(
"sequence": [
{"event": "running"},
{"wait_template": "{{ is_state('test.entity', 'goodbye') }}"},
{"service": "test.script"},
{"action": "test.script"},
],
}
}
@ -473,13 +485,13 @@ async def test_reload_unchanged_does_not_stop(
[
{
"test": {
"sequence": [{"service": "test.script"}],
"sequence": [{"action": "test.script"}],
}
},
# A script using templates
{
"test": {
"sequence": [{"service": "{{ 'test.script' }}"}],
"sequence": [{"action": "{{ 'test.script' }}"}],
}
},
# A script using blueprint
@ -666,7 +678,7 @@ async def test_logging_script_error(
assert await async_setup_component(
hass,
"script",
{"script": {"hello": {"sequence": [{"service": "non.existing"}]}}},
{"script": {"hello": {"sequence": [{"action": "non.existing"}]}}},
)
with pytest.raises(ServiceNotFound) as err:
await hass.services.async_call("script", "hello", blocking=True)
@ -690,7 +702,7 @@ async def test_async_get_descriptions_script(hass: HomeAssistant) -> None:
"""Test async_set_service_schema for the script integration."""
script_config = {
DOMAIN: {
"test1": {"sequence": [{"service": "homeassistant.restart"}]},
"test1": {"sequence": [{"action": "homeassistant.restart"}]},
"test2": {
"description": "test2",
"fields": {
@ -699,7 +711,7 @@ async def test_async_get_descriptions_script(hass: HomeAssistant) -> None:
"example": "param_example",
}
},
"sequence": [{"service": "homeassistant.restart"}],
"sequence": [{"action": "homeassistant.restart"}],
},
}
}
@ -795,11 +807,11 @@ async def test_extraction_functions(
"test1": {
"sequence": [
{
"service": "test.script",
"action": "test.script",
"data": {"entity_id": "light.in_both"},
},
{
"service": "test.script",
"action": "test.script",
"data": {"entity_id": "light.in_first"},
},
{
@ -809,15 +821,15 @@ async def test_extraction_functions(
"device_id": device_in_both.id,
},
{
"service": "test.test",
"action": "test.test",
"target": {"area_id": "area-in-both"},
},
{
"service": "test.test",
"action": "test.test",
"target": {"floor_id": "floor-in-both"},
},
{
"service": "test.test",
"action": "test.test",
"target": {"label_id": "label-in-both"},
},
]
@ -825,7 +837,7 @@ async def test_extraction_functions(
"test2": {
"sequence": [
{
"service": "test.script",
"action": "test.script",
"data": {"entity_id": "light.in_both"},
},
{
@ -851,7 +863,7 @@ async def test_extraction_functions(
"test3": {
"sequence": [
{
"service": "test.script",
"action": "test.script",
"data": {"entity_id": "light.in_both"},
},
{
@ -861,27 +873,27 @@ async def test_extraction_functions(
},
{"scene": "scene.hello"},
{
"service": "test.test",
"action": "test.test",
"target": {"area_id": "area-in-both"},
},
{
"service": "test.test",
"action": "test.test",
"target": {"area_id": "area-in-last"},
},
{
"service": "test.test",
"action": "test.test",
"target": {"floor_id": "floor-in-both"},
},
{
"service": "test.test",
"action": "test.test",
"target": {"floor_id": "floor-in-last"},
},
{
"service": "test.test",
"action": "test.test",
"target": {"label_id": "label-in-both"},
},
{
"service": "test.test",
"action": "test.test",
"target": {"label_id": "label-in-last"},
},
],
@ -1028,11 +1040,11 @@ async def test_concurrent_script(hass: HomeAssistant, concurrently) -> None:
"""Test calling script concurrently or not."""
if concurrently:
call_script_2 = {
"service": "script.turn_on",
"action": "script.turn_on",
"data": {"entity_id": "script.script2"},
}
else:
call_script_2 = {"service": "script.script2"}
call_script_2 = {"action": "script.script2"}
assert await async_setup_component(
hass,
"script",
@ -1045,17 +1057,17 @@ async def test_concurrent_script(hass: HomeAssistant, concurrently) -> None:
{
"wait_template": "{{ is_state('input_boolean.test1', 'on') }}"
},
{"service": "test.script", "data": {"value": "script1"}},
{"action": "test.script", "data": {"value": "script1"}},
],
},
"script2": {
"mode": "parallel",
"sequence": [
{"service": "test.script", "data": {"value": "script2a"}},
{"action": "test.script", "data": {"value": "script2a"}},
{
"wait_template": "{{ is_state('input_boolean.test2', 'on') }}"
},
{"service": "test.script", "data": {"value": "script2b"}},
{"action": "test.script", "data": {"value": "script2b"}},
],
},
}
@ -1126,7 +1138,7 @@ async def test_script_variables(
},
"sequence": [
{
"service": "test.script",
"action": "test.script",
"data": {
"value": "{{ test_var }}",
"templated_config_var": "{{ templated_config_var }}",
@ -1142,7 +1154,7 @@ async def test_script_variables(
},
"sequence": [
{
"service": "test.script",
"action": "test.script",
"data": {
"value": "{{ test_var }}",
},
@ -1155,7 +1167,7 @@ async def test_script_variables(
},
"sequence": [
{
"service": "test.script",
"action": "test.script",
"data": {
"value": "{{ test_var }}",
},
@ -1221,7 +1233,7 @@ async def test_script_this_var_always(
"script1": {
"sequence": [
{
"service": "test.script",
"action": "test.script",
"data": {
"this_template": "{{this.entity_id}}",
},
@ -1306,8 +1318,8 @@ async def test_recursive_script(
"script1": {
"mode": script_mode,
"sequence": [
{"service": "script.script1"},
{"service": "test.script"},
{"action": "script.script1"},
{"action": "test.script"},
],
},
}
@ -1356,26 +1368,26 @@ async def test_recursive_script_indirect(
"script1": {
"mode": script_mode,
"sequence": [
{"service": "script.script2"},
{"action": "script.script2"},
],
},
"script2": {
"mode": script_mode,
"sequence": [
{"service": "script.script3"},
{"action": "script.script3"},
],
},
"script3": {
"mode": script_mode,
"sequence": [
{"service": "script.script4"},
{"action": "script.script4"},
],
},
"script4": {
"mode": script_mode,
"sequence": [
{"service": "script.script1"},
{"service": "test.script"},
{"action": "script.script1"},
{"action": "test.script"},
],
},
}
@ -1440,10 +1452,10 @@ async def test_recursive_script_turn_on(
"condition": "template",
"value_template": "{{ request == 'step_2' }}",
},
"sequence": {"service": "test.script_done"},
"sequence": {"action": "test.script_done"},
},
"default": {
"service": "script.turn_on",
"action": "script.turn_on",
"data": {
"entity_id": "script.script1",
"variables": {"request": "step_2"},
@ -1451,7 +1463,7 @@ async def test_recursive_script_turn_on(
},
},
{
"service": "script.turn_on",
"action": "script.turn_on",
"data": {"entity_id": "script.script1"},
},
],
@ -1513,7 +1525,7 @@ async def test_websocket_config(
"""Test config command."""
config = {
"alias": "hello",
"sequence": [{"service": "light.turn_on"}],
"sequence": [{"action": "light.turn_on"}],
}
assert await async_setup_component(
hass,
@ -1577,7 +1589,7 @@ async def test_script_service_changed_entity_id(
"script": {
"test": {
"sequence": {
"service": "test.script",
"action": "test.script",
"data_template": {"entity_id": "{{ this.entity_id }}"},
}
}
@ -1658,7 +1670,7 @@ async def test_blueprint_script(hass: HomeAssistant, calls: list[ServiceCall]) -
"a_number": 5,
},
"Blueprint 'Call service' generated invalid script",
"value should be a string for dictionary value @ data['sequence'][0]['service']",
"value should be a string for dictionary value @ data['sequence'][0]['action']",
),
],
)
@ -1839,10 +1851,10 @@ async def test_script_queued_mode(hass: HomeAssistant) -> None:
"sequence": [
{
"parallel": [
{"service": "script.test_sub"},
{"service": "script.test_sub"},
{"service": "script.test_sub"},
{"service": "script.test_sub"},
{"action": "script.test_sub"},
{"action": "script.test_sub"},
{"action": "script.test_sub"},
{"action": "script.test_sub"},
]
}
]
@ -1850,7 +1862,7 @@ async def test_script_queued_mode(hass: HomeAssistant) -> None:
"test_sub": {
"mode": "queued",
"sequence": [
{"service": "test.simulated_remote"},
{"action": "test.simulated_remote"},
],
},
}

View file

@ -52,7 +52,7 @@ async def test_exclude_attributes(
"script": {
"test": {
"sequence": {
"service": "test.script",
"action": "test.script",
"data_template": {"hello": "{{ greeting }}"},
}
}

View file

@ -6,6 +6,7 @@ import enum
import logging
import os
from socket import _GLOBAL_DEFAULT_TIMEOUT
from typing import Any
from unittest.mock import Mock, patch
import uuid
@ -416,27 +417,9 @@ def test_service() -> None:
schema("homeassistant.turn_on")
def test_service_schema(hass: HomeAssistant) -> None:
"""Test service_schema validation."""
options = (
{},
None,
{
"service": "homeassistant.turn_on",
"service_template": "homeassistant.turn_on",
},
{"data": {"entity_id": "light.kitchen"}},
{"service": "homeassistant.turn_on", "data": None},
{
"service": "homeassistant.turn_on",
"data_template": {"brightness": "{{ no_end"},
},
)
for value in options:
with pytest.raises(vol.MultipleInvalid):
cv.SERVICE_SCHEMA(value)
options = (
@pytest.mark.parametrize(
"config",
[
{"service": "homeassistant.turn_on"},
{"service": "homeassistant.turn_on", "entity_id": "light.kitchen"},
{"service": "light.turn_on", "entity_id": "all"},
@ -450,14 +433,70 @@ def test_service_schema(hass: HomeAssistant) -> None:
"alias": "turn on kitchen lights",
},
{"service": "scene.turn_on", "metadata": {}},
)
for value in options:
cv.SERVICE_SCHEMA(value)
{"action": "homeassistant.turn_on"},
{"action": "homeassistant.turn_on", "entity_id": "light.kitchen"},
{"action": "light.turn_on", "entity_id": "all"},
{
"action": "homeassistant.turn_on",
"entity_id": ["light.kitchen", "light.ceiling"],
},
{
"action": "light.turn_on",
"entity_id": "all",
"alias": "turn on kitchen lights",
},
{"action": "scene.turn_on", "metadata": {}},
],
)
def test_service_schema(hass: HomeAssistant, config: dict[str, Any]) -> None:
"""Test service_schema validation."""
validated = cv.SERVICE_SCHEMA(config)
# Check metadata is removed from the validated output
assert cv.SERVICE_SCHEMA({"service": "scene.turn_on", "metadata": {}}) == {
"service": "scene.turn_on"
}
# Ensure metadata is removed from the validated output
assert "metadata" not in validated
# Ensure service is migrated to action
assert "service" not in validated
assert "action" in validated
assert validated["action"] == config.get("service", config["action"])
@pytest.mark.parametrize(
"config",
[
{},
None,
{"data": {"entity_id": "light.kitchen"}},
{
"service": "homeassistant.turn_on",
"service_template": "homeassistant.turn_on",
},
{"service": "homeassistant.turn_on", "data": None},
{
"service": "homeassistant.turn_on",
"data_template": {"brightness": "{{ no_end"},
},
{
"service": "homeassistant.turn_on",
"action": "homeassistant.turn_on",
},
{
"action": "homeassistant.turn_on",
"service_template": "homeassistant.turn_on",
},
{"action": "homeassistant.turn_on", "data": None},
{
"action": "homeassistant.turn_on",
"data_template": {"brightness": "{{ no_end"},
},
],
)
def test_invalid_service_schema(
hass: HomeAssistant, config: dict[str, Any] | None
) -> None:
"""Test service_schema validation fails."""
with pytest.raises(vol.MultipleInvalid):
cv.SERVICE_SCHEMA(config)
def test_entity_service_schema() -> None:

View file

@ -249,7 +249,7 @@ async def test_calling_service_basic(
alias = "service step"
sequence = cv.SCRIPT_SCHEMA(
{"alias": alias, "service": "test.script", "data": {"hello": "world"}}
{"alias": alias, "action": "test.script", "data": {"hello": "world"}}
)
script_obj = script.Script(hass, sequence, "Test Name", "test_domain")
@ -352,13 +352,13 @@ async def test_calling_service_response_data(
[
{
"alias": "service step1",
"service": "test.script",
"action": "test.script",
# Store the result of the service call as a variable
"response_variable": "my_response",
},
{
"alias": "service step2",
"service": "test.script",
"action": "test.script",
"data_template": {
# Result of previous service call
"key": "{{ my_response.data }}"
@ -441,7 +441,7 @@ async def test_service_response_data_errors(
[
{
"alias": "service step1",
"service": "test.script",
"action": "test.script",
**params,
},
]
@ -458,7 +458,7 @@ async def test_data_template_with_templated_key(hass: HomeAssistant) -> None:
calls = async_mock_service(hass, "test", "script")
sequence = cv.SCRIPT_SCHEMA(
{"service": "test.script", "data_template": {"{{ hello_var }}": "world"}}
{"action": "test.script", "data_template": {"{{ hello_var }}": "world"}}
)
script_obj = script.Script(hass, sequence, "Test Name", "test_domain")
@ -525,11 +525,11 @@ async def test_multiple_runs_no_wait(hass: HomeAssistant) -> None:
sequence = cv.SCRIPT_SCHEMA(
[
{
"service": "test.script",
"action": "test.script",
"data_template": {"fire": "{{ fire1 }}", "listen": "{{ listen1 }}"},
},
{
"service": "test.script",
"action": "test.script",
"data_template": {"fire": "{{ fire2 }}", "listen": "{{ listen2 }}"},
},
]
@ -605,7 +605,7 @@ async def test_stop_no_wait(hass: HomeAssistant, count) -> None:
hass.services.async_register("test", "script", async_simulate_long_service)
sequence = cv.SCRIPT_SCHEMA([{"service": "test.script"}, {"event": event}])
sequence = cv.SCRIPT_SCHEMA([{"action": "test.script"}, {"event": event}])
script_obj = script.Script(
hass,
sequence,
@ -3894,7 +3894,7 @@ async def test_parallel_error(
sequence = cv.SCRIPT_SCHEMA(
{
"parallel": [
{"service": "epic.failure"},
{"action": "epic.failure"},
]
}
)
@ -3946,7 +3946,7 @@ async def test_propagate_error_service_not_found(hass: HomeAssistant) -> None:
await async_setup_component(hass, "homeassistant", {})
event = "test_event"
events = async_capture_events(hass, event)
sequence = cv.SCRIPT_SCHEMA([{"service": "test.script"}, {"event": event}])
sequence = cv.SCRIPT_SCHEMA([{"action": "test.script"}, {"event": event}])
script_obj = script.Script(hass, sequence, "Test Name", "test_domain")
with pytest.raises(exceptions.ServiceNotFound):
@ -3980,7 +3980,7 @@ async def test_propagate_error_invalid_service_data(hass: HomeAssistant) -> None
events = async_capture_events(hass, event)
calls = async_mock_service(hass, "test", "script", vol.Schema({"text": str}))
sequence = cv.SCRIPT_SCHEMA(
[{"service": "test.script", "data": {"text": 1}}, {"event": event}]
[{"action": "test.script", "data": {"text": 1}}, {"event": event}]
)
script_obj = script.Script(hass, sequence, "Test Name", "test_domain")
@ -4022,7 +4022,7 @@ async def test_propagate_error_service_exception(hass: HomeAssistant) -> None:
hass.services.async_register("test", "script", record_call)
sequence = cv.SCRIPT_SCHEMA([{"service": "test.script"}, {"event": event}])
sequence = cv.SCRIPT_SCHEMA([{"action": "test.script"}, {"event": event}])
script_obj = script.Script(hass, sequence, "Test Name", "test_domain")
with pytest.raises(ValueError):
@ -4057,35 +4057,35 @@ async def test_referenced_labels(hass: HomeAssistant) -> None:
cv.SCRIPT_SCHEMA(
[
{
"service": "test.script",
"action": "test.script",
"data": {"label_id": "label_service_not_list"},
},
{
"service": "test.script",
"action": "test.script",
"data": {
"label_id": ["label_service_list_1", "label_service_list_2"]
},
},
{
"service": "test.script",
"action": "test.script",
"data": {"label_id": "{{ 'label_service_template' }}"},
},
{
"service": "test.script",
"action": "test.script",
"target": {"label_id": "label_in_target"},
},
{
"service": "test.script",
"action": "test.script",
"data_template": {"label_id": "label_in_data_template"},
},
{"service": "test.script", "data": {"without": "label_id"}},
{"action": "test.script", "data": {"without": "label_id"}},
{
"choose": [
{
"conditions": "{{ true == false }}",
"sequence": [
{
"service": "test.script",
"action": "test.script",
"data": {"label_id": "label_choice_1_seq"},
}
],
@ -4094,7 +4094,7 @@ async def test_referenced_labels(hass: HomeAssistant) -> None:
"conditions": "{{ true == false }}",
"sequence": [
{
"service": "test.script",
"action": "test.script",
"data": {"label_id": "label_choice_2_seq"},
}
],
@ -4102,7 +4102,7 @@ async def test_referenced_labels(hass: HomeAssistant) -> None:
],
"default": [
{
"service": "test.script",
"action": "test.script",
"data": {"label_id": "label_default_seq"},
}
],
@ -4113,13 +4113,13 @@ async def test_referenced_labels(hass: HomeAssistant) -> None:
"if": [],
"then": [
{
"service": "test.script",
"action": "test.script",
"data": {"label_id": "label_if_then"},
}
],
"else": [
{
"service": "test.script",
"action": "test.script",
"data": {"label_id": "label_if_else"},
}
],
@ -4127,7 +4127,7 @@ async def test_referenced_labels(hass: HomeAssistant) -> None:
{
"parallel": [
{
"service": "test.script",
"action": "test.script",
"data": {"label_id": "label_parallel"},
}
],
@ -4161,33 +4161,33 @@ async def test_referenced_floors(hass: HomeAssistant) -> None:
cv.SCRIPT_SCHEMA(
[
{
"service": "test.script",
"action": "test.script",
"data": {"floor_id": "floor_service_not_list"},
},
{
"service": "test.script",
"action": "test.script",
"data": {"floor_id": ["floor_service_list"]},
},
{
"service": "test.script",
"action": "test.script",
"data": {"floor_id": "{{ 'floor_service_template' }}"},
},
{
"service": "test.script",
"action": "test.script",
"target": {"floor_id": "floor_in_target"},
},
{
"service": "test.script",
"action": "test.script",
"data_template": {"floor_id": "floor_in_data_template"},
},
{"service": "test.script", "data": {"without": "floor_id"}},
{"action": "test.script", "data": {"without": "floor_id"}},
{
"choose": [
{
"conditions": "{{ true == false }}",
"sequence": [
{
"service": "test.script",
"action": "test.script",
"data": {"floor_id": "floor_choice_1_seq"},
}
],
@ -4196,7 +4196,7 @@ async def test_referenced_floors(hass: HomeAssistant) -> None:
"conditions": "{{ true == false }}",
"sequence": [
{
"service": "test.script",
"action": "test.script",
"data": {"floor_id": "floor_choice_2_seq"},
}
],
@ -4204,7 +4204,7 @@ async def test_referenced_floors(hass: HomeAssistant) -> None:
],
"default": [
{
"service": "test.script",
"action": "test.script",
"data": {"floor_id": "floor_default_seq"},
}
],
@ -4215,13 +4215,13 @@ async def test_referenced_floors(hass: HomeAssistant) -> None:
"if": [],
"then": [
{
"service": "test.script",
"action": "test.script",
"data": {"floor_id": "floor_if_then"},
}
],
"else": [
{
"service": "test.script",
"action": "test.script",
"data": {"floor_id": "floor_if_else"},
}
],
@ -4229,7 +4229,7 @@ async def test_referenced_floors(hass: HomeAssistant) -> None:
{
"parallel": [
{
"service": "test.script",
"action": "test.script",
"data": {"floor_id": "floor_parallel"},
}
],
@ -4262,33 +4262,33 @@ async def test_referenced_areas(hass: HomeAssistant) -> None:
cv.SCRIPT_SCHEMA(
[
{
"service": "test.script",
"action": "test.script",
"data": {"area_id": "area_service_not_list"},
},
{
"service": "test.script",
"action": "test.script",
"data": {"area_id": ["area_service_list"]},
},
{
"service": "test.script",
"action": "test.script",
"data": {"area_id": "{{ 'area_service_template' }}"},
},
{
"service": "test.script",
"action": "test.script",
"target": {"area_id": "area_in_target"},
},
{
"service": "test.script",
"action": "test.script",
"data_template": {"area_id": "area_in_data_template"},
},
{"service": "test.script", "data": {"without": "area_id"}},
{"action": "test.script", "data": {"without": "area_id"}},
{
"choose": [
{
"conditions": "{{ true == false }}",
"sequence": [
{
"service": "test.script",
"action": "test.script",
"data": {"area_id": "area_choice_1_seq"},
}
],
@ -4297,7 +4297,7 @@ async def test_referenced_areas(hass: HomeAssistant) -> None:
"conditions": "{{ true == false }}",
"sequence": [
{
"service": "test.script",
"action": "test.script",
"data": {"area_id": "area_choice_2_seq"},
}
],
@ -4305,7 +4305,7 @@ async def test_referenced_areas(hass: HomeAssistant) -> None:
],
"default": [
{
"service": "test.script",
"action": "test.script",
"data": {"area_id": "area_default_seq"},
}
],
@ -4316,13 +4316,13 @@ async def test_referenced_areas(hass: HomeAssistant) -> None:
"if": [],
"then": [
{
"service": "test.script",
"action": "test.script",
"data": {"area_id": "area_if_then"},
}
],
"else": [
{
"service": "test.script",
"action": "test.script",
"data": {"area_id": "area_if_else"},
}
],
@ -4330,7 +4330,7 @@ async def test_referenced_areas(hass: HomeAssistant) -> None:
{
"parallel": [
{
"service": "test.script",
"action": "test.script",
"data": {"area_id": "area_parallel"},
}
],
@ -4364,27 +4364,27 @@ async def test_referenced_entities(hass: HomeAssistant) -> None:
cv.SCRIPT_SCHEMA(
[
{
"service": "test.script",
"action": "test.script",
"data": {"entity_id": "light.service_not_list"},
},
{
"service": "test.script",
"action": "test.script",
"data": {"entity_id": ["light.service_list"]},
},
{
"service": "test.script",
"action": "test.script",
"data": {"entity_id": "{{ 'light.service_template' }}"},
},
{
"service": "test.script",
"action": "test.script",
"entity_id": "light.direct_entity_referenced",
},
{
"service": "test.script",
"action": "test.script",
"target": {"entity_id": "light.entity_in_target"},
},
{
"service": "test.script",
"action": "test.script",
"data_template": {"entity_id": "light.entity_in_data_template"},
},
{
@ -4392,7 +4392,7 @@ async def test_referenced_entities(hass: HomeAssistant) -> None:
"entity_id": "sensor.condition",
"state": "100",
},
{"service": "test.script", "data": {"without": "entity_id"}},
{"action": "test.script", "data": {"without": "entity_id"}},
{"scene": "scene.hello"},
{
"choose": [
@ -4400,7 +4400,7 @@ async def test_referenced_entities(hass: HomeAssistant) -> None:
"conditions": "{{ states.light.choice_1_cond == 'on' }}",
"sequence": [
{
"service": "test.script",
"action": "test.script",
"data": {"entity_id": "light.choice_1_seq"},
}
],
@ -4413,7 +4413,7 @@ async def test_referenced_entities(hass: HomeAssistant) -> None:
},
"sequence": [
{
"service": "test.script",
"action": "test.script",
"data": {"entity_id": "light.choice_2_seq"},
}
],
@ -4421,7 +4421,7 @@ async def test_referenced_entities(hass: HomeAssistant) -> None:
],
"default": [
{
"service": "test.script",
"action": "test.script",
"data": {"entity_id": "light.default_seq"},
}
],
@ -4432,13 +4432,13 @@ async def test_referenced_entities(hass: HomeAssistant) -> None:
"if": [],
"then": [
{
"service": "test.script",
"action": "test.script",
"data": {"entity_id": "light.if_then"},
}
],
"else": [
{
"service": "test.script",
"action": "test.script",
"data": {"entity_id": "light.if_else"},
}
],
@ -4446,7 +4446,7 @@ async def test_referenced_entities(hass: HomeAssistant) -> None:
{
"parallel": [
{
"service": "test.script",
"action": "test.script",
"data": {"entity_id": "light.parallel"},
}
],
@ -4491,19 +4491,19 @@ async def test_referenced_devices(hass: HomeAssistant) -> None:
"domain": "switch",
},
{
"service": "test.script",
"action": "test.script",
"data": {"device_id": "data-string-id"},
},
{
"service": "test.script",
"action": "test.script",
"data_template": {"device_id": "data-template-string-id"},
},
{
"service": "test.script",
"action": "test.script",
"target": {"device_id": "target-string-id"},
},
{
"service": "test.script",
"action": "test.script",
"target": {"device_id": ["target-list-id-1", "target-list-id-2"]},
},
{
@ -4515,7 +4515,7 @@ async def test_referenced_devices(hass: HomeAssistant) -> None:
),
"sequence": [
{
"service": "test.script",
"action": "test.script",
"target": {
"device_id": "choice-1-seq-device-target"
},
@ -4530,7 +4530,7 @@ async def test_referenced_devices(hass: HomeAssistant) -> None:
},
"sequence": [
{
"service": "test.script",
"action": "test.script",
"target": {
"device_id": "choice-2-seq-device-target"
},
@ -4540,7 +4540,7 @@ async def test_referenced_devices(hass: HomeAssistant) -> None:
],
"default": [
{
"service": "test.script",
"action": "test.script",
"target": {"device_id": "default-device-target"},
}
],
@ -4549,13 +4549,13 @@ async def test_referenced_devices(hass: HomeAssistant) -> None:
"if": [],
"then": [
{
"service": "test.script",
"action": "test.script",
"data": {"device_id": "if-then"},
}
],
"else": [
{
"service": "test.script",
"action": "test.script",
"data": {"device_id": "if-else"},
}
],
@ -4563,7 +4563,7 @@ async def test_referenced_devices(hass: HomeAssistant) -> None:
{
"parallel": [
{
"service": "test.script",
"action": "test.script",
"target": {"device_id": "parallel-device"},
}
],
@ -5104,7 +5104,7 @@ async def test_set_variable(
sequence = cv.SCRIPT_SCHEMA(
[
{"alias": alias, "variables": {"variable": "value"}},
{"service": "test.script", "data": {"value": "{{ variable }}"}},
{"action": "test.script", "data": {"value": "{{ variable }}"}},
]
)
script_obj = script.Script(hass, sequence, "test script", "test_domain")
@ -5143,9 +5143,9 @@ async def test_set_redefines_variable(
sequence = cv.SCRIPT_SCHEMA(
[
{"variables": {"variable": "1"}},
{"service": "test.script", "data": {"value": "{{ variable }}"}},
{"action": "test.script", "data": {"value": "{{ variable }}"}},
{"variables": {"variable": "{{ variable | int + 1 }}"}},
{"service": "test.script", "data": {"value": "{{ variable }}"}},
{"action": "test.script", "data": {"value": "{{ variable }}"}},
]
)
script_obj = script.Script(hass, sequence, "test script", "test_domain")
@ -5214,7 +5214,7 @@ async def test_validate_action_config(
}
configs = {
cv.SCRIPT_ACTION_CALL_SERVICE: {"service": "light.turn_on"},
cv.SCRIPT_ACTION_CALL_SERVICE: {"action": "light.turn_on"},
cv.SCRIPT_ACTION_DELAY: {"delay": 5},
cv.SCRIPT_ACTION_WAIT_TEMPLATE: {
"wait_template": "{{ states.light.kitchen.state == 'on' }}"
@ -5349,7 +5349,7 @@ async def test_embedded_wait_for_trigger_in_automation(hass: HomeAssistant) -> N
}
]
},
{"service": "test.script"},
{"action": "test.script"},
],
}
},
@ -5704,12 +5704,12 @@ async def test_continue_on_error(hass: HomeAssistant) -> None:
{"event": "test_event"},
{
"continue_on_error": True,
"service": "broken.service",
"action": "broken.service",
},
{"event": "test_event"},
{
"continue_on_error": False,
"service": "broken.service",
"action": "broken.service",
},
{"event": "test_event"},
]
@ -5786,7 +5786,7 @@ async def test_continue_on_error_automation_issue(hass: HomeAssistant) -> None:
[
{
"continue_on_error": True,
"service": "service.not_found",
"action": "service.not_found",
},
]
)
@ -5834,7 +5834,7 @@ async def test_continue_on_error_unknown_error(hass: HomeAssistant) -> None:
[
{
"continue_on_error": True,
"service": "some.service",
"action": "some.service",
},
]
)
@ -5884,7 +5884,7 @@ async def test_disabled_actions(
{
"alias": "Hello",
"enabled": enabled_value,
"service": "broken.service",
"action": "broken.service",
},
{
"alias": "World",
@ -6255,7 +6255,7 @@ async def test_disallowed_recursion(
context = Context()
calls = 0
alias = "event step"
sequence1 = cv.SCRIPT_SCHEMA({"alias": alias, "service": "test.call_script_2"})
sequence1 = cv.SCRIPT_SCHEMA({"alias": alias, "action": "test.call_script_2"})
script1_obj = script.Script(
hass,
sequence1,
@ -6265,7 +6265,7 @@ async def test_disallowed_recursion(
running_description="test script1",
)
sequence2 = cv.SCRIPT_SCHEMA({"alias": alias, "service": "test.call_script_3"})
sequence2 = cv.SCRIPT_SCHEMA({"alias": alias, "action": "test.call_script_3"})
script2_obj = script.Script(
hass,
sequence2,
@ -6275,7 +6275,7 @@ async def test_disallowed_recursion(
running_description="test script2",
)
sequence3 = cv.SCRIPT_SCHEMA({"alias": alias, "service": "test.call_script_1"})
sequence3 = cv.SCRIPT_SCHEMA({"alias": alias, "action": "test.call_script_1"})
script3_obj = script.Script(
hass,
sequence3,
@ -6315,3 +6315,43 @@ async def test_disallowed_recursion(
"- test_domain2.Test Name2\n"
"- test_domain3.Test Name3"
) in caplog.text
async def test_calling_service_backwards_compatible(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test the calling of a service with the service instead of the action key."""
context = Context()
calls = async_mock_service(hass, "test", "script")
alias = "service step"
sequence = cv.SCRIPT_SCHEMA(
{"alias": alias, "service": "test.script", "data": {"hello": "{{ 'world' }}"}}
)
script_obj = script.Script(hass, sequence, "Test Name", "test_domain")
await script_obj.async_run(context=context)
await hass.async_block_till_done()
assert len(calls) == 1
assert calls[0].context is context
assert calls[0].data.get("hello") == "world"
assert f"Executing step {alias}" in caplog.text
assert_action_trace(
{
"0": [
{
"result": {
"params": {
"domain": "test",
"service": "script",
"service_data": {"hello": "world"},
"target": {},
},
"running_script": False,
}
}
],
}
)

View file

@ -405,7 +405,7 @@ async def test_service_call(hass: HomeAssistant) -> None:
"""Test service call with templating."""
calls = async_mock_service(hass, "test_domain", "test_service")
config = {
"service": "{{ 'test_domain.test_service' }}",
"action": "{{ 'test_domain.test_service' }}",
"entity_id": "hello.world",
"data": {
"hello": "{{ 'goodbye' }}",
@ -435,7 +435,7 @@ async def test_service_call(hass: HomeAssistant) -> None:
}
config = {
"service": "{{ 'test_domain.test_service' }}",
"action": "{{ 'test_domain.test_service' }}",
"target": {
"area_id": ["area-42", "{{ 'area-51' }}"],
"device_id": ["abcdef", "{{ 'fedcba' }}"],
@ -455,7 +455,7 @@ async def test_service_call(hass: HomeAssistant) -> None:
}
config = {
"service": "{{ 'test_domain.test_service' }}",
"action": "{{ 'test_domain.test_service' }}",
"target": "{{ var_target }}",
}
@ -542,7 +542,7 @@ async def test_split_entity_string(hass: HomeAssistant) -> None:
await service.async_call_from_config(
hass,
{
"service": "test_domain.test_service",
"action": "test_domain.test_service",
"entity_id": "hello.world, sensor.beer",
},
)
@ -554,7 +554,7 @@ async def test_not_mutate_input(hass: HomeAssistant) -> None:
"""Test for immutable input."""
async_mock_service(hass, "test_domain", "test_service")
config = {
"service": "test_domain.test_service",
"action": "test_domain.test_service",
"entity_id": "hello.world, sensor.beer",
"data": {"hello": 1},
"data_template": {"nested": {"value": "{{ 1 + 1 }}"}},
@ -581,7 +581,7 @@ async def test_fail_silently_if_no_service(mock_log, hass: HomeAssistant) -> Non
await service.async_call_from_config(hass, {})
assert mock_log.call_count == 2
await service.async_call_from_config(hass, {"service": "invalid"})
await service.async_call_from_config(hass, {"action": "invalid"})
assert mock_log.call_count == 3
@ -597,7 +597,7 @@ async def test_service_call_entry_id(
assert entry.entity_id == "hello.world"
config = {
"service": "test_domain.test_service",
"action": "test_domain.test_service",
"target": {"entity_id": entry.id},
}
@ -613,7 +613,7 @@ async def test_service_call_all_none(hass: HomeAssistant, target) -> None:
calls = async_mock_service(hass, "test_domain", "test_service")
config = {
"service": "test_domain.test_service",
"action": "test_domain.test_service",
"target": {"entity_id": target},
}