pymammotion 0.5.34__py3-none-any.whl → 0.5.44__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 (54) hide show
  1. pymammotion/__init__.py +3 -3
  2. pymammotion/aliyun/cloud_gateway.py +106 -18
  3. pymammotion/aliyun/model/dev_by_account_response.py +198 -20
  4. pymammotion/const.py +3 -0
  5. pymammotion/data/model/device.py +1 -0
  6. pymammotion/data/model/device_config.py +1 -1
  7. pymammotion/data/model/enums.py +5 -3
  8. pymammotion/data/model/generate_route_information.py +2 -2
  9. pymammotion/data/model/hash_list.py +113 -33
  10. pymammotion/data/model/region_data.py +4 -4
  11. pymammotion/data/{state_manager.py → mower_state_manager.py} +17 -7
  12. pymammotion/data/mqtt/event.py +47 -22
  13. pymammotion/data/mqtt/mammotion_properties.py +257 -0
  14. pymammotion/data/mqtt/properties.py +32 -29
  15. pymammotion/data/mqtt/status.py +17 -16
  16. pymammotion/homeassistant/__init__.py +3 -0
  17. pymammotion/homeassistant/mower_api.py +446 -0
  18. pymammotion/homeassistant/rtk_api.py +54 -0
  19. pymammotion/http/http.py +387 -13
  20. pymammotion/http/model/http.py +82 -2
  21. pymammotion/http/model/response_factory.py +10 -4
  22. pymammotion/mammotion/commands/mammotion_command.py +6 -0
  23. pymammotion/mammotion/commands/messages/navigation.py +10 -6
  24. pymammotion/mammotion/devices/__init__.py +27 -3
  25. pymammotion/mammotion/devices/base.py +16 -138
  26. pymammotion/mammotion/devices/mammotion.py +364 -204
  27. pymammotion/mammotion/devices/mammotion_bluetooth.py +7 -5
  28. pymammotion/mammotion/devices/mammotion_cloud.py +42 -83
  29. pymammotion/mammotion/devices/mammotion_mower_ble.py +49 -0
  30. pymammotion/mammotion/devices/mammotion_mower_cloud.py +39 -0
  31. pymammotion/mammotion/devices/managers/managers.py +81 -0
  32. pymammotion/mammotion/devices/mower_device.py +121 -0
  33. pymammotion/mammotion/devices/mower_manager.py +107 -0
  34. pymammotion/mammotion/devices/rtk_ble.py +89 -0
  35. pymammotion/mammotion/devices/rtk_cloud.py +113 -0
  36. pymammotion/mammotion/devices/rtk_device.py +50 -0
  37. pymammotion/mammotion/devices/rtk_manager.py +122 -0
  38. pymammotion/mqtt/__init__.py +2 -1
  39. pymammotion/mqtt/aliyun_mqtt.py +232 -0
  40. pymammotion/mqtt/mammotion_mqtt.py +174 -192
  41. pymammotion/mqtt/mqtt_models.py +66 -0
  42. pymammotion/proto/__init__.py +1 -1
  43. pymammotion/proto/mctrl_nav.proto +1 -1
  44. pymammotion/proto/mctrl_nav_pb2.py +1 -1
  45. pymammotion/proto/mctrl_nav_pb2.pyi +4 -4
  46. pymammotion/proto/mctrl_sys.proto +1 -1
  47. pymammotion/utility/datatype_converter.py +13 -12
  48. pymammotion/utility/device_type.py +88 -3
  49. pymammotion/utility/mur_mur_hash.py +132 -87
  50. {pymammotion-0.5.34.dist-info → pymammotion-0.5.44.dist-info}/METADATA +25 -31
  51. {pymammotion-0.5.34.dist-info → pymammotion-0.5.44.dist-info}/RECORD +59 -45
  52. {pymammotion-0.5.34.dist-info → pymammotion-0.5.44.dist-info}/WHEEL +1 -1
  53. pymammotion/http/_init_.py +0 -0
  54. {pymammotion-0.5.34.dist-info → pymammotion-0.5.44.dist-info/licenses}/LICENSE +0 -0
@@ -33,7 +33,7 @@ logger = logging.getLogger(__name__)
33
33
 
34
34
 
35
35
  class MessageNavigation(AbstractMessage, ABC):
