1
mirror of https://github.com/home-assistant/core synced 2024-08-02 23:40:32 +02:00
ha-core/homeassistant/components/humidifier/__init__.py
2022-04-01 20:10:52 +02:00

221 lines
6.5 KiB
Python

"""Provides functionality to interact with humidifier devices."""
from __future__ import annotations
from dataclasses import dataclass
from datetime import timedelta
import logging
from typing import Any, final
import voluptuous as vol
from homeassistant.backports.enum import StrEnum
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_MODE,
SERVICE_TOGGLE,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
STATE_ON,
)
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import ( # noqa: F401
PLATFORM_SCHEMA,
PLATFORM_SCHEMA_BASE,
)
from homeassistant.helpers.entity import ToggleEntity, ToggleEntityDescription
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.typing import ConfigType
from homeassistant.loader import bind_hass
from .const import ( # noqa: F401
ATTR_AVAILABLE_MODES,
ATTR_HUMIDITY,
ATTR_MAX_HUMIDITY,
ATTR_MIN_HUMIDITY,
DEFAULT_MAX_HUMIDITY,
DEFAULT_MIN_HUMIDITY,
DEVICE_CLASS_DEHUMIDIFIER,
DEVICE_CLASS_HUMIDIFIER,
DOMAIN,
SERVICE_SET_HUMIDITY,
SERVICE_SET_MODE,
SUPPORT_MODES,
HumidifierEntityFeature,
)
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(seconds=60)
ENTITY_ID_FORMAT = DOMAIN + ".{}"
class HumidifierDeviceClass(StrEnum):
"""Device class for humidifiers."""
HUMIDIFIER = "humidifier"
DEHUMIDIFIER = "dehumidifier"
DEVICE_CLASSES_SCHEMA = vol.All(vol.Lower, vol.Coerce(HumidifierDeviceClass))
# DEVICE_CLASSES below is deprecated as of 2021.12
# use the HumidifierDeviceClass enum instead.
DEVICE_CLASSES = [cls.value for cls in HumidifierDeviceClass]
@bind_hass
def is_on(hass, entity_id):
"""Return if the humidifier is on based on the statemachine.
Async friendly.
"""
return hass.states.is_state(entity_id, STATE_ON)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up humidifier devices."""
component = hass.data[DOMAIN] = EntityComponent(
_LOGGER, DOMAIN, hass, SCAN_INTERVAL
)
await component.async_setup(config)
component.async_register_entity_service(SERVICE_TURN_ON, {}, "async_turn_on")
component.async_register_entity_service(SERVICE_TURN_OFF, {}, "async_turn_off")
component.async_register_entity_service(SERVICE_TOGGLE, {}, "async_toggle")
component.async_register_entity_service(
SERVICE_SET_MODE,
{vol.Required(ATTR_MODE): cv.string},
"async_set_mode",
[HumidifierEntityFeature.MODES],
)
component.async_register_entity_service(
SERVICE_SET_HUMIDITY,
{
vol.Required(ATTR_HUMIDITY): vol.All(
vol.Coerce(int), vol.Range(min=0, max=100)
)
},
"async_set_humidity",
)
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up a config entry."""
component: EntityComponent = hass.data[DOMAIN]
return await component.async_setup_entry(entry)
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
component: EntityComponent = hass.data[DOMAIN]
return await component.async_unload_entry(entry)
@dataclass
class HumidifierEntityDescription(ToggleEntityDescription):
"""A class that describes humidifier entities."""
device_class: HumidifierDeviceClass | str | None = None
class HumidifierEntity(ToggleEntity):
"""Base class for humidifier entities."""
entity_description: HumidifierEntityDescription
_attr_available_modes: list[str] | None
_attr_device_class: HumidifierDeviceClass | str | None
_attr_max_humidity: int = DEFAULT_MAX_HUMIDITY
_attr_min_humidity: int = DEFAULT_MIN_HUMIDITY
_attr_mode: str | None
_attr_target_humidity: int | None = None
@property
def capability_attributes(self) -> dict[str, Any]:
"""Return capability attributes."""
supported_features = self.supported_features or 0
data: dict[str, int | list[str] | None] = {
ATTR_MIN_HUMIDITY: self.min_humidity,
ATTR_MAX_HUMIDITY: self.max_humidity,
}
if supported_features & HumidifierEntityFeature.MODES:
data[ATTR_AVAILABLE_MODES] = self.available_modes
return data
@property
def device_class(self) -> HumidifierDeviceClass | str | None:
"""Return the class of this entity."""
if hasattr(self, "_attr_device_class"):
return self._attr_device_class
if hasattr(self, "entity_description"):
return self.entity_description.device_class
return None
@final
@property
def state_attributes(self) -> dict[str, Any]:
"""Return the optional state attributes."""
supported_features = self.supported_features or 0
data: dict[str, int | str | None] = {}
if self.target_humidity is not None:
data[ATTR_HUMIDITY] = self.target_humidity
if supported_features & HumidifierEntityFeature.MODES:
data[ATTR_MODE] = self.mode
return data
@property
def target_humidity(self) -> int | None:
"""Return the humidity we try to reach."""
return self._attr_target_humidity
@property
def mode(self) -> str | None:
"""Return the current mode, e.g., home, auto, baby.
Requires HumidifierEntityFeature.MODES.
"""
return self._attr_mode
@property
def available_modes(self) -> list[str] | None:
"""Return a list of available modes.
Requires HumidifierEntityFeature.MODES.
"""
return self._attr_available_modes
def set_humidity(self, humidity: int) -> None:
"""Set new target humidity."""
raise NotImplementedError()
async def async_set_humidity(self, humidity: int) -> None:
"""Set new target humidity."""
await self.hass.async_add_executor_job(self.set_humidity, humidity)
def set_mode(self, mode: str) -> None:
"""Set new mode."""
raise NotImplementedError()
async def async_set_mode(self, mode: str) -> None:
"""Set new mode."""
await self.hass.async_add_executor_job(self.set_mode, mode)
@property
def min_humidity(self) -> int:
"""Return the minimum humidity."""
return self._attr_min_humidity
@property
def max_humidity(self) -> int:
"""Return the maximum humidity."""
return self._attr_max_humidity