diff --git a/homeassistant/components/xiaomi_miio/const.py b/homeassistant/components/xiaomi_miio/const.py index 54dd684f6b1..aaeaf19c5f9 100644 --- a/homeassistant/components/xiaomi_miio/const.py +++ b/homeassistant/components/xiaomi_miio/const.py @@ -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" diff --git a/homeassistant/components/xiaomi_miio/fan.py b/homeassistant/components/xiaomi_miio/fan.py index 7cb45296506..bd5a0c72047 100644 --- a/homeassistant/components/xiaomi_miio/fan.py +++ b/homeassistant/components/xiaomi_miio/fan.py @@ -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.""" diff --git a/homeassistant/components/xiaomi_miio/services.yaml b/homeassistant/components/xiaomi_miio/services.yaml index 36dcbc950be..4306ce4e719 100644 --- a/homeassistant/components/xiaomi_miio/services.yaml +++ b/homeassistant/components/xiaomi_miio/services.yaml @@ -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: