diff --git a/homeassistant/components/tuya/climate.py b/homeassistant/components/tuya/climate.py index 83b37b35ba3..ddfd874c9cb 100644 --- a/homeassistant/components/tuya/climate.py +++ b/homeassistant/components/tuya/climate.py @@ -2,6 +2,7 @@ from __future__ import annotations +import collections from dataclasses import dataclass from typing import Any, Self @@ -140,6 +141,22 @@ class _SwingModeWrapper(DeviceWrapper): return commands +def _filter_hvac_mode_mappings(tuya_range: list[str]) -> dict[str, HVACMode | None]: + """Filter TUYA_HVAC_TO_HA modes that are not in the range. + + If multiple Tuya modes map to the same HA mode, set the mapping to None to avoid + ambiguity when converting back from HA to Tuya modes. + """ + modes_in_range = { + tuya_mode: TUYA_HVAC_TO_HA.get(tuya_mode) for tuya_mode in tuya_range + } + modes_occurrences = collections.Counter(modes_in_range.values()) + for key, value in modes_in_range.items(): + if value is not None and modes_occurrences[value] > 1: + modes_in_range[key] = None + return modes_in_range + + class _HvacModeWrapper(DPCodeEnumWrapper): """Wrapper for managing climate HVACMode.""" @@ -148,10 +165,9 @@ class _HvacModeWrapper(DPCodeEnumWrapper): def __init__(self, dpcode: str, type_information: EnumTypeInformation) -> None: """Init _HvacModeWrapper.""" super().__init__(dpcode, type_information) + self._mappings = _filter_hvac_mode_mappings(type_information.range) self.options = [ - TUYA_HVAC_TO_HA[tuya_mode] - for tuya_mode in type_information.range - if tuya_mode in TUYA_HVAC_TO_HA + ha_mode for ha_mode in self._mappings.values() if ha_mode is not None ] def read_device_status(self, device: CustomerDevice) -> HVACMode | None: @@ -166,7 +182,7 @@ class _HvacModeWrapper(DPCodeEnumWrapper): """Convert value to raw value.""" return next( tuya_mode - for tuya_mode, ha_mode in TUYA_HVAC_TO_HA.items() + for tuya_mode, ha_mode in self._mappings.items() if ha_mode == value ) @@ -179,10 +195,9 @@ class _PresetWrapper(DPCodeEnumWrapper): def __init__(self, dpcode: str, type_information: EnumTypeInformation) -> None: """Init _PresetWrapper.""" super().__init__(dpcode, type_information) + mappings = _filter_hvac_mode_mappings(type_information.range) self.options = [ - tuya_mode - for tuya_mode in type_information.range - if tuya_mode not in TUYA_HVAC_TO_HA + tuya_mode for tuya_mode, ha_mode in mappings.items() if ha_mode is None ] def read_device_status(self, device: CustomerDevice) -> str | None: diff --git a/tests/components/tuya/snapshots/test_climate.ambr b/tests/components/tuya/snapshots/test_climate.ambr index 2cc5450c7ea..209acb5d48b 100644 --- a/tests/components/tuya/snapshots/test_climate.ambr +++ b/tests/components/tuya/snapshots/test_climate.ambr @@ -83,13 +83,13 @@ 'capabilities': dict({ 'hvac_modes': list([ , - , - , , ]), 'max_temp': 35.0, 'min_temp': 5.0, 'preset_modes': list([ + 'auto', + 'manual', 'off', ]), 'target_temp_step': 1.0, @@ -130,13 +130,13 @@ 'friendly_name': 'Anbau', 'hvac_modes': list([ , - , - , , ]), 'max_temp': 35.0, 'min_temp': 5.0, 'preset_modes': list([ + 'auto', + 'manual', 'off', ]), 'supported_features': , @@ -159,13 +159,13 @@ 'hvac_modes': list([ , , - , - , ]), 'max_temp': 70.0, 'min_temp': 1.0, 'preset_modes': list([ 'holiday', + 'auto', + 'manual', 'eco', ]), 'target_temp_step': 0.5, @@ -208,14 +208,14 @@ 'hvac_modes': list([ , , - , - , ]), 'max_temp': 70.0, 'min_temp': 1.0, 'preset_mode': None, 'preset_modes': list([ 'holiday', + 'auto', + 'manual', 'eco', ]), 'supported_features': , @@ -453,13 +453,13 @@ 'capabilities': dict({ 'hvac_modes': list([ , - , - , , ]), 'max_temp': 35.0, 'min_temp': 5.0, 'preset_modes': list([ + 'auto', + 'manual', 'off', ]), 'target_temp_step': 1.0, @@ -501,14 +501,14 @@ 'friendly_name': 'Empore', 'hvac_modes': list([ , - , - , , ]), 'max_temp': 35.0, 'min_temp': 5.0, 'preset_mode': None, 'preset_modes': list([ + 'auto', + 'manual', 'off', ]), 'supported_features': , @@ -532,12 +532,12 @@ 'hvac_modes': list([ , , - , - , ]), 'max_temp': 35.0, 'min_temp': 5.0, 'preset_modes': list([ + 'auto', + 'manual', 'off', ]), 'target_temp_step': 1.0, @@ -580,13 +580,13 @@ 'hvac_modes': list([ , , - , - , ]), 'max_temp': 35.0, 'min_temp': 5.0, 'preset_mode': None, 'preset_modes': list([ + 'auto', + 'manual', 'off', ]), 'supported_features': , @@ -1107,12 +1107,12 @@ 'hvac_modes': list([ , , - , - , ]), 'max_temp': 5.9, 'min_temp': 0.1, 'preset_modes': list([ + 'auto', + 'manual', 'holiday', ]), 'target_temp_step': 0.5, @@ -1155,13 +1155,13 @@ 'hvac_modes': list([ , , - , - , ]), 'max_temp': 5.9, 'min_temp': 0.1, 'preset_mode': None, 'preset_modes': list([ + 'auto', + 'manual', 'holiday', ]), 'supported_features': , @@ -1185,10 +1185,13 @@ 'hvac_modes': list([ , , - , ]), 'max_temp': 90.0, 'min_temp': 5.0, + 'preset_modes': list([ + 'auto', + 'manual', + ]), 'target_temp_step': 1.0, }), 'config_entry_id': , @@ -1215,7 +1218,7 @@ 'platform': 'tuya', 'previous_unique_id': None, 'suggested_object_id': None, - 'supported_features': , + 'supported_features': , 'translation_key': None, 'unique_id': 'tuya.sb3zdertrw50bgogkw', 'unit_of_measurement': None, @@ -1229,11 +1232,15 @@ 'hvac_modes': list([ , , - , ]), 'max_temp': 90.0, 'min_temp': 5.0, - 'supported_features': , + 'preset_mode': None, + 'preset_modes': list([ + 'auto', + 'manual', + ]), + 'supported_features': , 'target_temp_step': 1.0, 'temperature': 12.0, }),