Update HomeKit humidifiers to handle current humidity (#101964)

This commit is contained in:
J. Nick Koston 2023-10-13 14:23:50 -10:00 committed by GitHub
parent 8fd5d89d43
commit f8f39a29de
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 94 additions and 27 deletions

View file

@ -5,6 +5,7 @@ from typing import Any
from pyhap.const import CATEGORY_HUMIDIFIER
from homeassistant.components.humidifier import (
ATTR_CURRENT_HUMIDITY,
ATTR_HUMIDITY,
ATTR_MAX_HUMIDITY,
ATTR_MIN_HUMIDITY,
@ -64,11 +65,28 @@ HC_DEVICE_CLASS_TO_TARGET_CHAR = {
HC_DEHUMIDIFIER: CHAR_DEHUMIDIFIER_THRESHOLD_HUMIDITY,
}
HC_STATE_INACTIVE = 0
HC_STATE_IDLE = 1
HC_STATE_HUMIDIFYING = 2
HC_STATE_DEHUMIDIFYING = 3
BASE_VALID_VALUES = {
"Inactive": HC_STATE_INACTIVE,
"Idle": HC_STATE_IDLE,
}
VALID_VALUES_BY_DEVICE_CLASS = {
HumidifierDeviceClass.HUMIDIFIER: {
**BASE_VALID_VALUES,
"Humidifying": HC_STATE_HUMIDIFYING,
},
HumidifierDeviceClass.DEHUMIDIFIER: {
**BASE_VALID_VALUES,
"Dehumidifying": HC_STATE_DEHUMIDIFYING,
},
}
@TYPES.register("HumidifierDehumidifier")
class HumidifierDehumidifier(HomeAccessory):
@ -85,7 +103,8 @@ class HumidifierDehumidifier(HomeAccessory):
)
self.chars: list[str] = []
state = self.hass.states.get(self.entity_id)
states = self.hass.states
state = states.get(self.entity_id)
assert state
device_class = state.attributes.get(
ATTR_DEVICE_CLASS, HumidifierDeviceClass.HUMIDIFIER
@ -104,7 +123,9 @@ class HumidifierDehumidifier(HomeAccessory):
# Current and target mode characteristics
self.char_current_humidifier_dehumidifier = (
serv_humidifier_dehumidifier.configure_char(
CHAR_CURRENT_HUMIDIFIER_DEHUMIDIFIER, value=0
CHAR_CURRENT_HUMIDIFIER_DEHUMIDIFIER,
value=0,
valid_values=VALID_VALUES_BY_DEVICE_CLASS[device_class],
)
)
self.char_target_humidifier_dehumidifier = (
@ -149,8 +170,7 @@ class HumidifierDehumidifier(HomeAccessory):
self.linked_humidity_sensor = self.config.get(CONF_LINKED_HUMIDITY_SENSOR)
if self.linked_humidity_sensor:
humidity_state = self.hass.states.get(self.linked_humidity_sensor)
if humidity_state:
if humidity_state := states.get(self.linked_humidity_sensor):
self._async_update_current_humidity(humidity_state)
async def run(self) -> None:
@ -191,14 +211,6 @@ class HumidifierDehumidifier(HomeAccessory):
return
try:
current_humidity = float(new_state.state)
if self.char_current_humidity.value != current_humidity:
_LOGGER.debug(
"%s: Linked humidity sensor %s changed to %d",
self.entity_id,
self.linked_humidity_sensor,
current_humidity,
)
self.char_current_humidity.set_value(current_humidity)
except ValueError as ex:
_LOGGER.debug(
"%s: Unable to update from linked humidity sensor %s: %s",
@ -206,6 +218,20 @@ class HumidifierDehumidifier(HomeAccessory):
self.linked_humidity_sensor,
ex,
)
return
self._async_update_current_humidity_value(current_humidity)
@callback
def _async_update_current_humidity_value(self, current_humidity: float) -> None:
"""Handle linked humidity or built-in humidity."""
if self.char_current_humidity.value != current_humidity:
_LOGGER.debug(
"%s: Linked humidity sensor %s changed to %d",
self.entity_id,
self.linked_humidity_sensor,
current_humidity,
)
self.char_current_humidity.set_value(current_humidity)
def _set_chars(self, char_values: dict[str, Any]) -> None:
"""Set characteristics based on the data coming from HomeKit."""
@ -229,19 +255,7 @@ class HumidifierDehumidifier(HomeAccessory):
if self._target_humidity_char_name in char_values:
state = self.hass.states.get(self.entity_id)
assert state
max_humidity = state.attributes.get(ATTR_MAX_HUMIDITY, DEFAULT_MAX_HUMIDITY)
max_humidity = round(max_humidity)
max_humidity = min(max_humidity, 100)
min_humidity = state.attributes.get(ATTR_MIN_HUMIDITY, DEFAULT_MIN_HUMIDITY)
min_humidity = round(min_humidity)
min_humidity = max(min_humidity, 0)
# The min/max humidity values here should be clamped to the HomeKit
# min/max that was set when the accessory was added to HomeKit so
# that the user cannot set a value outside of the range that was
# originally set as it could cause HomeKit to report the accessory
# as not responding.
min_humidity, max_humidity = self.get_humidity_range(state)
humidity = round(char_values[self._target_humidity_char_name])
if (humidity < min_humidity) or (humidity > max_humidity):
@ -260,10 +274,22 @@ class HumidifierDehumidifier(HomeAccessory):
),
)
def get_humidity_range(self, state: State) -> tuple[int, int]:
"""Return min and max humidity range."""
attributes = state.attributes
min_humidity = max(
int(round(attributes.get(ATTR_MIN_HUMIDITY, DEFAULT_MIN_HUMIDITY))), 0
)
max_humidity = min(
int(round(attributes.get(ATTR_MAX_HUMIDITY, DEFAULT_MAX_HUMIDITY))), 100
)
return min_humidity, max_humidity
@callback
def async_update_state(self, new_state: State) -> None:
"""Update state without rechecking the device features."""
is_active = new_state.state == STATE_ON
attributes = new_state.attributes
# Update active state
self.char_active.set_value(is_active)
@ -279,6 +305,9 @@ class HumidifierDehumidifier(HomeAccessory):
self.char_current_humidifier_dehumidifier.set_value(current_state)
# Update target humidity
target_humidity = new_state.attributes.get(ATTR_HUMIDITY)
target_humidity = attributes.get(ATTR_HUMIDITY)
if isinstance(target_humidity, (int, float)):
self.char_target_humidity.set_value(target_humidity)
current_humidity = attributes.get(ATTR_CURRENT_HUMIDITY)
if isinstance(current_humidity, (int, float)):
self.char_current_humidity.set_value(current_humidity)