Add support for Mi AirPurifier 3 (#31729)

* Adding support for Mi AirPurifier 3

* Adding support for Mi AirPurifier 3H

* Enabled setting fan level for AirPurifier 3/3H

* Get rid of isinstance for MIOT AirPurifier

* Bumping xiaomi-miio dependency

* Fixed lint error

* Update manifest.json

* Fixed requirements files for 0.5.0.1

Co-authored-by: Piotr Król <Piotr@gorszyportal.pl>
pull/33586/head
Andrey Kupreychik 2020-04-03 21:32:18 +07:00 committed by GitHub
parent c0315e32bd
commit 912eb321d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 121 additions and 2 deletions

View File

@ -10,6 +10,7 @@ SERVICE_SET_CHILD_LOCK_ON = "fan_set_child_lock_on"
SERVICE_SET_CHILD_LOCK_OFF = "fan_set_child_lock_off"
SERVICE_SET_LED_BRIGHTNESS = "fan_set_led_brightness"
SERVICE_SET_FAVORITE_LEVEL = "fan_set_favorite_level"
SERVICE_SET_FAN_LEVEL = "fan_set_fan_level"
SERVICE_SET_AUTO_DETECT_ON = "fan_set_auto_detect_on"
SERVICE_SET_AUTO_DETECT_OFF = "fan_set_auto_detect_off"
SERVICE_SET_LEARN_MODE_ON = "fan_set_learn_mode_on"

View File

@ -8,6 +8,7 @@ from miio import ( # pylint: disable=import-error
AirFresh,
AirHumidifier,
AirPurifier,
AirPurifierMiot,
Device,
DeviceException,
)
@ -23,6 +24,10 @@ from miio.airpurifier import ( # pylint: disable=import-error, import-error
LedBrightness as AirpurifierLedBrightness,
OperationMode as AirpurifierOperationMode,
)
from miio.airpurifier_miot import ( # pylint: disable=import-error, import-error
LedBrightness as AirpurifierMiotLedBrightness,
OperationMode as AirpurifierMiotOperationMode,
)
import voluptuous as vol
from homeassistant.components.fan import PLATFORM_SCHEMA, SUPPORT_SET_SPEED, FanEntity
@ -48,6 +53,7 @@ from .const import (
SERVICE_SET_DRY_OFF,
SERVICE_SET_DRY_ON,
SERVICE_SET_EXTRA_FEATURES,
SERVICE_SET_FAN_LEVEL,
SERVICE_SET_FAVORITE_LEVEL,
SERVICE_SET_LEARN_MODE_OFF,
SERVICE_SET_LEARN_MODE_ON,
@ -77,6 +83,8 @@ MODEL_AIRPURIFIER_MA2 = "zhimi.airpurifier.ma2"
MODEL_AIRPURIFIER_SA1 = "zhimi.airpurifier.sa1"
MODEL_AIRPURIFIER_SA2 = "zhimi.airpurifier.sa2"
MODEL_AIRPURIFIER_2S = "zhimi.airpurifier.mc1"
MODEL_AIRPURIFIER_3 = "zhimi.airpurifier.ma4"
MODEL_AIRPURIFIER_3H = "zhimi.airpurifier.mb3"
MODEL_AIRHUMIDIFIER_V1 = "zhimi.humidifier.v1"
MODEL_AIRHUMIDIFIER_CA1 = "zhimi.humidifier.ca1"
@ -104,6 +112,8 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
MODEL_AIRPURIFIER_SA1,
MODEL_AIRPURIFIER_SA2,
MODEL_AIRPURIFIER_2S,
MODEL_AIRPURIFIER_3,
MODEL_AIRPURIFIER_3H,
MODEL_AIRHUMIDIFIER_V1,
MODEL_AIRHUMIDIFIER_CA1,
MODEL_AIRHUMIDIFIER_CB1,
@ -131,6 +141,7 @@ ATTR_AVERAGE_AIR_QUALITY_INDEX = "average_aqi"
ATTR_PURIFY_VOLUME = "purify_volume"
ATTR_BRIGHTNESS = "brightness"
ATTR_LEVEL = "level"
ATTR_FAN_LEVEL = "fan_level"
ATTR_MOTOR2_SPEED = "motor2_speed"
ATTR_ILLUMINANCE = "illuminance"
ATTR_FILTER_RFID_PRODUCT_ID = "filter_rfid_product_id"
@ -154,13 +165,15 @@ ATTR_TRANS_LEVEL = "trans_level"
ATTR_HARDWARE_VERSION = "hardware_version"
# Air Humidifier CA
ATTR_MOTOR_SPEED = "motor_speed"
# ATTR_MOTOR_SPEED = "motor_speed"
ATTR_DEPTH = "depth"
ATTR_DRY = "dry"
# Air Fresh
ATTR_CO2 = "co2"
PURIFIER_MIOT = [MODEL_AIRPURIFIER_3, MODEL_AIRPURIFIER_3H]
# Map attributes to properties of the state object
AVAILABLE_ATTRIBUTES_AIRPURIFIER_COMMON = {
ATTR_TEMPERATURE: "temperature",
@ -227,6 +240,28 @@ AVAILABLE_ATTRIBUTES_AIRPURIFIER_2S = {
ATTR_ILLUMINANCE: "illuminance",
}
AVAILABLE_ATTRIBUTES_AIRPURIFIER_3 = {
ATTR_TEMPERATURE: "temperature",
ATTR_HUMIDITY: "humidity",
ATTR_AIR_QUALITY_INDEX: "aqi",
ATTR_MODE: "mode",
ATTR_FILTER_HOURS_USED: "filter_hours_used",
ATTR_FILTER_LIFE: "filter_life_remaining",
ATTR_FAVORITE_LEVEL: "favorite_level",
ATTR_CHILD_LOCK: "child_lock",
ATTR_LED: "led",
ATTR_MOTOR_SPEED: "motor_speed",
ATTR_AVERAGE_AIR_QUALITY_INDEX: "average_aqi",
ATTR_PURIFY_VOLUME: "purify_volume",
ATTR_USE_TIME: "use_time",
ATTR_BUZZER: "buzzer",
ATTR_LED_BRIGHTNESS: "led_brightness",
ATTR_FILTER_RFID_PRODUCT_ID: "filter_rfid_product_id",
ATTR_FILTER_RFID_TAG: "filter_rfid_tag",
ATTR_FILTER_TYPE: "filter_type",
ATTR_FAN_LEVEL: "fan_level",
}
AVAILABLE_ATTRIBUTES_AIRPURIFIER_V3 = {
# Common set isn't used here. It's a very basic version of the device.
ATTR_AIR_QUALITY_INDEX: "aqi",
@ -302,6 +337,7 @@ OPERATION_MODES_AIRPURIFIER = ["Auto", "Silent", "Favorite", "Idle"]
OPERATION_MODES_AIRPURIFIER_PRO = ["Auto", "Silent", "Favorite"]
OPERATION_MODES_AIRPURIFIER_PRO_V7 = OPERATION_MODES_AIRPURIFIER_PRO
OPERATION_MODES_AIRPURIFIER_2S = ["Auto", "Silent", "Favorite"]
OPERATION_MODES_AIRPURIFIER_3 = ["Auto", "Silent", "Favorite", "Fan"]
OPERATION_MODES_AIRPURIFIER_V3 = [
"Auto",
"Silent",
@ -327,6 +363,7 @@ FEATURE_RESET_FILTER = 256
FEATURE_SET_EXTRA_FEATURES = 512
FEATURE_SET_TARGET_HUMIDITY = 1024
FEATURE_SET_DRY = 2048
FEATURE_SET_FAN_LEVEL = 4096
FEATURE_FLAGS_AIRPURIFIER = (
FEATURE_SET_BUZZER
@ -361,6 +398,15 @@ FEATURE_FLAGS_AIRPURIFIER_2S = (
| FEATURE_SET_FAVORITE_LEVEL
)
FEATURE_FLAGS_AIRPURIFIER_3 = (
FEATURE_SET_BUZZER
| FEATURE_SET_CHILD_LOCK
| FEATURE_SET_LED
| FEATURE_SET_FAVORITE_LEVEL
| FEATURE_SET_FAN_LEVEL
| FEATURE_SET_LED_BRIGHTNESS
)
FEATURE_FLAGS_AIRPURIFIER_V3 = (
FEATURE_SET_BUZZER | FEATURE_SET_CHILD_LOCK | FEATURE_SET_LED
)
@ -394,6 +440,10 @@ SERVICE_SCHEMA_FAVORITE_LEVEL = AIRPURIFIER_SERVICE_SCHEMA.extend(
{vol.Required(ATTR_LEVEL): vol.All(vol.Coerce(int), vol.Clamp(min=0, max=17))}
)
SERVICE_SCHEMA_FAN_LEVEL = AIRPURIFIER_SERVICE_SCHEMA.extend(
{vol.Required(ATTR_LEVEL): vol.All(vol.Coerce(int), vol.Clamp(min=1, max=3))}
)
SERVICE_SCHEMA_VOLUME = AIRPURIFIER_SERVICE_SCHEMA.extend(
{vol.Required(ATTR_VOLUME): vol.All(vol.Coerce(int), vol.Clamp(min=0, max=100))}
)
@ -430,6 +480,10 @@ SERVICE_TO_METHOD = {
"method": "async_set_favorite_level",
"schema": SERVICE_SCHEMA_FAVORITE_LEVEL,
},
SERVICE_SET_FAN_LEVEL: {
"method": "async_set_fan_level",
"schema": SERVICE_SCHEMA_FAN_LEVEL,
},
SERVICE_SET_VOLUME: {"method": "async_set_volume", "schema": SERVICE_SCHEMA_VOLUME},
SERVICE_SET_EXTRA_FEATURES: {
"method": "async_set_extra_features",
@ -472,7 +526,10 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
except DeviceException:
raise PlatformNotReady
if model.startswith("zhimi.airpurifier."):
if model in PURIFIER_MIOT:
air_purifier = AirPurifierMiot(host, token)
device = XiaomiAirPurifierMiot(name, air_purifier, model, unique_id)
elif model.startswith("zhimi.airpurifier."):
air_purifier = AirPurifier(host, token)
device = XiaomiAirPurifier(name, air_purifier, model, unique_id)
elif model.startswith("zhimi.humidifier."):
@ -690,6 +747,10 @@ class XiaomiAirPurifier(XiaomiGenericDevice):
self._device_features = FEATURE_FLAGS_AIRPURIFIER_2S
self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER_2S
self._speed_list = OPERATION_MODES_AIRPURIFIER_2S
elif self._model == MODEL_AIRPURIFIER_3 or self._model == MODEL_AIRPURIFIER_3H:
self._device_features = FEATURE_FLAGS_AIRPURIFIER_3
self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER_3
self._speed_list = OPERATION_MODES_AIRPURIFIER_3
elif self._model == MODEL_AIRPURIFIER_V3:
self._device_features = FEATURE_FLAGS_AIRPURIFIER_V3
self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER_V3
@ -795,6 +856,17 @@ class XiaomiAirPurifier(XiaomiGenericDevice):
level,
)
async def async_set_fan_level(self, level: int = 1):
"""Set the favorite level."""
if self._device_features & FEATURE_SET_FAN_LEVEL == 0:
return
await self._try_command(
"Setting the fan level of the miio device failed.",
self._device.set_fan_level,
level,
)
async def async_set_auto_detect_on(self):
"""Turn the auto detect on."""
if self._device_features & FEATURE_SET_AUTO_DETECT == 0:
@ -872,6 +944,42 @@ class XiaomiAirPurifier(XiaomiGenericDevice):
)
class XiaomiAirPurifierMiot(XiaomiAirPurifier):
"""Representation of a Xiaomi Air Purifier (MiOT protocol)."""
@property
def speed(self):
"""Return the current speed."""
if self._state:
return AirpurifierMiotOperationMode(self._state_attrs[ATTR_MODE]).name
return None
async def async_set_speed(self, speed: str) -> None:
"""Set the speed of the fan."""
if self.supported_features & SUPPORT_SET_SPEED == 0:
return
_LOGGER.debug("Setting the operation mode to: %s", speed)
await self._try_command(
"Setting operation mode of the miio device failed.",
self._device.set_mode,
AirpurifierMiotOperationMode[speed.title()],
)
async def async_set_led_brightness(self, brightness: int = 2):
"""Set the led brightness."""
if self._device_features & FEATURE_SET_LED_BRIGHTNESS == 0:
return
await self._try_command(
"Setting the led brightness of the miio device failed.",
self._device.set_led_brightness,
AirpurifierMiotLedBrightness(brightness),
)
class XiaomiAirHumidifier(XiaomiGenericDevice):
"""Representation of a Xiaomi Air Humidifier."""

View File

@ -50,6 +50,16 @@ fan_set_favorite_level:
description: Level, between 0 and 16.
example: 1
fan_set_fan_level:
description: Set the fan level.
fields:
entity_id:
description: Name of the xiaomi miio entity.
example: 'fan.xiaomi_miio_device'
level:
description: Level, between 1 and 3.
example: 1
fan_set_led_brightness:
description: Set the led brightness.
fields: