Add if to automation

pull/364/head
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.helpers import config_per_platform
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"
@ -19,6 +19,7 @@ CONF_ALIAS = "alias"
CONF_SERVICE = "execute_service"
CONF_SERVICE_ENTITY_ID = "service_entity_id"
CONF_SERVICE_DATA = "service_data"
CONF_IF = "if"
_LOGGER = logging.getLogger(__name__)
@ -34,7 +35,12 @@ def setup(hass, config):
_LOGGER.error("Unknown automation platform specified: %s", p_type)
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(
"Initialized %s rule %s", p_type, p_config.get(CONF_ALIAS, ""))
success = True
@ -72,3 +78,28 @@ def _get_action(hass, config):
hass.services.call(domain, service, service_data)
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__)
def register(hass, config, action):
def trigger(hass, config, action):
""" Listen for events based on config. """
event_type = config.get(CONF_EVENT_TYPE)

View File

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

View File

@ -16,7 +16,7 @@ CONF_ABOVE = "state_above"
_LOGGER = logging.getLogger(__name__)
def register(hass, config, action):
def trigger(hass, config, action):
""" Listen for state changes based on `config`. """
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_FROM = "state_from"
CONF_TO = "state_to"
CONF_STATE = "state"
def register(hass, config, action):
def trigger(hass, config, action):
""" Listen for state changes based on `config`. """
entity_id = config.get(CONF_ENTITY_ID)
if entity_id is None:
logging.getLogger(__name__).error(
"Missing configuration key %s", CONF_ENTITY_ID)
"Missing trigger configuration key %s", CONF_ENTITY_ID)
return False
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)
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.
"""
import logging
from homeassistant.util import convert
import homeassistant.util.dt as dt_util
from homeassistant.helpers.event import track_time_change
CONF_HOURS = "time_hours"
CONF_MINUTES = "time_minutes"
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`. """
hours = convert(config.get(CONF_HOURS), int)
minutes = convert(config.get(CONF_MINUTES), int)
@ -26,3 +34,49 @@ def register(hass, config, action):
hour=hours, minute=minutes, second=seconds)
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.components.automation as automation
import homeassistant.components.automation.state as state
from homeassistant.components.automation import event, state
from homeassistant.const import CONF_PLATFORM
@ -137,3 +137,31 @@ class TestAutomationState(unittest.TestCase):
self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done()
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.
"""
from datetime import timedelta
import unittest
from unittest.mock import patch
import homeassistant.core as ha
import homeassistant.loader as loader
import homeassistant.util.dt as dt_util
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 tests.common import fire_time_changed
@ -94,3 +95,133 @@ class TestAutomationTime(unittest.TestCase):
self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done()
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))