From c3f3f81f9044a700f7ed0e7ed4e660380495be13 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Mon, 20 Jan 2025 20:35:18 -0800 Subject: [PATCH] fix: set unique sequence numbers on outgoing messages --- poetry.lock | 53 +++++++++++++++++++++++++++++----- pyproject.toml | 1 + roborock/roborock_message.py | 8 ++--- tests/test_roborock_message.py | 29 +++++++++++++++++++ 4 files changed, 80 insertions(+), 11 deletions(-) create mode 100644 tests/test_roborock_message.py diff --git a/poetry.lock b/poetry.lock index e077feab..0564e1ee 100644 --- a/poetry.lock +++ b/poetry.lock @@ -272,6 +272,20 @@ docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2. testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] typing = ["typing-extensions (>=4.12.2)"] +[[package]] +name = "freezegun" +version = "1.5.1" +description = "Let your Python tests travel through time" +optional = false +python-versions = ">=3.7" +files = [ + {file = "freezegun-1.5.1-py3-none-any.whl", hash = "sha256:bf111d7138a8abe55ab48a71755673dbaa4ab87f4cff5634a4442dfec34c15f1"}, + {file = "freezegun-1.5.1.tar.gz", hash = "sha256:b29dedfcda6d5e8e083ce71b2b542753ad48cfec44037b3fc79702e2980a89e9"}, +] + +[package.dependencies] +python-dateutil = ">=2.7" + [[package]] name = "frozenlist" version = "1.5.0" @@ -375,13 +389,13 @@ files = [ [[package]] name = "identify" -version = "2.6.5" +version = "2.6.6" description = "File identification library for Python" optional = false python-versions = ">=3.9" files = [ - {file = "identify-2.6.5-py2.py3-none-any.whl", hash = "sha256:14181a47091eb75b337af4c23078c9d09225cd4c48929f521f3bf16b09d02566"}, - {file = "identify-2.6.5.tar.gz", hash = "sha256:c10b33f250e5bba374fae86fb57f3adcebf1161bce7cdf92031915fd480c13bc"}, + {file = "identify-2.6.6-py2.py3-none-any.whl", hash = "sha256:cbd1810bce79f8b671ecb20f53ee0ae8e86ae84b557de31d89709dc2a48ba881"}, + {file = "identify-2.6.6.tar.gz", hash = "sha256:7bec12768ed44ea4761efb47806f0a41f86e7c0a5fdf5950d4648c90eca7e251"}, ] [package.extras] @@ -893,13 +907,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" -version = "4.0.1" +version = "4.1.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.9" files = [ - {file = "pre_commit-4.0.1-py2.py3-none-any.whl", hash = "sha256:efde913840816312445dc98787724647c65473daefe420785f885e8ed9a06878"}, - {file = "pre_commit-4.0.1.tar.gz", hash = "sha256:80905ac375958c0444c65e9cebebd948b3cdb518f335a091a670a89d652139d2"}, + {file = "pre_commit-4.1.0-py2.py3-none-any.whl", hash = "sha256:d29e7cb346295bcc1cc75fc3e92e343495e3ea0196c9ec6ba53f49f10ab6ae7b"}, + {file = "pre_commit-4.1.0.tar.gz", hash = "sha256:ae3f018575a588e30dfddfab9a05448bfbd6b73d78709617b5a2b853549716d4"}, ] [package.dependencies] @@ -1137,6 +1151,20 @@ pytest = ">=8.2,<9" docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1)"] testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + [[package]] name = "pyyaml" version = "6.0.2" @@ -1226,6 +1254,17 @@ files = [ {file = "ruff-0.9.2.tar.gz", hash = "sha256:b5eceb334d55fae5f316f783437392642ae18e16dcf4f1858d55d3c2a0f8f5d0"}, ] +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + [[package]] name = "termcolor" version = "2.5.0" @@ -1399,4 +1438,4 @@ propcache = ">=0.2.0" [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "4eea680f4cfd1df5b3d65addf1d00c18c62f660d5d0a1a7c7e0bad002655fd14" +content-hash = "3f599a64cda4db14b1d2548a2f83adb35a941cf7342281c36003db97bff4434d" diff --git a/pyproject.toml b/pyproject.toml index 41075790..4c27c3b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,6 +45,7 @@ ruff = "*" codespell = "*" pyshark = "^0.6" aioresponses = "^0.7.7" +freezegun = "^1.5.1" [tool.semantic_release] branch = "main" diff --git a/roborock/roborock_message.py b/roborock/roborock_message.py index df031081..1386a4d5 100644 --- a/roborock/roborock_message.py +++ b/roborock/roborock_message.py @@ -3,7 +3,7 @@ import json import math import time -from dataclasses import dataclass +from dataclasses import dataclass, field from roborock import RoborockEnum from roborock.util import get_next_int @@ -155,10 +155,10 @@ class MessageRetry: class RoborockMessage: protocol: RoborockMessageProtocol payload: bytes | None = None - seq: int = get_next_int(100000, 999999) + seq: int = field(default_factory=lambda: get_next_int(100000, 999999)) version: bytes = b"1.0" - random: int = get_next_int(10000, 99999) - timestamp: int = math.floor(time.time()) + random: int = field(default_factory=lambda: get_next_int(10000, 99999)) + timestamp: int = field(default_factory=lambda: math.floor(time.time())) message_retry: MessageRetry | None = None def get_request_id(self) -> int | None: diff --git a/tests/test_roborock_message.py b/tests/test_roborock_message.py new file mode 100644 index 00000000..227f4511 --- /dev/null +++ b/tests/test_roborock_message.py @@ -0,0 +1,29 @@ +import json + +from freezegun import freeze_time + +from roborock.roborock_message import RoborockMessage, RoborockMessageProtocol + + +def test_roborock_message() -> None: + """Test the RoborockMessage class is initialized.""" + with freeze_time("2025-01-20T12:00:00"): + message1 = RoborockMessage( + protocol=RoborockMessageProtocol.RPC_REQUEST, + payload=json.dumps({"dps": {"101": json.dumps({"id": 4321})}}).encode(), + message_retry=None, + ) + assert message1.get_request_id() == 4321 + + with freeze_time("2025-01-20T11:00:00"): # Back in time 1hr to test timestamp + message2 = RoborockMessage( + protocol=RoborockMessageProtocol.RPC_RESPONSE, + payload=json.dumps({"dps": {"94": json.dumps({"id": 444}), "102": json.dumps({"id": 333})}}).encode(), + message_retry=None, + ) + assert message2.get_request_id() == 333 + + # Ensure the sequence, random numbers, etc are initialized properly + assert message1.seq != message2.seq + assert message1.random != message2.random + assert message1.timestamp > message2.timestamp