incyclist-devices 1.4.44 → 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 +132 -25
- package/lib/ble/ble-interface.d.ts +22 -5
- package/lib/ble/ble-interface.js +212 -189
- 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 +106 -8
- package/lib/ble/hrm.d.ts +0 -1
- package/lib/ble/hrm.js +1 -4
- package/lib/ble/incyclist-protocol.js +23 -7
- 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
|
@@ -15,19 +15,17 @@ const ble_1 = require("./ble");
|
|
|
15
15
|
const CONNECT_TIMEOUT = 5000;
|
|
16
16
|
const DEFAULT_SCAN_TIMEOUT = 20000;
|
|
17
17
|
const BACKGROUND_SCAN_TIMEOUT = 30000;
|
|
18
|
+
const DEFAULT_SERVICES = ['1818', '180d', '1826'];
|
|
18
19
|
class BleInterface extends ble_1.BleInterfaceClass {
|
|
19
20
|
constructor(props = {}) {
|
|
20
21
|
super(props);
|
|
21
22
|
this.scanState = { isScanning: false, isConnecting: false, timeout: undefined, isBackgroundScan: false };
|
|
22
23
|
this.connectState = { isConnecting: false, isConnected: false, isInitSuccess: false };
|
|
23
24
|
this.devices = [];
|
|
24
|
-
this.deviceCache = [];
|
|
25
25
|
this.peripheralCache = [];
|
|
26
26
|
if (props.logger)
|
|
27
27
|
this.logger = props.logger;
|
|
28
|
-
|
|
29
|
-
this.logger = new gd_eventlog_1.EventLogger('BLE');
|
|
30
|
-
}
|
|
28
|
+
this.logger = new gd_eventlog_1.EventLogger('BLE');
|
|
31
29
|
}
|
|
32
30
|
static getInstance(props = {}) {
|
|
33
31
|
if (!BleInterface._instance) {
|
|
@@ -213,7 +211,7 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
213
211
|
});
|
|
214
212
|
};
|
|
215
213
|
if (typeof services === 'string') {
|
|
216
|
-
return get(deviceTypes, (s) => s === ble_1.uuid(services));
|
|
214
|
+
return get(deviceTypes, (s) => s === (0, ble_1.uuid)(services));
|
|
217
215
|
}
|
|
218
216
|
if (Array.isArray(services)) {
|
|
219
217
|
return get(deviceTypes, s => services.map(ble_1.uuid).includes(s));
|
|
@@ -221,19 +219,24 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
221
219
|
return [];
|
|
222
220
|
}
|
|
223
221
|
getServicesFromDeviceTypes(deviceTypes) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
deviceTypes.forEach(DeviceType => {
|
|
229
|
-
if (DeviceType.services) {
|
|
230
|
-
const dtServices = DeviceType.services;
|
|
231
|
-
dtServices.forEach(s => {
|
|
232
|
-
if (!services.find(s2 => s2 === s))
|
|
233
|
-
services.push(s);
|
|
234
|
-
});
|
|
222
|
+
let services = [];
|
|
223
|
+
try {
|
|
224
|
+
if (!deviceTypes || !Array.isArray(deviceTypes) || deviceTypes.length === 0) {
|
|
225
|
+
return [];
|
|
235
226
|
}
|
|
236
|
-
|
|
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
|
+
}
|
|
237
240
|
return services;
|
|
238
241
|
}
|
|
239
242
|
getServicesFromDevice(device) {
|
|
@@ -263,15 +266,117 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
263
266
|
}, 100);
|
|
264
267
|
});
|
|
265
268
|
}
|
|
269
|
+
addPeripheralToCache(peripheral, props = {}) {
|
|
270
|
+
this.peripheralCache.push(Object.assign({ address: peripheral.address, ts: Date.now(), peripheral }, props));
|
|
271
|
+
}
|
|
272
|
+
getCharacteristics(peripheral) {
|
|
273
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
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
|
+
}
|
|
266
346
|
connectDevice(requested, timeout = DEFAULT_SCAN_TIMEOUT + CONNECT_TIMEOUT) {
|
|
267
347
|
return __awaiter(this, void 0, void 0, function* () {
|
|
268
|
-
const { id, name, address
|
|
269
|
-
const profile =
|
|
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;
|
|
270
352
|
this.logEvent({ message: 'connectDevice', id, name, address, profile, isbusy: this.scanState.isConnecting });
|
|
271
353
|
if (this.scanState.isConnecting) {
|
|
272
354
|
yield this.waitForConnectFinished(CONNECT_TIMEOUT);
|
|
273
355
|
}
|
|
274
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
|
+
}
|
|
275
380
|
let devices = [];
|
|
276
381
|
let retry = false;
|
|
277
382
|
let retryCount = 0;
|
|
@@ -280,7 +385,7 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
280
385
|
this.logEvent({ message: 'retry connect device', id, name, address, profile, retryCount });
|
|
281
386
|
}
|
|
282
387
|
try {
|
|
283
|
-
devices = yield this.scan({ timeout: DEFAULT_SCAN_TIMEOUT,
|
|
388
|
+
devices = yield this.scan({ timeout: DEFAULT_SCAN_TIMEOUT, requested: requested });
|
|
284
389
|
if (devices.length === 0) {
|
|
285
390
|
retryCount++;
|
|
286
391
|
retry = retryCount < 5;
|
|
@@ -288,7 +393,7 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
288
393
|
}
|
|
289
394
|
catch (err) {
|
|
290
395
|
if (err.message === 'scanning already in progress') {
|
|
291
|
-
yield utils_1.sleep(1000);
|
|
396
|
+
yield (0, utils_1.sleep)(1000);
|
|
292
397
|
retryCount++;
|
|
293
398
|
retry = retryCount < 5;
|
|
294
399
|
continue;
|
|
@@ -333,26 +438,17 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
333
438
|
}, 100);
|
|
334
439
|
});
|
|
335
440
|
}
|
|
336
|
-
addPeripheralToCache(peripheral) {
|
|
337
|
-
try {
|
|
338
|
-
this.logEvent({ message: 'adding device to cache', device: { address: peripheral.address, name: peripheral.advertisement ? peripheral.advertisement.localName : '' } });
|
|
339
|
-
const existing = this.deviceCache.find(p => p.address === peripheral.address);
|
|
340
|
-
if (!existing)
|
|
341
|
-
this.deviceCache.push(peripheral);
|
|
342
|
-
else {
|
|
343
|
-
if (peripheral.advertisement && peripheral.advertisement.localName !== '' && existing.advertisement && existing.advertisement.localName === '')
|
|
344
|
-
existing.advertisement.localName = peripheral.advertisement.localName;
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
catch (err) {
|
|
348
|
-
console.log('~~~ error', err);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
441
|
scan(props) {
|
|
352
442
|
return __awaiter(this, void 0, void 0, function* () {
|
|
353
|
-
const { timeout = DEFAULT_SCAN_TIMEOUT, deviceTypes = [],
|
|
354
|
-
|
|
355
|
-
|
|
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);
|
|
356
452
|
const bleBinding = this.getBinding();
|
|
357
453
|
if (!bleBinding)
|
|
358
454
|
return Promise.reject(new Error('no binding defined'));
|
|
@@ -360,7 +456,11 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
360
456
|
yield this.connect();
|
|
361
457
|
}
|
|
362
458
|
const peripheralsProcessed = [];
|
|
363
|
-
|
|
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
|
+
});
|
|
364
464
|
if (!props.isBackgroundScan && this.scanState.isBackgroundScan) {
|
|
365
465
|
yield this.stopScan();
|
|
366
466
|
this.scanState.isBackgroundScan = false;
|
|
@@ -368,7 +468,6 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
368
468
|
let opStr;
|
|
369
469
|
if (scanForDevice) {
|
|
370
470
|
opStr = 'search device';
|
|
371
|
-
const { id, address, name } = device;
|
|
372
471
|
this.logEvent({ message: 'search device request', device: { id, address, name }, deviceTypes });
|
|
373
472
|
}
|
|
374
473
|
else {
|
|
@@ -389,158 +488,92 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
389
488
|
this.scanState.isScanning = true;
|
|
390
489
|
if (props.isBackgroundScan)
|
|
391
490
|
this.scanState.isBackgroundScan = true;
|
|
392
|
-
if (scanForDevice
|
|
491
|
+
if (scanForDevice) {
|
|
393
492
|
if (this.devices && this.devices.length > 0) {
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
this.
|
|
397
|
-
const existing = this.devices.find(i => (i.device.address === device.address || i.device.name === device.name));
|
|
398
|
-
if (existing) {
|
|
399
|
-
const d = device;
|
|
400
|
-
const linkedDevice = existing.device;
|
|
401
|
-
d.peripheral = existing.device.peripheral;
|
|
402
|
-
if (d.setInterface && typeof (d.setInterface) === 'function')
|
|
403
|
-
d.setInterface(this);
|
|
404
|
-
setTimeout(() => {
|
|
405
|
-
let connectState = linkedDevice.getConnectState();
|
|
406
|
-
this.logEvent({ message: `${opStr}: device already registered`, device: device.name, address: device.address, connectState });
|
|
407
|
-
if (connectState.isConnecting) {
|
|
408
|
-
const waitStart = Date.now();
|
|
409
|
-
const waitTimeout = waitStart + timeout;
|
|
410
|
-
const waitIv = setInterval(() => {
|
|
411
|
-
try {
|
|
412
|
-
connectState = linkedDevice.getConnectState();
|
|
413
|
-
if (connectState.isConnecting && Date.now() > waitTimeout) {
|
|
414
|
-
clearInterval(waitIv);
|
|
415
|
-
this.scanState.isScanning = false;
|
|
416
|
-
return resolve([]);
|
|
417
|
-
}
|
|
418
|
-
if (!connectState.isConnecting) {
|
|
419
|
-
clearInterval(waitIv);
|
|
420
|
-
this.scanState.isScanning = false;
|
|
421
|
-
return resolve([device]);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
catch (err) {
|
|
425
|
-
console.log('~~~ error', err);
|
|
426
|
-
}
|
|
427
|
-
}, 100);
|
|
428
|
-
}
|
|
429
|
-
else if (connectState.isConnected) {
|
|
430
|
-
this.scanState.isScanning = false;
|
|
431
|
-
resolve([device]);
|
|
432
|
-
}
|
|
433
|
-
else {
|
|
434
|
-
this.scanState.isScanning = false;
|
|
435
|
-
resolve([]);
|
|
436
|
-
}
|
|
437
|
-
}, 100);
|
|
438
|
-
}
|
|
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));
|
|
439
496
|
}
|
|
440
497
|
}
|
|
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
|
+
};
|
|
441
514
|
const onPeripheralFound = (peripheral, fromCache = false) => __awaiter(this, void 0, void 0, function* () {
|
|
442
515
|
if (fromCache)
|
|
443
516
|
this.logEvent({ message: 'adding from Cache', peripheral: peripheral.address });
|
|
444
|
-
if (!peripheral || !peripheral.advertisement)
|
|
517
|
+
if (!peripheral || !peripheral.advertisement || !peripheral.advertisement.serviceUuids || peripheral.advertisement.serviceUuids.length === 0)
|
|
445
518
|
return;
|
|
446
|
-
|
|
447
|
-
if (
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
}
|
|
454
|
-
let
|
|
455
|
-
|
|
456
|
-
if (
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
if (peripheral.disconnect && typeof (peripheral.disconnect) === 'function')
|
|
467
|
-
peripheral.disconnect(() => { });
|
|
468
|
-
existingPeripheral.characteristics = res.characteristics;
|
|
469
|
-
characteristics = res.characteristics;
|
|
470
|
-
}
|
|
471
|
-
catch (err) {
|
|
472
|
-
console.log(err);
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
else {
|
|
476
|
-
characteristics = existingPeripheral.characteristics;
|
|
477
|
-
this.logEvent({ message: 'characteristic info (+):', info: characteristics.map(c => `${peripheral.address} ${c.uuid} ${c.properties}`) });
|
|
478
|
-
}
|
|
479
|
-
if (!fromCache)
|
|
480
|
-
this.addPeripheralToCache(peripheral);
|
|
481
|
-
let DeviceClasses;
|
|
482
|
-
if (scanForDevice && (!deviceTypes || deviceTypes.length === 0)) {
|
|
483
|
-
const classes = BleInterface.deviceClasses.map(c => c.Class);
|
|
484
|
-
DeviceClasses = this.getDevicesFromServices(classes, peripheral.advertisement.serviceUuids);
|
|
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++;
|
|
485
539
|
}
|
|
486
|
-
else
|
|
487
|
-
|
|
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;
|
|
488
549
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
const C = DeviceClass;
|
|
496
|
-
const d = new C({ peripheral });
|
|
497
|
-
if (device && device.getProfile && device.getProfile() !== d.getProfile())
|
|
498
|
-
return;
|
|
499
|
-
d.setInterface(this);
|
|
500
|
-
d.characteristics = characteristics;
|
|
501
|
-
if (scanForDevice) {
|
|
502
|
-
if ((device.id && device.id !== '' && d.id === device.id) ||
|
|
503
|
-
(device.address && device.address !== '' && d.address === device.address) ||
|
|
504
|
-
(device.name && device.name !== '' && d.name === device.name))
|
|
505
|
-
cntFound++;
|
|
506
|
-
}
|
|
507
|
-
else
|
|
508
|
-
cntFound++;
|
|
509
|
-
const existing = this.devices.find(i => i.device.id === d.id && i.device.getProfile() === d.getProfile());
|
|
510
|
-
if (!scanForDevice && cntFound > 0 && !existing) {
|
|
511
|
-
this.logEvent({ message: `${opStr}: device found`, device: d.name, address: d.address, services: d.services.join(',') });
|
|
512
|
-
this.devices.push({ device: d, isConnected: false });
|
|
513
|
-
this.emit('device', d);
|
|
514
|
-
}
|
|
515
|
-
if (scanForDevice && cntFound > 0) {
|
|
516
|
-
if (fromCache) {
|
|
517
|
-
resolve([d]);
|
|
518
|
-
return;
|
|
519
|
-
}
|
|
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(() => {
|
|
520
556
|
if (this.scanState.timeout) {
|
|
521
557
|
clearTimeout(this.scanState.timeout);
|
|
522
558
|
this.scanState.timeout = null;
|
|
523
|
-
this.logEvent({ message: `${opStr}: stop scanning`, requested: scanForDevice ? { name: device.name, address: device.address } : undefined, });
|
|
524
|
-
bleBinding.stopScanning(() => {
|
|
525
|
-
this.getBinding().removeAllListeners('discover');
|
|
526
|
-
this.scanState.isScanning = false;
|
|
527
|
-
resolve([d]);
|
|
528
|
-
});
|
|
529
559
|
}
|
|
530
|
-
|
|
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;
|
|
531
564
|
resolve([d]);
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
}
|
|
565
|
+
});
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
});
|
|
536
569
|
});
|
|
537
|
-
this.logEvent({ message: `${opStr}: start scanning`, requested: scanForDevice ? { name
|
|
538
|
-
this.
|
|
539
|
-
onPeripheralFound(peripheral, true);
|
|
570
|
+
this.logEvent({ message: `${opStr}: start scanning`, requested: scanForDevice ? { name, address, profile } : undefined, timeout });
|
|
571
|
+
this.peripheralCache.forEach(i => {
|
|
572
|
+
onPeripheralFound(i.peripheral, true);
|
|
540
573
|
});
|
|
541
574
|
bleBinding.startScanning([], true, (err) => {
|
|
542
575
|
if (err) {
|
|
543
|
-
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 });
|
|
544
577
|
this.scanState.isScanning = false;
|
|
545
578
|
return reject(err);
|
|
546
579
|
}
|
|
@@ -548,20 +581,7 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
548
581
|
onPeripheralFound(p);
|
|
549
582
|
});
|
|
550
583
|
});
|
|
551
|
-
this.scanState.timeout = setTimeout(
|
|
552
|
-
this.scanState.timeout = null;
|
|
553
|
-
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}` : '') });
|
|
554
|
-
this.getBinding().removeAllListeners('discover');
|
|
555
|
-
this.logEvent({ message: `${opStr}: stop scanning`, requested: scanForDevice ? { name: device.name, address: device.address } : undefined, });
|
|
556
|
-
bleBinding.stopScanning(() => {
|
|
557
|
-
this.scanState.isScanning = false;
|
|
558
|
-
if (scanForDevice) {
|
|
559
|
-
reject(new Error('device not found'));
|
|
560
|
-
return;
|
|
561
|
-
}
|
|
562
|
-
resolve(this.devices.map(i => i.device));
|
|
563
|
-
});
|
|
564
|
-
}, timeout);
|
|
584
|
+
this.scanState.timeout = setTimeout(onTimeout, timeout);
|
|
565
585
|
});
|
|
566
586
|
});
|
|
567
587
|
}
|
|
@@ -572,6 +592,9 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
572
592
|
if (!this.getBinding())
|
|
573
593
|
return Promise.reject(new Error('no binding defined'));
|
|
574
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; });
|
|
575
598
|
this.logEvent({ message: 'scan stop request' });
|
|
576
599
|
return new Promise(resolve => {
|
|
577
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 {
|