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.
- pymammotion/__init__.py +43 -0
- pymammotion/aliyun/cloud_gateway.py +549 -0
- pymammotion/aliyun/cloud_service.py +65 -0
- pymammotion/aliyun/dataclass/aep_response.py +18 -0
- pymammotion/aliyun/dataclass/connect_response.py +51 -0
- pymammotion/aliyun/dataclass/dev_by_account_response.py +43 -0
- pymammotion/aliyun/dataclass/login_by_oauth_response.py +65 -0
- pymammotion/aliyun/dataclass/regions_response.py +26 -0
- pymammotion/aliyun/dataclass/session_by_authcode_response.py +18 -0
- pymammotion/aliyun/tmp_constant.py +175 -0
- pymammotion/bluetooth/__init__.py +1 -0
- pymammotion/bluetooth/ble.py +74 -0
- pymammotion/bluetooth/ble_message.py +430 -0
- pymammotion/bluetooth/const.py +27 -0
- pymammotion/bluetooth/data/__init__.py +0 -0
- pymammotion/bluetooth/data/convert.py +26 -0
- pymammotion/bluetooth/data/framectrldata.py +40 -0
- pymammotion/bluetooth/data/notifydata.py +63 -0
- pymammotion/const.py +9 -0
- pymammotion/data/__init__.py +0 -0
- pymammotion/data/model/__init__.py +8 -0
- pymammotion/data/model/device.py +157 -0
- pymammotion/data/model/enums.py +67 -0
- pymammotion/data/model/excute_boarder_params.py +48 -0
- pymammotion/data/model/execute_boarder.py +36 -0
- pymammotion/data/model/generate_route_information.py +133 -0
- pymammotion/data/model/hash_list.py +17 -0
- pymammotion/data/model/mowing_modes.py +37 -0
- pymammotion/data/model/plan.py +58 -0
- pymammotion/data/model/rapid_state.py +45 -0
- pymammotion/data/model/region_data.py +99 -0
- pymammotion/data/mqtt/__init__.py +1 -0
- pymammotion/data/mqtt/event.py +90 -0
- pymammotion/data/mqtt/properties.py +140 -0
- pymammotion/data/mqtt/status.py +52 -0
- pymammotion/event/__init__.py +6 -0
- pymammotion/event/event.py +50 -0
- pymammotion/http/_init_.py +0 -0
- pymammotion/http/http.py +76 -0
- pymammotion/luba/_init_.py +0 -0
- pymammotion/luba/base.py +52 -0
- pymammotion/mammotion/__init__.py +0 -0
- pymammotion/mammotion/commands/__init__.py +0 -0
- pymammotion/mammotion/commands/abstract_message.py +7 -0
- pymammotion/mammotion/commands/mammotion_command.py +34 -0
- pymammotion/mammotion/commands/messages/__init__.py +0 -0
- pymammotion/mammotion/commands/messages/driver.py +108 -0
- pymammotion/mammotion/commands/messages/media.py +36 -0
- pymammotion/mammotion/commands/messages/navigation.py +535 -0
- pymammotion/mammotion/commands/messages/network.py +236 -0
- pymammotion/mammotion/commands/messages/ota.py +34 -0
- pymammotion/mammotion/commands/messages/system.py +266 -0
- pymammotion/mammotion/commands/messages/video.py +27 -0
- pymammotion/mammotion/control/__init__.py +0 -0
- pymammotion/mammotion/control/joystick.py +184 -0
- pymammotion/mammotion/devices/__init__.py +1 -0
- pymammotion/mammotion/devices/luba.py +564 -0
- pymammotion/mqtt/mqtt.py +230 -0
- pymammotion/proto/__init__.py +0 -0
- pymammotion/proto/common.proto +7 -0
- pymammotion/proto/common.py +12 -0
- pymammotion/proto/common_pb2.py +25 -0
- pymammotion/proto/common_pb2.pyi +13 -0
- pymammotion/proto/dev_net.proto +297 -0
- pymammotion/proto/dev_net.py +381 -0
- pymammotion/proto/dev_net_pb2.py +107 -0
- pymammotion/proto/dev_net_pb2.pyi +472 -0
- pymammotion/proto/luba_msg.proto +73 -0
- pymammotion/proto/luba_msg.py +80 -0
- pymammotion/proto/luba_msg_pb2.py +40 -0
- pymammotion/proto/luba_msg_pb2.pyi +93 -0
- pymammotion/proto/luba_mul.proto +68 -0
- pymammotion/proto/luba_mul.py +76 -0
- pymammotion/proto/luba_mul_pb2.py +45 -0
- pymammotion/proto/luba_mul_pb2.pyi +91 -0
- pymammotion/proto/mctrl_driver.proto +67 -0
- pymammotion/proto/mctrl_driver.py +100 -0
- pymammotion/proto/mctrl_driver_pb2.py +45 -0
- pymammotion/proto/mctrl_driver_pb2.pyi +112 -0
- pymammotion/proto/mctrl_nav.proto +485 -0
- pymammotion/proto/mctrl_nav.py +589 -0
- pymammotion/proto/mctrl_nav_pb2.py +116 -0
- pymammotion/proto/mctrl_nav_pb2.pyi +875 -0
- pymammotion/proto/mctrl_ota.proto +42 -0
- pymammotion/proto/mctrl_ota.py +48 -0
- pymammotion/proto/mctrl_ota_pb2.py +35 -0
- pymammotion/proto/mctrl_ota_pb2.pyi +65 -0
- pymammotion/proto/mctrl_pept.proto +29 -0
- pymammotion/proto/mctrl_pept.py +41 -0
- pymammotion/proto/mctrl_pept_pb2.py +31 -0
- pymammotion/proto/mctrl_pept_pb2.pyi +50 -0
- pymammotion/proto/mctrl_sys.proto +487 -0
- pymammotion/proto/mctrl_sys.py +574 -0
- pymammotion/proto/mctrl_sys_pb2.py +142 -0
- pymammotion/proto/mctrl_sys_pb2.pyi +787 -0
- pymammotion/py.typed +0 -0
- pymammotion/utility/constant/__init__.py +1 -0
- pymammotion/utility/constant/device_constant.py +238 -0
- pymammotion/utility/datatype_converter.py +80 -0
- pymammotion/utility/device_type.py +152 -0
- pymammotion/utility/periodic.py +41 -0
- pymammotion/utility/rocker_util.py +135 -0
- pymammotion-0.0.37.dist-info/LICENSE +674 -0
- pymammotion-0.0.37.dist-info/METADATA +92 -0
- pymammotion-0.0.37.dist-info/RECORD +106 -0
- 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
|