python-roborock 7.14.0__tar.gz → 7.15.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 (140) hide show
  1. {python_roborock-7.14.0 → python_roborock-7.15.0}/PKG-INFO +1 -1
  2. {python_roborock-7.14.0 → python_roborock-7.15.0}/pyproject.toml +1 -1
  3. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/data/__init__.py +1 -0
  4. python_roborock-7.15.0/roborock/data/camera.py +158 -0
  5. python_roborock-7.15.0/roborock/data/mower/mower_code_mappings.py +398 -0
  6. python_roborock-7.15.0/roborock/data/mower/mower_containers.py +113 -0
  7. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/data/v1/__init__.py +1 -0
  8. python_roborock-7.15.0/roborock/data/v1/v1_dynamic_map.py +221 -0
  9. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/a01/device_feature.py +5 -2
  10. python_roborock-7.15.0/roborock/protocols/camera_signaling.py +311 -0
  11. python_roborock-7.14.0/roborock/data/mower/mower_code_mappings.py +0 -72
  12. python_roborock-7.14.0/roborock/data/mower/mower_containers.py +0 -69
  13. {python_roborock-7.14.0 → python_roborock-7.15.0}/.gitignore +0 -0
  14. {python_roborock-7.14.0 → python_roborock-7.15.0}/LICENSE +0 -0
  15. {python_roborock-7.14.0 → python_roborock-7.15.0}/README.md +0 -0
  16. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/__init__.py +0 -0
  17. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/broadcast_protocol.py +0 -0
  18. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/callbacks.py +0 -0
  19. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/cli.py +0 -0
  20. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/const.py +0 -0
  21. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/data/b01_q10/__init__.py +0 -0
  22. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/data/b01_q10/b01_q10_code_mappings.py +0 -0
  23. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/data/b01_q10/b01_q10_containers.py +0 -0
  24. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/data/b01_q7/__init__.py +0 -0
  25. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/data/b01_q7/b01_q7_code_mappings.py +0 -0
  26. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/data/b01_q7/b01_q7_containers.py +0 -0
  27. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/data/code_mappings.py +0 -0
  28. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/data/containers.py +0 -0
  29. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/data/dyad/__init__.py +0 -0
  30. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/data/dyad/dyad_code_mappings.py +0 -0
  31. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/data/dyad/dyad_containers.py +0 -0
  32. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/data/mower/__init__.py +0 -0
  33. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/data/v1/v1_clean_modes.py +0 -0
  34. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/data/v1/v1_code_mappings.py +0 -0
  35. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/data/v1/v1_containers.py +0 -0
  36. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/data/zeo/__init__.py +0 -0
  37. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/data/zeo/zeo_code_mappings.py +0 -0
  38. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/data/zeo/zeo_containers.py +0 -0
  39. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/device_features.py +0 -0
  40. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/README.md +0 -0
  41. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/__init__.py +0 -0
  42. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/cache.py +0 -0
  43. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/device.py +0 -0
  44. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/device_manager.py +0 -0
  45. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/file_cache.py +0 -0
  46. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/rpc/__init__.py +0 -0
  47. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/rpc/a01_channel.py +0 -0
  48. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/rpc/b01_q10_channel.py +0 -0
  49. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/rpc/b01_q7_channel.py +0 -0
  50. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/rpc/v1_channel.py +0 -0
  51. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/__init__.py +0 -0
  52. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/a01/__init__.py +0 -0
  53. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/b01/__init__.py +0 -0
  54. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/b01/q10/__init__.py +0 -0
  55. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/b01/q10/button_light.py +0 -0
  56. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/b01/q10/child_lock.py +0 -0
  57. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/b01/q10/clean_history.py +0 -0
  58. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/b01/q10/command.py +0 -0
  59. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/b01/q10/common.py +0 -0
  60. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/b01/q10/consumable.py +0 -0
  61. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/b01/q10/do_not_disturb.py +0 -0
  62. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/b01/q10/dust_collection.py +0 -0
  63. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/b01/q10/goto.py +0 -0
  64. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/b01/q10/map.py +0 -0
  65. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/b01/q10/maps.py +0 -0
  66. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/b01/q10/network_info.py +0 -0
  67. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/b01/q10/remote.py +0 -0
  68. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/b01/q10/status.py +0 -0
  69. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/b01/q10/vacuum.py +0 -0
  70. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/b01/q10/volume.py +0 -0
  71. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/b01/q7/__init__.py +0 -0
  72. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/b01/q7/clean_summary.py +0 -0
  73. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/b01/q7/map.py +0 -0
  74. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/b01/q7/map_content.py +0 -0
  75. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/common.py +0 -0
  76. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/traits_mixin.py +0 -0
  77. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/v1/__init__.py +0 -0
  78. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/v1/child_lock.py +0 -0
  79. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/v1/clean_summary.py +0 -0
  80. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/v1/command.py +0 -0
  81. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/v1/common.py +0 -0
  82. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/v1/consumeable.py +0 -0
  83. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/v1/device_features.py +0 -0
  84. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/v1/do_not_disturb.py +0 -0
  85. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/v1/dust_collection_mode.py +0 -0
  86. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/v1/flow_led_status.py +0 -0
  87. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/v1/home.py +0 -0
  88. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/v1/led_status.py +0 -0
  89. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/v1/map_content.py +0 -0
  90. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/v1/maps.py +0 -0
  91. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/v1/mop_dryer.py +0 -0
  92. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/v1/network_info.py +0 -0
  93. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/v1/obstacle_photos.py +0 -0
  94. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/v1/rooms.py +0 -0
  95. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/v1/routines.py +0 -0
  96. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/v1/smart_wash_params.py +0 -0
  97. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/v1/status.py +0 -0
  98. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/v1/valley_electricity_timer.py +0 -0
  99. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/v1/volume.py +0 -0
  100. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/traits/v1/wash_towel_mode.py +0 -0
  101. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/transport/__init__.py +0 -0
  102. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/transport/channel.py +0 -0
  103. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/transport/local_channel.py +0 -0
  104. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/devices/transport/mqtt_channel.py +0 -0
  105. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/diagnostics.py +0 -0
  106. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/exceptions.py +0 -0
  107. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/map/__init__.py +0 -0
  108. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/map/b01_grid_layers.py +0 -0
  109. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/map/b01_map_parser.py +0 -0
  110. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/map/b01_q10_map_parser.py +0 -0
  111. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/map/b01_q10_overlays.py +0 -0
  112. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/map/b01_q10_render.py +0 -0
  113. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/map/dynamic_blocks.py +0 -0
  114. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/map/map_parser.py +0 -0
  115. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/map/proto/__init__.py +0 -0
  116. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/map/proto/b01_scmap.proto +0 -0
  117. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/map/proto/b01_scmap_pb2.py +0 -0
  118. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/map/room_colors.py +0 -0
  119. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/mqtt/__init__.py +0 -0
  120. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/mqtt/health_manager.py +0 -0
  121. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/mqtt/roborock_session.py +0 -0
  122. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/mqtt/session.py +0 -0
  123. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/protocol.py +0 -0
  124. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/protocols/__init__.py +0 -0
  125. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/protocols/a01_protocol.py +0 -0
  126. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/protocols/b01_q10_protocol.py +0 -0
  127. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/protocols/b01_q7_protocol.py +0 -0
  128. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/protocols/v1_protocol.py +0 -0
  129. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/py.typed +0 -0
  130. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/roborock_message.py +0 -0
  131. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/roborock_typing.py +0 -0
  132. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/testing/__init__.py +0 -0
  133. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/testing/a01_simulator.py +0 -0
  134. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/testing/b01_q10_simulator.py +0 -0
  135. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/testing/channel.py +0 -0
  136. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/testing/cloud.py +0 -0
  137. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/testing/simulator.py +0 -0
  138. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/testing/v1_simulator.py +0 -0
  139. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/util.py +0 -0
  140. {python_roborock-7.14.0 → python_roborock-7.15.0}/roborock/web_api.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: python-roborock
