From c99de9b1b1c337bd259373311da68955f63c8689 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Wed, 9 Nov 2022 23:41:09 +0100 Subject: [PATCH 01/40] Add caching --- miio/device.py | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/miio/device.py b/miio/device.py index 098502656..3e87e46f2 100644 --- a/miio/device.py +++ b/miio/device.py @@ -62,6 +62,9 @@ def __init__( self.token: Optional[str] = token self._model: Optional[str] = model self._info: Optional[DeviceInfo] = None + self._status: Optional[DeviceStatus] = None + self._settings: Optional[Dict[str, SettingDescriptor]] = None + self._sensors: Optional[Dict[str, SensorDescriptor]] = None timeout = timeout if timeout is not None else self.timeout self._protocol = MiIOProtocol( ip, token, start_id, debug, lazy_discover, timeout @@ -246,14 +249,27 @@ def status(self) -> DeviceStatus: """Return device status.""" raise NotImplementedError() + + def cached_status(self) -> DeviceStatus: + """Return device status from cache.""" + if self._status is None: + self._status = self.status() + + return self._status + def actions(self) -> Dict[str, ActionDescriptor]: """Return device actions.""" return {} def settings(self) -> Dict[str, SettingDescriptor]: """Return device settings.""" - settings = self.status().settings() - for setting in settings.values(): + if self._settings is not None: + return self._settings + + self._settings = ( + self.cached_status().settings() + ) # NOTE that this already does IO so schould be run in executer job in HA + for setting in self._settings.values(): # TODO: Bind setter methods, this should probably done only once during init. if setting.setter is None: # TODO: this is ugly, how to fix the issue where setter_name is optional and thus not acceptable for getattr? @@ -270,13 +286,13 @@ def settings(self) -> Dict[str, SettingDescriptor]: retrieve_choices_function = getattr(self, setting.choices_attribute) setting.choices = retrieve_choices_function() # This can do IO - return settings + return self._settings def sensors(self) -> Dict[str, SensorDescriptor]: """Return device sensors.""" - # TODO: the latest status should be cached and re-used by all meta information getters - sensors = self.status().sensors() - return sensors + if self._sensors is None: + self._sensors = self.cached_status().sensors() + return self._sensors def __repr__(self): return f"<{self.__class__.__name__ }: {self.ip} (token: {self.token})>" From 14933c49c69ea2c9f2e9c19f048a0105d78fdc09 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Thu, 10 Nov 2022 00:15:33 +0100 Subject: [PATCH 02/40] fix black --- miio/device.py | 1 - 1 file changed, 1 deletion(-) diff --git a/miio/device.py b/miio/device.py index 3e87e46f2..e4882b702 100644 --- a/miio/device.py +++ b/miio/device.py @@ -249,7 +249,6 @@ def status(self) -> DeviceStatus: """Return device status.""" raise NotImplementedError() - def cached_status(self) -> DeviceStatus: """Return device status from cache.""" if self._status is None: From 62cf7c7a6237989be66d787f0808be7f6a0809b1 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 14 Nov 2022 22:39:04 +0100 Subject: [PATCH 03/40] improve caching --- miio/device.py | 69 +++++++++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/miio/device.py b/miio/device.py index e4882b702..02e0b8865 100644 --- a/miio/device.py +++ b/miio/device.py @@ -62,7 +62,6 @@ def __init__( self.token: Optional[str] = token self._model: Optional[str] = model self._info: Optional[DeviceInfo] = None - self._status: Optional[DeviceStatus] = None self._settings: Optional[Dict[str, SettingDescriptor]] = None self._sensors: Optional[Dict[str, SensorDescriptor]] = None timeout = timeout if timeout is not None else self.timeout @@ -129,9 +128,13 @@ def info(self, *, skip_cache=False) -> DeviceInfo: This includes information about connected wlan network, and hardware and software versions. + This also caches the descriptors for sensors, settings and actions + which makes additional IO calls. :param skip_cache bool: Skip the cache """ + self._initialize_descriptors() + if self._info is not None and not skip_cache: return self._info @@ -158,6 +161,32 @@ def _fetch_info(self) -> DeviceInfo: "Unable to request miIO.info from the device" ) from ex + def _initialize_descriptors(self) -> None: + """Cache all the descriptors once on the first call.""" + if self._sensors is not None: + return + + status = self.status() + + # Sensors + self._sensors = status.sensors() + + # Settings + self._settings = status.settings() + for setting in self._settings.values(): + if setting.setter is None: + if setting.setter_name is None: + raise Exception( + f"Neither setter or setter_name was defined for {setting}" + ) + setting.setter = getattr(self, setting.setter_name) + if ( + isinstance(setting, EnumSettingDescriptor) + and setting.choices_attribute is not None + ): + retrieve_choices_function = getattr(self, setting.choices_attribute) + setting.choices = retrieve_choices_function() + @property def device_id(self) -> int: """Return device id (did), if available.""" @@ -249,48 +278,26 @@ def status(self) -> DeviceStatus: """Return device status.""" raise NotImplementedError() - def cached_status(self) -> DeviceStatus: - """Return device status from cache.""" - if self._status is None: - self._status = self.status() - - return self._status - def actions(self) -> Dict[str, ActionDescriptor]: """Return device actions.""" return {} def settings(self) -> Dict[str, SettingDescriptor]: """Return device settings.""" - if self._settings is not None: - return self._settings - - self._settings = ( - self.cached_status().settings() - ) # NOTE that this already does IO so schould be run in executer job in HA - for setting in self._settings.values(): - # TODO: Bind setter methods, this should probably done only once during init. - if setting.setter is None: - # TODO: this is ugly, how to fix the issue where setter_name is optional and thus not acceptable for getattr? - if setting.setter_name is None: - raise Exception( - f"Neither setter or setter_name was defined for {setting}" - ) - - setting.setter = getattr(self, setting.setter_name) - if ( - isinstance(setting, EnumSettingDescriptor) - and setting.choices_attribute is not None - ): - retrieve_choices_function = getattr(self, setting.choices_attribute) - setting.choices = retrieve_choices_function() # This can do IO + if self._settings is None: + raise Exception( + "setting descriptors schould first be cached using the info() call" + ) return self._settings def sensors(self) -> Dict[str, SensorDescriptor]: """Return device sensors.""" if self._sensors is None: - self._sensors = self.cached_status().sensors() + raise Exception( + "sensor descriptors schould first be cached using the info() call" + ) + return self._sensors def __repr__(self): From 316677250b5d2c588de4ac23a4cbb706aafee039 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 14 Nov 2022 22:41:35 +0100 Subject: [PATCH 04/40] Move descriptor initialization after model is known --- miio/device.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/miio/device.py b/miio/device.py index 02e0b8865..7c1769c23 100644 --- a/miio/device.py +++ b/miio/device.py @@ -133,11 +133,11 @@ def info(self, *, skip_cache=False) -> DeviceInfo: :param skip_cache bool: Skip the cache """ - self._initialize_descriptors() - if self._info is not None and not skip_cache: return self._info + self._initialize_descriptors() + return self._fetch_info() def _fetch_info(self) -> DeviceInfo: From 690482942dbbae779ecbbccbf3bc5f3049292349 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 14 Nov 2022 22:54:15 +0100 Subject: [PATCH 05/40] fix black --- miio/device.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/miio/device.py b/miio/device.py index 7c1769c23..94eda39ab 100644 --- a/miio/device.py +++ b/miio/device.py @@ -128,7 +128,7 @@ def info(self, *, skip_cache=False) -> DeviceInfo: This includes information about connected wlan network, and hardware and software versions. - This also caches the descriptors for sensors, settings and actions + This also caches the descriptors for sensors, settings and actions which makes additional IO calls. :param skip_cache bool: Skip the cache @@ -287,7 +287,7 @@ def settings(self) -> Dict[str, SettingDescriptor]: if self._settings is None: raise Exception( "setting descriptors schould first be cached using the info() call" - ) + ) return self._settings From b2a78736de458c20951ec2c774a2d20e9b5ff719 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 14 Nov 2022 23:35:12 +0100 Subject: [PATCH 06/40] Update miio/device.py Co-authored-by: Teemu R. --- miio/device.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/miio/device.py b/miio/device.py index 94eda39ab..13e550889 100644 --- a/miio/device.py +++ b/miio/device.py @@ -128,8 +128,7 @@ def info(self, *, skip_cache=False) -> DeviceInfo: This includes information about connected wlan network, and hardware and software versions. - This also caches the descriptors for sensors, settings and actions - which makes additional IO calls. + This method implicitly populates the internal data structures needed prior accessing `sensors()`, `settings()`, or `actions()`. :param skip_cache bool: Skip the cache """ From 1dee65ab2d11a985d8d76cb7f9f69e66826b25d6 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 14 Nov 2022 23:38:56 +0100 Subject: [PATCH 07/40] Update device.py --- miio/device.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miio/device.py b/miio/device.py index 94eda39ab..cf99a3f37 100644 --- a/miio/device.py +++ b/miio/device.py @@ -181,7 +181,7 @@ def _initialize_descriptors(self) -> None: ) setting.setter = getattr(self, setting.setter_name) if ( - isinstance(setting, EnumSettingDescriptor) + setting.type == SettingType.Enum and setting.choices_attribute is not None ): retrieve_choices_function = getattr(self, setting.choices_attribute) From 67013e43ccb51f202552f0d5c1aa09508fda4b1f Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 14 Nov 2022 23:46:55 +0100 Subject: [PATCH 08/40] simplify --- miio/device.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/miio/device.py b/miio/device.py index e3cad5909..dc80c9081 100644 --- a/miio/device.py +++ b/miio/device.py @@ -128,7 +128,8 @@ def info(self, *, skip_cache=False) -> DeviceInfo: This includes information about connected wlan network, and hardware and software versions. - This method implicitly populates the internal data structures needed prior accessing `sensors()`, `settings()`, or `actions()`. + This also caches the descriptors for sensors, settings and actions + which makes additional IO calls. :param skip_cache bool: Skip the cache """ @@ -173,12 +174,12 @@ def _initialize_descriptors(self) -> None: # Settings self._settings = status.settings() for setting in self._settings.values(): - if setting.setter is None: - if setting.setter_name is None: - raise Exception( - f"Neither setter or setter_name was defined for {setting}" - ) + if setting.setter_name is not None: setting.setter = getattr(self, setting.setter_name) + if setting.setter is None: + raise Exception( + f"Neither setter or setter_name was defined for {setting}" + ) if ( setting.type == SettingType.Enum and setting.choices_attribute is not None From 54121f5165ded615a6c6f0ef1eb47eef088233de Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Tue, 15 Nov 2022 08:31:56 +0100 Subject: [PATCH 09/40] Update miio/device.py Co-authored-by: Teemu R. --- miio/device.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miio/device.py b/miio/device.py index dc80c9081..7554263d3 100644 --- a/miio/device.py +++ b/miio/device.py @@ -286,7 +286,7 @@ def settings(self) -> Dict[str, SettingDescriptor]: """Return device settings.""" if self._settings is None: raise Exception( - "setting descriptors schould first be cached using the info() call" + "setting descriptors should first be cached using the info() call" ) return self._settings From d5ec6157ebd6ae625082ca8c9fe42c3f394b07c7 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Tue, 15 Nov 2022 08:33:40 +0100 Subject: [PATCH 10/40] initialize descriptors when needed --- miio/device.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/miio/device.py b/miio/device.py index dc80c9081..96f22c0a8 100644 --- a/miio/device.py +++ b/miio/device.py @@ -285,18 +285,14 @@ def actions(self) -> Dict[str, ActionDescriptor]: def settings(self) -> Dict[str, SettingDescriptor]: """Return device settings.""" if self._settings is None: - raise Exception( - "setting descriptors schould first be cached using the info() call" - ) + self._initialize_descriptors() return self._settings def sensors(self) -> Dict[str, SensorDescriptor]: """Return device sensors.""" if self._sensors is None: - raise Exception( - "sensor descriptors schould first be cached using the info() call" - ) + self._initialize_descriptors() return self._sensors From 79b5e0eeffdd984f26116c4a8e8153027e6b6bae Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Tue, 15 Nov 2022 21:45:28 +0100 Subject: [PATCH 11/40] Move actions to _initialize_descriptors --- miio/device.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/miio/device.py b/miio/device.py index 63d2ba14a..536f565fb 100644 --- a/miio/device.py +++ b/miio/device.py @@ -189,6 +189,14 @@ def _initialize_descriptors(self) -> None: retrieve_choices_function = getattr(self, setting.choices_attribute) setting.choices = retrieve_choices_function() + # Actions + self._actions = {} + for action_tuple in getmembers(self, lambda o: hasattr(o, "_action")): + method_name, method = action_tuple + action = method._action + action.method = method # bind the method + self._actions[method_name] = action + @property def device_id(self) -> int: """Return device id (did), if available.""" @@ -283,12 +291,7 @@ def status(self) -> DeviceStatus: def actions(self) -> Dict[str, ActionDescriptor]: """Return device actions.""" if self._actions is None: - self._actions = {} - for action_tuple in getmembers(self, lambda o: hasattr(o, "_action")): - method_name, method = action_tuple - action = method._action - action.method = method # bind the method - self._actions[method_name] = action + self._initialize_descriptors() return self._actions From 4494ad13ad9662280b3079363fd65972a9769a29 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Tue, 15 Nov 2022 21:49:06 +0100 Subject: [PATCH 12/40] fix linting --- miio/device.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miio/device.py b/miio/device.py index 536f565fb..af14288a0 100644 --- a/miio/device.py +++ b/miio/device.py @@ -8,9 +8,9 @@ from .click_common import DeviceGroupMeta, LiteralParamType, command, format_output from .descriptors import ( ActionDescriptor, - EnumSettingDescriptor, SensorDescriptor, SettingDescriptor, + SettingType, ) from .deviceinfo import DeviceInfo from .devicestatus import DeviceStatus From cc010b61edd59dd25b4eb4f7f16789d9523dbb2e Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Tue, 15 Nov 2022 22:07:10 +0100 Subject: [PATCH 13/40] fix typing --- miio/device.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/miio/device.py b/miio/device.py index af14288a0..67d0fae0c 100644 --- a/miio/device.py +++ b/miio/device.py @@ -1,13 +1,14 @@ import logging from enum import Enum from inspect import getmembers -from typing import Any, Dict, List, Optional, Union # noqa: F401 +from typing import Any, cast, Dict, List, Optional, Union # noqa: F401 import click from .click_common import DeviceGroupMeta, LiteralParamType, command, format_output from .descriptors import ( ActionDescriptor, + EnumSettingDescriptor, SensorDescriptor, SettingDescriptor, SettingType, @@ -182,6 +183,7 @@ def _initialize_descriptors(self) -> None: raise Exception( f"Neither setter or setter_name was defined for {setting}" ) + setting = cast(setting, EnumSettingDescriptor) if ( setting.type == SettingType.Enum and setting.choices_attribute is not None @@ -288,21 +290,21 @@ def status(self) -> DeviceStatus: """Return device status.""" raise NotImplementedError() - def actions(self) -> Dict[str, ActionDescriptor]: + def actions(self) -> Optional[Dict[str, ActionDescriptor]]: """Return device actions.""" if self._actions is None: self._initialize_descriptors() return self._actions - def settings(self) -> Dict[str, SettingDescriptor]: + def settings(self) -> Optional[Dict[str, SettingDescriptor]]: """Return device settings.""" if self._settings is None: self._initialize_descriptors() return self._settings - def sensors(self) -> Dict[str, SensorDescriptor]: + def sensors(self) -> Optional[Dict[str, SensorDescriptor]]: """Return device sensors.""" if self._sensors is None: self._initialize_descriptors() From 96ecb458a68d0c0a21fdff50645bc999f9b0179b Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Tue, 15 Nov 2022 22:10:51 +0100 Subject: [PATCH 14/40] fix isort --- miio/device.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miio/device.py b/miio/device.py index 67d0fae0c..55fcac863 100644 --- a/miio/device.py +++ b/miio/device.py @@ -1,7 +1,7 @@ import logging from enum import Enum from inspect import getmembers -from typing import Any, cast, Dict, List, Optional, Union # noqa: F401 +from typing import Any, Dict, List, Optional, Union, cast # noqa: F401 import click From 7570e81362010ae21152777de7f9d7758571ed29 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Tue, 15 Nov 2022 22:19:59 +0100 Subject: [PATCH 15/40] fix casting --- miio/device.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miio/device.py b/miio/device.py index 55fcac863..13ce2182c 100644 --- a/miio/device.py +++ b/miio/device.py @@ -183,7 +183,7 @@ def _initialize_descriptors(self) -> None: raise Exception( f"Neither setter or setter_name was defined for {setting}" ) - setting = cast(setting, EnumSettingDescriptor) + setting = cast(EnumSettingDescriptor, setting) if ( setting.type == SettingType.Enum and setting.choices_attribute is not None From a8a1ae5514d58b8271f9df51441f225289bd4f24 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Thu, 17 Nov 2022 10:28:58 +0100 Subject: [PATCH 16/40] Do not make descriptors optional --- miio/device.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/miio/device.py b/miio/device.py index 13ce2182c..0b2d322fc 100644 --- a/miio/device.py +++ b/miio/device.py @@ -290,23 +290,26 @@ def status(self) -> DeviceStatus: """Return device status.""" raise NotImplementedError() - def actions(self) -> Optional[Dict[str, ActionDescriptor]]: + def actions(self) -> Dict[str, ActionDescriptor]: """Return device actions.""" if self._actions is None: + self._actions = {} self._initialize_descriptors() return self._actions - def settings(self) -> Optional[Dict[str, SettingDescriptor]]: + def settings(self) -> Dict[str, SettingDescriptor]: """Return device settings.""" if self._settings is None: + self._settings = {} self._initialize_descriptors() return self._settings - def sensors(self) -> Optional[Dict[str, SensorDescriptor]]: + def sensors(self) -> Dict[str, SensorDescriptor]: """Return device sensors.""" if self._sensors is None: + self._sensors = {} self._initialize_descriptors() return self._sensors From ba136e268025923147da8c53396c0438ac157b21 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Thu, 17 Nov 2022 10:37:59 +0100 Subject: [PATCH 17/40] Split out action/setting/sensor descriptor retrieval --- miio/device.py | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/miio/device.py b/miio/device.py index 0b2d322fc..839c3cefc 100644 --- a/miio/device.py +++ b/miio/device.py @@ -164,19 +164,10 @@ def _fetch_info(self) -> DeviceInfo: "Unable to request miIO.info from the device" ) from ex - def _initialize_descriptors(self) -> None: - """Cache all the descriptors once on the first call.""" - if self._sensors is not None: - return - - status = self.status() - - # Sensors - self._sensors = status.sensors() - - # Settings - self._settings = status.settings() - for setting in self._settings.values(): + def _setting_descriptors_from_status(self, status: DeviceStatus) -> Dict[str, SettingDescriptor]: + """Get the setting descriptors from a DeviceStatus.""" + settings = status.settings() + for setting in settings.values(): if setting.setter_name is not None: setting.setter = getattr(self, setting.setter_name) if setting.setter is None: @@ -191,13 +182,33 @@ def _initialize_descriptors(self) -> None: retrieve_choices_function = getattr(self, setting.choices_attribute) setting.choices = retrieve_choices_function() - # Actions - self._actions = {} + return settings + + def _sensor_descriptors_from_status(self, status: DeviceStatus) -> Dict[str, SensorDescriptor]: + """Get the sensor descriptors from a DeviceStatus.""" + return status.sensors() + + def _action_descriptors(self) -> Dict[str, ActionDescriptor]: + """Get the action descriptors from a DeviceStatus.""" + actions = {} for action_tuple in getmembers(self, lambda o: hasattr(o, "_action")): method_name, method = action_tuple action = method._action action.method = method # bind the method - self._actions[method_name] = action + actions[method_name] = action + + return actions + + def _initialize_descriptors(self) -> None: + """Cache all the descriptors once on the first call.""" + if self._sensors is not None: + return + + status = self.status() + + self._sensors = self._sensor_descriptors_from_status(status) + self._settings = self._setting_descriptors_from_status(status) + self._actions = self._action_descriptors() @property def device_id(self) -> int: From db24017c14429d41c9e5c63a262e6b3dec4971a4 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Thu, 17 Nov 2022 11:10:52 +0100 Subject: [PATCH 18/40] Update dummies.py --- miio/tests/dummies.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/miio/tests/dummies.py b/miio/tests/dummies.py index 730a9a882..6a4c1587e 100644 --- a/miio/tests/dummies.py +++ b/miio/tests/dummies.py @@ -37,6 +37,9 @@ def __init__(self, *args, **kwargs): self.start_state = self.state.copy() self._protocol = DummyMiIOProtocol(self) self._info = None + self._settings = None + self._sensors = None + self._actions = None # TODO: ugly hack to check for pre-existing _model if getattr(self, "_model", None) is None: self._model = "dummy.model" From 9c9493cb0c735becfd8d910edd13b7d535693a74 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Thu, 17 Nov 2022 11:16:48 +0100 Subject: [PATCH 19/40] Move _initialize_descriptors out of info call --- miio/device.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/miio/device.py b/miio/device.py index 839c3cefc..bc6a3a4a2 100644 --- a/miio/device.py +++ b/miio/device.py @@ -139,10 +139,13 @@ def info(self, *, skip_cache=False) -> DeviceInfo: if self._info is not None and not skip_cache: return self._info - self._initialize_descriptors() - return self._fetch_info() + def initialize(self) -> None: + """Initialize the device by caching information.""" + self.info() + self._initialize_descriptors() + def _fetch_info(self) -> DeviceInfo: """Perform miIO.info query on the device and cache the result.""" try: From f31c701f5b090110e55e201655c8348b2756eae0 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Fri, 18 Nov 2022 15:01:12 +0100 Subject: [PATCH 20/40] fix black --- miio/device.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/miio/device.py b/miio/device.py index bc6a3a4a2..8d8ff9e5d 100644 --- a/miio/device.py +++ b/miio/device.py @@ -167,7 +167,9 @@ def _fetch_info(self) -> DeviceInfo: "Unable to request miIO.info from the device" ) from ex - def _setting_descriptors_from_status(self, status: DeviceStatus) -> Dict[str, SettingDescriptor]: + def _setting_descriptors_from_status( + self, status: DeviceStatus + ) -> Dict[str, SettingDescriptor]: """Get the setting descriptors from a DeviceStatus.""" settings = status.settings() for setting in settings.values(): @@ -187,7 +189,9 @@ def _setting_descriptors_from_status(self, status: DeviceStatus) -> Dict[str, Se return settings - def _sensor_descriptors_from_status(self, status: DeviceStatus) -> Dict[str, SensorDescriptor]: + def _sensor_descriptors_from_status( + self, status: DeviceStatus + ) -> Dict[str, SensorDescriptor]: """Get the sensor descriptors from a DeviceStatus.""" return status.sensors() From a29c05d5b118248584af9c6b80c5641631763a45 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Fri, 18 Nov 2022 15:06:22 +0100 Subject: [PATCH 21/40] update descriptions --- miio/device.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/miio/device.py b/miio/device.py index 8d8ff9e5d..cdf006c00 100644 --- a/miio/device.py +++ b/miio/device.py @@ -131,8 +131,6 @@ def info(self, *, skip_cache=False) -> DeviceInfo: This includes information about connected wlan network, and hardware and software versions. - This also caches the descriptors for sensors, settings and actions - which makes additional IO calls. :param skip_cache bool: Skip the cache """ @@ -142,7 +140,10 @@ def info(self, *, skip_cache=False) -> DeviceInfo: return self._fetch_info() def initialize(self) -> None: - """Initialize the device by caching information.""" + """ + This method implicitly populates the internal data structures needed + prior accessing `info()`, `sensors()`, `settings()`, or `actions()`. + """ self.info() self._initialize_descriptors() From c2691bc0499965224a41c024f1143f237aabe212 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Fri, 18 Nov 2022 17:52:29 +0100 Subject: [PATCH 22/40] fix docstring --- miio/device.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/miio/device.py b/miio/device.py index cdf006c00..b4cacd481 100644 --- a/miio/device.py +++ b/miio/device.py @@ -140,10 +140,8 @@ def info(self, *, skip_cache=False) -> DeviceInfo: return self._fetch_info() def initialize(self) -> None: - """ - This method implicitly populates the internal data structures needed - prior accessing `info()`, `sensors()`, `settings()`, or `actions()`. - """ + """This method implicitly populates the internal data structures needed + prior accessing `info()`, `sensors()`, `settings()`, or `actions()`.""" self.info() self._initialize_descriptors() From 15b67f96c48372ab745fe303ded6ab604c677772 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Fri, 18 Nov 2022 18:18:45 +0100 Subject: [PATCH 23/40] docformatter --- miio/device.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/miio/device.py b/miio/device.py index b4cacd481..5e5ed2309 100644 --- a/miio/device.py +++ b/miio/device.py @@ -31,9 +31,10 @@ class UpdateState(Enum): class Device(metaclass=DeviceGroupMeta): """Base class for all device implementations. - This is the main class providing the basic protocol handling for devices using the - ``miIO`` protocol. This class should not be initialized directly but a device- - specific class inheriting it should be used instead of it. + This is the main class providing the basic protocol handling for + devices using the ``miIO`` protocol. This class should not be + initialized directly but a device- specific class inheriting it + should be used instead of it. """ retry_count = 3 @@ -109,8 +110,8 @@ def send_handshake(self): click.argument("parameters", type=LiteralParamType(), required=False), ) def raw_command(self, command, parameters): - """Send a raw command to the device. This is mostly useful when trying out - commands which are not implemented by a given device instance. + """Send a raw command to the device. This is mostly useful when trying + out commands which are not implemented by a given device instance. :param str command: Command to send :param dict parameters: Parameters to send From 1ebb309a05ec1a7ae8307b5f1876f72f9cd0be0d Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 28 Nov 2022 17:36:21 +0100 Subject: [PATCH 24/40] revert docstring changes --- miio/device.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/miio/device.py b/miio/device.py index 5a82c11fa..0690d103d 100644 --- a/miio/device.py +++ b/miio/device.py @@ -32,10 +32,9 @@ class UpdateState(Enum): class Device(metaclass=DeviceGroupMeta): """Base class for all device implementations. - This is the main class providing the basic protocol handling for - devices using the ``miIO`` protocol. This class should not be - initialized directly but a device- specific class inheriting it - should be used instead of it. + This is the main class providing the basic protocol handling for devices using the + ``miIO`` protocol. This class should not be initialized directly but a device- + specific class inheriting it should be used instead of it. """ retry_count = 3 @@ -111,8 +110,8 @@ def send_handshake(self): click.argument("parameters", type=LiteralParamType(), required=False), ) def raw_command(self, command, parameters): - """Send a raw command to the device. This is mostly useful when trying - out commands which are not implemented by a given device instance. + """Send a raw command to the device. This is mostly useful when trying out + commands which are not implemented by a given device instance. :param str command: Command to send :param dict parameters: Parameters to send From 441de02a5de0e2ffc172e2fe947669a50832d980 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 28 Nov 2022 17:52:40 +0100 Subject: [PATCH 25/40] do not add initialize method --- miio/airdehumidifier.py | 2 +- miio/airqualitymonitor.py | 2 +- miio/device.py | 15 +++++++-------- miio/gateway/gateway.py | 2 +- .../humidifier/zhimi/airhumidifier.py | 2 +- miio/integrations/vacuum/roborock/vacuum.py | 2 +- miio/integrations/vacuum/roborock/vacuum_tui.py | 2 +- miio/wifirepeater.py | 4 ++-- 8 files changed, 15 insertions(+), 16 deletions(-) diff --git a/miio/airdehumidifier.py b/miio/airdehumidifier.py index 15fd1b620..362bfae4b 100644 --- a/miio/airdehumidifier.py +++ b/miio/airdehumidifier.py @@ -185,7 +185,7 @@ def status(self) -> AirDehumidifierStatus: values = self.get_properties(properties, max_properties=1) return AirDehumidifierStatus( - defaultdict(lambda: None, zip(properties, values)), self.info() + defaultdict(lambda: None, zip(properties, values)), self._fetch_info() ) @command(default_output=format_output("Powering on")) diff --git a/miio/airqualitymonitor.py b/miio/airqualitymonitor.py index f44ea0ec9..4ead34b21 100644 --- a/miio/airqualitymonitor.py +++ b/miio/airqualitymonitor.py @@ -172,7 +172,7 @@ def status(self) -> AirQualityMonitorStatus: is_s1_firmware_version_4 = ( self.model == MODEL_AIRQUALITYMONITOR_S1 - and self.info().firmware_version.startswith("4") + and self._fetch_info().firmware_version.startswith("4") ) if is_s1_firmware_version_4 and "battery" in properties: properties.remove("battery") diff --git a/miio/device.py b/miio/device.py index 0690d103d..c2bf3616b 100644 --- a/miio/device.py +++ b/miio/device.py @@ -135,19 +135,18 @@ def info(self, *, skip_cache=False) -> DeviceInfo: :param skip_cache bool: Skip the cache """ + self._initialize_descriptors() + if self._info is not None and not skip_cache: return self._info return self._fetch_info() - def initialize(self) -> None: - """This method implicitly populates the internal data structures needed - prior accessing `info()`, `sensors()`, `settings()`, or `actions()`.""" - self.info() - self._initialize_descriptors() - - def _fetch_info(self) -> DeviceInfo: + def _fetch_info(self, *, skip_cache=False) -> DeviceInfo: """Perform miIO.info query on the device and cache the result.""" + if self._info is not None and not skip_cache: + return self._info + try: devinfo = DeviceInfo(self.send("miIO.info")) self._info = devinfo @@ -247,7 +246,7 @@ def model(self) -> str: if self._model is not None: return self._model - return self.info().model + return self._fetch_info().model def update(self, url: str, md5: str): """Start an OTA update.""" diff --git a/miio/gateway/gateway.py b/miio/gateway/gateway.py index cbc6333e9..758ad9390 100644 --- a/miio/gateway/gateway.py +++ b/miio/gateway/gateway.py @@ -153,7 +153,7 @@ def devices(self): def mac(self): """Return the mac address of the gateway.""" if self._info is None: - self._info = self.info() + self._info = self._fetch_info() return self._info.mac_address @property diff --git a/miio/integrations/humidifier/zhimi/airhumidifier.py b/miio/integrations/humidifier/zhimi/airhumidifier.py index 3045e27a8..5fab21716 100644 --- a/miio/integrations/humidifier/zhimi/airhumidifier.py +++ b/miio/integrations/humidifier/zhimi/airhumidifier.py @@ -370,7 +370,7 @@ def status(self) -> AirHumidifierStatus: values = self.get_properties(properties, max_properties=_props_per_request) return AirHumidifierStatus( - defaultdict(lambda: None, zip(properties, values)), self.info() + defaultdict(lambda: None, zip(properties, values)), self._fetch_info() ) @command(default_output=format_output("Powering on")) diff --git a/miio/integrations/vacuum/roborock/vacuum.py b/miio/integrations/vacuum/roborock/vacuum.py index c66f7e9ed..3283b900d 100644 --- a/miio/integrations/vacuum/roborock/vacuum.py +++ b/miio/integrations/vacuum/roborock/vacuum.py @@ -596,7 +596,7 @@ def _enum_as_dict(cls): if self.model == ROCKROBO_V1: _LOGGER.debug("Got robov1, checking for firmware version") - fw_version = self.info().firmware_version + fw_version = self._fetch_info().firmware_version version, build = fw_version.split("_") version = tuple(map(int, version.split("."))) if version >= (3, 5, 8): diff --git a/miio/integrations/vacuum/roborock/vacuum_tui.py b/miio/integrations/vacuum/roborock/vacuum_tui.py index 1c0e2de01..bd62ec51a 100644 --- a/miio/integrations/vacuum/roborock/vacuum_tui.py +++ b/miio/integrations/vacuum/roborock/vacuum_tui.py @@ -70,7 +70,7 @@ def handle_key(self, key: str) -> Tuple[str, bool]: return f"Ignoring {key}: {e}.\n", False done = self.dispatch_control(ctl) - return self.info(), done + return self._fetch_info(), done def dispatch_control(self, ctl: Control) -> bool: if ctl == Control.Quit: diff --git a/miio/wifirepeater.py b/miio/wifirepeater.py index ab98677ac..4e4a58475 100644 --- a/miio/wifirepeater.py +++ b/miio/wifirepeater.py @@ -132,9 +132,9 @@ def set_configuration(self, ssid: str, password: str, ssid_hidden: bool = False) ) def wifi_roaming(self) -> bool: """Return the roaming setting.""" - return self.info().raw["desc"]["wifi_explorer"] == 1 + return self._fetch_info().raw["desc"]["wifi_explorer"] == 1 @command(default_output=format_output("RSSI of the accesspoint: {result}")) def rssi_accesspoint(self) -> int: """Received signal strength indicator of the accesspoint.""" - return self.info().accesspoint["rssi"] + return self._fetch_info().accesspoint["rssi"] From d5c8a6c5ad07bb056a544c0dc522195e3ff708fe Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 28 Nov 2022 18:01:09 +0100 Subject: [PATCH 26/40] fix typing --- miio/device.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/miio/device.py b/miio/device.py index c2bf3616b..4429a34b1 100644 --- a/miio/device.py +++ b/miio/device.py @@ -317,25 +317,25 @@ def status(self) -> DeviceStatus: def actions(self) -> Dict[str, ActionDescriptor]: """Return device actions.""" if self._actions is None: - self._actions = {} self._initialize_descriptors() + self._actions = cast(Dict[str, ActionDescriptor], self._actions) return self._actions def settings(self) -> Dict[str, SettingDescriptor]: """Return device settings.""" if self._settings is None: - self._settings = {} self._initialize_descriptors() + self._settings = cast(Dict[str, SettingDescriptor], self._settings) return self._settings def sensors(self) -> Dict[str, SensorDescriptor]: """Return device sensors.""" if self._sensors is None: - self._sensors = {} self._initialize_descriptors() + self._sensors = cast(Dict[str, SensorDescriptor], self._sensors) return self._sensors def __repr__(self): From 1727060f51cfea8109400d8e3be2ec42fa144eef Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 28 Nov 2022 18:02:57 +0100 Subject: [PATCH 27/40] fix black --- miio/device.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miio/device.py b/miio/device.py index 4429a34b1..88b19994c 100644 --- a/miio/device.py +++ b/miio/device.py @@ -136,7 +136,7 @@ def info(self, *, skip_cache=False) -> DeviceInfo: :param skip_cache bool: Skip the cache """ self._initialize_descriptors() - + if self._info is not None and not skip_cache: return self._info From 7d17f085ef9f38d3b0123d1860e68ba7c7133c96 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 28 Nov 2022 18:43:54 +0100 Subject: [PATCH 28/40] fix mypy None possibilities --- miio/airqualitymonitor.py | 6 +++++- miio/deviceinfo.py | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/miio/airqualitymonitor.py b/miio/airqualitymonitor.py index 4ead34b21..a730ac28c 100644 --- a/miio/airqualitymonitor.py +++ b/miio/airqualitymonitor.py @@ -170,9 +170,13 @@ def status(self) -> AirQualityMonitorStatus: self.model, AVAILABLE_PROPERTIES[MODEL_AIRQUALITYMONITOR_V1] ) + firmware_version = self._fetch_info().firmware_version + if firmware_version is None: + firmware_version = "unknown" + is_s1_firmware_version_4 = ( self.model == MODEL_AIRQUALITYMONITOR_S1 - and self._fetch_info().firmware_version.startswith("4") + and firmware_version.startswith("4") ) if is_s1_firmware_version_4 and "battery" in properties: properties.remove("battery") diff --git a/miio/deviceinfo.py b/miio/deviceinfo.py index fdff5f91c..639245f84 100644 --- a/miio/deviceinfo.py +++ b/miio/deviceinfo.py @@ -56,9 +56,9 @@ def accesspoint(self): return self.data.get("ap", {}) @property - def model(self) -> Optional[str]: + def model(self) -> str: """Model string if available.""" - return self.data.get("model") + return self.data.get("model", "Unknown") @property def firmware_version(self) -> Optional[str]: From c92be40fafd6083afe2b101fcc63460650ccdbbe Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 28 Nov 2022 19:04:58 +0100 Subject: [PATCH 29/40] fixes --- miio/integrations/vacuum/roborock/vacuum.py | 5 ++++- miio/integrations/vacuum/roborock/vacuum_tui.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/miio/integrations/vacuum/roborock/vacuum.py b/miio/integrations/vacuum/roborock/vacuum.py index 3283b900d..1deafa438 100644 --- a/miio/integrations/vacuum/roborock/vacuum.py +++ b/miio/integrations/vacuum/roborock/vacuum.py @@ -175,12 +175,15 @@ def resume_or_start(self): return self.start() - def _fetch_info(self) -> DeviceInfo: + def _fetch_info(self, *, skip_cache=False) -> DeviceInfo: """Return info about the device. This is overrides the base class info to account for gen1 devices that do not respond to info query properly when not connected to the cloud. """ + if self._info is not None and not skip_cache: + return self._info + try: info = super()._fetch_info() return info diff --git a/miio/integrations/vacuum/roborock/vacuum_tui.py b/miio/integrations/vacuum/roborock/vacuum_tui.py index bd62ec51a..1c0e2de01 100644 --- a/miio/integrations/vacuum/roborock/vacuum_tui.py +++ b/miio/integrations/vacuum/roborock/vacuum_tui.py @@ -70,7 +70,7 @@ def handle_key(self, key: str) -> Tuple[str, bool]: return f"Ignoring {key}: {e}.\n", False done = self.dispatch_control(ctl) - return self._fetch_info(), done + return self.info(), done def dispatch_control(self, ctl: Control) -> bool: if ctl == Control.Quit: From 58f72b36a17359a916b6e97951a583e688904f1e Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 28 Nov 2022 19:11:25 +0100 Subject: [PATCH 30/40] fix mypy --- miio/integrations/vacuum/roborock/vacuum.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/miio/integrations/vacuum/roborock/vacuum.py b/miio/integrations/vacuum/roborock/vacuum.py index 1deafa438..577716727 100644 --- a/miio/integrations/vacuum/roborock/vacuum.py +++ b/miio/integrations/vacuum/roborock/vacuum.py @@ -600,12 +600,15 @@ def _enum_as_dict(cls): if self.model == ROCKROBO_V1: _LOGGER.debug("Got robov1, checking for firmware version") fw_version = self._fetch_info().firmware_version - version, build = fw_version.split("_") - version = tuple(map(int, version.split("."))) - if version >= (3, 5, 8): - fanspeeds = FanspeedV3 - elif version == (3, 5, 7): - fanspeeds = FanspeedV2 + if fw_version is not None: + version, build = fw_version.split("_") + version = tuple(map(int, version.split("."))) + if version >= (3, 5, 8): + fanspeeds = FanspeedV3 + elif version == (3, 5, 7): + fanspeeds = FanspeedV2 + else: + fanspeeds = FanspeedV1 else: fanspeeds = FanspeedV1 elif self.model == ROCKROBO_E2: From ddb0e82060a4aabb8ac4a35d680cadfae8b956d3 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 28 Nov 2022 19:21:42 +0100 Subject: [PATCH 31/40] fix mypy complaining --- miio/integrations/vacuum/roborock/vacuum.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/miio/integrations/vacuum/roborock/vacuum.py b/miio/integrations/vacuum/roborock/vacuum.py index 577716727..b8f02eccf 100644 --- a/miio/integrations/vacuum/roborock/vacuum.py +++ b/miio/integrations/vacuum/roborock/vacuum.py @@ -602,10 +602,10 @@ def _enum_as_dict(cls): fw_version = self._fetch_info().firmware_version if fw_version is not None: version, build = fw_version.split("_") - version = tuple(map(int, version.split("."))) - if version >= (3, 5, 8): + version_tuple = tuple(map(int, version.split("."))) + if version_tuple >= (3, 5, 8): fanspeeds = FanspeedV3 - elif version == (3, 5, 7): + elif version_tuple == (3, 5, 7): fanspeeds = FanspeedV2 else: fanspeeds = FanspeedV1 From 587194fac6463152d63caca2641144751cb202a0 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 28 Nov 2022 19:41:20 +0100 Subject: [PATCH 32/40] fix tests --- miio/tests/test_airdehumidifier.py | 2 +- miio/tests/test_wifirepeater.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/miio/tests/test_airdehumidifier.py b/miio/tests/test_airdehumidifier.py index 5e53f53f2..f52c506e9 100644 --- a/miio/tests/test_airdehumidifier.py +++ b/miio/tests/test_airdehumidifier.py @@ -64,7 +64,7 @@ def __init__(self, *args, **kwargs): "set_child_lock": lambda x: self._set_state("child_lock", x), "set_fan_speed": lambda x: self._set_state("fan_st", x), "set_auto": lambda x: self._set_state("auto", x), - "miIO.info": self._get_device_info, + "miIO._fetch_info": self._get_device_info, } super().__init__(args, kwargs) diff --git a/miio/tests/test_wifirepeater.py b/miio/tests/test_wifirepeater.py index 5fca85636..e4188ed16 100644 --- a/miio/tests/test_wifirepeater.py +++ b/miio/tests/test_wifirepeater.py @@ -70,7 +70,7 @@ def __init__(self, *args, **kwargs): "miIO.get_repeater_ap_info": self._get_configuration, "miIO.switch_wifi_explorer": self._set_wifi_explorer, "miIO.switch_wifi_ssid": self._set_configuration, - "miIO.info": self._get_info, + "miIO._fetch_info": self._get_info, } self.start_state = self.state.copy() self.start_config = self.config.copy() From 480f384fabdc9ae8f8110a3fb25ab12b512db5df Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 28 Nov 2022 19:44:37 +0100 Subject: [PATCH 33/40] do not change model can be None --- miio/device.py | 2 +- miio/deviceinfo.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/miio/device.py b/miio/device.py index 88b19994c..163d4dfe0 100644 --- a/miio/device.py +++ b/miio/device.py @@ -241,7 +241,7 @@ def supported_models(self) -> List[str]: return list(self._mappings.keys()) or self._supported_models @property - def model(self) -> str: + def model(self) -> Optional[str]: """Return device model.""" if self._model is not None: return self._model diff --git a/miio/deviceinfo.py b/miio/deviceinfo.py index 639245f84..fdff5f91c 100644 --- a/miio/deviceinfo.py +++ b/miio/deviceinfo.py @@ -56,9 +56,9 @@ def accesspoint(self): return self.data.get("ap", {}) @property - def model(self) -> str: + def model(self) -> Optional[str]: """Model string if available.""" - return self.data.get("model", "Unknown") + return self.data.get("model") @property def firmware_version(self) -> Optional[str]: From 82d3782ab4e730fc65ef713df569355e0684b7c7 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 28 Nov 2022 21:34:22 +0100 Subject: [PATCH 34/40] cast model --- miio/device.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/miio/device.py b/miio/device.py index 163d4dfe0..1a44b72d6 100644 --- a/miio/device.py +++ b/miio/device.py @@ -241,12 +241,12 @@ def supported_models(self) -> List[str]: return list(self._mappings.keys()) or self._supported_models @property - def model(self) -> Optional[str]: + def model(self) -> str: """Return device model.""" if self._model is not None: return self._model - return self._fetch_info().model + return cast(str, self._fetch_info().model) def update(self, url: str, md5: str): """Start an OTA update.""" From a43614c034820ce0b23db04273fba5f34b5dede6 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 28 Nov 2022 21:45:12 +0100 Subject: [PATCH 35/40] try fixing tests --- miio/tests/dummies.py | 6 +++--- miio/tests/test_airdehumidifier.py | 2 +- miio/tests/test_wifirepeater.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/miio/tests/dummies.py b/miio/tests/dummies.py index 6a4c1587e..eb99c243e 100644 --- a/miio/tests/dummies.py +++ b/miio/tests/dummies.py @@ -37,9 +37,9 @@ def __init__(self, *args, **kwargs): self.start_state = self.state.copy() self._protocol = DummyMiIOProtocol(self) self._info = None - self._settings = None - self._sensors = None - self._actions = None + self._settings = {} + self._sensors = {} + self._actions = {} # TODO: ugly hack to check for pre-existing _model if getattr(self, "_model", None) is None: self._model = "dummy.model" diff --git a/miio/tests/test_airdehumidifier.py b/miio/tests/test_airdehumidifier.py index f52c506e9..5e53f53f2 100644 --- a/miio/tests/test_airdehumidifier.py +++ b/miio/tests/test_airdehumidifier.py @@ -64,7 +64,7 @@ def __init__(self, *args, **kwargs): "set_child_lock": lambda x: self._set_state("child_lock", x), "set_fan_speed": lambda x: self._set_state("fan_st", x), "set_auto": lambda x: self._set_state("auto", x), - "miIO._fetch_info": self._get_device_info, + "miIO.info": self._get_device_info, } super().__init__(args, kwargs) diff --git a/miio/tests/test_wifirepeater.py b/miio/tests/test_wifirepeater.py index e4188ed16..5fca85636 100644 --- a/miio/tests/test_wifirepeater.py +++ b/miio/tests/test_wifirepeater.py @@ -70,7 +70,7 @@ def __init__(self, *args, **kwargs): "miIO.get_repeater_ap_info": self._get_configuration, "miIO.switch_wifi_explorer": self._set_wifi_explorer, "miIO.switch_wifi_ssid": self._set_configuration, - "miIO._fetch_info": self._get_info, + "miIO.info": self._get_info, } self.start_state = self.state.copy() self.start_config = self.config.copy() From 0f01e36720001fc692a12d265fd029c2105abce6 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 28 Nov 2022 22:23:17 +0100 Subject: [PATCH 36/40] try to fix tests --- miio/tests/test_airqualitymonitor.py | 2 +- miio/tests/test_devicestatus.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/miio/tests/test_airqualitymonitor.py b/miio/tests/test_airqualitymonitor.py index 5f24fa3da..c870fd776 100644 --- a/miio/tests/test_airqualitymonitor.py +++ b/miio/tests/test_airqualitymonitor.py @@ -100,7 +100,7 @@ def _get_state(self, props): """Return wanted properties.""" return self.state - def info(self): + def _fetch_info(self): return DummyDeviceInfo(version=self.version) diff --git a/miio/tests/test_devicestatus.py b/miio/tests/test_devicestatus.py index 4f6baaa45..595365b46 100644 --- a/miio/tests/test_devicestatus.py +++ b/miio/tests/test_devicestatus.py @@ -122,6 +122,7 @@ def level(self) -> int: return 1 mocker.patch("miio.Device.send") + mocker.patch("miio.Device.send_handshake") d = Device("127.0.0.1", "68ffffffffffffffffffffffffffffff") # Patch status to return our class From 2c79fe9d94abf29a9d791d2b77ee0bcbc6a022a5 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Tue, 29 Nov 2022 00:57:05 +0100 Subject: [PATCH 37/40] try fixing tests --- miio/tests/test_airqualitymonitor.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/miio/tests/test_airqualitymonitor.py b/miio/tests/test_airqualitymonitor.py index c870fd776..9d0c8f916 100644 --- a/miio/tests/test_airqualitymonitor.py +++ b/miio/tests/test_airqualitymonitor.py @@ -35,6 +35,9 @@ def __init__(self, *args, **kwargs): } super().__init__(args, kwargs) + def _fetch_info(self): + return DummyDeviceInfo(version=self.version) + @pytest.fixture(scope="class") def airqualitymonitorv1(request): From 99a7f11ffd816251e664be91ddb3f839dcf3533a Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 5 Dec 2022 14:57:28 +0100 Subject: [PATCH 38/40] try fixing tests --- miio/tests/test_airqualitymonitor.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/miio/tests/test_airqualitymonitor.py b/miio/tests/test_airqualitymonitor.py index 9d0c8f916..72daa3bac 100644 --- a/miio/tests/test_airqualitymonitor.py +++ b/miio/tests/test_airqualitymonitor.py @@ -16,6 +16,7 @@ class DummyAirQualityMonitorV1(DummyDevice, AirQualityMonitor): def __init__(self, *args, **kwargs): self._model = MODEL_AIRQUALITYMONITOR_V1 + self.version = "3.1.8_9999" self.state = { "power": "on", "aqi": 34, @@ -188,6 +189,7 @@ def test_status(self): class DummyAirQualityMonitorB1(DummyDevice, AirQualityMonitor): def __init__(self, *args, **kwargs): self._model = MODEL_AIRQUALITYMONITOR_B1 + self.version = "3.1.8_9999" self.state = { "co2e": 1466, "humidity": 59.79999923706055, @@ -204,6 +206,9 @@ def _get_state(self, props): """Return wanted properties.""" return self.state + def _fetch_info(self): + return DummyDeviceInfo(version=self.version) + @pytest.fixture(scope="class") def airqualitymonitorb1(request): From 051748cda807740cea8f9dd1e4f4db555f3aeb7c Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 5 Dec 2022 15:05:36 +0100 Subject: [PATCH 39/40] fix tests device_id query --- miio/tests/dummies.py | 1 + 1 file changed, 1 insertion(+) diff --git a/miio/tests/dummies.py b/miio/tests/dummies.py index eb99c243e..b874e14e2 100644 --- a/miio/tests/dummies.py +++ b/miio/tests/dummies.py @@ -5,6 +5,7 @@ def __init__(self, dummy_device): # TODO: Ideally, return_values should be passed in here. Passing in dummy_device (which must have # return_values) is a temporary workaround to minimize diff size. self.dummy_device = dummy_device + self._device_id = b"12345678" def send(self, command: str, parameters=None, retry_count=3, extra_parameters=None): """Overridden send() to return values from `self.return_values`.""" From 7f2e16346e902c627894a83fc9e76449824f6e0d Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 5 Dec 2022 15:22:00 +0100 Subject: [PATCH 40/40] try fixing tests --- miio/tests/dummies.py | 1 - miio/tests/test_devicestatus.py | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/miio/tests/dummies.py b/miio/tests/dummies.py index b874e14e2..eb99c243e 100644 --- a/miio/tests/dummies.py +++ b/miio/tests/dummies.py @@ -5,7 +5,6 @@ def __init__(self, dummy_device): # TODO: Ideally, return_values should be passed in here. Passing in dummy_device (which must have # return_values) is a temporary workaround to minimize diff size. self.dummy_device = dummy_device - self._device_id = b"12345678" def send(self, command: str, parameters=None, retry_count=3, extra_parameters=None): """Overridden send() to return values from `self.return_values`.""" diff --git a/miio/tests/test_devicestatus.py b/miio/tests/test_devicestatus.py index 595365b46..7e1503358 100644 --- a/miio/tests/test_devicestatus.py +++ b/miio/tests/test_devicestatus.py @@ -168,7 +168,9 @@ def level(self) -> int: return 1 mocker.patch("miio.Device.send") + mocker.patch("miio.Device.send_handshake") d = Device("127.0.0.1", "68ffffffffffffffffffffffffffffff") + d._protocol._device_id = b"12345678" # Patch status to return our class mocker.patch.object(d, "status", return_value=Settings()) @@ -209,7 +211,9 @@ def level(self) -> TestEnum: return TestEnum.First mocker.patch("miio.Device.send") + mocker.patch("miio.Device.send_handshake") d = Device("127.0.0.1", "68ffffffffffffffffffffffffffffff") + d._protocol._device_id = b"12345678" # Patch status to return our class mocker.patch.object(d, "status", return_value=Settings())