incyclist-devices 1.4.45 → 1.4.46
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.d.ts +2 -0
- package/lib/Device.js +1 -0
- package/lib/ant/AntAdapter.d.ts +1 -0
- package/lib/ant/AntAdapter.js +6 -0
- package/lib/ble/ble-device.d.ts +9 -7
- package/lib/ble/ble-device.js +83 -121
- package/lib/ble/ble-erg-mode.d.ts +24 -0
- package/lib/ble/ble-erg-mode.js +148 -0
- package/lib/ble/ble-interface.d.ts +13 -0
- package/lib/ble/ble-interface.js +49 -25
- package/lib/ble/ble-peripheral.d.ts +34 -0
- package/lib/ble/ble-peripheral.js +170 -0
- package/lib/ble/ble-st-mode.d.ts +24 -0
- package/lib/ble/ble-st-mode.js +148 -0
- package/lib/ble/ble.d.ts +9 -0
- package/lib/ble/fm.d.ts +6 -1
- package/lib/ble/fm.js +46 -6
- package/lib/ble/hrm.d.ts +1 -1
- package/lib/ble/hrm.js +6 -4
- package/lib/ble/incyclist-protocol.js +9 -1
- package/lib/ble/pwr.d.ts +1 -1
- package/lib/ble/pwr.js +6 -4
- package/lib/daum/DaumAdapter.d.ts +1 -0
- package/lib/daum/DaumAdapter.js +6 -0
- package/lib/kettler/ergo-racer/adapter.d.ts +1 -0
- package/lib/kettler/ergo-racer/adapter.js +6 -0
- package/lib/simulator/Simulator.d.ts +1 -0
- package/lib/simulator/Simulator.js +5 -0
- package/package.json +1 -1
package/lib/ble/ble-interface.js
CHANGED
|
@@ -8,10 +8,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
11
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
15
|
const gd_eventlog_1 = require("gd-eventlog");
|
|
13
16
|
const utils_1 = require("../utils");
|
|
14
17
|
const ble_1 = require("./ble");
|
|
18
|
+
const ble_peripheral_1 = __importDefault(require("./ble-peripheral"));
|
|
15
19
|
const CONNECT_TIMEOUT = 5000;
|
|
16
20
|
const DEFAULT_SCAN_TIMEOUT = 20000;
|
|
17
21
|
const BACKGROUND_SCAN_TIMEOUT = 30000;
|
|
@@ -71,6 +75,7 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
71
75
|
connect(props = {}) {
|
|
72
76
|
const timeout = props.timeout || 2000;
|
|
73
77
|
const runBackgroundScan = () => {
|
|
78
|
+
return;
|
|
74
79
|
this.scanState.isBackgroundScan = true;
|
|
75
80
|
this.scan({ timeout: BACKGROUND_SCAN_TIMEOUT, isBackgroundScan: true })
|
|
76
81
|
.then(() => {
|
|
@@ -267,7 +272,22 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
267
272
|
});
|
|
268
273
|
}
|
|
269
274
|
addPeripheralToCache(peripheral, props = {}) {
|
|
270
|
-
this.peripheralCache.
|
|
275
|
+
const info = this.peripheralCache.find(i => i.address === peripheral.address);
|
|
276
|
+
const connector = info && info.connector ? info.connector : new ble_peripheral_1.default(this, peripheral);
|
|
277
|
+
this.peripheralCache.push(Object.assign({ address: peripheral.address, ts: Date.now(), peripheral, connector }, props));
|
|
278
|
+
}
|
|
279
|
+
getConnector(peripheral) {
|
|
280
|
+
const info = this.peripheralCache.find(i => i.address === peripheral.address);
|
|
281
|
+
if (!info) {
|
|
282
|
+
const connector = new ble_peripheral_1.default(this, peripheral);
|
|
283
|
+
this.peripheralCache.push({ address: peripheral.address, ts: Date.now(), peripheral, connector });
|
|
284
|
+
return connector;
|
|
285
|
+
}
|
|
286
|
+
return info.connector;
|
|
287
|
+
}
|
|
288
|
+
findPeripheral(peripheral) {
|
|
289
|
+
const info = this.peripheralCache.find(i => i.address === peripheral.address || peripheral.address === i.peripheral.address || peripheral.name === i.peripheral.name || peripheral.id === i.peripheral.id);
|
|
290
|
+
return info ? info.peripheral : undefined;
|
|
271
291
|
}
|
|
272
292
|
getCharacteristics(peripheral) {
|
|
273
293
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -280,28 +300,17 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
280
300
|
this.addPeripheralToCache(peripheral);
|
|
281
301
|
chachedPeripheralInfo = this.peripheralCache.find(i => i.address === peripheral.address);
|
|
282
302
|
}
|
|
303
|
+
const connector = chachedPeripheralInfo.connector;
|
|
283
304
|
if (!chachedPeripheralInfo.characteristics) {
|
|
284
305
|
try {
|
|
285
306
|
chachedPeripheralInfo.state = { isConfigured: false, isLoading: true, isInterrupted: false };
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}
|
|
293
|
-
const res = yield peripheral.discoverSomeServicesAndCharacteristicsAsync([], []);
|
|
294
|
-
if (!chachedPeripheralInfo.state.isInterrupted) {
|
|
295
|
-
this.logEvent({ message: 'characteristic info (+):', info: res.characteristics.map(c => `${peripheral.address} ${c.uuid} ${c.properties}`) });
|
|
296
|
-
chachedPeripheralInfo.characteristics = res.characteristics;
|
|
297
|
-
chachedPeripheralInfo.state = { isConfigured: true, isLoading: false, isInterrupted: false };
|
|
298
|
-
characteristics = res.characteristics;
|
|
299
|
-
}
|
|
300
|
-
else {
|
|
301
|
-
this.logEvent({ message: 'characteristic info:', info: 'interrupted' });
|
|
302
|
-
chachedPeripheralInfo.state = { isConfigured: false, isLoading: false, isInterrupted: false };
|
|
303
|
-
throw new Error('interrupted');
|
|
304
|
-
}
|
|
307
|
+
yield connector.connect();
|
|
308
|
+
peripheral.state = connector.getState();
|
|
309
|
+
yield connector.initialize();
|
|
310
|
+
characteristics = connector.getCharachteristics();
|
|
311
|
+
this.logEvent({ message: 'characteristic info (+):', info: characteristics.map(c => `${peripheral.address} ${c.uuid} ${c.properties}`) });
|
|
312
|
+
chachedPeripheralInfo.characteristics = characteristics;
|
|
313
|
+
chachedPeripheralInfo.state = { isConfigured: true, isLoading: false, isInterrupted: false };
|
|
305
314
|
}
|
|
306
315
|
catch (err) {
|
|
307
316
|
console.log(err);
|
|
@@ -339,6 +348,9 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
339
348
|
createDevice(DeviceClass, peripheral, characteristics) {
|
|
340
349
|
const C = DeviceClass;
|
|
341
350
|
const device = new C({ peripheral });
|
|
351
|
+
const existingDevice = this.devices.find(i => i.device.id === device.id && i.device.getProfile() === device.getProfile());
|
|
352
|
+
if (existingDevice)
|
|
353
|
+
return existingDevice;
|
|
342
354
|
device.setInterface(this);
|
|
343
355
|
device.characteristics = characteristics;
|
|
344
356
|
return device;
|
|
@@ -525,12 +537,13 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
525
537
|
const characteristics = yield this.getCharacteristics(peripheral);
|
|
526
538
|
const DeviceClasses = this.getDeviceClasses(peripheral, { profile });
|
|
527
539
|
let cntFound = 0;
|
|
528
|
-
DeviceClasses.forEach(DeviceClass => {
|
|
540
|
+
DeviceClasses.forEach((DeviceClass) => __awaiter(this, void 0, void 0, function* () {
|
|
529
541
|
if (!DeviceClass)
|
|
530
542
|
return;
|
|
531
543
|
if (scanForDevice && cntFound > 0)
|
|
532
544
|
return;
|
|
533
545
|
const d = this.createDevice(DeviceClass, peripheral, characteristics);
|
|
546
|
+
yield d.connect();
|
|
534
547
|
if (scanForDevice) {
|
|
535
548
|
if ((id && id !== '' && d.id === id) ||
|
|
536
549
|
(address && address !== '' && d.address === address) ||
|
|
@@ -542,14 +555,14 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
542
555
|
const existing = devicesProcessed.find(device => device.id === d.id && device.getProfile() === d.getProfile());
|
|
543
556
|
if (!scanForDevice && cntFound > 0 && !existing) {
|
|
544
557
|
this.logEvent({ message: `${opStr}: device found`, device: d.name, address: d.address, services: d.services.join(',') });
|
|
545
|
-
this.
|
|
558
|
+
this.addDeviceToCache(d, peripheral.state === 'connected');
|
|
546
559
|
devicesProcessed.push(d);
|
|
547
560
|
this.emit('device', d);
|
|
548
561
|
return;
|
|
549
562
|
}
|
|
550
563
|
if (scanForDevice && cntFound > 0) {
|
|
551
564
|
this.logEvent({ message: `${opStr}: device found`, device: d.name, address: d.address, services: d.services.join(',') });
|
|
552
|
-
this.
|
|
565
|
+
this.addDeviceToCache(d, peripheral.state === 'connected');
|
|
553
566
|
devicesProcessed.push(d);
|
|
554
567
|
this.emit('device', d);
|
|
555
568
|
process.nextTick(() => {
|
|
@@ -565,7 +578,7 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
565
578
|
});
|
|
566
579
|
});
|
|
567
580
|
}
|
|
568
|
-
});
|
|
581
|
+
}));
|
|
569
582
|
});
|
|
570
583
|
this.logEvent({ message: `${opStr}: start scanning`, requested: scanForDevice ? { name, address, profile } : undefined, timeout });
|
|
571
584
|
this.peripheralCache.forEach(i => {
|
|
@@ -608,19 +621,30 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
608
621
|
return this.scanState.isScanning;
|
|
609
622
|
}
|
|
610
623
|
addConnectedDevice(device) {
|
|
611
|
-
const existigDevice = this.devices.find(i => i.device.id === device.id);
|
|
624
|
+
const existigDevice = this.devices.find(i => i.device.id === device.id && i.device.getProfile() === device.getProfile());
|
|
612
625
|
if (existigDevice) {
|
|
613
626
|
existigDevice.isConnected = true;
|
|
614
627
|
return;
|
|
615
628
|
}
|
|
616
629
|
this.devices.push({ device, isConnected: true });
|
|
617
630
|
}
|
|
631
|
+
addDeviceToCache(device, isConnected) {
|
|
632
|
+
const existigDevice = this.devices.find(i => i.device.id === device.id && i.device.getProfile() === device.getProfile());
|
|
633
|
+
if (existigDevice) {
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
this.devices.push({ device, isConnected });
|
|
637
|
+
}
|
|
618
638
|
findConnected(device) {
|
|
619
639
|
const connected = this.devices.find(i => i.device.id === device.id && i.isConnected);
|
|
620
640
|
if (connected)
|
|
621
641
|
return connected.device;
|
|
622
642
|
return undefined;
|
|
623
643
|
}
|
|
644
|
+
findDeviceInCache(device) {
|
|
645
|
+
const existing = this.devices.find(i => (i.device.id === device.id || i.device.address === device.address || i.device.name === device.name) && i.device.getProfile() === device.profile);
|
|
646
|
+
return existing ? existing.device : undefined;
|
|
647
|
+
}
|
|
624
648
|
removeConnectedDevice(device) {
|
|
625
649
|
const existigDevice = this.devices.find(i => i.device.id === device.id);
|
|
626
650
|
if (existigDevice) {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { BleCharacteristic, BlePeripheral } from "./ble";
|
|
2
|
+
import BleInterface from "./ble-interface";
|
|
3
|
+
export declare type ConnectorState = {
|
|
4
|
+
isConnected: boolean;
|
|
5
|
+
isConnecting: boolean;
|
|
6
|
+
isInitialized: boolean;
|
|
7
|
+
isInitializing: boolean;
|
|
8
|
+
isSubscribing: boolean;
|
|
9
|
+
subscribed?: string[];
|
|
10
|
+
};
|
|
11
|
+
export default class BlePeripheralConnector {
|
|
12
|
+
private state;
|
|
13
|
+
private services;
|
|
14
|
+
private characteristics;
|
|
15
|
+
private ble;
|
|
16
|
+
private peripheral;
|
|
17
|
+
private logger?;
|
|
18
|
+
private emitter;
|
|
19
|
+
constructor(ble: BleInterface, peripheral: BlePeripheral);
|
|
20
|
+
logEvent(event: any): void;
|
|
21
|
+
connect(): Promise<void>;
|
|
22
|
+
reconnect(): Promise<void>;
|
|
23
|
+
onDisconnect(): void;
|
|
24
|
+
initialize(enforce?: boolean): Promise<void>;
|
|
25
|
+
subscribeAll(callback: (characteristicUuid: string, data: any) => void): Promise<string[]>;
|
|
26
|
+
subscribe(characteristicUuid: string): Promise<boolean>;
|
|
27
|
+
onData(characteristicUuid: string, data: any): void;
|
|
28
|
+
on(characteristicUuid: string, callback: (characteristicUuid: string, data: any) => void): void;
|
|
29
|
+
off(characteristicUuid: string, callback: (characteristicUuid: string, data: any) => void): void;
|
|
30
|
+
removeAllListeners(characteristicUuid: string): void;
|
|
31
|
+
getState(): string;
|
|
32
|
+
getCharachteristics(): BleCharacteristic[];
|
|
33
|
+
getServices(): string[];
|
|
34
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const ble_1 = require("./ble");
|
|
16
|
+
const gd_eventlog_1 = require("gd-eventlog");
|
|
17
|
+
const events_1 = __importDefault(require("events"));
|
|
18
|
+
class BlePeripheralConnector {
|
|
19
|
+
constructor(ble, peripheral) {
|
|
20
|
+
this.ble = ble;
|
|
21
|
+
this.peripheral = peripheral;
|
|
22
|
+
this.emitter = new events_1.default();
|
|
23
|
+
if (!this.peripheral || !this.ble)
|
|
24
|
+
throw new Error('Illegal Arguments');
|
|
25
|
+
this.state = { isConnected: false, isConnecting: false, isInitialized: false, isInitializing: false, isSubscribing: false };
|
|
26
|
+
this.services = undefined;
|
|
27
|
+
this.characteristics = undefined;
|
|
28
|
+
this.logger = new gd_eventlog_1.EventLogger('BLE');
|
|
29
|
+
}
|
|
30
|
+
logEvent(event) {
|
|
31
|
+
if (this.logger) {
|
|
32
|
+
this.logger.logEvent(event);
|
|
33
|
+
}
|
|
34
|
+
if (process.env.BLE_DEBUG) {
|
|
35
|
+
console.log('~~~BLE:', event);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
connect() {
|
|
39
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
40
|
+
if (this.state.isConnected)
|
|
41
|
+
return;
|
|
42
|
+
this.logEvent({ message: 'connect', peripheral: this.peripheral.address, state: this.state });
|
|
43
|
+
this.state.isConnecting = true;
|
|
44
|
+
try {
|
|
45
|
+
if (!this.state.isConnected || (this.peripheral && this.peripheral.state !== 'connected')) {
|
|
46
|
+
yield this.peripheral.connectAsync();
|
|
47
|
+
this.peripheral.once('disconnect', this.onDisconnect.bind(this));
|
|
48
|
+
}
|
|
49
|
+
this.state.isConnected = this.peripheral.state === 'connected';
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
this.logEvent({ message: 'Error', fn: 'connect()', error: err.message });
|
|
53
|
+
}
|
|
54
|
+
this.state.isConnecting = false;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
reconnect() {
|
|
58
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
59
|
+
this.connect();
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
onDisconnect() {
|
|
63
|
+
this.logEvent({ message: 'onDisconnected', peripheral: this.peripheral.address, state: this.state });
|
|
64
|
+
this.state.isConnected = false;
|
|
65
|
+
this.reconnect();
|
|
66
|
+
}
|
|
67
|
+
initialize(enforce = false) {
|
|
68
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
69
|
+
if (this.state.isInitialized && !enforce)
|
|
70
|
+
return;
|
|
71
|
+
this.logEvent({ message: 'initialize', peripheral: this.peripheral.address, state: this.state, enforce });
|
|
72
|
+
if (this.state.isInitialized && enforce) {
|
|
73
|
+
this.state.isInitialized = false;
|
|
74
|
+
}
|
|
75
|
+
this.state.isInitializing = true;
|
|
76
|
+
this.characteristics = undefined;
|
|
77
|
+
this.services = undefined;
|
|
78
|
+
try {
|
|
79
|
+
const res = yield this.peripheral.discoverSomeServicesAndCharacteristicsAsync([], []);
|
|
80
|
+
this.characteristics = res.characteristics;
|
|
81
|
+
this.services = res.services;
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
}
|
|
85
|
+
this.state.isInitializing = false;
|
|
86
|
+
this.state.isInitialized = this.characteristics !== undefined && this.services !== undefined;
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
subscribeAll(callback) {
|
|
90
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
91
|
+
const cnt = this.characteristics.length;
|
|
92
|
+
this.state.isSubscribing = true;
|
|
93
|
+
const subscribed = [];
|
|
94
|
+
if (!this.state.subscribed)
|
|
95
|
+
this.state.subscribed = [];
|
|
96
|
+
for (let i = 0; i < cnt; i++) {
|
|
97
|
+
try {
|
|
98
|
+
const c = this.characteristics[i];
|
|
99
|
+
const isNotify = c.properties.find(p => p === 'notify');
|
|
100
|
+
if (isNotify && subscribed.find(uuid => uuid === c.uuid) === undefined) {
|
|
101
|
+
c.on('data', (data, _isNotification) => {
|
|
102
|
+
this.onData((0, ble_1.uuid)(c.uuid), data);
|
|
103
|
+
});
|
|
104
|
+
if (callback)
|
|
105
|
+
this.on((0, ble_1.uuid)(c.uuid), callback);
|
|
106
|
+
this.logEvent({ message: 'subscribe', peripheral: this.peripheral.address, characteristic: c.uuid });
|
|
107
|
+
if (this.state.subscribed.find(uuid => uuid === c.uuid) === undefined) {
|
|
108
|
+
try {
|
|
109
|
+
yield this.subscribe(c.uuid);
|
|
110
|
+
subscribed.push(c.uuid);
|
|
111
|
+
this.state.subscribed.push(c.uuid);
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
this.logEvent({ message: 'cannot subscribe', peripheral: this.peripheral.address, characteristic: c.uuid, error: err.message || err });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
console.log('~~~ error', err);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
this.state.isSubscribing = false;
|
|
124
|
+
this.state.subscribed = subscribed;
|
|
125
|
+
return subscribed;
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
subscribe(characteristicUuid) {
|
|
129
|
+
return new Promise((resolve, reject) => {
|
|
130
|
+
const characteristic = this.characteristics.find(c => c.uuid === characteristicUuid || (0, ble_1.uuid)(c.uuid) === characteristicUuid);
|
|
131
|
+
if (!characteristic) {
|
|
132
|
+
reject(new Error('Characteristic not found'));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
characteristic.on('data', (data, _isNotification) => {
|
|
136
|
+
this.onData(characteristicUuid, data);
|
|
137
|
+
});
|
|
138
|
+
characteristic.subscribe((err) => {
|
|
139
|
+
if (err)
|
|
140
|
+
reject(err);
|
|
141
|
+
else
|
|
142
|
+
resolve(true);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
onData(characteristicUuid, data) {
|
|
147
|
+
this.emitter.emit(characteristicUuid, characteristicUuid, data);
|
|
148
|
+
}
|
|
149
|
+
on(characteristicUuid, callback) {
|
|
150
|
+
if (callback)
|
|
151
|
+
this.emitter.on(characteristicUuid, callback);
|
|
152
|
+
}
|
|
153
|
+
off(characteristicUuid, callback) {
|
|
154
|
+
if (callback)
|
|
155
|
+
this.emitter.off(characteristicUuid, callback);
|
|
156
|
+
}
|
|
157
|
+
removeAllListeners(characteristicUuid) {
|
|
158
|
+
this.emitter.removeAllListeners(characteristicUuid);
|
|
159
|
+
}
|
|
160
|
+
getState() {
|
|
161
|
+
return this.peripheral.state;
|
|
162
|
+
}
|
|
163
|
+
getCharachteristics() {
|
|
164
|
+
return this.characteristics;
|
|
165
|
+
}
|
|
166
|
+
getServices() {
|
|
167
|
+
return this.services;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
exports.default = BlePeripheralConnector;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import CyclingMode, { CyclingModeProperty, IncyclistBikeData, UpdateRequest } from "../CyclingMode";
|
|
2
|
+
import PowerBasedCyclingModeBase from "../modes/power-base";
|
|
3
|
+
import { FmAdapter } from "./fm";
|
|
4
|
+
export declare type ERGEvent = {
|
|
5
|
+
rpmUpdated?: boolean;
|
|
6
|
+
gearUpdated?: boolean;
|
|
7
|
+
starting?: boolean;
|
|
8
|
+
tsStart?: number;
|
|
9
|
+
};
|
|
10
|
+
export default class ERGCyclingMode extends PowerBasedCyclingModeBase implements CyclingMode {
|
|
11
|
+
prevRequest: UpdateRequest;
|
|
12
|
+
hasBikeUpdate: boolean;
|
|
13
|
+
chain: number[];
|
|
14
|
+
cassette: number[];
|
|
15
|
+
event: ERGEvent;
|
|
16
|
+
constructor(adapter: FmAdapter, props?: any);
|
|
17
|
+
getName(): string;
|
|
18
|
+
getDescription(): string;
|
|
19
|
+
getProperties(): CyclingModeProperty[];
|
|
20
|
+
getProperty(name: string): CyclingModeProperty;
|
|
21
|
+
getBikeInitRequest(): UpdateRequest;
|
|
22
|
+
sendBikeUpdate(request: UpdateRequest): UpdateRequest;
|
|
23
|
+
updateData(bikeData: IncyclistBikeData): any;
|
|
24
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const CyclingMode_1 = require("../CyclingMode");
|
|
7
|
+
const power_base_1 = __importDefault(require("../modes/power-base"));
|
|
8
|
+
const config = {
|
|
9
|
+
name: "Smart Trainer",
|
|
10
|
+
description: "Calculates speed based on power and slope. Slope is set to the device",
|
|
11
|
+
properties: [
|
|
12
|
+
{ key: 'bikeType', name: 'Bike Type', description: '', type: CyclingMode_1.CyclingModeProperyType.SingleSelect, options: ['Race', 'Mountain', 'Triathlon'], default: 'Race' },
|
|
13
|
+
{ key: 'startPower', name: 'Starting Power', description: 'Initial power in Watts at start of training', type: CyclingMode_1.CyclingModeProperyType.Integer, default: 50, min: 25, max: 800 },
|
|
14
|
+
]
|
|
15
|
+
};
|
|
16
|
+
class ERGCyclingMode extends power_base_1.default {
|
|
17
|
+
constructor(adapter, props) {
|
|
18
|
+
super(adapter, props);
|
|
19
|
+
this.hasBikeUpdate = false;
|
|
20
|
+
this.event = {};
|
|
21
|
+
this.initLogger('ERGMode');
|
|
22
|
+
}
|
|
23
|
+
getName() {
|
|
24
|
+
return config.name;
|
|
25
|
+
}
|
|
26
|
+
getDescription() {
|
|
27
|
+
return config.description;
|
|
28
|
+
}
|
|
29
|
+
getProperties() {
|
|
30
|
+
return config.properties;
|
|
31
|
+
}
|
|
32
|
+
getProperty(name) {
|
|
33
|
+
return config.properties.find(p => p.name === name);
|
|
34
|
+
}
|
|
35
|
+
getBikeInitRequest() {
|
|
36
|
+
const startPower = this.getSetting('startPower');
|
|
37
|
+
return { targetPower: startPower };
|
|
38
|
+
}
|
|
39
|
+
sendBikeUpdate(request) {
|
|
40
|
+
const getData = () => {
|
|
41
|
+
if (!this.data)
|
|
42
|
+
return {};
|
|
43
|
+
const { pedalRpm, slope, power, speed } = this.data;
|
|
44
|
+
return { pedalRpm, slope, power, speed };
|
|
45
|
+
};
|
|
46
|
+
this.logger.logEvent({ message: "processing update request", request, prev: this.prevRequest, data: getData(), event: this.event });
|
|
47
|
+
let newRequest = {};
|
|
48
|
+
try {
|
|
49
|
+
if (!request || request.reset || Object.keys(request).length === 0) {
|
|
50
|
+
this.prevRequest = {};
|
|
51
|
+
return request || {};
|
|
52
|
+
}
|
|
53
|
+
const prevData = this.data || {};
|
|
54
|
+
if (request.targetPower !== undefined) {
|
|
55
|
+
delete request.slope;
|
|
56
|
+
delete request.refresh;
|
|
57
|
+
}
|
|
58
|
+
if (this.event.starting && request.targetPower === undefined) {
|
|
59
|
+
newRequest.targetPower = this.getSetting('startPower');
|
|
60
|
+
if (this.event.tsStart && Date.now() - this.event.tsStart > 5000) {
|
|
61
|
+
delete this.event.starting;
|
|
62
|
+
delete this.event.tsStart;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (request.refresh) {
|
|
66
|
+
delete request.refresh;
|
|
67
|
+
newRequest.targetPower = this.prevRequest.targetPower;
|
|
68
|
+
}
|
|
69
|
+
if (request.slope !== undefined) {
|
|
70
|
+
if (!this.data)
|
|
71
|
+
this.data = {};
|
|
72
|
+
this.data.slope = request.slope;
|
|
73
|
+
}
|
|
74
|
+
if (request.maxPower !== undefined && request.minPower !== undefined && request.maxPower === request.minPower) {
|
|
75
|
+
request.targetPower = request.maxPower;
|
|
76
|
+
}
|
|
77
|
+
if (request.targetPower !== undefined) {
|
|
78
|
+
newRequest.targetPower = request.targetPower;
|
|
79
|
+
}
|
|
80
|
+
delete request.slope;
|
|
81
|
+
if (request.maxPower !== undefined) {
|
|
82
|
+
if (newRequest.targetPower !== undefined && newRequest.targetPower > request.maxPower) {
|
|
83
|
+
newRequest.targetPower = request.maxPower;
|
|
84
|
+
}
|
|
85
|
+
newRequest.maxPower = request.maxPower;
|
|
86
|
+
}
|
|
87
|
+
if (request.minPower !== undefined) {
|
|
88
|
+
if (newRequest.targetPower !== undefined && newRequest.targetPower < request.minPower) {
|
|
89
|
+
newRequest.targetPower = request.minPower;
|
|
90
|
+
}
|
|
91
|
+
newRequest.minPower = request.minPower;
|
|
92
|
+
}
|
|
93
|
+
if (newRequest.targetPower !== undefined && prevData.power !== undefined && newRequest.targetPower === prevData.power) {
|
|
94
|
+
delete newRequest.targetPower;
|
|
95
|
+
}
|
|
96
|
+
this.prevRequest = JSON.parse(JSON.stringify(request));
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
this.logger.logEvent({ message: "error", fn: 'sendBikeUpdate()', error: err.message || err, stack: err.stack });
|
|
100
|
+
}
|
|
101
|
+
return newRequest;
|
|
102
|
+
}
|
|
103
|
+
updateData(bikeData) {
|
|
104
|
+
const prevData = JSON.parse(JSON.stringify(this.data || {}));
|
|
105
|
+
const prevSpeed = prevData.speed;
|
|
106
|
+
const prevRequest = this.prevRequest || {};
|
|
107
|
+
const data = this.data || {};
|
|
108
|
+
const bikeType = this.getSetting('bikeType').toLowerCase();
|
|
109
|
+
delete this.event.rpmUpdated;
|
|
110
|
+
if (prevData === {} || prevData.speed === undefined || prevData.speed === 0) {
|
|
111
|
+
this.event.starting = true;
|
|
112
|
+
this.event.tsStart = Date.now();
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
const rpm = bikeData.pedalRpm || 0;
|
|
116
|
+
let power = bikeData.power || 0;
|
|
117
|
+
const slope = (prevData.slope !== undefined ? prevData.slope : prevRequest.slope || 0);
|
|
118
|
+
const distanceInternal = prevData.distanceInternal || 0;
|
|
119
|
+
if (!bikeData.pedalRpm || bikeData.isPedalling === false) {
|
|
120
|
+
power = 0;
|
|
121
|
+
}
|
|
122
|
+
const m = this.getWeight();
|
|
123
|
+
const t = this.getTimeSinceLastUpdate();
|
|
124
|
+
const { speed, distance } = this.calculateSpeedAndDistance(power, slope, m, t, { bikeType });
|
|
125
|
+
data.speed = parseFloat(speed.toFixed(1));
|
|
126
|
+
data.power = Math.round(power);
|
|
127
|
+
data.distanceInternal = Math.round(distanceInternal + distance);
|
|
128
|
+
data.slope = slope;
|
|
129
|
+
data.pedalRpm = rpm;
|
|
130
|
+
if (data.time !== undefined && !(this.event.starting && !bikeData.pedalRpm))
|
|
131
|
+
data.time += t;
|
|
132
|
+
else
|
|
133
|
+
data.time = 0;
|
|
134
|
+
data.heartrate = bikeData.heartrate;
|
|
135
|
+
data.isPedalling = bikeData.isPedalling;
|
|
136
|
+
if (rpm && rpm !== prevData.pedalRpm) {
|
|
137
|
+
this.event.rpmUpdated = true;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
this.logger.logEvent({ message: 'error', fn: 'updateData()', error: err.message || err });
|
|
142
|
+
}
|
|
143
|
+
this.logger.logEvent({ message: "updateData result", data, bikeData, prevRequest, prevSpeed });
|
|
144
|
+
this.data = data;
|
|
145
|
+
return data;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
exports.default = ERGCyclingMode;
|
package/lib/ble/ble.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
/// <reference types="node" />
|
|
3
3
|
import EventEmitter from "events";
|
|
4
|
+
import BlePeripheralConnector from "./ble-peripheral";
|
|
4
5
|
export declare type ConnectProps = {
|
|
5
6
|
timeout?: number;
|
|
6
7
|
};
|
|
@@ -77,6 +78,12 @@ export declare abstract class BleInterfaceClass extends EventEmitter {
|
|
|
77
78
|
abstract addConnectedDevice(device: BleDeviceClass): void;
|
|
78
79
|
abstract removeConnectedDevice(device: BleDeviceClass): void;
|
|
79
80
|
abstract findConnected(device: BleDeviceClass | BlePeripheral): BleDeviceClass;
|
|
81
|
+
abstract getConnector(peripheral: BlePeripheral): BlePeripheralConnector;
|
|
82
|
+
abstract findPeripheral(peripheral: BlePeripheral | {
|
|
83
|
+
id?: string;
|
|
84
|
+
address?: string;
|
|
85
|
+
name?: string;
|
|
86
|
+
}): BlePeripheral;
|
|
80
87
|
getBinding(): BleBinding;
|
|
81
88
|
setBinding(binding: BleBinding): void;
|
|
82
89
|
}
|
|
@@ -89,6 +96,8 @@ export interface BlePeripheral extends EventEmitter, BleDeviceIdentifier {
|
|
|
89
96
|
discoverSomeServicesAndCharacteristicsAsync(serviceUUIDs: string[], characteristicUUIDs: string[]): Promise<any>;
|
|
90
97
|
}
|
|
91
98
|
export interface BleCharacteristic extends EventEmitter {
|
|
99
|
+
uuid: string;
|
|
100
|
+
properties: string[];
|
|
92
101
|
subscribe(callback: (err: Error) => void): void;
|
|
93
102
|
read(callback: (err: Error, data: Buffer) => void): void;
|
|
94
103
|
write(data: Buffer, withoutResponse: boolean, callback?: (err: Error) => void): void;
|
package/lib/ble/fm.d.ts
CHANGED
|
@@ -41,8 +41,11 @@ export default class BleFitnessMachineDevice extends BleDevice {
|
|
|
41
41
|
static characteristics: string[];
|
|
42
42
|
data: IndoorBikeData;
|
|
43
43
|
features: IndoorBikeFeatures;
|
|
44
|
+
hasControl: boolean;
|
|
45
|
+
isCPSubscribed: boolean;
|
|
44
46
|
constructor(props?: any);
|
|
45
47
|
init(): Promise<boolean>;
|
|
48
|
+
onDisconnect(): void;
|
|
46
49
|
getProfile(): string;
|
|
47
50
|
getServiceUUids(): string[];
|
|
48
51
|
isBike(): boolean;
|
|
@@ -52,7 +55,8 @@ export default class BleFitnessMachineDevice extends BleDevice {
|
|
|
52
55
|
parseIndoorBikeData(_data: Uint8Array): IndoorBikeData;
|
|
53
56
|
getFitnessMachineFeatures(): Promise<IndoorBikeFeatures>;
|
|
54
57
|
onData(characteristic: string, data: Buffer): void;
|
|
55
|
-
|
|
58
|
+
requestControl(): Promise<boolean>;
|
|
59
|
+
setTargetPower(power: number): Promise<void>;
|
|
56
60
|
reset(): void;
|
|
57
61
|
}
|
|
58
62
|
export declare class FmAdapter extends DeviceAdapter {
|
|
@@ -69,6 +73,7 @@ export declare class FmAdapter extends DeviceAdapter {
|
|
|
69
73
|
isBike(): boolean;
|
|
70
74
|
isHrm(): boolean;
|
|
71
75
|
isPower(): boolean;
|
|
76
|
+
isSame(device: DeviceAdapter): boolean;
|
|
72
77
|
getProfile(): string;
|
|
73
78
|
getName(): string;
|
|
74
79
|
getDisplayName(): string;
|