Skip to content

Commit

Permalink
Add Xiaomi Smartmi Fresh Air System support (#18097)
Browse files Browse the repository at this point in the history
* Add Xiaomi Air Fresh VA2 support

* Add LED property again (available now)
  • Loading branch information
syssi authored and rytilahti committed Nov 5, 2018
1 parent 3366d2c commit d951ed4
Showing 1 changed file with 161 additions and 9 deletions.
170 changes: 161 additions & 9 deletions homeassistant/components/fan/xiaomi_miio.py
Expand Up @@ -11,10 +11,10 @@

import voluptuous as vol

from homeassistant.components.fan import (
DOMAIN, PLATFORM_SCHEMA, SUPPORT_SET_SPEED, FanEntity)
from homeassistant.const import (
ATTR_ENTITY_ID, CONF_HOST, CONF_NAME, CONF_TOKEN)
from homeassistant.components.fan import (FanEntity, PLATFORM_SCHEMA,
SUPPORT_SET_SPEED, DOMAIN, )
from homeassistant.const import (CONF_NAME, CONF_HOST, CONF_TOKEN,
ATTR_ENTITY_ID, )
from homeassistant.exceptions import PlatformNotReady
import homeassistant.helpers.config_validation as cv

Expand All @@ -30,6 +30,7 @@
MODEL_AIRPURIFIER_V3 = 'zhimi.airpurifier.v3'
MODEL_AIRHUMIDIFIER_V1 = 'zhimi.humidifier.v1'
MODEL_AIRHUMIDIFIER_CA = 'zhimi.humidifier.ca1'
MODEL_AIRFRESH_VA2 = 'zhimi.airfresh.va2'

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string,
Expand All @@ -48,7 +49,8 @@
'zhimi.airpurifier.v5',
'zhimi.airpurifier.v6',
'zhimi.humidifier.v1',
'zhimi.humidifier.ca1']),
'zhimi.humidifier.ca1',
'zhimi.airfresh.va2']),
})

ATTR_MODEL = 'model'
Expand Down Expand Up @@ -97,6 +99,9 @@
ATTR_DEPTH = 'depth'
ATTR_DRY = 'dry'

# Air Fresh
ATTR_CO2 = 'co2'

# Map attributes to properties of the state object
AVAILABLE_ATTRIBUTES_AIRPURIFIER_COMMON = {
ATTR_TEMPERATURE: 'temperature',
Expand Down Expand Up @@ -166,31 +171,55 @@
ATTR_BUTTON_PRESSED: 'button_pressed',
}

