incyclist-devices 1.4.18 → 1.4.21
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 +1 -0
- package/lib/kettler/comms.js +11 -9
- package/lib/kettler/ergo-racer/adapter.d.ts +4 -3
- package/lib/kettler/ergo-racer/adapter.js +81 -68
- package/lib/utils.js +6 -3
- package/package.json +1 -1
package/lib/Device.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export declare type DeviceData = {
|
|
|
12
12
|
timestamp?: number;
|
|
13
13
|
deviceTime?: number;
|
|
14
14
|
deviceDistanceCounter?: number;
|
|
15
|
+
internalDistanceCounter?: number;
|
|
15
16
|
};
|
|
16
17
|
export declare type OnDeviceDataCallback = (data: DeviceData) => void;
|
|
17
18
|
export declare type OnDeviceStartCallback = (completed: number, total: number) => void;
|
package/lib/kettler/comms.js
CHANGED
|
@@ -16,7 +16,7 @@ const gd_eventlog_1 = require("gd-eventlog");
|
|
|
16
16
|
const utils_1 = require("../utils");
|
|
17
17
|
const events_1 = __importDefault(require("events"));
|
|
18
18
|
const DEFAULT_RCV_TIMEOUT = 1500;
|
|
19
|
-
const DEFAULT_OPEN_TIMEOUT =
|
|
19
|
+
const DEFAULT_OPEN_TIMEOUT = 3000;
|
|
20
20
|
const DEBUG_LOGGER = {
|
|
21
21
|
log: (e, ...args) => console.log(e, ...args),
|
|
22
22
|
logEvent: (event) => console.log(JSON.stringify(event))
|
|
@@ -200,6 +200,7 @@ class KettlerSerialComms extends events_1.default {
|
|
|
200
200
|
cmd.onError(err);
|
|
201
201
|
this.sendState = SendState.Idle;
|
|
202
202
|
this.currentCmd = undefined;
|
|
203
|
+
this.stopCurrentTimeoutCheck();
|
|
203
204
|
};
|
|
204
205
|
try {
|
|
205
206
|
this.logger.logEvent({ message: "sendCommand:sending:", cmd: logStr, msg, port: this.getPort() });
|
|
@@ -208,16 +209,17 @@ class KettlerSerialComms extends events_1.default {
|
|
|
208
209
|
this.sp.write(msg + CRLF, (err) => {
|
|
209
210
|
this.sendState = SendState.Receiving;
|
|
210
211
|
this.currentCmd = cmd;
|
|
211
|
-
if (timeout) {
|
|
212
|
-
this.currentTimeout = setTimeout(() => {
|
|
213
|
-
if (this.sendState === SendState.Receiving) {
|
|
214
|
-
onError(new Error("response timeout"));
|
|
215
|
-
}
|
|
216
|
-
}, timeout);
|
|
217
|
-
}
|
|
218
212
|
if (err)
|
|
219
213
|
onError(err);
|
|
220
214
|
});
|
|
215
|
+
this.currentTimeout = setTimeout(() => {
|
|
216
|
+
if (this.sendState === SendState.Sending) {
|
|
217
|
+
onError(new Error("send timeout"));
|
|
218
|
+
}
|
|
219
|
+
if (this.sendState === SendState.Receiving) {
|
|
220
|
+
onError(new Error("response timeout"));
|
|
221
|
+
}
|
|
222
|
+
}, timeout);
|
|
221
223
|
}
|
|
222
224
|
catch (err) {
|
|
223
225
|
onError(err);
|
|
@@ -232,7 +234,7 @@ class KettlerSerialComms extends events_1.default {
|
|
|
232
234
|
this.write(cmd);
|
|
233
235
|
}
|
|
234
236
|
send(cmd) {
|
|
235
|
-
this.logger.logEvent({ message: '
|
|
237
|
+
this.logger.logEvent({ message: 'add command to queue', cmd: cmd.logStr, msg: cmd.message, port: this.getPort(), queueSize: this.queue.size() });
|
|
236
238
|
this.queue.enqueue(cmd);
|
|
237
239
|
}
|
|
238
240
|
}
|
|
@@ -37,11 +37,12 @@ export default class KettlerRacerAdapter extends DeviceAdapterBase implements De
|
|
|
37
37
|
private iv;
|
|
38
38
|
private requests;
|
|
39
39
|
private data;
|
|
40
|
-
private
|
|
40
|
+
private internalData;
|
|
41
41
|
private kettlerData;
|
|
42
42
|
private updateBusy;
|
|
43
43
|
private requestBusy;
|
|
44
44
|
private comms;
|
|
45
|
+
private prevDistance;
|
|
45
46
|
constructor(protocol: DeviceProtocol, settings: DeviceSettings);
|
|
46
47
|
isBike(): boolean;
|
|
47
48
|
isPower(): boolean;
|
|
@@ -76,7 +77,7 @@ export default class KettlerRacerAdapter extends DeviceAdapterBase implements De
|
|
|
76
77
|
parseExtendedStatus(data: string): KettlerExtendedBikeData;
|
|
77
78
|
parseStatus(data: string): KettlerBikeData;
|
|
78
79
|
check(): Promise<boolean>;
|
|
79
|
-
start(props?: any): Promise<
|
|
80
|
+
start(props?: any): Promise<IncyclistBikeData>;
|
|
80
81
|
startUpdatePull(): void;
|
|
81
82
|
stop(): Promise<boolean>;
|
|
82
83
|
pause(): Promise<boolean>;
|
|
@@ -91,7 +92,7 @@ export default class KettlerRacerAdapter extends DeviceAdapterBase implements De
|
|
|
91
92
|
sendData(): void;
|
|
92
93
|
refreshRequests(): void;
|
|
93
94
|
processClientRequest(request: any): Promise<unknown>;
|
|
94
|
-
waitForOpened(): Promise<boolean>;
|
|
95
|
+
waitForOpened(retries?: boolean): Promise<boolean>;
|
|
95
96
|
waitForClosed(): Promise<boolean>;
|
|
96
97
|
getSupportedCyclingModes(): any[];
|
|
97
98
|
setCyclingMode(mode: CyclingMode | string, settings?: any): void;
|
|
@@ -220,7 +220,7 @@ class KettlerRacerAdapter extends Device_1.default {
|
|
|
220
220
|
}
|
|
221
221
|
const distance = parseInt(states[3]);
|
|
222
222
|
if (!isNaN(distance)) {
|
|
223
|
-
result.distance = distance;
|
|
223
|
+
result.distance = distance * 100;
|
|
224
224
|
}
|
|
225
225
|
const requestedPower = parseInt(states[4]);
|
|
226
226
|
if (!isNaN(requestedPower)) {
|
|
@@ -228,7 +228,7 @@ class KettlerRacerAdapter extends Device_1.default {
|
|
|
228
228
|
}
|
|
229
229
|
const energy = parseInt(states[5]);
|
|
230
230
|
if (!isNaN(energy)) {
|
|
231
|
-
result.
|
|
231
|
+
result.energy = energy;
|
|
232
232
|
}
|
|
233
233
|
const timeStr = states[6];
|
|
234
234
|
const time = timeStr.split(':');
|
|
@@ -284,47 +284,49 @@ class KettlerRacerAdapter extends Device_1.default {
|
|
|
284
284
|
});
|
|
285
285
|
}
|
|
286
286
|
start(props) {
|
|
287
|
-
this
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
info.checkDone = yield this.check();
|
|
293
|
-
}
|
|
287
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
288
|
+
this.logger.logEvent({ message: 'start()' });
|
|
289
|
+
var info = {};
|
|
290
|
+
yield this.waitForOpened(true);
|
|
291
|
+
return utils_1.runWithRetries(() => __awaiter(this, void 0, void 0, function* () {
|
|
294
292
|
try {
|
|
295
|
-
if (!info.
|
|
296
|
-
info.
|
|
293
|
+
if (!info.checkDone) {
|
|
294
|
+
info.checkDone = yield this.check();
|
|
297
295
|
}
|
|
296
|
+
try {
|
|
297
|
+
if (!info.started) {
|
|
298
|
+
info.started = yield this.startTraining();
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
catch (e) {
|
|
302
|
+
this.logger.logEvent({ message: 'Error', error: e.message });
|
|
303
|
+
}
|
|
304
|
+
try {
|
|
305
|
+
yield this.setPower(100);
|
|
306
|
+
}
|
|
307
|
+
catch (e) {
|
|
308
|
+
this.logger.logEvent({ message: 'Error', error: e.message });
|
|
309
|
+
}
|
|
310
|
+
if (!info.data) {
|
|
311
|
+
yield this.update();
|
|
312
|
+
info.data = this.data;
|
|
313
|
+
}
|
|
314
|
+
return info.data;
|
|
298
315
|
}
|
|
299
|
-
catch (
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
}
|
|
308
|
-
if (!info.data) {
|
|
309
|
-
yield this.update();
|
|
310
|
-
info.data = this.data;
|
|
311
|
-
}
|
|
312
|
-
return info.data;
|
|
313
|
-
}
|
|
314
|
-
catch (err) {
|
|
315
|
-
console.log('~~~ Error', err);
|
|
316
|
-
try {
|
|
317
|
-
yield this.reset();
|
|
318
|
-
}
|
|
319
|
-
catch (e) {
|
|
320
|
-
this.logger.logEvent({ message: 'Error', error: e.message });
|
|
316
|
+
catch (err) {
|
|
317
|
+
try {
|
|
318
|
+
yield this.reset();
|
|
319
|
+
}
|
|
320
|
+
catch (e) {
|
|
321
|
+
this.logger.logEvent({ message: 'Error', error: e.message });
|
|
322
|
+
}
|
|
323
|
+
throw (new Error(`could not start device, reason:${err.message}`));
|
|
321
324
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
return data;
|
|
325
|
+
}), 5, 1000)
|
|
326
|
+
.then((data) => {
|
|
327
|
+
this.startUpdatePull();
|
|
328
|
+
return data;
|
|
329
|
+
});
|
|
328
330
|
});
|
|
329
331
|
}
|
|
330
332
|
startUpdatePull() {
|
|
@@ -395,6 +397,10 @@ class KettlerRacerAdapter extends Device_1.default {
|
|
|
395
397
|
}
|
|
396
398
|
transformData(internalData, bikeData) {
|
|
397
399
|
let data = {};
|
|
400
|
+
const prevDistance = this.prevDistance || 0;
|
|
401
|
+
let distance = internalData.distanceInternal - prevDistance;
|
|
402
|
+
if (distance < 0)
|
|
403
|
+
distance = internalData.distanceInternal < 100 ? internalData.distanceInternal : 0;
|
|
398
404
|
data.heartrate = internalData.heartrate;
|
|
399
405
|
data.timestamp = Date.now();
|
|
400
406
|
data.deviceTime = bikeData.time;
|
|
@@ -402,8 +408,10 @@ class KettlerRacerAdapter extends Device_1.default {
|
|
|
402
408
|
data.speed = internalData.speed;
|
|
403
409
|
data.power = internalData.power;
|
|
404
410
|
data.cadence = internalData.pedalRpm;
|
|
405
|
-
data.distance =
|
|
411
|
+
data.distance = distance;
|
|
406
412
|
data.deviceDistanceCounter = bikeData.distance;
|
|
413
|
+
data.internalDistanceCounter = internalData.distanceInternal;
|
|
414
|
+
this.prevDistance = internalData.distanceInternal;
|
|
407
415
|
}
|
|
408
416
|
if (this.ignoreHrm)
|
|
409
417
|
delete this.data.heartrate;
|
|
@@ -416,13 +424,14 @@ class KettlerRacerAdapter extends Device_1.default {
|
|
|
416
424
|
update() {
|
|
417
425
|
return __awaiter(this, void 0, void 0, function* () {
|
|
418
426
|
this.updateBusy = true;
|
|
419
|
-
this.getStatus()
|
|
427
|
+
return this.getStatus()
|
|
420
428
|
.then((bikeData) => {
|
|
421
429
|
if (bikeData) {
|
|
422
430
|
try {
|
|
423
431
|
this.kettlerData = bikeData;
|
|
424
432
|
let data = this.mapData(bikeData);
|
|
425
433
|
data = this.getCyclingMode().updateData(data);
|
|
434
|
+
this.internalData = data;
|
|
426
435
|
this.data = this.transformData(data, bikeData);
|
|
427
436
|
}
|
|
428
437
|
catch (err) {
|
|
@@ -534,34 +543,38 @@ class KettlerRacerAdapter extends Device_1.default {
|
|
|
534
543
|
resolve(bikeRequest);
|
|
535
544
|
}));
|
|
536
545
|
}
|
|
537
|
-
waitForOpened() {
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
return;
|
|
544
|
-
}
|
|
545
|
-
const cleanup = () => {
|
|
546
|
-
this.comms.removeAllListeners();
|
|
547
|
-
};
|
|
548
|
-
const onOpen = () => {
|
|
549
|
-
resolve(true);
|
|
550
|
-
cleanup();
|
|
551
|
-
};
|
|
552
|
-
const onError = (err) => { reject(err); cleanup(); };
|
|
553
|
-
const onClose = () => { cleanup(); };
|
|
554
|
-
this.comms.on('opened', onOpen);
|
|
555
|
-
this.comms.on('closed', onClose);
|
|
556
|
-
this.comms.on('error', onError);
|
|
557
|
-
this.logger.logEvent({ message: 'opening', port: this.getPort() });
|
|
558
|
-
this.comms.open();
|
|
559
|
-
}
|
|
560
|
-
catch (err) {
|
|
561
|
-
this.logger.logEvent({ message: 'error', fn: 'waitForOpened()', error: err.message || err });
|
|
562
|
-
reject(err);
|
|
546
|
+
waitForOpened(retries = false) {
|
|
547
|
+
const run = (resolve, reject) => {
|
|
548
|
+
try {
|
|
549
|
+
if (this.comms.isConnected()) {
|
|
550
|
+
resolve(true);
|
|
551
|
+
return;
|
|
563
552
|
}
|
|
564
|
-
|
|
553
|
+
const cleanup = () => {
|
|
554
|
+
this.comms.removeAllListeners();
|
|
555
|
+
};
|
|
556
|
+
const onOpen = () => {
|
|
557
|
+
resolve(true);
|
|
558
|
+
cleanup();
|
|
559
|
+
};
|
|
560
|
+
const onError = (err) => { reject(err); cleanup(); };
|
|
561
|
+
const onClose = () => { cleanup(); };
|
|
562
|
+
this.comms.on('opened', onOpen);
|
|
563
|
+
this.comms.on('closed', onClose);
|
|
564
|
+
this.comms.on('error', onError);
|
|
565
|
+
this.logger.logEvent({ message: 'opening', port: this.getPort() });
|
|
566
|
+
this.comms.open();
|
|
567
|
+
}
|
|
568
|
+
catch (err) {
|
|
569
|
+
this.logger.logEvent({ message: 'error', fn: 'waitForOpened()', error: err.message || err });
|
|
570
|
+
reject(err);
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
if (!retries) {
|
|
574
|
+
return new Promise((resolve, reject) => run(resolve, reject));
|
|
575
|
+
}
|
|
576
|
+
return utils_1.runWithRetries(() => {
|
|
577
|
+
return new Promise((resolve, reject) => run(resolve, reject));
|
|
565
578
|
}, 3, 1000);
|
|
566
579
|
}
|
|
567
580
|
waitForClosed() {
|
package/lib/utils.js
CHANGED
|
@@ -17,16 +17,18 @@ function runWithRetries(fn, maxRetries, timeBetween) {
|
|
|
17
17
|
let retries = 0;
|
|
18
18
|
let tLastFailure = undefined;
|
|
19
19
|
let busy = false;
|
|
20
|
-
|
|
20
|
+
let iv = setInterval(() => __awaiter(this, void 0, void 0, function* () {
|
|
21
21
|
const tNow = Date.now();
|
|
22
|
-
if (busy)
|
|
22
|
+
if (busy) {
|
|
23
23
|
return;
|
|
24
|
+
}
|
|
24
25
|
if (tLastFailure === undefined || tNow - tLastFailure > timeBetween) {
|
|
25
26
|
try {
|
|
26
27
|
busy = true;
|
|
27
28
|
const data = yield fn();
|
|
28
|
-
busy = false;
|
|
29
29
|
clearInterval(iv);
|
|
30
|
+
iv = undefined;
|
|
31
|
+
busy = false;
|
|
30
32
|
return resolve(data);
|
|
31
33
|
}
|
|
32
34
|
catch (err) {
|
|
@@ -34,6 +36,7 @@ function runWithRetries(fn, maxRetries, timeBetween) {
|
|
|
34
36
|
retries++;
|
|
35
37
|
if (retries >= maxRetries) {
|
|
36
38
|
clearInterval(iv);
|
|
39
|
+
iv = undefined;
|
|
37
40
|
busy = false;
|
|
38
41
|
return reject(err);
|
|
39
42
|
}
|