incyclist-devices 1.4.91 → 1.4.93
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 +5 -1
- package/lib/Device.js +26 -2
- package/lib/ble/wahoo-kickr.js +2 -2
- package/lib/kettler/ergo-racer/ERGCyclingMode.d.ts +24 -0
- package/lib/kettler/ergo-racer/ERGCyclingMode.js +144 -0
- package/lib/kettler/ergo-racer/adapter.d.ts +1 -4
- package/lib/kettler/ergo-racer/adapter.js +4 -26
- package/package.json +1 -1
package/lib/Device.d.ts
CHANGED
|
@@ -55,7 +55,8 @@ export default class IncyclistDevice implements DeviceAdapter {
|
|
|
55
55
|
detected: boolean;
|
|
56
56
|
selected: boolean;
|
|
57
57
|
onDataFn: OnDeviceDataCallback;
|
|
58
|
-
|
|
58
|
+
settings: any;
|
|
59
|
+
constructor(proto: DeviceProtocol, settings?: any);
|
|
59
60
|
isBike(): boolean;
|
|
60
61
|
isPower(): boolean;
|
|
61
62
|
isHrm(): boolean;
|
|
@@ -66,7 +67,10 @@ export default class IncyclistDevice implements DeviceAdapter {
|
|
|
66
67
|
getPort(): string;
|
|
67
68
|
getProtocol(): DeviceProtocol;
|
|
68
69
|
getProtocolName(): string | undefined;
|
|
70
|
+
getSupportedCyclingModes(): any[];
|
|
71
|
+
getDefaultCyclingMode(): CyclingMode;
|
|
69
72
|
setCyclingMode(mode: CyclingMode | string, settings?: any): void;
|
|
73
|
+
getCyclingMode(): CyclingMode;
|
|
70
74
|
setIgnoreHrm(ignore: any): void;
|
|
71
75
|
setIgnorePower(ignore: any): void;
|
|
72
76
|
setIgnoreBike(ignore: any): void;
|
package/lib/Device.js
CHANGED
|
@@ -4,11 +4,12 @@ exports.DEFAULT_USER_WEIGHT = exports.DEFAULT_BIKE_WEIGHT = void 0;
|
|
|
4
4
|
exports.DEFAULT_BIKE_WEIGHT = 10;
|
|
5
5
|
exports.DEFAULT_USER_WEIGHT = 75;
|
|
6
6
|
class IncyclistDevice {
|
|
7
|
-
constructor(proto) {
|
|
7
|
+
constructor(proto, settings) {
|
|
8
8
|
this.protocol = proto;
|
|
9
9
|
this.detected = false;
|
|
10
10
|
this.selected = false;
|
|
11
11
|
this.onDataFn = undefined;
|
|
12
|
+
this.settings = settings;
|
|
12
13
|
}
|
|
13
14
|
isBike() { throw new Error('not implemented'); }
|
|
14
15
|
isPower() { throw new Error('not implemented'); }
|
|
@@ -22,7 +23,30 @@ class IncyclistDevice {
|
|
|
22
23
|
getProtocolName() {
|
|
23
24
|
return this.protocol ? this.protocol.getName() : undefined;
|
|
24
25
|
}
|
|
25
|
-
|
|
26
|
+
getSupportedCyclingModes() { throw new Error('not implemented'); }
|
|
27
|
+
getDefaultCyclingMode() { throw new Error('not implemented'); }
|
|
28
|
+
setCyclingMode(mode, settings) {
|
|
29
|
+
let selectedMode;
|
|
30
|
+
if (typeof mode === 'string') {
|
|
31
|
+
const supported = this.getSupportedCyclingModes();
|
|
32
|
+
const CyclingModeClass = supported.find(M => { const m = new M(this); return m.getName() === mode; });
|
|
33
|
+
if (CyclingModeClass) {
|
|
34
|
+
this.settings.cyclingMode = new CyclingModeClass(this, settings);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
selectedMode = this.getDefaultCyclingMode();
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
selectedMode = mode;
|
|
41
|
+
}
|
|
42
|
+
this.settings.cyclingMode = selectedMode;
|
|
43
|
+
this.settings.cyclingMode.setSettings(settings);
|
|
44
|
+
}
|
|
45
|
+
getCyclingMode() {
|
|
46
|
+
if (!this.settings.cyclingMode)
|
|
47
|
+
this.setCyclingMode(this.getDefaultCyclingMode());
|
|
48
|
+
return this.settings.cyclingMode;
|
|
49
|
+
}
|
|
26
50
|
setIgnoreHrm(ignore) { }
|
|
27
51
|
setIgnorePower(ignore) { }
|
|
28
52
|
setIgnoreBike(ignore) { }
|
package/lib/ble/wahoo-kickr.js
CHANGED
|
@@ -345,9 +345,9 @@ class WahooAdvancedFitnessMachineDevice extends fm_1.default {
|
|
|
345
345
|
s = -100;
|
|
346
346
|
if (s > 100)
|
|
347
347
|
s = 100;
|
|
348
|
-
const slopeVal = Math.min(Math.
|
|
348
|
+
const slopeVal = Math.min(Math.round((1 + s / 100) * 65535 / 2.0), 65535);
|
|
349
349
|
const data = Buffer.alloc(2);
|
|
350
|
-
data.
|
|
350
|
+
data.writeUInt16LE(slopeVal, 0);
|
|
351
351
|
const res = yield this.writeWahooFtmsMessage(70, data);
|
|
352
352
|
return res;
|
|
353
353
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import CyclingMode, { CyclingModeProperty, IncyclistBikeData, UpdateRequest } from '../../CyclingMode';
|
|
2
|
+
import KettlerAdapter from "./adapter";
|
|
3
|
+
import PowerBasedCyclingModeBase from '../../modes/power-base';
|
|
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: KettlerAdapter, 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,144 @@
|
|
|
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 MIN_SPEED = 10;
|
|
9
|
+
const config = {
|
|
10
|
+
name: "ERG",
|
|
11
|
+
description: "Calculates speed based on power and slope. Power is either set by a workout",
|
|
12
|
+
properties: [
|
|
13
|
+
{ key: 'bikeType', name: 'Bike Type', description: '', type: CyclingMode_1.CyclingModeProperyType.SingleSelect, options: ['Race', 'Mountain', 'Triathlon'], default: 'Race' },
|
|
14
|
+
{ 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 },
|
|
15
|
+
]
|
|
16
|
+
};
|
|
17
|
+
class ERGCyclingMode extends power_base_1.default {
|
|
18
|
+
constructor(adapter, props) {
|
|
19
|
+
super(adapter, props);
|
|
20
|
+
this.hasBikeUpdate = false;
|
|
21
|
+
this.event = {};
|
|
22
|
+
this.initLogger('ERGMode');
|
|
23
|
+
}
|
|
24
|
+
getName() {
|
|
25
|
+
return config.name;
|
|
26
|
+
}
|
|
27
|
+
getDescription() {
|
|
28
|
+
return config.description;
|
|
29
|
+
}
|
|
30
|
+
getProperties() {
|
|
31
|
+
return config.properties;
|
|
32
|
+
}
|
|
33
|
+
getProperty(name) {
|
|
34
|
+
return config.properties.find(p => p.name === name);
|
|
35
|
+
}
|
|
36
|
+
getBikeInitRequest() {
|
|
37
|
+
const startPower = this.getSetting('startPower');
|
|
38
|
+
return { targetPower: startPower };
|
|
39
|
+
}
|
|
40
|
+
sendBikeUpdate(request) {
|
|
41
|
+
const getData = () => {
|
|
42
|
+
if (!this.data)
|
|
43
|
+
return {};
|
|
44
|
+
const { pedalRpm, slope, power, speed } = this.data;
|
|
45
|
+
return { pedalRpm, slope, power, speed };
|
|
46
|
+
};
|
|
47
|
+
this.logger.logEvent({ message: "processing update request", request, prev: this.prevRequest, data: getData() });
|
|
48
|
+
let newRequest = {};
|
|
49
|
+
try {
|
|
50
|
+
if (!request || request.reset || Object.keys(request).length === 0) {
|
|
51
|
+
this.prevRequest = {};
|
|
52
|
+
return request.reset ? { reset: true } : {};
|
|
53
|
+
}
|
|
54
|
+
const prevData = this.data || {};
|
|
55
|
+
if (request.targetPower !== undefined) {
|
|
56
|
+
delete request.slope;
|
|
57
|
+
delete request.refresh;
|
|
58
|
+
}
|
|
59
|
+
if (request.refresh) {
|
|
60
|
+
delete request.refresh;
|
|
61
|
+
if (this.prevRequest)
|
|
62
|
+
newRequest.targetPower = this.prevRequest.targetPower;
|
|
63
|
+
else {
|
|
64
|
+
this.prevRequest = this.getBikeInitRequest();
|
|
65
|
+
newRequest.targetPower = this.prevRequest.targetPower;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (request.slope !== undefined) {
|
|
69
|
+
if (!this.data)
|
|
70
|
+
this.data = {};
|
|
71
|
+
this.data.slope = request.slope;
|
|
72
|
+
delete request.slope;
|
|
73
|
+
}
|
|
74
|
+
if (request.maxPower !== undefined && request.minPower !== undefined && request.maxPower === request.minPower) {
|
|
75
|
+
request.targetPower = request.maxPower;
|
|
76
|
+
newRequest.targetPower = request.targetPower;
|
|
77
|
+
}
|
|
78
|
+
if (request.maxPower !== undefined) {
|
|
79
|
+
if (newRequest.targetPower !== undefined && newRequest.targetPower > request.maxPower) {
|
|
80
|
+
newRequest.targetPower = request.maxPower;
|
|
81
|
+
}
|
|
82
|
+
newRequest.maxPower = request.maxPower;
|
|
83
|
+
}
|
|
84
|
+
if (request.minPower !== undefined) {
|
|
85
|
+
if (newRequest.targetPower !== undefined && newRequest.targetPower < request.minPower) {
|
|
86
|
+
newRequest.targetPower = request.minPower;
|
|
87
|
+
}
|
|
88
|
+
newRequest.minPower = request.minPower;
|
|
89
|
+
if (prevData.power && prevData.power < request.minPower)
|
|
90
|
+
newRequest.targetPower = request.minPower;
|
|
91
|
+
}
|
|
92
|
+
if (!newRequest.targetPower && newRequest.maxPower && prevData.power > newRequest.maxPower) {
|
|
93
|
+
newRequest.targetPower = newRequest.maxPower;
|
|
94
|
+
}
|
|
95
|
+
if (!newRequest.targetPower && newRequest.minPower && prevData.power < newRequest.minPower) {
|
|
96
|
+
newRequest.targetPower = newRequest.minPower;
|
|
97
|
+
}
|
|
98
|
+
this.prevRequest = JSON.parse(JSON.stringify(request));
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
this.logger.logEvent({ message: "error", fn: 'sendBikeUpdate()', error: err.message || err, stack: err.stack });
|
|
102
|
+
}
|
|
103
|
+
return newRequest;
|
|
104
|
+
}
|
|
105
|
+
updateData(bikeData) {
|
|
106
|
+
const prevData = JSON.parse(JSON.stringify(this.data || {}));
|
|
107
|
+
const prevSpeed = prevData.speed;
|
|
108
|
+
const prevRequest = this.prevRequest || {};
|
|
109
|
+
const data = this.data || {};
|
|
110
|
+
const bikeType = this.getSetting('bikeType').toLowerCase();
|
|
111
|
+
try {
|
|
112
|
+
let power = bikeData.power || 0;
|
|
113
|
+
const slope = (prevData.slope !== undefined ? prevData.slope : prevRequest.slope || 0);
|
|
114
|
+
const distanceInternal = prevData.distanceInternal || 0;
|
|
115
|
+
const m = this.getWeight();
|
|
116
|
+
const t = this.getTimeSinceLastUpdate();
|
|
117
|
+
const { speed, distance } = this.calculateSpeedAndDistance(power, slope, m, t, { bikeType });
|
|
118
|
+
if (power === 0 && speed < MIN_SPEED) {
|
|
119
|
+
data.speed = Math.round(prevData.speed - 1) < 0 ? 0 : Math.round(prevData.speed - 1);
|
|
120
|
+
data.distanceInternal = distanceInternal + data.speed / 3.6 * t;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
data.speed = (power === 0 && speed < MIN_SPEED) ? 0 : speed;
|
|
124
|
+
data.distanceInternal = (power === 0 && speed < MIN_SPEED) ? distanceInternal : distanceInternal + distance;
|
|
125
|
+
}
|
|
126
|
+
data.power = Math.round(power);
|
|
127
|
+
data.slope = slope;
|
|
128
|
+
data.pedalRpm = bikeData.pedalRpm || 0;
|
|
129
|
+
if (data.time !== undefined && data.speed > 0)
|
|
130
|
+
data.time += t;
|
|
131
|
+
else
|
|
132
|
+
data.time = 0;
|
|
133
|
+
data.heartrate = bikeData.heartrate;
|
|
134
|
+
data.isPedalling = bikeData.isPedalling;
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
this.logger.logEvent({ message: 'error', fn: 'updateData()', error: err.message || err });
|
|
138
|
+
}
|
|
139
|
+
this.logger.logEvent({ message: "updateData result", data, bikeData, prevRequest, prevSpeed });
|
|
140
|
+
this.data = data;
|
|
141
|
+
return data;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
exports.default = ERGCyclingMode;
|
|
@@ -28,7 +28,6 @@ export interface KettlerDeviceSettings extends DeviceSettings {
|
|
|
28
28
|
}
|
|
29
29
|
export default class KettlerRacerAdapter extends DeviceAdapterBase implements DeviceAdapter, Bike {
|
|
30
30
|
private id;
|
|
31
|
-
private settings;
|
|
32
31
|
private ignoreHrm;
|
|
33
32
|
private ignoreBike;
|
|
34
33
|
private ignorePower;
|
|
@@ -43,7 +42,7 @@ export default class KettlerRacerAdapter extends DeviceAdapterBase implements De
|
|
|
43
42
|
private requestBusy;
|
|
44
43
|
private comms;
|
|
45
44
|
private prevDistance;
|
|
46
|
-
constructor(protocol: DeviceProtocol, settings:
|
|
45
|
+
constructor(protocol: DeviceProtocol, settings: KettlerDeviceSettings);
|
|
47
46
|
isBike(): boolean;
|
|
48
47
|
isPower(): boolean;
|
|
49
48
|
isHrm(): boolean;
|
|
@@ -96,8 +95,6 @@ export default class KettlerRacerAdapter extends DeviceAdapterBase implements De
|
|
|
96
95
|
waitForOpened(retries?: boolean): Promise<boolean>;
|
|
97
96
|
waitForClosed(): Promise<boolean>;
|
|
98
97
|
getSupportedCyclingModes(): any[];
|
|
99
|
-
setCyclingMode(mode: CyclingMode | string, settings?: any): void;
|
|
100
|
-
getCyclingMode(): CyclingMode;
|
|
101
98
|
getDefaultCyclingMode(): CyclingMode;
|
|
102
99
|
setUserSettings(userSettings: any): void;
|
|
103
100
|
setBikeSettings(bikeSettings: any): void;
|
|
@@ -36,12 +36,12 @@ const gd_eventlog_1 = require("gd-eventlog");
|
|
|
36
36
|
const comms_1 = __importDefault(require("../comms"));
|
|
37
37
|
const utils_1 = require("../../utils");
|
|
38
38
|
const power_meter_1 = __importDefault(require("../../modes/power-meter"));
|
|
39
|
+
const ERGCyclingMode_1 = __importDefault(require("./ERGCyclingMode"));
|
|
39
40
|
class KettlerRacerAdapter extends Device_1.default {
|
|
40
41
|
constructor(protocol, settings) {
|
|
41
|
-
super(protocol);
|
|
42
|
+
super(protocol, settings);
|
|
42
43
|
this.requests = [];
|
|
43
44
|
this.logger = new gd_eventlog_1.EventLogger('KettlerRacer');
|
|
44
|
-
this.settings = settings;
|
|
45
45
|
this.ignoreHrm = false;
|
|
46
46
|
this.ignorePower = false;
|
|
47
47
|
this.ignoreBike = false;
|
|
@@ -624,32 +624,10 @@ class KettlerRacerAdapter extends Device_1.default {
|
|
|
624
624
|
});
|
|
625
625
|
}
|
|
626
626
|
getSupportedCyclingModes() {
|
|
627
|
-
return [power_meter_1.default];
|
|
628
|
-
}
|
|
629
|
-
setCyclingMode(mode, settings) {
|
|
630
|
-
let selectedMode;
|
|
631
|
-
if (typeof mode === 'string') {
|
|
632
|
-
const supported = this.getSupportedCyclingModes();
|
|
633
|
-
const CyclingModeClass = supported.find(M => { const m = new M(this); return m.getName() === mode; });
|
|
634
|
-
if (CyclingModeClass) {
|
|
635
|
-
this.settings.cyclingMode = new CyclingModeClass(this, settings);
|
|
636
|
-
return;
|
|
637
|
-
}
|
|
638
|
-
selectedMode = this.getDefaultCyclingMode();
|
|
639
|
-
}
|
|
640
|
-
else {
|
|
641
|
-
selectedMode = mode;
|
|
642
|
-
}
|
|
643
|
-
this.settings.cyclingMode = selectedMode;
|
|
644
|
-
this.settings.cyclingMode.setSettings(settings);
|
|
645
|
-
}
|
|
646
|
-
getCyclingMode() {
|
|
647
|
-
if (!this.settings.cyclingMode)
|
|
648
|
-
this.setCyclingMode(this.getDefaultCyclingMode());
|
|
649
|
-
return this.settings.cyclingMode;
|
|
627
|
+
return [power_meter_1.default, ERGCyclingMode_1.default];
|
|
650
628
|
}
|
|
651
629
|
getDefaultCyclingMode() {
|
|
652
|
-
return new
|
|
630
|
+
return new ERGCyclingMode_1.default(this);
|
|
653
631
|
}
|
|
654
632
|
setUserSettings(userSettings) {
|
|
655
633
|
this.settings.userSettings = userSettings;
|