mirror of
https://github.com/python-kasa/python-kasa.git
synced 2026-10-25 06:03:01 +00:00
Keep multi requests for smartcam devices after a timeout (#1802)
Some checks failed
CI / Perform Lint Checks (3.14) (push) Has been cancelled
CodeQL Checks / Analyze (python) (push) Has been cancelled
CI / Python 3.11 on macos-latest (push) Has been cancelled
CI / Python 3.12 on macos-latest (push) Has been cancelled
CI / Python 3.13 on macos-latest (push) Has been cancelled
CI / Python 3.14 on macos-latest (push) Has been cancelled
CI / Python 3.11 on ubuntu-latest (push) Has been cancelled
CI / Python 3.12 on ubuntu-latest (push) Has been cancelled
CI / Python 3.13 on ubuntu-latest (push) Has been cancelled
CI / Python 3.14 on ubuntu-latest (push) Has been cancelled
CI / Python 3.11 on windows-latest (push) Has been cancelled
CI / Python 3.12 on windows-latest (push) Has been cancelled
CI / Python 3.13 on windows-latest (push) Has been cancelled
CI / Python 3.14 on windows-latest (push) Has been cancelled
Stale / stale (push) Has been cancelled
Some checks failed
CI / Perform Lint Checks (3.14) (push) Has been cancelled
CodeQL Checks / Analyze (python) (push) Has been cancelled
CI / Python 3.11 on macos-latest (push) Has been cancelled
CI / Python 3.12 on macos-latest (push) Has been cancelled
CI / Python 3.13 on macos-latest (push) Has been cancelled
CI / Python 3.14 on macos-latest (push) Has been cancelled
CI / Python 3.11 on ubuntu-latest (push) Has been cancelled
CI / Python 3.12 on ubuntu-latest (push) Has been cancelled
CI / Python 3.13 on ubuntu-latest (push) Has been cancelled
CI / Python 3.14 on ubuntu-latest (push) Has been cancelled
CI / Python 3.11 on windows-latest (push) Has been cancelled
CI / Python 3.12 on windows-latest (push) Has been cancelled
CI / Python 3.13 on windows-latest (push) Has been cancelled
CI / Python 3.14 on windows-latest (push) Has been cancelled
Stale / stale (push) Has been cancelled
Smartcam devices do not support sending individual requests, but respond always with `PROTOCOL_FORMAT_ERROR (-40210)`. This commit forces them to always use the multi-request format for requests even after receiving an error where we would otherwise fallback to individual requests.
This commit is contained in:
@@ -49,6 +49,8 @@ class SingleRequest:
|
|||||||
class SmartCamProtocol(SmartProtocol):
|
class SmartCamProtocol(SmartProtocol):
|
||||||
"""Class for SmartCam Protocol."""
|
"""Class for SmartCam Protocol."""
|
||||||
|
|
||||||
|
REQUIRES_MULTIREQUESTS = True
|
||||||
|
|
||||||
def _get_list_request(
|
def _get_list_request(
|
||||||
self, method: str, params: dict | None, start_index: int
|
self, method: str, params: dict | None, start_index: int
|
||||||
) -> dict:
|
) -> dict:
|
||||||
|
|||||||
@@ -103,6 +103,8 @@ class SmartProtocol(BaseProtocol):
|
|||||||
|
|
||||||
BACKOFF_SECONDS_AFTER_TIMEOUT = 1
|
BACKOFF_SECONDS_AFTER_TIMEOUT = 1
|
||||||
DEFAULT_MULTI_REQUEST_BATCH_SIZE = 5
|
DEFAULT_MULTI_REQUEST_BATCH_SIZE = 5
|
||||||
|
# True if the device rejects single requests, so batching is never disabled.
|
||||||
|
REQUIRES_MULTIREQUESTS = False
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -248,6 +250,8 @@ class SmartProtocol(BaseProtocol):
|
|||||||
try:
|
try:
|
||||||
response_step = await self._transport.send(smart_request)
|
response_step = await self._transport.send(smart_request)
|
||||||
except TimeoutError as ex:
|
except TimeoutError as ex:
|
||||||
|
if self.REQUIRES_MULTIREQUESTS:
|
||||||
|
raise
|
||||||
# P300 does not respond to some batched requests (e.g. both child
|
# P300 does not respond to some batched requests (e.g. both child
|
||||||
# list requests together) so disable batching. Batch size is never
|
# list requests together) so disable batching. Batch size is never
|
||||||
# 1 here as that case sends single requests above.
|
# 1 here as that case sends single requests above.
|
||||||
@@ -278,6 +282,7 @@ class SmartProtocol(BaseProtocol):
|
|||||||
SmartErrorCode.INTERNAL_UNKNOWN_ERROR,
|
SmartErrorCode.INTERNAL_UNKNOWN_ERROR,
|
||||||
}
|
}
|
||||||
and self._multi_request_batch_size != 1
|
and self._multi_request_batch_size != 1
|
||||||
|
and not self.REQUIRES_MULTIREQUESTS
|
||||||
):
|
):
|
||||||
self._multi_request_batch_size = 1
|
self._multi_request_batch_size = 1
|
||||||
raise _RetryableError(
|
raise _RetryableError(
|
||||||
|
|||||||
@@ -333,6 +333,39 @@ async def test_smart_device_multiple_request_child_lists_timeout(
|
|||||||
assert send_mock.call_count == 3
|
assert send_mock.call_count == 3
|
||||||
|
|
||||||
|
|
||||||
|
async def test_smartcam_multiple_request_timeout_keeps_batching(
|
||||||
|
dummy_protocol: SmartProtocol, mocker: MockerFixture
|
||||||
|
) -> None:
|
||||||
|
"""Test that a timeout does not disable batching for smartcam devices."""
|
||||||
|
protocol = SmartCamProtocol(transport=dummy_protocol._transport)
|
||||||
|
requests = {f"get_method_{i}": {"foo": "bar"} for i in range(3)}
|
||||||
|
batch_response = {
|
||||||
|
"result": {
|
||||||
|
"responses": [
|
||||||
|
{"method": method, "result": {"great": "success"}, "error_code": 0}
|
||||||
|
for method in requests
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"error_code": 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
send_mock = mocker.patch.object(
|
||||||
|
protocol._transport,
|
||||||
|
"send",
|
||||||
|
side_effect=[TimeoutError("Simulated timeout"), batch_response],
|
||||||
|
)
|
||||||
|
mocker.patch("asyncio.sleep")
|
||||||
|
batch_size = protocol._multi_request_batch_size
|
||||||
|
assert batch_size > 1
|
||||||
|
resp = await protocol.query(requests, retry_count=1)
|
||||||
|
assert resp == {method: {"great": "success"} for method in requests}
|
||||||
|
assert protocol._multi_request_batch_size == batch_size
|
||||||
|
# The timed out batch + the retried batch, no single requests
|
||||||
|
assert send_mock.call_count == 2
|
||||||
|
for call in send_mock.call_args_list:
|
||||||
|
assert json.loads(call.args[0])["method"] == "multipleRequest"
|
||||||
|
|
||||||
|
|
||||||
async def test_childdevicewrapper_unwrapping(
|
async def test_childdevicewrapper_unwrapping(
|
||||||
dummy_protocol: SmartProtocol, mocker: MockerFixture
|
dummy_protocol: SmartProtocol, mocker: MockerFixture
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user