sensor-sdk 0.0.71__tar.gz → 0.1.2__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.
- {sensor_sdk-0.0.71/sensor_sdk.egg-info → sensor_sdk-0.1.2}/PKG-INFO +41 -11
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.2}/README.md +40 -10
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.2}/sensor/bleak_host.py +61 -28
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.2}/sensor/bleak_process.py +1242 -1088
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.2}/sensor/gforce.py +1098 -1077
- sensor_sdk-0.1.2/sensor/sdk_log.py +259 -0
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.2}/sensor/sensor_controller.py +43 -14
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.2}/sensor/sensor_data.py +103 -103
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.2}/sensor/sensor_data_context.py +1640 -1488
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.2}/sensor/sensor_device.py +4 -0
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.2}/sensor/sensor_profile.py +630 -540
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.2}/sensor/sensor_utils.py +908 -906
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.2/sensor_sdk.egg-info}/PKG-INFO +41 -11
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.2}/sensor_sdk.egg-info/SOURCES.txt +1 -0
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.2}/setup.py +1 -1
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.2}/LICENSE.txt +0 -0
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.2}/sensor/__init__.py +0 -0
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.2}/sensor_sdk.egg-info/dependency_links.txt +0 -0
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.2}/sensor_sdk.egg-info/requires.txt +0 -0
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.2}/sensor_sdk.egg-info/top_level.txt +0 -0
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.2}/sensor_sdk.egg-info/zip-safe +0 -0
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sensor-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Python sdk for Synchroni
|
|
5
5
|
Home-page: https://github.com/oymotion/SynchroniSDKPython
|
|
6
6
|
Author: Martin Ye
|
|
@@ -208,6 +208,8 @@ success = sensorProfile.connect()
|
|
|
208
208
|
|
|
209
209
|
Use `def disconnect() -> bool` to disconnect.
|
|
210
210
|
|
|
211
|
+
If data notification is currently active, `disconnect()` will automatically stop it first before closing the BLE connection.
|
|
212
|
+
|
|
211
213
|
```python
|
|
212
214
|
success = sensorProfile.disconnect()
|
|
213
215
|
```
|
|
@@ -369,24 +371,31 @@ Please check async_console.py in examples directory
|
|
|
369
371
|
|
|
370
372
|
Use `def setParam(self, key: str, value: str) -> str` to set parameter of sensor profile. Please call after device in 'Ready' state.
|
|
371
373
|
|
|
374
|
+
The asynchronous variant is `asyncSetParam(self, key: str, value: str) -> str`.
|
|
375
|
+
|
|
376
|
+
If the device is already streaming when you change an `NTF_*` or `FILTER_*` key, the SDK will stop and restart the data notification so the new setting takes effect immediately.
|
|
377
|
+
|
|
372
378
|
Below is available key and value:
|
|
373
379
|
|
|
374
380
|
```python
|
|
381
|
+
# Data stream toggles
|
|
382
|
+
result = sensorProfile.setParam("NTF_GEST", "ON")
|
|
375
383
|
result = sensorProfile.setParam("NTF_EMG", "ON")
|
|
376
|
-
# set EMG data to ON or OFF, result is "OK" if succeed
|
|
377
|
-
|
|
378
384
|
result = sensorProfile.setParam("NTF_EEG", "ON")
|
|
379
|
-
# set EEG data to ON or OFF, result is "OK" if succeed
|
|
380
|
-
|
|
381
385
|
result = sensorProfile.setParam("NTF_ECG", "ON")
|
|
382
|
-
# set ECG data to ON or OFF, result is "OK" if succeed
|
|
383
|
-
|
|
384
386
|
result = sensorProfile.setParam("NTF_IMU", "ON")
|
|
385
|
-
# set IMU data to ON or OFF, result is "OK" if succeed
|
|
386
|
-
|
|
387
387
|
result = sensorProfile.setParam("NTF_BRTH", "ON")
|
|
388
|
-
|
|
389
|
-
|
|
388
|
+
result = sensorProfile.setParam("NTF_IMPEDANCE", "ON")
|
|
389
|
+
result = sensorProfile.setParam("NTF_MAG_ANGLE", "ON")
|
|
390
|
+
result = sensorProfile.setParam("NTF_PPG_RAW", "ON")
|
|
391
|
+
result = sensorProfile.setParam("NTF_GFORCE_EULER", "ON")
|
|
392
|
+
result = sensorProfile.setParam("NTF_GFORCE_QUAT", "ON")
|
|
393
|
+
result = sensorProfile.setParam("NTF_GFORCE_ACC", "ON")
|
|
394
|
+
result = sensorProfile.setParam("NTF_GFORCE_GYRO", "ON")
|
|
395
|
+
# set data stream to ON or OFF, result is "OK" if succeed
|
|
396
|
+
# Note: on legacy (non-new) EMG devices, NTF_GEST and NTF_EMG are mutually exclusive.
|
|
397
|
+
|
|
398
|
+
# Firmware filter toggles
|
|
390
399
|
result = sensorProfile.setParam("FILTER_50HZ", "ON")
|
|
391
400
|
# set 50Hz notch filter to ON or OFF, result is "OK" if succeed
|
|
392
401
|
|
|
@@ -403,3 +412,24 @@ result = sensorProfile.setParam("DEBUG_BLE_DATA_PATH", "d:/temp/test.csv")
|
|
|
403
412
|
# set debug ble data path, result is "OK" if succeed
|
|
404
413
|
# please give an absolute path and make sure it is valid and writeable by yourself
|
|
405
414
|
```
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
### getParam method
|
|
418
|
+
|
|
419
|
+
Use `def getParam(self, key: str) -> str` to query the current parameter state of a sensor profile. Please call after the device reaches the 'Ready' state.
|
|
420
|
+
|
|
421
|
+
The asynchronous variant is `asyncGetParam(self, key: str) -> str`.
|
|
422
|
+
|
|
423
|
+
Supported aggregate query keys:
|
|
424
|
+
|
|
425
|
+
```python
|
|
426
|
+
result = sensorProfile.getParam("FILTER")
|
|
427
|
+
# Returns a pipe-separated string of all filter states, e.g.:
|
|
428
|
+
# "FILTER_50HZ|ON|FILTER_60HZ|ON|FILTER_HPF|ON|FILTER_LPF|ON"
|
|
429
|
+
|
|
430
|
+
result = sensorProfile.getParam("NTF")
|
|
431
|
+
# Returns a pipe-separated string of all notification states, e.g.:
|
|
432
|
+
# "NTF_BRTH|ON|NTF_ECG|ON|NTF_EEG|ON|NTF_EMG|ON|..."
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
If the key is not supported, the result starts with `"Error"`.
|
|
@@ -185,6 +185,8 @@ success = sensorProfile.connect()
|
|
|
185
185
|
|
|
186
186
|
Use `def disconnect() -> bool` to disconnect.
|
|
187
187
|
|
|
188
|
+
If data notification is currently active, `disconnect()` will automatically stop it first before closing the BLE connection.
|
|
189
|
+
|
|
188
190
|
```python
|
|
189
191
|
success = sensorProfile.disconnect()
|
|
190
192
|
```
|
|
@@ -346,24 +348,31 @@ Please check async_console.py in examples directory
|
|
|
346
348
|
|
|
347
349
|
Use `def setParam(self, key: str, value: str) -> str` to set parameter of sensor profile. Please call after device in 'Ready' state.
|
|
348
350
|
|
|
351
|
+
The asynchronous variant is `asyncSetParam(self, key: str, value: str) -> str`.
|
|
352
|
+
|
|
353
|
+
If the device is already streaming when you change an `NTF_*` or `FILTER_*` key, the SDK will stop and restart the data notification so the new setting takes effect immediately.
|
|
354
|
+
|
|
349
355
|
Below is available key and value:
|
|
350
356
|
|
|
351
357
|
```python
|
|
358
|
+
# Data stream toggles
|
|
359
|
+
result = sensorProfile.setParam("NTF_GEST", "ON")
|
|
352
360
|
result = sensorProfile.setParam("NTF_EMG", "ON")
|
|
353
|
-
# set EMG data to ON or OFF, result is "OK" if succeed
|
|
354
|
-
|
|
355
361
|
result = sensorProfile.setParam("NTF_EEG", "ON")
|
|
356
|
-
# set EEG data to ON or OFF, result is "OK" if succeed
|
|
357
|
-
|
|
358
362
|
result = sensorProfile.setParam("NTF_ECG", "ON")
|
|
359
|
-
# set ECG data to ON or OFF, result is "OK" if succeed
|
|
360
|
-
|
|
361
363
|
result = sensorProfile.setParam("NTF_IMU", "ON")
|
|
362
|
-
# set IMU data to ON or OFF, result is "OK" if succeed
|
|
363
|
-
|
|
364
364
|
result = sensorProfile.setParam("NTF_BRTH", "ON")
|
|
365
|
-
|
|
366
|
-
|
|
365
|
+
result = sensorProfile.setParam("NTF_IMPEDANCE", "ON")
|
|
366
|
+
result = sensorProfile.setParam("NTF_MAG_ANGLE", "ON")
|
|
367
|
+
result = sensorProfile.setParam("NTF_PPG_RAW", "ON")
|
|
368
|
+
result = sensorProfile.setParam("NTF_GFORCE_EULER", "ON")
|
|
369
|
+
result = sensorProfile.setParam("NTF_GFORCE_QUAT", "ON")
|
|
370
|
+
result = sensorProfile.setParam("NTF_GFORCE_ACC", "ON")
|
|
371
|
+
result = sensorProfile.setParam("NTF_GFORCE_GYRO", "ON")
|
|
372
|
+
# set data stream to ON or OFF, result is "OK" if succeed
|
|
373
|
+
# Note: on legacy (non-new) EMG devices, NTF_GEST and NTF_EMG are mutually exclusive.
|
|
374
|
+
|
|
375
|
+
# Firmware filter toggles
|
|
367
376
|
result = sensorProfile.setParam("FILTER_50HZ", "ON")
|
|
368
377
|
# set 50Hz notch filter to ON or OFF, result is "OK" if succeed
|
|
369
378
|
|
|
@@ -380,3 +389,24 @@ result = sensorProfile.setParam("DEBUG_BLE_DATA_PATH", "d:/temp/test.csv")
|
|
|
380
389
|
# set debug ble data path, result is "OK" if succeed
|
|
381
390
|
# please give an absolute path and make sure it is valid and writeable by yourself
|
|
382
391
|
```
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
### getParam method
|
|
395
|
+
|
|
396
|
+
Use `def getParam(self, key: str) -> str` to query the current parameter state of a sensor profile. Please call after the device reaches the 'Ready' state.
|
|
397
|
+
|
|
398
|
+
The asynchronous variant is `asyncGetParam(self, key: str) -> str`.
|
|
399
|
+
|
|
400
|
+
Supported aggregate query keys:
|
|
401
|
+
|
|
402
|
+
```python
|
|
403
|
+
result = sensorProfile.getParam("FILTER")
|
|
404
|
+
# Returns a pipe-separated string of all filter states, e.g.:
|
|
405
|
+
# "FILTER_50HZ|ON|FILTER_60HZ|ON|FILTER_HPF|ON|FILTER_LPF|ON"
|
|
406
|
+
|
|
407
|
+
result = sensorProfile.getParam("NTF")
|
|
408
|
+
# Returns a pipe-separated string of all notification states, e.g.:
|
|
409
|
+
# "NTF_BRTH|ON|NTF_ECG|ON|NTF_EEG|ON|NTF_EMG|ON|..."
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
If the key is not supported, the result starts with `"Error"`.
|
|
@@ -6,6 +6,9 @@ import uuid
|
|
|
6
6
|
|
|
7
7
|
from sensor.bleak_process import BleakProcess
|
|
8
8
|
from sensor import sensor_utils
|
|
9
|
+
from sensor.sdk_log import SdkLog
|
|
10
|
+
|
|
11
|
+
_TAG = "BleakHost"
|
|
9
12
|
|
|
10
13
|
|
|
11
14
|
class BleakHost:
|
|
@@ -14,7 +17,7 @@ class BleakHost:
|
|
|
14
17
|
def __init__(self):
|
|
15
18
|
self._cmd_queue = multiprocessing.Queue(maxsize=50)
|
|
16
19
|
self._result_queue = multiprocessing.Queue(maxsize=sensor_utils.BLEAK_RESULT_QUEUE_MAXSIZE)
|
|
17
|
-
self._bleak_process = BleakProcess(self._cmd_queue, self._result_queue)
|
|
20
|
+
self._bleak_process = BleakProcess(self._cmd_queue, self._result_queue, SdkLog.get_log_path())
|
|
18
21
|
self._started = False
|
|
19
22
|
|
|
20
23
|
|
|
@@ -48,19 +51,32 @@ class BleakHost:
|
|
|
48
51
|
def stop(self):
|
|
49
52
|
if not self._started:
|
|
50
53
|
return
|
|
54
|
+
self._should_exit = True
|
|
51
55
|
try:
|
|
52
56
|
self._cmd_queue.put({"type": "terminate"})
|
|
53
57
|
except Exception as e:
|
|
54
|
-
|
|
55
|
-
traceback.print_exc()
|
|
58
|
+
SdkLog.exception(_TAG, "Unexpected error")
|
|
56
59
|
|
|
57
60
|
if self._bleak_process.is_alive():
|
|
58
61
|
self._bleak_process.join(timeout=5)
|
|
59
62
|
if self._bleak_process.is_alive():
|
|
60
63
|
self._bleak_process.terminate()
|
|
64
|
+
self._bleak_process.join(timeout=2)
|
|
61
65
|
|
|
62
66
|
self._stop_result_loop()
|
|
63
67
|
self._stop_cmd_loop()
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
self._cmd_queue.close()
|
|
71
|
+
self._result_queue.close()
|
|
72
|
+
except Exception as e:
|
|
73
|
+
SdkLog.exception(_TAG, "Error closing queues")
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
self._bleak_process.close()
|
|
77
|
+
except Exception as e:
|
|
78
|
+
SdkLog.exception(_TAG, "Error closing bleak process")
|
|
79
|
+
|
|
64
80
|
self._started = False
|
|
65
81
|
|
|
66
82
|
# ------------------------------------------------------------------
|
|
@@ -75,32 +91,44 @@ class BleakHost:
|
|
|
75
91
|
result_thread.start()
|
|
76
92
|
self._result_loop = result_loop
|
|
77
93
|
self._result_thread = result_thread
|
|
78
|
-
|
|
94
|
+
|
|
95
|
+
def _create_consume_task():
|
|
96
|
+
self._consume_task = result_loop.create_task(self._consume_results())
|
|
97
|
+
|
|
98
|
+
result_loop.call_soon_threadsafe(_create_consume_task)
|
|
79
99
|
|
|
80
100
|
def _stop_result_loop(self):
|
|
101
|
+
task = getattr(self, '_consume_task', None)
|
|
102
|
+
if task is not None:
|
|
103
|
+
try:
|
|
104
|
+
loop = task.get_loop()
|
|
105
|
+
if loop is not None and not loop.is_closed():
|
|
106
|
+
loop.call_soon_threadsafe(task.cancel)
|
|
107
|
+
except Exception as e:
|
|
108
|
+
SdkLog.exception(_TAG, "Unexpected error")
|
|
109
|
+
self._consume_task = None
|
|
110
|
+
|
|
81
111
|
loop = getattr(self, '_result_loop', None)
|
|
82
112
|
thread = getattr(self, '_result_thread', None)
|
|
83
113
|
if loop is not None and not loop.is_closed():
|
|
84
114
|
try:
|
|
85
115
|
loop.call_soon_threadsafe(loop.stop)
|
|
86
116
|
except Exception as e:
|
|
87
|
-
|
|
88
|
-
traceback.print_exc()
|
|
117
|
+
SdkLog.exception(_TAG, "Unexpected error")
|
|
89
118
|
if thread is not None and thread.is_alive():
|
|
90
119
|
try:
|
|
91
120
|
thread.join(timeout=2)
|
|
92
121
|
except Exception as e:
|
|
93
|
-
|
|
94
|
-
traceback.print_exc()
|
|
122
|
+
SdkLog.exception(_TAG, "Unexpected error")
|
|
95
123
|
self._result_loop = None
|
|
96
124
|
self._result_thread = None
|
|
97
125
|
|
|
98
126
|
async def _consume_results(self):
|
|
99
127
|
|
|
100
|
-
while
|
|
128
|
+
while not getattr(self, '_should_exit', False):
|
|
101
129
|
try:
|
|
102
130
|
msg = await asyncio.get_event_loop().run_in_executor(
|
|
103
|
-
None, self._result_queue.get, True, 0.
|
|
131
|
+
None, self._result_queue.get, True, 0.1
|
|
104
132
|
)
|
|
105
133
|
except queue.Empty:
|
|
106
134
|
continue
|
|
@@ -113,15 +141,13 @@ class BleakHost:
|
|
|
113
141
|
try:
|
|
114
142
|
self.on_scan_once_result(msg)
|
|
115
143
|
except Exception as e:
|
|
116
|
-
|
|
117
|
-
traceback.print_exc()
|
|
144
|
+
SdkLog.exception(_TAG, "Unexpected error")
|
|
118
145
|
elif msg_type == "devices":
|
|
119
146
|
if self.on_scan_result is not None:
|
|
120
147
|
try:
|
|
121
148
|
self.on_scan_result(msg)
|
|
122
149
|
except Exception as e:
|
|
123
|
-
|
|
124
|
-
traceback.print_exc()
|
|
150
|
+
SdkLog.exception(_TAG, "Unexpected error")
|
|
125
151
|
elif msg_type == "command_result":
|
|
126
152
|
self._handle_command_result(msg)
|
|
127
153
|
elif msg_type in ("state_changed", "power_changed", "sensor_data", "error"):
|
|
@@ -130,8 +156,7 @@ class BleakHost:
|
|
|
130
156
|
try:
|
|
131
157
|
self.on_device_message(device_mac, msg)
|
|
132
158
|
except Exception as e:
|
|
133
|
-
|
|
134
|
-
traceback.print_exc()
|
|
159
|
+
SdkLog.exception(_TAG, "Unexpected error")
|
|
135
160
|
|
|
136
161
|
def _handle_command_result(self, msg):
|
|
137
162
|
|
|
@@ -149,6 +174,15 @@ class BleakHost:
|
|
|
149
174
|
if loop is not None and not loop.is_closed() and not waiter.done():
|
|
150
175
|
loop.call_soon_threadsafe(waiter.set_result, msg)
|
|
151
176
|
|
|
177
|
+
# 将 connect 返回的 chip_type 转发给对应 SensorProfile
|
|
178
|
+
chip_type = msg.get("chip_type")
|
|
179
|
+
device_mac = msg.get("device_mac")
|
|
180
|
+
if chip_type is not None and self.on_device_message is not None and device_mac is not None:
|
|
181
|
+
try:
|
|
182
|
+
self.on_device_message(device_mac, {"type": "chip_type", "chip_type": chip_type})
|
|
183
|
+
except Exception as e:
|
|
184
|
+
SdkLog.exception(_TAG, "Error forwarding chip_type")
|
|
185
|
+
|
|
152
186
|
# ------------------------------------------------------------------
|
|
153
187
|
# Command loop
|
|
154
188
|
# ------------------------------------------------------------------
|
|
@@ -169,14 +203,12 @@ class BleakHost:
|
|
|
169
203
|
try:
|
|
170
204
|
loop.call_soon_threadsafe(loop.stop)
|
|
171
205
|
except Exception as e:
|
|
172
|
-
|
|
173
|
-
traceback.print_exc()
|
|
206
|
+
SdkLog.exception(_TAG, "Unexpected error")
|
|
174
207
|
if thread is not None and thread.is_alive():
|
|
175
208
|
try:
|
|
176
209
|
thread.join(timeout=2)
|
|
177
210
|
except Exception as e:
|
|
178
|
-
|
|
179
|
-
traceback.print_exc()
|
|
211
|
+
SdkLog.exception(_TAG, "Unexpected error")
|
|
180
212
|
self._cmd_loop = None
|
|
181
213
|
self._cmd_thread = None
|
|
182
214
|
|
|
@@ -192,32 +224,28 @@ class BleakHost:
|
|
|
192
224
|
try:
|
|
193
225
|
self._cmd_queue.put_nowait({"type": "scan_once", "period": period})
|
|
194
226
|
except queue.Full as e:
|
|
195
|
-
|
|
196
|
-
traceback.print_exc()
|
|
227
|
+
SdkLog.exception(_TAG, "Unexpected error")
|
|
197
228
|
|
|
198
229
|
def start_scan(self, period):
|
|
199
230
|
|
|
200
231
|
try:
|
|
201
232
|
self._cmd_queue.put_nowait({"type": "start_scan", "period": period})
|
|
202
233
|
except queue.Full as e:
|
|
203
|
-
|
|
204
|
-
traceback.print_exc()
|
|
234
|
+
SdkLog.exception(_TAG, "Unexpected error")
|
|
205
235
|
|
|
206
236
|
def stop_scan(self):
|
|
207
237
|
|
|
208
238
|
try:
|
|
209
239
|
self._cmd_queue.put_nowait({"type": "stop_scan"})
|
|
210
240
|
except queue.Full as e:
|
|
211
|
-
|
|
212
|
-
traceback.print_exc()
|
|
241
|
+
SdkLog.exception(_TAG, "Unexpected error")
|
|
213
242
|
|
|
214
243
|
def send_command(self, cmd: dict):
|
|
215
244
|
|
|
216
245
|
try:
|
|
217
246
|
self._cmd_queue.put_nowait(cmd)
|
|
218
247
|
except queue.Full as e:
|
|
219
|
-
|
|
220
|
-
traceback.print_exc()
|
|
248
|
+
SdkLog.exception(_TAG, "Command queue full")
|
|
221
249
|
|
|
222
250
|
def send_command_sync(self, cmd: dict, timeout: float = 10.0) -> dict:
|
|
223
251
|
|
|
@@ -235,12 +263,14 @@ class BleakHost:
|
|
|
235
263
|
try:
|
|
236
264
|
self._cmd_queue.put(cmd, timeout=1.0)
|
|
237
265
|
except queue.Full:
|
|
266
|
+
SdkLog.e(_TAG, f"send_command_sync queue full: {cmd.get('type')}")
|
|
238
267
|
with self._pending_lock:
|
|
239
268
|
self._pending.pop(cmd_id, None)
|
|
240
269
|
return {}
|
|
241
270
|
try:
|
|
242
271
|
await asyncio.wait_for(event.wait(), timeout=timeout)
|
|
243
272
|
except asyncio.TimeoutError:
|
|
273
|
+
SdkLog.e(_TAG, f"send_command_sync timeout: {cmd.get('type')}")
|
|
244
274
|
with self._pending_lock:
|
|
245
275
|
self._pending.pop(cmd_id, None)
|
|
246
276
|
return {}
|
|
@@ -252,6 +282,7 @@ class BleakHost:
|
|
|
252
282
|
try:
|
|
253
283
|
return future.result(timeout=timeout)
|
|
254
284
|
except Exception:
|
|
285
|
+
SdkLog.exception(_TAG, "send_command_sync future failed")
|
|
255
286
|
return {}
|
|
256
287
|
|
|
257
288
|
async def send_command_async(self, cmd: dict, timeout: float = 10.0) -> dict:
|
|
@@ -269,12 +300,14 @@ class BleakHost:
|
|
|
269
300
|
try:
|
|
270
301
|
self._cmd_queue.put(cmd, timeout=1.0)
|
|
271
302
|
except queue.Full:
|
|
303
|
+
SdkLog.e(_TAG, f"send_command_async queue full: {cmd.get('type')}")
|
|
272
304
|
with self._pending_lock:
|
|
273
305
|
self._pending.pop(cmd_id, None)
|
|
274
306
|
return {}
|
|
275
307
|
try:
|
|
276
308
|
return await asyncio.wait_for(future, timeout=timeout)
|
|
277
309
|
except asyncio.TimeoutError:
|
|
310
|
+
SdkLog.e(_TAG, f"send_command_async timeout: {cmd.get('type')}")
|
|
278
311
|
with self._pending_lock:
|
|
279
312
|
self._pending.pop(cmd_id, None)
|
|
280
313
|
return {}
|