python-roborock 5.15.1__tar.gz → 5.16.0__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 (104) hide show
  1. {python_roborock-5.15.1 → python_roborock-5.16.0}/PKG-INFO +1 -1
  2. {python_roborock-5.15.1 → python_roborock-5.16.0}/pyproject.toml +1 -1
  3. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/b01/q7/__init__.py +4 -0
  4. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/web_api.py +43 -3
  5. {python_roborock-5.15.1 → python_roborock-5.16.0}/.gitignore +0 -0
  6. {python_roborock-5.15.1 → python_roborock-5.16.0}/LICENSE +0 -0
  7. {python_roborock-5.15.1 → python_roborock-5.16.0}/README.md +0 -0
  8. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/__init__.py +0 -0
  9. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/broadcast_protocol.py +0 -0
  10. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/callbacks.py +0 -0
  11. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/cli.py +0 -0
  12. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/const.py +0 -0
  13. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/data/__init__.py +0 -0
  14. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/data/b01_q10/__init__.py +0 -0
  15. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/data/b01_q10/b01_q10_code_mappings.py +0 -0
  16. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/data/b01_q10/b01_q10_containers.py +0 -0
  17. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/data/b01_q7/__init__.py +0 -0
  18. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/data/b01_q7/b01_q7_code_mappings.py +0 -0
  19. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/data/b01_q7/b01_q7_containers.py +0 -0
  20. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/data/code_mappings.py +0 -0
  21. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/data/containers.py +0 -0
  22. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/data/dyad/__init__.py +0 -0
  23. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/data/dyad/dyad_code_mappings.py +0 -0
  24. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/data/dyad/dyad_containers.py +0 -0
  25. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/data/v1/__init__.py +0 -0
  26. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/data/v1/v1_clean_modes.py +0 -0
  27. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/data/v1/v1_code_mappings.py +0 -0
  28. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/data/v1/v1_containers.py +0 -0
  29. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/data/zeo/__init__.py +0 -0
  30. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/data/zeo/zeo_code_mappings.py +0 -0
  31. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/data/zeo/zeo_containers.py +0 -0
  32. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/device_features.py +0 -0
  33. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/README.md +0 -0
  34. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/__init__.py +0 -0
  35. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/cache.py +0 -0
  36. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/device.py +0 -0
  37. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/device_manager.py +0 -0
  38. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/file_cache.py +0 -0
  39. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/rpc/__init__.py +0 -0
  40. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/rpc/a01_channel.py +0 -0
  41. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/rpc/b01_q10_channel.py +0 -0
  42. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/rpc/b01_q7_channel.py +0 -0
  43. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/rpc/v1_channel.py +0 -0
  44. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/__init__.py +0 -0
  45. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/a01/__init__.py +0 -0
  46. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/b01/__init__.py +0 -0
  47. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/b01/q10/__init__.py +0 -0
  48. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/b01/q10/command.py +0 -0
  49. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/b01/q10/remote.py +0 -0
  50. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/b01/q10/status.py +0 -0
  51. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/b01/q10/vacuum.py +0 -0
  52. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/b01/q7/clean_summary.py +0 -0
  53. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/b01/q7/map.py +0 -0
  54. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/b01/q7/map_content.py +0 -0
  55. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/common.py +0 -0
  56. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/traits_mixin.py +0 -0
  57. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/v1/__init__.py +0 -0
  58. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/v1/child_lock.py +0 -0
  59. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/v1/clean_summary.py +0 -0
  60. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/v1/command.py +0 -0
  61. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/v1/common.py +0 -0
  62. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/v1/consumeable.py +0 -0
  63. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/v1/device_features.py +0 -0
  64. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/v1/do_not_disturb.py +0 -0
  65. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/v1/dust_collection_mode.py +0 -0
  66. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/v1/flow_led_status.py +0 -0
  67. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/v1/home.py +0 -0
  68. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/v1/led_status.py +0 -0
  69. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/v1/map_content.py +0 -0
  70. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/v1/maps.py +0 -0
  71. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/v1/network_info.py +0 -0
  72. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/v1/rooms.py +0 -0
  73. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/v1/routines.py +0 -0
  74. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/v1/smart_wash_params.py +0 -0
  75. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/v1/status.py +0 -0
  76. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/v1/valley_electricity_timer.py +0 -0
  77. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/v1/volume.py +0 -0
  78. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/traits/v1/wash_towel_mode.py +0 -0
  79. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/transport/__init__.py +0 -0
  80. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/transport/channel.py +0 -0
  81. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/transport/local_channel.py +0 -0
  82. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/devices/transport/mqtt_channel.py +0 -0
  83. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/diagnostics.py +0 -0
  84. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/exceptions.py +0 -0
  85. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/map/__init__.py +0 -0
  86. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/map/b01_map_parser.py +0 -0
  87. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/map/map_parser.py +0 -0
  88. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/map/proto/__init__.py +0 -0
  89. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/map/proto/b01_scmap.proto +0 -0
  90. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/map/proto/b01_scmap_pb2.py +0 -0
  91. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/mqtt/__init__.py +0 -0
  92. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/mqtt/health_manager.py +0 -0
  93. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/mqtt/roborock_session.py +0 -0
  94. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/mqtt/session.py +0 -0
  95. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/protocol.py +0 -0
  96. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/protocols/__init__.py +0 -0
  97. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/protocols/a01_protocol.py +0 -0
  98. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/protocols/b01_q10_protocol.py +0 -0
  99. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/protocols/b01_q7_protocol.py +0 -0
  100. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/protocols/v1_protocol.py +0 -0
  101. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/py.typed +0 -0
  102. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/roborock_message.py +0 -0
  103. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/roborock_typing.py +0 -0
  104. {python_roborock-5.15.1 → python_roborock-5.16.0}/roborock/util.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-roborock
