pymammotion 0.2.28__py3-none-any.whl → 0.2.30__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 (55) hide show
  1. pymammotion/__init__.py +12 -9
  2. pymammotion/aliyun/cloud_gateway.py +57 -39
  3. pymammotion/aliyun/cloud_service.py +3 -3
  4. pymammotion/aliyun/dataclass/dev_by_account_response.py +1 -2
  5. pymammotion/aliyun/dataclass/session_by_authcode_response.py +1 -0
  6. pymammotion/bluetooth/ble.py +6 -6
  7. pymammotion/bluetooth/ble_message.py +30 -16
  8. pymammotion/bluetooth/data/convert.py +1 -1
  9. pymammotion/bluetooth/data/framectrldata.py +1 -1
  10. pymammotion/bluetooth/data/notifydata.py +6 -6
  11. pymammotion/const.py +1 -0
  12. pymammotion/data/model/__init__.py +2 -0
  13. pymammotion/data/model/account.py +1 -1
  14. pymammotion/data/model/device.py +31 -24
  15. pymammotion/data/model/device_config.py +71 -0
  16. pymammotion/data/model/enums.py +4 -4
  17. pymammotion/data/model/excute_boarder_params.py +5 -5
  18. pymammotion/data/model/execute_boarder.py +4 -4
  19. pymammotion/data/model/generate_route_information.py +18 -124
  20. pymammotion/data/model/hash_list.py +4 -7
  21. pymammotion/data/model/location.py +3 -3
  22. pymammotion/data/model/mowing_modes.py +1 -1
  23. pymammotion/data/model/plan.py +4 -4
  24. pymammotion/data/model/region_data.py +4 -4
  25. pymammotion/data/model/report_info.py +1 -1
  26. pymammotion/data/mqtt/event.py +8 -3
  27. pymammotion/data/state_manager.py +13 -12
  28. pymammotion/event/event.py +14 -14
  29. pymammotion/http/http.py +33 -45
  30. pymammotion/http/model/http.py +75 -0
  31. pymammotion/mammotion/commands/messages/driver.py +20 -23
  32. pymammotion/mammotion/commands/messages/navigation.py +47 -48
  33. pymammotion/mammotion/commands/messages/network.py +17 -35
  34. pymammotion/mammotion/commands/messages/system.py +6 -7
  35. pymammotion/mammotion/control/joystick.py +11 -10
  36. pymammotion/mammotion/devices/__init__.py +2 -2
  37. pymammotion/mammotion/devices/base.py +248 -0
  38. pymammotion/mammotion/devices/mammotion.py +52 -1042
  39. pymammotion/mammotion/devices/mammotion_bluetooth.py +447 -0
  40. pymammotion/mammotion/devices/mammotion_cloud.py +244 -0
  41. pymammotion/mqtt/mammotion_future.py +3 -2
  42. pymammotion/mqtt/mammotion_mqtt.py +23 -23
  43. pymammotion/proto/__init__.py +6 -0
  44. pymammotion/utility/constant/__init__.py +3 -1
  45. pymammotion/utility/conversions.py +1 -1
  46. pymammotion/utility/datatype_converter.py +9 -9
  47. pymammotion/utility/device_type.py +47 -18
  48. pymammotion/utility/map.py +2 -2
  49. pymammotion/utility/movement.py +2 -1
  50. pymammotion/utility/periodic.py +5 -5
  51. pymammotion/utility/rocker_util.py +1 -1
  52. {pymammotion-0.2.28.dist-info → pymammotion-0.2.30.dist-info}/METADATA +3 -1
  53. {pymammotion-0.2.28.dist-info → pymammotion-0.2.30.dist-info}/RECORD +55 -51
  54. {pymammotion-0.2.28.dist-info → pymammotion-0.2.30.dist-info}/LICENSE +0 -0
  55. {pymammotion-0.2.28.dist-info → pymammotion-0.2.30.dist-info}/WHEEL +0 -0
@@ -1,6 +1,5 @@
1
1
  """MowingDevice class to wrap around the betterproto dataclasses."""
2
2
 
3
- import math
4
3
  from dataclasses import dataclass
5
4
 
6
5
  import betterproto
@@ -16,8 +15,13 @@ from pymammotion.proto.mctrl_driver import MctlDriver
16
15
  from pymammotion.proto.mctrl_nav import MctlNav
17
16
  from pymammotion.proto.mctrl_ota import MctlOta
18
17
  from pymammotion.proto.mctrl_pept import MctlPept
19
- from pymammotion.proto.mctrl_sys import MctlSys, MowToAppInfoT, ReportInfoData, SystemUpdateBufMsg, \
20
- SystemRapidStateTunnelMsg
18
+ from pymammotion.proto.mctrl_sys import (
19
+ MctlSys,
20
+ MowToAppInfoT,
21
+ ReportInfoData,
22
+ SystemRapidStateTunnelMsg,
23
+ SystemUpdateBufMsg,
24
+ )
21
25
  from pymammotion.utility.constant import WorkMode
22
26
  from pymammotion.utility.conversions import parse_double
23
27
  from pymammotion.utility.map import CoordinateConverter
@@ -32,7 +36,7 @@ class MowingDevice:
32
36
  location: Location
33
37
  mowing_state: RapidState
34
38
 
35
- def __init__(self):
39
+ def __init__(self) -> None:
36
40
  self.device = LubaMsg()
37
41
  self.map = HashList(area={}, path={}, obstacle={}, hashlist=[])
38
42
  self.location = Location()
@@ -53,7 +57,7 @@ class MowingDevice:
53
57
  """Update the raw LubaMsg data."""
54
58
  self.device = LubaMsg(**raw)
55
59
 
56
- def buffer(self, buffer_list: SystemUpdateBufMsg):
60
+ def buffer(self, buffer_list: SystemUpdateBufMsg) -> None:
57
61
  """Update the device based on which buffer we are reading from."""
58
62
  match buffer_list.update_buf_data[0]:
59
63
  case 1:
@@ -95,7 +99,7 @@ class MowingDevice:
95
99
  ]
96
100
  )
97
101
 
98
- def update_report_data(self, toapp_report_data: ReportInfoData):
102
+ def update_report_data(self, toapp_report_data: ReportInfoData) -> None:
99
103
  coordinate_converter = CoordinateConverter(self.location.RTK.latitude, self.location.RTK.longitude)
100
104
  for index, location in enumerate(toapp_report_data.locations):
101
105
  if index == 0 and location.real_pos_y != 0:
@@ -105,18 +109,21 @@ class MowingDevice:
105
109
  parse_double(location.real_pos_y, 4.0), parse_double(location.real_pos_x, 4.0)
106
110
  )
107
111
  if location.zone_hash:
108
- self.location.work_zone = location.zone_hash if self.report_data.dev.sys_status == WorkMode.MODE_WORKING else 0
109
-
110
-
112
+ self.location.work_zone = (
113
+ location.zone_hash if self.report_data.dev.sys_status == WorkMode.MODE_WORKING else 0
114
+ )
111
115
 
112
116
  self.report_data = self.report_data.from_dict(toapp_report_data.to_dict(casing=betterproto.Casing.SNAKE))
113
117
 
114
- def run_state_update(self, rapid_state: SystemRapidStateTunnelMsg):
118
+ def run_state_update(self, rapid_state: SystemRapidStateTunnelMsg) -> None:
115
119
  self.mowing_state = RapidState().from_raw(rapid_state.rapid_state_data)
116
120
 
117
- def mow_info(self, toapp_mow_info: MowToAppInfoT):
121
+ def mow_info(self, toapp_mow_info: MowToAppInfoT) -> None:
118
122
  pass
119
123
 
124
+ def report_missing_data(self) -> None:
125
+ """Report missing data so we can refetch it."""
126
+
120
127
  @property
121
128
  def net(self):
122
129
  """Will return a wrapped betterproto of net."""
@@ -159,7 +166,7 @@ class DevNetData:
159
166
 
160
167
  net: dict
161
168
 
162
- def __init__(self, net: DevNet):
169
+ def __init__(self, net: DevNet) -> None:
163
170
  if isinstance(net, dict):
164
171
  self.net = net
165
172
  else:
@@ -182,13 +189,13 @@ class SysData:
182
189
 
183
190
  sys: dict
184
191
 
185
- def __init__(self, sys: MctlSys):
192
+ def __init__(self, sys: MctlSys) -> None:
186
193
  if isinstance(sys, dict):
