python-roborock 7.15.0__tar.gz → 7.15.1__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 (138) hide show
  1. {python_roborock-7.15.0 → python_roborock-7.15.1}/PKG-INFO +1 -1
  2. {python_roborock-7.15.0 → python_roborock-7.15.1}/pyproject.toml +1 -1
  3. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/protocols/v1_protocol.py +52 -6
  4. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/roborock_message.py +1 -0
  5. {python_roborock-7.15.0 → python_roborock-7.15.1}/.gitignore +0 -0
  6. {python_roborock-7.15.0 → python_roborock-7.15.1}/LICENSE +0 -0
  7. {python_roborock-7.15.0 → python_roborock-7.15.1}/README.md +0 -0
  8. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/__init__.py +0 -0
  9. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/broadcast_protocol.py +0 -0
  10. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/callbacks.py +0 -0
  11. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/cli.py +0 -0
  12. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/const.py +0 -0
  13. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/data/__init__.py +0 -0
  14. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/data/b01_q10/__init__.py +0 -0
  15. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/data/b01_q10/b01_q10_code_mappings.py +0 -0
  16. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/data/b01_q10/b01_q10_containers.py +0 -0
  17. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/data/b01_q7/__init__.py +0 -0
  18. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/data/b01_q7/b01_q7_code_mappings.py +0 -0
  19. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/data/b01_q7/b01_q7_containers.py +0 -0
  20. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/data/camera.py +0 -0
  21. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/data/code_mappings.py +0 -0
  22. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/data/containers.py +0 -0
  23. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/data/dyad/__init__.py +0 -0
  24. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/data/dyad/dyad_code_mappings.py +0 -0
  25. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/data/dyad/dyad_containers.py +0 -0
  26. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/data/mower/__init__.py +0 -0
  27. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/data/mower/mower_code_mappings.py +0 -0
  28. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/data/mower/mower_containers.py +0 -0
  29. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/data/v1/__init__.py +0 -0
  30. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/data/v1/v1_clean_modes.py +0 -0
  31. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/data/v1/v1_code_mappings.py +0 -0
  32. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/data/v1/v1_containers.py +0 -0
  33. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/data/v1/v1_dynamic_map.py +0 -0
  34. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/data/zeo/__init__.py +0 -0
  35. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/data/zeo/zeo_code_mappings.py +0 -0
  36. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/data/zeo/zeo_containers.py +0 -0
  37. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/device_features.py +0 -0
  38. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/README.md +0 -0
  39. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/__init__.py +0 -0
  40. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/cache.py +0 -0
  41. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/device.py +0 -0
  42. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/device_manager.py +0 -0
  43. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/file_cache.py +0 -0
  44. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/rpc/__init__.py +0 -0
  45. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/rpc/a01_channel.py +0 -0
  46. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/rpc/b01_q10_channel.py +0 -0
  47. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/rpc/b01_q7_channel.py +0 -0
  48. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/rpc/v1_channel.py +0 -0
  49. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/__init__.py +0 -0
  50. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/a01/__init__.py +0 -0
  51. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/a01/device_feature.py +0 -0
  52. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/b01/__init__.py +0 -0
  53. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/b01/q10/__init__.py +0 -0
  54. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/b01/q10/button_light.py +0 -0
  55. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/b01/q10/child_lock.py +0 -0
  56. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/b01/q10/clean_history.py +0 -0
  57. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/b01/q10/command.py +0 -0
  58. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/b01/q10/common.py +0 -0
  59. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/b01/q10/consumable.py +0 -0
  60. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/b01/q10/do_not_disturb.py +0 -0
  61. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/b01/q10/dust_collection.py +0 -0
  62. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/b01/q10/goto.py +0 -0
  63. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/b01/q10/map.py +0 -0
  64. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/b01/q10/maps.py +0 -0
  65. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/b01/q10/network_info.py +0 -0
  66. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/b01/q10/remote.py +0 -0
  67. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/b01/q10/status.py +0 -0
  68. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/b01/q10/vacuum.py +0 -0
  69. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/b01/q10/volume.py +0 -0
  70. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/b01/q7/__init__.py +0 -0
  71. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/b01/q7/clean_summary.py +0 -0
  72. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/b01/q7/map.py +0 -0
  73. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/b01/q7/map_content.py +0 -0
  74. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/common.py +0 -0
  75. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/traits_mixin.py +0 -0
  76. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/v1/__init__.py +0 -0
  77. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/v1/child_lock.py +0 -0
  78. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/v1/clean_summary.py +0 -0
  79. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/v1/command.py +0 -0
  80. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/v1/common.py +0 -0
  81. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/v1/consumeable.py +0 -0
  82. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/v1/device_features.py +0 -0
  83. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/v1/do_not_disturb.py +0 -0
  84. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/v1/dust_collection_mode.py +0 -0
  85. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/v1/flow_led_status.py +0 -0
  86. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/v1/home.py +0 -0
  87. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/v1/led_status.py +0 -0
  88. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/v1/map_content.py +0 -0
  89. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/v1/maps.py +0 -0
  90. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/v1/mop_dryer.py +0 -0
  91. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/v1/network_info.py +0 -0
  92. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/v1/obstacle_photos.py +0 -0
  93. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/v1/rooms.py +0 -0
  94. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/v1/routines.py +0 -0
  95. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/v1/smart_wash_params.py +0 -0
  96. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/v1/status.py +0 -0
  97. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/v1/valley_electricity_timer.py +0 -0
  98. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/v1/volume.py +0 -0
  99. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/traits/v1/wash_towel_mode.py +0 -0
  100. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/transport/__init__.py +0 -0
  101. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/transport/channel.py +0 -0
  102. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/transport/local_channel.py +0 -0
  103. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/devices/transport/mqtt_channel.py +0 -0
  104. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/diagnostics.py +0 -0
  105. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/exceptions.py +0 -0
  106. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/map/__init__.py +0 -0
  107. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/map/b01_grid_layers.py +0 -0
  108. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/map/b01_map_parser.py +0 -0
  109. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/map/b01_q10_map_parser.py +0 -0
  110. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/map/b01_q10_overlays.py +0 -0
  111. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/map/b01_q10_render.py +0 -0
  112. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/map/dynamic_blocks.py +0 -0
  113. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/map/map_parser.py +0 -0
  114. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/map/proto/__init__.py +0 -0
  115. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/map/proto/b01_scmap.proto +0 -0
  116. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/map/proto/b01_scmap_pb2.py +0 -0
  117. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/map/room_colors.py +0 -0
  118. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/mqtt/__init__.py +0 -0
  119. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/mqtt/health_manager.py +0 -0
  120. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/mqtt/roborock_session.py +0 -0
  121. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/mqtt/session.py +0 -0
  122. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/protocol.py +0 -0
  123. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/protocols/__init__.py +0 -0
  124. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/protocols/a01_protocol.py +0 -0
  125. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/protocols/b01_q10_protocol.py +0 -0
  126. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/protocols/b01_q7_protocol.py +0 -0
  127. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/protocols/camera_signaling.py +0 -0
  128. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/py.typed +0 -0
  129. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/roborock_typing.py +0 -0
  130. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/testing/__init__.py +0 -0
  131. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/testing/a01_simulator.py +0 -0
  132. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/testing/b01_q10_simulator.py +0 -0
  133. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/testing/channel.py +0 -0
  134. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/testing/cloud.py +0 -0
  135. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/testing/simulator.py +0 -0
  136. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/testing/v1_simulator.py +0 -0
  137. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/util.py +0 -0
  138. {python_roborock-7.15.0 → python_roborock-7.15.1}/roborock/web_api.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: python-roborock
