Convert automation to entities with services

This commit is contained in:
Paulus Schoutsen 2016-08-25 23:25:57 -07:00
parent d9ecc4af64
commit 3fa1963345
18 changed files with 365 additions and 61 deletions

View file

@ -4,19 +4,26 @@ Allow to setup simple automation rules via the config file.
For more details about this component, please refer to the documentation at For more details about this component, please refer to the documentation at
https://home-assistant.io/components/automation/ https://home-assistant.io/components/automation/
""" """
from functools import partial
import logging import logging
import voluptuous as vol import voluptuous as vol
from homeassistant.bootstrap import prepare_setup_platform from homeassistant.bootstrap import prepare_setup_platform
from homeassistant.const import ATTR_ENTITY_ID, CONF_PLATFORM from homeassistant.const import (
ATTR_ENTITY_ID, CONF_PLATFORM, STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF,
SERVICE_TOGGLE)
from homeassistant.components import logbook from homeassistant.components import logbook
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import extract_domain_configs, script, condition from homeassistant.helpers import extract_domain_configs, script, condition
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.loader import get_platform from homeassistant.loader import get_platform
from homeassistant.util.dt import utcnow
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
DOMAIN = 'automation' DOMAIN = 'automation'
ENTITY_ID_FORMAT = DOMAIN + '.{}'
DEPENDENCIES = ['group'] DEPENDENCIES = ['group']
@ -36,6 +43,10 @@ DEFAULT_CONDITION_TYPE = CONDITION_TYPE_AND
METHOD_TRIGGER = 'trigger' METHOD_TRIGGER = 'trigger'
METHOD_IF_ACTION = 'if_action' METHOD_IF_ACTION = 'if_action'
ATTR_LAST_TRIGGERED = 'last_triggered'
ATTR_VARIABLES = 'variables'
SERVICE_TRIGGER = 'trigger'
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -88,41 +99,170 @@ PLATFORM_SCHEMA = vol.Schema({
vol.Required(CONF_TRIGGER): _TRIGGER_SCHEMA, vol.Required(CONF_TRIGGER): _TRIGGER_SCHEMA,
vol.Required(CONF_CONDITION_TYPE, default=DEFAULT_CONDITION_TYPE): vol.Required(CONF_CONDITION_TYPE, default=DEFAULT_CONDITION_TYPE):
vol.All(vol.Lower, vol.Any(CONDITION_TYPE_AND, CONDITION_TYPE_OR)), vol.All(vol.Lower, vol.Any(CONDITION_TYPE_AND, CONDITION_TYPE_OR)),
CONF_CONDITION: _CONDITION_SCHEMA, vol.Optional(CONF_CONDITION): _CONDITION_SCHEMA,
vol.Required(CONF_ACTION): cv.SCRIPT_SCHEMA, vol.Required(CONF_ACTION): cv.SCRIPT_SCHEMA,
}) })
SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
})
TRIGGER_SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_VARIABLES, default={}): dict,
})
def is_on(hass, entity_id=None):
"""
Return true if specified automation entity_id is on.
Check all automation if no entity_id specified.
"""
entity_ids = [entity_id] if entity_id else hass.states.entity_ids(DOMAIN)
return any(hass.states.is_state(entity_id, STATE_ON)
for entity_id in entity_ids)
def 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)
def 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)
def 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)
def 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)
def setup(hass, config): def setup(hass, config):
"""Setup the automation.""" """Setup the automation."""
component = EntityComponent(_LOGGER, DOMAIN, hass)
success = False success = False
for config_key in extract_domain_configs(config, DOMAIN): for config_key in extract_domain_configs(config, DOMAIN):
conf = config[config_key] conf = config[config_key]
for list_no, config_block in enumerate(conf): for list_no, config_block in enumerate(conf):
name = config_block.get(CONF_ALIAS, "{}, {}".format(config_key, name = config_block.get(CONF_ALIAS) or "{} {}".format(config_key,
list_no)) list_no)
success = (_setup_automation(hass, config_block, name, config) or
success)
return success
def _setup_automation(hass, config_block, name, config):
"""Setup one instance of automation."""
action = _get_action(hass, config_block.get(CONF_ACTION, {}), name) action = _get_action(hass, config_block.get(CONF_ACTION, {}), name)
if CONF_CONDITION in config_block: if CONF_CONDITION in config_block:
action = _process_if(hass, config, config_block, action) cond_func = _process_if(hass, config, config_block)
if action is None: if cond_func is None:
continue
else:
def cond_func(variables):
"""Condition will always pass."""
return True
attach_triggers = partial(_process_trigger, hass, config,
config_block.get(CONF_TRIGGER, []), name)
entity = AutomationEntity(name, attach_triggers, cond_func, action)
component.add_entities((entity,))
success = True
if not success:
return False return False
_process_trigger(hass, config, config_block.get(CONF_TRIGGER, []), name, def trigger_service_handler(service_call):
action) """Handle automation triggers."""
for entity in component.extract_from_service(service_call):
entity.trigger(service_call.data.get(ATTR_VARIABLES))
def service_handler(service_call):
"""Handle automation service calls."""
for entity in component.extract_from_service(service_call):
getattr(entity, service_call.service)()
hass.services.register(DOMAIN, SERVICE_TRIGGER, trigger_service_handler,
schema=TRIGGER_SERVICE_SCHEMA)
for service in (SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE):
hass.services.register(DOMAIN, service, service_handler,
schema=SERVICE_SCHEMA)
return True return True
class AutomationEntity(ToggleEntity):
"""Entity to show status of entity."""
def __init__(self, name, attach_triggers, cond_func, action):
"""Initialize an automation entity."""
self._name = name
self._attach_triggers = attach_triggers
self._detach_triggers = attach_triggers(self.trigger)
self._cond_func = cond_func
self._action = action
self._enabled = True
self._last_triggered = None
@property
def name(self):
"""Name of the automation."""
return self._name
@property
def should_poll(self):
"""No polling needed for automation entities."""
return False
@property
def state_attributes(self):
"""Return the entity state attributes."""
return {
ATTR_LAST_TRIGGERED: self._last_triggered
}
@property
def is_on(self) -> bool:
"""Return True if entity is on."""
return self._enabled
def turn_on(self, **kwargs) -> None:
"""Turn the entity on."""
if self._enabled:
return
self._detach_triggers = self._attach_triggers(self.trigger)
self._enabled = True
self.update_ha_state()
def turn_off(self, **kwargs) -> None:
"""Turn the entity off."""
if not self._enabled:
return
self._detach_triggers()
self._detach_triggers = None
self._enabled = False
self.update_ha_state()
def trigger(self, variables):
"""Trigger automation."""
if self._cond_func(variables):
self._action(variables)
self._last_triggered = utcnow()
self.update_ha_state()
def _get_action(hass, config, name): def _get_action(hass, config, name):
"""Return an action based on a configuration.""" """Return an action based on a configuration."""
script_obj = script.Script(hass, config, name) script_obj = script.Script(hass, config, name)
@ -136,7 +276,7 @@ def _get_action(hass, config, name):
return action return action
def _process_if(hass, config, p_config, action): def _process_if(hass, config, p_config):
"""Process if checks.""" """Process if checks."""
cond_type = p_config.get(CONF_CONDITION_TYPE, cond_type = p_config.get(CONF_CONDITION_TYPE,
DEFAULT_CONDITION_TYPE).lower() DEFAULT_CONDITION_TYPE).lower()
@ -182,29 +322,43 @@ def _process_if(hass, config, p_config, action):
if cond_type == CONDITION_TYPE_AND: if cond_type == CONDITION_TYPE_AND:
def if_action(variables=None): def if_action(variables=None):
"""AND all conditions.""" """AND all conditions."""
if all(check(hass, variables) for check in checks): return all(check(hass, variables) for check in checks)
action(variables)
else: else:
def if_action(variables=None): def if_action(variables=None):
"""OR all conditions.""" """OR all conditions."""
if any(check(hass, variables) for check in checks): return any(check(hass, variables) for check in checks)
action(variables)
return if_action return if_action
def _process_trigger(hass, config, trigger_configs, name, action): def _process_trigger(hass, config, trigger_configs, name, action):
"""Setup the triggers.""" """Setup the triggers."""
removes = []
for conf in trigger_configs: for conf in trigger_configs:
platform = _resolve_platform(METHOD_TRIGGER, hass, config, platform = _resolve_platform(METHOD_TRIGGER, hass, config,
conf.get(CONF_PLATFORM)) conf.get(CONF_PLATFORM))
if platform is None: if platform is None:
continue continue
if platform.trigger(hass, conf, action): remove = platform.trigger(hass, conf, action)
_LOGGER.info("Initialized rule %s", name)
else: if not remove:
_LOGGER.error("Error setting up rule %s", name) _LOGGER.error("Error setting up rule %s", name)
continue
_LOGGER.info("Initialized rule %s", name)
removes.append(remove)
if not removes:
return None
def remove_triggers():
"""Remove attached triggers."""
for remove in removes:
remove()
return remove_triggers
def _resolve_platform(method, hass, config, platform): def _resolve_platform(method, hass, config, platform):

