diff --git a/homeassistant/components/light/zwave.py b/homeassistant/components/light/zwave.py index 02664ed896c..62c970badc1 100644 --- a/homeassistant/components/light/zwave.py +++ b/homeassistant/components/light/zwave.py @@ -11,8 +11,10 @@ https://home-assistant.io/components/light.zwave/ from threading import Timer from homeassistant.const import STATE_ON, STATE_OFF -from homeassistant.components.light import (Light, ATTR_BRIGHTNESS) -import homeassistant.components.zwave as zwave +from homeassistant.components.light import Light, ATTR_BRIGHTNESS, DOMAIN +from homeassistant.components.zwave import ( + COMMAND_CLASS_SWITCH_MULTILEVEL, TYPE_BYTE, GENRE_USER, NETWORK, + ATTR_NODE_ID, ATTR_VALUE_ID, ZWaveDeviceEntity) def setup_platform(hass, config, add_devices, discovery_info=None): @@ -20,14 +22,14 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if discovery_info is None: return - node = zwave.NETWORK.nodes[discovery_info[zwave.ATTR_NODE_ID]] - value = node.values[discovery_info[zwave.ATTR_VALUE_ID]] + node = NETWORK.nodes[discovery_info[ATTR_NODE_ID]] + value = node.values[discovery_info[ATTR_VALUE_ID]] - if value.command_class != zwave.COMMAND_CLASS_SWITCH_MULTILEVEL: + if value.command_class != COMMAND_CLASS_SWITCH_MULTILEVEL: return - if value.type != zwave.TYPE_BYTE: + if value.type != TYPE_BYTE: return - if value.genre != zwave.GENRE_USER: + if value.genre != GENRE_USER: return value.set_change_verified(False) @@ -45,15 +47,14 @@ def brightness_state(value): return 255, STATE_OFF -class ZwaveDimmer(Light): +class ZwaveDimmer(ZWaveDeviceEntity, Light): """ Provides a Z-Wave dimmer. """ # pylint: disable=too-many-arguments def __init__(self, value): from openzwave.network import ZWaveNetwork from pydispatch import dispatcher - self._value = value - self._node = value.node + ZWaveDeviceEntity.__init__(self, value, DOMAIN) self._brightness, self._state = brightness_state(value) @@ -86,18 +87,6 @@ class ZwaveDimmer(Light): self.update_ha_state() - @property - def should_poll(self): - """ No polling needed for a light. """ - return False - - @property - def name(self): - """ Returns the name of the device if any. """ - name = self._node.name or "{}".format(self._node.product_name) - - return "{}".format(name or self._value.label) - @property def brightness(self): """ Brightness of this light between 0..255. """ @@ -118,10 +107,10 @@ class ZwaveDimmer(Light): # brightness. brightness = (self._brightness / 255) * 99 - if self._node.set_dimmer(self._value.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._value.value_id, 0): + if self._value.node.set_dimmer(self._value.value_id, 0): self._state = STATE_OFF diff --git a/homeassistant/components/sensor/zwave.py b/homeassistant/components/sensor/zwave.py index 869f4dbe810..f28daa5b9e4 100644 --- a/homeassistant/components/sensor/zwave.py +++ b/homeassistant/components/sensor/zwave.py @@ -12,11 +12,15 @@ import datetime from homeassistant.helpers.event import track_point_in_time import homeassistant.util.dt as dt_util -import homeassistant.components.zwave as zwave +from homeassistant.components.sensor import DOMAIN from homeassistant.helpers.entity import Entity +from homeassistant.components.zwave import ( + NETWORK, ATTR_NODE_ID, ATTR_VALUE_ID, COMMAND_CLASS_SENSOR_BINARY, + COMMAND_CLASS_SENSOR_MULTILEVEL, COMMAND_CLASS_METER, TYPE_DECIMAL, + COMMAND_CLASS_ALARM, ZWaveDeviceEntity, get_config_value) + from homeassistant.const import ( - ATTR_BATTERY_LEVEL, STATE_ON, STATE_OFF, - TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_LOCATION) + STATE_ON, STATE_OFF, TEMP_CELCIUS, TEMP_FAHRENHEIT) PHILIO = '013c' PHILIO_SLIM_SENSOR = '0002' @@ -41,14 +45,14 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if discovery_info is None: return - node = zwave.NETWORK.nodes[discovery_info[zwave.ATTR_NODE_ID]] - value = node.values[discovery_info[zwave.ATTR_VALUE_ID]] + node = NETWORK.nodes[discovery_info[ATTR_NODE_ID]] + value = node.values[discovery_info[ATTR_VALUE_ID]] value.set_change_verified(False) - # if 1 in groups and (zwave.NETWORK.controller.node_id not in + # if 1 in groups and (NETWORK.controller.node_id not in # groups[1].associations): - # node.groups[1].add_association(zwave.NETWORK.controller.node_id) + # node.groups[1].add_association(NETWORK.controller.node_id) specific_sensor_key = (value.node.manufacturer_id, value.node.product_id, @@ -58,81 +62,43 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if specific_sensor_key in DEVICE_MAPPINGS: if DEVICE_MAPPINGS[specific_sensor_key] == WORKAROUND_NO_OFF_EVENT: # Default the multiplier to 4 - re_arm_multiplier = (zwave.get_config_value(value.node, 9) or 4) + re_arm_multiplier = (get_config_value(value.node, 9) or 4) add_devices([ ZWaveTriggerSensor(value, hass, re_arm_multiplier * 8) ]) # generic Device mappings - elif value.command_class == zwave.COMMAND_CLASS_SENSOR_BINARY: + elif value.command_class == COMMAND_CLASS_SENSOR_BINARY: add_devices([ZWaveBinarySensor(value)]) - elif value.command_class == zwave.COMMAND_CLASS_SENSOR_MULTILEVEL: + elif value.command_class == COMMAND_CLASS_SENSOR_MULTILEVEL: add_devices([ZWaveMultilevelSensor(value)]) - elif (value.command_class == zwave.COMMAND_CLASS_METER and - value.type == zwave.TYPE_DECIMAL): + elif (value.command_class == COMMAND_CLASS_METER and + value.type == TYPE_DECIMAL): add_devices([ZWaveMultilevelSensor(value)]) - elif value.command_class == zwave.COMMAND_CLASS_ALARM: + elif value.command_class == COMMAND_CLASS_ALARM: add_devices([ZWaveAlarmSensor(value)]) -class ZWaveSensor(Entity): +class ZWaveSensor(ZWaveDeviceEntity, Entity): """ Represents a Z-Wave sensor. """ def __init__(self, sensor_value): from openzwave.network import ZWaveNetwork from pydispatch import dispatcher - self._value = sensor_value - self._node = sensor_value.node + ZWaveDeviceEntity.__init__(self, sensor_value, DOMAIN) dispatcher.connect( self.value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED) - @property - def should_poll(self): - """ False because we will push our own state to HA when changed. """ - return False - - @property - def unique_id(self): - """ Returns a unique id. """ - return "ZWAVE-{}-{}".format(self._node.node_id, self._value.object_id) - - @property - def name(self): - """ Returns the name of the device. """ - name = self._node.name or "{} {}".format( - self._node.manufacturer_name, self._node.product_name) - - return "{} {}".format(name, self._value.label) - @property def state(self): """ Returns the state of the sensor. """ return self._value.data - @property - def state_attributes(self): - """ Returns the state attributes. """ - attrs = { - zwave.ATTR_NODE_ID: self._node.node_id, - } - - battery_level = self._node.get_battery_level() - - if battery_level is not None: - attrs[ATTR_BATTERY_LEVEL] = battery_level - - location = self._node.location - - if location: - attrs[ATTR_LOCATION] = location - - return attrs - @property def unit_of_measurement(self): return self._value.units diff --git a/homeassistant/components/switch/zwave.py b/homeassistant/components/switch/zwave.py index f4777340445..97254ed702f 100644 --- a/homeassistant/components/switch/zwave.py +++ b/homeassistant/components/switch/zwave.py @@ -6,9 +6,10 @@ Zwave platform that handles simple binary switches. """ # Because we do not compile openzwave on CI # pylint: disable=import-error -import homeassistant.components.zwave as zwave - -from homeassistant.components.switch import SwitchDevice +from homeassistant.components.switch import SwitchDevice, DOMAIN +from homeassistant.components.zwave import ( + COMMAND_CLASS_SWITCH_BINARY, TYPE_BOOL, GENRE_USER, NETWORK, + ATTR_NODE_ID, ATTR_VALUE_ID, ZWaveDeviceEntity) # pylint: disable=unused-argument @@ -17,28 +18,27 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if discovery_info is None: return - node = zwave.NETWORK.nodes[discovery_info[zwave.ATTR_NODE_ID]] - value = node.values[discovery_info[zwave.ATTR_VALUE_ID]] + node = NETWORK.nodes[discovery_info[ATTR_NODE_ID]] + value = node.values[discovery_info[ATTR_VALUE_ID]] - if value.command_class != zwave.COMMAND_CLASS_SWITCH_BINARY: + if value.command_class != COMMAND_CLASS_SWITCH_BINARY: return - if value.type != zwave.TYPE_BOOL: + if value.type != TYPE_BOOL: return - if value.genre != zwave.GENRE_USER: + if value.genre != GENRE_USER: return value.set_change_verified(False) add_devices([ZwaveSwitch(value)]) -class ZwaveSwitch(SwitchDevice): +class ZwaveSwitch(ZWaveDeviceEntity, SwitchDevice): """ Provides a zwave switch. """ def __init__(self, value): from openzwave.network import ZWaveNetwork from pydispatch import dispatcher - self._value = value - self._node = value.node + ZWaveDeviceEntity.__init__(self, value, DOMAIN) self._state = value.data dispatcher.connect( @@ -50,18 +50,6 @@ class ZwaveSwitch(SwitchDevice): self._state = value.data self.update_ha_state() - @property - def should_poll(self): - """ No polling needed for a demo switch. """ - return False - - @property - def name(self): - """ Returns the name of the device if any. """ - name = self._node.name or "{}".format(self._node.product_name) - - return "{}".format(name or self._value.label) - @property def is_on(self): """ True if device is on. """ @@ -69,8 +57,8 @@ class ZwaveSwitch(SwitchDevice): def turn_on(self, **kwargs): """ Turn the device on. """ - self._node.set_switch(self._value.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._value.value_id, False) + self._value.node.set_switch(self._value.value_id, False) diff --git a/homeassistant/components/zwave.py b/homeassistant/components/zwave.py index 6b4de82a70a..a46918353ec 100644 --- a/homeassistant/components/zwave.py +++ b/homeassistant/components/zwave.py @@ -10,11 +10,13 @@ import sys import os.path from pprint import pprint - +from homeassistant.util import slugify from homeassistant import bootstrap from homeassistant.const import ( EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, - EVENT_PLATFORM_DISCOVERED, ATTR_SERVICE, ATTR_DISCOVERED) + EVENT_PLATFORM_DISCOVERED, ATTR_SERVICE, ATTR_DISCOVERED, + ATTR_BATTERY_LEVEL, ATTR_LOCATION) + DOMAIN = "zwave" REQUIREMENTS = ['pydispatcher==2.0.5'] @@ -211,3 +213,62 @@ def setup(hass, config): hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_zwave) return True + + +class ZWaveDeviceEntity: + """ Represents a ZWave node entity within Home Assistant. """ + def __init__(self, value, domain): + self._value = value + self.entity_id = "{}.{}".format(domain, self._object_id()) + + @property + def should_poll(self): + """ False because we will push our own state to HA when changed. """ + return False + + @property + def unique_id(self): + """ Returns a unique id. """ + return "ZWAVE-{}-{}".format(self._value.node.node_id, + self._value.object_id) + + @property + def name(self): + """ Returns the name of the device. """ + name = self._value.node.name or "{} {}".format( + self._value.node.manufacturer_name, self._value.node.product_name) + + return "{} {}".format(name, self._value.label) + + def _object_id(self): + """ Returns the object_id of the device value. + The object_id contains node_id and value instance id + to not collide with other entity_ids""" + + object_id = "{}_{}".format(slugify(self.name), + self._value.node.node_id) + + # Add the instance id if there is more than one instance for the value + if self._value.instance > 1: + return "{}_{}".format(object_id, self._value.instance) + + return object_id + + @property + def state_attributes(self): + """ Returns the state attributes. """ + attrs = { + ATTR_NODE_ID: self._value.node.node_id, + } + + battery_level = self._value.node.get_battery_level() + + if battery_level is not None: + attrs[ATTR_BATTERY_LEVEL] = battery_level + + location = self._value.node.location + + if location: + attrs[ATTR_LOCATION] = location + + return attrs