3
- Version: 5.15.1
3
+ Version: 5.16.0
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 = "5.15.1"
3
+ version = "5.16.0"
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"
@@ -108,6 +108,10 @@ class Q7PropertiesApi(Trait):
108
108
  """Set the cleaning repeat state (cycles)."""
109
109
  await self.set_prop(RoborockB01Props.REPEAT_STATE, repeat.code)
110
110
 
111
+ async def set_volume(self, volume: int) -> None:
112
+ """Set the robot voice volume (0-100)."""
113
+ await self.set_prop(RoborockB01Props.VOLUME, volume)
114
+
111
115
  async def start_clean(self) -> None:
112
116
  """Start cleaning."""
113
117
  await self.send(
@@ -1,6 +1,7 @@
1
1
  import base64
2
2
  import hashlib
3
3
  import hmac
4
+ import json
4
5
  import logging
5
6
  import math
6
7
  import secrets
@@ -646,6 +647,31 @@ class RoborockApiClient:
646
647
  else:
647
648
  raise RoborockException(f"schedule_response result was an unexpected type: {schedules}")
648
649
 
650
+ async def create_job(self, user_data: UserData, device_id: str, job: dict) -> dict:
651
+ """Create a /jobs entry (schedule or one-time room clean) on a B01 device.
652
+
653
+ Body-bearing writes must sign the request body in the Hawk payload slot and send those same
654
+ compact bytes via ``data=``; ``json=`` would re-serialize with spaces and break the MAC.
655
+ """
656
+ rriot = user_data.rriot
657
+ if rriot is None:
658
+ raise RoborockException("rriot is none")
659
+ if rriot.r.a is None:
660
+ raise RoborockException("Missing field 'a' in rriot reference")
661
+ path = f"/user/devices/{device_id}/jobs"
662
+ job_request = PreparedRequest(
663
+ rriot.r.a,
664
+ self.session,
665
+ {
666
+ "Authorization": _get_hawk_authentication(rriot, path, body=job),
667
+ "Content-Type": "application/json",
668
+ },
669
+ )
670
+ response = await job_request.request("post", path, data=_compact_json(job).encode())
671
+ if not response.get("success"):
672
+ raise RoborockException(response)
673
+ return response
674
+
649
675
  async def get_products(self, user_data: UserData) -> ProductResponse:
650
676
  """Gets all products and their schemas, good for determining status codes and model numbers."""
651
677
  base_url = await self.base_url
@@ -738,11 +764,25 @@ def _process_extra_hawk_values(values: dict | None) -> str:
738
764
  return hashlib.md5("&".join(result).encode()).hexdigest()
739
765
 
740
766
 
741
- def _get_hawk_authentication(rriot: RRiot, url: str, formdata: dict | None = None, params: dict | None = None) -> str:
767
+ def _compact_json(body: dict) -> str:
768
+ """Serialize a JSON body to the exact compact bytes that are both signed and sent."""
769
+ return json.dumps(body, separators=(",", ":"))
770
+
771
+
772
+ def _get_hawk_authentication(
773
+ rriot: RRiot,
774
+ url: str,
775
+ formdata: dict | None = None,
776
+ params: dict | None = None,
777
+ body: dict | None = None,
778
+ ) -> str:
742
779
  timestamp = math.floor(time.time())
743
780
  nonce = secrets.token_urlsafe(6)
744
- formdata_str = _process_extra_hawk_values(formdata)
745
781
  params_str = _process_extra_hawk_values(params)
782
+ if body is not None:
783
+ payload_str = hashlib.md5(_compact_json(body).encode()).hexdigest()
784
+ else:
785
+ payload_str = _process_extra_hawk_values(formdata)
746
786
 
747
787
  prestr = ":".join(
748
788
  [
@@ -752,7 +792,7 @@ def _get_hawk_authentication(rriot: RRiot, url: str, formdata: dict | None = Non
752
792
  str(timestamp),
753
793
  hashlib.md5(url.encode()).hexdigest(),
754
794
  params_str,
755
- formdata_str,
795
+ payload_str,
756
796
  ]
757
797
  )
758
798
  mac = base64.b64encode(hmac.new(rriot.h.encode(), prestr.encode(), hashlib.sha256).digest()).decode()