View file

@ -39,5 +39,4 @@ def trigger(hass, config, action):
}, },
}) })
hass.bus.listen(event_type, handle_event) return hass.bus.listen(event_type, handle_event)
return True

View file

@ -39,6 +39,4 @@ def trigger(hass, config, action):
} }
}) })
mqtt.subscribe(hass, topic, mqtt_automation_listener) return mqtt.subscribe(hass, topic, mqtt_automation_listener)
return True

View file

@ -63,7 +63,4 @@ def trigger(hass, config, action):
action(variables) action(variables)
track_state_change( return track_state_change(hass, entity_id, state_automation_listener)
hass, entity_id, state_automation_listener)
return True

View file

@ -38,9 +38,13 @@ def trigger(hass, config, action):
from_state = config.get(CONF_FROM, MATCH_ALL) from_state = config.get(CONF_FROM, MATCH_ALL)
to_state = config.get(CONF_TO) or config.get(CONF_STATE) or MATCH_ALL to_state = config.get(CONF_TO) or config.get(CONF_STATE) or MATCH_ALL
time_delta = config.get(CONF_FOR) time_delta = config.get(CONF_FOR)
remove_state_for_cancel = None
remove_state_for_listener = None
def state_automation_listener(entity, from_s, to_s): def state_automation_listener(entity, from_s, to_s):
"""Listen for state changes and calls action.""" """Listen for state changes and calls action."""
nonlocal remove_state_for_cancel, remove_state_for_listener
def call_action(): def call_action():
"""Call action with right context.""" """Call action with right context."""
action({ action({
@ -75,7 +79,17 @@ def trigger(hass, config, action):
remove_state_for_cancel = track_state_change( remove_state_for_cancel = track_state_change(
hass, entity, state_for_cancel_listener) hass, entity, state_for_cancel_listener)
track_state_change( unsub = track_state_change(hass, entity_id, state_automation_listener,
hass, entity_id, state_automation_listener, from_state, to_state) from_state, to_state)
return True def remove():
"""Remove state listeners."""
unsub()
if remove_state_for_cancel is not None:
remove_state_for_cancel()
if remove_state_for_listener is not None:
remove_state_for_listener()
return remove

View file

@ -42,8 +42,6 @@ def trigger(hass, config, action):
# Do something to call action # Do something to call action
if event == SUN_EVENT_SUNRISE: if event == SUN_EVENT_SUNRISE:
track_sunrise(hass, call_action, offset) return track_sunrise(hass, call_action, offset)
else: else:
track_sunset(hass, call_action, offset) return track_sunset(hass, call_action, offset)
return True

View file

@ -49,5 +49,4 @@ def trigger(hass, config, action):
elif not template_result: elif not template_result:
already_triggered = False already_triggered = False
track_state_change(hass, MATCH_ALL, state_changed_listener) return track_state_change(hass, MATCH_ALL, state_changed_listener)
return True

View file

@ -47,7 +47,5 @@ def trigger(hass, config, action):
}, },
}) })
track_time_change(hass, time_automation_listener, return track_time_change(hass, time_automation_listener,
hour=hours, minute=minutes, second=seconds) hour=hours, minute=minutes, second=seconds)
return True

