Ozw climate fixes (#37560)
* fix presets and mode conversion * fix mapping issues in ozw climate * build mapping table in advance * Copying a dict to a list copies the keys by defaultpull/37635/head
parent
2b37cbe079
commit
cbccf011e7
|
@ -12,7 +12,6 @@ from homeassistant.components.climate.const import (
|
|||
CURRENT_HVAC_HEAT,
|
||||
CURRENT_HVAC_IDLE,
|
||||
CURRENT_HVAC_OFF,
|
||||
HVAC_MODE_AUTO,
|
||||
HVAC_MODE_COOL,
|
||||
HVAC_MODE_DRY,
|
||||
HVAC_MODE_FAN_ONLY,
|
||||
|
@ -65,6 +64,16 @@ class ThermostatMode(IntEnum):
|
|||
MANUFACTURER_SPECIFIC = 31
|
||||
|
||||
|
||||
# In Z-Wave the modes and presets are both in ThermostatMode.
|
||||
# This list contains thermostatmodes we should consider a mode only
|
||||
MODES_LIST = [
|
||||
ThermostatMode.OFF,
|
||||
ThermostatMode.HEAT,
|
||||
ThermostatMode.COOL,
|
||||
ThermostatMode.AUTO,
|
||||
ThermostatMode.AUTO_CHANGE_OVER,
|
||||
]
|
||||
|
||||
MODE_SETPOINT_MAPPINGS = {
|
||||
ThermostatMode.OFF: (),
|
||||
ThermostatMode.HEAT: ("setpoint_heating",),
|
||||
|
@ -99,11 +108,14 @@ HVAC_CURRENT_MAPPINGS = {
|
|||
|
||||
|
||||
# Map Z-Wave HVAC Mode to Home Assistant value
|
||||
# Note: We treat "auto" as "heat_cool" as most Z-Wave devices
|
||||
# report auto_changeover as auto without schedule support.
|
||||
ZW_HVAC_MODE_MAPPINGS = {
|
||||
ThermostatMode.OFF: HVAC_MODE_OFF,
|
||||
ThermostatMode.HEAT: HVAC_MODE_HEAT,
|
||||
ThermostatMode.COOL: HVAC_MODE_COOL,
|
||||
ThermostatMode.AUTO: HVAC_MODE_AUTO,
|
||||
# Z-Wave auto mode is actually heat/cool in the hass world
|
||||
ThermostatMode.AUTO: HVAC_MODE_HEAT_COOL,
|
||||
ThermostatMode.AUXILIARY: HVAC_MODE_HEAT,
|
||||
ThermostatMode.FAN: HVAC_MODE_FAN_ONLY,
|
||||
ThermostatMode.FURNANCE: HVAC_MODE_HEAT,
|
||||
|
@ -120,7 +132,6 @@ HVAC_MODE_ZW_MAPPINGS = {
|
|||
HVAC_MODE_OFF: ThermostatMode.OFF,
|
||||
HVAC_MODE_HEAT: ThermostatMode.HEAT,
|
||||
HVAC_MODE_COOL: ThermostatMode.COOL,
|
||||
HVAC_MODE_AUTO: ThermostatMode.AUTO,
|
||||
HVAC_MODE_FAN_ONLY: ThermostatMode.FAN,
|
||||
HVAC_MODE_DRY: ThermostatMode.DRY,
|
||||
HVAC_MODE_HEAT_COOL: ThermostatMode.AUTO_CHANGE_OVER,
|
||||
|
@ -148,12 +159,16 @@ class ZWaveClimateEntity(ZWaveDeviceEntity, ClimateEntity):
|
|||
def __init__(self, values):
|
||||
"""Initialize the entity."""
|
||||
super().__init__(values)
|
||||
self._current_mode_setpoint_values = self._get_current_mode_setpoint_values()
|
||||
self._hvac_modes = {}
|
||||
self._hvac_presets = {}
|
||||
self.on_value_update()
|
||||
|
||||
@callback
|
||||
def on_value_update(self):
|
||||
"""Call when the underlying value(s) is added or updated."""
|
||||
"""Call when the underlying values object changes."""
|
||||
self._current_mode_setpoint_values = self._get_current_mode_setpoint_values()
|
||||
if not self._hvac_modes:
|
||||
self._set_modes_and_presets()
|
||||
|
||||
@property
|
||||
def hvac_mode(self):
|
||||
|
@ -161,21 +176,13 @@ class ZWaveClimateEntity(ZWaveDeviceEntity, ClimateEntity):
|
|||
if not self.values.mode:
|
||||
return None
|
||||
return ZW_HVAC_MODE_MAPPINGS.get(
|
||||
self.values.mode.value[VALUE_SELECTED_ID], HVAC_MODE_AUTO
|
||||
self.values.mode.value[VALUE_SELECTED_ID], HVAC_MODE_HEAT_COOL
|
||||
)
|
||||
|
||||
@property
|
||||
def hvac_modes(self):
|
||||
"""Return the list of available hvac operation modes."""
|
||||
if not self.values.mode:
|
||||
return []
|
||||
# Z-Wave uses one list for both modes and presets. Extract the unique modes
|
||||
all_modes = []
|
||||
for val in self.values.mode.value[VALUE_LIST]:
|
||||
hass_mode = ZW_HVAC_MODE_MAPPINGS.get(val[VALUE_ID])
|
||||
if hass_mode and hass_mode not in all_modes:
|
||||
all_modes.append(hass_mode)
|
||||
return all_modes
|
||||
return list(self._hvac_modes)
|
||||
|
||||
@property
|
||||
def fan_mode(self):
|
||||
|
@ -212,20 +219,15 @@ class ZWaveClimateEntity(ZWaveDeviceEntity, ClimateEntity):
|
|||
@property
|
||||
def preset_mode(self):
|
||||
"""Return preset operation ie. eco, away."""
|
||||
# Z-Wave uses mode-values > 10 for presets
|
||||
if self.values.mode.value[VALUE_SELECTED_ID] > 10:
|
||||
# A Zwave mode that can not be translated to a hass mode is considered a preset
|
||||
if self.values.mode.value[VALUE_SELECTED_ID] not in MODES_LIST:
|
||||
return self.values.mode.value[VALUE_SELECTED_LABEL]
|
||||
return PRESET_NONE
|
||||
|
||||
@property
|
||||
def preset_modes(self):
|
||||
"""Return the list of available preset operation modes."""
|
||||
# Z-Wave uses mode-values > 10 for presets
|
||||
return [PRESET_NONE] + [
|
||||
val[VALUE_LABEL]
|
||||
for val in self.values.mode.value[VALUE_LIST]
|
||||
if val[VALUE_ID] > 10
|
||||
]
|
||||
return list(self._hvac_presets)
|
||||
|
||||
@property
|
||||
def target_temperature(self):
|
||||
|
@ -272,12 +274,10 @@ class ZWaveClimateEntity(ZWaveDeviceEntity, ClimateEntity):
|
|||
|
||||
async def async_set_hvac_mode(self, hvac_mode):
|
||||
"""Set new target hvac mode."""
|
||||
if not self.values.mode:
|
||||
return
|
||||
if hvac_mode not in self.hvac_modes:
|
||||
hvac_mode_value = self._hvac_modes.get(hvac_mode)
|
||||
if not hvac_mode_value:
|
||||
_LOGGER.warning("Received an invalid hvac mode: %s", hvac_mode)
|
||||
return
|
||||
hvac_mode_value = HVAC_MODE_ZW_MAPPINGS.get(hvac_mode)
|
||||
self.values.mode.send_value(hvac_mode_value)
|
||||
|
||||
async def async_set_preset_mode(self, preset_mode):
|
||||
|
@ -286,9 +286,7 @@ class ZWaveClimateEntity(ZWaveDeviceEntity, ClimateEntity):
|
|||
# try to restore to the (translated) main hvac mode
|
||||
await self.async_set_hvac_mode(self.hvac_mode)
|
||||
return
|
||||
preset_mode_value = _get_list_id(
|
||||
self.values.mode.value[VALUE_LIST], preset_mode
|
||||
)
|
||||
preset_mode_value = self._hvac_presets.get(preset_mode)
|
||||
if preset_mode_value is None:
|
||||
_LOGGER.warning("Received an invalid preset mode: %s", preset_mode)
|
||||
return
|
||||
|
@ -331,6 +329,25 @@ class ZWaveClimateEntity(ZWaveDeviceEntity, ClimateEntity):
|
|||
if getattr(self.values, value_name, None)
|
||||
)
|
||||
|
||||
def _set_modes_and_presets(self):
|
||||
"""Convert Z-Wave Thermostat modes into Home Assistant modes and presets."""
|
||||
if not self.values.mode:
|
||||
return
|
||||
all_modes = {}
|
||||
all_presets = {PRESET_NONE: None}
|
||||
# Z-Wave uses one list for both modes and presets.
|
||||
# Iterate over all Z-Wave ThermostatModes and extract the hvac modes and presets.
|
||||
for val in self.values.mode.value[VALUE_LIST]:
|
||||
if val[VALUE_ID] in MODES_LIST:
|
||||
# treat value as hvac mode
|
||||
hass_mode = ZW_HVAC_MODE_MAPPINGS.get(val[VALUE_ID])
|
||||
all_modes[hass_mode] = val[VALUE_ID]
|
||||
else:
|
||||
# treat value as hvac preset
|
||||
all_presets[val[VALUE_LABEL]] = val[VALUE_ID]
|
||||
self._hvac_modes = all_modes
|
||||
self._hvac_presets = all_presets
|
||||
|
||||
|
||||
def _get_list_id(value_lst, value_lbl):
|
||||
"""Return the id for the value in the list."""
|
||||
|
|
|
@ -10,9 +10,9 @@ from homeassistant.components.climate.const import (
|
|||
ATTR_TARGET_TEMP_HIGH,
|
||||
ATTR_TARGET_TEMP_LOW,
|
||||
CURRENT_HVAC_IDLE,
|
||||
HVAC_MODE_AUTO,
|
||||
HVAC_MODE_COOL,
|
||||
HVAC_MODE_HEAT,
|
||||
HVAC_MODE_HEAT_COOL,
|
||||
HVAC_MODE_OFF,
|
||||
)
|
||||
|
||||
|
@ -32,7 +32,7 @@ async def test_climate(hass, climate_data, sent_messages, climate_msg, caplog):
|
|||
HVAC_MODE_OFF,
|
||||
HVAC_MODE_HEAT,
|
||||
HVAC_MODE_COOL,
|
||||
HVAC_MODE_AUTO,
|
||||
HVAC_MODE_HEAT_COOL,
|
||||
]
|
||||
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_IDLE
|
||||
assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 23.1
|
||||
|
@ -60,7 +60,7 @@ async def test_climate(hass, climate_data, sent_messages, climate_msg, caplog):
|
|||
await hass.services.async_call(
|
||||
"climate",
|
||||
"set_hvac_mode",
|
||||
{"entity_id": "climate.ct32_thermostat_mode", "hvac_mode": HVAC_MODE_AUTO},
|
||||
{"entity_id": "climate.ct32_thermostat_mode", "hvac_mode": HVAC_MODE_HEAT_COOL},
|
||||
blocking=True,
|
||||
)
|
||||
assert len(sent_messages) == 2
|
||||
|
@ -106,7 +106,7 @@ async def test_climate(hass, climate_data, sent_messages, climate_msg, caplog):
|
|||
await hass.async_block_till_done()
|
||||
state = hass.states.get("climate.ct32_thermostat_mode")
|
||||
assert state is not None
|
||||
assert state.state == HVAC_MODE_AUTO
|
||||
assert state.state == HVAC_MODE_HEAT_COOL
|
||||
assert state.attributes.get(ATTR_TEMPERATURE) is None
|
||||
assert state.attributes[ATTR_TARGET_TEMP_LOW] == 21.1
|
||||
assert state.attributes[ATTR_TARGET_TEMP_HIGH] == 25.6
|
||||
|
|
Loading…
Reference in New Issue