Get Z-Wave sensors to work with Home Assistant

This commit is contained in:
Paulus Schoutsen 2015-02-22 17:36:28 -08:00
parent 85f7f5589d
commit e7b9b86c64
8 changed files with 313 additions and 42 deletions

View file

@ -7,19 +7,6 @@ RUN apt-get update && \
apt-get install -y cython3 libudev-dev python-sphinx python3-setuptools mercurial && \
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \
pip3 install cython && \
cd .. && \
git clone https://github.com/Artanis/louie.git && \
cd louie && \
python setup.py install && \
cd .. && \
hg clone https://code.google.com/r/balloob-python-openzwave/ && \
cd balloob-python-openzwave && \
./update.sh && \
sed -i '253s/.*//' openzwave/cpp/src/value_classes/ValueID.h && \
./compile.sh && \
./install.sh
# L18 sed is to apply a patch to make openzwave compile
# L19 2to3 to have the api code work in Python 3
scripts/build_python_openzwave
CMD [ "python", "-m", "homeassistant", "--config", "/config" ]

View file

@ -8,19 +8,13 @@ from datetime import timedelta
from homeassistant.loader import get_component
import homeassistant.util as util
from homeassistant.const import (
STATE_OPEN)
from homeassistant.helpers import (
platform_devices_from_config)
from homeassistant.components import group, discovery, wink
platform_devices_from_config, generate_entity_id)
from homeassistant.components import discovery, wink, zwave
DOMAIN = 'sensor'
DEPENDENCIES = []
GROUP_NAME_ALL_SENSORS = 'all_sensors'
ENTITY_ID_ALL_SENSORS = group.ENTITY_ID_FORMAT.format(
GROUP_NAME_ALL_SENSORS)
ENTITY_ID_FORMAT = DOMAIN + '.{}'
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=1)
@ -28,18 +22,12 @@ MIN_TIME_BETWEEN_SCANS = timedelta(seconds=1)
# Maps discovered services to their platforms
DISCOVERY_PLATFORMS = {
wink.DISCOVER_SENSORS: 'wink',
zwave.DISCOVER_SENSORS: 'zwave',
}
_LOGGER = logging.getLogger(__name__)
def is_on(hass, entity_id=None):
""" Returns if the sensor is open based on the statemachine. """
entity_id = entity_id or ENTITY_ID_ALL_SENSORS
return hass.states.is_state(entity_id, STATE_OPEN)
def setup(hass, config):
""" Track states and offer events for sensors. """
logger = logging.getLogger(__name__)
@ -58,10 +46,6 @@ def setup(hass, config):
update_sensor_states(None)
# Track all sensors in a group
sensor_group = group.Group(
hass, GROUP_NAME_ALL_SENSORS, sensors.keys(), False)
def sensor_discovered(service, info):
""" Called when a sensor is discovered. """
platform = get_component("{}.{}".format(
@ -71,16 +55,13 @@ def setup(hass, config):
for sensor in discovered:
if sensor is not None and sensor not in sensors.values():
sensor.entity_id = util.ensure_unique_string(
ENTITY_ID_FORMAT.format(util.slugify(sensor.name)),
sensors.keys())
sensor.entity_id = generate_entity_id(
ENTITY_ID_FORMAT, sensor.name, sensors.keys())
sensors[sensor.entity_id] = sensor
sensor.update_ha_state(hass)
sensor_group.update_tracked_entity_ids(sensors.keys())
discovery.listen(hass, DISCOVERY_PLATFORMS.keys(), sensor_discovered)
# Fire every 3 seconds

View file

@ -0,0 +1,154 @@
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)
def devices_discovered(hass, config, info):
""" """
from louie import connect
from openzwave.network import ZWaveNetwork
VALUE_CLASS_MAP = {
zwave.VALUE_TEMPERATURE: ZWaveTemperatureSensor,
zwave.VALUE_LUMINANCE: ZWaveLuminanceSensor,
zwave.VALUE_RELATIVE_HUMIDITY: ZWaveRelativeHumiditySensor,
}
sensors = []
for node in zwave.NETWORK.nodes.values():
for value, klass in VALUE_CLASS_MAP.items():
if value in node.values:
sensors.append(klass(node))
if sensors[-1] is None:
print("")
print("WTF BBQ")
print(node, klass)
print("")
continue
def value_changed(network, node, value):
""" """
print("")
print("")
print("")
print("ValueChanged in sensor !!", node, value)
print("")
print("")
print("")
# triggered when sensors have updated
connect(value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED, weak=False)
return sensors
class ZWaveSensor(Device):
def __init__(self, node, sensor_value):
self._node = node
self._value = node.values[sensor_value]
@property
def unique_id(self):
""" Returns a unique id. """
return "ZWAVE-{}-{}".format(self._node.node_id, self._value)
@property
def name(self):
""" Returns the name of the device. """
name = self._node.name or "{} {}".format(
self._node.manufacturer_name, self._node.product_name)
return "{} {}".format(name, self._value.label)
@property
def state(self):
""" Returns the state of the sensor. """
return self._value.data
@property
def state_attributes(self):
""" Returns the state attributes. """
attrs = {
ATTR_FRIENDLY_NAME: self.name
}
battery_level = zwave.get_node_value(
self._node, zwave.VALUE_BATTERY_LEVEL)
if battery_level is not None:
attrs[ATTR_BATTERY_LEVEL] = battery_level
unit = self.unit
if unit is not None:
attrs[ATTR_UNIT_OF_MEASUREMENT] = unit
location = self._node.location
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 {}
class ZWaveTemperatureSensor(ZWaveSensor):
""" Represents a ZWave Temperature Sensor. """
def __init__(self, node):
super().__init__(node, zwave.VALUE_TEMPERATURE)
@property
def state(self):
""" Returns the state of the sensor. """
return round(self._value.data/1000, 1)
@property
def unit(self):
""" Unit of this sensor. """
unit = self._value.units
if unit == 'C':
return TEMP_CELCIUS
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

View file

@ -0,0 +1,97 @@
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)
DOMAIN = "zwave"
DEPENDENCIES = []
CONF_USB_STICK_PATH = "usb_path"
DEFAULT_CONF_USB_STICK_PATH = "/zwaveusbstick"
CONF_DEBUG = "debug"
DISCOVER_SENSORS = "zwave.sensors"
VALUE_SENSOR = 72057594076463104
VALUE_TEMPERATURE = 72057594076479506
VALUE_LUMINANCE = 72057594076479538
VALUE_RELATIVE_HUMIDITY = 72057594076479570
VALUE_BATTERY_LEVEL = 72057594077773825
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 setup(hass, config):
"""
Setup Z-wave.
Will automatically load components to support devices found on the network.
"""
global NETWORK
from louie import connect
from openzwave.option import ZWaveOption
from openzwave.network import ZWaveNetwork
# Setup options
options = ZWaveOption(
config[DOMAIN].get(CONF_USB_STICK_PATH, DEFAULT_CONF_USB_STICK_PATH),
user_path=hass.config_dir)
if config[DOMAIN].get(CONF_DEBUG) == '1':
options.set_console_output(True)
options.lock()
NETWORK = ZWaveNetwork(options, autostart=False)
def log_all(signal):
print("")
print("LOG ALL")
print(signal)
print("")
print("")
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 get_node_value(node, VALUE_SENSOR) and not init_sensor:
init_sensor = True
component = get_component('sensor')
# Ensure component is loaded
if component.DOMAIN not in hass.components:
bootstrap.setup_component(hass, component.DOMAIN, config)
# Fire discovery event
hass.bus.fire(EVENT_PLATFORM_DISCOVERED, {
ATTR_SERVICE: DISCOVER_SENSORS,
ATTR_DISCOVERED: {}
})
connect(
zwave_init_done, ZWaveNetwork.SIGNAL_NETWORK_READY, weak=False)
def stop_zwave(event):
""" Stop Z-wave. """
NETWORK.stop()
def start_zwave(event):
""" Called when Home Assistant starts up. """
NETWORK.start()
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_zwave)
hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_zwave)

