pymammotion 0.2.62__py3-none-any.whl → 0.5.51__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pymammotion might be problematic. Click here for more details.

Files changed (135) hide show
  1. pymammotion/__init__.py +9 -6
  2. pymammotion/aliyun/client.py +235 -0
  3. pymammotion/aliyun/cloud_gateway.py +320 -69
  4. pymammotion/aliyun/model/aep_response.py +1 -2
  5. pymammotion/aliyun/model/dev_by_account_response.py +170 -23
  6. pymammotion/aliyun/model/login_by_oauth_response.py +2 -3
  7. pymammotion/aliyun/model/regions_response.py +3 -3
  8. pymammotion/aliyun/model/session_by_authcode_response.py +2 -2
  9. pymammotion/aliyun/model/thing_response.py +12 -0
  10. pymammotion/aliyun/regions.py +62 -0
  11. pymammotion/aliyun/tea/core.py +297 -0
  12. pymammotion/bluetooth/ble.py +11 -15
  13. pymammotion/bluetooth/ble_message.py +389 -106
  14. pymammotion/bluetooth/model/atomic_integer.py +54 -0
  15. pymammotion/const.py +3 -0
  16. pymammotion/data/model/__init__.py +1 -2
  17. pymammotion/data/model/device.py +92 -240
  18. pymammotion/data/model/device_config.py +10 -24
  19. pymammotion/data/model/device_info.py +35 -0
  20. pymammotion/data/model/device_limits.py +49 -0
  21. pymammotion/data/model/enums.py +12 -2
  22. pymammotion/data/model/errors.py +12 -0
  23. pymammotion/data/model/events.py +14 -0
  24. pymammotion/data/model/generate_geojson.py +521 -0
  25. pymammotion/data/model/generate_route_information.py +3 -4
  26. pymammotion/data/model/hash_list.py +384 -48
  27. pymammotion/data/model/location.py +4 -4
  28. pymammotion/data/model/mowing_modes.py +24 -1
  29. pymammotion/data/model/raw_data.py +215 -0
  30. pymammotion/data/model/region_data.py +10 -11
  31. pymammotion/data/model/report_info.py +62 -6
  32. pymammotion/data/model/work.py +27 -0
  33. pymammotion/data/mower_state_manager.py +316 -0
  34. pymammotion/data/mqtt/event.py +73 -28
  35. pymammotion/data/mqtt/mammotion_properties.py +257 -0
  36. pymammotion/data/mqtt/properties.py +93 -78
  37. pymammotion/data/mqtt/status.py +18 -17
  38. pymammotion/event/event.py +32 -8
  39. pymammotion/homeassistant/__init__.py +3 -0
  40. pymammotion/homeassistant/mower_api.py +484 -0
  41. pymammotion/homeassistant/rtk_api.py +54 -0
  42. pymammotion/http/__init__.py +0 -0
  43. pymammotion/http/encryption.py +220 -0
  44. pymammotion/http/http.py +652 -44
  45. pymammotion/http/model/__init__.py +0 -0
  46. pymammotion/{aliyun/model/stream_subscription_response.py → http/model/camera_stream.py} +14 -2
  47. pymammotion/http/model/http.py +160 -9
  48. pymammotion/http/model/response_factory.py +61 -0
  49. pymammotion/http/model/rtk.py +16 -0
  50. pymammotion/mammotion/commands/abstract_message.py +7 -5
  51. pymammotion/mammotion/commands/mammotion_command.py +32 -3
  52. pymammotion/mammotion/commands/messages/basestation.py +43 -0
  53. pymammotion/mammotion/commands/messages/driver.py +61 -29
  54. pymammotion/mammotion/commands/messages/media.py +68 -15
  55. pymammotion/mammotion/commands/messages/navigation.py +61 -25
  56. pymammotion/mammotion/commands/messages/network.py +93 -100
  57. pymammotion/mammotion/commands/messages/ota.py +18 -18
  58. pymammotion/mammotion/commands/messages/system.py +97 -72
  59. pymammotion/mammotion/commands/messages/video.py +17 -12
  60. pymammotion/mammotion/devices/__init__.py +27 -3
  61. pymammotion/mammotion/devices/base.py +50 -127
  62. pymammotion/mammotion/devices/mammotion.py +447 -212
  63. pymammotion/mammotion/devices/mammotion_bluetooth.py +105 -60
  64. pymammotion/mammotion/devices/mammotion_cloud.py +157 -105
  65. pymammotion/mammotion/devices/mammotion_mower_ble.py +49 -0
  66. pymammotion/mammotion/devices/mammotion_mower_cloud.py +39 -0
  67. pymammotion/mammotion/devices/managers/managers.py +81 -0
  68. pymammotion/mammotion/devices/mower_device.py +124 -0
  69. pymammotion/mammotion/devices/mower_manager.py +107 -0
  70. pymammotion/mammotion/devices/rtk_ble.py +89 -0
  71. pymammotion/mammotion/devices/rtk_cloud.py +113 -0
  72. pymammotion/mammotion/devices/rtk_device.py +50 -0
  73. pymammotion/mammotion/devices/rtk_manager.py +122 -0
  74. pymammotion/mqtt/__init__.py +2 -1
  75. pymammotion/mqtt/aliyun_mqtt.py +232 -0
  76. pymammotion/mqtt/linkkit/__init__.py +5 -0
  77. pymammotion/mqtt/linkkit/h2client.py +585 -0
  78. pymammotion/mqtt/linkkit/linkkit.py +3023 -0
  79. pymammotion/mqtt/mammotion_mqtt.py +176 -169
  80. pymammotion/mqtt/mqtt_models.py +66 -0
  81. pymammotion/proto/__init__.py +4839 -4
  82. pymammotion/proto/basestation.proto +8 -0
  83. pymammotion/proto/basestation_pb2.py +11 -9
  84. pymammotion/proto/basestation_pb2.pyi +16 -2
  85. pymammotion/proto/dev_net.proto +79 -55
  86. pymammotion/proto/dev_net_pb2.py +60 -56
  87. pymammotion/proto/dev_net_pb2.pyi +49 -6
  88. pymammotion/proto/luba_msg.proto +2 -1
  89. pymammotion/proto/luba_msg_pb2.py +6 -6
  90. pymammotion/proto/luba_msg_pb2.pyi +1 -0
  91. pymammotion/proto/luba_mul.proto +62 -1
  92. pymammotion/proto/luba_mul_pb2.py +38 -22
  93. pymammotion/proto/luba_mul_pb2.pyi +94 -7
  94. pymammotion/proto/mctrl_driver.proto +44 -4
  95. pymammotion/proto/mctrl_driver_pb2.py +26 -14
  96. pymammotion/proto/mctrl_driver_pb2.pyi +66 -11
  97. pymammotion/proto/mctrl_nav.proto +97 -51
  98. pymammotion/proto/mctrl_nav_pb2.py +75 -67
  99. pymammotion/proto/mctrl_nav_pb2.pyi +142 -56
  100. pymammotion/proto/mctrl_ota.proto +40 -2
  101. pymammotion/proto/mctrl_ota_pb2.py +23 -13
  102. pymammotion/proto/mctrl_ota_pb2.pyi +67 -4
  103. pymammotion/proto/mctrl_pept.proto +8 -3
  104. pymammotion/proto/mctrl_pept_pb2.py +8 -6
  105. pymammotion/proto/mctrl_pept_pb2.pyi +14 -6
  106. pymammotion/proto/mctrl_sys.proto +325 -86
  107. pymammotion/proto/mctrl_sys_pb2.py +162 -98
  108. pymammotion/proto/mctrl_sys_pb2.pyi +451 -25
  109. pymammotion/proto/message_pool.py +3 -0
  110. pymammotion/proto/py.typed +0 -0
  111. pymammotion/utility/constant/device_constant.py +65 -21
  112. pymammotion/utility/datatype_converter.py +13 -12
  113. pymammotion/utility/device_config.py +755 -0
  114. pymammotion/utility/device_type.py +218 -21
  115. pymammotion/utility/map.py +238 -51
  116. pymammotion/utility/mur_mur_hash.py +159 -0
  117. {pymammotion-0.2.62.dist-info → pymammotion-0.5.51.dist-info}/METADATA +27 -31
  118. pymammotion-0.5.51.dist-info/RECORD +152 -0
  119. {pymammotion-0.2.62.dist-info → pymammotion-0.5.51.dist-info}/WHEEL +1 -1
  120. pymammotion/aliyun/cloud_service.py +0 -65
  121. pymammotion/data/model/plan.py +0 -58
  122. pymammotion/data/state_manager.py +0 -130
  123. pymammotion/proto/basestation.py +0 -59
  124. pymammotion/proto/common.py +0 -12
  125. pymammotion/proto/dev_net.py +0 -381
  126. pymammotion/proto/luba_msg.py +0 -81
  127. pymammotion/proto/luba_mul.py +0 -76
  128. pymammotion/proto/mctrl_driver.py +0 -100
  129. pymammotion/proto/mctrl_nav.py +0 -660
  130. pymammotion/proto/mctrl_ota.py +0 -48
  131. pymammotion/proto/mctrl_pept.py +0 -41
  132. pymammotion/proto/mctrl_sys.py +0 -574
  133. pymammotion-0.2.62.dist-info/RECORD +0 -125
  134. /pymammotion/{http/_init_.py → bluetooth/model/__init__.py} +0 -0
  135. {pymammotion-0.2.62.dist-info → pymammotion-0.5.51.dist-info/licenses}/LICENSE +0 -0
