Zwave garagedoor (#2361)

* First go at zwave Garage door

* Refactor of zwave discovery

* Allaround fixes for rollershutter and garage door
This commit is contained in:
John Arild Berentsen 2016-06-24 17:44:24 +02:00 committed by Josh Wright
parent 600a3e3965
commit ec8dc25c9c
3 changed files with 168 additions and 52 deletions

View file

@ -0,0 +1,70 @@
"""
Support for Zwave garage door components.
For more details about this platform, please refer to the documentation
https://home-assistant.io/components/garagedoor.zwave/
"""
# Because we do not compile openzwave on CI
# pylint: disable=import-error
import logging
from homeassistant.components.garage_door import DOMAIN
from homeassistant.components.zwave import ZWaveDeviceEntity
from homeassistant.components import zwave
from homeassistant.components.garage_door import GarageDoorDevice
COMMAND_CLASS_SWITCH_BINARY = 0x25 # 37
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Find and return Z-Wave garage door device."""
if discovery_info is None or zwave.NETWORK is None:
return
node = zwave.NETWORK.nodes[discovery_info[zwave.ATTR_NODE_ID]]
value = node.values[discovery_info[zwave.ATTR_VALUE_ID]]
if value.command_class != zwave.COMMAND_CLASS_SWITCH_BINARY:
return
if value.type != zwave.TYPE_BOOL:
return
if value.genre != zwave.GENRE_USER:
return
value.set_change_verified(False)
add_devices([ZwaveGarageDoor(value)])
class ZwaveGarageDoor(zwave.ZWaveDeviceEntity, GarageDoorDevice):
"""Representation of an Zwave garage door device."""
def __init__(self, value):
"""Initialize the zwave garage door."""
from openzwave.network import ZWaveNetwork
from pydispatch import dispatcher
ZWaveDeviceEntity.__init__(self, value, DOMAIN)
self._node = value.node
self._state = value.data
dispatcher.connect(
self.value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED)
def value_changed(self, value):
"""Called when a value has changed on the network."""
if self._value.node == value.node:
self._state = value.data
self.update_ha_state(True)
_LOGGER.debug("Value changed on network %s", value)
@property
def is_closed(self):
"""Return the current position of Zwave garage door."""
return self._state
def close_door(self):
"""Close the garage door."""
self._value.node.set_switch(self._value.value_id, False)
def open_door(self):
"""Open the garage door."""
self._value.node.set_switch(self._value.value_id, True)

View file

@ -28,7 +28,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
if value.command_class != zwave.COMMAND_CLASS_SWITCH_MULTILEVEL:
return
if value.index != 1:
if value.index != 0:
return
value.set_change_verified(False)
@ -56,26 +56,15 @@ class ZwaveRollershutter(zwave.ZWaveDeviceEntity, RollershutterDevice):
@property
def current_position(self):
"""Return the current position of Zwave roller shutter."""
for value in self._node.get_values(
class_id=COMMAND_CLASS_SWITCH_MULTILEVEL).values():
if value.command_class == 38 and value.index == 0:
return value.data
return self._value.data
def move_up(self, **kwargs):
"""Move the roller shutter up."""
for value in self._node.get_values(
class_id=COMMAND_CLASS_SWITCH_MULTILEVEL).values():
if value.command_class == 38 and value.index == 0:
value.data = 255
break
self._node.set_dimmer(self._value.value_id, 0)
def move_down(self, **kwargs):
"""Move the roller shutter down."""
for value in self._node.get_values(
class_id=COMMAND_CLASS_SWITCH_MULTILEVEL).values():
if value.command_class == 38 and value.index == 0:
value.data = 0
break
self._node.set_dimmer(self._value.value_id, 100)
def stop(self, **kwargs):
"""Stop the roller shutter."""
@ -84,3 +73,4 @@ class ZwaveRollershutter(zwave.ZWaveDeviceEntity, RollershutterDevice):
# Rollershutter will toggle between UP (True), DOWN (False).
# It also stops the shutter if the same value is sent while moving.
value.data = value.data
break

View file

@ -39,23 +39,39 @@ SERVICE_TEST_NETWORK = "test_network"
EVENT_SCENE_ACTIVATED = "zwave.scene_activated"
COMMAND_CLASS_SWITCH_MULTILEVEL = 38
COMMAND_CLASS_DOOR_LOCK = 98
COMMAND_CLASS_SWITCH_BINARY = 37
COMMAND_CLASS_SENSOR_BINARY = 48
COMMAND_CLASS_WHATEVER = None
COMMAND_CLASS_SENSOR_MULTILEVEL = 49
COMMAND_CLASS_METER = 50
COMMAND_CLASS_ALARM = 113
COMMAND_CLASS_SWITCH_BINARY = 37
COMMAND_CLASS_SENSOR_BINARY = 48
COMMAND_CLASS_SWITCH_MULTILEVEL = 38
COMMAND_CLASS_DOOR_LOCK = 98
COMMAND_CLASS_THERMOSTAT_SETPOINT = 67
COMMAND_CLASS_THERMOSTAT_FAN_MODE = 68
COMMAND_CLASS_BATTERY = 128
COMMAND_CLASS_ALARM = 113 # 0x71
COMMAND_CLASS_THERMOSTAT_SETPOINT = 67 # 0x43
COMMAND_CLASS_THERMOSTAT_FAN_MODE = 68 # 0x44
GENERIC_COMMAND_CLASS_WHATEVER = None
GENERIC_COMMAND_CLASS_MULTILEVEL_SWITCH = 17
GENERIC_COMMAND_CLASS_BINARY_SWITCH = 16
GENERIC_COMMAND_CLASS_ENTRY_CONTROL = 64
GENERIC_COMMAND_CLASS_BINARY_SENSOR = 32
GENERIC_COMMAND_CLASS_MULTILEVEL_SENSOR = 33
GENERIC_COMMAND_CLASS_METER = 49
GENERIC_COMMAND_CLASS_ALARM_SENSOR = 161
GENERIC_COMMAND_CLASS_THERMOSTAT = 8
SPECIFIC_DEVICE_CLASS_WHATEVER = None
SPECIFIC_DEVICE_CLASS_NOT_USED = 0
SPECIFIC_DEVICE_CLASS_MULTILEVEL_POWER_SWITCH = 1
SPECIFIC_DEVICE_CLASS_ADVANCED_DOOR_LOCK = 2
SPECIFIC_DEVICE_CLASS_MULTIPOSITION_MOTOR = 3
SPECIFIC_DEVICE_CLASS_SECURE_KEYPAD_DOOR_LOCK = 3
SPECIFIC_DEVICE_CLASS_MULTILEVEL_SCENE = 4
SPECIFIC_DEVICE_CLASS_SECURE_DOOR = 5
SPECIFIC_DEVICE_CLASS_MOTOR_CONTROL_CLASS_A = 5
SPECIFIC_DEVICE_CLASS_MOTOR_CONTROL_CLASS_B = 6
SPECIFIC_DEVICE_CLASS_SECURE_BARRIER_ADD_ON = 7
SPECIFIC_DEVICE_CLASS_MOTOR_CONTROL_CLASS_C = 7
GENRE_WHATEVER = None
@ -71,51 +87,67 @@ TYPE_DECIMAL = "Decimal"
# value type, genre type, specific device class).
DISCOVERY_COMPONENTS = [
('sensor',
[GENERIC_COMMAND_CLASS_WHATEVER],
[SPECIFIC_DEVICE_CLASS_WHATEVER],
[COMMAND_CLASS_SENSOR_MULTILEVEL,
COMMAND_CLASS_METER,
COMMAND_CLASS_ALARM],
TYPE_WHATEVER,
GENRE_USER,
SPECIFIC_DEVICE_CLASS_WHATEVER),
GENRE_USER),
('light',
[GENERIC_COMMAND_CLASS_MULTILEVEL_SWITCH],
[SPECIFIC_DEVICE_CLASS_MULTILEVEL_POWER_SWITCH,
SPECIFIC_DEVICE_CLASS_MULTILEVEL_SCENE],
[COMMAND_CLASS_SWITCH_MULTILEVEL],
TYPE_BYTE,
GENRE_USER,
[SPECIFIC_DEVICE_CLASS_MULTILEVEL_POWER_SWITCH,
SPECIFIC_DEVICE_CLASS_MULTILEVEL_SCENE]),
GENRE_USER),
('switch',
[GENERIC_COMMAND_CLASS_BINARY_SWITCH],
[SPECIFIC_DEVICE_CLASS_WHATEVER],
[COMMAND_CLASS_SWITCH_BINARY],
TYPE_BOOL,
GENRE_USER,
SPECIFIC_DEVICE_CLASS_WHATEVER),
GENRE_USER),
('binary_sensor',
[GENERIC_COMMAND_CLASS_BINARY_SENSOR],
[SPECIFIC_DEVICE_CLASS_WHATEVER],
[COMMAND_CLASS_SENSOR_BINARY],
TYPE_BOOL,
GENRE_USER,
SPECIFIC_DEVICE_CLASS_WHATEVER),
GENRE_USER),
('thermostat',
[GENERIC_COMMAND_CLASS_THERMOSTAT],
[SPECIFIC_DEVICE_CLASS_WHATEVER],
[COMMAND_CLASS_THERMOSTAT_SETPOINT],
TYPE_WHATEVER,
GENRE_WHATEVER,
SPECIFIC_DEVICE_CLASS_WHATEVER),
GENRE_WHATEVER),
('hvac',
[GENERIC_COMMAND_CLASS_THERMOSTAT],
[SPECIFIC_DEVICE_CLASS_WHATEVER],
[COMMAND_CLASS_THERMOSTAT_FAN_MODE],
TYPE_WHATEVER,
GENRE_WHATEVER,
SPECIFIC_DEVICE_CLASS_WHATEVER),
GENRE_WHATEVER),
('lock',
[GENERIC_COMMAND_CLASS_ENTRY_CONTROL],
[SPECIFIC_DEVICE_CLASS_ADVANCED_DOOR_LOCK,
SPECIFIC_DEVICE_CLASS_SECURE_KEYPAD_DOOR_LOCK],
[COMMAND_CLASS_DOOR_LOCK],
TYPE_BOOL,
GENRE_USER,
SPECIFIC_DEVICE_CLASS_WHATEVER),
GENRE_USER),
('rollershutter',
[COMMAND_CLASS_SWITCH_MULTILEVEL],
TYPE_WHATEVER,
GENRE_USER,
[GENERIC_COMMAND_CLASS_MULTILEVEL_SWITCH],
[SPECIFIC_DEVICE_CLASS_MOTOR_CONTROL_CLASS_A,
SPECIFIC_DEVICE_CLASS_MOTOR_CONTROL_CLASS_B,
SPECIFIC_DEVICE_CLASS_MOTOR_CONTROL_CLASS_C,
SPECIFIC_DEVICE_CLASS_MULTIPOSITION_MOTOR]),
SPECIFIC_DEVICE_CLASS_MULTIPOSITION_MOTOR],
[COMMAND_CLASS_WHATEVER],
TYPE_WHATEVER,
GENRE_USER),
('garage_door',
[GENERIC_COMMAND_CLASS_ENTRY_CONTROL],
[SPECIFIC_DEVICE_CLASS_SECURE_BARRIER_ADD_ON,
SPECIFIC_DEVICE_CLASS_SECURE_DOOR],
[COMMAND_CLASS_SWITCH_BINARY],
TYPE_BOOL,
GENRE_USER)
]
@ -244,25 +276,49 @@ def setup(hass, config):
def value_added(node, value):
"""Called when a value is added to a node on the network."""
for (component,
command_ids,
generic_device_class,
specific_device_class,
command_class,
value_type,
value_genre,
specific_device_class) in DISCOVERY_COMPONENTS:
value_genre) in DISCOVERY_COMPONENTS:
if value.command_class not in command_ids:
_LOGGER.debug("Component=%s Node_id=%s query start",
component, node.node_id)
if node.generic not in generic_device_class and \
None not in generic_device_class:
_LOGGER.debug("node.generic %s not None and in \
generic_device_class %s",
node.generic, generic_device_class)
continue
if value_type is not None and value_type != value.type:
if node.specific not in specific_device_class and \
None not in specific_device_class:
_LOGGER.debug("node.specific %s is not None and in \
specific_device_class %s", node.specific,
specific_device_class)
continue
if value_genre is not None and value_genre != value.genre:
if value.command_class not in command_class and \
None not in command_class:
_LOGGER.debug("value.command_class %s is not None \
and in command_class %s",
value.command_class, command_class)
continue
if specific_device_class is not None and \
specific_device_class != node.specific:
if value_type != value.type and value_type is not None:
_LOGGER.debug("value.type %s != value_type %s",
value.type, value_type)
continue
if value_genre != value.genre and value_genre is not None:
_LOGGER.debug("value.genre %s != value_genre %s",
value.genre, value_genre)
continue
# Configure node
_LOGGER.debug("Node_id=%s Value type=%s Genre=%s \
Specific Device_class=%s", node.node_id,
value.type, value.genre, specific_device_class)
_LOGGER.debug("Adding Node_id=%s Generic_command_class=%s, \
Specific_command_class=%s, \
Command_class=%s, Value type=%s, \
Genre=%s", node.node_id,
node.generic, node.specific,
value.command_class, value.type,
value.genre)
name = "{}.{}".format(component, _object_id(value))
node_config = customize.get(name, {})