187
194
  self.sys = sys
188
195
  else:
189
196
  self.sys = sys.to_dict()
190
197
 
191
- def __getattr__(self, item):
198
+ def __getattr__(self, item: str):
192
199
  """Intercept call to get sys in dict and return a betterproto dataclass."""
193
200
  if self.sys.get(item) is None:
194
201
  return MctlSys().__getattribute__(item)
@@ -205,13 +212,13 @@ class NavData:
205
212
 
206
213
  nav: dict
207
214
 
208
- def __init__(self, nav: MctlNav):
215
+ def __init__(self, nav: MctlNav) -> None:
209
216
  if isinstance(nav, dict):
210
217
  self.nav = nav
211
218
  else:
212
219
  self.nav = nav.to_dict()
213
220
 
214
- def __getattr__(self, item):
221
+ def __getattr__(self, item: str):
215
222
  """Intercept call to get nav in dict and return a betterproto dataclass."""
216
223
  if self.nav.get(item) is None:
217
224
  return MctlNav().__getattribute__(item)
@@ -228,13 +235,13 @@ class DriverData:
228
235
 
229
236
  driver: dict
230
237
 
231
- def __init__(self, driver: MctlDriver):
238
+ def __init__(self, driver: MctlDriver) -> None:
232
239
  if isinstance(driver, dict):
233
240
  self.driver = driver
234
241
  else:
235
242
  self.driver = driver.to_dict()
236
243
 
237
- def __getattr__(self, item):
244
+ def __getattr__(self, item: str):
238
245
  """Intercept call to get driver in dict and return a betterproto dataclass."""
239
246
  if self.driver.get(item) is None:
240
247
  return MctlDriver().__getattribute__(item)
@@ -251,13 +258,13 @@ class MulData:
251
258
 
252
259
  mul: dict
253
260
 
254
- def __init__(self, mul: SocMul):
261
+ def __init__(self, mul: SocMul) -> None:
255
262
  if isinstance(mul, dict):
256
263
  self.mul = mul
257
264
  else:
258
265
  self.mul = mul.to_dict()
259
266
 
260
- def __getattr__(self, item):
267
+ def __getattr__(self, item: str):
261
268
  """Intercept call to get mul in dict and return a betterproto dataclass."""
262
269
  if self.mul.get(item) is None:
263
270
  return SocMul().__getattribute__(item)
@@ -274,13 +281,13 @@ class OtaData:
274
281
 
275
282
  ota: dict
276
283
 
277
- def __init__(self, ota: MctlOta):
284
+ def __init__(self, ota: MctlOta) -> None:
278
285
  if isinstance(ota, dict):
279
286
  self.ota = ota
280
287
  else:
281
288
  self.ota = ota.to_dict()
282
289
 
283
- def __getattr__(self, item):
290
+ def __getattr__(self, item: str):
284
291
  """Intercept call to get ota in dict and return a betterproto dataclass."""
285
292
  if self.ota.get(item) is None:
286
293
  return MctlOta().__getattribute__(item)
@@ -297,13 +304,13 @@ class PeptData:
297
304
 
298
305
  pept: dict
299
306
 
300
- def __init__(self, pept: MctlPept):
307
+ def __init__(self, pept: MctlPept) -> None:
301
308
  if isinstance(pept, dict):
302
309
  self.pept = pept
303
310
  else:
304
311
  self.pept = pept.to_dict()
305
312
 
306
- def __getattr__(self, item):
313
+ def __getattr__(self, item: str):
307
314
  """Intercept call to get pept in dict and return a betterproto dataclass."""
308
315
  if self.pept.get(item) is None:
309
316
  return MctlPept().__getattribute__(item)
@@ -1,5 +1,7 @@
1
1
  from dataclasses import dataclass
2
2
 
3
+ from pymammotion.utility.device_type import DeviceType
4
+
3
5
 
4
6
  @dataclass
5
7
  class DeviceLimits:
@@ -7,3 +9,72 @@ class DeviceLimits:
7
9
  blade_height_max: int
8
10
  working_speed_min: float
9
11
  working_speed_max: float
