From 0e230363fd5d9eea8647234813ae7f2e9db01ace Mon Sep 17 00:00:00 2001 From: Teemu Rytilahti Date: Fri, 17 Sep 2021 14:38:34 +0200 Subject: [PATCH 1/2] Make sure all device-derived classes accept model kwarg Converts the missing mapping information on MiotDevice to a warning, fixing #1118 --- miio/airconditioningcompanionMCN.py | 2 +- miio/airhumidifier_jsq.py | 17 +++-------------- miio/chuangmi_plug.py | 20 +++----------------- miio/gateway/gateway.py | 11 +++-------- miio/miot_device.py | 4 +--- miio/tests/test_device.py | 12 ++++++++++++ miio/viomivacuum.py | 10 ++++++++-- 7 files changed, 31 insertions(+), 45 deletions(-) diff --git a/miio/airconditioningcompanionMCN.py b/miio/airconditioningcompanionMCN.py index 3f763063d..cc90b016c 100644 --- a/miio/airconditioningcompanionMCN.py +++ b/miio/airconditioningcompanionMCN.py @@ -113,7 +113,7 @@ def __init__( ) -> None: if start_id is None: start_id = random.randint(0, 999) # nosec - super().__init__(ip, token, start_id, debug, lazy_discover) + super().__init__(ip, token, start_id, debug, lazy_discover, model=model) if model != MODEL_ACPARTNER_MCN02: _LOGGER.error( diff --git a/miio/airhumidifier_jsq.py b/miio/airhumidifier_jsq.py index d9ade0056..98f5326e6 100644 --- a/miio/airhumidifier_jsq.py +++ b/miio/airhumidifier_jsq.py @@ -133,19 +133,6 @@ def lid_opened(self) -> bool: class AirHumidifierJsq(Device): """Implementation of Xiaomi Zero Fog Humidifier: shuii.humidifier.jsq001.""" - def __init__( - self, - ip: str = None, - token: str = None, - start_id: int = 0, - debug: int = 0, - lazy_discover: bool = True, - model: str = MODEL_HUMIDIFIER_JSQ001, - ) -> None: - super().__init__(ip, token, start_id, debug, lazy_discover, model=model) - if model not in AVAILABLE_PROPERTIES: - self._model = MODEL_HUMIDIFIER_JSQ001 - @command( default_output=format_output( "", @@ -178,7 +165,9 @@ def status(self) -> AirHumidifierStatus: # status[7]: water level state (0: ok, 1: add water) # status[8]: lid state (0: ok, 1: lid is opened) - properties = AVAILABLE_PROPERTIES[self.model] + properties = AVAILABLE_PROPERTIES.get( + self.model, AVAILABLE_PROPERTIES[MODEL_HUMIDIFIER_JSQ001] + ) if len(properties) != len(values): _LOGGER.error( "Count (%s) of requested properties (%s) does not match the " diff --git a/miio/chuangmi_plug.py b/miio/chuangmi_plug.py index 3303aa18b..5a7af9646 100644 --- a/miio/chuangmi_plug.py +++ b/miio/chuangmi_plug.py @@ -89,22 +89,6 @@ def wifi_led(self) -> Optional[bool]: class ChuangmiPlug(Device): """Main class representing the Chuangmi Plug.""" - def __init__( - self, - ip: str = None, - token: str = None, - start_id: int = 0, - debug: int = 0, - lazy_discover: bool = True, - model: str = MODEL_CHUANGMI_PLUG_M1, - ) -> None: - super().__init__(ip, token, start_id, debug, lazy_discover) - - if model in AVAILABLE_PROPERTIES: - self._model = model - else: - self._model = MODEL_CHUANGMI_PLUG_M1 - @command( default_output=format_output( "", @@ -117,7 +101,9 @@ def __init__( ) def status(self) -> ChuangmiPlugStatus: """Retrieve properties.""" - properties = AVAILABLE_PROPERTIES[self.model].copy() + properties = AVAILABLE_PROPERTIES.get( + self.model, AVAILABLE_PROPERTIES[MODEL_CHUANGMI_PLUG_M1] + ).copy() values = self.get_properties(properties) if self.model == MODEL_CHUANGMI_PLUG_V3: diff --git a/miio/gateway/gateway.py b/miio/gateway/gateway.py index 999962a0a..053e2f1cb 100644 --- a/miio/gateway/gateway.py +++ b/miio/gateway/gateway.py @@ -84,8 +84,10 @@ def __init__( start_id: int = 0, debug: int = 0, lazy_discover: bool = True, + *, + model: str = None, ) -> None: - super().__init__(ip, token, start_id, debug, lazy_discover) + super().__init__(ip, token, start_id, debug, lazy_discover, model=model) self._alarm = Alarm(parent=self) self._radio = Radio(parent=self) @@ -134,13 +136,6 @@ def mac(self): self._info = self.info() return self._info.mac_address - @property - def model(self): - """Return the zigbee model of the gateway.""" - if self._info is None: - self._info = self.info() - return self._info.model - @property def subdevice_model_map(self): """Return the subdevice model map.""" diff --git a/miio/miot_device.py b/miio/miot_device.py index 2c4d134f2..d53557454 100644 --- a/miio/miot_device.py +++ b/miio/miot_device.py @@ -50,9 +50,7 @@ def __init__( ) if mapping is None and not hasattr(self, "mapping"): - raise DeviceException( - "Neither the class nor the parameter defines the mapping" - ) + _LOGGER.warning("Neither the class nor the parameter defines the mapping") if mapping is not None: self.mapping = mapping diff --git a/miio/tests/test_device.py b/miio/tests/test_device.py index 467de5c60..6412cae24 100644 --- a/miio/tests/test_device.py +++ b/miio/tests/test_device.py @@ -92,3 +92,15 @@ def test_missing_supported(mocker, caplog): assert "Found an unsupported model" in caplog.text assert "for class 'Device'" in caplog.text + + +@pytest.mark.parametrize("cls", Device.__subclasses__()) +def test_device_ctor_model(cls): + """Make sure that every device subclass ctor accepts model kwarg.""" + ignore_classes = ["GatewayDevice", "CustomDevice"] + if cls.__name__ in ignore_classes: + return + + dummy_model = "dummy" + dev = cls("127.0.0.1", "68ffffffffffffffffffffffffffffff", model=dummy_model) + assert dev.model == dummy_model diff --git a/miio/viomivacuum.py b/miio/viomivacuum.py index 947fffd75..1c2f0a619 100644 --- a/miio/viomivacuum.py +++ b/miio/viomivacuum.py @@ -480,9 +480,15 @@ class ViomiVacuum(Device): retry_count = 10 def __init__( - self, ip: str, token: str = None, start_id: int = 0, debug: int = 0 + self, + ip: str, + token: str = None, + start_id: int = 0, + debug: int = 0, + *, + model: str = None, ) -> None: - super().__init__(ip, token, start_id, debug) + super().__init__(ip, token, start_id, debug, model=model) self.manual_seqnum = -1 self._cache: Dict[str, Any] = {"edge_state": None, "rooms": {}, "maps": {}} From a1b3b98a9eb609879b2bbd04aa22cd2963d0ad71 Mon Sep 17 00:00:00 2001 From: Teemu Rytilahti Date: Fri, 17 Sep 2021 14:50:15 +0200 Subject: [PATCH 2/2] miotdevice test: check for log entry instead of exception --- miio/tests/test_miotdevice.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/miio/tests/test_miotdevice.py b/miio/tests/test_miotdevice.py index ec3c71b99..429e85d40 100644 --- a/miio/tests/test_miotdevice.py +++ b/miio/tests/test_miotdevice.py @@ -1,6 +1,6 @@ import pytest -from miio import DeviceException, MiotDevice +from miio import MiotDevice from miio.miot_device import MiotValueType @@ -14,11 +14,11 @@ def dev(module_mocker): return device -def test_missing_mapping(): +def test_missing_mapping(caplog): """Make sure ctor raises exception if neither class nor parameter defines the mapping.""" - with pytest.raises(DeviceException): - _ = MiotDevice("127.0.0.1", "68ffffffffffffffffffffffffffffff") + _ = MiotDevice("127.0.0.1", "68ffffffffffffffffffffffffffffff") + assert "Neither the class nor the parameter defines the mapping" in caplog.text def test_ctor_mapping():