diff --git a/.gitignore b/.gitignore index 63bb2a9e37b..1e79f07b663 100644 --- a/.gitignore +++ b/.gitignore @@ -77,6 +77,7 @@ nosetests.xml # venv stuff pyvenv.cfg pip-selfcheck.json +venv # vimmy stuff *.swp diff --git a/homeassistant/components/automation/time.py b/homeassistant/components/automation/time.py index e8cf9c3b6ee..d02765f75c6 100644 --- a/homeassistant/components/automation/time.py +++ b/homeassistant/components/automation/time.py @@ -8,7 +8,6 @@ at https://home-assistant.io/components/automation/#time-trigger """ import logging -from homeassistant.util import convert import homeassistant.util.dt as dt_util from homeassistant.helpers.event import track_time_change @@ -34,9 +33,9 @@ def trigger(hass, config, action): hours, minutes, seconds = after.hour, after.minute, after.second elif (CONF_HOURS in config or CONF_MINUTES in config or CONF_SECONDS in config): - hours = convert(config.get(CONF_HOURS), int) - minutes = convert(config.get(CONF_MINUTES), int) - seconds = convert(config.get(CONF_SECONDS), int) + hours = config.get(CONF_HOURS) + minutes = config.get(CONF_MINUTES) + seconds = config.get(CONF_SECONDS) else: _LOGGER.error('One of %s, %s, %s OR %s needs to be specified', CONF_HOURS, CONF_MINUTES, CONF_SECONDS, CONF_AFTER) @@ -59,7 +58,7 @@ def if_action(hass, config): weekday = config.get(CONF_WEEKDAY) if before is None and after is None and weekday is None: - logging.getLogger(__name__).error( + _LOGGER.error( "Missing if-condition configuration key %s, %s or %s", CONF_BEFORE, CONF_AFTER, CONF_WEEKDAY) return None diff --git a/homeassistant/helpers/event.py b/homeassistant/helpers/event.py index 42725b8eea9..0f0deac58b1 100644 --- a/homeassistant/helpers/event.py +++ b/homeassistant/helpers/event.py @@ -170,7 +170,6 @@ def track_utc_time_change(hass, action, year=None, month=None, day=None, if local: now = dt_util.as_local(now) - mat = _matcher # pylint: disable=too-many-boolean-expressions @@ -199,6 +198,8 @@ def _process_match_param(parameter): """ Wraps parameter in a tuple if it is not one and returns it. """ if parameter is None or parameter == MATCH_ALL: return MATCH_ALL + elif isinstance(parameter, str) and parameter.startswith('/'): + return parameter elif isinstance(parameter, str) or not hasattr(parameter, '__iter__'): return (parameter,) else: @@ -210,4 +211,10 @@ def _matcher(subject, pattern): Pattern is either a tuple of allowed subjects or a `MATCH_ALL`. """ + if isinstance(pattern, str) and pattern.startswith('/'): + try: + return subject % float(pattern.lstrip('/')) == 0 + except ValueError: + return False + return MATCH_ALL == pattern or subject in pattern diff --git a/tests/components/automation/test_time.py b/tests/components/automation/test_time.py index e233c93988d..dd3f5a6e9fa 100644 --- a/tests/components/automation/test_time.py +++ b/tests/components/automation/test_time.py @@ -301,6 +301,63 @@ class TestAutomationTime(unittest.TestCase): self.hass.pool.block_till_done() self.assertEqual(1, len(self.calls)) + def test_if_fires_periodic_seconds(self): + self.assertTrue(automation.setup(self.hass, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'time', + 'seconds': "/2", + }, + 'action': { + 'service': 'test.automation' + } + } + })) + + fire_time_changed(self.hass, dt_util.utcnow().replace( + hour=0, minute=0, second=2)) + + self.hass.pool.block_till_done() + self.assertEqual(1, len(self.calls)) + + def test_if_fires_periodic_minutes(self): + self.assertTrue(automation.setup(self.hass, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'time', + 'minutes': "/2", + }, + 'action': { + 'service': 'test.automation' + } + } + })) + + fire_time_changed(self.hass, dt_util.utcnow().replace( + hour=0, minute=2, second=0)) + + self.hass.pool.block_till_done() + self.assertEqual(1, len(self.calls)) + + def test_if_fires_periodic_hours(self): + self.assertTrue(automation.setup(self.hass, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'time', + 'hours': "/2", + }, + 'action': { + 'service': 'test.automation' + } + } + })) + + fire_time_changed(self.hass, dt_util.utcnow().replace( + hour=2, minute=0, second=0)) + + self.hass.pool.block_till_done() + self.assertEqual(1, len(self.calls)) + def test_if_fires_using_after(self): self.assertTrue(automation.setup(self.hass, { automation.DOMAIN: { @@ -320,6 +377,24 @@ class TestAutomationTime(unittest.TestCase): self.hass.pool.block_till_done() self.assertEqual(1, len(self.calls)) + def test_if_not_working_if_no_values_in_conf_provided(self): + self.assertTrue(automation.setup(self.hass, { + 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.pool.block_till_done() + self.assertEqual(0, len(self.calls)) + @patch('homeassistant.components.automation.time._LOGGER.error') def test_if_not_fires_using_wrong_after(self, mock_error): """ YAML translates time values to total seconds. This should break the diff --git a/tests/helpers/test_event.py b/tests/helpers/test_event.py index e12ca0c4124..aef7b834016 100644 --- a/tests/helpers/test_event.py +++ b/tests/helpers/test_event.py @@ -219,3 +219,93 @@ class TestEventHelpers(unittest.TestCase): def _send_time_changed(self, now): """ Send a time changed event. """ self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {ha.ATTR_NOW: now}) + + def test_periodic_task_minute(self): + specific_runs = [] + + track_utc_time_change( + self.hass, lambda x: specific_runs.append(1), minute='/5') + + self._send_time_changed(datetime(2014, 5, 24, 12, 0, 0)) + self.hass.pool.block_till_done() + self.assertEqual(1, len(specific_runs)) + + self._send_time_changed(datetime(2014, 5, 24, 12, 3, 0)) + self.hass.pool.block_till_done() + self.assertEqual(1, len(specific_runs)) + + self._send_time_changed(datetime(2014, 5, 24, 12, 5, 0)) + self.hass.pool.block_till_done() + self.assertEqual(2, len(specific_runs)) + + def test_periodic_task_hour(self): + specific_runs = [] + + track_utc_time_change( + self.hass, lambda x: specific_runs.append(1), hour='/2') + + self._send_time_changed(datetime(2014, 5, 24, 22, 0, 0)) + self.hass.pool.block_till_done() + self.assertEqual(1, len(specific_runs)) + + self._send_time_changed(datetime(2014, 5, 24, 23, 0, 0)) + self.hass.pool.block_till_done() + self.assertEqual(1, len(specific_runs)) + + self._send_time_changed(datetime(2014, 5, 24, 0, 0, 0)) + self.hass.pool.block_till_done() + self.assertEqual(2, len(specific_runs)) + + self._send_time_changed(datetime(2014, 5, 25, 1, 0, 0)) + self.hass.pool.block_till_done() + self.assertEqual(2, len(specific_runs)) + + self._send_time_changed(datetime(2014, 5, 25, 2, 0, 0)) + self.hass.pool.block_till_done() + self.assertEqual(3, len(specific_runs)) + + def test_periodic_task_day(self): + specific_runs = [] + + track_utc_time_change( + self.hass, lambda x: specific_runs.append(1), day='/2') + + self._send_time_changed(datetime(2014, 5, 2, 0, 0, 0)) + self.hass.pool.block_till_done() + self.assertEqual(1, len(specific_runs)) + + self._send_time_changed(datetime(2014, 5, 3, 12, 0, 0)) + self.hass.pool.block_till_done() + self.assertEqual(1, len(specific_runs)) + + self._send_time_changed(datetime(2014, 5, 4, 0, 0, 0)) + self.hass.pool.block_till_done() + self.assertEqual(2, len(specific_runs)) + + def test_periodic_task_year(self): + specific_runs = [] + + track_utc_time_change( + self.hass, lambda x: specific_runs.append(1), year='/2') + + self._send_time_changed(datetime(2014, 5, 2, 0, 0, 0)) + self.hass.pool.block_till_done() + self.assertEqual(1, len(specific_runs)) + + self._send_time_changed(datetime(2015, 5, 2, 0, 0, 0)) + self.hass.pool.block_till_done() + self.assertEqual(1, len(specific_runs)) + + self._send_time_changed(datetime(2016, 5, 2, 0, 0, 0)) + self.hass.pool.block_till_done() + self.assertEqual(2, len(specific_runs)) + + def test_periodic_task_wrong_input(self): + specific_runs = [] + + track_utc_time_change( + self.hass, lambda x: specific_runs.append(1), year='/two') + + self._send_time_changed(datetime(2014, 5, 2, 0, 0, 0)) + self.hass.pool.block_till_done() + self.assertEqual(0, len(specific_runs))