12
+
13
+
14
+ @dataclass
15
+ class OperationSettings:
16
+ """Operation settings for a device."""
17
+
18
+ is_mow: bool = True
19
+ is_dump: bool = True
20
+ is_edge: bool = False
21
+ collect_grass_frequency: int = 10
22
+ job_mode: int = 0 # taskMode
23
+ job_version: int = 0
24
+ job_id: int = 0
25
+ speed: float = 0.3
26
+ ultra_wave: int = 2 # touch no touch etc
27
+ channel_mode: int = 0 # line mode is grid single double or single2
28
+ channel_width: int = 25
29
+ rain_tactics: int = 0
30
+ blade_height: int = 0
31
+ path_order: str = ""
32
+ toward: int = 0 # is just angle
33
+ toward_included_angle: int = 0
34
+ toward_mode: int = 0 # angle type relative etc
35
+ border_mode: int = 1 # border laps
36
+ obstacle_laps: int = 1
37
+ start_progress: int = 0
38
+
39
+
40
+ def create_path_order(operation_mode: OperationSettings, device_name: str) -> str:
41
+ i = 8
42
+ bArr = bytearray(8)
43
+ bArr[0] = operation_mode.border_mode
44
+ bArr[1] = operation_mode.obstacle_laps
45
+ bArr[3] = operation_mode.start_progress
46
+ bArr[2] = 0
47
+
48
+ if not DeviceType.is_luba1(device_name):
49
+ bArr[4] = 0
50
+ if DeviceType.is_yuka(device_name):
51
+ i = calculate_yuka_mode(operation_mode)
52
+ elif not DeviceType.is_luba_2(device_name):
53
+ i = 0
54
+ bArr[5] = i
55
+ if operation_mode.is_dump:
56
+ b = operation_mode.collect_grass_frequency
57
+ else:
58
+ b = 10
59
+ bArr[6] = b
60
+ if DeviceType.is_luba1(device_name):
61
+ bArr[4] = operation_mode.toward_mode
62
+ return str(bArr, "UTF-8")
63
+
64
+
65
+ def calculate_yuka_mode(operation_mode: OperationSettings) -> int:
66
+ if operation_mode.is_mow and operation_mode.is_dump and operation_mode.is_edge:
67
+ return 14
68
+ if operation_mode.is_mow and operation_mode.is_dump and not operation_mode.is_edge:
69
+ return 12
70
+ if operation_mode.is_mow and not operation_mode.is_dump and operation_mode.is_edge:
71
+ return 10
72
+ if operation_mode.is_mow and not operation_mode.is_dump and not operation_mode.is_edge:
73
+ return 8
74
+ if not operation_mode.is_mow and operation_mode.is_dump and operation_mode.is_edge:
75
+ return 6
76
+ if not operation_mode.is_mow and not operation_mode.is_dump and operation_mode.is_edge:
77
+ return 2
78
+ if not operation_mode.is_mow and operation_mode.is_dump and not operation_mode.is_edge:
79
+ return 4
80
+ return 0
@@ -9,7 +9,7 @@ class PositionMode(Enum):
9
9
  UNKNOWN = 4
10
10
 
11
11
  @staticmethod
12
- def from_value(value):
12
+ def from_value(value: int):
13
13
  if value == 0:
14
14
  return PositionMode.FIX
15
15
  elif value == 1:
@@ -21,7 +21,7 @@ class PositionMode(Enum):
21
21
  else:
22
22
  return PositionMode.UNKNOWN
23
23
 
24
- def __str__(self):
24
+ def __str__(self) -> str:
25
25
  if self == PositionMode.FIX:
26
26
  return "Fix"
27
27
  elif self == PositionMode.SINGLE:
@@ -42,7 +42,7 @@ class RTKStatus(Enum):
42
42
  UNKNOWN = 6
43
43
 
44
44
  @staticmethod
45
- def from_value(value):
45
+ def from_value(value: int):
46
46
  if value == 0:
47
47
  return RTKStatus.NONE
48
48
  elif value == 1 or value == 2:
@@ -54,7 +54,7 @@ class RTKStatus(Enum):
54
54
  else:
55
55
  return RTKStatus.UNKNOWN
56
56
 
57
- def __str__(self):
57
+ def __str__(self) -> str:
58
58
  if self == RTKStatus.NONE:
59
59
  return "None"
60
60
  elif self == RTKStatus.SINGLE:
@@ -5,7 +5,7 @@ class ExecuteBorderParams:
5
5
  currentFrame = 0
