From 71ca07363a9b521523b893dd26841cb1ac5ac8a5 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 25 Feb 2015 23:27:17 -0800 Subject: [PATCH] Z-Wave sensors should work now --- homeassistant/components/sensor/zwave.py | 122 +++++++++-------------- homeassistant/components/zwave.py | 101 ++++++++++--------- homeassistant/const.py | 2 - 3 files changed, 96 insertions(+), 129 deletions(-) diff --git a/homeassistant/components/sensor/zwave.py b/homeassistant/components/sensor/zwave.py index 36b3fcdb06d..b39ef5cbb32 100644 --- a/homeassistant/components/sensor/zwave.py +++ b/homeassistant/components/sensor/zwave.py @@ -1,25 +1,26 @@ +""" +homeassistant.components.sensor.zwave +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Interfaces with Z-Wave sensors. +""" import homeassistant.components.zwave as zwave from homeassistant.helpers import Device from homeassistant.const import ( ATTR_FRIENDLY_NAME, ATTR_BATTERY_LEVEL, ATTR_UNIT_OF_MEASUREMENT, - TEMP_CELCIUS, TEMP_FAHRENHEIT, LIGHT_LUX, ATTR_LOCATION) - -VALUE_REPORT = 72057594081707603 -REPORT_BATTERY = 1 -REPORT_TEMPERATURE = 1 << 5 -REPORT_HUMIDITY = 1 << 6 -REPORT_LUMINOSITY = 1 << 7 + TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_LOCATION, STATE_ON, STATE_OFF) class ZWaveSensor(Device): - def __init__(self, node, sensor_value): - self._node = node - self._value = node.values[sensor_value] + """ Represents a Z-Wave sensor. """ + def __init__(self, sensor_value): + self._value = sensor_value + self._node = sensor_value.node @property def unique_id(self): """ Returns a unique id. """ - return "ZWAVE-{}-{}".format(self._node.node_id, self._value) + return "ZWAVE-{}-{}".format(self._node.node_id, self._value.object_id) @property def name(self): @@ -38,18 +39,18 @@ class ZWaveSensor(Device): def state_attributes(self): """ Returns the state attributes. """ attrs = { - ATTR_FRIENDLY_NAME: self.name + ATTR_FRIENDLY_NAME: self.name, + zwave.ATTR_NODE_ID: self._node.node_id, } - battery_level = zwave.get_node_value( - self._node, zwave.VALUE_BATTERY_LEVEL) + battery_level = self._node.get_battery_level() if battery_level is not None: attrs[ATTR_BATTERY_LEVEL] = battery_level unit = self.unit - if unit is not None: + if unit: attrs[ATTR_UNIT_OF_MEASUREMENT] = unit location = self._node.location @@ -57,30 +58,36 @@ class ZWaveSensor(Device): if location: attrs[ATTR_LOCATION] = location - attrs.update(self.get_sensor_attributes()) - return attrs @property def unit(self): """ Unit if sensor has one. """ - return None - - def get_sensor_attributes(self): - """ Get sensor attributes. """ - return {} + return self._value.units -class ZWaveTemperatureSensor(ZWaveSensor): - """ Represents a ZWave Temperature Sensor. """ - - def __init__(self, node): - super().__init__(node, zwave.VALUE_TEMPERATURE) +# pylint: disable=too-few-public-methods +class ZWaveBinarySensor(ZWaveSensor): + """ Represents a binary sensor within Z-Wave. """ @property def state(self): """ Returns the state of the sensor. """ - return round(self._value.data, 1) + return STATE_ON if self._value.data else STATE_OFF + + +class ZWaveMultilevelSensor(ZWaveSensor): + """ Represents a multi level sensor Z-Wave sensor. """ + + @property + def state(self): + """ Returns the state of the sensor. """ + value = self._value.data + + if self._value.units in ('C', 'F'): + return round(value, 1) + + return value @property def unit(self): @@ -92,58 +99,21 @@ class ZWaveTemperatureSensor(ZWaveSensor): elif unit == 'F': return TEMP_FAHRENHEIT else: - return None - - -class ZWaveRelativeHumiditySensor(ZWaveSensor): - """ Represents a ZWave Relative Humidity Sensor. """ - - def __init__(self, node): - super().__init__(node, zwave.VALUE_RELATIVE_HUMIDITY) - - @property - def unit(self): - """ Unit of this sensor. """ - return '%' - - -class ZWaveLuminanceSensor(ZWaveSensor): - """ Represents a ZWave luminance Sensor. """ - - def __init__(self, node): - super().__init__(node, zwave.VALUE_LUMINANCE) - - @property - def unit(self): - """ Unit of this sensor. """ - return LIGHT_LUX - - -VALUE_CLASS_MAP = [ - (zwave.VALUE_TEMPERATURE, ZWaveTemperatureSensor, REPORT_TEMPERATURE), - (zwave.VALUE_LUMINANCE, ZWaveLuminanceSensor, REPORT_LUMINOSITY), - (zwave.VALUE_RELATIVE_HUMIDITY, ZWaveRelativeHumiditySensor, - REPORT_HUMIDITY), -] + return unit def devices_discovered(hass, config, info): - """ """ - # from louie import connect - # from openzwave.network import ZWaveNetwork + """ Called when a device is discovered. """ + node = zwave.NETWORK.nodes[info[zwave.ATTR_NODE_ID]] + value = node.values[info[zwave.ATTR_VALUE_ID]] - sensors = [] + value.set_change_verified(False) - for node in zwave.NETWORK.nodes.values(): - report_mask = REPORT_BATTERY + if zwave.NETWORK.controller.node_id not in node.groups[1].associations: + node.groups[1].add_association(zwave.NETWORK.controller.node_id) - for value, klass, sensor_report_mask in VALUE_CLASS_MAP: + if value.command_class == zwave.COMMAND_CLASS_SENSOR_BINARY: + return [ZWaveBinarySensor(value)] - if value in node.get_sensors(): - sensors.append(klass(node)) - report_mask |= sensor_report_mask - - if report_mask != REPORT_BATTERY and VALUE_REPORT in node.values: - node.values[VALUE_REPORT].data = report_mask - - return sensors + elif value.command_class == zwave.COMMAND_CLASS_SENSOR_MULTILEVEL: + return [ZWaveMultilevelSensor(value)] diff --git a/homeassistant/components/zwave.py b/homeassistant/components/zwave.py index ff3f9eb0bbd..8e2861cbb21 100644 --- a/homeassistant/components/zwave.py +++ b/homeassistant/components/zwave.py @@ -1,5 +1,12 @@ +""" +homeassistant.components.zwave +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Connects Home Assistant to a Z-Wave network. +""" +from pprint import pprint + from homeassistant import bootstrap -from homeassistant.loader import get_component from homeassistant.const import ( EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, EVENT_PLATFORM_DISCOVERED, ATTR_SERVICE, ATTR_DISCOVERED) @@ -13,44 +20,39 @@ CONF_DEBUG = "debug" DISCOVER_SENSORS = "zwave.sensors" -VALUE_SENSOR = 72057594076463104 -VALUE_TEMPERATURE = 72057594076479506 -VALUE_LUMINANCE = 72057594076479538 -VALUE_RELATIVE_HUMIDITY = 72057594076479570 -VALUE_BATTERY_LEVEL = 72057594077773825 +COMMAND_CLASS_SENSOR_BINARY = 48 +COMMAND_CLASS_SENSOR_MULTILEVEL = 49 +COMMAND_CLASS_BATTERY = 128 + +# list of tuple (DOMAIN, discovered service, supported command classes) +DISCOVERY_COMPONENTS = [ + ('sensor', DISCOVER_SENSORS, + [COMMAND_CLASS_SENSOR_BINARY, COMMAND_CLASS_SENSOR_MULTILEVEL]), +] + +ATTR_NODE_ID = "node_id" +ATTR_VALUE_ID = "value_id" NETWORK = None -def get_node_value(node, key): - """ Helper function to get a node value. """ - return node.values[key].data if key in node.values else None +def _obj_to_dict(obj): + """ Converts an obj into a hash for debug. """ + return {key: getattr(obj, key) for key + in dir(obj) + if key[0] != '_' and not hasattr(getattr(obj, key), '__call__')} def nice_print_node(node): - """ Prints a nice formatted node to the output """ - from pprint import pprint + """ Prints a nice formatted node to the output (debug method) """ + node_dict = _obj_to_dict(node) + node_dict['values'] = {value_id: _obj_to_dict(value) + for value_id, value in node.values.items()} - print("") - print("") - print("") + print("\n\n\n") print("FOUND NODE", node.product_name) - pprint({key: getattr(node, key) for key - in dir(node) - if key != 'values' and - not hasattr(getattr(node, key), '__call__')}) - print("") - print("") - print("VALUES") - pprint({ - value_id: {key: getattr(value, key) for key - in dir(value) - if key[0] != '_' and - not hasattr(getattr(value, key), '__call__')} - for value_id, value in node.values.items()}) - - print("") - print("") + pprint(node_dict) + print("\n\n\n") def setup(hass, config): @@ -58,6 +60,7 @@ def setup(hass, config): Setup Z-wave. Will automatically load components to support devices found on the network. """ + # pylint: disable=global-statement, import-error global NETWORK from louie import connect @@ -71,46 +74,42 @@ def setup(hass, config): config[DOMAIN].get(CONF_USB_STICK_PATH, DEFAULT_CONF_USB_STICK_PATH), user_path=hass.config_dir) - options.set_associate(True) options.set_console_output(use_debug) options.lock() NETWORK = ZWaveNetwork(options, autostart=False) if use_debug: - def log_all(signal): + def log_all(signal, value=None): + """ Log all the louie signals. """ print("") print("LOUIE SIGNAL *****", signal) + if value and signal in (ZWaveNetwork.SIGNAL_VALUE_CHANGED, + ZWaveNetwork.SIGNAL_VALUE_ADDED): + pprint(_obj_to_dict(value)) print("") connect(log_all, weak=False) - def zwave_init_done(network): - """ Called when Z-Wave has initialized. """ - init_sensor = False - - # This should be rewritten more efficient when supporting more types - for node in network.nodes.values(): - if use_debug: - nice_print_node(node) - - if get_node_value(node, VALUE_SENSOR) and not init_sensor: - init_sensor = True - - component = get_component('sensor') - + def value_added(node, value): + """ Called when a value is added to a node on the network. """ + for component, discovery_service, command_ids in DISCOVERY_COMPONENTS: + if value.command_class in command_ids: # Ensure component is loaded - if component.DOMAIN not in hass.components: - bootstrap.setup_component(hass, component.DOMAIN, config) + if component not in hass.components: + bootstrap.setup_component(hass, component, config) # Fire discovery event hass.bus.fire(EVENT_PLATFORM_DISCOVERED, { - ATTR_SERVICE: DISCOVER_SENSORS, - ATTR_DISCOVERED: {} + ATTR_SERVICE: discovery_service, + ATTR_DISCOVERED: { + ATTR_NODE_ID: node.node_id, + ATTR_VALUE_ID: value.value_id, + } }) connect( - zwave_init_done, ZWaveNetwork.SIGNAL_NETWORK_READY, weak=False) + value_added, ZWaveNetwork.SIGNAL_VALUE_ADDED, weak=False) def stop_zwave(event): """ Stop Z-wave. """ diff --git a/homeassistant/const.py b/homeassistant/const.py index a4efc5611b9..6bce196437f 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -76,8 +76,6 @@ ATTR_LOCATION = "location" ATTR_BATTERY_LEVEL = "battery_level" -LIGHT_LUX = "lux" - # #### SERVICES #### SERVICE_HOMEASSISTANT_STOP = "stop"