sensor-sdk 0.0.3__tar.gz → 0.0.5__tar.gz
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 sensor-sdk might be problematic. Click here for more details.
- {sensor-sdk-0.0.3 → sensor-sdk-0.0.5}/PKG-INFO +14 -2
- {sensor-sdk-0.0.3 → sensor-sdk-0.0.5}/README.md +13 -1
- {sensor-sdk-0.0.3 → sensor-sdk-0.0.5}/sensor/gforce.py +73 -46
- {sensor-sdk-0.0.3 → sensor-sdk-0.0.5}/sensor/sensor_controller.py +114 -38
- sensor-sdk-0.0.5/sensor/sensor_data.py +94 -0
- {sensor-sdk-0.0.3 → sensor-sdk-0.0.5}/sensor/sensor_data_context.py +428 -107
- {sensor-sdk-0.0.3 → sensor-sdk-0.0.5}/sensor/sensor_device.py +34 -34
- {sensor-sdk-0.0.3 → sensor-sdk-0.0.5}/sensor/sensor_profile.py +243 -109
- sensor-sdk-0.0.5/sensor/utils.py +72 -0
- {sensor-sdk-0.0.3 → sensor-sdk-0.0.5}/sensor_sdk.egg-info/PKG-INFO +14 -2
- sensor-sdk-0.0.5/setup.py +23 -0
- sensor-sdk-0.0.3/sensor/sensor_data.py +0 -91
- sensor-sdk-0.0.3/sensor/utils.py +0 -28
- sensor-sdk-0.0.3/setup.py +0 -29
- {sensor-sdk-0.0.3 → sensor-sdk-0.0.5}/LICENSE.txt +0 -0
- {sensor-sdk-0.0.3 → sensor-sdk-0.0.5}/sensor/__init__.py +0 -0
- {sensor-sdk-0.0.3 → sensor-sdk-0.0.5}/sensor_sdk.egg-info/SOURCES.txt +0 -0
- {sensor-sdk-0.0.3 → sensor-sdk-0.0.5}/sensor_sdk.egg-info/dependency_links.txt +0 -0
- {sensor-sdk-0.0.3 → sensor-sdk-0.0.5}/sensor_sdk.egg-info/requires.txt +0 -0
- {sensor-sdk-0.0.3 → sensor-sdk-0.0.5}/sensor_sdk.egg-info/top_level.txt +0 -0
- {sensor-sdk-0.0.3 → sensor-sdk-0.0.5}/sensor_sdk.egg-info/zip-safe +0 -0
- {sensor-sdk-0.0.3 → sensor-sdk-0.0.5}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sensor-sdk
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.5
|
|
4
4
|
Summary: Python sdk for Synchroni
|
|
5
5
|
Home-page: https://github.com/oymotion/SynchroniSDKPython
|
|
6
6
|
Author: Martin Ye
|
|
@@ -68,6 +68,12 @@ success = SensorControllerInstance.startScan(6000)
|
|
|
68
68
|
|
|
69
69
|
returns true if start scan success, periodInMS means onDeviceCallback will be called every periodInMS
|
|
70
70
|
|
|
71
|
+
Use `def scan(period_in_ms: int) -> list[BLEDevice]` to scan once time
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
bleDevices = SensorControllerInstance.scan(6000)
|
|
75
|
+
```
|
|
76
|
+
|
|
71
77
|
### 3. Stop scan
|
|
72
78
|
|
|
73
79
|
Use `def stopScan() -> None` to stop scan
|
|
@@ -122,7 +128,7 @@ sensorProfiles = SensorControllerInstance.getConnectedSensors()
|
|
|
122
128
|
|
|
123
129
|
### 9. Get Connected BLE Devices
|
|
124
130
|
|
|
125
|
-
Use `def getConnectedDevices() -> list[
|
|
131
|
+
Use `def getConnectedDevices() -> list[BLEDevice]` to get connected BLE Devices.
|
|
126
132
|
|
|
127
133
|
```python
|
|
128
134
|
bleDevices = SensorControllerInstance.getConnectedDevices()
|
|
@@ -340,3 +346,9 @@ batteryPower = sensorProfile.getBatteryLevel()
|
|
|
340
346
|
```
|
|
341
347
|
|
|
342
348
|
Please check console.py in examples directory
|
|
349
|
+
|
|
350
|
+
## Async methods
|
|
351
|
+
|
|
352
|
+
all methods start with async is async methods, they has same params and return result as sync methods.
|
|
353
|
+
|
|
354
|
+
Please check async_console.py in examples directory
|
|
@@ -57,6 +57,12 @@ success = SensorControllerInstance.startScan(6000)
|
|
|
57
57
|
|
|
58
58
|
returns true if start scan success, periodInMS means onDeviceCallback will be called every periodInMS
|
|
59
59
|
|
|
60
|
+
Use `def scan(period_in_ms: int) -> list[BLEDevice]` to scan once time
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
bleDevices = SensorControllerInstance.scan(6000)
|
|
64
|
+
```
|
|
65
|
+
|
|
60
66
|
### 3. Stop scan
|
|
61
67
|
|
|
62
68
|
Use `def stopScan() -> None` to stop scan
|
|
@@ -111,7 +117,7 @@ sensorProfiles = SensorControllerInstance.getConnectedSensors()
|
|
|
111
117
|
|
|
112
118
|
### 9. Get Connected BLE Devices
|
|
113
119
|
|
|
114
|
-
Use `def getConnectedDevices() -> list[
|
|
120
|
+
Use `def getConnectedDevices() -> list[BLEDevice]` to get connected BLE Devices.
|
|
115
121
|
|
|
116
122
|
```python
|
|
117
123
|
bleDevices = SensorControllerInstance.getConnectedDevices()
|
|
@@ -329,3 +335,9 @@ batteryPower = sensorProfile.getBatteryLevel()
|
|
|
329
335
|
```
|
|
330
336
|
|
|
331
337
|
Please check console.py in examples directory
|
|
338
|
+
|
|
339
|
+
## Async methods
|
|
340
|
+
|
|
341
|
+
all methods start with async is async methods, they has same params and return result as sync methods.
|
|
342
|
+
|
|
343
|
+
Please check async_console.py in examples directory
|
|
@@ -16,6 +16,8 @@ from bleak import (
|
|
|
16
16
|
BleakGATTCharacteristic,
|
|
17
17
|
)
|
|
18
18
|
|
|
19
|
+
from sensor import utils
|
|
20
|
+
|
|
19
21
|
|
|
20
22
|
@dataclass
|
|
21
23
|
class Characteristic:
|
|
@@ -146,15 +148,15 @@ class DataSubscription(IntEnum):
|
|
|
146
148
|
LOG = (0x00000800,)
|
|
147
149
|
|
|
148
150
|
DNF_EEG = (0x00010000,)
|
|
149
|
-
|
|
151
|
+
|
|
150
152
|
DNF_ECG = (0x00020000,)
|
|
151
|
-
|
|
153
|
+
|
|
152
154
|
DNF_IMPEDANCE = (0x00040000,)
|
|
153
|
-
|
|
155
|
+
|
|
154
156
|
DNF_IMU = (0x00080000,)
|
|
155
|
-
|
|
157
|
+
|
|
156
158
|
DNF_ADS = (0x00100000,)
|
|
157
|
-
|
|
159
|
+
|
|
158
160
|
DNF_BRTH = (0x00200000,)
|
|
159
161
|
|
|
160
162
|
DNF_CONCAT_BLE = (0x80000000,)
|
|
@@ -181,8 +183,8 @@ class DataType(IntEnum):
|
|
|
181
183
|
|
|
182
184
|
class SampleResolution(IntEnum):
|
|
183
185
|
BITS_8 = (8,)
|
|
184
|
-
BITS_12 = 12,
|
|
185
|
-
BITS_16 = 16,
|
|
186
|
+
BITS_12 = (12,)
|
|
187
|
+
BITS_16 = (16,)
|
|
186
188
|
BITS_24 = 24
|
|
187
189
|
|
|
188
190
|
|
|
@@ -215,6 +217,7 @@ class EmgRawDataConfig:
|
|
|
215
217
|
)
|
|
216
218
|
return cls(fs, channel_mask, batch_len, resolution)
|
|
217
219
|
|
|
220
|
+
|
|
218
221
|
@dataclass
|
|
219
222
|
class EegRawDataConfig:
|
|
220
223
|
fs: SamplingRate = 0
|
|
@@ -240,6 +243,7 @@ class EegRawDataConfig:
|
|
|
240
243
|
)
|
|
241
244
|
return cls(fs, channel_mask, batch_len, resolution, K)
|
|
242
245
|
|
|
246
|
+
|
|
243
247
|
@dataclass
|
|
244
248
|
class EegRawDataCap:
|
|
245
249
|
fs: SamplingRate = 0
|
|
@@ -262,7 +266,8 @@ class EegRawDataCap:
|
|
|
262
266
|
data,
|
|
263
267
|
)
|
|
264
268
|
return cls(fs, channel_count, batch_len, resolution)
|
|
265
|
-
|
|
269
|
+
|
|
270
|
+
|
|
266
271
|
@dataclass
|
|
267
272
|
class EcgRawDataConfig:
|
|
268
273
|
fs: SamplingRate = SamplingRate.HZ_250
|
|
@@ -287,7 +292,8 @@ class EcgRawDataConfig:
|
|
|
287
292
|
data,
|
|
288
293
|
)
|
|
289
294
|
return cls(fs, channel_mask, batch_len, resolution, K)
|
|
290
|
-
|
|
295
|
+
|
|
296
|
+
|
|
291
297
|
@dataclass
|
|
292
298
|
class ImuRawDataConfig:
|
|
293
299
|
channel_count: int = 0
|
|
@@ -295,6 +301,7 @@ class ImuRawDataConfig:
|
|
|
295
301
|
batch_len: int = 0
|
|
296
302
|
accK: float = 0
|
|
297
303
|
gyroK: float = 0
|
|
304
|
+
|
|
298
305
|
def to_bytes(self) -> bytes:
|
|
299
306
|
body = b""
|
|
300
307
|
body += struct.pack("<i", self.channel_count)
|
|
@@ -311,7 +318,8 @@ class ImuRawDataConfig:
|
|
|
311
318
|
data,
|
|
312
319
|
)
|
|
313
320
|
return cls(channel_count, fs, batch_len, accK, gyroK)
|
|
314
|
-
|
|
321
|
+
|
|
322
|
+
|
|
315
323
|
@dataclass
|
|
316
324
|
class BrthRawDataConfig:
|
|
317
325
|
fs: SamplingRate = 0
|
|
@@ -336,7 +344,8 @@ class BrthRawDataConfig:
|
|
|
336
344
|
data,
|
|
337
345
|
)
|
|
338
346
|
return cls(fs, channel_mask, batch_len, resolution, K)
|
|
339
|
-
|
|
347
|
+
|
|
348
|
+
|
|
340
349
|
@dataclass
|
|
341
350
|
class Request:
|
|
342
351
|
cmd: Command
|
|
@@ -361,7 +370,9 @@ class Response:
|
|
|
361
370
|
|
|
362
371
|
|
|
363
372
|
class GForce:
|
|
364
|
-
def __init__(
|
|
373
|
+
def __init__(
|
|
374
|
+
self, device: BLEDevice, cmd_char: str, data_char: str, isUniversalStream: bool
|
|
375
|
+
):
|
|
365
376
|
self.device_name = ""
|
|
366
377
|
self.client = None
|
|
367
378
|
self.cmd_char = cmd_char
|
|
@@ -371,21 +382,33 @@ class GForce:
|
|
|
371
382
|
self._num_channels = 8
|
|
372
383
|
self._device = device
|
|
373
384
|
self._is_universal_stream = isUniversalStream
|
|
374
|
-
self._raw_data_buf:queue.Queue[bytes] = None
|
|
385
|
+
self._raw_data_buf: queue.Queue[bytes] = None
|
|
375
386
|
self.packet_id = 0
|
|
376
387
|
self.data_packet = []
|
|
377
388
|
|
|
378
389
|
async def connect(self, disconnect_cb, buf: queue.Queue[bytes]):
|
|
379
390
|
client = BleakClient(self._device, disconnected_callback=disconnect_cb)
|
|
380
|
-
await client.connect()
|
|
381
|
-
|
|
382
391
|
self.client = client
|
|
383
392
|
self.device_name = self._device.name
|
|
384
393
|
self._raw_data_buf = buf
|
|
385
|
-
|
|
386
|
-
|
|
394
|
+
|
|
395
|
+
try:
|
|
396
|
+
await asyncio.wait_for(client.connect(), utils._TIMEOUT)
|
|
397
|
+
except Exception as e:
|
|
398
|
+
return
|
|
399
|
+
|
|
400
|
+
if not client.is_connected:
|
|
401
|
+
return
|
|
402
|
+
if not self._is_universal_stream:
|
|
403
|
+
await asyncio.wait_for(
|
|
404
|
+
client.start_notify(self.cmd_char, self._on_cmd_response),
|
|
405
|
+
utils._TIMEOUT,
|
|
406
|
+
)
|
|
387
407
|
else:
|
|
388
|
-
await
|
|
408
|
+
await asyncio.wait_for(
|
|
409
|
+
client.start_notify(self.data_char, self._on_universal_response),
|
|
410
|
+
utils._TIMEOUT,
|
|
411
|
+
)
|
|
389
412
|
|
|
390
413
|
def _on_data_response(self, q: Queue[bytes], bs: bytearray):
|
|
391
414
|
bs = bytes(bs)
|
|
@@ -537,6 +560,7 @@ class GForce:
|
|
|
537
560
|
num_channels = 6
|
|
538
561
|
|
|
539
562
|
return emg_gesture_data.reshape(-1, num_channels)
|
|
563
|
+
|
|
540
564
|
def _on_universal_response(self, _: BleakGATTCharacteristic, bs: bytearray):
|
|
541
565
|
self._raw_data_buf.put_nowait(bytes(bs))
|
|
542
566
|
|
|
@@ -675,7 +699,6 @@ class GForce:
|
|
|
675
699
|
)
|
|
676
700
|
)
|
|
677
701
|
|
|
678
|
-
|
|
679
702
|
async def system_reset(self):
|
|
680
703
|
await self._send_request(
|
|
681
704
|
Request(
|
|
@@ -684,11 +707,8 @@ class GForce:
|
|
|
684
707
|
)
|
|
685
708
|
)
|
|
686
709
|
|
|
687
|
-
|
|
688
710
|
async def set_motor(self, switchStatus):
|
|
689
|
-
body = [
|
|
690
|
-
switchStatus == True
|
|
691
|
-
]
|
|
711
|
+
body = [switchStatus == True]
|
|
692
712
|
body = bytes(body)
|
|
693
713
|
ret = await self._send_request(
|
|
694
714
|
Request(
|
|
@@ -699,9 +719,7 @@ class GForce:
|
|
|
699
719
|
)
|
|
700
720
|
|
|
701
721
|
async def set_led(self, switchStatus):
|
|
702
|
-
body = [
|
|
703
|
-
switchStatus == True
|
|
704
|
-
]
|
|
722
|
+
body = [switchStatus == True]
|
|
705
723
|
body = bytes(body)
|
|
706
724
|
ret = await self._send_request(
|
|
707
725
|
Request(
|
|
@@ -712,9 +730,7 @@ class GForce:
|
|
|
712
730
|
)
|
|
713
731
|
|
|
714
732
|
async def set_log_level(self, logLevel):
|
|
715
|
-
body = [
|
|
716
|
-
0xFF & logLevel
|
|
717
|
-
]
|
|
733
|
+
body = [0xFF & logLevel]
|
|
718
734
|
body = bytes(body)
|
|
719
735
|
ret = await self._send_request(
|
|
720
736
|
Request(
|
|
@@ -724,7 +740,6 @@ class GForce:
|
|
|
724
740
|
)
|
|
725
741
|
)
|
|
726
742
|
|
|
727
|
-
|
|
728
743
|
async def set_emg_raw_data_config(self, cfg=EmgRawDataConfig()):
|
|
729
744
|
body = cfg.to_bytes()
|
|
730
745
|
await self._send_request(
|
|
@@ -757,7 +772,7 @@ class GForce:
|
|
|
757
772
|
)
|
|
758
773
|
)
|
|
759
774
|
return EmgRawDataConfig.from_bytes(buf)
|
|
760
|
-
|
|
775
|
+
|
|
761
776
|
async def get_eeg_raw_data_config(self) -> EegRawDataConfig:
|
|
762
777
|
buf = await self._send_request(
|
|
763
778
|
Request(
|
|
@@ -766,7 +781,7 @@ class GForce:
|
|
|
766
781
|
)
|
|
767
782
|
)
|
|
768
783
|
return EegRawDataConfig.from_bytes(buf)
|
|
769
|
-
|
|
784
|
+
|
|
770
785
|
async def get_eeg_raw_data_cap(self) -> EegRawDataCap:
|
|
771
786
|
buf = await self._send_request(
|
|
772
787
|
Request(
|
|
@@ -784,7 +799,7 @@ class GForce:
|
|
|
784
799
|
)
|
|
785
800
|
)
|
|
786
801
|
return EcgRawDataConfig.from_bytes(buf)
|
|
787
|
-
|
|
802
|
+
|
|
788
803
|
async def get_imu_raw_data_config(self) -> ImuRawDataConfig:
|
|
789
804
|
buf = await self._send_request(
|
|
790
805
|
Request(
|
|
@@ -792,8 +807,8 @@ class GForce:
|
|
|
792
807
|
has_res=True,
|
|
793
808
|
)
|
|
794
809
|
)
|
|
795
|
-
return ImuRawDataConfig.from_bytes(buf)
|
|
796
|
-
|
|
810
|
+
return ImuRawDataConfig.from_bytes(buf)
|
|
811
|
+
|
|
797
812
|
async def get_brth_raw_data_config(self) -> BrthRawDataConfig:
|
|
798
813
|
buf = await self._send_request(
|
|
799
814
|
Request(
|
|
@@ -801,8 +816,8 @@ class GForce:
|
|
|
801
816
|
has_res=True,
|
|
802
817
|
)
|
|
803
818
|
)
|
|
804
|
-
return BrthRawDataConfig.from_bytes(buf)
|
|
805
|
-
|
|
819
|
+
return BrthRawDataConfig.from_bytes(buf)
|
|
820
|
+
|
|
806
821
|
async def set_subscription(self, subscription: DataSubscription):
|
|
807
822
|
body = [
|
|
808
823
|
0xFF & subscription,
|
|
@@ -819,20 +834,27 @@ class GForce:
|
|
|
819
834
|
)
|
|
820
835
|
)
|
|
821
836
|
|
|
822
|
-
async def start_streaming(self, q:queue.Queue):
|
|
823
|
-
await
|
|
824
|
-
self.
|
|
825
|
-
|
|
837
|
+
async def start_streaming(self, q: queue.Queue):
|
|
838
|
+
await asyncio.wait_for(
|
|
839
|
+
self.client.start_notify(
|
|
840
|
+
self.data_char,
|
|
841
|
+
lambda _, data: self._on_data_response(q, data),
|
|
842
|
+
),
|
|
843
|
+
utils._TIMEOUT,
|
|
826
844
|
)
|
|
827
845
|
|
|
828
846
|
async def stop_streaming(self):
|
|
829
847
|
exceptions = []
|
|
830
848
|
try:
|
|
831
|
-
await
|
|
849
|
+
await asyncio.wait_for(
|
|
850
|
+
self.set_subscription(DataSubscription.OFF), utils._TIMEOUT
|
|
851
|
+
)
|
|
832
852
|
except Exception as e:
|
|
833
853
|
exceptions.append(e)
|
|
834
854
|
try:
|
|
835
|
-
await
|
|
855
|
+
await asyncio.wait_for(
|
|
856
|
+
self.client.stop_notify(self.data_char), utils._TIMEOUT
|
|
857
|
+
)
|
|
836
858
|
except Exception as e:
|
|
837
859
|
exceptions.append(e)
|
|
838
860
|
|
|
@@ -841,7 +863,10 @@ class GForce:
|
|
|
841
863
|
|
|
842
864
|
async def disconnect(self):
|
|
843
865
|
with suppress(asyncio.CancelledError):
|
|
844
|
-
|
|
866
|
+
try:
|
|
867
|
+
await asyncio.wait_for(self.client.disconnect(), utils._TIMEOUT)
|
|
868
|
+
except Exception as e:
|
|
869
|
+
pass
|
|
845
870
|
|
|
846
871
|
def _get_response_channel(self, cmd: Command) -> Queue:
|
|
847
872
|
q = Queue()
|
|
@@ -856,9 +881,11 @@ class GForce:
|
|
|
856
881
|
bs = bytes([req.cmd])
|
|
857
882
|
if req.body is not None:
|
|
858
883
|
bs += req.body
|
|
859
|
-
await
|
|
884
|
+
await asyncio.wait_for(
|
|
885
|
+
self.client.write_gatt_char(self.cmd_char, bs), utils._TIMEOUT
|
|
886
|
+
)
|
|
860
887
|
|
|
861
888
|
if not req.has_res:
|
|
862
889
|
return None
|
|
863
890
|
|
|
864
|
-
return await asyncio.wait_for(q.get(),
|
|
891
|
+
return await asyncio.wait_for(q.get(), utils._TIMEOUT)
|
|
@@ -5,24 +5,28 @@ from typing import Callable, Dict, List, Optional, Tuple
|
|
|
5
5
|
import bleak
|
|
6
6
|
|
|
7
7
|
from sensor import sensor_profile
|
|
8
|
+
from sensor import utils
|
|
8
9
|
from sensor.sensor_profile import DeviceStateEx, SensorProfile
|
|
9
10
|
import asyncio
|
|
10
11
|
|
|
11
|
-
from sensor.utils import start_loop, sync_timer, timer
|
|
12
|
+
from sensor.utils import async_timer, start_loop, sync_timer, timer
|
|
12
13
|
from bleak import (
|
|
13
14
|
BleakScanner,
|
|
14
15
|
AdvertisementData,
|
|
15
16
|
)
|
|
17
|
+
|
|
16
18
|
SERVICE_GUID = "0000ffd0-0000-1000-8000-00805f9b34fb"
|
|
17
19
|
RFSTAR_SERVICE_GUID = "00001812-0000-1000-8000-00805f9b34fb"
|
|
18
20
|
|
|
21
|
+
|
|
19
22
|
class SensorController:
|
|
20
23
|
_instance_lock = threading.Lock()
|
|
24
|
+
|
|
21
25
|
def __new__(cls, *args, **kwargs):
|
|
22
26
|
if not hasattr(SensorController, "_instance"):
|
|
23
27
|
with SensorController._instance_lock:
|
|
24
28
|
if not hasattr(SensorController, "_instance"):
|
|
25
|
-
SensorController._instance = object.__new__(cls)
|
|
29
|
+
SensorController._instance = object.__new__(cls)
|
|
26
30
|
return SensorController._instance
|
|
27
31
|
|
|
28
32
|
"""
|
|
@@ -34,16 +38,23 @@ class SensorController:
|
|
|
34
38
|
初始化 SensorController 实例。
|
|
35
39
|
"""
|
|
36
40
|
self._event_loop = asyncio.new_event_loop()
|
|
37
|
-
self._event_thread = threading.Thread(
|
|
41
|
+
self._event_thread = threading.Thread(
|
|
42
|
+
target=start_loop, args=(self._event_loop,)
|
|
43
|
+
)
|
|
38
44
|
self._event_thread.daemon = True
|
|
39
45
|
self._event_thread.name = "SensorController event"
|
|
40
46
|
self._event_thread.start()
|
|
41
47
|
self._gforce_event_loop = asyncio.new_event_loop()
|
|
42
|
-
self._gforce_event_thread = threading.Thread(
|
|
48
|
+
self._gforce_event_thread = threading.Thread(
|
|
49
|
+
target=start_loop, args=(self._gforce_event_loop,)
|
|
50
|
+
)
|
|
43
51
|
self._gforce_event_thread.daemon = True
|
|
44
52
|
self._gforce_event_thread.name = "BLE operation"
|
|
45
53
|
self._gforce_event_thread.start()
|
|
46
|
-
self._scanner = BleakScanner(
|
|
54
|
+
self._scanner = BleakScanner(
|
|
55
|
+
detection_callback=self._match_device,
|
|
56
|
+
service_uuids=[SERVICE_GUID, RFSTAR_SERVICE_GUID],
|
|
57
|
+
)
|
|
47
58
|
self._is_scanning = False
|
|
48
59
|
self._device_callback: Callable[[List[sensor_profile.BLEDevice]], None] = None
|
|
49
60
|
self._device_callback_period = 0
|
|
@@ -59,18 +70,19 @@ class SensorController:
|
|
|
59
70
|
self._gforce_event_loop.close()
|
|
60
71
|
|
|
61
72
|
def terminate(self):
|
|
73
|
+
utils._terminated = True
|
|
62
74
|
for sensor in self._sensor_profiles.values():
|
|
63
|
-
if
|
|
75
|
+
if (
|
|
76
|
+
sensor.deviceState == DeviceStateEx.Connected
|
|
77
|
+
or sensor.deviceState == DeviceStateEx.Ready
|
|
78
|
+
):
|
|
64
79
|
sensor._destroy()
|
|
65
|
-
|
|
66
80
|
|
|
67
81
|
def _match_device(self, _device: bleak.BLEDevice, adv: AdvertisementData):
|
|
68
82
|
if _device.name == None:
|
|
69
83
|
return False
|
|
70
84
|
|
|
71
|
-
if
|
|
72
|
-
SERVICE_GUID in adv.service_uuids
|
|
73
|
-
):
|
|
85
|
+
if SERVICE_GUID in adv.service_uuids:
|
|
74
86
|
print("Device found: {0}, RSSI: {1}".format(_device.name, adv.rssi))
|
|
75
87
|
return True
|
|
76
88
|
|
|
@@ -113,7 +125,9 @@ class SensorController:
|
|
|
113
125
|
return self._device_callback != None
|
|
114
126
|
|
|
115
127
|
@hasDeviceFoundCallback.setter
|
|
116
|
-
def onDeviceFoundCallback(
|
|
128
|
+
def onDeviceFoundCallback(
|
|
129
|
+
self, callback: Callable[[List[sensor_profile.BLEDevice]], None]
|
|
130
|
+
):
|
|
117
131
|
"""
|
|
118
132
|
设置扫描设备回调。
|
|
119
133
|
|
|
@@ -121,29 +135,82 @@ class SensorController:
|
|
|
121
135
|
"""
|
|
122
136
|
self._device_callback = callback
|
|
123
137
|
|
|
124
|
-
|
|
138
|
+
def _process_ble_devices(
|
|
139
|
+
self, found_devices: Dict[str, Tuple[bleak.BLEDevice, AdvertisementData]]
|
|
140
|
+
) -> List[sensor_profile.BLEDevice]:
|
|
141
|
+
devices: List[sensor_profile.BLEDevice] = list()
|
|
142
|
+
deviceMap: Dict[str, SensorProfile] = self._sensor_profiles.copy()
|
|
143
|
+
for uuid in found_devices:
|
|
144
|
+
device = found_devices[uuid][0]
|
|
145
|
+
if device.name == None:
|
|
146
|
+
continue
|
|
147
|
+
adv = found_devices[uuid][1]
|
|
148
|
+
if SERVICE_GUID in adv.service_uuids:
|
|
149
|
+
mac = None
|
|
150
|
+
if adv.service_data.get(SERVICE_GUID) != None:
|
|
151
|
+
bytes_val = adv.service_data[SERVICE_GUID]
|
|
152
|
+
mac = ":".join(f"{byte:02X}" for byte in bytes_val)
|
|
153
|
+
elif adv.service_data.get(RFSTAR_SERVICE_GUID) != None:
|
|
154
|
+
bytes_val = adv.service_data[RFSTAR_SERVICE_GUID]
|
|
155
|
+
mac = ":".join(f"{byte:02X}" for byte in reversed(bytes_val))
|
|
156
|
+
|
|
157
|
+
if mac == None:
|
|
158
|
+
continue
|
|
159
|
+
if deviceMap.get(mac) != None:
|
|
160
|
+
devices.append(self._sensor_profiles[mac].BLEDevice)
|
|
161
|
+
else:
|
|
162
|
+
newSensor = SensorProfile(device, adv, mac, self._gforce_event_loop)
|
|
163
|
+
deviceMap[mac] = newSensor
|
|
164
|
+
devices.append(newSensor.BLEDevice)
|
|
165
|
+
|
|
166
|
+
self._sensor_profiles = deviceMap
|
|
167
|
+
return devices
|
|
168
|
+
|
|
169
|
+
async def _async_scan(self, period):
|
|
170
|
+
self._is_scanning = True
|
|
171
|
+
found_devices = await self._scanner.discover(
|
|
172
|
+
timeout=period / 1000, return_adv=True
|
|
173
|
+
)
|
|
174
|
+
self._is_scanning = False
|
|
175
|
+
return self._process_ble_devices(found_devices)
|
|
176
|
+
|
|
177
|
+
def scan(self, period) -> List[sensor_profile.BLEDevice]:
|
|
178
|
+
"""
|
|
179
|
+
扫描一段时间后返回BLEDevice列表。
|
|
180
|
+
|
|
181
|
+
:param periodInMs (int): 扫描时长(毫秒)
|
|
182
|
+
|
|
183
|
+
:return: List[sensor_profile.BLEDevice]: BLEDevice列表
|
|
184
|
+
"""
|
|
185
|
+
return sync_timer(self._gforce_event_loop, 0, self._async_scan(period))
|
|
186
|
+
|
|
187
|
+
async def asyncScan(self, period) -> List[sensor_profile.BLEDevice]:
|
|
188
|
+
"""
|
|
189
|
+
扫描一段时间后返回BLEDevice列表。
|
|
190
|
+
|
|
191
|
+
:param periodInMs (int): 扫描时长(毫秒)
|
|
192
|
+
|
|
193
|
+
:return: List[sensor_profile.BLEDevice]: BLEDevice列表
|
|
194
|
+
"""
|
|
195
|
+
return await async_timer(self._gforce_event_loop, 0, self._async_scan(period))
|
|
196
|
+
|
|
197
|
+
async def _device_scan_callback(self, devices: List[sensor_profile.BLEDevice]):
|
|
125
198
|
if self._device_callback:
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
adv = found_devices[mac][1]
|
|
131
|
-
if SERVICE_GUID in adv.service_uuids:
|
|
132
|
-
if deviceMap.get(mac) != None:
|
|
133
|
-
devices.append(self._sensor_profiles[mac].BLEDevice)
|
|
134
|
-
else:
|
|
135
|
-
newSensor = SensorProfile(device, adv,self._gforce_event_loop)
|
|
136
|
-
deviceMap[mac] = newSensor
|
|
137
|
-
devices.append(newSensor.BLEDevice)
|
|
138
|
-
self._sensor_profiles = deviceMap
|
|
139
|
-
self._device_callback(devices)
|
|
199
|
+
try:
|
|
200
|
+
self._device_callback(devices)
|
|
201
|
+
except Exception as e:
|
|
202
|
+
print(e)
|
|
140
203
|
|
|
141
204
|
if self._is_scanning:
|
|
142
205
|
timer(self._gforce_event_loop, 0, self._startScan())
|
|
143
206
|
|
|
144
207
|
async def _startScan(self) -> bool:
|
|
145
|
-
|
|
208
|
+
found_devices = await self._scanner.discover(
|
|
209
|
+
timeout=self._device_callback_period / 1000, return_adv=True
|
|
210
|
+
)
|
|
211
|
+
devices = self._process_ble_devices(found_devices)
|
|
146
212
|
timer(self._event_loop, 0, self._device_scan_callback(devices))
|
|
213
|
+
|
|
147
214
|
def startScan(self, periodInMs: int) -> bool:
|
|
148
215
|
"""
|
|
149
216
|
开始扫描。
|
|
@@ -152,12 +219,12 @@ class SensorController:
|
|
|
152
219
|
|
|
153
220
|
:return: bool: 扫描是否成功启动
|
|
154
221
|
"""
|
|
155
|
-
if
|
|
222
|
+
if self._is_scanning:
|
|
156
223
|
return True
|
|
157
|
-
|
|
224
|
+
|
|
158
225
|
self._is_scanning = True
|
|
159
226
|
self._device_callback_period = periodInMs
|
|
160
|
-
|
|
227
|
+
|
|
161
228
|
timer(self._gforce_event_loop, 0, self._startScan())
|
|
162
229
|
return True
|
|
163
230
|
|
|
@@ -165,12 +232,14 @@ class SensorController:
|
|
|
165
232
|
"""
|
|
166
233
|
停止扫描。
|
|
167
234
|
"""
|
|
168
|
-
if
|
|
169
|
-
return
|
|
170
|
-
|
|
235
|
+
if not self._is_scanning:
|
|
236
|
+
return
|
|
237
|
+
|
|
171
238
|
self._is_scanning = False
|
|
172
239
|
|
|
173
|
-
def requireSensor(
|
|
240
|
+
def requireSensor(
|
|
241
|
+
self, device: sensor_profile.BLEDevice
|
|
242
|
+
) -> Optional[SensorProfile]:
|
|
174
243
|
"""
|
|
175
244
|
根据设备信息获取或创建SensorProfile。
|
|
176
245
|
|
|
@@ -200,9 +269,12 @@ class SensorController:
|
|
|
200
269
|
|
|
201
270
|
:return: List[SensorProfile]: 已连接的SensorProfile列表
|
|
202
271
|
"""
|
|
203
|
-
sensors:List[SensorProfile] = list()
|
|
272
|
+
sensors: List[SensorProfile] = list()
|
|
204
273
|
for sensor in self._sensor_profiles.values():
|
|
205
|
-
if
|
|
274
|
+
if (
|
|
275
|
+
sensor.deviceState == DeviceStateEx.Connected
|
|
276
|
+
or sensor.deviceState == DeviceStateEx.Ready
|
|
277
|
+
):
|
|
206
278
|
sensors.append(sensor)
|
|
207
279
|
|
|
208
280
|
return sensors
|
|
@@ -213,11 +285,15 @@ class SensorController:
|
|
|
213
285
|
|
|
214
286
|
:return: List[BLEDevice]: 已连接的蓝牙设备列表
|
|
215
287
|
"""
|
|
216
|
-
devices:List[sensor_profile.BLEDevice] = list()
|
|
288
|
+
devices: List[sensor_profile.BLEDevice] = list()
|
|
217
289
|
for sensor in self._sensor_profiles.values():
|
|
218
|
-
if
|
|
290
|
+
if (
|
|
291
|
+
sensor.deviceState == DeviceStateEx.Connected
|
|
292
|
+
or sensor.deviceState == DeviceStateEx.Ready
|
|
293
|
+
):
|
|
219
294
|
devices.append(sensor.BLEDevice)
|
|
220
295
|
|
|
221
296
|
return devices
|
|
222
297
|
|
|
298
|
+
|
|
223
299
|
SensorControllerInstance = SensorController()
|