diff --git a/homeassistant/components/binary_sensor/zwave.py b/homeassistant/components/binary_sensor/zwave.py index 5fd9c39ef2a..e0ff8535cfd 100644 --- a/homeassistant/components/binary_sensor/zwave.py +++ b/homeassistant/components/binary_sensor/zwave.py @@ -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 diff --git a/homeassistant/components/climate/zwave.py b/homeassistant/components/climate/zwave.py index 4d625d78b62..660eb76098d 100755 --- a/homeassistant/components/climate/zwave.py +++ b/homeassistant/components/climate/zwave.py @@ -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 diff --git a/homeassistant/components/cover/zwave.py b/homeassistant/components/cover/zwave.py index 129dbd32ffe..46f23a68515 100644 --- a/homeassistant/components/cover/zwave.py +++ b/homeassistant/components/cover/zwave.py @@ -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): diff --git a/homeassistant/components/light/zwave.py b/homeassistant/components/light/zwave.py index c2357787999..59df6d8a745 100644 --- a/homeassistant/components/light/zwave.py +++ b/homeassistant/components/light/zwave.py @@ -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) diff --git a/homeassistant/components/lock/zwave.py b/homeassistant/components/lock/zwave.py index 55e5b6276a6..cfafe955e2c 100644 --- a/homeassistant/components/lock/zwave.py +++ b/homeassistant/components/lock/zwave.py @@ -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 diff --git a/homeassistant/components/sensor/zwave.py b/homeassistant/components/sensor/zwave.py index 25371d1ca78..0c4d61d86d2 100644 --- a/homeassistant/components/sensor/zwave.py +++ b/homeassistant/components/sensor/zwave.py @@ -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): diff --git a/homeassistant/components/switch/zwave.py b/homeassistant/components/switch/zwave.py index ba64785013f..bbae1c1c68c 100644 --- a/homeassistant/components/switch/zwave.py +++ b/homeassistant/components/switch/zwave.py @@ -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) diff --git a/homeassistant/components/zwave/__init__.py b/homeassistant/components/zwave/__init__.py index 6450c1f9393..2d249146ea4 100755 --- a/homeassistant/components/zwave/__init__.py +++ b/homeassistant/components/zwave/__init__.py @@ -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): diff --git a/homeassistant/components/zwave/const.py b/homeassistant/components/zwave/const.py index edcdbab1e71..ab4c7604dc4 100644 --- a/homeassistant/components/zwave/const.py +++ b/homeassistant/components/zwave/const.py @@ -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" diff --git a/homeassistant/components/zwave/discovery_schemas.py b/homeassistant/components/zwave/discovery_schemas.py deleted file mode 100644 index 9b4af47000f..00000000000 --- a/homeassistant/components/zwave/discovery_schemas.py +++ /dev/null @@ -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, - }})}, -] diff --git a/homeassistant/components/zwave/util.py b/homeassistant/components/zwave/util.py index 09c14fde80a..3ce99dbf1ab 100644 --- a/homeassistant/components/zwave/util.py +++ b/homeassistant/components/zwave/util.py @@ -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