pymammotion 0.4.11b1__tar.gz → 0.4.12__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 (123) hide show
  1. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/PKG-INFO +1 -2
  2. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/bluetooth/ble.py +2 -4
  3. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/data/model/hash_list.py +52 -16
  4. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/mammotion/commands/abstract_message.py +4 -0
  5. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/mammotion/commands/mammotion_command.py +2 -1
  6. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/mammotion/commands/messages/driver.py +2 -2
  7. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/mammotion/commands/messages/media.py +2 -2
  8. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/mammotion/commands/messages/navigation.py +2 -2
  9. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/mammotion/commands/messages/network.py +3 -4
  10. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/mammotion/commands/messages/ota.py +5 -3
  11. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/mammotion/commands/messages/system.py +9 -10
  12. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/mammotion/commands/messages/video.py +2 -2
  13. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/mammotion/devices/base.py +16 -11
  14. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/mammotion/devices/mammotion_bluetooth.py +1 -1
  15. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/mammotion/devices/mammotion_cloud.py +6 -3
  16. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/__init__.py +1 -1
  17. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pyproject.toml +4 -3
  18. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/LICENSE +0 -0
  19. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/README.md +0 -0
  20. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/__init__.py +0 -0
  21. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/aliyun/__init__.py +0 -0
  22. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/aliyun/cloud_gateway.py +0 -0
  23. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/aliyun/model/aep_response.py +0 -0
  24. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/aliyun/model/connect_response.py +0 -0
  25. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/aliyun/model/dev_by_account_response.py +0 -0
  26. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/aliyun/model/login_by_oauth_response.py +0 -0
  27. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/aliyun/model/regions_response.py +0 -0
  28. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/aliyun/model/session_by_authcode_response.py +0 -0
  29. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/aliyun/model/stream_subscription_response.py +0 -0
  30. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/aliyun/tmp_constant.py +0 -0
  31. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/bluetooth/__init__.py +0 -0
  32. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/bluetooth/ble_message.py +0 -0
  33. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/bluetooth/const.py +0 -0
  34. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/bluetooth/data/__init__.py +0 -0
  35. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/bluetooth/data/convert.py +0 -0
  36. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/bluetooth/data/framectrldata.py +0 -0
  37. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/bluetooth/data/notifydata.py +0 -0
  38. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/bluetooth/model/__init__.py +0 -0
  39. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/bluetooth/model/atomic_integer.py +0 -0
  40. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/const.py +0 -0
  41. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/data/__init__.py +0 -0
  42. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/data/model/__init__.py +0 -0
  43. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/data/model/account.py +0 -0
  44. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/data/model/device.py +0 -0
  45. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/data/model/device_config.py +0 -0
  46. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/data/model/device_info.py +0 -0
  47. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/data/model/device_limits.py +0 -0
  48. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/data/model/enums.py +0 -0
  49. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/data/model/excute_boarder_params.py +0 -0
  50. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/data/model/execute_boarder.py +0 -0
  51. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/data/model/generate_route_information.py +0 -0
  52. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/data/model/location.py +0 -0
  53. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/data/model/mowing_modes.py +0 -0
  54. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/data/model/plan.py +0 -0
  55. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/data/model/rapid_state.py +0 -0
  56. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/data/model/raw_data.py +0 -0
  57. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/data/model/region_data.py +0 -0
  58. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/data/model/report_info.py +0 -0
  59. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/data/mqtt/__init__.py +0 -0
  60. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/data/mqtt/event.py +0 -0
  61. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/data/mqtt/properties.py +0 -0
  62. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/data/mqtt/status.py +0 -0
  63. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/data/state_manager.py +0 -0
  64. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/event/__init__.py +0 -0
  65. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/event/event.py +0 -0
  66. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/http/_init_.py +0 -0
  67. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/http/encryption.py +0 -0
  68. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/http/http.py +0 -0
  69. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/http/model/http.py +0 -0
  70. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/mammotion/__init__.py +0 -0
  71. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/mammotion/commands/__init__.py +0 -0
  72. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/mammotion/commands/messages/__init__.py +0 -0
  73. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/mammotion/control/__init__.py +0 -0
  74. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/mammotion/control/joystick.py +0 -0
  75. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/mammotion/devices/__init__.py +0 -0
  76. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/mammotion/devices/mammotion.py +0 -0
  77. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/mqtt/__init__.py +0 -0
  78. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/mqtt/linkkit/__init__.py +0 -0
  79. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/mqtt/linkkit/h2client.py +0 -0
  80. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/mqtt/linkkit/linkkit.py +0 -0
  81. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/mqtt/mammotion_future.py +0 -0
  82. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/mqtt/mammotion_mqtt.py +0 -0
  83. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/basestation.proto +0 -0
  84. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/basestation_pb2.py +0 -0
  85. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/basestation_pb2.pyi +0 -0
  86. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/common.proto +0 -0
  87. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/common_pb2.py +0 -0
  88. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/common_pb2.pyi +0 -0
  89. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/dev_net.proto +0 -0
  90. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/dev_net_pb2.py +0 -0
  91. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/dev_net_pb2.pyi +0 -0
  92. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/luba_msg.proto +0 -0
  93. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/luba_msg_pb2.py +0 -0
  94. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/luba_msg_pb2.pyi +0 -0
  95. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/luba_mul.proto +0 -0
  96. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/luba_mul_pb2.py +0 -0
  97. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/luba_mul_pb2.pyi +0 -0
  98. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/mctrl_driver.proto +0 -0
  99. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/mctrl_driver_pb2.py +0 -0
  100. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/mctrl_driver_pb2.pyi +0 -0
  101. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/mctrl_nav.proto +0 -0
  102. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/mctrl_nav_pb2.py +0 -0
  103. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/mctrl_nav_pb2.pyi +0 -0
  104. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/mctrl_ota.proto +0 -0
  105. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/mctrl_ota_pb2.py +0 -0
  106. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/mctrl_ota_pb2.pyi +0 -0
  107. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/mctrl_pept.proto +0 -0
  108. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/mctrl_pept_pb2.py +0 -0
  109. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/mctrl_pept_pb2.pyi +0 -0
  110. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/mctrl_sys.proto +0 -0
  111. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/mctrl_sys_pb2.py +0 -0
  112. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/proto/mctrl_sys_pb2.pyi +0 -0
  113. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/py.typed +0 -0
  114. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/utility/constant/__init__.py +0 -0
  115. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/utility/constant/device_constant.py +0 -0
  116. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/utility/conversions.py +0 -0
  117. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/utility/datatype_converter.py +0 -0
  118. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/utility/device_config.py +0 -0
  119. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/utility/device_type.py +0 -0
  120. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/utility/map.py +0 -0
  121. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/utility/movement.py +0 -0
  122. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/utility/periodic.py +0 -0
  123. {pymammotion-0.4.11b1 → pymammotion-0.4.12}/pymammotion/utility/rocker_util.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pymammotion
