Xiaomi_miio fan percentage based speeds and preset_modes (#51791)

This commit is contained in:
Jan Bouwhuis 2021-06-23 11:30:42 +02:00 committed by GitHub
parent 6c4816567c
commit 29bfb4b046
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -3,6 +3,7 @@ import asyncio
from enum import Enum from enum import Enum
from functools import partial from functools import partial
import logging import logging
import math
from miio import ( from miio import (
AirFresh, AirFresh,
@ -40,6 +41,7 @@ from homeassistant.components.fan import (
SPEED_HIGH, SPEED_HIGH,
SPEED_LOW, SPEED_LOW,
SPEED_MEDIUM, SPEED_MEDIUM,
SUPPORT_PRESET_MODE,
SUPPORT_SET_SPEED, SUPPORT_SET_SPEED,
FanEntity, FanEntity,
) )
@ -53,6 +55,10 @@ from homeassistant.const import (
CONF_TOKEN, CONF_TOKEN,
) )
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.util.percentage import (
percentage_to_ranged_value,
ranged_value_to_percentage,
)
from .const import ( from .const import (
CONF_DEVICE, CONF_DEVICE,
@ -157,7 +163,6 @@ ATTR_DRY = "dry"
# Air Humidifier CA4 # Air Humidifier CA4
ATTR_ACTUAL_MOTOR_SPEED = "actual_speed" ATTR_ACTUAL_MOTOR_SPEED = "actual_speed"
ATTR_FAHRENHEIT = "fahrenheit" ATTR_FAHRENHEIT = "fahrenheit"
ATTR_FAULT = "fault"
# Air Fresh # Air Fresh
ATTR_CO2 = "co2" ATTR_CO2 = "co2"
@ -332,10 +337,15 @@ AVAILABLE_ATTRIBUTES_AIRFRESH = {
} }
OPERATION_MODES_AIRPURIFIER = ["Auto", "Silent", "Favorite", "Idle"] OPERATION_MODES_AIRPURIFIER = ["Auto", "Silent", "Favorite", "Idle"]
PRESET_MODES_AIRPURIFIER = ["Auto", "Silent", "Favorite", "Idle"]
OPERATION_MODES_AIRPURIFIER_PRO = ["Auto", "Silent", "Favorite"] OPERATION_MODES_AIRPURIFIER_PRO = ["Auto", "Silent", "Favorite"]
PRESET_MODES_AIRPURIFIER_PRO = ["Auto", "Silent", "Favorite"]
OPERATION_MODES_AIRPURIFIER_PRO_V7 = OPERATION_MODES_AIRPURIFIER_PRO OPERATION_MODES_AIRPURIFIER_PRO_V7 = OPERATION_MODES_AIRPURIFIER_PRO
PRESET_MODES_AIRPURIFIER_PRO_V7 = PRESET_MODES_AIRPURIFIER_PRO
OPERATION_MODES_AIRPURIFIER_2S = ["Auto", "Silent", "Favorite"] OPERATION_MODES_AIRPURIFIER_2S = ["Auto", "Silent", "Favorite"]
PRESET_MODES_AIRPURIFIER_2S = ["Auto", "Silent", "Favorite"]
OPERATION_MODES_AIRPURIFIER_3 = ["Auto", "Silent", "Favorite", "Fan"] OPERATION_MODES_AIRPURIFIER_3 = ["Auto", "Silent", "Favorite", "Fan"]
PRESET_MODES_AIRPURIFIER_3 = ["Auto", "Silent", "Favorite", "Fan"]
OPERATION_MODES_AIRPURIFIER_V3 = [ OPERATION_MODES_AIRPURIFIER_V3 = [
"Auto", "Auto",
"Silent", "Silent",
@ -345,7 +355,19 @@ OPERATION_MODES_AIRPURIFIER_V3 = [
"High", "High",
"Strong", "Strong",
] ]
PRESET_MODES_AIRPURIFIER_V3 = [
"Auto",
"Silent",
"Favorite",
"Idle",
"Medium",
"High",
"Strong",
]
OPERATION_MODES_AIRFRESH = ["Auto", "Silent", "Interval", "Low", "Middle", "Strong"] OPERATION_MODES_AIRFRESH = ["Auto", "Silent", "Interval", "Low", "Middle", "Strong"]
PRESET_MODES_AIRFRESH = ["Auto", "Interval"]
PRESET_MODES_AIRHUMIDIFIER = ["Auto"]
PRESET_MODES_AIRHUMIDIFIER_CA4 = ["Auto"]
SUCCESS = ["ok"] SUCCESS = ["ok"]
@ -635,11 +657,42 @@ class XiaomiGenericDevice(XiaomiMiioEntity, FanEntity):
self._state_attrs = {ATTR_MODEL: self._model} self._state_attrs = {ATTR_MODEL: self._model}
self._device_features = FEATURE_SET_CHILD_LOCK self._device_features = FEATURE_SET_CHILD_LOCK
self._skip_update = False self._skip_update = False
self._supported_features = 0
self._speed_count = 100
self._preset_modes = []
# the speed_list attribute is deprecated, support will end with release 2021.7
self._speed_list = []
@property @property
def supported_features(self): def supported_features(self):
"""Flag supported features.""" """Flag supported features."""
return SUPPORT_SET_SPEED return self._supported_features
# the speed_list attribute is deprecated, support will end with release 2021.7
@property
def speed_list(self) -> list:
"""Get the list of available speeds."""
return self._speed_list
@property
def speed_count(self):
"""Return the number of speeds of the fan supported."""
return self._speed_count
@property
def preset_modes(self) -> list:
"""Get the list of available preset modes."""
return self._preset_modes
@property
def percentage(self):
"""Return the percentage based speed of the fan."""
return None
@property
def preset_mode(self):
"""Return the percentage based speed of the fan."""
return None
@property @property
def should_poll(self): def should_poll(self):
@ -701,9 +754,14 @@ class XiaomiGenericDevice(XiaomiMiioEntity, FanEntity):
**kwargs, **kwargs,
) -> None: ) -> None:
"""Turn the device on.""" """Turn the device on."""
# Remove the async_set_speed call is async_set_percentage and async_set_preset_modes have been implemented
if speed: if speed:
# If operation mode was set the device must not be turned on. await self.async_set_speed(speed)
result = await self.async_set_speed(speed) # If operation mode was set the device must not be turned on.
if percentage:
await self.async_set_percentage(percentage)
if preset_mode:
await self.async_set_preset_mode(preset_mode)
else: else:
result = await self._try_command( result = await self._try_command(
"Turning the miio device on failed.", self._device.on "Turning the miio device on failed.", self._device.on
@ -771,6 +829,22 @@ class XiaomiGenericDevice(XiaomiMiioEntity, FanEntity):
class XiaomiAirPurifier(XiaomiGenericDevice): class XiaomiAirPurifier(XiaomiGenericDevice):
"""Representation of a Xiaomi Air Purifier.""" """Representation of a Xiaomi Air Purifier."""
PRESET_MODE_MAPPING = {
"Auto": AirpurifierOperationMode.Auto,
"Silent": AirpurifierOperationMode.Silent,
"Favorite": AirpurifierOperationMode.Favorite,
"Idle": AirpurifierOperationMode.Favorite,
}
SPEED_MODE_MAPPING = {
1: AirpurifierOperationMode.Silent,
2: AirpurifierOperationMode.Medium,
3: AirpurifierOperationMode.High,
4: AirpurifierOperationMode.Strong,
}
REVERSE_SPEED_MODE_MAPPING = {v: k for k, v in SPEED_MODE_MAPPING.items()}
def __init__(self, name, device, entry, unique_id, allowed_failures=0): def __init__(self, name, device, entry, unique_id, allowed_failures=0):
"""Initialize the plug switch.""" """Initialize the plug switch."""
super().__init__(name, device, entry, unique_id) super().__init__(name, device, entry, unique_id)
@ -780,26 +854,60 @@ class XiaomiAirPurifier(XiaomiGenericDevice):
if self._model == MODEL_AIRPURIFIER_PRO: if self._model == MODEL_AIRPURIFIER_PRO:
self._device_features = FEATURE_FLAGS_AIRPURIFIER_PRO self._device_features = FEATURE_FLAGS_AIRPURIFIER_PRO
self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER_PRO self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER_PRO
# SUPPORT_SET_SPEED was disabled
# the device supports preset_modes only
self._preset_modes = PRESET_MODES_AIRPURIFIER_PRO
self._supported_features = SUPPORT_PRESET_MODE
self._speed_count = 1
# the speed_list attribute is deprecated, support will end with release 2021.7
self._speed_list = OPERATION_MODES_AIRPURIFIER_PRO self._speed_list = OPERATION_MODES_AIRPURIFIER_PRO
elif self._model == MODEL_AIRPURIFIER_PRO_V7: elif self._model == MODEL_AIRPURIFIER_PRO_V7:
self._device_features = FEATURE_FLAGS_AIRPURIFIER_PRO_V7 self._device_features = FEATURE_FLAGS_AIRPURIFIER_PRO_V7
self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER_PRO_V7 self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER_PRO_V7
# SUPPORT_SET_SPEED was disabled
# the device supports preset_modes only
self._preset_modes = PRESET_MODES_AIRPURIFIER_PRO_V7
self._supported_features = SUPPORT_PRESET_MODE
self._speed_count = 1
# the speed_list attribute is deprecated, support will end with release 2021.7
self._speed_list = OPERATION_MODES_AIRPURIFIER_PRO_V7 self._speed_list = OPERATION_MODES_AIRPURIFIER_PRO_V7
elif self._model in [MODEL_AIRPURIFIER_2S, MODEL_AIRPURIFIER_2H]: elif self._model in [MODEL_AIRPURIFIER_2S, MODEL_AIRPURIFIER_2H]:
self._device_features = FEATURE_FLAGS_AIRPURIFIER_2S self._device_features = FEATURE_FLAGS_AIRPURIFIER_2S
self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER_2S self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER_2S
# SUPPORT_SET_SPEED was disabled
# the device supports preset_modes only
self._preset_modes = PRESET_MODES_AIRPURIFIER_2S
self._supported_features = SUPPORT_PRESET_MODE
self._speed_count = 1
# the speed_list attribute is deprecated, support will end with release 2021.7
self._speed_list = OPERATION_MODES_AIRPURIFIER_2S self._speed_list = OPERATION_MODES_AIRPURIFIER_2S
elif self._model in MODELS_PURIFIER_MIOT: elif self._model in MODELS_PURIFIER_MIOT:
self._device_features = FEATURE_FLAGS_AIRPURIFIER_3 self._device_features = FEATURE_FLAGS_AIRPURIFIER_3
self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER_3 self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER_3
# SUPPORT_SET_SPEED was disabled
# the device supports preset_modes only
self._preset_modes = PRESET_MODES_AIRPURIFIER_3
self._supported_features = SUPPORT_SET_SPEED | SUPPORT_PRESET_MODE
self._speed_count = 3
# the speed_list attribute is deprecated, support will end with release 2021.7
self._speed_list = OPERATION_MODES_AIRPURIFIER_3 self._speed_list = OPERATION_MODES_AIRPURIFIER_3
elif self._model == MODEL_AIRPURIFIER_V3: elif self._model == MODEL_AIRPURIFIER_V3:
self._device_features = FEATURE_FLAGS_AIRPURIFIER_V3 self._device_features = FEATURE_FLAGS_AIRPURIFIER_V3
self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER_V3 self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER_V3
# SUPPORT_SET_SPEED was disabled
# the device supports preset_modes only
self._preset_modes = PRESET_MODES_AIRPURIFIER_V3
self._supported_features = SUPPORT_PRESET_MODE
self._speed_count = 1
# the speed_list attribute is deprecated, support will end with release 2021.7
self._speed_list = OPERATION_MODES_AIRPURIFIER_V3 self._speed_list = OPERATION_MODES_AIRPURIFIER_V3
else: else:
self._device_features = FEATURE_FLAGS_AIRPURIFIER self._device_features = FEATURE_FLAGS_AIRPURIFIER
self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER
self._preset_modes = PRESET_MODES_AIRPURIFIER
self._supported_features = SUPPORT_SET_SPEED | SUPPORT_PRESET_MODE
self._speed_count = 4
# the speed_list attribute is deprecated, support will end with release 2021.7
self._speed_list = OPERATION_MODES_AIRPURIFIER self._speed_list = OPERATION_MODES_AIRPURIFIER
self._state_attrs.update( self._state_attrs.update(
@ -846,10 +954,27 @@ class XiaomiAirPurifier(XiaomiGenericDevice):
) )
@property @property
def speed_list(self) -> list: def preset_mode(self):
"""Get the list of available speeds.""" """Get the active preset mode."""
return self._speed_list if self._state:
preset_mode = AirpurifierOperationMode(self._state_attrs[ATTR_MODE]).name
return preset_mode if preset_mode in self._preset_modes else None
return None
@property
def percentage(self):
"""Return the current percentage based speed."""
if self._state:
mode = AirpurifierOperationMode(self._state_attrs[ATTR_MODE])
if mode in self.REVERSE_SPEED_MODE_MAPPING:
return ranged_value_to_percentage(
(1, self._speed_count), self.REVERSE_SPEED_MODE_MAPPING[mode]
)
return None
# the speed attribute is deprecated, support will end with release 2021.7
@property @property
def speed(self): def speed(self):
"""Return the current speed.""" """Return the current speed."""
@ -858,6 +983,37 @@ class XiaomiAirPurifier(XiaomiGenericDevice):
return None return None
async def async_set_percentage(self, percentage: int) -> None:
"""Set the percentage of the fan.
This method is a coroutine.
"""
speed_mode = math.ceil(
percentage_to_ranged_value((1, self._speed_count), percentage)
)
if speed_mode:
await self._try_command(
"Setting operation mode of the miio device failed.",
self._device.set_mode,
AirpurifierOperationMode(self.SPEED_MODE_MAPPING[speed_mode]),
)
async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set the preset mode of the fan.
This method is a coroutine.
"""
if preset_mode not in self.preset_modes:
_LOGGER.warning("'%s'is not a valid preset mode", preset_mode)
return
await self._try_command(
"Setting operation mode of the miio device failed.",
self._device.set_mode,
self.PRESET_MODE_MAPPING[preset_mode],
)
# the async_set_speed function is deprecated, support will end with release 2021.7
# it is added here only for compatibility with legacy speeds
async def async_set_speed(self, speed: str) -> None: async def async_set_speed(self, speed: str) -> None:
"""Set the speed of the fan.""" """Set the speed of the fan."""
if self.supported_features & SUPPORT_SET_SPEED == 0: if self.supported_features & SUPPORT_SET_SPEED == 0:
@ -1004,6 +1160,34 @@ class XiaomiAirPurifier(XiaomiGenericDevice):
class XiaomiAirPurifierMiot(XiaomiAirPurifier): class XiaomiAirPurifierMiot(XiaomiAirPurifier):
"""Representation of a Xiaomi Air Purifier (MiOT protocol).""" """Representation of a Xiaomi Air Purifier (MiOT protocol)."""
PRESET_MODE_MAPPING = {
"Auto": AirpurifierMiotOperationMode.Auto,
"Silent": AirpurifierMiotOperationMode.Silent,
"Favorite": AirpurifierMiotOperationMode.Favorite,
"Fan": AirpurifierMiotOperationMode.Fan,
}
@property
def percentage(self):
"""Return the current percentage based speed."""
if self._state:
fan_level = self._state_attrs[ATTR_FAN_LEVEL]
return ranged_value_to_percentage((1, 3), fan_level)
return None
@property
def preset_mode(self):
"""Get the active preset mode."""
if self._state:
preset_mode = AirpurifierMiotOperationMode(
self._state_attrs[ATTR_MODE]
).name
return preset_mode if preset_mode in self._preset_modes else None
return None
# the speed attribute is deprecated, support will end with release 2021.7
@property @property
def speed(self): def speed(self):
"""Return the current speed.""" """Return the current speed."""
@ -1012,6 +1196,35 @@ class XiaomiAirPurifierMiot(XiaomiAirPurifier):
return None return None
async def async_set_percentage(self, percentage: int) -> None:
"""Set the percentage of the fan.
This method is a coroutine.
"""
fan_level = math.ceil(percentage_to_ranged_value((1, 3), percentage))
if fan_level:
await self._try_command(
"Setting fan level of the miio device failed.",
self._device.set_fan_level,
fan_level,
)
async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set the preset mode of the fan.
This method is a coroutine.
"""
if preset_mode not in self.preset_modes:
_LOGGER.warning("'%s'is not a valid preset mode", preset_mode)
return
await self._try_command(
"Setting operation mode of the miio device failed.",
self._device.set_mode,
self.PRESET_MODE_MAPPING[preset_mode],
)
# the async_set_speed function is deprecated, support will end with release 2021.7
# it is added here only for compatibility with legacy speeds
async def async_set_speed(self, speed: str) -> None: async def async_set_speed(self, speed: str) -> None:
"""Set the speed of the fan.""" """Set the speed of the fan."""
if self.supported_features & SUPPORT_SET_SPEED == 0: if self.supported_features & SUPPORT_SET_SPEED == 0:
@ -1040,30 +1253,58 @@ class XiaomiAirPurifierMiot(XiaomiAirPurifier):
class XiaomiAirHumidifier(XiaomiGenericDevice): class XiaomiAirHumidifier(XiaomiGenericDevice):
"""Representation of a Xiaomi Air Humidifier.""" """Representation of a Xiaomi Air Humidifier."""
SPEED_MODE_MAPPING = {
1: AirhumidifierOperationMode.Silent,
2: AirhumidifierOperationMode.Medium,
3: AirhumidifierOperationMode.High,
4: AirhumidifierOperationMode.Strong,
}
REVERSE_SPEED_MODE_MAPPING = {v: k for k, v in SPEED_MODE_MAPPING.items()}
PRESET_MODE_MAPPING = {
"Auto": AirhumidifierOperationMode.Auto,
}
def __init__(self, name, device, entry, unique_id): def __init__(self, name, device, entry, unique_id):
"""Initialize the plug switch.""" """Initialize the plug switch."""
super().__init__(name, device, entry, unique_id) super().__init__(name, device, entry, unique_id)
self._percentage = None
self._preset_mode = None
self._supported_features = SUPPORT_SET_SPEED
self._preset_modes = []
if self._model in [MODEL_AIRHUMIDIFIER_CA1, MODEL_AIRHUMIDIFIER_CB1]: if self._model in [MODEL_AIRHUMIDIFIER_CA1, MODEL_AIRHUMIDIFIER_CB1]:
self._device_features = FEATURE_FLAGS_AIRHUMIDIFIER_CA_AND_CB self._device_features = FEATURE_FLAGS_AIRHUMIDIFIER_CA_AND_CB
self._available_attributes = AVAILABLE_ATTRIBUTES_AIRHUMIDIFIER_CA_AND_CB self._available_attributes = AVAILABLE_ATTRIBUTES_AIRHUMIDIFIER_CA_AND_CB
# the speed_list attribute is deprecated, support will end with release 2021.7
self._speed_list = [ self._speed_list = [
mode.name mode.name
for mode in AirhumidifierOperationMode for mode in AirhumidifierOperationMode
if mode is not AirhumidifierOperationMode.Strong if mode is not AirhumidifierOperationMode.Strong
] ]
self._supported_features |= SUPPORT_PRESET_MODE
self._preset_modes = PRESET_MODES_AIRHUMIDIFIER
self._speed_count = 3
elif self._model in [MODEL_AIRHUMIDIFIER_CA4]: elif self._model in [MODEL_AIRHUMIDIFIER_CA4]:
self._device_features = FEATURE_FLAGS_AIRHUMIDIFIER_CA4 self._device_features = FEATURE_FLAGS_AIRHUMIDIFIER_CA4
self._available_attributes = AVAILABLE_ATTRIBUTES_AIRHUMIDIFIER_CA4 self._available_attributes = AVAILABLE_ATTRIBUTES_AIRHUMIDIFIER_CA4
# the speed_list attribute is deprecated, support will end with release 2021.7
self._speed_list = [SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH] self._speed_list = [SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH]
self._supported_features |= SUPPORT_PRESET_MODE
self._preset_modes = PRESET_MODES_AIRHUMIDIFIER
self._speed_count = 3
else: else:
self._device_features = FEATURE_FLAGS_AIRHUMIDIFIER self._device_features = FEATURE_FLAGS_AIRHUMIDIFIER
self._available_attributes = AVAILABLE_ATTRIBUTES_AIRHUMIDIFIER self._available_attributes = AVAILABLE_ATTRIBUTES_AIRHUMIDIFIER
# the speed_list attribute is deprecated, support will end with release 2021.7
self._speed_list = [ self._speed_list = [
mode.name mode.name
for mode in AirhumidifierOperationMode for mode in AirhumidifierOperationMode
if mode is not AirhumidifierOperationMode.Auto if mode is not AirhumidifierOperationMode.Auto
] ]
self._supported_features |= SUPPORT_PRESET_MODE
self._preset_modes = PRESET_MODES_AIRHUMIDIFIER
self._speed_count = 4
self._state_attrs.update( self._state_attrs.update(
{attribute: None for attribute in self._available_attributes} {attribute: None for attribute in self._available_attributes}
@ -1095,10 +1336,27 @@ class XiaomiAirHumidifier(XiaomiGenericDevice):
_LOGGER.error("Got exception while fetching the state: %s", ex) _LOGGER.error("Got exception while fetching the state: %s", ex)
@property @property
def speed_list(self) -> list: def preset_mode(self):
"""Get the list of available speeds.""" """Get the active preset mode."""
return self._speed_list if self._state:
preset_mode = AirhumidifierOperationMode(self._state_attrs[ATTR_MODE]).name
return preset_mode if preset_mode in self._preset_modes else None
return None
@property
def percentage(self):
"""Return the current percentage based speed."""
if self._state:
mode = AirhumidifierOperationMode(self._state_attrs[ATTR_MODE])
if mode in self.REVERSE_SPEED_MODE_MAPPING:
return ranged_value_to_percentage(
(1, self._speed_count), self.REVERSE_SPEED_MODE_MAPPING[mode]
)
return None
# the speed attribute is deprecated, support will end with release 2021.7
@property @property
def speed(self): def speed(self):
"""Return the current speed.""" """Return the current speed."""
@ -1107,6 +1365,37 @@ class XiaomiAirHumidifier(XiaomiGenericDevice):
return None return None
async def async_set_percentage(self, percentage: int) -> None:
"""Set the percentage of the fan.
This method is a coroutine.
"""
speed_mode = math.ceil(
percentage_to_ranged_value((1, self._speed_count), percentage)
)
if speed_mode:
await self._try_command(
"Setting operation mode of the miio device failed.",
self._device.set_mode,
AirhumidifierOperationMode(self.SPEED_MODE_MAPPING[speed_mode]),
)
async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set the preset mode of the fan.
This method is a coroutine.
"""
if preset_mode not in self.preset_modes:
_LOGGER.warning("'%s'is not a valid preset mode", preset_mode)
return
await self._try_command(
"Setting operation mode of the miio device failed.",
self._device.set_mode,
self.PRESET_MODE_MAPPING[preset_mode],
)
# the async_set_speed function is deprecated, support will end with release 2021.7
# it is added here only for compatibility with legacy speeds
async def async_set_speed(self, speed: str) -> None: async def async_set_speed(self, speed: str) -> None:
"""Set the speed of the fan.""" """Set the speed of the fan."""
if self.supported_features & SUPPORT_SET_SPEED == 0: if self.supported_features & SUPPORT_SET_SPEED == 0:
@ -1168,21 +1457,64 @@ class XiaomiAirHumidifier(XiaomiGenericDevice):
class XiaomiAirHumidifierMiot(XiaomiAirHumidifier): class XiaomiAirHumidifierMiot(XiaomiAirHumidifier):
"""Representation of a Xiaomi Air Humidifier (MiOT protocol).""" """Representation of a Xiaomi Air Humidifier (MiOT protocol)."""
MODE_MAPPING = { PRESET_MODE_MAPPING = {
AirhumidifierMiotOperationMode.Auto: "Auto",
}
REVERSE_PRESET_MODE_MAPPING = {v: k for k, v in PRESET_MODE_MAPPING.items()}
SPEED_MAPPING = {
AirhumidifierMiotOperationMode.Low: SPEED_LOW, AirhumidifierMiotOperationMode.Low: SPEED_LOW,
AirhumidifierMiotOperationMode.Mid: SPEED_MEDIUM, AirhumidifierMiotOperationMode.Mid: SPEED_MEDIUM,
AirhumidifierMiotOperationMode.High: SPEED_HIGH, AirhumidifierMiotOperationMode.High: SPEED_HIGH,
} }
REVERSE_MODE_MAPPING = {v: k for k, v in MODE_MAPPING.items()} REVERSE_SPEED_MAPPING = {v: k for k, v in SPEED_MAPPING.items()}
SPEEDS = [
AirhumidifierMiotOperationMode.Low,
AirhumidifierMiotOperationMode.Mid,
AirhumidifierMiotOperationMode.High,
]
# the speed attribute is deprecated, support will end with release 2021.7
# it is added here for compatibility
@property @property
def speed(self): def speed(self):
"""Return the current speed.""" """Return current legacy speed."""
if (
self.state
and AirhumidifierMiotOperationMode(self._state_attrs[ATTR_MODE])
in self.SPEED_MAPPING
):
return self.SPEED_MAPPING[
AirhumidifierMiotOperationMode(self._state_attrs[ATTR_MODE])
]
return None
@property
def percentage(self):
"""Return the current percentage based speed."""
if (
self.state
and AirhumidifierMiotOperationMode(self._state_attrs[ATTR_MODE])
in self.SPEEDS
):
return ranged_value_to_percentage(
(1, self.speed_count), self._state_attrs[ATTR_MODE]
)
return None
@property
def preset_mode(self):
"""Return the current preset_mode."""
if self._state: if self._state:
return self.MODE_MAPPING.get( mode = self.PRESET_MODE_MAPPING.get(
AirhumidifierMiotOperationMode(self._state_attrs[ATTR_MODE]) AirhumidifierMiotOperationMode(self._state_attrs[ATTR_MODE])
) )
if mode in self._preset_modes:
return mode
return None return None
@ -1196,12 +1528,42 @@ class XiaomiAirHumidifierMiot(XiaomiAirHumidifier):
return None return None
# the async_set_speed function is deprecated, support will end with release 2021.7
# it is added here only for compatibility with legacy speeds
async def async_set_speed(self, speed: str) -> None: async def async_set_speed(self, speed: str) -> None:
"""Set the speed of the fan.""" """Override for set async_set_speed of the super() class."""
if speed and speed in self.REVERSE_SPEED_MAPPING:
await self._try_command(
"Setting operation mode of the miio device failed.",
self._device.set_mode,
self.REVERSE_SPEED_MAPPING[speed],
)
async def async_set_percentage(self, percentage: int) -> None:
"""Set the percentage of the fan.
This method is a coroutine.
"""
mode = math.ceil(percentage_to_ranged_value((1, 3), percentage))
if mode:
await self._try_command(
"Setting operation mode of the miio device failed.",
self._device.set_mode,
AirhumidifierMiotOperationMode(mode),
)
async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set the preset mode of the fan.
This method is a coroutine.
"""
if preset_mode not in self.preset_modes:
_LOGGER.warning("'%s'is not a valid preset mode", preset_mode)
return
await self._try_command( await self._try_command(
"Setting operation mode of the miio device failed.", "Setting operation mode of the miio device failed.",
self._device.set_mode, self._device.set_mode,
self.REVERSE_MODE_MAPPING[speed], self.REVERSE_PRESET_MODE_MAPPING[preset_mode],
) )
async def async_set_led_brightness(self, brightness: int = 2): async def async_set_led_brightness(self, brightness: int = 2):
@ -1230,13 +1592,30 @@ class XiaomiAirHumidifierMiot(XiaomiAirHumidifier):
class XiaomiAirFresh(XiaomiGenericDevice): class XiaomiAirFresh(XiaomiGenericDevice):
"""Representation of a Xiaomi Air Fresh.""" """Representation of a Xiaomi Air Fresh."""
SPEED_MODE_MAPPING = {
1: AirfreshOperationMode.Silent,
2: AirfreshOperationMode.Low,
3: AirfreshOperationMode.Middle,
4: AirfreshOperationMode.Strong,
}
REVERSE_SPEED_MODE_MAPPING = {v: k for k, v in SPEED_MODE_MAPPING.items()}
PRESET_MODE_MAPPING = {
"Auto": AirfreshOperationMode.Auto,
"Interval": AirfreshOperationMode.Interval,
}
def __init__(self, name, device, entry, unique_id): def __init__(self, name, device, entry, unique_id):
"""Initialize the miio device.""" """Initialize the miio device."""
super().__init__(name, device, entry, unique_id) super().__init__(name, device, entry, unique_id)
self._device_features = FEATURE_FLAGS_AIRFRESH self._device_features = FEATURE_FLAGS_AIRFRESH
self._available_attributes = AVAILABLE_ATTRIBUTES_AIRFRESH self._available_attributes = AVAILABLE_ATTRIBUTES_AIRFRESH
# the speed_list attribute is deprecated, support will end with release 2021.7
self._speed_list = OPERATION_MODES_AIRFRESH self._speed_list = OPERATION_MODES_AIRFRESH
self._speed_count = 4
self._preset_modes = PRESET_MODES_AIRFRESH
self._state_attrs.update( self._state_attrs.update(
{attribute: None for attribute in self._available_attributes} {attribute: None for attribute in self._available_attributes}
) )
@ -1267,10 +1646,27 @@ class XiaomiAirFresh(XiaomiGenericDevice):
_LOGGER.error("Got exception while fetching the state: %s", ex) _LOGGER.error("Got exception while fetching the state: %s", ex)
@property @property
def speed_list(self) -> list: def preset_mode(self):
"""Get the list of available speeds.""" """Get the active preset mode."""
return self._speed_list if self._state:
preset_mode = AirfreshOperationMode(self._state_attrs[ATTR_MODE]).name
return preset_mode if preset_mode in self._preset_modes else None
return None
@property
def percentage(self):
"""Return the current percentage based speed."""
if self._state:
mode = AirfreshOperationMode(self._state_attrs[ATTR_MODE])
if mode in self.REVERSE_SPEED_MODE_MAPPING:
return ranged_value_to_percentage(
(1, self._speed_count), self.REVERSE_SPEED_MODE_MAPPING[mode]
)
return None
# the speed attribute is deprecated, support will end with release 2021.7
@property @property
def speed(self): def speed(self):
"""Return the current speed.""" """Return the current speed."""
@ -1279,6 +1675,37 @@ class XiaomiAirFresh(XiaomiGenericDevice):
return None return None
async def async_set_percentage(self, percentage: int) -> None:
"""Set the percentage of the fan.
This method is a coroutine.
"""
speed_mode = math.ceil(
percentage_to_ranged_value((1, self._speed_count), percentage)
)
if speed_mode:
await self._try_command(
"Setting operation mode of the miio device failed.",
self._device.set_mode,
AirfreshOperationMode(self.SPEED_MODE_MAPPING[speed_mode]),
)
async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set the preset mode of the fan.
This method is a coroutine.
"""
if preset_mode not in self.preset_modes:
_LOGGER.warning("'%s'is not a valid preset mode", preset_mode)
return
await self._try_command(
"Setting operation mode of the miio device failed.",
self._device.set_mode,
self.PRESET_MODE_MAPPING[preset_mode],
)
# the async_set_speed function is deprecated, support will end with release 2021.7
# it is added here only for compatibility with legacy speeds
async def async_set_speed(self, speed: str) -> None: async def async_set_speed(self, speed: str) -> None:
"""Set the speed of the fan.""" """Set the speed of the fan."""
if self.supported_features & SUPPORT_SET_SPEED == 0: if self.supported_features & SUPPORT_SET_SPEED == 0: