pymammotion 0.0.37__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 (106) hide show
  1. pymammotion/__init__.py +43 -0
  2. pymammotion/aliyun/cloud_gateway.py +549 -0
  3. pymammotion/aliyun/cloud_service.py +65 -0
  4. pymammotion/aliyun/dataclass/aep_response.py +18 -0
  5. pymammotion/aliyun/dataclass/connect_response.py +51 -0
  6. pymammotion/aliyun/dataclass/dev_by_account_response.py +43 -0
  7. pymammotion/aliyun/dataclass/login_by_oauth_response.py +65 -0
  8. pymammotion/aliyun/dataclass/regions_response.py +26 -0
  9. pymammotion/aliyun/dataclass/session_by_authcode_response.py +18 -0
  10. pymammotion/aliyun/tmp_constant.py +175 -0
  11. pymammotion/bluetooth/__init__.py +1 -0
  12. pymammotion/bluetooth/ble.py +74 -0
  13. pymammotion/bluetooth/ble_message.py +430 -0
  14. pymammotion/bluetooth/const.py +27 -0
  15. pymammotion/bluetooth/data/__init__.py +0 -0
  16. pymammotion/bluetooth/data/convert.py +26 -0
  17. pymammotion/bluetooth/data/framectrldata.py +40 -0
  18. pymammotion/bluetooth/data/notifydata.py +63 -0
  19. pymammotion/const.py +9 -0
  20. pymammotion/data/__init__.py +0 -0
  21. pymammotion/data/model/__init__.py +8 -0
  22. pymammotion/data/model/device.py +157 -0
  23. pymammotion/data/model/enums.py +67 -0
  24. pymammotion/data/model/excute_boarder_params.py +48 -0
  25. pymammotion/data/model/execute_boarder.py +36 -0
  26. pymammotion/data/model/generate_route_information.py +133 -0
  27. pymammotion/data/model/hash_list.py +17 -0
  28. pymammotion/data/model/mowing_modes.py +37 -0
  29. pymammotion/data/model/plan.py +58 -0
  30. pymammotion/data/model/rapid_state.py +45 -0
  31. pymammotion/data/model/region_data.py +99 -0
  32. pymammotion/data/mqtt/__init__.py +1 -0
  33. pymammotion/data/mqtt/event.py +90 -0
  34. pymammotion/data/mqtt/properties.py +140 -0
  35. pymammotion/data/mqtt/status.py +52 -0
  36. pymammotion/event/__init__.py +6 -0
  37. pymammotion/event/event.py +50 -0
  38. pymammotion/http/_init_.py +0 -0
  39. pymammotion/http/http.py +76 -0
  40. pymammotion/luba/_init_.py +0 -0
  41. pymammotion/luba/base.py +52 -0
  42. pymammotion/mammotion/__init__.py +0 -0
  43. pymammotion/mammotion/commands/__init__.py +0 -0
  44. pymammotion/mammotion/commands/abstract_message.py +7 -0
  45. pymammotion/mammotion/commands/mammotion_command.py +34 -0
  46. pymammotion/mammotion/commands/messages/__init__.py +0 -0
  47. pymammotion/mammotion/commands/messages/driver.py +108 -0
  48. pymammotion/mammotion/commands/messages/media.py +36 -0
  49. pymammotion/mammotion/commands/messages/navigation.py +535 -0
  50. pymammotion/mammotion/commands/messages/network.py +236 -0
  51. pymammotion/mammotion/commands/messages/ota.py +34 -0
  52. pymammotion/mammotion/commands/messages/system.py +266 -0
  53. pymammotion/mammotion/commands/messages/video.py +27 -0
  54. pymammotion/mammotion/control/__init__.py +0 -0
  55. pymammotion/mammotion/control/joystick.py +184 -0
  56. pymammotion/mammotion/devices/__init__.py +1 -0
  57. pymammotion/mammotion/devices/luba.py +564 -0
  58. pymammotion/mqtt/mqtt.py +230 -0
  59. pymammotion/proto/__init__.py +0 -0
  60. pymammotion/proto/common.proto +7 -0
  61. pymammotion/proto/common.py +12 -0
  62. pymammotion/proto/common_pb2.py +25 -0
  63. pymammotion/proto/common_pb2.pyi +13 -0
  64. pymammotion/proto/dev_net.proto +297 -0
  65. pymammotion/proto/dev_net.py +381 -0
  66. pymammotion/proto/dev_net_pb2.py +107 -0
  67. pymammotion/proto/dev_net_pb2.pyi +472 -0
  68. pymammotion/proto/luba_msg.proto +73 -0
  69. pymammotion/proto/luba_msg.py +80 -0
  70. pymammotion/proto/luba_msg_pb2.py +40 -0
  71. pymammotion/proto/luba_msg_pb2.pyi +93 -0
  72. pymammotion/proto/luba_mul.proto +68 -0
  73. pymammotion/proto/luba_mul.py +76 -0
  74. pymammotion/proto/luba_mul_pb2.py +45 -0
  75. pymammotion/proto/luba_mul_pb2.pyi +91 -0
  76. pymammotion/proto/mctrl_driver.proto +67 -0
  77. pymammotion/proto/mctrl_driver.py +100 -0
  78. pymammotion/proto/mctrl_driver_pb2.py +45 -0
  79. pymammotion/proto/mctrl_driver_pb2.pyi +112 -0
  80. pymammotion/proto/mctrl_nav.proto +485 -0
  81. pymammotion/proto/mctrl_nav.py +589 -0
  82. pymammotion/proto/mctrl_nav_pb2.py +116 -0
  83. pymammotion/proto/mctrl_nav_pb2.pyi +875 -0
  84. pymammotion/proto/mctrl_ota.proto +42 -0
  85. pymammotion/proto/mctrl_ota.py +48 -0
  86. pymammotion/proto/mctrl_ota_pb2.py +35 -0
  87. pymammotion/proto/mctrl_ota_pb2.pyi +65 -0
  88. pymammotion/proto/mctrl_pept.proto +29 -0
  89. pymammotion/proto/mctrl_pept.py +41 -0
  90. pymammotion/proto/mctrl_pept_pb2.py +31 -0
  91. pymammotion/proto/mctrl_pept_pb2.pyi +50 -0
  92. pymammotion/proto/mctrl_sys.proto +487 -0
  93. pymammotion/proto/mctrl_sys.py +574 -0
  94. pymammotion/proto/mctrl_sys_pb2.py +142 -0
  95. pymammotion/proto/mctrl_sys_pb2.pyi +787 -0
  96. pymammotion/py.typed +0 -0
  97. pymammotion/utility/constant/__init__.py +1 -0
  98. pymammotion/utility/constant/device_constant.py +238 -0
  99. pymammotion/utility/datatype_converter.py +80 -0
  100. pymammotion/utility/device_type.py +152 -0
  101. pymammotion/utility/periodic.py +41 -0
  102. pymammotion/utility/rocker_util.py +135 -0
  103. pymammotion-0.0.37.dist-info/LICENSE +674 -0
  104. pymammotion-0.0.37.dist-info/METADATA +92 -0
  105. pymammotion-0.0.37.dist-info/RECORD +106 -0
  106. pymammotion-0.0.37.dist-info/WHEEL +4 -0
@@ -0,0 +1,430 @@
1
+ import itertools
2
+ import json
3
+ import logging
4
+ import queue
5
+ import sys
6
+ import time
7
+ from asyncio import sleep
8
+ from io import BytesIO
9
+
10
+ from bleak import BleakClient
11
+ from jsonic.serializable import serialize
12
+
13
+ from pyluba.aliyun.tmp_constant import tmp_constant
14
+ from pyluba.bluetooth.const import UUID_WRITE_CHARACTERISTIC
15
+ from pyluba.bluetooth.data.convert import parse_custom_data
16
+ from pyluba.bluetooth.data.framectrldata import FrameCtrlData
17
+ from pyluba.bluetooth.data.notifydata import BlufiNotifyData
18
+ from pyluba.data.model.execute_boarder import ExecuteBorder
19
+ from pyluba.mammotion.commands.messages.navigation import MessageNavigation
20
+ from pyluba.proto import (
21
+ dev_net_pb2,
22
+ luba_msg_pb2,
23
+ )
24
+ from pyluba.utility.constant.device_constant import bleOrderCmd
25
+
26
+ _LOGGER = logging.getLogger(__name__)
27
+
28
+
29
+ class BleMessage:
30
+ """Class for sending and recieving messages from Luba"""
31
+
32
+ AES_TRANSFORMATION = "AES/CFB/NoPadding"
33
+ DEFAULT_PACKAGE_LENGTH = 20
34
+ DH_G = "2"
35
+ DH_P = "cf5cf5c38419a724957ff5dd323b9c45c3cdd261eb740f69aa94b8bb1a5c96409153bd76b24222d03274e4725a5406092e9e82e9135c643cae98132b0d95f7d65347c68afc1e677da90e51bbab5f5cf429c291b4ba39c6b2dc5e8c7231e46aa7728e87664532cdf547be20c9a3fa8342be6e34371a27c06f7dc0edddd2f86373"
36
+ MIN_PACKAGE_LENGTH = 20
37
+ NEG_SECURITY_SET_ALL_DATA = 1
38
+ NEG_SECURITY_SET_TOTAL_LENGTH = 0
39
+ PACKAGE_HEADER_LENGTH = 4
40
+ mPrintDebug = False
41
+ mWriteTimeout = -1
42
+ mPackageLengthLimit = -1
43
+ mBlufiMTU = -1
44
+ mEncrypted = False
45
+ mChecksum = False
46
+ mRequireAck = False
47
+ mConnectState = 0
48
+ mSendSequence: iter
49
+ mReadSequence: iter
50
+ mAck: queue
51
+ notification: BlufiNotifyData
52
+ messageNavigation: MessageNavigation = MessageNavigation()
53
+
54
+ def __init__(self, client: BleakClient):
55
+ self.client = client
56
+ self.mSendSequence = itertools.count()
57
+ self.mReadSequence = itertools.count()
58
+ self.mAck = queue.Queue()
59
+ self.notification = BlufiNotifyData()
60
+
61
+ async def get_device_version_main(self):
62
+ commEsp = dev_net_pb2.DevNet(todev_devinfo_req=dev_net_pb2.DrvDevInfoReq())
63
+
64
+ for i in range(1, 8):
65
+ if i == 1:
66
+ commEsp.todev_devinfo_req.req_ids.add(id=i, type=6)
67
+ commEsp.todev_devinfo_req.req_ids.add(id=i, type=3)
68
+
69
+ lubaMsg = luba_msg_pb2.LubaMsg()
70
+ lubaMsg.msgtype = luba_msg_pb2.MSG_CMD_TYPE_ESP
71
+ lubaMsg.sender = luba_msg_pb2.DEV_MOBILEAPP
72
+ lubaMsg.msgattr = luba_msg_pb2.MSG_ATTR_REQ
73
+ lubaMsg.seqs = 1
74
+ lubaMsg.version = 1
75
+ lubaMsg.subtype = 1
76
+ lubaMsg.net.CopyFrom(commEsp)
77
+ byte_arr = lubaMsg.SerializeToString()
78
+ await self.messageNavigation.post_custom_data_bytes(byte_arr)
79
+
80
+ async def get_task(self):
81
+ hash_map = {"pver": 1, "subCmd": 2, "result": 0}
82
+ await self.messageNavigation.post_custom_data(
83
+ self.get_json_string(bleOrderCmd.task, hash_map)
84
+ )
85
+
86
+ async def send_ble_alive(self):
87
+ hash_map = {"ctrl": 1}
88
+ await self.messageNavigation.post_custom_data(
89
+ self.get_json_string(bleOrderCmd.bleAlive, hash_map)
90
+ )
91
+
92
+ def clearNotification(self):
93
+ self.notification = None
94
+ self.notification = BlufiNotifyData()
95
+
96
+ # async def get_device_info(self):
97
+ # await self.postCustomData(self.getJsonString(bleOrderCmd.getDeviceInfo))
98
+
99
+ async def send_device_info(self):
100
+ """Currently not called"""
101
+ luba_msg = luba_msg_pb2.LubaMsg(
102
+ msgtype=luba_msg_pb2.MsgCmdType.MSG_CMD_TYPE_ESP,
103
+ sender=luba_msg_pb2.MsgDevice.DEV_MOBILEAPP,
104
+ rcver=luba_msg_pb2.MsgDevice.DEV_COMM_ESP,
105
+ msgattr=luba_msg_pb2.MsgAttr.MSG_ATTR_REQ,
106
+ seqs=1,
107
+ version=1,
108
+ subtype=1,
109
+ net=dev_net_pb2.DevNet(
110
+ todev_ble_sync=1, todev_devinfo_req=dev_net_pb2.DrvDevInfoReq()
111
+ ),
112
+ )
113
+ byte_arr = luba_msg.SerializeToString()
114
+ await self.post_custom_data_bytes(byte_arr)
115
+
116
+ async def requestDeviceStatus(self):
117
+ request = False
118
+ type = self.messageNavigation.getTypeValue(0, 5)
119
+ try:
120
+ request = await self.messageNavigation.post(
121
+ BleMessage.mEncrypted, BleMessage.mChecksum, False, type, None
122
+ )
123
+ # print(request)
124
+ except Exception as err:
125
+ # Log.w(TAG, "post requestDeviceStatus interrupted")
126
+ request = False
127
+ print(err)
128
+
129
+ # if not request:
130
+ # onStatusResponse(BlufiCallback.CODE_WRITE_DATA_FAILED, null)
131
+
132
+ async def requestDeviceVersion(self):
133
+ request = False
134
+ type = self.messageNavigation.getTypeValue(0, 7)
135
+ try:
136
+ request = await self.messageNavigation.post(
137
+ BleMessage.mEncrypted, BleMessage.mChecksum, False, type, None
138
+ )
139
+ # print(request)
140
+ except Exception as err:
141
+ # Log.w(TAG, "post requestDeviceStatus interrupted")
142
+ request = False
143
+ print(err)
144
+
145
+ async def sendBorderPackage(self, executeBorder: ExecuteBorder):
146
+ await self.messageNavigation.post_custom_data(serialize(executeBorder))
147
+
148
+ async def gatt_write(self, data: bytes) -> None:
149
+ await self.client.write_gatt_char(UUID_WRITE_CHARACTERISTIC, data, True)
150
+
151
+ def parseNotification(self, response: bytearray):
152
+ dataOffset = None
153
+ if response is None:
154
+ # Log.w(TAG, "parseNotification null data");
155
+ return -1
156
+
157
+ # if (this.mPrintDebug):
158
+ # Log.d(TAG, "parseNotification Notification= " + Arrays.toString(response));
159
+ # }
160
+ if len(response) >= 4:
161
+ sequence = int(response[2]) # toInt
162
+ if sequence != next(self.mReadSequence):
163
+ print(
164
+ "parseNotification read sequence wrong",
165
+ sequence,
166
+ self.mReadSequence,
167
+ )
168
+ self.mReadSequence = itertools.count(start=sequence)
169
+ # this is questionable
170
+ # self.mReadSequence = sequence
171
+ # self.mReadSequence_2.incrementAndGet()
172
+
173
+ # LogUtil.m7773e(self.mGatt.getDevice().getName() + "打印丢包率", self.mReadSequence_2 + "/" + self.mReadSequence_1);
174
+ pkt_type = int(response[0]) # toInt
175
+ pkgType = self._getPackageType(pkt_type)
176
+ subType = self._getSubType(pkt_type)
177
+ self.notification.setType(pkt_type)
178
+ self.notification.setPkgType(pkgType)
179
+ self.notification.setSubType(subType)
180
+ frameCtrl = int(response[1]) # toInt
181
+ # print("frame ctrl")
182
+ # print(frameCtrl)
183
+ # print(response)
184
+ # print(f"pktType {pkt_type} pkgType {pkgType} subType {subType}")
185
+ self.notification.setFrameCtrl(frameCtrl)
186
+ frameCtrlData = FrameCtrlData(frameCtrl)
187
+ dataLen = int(response[3]) # toInt specifies length of data
188
+
189
+ try:
190
+ dataBytes = response[4 : 4 + dataLen]
191
+ if frameCtrlData.isEncrypted():
192
+ print("is encrypted")
193
+ # BlufiAES aes = new BlufiAES(self.mAESKey, AES_TRANSFORMATION, generateAESIV(sequence));
194
+ # dataBytes = aes.decrypt(dataBytes);
195
+ # }
196
+ if frameCtrlData.isChecksum():
197
+ print("checksum")
198
+ # int respChecksum1 = toInt(response[response.length - 1]);
199
+ # int respChecksum2 = toInt(response[response.length - 2]);
200
+ # int crc = BlufiCRC.calcCRC(BlufiCRC.calcCRC(0, new byte[]{(byte) sequence, (byte) dataLen}), dataBytes);
201
+ # int calcChecksum1 = (crc >> 8) & 255;
202
+ # int calcChecksum2 = crc & 255;
203
+ # if (respChecksum1 != calcChecksum1 || respChecksum2 != calcChecksum2) {
204
+ # Log.w(TAG, "parseNotification: read invalid checksum");
205
+ # if (self.mPrintDebug) {
206
+ # Log.d(TAG, "expect checksum: " + respChecksum1 + ", " + respChecksum2);
207
+ # Log.d(TAG, "received checksum: " + calcChecksum1 + ", " + calcChecksum2);
208
+ # return -4;
209
+ # }
210
+ # return -4;
211
+ # }
212
+ # }
213
+ if frameCtrlData.hasFrag():
214
+ dataOffset = 2
215
+ else:
216
+ dataOffset = 0
217
+
218
+ self.notification.addData(dataBytes, dataOffset)
219
+ return 1 if frameCtrlData.hasFrag() else 0
220
+ except Exception as e:
221
+ print(e)
222
+ return -100
223
+
224
+ # Log.w(TAG, "parseNotification data length less than 4");
225
+ return -2
226
+
227
+ async def parseBlufiNotifyData(self, return_bytes: bool = False):
228
+ pkgType = self.notification.getPkgType()
229
+ subType = self.notification.getSubType()
230
+ dataBytes = self.notification.getDataArray()
231
+ if pkgType == 0:
232
+ # never seem to get these..
233
+ self._parseCtrlData(subType, dataBytes)
234
+ if pkgType == 1:
235
+ if return_bytes:
236
+ return dataBytes
237
+ return await self._parseDataData(subType, dataBytes)
238
+
239
+ def _parseCtrlData(self, subType: int, data: bytes):
240
+ pass
241
+ # self._parseAck(data)
242
+
243
+ async def _parseDataData(self, subType: int, data: bytes):
244
+ # if (subType == 0) {
245
+ # this.mSecurityCallback.onReceiveDevicePublicKey(data);
246
+ # return;
247
+ # }
248
+ print(subType)
249
+ match subType:
250
+ # case 15:
251
+ # parseWifiState(data);
252
+ # return;
253
+ # case 16:
254
+ # parseVersion(data);
255
+ # return;
256
+ # case 17:
257
+ # parseWifiScanList(data);
258
+ # return;
259
+ # case 18:
260
+ # int errCode = data.length > 0 ? 255 & data[0] : 255;
261
+ # onError(errCode);
262
+ # return;
263
+ case 19:
264
+ # # com/agilexrobotics/utils/EspBleUtil$BlufiCallbackMain.smali
265
+ luba_msg = parse_custom_data(data) # parse to protobuf message
266
+ # really need some sort of callback
267
+ if luba_msg.HasField("net"):
268
+ if luba_msg.net.HasField("toapp_wifi_iot_status"):
269
+ # await sleep(1.5)
270
+ print("sending ble sync")
271
+ # await self.send_todev_ble_sync(2)
272
+ return luba_msg
273
+
274
+ # private void parseCtrlData(int i, byte[] bArr) {
275
+ # if (i == 0) {
276
+ # parseAck(bArr);
277
+ # }
278
+ # }
279
+
280
+ # private void parseAck(byte[] bArr) {
281
+ # this.mAck.add(Integer.valueOf(bArr.length > 0 ? bArr[0] & 255 : 256));
282
+ # }
283
+
284
+ def getJsonString(self, cmd: int) -> str:
285
+ jSONObject = {}
286
+ try:
287
+ jSONObject["cmd"] = cmd
288
+ jSONObject[tmp_constant.REQUEST_ID] = int(time.time())
289
+ return json.dumps(jSONObject)
290
+ except Exception:
291
+ return ""
292
+
293
+ def current_milli_time(self):
294
+ return round(time.time() * 1000)
295
+
296
+ def _getPackageType(self, typeValue: int):
297
+ return typeValue & 3
298
+
299
+ def _getSubType(self, typeValue: int):
300
+ return (typeValue & 252) >> 2
301
+
302
+ def getTypeValue(self, type: int, subtype: int):
303
+ return (subtype << 2) | type
304
+
305
+ def receiveAck(self, expectAck: int) -> bool:
306
+ try:
307
+ ack = next(self.mAck)
308
+ return ack == expectAck
309
+ except Exception as err:
310
+ print(err)
311
+ return False
312
+
313
+ def generateSendSequence(self):
314
+ return next(self.mSendSequence) & 255
315
+
316
+ async def post_custom_data_bytes(self, data: bytes):
317
+ if data == None:
318
+ return
319
+ type_val = self.getTypeValue(1, 19)
320
+ try:
321
+ suc = await self.post(
322
+ self.mEncrypted, self.mChecksum, self.mRequireAck, type_val, data
323
+ )
324
+ # int status = suc ? 0 : BlufiCallback.CODE_WRITE_DATA_FAILED
325
+ # onPostCustomDataResult(status, data)
326
+ # print(suc)
327
+ except Exception as err:
328
+ print(err)
329
+
330
+ async def post_custom_data(self, data_str: str):
331
+ data = data_str.encode()
332
+ if data == None:
333
+ return
334
+ type_val = self.getTypeValue(1, 19)
335
+ try:
336
+ suc = await self.post(
337
+ self.mEncrypted, self.mChecksum, self.mRequireAck, type_val, data
338
+ )
339
+ # int status = suc ? 0 : BlufiCallback.CODE_WRITE_DATA_FAILED
340
+ # onPostCustomDataResult(status, data)
341
+ except Exception as err:
342
+ print(err)
343
+
344
+ async def post(
345
+ self,
346
+ encrypt: bool,
347
+ checksum: bool,
348
+ require_ack: bool,
349
+ type_of: int,
350
+ data: bytes,
351
+ ) -> bool:
352
+ if data is None:
353
+ return await self.post_non_data(encrypt, checksum, require_ack, type_of)
354
+
355
+ return await self.post_contains_data(
356
+ encrypt, checksum, require_ack, type_of, data
357
+ )
358
+
359
+ async def post_non_data(
360
+ self, encrypt: bool, checksum: bool, require_ack: bool, type_of: int
361
+ ) -> bool:
362
+ sequence = self.generateSendSequence()
363
+ postBytes = self.getPostBytes(
364
+ type_of, encrypt, checksum, require_ack, False, sequence, None
365
+ )
366
+ posted = await self.gatt_write(postBytes)
367
+ return posted and (not require_ack or self.receiveAck(sequence))
368
+
369
+ async def post_contains_data(
370
+ self,
371
+ encrypt: bool,
372
+ checksum: bool,
373
+ require_ack: bool,
374
+ type_of: int,
375
+ data: bytes,
376
+ ) -> bool:
377
+ chunk_size = 517 # self.client.mtu_size - 3
378
+
379
+ chunks = list()
380
+ for i in range(0, len(data), chunk_size):
381
+ if i + chunk_size > len(data):
382
+ chunks.append(data[i : len(data)])
383
+ else:
384
+ chunks.append(data[i : i + chunk_size])
385
+ for index, chunk in enumerate(chunks):
386
+ frag = index != len(chunks) - 1
387
+ sequence = self.generateSendSequence()
388
+ postBytes = self.getPostBytes(
389
+ type_of, encrypt, checksum, require_ack, frag, sequence, chunk
390
+ )
391
+ # print("sequence")
392
+ # print(sequence)
393
+ posted = await self.gatt_write(postBytes)
394
+ if posted != None:
395
+ return False
396
+
397
+ if not frag:
398
+ return not require_ack or self.receiveAck(sequence)
399
+
400
+ if require_ack and not self.receiveAck(sequence):
401
+ return False
402
+ else:
403
+ print("sleeping 0.01")
404
+ await sleep(0.01)
405
+
406
+ def getPostBytes(
407
+ self,
408
+ type: int,
409
+ encrypt: bool,
410
+ checksum: bool,
411
+ require_ack: bool,
412
+ hasFrag: bool,
413
+ sequence: int,
414
+ data: bytes,
415
+ ) -> bytes:
416
+ byteOS = BytesIO()
417
+ dataLength = 0 if data == None else len(data)
418
+ frameCtrl = FrameCtrlData.getFrameCTRLValue(
419
+ encrypt, checksum, 0, require_ack, hasFrag
420
+ )
421
+ byteOS.write(type.to_bytes(1, sys.byteorder))
422
+ byteOS.write(frameCtrl.to_bytes(1, sys.byteorder))
423
+ byteOS.write(sequence.to_bytes(1, sys.byteorder))
424
+ byteOS.write(dataLength.to_bytes(1, sys.byteorder))
425
+
426
+ if data != None:
427
+ byteOS.write(data)
428
+
429
+ print(byteOS.getvalue())
430
+ return byteOS.getvalue()
@@ -0,0 +1,27 @@
1
+ MODEL_NBR_UUID = "0000ff02-0000-1000-8000-00805f9b34fb"
2
+
3
+ UART_SERVICE_UUID = "0000ffff-0000-1000-8000-00805f9b34fb"
4
+ UART_RX_CHAR_UUID = "0000ff01-0000-1000-8000-00805f9b34fb"
5
+ UART_TX_CHAR_UUID = "0000ff02-0000-1000-8000-00805f9b34fb"
6
+
7
+
8
+ # 01-31 14:06:23.761 21981 22174 E EspBleUtil: ServiceName:00001801-0000-1000-8000-00805f9b34fb
9
+ # 01-31 14:06:23.761 21981 22174 E EspBleUtil: ---CharacterName:00002a05-0000-1000-8000-00805f9b34fb
10
+ # 01-31 14:06:23.761 21981 22174 E EspBleUtil: ServiceName:00001800-0000-1000-8000-00805f9b34fb
11
+ # 01-31 14:06:23.761 21981 22174 E EspBleUtil: ---CharacterName:00002a00-0000-1000-8000-00805f9b34fb
12
+ # 01-31 14:06:23.761 21981 22174 E EspBleUtil: ---CharacterName:00002a01-0000-1000-8000-00805f9b34fb
13
+ # 01-31 14:06:23.761 21981 22174 E EspBleUtil: ---CharacterName:00002aa6-0000-1000-8000-00805f9b34fb
14
+ # 01-31 14:06:23.761 21981 22174 E EspBleUtil: ServiceName:0000ffff-0000-1000-8000-00805f9b34fb
15
+ # 01-31 14:06:23.762 21981 22174 E EspBleUtil: ---CharacterName:0000ff01-0000-1000-8000-00805f9b34fb
16
+ # 01-31 14:06:23.762 21981 22174 E EspBleUtil: ---CharacterName:0000ff02-0000-1000-8000-00805f9b34fb
17
+
18
+ UUID_SERVICE = "0000ffff-0000-1000-8000-00805f9b34fb"
19
+ UUID_WRITE_CHARACTERISTIC = "0000ff01-0000-1000-8000-00805f9b34fb"
20
+ UUID_NOTIFICATION_CHARACTERISTIC = "0000ff02-0000-1000-8000-00805f9b34fb"
21
+ UUID_NOTIFICATION_DESCRIPTOR = "00002902-0000-1000-8000-00805f9b34fb"
22
+
23
+ CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID = "00002902-0000-1000-8000-00805f9b34fb"
24
+ BATTERY_SERVICE = "0000180F-0000-1000-8000-00805f9b34fb"
25
+ BATTERY_LEVEL_CHARACTERISTIC = "00002A19-0000-1000-8000-00805f9b34fb"
26
+ GENERIC_ATTRIBUTE_SERVICE = "00001801-0000-1000-8000-00805f9b34fb"
27
+ SERVICE_CHANGED_CHARACTERISTIC = "00002A05-0000-1000-8000-00805f9b34fb"
File without changes
@@ -0,0 +1,26 @@
1
+ from google.protobuf.message import DecodeError
2
+ from pyluba.proto import luba_msg_pb2
3
+
4
+
5
+ def parse_custom_data(data: bytes):
6
+ """Convert data into protobuf message."""
7
+ luba_msg = luba_msg_pb2.LubaMsg()
8
+ try:
9
+ luba_msg.ParseFromString(data)
10
+ return luba_msg
11
+
12
+ except DecodeError as err:
13
+ print(err)
14
+
15
+
16
+ def store_sys_data(sys):
17
+ if sys.HasField("systemTardStateTunnel"):
18
+ tard_state_data_list = sys.systemTardStateTunnel.tard_state_data
19
+ longValue8 = tard_state_data_list[0]
20
+ longValue9 = tard_state_data_list[1]
21
+ print(
22
+ "Device status report,deviceState:", longValue8, ",deviceName:", "Luba..."
23
+ )
24
+ chargeStateTemp = longValue9
25
+ longValue10 = tard_state_data_list[6]
26
+ longValue11 = tard_state_data_list[7]
@@ -0,0 +1,40 @@
1
+ class FrameCtrlData:
2
+ FRAME_CTRL_POSITION_CHECKSUM = 1
3
+ FRAME_CTRL_POSITION_DATA_DIRECTION = 2
4
+ FRAME_CTRL_POSITION_ENCRYPTED = 0
5
+ FRAME_CTRL_POSITION_FRAG = 4
6
+ FRAME_CTRL_POSITION_REQUIRE_ACK = 3
7
+ mValue = 0
8
+
9
+ def __init__(self, frameCtrlValue):
10
+ self.mValue = frameCtrlValue
11
+
12
+ def check(self, position):
13
+ return ((self.mValue >> position) & 1) == 1
14
+
15
+ def isEncrypted(self):
16
+ return self.check(0)
17
+
18
+ def isChecksum(self):
19
+ return self.check(1)
20
+
21
+ def isAckRequirement(self):
22
+ return self.check(3)
23
+
24
+ def hasFrag(self):
25
+ return self.check(4)
26
+
27
+ @staticmethod
28
+ def getFrameCTRLValue(encrypted, checksum, direction, requireAck, frag):
29
+ frame = 0
30
+ if encrypted:
31
+ frame = 0 | 1
32
+ if checksum:
33
+ frame |= 2
34
+ if direction == 1:
35
+ frame |= 4
36
+ if requireAck:
37
+ frame |= 8
38
+ if frag:
39
+ return frame | 16
40
+ return frame
@@ -0,0 +1,63 @@
1
+ from io import BytesIO
2
+
3
+ """Notify data object"""
4
+
5
+
6
+ class BlufiNotifyData:
7
+ """generated source for class BlufiNotifyData"""
8
+
9
+ def __init__(self):
10
+ self.mDataOS = BytesIO()
11
+ self.mFrameCtrlValue = 0
12
+ self.mPkgType = 0
13
+ self.mSubType = 0
14
+ self.mTypeValue = 0
15
+
16
+ def getType(self):
17
+ """Generated source for method getType"""
18
+ return self.mTypeValue
19
+
20
+ # JADX INFO: Access modifiers changed from: package-private
21
+ def setType(self, i):
22
+ """Generated source for method setType"""
23
+ self.mTypeValue = i
24
+
25
+ # JADX INFO: Access modifiers changed from: package-private
26
+ def getPkgType(self):
27
+ """Generated source for method getPkgType"""
28
+ return self.mPkgType
29
+
30
+ # JADX INFO: Access modifiers changed from: package-private
31
+ def setPkgType(self, i):
32
+ """Generated source for method setPkgType"""
33
+ self.mPkgType = i
34
+
35
+ # JADX INFO: Access modifiers changed from: package-private
36
+ def getSubType(self):
37
+ """Generated source for method getSubType"""
38
+ return self.mSubType
39
+
40
+ # JADX INFO: Access modifiers changed from: package-private
41
+ def setSubType(self, i):
42
+ """Generated source for method setSubType"""
43
+ self.mSubType = i
44
+
45
+ def getFrameCtrl(self):
46
+ """Generated source for method getFrameCtrl"""
47
+ return self.mFrameCtrlValue
48
+
49
+ # JADX INFO: Access modifiers changed from: package-private
50
+ def setFrameCtrl(self, i):
51
+ """Generated source for method setFrameCtrl"""
52
+ self.mFrameCtrlValue = i
53
+
54
+ # JADX INFO: Access modifiers changed from: package-private
55
+ def addData(self, bArr, i):
56
+ """Generated source for method addData"""
57
+ self.mDataOS.write(bArr[i:])
58
+
59
+ # JADX INFO: Access modifiers changed from: package-private
60
+ def getDataArray(self):
61
+ """Generated source for method getDataArray"""
62
+ print("data Array")
63
+ return self.mDataOS.getvalue()
pymammotion/const.py ADDED
@@ -0,0 +1,9 @@
1
+ """App key and secret as taken from the mammotion android app."""
2
+
3
+ APP_KEY = "34231230"
4
+ APP_SECRET = "1ba85698bb10e19c6437413b61ba3445"
5
+ APP_VERSION = "1.11.1220"
6
+ ALIYUN_DOMAIN = "api.link.aliyun.com"
7
+ MAMMOTION_DOMAIN = "https://domestic.mammotion.com"
8
+ MAMMOTION_CLIENT_ID = "MADKALUBAS"
9
+ MAMMOTION_CLIENT_SECRET = "GshzGRZJjuMUgd2sYHM7"
File without changes
@@ -0,0 +1,8 @@
1
+ """data models"""
2
+
3
+ from .generate_route_information import GenerateRouteInformation
4
+ from .hash_list import HashList
5
+ from .plan import Plan
6
+ from .rapid_state import RapidState, RTKStatus
7
+ from .region_data import RegionData
8
+