incyclist-devices 1.4.93 → 1.4.95

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/Device.js CHANGED
@@ -9,7 +9,7 @@ class IncyclistDevice {
9
9
  this.detected = false;
10
10
  this.selected = false;
11
11
  this.onDataFn = undefined;
12
- this.settings = settings;
12
+ this.settings = settings || {};
13
13
  }
14
14
  isBike() { throw new Error('not implemented'); }
15
15
  isPower() { throw new Error('not implemented'); }
@@ -36,6 +36,7 @@ export default class AntFEAdapter extends AntAdapter {
36
36
  getDisplayName(): string;
37
37
  getCyclingMode(): CyclingMode;
38
38
  getDefaultCyclingMode(): CyclingMode;
39
+ getSupportedCyclingModes(): any[];
39
40
  onAttached(): void;
40
41
  getLogData(data: any, excludeList: any): any;
41
42
  onDeviceData(deviceData: AntPwrData): void;
@@ -58,6 +58,9 @@ class AntFEAdapter extends AntAdapter_1.default {
58
58
  getDefaultCyclingMode() {
59
59
  return new power_meter_1.default(this);
60
60
  }
61
+ getSupportedCyclingModes() {
62
+ return [power_meter_1.default];
63
+ }
61
64
  onAttached() {
62
65
  this.logger.logEvent({ message: 'Device connected' });
63
66
  this.connected = true;
@@ -35,6 +35,7 @@ export declare abstract class BleDevice extends BleDeviceClass {
35
35
  workerIv: NodeJS.Timeout;
36
36
  prevMessages: MessageLog[];
37
37
  constructor(props?: BleDeviceConstructProps);
38
+ getServices(): string[];
38
39
  logEvent(event: any): void;
39
40
  setLogger(logger: EventLogger): void;
40
41
  setInterface(ble: BleInterfaceClass): void;
@@ -51,7 +52,7 @@ export declare abstract class BleDevice extends BleDeviceClass {
51
52
  disconnect(): Promise<boolean>;
52
53
  abstract getProfile(): string;
53
54
  checkForDuplicate(characteristic: string, data: Buffer): boolean;
54
- onData(characteristic: string, data: Buffer): void;
55
+ onData(characteristic: string, _data: Buffer): boolean;
55
56
  timeoutCheck(): void;
56
57
  startWorker(): void;
57
58
  stopWorker(): void;
@@ -47,13 +47,14 @@ class BleDevice extends ble_1.BleDeviceClass {
47
47
  this.logger = new gd_eventlog_1.EventLogger('BleDevice');
48
48
  }
49
49
  }
50
+ getServices() {
51
+ return this.services;
52
+ }
50
53
  logEvent(event) {
51
54
  if (this.logger) {
52
55
  this.logger.logEvent(event);
53
56
  }
54
- if (process.env.BLE_DEBUG) {
55
- console.log('~~~BLE:', event);
56
- }
57
+ console.log('~~~BLE:', event);
57
58
  }
58
59
  setLogger(logger) {
59
60
  this.logger = logger;
@@ -80,8 +81,12 @@ class BleDevice extends ble_1.BleDeviceClass {
80
81
  this.state = "disconnected";
81
82
  if (!this.connectState.isDisconnecting) {
82
83
  this.peripheral.state = 'disconnected';
84
+ this.connectState.isConnecting = false;
83
85
  this.connectState.isConnected = false;
84
- this.connect();
86
+ this.cleanupListeners();
87
+ this.subscribedCharacteristics = [];
88
+ this.ble.onDisconnect(this.peripheral);
89
+ this.connect({ reconnect: true });
85
90
  }
86
91
  this.emit('disconnected');
87
92
  }
@@ -128,6 +133,7 @@ class BleDevice extends ble_1.BleDeviceClass {
128
133
  this.connectState.isConnecting = true;
129
134
  try {
130
135
  const connector = this.ble.getConnector(peripheral);
136
+ connector.on('disconnect', () => { this.onDisconnect(); });
131
137
  yield connector.connect();
132
138
  yield connector.initialize();
133
139
  yield this.subscribeAll(connector);
@@ -156,9 +162,10 @@ class BleDevice extends ble_1.BleDeviceClass {
156
162
  }
157
163
  connect(props) {
158
164
  return __awaiter(this, void 0, void 0, function* () {
165
+ const { reconnect } = props || {};
159
166
  try {
160
- this.logEvent({ message: 'connect', address: this.peripheral ? this.peripheral.address : this.address, state: this.connectState });
161
- if (this.connectState.isConnecting) {
167
+ this.logEvent({ message: reconnect ? 'reconnect' : 'connect', address: this.peripheral ? this.peripheral.address : this.address, state: this.connectState });
168
+ if (!reconnect && this.connectState.isConnecting) {
162
169
  yield this.waitForConnectFinished(CONNECT_WAIT_TIMEOUT);
163
170
  }
164
171
  if (this.connectState.isConnected) {
@@ -238,6 +245,8 @@ class BleDevice extends ble_1.BleDeviceClass {
238
245
  }
239
246
  if (!this.connectState.isConnecting && !this.connectState.isConnected) {
240
247
  this.connectState.isDisconnecting = false;
248
+ this.connectState.isConnecting = false;
249
+ this.connectState.isConnected = false;
241
250
  this.logEvent({ message: 'disconnect result: success', device: { id, name, address } });
242
251
  return true;
243
252
  }
@@ -245,13 +254,17 @@ class BleDevice extends ble_1.BleDeviceClass {
245
254
  this.cleanupListeners();
246
255
  setTimeout(() => { this.connectState.isDisconnecting = false; }, 1000);
247
256
  this.logEvent({ message: 'disconnect result: unclear - connect ongoing', device: { id, name, address } });
257
+ this.connectState.isConnecting = false;
258
+ this.connectState.isConnected = false;
248
259
  return true;
249
260
  }
250
261
  if (this.connectState.isConnected) {
251
262
  this.ble.removeConnectedDevice(this);
252
263
  this.cleanupListeners();
253
- setTimeout(() => { this.connectState.isDisconnecting = false; }, 1000);
254
264
  this.logEvent({ message: 'disconnect result: success', device: { id, name, address } });
265
+ this.connectState.isDisconnecting = false;
266
+ this.connectState.isConnecting = false;
267
+ this.connectState.isConnected = false;
255
268
  return true;
256
269
  }
257
270
  });
@@ -273,10 +286,11 @@ class BleDevice extends ble_1.BleDeviceClass {
273
286
  }
274
287
  return false;
275
288
  }
276
- onData(characteristic, data) {
289
+ onData(characteristic, _data) {
290
+ const data = Buffer.from(_data);
277
291
  const isDuplicate = this.checkForDuplicate(characteristic, data);
278
292
  if (isDuplicate) {
279
- return;
293
+ return false;
280
294
  }
281
295
  this.logEvent({ message: 'got data', characteristic, data: data.toString('hex'), writeQueue: this.writeQueue.length });
282
296
  if (this.writeQueue.length > 0) {
@@ -286,8 +300,10 @@ class BleDevice extends ble_1.BleDeviceClass {
286
300
  this.writeQueue.splice(writeIdx, 1);
287
301
  if (writeItem.resolve)
288
302
  writeItem.resolve(data);
303
+ return false;
289
304
  }
290
305
  }
306
+ return true;
291
307
  }
292
308
  timeoutCheck() {
293
309
  const now = Date.now();
@@ -321,6 +337,8 @@ class BleDevice extends ble_1.BleDeviceClass {
321
337
  write(characteristicUuid, data, props) {
322
338
  return __awaiter(this, void 0, void 0, function* () {
323
339
  try {
340
+ if (!this.isConnected())
341
+ throw new Error('not connected');
324
342
  const { withoutResponse, timeout } = props || {};
325
343
  const connector = this.ble.getConnector(this.peripheral);
326
344
  const isAlreadySubscribed = connector.isSubscribed(characteristicUuid);
@@ -380,6 +398,8 @@ class BleDevice extends ble_1.BleDeviceClass {
380
398
  }
381
399
  read(characteristicUuid) {
382
400
  return new Promise((resolve, reject) => {
401
+ if (!this.isConnected())
402
+ return reject(new Error('not connected'));
383
403
  const characteristic = this.characteristics.find(c => c.uuid === characteristicUuid || (0, ble_1.uuid)(c.uuid) === characteristicUuid);
384
404
  if (!characteristic) {
385
405
  reject(new Error('Characteristic not found'));
@@ -67,6 +67,7 @@ export default class BleInterface extends BleInterfaceClass {
67
67
  getServicesFromDevice(device: BleDeviceClass): string[];
68
68
  waitForConnectFinished(timeout: any): Promise<unknown>;
69
69
  addPeripheralToCache(peripheral: any, props?: {}): void;
70
+ onDisconnect(peripheral: any): void;
70
71
  getConnector(peripheral: BlePeripheral): any;
71
72
  findPeripheral(peripheral: BlePeripheral | {
72
73
  id?: string;
@@ -56,7 +56,9 @@ class BleInterface extends ble_1.BleInterfaceClass {
56
56
  if (this.logger) {
57
57
  this.logger.logEvent(event);
58
58
  }
59
- console.log('~~BLE:', event);
59
+ if (process.env.BLE_DEBUG) {
60
+ console.log('~~BLE:', event);
61
+ }
60
62
  }
61
63
  onStateChange(state) {
62
64
  if (state !== ble_1.BleState.POWERED_ON) {
@@ -290,6 +292,11 @@ class BleInterface extends ble_1.BleInterfaceClass {
290
292
  const connector = info && info.connector ? info.connector : new ble_peripheral_1.default(this, peripheral);
291
293
  this.peripheralCache.push(Object.assign({ address: peripheral.address, ts: Date.now(), peripheral, connector }, props));
292
294
  }
295
+ onDisconnect(peripheral) {
296
+ const idx = this.peripheralCache.findIndex(i => i.address === peripheral.address);
297
+ if (idx !== -1)
298
+ this.peripheralCache.splice(idx, 1);
299
+ }
293
300
  getConnector(peripheral) {
294
301
  const info = this.peripheralCache.find(i => i.address === peripheral.address);
295
302
  if (!info) {
@@ -411,7 +418,8 @@ class BleInterface extends ble_1.BleInterfaceClass {
411
418
  if (devices && devices.length > 0) {
412
419
  for (let i = 0; i < devices.length; i++) {
413
420
  const idx = this.devices.push({ device: devices[i], isConnected: false }) - 1;
414
- yield devices[i].connect();
421
+ if (!devices[i].isConnected())
422
+ yield devices[i].connect();
415
423
  this.devices[idx].isConnected = true;
416
424
  }
417
425
  }
@@ -558,14 +566,15 @@ class BleInterface extends ble_1.BleInterfaceClass {
558
566
  });
559
567
  };
560
568
  const onPeripheralFound = (peripheral, fromCache = false) => __awaiter(this, void 0, void 0, function* () {
561
- if (fromCache)
569
+ if (!peripheral || !peripheral.advertisement || !peripheral.advertisement.localName || !peripheral.advertisement.serviceUuids || peripheral.advertisement.serviceUuids.length === 0)
570
+ return;
571
+ if (fromCache) {
562
572
  this.logEvent({ message: 'adding from Cache', peripheral: peripheral.address });
573
+ }
563
574
  else {
564
575
  const { id, name, address, advertisement = {} } = peripheral;
565
576
  this.logEvent({ message: 'BLE scan: found device', peripheral: { id, name, address, services: advertisement.serviceUuids } });
566
577
  }
567
- if (!peripheral || !peripheral.advertisement || !peripheral.advertisement.serviceUuids || peripheral.advertisement.serviceUuids.length === 0)
568
- return;
569
578
  if (peripheral.address === undefined || peripheral.address === '')
570
579
  peripheral.address = peripheral.id;
571
580
  const isPeripheralProcessed = peripheralsProcessed.find(p => p === peripheral.address) !== undefined;
@@ -578,67 +587,70 @@ class BleInterface extends ble_1.BleInterfaceClass {
578
587
  const services = connectedServices ? connectedServices.map(cs => cs.uuid) : undefined;
579
588
  const connectedPeripheral = connector.getPeripheral();
580
589
  const { id, name, address, advertisement = {} } = connectedPeripheral;
581
- const DeviceClasses = this.getDeviceClasses(connectedPeripheral, { profile, services });
590
+ const DeviceClasses = this.getDeviceClasses(connectedPeripheral, { profile, services }) || [];
582
591
  this.logEvent({ message: 'BLE scan: device connected', peripheral: { id, name, address, services: advertisement.serviceUuids }, services, classes: DeviceClasses.map(c => c.prototype.constructor.name) });
583
592
  let cntFound = 0;
584
- DeviceClasses.forEach((DeviceClass) => __awaiter(this, void 0, void 0, function* () {
585
- if (!DeviceClass)
586
- return;
587
- if (scanForDevice && cntFound > 0)
588
- return;
589
- const d = this.createDevice(DeviceClass, peripheral, characteristics);
590
- if (!d) {
591
- this.logEvent({ message: `${opStr}: could not create device `, DeviceClass });
592
- return;
593
- }
594
- try {
595
- this.logEvent({ message: `${opStr}: connecting `, device: d.name, profile: d.getProfile(), address: d.address });
596
- yield d.connect();
597
- }
598
- catch (err) {
599
- this.logEvent({ message: 'error', fn: 'onPeripheralFound()', error: err.message || err, stack: err.stack });
600
- }
601
- if (scanForDevice) {
602
- if ((id && id !== '' && d.id === id) ||
603
- (address && address !== '' && d.address === address) ||
604
- (name && name !== '' && d.name === name))
605
- cntFound++;
606
- }
607
- else
593
+ const DeviceClass = DeviceClasses.sort((a, b) => (a.detectionPriority || 0 - b.detectionPriority || 0))[0];
594
+ if (!DeviceClass)
595
+ return;
596
+ if (scanForDevice && cntFound > 0)
597
+ return;
598
+ const d = this.createDevice(DeviceClass, peripheral, characteristics);
599
+ if (!d) {
600
+ this.logEvent({ message: `${opStr}: could not create device `, DeviceClass });
601
+ return;
602
+ }
603
+ try {
604
+ this.logEvent({ message: `${opStr}: connecting `, device: d.name, profile: d.getProfile(), address: d.address });
605
+ yield d.connect();
606
+ }
607
+ catch (err) {
608
+ this.logEvent({ message: 'error', fn: 'onPeripheralFound()', error: err.message || err, stack: err.stack });
609
+ }
610
+ if (scanForDevice) {
611
+ if ((id && id !== '' && d.id === id) ||
612
+ (address && address !== '' && d.address === address) ||
613
+ (name && name !== '' && d.name === name))
608
614
  cntFound++;
609
- const existing = devicesProcessed.find(device => device.id === d.id && device.getProfile() === d.getProfile());
610
- if (!scanForDevice && cntFound > 0 && !existing) {
611
- this.logEvent({ message: `${opStr}: device found`, device: d.name, profile: d.getProfile(), address: d.address, services: d.services.join(',') });
612
- this.addDeviceToCache(d, peripheral.state === 'connected');
613
- devicesProcessed.push(d);
614
- this.emit('device', d);
615
- return;
616
- }
617
- if (scanForDevice && cntFound > 0) {
618
- this.logEvent({ message: `${opStr}: device found`, device: d.name, profile: d.getProfile(), address: d.address, services: d.services.join(',') });
619
- this.addDeviceToCache(d, peripheral.state === 'connected');
620
- devicesProcessed.push(d);
621
- this.emit('device', d);
622
- process.nextTick(() => {
623
- if (this.scanState.timeout) {
624
- clearTimeout(this.scanState.timeout);
625
- this.scanState.timeout = null;
626
- }
627
- this.logEvent({ message: `${opStr}: stop scanning`, requested: scanForDevice ? { name, address, profile } : undefined, });
628
- bleBinding.stopScanning(() => {
629
- this.getBinding().removeAllListeners('discover');
630
- this.scanState.isScanning = false;
631
- resolve([d]);
632
- });
615
+ }
616
+ else
617
+ cntFound++;
618
+ const existing = devicesProcessed.find(device => device.id === d.id && device.getProfile() === d.getProfile());
619
+ if (!scanForDevice && cntFound > 0 && !existing) {
620
+ this.logEvent({ message: `${opStr}: device found`, device: d.name, profile: d.getProfile(), address: d.address, services: d.services.join(',') });
621
+ this.addDeviceToCache(d, peripheral.state === 'connected');
622
+ devicesProcessed.push(d);
623
+ this.emit('device', d);
624
+ return;
625
+ }
626
+ if (scanForDevice && cntFound > 0) {
627
+ this.logEvent({ message: `${opStr}: device found`, device: d.name, profile: d.getProfile(), address: d.address, services: d.services.join(',') });
628
+ this.addDeviceToCache(d, peripheral.state === 'connected');
629
+ devicesProcessed.push(d);
630
+ this.emit('device', d);
631
+ process.nextTick(() => {
632
+ if (this.scanState.timeout) {
633
+ clearTimeout(this.scanState.timeout);
634
+ this.scanState.timeout = null;
635
+ }
636
+ this.logEvent({ message: `${opStr}: stop scanning`, requested: scanForDevice ? { name, address, profile } : undefined, });
637
+ bleBinding.stopScanning(() => {
638
+ this.getBinding().removeAllListeners('discover');
639
+ this.scanState.isScanning = false;
640
+ resolve([d]);
633
641
  });
634
- }
635
- }));
642
+ });
643
+ }
636
644
  });
637
645
  this.logEvent({ message: `${opStr}: start scanning`, requested: scanForDevice ? { name, address, profile } : undefined, timeout });
638
- this.peripheralCache.forEach(i => {
639
- onPeripheralFound(i.peripheral, true);
640
- });
641
- bleBinding.startScanning([], true, (err) => {
646
+ let services = [];
647
+ if (scanForDevice) {
648
+ if (props.requested instanceof ble_1.BleDeviceClass) {
649
+ const device = props.requested;
650
+ services = (device.getServices()) || [];
651
+ }
652
+ }
653
+ bleBinding.startScanning(services, false, (err) => {
642
654
  if (err) {
643
655
  this.logEvent({ message: `${opStr} result: error`, requested: scanForDevice ? { name, address, profile } : undefined, error: err.message });
644
656
  this.scanState.isScanning = false;
@@ -24,7 +24,7 @@ export default class BlePeripheralConnector {
24
24
  initialize(enforce?: boolean): Promise<void>;
25
25
  isSubscribed(characteristicUuid: string): boolean;
26
26
  subscribeAll(callback: (characteristicUuid: string, data: any) => void): Promise<string[]>;
27
- subscribe(characteristicUuid: string): Promise<boolean>;
27
+ subscribe(characteristicUuid: string, timeout?: number): Promise<boolean>;
28
28
  onData(characteristicUuid: string, data: any): void;
29
29
  on(characteristicUuid: string, callback: (characteristicUuid: string, data: any) => void): void;
30
30
  off(characteristicUuid: string, callback: (characteristicUuid: string, data: any) => void): void;
@@ -22,7 +22,7 @@ class BlePeripheralConnector {
22
22
  this.emitter = new events_1.default();
23
23
  if (!this.peripheral || !this.ble)
24
24
  throw new Error('Illegal Arguments');
25
- this.state = { isConnected: false, isConnecting: false, isInitialized: false, isInitializing: false, isSubscribing: false };
25
+ this.state = { subscribed: [], isConnected: false, isConnecting: false, isInitialized: false, isInitializing: false, isSubscribing: false };
26
26
  this.services = undefined;
27
27
  this.characteristics = undefined;
28
28
  this.logger = new gd_eventlog_1.EventLogger('BLE');
@@ -31,7 +31,9 @@ class BlePeripheralConnector {
31
31
  if (this.logger) {
32
32
  this.logger.logEvent(event);
33
33
  }
34
- console.log('~~~BLE:', event);
34
+ if (process.env.BLE_DEBUG) {
35
+ console.log('~~~BLE:', event);
36
+ }
35
37
  }
36
38
  connect() {
37
39
  return __awaiter(this, void 0, void 0, function* () {
@@ -40,9 +42,10 @@ class BlePeripheralConnector {
40
42
  this.logEvent({ message: 'connect', peripheral: this.peripheral.address, state: this.state });
41
43
  this.state.isConnecting = true;
42
44
  try {
45
+ this.peripheral.on('connect', () => { console.log('~~~ peripheral connected', this.peripheral); });
46
+ this.peripheral.on('disconnect', () => { this.onDisconnect(); });
43
47
  if (!this.state.isConnected || (this.peripheral && this.peripheral.state !== 'connected')) {
44
48
  yield this.peripheral.connectAsync();
45
- this.peripheral.once('disconnect', this.onDisconnect.bind(this));
46
49
  }
47
50
  this.state.isConnected = this.peripheral.state === 'connected';
48
51
  }
@@ -58,9 +61,12 @@ class BlePeripheralConnector {
58
61
  });
59
62
  }
60
63
  onDisconnect() {
64
+ this.peripheral.removeAllListeners('connect');
65
+ this.peripheral.removeAllListeners('disconnect');
61
66
  this.logEvent({ message: 'onDisconnected', peripheral: this.peripheral.address, state: this.state });
62
67
  this.state.isConnected = false;
63
- this.reconnect();
68
+ this.state.isConnecting = false;
69
+ this.emitter.emit('disconnect');
64
70
  }
65
71
  initialize(enforce = false) {
66
72
  return __awaiter(this, void 0, void 0, function* () {
@@ -108,7 +114,7 @@ class BlePeripheralConnector {
108
114
  this.logEvent({ message: 'subscribe', peripheral: this.peripheral.address, characteristic: c.uuid, uuid: (0, ble_1.uuid)(c.uuid) });
109
115
  if (this.state.subscribed.find(uuid => uuid === c.uuid) === undefined) {
110
116
  try {
111
- yield this.subscribe(c.uuid);
117
+ yield this.subscribe(c.uuid, 3000);
112
118
  subscribed.push(c.uuid);
113
119
  }
114
120
  catch (err) {
@@ -126,8 +132,8 @@ class BlePeripheralConnector {
126
132
  return subscribed;
127
133
  });
128
134
  }
129
- subscribe(characteristicUuid) {
130
- this.logEvent({ message: 'subscribe', characteristic: characteristicUuid, characteristics: this.characteristics.map(c => ({ characteristic: c.uuid, uuid: (0, ble_1.uuid)(c.uuid) })) });
135
+ subscribe(characteristicUuid, timeout) {
136
+ this.logEvent({ message: 'subscribe attempt', characteristic: characteristicUuid, characteristics: this.characteristics.map(c => ({ characteristic: c.uuid, uuid: (0, ble_1.uuid)(c.uuid) })) });
131
137
  return new Promise((resolve, reject) => {
132
138
  try {
133
139
  const characteristic = this.characteristics.find(c => (0, ble_1.uuid)(c.uuid) === (0, ble_1.uuid)(characteristicUuid) || (0, ble_1.uuid)(c.uuid) === (0, ble_1.uuid)(characteristicUuid));
@@ -135,17 +141,21 @@ class BlePeripheralConnector {
135
141
  reject(new Error('Characteristic not found'));
136
142
  return;
137
143
  }
138
- this.logEvent({ message: 'subscribe', peripheral: this.peripheral.address, characteristic: characteristic.uuid, uuid: (0, ble_1.uuid)(characteristic.uuid) });
144
+ this.logEvent({ message: 'subscribe', peripheral: this.peripheral.address, characteristic: characteristic.uuid });
139
145
  characteristic.removeAllListeners('data');
140
146
  characteristic.on('data', (data, _isNotification) => {
141
147
  this.onData((0, ble_1.uuid)(characteristicUuid), data);
142
148
  });
143
- const to = setTimeout(() => {
144
- this.logEvent({ message: 'subscribe result', characteristic: characteristicUuid, error: 'timeout' });
145
- reject(new Error('timeout'));
146
- }, 3000);
149
+ let to;
150
+ if (timeout) {
151
+ to = setTimeout(() => {
152
+ this.logEvent({ message: 'subscribe result', characteristic: characteristicUuid, error: 'timeout' });
153
+ reject(new Error('timeout'));
154
+ }, timeout);
155
+ }
147
156
  characteristic.subscribe((err) => {
148
- clearTimeout(to);
157
+ if (to)
158
+ clearTimeout(to);
149
159
  this.logEvent({ message: 'subscribe result', characteristic: characteristicUuid, error: err });
150
160
  if (err)
151
161
  reject(err);
package/lib/ble/ble.d.ts CHANGED
@@ -3,6 +3,7 @@ import EventEmitter from "events";
3
3
  import BlePeripheralConnector from "./ble-peripheral";
4
4
  export declare type ConnectProps = {
5
5
  timeout?: number;
6
+ reconnect?: boolean;
6
7
  };
7
8
  export interface BleDeviceIdentifier {
8
9
  id?: string;
@@ -42,6 +43,7 @@ export declare abstract class BleDeviceClass extends EventEmitter {
42
43
  abstract connect(props?: ConnectProps): Promise<boolean>;
43
44
  abstract disconnect(): Promise<boolean>;
44
45
  abstract getDeviceInfo(): Promise<BleDeviceInfo>;
46
+ abstract getServices(): string[];
45
47
  setCharacteristicUUIDs(uuids: string[]): void;
46
48
  }
47
49
  export interface BleWriteProps {
@@ -78,6 +80,7 @@ export declare abstract class BleInterfaceClass extends EventEmitter {
78
80
  abstract scan(props: ScanProps): Promise<BleDeviceClass[]>;
79
81
  abstract stopScan(): Promise<boolean>;
80
82
  abstract disconnect(): Promise<boolean>;
83
+ abstract onDisconnect(peripheral: BlePeripheral): void;
81
84
  abstract isScanning(): boolean;
82
85
  abstract addConnectedDevice(device: BleDeviceClass): void;
83
86
  abstract removeConnectedDevice(device: BleDeviceClass): void;
package/lib/ble/fm.d.ts CHANGED
@@ -8,6 +8,7 @@ import { DeviceProtocol } from '../DeviceProtocol';
8
8
  import { EventLogger } from 'gd-eventlog';
9
9
  import CyclingMode from '../CyclingMode';
10
10
  import { IncyclistBikeData } from '../CyclingMode';
11
+ import BlePeripheralConnector from './ble-peripheral';
11
12
  declare type PowerData = {
12
13
  instantaneousPower?: number;
13
14
  balance?: number;
@@ -25,7 +26,9 @@ export declare type IndoorBikeData = {
25
26
  resistanceLevel?: number;
26
27
  instantaneousPower?: number;
27
28
  averagePower?: number;
28
- expendedEnergy?: number;
29
+ totalEnergy?: number;
30
+ energyPerHour?: number;
31
+ energyPerMinute?: number;
29
32
  heartrate?: number;
30
33
  metabolicEquivalent?: number;
31
34
  time?: number;
@@ -42,6 +45,7 @@ declare type IndoorBikeFeatures = {
42
45
  export default class BleFitnessMachineDevice extends BleDevice {
43
46
  static services: string[];
44
47
  static characteristics: string[];
48
+ static detectionPriority: number;
45
49
  data: IndoorBikeData;
46
50
  features: IndoorBikeFeatures;
47
51
  hasControl: boolean;
@@ -54,6 +58,7 @@ export default class BleFitnessMachineDevice extends BleDevice {
54
58
  constructor(props?: any);
55
59
  isMatching(characteristics: string[]): boolean;
56
60
  subscribeWriteResponse(cuuid: string): Promise<void>;
61
+ subscribeAll(conn?: BlePeripheralConnector): Promise<void>;
57
62
  init(): Promise<boolean>;
58
63
  onDisconnect(): void;
59
64
  getProfile(): string;
@@ -71,7 +76,7 @@ export default class BleFitnessMachineDevice extends BleDevice {
71
76
  parseIndoorBikeData(_data: Uint8Array): IndoorBikeData;
72
77
  parseFitnessMachineStatus(_data: Uint8Array): IndoorBikeData;
73
78
  getFitnessMachineFeatures(): Promise<IndoorBikeFeatures>;
74
- onData(characteristic: string, data: Buffer): void;
79
+ onData(characteristic: string, data: Buffer): boolean;
75
80
  writeFtmsMessage(requestedOpCode: any, data: any, props?: BleWriteProps): Promise<number>;
76
81
  requestControl(): Promise<boolean>;
77
82
  setTargetPower(power: number): Promise<boolean>;
package/lib/ble/fm.js CHANGED
@@ -93,6 +93,7 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
93
93
  this.windSpeed = 0;
94
94
  this.wheelSize = 2100;
95
95
  this.data = {};
96
+ this.services = BleFitnessMachineDevice.services;
96
97
  }
97
98
  isMatching(characteristics) {
98
99
  if (!characteristics)
@@ -100,7 +101,6 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
100
101
  const hasStatus = characteristics.find(c => c === consts_1.FTMS_STATUS) !== undefined;
101
102
  const hasCP = characteristics.find(c => c === consts_1.FTMS_CP) !== undefined;
102
103
  const hasIndoorBike = characteristics.find(c => c === consts_1.INDOOR_BIKE_DATA) !== undefined;
103
- const hasTacx = characteristics.find(c => c === consts_1.TACX_FE_C_RX) !== undefined && characteristics.find(c => c === consts_1.TACX_FE_C_TX) !== undefined;
104
104
  return hasStatus && hasCP && hasIndoorBike;
105
105
  }
106
106
  subscribeWriteResponse(cuuid) {
@@ -125,13 +125,44 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
125
125
  }
126
126
  });
127
127
  }
128
+ subscribeAll(conn) {
129
+ return new Promise(resolve => {
130
+ const characteristics = [consts_1.INDOOR_BIKE_DATA, consts_1.FTMS_STATUS, consts_1.FTMS_CP];
131
+ const timeout = Date.now() + 5500;
132
+ const iv = setInterval(() => {
133
+ const subscriptionStatus = characteristics.map(c => this.subscribedCharacteristics.find(s => s === c) !== undefined);
134
+ const done = subscriptionStatus.filter(s => s === true).length === characteristics.length;
135
+ if (done || Date.now() > timeout) {
136
+ clearInterval(iv);
137
+ resolve();
138
+ }
139
+ }, 100);
140
+ try {
141
+ const connector = conn || this.ble.getConnector(this.peripheral);
142
+ for (let i = 0; i < characteristics.length; i++) {
143
+ const c = characteristics[i];
144
+ const isAlreadySubscribed = connector.isSubscribed(c);
145
+ if (!isAlreadySubscribed) {
146
+ connector.removeAllListeners(c);
147
+ connector.on(c, (uuid, data) => {
148
+ this.onData(uuid, data);
149
+ });
150
+ connector.subscribe(c);
151
+ this.subscribedCharacteristics.push(c);
152
+ }
153
+ }
154
+ }
155
+ catch (err) {
156
+ this.logEvent({ message: 'Error', fn: 'subscribeAll()', error: err.message, stack: err.stack });
157
+ }
158
+ });
159
+ }
128
160
  init() {
129
161
  const _super = Object.create(null, {
130
162
  initDevice: { get: () => super.initDevice }
131
163
  });
132
164
  return __awaiter(this, void 0, void 0, function* () {
133
165
  try {
134
- yield this.subscribeWriteResponse(consts_1.FTMS_CP);
135
166
  yield _super.initDevice.call(this);
136
167
  yield this.getFitnessMachineFeatures();
137
168
  this.logEvent({ message: 'device info', deviceInfo: this.deviceInfo, features: this.features });
@@ -153,8 +184,8 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
153
184
  return BleFitnessMachineDevice.services;
154
185
  }
155
186
  isBike() {
156
- return this.features !== undefined &&
157
- (this.features.targetSettings & TargetSettingFeatureFlag.IndoorBikeSimulationParametersSupported) !== 0;
187
+ return this.features === undefined ||
188
+ ((this.features.targetSettings & TargetSettingFeatureFlag.IndoorBikeSimulationParametersSupported) !== 0);
158
189
  }
159
190
  isPower() {
160
191
  if (this.hasService('1818'))
@@ -180,6 +211,7 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
180
211
  }
181
212
  }
182
213
  catch (err) {
214
+ this.logEvent({ message: 'error', fn: 'parseHrm()', error: err.message | err, stack: err.stack });
183
215
  }
184
216
  return Object.assign(Object.assign({}, this.data), { raw: `2a37:${data.toString('hex')}` });
185
217
  }
@@ -191,99 +223,113 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
191
223
  getWindSpeed() { return this.windSpeed; }
192
224
  parseIndoorBikeData(_data) {
193
225
  const data = Buffer.from(_data);
194
- const flags = data.readUInt16LE(0);
195
- let offset = 2;
196
- if ((flags & IndoorBikeDataFlag.MoreData) === 0) {
197
- this.data.speed = data.readUInt16LE(offset) / 100;
198
- offset += 2;
199
- }
200
- if (flags & IndoorBikeDataFlag.AverageSpeedPresent) {
201
- this.data.averageSpeed = data.readUInt16LE(offset) / 100;
202
- offset += 2;
203
- }
204
- if (flags & IndoorBikeDataFlag.InstantaneousCadence) {
205
- this.data.cadence = data.readUInt16LE(offset) / 2;
206
- offset += 2;
207
- }
208
- if (flags & IndoorBikeDataFlag.AverageCadencePresent) {
209
- this.data.averageCadence = data.readUInt16LE(offset) / 2;
210
- offset += 2;
211
- }
212
- if (flags & IndoorBikeDataFlag.TotalDistancePresent) {
213
- const dvLow = data.readUInt8(offset);
214
- offset += 1;
215
- const dvHigh = data.readUInt16LE(offset);
216
- offset += 2;
217
- this.data.totalDistance = (dvHigh << 8) + dvLow;
218
- }
219
- if (flags & IndoorBikeDataFlag.ResistanceLevelPresent) {
220
- this.data.resistanceLevel = data.readInt16LE(offset);
221
- offset += 2;
222
- }
223
- if (flags & IndoorBikeDataFlag.InstantaneousPowerPresent) {
224
- this.data.instantaneousPower = data.readInt16LE(offset);
225
- offset += 2;
226
- }
227
- if (flags & IndoorBikeDataFlag.AveragePowerPresent) {
228
- this.data.averagePower = data.readInt16LE(offset);
229
- offset += 2;
230
- }
231
- if (flags & IndoorBikeDataFlag.ExpendedEnergyPresent) {
232
- this.data.expendedEnergy = data.readUInt16LE(offset);
233
- offset += 2;
234
- }
235
- if (flags & IndoorBikeDataFlag.HeartRatePresent) {
236
- this.data.heartrate = data.readUInt16LE(offset);
237
- offset += 2;
238
- }
239
- if (flags & IndoorBikeDataFlag.MetabolicEquivalentPresent) {
240
- this.data.metabolicEquivalent = data.readUInt16LE(offset) / 10;
241
- offset += 2;
242
- }
243
- if (flags & IndoorBikeDataFlag.ElapsedTimePresent) {
244
- this.data.time = data.readUInt16LE(offset);
245
- offset += 2;
226
+ try {
227
+ const flags = data.readUInt16LE(0);
228
+ let offset = 2;
229
+ if ((flags & IndoorBikeDataFlag.MoreData) === 0) {
230
+ this.data.speed = data.readUInt16LE(offset) / 100;
231
+ offset += 2;
232
+ }
233
+ if (flags & IndoorBikeDataFlag.AverageSpeedPresent) {
234
+ this.data.averageSpeed = data.readUInt16LE(offset) / 100;
235
+ offset += 2;
236
+ }
237
+ if (flags & IndoorBikeDataFlag.InstantaneousCadence) {
238
+ this.data.cadence = data.readUInt16LE(offset) / 2;
239
+ offset += 2;
240
+ }
241
+ if (flags & IndoorBikeDataFlag.AverageCadencePresent) {
242
+ this.data.averageCadence = data.readUInt16LE(offset) / 2;
243
+ offset += 2;
244
+ }
245
+ if (flags & IndoorBikeDataFlag.TotalDistancePresent) {
246
+ const dvLow = data.readUInt8(offset);
247
+ offset += 1;
248
+ const dvHigh = data.readUInt16LE(offset);
249
+ offset += 2;
250
+ this.data.totalDistance = (dvHigh << 8) + dvLow;
251
+ }
252
+ if (flags & IndoorBikeDataFlag.ResistanceLevelPresent) {
253
+ this.data.resistanceLevel = data.readInt8(offset);
254
+ offset += 1;
255
+ }
256
+ if (flags & IndoorBikeDataFlag.InstantaneousPowerPresent) {
257
+ this.data.instantaneousPower = data.readInt16LE(offset);
258
+ offset += 2;
259
+ }
260
+ if (flags & IndoorBikeDataFlag.AveragePowerPresent) {
261
+ this.data.averagePower = data.readInt16LE(offset);
262
+ offset += 2;
263
+ }
264
+ if (flags & IndoorBikeDataFlag.ExpendedEnergyPresent) {
265
+ this.data.totalEnergy = data.readUInt16LE(offset);
266
+ offset += 2;
267
+ this.data.energyPerHour = data.readUInt16LE(offset);
268
+ offset += 2;
269
+ this.data.energyPerMinute = data.readUInt8(offset);
270
+ offset += 1;
271
+ }
272
+ if (flags & IndoorBikeDataFlag.HeartRatePresent) {
273
+ this.data.heartrate = data.readUInt8(offset);
274
+ offset += 1;
275
+ }
276
+ if (flags & IndoorBikeDataFlag.MetabolicEquivalentPresent) {
277
+ this.data.metabolicEquivalent = data.readUInt8(offset) / 10;
278
+ offset += 2;
279
+ }
280
+ if (flags & IndoorBikeDataFlag.ElapsedTimePresent) {
281
+ this.data.time = data.readUInt16LE(offset);
282
+ offset += 2;
283
+ }
284
+ if (flags & IndoorBikeDataFlag.RemainingTimePresent) {
285
+ this.data.remainingTime = data.readUInt16LE(offset);
286
+ offset += 2;
287
+ }
246
288
  }
247
- if (flags & IndoorBikeDataFlag.RemainingTimePresent) {
248
- this.data.remainingTime = data.readUInt16LE(offset);
249
- offset += 2;
289
+ catch (err) {
290
+ this.logEvent({ message: 'error', fn: 'parseIndoorBikeData()', error: err.message | err, stack: err.stack });
250
291
  }
251
292
  return Object.assign(Object.assign({}, this.data), { raw: `2ad2:${data.toString('hex')}` });
252
293
  }
253
294
  parseFitnessMachineStatus(_data) {
254
295
  const data = Buffer.from(_data);
255
- const OpCode = data.readUInt8(0);
256
- switch (OpCode) {
257
- case 8:
258
- this.data.targetPower = data.readInt16LE(1);
259
- break;
260
- case 6:
261
- this.data.targetInclination = data.readInt16LE(1) / 10;
262
- break;
263
- case 4:
264
- this.data.status = "STARTED";
265
- break;
266
- case 3:
267
- case 2:
268
- this.data.status = "STOPPED";
269
- break;
270
- case 20:
271
- const spinDownStatus = data.readUInt8(1);
272
- switch (spinDownStatus) {
273
- case 1:
274
- this.data.status = "SPIN DOWN REQUESTED";
275
- break;
276
- case 2:
277
- this.data.status = "SPIN DOWN SUCCESS";
278
- break;
279
- case 3:
280
- this.data.status = "SPIN DOWN ERROR";
281
- break;
282
- case 4:
283
- this.data.status = "STOP PEDALING";
284
- break;
285
- default: break;
286
- }
296
+ try {
297
+ const OpCode = data.readUInt8(0);
298
+ switch (OpCode) {
299
+ case 8:
300
+ this.data.targetPower = data.readInt16LE(1);
301
+ break;
302
+ case 6:
303
+ this.data.targetInclination = data.readInt16LE(1) / 10;
304
+ break;
305
+ case 4:
306
+ this.data.status = "STARTED";
307
+ break;
308
+ case 3:
309
+ case 2:
310
+ this.data.status = "STOPPED";
311
+ break;
312
+ case 20:
313
+ const spinDownStatus = data.readUInt8(1);
314
+ switch (spinDownStatus) {
315
+ case 1:
316
+ this.data.status = "SPIN DOWN REQUESTED";
317
+ break;
318
+ case 2:
319
+ this.data.status = "SPIN DOWN SUCCESS";
320
+ break;
321
+ case 3:
322
+ this.data.status = "SPIN DOWN ERROR";
323
+ break;
324
+ case 4:
325
+ this.data.status = "STOP PEDALING";
326
+ break;
327
+ default: break;
328
+ }
329
+ }
330
+ }
331
+ catch (err) {
332
+ this.logEvent({ message: 'error', fn: 'parseFitnessMachineStatus()', error: err.message | err, stack: err.stack });
287
333
  }
288
334
  return Object.assign(Object.assign({}, this.data), { raw: `2ada:${data.toString('hex')}` });
289
335
  }
@@ -298,6 +344,7 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
298
344
  const fitnessMachine = buffer.readUInt32LE(0);
299
345
  const targetSettings = buffer.readUInt32LE(4);
300
346
  this.features = { fitnessMachine, targetSettings };
347
+ this.logEvent({ message: 'supported Features: ', fatures: this.features });
301
348
  }
302
349
  }
303
350
  catch (err) {
@@ -306,7 +353,9 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
306
353
  });
307
354
  }
308
355
  onData(characteristic, data) {
309
- super.onData(characteristic, data);
356
+ const hasData = super.onData(characteristic, data);
357
+ if (!hasData)
358
+ return false;
310
359
  const uuid = characteristic.toLocaleLowerCase();
311
360
  let res = undefined;
312
361
  switch (uuid) {
@@ -326,8 +375,11 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
326
375
  default:
327
376
  break;
328
377
  }
329
- if (res)
378
+ if (res) {
330
379
  this.emit('data', res);
380
+ return false;
381
+ }
382
+ return true;
331
383
  }
332
384
  writeFtmsMessage(requestedOpCode, data, props) {
333
385
  return __awaiter(this, void 0, void 0, function* () {
@@ -486,6 +538,7 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
486
538
  exports.default = BleFitnessMachineDevice;
487
539
  BleFitnessMachineDevice.services = [consts_1.FTMS];
488
540
  BleFitnessMachineDevice.characteristics = ['2acc', consts_1.INDOOR_BIKE_DATA, '2ad6', '2ad8', consts_1.FTMS_CP, consts_1.FTMS_STATUS];
541
+ BleFitnessMachineDevice.detectionPriority = 100;
489
542
  ble_interface_1.default.register('BleFitnessMachineDevice', 'fm', BleFitnessMachineDevice, BleFitnessMachineDevice.services);
490
543
  class FmAdapter extends Device_1.default {
491
544
  constructor(device, protocol) {
package/lib/ble/hrm.d.ts CHANGED
@@ -14,13 +14,14 @@ declare type HrmData = {
14
14
  export default class BleHrmDevice extends BleDevice {
15
15
  static services: string[];
16
16
  static characteristics: string[];
17
+ static detectionPriority: number;
17
18
  heartrate: number;
18
19
  rr: number;
19
20
  constructor(props?: any);
20
21
  getProfile(): string;
21
22
  getServiceUUids(): string[];
22
23
  parseHrm(_data: Uint8Array): HrmData;
23
- onData(characteristic: string, data: Buffer): void;
24
+ onData(characteristic: string, data: Buffer): boolean;
24
25
  }
25
26
  export declare class HrmAdapter extends DeviceAdapter {
26
27
  device: BleHrmDevice;
package/lib/ble/hrm.js CHANGED
@@ -51,19 +51,21 @@ class BleHrmDevice extends ble_device_1.BleDevice {
51
51
  return { heartrate, rr, raw: data.toString('hex') };
52
52
  }
53
53
  onData(characteristic, data) {
54
- super.onData(characteristic, data);
55
- const isDuplicate = this.checkForDuplicate(characteristic, data);
56
- if (isDuplicate)
54
+ const hasData = super.onData(characteristic, data);
55
+ if (!hasData)
57
56
  return;
58
57
  if (characteristic.toLocaleLowerCase() === '2a37') {
59
58
  const res = this.parseHrm(data);
60
59
  this.emit('data', res);
60
+ return false;
61
61
  }
62
+ return true;
62
63
  }
63
64
  }
64
65
  exports.default = BleHrmDevice;
65
66
  BleHrmDevice.services = ['180d'];
66
67
  BleHrmDevice.characteristics = ['2a37', '2a38', '2a39', '2a3c'];
68
+ BleHrmDevice.detectionPriority = 1;
67
69
  ble_interface_1.default.register('BleHrmDevice', 'hr', BleHrmDevice, BleHrmDevice.services);
68
70
  class HrmAdapter extends Device_1.default {
69
71
  constructor(device, protocol) {
package/lib/ble/pwr.d.ts CHANGED
@@ -24,6 +24,7 @@ declare type CrankData = {
24
24
  export default class BleCyclingPowerDevice extends BleDevice {
25
25
  static services: string[];
26
26
  static characteristics: string[];
27
+ static detectionPriority: number;
27
28
  instantaneousPower: number;
28
29
  balance: number;
29
30
  accTorque: number;
@@ -42,7 +43,7 @@ export default class BleCyclingPowerDevice extends BleDevice {
42
43
  time: any;
43
44
  };
44
45
  parsePower(_data: Uint8Array): PowerData;
45
- onData(characteristic: string, data: Buffer): void;
46
+ onData(characteristic: string, data: Buffer): boolean;
46
47
  reset(): void;
47
48
  }
48
49
  export declare class PwrAdapter extends DeviceAdapter {
@@ -71,6 +72,7 @@ export declare class PwrAdapter extends DeviceAdapter {
71
72
  getDisplayName(): string;
72
73
  getCyclingMode(): CyclingMode;
73
74
  getDefaultCyclingMode(): CyclingMode;
75
+ getSupportedCyclingModes(): any[];
74
76
  getPort(): string;
75
77
  getWeight(): number;
76
78
  setIgnoreBike(ignore: any): void;
package/lib/ble/pwr.js CHANGED
@@ -34,6 +34,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
34
34
  exports.PwrAdapter = void 0;
35
35
  const ble_device_1 = require("./ble-device");
36
36
  const ble_interface_1 = __importDefault(require("./ble-interface"));
37
+ const ble_1 = require("./ble");
37
38
  const Device_1 = __importStar(require("../Device"));
38
39
  const gd_eventlog_1 = require("gd-eventlog");
39
40
  const consts_1 = require("./consts");
@@ -138,14 +139,15 @@ class BleCyclingPowerDevice extends ble_device_1.BleDevice {
138
139
  return { instantaneousPower, balance, accTorque, rpm, time, raw: `2a63:${data.toString('hex')}` };
139
140
  }
140
141
  onData(characteristic, data) {
141
- super.onData(characteristic, data);
142
- const isDuplicate = this.checkForDuplicate(characteristic, data);
143
- if (isDuplicate)
144
- return;
145
- if (characteristic.toLocaleLowerCase() === consts_1.CSP_MEASUREMENT) {
142
+ const hasData = super.onData(characteristic, data);
143
+ if (!hasData)
144
+ return false;
145
+ if ((0, ble_1.matches)(characteristic, consts_1.CSP_MEASUREMENT)) {
146
146
  const res = this.parsePower(data);
147
147
  this.emit('data', res);
148
+ return false;
148
149
  }
150
+ return true;
149
151
  }
150
152
  reset() {
151
153
  this.instantaneousPower = undefined;
@@ -161,6 +163,7 @@ class BleCyclingPowerDevice extends ble_device_1.BleDevice {
161
163
  exports.default = BleCyclingPowerDevice;
162
164
  BleCyclingPowerDevice.services = [consts_1.CSP];
163
165
  BleCyclingPowerDevice.characteristics = [consts_1.CSP_MEASUREMENT, consts_1.CSP_FEATURE, '2a5d', '2a3c'];
166
+ BleCyclingPowerDevice.detectionPriority = 1;
164
167
  ble_interface_1.default.register('BleCyclingPowerDevice', 'cp', BleCyclingPowerDevice, BleCyclingPowerDevice.services);
165
168
  class PwrAdapter extends Device_1.default {
166
169
  constructor(device, protocol) {
@@ -201,6 +204,9 @@ class PwrAdapter extends Device_1.default {
201
204
  getDefaultCyclingMode() {
202
205
  return new power_meter_1.default(this);
203
206
  }
207
+ getSupportedCyclingModes() {
208
+ return [power_meter_1.default];
209
+ }
204
210
  getPort() {
205
211
  return 'ble';
206
212
  }
package/lib/ble/tacx.d.ts CHANGED
@@ -28,6 +28,7 @@ export default class TacxAdvancedFitnessMachineDevice extends BleFitnessMachineD
28
28
  static services: string[];
29
29
  static characteristics: string[];
30
30
  static PROFILE: string;
31
+ static detectionPriority: number;
31
32
  prevCrankData: CrankData;
32
33
  currentCrankData: CrankData;
33
34
  timeOffset: number;
@@ -63,7 +64,7 @@ export default class TacxAdvancedFitnessMachineDevice extends BleFitnessMachineD
63
64
  parseTrainerData(data: Buffer): BleFeBikeData;
64
65
  parseProductInformation(data: Buffer): BleFeBikeData;
65
66
  parseFECMessage(_data: Buffer): BleFeBikeData;
66
- onData(characteristic: string, data: Buffer): any;
67
+ onData(characteristic: string, data: Buffer): boolean;
67
68
  getChecksum(message: any[]): number;
68
69
  buildMessage(payload?: number[], msgID?: number): Buffer;
69
70
  sendMessage(message: Buffer): Promise<boolean>;
package/lib/ble/tacx.js CHANGED
@@ -84,7 +84,6 @@ class TacxAdvancedFitnessMachineDevice extends fm_1.default {
84
84
  return false;
85
85
  const hasTacxCP = characteristics.find(c => (0, ble_1.matches)(c, consts_1.TACX_FE_C_RX)) !== undefined &&
86
86
  characteristics.find(c => (0, ble_1.matches)(c, consts_1.TACX_FE_C_TX)) !== undefined;
87
- const hasFTMS = characteristics.find(c => (0, ble_1.matches)(c, consts_1.FTMS_CP)) !== undefined;
88
87
  return hasTacxCP;
89
88
  }
90
89
  setCharacteristicUUIDs(uuids) {
@@ -164,10 +163,10 @@ class TacxAdvancedFitnessMachineDevice extends fm_1.default {
164
163
  return { rpm, time: this.timeOffset + c.time };
165
164
  }
166
165
  parseCSC(_data, logOnly = false) {
167
- this.logEvent({ message: 'BLE CSC message', data: _data.toString('hex') });
166
+ const data = Buffer.from(_data);
167
+ this.logEvent({ message: 'BLE CSC message', data: data.toString('hex') });
168
168
  if (logOnly)
169
169
  return this.data;
170
- const data = Buffer.from(_data);
171
170
  let offset = 0;
172
171
  const flags = data.readUInt8(offset);
173
172
  offset++;
@@ -187,10 +186,10 @@ class TacxAdvancedFitnessMachineDevice extends fm_1.default {
187
186
  return this.data;
188
187
  }
189
188
  parsePower(_data, logOnly = false) {
190
- this.logEvent({ message: 'BLE CSP message', data: _data.toString('hex') });
189
+ const data = Buffer.from(_data);
190
+ this.logEvent({ message: 'BLE CSP message', data: data.toString('hex') });
191
191
  if (logOnly)
192
192
  return this.data;
193
- const data = Buffer.from(_data);
194
193
  try {
195
194
  let offset = 4;
196
195
  const flags = data.readUInt16LE(0);
@@ -658,6 +657,7 @@ exports.default = TacxAdvancedFitnessMachineDevice;
658
657
  TacxAdvancedFitnessMachineDevice.services = [consts_1.TACX_FE_C_BLE];
659
658
  TacxAdvancedFitnessMachineDevice.characteristics = ['2acc', '2ad2', '2ad6', '2ad8', '2ad9', '2ada', consts_1.TACX_FE_C_RX, consts_1.TACX_FE_C_TX];
660
659
  TacxAdvancedFitnessMachineDevice.PROFILE = PROFILE_ID;
660
+ TacxAdvancedFitnessMachineDevice.detectionPriority = 10;
661
661
  ble_interface_1.default.register('TacxBleFEDevice', 'tacx-ble-fec', TacxAdvancedFitnessMachineDevice, TacxAdvancedFitnessMachineDevice.services);
662
662
  class TacxBleFEAdapter extends fm_1.FmAdapter {
663
663
  constructor(device, protocol) {
@@ -42,6 +42,7 @@ declare type CrankData = {
42
42
  export default class WahooAdvancedFitnessMachineDevice extends BleFitnessMachineDevice {
43
43
  static services: string[];
44
44
  static characteristics: string[];
45
+ static detectionPriority: number;
45
46
  prevCrankData: CrankData;
46
47
  currentCrankData: CrankData;
47
48
  timeOffset: number;
@@ -70,7 +71,7 @@ export default class WahooAdvancedFitnessMachineDevice extends BleFitnessMachine
70
71
  time: any;
71
72
  };
72
73
  parsePower(_data: Buffer): IndoorBikeData;
73
- onData(characteristic: string, data: Buffer): void;
74
+ onData(characteristic: string, data: Buffer): boolean;
74
75
  writeWahooFtmsMessage(requestedOpCode: number, data: Buffer, props?: BleWriteProps): Promise<boolean>;
75
76
  requestControl(): Promise<boolean>;
76
77
  setPowerAdjusting(): void;
@@ -170,7 +170,9 @@ class WahooAdvancedFitnessMachineDevice extends fm_1.default {
170
170
  return { instantaneousPower, cadence, time, raw: data.toString('hex') };
171
171
  }
172
172
  onData(characteristic, data) {
173
- super.onData(characteristic, data);
173
+ const hasData = super.onData(characteristic, data);
174
+ if (!hasData)
175
+ return false;
174
176
  const uuid = characteristic.toLowerCase();
175
177
  let res = undefined;
176
178
  switch (uuid) {
@@ -190,8 +192,11 @@ class WahooAdvancedFitnessMachineDevice extends fm_1.default {
190
192
  this.logEvent({ message: 'data', uuid, data: data.toString('hex') });
191
193
  break;
192
194
  }
193
- if (res)
195
+ if (res) {
194
196
  this.emit('data', res);
197
+ return false;
198
+ }
199
+ return true;
195
200
  }
196
201
  writeWahooFtmsMessage(requestedOpCode, data, props) {
197
202
  return __awaiter(this, void 0, void 0, function* () {
@@ -419,6 +424,7 @@ class WahooAdvancedFitnessMachineDevice extends fm_1.default {
419
424
  exports.default = WahooAdvancedFitnessMachineDevice;
420
425
  WahooAdvancedFitnessMachineDevice.services = [consts_1.CSP];
421
426
  WahooAdvancedFitnessMachineDevice.characteristics = ['2acc', '2ad2', '2ad6', '2ad8', '2ad9', '2ada', consts_1.WAHOO_ADVANCED_TRAINER_CP];
427
+ WahooAdvancedFitnessMachineDevice.detectionPriority = 5;
422
428
  ble_interface_1.default.register('WahooAdvancedFitnessMachineDevice', 'wahoo-fm', WahooAdvancedFitnessMachineDevice, WahooAdvancedFitnessMachineDevice.services);
423
429
  class WahooAdvancedFmAdapter extends fm_1.FmAdapter {
424
430
  constructor(device, protocol) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "incyclist-devices",
3
- "version": "1.4.93",
3
+ "version": "1.4.95",
4
4
  "dependencies": {
5
5
  "@serialport/parser-byte-length": "^9.0.1",
6
6
  "@serialport/parser-delimiter": "^9.0.1",