pymammotion 0.4.0a2__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 (133) hide show
  1. pymammotion/__init__.py +5 -4
  2. pymammotion/aliyun/client.py +235 -0
  3. pymammotion/aliyun/cloud_gateway.py +312 -64
  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 +7 -9
  13. pymammotion/bluetooth/ble_message.py +10 -14
  14. pymammotion/const.py +3 -0
  15. pymammotion/data/model/__init__.py +1 -2
  16. pymammotion/data/model/device.py +95 -27
  17. pymammotion/data/model/device_config.py +4 -4
  18. pymammotion/data/model/device_info.py +35 -0
  19. pymammotion/data/model/device_limits.py +10 -10
  20. pymammotion/data/model/enums.py +12 -2
  21. pymammotion/data/model/errors.py +12 -0
  22. pymammotion/data/model/events.py +14 -0
  23. pymammotion/data/model/generate_geojson.py +521 -0
  24. pymammotion/data/model/generate_route_information.py +2 -2
  25. pymammotion/data/model/hash_list.py +370 -57
  26. pymammotion/data/model/location.py +4 -4
  27. pymammotion/data/model/mowing_modes.py +17 -1
  28. pymammotion/data/model/raw_data.py +2 -10
  29. pymammotion/data/model/region_data.py +10 -11
  30. pymammotion/data/model/report_info.py +31 -5
  31. pymammotion/data/model/work.py +27 -0
  32. pymammotion/data/mower_state_manager.py +316 -0
  33. pymammotion/data/mqtt/event.py +73 -28
  34. pymammotion/data/mqtt/mammotion_properties.py +257 -0
  35. pymammotion/data/mqtt/properties.py +93 -78
  36. pymammotion/data/mqtt/status.py +18 -17
  37. pymammotion/event/event.py +27 -6
  38. pymammotion/homeassistant/__init__.py +3 -0
  39. pymammotion/homeassistant/mower_api.py +484 -0
  40. pymammotion/homeassistant/rtk_api.py +54 -0
  41. pymammotion/http/encryption.py +5 -6
  42. pymammotion/http/http.py +574 -28
  43. pymammotion/http/model/__init__.py +0 -0
  44. pymammotion/{aliyun/model/stream_subscription_response.py → http/model/camera_stream.py} +14 -2
  45. pymammotion/http/model/http.py +129 -4
  46. pymammotion/http/model/response_factory.py +61 -0
  47. pymammotion/http/model/rtk.py +16 -0
  48. pymammotion/mammotion/commands/abstract_message.py +7 -5
  49. pymammotion/mammotion/commands/mammotion_command.py +30 -1
  50. pymammotion/mammotion/commands/messages/basestation.py +43 -0
  51. pymammotion/mammotion/commands/messages/driver.py +61 -29
  52. pymammotion/mammotion/commands/messages/media.py +68 -15
  53. pymammotion/mammotion/commands/messages/navigation.py +61 -25
  54. pymammotion/mammotion/commands/messages/network.py +17 -23
  55. pymammotion/mammotion/commands/messages/ota.py +18 -18
  56. pymammotion/mammotion/commands/messages/system.py +32 -49
  57. pymammotion/mammotion/commands/messages/video.py +15 -16
  58. pymammotion/mammotion/devices/__init__.py +27 -3
  59. pymammotion/mammotion/devices/base.py +40 -131
  60. pymammotion/mammotion/devices/mammotion.py +436 -201
  61. pymammotion/mammotion/devices/mammotion_bluetooth.py +57 -47
  62. pymammotion/mammotion/devices/mammotion_cloud.py +134 -105
  63. pymammotion/mammotion/devices/mammotion_mower_ble.py +49 -0
  64. pymammotion/mammotion/devices/mammotion_mower_cloud.py +39 -0
  65. pymammotion/mammotion/devices/managers/managers.py +81 -0
  66. pymammotion/mammotion/devices/mower_device.py +124 -0
  67. pymammotion/mammotion/devices/mower_manager.py +107 -0
  68. pymammotion/mammotion/devices/rtk_ble.py +89 -0
  69. pymammotion/mammotion/devices/rtk_cloud.py +113 -0
  70. pymammotion/mammotion/devices/rtk_device.py +50 -0
  71. pymammotion/mammotion/devices/rtk_manager.py +122 -0
  72. pymammotion/mqtt/__init__.py +2 -1
  73. pymammotion/mqtt/aliyun_mqtt.py +232 -0
  74. pymammotion/mqtt/linkkit/__init__.py +5 -0
  75. pymammotion/mqtt/linkkit/h2client.py +585 -0
  76. pymammotion/mqtt/linkkit/linkkit.py +3023 -0
  77. pymammotion/mqtt/mammotion_mqtt.py +176 -169
  78. pymammotion/mqtt/mqtt_models.py +66 -0
  79. pymammotion/proto/__init__.py +4839 -4
  80. pymammotion/proto/basestation.proto +8 -0
  81. pymammotion/proto/basestation_pb2.py +11 -9
  82. pymammotion/proto/basestation_pb2.pyi +16 -2
  83. pymammotion/proto/dev_net.proto +79 -55
  84. pymammotion/proto/dev_net_pb2.py +60 -56
  85. pymammotion/proto/dev_net_pb2.pyi +49 -6
  86. pymammotion/proto/luba_msg.proto +2 -1
  87. pymammotion/proto/luba_msg_pb2.py +6 -6
  88. pymammotion/proto/luba_msg_pb2.pyi +1 -0
  89. pymammotion/proto/luba_mul.proto +62 -1
  90. pymammotion/proto/luba_mul_pb2.py +38 -22
  91. pymammotion/proto/luba_mul_pb2.pyi +94 -7
  92. pymammotion/proto/mctrl_driver.proto +44 -4
  93. pymammotion/proto/mctrl_driver_pb2.py +26 -14
  94. pymammotion/proto/mctrl_driver_pb2.pyi +66 -11
  95. pymammotion/proto/mctrl_nav.proto +93 -52
  96. pymammotion/proto/mctrl_nav_pb2.py +75 -67
  97. pymammotion/proto/mctrl_nav_pb2.pyi +142 -56
  98. pymammotion/proto/mctrl_ota.proto +40 -2
  99. pymammotion/proto/mctrl_ota_pb2.py +23 -13
  100. pymammotion/proto/mctrl_ota_pb2.pyi +67 -4
  101. pymammotion/proto/mctrl_pept.proto +8 -3
  102. pymammotion/proto/mctrl_pept_pb2.py +8 -6
  103. pymammotion/proto/mctrl_pept_pb2.pyi +14 -6
  104. pymammotion/proto/mctrl_sys.proto +325 -86
  105. pymammotion/proto/mctrl_sys_pb2.py +162 -98
  106. pymammotion/proto/mctrl_sys_pb2.pyi +451 -25
  107. pymammotion/proto/message_pool.py +3 -0
  108. pymammotion/proto/py.typed +0 -0
  109. pymammotion/utility/constant/device_constant.py +29 -5
  110. pymammotion/utility/datatype_converter.py +13 -12
  111. pymammotion/utility/device_config.py +522 -130
  112. pymammotion/utility/device_type.py +218 -21
  113. pymammotion/utility/map.py +238 -51
  114. pymammotion/utility/mur_mur_hash.py +159 -0
  115. {pymammotion-0.4.0a2.dist-info → pymammotion-0.5.51.dist-info}/METADATA +26 -31
  116. pymammotion-0.5.51.dist-info/RECORD +152 -0
  117. {pymammotion-0.4.0a2.dist-info → pymammotion-0.5.51.dist-info}/WHEEL +1 -1
  118. pymammotion/aliyun/cloud_service.py +0 -65
  119. pymammotion/data/model/plan.py +0 -58
  120. pymammotion/data/state_manager.py +0 -129
  121. pymammotion/proto/basestation.py +0 -59
  122. pymammotion/proto/common.py +0 -12
  123. pymammotion/proto/dev_net.py +0 -381
  124. pymammotion/proto/luba_msg.py +0 -81
  125. pymammotion/proto/luba_mul.py +0 -76
  126. pymammotion/proto/mctrl_driver.py +0 -100
  127. pymammotion/proto/mctrl_nav.py +0 -664
  128. pymammotion/proto/mctrl_ota.py +0 -48
  129. pymammotion/proto/mctrl_pept.py +0 -41
  130. pymammotion/proto/mctrl_sys.py +0 -574
  131. pymammotion-0.4.0a2.dist-info/RECORD +0 -131
  132. /pymammotion/http/{_init_.py → __init__.py} +0 -0
  133. {pymammotion-0.4.0a2.dist-info → pymammotion-0.5.51.dist-info/licenses}/LICENSE +0 -0
@@ -1,101 +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
10
  from pymammotion.data.model.raw_data import RawMowerData
12
- from pymammotion.data.state_manager import StateManager
13
- from pymammotion.proto.luba_msg import LubaMsg
14
- from pymammotion.proto.mctrl_nav import NavGetCommDataAck, NavGetHashListAck, SvgMessageAckT
11
+ from pymammotion.data.mower_state_manager import MowerStateManager
12
+ from pymammotion.proto import LubaMsg
15
13
 
16
14
  _LOGGER = logging.getLogger(__name__)
17
15
 
18
16
 
19
- def find_next_integer(lst: list[int], current_hash: int) -> int | None:
20
- try:
21
- # Find the index of the current integer
22
- current_index = lst.index(current_hash)
23
-
24
- # Check if there is a next integer in the list
25
- if current_index + 1 < len(lst):
26
- return lst[current_index + 1]
27
- else:
28
- return None # Or raise an exception or handle it in some other way
29
- except ValueError:
30
- # Handle the case where current_int is not in the list
31
- return None # Or raise an exception or handle it in some other way
32
-
33
-
34
- class MammotionBaseDevice:
17
+ class MammotionBaseDevice(ABC):
35
18
  """Base class for Mammotion devices."""
36
19
 
37
- def __init__(self, state_manager: StateManager, cloud_device: Device | None = None) -> None:
20
+ def __init__(self, state_manager: MowerStateManager, cloud_device: Device) -> None:
38
21
  """Initialize MammotionBaseDevice."""
