Fix Tado fan speed for AC (#122415)

* change capabilities

* fix tests 2

* improve usability with capabilities

* fix swings management

* Update homeassistant/components/tado/climate.py

Co-authored-by: Erwin Douna <e.douna@gmail.com>

* fix after Erwin's review

* fix after joostlek's review

* use constant

* use in instead of get

---------

Co-authored-by: Erwin Douna <e.douna@gmail.com>
This commit is contained in:
Etienne Soufflet 2024-09-01 18:33:45 +02:00 committed by GitHub
parent ae1f53775f
commit 92c1fb77e9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 115 additions and 57 deletions

View File

@ -16,6 +16,7 @@ from homeassistant.components.climate import (
SWING_BOTH, SWING_BOTH,
SWING_HORIZONTAL, SWING_HORIZONTAL,
SWING_OFF, SWING_OFF,
SWING_ON,
SWING_VERTICAL, SWING_VERTICAL,
ClimateEntity, ClimateEntity,
ClimateEntityFeature, ClimateEntityFeature,
@ -47,7 +48,6 @@ from .const import (
HA_TO_TADO_FAN_MODE_MAP, HA_TO_TADO_FAN_MODE_MAP,
HA_TO_TADO_FAN_MODE_MAP_LEGACY, HA_TO_TADO_FAN_MODE_MAP_LEGACY,
HA_TO_TADO_HVAC_MODE_MAP, HA_TO_TADO_HVAC_MODE_MAP,
HA_TO_TADO_SWING_MODE_MAP,
ORDERED_KNOWN_TADO_MODES, ORDERED_KNOWN_TADO_MODES,
PRESET_AUTO, PRESET_AUTO,
SIGNAL_TADO_UPDATE_RECEIVED, SIGNAL_TADO_UPDATE_RECEIVED,
@ -55,17 +55,20 @@ from .const import (
SUPPORT_PRESET_MANUAL, SUPPORT_PRESET_MANUAL,
TADO_DEFAULT_MAX_TEMP, TADO_DEFAULT_MAX_TEMP,
TADO_DEFAULT_MIN_TEMP, TADO_DEFAULT_MIN_TEMP,
TADO_FAN_LEVELS, TADO_FANLEVEL_SETTING,
TADO_FAN_SPEEDS, TADO_FANSPEED_SETTING,
TADO_HORIZONTAL_SWING_SETTING,
TADO_HVAC_ACTION_TO_HA_HVAC_ACTION, TADO_HVAC_ACTION_TO_HA_HVAC_ACTION,
TADO_MODES_WITH_NO_TEMP_SETTING, TADO_MODES_WITH_NO_TEMP_SETTING,
TADO_SWING_OFF, TADO_SWING_OFF,
TADO_SWING_ON, TADO_SWING_ON,
TADO_SWING_SETTING,
TADO_TO_HA_FAN_MODE_MAP, TADO_TO_HA_FAN_MODE_MAP,
TADO_TO_HA_FAN_MODE_MAP_LEGACY, TADO_TO_HA_FAN_MODE_MAP_LEGACY,
TADO_TO_HA_HVAC_MODE_MAP, TADO_TO_HA_HVAC_MODE_MAP,
TADO_TO_HA_OFFSET_MAP, TADO_TO_HA_OFFSET_MAP,
TADO_TO_HA_SWING_MODE_MAP, TADO_TO_HA_SWING_MODE_MAP,
TADO_VERTICAL_SWING_SETTING,
TEMP_OFFSET, TEMP_OFFSET,
TYPE_AIR_CONDITIONING, TYPE_AIR_CONDITIONING,
TYPE_HEATING, TYPE_HEATING,
@ -166,29 +169,30 @@ def create_climate_entity(
supported_hvac_modes.append(TADO_TO_HA_HVAC_MODE_MAP[mode]) supported_hvac_modes.append(TADO_TO_HA_HVAC_MODE_MAP[mode])
if ( if (
capabilities[mode].get("swings") TADO_SWING_SETTING in capabilities[mode]
or capabilities[mode].get("verticalSwing") or TADO_VERTICAL_SWING_SETTING in capabilities[mode]
or capabilities[mode].get("horizontalSwing") or TADO_VERTICAL_SWING_SETTING in capabilities[mode]
): ):
support_flags |= ClimateEntityFeature.SWING_MODE support_flags |= ClimateEntityFeature.SWING_MODE
supported_swing_modes = [] supported_swing_modes = []
if capabilities[mode].get("swings"): if TADO_SWING_SETTING in capabilities[mode]:
supported_swing_modes.append( supported_swing_modes.append(
TADO_TO_HA_SWING_MODE_MAP[TADO_SWING_ON] TADO_TO_HA_SWING_MODE_MAP[TADO_SWING_ON]
) )
if capabilities[mode].get("verticalSwing"): if TADO_VERTICAL_SWING_SETTING in capabilities[mode]:
supported_swing_modes.append(SWING_VERTICAL) supported_swing_modes.append(SWING_VERTICAL)
if capabilities[mode].get("horizontalSwing"): if TADO_HORIZONTAL_SWING_SETTING in capabilities[mode]:
supported_swing_modes.append(SWING_HORIZONTAL) supported_swing_modes.append(SWING_HORIZONTAL)
if ( if (
SWING_HORIZONTAL in supported_swing_modes SWING_HORIZONTAL in supported_swing_modes
and SWING_HORIZONTAL in supported_swing_modes and SWING_VERTICAL in supported_swing_modes
): ):
supported_swing_modes.append(SWING_BOTH) supported_swing_modes.append(SWING_BOTH)
supported_swing_modes.append(TADO_TO_HA_SWING_MODE_MAP[TADO_SWING_OFF]) supported_swing_modes.append(TADO_TO_HA_SWING_MODE_MAP[TADO_SWING_OFF])
if not capabilities[mode].get("fanSpeeds") and not capabilities[mode].get( if (
"fanLevel" TADO_FANSPEED_SETTING not in capabilities[mode]
and TADO_FANLEVEL_SETTING not in capabilities[mode]
): ):
continue continue
@ -197,14 +201,15 @@ def create_climate_entity(
if supported_fan_modes: if supported_fan_modes:
continue continue
if capabilities[mode].get("fanSpeeds"): if TADO_FANSPEED_SETTING in capabilities[mode]:
supported_fan_modes = generate_supported_fanmodes( supported_fan_modes = generate_supported_fanmodes(
TADO_TO_HA_FAN_MODE_MAP_LEGACY, capabilities[mode]["fanSpeeds"] TADO_TO_HA_FAN_MODE_MAP_LEGACY,
capabilities[mode][TADO_FANSPEED_SETTING],
) )
else: else:
supported_fan_modes = generate_supported_fanmodes( supported_fan_modes = generate_supported_fanmodes(
TADO_TO_HA_FAN_MODE_MAP, capabilities[mode]["fanLevel"] TADO_TO_HA_FAN_MODE_MAP, capabilities[mode][TADO_FANLEVEL_SETTING]
) )
cool_temperatures = capabilities[CONST_MODE_COOL]["temperatures"] cool_temperatures = capabilities[CONST_MODE_COOL]["temperatures"]
@ -316,12 +321,16 @@ class TadoClimate(TadoZoneEntity, ClimateEntity):
self._target_temp: float | None = None self._target_temp: float | None = None
self._current_tado_fan_speed = CONST_FAN_OFF self._current_tado_fan_speed = CONST_FAN_OFF
self._current_tado_fan_level = CONST_FAN_OFF
self._current_tado_hvac_mode = CONST_MODE_OFF self._current_tado_hvac_mode = CONST_MODE_OFF
self._current_tado_hvac_action = HVACAction.OFF self._current_tado_hvac_action = HVACAction.OFF
self._current_tado_swing_mode = TADO_SWING_OFF self._current_tado_swing_mode = TADO_SWING_OFF
self._current_tado_vertical_swing = TADO_SWING_OFF self._current_tado_vertical_swing = TADO_SWING_OFF
self._current_tado_horizontal_swing = TADO_SWING_OFF self._current_tado_horizontal_swing = TADO_SWING_OFF
capabilities = tado.get_capabilities(zone_id)
self._current_tado_capabilities = capabilities
self._tado_zone_data: PyTado.TadoZone = {} self._tado_zone_data: PyTado.TadoZone = {}
self._tado_geofence_data: dict[str, str] | None = None self._tado_geofence_data: dict[str, str] | None = None
@ -382,20 +391,23 @@ class TadoClimate(TadoZoneEntity, ClimateEntity):
def fan_mode(self) -> str | None: def fan_mode(self) -> str | None:
"""Return the fan setting.""" """Return the fan setting."""
if self._ac_device: if self._ac_device:
return TADO_TO_HA_FAN_MODE_MAP.get( if self._is_valid_setting_for_hvac_mode(TADO_FANSPEED_SETTING):
self._current_tado_fan_speed, return TADO_TO_HA_FAN_MODE_MAP_LEGACY.get(
TADO_TO_HA_FAN_MODE_MAP_LEGACY.get(
self._current_tado_fan_speed, FAN_AUTO self._current_tado_fan_speed, FAN_AUTO
),
) )
if self._is_valid_setting_for_hvac_mode(TADO_FANLEVEL_SETTING):
return TADO_TO_HA_FAN_MODE_MAP.get(
self._current_tado_fan_level, FAN_AUTO
)
return FAN_AUTO
return None return None
def set_fan_mode(self, fan_mode: str) -> None: def set_fan_mode(self, fan_mode: str) -> None:
"""Turn fan on/off.""" """Turn fan on/off."""
if self._current_tado_fan_speed in TADO_FAN_LEVELS: if self._is_valid_setting_for_hvac_mode(TADO_FANSPEED_SETTING):
self._control_hvac(fan_mode=HA_TO_TADO_FAN_MODE_MAP[fan_mode])
else:
self._control_hvac(fan_mode=HA_TO_TADO_FAN_MODE_MAP_LEGACY[fan_mode]) self._control_hvac(fan_mode=HA_TO_TADO_FAN_MODE_MAP_LEGACY[fan_mode])
elif self._is_valid_setting_for_hvac_mode(TADO_FANLEVEL_SETTING):
self._control_hvac(fan_mode=HA_TO_TADO_FAN_MODE_MAP[fan_mode])
@property @property
def preset_mode(self) -> str: def preset_mode(self) -> str:
@ -555,24 +567,30 @@ class TadoClimate(TadoZoneEntity, ClimateEntity):
swing = None swing = None
if self._attr_swing_modes is None: if self._attr_swing_modes is None:
return return
if ( if swing_mode == SWING_OFF:
SWING_VERTICAL in self._attr_swing_modes if self._is_valid_setting_for_hvac_mode(TADO_SWING_SETTING):
or SWING_HORIZONTAL in self._attr_swing_modes swing = TADO_SWING_OFF
): if self._is_valid_setting_for_hvac_mode(TADO_HORIZONTAL_SWING_SETTING):
if swing_mode == SWING_VERTICAL:
vertical_swing = TADO_SWING_ON
elif swing_mode == SWING_HORIZONTAL:
horizontal_swing = TADO_SWING_ON
elif swing_mode == SWING_BOTH:
vertical_swing = TADO_SWING_ON
horizontal_swing = TADO_SWING_ON
elif swing_mode == SWING_OFF:
if SWING_VERTICAL in self._attr_swing_modes:
vertical_swing = TADO_SWING_OFF
if SWING_HORIZONTAL in self._attr_swing_modes:
horizontal_swing = TADO_SWING_OFF horizontal_swing = TADO_SWING_OFF
else: if self._is_valid_setting_for_hvac_mode(TADO_VERTICAL_SWING_SETTING):
swing = HA_TO_TADO_SWING_MODE_MAP[swing_mode] vertical_swing = TADO_SWING_OFF
if swing_mode == SWING_ON:
swing = TADO_SWING_ON
if swing_mode == SWING_VERTICAL:
if self._is_valid_setting_for_hvac_mode(TADO_VERTICAL_SWING_SETTING):
vertical_swing = TADO_SWING_ON
if self._is_valid_setting_for_hvac_mode(TADO_HORIZONTAL_SWING_SETTING):
horizontal_swing = TADO_SWING_OFF
if swing_mode == SWING_HORIZONTAL:
if self._is_valid_setting_for_hvac_mode(TADO_VERTICAL_SWING_SETTING):
vertical_swing = TADO_SWING_OFF
if self._is_valid_setting_for_hvac_mode(TADO_HORIZONTAL_SWING_SETTING):
horizontal_swing = TADO_SWING_ON
if swing_mode == SWING_BOTH:
if self._is_valid_setting_for_hvac_mode(TADO_VERTICAL_SWING_SETTING):
vertical_swing = TADO_SWING_ON
if self._is_valid_setting_for_hvac_mode(TADO_HORIZONTAL_SWING_SETTING):
horizontal_swing = TADO_SWING_ON
self._control_hvac( self._control_hvac(
swing_mode=swing, swing_mode=swing,
@ -596,18 +614,20 @@ class TadoClimate(TadoZoneEntity, ClimateEntity):
self._device_id self._device_id
][TEMP_OFFSET][offset_key] ][TEMP_OFFSET][offset_key]
self._current_tado_fan_speed = (
self._tado_zone_data.current_fan_level
if self._tado_zone_data.current_fan_level is not None
else self._tado_zone_data.current_fan_speed
)
self._current_tado_hvac_mode = self._tado_zone_data.current_hvac_mode self._current_tado_hvac_mode = self._tado_zone_data.current_hvac_mode
self._current_tado_hvac_action = self._tado_zone_data.current_hvac_action self._current_tado_hvac_action = self._tado_zone_data.current_hvac_action
if self._is_valid_setting_for_hvac_mode(TADO_FANLEVEL_SETTING):
self._current_tado_fan_level = self._tado_zone_data.current_fan_level
if self._is_valid_setting_for_hvac_mode(TADO_FANSPEED_SETTING):
self._current_tado_fan_speed = self._tado_zone_data.current_fan_speed
if self._is_valid_setting_for_hvac_mode(TADO_SWING_SETTING):
self._current_tado_swing_mode = self._tado_zone_data.current_swing_mode self._current_tado_swing_mode = self._tado_zone_data.current_swing_mode
if self._is_valid_setting_for_hvac_mode(TADO_VERTICAL_SWING_SETTING):
self._current_tado_vertical_swing = ( self._current_tado_vertical_swing = (
self._tado_zone_data.current_vertical_swing_mode self._tado_zone_data.current_vertical_swing_mode
) )
if self._is_valid_setting_for_hvac_mode(TADO_HORIZONTAL_SWING_SETTING):
self._current_tado_horizontal_swing = ( self._current_tado_horizontal_swing = (
self._tado_zone_data.current_horizontal_swing_mode self._tado_zone_data.current_horizontal_swing_mode
) )
@ -665,7 +685,10 @@ class TadoClimate(TadoZoneEntity, ClimateEntity):
self._target_temp = target_temp self._target_temp = target_temp
if fan_mode: if fan_mode:
if self._is_valid_setting_for_hvac_mode(TADO_FANSPEED_SETTING):
self._current_tado_fan_speed = fan_mode self._current_tado_fan_speed = fan_mode
if self._is_valid_setting_for_hvac_mode(TADO_FANLEVEL_SETTING):
self._current_tado_fan_level = fan_mode
if swing_mode: if swing_mode:
self._current_tado_swing_mode = swing_mode self._current_tado_swing_mode = swing_mode
@ -735,21 +758,32 @@ class TadoClimate(TadoZoneEntity, ClimateEntity):
fan_speed = None fan_speed = None
fan_level = None fan_level = None
if self.supported_features & ClimateEntityFeature.FAN_MODE: if self.supported_features & ClimateEntityFeature.FAN_MODE:
if self._current_tado_fan_speed in TADO_FAN_LEVELS: if self._is_current_setting_supported_by_current_hvac_mode(
fan_level = self._current_tado_fan_speed TADO_FANSPEED_SETTING, self._current_tado_fan_speed
elif self._current_tado_fan_speed in TADO_FAN_SPEEDS: ):
fan_speed = self._current_tado_fan_speed fan_speed = self._current_tado_fan_speed
if self._is_current_setting_supported_by_current_hvac_mode(
TADO_FANLEVEL_SETTING, self._current_tado_fan_level
):
fan_level = self._current_tado_fan_level
swing = None swing = None
vertical_swing = None vertical_swing = None
horizontal_swing = None horizontal_swing = None
if ( if (
self.supported_features & ClimateEntityFeature.SWING_MODE self.supported_features & ClimateEntityFeature.SWING_MODE
) and self._attr_swing_modes is not None: ) and self._attr_swing_modes is not None:
if SWING_VERTICAL in self._attr_swing_modes: if self._is_current_setting_supported_by_current_hvac_mode(
TADO_VERTICAL_SWING_SETTING, self._current_tado_vertical_swing
):
vertical_swing = self._current_tado_vertical_swing vertical_swing = self._current_tado_vertical_swing
if SWING_HORIZONTAL in self._attr_swing_modes: if self._is_current_setting_supported_by_current_hvac_mode(
TADO_HORIZONTAL_SWING_SETTING, self._current_tado_horizontal_swing
):
horizontal_swing = self._current_tado_horizontal_swing horizontal_swing = self._current_tado_horizontal_swing
if vertical_swing is None and horizontal_swing is None: if self._is_current_setting_supported_by_current_hvac_mode(
TADO_SWING_SETTING, self._current_tado_swing_mode
):
swing = self._current_tado_swing_mode swing = self._current_tado_swing_mode
self._tado.set_zone_overlay( self._tado.set_zone_overlay(
@ -765,3 +799,20 @@ class TadoClimate(TadoZoneEntity, ClimateEntity):
vertical_swing=vertical_swing, # api defaults to not sending verticalSwing if swing not None vertical_swing=vertical_swing, # api defaults to not sending verticalSwing if swing not None
horizontal_swing=horizontal_swing, # api defaults to not sending horizontalSwing if swing not None horizontal_swing=horizontal_swing, # api defaults to not sending horizontalSwing if swing not None
) )
def _is_valid_setting_for_hvac_mode(self, setting: str) -> bool:
return (
self._current_tado_capabilities.get(self._current_tado_hvac_mode, {}).get(
setting
)
is not None
)
def _is_current_setting_supported_by_current_hvac_mode(
self, setting: str, current_state: str | None
) -> bool:
if self._is_valid_setting_for_hvac_mode(setting):
return current_state in self._current_tado_capabilities[
self._current_tado_hvac_mode
].get(setting, [])
return False

View File

@ -234,3 +234,10 @@ CONF_READING = "reading"
ATTR_MESSAGE = "message" ATTR_MESSAGE = "message"
WATER_HEATER_FALLBACK_REPAIR = "water_heater_fallback" WATER_HEATER_FALLBACK_REPAIR = "water_heater_fallback"
TADO_SWING_SETTING = "swings"
TADO_FANSPEED_SETTING = "fanSpeeds"
TADO_FANLEVEL_SETTING = "fanLevel"
TADO_VERTICAL_SWING_SETTING = "verticalSwing"
TADO_HORIZONTAL_SWING_SETTING = "horizontalSwing"