incyclist-devices 1.4.42 → 1.4.45
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.
- package/lib/CyclingMode.js +1 -0
- package/lib/Device.js +1 -0
- package/lib/DeviceProtocol.js +1 -0
- package/lib/DeviceSupport.js +25 -8
- package/lib/ant/AntAdapter.js +1 -0
- package/lib/ant/AntScanner.js +24 -7
- package/lib/ant/antfe/AntFEAdapter.js +7 -7
- package/lib/ant/anthrm/AntHrmAdapter.js +1 -1
- package/lib/ant/antpwr/pwr-adapter.js +1 -1
- package/lib/ant/utils.js +3 -1
- package/lib/ble/ble-device.d.ts +10 -3
- package/lib/ble/ble-device.js +144 -32
- package/lib/ble/ble-interface.d.ts +22 -4
- package/lib/ble/ble-interface.js +224 -167
- package/lib/ble/ble.d.ts +20 -1
- package/lib/ble/ble.js +3 -1
- package/lib/ble/fm.d.ts +11 -1
- package/lib/ble/fm.js +109 -11
- package/lib/ble/hrm.d.ts +0 -1
- package/lib/ble/hrm.js +1 -4
- package/lib/ble/incyclist-protocol.js +24 -8
- package/lib/ble/pwr.d.ts +1 -1
- package/lib/ble/pwr.js +14 -4
- package/lib/calculations.js +1 -0
- package/lib/daum/DaumAdapter.js +29 -13
- package/lib/daum/SmartTrainerCyclingMode.js +1 -0
- package/lib/daum/classic/DaumClassicAdapter.js +1 -1
- package/lib/daum/classic/DaumClassicProtocol.js +23 -7
- package/lib/daum/classic/bike.js +26 -26
- package/lib/daum/classic/utils.js +1 -0
- package/lib/daum/constants.js +1 -0
- package/lib/daum/premium/DaumPremiumAdapter.js +1 -1
- package/lib/daum/premium/DaumPremiumProtocol.js +23 -7
- package/lib/daum/premium/bike.js +18 -17
- package/lib/daum/premium/utils.js +1 -0
- package/lib/kettler/comms.d.ts +1 -0
- package/lib/kettler/comms.js +2 -1
- package/lib/kettler/ergo-racer/adapter.js +25 -9
- package/lib/kettler/ergo-racer/protocol.d.ts +1 -1
- package/lib/kettler/ergo-racer/protocol.js +23 -7
- package/lib/modes/power-meter.js +1 -0
- package/lib/simulator/Simulator.d.ts +1 -1
- package/lib/simulator/Simulator.js +24 -7
- package/lib/types/route.js +1 -0
- package/lib/types/user.js +1 -0
- package/lib/utils.js +3 -1
- package/package.json +1 -1
package/lib/ble/ble-interface.js
CHANGED
|
@@ -13,18 +13,19 @@ const gd_eventlog_1 = require("gd-eventlog");
|
|
|
13
13
|
const utils_1 = require("../utils");
|
|
14
14
|
const ble_1 = require("./ble");
|
|
15
15
|
const CONNECT_TIMEOUT = 5000;
|
|
16
|
+
const DEFAULT_SCAN_TIMEOUT = 20000;
|
|
17
|
+
const BACKGROUND_SCAN_TIMEOUT = 30000;
|
|
18
|
+
const DEFAULT_SERVICES = ['1818', '180d', '1826'];
|
|
16
19
|
class BleInterface extends ble_1.BleInterfaceClass {
|
|
17
20
|
constructor(props = {}) {
|
|
18
21
|
super(props);
|
|
19
22
|
this.scanState = { isScanning: false, isConnecting: false, timeout: undefined, isBackgroundScan: false };
|
|
20
23
|
this.connectState = { isConnecting: false, isConnected: false, isInitSuccess: false };
|
|
21
24
|
this.devices = [];
|
|
22
|
-
this.
|
|
25
|
+
this.peripheralCache = [];
|
|
23
26
|
if (props.logger)
|
|
24
27
|
this.logger = props.logger;
|
|
25
|
-
|
|
26
|
-
this.logger = new gd_eventlog_1.EventLogger('BLE');
|
|
27
|
-
}
|
|
28
|
+
this.logger = new gd_eventlog_1.EventLogger('BLE');
|
|
28
29
|
}
|
|
29
30
|
static getInstance(props = {}) {
|
|
30
31
|
if (!BleInterface._instance) {
|
|
@@ -71,7 +72,7 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
71
72
|
const timeout = props.timeout || 2000;
|
|
72
73
|
const runBackgroundScan = () => {
|
|
73
74
|
this.scanState.isBackgroundScan = true;
|
|
74
|
-
this.scan({ timeout:
|
|
75
|
+
this.scan({ timeout: BACKGROUND_SCAN_TIMEOUT, isBackgroundScan: true })
|
|
75
76
|
.then(() => {
|
|
76
77
|
this.scanState.isBackgroundScan = false;
|
|
77
78
|
})
|
|
@@ -210,7 +211,7 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
210
211
|
});
|
|
211
212
|
};
|
|
212
213
|
if (typeof services === 'string') {
|
|
213
|
-
return get(deviceTypes, (s) => s === ble_1.uuid(services));
|
|
214
|
+
return get(deviceTypes, (s) => s === (0, ble_1.uuid)(services));
|
|
214
215
|
}
|
|
215
216
|
if (Array.isArray(services)) {
|
|
216
217
|
return get(deviceTypes, s => services.map(ble_1.uuid).includes(s));
|
|
@@ -218,19 +219,24 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
218
219
|
return [];
|
|
219
220
|
}
|
|
220
221
|
getServicesFromDeviceTypes(deviceTypes) {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
deviceTypes.forEach(DeviceType => {
|
|
226
|
-
if (DeviceType.services) {
|
|
227
|
-
const dtServices = DeviceType.services;
|
|
228
|
-
dtServices.forEach(s => {
|
|
229
|
-
if (!services.find(s2 => s2 === s))
|
|
230
|
-
services.push(s);
|
|
231
|
-
});
|
|
222
|
+
let services = [];
|
|
223
|
+
try {
|
|
224
|
+
if (!deviceTypes || !Array.isArray(deviceTypes) || deviceTypes.length === 0) {
|
|
225
|
+
return [];
|
|
232
226
|
}
|
|
233
|
-
|
|
227
|
+
deviceTypes.forEach(DeviceType => {
|
|
228
|
+
if (DeviceType.services) {
|
|
229
|
+
const dtServices = DeviceType.services;
|
|
230
|
+
dtServices.forEach(s => {
|
|
231
|
+
if (!services.find(s2 => s2 === s))
|
|
232
|
+
services.push(s);
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
catch (err) {
|
|
238
|
+
console.log(err);
|
|
239
|
+
}
|
|
234
240
|
return services;
|
|
235
241
|
}
|
|
236
242
|
getServicesFromDevice(device) {
|
|
@@ -260,15 +266,117 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
260
266
|
}, 100);
|
|
261
267
|
});
|
|
262
268
|
}
|
|
263
|
-
|
|
269
|
+
addPeripheralToCache(peripheral, props = {}) {
|
|
270
|
+
this.peripheralCache.push(Object.assign({ address: peripheral.address, ts: Date.now(), peripheral }, props));
|
|
271
|
+
}
|
|
272
|
+
getCharacteristics(peripheral) {
|
|
264
273
|
return __awaiter(this, void 0, void 0, function* () {
|
|
265
|
-
|
|
266
|
-
|
|
274
|
+
let characteristics = undefined;
|
|
275
|
+
let chachedPeripheralInfo = this.peripheralCache.find(i => i.address === peripheral.address);
|
|
276
|
+
if (chachedPeripheralInfo && Date.now() - chachedPeripheralInfo.ts > 600000) {
|
|
277
|
+
chachedPeripheralInfo.ts = Date.now();
|
|
278
|
+
}
|
|
279
|
+
if (!chachedPeripheralInfo) {
|
|
280
|
+
this.addPeripheralToCache(peripheral);
|
|
281
|
+
chachedPeripheralInfo = this.peripheralCache.find(i => i.address === peripheral.address);
|
|
282
|
+
}
|
|
283
|
+
if (!chachedPeripheralInfo.characteristics) {
|
|
284
|
+
try {
|
|
285
|
+
chachedPeripheralInfo.state = { isConfigured: false, isLoading: true, isInterrupted: false };
|
|
286
|
+
if (chachedPeripheralInfo.peripheral && chachedPeripheralInfo.peripheral.state !== 'connected') {
|
|
287
|
+
yield peripheral.connectAsync();
|
|
288
|
+
chachedPeripheralInfo.peripheral.state = peripheral.state;
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
peripheral.state = chachedPeripheralInfo.peripheral.state;
|
|
292
|
+
}
|
|
293
|
+
const res = yield peripheral.discoverSomeServicesAndCharacteristicsAsync([], []);
|
|
294
|
+
if (!chachedPeripheralInfo.state.isInterrupted) {
|
|
295
|
+
this.logEvent({ message: 'characteristic info (+):', info: res.characteristics.map(c => `${peripheral.address} ${c.uuid} ${c.properties}`) });
|
|
296
|
+
chachedPeripheralInfo.characteristics = res.characteristics;
|
|
297
|
+
chachedPeripheralInfo.state = { isConfigured: true, isLoading: false, isInterrupted: false };
|
|
298
|
+
characteristics = res.characteristics;
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
this.logEvent({ message: 'characteristic info:', info: 'interrupted' });
|
|
302
|
+
chachedPeripheralInfo.state = { isConfigured: false, isLoading: false, isInterrupted: false };
|
|
303
|
+
throw new Error('interrupted');
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
catch (err) {
|
|
307
|
+
console.log(err);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
characteristics = chachedPeripheralInfo.characteristics;
|
|
312
|
+
this.logEvent({ message: 'characteristic info (*):', info: characteristics.map(c => `${peripheral.address} ${c.uuid} ${c.properties}`) });
|
|
313
|
+
}
|
|
314
|
+
if (!characteristics)
|
|
315
|
+
this.logEvent({ message: 'characteristic info:', info: 'none' });
|
|
316
|
+
return characteristics;
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
getDeviceClasses(peripheral, props = {}) {
|
|
320
|
+
let DeviceClasses;
|
|
321
|
+
const { deviceTypes, profile } = props;
|
|
322
|
+
if ((!deviceTypes || deviceTypes.length === 0)) {
|
|
323
|
+
const classes = BleInterface.deviceClasses.map(c => c.Class);
|
|
324
|
+
DeviceClasses = this.getDevicesFromServices(classes, peripheral.advertisement.serviceUuids);
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
DeviceClasses = this.getDevicesFromServices(deviceTypes, peripheral.advertisement.serviceUuids);
|
|
328
|
+
}
|
|
329
|
+
if (profile && DeviceClasses && DeviceClasses.length > 0) {
|
|
330
|
+
DeviceClasses = DeviceClasses.filter(C => {
|
|
331
|
+
const device = new C({ peripheral });
|
|
332
|
+
if (device.getProfile() !== profile)
|
|
333
|
+
return false;
|
|
334
|
+
return true;
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
return DeviceClasses;
|
|
338
|
+
}
|
|
339
|
+
createDevice(DeviceClass, peripheral, characteristics) {
|
|
340
|
+
const C = DeviceClass;
|
|
341
|
+
const device = new C({ peripheral });
|
|
342
|
+
device.setInterface(this);
|
|
343
|
+
device.characteristics = characteristics;
|
|
344
|
+
return device;
|
|
345
|
+
}
|
|
346
|
+
connectDevice(requested, timeout = DEFAULT_SCAN_TIMEOUT + CONNECT_TIMEOUT) {
|
|
347
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
348
|
+
const { id, name, address } = requested;
|
|
349
|
+
const profile = requested instanceof ble_1.BleDeviceClass ?
|
|
350
|
+
(requested.getProfile && typeof (requested.getProfile) === 'function' ? requested.getProfile() : undefined) :
|
|
351
|
+
requested.profile;
|
|
267
352
|
this.logEvent({ message: 'connectDevice', id, name, address, profile, isbusy: this.scanState.isConnecting });
|
|
268
353
|
if (this.scanState.isConnecting) {
|
|
269
|
-
yield this.waitForConnectFinished(
|
|
354
|
+
yield this.waitForConnectFinished(CONNECT_TIMEOUT);
|
|
270
355
|
}
|
|
271
356
|
this.scanState.isConnecting = true;
|
|
357
|
+
const existing = this.devices.find(i => (!profile || i.device.getProfile() === profile) && (i.device.address === requested.address || i.device.id === requested.id || i.device.name === requested.name));
|
|
358
|
+
if (existing) {
|
|
359
|
+
const connected = yield existing.device.connect();
|
|
360
|
+
this.scanState.isConnecting = false;
|
|
361
|
+
return existing.device;
|
|
362
|
+
}
|
|
363
|
+
const peripheralInfo = this.peripheralCache.find(i => (i.address === requested.address || (i.periphal && i.peripheral.id === requested.id)));
|
|
364
|
+
if (peripheralInfo) {
|
|
365
|
+
if (!peripheralInfo.characteristic) {
|
|
366
|
+
yield this.getCharacteristics(peripheralInfo.periphal);
|
|
367
|
+
const DeviceClasses = this.getDeviceClasses(peripheralInfo.peripheral, { profile });
|
|
368
|
+
if (!DeviceClasses || DeviceClasses.length === 0)
|
|
369
|
+
return;
|
|
370
|
+
const devices = DeviceClasses.map(C => this.createDevice(C, peripheralInfo.periphal, peripheralInfo.characteristics));
|
|
371
|
+
if (devices && devices.length > 0) {
|
|
372
|
+
for (let i = 0; i < devices.length; i++) {
|
|
373
|
+
const idx = this.devices.push({ device: devices[i], isConnected: false }) - 1;
|
|
374
|
+
yield devices[i].connect();
|
|
375
|
+
this.devices[idx].isConnected = true;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
272
380
|
let devices = [];
|
|
273
381
|
let retry = false;
|
|
274
382
|
let retryCount = 0;
|
|
@@ -277,7 +385,7 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
277
385
|
this.logEvent({ message: 'retry connect device', id, name, address, profile, retryCount });
|
|
278
386
|
}
|
|
279
387
|
try {
|
|
280
|
-
devices = yield this.scan({ timeout,
|
|
388
|
+
devices = yield this.scan({ timeout: DEFAULT_SCAN_TIMEOUT, requested: requested });
|
|
281
389
|
if (devices.length === 0) {
|
|
282
390
|
retryCount++;
|
|
283
391
|
retry = retryCount < 5;
|
|
@@ -285,7 +393,7 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
285
393
|
}
|
|
286
394
|
catch (err) {
|
|
287
395
|
if (err.message === 'scanning already in progress') {
|
|
288
|
-
yield utils_1.sleep(1000);
|
|
396
|
+
yield (0, utils_1.sleep)(1000);
|
|
289
397
|
retryCount++;
|
|
290
398
|
retry = retryCount < 5;
|
|
291
399
|
continue;
|
|
@@ -330,42 +438,36 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
330
438
|
}, 100);
|
|
331
439
|
});
|
|
332
440
|
}
|
|
333
|
-
addPeripheralToCache(peripheral) {
|
|
334
|
-
try {
|
|
335
|
-
this.logEvent({ message: 'adding device to cache', device: { address: peripheral.address, name: peripheral.advertisement ? peripheral.advertisement.localName : '' } });
|
|
336
|
-
const existing = this.deviceCache.find(p => p.address === peripheral.address);
|
|
337
|
-
if (!existing)
|
|
338
|
-
this.deviceCache.push(peripheral);
|
|
339
|
-
else {
|
|
340
|
-
if (peripheral.advertisement && peripheral.advertisement.localName !== '' && existing.advertisement && existing.advertisement.localName === '')
|
|
341
|
-
existing.advertisement.localName = peripheral.advertisement.localName;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
catch (err) {
|
|
345
|
-
console.log('~~~ error', err);
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
441
|
scan(props) {
|
|
349
442
|
return __awaiter(this, void 0, void 0, function* () {
|
|
350
|
-
const { timeout =
|
|
351
|
-
|
|
352
|
-
|
|
443
|
+
const { timeout = DEFAULT_SCAN_TIMEOUT, deviceTypes = [], requested } = props;
|
|
444
|
+
let profile;
|
|
445
|
+
if (requested)
|
|
446
|
+
profile = requested instanceof ble_1.BleDeviceClass ?
|
|
447
|
+
(requested.getProfile && typeof (requested.getProfile) === 'function' ? requested.getProfile() : undefined) :
|
|
448
|
+
requested.profile;
|
|
449
|
+
const { id, address, name } = requested || {};
|
|
450
|
+
const scanForDevice = (requested !== null && requested !== undefined);
|
|
451
|
+
const services = (props.isBackgroundScan || !deviceTypes || deviceTypes.length === 0) ? DEFAULT_SERVICES : this.getServicesFromDeviceTypes(deviceTypes);
|
|
353
452
|
const bleBinding = this.getBinding();
|
|
354
453
|
if (!bleBinding)
|
|
355
454
|
return Promise.reject(new Error('no binding defined'));
|
|
356
455
|
if (!this.isConnected()) {
|
|
357
456
|
yield this.connect();
|
|
358
457
|
}
|
|
359
|
-
|
|
458
|
+
const peripheralsProcessed = [];
|
|
459
|
+
const devicesProcessed = [];
|
|
460
|
+
this.logEvent({ message: 'scan()', props, scanState: this.scanState,
|
|
461
|
+
peripheralCache: this.peripheralCache.map(i => ({ address: i.address, ts: i.ts, name: i.peripheral ? i.peripheral.advertisement.localName : '' })),
|
|
462
|
+
deviceCache: this.devices.map(i => ({ address: i.device.address, profile: i.device.getProfile(), isConnected: i.isConnected }))
|
|
463
|
+
});
|
|
360
464
|
if (!props.isBackgroundScan && this.scanState.isBackgroundScan) {
|
|
361
465
|
yield this.stopScan();
|
|
362
466
|
this.scanState.isBackgroundScan = false;
|
|
363
467
|
}
|
|
364
|
-
const detectedPeripherals = {};
|
|
365
468
|
let opStr;
|
|
366
469
|
if (scanForDevice) {
|
|
367
470
|
opStr = 'search device';
|
|
368
|
-
const { id, address, name } = device;
|
|
369
471
|
this.logEvent({ message: 'search device request', device: { id, address, name }, deviceTypes });
|
|
370
472
|
}
|
|
371
473
|
else {
|
|
@@ -374,6 +476,7 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
374
476
|
}
|
|
375
477
|
if (this.scanState.isScanning) {
|
|
376
478
|
try {
|
|
479
|
+
this.logEvent({ message: `${opStr}: waiting for previous scan to finish` });
|
|
377
480
|
yield this.waitForScanFinished(timeout);
|
|
378
481
|
}
|
|
379
482
|
catch (err) {
|
|
@@ -383,151 +486,102 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
383
486
|
}
|
|
384
487
|
return new Promise((resolve, reject) => {
|
|
385
488
|
this.scanState.isScanning = true;
|
|
386
|
-
if (
|
|
489
|
+
if (props.isBackgroundScan)
|
|
490
|
+
this.scanState.isBackgroundScan = true;
|
|
491
|
+
if (scanForDevice) {
|
|
387
492
|
if (this.devices && this.devices.length > 0) {
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
this.
|
|
391
|
-
const existing = this.devices.find(i => (i.device.address === device.address || i.device.name === device.name));
|
|
392
|
-
if (existing) {
|
|
393
|
-
const d = device;
|
|
394
|
-
const linkedDevice = existing.device;
|
|
395
|
-
d.peripheral = existing.device.peripheral;
|
|
396
|
-
if (d.setInterface && typeof (d.setInterface) === 'function')
|
|
397
|
-
d.setInterface(this);
|
|
398
|
-
setTimeout(() => {
|
|
399
|
-
let connectState = linkedDevice.getConnectState();
|
|
400
|
-
this.logEvent({ message: `${opStr}: device already registered`, device: device.name, address: device.address, connectState });
|
|
401
|
-
if (connectState.isConnecting) {
|
|
402
|
-
const waitStart = Date.now();
|
|
403
|
-
const waitTimeout = waitStart + timeout;
|
|
404
|
-
const waitIv = setInterval(() => {
|
|
405
|
-
try {
|
|
406
|
-
connectState = linkedDevice.getConnectState();
|
|
407
|
-
if (connectState.isConnecting && Date.now() > waitTimeout) {
|
|
408
|
-
clearInterval(waitIv);
|
|
409
|
-
this.scanState.isScanning = false;
|
|
410
|
-
return resolve([]);
|
|
411
|
-
}
|
|
412
|
-
if (!connectState.isConnecting) {
|
|
413
|
-
clearInterval(waitIv);
|
|
414
|
-
this.scanState.isScanning = false;
|
|
415
|
-
return resolve([device]);
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
catch (err) {
|
|
419
|
-
console.log('~~~ error', err);
|
|
420
|
-
}
|
|
421
|
-
}, 100);
|
|
422
|
-
}
|
|
423
|
-
else if (connectState.isConnected) {
|
|
424
|
-
this.scanState.isScanning = false;
|
|
425
|
-
resolve([device]);
|
|
426
|
-
}
|
|
427
|
-
else {
|
|
428
|
-
this.scanState.isScanning = false;
|
|
429
|
-
resolve([]);
|
|
430
|
-
}
|
|
431
|
-
}, 100);
|
|
432
|
-
}
|
|
493
|
+
const knownDevices = this.devices.map(i => ({ name: i.device.name, address: i.device.address, isConnected: i.isConnected, connectState: i.device.getConnectState() }));
|
|
494
|
+
this.logEvent({ message: `${opStr}: check if already registered`, device: { name, address }, knownDevices });
|
|
495
|
+
const existing = this.devices.find(i => (i.device.address === address || i.device.name === name || i.device.id === id));
|
|
433
496
|
}
|
|
434
497
|
}
|
|
435
|
-
const
|
|
498
|
+
const onTimeout = () => {
|
|
499
|
+
if (!this.scanState.isScanning || !this.scanState.timeout)
|
|
500
|
+
return;
|
|
501
|
+
this.scanState.timeout = null;
|
|
502
|
+
this.logEvent({ message: `${opStr} result: devices found`, requested: scanForDevice ? { name, address, profile } : undefined, devices: this.devices.map(i => i.device.name + (!i.device.name || i.device.name === '') ? `addr=${i.device.address}` : '') });
|
|
503
|
+
this.getBinding().removeAllListeners('discover');
|
|
504
|
+
this.logEvent({ message: `${opStr}: stop scanning`, requested: scanForDevice ? { name, address, profile } : undefined, });
|
|
505
|
+
bleBinding.stopScanning(() => {
|
|
506
|
+
this.scanState.isScanning = false;
|
|
507
|
+
if (scanForDevice) {
|
|
508
|
+
reject(new Error('device not found'));
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
resolve(this.devices.map(i => i.device));
|
|
512
|
+
});
|
|
513
|
+
};
|
|
514
|
+
const onPeripheralFound = (peripheral, fromCache = false) => __awaiter(this, void 0, void 0, function* () {
|
|
436
515
|
if (fromCache)
|
|
437
516
|
this.logEvent({ message: 'adding from Cache', peripheral: peripheral.address });
|
|
438
|
-
if (!peripheral || !peripheral.advertisement)
|
|
517
|
+
if (!peripheral || !peripheral.advertisement || !peripheral.advertisement.serviceUuids || peripheral.advertisement.serviceUuids.length === 0)
|
|
439
518
|
return;
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
519
|
+
const isPeripheralProcessed = peripheralsProcessed.find(p => p === peripheral.address) !== undefined;
|
|
520
|
+
if (isPeripheralProcessed)
|
|
521
|
+
return;
|
|
522
|
+
peripheralsProcessed.push(peripheral.address);
|
|
523
|
+
let chachedPeripheralInfo = this.peripheralCache.find(i => i.address === peripheral.address);
|
|
524
|
+
const str = fromCache ? 'added' : 'detected';
|
|
525
|
+
const characteristics = yield this.getCharacteristics(peripheral);
|
|
526
|
+
const DeviceClasses = this.getDeviceClasses(peripheral, { profile });
|
|
527
|
+
let cntFound = 0;
|
|
528
|
+
DeviceClasses.forEach(DeviceClass => {
|
|
529
|
+
if (!DeviceClass)
|
|
530
|
+
return;
|
|
531
|
+
if (scanForDevice && cntFound > 0)
|
|
532
|
+
return;
|
|
533
|
+
const d = this.createDevice(DeviceClass, peripheral, characteristics);
|
|
534
|
+
if (scanForDevice) {
|
|
535
|
+
if ((id && id !== '' && d.id === id) ||
|
|
536
|
+
(address && address !== '' && d.address === address) ||
|
|
537
|
+
(name && name !== '' && d.name === name))
|
|
538
|
+
cntFound++;
|
|
449
539
|
}
|
|
450
|
-
else
|
|
451
|
-
|
|
540
|
+
else
|
|
541
|
+
cntFound++;
|
|
542
|
+
const existing = devicesProcessed.find(device => device.id === d.id && device.getProfile() === d.getProfile());
|
|
543
|
+
if (!scanForDevice && cntFound > 0 && !existing) {
|
|
544
|
+
this.logEvent({ message: `${opStr}: device found`, device: d.name, address: d.address, services: d.services.join(',') });
|
|
545
|
+
this.devices.push({ device: d, isConnected: peripheral.state === 'connected' });
|
|
546
|
+
devicesProcessed.push(d);
|
|
547
|
+
this.emit('device', d);
|
|
548
|
+
return;
|
|
452
549
|
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
const C = DeviceClass;
|
|
460
|
-
const d = new C({ peripheral });
|
|
461
|
-
if (device && device.getProfile && device.getProfile() !== d.getProfile())
|
|
462
|
-
return;
|
|
463
|
-
d.setInterface(this);
|
|
464
|
-
if (scanForDevice) {
|
|
465
|
-
if ((device.id && device.id !== '' && d.id === device.id) ||
|
|
466
|
-
(device.address && device.address !== '' && d.address === device.address) ||
|
|
467
|
-
(device.name && device.name !== '' && d.name === device.name))
|
|
468
|
-
cntFound++;
|
|
469
|
-
}
|
|
470
|
-
else
|
|
471
|
-
cntFound++;
|
|
472
|
-
const existing = this.devices.find(i => i.device.id === d.id && i.device.getProfile() === d.getProfile());
|
|
473
|
-
if (!scanForDevice && cntFound > 0 && !existing) {
|
|
474
|
-
this.logEvent({ message: `${opStr}: device found`, device: d.name, address: d.address, services: d.services.join(',') });
|
|
475
|
-
this.devices.push({ device: d, isConnected: false });
|
|
476
|
-
this.emit('device', d);
|
|
477
|
-
}
|
|
478
|
-
if (scanForDevice && cntFound > 0) {
|
|
479
|
-
if (fromCache) {
|
|
480
|
-
resolve([d]);
|
|
481
|
-
return;
|
|
482
|
-
}
|
|
550
|
+
if (scanForDevice && cntFound > 0) {
|
|
551
|
+
this.logEvent({ message: `${opStr}: device found`, device: d.name, address: d.address, services: d.services.join(',') });
|
|
552
|
+
this.devices.push({ device: d, isConnected: peripheral.state === 'connected' });
|
|
553
|
+
devicesProcessed.push(d);
|
|
554
|
+
this.emit('device', d);
|
|
555
|
+
process.nextTick(() => {
|
|
483
556
|
if (this.scanState.timeout) {
|
|
484
557
|
clearTimeout(this.scanState.timeout);
|
|
485
558
|
this.scanState.timeout = null;
|
|
486
|
-
this.logEvent({ message: `${opStr}: stop scanning`, requested: scanForDevice ? { name: device.name, address: device.address } : undefined, });
|
|
487
|
-
bleBinding.stopScanning(() => {
|
|
488
|
-
this.getBinding().removeAllListeners('discover');
|
|
489
|
-
this.scanState.isScanning = false;
|
|
490
|
-
resolve([d]);
|
|
491
|
-
});
|
|
492
559
|
}
|
|
493
|
-
|
|
560
|
+
this.logEvent({ message: `${opStr}: stop scanning`, requested: scanForDevice ? { name, address, profile } : undefined, });
|
|
561
|
+
bleBinding.stopScanning(() => {
|
|
562
|
+
this.getBinding().removeAllListeners('discover');
|
|
563
|
+
this.scanState.isScanning = false;
|
|
494
564
|
resolve([d]);
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
this.deviceCache.forEach(peripheral => {
|
|
504
|
-
onPeripheralFound(peripheral, true);
|
|
565
|
+
});
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
});
|
|
569
|
+
});
|
|
570
|
+
this.logEvent({ message: `${opStr}: start scanning`, requested: scanForDevice ? { name, address, profile } : undefined, timeout });
|
|
571
|
+
this.peripheralCache.forEach(i => {
|
|
572
|
+
onPeripheralFound(i.peripheral, true);
|
|
505
573
|
});
|
|
506
574
|
bleBinding.startScanning([], true, (err) => {
|
|
507
575
|
if (err) {
|
|
508
|
-
this.logEvent({ message: `${opStr} result: error`, requested: scanForDevice ? { name
|
|
576
|
+
this.logEvent({ message: `${opStr} result: error`, requested: scanForDevice ? { name, address, profile } : undefined, error: err.message });
|
|
509
577
|
this.scanState.isScanning = false;
|
|
510
578
|
return reject(err);
|
|
511
579
|
}
|
|
512
580
|
bleBinding.on('discover', (p) => {
|
|
513
|
-
console.log('~~~ discovered:', p.address, p.advertisement ? p.advertisement.localName : '');
|
|
514
581
|
onPeripheralFound(p);
|
|
515
582
|
});
|
|
516
583
|
});
|
|
517
|
-
this.scanState.timeout = setTimeout(
|
|
518
|
-
this.scanState.timeout = null;
|
|
519
|
-
this.logEvent({ message: `${opStr} result: devices found`, requested: scanForDevice ? { name: device.name, address: device.address } : undefined, devices: this.devices.map(i => i.device.name + (!i.device.name || i.device.name === '') ? `addr=${i.device.address}` : '') });
|
|
520
|
-
this.getBinding().removeAllListeners('discover');
|
|
521
|
-
this.logEvent({ message: `${opStr}: stop scanning`, requested: scanForDevice ? { name: device.name, address: device.address } : undefined, });
|
|
522
|
-
bleBinding.stopScanning(() => {
|
|
523
|
-
this.scanState.isScanning = false;
|
|
524
|
-
if (scanForDevice) {
|
|
525
|
-
reject(new Error('device not found'));
|
|
526
|
-
return;
|
|
527
|
-
}
|
|
528
|
-
resolve(this.devices.map(i => i.device));
|
|
529
|
-
});
|
|
530
|
-
}, timeout);
|
|
584
|
+
this.scanState.timeout = setTimeout(onTimeout, timeout);
|
|
531
585
|
});
|
|
532
586
|
});
|
|
533
587
|
}
|
|
@@ -538,6 +592,9 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
538
592
|
if (!this.getBinding())
|
|
539
593
|
return Promise.reject(new Error('no binding defined'));
|
|
540
594
|
this.getBinding().removeAllListeners('discover');
|
|
595
|
+
const ongoing = this.peripheralCache.filter(i => i.state.isLoading);
|
|
596
|
+
if (ongoing)
|
|
597
|
+
ongoing.forEach(i => { i.isInterrupted = true; });
|
|
541
598
|
this.logEvent({ message: 'scan stop request' });
|
|
542
599
|
return new Promise(resolve => {
|
|
543
600
|
this.getBinding().stopScanning(() => {
|
package/lib/ble/ble.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
2
3
|
import EventEmitter from "events";
|
|
3
4
|
export declare type ConnectProps = {
|
|
4
5
|
timeout?: number;
|
|
@@ -13,6 +14,20 @@ export interface ConnectState {
|
|
|
13
14
|
isConnected: boolean;
|
|
14
15
|
isDisconnecting: boolean;
|
|
15
16
|
}
|
|
17
|
+
export declare type BleDeviceInfo = {
|
|
18
|
+
manufacturer?: string;
|
|
19
|
+
hwRevision?: string;
|
|
20
|
+
swRevision?: string;
|
|
21
|
+
fwRevision?: string;
|
|
22
|
+
model?: string;
|
|
23
|
+
serialNo?: string;
|
|
24
|
+
};
|
|
25
|
+
export interface BleDeviceDescription {
|
|
26
|
+
id?: string;
|
|
27
|
+
address: string;
|
|
28
|
+
name?: string;
|
|
29
|
+
profile: string;
|
|
30
|
+
}
|
|
16
31
|
export declare abstract class BleDeviceClass extends EventEmitter {
|
|
17
32
|
static services: string[];
|
|
18
33
|
id?: string;
|
|
@@ -26,6 +41,7 @@ export declare abstract class BleDeviceClass extends EventEmitter {
|
|
|
26
41
|
abstract getServiceUUids(): string[];
|
|
27
42
|
abstract connect(props?: ConnectProps): Promise<boolean>;
|
|
28
43
|
abstract disconnect(): Promise<boolean>;
|
|
44
|
+
abstract getDeviceInfo(): Promise<BleDeviceInfo>;
|
|
29
45
|
}
|
|
30
46
|
export interface BleBinding extends EventEmitter {
|
|
31
47
|
startScanning(serviceUUIDs?: string[], allowDuplicates?: boolean, callback?: (error?: Error) => void): void;
|
|
@@ -36,7 +52,7 @@ export interface BleBinding extends EventEmitter {
|
|
|
36
52
|
export declare type ScanProps = {
|
|
37
53
|
timeout?: number;
|
|
38
54
|
deviceTypes?: (typeof BleDeviceClass)[];
|
|
39
|
-
|
|
55
|
+
requested?: BleDeviceClass | BleDeviceDescription;
|
|
40
56
|
isBackgroundScan?: boolean;
|
|
41
57
|
};
|
|
42
58
|
export declare class BleBindingWrapper {
|
|
@@ -73,6 +89,9 @@ export interface BlePeripheral extends EventEmitter, BleDeviceIdentifier {
|
|
|
73
89
|
discoverSomeServicesAndCharacteristicsAsync(serviceUUIDs: string[], characteristicUUIDs: string[]): Promise<any>;
|
|
74
90
|
}
|
|
75
91
|
export interface BleCharacteristic extends EventEmitter {
|
|
92
|
+
subscribe(callback: (err: Error) => void): void;
|
|
93
|
+
read(callback: (err: Error, data: Buffer) => void): void;
|
|
94
|
+
write(data: Buffer, withoutResponse: boolean, callback?: (err: Error) => void): void;
|
|
76
95
|
}
|
|
77
96
|
export declare type BleDeviceProps = {
|
|
78
97
|
id?: string;
|
package/lib/ble/ble.js
CHANGED
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.uuid = exports.BleState = exports.BleInterfaceClass = exports.BleBindingWrapper = exports.BleDeviceClass = void 0;
|
|
6
7
|
const events_1 = __importDefault(require("events"));
|
|
7
8
|
class BleDeviceClass extends events_1.default {
|
|
8
9
|
constructor() {
|
|
@@ -59,7 +60,7 @@ var BleState;
|
|
|
59
60
|
BleState["POWERED_OFF"] = "poweredOff";
|
|
60
61
|
BleState["POWERED_ON"] = "poweredOn";
|
|
61
62
|
})(BleState = exports.BleState || (exports.BleState = {}));
|
|
62
|
-
|
|
63
|
+
const uuid = (s) => {
|
|
63
64
|
if (s) {
|
|
64
65
|
if (s.includes('-')) {
|
|
65
66
|
const parts = s.split('-');
|
|
@@ -69,3 +70,4 @@ exports.uuid = (s) => {
|
|
|
69
70
|
return s;
|
|
70
71
|
}
|
|
71
72
|
};
|
|
73
|
+
exports.uuid = uuid;
|
package/lib/ble/fm.d.ts
CHANGED
|
@@ -32,17 +32,27 @@ declare type IndoorBikeData = {
|
|
|
32
32
|
remainingTime?: number;
|
|
33
33
|
raw?: string;
|
|
34
34
|
};
|
|
35
|
+
declare type IndoorBikeFeatures = {
|
|
36
|
+
fitnessMachine: number;
|
|
37
|
+
targetSettings: number;
|
|
38
|
+
};
|
|
35
39
|
export default class BleFitnessMachineDevice extends BleDevice {
|
|
36
40
|
static services: string[];
|
|
37
41
|
static characteristics: string[];
|
|
38
42
|
data: IndoorBikeData;
|
|
43
|
+
features: IndoorBikeFeatures;
|
|
39
44
|
constructor(props?: any);
|
|
45
|
+
init(): Promise<boolean>;
|
|
40
46
|
getProfile(): string;
|
|
41
47
|
getServiceUUids(): string[];
|
|
48
|
+
isBike(): boolean;
|
|
49
|
+
isPower(): boolean;
|
|
50
|
+
isHrm(): boolean;
|
|
51
|
+
parseHrm(_data: Uint8Array): IndoorBikeData;
|
|
42
52
|
parseIndoorBikeData(_data: Uint8Array): IndoorBikeData;
|
|
53
|
+
getFitnessMachineFeatures(): Promise<IndoorBikeFeatures>;
|
|
43
54
|
onData(characteristic: string, data: Buffer): void;
|
|
44
55
|
write(characteristic: any, data: any): Promise<boolean>;
|
|
45
|
-
read(characteristic: any): Promise<Buffer>;
|
|
46
56
|
reset(): void;
|
|
47
57
|
}
|
|
48
58
|
export declare class FmAdapter extends DeviceAdapter {
|