39
22
  self.loop = asyncio.get_event_loop()
40
23
  self._state_manager = state_manager
41
24
  self._raw_data = dict()
42
25
  self._raw_mower_data: RawMowerData = RawMowerData()
43
- self._state_manager.gethash_ack_callback = self.datahash_response
44
- self._state_manager.get_commondata_ack_callback = self.commdata_response
45
26
  self._notify_future: asyncio.Future[bytes] | None = None
46
27
  self._cloud_device = cloud_device
47
28
 
48
- def set_notification_callback(self, func: Callable[[tuple[str, Any | None]], Awaitable[None]]) -> None:
49
- self._state_manager.on_notification_callback = func
50
-
51
- def set_queue_callback(self, func: Callable[[str, dict[str, Any]], Awaitable[bytes]]) -> None:
52
- self._state_manager.queue_command_callback = func
53
-
54
- async def datahash_response(self, hash_ack: NavGetHashListAck) -> None:
55
- """Handle datahash responses."""
56
- current_frame = hash_ack.current_frame
57
-
58
- missing_frames = self.mower.map.missing_hash_frame()
59
- if len(missing_frames) == 0:
60
- if len(self.mower.map.missing_hashlist) > 0:
61
- data_hash = self.mower.map.missing_hashlist.pop()
62
- return await self.queue_command("synchronize_hash_data", hash_num=data_hash)
63
- return
64
-
65
- if current_frame != missing_frames[0] - 1:
66
- current_frame = missing_frames[0] - 1
67
- await self.queue_command("get_hash_response", total_frame=hash_ack.total_frame, current_frame=current_frame)
68
-
69
- async def commdata_response(self, common_data: NavGetCommDataAck | SvgMessageAckT) -> None:
70
- """Handle common data responses."""
71
- total_frame = common_data.total_frame
72
- current_frame = common_data.current_frame
73
-
74
- missing_frames = self.mower.map.missing_frame(common_data)
75
- if len(missing_frames) == 0:
76
- # get next in hash ack list
77
-
78
- data_hash = self.mower.map.missing_hashlist.pop() if len(self.mower.map.missing_hashlist) > 0 else None
79
- if data_hash is None:
80
- return
81
-
82
- await self.queue_command("synchronize_hash_data", hash_num=data_hash)
83
- else:
84
- if current_frame != missing_frames[0] - 1:
85
- current_frame = missing_frames[0] - 1
86
-
87
- region_data = RegionData()
88
- region_data.hash = common_data.data_hash if isinstance(common_data, SvgMessageAckT) else common_data.hash
89
- region_data.action = common_data.action if isinstance(common_data, NavGetCommDataAck) else None
90
- region_data.type = common_data.type
91
- region_data.total_frame = total_frame
92
- region_data.current_frame = current_frame
93
- await self.queue_command("get_regional_data", regional_data=region_data)
94
-
95
29
  def _update_raw_data(self, data: bytes) -> None:
