From 36785296ce0fcc2ce2b00e7b1ce52351652e806d Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Sun, 11 Sep 2016 03:07:13 -0400 Subject: [PATCH 1/5] Fixed voluptuous to accept string instead positive_int for CODE on Simplisafe (#3310) --- homeassistant/components/alarm_control_panel/simplisafe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/alarm_control_panel/simplisafe.py b/homeassistant/components/alarm_control_panel/simplisafe.py index 82927246ec6..38128489ba0 100644 --- a/homeassistant/components/alarm_control_panel/simplisafe.py +++ b/homeassistant/components/alarm_control_panel/simplisafe.py @@ -26,7 +26,7 @@ DEFAULT_NAME = 'SimpliSafe' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_PASSWORD): cv.string, vol.Required(CONF_USERNAME): cv.string, - vol.Optional(CONF_CODE): cv.positive_int, + vol.Optional(CONF_CODE): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, }) From 7bce8bc33f5db48f87f23ff28b08da5f7bc20c6f Mon Sep 17 00:00:00 2001 From: John Arild Berentsen Date: Mon, 12 Sep 2016 06:46:14 +0200 Subject: [PATCH 2/5] Revert only add 1 device (#3324) --- homeassistant/components/climate/zwave.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/homeassistant/components/climate/zwave.py b/homeassistant/components/climate/zwave.py index 0ba85105c18..6c08bf391d8 100755 --- a/homeassistant/components/climate/zwave.py +++ b/homeassistant/components/climate/zwave.py @@ -70,8 +70,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None): node = zwave.NETWORK.nodes[discovery_info[ATTR_NODE_ID]] value = node.values[discovery_info[ATTR_VALUE_ID]] value.set_change_verified(False) - if value.index != 1: # Only add 1 device - return add_devices([ZWaveClimate(value, temp_unit)]) _LOGGER.debug("discovery_info=%s and zwave.NETWORK=%s", discovery_info, zwave.NETWORK) From d6ca93042784aea56630ee09661bed27bd493b2a Mon Sep 17 00:00:00 2001 From: Teagan Glenn Date: Sun, 11 Sep 2016 22:53:05 -0600 Subject: [PATCH 3/5] Automatic Device Tracker Bug Fix (#3330) * Iterate over items * Pass display name as host name --- homeassistant/components/device_tracker/__init__.py | 2 +- homeassistant/components/device_tracker/automatic.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index 4247213087b..236fde6fb3f 100644 --- a/homeassistant/components/device_tracker/__init__.py +++ b/homeassistant/components/device_tracker/__init__.py @@ -338,7 +338,7 @@ class Device(Entity): attr[ATTR_BATTERY] = self.battery if self.attributes: - for key, value in self.attributes: + for key, value in self.attributes.items(): attr[key] = value return attr diff --git a/homeassistant/components/device_tracker/automatic.py b/homeassistant/components/device_tracker/automatic.py index 927c515b3a5..7855323ba06 100644 --- a/homeassistant/components/device_tracker/automatic.py +++ b/homeassistant/components/device_tracker/automatic.py @@ -142,6 +142,7 @@ class AutomaticDeviceScanner(object): for vehicle in self.last_results: dev_id = vehicle.get('id') + host_name = vehicle.get('display_name') attrs = { 'fuel_level': vehicle.get('fuel_level_percent') @@ -149,6 +150,7 @@ class AutomaticDeviceScanner(object): kwargs = { 'dev_id': dev_id, + 'host_name': host_name, 'mac': dev_id, ATTR_ATTRIBUTES: attrs } From f0ec51711c85f94753577ffaeb79cf849da06cdc Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 11 Sep 2016 22:25:01 -0700 Subject: [PATCH 4/5] Bugfix group order (#3323) * Add ordered dict config validator * Have group component use ordered dict config validator * Improve config_validation testing * update doc string config_validation.ordered_dict * validate full dict entries * Further simplify ordered_dict validator. * Lint fix --- homeassistant/components/group.py | 4 +- homeassistant/helpers/config_validation.py | 22 ++++++++++ tests/components/test_group.py | 17 +++++--- tests/helpers/test_config_validation.py | 50 ++++++++++++++++++++++ 4 files changed, 84 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/group.py b/homeassistant/components/group.py index c4cd177925d..41901d87e86 100644 --- a/homeassistant/components/group.py +++ b/homeassistant/components/group.py @@ -46,12 +46,12 @@ def _conf_preprocess(value): CONFIG_SCHEMA = vol.Schema({ - DOMAIN: {cv.match_all: vol.Schema(vol.All(_conf_preprocess, { + DOMAIN: cv.ordered_dict(vol.All(_conf_preprocess, { vol.Optional(CONF_ENTITIES): vol.Any(cv.entity_ids, None), CONF_VIEW: cv.boolean, CONF_NAME: cv.string, CONF_ICON: cv.icon, - }))} + }, cv.match_all)) }, extra=vol.ALLOW_EXTRA) # List of ON/OFF state tuples for groupable states diff --git a/homeassistant/helpers/config_validation.py b/homeassistant/helpers/config_validation.py index 1be157c789d..009736024a1 100644 --- a/homeassistant/helpers/config_validation.py +++ b/homeassistant/helpers/config_validation.py @@ -1,4 +1,5 @@ """Helpers for config validation using voluptuous.""" +from collections import OrderedDict from datetime import timedelta import os from urllib.parse import urlparse @@ -290,6 +291,27 @@ def url(value: Any) -> str: raise vol.Invalid('invalid url') +def ordered_dict(value_validator, key_validator=match_all): + """Validate an ordered dict validator that maintains ordering. + + value_validator will be applied to each value of the dictionary. + key_validator (optional) will be applied to each key of the dictionary. + """ + item_validator = vol.Schema({key_validator: value_validator}) + + def validator(value): + """Validate ordered dict.""" + config = OrderedDict() + + for key, val in value.items(): + v_res = item_validator({key: val}) + config.update(v_res) + + return config + + return validator + + # Validator helpers def key_dependency(key, dependency): diff --git a/tests/components/test_group.py b/tests/components/test_group.py index e82190a3f29..6c601a411fb 100644 --- a/tests/components/test_group.py +++ b/tests/components/test_group.py @@ -1,5 +1,6 @@ """The tests for the Group components.""" # pylint: disable=protected-access,too-many-public-methods +from collections import OrderedDict import unittest from unittest.mock import patch @@ -220,16 +221,16 @@ class TestComponentsGroup(unittest.TestCase): test_group = group.Group( self.hass, 'init_group', ['light.Bowl', 'light.Ceiling'], False) - _setup_component(self.hass, 'group', {'group': { - 'second_group': { + group_conf = OrderedDict() + group_conf['second_group'] = { 'entities': 'light.Bowl, ' + test_group.entity_id, 'icon': 'mdi:work', 'view': True, - }, - 'test_group': 'hello.world,sensor.happy', - 'empty_group': {'name': 'Empty Group', 'entities': None}, - } - }) + } + group_conf['test_group'] = 'hello.world,sensor.happy' + group_conf['empty_group'] = {'name': 'Empty Group', 'entities': None} + + _setup_component(self.hass, 'group', {'group': group_conf}) group_state = self.hass.states.get( group.ENTITY_ID_FORMAT.format('second_group')) @@ -241,6 +242,7 @@ class TestComponentsGroup(unittest.TestCase): group_state.attributes.get(ATTR_ICON)) self.assertTrue(group_state.attributes.get(group.ATTR_VIEW)) self.assertTrue(group_state.attributes.get(ATTR_HIDDEN)) + self.assertEqual(1, group_state.attributes.get(group.ATTR_ORDER)) group_state = self.hass.states.get( group.ENTITY_ID_FORMAT.format('test_group')) @@ -251,6 +253,7 @@ class TestComponentsGroup(unittest.TestCase): self.assertIsNone(group_state.attributes.get(ATTR_ICON)) self.assertIsNone(group_state.attributes.get(group.ATTR_VIEW)) self.assertIsNone(group_state.attributes.get(ATTR_HIDDEN)) + self.assertEqual(2, group_state.attributes.get(group.ATTR_ORDER)) def test_groups_get_unique_names(self): """Two groups with same name should both have a unique entity id.""" diff --git a/tests/helpers/test_config_validation.py b/tests/helpers/test_config_validation.py index d9da2c51da7..60b14757378 100644 --- a/tests/helpers/test_config_validation.py +++ b/tests/helpers/test_config_validation.py @@ -1,3 +1,5 @@ +"""Test config validators.""" +from collections import OrderedDict from datetime import timedelta import os import tempfile @@ -367,3 +369,51 @@ def test_has_at_least_one_key(): for value in ({'beer': None}, {'soda': None}): schema(value) + + +def test_ordered_dict_order(): + """Test ordered_dict validator.""" + schema = vol.Schema(cv.ordered_dict(int, cv.string)) + + val = OrderedDict() + val['first'] = 1 + val['second'] = 2 + + validated = schema(val) + + assert isinstance(validated, OrderedDict) + assert ['first', 'second'] == list(validated.keys()) + + +def test_ordered_dict_key_validator(): + """Test ordered_dict key validator.""" + schema = vol.Schema(cv.ordered_dict(cv.match_all, cv.string)) + + with pytest.raises(vol.Invalid): + schema({None: 1}) + + schema({'hello': 'world'}) + + schema = vol.Schema(cv.ordered_dict(cv.match_all, int)) + + with pytest.raises(vol.Invalid): + schema({'hello': 1}) + + schema({1: 'works'}) + + +def test_ordered_dict_value_validator(): + """Test ordered_dict validator.""" + schema = vol.Schema(cv.ordered_dict(cv.string)) + + with pytest.raises(vol.Invalid): + schema({'hello': None}) + + schema({'hello': 'world'}) + + schema = vol.Schema(cv.ordered_dict(int)) + + with pytest.raises(vol.Invalid): + schema({'hello': 'world'}) + + schema({'hello': 5}) From 0cfa5e5f674d7a67ed4ee40d2e4442efc18fee0c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 11 Sep 2016 22:28:53 -0700 Subject: [PATCH 5/5] Version bump to 0.28.1 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index eb8b65df998..797fd3108b9 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 28 -PATCH_VERSION = '0' +PATCH_VERSION = '1' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 4)