36
- def send_order_msg_nav(self, build) -> bytes:
36
+ def send_order_msg_nav(self, build: MctlNav) -> bytes:
37
37
  luba_msg = LubaMsg(
38
38
  msgtype=MsgCmdType.NAV,
39
39
  sender=MsgDevice.DEV_MOBILEAPP,
@@ -206,9 +206,9 @@ class MessageNavigation(AbstractMessage, ABC):
206
206
  reserved=plan_bean.reserved,
207
207
  weeks=plan_bean.weeks,
208
208
  start_date=plan_bean.start_date,
209
- trigger_type=plan_bean.job_type,
210
- day=plan_bean.interval_days,
211
- toward_included_angle=plan_bean.demond_angle,
209
+ trigger_type=plan_bean.trigger_type,
210
+ day=plan_bean.day,
211
+ toward_included_angle=plan_bean.toward_included_angle,
212
212
  toward_mode=0,
213
213
  )
214
214
  logger.debug(f"Send read job plan command planBean={plan_bean}")
@@ -262,7 +262,7 @@ class MessageNavigation(AbstractMessage, ABC):
262
262
  toapp_map_name_msg=NavMapNameMsg(
263
263
  hash=0,
264
264
  result=0,
265
- device_id=device_id, # iotId or ???
265
+ device_id=device_id, # iot_id
266
266
  rw=0,
267
267
  )
268
268
  )
@@ -390,7 +390,7 @@ class MessageNavigation(AbstractMessage, ABC):
390
390
  generate_route_information}")
391
391
  return self.send_order_msg_nav(MctlNav(bidire_reqconver_path=build))
392
392
 
393
- def modify_generate_route_information(self, generate_route_information: GenerateRouteInformation) -> bytes:
393
+ def modify_route_information(self, generate_route_information: GenerateRouteInformation) -> bytes:
394
394
  logger.debug(f"Generate route data source: {generate_route_information}")