View file

@ -58,7 +58,5 @@ def trigger(hass, config, action):
}, },
}) })
track_state_change( return track_state_change(hass, entity_id, zone_automation_listener,
hass, entity_id, zone_automation_listener, MATCH_ALL, MATCH_ALL) MATCH_ALL, MATCH_ALL)
return True

View file

@ -44,6 +44,13 @@ class TestAutomationEvent(unittest.TestCase):
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls)) self.assertEqual(1, len(self.calls))
automation.turn_off(self.hass)
self.hass.pool.block_till_done()
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_fires_on_event_with_data(self): def test_if_fires_on_event_with_data(self):
"""Test the firing of events with data.""" """Test the firing of events with data."""
assert _setup_component(self.hass, automation.DOMAIN, { assert _setup_component(self.hass, automation.DOMAIN, {

View file

@ -1,9 +1,11 @@
"""The tests for the automation component.""" """The tests for the automation component."""
import unittest import unittest
from unittest.mock import patch
from homeassistant.bootstrap import _setup_component from homeassistant.bootstrap import _setup_component
import homeassistant.components.automation as automation import homeassistant.components.automation as automation
from homeassistant.const import ATTR_ENTITY_ID from homeassistant.const import ATTR_ENTITY_ID
import homeassistant.util.dt as dt_util
from tests.common import get_test_home_assistant from tests.common import get_test_home_assistant
@ -45,6 +47,7 @@ class TestAutomation(unittest.TestCase):
"""Test service data.""" """Test service data."""
assert _setup_component(self.hass, automation.DOMAIN, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'alias': 'hello',
'trigger': { 'trigger': {
'platform': 'event', 'platform': 'event',
'event_type': 'test_event', 'event_type': 'test_event',
@ -59,10 +62,17 @@ class TestAutomation(unittest.TestCase):
} }
}) })
time = dt_util.utcnow()
with patch('homeassistant.components.automation.utcnow',
return_value=time):
self.hass.bus.fire('test_event') self.hass.bus.fire('test_event')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls)) assert len(self.calls) == 1
self.assertEqual('event - test_event', self.calls[0].data['some']) assert 'event - test_event' == self.calls[0].data['some']
state = self.hass.states.get('automation.hello')
assert state is not None
assert state.attributes.get('last_triggered') == time
def test_service_specify_entity_id(self): def test_service_specify_entity_id(self):
"""Test service data.""" """Test service data."""
@ -347,3 +357,60 @@ class TestAutomation(unittest.TestCase):
assert len(self.calls) == 2 assert len(self.calls) == 2
assert self.calls[0].data['position'] == 0 assert self.calls[0].data['position'] == 0
assert self.calls[1].data['position'] == 1 assert self.calls[1].data['position'] == 1
def test_services(self):
""" """
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.pool.block_till_done()
assert len(self.calls) == 1
automation.turn_off(self.hass, entity_id)
self.hass.pool.block_till_done()
assert not automation.is_on(self.hass, entity_id)
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
assert len(self.calls) == 1
automation.toggle(self.hass, entity_id)
self.hass.pool.block_till_done()
assert automation.is_on(self.hass, entity_id)
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
assert len(self.calls) == 2
automation.trigger(self.hass, entity_id)
self.hass.pool.block_till_done()
assert len(self.calls) == 3
automation.turn_off(self.hass, entity_id)
self.hass.pool.block_till_done()
automation.trigger(self.hass, entity_id)
self.hass.pool.block_till_done()
assert len(self.calls) == 4
automation.turn_on(self.hass, entity_id)
self.hass.pool.block_till_done()
assert automation.is_on(self.hass, entity_id)

