From ca9ae67cfa3b040136b7255bf9d3d12e2a6e4dfe Mon Sep 17 00:00:00 2001 From: Sebastian Muszynski Date: Tue, 21 Aug 2018 12:23:05 +0200 Subject: [PATCH] Air Humidifier: Add strong mode property and update docstrings (#300) --- miio/airhumidifier.py | 67 ++++++++++++++++++++++++++++---- miio/tests/test_airhumidifier.py | 36 ++++++++++++++++- 2 files changed, 94 insertions(+), 9 deletions(-) diff --git a/miio/airhumidifier.py b/miio/airhumidifier.py index f1e02c650..72aa136e7 100644 --- a/miio/airhumidifier.py +++ b/miio/airhumidifier.py @@ -6,7 +6,7 @@ import click from .click_common import command, format_output, EnumType -from .device import Device, DeviceException +from .device import Device, DeviceInfo, DeviceException _LOGGER = logging.getLogger(__name__) @@ -20,6 +20,7 @@ class OperationMode(enum.Enum): Medium = 'medium' High = 'high' Auto = 'auto' + Strong = 'strong' class LedBrightness(enum.Enum): @@ -31,7 +32,7 @@ class LedBrightness(enum.Enum): class AirHumidifierStatus: """Container for status reports from the air humidifier.""" - def __init__(self, data: Dict[str, Any]) -> None: + def __init__(self, data: Dict[str, Any], device_info: DeviceInfo) -> None: """ Response of a Air Humidifier (zhimi.humidifier.v1): @@ -43,6 +44,7 @@ def __init__(self, data: Dict[str, Any]) -> None: """ self.data = data + self.device_info = device_info @property def power(self) -> str: @@ -95,9 +97,39 @@ def target_humidity(self) -> int: @property def trans_level(self) -> int: - """The meaning of the property is unknown.""" + """ + The meaning of the property is unknown. + + The property is used to determine the strong mode is enabled on old firmware. + """ return self.data["trans_level"] + @property + def strong_mode_enabled(self) -> bool: + if self.firmware_version_minor == 25: + if self.trans_level == 90: + return True + + elif self.firmware_version_minor > 25: + return self.mode.value == "strong" + + return False + + @property + def firmware_version(self) -> str: + """Returns the fw_ver of miIO.info. For example 1.2.9_5033.""" + return self.device_info.firmware_version + + @property + def firmware_version_major(self) -> str: + major, _ = self.firmware_version.rsplit('_', 1) + return major + + @property + def firmware_version_minor(self) -> int: + _, minor = self.firmware_version.rsplit('_', 1) + return int(minor) + @property def speed(self) -> Optional[int]: """Current fan speed.""" @@ -105,12 +137,16 @@ def speed(self) -> Optional[int]: @property def depth(self) -> Optional[int]: - """Current depth.""" + """The remaining amount of water in percent.""" return self.data["depth"] @property def dry(self) -> Optional[bool]: - """Return True if dry mode is on if available.""" + """ + Dry mode: The amount of water is not enough to continue to work for about 8 hours. + + Return True if dry mode is on if available. + """ if self.data["dry"] is not None: return self.data["dry"] == "on" return None @@ -145,7 +181,10 @@ def __repr__(self) -> str: "dry=%s, " \ "use_time=%s, " \ "hardware_version=%s, " \ - "button_pressed=%s>" % \ + "button_pressed=%s, " \ + "strong_mode_enabled=%s, " \ + "firmware_version_major=%s, " \ + "firmware_version_minor=%s>" % \ (self.power, self.mode, self.temperature, @@ -160,7 +199,10 @@ def __repr__(self) -> str: self.dry, self.use_time, self.hardware_version, - self.button_pressed) + self.button_pressed, + self.strong_mode_enabled, + self.firmware_version_major, + self.firmware_version_minor) return s def __json__(self): @@ -170,6 +212,12 @@ def __json__(self): class AirHumidifier(Device): """Implementation of Xiaomi Mi Air Humidifier.""" + def __init__(self, ip: str = None, token: str = None, start_id: int = 0, + debug: int = 0, lazy_discover: bool = True) -> None: + super().__init__(ip, token, start_id, debug, lazy_discover) + + self.device_info = None + @command( default_output=format_output( "", @@ -193,6 +241,9 @@ class AirHumidifier(Device): def status(self) -> AirHumidifierStatus: """Retrieve properties.""" + if self.device_info is None: + self.device_info = self.info() + properties = ['power', 'mode', 'temp_dec', 'humidity', 'buzzer', 'led_b', 'child_lock', 'limit_hum', 'trans_level', 'speed', 'depth', 'dry', 'use_time', 'button_pressed', @@ -212,7 +263,7 @@ def status(self) -> AirHumidifierStatus: properties_count, values_count) return AirHumidifierStatus( - defaultdict(lambda: None, zip(properties, values))) + defaultdict(lambda: None, zip(properties, values)), self.device_info) @command( default_output=format_output("Powering on"), diff --git a/miio/tests/test_airhumidifier.py b/miio/tests/test_airhumidifier.py index 321c79eee..45bc3eb9c 100644 --- a/miio/tests/test_airhumidifier.py +++ b/miio/tests/test_airhumidifier.py @@ -6,10 +6,32 @@ from miio.airhumidifier import (OperationMode, LedBrightness, AirHumidifierStatus, AirHumidifierException, ) from .dummies import DummyDevice +from miio.device import DeviceInfo class DummyAirHumidifier(DummyDevice, AirHumidifier): def __init__(self, *args, **kwargs): + self.dummy_device_info = { + 'fw_ver': '1.2.9_5033', + 'token': '68ffffffffffffffffffffffffffffff', + 'otu_stat': [101, 74, 5343, 0, 5327, 407], + 'mmfree': 228248, + 'netif': {'gw': '192.168.0.1', + 'localIp': '192.168.0.25', + 'mask': '255.255.255.0'}, + 'ott_stat': [0, 0, 0, 0], + 'model': 'zhimi.humidifier.v1', + 'cfg_time': 0, + 'life': 575661, + 'ap': {'rssi': -35, 'ssid': 'ap', + 'bssid': 'FF:FF:FF:FF:FF:FF'}, + 'wifi_fw_ver': 'SD878x-14.76.36.p84-702.1.0-WM', + 'hw_ver': 'MW300', + 'ot': 'otu', + 'mac': '78:11:FF:FF:FF:FF' + } + self.device_info = None + self.state = { 'power': 'on', 'mode': 'medium', @@ -37,9 +59,14 @@ def __init__(self, *args, **kwargs): 'set_child_lock': lambda x: self._set_state("child_lock", x), 'set_limit_hum': lambda x: self._set_state("limit_hum", x), 'set_dry': lambda x: self._set_state("dry", x), + 'miIO.info': self._get_device_info, } super().__init__(args, kwargs) + def _get_device_info(self, _): + """Return dummy device info.""" + return self.dummy_device_info + @pytest.fixture(scope="class") def airhumidifier(request): @@ -72,7 +99,9 @@ def test_off(self): def test_status(self): self.device._reset_state() - assert repr(self.state()) == repr(AirHumidifierStatus(self.device.start_state)) + device_info = DeviceInfo(self.device.dummy_device_info) + + assert repr(self.state()) == repr(AirHumidifierStatus(self.device.start_state, device_info)) assert self.is_on() is True assert self.state().temperature == self.device.start_state["temp_dec"] / 10.0 @@ -90,6 +119,11 @@ def test_status(self): assert self.state().hardware_version == self.device.start_state["hw_version"] assert self.state().button_pressed == self.device.start_state["button_pressed"] + assert self.state().firmware_version == device_info.firmware_version + assert self.state().firmware_version_major == device_info.firmware_version.rsplit('_', 1)[0] + assert self.state().firmware_version_minor == int(device_info.firmware_version.rsplit('_', 1)[1]) + assert self.state().strong_mode_enabled is False + def test_set_mode(self): def mode(): return self.device.status().mode