1 """Class to hold all thermostat accessories."""
6 from pyhap.const
import CATEGORY_HUMIDIFIER
7 from pyhap.util
import callback
as pyhap_callback
10 ATTR_CURRENT_HUMIDITY,
16 DOMAIN
as HUMIDIFIER_DOMAIN,
18 HumidifierDeviceClass,
30 EventStateChangedData,
37 from .accessories
import TYPES, HomeAccessory
40 CHAR_CURRENT_HUMIDIFIER_DEHUMIDIFIER,
41 CHAR_CURRENT_HUMIDITY,
42 CHAR_DEHUMIDIFIER_THRESHOLD_HUMIDITY,
43 CHAR_HUMIDIFIER_THRESHOLD_HUMIDITY,
44 CHAR_TARGET_HUMIDIFIER_DEHUMIDIFIER,
45 CONF_LINKED_HUMIDITY_SENSOR,
49 SERV_HUMIDIFIER_DEHUMIDIFIER,
52 _LOGGER = logging.getLogger(__name__)
57 HC_HASS_TO_HOMEKIT_DEVICE_CLASS = {
58 HumidifierDeviceClass.HUMIDIFIER: HC_HUMIDIFIER,
59 HumidifierDeviceClass.DEHUMIDIFIER: HC_DEHUMIDIFIER,
62 HC_HASS_TO_HOMEKIT_DEVICE_CLASS_NAME = {
63 HumidifierDeviceClass.HUMIDIFIER:
"Humidifier",
64 HumidifierDeviceClass.DEHUMIDIFIER:
"Dehumidifier",
67 HC_DEVICE_CLASS_TO_TARGET_CHAR = {
68 HC_HUMIDIFIER: CHAR_HUMIDIFIER_THRESHOLD_HUMIDITY,
69 HC_DEHUMIDIFIER: CHAR_DEHUMIDIFIER_THRESHOLD_HUMIDITY,
75 HC_STATE_HUMIDIFYING = 2
76 HC_STATE_DEHUMIDIFYING = 3
79 "Inactive": HC_STATE_INACTIVE,
80 "Idle": HC_STATE_IDLE,
83 VALID_VALUES_BY_DEVICE_CLASS = {
84 HumidifierDeviceClass.HUMIDIFIER: {
86 "Humidifying": HC_STATE_HUMIDIFYING,
88 HumidifierDeviceClass.DEHUMIDIFIER: {
90 "Dehumidifying": HC_STATE_DEHUMIDIFYING,
95 @TYPES.register("HumidifierDehumidifier")
97 """Generate a HumidifierDehumidifier accessory for a humidifier."""
100 """Initialize a HumidifierDehumidifier accessory object."""
101 super().
__init__(*args, category=CATEGORY_HUMIDIFIER)
109 self.chars: list[str] = []
110 states = self.
hasshass.states
111 state = states.get(self.
entity_identity_id)
113 device_class = state.attributes.get(
114 ATTR_DEVICE_CLASS, HumidifierDeviceClass.HUMIDIFIER
123 serv_humidifier_dehumidifier = self.add_preload_service(
124 SERV_HUMIDIFIER_DEHUMIDIFIER, self.chars
129 serv_humidifier_dehumidifier.configure_char(
130 CHAR_CURRENT_HUMIDIFIER_DEHUMIDIFIER,
132 valid_values=VALID_VALUES_BY_DEVICE_CLASS[device_class],
136 serv_humidifier_dehumidifier.configure_char(
137 CHAR_TARGET_HUMIDIFIER_DEHUMIDIFIER,
144 HC_HASS_TO_HOMEKIT_DEVICE_CLASS_NAME[
153 CHAR_CURRENT_HUMIDITY, value=0
160 PROP_MIN_VALUE: DEFAULT_MIN_HUMIDITY,
161 PROP_MAX_VALUE: DEFAULT_MAX_HUMIDITY,
167 self.
char_activechar_active = serv_humidifier_dehumidifier.configure_char(
168 CHAR_ACTIVE, value=
False
173 serv_humidifier_dehumidifier.setter_callback = self.
_set_chars_set_chars
183 """Handle accessory driver started event.
185 Run inside the Home Assistant event loop.
188 self._subscriptions.append(
193 job_type=HassJobType.Callback,
201 self, event: Event[EventStateChangedData]
203 """Handle state change event listener callback."""
208 """Handle linked humidity sensor state change to update HomeKit value."""
209 if new_state
is None:
212 "%s: Unable to update from linked humidity sensor %s: the entity"
220 current_humidity =
float(new_state.state)
221 except ValueError
as ex:
223 "%s: Unable to update from linked humidity sensor %s: %s",
233 """Handle linked humidity or built-in humidity."""
236 "%s: Linked humidity sensor %s changed to %d",
244 """Set characteristics based on the data coming from HomeKit."""
245 _LOGGER.debug(
"HumidifierDehumidifier _set_chars: %s", char_values)
247 if CHAR_TARGET_HUMIDIFIER_DEHUMIDIFIER
in char_values:
248 hk_value = char_values[CHAR_TARGET_HUMIDIFIER_DEHUMIDIFIER]
251 "%s is not supported", CHAR_TARGET_HUMIDIFIER_DEHUMIDIFIER
254 if CHAR_ACTIVE
in char_values:
257 SERVICE_TURN_ON
if char_values[CHAR_ACTIVE]
else SERVICE_TURN_OFF,
258 {ATTR_ENTITY_ID: self.
entity_identity_id},
259 f
"{CHAR_ACTIVE} to {char_values[CHAR_ACTIVE]}",
268 if (humidity < min_humidity)
or (humidity > max_humidity):
269 humidity =
min(max_humidity,
max(min_humidity, humidity))
276 SERVICE_SET_HUMIDITY,
277 {ATTR_ENTITY_ID: self.
entity_identity_id, ATTR_HUMIDITY: humidity},
279 f
"{self._target_humidity_char_name} to "
280 f
"{char_values[self._target_humidity_char_name]}{PERCENTAGE}"
285 """Return min and max humidity range."""
286 attributes = state.attributes
288 int(round(attributes.get(ATTR_MIN_HUMIDITY, DEFAULT_MIN_HUMIDITY))), 0
291 int(round(attributes.get(ATTR_MAX_HUMIDITY, DEFAULT_MAX_HUMIDITY))), 100
293 return min_humidity, max_humidity
297 """Update state without rechecking the device features."""
298 is_active = new_state.state == STATE_ON
299 attributes = new_state.attributes
307 current_state = HC_STATE_HUMIDIFYING
309 current_state = HC_STATE_DEHUMIDIFYING
311 current_state = HC_STATE_INACTIVE
315 target_humidity = attributes.get(ATTR_HUMIDITY)
316 if isinstance(target_humidity, (int, float)):
318 current_humidity = attributes.get(ATTR_CURRENT_HUMIDITY)
319 if isinstance(current_humidity, (int, float)):
None async_call_service(self, str domain, str service, dict[str, Any]|None service_data, Any|None value=None)
None async_update_state(self, State new_state)
tuple[int, int] get_humidity_range(self, State state)
_target_humidity_char_name
None async_update_state(self, State new_state)
char_target_humidifier_dehumidifier
None _async_update_current_humidity(self, State|None new_state)
None _set_chars(self, dict[str, Any] char_values)
None __init__(self, *Any args)
None _async_update_current_humidity_value(self, float current_humidity)
None async_update_current_humidity_event(self, Event[EventStateChangedData] event)
char_current_humidifier_dehumidifier
web.Response get(self, web.Request request, str config_key)
CALLBACK_TYPE async_track_state_change_event(HomeAssistant hass, str|Iterable[str] entity_ids, Callable[[Event[EventStateChangedData]], Any] action, HassJobType|None job_type=None)