View file

@ -50,6 +50,12 @@ class TestAutomationMQTT(unittest.TestCase):
self.assertEqual('mqtt - test-topic - test_payload', self.assertEqual('mqtt - test-topic - test_payload',
self.calls[0].data['some']) self.calls[0].data['some'])
automation.turn_off(self.hass)
self.hass.pool.block_till_done()
fire_mqtt_message(self.hass, 'test-topic', 'test_payload')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_fires_on_topic_and_payload_match(self): def test_if_fires_on_topic_and_payload_match(self):
"""Test if message is fired on topic and payload match.""" """Test if message is fired on topic and payload match."""
assert _setup_component(self.hass, automation.DOMAIN, { assert _setup_component(self.hass, automation.DOMAIN, {

View file

@ -45,6 +45,14 @@ class TestAutomationNumericState(unittest.TestCase):
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls)) self.assertEqual(1, len(self.calls))
# Set above 12 so the automation will fire again
self.hass.states.set('test.entity', 12)
automation.turn_off(self.hass)
self.hass.pool.block_till_done()
self.hass.states.set('test.entity', 9)
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_fires_on_entity_change_over_to_below(self): def test_if_fires_on_entity_change_over_to_below(self):
""""Test the firing with changed entity.""" """"Test the firing with changed entity."""
self.hass.states.set('test.entity', 11) self.hass.states.set('test.entity', 11)

View file

@ -59,6 +59,12 @@ class TestAutomationState(unittest.TestCase):
'state - test.entity - hello - world - None', 'state - test.entity - hello - world - None',
self.calls[0].data['some']) self.calls[0].data['some'])
automation.turn_off(self.hass)
self.hass.pool.block_till_done()
self.hass.states.set('test.entity', 'planet')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_fires_on_entity_change_with_from_filter(self): def test_if_fires_on_entity_change_with_from_filter(self):
"""Test for firing on entity change with filter.""" """Test for firing on entity change with filter."""
assert _setup_component(self.hass, automation.DOMAIN, { assert _setup_component(self.hass, automation.DOMAIN, {

View file

@ -54,6 +54,18 @@ class TestAutomationSun(unittest.TestCase):
} }
}) })
automation.turn_off(self.hass)
self.hass.pool.block_till_done()
fire_time_changed(self.hass, trigger_time)
self.hass.pool.block_till_done()
self.assertEqual(0, len(self.calls))
with patch('homeassistant.util.dt.utcnow',
return_value=now):
automation.turn_on(self.hass)
self.hass.pool.block_till_done()
fire_time_changed(self.hass, trigger_time) fire_time_changed(self.hass, trigger_time)
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls)) self.assertEqual(1, len(self.calls))

