python-roborock 2.9.3__tar.gz → 2.9.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. {python_roborock-2.9.3 → python_roborock-2.9.5}/PKG-INFO +2 -2
  2. {python_roborock-2.9.3 → python_roborock-2.9.5}/pyproject.toml +6 -2
  3. {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/api.py +3 -2
  4. {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/cloud_api.py +7 -6
  5. {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/local_api.py +2 -2
  6. {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/roborock_message.py +4 -4
  7. {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/version_1_apis/roborock_local_client_v1.py +2 -1
  8. {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/version_1_apis/roborock_mqtt_client_v1.py +2 -1
  9. {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/version_a01_apis/roborock_client_a01.py +3 -3
  10. {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/version_a01_apis/roborock_mqtt_client_a01.py +3 -2
  11. {python_roborock-2.9.3 → python_roborock-2.9.5}/LICENSE +0 -0
  12. {python_roborock-2.9.3 → python_roborock-2.9.5}/README.md +0 -0
  13. {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/__init__.py +0 -0
  14. {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/cli.py +0 -0
  15. {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/code_mappings.py +0 -0
  16. {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/command_cache.py +0 -0
  17. {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/const.py +0 -0
  18. {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/containers.py +0 -0
  19. {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/exceptions.py +0 -0
  20. {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/protocol.py +0 -0
  21. {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/py.typed +0 -0
  22. {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/roborock_future.py +0 -0
  23. {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/roborock_typing.py +0 -0
  24. {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/util.py +0 -0
  25. {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/version_1_apis/__init__.py +0 -0
  26. {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/version_1_apis/roborock_client_v1.py +0 -0
  27. {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/version_a01_apis/__init__.py +0 -0
  28. {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/web_api.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: python-roborock
3
- Version: 2.9.3
3
+ Version: 2.9.5
4
4
  Summary: A package to control Roborock vacuums.
5
5
  License: GPL-3.0-only
6
6
  Keywords: roborock,vacuum,homeassistant
@@ -21,7 +21,7 @@ Requires-Dist: aiohttp (>=3.8.2,<4.0.0)
21
21
  Requires-Dist: async-timeout
22
22
  Requires-Dist: click (>=8)
23
23
  Requires-Dist: construct (>=2.10.57,<3.0.0)
24
- Requires-Dist: paho-mqtt (>=1.6.1,<2.0.0)
24
+ Requires-Dist: paho-mqtt (>=1.6.1,<3.0.0)
25
25
  Requires-Dist: pycryptodome (>=3.18,<4.0)
26
26
  Requires-Dist: pycryptodomex (>=3.18,<4.0) ; sys_platform == "darwin"
27
27
  Requires-Dist: vacuum-map-parser-roborock
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "python-roborock"
3
- version = "2.9.3"
3
+ version = "2.9.5"
4
4
  description = "A package to control Roborock vacuums."
5
5
  authors = ["humbertogontijo <humbertogontijo@users.noreply.github.com>"]
6
6
  license = "GPL-3.0-only"
@@ -27,7 +27,7 @@ aiohttp = "^3.8.2"
27
27
  async-timeout = "*"
28
28
  pycryptodome = "^3.18"
29
29
  pycryptodomex = {version = "^3.18", markers = "sys_platform == 'darwin'"}
30
- paho-mqtt = "^1.6.1"
30
+ paho-mqtt = ">=1.6.1,<3.0.0"
31
31
  construct = "^2.10.57"
32
32
  vacuum-map-parser-roborock = "*"
33
33
 
@@ -45,6 +45,8 @@ ruff = "*"
45
45
  codespell = "*"
46
46
  pyshark = "^0.6"
47
47
  aioresponses = "^0.7.7"
48
+ freezegun = "^1.5.1"
49
+ pytest-timeout = "^2.3.1"
48
50
 
49
51
  [tool.semantic_release]
50
52
  branch = "main"
@@ -70,3 +72,5 @@ select=["E", "F", "UP", "I"]
70
72
 
71
73
  [tool.pytest.ini_options]
72
74
  asyncio_mode = "auto"
75
+ asyncio_default_fixture_loop_scope = "function"
76
+ timeout = 20
@@ -31,8 +31,9 @@ class RoborockClient(ABC):
31
31
  """Roborock client base class."""
32
32
 
33
33
  _logger: logging.LoggerAdapter
34
+ queue_timeout: int
34
35
 
35
- def __init__(self, device_info: DeviceData, queue_timeout: int = 4) -> None:
36
+ def __init__(self, device_info: DeviceData) -> None:
36
37
  """Initialize RoborockClient."""
37
38
  self.event_loop = get_running_loop_or_create_one()
38
39
  self.device_info = device_info
@@ -45,7 +46,6 @@ class RoborockClient(ABC):
45
46
  "misc_info": {"Nonce": base64.b64encode(self._nonce).decode("utf-8")}
46
47
  }
47
48
  self.is_available: bool = True
48
- self.queue_timeout = queue_timeout
49
49
 
50
50
  def __del__(self) -> None:
51
51
  self.release()
@@ -91,6 +91,7 @@ class RoborockClient(ABC):
91
91
 
92
92
  async def validate_connection(self) -> None:
93
93
  if not self.should_keepalive():
94
+ self._logger.info("Resetting Roborock connection due to kepalive timeout")
94
95
  await self.async_disconnect()
95
96
  await self.async_connect()
96
97
 
@@ -46,12 +46,12 @@ class _Mqtt(mqtt.Client):
46
46
  class RoborockMqttClient(RoborockClient, ABC):
47
47
  """Roborock MQTT client base class."""
48
48
 
49
- def __init__(self, user_data: UserData, device_info: DeviceData, queue_timeout: int = 10) -> None:
49
+ def __init__(self, user_data: UserData, device_info: DeviceData) -> None:
50
50
  """Initialize the Roborock MQTT client."""
51
51
  rriot = user_data.rriot
52
52
  if rriot is None:
53
53
  raise RoborockException("Got no rriot data from user_data")
54
- RoborockClient.__init__(self, device_info, queue_timeout)
54
+ RoborockClient.__init__(self, device_info)
55
55
  self._mqtt_user = rriot.u
56
56
  self._hashed_user = md5hex(self._mqtt_user + ":" + rriot.k)[2:10]
57
57
  url = urlparse(rriot.r.m)
@@ -82,6 +82,8 @@ class RoborockMqttClient(RoborockClient, ABC):
82
82
  self._logger.error(message)
83
83
  if connection_queue:
84
84
  connection_queue.set_exception(VacuumError(message))
85
+ else:
86
+ self._logger.debug("Failed to notify connect future, not in queue")
85
87
  return
86
88
  self._logger.info(f"Connected to mqtt {self._mqtt_host}:{self._mqtt_port}")
87
89
  topic = f"rr/m/o/{self._mqtt_user}/{self._hashed_user}/{self.device_info.device.duid}"
@@ -154,10 +156,9 @@ class RoborockMqttClient(RoborockClient, ABC):
154
156
  async def async_disconnect(self) -> None:
155
157
  async with self._mutex:
156
158
  if disconnected_future := self.sync_disconnect():
157
- try:
158
- await disconnected_future
159
- except VacuumError as err:
160
- raise RoborockException(err) from err
159
+ # There are no errors set on this future
160
+ await disconnected_future
161
+ await self.event_loop.run_in_executor(None, self._mqtt_client.loop_stop)
161
162
 
162
163
  async def async_connect(self) -> None:
163
164
  async with self._mutex:
@@ -37,7 +37,7 @@ class _LocalProtocol(asyncio.Protocol):
37
37
  class RoborockLocalClient(RoborockClient, ABC):
38
38
  """Roborock local client base class."""
39
39
 
40
- def __init__(self, device_data: DeviceData, queue_timeout: int = 4):
40
+ def __init__(self, device_data: DeviceData):
41
41
  """Initialize the Roborock local client."""
42
42
  if device_data.host is None:
43
43
  raise RoborockException("Host is required")
@@ -48,7 +48,7 @@ class RoborockLocalClient(RoborockClient, ABC):
48
48
  self.transport: Transport | None = None
49
49
  self._mutex = Lock()
50
50
  self.keep_alive_task: TimerHandle | None = None
51
- RoborockClient.__init__(self, device_data, queue_timeout)
51
+ RoborockClient.__init__(self, device_data)
52
52
  self._local_protocol = _LocalProtocol(self._data_received, self._connection_lost)
53
53
 
54
54
  def _data_received(self, message):
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import json
4
4
  import math
5
5
  import time
6
- from dataclasses import dataclass
6
+ from dataclasses import dataclass, field
7
7
 
8
8
  from roborock import RoborockEnum
9
9
  from roborock.util import get_next_int
@@ -155,10 +155,10 @@ class MessageRetry:
155
155
  class RoborockMessage:
156
156
  protocol: RoborockMessageProtocol
157
157
  payload: bytes | None = None
158
- seq: int = get_next_int(100000, 999999)
158
+ seq: int = field(default_factory=lambda: get_next_int(100000, 999999))
159
159
  version: bytes = b"1.0"
160
- random: int = get_next_int(10000, 99999)
161
- timestamp: int = math.floor(time.time())
160
+ random: int = field(default_factory=lambda: get_next_int(10000, 99999))
161
+ timestamp: int = field(default_factory=lambda: math.floor(time.time()))
162
162
  message_retry: MessageRetry | None = None
163
163
 
164
164
  def get_request_id(self) -> int | None:
@@ -17,8 +17,9 @@ class RoborockLocalClientV1(RoborockLocalClient, RoborockClientV1):
17
17
 
18
18
  def __init__(self, device_data: DeviceData, queue_timeout: int = 4):
19
19
  """Initialize the Roborock local client."""
20
- RoborockLocalClient.__init__(self, device_data, queue_timeout)
20
+ RoborockLocalClient.__init__(self, device_data)
21
21
  RoborockClientV1.__init__(self, device_data, "abc")
22
+ self.queue_timeout = queue_timeout
22
23
  self._logger = RoborockLoggerAdapter(device_data.device.name, _LOGGER)
23
24
 
24
25
  def build_roborock_message(
@@ -32,8 +32,9 @@ class RoborockMqttClientV1(RoborockMqttClient, RoborockClientV1):
32
32
  raise RoborockException("Got no rriot data from user_data")
33
33
  endpoint = base64.b64encode(Utils.md5(rriot.k.encode())[8:14]).decode()
34
34
 
35
- RoborockMqttClient.__init__(self, user_data, device_info, queue_timeout)
35
+ RoborockMqttClient.__init__(self, user_data, device_info)
36
36
  RoborockClientV1.__init__(self, device_info, endpoint)
37
+ self.queue_timeout = queue_timeout
37
38
  self._logger = RoborockLoggerAdapter(device_info.device.name, _LOGGER)
38
39
 
39
40
  async def send_message(self, roborock_message: RoborockMessage):
@@ -108,9 +108,9 @@ zeo_data_protocol_entries = {
108
108
  class RoborockClientA01(RoborockClient, ABC):
109
109
  """Roborock client base class for A01 devices."""
110
110
 
111
- def __init__(self, device_info: DeviceData, category: RoborockCategory, queue_timeout: int = 4):
111
+ def __init__(self, device_info: DeviceData, category: RoborockCategory):
112
112
  """Initialize the Roborock client."""
113
- super().__init__(device_info, queue_timeout)
113
+ super().__init__(device_info)
114
114
  self.category = category
115
115
 
116
116
  def on_message_received(self, messages: list[RoborockMessage]) -> None:
@@ -128,8 +128,8 @@ class RoborockClientA01(RoborockClient, ABC):
128
128
  continue
129
129
  payload_json = json.loads(payload.decode())
130
130
  for data_point_number, data_point in payload_json.get("dps").items():
131
- self._logger.debug("data point number=%s", data_point_number)
132
131
  data_point_protocol: RoborockDyadDataProtocol | RoborockZeoProtocol
132
+ self._logger.debug("received msg with dps, protocol: %s, %s", data_point_number, protocol)
133
133
  entries: dict
134
134
  if self.category == RoborockCategory.WET_DRY_VAC:
135
135
  data_point_protocol = RoborockDyadDataProtocol(int(data_point_number))
@@ -34,8 +34,9 @@ class RoborockMqttClientA01(RoborockMqttClient, RoborockClientA01):
34
34
  if rriot is None:
35
35
  raise RoborockException("Got no rriot data from user_data")
36
36
 
37
- RoborockMqttClient.__init__(self, user_data, device_info, queue_timeout)
38
- RoborockClientA01.__init__(self, device_info, category, queue_timeout)
37
+ RoborockMqttClient.__init__(self, user_data, device_info)
38
+ RoborockClientA01.__init__(self, device_info, category)
39
+ self.queue_timeout = queue_timeout
39
40
  self._logger = RoborockLoggerAdapter(device_info.device.name, _LOGGER)
40
41
 
41
42
  async def send_message(self, roborock_message: RoborockMessage):
File without changes