3
- Version: 0.4.11b1
3
+ Version: 0.4.12
4
4
  Summary:
5
5
  License: GPL-3.0
6
6
  Author: Michael Arthur
@@ -8,7 +8,6 @@ Author-email: michael@jumblesoft.co.nz
8
8
  Requires-Python: >=3.10
9
9
  Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
10
10
  Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.10
12
11
  Classifier: Programming Language :: Python :: 3.11
13
12
  Classifier: Programming Language :: Python :: 3.12
14
13
  Requires-Dist: aiohttp (>=3.9.1,<4.0.0)
@@ -36,12 +36,10 @@ class MammotionBLE:
36
36
  return await self.connect()
37
37
 
38
38
  async def connect(self) -> bool:
39
- if self.client is not None:
40
- return await self.client.connect()
39
+ return await self.client.connect() if self.client is not None else False
41
40
 
42
41
  async def disconnect(self) -> bool:
43
- if self.client is not None:
44
- return await self.client.disconnect()
42
+ return await self.client.disconnect() if self.client is not None else False
45
43
 
46
44
  async def notification_handler(self, _characteristic: BleakGATTCharacteristic, data: bytearray) -> None:
47
45
  """Simple notification handler which prints the data received."""
@@ -3,7 +3,7 @@ from enum import IntEnum
3
3
 
