Revert "Refactor zwave discovery to entity schema (#6445)" (#6564)

This reverts commit 56abc7f9b4.
pull/6526/head
Paulus Schoutsen 2017-03-12 23:35:10 -07:00 committed by GitHub
parent 56abc7f9b4
commit 58826b264a
11 changed files with 565 additions and 658 deletions

View File

@ -19,34 +19,34 @@ _LOGGER = logging.getLogger(__name__)
DEPENDENCIES = []
def get_device(values, **kwargs):
def get_device(value, **kwargs):
"""Create zwave entity device."""
device_mapping = workaround.get_device_mapping(values.primary)
device_mapping = workaround.get_device_mapping(value)
if device_mapping == workaround.WORKAROUND_NO_OFF_EVENT:
# Default the multiplier to 4
re_arm_multiplier = zwave.get_config_value(values.primary.node, 9) or 4
return ZWaveTriggerSensor(values, "motion", re_arm_multiplier * 8)
re_arm_multiplier = (zwave.get_config_value(value.node, 9) or 4)
return ZWaveTriggerSensor(value, "motion", re_arm_multiplier * 8)
if workaround.get_device_component_mapping(values.primary) == DOMAIN:
return ZWaveBinarySensor(values, None)
if workaround.get_device_component_mapping(value) == DOMAIN:
return ZWaveBinarySensor(value, None)
if values.primary.command_class == zwave.const.COMMAND_CLASS_SENSOR_BINARY:
return ZWaveBinarySensor(values, None)
if value.command_class == zwave.const.COMMAND_CLASS_SENSOR_BINARY:
return ZWaveBinarySensor(value, None)
return None
class ZWaveBinarySensor(BinarySensorDevice, zwave.ZWaveDeviceEntity):
"""Representation of a binary sensor within Z-Wave."""
def __init__(self, values, device_class):
def __init__(self, value, device_class):
"""Initialize the sensor."""
zwave.ZWaveDeviceEntity.__init__(self, values, DOMAIN)
zwave.ZWaveDeviceEntity.__init__(self, value, DOMAIN)
self._sensor_type = device_class
self._state = self.values.primary.data
self._state = self._value.data
def update_properties(self):
"""Callback on data changes for node values."""
self._state = self.values.primary.data
self._state = self._value.data
@property
def is_on(self):
@ -62,15 +62,15 @@ class ZWaveBinarySensor(BinarySensorDevice, zwave.ZWaveDeviceEntity):
class ZWaveTriggerSensor(ZWaveBinarySensor):
"""Representation of a stateless sensor within Z-Wave."""
def __init__(self, values, device_class, re_arm_sec=60):
def __init__(self, value, device_class, re_arm_sec=60):
"""Initialize the sensor."""
super(ZWaveTriggerSensor, self).__init__(values, device_class)
super(ZWaveTriggerSensor, self).__init__(value, device_class)
self.re_arm_sec = re_arm_sec
self.invalidate_after = None
def update_properties(self):
"""Called when a value for this entity's node has changed."""
self._state = self.values.primary.data
self._state = self._value.data
# only allow this value to be true for re_arm secs
if not self.hass:
return

View File

@ -10,6 +10,7 @@ import logging
from homeassistant.components.climate import DOMAIN
from homeassistant.components.climate import ClimateDevice
from homeassistant.components.zwave import ZWaveDeviceEntity
from homeassistant.components import zwave
from homeassistant.components.zwave import async_setup_platform # noqa # pylint: disable=unused-import
from homeassistant.const import (
TEMP_CELSIUS, TEMP_FAHRENHEIT, ATTR_TEMPERATURE)
@ -32,18 +33,20 @@ DEVICE_MAPPINGS = {
}
def get_device(hass, values, **kwargs):
def get_device(hass, value, **kwargs):
"""Create zwave entity device."""
temp_unit = hass.config.units.temperature_unit
return ZWaveClimate(values, temp_unit)
return ZWaveClimate(value, temp_unit)
class ZWaveClimate(ZWaveDeviceEntity, ClimateDevice):
"""Representation of a Z-Wave Climate device."""
def __init__(self, values, temp_unit):
def __init__(self, value, temp_unit):
"""Initialize the Z-Wave climate device."""
ZWaveDeviceEntity.__init__(self, values, DOMAIN)
ZWaveDeviceEntity.__init__(self, value, DOMAIN)
self._index = value.index
self._node = value.node
self._target_temperature = None
self._current_temperature = None
self._current_operation = None
@ -58,11 +61,10 @@ class ZWaveClimate(ZWaveDeviceEntity, ClimateDevice):
_LOGGER.debug("temp_unit is %s", self._unit)
self._zxt_120 = None
# Make sure that we have values for the key before converting to int
if (self.node.manufacturer_id.strip() and
self.node.product_id.strip()):
specific_sensor_key = (
int(self.node.manufacturer_id, 16),
int(self.node.product_id, 16))
if (value.node.manufacturer_id.strip() and
value.node.product_id.strip()):
specific_sensor_key = (int(value.node.manufacturer_id, 16),
int(value.node.product_id, 16))
if specific_sensor_key in DEVICE_MAPPINGS:
if DEVICE_MAPPINGS[specific_sensor_key] == WORKAROUND_ZXT_120:
_LOGGER.debug("Remotec ZXT-120 Zwave Thermostat"
@ -73,58 +75,81 @@ class ZWaveClimate(ZWaveDeviceEntity, ClimateDevice):
def update_properties(self):
"""Callback on data changes for node values."""
# Operation Mode
if self.values.mode:
self._current_operation = self.values.mode.data
operation_list = self.values.mode.data_items
if operation_list:
self._operation_list = list(operation_list)
self._current_operation = self.get_value(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_MODE, member='data')
operation_list = self.get_value(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_MODE,
member='data_items')
if operation_list:
self._operation_list = list(operation_list)
_LOGGER.debug("self._operation_list=%s", self._operation_list)
_LOGGER.debug("self._current_operation=%s", self._current_operation)
# Current Temp
if self.values.temperature:
self._current_temperature = self.values.temperature.data
device_unit = self.values.temperature.units
if device_unit is not None:
self._unit = device_unit
self._current_temperature = self.get_value(
class_id=zwave.const.COMMAND_CLASS_SENSOR_MULTILEVEL,
label=['Temperature'], member='data')
device_unit = self.get_value(
class_id=zwave.const.COMMAND_CLASS_SENSOR_MULTILEVEL,
label=['Temperature'], member='units')
if device_unit is not None:
self._unit = device_unit
# Fan Mode
if self.values.fan_mode:
self._current_fan_mode = self.values.fan_mode.data
fan_list = self.values.fan_mode.data_items
if fan_list:
self._fan_list = list(fan_list)
self._current_fan_mode = self.get_value(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_FAN_MODE,
member='data')
fan_list = self.get_value(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_FAN_MODE,
member='data_items')
if fan_list:
self._fan_list = list(fan_list)
_LOGGER.debug("self._fan_list=%s", self._fan_list)
_LOGGER.debug("self._current_fan_mode=%s",
self._current_fan_mode)
# Swing mode
if self._zxt_120 == 1:
if self.values.zxt_120_swing_mode:
self._current_swing_mode = self.values.zxt_120_swing_mode.data
swing_list = self.values.zxt_120_swing_mode.data_items
if swing_list:
self._swing_list = list(swing_list)
self._current_swing_mode = (
self.get_value(
class_id=zwave.const.COMMAND_CLASS_CONFIGURATION,
index=33,
member='data'))
swing_list = self.get_value(class_id=zwave.const
.COMMAND_CLASS_CONFIGURATION,
index=33,
member='data_items')
if swing_list:
self._swing_list = list(swing_list)
_LOGGER.debug("self._swing_list=%s", self._swing_list)
_LOGGER.debug("self._current_swing_mode=%s",
self._current_swing_mode)
# Set point
if self.values.primary.data == 0:
_LOGGER.debug("Setpoint is 0, setting default to "
"current_temperature=%s",
self._current_temperature)
self._target_temperature = (
round((float(self._current_temperature)), 1))
else:
self._target_temperature = round(
(float(self.values.primary.data)), 1)
temps = []
for value in (
self._node.get_values(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_SETPOINT)
.values()):
temps.append((round(float(value.data)), 1))
if value.index == self._index:
if value.data == 0:
_LOGGER.debug("Setpoint is 0, setting default to "
"current_temperature=%s",
self._current_temperature)
self._target_temperature = (
round((float(self._current_temperature)), 1))
break
else:
self._target_temperature = round((float(value.data)), 1)
# Operating state
if self.values.operating_state:
self._operating_state = self.values.operating_state.data
self._operating_state = self.get_value(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_OPERATING_STATE,
member='data')
# Fan operating state
if self.values.fan_state:
self._fan_state = self.values.fan_state.data
self._fan_state = self.get_value(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_FAN_STATE,
member='data')
@property
def should_poll(self):
@ -188,24 +213,29 @@ class ZWaveClimate(ZWaveDeviceEntity, ClimateDevice):
else:
return
self.values.primary.data = temperature
self.set_value(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_SETPOINT,
index=self._index, data=temperature)
self.schedule_update_ha_state()
def set_fan_mode(self, fan):
"""Set new target fan mode."""
if self.values.fan_mode:
self.values.fan_mode.data = bytes(fan, 'utf-8')
self.set_value(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_FAN_MODE,
index=0, data=bytes(fan, 'utf-8'))
def set_operation_mode(self, operation_mode):
"""Set new target operation mode."""
if self.values.mode:
self.values.mode.data = bytes(operation_mode, 'utf-8')
self.set_value(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_MODE,
index=0, data=bytes(operation_mode, 'utf-8'))
def set_swing_mode(self, swing_mode):
"""Set new target swing mode."""
if self._zxt_120 == 1:
if self.values.zxt_120_swing_mode:
self.values.zxt_120_swing_mode = bytes(swing_mode, 'utf-8')
self.set_value(
class_id=zwave.const.COMMAND_CLASS_CONFIGURATION,
index=33, data=bytes(swing_mode, 'utf-8'))
@property
def device_state_attributes(self):
@ -216,3 +246,8 @@ class ZWaveClimate(ZWaveDeviceEntity, ClimateDevice):
if self._fan_state:
data[ATTR_FAN_STATE] = self._fan_state
return data
@property
def dependent_value_ids(self):
"""List of value IDs a device depends on."""
return None

View File

@ -20,49 +20,64 @@ _LOGGER = logging.getLogger(__name__)
SUPPORT_GARAGE = SUPPORT_OPEN | SUPPORT_CLOSE
def get_device(values, **kwargs):
def get_device(value, **kwargs):
"""Create zwave entity device."""
if (values.primary.command_class ==
zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL
and values.primary.index == 0):
return ZwaveRollershutter(values)
elif (values.primary.command_class in [
zwave.const.COMMAND_CLASS_SWITCH_BINARY,
zwave.const.COMMAND_CLASS_BARRIER_OPERATOR]):
return ZwaveGarageDoor(values)
if (value.command_class == zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL
and value.index == 0):
return ZwaveRollershutter(value)
elif (value.command_class == zwave.const.COMMAND_CLASS_SWITCH_BINARY or
value.command_class == zwave.const.COMMAND_CLASS_BARRIER_OPERATOR):
return ZwaveGarageDoor(value)
return None
class ZwaveRollershutter(zwave.ZWaveDeviceEntity, CoverDevice):
"""Representation of an Zwave roller shutter."""
def __init__(self, values):
def __init__(self, value):
"""Initialize the zwave rollershutter."""
ZWaveDeviceEntity.__init__(self, values, DOMAIN)
ZWaveDeviceEntity.__init__(self, value, DOMAIN)
# pylint: disable=no-member
self._node = value.node
self._open_id = None
self._close_id = None
self._current_position_id = None
self._current_position = None
self._workaround = workaround.get_device_mapping(values.primary)
self._workaround = workaround.get_device_mapping(value)
if self._workaround:
_LOGGER.debug("Using workaround %s", self._workaround)
self.update_properties()
@property
def dependent_value_ids(self):
"""List of value IDs a device depends on."""
if not self._node.is_ready:
return None
return [self._current_position_id]
def update_properties(self):
"""Callback on data changes for node values."""
# Position value
self._current_position = self.values.primary.data
if self.values.open and self.values.close and \
self._open_id is None and self._close_id is None:
if self._workaround == workaround.WORKAROUND_REVERSE_OPEN_CLOSE:
self._open_id = self.values.close.value_id
self._close_id = self.values.open.value_id
self._workaround = None
else:
self._open_id = self.values.open.value_id
self._close_id = self.values.close.value_id
if not self._node.is_ready:
if self._current_position_id is None:
self._current_position_id = self.get_value(
class_id=zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL,
label=['Level'], member='value_id')
if self._open_id is None:
self._open_id = self.get_value(
class_id=zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL,
label=['Open', 'Up'], member='value_id')
if self._close_id is None:
self._close_id = self.get_value(
class_id=zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL,
label=['Close', 'Down'], member='value_id')
if self._open_id and self._close_id and \
self._workaround == workaround.WORKAROUND_REVERSE_OPEN_CLOSE:
self._open_id, self._close_id = self._close_id, self._open_id
self._workaround = None
self._current_position = self._node.get_dimmer_level(
self._current_position_id)
@property
def is_closed(self):
@ -97,7 +112,7 @@ class ZwaveRollershutter(zwave.ZWaveDeviceEntity, CoverDevice):
def set_cover_position(self, position, **kwargs):
"""Move the roller shutter to a specific position."""
self.node.set_dimmer(self.values.primary.value_id, position)
self._node.set_dimmer(self._value.value_id, position)
def stop_cover(self, **kwargs):
"""Stop the roller shutter."""
@ -107,14 +122,14 @@ class ZwaveRollershutter(zwave.ZWaveDeviceEntity, CoverDevice):
class ZwaveGarageDoor(zwave.ZWaveDeviceEntity, CoverDevice):
"""Representation of an Zwave garage door device."""
def __init__(self, values):
def __init__(self, value):
"""Initialize the zwave garage door."""
ZWaveDeviceEntity.__init__(self, values, DOMAIN)
ZWaveDeviceEntity.__init__(self, value, DOMAIN)
self.update_properties()
def update_properties(self):
"""Callback on data changes for node values."""
self._state = self.values.primary.data
self._state = self._value.data
@property
def is_closed(self):
@ -123,11 +138,11 @@ class ZwaveGarageDoor(zwave.ZWaveDeviceEntity, CoverDevice):
def close_cover(self):
"""Close the garage door."""
self.values.primary.data = False
self._value.data = False
def open_cover(self):
"""Open the garage door."""
self.values.primary.data = True
self._value.data = True
@property
def device_class(self):

View File

@ -49,9 +49,9 @@ SUPPORT_ZWAVE_COLORTEMP = (SUPPORT_BRIGHTNESS | SUPPORT_RGB_COLOR
| SUPPORT_COLOR_TEMP)
def get_device(node, values, node_config, **kwargs):
def get_device(node, value, node_config, **kwargs):
"""Create zwave entity device."""
name = '{}.{}'.format(DOMAIN, zwave.object_id(values.primary))
name = '{}.{}'.format(DOMAIN, zwave.object_id(value))
refresh = node_config.get(zwave.CONF_REFRESH_VALUE)
delay = node_config.get(zwave.CONF_REFRESH_DELAY)
_LOGGER.debug('name=%s node_config=%s CONF_REFRESH_VALUE=%s'
@ -59,9 +59,9 @@ def get_device(node, values, node_config, **kwargs):
refresh, delay)
if node.has_command_class(zwave.const.COMMAND_CLASS_SWITCH_COLOR):
return ZwaveColorLight(values, refresh, delay)
return ZwaveColorLight(value, refresh, delay)
else:
return ZwaveDimmer(values, refresh, delay)
return ZwaveDimmer(value, refresh, delay)
def brightness_state(value):
@ -75,9 +75,9 @@ def brightness_state(value):
class ZwaveDimmer(zwave.ZWaveDeviceEntity, Light):
"""Representation of a Z-Wave dimmer."""
def __init__(self, values, refresh, delay):
def __init__(self, value, refresh, delay):
"""Initialize the light."""
zwave.ZWaveDeviceEntity.__init__(self, values, DOMAIN)
zwave.ZWaveDeviceEntity.__init__(self, value, DOMAIN)
self._brightness = None
self._state = None
self._delay = delay
@ -86,10 +86,10 @@ class ZwaveDimmer(zwave.ZWaveDeviceEntity, Light):
# Enable appropriate workaround flags for our device
# Make sure that we have values for the key before converting to int
if (self.node.manufacturer_id.strip() and
self.node.product_id.strip()):
specific_sensor_key = (int(self.node.manufacturer_id, 16),
int(self.node.product_id, 16))
if (value.node.manufacturer_id.strip() and
value.node.product_id.strip()):
specific_sensor_key = (int(value.node.manufacturer_id, 16),
int(value.node.product_id, 16))
if specific_sensor_key in DEVICE_MAPPINGS:
if DEVICE_MAPPINGS[specific_sensor_key] == WORKAROUND_ZW098:
_LOGGER.debug("AEOTEC ZW098 workaround enabled")
@ -105,7 +105,7 @@ class ZwaveDimmer(zwave.ZWaveDeviceEntity, Light):
def update_properties(self):
"""Update internal properties based on zwave values."""
# Brightness
self._brightness, self._state = brightness_state(self.values.primary)
self._brightness, self._state = brightness_state(self._value)
def value_changed(self):
"""Called when a value for this entity's node has changed."""
@ -116,7 +116,7 @@ class ZwaveDimmer(zwave.ZWaveDeviceEntity, Light):
def _refresh_value():
"""Used timer callback for delayed value refresh."""
self._refreshing = True
self.values.primary.refresh()
self._value.refresh()
if self._timer is not None and self._timer.isAlive():
self._timer.cancel()
@ -151,12 +151,12 @@ class ZwaveDimmer(zwave.ZWaveDeviceEntity, Light):
else:
brightness = 255
if self.node.set_dimmer(self.values.primary.value_id, brightness):
if self._value.node.set_dimmer(self._value.value_id, brightness):
self._state = STATE_ON
def turn_off(self, **kwargs):
"""Turn the device off."""
if self.node.set_dimmer(self.values.primary.value_id, 0):
if self._value.node.set_dimmer(self._value.value_id, 0):
self._state = STATE_OFF
@ -170,28 +170,73 @@ def ct_to_rgb(temp):
class ZwaveColorLight(ZwaveDimmer):
"""Representation of a Z-Wave color changing light."""
def __init__(self, values, refresh, delay):
def __init__(self, value, refresh, delay):
"""Initialize the light."""
from openzwave.network import ZWaveNetwork
from pydispatch import dispatcher
self._value_color = None
self._value_color_channels = None
self._color_channels = None
self._rgb = None
self._ct = None
super().__init__(values, refresh, delay)
super().__init__(value, refresh, delay)
# Create a listener so the color values can be linked to this entity
dispatcher.connect(
self._value_added, ZWaveNetwork.SIGNAL_VALUE_ADDED)
self._get_color_values()
@property
def dependent_value_ids(self):
"""List of value IDs a device depends on."""
return [val.value_id for val in [
self._value_color, self._value_color_channels] if val]
def _get_color_values(self):
"""Search for color values available on this node."""
from openzwave.network import ZWaveNetwork
from pydispatch import dispatcher
_LOGGER.debug("Searching for zwave color values")
# Currently zwave nodes only exist with one color element per node.
if self._value_color is None:
for value_color in self._value.node.get_rgbbulbs().values():
self._value_color = value_color
if self._value_color_channels is None:
self._value_color_channels = self.get_value(
class_id=zwave.const.COMMAND_CLASS_SWITCH_COLOR,
genre=zwave.const.GENRE_SYSTEM, type=zwave.const.TYPE_INT)
if self._value_color and self._value_color_channels:
_LOGGER.debug("Zwave node color values found.")
dispatcher.disconnect(
self._value_added, ZWaveNetwork.SIGNAL_VALUE_ADDED)
self.update_properties()
def _value_added(self, value):
"""Called when a value has been added to the network."""
if self._value.node != value.node:
return
# Check for the missing color values
self._get_color_values()
def update_properties(self):
"""Update internal properties based on zwave values."""
super().update_properties()
if self.values.color is None:
if self._value_color is None:
return
if self.values.color_channels is None:
if self._value_color_channels is None:
return
# Color Channels
self._color_channels = self.values.color_channels.data
self._color_channels = self._value_color_channels.data
# Color Data String
data = self.values.color.data
data = self._value_color.data
# RGB is always present in the openzwave color data string.
self._rgb = [
@ -284,8 +329,8 @@ class ZwaveColorLight(ZwaveDimmer):
rgbw += format(colorval, '02x').encode('utf-8')
rgbw += b'0000'
if rgbw and self.values.color:
self.values.color.data = rgbw
if rgbw and self._value_color:
self._value_color.node.set_rgbw(self._value_color.value_id, rgbw)
super().turn_on(**kwargs)

View File

@ -120,7 +120,7 @@ CLEAR_USERCODE_SCHEMA = vol.Schema({
})
def get_device(hass, node, values, **kwargs):
def get_device(hass, node, value, **kwargs):
"""Create zwave entity device."""
descriptions = load_yaml_config_file(
path.join(path.dirname(__file__), 'services.yaml'))
@ -191,15 +191,16 @@ def get_device(hass, node, values, **kwargs):
clear_usercode,
descriptions.get(SERVICE_CLEAR_USERCODE),
schema=CLEAR_USERCODE_SCHEMA)
return ZwaveLock(values)
return ZwaveLock(value)
class ZwaveLock(zwave.ZWaveDeviceEntity, LockDevice):
"""Representation of a Z-Wave Lock."""
def __init__(self, values):
def __init__(self, value):
"""Initialize the Z-Wave lock device."""
zwave.ZWaveDeviceEntity.__init__(self, values, DOMAIN)
zwave.ZWaveDeviceEntity.__init__(self, value, DOMAIN)
self._node = value.node
self._state = None
self._notification = None
self._lock_status = None
@ -207,10 +208,10 @@ class ZwaveLock(zwave.ZWaveDeviceEntity, LockDevice):
# Enable appropriate workaround flags for our device
# Make sure that we have values for the key before converting to int
if (self.node.manufacturer_id.strip() and
self.node.product_id.strip()):
specific_sensor_key = (int(self.node.manufacturer_id, 16),
int(self.node.product_id, 16))
if (value.node.manufacturer_id.strip() and
value.node.product_id.strip()):
specific_sensor_key = (int(value.node.manufacturer_id, 16),
int(value.node.product_id, 16))
if specific_sensor_key in DEVICE_MAPPINGS:
if DEVICE_MAPPINGS[specific_sensor_key] == WORKAROUND_V2BTZE:
self._v2btze = 1
@ -220,33 +221,36 @@ class ZwaveLock(zwave.ZWaveDeviceEntity, LockDevice):
def update_properties(self):
"""Callback on data changes for node values."""
self._state = self.values.primary.data
self._state = self._value.data
_LOGGER.debug('Lock state set from Bool value and'
' is %s', self._state)
if self.values.access_control:
notification_data = self.values.access_control.data
notification_data = self.get_value(class_id=zwave.const
.COMMAND_CLASS_ALARM,
label=['Access Control'],
member='data')
if notification_data:
self._notification = LOCK_NOTIFICATION.get(str(notification_data))
if self._v2btze:
advanced_config = self.get_value(class_id=zwave.const
.COMMAND_CLASS_CONFIGURATION,
index=12,
data=CONFIG_ADVANCED,
member='data')
if advanced_config:
self._state = LOCK_STATUS.get(str(notification_data))
_LOGGER.debug('Lock state set from Access Control '
'value and is %s, get=%s',
str(notification_data),
self.state)
if self._v2btze:
if self.values.v2btze_advanced and \
self.values.v2btze_advanced.data == CONFIG_ADVANCED:
self._state = LOCK_STATUS.get(str(notification_data))
_LOGGER.debug('Lock state set from Access Control '
'value and is %s, get=%s',
str(notification_data),
self.state)
if not self.values.alarm_type:
return
alarm_type = self.values.alarm_type.data
alarm_type = self.get_value(class_id=zwave.const
.COMMAND_CLASS_ALARM,
label=['Alarm Type'], member='data')
_LOGGER.debug('Lock alarm_type is %s', str(alarm_type))
if self.values.alarm_level:
alarm_level = self.values.alarm_level.data
else:
alarm_level = None
alarm_level = self.get_value(class_id=zwave.const
.COMMAND_CLASS_ALARM,
label=['Alarm Level'], member='data')
_LOGGER.debug('Lock alarm_level is %s', str(alarm_level))
if not alarm_type:
return
if alarm_type is 21:
@ -273,11 +277,11 @@ class ZwaveLock(zwave.ZWaveDeviceEntity, LockDevice):
def lock(self, **kwargs):
"""Lock the device."""
self.values.primary.data = True
self._value.data = True
def unlock(self, **kwargs):
"""Unlock the device."""
self.values.primary.data = False
self._value.data = False
@property
def device_state_attributes(self):
@ -288,3 +292,8 @@ class ZwaveLock(zwave.ZWaveDeviceEntity, LockDevice):
if self._lock_status:
data[ATTR_LOCK_STATUS] = self._lock_status
return data
@property
def dependent_value_ids(self):
"""List of value IDs a device depends on."""
return None

View File

@ -15,34 +15,34 @@ from homeassistant.components.zwave import async_setup_platform # noqa # pylint
_LOGGER = logging.getLogger(__name__)
def get_device(node, values, **kwargs):
def get_device(node, value, **kwargs):
"""Create zwave entity device."""
# Generic Device mappings
if values.primary.command_class == zwave.const.COMMAND_CLASS_BATTERY:
return ZWaveSensor(values)
if value.command_class == zwave.const.COMMAND_CLASS_BATTERY:
return ZWaveSensor(value)
if node.has_command_class(zwave.const.COMMAND_CLASS_SENSOR_MULTILEVEL):
return ZWaveMultilevelSensor(values)
return ZWaveMultilevelSensor(value)
if node.has_command_class(zwave.const.COMMAND_CLASS_METER) and \
values.primary.type == zwave.const.TYPE_DECIMAL:
return ZWaveMultilevelSensor(values)
value.type == zwave.const.TYPE_DECIMAL:
return ZWaveMultilevelSensor(value)
if node.has_command_class(zwave.const.COMMAND_CLASS_ALARM) or \
node.has_command_class(zwave.const.COMMAND_CLASS_SENSOR_ALARM):
return ZWaveAlarmSensor(values)
return ZWaveAlarmSensor(value)
return None
class ZWaveSensor(zwave.ZWaveDeviceEntity):
"""Representation of a Z-Wave sensor."""
def __init__(self, values):
def __init__(self, value):
"""Initialize the sensor."""
zwave.ZWaveDeviceEntity.__init__(self, values, DOMAIN)
zwave.ZWaveDeviceEntity.__init__(self, value, DOMAIN)
self.update_properties()
def update_properties(self):
"""Callback on data changes for node values."""
self._state = self.values.primary.data
self._units = self.values.primary.units
self._state = self._value.data
self._units = self._value.units
@property
def force_update(self):

View File

@ -15,30 +15,29 @@ from homeassistant.components.zwave import workaround, async_setup_platform # n
_LOGGER = logging.getLogger(__name__)
def get_device(values, **kwargs):
def get_device(value, **kwargs):
"""Create zwave entity device."""
return ZwaveSwitch(values)
return ZwaveSwitch(value)
class ZwaveSwitch(zwave.ZWaveDeviceEntity, SwitchDevice):
"""Representation of a Z-Wave switch."""
def __init__(self, values):
def __init__(self, value):
"""Initialize the Z-Wave switch device."""
zwave.ZWaveDeviceEntity.__init__(self, values, DOMAIN)
self.refresh_on_update = (
workaround.get_device_mapping(values.primary) ==
workaround.WORKAROUND_REFRESH_NODE_ON_UPDATE)
zwave.ZWaveDeviceEntity.__init__(self, value, DOMAIN)
self.refresh_on_update = (workaround.get_device_mapping(value) ==
workaround.WORKAROUND_REFRESH_NODE_ON_UPDATE)
self.last_update = time.perf_counter()
self._state = self.values.primary.data
self._state = self._value.data
def update_properties(self):
"""Callback on data changes for node values."""
self._state = self.values.primary.data
self._state = self._value.data
if self.refresh_on_update and \
time.perf_counter() - self.last_update > 30:
self.last_update = time.perf_counter()
self.node.request_state()
self._value.node.request_state()
@property
def is_on(self):
@ -47,8 +46,8 @@ class ZwaveSwitch(zwave.ZWaveDeviceEntity, SwitchDevice):
def turn_on(self, **kwargs):
"""Turn the device on."""
self.node.set_switch(self.values.primary.value_id, True)
self._value.node.set_switch(self._value.value_id, True)
def turn_off(self, **kwargs):
"""Turn the device off."""
self.node.set_switch(self.values.primary.value_id, False)
self._value.node.set_switch(self._value.value_id, False)

View File

@ -5,7 +5,6 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/zwave/
"""
import asyncio
import copy
import logging
import os.path
import time
@ -30,8 +29,7 @@ from homeassistant.helpers.dispatcher import (
from . import const
from . import workaround
from .discovery_schemas import DISCOVERY_SCHEMAS
from .util import check_node_schema, check_value_schema
from .util import value_handler
REQUIREMENTS = ['pydispatcher==2.0.5']
@ -66,6 +64,88 @@ DATA_ZWAVE_DICT = 'zwave_devices'
NETWORK = None
# List of tuple (DOMAIN, discovered service, supported command classes,
# value type, genre type, specific device class).
DISCOVERY_COMPONENTS = [
('sensor',
[const.GENERIC_TYPE_WHATEVER],
[const.SPECIFIC_TYPE_WHATEVER],
[const.COMMAND_CLASS_SENSOR_MULTILEVEL,
const.COMMAND_CLASS_METER,
const.COMMAND_CLASS_ALARM,
const.COMMAND_CLASS_SENSOR_ALARM,
const.COMMAND_CLASS_BATTERY],
const.TYPE_WHATEVER,
const.GENRE_USER),
('light',
[const.GENERIC_TYPE_SWITCH_MULTILEVEL,
const.GENERIC_TYPE_SWITCH_REMOTE],
[const.SPECIFIC_TYPE_POWER_SWITCH_MULTILEVEL,
const.SPECIFIC_TYPE_SCENE_SWITCH_MULTILEVEL,
const.SPECIFIC_TYPE_NOT_USED],
[const.COMMAND_CLASS_SWITCH_MULTILEVEL],
const.TYPE_BYTE,
const.GENRE_USER),
('switch',
[const.GENERIC_TYPE_SENSOR_ALARM,
const.GENERIC_TYPE_SENSOR_BINARY,
const.GENERIC_TYPE_SWITCH_BINARY,
const.GENERIC_TYPE_ENTRY_CONTROL,
const.GENERIC_TYPE_SENSOR_MULTILEVEL,
const.GENERIC_TYPE_SWITCH_MULTILEVEL,
const.GENERIC_TYPE_SENSOR_NOTIFICATION,
const.GENERIC_TYPE_GENERIC_CONTROLLER,
const.GENERIC_TYPE_SWITCH_REMOTE,
const.GENERIC_TYPE_REPEATER_SLAVE,
const.GENERIC_TYPE_THERMOSTAT,
const.GENERIC_TYPE_WALL_CONTROLLER],
[const.SPECIFIC_TYPE_WHATEVER],
[const.COMMAND_CLASS_SWITCH_BINARY],
const.TYPE_BOOL,
const.GENRE_USER),
('binary_sensor',
[const.GENERIC_TYPE_SENSOR_ALARM,
const.GENERIC_TYPE_SENSOR_BINARY,
const.GENERIC_TYPE_SWITCH_BINARY,
const.GENERIC_TYPE_METER,
const.GENERIC_TYPE_SENSOR_MULTILEVEL,
const.GENERIC_TYPE_SWITCH_MULTILEVEL,
const.GENERIC_TYPE_SENSOR_NOTIFICATION,
const.GENERIC_TYPE_THERMOSTAT],
[const.SPECIFIC_TYPE_WHATEVER],
[const.COMMAND_CLASS_SENSOR_BINARY],
const.TYPE_BOOL,
const.GENRE_USER),
('lock',
[const.GENERIC_TYPE_ENTRY_CONTROL],
[const.SPECIFIC_TYPE_ADVANCED_DOOR_LOCK,
const.SPECIFIC_TYPE_SECURE_KEYPAD_DOOR_LOCK],
[const.COMMAND_CLASS_DOOR_LOCK],
const.TYPE_BOOL,
const.GENRE_USER),
('cover',
[const.GENERIC_TYPE_SWITCH_MULTILEVEL,
const.GENERIC_TYPE_ENTRY_CONTROL],
[const.SPECIFIC_TYPE_CLASS_A_MOTOR_CONTROL,
const.SPECIFIC_TYPE_CLASS_B_MOTOR_CONTROL,
const.SPECIFIC_TYPE_CLASS_C_MOTOR_CONTROL,
const.SPECIFIC_TYPE_MOTOR_MULTIPOSITION,
const.SPECIFIC_TYPE_SECURE_BARRIER_ADDON,
const.SPECIFIC_TYPE_SECURE_DOOR],
[const.COMMAND_CLASS_SWITCH_BINARY,
const.COMMAND_CLASS_BARRIER_OPERATOR,
const.COMMAND_CLASS_SWITCH_MULTILEVEL],
const.TYPE_WHATEVER,
const.GENRE_USER),
('climate',
[const.GENERIC_TYPE_THERMOSTAT],
[const.SPECIFIC_TYPE_WHATEVER],
[const.COMMAND_CLASS_THERMOSTAT_SETPOINT],
const.TYPE_WHATEVER,
const.GENRE_WHATEVER),
]
RENAME_NODE_SCHEMA = vol.Schema({
vol.Required(ATTR_ENTITY_ID): cv.entity_id,
vol.Required(const.ATTR_NAME): cv.string,
@ -278,25 +358,92 @@ def setup(hass, config):
dispatcher.connect(log_all, weak=False)
discovered_values = []
def value_added(node, value):
"""Called when a value is added to a node on the network."""
# Check if this value should be tracked by an existing entity
for values in discovered_values:
values.check_value(value)
for (component,
generic_device_class,
specific_device_class,
command_class,
value_type,
value_genre) in DISCOVERY_COMPONENTS:
for schema in DISCOVERY_SCHEMAS:
if not check_node_schema(node, schema):
_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 not check_value_schema(
value,
schema[const.DISC_INSTANCE_VALUES][const.DISC_PRIMARY]):
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.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 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
values = ZWaveDeviceEntityValues(
hass, schema, value, config, device_config)
discovered_values.append(values)
# Configure node
_LOGGER.debug("Adding Node_id=%s Generic_command_class=%s, "
"Specific_command_class=%s, "
"Command_class=%s, Value type=%s, "
"Genre=%s as %s", node.node_id,
node.generic, node.specific,
value.command_class, value.type,
value.genre, component)
workaround_component = workaround.get_device_component_mapping(
value)
if workaround_component and workaround_component != component:
if workaround_component == workaround.WORKAROUND_IGNORE:
_LOGGER.info("Ignoring device %s due to workaround.",
"{}.{}".format(component, object_id(value)))
continue
_LOGGER.debug("Using %s instead of %s",
workaround_component, component)
component = workaround_component
name = "{}.{}".format(component, object_id(value))
node_config = device_config.get(name)
if node_config.get(CONF_IGNORED):
_LOGGER.info(
"Ignoring device %s due to device settings.", name)
return
polling_intensity = convert(
node_config.get(CONF_POLLING_INTENSITY), int)
if polling_intensity:
value.enable_poll(polling_intensity)
else:
value.disable_poll()
platform = get_platform(component, DOMAIN)
device = platform.get_device(
node=node, value=value, node_config=node_config, hass=hass)
if not device:
continue
dict_id = value.value_id
@asyncio.coroutine
def discover_device(component, device, dict_id):
"""Put device in a dictionary and call discovery on it."""
hass.data[DATA_ZWAVE_DICT][dict_id] = device
yield from discovery.async_load_platform(
hass, component, DOMAIN,
{const.DISCOVERY_DEVICE: dict_id}, config)
hass.add_job(discover_device, component, device, dict_id)
def scene_activated(node, scene_id):
"""Called when a scene is activated on any node in the network."""
@ -605,166 +752,24 @@ def setup(hass, config):
return True
class ZWaveDeviceEntityValues():
"""Manages entity access to the underlying zwave value objects."""
def __init__(self, hass, schema, primary_value, zwave_config,
device_config):
"""Initialize the values object with the passed entity schema."""
self._hass = hass
self._zwave_config = zwave_config
self._device_config = device_config
self._schema = copy.deepcopy(schema)
self._values = {}
self._entity = None
self._workaround_ignore = False
# Combine node value schemas and instance value schemas into one
# values schema mapping.
self._schema[const.DISC_VALUES] = {}
for name in self._schema[const.DISC_NODE_VALUES].keys():
self._values[name] = None
self._schema[const.DISC_VALUES][name] = \
self._schema[const.DISC_NODE_VALUES][name]
for name in self._schema[const.DISC_INSTANCE_VALUES].keys():
self._values[name] = None
self._schema[const.DISC_VALUES][name] = \
self._schema[const.DISC_INSTANCE_VALUES][name]
self._schema[const.DISC_VALUES][name][const.DISC_INSTANCE] = \
[primary_value.instance]
self._values[const.DISC_PRIMARY] = primary_value
self._node = primary_value.node
self._schema[const.DISC_NODE_ID] = [self._node.node_id]
# Check values that have already been discovered for node
for value in self._node.values.values():
self.check_value(value)
self._check_entity_ready()
def __getattr__(self, name):
"""Get the specified value for this entity."""
return self._values[name]
def __iter__(self):
"""Allow iteration over all values."""
return iter(self._values.values())
def check_value(self, value):
"""Check if the new value matches a missing value for this entity.
If a match is found, it is added to the values mapping.
"""
if not check_node_schema(value.node, self._schema):
return
for name in self._values:
if self._values[name] is not None:
continue
if not check_value_schema(
value, self._schema[const.DISC_VALUES][name]):
continue
self._values[name] = value
if self._entity:
self._entity.value_changed()
self._check_entity_ready()
def _check_entity_ready(self):
"""Check if all required values are discovered and create entity."""
if self._workaround_ignore:
return
if self._entity is not None:
return
for name in self._schema[const.DISC_VALUES]:
if self._values[name] is None and \
not self._schema[const.DISC_VALUES][name].get(
const.DISC_OPTIONAL):
return
component = self._schema[const.DISC_COMPONENT]
workaround_component = workaround.get_device_component_mapping(
self.primary)
if workaround_component and workaround_component != component:
if workaround_component == workaround.WORKAROUND_IGNORE:
_LOGGER.info("Ignoring device %s due to workaround.",
"{}.{}".format(
component, object_id(self.primary)))
# No entity will be created for this value
self._workaround_ignore = True
return
_LOGGER.debug("Using %s instead of %s",
workaround_component, component)
component = workaround_component
name = "{}.{}".format(component, object_id(self.primary))
node_config = self._device_config.get(name)
# Configure node
_LOGGER.debug("Adding Node_id=%s Generic_command_class=%s, "
"Specific_command_class=%s, "
"Command_class=%s, Value type=%s, "
"Genre=%s as %s", self._node.node_id,
self._node.generic, self._node.specific,
self.primary.command_class, self.primary.type,
self.primary.genre, component)
if node_config.get(CONF_IGNORED):
_LOGGER.info(
"Ignoring node %s due to device settings.", self._node.node_id)
# No entity will be created for this value
self._workaround_ignore = True
return
polling_intensity = convert(
node_config.get(CONF_POLLING_INTENSITY), int)
if polling_intensity:
self.primary.enable_poll(polling_intensity)
else:
self.primary.disable_poll()
platform = get_platform(component, DOMAIN)
device = platform.get_device(
node=self._node, values=self,
node_config=node_config, hass=self._hass)
if not device:
# No entity will be created for this value
self._workaround_ignore = True
return
self._entity = device
dict_id = self.primary.value_id
@asyncio.coroutine
def discover_device(component, device, dict_id):
"""Put device in a dictionary and call discovery on it."""
self._hass.data[DATA_ZWAVE_DICT][dict_id] = device
yield from discovery.async_load_platform(
self._hass, component, DOMAIN,
{const.DISCOVERY_DEVICE: dict_id}, self._zwave_config)
self._hass.add_job(discover_device, component, device, dict_id)
class ZWaveDeviceEntity(Entity):
"""Representation of a Z-Wave node entity."""
def __init__(self, values, domain):
def __init__(self, value, domain):
"""Initialize the z-Wave device."""
# pylint: disable=import-error
from openzwave.network import ZWaveNetwork
from pydispatch import dispatcher
self.values = values
self.node = values.primary.node
self.values.primary.set_change_verified(False)
self.entity_id = "{}.{}".format(domain, object_id(values.primary))
self._value = value
self._value.set_change_verified(False)
self.entity_id = "{}.{}".format(domain, object_id(value))
self._name = _value_name(self.values.primary)
self._unique_id = "ZWAVE-{}-{}".format(self.node.node_id,
self.values.primary.object_id)
self._name = _value_name(self._value)
self._unique_id = "ZWAVE-{}-{}".format(self._value.node.node_id,
self._value.object_id)
self._wakeup_value_id = None
self._battery_value_id = None
self._power_value_id = None
self._update_scheduled = False
self._update_attributes()
@ -773,11 +778,19 @@ class ZWaveDeviceEntity(Entity):
def network_value_changed(self, value):
"""Called when a value has changed on the network."""
if value.value_id in [v.value_id for v in self.values if v]:
if self._value.value_id == value.value_id:
return self.value_changed()
dependent_ids = self._get_dependent_value_ids()
if dependent_ids is None and self._value.node == value.node:
return self.value_changed()
if dependent_ids is not None and value.value_id in dependent_ids:
return self.value_changed()
def value_changed(self):
"""Called when a value for this entity's node has changed."""
if not self._value.node.is_ready:
self._update_ids()
self._update_attributes()
self.update_properties()
# If value changed after device was created but before setup_platform
@ -785,6 +798,29 @@ class ZWaveDeviceEntity(Entity):
if self.hass and not self._update_scheduled:
self.hass.add_job(self._schedule_update)
def _update_ids(self):
"""Update value_ids from which to pull attributes."""
if self._wakeup_value_id is None:
self._wakeup_value_id = self.get_value(
class_id=const.COMMAND_CLASS_WAKE_UP, member='value_id')
if self._battery_value_id is None:
self._battery_value_id = self.get_value(
class_id=const.COMMAND_CLASS_BATTERY, member='value_id')
if self._power_value_id is None:
self._power_value_id = self.get_value(
class_id=[const.COMMAND_CLASS_SENSOR_MULTILEVEL,
const.COMMAND_CLASS_METER],
label=['Power'], member='value_id',
instance=self._value.instance)
@property
def dependent_value_ids(self):
"""List of value IDs a device depends on.
None if depends on the whole node.
"""
return []
@asyncio.coroutine
def async_added_to_hass(self):
"""Add device to dict."""
@ -793,26 +829,45 @@ class ZWaveDeviceEntity(Entity):
SIGNAL_REFRESH_ENTITY_FORMAT.format(self.entity_id),
self.refresh_from_network)
def _get_dependent_value_ids(self):
"""Return a list of value_ids this device depend on.
Return None if it depends on the whole node.
"""
if self.dependent_value_ids is None:
# Device depends on node.
return None
if not self._value.node.is_ready:
# Node is not ready, so depend on the whole node.
return None
return [val for val in (self.dependent_value_ids + [
self._wakeup_value_id, self._battery_value_id,
self._power_value_id]) if val]
def _update_attributes(self):
"""Update the node attributes. May only be used inside callback."""
self.node_id = self.node.node_id
self.location = self.node.location
self.node_id = self._value.node.node_id
self.location = self._value.node.location
self.battery_level = self._value.node.get_battery_level(
self._battery_value_id)
self.wakeup_interval = None
if self._wakeup_value_id:
self.wakeup_interval = self._value.node.values[
self._wakeup_value_id].data
power_value = None
if self._power_value_id:
power_value = self._value.node.values[self._power_value_id]
self.power_consumption = round(
power_value.data, power_value.precision) if power_value else None
if self.values.battery:
self.battery_level = self.values.battery.data
else:
self.battery_level = None
def get_value(self, **kwargs):
"""Simplifyer to get values. May only be used inside callback."""
return value_handler(self._value, method='get', **kwargs)
if self.values.wakeup:
self.wakeup_interval = self.values.wakeup.data
else:
self.wakeup_interval = None
if self.values.power:
self.power_consumption = round(
self.values.power.data, self.values.power.precision)
else:
self.power_consumption = None
def set_value(self, **kwargs):
"""Simplifyer to set a value."""
return value_handler(self._value, method='set', **kwargs)
def update_properties(self):
"""Callback on data changes for node values."""
@ -856,8 +911,13 @@ class ZWaveDeviceEntity(Entity):
def refresh_from_network(self):
"""Refresh all dependent values from zwave network."""
for value in self.values:
self.node.refresh_value(value.value_id)
dependent_ids = self._get_dependent_value_ids()
if dependent_ids is None:
# Entity depends on the whole node
self._value.node.refresh_info()
return
for value_id in dependent_ids + [self._value.value_id]:
self._value.node.refresh_value(value_id)
@callback
def _schedule_update(self):

View File

@ -311,22 +311,3 @@ TYPE_BOOL = "Bool"
TYPE_DECIMAL = "Decimal"
TYPE_INT = "Int"
TYPE_LIST = "List"
TYPE_STRING = "String"
DISC_COMMAND_CLASS = "command_class"
DISC_COMPONENT = "component"
DISC_GENERIC_DEVICE_CLASS = "generic_device_class"
DISC_GENRE = "genre"
DISC_INDEX = "index"
DISC_INSTANCE_VALUES = "instance_values"
DISC_INSTANCE = "instance"
DISC_LABEL = "label"
DISC_NODE_ID = "node_id"
DISC_NODE_VALUES = "node_values"
DISC_OPTIONAL = "optional"
DISC_PRIMARY = "primary"
DISC_READONLY = "readonly"
DISC_SPECIFIC_DEVICE_CLASS = "specific_device_class"
DISC_TYPE = "type"
DISC_VALUES = "values"
DISC_WRITEONLY = "writeonly"

View File

@ -1,220 +0,0 @@
"""Zwave discovery schemas."""
from . import const
DEFAULT_NODE_VALUES_SCHEMA = {
'wakeup': {
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_WAKE_UP],
const.DISC_GENRE: const.GENRE_USER,
const.DISC_OPTIONAL: True,
},
'battery': {
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_BATTERY],
const.DISC_OPTIONAL: True,
},
}
DEFAULT_INSTANCE_VALUES_SCHEMA = {
'power': {
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_SENSOR_MULTILEVEL,
const.COMMAND_CLASS_METER],
const.DISC_LABEL: ['Power'],
const.DISC_OPTIONAL: True,
},
}
DISCOVERY_SCHEMAS = [
{const.DISC_COMPONENT: 'binary_sensor',
const.DISC_GENERIC_DEVICE_CLASS: [
const.GENERIC_TYPE_SENSOR_ALARM,
const.GENERIC_TYPE_SENSOR_BINARY,
const.GENERIC_TYPE_SWITCH_BINARY,
const.GENERIC_TYPE_METER,
const.GENERIC_TYPE_SENSOR_MULTILEVEL,
const.GENERIC_TYPE_SWITCH_MULTILEVEL,
const.GENERIC_TYPE_SENSOR_NOTIFICATION,
const.GENERIC_TYPE_THERMOSTAT],
const.DISC_NODE_VALUES: dict(DEFAULT_NODE_VALUES_SCHEMA),
const.DISC_INSTANCE_VALUES: dict(DEFAULT_INSTANCE_VALUES_SCHEMA, **{
const.DISC_PRIMARY: {
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_SENSOR_BINARY],
const.DISC_TYPE: const.TYPE_BOOL,
const.DISC_GENRE: const.GENRE_USER
}})},
{const.DISC_COMPONENT: 'climate',
const.DISC_GENERIC_DEVICE_CLASS: [const.GENERIC_TYPE_THERMOSTAT],
const.DISC_NODE_VALUES: dict(DEFAULT_NODE_VALUES_SCHEMA),
const.DISC_INSTANCE_VALUES: dict(DEFAULT_INSTANCE_VALUES_SCHEMA, **{
const.DISC_PRIMARY: {
const.DISC_COMMAND_CLASS: [
const.COMMAND_CLASS_THERMOSTAT_SETPOINT],
},
'temperature': {
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_SENSOR_MULTILEVEL],
const.DISC_LABEL: 'Temperature',
const.DISC_OPTIONAL: True,
},
'mode': {
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_THERMOSTAT_MODE],
const.DISC_OPTIONAL: True,
},
'fan_mode': {
const.DISC_COMMAND_CLASS: [
const.COMMAND_CLASS_THERMOSTAT_FAN_MODE],
const.DISC_OPTIONAL: True,
},
'operating_state': {
const.DISC_COMMAND_CLASS: [
const.COMMAND_CLASS_THERMOSTAT_OPERATING_STATE],
const.DISC_OPTIONAL: True,
},
'fan_state': {
const.DISC_COMMAND_CLASS: [
const.COMMAND_CLASS_THERMOSTAT_FAN_STATE],
const.DISC_OPTIONAL: True,
},
'zxt_120_swing_mode': {
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_CONFIGURATION],
const.DISC_INDEX: [33],
const.DISC_OPTIONAL: True,
}})},
{const.DISC_COMPONENT: 'cover', # Rollershutter
const.DISC_GENERIC_DEVICE_CLASS: [
const.GENERIC_TYPE_SWITCH_MULTILEVEL,
const.GENERIC_TYPE_ENTRY_CONTROL],
const.DISC_SPECIFIC_DEVICE_CLASS: [
const.SPECIFIC_TYPE_CLASS_A_MOTOR_CONTROL,
const.SPECIFIC_TYPE_CLASS_B_MOTOR_CONTROL,
const.SPECIFIC_TYPE_CLASS_C_MOTOR_CONTROL,
const.SPECIFIC_TYPE_MOTOR_MULTIPOSITION,
const.SPECIFIC_TYPE_SECURE_BARRIER_ADDON,
const.SPECIFIC_TYPE_SECURE_DOOR],
const.DISC_NODE_VALUES: dict(DEFAULT_NODE_VALUES_SCHEMA),
const.DISC_INSTANCE_VALUES: dict(DEFAULT_INSTANCE_VALUES_SCHEMA, **{
const.DISC_PRIMARY: {
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_SWITCH_MULTILEVEL],
const.DISC_GENRE: const.GENRE_USER,
},
'open': {
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_SWITCH_MULTILEVEL],
const.DISC_LABEL: ['Open', 'Up'],
const.DISC_OPTIONAL: True,
},
'close': {
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_SWITCH_MULTILEVEL],
const.DISC_LABEL: ['Close', 'Down'],
const.DISC_OPTIONAL: True,
}})},
{const.DISC_COMPONENT: 'cover', # Garage Door
const.DISC_GENERIC_DEVICE_CLASS: [
const.GENERIC_TYPE_SWITCH_MULTILEVEL,
const.GENERIC_TYPE_ENTRY_CONTROL],
const.DISC_SPECIFIC_DEVICE_CLASS: [
const.SPECIFIC_TYPE_CLASS_A_MOTOR_CONTROL,
const.SPECIFIC_TYPE_CLASS_B_MOTOR_CONTROL,
const.SPECIFIC_TYPE_CLASS_C_MOTOR_CONTROL,
const.SPECIFIC_TYPE_MOTOR_MULTIPOSITION,
const.SPECIFIC_TYPE_SECURE_BARRIER_ADDON,
const.SPECIFIC_TYPE_SECURE_DOOR],
const.DISC_NODE_VALUES: dict(DEFAULT_NODE_VALUES_SCHEMA),
const.DISC_INSTANCE_VALUES: dict(DEFAULT_INSTANCE_VALUES_SCHEMA, **{
const.DISC_PRIMARY: {
const.DISC_COMMAND_CLASS: [
const.COMMAND_CLASS_BARRIER_OPERATOR,
const.COMMAND_CLASS_SWITCH_BINARY],
const.DISC_GENRE: const.GENRE_USER,
}})},
{const.DISC_COMPONENT: 'light',
const.DISC_GENERIC_DEVICE_CLASS: [
const.GENERIC_TYPE_SWITCH_MULTILEVEL,
const.GENERIC_TYPE_SWITCH_REMOTE],
const.DISC_SPECIFIC_DEVICE_CLASS: [
const.SPECIFIC_TYPE_POWER_SWITCH_MULTILEVEL,
const.SPECIFIC_TYPE_SCENE_SWITCH_MULTILEVEL,
const.SPECIFIC_TYPE_NOT_USED],
const.DISC_NODE_VALUES: dict(DEFAULT_NODE_VALUES_SCHEMA),
const.DISC_INSTANCE_VALUES: dict(DEFAULT_INSTANCE_VALUES_SCHEMA, **{
const.DISC_PRIMARY: {
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_SWITCH_MULTILEVEL],
const.DISC_GENRE: const.GENRE_USER,
const.DISC_TYPE: const.TYPE_BYTE,
},
'color': {
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_SWITCH_COLOR],
const.DISC_GENRE: const.GENRE_USER,
const.DISC_TYPE: const.TYPE_STRING,
const.DISC_READONLY: False,
const.DISC_WRITEONLY: False,
const.DISC_OPTIONAL: True,
},
'color_channels': {
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_SWITCH_COLOR],
const.DISC_GENRE: const.GENRE_SYSTEM,
const.DISC_TYPE: const.TYPE_INT,
const.DISC_OPTIONAL: True,
}})},
{const.DISC_COMPONENT: 'lock',
const.DISC_GENERIC_DEVICE_CLASS: [const.GENERIC_TYPE_ENTRY_CONTROL],
const.DISC_SPECIFIC_DEVICE_CLASS: [
const.SPECIFIC_TYPE_ADVANCED_DOOR_LOCK,
const.SPECIFIC_TYPE_SECURE_KEYPAD_DOOR_LOCK],
const.DISC_NODE_VALUES: dict(DEFAULT_NODE_VALUES_SCHEMA),
const.DISC_INSTANCE_VALUES: dict(DEFAULT_INSTANCE_VALUES_SCHEMA, **{
const.DISC_PRIMARY: {
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_DOOR_LOCK],
const.DISC_TYPE: const.TYPE_BOOL,
const.DISC_GENRE: const.GENRE_USER,
},
'access_control': {
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_ALARM],
const.DISC_LABEL: 'Access Control',
const.DISC_OPTIONAL: True,
},
'alarm_type': {
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_ALARM],
const.DISC_LABEL: 'Alarm Type',
const.DISC_OPTIONAL: True,
},
'alarm_level': {
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_ALARM],
const.DISC_LABEL: 'Alarm Level',
const.DISC_OPTIONAL: True,
},
'v2btze_advanced': {
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_CONFIGURATION],
const.DISC_INDEX: [12],
const.DISC_LABEL: 'Access Control',
const.DISC_OPTIONAL: True,
}})},
{const.DISC_COMPONENT: 'sensor',
const.DISC_NODE_VALUES: dict(DEFAULT_NODE_VALUES_SCHEMA),
const.DISC_INSTANCE_VALUES: dict(DEFAULT_INSTANCE_VALUES_SCHEMA, **{
const.DISC_PRIMARY: {
const.DISC_COMMAND_CLASS: [
const.COMMAND_CLASS_SENSOR_MULTILEVEL,
const.COMMAND_CLASS_METER,
const.COMMAND_CLASS_ALARM,
const.COMMAND_CLASS_SENSOR_ALARM,
const.COMMAND_CLASS_BATTERY],
const.DISC_GENRE: const.GENRE_USER,
}})},
{const.DISC_COMPONENT: 'switch',
const.DISC_GENERIC_DEVICE_CLASS: [
const.GENERIC_TYPE_SENSOR_ALARM,
const.GENERIC_TYPE_SENSOR_BINARY,
const.GENERIC_TYPE_SWITCH_BINARY,
const.GENERIC_TYPE_ENTRY_CONTROL,
const.GENERIC_TYPE_SENSOR_MULTILEVEL,
const.GENERIC_TYPE_SWITCH_MULTILEVEL,
const.GENERIC_TYPE_SENSOR_NOTIFICATION,
const.GENERIC_TYPE_GENERIC_CONTROLLER,
const.GENERIC_TYPE_SWITCH_REMOTE,
const.GENERIC_TYPE_REPEATER_SLAVE,
const.GENERIC_TYPE_THERMOSTAT,
const.GENERIC_TYPE_WALL_CONTROLLER],
const.DISC_NODE_VALUES: dict(DEFAULT_NODE_VALUES_SCHEMA),
const.DISC_INSTANCE_VALUES: dict(DEFAULT_INSTANCE_VALUES_SCHEMA, **{
const.DISC_PRIMARY: {
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_SWITCH_BINARY],
const.DISC_TYPE: const.TYPE_BOOL,
const.DISC_GENRE: const.GENRE_USER,
}})},
]