@@ -1,17 +1,13 @@
1
1
  from bleak import BleakClient, BleakScanner, BLEDevice
2
2
  from bleak.backends.characteristic import BleakGATTCharacteristic
3
3
 
4
- from pymammotion.bluetooth.const import (
5
- SERVICE_CHANGED_CHARACTERISTIC,
6
- UUID_NOTIFICATION_CHARACTERISTIC,
7
- )
4
+ from pymammotion.bluetooth.const import SERVICE_CHANGED_CHARACTERISTIC, UUID_NOTIFICATION_CHARACTERISTIC
8
5
  from pymammotion.event.event import BleNotificationEvent
9
6
 
10
- # TODO setup for each Luba
11
- address = "90:38:0C:6E:EE:9E"
12
-
13
7
 
14
8
  class MammotionBLE:
9
+ """Class for basic ble connections to mowers."""
10
+
15
11
  client: BleakClient
16
12
 
17
13
  def __init__(self, bleEvt: BleNotificationEvent) -> None:
@@ -24,27 +20,26 @@ class MammotionBLE:
24
20
  # TODO: do something with incoming data
25
21
  print(device)
26
22
  print(advertising_data)
27
- if device.address == "90:38:0C:6E:EE:9E":
28
- return True
29
- if advertising_data.local_name and "Luba-" in advertising_data.local_name:
23
+ if advertising_data.local_name and (
24
+ "Luba-" in advertising_data.local_name or "Yuka-" in advertising_data.local_name
25
+ ):
30
26
  return True