6
6
  jobIndex = ""
7
7
 
8
- def __init__(self, i, str_, list_):
8
+ def __init__(self, i, str_, list_) -> None:
9
9
  """Generated source for method __init__"""
10
10
  self.currentFrame = i
11
11
  self.border = list_
@@ -15,7 +15,7 @@ class ExecuteBorderParams:
15
15
  """Generated source for method getCurrentFrame"""
16
16
  return self.currentFrame
17
17
 
18
- def set_current_frame(self, i):
18
+ def set_current_frame(self, i) -> None:
19
19
  """Generated source for method setCurrentFrame"""
20
20
  self.currentFrame = i
21
21
 
@@ -23,7 +23,7 @@ class ExecuteBorderParams:
23
23
  """Generated source for method getJobIndex"""
24
24
  return self.jobIndex
25
25
 
26
- def set_job_index(self, str_):
26
+ def set_job_index(self, str_) -> None:
27
27
  """Generated source for method setJobIndex"""
28
28
  self.jobIndex = str_
29
29
 
@@ -31,11 +31,11 @@ class ExecuteBorderParams:
31
31
  """Generated source for method getBorder"""
32
32
  return self.border
33
33
 
34
- def set_border(self, border_list):
34
+ def set_border(self, border_list) -> None:
35
35
  """Generated source for method setBorder"""
36
36
  self.border = border_list
37
37
 
38
- def __str__(self):
38
+ def __str__(self) -> str:
39
39
  """Generated source for method toString"""
