diff --git a/homeassistant/components/xiaomi_miio/__init__.py b/homeassistant/components/xiaomi_miio/__init__.py index 63fc3172253..2849b249762 100644 --- a/homeassistant/components/xiaomi_miio/__init__.py +++ b/homeassistant/components/xiaomi_miio/__init__.py @@ -8,6 +8,8 @@ import logging import async_timeout from miio import ( AirFresh, + AirFreshA1, + AirFreshT2017, AirHumidifier, AirHumidifierMiot, AirHumidifierMjjsq, @@ -48,6 +50,8 @@ from .const import ( DOMAIN, KEY_COORDINATOR, KEY_DEVICE, + MODEL_AIRFRESH_A1, + MODEL_AIRFRESH_T2017, MODEL_AIRPURIFIER_3C, MODEL_FAN_1C, MODEL_FAN_P5, @@ -310,7 +314,7 @@ 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: + elif model == MODEL_AIRPURIFIER_3C: device = AirPurifierMB4(host, token) elif model in MODELS_PURIFIER_MIOT: device = AirPurifierMiot(host, token) @@ -318,6 +322,10 @@ async def async_create_miio_device_and_coordinator( device = AirPurifier(host, token) elif model.startswith("zhimi.airfresh."): device = AirFresh(host, token) + elif model == MODEL_AIRFRESH_A1: + device = AirFreshA1(host, token) + elif model == MODEL_AIRFRESH_T2017: + device = AirFreshT2017(host, token) elif ( model in MODELS_VACUUM or model.startswith(ROBOROCK_GENERIC) diff --git a/homeassistant/components/xiaomi_miio/const.py b/homeassistant/components/xiaomi_miio/const.py index b361e8ba1b3..cc607b3f419 100644 --- a/homeassistant/components/xiaomi_miio/const.py +++ b/homeassistant/components/xiaomi_miio/const.py @@ -75,7 +75,9 @@ MODEL_AIRHUMIDIFIER_JSQ = "deerma.humidifier.jsq" MODEL_AIRHUMIDIFIER_JSQ1 = "deerma.humidifier.jsq1" MODEL_AIRHUMIDIFIER_MJJSQ = "deerma.humidifier.mjjsq" +MODEL_AIRFRESH_A1 = "dmaker.airfresh.a1" MODEL_AIRFRESH_VA2 = "zhimi.airfresh.va2" +MODEL_AIRFRESH_T2017 = "dmaker.airfresh.t2017" MODEL_FAN_1C = "dmaker.fan.1c" MODEL_FAN_P10 = "dmaker.fan.p10" @@ -129,7 +131,9 @@ MODELS_PURIFIER_MIIO = [ MODEL_AIRPURIFIER_SA2, MODEL_AIRPURIFIER_2S, MODEL_AIRPURIFIER_2H, + MODEL_AIRFRESH_A1, MODEL_AIRFRESH_VA2, + MODEL_AIRFRESH_T2017, ] MODELS_HUMIDIFIER_MIIO = [ MODEL_AIRHUMIDIFIER_V1, @@ -383,6 +387,8 @@ FEATURE_FLAGS_AIRHUMIDIFIER_CA4 = ( | FEATURE_SET_CLEAN ) +FEATURE_FLAGS_AIRFRESH_A1 = FEATURE_SET_BUZZER | FEATURE_SET_CHILD_LOCK + FEATURE_FLAGS_AIRFRESH = ( FEATURE_SET_BUZZER | FEATURE_SET_CHILD_LOCK @@ -392,6 +398,8 @@ FEATURE_FLAGS_AIRFRESH = ( | FEATURE_SET_EXTRA_FEATURES ) +FEATURE_FLAGS_AIRFRESH_T2017 = FEATURE_SET_BUZZER | FEATURE_SET_CHILD_LOCK + FEATURE_FLAGS_FAN_P5 = ( FEATURE_SET_BUZZER | FEATURE_SET_CHILD_LOCK diff --git a/homeassistant/components/xiaomi_miio/fan.py b/homeassistant/components/xiaomi_miio/fan.py index a12a8a6063b..1337aa05895 100644 --- a/homeassistant/components/xiaomi_miio/fan.py +++ b/homeassistant/components/xiaomi_miio/fan.py @@ -5,6 +5,7 @@ import logging import math from miio.airfresh import OperationMode as AirfreshOperationMode +from miio.airfresh_t2017 import OperationMode as AirfreshOperationModeT2017 from miio.airpurifier import OperationMode as AirpurifierOperationMode from miio.airpurifier_miot import OperationMode as AirpurifierMiotOperationMode from miio.fan import ( @@ -39,6 +40,8 @@ from .const import ( CONF_FLOW_TYPE, DOMAIN, FEATURE_FLAGS_AIRFRESH, + FEATURE_FLAGS_AIRFRESH_A1, + FEATURE_FLAGS_AIRFRESH_T2017, FEATURE_FLAGS_AIRPURIFIER_2S, FEATURE_FLAGS_AIRPURIFIER_3C, FEATURE_FLAGS_AIRPURIFIER_MIIO, @@ -56,6 +59,8 @@ from .const import ( FEATURE_SET_EXTRA_FEATURES, KEY_COORDINATOR, KEY_DEVICE, + MODEL_AIRFRESH_A1, + MODEL_AIRFRESH_T2017, MODEL_AIRPURIFIER_2H, MODEL_AIRPURIFIER_2S, MODEL_AIRPURIFIER_3C, @@ -97,6 +102,9 @@ ATTR_SLEEP_MODE = "sleep_mode" ATTR_USE_TIME = "use_time" ATTR_BUTTON_PRESSED = "button_pressed" +# Air Fresh A1 +ATTR_FAVORITE_SPEED = "favorite_speed" + # Map attributes to properties of the state object AVAILABLE_ATTRIBUTES_AIRPURIFIER_COMMON = { ATTR_EXTRA_FEATURES: "extra_features", @@ -153,6 +161,7 @@ PRESET_MODES_AIRPURIFIER_V3 = [ "Strong", ] PRESET_MODES_AIRFRESH = ["Auto", "Interval"] +PRESET_MODES_AIRFRESH_A1 = ["Auto", "Sleep", "Favorite"] AIRPURIFIER_SERVICE_SCHEMA = vol.Schema({vol.Optional(ATTR_ENTITY_ID): cv.entity_ids}) @@ -213,6 +222,10 @@ async def async_setup_entry( entity = XiaomiAirPurifier(name, device, config_entry, unique_id, coordinator) elif model.startswith("zhimi.airfresh."): entity = XiaomiAirFresh(name, device, config_entry, unique_id, coordinator) + elif model == MODEL_AIRFRESH_A1: + entity = XiaomiAirFreshA1(name, device, config_entry, unique_id, coordinator) + elif model == MODEL_AIRFRESH_T2017: + entity = XiaomiAirFreshT2017(name, device, config_entry, unique_id, coordinator) elif model == MODEL_FAN_P5: entity = XiaomiFanP5(name, device, config_entry, unique_id, coordinator) elif model in MODELS_FAN_MIIO: @@ -709,6 +722,89 @@ class XiaomiAirFresh(XiaomiGenericAirPurifier): ) +class XiaomiAirFreshA1(XiaomiGenericAirPurifier): + """Representation of a Xiaomi Air Fresh A1.""" + + def __init__(self, name, device, entry, unique_id, coordinator): + """Initialize the miio device.""" + super().__init__(name, device, entry, unique_id, coordinator) + self._favorite_speed = None + self._device_features = FEATURE_FLAGS_AIRFRESH_A1 + self._preset_modes = PRESET_MODES_AIRFRESH_A1 + self._supported_features = SUPPORT_SET_SPEED | SUPPORT_PRESET_MODE + + self._state = self.coordinator.data.is_on + self._mode = self.coordinator.data.mode.value + self._speed_range = (60, 150) + + @property + def operation_mode_class(self): + """Hold operation mode class.""" + return AirfreshOperationModeT2017 + + @property + def percentage(self): + """Return the current percentage based speed.""" + if self._favorite_speed is None: + return None + if self._state: + return ranged_value_to_percentage(self._speed_range, self._favorite_speed) + + return None + + async def async_set_percentage(self, percentage: int) -> None: + """Set the percentage of the fan. This method is a coroutine.""" + if percentage == 0: + await self.async_turn_off() + return + + await self.async_set_preset_mode("Favorite") + + favorite_speed = math.ceil( + percentage_to_ranged_value(self._speed_range, percentage) + ) + if not favorite_speed: + return + if await self._try_command( + "Setting fan level of the miio device failed.", + self._device.set_favorite_speed, + favorite_speed, + ): + self._favorite_speed = favorite_speed + 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.""" + if preset_mode not in self.preset_modes: + _LOGGER.warning("'%s'is not a valid preset mode", preset_mode) + return + if await self._try_command( + "Setting operation mode of the miio device failed.", + self._device.set_mode, + self.operation_mode_class[preset_mode], + ): + self._mode = self.operation_mode_class[preset_mode].value + self.async_write_ha_state() + + @callback + def _handle_coordinator_update(self): + """Fetch state from the device.""" + self._state = self.coordinator.data.is_on + self._mode = self.coordinator.data.mode.value + self._favorite_speed = getattr(self.coordinator.data, ATTR_FAVORITE_SPEED, None) + self.async_write_ha_state() + + +class XiaomiAirFreshT2017(XiaomiAirFreshA1): + """Representation of a Xiaomi Air Fresh T2017.""" + + def __init__(self, name, device, entry, unique_id, coordinator): + """Initialize the miio device.""" + super().__init__(name, device, entry, unique_id, coordinator) + self._device_features = FEATURE_FLAGS_AIRFRESH_T2017 + self._speed_range = (60, 300) + + class XiaomiGenericFan(XiaomiGenericDevice): """Representation of a generic Xiaomi Fan.""" diff --git a/homeassistant/components/xiaomi_miio/number.py b/homeassistant/components/xiaomi_miio/number.py index 8939200d107..4ffc773d1c1 100644 --- a/homeassistant/components/xiaomi_miio/number.py +++ b/homeassistant/components/xiaomi_miio/number.py @@ -18,6 +18,8 @@ from .const import ( CONF_MODEL, DOMAIN, FEATURE_FLAGS_AIRFRESH, + FEATURE_FLAGS_AIRFRESH_A1, + FEATURE_FLAGS_AIRFRESH_T2017, FEATURE_FLAGS_AIRHUMIDIFIER_CA4, FEATURE_FLAGS_AIRHUMIDIFIER_CA_AND_CB, FEATURE_FLAGS_AIRPURIFIER_2S, @@ -45,6 +47,8 @@ from .const import ( FEATURE_SET_VOLUME, KEY_COORDINATOR, KEY_DEVICE, + MODEL_AIRFRESH_A1, + MODEL_AIRFRESH_T2017, MODEL_AIRFRESH_VA2, MODEL_AIRHUMIDIFIER_CA1, MODEL_AIRHUMIDIFIER_CA4, @@ -199,7 +203,9 @@ NUMBER_TYPES = { } MODEL_TO_FEATURES_MAP = { + MODEL_AIRFRESH_A1: FEATURE_FLAGS_AIRFRESH_A1, MODEL_AIRFRESH_VA2: FEATURE_FLAGS_AIRFRESH, + MODEL_AIRFRESH_T2017: FEATURE_FLAGS_AIRFRESH_T2017, MODEL_AIRHUMIDIFIER_CA1: FEATURE_FLAGS_AIRHUMIDIFIER_CA_AND_CB, MODEL_AIRHUMIDIFIER_CA4: FEATURE_FLAGS_AIRHUMIDIFIER_CA4, MODEL_AIRHUMIDIFIER_CB1: FEATURE_FLAGS_AIRHUMIDIFIER_CA_AND_CB, diff --git a/homeassistant/components/xiaomi_miio/sensor.py b/homeassistant/components/xiaomi_miio/sensor.py index d6d1c8500ed..6095bfe5647 100644 --- a/homeassistant/components/xiaomi_miio/sensor.py +++ b/homeassistant/components/xiaomi_miio/sensor.py @@ -52,6 +52,8 @@ from .const import ( DOMAIN, KEY_COORDINATOR, KEY_DEVICE, + MODEL_AIRFRESH_A1, + MODEL_AIRFRESH_T2017, MODEL_AIRFRESH_VA2, MODEL_AIRHUMIDIFIER_CA1, MODEL_AIRHUMIDIFIER_CB1, @@ -375,6 +377,14 @@ AIRFRESH_SENSORS = ( ATTR_TEMPERATURE, ATTR_USE_TIME, ) +AIRFRESH_SENSORS_A1 = ( + ATTR_CARBON_DIOXIDE, + ATTR_TEMPERATURE, +) +AIRFRESH_SENSORS_T2017 = ( + ATTR_CARBON_DIOXIDE, + ATTR_TEMPERATURE, +) FAN_V2_V3_SENSORS = ( ATTR_BATTERY, ATTR_HUMIDITY, @@ -384,7 +394,9 @@ FAN_V2_V3_SENSORS = ( FAN_ZA5_SENSORS = (ATTR_HUMIDITY, ATTR_TEMPERATURE) MODEL_TO_SENSORS_MAP = { + MODEL_AIRFRESH_A1: AIRFRESH_SENSORS_A1, MODEL_AIRFRESH_VA2: AIRFRESH_SENSORS, + MODEL_AIRFRESH_T2017: AIRFRESH_SENSORS_T2017, MODEL_AIRHUMIDIFIER_CA1: HUMIDIFIER_CA1_CB1_SENSORS, MODEL_AIRHUMIDIFIER_CB1: HUMIDIFIER_CA1_CB1_SENSORS, MODEL_AIRPURIFIER_3C: PURIFIER_3C_SENSORS, @@ -808,7 +820,6 @@ class XiaomiGatewayIlluminanceSensor(SensorEntity): def __init__(self, gateway_device, gateway_name, gateway_device_id, description): """Initialize the entity.""" - self._attr_name = f"{gateway_name} {description.name}" self._attr_unique_id = f"{gateway_device_id}-{description.key}" self._attr_device_info = {"identifiers": {(DOMAIN, gateway_device_id)}} diff --git a/homeassistant/components/xiaomi_miio/switch.py b/homeassistant/components/xiaomi_miio/switch.py index bd6482e891b..ce05a891c0a 100644 --- a/homeassistant/components/xiaomi_miio/switch.py +++ b/homeassistant/components/xiaomi_miio/switch.py @@ -35,6 +35,8 @@ from .const import ( CONF_MODEL, DOMAIN, FEATURE_FLAGS_AIRFRESH, + FEATURE_FLAGS_AIRFRESH_A1, + FEATURE_FLAGS_AIRFRESH_T2017, FEATURE_FLAGS_AIRHUMIDIFIER, FEATURE_FLAGS_AIRHUMIDIFIER_CA4, FEATURE_FLAGS_AIRHUMIDIFIER_CA_AND_CB, @@ -63,6 +65,8 @@ from .const import ( FEATURE_SET_LED, KEY_COORDINATOR, KEY_DEVICE, + MODEL_AIRFRESH_A1, + MODEL_AIRFRESH_T2017, MODEL_AIRFRESH_VA2, MODEL_AIRHUMIDIFIER_CA1, MODEL_AIRHUMIDIFIER_CA4, @@ -167,7 +171,9 @@ SERVICE_TO_METHOD = { } MODEL_TO_FEATURES_MAP = { + MODEL_AIRFRESH_A1: FEATURE_FLAGS_AIRFRESH_A1, MODEL_AIRFRESH_VA2: FEATURE_FLAGS_AIRFRESH, + MODEL_AIRFRESH_T2017: FEATURE_FLAGS_AIRFRESH_T2017, MODEL_AIRHUMIDIFIER_CA1: FEATURE_FLAGS_AIRHUMIDIFIER_CA_AND_CB, MODEL_AIRHUMIDIFIER_CA4: FEATURE_FLAGS_AIRHUMIDIFIER_CA4, MODEL_AIRHUMIDIFIER_CB1: FEATURE_FLAGS_AIRHUMIDIFIER_CA_AND_CB,