Add if to automation

This commit is contained in:
Paulus Schoutsen 2015-09-13 22:25:42 -07:00
parent 046c5653cb
commit 2a11d02fe4
8 changed files with 275 additions and 11 deletions

View File

@ -9,7 +9,7 @@ import logging
from homeassistant.bootstrap import prepare_setup_platform from homeassistant.bootstrap import prepare_setup_platform
from homeassistant.helpers import config_per_platform from homeassistant.helpers import config_per_platform
from homeassistant.util import split_entity_id from homeassistant.util import split_entity_id
from homeassistant.const import ATTR_ENTITY_ID from homeassistant.const import ATTR_ENTITY_ID, CONF_PLATFORM
DOMAIN = "automation" DOMAIN = "automation"
@ -19,6 +19,7 @@ CONF_ALIAS = "alias"
CONF_SERVICE = "execute_service" CONF_SERVICE = "execute_service"
CONF_SERVICE_ENTITY_ID = "service_entity_id" CONF_SERVICE_ENTITY_ID = "service_entity_id"
CONF_SERVICE_DATA = "service_data" CONF_SERVICE_DATA = "service_data"
CONF_IF = "if"
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -34,7 +35,12 @@ def setup(hass, config):
_LOGGER.error("Unknown automation platform specified: %s", p_type) _LOGGER.error("Unknown automation platform specified: %s", p_type)
continue continue
if platform.register(hass, p_config, _get_action(hass, p_config)): action = _get_action(hass, p_config)
if CONF_IF in p_config:
action = _process_if(hass, config, p_config[CONF_IF], action)
if platform.trigger(hass, p_config, action):
_LOGGER.info( _LOGGER.info(
"Initialized %s rule %s", p_type, p_config.get(CONF_ALIAS, "")) "Initialized %s rule %s", p_type, p_config.get(CONF_ALIAS, ""))
success = True success = True
@ -72,3 +78,28 @@ def _get_action(hass, config):
hass.services.call(domain, service, service_data) hass.services.call(domain, service, service_data)
return action return action
def _process_if(hass, config, if_configs, action):
""" Processes if checks. """
if isinstance(if_configs, dict):
if_configs = [if_configs]
for if_config in if_configs:
p_type = if_config.get(CONF_PLATFORM)
if p_type is None:
_LOGGER.error("No platform defined found for if-statement %s",
if_config)
continue
platform = prepare_setup_platform(hass, config, DOMAIN, p_type)
if platform is None or not hasattr(platform, 'if_action'):
_LOGGER.error("Unsupported if-statement platform specified: %s",
p_type)
continue
action = platform.if_action(hass, if_config, action)
return action

View File

@ -12,7 +12,7 @@ CONF_EVENT_DATA = "event_data"
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
def register(hass, config, action): def trigger(hass, config, action):
""" Listen for events based on config. """ """ Listen for events based on config. """
event_type = config.get(CONF_EVENT_TYPE) event_type = config.get(CONF_EVENT_TYPE)

View File

@ -14,7 +14,7 @@ CONF_TOPIC = 'mqtt_topic'
CONF_PAYLOAD = 'mqtt_payload' CONF_PAYLOAD = 'mqtt_payload'
def register(hass, config, action): def trigger(hass, config, action):
""" Listen for state changes based on `config`. """ """ Listen for state changes based on `config`. """
topic = config.get(CONF_TOPIC) topic = config.get(CONF_TOPIC)
payload = config.get(CONF_PAYLOAD) payload = config.get(CONF_PAYLOAD)

View File

@ -16,7 +16,7 @@ CONF_ABOVE = "state_above"
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
def register(hass, config, action): def trigger(hass, config, action):
""" Listen for state changes based on `config`. """ """ Listen for state changes based on `config`. """
entity_id = config.get(CONF_ENTITY_ID) entity_id = config.get(CONF_ENTITY_ID)

View File

@ -13,15 +13,16 @@ from homeassistant.const import MATCH_ALL
CONF_ENTITY_ID = "state_entity_id" CONF_ENTITY_ID = "state_entity_id"
CONF_FROM = "state_from" CONF_FROM = "state_from"
CONF_TO = "state_to" CONF_TO = "state_to"
CONF_STATE = "state"
def register(hass, config, action): def trigger(hass, config, action):
""" Listen for state changes based on `config`. """ """ Listen for state changes based on `config`. """
entity_id = config.get(CONF_ENTITY_ID) entity_id = config.get(CONF_ENTITY_ID)
if entity_id is None: if entity_id is None:
logging.getLogger(__name__).error( logging.getLogger(__name__).error(
"Missing configuration key %s", CONF_ENTITY_ID) "Missing trigger configuration key %s", CONF_ENTITY_ID)
return False return False
from_state = config.get(CONF_FROM, MATCH_ALL) from_state = config.get(CONF_FROM, MATCH_ALL)
@ -35,3 +36,22 @@ def register(hass, config, action):
hass, entity_id, state_automation_listener, from_state, to_state) hass, entity_id, state_automation_listener, from_state, to_state)
return True return True
def if_action(hass, config, action):
""" Wraps action method with state based condition. """
entity_id = config.get(CONF_ENTITY_ID)
state = config.get(CONF_STATE)
if entity_id is None or state is None:
logging.getLogger(__name__).error(
"Missing if-condition configuration key %s or %s", CONF_ENTITY_ID,
CONF_STATE)
return action
def state_if():
""" Execute action if state matches. """
if hass.states.is_state(entity_id, state):
action()
return state_if

View File

@ -4,15 +4,23 @@ homeassistant.components.automation.time
Offers time listening automation rules. Offers time listening automation rules.
""" """
import logging
from homeassistant.util import convert from homeassistant.util import convert
import homeassistant.util.dt as dt_util
from homeassistant.helpers.event import track_time_change from homeassistant.helpers.event import track_time_change
CONF_HOURS = "time_hours" CONF_HOURS = "time_hours"
CONF_MINUTES = "time_minutes" CONF_MINUTES = "time_minutes"
CONF_SECONDS = "time_seconds" CONF_SECONDS = "time_seconds"
CONF_BEFORE = "before"
CONF_AFTER = "after"
CONF_WEEKDAY = "weekday"
WEEKDAYS = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']
def register(hass, config, action): def trigger(hass, config, action):
""" Listen for state changes based on `config`. """ """ Listen for state changes based on `config`. """
hours = convert(config.get(CONF_HOURS), int) hours = convert(config.get(CONF_HOURS), int)
minutes = convert(config.get(CONF_MINUTES), int) minutes = convert(config.get(CONF_MINUTES), int)
@ -26,3 +34,49 @@ def register(hass, config, action):
hour=hours, minute=minutes, second=seconds) hour=hours, minute=minutes, second=seconds)
return True return True
def if_action(hass, config, action):
""" Wraps action method with time based condition. """
before = config.get(CONF_BEFORE)
after = config.get(CONF_AFTER)
weekday = config.get(CONF_WEEKDAY)
if before is None and after is None and weekday is None:
logging.getLogger(__name__).error(
"Missing if-condition configuration key %s, %s or %s",
CONF_BEFORE, CONF_AFTER, CONF_WEEKDAY)
def time_if():
""" Validate time based if-condition """
now = dt_util.now()
if before is not None:
# Strip seconds if given
before_h, before_m = before.split(':')[0:2]
before_point = now.replace(hour=int(before_h),
minute=int(before_m))
if now > before_point:
return
if after is not None:
# Strip seconds if given
after_h, after_m = after.split(':')[0:2]
after_point = now.replace(hour=int(after_h), minute=int(after_m))
if now < after_point:
return
if weekday is not None:
now_weekday = WEEKDAYS[now.weekday()]
if isinstance(weekday, str) and weekday != now_weekday or \
now_weekday not in weekday:
return
action()
return time_if

View File

