async def test_repeat_conditional(hass, condition): """Test repeat action w/ while option.""" event = "test_event" events = async_capture_events(hass, event) count = 3 sequence = { "repeat": { "sequence": [ { "event": event, "event_data_template": { "first": "{{ repeat.first }}", "index": "{{ repeat.index }}", }, }, {"wait_template": "{{ is_state('sensor.test', 'next') }}"}, {"wait_template": "{{ not is_state('sensor.test', 'next') }}"}, ], } } if condition == "while": sequence["repeat"]["while"] = { "condition": "template", "value_template": "{{ not is_state('sensor.test', 'done') }}", } else: sequence["repeat"]["until"] = { "condition": "template", "value_template": "{{ is_state('sensor.test', 'done') }}", } script_obj = script.Script(hass, cv.SCRIPT_SCHEMA(sequence)) wait_started = async_watch_for_action(script_obj, "wait") hass.states.async_set("sensor.test", "1") hass.async_create_task(script_obj.async_run()) try: for index in range(2, count + 1): await asyncio.wait_for(wait_started.wait(), 1) wait_started.clear() hass.states.async_set("sensor.test", "next") await asyncio.wait_for(wait_started.wait(), 1) wait_started.clear() hass.states.async_set("sensor.test", index) await asyncio.wait_for(wait_started.wait(), 1) hass.states.async_set("sensor.test", "next") await asyncio.wait_for(wait_started.wait(), 1) wait_started.clear() hass.states.async_set("sensor.test", "done") await asyncio.wait_for(hass.async_block_till_done(), 1) except asyncio.TimeoutError: await script_obj.async_stop() raise assert len(events) == count for index, event in enumerate(events): assert event.data.get("first") == str(index == 0) assert event.data.get("index") == str(index + 1)
async def test_script_mode_1(hass, caplog, script_mode, expectation, messages): """Test overlapping runs with script_mode='ignore'.""" event = "test_event" events = async_capture_events(hass, event) sequence = cv.SCRIPT_SCHEMA([ { "event": event, "event_data": { "value": 1 } }, { "wait_template": "{{ states.switch.test.state == 'off' }}" }, { "event": event, "event_data": { "value": 2 } }, ]) logger = logging.getLogger("TEST") script_obj = script.Script(hass, sequence, script_mode=script_mode, logger=logger) wait_started_flag = async_watch_for_action(script_obj, "wait") try: hass.states.async_set("switch.test", "on") hass.async_create_task(script_obj.async_run()) await asyncio.wait_for(wait_started_flag.wait(), 1) assert script_obj.is_running assert len(events) == 1 assert events[0].data["value"] == 1 # Start second run of script while first run is suspended in wait_template. with expectation: await script_obj.async_run() assert script_obj.is_running assert all( any(rec.levelname == "INFO" and rec.name == "TEST" and message in rec.message for rec in caplog.records) for message in messages) except (AssertionError, asyncio.TimeoutError): await script_obj.async_stop() raise else: hass.states.async_set("switch.test", "off") await hass.async_block_till_done() assert not script_obj.is_running assert len(events) == 2 assert events[1].data["value"] == 2
async def test_multiple_runs_delay(hass, mock_timeout, script_mode): """Test multiple runs with delay in script.""" event = "test_event" events = async_capture_events(hass, event) delay = timedelta(seconds=5) sequence = cv.SCRIPT_SCHEMA([ { "event": event, "event_data": { "value": 1 } }, { "delay": delay }, { "event": event, "event_data": { "value": 2 } }, ]) script_obj = script.Script(hass, sequence, script_mode=script_mode) delay_started_flag = async_watch_for_action(script_obj, "delay") try: hass.async_create_task(script_obj.async_run()) await asyncio.wait_for(delay_started_flag.wait(), 1) assert script_obj.is_running assert len(events) == 1 assert events[-1].data["value"] == 1 except (AssertionError, asyncio.TimeoutError): await script_obj.async_stop() raise else: # Start second run of script while first run is in a delay. if script_mode == "legacy": await script_obj.async_run() else: script_obj.sequence[1]["alias"] = "delay run 2" delay_started_flag = async_watch_for_action( script_obj, "delay run 2") hass.async_create_task(script_obj.async_run()) await asyncio.wait_for(delay_started_flag.wait(), 1) async_fire_time_changed(hass, dt_util.utcnow() + delay) await hass.async_block_till_done() assert not script_obj.is_running if script_mode == "legacy": assert len(events) == 2 else: assert len(events) == 4 assert events[-3].data["value"] == 1 assert events[-2].data["value"] == 2 assert events[-1].data["value"] == 2
def __init__(self, hass, intents): """Initialize Alexa view.""" super().__init__(hass) for name, intent in intents.items(): if CONF_ACTION in intent: intent[CONF_ACTION] = script.Script( hass, intent[CONF_ACTION], "Alexa intent {}".format(name)) self.intents = intents
def _get_action(hass, config, name): """Return an action based on a configuration.""" script_obj = script.Script(hass, config, name) def action(variables=None): """Action to be executed.""" _LOGGER.info('Executing %s', name) logbook.log_entry(hass, name, 'has been triggered', DOMAIN) script_obj.run(variables) return action
async def async_setup(hass, config): """Activate Alexa component.""" intents = copy.deepcopy(config[DOMAIN]) template.attach(hass, intents) for intent_type, conf in intents.items(): if CONF_ACTION in conf: conf[CONF_ACTION] = script.Script(hass, conf[CONF_ACTION], f"Intent Script {intent_type}") intent.async_register(hass, ScriptIntentHandler(intent_type, conf)) return True
async def test_propagate_error_service_not_found(hass, script_mode): """Test that a script aborts when a service is not found.""" event = "test_event" events = async_capture_events(hass, event) sequence = cv.SCRIPT_SCHEMA([{"service": "test.script"}, {"event": event}]) script_obj = script.Script(hass, sequence, script_mode=script_mode) with pytest.raises(exceptions.ServiceNotFound): await script_obj.async_run() assert len(events) == 0 assert not script_obj.is_running
def __init__(self, hass, intents): """Initialize the intent handler.""" self.hass = hass intents = copy.deepcopy(intents) template.attach(hass, intents) for name, intent in intents.items(): if CONF_ACTION in intent: intent[CONF_ACTION] = script.Script( hass, intent[CONF_ACTION], "Snips intent {}".format(name)) self.intents = intents
def _async_get_action(hass, config, name): """Return an action based on a configuration.""" script_obj = script.Script(hass, config, name) async def action(entity_id, variables, context): """Execute an action.""" _LOGGER.info('Executing %s', name) hass.components.logbook.async_log_entry(name, 'has been triggered', DOMAIN, entity_id) await script_obj.async_run(variables, context) return action
async def test_multiple_runs_wait_template(hass, script_mode): """Test multiple runs with wait_template in script.""" event = "test_event" events = async_capture_events(hass, event) sequence = cv.SCRIPT_SCHEMA([ { "event": event, "event_data": { "value": 1 } }, { "wait_template": "{{ states.switch.test.state == 'off' }}" }, { "event": event, "event_data": { "value": 2 } }, ]) script_obj = script.Script(hass, sequence, script_mode=script_mode) wait_started_flag = async_watch_for_action(script_obj, "wait") try: hass.states.async_set("switch.test", "on") hass.async_create_task(script_obj.async_run()) await asyncio.wait_for(wait_started_flag.wait(), 1) assert script_obj.is_running assert len(events) == 1 assert events[-1].data["value"] == 1 except (AssertionError, asyncio.TimeoutError): await script_obj.async_stop() raise else: # Start second run of script while first run is in wait_template. if script_mode == "legacy": await script_obj.async_run() else: hass.async_create_task(script_obj.async_run()) hass.states.async_set("switch.test", "off") await hass.async_block_till_done() assert not script_obj.is_running if script_mode == "legacy": assert len(events) == 2 else: assert len(events) == 4 assert events[-3].data["value"] == 1 assert events[-2].data["value"] == 2 assert events[-1].data["value"] == 2
def _async_get_action(hass, config, name): """Return an action based on a configuration.""" script_obj = script.Script(hass, config, name) @asyncio.coroutine def action(entity_id, variables): """Action to be executed.""" _LOGGER.info('Executing %s', name) logbook.async_log_entry(hass, name, 'has been triggered', DOMAIN, entity_id) yield from script_obj.async_run(variables) return action
async def test_passing_variables_to_script(hass): """Test if we can pass variables to script.""" calls = [] @callback def record_call(service): """Add recorded event to set.""" calls.append(service) hass.services.async_register("test", "script", record_call) script_obj = script.Script( hass, cv.SCRIPT_SCHEMA([ { "service": "test.script", "data_template": { "hello": "{{ greeting }}" }, }, { "delay": "{{ delay_period }}" }, { "service": "test.script", "data_template": { "hello": "{{ greeting2 }}" }, }, ]), ) await script_obj.async_run({ "greeting": "world", "greeting2": "universe", "delay_period": "00:00:05" }) await hass.async_block_till_done() assert script_obj.is_running assert len(calls) == 1 assert calls[-1].data["hello"] == "world" future = dt_util.utcnow() + timedelta(seconds=5) async_fire_time_changed(hass, future) await hass.async_block_till_done() assert not script_obj.is_running assert len(calls) == 2 assert calls[-1].data["hello"] == "universe"
def setup(hass, config): """Activate Alexa component.""" intents = config[DOMAIN].get(CONF_INTENTS, {}) for name, intent in intents.items(): if CONF_ACTION in intent: intent[CONF_ACTION] = script.Script(hass, intent[CONF_ACTION], "Alexa intent {}".format(name)) _CONFIG.update(intents) hass.http.register_path('POST', API_ENDPOINT, _handle_alexa, True) return True
async def test_calling_service_basic(hass): """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"}}) script_obj = script.Script(hass, sequence) 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"
async def test_activating_scene(hass): """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"}) script_obj = script.Script(hass, sequence) 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(ATTR_ENTITY_ID) == "scene.hello"
def __init__(self,hass,intents): super().__init__() _LOGGER.debug("DingdongIntentsView init ") self.hass = hass intents = copy.deepcopy(intents) template.attach(hass, intents) for name, intent in intents.items(): if CONF_ACTION in intent: intent[CONF_ACTION] = script.Script(hass,intent[CONF_ACTION],"DingDong {}".format(name)) self.intents = intents
async def test_last_triggered(hass, script_mode): """Test the last_triggered.""" event = "test_event" sequence = cv.SCRIPT_SCHEMA({"event": event}) script_obj = script.Script(hass, sequence, script_mode=script_mode) assert script_obj.last_triggered is None time = dt_util.utcnow() with mock.patch("homeassistant.helpers.script.utcnow", return_value=time): await script_obj.async_run() await hass.async_block_till_done() assert script_obj.last_triggered == time
def __init__(self, hass, intents): """Initialize API.AI view.""" super().__init__() self.hass = hass intents = copy.deepcopy(intents) template.attach(hass, intents) for name, intent in intents.items(): if CONF_ACTION in intent: intent[CONF_ACTION] = script.Script( hass, intent[CONF_ACTION], "Apiai intent {}".format(name)) self.intents = intents
async def test_wait_template_cancel(hass): """Test the wait template cancel action.""" event = "test_event" events = [] wait_alias = "wait step" @callback def record_event(event): """Add recorded event to set.""" events.append(event) hass.bus.async_listen(event, record_event) hass.states.async_set("switch.test", "on") script_obj = script.Script( hass, cv.SCRIPT_SCHEMA([ { "event": event }, { "wait_template": "{{states.switch.test.state == 'off'}}", "alias": wait_alias, }, { "event": event }, ]), ) await script_obj.async_run() await hass.async_block_till_done() assert script_obj.is_running assert script_obj.can_cancel assert script_obj.last_action == wait_alias assert len(events) == 1 script_obj.async_stop() assert not script_obj.is_running assert len(events) == 1 hass.states.async_set("switch.test", "off") await hass.async_block_till_done() assert not script_obj.is_running assert len(events) == 1
async def test_script_mode_single(hass, caplog): """Test overlapping runs with max_runs = 1.""" event = "test_event" events = async_capture_events(hass, event) sequence = cv.SCRIPT_SCHEMA([ { "event": event, "event_data": { "value": 1 } }, { "wait_template": "{{ states.switch.test.state == 'off' }}" }, { "event": event, "event_data": { "value": 2 } }, ]) script_obj = script.Script(hass, sequence) wait_started_flag = async_watch_for_action(script_obj, "wait") try: hass.states.async_set("switch.test", "on") hass.async_create_task(script_obj.async_run()) await asyncio.wait_for(wait_started_flag.wait(), 1) assert script_obj.is_running assert len(events) == 1 assert events[0].data["value"] == 1 # Start second run of script while first run is suspended in wait_template. await script_obj.async_run() assert "Already running" in caplog.text assert script_obj.is_running except (AssertionError, asyncio.TimeoutError): await script_obj.async_stop() raise else: hass.states.async_set("switch.test", "off") await hass.async_block_till_done() assert not script_obj.is_running assert len(events) == 2 assert events[1].data["value"] == 2
def _async_get_action(hass, config, name): """Return an action based on a configuration.""" script_obj = script.Script(hass, config, name) async def action(entity_id, variables, context): """Execute an action.""" _LOGGER.info("Executing %s", name) try: await script_obj.async_run(variables, context) except Exception as err: # pylint: disable=broad-except script_obj.async_log_exception( _LOGGER, f"Error while executing automation {entity_id}", err) return action
async def test_firing_event_basic(hass): """Test the firing of events.""" event = "test_event" context = Context() events = async_capture_events(hass, event) sequence = cv.SCRIPT_SCHEMA({"event": event, "event_data": {"hello": "world"}}) script_obj = script.Script(hass, sequence) await script_obj.async_run(context=context) await hass.async_block_till_done() assert len(events) == 1 assert events[0].context is context assert events[0].data.get("hello") == "world"
async def test_multiple_runs_repeat_choose(hass, caplog, action): """Test parallel runs with repeat & choose actions & max_runs > default.""" max_runs = script.DEFAULT_MAX + 1 script_obj = script.Script( hass, cv.SCRIPT_SCHEMA(action), script_mode="parallel", max_runs=max_runs ) events = async_capture_events(hass, "abc") for _ in range(max_runs): hass.async_create_task(script_obj.async_run()) await hass.async_block_till_done() assert "WARNING" not in caplog.text assert "ERROR" not in caplog.text assert len(events) == max_runs
async def test_delay(hass): """Test the delay.""" event = "test_event" events = [] context = Context() delay_alias = "delay step" @callback def record_event(event): """Add recorded event to set.""" events.append(event) hass.bus.async_listen(event, record_event) script_obj = script.Script( hass, cv.SCRIPT_SCHEMA([ { "event": event }, { "delay": { "seconds": 5 }, "alias": delay_alias }, { "event": event }, ]), ) await script_obj.async_run(context=context) await hass.async_block_till_done() assert script_obj.is_running assert script_obj.can_cancel assert script_obj.last_action == delay_alias assert len(events) == 1 future = dt_util.utcnow() + timedelta(seconds=5) async_fire_time_changed(hass, future) await hass.async_block_till_done() assert not script_obj.is_running assert len(events) == 2 assert events[0].context is context assert events[1].context is context
async def test_propagate_error_invalid_service_data(hass): """Test that a script aborts when we send invalid service data.""" event = "test_event" 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}] ) script_obj = script.Script(hass, sequence) with pytest.raises(vol.Invalid): await script_obj.async_run() assert len(events) == 0 assert len(calls) == 0 assert not script_obj.is_running
def _async_get_action(hass, sequence, button): """Return an action based on a configuration.""" script_obj = script.Script(hass, sequence, button) async def action(entity_id): """Execute an action.""" _LOGGER.info('Executing %s "%s" button', entity_id, button) try: await script_obj.async_run() except Exception as err: # pylint: disable=broad-except script_obj.async_log_exception( _LOGGER, 'Error while executing button action {} on {}'.format(button, entity_id), err) return action
async def test_referenced_entities(): """Test referenced entities.""" script_obj = script.Script( None, cv.SCRIPT_SCHEMA([ { "service": "test.script", "data": { "entity_id": "light.service_not_list" }, }, { "service": "test.script", "data": { "entity_id": ["light.service_list"] }, }, { "condition": "state", "entity_id": "sensor.condition", "state": "100", }, { "service": "test.script", "data": { "without": "entity_id" } }, { "scene": "scene.hello" }, { "event": "test_event" }, { "delay": "{{ delay_period }}" }, ]), ) assert script_obj.referenced_entities == { "light.service_not_list", "light.service_list", "sensor.condition", "scene.hello", } # Test we cache results. assert script_obj.referenced_entities is script_obj.referenced_entities
async def test_wait_template_timeout_continue(hass): """Test the wait template with continuing the script.""" event = "test_event" events = [] wait_alias = "wait step" @callback def record_event(event): """Add recorded event to set.""" events.append(event) hass.bus.async_listen(event, record_event) hass.states.async_set("switch.test", "on") script_obj = script.Script( hass, cv.SCRIPT_SCHEMA([ { "event": event }, { "wait_template": "{{states.switch.test.state == 'off'}}", "timeout": 5, "continue_on_timeout": True, "alias": wait_alias, }, { "event": event }, ]), ) await script_obj.async_run() await hass.async_block_till_done() assert script_obj.is_running assert script_obj.can_cancel assert script_obj.last_action == wait_alias assert len(events) == 1 future = dt_util.utcnow() + timedelta(seconds=5) async_fire_time_changed(hass, future) await hass.async_block_till_done() assert not script_obj.is_running assert len(events) == 2
def test_passing_variables_to_script(self): """Test if we can pass variables to script.""" calls = [] @callback def record_call(service): """Add recorded event to set.""" calls.append(service) self.hass.services.register('test', 'script', record_call) script_obj = script.Script( self.hass, cv.SCRIPT_SCHEMA([{ 'service': 'test.script', 'data_template': { 'hello': '{{ greeting }}', }, }, { 'delay': '{{ delay_period }}' }, { 'service': 'test.script', 'data_template': { 'hello': '{{ greeting2 }}', }, }])) script_obj.run({ 'greeting': 'world', 'greeting2': 'universe', 'delay_period': '00:00:05' }) self.hass.block_till_done() assert script_obj.is_running assert len(calls) == 1 assert calls[-1].data['hello'] == 'world' future = dt_util.utcnow() + timedelta(seconds=5) fire_time_changed(self.hass, future) self.hass.block_till_done() assert not script_obj.is_running assert len(calls) == 2 assert calls[-1].data['hello'] == 'universe'
async def test_update_logger(hass, caplog): """Test updating logger.""" sequence = cv.SCRIPT_SCHEMA({"event": "test_event"}) script_obj = script.Script(hass, sequence) await script_obj.async_run() await hass.async_block_till_done() assert script.__name__ in caplog.text log_name = "testing.123" script_obj.update_logger(logging.getLogger(log_name)) await script_obj.async_run() await hass.async_block_till_done() assert log_name in caplog.text