diff --git a/homeassistant/components/xiaomi_miio/__init__.py b/homeassistant/components/xiaomi_miio/__init__.py index 97e52f84dfc..0559cab9461 100644 --- a/homeassistant/components/xiaomi_miio/__init__.py +++ b/homeassistant/components/xiaomi_miio/__init__.py @@ -46,7 +46,7 @@ _LOGGER = logging.getLogger(__name__) GATEWAY_PLATFORMS = ["alarm_control_panel", "light", "sensor", "switch"] SWITCH_PLATFORMS = ["switch"] -FAN_PLATFORMS = ["fan", "select", "sensor", "switch"] +FAN_PLATFORMS = ["fan", "number", "select", "sensor", "switch"] HUMIDIFIER_PLATFORMS = [ "binary_sensor", "humidifier", diff --git a/homeassistant/components/xiaomi_miio/fan.py b/homeassistant/components/xiaomi_miio/fan.py index c3e3bce289a..54ce701cf92 100644 --- a/homeassistant/components/xiaomi_miio/fan.py +++ b/homeassistant/components/xiaomi_miio/fan.py @@ -35,9 +35,6 @@ from .const import ( FEATURE_FLAGS_AIRPURIFIER_V3, FEATURE_RESET_FILTER, FEATURE_SET_EXTRA_FEATURES, - FEATURE_SET_FAN_LEVEL, - FEATURE_SET_FAVORITE_LEVEL, - FEATURE_SET_VOLUME, KEY_COORDINATOR, KEY_DEVICE, MODEL_AIRPURIFIER_2H, @@ -48,9 +45,6 @@ from .const import ( MODELS_PURIFIER_MIOT, SERVICE_RESET_FILTER, SERVICE_SET_EXTRA_FEATURES, - SERVICE_SET_FAN_LEVEL, - SERVICE_SET_FAVORITE_LEVEL, - SERVICE_SET_VOLUME, ) from .device import XiaomiCoordinatedMiioEntity @@ -64,9 +58,7 @@ CONF_MODEL = "model" ATTR_MODEL = "model" # Air Purifier -ATTR_FAVORITE_LEVEL = "favorite_level" ATTR_BRIGHTNESS = "brightness" -ATTR_LEVEL = "level" ATTR_FAN_LEVEL = "fan_level" ATTR_SLEEP_TIME = "sleep_time" ATTR_SLEEP_LEARN_COUNT = "sleep_mode_learn_count" @@ -74,14 +66,12 @@ ATTR_EXTRA_FEATURES = "extra_features" ATTR_FEATURES = "features" ATTR_TURBO_MODE_SUPPORTED = "turbo_mode_supported" ATTR_SLEEP_MODE = "sleep_mode" -ATTR_VOLUME = "volume" ATTR_USE_TIME = "use_time" ATTR_BUTTON_PRESSED = "button_pressed" # Map attributes to properties of the state object AVAILABLE_ATTRIBUTES_AIRPURIFIER_COMMON = { ATTR_MODE: "mode", - ATTR_FAVORITE_LEVEL: "favorite_level", ATTR_EXTRA_FEATURES: "extra_features", ATTR_TURBO_MODE_SUPPORTED: "turbo_mode_supported", ATTR_BUTTON_PRESSED: "button_pressed", @@ -98,27 +88,20 @@ AVAILABLE_ATTRIBUTES_AIRPURIFIER = { AVAILABLE_ATTRIBUTES_AIRPURIFIER_PRO = { **AVAILABLE_ATTRIBUTES_AIRPURIFIER_COMMON, ATTR_USE_TIME: "use_time", - ATTR_VOLUME: "volume", ATTR_SLEEP_TIME: "sleep_time", ATTR_SLEEP_LEARN_COUNT: "sleep_mode_learn_count", } AVAILABLE_ATTRIBUTES_AIRPURIFIER_MIOT = { ATTR_MODE: "mode", - ATTR_FAVORITE_LEVEL: "favorite_level", ATTR_USE_TIME: "use_time", - ATTR_FAN_LEVEL: "fan_level", } -AVAILABLE_ATTRIBUTES_AIRPURIFIER_PRO_V7 = { - **AVAILABLE_ATTRIBUTES_AIRPURIFIER_COMMON, - ATTR_VOLUME: "volume", -} +AVAILABLE_ATTRIBUTES_AIRPURIFIER_PRO_V7 = AVAILABLE_ATTRIBUTES_AIRPURIFIER_COMMON AVAILABLE_ATTRIBUTES_AIRPURIFIER_V3 = { # Common set isn't used here. It's a very basic version of the device. ATTR_MODE: "mode", - ATTR_VOLUME: "volume", ATTR_SLEEP_TIME: "sleep_time", ATTR_SLEEP_LEARN_COUNT: "sleep_mode_learn_count", ATTR_EXTRA_FEATURES: "extra_features", @@ -164,33 +147,12 @@ PRESET_MODES_AIRFRESH = ["Auto", "Interval"] AIRPURIFIER_SERVICE_SCHEMA = vol.Schema({vol.Optional(ATTR_ENTITY_ID): cv.entity_ids}) -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))} -) - SERVICE_SCHEMA_EXTRA_FEATURES = AIRPURIFIER_SERVICE_SCHEMA.extend( {vol.Required(ATTR_FEATURES): cv.positive_int} ) SERVICE_TO_METHOD = { SERVICE_RESET_FILTER: {"method": "async_reset_filter"}, - SERVICE_SET_FAVORITE_LEVEL: { - "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", "schema": SERVICE_SCHEMA_EXTRA_FEATURES, @@ -513,39 +475,6 @@ class XiaomiAirPurifier(XiaomiGenericDevice): self.PRESET_MODE_MAPPING[preset_mode], ) - async def async_set_favorite_level(self, level: int = 1): - """Set the favorite level.""" - if self._device_features & FEATURE_SET_FAVORITE_LEVEL == 0: - return - - await self._try_command( - "Setting the favorite level of the miio device failed.", - self._device.set_favorite_level, - 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_volume(self, volume: int = 50): - """Set the sound volume.""" - if self._device_features & FEATURE_SET_VOLUME == 0: - return - - await self._try_command( - "Setting the sound volume of the miio device failed.", - self._device.set_volume, - volume, - ) - async def async_set_extra_features(self, features: int = 1): """Set the extra features.""" if self._device_features & FEATURE_SET_EXTRA_FEATURES == 0: diff --git a/homeassistant/components/xiaomi_miio/number.py b/homeassistant/components/xiaomi_miio/number.py index 9a4961bfdf0..d68d845cc5a 100644 --- a/homeassistant/components/xiaomi_miio/number.py +++ b/homeassistant/components/xiaomi_miio/number.py @@ -12,14 +12,40 @@ from .const import ( CONF_FLOW_TYPE, CONF_MODEL, DOMAIN, + FEATURE_FLAGS_AIRFRESH, + FEATURE_FLAGS_AIRHUMIDIFIER_CA4, + FEATURE_FLAGS_AIRHUMIDIFIER_CA_AND_CB, + FEATURE_FLAGS_AIRPURIFIER_2S, + FEATURE_FLAGS_AIRPURIFIER_MIIO, + FEATURE_FLAGS_AIRPURIFIER_MIOT, + FEATURE_FLAGS_AIRPURIFIER_PRO, + FEATURE_FLAGS_AIRPURIFIER_PRO_V7, + FEATURE_FLAGS_AIRPURIFIER_V1, + FEATURE_FLAGS_AIRPURIFIER_V3, + FEATURE_SET_FAN_LEVEL, + FEATURE_SET_FAVORITE_LEVEL, FEATURE_SET_MOTOR_SPEED, + FEATURE_SET_VOLUME, KEY_COORDINATOR, KEY_DEVICE, + MODEL_AIRFRESH_VA2, + MODEL_AIRHUMIDIFIER_CA1, MODEL_AIRHUMIDIFIER_CA4, + MODEL_AIRHUMIDIFIER_CB1, + MODEL_AIRPURIFIER_2S, + MODEL_AIRPURIFIER_PRO, + MODEL_AIRPURIFIER_PRO_V7, + MODEL_AIRPURIFIER_V1, + MODEL_AIRPURIFIER_V3, + MODELS_PURIFIER_MIIO, + MODELS_PURIFIER_MIOT, ) from .device import XiaomiCoordinatedMiioEntity +ATTR_FAN_LEVEL = "fan_level" +ATTR_FAVORITE_LEVEL = "favorite_level" ATTR_MOTOR_SPEED = "motor_speed" +ATTR_VOLUME = "volume" @dataclass @@ -30,6 +56,7 @@ class XiaomiMiioNumberDescription(NumberEntityDescription): max_value: float | None = None step: float | None = None available_with_device_off: bool = True + method: str | None = None NUMBER_TYPES = { @@ -42,7 +69,47 @@ NUMBER_TYPES = { max_value=2000, step=10, available_with_device_off=False, + method="async_set_motor_speed", ), + FEATURE_SET_FAVORITE_LEVEL: XiaomiMiioNumberDescription( + key=ATTR_FAVORITE_LEVEL, + name="Favorite Level", + icon="mdi:star-cog", + min_value=0, + max_value=17, + step=1, + method="async_set_favorite_level", + ), + FEATURE_SET_FAN_LEVEL: XiaomiMiioNumberDescription( + key=ATTR_FAN_LEVEL, + name="Fan Level", + icon="mdi:fan", + min_value=1, + max_value=3, + step=1, + method="async_set_fan_level", + ), + FEATURE_SET_VOLUME: XiaomiMiioNumberDescription( + key=ATTR_VOLUME, + name="Volume", + icon="mdi:volume-high", + min_value=1, + max_value=100, + step=1, + method="async_set_volume", + ), +} + +MODEL_TO_FEATURES_MAP = { + 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, + MODEL_AIRPURIFIER_2S: FEATURE_FLAGS_AIRPURIFIER_2S, + MODEL_AIRPURIFIER_PRO: FEATURE_FLAGS_AIRPURIFIER_PRO, + MODEL_AIRPURIFIER_PRO_V7: FEATURE_FLAGS_AIRPURIFIER_PRO_V7, + MODEL_AIRPURIFIER_V1: FEATURE_FLAGS_AIRPURIFIER_V1, + MODEL_AIRPURIFIER_V3: FEATURE_FLAGS_AIRPURIFIER_V3, + MODEL_AIRFRESH_VA2: FEATURE_FLAGS_AIRFRESH, } @@ -54,25 +121,32 @@ 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_AIRHUMIDIFIER_CA4: + if model in MODEL_TO_FEATURES_MAP: + features = MODEL_TO_FEATURES_MAP[model] + elif model in MODELS_PURIFIER_MIIO: + features = FEATURE_FLAGS_AIRPURIFIER_MIIO + elif model in MODELS_PURIFIER_MIOT: + features = FEATURE_FLAGS_AIRPURIFIER_MIOT + else: return - description = NUMBER_TYPES[FEATURE_SET_MOTOR_SPEED] - entities.append( - XiaomiAirHumidifierNumber( - f"{config_entry.title} {description.name}", - device, - config_entry, - f"{description.key}_{config_entry.unique_id}", - hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR], - description, - ) - ) + for feature, description in NUMBER_TYPES.items(): + if feature & features: + entities.append( + XiaomiNumberEntity( + f"{config_entry.title} {description.name}", + device, + config_entry, + f"{description.key}_{config_entry.unique_id}", + hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR], + description, + ) + ) async_add_entities(entities) -class XiaomiAirHumidifierNumber(XiaomiCoordinatedMiioEntity, NumberEntity): +class XiaomiNumberEntity(XiaomiCoordinatedMiioEntity, NumberEntity): """Representation of a generic Xiaomi attribute selector.""" def __init__(self, name, device, entry, unique_id, coordinator, description): @@ -108,7 +182,8 @@ class XiaomiAirHumidifierNumber(XiaomiCoordinatedMiioEntity, NumberEntity): async def async_set_value(self, value): """Set an option of the miio device.""" - if await self.async_set_motor_speed(value): + method = getattr(self, self.entity_description.method) + if await method(value): self._attr_value = value self.async_write_ha_state() @@ -128,3 +203,27 @@ class XiaomiAirHumidifierNumber(XiaomiCoordinatedMiioEntity, NumberEntity): self._device.set_speed, motor_speed, ) + + async def async_set_favorite_level(self, level: int = 1): + """Set the favorite level.""" + return await self._try_command( + "Setting the favorite level of the miio device failed.", + self._device.set_favorite_level, + level, + ) + + async def async_set_fan_level(self, level: int = 1): + """Set the fan level.""" + return await self._try_command( + "Setting the favorite level of the miio device failed.", + self._device.set_fan_level, + level, + ) + + async def async_set_volume(self, volume: int = 50): + """Set the volume.""" + return await self._try_command( + "Setting the volume of the miio device failed.", + self._device.set_volume, + volume, + ) diff --git a/homeassistant/components/xiaomi_miio/services.yaml b/homeassistant/components/xiaomi_miio/services.yaml index 250b0404a41..b8f81e1a34d 100644 --- a/homeassistant/components/xiaomi_miio/services.yaml +++ b/homeassistant/components/xiaomi_miio/services.yaml @@ -1,58 +1,3 @@ -fan_set_favorite_level: - name: Fan set favorite level - description: Set the favorite level. - fields: - entity_id: - description: Name of the xiaomi miio entity. - selector: - entity: - integration: xiaomi_miio - domain: fan - level: - name: Level - description: Level. - required: true - selector: - number: - min: 0 - max: 17 - -fan_set_fan_level: - name: Fan set level - description: Set the fan level. - fields: - entity_id: - description: Name of the xiaomi miio entity. - selector: - entity: - integration: xiaomi_miio - domain: fan - level: - name: Level - description: Level. - selector: - number: - min: 1 - max: 3 - -fan_set_volume: - name: Fan set volume - description: Set the sound volume. - fields: - entity_id: - description: Name of the xiaomi miio entity. - selector: - entity: - integration: xiaomi_miio - domain: fan - volume: - description: Volume. - required: true - selector: - number: - min: 0 - max: 100 - fan_reset_filter: name: Fan reset filter description: Reset the filter lifetime and usage. @@ -83,26 +28,6 @@ fan_set_extra_features: min: 0 max: 1 -fan_set_motor_speed: - name: Fan set motor speed - description: Set the target motor speed. - fields: - entity_id: - description: Name of the xiaomi miio entity. - selector: - entity: - integration: xiaomi_miio - domain: fan - motor_speed: - name: Motor speed - description: Set motor speed. - required: true - selector: - number: - min: 200 - max: 2000 - unit_of_measurement: 'RPM' - light_set_scene: name: Light set scene description: Set a fixed scene.