From 1111d11b055e6067fe6c4713f83c7357d37962f3 Mon Sep 17 00:00:00 2001 From: Maxim Mikityanskiy Date: Sun, 11 Aug 2024 18:17:59 +0300 Subject: [PATCH 1/4] Use _attr_fan_mode in send_ir() Replace self.fan_mode by self._attr_fan_mode, because the latter is used everywhere else. --- custom_components/tasmota_irhvac/climate.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/custom_components/tasmota_irhvac/climate.py b/custom_components/tasmota_irhvac/climate.py index e404f2f..4148bdf 100644 --- a/custom_components/tasmota_irhvac/climate.py +++ b/custom_components/tasmota_irhvac/climate.py @@ -1193,14 +1193,14 @@ async def set_mode(self, hvac_mode): async def send_ir(self): """Send the payload to tasmota mqtt topic.""" - fan_speed = self.fan_mode + fan_speed = self._attr_fan_mode # tweak for some ELECTRA_AC devices if HVAC_FAN_MAX_HIGH in (self._attr_fan_modes or []) and HVAC_FAN_AUTO_MAX in ( self._attr_fan_modes or [] ): - if self.fan_mode == FAN_HIGH: + if fan_speed == FAN_HIGH: fan_speed = HVAC_FAN_MAX - if self.fan_mode == HVAC_FAN_MAX: + elif fan_speed == HVAC_FAN_MAX: fan_speed = HVAC_FAN_AUTO # Set the swing mode - default off From 8e4a1b7eae5406c6be864d44d00c1e5e7dc7e7df Mon Sep 17 00:00:00 2001 From: Maxim Mikityanskiy Date: Sun, 11 Aug 2024 18:28:41 +0300 Subject: [PATCH 2/4] Remove redundant checks in async_set_fan_mode() self._attr_fan_modes already contains values rewritten to FAN_HIGH and HVAC_FAN_MAX. `if fan_mode not in self._attr_fan_modes`, it's impossible to fail the `fan_mode != FAN_HIGH and fan_mode != HVAC_FAN_MAX` check. Also fix a typo: "swing mode" -> "fan mode". --- custom_components/tasmota_irhvac/climate.py | 24 ++++++--------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/custom_components/tasmota_irhvac/climate.py b/custom_components/tasmota_irhvac/climate.py index 4148bdf..9ffb3cd 100644 --- a/custom_components/tasmota_irhvac/climate.py +++ b/custom_components/tasmota_irhvac/climate.py @@ -933,24 +933,12 @@ async def async_set_temperature(self, **kwargs): async def async_set_fan_mode(self, fan_mode): """Set new target fan mode.""" if fan_mode not in (self._attr_fan_modes or []): - # tweak for some ELECTRA_AC devices - if HVAC_FAN_MAX_HIGH in ( - self._attr_fan_modes or [] - ) and HVAC_FAN_AUTO_MAX in (self._attr_fan_modes or []): - if fan_mode != FAN_HIGH and fan_mode != HVAC_FAN_MAX: - _LOGGER.error( - "Invalid swing mode selected. Got '%s'. Allowed modes are:", - fan_mode, - ) - _LOGGER.error(self._attr_fan_modes) - return - else: - _LOGGER.error( - "Invalid swing mode selected. Got '%s'. Allowed modes are:", - fan_mode, - ) - _LOGGER.error(self._attr_fan_modes) - return + _LOGGER.error( + "Invalid fan mode selected. Got '%s'. Allowed modes are:", + fan_mode, + ) + _LOGGER.error(self._attr_fan_modes) + return self._attr_fan_mode = fan_mode if not self._attr_hvac_mode == HVACMode.OFF: self.power_mode = STATE_ON From b772248cd5998740badc8e5adad7cbd6deb862d6 Mon Sep 17 00:00:00 2001 From: Maxim Mikityanskiy Date: Sun, 11 Aug 2024 18:34:35 +0300 Subject: [PATCH 3/4] Fix the ELECTRA_AC workaround HVAC_FAN_MAX_HIGH and HVAC_FAN_AUTO_MAX are filtered out from self._attr_fan_modes in __init__(). All the further checks for HVAC_FAN_MAX_HIGH and HVAC_FAN_AUTO_MAX will always fail, making the ELECTRA_AC workaround a no-op. Fix it by storing the workaround status in self._quirk_fan_max_high. Fixes: de5a190852e4 ("Aggressive use of _attr_* properties, cleanup and normalization") --- custom_components/tasmota_irhvac/climate.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/custom_components/tasmota_irhvac/climate.py b/custom_components/tasmota_irhvac/climate.py index 9ffb3cd..c338a33 100644 --- a/custom_components/tasmota_irhvac/climate.py +++ b/custom_components/tasmota_irhvac/climate.py @@ -546,11 +546,12 @@ def __init__( self._attr_target_temperature_step = config[CONF_TEMP_STEP] self._attr_hvac_modes = config[CONF_MODES_LIST] self._attr_fan_modes = config.get(CONF_FAN_LIST) - if ( - isinstance(self._attr_fan_modes, list) - and HVAC_FAN_MAX_HIGH in self._attr_fan_modes - and HVAC_FAN_AUTO_MAX in self._attr_fan_modes - ): + self._quirk_fan_max_high = all([ + isinstance(self._attr_fan_modes, list), + HVAC_FAN_MAX_HIGH in self._attr_fan_modes, + HVAC_FAN_AUTO_MAX in self._attr_fan_modes, + ]) + if self._quirk_fan_max_high: new_fan_list = [] for val in self._attr_fan_modes: if val == HVAC_FAN_MAX_HIGH: @@ -792,9 +793,7 @@ async def state_message_received(message: mqtt.ReceiveMessage) -> None: if "FanSpeed" in payload: fan_mode = payload["FanSpeed"].lower() # ELECTRA_AC fan modes fix - if HVAC_FAN_MAX_HIGH in ( - self._attr_fan_modes or [] - ) and HVAC_FAN_AUTO_MAX in (self._attr_fan_modes or []): + if self._quirk_fan_max_high: if fan_mode == HVAC_FAN_MAX: self._attr_fan_mode = FAN_HIGH elif fan_mode == HVAC_FAN_AUTO: @@ -1183,9 +1182,7 @@ async def send_ir(self): """Send the payload to tasmota mqtt topic.""" fan_speed = self._attr_fan_mode # tweak for some ELECTRA_AC devices - if HVAC_FAN_MAX_HIGH in (self._attr_fan_modes or []) and HVAC_FAN_AUTO_MAX in ( - self._attr_fan_modes or [] - ): + if self._quirk_fan_max_high: if fan_speed == FAN_HIGH: fan_speed = HVAC_FAN_MAX elif fan_speed == HVAC_FAN_MAX: From 9e3ad3449f1dc8b59303a5a87441e6a7dfcfc4a5 Mon Sep 17 00:00:00 2001 From: Maxim Mikityanskiy Date: Sun, 11 Aug 2024 18:54:36 +0300 Subject: [PATCH 4/4] Prettify fan modes for ACs with min/max and without high/low Home Assistant has standard high/low fan modes that are localized and displayed in the UI in a pretty way. Tasmota IRHVAC has min, low, middle, high and max, along with a few other modes. Min and max are displayed in Home Assistant as is and not localized. While nothing can be done on the integration side for air conditioners that use 5 levels of the fan, some air conditioners have 3 levels, but use min/medium/max instead of low/medium/high. For such ACs, convert min->low and max->high to display pretty localized values in the UI. --- custom_components/tasmota_irhvac/climate.py | 27 ++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/custom_components/tasmota_irhvac/climate.py b/custom_components/tasmota_irhvac/climate.py index c338a33..b11d7a3 100644 --- a/custom_components/tasmota_irhvac/climate.py +++ b/custom_components/tasmota_irhvac/climate.py @@ -561,6 +561,9 @@ def __init__( else: new_fan_list.append(val) self._attr_fan_modes = new_fan_list if len(new_fan_list) else None + self._quirk_fan_prettify = all(mode not in (self._attr_fan_modes or []) for mode in [FAN_LOW, FAN_HIGH]) + if self._quirk_fan_prettify and self._attr_fan_modes: + self._attr_fan_modes = [self.fan_prettify(mode) for mode in self._attr_fan_modes] self._attr_fan_mode = ( self._attr_fan_modes[0] if isinstance(self._attr_fan_modes, list) and len(self._attr_fan_modes) @@ -586,6 +589,24 @@ def __init__( if self._attr_swing_mode is not None: self._support_flags = self._support_flags | ClimateEntityFeature.SWING_MODE + def fan_prettify(self, mode): + if not self._quirk_fan_prettify: + return mode + if mode == HVAC_FAN_MIN: + return FAN_LOW + if mode == HVAC_FAN_MAX: + return FAN_HIGH + return mode + + def fan_unprettify(self, mode): + if not self._quirk_fan_prettify: + return mode + if mode == FAN_LOW: + return HVAC_FAN_MIN + if mode == FAN_HIGH: + return HVAC_FAN_MAX + return mode + async def async_added_to_hass(self): # Replacing `async_track_state_change` with `async_track_state_change_event` # See, https://developers.home-assistant.io/blog/2024/04/13/deprecate_async_track_state_change/ @@ -799,9 +820,9 @@ async def state_message_received(message: mqtt.ReceiveMessage) -> None: elif fan_mode == HVAC_FAN_AUTO: self._attr_fan_mode = HVAC_FAN_MAX else: - self._attr_fan_mode = fan_mode + self._attr_fan_mode = self.fan_prettify(fan_mode) else: - self._attr_fan_mode = fan_mode + self._attr_fan_mode = self.fan_prettify(fan_mode) _LOGGER.debug(self._attr_fan_mode) if self._attr_hvac_mode is not HVACMode.OFF: @@ -1180,7 +1201,7 @@ async def set_mode(self, hvac_mode): async def send_ir(self): """Send the payload to tasmota mqtt topic.""" - fan_speed = self._attr_fan_mode + fan_speed = self.fan_unprettify(self._attr_fan_mode) # tweak for some ELECTRA_AC devices if self._quirk_fan_max_high: if fan_speed == FAN_HIGH: