pymammotion 0.3.8__py3-none-any.whl → 0.4.0__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.
Files changed (69) hide show
  1. pymammotion/__init__.py +2 -2
  2. pymammotion/aliyun/cloud_gateway.py +12 -9
  3. pymammotion/aliyun/model/aep_response.py +1 -2
  4. pymammotion/aliyun/model/dev_by_account_response.py +7 -8
  5. pymammotion/aliyun/model/login_by_oauth_response.py +2 -3
  6. pymammotion/aliyun/model/regions_response.py +3 -3
  7. pymammotion/aliyun/model/session_by_authcode_response.py +1 -2
  8. pymammotion/aliyun/model/stream_subscription_response.py +1 -2
  9. pymammotion/bluetooth/ble.py +5 -5
  10. pymammotion/bluetooth/ble_message.py +9 -13
  11. pymammotion/data/model/device.py +31 -228
  12. pymammotion/data/model/device_config.py +0 -10
  13. pymammotion/data/model/device_info.py +13 -0
  14. pymammotion/data/model/device_limits.py +49 -0
  15. pymammotion/data/model/generate_route_information.py +1 -1
  16. pymammotion/data/model/hash_list.py +6 -2
  17. pymammotion/data/model/plan.py +0 -3
  18. pymammotion/data/model/raw_data.py +215 -0
  19. pymammotion/data/model/region_data.py +10 -11
  20. pymammotion/data/model/report_info.py +1 -1
  21. pymammotion/data/mqtt/event.py +18 -14
  22. pymammotion/data/mqtt/properties.py +1 -1
  23. pymammotion/data/mqtt/status.py +1 -1
  24. pymammotion/data/state_manager.py +83 -23
  25. pymammotion/http/encryption.py +220 -0
  26. pymammotion/http/http.py +92 -39
  27. pymammotion/http/model/http.py +2 -2
  28. pymammotion/mammotion/commands/abstract_message.py +2 -2
  29. pymammotion/mammotion/commands/messages/driver.py +28 -21
  30. pymammotion/mammotion/commands/messages/media.py +10 -14
  31. pymammotion/mammotion/commands/messages/navigation.py +14 -11
  32. pymammotion/mammotion/commands/messages/network.py +15 -12
  33. pymammotion/mammotion/commands/messages/ota.py +9 -14
  34. pymammotion/mammotion/commands/messages/system.py +27 -24
  35. pymammotion/mammotion/commands/messages/video.py +9 -14
  36. pymammotion/mammotion/devices/base.py +7 -14
  37. pymammotion/mammotion/devices/mammotion.py +22 -13
  38. pymammotion/mammotion/devices/mammotion_bluetooth.py +15 -4
  39. pymammotion/mammotion/devices/mammotion_cloud.py +30 -12
  40. pymammotion/mqtt/linkkit/__init__.py +5 -0
  41. pymammotion/mqtt/linkkit/h2client.py +585 -0
  42. pymammotion/mqtt/linkkit/linkkit.py +3020 -0
  43. pymammotion/mqtt/mammotion_mqtt.py +13 -9
  44. pymammotion/proto/__init__.py +2176 -1
  45. pymammotion/proto/luba_mul.proto +1 -0
  46. pymammotion/proto/luba_mul_pb2.py +8 -8
  47. pymammotion/proto/luba_mul_pb2.pyi +1 -0
  48. pymammotion/proto/mctrl_nav_pb2.py +69 -67
  49. pymammotion/proto/mctrl_nav_pb2.pyi +13 -5
  50. pymammotion/proto/mctrl_sys_pb2.py +41 -37
  51. pymammotion/proto/mctrl_sys_pb2.pyi +34 -11
  52. pymammotion/utility/constant/device_constant.py +14 -5
  53. pymammotion/utility/device_config.py +754 -0
  54. pymammotion/utility/device_type.py +64 -16
  55. {pymammotion-0.3.8.dist-info → pymammotion-0.4.0.dist-info}/METADATA +9 -9
  56. {pymammotion-0.3.8.dist-info → pymammotion-0.4.0.dist-info}/RECORD +58 -62
  57. {pymammotion-0.3.8.dist-info → pymammotion-0.4.0.dist-info}/WHEEL +1 -1
  58. pymammotion/aliyun/cloud_service.py +0 -65
  59. pymammotion/proto/basestation.py +0 -59
  60. pymammotion/proto/common.py +0 -12
  61. pymammotion/proto/dev_net.py +0 -381
  62. pymammotion/proto/luba_msg.py +0 -81
  63. pymammotion/proto/luba_mul.py +0 -76
  64. pymammotion/proto/mctrl_driver.py +0 -100
  65. pymammotion/proto/mctrl_nav.py +0 -664
  66. pymammotion/proto/mctrl_ota.py +0 -48
  67. pymammotion/proto/mctrl_pept.py +0 -41
  68. pymammotion/proto/mctrl_sys.py +0 -574
  69. {pymammotion-0.3.8.dist-info → pymammotion-0.4.0.dist-info}/LICENSE +0 -0
@@ -1,11 +1,11 @@
1
1
  # === sendOrderMsg_Net ===
2
- import time
3
2
  from abc import ABC
3
+ import time
4
4
 
5
5
  from pymammotion import logger
6
6
  from pymammotion.mammotion.commands.abstract_message import AbstractMessage
7
7
  from pymammotion.mammotion.commands.messages.navigation import MessageNavigation
8
- from pymammotion.proto.dev_net import (
8
+ from pymammotion.proto import (
9
9
  DevNet,
10
10
  DrvDebugDdsZmq,
11
11
  DrvDevInfoReq,
@@ -18,11 +18,14 @@ from pymammotion.proto.dev_net import (
18
18
  DrvWifiUpload,
19
19
  GetNetworkInfoReq,
20
20
  IotConctrlType,
21
+ LubaMsg,
21
22
  MnetCfg,
23
+ MsgAttr,
24
+ MsgCmdType,
25
+ MsgDevice,
22
26
  NetType,
23
27
  SetMnetCfgReq,
24
28
  )
25
- from pymammotion.proto.luba_msg import LubaMsg, MsgAttr, MsgCmdType, MsgDevice
26
29
 
27
30
 
28
31
  class MessageNetwork(AbstractMessage, ABC):
@@ -31,10 +34,10 @@ class MessageNetwork(AbstractMessage, ABC):
31
34
  @staticmethod
32
35
  def send_order_msg_net(build: DevNet) -> bytes:
33
36
  luba_msg = LubaMsg(
34
- msgtype=MsgCmdType.MSG_CMD_TYPE_ESP,
37
+ msgtype=MsgCmdType.ESP,
35
38
  sender=MsgDevice.DEV_MOBILEAPP,
36
39
  rcver=MsgDevice.DEV_COMM_ESP,
37
- msgattr=MsgAttr.MSG_ATTR_REQ,
40
+ msgattr=MsgAttr.REQ,
38
41
  seqs=1,
39
42
  version=1,
40
43
  subtype=1,
@@ -76,7 +79,7 @@ class MessageNetwork(AbstractMessage, ABC):
76
79
 
77
80
  def set_zmq_enable(self) -> bytes:
78
81
  build = DevNet(
79
- todev_set_dds2zmq=DrvDebugDdsZmq(
82
+ todev_set_dds2_zmq=DrvDebugDdsZmq(
80
83
  is_enable=True,
81
84
  rx_topic_name="perception_post_result",
82
85
  tx_zmq_url="tcp://0.0.0.0:5555",
@@ -164,7 +167,7 @@ class MessageNetwork(AbstractMessage, ABC):
164
167
  todev_ble_sync=1,
165
168
  todev_set_mnet_cfg_req=SetMnetCfgReq(
166
169
  cfg=MnetCfg(
167
- type=NetType.NET_TYPE_WIFI,
170
+ type=NetType.WIFI,
168
171
  inet_enable=new_4g_status,
169
172
  mnet_enable=new_4g_status,
170
173
  )
@@ -177,7 +180,7 @@ class MessageNetwork(AbstractMessage, ABC):
177
180
  def set_device_wifi_enable_status(self, new_wifi_status: bool) -> bytes:
178
181
  build = DevNet(
179
182
  todev_ble_sync=1,
180
- todev__wifi__configuration=DrvWifiSet(config_param=4, wifi_enable=new_wifi_status),
183
+ todev_wifi_configuration=DrvWifiSet(config_param=4, wifi_enable=new_wifi_status),
181
184
  )
182
185
  logger.debug(f"szNetwork: Send command - set network (on/off status). newWifiStatus={new_wifi_status}")
183
186
  return self.send_order_msg_net(build)
@@ -185,7 +188,7 @@ class MessageNetwork(AbstractMessage, ABC):
185
188
  def wifi_connectinfo_update(self) -> bytes:
186
189
  build = DevNet(
187
190
  todev_ble_sync=1,
188
- todev__wifi_msg_upload=DrvWifiUpload(wifi_msg_upload=1),
191
+ todev_wifi_msg_upload=DrvWifiUpload(wifi_msg_upload=1),
189
192
  )
190
193
  logger.debug("Send command - get Wifi connection information")
191
194
  return self.send_order_msg_net(build)
@@ -193,17 +196,17 @@ class MessageNetwork(AbstractMessage, ABC):
193
196
  def wifi_connectinfo_update2(self) -> None:
194
197
  hash_map = {"getMsgCmd": 1}
195
198
  # self.post_custom_data(self.get_json_string(
196
- # 68, hash_map)) # ToDo: Fix this
199
+ # 68, hash_map)) # TODO: Fix this
197
200
 
198
201
  def get_record_wifi_list(self) -> bytes:
199
- build = DevNet(todev_ble_sync=1, todev__wifi_list_upload=DrvWifiList())
202
+ build = DevNet(todev_ble_sync=1, todev_wifi_list_upload=DrvWifiList())
200
203
  logger.debug("Send command - get memorized WiFi list upload command")
201
204
  return self.send_order_msg_net(build)
202
205
 
203
206
  def close_clear_connect_current_wifi(self, ssid: str, status: int) -> bytes:
204
207
  build = DevNet(
205
208
  todev_ble_sync=1,
206
- todev__wifi__configuration=DrvWifiSet(config_param=status, confssid=ssid),
209
+ todev_wifi_configuration=DrvWifiSet(config_param=status, confssid=ssid),
207
210
  )
208
211
  logger.debug(
209
212
  f"Send command - set network (disconnect, direct connect, forget, no operation reconnect) operation command (downlink ssid={ssid}, status={status})"
@@ -2,17 +2,16 @@
2
2
  from abc import ABC
3
3
 
4
4
  from pymammotion.mammotion.commands.abstract_message import AbstractMessage
5
- from pymammotion.proto import luba_msg_pb2, mctrl_ota_pb2
6
- from pymammotion.proto.luba_msg import MsgCmdType, MsgDevice
5
+ from pymammotion.proto import GetInfoReq, InfoType, LubaMsg, MctlOta, MsgAttr, MsgCmdType, MsgDevice
7
6
 
8
7
 
9
8
  class MessageOta(AbstractMessage, ABC):
10
9
  def send_order_msg_ota(self, ota):
11
- luba_msg = luba_msg_pb2.LubaMsg(
12
- msgtype=luba_msg_pb2.MSG_CMD_TYPE_EMBED_OTA,
13
- sender=luba_msg_pb2.DEV_MOBILEAPP,
14
- rcver=self.get_msg_device(MsgCmdType.MSG_CMD_TYPE_EMBED_OTA, MsgDevice.DEV_MAINCTL),
15
- msgattr=luba_msg_pb2.MSG_ATTR_REQ,
10
+ luba_msg = LubaMsg(
11
+ msgtype=MsgCmdType.EMBED_OTA,
12
+ sender=MsgDevice.DEV_MOBILEAPP,
13
+ rcver=self.get_msg_device(MsgCmdType.EMBED_OTA, MsgDevice.DEV_MAINCTL),
14
+ msgattr=MsgAttr.MSG_ATTR_REQ,
16
15
  seqs=1,
17
16
  version=1,
18
17
  subtype=1,
@@ -22,17 +21,13 @@ class MessageOta(AbstractMessage, ABC):
22
21
  return luba_msg.SerializeToString()
23
22
 
24
23
  def get_device_ota_info(self, log_type: int):
25
- todev_get_info_req = mctrl_ota_pb2.MctlOta(
26
- todev_get_info_req=mctrl_ota_pb2.getInfoReq(type=mctrl_ota_pb2.IT_OTA)
27
- )
24
+ todev_get_info_req = MctlOta(todev_get_info_req=GetInfoReq(type=InfoType.IT_OTA))
28
25
 
29
26
  print("===Send command to get upgrade details===logType:" + str(log_type))
30
27
  return self.send_order_msg_ota(todev_get_info_req)
31
28
 
32
- def get_device_info_new(self):
29
+ def get_device_info_new(self) -> bytes:
33
30
  """New device call for OTA upgrade information."""
34
- todev_get_info_req = mctrl_ota_pb2.MctlOta(
35
- todev_get_info_req=mctrl_ota_pb2.getInfoReq(type=mctrl_ota_pb2.IT_BASE)
36
- )
31
+ todev_get_info_req = MctlOta(todev_get_info_req=GetInfoReq(type=InfoType.IT_BASE))
37
32
  print("Send to get OTA upgrade information", "Get device information")
38
33
  return self.send_order_msg_ota(todev_get_info_req)
@@ -1,17 +1,20 @@
1
1
  # === sendOrderMsg_Sys ===
2
+ from abc import ABC
2
3
  import datetime
3
4
  import time
4
- from abc import ABC
5
5
 
6
6
  from pymammotion import logger
7
7
  from pymammotion.mammotion.commands.abstract_message import AbstractMessage
8
8
  from pymammotion.mammotion.commands.messages.navigation import MessageNavigation
9
- from pymammotion.proto.luba_msg import LubaMsg, MsgAttr, MsgCmdType, MsgDevice
10
- from pymammotion.proto.mctrl_sys import (
9
+ from pymammotion.proto import (
11
10
  DeviceProductTypeInfoT,
12
11
  LoraCfgReq,
12
+ LubaMsg,
13
13
  MctlSys,
14
14
  MCtrlSimulationCmdData,
15
+ MsgAttr,
16
+ MsgCmdType,
17
+ MsgDevice,
15
18
  ReportInfoCfg,
16
19
  RptAct,
17
20
  RptInfoType,
@@ -26,12 +29,12 @@ from pymammotion.utility.device_type import DeviceType
26
29
  class MessageSystem(AbstractMessage, ABC):
27
30
  messageNavigation: MessageNavigation = MessageNavigation()
28
31
 
29
- def send_order_msg_sys(self, sys):
32
+ def send_order_msg_sys(self, sys) -> bytes:
30
33
  luba_msg = LubaMsg(
31
- msgtype=MsgCmdType.MSG_CMD_TYPE_EMBED_SYS,
32
- msgattr=MsgAttr.MSG_ATTR_REQ,
34
+ msgtype=MsgCmdType.EMBED_SYS,
35
+ msgattr=MsgAttr.REQ,
33
36
  sender=MsgDevice.DEV_MOBILEAPP,
34
- rcver=self.get_msg_device(MsgCmdType.MSG_CMD_TYPE_EMBED_SYS, MsgDevice.DEV_MAINCTL),
37
+ rcver=self.get_msg_device(MsgCmdType.EMBED_SYS, MsgDevice.DEV_MAINCTL),
35
38
  sys=sys,
36
39
  seqs=1,
37
40
  version=1,
@@ -42,10 +45,10 @@ class MessageSystem(AbstractMessage, ABC):
42
45
  return luba_msg.SerializeToString()
43
46
 
44
47
  @staticmethod
45
- def send_order_msg_sys_legacy(sys):
48
+ def send_order_msg_sys_legacy(sys) -> bytes:
46
49
  luba_msg = LubaMsg(
47
- msgtype=MsgCmdType.MSG_CMD_TYPE_EMBED_SYS,
48
- msgattr=MsgAttr.MSG_ATTR_REQ,
50
+ msgtype=MsgCmdType.EMBED_SYS,
51
+ msgattr=MsgAttr.REQ,
49
52
  sender=MsgDevice.DEV_MOBILEAPP,
50
53
  rcver=MsgDevice.DEV_MAINCTL,
51
54
  sys=sys,
@@ -57,7 +60,7 @@ class MessageSystem(AbstractMessage, ABC):
57
60
 
58
61
  return luba_msg.SerializeToString()
59
62
 
60
- def reset_system(self):
63
+ def reset_system(self) -> bytes:
61
64
  build = MctlSys(todev_reset_system=1)
62
65
  logger.debug("Send command - send factory reset")
63
66
  return self.send_order_msg_sys(build)
@@ -102,7 +105,7 @@ class MessageSystem(AbstractMessage, ABC):
102
105
  is_sidelight}, operate:{operate}, timeCtrlLight:{build}")
103
106
  return self.send_order_msg_sys(build2)
104
107
 
105
- def test_tool_order_to_sys(self, sub_cmd: int, param_id: int, param_value: list[int]):
108
+ def test_tool_order_to_sys(self, sub_cmd: int, param_id: int, param_value: list[int]) -> bytes:
106
109
  build = MCtrlSimulationCmdData(sub_cmd=sub_cmd, param_id=param_id, param_value=param_value)
107
110
  logger.debug(f"Send tool test command: subCmd={sub_cmd}, param_id:{
108
111
  param_id}, param_value={param_value}")
@@ -111,20 +114,20 @@ class MessageSystem(AbstractMessage, ABC):
111
114
  param_id}, param_value={param_value}")
112
115
  return self.send_order_msg_sys(build2)
113
116
 
114
- def read_and_set_rtk_paring_code(self, op: int, cgf: str | None = None):
117
+ def read_and_set_rtk_paring_code(self, op: int, cgf: str | None = None) -> bytes:
115
118
  logger.debug(f"Send read and write base station configuration quality op:{
116
119
  op}, cgf:{cgf}")
117
120
  return self.send_order_msg_sys(MctlSys(todev_lora_cfg_req=LoraCfgReq(op=op, cfg=cgf)))
118
121
 
119
- def allpowerfull_rw(self, id: int, context: int, rw: int) -> bytes:
120
- if (id == 6 or id == 3 or id == 7) and DeviceType.is_luba_2(self.get_device_name()):
121
- return self.messageNavigation.allpowerfull_rw_adapter_x3(id, context, rw)
122
- build = MctlSys(bidire_comm_cmd=SysCommCmd(id=id, context=context, rw=rw))
123
- logger.debug(f"Send command - 9 general read and write command id={id}, context={context}, rw={rw}")
124
- if id == 5:
122
+ def allpowerfull_rw(self, rw_id: int, context: int, rw: int) -> bytes:
123
+ if (rw_id == 6 or rw_id == 3 or rw_id == 7) and DeviceType.is_luba_2(self.get_device_name()):
124
+ return self.messageNavigation.allpowerfull_rw_adapter_x3(rw_id, context, rw)
125
+ build = MctlSys(bidire_comm_cmd=SysCommCmd(id=rw_id, context=context, rw=rw))
126
+ logger.debug(f"Send command - 9 general read and write command id={rw_id}, context={context}, rw={rw}")
127
+ if rw_id == 5:
125
128
  # TODO investigate if the original code makes any difference to this call.
126
129
  """
127
- LubaMsgOuterClass.LubaMsg.Builder protoBufBuilderSet = getProtoBufBuilderSet(LubaMsgOuterClass.MsgCmdType.MSG_CMD_TYPE_EMBED_SYS, LubaMsgOuterClass.MsgDevice.DEV_MAINCTL, LubaMsgOuterClass.MsgAttr.MSG_ATTR_REQ);
130
+ LubaMsgOuterClass.LubaMsg.Builder protoBufBuilderSet = getProtoBufBuilderSet(LubaMsgOuterClass.MsgCmdType.EMBED_SYS, LubaMsgOuterClass.MsgDevice.DEV_MAINCTL, LubaMsgOuterClass.MsgAttr.REQ);
128
131
  protoBufBuilderSet.setSys(build);
129
132
  sendMsg(protoBufBuilderSet, 122, true, "发送指令--9通用读写命令id=" + i + ",context=" + i2 + ",rw=" + i3);
130
133
  """
@@ -298,10 +301,10 @@ class MessageSystem(AbstractMessage, ABC):
298
301
  mctl_sys.todev_report_cfg.sub.append(RptInfoType.RIT_VISION_STATISTIC)
299
302
 
300
303
  luba_msg = LubaMsg(
301
- msgtype=MsgCmdType.MSG_CMD_TYPE_EMBED_SYS,
304
+ msgtype=MsgCmdType.EMBED_SYS,
302
305
  sender=MsgDevice.DEV_MOBILEAPP,
303
306
  rcver=MsgDevice.DEV_MAINCTL,
304
- msgattr=MsgAttr.MSG_ATTR_REQ,
307
+ msgattr=MsgAttr.REQ,
305
308
  seqs=1,
306
309
  version=1,
307
310
  subtype=1,
@@ -334,10 +337,10 @@ class MessageSystem(AbstractMessage, ABC):
334
337
  mctl_sys.todev_report_cfg.sub.append(RptInfoType.RIT_BASESTATION_INFO)
335
338
 
336
339
  luba_msg = LubaMsg(
337
- msgtype=MsgCmdType.MSG_CMD_TYPE_EMBED_SYS,
340
+ msgtype=MsgCmdType.EMBED_SYS,
338
341
  sender=MsgDevice.DEV_MOBILEAPP,
339
342
  rcver=MsgDevice.DEV_MAINCTL,
340
- msgattr=MsgAttr.MSG_ATTR_REQ,
343
+ msgattr=MsgAttr.REQ,
341
344
  seqs=1,
342
345
  version=1,
343
346
  subtype=1,
@@ -1,20 +1,19 @@
1
1
  # === sendOrderMsg_Video ===
2
- import time
3
2
  from abc import ABC
3
+ import time
4
4
 
5
5
  from pymammotion.mammotion.commands.abstract_message import AbstractMessage
6
- from pymammotion.proto import luba_msg_pb2, luba_mul_pb2
7
- from pymammotion.proto.luba_msg import MsgAttr, MsgCmdType, MsgDevice
6
+ from pymammotion.proto import LubaMsg, MsgAttr, MsgCmdType, MsgDevice, MulCameraPosition, MulSetVideo, SocMul
8
7
  from pymammotion.utility.device_type import DeviceType
9
8
 
10
9
 
11
10
  class MessageVideo(AbstractMessage, ABC):
12
11
  async def send_order_msg_video(self, mul):
13
- luba_msg = luba_msg_pb2.LubaMsg(
14
- msgtype=luba_msg_pb2.MSG_CMD_TYPE_MUL,
15
- msgattr=MsgAttr.MSG_ATTR_REQ,
16
- sender=luba_msg_pb2.DEV_MOBILEAPP,
17
- rcver=self.get_msg_device(MsgCmdType.MSG_CMD_TYPE_MUL, MsgDevice.SOC_MODULE_MULTIMEDIA),
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),
18
17
  mul=mul,
19
18
  seqs=1,
20
19
  version=1,
@@ -25,10 +24,6 @@ class MessageVideo(AbstractMessage, ABC):
25
24
  return luba_msg.SerializeToString()
26
25
 
27
26
  def device_agora_join_channel_with_position(self, enter_state: int):
28
- position = (
29
- luba_mul_pb2.MUL_CAMERA_POSITION.ALL
30
- if DeviceType.is_yuka(self.get_device_name())
31
- else luba_mul_pb2.MUL_CAMERA_POSITION.LEFT
32
- )
33
- mctl_sys = luba_mul_pb2.SocMul(set_video=luba_mul_pb2.MulSetVideo(position=position, vi_switch=enter_state))
27
+ position = MulCameraPosition.ALL if DeviceType.is_yuka(self.get_device_name()) else MulCameraPosition.LEFT
28
+ mctl_sys = SocMul(set_video=MulSetVideo(position=position, vi_switch=enter_state))
34
29
  return self.send_order_msg_video(mctl_sys)
@@ -1,16 +1,16 @@
1
+ from abc import 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
6
  import betterproto
7
7
 
8
8
  from pymammotion.aliyun.model.dev_by_account_response import Device
9
9
  from pymammotion.data.model import RegionData
10
10
  from pymammotion.data.model.device import MowingDevice
11
+ from pymammotion.data.model.raw_data import RawMowerData
11
12
  from pymammotion.data.state_manager import StateManager
12
- from pymammotion.proto.luba_msg import LubaMsg
13
- from pymammotion.proto.mctrl_nav import NavGetCommDataAck, NavGetHashListAck, SvgMessageAckT
13
+ from pymammotion.proto import LubaMsg, NavGetCommDataAck, NavGetHashListAck, SvgMessageAckT
14
14
 
15
15
  _LOGGER = logging.getLogger(__name__)
16
16
 
@@ -36,19 +36,12 @@ class MammotionBaseDevice:
36
36
  def __init__(self, state_manager: StateManager, cloud_device: Device | None = None) -> None:
37
37
  """Initialize MammotionBaseDevice."""
38
38
  self.loop = asyncio.get_event_loop()
39
- self._raw_data = LubaMsg().to_dict(casing=betterproto.Casing.SNAKE)
40
39
  self._state_manager = state_manager
41
- self._state_manager.gethash_ack_callback = self.datahash_response
42
- self._state_manager.get_commondata_ack_callback = self.commdata_response
40
+ self._raw_data = dict()
41
+ self._raw_mower_data: RawMowerData = RawMowerData()
43
42
  self._notify_future: asyncio.Future[bytes] | None = None
44
43
  self._cloud_device = cloud_device
45
44
 
46
- def set_notification_callback(self, func: Callable[[tuple[str, Any | None]], Awaitable[None]]) -> None:
47
- self._state_manager.on_notification_callback = func
48
-
49
- def set_queue_callback(self, func: Callable[[str, dict[str, Any]], Awaitable[bytes]]) -> None:
50
- self._state_manager.queue_command_callback = func
51
-
52
45
  async def datahash_response(self, hash_ack: NavGetHashListAck) -> None:
53
46
  """Handle datahash responses."""
54
47
  current_frame = hash_ack.current_frame
@@ -108,7 +101,7 @@ class MammotionBaseDevice:
108
101
  case "ota":
109
102
  self._update_ota_data(tmp_msg)
110
103
 
111
- self.mower.update_raw(self._raw_data)
104
+ self._raw_mower_data.update_raw(self._raw_data)
112
105
 
113
106
  def _update_nav_data(self, tmp_msg) -> None:
114
107
  """Update navigation data."""
@@ -3,8 +3,8 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import asyncio
6
- import logging
7
6
  from enum import Enum
7
+ import logging
8
8
  from typing import Any
9
9
 
10
10
  from bleak.backends.device import BLEDevice
@@ -14,7 +14,7 @@ from pymammotion.aliyun.model.dev_by_account_response import Device
14
14
  from pymammotion.data.model.account import Credentials
15
15
  from pymammotion.data.model.device import MowingDevice
16
16
  from pymammotion.data.state_manager import StateManager
17
- from pymammotion.http.http import connect_http
17
+ from pymammotion.http.http import MammotionHTTP
18
18
  from pymammotion.mammotion.devices.mammotion_bluetooth import MammotionBaseBLEDevice
19
19
  from pymammotion.mammotion.devices.mammotion_cloud import MammotionBaseCloudDevice, MammotionCloud
20
20
  from pymammotion.mqtt import MammotionMQTT
@@ -84,9 +84,15 @@ class MammotionMixedDeviceManager:
84
84
  def replace_cloud(self, cloud_device: MammotionBaseCloudDevice) -> None:
85
85
  self._cloud_device = cloud_device
86
86
 
87
+ def remove_cloud(self) -> None:
88
+ del self._cloud_device
89
+
87
90
  def replace_ble(self, ble_device: MammotionBaseBLEDevice) -> None:
88
91
  self._ble_device = ble_device
89
92
 
93
+ def remove_ble(self) -> None:
94
+ del self._ble_device
95
+
90
96
  def replace_mqtt(self, mqtt: MammotionCloud) -> None:
91
97
  device = self._cloud_device.device
92
98
  self._cloud_device = MammotionBaseCloudDevice(mqtt, cloud_device=device, state_manager=self._state_manager)
@@ -98,7 +104,7 @@ class MammotionMixedDeviceManager:
98
104
  return self._ble_device is not None
99
105
 
100
106
 
101
- class MammotionDevices:
107
+ class MammotionDeviceManager:
102
108
  devices: dict[str, MammotionMixedDeviceManager] = {}
103
109
 
104
110
  def add_device(self, mammotion_device: MammotionMixedDeviceManager) -> None:
@@ -151,10 +157,10 @@ async def create_devices(
151
157
  class Mammotion:
152
158
  """Represents a Mammotion account and its devices."""
153
159
 
154
- devices = MammotionDevices()
160
+ device_manager = MammotionDeviceManager()
155
161
  mqtt_list: dict[str, MammotionCloud] = dict()
156
162
 
157
- _instance: Mammotion = None
163
+ _instance: Mammotion | None = None
158
164
 
159
165
  def __new__(cls, *args: Any, **kwargs: Any):
160
166
  if not cls._instance:
@@ -169,7 +175,7 @@ class Mammotion:
169
175
  self, ble_device: BLEDevice, preference: ConnectionPreference = ConnectionPreference.BLUETOOTH
170
176
  ) -> None:
171
177
  if ble_device:
172
- self.devices.add_device(
178
+ self.device_manager.add_device(
173
179
  MammotionMixedDeviceManager(name=ble_device.name, ble_device=ble_device, preference=preference)
174
180
  )
175
181
 
@@ -205,9 +211,9 @@ class Mammotion:
205
211
 
206
212
  def add_cloud_devices(self, mqtt_client: MammotionCloud) -> None:
207
213
  for device in mqtt_client.cloud_client.devices_by_account_response.data.data:
208
- mower_device = self.devices.get_device(device.deviceName)
214
+ mower_device = self.device_manager.get_device(device.deviceName)
209
215
  if device.deviceName.startswith(("Luba-", "Yuka-")) and mower_device is None:
210
- self.devices.add_device(
216
+ self.device_manager.add_device(
211
217
  MammotionMixedDeviceManager(
212
218
  name=device.deviceName,
213
219
  cloud_device=device,
@@ -222,7 +228,7 @@ class Mammotion:
222
228
  mower_device.replace_mqtt(mqtt_client)
223
229
 
224
230
  def set_disconnect_strategy(self, disconnect: bool) -> None:
225
- for device_name, device in self.devices.devices.items():
231
+ for device_name, device in self.device_manager.devices.items():
226
232
  if device.ble() is not None:
227
233
  ble_device: MammotionBaseBLEDevice = device.ble()
228
234
  ble_device.set_disconnect_strategy(disconnect)
@@ -230,7 +236,8 @@ class Mammotion:
230
236
  async def login(self, account: str, password: str) -> CloudIOTGateway:
231
237
  """Login to mammotion cloud."""
232
238
  cloud_client = CloudIOTGateway()
233
- mammotion_http = await connect_http(account, password)
239
+ mammotion_http = MammotionHTTP()
240
+ await mammotion_http.login(account, password)
234
241
  country_code = mammotion_http.login_info.userInformation.domainAbbreviation
235
242
  _LOGGER.debug("CountryCode: " + country_code)
236
243
  _LOGGER.debug("AuthCode: " + mammotion_http.login_info.authorization_code)
@@ -248,10 +255,10 @@ class Mammotion:
248
255
  return cloud_client
249
256
 
250
257
  async def remove_device(self, name: str) -> None:
251
- await self.devices.remove_device(name)
258
+ await self.device_manager.remove_device(name)
252
259
 
253
260
  def get_device_by_name(self, name: str) -> MammotionMixedDeviceManager:
254
- return self.devices.get_device(name)
261
+ return self.device_manager.get_device(name)
255
262
 
256
263
  async def send_command(self, name: str, key: str):
257
264
  """Send a command to the device."""
@@ -295,7 +302,9 @@ class Mammotion:
295
302
  device = self.get_device_by_name(name)
296
303
  if device.preference is ConnectionPreference.WIFI:
297
304
  if device.has_cloud():
298
- _stream_response = await device.cloud().mqtt.cloud_client.get_stream_subscription(device.cloud().iot_id)
305
+ _stream_response = await device.cloud().mqtt.cloud_client.mammotion_http.get_stream_subscription(
306
+ device.cloud().iot_id
307
+ )
299
308
  _LOGGER.debug(_stream_response)
300
309
  return _stream_response
301
310
 
@@ -1,4 +1,5 @@
1
1
  import asyncio
2
+ from collections.abc import Awaitable, Callable
2
3
  import logging
3
4
  from typing import Any, cast
4
5
  from uuid import UUID
@@ -17,8 +18,7 @@ from pymammotion.bluetooth import BleMessage
17
18
  from pymammotion.data.state_manager import StateManager
18
19
  from pymammotion.mammotion.commands.mammotion_command import MammotionCommand
19
20
  from pymammotion.mammotion.devices.base import MammotionBaseDevice
20
- from pymammotion.proto import has_field
21
- from pymammotion.proto.luba_msg import LubaMsg
21
+ from pymammotion.proto import LubaMsg, has_field
22
22
 
23
23
  DBUS_ERROR_BACKOFF_TIME = 0.25
24
24
 
@@ -89,9 +89,17 @@ class MammotionBaseBLEDevice(MammotionBaseDevice):
89
89
  self._operation_lock = asyncio.Lock()
90
90
  self._key: str | None = None
91
91
  self.set_queue_callback(self.queue_command)
92
+ self._state_manager.ble_gethash_ack_callback = self.datahash_response
93
+ self._state_manager.ble_get_commondata_ack_callback = self.commdata_response
92
94
  loop = asyncio.get_event_loop()
93
95
  loop.create_task(self.process_queue())
94
96
 
97
+ def set_notification_callback(self, func: Callable[[tuple[str, Any | None]], Awaitable[None]]) -> None:
98
+ self._state_manager.ble_on_notification_callback = func
99
+
100
+ def set_queue_callback(self, func: Callable[[str, dict[str, Any]], Awaitable[bytes]]) -> None:
101
+ self._state_manager.ble_queue_command_callback = func
102
+
95
103
  def update_device(self, device: BLEDevice) -> None:
96
104
  """Update the BLE device."""
97
105
  self.ble_device = device
@@ -325,7 +333,7 @@ class MammotionBaseBLEDevice(MammotionBaseDevice):
325
333
  _LOGGER.exception("Error parsing message %s", data)
326
334
  data = b""
327
335
  finally:
328
- self._message.clearNotification()
336
+ self._message.clear_notification()
329
337
 
330
338
  _LOGGER.debug("%s: Received notification: %s", self.name, data)
331
339
  else:
@@ -338,12 +346,15 @@ class MammotionBaseBLEDevice(MammotionBaseDevice):
338
346
 
339
347
  return
340
348
 
349
+ await self._state_manager.notification(new_msg)
341
350
  # may or may not be correct, some work could be done here to correctly match responses
342
351
  if self._notify_future and not self._notify_future.done():
343
352
  self._notify_future.set_result(data)
344
353
 
354
+ if self._execute_timed_disconnect is None:
355
+ await self._execute_forced_disconnect()
356
+
345
357
  self._reset_disconnect_timer()
346
- await self._state_manager.notification(new_msg)
347
358
 
348
359
  async def _start_notify(self) -> None:
349
360
  """Start notification."""
@@ -1,10 +1,11 @@
1
1
  import asyncio
2
+ from asyncio import TimerHandle
2
3
  import base64
4
+ from collections import deque
5
+ from collections.abc import Awaitable, Callable
3
6
  import json
4
7
  import logging
5
- from asyncio import TimerHandle
6
- from collections import deque
7
- from typing import Any, Awaitable, Callable, Optional, cast
8
+ from typing import Any, cast
8
9
 
9
10
  import betterproto
10
11
 
@@ -13,13 +14,13 @@ from pymammotion.aliyun.cloud_gateway import DeviceOfflineException
13
14
  from pymammotion.aliyun.model.dev_by_account_response import Device
14
15
  from pymammotion.data.mqtt.event import ThingEventMessage
15
16
  from pymammotion.data.mqtt.properties import ThingPropertiesMessage
17
+ from pymammotion.data.mqtt.status import ThingStatusMessage
16
18
  from pymammotion.data.state_manager import StateManager
17
19
  from pymammotion.event.event import DataEvent
18
20
  from pymammotion.mammotion.commands.mammotion_command import MammotionCommand
19
21
  from pymammotion.mammotion.devices.base import MammotionBaseDevice
20
22
  from pymammotion.mqtt.mammotion_future import MammotionFuture
21
- from pymammotion.proto import has_field
22
- from pymammotion.proto.luba_msg import LubaMsg
23
+ from pymammotion.proto import LubaMsg, has_field
23
24
 
24
25
  _LOGGER = logging.getLogger(__name__)
25
26
 
@@ -35,6 +36,7 @@ class MammotionCloud:
35
36
  self._waiting_queue = deque()
36
37
  self.mqtt_message_event = DataEvent()
37
38
  self.mqtt_properties_event = DataEvent()
39
+ self.mqtt_status_event = DataEvent()
38
40
  self.on_ready_event = DataEvent()
39
41
  self.on_disconnected_event = DataEvent()
40
42
  self.on_connected_event = DataEvent()
@@ -116,7 +118,7 @@ class MammotionCloud:
116
118
  json_str = json.dumps(payload)
117
119
  payload = json.loads(json_str)
118
120
 
119
- await self._handle_mqtt_message(topic, payload)
121
+ await self._parse_mqtt_response(topic, payload)
120
122
 
121
123
  async def _parse_mqtt_response(self, topic: str, payload: dict) -> None:
122
124
  """Parse the MQTT response."""
@@ -134,10 +136,9 @@ class MammotionCloud:
134
136
  if event.method == "thing.properties":
135
137
  await self.mqtt_properties_event.data_event(event)
136
138
  _LOGGER.debug(event)
137
-
138
- async def _handle_mqtt_message(self, topic: str, payload: dict) -> None:
139
- """Async handler for incoming MQTT messages."""
140
- await self._parse_mqtt_response(topic=topic, payload=payload)
139
+ elif topic.endswith("/app/down/thing/status"):
140
+ status = ThingStatusMessage.from_dict(payload)
141
+ await self.mqtt_status_event.data_event(status)
141
142
 
142
143
  def _disconnect(self) -> None:
143
144
  """Disconnect the MQTT client."""
@@ -156,7 +157,7 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
156
157
  super().__init__(state_manager, cloud_device)
157
158
  self._ble_sync_task: TimerHandle | None = None
158
159
  self.stopped = False
159
- self.on_ready_callback: Optional[Callable[[], Awaitable[None]]] = None
160
+ self.on_ready_callback: Callable[[], Awaitable[None]] | None = None
160
161
  self.loop = asyncio.get_event_loop()
161
162
  self._mqtt = mqtt
162
163
  self.iot_id = cloud_device.iotId
@@ -166,9 +167,12 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
166
167
  self.currentID = ""
167
168
  self._mqtt.mqtt_message_event.add_subscribers(self._parse_message_for_device)
168
169
  self._mqtt.mqtt_properties_event.add_subscribers(self._parse_message_properties_for_device)
170
+ self._mqtt.mqtt_status_event.add_subscribers(self._parse_message_status_for_device)
169
171
  self._mqtt.on_ready_event.add_subscribers(self.on_ready)
170
172
  self._mqtt.on_disconnected_event.add_subscribers(self.on_disconnect)
171
173
  self._mqtt.on_connected_event.add_subscribers(self.on_connect)
174
+ self._state_manager.cloud_gethash_ack_callback = self.datahash_response
175
+ self._state_manager.cloud_get_commondata_ack_callback = self.commdata_response
172
176
  self.set_queue_callback(self.queue_command)
173
177
 
174
178
  if self._mqtt.is_ready:
@@ -179,9 +183,17 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
179
183
  self._mqtt.on_disconnected_event.remove_subscribers(self.on_disconnect)
180
184
  self._mqtt.on_connected_event.remove_subscribers(self.on_connect)
181
185
  self._mqtt.mqtt_message_event.remove_subscribers(self._parse_message_for_device)
186
+ self._state_manager.cloud_gethash_ack_callback = None
187
+ self._state_manager.cloud_get_commondata_ack_callback = None
182
188
  if self._ble_sync_task:
183
189
  self._ble_sync_task.cancel()
184
190
 
191
+ def set_notification_callback(self, func: Callable[[tuple[str, Any | None]], Awaitable[None]]) -> None:
192
+ self._state_manager.cloud_on_notification_callback = func
193
+
194
+ def set_queue_callback(self, func: Callable[[str, dict[str, Any]], Awaitable[bytes]]) -> None:
195
+ self._state_manager.cloud_queue_command_callback = func
196
+
185
197
  async def on_ready(self) -> None:
186
198
  """Callback for when MQTT is subscribed to events."""
187
199
  if self.stopped:
@@ -278,6 +290,11 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
278
290
  return
279
291
  self.state_manager.properties(event)
280
292
 
293
+ async def _parse_message_status_for_device(self, status: ThingStatusMessage) -> None:
294
+ if status.params.iotId != self.iot_id:
295
+ return
296
+ self.state_manager.status(status)
297
+
281
298
  async def _parse_message_for_device(self, event: ThingEventMessage) -> None:
282
299
  _LOGGER.debug("_parse_message_for_device")
283
300
  params = event.params
@@ -301,6 +318,8 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
301
318
  if new_msg.net.todev_ble_sync != 0 or has_field(new_msg.net.toapp_wifi_iot_status):
302
319
  return
303
320
 
321
+ await self._state_manager.notification(new_msg)
322
+
304
323
  if len(self._mqtt.waiting_queue) > 0:
305
324
  fut: MammotionFuture = self.dequeue_by_iot_id(self._mqtt.waiting_queue, self.iot_id)
306
325
  if fut is None:
@@ -309,7 +328,6 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
309
328
  fut = self.dequeue_by_iot_id(self._mqtt.waiting_queue, self.iot_id)
310
329
  if not fut.fut.cancelled():
311
330
  fut.resolve(cast(bytes, binary_data))
312
- await self._state_manager.notification(new_msg)
313
331
 
314
332
  @property
315
333
  def mqtt(self):