31
27
  return False
32
28
 
33
29
  device = await scanner.find_device_by_filter(scanCallback)
34
30
  if device is not None:
35
31
  return await self.create_client(device)
32
+ return False
36
33
 
37
34
  async def create_client(self, device: BLEDevice):
38
35
  self.client = BleakClient(device.address)
39
36
  return await self.connect()
40
37
 
41
38
  async def connect(self) -> bool:
42
- if self.client is not None:
43
- return await self.client.connect()
39
+ return await self.client.connect() if self.client is not None else False
44
40
 
45
41
  async def disconnect(self) -> bool:
46
- if self.client is not None:
47
- return await self.client.disconnect()
42
+ return await self.client.disconnect() if self.client is not None else False
48
43
 
49
44
  async def notification_handler(self, _characteristic: BleakGATTCharacteristic, data: bytearray) -> None:
50
45
  """Simple notification handler which prints the data received."""
@@ -62,5 +57,6 @@ class MammotionBLE:
62
57
  await self.client.start_notify(UUID_NOTIFICATION_CHARACTERISTIC, self.notification_handler)
63
58
  await self.client.start_notify(SERVICE_CHANGED_CHARACTERISTIC, self.service_changed_handler)
64
59
 
65
- def getClient(self):
60
+ def get_client(self):
61
+ """Returns the ble client."""
66
62
  return self.client
@@ -1,29 +1,285 @@
1
+ from asyncio import sleep
2
+ from io import BytesIO
1
3
  import itertools
2
4
  import json
3
5
  import logging
4
6
  import queue
5
7
  import sys
6
8
  import time
7
- from asyncio import sleep
8
- from io import BytesIO
9
9
 
10
10
  from bleak import BleakClient
11
11
  from jsonic.serializable import serialize
12
12
 
13
13
  from pymammotion.aliyun.tmp_constant import tmp_constant
14
14
  from pymammotion.bluetooth.const import UUID_WRITE_CHARACTERISTIC
15
- from pymammotion.bluetooth.data.convert import parse_custom_data
16
15
  from pymammotion.bluetooth.data.framectrldata import FrameCtrlData
17
16
  from pymammotion.bluetooth.data.notifydata import BlufiNotifyData
17
+ from pymammotion.bluetooth.model.atomic_integer import AtomicInteger
18
18
  from pymammotion.data.model.execute_boarder import ExecuteBorder
19
- from pymammotion.proto import (
20
- dev_net_pb2,
21
- luba_msg_pb2,
22
- )
19
+ from pymammotion.proto import DevNet, DrvDevInfoReq, LubaMsg, MsgAttr, MsgCmdType, MsgDevice
23
20
  from pymammotion.utility.constant.device_constant import bleOrderCmd
24
21
 
25
22
  _LOGGER = logging.getLogger(__name__)
26
23
 
