Add support for pedestal MIOT fans to Xiaomi Miio integration (#56555)
* Add initial support for Xiaomi Fan ZA5 * Add sensor, number and switch platform * Addionizer switch * Improve ionizer icon * Fix parent of XiaomiFanMiot class * Add another MIOT models * Fix consts * Add powersupply attached binary sensor * Simplify async_create_miio_device_and_coordinator * Simplify XiaomiGenericFan * Fix XiaomiFanZA5 parent * Remove pass * Remove unused _available variable * 1C doesn't support direction * Suggested change * Use elif * Clean up oscillation angle * Fix typo
This commit is contained in:
parent
b179301152
commit
0044fa9fb9
7 changed files with 291 additions and 33 deletions
|
@ -11,6 +11,10 @@ from miio.fan import (
|
|||
MoveDirection as FanMoveDirection,
|
||||
OperationMode as FanOperationMode,
|
||||
)
|
||||
from miio.fan_miot import (
|
||||
OperationMode as FanMiotOperationMode,
|
||||
OperationModeFanZA5 as FanZA5OperationMode,
|
||||
)
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.fan import (
|
||||
|
@ -41,7 +45,11 @@ from .const import (
|
|||
FEATURE_FLAGS_AIRPURIFIER_PRO_V7,
|
||||
FEATURE_FLAGS_AIRPURIFIER_V3,
|
||||
FEATURE_FLAGS_FAN,
|
||||
FEATURE_FLAGS_FAN_1C,
|
||||
FEATURE_FLAGS_FAN_P5,
|
||||
FEATURE_FLAGS_FAN_P9,
|
||||
FEATURE_FLAGS_FAN_P10_P11,
|
||||
FEATURE_FLAGS_FAN_ZA5,
|
||||
FEATURE_RESET_FILTER,
|
||||
FEATURE_SET_EXTRA_FEATURES,
|
||||
KEY_COORDINATOR,
|
||||
|
@ -52,8 +60,14 @@ from .const import (
|
|||
MODEL_AIRPURIFIER_PRO,
|
||||
MODEL_AIRPURIFIER_PRO_V7,
|
||||
MODEL_AIRPURIFIER_V3,
|
||||
MODEL_FAN_1C,
|
||||
MODEL_FAN_P5,
|
||||
MODEL_FAN_P9,
|
||||
MODEL_FAN_P10,
|
||||
MODEL_FAN_P11,
|
||||
MODEL_FAN_ZA5,
|
||||
MODELS_FAN_MIIO,
|
||||
MODELS_FAN_MIOT,
|
||||
MODELS_PURIFIER_MIOT,
|
||||
SERVICE_RESET_FILTER,
|
||||
SERVICE_SET_EXTRA_FEATURES,
|
||||
|
@ -197,6 +211,10 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||
entity = XiaomiFanP5(name, device, config_entry, unique_id, coordinator)
|
||||
elif model in MODELS_FAN_MIIO:
|
||||
entity = XiaomiFan(name, device, config_entry, unique_id, coordinator)
|
||||
elif model == MODEL_FAN_ZA5:
|
||||
entity = XiaomiFanZA5(name, device, config_entry, unique_id, coordinator)
|
||||
elif model in MODELS_FAN_MIOT:
|
||||
entity = XiaomiFanMiot(name, device, config_entry, unique_id, coordinator)
|
||||
else:
|
||||
return
|
||||
|
||||
|
@ -264,6 +282,11 @@ class XiaomiGenericDevice(XiaomiCoordinatedMiioEntity, FanEntity):
|
|||
"""Flag supported features."""
|
||||
return self._supported_features
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def operation_mode_class(self):
|
||||
"""Hold operation mode class."""
|
||||
|
||||
@property
|
||||
def preset_modes(self) -> list:
|
||||
"""Get the list of available preset modes."""
|
||||
|
@ -326,11 +349,6 @@ class XiaomiGenericAirPurifier(XiaomiGenericDevice):
|
|||
|
||||
self._speed_count = 100
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def operation_mode_class(self):
|
||||
"""Hold operation mode class."""
|
||||
|
||||
@property
|
||||
def speed_count(self):
|
||||
"""Return the number of speeds of the fan supported."""
|
||||
|
@ -695,16 +713,21 @@ class XiaomiGenericFan(XiaomiGenericDevice):
|
|||
|
||||
if self._model == MODEL_FAN_P5:
|
||||
self._device_features = FEATURE_FLAGS_FAN_P5
|
||||
self._preset_modes = [mode.name for mode in FanOperationMode]
|
||||
elif self._model == MODEL_FAN_ZA5:
|
||||
self._device_features = FEATURE_FLAGS_FAN_ZA5
|
||||
elif self._model == MODEL_FAN_1C:
|
||||
self._device_features = FEATURE_FLAGS_FAN_1C
|
||||
elif self._model == MODEL_FAN_P9:
|
||||
self._device_features = FEATURE_FLAGS_FAN_P9
|
||||
elif self._model in (MODEL_FAN_P10, MODEL_FAN_P11):
|
||||
self._device_features = FEATURE_FLAGS_FAN_P10_P11
|
||||
else:
|
||||
self._device_features = FEATURE_FLAGS_FAN
|
||||
self._preset_modes = [ATTR_MODE_NATURE, ATTR_MODE_NORMAL]
|
||||
self._supported_features = (
|
||||
SUPPORT_SET_SPEED
|
||||
| SUPPORT_OSCILLATE
|
||||
| SUPPORT_PRESET_MODE
|
||||
| SUPPORT_DIRECTION
|
||||
SUPPORT_SET_SPEED | SUPPORT_OSCILLATE | SUPPORT_PRESET_MODE
|
||||
)
|
||||
if self._model != MODEL_FAN_1C:
|
||||
self._supported_features |= SUPPORT_DIRECTION
|
||||
self._preset_mode = None
|
||||
self._oscillating = None
|
||||
self._percentage = None
|
||||
|
@ -714,6 +737,11 @@ class XiaomiGenericFan(XiaomiGenericDevice):
|
|||
"""Get the active preset mode."""
|
||||
return self._preset_mode
|
||||
|
||||
@property
|
||||
def preset_modes(self) -> list:
|
||||
"""Get the list of available preset modes."""
|
||||
return [mode.name for mode in self.operation_mode_class]
|
||||
|
||||
@property
|
||||
def percentage(self):
|
||||
"""Return the current speed as a percentage."""
|
||||
|
@ -764,11 +792,20 @@ class XiaomiFan(XiaomiGenericFan):
|
|||
else:
|
||||
self._percentage = self.coordinator.data.direct_speed
|
||||
|
||||
@property
|
||||
def operation_mode_class(self):
|
||||
"""Hold operation mode class."""
|
||||
|
||||
@property
|
||||
def preset_mode(self):
|
||||
"""Get the active preset mode."""
|
||||
return ATTR_MODE_NATURE if self._nature_mode else ATTR_MODE_NORMAL
|
||||
|
||||
@property
|
||||
def preset_modes(self) -> list:
|
||||
"""Get the list of available preset modes."""
|
||||
return [ATTR_MODE_NATURE, ATTR_MODE_NORMAL]
|
||||
|
||||
@callback
|
||||
def _handle_coordinator_update(self):
|
||||
"""Fetch state from the device."""
|
||||
|
@ -843,6 +880,11 @@ class XiaomiFanP5(XiaomiGenericFan):
|
|||
self._oscillating = self.coordinator.data.oscillate
|
||||
self._percentage = self.coordinator.data.speed
|
||||
|
||||
@property
|
||||
def operation_mode_class(self):
|
||||
"""Hold operation mode class."""
|
||||
return FanOperationMode
|
||||
|
||||
@callback
|
||||
def _handle_coordinator_update(self):
|
||||
"""Fetch state from the device."""
|
||||
|
@ -861,7 +903,7 @@ class XiaomiFanP5(XiaomiGenericFan):
|
|||
await self._try_command(
|
||||
"Setting operation mode of the miio device failed.",
|
||||
self._device.set_mode,
|
||||
FanOperationMode[preset_mode],
|
||||
self.operation_mode_class[preset_mode],
|
||||
)
|
||||
self._preset_mode = preset_mode
|
||||
self.async_write_ha_state()
|
||||
|
@ -884,3 +926,71 @@ class XiaomiFanP5(XiaomiGenericFan):
|
|||
await self.async_turn_on()
|
||||
else:
|
||||
self.async_write_ha_state()
|
||||
|
||||
|
||||
class XiaomiFanMiot(XiaomiGenericFan):
|
||||
"""Representation of a Xiaomi Fan Miot."""
|
||||
|
||||
@property
|
||||
def operation_mode_class(self):
|
||||
"""Hold operation mode class."""
|
||||
return FanMiotOperationMode
|
||||
|
||||
@property
|
||||
def preset_mode(self):
|
||||
"""Get the active preset mode."""
|
||||
return self._preset_mode
|
||||
|
||||
@callback
|
||||
def _handle_coordinator_update(self):
|
||||
"""Fetch state from the device."""
|
||||
self._state = self.coordinator.data.is_on
|
||||
self._preset_mode = self.coordinator.data.mode.name
|
||||
self._oscillating = self.coordinator.data.oscillate
|
||||
if self.coordinator.data.is_on:
|
||||
self._percentage = self.coordinator.data.fan_speed
|
||||
else:
|
||||
self._percentage = 0
|
||||
|
||||
self.async_write_ha_state()
|
||||
|
||||
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
|
||||
await self._try_command(
|
||||
"Setting operation mode of the miio device failed.",
|
||||
self._device.set_mode,
|
||||
self.operation_mode_class[preset_mode],
|
||||
)
|
||||
self._preset_mode = preset_mode
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_set_percentage(self, percentage: int) -> None:
|
||||
"""Set the percentage of the fan."""
|
||||
if percentage == 0:
|
||||
self._percentage = 0
|
||||
await self.async_turn_off()
|
||||
return
|
||||
|
||||
await self._try_command(
|
||||
"Setting fan speed percentage of the miio device failed.",
|
||||
self._device.set_speed,
|
||||
percentage,
|
||||
)
|
||||
self._percentage = percentage
|
||||
|
||||
if not self.is_on:
|
||||
await self.async_turn_on()
|
||||
else:
|
||||
self.async_write_ha_state()
|
||||
|
||||
|
||||
class XiaomiFanZA5(XiaomiFanMiot):
|
||||
"""Representation of a Xiaomi Fan ZA5."""
|
||||
|
||||
@property
|
||||
def operation_mode_class(self):
|
||||
"""Hold operation mode class."""
|
||||
return FanZA5OperationMode
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue