diff --git a/homeassistant/components/thermostat/__init__.py b/homeassistant/components/thermostat/__init__.py index c6ddbeb0df4..f31d43857ac 100644 --- a/homeassistant/components/thermostat/__init__.py +++ b/homeassistant/components/thermostat/__init__.py @@ -14,7 +14,7 @@ import homeassistant.util as util from homeassistant.helpers.entity import Entity from homeassistant.helpers.temperature import convert from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa -from homeassistant.components import ecobee +from homeassistant.components import (ecobee, zwave) from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_TEMPERATURE, STATE_ON, STATE_OFF, STATE_UNKNOWN, TEMP_CELCIUS) @@ -45,6 +45,7 @@ _LOGGER = logging.getLogger(__name__) DISCOVERY_PLATFORMS = { ecobee.DISCOVER_THERMOSTAT: 'ecobee', + zwave.DISCOVER_THERMOSTATS: 'zwave' } diff --git a/homeassistant/components/thermostat/zwave.py b/homeassistant/components/thermostat/zwave.py new file mode 100644 index 00000000000..fe6ff05203d --- /dev/null +++ b/homeassistant/components/thermostat/zwave.py @@ -0,0 +1,114 @@ +"""ZWave Thermostat.""" + +# Because we do not compile openzwave on CI +# pylint: disable=import-error +from homeassistant.components.thermostat import DOMAIN +from homeassistant.components.thermostat import ( + ThermostatDevice, + STATE_IDLE) +from homeassistant.components.zwave import ( + ATTR_NODE_ID, ATTR_VALUE_ID, NETWORK, + ZWaveDeviceEntity) +from homeassistant.const import (TEMP_FAHRENHEIT, TEMP_CELCIUS) + +CONF_NAME = 'name' +DEFAULT_NAME = 'ZWave Thermostat' + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Setup the ZWave thermostats.""" + if discovery_info is None or NETWORK is None: + return + + node = NETWORK.nodes[discovery_info[ATTR_NODE_ID]] + value = node.values[discovery_info[ATTR_VALUE_ID]] + value.set_change_verified(False) + add_devices([ZWaveThermostat(value)]) + + +# pylint: disable=too-many-arguments +class ZWaveThermostat(ZWaveDeviceEntity, ThermostatDevice): + """Represents a HeatControl thermostat.""" + + def __init__(self, value): + """Initialize the zwave thermostat.""" + from openzwave.network import ZWaveNetwork + from pydispatch import dispatcher + ZWaveDeviceEntity.__init__(self, value, DOMAIN) + self._node = value.node + self._target_temperature = None + self._current_temperature = None + self._current_operation = STATE_IDLE + self._current_operation_state = STATE_IDLE + self._unit = None + self.update_properties() + # register listener + dispatcher.connect( + self.value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED) + + def value_changed(self, value): + """Called when a value has changed on the network.""" + if self._value.node == value.node: + self.update_properties() + self.update_ha_state() + + def update_properties(self): + """Callback on data change for the registered node/value pair.""" + # set point + for _, value in self._node.get_values(class_id=0x43).items(): + if int(value.data) != 0: + self._target_temperature = int(value.data) + # Operation + for _, value in self._node.get_values(class_id=0x40).items(): + self._current_operation = value.data_as_string + # Current Temp + for _, value in self._node.get_values_for_command_class(0x31).items(): + self._current_temperature = int(value.data) + self._unit = value.units + # COMMAND_CLASS_THERMOSTAT_OPERATING_STATE + for _, value in self._node.get_values(class_id=0x42).items(): + self._current_operation_state = value.data_as_string + + @property + def should_poll(self): + """No polling on ZWave.""" + return False + + @property + def is_fan_on(self): + """Return if the fan is not idle.""" + return self._current_operation_state != 'Idle' + + @property + def unit_of_measurement(self): + """Return the unit of measurement.""" + unit = self._unit + if unit == 'C': + return TEMP_CELCIUS + elif unit == 'F': + return TEMP_FAHRENHEIT + else: + return unit + return self.hass.config.temperature_unit + + @property + def current_temperature(self): + """Return the current temperature.""" + return self._current_temperature + + @property + def operation(self): + """Return the operation mode.""" + return self._current_operation + + @property + def target_temperature(self): + """Return the temperature we try to reach.""" + return self._target_temperature + + def set_temperature(self, temperature): + """Set new target temperature.""" + # set point + for _, value in self._node.get_values_for_command_class(0x43).items(): + if int(value.data) != 0: + value.data = temperature diff --git a/homeassistant/components/zwave.py b/homeassistant/components/zwave.py index 31729c23f45..0c6eba45106 100644 --- a/homeassistant/components/zwave.py +++ b/homeassistant/components/zwave.py @@ -41,6 +41,7 @@ DISCOVER_SENSORS = "zwave.sensors" DISCOVER_SWITCHES = "zwave.switch" DISCOVER_LIGHTS = "zwave.light" DISCOVER_BINARY_SENSORS = 'zwave.binary_sensor' +DISCOVER_THERMOSTATS = 'zwave.thermostat' EVENT_SCENE_ACTIVATED = "zwave.scene_activated" @@ -52,6 +53,7 @@ COMMAND_CLASS_SENSOR_MULTILEVEL = 49 COMMAND_CLASS_METER = 50 COMMAND_CLASS_BATTERY = 128 COMMAND_CLASS_ALARM = 113 # 0x71 +COMMAND_CLASS_THERMOSTAT_MODE = 64 # 0x40 GENRE_WHATEVER = None GENRE_USER = "User" @@ -86,7 +88,12 @@ DISCOVERY_COMPONENTS = [ DISCOVER_BINARY_SENSORS, [COMMAND_CLASS_SENSOR_BINARY], TYPE_BOOL, - GENRE_USER) + GENRE_USER), + ('thermostat', + DISCOVER_THERMOSTATS, + [COMMAND_CLASS_THERMOSTAT_MODE], + TYPE_WHATEVER, + GENRE_WHATEVER), ]