From e276e899cf42ea624c325a3750795bfbc4daf03f Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 26 Oct 2018 11:31:14 +0200 Subject: [PATCH] Convert automation tests to async (#17794) * Convert automation tests to async * Fix 8 last tests * Lint --- tests/components/automation/common.py | 29 +- tests/components/automation/test_event.py | 277 ++- .../automation/test_geo_location.py | 457 +++-- tests/components/automation/test_init.py | 940 ++++----- tests/components/automation/test_mqtt.py | 154 +- .../automation/test_numeric_state.py | 1736 +++++++++-------- tests/components/automation/test_state.py | 1087 ++++++----- tests/components/automation/test_sun.py | 543 +++--- tests/components/automation/test_template.py | 811 ++++---- tests/components/automation/test_time.py | 684 +++---- tests/components/automation/test_zone.py | 340 ++-- 11 files changed, 3544 insertions(+), 3514 deletions(-) diff --git a/tests/components/automation/common.py b/tests/components/automation/common.py index 4c8f91849aa..2a5024c0c30 100644 --- a/tests/components/automation/common.py +++ b/tests/components/automation/common.py @@ -11,43 +11,34 @@ from homeassistant.loader import bind_hass @bind_hass -def turn_on(hass, entity_id=None): +async def async_turn_on(hass, entity_id=None): """Turn on specified automation or all.""" data = {ATTR_ENTITY_ID: entity_id} if entity_id else {} - hass.services.call(DOMAIN, SERVICE_TURN_ON, data) + await hass.services.async_call(DOMAIN, SERVICE_TURN_ON, data) @bind_hass -def turn_off(hass, entity_id=None): +async def async_turn_off(hass, entity_id=None): """Turn off specified automation or all.""" data = {ATTR_ENTITY_ID: entity_id} if entity_id else {} - hass.services.call(DOMAIN, SERVICE_TURN_OFF, data) + await hass.services.async_call(DOMAIN, SERVICE_TURN_OFF, data) @bind_hass -def toggle(hass, entity_id=None): +async def async_toggle(hass, entity_id=None): """Toggle specified automation or all.""" data = {ATTR_ENTITY_ID: entity_id} if entity_id else {} - hass.services.call(DOMAIN, SERVICE_TOGGLE, data) + await hass.services.async_call(DOMAIN, SERVICE_TOGGLE, data) @bind_hass -def trigger(hass, entity_id=None): +async def async_trigger(hass, entity_id=None): """Trigger specified automation or all.""" data = {ATTR_ENTITY_ID: entity_id} if entity_id else {} - hass.services.call(DOMAIN, SERVICE_TRIGGER, data) + await hass.services.async_call(DOMAIN, SERVICE_TRIGGER, data) @bind_hass -def reload(hass): +async def async_reload(hass): """Reload the automation from config.""" - hass.services.call(DOMAIN, SERVICE_RELOAD) - - -@bind_hass -def async_reload(hass): - """Reload the automation from config. - - Returns a coroutine object. - """ - return hass.services.async_call(DOMAIN, SERVICE_RELOAD) + await hass.services.async_call(DOMAIN, SERVICE_RELOAD) diff --git a/tests/components/automation/test_event.py b/tests/components/automation/test_event.py index b925e5a809d..4b669fc1356 100644 --- a/tests/components/automation/test_event.py +++ b/tests/components/automation/test_event.py @@ -1,175 +1,172 @@ """The tests for the Event automation.""" -import unittest +import pytest -from homeassistant.core import Context, callback -from homeassistant.setup import setup_component +from homeassistant.core import Context +from homeassistant.setup import async_setup_component import homeassistant.components.automation as automation -from tests.common import get_test_home_assistant, mock_component +from tests.common import mock_component from tests.components.automation import common +from tests.common import async_mock_service -# pylint: disable=invalid-name -class TestAutomationEvent(unittest.TestCase): - """Test the event automation.""" +@pytest.fixture +def calls(hass): + """Track calls to a mock serivce.""" + return async_mock_service(hass, 'test', 'automation') - def setUp(self): - """Set up things to be run when tests are started.""" - self.hass = get_test_home_assistant() - mock_component(self.hass, 'group') - self.calls = [] - @callback - def record_call(service): - """Record the call.""" - self.calls.append(service) +@pytest.fixture(autouse=True) +def setup_comp(hass): + """Initialize components.""" + mock_component(hass, 'group') - self.hass.services.register('test', 'automation', record_call) - def tearDown(self): - """Stop everything that was started.""" - self.hass.stop() +async def test_if_fires_on_event(hass, calls): + """Test the firing of events.""" + context = Context() - def test_if_fires_on_event(self): - """Test the firing of events.""" - context = Context() - - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event', - }, - 'action': { - 'service': 'test.automation', - } + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event', + }, + 'action': { + 'service': 'test.automation', } - }) + } + }) - self.hass.bus.fire('test_event', context=context) - self.hass.block_till_done() - assert 1 == len(self.calls) - assert self.calls[0].context is context + hass.bus.async_fire('test_event', context=context) + await hass.async_block_till_done() + assert 1 == len(calls) + assert calls[0].context is context - common.turn_off(self.hass) - self.hass.block_till_done() + await common.async_turn_off(hass) + await hass.async_block_till_done() - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 1 == len(self.calls) + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + assert 1 == len(calls) - def test_if_fires_on_event_extra_data(self): - """Test the firing of events still matches with event data.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event', - }, - 'action': { - 'service': 'test.automation', - } + +async def test_if_fires_on_event_extra_data(hass, calls): + """Test the firing of events still matches with event data.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event', + }, + 'action': { + 'service': 'test.automation', } - }) + } + }) - self.hass.bus.fire('test_event', {'extra_key': 'extra_data'}) - self.hass.block_till_done() - assert 1 == len(self.calls) + hass.bus.async_fire('test_event', {'extra_key': 'extra_data'}) + await hass.async_block_till_done() + assert 1 == len(calls) - common.turn_off(self.hass) - self.hass.block_till_done() + await common.async_turn_off(hass) + await hass.async_block_till_done() - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 1 == len(self.calls) + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + assert 1 == len(calls) - def test_if_fires_on_event_with_data(self): - """Test the firing of events with data.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event', - 'event_data': {'some_attr': 'some_value'} - }, - 'action': { - 'service': 'test.automation', - } + +async def test_if_fires_on_event_with_data(hass, calls): + """Test the firing of events with data.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event', + 'event_data': {'some_attr': 'some_value'} + }, + 'action': { + 'service': 'test.automation', } - }) + } + }) - self.hass.bus.fire('test_event', {'some_attr': 'some_value', - 'another': 'value'}) - self.hass.block_till_done() - assert 1 == len(self.calls) + hass.bus.async_fire('test_event', {'some_attr': 'some_value', + 'another': 'value'}) + await hass.async_block_till_done() + assert 1 == len(calls) - def test_if_fires_on_event_with_empty_data_config(self): - """Test the firing of events with empty data config. - The frontend automation editor can produce configurations with an - empty dict for event_data instead of no key. - """ - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event', - 'event_data': {} - }, - 'action': { - 'service': 'test.automation', - } +async def test_if_fires_on_event_with_empty_data_config(hass, calls): + """Test the firing of events with empty data config. + + The frontend automation editor can produce configurations with an + empty dict for event_data instead of no key. + """ + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event', + 'event_data': {} + }, + 'action': { + 'service': 'test.automation', } - }) + } + }) - self.hass.bus.fire('test_event', {'some_attr': 'some_value', - 'another': 'value'}) - self.hass.block_till_done() - assert 1 == len(self.calls) + hass.bus.async_fire('test_event', {'some_attr': 'some_value', + 'another': 'value'}) + await hass.async_block_till_done() + assert 1 == len(calls) - def test_if_fires_on_event_with_nested_data(self): - """Test the firing of events with nested data.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event', - 'event_data': { - 'parent_attr': { - 'some_attr': 'some_value' - } + +async def test_if_fires_on_event_with_nested_data(hass, calls): + """Test the firing of events with nested data.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event', + 'event_data': { + 'parent_attr': { + 'some_attr': 'some_value' } - }, - 'action': { - 'service': 'test.automation', } + }, + 'action': { + 'service': 'test.automation', } - }) + } + }) - self.hass.bus.fire('test_event', { - 'parent_attr': { - 'some_attr': 'some_value', - 'another': 'value' + hass.bus.async_fire('test_event', { + 'parent_attr': { + 'some_attr': 'some_value', + 'another': 'value' + } + }) + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_not_fires_if_event_data_not_matches(hass, calls): + """Test firing of event if no match.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event', + 'event_data': {'some_attr': 'some_value'} + }, + 'action': { + 'service': 'test.automation', } - }) - self.hass.block_till_done() - assert 1 == len(self.calls) + } + }) - def test_if_not_fires_if_event_data_not_matches(self): - """Test firing of event if no match.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event', - 'event_data': {'some_attr': 'some_value'} - }, - 'action': { - 'service': 'test.automation', - } - } - }) - - self.hass.bus.fire('test_event', {'some_attr': 'some_other_value'}) - self.hass.block_till_done() - assert 0 == len(self.calls) + hass.bus.async_fire('test_event', {'some_attr': 'some_other_value'}) + await hass.async_block_till_done() + assert 0 == len(calls) diff --git a/tests/components/automation/test_geo_location.py b/tests/components/automation/test_geo_location.py index f14ea34d297..946c9a8abc6 100644 --- a/tests/components/automation/test_geo_location.py +++ b/tests/components/automation/test_geo_location.py @@ -1,268 +1,265 @@ """The tests for the geo location trigger.""" -import unittest +import pytest from homeassistant.components import automation, zone -from homeassistant.core import callback, Context -from homeassistant.setup import setup_component +from homeassistant.core import Context +from homeassistant.setup import async_setup_component -from tests.common import get_test_home_assistant, mock_component +from tests.common import mock_component from tests.components.automation import common +from tests.common import async_mock_service -class TestAutomationGeoLocation(unittest.TestCase): - """Test the geo location trigger.""" +@pytest.fixture +def calls(hass): + """Track calls to a mock serivce.""" + return async_mock_service(hass, 'test', 'automation') - def setUp(self): - """Set up things to be run when tests are started.""" - self.hass = get_test_home_assistant() - mock_component(self.hass, 'group') - assert setup_component(self.hass, zone.DOMAIN, { + +@pytest.fixture(autouse=True) +def setup_comp(hass): + """Initialize components.""" + mock_component(hass, 'group') + hass.loop.run_until_complete(async_setup_component(hass, zone.DOMAIN, { 'zone': { 'name': 'test', 'latitude': 32.880837, 'longitude': -117.237561, 'radius': 250, } - }) + })) - self.calls = [] - @callback - def record_call(service): - """Record calls.""" - self.calls.append(service) +async def test_if_fires_on_zone_enter(hass, calls): + """Test for firing on zone enter.""" + context = Context() + hass.states.async_set('geo_location.entity', 'hello', { + 'latitude': 32.881011, + 'longitude': -117.234758, + 'source': 'test_source' + }) + await hass.async_block_till_done() - self.hass.services.register('test', 'automation', record_call) - - def tearDown(self): - """Stop everything that was started.""" - self.hass.stop() - - def test_if_fires_on_zone_enter(self): - """Test for firing on zone enter.""" - context = Context() - self.hass.states.set('geo_location.entity', 'hello', { - 'latitude': 32.881011, - 'longitude': -117.234758, - 'source': 'test_source' - }) - self.hass.block_till_done() - - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'geo_location', - 'source': 'test_source', - 'zone': 'zone.test', - 'event': 'enter', + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'geo_location', + 'source': 'test_source', + 'zone': 'zone.test', + 'event': 'enter', + }, + 'action': { + 'service': 'test.automation', + 'data_template': { + 'some': '{{ trigger.%s }}' % '}} - {{ trigger.'.join(( + 'platform', 'entity_id', + 'from_state.state', 'to_state.state', + 'zone.name')) }, - 'action': { - 'service': 'test.automation', - 'data_template': { - 'some': '{{ trigger.%s }}' % '}} - {{ trigger.'.join(( - 'platform', 'entity_id', - 'from_state.state', 'to_state.state', - 'zone.name')) - }, - } } - }) + } + }) - self.hass.states.set('geo_location.entity', 'hello', { - 'latitude': 32.880586, - 'longitude': -117.237564 - }, context=context) - self.hass.block_till_done() + hass.states.async_set('geo_location.entity', 'hello', { + 'latitude': 32.880586, + 'longitude': -117.237564 + }, context=context) + await hass.async_block_till_done() - assert 1 == len(self.calls) - assert self.calls[0].context is context - assert 'geo_location - geo_location.entity - hello - hello - test' == \ - self.calls[0].data['some'] + assert 1 == len(calls) + assert calls[0].context is context + assert 'geo_location - geo_location.entity - hello - hello - test' == \ + calls[0].data['some'] - # Set out of zone again so we can trigger call - self.hass.states.set('geo_location.entity', 'hello', { - 'latitude': 32.881011, - 'longitude': -117.234758 - }) - self.hass.block_till_done() + # Set out of zone again so we can trigger call + hass.states.async_set('geo_location.entity', 'hello', { + 'latitude': 32.881011, + 'longitude': -117.234758 + }) + await hass.async_block_till_done() - common.turn_off(self.hass) - self.hass.block_till_done() + await common.async_turn_off(hass) + await hass.async_block_till_done() - self.hass.states.set('geo_location.entity', 'hello', { - 'latitude': 32.880586, - 'longitude': -117.237564 - }) - self.hass.block_till_done() + hass.states.async_set('geo_location.entity', 'hello', { + 'latitude': 32.880586, + 'longitude': -117.237564 + }) + await hass.async_block_till_done() - assert 1 == len(self.calls) + assert 1 == len(calls) - def test_if_not_fires_for_enter_on_zone_leave(self): - """Test for not firing on zone leave.""" - self.hass.states.set('geo_location.entity', 'hello', { - 'latitude': 32.880586, - 'longitude': -117.237564, - 'source': 'test_source' - }) - self.hass.block_till_done() - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'geo_location', - 'source': 'test_source', - 'zone': 'zone.test', - 'event': 'enter', +async def test_if_not_fires_for_enter_on_zone_leave(hass, calls): + """Test for not firing on zone leave.""" + hass.states.async_set('geo_location.entity', 'hello', { + 'latitude': 32.880586, + 'longitude': -117.237564, + 'source': 'test_source' + }) + await hass.async_block_till_done() + + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'geo_location', + 'source': 'test_source', + 'zone': 'zone.test', + 'event': 'enter', + }, + 'action': { + 'service': 'test.automation', + } + } + }) + + hass.states.async_set('geo_location.entity', 'hello', { + 'latitude': 32.881011, + 'longitude': -117.234758 + }) + await hass.async_block_till_done() + + assert 0 == len(calls) + + +async def test_if_fires_on_zone_leave(hass, calls): + """Test for firing on zone leave.""" + hass.states.async_set('geo_location.entity', 'hello', { + 'latitude': 32.880586, + 'longitude': -117.237564, + 'source': 'test_source' + }) + await hass.async_block_till_done() + + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'geo_location', + 'source': 'test_source', + 'zone': 'zone.test', + 'event': 'leave', + }, + 'action': { + 'service': 'test.automation', + } + } + }) + + hass.states.async_set('geo_location.entity', 'hello', { + 'latitude': 32.881011, + 'longitude': -117.234758, + 'source': 'test_source' + }) + await hass.async_block_till_done() + + assert 1 == len(calls) + + +async def test_if_not_fires_for_leave_on_zone_enter(hass, calls): + """Test for not firing on zone enter.""" + hass.states.async_set('geo_location.entity', 'hello', { + 'latitude': 32.881011, + 'longitude': -117.234758, + 'source': 'test_source' + }) + await hass.async_block_till_done() + + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'geo_location', + 'source': 'test_source', + 'zone': 'zone.test', + 'event': 'leave', + }, + 'action': { + 'service': 'test.automation', + } + } + }) + + hass.states.async_set('geo_location.entity', 'hello', { + 'latitude': 32.880586, + 'longitude': -117.237564 + }) + await hass.async_block_till_done() + + assert 0 == len(calls) + + +async def test_if_fires_on_zone_appear(hass, calls): + """Test for firing if entity appears in zone.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'geo_location', + 'source': 'test_source', + 'zone': 'zone.test', + 'event': 'enter', + }, + 'action': { + 'service': 'test.automation', + 'data_template': { + 'some': '{{ trigger.%s }}' % '}} - {{ trigger.'.join(( + 'platform', 'entity_id', + 'from_state.state', 'to_state.state', + 'zone.name')) }, - 'action': { - 'service': 'test.automation', - } + } - }) + } + }) - self.hass.states.set('geo_location.entity', 'hello', { - 'latitude': 32.881011, - 'longitude': -117.234758 - }) - self.hass.block_till_done() + # Entity appears in zone without previously existing outside the zone. + context = Context() + hass.states.async_set('geo_location.entity', 'hello', { + 'latitude': 32.880586, + 'longitude': -117.237564, + 'source': 'test_source' + }, context=context) + await hass.async_block_till_done() - assert 0 == len(self.calls) + assert 1 == len(calls) + assert calls[0].context is context + assert 'geo_location - geo_location.entity - - hello - test' == \ + calls[0].data['some'] - def test_if_fires_on_zone_leave(self): - """Test for firing on zone leave.""" - self.hass.states.set('geo_location.entity', 'hello', { - 'latitude': 32.880586, - 'longitude': -117.237564, - 'source': 'test_source' - }) - self.hass.block_till_done() - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'geo_location', - 'source': 'test_source', - 'zone': 'zone.test', - 'event': 'leave', +async def test_if_fires_on_zone_disappear(hass, calls): + """Test for firing if entity disappears from zone.""" + hass.states.async_set('geo_location.entity', 'hello', { + 'latitude': 32.880586, + 'longitude': -117.237564, + 'source': 'test_source' + }) + await hass.async_block_till_done() + + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'geo_location', + 'source': 'test_source', + 'zone': 'zone.test', + 'event': 'leave', + }, + 'action': { + 'service': 'test.automation', + 'data_template': { + 'some': '{{ trigger.%s }}' % '}} - {{ trigger.'.join(( + 'platform', 'entity_id', + 'from_state.state', 'to_state.state', + 'zone.name')) }, - 'action': { - 'service': 'test.automation', - } + } - }) + } + }) - self.hass.states.set('geo_location.entity', 'hello', { - 'latitude': 32.881011, - 'longitude': -117.234758, - 'source': 'test_source' - }) - self.hass.block_till_done() + # Entity disappears from zone without new coordinates outside the zone. + hass.states.async_remove('geo_location.entity') + await hass.async_block_till_done() - assert 1 == len(self.calls) - - def test_if_not_fires_for_leave_on_zone_enter(self): - """Test for not firing on zone enter.""" - self.hass.states.set('geo_location.entity', 'hello', { - 'latitude': 32.881011, - 'longitude': -117.234758, - 'source': 'test_source' - }) - self.hass.block_till_done() - - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'geo_location', - 'source': 'test_source', - 'zone': 'zone.test', - 'event': 'leave', - }, - 'action': { - 'service': 'test.automation', - } - } - }) - - self.hass.states.set('geo_location.entity', 'hello', { - 'latitude': 32.880586, - 'longitude': -117.237564 - }) - self.hass.block_till_done() - - assert 0 == len(self.calls) - - def test_if_fires_on_zone_appear(self): - """Test for firing if entity appears in zone.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'geo_location', - 'source': 'test_source', - 'zone': 'zone.test', - 'event': 'enter', - }, - 'action': { - 'service': 'test.automation', - 'data_template': { - 'some': '{{ trigger.%s }}' % '}} - {{ trigger.'.join(( - 'platform', 'entity_id', - 'from_state.state', 'to_state.state', - 'zone.name')) - }, - - } - } - }) - - # Entity appears in zone without previously existing outside the zone. - context = Context() - self.hass.states.set('geo_location.entity', 'hello', { - 'latitude': 32.880586, - 'longitude': -117.237564, - 'source': 'test_source' - }, context=context) - self.hass.block_till_done() - - assert 1 == len(self.calls) - assert self.calls[0].context is context - assert 'geo_location - geo_location.entity - - hello - test' == \ - self.calls[0].data['some'] - - def test_if_fires_on_zone_disappear(self): - """Test for firing if entity disappears from zone.""" - self.hass.states.set('geo_location.entity', 'hello', { - 'latitude': 32.880586, - 'longitude': -117.237564, - 'source': 'test_source' - }) - self.hass.block_till_done() - - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'geo_location', - 'source': 'test_source', - 'zone': 'zone.test', - 'event': 'leave', - }, - 'action': { - 'service': 'test.automation', - 'data_template': { - 'some': '{{ trigger.%s }}' % '}} - {{ trigger.'.join(( - 'platform', 'entity_id', - 'from_state.state', 'to_state.state', - 'zone.name')) - }, - - } - } - }) - - # Entity disappears from zone without new coordinates outside the zone. - self.hass.states.async_remove('geo_location.entity') - self.hass.block_till_done() - - assert 1 == len(self.calls) - assert 'geo_location - geo_location.entity - hello - - test' == \ - self.calls[0].data['some'] + assert 1 == len(calls) + assert 'geo_location - geo_location.entity - hello - - test' == \ + calls[0].data['some'] diff --git a/tests/components/automation/test_init.py b/tests/components/automation/test_init.py index 015c318874a..28d4c0979c4 100644 --- a/tests/components/automation/test_init.py +++ b/tests/components/automation/test_init.py @@ -1,11 +1,12 @@ """The tests for the automation component.""" import asyncio from datetime import timedelta -import unittest from unittest.mock import patch +import pytest + from homeassistant.core import State, CoreState -from homeassistant.setup import setup_component, async_setup_component +from homeassistant.setup import async_setup_component import homeassistant.components.automation as automation from homeassistant.const import ( ATTR_ENTITY_ID, STATE_ON, STATE_OFF, EVENT_HOMEASSISTANT_START) @@ -13,462 +14,428 @@ from homeassistant.exceptions import HomeAssistantError import homeassistant.util.dt as dt_util from tests.common import ( - assert_setup_component, get_test_home_assistant, fire_time_changed, - mock_service, async_mock_service, mock_restore_cache) + assert_setup_component, async_fire_time_changed, + mock_restore_cache, async_mock_service) from tests.components.automation import common -# pylint: disable=invalid-name -class TestAutomation(unittest.TestCase): - """Test the event automation.""" +@pytest.fixture +def calls(hass): + """Track calls to a mock serivce.""" + return async_mock_service(hass, 'test', 'automation') - def setUp(self): - """Set up things to be run when tests are started.""" - self.hass = get_test_home_assistant() - self.calls = mock_service(self.hass, 'test', 'automation') - def tearDown(self): - """Stop everything that was started.""" - self.hass.stop() - - def test_service_data_not_a_dict(self): - """Test service data not dict.""" - with assert_setup_component(0, automation.DOMAIN): - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event', - }, - 'action': { - 'service': 'test.automation', - 'data': 100, - } - } - }) - - def test_service_specify_data(self): - """Test service data.""" - assert setup_component(self.hass, automation.DOMAIN, { +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, { automation.DOMAIN: { - 'alias': 'hello', 'trigger': { 'platform': 'event', 'event_type': 'test_event', }, 'action': { + 'service': 'test.automation', + 'data': 100, + } + } + }) + + +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 }}' + }, + } + } + }) + + time = dt_util.utcnow() + + 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': [ + { 'service': 'test.automation', 'data_template': { 'some': '{{ trigger.platform }} - ' '{{ trigger.event.event_type }}' - }, - } - } - }) - - time = dt_util.utcnow() - - with patch('homeassistant.components.automation.utcnow', - return_value=time): - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert len(self.calls) == 1 - assert self.calls[0].data['some'] == 'event - test_event' - state = self.hass.states.get('automation.hello') - assert state is not None - assert state.attributes.get('last_triggered') == time - - state = self.hass.states.get('group.all_automations') - assert state is not None - assert state.attributes.get('entity_id') == ('automation.hello',) - - def test_action_delay(self): - """Test action delay.""" - assert setup_component(self.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 }}' - } - }, - {'delay': {'minutes': '10'}}, - { - 'service': 'test.automation', - 'data_template': { - 'some': '{{ trigger.platform }} - ' - '{{ trigger.event.event_type }}' - } - }, - ] - } - }) - - time = dt_util.utcnow() - - with patch('homeassistant.components.automation.utcnow', - return_value=time): - self.hass.bus.fire('test_event') - self.hass.block_till_done() - - assert len(self.calls) == 1 - assert self.calls[0].data['some'] == 'event - test_event' - - future = dt_util.utcnow() + timedelta(minutes=10) - fire_time_changed(self.hass, future) - self.hass.block_till_done() - - assert len(self.calls) == 2 - assert self.calls[1].data['some'] == 'event - test_event' - - state = self.hass.states.get('automation.hello') - assert state is not None - assert state.attributes.get('last_triggered') == time - state = self.hass.states.get('group.all_automations') - assert state is not None - assert state.attributes.get('entity_id') == ('automation.hello',) - - def test_service_specify_entity_id(self): - """Test service data.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event', - }, - 'action': { - 'service': 'test.automation', - 'entity_id': 'hello.world' - } - } - }) - - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 1 == len(self.calls) - assert ['hello.world'] == \ - self.calls[0].data.get(ATTR_ENTITY_ID) - - def test_service_specify_entity_id_list(self): - """Test service data.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event', - }, - 'action': { - 'service': 'test.automation', - 'entity_id': ['hello.world', 'hello.world2'] - } - } - }) - - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 1 == len(self.calls) - assert ['hello.world', 'hello.world2'] == \ - self.calls[0].data.get(ATTR_ENTITY_ID) - - def test_two_triggers(self): - """Test triggers.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': [ - { - 'platform': 'event', - 'event_type': 'test_event', - }, - { - 'platform': 'state', - 'entity_id': 'test.entity', } - ], - 'action': { - 'service': 'test.automation', - } - } - }) - - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 1 == len(self.calls) - self.hass.states.set('test.entity', 'hello') - self.hass.block_till_done() - assert 2 == len(self.calls) - - def test_trigger_service_ignoring_condition(self): - """Test triggers.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'alias': 'test', - 'trigger': [ - { - 'platform': 'event', - 'event_type': 'test_event', - }, - ], - 'condition': { - 'condition': 'state', - 'entity_id': 'non.existing', - 'state': 'beer', }, - 'action': { - 'service': 'test.automation', - } - } - }) - - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert len(self.calls) == 0 - - self.hass.services.call('automation', 'trigger', - {'entity_id': 'automation.test'}, - blocking=True) - self.hass.block_till_done() - assert len(self.calls) == 1 - - def test_two_conditions_with_and(self): - """Test two and conditions.""" - entity_id = 'test.entity' - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': [ - { - 'platform': 'event', - 'event_type': 'test_event', - }, - ], - 'condition': [ - { - 'condition': 'state', - 'entity_id': entity_id, - 'state': '100' - }, - { - 'condition': 'numeric_state', - 'entity_id': entity_id, - 'below': 150 - } - ], - 'action': { - 'service': 'test.automation', - } - } - }) - - self.hass.states.set(entity_id, 100) - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 1 == len(self.calls) - - self.hass.states.set(entity_id, 101) - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 1 == len(self.calls) - - self.hass.states.set(entity_id, 151) - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_automation_list_setting(self): - """Event is not a valid condition.""" - assert setup_component(self.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', - } - }] - }) - - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 1 == len(self.calls) - - self.hass.bus.fire('test_event_2') - self.hass.block_till_done() - assert 2 == len(self.calls) - - def test_automation_calling_two_actions(self): - """Test if we can call two actions from automation definition.""" - assert setup_component(self.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}, - }], - } - }) - - self.hass.bus.fire('test_event') - self.hass.block_till_done() - - assert len(self.calls) == 2 - assert self.calls[0].data['position'] == 0 - assert self.calls[1].data['position'] == 1 - - def test_services(self): - """Test the automation services for turning entities on/off.""" - entity_id = 'automation.hello' - - assert self.hass.states.get(entity_id) is None - assert not automation.is_on(self.hass, entity_id) - - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'alias': 'hello', - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event', - }, - 'action': { - 'service': 'test.automation', - } - } - }) - - assert self.hass.states.get(entity_id) is not None - assert automation.is_on(self.hass, entity_id) - - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert len(self.calls) == 1 - - common.turn_off(self.hass, entity_id) - self.hass.block_till_done() - - assert not automation.is_on(self.hass, entity_id) - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert len(self.calls) == 1 - - common.toggle(self.hass, entity_id) - self.hass.block_till_done() - - assert automation.is_on(self.hass, entity_id) - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert len(self.calls) == 2 - - common.trigger(self.hass, entity_id) - self.hass.block_till_done() - assert len(self.calls) == 3 - - common.turn_off(self.hass, entity_id) - self.hass.block_till_done() - common.trigger(self.hass, entity_id) - self.hass.block_till_done() - assert len(self.calls) == 4 - - common.turn_on(self.hass, entity_id) - self.hass.block_till_done() - assert automation.is_on(self.hass, entity_id) - - def test_reload_config_service(self): - """Test the reload config service.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'alias': 'hello', - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event', - }, - 'action': { + {'delay': {'minutes': '10'}}, + { 'service': 'test.automation', 'data_template': { - 'event': '{{ trigger.event.event_type }}' + 'some': '{{ trigger.platform }} - ' + '{{ trigger.event.event_type }}' } + }, + ] + } + }) + + time = dt_util.utcnow() + + 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' + } + } + }) + + 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': [ + { + 'platform': 'event', + 'event_type': 'test_event', + }, + { + 'platform': 'state', + 'entity_id': 'test.entity', + } + ], + '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': [ + { + 'platform': 'event', + 'event_type': 'test_event', + }, + ], + 'condition': { + 'condition': 'state', + 'entity_id': 'non.existing', + 'state': 'beer', + }, + 'action': { + 'service': 'test.automation', + } + } + }) + + 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': [ + { + 'platform': 'event', + 'event_type': 'test_event', + }, + ], + 'condition': [ + { + 'condition': 'state', + 'entity_id': entity_id, + 'state': '100' + }, + { + 'condition': 'numeric_state', + 'entity_id': entity_id, + 'below': 150 + } + ], + 'action': { + 'service': 'test.automation', + } + } + }) + + 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 + + +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 }}' } } - }) - assert self.hass.states.get('automation.hello') is not None - assert self.hass.states.get('automation.bye') is None - listeners = self.hass.bus.listeners - assert listeners.get('test_event') == 1 - assert listeners.get('test_event2') is None + } + }) + 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 - self.hass.bus.fire('test_event') - self.hass.block_till_done() + hass.bus.async_fire('test_event') + await hass.async_block_till_done() - assert len(self.calls) == 1 - assert self.calls[0].data.get('event') == 'test_event' + 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: { - 'alias': 'bye', - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event2', - }, - 'action': { - 'service': 'test.automation', - 'data_template': { - 'event': '{{ trigger.event.event_type }}' - } - } - }}): - with patch('homeassistant.config.find_config_file', - return_value=''): - common.reload(self.hass) - self.hass.block_till_done() - # De-flake ?! - self.hass.block_till_done() - - assert self.hass.states.get('automation.hello') is None - assert self.hass.states.get('automation.bye') is not None - listeners = self.hass.bus.listeners - assert listeners.get('test_event') is None - assert listeners.get('test_event2') == 1 - - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert len(self.calls) == 1 - - self.hass.bus.fire('test_event2') - self.hass.block_till_done() - assert len(self.calls) == 2 - assert self.calls[1].data.get('event') == 'test_event2' - - def test_reload_config_when_invalid_config(self): - """Test the reload config service handling invalid config.""" - with assert_setup_component(1, automation.DOMAIN): - assert setup_component(self.hass, automation.DOMAIN, { + with patch('homeassistant.config.load_yaml_config_file', autospec=True, + return_value={ automation.DOMAIN: { - 'alias': 'hello', + 'alias': 'bye', 'trigger': { 'platform': 'event', - 'event_type': 'test_event', + 'event_type': 'test_event2', }, 'action': { 'service': 'test.automation', @@ -476,32 +443,34 @@ class TestAutomation(unittest.TestCase): 'event': '{{ trigger.event.event_type }}' } } - } - }) - assert self.hass.states.get('automation.hello') is not None + }}): + 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() - self.hass.bus.fire('test_event') - self.hass.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 - assert len(self.calls) == 1 - assert self.calls[0].data.get('event') == 'test_event' + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + assert len(calls) == 1 - 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=''): - common.reload(self.hass) - self.hass.block_till_done() + hass.bus.async_fire('test_event2') + await hass.async_block_till_done() + assert len(calls) == 2 + assert calls[1].data.get('event') == 'test_event2' - assert self.hass.states.get('automation.hello') is None - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert len(self.calls) == 1 - - def test_reload_config_handles_load_fails(self): - """Test the reload config service.""" - assert setup_component(self.hass, automation.DOMAIN, { +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, { automation.DOMAIN: { 'alias': 'hello', 'trigger': { @@ -516,26 +485,65 @@ class TestAutomation(unittest.TestCase): } } }) - assert self.hass.states.get('automation.hello') is not None + assert hass.states.get('automation.hello') is not None - self.hass.bus.fire('test_event') - self.hass.block_till_done() + hass.bus.async_fire('test_event') + await hass.async_block_till_done() - assert len(self.calls) == 1 - assert self.calls[0].data.get('event') == 'test_event' + assert len(calls) == 1 + assert calls[0].data.get('event') == 'test_event' - with patch('homeassistant.config.load_yaml_config_file', - side_effect=HomeAssistantError('bla')): - with patch('homeassistant.config.find_config_file', - return_value=''): - common.reload(self.hass) - self.hass.block_till_done() + 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 self.hass.states.get('automation.hello') is not None + assert hass.states.get('automation.hello') is None - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert len(self.calls) == 2 + 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 + + 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', + side_effect=HomeAssistantError('bla')): + 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 not None + + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + assert len(calls) == 2 @asyncio.coroutine diff --git a/tests/components/automation/test_mqtt.py b/tests/components/automation/test_mqtt.py index 2d43c4a6d65..196fdaa9a6f 100644 --- a/tests/components/automation/test_mqtt.py +++ b/tests/components/automation/test_mqtt.py @@ -1,102 +1,94 @@ """The tests for the MQTT automation.""" -import unittest +import pytest -from homeassistant.core import callback -from homeassistant.setup import setup_component +from homeassistant.setup import async_setup_component import homeassistant.components.automation as automation from tests.common import ( - mock_mqtt_component, fire_mqtt_message, get_test_home_assistant, - mock_component) + async_fire_mqtt_message, + mock_component, async_mock_service, async_mock_mqtt_component) from tests.components.automation import common -# pylint: disable=invalid-name -class TestAutomationMQTT(unittest.TestCase): - """Test the event automation.""" +@pytest.fixture +def calls(hass): + """Track calls to a mock serivce.""" + return async_mock_service(hass, 'test', 'automation') - def setUp(self): - """Set up things to be run when tests are started.""" - self.hass = get_test_home_assistant() - mock_component(self.hass, 'group') - mock_mqtt_component(self.hass) - self.calls = [] - @callback - def record_call(service): - """Record calls.""" - self.calls.append(service) +@pytest.fixture(autouse=True) +def setup_comp(hass): + """Initialize components.""" + mock_component(hass, 'group') + hass.loop.run_until_complete(async_mock_mqtt_component(hass)) - self.hass.services.register('test', 'automation', record_call) - def tearDown(self): - """Stop everything that was started.""" - self.hass.stop() - - def test_if_fires_on_topic_match(self): - """Test if message is fired on topic match.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'mqtt', - 'topic': 'test-topic' +async def test_if_fires_on_topic_match(hass, calls): + """Test if message is fired on topic match.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'mqtt', + 'topic': 'test-topic' + }, + 'action': { + 'service': 'test.automation', + 'data_template': { + 'some': '{{ trigger.platform }} - {{ trigger.topic }}' + ' - {{ trigger.payload }} - ' + '{{ trigger.payload_json.hello }}' }, - 'action': { - 'service': 'test.automation', - 'data_template': { - 'some': '{{ trigger.platform }} - {{ trigger.topic }}' - ' - {{ trigger.payload }} - ' - '{{ trigger.payload_json.hello }}' - }, - } } - }) + } + }) - fire_mqtt_message(self.hass, 'test-topic', '{ "hello": "world" }') - self.hass.block_till_done() - assert 1 == len(self.calls) - assert 'mqtt - test-topic - { "hello": "world" } - world' == \ - self.calls[0].data['some'] + async_fire_mqtt_message(hass, 'test-topic', '{ "hello": "world" }') + await hass.async_block_till_done() + assert 1 == len(calls) + assert 'mqtt - test-topic - { "hello": "world" } - world' == \ + calls[0].data['some'] - common.turn_off(self.hass) - self.hass.block_till_done() - fire_mqtt_message(self.hass, 'test-topic', 'test_payload') - self.hass.block_till_done() - assert 1 == len(self.calls) + await common.async_turn_off(hass) + await hass.async_block_till_done() + async_fire_mqtt_message(hass, 'test-topic', 'test_payload') + await hass.async_block_till_done() + assert 1 == len(calls) - def test_if_fires_on_topic_and_payload_match(self): - """Test if message is fired on topic and payload match.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'mqtt', - 'topic': 'test-topic', - 'payload': 'hello' - }, - 'action': { - 'service': 'test.automation' - } + +async def test_if_fires_on_topic_and_payload_match(hass, calls): + """Test if message is fired on topic and payload match.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'mqtt', + 'topic': 'test-topic', + 'payload': 'hello' + }, + 'action': { + 'service': 'test.automation' } - }) + } + }) - fire_mqtt_message(self.hass, 'test-topic', 'hello') - self.hass.block_till_done() - assert 1 == len(self.calls) + async_fire_mqtt_message(hass, 'test-topic', 'hello') + await hass.async_block_till_done() + assert 1 == len(calls) - def test_if_not_fires_on_topic_but_no_payload_match(self): - """Test if message is not fired on topic but no payload.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'mqtt', - 'topic': 'test-topic', - 'payload': 'hello' - }, - 'action': { - 'service': 'test.automation' - } + +async def test_if_not_fires_on_topic_but_no_payload_match(hass, calls): + """Test if message is not fired on topic but no payload.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'mqtt', + 'topic': 'test-topic', + 'payload': 'hello' + }, + 'action': { + 'service': 'test.automation' } - }) + } + }) - fire_mqtt_message(self.hass, 'test-topic', 'no-hello') - self.hass.block_till_done() - assert 0 == len(self.calls) + async_fire_mqtt_message(hass, 'test-topic', 'no-hello') + await hass.async_block_till_done() + assert 0 == len(calls) diff --git a/tests/components/automation/test_numeric_state.py b/tests/components/automation/test_numeric_state.py index f7124be9dab..92a5f3b8b92 100644 --- a/tests/components/automation/test_numeric_state.py +++ b/tests/components/automation/test_numeric_state.py @@ -1,876 +1,908 @@ """The tests for numeric state automation.""" from datetime import timedelta -import unittest +import pytest from unittest.mock import patch import homeassistant.components.automation as automation -from homeassistant.core import Context, callback -from homeassistant.setup import setup_component +from homeassistant.core import Context +from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util from tests.common import ( - get_test_home_assistant, mock_component, fire_time_changed, - assert_setup_component) + mock_component, async_fire_time_changed, + assert_setup_component, async_mock_service) from tests.components.automation import common -# pylint: disable=invalid-name -class TestAutomationNumericState(unittest.TestCase): - """Test the event automation.""" +@pytest.fixture +def calls(hass): + """Track calls to a mock serivce.""" + return async_mock_service(hass, 'test', 'automation') - def setUp(self): - """Set up things to be run when tests are started.""" - self.hass = get_test_home_assistant() - mock_component(self.hass, 'group') - self.calls = [] - @callback - def record_call(service): - """Record calls.""" - self.calls.append(service) +@pytest.fixture(autouse=True) +def setup_comp(hass): + """Initialize components.""" + mock_component(hass, 'group') - self.hass.services.register('test', 'automation', record_call) - def tearDown(self): # pylint: disable=invalid-name - """Stop everything that was started.""" - self.hass.stop() - - def test_if_fires_on_entity_change_below(self): - """Test the firing with changed entity.""" - context = Context() - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'below': 10, - }, - 'action': { - 'service': 'test.automation' - } +async def test_if_fires_on_entity_change_below(hass, calls): + """Test the firing with changed entity.""" + context = Context() + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'below': 10, + }, + 'action': { + 'service': 'test.automation' } - }) - # 9 is below 10 - self.hass.states.set('test.entity', 9, context=context) - self.hass.block_till_done() - assert 1 == len(self.calls) - assert self.calls[0].context is context + } + }) + # 9 is below 10 + hass.states.async_set('test.entity', 9, context=context) + await hass.async_block_till_done() + assert 1 == len(calls) + assert calls[0].context is context - # Set above 12 so the automation will fire again - self.hass.states.set('test.entity', 12) - common.turn_off(self.hass) - self.hass.block_till_done() - self.hass.states.set('test.entity', 9) - self.hass.block_till_done() - assert 1 == len(self.calls) + # Set above 12 so the automation will fire again + hass.states.async_set('test.entity', 12) + await common.async_turn_off(hass) + await hass.async_block_till_done() + hass.states.async_set('test.entity', 9) + await hass.async_block_till_done() + assert 1 == len(calls) - def test_if_fires_on_entity_change_over_to_below(self): - """Test the firing with changed entity.""" - self.hass.states.set('test.entity', 11) - self.hass.block_till_done() - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'below': 10, - }, - 'action': { - 'service': 'test.automation' - } +async def test_if_fires_on_entity_change_over_to_below(hass, calls): + """Test the firing with changed entity.""" + hass.states.async_set('test.entity', 11) + await hass.async_block_till_done() + + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'below': 10, + }, + 'action': { + 'service': 'test.automation' } - }) - - # 9 is below 10 - self.hass.states.set('test.entity', 9) - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_if_fires_on_entities_change_over_to_below(self): - """Test the firing with changed entities.""" - self.hass.states.set('test.entity_1', 11) - self.hass.states.set('test.entity_2', 11) - self.hass.block_till_done() - - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': [ - 'test.entity_1', - 'test.entity_2', - ], - 'below': 10, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - # 9 is below 10 - self.hass.states.set('test.entity_1', 9) - self.hass.block_till_done() - assert 1 == len(self.calls) - self.hass.states.set('test.entity_2', 9) - self.hass.block_till_done() - assert 2 == len(self.calls) - - def test_if_not_fires_on_entity_change_below_to_below(self): - """Test the firing with changed entity.""" - context = Context() - self.hass.states.set('test.entity', 11) - self.hass.block_till_done() - - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'below': 10, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - # 9 is below 10 so this should fire - self.hass.states.set('test.entity', 9, context=context) - self.hass.block_till_done() - assert 1 == len(self.calls) - assert self.calls[0].context is context - - # already below so should not fire again - self.hass.states.set('test.entity', 5) - self.hass.block_till_done() - assert 1 == len(self.calls) - - # still below so should not fire again - self.hass.states.set('test.entity', 3) - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_if_not_below_fires_on_entity_change_to_equal(self): - """Test the firing with changed entity.""" - self.hass.states.set('test.entity', 11) - self.hass.block_till_done() - - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'below': 10, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - # 10 is not below 10 so this should not fire again - self.hass.states.set('test.entity', 10) - self.hass.block_till_done() - assert 0 == len(self.calls) - - def test_if_fires_on_initial_entity_below(self): - """Test the firing when starting with a match.""" - self.hass.states.set('test.entity', 9) - self.hass.block_till_done() - - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'below': 10, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - # Fire on first update even if initial state was already below - self.hass.states.set('test.entity', 8) - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_if_fires_on_initial_entity_above(self): - """Test the firing when starting with a match.""" - self.hass.states.set('test.entity', 11) - self.hass.block_till_done() - - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'above': 10, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - # Fire on first update even if initial state was already above - self.hass.states.set('test.entity', 12) - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_if_fires_on_entity_change_above(self): - """Test the firing with changed entity.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'above': 10, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - # 11 is above 10 - self.hass.states.set('test.entity', 11) - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_if_fires_on_entity_change_below_to_above(self): - """Test the firing with changed entity.""" - # set initial state - self.hass.states.set('test.entity', 9) - self.hass.block_till_done() - - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'above': 10, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - # 11 is above 10 and 9 is below - self.hass.states.set('test.entity', 11) - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_if_not_fires_on_entity_change_above_to_above(self): - """Test the firing with changed entity.""" - # set initial state - self.hass.states.set('test.entity', 9) - self.hass.block_till_done() - - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'above': 10, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - # 12 is above 10 so this should fire - self.hass.states.set('test.entity', 12) - self.hass.block_till_done() - assert 1 == len(self.calls) - - # already above, should not fire again - self.hass.states.set('test.entity', 15) - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_if_not_above_fires_on_entity_change_to_equal(self): - """Test the firing with changed entity.""" - # set initial state - self.hass.states.set('test.entity', 9) - self.hass.block_till_done() - - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'above': 10, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - # 10 is not above 10 so this should not fire again - self.hass.states.set('test.entity', 10) - self.hass.block_till_done() - assert 0 == len(self.calls) - - def test_if_fires_on_entity_change_below_range(self): - """Test the firing with changed entity.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'below': 10, - 'above': 5, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - # 9 is below 10 - self.hass.states.set('test.entity', 9) - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_if_fires_on_entity_change_below_above_range(self): - """Test the firing with changed entity.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'below': 10, - 'above': 5, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - # 4 is below 5 - self.hass.states.set('test.entity', 4) - self.hass.block_till_done() - assert 0 == len(self.calls) - - def test_if_fires_on_entity_change_over_to_below_range(self): - """Test the firing with changed entity.""" - self.hass.states.set('test.entity', 11) - self.hass.block_till_done() - - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'below': 10, - 'above': 5, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - # 9 is below 10 - self.hass.states.set('test.entity', 9) - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_if_fires_on_entity_change_over_to_below_above_range(self): - """Test the firing with changed entity.""" - self.hass.states.set('test.entity', 11) - self.hass.block_till_done() - - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'below': 10, - 'above': 5, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - # 4 is below 5 so it should not fire - self.hass.states.set('test.entity', 4) - self.hass.block_till_done() - assert 0 == len(self.calls) - - def test_if_not_fires_if_entity_not_match(self): - """Test if not fired with non matching entity.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.another_entity', - 'below': 100, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - self.hass.states.set('test.entity', 11) - self.hass.block_till_done() - assert 0 == len(self.calls) - - def test_if_fires_on_entity_change_below_with_attribute(self): - """Test attributes change.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'below': 10, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - # 9 is below 10 - self.hass.states.set('test.entity', 9, {'test_attribute': 11}) - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_if_not_fires_on_entity_change_not_below_with_attribute(self): - """Test attributes.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'below': 10, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - # 11 is not below 10 - self.hass.states.set('test.entity', 11, {'test_attribute': 9}) - self.hass.block_till_done() - assert 0 == len(self.calls) - - def test_if_fires_on_attribute_change_with_attribute_below(self): - """Test attributes change.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'value_template': '{{ state.attributes.test_attribute }}', - 'below': 10, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - # 9 is below 10 - self.hass.states.set('test.entity', 'entity', {'test_attribute': 9}) - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_if_not_fires_on_attribute_change_with_attribute_not_below(self): - """Test attributes change.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'value_template': '{{ state.attributes.test_attribute }}', - 'below': 10, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - # 11 is not below 10 - self.hass.states.set('test.entity', 'entity', {'test_attribute': 11}) - self.hass.block_till_done() - assert 0 == len(self.calls) - - def test_if_not_fires_on_entity_change_with_attribute_below(self): - """Test attributes change.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'value_template': '{{ state.attributes.test_attribute }}', - 'below': 10, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - # 11 is not below 10, entity state value should not be tested - self.hass.states.set('test.entity', '9', {'test_attribute': 11}) - self.hass.block_till_done() - assert 0 == len(self.calls) - - def test_if_not_fires_on_entity_change_with_not_attribute_below(self): - """Test attributes change.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'value_template': '{{ state.attributes.test_attribute }}', - 'below': 10, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - # 11 is not below 10, entity state value should not be tested - self.hass.states.set('test.entity', 'entity') - self.hass.block_till_done() - assert 0 == len(self.calls) - - def test_fires_on_attr_change_with_attribute_below_and_multiple_attr(self): - """Test attributes change.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'value_template': '{{ state.attributes.test_attribute }}', - 'below': 10, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - # 9 is not below 10 - self.hass.states.set('test.entity', 'entity', - {'test_attribute': 9, 'not_test_attribute': 11}) - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_template_list(self): - """Test template list.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'value_template': - '{{ state.attributes.test_attribute[2] }}', - 'below': 10, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - # 3 is below 10 - self.hass.states.set('test.entity', 'entity', - {'test_attribute': [11, 15, 3]}) - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_template_string(self): - """Test template string.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'value_template': - '{{ state.attributes.test_attribute | multiply(10) }}', - 'below': 10, - }, - 'action': { - 'service': 'test.automation', - 'data_template': { - 'some': '{{ trigger.%s }}' % '}} - {{ trigger.'.join(( - 'platform', 'entity_id', 'below', 'above', - 'from_state.state', 'to_state.state')) - }, - } - } - }) - self.hass.states.set('test.entity', 'test state 1', - {'test_attribute': '1.2'}) - self.hass.block_till_done() - self.hass.states.set('test.entity', 'test state 2', - {'test_attribute': '0.9'}) - self.hass.block_till_done() - assert 1 == len(self.calls) - assert 'numeric_state - test.entity - 10.0 - None - test state 1 - ' \ - 'test state 2' == \ - self.calls[0].data['some'] - - def test_not_fires_on_attr_change_with_attr_not_below_multiple_attr(self): - """Test if not fired changed attributes.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'value_template': '{{ state.attributes.test_attribute }}', - 'below': 10, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - # 11 is not below 10 - self.hass.states.set('test.entity', 'entity', - {'test_attribute': 11, 'not_test_attribute': 9}) - self.hass.block_till_done() - assert 0 == len(self.calls) - - def test_if_action(self): - """Test if action.""" - entity_id = 'domain.test_entity' - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event', - }, - 'condition': { - 'condition': 'numeric_state', - 'entity_id': entity_id, - 'above': 8, - 'below': 12, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - self.hass.states.set(entity_id, 10) - self.hass.bus.fire('test_event') - self.hass.block_till_done() - - assert 1 == len(self.calls) - - self.hass.states.set(entity_id, 8) - self.hass.bus.fire('test_event') - self.hass.block_till_done() - - assert 1 == len(self.calls) - - self.hass.states.set(entity_id, 9) - self.hass.bus.fire('test_event') - self.hass.block_till_done() - - assert 2 == len(self.calls) - - def test_if_fails_setup_bad_for(self): - """Test for setup failure for bad for.""" - with assert_setup_component(0): - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'above': 8, - 'below': 12, - 'for': { - 'invalid': 5 - }, - }, - 'action': { - 'service': 'homeassistant.turn_on', - } - }}) - - def test_if_fails_setup_for_without_above_below(self): - """Test for setup failures for missing above or below.""" - with assert_setup_component(0): - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'for': { - 'seconds': 5 - }, - }, - 'action': { - 'service': 'homeassistant.turn_on', - } - }}) - - def test_if_not_fires_on_entity_change_with_for(self): - """Test for not firing on entity change with for.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'above': 8, - 'below': 12, - 'for': { - 'seconds': 5 - }, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - self.hass.states.set('test.entity', 9) - self.hass.block_till_done() - self.hass.states.set('test.entity', 15) - self.hass.block_till_done() - fire_time_changed(self.hass, dt_util.utcnow() + timedelta(seconds=10)) - self.hass.block_till_done() - assert 0 == len(self.calls) - - def test_if_not_fires_on_entities_change_with_for_after_stop(self): - """Test for not firing on entities change with for after stop.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': [ - 'test.entity_1', - 'test.entity_2', - ], - 'above': 8, - 'below': 12, - 'for': { - 'seconds': 5 - }, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - self.hass.states.set('test.entity_1', 9) - self.hass.states.set('test.entity_2', 9) - self.hass.block_till_done() - fire_time_changed(self.hass, dt_util.utcnow() + timedelta(seconds=10)) - self.hass.block_till_done() - assert 2 == len(self.calls) - - self.hass.states.set('test.entity_1', 15) - self.hass.states.set('test.entity_2', 15) - self.hass.block_till_done() - self.hass.states.set('test.entity_1', 9) - self.hass.states.set('test.entity_2', 9) - self.hass.block_till_done() - common.turn_off(self.hass) - self.hass.block_till_done() - - fire_time_changed(self.hass, dt_util.utcnow() + timedelta(seconds=10)) - self.hass.block_till_done() - assert 2 == len(self.calls) - - def test_if_fires_on_entity_change_with_for_attribute_change(self): - """Test for firing on entity change with for and attribute change.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'above': 8, - 'below': 12, - 'for': { - 'seconds': 5 - }, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - utcnow = dt_util.utcnow() - with patch('homeassistant.core.dt_util.utcnow') as mock_utcnow: - mock_utcnow.return_value = utcnow - self.hass.states.set('test.entity', 9) - self.hass.block_till_done() - mock_utcnow.return_value += timedelta(seconds=4) - fire_time_changed(self.hass, mock_utcnow.return_value) - self.hass.states.set('test.entity', 9, - attributes={"mock_attr": "attr_change"}) - self.hass.block_till_done() - assert 0 == len(self.calls) - mock_utcnow.return_value += timedelta(seconds=4) - fire_time_changed(self.hass, mock_utcnow.return_value) - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_if_fires_on_entity_change_with_for(self): - """Test for firing on entity change with for.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'above': 8, - 'below': 12, - 'for': { - 'seconds': 5 - }, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - self.hass.states.set('test.entity', 9) - self.hass.block_till_done() - fire_time_changed(self.hass, dt_util.utcnow() + timedelta(seconds=10)) - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_wait_template_with_trigger(self): - """Test using wait template with 'trigger.entity_id'.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'numeric_state', - 'entity_id': 'test.entity', - 'above': 10, - }, - 'action': [ - {'wait_template': - "{{ states(trigger.entity_id) | int < 10 }}"}, - {'service': 'test.automation', - 'data_template': { - 'some': - '{{ trigger.%s }}' % '}} - {{ trigger.'.join(( - 'platform', 'entity_id', 'to_state.state')) - }} + } + }) + + # 9 is below 10 + hass.states.async_set('test.entity', 9) + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_fires_on_entities_change_over_to_below(hass, calls): + """Test the firing with changed entities.""" + hass.states.async_set('test.entity_1', 11) + hass.states.async_set('test.entity_2', 11) + await hass.async_block_till_done() + + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': [ + 'test.entity_1', + 'test.entity_2', ], + 'below': 10, + }, + 'action': { + 'service': 'test.automation' } - }) + } + }) - self.hass.block_till_done() - self.calls = [] + # 9 is below 10 + hass.states.async_set('test.entity_1', 9) + await hass.async_block_till_done() + assert 1 == len(calls) + hass.states.async_set('test.entity_2', 9) + await hass.async_block_till_done() + assert 2 == len(calls) - self.hass.states.set('test.entity', '12') - self.hass.block_till_done() - self.hass.states.set('test.entity', '8') - self.hass.block_till_done() - assert 1 == len(self.calls) - assert 'numeric_state - test.entity - 12' == \ - self.calls[0].data['some'] + +async def test_if_not_fires_on_entity_change_below_to_below(hass, calls): + """Test the firing with changed entity.""" + context = Context() + hass.states.async_set('test.entity', 11) + await hass.async_block_till_done() + + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'below': 10, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + # 9 is below 10 so this should fire + hass.states.async_set('test.entity', 9, context=context) + await hass.async_block_till_done() + assert 1 == len(calls) + assert calls[0].context is context + + # already below so should not fire again + hass.states.async_set('test.entity', 5) + await hass.async_block_till_done() + assert 1 == len(calls) + + # still below so should not fire again + hass.states.async_set('test.entity', 3) + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_not_below_fires_on_entity_change_to_equal(hass, calls): + """Test the firing with changed entity.""" + hass.states.async_set('test.entity', 11) + await hass.async_block_till_done() + + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'below': 10, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + # 10 is not below 10 so this should not fire again + hass.states.async_set('test.entity', 10) + await hass.async_block_till_done() + assert 0 == len(calls) + + +async def test_if_fires_on_initial_entity_below(hass, calls): + """Test the firing when starting with a match.""" + hass.states.async_set('test.entity', 9) + await hass.async_block_till_done() + + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'below': 10, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + # Fire on first update even if initial state was already below + hass.states.async_set('test.entity', 8) + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_fires_on_initial_entity_above(hass, calls): + """Test the firing when starting with a match.""" + hass.states.async_set('test.entity', 11) + await hass.async_block_till_done() + + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'above': 10, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + # Fire on first update even if initial state was already above + hass.states.async_set('test.entity', 12) + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_fires_on_entity_change_above(hass, calls): + """Test the firing with changed entity.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'above': 10, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + # 11 is above 10 + hass.states.async_set('test.entity', 11) + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_fires_on_entity_change_below_to_above(hass, calls): + """Test the firing with changed entity.""" + # set initial state + hass.states.async_set('test.entity', 9) + await hass.async_block_till_done() + + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'above': 10, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + # 11 is above 10 and 9 is below + hass.states.async_set('test.entity', 11) + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_not_fires_on_entity_change_above_to_above(hass, calls): + """Test the firing with changed entity.""" + # set initial state + hass.states.async_set('test.entity', 9) + await hass.async_block_till_done() + + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'above': 10, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + # 12 is above 10 so this should fire + hass.states.async_set('test.entity', 12) + await hass.async_block_till_done() + assert 1 == len(calls) + + # already above, should not fire again + hass.states.async_set('test.entity', 15) + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_not_above_fires_on_entity_change_to_equal(hass, calls): + """Test the firing with changed entity.""" + # set initial state + hass.states.async_set('test.entity', 9) + await hass.async_block_till_done() + + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'above': 10, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + # 10 is not above 10 so this should not fire again + hass.states.async_set('test.entity', 10) + await hass.async_block_till_done() + assert 0 == len(calls) + + +async def test_if_fires_on_entity_change_below_range(hass, calls): + """Test the firing with changed entity.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'below': 10, + 'above': 5, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + # 9 is below 10 + hass.states.async_set('test.entity', 9) + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_fires_on_entity_change_below_above_range(hass, calls): + """Test the firing with changed entity.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'below': 10, + 'above': 5, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + # 4 is below 5 + hass.states.async_set('test.entity', 4) + await hass.async_block_till_done() + assert 0 == len(calls) + + +async def test_if_fires_on_entity_change_over_to_below_range(hass, calls): + """Test the firing with changed entity.""" + hass.states.async_set('test.entity', 11) + await hass.async_block_till_done() + + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'below': 10, + 'above': 5, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + # 9 is below 10 + hass.states.async_set('test.entity', 9) + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_fires_on_entity_change_over_to_below_above_range( + hass, calls): + """Test the firing with changed entity.""" + hass.states.async_set('test.entity', 11) + await hass.async_block_till_done() + + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'below': 10, + 'above': 5, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + # 4 is below 5 so it should not fire + hass.states.async_set('test.entity', 4) + await hass.async_block_till_done() + assert 0 == len(calls) + + +async def test_if_not_fires_if_entity_not_match(hass, calls): + """Test if not fired with non matching entity.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.another_entity', + 'below': 100, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + hass.states.async_set('test.entity', 11) + await hass.async_block_till_done() + assert 0 == len(calls) + + +async def test_if_fires_on_entity_change_below_with_attribute(hass, calls): + """Test attributes change.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'below': 10, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + # 9 is below 10 + hass.states.async_set('test.entity', 9, {'test_attribute': 11}) + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_not_fires_on_entity_change_not_below_with_attribute( + hass, calls): + """Test attributes.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'below': 10, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + # 11 is not below 10 + hass.states.async_set('test.entity', 11, {'test_attribute': 9}) + await hass.async_block_till_done() + assert 0 == len(calls) + + +async def test_if_fires_on_attribute_change_with_attribute_below(hass, calls): + """Test attributes change.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'value_template': '{{ state.attributes.test_attribute }}', + 'below': 10, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + # 9 is below 10 + hass.states.async_set('test.entity', 'entity', {'test_attribute': 9}) + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_not_fires_on_attribute_change_with_attribute_not_below( + hass, calls): + """Test attributes change.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'value_template': '{{ state.attributes.test_attribute }}', + 'below': 10, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + # 11 is not below 10 + hass.states.async_set('test.entity', 'entity', {'test_attribute': 11}) + await hass.async_block_till_done() + assert 0 == len(calls) + + +async def test_if_not_fires_on_entity_change_with_attribute_below(hass, calls): + """Test attributes change.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'value_template': '{{ state.attributes.test_attribute }}', + 'below': 10, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + # 11 is not below 10, entity state value should not be tested + hass.states.async_set('test.entity', '9', {'test_attribute': 11}) + await hass.async_block_till_done() + assert 0 == len(calls) + + +async def test_if_not_fires_on_entity_change_with_not_attribute_below( + hass, calls): + """Test attributes change.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'value_template': '{{ state.attributes.test_attribute }}', + 'below': 10, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + # 11 is not below 10, entity state value should not be tested + hass.states.async_set('test.entity', 'entity') + await hass.async_block_till_done() + assert 0 == len(calls) + + +async def test_fires_on_attr_change_with_attribute_below_and_multiple_attr( + hass, calls): + """Test attributes change.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'value_template': '{{ state.attributes.test_attribute }}', + 'below': 10, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + # 9 is not below 10 + hass.states.async_set('test.entity', 'entity', + {'test_attribute': 9, 'not_test_attribute': 11}) + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_template_list(hass, calls): + """Test template list.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'value_template': + '{{ state.attributes.test_attribute[2] }}', + 'below': 10, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + # 3 is below 10 + hass.states.async_set('test.entity', 'entity', + {'test_attribute': [11, 15, 3]}) + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_template_string(hass, calls): + """Test template string.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'value_template': + '{{ state.attributes.test_attribute | multiply(10) }}', + 'below': 10, + }, + 'action': { + 'service': 'test.automation', + 'data_template': { + 'some': '{{ trigger.%s }}' % '}} - {{ trigger.'.join(( + 'platform', 'entity_id', 'below', 'above', + 'from_state.state', 'to_state.state')) + }, + } + } + }) + hass.states.async_set('test.entity', 'test state 1', + {'test_attribute': '1.2'}) + await hass.async_block_till_done() + hass.states.async_set('test.entity', 'test state 2', + {'test_attribute': '0.9'}) + await hass.async_block_till_done() + assert 1 == len(calls) + assert 'numeric_state - test.entity - 10.0 - None - test state 1 - ' \ + 'test state 2' == \ + calls[0].data['some'] + + +async def test_not_fires_on_attr_change_with_attr_not_below_multiple_attr( + hass, calls): + """Test if not fired changed attributes.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'value_template': '{{ state.attributes.test_attribute }}', + 'below': 10, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + # 11 is not below 10 + hass.states.async_set('test.entity', 'entity', + {'test_attribute': 11, 'not_test_attribute': 9}) + await hass.async_block_till_done() + assert 0 == len(calls) + + +async def test_if_action(hass, calls): + """Test if action.""" + entity_id = 'domain.test_entity' + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event', + }, + 'condition': { + 'condition': 'numeric_state', + 'entity_id': entity_id, + 'above': 8, + 'below': 12, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + hass.states.async_set(entity_id, 10) + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + + assert 1 == len(calls) + + hass.states.async_set(entity_id, 8) + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + + assert 1 == len(calls) + + hass.states.async_set(entity_id, 9) + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + + assert 2 == len(calls) + + +async def test_if_fails_setup_bad_for(hass, calls): + """Test for setup failure for bad for.""" + with assert_setup_component(0): + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'above': 8, + 'below': 12, + 'for': { + 'invalid': 5 + }, + }, + 'action': { + 'service': 'homeassistant.turn_on', + } + }}) + + +async def test_if_fails_setup_for_without_above_below(hass, calls): + """Test for setup failures for missing above or below.""" + with assert_setup_component(0): + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'for': { + 'seconds': 5 + }, + }, + 'action': { + 'service': 'homeassistant.turn_on', + } + }}) + + +async def test_if_not_fires_on_entity_change_with_for(hass, calls): + """Test for not firing on entity change with for.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'above': 8, + 'below': 12, + 'for': { + 'seconds': 5 + }, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + hass.states.async_set('test.entity', 9) + await hass.async_block_till_done() + hass.states.async_set('test.entity', 15) + await hass.async_block_till_done() + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=10)) + await hass.async_block_till_done() + assert 0 == len(calls) + + +async def test_if_not_fires_on_entities_change_with_for_after_stop(hass, + calls): + """Test for not firing on entities change with for after stop.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': [ + 'test.entity_1', + 'test.entity_2', + ], + 'above': 8, + 'below': 12, + 'for': { + 'seconds': 5 + }, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + hass.states.async_set('test.entity_1', 9) + hass.states.async_set('test.entity_2', 9) + await hass.async_block_till_done() + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=10)) + await hass.async_block_till_done() + assert 2 == len(calls) + + hass.states.async_set('test.entity_1', 15) + hass.states.async_set('test.entity_2', 15) + await hass.async_block_till_done() + hass.states.async_set('test.entity_1', 9) + hass.states.async_set('test.entity_2', 9) + await hass.async_block_till_done() + await common.async_turn_off(hass) + await hass.async_block_till_done() + + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=10)) + await hass.async_block_till_done() + assert 2 == len(calls) + + +async def test_if_fires_on_entity_change_with_for_attribute_change(hass, + calls): + """Test for firing on entity change with for and attribute change.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'above': 8, + 'below': 12, + 'for': { + 'seconds': 5 + }, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + utcnow = dt_util.utcnow() + with patch('homeassistant.core.dt_util.utcnow') as mock_utcnow: + mock_utcnow.return_value = utcnow + hass.states.async_set('test.entity', 9) + await hass.async_block_till_done() + mock_utcnow.return_value += timedelta(seconds=4) + async_fire_time_changed(hass, mock_utcnow.return_value) + hass.states.async_set('test.entity', 9, + attributes={"mock_attr": "attr_change"}) + await hass.async_block_till_done() + assert 0 == len(calls) + mock_utcnow.return_value += timedelta(seconds=4) + async_fire_time_changed(hass, mock_utcnow.return_value) + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_fires_on_entity_change_with_for(hass, calls): + """Test for firing on entity change with for.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'above': 8, + 'below': 12, + 'for': { + 'seconds': 5 + }, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + hass.states.async_set('test.entity', 9) + await hass.async_block_till_done() + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=10)) + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_wait_template_with_trigger(hass, calls): + """Test using wait template with 'trigger.entity_id'.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'above': 10, + }, + 'action': [ + {'wait_template': + "{{ states(trigger.entity_id) | int < 10 }}"}, + {'service': 'test.automation', + 'data_template': { + 'some': + '{{ trigger.%s }}' % '}} - {{ trigger.'.join(( + 'platform', 'entity_id', 'to_state.state')) + }} + ], + } + }) + + await hass.async_block_till_done() + + hass.states.async_set('test.entity', '12') + await hass.async_block_till_done() + hass.states.async_set('test.entity', '8') + await hass.async_block_till_done() + await hass.async_block_till_done() + assert 1 == len(calls) + assert 'numeric_state - test.entity - 12' == \ + calls[0].data['some'] diff --git a/tests/components/automation/test_state.py b/tests/components/automation/test_state.py index 599816ac7dc..abe02638f26 100644 --- a/tests/components/automation/test_state.py +++ b/tests/components/automation/test_state.py @@ -1,619 +1,634 @@ """The test for state automation.""" from datetime import timedelta -import unittest +import pytest from unittest.mock import patch -from homeassistant.core import Context, callback -from homeassistant.setup import setup_component +from homeassistant.core import Context +from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util import homeassistant.components.automation as automation from tests.common import ( - fire_time_changed, get_test_home_assistant, assert_setup_component, - mock_component) + async_fire_time_changed, assert_setup_component, mock_component) from tests.components.automation import common +from tests.common import async_mock_service -# pylint: disable=invalid-name -class TestAutomationState(unittest.TestCase): - """Test the event automation.""" +@pytest.fixture +def calls(hass): + """Track calls to a mock serivce.""" + return async_mock_service(hass, 'test', 'automation') - def setUp(self): - """Set up things to be run when tests are started.""" - self.hass = get_test_home_assistant() - mock_component(self.hass, 'group') - self.hass.states.set('test.entity', 'hello') - self.calls = [] - @callback - def record_call(service): - """Call recorder.""" - self.calls.append(service) +@pytest.fixture(autouse=True) +def setup_comp(hass): + """Initialize components.""" + mock_component(hass, 'group') + hass.states.async_set('test.entity', 'hello') - self.hass.services.register('test', 'automation', record_call) - def tearDown(self): - """Stop everything that was started.""" - self.hass.stop() +async def test_if_fires_on_entity_change(hass, calls): + """Test for firing on entity change.""" + context = Context() + hass.states.async_set('test.entity', 'hello') + await hass.async_block_till_done() - def test_if_fires_on_entity_change(self): - """Test for firing on entity change.""" - context = Context() - self.hass.states.set('test.entity', 'hello') - self.hass.block_till_done() + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'state', + 'entity_id': 'test.entity', + }, + 'action': { + 'service': 'test.automation', + 'data_template': { + 'some': '{{ trigger.%s }}' % '}} - {{ trigger.'.join(( + 'platform', 'entity_id', + 'from_state.state', 'to_state.state', + 'for')) + }, + } + } + }) - assert setup_component(self.hass, automation.DOMAIN, { + hass.states.async_set('test.entity', 'world', context=context) + await hass.async_block_till_done() + assert 1 == len(calls) + assert calls[0].context is context + assert 'state - test.entity - hello - world - None' == \ + calls[0].data['some'] + + await common.async_turn_off(hass) + await hass.async_block_till_done() + hass.states.async_set('test.entity', 'planet') + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_fires_on_entity_change_with_from_filter(hass, calls): + """Test for firing on entity change with filter.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'state', + 'entity_id': 'test.entity', + 'from': 'hello' + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + hass.states.async_set('test.entity', 'world') + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_fires_on_entity_change_with_to_filter(hass, calls): + """Test for firing on entity change with no filter.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'state', + 'entity_id': 'test.entity', + 'to': 'world' + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + hass.states.async_set('test.entity', 'world') + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_fires_on_attribute_change_with_to_filter(hass, calls): + """Test for not firing on attribute change.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'state', + 'entity_id': 'test.entity', + 'to': 'world' + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + hass.states.async_set('test.entity', 'world', {'test_attribute': 11}) + hass.states.async_set('test.entity', 'world', {'test_attribute': 12}) + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_fires_on_entity_change_with_both_filters(hass, calls): + """Test for firing if both filters are a non match.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'state', + 'entity_id': 'test.entity', + 'from': 'hello', + 'to': 'world' + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + hass.states.async_set('test.entity', 'world') + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_not_fires_if_to_filter_not_match(hass, calls): + """Test for not firing if to filter is not a match.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'state', + 'entity_id': 'test.entity', + 'from': 'hello', + 'to': 'world' + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + hass.states.async_set('test.entity', 'moon') + await hass.async_block_till_done() + assert 0 == len(calls) + + +async def test_if_not_fires_if_from_filter_not_match(hass, calls): + """Test for not firing if from filter is not a match.""" + hass.states.async_set('test.entity', 'bye') + + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'state', + 'entity_id': 'test.entity', + 'from': 'hello', + 'to': 'world' + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + hass.states.async_set('test.entity', 'world') + await hass.async_block_till_done() + assert 0 == len(calls) + + +async def test_if_not_fires_if_entity_not_match(hass, calls): + """Test for not firing if entity is not matching.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'state', + 'entity_id': 'test.another_entity', + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + hass.states.async_set('test.entity', 'world') + await hass.async_block_till_done() + assert 0 == len(calls) + + +async def test_if_action(hass, calls): + """Test for to action.""" + entity_id = 'domain.test_entity' + test_state = 'new_state' + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event', + }, + 'condition': [{ + 'condition': 'state', + 'entity_id': entity_id, + 'state': test_state + }], + 'action': { + 'service': 'test.automation' + } + } + }) + + hass.states.async_set(entity_id, test_state) + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + + assert 1 == len(calls) + + hass.states.async_set(entity_id, test_state + 'something') + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + + assert 1 == len(calls) + + +async def test_if_fails_setup_if_to_boolean_value(hass, calls): + """Test for setup failure for boolean to.""" + with assert_setup_component(0): + assert await async_setup_component(hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'state', 'entity_id': 'test.entity', + 'to': True, }, 'action': { - 'service': 'test.automation', - 'data_template': { - 'some': '{{ trigger.%s }}' % '}} - {{ trigger.'.join(( - 'platform', 'entity_id', - 'from_state.state', 'to_state.state', - 'for')) + 'service': 'homeassistant.turn_on', + } + }}) + + +async def test_if_fails_setup_if_from_boolean_value(hass, calls): + """Test for setup failure for boolean from.""" + with assert_setup_component(0): + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'state', + 'entity_id': 'test.entity', + 'from': True, + }, + 'action': { + 'service': 'homeassistant.turn_on', + } + }}) + + +async def test_if_fails_setup_bad_for(hass, calls): + """Test for setup failure for bad for.""" + with assert_setup_component(0): + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'state', + 'entity_id': 'test.entity', + 'to': 'world', + 'for': { + 'invalid': 5 }, + }, + 'action': { + 'service': 'homeassistant.turn_on', } - } - }) + }}) - self.hass.states.set('test.entity', 'world', context=context) - self.hass.block_till_done() - assert 1 == len(self.calls) - assert self.calls[0].context is context - assert 'state - test.entity - hello - world - None' == \ - self.calls[0].data['some'] - common.turn_off(self.hass) - self.hass.block_till_done() - self.hass.states.set('test.entity', 'planet') - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_if_fires_on_entity_change_with_from_filter(self): - """Test for firing on entity change with filter.""" - assert setup_component(self.hass, automation.DOMAIN, { +async def test_if_fails_setup_for_without_to(hass, calls): + """Test for setup failures for missing to.""" + with assert_setup_component(0): + assert await async_setup_component(hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'state', 'entity_id': 'test.entity', - 'from': 'hello' + 'for': { + 'seconds': 5 + }, }, 'action': { - 'service': 'test.automation' + 'service': 'homeassistant.turn_on', } - } - }) + }}) - self.hass.states.set('test.entity', 'world') - self.hass.block_till_done() - assert 1 == len(self.calls) - def test_if_fires_on_entity_change_with_to_filter(self): - """Test for firing on entity change with no filter.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'state', - 'entity_id': 'test.entity', - 'to': 'world' +async def test_if_not_fires_on_entity_change_with_for(hass, calls): + """Test for not firing on entity change with for.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'state', + 'entity_id': 'test.entity', + 'to': 'world', + 'for': { + 'seconds': 5 }, - 'action': { - 'service': 'test.automation' - } + }, + 'action': { + 'service': 'test.automation' } - }) + } + }) - self.hass.states.set('test.entity', 'world') - self.hass.block_till_done() - assert 1 == len(self.calls) + hass.states.async_set('test.entity', 'world') + await hass.async_block_till_done() + hass.states.async_set('test.entity', 'not_world') + await hass.async_block_till_done() + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=10)) + await hass.async_block_till_done() + assert 0 == len(calls) - def test_if_fires_on_attribute_change_with_to_filter(self): - """Test for not firing on attribute change.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'state', - 'entity_id': 'test.entity', - 'to': 'world' + +async def test_if_not_fires_on_entities_change_with_for_after_stop(hass, + calls): + """Test for not firing on entity change with for after stop trigger.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'state', + 'entity_id': [ + 'test.entity_1', + 'test.entity_2', + ], + 'to': 'world', + 'for': { + 'seconds': 5 }, - 'action': { - 'service': 'test.automation' - } + }, + 'action': { + 'service': 'test.automation' } - }) + } + }) - self.hass.states.set('test.entity', 'world', {'test_attribute': 11}) - self.hass.states.set('test.entity', 'world', {'test_attribute': 12}) - self.hass.block_till_done() - assert 1 == len(self.calls) + hass.states.async_set('test.entity_1', 'world') + hass.states.async_set('test.entity_2', 'world') + await hass.async_block_till_done() + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=10)) + await hass.async_block_till_done() + assert 2 == len(calls) - def test_if_fires_on_entity_change_with_both_filters(self): - """Test for firing if both filters are a non match.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'state', - 'entity_id': 'test.entity', - 'from': 'hello', - 'to': 'world' + hass.states.async_set('test.entity_1', 'world_no') + hass.states.async_set('test.entity_2', 'world_no') + await hass.async_block_till_done() + hass.states.async_set('test.entity_1', 'world') + hass.states.async_set('test.entity_2', 'world') + await hass.async_block_till_done() + await common.async_turn_off(hass) + await hass.async_block_till_done() + + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=10)) + await hass.async_block_till_done() + assert 2 == len(calls) + + +async def test_if_fires_on_entity_change_with_for_attribute_change(hass, + calls): + """Test for firing on entity change with for and attribute change.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'state', + 'entity_id': 'test.entity', + 'to': 'world', + 'for': { + 'seconds': 5 }, - 'action': { - 'service': 'test.automation' - } + }, + 'action': { + 'service': 'test.automation' } - }) + } + }) - self.hass.states.set('test.entity', 'world') - self.hass.block_till_done() - assert 1 == len(self.calls) + utcnow = dt_util.utcnow() + with patch('homeassistant.core.dt_util.utcnow') as mock_utcnow: + mock_utcnow.return_value = utcnow + hass.states.async_set('test.entity', 'world') + await hass.async_block_till_done() + mock_utcnow.return_value += timedelta(seconds=4) + async_fire_time_changed(hass, mock_utcnow.return_value) + hass.states.async_set('test.entity', 'world', + attributes={"mock_attr": "attr_change"}) + await hass.async_block_till_done() + assert 0 == len(calls) + mock_utcnow.return_value += timedelta(seconds=4) + async_fire_time_changed(hass, mock_utcnow.return_value) + await hass.async_block_till_done() + assert 1 == len(calls) - def test_if_not_fires_if_to_filter_not_match(self): - """Test for not firing if to filter is not a match.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'state', - 'entity_id': 'test.entity', - 'from': 'hello', - 'to': 'world' + +async def test_if_fires_on_entity_change_with_for_multiple_force_update(hass, + calls): + """Test for firing on entity change with for and force update.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'state', + 'entity_id': 'test.force_entity', + 'to': 'world', + 'for': { + 'seconds': 5 }, - 'action': { - 'service': 'test.automation' - } + }, + 'action': { + 'service': 'test.automation' } - }) + } + }) - self.hass.states.set('test.entity', 'moon') - self.hass.block_till_done() - assert 0 == len(self.calls) + utcnow = dt_util.utcnow() + with patch('homeassistant.core.dt_util.utcnow') as mock_utcnow: + mock_utcnow.return_value = utcnow + hass.states.async_set('test.force_entity', 'world', None, True) + await hass.async_block_till_done() + for _ in range(0, 4): + mock_utcnow.return_value += timedelta(seconds=1) + async_fire_time_changed(hass, mock_utcnow.return_value) + hass.states.async_set('test.force_entity', 'world', None, True) + await hass.async_block_till_done() + assert 0 == len(calls) + mock_utcnow.return_value += timedelta(seconds=4) + async_fire_time_changed(hass, mock_utcnow.return_value) + await hass.async_block_till_done() + assert 1 == len(calls) - def test_if_not_fires_if_from_filter_not_match(self): - """Test for not firing if from filter is not a match.""" - self.hass.states.set('test.entity', 'bye') - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'state', - 'entity_id': 'test.entity', - 'from': 'hello', - 'to': 'world' +async def test_if_fires_on_entity_change_with_for(hass, calls): + """Test for firing on entity change with for.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'state', + 'entity_id': 'test.entity', + 'to': 'world', + 'for': { + 'seconds': 5 }, - 'action': { - 'service': 'test.automation' - } + }, + 'action': { + 'service': 'test.automation' } - }) + } + }) - self.hass.states.set('test.entity', 'world') - self.hass.block_till_done() - assert 0 == len(self.calls) + hass.states.async_set('test.entity', 'world') + await hass.async_block_till_done() + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=10)) + await hass.async_block_till_done() + assert 1 == len(calls) - def test_if_not_fires_if_entity_not_match(self): - """Test for not firing if entity is not matching.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'state', - 'entity_id': 'test.another_entity', - }, - 'action': { - 'service': 'test.automation' - } - } - }) - self.hass.states.set('test.entity', 'world') - self.hass.block_till_done() - assert 0 == len(self.calls) - - def test_if_action(self): - """Test for to action.""" - entity_id = 'domain.test_entity' - test_state = 'new_state' - assert setup_component(self.hass, automation.DOMAIN, { +async def test_if_fires_on_for_condition(hass, calls): + """Test for firing if condition is on.""" + point1 = dt_util.utcnow() + point2 = point1 + timedelta(seconds=10) + with patch('homeassistant.core.dt_util.utcnow') as mock_utcnow: + mock_utcnow.return_value = point1 + hass.states.async_set('test.entity', 'on') + assert await async_setup_component(hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'event', 'event_type': 'test_event', }, - 'condition': [{ + 'condition': { 'condition': 'state', - 'entity_id': entity_id, - 'state': test_state - }], - 'action': { - 'service': 'test.automation' - } + 'entity_id': 'test.entity', + 'state': 'on', + 'for': { + 'seconds': 5 + }, + }, + 'action': {'service': 'test.automation'}, } }) - self.hass.states.set(entity_id, test_state) - self.hass.bus.fire('test_event') - self.hass.block_till_done() + # not enough time has passed + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + assert 0 == len(calls) - assert 1 == len(self.calls) + # Time travel 10 secs into the future + mock_utcnow.return_value = point2 + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + assert 1 == len(calls) - self.hass.states.set(entity_id, test_state + 'something') - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_if_fails_setup_if_to_boolean_value(self): - """Test for setup failure for boolean to.""" - with assert_setup_component(0): - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'state', - 'entity_id': 'test.entity', - 'to': True, - }, - 'action': { - 'service': 'homeassistant.turn_on', - } - }}) - - def test_if_fails_setup_if_from_boolean_value(self): - """Test for setup failure for boolean from.""" - with assert_setup_component(0): - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'state', - 'entity_id': 'test.entity', - 'from': True, - }, - 'action': { - 'service': 'homeassistant.turn_on', - } - }}) - - def test_if_fails_setup_bad_for(self): - """Test for setup failure for bad for.""" - with assert_setup_component(0): - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'state', - 'entity_id': 'test.entity', - 'to': 'world', - 'for': { - 'invalid': 5 - }, - }, - 'action': { - 'service': 'homeassistant.turn_on', - } - }}) - - def test_if_fails_setup_for_without_to(self): - """Test for setup failures for missing to.""" - with assert_setup_component(0): - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'state', - 'entity_id': 'test.entity', - 'for': { - 'seconds': 5 - }, - }, - 'action': { - 'service': 'homeassistant.turn_on', - } - }}) - - def test_if_not_fires_on_entity_change_with_for(self): - """Test for not firing on entity change with for.""" - assert setup_component(self.hass, automation.DOMAIN, { +async def test_if_fires_on_for_condition_attribute_change(hass, calls): + """Test for firing if condition is on with attribute change.""" + point1 = dt_util.utcnow() + point2 = point1 + timedelta(seconds=4) + point3 = point1 + timedelta(seconds=8) + with patch('homeassistant.core.dt_util.utcnow') as mock_utcnow: + mock_utcnow.return_value = point1 + hass.states.async_set('test.entity', 'on') + assert await async_setup_component(hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { + 'platform': 'event', + 'event_type': 'test_event', + }, + 'condition': { + 'condition': 'state', + 'entity_id': 'test.entity', + 'state': 'on', + 'for': { + 'seconds': 5 + }, + }, + 'action': {'service': 'test.automation'}, + } + }) + + # not enough time has passed + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + assert 0 == len(calls) + + # Still not enough time has passed, but an attribute is changed + mock_utcnow.return_value = point2 + hass.states.async_set('test.entity', 'on', + attributes={"mock_attr": "attr_change"}) + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + assert 0 == len(calls) + + # Enough time has now passed + mock_utcnow.return_value = point3 + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_fails_setup_for_without_time(hass, calls): + """Test for setup failure if no time is provided.""" + with assert_setup_component(0): + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'event', + 'event_type': 'bla' + }, + 'condition': { 'platform': 'state', 'entity_id': 'test.entity', - 'to': 'world', + 'state': 'on', + 'for': {}, + }, + 'action': {'service': 'test.automation'}, + }}) + + +async def test_if_fails_setup_for_without_entity(hass, calls): + """Test for setup failure if no entity is provided.""" + with assert_setup_component(0): + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': {'event_type': 'bla'}, + 'condition': { + 'platform': 'state', + 'state': 'on', 'for': { 'seconds': 5 }, }, - 'action': { - 'service': 'test.automation' - } - } - }) + 'action': {'service': 'test.automation'}, + }}) - self.hass.states.set('test.entity', 'world') - self.hass.block_till_done() - self.hass.states.set('test.entity', 'not_world') - self.hass.block_till_done() - fire_time_changed(self.hass, dt_util.utcnow() + timedelta(seconds=10)) - self.hass.block_till_done() - assert 0 == len(self.calls) - def test_if_not_fires_on_entities_change_with_for_after_stop(self): - """Test for not firing on entity change with for after stop trigger.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'state', - 'entity_id': [ - 'test.entity_1', - 'test.entity_2', - ], - 'to': 'world', - 'for': { - 'seconds': 5 - }, - }, - 'action': { - 'service': 'test.automation' - } - } - }) +async def test_wait_template_with_trigger(hass, calls): + """Test using wait template with 'trigger.entity_id'.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'state', + 'entity_id': 'test.entity', + 'to': 'world', + }, + 'action': [ + {'wait_template': + "{{ is_state(trigger.entity_id, 'hello') }}"}, + {'service': 'test.automation', + 'data_template': { + 'some': + '{{ trigger.%s }}' % '}} - {{ trigger.'.join(( + 'platform', 'entity_id', 'from_state.state', + 'to_state.state')) + }} + ], + } + }) - self.hass.states.set('test.entity_1', 'world') - self.hass.states.set('test.entity_2', 'world') - self.hass.block_till_done() - fire_time_changed(self.hass, dt_util.utcnow() + timedelta(seconds=10)) - self.hass.block_till_done() - assert 2 == len(self.calls) + await hass.async_block_till_done() - self.hass.states.set('test.entity_1', 'world_no') - self.hass.states.set('test.entity_2', 'world_no') - self.hass.block_till_done() - self.hass.states.set('test.entity_1', 'world') - self.hass.states.set('test.entity_2', 'world') - self.hass.block_till_done() - common.turn_off(self.hass) - self.hass.block_till_done() - - fire_time_changed(self.hass, dt_util.utcnow() + timedelta(seconds=10)) - self.hass.block_till_done() - assert 2 == len(self.calls) - - def test_if_fires_on_entity_change_with_for_attribute_change(self): - """Test for firing on entity change with for and attribute change.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'state', - 'entity_id': 'test.entity', - 'to': 'world', - 'for': { - 'seconds': 5 - }, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - utcnow = dt_util.utcnow() - with patch('homeassistant.core.dt_util.utcnow') as mock_utcnow: - mock_utcnow.return_value = utcnow - self.hass.states.set('test.entity', 'world') - self.hass.block_till_done() - mock_utcnow.return_value += timedelta(seconds=4) - fire_time_changed(self.hass, mock_utcnow.return_value) - self.hass.states.set('test.entity', 'world', - attributes={"mock_attr": "attr_change"}) - self.hass.block_till_done() - assert 0 == len(self.calls) - mock_utcnow.return_value += timedelta(seconds=4) - fire_time_changed(self.hass, mock_utcnow.return_value) - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_if_fires_on_entity_change_with_for_multiple_force_update(self): - """Test for firing on entity change with for and force update.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'state', - 'entity_id': 'test.force_entity', - 'to': 'world', - 'for': { - 'seconds': 5 - }, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - utcnow = dt_util.utcnow() - with patch('homeassistant.core.dt_util.utcnow') as mock_utcnow: - mock_utcnow.return_value = utcnow - self.hass.states.set('test.force_entity', 'world', None, True) - self.hass.block_till_done() - for _ in range(0, 4): - mock_utcnow.return_value += timedelta(seconds=1) - fire_time_changed(self.hass, mock_utcnow.return_value) - self.hass.states.set('test.force_entity', 'world', None, True) - self.hass.block_till_done() - assert 0 == len(self.calls) - mock_utcnow.return_value += timedelta(seconds=4) - fire_time_changed(self.hass, mock_utcnow.return_value) - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_if_fires_on_entity_change_with_for(self): - """Test for firing on entity change with for.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'state', - 'entity_id': 'test.entity', - 'to': 'world', - 'for': { - 'seconds': 5 - }, - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - self.hass.states.set('test.entity', 'world') - self.hass.block_till_done() - fire_time_changed(self.hass, dt_util.utcnow() + timedelta(seconds=10)) - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_if_fires_on_for_condition(self): - """Test for firing if condition is on.""" - point1 = dt_util.utcnow() - point2 = point1 + timedelta(seconds=10) - with patch('homeassistant.core.dt_util.utcnow') as mock_utcnow: - mock_utcnow.return_value = point1 - self.hass.states.set('test.entity', 'on') - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event', - }, - 'condition': { - 'condition': 'state', - 'entity_id': 'test.entity', - 'state': 'on', - 'for': { - 'seconds': 5 - }, - }, - 'action': {'service': 'test.automation'}, - } - }) - - # not enough time has passed - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 0 == len(self.calls) - - # Time travel 10 secs into the future - mock_utcnow.return_value = point2 - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_if_fires_on_for_condition_attribute_change(self): - """Test for firing if condition is on with attribute change.""" - point1 = dt_util.utcnow() - point2 = point1 + timedelta(seconds=4) - point3 = point1 + timedelta(seconds=8) - with patch('homeassistant.core.dt_util.utcnow') as mock_utcnow: - mock_utcnow.return_value = point1 - self.hass.states.set('test.entity', 'on') - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event', - }, - 'condition': { - 'condition': 'state', - 'entity_id': 'test.entity', - 'state': 'on', - 'for': { - 'seconds': 5 - }, - }, - 'action': {'service': 'test.automation'}, - } - }) - - # not enough time has passed - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 0 == len(self.calls) - - # Still not enough time has passed, but an attribute is changed - mock_utcnow.return_value = point2 - self.hass.states.set('test.entity', 'on', - attributes={"mock_attr": "attr_change"}) - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 0 == len(self.calls) - - # Enough time has now passed - mock_utcnow.return_value = point3 - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_if_fails_setup_for_without_time(self): - """Test for setup failure if no time is provided.""" - with assert_setup_component(0): - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'event', - 'event_type': 'bla' - }, - 'condition': { - 'platform': 'state', - 'entity_id': 'test.entity', - 'state': 'on', - 'for': {}, - }, - 'action': {'service': 'test.automation'}, - }}) - - def test_if_fails_setup_for_without_entity(self): - """Test for setup failure if no entity is provided.""" - with assert_setup_component(0): - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': {'event_type': 'bla'}, - 'condition': { - 'platform': 'state', - 'state': 'on', - 'for': { - 'seconds': 5 - }, - }, - 'action': {'service': 'test.automation'}, - }}) - - def test_wait_template_with_trigger(self): - """Test using wait template with 'trigger.entity_id'.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'state', - 'entity_id': 'test.entity', - 'to': 'world', - }, - 'action': [ - {'wait_template': - "{{ is_state(trigger.entity_id, 'hello') }}"}, - {'service': 'test.automation', - 'data_template': { - 'some': - '{{ trigger.%s }}' % '}} - {{ trigger.'.join(( - 'platform', 'entity_id', 'from_state.state', - 'to_state.state')) - }} - ], - } - }) - - self.hass.block_till_done() - self.calls = [] - - self.hass.states.set('test.entity', 'world') - self.hass.block_till_done() - self.hass.states.set('test.entity', 'hello') - self.hass.block_till_done() - assert 1 == len(self.calls) - assert 'state - test.entity - hello - world' == \ - self.calls[0].data['some'] + hass.states.async_set('test.entity', 'world') + await hass.async_block_till_done() + hass.states.async_set('test.entity', 'hello') + await hass.async_block_till_done() + assert 1 == len(calls) + assert 'state - test.entity - hello - world' == \ + calls[0].data['some'] diff --git a/tests/components/automation/test_sun.py b/tests/components/automation/test_sun.py index 3eb30b5594e..dce7933a759 100644 --- a/tests/components/automation/test_sun.py +++ b/tests/components/automation/test_sun.py @@ -1,322 +1,319 @@ """The tests for the sun automation.""" from datetime import datetime -import unittest +import pytest from unittest.mock import patch -from homeassistant.core import callback -from homeassistant.setup import setup_component +from homeassistant.setup import async_setup_component from homeassistant.components import sun import homeassistant.components.automation as automation import homeassistant.util.dt as dt_util from tests.common import ( - fire_time_changed, get_test_home_assistant, mock_component) + async_fire_time_changed, mock_component, async_mock_service) from tests.components.automation import common -# pylint: disable=invalid-name -class TestAutomationSun(unittest.TestCase): - """Test the sun automation.""" +@pytest.fixture +def calls(hass): + """Track calls to a mock serivce.""" + return async_mock_service(hass, 'test', 'automation') - def setUp(self): - """Set up things to be run when tests are started.""" - self.hass = get_test_home_assistant() - mock_component(self.hass, 'group') - setup_component(self.hass, sun.DOMAIN, { - sun.DOMAIN: {sun.CONF_ELEVATION: 0}}) - self.calls = [] +@pytest.fixture(autouse=True) +def setup_comp(hass): + """Initialize components.""" + mock_component(hass, 'group') + hass.loop.run_until_complete(async_setup_component(hass, sun.DOMAIN, { + sun.DOMAIN: {sun.CONF_ELEVATION: 0}})) - @callback - def record_call(service): - """Call recorder.""" - self.calls.append(service) - self.hass.services.register('test', 'automation', record_call) +async def test_sunset_trigger(hass, calls): + """Test the sunset trigger.""" + now = datetime(2015, 9, 15, 23, tzinfo=dt_util.UTC) + trigger_time = datetime(2015, 9, 16, 2, tzinfo=dt_util.UTC) - def tearDown(self): - """Stop everything that was started.""" - self.hass.stop() + with patch('homeassistant.util.dt.utcnow', + return_value=now): + await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'sun', + 'event': 'sunset', + }, + 'action': { + 'service': 'test.automation', + } + } + }) - def test_sunset_trigger(self): - """Test the sunset trigger.""" - now = datetime(2015, 9, 15, 23, tzinfo=dt_util.UTC) - trigger_time = datetime(2015, 9, 16, 2, tzinfo=dt_util.UTC) + await common.async_turn_off(hass) + await hass.async_block_till_done() - with patch('homeassistant.util.dt.utcnow', - return_value=now): - setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'sun', - 'event': 'sunset', + async_fire_time_changed(hass, trigger_time) + await hass.async_block_till_done() + assert 0 == len(calls) + + with patch('homeassistant.util.dt.utcnow', + return_value=now): + await common.async_turn_on(hass) + await hass.async_block_till_done() + + async_fire_time_changed(hass, trigger_time) + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_sunrise_trigger(hass, calls): + """Test the sunrise trigger.""" + now = datetime(2015, 9, 13, 23, tzinfo=dt_util.UTC) + trigger_time = datetime(2015, 9, 16, 14, tzinfo=dt_util.UTC) + + with patch('homeassistant.util.dt.utcnow', + return_value=now): + await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'sun', + 'event': 'sunrise', + }, + 'action': { + 'service': 'test.automation', + } + } + }) + + async_fire_time_changed(hass, trigger_time) + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_sunset_trigger_with_offset(hass, calls): + """Test the sunset trigger with offset.""" + now = datetime(2015, 9, 15, 23, tzinfo=dt_util.UTC) + trigger_time = datetime(2015, 9, 16, 2, 30, tzinfo=dt_util.UTC) + + with patch('homeassistant.util.dt.utcnow', + return_value=now): + await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'sun', + 'event': 'sunset', + 'offset': '0:30:00' + }, + 'action': { + 'service': 'test.automation', + 'data_template': { + 'some': + '{{ trigger.%s }}' % '}} - {{ trigger.'.join(( + 'platform', 'event', 'offset')) }, - 'action': { - 'service': 'test.automation', - } - } - }) - - common.turn_off(self.hass) - self.hass.block_till_done() - - fire_time_changed(self.hass, trigger_time) - self.hass.block_till_done() - assert 0 == len(self.calls) - - with patch('homeassistant.util.dt.utcnow', - return_value=now): - common.turn_on(self.hass) - self.hass.block_till_done() - - fire_time_changed(self.hass, trigger_time) - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_sunrise_trigger(self): - """Test the sunrise trigger.""" - now = datetime(2015, 9, 13, 23, tzinfo=dt_util.UTC) - trigger_time = datetime(2015, 9, 16, 14, tzinfo=dt_util.UTC) - - with patch('homeassistant.util.dt.utcnow', - return_value=now): - setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'sun', - 'event': 'sunrise', - }, - 'action': { - 'service': 'test.automation', - } - } - }) - - fire_time_changed(self.hass, trigger_time) - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_sunset_trigger_with_offset(self): - """Test the sunset trigger with offset.""" - now = datetime(2015, 9, 15, 23, tzinfo=dt_util.UTC) - trigger_time = datetime(2015, 9, 16, 2, 30, tzinfo=dt_util.UTC) - - with patch('homeassistant.util.dt.utcnow', - return_value=now): - setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'sun', - 'event': 'sunset', - 'offset': '0:30:00' - }, - 'action': { - 'service': 'test.automation', - 'data_template': { - 'some': - '{{ trigger.%s }}' % '}} - {{ trigger.'.join(( - 'platform', 'event', 'offset')) - }, - } - } - }) - - fire_time_changed(self.hass, trigger_time) - self.hass.block_till_done() - assert 1 == len(self.calls) - assert 'sun - sunset - 0:30:00' == self.calls[0].data['some'] - - def test_sunrise_trigger_with_offset(self): - """Test the sunrise trigger with offset.""" - now = datetime(2015, 9, 13, 23, tzinfo=dt_util.UTC) - trigger_time = datetime(2015, 9, 16, 13, 30, tzinfo=dt_util.UTC) - - with patch('homeassistant.util.dt.utcnow', - return_value=now): - setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'sun', - 'event': 'sunrise', - 'offset': '-0:30:00' - }, - 'action': { - 'service': 'test.automation', - } - } - }) - - fire_time_changed(self.hass, trigger_time) - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_if_action_before(self): - """Test if action was before.""" - setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event', - }, - 'condition': { - 'condition': 'sun', - 'before': 'sunrise', - }, - 'action': { - 'service': 'test.automation' } } }) - now = datetime(2015, 9, 16, 15, tzinfo=dt_util.UTC) - with patch('homeassistant.util.dt.utcnow', - return_value=now): - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 0 == len(self.calls) + async_fire_time_changed(hass, trigger_time) + await hass.async_block_till_done() + assert 1 == len(calls) + assert 'sun - sunset - 0:30:00' == calls[0].data['some'] - now = datetime(2015, 9, 16, 10, tzinfo=dt_util.UTC) - with patch('homeassistant.util.dt.utcnow', - return_value=now): - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 1 == len(self.calls) - def test_if_action_after(self): - """Test if action was after.""" - setup_component(self.hass, automation.DOMAIN, { +async def test_sunrise_trigger_with_offset(hass, calls): + """Test the sunrise trigger with offset.""" + now = datetime(2015, 9, 13, 23, tzinfo=dt_util.UTC) + trigger_time = datetime(2015, 9, 16, 13, 30, tzinfo=dt_util.UTC) + + with patch('homeassistant.util.dt.utcnow', + return_value=now): + await async_setup_component(hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { - 'platform': 'event', - 'event_type': 'test_event', - }, - 'condition': { - 'condition': 'sun', - 'after': 'sunrise', + 'platform': 'sun', + 'event': 'sunrise', + 'offset': '-0:30:00' }, 'action': { - 'service': 'test.automation' + 'service': 'test.automation', } } }) - now = datetime(2015, 9, 16, 13, tzinfo=dt_util.UTC) - with patch('homeassistant.util.dt.utcnow', - return_value=now): - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 0 == len(self.calls) + async_fire_time_changed(hass, trigger_time) + await hass.async_block_till_done() + assert 1 == len(calls) - now = datetime(2015, 9, 16, 15, tzinfo=dt_util.UTC) - with patch('homeassistant.util.dt.utcnow', - return_value=now): - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 1 == len(self.calls) - def test_if_action_before_with_offset(self): - """Test if action was before offset.""" - setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event', - }, - 'condition': { - 'condition': 'sun', - 'before': 'sunrise', - 'before_offset': '+1:00:00' - }, - 'action': { - 'service': 'test.automation' - } +async def test_if_action_before(hass, calls): + """Test if action was before.""" + await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event', + }, + 'condition': { + 'condition': 'sun', + 'before': 'sunrise', + }, + 'action': { + 'service': 'test.automation' } - }) + } + }) - now = datetime(2015, 9, 16, 14, 32, 44, tzinfo=dt_util.UTC) - with patch('homeassistant.util.dt.utcnow', - return_value=now): - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 0 == len(self.calls) + now = datetime(2015, 9, 16, 15, tzinfo=dt_util.UTC) + with patch('homeassistant.util.dt.utcnow', + return_value=now): + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + assert 0 == len(calls) - now = datetime(2015, 9, 16, 14, 32, 43, tzinfo=dt_util.UTC) - with patch('homeassistant.util.dt.utcnow', - return_value=now): - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 1 == len(self.calls) + now = datetime(2015, 9, 16, 10, tzinfo=dt_util.UTC) + with patch('homeassistant.util.dt.utcnow', + return_value=now): + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + assert 1 == len(calls) - def test_if_action_after_with_offset(self): - """Test if action was after offset.""" - setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event', - }, - 'condition': { - 'condition': 'sun', - 'after': 'sunrise', - 'after_offset': '+1:00:00' - }, - 'action': { - 'service': 'test.automation' - } + +async def test_if_action_after(hass, calls): + """Test if action was after.""" + await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event', + }, + 'condition': { + 'condition': 'sun', + 'after': 'sunrise', + }, + 'action': { + 'service': 'test.automation' } - }) + } + }) - now = datetime(2015, 9, 16, 14, 32, 42, tzinfo=dt_util.UTC) - with patch('homeassistant.util.dt.utcnow', - return_value=now): - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 0 == len(self.calls) + now = datetime(2015, 9, 16, 13, tzinfo=dt_util.UTC) + with patch('homeassistant.util.dt.utcnow', + return_value=now): + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + assert 0 == len(calls) - now = datetime(2015, 9, 16, 14, 32, 43, tzinfo=dt_util.UTC) - with patch('homeassistant.util.dt.utcnow', - return_value=now): - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 1 == len(self.calls) + now = datetime(2015, 9, 16, 15, tzinfo=dt_util.UTC) + with patch('homeassistant.util.dt.utcnow', + return_value=now): + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + assert 1 == len(calls) - def test_if_action_before_and_after_during(self): - """Test if action was before and after during.""" - setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event', - }, - 'condition': { - 'condition': 'sun', - 'after': 'sunrise', - 'before': 'sunset' - }, - 'action': { - 'service': 'test.automation' - } + +async def test_if_action_before_with_offset(hass, calls): + """Test if action was before offset.""" + await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event', + }, + 'condition': { + 'condition': 'sun', + 'before': 'sunrise', + 'before_offset': '+1:00:00' + }, + 'action': { + 'service': 'test.automation' } - }) + } + }) - now = datetime(2015, 9, 16, 13, 8, 51, tzinfo=dt_util.UTC) - with patch('homeassistant.util.dt.utcnow', - return_value=now): - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 0 == len(self.calls) + now = datetime(2015, 9, 16, 14, 32, 44, tzinfo=dt_util.UTC) + with patch('homeassistant.util.dt.utcnow', + return_value=now): + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + assert 0 == len(calls) - now = datetime(2015, 9, 17, 2, 25, 18, tzinfo=dt_util.UTC) - with patch('homeassistant.util.dt.utcnow', - return_value=now): - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 0 == len(self.calls) + now = datetime(2015, 9, 16, 14, 32, 43, tzinfo=dt_util.UTC) + with patch('homeassistant.util.dt.utcnow', + return_value=now): + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + assert 1 == len(calls) - now = datetime(2015, 9, 16, 16, tzinfo=dt_util.UTC) - with patch('homeassistant.util.dt.utcnow', - return_value=now): - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 1 == len(self.calls) + +async def test_if_action_after_with_offset(hass, calls): + """Test if action was after offset.""" + await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event', + }, + 'condition': { + 'condition': 'sun', + 'after': 'sunrise', + 'after_offset': '+1:00:00' + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + now = datetime(2015, 9, 16, 14, 32, 42, tzinfo=dt_util.UTC) + with patch('homeassistant.util.dt.utcnow', + return_value=now): + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + assert 0 == len(calls) + + now = datetime(2015, 9, 16, 14, 32, 43, tzinfo=dt_util.UTC) + with patch('homeassistant.util.dt.utcnow', + return_value=now): + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_action_before_and_after_during(hass, calls): + """Test if action was before and after during.""" + await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event', + }, + 'condition': { + 'condition': 'sun', + 'after': 'sunrise', + 'before': 'sunset' + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + now = datetime(2015, 9, 16, 13, 8, 51, tzinfo=dt_util.UTC) + with patch('homeassistant.util.dt.utcnow', + return_value=now): + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + assert 0 == len(calls) + + now = datetime(2015, 9, 17, 2, 25, 18, tzinfo=dt_util.UTC) + with patch('homeassistant.util.dt.utcnow', + return_value=now): + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + assert 0 == len(calls) + + now = datetime(2015, 9, 16, 16, tzinfo=dt_util.UTC) + with patch('homeassistant.util.dt.utcnow', + return_value=now): + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + assert 1 == len(calls) diff --git a/tests/components/automation/test_template.py b/tests/components/automation/test_template.py index 9945677c123..d9ad765db3f 100644 --- a/tests/components/automation/test_template.py +++ b/tests/components/automation/test_template.py @@ -1,44 +1,380 @@ """The tests for the Template automation.""" -import unittest +import pytest -from homeassistant.core import Context, callback -from homeassistant.setup import setup_component +from homeassistant.core import Context +from homeassistant.setup import async_setup_component import homeassistant.components.automation as automation -from tests.common import ( - get_test_home_assistant, assert_setup_component, mock_component) +from tests.common import (assert_setup_component, mock_component) from tests.components.automation import common +from tests.common import async_mock_service -# pylint: disable=invalid-name -class TestAutomationTemplate(unittest.TestCase): - """Test the event automation.""" +@pytest.fixture +def calls(hass): + """Track calls to a mock serivce.""" + return async_mock_service(hass, 'test', 'automation') - def setUp(self): - """Set up things to be run when tests are started.""" - self.hass = get_test_home_assistant() - mock_component(self.hass, 'group') - self.hass.states.set('test.entity', 'hello') - self.calls = [] - @callback - def record_call(service): - """Record calls.""" - self.calls.append(service) +@pytest.fixture(autouse=True) +def setup_comp(hass): + """Initialize components.""" + mock_component(hass, 'group') + hass.states.async_set('test.entity', 'hello') - self.hass.services.register('test', 'automation', record_call) - def tearDown(self): - """Stop everything that was started.""" - self.hass.stop() +async def test_if_fires_on_change_bool(hass, calls): + """Test for firing on boolean change.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'template', + 'value_template': '{{ true }}', + }, + 'action': { + 'service': 'test.automation' + } + } + }) - def test_if_fires_on_change_bool(self): - """Test for firing on boolean change.""" - assert setup_component(self.hass, automation.DOMAIN, { + hass.states.async_set('test.entity', 'world') + await hass.async_block_till_done() + assert 1 == len(calls) + + await common.async_turn_off(hass) + await hass.async_block_till_done() + + hass.states.async_set('test.entity', 'planet') + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_fires_on_change_str(hass, calls): + """Test for firing on change.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'template', + 'value_template': 'true', + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + hass.states.async_set('test.entity', 'world') + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_fires_on_change_str_crazy(hass, calls): + """Test for firing on change.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'template', + 'value_template': 'TrUE', + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + hass.states.async_set('test.entity', 'world') + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_not_fires_on_change_bool(hass, calls): + """Test for not firing on boolean change.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'template', + 'value_template': '{{ false }}', + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + hass.states.async_set('test.entity', 'world') + await hass.async_block_till_done() + assert 0 == len(calls) + + +async def test_if_not_fires_on_change_str(hass, calls): + """Test for not firing on string change.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'template', + 'value_template': 'False', + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + hass.states.async_set('test.entity', 'world') + await hass.async_block_till_done() + assert 0 == len(calls) + + +async def test_if_not_fires_on_change_str_crazy(hass, calls): + """Test for not firing on string change.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'template', + 'value_template': 'Anything other than "true" is false.', + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + hass.states.async_set('test.entity', 'world') + await hass.async_block_till_done() + assert 0 == len(calls) + + +async def test_if_fires_on_no_change(hass, calls): + """Test for firing on no change.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'template', + 'value_template': '{{ true }}', + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + await hass.async_block_till_done() + cur_len = len(calls) + + hass.states.async_set('test.entity', 'hello') + await hass.async_block_till_done() + assert cur_len == len(calls) + + +async def test_if_fires_on_two_change(hass, calls): + """Test for firing on two changes.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'template', + 'value_template': '{{ true }}', + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + # Trigger once + hass.states.async_set('test.entity', 'world') + await hass.async_block_till_done() + assert 1 == len(calls) + + # Trigger again + hass.states.async_set('test.entity', 'world') + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_fires_on_change_with_template(hass, calls): + """Test for firing on change with template.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'template', + 'value_template': '{{ is_state("test.entity", "world") }}', + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + hass.states.async_set('test.entity', 'world') + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_not_fires_on_change_with_template(hass, calls): + """Test for not firing on change with template.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'template', + 'value_template': '{{ is_state("test.entity", "hello") }}', + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + await hass.async_block_till_done() + + hass.states.async_set('test.entity', 'world') + await hass.async_block_till_done() + assert len(calls) == 0 + + +async def test_if_fires_on_change_with_template_advanced(hass, calls): + """Test for firing on change with template advanced.""" + context = Context() + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'template', + 'value_template': '{{ is_state("test.entity", "world") }}' + }, + 'action': { + 'service': 'test.automation', + 'data_template': { + 'some': + '{{ trigger.%s }}' % '}} - {{ trigger.'.join(( + 'platform', 'entity_id', 'from_state.state', + 'to_state.state')) + }, + } + } + }) + + await hass.async_block_till_done() + + hass.states.async_set('test.entity', 'world', context=context) + await hass.async_block_till_done() + assert 1 == len(calls) + assert calls[0].context is context + assert 'template - test.entity - hello - world' == \ + calls[0].data['some'] + + +async def test_if_fires_on_no_change_with_template_advanced(hass, calls): + """Test for firing on no change with template advanced.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'template', + 'value_template': '''{%- if is_state("test.entity", "world") -%} + true + {%- else -%} + false + {%- endif -%}''', + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + # Different state + hass.states.async_set('test.entity', 'worldz') + await hass.async_block_till_done() + assert 0 == len(calls) + + # Different state + hass.states.async_set('test.entity', 'hello') + await hass.async_block_till_done() + assert 0 == len(calls) + + +async def test_if_fires_on_change_with_template_2(hass, calls): + """Test for firing on change with template.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'template', + 'value_template': + '{{ not is_state("test.entity", "world") }}', + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + await hass.async_block_till_done() + + hass.states.async_set('test.entity', 'world') + await hass.async_block_till_done() + assert len(calls) == 0 + + hass.states.async_set('test.entity', 'home') + await hass.async_block_till_done() + assert len(calls) == 1 + + hass.states.async_set('test.entity', 'work') + await hass.async_block_till_done() + assert len(calls) == 1 + + hass.states.async_set('test.entity', 'not_home') + await hass.async_block_till_done() + assert len(calls) == 1 + + hass.states.async_set('test.entity', 'world') + await hass.async_block_till_done() + assert len(calls) == 1 + + hass.states.async_set('test.entity', 'home') + await hass.async_block_till_done() + assert len(calls) == 2 + + +async def test_if_action(hass, calls): + """Test for firing if action.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event', + }, + 'condition': [{ + 'condition': 'template', + 'value_template': '{{ is_state("test.entity", "world") }}' + }], + 'action': { + 'service': 'test.automation' + } + } + }) + + # Condition is not true yet + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + assert 0 == len(calls) + + # Change condition to true, but it shouldn't be triggered yet + hass.states.async_set('test.entity', 'world') + await hass.async_block_till_done() + assert 0 == len(calls) + + # Condition is true and event is triggered + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_fires_on_change_with_bad_template(hass, calls): + """Test for firing on change with bad template.""" + with assert_setup_component(0): + assert await async_setup_component(hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'template', - 'value_template': '{{ true }}', + 'value_template': '{{ ', }, 'action': { 'service': 'test.automation' @@ -46,388 +382,55 @@ class TestAutomationTemplate(unittest.TestCase): } }) - self.hass.states.set('test.entity', 'world') - self.hass.block_till_done() - assert 1 == len(self.calls) - common.turn_off(self.hass) - self.hass.block_till_done() - - self.hass.states.set('test.entity', 'planet') - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_if_fires_on_change_str(self): - """Test for firing on change.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'template', - 'value_template': 'true', - }, - 'action': { - 'service': 'test.automation' - } +async def test_if_fires_on_change_with_bad_template_2(hass, calls): + """Test for firing on change with bad template.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'template', + 'value_template': '{{ xyz | round(0) }}', + }, + 'action': { + 'service': 'test.automation' } - }) + } + }) - self.hass.states.set('test.entity', 'world') - self.hass.block_till_done() - assert 1 == len(self.calls) + hass.states.async_set('test.entity', 'world') + await hass.async_block_till_done() + assert 0 == len(calls) - def test_if_fires_on_change_str_crazy(self): - """Test for firing on change.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'template', - 'value_template': 'TrUE', - }, - 'action': { - 'service': 'test.automation' - } - } - }) - self.hass.states.set('test.entity', 'world') - self.hass.block_till_done() - assert 1 == len(self.calls) +async def test_wait_template_with_trigger(hass, calls): + """Test using wait template with 'trigger.entity_id'.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'template', + 'value_template': + "{{ states.test.entity.state == 'world' }}", + }, + 'action': [ + {'wait_template': + "{{ is_state(trigger.entity_id, 'hello') }}"}, + {'service': 'test.automation', + 'data_template': { + 'some': + '{{ trigger.%s }}' % '}} - {{ trigger.'.join(( + 'platform', 'entity_id', 'from_state.state', + 'to_state.state')) + }} + ], + } + }) - def test_if_not_fires_on_change_bool(self): - """Test for not firing on boolean change.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'template', - 'value_template': '{{ false }}', - }, - 'action': { - 'service': 'test.automation' - } - } - }) + await hass.async_block_till_done() - self.hass.states.set('test.entity', 'world') - self.hass.block_till_done() - assert 0 == len(self.calls) - - def test_if_not_fires_on_change_str(self): - """Test for not firing on string change.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'template', - 'value_template': 'False', - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - self.hass.states.set('test.entity', 'world') - self.hass.block_till_done() - assert 0 == len(self.calls) - - def test_if_not_fires_on_change_str_crazy(self): - """Test for not firing on string change.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'template', - 'value_template': 'Anything other than "true" is false.', - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - self.hass.states.set('test.entity', 'world') - self.hass.block_till_done() - assert 0 == len(self.calls) - - def test_if_fires_on_no_change(self): - """Test for firing on no change.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'template', - 'value_template': '{{ true }}', - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - self.hass.block_till_done() - self.calls = [] - - self.hass.states.set('test.entity', 'hello') - self.hass.block_till_done() - assert 0 == len(self.calls) - - def test_if_fires_on_two_change(self): - """Test for firing on two changes.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'template', - 'value_template': '{{ true }}', - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - # Trigger once - self.hass.states.set('test.entity', 'world') - self.hass.block_till_done() - assert 1 == len(self.calls) - - # Trigger again - self.hass.states.set('test.entity', 'world') - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_if_fires_on_change_with_template(self): - """Test for firing on change with template.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'template', - 'value_template': '{{ is_state("test.entity", "world") }}', - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - self.hass.states.set('test.entity', 'world') - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_if_not_fires_on_change_with_template(self): - """Test for not firing on change with template.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'template', - 'value_template': '{{ is_state("test.entity", "hello") }}', - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - self.hass.block_till_done() - self.calls = [] - - self.hass.states.set('test.entity', 'world') - self.hass.block_till_done() - assert len(self.calls) == 0 - - def test_if_fires_on_change_with_template_advanced(self): - """Test for firing on change with template advanced.""" - context = Context() - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'template', - 'value_template': '{{ is_state("test.entity", "world") }}' - }, - 'action': { - 'service': 'test.automation', - 'data_template': { - 'some': - '{{ trigger.%s }}' % '}} - {{ trigger.'.join(( - 'platform', 'entity_id', 'from_state.state', - 'to_state.state')) - }, - } - } - }) - - self.hass.block_till_done() - self.calls = [] - - self.hass.states.set('test.entity', 'world', context=context) - self.hass.block_till_done() - assert 1 == len(self.calls) - assert self.calls[0].context is context - assert 'template - test.entity - hello - world' == \ - self.calls[0].data['some'] - - def test_if_fires_on_no_change_with_template_advanced(self): - """Test for firing on no change with template advanced.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'template', - 'value_template': '''{%- if is_state("test.entity", "world") -%} - true - {%- else -%} - false - {%- endif -%}''', - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - # Different state - self.hass.states.set('test.entity', 'worldz') - self.hass.block_till_done() - assert 0 == len(self.calls) - - # Different state - self.hass.states.set('test.entity', 'hello') - self.hass.block_till_done() - assert 0 == len(self.calls) - - def test_if_fires_on_change_with_template_2(self): - """Test for firing on change with template.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'template', - 'value_template': - '{{ not is_state("test.entity", "world") }}', - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - self.hass.block_till_done() - self.calls = [] - - self.hass.states.set('test.entity', 'world') - self.hass.block_till_done() - assert len(self.calls) == 0 - - self.hass.states.set('test.entity', 'home') - self.hass.block_till_done() - assert len(self.calls) == 1 - - self.hass.states.set('test.entity', 'work') - self.hass.block_till_done() - assert len(self.calls) == 1 - - self.hass.states.set('test.entity', 'not_home') - self.hass.block_till_done() - assert len(self.calls) == 1 - - self.hass.states.set('test.entity', 'world') - self.hass.block_till_done() - assert len(self.calls) == 1 - - self.hass.states.set('test.entity', 'home') - self.hass.block_till_done() - assert len(self.calls) == 2 - - def test_if_action(self): - """Test for firing if action.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event', - }, - 'condition': [{ - 'condition': 'template', - 'value_template': '{{ is_state("test.entity", "world") }}' - }], - 'action': { - 'service': 'test.automation' - } - } - }) - - # Condition is not true yet - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 0 == len(self.calls) - - # Change condition to true, but it shouldn't be triggered yet - self.hass.states.set('test.entity', 'world') - self.hass.block_till_done() - assert 0 == len(self.calls) - - # Condition is true and event is triggered - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 1 == len(self.calls) - - def test_if_fires_on_change_with_bad_template(self): - """Test for firing on change with bad template.""" - with assert_setup_component(0): - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'template', - 'value_template': '{{ ', - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - def test_if_fires_on_change_with_bad_template_2(self): - """Test for firing on change with bad template.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'template', - 'value_template': '{{ xyz | round(0) }}', - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - self.hass.states.set('test.entity', 'world') - self.hass.block_till_done() - assert 0 == len(self.calls) - - def test_wait_template_with_trigger(self): - """Test using wait template with 'trigger.entity_id'.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'template', - 'value_template': - "{{ states.test.entity.state == 'world' }}", - }, - 'action': [ - {'wait_template': - "{{ is_state(trigger.entity_id, 'hello') }}"}, - {'service': 'test.automation', - 'data_template': { - 'some': - '{{ trigger.%s }}' % '}} - {{ trigger.'.join(( - 'platform', 'entity_id', 'from_state.state', - 'to_state.state')) - }} - ], - } - }) - - self.hass.block_till_done() - self.calls = [] - - self.hass.states.set('test.entity', 'world') - self.hass.block_till_done() - self.hass.states.set('test.entity', 'hello') - self.hass.block_till_done() - assert 1 == len(self.calls) - assert 'template - test.entity - hello - world' == \ - self.calls[0].data['some'] + hass.states.async_set('test.entity', 'world') + await hass.async_block_till_done() + hass.states.async_set('test.entity', 'hello') + await hass.async_block_till_done() + assert 1 == len(calls) + assert 'template - test.entity - hello - world' == \ + calls[0].data['some'] diff --git a/tests/components/automation/test_time.py b/tests/components/automation/test_time.py index d1d9bcaecdf..11387f25889 100644 --- a/tests/components/automation/test_time.py +++ b/tests/components/automation/test_time.py @@ -1,47 +1,216 @@ """The tests for the time automation.""" from datetime import timedelta -import unittest from unittest.mock import patch -from homeassistant.core import callback -from homeassistant.setup import setup_component +import pytest + +from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util import homeassistant.components.automation as automation from tests.common import ( - fire_time_changed, get_test_home_assistant, assert_setup_component, - mock_component) + async_fire_time_changed, assert_setup_component, mock_component) from tests.components.automation import common +from tests.common import async_mock_service -# pylint: disable=invalid-name -class TestAutomationTime(unittest.TestCase): - """Test the event automation.""" +@pytest.fixture +def calls(hass): + """Track calls to a mock serivce.""" + return async_mock_service(hass, 'test', 'automation') - def setUp(self): - """Set up things to be run when tests are started.""" - self.hass = get_test_home_assistant() - mock_component(self.hass, 'group') - self.calls = [] - @callback - def record_call(service): - """Record calls.""" - self.calls.append(service) +@pytest.fixture(autouse=True) +def setup_comp(hass): + """Initialize components.""" + mock_component(hass, 'group') - self.hass.services.register('test', 'automation', record_call) - def tearDown(self): - """Stop everything that was started.""" - self.hass.stop() +async def test_if_fires_when_hour_matches(hass, calls): + """Test for firing if hour is matching.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'time', + 'hours': 0, + }, + 'action': { + 'service': 'test.automation' + } + } + }) - def test_if_fires_when_hour_matches(self): - """Test for firing if hour is matching.""" - assert setup_component(self.hass, automation.DOMAIN, { + async_fire_time_changed(hass, dt_util.utcnow().replace(hour=0)) + await hass.async_block_till_done() + assert 1 == len(calls) + + await common.async_turn_off(hass) + await hass.async_block_till_done() + + async_fire_time_changed(hass, dt_util.utcnow().replace(hour=0)) + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_fires_when_minute_matches(hass, calls): + """Test for firing if minutes are matching.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'time', + 'minutes': 0, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + async_fire_time_changed(hass, dt_util.utcnow().replace(minute=0)) + + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_fires_when_second_matches(hass, calls): + """Test for firing if seconds are matching.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'time', + 'seconds': 0, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + async_fire_time_changed(hass, dt_util.utcnow().replace(second=0)) + + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_fires_when_all_matches(hass, calls): + """Test for firing if everything matches.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'time', + 'hours': 1, + 'minutes': 2, + 'seconds': 3, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + async_fire_time_changed(hass, dt_util.utcnow().replace( + hour=1, minute=2, second=3)) + + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_fires_periodic_seconds(hass, calls): + """Test for firing periodically every second.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'time', + 'seconds': "/2", + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + async_fire_time_changed(hass, dt_util.utcnow().replace( + hour=0, minute=0, second=2)) + + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_fires_periodic_minutes(hass, calls): + """Test for firing periodically every minute.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'time', + 'minutes': "/2", + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + async_fire_time_changed(hass, dt_util.utcnow().replace( + hour=0, minute=2, second=0)) + + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_fires_periodic_hours(hass, calls): + """Test for firing periodically every hour.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'time', + 'hours': "/2", + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + async_fire_time_changed(hass, dt_util.utcnow().replace( + hour=2, minute=0, second=0)) + + await hass.async_block_till_done() + assert 1 == len(calls) + + +async def test_if_fires_using_at(hass, calls): + """Test for firing at.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'time', + 'at': '5:00:00', + }, + 'action': { + 'service': 'test.automation', + 'data_template': { + 'some': '{{ trigger.platform }} - ' + '{{ trigger.now.hour }}' + }, + } + } + }) + + async_fire_time_changed(hass, dt_util.utcnow().replace( + hour=5, minute=0, second=0)) + + await hass.async_block_till_done() + assert 1 == len(calls) + assert 'time - 5' == calls[0].data['some'] + + +async def test_if_not_working_if_no_values_in_conf_provided(hass, calls): + """Test for failure if no configuration.""" + with assert_setup_component(0): + assert await async_setup_component(hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'time', - 'hours': 0, }, 'action': { 'service': 'test.automation' @@ -49,24 +218,25 @@ class TestAutomationTime(unittest.TestCase): } }) - fire_time_changed(self.hass, dt_util.utcnow().replace(hour=0)) - self.hass.block_till_done() - assert 1 == len(self.calls) + async_fire_time_changed(hass, dt_util.utcnow().replace( + hour=5, minute=0, second=0)) - common.turn_off(self.hass) - self.hass.block_till_done() + await hass.async_block_till_done() + assert 0 == len(calls) - fire_time_changed(self.hass, dt_util.utcnow().replace(hour=0)) - self.hass.block_till_done() - assert 1 == len(self.calls) - def test_if_fires_when_minute_matches(self): - """Test for firing if minutes are matching.""" - assert setup_component(self.hass, automation.DOMAIN, { +async def test_if_not_fires_using_wrong_at(hass, calls): + """YAML translates time values to total seconds. + + This should break the before rule. + """ + with assert_setup_component(0): + assert await async_setup_component(hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'time', - 'minutes': 0, + 'at': 3605, + # Total seconds. Hour = 3600 second }, 'action': { 'service': 'test.automation' @@ -74,328 +244,162 @@ class TestAutomationTime(unittest.TestCase): } }) - fire_time_changed(self.hass, dt_util.utcnow().replace(minute=0)) + async_fire_time_changed(hass, dt_util.utcnow().replace( + hour=1, minute=0, second=5)) - self.hass.block_till_done() - assert 1 == len(self.calls) + await hass.async_block_till_done() + assert 0 == len(calls) - def test_if_fires_when_second_matches(self): - """Test for firing if seconds are matching.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'time', - 'seconds': 0, - }, - 'action': { - 'service': 'test.automation' - } + +async def test_if_action_before(hass, calls): + """Test for if action before.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event' + }, + 'condition': { + 'condition': 'time', + 'before': '10:00', + }, + 'action': { + 'service': 'test.automation' } - }) + } + }) - fire_time_changed(self.hass, dt_util.utcnow().replace(second=0)) + before_10 = dt_util.now().replace(hour=8) + after_10 = dt_util.now().replace(hour=14) - self.hass.block_till_done() - assert 1 == len(self.calls) + with patch('homeassistant.helpers.condition.dt_util.now', + return_value=before_10): + hass.bus.async_fire('test_event') + await hass.async_block_till_done() - def test_if_fires_when_all_matches(self): - """Test for firing if everything matches.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'time', - 'hours': 1, - 'minutes': 2, - 'seconds': 3, - }, - 'action': { - 'service': 'test.automation' - } + assert 1 == len(calls) + + with patch('homeassistant.helpers.condition.dt_util.now', + return_value=after_10): + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + + assert 1 == len(calls) + + +async def test_if_action_after(hass, calls): + """Test for if action after.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event' + }, + 'condition': { + 'condition': 'time', + 'after': '10:00', + }, + 'action': { + 'service': 'test.automation' } - }) + } + }) - fire_time_changed(self.hass, dt_util.utcnow().replace( - hour=1, minute=2, second=3)) + before_10 = dt_util.now().replace(hour=8) + after_10 = dt_util.now().replace(hour=14) - self.hass.block_till_done() - assert 1 == len(self.calls) + with patch('homeassistant.helpers.condition.dt_util.now', + return_value=before_10): + hass.bus.async_fire('test_event') + await hass.async_block_till_done() - def test_if_fires_periodic_seconds(self): - """Test for firing periodically every second.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'time', - 'seconds': "/2", - }, - 'action': { - 'service': 'test.automation' - } + assert 0 == len(calls) + + with patch('homeassistant.helpers.condition.dt_util.now', + return_value=after_10): + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + + assert 1 == len(calls) + + +async def test_if_action_one_weekday(hass, calls): + """Test for if action with one weekday.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event' + }, + 'condition': { + 'condition': 'time', + 'weekday': 'mon', + }, + 'action': { + 'service': 'test.automation' } - }) + } + }) - fire_time_changed(self.hass, dt_util.utcnow().replace( - hour=0, minute=0, second=2)) + days_past_monday = dt_util.now().weekday() + monday = dt_util.now() - timedelta(days=days_past_monday) + tuesday = monday + timedelta(days=1) - self.hass.block_till_done() - assert 1 == len(self.calls) + with patch('homeassistant.helpers.condition.dt_util.now', + return_value=monday): + hass.bus.async_fire('test_event') + await hass.async_block_till_done() - def test_if_fires_periodic_minutes(self): - """Test for firing periodically every minute.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'time', - 'minutes': "/2", - }, - 'action': { - 'service': 'test.automation' - } + assert 1 == len(calls) + + with patch('homeassistant.helpers.condition.dt_util.now', + return_value=tuesday): + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + + assert 1 == len(calls) + + +async def test_if_action_list_weekday(hass, calls): + """Test for action with a list of weekdays.""" + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event' + }, + 'condition': { + 'condition': 'time', + 'weekday': ['mon', 'tue'], + }, + 'action': { + 'service': 'test.automation' } - }) + } + }) - fire_time_changed(self.hass, dt_util.utcnow().replace( - hour=0, minute=2, second=0)) + days_past_monday = dt_util.now().weekday() + monday = dt_util.now() - timedelta(days=days_past_monday) + tuesday = monday + timedelta(days=1) + wednesday = tuesday + timedelta(days=1) - self.hass.block_till_done() - assert 1 == len(self.calls) + with patch('homeassistant.helpers.condition.dt_util.now', + return_value=monday): + hass.bus.async_fire('test_event') + await hass.async_block_till_done() - def test_if_fires_periodic_hours(self): - """Test for firing periodically every hour.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'time', - 'hours': "/2", - }, - 'action': { - 'service': 'test.automation' - } - } - }) + assert 1 == len(calls) - fire_time_changed(self.hass, dt_util.utcnow().replace( - hour=2, minute=0, second=0)) + with patch('homeassistant.helpers.condition.dt_util.now', + return_value=tuesday): + hass.bus.async_fire('test_event') + await hass.async_block_till_done() - self.hass.block_till_done() - assert 1 == len(self.calls) + assert 2 == len(calls) - def test_if_fires_using_at(self): - """Test for firing at.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'time', - 'at': '5:00:00', - }, - 'action': { - 'service': 'test.automation', - 'data_template': { - 'some': '{{ trigger.platform }} - ' - '{{ trigger.now.hour }}' - }, - } - } - }) + with patch('homeassistant.helpers.condition.dt_util.now', + return_value=wednesday): + hass.bus.async_fire('test_event') + await hass.async_block_till_done() - fire_time_changed(self.hass, dt_util.utcnow().replace( - hour=5, minute=0, second=0)) - - self.hass.block_till_done() - assert 1 == len(self.calls) - assert 'time - 5' == self.calls[0].data['some'] - - def test_if_not_working_if_no_values_in_conf_provided(self): - """Test for failure if no configuration.""" - with assert_setup_component(0): - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'time', - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - fire_time_changed(self.hass, dt_util.utcnow().replace( - hour=5, minute=0, second=0)) - - self.hass.block_till_done() - assert 0 == len(self.calls) - - def test_if_not_fires_using_wrong_at(self): - """YAML translates time values to total seconds. - - This should break the before rule. - """ - with assert_setup_component(0): - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'time', - 'at': 3605, - # Total seconds. Hour = 3600 second - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - fire_time_changed(self.hass, dt_util.utcnow().replace( - hour=1, minute=0, second=5)) - - self.hass.block_till_done() - assert 0 == len(self.calls) - - def test_if_action_before(self): - """Test for if action before.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event' - }, - 'condition': { - 'condition': 'time', - 'before': '10:00', - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - before_10 = dt_util.now().replace(hour=8) - after_10 = dt_util.now().replace(hour=14) - - with patch('homeassistant.helpers.condition.dt_util.now', - return_value=before_10): - self.hass.bus.fire('test_event') - self.hass.block_till_done() - - assert 1 == len(self.calls) - - with patch('homeassistant.helpers.condition.dt_util.now', - return_value=after_10): - self.hass.bus.fire('test_event') - self.hass.block_till_done() - - assert 1 == len(self.calls) - - def test_if_action_after(self): - """Test for if action after.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event' - }, - 'condition': { - 'condition': 'time', - 'after': '10:00', - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - before_10 = dt_util.now().replace(hour=8) - after_10 = dt_util.now().replace(hour=14) - - with patch('homeassistant.helpers.condition.dt_util.now', - return_value=before_10): - self.hass.bus.fire('test_event') - self.hass.block_till_done() - - assert 0 == len(self.calls) - - with patch('homeassistant.helpers.condition.dt_util.now', - return_value=after_10): - self.hass.bus.fire('test_event') - self.hass.block_till_done() - - assert 1 == len(self.calls) - - def test_if_action_one_weekday(self): - """Test for if action with one weekday.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event' - }, - 'condition': { - 'condition': 'time', - 'weekday': 'mon', - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - days_past_monday = dt_util.now().weekday() - monday = dt_util.now() - timedelta(days=days_past_monday) - tuesday = monday + timedelta(days=1) - - with patch('homeassistant.helpers.condition.dt_util.now', - return_value=monday): - self.hass.bus.fire('test_event') - self.hass.block_till_done() - - assert 1 == len(self.calls) - - with patch('homeassistant.helpers.condition.dt_util.now', - return_value=tuesday): - self.hass.bus.fire('test_event') - self.hass.block_till_done() - - assert 1 == len(self.calls) - - def test_if_action_list_weekday(self): - """Test for action with a list of weekdays.""" - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event' - }, - 'condition': { - 'condition': 'time', - 'weekday': ['mon', 'tue'], - }, - 'action': { - 'service': 'test.automation' - } - } - }) - - days_past_monday = dt_util.now().weekday() - monday = dt_util.now() - timedelta(days=days_past_monday) - tuesday = monday + timedelta(days=1) - wednesday = tuesday + timedelta(days=1) - - with patch('homeassistant.helpers.condition.dt_util.now', - return_value=monday): - self.hass.bus.fire('test_event') - self.hass.block_till_done() - - assert 1 == len(self.calls) - - with patch('homeassistant.helpers.condition.dt_util.now', - return_value=tuesday): - self.hass.bus.fire('test_event') - self.hass.block_till_done() - - assert 2 == len(self.calls) - - with patch('homeassistant.helpers.condition.dt_util.now', - return_value=wednesday): - self.hass.bus.fire('test_event') - self.hass.block_till_done() - - assert 2 == len(self.calls) + assert 2 == len(calls) diff --git a/tests/components/automation/test_zone.py b/tests/components/automation/test_zone.py index d0ab4d726ab..04ffeaf13aa 100644 --- a/tests/components/automation/test_zone.py +++ b/tests/components/automation/test_zone.py @@ -1,218 +1,212 @@ """The tests for the location automation.""" -import unittest +import pytest -from homeassistant.core import Context, callback -from homeassistant.setup import setup_component +from homeassistant.core import Context +from homeassistant.setup import async_setup_component from homeassistant.components import automation, zone -from tests.common import get_test_home_assistant, mock_component from tests.components.automation import common +from tests.common import async_mock_service, mock_component -# pylint: disable=invalid-name -class TestAutomationZone(unittest.TestCase): - """Test the event automation.""" +@pytest.fixture +def calls(hass): + """Track calls to a mock serivce.""" + return async_mock_service(hass, 'test', 'automation') - def setUp(self): - """Set up things to be run when tests are started.""" - self.hass = get_test_home_assistant() - mock_component(self.hass, 'group') - assert setup_component(self.hass, zone.DOMAIN, { + +@pytest.fixture(autouse=True) +def setup_comp(hass): + """Initialize components.""" + mock_component(hass, 'group') + hass.loop.run_until_complete(async_setup_component(hass, zone.DOMAIN, { 'zone': { 'name': 'test', 'latitude': 32.880837, 'longitude': -117.237561, 'radius': 250, } - }) + })) - self.calls = [] - @callback - def record_call(service): - """Record calls.""" - self.calls.append(service) +async def test_if_fires_on_zone_enter(hass, calls): + """Test for firing on zone enter.""" + context = Context() + hass.states.async_set('test.entity', 'hello', { + 'latitude': 32.881011, + 'longitude': -117.234758 + }) + await hass.async_block_till_done() - self.hass.services.register('test', 'automation', record_call) - - def tearDown(self): - """Stop everything that was started.""" - self.hass.stop() - - def test_if_fires_on_zone_enter(self): - """Test for firing on zone enter.""" - context = Context() - self.hass.states.set('test.entity', 'hello', { - 'latitude': 32.881011, - 'longitude': -117.234758 - }) - self.hass.block_till_done() - - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'zone', - 'entity_id': 'test.entity', - 'zone': 'zone.test', - 'event': 'enter', + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'zone', + 'entity_id': 'test.entity', + 'zone': 'zone.test', + 'event': 'enter', + }, + 'action': { + 'service': 'test.automation', + 'data_template': { + 'some': '{{ trigger.%s }}' % '}} - {{ trigger.'.join(( + 'platform', 'entity_id', + 'from_state.state', 'to_state.state', + 'zone.name')) }, - 'action': { - 'service': 'test.automation', - 'data_template': { - 'some': '{{ trigger.%s }}' % '}} - {{ trigger.'.join(( - 'platform', 'entity_id', - 'from_state.state', 'to_state.state', - 'zone.name')) - }, - } } - }) + } + }) - self.hass.states.set('test.entity', 'hello', { - 'latitude': 32.880586, - 'longitude': -117.237564 - }, context=context) - self.hass.block_till_done() + hass.states.async_set('test.entity', 'hello', { + 'latitude': 32.880586, + 'longitude': -117.237564 + }, context=context) + await hass.async_block_till_done() - assert 1 == len(self.calls) - assert self.calls[0].context is context - assert 'zone - test.entity - hello - hello - test' == \ - self.calls[0].data['some'] + assert 1 == len(calls) + assert calls[0].context is context + assert 'zone - test.entity - hello - hello - test' == \ + calls[0].data['some'] - # Set out of zone again so we can trigger call - self.hass.states.set('test.entity', 'hello', { - 'latitude': 32.881011, - 'longitude': -117.234758 - }) - self.hass.block_till_done() + # Set out of zone again so we can trigger call + hass.states.async_set('test.entity', 'hello', { + 'latitude': 32.881011, + 'longitude': -117.234758 + }) + await hass.async_block_till_done() - common.turn_off(self.hass) - self.hass.block_till_done() + await common.async_turn_off(hass) + await hass.async_block_till_done() - self.hass.states.set('test.entity', 'hello', { - 'latitude': 32.880586, - 'longitude': -117.237564 - }) - self.hass.block_till_done() + hass.states.async_set('test.entity', 'hello', { + 'latitude': 32.880586, + 'longitude': -117.237564 + }) + await hass.async_block_till_done() - assert 1 == len(self.calls) + assert 1 == len(calls) - def test_if_not_fires_for_enter_on_zone_leave(self): - """Test for not firing on zone leave.""" - self.hass.states.set('test.entity', 'hello', { - 'latitude': 32.880586, - 'longitude': -117.237564 - }) - self.hass.block_till_done() - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'zone', - 'entity_id': 'test.entity', - 'zone': 'zone.test', - 'event': 'enter', - }, - 'action': { - 'service': 'test.automation', - } +async def test_if_not_fires_for_enter_on_zone_leave(hass, calls): + """Test for not firing on zone leave.""" + hass.states.async_set('test.entity', 'hello', { + 'latitude': 32.880586, + 'longitude': -117.237564 + }) + await hass.async_block_till_done() + + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'zone', + 'entity_id': 'test.entity', + 'zone': 'zone.test', + 'event': 'enter', + }, + 'action': { + 'service': 'test.automation', } - }) + } + }) - self.hass.states.set('test.entity', 'hello', { - 'latitude': 32.881011, - 'longitude': -117.234758 - }) - self.hass.block_till_done() + hass.states.async_set('test.entity', 'hello', { + 'latitude': 32.881011, + 'longitude': -117.234758 + }) + await hass.async_block_till_done() - assert 0 == len(self.calls) + assert 0 == len(calls) - def test_if_fires_on_zone_leave(self): - """Test for firing on zone leave.""" - self.hass.states.set('test.entity', 'hello', { - 'latitude': 32.880586, - 'longitude': -117.237564 - }) - self.hass.block_till_done() - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'zone', - 'entity_id': 'test.entity', - 'zone': 'zone.test', - 'event': 'leave', - }, - 'action': { - 'service': 'test.automation', - } +async def test_if_fires_on_zone_leave(hass, calls): + """Test for firing on zone leave.""" + hass.states.async_set('test.entity', 'hello', { + 'latitude': 32.880586, + 'longitude': -117.237564 + }) + await hass.async_block_till_done() + + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'zone', + 'entity_id': 'test.entity', + 'zone': 'zone.test', + 'event': 'leave', + }, + 'action': { + 'service': 'test.automation', } - }) + } + }) - self.hass.states.set('test.entity', 'hello', { - 'latitude': 32.881011, - 'longitude': -117.234758 - }) - self.hass.block_till_done() + hass.states.async_set('test.entity', 'hello', { + 'latitude': 32.881011, + 'longitude': -117.234758 + }) + await hass.async_block_till_done() - assert 1 == len(self.calls) + assert 1 == len(calls) - def test_if_not_fires_for_leave_on_zone_enter(self): - """Test for not firing on zone enter.""" - self.hass.states.set('test.entity', 'hello', { - 'latitude': 32.881011, - 'longitude': -117.234758 - }) - self.hass.block_till_done() - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'zone', - 'entity_id': 'test.entity', - 'zone': 'zone.test', - 'event': 'leave', - }, - 'action': { - 'service': 'test.automation', - } +async def test_if_not_fires_for_leave_on_zone_enter(hass, calls): + """Test for not firing on zone enter.""" + hass.states.async_set('test.entity', 'hello', { + 'latitude': 32.881011, + 'longitude': -117.234758 + }) + await hass.async_block_till_done() + + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'zone', + 'entity_id': 'test.entity', + 'zone': 'zone.test', + 'event': 'leave', + }, + 'action': { + 'service': 'test.automation', } - }) + } + }) - self.hass.states.set('test.entity', 'hello', { - 'latitude': 32.880586, - 'longitude': -117.237564 - }) - self.hass.block_till_done() + hass.states.async_set('test.entity', 'hello', { + 'latitude': 32.880586, + 'longitude': -117.237564 + }) + await hass.async_block_till_done() - assert 0 == len(self.calls) + assert 0 == len(calls) - def test_zone_condition(self): - """Test for zone condition.""" - self.hass.states.set('test.entity', 'hello', { - 'latitude': 32.880586, - 'longitude': -117.237564 - }) - self.hass.block_till_done() - assert setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event' - }, - 'condition': { - 'condition': 'zone', - 'entity_id': 'test.entity', - 'zone': 'zone.test', - }, - 'action': { - 'service': 'test.automation', - } +async def test_zone_condition(hass, calls): + """Test for zone condition.""" + hass.states.async_set('test.entity', 'hello', { + 'latitude': 32.880586, + 'longitude': -117.237564 + }) + await hass.async_block_till_done() + + assert await async_setup_component(hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event' + }, + 'condition': { + 'condition': 'zone', + 'entity_id': 'test.entity', + 'zone': 'zone.test', + }, + 'action': { + 'service': 'test.automation', } - }) + } + }) - self.hass.bus.fire('test_event') - self.hass.block_till_done() - assert 1 == len(self.calls) + hass.bus.async_fire('test_event') + await hass.async_block_till_done() + assert 1 == len(calls)