Home Assistant Unofficial Reference 2024.12.1
humidifier.py
Go to the documentation of this file.
1 """Support for Tuya (de)humidifiers."""
2 
3 from __future__ import annotations
4 
5 from dataclasses import dataclass
6 
7 from tuya_sharing import CustomerDevice, Manager
8 
10  HumidifierDeviceClass,
11  HumidifierEntity,
12  HumidifierEntityDescription,
13  HumidifierEntityFeature,
14 )
15 from homeassistant.core import HomeAssistant, callback
16 from homeassistant.helpers.dispatcher import async_dispatcher_connect
17 from homeassistant.helpers.entity_platform import AddEntitiesCallback
18 
19 from . import TuyaConfigEntry
20 from .const import TUYA_DISCOVERY_NEW, DPCode, DPType
21 from .entity import IntegerTypeData, TuyaEntity
22 
23 
24 @dataclass(frozen=True)
26  """Describe an Tuya (de)humidifier entity."""
27 
28  # DPCode, to use. If None, the key will be used as DPCode
29  dpcode: DPCode | tuple[DPCode, ...] | None = None
30 
31  current_humidity: DPCode | None = None
32  humidity: DPCode | None = None
33 
34 
35 HUMIDIFIERS: dict[str, TuyaHumidifierEntityDescription] = {
36  # Dehumidifier
37  # https://developer.tuya.com/en/docs/iot/categorycs?id=Kaiuz1vcz4dha
39  key=DPCode.SWITCH,
40  dpcode=(DPCode.SWITCH, DPCode.SWITCH_SPRAY),
41  current_humidity=DPCode.HUMIDITY_INDOOR,
42  humidity=DPCode.DEHUMIDITY_SET_VALUE,
43  device_class=HumidifierDeviceClass.DEHUMIDIFIER,
44  ),
45  # Humidifier
46  # https://developer.tuya.com/en/docs/iot/categoryjsq?id=Kaiuz1smr440b
48  key=DPCode.SWITCH,
49  dpcode=(DPCode.SWITCH, DPCode.SWITCH_SPRAY),
50  current_humidity=DPCode.HUMIDITY_CURRENT,
51  humidity=DPCode.HUMIDITY_SET,
52  device_class=HumidifierDeviceClass.HUMIDIFIER,
53  ),
54 }
55 
56 
58  hass: HomeAssistant, entry: TuyaConfigEntry, async_add_entities: AddEntitiesCallback
59 ) -> None:
60  """Set up Tuya (de)humidifier dynamically through Tuya discovery."""
61  hass_data = entry.runtime_data
62 
63  @callback
64  def async_discover_device(device_ids: list[str]) -> None:
65  """Discover and add a discovered Tuya (de)humidifier."""
66  entities: list[TuyaHumidifierEntity] = []
67  for device_id in device_ids:
68  device = hass_data.manager.device_map[device_id]
69  if description := HUMIDIFIERS.get(device.category):
70  entities.append(
71  TuyaHumidifierEntity(device, hass_data.manager, description)
72  )
73  async_add_entities(entities)
74 
75  async_discover_device([*hass_data.manager.device_map])
76 
77  entry.async_on_unload(
78  async_dispatcher_connect(hass, TUYA_DISCOVERY_NEW, async_discover_device)
79  )
80 
81 
83  """Tuya (de)humidifier Device."""
84 
85  _current_humidity: IntegerTypeData | None = None
86  _set_humidity: IntegerTypeData | None = None
87  _switch_dpcode: DPCode | None = None
88  entity_description: TuyaHumidifierEntityDescription
89  _attr_name = None
90 
91  def __init__(
92  self,
93  device: CustomerDevice,
94  device_manager: Manager,
95  description: TuyaHumidifierEntityDescription,
96  ) -> None:
97  """Init Tuya (de)humidifier."""
98  super().__init__(device, device_manager)
99  self.entity_descriptionentity_description = description
100  self._attr_unique_id_attr_unique_id_attr_unique_id = f"{super().unique_id}{description.key}"
101 
102  # Determine main switch DPCode
103  self._switch_dpcode_switch_dpcode = self.find_dpcodefind_dpcodefind_dpcodefind_dpcodefind_dpcode(
104  description.dpcode or DPCode(description.key), prefer_function=True
105  )
106 
107  # Determine humidity parameters
108  if int_type := self.find_dpcodefind_dpcodefind_dpcodefind_dpcodefind_dpcode(
109  description.humidity, dptype=DPType.INTEGER, prefer_function=True
110  ):
111  self._set_humidity_set_humidity = int_type
112  self._attr_min_humidity_attr_min_humidity = int(int_type.min_scaled)
113  self._attr_max_humidity_attr_max_humidity = int(int_type.max_scaled)
114 
115  # Determine current humidity DPCode
116  if int_type := self.find_dpcodefind_dpcodefind_dpcodefind_dpcodefind_dpcode(
117  description.current_humidity,
118  dptype=DPType.INTEGER,
119  ):
120  self._current_humidity_current_humidity = int_type
121 
122  # Determine mode support and provided modes
123  if enum_type := self.find_dpcodefind_dpcodefind_dpcodefind_dpcodefind_dpcode(
124  DPCode.MODE, dptype=DPType.ENUM, prefer_function=True
125  ):
126  self._attr_supported_features |= HumidifierEntityFeature.MODES
127  self._attr_available_modes_attr_available_modes = enum_type.range
128 
129  @property
130  def is_on(self) -> bool:
131  """Return the device is on or off."""
132  if self._switch_dpcode_switch_dpcode is None:
133  return False
134  return self.devicedevice.status.get(self._switch_dpcode_switch_dpcode, False)
135 
136  @property
137  def mode(self) -> str | None:
138  """Return the current mode."""
139  return self.devicedevice.status.get(DPCode.MODE)
140 
141  @property
142  def target_humidity(self) -> int | None:
143  """Return the humidity we try to reach."""
144  if self._set_humidity_set_humidity is None:
145  return None
146 
147  humidity = self.devicedevice.status.get(self._set_humidity_set_humidity.dpcode)
148  if humidity is None:
149  return None
150 
151  return round(self._set_humidity_set_humidity.scale_value(humidity))
152 
153  @property
154  def current_humidity(self) -> int | None:
155  """Return the current humidity."""
156  if self._current_humidity_current_humidity is None:
157  return None
158 
159  if (
160  current_humidity := self.devicedevice.status.get(self._current_humidity_current_humidity.dpcode)
161  ) is None:
162  return None
163 
164  return round(self._current_humidity_current_humidity.scale_value(current_humidity))
165 
166  def turn_on(self, **kwargs):
167  """Turn the device on."""
168  self._send_command_send_command([{"code": self._switch_dpcode_switch_dpcode, "value": True}])
169 
170  def turn_off(self, **kwargs):
171  """Turn the device off."""
172  self._send_command_send_command([{"code": self._switch_dpcode_switch_dpcode, "value": False}])
173 
174  def set_humidity(self, humidity: int) -> None:
175  """Set new target humidity."""
176  if self._set_humidity_set_humidity is None:
177  raise RuntimeError(
178  "Cannot set humidity, device doesn't provide methods to set it"
179  )
180 
181  self._send_command_send_command(
182  [
183  {
184  "code": self._set_humidity_set_humidity.dpcode,
185  "value": self._set_humidity_set_humidity.scale_value_back(humidity),
186  }
187  ]
188  )
189 
190  def set_mode(self, mode):
191  """Set new target preset mode."""
192  self._send_command_send_command([{"code": DPCode.MODE, "value": mode}])
None _send_command(self, list[dict[str, Any]] commands)
Definition: entity.py:295
DPCode|EnumTypeData|IntegerTypeData|None find_dpcode(self, str|DPCode|tuple[DPCode,...]|None dpcodes, *bool prefer_function=False, DPType|None dptype=None)
Definition: entity.py:206
IntegerTypeData|None find_dpcode(self, str|DPCode|tuple[DPCode,...]|None dpcodes, *bool prefer_function=False, Literal[DPType.INTEGER] dptype)
Definition: entity.py:190
DPCode|None find_dpcode(self, str|DPCode|tuple[DPCode,...]|None dpcodes, *bool prefer_function=False)
Definition: entity.py:198
EnumTypeData|None find_dpcode(self, str|DPCode|tuple[DPCode,...]|None dpcodes, *bool prefer_function=False, Literal[DPType.ENUM] dptype)
Definition: entity.py:181
None __init__(self, CustomerDevice device, Manager device_manager, TuyaHumidifierEntityDescription description)
Definition: humidifier.py:96
ElkSystem|None async_discover_device(HomeAssistant hass, str host)
Definition: discovery.py:78
None async_setup_entry(HomeAssistant hass, TuyaConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: humidifier.py:59
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)
Definition: dispatcher.py:103