Add support for "alias" in script steps device, device_condition, and conditions (#46647)
Co-authored-by: Donnie <donniekarnsinsb@hotmail.com>
This commit is contained in:
parent
3ad207a499
commit
2d70806035
5 changed files with 146 additions and 72 deletions
|
@ -888,9 +888,11 @@ def script_action(value: Any) -> dict:
|
|||
|
||||
SCRIPT_SCHEMA = vol.All(ensure_list, [script_action])
|
||||
|
||||
SCRIPT_ACTION_BASE_SCHEMA = {vol.Optional(CONF_ALIAS): string}
|
||||
|
||||
EVENT_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_ALIAS): string,
|
||||
**SCRIPT_ACTION_BASE_SCHEMA,
|
||||
vol.Required(CONF_EVENT): string,
|
||||
vol.Optional(CONF_EVENT_DATA): vol.All(dict, template_complex),
|
||||
vol.Optional(CONF_EVENT_DATA_TEMPLATE): vol.All(dict, template_complex),
|
||||
|
@ -900,7 +902,7 @@ EVENT_SCHEMA = vol.Schema(
|
|||
SERVICE_SCHEMA = vol.All(
|
||||
vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_ALIAS): string,
|
||||
**SCRIPT_ACTION_BASE_SCHEMA,
|
||||
vol.Exclusive(CONF_SERVICE, "service name"): vol.Any(
|
||||
service, dynamic_template
|
||||
),
|
||||
|
@ -920,9 +922,12 @@ NUMERIC_STATE_THRESHOLD_SCHEMA = vol.Any(
|
|||
vol.Coerce(float), vol.All(str, entity_domain("input_number"))
|
||||
)
|
||||
|
||||
CONDITION_BASE_SCHEMA = {vol.Optional(CONF_ALIAS): string}
|
||||
|
||||
NUMERIC_STATE_CONDITION_SCHEMA = vol.All(
|
||||
vol.Schema(
|
||||
{
|
||||
**CONDITION_BASE_SCHEMA,
|
||||
vol.Required(CONF_CONDITION): "numeric_state",
|
||||
vol.Required(CONF_ENTITY_ID): entity_ids,
|
||||
vol.Optional(CONF_ATTRIBUTE): str,
|
||||
|
@ -935,6 +940,7 @@ NUMERIC_STATE_CONDITION_SCHEMA = vol.All(
|
|||
)
|
||||
|
||||
STATE_CONDITION_BASE_SCHEMA = {
|
||||
**CONDITION_BASE_SCHEMA,
|
||||
vol.Required(CONF_CONDITION): "state",
|
||||
vol.Required(CONF_ENTITY_ID): entity_ids,
|
||||
vol.Optional(CONF_ATTRIBUTE): str,
|
||||
|
@ -975,6 +981,7 @@ def STATE_CONDITION_SCHEMA(value: Any) -> dict: # pylint: disable=invalid-name
|
|||
SUN_CONDITION_SCHEMA = vol.All(
|
||||
vol.Schema(
|
||||
{
|
||||
**CONDITION_BASE_SCHEMA,
|
||||
vol.Required(CONF_CONDITION): "sun",
|
||||
vol.Optional("before"): sun_event,
|
||||
vol.Optional("before_offset"): time_period,
|
||||
|
@ -989,6 +996,7 @@ SUN_CONDITION_SCHEMA = vol.All(
|
|||
|
||||
TEMPLATE_CONDITION_SCHEMA = vol.Schema(
|
||||
{
|
||||
**CONDITION_BASE_SCHEMA,
|
||||
vol.Required(CONF_CONDITION): "template",
|
||||
vol.Required(CONF_VALUE_TEMPLATE): template,
|
||||
}
|
||||
|
@ -997,6 +1005,7 @@ TEMPLATE_CONDITION_SCHEMA = vol.Schema(
|
|||
TIME_CONDITION_SCHEMA = vol.All(
|
||||
vol.Schema(
|
||||
{
|
||||
**CONDITION_BASE_SCHEMA,
|
||||
vol.Required(CONF_CONDITION): "time",
|
||||
"before": vol.Any(time, vol.All(str, entity_domain("input_datetime"))),
|
||||
"after": vol.Any(time, vol.All(str, entity_domain("input_datetime"))),
|
||||
|
@ -1008,6 +1017,7 @@ TIME_CONDITION_SCHEMA = vol.All(
|
|||
|
||||
ZONE_CONDITION_SCHEMA = vol.Schema(
|
||||
{
|
||||
**CONDITION_BASE_SCHEMA,
|
||||
vol.Required(CONF_CONDITION): "zone",
|
||||
vol.Required(CONF_ENTITY_ID): entity_ids,
|
||||
"zone": entity_ids,
|
||||
|
@ -1019,6 +1029,7 @@ ZONE_CONDITION_SCHEMA = vol.Schema(
|
|||
|
||||
AND_CONDITION_SCHEMA = vol.Schema(
|
||||
{
|
||||
**CONDITION_BASE_SCHEMA,
|
||||
vol.Required(CONF_CONDITION): "and",
|
||||
vol.Required(CONF_CONDITIONS): vol.All(
|
||||
ensure_list,
|
||||
|
@ -1030,6 +1041,7 @@ AND_CONDITION_SCHEMA = vol.Schema(
|
|||
|
||||
OR_CONDITION_SCHEMA = vol.Schema(
|
||||
{
|
||||
**CONDITION_BASE_SCHEMA,
|
||||
vol.Required(CONF_CONDITION): "or",
|
||||
vol.Required(CONF_CONDITIONS): vol.All(
|
||||
ensure_list,
|
||||
|
@ -1041,6 +1053,7 @@ OR_CONDITION_SCHEMA = vol.Schema(
|
|||
|
||||
NOT_CONDITION_SCHEMA = vol.Schema(
|
||||
{
|
||||
**CONDITION_BASE_SCHEMA,
|
||||
vol.Required(CONF_CONDITION): "not",
|
||||
vol.Required(CONF_CONDITIONS): vol.All(
|
||||
ensure_list,
|
||||
|
@ -1052,6 +1065,7 @@ NOT_CONDITION_SCHEMA = vol.Schema(
|
|||
|
||||
DEVICE_CONDITION_BASE_SCHEMA = vol.Schema(
|
||||
{
|
||||
**CONDITION_BASE_SCHEMA,
|
||||
vol.Required(CONF_CONDITION): "device",
|
||||
vol.Required(CONF_DEVICE_ID): str,
|
||||
vol.Required(CONF_DOMAIN): str,
|
||||
|
@ -1087,14 +1101,14 @@ TRIGGER_SCHEMA = vol.All(
|
|||
|
||||
_SCRIPT_DELAY_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_ALIAS): string,
|
||||
**SCRIPT_ACTION_BASE_SCHEMA,
|
||||
vol.Required(CONF_DELAY): positive_time_period_template,
|
||||
}
|
||||
)
|
||||
|
||||
_SCRIPT_WAIT_TEMPLATE_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_ALIAS): string,
|
||||
**SCRIPT_ACTION_BASE_SCHEMA,
|
||||
vol.Required(CONF_WAIT_TEMPLATE): template,
|
||||
vol.Optional(CONF_TIMEOUT): positive_time_period_template,
|
||||
vol.Optional(CONF_CONTINUE_ON_TIMEOUT): boolean,
|
||||
|
@ -1102,16 +1116,22 @@ _SCRIPT_WAIT_TEMPLATE_SCHEMA = vol.Schema(
|
|||
)
|
||||
|
||||
DEVICE_ACTION_BASE_SCHEMA = vol.Schema(
|
||||
{vol.Required(CONF_DEVICE_ID): string, vol.Required(CONF_DOMAIN): str}
|
||||
{
|
||||
**SCRIPT_ACTION_BASE_SCHEMA,
|
||||
vol.Required(CONF_DEVICE_ID): string,
|
||||
vol.Required(CONF_DOMAIN): str,
|
||||
}
|
||||
)
|
||||
|
||||
DEVICE_ACTION_SCHEMA = DEVICE_ACTION_BASE_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
_SCRIPT_SCENE_SCHEMA = vol.Schema({vol.Required(CONF_SCENE): entity_domain("scene")})
|
||||
_SCRIPT_SCENE_SCHEMA = vol.Schema(
|
||||
{**SCRIPT_ACTION_BASE_SCHEMA, vol.Required(CONF_SCENE): entity_domain("scene")}
|
||||
)
|
||||
|
||||
_SCRIPT_REPEAT_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_ALIAS): string,
|
||||
**SCRIPT_ACTION_BASE_SCHEMA,
|
||||
vol.Required(CONF_REPEAT): vol.All(
|
||||
{
|
||||
vol.Exclusive(CONF_COUNT, "repeat"): vol.Any(vol.Coerce(int), template),
|
||||
|
@ -1130,11 +1150,12 @@ _SCRIPT_REPEAT_SCHEMA = vol.Schema(
|
|||
|
||||
_SCRIPT_CHOOSE_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_ALIAS): string,
|
||||
**SCRIPT_ACTION_BASE_SCHEMA,
|
||||
vol.Required(CONF_CHOOSE): vol.All(
|
||||
ensure_list,
|
||||
[
|
||||
{
|
||||
vol.Optional(CONF_ALIAS): string,
|
||||
vol.Required(CONF_CONDITIONS): vol.All(
|
||||
ensure_list, [CONDITION_SCHEMA]
|
||||
),
|
||||
|
@ -1148,7 +1169,7 @@ _SCRIPT_CHOOSE_SCHEMA = vol.Schema(
|
|||
|
||||
_SCRIPT_WAIT_FOR_TRIGGER_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_ALIAS): string,
|
||||
**SCRIPT_ACTION_BASE_SCHEMA,
|
||||
vol.Required(CONF_WAIT_FOR_TRIGGER): TRIGGER_SCHEMA,
|
||||
vol.Optional(CONF_TIMEOUT): positive_time_period_template,
|
||||
vol.Optional(CONF_CONTINUE_ON_TIMEOUT): boolean,
|
||||
|
@ -1157,7 +1178,7 @@ _SCRIPT_WAIT_FOR_TRIGGER_SCHEMA = vol.Schema(
|
|||
|
||||
_SCRIPT_SET_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_ALIAS): string,
|
||||
**SCRIPT_ACTION_BASE_SCHEMA,
|
||||
vol.Required(CONF_VARIABLES): SCRIPT_VARIABLES_SCHEMA,
|
||||
}
|
||||
)
|
||||
|
|
|
@ -18,7 +18,7 @@ from typing import (
|
|||
cast,
|
||||
)
|
||||
|
||||
from async_timeout import timeout
|
||||
import async_timeout
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import exceptions
|
||||
|
@ -235,6 +235,13 @@ class _ScriptRun:
|
|||
msg, *args, level=level, **kwargs
|
||||
)
|
||||
|
||||
def _step_log(self, default_message, timeout=None):
|
||||
self._script.last_action = self._action.get(CONF_ALIAS, default_message)
|
||||
_timeout = (
|
||||
"" if timeout is None else f" (timeout: {timedelta(seconds=timeout)})"
|
||||
)
|
||||
self._log("Executing step %s%s", self._script.last_action, _timeout)
|
||||
|
||||
async def async_run(self) -> None:
|
||||
"""Run script."""
|
||||
try:
|
||||
|
@ -327,13 +334,12 @@ class _ScriptRun:
|
|||
"""Handle delay."""
|
||||
delay = self._get_pos_time_period_template(CONF_DELAY)
|
||||
|
||||
self._script.last_action = self._action.get(CONF_ALIAS, f"delay {delay}")
|
||||
self._log("Executing step %s", self._script.last_action)
|
||||
self._step_log(f"delay {delay}")
|
||||
|
||||
delay = delay.total_seconds()
|
||||
self._changed()
|
||||
try:
|
||||
async with timeout(delay):
|
||||
async with async_timeout.timeout(delay):
|
||||
await self._stop.wait()
|
||||
except asyncio.TimeoutError:
|
||||
pass
|
||||
|
@ -341,18 +347,13 @@ class _ScriptRun:
|
|||
async def _async_wait_template_step(self):
|
||||
"""Handle a wait template."""
|
||||
if CONF_TIMEOUT in self._action:
|
||||
delay = self._get_pos_time_period_template(CONF_TIMEOUT).total_seconds()
|
||||
timeout = self._get_pos_time_period_template(CONF_TIMEOUT).total_seconds()
|
||||
else:
|
||||
delay = None
|
||||
timeout = None
|
||||
|
||||
self._script.last_action = self._action.get(CONF_ALIAS, "wait template")
|
||||
self._log(
|
||||
"Executing step %s%s",
|
||||
self._script.last_action,
|
||||
"" if delay is None else f" (timeout: {timedelta(seconds=delay)})",
|
||||
)
|
||||
self._step_log("wait template", timeout)
|
||||
|
||||
self._variables["wait"] = {"remaining": delay, "completed": False}
|
||||
self._variables["wait"] = {"remaining": timeout, "completed": False}
|
||||
|
||||
wait_template = self._action[CONF_WAIT_TEMPLATE]
|
||||
wait_template.hass = self._hass
|
||||
|
@ -366,7 +367,7 @@ class _ScriptRun:
|
|||
def async_script_wait(entity_id, from_s, to_s):
|
||||
"""Handle script after template condition is true."""
|
||||
self._variables["wait"] = {
|
||||
"remaining": to_context.remaining if to_context else delay,
|
||||
"remaining": to_context.remaining if to_context else timeout,
|
||||
"completed": True,
|
||||
}
|
||||
done.set()
|
||||
|
@ -382,7 +383,7 @@ class _ScriptRun:
|
|||
self._hass.async_create_task(flag.wait()) for flag in (self._stop, done)
|
||||
]
|
||||
try:
|
||||
async with timeout(delay) as to_context:
|
||||
async with async_timeout.timeout(timeout) as to_context:
|
||||
await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
|
||||
except asyncio.TimeoutError as ex:
|
||||
if not self._action.get(CONF_CONTINUE_ON_TIMEOUT, True):
|
||||
|
@ -431,8 +432,7 @@ class _ScriptRun:
|
|||
|
||||
async def _async_call_service_step(self):
|
||||
"""Call the service specified in the action."""
|
||||
self._script.last_action = self._action.get(CONF_ALIAS, "call service")
|
||||
self._log("Executing step %s", self._script.last_action)
|
||||
self._step_log("call service")
|
||||
|
||||
params = service.async_prepare_call_from_config(
|
||||
self._hass, self._action, self._variables
|
||||
|
@ -467,8 +467,7 @@ class _ScriptRun:
|
|||
|
||||
async def _async_device_step(self):
|
||||
"""Perform the device automation specified in the action."""
|
||||
self._script.last_action = self._action.get(CONF_ALIAS, "device automation")
|
||||
self._log("Executing step %s", self._script.last_action)
|
||||
self._step_log("device automation")
|
||||
platform = await device_automation.async_get_device_automation_platform(
|
||||
self._hass, self._action[CONF_DOMAIN], "action"
|
||||
)
|
||||
|
@ -478,8 +477,7 @@ class _ScriptRun:
|
|||
|
||||
async def _async_scene_step(self):
|
||||
"""Activate the scene specified in the action."""
|
||||
self._script.last_action = self._action.get(CONF_ALIAS, "activate scene")
|
||||
self._log("Executing step %s", self._script.last_action)
|
||||
self._step_log("activate scene")
|
||||
await self._hass.services.async_call(
|
||||
scene.DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
|
@ -490,10 +488,7 @@ class _ScriptRun:
|
|||
|
||||
async def _async_event_step(self):
|
||||
"""Fire an event."""
|
||||
self._script.last_action = self._action.get(
|
||||
CONF_ALIAS, self._action[CONF_EVENT]
|
||||
)
|
||||
self._log("Executing step %s", self._script.last_action)
|
||||
self._step_log(self._action.get(CONF_ALIAS, self._action[CONF_EVENT]))
|
||||
event_data = {}
|
||||
for conf in [CONF_EVENT_DATA, CONF_EVENT_DATA_TEMPLATE]:
|
||||
if conf not in self._action:
|
||||
|
@ -627,25 +622,20 @@ class _ScriptRun:
|
|||
async def _async_wait_for_trigger_step(self):
|
||||
"""Wait for a trigger event."""
|
||||
if CONF_TIMEOUT in self._action:
|
||||
delay = self._get_pos_time_period_template(CONF_TIMEOUT).total_seconds()
|
||||
timeout = self._get_pos_time_period_template(CONF_TIMEOUT).total_seconds()
|
||||
else:
|
||||
delay = None
|
||||
timeout = None
|
||||
|
||||
self._script.last_action = self._action.get(CONF_ALIAS, "wait for trigger")
|
||||
self._log(
|
||||
"Executing step %s%s",
|
||||
self._script.last_action,
|
||||
"" if delay is None else f" (timeout: {timedelta(seconds=delay)})",
|
||||
)
|
||||
self._step_log("wait for trigger", timeout)
|
||||
|
||||
variables = {**self._variables}
|
||||
self._variables["wait"] = {"remaining": delay, "trigger": None}
|
||||
self._variables["wait"] = {"remaining": timeout, "trigger": None}
|
||||
|
||||
done = asyncio.Event()
|
||||
|
||||
async def async_done(variables, context=None):
|
||||
self._variables["wait"] = {
|
||||
"remaining": to_context.remaining if to_context else delay,
|
||||
"remaining": to_context.remaining if to_context else timeout,
|
||||
"trigger": variables["trigger"],
|
||||
}
|
||||
done.set()
|
||||
|
@ -671,7 +661,7 @@ class _ScriptRun:
|
|||
self._hass.async_create_task(flag.wait()) for flag in (self._stop, done)
|
||||
]
|
||||
try:
|
||||
async with timeout(delay) as to_context:
|
||||
async with async_timeout.timeout(timeout) as to_context:
|
||||
await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
|
||||
except asyncio.TimeoutError as ex:
|
||||
if not self._action.get(CONF_CONTINUE_ON_TIMEOUT, True):
|
||||
|
@ -685,8 +675,7 @@ class _ScriptRun:
|
|||
|
||||
async def _async_variables_step(self):
|
||||
"""Set a variable value."""
|
||||
self._script.last_action = self._action.get(CONF_ALIAS, "setting variables")
|
||||
self._log("Executing step %s", self._script.last_action)
|
||||
self._step_log("setting variables")
|
||||
self._variables = self._action[CONF_VARIABLES].async_render(
|
||||
self._hass, self._variables, render_as_defaults=False
|
||||
)
|
||||
|
@ -1111,10 +1100,11 @@ class Script:
|
|||
await self._async_get_condition(config)
|
||||
for config in choice.get(CONF_CONDITIONS, [])
|
||||
]
|
||||
choice_name = choice.get(CONF_ALIAS, f"choice {idx}")
|
||||
sub_script = Script(
|
||||
self._hass,
|
||||
choice[CONF_SEQUENCE],
|
||||
f"{self.name}: {step_name}: choice {idx}",
|
||||
f"{self.name}: {step_name}: {choice_name}",
|
||||
self.domain,
|
||||
running_description=self.running_description,
|
||||
script_mode=SCRIPT_MODE_PARALLEL,
|
||||
|
|
|
@ -34,6 +34,7 @@ async def test_and_condition(hass):
|
|||
test = await condition.async_from_config(
|
||||
hass,
|
||||
{
|
||||
"alias": "And Condition",
|
||||
"condition": "and",
|
||||
"conditions": [
|
||||
{
|
||||
|
@ -71,6 +72,7 @@ async def test_and_condition_with_template(hass):
|
|||
"condition": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"alias": "Template Condition",
|
||||
"condition": "template",
|
||||
"value_template": '{{ states.sensor.temperature.state == "100" }}',
|
||||
},
|
||||
|
@ -98,6 +100,7 @@ async def test_or_condition(hass):
|
|||
test = await condition.async_from_config(
|
||||
hass,
|
||||
{
|
||||
"alias": "Or Condition",
|
||||
"condition": "or",
|
||||
"conditions": [
|
||||
{
|
||||
|
@ -159,6 +162,7 @@ async def test_not_condition(hass):
|
|||
test = await condition.async_from_config(
|
||||
hass,
|
||||
{
|
||||
"alias": "Not Condition",
|
||||
"condition": "not",
|
||||
"conditions": [
|
||||
{
|
||||
|
@ -226,36 +230,45 @@ async def test_not_condition_with_template(hass):
|
|||
|
||||
async def test_time_window(hass):
|
||||
"""Test time condition windows."""
|
||||
sixam = dt.parse_time("06:00:00")
|
||||
sixpm = dt.parse_time("18:00:00")
|
||||
sixam = "06:00:00"
|
||||
sixpm = "18:00:00"
|
||||
|
||||
test1 = await condition.async_from_config(
|
||||
hass,
|
||||
{"alias": "Time Cond", "condition": "time", "after": sixam, "before": sixpm},
|
||||
)
|
||||
test2 = await condition.async_from_config(
|
||||
hass,
|
||||
{"alias": "Time Cond", "condition": "time", "after": sixpm, "before": sixam},
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.condition.dt_util.now",
|
||||
return_value=dt.now().replace(hour=3),
|
||||
):
|
||||
assert not condition.time(hass, after=sixam, before=sixpm)
|
||||
assert condition.time(hass, after=sixpm, before=sixam)
|
||||
assert not test1(hass)
|
||||
assert test2(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.condition.dt_util.now",
|
||||
return_value=dt.now().replace(hour=9),
|
||||
):
|
||||
assert condition.time(hass, after=sixam, before=sixpm)
|
||||
assert not condition.time(hass, after=sixpm, before=sixam)
|
||||
assert test1(hass)
|
||||
assert not test2(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.condition.dt_util.now",
|
||||
return_value=dt.now().replace(hour=15),
|
||||
):
|
||||
assert condition.time(hass, after=sixam, before=sixpm)
|
||||
assert not condition.time(hass, after=sixpm, before=sixam)
|
||||
assert test1(hass)
|
||||
assert not test2(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.condition.dt_util.now",
|
||||
return_value=dt.now().replace(hour=21),
|
||||
):
|
||||
assert not condition.time(hass, after=sixam, before=sixpm)
|
||||
assert condition.time(hass, after=sixpm, before=sixam)
|
||||
assert not test1(hass)
|
||||
assert test2(hass)
|
||||
|
||||
|
||||
async def test_time_using_input_datetime(hass):
|
||||
|
@ -439,6 +452,7 @@ async def test_multiple_states(hass):
|
|||
"condition": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"alias": "State Condition",
|
||||
"condition": "state",
|
||||
"entity_id": "sensor.temperature",
|
||||
"state": ["100", "200"],
|
||||
|
@ -709,6 +723,7 @@ async def test_numeric_state_multiple_entities(hass):
|
|||
"condition": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"alias": "Numeric State Condition",
|
||||
"condition": "numeric_state",
|
||||
"entity_id": ["sensor.temperature_1", "sensor.temperature_2"],
|
||||
"below": 50,
|
||||
|
@ -911,6 +926,7 @@ async def test_zone_multiple_entities(hass):
|
|||
"condition": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"alias": "Zone Condition",
|
||||
"condition": "zone",
|
||||
"entity_id": ["device_tracker.person_1", "device_tracker.person_2"],
|
||||
"zone": "zone.home",
|
||||
|
|
|
@ -358,6 +358,11 @@ def test_service_schema():
|
|||
"service": "homeassistant.turn_on",
|
||||
"entity_id": ["light.kitchen", "light.ceiling"],
|
||||
},
|
||||
{
|
||||
"service": "light.turn_on",
|
||||
"entity_id": "all",
|
||||
"alias": "turn on kitchen lights",
|
||||
},
|
||||
)
|
||||
for value in options:
|
||||
cv.SERVICE_SCHEMA(value)
|
||||
|
|
|
@ -50,7 +50,10 @@ async def test_firing_event_basic(hass, caplog):
|
|||
context = Context()
|
||||
events = async_capture_events(hass, event)
|
||||
|
||||
sequence = cv.SCRIPT_SCHEMA({"event": event, "event_data": {"hello": "world"}})
|
||||
alias = "event step"
|
||||
sequence = cv.SCRIPT_SCHEMA(
|
||||
{"alias": alias, "event": event, "event_data": {"hello": "world"}}
|
||||
)
|
||||
script_obj = script.Script(
|
||||
hass, sequence, "Test Name", "test_domain", running_description="test script"
|
||||
)
|
||||
|
@ -63,6 +66,7 @@ async def test_firing_event_basic(hass, caplog):
|
|||
assert events[0].data.get("hello") == "world"
|
||||
assert ".test_name:" in caplog.text
|
||||
assert "Test Name: Running test script" in caplog.text
|
||||
assert f"Executing step {alias}" in caplog.text
|
||||
|
||||
|
||||
async def test_firing_event_template(hass):
|
||||
|
@ -107,12 +111,15 @@ async def test_firing_event_template(hass):
|
|||
}
|
||||
|
||||
|
||||
async def test_calling_service_basic(hass):
|
||||
async def test_calling_service_basic(hass, caplog):
|
||||
"""Test the calling of a service."""
|
||||
context = Context()
|
||||
calls = async_mock_service(hass, "test", "script")
|
||||
|
||||
sequence = cv.SCRIPT_SCHEMA({"service": "test.script", "data": {"hello": "world"}})
|
||||
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)
|
||||
|
@ -121,6 +128,7 @@ async def test_calling_service_basic(hass):
|
|||
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
|
||||
|
||||
|
||||
async def test_calling_service_template(hass):
|
||||
|
@ -250,12 +258,13 @@ async def test_multiple_runs_no_wait(hass):
|
|||
assert len(calls) == 4
|
||||
|
||||
|
||||
async def test_activating_scene(hass):
|
||||
async def test_activating_scene(hass, caplog):
|
||||
"""Test the activation of a scene."""
|
||||
context = Context()
|
||||
calls = async_mock_service(hass, scene.DOMAIN, SERVICE_TURN_ON)
|
||||
|
||||
sequence = cv.SCRIPT_SCHEMA({"scene": "scene.hello"})
|
||||
alias = "scene step"
|
||||
sequence = cv.SCRIPT_SCHEMA({"alias": alias, "scene": "scene.hello"})
|
||||
script_obj = script.Script(hass, sequence, "Test Name", "test_domain")
|
||||
|
||||
await script_obj.async_run(context=context)
|
||||
|
@ -264,6 +273,7 @@ async def test_activating_scene(hass):
|
|||
assert len(calls) == 1
|
||||
assert calls[0].context is context
|
||||
assert calls[0].data.get(ATTR_ENTITY_ID) == "scene.hello"
|
||||
assert f"Executing step {alias}" in caplog.text
|
||||
|
||||
|
||||
@pytest.mark.parametrize("count", [1, 3])
|
||||
|
@ -1063,14 +1073,16 @@ async def test_condition_warning(hass, caplog):
|
|||
assert len(events) == 1
|
||||
|
||||
|
||||
async def test_condition_basic(hass):
|
||||
async def test_condition_basic(hass, caplog):
|
||||
"""Test if we can use conditions in a script."""
|
||||
event = "test_event"
|
||||
events = async_capture_events(hass, event)
|
||||
alias = "condition step"
|
||||
sequence = cv.SCRIPT_SCHEMA(
|
||||
[
|
||||
{"event": event},
|
||||
{
|
||||
"alias": alias,
|
||||
"condition": "template",
|
||||
"value_template": "{{ states.test.entity.state == 'hello' }}",
|
||||
},
|
||||
|
@ -1083,6 +1095,8 @@ async def test_condition_basic(hass):
|
|||
await script_obj.async_run(context=Context())
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert f"Test condition {alias}: True" in caplog.text
|
||||
caplog.clear()
|
||||
assert len(events) == 2
|
||||
|
||||
hass.states.async_set("test.entity", "goodbye")
|
||||
|
@ -1090,6 +1104,7 @@ async def test_condition_basic(hass):
|
|||
await script_obj.async_run(context=Context())
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert f"Test condition {alias}: False" in caplog.text
|
||||
assert len(events) == 3
|
||||
|
||||
|
||||
|
@ -1140,14 +1155,16 @@ async def test_condition_all_cached(hass):
|
|||
assert len(script_obj._config_cache) == 2
|
||||
|
||||
|
||||
async def test_repeat_count(hass):
|
||||
async def test_repeat_count(hass, caplog):
|
||||
"""Test repeat action w/ count option."""
|
||||
event = "test_event"
|
||||
events = async_capture_events(hass, event)
|
||||
count = 3
|
||||
|
||||
alias = "condition step"
|
||||
sequence = cv.SCRIPT_SCHEMA(
|
||||
{
|
||||
"alias": alias,
|
||||
"repeat": {
|
||||
"count": count,
|
||||
"sequence": {
|
||||
|
@ -1158,7 +1175,7 @@ async def test_repeat_count(hass):
|
|||
"last": "{{ repeat.last }}",
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
script_obj = script.Script(hass, sequence, "Test Name", "test_domain")
|
||||
|
@ -1171,6 +1188,7 @@ async def test_repeat_count(hass):
|
|||
assert event.data.get("first") == (index == 0)
|
||||
assert event.data.get("index") == index + 1
|
||||
assert event.data.get("last") == (index == count - 1)
|
||||
assert caplog.text.count(f"Repeating {alias}") == count
|
||||
|
||||
|
||||
@pytest.mark.parametrize("condition", ["while", "until"])
|
||||
|
@ -1470,26 +1488,44 @@ async def test_choose_warning(hass, caplog):
|
|||
|
||||
|
||||
@pytest.mark.parametrize("var,result", [(1, "first"), (2, "second"), (3, "default")])
|
||||
async def test_choose(hass, var, result):
|
||||
async def test_choose(hass, caplog, var, result):
|
||||
"""Test choose action."""
|
||||
event = "test_event"
|
||||
events = async_capture_events(hass, event)
|
||||
alias = "choose step"
|
||||
choice = {1: "choice one", 2: "choice two", 3: None}
|
||||
aliases = {1: "sequence one", 2: "sequence two", 3: "default sequence"}
|
||||
sequence = cv.SCRIPT_SCHEMA(
|
||||
{
|
||||
"alias": alias,
|
||||
"choose": [
|
||||
{
|
||||
"alias": choice[1],
|
||||
"conditions": {
|
||||
"condition": "template",
|
||||
"value_template": "{{ var == 1 }}",
|
||||
},
|
||||
"sequence": {"event": event, "event_data": {"choice": "first"}},
|
||||
"sequence": {
|
||||
"alias": aliases[1],
|
||||
"event": event,
|
||||
"event_data": {"choice": "first"},
|
||||
},
|
||||
},
|
||||
{
|
||||
"alias": choice[2],
|
||||
"conditions": "{{ var == 2 }}",
|
||||
"sequence": {"event": event, "event_data": {"choice": "second"}},
|
||||
"sequence": {
|
||||
"alias": aliases[2],
|
||||
"event": event,
|
||||
"event_data": {"choice": "second"},
|
||||
},
|
||||
},
|
||||
],
|
||||
"default": {"event": event, "event_data": {"choice": "default"}},
|
||||
"default": {
|
||||
"alias": aliases[3],
|
||||
"event": event,
|
||||
"event_data": {"choice": "default"},
|
||||
},
|
||||
}
|
||||
)
|
||||
script_obj = script.Script(hass, sequence, "Test Name", "test_domain")
|
||||
|
@ -1499,6 +1535,10 @@ async def test_choose(hass, var, result):
|
|||
|
||||
assert len(events) == 1
|
||||
assert events[0].data["choice"] == result
|
||||
expected_choice = choice[var]
|
||||
if var == 3:
|
||||
expected_choice = "default"
|
||||
assert f"{alias}: {expected_choice}: Executing step {aliases[var]}" in caplog.text
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
@ -2115,9 +2155,10 @@ async def test_started_action(hass, caplog):
|
|||
|
||||
async def test_set_variable(hass, caplog):
|
||||
"""Test setting variables in scripts."""
|
||||
alias = "variables step"
|
||||
sequence = cv.SCRIPT_SCHEMA(
|
||||
[
|
||||
{"variables": {"variable": "value"}},
|
||||
{"alias": alias, "variables": {"variable": "value"}},
|
||||
{"service": "test.script", "data": {"value": "{{ variable }}"}},
|
||||
]
|
||||
)
|
||||
|
@ -2129,6 +2170,7 @@ async def test_set_variable(hass, caplog):
|
|||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_calls[0].data["value"] == "value"
|
||||
assert f"Executing step {alias}" in caplog.text
|
||||
|
||||
|
||||
async def test_set_redefines_variable(hass, caplog):
|
||||
|
|
Loading…
Add table
Reference in a new issue