From 58b03d446146b70d8803adeb26098ea48fc87e39 Mon Sep 17 00:00:00 2001 From: arturdobo Date: Sun, 13 Dec 2020 17:08:27 +0100 Subject: [PATCH 01/11] split into two models --- miio/airpurifier_miot.py | 299 ++++++++++++++++++++++++++++----------- 1 file changed, 219 insertions(+), 80 deletions(-) diff --git a/miio/airpurifier_miot.py b/miio/airpurifier_miot.py index 6d4e736b5..2a5eaf3c5 100644 --- a/miio/airpurifier_miot.py +++ b/miio/airpurifier_miot.py @@ -46,6 +46,27 @@ "app_extra": {"siid": 15, "piid": 1}, } +# https://miot-spec.org/miot-spec-v2/instance?type=urn:miot-spec-v2:device:air-purifier:0000A007:zhimi-mb4:2 +_MODEL_AIRPURIFIER_MB4 = { + # Air Purifier + "power": {"siid": 2, "piid": 1}, + "mode": {"siid": 2, "piid": 4}, + # Environment + "aqi": {"siid": 3, "piid": 4}, + # Filter + "filter_life_remaining": {"siid": 4, "piid": 1}, + "filter_hours_used": {"siid": 4, "piid": 3}, + # Alarm + "buzzer": {"siid": 6, "piid": 1}, + # Screen + "led_brightness_level": {"siid": 7, "piid": 2}, + # Physical Control Locked + "child_lock": {"siid": 8, "piid": 1}, + # custom-service + "motor_speed": {"siid": 9, "piid": 1}, + "favorite_rpm": {"siid": 9, "piid": 3}, +} + class AirPurifierMiotException(DeviceException): pass @@ -64,7 +85,7 @@ class LedBrightness(enum.Enum): Off = 2 -class AirPurifierMiotStatus: +class BasicAirPurifierMiotStatus: """Container for status reports from the air purifier.""" def __init__(self, data: Dict[str, Any]) -> None: @@ -90,6 +111,46 @@ def aqi(self) -> int: return None return self.data["aqi"] + @property + def mode(self) -> OperationMode: + """Current operation mode.""" + return OperationMode(self.data["mode"]) + + @property + def buzzer(self) -> Optional[bool]: + """Return True if buzzer is on.""" + if self.data["buzzer"] is not None: + return self.data["buzzer"] + + return None + + @property + def child_lock(self) -> bool: + """Return True if child lock is on.""" + return self.data["child_lock"] + + @property + def filter_life_remaining(self) -> int: + """Time until the filter should be changed.""" + return self.data["filter_life_remaining"] + + @property + def filter_hours_used(self) -> int: + """How long the filter has been in use.""" + return self.data["filter_hours_used"] + + @property + def motor_speed(self) -> int: + """Speed of the motor.""" + return self.data["motor_speed"] + + +class AirPurifierMiotStatus(BasicAirPurifierMiotStatus): + """Container for status reports from the air purifier.""" + + def __init__(self, data: Dict[str, Any]) -> None: + super().__init__(data) + @property def average_aqi(self) -> int: """Average of the air quality index.""" @@ -113,11 +174,6 @@ def fan_level(self) -> int: """Current fan level.""" return self.data["fan_level"] - @property - def mode(self) -> OperationMode: - """Current operation mode.""" - return OperationMode(self.data["mode"]) - @property def led(self) -> bool: """Return True if LED is on.""" @@ -134,14 +190,6 @@ def led_brightness(self) -> Optional[LedBrightness]: return None - @property - def buzzer(self) -> Optional[bool]: - """Return True if buzzer is on.""" - if self.data["buzzer"] is not None: - return self.data["buzzer"] - - return None - @property def buzzer_volume(self) -> Optional[int]: """Return buzzer volume.""" @@ -150,27 +198,12 @@ def buzzer_volume(self) -> Optional[int]: return None - @property - def child_lock(self) -> bool: - """Return True if child lock is on.""" - return self.data["child_lock"] - @property def favorite_level(self) -> int: """Return favorite level, which is used if the mode is ``favorite``.""" # Favorite level used when the mode is `favorite`. return self.data["favorite_level"] - @property - def filter_life_remaining(self) -> int: - """Time until the filter should be changed.""" - return self.data["filter_life_remaining"] - - @property - def filter_hours_used(self) -> int: - """How long the filter has been in use.""" - return self.data["filter_hours_used"] - @property def use_time(self) -> int: """How long the device has been active in seconds.""" @@ -181,11 +214,6 @@ 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["motor_speed"] - @property def filter_rfid_product_id(self) -> Optional[str]: """RFID product ID of installed filter.""" @@ -253,7 +281,118 @@ def __repr__(self) -> str: return s -class AirPurifierMiot(MiotDevice): +class AirPurifierMB4Status(BasicAirPurifierMiotStatus): + """Container for status reports from the air purifier.""" + + def __init__(self, data: Dict[str, Any]) -> None: + super().__init__(data) + + @property + def led_brightness_level(self) -> int: + """Return brightness level.""" + return self.data["led_brightness_level"] + + @property + def favorite_level(self) -> int: + """Return favorite rpm level.""" + return self.data["favorite_rpm"] + + def __repr__(self) -> str: + s = ( + "" + % ( + self.power, + self.aqi, + self.mode, + self.led_brightness_level, + self.buzzer, + self.child_lock, + self.filter_life_remaining, + self.filter_hours_used, + self.motor_speed, + self.favorite_rpm, + ) + ) + return s + + +class BasicAirPurifierMiot(MiotDevice): + """Main class representing the air purifier which uses MIoT protocol.""" + + def __init__( + self, + attributes: dict = [], + ip: str = None, + token: str = None, + start_id: int = 0, + debug: int = 0, + lazy_discover: bool = True, + ) -> None: + super().__init__(attributes, ip, token, start_id, debug, lazy_discover) + + @command(default_output=format_output("Powering on")) + def on(self): + """Power on.""" + return self.set_property("power", True) + + @command(default_output=format_output("Powering off")) + def off(self): + """Power off.""" + return self.set_property("power", False) + + @command( + click.argument("rpm", type=int), + default_output=format_output("Setting favorite motor speed '{rpm}' rpm"), + ) + def set_favorite_rpm(self, rpm: int): + """Set favorite motor speed.""" + # Note: documentation says the maximum is 2300, however, the purifier may return an error for rpm over 2200. + if rpm < 300 or rpm > 2300 or rpm % 10 != 0: + raise AirPurifierMiotException( + "Invalid favorite motor speed: %s. Must be between 300 and 2300 and divisible by 10" + % rpm + ) + return self.set_property("favorite_rpm", rpm) + + @command( + click.argument("mode", type=EnumType(OperationMode)), + default_output=format_output("Setting mode to '{mode.value}'"), + ) + def set_mode(self, mode: OperationMode): + """Set mode.""" + return self.set_property("mode", mode.value) + + @command( + click.argument("buzzer", type=bool), + default_output=format_output( + lambda buzzer: "Turning on buzzer" if buzzer else "Turning off buzzer" + ), + ) + def set_buzzer(self, buzzer: bool): + """Set buzzer on/off.""" + return self.set_property("buzzer", buzzer) + + @command( + click.argument("lock", type=bool), + default_output=format_output( + lambda lock: "Turning on child lock" if lock else "Turning off child lock" + ), + ) + def set_child_lock(self, lock: bool): + """Set child lock on/off.""" + return self.set_property("child_lock", lock) + + +class AirPurifierMiot(BasicAirPurifierMiot): """Main class representing the air purifier which uses MIoT protocol.""" def __init__( @@ -302,16 +441,6 @@ def status(self) -> AirPurifierMiotStatus: } ) - @command(default_output=format_output("Powering on")) - def on(self): - """Power on.""" - return self.set_property("power", True) - - @command(default_output=format_output("Powering off")) - def off(self): - """Power off.""" - return self.set_property("power", False) - @command( click.argument("level", type=int), default_output=format_output("Setting fan level to '{level}'"), @@ -322,20 +451,6 @@ def set_fan_level(self, level: int): raise AirPurifierMiotException("Invalid fan level: %s" % level) return self.set_property("fan_level", level) - @command( - click.argument("rpm", type=int), - default_output=format_output("Setting favorite motor speed '{rpm}' rpm"), - ) - def set_favorite_rpm(self, rpm: int): - """Set favorite motor speed.""" - # Note: documentation says the maximum is 2300, however, the purifier may return an error for rpm over 2200. - if rpm < 300 or rpm > 2300 or rpm % 10 != 0: - raise AirPurifierMiotException( - "Invalid favorite motor speed: %s. Must be between 300 and 2300 and divisible by 10" - % rpm - ) - return self.set_property("favorite_rpm", rpm) - @command( click.argument("volume", type=int), default_output=format_output("Setting sound volume to {volume}"), @@ -348,14 +463,6 @@ def set_volume(self, volume: int): ) return self.set_property("buzzer_volume", volume) - @command( - click.argument("mode", type=EnumType(OperationMode)), - default_output=format_output("Setting mode to '{mode.value}'"), - ) - def set_mode(self, mode: OperationMode): - """Set mode.""" - return self.set_property("mode", mode.value) - @command( click.argument("level", type=int), default_output=format_output("Setting favorite level to {level}"), @@ -388,22 +495,54 @@ def set_led(self, led: bool): """Turn led on/off.""" return self.set_property("led", led) + +class AirPurifierMB4(BasicAirPurifierMiot): + """Main class representing the air purifier which uses MIoT protocol.""" + + def __init__( + self, + ip: str = None, + token: str = None, + start_id: int = 0, + debug: int = 0, + lazy_discover: bool = True, + ) -> None: + super().__init__( + _MODEL_AIRPURIFIER_MB4, ip, token, start_id, debug, lazy_discover + ) + @command( - click.argument("buzzer", type=bool), default_output=format_output( - lambda buzzer: "Turning on buzzer" if buzzer else "Turning off buzzer" - ), + "", + "Power: {result.power}\n" + "AQI: {result.aqi} μg/m³\n" + "Mode: {result.mode}\n" + "LED brightness level: {result.led_brightness_level}\n" + "Buzzer: {result.buzzer}\n" + "Child lock: {result.child_lock}\n" + "Filter life remaining: {result.filter_life_remaining} %\n" + "Filter hours used: {result.filter_hours_used}\n" + "Motor speed: {result.motor_speed} rpm\n" + "Favorite RPM: {result.favorite_rpm} rpm\n", + ) ) - def set_buzzer(self, buzzer: bool): - """Set buzzer on/off.""" - return self.set_property("buzzer", buzzer) + def status(self) -> AirPurifierMB4Status: + """Retrieve properties.""" + + return AirPurifierMB4Status( + { + prop["did"]: prop["value"] if prop["code"] == 0 else None + for prop in self.get_properties_for_mapping() + } + ) @command( - click.argument("lock", type=bool), - default_output=format_output( - lambda lock: "Turning on child lock" if lock else "Turning off child lock" - ), + click.argument("led_brightness_level", type=int), + default_output=format_output("Setting LED brightness level to {brightness}"), ) - def set_child_lock(self, lock: bool): - """Set child lock on/off.""" - return self.set_property("child_lock", lock) + def set_led_brightness_level(self, level: int): + """Set led brightness level.""" + if level < 0 or level > 8: + raise AirPurifierMiotException("Invalid brightness level: %s" % level) + + return self.set_property("led_brightness_level", level) From f928335b9debcd8291edbe65849025294eb9a139 Mon Sep 17 00:00:00 2001 From: arturdobo Date: Fri, 1 Jan 2021 17:59:18 +0100 Subject: [PATCH 02/11] Add test for mb4 --- miio/tests/test_airpurifier_miot_mb4.py | 141 ++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 miio/tests/test_airpurifier_miot_mb4.py diff --git a/miio/tests/test_airpurifier_miot_mb4.py b/miio/tests/test_airpurifier_miot_mb4.py new file mode 100644 index 000000000..7eb8ac7dc --- /dev/null +++ b/miio/tests/test_airpurifier_miot_mb4.py @@ -0,0 +1,141 @@ +from unittest import TestCase + +import pytest + +from miio import AirPurifierMB4 +from miio.airpurifier_miot import AirPurifierMiotException, OperationMode + +from .dummies import DummyMiotDevice + +_INITIAL_STATE = { + "power": True, + "mode": 0, + "aqi": 10, + "filter_life_remaining": 80, + "filter_hours_used": 682, + "buzzer": False, + "led_brightness_level": 4, + "child_lock": False, + "motor_speed": 354, + "favorite_rpm": 500, +} + + +class DummyAirPurifierMiot(DummyMiotDevice, AirPurifierMB4): + def __init__(self, *args, **kwargs): + self.state = _INITIAL_STATE + self.return_values = { + "get_prop": self._get_state, + "set_power": lambda x: self._set_state("power", x), + "set_mode": lambda x: self._set_state("mode", x), + "set_buzzer": lambda x: self._set_state("buzzer", x), + "set_child_lock": lambda x: self._set_state("child_lock", x), + "set_level_favorite_rpm": lambda x: self._set_state( + "favorite_level_rpm", x + ), + "reset_filter1": lambda x: ( + self._set_state("f1_hour_used", [0]), + self._set_state("filter1_life", [100]), + ), + } + super().__init__(*args, **kwargs) + + +@pytest.fixture(scope="function") +def airpurifier(request): + request.cls.device = DummyAirPurifierMiot() + + +@pytest.mark.usefixtures("airpurifier") +class TestAirPurifier(TestCase): + def test_on(self): + self.device.off() # ensure off + assert self.device.status().is_on is False + + self.device.on() + assert self.device.status().is_on is True + + def test_off(self): + self.device.on() # ensure on + assert self.device.status().is_on is True + + self.device.off() + assert self.device.status().is_on is False + + def test_status(self): + status = self.device.status() + assert status.is_on is _INITIAL_STATE["power"] + assert status.aqi == _INITIAL_STATE["aqi"] + assert status.mode == OperationMode(_INITIAL_STATE["mode"]) + assert status.led_brightness_level == _INITIAL_STATE["led_brightness_level"] + assert status.buzzer == _INITIAL_STATE["buzzer"] + assert status.child_lock == _INITIAL_STATE["child_lock"] + assert status.favorite_rpm == _INITIAL_STATE["favorite_rpm"] + assert status.filter_life_remaining == _INITIAL_STATE["filter_life_remaining"] + assert status.filter_hours_used == _INITIAL_STATE["filter_hours_used"] + assert status.motor_speed == _INITIAL_STATE["motor_speed"] + + def test_set_mode(self): + def mode(): + return self.device.status().mode + + self.device.set_mode(OperationMode.Auto) + assert mode() == OperationMode.Auto + + self.device.set_mode(OperationMode.Silent) + assert mode() == OperationMode.Silent + + self.device.set_mode(OperationMode.Favorite) + assert mode() == OperationMode.Favorite + + self.device.set_mode(OperationMode.Fan) + assert mode() == OperationMode.Fan + + def test_set_favorite_rpm(self): + def favorite_rpm(): + return self.device.status().favorite_rpm + + self.device.set_favorite_rpm(300) + assert favorite_rpm() == 300 + self.device.set_favorite_rpm(1000) + assert favorite_rpm() == 1000 + self.device.set_favorite_rpm(2300) + assert favorite_rpm() == 2300 + + with pytest.raises(AirPurifierMiotException): + self.device.set_favorite_rpm(301) + + with pytest.raises(AirPurifierMiotException): + self.device.set_favorite_rpm(290) + + with pytest.raises(AirPurifierMiotException): + self.device.set_favorite_rpm(2310) + + def test_set_led_brightness_level(self): + def led_brightness_level(): + return self.device.status().led_brightness_level + + self.device.set_led_brightness_level(0) + assert led_brightness_level() == 0 + + self.device.set_led_brightness_level(4) + assert led_brightness_level() == 4 + + self.device.set_led_brightness_level(8) + assert led_brightness_level() == 8 + + with pytest.raises(AirPurifierMiotException): + self.device.set_led_brightness_level(-1) + + with pytest.raises(AirPurifierMiotException): + self.device.set_led_brightness_level(9) + + def test_set_child_lock(self): + def child_lock(): + return self.device.status().child_lock + + self.device.set_child_lock(True) + assert child_lock() is True + + self.device.set_child_lock(False) + assert child_lock() is False From 652989abad3b9fd0e4b397e6f1069da2beef7159 Mon Sep 17 00:00:00 2001 From: arturdobo Date: Fri, 1 Jan 2021 18:24:29 +0100 Subject: [PATCH 03/11] Fixed missing import --- miio/__init__.py | 2 +- miio/tests/test_airpurifier_miot_mb4.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/miio/__init__.py b/miio/__init__.py index 02c43a180..e91175222 100644 --- a/miio/__init__.py +++ b/miio/__init__.py @@ -21,7 +21,7 @@ from miio.airhumidifier_mjjsq import AirHumidifierMjjsq from miio.airpurifier import AirPurifier from miio.airpurifier_airdog import AirDogX3, AirDogX5, AirDogX7SM -from miio.airpurifier_miot import AirPurifierMiot +from miio.airpurifier_miot import AirPurifierMB4, AirPurifierMiot from miio.airqualitymonitor import AirQualityMonitor from miio.airqualitymonitor_miot import AirQualityMonitorCGDN1 from miio.aqaracamera import AqaraCamera diff --git a/miio/tests/test_airpurifier_miot_mb4.py b/miio/tests/test_airpurifier_miot_mb4.py index 7eb8ac7dc..d452a1593 100644 --- a/miio/tests/test_airpurifier_miot_mb4.py +++ b/miio/tests/test_airpurifier_miot_mb4.py @@ -70,7 +70,7 @@ def test_status(self): assert status.led_brightness_level == _INITIAL_STATE["led_brightness_level"] assert status.buzzer == _INITIAL_STATE["buzzer"] assert status.child_lock == _INITIAL_STATE["child_lock"] - assert status.favorite_rpm == _INITIAL_STATE["favorite_rpm"] + assert status.favorite_level == _INITIAL_STATE["favorite_rpm"] assert status.filter_life_remaining == _INITIAL_STATE["filter_life_remaining"] assert status.filter_hours_used == _INITIAL_STATE["filter_hours_used"] assert status.motor_speed == _INITIAL_STATE["motor_speed"] @@ -92,15 +92,15 @@ def mode(): assert mode() == OperationMode.Fan def test_set_favorite_rpm(self): - def favorite_rpm(): - return self.device.status().favorite_rpm + def favorite_level(): + return self.device.status().favorite_level self.device.set_favorite_rpm(300) - assert favorite_rpm() == 300 + assert favorite_level() == 300 self.device.set_favorite_rpm(1000) - assert favorite_rpm() == 1000 + assert favorite_level() == 1000 self.device.set_favorite_rpm(2300) - assert favorite_rpm() == 2300 + assert favorite_level() == 2300 with pytest.raises(AirPurifierMiotException): self.device.set_favorite_rpm(301) From bbf205af93e2086fe0cc84d53895d0125d5e8b4c Mon Sep 17 00:00:00 2001 From: arturdobo Date: Thu, 28 Jan 2021 21:38:14 +0100 Subject: [PATCH 04/11] fix favorite level and brightness setting --- miio/airpurifier_miot.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/miio/airpurifier_miot.py b/miio/airpurifier_miot.py index 2a5eaf3c5..63252a601 100644 --- a/miio/airpurifier_miot.py +++ b/miio/airpurifier_miot.py @@ -285,6 +285,7 @@ class AirPurifierMB4Status(BasicAirPurifierMiotStatus): """Container for status reports from the air purifier.""" def __init__(self, data: Dict[str, Any]) -> None: + print(data) super().__init__(data) @property @@ -293,7 +294,7 @@ def led_brightness_level(self) -> int: return self.data["led_brightness_level"] @property - def favorite_level(self) -> int: + def favorite_rpm(self) -> int: """Return favorite rpm level.""" return self.data["favorite_rpm"] @@ -537,8 +538,8 @@ def status(self) -> AirPurifierMB4Status: ) @command( - click.argument("led_brightness_level", type=int), - default_output=format_output("Setting LED brightness level to {brightness}"), + click.argument("level", type=int), + default_output=format_output("Setting LED brightness level to {level}"), ) def set_led_brightness_level(self, level: int): """Set led brightness level.""" From 505cd0c7c1057a00809ccfd44e69971110ec5584 Mon Sep 17 00:00:00 2001 From: arturdobo Date: Thu, 28 Jan 2021 21:47:34 +0100 Subject: [PATCH 05/11] fixed favorite level in test --- miio/tests/test_airpurifier_miot_mb4.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/miio/tests/test_airpurifier_miot_mb4.py b/miio/tests/test_airpurifier_miot_mb4.py index d452a1593..c95072e17 100644 --- a/miio/tests/test_airpurifier_miot_mb4.py +++ b/miio/tests/test_airpurifier_miot_mb4.py @@ -30,9 +30,7 @@ def __init__(self, *args, **kwargs): "set_mode": lambda x: self._set_state("mode", x), "set_buzzer": lambda x: self._set_state("buzzer", x), "set_child_lock": lambda x: self._set_state("child_lock", x), - "set_level_favorite_rpm": lambda x: self._set_state( - "favorite_level_rpm", x - ), + "set_favorite_rpm": lambda x: self._set_state("favorite_rpm", x), "reset_filter1": lambda x: ( self._set_state("f1_hour_used", [0]), self._set_state("filter1_life", [100]), @@ -70,7 +68,7 @@ def test_status(self): assert status.led_brightness_level == _INITIAL_STATE["led_brightness_level"] assert status.buzzer == _INITIAL_STATE["buzzer"] assert status.child_lock == _INITIAL_STATE["child_lock"] - assert status.favorite_level == _INITIAL_STATE["favorite_rpm"] + assert status.favorite_rpm == _INITIAL_STATE["favorite_rpm"] assert status.filter_life_remaining == _INITIAL_STATE["filter_life_remaining"] assert status.filter_hours_used == _INITIAL_STATE["filter_hours_used"] assert status.motor_speed == _INITIAL_STATE["motor_speed"] @@ -92,15 +90,15 @@ def mode(): assert mode() == OperationMode.Fan def test_set_favorite_rpm(self): - def favorite_level(): - return self.device.status().favorite_level + def favorite_rpm(): + return self.device.status().favorite_rpm self.device.set_favorite_rpm(300) - assert favorite_level() == 300 + assert favorite_rpm() == 300 self.device.set_favorite_rpm(1000) - assert favorite_level() == 1000 + assert favorite_rpm() == 1000 self.device.set_favorite_rpm(2300) - assert favorite_level() == 2300 + assert favorite_rpm() == 2300 with pytest.raises(AirPurifierMiotException): self.device.set_favorite_rpm(301) From 8ebedebc68917bd8586f6ca8cdc8c80287fc34cc Mon Sep 17 00:00:00 2001 From: arturdobo Date: Thu, 28 Jan 2021 22:06:04 +0100 Subject: [PATCH 06/11] applied status review remarks --- miio/airpurifier_miot.py | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/miio/airpurifier_miot.py b/miio/airpurifier_miot.py index 63252a601..5d2fa2d93 100644 --- a/miio/airpurifier_miot.py +++ b/miio/airpurifier_miot.py @@ -282,11 +282,38 @@ def __repr__(self) -> str: class AirPurifierMB4Status(BasicAirPurifierMiotStatus): - """Container for status reports from the air purifier.""" - - def __init__(self, data: Dict[str, Any]) -> None: - print(data) - super().__init__(data) + """ + Container for status reports from the Mi Air Purifier 3C (zhimi.airpurifier.mb4). + + { + 'power': True, + 'mode': 1, + 'aqi': 2, + 'filter_life_remaining': 97, + 'filter_hours_used': 100, + 'buzzer': True, + 'led_brightness_level': 8, + 'child_lock': False, + 'motor_speed': 392, + 'favorite_rpm': 500 + } + + Response (MIoT format) + + [ + {'did': 'power', 'siid': 2, 'piid': 1, 'code': 0, 'value': True}, + {'did': 'mode', 'siid': 2, 'piid': 4, 'code': 0, 'value': 1}, + {'did': 'aqi', 'siid': 3, 'piid': 4, 'code': 0, 'value': 3}, + {'did': 'filter_life_remaining', 'siid': 4, 'piid': 1, 'code': 0, 'value': 97}, + {'did': 'filter_hours_used', 'siid': 4, 'piid': 3, 'code': 0, 'value': 100}, + {'did': 'buzzer', 'siid': 6, 'piid': 1, 'code': 0, 'value': True}, + {'did': 'led_brightness_level', 'siid': 7, 'piid': 2, 'code': 0, 'value': 8}, + {'did': 'child_lock', 'siid': 8, 'piid': 1, 'code': 0, 'value': False}, + {'did': 'motor_speed', 'siid': 9, 'piid': 1, 'code': 0, 'value': 388}, + {'did': 'favorite_rpm', 'siid': 9, 'piid': 3, 'code': 0, 'value': 500} + ] + + """ @property def led_brightness_level(self) -> int: From 7969f8cd6d566511475e2d1120b6b975771c7e8d Mon Sep 17 00:00:00 2001 From: arturdobo Date: Sun, 7 Feb 2021 20:41:29 +0100 Subject: [PATCH 07/11] Removed unnecessary constructor Co-authored-by: Teemu R. --- miio/airpurifier_miot.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/miio/airpurifier_miot.py b/miio/airpurifier_miot.py index 5d2fa2d93..adc4c8461 100644 --- a/miio/airpurifier_miot.py +++ b/miio/airpurifier_miot.py @@ -356,16 +356,6 @@ def __repr__(self) -> str: class BasicAirPurifierMiot(MiotDevice): """Main class representing the air purifier which uses MIoT protocol.""" - def __init__( - self, - attributes: dict = [], - ip: str = None, - token: str = None, - start_id: int = 0, - debug: int = 0, - lazy_discover: bool = True, - ) -> None: - super().__init__(attributes, ip, token, start_id, debug, lazy_discover) @command(default_output=format_output("Powering on")) def on(self): From 67e92bb30631178baa6428da1a910bf6c38b1fdd Mon Sep 17 00:00:00 2001 From: arturdobo Date: Sun, 7 Feb 2021 20:41:57 +0100 Subject: [PATCH 08/11] Improved comment Co-authored-by: Teemu R. --- miio/airpurifier_miot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miio/airpurifier_miot.py b/miio/airpurifier_miot.py index adc4c8461..989f68edb 100644 --- a/miio/airpurifier_miot.py +++ b/miio/airpurifier_miot.py @@ -559,7 +559,7 @@ def status(self) -> AirPurifierMB4Status: default_output=format_output("Setting LED brightness level to {level}"), ) def set_led_brightness_level(self, level: int): - """Set led brightness level.""" + """Set led brightness level (0..8).""" if level < 0 or level > 8: raise AirPurifierMiotException("Invalid brightness level: %s" % level) From e944d9c23f3920c474845800245f670399bdf5bc Mon Sep 17 00:00:00 2001 From: arturdobo Date: Sun, 7 Feb 2021 20:42:26 +0100 Subject: [PATCH 09/11] Removed unnecessary constructor Co-authored-by: Teemu R. --- miio/airpurifier_miot.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/miio/airpurifier_miot.py b/miio/airpurifier_miot.py index 989f68edb..b2ddf4e47 100644 --- a/miio/airpurifier_miot.py +++ b/miio/airpurifier_miot.py @@ -148,9 +148,6 @@ def motor_speed(self) -> int: class AirPurifierMiotStatus(BasicAirPurifierMiotStatus): """Container for status reports from the air purifier.""" - def __init__(self, data: Dict[str, Any]) -> None: - super().__init__(data) - @property def average_aqi(self) -> int: """Average of the air quality index.""" From d3668518c8d3fa2572e2feb49bcf4e2c2cdba6a1 Mon Sep 17 00:00:00 2001 From: arturdobo Date: Sun, 7 Feb 2021 20:56:07 +0100 Subject: [PATCH 10/11] fixed linting --- miio/airpurifier_miot.py | 1 - 1 file changed, 1 deletion(-) diff --git a/miio/airpurifier_miot.py b/miio/airpurifier_miot.py index b2ddf4e47..163b223ea 100644 --- a/miio/airpurifier_miot.py +++ b/miio/airpurifier_miot.py @@ -353,7 +353,6 @@ def __repr__(self) -> str: class BasicAirPurifierMiot(MiotDevice): """Main class representing the air purifier which uses MIoT protocol.""" - @command(default_output=format_output("Powering on")) def on(self): """Power on.""" From ece38bebf603314b23cef5092ec9fd86d738282c Mon Sep 17 00:00:00 2001 From: arturdobo Date: Sun, 7 Feb 2021 21:08:09 +0100 Subject: [PATCH 11/11] updated readme --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 103fe0740..a93d1f401 100644 --- a/README.rst +++ b/README.rst @@ -89,7 +89,7 @@ Supported devices - Xiaomi Mi Robot Vacuum V1, S5, M1S - Xiaomi Mi Home Air Conditioner Companion - Xiaomi Mi Smart Air Conditioner A (xiaomi.aircondition.mc1, mc2, mc4, mc5) -- Xiaomi Mi Air Purifier +- Xiaomi Mi Air Purifier 2, 3H, 3C, Pro (zhimi.airpurifier.m2, mb3, mb4, v7) - Xiaomi Mi Air (Purifier) Dog X3, X5, X7SM (airdog.airpurifier.x3, airdog.airpurifier.x5, airdog.airpurifier.x7sm) - Xiaomi Mi Air Humidifier - Xiaomi Aqara Camera