sensor-sdk 1.3.3__py3-none-win_amd64.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.
- sensor/__init__.py +34 -0
- sensor/_dll.py +381 -0
- sensor/_sync.py +85 -0
- sensor/bin/sensor.dll +0 -0
- sensor/bin/sensor_ble_helper.exe +0 -0
- sensor/pyinstaller_hooks/__init__.py +4 -0
- sensor/pyinstaller_hooks/hook-sensor.py +3 -0
- sensor/sensor_controller.py +666 -0
- sensor/sensor_data.py +294 -0
- sensor/sensor_device.py +120 -0
- sensor/sensor_profile.py +674 -0
- sensor_sdk-1.3.3.dist-info/METADATA +23 -0
- sensor_sdk-1.3.3.dist-info/RECORD +16 -0
- sensor_sdk-1.3.3.dist-info/WHEEL +5 -0
- sensor_sdk-1.3.3.dist-info/entry_points.txt +2 -0
- sensor_sdk-1.3.3.dist-info/top_level.txt +1 -0
sensor/sensor_profile.py
ADDED
|
@@ -0,0 +1,674 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import ctypes
|
|
3
|
+
import inspect
|
|
4
|
+
import threading
|
|
5
|
+
import traceback
|
|
6
|
+
from typing import Callable, Optional
|
|
7
|
+
|
|
8
|
+
from . import _dll
|
|
9
|
+
from . import _sync
|
|
10
|
+
from .sensor_data import SensorData
|
|
11
|
+
from .sensor_device import BLEDevice, DeviceInfo, DeviceStateEx
|
|
12
|
+
|
|
13
|
+
_sdk_thread_ids = set()
|
|
14
|
+
_sdk_thread_ids_lock = threading.Lock()
|
|
15
|
+
|
|
16
|
+
def _mark_sdk_thread():
|
|
17
|
+
with _sdk_thread_ids_lock:
|
|
18
|
+
_sdk_thread_ids.add(threading.get_ident())
|
|
19
|
+
|
|
20
|
+
def _is_sdk_thread() -> bool:
|
|
21
|
+
with _sdk_thread_ids_lock:
|
|
22
|
+
return threading.get_ident() in _sdk_thread_ids
|
|
23
|
+
|
|
24
|
+
def _raise_on_sdk_thread():
|
|
25
|
+
if _is_sdk_thread():
|
|
26
|
+
raise RuntimeError("blocking sync call from an SDK callback thread")
|
|
27
|
+
|
|
28
|
+
def _decode_str(raw) -> str:
|
|
29
|
+
if not raw:
|
|
30
|
+
return ""
|
|
31
|
+
return raw.decode("utf-8", errors="replace") if isinstance(raw, bytes) else str(raw)
|
|
32
|
+
|
|
33
|
+
def _resolve_future(loop, fut, value):
|
|
34
|
+
def _set():
|
|
35
|
+
if not fut.done():
|
|
36
|
+
fut.set_result(value)
|
|
37
|
+
try:
|
|
38
|
+
loop.call_soon_threadsafe(_set)
|
|
39
|
+
except Exception:
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
def _ctx_self(ctx) -> "SensorProfile":
|
|
43
|
+
return ctypes.cast(ctx, ctypes.POINTER(ctypes.py_object)).contents.value
|
|
44
|
+
|
|
45
|
+
def _wrap_auto_reconnect_callback(callback):
|
|
46
|
+
if callback is None:
|
|
47
|
+
return None
|
|
48
|
+
try:
|
|
49
|
+
argc = len(inspect.signature(callback).parameters)
|
|
50
|
+
except (TypeError, ValueError):
|
|
51
|
+
argc = 3
|
|
52
|
+
if argc == 2:
|
|
53
|
+
def _wrapped(sensor, has_last_session, answer):
|
|
54
|
+
answer(bool(callback(sensor, has_last_session)))
|
|
55
|
+
return _wrapped
|
|
56
|
+
return callback
|
|
57
|
+
|
|
58
|
+
class SensorProfile:
|
|
59
|
+
def __init__(self, controller, handle):
|
|
60
|
+
self._controller = controller
|
|
61
|
+
self._handle = handle
|
|
62
|
+
|
|
63
|
+
self._on_error_callback: Optional[Callable[["SensorProfile", str], None]] = None
|
|
64
|
+
self._on_state_change: Optional[Callable[["SensorProfile", DeviceStateEx], None]] = None
|
|
65
|
+
self._on_sensor_notify_data: Optional[Callable[["SensorProfile", list], None]] = None
|
|
66
|
+
self._on_auto_reconnect: Optional[Callable[["SensorProfile", bool, Callable[[bool], None]], None]] = None
|
|
67
|
+
self._on_auto_reconnect_orig: Optional[Callable] = None
|
|
68
|
+
self._on_power_changed: Optional[Callable[["SensorProfile", int], None]] = None
|
|
69
|
+
self._on_device_info_update: Optional[Callable[["SensorProfile", DeviceInfo], None]] = None
|
|
70
|
+
self._on_data_transfer_state_change: Optional[Callable[["SensorProfile", bool], None]] = None
|
|
71
|
+
|
|
72
|
+
self._power = -1
|
|
73
|
+
self._auto_reconnect = False
|
|
74
|
+
self._device_info: Optional[DeviceInfo] = None
|
|
75
|
+
self._ble_device: Optional[BLEDevice] = None
|
|
76
|
+
self._pending_cbs = []
|
|
77
|
+
|
|
78
|
+
self._ctx = ctypes.py_object(self)
|
|
79
|
+
self._ctx_ptr = ctypes.cast(ctypes.pointer(self._ctx), ctypes.c_void_p)
|
|
80
|
+
self._c_on_data = _dll._sen_data_cb(SensorProfile._c_data)
|
|
81
|
+
self._c_on_state = _dll._sen_state_cb(SensorProfile._c_state)
|
|
82
|
+
self._c_on_error = _dll._sen_error_cb(SensorProfile._c_error)
|
|
83
|
+
self._c_on_power = _dll._sen_power_cb(SensorProfile._c_power)
|
|
84
|
+
self._c_on_auto_reconnect = _dll._sen_auto_reconnect_cb(SensorProfile._c_auto_reconnect)
|
|
85
|
+
self._c_on_device_info_update = _dll._sen_device_info_update_cb(SensorProfile._c_device_info_update)
|
|
86
|
+
self._c_on_data_transfer_state_change = _dll._sen_data_transfer_state_cb(SensorProfile._c_data_transfer_state_change)
|
|
87
|
+
self._cbs = _dll.sen_profile_cbs_t()
|
|
88
|
+
self._cbs.structSize = ctypes.sizeof(self._cbs)
|
|
89
|
+
self._cbs.onData = self._c_on_data
|
|
90
|
+
self._cbs.onStateChange = self._c_on_state
|
|
91
|
+
self._cbs.onError = self._c_on_error
|
|
92
|
+
self._cbs.onPowerChange = self._c_on_power
|
|
93
|
+
self._cbs.onAutoReconnect = self._c_on_auto_reconnect
|
|
94
|
+
self._cbs.onDeviceInfoUpdate = self._c_on_device_info_update
|
|
95
|
+
self._cbs.onDataTransferStateChange = self._c_on_data_transfer_state_change
|
|
96
|
+
_dll.load_dll().sen_profile_set_callbacks(
|
|
97
|
+
self._handle, ctypes.byref(self._cbs), self._ctx_ptr)
|
|
98
|
+
|
|
99
|
+
@staticmethod
|
|
100
|
+
def _c_data(ctx, profile, views, view_count):
|
|
101
|
+
_mark_sdk_thread()
|
|
102
|
+
try:
|
|
103
|
+
self = _ctx_self(ctx)
|
|
104
|
+
cb = self._on_sensor_notify_data
|
|
105
|
+
if cb is None or view_count == 0:
|
|
106
|
+
return
|
|
107
|
+
data_list = [SensorProfile._copy_view(views[i]) for i in range(view_count)]
|
|
108
|
+
cb(self, data_list)
|
|
109
|
+
except Exception:
|
|
110
|
+
traceback.print_exc()
|
|
111
|
+
|
|
112
|
+
@staticmethod
|
|
113
|
+
def _c_state(ctx, profile, new_state):
|
|
114
|
+
_mark_sdk_thread()
|
|
115
|
+
try:
|
|
116
|
+
self = _ctx_self(ctx)
|
|
117
|
+
if new_state == DeviceStateEx.Disconnected.value:
|
|
118
|
+
self._device_info = None
|
|
119
|
+
self._ble_device = None
|
|
120
|
+
self._power = -1
|
|
121
|
+
cb = self._on_state_change
|
|
122
|
+
if cb is not None:
|
|
123
|
+
cb(self, DeviceStateEx(new_state))
|
|
124
|
+
except Exception:
|
|
125
|
+
traceback.print_exc()
|
|
126
|
+
|
|
127
|
+
@staticmethod
|
|
128
|
+
def _c_error(ctx, profile, error_msg):
|
|
129
|
+
_mark_sdk_thread()
|
|
130
|
+
try:
|
|
131
|
+
self = _ctx_self(ctx)
|
|
132
|
+
cb = self._on_error_callback
|
|
133
|
+
if cb is not None:
|
|
134
|
+
cb(self, _decode_str(error_msg))
|
|
135
|
+
except Exception:
|
|
136
|
+
traceback.print_exc()
|
|
137
|
+
|
|
138
|
+
@staticmethod
|
|
139
|
+
def _c_power(ctx, profile, power):
|
|
140
|
+
_mark_sdk_thread()
|
|
141
|
+
try:
|
|
142
|
+
self = _ctx_self(ctx)
|
|
143
|
+
if power >= 0:
|
|
144
|
+
self._power = power
|
|
145
|
+
cb = self._on_power_changed
|
|
146
|
+
if cb is not None:
|
|
147
|
+
cb(self, power)
|
|
148
|
+
except Exception:
|
|
149
|
+
traceback.print_exc()
|
|
150
|
+
|
|
151
|
+
@staticmethod
|
|
152
|
+
def _c_auto_reconnect(ctx, profile, has_last_session, answer, answer_ctx):
|
|
153
|
+
_mark_sdk_thread()
|
|
154
|
+
answered = False
|
|
155
|
+
|
|
156
|
+
def _answer(handled: bool):
|
|
157
|
+
nonlocal answered
|
|
158
|
+
if answered:
|
|
159
|
+
return
|
|
160
|
+
answered = True
|
|
161
|
+
answer(answer_ctx, 1 if handled else 0)
|
|
162
|
+
|
|
163
|
+
try:
|
|
164
|
+
self = _ctx_self(ctx)
|
|
165
|
+
cb = self._on_auto_reconnect
|
|
166
|
+
if cb is None:
|
|
167
|
+
_answer(False)
|
|
168
|
+
return
|
|
169
|
+
cb(self, has_last_session != 0, _answer)
|
|
170
|
+
except Exception:
|
|
171
|
+
traceback.print_exc()
|
|
172
|
+
try:
|
|
173
|
+
_answer(False)
|
|
174
|
+
except Exception:
|
|
175
|
+
traceback.print_exc()
|
|
176
|
+
|
|
177
|
+
@staticmethod
|
|
178
|
+
def _c_device_info_update(ctx, profile, info):
|
|
179
|
+
_mark_sdk_thread()
|
|
180
|
+
try:
|
|
181
|
+
self = _ctx_self(ctx)
|
|
182
|
+
if info:
|
|
183
|
+
self._device_info = DeviceInfo._from_c(info.contents)
|
|
184
|
+
cb = self._on_device_info_update
|
|
185
|
+
if cb is not None and self._device_info is not None:
|
|
186
|
+
cb(self, self._device_info)
|
|
187
|
+
except Exception:
|
|
188
|
+
traceback.print_exc()
|
|
189
|
+
|
|
190
|
+
@staticmethod
|
|
191
|
+
def _c_data_transfer_state_change(ctx, profile, is_transferring):
|
|
192
|
+
_mark_sdk_thread()
|
|
193
|
+
try:
|
|
194
|
+
self = _ctx_self(ctx)
|
|
195
|
+
cb = self._on_data_transfer_state_change
|
|
196
|
+
if cb is not None:
|
|
197
|
+
cb(self, is_transferring != 0)
|
|
198
|
+
except Exception:
|
|
199
|
+
traceback.print_exc()
|
|
200
|
+
|
|
201
|
+
@staticmethod
|
|
202
|
+
def _copy_view(view) -> SensorData:
|
|
203
|
+
sd = SensorData()
|
|
204
|
+
info_addr = ctypes.cast(view.info, ctypes.c_void_p).value
|
|
205
|
+
sd._info = ctypes.cast(info_addr, ctypes.POINTER(_dll.sen_data_info_t)) if info_addr else None
|
|
206
|
+
sd._startSampleIndex = view.startSampleIndex
|
|
207
|
+
sd._view_start_time_stamp = view.startTimeStamp
|
|
208
|
+
samples_addr = ctypes.cast(view.samples, ctypes.c_void_p).value
|
|
209
|
+
if samples_addr and view.samplesBytes > 0:
|
|
210
|
+
sd._buf = (ctypes.c_char * view.samplesBytes).from_address(samples_addr)
|
|
211
|
+
return sd
|
|
212
|
+
|
|
213
|
+
@property
|
|
214
|
+
def on_error_callback(self):
|
|
215
|
+
return self._on_error_callback
|
|
216
|
+
|
|
217
|
+
@on_error_callback.setter
|
|
218
|
+
def on_error_callback(self, callback):
|
|
219
|
+
self._on_error_callback = callback
|
|
220
|
+
|
|
221
|
+
@property
|
|
222
|
+
def on_state_change(self):
|
|
223
|
+
return self._on_state_change
|
|
224
|
+
|
|
225
|
+
@on_state_change.setter
|
|
226
|
+
def on_state_change(self, callback):
|
|
227
|
+
self._on_state_change = callback
|
|
228
|
+
|
|
229
|
+
@property
|
|
230
|
+
def on_sensor_notify_data(self):
|
|
231
|
+
return self._on_sensor_notify_data
|
|
232
|
+
|
|
233
|
+
@on_sensor_notify_data.setter
|
|
234
|
+
def on_sensor_notify_data(self, callback):
|
|
235
|
+
self._on_sensor_notify_data = callback
|
|
236
|
+
|
|
237
|
+
@property
|
|
238
|
+
def on_auto_reconnect(self):
|
|
239
|
+
return self._on_auto_reconnect_orig
|
|
240
|
+
|
|
241
|
+
@on_auto_reconnect.setter
|
|
242
|
+
def on_auto_reconnect(self, callback):
|
|
243
|
+
self._on_auto_reconnect_orig = callback
|
|
244
|
+
self._on_auto_reconnect = _wrap_auto_reconnect_callback(callback)
|
|
245
|
+
|
|
246
|
+
@property
|
|
247
|
+
def onAutoReconnect(self):
|
|
248
|
+
return self._on_auto_reconnect_orig
|
|
249
|
+
|
|
250
|
+
@onAutoReconnect.setter
|
|
251
|
+
def onAutoReconnect(self, callback):
|
|
252
|
+
self._on_auto_reconnect_orig = callback
|
|
253
|
+
self._on_auto_reconnect = _wrap_auto_reconnect_callback(callback)
|
|
254
|
+
|
|
255
|
+
@property
|
|
256
|
+
def on_power_changed(self):
|
|
257
|
+
return self._on_power_changed
|
|
258
|
+
|
|
259
|
+
@on_power_changed.setter
|
|
260
|
+
def on_power_changed(self, callback):
|
|
261
|
+
self._on_power_changed = callback
|
|
262
|
+
|
|
263
|
+
@property
|
|
264
|
+
def on_device_info_update(self):
|
|
265
|
+
return self._on_device_info_update
|
|
266
|
+
|
|
267
|
+
@on_device_info_update.setter
|
|
268
|
+
def on_device_info_update(self, callback):
|
|
269
|
+
self._on_device_info_update = callback
|
|
270
|
+
|
|
271
|
+
@property
|
|
272
|
+
def on_data_transfer_state_change(self):
|
|
273
|
+
return self._on_data_transfer_state_change
|
|
274
|
+
|
|
275
|
+
@on_data_transfer_state_change.setter
|
|
276
|
+
def on_data_transfer_state_change(self, callback):
|
|
277
|
+
self._on_data_transfer_state_change = callback
|
|
278
|
+
|
|
279
|
+
@property
|
|
280
|
+
def onStateChanged(self):
|
|
281
|
+
return self._on_state_change
|
|
282
|
+
|
|
283
|
+
@onStateChanged.setter
|
|
284
|
+
def onStateChanged(self, callback):
|
|
285
|
+
self._on_state_change = callback
|
|
286
|
+
|
|
287
|
+
@property
|
|
288
|
+
def onErrorCallback(self):
|
|
289
|
+
return self._on_error_callback
|
|
290
|
+
|
|
291
|
+
@onErrorCallback.setter
|
|
292
|
+
def onErrorCallback(self, callback):
|
|
293
|
+
self._on_error_callback = callback
|
|
294
|
+
|
|
295
|
+
@property
|
|
296
|
+
def onDataCallback(self):
|
|
297
|
+
return self._on_sensor_notify_data
|
|
298
|
+
|
|
299
|
+
@onDataCallback.setter
|
|
300
|
+
def onDataCallback(self, callback):
|
|
301
|
+
self._on_sensor_notify_data = callback
|
|
302
|
+
|
|
303
|
+
@property
|
|
304
|
+
def onPowerChanged(self):
|
|
305
|
+
return self._on_power_changed
|
|
306
|
+
|
|
307
|
+
@onPowerChanged.setter
|
|
308
|
+
def onPowerChanged(self, callback):
|
|
309
|
+
self._on_power_changed = callback
|
|
310
|
+
|
|
311
|
+
@property
|
|
312
|
+
def onDeviceInfoUpdate(self):
|
|
313
|
+
return self._on_device_info_update
|
|
314
|
+
|
|
315
|
+
@onDeviceInfoUpdate.setter
|
|
316
|
+
def onDeviceInfoUpdate(self, callback):
|
|
317
|
+
self._on_device_info_update = callback
|
|
318
|
+
|
|
319
|
+
@property
|
|
320
|
+
def onDataTransferStateChange(self):
|
|
321
|
+
return self._on_data_transfer_state_change
|
|
322
|
+
|
|
323
|
+
@onDataTransferStateChange.setter
|
|
324
|
+
def onDataTransferStateChange(self, callback):
|
|
325
|
+
self._on_data_transfer_state_change = callback
|
|
326
|
+
|
|
327
|
+
@property
|
|
328
|
+
def autoReconnect(self) -> bool:
|
|
329
|
+
return self._auto_reconnect
|
|
330
|
+
|
|
331
|
+
@autoReconnect.setter
|
|
332
|
+
def autoReconnect(self, enabled: bool):
|
|
333
|
+
self.setAutoReconnect(enabled)
|
|
334
|
+
self._auto_reconnect = bool(enabled)
|
|
335
|
+
|
|
336
|
+
@property
|
|
337
|
+
def BLEDevice(self) -> BLEDevice:
|
|
338
|
+
if self._ble_device is None:
|
|
339
|
+
dev = _dll.sen_ble_device_t()
|
|
340
|
+
_dll.load_dll().sen_profile_get_device(self._handle, ctypes.byref(dev))
|
|
341
|
+
self._ble_device = BLEDevice._from_c(dev)
|
|
342
|
+
return self._ble_device
|
|
343
|
+
|
|
344
|
+
@property
|
|
345
|
+
def deviceState(self) -> DeviceStateEx:
|
|
346
|
+
return DeviceStateEx(_dll.load_dll().sen_profile_get_state(self._handle))
|
|
347
|
+
|
|
348
|
+
@property
|
|
349
|
+
def isReady(self) -> bool:
|
|
350
|
+
return self.deviceState == DeviceStateEx.Ready
|
|
351
|
+
|
|
352
|
+
@property
|
|
353
|
+
def hasInited(self) -> bool:
|
|
354
|
+
return _dll.load_dll().sen_profile_has_init(self._handle) != 0
|
|
355
|
+
|
|
356
|
+
@property
|
|
357
|
+
def isDataTransfering(self) -> bool:
|
|
358
|
+
return _dll.load_dll().sen_profile_has_start_data_notification(self._handle) != 0
|
|
359
|
+
|
|
360
|
+
def _track(self, cb_obj):
|
|
361
|
+
self._pending_cbs.append(cb_obj)
|
|
362
|
+
return cb_obj
|
|
363
|
+
|
|
364
|
+
def _discard(self, cb_obj):
|
|
365
|
+
try:
|
|
366
|
+
self._pending_cbs.remove(cb_obj)
|
|
367
|
+
except ValueError:
|
|
368
|
+
pass
|
|
369
|
+
|
|
370
|
+
def _connect(self, cb: Optional[Callable[[bool, str], None]] = None):
|
|
371
|
+
@_dll._sen_completion_cb
|
|
372
|
+
def _cb(ctx, result, errorMsg):
|
|
373
|
+
_mark_sdk_thread()
|
|
374
|
+
try:
|
|
375
|
+
if cb is not None:
|
|
376
|
+
cb(result != 0, _decode_str(errorMsg))
|
|
377
|
+
finally:
|
|
378
|
+
self._discard(_cb)
|
|
379
|
+
self._track(_cb)
|
|
380
|
+
_dll.load_dll().sen_profile_connect(self._handle, _cb, None)
|
|
381
|
+
|
|
382
|
+
def _disconnect(self, cb: Optional[Callable[[bool, str], None]] = None):
|
|
383
|
+
@_dll._sen_completion_cb
|
|
384
|
+
def _cb(ctx, result, errorMsg):
|
|
385
|
+
_mark_sdk_thread()
|
|
386
|
+
try:
|
|
387
|
+
if cb is not None:
|
|
388
|
+
cb(result != 0, _decode_str(errorMsg))
|
|
389
|
+
finally:
|
|
390
|
+
self._discard(_cb)
|
|
391
|
+
self._track(_cb)
|
|
392
|
+
_dll.load_dll().sen_profile_disconnect(self._handle, _cb, None)
|
|
393
|
+
|
|
394
|
+
def setAutoReconnect(self, enabled: bool):
|
|
395
|
+
_dll.load_dll().sen_profile_set_auto_reconnect(self._handle, 1 if enabled else 0)
|
|
396
|
+
|
|
397
|
+
def _init(self, packageSampleCount: int, powerRefreshInterval: int,
|
|
398
|
+
timeoutInMS: int, cb: Callable[[bool, str], None]):
|
|
399
|
+
@_dll._sen_completion_cb
|
|
400
|
+
def _cb(ctx, result, errorMsg):
|
|
401
|
+
_mark_sdk_thread()
|
|
402
|
+
try:
|
|
403
|
+
if cb is not None:
|
|
404
|
+
cb(result != 0, _decode_str(errorMsg))
|
|
405
|
+
finally:
|
|
406
|
+
self._discard(_cb)
|
|
407
|
+
self._track(_cb)
|
|
408
|
+
_dll.load_dll().sen_profile_init(
|
|
409
|
+
self._handle, packageSampleCount, timeoutInMS, powerRefreshInterval, _cb, None)
|
|
410
|
+
|
|
411
|
+
def _startDataNotification(self, timeoutInMS: int, cb: Callable[[bool, str], None]):
|
|
412
|
+
@_dll._sen_completion_cb
|
|
413
|
+
def _cb(ctx, result, errorMsg):
|
|
414
|
+
_mark_sdk_thread()
|
|
415
|
+
try:
|
|
416
|
+
if cb is not None:
|
|
417
|
+
cb(result != 0, _decode_str(errorMsg))
|
|
418
|
+
finally:
|
|
419
|
+
self._discard(_cb)
|
|
420
|
+
self._track(_cb)
|
|
421
|
+
_dll.load_dll().sen_profile_start_data(self._handle, timeoutInMS, _cb, None)
|
|
422
|
+
|
|
423
|
+
def _stopDataNotification(self, timeoutInMS: int, cb: Callable[[bool, str], None]):
|
|
424
|
+
@_dll._sen_completion_cb
|
|
425
|
+
def _cb(ctx, result, errorMsg):
|
|
426
|
+
_mark_sdk_thread()
|
|
427
|
+
try:
|
|
428
|
+
if cb is not None:
|
|
429
|
+
cb(result != 0, _decode_str(errorMsg))
|
|
430
|
+
finally:
|
|
431
|
+
self._discard(_cb)
|
|
432
|
+
self._track(_cb)
|
|
433
|
+
_dll.load_dll().sen_profile_stop_data(self._handle, timeoutInMS, _cb, None)
|
|
434
|
+
|
|
435
|
+
def _setParam(self, timeoutInMS: int, key: str, value: str,
|
|
436
|
+
cb: Callable[[str, str], None]):
|
|
437
|
+
@_dll._sen_param_cb
|
|
438
|
+
def _cb(ctx, result, errorMsg):
|
|
439
|
+
_mark_sdk_thread()
|
|
440
|
+
try:
|
|
441
|
+
if cb is not None:
|
|
442
|
+
cb(_decode_str(result), _decode_str(errorMsg))
|
|
443
|
+
finally:
|
|
444
|
+
self._discard(_cb)
|
|
445
|
+
self._track(_cb)
|
|
446
|
+
_dll.load_dll().sen_profile_set_param(
|
|
447
|
+
self._handle, timeoutInMS, key.encode(), value.encode(), _cb, None)
|
|
448
|
+
|
|
449
|
+
def _getParam(self, timeoutInMS: int, key: str, cb: Callable[[str, str], None]):
|
|
450
|
+
@_dll._sen_param_cb
|
|
451
|
+
def _cb(ctx, result, errorMsg):
|
|
452
|
+
_mark_sdk_thread()
|
|
453
|
+
try:
|
|
454
|
+
if cb is not None:
|
|
455
|
+
cb(_decode_str(result), _decode_str(errorMsg))
|
|
456
|
+
finally:
|
|
457
|
+
self._discard(_cb)
|
|
458
|
+
self._track(_cb)
|
|
459
|
+
_dll.load_dll().sen_profile_get_param(
|
|
460
|
+
self._handle, timeoutInMS, key.encode(), _cb, None)
|
|
461
|
+
|
|
462
|
+
def _getBatteryLevel(self, timeoutInMS: int, cb: Callable[[int, str], None]):
|
|
463
|
+
@_dll._sen_battery_cb
|
|
464
|
+
def _cb(ctx, result, errorMsg):
|
|
465
|
+
_mark_sdk_thread()
|
|
466
|
+
try:
|
|
467
|
+
if result >= 0:
|
|
468
|
+
self._power = result
|
|
469
|
+
if cb is not None:
|
|
470
|
+
cb(result, _decode_str(errorMsg))
|
|
471
|
+
finally:
|
|
472
|
+
self._discard(_cb)
|
|
473
|
+
self._track(_cb)
|
|
474
|
+
_dll.load_dll().sen_profile_get_battery_level(self._handle, timeoutInMS, _cb, None)
|
|
475
|
+
|
|
476
|
+
def _fetchDeviceInfo(self, timeoutInMS: int,
|
|
477
|
+
cb: Callable[[Optional[DeviceInfo], str], None]):
|
|
478
|
+
@_dll._sen_info_cb
|
|
479
|
+
def _cb(ctx, info, errorMsg):
|
|
480
|
+
_mark_sdk_thread()
|
|
481
|
+
try:
|
|
482
|
+
if cb is not None:
|
|
483
|
+
out = DeviceInfo._from_c(info.contents) if info else None
|
|
484
|
+
if out is not None:
|
|
485
|
+
self._device_info = out
|
|
486
|
+
cb(out, _decode_str(errorMsg))
|
|
487
|
+
finally:
|
|
488
|
+
self._discard(_cb)
|
|
489
|
+
self._track(_cb)
|
|
490
|
+
_dll.load_dll().sen_profile_fetch_device_info(self._handle, timeoutInMS, _cb, None)
|
|
491
|
+
|
|
492
|
+
def connect(self):
|
|
493
|
+
_raise_on_sdk_thread()
|
|
494
|
+
try:
|
|
495
|
+
return _sync.run_sync(self.asyncConnect(), 40.0)
|
|
496
|
+
except _sync.FuturesTimeoutError:
|
|
497
|
+
return False
|
|
498
|
+
|
|
499
|
+
def disconnect(self):
|
|
500
|
+
_raise_on_sdk_thread()
|
|
501
|
+
try:
|
|
502
|
+
return _sync.run_sync(self.asyncDisconnect(), 25.0)
|
|
503
|
+
except _sync.FuturesTimeoutError:
|
|
504
|
+
return None
|
|
505
|
+
|
|
506
|
+
def init(self, packageSampleCount: int, powerRefreshInterval: int,
|
|
507
|
+
timeoutInMS: int = 0):
|
|
508
|
+
_raise_on_sdk_thread()
|
|
509
|
+
try:
|
|
510
|
+
return _sync.run_sync(
|
|
511
|
+
self.asyncInit(packageSampleCount, powerRefreshInterval, timeoutInMS),
|
|
512
|
+
_sync.effective_timeout_ms(timeoutInMS) / 1000.0 + 15.0)
|
|
513
|
+
except _sync.FuturesTimeoutError:
|
|
514
|
+
return None
|
|
515
|
+
|
|
516
|
+
def startDataNotification(self, timeoutInMS: int = 0):
|
|
517
|
+
_raise_on_sdk_thread()
|
|
518
|
+
try:
|
|
519
|
+
return _sync.run_sync(self.asyncStartDataNotification(timeoutInMS),
|
|
520
|
+
_sync.effective_timeout_ms(timeoutInMS) / 1000.0 + 15.0)
|
|
521
|
+
except _sync.FuturesTimeoutError:
|
|
522
|
+
return None
|
|
523
|
+
|
|
524
|
+
def stopDataNotification(self, timeoutInMS: int = 0):
|
|
525
|
+
_raise_on_sdk_thread()
|
|
526
|
+
try:
|
|
527
|
+
return _sync.run_sync(self.asyncStopDataNotification(timeoutInMS),
|
|
528
|
+
_sync.effective_timeout_ms(timeoutInMS) / 1000.0 + 15.0)
|
|
529
|
+
except _sync.FuturesTimeoutError:
|
|
530
|
+
return None
|
|
531
|
+
|
|
532
|
+
def setParam(self, key: str, value, timeoutInMS: int = 0) -> str:
|
|
533
|
+
_raise_on_sdk_thread()
|
|
534
|
+
try:
|
|
535
|
+
return _sync.run_sync(self.asyncSetParam(key, value, timeoutInMS),
|
|
536
|
+
_sync.effective_timeout_ms(timeoutInMS) / 1000.0 + 15.0)
|
|
537
|
+
except _sync.FuturesTimeoutError:
|
|
538
|
+
return "Error: Timeout"
|
|
539
|
+
|
|
540
|
+
def getParam(self, key: str, timeoutInMS: int = 0) -> str:
|
|
541
|
+
_raise_on_sdk_thread()
|
|
542
|
+
try:
|
|
543
|
+
return _sync.run_sync(self.asyncGetParam(key, timeoutInMS),
|
|
544
|
+
_sync.effective_timeout_ms(timeoutInMS) / 1000.0 + 15.0)
|
|
545
|
+
except _sync.FuturesTimeoutError:
|
|
546
|
+
return "Error: Timeout"
|
|
547
|
+
|
|
548
|
+
def getBatteryLevel(self) -> int:
|
|
549
|
+
if self._power >= 0:
|
|
550
|
+
return self._power
|
|
551
|
+
_raise_on_sdk_thread()
|
|
552
|
+
try:
|
|
553
|
+
return _sync.run_sync(self.asyncGetBatteryLevel(),
|
|
554
|
+
_sync.effective_timeout_ms(0) / 1000.0 + 17.0)
|
|
555
|
+
except _sync.FuturesTimeoutError:
|
|
556
|
+
return -1
|
|
557
|
+
|
|
558
|
+
def fetchDeviceInfo(self, timeoutInMS: int = 0) -> Optional[DeviceInfo]:
|
|
559
|
+
_raise_on_sdk_thread()
|
|
560
|
+
try:
|
|
561
|
+
return _sync.run_sync(self.asyncFetchDeviceInfo(timeoutInMS),
|
|
562
|
+
_sync.effective_timeout_ms(timeoutInMS) / 1000.0 + 15.0)
|
|
563
|
+
except _sync.FuturesTimeoutError:
|
|
564
|
+
return None
|
|
565
|
+
|
|
566
|
+
def getDeviceInfo(self) -> Optional[DeviceInfo]:
|
|
567
|
+
if self._device_info is not None:
|
|
568
|
+
return self._device_info
|
|
569
|
+
info = _dll.sen_device_info_t()
|
|
570
|
+
info.structSize = ctypes.sizeof(info)
|
|
571
|
+
_dll.load_dll().sen_profile_get_device_info(self._handle, ctypes.byref(info))
|
|
572
|
+
out = DeviceInfo._from_c(info)
|
|
573
|
+
if not (out.DeviceName or out.ModelName):
|
|
574
|
+
return None
|
|
575
|
+
self._device_info = out
|
|
576
|
+
return out
|
|
577
|
+
|
|
578
|
+
def log(self, message: str, level: str = "I"):
|
|
579
|
+
try:
|
|
580
|
+
_dll.load_dll().sen_profile_log(
|
|
581
|
+
self._handle, str(message).encode(), str(level).encode())
|
|
582
|
+
except Exception:
|
|
583
|
+
traceback.print_exc()
|
|
584
|
+
|
|
585
|
+
async def asyncConnect(self) -> bool:
|
|
586
|
+
loop = asyncio.get_running_loop()
|
|
587
|
+
fut = loop.create_future()
|
|
588
|
+
self._connect(cb=lambda ok, err: _resolve_future(loop, fut, ok))
|
|
589
|
+
try:
|
|
590
|
+
return await asyncio.wait_for(fut, 30.0)
|
|
591
|
+
except asyncio.TimeoutError:
|
|
592
|
+
return False
|
|
593
|
+
|
|
594
|
+
async def asyncDisconnect(self) -> bool:
|
|
595
|
+
loop = asyncio.get_running_loop()
|
|
596
|
+
fut = loop.create_future()
|
|
597
|
+
self._disconnect(cb=lambda ok, err: _resolve_future(loop, fut, ok))
|
|
598
|
+
try:
|
|
599
|
+
return await asyncio.wait_for(fut, 15.0)
|
|
600
|
+
except asyncio.TimeoutError:
|
|
601
|
+
return False
|
|
602
|
+
|
|
603
|
+
async def asyncInit(self, packageSampleCount: int, powerRefreshInterval: int,
|
|
604
|
+
timeoutInMS: int = 0) -> bool:
|
|
605
|
+
loop = asyncio.get_running_loop()
|
|
606
|
+
fut = loop.create_future()
|
|
607
|
+
self._init(packageSampleCount, powerRefreshInterval, timeoutInMS,
|
|
608
|
+
cb=lambda ok, err: _resolve_future(loop, fut, ok))
|
|
609
|
+
try:
|
|
610
|
+
return await asyncio.wait_for(
|
|
611
|
+
fut, _sync.effective_timeout_ms(timeoutInMS) / 1000.0 + 5.0)
|
|
612
|
+
except asyncio.TimeoutError:
|
|
613
|
+
return False
|
|
614
|
+
|
|
615
|
+
async def asyncStartDataNotification(self, timeoutInMS: int = 0) -> bool:
|
|
616
|
+
loop = asyncio.get_running_loop()
|
|
617
|
+
fut = loop.create_future()
|
|
618
|
+
self._startDataNotification(timeoutInMS, cb=lambda ok, err: _resolve_future(loop, fut, ok))
|
|
619
|
+
try:
|
|
620
|
+
return await asyncio.wait_for(
|
|
621
|
+
fut, _sync.effective_timeout_ms(timeoutInMS) / 1000.0 + 5.0)
|
|
622
|
+
except asyncio.TimeoutError:
|
|
623
|
+
return False
|
|
624
|
+
|
|
625
|
+
async def asyncStopDataNotification(self, timeoutInMS: int = 0) -> bool:
|
|
626
|
+
loop = asyncio.get_running_loop()
|
|
627
|
+
fut = loop.create_future()
|
|
628
|
+
self._stopDataNotification(timeoutInMS, cb=lambda ok, err: _resolve_future(loop, fut, ok))
|
|
629
|
+
try:
|
|
630
|
+
return await asyncio.wait_for(
|
|
631
|
+
fut, _sync.effective_timeout_ms(timeoutInMS) / 1000.0 + 5.0)
|
|
632
|
+
except asyncio.TimeoutError:
|
|
633
|
+
return False
|
|
634
|
+
|
|
635
|
+
async def asyncGetBatteryLevel(self) -> int:
|
|
636
|
+
loop = asyncio.get_running_loop()
|
|
637
|
+
fut = loop.create_future()
|
|
638
|
+
self._getBatteryLevel(0, cb=lambda level, err: _resolve_future(loop, fut, level))
|
|
639
|
+
try:
|
|
640
|
+
return await asyncio.wait_for(
|
|
641
|
+
fut, _sync.effective_timeout_ms(0) / 1000.0 + 2.0)
|
|
642
|
+
except asyncio.TimeoutError:
|
|
643
|
+
return -1
|
|
644
|
+
|
|
645
|
+
async def asyncSetParam(self, key: str, value, timeoutInMS: int = 0) -> str:
|
|
646
|
+
loop = asyncio.get_running_loop()
|
|
647
|
+
fut = loop.create_future()
|
|
648
|
+
self._setParam(timeoutInMS, key, str(value),
|
|
649
|
+
cb=lambda res, err: _resolve_future(loop, fut, err if err and res in ('', 'Error') else res))
|
|
650
|
+
try:
|
|
651
|
+
return await asyncio.wait_for(
|
|
652
|
+
fut, _sync.effective_timeout_ms(timeoutInMS) / 1000.0 + 2.0)
|
|
653
|
+
except asyncio.TimeoutError:
|
|
654
|
+
return "Error: Timeout"
|
|
655
|
+
|
|
656
|
+
async def asyncGetParam(self, key: str, timeoutInMS: int = 0) -> str:
|
|
657
|
+
loop = asyncio.get_running_loop()
|
|
658
|
+
fut = loop.create_future()
|
|
659
|
+
self._getParam(timeoutInMS, key, cb=lambda res, err: _resolve_future(loop, fut, err if err and res in ('', 'Error') else res))
|
|
660
|
+
try:
|
|
661
|
+
return await asyncio.wait_for(
|
|
662
|
+
fut, _sync.effective_timeout_ms(timeoutInMS) / 1000.0 + 2.0)
|
|
663
|
+
except asyncio.TimeoutError:
|
|
664
|
+
return "Error: Timeout"
|
|
665
|
+
|
|
666
|
+
async def asyncFetchDeviceInfo(self, timeoutInMS: int = 0) -> Optional[DeviceInfo]:
|
|
667
|
+
loop = asyncio.get_running_loop()
|
|
668
|
+
fut = loop.create_future()
|
|
669
|
+
self._fetchDeviceInfo(timeoutInMS, cb=lambda info, err: _resolve_future(loop, fut, info))
|
|
670
|
+
try:
|
|
671
|
+
return await asyncio.wait_for(
|
|
672
|
+
fut, _sync.effective_timeout_ms(timeoutInMS) / 1000.0 + 5.0)
|
|
673
|
+
except asyncio.TimeoutError:
|
|
674
|
+
return None
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sensor-sdk
|
|
3
|
+
Version: 1.3.3
|
|
4
|
+
Summary: Python sdk for Synchroni
|
|
5
|
+
Home-page: https://github.com/oymotion/SynchroniSDKPython
|
|
6
|
+
Author: Martin Ye
|
|
7
|
+
Author-email: yecq_82@hotmail.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Requires-Dist: numpy
|
|
13
|
+
Dynamic: author
|
|
14
|
+
Dynamic: author-email
|
|
15
|
+
Dynamic: classifier
|
|
16
|
+
Dynamic: description
|
|
17
|
+
Dynamic: home-page
|
|
18
|
+
Dynamic: license
|
|
19
|
+
Dynamic: requires-dist
|
|
20
|
+
Dynamic: requires-python
|
|
21
|
+
Dynamic: summary
|
|
22
|
+
|
|
23
|
+
Pure-ctypes Python bindings over the SensorSDKCXX flat C API (drop-in replacement for the SensorSDKPython package), bundling the native sensor library and the frozen BLE helper (USB dongle backend).
|