3
- Version: 7.15.0
3
+ Version: 7.15.1
4
4
  Summary: A package to control Roborock vacuums.
5
5
  Project-URL: Repository, https://github.com/python-roborock/python-roborock
6
6
  Project-URL: Documentation, https://python-roborock.readthedocs.io/
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-roborock"
3
- version = "7.15.0"
3
+ version = "7.15.1"
4
4
  description = "A package to control Roborock vacuums."
5
5
  authors = [{ name = "humbertogontijo", email = "humbertogontijo@users.noreply.github.com" }, {name="Lash-L"}, {name="allenporter"}]
6
6
  requires-python = ">=3.11, <4"
@@ -306,16 +306,29 @@ def create_blob_response_decoder() -> Callable[[RoborockMessage], BlobResponse |
306
306
  return _decode_blob_response
307
307
 
308
308
 
309
+ # Number of incomplete multi-part map responses kept while waiting for missing parts.
310
+ _MAX_PENDING_MAP_RESPONSES = 4
311
+
312
+
309
313
  def create_map_response_decoder(security_data: SecurityData) -> Callable[[RoborockMessage], MapResponse | None]:
310
- """Create a decoder for V1 map response messages."""
314
+ """Create a decoder for V1 map response messages.
311
315
 
312
- def _decode_map_response(message: RoborockMessage) -> MapResponse | None:
313
- """Decode a V1 map response message."""
314
- if not message.payload or len(message.payload) < 24:
316
+ Large maps are split into multiple messages that share the same ``random``
317
+ and ``timestamp`` and are numbered by ``seq``. All parts except the last
318
+ one use protocol ``MAP_RESPONSE_PART`` (300), the last one uses
319
+ ``MAP_RESPONSE`` (301). Only the first part starts with the map header.
320
+ Parts may arrive out of order, so they are buffered until complete.
321
+ """
322
+ endpoint_bytes = security_data.endpoint.encode()
323
+ # (random, timestamp) -> seq -> (is last part, payload)
324
+ pending: dict[tuple[int, int], dict[int, tuple[bool, bytes]]] = {}
325
+
326
+ def _decode_payload(payload: bytes) -> MapResponse | None:
327
+ if len(payload) < 24:
315
328
  raise RoborockException("Invalid V1 map response format: missing payload")
316
- header, body = message.payload[:24], message.payload[24:]
329
+ header, body = payload[:24], payload[24:]
317
330
  [endpoint, _, request_id, _] = struct.unpack("<8s8sH6s", header)
318
- if not endpoint.decode().startswith(security_data.endpoint):
331
+ if not endpoint.startswith(endpoint_bytes):
319
332
  _LOGGER.debug("Received map response not requested by this device, ignoring.")
320
333
  return None
321
334
  try:
@@ -325,6 +338,39 @@ def create_map_response_decoder(security_data: SecurityData) -> Callable[[Roboro
325
338
  decompressed = Utils.decompress(decrypted)
326
339
  return MapResponse(request_id=request_id, data=decompressed)
327
340
 
341
+ def _remember(key: tuple[int, int], parts: dict[int, tuple[bool, bytes]]) -> None:
342
+ pending[key] = parts
343
+ while len(pending) > _MAX_PENDING_MAP_RESPONSES:
344
+ del pending[next(iter(pending))]
345
+
346
+ def _add_part(key: tuple[int, int], seq: int, is_last: bool, payload: bytes) -> MapResponse | None:
347
+ parts = pending.pop(key, {})
348
+ parts[seq] = (is_last, payload)
349
+ seqs = sorted(parts)
350
+ complete = (
351
+ parts[seqs[-1]][0]
352
+ and seqs == list(range(seqs[0], seqs[-1] + 1))
353
+ and parts[seqs[0]][1].startswith(endpoint_bytes)
354
+ )
355
+ if not complete:
356
+ _remember(key, parts)
357
+ return None
358
+ return _decode_payload(b"".join(parts[s][1] for s in seqs))
359
+
360
+ def _decode_map_response(message: RoborockMessage) -> MapResponse | None:
361
+ """Decode a V1 map response message."""
362
+ if not message.payload:
363
+ raise RoborockException("Invalid V1 map response format: missing payload")
364
+ payload = message.payload
365
+ key = (message.random, message.timestamp)
366
+ is_last = message.protocol == RoborockMessageProtocol.MAP_RESPONSE
367
+ if message.protocol == RoborockMessageProtocol.MAP_RESPONSE_PART or (is_last and key in pending):
368
+ return _add_part(key, message.seq, is_last, payload)
369
+ if is_last and not payload.startswith(endpoint_bytes):
370
+ # Possibly the last part of a multi-part response that arrived first.
371
+ _remember(key, {message.seq: (True, payload)})
372
+ return _decode_payload(payload)
373
+
328
374
  return _decode_map_response
329
375
 
330
376
 
@@ -15,6 +15,7 @@ class RoborockMessageProtocol(RoborockEnum):
15
15
  GENERAL_RESPONSE = 5
16
16
  RPC_REQUEST = 101
17
17
  RPC_RESPONSE = 102
18
+ MAP_RESPONSE_PART = 300 # Non-final part of a map response split into multiple messages
18
19
  MAP_RESPONSE = 301
19
20
 
20
21