2016-03-09 09:25:50 +00:00
|
|
|
"""The tests for the automation component."""
|
2017-03-04 23:19:01 +00:00
|
|
|
import asyncio
|
2016-11-29 16:45:04 +00:00
|
|
|
from datetime import timedelta
|
2018-12-04 08:45:17 +00:00
|
|
|
from unittest.mock import patch, Mock
|
2015-08-11 06:11:46 +00:00
|
|
|
|
2018-10-26 09:31:14 +00:00
|
|
|
import pytest
|
|
|
|
|
2018-12-04 08:45:17 +00:00
|
|
|
from homeassistant.core import State, CoreState, Context
|
2018-10-26 09:31:14 +00:00
|
|
|
from homeassistant.setup import async_setup_component
|
2015-08-11 06:11:46 +00:00
|
|
|
import homeassistant.components.automation as automation
|
2017-03-24 22:52:14 +00:00
|
|
|
from homeassistant.const import (
|
2018-12-04 08:45:17 +00:00
|
|
|
ATTR_NAME, ATTR_ENTITY_ID, STATE_ON, STATE_OFF,
|
|
|
|
EVENT_HOMEASSISTANT_START, EVENT_AUTOMATION_TRIGGERED)
|
2016-09-04 15:15:52 +00:00
|
|
|
from homeassistant.exceptions import HomeAssistantError
|
2016-08-26 06:25:57 +00:00
|
|
|
import homeassistant.util.dt as dt_util
|
2015-08-11 06:11:46 +00:00
|
|
|
|
2017-03-04 23:19:01 +00:00
|
|
|
from tests.common import (
|
2018-10-26 09:31:14 +00:00
|
|
|
assert_setup_component, async_fire_time_changed,
|
|
|
|
mock_restore_cache, async_mock_service)
|
2018-09-26 07:49:55 +00:00
|
|
|
from tests.components.automation import common
|
2016-02-14 23:08:23 +00:00
|
|
|
|
2015-08-11 06:11:46 +00:00
|
|
|
|
2018-10-26 09:31:14 +00:00
|
|
|
@pytest.fixture
|
|
|
|
def calls(hass):
|
|
|
|
"""Track calls to a mock serivce."""
|
|
|
|
return async_mock_service(hass, 'test', 'automation')
|
2015-08-11 06:11:46 +00:00
|
|
|
|
2015-09-19 15:43:56 +00:00
|
|
|
|
2018-10-26 09:31:14 +00:00
|
|
|
async def test_service_data_not_a_dict(hass, calls):
|
|
|
|
"""Test service data not dict."""
|
|
|
|
with assert_setup_component(0, automation.DOMAIN):
|
|
|
|
assert await async_setup_component(hass, automation.DOMAIN, {
|
2015-08-11 06:11:46 +00:00
|
|
|
automation.DOMAIN: {
|
2015-09-15 05:05:40 +00:00
|
|
|
'trigger': {
|
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event',
|
|
|
|
},
|
|
|
|
'action': {
|
2015-09-19 15:43:56 +00:00
|
|
|
'service': 'test.automation',
|
2018-10-26 09:31:14 +00:00
|
|
|
'data': 100,
|
2015-09-15 05:05:40 +00:00
|
|
|
}
|
2015-08-11 06:11:46 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2016-11-29 16:45:04 +00:00
|
|
|
|
2018-10-26 09:31:14 +00:00
|
|
|
async def test_service_specify_data(hass, calls):
|
|
|
|
"""Test service data."""
|
|
|
|
assert await async_setup_component(hass, automation.DOMAIN, {
|
|
|
|
automation.DOMAIN: {
|
|
|
|
'alias': 'hello',
|
|
|
|
'trigger': {
|
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event',
|
|
|
|
},
|
|
|
|
'action': {
|
|
|
|
'service': 'test.automation',
|
|
|
|
'data_template': {
|
|
|
|
'some': '{{ trigger.platform }} - '
|
|
|
|
'{{ trigger.event.event_type }}'
|
2015-09-15 05:05:40 +00:00
|
|
|
},
|
2015-08-11 06:11:46 +00:00
|
|
|
}
|
2018-10-26 09:31:14 +00:00
|
|
|
}
|
|
|
|
})
|
2015-08-11 06:11:46 +00:00
|
|
|
|
2018-10-26 09:31:14 +00:00
|
|
|
time = dt_util.utcnow()
|
2015-08-25 04:50:20 +00:00
|
|
|
|
2018-10-26 09:31:14 +00:00
|
|
|
with patch('homeassistant.components.automation.utcnow',
|
|
|
|
return_value=time):
|
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert len(calls) == 1
|
|
|
|
assert calls[0].data['some'] == 'event - test_event'
|
|
|
|
state = hass.states.get('automation.hello')
|
|
|
|
assert state is not None
|
|
|
|
assert state.attributes.get('last_triggered') == time
|
|
|
|
|
|
|
|
state = hass.states.get('group.all_automations')
|
|
|
|
assert state is not None
|
|
|
|
assert state.attributes.get('entity_id') == ('automation.hello',)
|
|
|
|
|
|
|
|
|
|
|
|
async def test_action_delay(hass, calls):
|
|
|
|
"""Test action delay."""
|
|
|
|
assert await async_setup_component(hass, automation.DOMAIN, {
|
|
|
|
automation.DOMAIN: {
|
|
|
|
'alias': 'hello',
|
|
|
|
'trigger': {
|
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event',
|
|
|
|
},
|
|
|
|
'action': [
|
|
|
|
{
|
2015-09-19 15:43:56 +00:00
|
|
|
'service': 'test.automation',
|
2018-10-26 09:31:14 +00:00
|
|
|
'data_template': {
|
|
|
|
'some': '{{ trigger.platform }} - '
|
|
|
|
'{{ trigger.event.event_type }}'
|
2015-09-15 05:05:40 +00:00
|
|
|
}
|
2016-10-04 05:39:27 +00:00
|
|
|
},
|
2018-10-26 09:31:14 +00:00
|
|
|
{'delay': {'minutes': '10'}},
|
|
|
|
{
|
2016-10-04 05:39:27 +00:00
|
|
|
'service': 'test.automation',
|
2018-10-26 09:31:14 +00:00
|
|
|
'data_template': {
|
|
|
|
'some': '{{ trigger.platform }} - '
|
|
|
|
'{{ trigger.event.event_type }}'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
]
|
|
|
|
}
|
|
|
|
})
|
2016-10-04 05:39:27 +00:00
|
|
|
|
2018-10-26 09:31:14 +00:00
|
|
|
time = dt_util.utcnow()
|
2016-10-04 05:39:27 +00:00
|
|
|
|
2018-10-26 09:31:14 +00:00
|
|
|
with patch('homeassistant.components.automation.utcnow',
|
|
|
|
return_value=time):
|
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert len(calls) == 1
|
|
|
|
assert calls[0].data['some'] == 'event - test_event'
|
|
|
|
|
|
|
|
future = dt_util.utcnow() + timedelta(minutes=10)
|
|
|
|
async_fire_time_changed(hass, future)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert len(calls) == 2
|
|
|
|
assert calls[1].data['some'] == 'event - test_event'
|
|
|
|
|
|
|
|
state = hass.states.get('automation.hello')
|
|
|
|
assert state is not None
|
|
|
|
assert state.attributes.get('last_triggered') == time
|
|
|
|
state = hass.states.get('group.all_automations')
|
|
|
|
assert state is not None
|
|
|
|
assert state.attributes.get('entity_id') == ('automation.hello',)
|
|
|
|
|
|
|
|
|
|
|
|
async def test_service_specify_entity_id(hass, calls):
|
|
|
|
"""Test service data."""
|
|
|
|
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'
|
2015-09-15 05:51:28 +00:00
|
|
|
}
|
2018-10-26 09:31:14 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert 1 == len(calls)
|
|
|
|
assert ['hello.world'] == \
|
|
|
|
calls[0].data.get(ATTR_ENTITY_ID)
|
|
|
|
|
|
|
|
|
|
|
|
async def test_service_specify_entity_id_list(hass, calls):
|
|
|
|
"""Test service data."""
|
|
|
|
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', 'hello.world2']
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert 1 == len(calls)
|
|
|
|
assert ['hello.world', 'hello.world2'] == \
|
|
|
|
calls[0].data.get(ATTR_ENTITY_ID)
|
|
|
|
|
|
|
|
|
|
|
|
async def test_two_triggers(hass, calls):
|
|
|
|
"""Test triggers."""
|
|
|
|
assert await async_setup_component(hass, automation.DOMAIN, {
|
|
|
|
automation.DOMAIN: {
|
|
|
|
'trigger': [
|
|
|
|
{
|
2015-09-20 04:02:28 +00:00
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event',
|
2015-09-19 19:13:09 +00:00
|
|
|
},
|
2018-10-26 09:31:14 +00:00
|
|
|
{
|
|
|
|
'platform': 'state',
|
|
|
|
'entity_id': 'test.entity',
|
2015-09-20 04:02:28 +00:00
|
|
|
}
|
2018-10-26 09:31:14 +00:00
|
|
|
],
|
|
|
|
'action': {
|
|
|
|
'service': 'test.automation',
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert 1 == len(calls)
|
|
|
|
hass.states.async_set('test.entity', 'hello')
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert 2 == len(calls)
|
|
|
|
|
|
|
|
|
|
|
|
async def test_trigger_service_ignoring_condition(hass, calls):
|
|
|
|
"""Test triggers."""
|
|
|
|
assert await async_setup_component(hass, automation.DOMAIN, {
|
|
|
|
automation.DOMAIN: {
|
|
|
|
'alias': 'test',
|
|
|
|
'trigger': [
|
|
|
|
{
|
2016-04-22 02:36:14 +00:00
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event',
|
|
|
|
},
|
2018-10-26 09:31:14 +00:00
|
|
|
],
|
|
|
|
'condition': {
|
|
|
|
'condition': 'state',
|
|
|
|
'entity_id': 'non.existing',
|
|
|
|
'state': 'beer',
|
|
|
|
},
|
|
|
|
'action': {
|
|
|
|
'service': 'test.automation',
|
2016-04-22 02:36:14 +00:00
|
|
|
}
|
2018-10-26 09:31:14 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert len(calls) == 0
|
|
|
|
|
|
|
|
await hass.services.async_call(
|
|
|
|
'automation', 'trigger',
|
|
|
|
{'entity_id': 'automation.test'},
|
|
|
|
blocking=True)
|
|
|
|
assert len(calls) == 1
|
|
|
|
|
|
|
|
|
|
|
|
async def test_two_conditions_with_and(hass, calls):
|
|
|
|
"""Test two and conditions."""
|
|
|
|
entity_id = 'test.entity'
|
|
|
|
assert await async_setup_component(hass, automation.DOMAIN, {
|
|
|
|
automation.DOMAIN: {
|
|
|
|
'trigger': [
|
|
|
|
{
|
2016-08-26 06:25:57 +00:00
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event',
|
|
|
|
},
|
2018-10-26 09:31:14 +00:00
|
|
|
],
|
|
|
|
'condition': [
|
|
|
|
{
|
|
|
|
'condition': 'state',
|
|
|
|
'entity_id': entity_id,
|
|
|
|
'state': '100'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
'condition': 'numeric_state',
|
|
|
|
'entity_id': entity_id,
|
|
|
|
'below': 150
|
2016-08-26 06:25:57 +00:00
|
|
|
}
|
2018-10-26 09:31:14 +00:00
|
|
|
],
|
|
|
|
'action': {
|
|
|
|
'service': 'test.automation',
|
2016-08-26 06:25:57 +00:00
|
|
|
}
|
2018-10-26 09:31:14 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
hass.states.async_set(entity_id, 100)
|
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert 1 == len(calls)
|
|
|
|
|
|
|
|
hass.states.async_set(entity_id, 101)
|
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert 1 == len(calls)
|
|
|
|
|
|
|
|
hass.states.async_set(entity_id, 151)
|
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert 1 == len(calls)
|
|
|
|
|
|
|
|
|
|
|
|
async def test_automation_list_setting(hass, calls):
|
|
|
|
"""Event is not a valid condition."""
|
|
|
|
assert await async_setup_component(hass, automation.DOMAIN, {
|
|
|
|
automation.DOMAIN: [{
|
|
|
|
'trigger': {
|
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event',
|
|
|
|
},
|
|
|
|
|
|
|
|
'action': {
|
|
|
|
'service': 'test.automation',
|
|
|
|
}
|
|
|
|
}, {
|
|
|
|
'trigger': {
|
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event_2',
|
|
|
|
},
|
|
|
|
'action': {
|
|
|
|
'service': 'test.automation',
|
|
|
|
}
|
|
|
|
}]
|
|
|
|
})
|
|
|
|
|
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert 1 == len(calls)
|
|
|
|
|
|
|
|
hass.bus.async_fire('test_event_2')
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert 2 == len(calls)
|
|
|
|
|
|
|
|
|
|
|
|
async def test_automation_calling_two_actions(hass, calls):
|
|
|
|
"""Test if we can call two actions from automation async definition."""
|
|
|
|
assert await async_setup_component(hass, automation.DOMAIN, {
|
|
|
|
automation.DOMAIN: {
|
|
|
|
'trigger': {
|
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event',
|
|
|
|
},
|
|
|
|
|
|
|
|
'action': [{
|
|
|
|
'service': 'test.automation',
|
|
|
|
'data': {'position': 0},
|
|
|
|
}, {
|
|
|
|
'service': 'test.automation',
|
|
|
|
'data': {'position': 1},
|
|
|
|
}],
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert len(calls) == 2
|
|
|
|
assert calls[0].data['position'] == 0
|
|
|
|
assert calls[1].data['position'] == 1
|
|
|
|
|
|
|
|
|
2018-12-04 08:45:17 +00:00
|
|
|
async def test_shared_context(hass, calls):
|
|
|
|
"""Test that the shared context is passed down the chain."""
|
|
|
|
assert await async_setup_component(hass, automation.DOMAIN, {
|
|
|
|
automation.DOMAIN: [
|
|
|
|
{
|
|
|
|
'alias': 'hello',
|
|
|
|
'trigger': {
|
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event',
|
|
|
|
},
|
|
|
|
'action': {'event': 'test_event2'}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
'alias': 'bye',
|
|
|
|
'trigger': {
|
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event2',
|
|
|
|
},
|
|
|
|
'action': {
|
|
|
|
'service': 'test.automation',
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
})
|
|
|
|
|
|
|
|
context = Context()
|
2019-03-01 18:08:38 +00:00
|
|
|
first_automation_listener = Mock()
|
2018-12-04 08:45:17 +00:00
|
|
|
event_mock = Mock()
|
|
|
|
|
2019-03-01 18:08:38 +00:00
|
|
|
hass.bus.async_listen('test_event2', first_automation_listener)
|
2018-12-04 08:45:17 +00:00
|
|
|
hass.bus.async_listen(EVENT_AUTOMATION_TRIGGERED, event_mock)
|
|
|
|
hass.bus.async_fire('test_event', context=context)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
# Ensure events was fired
|
2019-03-01 18:08:38 +00:00
|
|
|
assert first_automation_listener.call_count == 1
|
2018-12-04 08:45:17 +00:00
|
|
|
assert event_mock.call_count == 2
|
|
|
|
|
2019-03-01 18:08:38 +00:00
|
|
|
# Verify automation triggered evenet for 'hello' automation
|
|
|
|
args, kwargs = event_mock.call_args_list[0]
|
|
|
|
first_trigger_context = args[0].context
|
|
|
|
assert first_trigger_context.parent_id == context.id
|
|
|
|
# Ensure event data has all attributes set
|
|
|
|
assert args[0].data.get(ATTR_NAME) is not None
|
|
|
|
assert args[0].data.get(ATTR_ENTITY_ID) is not None
|
2018-12-04 08:45:17 +00:00
|
|
|
|
2019-03-01 18:08:38 +00:00
|
|
|
# Ensure context set correctly for event fired by 'hello' automation
|
|
|
|
args, kwargs = first_automation_listener.call_args
|
|
|
|
assert args[0].context is first_trigger_context
|
2018-12-04 08:45:17 +00:00
|
|
|
|
2019-03-01 18:08:38 +00:00
|
|
|
# Ensure the 'hello' automation state has the right context
|
2018-12-04 08:45:17 +00:00
|
|
|
state = hass.states.get('automation.hello')
|
|
|
|
assert state is not None
|
2019-03-01 18:08:38 +00:00
|
|
|
assert state.context is first_trigger_context
|
|
|
|
|
|
|
|
# Verify automation triggered evenet for 'bye' automation
|
|
|
|
args, kwargs = event_mock.call_args_list[1]
|
|
|
|
second_trigger_context = args[0].context
|
|
|
|
assert second_trigger_context.parent_id == first_trigger_context.id
|
|
|
|
# Ensure event data has all attributes set
|
|
|
|
assert args[0].data.get(ATTR_NAME) is not None
|
|
|
|
assert args[0].data.get(ATTR_ENTITY_ID) is not None
|
2018-12-04 08:45:17 +00:00
|
|
|
|
|
|
|
# Ensure the service call from the second automation
|
|
|
|
# shares the same context
|
|
|
|
assert len(calls) == 1
|
2019-03-01 18:08:38 +00:00
|
|
|
assert calls[0].context is second_trigger_context
|
2018-12-04 08:45:17 +00:00
|
|
|
|
|
|
|
|
2018-10-26 09:31:14 +00:00
|
|
|
async def test_services(hass, calls):
|
|
|
|
"""Test the automation services for turning entities on/off."""
|
|
|
|
entity_id = 'automation.hello'
|
|
|
|
|
|
|
|
assert hass.states.get(entity_id) is None
|
|
|
|
assert not automation.is_on(hass, entity_id)
|
|
|
|
|
|
|
|
assert await async_setup_component(hass, automation.DOMAIN, {
|
|
|
|
automation.DOMAIN: {
|
|
|
|
'alias': 'hello',
|
|
|
|
'trigger': {
|
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event',
|
|
|
|
},
|
|
|
|
'action': {
|
|
|
|
'service': 'test.automation',
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
assert hass.states.get(entity_id) is not None
|
|
|
|
assert automation.is_on(hass, entity_id)
|
|
|
|
|
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert len(calls) == 1
|
|
|
|
|
|
|
|
await common.async_turn_off(hass, entity_id)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert not automation.is_on(hass, entity_id)
|
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert len(calls) == 1
|
|
|
|
|
|
|
|
await common.async_toggle(hass, entity_id)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert automation.is_on(hass, entity_id)
|
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert len(calls) == 2
|
|
|
|
|
|
|
|
await common.async_trigger(hass, entity_id)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert len(calls) == 3
|
|
|
|
|
|
|
|
await common.async_turn_off(hass, entity_id)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
await common.async_trigger(hass, entity_id)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert len(calls) == 4
|
|
|
|
|
|
|
|
await common.async_turn_on(hass, entity_id)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert automation.is_on(hass, entity_id)
|
|
|
|
|
|
|
|
|
|
|
|
async def test_reload_config_service(hass, calls):
|
|
|
|
"""Test the reload config service."""
|
|
|
|
assert await async_setup_component(hass, automation.DOMAIN, {
|
|
|
|
automation.DOMAIN: {
|
|
|
|
'alias': 'hello',
|
|
|
|
'trigger': {
|
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event',
|
|
|
|
},
|
|
|
|
'action': {
|
|
|
|
'service': 'test.automation',
|
|
|
|
'data_template': {
|
|
|
|
'event': '{{ trigger.event.event_type }}'
|
2016-09-04 15:15:52 +00:00
|
|
|
}
|
|
|
|
}
|
2018-10-26 09:31:14 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
assert hass.states.get('automation.hello') is not None
|
|
|
|
assert hass.states.get('automation.bye') is None
|
|
|
|
listeners = hass.bus.async_listeners()
|
|
|
|
assert listeners.get('test_event') == 1
|
|
|
|
assert listeners.get('test_event2') is None
|
|
|
|
|
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert len(calls) == 1
|
|
|
|
assert calls[0].data.get('event') == 'test_event'
|
|
|
|
|
|
|
|
with patch('homeassistant.config.load_yaml_config_file', autospec=True,
|
|
|
|
return_value={
|
2016-10-08 18:27:35 +00:00
|
|
|
automation.DOMAIN: {
|
2018-10-26 09:31:14 +00:00
|
|
|
'alias': 'bye',
|
2016-10-08 18:27:35 +00:00
|
|
|
'trigger': {
|
|
|
|
'platform': 'event',
|
2018-10-26 09:31:14 +00:00
|
|
|
'event_type': 'test_event2',
|
2016-10-08 18:27:35 +00:00
|
|
|
},
|
|
|
|
'action': {
|
|
|
|
'service': 'test.automation',
|
|
|
|
'data_template': {
|
|
|
|
'event': '{{ trigger.event.event_type }}'
|
|
|
|
}
|
2016-09-04 15:15:52 +00:00
|
|
|
}
|
2018-10-26 09:31:14 +00:00
|
|
|
}}):
|
|
|
|
with patch('homeassistant.config.find_config_file',
|
|
|
|
return_value=''):
|
|
|
|
await common.async_reload(hass)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
# De-flake ?!
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert hass.states.get('automation.hello') is None
|
|
|
|
assert hass.states.get('automation.bye') is not None
|
|
|
|
listeners = hass.bus.async_listeners()
|
|
|
|
assert listeners.get('test_event') is None
|
|
|
|
assert listeners.get('test_event2') == 1
|
|
|
|
|
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert len(calls) == 1
|
|
|
|
|
|
|
|
hass.bus.async_fire('test_event2')
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert len(calls) == 2
|
|
|
|
assert calls[1].data.get('event') == 'test_event2'
|
|
|
|
|
|
|
|
|
|
|
|
async def test_reload_config_when_invalid_config(hass, calls):
|
|
|
|
"""Test the reload config service handling invalid config."""
|
|
|
|
with assert_setup_component(1, automation.DOMAIN):
|
|
|
|
assert await async_setup_component(hass, automation.DOMAIN, {
|
2016-09-04 15:15:52 +00:00
|
|
|
automation.DOMAIN: {
|
|
|
|
'alias': 'hello',
|
|
|
|
'trigger': {
|
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event',
|
|
|
|
},
|
|
|
|
'action': {
|
|
|
|
'service': 'test.automation',
|
|
|
|
'data_template': {
|
|
|
|
'event': '{{ trigger.event.event_type }}'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
2018-10-26 09:31:14 +00:00
|
|
|
assert hass.states.get('automation.hello') is not None
|
|
|
|
|
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert len(calls) == 1
|
|
|
|
assert calls[0].data.get('event') == 'test_event'
|
|
|
|
|
|
|
|
with patch('homeassistant.config.load_yaml_config_file', autospec=True,
|
|
|
|
return_value={automation.DOMAIN: 'not valid'}):
|
|
|
|
with patch('homeassistant.config.find_config_file',
|
|
|
|
return_value=''):
|
|
|
|
await common.async_reload(hass)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert hass.states.get('automation.hello') is None
|
|
|
|
|
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert len(calls) == 1
|
|
|
|
|
|
|
|
|
|
|
|
async def test_reload_config_handles_load_fails(hass, calls):
|
|
|
|
"""Test the reload config service."""
|
|
|
|
assert await async_setup_component(hass, automation.DOMAIN, {
|
|
|
|
automation.DOMAIN: {
|
|
|
|
'alias': 'hello',
|
|
|
|
'trigger': {
|
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event',
|
|
|
|
},
|
|
|
|
'action': {
|
|
|
|
'service': 'test.automation',
|
|
|
|
'data_template': {
|
|
|
|
'event': '{{ trigger.event.event_type }}'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
assert hass.states.get('automation.hello') is not None
|
2016-09-04 15:15:52 +00:00
|
|
|
|
2018-10-26 09:31:14 +00:00
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
await hass.async_block_till_done()
|
2016-09-04 15:15:52 +00:00
|
|
|
|
2018-10-26 09:31:14 +00:00
|
|
|
assert len(calls) == 1
|
|
|
|
assert calls[0].data.get('event') == 'test_event'
|
2016-09-04 15:15:52 +00:00
|
|
|
|
2018-10-26 09:31:14 +00:00
|
|
|
with patch('homeassistant.config.load_yaml_config_file',
|
|
|
|
side_effect=HomeAssistantError('bla')):
|
|
|
|
with patch('homeassistant.config.find_config_file',
|
|
|
|
return_value=''):
|
|
|
|
await common.async_reload(hass)
|
|
|
|
await hass.async_block_till_done()
|
2016-09-04 15:15:52 +00:00
|
|
|
|
2018-10-26 09:31:14 +00:00
|
|
|
assert hass.states.get('automation.hello') is not None
|
2016-09-04 15:15:52 +00:00
|
|
|
|
2018-10-26 09:31:14 +00:00
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert len(calls) == 2
|
2017-03-04 23:19:01 +00:00
|
|
|
|
|
|
|
|
|
|
|
@asyncio.coroutine
|
|
|
|
def test_automation_restore_state(hass):
|
|
|
|
"""Ensure states are restored on startup."""
|
|
|
|
time = dt_util.utcnow()
|
|
|
|
|
|
|
|
mock_restore_cache(hass, (
|
|
|
|
State('automation.hello', STATE_ON),
|
|
|
|
State('automation.bye', STATE_OFF, {'last_triggered': time}),
|
|
|
|
))
|
|
|
|
|
|
|
|
config = {automation.DOMAIN: [{
|
|
|
|
'alias': 'hello',
|
|
|
|
'trigger': {
|
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event_hello',
|
|
|
|
},
|
|
|
|
'action': {'service': 'test.automation'}
|
|
|
|
}, {
|
|
|
|
'alias': 'bye',
|
|
|
|
'trigger': {
|
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event_bye',
|
|
|
|
},
|
|
|
|
'action': {'service': 'test.automation'}
|
|
|
|
}]}
|
|
|
|
|
|
|
|
assert (yield from async_setup_component(hass, automation.DOMAIN, config))
|
|
|
|
|
|
|
|
state = hass.states.get('automation.hello')
|
|
|
|
assert state
|
|
|
|
assert state.state == STATE_ON
|
|
|
|
|
|
|
|
state = hass.states.get('automation.bye')
|
|
|
|
assert state
|
|
|
|
assert state.state == STATE_OFF
|
|
|
|
assert state.attributes.get('last_triggered') == time
|
|
|
|
|
2017-06-25 17:53:15 +00:00
|
|
|
calls = async_mock_service(hass, 'test', 'automation')
|
2017-03-04 23:19:01 +00:00
|
|
|
|
|
|
|
assert automation.is_on(hass, 'automation.bye') is False
|
|
|
|
|
|
|
|
hass.bus.async_fire('test_event_bye')
|
|
|
|
yield from hass.async_block_till_done()
|
|
|
|
assert len(calls) == 0
|
|
|
|
|
|
|
|
assert automation.is_on(hass, 'automation.hello')
|
|
|
|
|
|
|
|
hass.bus.async_fire('test_event_hello')
|
|
|
|
yield from hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert len(calls) == 1
|
2017-04-04 06:11:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
@asyncio.coroutine
|
|
|
|
def test_initial_value_off(hass):
|
|
|
|
"""Test initial value off."""
|
2017-06-25 17:53:15 +00:00
|
|
|
calls = async_mock_service(hass, 'test', 'automation')
|
2017-04-04 06:11:39 +00:00
|
|
|
|
|
|
|
res = yield from async_setup_component(hass, automation.DOMAIN, {
|
|
|
|
automation.DOMAIN: {
|
|
|
|
'alias': 'hello',
|
|
|
|
'initial_state': 'off',
|
|
|
|
'trigger': {
|
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event',
|
|
|
|
},
|
|
|
|
'action': {
|
|
|
|
'service': 'test.automation',
|
|
|
|
'entity_id': 'hello.world'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
assert res
|
|
|
|
assert not automation.is_on(hass, 'automation.hello')
|
|
|
|
|
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
yield from hass.async_block_till_done()
|
|
|
|
assert len(calls) == 0
|
|
|
|
|
|
|
|
|
2019-06-08 06:08:22 +00:00
|
|
|
async def test_initial_value_on(hass):
|
2017-04-04 06:11:39 +00:00
|
|
|
"""Test initial value on."""
|
2019-06-08 06:08:22 +00:00
|
|
|
hass.state = CoreState.not_running
|
2017-06-25 17:53:15 +00:00
|
|
|
calls = async_mock_service(hass, 'test', 'automation')
|
2017-04-04 06:11:39 +00:00
|
|
|
|
2019-06-08 06:08:22 +00:00
|
|
|
assert await async_setup_component(hass, automation.DOMAIN, {
|
2017-04-04 06:11:39 +00:00
|
|
|
automation.DOMAIN: {
|
|
|
|
'alias': 'hello',
|
|
|
|
'initial_state': 'on',
|
|
|
|
'trigger': {
|
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event',
|
|
|
|
},
|
|
|
|
'action': {
|
|
|
|
'service': 'test.automation',
|
|
|
|
'entity_id': ['hello.world', 'hello.world2']
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
assert automation.is_on(hass, 'automation.hello')
|
|
|
|
|
2019-06-08 06:08:22 +00:00
|
|
|
await hass.async_start()
|
2017-04-04 06:11:39 +00:00
|
|
|
hass.bus.async_fire('test_event')
|
2019-06-08 06:08:22 +00:00
|
|
|
await hass.async_block_till_done()
|
2017-04-04 06:11:39 +00:00
|
|
|
assert len(calls) == 1
|
|
|
|
|
|
|
|
|
2019-06-08 06:08:22 +00:00
|
|
|
async def test_initial_value_off_but_restore_on(hass):
|
2017-04-04 06:11:39 +00:00
|
|
|
"""Test initial value off and restored state is turned on."""
|
2019-06-08 06:08:22 +00:00
|
|
|
hass.state = CoreState.not_running
|
2017-06-25 17:53:15 +00:00
|
|
|
calls = async_mock_service(hass, 'test', 'automation')
|
2017-04-04 06:11:39 +00:00
|
|
|
mock_restore_cache(hass, (
|
|
|
|
State('automation.hello', STATE_ON),
|
|
|
|
))
|
|
|
|
|
2019-06-08 06:08:22 +00:00
|
|
|
await async_setup_component(hass, automation.DOMAIN, {
|
2017-04-04 06:11:39 +00:00
|
|
|
automation.DOMAIN: {
|
|
|
|
'alias': 'hello',
|
|
|
|
'initial_state': 'off',
|
|
|
|
'trigger': {
|
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event',
|
|
|
|
},
|
|
|
|
'action': {
|
|
|
|
'service': 'test.automation',
|
|
|
|
'entity_id': 'hello.world'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
assert not automation.is_on(hass, 'automation.hello')
|
|
|
|
|
2019-06-08 06:08:22 +00:00
|
|
|
await hass.async_start()
|
2017-04-04 06:11:39 +00:00
|
|
|
hass.bus.async_fire('test_event')
|
2019-06-08 06:08:22 +00:00
|
|
|
await hass.async_block_till_done()
|
2017-04-04 06:11:39 +00:00
|
|
|
assert len(calls) == 0
|
|
|
|
|
|
|
|
|
|
|
|
@asyncio.coroutine
|
|
|
|
def test_initial_value_on_but_restore_off(hass):
|
|
|
|
"""Test initial value on and restored state is turned off."""
|
2017-06-25 17:53:15 +00:00
|
|
|
calls = async_mock_service(hass, 'test', 'automation')
|
2017-04-04 06:11:39 +00:00
|
|
|
mock_restore_cache(hass, (
|
|
|
|
State('automation.hello', STATE_OFF),
|
|
|
|
))
|
|
|
|
|
|
|
|
res = yield from async_setup_component(hass, automation.DOMAIN, {
|
|
|
|
automation.DOMAIN: {
|
|
|
|
'alias': 'hello',
|
|
|
|
'initial_state': 'on',
|
|
|
|
'trigger': {
|
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event',
|
|
|
|
},
|
|
|
|
'action': {
|
|
|
|
'service': 'test.automation',
|
|
|
|
'entity_id': 'hello.world'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
assert res
|
|
|
|
assert automation.is_on(hass, 'automation.hello')
|
|
|
|
|
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
yield from hass.async_block_till_done()
|
|
|
|
assert len(calls) == 1
|
|
|
|
|
|
|
|
|
|
|
|
@asyncio.coroutine
|
|
|
|
def test_no_initial_value_and_restore_off(hass):
|
|
|
|
"""Test initial value off and restored state is turned on."""
|
2017-06-25 17:53:15 +00:00
|
|
|
calls = async_mock_service(hass, 'test', 'automation')
|
2017-04-04 06:11:39 +00:00
|
|
|
mock_restore_cache(hass, (
|
|
|
|
State('automation.hello', STATE_OFF),
|
|
|
|
))
|
|
|
|
|
|
|
|
res = yield from async_setup_component(hass, automation.DOMAIN, {
|
|
|
|
automation.DOMAIN: {
|
|
|
|
'alias': 'hello',
|
|
|
|
'trigger': {
|
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event',
|
|
|
|
},
|
|
|
|
'action': {
|
|
|
|
'service': 'test.automation',
|
|
|
|
'entity_id': 'hello.world'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
assert res
|
|
|
|
assert not automation.is_on(hass, 'automation.hello')
|
|
|
|
|
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
yield from hass.async_block_till_done()
|
|
|
|
assert len(calls) == 0
|
|
|
|
|
|
|
|
|
|
|
|
@asyncio.coroutine
|
|
|
|
def test_automation_is_on_if_no_initial_state_or_restore(hass):
|
|
|
|
"""Test initial value is on when no initial state or restored state."""
|
2017-06-25 17:53:15 +00:00
|
|
|
calls = async_mock_service(hass, 'test', 'automation')
|
2017-04-04 06:11:39 +00:00
|
|
|
|
|
|
|
res = yield from async_setup_component(hass, automation.DOMAIN, {
|
|
|
|
automation.DOMAIN: {
|
|
|
|
'alias': 'hello',
|
|
|
|
'trigger': {
|
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event',
|
|
|
|
},
|
|
|
|
'action': {
|
|
|
|
'service': 'test.automation',
|
|
|
|
'entity_id': 'hello.world'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
assert res
|
|
|
|
assert automation.is_on(hass, 'automation.hello')
|
|
|
|
|
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
yield from hass.async_block_till_done()
|
|
|
|
assert len(calls) == 1
|
|
|
|
|
|
|
|
|
|
|
|
@asyncio.coroutine
|
|
|
|
def test_automation_not_trigger_on_bootstrap(hass):
|
|
|
|
"""Test if automation is not trigger on bootstrap."""
|
|
|
|
hass.state = CoreState.not_running
|
2017-06-25 17:53:15 +00:00
|
|
|
calls = async_mock_service(hass, 'test', 'automation')
|
2017-04-04 06:11:39 +00:00
|
|
|
|
|
|
|
res = yield from async_setup_component(hass, automation.DOMAIN, {
|
|
|
|
automation.DOMAIN: {
|
|
|
|
'alias': 'hello',
|
|
|
|
'trigger': {
|
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event',
|
|
|
|
},
|
|
|
|
'action': {
|
|
|
|
'service': 'test.automation',
|
|
|
|
'entity_id': 'hello.world'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
assert res
|
2019-06-08 06:08:22 +00:00
|
|
|
assert automation.is_on(hass, 'automation.hello')
|
2017-04-04 06:11:39 +00:00
|
|
|
|
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
yield from hass.async_block_till_done()
|
|
|
|
assert len(calls) == 0
|
|
|
|
|
|
|
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
|
|
|
yield from hass.async_block_till_done()
|
|
|
|
assert automation.is_on(hass, 'automation.hello')
|
|
|
|
|
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
yield from hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert len(calls) == 1
|
|
|
|
assert ['hello.world'] == calls[0].data.get(ATTR_ENTITY_ID)
|
2018-12-13 11:21:16 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_automation_with_error_in_script(hass, caplog):
|
|
|
|
"""Test automation with an error in script."""
|
|
|
|
assert await async_setup_component(hass, automation.DOMAIN, {
|
|
|
|
automation.DOMAIN: {
|
|
|
|
'alias': 'hello',
|
|
|
|
'trigger': {
|
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event',
|
|
|
|
},
|
|
|
|
'action': {
|
|
|
|
'service': 'test.automation',
|
|
|
|
'entity_id': 'hello.world'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
hass.bus.async_fire('test_event')
|
|
|
|
await hass.async_block_till_done()
|
2019-05-14 05:09:11 +00:00
|
|
|
assert 'Service not found' in caplog.text
|
2019-06-08 19:48:37 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_automation_restore_last_triggered_with_initial_state(hass):
|
|
|
|
"""Ensure last_triggered is restored, even when initial state is set."""
|
|
|
|
time = dt_util.utcnow()
|
|
|
|
|
|
|
|
mock_restore_cache(hass, (
|
|
|
|
State('automation.hello', STATE_ON),
|
|
|
|
State('automation.bye', STATE_ON, {'last_triggered': time}),
|
|
|
|
State('automation.solong', STATE_OFF, {'last_triggered': time}),
|
|
|
|
))
|
|
|
|
|
|
|
|
config = {automation.DOMAIN: [{
|
|
|
|
'alias': 'hello',
|
|
|
|
'initial_state': 'off',
|
|
|
|
'trigger': {
|
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event',
|
|
|
|
},
|
|
|
|
'action': {'service': 'test.automation'}
|
|
|
|
}, {
|
|
|
|
'alias': 'bye',
|
|
|
|
'initial_state': 'off',
|
|
|
|
'trigger': {
|
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event',
|
|
|
|
},
|
|
|
|
'action': {'service': 'test.automation'}
|
|
|
|
}, {
|
|
|
|
'alias': 'solong',
|
|
|
|
'initial_state': 'on',
|
|
|
|
'trigger': {
|
|
|
|
'platform': 'event',
|
|
|
|
'event_type': 'test_event',
|
|
|
|
},
|
|
|
|
'action': {'service': 'test.automation'}
|
|
|
|
}]}
|
|
|
|
|
|
|
|
await async_setup_component(hass, automation.DOMAIN, config)
|
|
|
|
|
|
|
|
state = hass.states.get('automation.hello')
|
|
|
|
assert state
|
|
|
|
assert state.state == STATE_OFF
|
|
|
|
assert state.attributes['last_triggered'] is None
|
|
|
|
|
|
|
|
state = hass.states.get('automation.bye')
|
|
|
|
assert state
|
|
|
|
assert state.state == STATE_OFF
|
|
|
|
assert state.attributes['last_triggered'] == time
|
|
|
|
|
|
|
|
state = hass.states.get('automation.solong')
|
|
|
|
assert state
|
|
|
|
assert state.state == STATE_ON
|
|
|
|
assert state.attributes['last_triggered'] == time
|