1
mirror of https://github.com/home-assistant/core synced 2024-08-31 05:57:13 +02:00
ha-core/homeassistant/components/homekit/type_humidifiers.py
2021-03-20 17:08:49 -10:00

248 lines
8.4 KiB
Python

"""Class to hold all thermostat accessories."""
import logging
from pyhap.const import CATEGORY_HUMIDIFIER
from homeassistant.components.humidifier.const import (
ATTR_HUMIDITY,
ATTR_MAX_HUMIDITY,
ATTR_MIN_HUMIDITY,
DEFAULT_MAX_HUMIDITY,
DEFAULT_MIN_HUMIDITY,
DEVICE_CLASS_DEHUMIDIFIER,
DEVICE_CLASS_HUMIDIFIER,
DOMAIN,
SERVICE_SET_HUMIDITY,
)
from homeassistant.const import (
ATTR_DEVICE_CLASS,
ATTR_ENTITY_ID,
PERCENTAGE,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
STATE_ON,
)
from homeassistant.core import callback
from homeassistant.helpers.event import async_track_state_change_event
from .accessories import TYPES, HomeAccessory
from .const import (
CHAR_ACTIVE,
CHAR_CURRENT_HUMIDIFIER_DEHUMIDIFIER,
CHAR_CURRENT_HUMIDITY,
CHAR_DEHUMIDIFIER_THRESHOLD_HUMIDITY,
CHAR_HUMIDIFIER_THRESHOLD_HUMIDITY,
CHAR_TARGET_HUMIDIFIER_DEHUMIDIFIER,
CONF_LINKED_HUMIDITY_SENSOR,
PROP_MAX_VALUE,
PROP_MIN_STEP,
PROP_MIN_VALUE,
SERV_HUMIDIFIER_DEHUMIDIFIER,
)
_LOGGER = logging.getLogger(__name__)
HC_HUMIDIFIER = 1
HC_DEHUMIDIFIER = 2
HC_HASS_TO_HOMEKIT_DEVICE_CLASS = {
DEVICE_CLASS_HUMIDIFIER: HC_HUMIDIFIER,
DEVICE_CLASS_DEHUMIDIFIER: HC_DEHUMIDIFIER,
}
HC_HASS_TO_HOMEKIT_DEVICE_CLASS_NAME = {
DEVICE_CLASS_HUMIDIFIER: "Humidifier",
DEVICE_CLASS_DEHUMIDIFIER: "Dehumidifier",
}
HC_DEVICE_CLASS_TO_TARGET_CHAR = {
HC_HUMIDIFIER: CHAR_HUMIDIFIER_THRESHOLD_HUMIDITY,
HC_DEHUMIDIFIER: CHAR_DEHUMIDIFIER_THRESHOLD_HUMIDITY,
}
HC_STATE_INACTIVE = 0
HC_STATE_IDLE = 1
HC_STATE_HUMIDIFYING = 2
HC_STATE_DEHUMIDIFYING = 3
@TYPES.register("HumidifierDehumidifier")
class HumidifierDehumidifier(HomeAccessory):
"""Generate a HumidifierDehumidifier accessory for a humidifier."""
def __init__(self, *args):
"""Initialize a HumidifierDehumidifier accessory object."""
super().__init__(*args, category=CATEGORY_HUMIDIFIER)
self.chars = []
state = self.hass.states.get(self.entity_id)
device_class = state.attributes.get(ATTR_DEVICE_CLASS, DEVICE_CLASS_HUMIDIFIER)
self._hk_device_class = HC_HASS_TO_HOMEKIT_DEVICE_CLASS[device_class]
self._target_humidity_char_name = HC_DEVICE_CLASS_TO_TARGET_CHAR[
self._hk_device_class
]
self.chars.append(self._target_humidity_char_name)
serv_humidifier_dehumidifier = self.add_preload_service(
SERV_HUMIDIFIER_DEHUMIDIFIER, self.chars
)
# Current and target mode characteristics
self.char_current_humidifier_dehumidifier = (
serv_humidifier_dehumidifier.configure_char(
CHAR_CURRENT_HUMIDIFIER_DEHUMIDIFIER, value=0
)
)
self.char_target_humidifier_dehumidifier = (
serv_humidifier_dehumidifier.configure_char(
CHAR_TARGET_HUMIDIFIER_DEHUMIDIFIER,
value=self._hk_device_class,
valid_values={
HC_HASS_TO_HOMEKIT_DEVICE_CLASS_NAME[
device_class
]: self._hk_device_class
},
)
)
# Current and target humidity characteristics
self.char_current_humidity = serv_humidifier_dehumidifier.configure_char(
CHAR_CURRENT_HUMIDITY, value=0
)
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)
self.char_target_humidity = serv_humidifier_dehumidifier.configure_char(
self._target_humidity_char_name,
value=45,
properties={
PROP_MIN_VALUE: min_humidity,
PROP_MAX_VALUE: max_humidity,
PROP_MIN_STEP: 1,
},
)
# Active/inactive characteristics
self.char_active = serv_humidifier_dehumidifier.configure_char(
CHAR_ACTIVE, value=False
)
self.async_update_state(state)
serv_humidifier_dehumidifier.setter_callback = self._set_chars
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:
self._async_update_current_humidity(humidity_state)
async def run(self):
"""Handle accessory driver started event.
Run inside the Home Assistant event loop.
"""
if self.linked_humidity_sensor:
async_track_state_change_event(
self.hass,
[self.linked_humidity_sensor],
self.async_update_current_humidity_event,
)
await super().run()
@callback
def async_update_current_humidity_event(self, event):
"""Handle state change event listener callback."""
self._async_update_current_humidity(event.data.get("new_state"))
@callback
def _async_update_current_humidity(self, new_state):
"""Handle linked humidity sensor state change to update HomeKit value."""
if new_state is None:
_LOGGER.error(
"%s: Unable to update from linked humidity sensor %s: the entity state is None",
self.entity_id,
self.linked_humidity_sensor,
)
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.error(
"%s: Unable to update from linked humidity sensor %s: %s",
self.entity_id,
self.linked_humidity_sensor,
ex,
)
def _set_chars(self, char_values):
_LOGGER.debug("HumidifierDehumidifier _set_chars: %s", char_values)
if CHAR_TARGET_HUMIDIFIER_DEHUMIDIFIER in char_values:
hk_value = char_values[CHAR_TARGET_HUMIDIFIER_DEHUMIDIFIER]
if self._hk_device_class != hk_value:
_LOGGER.error(
"%s is not supported", CHAR_TARGET_HUMIDIFIER_DEHUMIDIFIER
)
if CHAR_ACTIVE in char_values:
self.async_call_service(
DOMAIN,
SERVICE_TURN_ON if char_values[CHAR_ACTIVE] else SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: self.entity_id},
f"{CHAR_ACTIVE} to {char_values[CHAR_ACTIVE]}",
)
if self._target_humidity_char_name in char_values:
humidity = round(char_values[self._target_humidity_char_name])
self.async_call_service(
DOMAIN,
SERVICE_SET_HUMIDITY,
{ATTR_ENTITY_ID: self.entity_id, ATTR_HUMIDITY: humidity},
f"{self._target_humidity_char_name} to "
f"{char_values[self._target_humidity_char_name]}{PERCENTAGE}",
)
@callback
def async_update_state(self, new_state):
"""Update state without rechecking the device features."""
is_active = new_state.state == STATE_ON
# Update active state
if self.char_active.value != is_active:
self.char_active.set_value(is_active)
# Set current state
if is_active:
if self._hk_device_class == HC_HUMIDIFIER:
current_state = HC_STATE_HUMIDIFYING
else:
current_state = HC_STATE_DEHUMIDIFYING
else:
current_state = HC_STATE_INACTIVE
if self.char_current_humidifier_dehumidifier.value != current_state:
self.char_current_humidifier_dehumidifier.set_value(current_state)
# Update target humidity
target_humidity = new_state.attributes.get(ATTR_HUMIDITY)
if (
isinstance(target_humidity, (int, float))
and self.char_target_humidity.value != target_humidity
):
self.char_target_humidity.set_value(target_humidity)