@ -8,7 +8,7 @@ import unittest
import homeassistant.core as ha import homeassistant.core as ha
import homeassistant.components.automation as automation import homeassistant.components.automation as automation
import homeassistant.components.automation.state as state from homeassistant.components.automation import event, state
from homeassistant.const import CONF_PLATFORM from homeassistant.const import CONF_PLATFORM
@ -137,3 +137,31 @@ class TestAutomationState(unittest.TestCase):
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)) self.assertEqual(0, len(self.calls))
def test_if_action(self):
entity_id = 'domain.test_entity'
test_state = 'new_state'
automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'event',
event.CONF_EVENT_TYPE: 'test_event',
automation.CONF_SERVICE: 'test.automation',
automation.CONF_IF: [{
CONF_PLATFORM: 'state',
state.CONF_ENTITY_ID: entity_id,
state.CONF_STATE: test_state,
}]
}
})
self.hass.states.set(entity_id, test_state)
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
self.hass.states.set(entity_id, test_state + 'something')
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))

View File

@ -4,13 +4,14 @@ tests.test_component_demo
Tests demo component. Tests demo component.
""" """
from datetime import timedelta
import unittest import unittest
from unittest.mock import patch
import homeassistant.core as ha import homeassistant.core as ha
import homeassistant.loader as loader
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
import homeassistant.components.automation as automation import homeassistant.components.automation as automation
import homeassistant.components.automation.time as time from homeassistant.components.automation import time, event
from homeassistant.const import CONF_PLATFORM from homeassistant.const import CONF_PLATFORM
from tests.common import fire_time_changed from tests.common import fire_time_changed
@ -94,3 +95,133 @@ class TestAutomationTime(unittest.TestCase):
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))
def test_if_action_before(self):
automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'event',
event.CONF_EVENT_TYPE: 'test_event',
automation.CONF_SERVICE: 'test.automation',
automation.CONF_IF: {
CONF_PLATFORM: 'time',
time.CONF_BEFORE: '10:00'
}
}
})
before_10 = dt_util.now().replace(hour=8)
after_10 = dt_util.now().replace(hour=14)
with patch('homeassistant.components.automation.time.dt_util.now',
return_value=before_10):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
with patch('homeassistant.components.automation.time.dt_util.now',
return_value=after_10):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_action_after(self):
automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'event',
event.CONF_EVENT_TYPE: 'test_event',
automation.CONF_SERVICE: 'test.automation',
automation.CONF_IF: {
CONF_PLATFORM: 'time',
time.CONF_AFTER: '10:00'
}
}
})
before_10 = dt_util.now().replace(hour=8)
after_10 = dt_util.now().replace(hour=14)
with patch('homeassistant.components.automation.time.dt_util.now',
return_value=before_10):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(0, len(self.calls))
with patch('homeassistant.components.automation.time.dt_util.now',
return_value=after_10):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_action_one_weekday(self):
automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'event',
event.CONF_EVENT_TYPE: 'test_event',
automation.CONF_SERVICE: 'test.automation',
automation.CONF_IF: {
CONF_PLATFORM: 'time',
time.CONF_WEEKDAY: 'mon',
}
}
})
days_past_monday = dt_util.now().weekday()
monday = dt_util.now() - timedelta(days=days_past_monday)
tuesday = monday + timedelta(days=1)
with patch('homeassistant.components.automation.time.dt_util.now',
return_value=monday):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
with patch('homeassistant.components.automation.time.dt_util.now',
return_value=tuesday):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_action_list_weekday(self):
automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'event',
event.CONF_EVENT_TYPE: 'test_event',
automation.CONF_SERVICE: 'test.automation',
automation.CONF_IF: {
CONF_PLATFORM: 'time',
time.CONF_WEEKDAY: ['mon', 'tue'],
}
}
})
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.components.automation.time.dt_util.now',
return_value=monday):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
with patch('homeassistant.components.automation.time.dt_util.now',
return_value=tuesday):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(2, len(self.calls))
with patch('homeassistant.components.automation.time.dt_util.now',
return_value=wednesday):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(2, len(self.calls))