Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Additional properties of the Xiaomi Air Purifier 2 introduced #132

Merged
merged 11 commits into from
Nov 26, 2017
110 changes: 82 additions & 28 deletions miio/airpurifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
_LOGGER = logging.getLogger(__name__)


class AirPurifierException(Exception):
pass


class OperationMode(enum.Enum):
Auto = 'auto'
Silent = 'silent'
Expand Down Expand Up @@ -36,14 +40,14 @@ def __init__(self, data: Dict[str, Any]) -> None:

Response of a Air Purifier 2:

['power': 'off', 'aqi': 141, 'humidity': 64, 'temp_dec': 236,
'mode': 'auto', 'led': 'on', 'led_b': 1, 'buzzer': 'on',
'child_lock': 'off', 'limit_hum': null, 'trans_level': null,
'bright': null, 'favorite_level': 10, 'filter1_life': 80,
'act_det': null, 'f1_hour_used': 680 ]
{'power': 'on, 'aqi': 10, 'average_aqi': 8, 'humidity': 62,
'temp_dec': 186, 'mode': 'auto', 'favorite_level': 10,
'filter1_life': 80, 'f1_hour_used': 682, 'use_time': 2457000,
'motor1_speed': 354, 'purify_volume': 25262, 'f1_hour': 3500,
'led': 'off', 'led_b': 2, 'bright': None, 'buzzer': 'off',
'child_lock': 'off'}