AVAILABLE_ATTRIBUTES_AIRHUMIDIFIER = {
AVAILABLE_ATTRIBUTES_AIRHUMIDIFIER_COMMON = {
ATTR_TEMPERATURE: 'temperature',
ATTR_HUMIDITY: 'humidity',
ATTR_MODE: 'mode',
ATTR_BUZZER: 'buzzer',
ATTR_CHILD_LOCK: 'child_lock',
ATTR_TRANS_LEVEL: 'trans_level',
ATTR_TARGET_HUMIDITY: 'target_humidity',
ATTR_LED_BRIGHTNESS: 'led_brightness',
ATTR_BUTTON_PRESSED: 'button_pressed',
ATTR_USE_TIME: 'use_time',
ATTR_HARDWARE_VERSION: 'hardware_version',
}

AVAILABLE_ATTRIBUTES_AIRHUMIDIFIER = {
**AVAILABLE_ATTRIBUTES_AIRHUMIDIFIER_COMMON,
ATTR_TRANS_LEVEL: 'trans_level',
ATTR_BUTTON_PRESSED: 'button_pressed',
}

AVAILABLE_ATTRIBUTES_AIRHUMIDIFIER_CA = {
**AVAILABLE_ATTRIBUTES_AIRHUMIDIFIER,
**AVAILABLE_ATTRIBUTES_AIRHUMIDIFIER_COMMON,
ATTR_SPEED: 'speed',
ATTR_DEPTH: 'depth',
ATTR_DRY: 'dry',
}

AVAILABLE_ATTRIBUTES_AIRFRESH = {
ATTR_TEMPERATURE: 'temperature',
ATTR_AIR_QUALITY_INDEX: 'aqi',
ATTR_AVERAGE_AIR_QUALITY_INDEX: 'average_aqi',
ATTR_CO2: 'co2',
ATTR_HUMIDITY: 'humidity',
ATTR_MODE: 'mode',
ATTR_LED: 'led',
ATTR_LED_BRIGHTNESS: 'led_brightness',
ATTR_BUZZER: 'buzzer',
ATTR_CHILD_LOCK: 'child_lock',
ATTR_FILTER_LIFE: 'filter_life_remaining',
ATTR_FILTER_HOURS_USED: 'filter_hours_used',
ATTR_USE_TIME: 'use_time',
ATTR_MOTOR_SPEED: 'motor_speed',
ATTR_EXTRA_FEATURES: 'extra_features',
}

OPERATION_MODES_AIRPURIFIER = ['Auto', 'Silent', 'Favorite', 'Idle']
OPERATION_MODES_AIRPURIFIER_PRO = ['Auto', 'Silent', 'Favorite']
OPERATION_MODES_AIRPURIFIER_V3 = ['Auto', 'Silent', 'Favorite', 'Idle',
'Medium', 'High', 'Strong']
OPERATION_MODES_AIRFRESH = ['Auto', 'Silent', 'Interval', 'Low',
'Middle', 'Strong']

SUCCESS = ['ok']

Expand Down Expand Up @@ -234,6 +263,12 @@
FEATURE_FLAGS_AIRHUMIDIFIER_CA = (FEATURE_FLAGS_AIRHUMIDIFIER |
FEATURE_SET_DRY)

FEATURE_FLAGS_AIRFRESH = (FEATURE_FLAGS_GENERIC |
FEATURE_SET_LED |
FEATURE_SET_LED_BRIGHTNESS |
FEATURE_RESET_FILTER |
FEATURE_SET_EXTRA_FEATURES)

SERVICE_SET_BUZZER_ON = 'xiaomi_miio_set_buzzer_on'
SERVICE_SET_BUZZER_OFF = 'xiaomi_miio_set_buzzer_off'
SERVICE_SET_LED_ON = 'xiaomi_miio_set_led_on'
Expand Down Expand Up @@ -350,6 +385,10 @@ async def async_setup_platform(hass, config, async_add_entities,
from miio import AirHumidifier
air_humidifier = AirHumidifier(host, token, model=model)
device = XiaomiAirHumidifier(name, air_humidifier, model, unique_id)
elif model.startswith('zhimi.airfresh.'):
from miio import AirFresh
air_fresh = AirFresh(host, token)
device = XiaomiAirFresh(name, air_fresh, model, unique_id)
else:
_LOGGER.error(
'Unsupported device found! Please create an issue at '
Expand Down Expand Up @@ -812,3 +851,116 @@ async def async_set_dry_off(self):
await self._try_command(
"Turning the dry mode of the miio device off failed.",
self._device.set_dry, False)


class XiaomiAirFresh(XiaomiGenericDevice):
"""Representation of a Xiaomi Air Fresh."""

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

self._device_features = FEATURE_FLAGS_AIRFRESH
self._available_attributes = AVAILABLE_ATTRIBUTES_AIRFRESH
self._speed_list = OPERATION_MODES_AIRFRESH
self._state_attrs.update(
{attribute: None for attribute in self._available_attributes})

async def async_update(self):
"""Fetch state from the device."""
from miio import DeviceException

# 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._state = state.is_on
self._state_attrs.update(
{key: self._extract_value_from_attribute(state, value) for
key, value in self._available_attributes.items()})

except DeviceException as ex:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)

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

@property
def speed(self):
"""Return the current speed."""
if self._state:
from miio.airfresh import OperationMode

return OperationMode(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

from miio.airfresh import OperationMode

_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, OperationMode[speed.title()])

async def async_set_led_on(self):
"""Turn the led on."""
if self._device_features & FEATURE_SET_LED == 0:
return

await self._try_command(
"Turning the led of the miio device off failed.",
self._device.set_led, True)

async def async_set_led_off(self):
"""Turn the led off."""
if self._device_features & FEATURE_SET_LED == 0:
return

await self._try_command(
"Turning the led of the miio device off failed.",
self._device.set_led, False)

async def async_set_led_brightness(self, brightness: int = 2):
"""Set the led brightness."""
if self._device_features & FEATURE_SET_LED_BRIGHTNESS == 0:
return

from miio.airfresh import LedBrightness

await self._try_command(
"Setting the led brightness of the miio device failed.",
self._device.set_led_brightness, LedBrightness(brightness))

async def async_set_extra_features(self, features: int = 1):
"""Set the extra features."""
if self._device_features & FEATURE_SET_EXTRA_FEATURES == 0:
return

await self._try_command(
"Setting the extra features of the miio device failed.",
self._device.set_extra_features, features)

async def async_reset_filter(self):
"""Reset the filter lifetime and usage."""
if self._device_features & FEATURE_RESET_FILTER == 0:
return

await self._try_command(
"Resetting the filter lifetime of the miio device failed.",
self._device.reset_filter)

0 comments on commit d951ed4

Please sign in to comment.