Add support for Xiaomi Miio Air Purifier 3C (#55484)

This commit is contained in:
Maciej Bieniek 2021-08-31 16:44:13 +02:00 committed by GitHub
parent 4d98a7e156
commit 08a0377dcb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 131 additions and 17 deletions

View File

@ -9,6 +9,7 @@ from miio import (
AirHumidifierMiot,
AirHumidifierMjjsq,
AirPurifier,
AirPurifierMB4,
AirPurifierMiot,
DeviceException,
Fan,
@ -31,6 +32,7 @@ from .const import (
DOMAIN,
KEY_COORDINATOR,
KEY_DEVICE,
MODEL_AIRPURIFIER_3C,
MODEL_FAN_P5,
MODELS_AIR_MONITOR,
MODELS_FAN,
@ -139,6 +141,8 @@ async def async_create_miio_device_and_coordinator(
device = AirHumidifier(host, token, model=model)
migrate = True
# Airpurifiers and Airfresh
elif model in MODEL_AIRPURIFIER_3C:
device = AirPurifierMB4(host, token)
elif model in MODELS_PURIFIER_MIOT:
device = AirPurifierMiot(host, token)
elif model.startswith("zhimi.airpurifier."):

View File

@ -30,23 +30,24 @@ SERVER_COUNTRY_CODES = ["cn", "de", "i2", "ru", "sg", "us"]
DEFAULT_CLOUD_COUNTRY = "cn"
# Fan Models
MODEL_AIRPURIFIER_V1 = "zhimi.airpurifier.v1"
MODEL_AIRPURIFIER_V2 = "zhimi.airpurifier.v2"
MODEL_AIRPURIFIER_V3 = "zhimi.airpurifier.v3"
MODEL_AIRPURIFIER_V5 = "zhimi.airpurifier.v5"
MODEL_AIRPURIFIER_PRO = "zhimi.airpurifier.v6"
MODEL_AIRPURIFIER_PRO_V7 = "zhimi.airpurifier.v7"
MODEL_AIRPURIFIER_2H = "zhimi.airpurifier.mc2"
MODEL_AIRPURIFIER_2S = "zhimi.airpurifier.mc1"
MODEL_AIRPURIFIER_3 = "zhimi.airpurifier.ma4"
MODEL_AIRPURIFIER_3C = "zhimi.airpurifier.mb4"
MODEL_AIRPURIFIER_3H = "zhimi.airpurifier.mb3"
MODEL_AIRPURIFIER_M1 = "zhimi.airpurifier.m1"
MODEL_AIRPURIFIER_M2 = "zhimi.airpurifier.m2"
MODEL_AIRPURIFIER_MA1 = "zhimi.airpurifier.ma1"
MODEL_AIRPURIFIER_MA2 = "zhimi.airpurifier.ma2"
MODEL_AIRPURIFIER_PRO = "zhimi.airpurifier.v6"
MODEL_AIRPURIFIER_PROH = "zhimi.airpurifier.va1"
MODEL_AIRPURIFIER_PRO_V7 = "zhimi.airpurifier.v7"
MODEL_AIRPURIFIER_SA1 = "zhimi.airpurifier.sa1"
MODEL_AIRPURIFIER_SA2 = "zhimi.airpurifier.sa2"
MODEL_AIRPURIFIER_2S = "zhimi.airpurifier.mc1"
MODEL_AIRPURIFIER_2H = "zhimi.airpurifier.mc2"
MODEL_AIRPURIFIER_3 = "zhimi.airpurifier.ma4"
MODEL_AIRPURIFIER_3H = "zhimi.airpurifier.mb3"
MODEL_AIRPURIFIER_PROH = "zhimi.airpurifier.va1"
MODEL_AIRPURIFIER_V1 = "zhimi.airpurifier.v1"
MODEL_AIRPURIFIER_V2 = "zhimi.airpurifier.v2"
MODEL_AIRPURIFIER_V3 = "zhimi.airpurifier.v3"
MODEL_AIRPURIFIER_V5 = "zhimi.airpurifier.v5"
MODEL_AIRHUMIDIFIER_V1 = "zhimi.humidifier.v1"
MODEL_AIRHUMIDIFIER_CA1 = "zhimi.humidifier.ca1"
@ -78,6 +79,7 @@ MODELS_FAN_MIIO = [
MODELS_PURIFIER_MIOT = [
MODEL_AIRPURIFIER_3,
MODEL_AIRPURIFIER_3C,
MODEL_AIRPURIFIER_3H,
MODEL_AIRPURIFIER_PROH,
]
@ -229,6 +231,8 @@ FEATURE_SET_CLEAN = 16384
FEATURE_SET_OSCILLATION_ANGLE = 32768
FEATURE_SET_OSCILLATION_ANGLE_MAX_140 = 65536
FEATURE_SET_DELAY_OFF_COUNTDOWN = 131072
FEATURE_SET_LED_BRIGHTNESS_LEVEL = 262144
FEATURE_SET_FAVORITE_RPM = 524288
FEATURE_FLAGS_AIRPURIFIER_MIIO = (
FEATURE_SET_BUZZER
@ -248,6 +252,13 @@ FEATURE_FLAGS_AIRPURIFIER_MIOT = (
| FEATURE_SET_LED_BRIGHTNESS
)
FEATURE_FLAGS_AIRPURIFIER_3C = (
FEATURE_SET_BUZZER
| FEATURE_SET_CHILD_LOCK
| FEATURE_SET_LED_BRIGHTNESS_LEVEL
| FEATURE_SET_FAVORITE_RPM
)
FEATURE_FLAGS_AIRPURIFIER_PRO = (
FEATURE_SET_CHILD_LOCK
| FEATURE_SET_LED

View File

@ -34,6 +34,7 @@ from .const import (
DOMAIN,
FEATURE_FLAGS_AIRFRESH,
FEATURE_FLAGS_AIRPURIFIER_2S,
FEATURE_FLAGS_AIRPURIFIER_3C,
FEATURE_FLAGS_AIRPURIFIER_MIIO,
FEATURE_FLAGS_AIRPURIFIER_MIOT,
FEATURE_FLAGS_AIRPURIFIER_PRO,
@ -47,6 +48,7 @@ from .const import (
KEY_DEVICE,
MODEL_AIRPURIFIER_2H,
MODEL_AIRPURIFIER_2S,
MODEL_AIRPURIFIER_3C,
MODEL_AIRPURIFIER_PRO,
MODEL_AIRPURIFIER_PRO_V7,
MODEL_AIRPURIFIER_V3,
@ -193,7 +195,15 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR]
device = hass.data[DOMAIN][config_entry.entry_id][KEY_DEVICE]
if model in MODELS_PURIFIER_MIOT:
if model == MODEL_AIRPURIFIER_3C:
entity = XiaomiAirPurifierMB4(
name,
device,
config_entry,
unique_id,
coordinator,
)
elif model in MODELS_PURIFIER_MIOT:
entity = XiaomiAirPurifierMiot(
name,
device,
@ -567,11 +577,35 @@ class XiaomiAirPurifierMiot(XiaomiAirPurifier):
self._fan_level = fan_level
self.async_write_ha_state()
async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set the preset mode of the fan.
This method is a coroutine.
"""
class XiaomiAirPurifierMB4(XiaomiGenericDevice):
"""Representation of a Xiaomi Air Purifier MB4."""
PRESET_MODE_MAPPING = {
"Auto": AirpurifierMiotOperationMode.Auto,
"Silent": AirpurifierMiotOperationMode.Silent,
"Favorite": AirpurifierMiotOperationMode.Favorite,
}
def __init__(self, name, device, entry, unique_id, coordinator):
"""Initialize Air Purifier MB4."""
super().__init__(name, device, entry, unique_id, coordinator)
self._device_features = FEATURE_FLAGS_AIRPURIFIER_3C
self._preset_modes = list(self.PRESET_MODE_MAPPING)
self._supported_features = SUPPORT_PRESET_MODE
@property
def preset_mode(self):
"""Get the active preset mode."""
if self.coordinator.data.is_on:
preset_mode = AirpurifierMiotOperationMode(self._mode).name
return preset_mode if preset_mode in self._preset_modes else None
return None
async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set the preset mode of the fan."""
if preset_mode not in self.preset_modes:
_LOGGER.warning("'%s'is not a valid preset mode", preset_mode)
return
@ -583,6 +617,14 @@ class XiaomiAirPurifierMiot(XiaomiAirPurifier):
self._mode = self.PRESET_MODE_MAPPING[preset_mode].value
self.async_write_ha_state()
@callback
def _handle_coordinator_update(self):
"""Fetch state from the device."""
self._available = True
self._state = self.coordinator.data.is_on
self._mode = self.coordinator.data.mode.value
self.async_write_ha_state()
class XiaomiAirFresh(XiaomiGenericDevice):
"""Representation of a Xiaomi Air Fresh."""

View File

@ -17,6 +17,7 @@ from .const import (
FEATURE_FLAGS_AIRHUMIDIFIER_CA4,
FEATURE_FLAGS_AIRHUMIDIFIER_CA_AND_CB,
FEATURE_FLAGS_AIRPURIFIER_2S,
FEATURE_FLAGS_AIRPURIFIER_3C,
FEATURE_FLAGS_AIRPURIFIER_MIIO,
FEATURE_FLAGS_AIRPURIFIER_MIOT,
FEATURE_FLAGS_AIRPURIFIER_PRO,
@ -28,6 +29,8 @@ from .const import (
FEATURE_SET_DELAY_OFF_COUNTDOWN,
FEATURE_SET_FAN_LEVEL,
FEATURE_SET_FAVORITE_LEVEL,
FEATURE_SET_FAVORITE_RPM,
FEATURE_SET_LED_BRIGHTNESS_LEVEL,
FEATURE_SET_MOTOR_SPEED,
FEATURE_SET_OSCILLATION_ANGLE,
FEATURE_SET_OSCILLATION_ANGLE_MAX_140,
@ -39,6 +42,7 @@ from .const import (
MODEL_AIRHUMIDIFIER_CA4,
MODEL_AIRHUMIDIFIER_CB1,
MODEL_AIRPURIFIER_2S,
MODEL_AIRPURIFIER_3C,
MODEL_AIRPURIFIER_PRO,
MODEL_AIRPURIFIER_PRO_V7,
MODEL_AIRPURIFIER_V1,
@ -58,6 +62,8 @@ from .device import XiaomiCoordinatedMiioEntity
ATTR_DELAY_OFF_COUNTDOWN = "delay_off_countdown"
ATTR_FAN_LEVEL = "fan_level"
ATTR_FAVORITE_LEVEL = "favorite_level"
ATTR_FAVORITE_RPM = "favorite_rpm"
ATTR_LED_BRIGHTNESS_LEVEL = "led_brightness_level"
ATTR_MOTOR_SPEED = "motor_speed"
ATTR_OSCILLATION_ANGLE = "angle"
ATTR_VOLUME = "volume"
@ -143,6 +149,25 @@ NUMBER_TYPES = {
step=1,
method="async_set_delay_off_countdown",
),
FEATURE_SET_LED_BRIGHTNESS_LEVEL: XiaomiMiioNumberDescription(
key=ATTR_LED_BRIGHTNESS_LEVEL,
name="Led Brightness",
icon="mdi:brightness-6",
min_value=0,
max_value=8,
step=1,
method="async_set_led_brightness_level",
),
FEATURE_SET_FAVORITE_RPM: XiaomiMiioNumberDescription(
key=ATTR_FAVORITE_RPM,
name="Favorite Motor Speed",
icon="mdi:star-cog",
unit_of_measurement="rpm",
min_value=300,
max_value=2300,
step=10,
method="async_set_favorite_rpm",
),
}
MODEL_TO_FEATURES_MAP = {
@ -151,6 +176,7 @@ MODEL_TO_FEATURES_MAP = {
MODEL_AIRHUMIDIFIER_CA4: FEATURE_FLAGS_AIRHUMIDIFIER_CA4,
MODEL_AIRHUMIDIFIER_CB1: FEATURE_FLAGS_AIRHUMIDIFIER_CA_AND_CB,
MODEL_AIRPURIFIER_2S: FEATURE_FLAGS_AIRPURIFIER_2S,
MODEL_AIRPURIFIER_3C: FEATURE_FLAGS_AIRPURIFIER_3C,
MODEL_AIRPURIFIER_PRO: FEATURE_FLAGS_AIRPURIFIER_PRO,
MODEL_AIRPURIFIER_PRO_V7: FEATURE_FLAGS_AIRPURIFIER_PRO_V7,
MODEL_AIRPURIFIER_V1: FEATURE_FLAGS_AIRPURIFIER_V1,
@ -293,3 +319,19 @@ class XiaomiNumberEntity(XiaomiCoordinatedMiioEntity, NumberEntity):
self._device.delay_off,
delay_off_countdown * 60,
)
async def async_set_led_brightness_level(self, level: int):
"""Set the led brightness level."""
return await self._try_command(
"Setting the led brightness level of the miio device failed.",
self._device.set_led_brightness_level,
level,
)
async def async_set_favorite_rpm(self, rpm: int):
"""Set the target motor speed."""
return await self._try_command(
"Setting the favorite rpm of the miio device failed.",
self._device.set_favorite_rpm,
rpm,
)

View File

@ -23,6 +23,7 @@ from .const import (
KEY_COORDINATOR,
KEY_DEVICE,
MODEL_AIRFRESH_VA2,
MODEL_AIRPURIFIER_3C,
MODEL_AIRPURIFIER_M1,
MODEL_AIRPURIFIER_M2,
MODEL_FAN_SA1,
@ -75,6 +76,8 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
model = config_entry.data[CONF_MODEL]
device = hass.data[DOMAIN][config_entry.entry_id][KEY_DEVICE]
if model == MODEL_AIRPURIFIER_3C:
return
if model in MODELS_HUMIDIFIER_MIIO:
entity_class = XiaomiAirHumidifierSelector
elif model in MODELS_HUMIDIFIER_MIOT:

View File

@ -33,6 +33,7 @@ from homeassistant.const import (
DEVICE_CLASS_GAS,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_ILLUMINANCE,
DEVICE_CLASS_PM25,
DEVICE_CLASS_POWER,
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
@ -56,6 +57,7 @@ from .const import (
MODEL_AIRFRESH_VA2,
MODEL_AIRHUMIDIFIER_CA1,
MODEL_AIRHUMIDIFIER_CB1,
MODEL_AIRPURIFIER_3C,
MODEL_AIRPURIFIER_PRO,
MODEL_AIRPURIFIER_PRO_V7,
MODEL_AIRPURIFIER_V2,
@ -194,7 +196,7 @@ SENSOR_TYPES = {
key=ATTR_AQI,
name="PM2.5",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
icon="mdi:blur",
device_class=DEVICE_CLASS_PM25,
state_class=STATE_CLASS_MEASUREMENT,
),
ATTR_FILTER_LIFE_REMAINING: XiaomiMiioSensorDescription(
@ -268,6 +270,12 @@ PURIFIER_MIOT_SENSORS = (
ATTR_PURIFY_VOLUME,
ATTR_TEMPERATURE,
)
PURIFIER_3C_SENSORS = (
ATTR_FILTER_LIFE_REMAINING,
ATTR_FILTER_USE,
ATTR_MOTOR_SPEED,
ATTR_PM25,
)
PURIFIER_V2_SENSORS = (
ATTR_FILTER_LIFE_REMAINING,
ATTR_FILTER_USE,
@ -326,6 +334,7 @@ MODEL_TO_SENSORS_MAP = {
MODEL_AIRFRESH_VA2: AIRFRESH_SENSORS,
MODEL_AIRHUMIDIFIER_CA1: HUMIDIFIER_CA1_CB1_SENSORS,
MODEL_AIRHUMIDIFIER_CB1: HUMIDIFIER_CA1_CB1_SENSORS,
MODEL_AIRPURIFIER_3C: PURIFIER_3C_SENSORS,
MODEL_AIRPURIFIER_PRO: PURIFIER_PRO_SENSORS,
MODEL_AIRPURIFIER_PRO_V7: PURIFIER_PRO_V7_SENSORS,
MODEL_AIRPURIFIER_V2: PURIFIER_V2_SENSORS,

View File

@ -38,6 +38,7 @@ from .const import (
FEATURE_FLAGS_AIRHUMIDIFIER_CA_AND_CB,
FEATURE_FLAGS_AIRHUMIDIFIER_MJSSQ,
FEATURE_FLAGS_AIRPURIFIER_2S,
FEATURE_FLAGS_AIRPURIFIER_3C,
FEATURE_FLAGS_AIRPURIFIER_MIIO,
FEATURE_FLAGS_AIRPURIFIER_MIOT,
FEATURE_FLAGS_AIRPURIFIER_PRO,
@ -61,6 +62,7 @@ from .const import (
MODEL_AIRHUMIDIFIER_CB1,
MODEL_AIRPURIFIER_2H,
MODEL_AIRPURIFIER_2S,
MODEL_AIRPURIFIER_3C,
MODEL_AIRPURIFIER_PRO,
MODEL_AIRPURIFIER_PRO_V7,
MODEL_AIRPURIFIER_V1,
@ -158,6 +160,7 @@ MODEL_TO_FEATURES_MAP = {
MODEL_AIRHUMIDIFIER_CB1: FEATURE_FLAGS_AIRHUMIDIFIER_CA_AND_CB,
MODEL_AIRPURIFIER_2H: FEATURE_FLAGS_AIRPURIFIER_2S,
MODEL_AIRPURIFIER_2S: FEATURE_FLAGS_AIRPURIFIER_2S,
MODEL_AIRPURIFIER_3C: FEATURE_FLAGS_AIRPURIFIER_3C,
MODEL_AIRPURIFIER_PRO: FEATURE_FLAGS_AIRPURIFIER_PRO,
MODEL_AIRPURIFIER_PRO_V7: FEATURE_FLAGS_AIRPURIFIER_PRO_V7,
MODEL_AIRPURIFIER_V1: FEATURE_FLAGS_AIRPURIFIER_V1,