core/homeassistant/components/xiaomi_miio/number.py

131 lines
3.9 KiB
Python

"""Motor speed support for Xiaomi Mi Air Humidifier."""
from __future__ import annotations
from dataclasses import dataclass
from enum import Enum
from homeassistant.components.number import NumberEntity, NumberEntityDescription
from homeassistant.core import callback
from .const import (
CONF_DEVICE,
CONF_FLOW_TYPE,
CONF_MODEL,
DOMAIN,
FEATURE_SET_MOTOR_SPEED,
KEY_COORDINATOR,
KEY_DEVICE,
MODEL_AIRHUMIDIFIER_CA4,
)
from .device import XiaomiCoordinatedMiioEntity
ATTR_MOTOR_SPEED = "motor_speed"
@dataclass
class XiaomiMiioNumberDescription(NumberEntityDescription):
"""A class that describes number entities."""
min_value: float | None = None
max_value: float | None = None
step: float | None = None
available_with_device_off: bool = True
NUMBER_TYPES = {
FEATURE_SET_MOTOR_SPEED: XiaomiMiioNumberDescription(
key=ATTR_MOTOR_SPEED,
name="Motor Speed",
icon="mdi:fast-forward-outline",
unit_of_measurement="rpm",
min_value=200,
max_value=2000,
step=10,
available_with_device_off=False,
),
}
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the Selectors from a config entry."""
entities = []
if not config_entry.data[CONF_FLOW_TYPE] == CONF_DEVICE:
return
model = config_entry.data[CONF_MODEL]
device = hass.data[DOMAIN][config_entry.entry_id][KEY_DEVICE]
if model != MODEL_AIRHUMIDIFIER_CA4:
return
description = NUMBER_TYPES[FEATURE_SET_MOTOR_SPEED]
entities.append(
XiaomiAirHumidifierNumber(
f"{config_entry.title} {description.name}",
device,
config_entry,
f"{description.key}_{config_entry.unique_id}",
hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR],
description,
)
)
async_add_entities(entities)
class XiaomiAirHumidifierNumber(XiaomiCoordinatedMiioEntity, NumberEntity):
"""Representation of a generic Xiaomi attribute selector."""
def __init__(self, name, device, entry, unique_id, coordinator, description):
"""Initialize the generic Xiaomi attribute selector."""
super().__init__(name, device, entry, unique_id, coordinator)
self._attr_min_value = description.min_value
self._attr_max_value = description.max_value
self._attr_step = description.step
self._attr_value = self._extract_value_from_attribute(
coordinator.data, description.key
)
self.entity_description = description
@property
def available(self):
"""Return the number controller availability."""
if (
super().available
and not self.coordinator.data.is_on
and not self.entity_description.available_with_device_off
):
return False
return super().available
@staticmethod
def _extract_value_from_attribute(state, attribute):
value = getattr(state, attribute)
if isinstance(value, Enum):
return value.value
return value
async def async_set_value(self, value):
"""Set an option of the miio device."""
if await self.async_set_motor_speed(value):
self._attr_value = value
self.async_write_ha_state()
@callback
def _handle_coordinator_update(self):
"""Fetch state from the device."""
# On state change the device doesn't provide the new state immediately.
self._attr_value = self._extract_value_from_attribute(
self.coordinator.data, self.entity_description.key
)
self.async_write_ha_state()
async def async_set_motor_speed(self, motor_speed: int = 400):
"""Set the target motor speed."""
return await self._try_command(
"Setting the target motor speed of the miio device failed.",
self._device.set_speed,
motor_speed,
)