View file

@ -45,6 +45,13 @@ class TestAutomationTemplate(unittest.TestCase):
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls)) self.assertEqual(1, len(self.calls))
automation.turn_off(self.hass)
self.hass.pool.block_till_done()
self.hass.states.set('test.entity', 'planet')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_fires_on_change_str(self): def test_if_fires_on_change_str(self):
"""Test for firing on change.""" """Test for firing on change."""
assert _setup_component(self.hass, automation.DOMAIN, { assert _setup_component(self.hass, automation.DOMAIN, {
@ -149,6 +156,9 @@ class TestAutomationTemplate(unittest.TestCase):
} }
}) })
self.hass.pool.block_till_done()
self.calls = []
self.hass.states.set('test.entity', 'hello') self.hass.states.set('test.entity', 'hello')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(0, len(self.calls)) self.assertEqual(0, len(self.calls))
@ -209,9 +219,12 @@ class TestAutomationTemplate(unittest.TestCase):
} }
}) })
self.hass.pool.block_till_done()
self.calls = []
self.hass.states.set('test.entity', 'world') self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(0, len(self.calls)) assert len(self.calls) == 0
def test_if_fires_on_change_with_template_advanced(self): def test_if_fires_on_change_with_template_advanced(self):
"""Test for firing on change with template advanced.""" """Test for firing on change with template advanced."""
@ -237,6 +250,9 @@ class TestAutomationTemplate(unittest.TestCase):
} }
}) })
self.hass.pool.block_till_done()
self.calls = []
self.hass.states.set('test.entity', 'world') self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls)) self.assertEqual(1, len(self.calls))
@ -287,29 +303,32 @@ class TestAutomationTemplate(unittest.TestCase):
} }
}) })
self.hass.pool.block_till_done()
self.calls = []
self.hass.states.set('test.entity', 'world') self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(0, len(self.calls)) assert len(self.calls) == 0
self.hass.states.set('test.entity', 'home') self.hass.states.set('test.entity', 'home')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls)) assert len(self.calls) == 1
self.hass.states.set('test.entity', 'work') self.hass.states.set('test.entity', 'work')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls)) assert len(self.calls) == 1
self.hass.states.set('test.entity', 'not_home') self.hass.states.set('test.entity', 'not_home')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls)) assert len(self.calls) == 1
self.hass.states.set('test.entity', 'world') self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls)) assert len(self.calls) == 1
self.hass.states.set('test.entity', 'home') self.hass.states.set('test.entity', 'home')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(2, len(self.calls)) assert len(self.calls) == 2
def test_if_action(self): def test_if_action(self):
"""Test for firing if action.""" """Test for firing if action."""

View file

@ -43,7 +43,13 @@ class TestAutomationTime(unittest.TestCase):
}) })
fire_time_changed(self.hass, dt_util.utcnow().replace(hour=0)) fire_time_changed(self.hass, dt_util.utcnow().replace(hour=0))
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
automation.turn_off(self.hass)
self.hass.pool.block_till_done()
fire_time_changed(self.hass, dt_util.utcnow().replace(hour=0))
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls)) self.assertEqual(1, len(self.calls))

View file

@ -74,6 +74,24 @@ class TestAutomationZone(unittest.TestCase):
'zone - test.entity - hello - hello - test', 'zone - test.entity - hello - hello - test',
self.calls[0].data['some']) self.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.pool.block_till_done()
automation.turn_off(self.hass)
self.hass.pool.block_till_done()
self.hass.states.set('test.entity', 'hello', {
'latitude': 32.880586,
'longitude': -117.237564
})
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_not_fires_for_enter_on_zone_leave(self): def test_if_not_fires_for_enter_on_zone_leave(self):
"""Test for not firing on zone leave.""" """Test for not firing on zone leave."""
self.hass.states.set('test.entity', 'hello', { self.hass.states.set('test.entity', 'hello', {