Skip to content

Commit

Permalink
Prepare release 0.4.6 (#541)
Browse files Browse the repository at this point in the history
* Bump version
* Update changelog
* Add additional changes
* Add new supported device and hardware versions
* Remove "Code Health" batch temporary
* Fix Air Humidifier tests
* Fix Air Quality Monitor V1 tests
* Add Air Quality Monitor S1 tests
  • Loading branch information
syssi authored Oct 2, 2019
1 parent a29f30f commit 5184dcf
Show file tree
Hide file tree
Showing 7 changed files with 195 additions and 48 deletions.
75 changes: 75 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,80 @@
# Change Log

This release adds support for the following new devices:

* Xiaomi Air Quality Monitor S1 \(cgllc.airmonitor.s1\)
* Xiaomi Mi Dehumidifier V1 \(nwt.derh.wdh318efw1\)
* Xiaomi Mi Roborock M1S and Mi Robot S1
* Xiaomi Mijia 360 1080p camera \(chuangmi.camera.ipc009\)
* Xiaomi Mi Smart Fan \(zhimi.fan.za3, zhimi.fan.za4, dmaker.fan.p5\)
* Xiaomi Smartmi Pure Evaporative Air Humidifier \(zhimi.humidifier.cb1\)
* Xiaomi Tinymu Smart Toilet Cover
* Xiaomi 16 Relays Module

Fixes & Enhancements:

* Air Conditioning Companion: Add particular swing mode values of a chigo air conditioner
* Air Humidifier: Handle poweroff exception on set\_mode
* Chuangmi IR controller: Add indicator led support
* Chuangmi IR controller: Add discovery of the Xiaomi IR remote 2gen \(chuangmi.remote.h102a03\)
* Chuangmi Plug: Fix set\_wifi\_led cli command
* Vacuum: Add state 18 as "segment cleaning"
* Device: Add easily accessible properties to DeviceError exception
* Always import DeviceError exception
* Require click version \>=7
* Remove pretty\_cron and typing dependencies from requirements.txt

## [0.4.6](https://github.com/rytilahti/python-miio/tree/0.4.6)

[Full Changelog](https://github.com/rytilahti/python-miio/compare/0.4.5...0.4.6)

**Closed issues:**

- Roborock Vacuum Bin full [\#546](https://github.com/rytilahti/python-miio/issues/546)
- Add support for Xiao AI Smart Alarm Clock [\#538](https://github.com/rytilahti/python-miio/issues/538)
- rockrobo.vacuum.v1 Error: No response from the device [\#536](https://github.com/rytilahti/python-miio/issues/536)
- Assistance [\#532](https://github.com/rytilahti/python-miio/issues/532)
- Unsupported device found - roborock.vacuum.s5 [\#527](https://github.com/rytilahti/python-miio/issues/527)
- Discovery mode to chuangmi\_camera. [\#522](https://github.com/rytilahti/python-miio/issues/522)
- 新款小米1X电风扇不支持 [\#520](https://github.com/rytilahti/python-miio/issues/520)
- Add swing mode of a Chigo Air Conditioner [\#518](https://github.com/rytilahti/python-miio/issues/518)
- Discover not working with Mi AirHumidifier CA1 [\#514](https://github.com/rytilahti/python-miio/issues/514)
- Question about vacuum errors\_codes duration [\#511](https://github.com/rytilahti/python-miio/issues/511)
- Support device model dmaker.fan.p5 [\#510](https://github.com/rytilahti/python-miio/issues/510)
- Roborock S50: ERROR:miio.updater:No request was made.. [\#508](https://github.com/rytilahti/python-miio/issues/508)
- Roborock S50: losing connection with mirobo [\#507](https://github.com/rytilahti/python-miio/issues/507)
- Support for Xiaomi IR Remote \(chuangmi.remote.v2\) [\#506](https://github.com/rytilahti/python-miio/issues/506)
- Support for Humidifier new model: zhimi.humidifier.cb1 [\#492](https://github.com/rytilahti/python-miio/issues/492)
- impossible to get the last version \(0.4.5\) or even the 0.4.4 [\#489](https://github.com/rytilahti/python-miio/issues/489)
- Getting the token of Air Purifier Pro v7 [\#461](https://github.com/rytilahti/python-miio/issues/461)
- Moonlight sync with HA [\#452](https://github.com/rytilahti/python-miio/issues/452)
- Replace pretty-cron dependency with cron\_descriptor [\#423](https://github.com/rytilahti/python-miio/issues/423)

**Merged pull requests:**

- remove pretty\_cron and typing dependencies from requirements.txt [\#548](https://github.com/rytilahti/python-miio/pull/548) ([rytilahti](https://github.com/rytilahti))
- Add tinymu smart toiletlid [\#544](https://github.com/rytilahti/python-miio/pull/544) ([scp10011](https://github.com/scp10011))
- Add support for Air Quality Monitor S1 \(cgllc.airmonitor.s1\) [\#539](https://github.com/rytilahti/python-miio/pull/539) ([zhumuht](https://github.com/zhumuht))
- Add pwzn relay [\#537](https://github.com/rytilahti/python-miio/pull/537) ([SchumyHao](https://github.com/SchumyHao))
- add mi dehumidifier v1 \(nwt.derh.wdh318efw1\) [\#535](https://github.com/rytilahti/python-miio/pull/535) ([stkang](https://github.com/stkang))
- add mi robot s1 \(m1s\) to discovery [\#531](https://github.com/rytilahti/python-miio/pull/531) ([rytilahti](https://github.com/rytilahti))
- Add preliminary Roborock M1S / Mi Robot S1 support [\#526](https://github.com/rytilahti/python-miio/pull/526) ([syssi](https://github.com/syssi))
- Add state 18 as "segment cleaning" [\#525](https://github.com/rytilahti/python-miio/pull/525) ([syssi](https://github.com/syssi))
- Add particular swing mode values of a chigo air conditioner [\#519](https://github.com/rytilahti/python-miio/pull/519) ([syssi](https://github.com/syssi))
- Add chuangmi.camera.ipc009 support [\#516](https://github.com/rytilahti/python-miio/pull/516) ([impankratov](https://github.com/impankratov))
- Add zhimi.fan.za3 support [\#515](https://github.com/rytilahti/python-miio/pull/515) ([syssi](https://github.com/syssi))
- Add dmaker.fan.p5 support [\#513](https://github.com/rytilahti/python-miio/pull/513) ([syssi](https://github.com/syssi))
- Add zhimi.fan.za4 support [\#512](https://github.com/rytilahti/python-miio/pull/512) ([syssi](https://github.com/syssi))
- Require click version \>=7 [\#503](https://github.com/rytilahti/python-miio/pull/503) ([fvollmer](https://github.com/fvollmer))
- Add indicator led support of the chuangmi.remote.h102a03 and chuangmi.remote.v2 [\#500](https://github.com/rytilahti/python-miio/pull/500) ([syssi](https://github.com/syssi))
- Chuangmi Plug: Fix set\_wifi\_led cli command [\#499](https://github.com/rytilahti/python-miio/pull/499) ([syssi](https://github.com/syssi))
- Add discovery of the Xiaomi IR remote 2gen \(chuangmi.remote.h102a03\) [\#497](https://github.com/rytilahti/python-miio/pull/497) ([syssi](https://github.com/syssi))
- Air Humidifier: Handle poweroff exception on set\_mode [\#496](https://github.com/rytilahti/python-miio/pull/496) ([syssi](https://github.com/syssi))
- Add zhimi.humidifier.cb1 support [\#493](https://github.com/rytilahti/python-miio/pull/493) ([antylama](https://github.com/antylama))
- Add easily accessible properties to DeviceError exception [\#488](https://github.com/rytilahti/python-miio/pull/488) ([syssi](https://github.com/syssi))
- Always import DeviceError exception [\#487](https://github.com/rytilahti/python-miio/pull/487) ([syssi](https://github.com/syssi))


## [0.4.5](https://github.com/rytilahti/python-miio/tree/0.4.5)

This release adds support for the following new devices:
Expand Down
10 changes: 5 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
python-miio
===========

|PyPI version| |Build Status| |Code Health| |Coverage Status| |Docs| |Hound|
|PyPI version| |Build Status| |Coverage Status| |Docs| |Hound|

This library (and its accompanying cli tool) is used to interface with devices using Xiaomi's `miIO protocol <https://github.com/OpenMiHome/mihome-binary-protocol/blob/master/doc/PROTOCOL.md>`__.


Supported devices
-----------------

- :doc:`Xiaomi Mi Robot Vacuum <vacuum>` (:class:`miio.vacuum`)
- :doc:`Xiaomi Mi Robot Vacuum <vacuum>` V1, S5, M1S (:class:`miio.vacuum`)
- Xiaomi Mi Home Air Conditioner Companion (:class:`miio.airconditioningcompanion`)
- Xiaomi Mi Air Purifier (:class:`miio.airpurifier`)
- Xiaomi Aqara Camera (:class:`miia.aqaracamera`)
Expand All @@ -24,10 +24,10 @@ Supported devices
- Xiaomi Philips Zhirui Smart LED Bulb E14 Candle Lamp (:class:`miio.philips_bulb`)
- Xiaomi Philips Zhirui Bedroom Smart Lamp (:class:`miio.philips_moonlight`)
- Xiaomi Universal IR Remote Controller (Chuangmi IR) (:class:`miio.chuangmi_ir`)
- Xiaomi Mi Smart Pedestal Fan V2, V3, SA1, ZA1, ZA3, ZA4 (:class:`miio.fan`)
- Xiaomi Mi Air Humidifier (:class:`miio.airhumidifier`)
- Xiaomi Mi Smart Pedestal Fan V2, V3, SA1, ZA1, ZA3, ZA4, P5 (:class:`miio.fan`)
- Xiaomi Mi Air Humidifier V1, CA1, CB1 (:class:`miio.airhumidifier`)
- Xiaomi Mi Water Purifier (Basic support: Turn on & off) (:class:`miio.waterpurifier`)
- Xiaomi PM2.5 Air Quality Monitor (:class:`miio.airqualitymonitor`)
- Xiaomi PM2.5 Air Quality Monitor V1, B1, S1 (:class:`miio.airqualitymonitor`)
- Xiaomi Smart WiFi Speaker (:class:`miio.wifispeaker`)
- Xiaomi Mi WiFi Repeater 2 (:class:`miio.wifirepeater`)
- Xiaomi Mi Smart Rice Cooker (:class:`miio.cooker`)
Expand Down
2 changes: 1 addition & 1 deletion miio/airdehumidifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def fan_speed(self) -> Optional[FanSpeed]:
@property
def tank_full(self) -> bool:
"""The remaining amount of water in percent."""
return self.data["tank_full"]
return self.data["tank_full"] == "on"

@property
def compressor_status(self) -> bool:
Expand Down
79 changes: 53 additions & 26 deletions miio/airqualitymonitor.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging
from collections import defaultdict
from typing import Optional

import click

Expand Down Expand Up @@ -54,79 +55,105 @@ def __init__(self, data):
self.data = data

@property
def power(self) -> str:
def power(self) -> Optional[str]:
"""Current power state."""
return self.data["power"]
if "power" in self.data and self.data["power"] is not None:
return self.data["power"]
return None

@property
def is_on(self) -> bool:
"""Return True if the device is turned on."""
return self.power == "on"

@property
def usb_power(self) -> bool:
def usb_power(self) -> Optional[bool]:
"""Return True if the device's usb is on."""
return self.data["usb_state"] == "on"
if "usb_state" in self.data and self.data["usb_state"] is not None:
return self.data["usb_state"] == "on"
return None

@property
def aqi(self) -> int:
def aqi(self) -> Optional[int]:
"""Air quality index value. (0...600)."""
return self.data["aqi"]
if "aqi" in self.data and self.data["aqi"] is not None:
return self.data["aqi"]
return None

@property
def battery(self) -> int:
"""Current battery level (0...100)."""
return self.data["battery"]

@property
def display_clock(self) -> bool:
def display_clock(self) -> Optional[bool]:
"""Display a clock instead the AQI."""
return self.data["time_state"] == "on"
if "time_state" in self.data and self.data["time_state"] is not None:
return self.data["time_state"] == "on"
return None

@property
def night_mode(self) -> bool:
def night_mode(self) -> Optional[bool]:
"""Return True if the night mode is on."""
return self.data["night_state"] == "on"
if "night_state" in self.data and self.data["night_state"] is not None:
return self.data["night_state"] == "on"
return None

@property
def night_time_begin(self) -> str:
def night_time_begin(self) -> Optional[str]:
"""Return the begin of the night time."""
return self.data["night_beg_time"]
if "night_beg_time" in self.data and self.data["night_beg_time"] is not None:
return self.data["night_beg_time"]
return None

@property
def night_time_end(self) -> str:
def night_time_end(self) -> Optional[str]:
"""Return the end of the night time."""
return self.data["night_end_time"]
if "night_end_time" in self.data and self.data["night_end_time"] is not None:
return self.data["night_end_time"]
return None

@property
def sensor_state(self) -> str:
def sensor_state(self) -> Optional[str]:
"""Sensor state."""
return self.data["sensor_state"]
if "sensor_state" in self.data and self.data["sensor_state"] is not None:
return self.data["sensor_state"]
return None

@property
def co2(self) -> int:
def co2(self) -> Optional[int]:
"""Return co2 value (400...9999)ppm for MODEL_AIRQUALITYMONITOR_S1."""
return self.data["co2"]
if "co2" in self.data and self.data["co2"] is not None:
return self.data["co2"]
return None

@property
def humidity(self) -> float:
def humidity(self) -> Optional[float]:
"""Return humidity value (0...100)% for MODEL_AIRQUALITYMONITOR_S1."""
return self.data["humidity"]
if "humidity" in self.data and self.data["humidity"] is not None:
return self.data["humidity"]
return None

@property
def pm25(self) -> float:
def pm25(self) -> Optional[float]:
"""Return pm2.5 value (0...999)μg/m³ for MODEL_AIRQUALITYMONITOR_S1."""
return self.data["pm25"]
if "pm25" in self.data and self.data["pm25"] is not None:
return self.data["pm25"]
return None

@property
def temperature(self) -> float:
def temperature(self) -> Optional[float]:
"""Return temperature value (-10...50)°C for MODEL_AIRQUALITYMONITOR_S1."""
return self.data["temperature"]
if "temperature" in self.data and self.data["temperature"] is not None:
return self.data["temperature"]
return None

@property
def tvoc(self) -> int:
def tvoc(self) -> Optional[int]:
"""Return tvoc value for MODEL_AIRQUALITYMONITOR_S1."""
return self.data["tvoc"]
if "tvoc" in self.data and self.data["tvoc"] is not None:
return self.data["tvoc"]
return None

def __repr__(self) -> str:
s = "<AirQualityMonitorStatus power=%s, " \
Expand Down
12 changes: 3 additions & 9 deletions miio/tests/test_airdehumidifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,13 @@ def __init__(self, *args, **kwargs):

self.return_values = {
'get_prop': self._get_state,
'set_power': lambda x: self._set_state("power", x),
'set_power': lambda x: self._set_state("on_off", x),
'set_mode': lambda x: self._set_state("mode", x),
'set_led': lambda x: self._set_state("led", x),
'set_buzzer': lambda x: self._set_state("buzzer", x),
'set_child_lock': lambda x: self._set_state("child_lock", x),
'set_fan_speed': lambda x: self._set_state("fan_st", x),
'set_target_humidity': lambda x: self._set_state("auto", x),
'set_auto': lambda x: self._set_state("auto", x),
'miIO.info': self._get_device_info,
}
super().__init__(args, kwargs)
Expand Down Expand Up @@ -107,7 +107,7 @@ def test_status(self):
assert self.state().temperature == self.device.start_state["temp"]
assert self.state().humidity == self.device.start_state["humidity"]
assert self.state().mode == OperationMode(self.device.start_state["mode"])
assert self.state().led == self.device.start_state["led"]
assert self.state().led == (self.device.start_state["led"] == 'on')
assert self.state().buzzer == (self.device.start_state["buzzer"] == 'on')
assert self.state().child_lock == (self.device.start_state["child_lock"] == 'on')
assert self.state().target_humidity == self.device.start_state["auto"]
Expand Down Expand Up @@ -158,12 +158,6 @@ def test_status_without_temperature(self):

assert self.state().temperature is None

def test_status_without_led(self):
self.device._reset_state()
self.device.state["led"] = None

assert self.state().led_brightness is None

def test_set_target_humidity(self):
def target_humidity():
return self.device.status().target_humidity
Expand Down
63 changes: 57 additions & 6 deletions miio/tests/test_airqualitymonitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
import pytest

from miio import AirQualityMonitor
from miio.airqualitymonitor import AirQualityMonitorStatus, MODEL_AIRQUALITYMONITOR_V1
from miio.airqualitymonitor import (AirQualityMonitorStatus,
MODEL_AIRQUALITYMONITOR_V1,
MODEL_AIRQUALITYMONITOR_S1)
from .dummies import DummyDevice


class DummyAirQualityMonitor(DummyDevice, AirQualityMonitor):
class DummyAirQualityMonitorV1(DummyDevice, AirQualityMonitor):
def __init__(self, *args, **kwargs):
self.model = MODEL_AIRQUALITYMONITOR_V1
self.state = {
Expand All @@ -31,13 +33,13 @@ def __init__(self, *args, **kwargs):


@pytest.fixture(scope="class")
def airqualitymonitor(request):
request.cls.device = DummyAirQualityMonitor()
def airqualitymonitorv1(request):
request.cls.device = DummyAirQualityMonitorV1()
# TODO add ability to test on a real device


@pytest.mark.usefixtures("airqualitymonitor")
class TestAirQualityMonitor(TestCase):
@pytest.mark.usefixtures("airqualitymonitorv1")
class TestAirQualityMonitorV1(TestCase):
def is_on(self):
return self.device.status().is_on

Expand Down Expand Up @@ -69,3 +71,52 @@ def test_status(self):
assert self.state().usb_power is (self.device.start_state["usb_state"] == 'on')
assert self.state().display_clock is (self.device.start_state["time_state"] == 'on')
assert self.state().night_mode is (self.device.start_state["night_state"] == 'on')


class DummyAirQualityMonitorS1(DummyDevice, AirQualityMonitor):
def __init__(self, *args, **kwargs):
self.model = MODEL_AIRQUALITYMONITOR_S1
self.state = {
'battery': 100,
'co2': 695,
'humidity': 62.1,
'pm25': 19.4,
'temperature': 27.4,
'tvoc': 254,
}
self.return_values = {
'get_prop': self._get_state,
}
super().__init__(args, kwargs)

def _get_state(self, props):
"""Return wanted properties"""
return self.state


@pytest.fixture(scope="class")
def airqualitymonitors1(request):
request.cls.device = DummyAirQualityMonitorS1()
# TODO add ability to test on a real device


@pytest.mark.usefixtures("airqualitymonitors1")
class TestAirQualityMonitorS1(TestCase):
def state(self):
return self.device.status()

def test_status(self):
self.device._reset_state()

assert repr(self.state()) == repr(AirQualityMonitorStatus(self.device.start_state))

assert self.state().battery == self.device.start_state["battery"]
assert self.state().co2 == self.device.start_state["co2"]
assert self.state().humidity == self.device.start_state["humidity"]
assert self.state().pm25 == self.device.start_state["pm25"]
assert self.state().temperature == self.device.start_state["temperature"]
assert self.state().tvoc == self.device.start_state["tvoc"]
assert self.state().aqi is None
assert self.state().usb_power is None
assert self.state().display_clock is None
assert self.state().night_mode is None
2 changes: 1 addition & 1 deletion miio/version.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# flake8: noqa
__version__ = "0.4.5"
__version__ = "0.4.6"

0 comments on commit 5184dcf

Please sign in to comment.