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.
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 +132 -25
  13. package/lib/ble/ble-interface.d.ts +22 -5
  14. package/lib/ble/ble-interface.js +212 -189
  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 +106 -8
  19. package/lib/ble/hrm.d.ts +0 -1
  20. package/lib/ble/hrm.js +1 -4
  21. package/lib/ble/incyclist-protocol.js +23 -7
  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
@@ -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
- else if (props.log) {
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
- if (!deviceTypes || !Array.isArray(deviceTypes) || deviceTypes.length === 0) {
225
- return [];
226
- }
227
- const services = [];
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, getProfile } = requested;
269
- const profile = getProfile && typeof (getProfile) === 'function' ? getProfile() : undefined;
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, device: requested });
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 = [], device } = props;
354
- const scanForDevice = (device !== null && device !== undefined);
355
- 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);
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
- this.logEvent({ message: 'scan()', props, scanState: this.scanState, cache: this.deviceCache.map(p => ({ name: p.advertisement ? p.advertisement.localName : '', address: p.address })) });
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 && device instanceof ble_1.BleDeviceClass) {
491
+ if (scanForDevice) {
393
492
  if (this.devices && this.devices.length > 0) {
394
- const connectedDevices = this.devices.map(i => ({ name: i.device.name, address: i.device.address, isConnected: i.isConnected, connectState: i.device.getConnectState() }));
395
- const { name, address } = device;
396
- this.logEvent({ message: `${opStr}: check if already registered`, device: { name, address }, connectedDevices });
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
- let existingPeripheral = this.peripheralCache.find(i => i.address === peripheral.address);
447
- if (existingPeripheral && Date.now() - existingPeripheral.ts > 600000) {
448
- existingPeripheral.ts = Date.now();
449
- }
450
- if (!existingPeripheral) {
451
- this.peripheralCache.push({ address: peripheral.address, ts: Date.now(), peripheral });
452
- existingPeripheral = this.peripheralCache.find(i => i.address === peripheral.address);
453
- }
454
- let shouldAddDevice = peripheralsProcessed.find(p => p === peripheral.address) === undefined;
455
- if (shouldAddDevice) {
456
- if (process.env.BLE_DEBUG)
457
- console.log('discovered', peripheral.id, peripheral.address, peripheral.advertisement.localName);
458
- peripheralsProcessed.push(peripheral.address);
459
- let characteristics;
460
- if (!existingPeripheral.characteristics) {
461
- try {
462
- if (existingPeripheral.peripheral && existingPeripheral.peripheral.state !== 'connected')
463
- yield peripheral.connectAsync();
464
- const res = yield peripheral.discoverSomeServicesAndCharacteristicsAsync([], []);
465
- this.logEvent({ message: 'characteristic info (+):', info: res.characteristics.map(c => `${peripheral.address} ${c.uuid} ${c.properties}`) });
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
- 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;
488
549
  }
489
- DeviceClasses.forEach(DeviceClass => {
490
- let cntFound = 0;
491
- if (!DeviceClass)
492
- return;
493
- if (scanForDevice && cntFound > 0)
494
- return;
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
- 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;
531
564
  resolve([d]);
532
- }
533
- }
534
- });
535
- }
565
+ });
566
+ });
567
+ }
568
+ });
536
569
  });
537
- this.logEvent({ message: `${opStr}: start scanning`, requested: scanForDevice ? { name: device.name, address: device.address } : undefined, timeout });
538
- this.deviceCache.forEach(peripheral => {
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: 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 });
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
- 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 {