395
395
  build = NavReqCoverPath(
396
396
  pver=1,
@@ -432,7 +432,11 @@ class MessageNavigation(AbstractMessage, ABC):
432
432
  return self.send_order_msg_nav(build)
433
433
 
434
434
  def get_line_info_list(self, hash_list: list[int], transaction_id: int) -> bytes:
435
+ """Get route information (mow path) corresponding to the specified hash list based on time.
436
+ e.g transaction_id = int(time.time() * 1000)
437
+ """
435
438
  logger.debug(f"Sending==========Get route command: {hash_list}")
439
+
436
440
  build = MctlNav(
437
441
  app_request_cover_paths=AppRequestCoverPathsT(
438
442
  pver=1, hash_list=hash_list, transaction_id=transaction_id, sub_cmd=0
@@ -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,4 +1,4 @@
1
- from abc import abstractmethod
1
+ from abc import ABC, abstractmethod
2
2
  import asyncio
3
3
  import logging
4
4
  from typing import Any
@@ -6,35 +6,18 @@ from typing import Any
6
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 import LubaMsg, NavGetCommDataAck, NavGetHashListAck, NavPlanJobSet, SvgMessageAckT
14
- from pymammotion.utility.device_type import DeviceType
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:
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
@@ -43,57 +26,6 @@ class MammotionBaseDevice:
43
26
  self._notify_future: asyncio.Future[bytes] | None = None
44
27
  self._cloud_device = cloud_device
45
28
 
46
- async def datahash_response(self, hash_ack: NavGetHashListAck) -> None:
47
- """Handle datahash responses for root level hashs."""
48
- current_frame = hash_ack.current_frame
49
-
50
- missing_frames = self.mower.map.missing_root_hash_frame(hash_ack)
51
- if len(missing_frames) == 0:
52
- if len(self.mower.map.missing_hashlist(hash_ack.sub_cmd)) > 0:
53
- data_hash = self.mower.map.missing_hashlist(hash_ack.sub_cmd).pop(0)
54
- await self.queue_command("synchronize_hash_data", hash_num=data_hash)
55
- return
56
-
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)
60
-
61
- async def commdata_response(self, common_data: NavGetCommDataAck | SvgMessageAckT) -> None:
62
- """Handle common data responses."""
63
- total_frame = common_data.total_frame
64
- current_frame = common_data.current_frame
65
-
66
- missing_frames = self.mower.map.missing_frame(common_data)
67
- if len(missing_frames) == 0:
68
- # get next in hash ack list
69
-
70
- data_hash = (
71
- self.mower.map.missing_hashlist(common_data.sub_cmd).pop(0)
72
- if len(self.mower.map.missing_hashlist(common_data.sub_cmd)) > 0
73
- else None
74
- )
75
- if data_hash is None:
76
- return
77
-
78
- await self.queue_command("synchronize_hash_data", hash_num=data_hash)
79
- else:
80
- if current_frame != missing_frames[0] - 1:
81
- current_frame = missing_frames[0] - 1
82
-
83
- region_data = RegionData()
84
- region_data.hash = common_data.data_hash if isinstance(common_data, SvgMessageAckT) else common_data.hash
85
- region_data.action = common_data.action if isinstance(common_data, NavGetCommDataAck) else None
86
- region_data.type = common_data.type
87
- region_data.sub_cmd = common_data.sub_cmd
88
- region_data.total_frame = total_frame
89
- region_data.current_frame = current_frame
90
- await self.queue_command("get_regional_data", regional_data=region_data)
91
-
92
- async def plan_callback(self, plan: NavPlanJobSet) -> None:
93
- if plan.plan_index < plan.total_plan_num - 1:
94
- index = plan.plan_index + 1
95
- await self.queue_command("read_plan", sub_cmd=2, plan_index=index)
96
-
97
29
  def _update_raw_data(self, data: bytes) -> None:
98
30
  """Update raw and model data from notifications."""
99
31
  tmp_msg = LubaMsg().parse(data)
@@ -127,7 +59,7 @@ class MammotionBaseDevice:
127
59
  nav[nav_sub_msg[0]] = nav_sub_msg[1].to_dict(casing=betterproto2.Casing.SNAKE)
128
60
  self._raw_data["nav"] = nav
129
61
 
130
- def _update_sys_data(self, tmp_msg) -> None:
62
+ def _update_sys_data(self, tmp_msg: LubaMsg) -> None:
131
63
  """Update system data."""
132
64
  sys_sub_msg = betterproto2.which_one_of(tmp_msg.sys, "SubSysMsg")
133
65
  if sys_sub_msg[1] is None:
@@ -137,7 +69,7 @@ class MammotionBaseDevice:
137
69
  sys[sys_sub_msg[0]] = sys_sub_msg[1].to_dict(casing=betterproto2.Casing.SNAKE)
138
70
  self._raw_data["sys"] = sys
139
71
 
140
- def _update_driver_data(self, tmp_msg) -> None:
72
+ def _update_driver_data(self, tmp_msg: LubaMsg) -> None:
141
73
  """Update driver data."""
142
74
  drv_sub_msg = betterproto2.which_one_of(tmp_msg.driver, "SubDrvMsg")
143
75
  if drv_sub_msg[1] is None:
@@ -147,7 +79,7 @@ class MammotionBaseDevice:
147
79
  drv[drv_sub_msg[0]] = drv_sub_msg[1].to_dict(casing=betterproto2.Casing.SNAKE)
148
80
  self._raw_data["driver"] = drv
149
81
 
150
- def _update_net_data(self, tmp_msg) -> None:
82
+ def _update_net_data(self, tmp_msg: LubaMsg) -> None:
151
83
  """Update network data."""
152
84
  net_sub_msg = betterproto2.which_one_of(tmp_msg.net, "NetSubType")
153
85
  if net_sub_msg[1] is None:
@@ -160,7 +92,7 @@ class MammotionBaseDevice:
160
92
  net[net_sub_msg[0]] = net_sub_msg[1].to_dict(casing=betterproto2.Casing.SNAKE)
161
93
  self._raw_data["net"] = net
162
94
 
163
- def _update_mul_data(self, tmp_msg) -> None:
95
+ def _update_mul_data(self, tmp_msg: LubaMsg) -> None:
164
96
  """Update mul data."""
165
97
  mul_sub_msg = betterproto2.which_one_of(tmp_msg.mul, "SubMul")
166
98
  if mul_sub_msg[1] is None:
@@ -170,7 +102,7 @@ class MammotionBaseDevice:
170
102
  mul[mul_sub_msg[0]] = mul_sub_msg[1].to_dict(casing=betterproto2.Casing.SNAKE)
171
103
  self._raw_data["mul"] = mul
172
104
 
173
- def _update_ota_data(self, tmp_msg) -> None:
105
+ def _update_ota_data(self, tmp_msg: LubaMsg) -> None:
174
106
  """Update OTA data."""
175
107
  ota_sub_msg = betterproto2.which_one_of(tmp_msg.ota, "SubOtaMsg")
176
108
  if ota_sub_msg[1] is None:
@@ -191,71 +123,17 @@ class MammotionBaseDevice:
191
123
  return self._state_manager.get_device()
192
124
 
193
125
  @abstractmethod
194
- async def queue_command(self, key: str, **kwargs: any) -> bytes | None:
126
+ async def queue_command(self, key: str, **kwargs: Any) -> None:
195
127
  """Queue commands to mower."""
196
128
 
197
129
  @abstractmethod
198
- async def _ble_sync(self):
130
+ async def _ble_sync(self) -> None:
199
131
  """Send ble sync command every 3 seconds or sooner."""
200
132
 
201
133
  @abstractmethod
202
- def stop(self):
134
+ def stop(self) -> None:
203
135
  """Stop everything ready for destroying."""
204
136
 
205
- async def start_sync(self, retry: int) -> None:
206
- """Start synchronization with the device."""
207
- await self.queue_command("get_device_base_info")
208
- await self.queue_command("get_device_product_model")
209
- await self.queue_command("get_report_cfg")
210
- """RTK and dock location."""
211
- await self.queue_command("read_write_device", rw_id=5, context=1, rw=1)
212
- await self.async_read_settings()
213
-
214
- async def start_map_sync(self) -> None:
215
- """Start sync of map data."""
216
-
217
- self.mower.map.update_hash_lists(self.mower.map.hashlist)
218
-
219
- await self.queue_command("send_todev_ble_sync", sync_type=3)
220
-
221
- if self._cloud_device and len(self.mower.map.area_name) == 0 and not DeviceType.is_luba1(self.mower.name):
222
- await self.queue_command("get_area_name_list", device_id=self._cloud_device.iotId)
223
-
224
- if len(self.mower.map.plan) == 0 or list(self.mower.map.plan.values())[0].total_plan_num != len(
225
- self.mower.map.plan
226
- ):
227
- await self.queue_command("read_plan", sub_cmd=2, plan_index=0)
228
-
229
- for hash_id, frame in list(self.mower.map.area.items()):
230
- missing_frames = self.mower.map.find_missing_frames(frame)
231
- if len(missing_frames) > 0:
232
- del self.mower.map.area[hash_id]
233
-
234
- for hash_id, frame in list(self.mower.map.path.items()):
235
- missing_frames = self.mower.map.find_missing_frames(frame)
236
- if len(missing_frames) > 0:
237
- del self.mower.map.path[hash_id]
238
-
239
- for hash_id, frame in list(self.mower.map.obstacle.items()):
240
- missing_frames = self.mower.map.find_missing_frames(frame)
241
- if len(missing_frames) > 0:
242
- del self.mower.map.obstacle[hash_id]
243
-
244
- # don't know why but total frame on svg is wrong
245
- # for hash, frame in self.mower.map.svg.items():
246
- # missing_frames = self.mower.map.find_missing_frames(frame)
247
- # if len(missing_frames) > 0:
248
- # del self.mower.map.svg[hash]
249
-
250
- if len(self.mower.map.root_hash_lists) == 0 or len(self.mower.map.missing_hashlist()) > 0:
251
- await self.queue_command("get_all_boundary_hash_list", sub_cmd=0)
252
-
253
- # sub_cmd 3 is job hashes??
254
- # sub_cmd 4 is dump location (yuka)
255
- # jobs list
256
- #
257
- # await self.queue_command("get_all_boundary_hash_list", sub_cmd=3)
258
-
259
137
  async def async_read_settings(self) -> None:
260
138
  """Read settings from device."""
261
139
  # no cutting in rain nav_sys_param_cmd (id 3 context 1/0)
@@ -276,10 +154,10 @@ class MammotionBaseDevice:
276
154
  await self.queue_command("read_write_device", rw_id=5, rw=1, context=2)
277
155
  await self.queue_command("read_write_device", rw_id=5, rw=1, context=3)
278
156
 
279
- async def command(self, key: str, **kwargs):
157
+ async def command(self, key: str, **kwargs: Any) -> None:
280
158
  """Send a command to the device."""
281
- return await self.queue_command(key, **kwargs)
159
+ await self.queue_command(key, **kwargs)
282
160
 
283
161
  @property
284
- def state_manager(self):
162
+ def state_manager(self) -> MowerStateManager:
285
163
  return self._state_manager