View file

@ -71,6 +71,13 @@ TEMP_FAHRENHEIT = "°F"
# Contains the information that is discovered
ATTR_DISCOVERED = "discovered"
# Location of the device/sensor
ATTR_LOCATION = "location"
ATTR_BATTERY_LEVEL = "battery_level"
LIGHT_LUX = "LUX"
# #### SERVICES ####
SERVICE_HOMEASSISTANT_STOP = "stop"

28
scripts/build_python_openzwave Executable file
View file

@ -0,0 +1,28 @@
# Sets up and builds python open zwave to be used with Home Assistant
# Dependencies that need to be installed:
# apt-get install cython3 libudev-dev python-sphinx python3-setuptools mercurial
# pip3 install cython
# If current pwd is scripts, go 1 up.
if [ ${PWD##*/} == "scripts" ]; then
cd ..
fi
cd ..
# We need to install louie here or else python-openzwave install
# will download louie from PIP and that one is not compatible with Python 3
git clone https://github.com/balloob/louie.git
cd louie
python setup.py install
cd ..
hg clone https://code.google.com/r/balloob-python-openzwave/
cd balloob-python-openzwave
./update.sh
# Fix an issue with openzwave
sed -i '253s/.*//' openzwave/cpp/src/value_classes/ValueID.h
./compile.sh
./install.sh

View file

@ -1,3 +1,5 @@
# Build and run Home Assinstant in Docker
# Optional: pass in a timezone as first argument
# If not given will attempt to mount /etc/localtime
@ -8,8 +10,6 @@ fi
docker build -t home-assistant-dev .
# TODO set device via command line, remove /bin/bash
if [ $# -gt 0 ]
then
docker run \
@ -18,8 +18,7 @@ then
-e "TZ=$1" \
-v `pwd`:/usr/src/app \
-v `pwd`/config:/config \
-t -i home-assistant-dev \
/bin/bash
-t -i home-assistant-dev
else
docker run \

18
scripts/dev_openzwave_docker Executable file
View file

@ -0,0 +1,18 @@
# Open a docker that can be used to debug/dev python-openzwave
# Pass in a command line argument to skip build
# If current pwd is scripts, go 1 up.
if [ ${PWD##*/} == "scripts" ]; then
cd ..
fi
if [ $# -gt 0 ]
then
docker build -t home-assistant-dev .
fi
docker run \
--device=/dev/ttyUSB0:/zwaveusbstick:rwm \
-v `pwd`:/usr/src/app \
-t -i home-assistant-dev \
/bin/bash