Skip to content

Commit

Permalink
Add Rosou SS4 Ventilator (leshow.fan.ss4) support (Closes: #112)
Browse files Browse the repository at this point in the history
  • Loading branch information
syssi authored and github-actions[bot] committed Jan 16, 2021
1 parent de2fbed commit 8407059
Showing 1 changed file with 156 additions and 1 deletion.
157 changes: 156 additions & 1 deletion custom_components/xiaomi_miio_airpurifier/fan.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
Device,
DeviceException,
Fan,
FanLeshow,
FanMiot,
FanP5,
)
Expand All @@ -24,9 +25,9 @@
OperationMode as AirfreshOperationMode,
)
from miio.airfresh_t2017 import ( # pylint: disable=import-error, import-error
DisplayOrientation as AirfreshT2017DisplayOrientation,
OperationMode as AirfreshT2017OperationMode,
PtcLevel as AirfreshT2017PtcLevel,
DisplayOrientation as AirfreshT2017DisplayOrientation,
)
from miio.airhumidifier import ( # pylint: disable=import-error, import-error
LedBrightness as AirhumidifierLedBrightness,
Expand Down Expand Up @@ -57,6 +58,9 @@
MoveDirection as FanMoveDirection,
OperationMode as FanOperationMode,
)
from miio.fan_leshow import ( # pylint: disable=import-error, import-error
OperationMode as FanLeshowOperationMode,
)
import voluptuous as vol

from homeassistant.components.fan import (
Expand Down Expand Up @@ -128,6 +132,7 @@
MODEL_FAN_P9 = "dmaker.fan.p9"
MODEL_FAN_P10 = "dmaker.fan.p10"
MODEL_FAN_P11 = "dmaker.fan.p11"
MODEL_FAN_LESHOW_SS4 = "leshow.fan.ss4"

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
Expand Down Expand Up @@ -173,6 +178,7 @@
MODEL_FAN_P9,
MODEL_FAN_P10,
MODEL_FAN_P11,
MODEL_FAN_LESHOW_SS4,
]
),
vol.Optional(CONF_RETRIES, default=DEFAULT_RETRIES): cv.positive_int,
Expand Down Expand Up @@ -271,6 +277,9 @@
ATTR_SPEED_LEVEL = "speed_level"
ATTR_RAW_SPEED = "raw_speed"

# Fan Leshow SS4
ATTR_ERROR_DETECTED = "error_detected"

PURIFIER_MIOT = [MODEL_AIRPURIFIER_3, MODEL_AIRPURIFIER_3H]
HUMIDIFIER_MIOT = [MODEL_AIRHUMIDIFIER_CA4]

Expand Down Expand Up @@ -551,6 +560,15 @@
ATTR_RAW_SPEED: "speed",
}

AVAILABLE_ATTRIBUTES_FAN_LESHOW_SS4 = {
ATTR_MODE: "mode",
ATTR_SPEED: "speed",
ATTR_BUZZER: "buzzer",
ATTR_OSCILLATE: "oscillate",
ATTR_DELAY_OFF_COUNTDOWN: "delay_off_countdown",
ATTR_ERROR_DETECTED: "error_detected",
}

FAN_SPEED_LEVEL1 = "Level 1"
FAN_SPEED_LEVEL2 = "Level 2"
FAN_SPEED_LEVEL3 = "Level 3"
Expand Down Expand Up @@ -764,6 +782,8 @@
| FEATURE_SET_LED
)

FEATURE_FLAGS_FAN_LESHOW_SS4 = FEATURE_SET_BUZZER

SERVICE_SET_BUZZER_ON = "fan_set_buzzer_on"
SERVICE_SET_BUZZER_OFF = "fan_set_buzzer_off"
SERVICE_SET_FAN_LED_ON = "fan_set_led_on"
Expand Down Expand Up @@ -1011,6 +1031,9 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
elif model in [MODEL_FAN_P9, MODEL_FAN_P10, MODEL_FAN_P11]:
fan = FanMiot(host, token, model=model)
device = XiaomiFanMiot(name, fan, model, unique_id, retries)
elif model == MODEL_FAN_LESHOW_SS4:
fan = FanLeshow(host, token, model=model)
device = XiaomiFanLeshow(name, fan, model, unique_id, retries)
else:
_LOGGER.error(
"Unsupported device found! Please create an issue at "
Expand Down Expand Up @@ -2378,3 +2401,135 @@ async def async_set_delay_off(self, delay_off_countdown: int) -> None:

class XiaomiFanMiot(XiaomiFanP5):
"""Representation of a Xiaomi Pedestal Fan P9, P10, P11."""


class XiaomiFanLeshow(XiaomiGenericDevice):
"""Representation of a Xiaomi Fan Leshow SS4."""

def __init__(self, name, device, model, unique_id, retries):
"""Initialize the fan entity."""
super().__init__(name, device, model, unique_id, retries)

self._device_features = FEATURE_FLAGS_FAN_LESHOW_SS4
self._available_attributes = AVAILABLE_ATTRIBUTES_FAN_LESHOW_SS4
self._speed_list = list(FAN_SPEED_LIST)
self._speed = None
self._oscillate = None

self._state_attrs[ATTR_SPEED] = None
self._state_attrs.update(
{attribute: None for attribute in self._available_attributes}
)

@property
def supported_features(self) -> int:
"""Supported features."""
return SUPPORT_SET_SPEED | SUPPORT_OSCILLATE

async def async_update(self):
"""Fetch state from the device."""
# On state change the device doesn't provide the new state immediately.
if self._skip_update:
self._skip_update = False
return

try:
state = await self.hass.async_add_job(self._device.status)
_LOGGER.debug("Got new state: %s", state)

self._available = True
self._oscillate = state.oscillate
self._state = state.is_on

for level, range in FAN_SPEED_LIST.items():
if state.speed in range:
self._speed = level
self._state_attrs[ATTR_SPEED] = level
break

self._state_attrs.update(
{
key: self._extract_value_from_attribute(state, value)
for key, value in self._available_attributes.items()
}
)
self._retry = 0

except DeviceException as ex:
self._retry = self._retry + 1
if self._retry < self._retries:
_LOGGER.info(
"Got exception while fetching the state: %s , _retry=%s",
ex,
self._retry,
)
else:
self._available = False
_LOGGER.error(
"Got exception while fetching the state: %s , _retry=%s",
ex,
self._retry,
)

@property
def speed_list(self) -> list:
"""Get the list of available speeds."""
return self._speed_list

@property
def speed(self):
"""Return the current speed."""
return self._speed

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 fan speed to: %s", speed)

if speed.isdigit():
speed = int(speed)

if speed in [SPEED_OFF, 0]:
await self.async_turn_off()
return

# Map speed level to speed
if speed in FAN_SPEED_VALUES:
speed = FAN_SPEED_VALUES[speed]

await self._try_command(
"Setting fan speed of the miio device failed.",
self._device.set_speed,
speed,
)

@property
def oscillating(self):
"""Return the oscillation state."""
return self._oscillate

async def async_oscillate(self, oscillating: bool) -> None:
"""Set oscillation."""
if oscillating:
await self._try_command(
"Setting oscillate on of the miio device failed.",
self._device.set_oscillate,
True,
)
else:
await self._try_command(
"Setting oscillate off of the miio device failed.",
self._device.set_oscillate,
False,
)

async def async_set_delay_off(self, delay_off_countdown: int) -> None:
"""Set scheduled off timer in minutes."""

await self._try_command(
"Setting delay off miio device failed.",
self._device.delay_off,
delay_off_countdown,
)

0 comments on commit 8407059

Please sign in to comment.