Z-Wave sensors should work now
parent
e9218e2eb2
commit
71ca07363a
|
@ -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)]
|
||||
|
|
|
@ -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. """
|
||||
|
|
|
@ -76,8 +76,6 @@ ATTR_LOCATION = "location"
|
|||
|
||||
ATTR_BATTERY_LEVEL = "battery_level"
|
||||
|
||||
LIGHT_LUX = "lux"
|
||||
|
||||
# #### SERVICES ####
|
||||
SERVICE_HOMEASSISTANT_STOP = "stop"
|
||||
|
||||
|
|
Loading…
Reference in New Issue