use_time and motor1_speed is missing because a request is limitted
to 16 properties. We request 15 properties at the moment.
A request is limited to 16 properties.
"""

self.data = data
Expand All @@ -63,6 +67,11 @@ def aqi(self) -> int:
"""Air quality index."""
return self.data["aqi"]

@property
def average_aqi(self) -> int:
"""Average of the air quality index."""
return self.data["average_aqi"]

@property
def humidity(self) -> int:
"""Current humidity."""
Expand Down Expand Up @@ -90,6 +99,11 @@ def led_brightness(self) -> Optional[LedBrightness]:
"""Brightness of the LED."""
if self.data["led_b"] is not None:
return LedBrightness(self.data["led_b"])

# This is the property name of the Air Purifier Pro
if self.data["bright"] is not None:
return LedBrightness(self.data["bright"])
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean that when the device is not air purifier pro, the "led_b" will be empty? If yes, then it's fine.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. The air purifier provides a "bright" property XOR a "led_b" property.


return None

@property
Expand All @@ -102,11 +116,6 @@ def child_lock(self) -> bool:
"""Return True if child lock is on."""
return self.data["child_lock"] == "on"

@property
def brightness(self) -> int:
"""Return brightness."""
return self.data["bright"]

@property
def favorite_level(self) -> int:
"""Return favorite level, which is used if the mode is ``favorite``."""
Expand All @@ -125,24 +134,51 @@ def filter_hours_used(self) -> int:

@property
def use_time(self) -> int:
"""How long the device has been active FIXME"""
"""How long the device has been active in seconds."""
return self.data["use_time"]

@property
def purify_volume(self) -> int:
"""The volume of purified air in cubic meter."""
return self.data["purify_volume"]

@property
def motor_speed(self) -> int:
"""Speed of the motor."""
return self.data["motor1_speed"]

def __str__(self) -> str:
s = "<AirPurifierStatus power=%s, aqi=%s temperature=%s, " \
"humidity=%s%%, mode=%s, led=%s, led_brightness=%s, buzzer=%s, " \
"child_lock=%s, brightness=%s, favorite_level=%s, " \
"filter_life_remaining=%s, filter_hours_used=%s, " \
"use_time=%s, motor_speed=%s>" % \
(self.power, self.aqi, self.temperature, self.humidity, self.mode,
self.led, self.led_brightness, self.buzzer, self.child_lock,
self.brightness, self.favorite_level, self.filter_life_remaining,
self.filter_hours_used, self.use_time,
def __repr__(self) -> str:
s = "<AirPurifierStatus power=%s, " \
"aqi=%s," \
"average_aqi=%s," \
"temperature=%s, " \
"humidity=%s%%," \
"mode=%s," \
"led=%s," \
"led_brightness=%s," \
"buzzer=%s, " \
"child_lock=%s," \
"favorite_level=%s," \
"filter_life_remaining=%s, " \
"filter_hours_used=%s, " \
"use_time=%s, " \
"purify_volume=%s, " \
"motor_speed=%s>" % \
(self.power,
self.aqi,
self.average_aqi,
self.temperature,
self.humidity,
self.mode,
self.led,
self.led_brightness,
self.buzzer,
self.child_lock,
self.favorite_level,
self.filter_life_remaining,
self.filter_hours_used,
self.use_time,
self.purify_volume,
self.motor_speed)
return s

Expand All @@ -153,16 +189,25 @@ class AirPurifier(Device):
def status(self) -> AirPurifierStatus:
"""Retrieve properties."""

properties = ['power', 'aqi', 'humidity', 'temp_dec',
'mode', 'led', 'led_b', 'buzzer', 'child_lock',
'bright', 'favorite_level', 'filter1_life',
'f1_hour_used', 'use_time', 'motor1_speed']
properties = ['power', 'aqi', 'average_aqi', 'humidity', 'temp_dec',
'mode', 'favorite_level', 'filter1_life', 'f1_hour_used',
'use_time', 'motor1_speed', 'purify_volume', 'f1_hour',
# Second request
'led', 'led_b', 'bright', 'buzzer', 'child_lock', ]

# A single request is limited to 16 properties. Therefore the
# properties are divided in two groups here. The second group contains
# some infrequent and independent updated properties.
values = self.send(
"get_prop",
properties
properties[0:13]
)

values.extend(self.send(
"get_prop",
properties[13:]
))

properties_count = len(properties)
values_count = len(values)
if properties_count != values_count:
Expand All @@ -188,6 +233,8 @@ def set_mode(self, mode: OperationMode):

def set_favorite_level(self, level: int):
"""Set favorite level."""
if level < 0 or level > 16:
raise AirPurifierException("Invalid favorite level: %s" % level)

# Set the favorite level used when the mode is `favorite`,
# should be between 0 and 16.
Expand All @@ -210,3 +257,10 @@ def set_buzzer(self, buzzer: bool):
return self.send("set_buzzer", ["on"])
else:
return self.send("set_buzzer", ["off"])

def set_child_lock(self, lock: bool):
"""Set child lock on/off."""
if lock:
return self.send("set_child_lock", ["on"])
else:
return self.send("set_child_lock", ["off"])
10 changes: 5 additions & 5 deletions miio/ceil.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ def brightness(self) -> int:

@property
def scene(self) -> int:
"""Current scene. FIXME what is this?"""
"""Current fixed scene (brightness & colortemp)."""
return self.data["snm"]

@property
def delay_off_countdown(self) -> int:
"""Countdown until turning off."""
"""Countdown until turning off in seconds."""
return self.data["dv"]

@property
Expand All @@ -61,7 +61,7 @@ def automatic_color_temperature(self) -> bool:
"""Automatic color temperature state."""
return self.data["ac"] == 1

def __str__(self) -> str:
def __repr__(self) -> str:
s = "<CeilStatus power=%s, brightness=%s, " \
"color_temperature=%s, scene=%s, delay_off_countdown=%s, " \
"smart_night_light=%s, automatic_color_temperature=%s>" % \
Expand Down Expand Up @@ -118,7 +118,7 @@ def set_color_temperature(self, level: int):
return self.send("set_cct", [level])

def delay_off(self, seconds: int):
"""Set delay off seconds."""
"""Turn off delay in seconds."""

if seconds < 1:
raise CeilException(
Expand All @@ -127,7 +127,7 @@ def delay_off(self, seconds: int):
return self.send("delay_off", [seconds])

def set_scene(self, number: int):
"""Set scene number."""
"""Set a fixed scene. 4 fixed scenes are available (1-4)"""
if number < 1 or number > 4:
raise CeilException("Invalid fixed scene number: %s" % number)

Expand Down
7 changes: 4 additions & 3 deletions miio/fan.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,12 @@ def child_lock(self) -> bool:

@property
def natural_level(self) -> int:
"""Natural level. FIXME what is this?"""
"""Fan speed in natural mode."""
return self.data["natural_level"]

@property
def speed_level(self) -> int:
"""Speed level. FIXME how does this compare to speed?"""
"""Fan speed in direct mode."""
return self.data["speed_level"]

@property
Expand All @@ -105,7 +105,8 @@ def poweroff_time(self) -> int:

@property
def speed(self) -> int:
"""Current speed. FIXME how does this compare to speed_level?"""
"""FIXME What is the meaning of this value?
(cp. speed_level vs. natural_level)"""
return self.data["speed"]

@property
Expand Down
2 changes: 1 addition & 1 deletion miio/philips_bulb.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def scene(self) -> int:
def delay_off_countdown(self) -> int:
return self.data["dv"]

def __str__(self) -> str:
def __repr__(self) -> str:
s = "<PhilipsBulbStatus power=%s, brightness=%s, " \
"color_temperature=%s, scene=%s, delay_off_countdown=%s>" % \
(self.power, self.brightness,
Expand Down
36 changes: 18 additions & 18 deletions miio/philips_eyecare.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,40 +36,40 @@ def brightness(self) -> int:

@property
def reminder(self) -> bool:
"""True if reminder is on. FIXME be more descriptive"""
"""Indicates the eye fatigue notification is enabled or not."""
return self.data["notifystatus"] == "on"

@property
def ambient(self) -> bool:
"""True if ambient is on. FIXME be more descriptive."""
"""True if the ambient light (second light source) is on."""
return self.data["ambstatus"] == "on"

@property
def ambient_brightness(self) -> int:
"""Ambient brightness level."""
"""Brightness of the ambient light."""
return self.data["ambvalue"]

@property
def eyecare(self) -> bool:
"""True if eyecare is on."""
"""True if the eyecare light (first light source) is on."""
return self.data["eyecare"] == "on"

@property
def scene(self) -> int:
"""Current scene."""
"""Current fixed scene."""
return self.data["scene_num"]

@property
def smart_night_light(self) -> bool:
"""True if smart night light is on."""
"""True if the smart night light mode is on."""
return self.data["bls"] == "on"

@property
def delay_off_countdown(self) -> int:
"""Current delay off counter."""
"""Countdown until turning off in minutes."""
return self.data["dvalue"]

def __str__(self) -> str:
def __repr__(self) -> str:
s = "<PhilipsEyecareStatus power=%s, brightness=%s, " \
"notify=%s, ambient=%s, ambient_brightness=%s, " \
"eyecare=%s, scene=%s, smart_night_light=%s, " \
Expand Down Expand Up @@ -113,11 +113,11 @@ def off(self):
return self.send("set_power", ["off"])

def eyecare_on(self):
"""Eyecare on."""
"""Turn the eyecare light on."""
return self.send("set_eyecare", ["on"])

def eyecare_off(self):
"""Eyecare off."""
"""Turn the eyecare light off."""
return self.send("set_eyecare", ["off"])

def set_brightness(self, level: int):
Expand All @@ -128,7 +128,7 @@ def set_brightness(self, level: int):
return self.send("set_bright", [level])

def set_scene(self, number: int):
"""Set eyecare user scene."""
"""Set one of the fixed eyecare user scenes."""
if number < 1 or number > 4:
raise PhilipsEyecareException("Invalid fixed scene number: %s" % number)

Expand All @@ -144,31 +144,31 @@ def delay_off(self, minutes: int):
return self.send("delay_off", [minutes])

def smart_night_light_on(self):
"""Night Light On."""
"""Turn the smart night light mode on."""
return self.send("enable_bl", ["on"])

def smart_night_light_off(self):
"""Night Light Off."""
"""Turn the smart night light mode off."""
return self.send("enable_bl", ["off"])

def reminder_on(self):
"""Eye Fatigue Reminder On."""
"""Enable the eye fatigue reminder / notification."""
return self.send("set_notifyuser", ["on"])

def reminder_off(self):
"""Eye Fatigue Reminder Off."""
"""Disable the eye fatigue reminder / notification."""
return self.send("set_notifyuser", ["off"])

def ambient_on(self):
"""Amblient Light On."""
"""Turn the ambient light on."""
return self.send("enable_amb", ["on"])

def ambient_off(self):
"""Ambient Light Off."""
"""Turn the ambient light off."""
return self.send("enable_amb", ["off"])

def set_ambient_brightness(self, level: int):
"""Set Ambient Light brightness level."""
"""Set the brightness of the ambient light."""
if level < 1 or level > 100:
raise PhilipsEyecareException(
"Invalid ambient brightness: %s" % level)
Expand Down
Loading