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.
Files changed (47) hide show
  1. package/lib/CyclingMode.js +1 -0
  2. package/lib/Device.js +1 -0
  3. package/lib/DeviceProtocol.js +1 -0
  4. package/lib/DeviceSupport.js +25 -8
  5. package/lib/ant/AntAdapter.js +1 -0
  6. package/lib/ant/AntScanner.js +24 -7
  7. package/lib/ant/antfe/AntFEAdapter.js +7 -7
  8. package/lib/ant/anthrm/AntHrmAdapter.js +1 -1
  9. package/lib/ant/antpwr/pwr-adapter.js +1 -1
  10. package/lib/ant/utils.js +3 -1
  11. package/lib/ble/ble-device.d.ts +10 -3
  12. package/lib/ble/ble-device.js +144 -32
  13. package/lib/ble/ble-interface.d.ts +22 -4
  14. package/lib/ble/ble-interface.js +224 -167
  15. package/lib/ble/ble.d.ts +20 -1
  16. package/lib/ble/ble.js +3 -1
  17. package/lib/ble/fm.d.ts +11 -1
  18. package/lib/ble/fm.js +109 -11
  19. package/lib/ble/hrm.d.ts +0 -1
  20. package/lib/ble/hrm.js +1 -4
  21. package/lib/ble/incyclist-protocol.js +24 -8
  22. package/lib/ble/pwr.d.ts +1 -1
  23. package/lib/ble/pwr.js +14 -4
  24. package/lib/calculations.js +1 -0
  25. package/lib/daum/DaumAdapter.js +29 -13
  26. package/lib/daum/SmartTrainerCyclingMode.js +1 -0
  27. package/lib/daum/classic/DaumClassicAdapter.js +1 -1
  28. package/lib/daum/classic/DaumClassicProtocol.js +23 -7
  29. package/lib/daum/classic/bike.js +26 -26
  30. package/lib/daum/classic/utils.js +1 -0
  31. package/lib/daum/constants.js +1 -0
  32. package/lib/daum/premium/DaumPremiumAdapter.js +1 -1
  33. package/lib/daum/premium/DaumPremiumProtocol.js +23 -7
  34. package/lib/daum/premium/bike.js +18 -17
  35. package/lib/daum/premium/utils.js +1 -0
  36. package/lib/kettler/comms.d.ts +1 -0
  37. package/lib/kettler/comms.js +2 -1
  38. package/lib/kettler/ergo-racer/adapter.js +25 -9
  39. package/lib/kettler/ergo-racer/protocol.d.ts +1 -1
  40. package/lib/kettler/ergo-racer/protocol.js +23 -7
  41. package/lib/modes/power-meter.js +1 -0
  42. package/lib/simulator/Simulator.d.ts +1 -1
  43. package/lib/simulator/Simulator.js +24 -7
  44. package/lib/types/route.js +1 -0
  45. package/lib/types/user.js +1 -0
  46. package/lib/utils.js +3 -1
  47. package/package.json +1 -1
@@ -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.deviceCache = [];
25
+ this.peripheralCache = [];
23
26
  if (props.logger)
24
27
  this.logger = props.logger;
25
- else if (props.log) {
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: 5000, isBackgroundScan: true })
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
- if (!deviceTypes || !Array.isArray(deviceTypes) || deviceTypes.length === 0) {
222
- return [];
223
- }
224
- const services = [];
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
- connectDevice(requested, timeout = CONNECT_TIMEOUT) {
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
- const { id, name, address, getProfile } = requested;
266
- const profile = getProfile && typeof (getProfile) === 'function' ? getProfile() : undefined;
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(10000);
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, device: requested });
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 = 5000, deviceTypes = [], device } = props;
351
- const scanForDevice = (device !== null && device !== undefined);
352
- const services = this.getServicesFromDeviceTypes(deviceTypes);
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
- this.logEvent({ message: 'scan()', props, scanState: this.scanState, cache: this.deviceCache.map(p => ({ name: p.advertisement ? p.advertisement.localName : '', address: p.address })) });
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 (scanForDevice && device instanceof ble_1.BleDeviceClass) {
489
+ if (props.isBackgroundScan)
490
+ this.scanState.isBackgroundScan = true;
491
+ if (scanForDevice) {
387
492
  if (this.devices && this.devices.length > 0) {
388
- const connectedDevices = this.devices.map(i => ({ name: i.device.name, address: i.device.address, isConnected: i.isConnected, connectState: i.device.getConnectState() }));
389
- const { name, address } = device;
390
- this.logEvent({ message: `${opStr}: check if already registered`, device: { name, address }, connectedDevices });
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 onPeripheralFound = (peripheral, fromCache = false) => {
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
- if (!detectedPeripherals[peripheral.id]) {
441
- if (process.env.BLE_DEBUG)
442
- console.log('discovered', peripheral);
443
- detectedPeripherals[peripheral.id] = peripheral;
444
- this.addPeripheralToCache(peripheral);
445
- let DeviceClasses;
446
- if (scanForDevice && (!deviceTypes || deviceTypes.length === 0)) {
447
- const classes = BleInterface.deviceClasses.map(c => c.Class);
448
- 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++;
449
539
  }
450
- else {
451
- DeviceClasses = this.getDevicesFromServices(deviceTypes, peripheral.advertisement.serviceUuids);
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
- DeviceClasses.forEach(DeviceClass => {
454
- let cntFound = 0;
455
- if (!DeviceClass)
456
- return;
457
- if (scanForDevice && cntFound > 0)
458
- return;
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
- else {
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
- else {
500
- }
501
- };
502
- this.logEvent({ message: `${opStr}: start scanning`, requested: scanForDevice ? { name: device.name, address: device.address } : undefined, timeout });
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: device.name, address: device.address } : undefined, error: err.message });
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
- device?: BleDeviceClass;
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
- exports.uuid = (s) => {
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 {