incyclist-devices 2.3.29 → 2.3.32
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/antv2/fe/adapter.js +3 -1
- package/lib/base/adpater.d.ts +1 -0
- package/lib/base/adpater.js +3 -0
- package/lib/ble/base/interface.d.ts +1 -0
- package/lib/ble/base/interface.js +18 -0
- package/lib/ble/fm/adapter.js +4 -2
- package/lib/ble/zwift/play/sensor.d.ts +1 -1
- package/lib/modes/antble-smarttrainer.d.ts +23 -3
- package/lib/modes/antble-smarttrainer.js +230 -3
- package/lib/modes/daum-classic-standard.d.ts +2 -1
- package/lib/modes/daum-classic-standard.js +6 -0
- package/lib/modes/types.d.ts +1 -0
- package/lib/serial/daum/DaumAdapter.d.ts +1 -0
- package/lib/serial/daum/DaumAdapter.js +14 -0
- package/lib/types/adapter.d.ts +1 -0
- package/lib/types/data.d.ts +2 -0
- package/lib/utils/calculations.d.ts +1 -0
- package/lib/utils/calculations.js +13 -1
- package/lib/utils/utils.js +33 -26
- package/package.json +22 -10
package/lib/antv2/fe/adapter.js
CHANGED
|
@@ -61,7 +61,8 @@ class AntFEAdapter extends adapter_1.default {
|
|
|
61
61
|
this.promiseSendUpdate = fe.sendTrackResistance(update.slope);
|
|
62
62
|
}
|
|
63
63
|
if (update.targetPower !== undefined) {
|
|
64
|
-
|
|
64
|
+
const tp = update.targetPower > 0 ? update.targetPower : 0;
|
|
65
|
+
this.promiseSendUpdate = fe.sendTargetPower(tp);
|
|
65
66
|
}
|
|
66
67
|
yield this.promiseSendUpdate;
|
|
67
68
|
delete this.promiseSendUpdate;
|
|
@@ -112,6 +113,7 @@ class AntFEAdapter extends adapter_1.default {
|
|
|
112
113
|
power: adapterData.power,
|
|
113
114
|
speed: adapterData.speed,
|
|
114
115
|
cadence: adapterData.pedalRpm,
|
|
116
|
+
gearStr: adapterData.gearStr,
|
|
115
117
|
timestamp: Date.now()
|
|
116
118
|
});
|
|
117
119
|
if (adapterData.distanceInternal !== undefined) {
|
package/lib/base/adpater.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export default class IncyclistDevice<P extends DeviceProperties> extends EventEm
|
|
|
19
19
|
protected user: User;
|
|
20
20
|
protected data: IncyclistAdapterData;
|
|
21
21
|
constructor(settings: DeviceSettings, props?: P);
|
|
22
|
+
supportsVirtualShifting(): boolean;
|
|
22
23
|
getLogger(): EventLogger;
|
|
23
24
|
isDebugEnabled(): boolean;
|
|
24
25
|
logEvent(event: any): void;
|
package/lib/base/adpater.js
CHANGED
|
@@ -31,6 +31,9 @@ class IncyclistDevice extends events_1.default {
|
|
|
31
31
|
this.data = {};
|
|
32
32
|
this.cyclingMode = this.getDefaultCyclingMode();
|
|
33
33
|
}
|
|
34
|
+
supportsVirtualShifting() {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
34
37
|
getLogger() { return this.logger; }
|
|
35
38
|
isDebugEnabled() {
|
|
36
39
|
const w = global.window;
|
|
@@ -105,6 +105,7 @@ export declare class BleInterface extends EventEmitter implements IBleInterface<
|
|
|
105
105
|
protected getExpectedServices(): string[];
|
|
106
106
|
logEvent(event: any): void;
|
|
107
107
|
logError(err: Error, fn: string, args?: any): void;
|
|
108
|
+
protected reset(): Promise<void>;
|
|
108
109
|
}
|
|
109
110
|
export declare class BleInterfaceFactory extends InterfaceFactory {
|
|
110
111
|
protected iface: BleInterface;
|
|
@@ -675,6 +675,24 @@ class BleInterface extends events_1.default {
|
|
|
675
675
|
const logInfo = args || {};
|
|
676
676
|
this.logEvent(Object.assign(Object.assign({ message: 'Error', fn }, logInfo), { error: err.message, stack: err.stack }));
|
|
677
677
|
}
|
|
678
|
+
reset() {
|
|
679
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
680
|
+
if (this.connectTask) {
|
|
681
|
+
yield this.connectTask.stop();
|
|
682
|
+
}
|
|
683
|
+
if (this.disconnectTask) {
|
|
684
|
+
yield this.disconnectTask.stop();
|
|
685
|
+
}
|
|
686
|
+
if (this.scanTask) {
|
|
687
|
+
yield this.scanTask.stop();
|
|
688
|
+
}
|
|
689
|
+
if (this.discoverTask) {
|
|
690
|
+
yield this.discoverTask.stop();
|
|
691
|
+
}
|
|
692
|
+
this.removeAllListeners();
|
|
693
|
+
this.internalEvents.removeAllListeners();
|
|
694
|
+
});
|
|
695
|
+
}
|
|
678
696
|
}
|
|
679
697
|
exports.BleInterface = BleInterface;
|
|
680
698
|
BleInterface.INTERFACE_NAME = 'ble';
|
package/lib/ble/fm/adapter.js
CHANGED
|
@@ -106,7 +106,8 @@ class BleFmAdapter extends adapter_1.default {
|
|
|
106
106
|
cadence: bikeData.pedalRpm !== undefined ? Math.round(bikeData.pedalRpm) : undefined,
|
|
107
107
|
distance,
|
|
108
108
|
heartrate: bikeData.heartrate,
|
|
109
|
-
timestamp: Date.now()
|
|
109
|
+
timestamp: Date.now(),
|
|
110
|
+
gearStr: bikeData.gearStr
|
|
110
111
|
};
|
|
111
112
|
return data;
|
|
112
113
|
}
|
|
@@ -245,7 +246,8 @@ class BleFmAdapter extends adapter_1.default {
|
|
|
245
246
|
yield device.setSlope(update.slope);
|
|
246
247
|
}
|
|
247
248
|
if (update.targetPower !== undefined) {
|
|
248
|
-
|
|
249
|
+
const tp = update.targetPower > 0 ? update.targetPower : 0;
|
|
250
|
+
yield device.setTargetPower(tp);
|
|
249
251
|
}
|
|
250
252
|
}
|
|
251
253
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import ICyclingMode, { CyclingModeProperyType, UpdateRequest } from "./types";
|
|
1
|
+
import ICyclingMode, { CyclingModeConfig, CyclingModeProperyType, UpdateRequest } from "./types";
|
|
2
2
|
import PowerBasedCyclingModeBase from "./power-base";
|
|
3
|
-
import { IAdapter } from "../types";
|
|
3
|
+
import { IAdapter, IncyclistBikeData } from "../types";
|
|
4
|
+
type VirtshiftMode = 'Disabled' | 'SlopeDelta' | 'Adapter' | 'Simulated';
|
|
4
5
|
export default class SmartTrainerCyclingMode extends PowerBasedCyclingModeBase implements ICyclingMode {
|
|
5
6
|
protected static config: {
|
|
6
7
|
name: string;
|
|
@@ -26,10 +27,29 @@ export default class SmartTrainerCyclingMode extends PowerBasedCyclingModeBase i
|
|
|
26
27
|
options?: undefined;
|
|
27
28
|
})[];
|
|
28
29
|
};
|
|
30
|
+
protected gearDelta: number;
|
|
31
|
+
protected gear: number;
|
|
32
|
+
protected tsStart: number;
|
|
33
|
+
protected simPower: number;
|
|
34
|
+
protected simSlope: number;
|
|
35
|
+
protected readonly gearRatios: number[];
|
|
29
36
|
constructor(adapter: IAdapter, props?: any);
|
|
30
37
|
getBikeInitRequest(): UpdateRequest;
|
|
31
38
|
checkForResetOrEmpty(request: UpdateRequest): UpdateRequest | undefined;
|
|
32
|
-
|
|
39
|
+
getConfig(): CyclingModeConfig;
|
|
40
|
+
protected checkSlopeNoShiftig(request: UpdateRequest, newRequest?: UpdateRequest): void;
|
|
41
|
+
protected checkSlopeWithAdapterShifting(request: UpdateRequest, newRequest?: UpdateRequest): void;
|
|
42
|
+
protected getSlopeDelta(): number;
|
|
43
|
+
protected checkSlopeWithSlopeDelta(request: UpdateRequest, newRequest?: UpdateRequest): void;
|
|
44
|
+
protected checkSlopeWithSimulatedShifting(request: UpdateRequest, newRequest?: UpdateRequest): void;
|
|
45
|
+
checkSlope(request: UpdateRequest, newRequest?: UpdateRequest): void;
|
|
46
|
+
checkGearChange(request: UpdateRequest, newRequest?: UpdateRequest): void;
|
|
47
|
+
protected verifySimPower(): void;
|
|
33
48
|
protected checkEmptyRequest(newRequest: UpdateRequest): void;
|
|
49
|
+
protected getVirtualShiftMode(): VirtshiftMode;
|
|
50
|
+
updateData(bikeData: IncyclistBikeData, log?: boolean): IncyclistBikeData;
|
|
34
51
|
sendBikeUpdate(incoming: UpdateRequest): UpdateRequest;
|
|
52
|
+
protected setInitialGear(data: IncyclistBikeData): void;
|
|
53
|
+
protected getGearString(): string;
|
|
35
54
|
}
|
|
55
|
+
export {};
|
|
@@ -1,13 +1,53 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
5
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
39
|
const types_1 = require("./types");
|
|
7
40
|
const power_base_1 = __importDefault(require("./power-base"));
|
|
41
|
+
const calculations_1 = __importStar(require("../utils/calculations"));
|
|
42
|
+
const MIN_POWER = 25;
|
|
8
43
|
class SmartTrainerCyclingMode extends power_base_1.default {
|
|
9
44
|
constructor(adapter, props) {
|
|
10
45
|
super(adapter, props);
|
|
46
|
+
this.gearDelta = 0;
|
|
47
|
+
this.gearRatios = [
|
|
48
|
+
0.75, 0.87, 0.99, 1.11, 1.23, 1.38, 1.53, 1.68, 1.86, 2.04, 2.22, 2.40,
|
|
49
|
+
2.61, 2.82, 3.03, 3.24, 3.49, 3.74, 3.99, 4.24, 4.54, 4.84, 5.14, 5.49
|
|
50
|
+
];
|
|
11
51
|
this.initLogger('SmartTrainerMode');
|
|
12
52
|
}
|
|
13
53
|
getBikeInitRequest() {
|
|
@@ -23,7 +63,20 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
23
63
|
return this.prevRequest;
|
|
24
64
|
}
|
|
25
65
|
}
|
|
26
|
-
|
|
66
|
+
getConfig() {
|
|
67
|
+
const config = super.getConfig();
|
|
68
|
+
let virtshift = config.properties.find(p => p.key === 'virtshift');
|
|
69
|
+
if (!virtshift) {
|
|
70
|
+
virtshift = { key: 'virtshift', name: 'Virtual Shifting', description: 'Enable virtual shifting', type: types_1.CyclingModeProperyType.SingleSelect, options: ['Disabled', 'Enabled', 'Mixed'], default: 'Disabled' };
|
|
71
|
+
config.properties.push(virtshift);
|
|
72
|
+
}
|
|
73
|
+
if (this.adapter.supportsVirtualShifting()) {
|
|
74
|
+
virtshift.default = 'Enabled';
|
|
75
|
+
virtshift.options = ['Disabled', 'Enabled'];
|
|
76
|
+
}
|
|
77
|
+
return config;
|
|
78
|
+
}
|
|
79
|
+
checkSlopeNoShiftig(request, newRequest = {}) {
|
|
27
80
|
if (request.slope !== undefined) {
|
|
28
81
|
const targetSlope = newRequest.slope = parseFloat(request.slope.toFixed(1));
|
|
29
82
|
this.data.slope = newRequest.slope;
|
|
@@ -36,6 +89,118 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
36
89
|
}
|
|
37
90
|
}
|
|
38
91
|
}
|
|
92
|
+
checkSlopeWithAdapterShifting(request, newRequest = {}) {
|
|
93
|
+
return this.checkSlopeNoShiftig(request, newRequest);
|
|
94
|
+
}
|
|
95
|
+
getSlopeDelta() {
|
|
96
|
+
return this.gearDelta * 0.5;
|
|
97
|
+
}
|
|
98
|
+
checkSlopeWithSlopeDelta(request, newRequest = {}) {
|
|
99
|
+
if (request.slope !== undefined) {
|
|
100
|
+
const targetSlope = newRequest.slope = parseFloat(request.slope.toFixed(1));
|
|
101
|
+
this.data.slope = newRequest.slope;
|
|
102
|
+
const requestedSlope = targetSlope + this.getSlopeDelta();
|
|
103
|
+
try {
|
|
104
|
+
const slopeAdj = requestedSlope >= 0 ? this.getSetting('slopeAdj') : this.getSetting('slopeAdjDown');
|
|
105
|
+
newRequest.slope = slopeAdj !== undefined ? requestedSlope * slopeAdj / 100 : requestedSlope;
|
|
106
|
+
}
|
|
107
|
+
catch (_a) {
|
|
108
|
+
console.error('# Error calculating requested slope');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
checkSlopeWithSimulatedShifting(request, newRequest = {}) {
|
|
113
|
+
var _a, _b, _c, _d;
|
|
114
|
+
if (this.gear === undefined) {
|
|
115
|
+
this.checkSlopeNoShiftig(request, newRequest);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (request.slope !== undefined) {
|
|
119
|
+
const prev = (_a = this.data.slope) !== null && _a !== void 0 ? _a : 0;
|
|
120
|
+
this.data.slope = parseFloat(request.slope.toFixed(1));
|
|
121
|
+
delete request.slope;
|
|
122
|
+
delete newRequest.slope;
|
|
123
|
+
this.simSlope = this.data.slope;
|
|
124
|
+
try {
|
|
125
|
+
const slopeAdj = this.simSlope >= 0 ? this.getSetting('slopeAdj') : this.getSetting('slopeAdjDown');
|
|
126
|
+
if (slopeAdj !== undefined)
|
|
127
|
+
this.simSlope = this.simSlope * slopeAdj / 100;
|
|
128
|
+
}
|
|
129
|
+
catch (_e) {
|
|
130
|
+
}
|
|
131
|
+
if (this.data.slope !== prev) {
|
|
132
|
+
const virtualSpeed = (0, calculations_1.calculateVirtualSpeed)(this.data.pedalRpm, this.gearRatios[this.gear - 1]);
|
|
133
|
+
const m = (_c = (_b = this.adapter) === null || _b === void 0 ? void 0 : _b.getWeight()) !== null && _c !== void 0 ? _c : 85;
|
|
134
|
+
this.simPower = calculations_1.default.calculatePower(m, virtualSpeed, (_d = this.simSlope) !== null && _d !== void 0 ? _d : 0);
|
|
135
|
+
this.verifySimPower();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
newRequest.targetPower = this.simPower;
|
|
139
|
+
}
|
|
140
|
+
checkSlope(request, newRequest = {}) {
|
|
141
|
+
const virtshiftMode = this.getVirtualShiftMode();
|
|
142
|
+
switch (virtshiftMode) {
|
|
143
|
+
case 'SlopeDelta':
|
|
144
|
+
this.checkSlopeWithSlopeDelta(request, newRequest);
|
|
145
|
+
break;
|
|
146
|
+
case 'Simulated':
|
|
147
|
+
this.checkSlopeWithSimulatedShifting(request, newRequest);
|
|
148
|
+
break;
|
|
149
|
+
case 'Adapter':
|
|
150
|
+
this.checkSlopeWithAdapterShifting(request, newRequest);
|
|
151
|
+
break;
|
|
152
|
+
case 'Disabled':
|
|
153
|
+
default:
|
|
154
|
+
this.checkSlopeNoShiftig(request, newRequest);
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
checkGearChange(request, newRequest = {}) {
|
|
159
|
+
var _a, _b, _c, _d;
|
|
160
|
+
const virtshiftMode = this.getVirtualShiftMode();
|
|
161
|
+
switch (virtshiftMode) {
|
|
162
|
+
case 'SlopeDelta':
|
|
163
|
+
if (request.gearDelta !== undefined) {
|
|
164
|
+
this.gearDelta += request.gearDelta;
|
|
165
|
+
request.slope = this.data.slope;
|
|
166
|
+
delete request.gearDelta;
|
|
167
|
+
}
|
|
168
|
+
break;
|
|
169
|
+
case 'Simulated':
|
|
170
|
+
if (request.gearDelta !== undefined) {
|
|
171
|
+
if (this.gear === undefined) {
|
|
172
|
+
this.gear = 10 + request.gearDelta;
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
this.gear += request.gearDelta;
|
|
176
|
+
}
|
|
177
|
+
if (this.gear < 1) {
|
|
178
|
+
this.gear = 1;
|
|
179
|
+
}
|
|
180
|
+
if (this.gear > this.gearRatios.length) {
|
|
181
|
+
this.gear = this.gearRatios.length;
|
|
182
|
+
}
|
|
183
|
+
delete request.gearDelta;
|
|
184
|
+
const virtualSpeed = (0, calculations_1.calculateVirtualSpeed)(this.data.pedalRpm, this.gearRatios[this.gear - 1]);
|
|
185
|
+
const m = (_b = (_a = this.adapter) === null || _a === void 0 ? void 0 : _a.getWeight()) !== null && _b !== void 0 ? _b : 85;
|
|
186
|
+
this.simPower = calculations_1.default.calculatePower(m, virtualSpeed, (_d = (_c = this.simSlope) !== null && _c !== void 0 ? _c : this.data.slope) !== null && _d !== void 0 ? _d : 0);
|
|
187
|
+
this.verifySimPower();
|
|
188
|
+
}
|
|
189
|
+
break;
|
|
190
|
+
case 'Adapter':
|
|
191
|
+
case 'Disabled':
|
|
192
|
+
default:
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
verifySimPower() {
|
|
197
|
+
if (this.simPower < 0) {
|
|
198
|
+
this.simPower = 0;
|
|
199
|
+
}
|
|
200
|
+
if (this.data.pedalRpm > 0 && this.simPower < MIN_POWER) {
|
|
201
|
+
this.simPower = MIN_POWER;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
39
204
|
checkEmptyRequest(newRequest) {
|
|
40
205
|
if (Object.keys(newRequest).length === 0) {
|
|
41
206
|
if (this.prevRequest) {
|
|
@@ -44,14 +209,57 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
44
209
|
}
|
|
45
210
|
}
|
|
46
211
|
}
|
|
212
|
+
getVirtualShiftMode() {
|
|
213
|
+
const virtshiftMode = this.getSetting('virtshift');
|
|
214
|
+
if (virtshiftMode === 'Disabled') {
|
|
215
|
+
return 'Disabled';
|
|
216
|
+
}
|
|
217
|
+
if (this.adapter.supportsVirtualShifting()) {
|
|
218
|
+
if (virtshiftMode === 'Enabled') {
|
|
219
|
+
return 'Adapter';
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
return virtshiftMode === 'Mixed' ? 'SlopeDelta' : 'Simulated';
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
updateData(bikeData, log) {
|
|
227
|
+
var _a, _b, _c, _d;
|
|
228
|
+
const prev = Object.assign({}, this.data);
|
|
229
|
+
const data = super.updateData(bikeData, log);
|
|
230
|
+
const mode = this.getVirtualShiftMode();
|
|
231
|
+
let virtualSpeed;
|
|
232
|
+
data.gearStr = this.getGearString();
|
|
233
|
+
if (mode !== 'Simulated') {
|
|
234
|
+
return data;
|
|
235
|
+
}
|
|
236
|
+
if (data.power > 0 && !this.tsStart) {
|
|
237
|
+
this.tsStart = Date.now();
|
|
238
|
+
}
|
|
239
|
+
if (this.gear === undefined && this.tsStart && data.power > 0 && (Date.now() - this.tsStart > 3000)) {
|
|
240
|
+
this.setInitialGear(data);
|
|
241
|
+
data.gearStr = this.getGearString();
|
|
242
|
+
}
|
|
243
|
+
else if (this.gear !== undefined) {
|
|
244
|
+
if (prev.power !== data.power || prev.pedalRpm !== data.pedalRpm) {
|
|
245
|
+
virtualSpeed = (0, calculations_1.calculateVirtualSpeed)(data.pedalRpm, this.gearRatios[this.gear - 1]);
|
|
246
|
+
const m = (_b = (_a = this.adapter) === null || _a === void 0 ? void 0 : _a.getWeight()) !== null && _b !== void 0 ? _b : 85;
|
|
247
|
+
this.simPower = calculations_1.default.calculatePower(m, virtualSpeed, (_d = (_c = this.simSlope) !== null && _c !== void 0 ? _c : data.slope) !== null && _d !== void 0 ? _d : 0);
|
|
248
|
+
this.verifySimPower();
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return data;
|
|
252
|
+
}
|
|
47
253
|
sendBikeUpdate(incoming) {
|
|
48
254
|
this.logger.logEvent({ message: "processing update request", request: incoming, prev: this.prevRequest, data: this.getData() });
|
|
49
255
|
let newRequest = {};
|
|
50
256
|
const request = Object.assign({}, incoming);
|
|
51
257
|
try {
|
|
52
258
|
const req = this.checkForResetOrEmpty(request);
|
|
53
|
-
if (req)
|
|
259
|
+
if (req) {
|
|
54
260
|
return req;
|
|
261
|
+
}
|
|
262
|
+
this.checkGearChange(request, newRequest);
|
|
55
263
|
this.checkSlope(request, newRequest);
|
|
56
264
|
this.checkEmptyRequest(newRequest);
|
|
57
265
|
this.prevRequest = JSON.parse(JSON.stringify(newRequest));
|
|
@@ -62,6 +270,25 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
62
270
|
}
|
|
63
271
|
return newRequest;
|
|
64
272
|
}
|
|
273
|
+
setInitialGear(data) {
|
|
274
|
+
var _a, _b;
|
|
275
|
+
const m = (_b = (_a = this.adapter) === null || _a === void 0 ? void 0 : _a.getWeight()) !== null && _b !== void 0 ? _b : 85;
|
|
276
|
+
const virtualSpeeds = this.gearRatios.map(r => (0, calculations_1.calculateVirtualSpeed)(data.pedalRpm, r));
|
|
277
|
+
const requiredPowers = virtualSpeeds.map(v => { var _a; return calculations_1.default.calculatePower(m, v, (_a = data.slope) !== null && _a !== void 0 ? _a : 0); });
|
|
278
|
+
const deltas = requiredPowers.map((p, i) => ({ gear: i + 1, delta: Math.abs(p - data.power) }));
|
|
279
|
+
deltas.sort((a, b) => a.delta - b.delta);
|
|
280
|
+
this.gear = deltas[0].gear;
|
|
281
|
+
}
|
|
282
|
+
getGearString() {
|
|
283
|
+
const mode = this.getVirtualShiftMode();
|
|
284
|
+
if (mode === "Disabled")
|
|
285
|
+
return undefined;
|
|
286
|
+
if (mode === 'Simulated' && (this.gear === undefined || this.gear === null))
|
|
287
|
+
return '';
|
|
288
|
+
if (mode === "SlopeDelta")
|
|
289
|
+
return this.gearDelta > 0 ? `+${this.gearDelta}` : `${this.gearDelta}`;
|
|
290
|
+
return this.gear.toString();
|
|
291
|
+
}
|
|
65
292
|
}
|
|
66
293
|
SmartTrainerCyclingMode.config = {
|
|
67
294
|
name: "Smart Trainer",
|
|
@@ -70,7 +297,7 @@ SmartTrainerCyclingMode.config = {
|
|
|
70
297
|
properties: [
|
|
71
298
|
{ key: 'bikeType', name: 'Bike Type', description: '', type: types_1.CyclingModeProperyType.SingleSelect, options: ['Race', 'Mountain', 'Triathlon'], default: 'Race' },
|
|
72
299
|
{ key: 'slopeAdj', name: 'Bike Reality Factor', description: 'Percentage of slope that should be sent to the SmartTrainer. Should be used in case the slopes are feeling too hard', type: types_1.CyclingModeProperyType.Integer, default: 100, min: 0, max: 200 },
|
|
73
|
-
{ key: 'slopeAdjDown', name: 'Bike Reality Factor downhill', description: 'Percentage of slope that should be sent during downhill sections. Should be used to avoid spinning out', type: types_1.CyclingModeProperyType.Integer, default: 50, min: 0, max: 100 }
|
|
300
|
+
{ key: 'slopeAdjDown', name: 'Bike Reality Factor downhill', description: 'Percentage of slope that should be sent during downhill sections. Should be used to avoid spinning out', type: types_1.CyclingModeProperyType.Integer, default: 50, min: 0, max: 100 },
|
|
74
301
|
]
|
|
75
302
|
};
|
|
76
303
|
exports.default = SmartTrainerCyclingMode;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import ICyclingMode, { Settings, UpdateRequest } from "./types";
|
|
1
|
+
import ICyclingMode, { CyclingModeConfig, Settings, UpdateRequest } from "./types";
|
|
2
2
|
import { IncyclistBikeData } from "../types";
|
|
3
3
|
import SmartTrainerCyclingMode from "./antble-smarttrainer";
|
|
4
4
|
import { IncyclistDeviceAdapter } from "../base/adpater";
|
|
@@ -11,6 +11,7 @@ export default class DaumClassicCyclingMode extends SmartTrainerCyclingMode impl
|
|
|
11
11
|
event: DaumClassicEvent;
|
|
12
12
|
constructor(adapter: IncyclistDeviceAdapter, props?: Settings);
|
|
13
13
|
getBikeInitRequest(): UpdateRequest;
|
|
14
|
+
getConfig(): CyclingModeConfig;
|
|
14
15
|
checkForResetOrEmpty(request: UpdateRequest): UpdateRequest | undefined;
|
|
15
16
|
protected updateSpeedAndDistance(_power: number, _slope: any, _bikeType: any, data: IncyclistBikeData, prevData: any): number;
|
|
16
17
|
}
|
|
@@ -25,6 +25,12 @@ class DaumClassicCyclingMode extends antble_smarttrainer_1.default {
|
|
|
25
25
|
getBikeInitRequest() {
|
|
26
26
|
return { slope: 0 };
|
|
27
27
|
}
|
|
28
|
+
getConfig() {
|
|
29
|
+
if (this.localConfig)
|
|
30
|
+
return this.localConfig;
|
|
31
|
+
let cm = this.constructor;
|
|
32
|
+
return cm.config;
|
|
33
|
+
}
|
|
28
34
|
checkForResetOrEmpty(request) {
|
|
29
35
|
if (!request || request.reset) {
|
|
30
36
|
this.prevRequest = {};
|
package/lib/modes/types.d.ts
CHANGED
|
@@ -61,5 +61,6 @@ export default class DaumAdapter<S extends SerialDeviceSettings, P extends Devic
|
|
|
61
61
|
sendRequest(request: any): Promise<any>;
|
|
62
62
|
refreshRequests(): void;
|
|
63
63
|
processClientRequest(request: any): Promise<UpdateRequest>;
|
|
64
|
+
protected reset(): void;
|
|
64
65
|
getDeviceInfo(): Promise<any>;
|
|
65
66
|
}
|
|
@@ -529,6 +529,20 @@ class DaumAdapter extends adapter_1.SerialIncyclistDevice {
|
|
|
529
529
|
fn();
|
|
530
530
|
});
|
|
531
531
|
}
|
|
532
|
+
reset() {
|
|
533
|
+
if (this.iv) {
|
|
534
|
+
const { sync, update, emitter } = this.iv;
|
|
535
|
+
if (sync)
|
|
536
|
+
clearInterval(sync);
|
|
537
|
+
if (update)
|
|
538
|
+
clearInterval(update);
|
|
539
|
+
if (emitter)
|
|
540
|
+
emitter.removeAllListeners();
|
|
541
|
+
}
|
|
542
|
+
if (this.internalEmitter) {
|
|
543
|
+
this.internalEmitter.removeAllListeners();
|
|
544
|
+
}
|
|
545
|
+
}
|
|
532
546
|
getDeviceInfo() {
|
|
533
547
|
return __awaiter(this, void 0, void 0, function* () {
|
|
534
548
|
throw new Error('Method not implemented.');
|
package/lib/types/adapter.d.ts
CHANGED
|
@@ -34,6 +34,7 @@ export interface IAdapter extends EventEmitter, IBike, ISensor {
|
|
|
34
34
|
getName(): string;
|
|
35
35
|
getID(): string;
|
|
36
36
|
getUniqueName(): string;
|
|
37
|
+
supportsVirtualShifting(): boolean;
|
|
37
38
|
getDisplayName(): string;
|
|
38
39
|
getSettings(): DeviceSettings;
|
|
39
40
|
isSame(adapter: IAdapter): boolean;
|
package/lib/types/data.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export type IncyclistAdapterData = {
|
|
|
6
6
|
heartrate?: number;
|
|
7
7
|
distance?: number;
|
|
8
8
|
timestamp?: number;
|
|
9
|
+
gearStr?: string;
|
|
9
10
|
deviceTime?: number;
|
|
10
11
|
deviceDistanceCounter?: number;
|
|
11
12
|
internalDistanceCounter?: number;
|
|
@@ -19,5 +20,6 @@ export type IncyclistBikeData = {
|
|
|
19
20
|
distanceInternal?: number;
|
|
20
21
|
time?: number;
|
|
21
22
|
gear?: number;
|
|
23
|
+
gearStr?: string;
|
|
22
24
|
slope?: number;
|
|
23
25
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.IllegalArgumentException = void 0;
|
|
3
|
+
exports.calculateVirtualSpeed = exports.IllegalArgumentException = void 0;
|
|
4
4
|
exports.solveCubic = solveCubic;
|
|
5
5
|
const g = 9.80665;
|
|
6
6
|
const rho = 1.2041;
|
|
@@ -103,6 +103,18 @@ class C {
|
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
exports.default = C;
|
|
106
|
+
const calculateVirtualSpeed = (rpm, gearRatio, wheelCirc = 2125) => {
|
|
107
|
+
if (rpm === undefined || rpm === null || rpm < 0)
|
|
108
|
+
throw new IllegalArgumentException("rpm must be a positive number");
|
|
109
|
+
if (!gearRatio || gearRatio < 0)
|
|
110
|
+
throw new IllegalArgumentException("gearRatio must be a positive number");
|
|
111
|
+
if (!wheelCirc || wheelCirc < 0)
|
|
112
|
+
throw new IllegalArgumentException("wheelCirc must be a positive number");
|
|
113
|
+
let distRotation = wheelCirc * gearRatio / 1000;
|
|
114
|
+
let speed = rpm * distRotation * 60 / 1000;
|
|
115
|
+
return speed / 3.6;
|
|
116
|
+
};
|
|
117
|
+
exports.calculateVirtualSpeed = calculateVirtualSpeed;
|
|
106
118
|
function acosh(x) {
|
|
107
119
|
return Math.log(x + Math.sqrt(x * x - 1.0));
|
|
108
120
|
}
|
package/lib/utils/utils.js
CHANGED
|
@@ -26,39 +26,46 @@ const resolveNextTick = () => {
|
|
|
26
26
|
};
|
|
27
27
|
exports.resolveNextTick = resolveNextTick;
|
|
28
28
|
function runWithRetries(fn, maxRetries, timeBetween) {
|
|
29
|
-
return
|
|
30
|
-
let
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
try {
|
|
40
|
-
busy = true;
|
|
41
|
-
const data = yield fn();
|
|
42
|
-
clearInterval(iv);
|
|
43
|
-
iv = undefined;
|
|
44
|
-
busy = false;
|
|
45
|
-
return resolve(data);
|
|
29
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
30
|
+
let iv;
|
|
31
|
+
const res = yield new Promise((resolve, reject) => {
|
|
32
|
+
let retries = 0;
|
|
33
|
+
let tLastFailure = undefined;
|
|
34
|
+
let busy = false;
|
|
35
|
+
iv = setInterval(() => __awaiter(this, void 0, void 0, function* () {
|
|
36
|
+
const tNow = Date.now();
|
|
37
|
+
if (busy) {
|
|
38
|
+
return;
|
|
46
39
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
40
|
+
if (tLastFailure === undefined || tNow - tLastFailure > timeBetween) {
|
|
41
|
+
try {
|
|
42
|
+
busy = true;
|
|
43
|
+
const data = yield fn();
|
|
51
44
|
clearInterval(iv);
|
|
52
45
|
iv = undefined;
|
|
53
46
|
busy = false;
|
|
54
|
-
return
|
|
47
|
+
return resolve(data);
|
|
55
48
|
}
|
|
56
|
-
|
|
57
|
-
|
|
49
|
+
catch (err) {
|
|
50
|
+
tLastFailure = Date.now();
|
|
51
|
+
retries++;
|
|
52
|
+
if (retries >= maxRetries) {
|
|
53
|
+
clearInterval(iv);
|
|
54
|
+
iv = undefined;
|
|
55
|
+
busy = false;
|
|
56
|
+
return reject(err);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
busy = false;
|
|
60
|
+
}
|
|
58
61
|
}
|
|
59
62
|
}
|
|
60
|
-
}
|
|
61
|
-
})
|
|
63
|
+
}), 50);
|
|
64
|
+
});
|
|
65
|
+
if (iv) {
|
|
66
|
+
clearInterval(iv);
|
|
67
|
+
}
|
|
68
|
+
return res;
|
|
62
69
|
});
|
|
63
70
|
}
|
|
64
71
|
function floatVal(d) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "incyclist-devices",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.32",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"@serialport/bindings-interface": "^1.2.2",
|
|
6
6
|
"@serialport/parser-byte-length": "^9.0.1",
|
|
@@ -16,15 +16,16 @@
|
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"@serialport/binding-mock": "^10.2.2",
|
|
18
18
|
"@serialport/bindings-cpp": "^10.8.0",
|
|
19
|
-
"@stoprocent/noble": "^
|
|
20
|
-
"@types/jest": "^
|
|
21
|
-
"@types/node": "^
|
|
19
|
+
"@stoprocent/noble": "^2.3.5",
|
|
20
|
+
"@types/jest": "^30.0.0",
|
|
21
|
+
"@types/node": "^24.3.1",
|
|
22
|
+
"@typescript-eslint/eslint-plugin": "^8.43.0",
|
|
23
|
+
"@typescript-eslint/parser": "^8.43.0",
|
|
22
24
|
"bonjour-service": "^1.3.0",
|
|
23
|
-
"eslint": "^
|
|
24
|
-
"
|
|
25
|
-
"jest": "^29.
|
|
26
|
-
"
|
|
27
|
-
"typescript": "^5.8.3"
|
|
25
|
+
"eslint": "^9.35.0",
|
|
26
|
+
"jest": "^30.1.3",
|
|
27
|
+
"ts-jest": "^29.4.1",
|
|
28
|
+
"typescript": "^5.9.2"
|
|
28
29
|
},
|
|
29
30
|
"scripts": {
|
|
30
31
|
"lint": "eslint . --ext .ts",
|
|
@@ -43,7 +44,18 @@
|
|
|
43
44
|
"lib": "./src"
|
|
44
45
|
},
|
|
45
46
|
"eslintConfig": {
|
|
46
|
-
"extends":
|
|
47
|
+
"extends": [
|
|
48
|
+
"eslint:recommended",
|
|
49
|
+
"plugin:@typescript-eslint/recommended"
|
|
50
|
+
],
|
|
51
|
+
"parser": "@typescript-eslint/parser",
|
|
52
|
+
"parserOptions": {
|
|
53
|
+
"ecmaVersion": "latest",
|
|
54
|
+
"sourceType": "module"
|
|
55
|
+
},
|
|
56
|
+
"plugins": [
|
|
57
|
+
"@typescript-eslint"
|
|
58
|
+
],
|
|
47
59
|
"rules": {
|
|
48
60
|
"jsx-a11y/anchor-is-valid": [
|
|
49
61
|
"off"
|