Repository navigation
Expand file tree
/
Copy pathhumidifier.py
More file actions
175 lines (139 loc) · 5.77 KB
/
Copy pathhumidifier.py
File metadata and controls
175 lines (139 loc) · 5.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
"""Support for Tuya (de)humidifiers."""
from __future__ import annotations
from dataclasses import dataclass
from tuya_iot import TuyaDevice, TuyaDeviceManager
from homeassistant.components.humidifier import (
HumidifierDeviceClass,
HumidifierEntity,
HumidifierEntityDescription,
HumidifierEntityFeature,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import HomeAssistantTuyaData
from .base import IntegerTypeData, TuyaEntity
from .const import DOMAIN, TUYA_DISCOVERY_NEW, DPCode, DPType
from .util import ConfigMapper
@dataclass
class TuyaHumidifierEntityDescription(HumidifierEntityDescription):
"""Describe an Tuya (de)humidifier entity."""
# DPCode, to use. If None, the key will be used as DPCode
dpcode: DPCode | tuple[DPCode, ...] | None = None
current_humidity: DPCode | None = None
humidity: DPCode | None = None
HUMIDIFIERS: dict[str, TuyaHumidifierEntityDescription] = {
}
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up Tuya (de)humidifier dynamically through Tuya discovery."""
hass_data: HomeAssistantTuyaData = ConfigMapper.get_tuya_data(hass, entry)
@callback
def async_discover_device(device_ids: list[str]) -> None:
"""Discover and add a discovered Tuya (de)humidifier."""
entities: list[TuyaHumidifierEntity] = []
for device_id in device_ids:
device = hass_data.device_manager.device_map[device_id]
if description := HUMIDIFIERS.get(device.category):
entities.append(
TuyaHumidifierEntity(device, hass_data.device_manager, description)
)
async_add_entities(entities)
async_discover_device([*hass_data.device_manager.device_map])
entry.async_on_unload(
async_dispatcher_connect(hass, TUYA_DISCOVERY_NEW, async_discover_device)
)
class TuyaHumidifierEntity(TuyaEntity, HumidifierEntity):
"""Tuya (de)humidifier Device."""
_current_humidity: IntegerTypeData | None = None
_set_humidity: IntegerTypeData | None = None
_switch_dpcode: DPCode | None = None
entity_description: TuyaHumidifierEntityDescription
_attr_name = None
def __init__(
self,
device: TuyaDevice,
device_manager: TuyaDeviceManager,
description: TuyaHumidifierEntityDescription,
) -> None:
"""Init Tuya (de)humidifier."""
super().__init__(device, device_manager)
self.entity_description = description
self._attr_unique_id = f"{super().unique_id}{description.key}"
# Determine main switch DPCode
self._switch_dpcode = self.find_dpcode(
description.dpcode or DPCode(description.key), prefer_function=True
)
# Determine humidity parameters
if int_type := self.find_dpcode(
description.humidity, dptype=DPType.INTEGER, prefer_function=True
):
self._set_humidity = int_type
self._attr_min_humidity = int(int_type.min_scaled)
self._attr_max_humidity = int(int_type.max_scaled)
# Determine current humidity DPCode
if int_type := self.find_dpcode(
description.current_humidity,
dptype=DPType.INTEGER,
):
self._current_humidity = int_type
# Determine mode support and provided modes
if enum_type := self.find_dpcode(
DPCode.MODE, dptype=DPType.ENUM, prefer_function=True
):
self._attr_supported_features |= HumidifierEntityFeature.MODES
self._attr_available_modes = enum_type.range
@property
def is_on(self) -> bool:
"""Return the device is on or off."""
if self._switch_dpcode is None:
return False
return self.device.status.get(self._switch_dpcode, False)
@property
def mode(self) -> str | None:
"""Return the current mode."""
return self.device.status.get(DPCode.MODE)
@property
def target_humidity(self) -> int | None:
"""Return the humidity we try to reach."""
if self._set_humidity is None:
return None
humidity = self.device.status.get(self._set_humidity.dpcode)
if humidity is None:
return None
return round(self._set_humidity.scale_value(humidity))
@property
def current_humidity(self) -> int | None:
"""Return the current humidity."""
if self._current_humidity is None:
return None
if (
current_humidity := self.device.status.get(self._current_humidity.dpcode)
) is None:
return None
return round(self._current_humidity.scale_value(current_humidity))
def turn_on(self, **kwargs):
"""Turn the device on."""
self._send_command([{"code": self._switch_dpcode, "value": True}])
def turn_off(self, **kwargs):
"""Turn the device off."""
self._send_command([{"code": self._switch_dpcode, "value": False}])
def set_humidity(self, humidity: int) -> None:
"""Set new target humidity."""
if self._set_humidity is None:
raise RuntimeError(
"Cannot set humidity, device doesn't provide methods to set it"
)
self._send_command(
[
{
"code": self._set_humidity.dpcode,
"value": self._set_humidity.scale_value_back(humidity),
}
]
)
def set_mode(self, mode):
"""Set new target preset mode."""
self._send_command([{"code": DPCode.MODE, "value": mode}])