24
+ CRC_TB = [
25
+ 0x0000,
26
+ 0x1021,
27
+ 0x2042,
28
+ 0x3063,
29
+ 0x4084,
30
+ 0x50A5,
31
+ 0x60C6,
32
+ 0x70E7,
33
+ 0x8108,
34
+ 0x9129,
35
+ 0xA14A,
36
+ 0xB16B,
37
+ 0xC18C,
38
+ 0xD1AD,
39
+ 0xE1CE,
40
+ 0xF1EF,
41
+ 0x1231,
42
+ 0x0210,
43
+ 0x3273,
44
+ 0x2252,
45
+ 0x52B5,
46
+ 0x4294,
47
+ 0x72F7,
48
+ 0x62D6,
49
+ 0x9339,
50
+ 0x8318,
51
+ 0xB37B,
52
+ 0xA35A,
53
+ 0xD3BD,
54
+ 0xC39C,
55
+ 0xF3FF,
56
+ 0xE3DE,
57
+ 0x2462,
58
+ 0x3443,
59
+ 0x0420,
60
+ 0x1401,
61
+ 0x64E6,
62
+ 0x74C7,
63
+ 0x44A4,
64
+ 0x5485,
65
+ 0xA56A,
66
+ 0xB54B,
67
+ 0x8528,
68
+ 0x9509,
69
+ 0xE5EE,
70
+ 0xF5CF,
71
+ 0xC5AC,
72
+ 0xD58D,
73
+ 0x3653,
74
+ 0x2672,
75
+ 0x1611,
76
+ 0x0630,
77
+ 0x76D7,
78
+ 0x66F6,
79
+ 0x5695,
80
+ 0x46B4,
81
+ 0xB75B,
82
+ 0xA77A,
83
+ 0x9719,
84
+ 0x8738,
85
+ 0xF7DF,
86
+ 0xE7FE,
87
+ 0xD79D,
88
+ 0xC7BC,
89
+ 0x48C4,
90
+ 0x58E5,
91
+ 0x6886,
92
+ 0x78A7,
93
+ 0x0840,
94
+ 0x1861,
95
+ 0x2802,
96
+ 0x3823,
97
+ 0xC9CC,
98
+ 0xD9ED,
99
+ 0xE98E,
100
+ 0xF9AF,
101
+ 0x8948,
102
+ 0x9969,
103
+ 0xA90A,
104
+ 0xB92B,
105
+ 0x5AF5,
106
+ 0x4AD4,
107
+ 0x7AB7,
108
+ 0x6A96,
109
+ 0x1A71,
110
+ 0x0A50,
111
+ 0x3A33,
112
+ 0x2A12,
113
+ 0xDBFD,
114
+ 0xCBDC,
115
+ 0xFBBF,
116
+ 0xEB9E,
117
+ 0x9B79,
118
+ 0x8B58,
119
+ 0xBB3B,
120
+ 0xAB1A,
121
+ 0x6CA6,
122
+ 0x7C87,
123
+ 0x4CE4,
124
+ 0x5CC5,
125
+ 0x2C22,
126
+ 0x3C03,
127
+ 0x0C60,
128
+ 0x1C41,
129
+ 0xEDAE,
130
+ 0xFD8F,
131
+ 0xCDEC,
132
+ 0xDDCD,
133
+ 0xAD2A,
134
+ 0xBD0B,
135
+ 0x8D68,
136
+ 0x9D49,
137
+ 0x7E97,
138
+ 0x6EB6,
139
+ 0x5ED5,
140
+ 0x4EF4,
141
+ 0x3E13,
142
+ 0x2E32,
143
+ 0x1E51,
144
+ 0x0E70,
145
+ 0xFF9F,
146
+ 0xEFBE,
147
+ 0xDFDD,
148
+ 0xCFFC,
149
+ 0xBF1B,
150
+ 0xAF3A,
151
+ 0x9F59,
152
+ 0x8F78,
153
+ 0x9188,
154
+ 0x81A9,
155
+ 0xB1CA,
156
+ 0xA1EB,
157
+ 0xD10C,
158
+ 0xC12D,
159
+ 0xF14E,
160
+ 0xE16F,
161
+ 0x1080,
162
+ 0x00A1,
163
+ 0x30C2,
164
+ 0x20E3,
165
+ 0x5004,
166
+ 0x4025,
167
+ 0x7046,
168
+ 0x6067,
169
+ 0x83B9,
170
+ 0x9398,
171
+ 0xA3FB,
172
+ 0xB3DA,
173
+ 0xC33D,
174
+ 0xD31C,
175
+ 0xE37F,
176
+ 0xF35E,
177
+ 0x02B1,
178
+ 0x1290,
179
+ 0x22F3,
180
+ 0x32D2,
181
+ 0x4235,
182
+ 0x5214,
183
+ 0x6277,
184
+ 0x7256,
185
+ 0xB5EA,
186
+ 0xA5CB,
187
+ 0x95A8,
188
+ 0x8589,
189
+ 0xF56E,
190
+ 0xE54F,
191
+ 0xD52C,
192
+ 0xC50D,
193
+ 0x34E2,
194
+ 0x24C3,
195
+ 0x14A0,
196
+ 0x0481,
197
+ 0x7466,
198
+ 0x6447,
199
+ 0x5424,
200
+ 0x4405,
201
+ 0xA7DB,
202
+ 0xB7FA,
203
+ 0x8799,
204
+ 0x97B8,
205
+ 0xE75F,
206
+ 0xF77E,
207
+ 0xC71D,
208
+ 0xD73C,
209
+ 0x26D3,
210
+ 0x36F2,
211
+ 0x0691,
212
+ 0x16B0,
213
+ 0x6657,
214
+ 0x7676,
215
+ 0x4615,
216
+ 0x5634,
217
+ 0xD94C,
218
+ 0xC96D,
219
+ 0xF90E,
220
+ 0xE92F,
221
+ 0x99C8,
222
+ 0x89E9,
223
+ 0xB98A,
224
+ 0xA9AB,
225
+ 0x5844,
226
+ 0x4865,
227
+ 0x7806,
228
+ 0x6827,
229
+ 0x18C0,
230
+ 0x08E1,
231
+ 0x3882,
232
+ 0x28A3,
233
+ 0xCB7D,
234
+ 0xDB5C,
235
+ 0xEB3F,
236
+ 0xFB1E,
237
+ 0x8BF9,
238
+ 0x9BD8,
239
+ 0xABBB,
240
+ 0xBB9A,
241
+ 0x4A75,
242
+ 0x5A54,
243
+ 0x6A37,
244
+ 0x7A16,
245
+ 0x0AF1,
246
+ 0x1AD0,
247
+ 0x2AB3,
248
+ 0x3A92,
249
+ 0xFD2E,
250
+ 0xED0F,
251
+ 0xDD6C,
252
+ 0xCD4D,
253
+ 0xBDAA,
254
+ 0xAD8B,
255
+ 0x9DE8,
256
+ 0x8DC9,
257
+ 0x7C26,
258
+ 0x6C07,
259
+ 0x5C64,
260
+ 0x4C45,
261
+ 0x3CA2,
262
+ 0x2C83,
263
+ 0x1CE0,
264
+ 0x0CC1,
265
+ 0xEF1F,
266
+ 0xFF3E,
267
+ 0xCF5D,
268
+ 0xDF7C,
269
+ 0xAF9B,
270
+ 0xBFBA,
271
+ 0x8FD9,
272
+ 0x9FF8,
273
+ 0x6E17,
274
+ 0x7E36,
275
+ 0x4E55,
276
+ 0x5E74,
277
+ 0x2E93,
278
+ 0x3EB2,
279
+ 0x0ED1,
280
+ 0x1EF0,
281
+ ]
282
+
27
283
 
