From 6e458114f40b87b41b1789370c2b9f4e1348cbd5 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 9 Sep 2015 19:37:44 -0700 Subject: [PATCH 01/10] Bump version 0.7.2 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 539043bf61e..e5a44ff7b55 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,6 +1,6 @@ """ Constants used by Home Assistant components. """ -__version__ = "0.7.2rc0" +__version__ = "0.7.2" # Can be used to specify a catch all when registering state or event listeners. MATCH_ALL = '*' From 4d53fa01738f0fdcabb1158e6bfc1fc5b127f89f Mon Sep 17 00:00:00 2001 From: pavoni Date: Fri, 18 Sep 2015 12:21:08 +0100 Subject: [PATCH 02/10] First draft of read_after_set for scenes --- homeassistant/components/light/hue.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/light/hue.py b/homeassistant/components/light/hue.py index b438d7b92b1..be993695e13 100644 --- a/homeassistant/components/light/hue.py +++ b/homeassistant/components/light/hue.py @@ -205,6 +205,7 @@ class HueLight(Light): command['effect'] = 'none' self.bridge.set_light(self.light_id, command) + self.update() def turn_off(self, **kwargs): """ Turn the specified or all lights off. """ From 3a3374ed4b4eb93f30e33bd4c46a9d9a7e4f6968 Mon Sep 17 00:00:00 2001 From: pavoni Date: Fri, 18 Sep 2015 12:34:02 +0100 Subject: [PATCH 03/10] Remove incorrect change --- homeassistant/components/light/hue.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/light/hue.py b/homeassistant/components/light/hue.py index be993695e13..b438d7b92b1 100644 --- a/homeassistant/components/light/hue.py +++ b/homeassistant/components/light/hue.py @@ -205,7 +205,6 @@ class HueLight(Light): command['effect'] = 'none' self.bridge.set_light(self.light_id, command) - self.update() def turn_off(self, **kwargs): """ Turn the specified or all lights off. """ From ab79b8a5413f7ae5bed8deb36e18d9afad449aec Mon Sep 17 00:00:00 2001 From: pavoni Date: Fri, 18 Sep 2015 12:34:24 +0100 Subject: [PATCH 04/10] First cut of write after set for scenes --- homeassistant/components/scene.py | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/scene.py b/homeassistant/components/scene.py index 579ce1f20fb..0d19c77ae98 100644 --- a/homeassistant/components/scene.py +++ b/homeassistant/components/scene.py @@ -33,7 +33,7 @@ ATTR_ACTIVE_REQUESTED = "active_requested" CONF_ENTITIES = "entities" -SceneConfig = namedtuple('SceneConfig', ['name', 'states']) +SceneConfig = namedtuple('SceneConfig', ['name', 'states', 'write_after_set']) def setup(hass, config): @@ -71,6 +71,7 @@ def setup(hass, config): def _process_config(scene_config): """ Process passed in config into a format to work with. """ name = scene_config.get('name') + write_after_set = scene_config.get('write_after_set') states = {} c_entities = dict(scene_config.get(CONF_ENTITIES, {})) @@ -91,7 +92,7 @@ def _process_config(scene_config): states[entity_id.lower()] = State(entity_id, state, attributes) - return SceneConfig(name, states) + return SceneConfig(name, states, write_after_set) class Scene(ToggleEntity): @@ -178,14 +179,38 @@ class Scene(ToggleEntity): """ Returns if given state is as requested. """ state = self.scene_config.states.get(cur_state and cur_state.entity_id) - return (cur_state is not None and state.state == cur_state.state and + ret = (cur_state is not None and state.state == cur_state.state and all(value == cur_state.attributes.get(key) for key, value in state.attributes.items())) + print('AS REQUESTED') + print(ret) + print(self.scene_config.states) + return ret + + def _state_read_after_write(self, states): + """ Reads back state after setting. """ + print('BEFORE') + print(self.scene_config.states) + target_state = self.scene_config.states + for key, value in target_state.items(): + actual = self.hass.states.get(key) + print('ACTUAL') + print(actual) + if actual and actual != value and actual.state == value.state: + print('CHANGING CONFIG') + for k in value.attributes.keys(): + value.attributes[k] = actual.attributes[k] + print('AFTER') + print(self.scene_config.states) + def _reproduce_state(self, states): """ Wraps reproduce state with Scence specific logic. """ + print('REPRODUCE_STATE') self.ignore_updates = True reproduce_state(self.hass, states, True) + if self.scene_config.write_after_set: + self._state_read_after_write(states) self.ignore_updates = False self.update_ha_state(True) From c01e9bea2b5f601ea80becb0cf0d70b182109276 Mon Sep 17 00:00:00 2001 From: pavoni Date: Fri, 18 Sep 2015 14:38:15 +0100 Subject: [PATCH 05/10] Fix inconsistant naming --- homeassistant/components/scene.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/scene.py b/homeassistant/components/scene.py index 0d19c77ae98..194f4683522 100644 --- a/homeassistant/components/scene.py +++ b/homeassistant/components/scene.py @@ -188,7 +188,7 @@ class Scene(ToggleEntity): print(self.scene_config.states) return ret - def _state_read_after_write(self, states): + def _state_read_after_set(self, states): """ Reads back state after setting. """ print('BEFORE') print(self.scene_config.states) @@ -210,7 +210,7 @@ class Scene(ToggleEntity): self.ignore_updates = True reproduce_state(self.hass, states, True) if self.scene_config.write_after_set: - self._state_read_after_write(states) + self._state_read_after_set(states) self.ignore_updates = False self.update_ha_state(True) From 6abaebb2488f7ced049a110c1ec23997090216a2 Mon Sep 17 00:00:00 2001 From: pavoni Date: Fri, 18 Sep 2015 14:40:00 +0100 Subject: [PATCH 06/10] More consistant naming --- homeassistant/components/scene.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/scene.py b/homeassistant/components/scene.py index 194f4683522..013abcb3f92 100644 --- a/homeassistant/components/scene.py +++ b/homeassistant/components/scene.py @@ -33,7 +33,7 @@ ATTR_ACTIVE_REQUESTED = "active_requested" CONF_ENTITIES = "entities" -SceneConfig = namedtuple('SceneConfig', ['name', 'states', 'write_after_set']) +SceneConfig = namedtuple('SceneConfig', ['name', 'states', 'read_after_set']) def setup(hass, config): @@ -71,7 +71,7 @@ def setup(hass, config): def _process_config(scene_config): """ Process passed in config into a format to work with. """ name = scene_config.get('name') - write_after_set = scene_config.get('write_after_set') + read_after_set = scene_config.get('read_after_set') states = {} c_entities = dict(scene_config.get(CONF_ENTITIES, {})) @@ -92,7 +92,7 @@ def _process_config(scene_config): states[entity_id.lower()] = State(entity_id, state, attributes) - return SceneConfig(name, states, write_after_set) + return SceneConfig(name, states, read_after_set) class Scene(ToggleEntity): @@ -209,7 +209,7 @@ class Scene(ToggleEntity): print('REPRODUCE_STATE') self.ignore_updates = True reproduce_state(self.hass, states, True) - if self.scene_config.write_after_set: + if self.scene_config.read_after_set: self._state_read_after_set(states) self.ignore_updates = False From 61fb8271e5130502412cf2e0661846a27afb06ad Mon Sep 17 00:00:00 2001 From: pavoni Date: Fri, 25 Sep 2015 13:26:43 +0100 Subject: [PATCH 07/10] Change scene matching to use fuzzy logic for float values, if requested --- homeassistant/components/scene.py | 52 +++++++++++++++---------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/homeassistant/components/scene.py b/homeassistant/components/scene.py index 013abcb3f92..b4a665a8214 100644 --- a/homeassistant/components/scene.py +++ b/homeassistant/components/scene.py @@ -33,7 +33,7 @@ ATTR_ACTIVE_REQUESTED = "active_requested" CONF_ENTITIES = "entities" -SceneConfig = namedtuple('SceneConfig', ['name', 'states', 'read_after_set']) +SceneConfig = namedtuple('SceneConfig', ['name', 'states', 'fuzzy_match']) def setup(hass, config): @@ -71,7 +71,15 @@ def setup(hass, config): def _process_config(scene_config): """ Process passed in config into a format to work with. """ name = scene_config.get('name') - read_after_set = scene_config.get('read_after_set') + + fuzzy_match = scene_config.get('fuzzy_match') + if fuzzy_match: + # default to 1% + if isinstance(fuzzy_match, int): + fuzzy_match /= 100.0 + else: + fuzzy_match = 0.01 + states = {} c_entities = dict(scene_config.get(CONF_ENTITIES, {})) @@ -92,7 +100,7 @@ def _process_config(scene_config): states[entity_id.lower()] = State(entity_id, state, attributes) - return SceneConfig(name, states, read_after_set) + return SceneConfig(name, states, fuzzy_match) class Scene(ToggleEntity): @@ -179,38 +187,30 @@ class Scene(ToggleEntity): """ Returns if given state is as requested. """ state = self.scene_config.states.get(cur_state and cur_state.entity_id) - ret = (cur_state is not None and state.state == cur_state.state and - all(value == cur_state.attributes.get(key) + return (cur_state is not None and state.state == cur_state.state and + all(self._compare_state_attribites(value, cur_state.attributes.get(key)) for key, value in state.attributes.items())) - print('AS REQUESTED') - print(ret) - print(self.scene_config.states) - return ret + def _fuzzy_attribute_compare(self, a, b): + if not (isinstance(a, float) and isinstance(b, float)): + return False + diff = abs(a - b) / (abs(a) + abs(b)) + return diff <= self.scene_config.fuzzy_match - def _state_read_after_set(self, states): - """ Reads back state after setting. """ - print('BEFORE') - print(self.scene_config.states) - target_state = self.scene_config.states - for key, value in target_state.items(): - actual = self.hass.states.get(key) - print('ACTUAL') - print(actual) - if actual and actual != value and actual.state == value.state: - print('CHANGING CONFIG') - for k in value.attributes.keys(): - value.attributes[k] = actual.attributes[k] - print('AFTER') - print(self.scene_config.states) + def _compare_state_attribites(self, attr1, attr2): + if attr1 == attr2: + return True + if not self.scene_config.fuzzy_match: + return False + if isinstance(attr1, list): + return all(self._fuzzy_attribute_compare(a, b) for a,b in zip(attr1,attr2)) + return self._fuzzy_attribute_compare(attr1, attr2) def _reproduce_state(self, states): """ Wraps reproduce state with Scence specific logic. """ print('REPRODUCE_STATE') self.ignore_updates = True reproduce_state(self.hass, states, True) - if self.scene_config.read_after_set: - self._state_read_after_set(states) self.ignore_updates = False self.update_ha_state(True) From 476e4f0517f7c3c48e0f6a964848cabca887c324 Mon Sep 17 00:00:00 2001 From: pavoni Date: Fri, 25 Sep 2015 13:37:47 +0100 Subject: [PATCH 08/10] Add doc strings --- homeassistant/components/scene.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/homeassistant/components/scene.py b/homeassistant/components/scene.py index b4a665a8214..e23288b2c3e 100644 --- a/homeassistant/components/scene.py +++ b/homeassistant/components/scene.py @@ -192,12 +192,15 @@ class Scene(ToggleEntity): for key, value in state.attributes.items())) def _fuzzy_attribute_compare(self, a, b): + """ Compare the attributes passed, use fuzzy logic if they are floats. """ + if not (isinstance(a, float) and isinstance(b, float)): return False diff = abs(a - b) / (abs(a) + abs(b)) return diff <= self.scene_config.fuzzy_match def _compare_state_attribites(self, attr1, attr2): + """ Compare the attributes passed, using fuzzy logic if specified. """ if attr1 == attr2: return True if not self.scene_config.fuzzy_match: From 3ec00ce4fef2dca06dcb4e4471fdd80991b51be8 Mon Sep 17 00:00:00 2001 From: pavoni Date: Fri, 25 Sep 2015 15:49:56 +0100 Subject: [PATCH 09/10] Fix format errors --- homeassistant/components/scene.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/scene.py b/homeassistant/components/scene.py index e23288b2c3e..bf13989c3f2 100644 --- a/homeassistant/components/scene.py +++ b/homeassistant/components/scene.py @@ -188,15 +188,18 @@ class Scene(ToggleEntity): state = self.scene_config.states.get(cur_state and cur_state.entity_id) return (cur_state is not None and state.state == cur_state.state and - all(self._compare_state_attribites(value, cur_state.attributes.get(key)) + all(self._compare_state_attribites( + value, cur_state.attributes.get(key)) for key, value in state.attributes.items())) - def _fuzzy_attribute_compare(self, a, b): - """ Compare the attributes passed, use fuzzy logic if they are floats. """ + def _fuzzy_attribute_compare(self, attr_a, attr_b): + """ + Compare the attributes passed, use fuzzy logic if they are floats. + """ - if not (isinstance(a, float) and isinstance(b, float)): + if not (isinstance(attr_a, float) and isinstance(attr_b, float)): return False - diff = abs(a - b) / (abs(a) + abs(b)) + diff = abs(attr_a - attr_b) / (abs(attr_a) + abs(attr_b)) return diff <= self.scene_config.fuzzy_match def _compare_state_attribites(self, attr1, attr2): @@ -206,7 +209,8 @@ class Scene(ToggleEntity): if not self.scene_config.fuzzy_match: return False if isinstance(attr1, list): - return all(self._fuzzy_attribute_compare(a, b) for a,b in zip(attr1,attr2)) + return all(self._fuzzy_attribute_compare(a, b) + for a, b in zip(attr1, attr2)) return self._fuzzy_attribute_compare(attr1, attr2) def _reproduce_state(self, states): From 63bf4db96953ac2fd5c51bec88858bbd92be000a Mon Sep 17 00:00:00 2001 From: pavoni Date: Fri, 25 Sep 2015 15:51:09 +0100 Subject: [PATCH 10/10] Remove trace --- homeassistant/components/scene.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/scene.py b/homeassistant/components/scene.py index bf13989c3f2..4a85adefd17 100644 --- a/homeassistant/components/scene.py +++ b/homeassistant/components/scene.py @@ -215,7 +215,6 @@ class Scene(ToggleEntity): def _reproduce_state(self, states): """ Wraps reproduce state with Scence specific logic. """ - print('REPRODUCE_STATE') self.ignore_updates = True reproduce_state(self.hass, states, True) self.ignore_updates = False