Skip to content

Commit 13e4bb4

Browse files
authored
Migrate Tuya climate (hvac_mode/presets) to use wrapper class (home-assistant#156933)
1 parent d5fd27d commit 13e4bb4

File tree

3 files changed

+54
-39
lines changed

3 files changed

+54
-39
lines changed

homeassistant/components/tuya/climate.py

Lines changed: 49 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from .const import TUYA_DISCOVERY_NEW, DeviceCategory, DPCode, DPType
2828
from .entity import TuyaEntity
2929
from .models import (
30+
DPCodeBooleanWrapper,
3031
DPCodeEnumWrapper,
3132
DPCodeIntegerWrapper,
3233
IntegerTypeData,
@@ -120,6 +121,12 @@ def async_discover_device(device_ids: list[str]) -> None:
120121
(DPCode.FAN_SPEED_ENUM, DPCode.LEVEL, DPCode.WINDSPEED),
121122
prefer_function=True,
122123
),
124+
hvac_mode_wrapper=DPCodeEnumWrapper.find_dpcode(
125+
device, DPCode.MODE, prefer_function=True
126+
),
127+
switch_wrapper=DPCodeBooleanWrapper.find_dpcode(
128+
device, DPCode.SWITCH, prefer_function=True
129+
),
123130
target_humidity_wrapper=_RoundedIntegerWrapper.find_dpcode(
124131
device, DPCode.HUMIDITY_SET, prefer_function=True
125132
),
@@ -150,9 +157,11 @@ def __init__(
150157
description: TuyaClimateEntityDescription,
151158
system_temperature_unit: UnitOfTemperature,
152159
*,
153-
current_humidity_wrapper: _RoundedIntegerWrapper | None = None,
154-
fan_mode_wrapper: DPCodeEnumWrapper | None = None,
155-
target_humidity_wrapper: _RoundedIntegerWrapper | None = None,
160+
current_humidity_wrapper: _RoundedIntegerWrapper | None,
161+
fan_mode_wrapper: DPCodeEnumWrapper | None,
162+
hvac_mode_wrapper: DPCodeEnumWrapper | None,
163+
switch_wrapper: DPCodeBooleanWrapper | None,
164+
target_humidity_wrapper: _RoundedIntegerWrapper | None,
156165
) -> None:
157166
"""Determine which values to use."""
158167
self._attr_target_temperature_step = 1.0
@@ -161,6 +170,8 @@ def __init__(
161170
super().__init__(device, device_manager)
162171
self._current_humidity_wrapper = current_humidity_wrapper
163172
self._fan_mode_wrapper = fan_mode_wrapper
173+
self._hvac_mode_wrapper = hvac_mode_wrapper
174+
self._switch_wrapper = switch_wrapper
164175
self._target_humidity_wrapper = target_humidity_wrapper
165176

166177
# If both temperature values for celsius and fahrenheit are present,
@@ -234,12 +245,10 @@ def __init__(
234245
# Determine HVAC modes
235246
self._attr_hvac_modes: list[HVACMode] = []
236247
self._hvac_to_tuya = {}
237-
if enum_type := find_dpcode(
238-
self.device, DPCode.MODE, dptype=DPType.ENUM, prefer_function=True
239-
):
248+
if hvac_mode_wrapper:
240249
self._attr_hvac_modes = [HVACMode.OFF]
241250
unknown_hvac_modes: list[str] = []
242-
for tuya_mode in enum_type.range:
251+
for tuya_mode in hvac_mode_wrapper.type_information.range:
243252
if tuya_mode in TUYA_HVAC_TO_HA:
244253
ha_mode = TUYA_HVAC_TO_HA[tuya_mode]
245254
self._hvac_to_tuya[ha_mode] = tuya_mode
@@ -251,7 +260,7 @@ def __init__(
251260
self._attr_hvac_modes.append(description.switch_only_hvac_mode)
252261
self._attr_preset_modes = unknown_hvac_modes
253262
self._attr_supported_features |= ClimateEntityFeature.PRESET_MODE
254-
elif get_dpcode(self.device, DPCode.SWITCH):
263+
elif switch_wrapper:
255264
self._attr_hvac_modes = [
256265
HVACMode.OFF,
257266
description.switch_only_hvac_mode,
@@ -293,24 +302,31 @@ def __init__(
293302
if get_dpcode(self.device, DPCode.SWITCH_VERTICAL):
294303
self._attr_swing_modes.append(SWING_VERTICAL)
295304

296-
if DPCode.SWITCH in self.device.function:
305+
if switch_wrapper:
297306
self._attr_supported_features |= (
298307
ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
299308
)
300309

301-
def set_hvac_mode(self, hvac_mode: HVACMode) -> None:
310+
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
302311
"""Set new target hvac mode."""
303-
commands = [{"code": DPCode.SWITCH, "value": hvac_mode != HVACMode.OFF}]
304-
if hvac_mode in self._hvac_to_tuya:
312+
commands = []
313+
if self._switch_wrapper:
305314
commands.append(
306-
{"code": DPCode.MODE, "value": self._hvac_to_tuya[hvac_mode]}
315+
self._switch_wrapper.get_update_command(
316+
self.device, hvac_mode != HVACMode.OFF
317+
)
307318
)
308-
self._send_command(commands)
319+
if self._hvac_mode_wrapper and hvac_mode in self._hvac_to_tuya:
320+
commands.append(
321+
self._hvac_mode_wrapper.get_update_command(
322+
self.device, self._hvac_to_tuya[hvac_mode]
323+
)
324+
)
325+
await self._async_send_commands(commands)
309326

310-
def set_preset_mode(self, preset_mode: str) -> None:
327+
async def async_set_preset_mode(self, preset_mode: str) -> None:
311328
"""Set new target preset mode."""
312-
commands = [{"code": DPCode.MODE, "value": preset_mode}]
313-
self._send_command(commands)
329+
await self._async_send_dpcode_update(self._hvac_mode_wrapper, preset_mode)
314330

315331
async def async_set_fan_mode(self, fan_mode: str) -> None:
316332
"""Set new target fan mode."""
@@ -406,34 +422,29 @@ def target_humidity(self) -> int | None:
406422
@property
407423
def hvac_mode(self) -> HVACMode:
408424
"""Return hvac mode."""
409-
# If the switch off, hvac mode is off as well. Unless the switch
410-
# the switch is on or doesn't exists of course...
411-
if not self.device.status.get(DPCode.SWITCH, True):
412-
return HVACMode.OFF
413-
414-
if DPCode.MODE not in self.device.function:
415-
if self.device.status.get(DPCode.SWITCH, False):
416-
return self.entity_description.switch_only_hvac_mode
425+
# If the switch is off, hvac mode is off as well.
426+
# Unless the switch doesn't exists of course...
427+
if (switch_status := self._read_wrapper(self._switch_wrapper)) is False:
417428
return HVACMode.OFF
418429

419-
if (
420-
mode := self.device.status.get(DPCode.MODE)
421-
) is not None and mode in TUYA_HVAC_TO_HA:
422-
return TUYA_HVAC_TO_HA[mode]
430+
# If the mode is known and maps to an HVAC mode, return it.
431+
if (mode := self._read_wrapper(self._hvac_mode_wrapper)) and (
432+
hvac_mode := TUYA_HVAC_TO_HA.get(mode)
433+
):
434+
return hvac_mode
423435

424-
# If the switch is on, and the mode does not match any hvac mode.
425-
if self.device.status.get(DPCode.SWITCH, False):
436+
# If hvac_mode is unknown, return the switch only mode.
437+
if switch_status:
426438
return self.entity_description.switch_only_hvac_mode
427-
428439
return HVACMode.OFF
429440

430441
@property
431442
def preset_mode(self) -> str | None:
432443
"""Return preset mode."""
433-
if DPCode.MODE not in self.device.function:
444+
if self._hvac_mode_wrapper is None:
434445
return None
435446

436-
mode = self.device.status.get(DPCode.MODE)
447+
mode = self._read_wrapper(self._hvac_mode_wrapper)
437448
if mode in TUYA_HVAC_TO_HA:
438449
return None
439450

@@ -463,10 +474,10 @@ def swing_mode(self) -> str:
463474

464475
return SWING_OFF
465476

466-
def turn_on(self) -> None:
477+
async def async_turn_on(self) -> None:
467478
"""Turn the device on, retaining current HVAC (if supported)."""
468-
self._send_command([{"code": DPCode.SWITCH, "value": True}])
479+
await self._async_send_dpcode_update(self._switch_wrapper, True)
469480

470-
def turn_off(self) -> None:
481+
async def async_turn_off(self) -> None:
471482
"""Turn the device on, retaining current HVAC (if supported)."""
472-
self._send_command([{"code": DPCode.SWITCH, "value": False}])
483+
await self._async_send_dpcode_update(self._switch_wrapper, False)

homeassistant/components/tuya/entity.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ def _send_command(self, commands: list[dict[str, Any]]) -> None:
6666
LOGGER.debug("Sending commands for device %s: %s", self.device.id, commands)
6767
self.device_manager.send_commands(self.device.id, commands)
6868

69+
async def _async_send_commands(self, commands: list[dict[str, Any]]) -> None:
70+
"""Send a list of commands to the device."""
71+
await self.hass.async_add_executor_job(self._send_command, commands)
72+
6973
def _read_wrapper(self, dpcode_wrapper: DPCodeWrapper | None) -> Any | None:
7074
"""Read the wrapper device status."""
7175
if dpcode_wrapper is None:

tests/components/tuya/snapshots/test_climate.ambr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -922,7 +922,7 @@
922922
]),
923923
'max_temp': 40.0,
924924
'min_temp': 5.0,
925-
'preset_mode': 'hold',
925+
'preset_mode': None,
926926
'preset_modes': list([
927927
'holiday',
928928
]),

0 commit comments

Comments
 (0)