8000 Update hub children on first update and delay subsequent updates (#1438) · python-kasa/python-kasa@a211cc0 · GitHub
[go: up one dir, main page]

Skip to content

Commit a211cc0

Browse files
authored
Update hub children on first update and delay subsequent updates (#1438)
1 parent 333a36b commit a211cc0

File tree

6 files changed

+326
-81
lines changed

6 files changed

+326
-81
lines changed

kasa/smart/modules/devicemodule.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,15 @@ async def _post_update_hook(self) -> None:
1919

2020
def query(self) -> dict:
2121
"""Query to execute during the update cycle."""
22+
if self._device._is_hub_child:
23+
# Child devices get their device info updated by the parent device.
24+
return {}
2225
query = {
2326
"get_device_info": None,
2427
}
2528
# Device usage is not available on older firmware versions
2629
# or child devices of hubs
27-
if self.supported_version >= 2 and not self._device._is_hub_child:
30+
if self.supported_version >= 2:
2831
query["get_device_usage"] = None
2932

3033
return query

kasa/smart/smartchilddevice.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,22 @@ async def _update(self, update_children: bool = True) -> None:
8686
module_queries: list[SmartModule] = []
8787
req: dict[str, Any] = {}
8888
for module in self.modules.values():
89-
if module.disabled is False and (mod_query := module.query()):
89+
if (
90+
module.disabled is False
91+
and (mod_query := module.query())
92+
and module._should_update(now)
93+
):
9094
module_queries.append(module)
9195
req.update(mod_query)
9296
if req:
93-
self._last_update = await self.protocol.query(req)
97+
first_update = self._last_update != {}
98+
try:
99+
resp = await self.protocol.query(req)
100+
except Exception as ex:
101+
resp = await self._handle_modular_update_error(
102+
ex, first_update, ", ".join(mod.name for mod in module_queries), req
103+
)
104+
self._last_update = resp
94105

95106
for module in self.modules.values():
96107
await self._handle_module_post_update(

kasa/smart/smartdevice.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ def _update_internal_info(self, info_resp: dict) -> None:
183183
"""Update the internal device info."""
184184
self._info = self._try_get_response(info_resp, "get_device_info")
185185

186-
async def update(self, update_children: bool = False) -> None:
186+
async def update(self, update_children: bool = True) -> None:
187187
"""Update the device."""
188188
if self.credentials is None and self.credentials_hash is None:
189189
raise AuthenticationError("Tapo plug requires authentication.")
@@ -207,7 +207,7 @@ async def update(self, update_children: bool = False) -> None:
207207
# devices will always update children to prevent errors on module access.
208208
# This needs to go after updating the internal state of the children so that
209209
# child modules have access to their sysinfo.
210-
if update_children or self.device_type != DeviceType.Hub:
210+
if first_update or update_children or self.device_type != DeviceType.Hub:
211211
for child in self._children.values():
212212
if TYPE_CHECKING:
213213
assert isinstance(child, SmartChildDevice)
@@ -260,11 +260,7 @@ async def _modular_update(
260260
if first_update and module.__class__ in self.FIRST_UPDATE_MODULES:
261261
module._last_update_time = update_time
262262
continue
263-
if (
264-
not module.update_interval
265-
or not module._last_update_time
266-
or (update_time - module._last_update_time) >= module.update_interval
267-
):
263+
if module._should_update(update_time):
268264
module_queries.append(module)
269265
req.update(query)
270266

kasa/smart/smartmodule.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ class SmartModule(Module):
6262
REGISTERED_MODULES: dict[str, type[SmartModule]] = {}
6363

6464
MINIMUM_UPDATE_INTERVAL_SECS = 0
65+
MINIMUM_HUB_CHILD_UPDATE_INTERVAL_SECS = 60 * 60 * 24
66+
6567
UPDATE_INTERVAL_AFTER_ERROR_SECS = 30
6668

6769
DISABLE_AFTER_ERROR_COUNT = 10
@@ -107,16 +109,27 @@ def _set_error(self, err: Exception | None) -> None:
107109
@property
108110
def update_interval(self) -> int:
109111
"""Time to wait between updates."""
110-
if self._last_update_error is None:
111-
return self.MINIMUM_UPDATE_INTERVAL_SECS
112+
if self._last_update_error:
113+
return self.UPDATE_INTERVAL_AFTER_ERROR_SECS * self._error_count
114+
115+
if self._device._is_hub_child:
116+
return self.MINIMUM_HUB_CHILD_UPDATE_INTERVAL_SECS
112117

113-
return self.UPDATE_INTERVAL_AFTER_ERROR_SECS * self._error_count
118+
return self.MINIMUM_UPDATE_INTERVAL_SECS
114119

115120
@property
116121
def disabled(self) -> bool:
117122
"""Return true if the module is disabled due to errors."""
118123
return self._error_count >= self.DISABLE_AFTER_ERROR_COUNT
119124

125+
def _should_update(self, update_time: float) -> bool:
126+
"""Return true if module should update based on delay parameters."""
127+
return (
128+
not self.update_interval
129+
or not self._last_update_time
130+
or (update_time - self._last_update_time) >= self.update_interval
131+
)
132+
120133
@classmethod
121134
def _module_name(cls) -> str:
122135
return getattr(cls, "NAME", cls.__name__)

kasa/smartcam/modules/device.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ class DeviceModule(SmartCamModule):
1616

1717
def query(self) -> dict:
1818
"""Query to execute during the update cycle."""
19+
if self._device._is_hub_child:
20+
# Child devices get their device info updated by the parent device.
21+
# and generally don't support connection type as they're not
22+
# connected to the network
23+
return {}
1924
q = super().query()
2025
q["getConnectionType"] = {"network": {"get_connection_type": []}}
2126

@@ -70,14 +75,14 @@ async def _post_update_hook(self) -> None:
7075
@property
7176
def device_id(self) -> str:
7277
"""Return the device id."""
73-
return self.data[self.QUERY_GETTER_NAME]["basic_info"]["dev_id"]
78+
return self._device._info["device_id"]
7479

7580
@property
7681
def rssi(self) -> int | None:
7782
"""Return the device id."""
78-
return self.data["getConnectionType"].get("rssiValue")
83+
return self.data.get("getConnectionType", {}).get("rssiValue")
7984

8085
@property
8186
def signal_level(self) -> int | None:
8287
"""Return the device id."""
83-
return self.data["getConnectionType"].get("rssi")
88+
return self.data.get("getConnectionType", {}).get("rssi")

0 commit comments

Comments
 (0)
0