From de027609d8696a15ff57ee3f13b3868ec63f058e Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Wed, 28 Oct 2015 12:27:58 +0100 Subject: [PATCH 01/11] Fixed entity_id for the script component. Alias now does not override the entity_id Fixed issue: #561 --- homeassistant/components/script.py | 5 +++-- homeassistant/helpers/entity_component.py | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/script.py b/homeassistant/components/script.py index 3f892fdfa80..e5bfe61bfa5 100644 --- a/homeassistant/components/script.py +++ b/homeassistant/components/script.py @@ -75,7 +75,7 @@ def setup(hass, config): _LOGGER.warn("Missing key 'sequence' for script %s", name) continue alias = cfg.get(CONF_ALIAS, name) - script = Script(hass, alias, cfg[CONF_SEQUENCE]) + script = Script(hass, alias, name, cfg[CONF_SEQUENCE]) component.add_entities((script,)) _, object_id = split_entity_id(script.entity_id) hass.services.register(DOMAIN, object_id, service_handler) @@ -100,9 +100,10 @@ def setup(hass, config): class Script(ToggleEntity): """ Represents a script. """ - def __init__(self, hass, name, sequence): + def __init__(self, hass, name, entity_id, sequence): self.hass = hass self._name = name + self.entity_id = entity_id self.sequence = sequence self._lock = threading.Lock() self._cur = -1 diff --git a/homeassistant/helpers/entity_component.py b/homeassistant/helpers/entity_component.py index 708bf6e93a9..5223b7f0ca3 100644 --- a/homeassistant/helpers/entity_component.py +++ b/homeassistant/helpers/entity_component.py @@ -65,8 +65,10 @@ class EntityComponent(object): if entity is not None and entity not in self.entities.values(): entity.hass = self.hass - entity.entity_id = generate_entity_id( - self.entity_id_format, entity.name, self.entities.keys()) + if entity.entity_id is None: + entity.entity_id = generate_entity_id( + self.entity_id_format, entity.name, + self.entities.keys()) self.entities[entity.entity_id] = entity From 48bfc98acb45b0114ad7c004fc1aaa69ad823ab4 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Wed, 28 Oct 2015 19:52:09 +0100 Subject: [PATCH 02/11] Fixed entity name --- homeassistant/components/script.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/script.py b/homeassistant/components/script.py index e5bfe61bfa5..9849a243a41 100644 --- a/homeassistant/components/script.py +++ b/homeassistant/components/script.py @@ -75,7 +75,8 @@ def setup(hass, config): _LOGGER.warn("Missing key 'sequence' for script %s", name) continue alias = cfg.get(CONF_ALIAS, name) - script = Script(hass, alias, name, cfg[CONF_SEQUENCE]) + entity_id = "{}.{}".format(DOMAIN, name) + script = Script(hass, alias, entity_id, cfg[CONF_SEQUENCE]) component.add_entities((script,)) _, object_id = split_entity_id(script.entity_id) hass.services.register(DOMAIN, object_id, service_handler) From 10c95b435250ccc7b754aa726153413238026ad7 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Wed, 28 Oct 2015 20:17:17 +0100 Subject: [PATCH 03/11] Added pylint hint --- homeassistant/components/script.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/script.py b/homeassistant/components/script.py index 9849a243a41..084f5502426 100644 --- a/homeassistant/components/script.py +++ b/homeassistant/components/script.py @@ -99,6 +99,7 @@ def setup(hass, config): return True +# pylint: disable=too-many-instance-attributes class Script(ToggleEntity): """ Represents a script. """ def __init__(self, hass, name, entity_id, sequence): From f3500542dd85df362710aa921a5c2e2b777cb4cb Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Wed, 28 Oct 2015 20:17:17 +0100 Subject: [PATCH 04/11] Added pylint hint --- homeassistant/components/script.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/script.py b/homeassistant/components/script.py index 6d5f0ea37a1..a14919cab67 100644 --- a/homeassistant/components/script.py +++ b/homeassistant/components/script.py @@ -101,6 +101,7 @@ def setup(hass, config): return True +# pylint: disable=too-many-instance-attributes class Script(ToggleEntity): """ Represents a script. """ # pylint: disable=too-many-instance-attributes From 30de5af445772775401f894d76c0c8a095aed2c8 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Wed, 28 Oct 2015 22:08:04 +0100 Subject: [PATCH 05/11] Fix for Philio Zwave devices which don't send an off event. --- homeassistant/components/sensor/zwave.py | 63 +++++++++++++++++++++--- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/sensor/zwave.py b/homeassistant/components/sensor/zwave.py index 0cd136421a0..10d8734bcac 100644 --- a/homeassistant/components/sensor/zwave.py +++ b/homeassistant/components/sensor/zwave.py @@ -7,15 +7,27 @@ For more details about the zwave component, please refer to the documentation at https://home-assistant.io/components/zwave.html """ # pylint: disable=import-error +from homeassistant.helpers.event import track_point_in_time from openzwave.network import ZWaveNetwork from pydispatch import dispatcher - +import datetime +import homeassistant.util.dt as dt_util import homeassistant.components.zwave as zwave from homeassistant.helpers.entity import Entity from homeassistant.const import ( ATTR_BATTERY_LEVEL, STATE_ON, STATE_OFF, TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_LOCATION) +PHILIO = '013c' +PHILIO_SLIM_SENSOR = '0002' +PHILIO_SLIM_SENSOR_MOTION = (PHILIO, PHILIO_SLIM_SENSOR, 0) + +WORKAROUND_NO_TRIGGER_OFF_EVENT = 'trigger_no_off_event' + +SPECIFIC_DEVICE_MAPPINGS = [ + (WORKAROUND_NO_TRIGGER_OFF_EVENT, PHILIO_SLIM_SENSOR_MOTION), +] + def setup_platform(hass, config, add_devices, discovery_info=None): """ Sets up Z-Wave sensors. """ @@ -24,10 +36,17 @@ def setup_platform(hass, config, add_devices, discovery_info=None): value.set_change_verified(False) - # if 1 in groups and (zwave.NETWORK.controller.node_id not in - # groups[1].associations): - # node.groups[1].add_association(zwave.NETWORK.controller.node_id) + # Check workaround mappings for specific devices + for workaround_definition in SPECIFIC_DEVICE_MAPPINGS: + workaround, sensor_specification = workaround_definition + if sensor_specification == ( + value.command_class, value.node.manufacturer_id, + value.node.manufacturer_id, value.node.manufacturer_id): + if workaround == WORKAROUND_NO_TRIGGER_OFF_EVENT: + add_devices([ZWaveTriggerSensor(value, hass)]) + return + # generic Device mappings if value.command_class == zwave.COMMAND_CLASS_SENSOR_BINARY: add_devices([ZWaveBinarySensor(value)]) @@ -37,12 +56,13 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class ZWaveSensor(Entity): """ Represents a Z-Wave sensor. """ + def __init__(self, sensor_value): self._value = sensor_value self._node = sensor_value.node dispatcher.connect( - self._value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED) + self.value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED) @property def should_poll(self): @@ -90,7 +110,7 @@ class ZWaveSensor(Entity): def unit_of_measurement(self): return self._value.units - def _value_changed(self, value): + def value_changed(self, value): """ Called when a value has changed on the network. """ if self._value.value_id == value.value_id: self.update_ha_state() @@ -106,6 +126,37 @@ class ZWaveBinarySensor(ZWaveSensor): return STATE_ON if self._value.data else STATE_OFF +class ZWaveTriggerSensor(ZWaveSensor): + """ Represents a stateless sensor which triggers events within Z-Wave. """ + + def __init__(self, sensor_value, hass): + super(ZWaveTriggerSensor, self).__init__(sensor_value) + self._hass = hass + self.invalidate_after = None + + def value_changed(self, value): + """ Called when a value has changed on the network. """ + if self._value.value_id == value.value_id: + self.update_ha_state() + if value.data: + # only allow this value to be true for 60 secs + self.invalidate_after = dt_util.utcnow() + datetime.timedelta( + seconds=60) + track_point_in_time( + self._hass, self.update_ha_state, + self.invalidate_after) + + @property + def state(self): + """ Returns the state of the sensor. """ + if not self._value.data or \ + (self.invalidate_after is not None and + self.invalidate_after <= dt_util.utcnow()): + return STATE_OFF + + return STATE_ON + + class ZWaveMultilevelSensor(ZWaveSensor): """ Represents a multi level sensor Z-Wave sensor. """ From d578bf3494c0416c5c361f02242e0a6d027ebff4 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Wed, 28 Oct 2015 22:17:17 +0100 Subject: [PATCH 06/11] Removed extra pylint hint from a previous merge --- homeassistant/components/script.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/script.py b/homeassistant/components/script.py index a14919cab67..6d5f0ea37a1 100644 --- a/homeassistant/components/script.py +++ b/homeassistant/components/script.py @@ -101,7 +101,6 @@ def setup(hass, config): return True -# pylint: disable=too-many-instance-attributes class Script(ToggleEntity): """ Represents a script. """ # pylint: disable=too-many-instance-attributes From a56173676e43d0ba4b082aa393818b21af756367 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Fri, 30 Oct 2015 15:28:06 +0100 Subject: [PATCH 07/11] Fixed the workaround match logic --- homeassistant/components/sensor/zwave.py | 33 +++++++++++++----------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/sensor/zwave.py b/homeassistant/components/sensor/zwave.py index 10d8734bcac..d6b55f93641 100644 --- a/homeassistant/components/sensor/zwave.py +++ b/homeassistant/components/sensor/zwave.py @@ -22,12 +22,11 @@ PHILIO = '013c' PHILIO_SLIM_SENSOR = '0002' PHILIO_SLIM_SENSOR_MOTION = (PHILIO, PHILIO_SLIM_SENSOR, 0) -WORKAROUND_NO_TRIGGER_OFF_EVENT = 'trigger_no_off_event' - -SPECIFIC_DEVICE_MAPPINGS = [ - (WORKAROUND_NO_TRIGGER_OFF_EVENT, PHILIO_SLIM_SENSOR_MOTION), -] +WORKAROUND_NO_OFF_EVENT = 'trigger_no_off_event' +DEVICE_MAPPINGS = { + PHILIO_SLIM_SENSOR_MOTION: WORKAROUND_NO_OFF_EVENT, +} def setup_platform(hass, config, add_devices, discovery_info=None): """ Sets up Z-Wave sensors. """ @@ -36,17 +35,21 @@ def setup_platform(hass, config, add_devices, discovery_info=None): value.set_change_verified(False) - # Check workaround mappings for specific devices - for workaround_definition in SPECIFIC_DEVICE_MAPPINGS: - workaround, sensor_specification = workaround_definition - if sensor_specification == ( - value.command_class, value.node.manufacturer_id, - value.node.manufacturer_id, value.node.manufacturer_id): - if workaround == WORKAROUND_NO_TRIGGER_OFF_EVENT: - add_devices([ZWaveTriggerSensor(value, hass)]) - return + # if 1 in groups and (zwave.NETWORK.controller.node_id not in + # groups[1].associations): + # node.groups[1].add_association(zwave.NETWORK.controller.node_id) - # generic Device mappings + specific_sensor_key = (value.node.manufacturer_id, + value.node.product_id, + value.index) + + # Check workaround mappings for specific devices + if specific_sensor_key in DEVICE_MAPPINGS: + if DEVICE_MAPPINGS[specific_sensor_key] == WORKAROUND_NO_OFF_EVENT: + add_devices([ZWaveTriggerSensor(value, hass)]) + return + + # generic Device mappings if value.command_class == zwave.COMMAND_CLASS_SENSOR_BINARY: add_devices([ZWaveBinarySensor(value)]) From 2ad647bb097b72cf06e155d328e5a20ee20dc0b3 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Fri, 30 Oct 2015 15:30:08 +0100 Subject: [PATCH 08/11] Style fixes --- homeassistant/components/sensor/zwave.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/sensor/zwave.py b/homeassistant/components/sensor/zwave.py index d6b55f93641..da5c8839d58 100644 --- a/homeassistant/components/sensor/zwave.py +++ b/homeassistant/components/sensor/zwave.py @@ -25,9 +25,10 @@ PHILIO_SLIM_SENSOR_MOTION = (PHILIO, PHILIO_SLIM_SENSOR, 0) WORKAROUND_NO_OFF_EVENT = 'trigger_no_off_event' DEVICE_MAPPINGS = { - PHILIO_SLIM_SENSOR_MOTION: WORKAROUND_NO_OFF_EVENT, + PHILIO_SLIM_SENSOR_MOTION: WORKAROUND_NO_OFF_EVENT, } + def setup_platform(hass, config, add_devices, discovery_info=None): """ Sets up Z-Wave sensors. """ node = zwave.NETWORK.nodes[discovery_info[zwave.ATTR_NODE_ID]] From cae8932b180caef8039125747ed5ad07e95880a4 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Sat, 31 Oct 2015 21:23:33 +0100 Subject: [PATCH 09/11] Z-Wave workaround - Connected to the timeout to the configured node value "9. Turn Off Light Time" --- homeassistant/components/sensor/zwave.py | 24 +++++++++++++++++------- homeassistant/components/zwave.py | 9 +++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/sensor/zwave.py b/homeassistant/components/sensor/zwave.py index da5c8839d58..1880efccd57 100644 --- a/homeassistant/components/sensor/zwave.py +++ b/homeassistant/components/sensor/zwave.py @@ -47,11 +47,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None): # Check workaround mappings for specific devices if specific_sensor_key in DEVICE_MAPPINGS: if DEVICE_MAPPINGS[specific_sensor_key] == WORKAROUND_NO_OFF_EVENT: - add_devices([ZWaveTriggerSensor(value, hass)]) - return + re_arm_time = (zwave.get_config_value(node, 9) * 8) + add_devices([ZWaveTriggerSensor(value, hass, re_arm_time)]) - # generic Device mappings - if value.command_class == zwave.COMMAND_CLASS_SENSOR_BINARY: + # generic Device mappings + elif value.command_class == zwave.COMMAND_CLASS_SENSOR_BINARY: add_devices([ZWaveBinarySensor(value)]) elif value.command_class == zwave.COMMAND_CLASS_SENSOR_MULTILEVEL: @@ -131,12 +131,22 @@ class ZWaveBinarySensor(ZWaveSensor): class ZWaveTriggerSensor(ZWaveSensor): - """ Represents a stateless sensor which triggers events within Z-Wave. """ + """ + Represents a stateless sensor which + triggers events just 'On' within Z-Wave. + """ - def __init__(self, sensor_value, hass): + def __init__(self, sensor_value, hass, re_arm_sec=60): + """ + :param sensor_value: The z-wave node + :param hass: + :param re_arm_sec: Set state to Off re_arm_sec after the last On event + :return: + """ super(ZWaveTriggerSensor, self).__init__(sensor_value) self._hass = hass self.invalidate_after = None + self.re_arm_sec = re_arm_sec def value_changed(self, value): """ Called when a value has changed on the network. """ @@ -145,7 +155,7 @@ class ZWaveTriggerSensor(ZWaveSensor): if value.data: # only allow this value to be true for 60 secs self.invalidate_after = dt_util.utcnow() + datetime.timedelta( - seconds=60) + seconds=self.re_arm_sec) track_point_in_time( self._hass, self.update_ha_state, self.invalidate_after) diff --git a/homeassistant/components/zwave.py b/homeassistant/components/zwave.py index 86d65d1c42e..755908ad2d4 100644 --- a/homeassistant/components/zwave.py +++ b/homeassistant/components/zwave.py @@ -58,6 +58,15 @@ def nice_print_node(node): print("\n\n\n") +def get_config_value(node, value_index): + """ Returns the current config value for a specific index """ + + for _, value in node.values.items(): + # 112 == config command class + if value.command_class == 112 and value.index == value_index: + return value.data + + def setup(hass, config): """ Setup Z-wave. From c4261ae2e00d2f70de6a9019993db2e357985b68 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Sat, 31 Oct 2015 23:03:40 +0100 Subject: [PATCH 10/11] Z-Wave workaround - Added a default value if we did not get any config value. --- homeassistant/components/sensor/zwave.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sensor/zwave.py b/homeassistant/components/sensor/zwave.py index 1880efccd57..0cfc0682454 100644 --- a/homeassistant/components/sensor/zwave.py +++ b/homeassistant/components/sensor/zwave.py @@ -47,8 +47,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None): # Check workaround mappings for specific devices if specific_sensor_key in DEVICE_MAPPINGS: if DEVICE_MAPPINGS[specific_sensor_key] == WORKAROUND_NO_OFF_EVENT: - re_arm_time = (zwave.get_config_value(node, 9) * 8) - add_devices([ZWaveTriggerSensor(value, hass, re_arm_time)]) + # Default the multiplier to 4 + re_arm_multiplier = (zwave.get_config_value(value.node, 9) or 4) + add_devices([ + ZWaveTriggerSensor(value, hass, re_arm_multiplier * 8) + ]) # generic Device mappings elif value.command_class == zwave.COMMAND_CLASS_SENSOR_BINARY: @@ -145,7 +148,7 @@ class ZWaveTriggerSensor(ZWaveSensor): """ super(ZWaveTriggerSensor, self).__init__(sensor_value) self._hass = hass - self.invalidate_after = None + self.invalidate_after = dt_util.utcnow() self.re_arm_sec = re_arm_sec def value_changed(self, value): From ec732becfce6eb68b666997cb68ab7de0131d9b6 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Sat, 31 Oct 2015 23:34:19 +0100 Subject: [PATCH 11/11] Fixed the get_config_value method when the zwave node was changed while reading it. --- homeassistant/components/zwave.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/zwave.py b/homeassistant/components/zwave.py index 755908ad2d4..9f7df64312d 100644 --- a/homeassistant/components/zwave.py +++ b/homeassistant/components/zwave.py @@ -61,10 +61,15 @@ def nice_print_node(node): def get_config_value(node, value_index): """ Returns the current config value for a specific index """ - for _, value in node.values.items(): - # 112 == config command class - if value.command_class == 112 and value.index == value_index: - return value.data + try: + for value in node.values.values(): + # 112 == config command class + if value.command_class == 112 and value.index == value_index: + return value.data + except RuntimeError: + # If we get an runtime error the dict has changed while + # we was looking for a value, just do it again + return get_config_value(node, value_index) def setup(hass, config):