pymammotion 0.2.62__py3-none-any.whl → 0.5.51__py3-none-any.whl

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.

Potentially problematic release.


This version of pymammotion might be problematic. Click here for more details.

Files changed (135) hide show
  1. pymammotion/__init__.py +9 -6
  2. pymammotion/aliyun/client.py +235 -0
  3. pymammotion/aliyun/cloud_gateway.py +320 -69
  4. pymammotion/aliyun/model/aep_response.py +1 -2
  5. pymammotion/aliyun/model/dev_by_account_response.py +170 -23
  6. pymammotion/aliyun/model/login_by_oauth_response.py +2 -3
  7. pymammotion/aliyun/model/regions_response.py +3 -3
  8. pymammotion/aliyun/model/session_by_authcode_response.py +2 -2
  9. pymammotion/aliyun/model/thing_response.py +12 -0
  10. pymammotion/aliyun/regions.py +62 -0
  11. pymammotion/aliyun/tea/core.py +297 -0
  12. pymammotion/bluetooth/ble.py +11 -15
  13. pymammotion/bluetooth/ble_message.py +389 -106
  14. pymammotion/bluetooth/model/atomic_integer.py +54 -0
  15. pymammotion/const.py +3 -0
  16. pymammotion/data/model/__init__.py +1 -2
  17. pymammotion/data/model/device.py +92 -240
  18. pymammotion/data/model/device_config.py +10 -24
  19. pymammotion/data/model/device_info.py +35 -0
  20. pymammotion/data/model/device_limits.py +49 -0
  21. pymammotion/data/model/enums.py +12 -2
  22. pymammotion/data/model/errors.py +12 -0
  23. pymammotion/data/model/events.py +14 -0
  24. pymammotion/data/model/generate_geojson.py +521 -0
  25. pymammotion/data/model/generate_route_information.py +3 -4
  26. pymammotion/data/model/hash_list.py +384 -48
  27. pymammotion/data/model/location.py +4 -4
  28. pymammotion/data/model/mowing_modes.py +24 -1
  29. pymammotion/data/model/raw_data.py +215 -0
  30. pymammotion/data/model/region_data.py +10 -11
  31. pymammotion/data/model/report_info.py +62 -6
  32. pymammotion/data/model/work.py +27 -0
  33. pymammotion/data/mower_state_manager.py +316 -0
  34. pymammotion/data/mqtt/event.py +73 -28
  35. pymammotion/data/mqtt/mammotion_properties.py +257 -0
  36. pymammotion/data/mqtt/properties.py +93 -78
  37. pymammotion/data/mqtt/status.py +18 -17
  38. pymammotion/event/event.py +32 -8
  39. pymammotion/homeassistant/__init__.py +3 -0
  40. pymammotion/homeassistant/mower_api.py +484 -0
  41. pymammotion/homeassistant/rtk_api.py +54 -0
  42. pymammotion/http/__init__.py +0 -0
  43. pymammotion/http/encryption.py +220 -0
  44. pymammotion/http/http.py +652 -44
  45. pymammotion/http/model/__init__.py +0 -0
  46. pymammotion/{aliyun/model/stream_subscription_response.py → http/model/camera_stream.py} +14 -2
  47. pymammotion/http/model/http.py +160 -9
  48. pymammotion/http/model/response_factory.py +61 -0
  49. pymammotion/http/model/rtk.py +16 -0
  50. pymammotion/mammotion/commands/abstract_message.py +7 -5
  51. pymammotion/mammotion/commands/mammotion_command.py +32 -3
  52. pymammotion/mammotion/commands/messages/basestation.py +43 -0
  53. pymammotion/mammotion/commands/messages/driver.py +61 -29
  54. pymammotion/mammotion/commands/messages/media.py +68 -15
  55. pymammotion/mammotion/commands/messages/navigation.py +61 -25
  56. pymammotion/mammotion/commands/messages/network.py +93 -100
  57. pymammotion/mammotion/commands/messages/ota.py +18 -18
  58. pymammotion/mammotion/commands/messages/system.py +97 -72
  59. pymammotion/mammotion/commands/messages/video.py +17 -12
  60. pymammotion/mammotion/devices/__init__.py +27 -3
  61. pymammotion/mammotion/devices/base.py +50 -127
  62. pymammotion/mammotion/devices/mammotion.py +447 -212
  63. pymammotion/mammotion/devices/mammotion_bluetooth.py +105 -60
  64. pymammotion/mammotion/devices/mammotion_cloud.py +157 -105
  65. pymammotion/mammotion/devices/mammotion_mower_ble.py +49 -0
  66. pymammotion/mammotion/devices/mammotion_mower_cloud.py +39 -0
  67. pymammotion/mammotion/devices/managers/managers.py +81 -0
  68. pymammotion/mammotion/devices/mower_device.py +124 -0
  69. pymammotion/mammotion/devices/mower_manager.py +107 -0
  70. pymammotion/mammotion/devices/rtk_ble.py +89 -0
  71. pymammotion/mammotion/devices/rtk_cloud.py +113 -0
  72. pymammotion/mammotion/devices/rtk_device.py +50 -0
  73. pymammotion/mammotion/devices/rtk_manager.py +122 -0
  74. pymammotion/mqtt/__init__.py +2 -1
  75. pymammotion/mqtt/aliyun_mqtt.py +232 -0
  76. pymammotion/mqtt/linkkit/__init__.py +5 -0
  77. pymammotion/mqtt/linkkit/h2client.py +585 -0
  78. pymammotion/mqtt/linkkit/linkkit.py +3023 -0
  79. pymammotion/mqtt/mammotion_mqtt.py +176 -169
  80. pymammotion/mqtt/mqtt_models.py +66 -0
  81. pymammotion/proto/__init__.py +4839 -4
  82. pymammotion/proto/basestation.proto +8 -0
  83. pymammotion/proto/basestation_pb2.py +11 -9
  84. pymammotion/proto/basestation_pb2.pyi +16 -2
  85. pymammotion/proto/dev_net.proto +79 -55
  86. pymammotion/proto/dev_net_pb2.py +60 -56
  87. pymammotion/proto/dev_net_pb2.pyi +49 -6
  88. pymammotion/proto/luba_msg.proto +2 -1
  89. pymammotion/proto/luba_msg_pb2.py +6 -6
  90. pymammotion/proto/luba_msg_pb2.pyi +1 -0
  91. pymammotion/proto/luba_mul.proto +62 -1
  92. pymammotion/proto/luba_mul_pb2.py +38 -22
  93. pymammotion/proto/luba_mul_pb2.pyi +94 -7
  94. pymammotion/proto/mctrl_driver.proto +44 -4
  95. pymammotion/proto/mctrl_driver_pb2.py +26 -14
  96. pymammotion/proto/mctrl_driver_pb2.pyi +66 -11
  97. pymammotion/proto/mctrl_nav.proto +97 -51
  98. pymammotion/proto/mctrl_nav_pb2.py +75 -67
  99. pymammotion/proto/mctrl_nav_pb2.pyi +142 -56
  100. pymammotion/proto/mctrl_ota.proto +40 -2
  101. pymammotion/proto/mctrl_ota_pb2.py +23 -13
  102. pymammotion/proto/mctrl_ota_pb2.pyi +67 -4
  103. pymammotion/proto/mctrl_pept.proto +8 -3
  104. pymammotion/proto/mctrl_pept_pb2.py +8 -6
  105. pymammotion/proto/mctrl_pept_pb2.pyi +14 -6
  106. pymammotion/proto/mctrl_sys.proto +325 -86
  107. pymammotion/proto/mctrl_sys_pb2.py +162 -98
  108. pymammotion/proto/mctrl_sys_pb2.pyi +451 -25
  109. pymammotion/proto/message_pool.py +3 -0
  110. pymammotion/proto/py.typed +0 -0
  111. pymammotion/utility/constant/device_constant.py +65 -21
  112. pymammotion/utility/datatype_converter.py +13 -12
  113. pymammotion/utility/device_config.py +755 -0
  114. pymammotion/utility/device_type.py +218 -21
  115. pymammotion/utility/map.py +238 -51
  116. pymammotion/utility/mur_mur_hash.py +159 -0
  117. {pymammotion-0.2.62.dist-info → pymammotion-0.5.51.dist-info}/METADATA +27 -31
  118. pymammotion-0.5.51.dist-info/RECORD +152 -0
  119. {pymammotion-0.2.62.dist-info → pymammotion-0.5.51.dist-info}/WHEEL +1 -1
  120. pymammotion/aliyun/cloud_service.py +0 -65
  121. pymammotion/data/model/plan.py +0 -58
  122. pymammotion/data/state_manager.py +0 -130
  123. pymammotion/proto/basestation.py +0 -59
  124. pymammotion/proto/common.py +0 -12
  125. pymammotion/proto/dev_net.py +0 -381
  126. pymammotion/proto/luba_msg.py +0 -81
  127. pymammotion/proto/luba_mul.py +0 -76
  128. pymammotion/proto/mctrl_driver.py +0 -100
  129. pymammotion/proto/mctrl_nav.py +0 -660
  130. pymammotion/proto/mctrl_ota.py +0 -48
  131. pymammotion/proto/mctrl_pept.py +0 -41
  132. pymammotion/proto/mctrl_sys.py +0 -574
  133. pymammotion-0.2.62.dist-info/RECORD +0 -125
  134. /pymammotion/{http/_init_.py → bluetooth/model/__init__.py} +0 -0
  135. {pymammotion-0.2.62.dist-info → pymammotion-0.5.51.dist-info/licenses}/LICENSE +0 -0
@@ -1,16 +1,19 @@
1
1
  # === sendOrderMsg_Sys ===
2
- import datetime
3
2
  from abc import ABC
3
+ import datetime
4
+ import time
4
5
 
5
6
  from pymammotion import logger
6
7
  from pymammotion.mammotion.commands.abstract_message import AbstractMessage
7
- from pymammotion.mammotion.commands.messages.navigation import MessageNavigation
8
- from pymammotion.proto.luba_msg import LubaMsg, MsgAttr, MsgCmdType, MsgDevice
9
- from pymammotion.proto.mctrl_sys import (
8
+ from pymammotion.proto import (
10
9
  DeviceProductTypeInfoT,
11
10
  LoraCfgReq,
11
+ LubaMsg,
12
12
  MctlSys,
13
13
  MCtrlSimulationCmdData,
14
+ MsgAttr,
15
+ MsgCmdType,
16
+ MsgDevice,
14
17
  ReportInfoCfg,
15
18
  RptAct,
16
19
  RptInfoType,
@@ -19,38 +22,45 @@ from pymammotion.proto.mctrl_sys import (
19
22
  SysSetDateTime,
20
23
  TimeCtrlLight,
21
24
  )
22
- from pymammotion.utility.device_type import DeviceType
23
25
 
24
26
 
25
27
  class MessageSystem(AbstractMessage, ABC):
26
- messageNavigation: MessageNavigation = MessageNavigation()
27
-
28
- def send_order_msg_sys(self, sys):
28
+ def send_order_msg_sys(self, sys) -> bytes:
29
29
  luba_msg = LubaMsg(
30
- msgtype=MsgCmdType.MSG_CMD_TYPE_EMBED_SYS,
30
+ msgtype=MsgCmdType.EMBED_SYS,
31
+ msgattr=MsgAttr.REQ,
31
32
  sender=MsgDevice.DEV_MOBILEAPP,
32
- rcver=self.get_msg_device(MsgCmdType.MSG_CMD_TYPE_EMBED_SYS, MsgDevice.DEV_MAINCTL),
33
+ rcver=self.get_msg_device(MsgCmdType.EMBED_SYS, MsgDevice.DEV_MAINCTL),
33
34
  sys=sys,
35
+ seqs=self.seqs.increment_and_get() & 255,
36
+ version=1,
37
+ subtype=self.user_account,
38
+ timestamp=round(time.time() * 1000),
34
39
  )
35
40
 
36
41
  return luba_msg.SerializeToString()
37
42
 
38
- def send_order_msg_sys_legacy(self, sys):
43
+ def send_order_msg_sys_legacy(self, sys) -> bytes:
39
44
  luba_msg = LubaMsg(
40
- msgtype=MsgCmdType.MSG_CMD_TYPE_EMBED_SYS,
45
+ msgtype=MsgCmdType.EMBED_SYS,
46
+ msgattr=MsgAttr.REQ,
41
47
  sender=MsgDevice.DEV_MOBILEAPP,
42
48
  rcver=MsgDevice.DEV_MAINCTL,
43
49
  sys=sys,
50
+ seqs=self.seqs.increment_and_get() & 255,
51
+ version=1,
52
+ subtype=self.user_account,
53
+ timestamp=round(time.time() * 1000),
44
54
  )
45
55
 
46
56
  return luba_msg.SerializeToString()
47
57
 
48
- def reset_system(self):
58
+ def reset_system(self) -> bytes:
49
59
  build = MctlSys(todev_reset_system=1)
50
60
  logger.debug("Send command - send factory reset")
51
61
  return self.send_order_msg_sys(build)
52
62
 
53
- def set_blade_control(self, on_off: int):
63
+ def set_blade_control(self, on_off: int) -> bytes:
54
64
  mctlsys = MctlSys()
55
65
  sys_knife_control = SysKnifeControl()
56
66
  sys_knife_control.knife_status = on_off
@@ -58,10 +68,10 @@ class MessageSystem(AbstractMessage, ABC):
58
68
 
59
69
  return self.send_order_msg_sys(mctlsys)
60
70
 
61
- def get_device_product_model(self):
62
- return self.send_order_msg_sys(MctlSys(device_product_type_info=DeviceProductTypeInfoT()))
71
+ def get_device_product_model(self) -> bytes:
72
+ return self.send_order_msg_sys(MctlSys(device_product_type_info=DeviceProductTypeInfoT(result=1)))
63
73
 
64
- def read_and_set_sidelight(self, is_sidelight: bool, operate: int):
74
+ def read_and_set_sidelight(self, is_sidelight: bool, operate: int) -> bytes:
65
75
  """Read state of sidelight as well as set it."""
66
76
  if is_sidelight:
67
77
  build = TimeCtrlLight(
@@ -90,7 +100,7 @@ class MessageSystem(AbstractMessage, ABC):
90
100
  is_sidelight}, operate:{operate}, timeCtrlLight:{build}")
91
101
  return self.send_order_msg_sys(build2)
92
102
 
93
- def test_tool_order_to_sys(self, sub_cmd: int, param_id: int, param_value: list[int]):
103
+ def test_tool_order_to_sys(self, sub_cmd: int, param_id: int, param_value: list[int]) -> bytes:
94
104
  build = MCtrlSimulationCmdData(sub_cmd=sub_cmd, param_id=param_id, param_value=param_value)
95
105
  logger.debug(f"Send tool test command: subCmd={sub_cmd}, param_id:{
96
106
  param_id}, param_value={param_value}")
@@ -99,20 +109,14 @@ class MessageSystem(AbstractMessage, ABC):
99
109
  param_id}, param_value={param_value}")
100
110
  return self.send_order_msg_sys(build2)
101
111
 
102
- def read_and_set_rtk_paring_code(self, op: int, cgf: str | None = None):
112
+ def read_and_set_rtk_paring_code(self, op: int, cgf: str | None = None) -> bytes:
103
113
  logger.debug(f"Send read and write base station configuration quality op:{
104
114
  op}, cgf:{cgf}")
105
115
  return self.send_order_msg_sys(MctlSys(todev_lora_cfg_req=LoraCfgReq(op=op, cfg=cgf)))
106
116
 
107
- def allpowerfull_rw(self, id: int, context: int, rw: int):
108
- if (id == 6 or id == 3 or id == 7) and DeviceType.is_luba_2(self.get_device_name()):
109
- self.messageNavigation.allpowerfull_rw_adapter_x3(id, context, rw)
110
- return
111
- build = MctlSys(bidire_comm_cmd=SysCommCmd(id=id, context=context, rw=rw))
112
- logger.debug(f"Send command - 9 general read and write command id={id}, context={context}, rw={rw}")
113
- if id == 5:
114
- # This logic doesnt make snese, but its what they had so..
115
- return self.send_order_msg_sys(build)
117
+ def allpowerfull_rw(self, rw_id: int, context: int, rw: int) -> bytes:
118
+ build = MctlSys(bidire_comm_cmd=SysCommCmd(id=rw_id, context=context, rw=rw))
119
+ logger.debug(f"Send command - 9 general read and write command id={rw_id}, context={context}, rw={rw}")
116
120
  return self.send_order_msg_sys(build)
117
121
 
118
122
  # Commented out as not needed and too many refs to try fix up
@@ -175,7 +179,8 @@ class MessageSystem(AbstractMessage, ABC):
175
179
  # test_id}, testDuration={test_duration}", "Factory tool logger.debug222", True)
176
180
  # return self.send_order_msg_sys(build2)
177
181
 
178
- def send_sys_set_date_time(self):
182
+ def send_sys_set_date_time(self) -> bytes:
183
+ # TODO get HA timezone
179
184
  calendar = datetime.datetime.now()
180
185
  i = calendar.year
181
186
  i2 = calendar.month
@@ -209,9 +214,12 @@ class MessageSystem(AbstractMessage, ABC):
209
214
  )
210
215
  return self.send_order_msg_sys(build)
211
216
 
212
- def get_device_version_info(self):
217
+ def get_device_version_info(self) -> bytes:
213
218
  return self.send_order_msg_sys(MctlSys(todev_get_dev_fw_info=1))
214
219
 
220
+ def read_and_set_rtk_pairing_code(self, op: int, cfg: str) -> bytes:
221
+ return self.send_order_msg_sys(MctlSys(todev_lora_cfg_req=LoraCfgReq(op=op, cfg=cfg)))
222
+
215
223
  # === sendOrderMsg_Sys2 ===
216
224
 
217
225
  def request_iot_sys(
@@ -234,12 +242,26 @@ class MessageSystem(AbstractMessage, ABC):
234
242
  )
235
243
  )
236
244
  logger.debug(f"Send command==== IOT slim data Act {
237
- build.todev_report_cfg.act} {build}")
245
+ build.todev_report_cfg.act}")
238
246
  return self.send_order_msg_sys_legacy(build)
239
247
 
248
+ def get_maintenance(self) -> bytes:
249
+ return self.request_iot_sys(
250
+ rpt_act=RptAct.RPT_START,
251
+ rpt_info_type=[
252
+ RptInfoType.RIT_MAINTAIN,
253
+ RptInfoType.RIT_BASESTATION_INFO,
254
+ RptInfoType.RIT_FW_INFO,
255
+ ],
256
+ timeout=1000,
257
+ period=1000,
258
+ no_change_period=2000,
259
+ count=3,
260
+ )
261
+
240
262
  def get_report_cfg_stop(self, timeout: int = 10000, period: int = 1000, no_change_period: int = 1000):
241
263
  # TODO use send_order_msg_sys_legacy
242
- mctlsys = MctlSys(
264
+ mctl_sys = MctlSys(
243
265
  todev_report_cfg=ReportInfoCfg(
244
266
  act=RptAct.RPT_STOP,
245
267
  timeout=timeout,
@@ -249,30 +271,32 @@ class MessageSystem(AbstractMessage, ABC):
249
271
  )
250
272
  )
251
273
 
252
- mctlsys.todev_report_cfg.sub.append(RptInfoType.RIT_CONNECT)
253
- mctlsys.todev_report_cfg.sub.append(RptInfoType.RIT_RTK)
254
- mctlsys.todev_report_cfg.sub.append(RptInfoType.RIT_DEV_LOCAL)
255
- mctlsys.todev_report_cfg.sub.append(RptInfoType.RIT_WORK)
256
- mctlsys.todev_report_cfg.sub.append(RptInfoType.RIT_DEV_STA)
257
- mctlsys.todev_report_cfg.sub.append(RptInfoType.RIT_MAINTAIN)
258
- mctlsys.todev_report_cfg.sub.append(RptInfoType.RIT_VISION_POINT)
259
- mctlsys.todev_report_cfg.sub.append(RptInfoType.RIT_VIO)
260
- mctlsys.todev_report_cfg.sub.append(RptInfoType.RIT_VISION_STATISTIC)
274
+ mctl_sys.todev_report_cfg.sub.append(RptInfoType.RIT_CONNECT)
275
+ mctl_sys.todev_report_cfg.sub.append(RptInfoType.RIT_RTK)
276
+ mctl_sys.todev_report_cfg.sub.append(RptInfoType.RIT_DEV_LOCAL)
277
+ mctl_sys.todev_report_cfg.sub.append(RptInfoType.RIT_WORK)
278
+ mctl_sys.todev_report_cfg.sub.append(RptInfoType.RIT_DEV_STA)
279
+ mctl_sys.todev_report_cfg.sub.append(RptInfoType.RIT_VISION_POINT)
280
+ mctl_sys.todev_report_cfg.sub.append(RptInfoType.RIT_VIO)
281
+ mctl_sys.todev_report_cfg.sub.append(RptInfoType.RIT_VISION_STATISTIC)
261
282
 
262
- lubaMsg = LubaMsg()
263
- lubaMsg.msgtype = MsgCmdType.MSG_CMD_TYPE_EMBED_SYS
264
- lubaMsg.sender = MsgDevice.DEV_MOBILEAPP
265
- lubaMsg.rcver = MsgDevice.DEV_MAINCTL
266
- lubaMsg.msgattr = MsgAttr.MSG_ATTR_REQ
267
- lubaMsg.seqs = 1
268
- lubaMsg.version = 1
269
- lubaMsg.subtype = 1
270
- lubaMsg.sys = mctlsys
271
- return lubaMsg.SerializeToString()
283
+ luba_msg = LubaMsg(
284
+ msgtype=MsgCmdType.EMBED_SYS,
285
+ sender=MsgDevice.DEV_MOBILEAPP,
286
+ rcver=MsgDevice.DEV_MAINCTL,
287
+ msgattr=MsgAttr.REQ,
288
+ seqs=self.seqs.increment_and_get() & 255,
289
+ version=1,
290
+ subtype=self.user_account,
291
+ sys=mctl_sys,
292
+ timestamp=round(time.time() * 1000),
293
+ )
294
+
295
+ return luba_msg.SerializeToString()
272
296
 
273
297
  def get_report_cfg(self, timeout: int = 10000, period: int = 1000, no_change_period: int = 2000):
274
298
  # TODO use send_order_msg_sys_legacy
275
- mctlsys = MctlSys(
299
+ mctl_sys = MctlSys(
276
300
  todev_report_cfg=ReportInfoCfg(
277
301
  act=RptAct.RPT_START,
278
302
  timeout=timeout,
@@ -282,24 +306,25 @@ class MessageSystem(AbstractMessage, ABC):
282
306
  )
283
307
  )
284
308
 
285
- mctlsys.todev_report_cfg.sub.append(RptInfoType.RIT_CONNECT)
286
- mctlsys.todev_report_cfg.sub.append(RptInfoType.RIT_RTK)
287
- mctlsys.todev_report_cfg.sub.append(RptInfoType.RIT_DEV_LOCAL)
288
- mctlsys.todev_report_cfg.sub.append(RptInfoType.RIT_WORK)
289
- mctlsys.todev_report_cfg.sub.append(RptInfoType.RIT_DEV_STA)
290
- mctlsys.todev_report_cfg.sub.append(RptInfoType.RIT_MAINTAIN)
291
- mctlsys.todev_report_cfg.sub.append(RptInfoType.RIT_VISION_POINT)
292
- mctlsys.todev_report_cfg.sub.append(RptInfoType.RIT_VIO)
293
- mctlsys.todev_report_cfg.sub.append(RptInfoType.RIT_VISION_STATISTIC)
294
- mctlsys.todev_report_cfg.sub.append(RptInfoType.RIT_BASESTATION_INFO)
309
+ mctl_sys.todev_report_cfg.sub.append(RptInfoType.RIT_CONNECT)
310
+ mctl_sys.todev_report_cfg.sub.append(RptInfoType.RIT_RTK)
311
+ mctl_sys.todev_report_cfg.sub.append(RptInfoType.RIT_DEV_LOCAL)
312
+ mctl_sys.todev_report_cfg.sub.append(RptInfoType.RIT_WORK)
313
+ mctl_sys.todev_report_cfg.sub.append(RptInfoType.RIT_DEV_STA)
314
+ mctl_sys.todev_report_cfg.sub.append(RptInfoType.RIT_VISION_POINT)
315
+ mctl_sys.todev_report_cfg.sub.append(RptInfoType.RIT_VIO)
316
+ mctl_sys.todev_report_cfg.sub.append(RptInfoType.RIT_VISION_STATISTIC)
317
+ mctl_sys.todev_report_cfg.sub.append(RptInfoType.RIT_BASESTATION_INFO)
295
318
 
296
- lubaMsg = LubaMsg()
297
- lubaMsg.msgtype = MsgCmdType.MSG_CMD_TYPE_EMBED_SYS
298
- lubaMsg.sender = MsgDevice.DEV_MOBILEAPP
299
- lubaMsg.rcver = MsgDevice.DEV_MAINCTL
300
- lubaMsg.msgattr = MsgAttr.MSG_ATTR_REQ
301
- lubaMsg.seqs = 1
302
- lubaMsg.version = 1
303
- lubaMsg.subtype = 1
304
- lubaMsg.sys = mctlsys
305
- return lubaMsg.SerializeToString()
319
+ luba_msg = LubaMsg(
320
+ msgtype=MsgCmdType.EMBED_SYS,
321
+ sender=MsgDevice.DEV_MOBILEAPP,
322
+ rcver=MsgDevice.DEV_MAINCTL,
323
+ msgattr=MsgAttr.REQ,
324
+ seqs=self.seqs.increment_and_get() & 255,
325
+ version=1,
326
+ subtype=self.user_account,
327
+ sys=mctl_sys,
328
+ timestamp=round(time.time() * 1000),
329
+ )
330
+ return luba_msg.SerializeToString()
@@ -1,28 +1,33 @@
1
1
  # === sendOrderMsg_Video ===
2
2
  from abc import ABC
3
+ import time
3
4
 
4
5
  from pymammotion.mammotion.commands.abstract_message import AbstractMessage
5
- from pymammotion.proto import luba_msg_pb2, luba_mul_pb2
6
- from pymammotion.proto.luba_msg import MsgCmdType, MsgDevice
6
+ from pymammotion.proto import LubaMsg, MsgAttr, MsgCmdType, MsgDevice, MulCameraPosition, MulSetVideo, SocMul
7
7
  from pymammotion.utility.device_type import DeviceType
8
8
 
9
9
 
10
10
  class MessageVideo(AbstractMessage, ABC):
11
- async def send_order_msg_video(self, mul):
12
- luba_msg = luba_msg_pb2.LubaMsg(
13
- msgtype=luba_msg_pb2.MSG_CMD_TYPE_MUL,
14
- sender=luba_msg_pb2.DEV_MOBILEAPP,
15
- rcver=self.get_msg_device(MsgCmdType.MSG_CMD_TYPE_MUL, MsgDevice.SOC_MODULE_MULTIMEDIA),
11
+ def send_order_msg_video(self, mul: SocMul):
12
+ luba_msg = LubaMsg(
13
+ msgtype=MsgCmdType.MUL,
14
+ msgattr=MsgAttr.REQ,
15
+ sender=MsgDevice.DEV_MOBILEAPP,
16
+ rcver=self.get_msg_device(MsgCmdType.MUL, MsgDevice.SOC_MODULE_MULTIMEDIA),
16
17
  mul=mul,
18
+ seqs=self.seqs.increment_and_get() & 255,
19
+ version=1,
20
+ subtype=self.user_account,
21
+ timestamp=round(time.time() * 1000),
17
22
  )
18
23
 
19
24
  return luba_msg.SerializeToString()
20
25
 
21
26
  def device_agora_join_channel_with_position(self, enter_state: int):
22
27
  position = (
23
- luba_mul_pb2.MUL_CAMERA_POSITION.ALL
24
- if DeviceType.is_yuka(self.get_device_name())
25
- else luba_mul_pb2.MUL_CAMERA_POSITION.LEFT
28
+ MulCameraPosition.ALL
29
+ if DeviceType.value_of_str(self.get_device_name()).get_value() == DeviceType.LUBA_YUKA.get_value()
30
+ else MulCameraPosition.LEFT
26
31
  )
27
- mctl_sys = luba_mul_pb2.SocMul(set_video=luba_mul_pb2.MulSetVideo(position=position, vi_switch=enter_state))
28
- return self.send_order_msg_video(mctl_sys)
32
+ soc_mul = SocMul(set_video=MulSetVideo(position=position, vi_switch=enter_state))
33
+ return self.send_order_msg_video(soc_mul)
@@ -1,5 +1,29 @@
1
- """mqtt init."""
1
+ """Mammotion devices module."""
2
2
 
3
- from .mammotion import MammotionBaseBLEDevice
3
+ from .mammotion import Mammotion, MammotionDeviceManager
4
+ from .mammotion_bluetooth import MammotionBaseBLEDevice
5
+ from .mammotion_cloud import MammotionBaseCloudDevice, MammotionCloud
6
+ from .mammotion_mower_ble import MammotionMowerBLEDevice
7
+ from .mammotion_mower_cloud import MammotionMowerCloudDevice
8
+ from .mower_device import MammotionMowerDevice
9
+ from .mower_manager import MammotionMowerDeviceManager
10
+ from .rtk_ble import MammotionRTKBLEDevice
11
+ from .rtk_cloud import MammotionRTKCloudDevice
12
+ from .rtk_device import MammotionRTKDevice
13
+ from .rtk_manager import MammotionRTKDeviceManager
4
14
 
5
- __all__ = ["MammotionBaseBLEDevice"]
15
+ __all__ = [
16
+ "Mammotion",
17
+ "MammotionDeviceManager",
18
+ "MammotionMowerDeviceManager",
19
+ "MammotionBaseBLEDevice",
20
+ "MammotionBaseCloudDevice",
21
+ "MammotionCloud",
22
+ "MammotionMowerBLEDevice",
23
+ "MammotionMowerCloudDevice",
24
+ "MammotionMowerDevice",
25
+ "MammotionRTKBLEDevice",
26
+ "MammotionRTKCloudDevice",
27
+ "MammotionRTKDevice",
28
+ "MammotionRTKDeviceManager",
29
+ ]
@@ -1,100 +1,35 @@
1
+ from abc import ABC, abstractmethod
1
2
  import asyncio
2
3
  import logging
3
- from abc import abstractmethod
4
- from typing import Any, Awaitable, Callable
4
+ from typing import Any
5
5
 
6
- import betterproto
6
+ import betterproto2
7
7
 
8
8
  from pymammotion.aliyun.model.dev_by_account_response import Device
9
- from pymammotion.data.model import RegionData
10
9
  from pymammotion.data.model.device import MowingDevice
11
- from pymammotion.data.state_manager import StateManager
12
- from pymammotion.proto.luba_msg import LubaMsg
13
- from pymammotion.proto.mctrl_nav import NavGetCommDataAck, NavGetHashListAck
10
+ from pymammotion.data.model.raw_data import RawMowerData
11
+ from pymammotion.data.mower_state_manager import MowerStateManager
12
+ from pymammotion.proto import LubaMsg
14
13
 
15
14
  _LOGGER = logging.getLogger(__name__)
16
15
 
17
16
 
18
- def find_next_integer(lst: list[int], current_hash: int) -> int | None:
19
- try:
20
- # Find the index of the current integer
21
- current_index = lst.index(current_hash)
22
-
23
- # Check if there is a next integer in the list
24
- if current_index + 1 < len(lst):
25
- return lst[current_index + 1]
26
- else:
27
- return None # Or raise an exception or handle it in some other way
28
- except ValueError:
29
- # Handle the case where current_int is not in the list
30
- return None # Or raise an exception or handle it in some other way
31
-
32
-
33
- class MammotionBaseDevice:
17
+ class MammotionBaseDevice(ABC):
34
18
  """Base class for Mammotion devices."""
35
19
 
36
- _state_manager: StateManager
37
- _cloud_device: Device | None = None
38
-
39
- def __init__(self, device: MowingDevice, cloud_device: Device | None = None) -> None:
20
+ def __init__(self, state_manager: MowerStateManager, cloud_device: Device) -> None:
40
21
  """Initialize MammotionBaseDevice."""
41
22
  self.loop = asyncio.get_event_loop()
42
- self._raw_data = LubaMsg().to_dict(casing=betterproto.Casing.SNAKE)
43
- self._state_manager = StateManager(device)
44
- self._state_manager.gethash_ack_callback = self.datahash_response
45
- self._state_manager.get_commondata_ack_callback = self.commdata_response
23
+ self._state_manager = state_manager
24
+ self._raw_data = dict()
25
+ self._raw_mower_data: RawMowerData = RawMowerData()
46
26
  self._notify_future: asyncio.Future[bytes] | None = None
47
27
  self._cloud_device = cloud_device
48
28
 
49
- def set_notification_callback(self, func: Callable[[], Awaitable[None]]) -> None:
50
- self._state_manager.on_notification_callback = func
51
-
52
- def set_queue_callback(self, func: Callable[[str, dict[str, Any]], Awaitable[bytes]]) -> None:
53
- self._state_manager.queue_command_callback = func
54
-
55
- async def datahash_response(self, hash_ack: NavGetHashListAck) -> None:
56
- """Handle datahash responses."""
57
- current_frame = hash_ack.current_frame
58
-
59
- missing_frames = self.mower.map.missing_hash_frame()
60
- if len(missing_frames) == 0:
61
- data_hash = self.mower.map.missing_hashlist.pop()
62
- return await self.queue_command("synchronize_hash_data", hash_num=data_hash)
63
-
64
- if current_frame != missing_frames[0] - 1:
65
- current_frame = missing_frames[0] - 1
66
- await self.queue_command("get_hash_response", total_frame=hash_ack.total_frame, current_frame=current_frame)
67
-
68
- async def commdata_response(self, common_data: NavGetCommDataAck) -> None:
69
- """Handle common data responses."""
70
- total_frame = common_data.total_frame
71
- current_frame = common_data.current_frame
72
-
73
- missing_frames = self.mower.map.missing_frame(common_data)
74
- if len(missing_frames) == 0:
75
- # get next in hash ack list
76
-
77
- data_hash = self.mower.map.missing_hashlist.pop() if len(self.mower.map.missing_hashlist) > 0 else None
78
- if data_hash is None:
79
- return
80
-
81
- await self.queue_command("synchronize_hash_data", hash_num=data_hash)
82
- else:
83
- if current_frame != missing_frames[0] - 1:
84
- current_frame = missing_frames[0] - 1
85
-
86
- region_data = RegionData()
87
- region_data.hash = common_data.hash
88
- region_data.action = common_data.action
89
- region_data.type = common_data.type
90
- region_data.total_frame = total_frame
91
- region_data.current_frame = current_frame
92
- await self.queue_command("get_regional_data", regional_data=region_data)
93
-
94
29
  def _update_raw_data(self, data: bytes) -> None:
95
30
  """Update raw and model data from notifications."""
96
31
  tmp_msg = LubaMsg().parse(data)
97
- res = betterproto.which_one_of(tmp_msg, "LubaSubMsg")
32
+ res = betterproto2.which_one_of(tmp_msg, "LubaSubMsg")
98
33
  match res[0]:
99
34
  case "nav":
100
35
  self._update_nav_data(tmp_msg)
@@ -109,11 +44,11 @@ class MammotionBaseDevice:
109
44
  case "ota":
110
45
  self._update_ota_data(tmp_msg)
111
46
 
112
- self.mower.update_raw(self._raw_data)
47
+ self._raw_mower_data.update_raw(self._raw_data)
113
48
 
114
49
  def _update_nav_data(self, tmp_msg) -> None:
115
50
  """Update navigation data."""
116
- nav_sub_msg = betterproto.which_one_of(tmp_msg.nav, "SubNavMsg")
51
+ nav_sub_msg = betterproto2.which_one_of(tmp_msg.nav, "SubNavMsg")
117
52
  if nav_sub_msg[1] is None:
118
53
  _LOGGER.debug("Sub message was NoneType %s", nav_sub_msg[0])
119
54
  return
@@ -121,32 +56,32 @@ class MammotionBaseDevice:
121
56
  if isinstance(nav_sub_msg[1], int):
122
57
  nav[nav_sub_msg[0]] = nav_sub_msg[1]
123
58
  else:
124
- nav[nav_sub_msg[0]] = nav_sub_msg[1].to_dict(casing=betterproto.Casing.SNAKE)
59
+ nav[nav_sub_msg[0]] = nav_sub_msg[1].to_dict(casing=betterproto2.Casing.SNAKE)
125
60
  self._raw_data["nav"] = nav
126
61
 
127
- def _update_sys_data(self, tmp_msg) -> None:
62
+ def _update_sys_data(self, tmp_msg: LubaMsg) -> None:
128
63
  """Update system data."""
129
- sys_sub_msg = betterproto.which_one_of(tmp_msg.sys, "SubSysMsg")
64
+ sys_sub_msg = betterproto2.which_one_of(tmp_msg.sys, "SubSysMsg")
130
65
  if sys_sub_msg[1] is None:
131
66
  _LOGGER.debug("Sub message was NoneType %s", sys_sub_msg[0])
132
67
  return
133
68
  sys = self._raw_data.get("sys", {})
134
- sys[sys_sub_msg[0]] = sys_sub_msg[1].to_dict(casing=betterproto.Casing.SNAKE)
69
+ sys[sys_sub_msg[0]] = sys_sub_msg[1].to_dict(casing=betterproto2.Casing.SNAKE)
135
70
  self._raw_data["sys"] = sys
136
71
 
137
- def _update_driver_data(self, tmp_msg) -> None:
72
+ def _update_driver_data(self, tmp_msg: LubaMsg) -> None:
138
73
  """Update driver data."""
139
- drv_sub_msg = betterproto.which_one_of(tmp_msg.driver, "SubDrvMsg")
74
+ drv_sub_msg = betterproto2.which_one_of(tmp_msg.driver, "SubDrvMsg")
140
75
  if drv_sub_msg[1] is None:
141
76
  _LOGGER.debug("Sub message was NoneType %s", drv_sub_msg[0])
142
77
  return
143
78
  drv = self._raw_data.get("driver", {})
144
- drv[drv_sub_msg[0]] = drv_sub_msg[1].to_dict(casing=betterproto.Casing.SNAKE)
79
+ drv[drv_sub_msg[0]] = drv_sub_msg[1].to_dict(casing=betterproto2.Casing.SNAKE)
145
80
  self._raw_data["driver"] = drv
146
81
 
147
- def _update_net_data(self, tmp_msg) -> None:
82
+ def _update_net_data(self, tmp_msg: LubaMsg) -> None:
148
83
  """Update network data."""
149
- net_sub_msg = betterproto.which_one_of(tmp_msg.net, "NetSubType")
84
+ net_sub_msg = betterproto2.which_one_of(tmp_msg.net, "NetSubType")
150
85
  if net_sub_msg[1] is None:
151
86
  _LOGGER.debug("Sub message was NoneType %s", net_sub_msg[0])
152
87
  return
@@ -154,27 +89,27 @@ class MammotionBaseDevice:
154
89
  if isinstance(net_sub_msg[1], int):
155
90
  net[net_sub_msg[0]] = net_sub_msg[1]
156
91
  else:
157
- net[net_sub_msg[0]] = net_sub_msg[1].to_dict(casing=betterproto.Casing.SNAKE)
92
+ net[net_sub_msg[0]] = net_sub_msg[1].to_dict(casing=betterproto2.Casing.SNAKE)
158
93
  self._raw_data["net"] = net
159
94
 
160
- def _update_mul_data(self, tmp_msg) -> None:
95
+ def _update_mul_data(self, tmp_msg: LubaMsg) -> None:
161
96
  """Update mul data."""
162
- mul_sub_msg = betterproto.which_one_of(tmp_msg.mul, "SubMul")
97
+ mul_sub_msg = betterproto2.which_one_of(tmp_msg.mul, "SubMul")
163
98
  if mul_sub_msg[1] is None:
164
99
  _LOGGER.debug("Sub message was NoneType %s", mul_sub_msg[0])
165
100
  return
166
101
  mul = self._raw_data.get("mul", {})
167
- mul[mul_sub_msg[0]] = mul_sub_msg[1].to_dict(casing=betterproto.Casing.SNAKE)
102
+ mul[mul_sub_msg[0]] = mul_sub_msg[1].to_dict(casing=betterproto2.Casing.SNAKE)
168
103
  self._raw_data["mul"] = mul
169
104
 
170
- def _update_ota_data(self, tmp_msg) -> None:
105
+ def _update_ota_data(self, tmp_msg: LubaMsg) -> None:
171
106
  """Update OTA data."""
172
- ota_sub_msg = betterproto.which_one_of(tmp_msg.ota, "SubOtaMsg")
107
+ ota_sub_msg = betterproto2.which_one_of(tmp_msg.ota, "SubOtaMsg")
173
108
  if ota_sub_msg[1] is None:
174
109
  _LOGGER.debug("Sub message was NoneType %s", ota_sub_msg[0])
175
110
  return
176
111
  ota = self._raw_data.get("ota", {})
177
- ota[ota_sub_msg[0]] = ota_sub_msg[1].to_dict(casing=betterproto.Casing.SNAKE)
112
+ ota[ota_sub_msg[0]] = ota_sub_msg[1].to_dict(casing=betterproto2.Casing.SNAKE)
178
113
  self._raw_data["ota"] = ota
179
114
 
180
115
  @property
@@ -188,53 +123,41 @@ class MammotionBaseDevice:
188
123
  return self._state_manager.get_device()
189
124
 
190
125
  @abstractmethod
191
- async def queue_command(self, key: str, **kwargs: any) -> bytes | None:
126
+ async def queue_command(self, key: str, **kwargs: Any) -> None:
192
127
  """Queue commands to mower."""
193
128
 
194
129
  @abstractmethod
195
- async def _ble_sync(self):
130
+ async def _ble_sync(self) -> None:
196
131
  """Send ble sync command every 3 seconds or sooner."""
197
132
 
198
133
  @abstractmethod
199
- def stop(self):
134
+ def stop(self) -> None:
200
135
  """Stop everything ready for destroying."""
201
136
 
202
- async def start_sync(self, retry: int) -> None:
203
- """Start synchronization with the device."""
204
- await self.queue_command("get_device_base_info")
205
- await self.queue_command("get_device_product_model")
206
- await self.queue_command("get_report_cfg")
207
- """RTK and dock location."""
208
- await self.queue_command("allpowerfull_rw", id=5, rw=1, context=1)
209
-
210
- async def start_map_sync(self) -> None:
211
- """Start sync of map data."""
212
-
213
- if self._cloud_device and len(self.mower.map.area_name) == 0:
214
- await self.queue_command("get_area_name_list", device_id=self._cloud_device.iotId)
215
-
216
- await self.queue_command("read_plan", sub_cmd=2, plan_index=0)
137
+ async def async_read_settings(self) -> None:
138
+ """Read settings from device."""
139
+ # no cutting in rain nav_sys_param_cmd (id 3 context 1/0)
140
+ await self.queue_command("read_write_device", rw_id=3, context=1, rw=0)
141
+ # ??
142
+ await self.queue_command("read_write_device", rw_id=4, context=1, rw=0)
143
+ # turning mode nav_sys_param_cmd (id 6, context 1/0)
144
+ await self.queue_command("read_write_device", rw_id=6, context=1, rw=0)
145
+ # traversal mode
146
+ await self.queue_command("read_write_device", rw_id=7, context=1, rw=0)
217
147
 
218
- await self.queue_command("get_all_boundary_hash_list", sub_cmd=0)
219
- await self.queue_command("get_hash_response", total_frame=1, current_frame=1)
220
- if len(self.mower.map.missing_hashlist) > 0:
221
- data_hash = self.mower.map.missing_hashlist.pop()
222
- await self.queue_command("synchronize_hash_data", hash_num=data_hash)
148
+ await self.queue_command("read_and_set_sidelight", is_sidelight=True, operate=1)
223
149
 
224
- # sub_cmd 3 is job hashes??
225
- # sub_cmd 4 is dump location (yuka)
226
- # jobs list
227
- # hash_list_result = await self._send_command_with_args("get_all_boundary_hash_list", sub_cmd=3)
150
+ await self.queue_command("read_and_set_rtk_pairing_code", op=1, cfg="")
228
151
 
229
152
  async def async_get_errors(self) -> None:
230
153
  """Error codes."""
231
- await self.queue_command("allpowerfull_rw", id=5, rw=1, context=2)
232
- await self.queue_command("allpowerfull_rw", id=5, rw=1, context=3)
154
+ await self.queue_command("read_write_device", rw_id=5, rw=1, context=2)
155
+ await self.queue_command("read_write_device", rw_id=5, rw=1, context=3)
233
156
 
234
- async def command(self, key: str, **kwargs):
157
+ async def command(self, key: str, **kwargs: Any) -> None:
235
158
  """Send a command to the device."""
236
- return await self.queue_command(key, **kwargs)
159
+ await self.queue_command(key, **kwargs)
237
160
 
238
161
  @property
239
- def state_manager(self):
162
+ def state_manager(self) -> MowerStateManager:
240
163
  return self._state_manager