40
40
  return (
41
41
  "ExecuteBorderParamsBean{currentFrame="
@@ -9,7 +9,7 @@ class ExecuteBorder(Serializable):
9
9
  cmd = 0
10
10
  params = ExecuteBorderParams(None, None, None)
11
11
 
12
- def __init__(self, i, execute_border_params: ExecuteBorderParams):
12
+ def __init__(self, i, execute_border_params: ExecuteBorderParams) -> None:
13
13
  """Generated source for method __init__"""
14
14
  super().__init__()
15
15
  self.cmd = i
@@ -19,7 +19,7 @@ class ExecuteBorder(Serializable):
19
19
  """Generated source for method getCmd"""
20
20
  return self.cmd
21
21
 
22
- def set_cmd(self, i):
22
+ def set_cmd(self, i) -> None:
23
23
  """Generated source for method setCmd"""
24
24
  self.cmd = i
25
25
 
@@ -27,10 +27,10 @@ class ExecuteBorder(Serializable):
27
27
  """Generated source for method getParams"""
28
28
  return self.params
29
29
 
30
- def set_params(self, execute_border_params):
30
+ def set_params(self, execute_border_params) -> None:
31
31
  """Generated source for method setParams"""
32
32
  self.params = execute_border_params
33
33
 
34
- def __str__(self):
34
+ def __str__(self) -> str:
35
35
  """Generated source for method toString"""
36
36
  return "ExecuteBean{cmd=" + self.cmd + ", params=" + self.params + "}"
@@ -1,133 +1,27 @@
1
1
  import logging
2
+ from dataclasses import dataclass
2
3
  from typing import List
3
4
 
4
5
  logger = logging.getLogger(__name__)
5
6
 
6
7
 
8
+ @dataclass
7
9
  class GenerateRouteInformation:
8
10
  """Creates a model for generating route information and mowing plan before starting a job."""
9
11
 
10
- def __init__(
11
- self,
12
- one_hashs: List[int],
13
- job_mode: int,
14
- channel_width: int,
15
- speed: float,
16
- ultra_wave: int,
17
- channel_mode: int,
18
- rain_tactics: int,
19
- toward: int,
20
- knife_height: int,
21
- path_order: str,
22
- toward_included_angle: int,
23
- ):
24
- self.path_order = ""
25
- self.toward_mode = 0
26
- self.one_hashs = one_hashs
27
- self.rain_tactics = rain_tactics
28
- self.job_mode = job_mode
29
- self.knife_height = knife_height
30
- self.speed = speed
31
- self.ultra_wave = ultra_wave
32
- self.channel_width = channel_width
33
- self.channel_mode = channel_mode
34
- self.toward = toward
35
- self.edge_mode = rain_tactics
36
- self.path_order = path_order
37
- self.toward_included_angle = toward_included_angle
38
- logger.debug(
39
- f"Mode route command parameters jobMode={job_mode}//channelWidth={channel_width}//speed={speed}//UltraWave={ultra_wave}//channelMode={channel_mode}//edgeMode={rain_tactics}//knifeHeight={knife_height} pathOrder:{path_order.encode('utf-8')}"
40
- )
41
-
42
- def get_job_id(self) -> int:
43
- return self.job_id
44
-
45
- def set_job_id(self, job_id: int) -> None:
46
- self.job_id = job_id
47
-
48
- def get_job_ver(self) -> int:
49
- return self.job_ver
50
-
51
- def set_job_ver(self, job_ver: int) -> None:
52
- self.job_ver = job_ver
53
-
54
- def get_rain_tactics(self) -> int:
55
- return self.rain_tactics
56
-
57
- def set_rain_tactics(self, rain_tactics: int) -> None:
58
- self.rain_tactics = rain_tactics
59
-
60
- def get_job_mode(self) -> int:
61
- return self.job_mode
62
-
63
- def set_job_mode(self, job_mode: int) -> None:
64
- self.job_mode = job_mode
65
-
66
- def get_knife_height(self) -> int:
67
- return self.knife_height
68
-
69
- def set_knife_height(self, knife_height: int) -> None:
70
- self.knife_height = knife_height
71
-
72
- def get_speed(self) -> float:
73
- return self.speed
74
-
75
- def set_speed(self, speed: float) -> None:
76
- self.speed = speed
77
-
78
- def get_ultra_wave(self) -> int:
79
- return self.ultra_wave
80
-
81
- def set_ultra_wave(self, ultra_wave: int) -> None:
82
- self.ultra_wave = ultra_wave
83
-
84
- def get_channel_width(self) -> int:
85
- return self.channel_width
86
-
87
- def set_channel_width(self, channel_width: int) -> None:
88
- self.channel_width = channel_width
89
-
90
- def get_channel_mode(self) -> int:
91
- return self.channel_mode
92
-
93
- def set_channel_mode(self, channel_mode: int) -> None:
94
- self.channel_mode = channel_mode
95
-
96
- def get_toward(self) -> int:
97
- return self.toward
98
-
99
- def set_toward(self, toward: int) -> None:
100
- self.toward = toward
101
-
102
- def get_one_hashs(self) -> List[int]:
103
- return self.one_hashs if self.one_hashs else []
104
-
105
- def set_one_hashs(self, one_hashs: List[int]) -> None:
106
- self.one_hashs = one_hashs
107
-
108
- def get_path_order(self) -> str:
109
- return self.path_order
110
-
111
- def set_path_order(self, path_order: str) -> None:
112
- self.path_order = path_order
113
-
114
- def get_toward_included_angle(self) -> int:
115
- return self.toward_included_angle
116
-
117
- def set_toward_included_angle(self, toward_included_angle: int) -> None:
118
- self.toward_included_angle = toward_included_angle
119
-
120
- def get_toward_mode(self) -> int:
121
- return self.toward_mode
122
-
123
- def get_edge_mode(self) -> int:
124
- return self.edge_mode
125
-
126
- def set_edge_mode(self, edge_mode: int) -> None:
127
- self.edge_mode = edge_mode
128
-
129
- def __str__(self) -> str:
130
- try:
131
- return f"GenerateRouteInformation{{oneHashs={self.one_hashs}, jobId={self.job_id}, jobVer={self.job_ver}, rainTactics={self.rain_tactics}, jobMode={self.job_mode}, knifeHeight={self.knife_height}, speed={self.speed}, UltraWave={self.ultra_wave}, channelWidth={self.channel_width}, channelMode={self.channel_mode}, toward={self.toward}, pathOrder='{self.path_order.encode('utf-8')}', edgeMode={self.edge_mode}, towardIncludedAngle={self.toward_included_angle}}}"
132
- except Exception as e:
133
- return str(e)
12
+ one_hashs: list[int] = list
13
+ job_mode: int = 0 # taskMode
14
+ job_version: int = 0
15
+ job_id: int = 0
16
+ speed: float = 0.3
17
+ ultra_wave: int = 2 # touch no touch etc
18
+ channel_mode: int = 0 # line mode is grid single double or single2
19
+ channel_width: int = 25
20
+ rain_tactics: int = 0
21
+ blade_height: int = 0
22
+ path_order: str = ""
23
+ toward: int = 0 # is just angle
24
+ toward_included_angle: int = 0
25
+ toward_mode: int = 0 # angle type relative etc
26
+ edge_mode: int = 1 # border laps
27
+ obstacle_laps: int = 1
@@ -6,10 +6,12 @@ from pymammotion.proto.mctrl_nav import NavGetCommDataAck
6
6
 
7
7
  class PathType(IntEnum):
8
8
  """Path types for common data."""
9
+
9
10
  AREA = 0
10
11
  OBSTACLE = 1
11
12
  PATH = 2
12
13
 
14
+
13
15
  @dataclass
14
16
  class FrameList:
15
17
  total_frame: int
@@ -28,13 +30,12 @@ class HashList:
28
30
  obstacle: dict # type 1
29
31
  hashlist: list[int]
30
32
 
31
- def set_hashlist(self, hashlist: list[int]):
33
+ def set_hashlist(self, hashlist: list[int]) -> None:
32
34
  self.hashlist = hashlist
33
35
  self.area = {hash_id: frames for hash_id, frames in self.area.items() if hash_id in hashlist}
34
36
  self.path = {hash_id: frames for hash_id, frames in self.path.items() if hash_id in hashlist}
35
37
  self.obstacle = {hash_id: frames for hash_id, frames in self.obstacle.items() if hash_id in hashlist}
36
38
 
37
-
38
39
  def missing_frame(self, hash_data: NavGetCommDataAck) -> list[int]:
39
40
  if hash_data.type == PathType.AREA:
40
41
  return self._find_missing_frames(self.area.get(hash_data.hash))
@@ -45,8 +46,6 @@ class HashList:
45
46
  if hash_data.type == PathType.PATH:
46
47
  return self._find_missing_frames(self.path.get(hash_data.hash))
47
48
 
48
-
49
-
50
49
  def update(self, hash_data: NavGetCommDataAck) -> bool:
51
50
  """Update the map data."""
52
51
  if hash_data.type == PathType.AREA:
@@ -58,18 +57,16 @@ class HashList:
58
57
  if hash_data.type == PathType.PATH:
59
58
  return self._add_hash_data(self.path, hash_data)
60
59
 
61
-
62
60
  @staticmethod
63
61
  def _find_missing_frames(frame_list: FrameList) -> list[int]:
64
62
  if frame_list.total_frame == len(frame_list.data):
65
63
  return []
66
- number_list = list(range(1, frame_list.total_frame+1))
64
+ number_list = list(range(1, frame_list.total_frame + 1))
67
65
 
68
66
  current_frames = {frame.current_frame for frame in frame_list.data}
69
67
  missing_numbers = [num for num in number_list if num not in current_frames]
70
68
  return missing_numbers
71
69
 
72
-
73
70
  @staticmethod
74
71
  def _add_hash_data(hash_dict: dict, hash_data: NavGetCommDataAck) -> bool:
75
72
  if hash_dict.get(hash_data.hash) is None:
@@ -10,7 +10,7 @@ class Point:
10
10
  latitude: float = 0.0
11
11
  longitude: float = 0.0
12
12
 
13
- def __init__(self, latitude=0.0, longitude=0.0):
13
+ def __init__(self, latitude: float = 0.0, longitude: float = 0.0) -> None:
14
14
  self.latitude = latitude
15
15
  self.longitude = longitude
16
16
 
@@ -30,10 +30,10 @@ class Location:
30
30
  RTK: Point
31
31
  dock: Dock
32
32
  position_type: int = 0
33
- orientation: int = 0 # 360 degree rotation +-
33
+ orientation: int = 0 # 360 degree rotation +-
34
34
  work_zone: int = 0
35
35
 
36
- def __init__(self):
36
+ def __init__(self) -> None:
37
37
  self.device = Point()
38
38
  self.RTK = Point()
39
39
  self.dock = Dock()
@@ -51,4 +51,4 @@ class PathAngleSetting(IntEnum):
51
51
 
52
52
  relative_angle = 0
53
53
  absolute_angle = 1
54
- random_angle = 2 # Luba Pro / Luba 2 Yuka only
54
+ random_angle = 2 # Luba Pro / Luba 2 Yuka only
@@ -2,7 +2,7 @@ from typing import List
2
2
 
3
3
 
4
4
  class Plan:
5
- def __init__(self):
5
+ def __init__(self) -> None:
6
6
  self.pver: int = 0
7
7
  self.sub_cmd: int = 0
8
8
  self.area: int = 0
@@ -31,9 +31,9 @@ class Plan:
31
31
  self.speed: float = 0.0
32
32
  self.task_name: str = ""
33
33
  self.job_name: str = ""
34
- self.zone_hashs: List[int] = []
34
+ self.zone_hashs: list[int] = []
35
35
  self.reserved: str = ""
36
- self.weeks: List[int] = []
36
+ self.weeks: list[int] = []
37
37
  self.start_date: str = ""
38
38
  self.end_date: str = ""
39
39
  self.job_type: int = 0
@@ -46,7 +46,7 @@ class Plan:
46
46
  self.path_order: int = 0
47
47
  self.demond_angle: int = 90
48
48
 
49
- def __str__(self):
49
+ def __str__(self) -> str:
50
50
  return f"Plan(pver={self.pver}, sub_cmd={self.sub_cmd}, area={self.area}, work_time={self.work_time}, version='{self.version}', id='{self.id}', user_id='{self.user_id}', device_id='{self.device_id}', plan_id='{self.plan_id}', task_id='{self.task_id}', job_id='{self.job_id}', start_time='{self.start_time}', end_time='{self.end_time}', week={self.week}, knife_height={self.knife_height}, model={self.model}, edge_mode={self.edge_mode}, required_time={self.required_time}, route_angle={self.route_angle}, route_model={self.route_model}, route_spacing={self.route_spacing}, ultrasonic_barrier={self.ultrasonic_barrier}, total_plan_num={self.total_plan_num}, plan_index={self.plan_index}, result={self.result}, speed={self.speed}, task_name='{self.task_name}', job_name='{self.job_name}', zone_hashs={self.zone_hashs}, reserved='{self.reserved}', weeks={self.weeks}, start_date='{self.start_date}', end_date='{self.end_date}', job_type={self.job_type}, interval_days={self.interval_days}, count_down={self.count_down}, is_enable={self.is_enable}, mowing_laps={self.mowing_laps}, path_order={self.path_order}, demond_angle={self.demond_angle}, is_mow_work={self.is_mow_work}, is_sweeping_work={self.is_sweeping_work})"
51
51
 
52
52
  def __eq__(self, other):
@@ -2,7 +2,7 @@ from typing import List, Optional
2
2
 
3
3
 
4
4
  class RegionData:
5
- def __init__(self):
5
+ def __init__(self) -> None:
6
6
  self.hash: Optional[int] = None
7
7
  self.action: int = 0
8
8
  self.current_frame: int = 0
@@ -10,7 +10,7 @@ class RegionData:
10
10
  self.data_len: int = 0
11
11
  self.p_hash_a: Optional[int] = None
12
12
  self.p_hash_b: Optional[int] = None
13
- self.path: Optional[List[List[float]]] = None
13
+ self.path: Optional[list[list[float]]] = None
14
14
  self.pver: int = 0
15
15
  self.result: int = 0
16
16
  self.sub_cmd: int = 0
@@ -68,10 +68,10 @@ class RegionData:
68
68
  def set_current_frame(self, current_frame: int) -> None:
69
69
  self.current_frame = current_frame
70
70
 
71
- def get_path(self) -> Optional[List[List[float]]]:
71
+ def get_path(self) -> Optional[list[list[float]]]:
72
72
  return self.path
73
73
 
74
- def set_path(self, path: List[List[float]]) -> None:
74
+ def set_path(self, path: list[list[float]]) -> None:
75
75
  self.path = path
76
76
 
77
77
  def get_hash(self) -> Optional[int]:
@@ -8,7 +8,7 @@ class ConnectData:
8
8
  wifi_rssi: int = 0
9
9
 
10
10
  @classmethod
11
- def from_dict(cls, data: dict):
11
+ def from_dict(cls, data: dict) -> "ConnectData":
12
12
  return cls(
13
13
  connect_type=data.get("connect_type", 0),
14
14
  ble_rssi=data.get("ble_rssi", 0),