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
|
import homeassistant.components.zwave as zwave
|
||||||
from homeassistant.helpers import Device
|
from homeassistant.helpers import Device
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_FRIENDLY_NAME, ATTR_BATTERY_LEVEL, ATTR_UNIT_OF_MEASUREMENT,
|
ATTR_FRIENDLY_NAME, ATTR_BATTERY_LEVEL, ATTR_UNIT_OF_MEASUREMENT,
|
||||||
TEMP_CELCIUS, TEMP_FAHRENHEIT, LIGHT_LUX, ATTR_LOCATION)
|
TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_LOCATION, STATE_ON, STATE_OFF)
|
||||||
|
|
||||||
VALUE_REPORT = 72057594081707603
|
|
||||||
REPORT_BATTERY = 1
|
|
||||||
REPORT_TEMPERATURE = 1 << 5
|
|
||||||
REPORT_HUMIDITY = 1 << 6
|
|
||||||
REPORT_LUMINOSITY = 1 << 7
|
|
||||||
|
|
||||||
|
|
||||||
class ZWaveSensor(Device):
|
class ZWaveSensor(Device):
|
||||||
def __init__(self, node, sensor_value):
|
""" Represents a Z-Wave sensor. """
|
||||||
self._node = node
|
def __init__(self, sensor_value):
|
||||||
self._value = node.values[sensor_value]
|
self._value = sensor_value
|
||||||
|
self._node = sensor_value.node
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_id(self):
|
def unique_id(self):
|
||||||
""" Returns a unique id. """
|
""" 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
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
|
@ -38,18 +39,18 @@ class ZWaveSensor(Device):
|
||||||
def state_attributes(self):
|
def state_attributes(self):
|
||||||
""" Returns the state attributes. """
|
""" Returns the state attributes. """
|
||||||
attrs = {
|
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(
|
battery_level = self._node.get_battery_level()
|
||||||
self._node, zwave.VALUE_BATTERY_LEVEL)
|
|
||||||
|
|
||||||
if battery_level is not None:
|
if battery_level is not None:
|
||||||
attrs[ATTR_BATTERY_LEVEL] = battery_level
|
attrs[ATTR_BATTERY_LEVEL] = battery_level
|
||||||
|
|
||||||
unit = self.unit
|
unit = self.unit
|
||||||
|
|
||||||
if unit is not None:
|
if unit:
|
||||||
attrs[ATTR_UNIT_OF_MEASUREMENT] = unit
|
attrs[ATTR_UNIT_OF_MEASUREMENT] = unit
|
||||||
|
|
||||||
location = self._node.location
|
location = self._node.location
|
||||||
|
@ -57,30 +58,36 @@ class ZWaveSensor(Device):
|
||||||
if location:
|
if location:
|
||||||
attrs[ATTR_LOCATION] = location
|
attrs[ATTR_LOCATION] = location
|
||||||
|
|
||||||
attrs.update(self.get_sensor_attributes())
|
|
||||||
|
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unit(self):
|
def unit(self):
|
||||||
""" Unit if sensor has one. """
|
""" Unit if sensor has one. """
|
||||||
return None
|
return self._value.units
|
||||||
|
|
||||||
def get_sensor_attributes(self):
|
|
||||||
""" Get sensor attributes. """
|
|
||||||
return {}
|
|
||||||
|
|
||||||
|
|
||||||
class ZWaveTemperatureSensor(ZWaveSensor):
|
# pylint: disable=too-few-public-methods
|
||||||
""" Represents a ZWave Temperature Sensor. """
|
class ZWaveBinarySensor(ZWaveSensor):
|
||||||
|
""" Represents a binary sensor within Z-Wave. """
|
||||||
def __init__(self, node):
|
|
||||||
super().__init__(node, zwave.VALUE_TEMPERATURE)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
""" Returns the state of the sensor. """
|
""" 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
|
@property
|
||||||
def unit(self):
|
def unit(self):
|
||||||
|
@ -92,58 +99,21 @@ class ZWaveTemperatureSensor(ZWaveSensor):
|
||||||
elif unit == 'F':
|
elif unit == 'F':
|
||||||
return TEMP_FAHRENHEIT
|
return TEMP_FAHRENHEIT
|
||||||
else:
|
else:
|
||||||
return None
|
return unit
|
||||||
|
|
||||||
|
|
||||||
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),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def devices_discovered(hass, config, info):
|
def devices_discovered(hass, config, info):
|
||||||
""" """
|
""" Called when a device is discovered. """
|
||||||
# from louie import connect
|
node = zwave.NETWORK.nodes[info[zwave.ATTR_NODE_ID]]
|
||||||
# from openzwave.network import ZWaveNetwork
|
value = node.values[info[zwave.ATTR_VALUE_ID]]
|
||||||
|
|
||||||
sensors = []
|
value.set_change_verified(False)
|
||||||
|
|
||||||
for node in zwave.NETWORK.nodes.values():
|
if zwave.NETWORK.controller.node_id not in node.groups[1].associations:
|
||||||
report_mask = REPORT_BATTERY
|
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():
|
elif value.command_class == zwave.COMMAND_CLASS_SENSOR_MULTILEVEL:
|
||||||
sensors.append(klass(node))
|
return [ZWaveMultilevelSensor(value)]
|
||||||
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
|
|
||||||
|
|
|
@ -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 import bootstrap
|
||||||
from homeassistant.loader import get_component
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP,
|
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP,
|
||||||
EVENT_PLATFORM_DISCOVERED, ATTR_SERVICE, ATTR_DISCOVERED)
|
EVENT_PLATFORM_DISCOVERED, ATTR_SERVICE, ATTR_DISCOVERED)
|
||||||
|
@ -13,44 +20,39 @@ CONF_DEBUG = "debug"
|
||||||
|
|
||||||
DISCOVER_SENSORS = "zwave.sensors"
|
DISCOVER_SENSORS = "zwave.sensors"
|
||||||
|
|
||||||
VALUE_SENSOR = 72057594076463104
|
COMMAND_CLASS_SENSOR_BINARY = 48
|
||||||
VALUE_TEMPERATURE = 72057594076479506
|
COMMAND_CLASS_SENSOR_MULTILEVEL = 49
|
||||||
VALUE_LUMINANCE = 72057594076479538
|
COMMAND_CLASS_BATTERY = 128
|
||||||
VALUE_RELATIVE_HUMIDITY = 72057594076479570
|
|
||||||
VALUE_BATTERY_LEVEL = 72057594077773825
|
# 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
|
NETWORK = None
|
||||||
|
|
||||||
|
|
||||||
def get_node_value(node, key):
|
def _obj_to_dict(obj):
|
||||||
""" Helper function to get a node value. """
|
""" Converts an obj into a hash for debug. """
|
||||||
return node.values[key].data if key in node.values else None
|
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):
|
def nice_print_node(node):
|
||||||
""" Prints a nice formatted node to the output """
|
""" Prints a nice formatted node to the output (debug method) """
|
||||||
from pprint import pprint
|
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("\n\n\n")
|
||||||
print("")
|
|
||||||
print("")
|
|
||||||
print("FOUND NODE", node.product_name)
|
print("FOUND NODE", node.product_name)
|
||||||
pprint({key: getattr(node, key) for key
|
pprint(node_dict)
|
||||||
in dir(node)
|
print("\n\n\n")
|
||||||
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("")
|
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
|
@ -58,6 +60,7 @@ def setup(hass, config):
|
||||||
Setup Z-wave.
|
Setup Z-wave.
|
||||||
Will automatically load components to support devices found on the network.
|
Will automatically load components to support devices found on the network.
|
||||||
"""
|
"""
|
||||||
|
# pylint: disable=global-statement, import-error
|
||||||
global NETWORK
|
global NETWORK
|
||||||
|
|
||||||
from louie import connect
|
from louie import connect
|
||||||
|
@ -71,46 +74,42 @@ def setup(hass, config):
|
||||||
config[DOMAIN].get(CONF_USB_STICK_PATH, DEFAULT_CONF_USB_STICK_PATH),
|
config[DOMAIN].get(CONF_USB_STICK_PATH, DEFAULT_CONF_USB_STICK_PATH),
|
||||||
user_path=hass.config_dir)
|
user_path=hass.config_dir)
|
||||||
|
|
||||||
options.set_associate(True)
|
|
||||||
options.set_console_output(use_debug)
|
options.set_console_output(use_debug)
|
||||||
options.lock()
|
options.lock()
|
||||||
|
|
||||||
NETWORK = ZWaveNetwork(options, autostart=False)
|
NETWORK = ZWaveNetwork(options, autostart=False)
|
||||||
|
|
||||||
if use_debug:
|
if use_debug:
|
||||||
def log_all(signal):
|
def log_all(signal, value=None):
|
||||||
|
""" Log all the louie signals. """
|
||||||
print("")
|
print("")
|
||||||
print("LOUIE SIGNAL *****", signal)
|
print("LOUIE SIGNAL *****", signal)
|
||||||
|
if value and signal in (ZWaveNetwork.SIGNAL_VALUE_CHANGED,
|
||||||
|
ZWaveNetwork.SIGNAL_VALUE_ADDED):
|
||||||
|
pprint(_obj_to_dict(value))
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
connect(log_all, weak=False)
|
connect(log_all, weak=False)
|
||||||
|
|
||||||
def zwave_init_done(network):
|
def value_added(node, value):
|
||||||
""" Called when Z-Wave has initialized. """
|
""" Called when a value is added to a node on the network. """
|
||||||
init_sensor = False
|
for component, discovery_service, command_ids in DISCOVERY_COMPONENTS:
|
||||||
|
if value.command_class in command_ids:
|
||||||
# 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')
|
|
||||||
|
|
||||||
# Ensure component is loaded
|
# Ensure component is loaded
|
||||||
if component.DOMAIN not in hass.components:
|
if component not in hass.components:
|
||||||
bootstrap.setup_component(hass, component.DOMAIN, config)
|
bootstrap.setup_component(hass, component, config)
|
||||||
|
|
||||||
# Fire discovery event
|
# Fire discovery event
|
||||||
hass.bus.fire(EVENT_PLATFORM_DISCOVERED, {
|
hass.bus.fire(EVENT_PLATFORM_DISCOVERED, {
|
||||||
ATTR_SERVICE: DISCOVER_SENSORS,
|
ATTR_SERVICE: discovery_service,
|
||||||
ATTR_DISCOVERED: {}
|
ATTR_DISCOVERED: {
|
||||||
|
ATTR_NODE_ID: node.node_id,
|
||||||
|
ATTR_VALUE_ID: value.value_id,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
connect(
|
connect(
|
||||||
zwave_init_done, ZWaveNetwork.SIGNAL_NETWORK_READY, weak=False)
|
value_added, ZWaveNetwork.SIGNAL_VALUE_ADDED, weak=False)
|
||||||
|
|
||||||
def stop_zwave(event):
|
def stop_zwave(event):
|
||||||
""" Stop Z-wave. """
|
""" Stop Z-wave. """
|
||||||
|
|
|
@ -76,8 +76,6 @@ ATTR_LOCATION = "location"
|
||||||
|
|
||||||
ATTR_BATTERY_LEVEL = "battery_level"
|
ATTR_BATTERY_LEVEL = "battery_level"
|
||||||
|
|
||||||
LIGHT_LUX = "lux"
|
|
||||||
|
|
||||||
# #### SERVICES ####
|
# #### SERVICES ####
|
||||||
SERVICE_HOMEASSISTANT_STOP = "stop"
|
SERVICE_HOMEASSISTANT_STOP = "stop"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue