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 +1 -1
- package/lib/ant/antpwr/pwr-adapter.d.ts +1 -0
- package/lib/ant/antpwr/pwr-adapter.js +3 -0
- package/lib/ble/ble-device.d.ts +2 -1
- package/lib/ble/ble-device.js +29 -9
- package/lib/ble/ble-interface.d.ts +1 -0
- package/lib/ble/ble-interface.js +72 -60
- package/lib/ble/ble-peripheral.d.ts +1 -1
- package/lib/ble/ble-peripheral.js +23 -13
- package/lib/ble/ble.d.ts +3 -0
- package/lib/ble/fm.d.ts +7 -2
- package/lib/ble/fm.js +146 -93
- package/lib/ble/hrm.d.ts +2 -1
- package/lib/ble/hrm.js +5 -3
- package/lib/ble/pwr.d.ts +3 -1
- package/lib/ble/pwr.js +11 -5
- package/lib/ble/tacx.d.ts +2 -1
- package/lib/ble/tacx.js +5 -5
- package/lib/ble/wahoo-kickr.d.ts +2 -1
- package/lib/ble/wahoo-kickr.js +8 -2
- package/package.json +1 -1
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;
|
package/lib/ble/ble-device.d.ts
CHANGED
|
@@ -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,
|
|
55
|
+
onData(characteristic: string, _data: Buffer): boolean;
|
|
55
56
|
timeoutCheck(): void;
|
|
56
57
|
startWorker(): void;
|
|
57
58
|
stopWorker(): void;
|
package/lib/ble/ble-device.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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,
|
|
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;
|
package/lib/ble/ble-interface.js
CHANGED
|
@@ -56,7 +56,9 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
56
56
|
if (this.logger) {
|
|
57
57
|
this.logger.logEvent(event);
|
|
58
58
|
}
|
|
59
|
-
|
|
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
|
-
|
|
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 (
|
|
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.
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
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
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
this.
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
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
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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):
|
|
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
|
|
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
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
-
|
|
248
|
-
this.
|
|
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
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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):
|
|
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
|
-
|
|
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):
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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):
|
|
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
|
-
|
|
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
|
-
|
|
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) {
|
package/lib/ble/wahoo-kickr.d.ts
CHANGED
|
@@ -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):
|
|
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;
|
package/lib/ble/wahoo-kickr.js
CHANGED
|
@@ -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) {
|