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:
parent
600a3e3965
commit
ec8dc25c9c
3 changed files with 168 additions and 52 deletions
70
homeassistant/components/garage_door/zwave.py
Normal file
70
homeassistant/components/garage_door/zwave.py
Normal 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)
|
|
@ -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
|
||||
|
|
|
@ -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, {})
|
||||
|
|
Loading…
Add table
Reference in a new issue