96
30
  """Update raw and model data from notifications."""
97
31
  tmp_msg = LubaMsg().parse(data)
98
- res = betterproto.which_one_of(tmp_msg, "LubaSubMsg")
32
+ res = betterproto2.which_one_of(tmp_msg, "LubaSubMsg")
99
33
  match res[0]:
100
34
  case "nav":
101
35
  self._update_nav_data(tmp_msg)
@@ -114,7 +48,7 @@ class MammotionBaseDevice:
114
48
 
115
49
  def _update_nav_data(self, tmp_msg) -> None:
116
50
  """Update navigation data."""
117
- nav_sub_msg = betterproto.which_one_of(tmp_msg.nav, "SubNavMsg")
51
+ nav_sub_msg = betterproto2.which_one_of(tmp_msg.nav, "SubNavMsg")
118
52
  if nav_sub_msg[1] is None:
119
53
  _LOGGER.debug("Sub message was NoneType %s", nav_sub_msg[0])
120
54
  return
@@ -122,32 +56,32 @@ class MammotionBaseDevice:
122
56
  if isinstance(nav_sub_msg[1], int):
123
57
  nav[nav_sub_msg[0]] = nav_sub_msg[1]
124
58
  else:
125
- 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)
126
60
  self._raw_data["nav"] = nav
127
61
 
128
- def _update_sys_data(self, tmp_msg) -> None:
62
+ def _update_sys_data(self, tmp_msg: LubaMsg) -> None:
129
63
  """Update system data."""
130
- sys_sub_msg = betterproto.which_one_of(tmp_msg.sys, "SubSysMsg")
64
+ sys_sub_msg = betterproto2.which_one_of(tmp_msg.sys, "SubSysMsg")
131
65
  if sys_sub_msg[1] is None:
132
66
  _LOGGER.debug("Sub message was NoneType %s", sys_sub_msg[0])
133
67
  return
134
68
  sys = self._raw_data.get("sys", {})
135
- 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)
136
70
  self._raw_data["sys"] = sys
137
71
 
138
- def _update_driver_data(self, tmp_msg) -> None:
72
+ def _update_driver_data(self, tmp_msg: LubaMsg) -> None:
139
73
  """Update driver data."""
140
- drv_sub_msg = betterproto.which_one_of(tmp_msg.driver, "SubDrvMsg")
74
+ drv_sub_msg = betterproto2.which_one_of(tmp_msg.driver, "SubDrvMsg")
141
75
  if drv_sub_msg[1] is None:
142
76
  _LOGGER.debug("Sub message was NoneType %s", drv_sub_msg[0])
143
77
  return
144
78
  drv = self._raw_data.get("driver", {})
145
- 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)
146
80
  self._raw_data["driver"] = drv
147
81
 
148
- def _update_net_data(self, tmp_msg) -> None:
82
+ def _update_net_data(self, tmp_msg: LubaMsg) -> None:
149
83
  """Update network data."""
150
- net_sub_msg = betterproto.which_one_of(tmp_msg.net, "NetSubType")
84
+ net_sub_msg = betterproto2.which_one_of(tmp_msg.net, "NetSubType")
151
85
  if net_sub_msg[1] is None:
152
86
  _LOGGER.debug("Sub message was NoneType %s", net_sub_msg[0])
153
87
  return
@@ -155,27 +89,27 @@ class MammotionBaseDevice:
155
89
  if isinstance(net_sub_msg[1], int):
156
90
  net[net_sub_msg[0]] = net_sub_msg[1]
157
91
  else:
158
- 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)
159
93
  self._raw_data["net"] = net
160
94
 
161
- def _update_mul_data(self, tmp_msg) -> None:
95
+ def _update_mul_data(self, tmp_msg: LubaMsg) -> None:
162
96
  """Update mul data."""
163
- mul_sub_msg = betterproto.which_one_of(tmp_msg.mul, "SubMul")
97
+ mul_sub_msg = betterproto2.which_one_of(tmp_msg.mul, "SubMul")
164
98
  if mul_sub_msg[1] is None:
165
99
  _LOGGER.debug("Sub message was NoneType %s", mul_sub_msg[0])
166
100
  return
167
101
  mul = self._raw_data.get("mul", {})
168
- 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)
169
103
  self._raw_data["mul"] = mul
