From 4371355be1c054170adef7f6940360cc460504ee Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 17 Sep 2015 23:12:55 -0700 Subject: [PATCH 01/19] Better errors on time automation trigger --- homeassistant/components/automation/time.py | 43 ++++++++++++--------- tests/components/automation/test_time.py | 32 +++++++++++++-- 2 files changed, 53 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/automation/time.py b/homeassistant/components/automation/time.py index 17b0c989f16..559832eee80 100644 --- a/homeassistant/components/automation/time.py +++ b/homeassistant/components/automation/time.py @@ -27,8 +27,7 @@ def trigger(hass, config, action): if CONF_AFTER in config: after = dt_util.parse_time_str(config[CONF_AFTER]) if after is None: - _LOGGER.error( - 'Received invalid after value: %s', config[CONF_AFTER]) + _error_time(config[CONF_AFTER], CONF_AFTER) return False hours, minutes, seconds = after.hour, after.minute, after.second elif (CONF_HOURS in config or CONF_MINUTES in config @@ -63,27 +62,27 @@ def if_action(hass, config): CONF_BEFORE, CONF_AFTER, CONF_WEEKDAY) return None + if before is not None: + before = dt_util.parse_time_str(before) + if before is None: + _error_time(before, CONF_BEFORE) + return None + + if after is not None: + after = dt_util.parse_time_str(after) + if after is None: + _error_time(after, CONF_AFTER) + return None + def time_if(): """ Validate time based if-condition """ now = dt_util.now() - if before is not None: - time = dt_util.parse_time_str(before) - if time is None: + if before is not None and now > now.replace(hour=before.hour, + minute=before.minute): return False - before_point = now.replace(hour=time.hour, minute=time.minute) - - if now > before_point: - return False - - if after is not None: - time = dt_util.parse_time_str(after) - if time is None: - return False - - after_point = now.replace(hour=time.hour, minute=time.minute) - - if now < after_point: + if after is not None and now < now.replace(hour=after.hour, + minute=after.minute): return False if weekday is not None: @@ -96,3 +95,11 @@ def if_action(hass, config): return True return time_if + + +def _error_time(value, key): + """ Helper method to print error. """ + _LOGGER.error( + "Received invalid value for '%s': %s", key, value) + if isinstance(value, int): + _LOGGER.error('Make sure you wrap time values in quotes') diff --git a/tests/components/automation/test_time.py b/tests/components/automation/test_time.py index f7187592c66..95997bfec42 100644 --- a/tests/components/automation/test_time.py +++ b/tests/components/automation/test_time.py @@ -285,9 +285,9 @@ class TestAutomationTime(unittest.TestCase): automation.DOMAIN: { 'trigger': { 'platform': 'time', - 'hours': 0, - 'minutes': 0, - 'seconds': 0, + 'hours': 1, + 'minutes': 2, + 'seconds': 3, }, 'action': { 'execute_service': 'test.automation' @@ -296,7 +296,7 @@ class TestAutomationTime(unittest.TestCase): })) fire_time_changed(self.hass, dt_util.utcnow().replace( - hour=0, minute=0, second=0)) + hour=1, minute=2, second=3)) self.hass.pool.block_till_done() self.assertEqual(1, len(self.calls)) @@ -320,6 +320,30 @@ class TestAutomationTime(unittest.TestCase): self.hass.pool.block_till_done() self.assertEqual(1, 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 + before rule. """ + self.assertTrue(automation.setup(self.hass, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'time', + 'after': 3605, + # Total seconds. Hour = 3600 second + }, + 'action': { + 'execute_service': 'test.automation' + } + } + })) + + fire_time_changed(self.hass, dt_util.utcnow().replace( + hour=1, minute=0, second=5)) + + self.hass.pool.block_till_done() + self.assertEqual(0, len(self.calls)) + self.assertEqual(2, mock_error.call_count) + def test_if_action_before(self): automation.setup(self.hass, { automation.DOMAIN: { From 6c1f44242c33af7eaa1fa7c369cd8f541f61cee3 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 17 Sep 2015 23:55:47 -0700 Subject: [PATCH 02/19] Update setup script --- script/setup | 1 + 1 file changed, 1 insertion(+) diff --git a/script/setup b/script/setup index 80c15646eaf..6d3a774dd54 100755 --- a/script/setup +++ b/script/setup @@ -2,3 +2,4 @@ cd "$(dirname "$0")/.." git submodule init script/bootstrap +python3 setup.py develop From 722af9014dcc0307efea18c443983af2aabca132 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Thu, 17 Sep 2015 10:46:57 +0200 Subject: [PATCH 03/19] Update import style --- homeassistant/components/sensor/swiss_public_transport.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/sensor/swiss_public_transport.py b/homeassistant/components/sensor/swiss_public_transport.py index 9f0b90d729f..5a07b585430 100644 --- a/homeassistant/components/sensor/swiss_public_transport.py +++ b/homeassistant/components/sensor/swiss_public_transport.py @@ -31,7 +31,7 @@ Details for the API : http://transport.opendata.ch """ import logging from datetime import timedelta -from requests import get +import requests from homeassistant.util import Throttle import homeassistant.util.dt as dt_util @@ -53,8 +53,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): try: for location in [config.get('from', None), config.get('to', None)]: # transport.opendata.ch doesn't play nice with requests.Session - result = get(_RESOURCE + 'locations?query=%s' % location, - timeout=10) + result = requests.get(_RESOURCE + 'locations?query=%s' % location, + timeout=10) journey.append(result.json()['stations'][0]['name']) except KeyError: _LOGGER.exception( @@ -110,7 +110,7 @@ class PublicTransportData(object): def update(self): """ Gets the latest data from opendata.ch. """ - response = get( + response = requests.get( _RESOURCE + 'connections?' + 'from=' + self.start + '&' + From b33714bca391341371c94598a1dcb6a5d28d45e4 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 18 Sep 2015 16:41:35 +0200 Subject: [PATCH 04/19] Fix default value for correction_factor (Thanks @luxus) --- homeassistant/components/sensor/command_sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/sensor/command_sensor.py b/homeassistant/components/sensor/command_sensor.py index 3a3199be627..d20b7c8a42a 100644 --- a/homeassistant/components/sensor/command_sensor.py +++ b/homeassistant/components/sensor/command_sensor.py @@ -70,7 +70,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): data, config.get('name', DEFAULT_NAME), config.get('unit_of_measurement'), - config.get('correction_factor', None), + config.get('correction_factor', 1.0), config.get('decimal_places', 0) )]) From e4c5108c9deb8ecdf296f7c8da5f9dc26789d930 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Fri, 18 Sep 2015 18:12:27 +0200 Subject: [PATCH 05/19] Implemented configuration loading from --- .../components/automation/__init__.py | 52 +++++++++++++------ 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index 45859617624..6243a2f50af 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -40,27 +40,47 @@ def setup(hass, config): found = 1 while config_key in config: - p_config = _migrate_old_config(config[config_key]) + # check for one block syntax + if isinstance(config[config_key], dict): + config_block = _migrate_old_config(config[config_key]) + name = config_block.get(CONF_ALIAS, config_key) + _setup_automation(hass, config_block, name, config) + + # check for multiple block syntax + elif isinstance(config[config_key], list): + list_no = 0 + for config_block in config[config_key]: + name = config_block.get(CONF_ALIAS, "{}, {}".format(config_key, list_no)) + list_no += 1 + config_block = _migrate_old_config(config_block) + _setup_automation(hass, config_block, name, config) + + # any scalar value is incorrect + else: + _LOGGER.error('Error in config in section %s.', config_key) + found += 1 config_key = "{} {}".format(DOMAIN, found) - name = p_config.get(CONF_ALIAS, config_key) - action = _get_action(hass, p_config.get(CONF_ACTION, {}), name) - - if action is None: - continue - - if CONF_CONDITION in p_config or CONF_CONDITION_TYPE in p_config: - action = _process_if(hass, config, p_config, action) - - if action is None: - continue - - _process_trigger(hass, config, p_config.get(CONF_TRIGGER, []), name, - action) - return True +def _setup_automation(hass, config_block, name, config): + """ Setup one instance of automation """ + + action = _get_action(hass, config_block.get(CONF_ACTION, {}), name) + + if action is None: + return False + + if CONF_CONDITION in p_config or CONF_CONDITION_TYPE in p_config: + action = _process_if(hass, config, config_block, action) + + if action is None: + return False + + _process_trigger(hass, config, config_block.get(CONF_TRIGGER, []), name, + action) + return True def _get_action(hass, config, name): """ Return an action based on a config. """ From 9d7aef94e07e76ab9d211fbda6ba0a366e2900a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Sandstr=C3=B6m?= Date: Fri, 18 Sep 2015 21:51:24 +0200 Subject: [PATCH 06/19] remove output in terminal after service is started --- script/hass-daemon | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/script/hass-daemon b/script/hass-daemon index d11c2669e87..2b396699543 100755 --- a/script/hass-daemon +++ b/script/hass-daemon @@ -34,6 +34,7 @@ RUN_AS="USER" PID_FILE="/var/run/hass.pid" CONFIG_DIR="/var/opt/homeassistant" FLAGS="-v --config $CONFIG_DIR --pid-file $PID_FILE --daemon" +REDIRECT="> $CONFIG_DIR/home-assistant.log 2>&1" start() { if [ -f $PID_FILE ] && kill -0 $(cat $PID_FILE); then @@ -41,7 +42,7 @@ start() { return 1 fi echo 'Starting service…' >&2 - local CMD="$PRE_EXEC hass $FLAGS;" + local CMD="$PRE_EXEC hass $FLAGS $REDIRECT;" su -c "$CMD" $RUN_AS echo 'Service started' >&2 } From a32229b4ce33807c770db3e8e50c9dff5657050f Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sat, 19 Sep 2015 11:46:22 +0200 Subject: [PATCH 07/19] Allow decimal numbers (Thanks @luxus) --- homeassistant/components/sensor/command_sensor.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/sensor/command_sensor.py b/homeassistant/components/sensor/command_sensor.py index d20b7c8a42a..a6e6c19fdb8 100644 --- a/homeassistant/components/sensor/command_sensor.py +++ b/homeassistant/components/sensor/command_sensor.py @@ -108,12 +108,15 @@ class CommandSensor(Entity): self.data.update() value = self.data.value - if value is not None: - if self._corr_factor is not None: - self._state = round((int(value) * self._corr_factor), - self._decimal_places) - else: - self._state = value + try: + if value is not None: + if self._corr_factor is not None: + self._state = round((float(value) * self._corr_factor), + self._decimal_places) + else: + self._state = value + except ValueError: + self._state = value # pylint: disable=too-few-public-methods From be9cfbdeb016017827eaa23c3c10725df13e9786 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Sat, 19 Sep 2015 14:45:56 +0200 Subject: [PATCH 08/19] Fixed docblock --- homeassistant/components/script.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/script.py b/homeassistant/components/script.py index 0195ed18d8a..c4f70b6d6d3 100644 --- a/homeassistant/components/script.py +++ b/homeassistant/components/script.py @@ -1,7 +1,7 @@ """ homeassistant.components.script ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - +entity_id Scripts are a sequence of actions that can be triggered manually by the user or automatically based upon automation events, etc. """ @@ -25,6 +25,7 @@ CONF_SEQUENCE = "sequence" CONF_EVENT = "event" CONF_EVENT_DATA = "event_data" CONF_DELAY = "delay" +ATTR_ENTITY_ID = "entity_id" _LOGGER = logging.getLogger(__name__) @@ -43,15 +44,22 @@ def setup(hass, config): hass.services.register(DOMAIN, name, script) scripts.append(script) + def _get_entities(service): + """ Make sure that we always get a list of entities """ + if isinstance(service.data[ATTR_ENTITY_ID], list): + return service.data[ATTR_ENTITY_ID] + else: + return [service.data[ATTR_ENTITY_ID]] + def turn_on(service): """ Calls a script. """ - for entity_id in service.data['entity_id']: + for entity_id in _get_entities(service): domain, service = split_entity_id(entity_id) hass.services.call(domain, service, {}) def turn_off(service): """ Cancels a script. """ - for entity_id in service.data['entity_id']: + for entity_id in _get_entities(service): for script in scripts: if script.entity_id == entity_id: script.cancel() From b9e1b3eb9967ce9e61e31b89e61a7fb4c45f7539 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Sat, 19 Sep 2015 15:51:50 +0200 Subject: [PATCH 09/19] Fixed var name + flake8 --- homeassistant/components/automation/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index 6243a2f50af..e22b02119d8 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -50,7 +50,8 @@ def setup(hass, config): elif isinstance(config[config_key], list): list_no = 0 for config_block in config[config_key]: - name = config_block.get(CONF_ALIAS, "{}, {}".format(config_key, list_no)) + name = config_block.get(CONF_ALIAS, + "{}, {}".format(config_key, list_no)) list_no += 1 config_block = _migrate_old_config(config_block) _setup_automation(hass, config_block, name, config) @@ -64,6 +65,7 @@ def setup(hass, config): return True + def _setup_automation(hass, config_block, name, config): """ Setup one instance of automation """ @@ -72,7 +74,7 @@ def _setup_automation(hass, config_block, name, config): if action is None: return False - if CONF_CONDITION in p_config or CONF_CONDITION_TYPE in p_config: + if CONF_CONDITION in config_block or CONF_CONDITION_TYPE in config_block: action = _process_if(hass, config, config_block, action) if action is None: @@ -82,6 +84,7 @@ def _setup_automation(hass, config_block, name, config): action) return True + def _get_action(hass, config, name): """ Return an action based on a config. """ From 9019d654d7fe1e40dc3998103530402b0d14d995 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 19 Sep 2015 08:27:34 -0700 Subject: [PATCH 10/19] Event automation fuzzy matches on data --- homeassistant/components/automation/event.py | 5 +++-- tests/components/automation/test_event.py | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/automation/event.py b/homeassistant/components/automation/event.py index 22be921f66a..c5b0ee47923 100644 --- a/homeassistant/components/automation/event.py +++ b/homeassistant/components/automation/event.py @@ -20,11 +20,12 @@ def trigger(hass, config, action): _LOGGER.error("Missing configuration key %s", CONF_EVENT_TYPE) return False - event_data = config.get(CONF_EVENT_DATA, {}) + event_data = config.get(CONF_EVENT_DATA) def handle_event(event): """ Listens for events and calls the action when data matches. """ - if event_data == event.data: + if not event_data or all(val == event.data.get(key) for key, val + in event_data.items()): action() hass.bus.listen(event_type, handle_event) diff --git a/tests/components/automation/test_event.py b/tests/components/automation/test_event.py index 01867f3850e..cfe8592da5c 100644 --- a/tests/components/automation/test_event.py +++ b/tests/components/automation/test_event.py @@ -98,7 +98,8 @@ class TestAutomationEvent(unittest.TestCase): } })) - self.hass.bus.fire('test_event', {'some_attr': 'some_value'}) + self.hass.bus.fire('test_event', {'some_attr': 'some_value', + 'another': 'value'}) self.hass.pool.block_till_done() self.assertEqual(1, len(self.calls)) From 2084976bc25f428e57f417e524d6fdf3bbde1d5f Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Sat, 19 Sep 2015 17:42:21 +0200 Subject: [PATCH 11/19] Fixed suggestions from @balloob --- homeassistant/components/automation/__init__.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index e22b02119d8..4384b6a1848 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -42,17 +42,14 @@ def setup(hass, config): while config_key in config: # check for one block syntax if isinstance(config[config_key], dict): - config_block = _migrate_old_config(config[config_key]) - name = config_block.get(CONF_ALIAS, config_key) - _setup_automation(hass, config_block, name, config) + name = config[config_key].get(CONF_ALIAS, config_key) + _setup_automation(hass, config[config_key], name, config) # check for multiple block syntax elif isinstance(config[config_key], list): - list_no = 0 - for config_block in config[config_key]: + for list_no, config_block in enumerate(config[config_key]): name = config_block.get(CONF_ALIAS, "{}, {}".format(config_key, list_no)) - list_no += 1 config_block = _migrate_old_config(config_block) _setup_automation(hass, config_block, name, config) From dd4e1cbd1d74aac52c6fe1c214d826c73d062f9f Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 19 Sep 2015 08:43:56 -0700 Subject: [PATCH 12/19] Change automation action config keys --- .../components/automation/__init__.py | 11 +++-- tests/components/automation/test_event.py | 6 +-- tests/components/automation/test_init.py | 44 +++++++++++++------ tests/components/automation/test_mqtt.py | 6 +-- .../automation/test_numeric_state.py | 24 +++++----- tests/components/automation/test_state.py | 18 ++++---- tests/components/automation/test_sun.py | 8 ++-- tests/components/automation/test_time.py | 42 +++++++++--------- 8 files changed, 90 insertions(+), 69 deletions(-) diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index 45859617624..9b3ae7894b1 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -16,9 +16,9 @@ DOMAIN = 'automation' DEPENDENCIES = ['group'] CONF_ALIAS = 'alias' -CONF_SERVICE = 'execute_service' -CONF_SERVICE_ENTITY_ID = 'service_entity_id' -CONF_SERVICE_DATA = 'service_data' +CONF_SERVICE = 'service' +CONF_SERVICE_ENTITY_ID = 'entity_id' +CONF_SERVICE_DATA = 'data' CONF_CONDITION = 'condition' CONF_ACTION = 'action' @@ -118,7 +118,10 @@ def _migrate_old_config(config): ('trigger', 'state_from', 'from'), ('trigger', 'state_hours', 'hours'), ('trigger', 'state_minutes', 'minutes'), - ('trigger', 'state_seconds', 'seconds')): + ('trigger', 'state_seconds', 'seconds'), + ('action', 'execute_service', 'service'), + ('action', 'service_entity_id', 'entity_id'), + ('action', 'service_data', 'data')): if key in new_conf[cat]: new_conf[cat][new_key] = new_conf[cat].pop(key) diff --git a/tests/components/automation/test_event.py b/tests/components/automation/test_event.py index 01867f3850e..a0f852ee2e2 100644 --- a/tests/components/automation/test_event.py +++ b/tests/components/automation/test_event.py @@ -75,7 +75,7 @@ class TestAutomationEvent(unittest.TestCase): 'event_type': 'test_event', }, 'action': { - 'execute_service': 'test.automation', + 'service': 'test.automation', } } })) @@ -93,7 +93,7 @@ class TestAutomationEvent(unittest.TestCase): 'event_data': {'some_attr': 'some_value'} }, 'action': { - 'execute_service': 'test.automation', + 'service': 'test.automation', } } })) @@ -111,7 +111,7 @@ class TestAutomationEvent(unittest.TestCase): 'event_data': {'some_attr': 'some_value'} }, 'action': { - 'execute_service': 'test.automation', + 'service': 'test.automation', } } })) diff --git a/tests/components/automation/test_init.py b/tests/components/automation/test_init.py index 6a011a072a5..2d5329b4624 100644 --- a/tests/components/automation/test_init.py +++ b/tests/components/automation/test_init.py @@ -10,7 +10,7 @@ import homeassistant.components.automation as automation from homeassistant.const import ATTR_ENTITY_ID -class TestAutomationEvent(unittest.TestCase): +class TestAutomation(unittest.TestCase): """ Test the event automation. """ def setUp(self): # pylint: disable=invalid-name @@ -26,7 +26,7 @@ class TestAutomationEvent(unittest.TestCase): """ Stop down stuff we started. """ self.hass.stop() - def test_service_data_not_a_dict(self): + def test_old_config_service_data_not_a_dict(self): automation.setup(self.hass, { automation.DOMAIN: { 'platform': 'event', @@ -87,6 +87,24 @@ class TestAutomationEvent(unittest.TestCase): self.assertEqual(['hello.world', 'hello.world2'], self.calls[0].data.get(ATTR_ENTITY_ID)) + def test_service_data_not_a_dict(self): + automation.setup(self.hass, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event', + }, + 'action': { + 'service': 'test.automation', + 'data': 100, + } + } + }) + + self.hass.bus.fire('test_event') + self.hass.pool.block_till_done() + self.assertEqual(1, len(self.calls)) + def test_service_specify_data(self): automation.setup(self.hass, { automation.DOMAIN: { @@ -95,8 +113,8 @@ class TestAutomationEvent(unittest.TestCase): 'event_type': 'test_event', }, 'action': { - 'execute_service': 'test.automation', - 'service_data': {'some': 'data'} + 'service': 'test.automation', + 'data': {'some': 'data'} } } }) @@ -114,8 +132,8 @@ class TestAutomationEvent(unittest.TestCase): 'event_type': 'test_event', }, 'action': { - 'execute_service': 'test.automation', - 'service_entity_id': 'hello.world' + 'service': 'test.automation', + 'entity_id': 'hello.world' } } }) @@ -134,8 +152,8 @@ class TestAutomationEvent(unittest.TestCase): 'event_type': 'test_event', }, 'action': { - 'execute_service': 'test.automation', - 'service_entity_id': ['hello.world', 'hello.world2'] + 'service': 'test.automation', + 'entity_id': ['hello.world', 'hello.world2'] } } }) @@ -160,7 +178,7 @@ class TestAutomationEvent(unittest.TestCase): } ], 'action': { - 'execute_service': 'test.automation', + 'service': 'test.automation', } } }) @@ -195,7 +213,7 @@ class TestAutomationEvent(unittest.TestCase): } ], 'action': { - 'execute_service': 'test.automation', + 'service': 'test.automation', } } }) @@ -239,7 +257,7 @@ class TestAutomationEvent(unittest.TestCase): } ], 'action': { - 'execute_service': 'test.automation', + 'service': 'test.automation', } } }) @@ -278,7 +296,7 @@ class TestAutomationEvent(unittest.TestCase): ], 'condition': 'use_trigger_values', 'action': { - 'execute_service': 'test.automation', + 'service': 'test.automation', } } }) @@ -314,7 +332,7 @@ class TestAutomationEvent(unittest.TestCase): ], 'condition': 'use_trigger_values', 'action': { - 'execute_service': 'test.automation', + 'service': 'test.automation', } } }) diff --git a/tests/components/automation/test_mqtt.py b/tests/components/automation/test_mqtt.py index d5e969abe5d..174ef91f1c4 100644 --- a/tests/components/automation/test_mqtt.py +++ b/tests/components/automation/test_mqtt.py @@ -77,7 +77,7 @@ class TestAutomationState(unittest.TestCase): 'topic': 'test-topic' }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -95,7 +95,7 @@ class TestAutomationState(unittest.TestCase): 'payload': 'hello' }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -113,7 +113,7 @@ class TestAutomationState(unittest.TestCase): 'payload': 'hello' }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) diff --git a/tests/components/automation/test_numeric_state.py b/tests/components/automation/test_numeric_state.py index e946c138a95..a04b8d01f4e 100644 --- a/tests/components/automation/test_numeric_state.py +++ b/tests/components/automation/test_numeric_state.py @@ -35,7 +35,7 @@ class TestAutomationNumericState(unittest.TestCase): 'below': 10, }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -56,7 +56,7 @@ class TestAutomationNumericState(unittest.TestCase): 'below': 10, }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -78,7 +78,7 @@ class TestAutomationNumericState(unittest.TestCase): 'below': 10, }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -97,7 +97,7 @@ class TestAutomationNumericState(unittest.TestCase): 'above': 10, }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -119,7 +119,7 @@ class TestAutomationNumericState(unittest.TestCase): 'above': 10, }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -142,7 +142,7 @@ class TestAutomationNumericState(unittest.TestCase): 'above': 10, }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -162,7 +162,7 @@ class TestAutomationNumericState(unittest.TestCase): 'above': 5, }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -181,7 +181,7 @@ class TestAutomationNumericState(unittest.TestCase): 'above': 5, }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -203,7 +203,7 @@ class TestAutomationNumericState(unittest.TestCase): 'above': 5, }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -226,7 +226,7 @@ class TestAutomationNumericState(unittest.TestCase): 'above': 5, }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -244,7 +244,7 @@ class TestAutomationNumericState(unittest.TestCase): 'entity_id': 'test.another_entity', }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -269,7 +269,7 @@ class TestAutomationNumericState(unittest.TestCase): 'below': test_state + 2 }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } }) diff --git a/tests/components/automation/test_state.py b/tests/components/automation/test_state.py index b0410c75014..a7c13e866c6 100644 --- a/tests/components/automation/test_state.py +++ b/tests/components/automation/test_state.py @@ -164,7 +164,7 @@ class TestAutomationState(unittest.TestCase): 'entity_id': 'test.entity', }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -182,7 +182,7 @@ class TestAutomationState(unittest.TestCase): 'from': 'hello' }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -200,7 +200,7 @@ class TestAutomationState(unittest.TestCase): 'to': 'world' }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -218,7 +218,7 @@ class TestAutomationState(unittest.TestCase): 'state': 'world' }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -237,7 +237,7 @@ class TestAutomationState(unittest.TestCase): 'to': 'world' }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -256,7 +256,7 @@ class TestAutomationState(unittest.TestCase): 'to': 'world' }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -277,7 +277,7 @@ class TestAutomationState(unittest.TestCase): 'to': 'world' }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -294,7 +294,7 @@ class TestAutomationState(unittest.TestCase): 'entity_id': 'test.anoter_entity', }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -318,7 +318,7 @@ class TestAutomationState(unittest.TestCase): 'state': test_state }], 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } }) diff --git a/tests/components/automation/test_sun.py b/tests/components/automation/test_sun.py index 4781c5be79b..de8b2f8121b 100644 --- a/tests/components/automation/test_sun.py +++ b/tests/components/automation/test_sun.py @@ -51,7 +51,7 @@ class TestAutomationSun(unittest.TestCase): 'event': 'sunset', }, 'action': { - 'execute_service': 'test.automation', + 'service': 'test.automation', } } })) @@ -77,7 +77,7 @@ class TestAutomationSun(unittest.TestCase): 'event': 'sunrise', }, 'action': { - 'execute_service': 'test.automation', + 'service': 'test.automation', } } })) @@ -104,7 +104,7 @@ class TestAutomationSun(unittest.TestCase): 'offset': '0:30:00' }, 'action': { - 'execute_service': 'test.automation', + 'service': 'test.automation', } } })) @@ -131,7 +131,7 @@ class TestAutomationSun(unittest.TestCase): 'offset': '-0:30:00' }, 'action': { - 'execute_service': 'test.automation', + 'service': 'test.automation', } } })) diff --git a/tests/components/automation/test_time.py b/tests/components/automation/test_time.py index 95997bfec42..e233c93988d 100644 --- a/tests/components/automation/test_time.py +++ b/tests/components/automation/test_time.py @@ -36,9 +36,9 @@ class TestAutomationTime(unittest.TestCase): def test_old_config_if_fires_when_hour_matches(self): self.assertTrue(automation.setup(self.hass, { automation.DOMAIN: { - CONF_PLATFORM: 'time', + 'platform': 'time', time.CONF_HOURS: 0, - automation.CONF_SERVICE: 'test.automation' + 'execute_service': 'test.automation' } })) @@ -51,9 +51,9 @@ class TestAutomationTime(unittest.TestCase): def test_old_config_if_fires_when_minute_matches(self): self.assertTrue(automation.setup(self.hass, { automation.DOMAIN: { - CONF_PLATFORM: 'time', + 'platform': 'time', time.CONF_MINUTES: 0, - automation.CONF_SERVICE: 'test.automation' + 'execute_service': 'test.automation' } })) @@ -66,9 +66,9 @@ class TestAutomationTime(unittest.TestCase): def test_old_config_if_fires_when_second_matches(self): self.assertTrue(automation.setup(self.hass, { automation.DOMAIN: { - CONF_PLATFORM: 'time', + 'platform': 'time', time.CONF_SECONDS: 0, - automation.CONF_SERVICE: 'test.automation' + 'execute_service': 'test.automation' } })) @@ -85,7 +85,7 @@ class TestAutomationTime(unittest.TestCase): time.CONF_HOURS: 0, time.CONF_MINUTES: 0, time.CONF_SECONDS: 0, - automation.CONF_SERVICE: 'test.automation' + 'execute_service': 'test.automation' } })) @@ -101,7 +101,7 @@ class TestAutomationTime(unittest.TestCase): automation.DOMAIN: { CONF_PLATFORM: 'event', event.CONF_EVENT_TYPE: 'test_event', - automation.CONF_SERVICE: 'test.automation', + 'execute_service': 'test.automation', 'if': { CONF_PLATFORM: 'time', time.CONF_BEFORE: '10:00' @@ -131,7 +131,7 @@ class TestAutomationTime(unittest.TestCase): automation.DOMAIN: { CONF_PLATFORM: 'event', event.CONF_EVENT_TYPE: 'test_event', - automation.CONF_SERVICE: 'test.automation', + 'execute_service': 'test.automation', 'if': { CONF_PLATFORM: 'time', time.CONF_AFTER: '10:00' @@ -161,7 +161,7 @@ class TestAutomationTime(unittest.TestCase): automation.DOMAIN: { CONF_PLATFORM: 'event', event.CONF_EVENT_TYPE: 'test_event', - automation.CONF_SERVICE: 'test.automation', + 'execute_service': 'test.automation', 'if': { CONF_PLATFORM: 'time', time.CONF_WEEKDAY: 'mon', @@ -192,7 +192,7 @@ class TestAutomationTime(unittest.TestCase): automation.DOMAIN: { CONF_PLATFORM: 'event', event.CONF_EVENT_TYPE: 'test_event', - automation.CONF_SERVICE: 'test.automation', + 'execute_service': 'test.automation', 'if': { CONF_PLATFORM: 'time', time.CONF_WEEKDAY: ['mon', 'tue'], @@ -234,7 +234,7 @@ class TestAutomationTime(unittest.TestCase): 'hours': 0, }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -252,7 +252,7 @@ class TestAutomationTime(unittest.TestCase): 'minutes': 0, }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -270,7 +270,7 @@ class TestAutomationTime(unittest.TestCase): 'seconds': 0, }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -290,7 +290,7 @@ class TestAutomationTime(unittest.TestCase): 'seconds': 3, }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -309,7 +309,7 @@ class TestAutomationTime(unittest.TestCase): 'after': '5:00:00', }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -332,7 +332,7 @@ class TestAutomationTime(unittest.TestCase): # Total seconds. Hour = 3600 second }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } })) @@ -356,7 +356,7 @@ class TestAutomationTime(unittest.TestCase): 'before': '10:00', }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } }) @@ -390,7 +390,7 @@ class TestAutomationTime(unittest.TestCase): 'after': '10:00', }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } }) @@ -424,7 +424,7 @@ class TestAutomationTime(unittest.TestCase): 'weekday': 'mon', }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } }) @@ -459,7 +459,7 @@ class TestAutomationTime(unittest.TestCase): 'weekday': ['mon', 'tue'], }, 'action': { - 'execute_service': 'test.automation' + 'service': 'test.automation' } } }) From 40651ef2bc5948f3702a7dbee3e597b96dadc5e8 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Sat, 19 Sep 2015 21:13:09 +0200 Subject: [PATCH 13/19] Fixed old config value conversion Added a new unit test for the config list mode --- .../components/automation/__init__.py | 6 +-- tests/components/automation/test_init.py | 38 +++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index 4384b6a1848..5e35188a33c 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -42,15 +42,15 @@ def setup(hass, config): while config_key in config: # check for one block syntax if isinstance(config[config_key], dict): - name = config[config_key].get(CONF_ALIAS, config_key) - _setup_automation(hass, config[config_key], name, config) + config_block = _migrate_old_config(config[config_key]) + name = config_block.get(CONF_ALIAS, config_key) + _setup_automation(hass, config_block, name, config) # check for multiple block syntax elif isinstance(config[config_key], list): for list_no, config_block in enumerate(config[config_key]): name = config_block.get(CONF_ALIAS, "{}, {}".format(config_key, list_no)) - config_block = _migrate_old_config(config_block) _setup_automation(hass, config_block, name, config) # any scalar value is incorrect diff --git a/tests/components/automation/test_init.py b/tests/components/automation/test_init.py index 6a011a072a5..1df59840aea 100644 --- a/tests/components/automation/test_init.py +++ b/tests/components/automation/test_init.py @@ -322,3 +322,41 @@ class TestAutomationEvent(unittest.TestCase): self.hass.bus.fire('test_event') self.hass.pool.block_till_done() self.assertEqual(1, len(self.calls)) + + def test_automation_list_setting(self): + """ Event is not a valid condition. Will it still work? """ + automation.setup(self.hass, { + automation.DOMAIN: [ + { + 'trigger': [ + { + 'platform': 'event', + 'event_type': 'test_event', + }, + + ], + 'action': { + 'execute_service': 'test.automation', + } + }, + { + 'trigger': [ + { + 'platform': 'event', + 'event_type': 'test_event_2', + }, + ], + 'action': { + 'execute_service': 'test.automation', + } + } + ] + }) + + self.hass.bus.fire('test_event') + self.hass.pool.block_till_done() + self.assertEqual(1, len(self.calls)) + + self.hass.bus.fire('test_event_2') + self.hass.pool.block_till_done() + self.assertEqual(2, len(self.calls)) From ec1d5e617ed2b47e625ebe166053665c7cd9d456 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 19 Sep 2015 12:29:23 -0700 Subject: [PATCH 14/19] Fix CI --- .gitignore | 7 +++---- homeassistant/components/automation/time.py | 4 ++-- .../frontend/www_static/home-assistant-polymer | 2 +- homeassistant/components/logbook.py | 3 +++ pytest.ini | 2 ++ script/cibuild | 5 +++++ script/lint | 11 ++++++++++- script/test | 13 +++++++++++-- 8 files changed, 37 insertions(+), 10 deletions(-) create mode 100644 pytest.ini diff --git a/.gitignore b/.gitignore index 881411c54ea..8935ffedc17 100644 --- a/.gitignore +++ b/.gitignore @@ -15,10 +15,6 @@ tests/config/home-assistant.log *.sublime-project *.sublime-workspace -# Hide code validator output -pep8.txt -pylint.txt - # Hide some OS X stuff .DS_Store .AppleDouble @@ -30,6 +26,9 @@ Icon .idea +# pytest +.cache + # GITHUB Proposed Python stuff: *.py[cod] diff --git a/homeassistant/components/automation/time.py b/homeassistant/components/automation/time.py index 559832eee80..1d97ccc135d 100644 --- a/homeassistant/components/automation/time.py +++ b/homeassistant/components/automation/time.py @@ -79,11 +79,11 @@ def if_action(hass, config): now = dt_util.now() if before is not None and now > now.replace(hour=before.hour, minute=before.minute): - return False + return False if after is not None and now < now.replace(hour=after.hour, minute=after.minute): - return False + return False if weekday is not None: now_weekday = WEEKDAYS[now.weekday()] diff --git a/homeassistant/components/frontend/www_static/home-assistant-polymer b/homeassistant/components/frontend/www_static/home-assistant-polymer index 63e039a221a..68f6c6ae5d3 160000 --- a/homeassistant/components/frontend/www_static/home-assistant-polymer +++ b/homeassistant/components/frontend/www_static/home-assistant-polymer @@ -1 +1 @@ -Subproject commit 63e039a221ae6771e0d7c6990d9a93b7cc22fc64 +Subproject commit 68f6c6ae5d37a1f0fcd1c36a8803581f9367ac5f diff --git a/homeassistant/components/logbook.py b/homeassistant/components/logbook.py index b653d0f76f2..3a2c1e137cb 100644 --- a/homeassistant/components/logbook.py +++ b/homeassistant/components/logbook.py @@ -134,6 +134,9 @@ def humanify(events): if event.event_type == EVENT_STATE_CHANGED: entity_id = event.data['entity_id'] + if entity_id is None: + continue + if entity_id.startswith('sensor.'): last_sensor_event[entity_id] = event diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 00000000000..5ee64771657 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +testpaths = tests diff --git a/script/cibuild b/script/cibuild index ade1b1d91c5..b0e0a31fb89 100755 --- a/script/cibuild +++ b/script/cibuild @@ -4,4 +4,9 @@ # designed to run on the continuous integration server. script/test coverage + +STATUS=$? + coveralls + +exit $STATUS diff --git a/script/lint b/script/lint index 120f364120f..05178a20ad8 100755 --- a/script/lint +++ b/script/lint @@ -3,7 +3,16 @@ cd "$(dirname "$0")/.." echo "Checking style with flake8..." -flake8 homeassistant +flake8 --exclude www_static homeassistant + +STATUS=$? echo "Checking style with pylint..." pylint homeassistant + +if [ $STATUS -eq 0 ] +then + exit $? +else + exit $STATUS +fi diff --git a/script/test b/script/test index 56fe4dcec89..2e13bcc4b0e 100755 --- a/script/test +++ b/script/test @@ -7,10 +7,19 @@ cd "$(dirname "$0")/.." script/lint +STATUS=$? + echo "Running tests..." if [ "$1" = "coverage" ]; then - py.test --cov homeassistant tests + py.test --cov --cov-report= else - py.test tests + py.test +fi + +if [ $STATUS -eq 0 ] +then + exit $? +else + exit $STATUS fi From 1771f8b1b3d3a89c13c9b6992b5ecea32d8ac3cb Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 19 Sep 2015 13:13:28 -0700 Subject: [PATCH 15/19] Fix logbook crashing on custom state_changed events --- homeassistant/components/logbook.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/logbook.py b/homeassistant/components/logbook.py index 3a2c1e137cb..45ee7a2e319 100644 --- a/homeassistant/components/logbook.py +++ b/homeassistant/components/logbook.py @@ -132,7 +132,7 @@ def humanify(events): # Process events for event in events_batch: if event.event_type == EVENT_STATE_CHANGED: - entity_id = event.data['entity_id'] + entity_id = event.data.get('entity_id') if entity_id is None: continue From 49ce85f2e464c53deaa9b3641d6482e3b4e7241a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Sandstr=C3=B6m?= Date: Sat, 19 Sep 2015 22:45:30 +0200 Subject: [PATCH 16/19] fixed restart --- script/hass-daemon | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/script/hass-daemon b/script/hass-daemon index 2b396699543..bb14ce7f0a6 100755 --- a/script/hass-daemon +++ b/script/hass-daemon @@ -37,7 +37,7 @@ FLAGS="-v --config $CONFIG_DIR --pid-file $PID_FILE --daemon" REDIRECT="> $CONFIG_DIR/home-assistant.log 2>&1" start() { - if [ -f $PID_FILE ] && kill -0 $(cat $PID_FILE); then + if [ -f $PID_FILE ] && kill -0 $(cat $PID_FILE) 2> /dev/null; then echo 'Service already running' >&2 return 1 fi @@ -48,12 +48,13 @@ start() { } stop() { - if [ ! -f "$PID_FILE" ] || ! kill -0 $(cat "$PID_FILE"); then + if [ ! -f "$PID_FILE" ] || ! kill -0 $(cat "$PID_FILE") 2> /dev/null; then echo 'Service not running' >&2 return 1 fi echo 'Stopping service…' >&2 kill -3 $(cat "$PID_FILE") + while ps -p $(cat "$PID_FILE") > /dev/null 2>&1; do sleep 1;done; echo 'Service stopped' >&2 } From 720e5876a7e537a79ffb3aa6702248509bc8c2b2 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 19 Sep 2015 21:02:28 -0700 Subject: [PATCH 17/19] Fix broken automation test --- tests/components/automation/test_init.py | 43 ++++++++++-------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/tests/components/automation/test_init.py b/tests/components/automation/test_init.py index 272a9cb00f5..3f6b0dab6f1 100644 --- a/tests/components/automation/test_init.py +++ b/tests/components/automation/test_init.py @@ -343,33 +343,26 @@ class TestAutomation(unittest.TestCase): def test_automation_list_setting(self): """ Event is not a valid condition. Will it still work? """ - automation.setup(self.hass, { - automation.DOMAIN: [ - { - 'trigger': [ - { - 'platform': 'event', - 'event_type': 'test_event', - }, - - ], - 'action': { - 'execute_service': 'test.automation', - } + self.assertTrue(automation.setup(self.hass, { + automation.DOMAIN: [{ + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event', }, - { - 'trigger': [ - { - 'platform': 'event', - 'event_type': 'test_event_2', - }, - ], - 'action': { - 'execute_service': 'test.automation', - } + + 'action': { + 'service': 'test.automation', } - ] - }) + }, { + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event_2', + }, + 'action': { + 'service': 'test.automation', + } + }] + })) self.hass.bus.fire('test_event') self.hass.pool.block_till_done() From 2a3b911d7b6e8a7f2ddfd6927f18afbef03a59a9 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 19 Sep 2015 21:02:38 -0700 Subject: [PATCH 18/19] Remove debug statement --- homeassistant/components/history.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/homeassistant/components/history.py b/homeassistant/components/history.py index 01f75eabb5a..a723f9cbd71 100644 --- a/homeassistant/components/history.py +++ b/homeassistant/components/history.py @@ -147,8 +147,6 @@ def _api_history_period(handler, path_match, data): end_time = start_time + one_day - print("Fetching", start_time, end_time) - entity_id = data.get('filter_entity_id') handler.write_json( From 6352f10d9e0d7ddc428cf6da73cb839d3332fed1 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 19 Sep 2015 21:02:54 -0700 Subject: [PATCH 19/19] Device tracker minor tweak --- homeassistant/components/device_tracker/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index d33d182dd2c..97c3d769715 100644 --- a/homeassistant/components/device_tracker/__init__.py +++ b/homeassistant/components/device_tracker/__init__.py @@ -352,7 +352,7 @@ def load_config(path, hass, consider_home): Device(hass, consider_home, device.get('track', False), str(dev_id).lower(), str(device.get('mac')).upper(), device.get('name'), device.get('picture'), - device.get(CONF_AWAY_HIDE, False)) + device.get(CONF_AWAY_HIDE, DEFAULT_AWAY_HIDE)) for dev_id, device in load_yaml_config_file(path).items()]