View File

@ -1,71 +1,54 @@
"""Zwave util methods."""
import logging
from . import const
_LOGGER = logging.getLogger(__name__)
def check_node_schema(node, schema):
"""Check if node matches the passed node schema."""
if (const.DISC_NODE_ID in schema and
node.node_id not in schema[const.DISC_NODE_ID]):
_LOGGER.debug("node.node_id %s not in node_id %s",
node.node_id, schema[const.DISC_NODE_ID])
return False
if (const.DISC_GENERIC_DEVICE_CLASS in schema and
node.generic not in schema[const.DISC_GENERIC_DEVICE_CLASS]):
_LOGGER.debug("node.generic %s not in generic_device_class %s",
node.generic, schema[const.DISC_GENERIC_DEVICE_CLASS])
return False
if (const.DISC_SPECIFIC_DEVICE_CLASS in schema and
node.specific not in schema[const.DISC_SPECIFIC_DEVICE_CLASS]):
_LOGGER.debug("node.specific %s not in specific_device_class %s",
node.specific, schema[const.DISC_SPECIFIC_DEVICE_CLASS])
return False
return True
def value_handler(value, method=None, class_id=None, index=None,
label=None, data=None, member=None, instance=None,
**kwargs):
"""Get the values for a given command_class with arguments.
May only be used inside callback.
def check_value_schema(value, schema):
"""Check if the value matches the passed value schema."""
if (const.DISC_COMMAND_CLASS in schema and
value.command_class not in schema[const.DISC_COMMAND_CLASS]):
_LOGGER.debug("value.command_class %s not in command_class %s",
value.command_class, schema[const.DISC_COMMAND_CLASS])
return False
if (const.DISC_TYPE in schema and
value.type not in schema[const.DISC_TYPE]):
_LOGGER.debug("value.type %s not in type %s",
value.type, schema[const.DISC_TYPE])
return False
if (const.DISC_GENRE in schema and
value.genre not in schema[const.DISC_GENRE]):
_LOGGER.debug("value.genre %s not in genre %s",
value.genre, schema[const.DISC_GENRE])
return False
if (const.DISC_READONLY in schema and
value.is_read_only is not schema[const.DISC_READONLY]):
_LOGGER.debug("value.is_read_only %s not %s",
value.is_read_only, schema[const.DISC_READONLY])
return False
if (const.DISC_WRITEONLY in schema and
value.is_write_only is not schema[const.DISC_WRITEONLY]):
_LOGGER.debug("value.is_write_only %s not %s",
value.is_write_only, schema[const.DISC_WRITEONLY])
return False
if (const.DISC_LABEL in schema and
value.label not in schema[const.DISC_LABEL]):
_LOGGER.debug("value.label %s not in label %s",
value.label, schema[const.DISC_LABEL])
return False
if (const.DISC_INDEX in schema and
value.index not in schema[const.DISC_INDEX]):
_LOGGER.debug("value.index %s not in index %s",
value.index, schema[const.DISC_INDEX])
return False
if (const.DISC_INSTANCE in schema and
value.instance not in schema[const.DISC_INSTANCE]):
_LOGGER.debug("value.instance %s not in instance %s",
value.instance, schema[const.DISC_INSTANCE])
return False
return True
"""
values = []
if class_id is None:
values.extend(value.node.get_values(**kwargs).values())
else:
if not isinstance(class_id, list):
class_id = [class_id]
for cid in class_id:
values.extend(value.node.get_values(
class_id=cid, **kwargs).values())
_LOGGER.debug('method=%s, class_id=%s, index=%s, label=%s, data=%s,'
' member=%s, instance=%d, kwargs=%s',
method, class_id, index, label, data, member, instance,
kwargs)
_LOGGER.debug('values=%s', values)
results = None
for value in values:
if index is not None and value.index != index:
continue
if label is not None:
label_found = False
for entry in label:
if value.label == entry:
label_found = True
break
if not label_found:
continue
if method == 'set':
value.data = data
return
if data is not None and value.data != data:
continue
if instance is not None and value.instance != instance:
continue
if member is not None:
results = getattr(value, member)
else:
results = value
break
_LOGGER.debug('final result=%s', results)
return results