170
104
 
171
- def _update_ota_data(self, tmp_msg) -> None:
105
+ def _update_ota_data(self, tmp_msg: LubaMsg) -> None:
172
106
  """Update OTA data."""
173
- ota_sub_msg = betterproto.which_one_of(tmp_msg.ota, "SubOtaMsg")
107
+ ota_sub_msg = betterproto2.which_one_of(tmp_msg.ota, "SubOtaMsg")
174
108
  if ota_sub_msg[1] is None:
175
109
  _LOGGER.debug("Sub message was NoneType %s", ota_sub_msg[0])
176
110
  return
177
111
  ota = self._raw_data.get("ota", {})
178
- 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)
179
113
  self._raw_data["ota"] = ota
180
114
 
181
115
  @property
@@ -189,52 +123,27 @@ class MammotionBaseDevice:
189
123
  return self._state_manager.get_device()
190
124
 
191
125
  @abstractmethod
192
- async def queue_command(self, key: str, **kwargs: any) -> bytes | None:
126
+ async def queue_command(self, key: str, **kwargs: Any) -> None:
193
127
  """Queue commands to mower."""
194
128
 
195
129
  @abstractmethod
196
- async def _ble_sync(self):
130
+ async def _ble_sync(self) -> None:
197
131
  """Send ble sync command every 3 seconds or sooner."""
198
132
 
199
133
  @abstractmethod
200
- def stop(self):
134
+ def stop(self) -> None:
201
135
  """Stop everything ready for destroying."""
202
136
 
203
- async def start_sync(self, retry: int) -> None:
204
- """Start synchronization with the device."""
205
- await self.queue_command("get_device_base_info")
206
- await self.queue_command("get_device_product_model")
207
- await self.queue_command("get_report_cfg")
208
- """RTK and dock location."""
209
- await self.queue_command("allpowerfull_rw", id=5, context=1, rw=1)
210
- await self.async_read_settings()
211
-
212
- async def start_map_sync(self) -> None:
213
- """Start sync of map data."""
214
-
215
- if self._cloud_device and len(self.mower.map.area_name) == 0:
216
- await self.queue_command("get_area_name_list", device_id=self._cloud_device.iotId)
217
-
218
- await self.queue_command("read_plan", sub_cmd=2, plan_index=0)
219
-
220
- await self.queue_command("get_all_boundary_hash_list", sub_cmd=0)
221
- await self.queue_command("get_hash_response", total_frame=1, current_frame=1)
222
- if len(self.mower.map.missing_hashlist) > 0:
223
- data_hash = self.mower.map.missing_hashlist.pop()
224
- await self.queue_command("synchronize_hash_data", hash_num=data_hash)
225
-
226
- # sub_cmd 3 is job hashes??
227
- # sub_cmd 4 is dump location (yuka)
228
- # jobs list
229
- # hash_list_result = await self._send_command_with_args("get_all_boundary_hash_list", sub_cmd=3)
230
-
231
137
  async def async_read_settings(self) -> None:
232
138
  """Read settings from device."""
233
- await self.queue_command("allpowerfull_rw", id=3, context=1, rw=0)
234
- await self.queue_command("allpowerfull_rw", id=4, context=1, rw=0)
235
- await self.queue_command("allpowerfull_rw", id=6, context=1, rw=0)
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)
236
145
  # traversal mode
237
- await self.queue_command("allpowerfull_rw", id=7, context=1, rw=0)
146
+ await self.queue_command("read_write_device", rw_id=7, context=1, rw=0)
238
147
 
239
148
  await self.queue_command("read_and_set_sidelight", is_sidelight=True, operate=1)
240
149
 
@@ -242,13 +151,13 @@ class MammotionBaseDevice:
242
151
 
243
152
  async def async_get_errors(self) -> None:
244
153
  """Error codes."""
245
- await self.queue_command("allpowerfull_rw", id=5, rw=1, context=2)
246
- 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)
247
156
 
248
- async def command(self, key: str, **kwargs):
157
+ async def command(self, key: str, **kwargs: Any) -> None:
249
158
  """Send a command to the device."""
250
- return await self.queue_command(key, **kwargs)
159
+ await self.queue_command(key, **kwargs)
251
160
 
252
161
  @property
253
- def state_manager(self):
162
+ def state_manager(self) -> MowerStateManager:
254
163
  return self._state_manager