28
284
  class BleMessage:
29
285
  """Class for sending and recieving messages from Luba"""
@@ -44,15 +300,11 @@ class BleMessage:
44
300
  mChecksum = False
45
301
  mRequireAck = False
46
302
  mConnectState = 0
47
- mSendSequence: iter
48
- mReadSequence: iter
49
- mAck: queue
50
- notification: BlufiNotifyData
51
303
 
52
304
  def __init__(self, client: BleakClient) -> None:
53
305
  self.client = client
54
- self.mSendSequence = itertools.count()
55
- self.mReadSequence = itertools.count()
306
+ self.mSendSequence = AtomicInteger(-1)
307
+ self.mReadSequence = AtomicInteger(-1)
56
308
  self.mAck = queue.Queue()
57
309
  self.notification = BlufiNotifyData()
58
310
 
@@ -64,7 +316,7 @@ class BleMessage:
64
316
  hash_map = {"ctrl": 1}
65
317
  await self.post_custom_data(self.get_json_string(bleOrderCmd.bleAlive, hash_map))
66
318
 
67
- def get_json_string(self, cmd: int, hash_map: dict[str, object]) -> str:
319
+ def get_json_string(self, cmd: int, hash_map: dict[str, int]) -> str:
68
320
  jSONObject = {}
69
321
  try:
70
322
  jSONObject["cmd"] = cmd
@@ -78,7 +330,7 @@ class BleMessage:
78
330
  print(e)
79
331
  return ""
80
332
 
81
- def clearNotification(self) -> None:
333
+ def clear_notification(self) -> None:
82
334
  self.notification = None
83
335
  self.notification = BlufiNotifyData()
84
336
 
@@ -87,15 +339,15 @@ class BleMessage:
87
339
 
88
340
  async def send_device_info(self) -> None:
89
341
  """Currently not called"""
