-
-
Notifications
You must be signed in to change notification settings - Fork 31.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial Fibaro HC Climate support (#20256)
* Initial version of climate * initial commit of climate device support * Fixed opmode and fanmode * Cleanup * meh * added back all other components Oops * wider support for thermostats Added one more identifier for thermostats to broaden compatibility * Added even more climate types * Reworked detection mechanism Better support for combined devices * Added additional modes * force visibility on climate * Changed logging of device data * Improved operatingmode support Improved operatingmode support * Updated logic for opmode/fanmode list creation Implemented a universal mapping logic for opmode and fanmode, to make it more widely compatible Improved mapping of Fibaro FGT devices * Lint fixes * bump * Fixes based on code review * Fixes * Moved to fibaro folder * lint inspired cosmetic changes * Mapped all operating modes to existing HA ones Mapped all operating modes to existing HA ones * Improved compatibility with Heatit thermostats Thanks to astrandb for testing, debugging and fixing my code * Changes based on code review Changes based on code review * more fixes based on more code review more fixes based on more code review
- Loading branch information
1 parent
38f063a
commit 1a05f7b
Showing
2 changed files
with
325 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,291 @@ | ||
"""Support for Fibaro thermostats.""" | ||
import logging | ||
|
||
from homeassistant.components.climate.const import ( | ||
STATE_AUTO, STATE_COOL, STATE_DRY, | ||
STATE_ECO, STATE_FAN_ONLY, STATE_HEAT, | ||
STATE_MANUAL, SUPPORT_TARGET_TEMPERATURE, | ||
SUPPORT_OPERATION_MODE, SUPPORT_FAN_MODE) | ||
|
||
from homeassistant.components.climate import ( | ||
ClimateDevice) | ||
|
||
from homeassistant.const import ( | ||
ATTR_TEMPERATURE, | ||
STATE_OFF, | ||
TEMP_CELSIUS, | ||
TEMP_FAHRENHEIT) | ||
|
||
from . import ( | ||
FIBARO_DEVICES, FibaroDevice) | ||
|
||
SPEED_LOW = 'low' | ||
SPEED_MEDIUM = 'medium' | ||
SPEED_HIGH = 'high' | ||
|
||
# State definitions missing from HA, but defined by Z-Wave standard. | ||
# We map them to states known supported by HA here: | ||
STATE_AUXILIARY = STATE_HEAT | ||
STATE_RESUME = STATE_HEAT | ||
STATE_MOIST = STATE_DRY | ||
STATE_AUTO_CHANGEOVER = STATE_AUTO | ||
STATE_ENERGY_HEAT = STATE_ECO | ||
STATE_ENERGY_COOL = STATE_COOL | ||
STATE_FULL_POWER = STATE_AUTO | ||
STATE_FORCE_OPEN = STATE_MANUAL | ||
STATE_AWAY = STATE_AUTO | ||
STATE_FURNACE = STATE_HEAT | ||
|
||
FAN_AUTO_HIGH = 'auto_high' | ||
FAN_AUTO_MEDIUM = 'auto_medium' | ||
FAN_CIRCULATION = 'circulation' | ||
FAN_HUMIDITY_CIRCULATION = 'humidity_circulation' | ||
FAN_LEFT_RIGHT = 'left_right' | ||
FAN_UP_DOWN = 'up_down' | ||
FAN_QUIET = 'quiet' | ||
|
||
DEPENDENCIES = ['fibaro'] | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
# SDS13781-10 Z-Wave Application Command Class Specification 2019-01-04 | ||
# Table 128, Thermostat Fan Mode Set version 4::Fan Mode encoding | ||
FANMODES = { | ||
0: STATE_OFF, | ||
1: SPEED_LOW, | ||
2: FAN_AUTO_HIGH, | ||
3: SPEED_HIGH, | ||
4: FAN_AUTO_MEDIUM, | ||
5: SPEED_MEDIUM, | ||
6: FAN_CIRCULATION, | ||
7: FAN_HUMIDITY_CIRCULATION, | ||
8: FAN_LEFT_RIGHT, | ||
9: FAN_UP_DOWN, | ||
10: FAN_QUIET, | ||
128: STATE_AUTO | ||
} | ||
|
||
# SDS13781-10 Z-Wave Application Command Class Specification 2019-01-04 | ||
# Table 130, Thermostat Mode Set version 3::Mode encoding. | ||
OPMODES = { | ||
0: STATE_OFF, | ||
1: STATE_HEAT, | ||
2: STATE_COOL, | ||
3: STATE_AUTO, | ||
4: STATE_AUXILIARY, | ||
5: STATE_RESUME, | ||
6: STATE_FAN_ONLY, | ||
7: STATE_FURNACE, | ||
8: STATE_DRY, | ||
9: STATE_MOIST, | ||
10: STATE_AUTO_CHANGEOVER, | ||
11: STATE_ENERGY_HEAT, | ||
12: STATE_ENERGY_COOL, | ||
13: STATE_AWAY, | ||
15: STATE_FULL_POWER, | ||
31: STATE_FORCE_OPEN | ||
} | ||
|
||
SUPPORT_FLAGS = (SUPPORT_TARGET_TEMPERATURE) | ||
|
||
|
||
def setup_platform(hass, config, add_entities, discovery_info=None): | ||
"""Perform the setup for Fibaro controller devices.""" | ||
if discovery_info is None: | ||
return | ||
|
||
add_entities( | ||
[FibaroThermostat(device) | ||
for device in hass.data[FIBARO_DEVICES]['climate']], True) | ||
|
||
|
||
class FibaroThermostat(FibaroDevice, ClimateDevice): | ||
"""Representation of a Fibaro Thermostat.""" | ||
|
||
def __init__(self, fibaro_device): | ||
"""Initialize the Fibaro device.""" | ||
super().__init__(fibaro_device) | ||
self._temp_sensor_device = None | ||
self._target_temp_device = None | ||
self._op_mode_device = None | ||
self._fan_mode_device = None | ||
self._support_flags = 0 | ||
self.entity_id = 'climate.{}'.format(self.ha_id) | ||
self._fan_mode_to_state = {} | ||
self._fan_state_to_mode = {} | ||
self._op_mode_to_state = {} | ||
self._op_state_to_mode = {} | ||
|
||
siblings = fibaro_device.fibaro_controller.get_siblings( | ||
fibaro_device.id) | ||
tempunit = 'C' | ||
for device in siblings: | ||
if device.type == 'com.fibaro.temperatureSensor': | ||
self._temp_sensor_device = FibaroDevice(device) | ||
tempunit = device.properties.unit | ||
if 'setTargetLevel' in device.actions or \ | ||
'setThermostatSetpoint' in device.actions: | ||
self._target_temp_device = FibaroDevice(device) | ||
self._support_flags |= SUPPORT_TARGET_TEMPERATURE | ||
tempunit = device.properties.unit | ||
if 'setMode' in device.actions or \ | ||
'setOperatingMode' in device.actions: | ||
self._op_mode_device = FibaroDevice(device) | ||
self._support_flags |= SUPPORT_OPERATION_MODE | ||
if 'setFanMode' in device.actions: | ||
self._fan_mode_device = FibaroDevice(device) | ||
self._support_flags |= SUPPORT_FAN_MODE | ||
|
||
if tempunit == 'F': | ||
self._unit_of_temp = TEMP_FAHRENHEIT | ||
else: | ||
self._unit_of_temp = TEMP_CELSIUS | ||
|
||
if self._fan_mode_device: | ||
fan_modes = self._fan_mode_device.fibaro_device.\ | ||
properties.supportedModes.split(",") | ||
for mode in fan_modes: | ||
try: | ||
self._fan_mode_to_state[int(mode)] = FANMODES[int(mode)] | ||
self._fan_state_to_mode[FANMODES[int(mode)]] = int(mode) | ||
except KeyError: | ||
self._fan_mode_to_state[int(mode)] = 'unknown' | ||
|
||
if self._op_mode_device: | ||
prop = self._op_mode_device.fibaro_device.properties | ||
if "supportedOperatingModes" in prop: | ||
op_modes = prop.supportedOperatingModes.split(",") | ||
elif "supportedModes" in prop: | ||
op_modes = prop.supportedModes.split(",") | ||
for mode in op_modes: | ||
try: | ||
self._op_mode_to_state[int(mode)] = OPMODES[int(mode)] | ||
self._op_state_to_mode[OPMODES[int(mode)]] = int(mode) | ||
except KeyError: | ||
self._op_mode_to_state[int(mode)] = 'unknown' | ||
|
||
async def async_added_to_hass(self): | ||
"""Call when entity is added to hass.""" | ||
_LOGGER.debug("Climate %s\n" | ||
"- _temp_sensor_device %s\n" | ||
"- _target_temp_device %s\n" | ||
"- _op_mode_device %s\n" | ||
"- _fan_mode_device %s", | ||
self.ha_id, | ||
self._temp_sensor_device.ha_id | ||
if self._temp_sensor_device else "None", | ||
self._target_temp_device.ha_id | ||
if self._target_temp_device else "None", | ||
self._op_mode_device.ha_id | ||
if self._op_mode_device else "None", | ||
self._fan_mode_device.ha_id | ||
if self._fan_mode_device else "None") | ||
await super().async_added_to_hass() | ||
|
||
# Register update callback for child devices | ||
siblings = self.fibaro_device.fibaro_controller.get_siblings( | ||
self.fibaro_device.id) | ||
for device in siblings: | ||
if device != self.fibaro_device: | ||
self.controller.register(device.id, | ||
self._update_callback) | ||
|
||
@property | ||
def supported_features(self): | ||
"""Return the list of supported features.""" | ||
return self._support_flags | ||
|
||
@property | ||
def fan_list(self): | ||
"""Return the list of available fan modes.""" | ||
if self._fan_mode_device is None: | ||
return None | ||
return list(self._fan_state_to_mode) | ||
|
||
@property | ||
def current_fan_mode(self): | ||
"""Return the fan setting.""" | ||
if self._fan_mode_device is None: | ||
return None | ||
|
||
mode = int(self._fan_mode_device.fibaro_device.properties.mode) | ||
return self._fan_mode_to_state[mode] | ||
|
||
def set_fan_mode(self, fan_mode): | ||
"""Set new target fan mode.""" | ||
if self._fan_mode_device is None: | ||
return | ||
self._fan_mode_device.action( | ||
"setFanMode", self._fan_state_to_mode[fan_mode]) | ||
|
||
@property | ||
def current_operation(self): | ||
"""Return current operation ie. heat, cool, idle.""" | ||
if self._op_mode_device is None: | ||
return None | ||
|
||
if "operatingMode" in self._op_mode_device.fibaro_device.properties: | ||
mode = int(self._op_mode_device.fibaro_device. | ||
properties.operatingMode) | ||
else: | ||
mode = int(self._op_mode_device.fibaro_device.properties.mode) | ||
return self._op_mode_to_state.get(mode) | ||
|
||
@property | ||
def operation_list(self): | ||
"""Return the list of available operation modes.""" | ||
if self._op_mode_device is None: | ||
return None | ||
return list(self._op_state_to_mode) | ||
|
||
def set_operation_mode(self, operation_mode): | ||
"""Set new target operation mode.""" | ||
if self._op_mode_device is None: | ||
return | ||
if "setOperatingMode" in self._op_mode_device.fibaro_device.actions: | ||
self._op_mode_device.action( | ||
"setOperatingMode", self._op_state_to_mode[operation_mode]) | ||
elif "setMode" in self._op_mode_device.fibaro_device.actions: | ||
self._op_mode_device.action( | ||
"setMode", self._op_state_to_mode[operation_mode]) | ||
|
||
@property | ||
def temperature_unit(self): | ||
"""Return the unit of measurement.""" | ||
return self._unit_of_temp | ||
|
||
@property | ||
def current_temperature(self): | ||
"""Return the current temperature.""" | ||
if self._temp_sensor_device: | ||
device = self._temp_sensor_device.fibaro_device | ||
return float(device.properties.value) | ||
return None | ||
|
||
@property | ||
def target_temperature(self): | ||
"""Return the temperature we try to reach.""" | ||
if self._target_temp_device: | ||
device = self._target_temp_device.fibaro_device | ||
return float(device.properties.targetLevel) | ||
return None | ||
|
||
def set_temperature(self, **kwargs): | ||
"""Set new target temperatures.""" | ||
temperature = kwargs.get(ATTR_TEMPERATURE) | ||
target = self._target_temp_device | ||
if temperature is not None: | ||
if "setThermostatSetpoint" in target.fibaro_device.actions: | ||
target.action("setThermostatSetpoint", | ||
self._op_state_to_mode[self.current_operation], | ||
temperature) | ||
else: | ||
target.action("setTargetLevel", | ||
temperature) | ||
|
||
@property | ||
def is_on(self): | ||
"""Return true if on.""" | ||
if self.current_operation == STATE_OFF: | ||
return False | ||
return True |