Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initialize and cache descriptors only once #1592

Closed
wants to merge 48 commits into from
Closed
Changes from 15 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
c99de9b
Add caching
starkillerOG Nov 9, 2022
14933c4
fix black
starkillerOG Nov 9, 2022
62cf7c7
improve caching
starkillerOG Nov 14, 2022
3166772
Move descriptor initialization after model is known
starkillerOG Nov 14, 2022
ba83361
Merge branch 'master' into caching
starkillerOG Nov 14, 2022
6904829
fix black
starkillerOG Nov 14, 2022
0fe1954
Merge branch 'caching' of https://github.com/starkillerOG/python-miio…
starkillerOG Nov 14, 2022
b2a7873
Update miio/device.py
starkillerOG Nov 14, 2022
1dee65a
Update device.py
starkillerOG Nov 14, 2022
a7fcbd8
Merge branch 'caching' of https://github.com/starkillerOG/python-miio…
starkillerOG Nov 14, 2022
67013e4
simplify
starkillerOG Nov 14, 2022
2bcb4ba
Merge branch 'master' into caching
starkillerOG Nov 14, 2022
54121f5
Update miio/device.py
starkillerOG Nov 15, 2022
d5ec615
initialize descriptors when needed
starkillerOG Nov 15, 2022
391e42f
Merge branch 'caching' of https://github.com/starkillerOG/python-miio…
starkillerOG Nov 15, 2022
86d6e69
Merge branch 'master' into caching
starkillerOG Nov 15, 2022
79b5e0e
Move actions to _initialize_descriptors
starkillerOG Nov 15, 2022
4494ad1
fix linting
starkillerOG Nov 15, 2022
cc010b6
fix typing
starkillerOG Nov 15, 2022
96ecb45
fix isort
starkillerOG Nov 15, 2022
7570e81
fix casting
starkillerOG Nov 15, 2022
a8a1ae5
Do not make descriptors optional
starkillerOG Nov 17, 2022
ba136e2
Split out action/setting/sensor descriptor retrieval
starkillerOG Nov 17, 2022
db24017
Update dummies.py
starkillerOG Nov 17, 2022
9c9493c
Move _initialize_descriptors out of info call
starkillerOG Nov 17, 2022
f31c701
fix black
starkillerOG Nov 18, 2022
a29c05d
update descriptions
starkillerOG Nov 18, 2022
c2691bc
fix docstring
starkillerOG Nov 18, 2022
15b67f9
docformatter
starkillerOG Nov 18, 2022
97d4784
Merge branch 'master' into caching
starkillerOG Nov 27, 2022
1ebb309
revert docstring changes
starkillerOG Nov 28, 2022
d03c6f2
Merge branch 'master' into caching
starkillerOG Nov 28, 2022
441de02
do not add initialize method
starkillerOG Nov 28, 2022
d5c8a6c
fix typing
starkillerOG Nov 28, 2022
1727060
fix black
starkillerOG Nov 28, 2022
7d17f08
fix mypy None possibilities
starkillerOG Nov 28, 2022
c92be40
fixes
starkillerOG Nov 28, 2022
58f72b3
fix mypy
starkillerOG Nov 28, 2022
ddb0e82
fix mypy complaining
starkillerOG Nov 28, 2022
587194f
fix tests
starkillerOG Nov 28, 2022
480f384
do not change model can be None
starkillerOG Nov 28, 2022
82d3782
cast model
starkillerOG Nov 28, 2022
a43614c
try fixing tests
starkillerOG Nov 28, 2022
0f01e36
try to fix tests
starkillerOG Nov 28, 2022
2c79fe9
try fixing tests
starkillerOG Nov 28, 2022
99a7f11
try fixing tests
starkillerOG Dec 5, 2022
051748c
fix tests device_id query
starkillerOG Dec 5, 2022
7f2e163
try fixing tests
starkillerOG Dec 5, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 39 additions & 21 deletions miio/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ def __init__(
self.token: Optional[str] = token
self._model: Optional[str] = model
self._info: Optional[DeviceInfo] = 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
Expand Down Expand Up @@ -126,12 +128,16 @@ 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.
starkillerOG marked this conversation as resolved.
Show resolved Hide resolved

:param skip_cache bool: Skip the cache
"""
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:
Expand All @@ -155,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_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
rytilahti marked this conversation as resolved.
Show resolved Hide resolved
):
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."""
Expand Down Expand Up @@ -252,31 +284,17 @@ def actions(self) -> Dict[str, ActionDescriptor]:

def settings(self) -> Dict[str, SettingDescriptor]:
"""Return device settings."""
settings = self.status().settings()
for setting in 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:
self._initialize_descriptors()

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._initialize_descriptors()

return self._sensors

def __repr__(self):
return f"<{self.__class__.__name__ }: {self.ip} (token: {self.token})>"