Basic implementation of Zwave Rollershutters (#2313)
* Basic implementation of Zwave Rollershutters * Better filtering, by @wokar * Fix typo * Remove polling from component, and loop fix * linter fix * Filter to channel devices to correct component * Remove overwriting of parent node name
This commit is contained in:
parent
f59e242c63
commit
2e62053629
3 changed files with 126 additions and 10 deletions
|
@ -7,7 +7,6 @@ https://home-assistant.io/components/light.zwave/
|
||||||
# Because we do not compile openzwave on CI
|
# Because we do not compile openzwave on CI
|
||||||
# pylint: disable=import-error
|
# pylint: disable=import-error
|
||||||
from threading import Timer
|
from threading import Timer
|
||||||
|
|
||||||
from homeassistant.components.light import ATTR_BRIGHTNESS, DOMAIN, Light
|
from homeassistant.components.light import ATTR_BRIGHTNESS, DOMAIN, Light
|
||||||
from homeassistant.components import zwave
|
from homeassistant.components import zwave
|
||||||
from homeassistant.const import STATE_OFF, STATE_ON
|
from homeassistant.const import STATE_OFF, STATE_ON
|
||||||
|
|
86
homeassistant/components/rollershutter/zwave.py
Normal file
86
homeassistant/components/rollershutter/zwave.py
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
"""
|
||||||
|
Support for Zwave roller shutter components.
|
||||||
|
|
||||||
|
For more details about this platform, please refer to the documentation
|
||||||
|
https://home-assistant.io/components/rollershutter.zwave/
|
||||||
|
"""
|
||||||
|
# Because we do not compile openzwave on CI
|
||||||
|
# pylint: disable=import-error
|
||||||
|
import logging
|
||||||
|
from homeassistant.components.rollershutter import DOMAIN
|
||||||
|
from homeassistant.components.zwave import ZWaveDeviceEntity
|
||||||
|
from homeassistant.components import zwave
|
||||||
|
from homeassistant.components.rollershutter import RollershutterDevice
|
||||||
|
|
||||||
|
COMMAND_CLASS_SWITCH_MULTILEVEL = 0x26 # 38
|
||||||
|
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 roller shutters."""
|
||||||
|
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_MULTILEVEL:
|
||||||
|
return
|
||||||
|
if value.index != 1:
|
||||||
|
return
|
||||||
|
|
||||||
|
value.set_change_verified(False)
|
||||||
|
add_devices([ZwaveRollershutter(value)])
|
||||||
|
|
||||||
|
|
||||||
|
class ZwaveRollershutter(zwave.ZWaveDeviceEntity, RollershutterDevice):
|
||||||
|
"""Representation of an Zwave roller shutter."""
|
||||||
|
|
||||||
|
def __init__(self, value):
|
||||||
|
"""Initialize the zwave rollershutter."""
|
||||||
|
from openzwave.network import ZWaveNetwork
|
||||||
|
from pydispatch import dispatcher
|
||||||
|
ZWaveDeviceEntity.__init__(self, value, DOMAIN)
|
||||||
|
self._node = value.node
|
||||||
|
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.update_ha_state(True)
|
||||||
|
_LOGGER.debug("Value changed on network %s", value)
|
||||||
|
|
||||||
|
@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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
def stop(self, **kwargs):
|
||||||
|
"""Stop the roller shutter."""
|
||||||
|
for value in self._node.get_values(
|
||||||
|
class_id=COMMAND_CLASS_SWITCH_BINARY).values():
|
||||||
|
# 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
|
|
@ -50,6 +50,14 @@ COMMAND_CLASS_ALARM = 113 # 0x71
|
||||||
COMMAND_CLASS_THERMOSTAT_SETPOINT = 67 # 0x43
|
COMMAND_CLASS_THERMOSTAT_SETPOINT = 67 # 0x43
|
||||||
COMMAND_CLASS_THERMOSTAT_FAN_MODE = 68 # 0x44
|
COMMAND_CLASS_THERMOSTAT_FAN_MODE = 68 # 0x44
|
||||||
|
|
||||||
|
SPECIFIC_DEVICE_CLASS_WHATEVER = None
|
||||||
|
SPECIFIC_DEVICE_CLASS_MULTILEVEL_POWER_SWITCH = 1
|
||||||
|
SPECIFIC_DEVICE_CLASS_MULTIPOSITION_MOTOR = 3
|
||||||
|
SPECIFIC_DEVICE_CLASS_MULTILEVEL_SCENE = 4
|
||||||
|
SPECIFIC_DEVICE_CLASS_MOTOR_CONTROL_CLASS_A = 5
|
||||||
|
SPECIFIC_DEVICE_CLASS_MOTOR_CONTROL_CLASS_B = 6
|
||||||
|
SPECIFIC_DEVICE_CLASS_MOTOR_CONTROL_CLASS_C = 7
|
||||||
|
|
||||||
GENRE_WHATEVER = None
|
GENRE_WHATEVER = None
|
||||||
GENRE_USER = "User"
|
GENRE_USER = "User"
|
||||||
|
|
||||||
|
@ -60,38 +68,54 @@ TYPE_DECIMAL = "Decimal"
|
||||||
|
|
||||||
|
|
||||||
# List of tuple (DOMAIN, discovered service, supported command classes,
|
# List of tuple (DOMAIN, discovered service, supported command classes,
|
||||||
# value type).
|
# value type, genre type, specific device class).
|
||||||
DISCOVERY_COMPONENTS = [
|
DISCOVERY_COMPONENTS = [
|
||||||
('sensor',
|
('sensor',
|
||||||
[COMMAND_CLASS_SENSOR_MULTILEVEL,
|
[COMMAND_CLASS_SENSOR_MULTILEVEL,
|
||||||
COMMAND_CLASS_METER,
|
COMMAND_CLASS_METER,
|
||||||
COMMAND_CLASS_ALARM],
|
COMMAND_CLASS_ALARM],
|
||||||
TYPE_WHATEVER,
|
TYPE_WHATEVER,
|
||||||
GENRE_USER),
|
GENRE_USER,
|
||||||
|
SPECIFIC_DEVICE_CLASS_WHATEVER),
|
||||||
('light',
|
('light',
|
||||||
[COMMAND_CLASS_SWITCH_MULTILEVEL],
|
[COMMAND_CLASS_SWITCH_MULTILEVEL],
|
||||||
TYPE_BYTE,
|
TYPE_BYTE,
|
||||||
GENRE_USER),
|
GENRE_USER,
|
||||||
|
[SPECIFIC_DEVICE_CLASS_MULTILEVEL_POWER_SWITCH,
|
||||||
|
SPECIFIC_DEVICE_CLASS_MULTILEVEL_SCENE]),
|
||||||
('switch',
|
('switch',
|
||||||
[COMMAND_CLASS_SWITCH_BINARY],
|
[COMMAND_CLASS_SWITCH_BINARY],
|
||||||
TYPE_BOOL,
|
TYPE_BOOL,
|
||||||
GENRE_USER),
|
GENRE_USER,
|
||||||
|
SPECIFIC_DEVICE_CLASS_WHATEVER),
|
||||||
('binary_sensor',
|
('binary_sensor',
|
||||||
[COMMAND_CLASS_SENSOR_BINARY],
|
[COMMAND_CLASS_SENSOR_BINARY],
|
||||||
TYPE_BOOL,
|
TYPE_BOOL,
|
||||||
GENRE_USER),
|
GENRE_USER,
|
||||||
|
SPECIFIC_DEVICE_CLASS_WHATEVER),
|
||||||
('thermostat',
|
('thermostat',
|
||||||
[COMMAND_CLASS_THERMOSTAT_SETPOINT],
|
[COMMAND_CLASS_THERMOSTAT_SETPOINT],
|
||||||
TYPE_WHATEVER,
|
TYPE_WHATEVER,
|
||||||
GENRE_WHATEVER),
|
GENRE_WHATEVER,
|
||||||
|
SPECIFIC_DEVICE_CLASS_WHATEVER),
|
||||||
('hvac',
|
('hvac',
|
||||||
[COMMAND_CLASS_THERMOSTAT_FAN_MODE],
|
[COMMAND_CLASS_THERMOSTAT_FAN_MODE],
|
||||||
TYPE_WHATEVER,
|
TYPE_WHATEVER,
|
||||||
GENRE_WHATEVER),
|
GENRE_WHATEVER,
|
||||||
|
SPECIFIC_DEVICE_CLASS_WHATEVER),
|
||||||
('lock',
|
('lock',
|
||||||
[COMMAND_CLASS_DOOR_LOCK],
|
[COMMAND_CLASS_DOOR_LOCK],
|
||||||
TYPE_BOOL,
|
TYPE_BOOL,
|
||||||
GENRE_USER),
|
GENRE_USER,
|
||||||
|
SPECIFIC_DEVICE_CLASS_WHATEVER),
|
||||||
|
('rollershutter',
|
||||||
|
[COMMAND_CLASS_SWITCH_MULTILEVEL],
|
||||||
|
TYPE_WHATEVER,
|
||||||
|
GENRE_USER,
|
||||||
|
[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]),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -222,7 +246,8 @@ def setup(hass, config):
|
||||||
for (component,
|
for (component,
|
||||||
command_ids,
|
command_ids,
|
||||||
value_type,
|
value_type,
|
||||||
value_genre) in DISCOVERY_COMPONENTS:
|
value_genre,
|
||||||
|
specific_device_class) in DISCOVERY_COMPONENTS:
|
||||||
|
|
||||||
if value.command_class not in command_ids:
|
if value.command_class not in command_ids:
|
||||||
continue
|
continue
|
||||||
|
@ -230,8 +255,14 @@ def setup(hass, config):
|
||||||
continue
|
continue
|
||||||
if value_genre is not None and value_genre != value.genre:
|
if value_genre is not None and value_genre != value.genre:
|
||||||
continue
|
continue
|
||||||
|
if specific_device_class is not None and \
|
||||||
|
specific_device_class != node.specific:
|
||||||
|
continue
|
||||||
|
|
||||||
# Configure node
|
# 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)
|
||||||
name = "{}.{}".format(component, _object_id(value))
|
name = "{}.{}".format(component, _object_id(value))
|
||||||
|
|
||||||
node_config = customize.get(name, {})
|
node_config = customize.get(name, {})
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue