Get Z-Wave sensors to work with Home Assistant
This commit is contained in:
parent
7f7a1f2740
commit
a013ccf806
8 changed files with 314 additions and 43 deletions
15
Dockerfile
15
Dockerfile
|
@ -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" ]
|
||||
|
|
|
@ -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,19 +55,16 @@ 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
|
||||
hass.track_time_change(update_sensor_states, seconds=range(0, 60, 3))
|
||||
hass.track_time_change(update_sensor_states, second=range(0, 60, 3))
|
||||
|
||||
return True
|
||||
|
|
154
homeassistant/components/sensor/zwave.py
Normal file
154
homeassistant/components/sensor/zwave.py
Normal 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
|
97
homeassistant/components/zwave.py
Normal file
97
homeassistant/components/zwave.py
Normal 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)
|
|
@ -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
28
scripts/build_python_openzwave
Executable 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
|
|
@ -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
18
scripts/dev_openzwave_docker
Executable 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
|
Loading…
Add table
Reference in a new issue