4
4
  from mashumaro.mixins.orjson import DataClassORJSONMixin
5
5
 
6
- from pymammotion.proto import NavGetCommDataAck, SvgMessageAckT
6
+ from pymammotion.proto import NavGetCommDataAck, NavGetHashListAck, SvgMessageAckT
7
7
 
8
8
 
9
9
  class PathType(IntEnum):
@@ -78,8 +78,9 @@ class SvgMessage(DataClassORJSONMixin):
78
78
 
79
79
  @dataclass
80
80
  class FrameList(DataClassORJSONMixin):
81
- total_frame: int
82
- data: list[NavGetCommData | SvgMessage]
81
+ total_frame: int = 0
82
+ sub_cmd: int = 0
83
+ data: list[NavGetCommData | SvgMessage] = field(default_factory=list)
83
84
 
84
85
 
85
86
  @dataclass(eq=False, repr=False)
@@ -100,6 +101,7 @@ class NavGetHashListData(DataClassORJSONMixin):
100
101
  @dataclass
101
102
  class RootHashList(DataClassORJSONMixin):
102
103
  total_frame: int = 0
104
+ sub_cmd: int = 0
103
105
  data: list[NavGetHashListData] = field(default_factory=list)
104
106
 
105
107
 
@@ -119,11 +121,11 @@ class HashList(DataClassORJSONMixin):
119
121
  """
120
122
 
121
123
  root_hash_lists: list[RootHashList] = field(default_factory=list)
122
- area: dict = field(default_factory=dict) # type 0
123
- path: dict = field(default_factory=dict) # type 2
124
- obstacle: dict = field(default_factory=dict) # type 1
125
- dump: dict = field(default_factory=dict) # type 12?
126
- svg: dict = field(default_factory=dict) # type 13
124
+ area: dict[int, FrameList] = field(default_factory=dict) # type 0
125
+ path: dict[int, FrameList] = field(default_factory=dict) # type 2
126
+ obstacle: dict[int, FrameList] = field(default_factory=dict) # type 1
127
+ dump: dict[int, FrameList] = field(default_factory=dict) # type 12?
128
+ svg: dict[int, FrameList] = field(default_factory=dict) # type 13
127
129
  area_name: list[AreaHashNameList] = field(default_factory=list)
128
130
 
129
131
  def update_hash_lists(self, hashlist: list[int]) -> None:
@@ -132,6 +134,11 @@ class HashList(DataClassORJSONMixin):
132
134
  self.obstacle = {hash_id: frames for hash_id, frames in self.obstacle.items() if hash_id in hashlist}
133
135
  self.dump = {hash_id: frames for hash_id, frames in self.dump.items() if hash_id in hashlist}
134
136
  self.svg = {hash_id: frames for hash_id, frames in self.svg.items() if hash_id in hashlist}
137
+ self.area_name = [
138
+ area_item
139
+ for area_item in self.area_name
140
+ if area_item.hash in self.area.keys() or area_item.hash in self.hashlist
141
+ ]
135
142
 
136
143
  @property
137
144
  def hashlist(self) -> list[int]:
@@ -140,8 +147,7 @@ class HashList(DataClassORJSONMixin):
140
147
  # Combine data_couple from all RootHashLists
141
148
  return [i for root_list in self.root_hash_lists for obj in root_list.data for i in obj.data_couple]
142
149
 
143
- @property
144
- def missing_hashlist(self) -> list[int]:
150
+ def missing_hashlist(self, sub_cmd: int = 0) -> list[int]:
145
151
  """Return missing hashlist."""
146
152
  all_hash_ids = set(self.area.keys()).union(
147
153
  self.path.keys(), self.obstacle.keys(), self.dump.keys(), self.svg.keys()
@@ -150,16 +156,39 @@ class HashList(DataClassORJSONMixin):
150
156
  i
151
157
  for root_list in self.root_hash_lists
152
158
  for obj in root_list.data
159
+ if root_list.sub_cmd == sub_cmd
153
160
  for i in obj.data_couple
154
- if f"{i}" not in all_hash_ids
161
+ if i not in all_hash_ids
155
162
  ]
156
163
 
164
+ def missing_root_hash_frame(self, hash_list: NavGetHashListAck) -> list[int]:
165
+ """Return missing root hash frame."""
166
+ target_root_list = next(
167
+ (
168
+ rhl
169
+ for rhl in self.root_hash_lists
170
+ if rhl.total_frame == hash_list.total_frame and rhl.sub_cmd == hash_list.sub_cmd
171
+ ),
172
+ None,
173
+ )
174
+ if target_root_list is None:
175
+ return []
176
+
177
+ return self._find_missing_frames(target_root_list)
178
+
157
179
  def update_root_hash_list(self, hash_list: NavGetHashListData) -> None:
158
- target_root_list = next((rhl for rhl in self.root_hash_lists if rhl.total_frame == hash_list.total_frame), None)
180
+ target_root_list = next(
181
+ (
182
+ rhl
183
+ for rhl in self.root_hash_lists
184
+ if rhl.total_frame == hash_list.total_frame and rhl.sub_cmd == hash_list.sub_cmd
185
+ ),
186
+ None,
187
+ )
159
188
 
160
189
  if target_root_list is None:
161
190
  # Create new RootHashList if none exists for this total_frame
162
- new_root_list = RootHashList(total_frame=hash_list.total_frame, data=[hash_list])
191
+ new_root_list = RootHashList(total_frame=hash_list.total_frame, sub_cmd=hash_list.sub_cmd, data=[hash_list])
163
192
  self.root_hash_lists.append(new_root_list)
164
193
  return
165
194
 
@@ -172,10 +201,11 @@ class HashList(DataClassORJSONMixin):
172
201
  # If no match was found, append the new item
173
202
  target_root_list.data.append(hash_list)
174
203
 
175
- def missing_hash_frame(self) -> list[int]:
204
+ def missing_hash_frame(self, hash_ack: NavGetHashListAck) -> list[int]:
176
205
  """Returns a combined list of all missing frames across all RootHashLists."""
177
206
  missing_frames = []
178
- for root_list in self.root_hash_lists:
207
+ filtered_lists = [rl for rl in self.root_hash_lists if rl.sub_cmd == hash_ack.sub_cmd]
208
+ for root_list in filtered_lists:
179
209
  missing = self._find_missing_frames(root_list)
180
210
  if missing:
181
211
  missing_frames.extend(missing)
@@ -201,12 +231,15 @@ class HashList(DataClassORJSONMixin):
201
231
 
202
232
  def update(self, hash_data: NavGetCommData | SvgMessage) -> bool:
203
233
  """Update the map data."""
234
+
204
235
  if hash_data.type == PathType.AREA:
205
236
  existing_name = next((area for area in self.area_name if area.hash == hash_data.hash), None)
206
237
  if not existing_name:
207
238
  name = f"area {len(self.area_name)+1}" if hash_data.area_label is None else hash_data.area_label.label
208
239
  self.area_name.append(AreaHashNameList(name=name, hash=hash_data.hash))
209
- return self._add_hash_data(self.area, hash_data)
240
+ result = self._add_hash_data(self.area, hash_data)
241
+ self.update_hash_lists(self.hashlist)
242
+ return result
210
243
 
211
244
  if hash_data.type == PathType.OBSTACLE:
212
245
  return self._add_hash_data(self.obstacle, hash_data)
@@ -224,6 +257,9 @@ class HashList(DataClassORJSONMixin):
224
257
 
225
258
  @staticmethod
226
259
  def _find_missing_frames(frame_list: FrameList | RootHashList) -> list[int]:
260
+ if frame_list is None:
261
+ return []
262
+
227
263
  if frame_list.total_frame == len(frame_list.data):
228
264
  return []
229
265
  number_list = list(range(1, frame_list.total_frame + 1))
@@ -1,10 +1,14 @@
1
1
  from abc import abstractmethod
2
2
 
3
+ from pymammotion.bluetooth.model.atomic_integer import AtomicInteger
3
4
  from pymammotion.proto import MsgCmdType, MsgDevice
4
5
  from pymammotion.utility.device_type import DeviceType
5
6
 
6
7
 
7
8
  class AbstractMessage:
9
+ seqs = AtomicInteger(0)
10
+ user_account: int = 1
11
+
8
12
  @abstractmethod
9
13
  def get_device_name(self) -> str:
10
14
  """Get device name."""
@@ -13,9 +13,10 @@ class MammotionCommand(
13
13
  ):
14
14
  """MQTT commands for Luba."""
15
15
 
16
- def __init__(self, device_name: str) -> None:
16
+ def __init__(self, device_name: str, user_account: int) -> None:
17
17
  self._device_name = device_name
18
18
  self._product_key = ""
19
+ self.user_account = user_account
19
20
 
20
21
  def get_device_name(self) -> str:
21
22
  """Get device name."""
@@ -29,9 +29,9 @@ class MessageDriver(AbstractMessage, ABC):
29
29
  rcver=self.get_msg_device(MsgCmdType.EMBED_DRIVER, MsgDevice.DEV_MAINCTL),
30
30
  msgattr=MsgAttr.REQ,
31
31
  timestamp=round(time.time() * 1000),
32
- seqs=1,
32
+ seqs=self.seqs.increment_and_get() & 255,
33
33
  version=1,
34
- subtype=1,
34
+ subtype=self.user_account,
35
35
  driver=driver,
36
36
  ).SerializeToString()
37
37
 
@@ -12,9 +12,9 @@ class MessageMedia(AbstractMessage, ABC):
12
12
  sender=MsgDevice.DEV_MOBILEAPP,
13
13
  rcver=self.get_msg_device(MsgCmdType.MUL, MsgDevice.SOC_MODULE_MULTIMEDIA),
14
14
  msgattr=MsgAttr.REQ,
15
- seqs=1,
15
+ seqs=self.seqs.increment_and_get() & 255,
16
16
  version=1,
17
- subtype=1,
17
+ subtype=self.user_account,
18
18
  mul=mul,
19
19
  )
20
20
 
@@ -39,9 +39,9 @@ class MessageNavigation(AbstractMessage, ABC):
39
39
  sender=MsgDevice.DEV_MOBILEAPP,
40
40
  rcver=self.get_msg_device(MsgCmdType.NAV, MsgDevice.DEV_MAINCTL),
41
41
  msgattr=MsgAttr.REQ,
42
- seqs=1,
42
+ seqs=self.seqs.increment_and_get() & 255,
43
43
  version=1,
44
- subtype=1,
44
+ subtype=self.user_account,
45
45
  nav=build,
46
46
  timestamp=round(time.time() * 1000),
47
47
  )
@@ -31,16 +31,15 @@ from pymammotion.proto import (
31
31
  class MessageNetwork(AbstractMessage, ABC):
32
32
  messageNavigation: MessageNavigation = MessageNavigation()
33
33
 
34
- @staticmethod
35
- def send_order_msg_net(build: DevNet) -> bytes:
34
+ def send_order_msg_net(self, build: DevNet) -> bytes:
36
35
  luba_msg = LubaMsg(
37
36
  msgtype=MsgCmdType.ESP,
38
37
  sender=MsgDevice.DEV_MOBILEAPP,
39
38
  rcver=MsgDevice.DEV_COMM_ESP,
40
39
  msgattr=MsgAttr.REQ,
41
- seqs=1,
40
+ seqs=self.seqs.increment_and_get() & 255,
42
41
  version=1,
43
- subtype=1,
42
+ subtype=self.user_account,
44
43
  net=build,
45
44
  timestamp=round(time.time() * 1000),
46
45
  )
@@ -6,15 +6,17 @@ from pymammotion.proto import GetInfoReq, InfoType, LubaMsg, MctlOta, MsgAttr, M
6
6
 
7
7
 
8
8
  class MessageOta(AbstractMessage, ABC):
9
+ """Message OTA class."""
10
+
9
11
  def send_order_msg_ota(self, ota):
10
12
  luba_msg = LubaMsg(
11
13
  msgtype=MsgCmdType.EMBED_OTA,
12
14
  sender=MsgDevice.DEV_MOBILEAPP,
13
15
  rcver=self.get_msg_device(MsgCmdType.EMBED_OTA, MsgDevice.DEV_MAINCTL),
14
- msgattr=MsgAttr.MSG_ATTR_REQ,
15
- seqs=1,
16
+ msgattr=MsgAttr.REQ,
17
+ seqs=self.seqs.increment_and_get() & 255,
16
18
  version=1,
17
- subtype=1,
19
+ subtype=self.user_account,
18
20
  ota=ota,
19
21
  )
20
22
 
@@ -36,25 +36,24 @@ class MessageSystem(AbstractMessage, ABC):
36
36
  sender=MsgDevice.DEV_MOBILEAPP,
37
37
  rcver=self.get_msg_device(MsgCmdType.EMBED_SYS, MsgDevice.DEV_MAINCTL),
38
38
  sys=sys,
39
- seqs=1,
39
+ seqs=self.seqs.increment_and_get() & 255,
40
40
  version=1,
41
- subtype=1,
41
+ subtype=self.user_account,
42
42
  timestamp=round(time.time() * 1000),
43
43
  )
44
44
 
45
45
  return luba_msg.SerializeToString()
46
46
 
47
- @staticmethod
48
- def send_order_msg_sys_legacy(sys) -> bytes:
47
+ def send_order_msg_sys_legacy(self, sys) -> bytes:
49
48
  luba_msg = LubaMsg(
50
49
  msgtype=MsgCmdType.EMBED_SYS,
51
50
  msgattr=MsgAttr.REQ,
52
51
  sender=MsgDevice.DEV_MOBILEAPP,
53
52
  rcver=MsgDevice.DEV_MAINCTL,
54
53
  sys=sys,
55
- seqs=1,
54
+ seqs=self.seqs.increment_and_get() & 255,
56
55
  version=1,
57
- subtype=1,
56
+ subtype=self.user_account,
58
57
  timestamp=round(time.time() * 1000),
59
58
  )
60
59
 
@@ -315,9 +314,9 @@ class MessageSystem(AbstractMessage, ABC):
315
314
  sender=MsgDevice.DEV_MOBILEAPP,
316
315
  rcver=MsgDevice.DEV_MAINCTL,
317
316
  msgattr=MsgAttr.REQ,
318
- seqs=1,
317
+ seqs=self.seqs.increment_and_get() & 255,
319
318
  version=1,
320
- subtype=1,
319
+ subtype=self.user_account,
321
320
  sys=mctl_sys,
322
321
  timestamp=round(time.time() * 1000),
323
322
  )
@@ -351,9 +350,9 @@ class MessageSystem(AbstractMessage, ABC):
351
350
  sender=MsgDevice.DEV_MOBILEAPP,
352
351
  rcver=MsgDevice.DEV_MAINCTL,
353
352
  msgattr=MsgAttr.REQ,
354
- seqs=1,
353
+ seqs=self.seqs.increment_and_get() & 255,
355
354
  version=1,
356
- subtype=1,
355
+ subtype=self.user_account,
357
356
  sys=mctl_sys,
358
357
  timestamp=round(time.time() * 1000),
359
358
  )
@@ -15,9 +15,9 @@ class MessageVideo(AbstractMessage, ABC):
15
15
  sender=MsgDevice.DEV_MOBILEAPP,
16
16
  rcver=self.get_msg_device(MsgCmdType.MUL, MsgDevice.SOC_MODULE_MULTIMEDIA),
17
17
  mul=mul,
18
- seqs=1,
18
+ seqs=self.seqs.increment_and_get() & 255,
19
19
  version=1,
20
- subtype=1,
20
+ subtype=self.user_account,
21
21
  timestamp=round(time.time() * 1000),
22
22
  )
23
23
 
@@ -45,18 +45,18 @@ class MammotionBaseDevice:
45
45
 
46
46
  async def datahash_response(self, hash_ack: NavGetHashListAck) -> None:
47
47
  """Handle datahash responses."""
48
- current_frame = hash_ack.current_frame
49
48
 
50
- missing_frames = self.mower.map.missing_hash_frame()
49
+ missing_frames = self.mower.map.missing_root_hash_frame(hash_ack)
51
50
  if len(missing_frames) == 0:
52
- if len(self.mower.map.missing_hashlist) > 0:
53
- data_hash = self.mower.map.missing_hashlist.pop()
51
+ if len(self.mower.map.missing_hashlist(hash_ack.sub_cmd)) > 0:
52
+ data_hash = self.mower.map.missing_hashlist(hash_ack.sub_cmd).pop()
54
53
  return await self.queue_command("synchronize_hash_data", hash_num=data_hash)
55
54
  return
56
55
 
57
- if current_frame != missing_frames[0] - 1:
58
- current_frame = missing_frames[0] - 1
59
- await self.queue_command("get_hash_response", total_frame=hash_ack.total_frame, current_frame=current_frame)
56
+ for frame in missing_frames:
57
+ await self.queue_command(
58
+ "get_hash_response", sub_cmd=hash_ack.sub_cmd, total_frame=hash_ack.total_frame, current_frame=frame - 1
59
+ )
60
60
 
61
61
  async def commdata_response(self, common_data: NavGetCommDataAck | SvgMessageAckT) -> None:
62
62
  """Handle common data responses."""
@@ -67,7 +67,11 @@ class MammotionBaseDevice:
67
67
  if len(missing_frames) == 0:
68
68
  # get next in hash ack list
69
69
 
70
- data_hash = self.mower.map.missing_hashlist.pop() if len(self.mower.map.missing_hashlist) > 0 else None
70
+ data_hash = (
71
+ self.mower.map.missing_hashlist(common_data.sub_cmd).pop()
72
+ if len(self.mower.map.missing_hashlist(common_data.sub_cmd)) > 0
73
+ else None
74
+ )
71
75
  if data_hash is None:
72
76
  return
73
77
 
@@ -80,6 +84,7 @@ class MammotionBaseDevice:
80
84
  region_data.hash = common_data.data_hash if isinstance(common_data, SvgMessageAckT) else common_data.hash
81
85
  region_data.action = common_data.action if isinstance(common_data, NavGetCommDataAck) else None
82
86
  region_data.type = common_data.type
87
+ region_data.sub_cmd = common_data.sub_cmd
83
88
  region_data.total_frame = total_frame
84
89
  region_data.current_frame = current_frame
85
90
  await self.queue_command("get_regional_data", regional_data=region_data)
@@ -210,14 +215,14 @@ class MammotionBaseDevice:
210
215
  await self.queue_command("read_plan", sub_cmd=2, plan_index=0)
211
216
 
212
217
  await self.queue_command("get_all_boundary_hash_list", sub_cmd=0)
213
- await self.queue_command("get_hash_response", total_frame=1, current_frame=1)
214
- if len(self.mower.map.missing_hashlist) > 0:
215
- data_hash = self.mower.map.missing_hashlist.pop()
218
+ if len(self.mower.map.missing_hashlist()) > 0:
219
+ data_hash = self.mower.map.missing_hashlist().pop()
216
220
  await self.queue_command("synchronize_hash_data", hash_num=data_hash)
217
221
 
218
222
  # sub_cmd 3 is job hashes??
219
223
  # sub_cmd 4 is dump location (yuka)
220
224
  # jobs list
225
+ #
221
226
  # hash_list_result = await self._send_command_with_args("get_all_boundary_hash_list", sub_cmd=3)
222
227
 
223
228
  async def async_read_settings(self) -> None:
@@ -82,7 +82,7 @@ class MammotionBaseBLEDevice(MammotionBaseDevice):
82
82
  self._write_char: BleakGATTCharacteristic | int | str | UUID = 0
83
83
  self._disconnect_timer: asyncio.TimerHandle | None = None
84
84
  self._message: BleMessage | None = None
85
- self._commands: MammotionCommand = MammotionCommand(device.name)
85
+ self._commands: MammotionCommand = MammotionCommand(device.name, 1)
86
86
  self.command_queue = asyncio.Queue()
87
87
  self._expected_disconnect = False
88
88
  self._connect_lock = asyncio.Lock()
@@ -164,7 +164,10 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
164
164
  self.iot_id = cloud_device.iotId
165
165
  self.device = cloud_device
166
166
  self._command_futures = {}
167
- self._commands: MammotionCommand = MammotionCommand(cloud_device.deviceName)
167
+ self._commands: MammotionCommand = MammotionCommand(
168
+ cloud_device.deviceName,
169
+ int(mqtt.cloud_client.mammotion_http.response.data.get("userInformation").get("userAccount")),
170
+ )
168
171
  self.currentID = ""
169
172
  self._mqtt.mqtt_message_event.add_subscribers(self._parse_message_for_device)
170
173
  self._mqtt.mqtt_properties_event.add_subscribers(self._parse_message_properties_for_device)
@@ -329,9 +332,9 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
329
332
  fut: MammotionFuture = self.dequeue_by_iot_id(self._mqtt.waiting_queue, self.iot_id)
330
333
  if fut is None:
331
334
  return
332
- while fut.fut.cancelled() and len(self._mqtt.waiting_queue) > 0:
335
+ while fut is None or fut.fut.cancelled() and len(self._mqtt.waiting_queue) > 0:
333
336
  fut = self.dequeue_by_iot_id(self._mqtt.waiting_queue, self.iot_id)
334
- if not fut.fut.cancelled():
337
+ if fut is not None and not fut.fut.cancelled():
335
338
  fut.resolve(cast(bytes, binary_data))
336
339
 
337
340
  @property
@@ -892,7 +892,7 @@ class NavGetCommData(betterproto.Message):
892
892
  sub_cmd: int = betterproto.int32_field(2)
893
893
  action: int = betterproto.int32_field(3)
894
894
  type: int = betterproto.int32_field(4)
895
- hash: int = betterproto.fixed64_field(5)
895
+ hash: int = betterproto.int64_field(5)
896
896
  paternal_hash_a: int = betterproto.int64_field(6)
897
897
  paternal_hash_b: int = betterproto.int64_field(7)
898
898
  total_frame: int = betterproto.int32_field(8)
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [project]
6
6
  name = "pymammotion"
7
- version = "0.4.11-beta1"
7
+ version = "0.4.12"
8
8
  license = "GPL-3.0"
9
9
  description = ""
10
10
  readme = "README.md"
@@ -21,7 +21,7 @@ packages = [{include = "pymammotion"}]
21
21
 
22
22
 
23
23
  [tool.poetry.dependencies]
24
- python = ">=3.10,<3.13"
24
+ python = ">=3.11,<3.13"
25
25
  bleak = ">=0.21.0"
26
26
  protobuf = ">=4.23.1"
27
27
  py-jsonic = "^0.0.2"
@@ -63,9 +63,10 @@ ruff = "^0.9.3"
63
63
  pre-commit = "^3.8.0"
64
64
  mypy = "^1.11.2"
65
65
  autotyping = "^24.3.0"
66
+ ipython = "^9.0.2"
66
67
 
67
68
  [tool.bumpver]
68
- current_version = "0.4.11-beta1"
69
+ current_version = "0.4.12"
69
70
  version_pattern = "MAJOR.MINOR.PATCH"
70
71
  commit_message = "Bump version {old_version} -> {new_version}"
71
72
  commit = true
File without changes
File without changes