3
- Version: 7.14.0
3
+ Version: 7.15.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 = "7.14.0"
3
+ version = "7.15.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"
@@ -2,6 +2,7 @@
2
2
 
3
3
  from .b01_q7 import *
4
4
  from .b01_q10 import *
5
+ from .camera import *
5
6
  from .code_mappings import *
6
7
  from .containers import *
7
8
  from .dyad import *
@@ -0,0 +1,158 @@
1
+ """Data models for Roborock camera live view."""
2
+
3
+ from dataclasses import dataclass, field
4
+ from enum import StrEnum
5
+ from typing import Any, Self
6
+
7
+ from .containers import RoborockBase
8
+
9
+ __all__ = ["CameraIceCandidate", "CameraQuality", "CameraSdpDescription", "CameraTurnServer"]
10
+
11
+ _VALID_TURN_SCHEMES = ("turn:", "turns:", "stun:", "stuns:")
12
+
13
+
14
+ class CameraQuality(StrEnum):
15
+ """Camera video streaming resolution setting."""
16
+
17
+ HD = "HD"
18
+ SD = "SD"
19
+ FHD = "FHD"
20
+ AUTO = "auto"
21
+
22
+ @classmethod
23
+ def from_str(cls, value: str) -> Self:
24
+ """Parse CameraQuality case-insensitively."""
25
+ normalized = value.strip()
26
+ # "auto" is lowercase in the Roborock app enum, while HD/SD/FHD are uppercase
27
+ for member in cls:
28
+ if member.value.lower() == normalized.lower():
29
+ return member
30
+ valid = ", ".join(repr(m.value) for m in cls)
31
+ raise ValueError(f"Invalid camera quality: {value!r}. Expected one of {valid}.")
32
+
33
+
34
+ @dataclass
35
+ class CameraTurnServer(RoborockBase):
36
+ """Ephemeral TURN server credentials for WebRTC peer connection relay."""
37
+
38
+ urls: list[str]
39
+ username: str | None = None
40
+ credential: str | None = field(default=None, repr=False)
41
+
42
+ @classmethod
43
+ def from_dict(cls, data: dict[str, Any] | list[dict[str, Any]]) -> Self:
44
+ """Create CameraTurnServer from get_turn_server response or dict.
45
+
46
+ Handles both device RPC responses (single-item list or dict with 'url', 'user', 'pwd')
47
+ and standard WebRTC configuration dicts ('urls', 'username', 'credential').
48
+ """
49
+ if isinstance(data, list):
50
+ if not data:
51
+ raise ValueError("Empty TURN server data list")
52
+ item = data[0]
53
+ if not isinstance(item, dict):
54
+ raise TypeError(f"Expected dict inside TURN server list, got {type(item).__name__}")
55
+ data = item
56
+ if not isinstance(data, dict):
57
+ raise TypeError(f"Expected dict or list of dicts for TURN server, got {type(data).__name__}")
58
+
59
+ if "urls" in data:
60
+ urls_val = data["urls"]
61
+ elif "url" in data:
62
+ urls_val = data["url"]
63
+ else:
64
+ raise ValueError("Missing 'url' or 'urls' in TURN server response")
65
+
66
+ urls: list[str] = []
67
+ if isinstance(urls_val, str):
68
+ urls_clean = urls_val.strip()
69
+ if not urls_clean:
70
+ raise ValueError("TURN server URL cannot be empty")
71
+ urls = [urls_clean]
72
+ elif isinstance(urls_val, list):
73
+ if not urls_val:
74
+ raise ValueError("TURN server 'urls' list cannot be empty")
75
+ for u in urls_val:
76
+ if not isinstance(u, str) or not u.strip():
77
+ raise ValueError(f"Each URL in 'urls' must be a non-empty string, got {u!r}")
78
+ urls.append(u.strip())
79
+ else:
80
+ raise TypeError(f"Expected str or list for urls, got {type(urls_val).__name__}")
81
+
82
+ # Validate URL scheme (stun:, stuns:, turn:, turns:)
83
+ for u in urls:
84
+ if not any(u.lower().startswith(scheme) for scheme in _VALID_TURN_SCHEMES):
85
+ raise ValueError(f"Invalid ICE server URL scheme in {u!r}. Expected turn:, turns:, stun:, or stuns:")
86
+
87
+ user_val = data.get("username") if "username" in data else data.get("user")
88
+ pwd_val = data.get("credential") if "credential" in data else data.get("pwd")
89
+
90
+ if user_val is not None and not isinstance(user_val, str):
91
+ raise TypeError(f"Expected str for username, got {type(user_val).__name__}")
92
+ if pwd_val is not None and not isinstance(pwd_val, str):
93
+ raise TypeError(f"Expected str for credential, got {type(pwd_val).__name__}")
94
+
95
+ return cls(
96
+ urls=urls,
97
+ username=user_val,
98
+ credential=pwd_val,
99
+ )
100
+
101
+
102
+ @dataclass
103
+ class CameraIceCandidate(RoborockBase):
104
+ """Parsed WebRTC ICE candidate representation (RFC 5245 / RFC 8839)."""
105
+
106
+ foundation: str
107
+ component: int
108
+ protocol: str
109
+ priority: int
110
+ ip: str
111
+ port: int
112
+ candidate_type: str
113
+ related_address: str | None = None
114
+ related_port: int | None = None
115
+ tcp_type: str | None = None
116
+ sdp_mid: str | None = None
117
+ sdp_mline_index: int | None = None
118
+ extensions: dict[str, str] = field(default_factory=dict)
119
+
120
+ def to_sdp(self) -> str:
121
+ """Format candidate as an SDP attribute line value (RFC 5245 / RFC 8839)."""
122
+ parts = [
123
+ f"candidate:{self.foundation}",
124
+ str(self.component),
125
+ self.protocol.lower(),
126
+ str(self.priority),
127
+ self.ip,
128
+ str(self.port),
129
+ "typ",
130
+ self.candidate_type.lower(),
131
+ ]
132
+ if self.related_address is not None and self.related_port is not None:
133
+ parts.extend(["raddr", self.related_address, "rport", str(self.related_port)])
134
+ if self.tcp_type is not None:
135
+ parts.extend(["tcptype", self.tcp_type.lower()])
136
+ for ext_k, ext_v in self.extensions.items():
137
+ parts.extend([ext_k, ext_v])
138
+ return " ".join(parts)
139
+
140
+ def to_signaling_dict(self) -> dict[str, str | int]:
141
+ """Convert to Roborock camera signaling dictionary format."""
142
+ return {
143
+ "candidate": self.to_sdp(),
144
+ "sdpMid": self.sdp_mid if self.sdp_mid is not None else "0",
145
+ "sdpMLineIndex": self.sdp_mline_index if self.sdp_mline_index is not None else 0,
146
+ }
147
+
148
+ def __str__(self) -> str:
149
+ """Return the SDP candidate string."""
150
+ return self.to_sdp()
151
+
152
+
153
+ @dataclass
154
+ class CameraSdpDescription(RoborockBase):
155
+ """WebRTC session description exchanged during camera signaling."""
156
+
157
+ sdp: str
158
+ type: str
@@ -0,0 +1,398 @@
1
+ """Code mappings for Roborock mower devices."""
2
+
3
+ from typing import Self, cast
4
+
5
+ from roborock.data.code_mappings import RoborockModeEnum
6
+
7
+ __all__ = [
8
+ "MowerAfsStatus",
9
+ "MowerChargeState",
10
+ "MowerChargeType",
11
+ "MowerConfMode",
12
+ "MowerDetailState",
13
+ "MowerDockState",
14
+ "MowerEffMode",
15
+ "MowerEnum",
16
+ "MowerErrorCode",
17
+ "MowerFcState",
18
+ "MowerMappingState",
19
+ "MowerMappingType",
20
+ "MowerMowState",
21
+ "MowerMowType",
22
+ "MowerNetworkChannel",
23
+ "MowerOffDockNoTaskStatus",
24
+ "MowerOfflineStatus",
25
+ "MowerOtaState",
26
+ "MowerPattern",
27
+ "MowerPendType",
28
+ "MowerRemoteState",
29
+ "MowerStartType",
30
+ ]
31
+
32
+
33
+ class MowerEnum(RoborockModeEnum):
34
+ """Base for mower enums."""
35
+
36
+ @classmethod
37
+ def from_code(cls, code: int | str) -> Self:
38
+ """Resolve a wire value by name, string value, or integer code."""
39
+ if (member := cls.from_any_optional(code)) is not None:
40
+ return member
41
+ return cast(Self, cls.UNKNOWN) # type: ignore[attr-defined]
42
+
43
+
44
+ class MowerMowType(MowerEnum):
45
+ """Mowing task selection reported by DPS 122."""
46
+
47
+ UNKNOWN = ("unknown", -1)
48
+ NONE = ("none", 0)
49
+ GLOBAL = ("global", 1)
50
+ EDGE = ("edge", 2)
51
+ SELECTION = ("selection", 3)
52
+ ZONING = ("zoning", 4)
53
+ REMOTE = ("remote", 5)
54
+ RANDOM = ("random", 6)
55
+
56
+
57
+ class MowerMowState(MowerEnum):
58
+ """Compact mowing state (MowStateDpValue.Type)."""
59
+
60
+ UNKNOWN = ("unknown", -1)
61
+ IDLE = ("idle", 0)
62
+ ZIGZAG_MOWING = ("zigzag_mowing", 1)
63
+ EDGE_MOWING = ("edge_mowing", 2)
64
+ TRANSITION = ("transition", 3)
65
+ SUSPENDED = ("suspended", 4)
66
+ REMOTE_WALKING = ("remote_walking", 5)
67
+ REMOTE_MOWING = ("remote_mowing", 6)
68
+
69
+
70
+ class MowerMappingType(MowerEnum):
71
+ """Mapping operation reported by DPS 124."""
72
+
73
+ UNKNOWN = ("unknown", -1)
74
+ NONE = ("none", 0)
75
+ LAWN_MAPPING = ("lawn_mapping", 1)
76
+ FORBIDDEN_ZONE_MAPPING = ("forbidden_zone_mapping", 2)
77
+ CHANNEL_MAPPING = ("channel_mapping", 3)
78
+ SPLIT_WORKSPACE = ("split_workspace", 4)
79
+ MERGE_WORKSPACE = ("merge_workspace", 5)
80
+ BOUNDARY_MODIFY = ("boundary_modify", 6)
81
+
82
+
83
+ class MowerMappingState(MowerEnum):
84
+ """Mapping activity reported by DPS 125."""
85
+
86
+ UNKNOWN = ("unknown", -1)
87
+ IDLE = ("idle", 0)
88
+ AUTO_MAPPING = ("auto_mapping", 1)
89
+ TRACK_ERASE = ("track_erase", 2)
90
+ SUSPENDED = ("suspended", 3)
91
+ SAVING_MAP = ("saving_map", 4)
92
+ REMOTE_WALKING = ("remote_walking", 5)
93
+ REMOTE_MAPPING = ("remote_mapping", 6)
94
+
95
+
96
+ class MowerOtaState(MowerEnum):
97
+ """Firmware update state reported by DPS 126."""
98
+
99
+ UNKNOWN = ("unknown", -1)
100
+ IDLE = ("idle", 0)
101
+ DOWNLOADING = ("downloading", 1)
102
+ INSTALLING = ("installing", 2)
103
+ REBOOTING = ("rebooting", 3)
104
+
105
+
106
+ class MowerChargeState(MowerEnum):
107
+ """Charging state reported by DPS 127."""
108
+
109
+ UNKNOWN = ("unknown", -1)
110
+ NOT_CHARGING = ("not_charging", 0)
111
+ CHARGING = ("charging", 1)
112
+ CHARGE_COMPLETED = ("charge_completed", 2)
113
+ WAITING_CHARGE = ("waiting_charge", 3)
114
+ LOW_POWER = ("low_power", 4)
115
+ CHARGE_ERROR = ("charge_error", 5)
116
+
117
+
118
+ class MowerDockState(MowerEnum):
119
+ """Return-to-dock state reported by DPS 128."""
120
+
121
+ UNKNOWN = ("unknown", -1)
122
+ IDLE = ("idle", 0)
123
+ MOVING_TO_TARGET = ("moving_to_target", 1)
124
+ DOCKING = ("docking", 2)
125
+ SUSPENDED = ("suspended", 3)
126
+
127
+
128
+ class MowerChargeType(MowerEnum):
129
+ """Reason for returning to charge reported by DPS 129."""
130
+
131
+ UNKNOWN = ("unknown", 0)
132
+ MANUAL_DOCK = ("manual_dock", 1)
133
+ RAIN_DOCK = ("rain_dock", 2)
134
+ DO_NOT_DISTURB_DOCK = ("do_not_disturb_dock", 3)
135
+ STANDBY_TIMEOUT_DOCK = ("standby_timeout_dock", 4)
136
+ LOW_BATTERY_DOCK = ("low_battery_dock", 5)
137
+ TASK_FINISHED_DOCK = ("task_finished_dock", 6)
138
+ MOW_FAULT_DOCK = ("mow_fault_dock", 7)
139
+
140
+
141
+ class MowerPendType(MowerEnum):
142
+ """Pause or interruption reason reported by DPS 130."""
143
+
144
+ UNKNOWN = ("unknown", -1)
145
+ NOT_SUSPENDED = ("not_suspended", 0)
146
+ APP_PAUSE = ("app_pause", 1)
147
+ BUTTON_EMERGENCY_STOP = ("button_emergency_stop", 2)
148
+ FAULT = ("fault", 3)
149
+ BREAKPOINT_PAUSE = ("breakpoint_pause", 4)
150
+ OTHER = ("other", 5)
151
+
152
+
153
+ class MowerRemoteState(MowerEnum):
154
+ """Remote control state reported by DPS 131."""
155
+
156
+ UNKNOWN = ("unknown", -1)
157
+ IDLE = ("idle", 0)
158
+ VIDEO_REMOTE = ("video_remote", 1)
159
+ REMOTE_WALKING = ("remote_walking", 2)
160
+
161
+
162
+ class MowerStartType(MowerEnum):
163
+ """Task start trigger reported by DPS 132."""
164
+
165
+ UNKNOWN = ("unknown", -1)
166
+ DEFAULT = ("default", 0)
167
+ APP_START = ("app_start", 1)
168
+ BUTTON_START = ("button_start", 2)
169
+ SCHEDULE_START = ("schedule_start", 3)
170
+
171
+
172
+ class MowerEffMode(MowerEnum):
173
+ """Reported efficiency mode on DPS 133."""
174
+
175
+ UNKNOWN = ("unknown", 0)
176
+ DAILY = ("daily", 1)
177
+ PRECISE = ("precise", 2)
178
+ FAST = ("fast", 3)
179
+
180
+
181
+ class MowerPattern(MowerEnum):
182
+ """Reported mowing pattern on DPS 136."""
183
+
184
+ UNKNOWN = ("unknown", 0)
185
+ ZIGZAG = ("zigzag", 1)
186
+ WELL = ("well", 2)
187
+ DIAMOND = ("diamond", 3)
188
+ TRIANGLE = ("triangle", 4)
189
+ AUTO_DEFLECT = ("auto_deflect", 5)
190
+ RANDOM = ("random", 6)
191
+
192
+
193
+ class MowerConfMode(MowerEnum):
194
+ """Preference configuration scope on DPS 137."""
195
+
196
+ UNKNOWN = ("unknown", 0)
197
+ GLOBAL_CONFIG = ("global_config", 1)
198
+ ZONE_CUSTOM = ("zone_custom", 2)
199
+
200
+
201
+ class MowerOfflineStatus(MowerEnum):
202
+ """Shutdown or reboot reason on DPS 138."""
203
+
204
+ UNKNOWN = ("unknown", -1)
205
+ ONLINE = ("online", 0)
206
+ MANUAL_SHUTDOWN = ("manual_shutdown", 1)
207
+ SLEEP_SHUTDOWN = ("sleep_shutdown", 2)
208
+ LOW_BATTERY_SHUTDOWN = ("low_battery_shutdown", 3)
209
+ UPGRADE_REBOOT = ("upgrade_reboot", 4)
210
+
211
+
212
+ class MowerFcState(MowerEnum):
213
+ """Factory or test mode indicator on DPS 141."""
214
+
215
+ UNKNOWN = ("unknown", -1)
216
+ NOT_FC = ("not_fc", 0)
217
+ FC = ("fc", 1)
218
+
219
+
220
+ class MowerOffDockNoTaskStatus(MowerEnum):
221
+ """Mower off-dock status without an active task on DPS 143."""
222
+
223
+ UNKNOWN = ("unknown", 0)
224
+ WAITING = ("waiting", 1)
225
+ SUSPENDING = ("suspending", 2)
226
+ DOCKING = ("docking", 3)
227
+ REMOTING = ("remoting", 4)
228
+
229
+
230
+ class MowerAfsStatus(MowerEnum):
231
+ """Service status on DPS 144."""
232
+
233
+ UNKNOWN = ("unknown", -1)
234
+ IDLE = ("idle", 0)
235
+ BIT = ("bit", 1)
236
+ MMT_OBA = ("mmt_oba", 2)
237
+ CALIBRATION = ("calibration", 3)
238
+ ACTIVE = ("active", 4)
239
+ INACTIVE = ("inactive", 5)
240
+
241
+
242
+ class MowerNetworkChannel(MowerEnum):
243
+ """Current network route on DPS 145."""
244
+
245
+ UNKNOWN = ("unknown", 0)
246
+ WLAN0 = ("wlan0", 1)
247
+ WLAN1 = ("wlan1", 2)
248
+ MOBILE_4G = ("mobile_4g", 3)
249
+
250
+
251
+ class MowerDetailState(MowerEnum):
252
+ """Detailed task state reported on DPS 123 and in GET_ROBOT_INFO replies."""
253
+
254
+ UNKNOWN = ("unknown", 0)
255
+
256
+ MAP_INITIALIZING = ("map_initializing", 1)
257
+ MAP_UNDOCKING = ("map_undocking", 2)
258
+ MAP_UNDOCK_FAULT = ("map_undock_fault", 3)
259
+ MAP_LOCATING = ("map_locating", 4)
260
+ MAP_PREPARE_BOUNDARY = ("map_prepare_boundary", 5)
261
+ MAP_PREPARE_ISLAND = ("map_prepare_island", 6)
262
+ MAP_PREPARE_PATH = ("map_prepare_path", 7)
263
+ MAP_BOUNDARY = ("map_boundary", 8)
264
+ MAP_ISLAND = ("map_island", 9)
265
+ MAP_PATH = ("map_path", 10)
266
+ MAP_BOUNDARY_AUTO = ("map_boundary_auto", 11)
267
+ # The protocol spelling "MAP_ERASEING" is intentional.
268
+ MAP_ERASING = ("map_eraseing", 12)
269
+ MAP_SAVE = ("map_save", 13)
270
+ MAP_WAIT = ("map_wait", 14)
271
+ MAP_RECOVERABLE_FAULT = ("map_recoverable_fault", 15)
272
+ MAP_FAULT = ("map_fault", 16)
273
+ MAP_EMERGENCY_STOP = ("map_emergency_stop", 17)
274
+ MAP_WAITING_FAULT = ("map_waiting_fault", 18)
275
+ MAP_NO_AVOID = ("map_no_avoid", 19)
276
+ MAP_PREPARE_NO_AVOID = ("map_prepare_no_avoid", 20)
277
+ MAP_RESET_PASSAGE = ("map_reset_passage", 21)
278
+ MAP_PREPARE_RESET_PASSAGE = ("map_prepare_reset_passage", 22)
279
+ MAP_EDIT_BOUNDARY = ("map_edit_boundary", 23)
280
+ MAP_PREPARE_EDIT_BOUNDARY = ("map_prepare_edit_boundary", 24)
281
+ MAP_CONFIRM_EDIT_BOUNDARY = ("map_confirm_edit_boundary", 25)
282
+
283
+ MOW_INITIALIZING = ("mow_initializing", 51)
284
+ MOW_UNDOCKING = ("mow_undocking", 52)
285
+ MOW_LOCATING = ("mow_locating", 53)
286
+ MOW_ADJUST_CUTTER = ("mow_adjust_cutter", 54)
287
+ MOW_ZIG_ZAG = ("mow_zig_zag", 55)
288
+ MOW_EDGE = ("mow_edge", 56)
289
+ MOW_GOTO = ("mow_goto", 57)
290
+ MOW_SUSPEND = ("mow_suspend", 58)
291
+ MOW_RECOVERABLE_FAULT = ("mow_recoverable_fault", 59)
292
+ MOW_FAULT = ("mow_fault", 60)
293
+ MOW_DOCK_RAINFALL = ("mow_dock_rainfall", 61)
294
+ MOW_DOCK_NOTDISTURB = ("mow_dock_notdisturb", 62)
295
+ MOW_DOCK_LOW_BATTERY = ("mow_dock_low_battery", 63)
296
+ MOW_WAIT = ("mow_wait", 64)
297
+ MOW_PREPARE_REMOTE = ("mow_prepare_remote", 65)
298
+ MOW_REMOTE = ("mow_remote", 66)
299
+ MOW_EMERGENCY_STOP = ("mow_emergency_stop", 67)
300
+ MOW_DOCK_MANUAL = ("mow_dock_manual", 68)
301
+ MOW_DOCK_FAULT = ("mow_dock_fault", 69)
302
+ MOW_REMOTE_UNDOCKING = ("mow_remote_undocking", 70)
303
+ MOW_TO_DOCK_INITIALIZING = ("mow_to_dock_initializing", 71)
304
+ MOW_TO_DOCK_LOCATING = ("mow_to_dock_locating", 72)
305
+ MOW_TO_DOCK_RECOVERABLE_FAULT = ("mow_to_dock_recoverable_fault", 73)
306
+ MOW_TO_DOCK_FAULT = ("mow_to_dock_fault", 74)
307
+ MOW_TO_DOCK_EMERGENCY_STOP = ("mow_to_dock_emergency_stop", 75)
308
+ MOW_TO_DOCK_CHARGING = ("mow_to_dock_charging", 76)
309
+ MOW_TO_DOCK_CHARGE_COMPLETED = ("mow_to_dock_charge_completed", 77)
310
+ MOW_PAUSE = ("mow_pause", 78)
311
+
312
+ FREE = ("free", 101)
313
+ FREE_INITIALIZING = ("free_initializing", 102)
314
+ FREE_LOCATING = ("free_locating", 103)
315
+ FREE_DOCK_MANUAL = ("free_dock_manual", 104)
316
+ FREE_DOCK_MOW_END = ("free_dock_mow_end", 105)
317
+ FREE_DOCK_PLAN_END = ("free_dock_plan_end", 106)
318
+ FREE_EMERGENCY_STOP = ("free_emergency_stop", 107)
319
+ FREE_RECOVERABLE_FAULT = ("free_recoverable_fault", 108)
320
+ FREE_FAULT = ("free_fault", 109)
321
+ FREE_BATTERY_HOLD_MODE = ("free_battery_hold_mode", 110)
322
+
323
+ CHARGING = ("charging", 151)
324
+ CHARGE_COMPLETED = ("charge_completed", 152)
325
+ CHARGE_WAITING = ("charge_waiting", 153)
326
+ CHARGE_FAULT = ("charge_fault", 154)
327
+
328
+
329
+ class MowerErrorCode(MowerEnum):
330
+ """User-facing error code reported by DPS 120."""
331
+
332
+ UNKNOWN = ("unknown", 0)
333
+ MAIN_CUTTER_OVER_CURRENT = ("main_cutter_over_current", 1)
334
+ MAIN_CUTTER_OVER_TEMPERATURE = ("main_cutter_over_temperature", 2)
335
+ MAIN_CUTTER_DYNAMIC_UNBALANCE = ("main_cutter_dynamic_unbalance", 3)
336
+ EDGE_CUTTER_OVER_CURRENT = ("edge_cutter_over_current", 4)
337
+ EDGE_CUTTER_OVER_TEMPERATURE = ("edge_cutter_over_temperature", 5)
338
+ EDGE_CUTTER_DYNAMIC_UNBALANCE = ("edge_cutter_dynamic_unbalance", 6)
339
+ HEIGHT_MODULE_ADJUSTING_INCOMPLETE = ("height_module_adjusting_incomplete", 7)
340
+ TRAVEL_MOTOR_LEFT_FRONT_OVER_CURRENT = ("travel_motor_left_front_over_current", 8)
341
+ TRAVEL_MOTOR_RIGHT_FRONT_OVER_CURRENT = ("travel_motor_right_front_over_current", 9)
342
+ TRAVEL_MOTOR_LEFT_REAR_OVER_CURRENT = ("travel_motor_left_rear_over_current", 10)
343
+ TRAVEL_MOTOR_RIGHT_REAR_OVER_CURRENT = ("travel_motor_right_rear_over_current", 11)
344
+ TRAVEL_MOTOR_OVER_TEMPERATURE = ("travel_motor_over_temperature", 12)
345
+ STEERING_MOTOR_LEFT_FRONT_OVER_CURRENT = ("steering_motor_left_front_over_current", 13)
346
+ STEERING_MOTOR_RIGHT_FRONT_OVER_CURRENT = ("steering_motor_right_front_over_current", 14)
347
+ STEERING_MOTOR_OVER_TEMPERATURE = ("steering_motor_over_temperature", 15)
348
+ STOP = ("stop", 16)
349
+ TILT = ("tilt", 17)
350
+ LIFT = ("lift", 18)
351
+ FLIP = ("flip", 19)
352
+ OUT_OF_BOUND = ("out_of_bound", 20)
353
+ BUMPER = ("bumper", 21)
354
+ BATTERY_DISCHARGE_OVER_TEMPERATURE = ("battery_discharge_over_temperature", 22)
355
+ BATTERY_DISCHARGE_LOW_TEMPERATURE = ("battery_discharge_low_temperature", 23)
356
+ BATTERY_UNOFFICIAL = ("battery_unofficial", 24)
357
+ DIRTY_CHARGING_CLIPS = ("dirty_charging_clips", 25)
358
+ CHARGE_STATION_NO_POWER = ("charge_station_no_power", 26)
359
+ CAMERA_DIRTY_OR_OBSTRUCTED = ("camera_dirty_or_obstructed", 27)
360
+ CAMERA_OVER_TEMPERATURE = ("camera_over_temperature", 28)
361
+ CAMERA_LED_OBSTRUCTED = ("camera_led_obstructed", 29)
362
+ LIDAR_DIRTY = ("lidar_dirty", 30)
363
+ LIDAR_OVER_TEMPERATURE = ("lidar_over_temperature", 31)
364
+ LOCATION_FAILED = ("location_failed", 32)
365
+ LOCATION_AT_ISLAND = ("location_at_island", 33)
366
+ LOCATION_AT_NON_COMPLIANT_SLOPE = ("location_at_non_compliant_slope", 34)
367
+ BE_TRAPPED = ("be_trapped", 35)
368
+ CHARGE_STATION_OR_RTK_SLIGHT_MOVEMENT_DOCK = ("charge_station_or_rtk_slight_movement_dock", 36)
369
+ N_RTK_SIGNIFICANT_MOVEMENT = ("n_rtk_significant_movement", 37)
370
+ RTK_SIGNIFICANT_MOVEMENT = ("rtk_significant_movement", 38)
371
+ DOCK_CHARGE_STATION_SEARCH_FAIL = ("dock_charge_station_search_fail", 39)
372
+ DOCK_QRCODE_SCAN_FAIL = ("dock_qrcode_scan_fail", 40)
373
+ DOCK_ADJUST_ERROR = ("dock_adjust_error", 41)
374
+ UNDOCK_ERROR = ("undock_error", 42)
375
+ BATTERY_CHARGE_OVER_TEMPERATURE = ("battery_charge_over_temperature", 43)
376
+ BATTERY_CHARGE_LOW_TEMPERATURE = ("battery_charge_low_temperature", 44)
377
+ CAMERA_SEVERELY_DIRTY = ("camera_severely_dirty", 45)
378
+ NO_PLUGIN_KEY = ("no_plugin_key", 46)
379
+ CAMERA_REAR_SIDE_DIRTY = ("camera_rear_side_dirty", 48)
380
+ N_RTK_NETWORK_ERROR = ("n_rtk_network_error", 55)
381
+ MAIN_CUTTER_FAULT = ("main_cutter_fault", 101)
382
+ EDGE_CUTTER_FAULT = ("edge_cutter_fault", 102)
383
+ HEIGHT_MODULE_FAULT = ("height_module_fault", 103)
384
+ BATTERY_FAULT = ("battery_fault", 104)
385
+ CAMERA_FAULT = ("camera_fault", 105)
386
+ CAMERA_LED_FAULT = ("camera_led_fault", 106)
387
+ MAIN_BOARD_FAULT = ("main_board_fault", 107)
388
+ LIDAR_FAULT = ("lidar_fault", 108)
389
+ TRAVEL_MOTOR_LEFT_FRONT_FAULT = ("travel_motor_left_front_fault", 109)
390
+ TRAVEL_MOTOR_RIGHT_FRONT_FAULT = ("travel_motor_right_front_fault", 110)
391
+ TRAVEL_MOTOR_LEFT_REAR_FAULT = ("travel_motor_left_rear_fault", 111)
392
+ TRAVEL_MOTOR_RIGHT_REAR_FAULT = ("travel_motor_right_rear_fault", 112)
393
+ STEERING_MOTOR_LEFT_FRONT_FAULT = ("steering_motor_left_front_fault", 113)
394
+ STEERING_MOTOR_RIGHT_FRONT_FAULT = ("steering_motor_right_front_fault", 114)
395
+ LED_SCREEN_FAULT = ("led_screen_fault", 115)
396
+ LEFT_SIDE_CAMERA_FAULT = ("left_side_camera_fault", 116)
397
+ RIGHT_SIDE_CAMERA_FAULT = ("right_side_camera_fault", 117)
398
+ REAR_SIDE_CAMERA_FAULT = ("rear_side_camera_fault", 118)