90
- luba_msg = luba_msg_pb2.LubaMsg(
91
- msgtype=luba_msg_pb2.MsgCmdType.MSG_CMD_TYPE_ESP,
92
- sender=luba_msg_pb2.MsgDevice.DEV_MOBILEAPP,
93
- rcver=luba_msg_pb2.MsgDevice.DEV_COMM_ESP,
94
- msgattr=luba_msg_pb2.MsgAttr.MSG_ATTR_REQ,
342
+ luba_msg = LubaMsg(
343
+ msgtype=MsgCmdType.ESP,
344
+ sender=MsgDevice.DEV_MOBILEAPP,
345
+ rcver=MsgDevice.DEV_COMM_ESP,
346
+ msgattr=MsgAttr.REQ,
95
347
  seqs=1,
96
348
  version=1,
97
349
  subtype=1,
98
- net=dev_net_pb2.DevNet(todev_ble_sync=1, todev_devinfo_req=dev_net_pb2.DrvDevInfoReq()),
350
+ net=DevNet(todev_ble_sync=1, todev_devinfo_req=DrvDevInfoReq()),
99
351
  )
100
352
  byte_arr = luba_msg.SerializeToString()
101
353
  await self.post_custom_data_bytes(byte_arr)
@@ -132,7 +384,7 @@ class BleMessage:
132
384
  await self.client.write_gatt_char(UUID_WRITE_CHARACTERISTIC, data, True)
133
385
 
134
386
  def parseNotification(self, response: bytearray):
135
- dataOffset = None
387
+ """Parse notification data from BLE device."""
136
388
  if response is None:
137
389
  # Log.w(TAG, "parseNotification null data");
138
390
  return -1
@@ -140,72 +392,70 @@ class BleMessage:
140
392
  # if (this.mPrintDebug):
141
393
  # Log.d(TAG, "parseNotification Notification= " + Arrays.toString(response));
142
394
  # }
143
- if len(response) >= 4:
144
- sequence = int(response[2]) # toInt
145
- if sequence != next(self.mReadSequence):
146
- _LOGGER.debug(
147
- "parseNotification read sequence wrong",
148
- sequence,
149
- self.mReadSequence,
150
- )
151
- self.mReadSequence = itertools.count(start=sequence)
152
- # this is questionable
153
- # self.mReadSequence = sequence
154
- # self.mReadSequence_2.incrementAndGet()
155
-
156
- # LogUtil.m7773e(self.mGatt.getDevice().getName() + "打印丢包率", self.mReadSequence_2 + "/" + self.mReadSequence_1);
157
- pkt_type = int(response[0]) # toInt
158
- pkgType = self._getPackageType(pkt_type)
159
- subType = self._getSubType(pkt_type)
160
- self.notification.setType(pkt_type)
161
- self.notification.setPkgType(pkgType)
162
- self.notification.setSubType(subType)
163
- frameCtrl = int(response[1]) # toInt
164
- # _LOGGER.debug("frame ctrl")
165
- # _LOGGER.debug(frameCtrl)
166
- # _LOGGER.debug(response)
167
- # _LOGGER.debug(f"pktType {pkt_type} pkgType {pkgType} subType {subType}")
168
- self.notification.setFrameCtrl(frameCtrl)
169
- frameCtrlData = FrameCtrlData(frameCtrl)
170
- dataLen = int(response[3]) # toInt specifies length of data
171
-
172
- try:
173
- dataBytes = response[4 : 4 + dataLen]
174
- if frameCtrlData.isEncrypted():
175
- _LOGGER.debug("is encrypted")
176
- # BlufiAES aes = new BlufiAES(self.mAESKey, AES_TRANSFORMATION, generateAESIV(sequence));
177
- # dataBytes = aes.decrypt(dataBytes);
178
- # }
179
- if frameCtrlData.isChecksum():
180
- _LOGGER.debug("checksum")
181
- # int respChecksum1 = toInt(response[response.length - 1]);
182
- # int respChecksum2 = toInt(response[response.length - 2]);
183
- # int crc = BlufiCRC.calcCRC(BlufiCRC.calcCRC(0, new byte[]{(byte) sequence, (byte) dataLen}), dataBytes);
184
- # int calcChecksum1 = (crc >> 8) & 255;
185
- # int calcChecksum2 = crc & 255;
186
- # if (respChecksum1 != calcChecksum1 || respChecksum2 != calcChecksum2) {
187
- # Log.w(TAG, "parseNotification: read invalid checksum");
188
- # if (self.mPrintDebug) {
189
- # Log.d(TAG, "expect checksum: " + respChecksum1 + ", " + respChecksum2);
190
- # Log.d(TAG, "received checksum: " + calcChecksum1 + ", " + calcChecksum2);
191
- # return -4;
192
- # }
193
- # return -4;
194
- # }
195
- # }
196
- if frameCtrlData.hasFrag():
197
- dataOffset = 2
198
- else:
199
- dataOffset = 0
200
-
201
- self.notification.addData(dataBytes, dataOffset)
202
- return 1 if frameCtrlData.hasFrag() else 0
203
- except Exception as e:
204
- _LOGGER.debug(e)
205
- return -100
206
-
207
- # Log.w(TAG, "parseNotification data length less than 4");
208
- return -2
395
+ if len(response) < 4:
396
+ _LOGGER.debug("parseNotification data length less than 4")
397
+ return -2
398
+
399
+ sequence = int(response[2]) # toInt
400
+ current_sequence = self.mReadSequence.get() & 255
401
+ if sequence == current_sequence:
402
+ # _LOGGER.debug(f"Received bluetooth data 1: {response.hex()}, object: {self}")
403
+ return 2
404
+
405
+ # Compare with the second counter, mod 255
406
+ if sequence != (self.mReadSequence.increment_and_get() & 255):
407
+ _LOGGER.debug(
408
+ "parseNotification read sequence wrong %s %s",
409
+ sequence,
410
+ self.mReadSequence.get(),
411
+ )
412
+ # Set the value for mReadSequence manually
413
+ self.mReadSequence.set(sequence)
414
+
415
+ # LogUtil.m7773e(self.mGatt.getDevice().getName() + "打印丢包率", self.mReadSequence_2 + "/" + self.mReadSequence_1);
416
+ pkt_type = int(response[0]) # toInt
417
+ pkgType = self._getPackageType(pkt_type)
418
+ subType = self._getSubType(pkt_type)
419
+ self.notification.setType(pkt_type)
420
+ self.notification.setPkgType(pkgType)
421
+ self.notification.setSubType(subType)
422
+ frameCtrl = int(response[1]) # toInt
423
+ # _LOGGER.debug("frame ctrl")
424
+ # _LOGGER.debug(frameCtrl)
425
+ # _LOGGER.debug(response)
426
+ # _LOGGER.debug(f"pktType {pkt_type} pkgType {pkgType} subType {subType}")
427
+ self.notification.setFrameCtrl(frameCtrl)
428
+ frameCtrlData = FrameCtrlData(frameCtrl)
429
+ dataLen = int(response[3]) # toInt specifies length of data
430
+
431
+ try:
432
+ dataBytes = response[4 : 4 + dataLen]
433
+ if frameCtrlData.isEncrypted():
434
+ _LOGGER.debug("is encrypted")
435
+ # BlufiAES aes = new BlufiAES(self.mAESKey, AES_TRANSFORMATION, generateAESIV(sequence));
436
+ # dataBytes = aes.decrypt(dataBytes);
437
+ # }
438
+ if frameCtrlData.isChecksum():
439
+ respChecksum1 = int(response[-1])
440
+ respChecksum2 = int(response[-2])
441
+ crc = self.calc_crc(self.calc_crc(0, bytes([sequence, dataLen])), dataBytes)
442
+ calcChecksum1 = (crc >> 8) & 255
443
+ calcChecksum2 = crc & 255
444
+
445
+ if respChecksum1 != calcChecksum1 or respChecksum2 != calcChecksum2:
446
+ _LOGGER.debug(
447
+ f"expect checksum: {respChecksum1}, {respChecksum2}\n"
448
+ f"received checksum: {calcChecksum1}, {calcChecksum2}"
449
+ )
450
+ return -4
451
+
452
+ data_offset = 2 if frameCtrlData.hasFrag() else 0
453
+
454
+ self.notification.addData(dataBytes, data_offset)
455
+ return 1 if frameCtrlData.hasFrag() else 0
456
+ except Exception as e:
457
+ _LOGGER.debug(e)
458
+ return -100
209
459
 
210
460
  async def parseBlufiNotifyData(self, return_bytes: bool = False):
211
461
  pkgType = self.notification.getPkgType()
@@ -245,14 +495,8 @@ class BleMessage:
245
495
  # return;
246
496
  case 19:
247
497
  # # com/agilexrobotics/utils/EspBleUtil$BlufiCallbackMain.smali
248
- luba_msg = parse_custom_data(data) # parse to protobuf message
249
- # really need some sort of callback
250
- if luba_msg.HasField("net"):
251
- if luba_msg.net.HasField("toapp_wifi_iot_status"):
252
- # await sleep(1.5)
253
- _LOGGER.debug("sending ble sync")
254
- # await self.send_todev_ble_sync(2)
255
- return luba_msg
498
+ # luba_msg = parse_custom_data(data) # parse to protobuf message
499
+ return data
256
500
 
257
501
  # private void parseCtrlData(int i, byte[] bArr) {
258
502
  # if (i == 0) {
@@ -287,17 +531,17 @@ class BleMessage:
287
531
 
288
532
  def receiveAck(self, expectAck: int) -> bool:
289
533
  try:
290
- ack = next(self.mAck)
534
+ ack = self.mAck.get()
291
535
  return ack == expectAck
292
536
  except Exception as err:
293
537
  _LOGGER.debug(err)
294
538
  return False
295
539
 
296
- def generateSendSequence(self):
297
- return next(self.mSendSequence) & 255
540
+ def generate_send_sequence(self) -> int:
541
+ return self.mSendSequence.increment_and_get() & 255
298
542
 
299
543
  async def post_custom_data_bytes(self, data: bytes) -> None:
300
- if data == None:
544
+ if data is None:
301
545
  return
302
546
  type_val = self.getTypeValue(1, 19)
303
547
  try:
@@ -306,6 +550,7 @@ class BleMessage:
306
550
  # onPostCustomDataResult(status, data)
307
551
  # _LOGGER.debug(suc)
308
552
  except Exception as err:
553
+ await self.client.disconnect()
309
554
  _LOGGER.debug(err)
310
555
 
311
556
  async def post_custom_data(self, data_str: str) -> None:
@@ -338,7 +583,7 @@ class BleMessage:
338
583
  return await self.post_contains_data(encrypt, checksum, require_ack, type_of, data)
339
584
 
340
585
  async def post_non_data(self, encrypt: bool, checksum: bool, require_ack: bool, type_of: int) -> bool:
341
- sequence = self.generateSendSequence()
586
+ sequence = self.generate_send_sequence()
342
587
  postBytes = self.getPostBytes(type_of, encrypt, checksum, require_ack, False, sequence, None)
343
588
  posted = await self.gatt_write(postBytes)
344
589
  return posted and (not require_ack or self.receiveAck(sequence))
@@ -361,22 +606,26 @@ class BleMessage:
361
606
  chunks.append(data[i : i + chunk_size])
362
607
  for index, chunk in enumerate(chunks):
363
608
  frag = index != len(chunks) - 1
364
- sequence = self.generateSendSequence()
609
+ sequence = self.generate_send_sequence()
365
610
  postBytes = self.getPostBytes(type_of, encrypt, checksum, require_ack, frag, sequence, chunk)
366
611
  # _LOGGER.debug("sequence")
367
612
  # _LOGGER.debug(sequence)
368
613
  posted = await self.gatt_write(postBytes)
369
- if posted != None:
614
+ if posted is not None:
370
615
  return False
371
616
 
372
617
  if not frag:
373
618
  return not require_ack or self.receiveAck(sequence)
374
619
 
620
+ if require_ack and not self.receiveAck(sequence):
621
+ return False
622
+
623
+ _LOGGER.debug("sleeping 0.01")
624
+ await sleep(0.01)
375
625
  if require_ack and not self.receiveAck(sequence):
376
626
  return False
377
627
  else:
378
- _LOGGER.debug("sleeping 0.01")
379
- await sleep(0.01)
628
+ return True
380
629
 
381
630
  def getPostBytes(
382
631
  self,
@@ -396,8 +645,42 @@ class BleMessage:
396
645
  byteOS.write(sequence.to_bytes(1, sys.byteorder))
397
646
  byteOS.write(dataLength.to_bytes(1, sys.byteorder))
398
647
 
399
- if data != None:
648
+ if data is not None:
400
649
  byteOS.write(data)
401
650
 
402
651
  _LOGGER.debug(byteOS.getvalue())
403
652
  return byteOS.getvalue()
653
+
654
+ @staticmethod
655
+ def calc_crc(initial: int, data: bytes | bytearray) -> int:
656
+ """Calculate CRC value for given initial value and byte array.
657
+
658
+ Args:
659
+ initial: Initial CRC value
660
+ data: Bytes to calculate CRC for
661
+
662
+ Returns:
663
+ Calculated CRC value (16-bit)
664
+
665
+ Raises:
666
+ TypeError: If data is not bytes or bytearray
667
+ ValueError: If initial value is out of valid range
668
+
669
+ """
670
+ if not isinstance(data, (bytes, bytearray)):
671
+ raise TypeError("Data must be bytes or bytearray")
672
+
673
+ if not 0 <= initial <= 0xFFFF:
674
+ raise ValueError("Initial value must be between 0 and 65535")
675
+
676
+ try:
677
+ crc = (~initial) & 0xFFFF
678
+
679
+ for byte in data:
680
+ crc = ((crc << 8) ^ CRC_TB[byte ^ (crc >> 8)]) & 0xFFFF
681
+
682
+ return (~crc) & 0xFFFF
683
+
684
+ except Exception as e:
685
+ _LOGGER.error("Error calculating CRC: %s", str(e))
686
+ raise