incyclist-services 1.7.94 → 1.7.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/cjs/activities/upload/index.js +0 -1
- package/lib/cjs/apps/index.js +0 -1
- package/lib/cjs/apps/service.js +1 -3
- package/lib/cjs/devices/page/service.js +9 -3
- package/lib/cjs/devices/page/statemachine.js +14 -4
- package/lib/cjs/devices/ride/cyclingMode.js +29 -0
- package/lib/cjs/devices/ride/index.js +1 -0
- package/lib/cjs/ride/display/service.js +7 -13
- package/lib/cjs/ride/workout/WorkoutDisplayService.js +6 -2
- package/lib/cjs/workouts/ride/page/service.js +13 -2
- package/lib/cjs/workouts/ride/service.js +80 -11
- package/lib/esm/activities/upload/index.js +0 -1
- package/lib/esm/apps/index.js +0 -1
- package/lib/esm/apps/service.js +1 -3
- package/lib/esm/devices/page/service.js +9 -3
- package/lib/esm/devices/page/statemachine.js +14 -4
- package/lib/esm/devices/ride/cyclingMode.js +25 -0
- package/lib/esm/devices/ride/index.js +1 -0
- package/lib/esm/ride/display/service.js +8 -14
- package/lib/esm/ride/workout/WorkoutDisplayService.js +6 -2
- package/lib/esm/workouts/ride/page/service.js +13 -2
- package/lib/esm/workouts/ride/service.js +81 -12
- package/lib/types/activities/upload/index.d.ts +0 -1
- package/lib/types/apps/index.d.ts +0 -1
- package/lib/types/devices/page/statemachine.d.ts +2 -0
- package/lib/types/devices/ride/cyclingMode.d.ts +4 -0
- package/lib/types/devices/ride/index.d.ts +1 -0
- package/lib/types/workouts/ride/page/service.d.ts +2 -1
- package/lib/types/workouts/ride/page/types.d.ts +3 -0
- package/lib/types/workouts/ride/service.d.ts +10 -1
- package/lib/types/workouts/ride/types.d.ts +10 -1
- package/package.json +1 -1
|
@@ -14,7 +14,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./velohero"), exports);
|
|
18
17
|
__exportStar(require("./strava"), exports);
|
|
19
18
|
__exportStar(require("./intervals"), exports);
|
|
20
19
|
__exportStar(require("./types"), exports);
|
package/lib/cjs/apps/index.js
CHANGED
package/lib/cjs/apps/service.js
CHANGED
|
@@ -43,7 +43,6 @@ const settings_1 = require("../settings");
|
|
|
43
43
|
const base_1 = require("./base");
|
|
44
44
|
const IntervalsAppConnection_1 = require("./intervals/IntervalsAppConnection");
|
|
45
45
|
const komoot_1 = require("./komoot");
|
|
46
|
-
const VeloHeroAppConnection_1 = require("./velohero/VeloHeroAppConnection");
|
|
47
46
|
let AppsService = (() => {
|
|
48
47
|
let _classDecorators = [types_1.Singleton];
|
|
49
48
|
let _classDescriptor;
|
|
@@ -67,7 +66,7 @@ let AppsService = (() => {
|
|
|
67
66
|
__runInitializers(_classThis, _classExtraInitializers);
|
|
68
67
|
}
|
|
69
68
|
services = (__runInitializers(this, _instanceExtraInitializers), {
|
|
70
|
-
ActivityUpload: ['strava', '
|
|
69
|
+
ActivityUpload: ['strava', 'intervals'],
|
|
71
70
|
WorkoutUpload: [],
|
|
72
71
|
WorkoutDownload: ['intervals'],
|
|
73
72
|
ActivityDownload: [],
|
|
@@ -75,7 +74,6 @@ let AppsService = (() => {
|
|
|
75
74
|
});
|
|
76
75
|
serviceMap = [
|
|
77
76
|
{ name: 'Strava', key: 'strava', iconUrl: 'https://static.cdnlogo.com/logos/s/42/strava-wordmark.svg', connection: new base_1.StravaAppConnection() },
|
|
78
|
-
{ name: 'VeloHero', key: 'velohero', iconUrl: 'images/velo-white.png', connection: new VeloHeroAppConnection_1.VeloHeroAppConnection() },
|
|
79
77
|
{ name: 'Komoot', key: 'komoot', iconUrl: 'https://www.komoot.com/assets/4d8ae313eec53e6e.svg', connection: new komoot_1.KomootAppConnection() },
|
|
80
78
|
{ name: 'Intervals.icu', key: 'intervals', iconUrl: 'images/intervals.png', connection: new IntervalsAppConnection_1.IntervalsAppConnection() },
|
|
81
79
|
];
|
|
@@ -95,9 +95,15 @@ let DevicesPageService = (() => {
|
|
|
95
95
|
if (this.promiseOpen !== undefined) {
|
|
96
96
|
return this.getPageObserver();
|
|
97
97
|
}
|
|
98
|
-
|
|
98
|
+
const onStateMachineUpdate = () => {
|
|
99
99
|
this.getPageObserver().emit('page-update');
|
|
100
|
-
}
|
|
100
|
+
};
|
|
101
|
+
this.stateMachine.start(onStateMachineUpdate);
|
|
102
|
+
if (this.stateMachine.state !== 'Idle') {
|
|
103
|
+
this.logEvent({ message: 'state machine was not in expected state on open, resetting', page: 'Pairing', state: this.stateMachine.state });
|
|
104
|
+
this.stateMachine.stop();
|
|
105
|
+
this.stateMachine.start(onStateMachineUpdate);
|
|
106
|
+
}
|
|
101
107
|
this.promiseOpen = new Promise((done) => {
|
|
102
108
|
this.start()
|
|
103
109
|
.catch((err) => { this.logError(err, 'openPage'); })
|
|
@@ -321,7 +327,7 @@ let DevicesPageService = (() => {
|
|
|
321
327
|
this.updatePage();
|
|
322
328
|
}
|
|
323
329
|
async closeDeviceSelection(enabled) {
|
|
324
|
-
this.logEvent({ message: 'capability closed', capability: this.openedCapability });
|
|
330
|
+
this.logEvent({ message: 'capability closed', capability: this.openedCapability, enabled, caller: new Error().stack });
|
|
325
331
|
if (!enabled) {
|
|
326
332
|
const all = this.state.capabilities ?? [];
|
|
327
333
|
const requested = all.find(c => c.capability === this.openedCapability);
|
|
@@ -68,6 +68,8 @@ let PairingPageStateMachine = (() => {
|
|
|
68
68
|
selectStateChangeCallback;
|
|
69
69
|
retryTimeout;
|
|
70
70
|
selectionTimeout;
|
|
71
|
+
pairingPromise;
|
|
72
|
+
scanningPromise;
|
|
71
73
|
constructor() {
|
|
72
74
|
this.logger = new gd_eventlog_1.EventLogger('PairingPageState');
|
|
73
75
|
}
|
|
@@ -99,6 +101,8 @@ let PairingPageStateMachine = (() => {
|
|
|
99
101
|
this.unregisterServiceEventHandlers();
|
|
100
102
|
delete this.stateChangeCallback;
|
|
101
103
|
this.resetTimeouts();
|
|
104
|
+
delete this.pairingPromise;
|
|
105
|
+
delete this.scanningPromise;
|
|
102
106
|
this.setState('Closed');
|
|
103
107
|
this.logIncomingEvent('stop', prev, 'Closed');
|
|
104
108
|
}
|
|
@@ -199,14 +203,20 @@ let PairingPageStateMachine = (() => {
|
|
|
199
203
|
this.resetTimeouts();
|
|
200
204
|
this.setState('Done');
|
|
201
205
|
}
|
|
202
|
-
else {
|
|
206
|
+
else if (!this.pairingPromise) {
|
|
203
207
|
this.setState('Pairing');
|
|
204
|
-
service.startPairing(adapters, {})
|
|
208
|
+
this.pairingPromise = Promise.resolve(service.startPairing(adapters, {}))
|
|
209
|
+
.catch(err => { this.logError(err, 'startPairing'); })
|
|
210
|
+
.finally(() => { delete this.pairingPromise; });
|
|
205
211
|
}
|
|
206
212
|
}
|
|
207
213
|
else if (this.selectState === 'Closed' || this.selectState === 'Active') {
|
|
208
|
-
this.
|
|
209
|
-
|
|
214
|
+
if (!this.scanningPromise) {
|
|
215
|
+
this.setState('Scanning');
|
|
216
|
+
this.scanningPromise = Promise.resolve(service.startScanning(adapters, {}))
|
|
217
|
+
.catch(err => { this.logError(err, 'startScanning'); })
|
|
218
|
+
.finally(() => { delete this.scanningPromise; });
|
|
219
|
+
}
|
|
210
220
|
}
|
|
211
221
|
if (this.state !== oldState)
|
|
212
222
|
this.logger.logEvent({ message: 'state changed', stateTransition: { from: oldState, to: this.state } });
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isVirtualShiftingEnabled = isVirtualShiftingEnabled;
|
|
4
|
+
exports.getLoadButtonMode = getLoadButtonMode;
|
|
5
|
+
function isVirtualShiftingEnabled(mode) {
|
|
6
|
+
try {
|
|
7
|
+
if (!mode)
|
|
8
|
+
return false;
|
|
9
|
+
if (mode.isSIM()) {
|
|
10
|
+
const virtshiftMode = mode.getSetting('virtshift');
|
|
11
|
+
return (virtshiftMode === 'Mixed' || virtshiftMode === 'Incyclist' || virtshiftMode === 'SmartTrainer' || virtshiftMode === 'Enabled');
|
|
12
|
+
}
|
|
13
|
+
if (mode.isResistance()) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function getLoadButtonMode(mode) {
|
|
23
|
+
if (!mode)
|
|
24
|
+
return 'power';
|
|
25
|
+
if (mode.isSIM() || mode.isResistance()) {
|
|
26
|
+
return isVirtualShiftingEnabled(mode) ? 'gear' : 'hidden';
|
|
27
|
+
}
|
|
28
|
+
return 'power';
|
|
29
|
+
}
|
|
@@ -432,8 +432,11 @@ let RideDisplayService = (() => {
|
|
|
432
432
|
this.observer?.stop({ immediately: props.noStateUpdates });
|
|
433
433
|
if (props.noStateUpdates)
|
|
434
434
|
delete this.observer;
|
|
435
|
-
else
|
|
436
|
-
|
|
435
|
+
else {
|
|
436
|
+
const observerToClear = this.observer;
|
|
437
|
+
(0, utils_1.waitNextTick)().then(() => { if (this.observer === observerToClear)
|
|
438
|
+
delete this.observer; });
|
|
439
|
+
}
|
|
437
440
|
if (prevState === 'Finished' || prevState === 'Idle' || prevState === 'Closing') {
|
|
438
441
|
this.state = 'Idle';
|
|
439
442
|
return;
|
|
@@ -553,7 +556,7 @@ let RideDisplayService = (() => {
|
|
|
553
556
|
const { time, speed, routeDistance, distance } = currentValues;
|
|
554
557
|
const data = { time, speed, routeDistance, distance };
|
|
555
558
|
this.getRideModeService().onActivityUpdate(data, currentValues);
|
|
556
|
-
this.observer
|
|
559
|
+
this.observer?.emit('data-update', data, currentValues);
|
|
557
560
|
}
|
|
558
561
|
}
|
|
559
562
|
onWorkoutStopped() {
|
|
@@ -1085,16 +1088,7 @@ let RideDisplayService = (() => {
|
|
|
1085
1088
|
isVirtualShiftingEnabled() {
|
|
1086
1089
|
try {
|
|
1087
1090
|
const mode = this.getDeviceRide().getCyclingMode();
|
|
1088
|
-
|
|
1089
|
-
return false;
|
|
1090
|
-
if (mode.isSIM()) {
|
|
1091
|
-
const virtshiftMode = mode.getSetting('virtshift');
|
|
1092
|
-
return (virtshiftMode === 'Mixed' || virtshiftMode === 'Incyclist' || virtshiftMode === 'SmartTrainer' || virtshiftMode === 'Enabled');
|
|
1093
|
-
}
|
|
1094
|
-
if (mode.isResistance()) {
|
|
1095
|
-
return true;
|
|
1096
|
-
}
|
|
1097
|
-
return false;
|
|
1091
|
+
return (0, devices_1.isVirtualShiftingEnabled)(mode);
|
|
1098
1092
|
}
|
|
1099
1093
|
catch (err) {
|
|
1100
1094
|
this.logError(err, 'isVirtualShiftingEnabled');
|
|
@@ -35,6 +35,7 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
|
|
|
35
35
|
};
|
|
36
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
37
|
exports.WorkoutDisplayService = void 0;
|
|
38
|
+
const devices_1 = require("../../devices");
|
|
38
39
|
const workouts_1 = require("../../workouts");
|
|
39
40
|
const base_1 = require("../base/base");
|
|
40
41
|
const decorators_1 = require("../../base/decorators");
|
|
@@ -56,6 +57,9 @@ let WorkoutDisplayService = (() => {
|
|
|
56
57
|
prevPowerTs = __runInitializers(this, _instanceExtraInitializers);
|
|
57
58
|
buildRequest(props = {}) {
|
|
58
59
|
if (props?.limits) {
|
|
60
|
+
const mode = this.getDeviceRide().getCyclingMode();
|
|
61
|
+
if ((0, devices_1.getLoadButtonMode)(mode) !== 'power')
|
|
62
|
+
return {};
|
|
59
63
|
return props.limits;
|
|
60
64
|
}
|
|
61
65
|
else
|
|
@@ -76,12 +80,12 @@ let WorkoutDisplayService = (() => {
|
|
|
76
80
|
}
|
|
77
81
|
}
|
|
78
82
|
getLogProps() {
|
|
79
|
-
const workout = this.getWorkoutList().getSelected();
|
|
83
|
+
const workout = this.getWorkoutList().getSelected() ?? this.getWorkoutRide().getWorkout();
|
|
80
84
|
const settings = this.getWorkoutList().getStartSettings() ?? {};
|
|
81
85
|
const bikeProps = this.getBikeLogProps();
|
|
82
86
|
return {
|
|
83
87
|
mode: 'workout',
|
|
84
|
-
workout: workout
|
|
88
|
+
workout: workout?.name,
|
|
85
89
|
...settings,
|
|
86
90
|
...bikeProps
|
|
87
91
|
};
|
|
@@ -172,7 +172,8 @@ let WorkoutRidePageService = (() => {
|
|
|
172
172
|
steps: this.buildUpcomingSteps(current, wo.ftp),
|
|
173
173
|
dashboard: this.buildDashboardLine(wo),
|
|
174
174
|
gestureHint: this.buildGestureHint(base.startOverlayProps === null),
|
|
175
|
-
loadIncrement: this.getLoadIncrement()
|
|
175
|
+
loadIncrement: this.getLoadIncrement(),
|
|
176
|
+
loadButtonMode: wo.loadButtonMode ?? 'power'
|
|
176
177
|
};
|
|
177
178
|
}
|
|
178
179
|
catch (err) {
|
|
@@ -334,6 +335,15 @@ let WorkoutRidePageService = (() => {
|
|
|
334
335
|
return undefined;
|
|
335
336
|
}
|
|
336
337
|
}
|
|
338
|
+
getLoadButtonMode() {
|
|
339
|
+
try {
|
|
340
|
+
return this.getWorkoutRide().getLoadButtonMode();
|
|
341
|
+
}
|
|
342
|
+
catch (err) {
|
|
343
|
+
this.logError(err, 'getLoadButtonMode');
|
|
344
|
+
return 'power';
|
|
345
|
+
}
|
|
346
|
+
}
|
|
337
347
|
onRideStateUpdate(state) {
|
|
338
348
|
this.subscribeToWorkoutObserver();
|
|
339
349
|
switch (state) {
|
|
@@ -512,7 +522,8 @@ let WorkoutRidePageService = (() => {
|
|
|
512
522
|
steps: { previous: null, current: null, upcoming: [], hasMore: false },
|
|
513
523
|
dashboard: { text: '', mode: null },
|
|
514
524
|
gestureHint: null,
|
|
515
|
-
loadIncrement: DEFAULT_LOAD_INCREMENT
|
|
525
|
+
loadIncrement: DEFAULT_LOAD_INCREMENT,
|
|
526
|
+
loadButtonMode: 'power'
|
|
516
527
|
};
|
|
517
528
|
}
|
|
518
529
|
updatePageDisplay() {
|
|
@@ -57,14 +57,17 @@ let WorkoutRide = (() => {
|
|
|
57
57
|
let _instanceExtraInitializers = [];
|
|
58
58
|
let _getUserSettings_decorators;
|
|
59
59
|
let _getWorkoutList_decorators;
|
|
60
|
+
let _getDeviceRide_decorators;
|
|
60
61
|
var WorkoutRide = class extends _classSuper {
|
|
61
62
|
static { _classThis = this; }
|
|
62
63
|
static {
|
|
63
64
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
64
65
|
_getUserSettings_decorators = [decorators_1.Injectable];
|
|
65
66
|
_getWorkoutList_decorators = [decorators_1.Injectable];
|
|
67
|
+
_getDeviceRide_decorators = [decorators_1.Injectable];
|
|
66
68
|
__esDecorate(this, null, _getUserSettings_decorators, { kind: "method", name: "getUserSettings", static: false, private: false, access: { has: obj => "getUserSettings" in obj, get: obj => obj.getUserSettings }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
67
69
|
__esDecorate(this, null, _getWorkoutList_decorators, { kind: "method", name: "getWorkoutList", static: false, private: false, access: { has: obj => "getWorkoutList" in obj, get: obj => obj.getWorkoutList }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
70
|
+
__esDecorate(this, null, _getDeviceRide_decorators, { kind: "method", name: "getDeviceRide", static: false, private: false, access: { has: obj => "getDeviceRide" in obj, get: obj => obj.getDeviceRide }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
68
71
|
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
69
72
|
WorkoutRide = _classThis = _classDescriptor.value;
|
|
70
73
|
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
@@ -269,16 +272,68 @@ let WorkoutRide = (() => {
|
|
|
269
272
|
this.logError(err, 'backward', { ts, limits });
|
|
270
273
|
}
|
|
271
274
|
}
|
|
275
|
+
isPowerRangeAdjustable(delta) {
|
|
276
|
+
if (this.currentLimits?.minPower === this.currentLimits?.maxPower)
|
|
277
|
+
return false;
|
|
278
|
+
return delta >= 0
|
|
279
|
+
? this.currentLimits?.targetPower < this.currentLimits?.maxPower
|
|
280
|
+
: this.currentLimits?.targetPower > this.currentLimits?.minPower;
|
|
281
|
+
}
|
|
282
|
+
getPowerRangeDeltaVal(magnitude) {
|
|
283
|
+
if (!this.settings?.ftp)
|
|
284
|
+
return magnitude;
|
|
285
|
+
return magnitude === 1 ? 5 : 50;
|
|
286
|
+
}
|
|
287
|
+
getPowerRangeAdjustmentWatts(magnitude, increase) {
|
|
288
|
+
const nominal = this.getPowerRangeDeltaVal(magnitude);
|
|
289
|
+
const headroom = increase
|
|
290
|
+
? this.currentLimits.maxPower - this.currentLimits.targetPower
|
|
291
|
+
: this.currentLimits.targetPower - this.currentLimits.minPower;
|
|
292
|
+
return Math.min(nominal, headroom);
|
|
293
|
+
}
|
|
294
|
+
getLoadButtonMode() {
|
|
295
|
+
try {
|
|
296
|
+
const mode = this.getDeviceRide().getCyclingMode();
|
|
297
|
+
return (0, devices_1.getLoadButtonMode)(mode);
|
|
298
|
+
}
|
|
299
|
+
catch (err) {
|
|
300
|
+
this.logError(err, 'getLoadButtonMode');
|
|
301
|
+
return 'power';
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
getGearButtonLabels() {
|
|
305
|
+
return { inc5: '+5', inc1: '+1', dec1: '-1', dec5: '-5' };
|
|
306
|
+
}
|
|
307
|
+
getLoadButtonLabels() {
|
|
308
|
+
const label = (magnitude, increase) => {
|
|
309
|
+
const sign = increase ? '+' : '-';
|
|
310
|
+
const delta = increase ? magnitude : -magnitude;
|
|
311
|
+
return this.isPowerRangeAdjustable(delta)
|
|
312
|
+
? `${sign}${this.getPowerRangeAdjustmentWatts(magnitude, increase)}W`
|
|
313
|
+
: `${sign}${magnitude}%`;
|
|
314
|
+
};
|
|
315
|
+
return {
|
|
316
|
+
inc5: label(5, true),
|
|
317
|
+
inc1: label(1, true),
|
|
318
|
+
dec1: label(1, false),
|
|
319
|
+
dec5: label(5, false),
|
|
320
|
+
};
|
|
321
|
+
}
|
|
272
322
|
powerUp(delta) {
|
|
273
323
|
if (delta < 0)
|
|
274
324
|
return this.powerDown(-delta);
|
|
275
325
|
this.logEvent({ message: 'workout power up', delta });
|
|
276
326
|
try {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
327
|
+
const loadButtonMode = this.getLoadButtonMode();
|
|
328
|
+
if (loadButtonMode === 'hidden')
|
|
329
|
+
return undefined;
|
|
330
|
+
if (loadButtonMode === 'gear') {
|
|
331
|
+
this.gearChange(delta);
|
|
332
|
+
this.logEvent({ message: 'workout gear shift', gearDelta: delta });
|
|
333
|
+
return { type: 'gear', value: delta };
|
|
334
|
+
}
|
|
335
|
+
if (this.isPowerRangeAdjustable(delta)) {
|
|
336
|
+
const deltaVal = this.getPowerRangeDeltaVal(delta);
|
|
282
337
|
this.currentLimits.targetPower = Math.min(this.currentLimits.targetPower + deltaVal, this.currentLimits.maxPower);
|
|
283
338
|
this.logEvent({ message: 'workout target power adjusted', targetPower: this.currentLimits.targetPower });
|
|
284
339
|
this.emit('update', this.getDashboardDisplayProperties());
|
|
@@ -304,11 +359,16 @@ let WorkoutRide = (() => {
|
|
|
304
359
|
powerDown(delta) {
|
|
305
360
|
this.logEvent({ message: 'workout power down', delta });
|
|
306
361
|
try {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
362
|
+
const loadButtonMode = this.getLoadButtonMode();
|
|
363
|
+
if (loadButtonMode === 'hidden')
|
|
364
|
+
return undefined;
|
|
365
|
+
if (loadButtonMode === 'gear') {
|
|
366
|
+
this.gearChange(-delta);
|
|
367
|
+
this.logEvent({ message: 'workout gear shift', gearDelta: -delta });
|
|
368
|
+
return { type: 'gear', value: -delta };
|
|
369
|
+
}
|
|
370
|
+
if (this.isPowerRangeAdjustable(-delta)) {
|
|
371
|
+
const deltaVal = this.getPowerRangeDeltaVal(delta);
|
|
312
372
|
this.currentLimits.targetPower = Math.max(this.currentLimits.targetPower - deltaVal, this.currentLimits.minPower);
|
|
313
373
|
this.logEvent({ message: 'workout target power adjusted', targetPower: this.currentLimits.targetPower });
|
|
314
374
|
this.emit('update', this.getDashboardDisplayProperties());
|
|
@@ -331,6 +391,9 @@ let WorkoutRide = (() => {
|
|
|
331
391
|
return undefined;
|
|
332
392
|
}
|
|
333
393
|
}
|
|
394
|
+
gearChange(gearDelta) {
|
|
395
|
+
this.getDeviceRide().sendUpdate({ gearDelta });
|
|
396
|
+
}
|
|
334
397
|
toggleCyclingMode() {
|
|
335
398
|
const deviceRide = (0, devices_1.useDeviceRide)();
|
|
336
399
|
deviceRide.toggleCyclingMode();
|
|
@@ -344,6 +407,7 @@ let WorkoutRide = (() => {
|
|
|
344
407
|
const { start, stop } = this.getZoomParameters(this.trainingTime);
|
|
345
408
|
const title = this.getStepTitle(this.trainingTime);
|
|
346
409
|
const canShowBackward = Math.round((this.trainingTime ?? 0)) > 0;
|
|
410
|
+
const loadButtonMode = this.getLoadButtonMode();
|
|
347
411
|
const props = {
|
|
348
412
|
workout: this.workout, title,
|
|
349
413
|
ftp: this.settings.ftp,
|
|
@@ -351,7 +415,9 @@ let WorkoutRide = (() => {
|
|
|
351
415
|
start, stop,
|
|
352
416
|
mode: this.getCyclingModeText(),
|
|
353
417
|
canShowBackward,
|
|
354
|
-
canShowForward: true
|
|
418
|
+
canShowForward: true,
|
|
419
|
+
loadButtonMode,
|
|
420
|
+
loadButtons: loadButtonMode === 'power' ? this.getLoadButtonLabels() : this.getGearButtonLabels()
|
|
355
421
|
};
|
|
356
422
|
return props;
|
|
357
423
|
}
|
|
@@ -618,6 +684,9 @@ let WorkoutRide = (() => {
|
|
|
618
684
|
getWorkoutList() {
|
|
619
685
|
return (0, list_1.useWorkoutList)();
|
|
620
686
|
}
|
|
687
|
+
getDeviceRide() {
|
|
688
|
+
return (0, devices_1.useDeviceRide)();
|
|
689
|
+
}
|
|
621
690
|
};
|
|
622
691
|
return WorkoutRide = _classThis;
|
|
623
692
|
})();
|
package/lib/esm/apps/index.js
CHANGED
package/lib/esm/apps/service.js
CHANGED
|
@@ -40,7 +40,6 @@ import { useUserSettings } from "../settings";
|
|
|
40
40
|
import { StravaAppConnection } from "./base";
|
|
41
41
|
import { IntervalsAppConnection } from "./intervals/IntervalsAppConnection";
|
|
42
42
|
import { KomootAppConnection } from "./komoot";
|
|
43
|
-
import { VeloHeroAppConnection } from "./velohero/VeloHeroAppConnection";
|
|
44
43
|
let AppsService = (() => {
|
|
45
44
|
let _classDecorators = [Singleton];
|
|
46
45
|
let _classDescriptor;
|
|
@@ -64,7 +63,7 @@ let AppsService = (() => {
|
|
|
64
63
|
__runInitializers(_classThis, _classExtraInitializers);
|
|
65
64
|
}
|
|
66
65
|
services = (__runInitializers(this, _instanceExtraInitializers), {
|
|
67
|
-
ActivityUpload: ['strava', '
|
|
66
|
+
ActivityUpload: ['strava', 'intervals'],
|
|
68
67
|
WorkoutUpload: [],
|
|
69
68
|
WorkoutDownload: ['intervals'],
|
|
70
69
|
ActivityDownload: [],
|
|
@@ -72,7 +71,6 @@ let AppsService = (() => {
|
|
|
72
71
|
});
|
|
73
72
|
serviceMap = [
|
|
74
73
|
{ name: 'Strava', key: 'strava', iconUrl: 'https://static.cdnlogo.com/logos/s/42/strava-wordmark.svg', connection: new StravaAppConnection() },
|
|
75
|
-
{ name: 'VeloHero', key: 'velohero', iconUrl: 'images/velo-white.png', connection: new VeloHeroAppConnection() },
|
|
76
74
|
{ name: 'Komoot', key: 'komoot', iconUrl: 'https://www.komoot.com/assets/4d8ae313eec53e6e.svg', connection: new KomootAppConnection() },
|
|
77
75
|
{ name: 'Intervals.icu', key: 'intervals', iconUrl: 'images/intervals.png', connection: new IntervalsAppConnection() },
|
|
78
76
|
];
|
|
@@ -92,9 +92,15 @@ let DevicesPageService = (() => {
|
|
|
92
92
|
if (this.promiseOpen !== undefined) {
|
|
93
93
|
return this.getPageObserver();
|
|
94
94
|
}
|
|
95
|
-
|
|
95
|
+
const onStateMachineUpdate = () => {
|
|
96
96
|
this.getPageObserver().emit('page-update');
|
|
97
|
-
}
|
|
97
|
+
};
|
|
98
|
+
this.stateMachine.start(onStateMachineUpdate);
|
|
99
|
+
if (this.stateMachine.state !== 'Idle') {
|
|
100
|
+
this.logEvent({ message: 'state machine was not in expected state on open, resetting', page: 'Pairing', state: this.stateMachine.state });
|
|
101
|
+
this.stateMachine.stop();
|
|
102
|
+
this.stateMachine.start(onStateMachineUpdate);
|
|
103
|
+
}
|
|
98
104
|
this.promiseOpen = new Promise((done) => {
|
|
99
105
|
this.start()
|
|
100
106
|
.catch((err) => { this.logError(err, 'openPage'); })
|
|
@@ -318,7 +324,7 @@ let DevicesPageService = (() => {
|
|
|
318
324
|
this.updatePage();
|
|
319
325
|
}
|
|
320
326
|
async closeDeviceSelection(enabled) {
|
|
321
|
-
this.logEvent({ message: 'capability closed', capability: this.openedCapability });
|
|
327
|
+
this.logEvent({ message: 'capability closed', capability: this.openedCapability, enabled, caller: new Error().stack });
|
|
322
328
|
if (!enabled) {
|
|
323
329
|
const all = this.state.capabilities ?? [];
|
|
324
330
|
const requested = all.find(c => c.capability === this.openedCapability);
|
|
@@ -65,6 +65,8 @@ let PairingPageStateMachine = (() => {
|
|
|
65
65
|
selectStateChangeCallback;
|
|
66
66
|
retryTimeout;
|
|
67
67
|
selectionTimeout;
|
|
68
|
+
pairingPromise;
|
|
69
|
+
scanningPromise;
|
|
68
70
|
constructor() {
|
|
69
71
|
this.logger = new EventLogger('PairingPageState');
|
|
70
72
|
}
|
|
@@ -96,6 +98,8 @@ let PairingPageStateMachine = (() => {
|
|
|
96
98
|
this.unregisterServiceEventHandlers();
|
|
97
99
|
delete this.stateChangeCallback;
|
|
98
100
|
this.resetTimeouts();
|
|
101
|
+
delete this.pairingPromise;
|
|
102
|
+
delete this.scanningPromise;
|
|
99
103
|
this.setState('Closed');
|
|
100
104
|
this.logIncomingEvent('stop', prev, 'Closed');
|
|
101
105
|
}
|
|
@@ -196,14 +200,20 @@ let PairingPageStateMachine = (() => {
|
|
|
196
200
|
this.resetTimeouts();
|
|
197
201
|
this.setState('Done');
|
|
198
202
|
}
|
|
199
|
-
else {
|
|
203
|
+
else if (!this.pairingPromise) {
|
|
200
204
|
this.setState('Pairing');
|
|
201
|
-
service.startPairing(adapters, {})
|
|
205
|
+
this.pairingPromise = Promise.resolve(service.startPairing(adapters, {}))
|
|
206
|
+
.catch(err => { this.logError(err, 'startPairing'); })
|
|
207
|
+
.finally(() => { delete this.pairingPromise; });
|
|
202
208
|
}
|
|
203
209
|
}
|
|
204
210
|
else if (this.selectState === 'Closed' || this.selectState === 'Active') {
|
|
205
|
-
this.
|
|
206
|
-
|
|
211
|
+
if (!this.scanningPromise) {
|
|
212
|
+
this.setState('Scanning');
|
|
213
|
+
this.scanningPromise = Promise.resolve(service.startScanning(adapters, {}))
|
|
214
|
+
.catch(err => { this.logError(err, 'startScanning'); })
|
|
215
|
+
.finally(() => { delete this.scanningPromise; });
|
|
216
|
+
}
|
|
207
217
|
}
|
|
208
218
|
if (this.state !== oldState)
|
|
209
219
|
this.logger.logEvent({ message: 'state changed', stateTransition: { from: oldState, to: this.state } });
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function isVirtualShiftingEnabled(mode) {
|
|
2
|
+
try {
|
|
3
|
+
if (!mode)
|
|
4
|
+
return false;
|
|
5
|
+
if (mode.isSIM()) {
|
|
6
|
+
const virtshiftMode = mode.getSetting('virtshift');
|
|
7
|
+
return (virtshiftMode === 'Mixed' || virtshiftMode === 'Incyclist' || virtshiftMode === 'SmartTrainer' || virtshiftMode === 'Enabled');
|
|
8
|
+
}
|
|
9
|
+
if (mode.isResistance()) {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function getLoadButtonMode(mode) {
|
|
19
|
+
if (!mode)
|
|
20
|
+
return 'power';
|
|
21
|
+
if (mode.isSIM() || mode.isResistance()) {
|
|
22
|
+
return isVirtualShiftingEnabled(mode) ? 'gear' : 'hidden';
|
|
23
|
+
}
|
|
24
|
+
return 'power';
|
|
25
|
+
}
|
|
@@ -38,7 +38,7 @@ import { Observer, Singleton } from "../../base/types";
|
|
|
38
38
|
import { Injectable } from "../../base/decorators";
|
|
39
39
|
import { Step, useWorkoutList, useWorkoutRide, Workout } from "../../workouts";
|
|
40
40
|
import { useRouteList } from "../../routes";
|
|
41
|
-
import { useDeviceConfiguration, useDeviceRide } from "../../devices";
|
|
41
|
+
import { isVirtualShiftingEnabled as checkVirtualShiftingEnabled, useDeviceConfiguration, useDeviceRide } from "../../devices";
|
|
42
42
|
import { useUserSettings } from "../../settings";
|
|
43
43
|
import { IncyclistCapability } from "incyclist-devices";
|
|
44
44
|
import { formatDateTime, getLegacyInterface, waitNextTick } from "../../utils";
|
|
@@ -429,8 +429,11 @@ let RideDisplayService = (() => {
|
|
|
429
429
|
this.observer?.stop({ immediately: props.noStateUpdates });
|
|
430
430
|
if (props.noStateUpdates)
|
|
431
431
|
delete this.observer;
|
|
432
|
-
else
|
|
433
|
-
|
|
432
|
+
else {
|
|
433
|
+
const observerToClear = this.observer;
|
|
434
|
+
waitNextTick().then(() => { if (this.observer === observerToClear)
|
|
435
|
+
delete this.observer; });
|
|
436
|
+
}
|
|
434
437
|
if (prevState === 'Finished' || prevState === 'Idle' || prevState === 'Closing') {
|
|
435
438
|
this.state = 'Idle';
|
|
436
439
|
return;
|
|
@@ -550,7 +553,7 @@ let RideDisplayService = (() => {
|
|
|
550
553
|
const { time, speed, routeDistance, distance } = currentValues;
|
|
551
554
|
const data = { time, speed, routeDistance, distance };
|
|
552
555
|
this.getRideModeService().onActivityUpdate(data, currentValues);
|
|
553
|
-
this.observer
|
|
556
|
+
this.observer?.emit('data-update', data, currentValues);
|
|
554
557
|
}
|
|
555
558
|
}
|
|
556
559
|
onWorkoutStopped() {
|
|
@@ -1082,16 +1085,7 @@ let RideDisplayService = (() => {
|
|
|
1082
1085
|
isVirtualShiftingEnabled() {
|
|
1083
1086
|
try {
|
|
1084
1087
|
const mode = this.getDeviceRide().getCyclingMode();
|
|
1085
|
-
|
|
1086
|
-
return false;
|
|
1087
|
-
if (mode.isSIM()) {
|
|
1088
|
-
const virtshiftMode = mode.getSetting('virtshift');
|
|
1089
|
-
return (virtshiftMode === 'Mixed' || virtshiftMode === 'Incyclist' || virtshiftMode === 'SmartTrainer' || virtshiftMode === 'Enabled');
|
|
1090
|
-
}
|
|
1091
|
-
if (mode.isResistance()) {
|
|
1092
|
-
return true;
|
|
1093
|
-
}
|
|
1094
|
-
return false;
|
|
1088
|
+
return checkVirtualShiftingEnabled(mode);
|
|
1095
1089
|
}
|
|
1096
1090
|
catch (err) {
|
|
1097
1091
|
this.logError(err, 'isVirtualShiftingEnabled');
|
|
@@ -32,6 +32,7 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
|
|
|
32
32
|
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
33
33
|
done = true;
|
|
34
34
|
};
|
|
35
|
+
import { getLoadButtonMode } from "../../devices";
|
|
35
36
|
import { useWorkoutList, useWorkoutRide } from "../../workouts";
|
|
36
37
|
import { RideModeService } from "../base/base";
|
|
37
38
|
import { Injectable } from "../../base/decorators";
|
|
@@ -53,6 +54,9 @@ let WorkoutDisplayService = (() => {
|
|
|
53
54
|
prevPowerTs = __runInitializers(this, _instanceExtraInitializers);
|
|
54
55
|
buildRequest(props = {}) {
|
|
55
56
|
if (props?.limits) {
|
|
57
|
+
const mode = this.getDeviceRide().getCyclingMode();
|
|
58
|
+
if (getLoadButtonMode(mode) !== 'power')
|
|
59
|
+
return {};
|
|
56
60
|
return props.limits;
|
|
57
61
|
}
|
|
58
62
|
else
|
|
@@ -73,12 +77,12 @@ let WorkoutDisplayService = (() => {
|
|
|
73
77
|
}
|
|
74
78
|
}
|
|
75
79
|
getLogProps() {
|
|
76
|
-
const workout = this.getWorkoutList().getSelected();
|
|
80
|
+
const workout = this.getWorkoutList().getSelected() ?? this.getWorkoutRide().getWorkout();
|
|
77
81
|
const settings = this.getWorkoutList().getStartSettings() ?? {};
|
|
78
82
|
const bikeProps = this.getBikeLogProps();
|
|
79
83
|
return {
|
|
80
84
|
mode: 'workout',
|
|
81
|
-
workout: workout
|
|
85
|
+
workout: workout?.name,
|
|
82
86
|
...settings,
|
|
83
87
|
...bikeProps
|
|
84
88
|
};
|
|
@@ -169,7 +169,8 @@ let WorkoutRidePageService = (() => {
|
|
|
169
169
|
steps: this.buildUpcomingSteps(current, wo.ftp),
|
|
170
170
|
dashboard: this.buildDashboardLine(wo),
|
|
171
171
|
gestureHint: this.buildGestureHint(base.startOverlayProps === null),
|
|
172
|
-
loadIncrement: this.getLoadIncrement()
|
|
172
|
+
loadIncrement: this.getLoadIncrement(),
|
|
173
|
+
loadButtonMode: wo.loadButtonMode ?? 'power'
|
|
173
174
|
};
|
|
174
175
|
}
|
|
175
176
|
catch (err) {
|
|
@@ -331,6 +332,15 @@ let WorkoutRidePageService = (() => {
|
|
|
331
332
|
return undefined;
|
|
332
333
|
}
|
|
333
334
|
}
|
|
335
|
+
getLoadButtonMode() {
|
|
336
|
+
try {
|
|
337
|
+
return this.getWorkoutRide().getLoadButtonMode();
|
|
338
|
+
}
|
|
339
|
+
catch (err) {
|
|
340
|
+
this.logError(err, 'getLoadButtonMode');
|
|
341
|
+
return 'power';
|
|
342
|
+
}
|
|
343
|
+
}
|
|
334
344
|
onRideStateUpdate(state) {
|
|
335
345
|
this.subscribeToWorkoutObserver();
|
|
336
346
|
switch (state) {
|
|
@@ -509,7 +519,8 @@ let WorkoutRidePageService = (() => {
|
|
|
509
519
|
steps: { previous: null, current: null, upcoming: [], hasMore: false },
|
|
510
520
|
dashboard: { text: '', mode: null },
|
|
511
521
|
gestureHint: null,
|
|
512
|
-
loadIncrement: DEFAULT_LOAD_INCREMENT
|
|
522
|
+
loadIncrement: DEFAULT_LOAD_INCREMENT,
|
|
523
|
+
loadButtonMode: 'power'
|
|
513
524
|
};
|
|
514
525
|
}
|
|
515
526
|
updatePageDisplay() {
|
|
@@ -36,7 +36,7 @@ import { getBindings } from "../../api";
|
|
|
36
36
|
import { IncyclistService } from "../../base/service";
|
|
37
37
|
import { Singleton } from "../../base/types";
|
|
38
38
|
import { Observer } from "../../base/types/observer";
|
|
39
|
-
import { useDeviceRide } from "../../devices";
|
|
39
|
+
import { getLoadButtonMode, useDeviceRide } from "../../devices";
|
|
40
40
|
import { useUserSettings } from "../../settings";
|
|
41
41
|
import { waitNextTick } from "../../utils";
|
|
42
42
|
import { valid } from "../../utils/valid";
|
|
@@ -54,14 +54,17 @@ let WorkoutRide = (() => {
|
|
|
54
54
|
let _instanceExtraInitializers = [];
|
|
55
55
|
let _getUserSettings_decorators;
|
|
56
56
|
let _getWorkoutList_decorators;
|
|
57
|
+
let _getDeviceRide_decorators;
|
|
57
58
|
var WorkoutRide = class extends _classSuper {
|
|
58
59
|
static { _classThis = this; }
|
|
59
60
|
static {
|
|
60
61
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
61
62
|
_getUserSettings_decorators = [Injectable];
|
|
62
63
|
_getWorkoutList_decorators = [Injectable];
|
|
64
|
+
_getDeviceRide_decorators = [Injectable];
|
|
63
65
|
__esDecorate(this, null, _getUserSettings_decorators, { kind: "method", name: "getUserSettings", static: false, private: false, access: { has: obj => "getUserSettings" in obj, get: obj => obj.getUserSettings }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
64
66
|
__esDecorate(this, null, _getWorkoutList_decorators, { kind: "method", name: "getWorkoutList", static: false, private: false, access: { has: obj => "getWorkoutList" in obj, get: obj => obj.getWorkoutList }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
67
|
+
__esDecorate(this, null, _getDeviceRide_decorators, { kind: "method", name: "getDeviceRide", static: false, private: false, access: { has: obj => "getDeviceRide" in obj, get: obj => obj.getDeviceRide }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
65
68
|
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
66
69
|
WorkoutRide = _classThis = _classDescriptor.value;
|
|
67
70
|
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
@@ -266,16 +269,68 @@ let WorkoutRide = (() => {
|
|
|
266
269
|
this.logError(err, 'backward', { ts, limits });
|
|
267
270
|
}
|
|
268
271
|
}
|
|
272
|
+
isPowerRangeAdjustable(delta) {
|
|
273
|
+
if (this.currentLimits?.minPower === this.currentLimits?.maxPower)
|
|
274
|
+
return false;
|
|
275
|
+
return delta >= 0
|
|
276
|
+
? this.currentLimits?.targetPower < this.currentLimits?.maxPower
|
|
277
|
+
: this.currentLimits?.targetPower > this.currentLimits?.minPower;
|
|
278
|
+
}
|
|
279
|
+
getPowerRangeDeltaVal(magnitude) {
|
|
280
|
+
if (!this.settings?.ftp)
|
|
281
|
+
return magnitude;
|
|
282
|
+
return magnitude === 1 ? 5 : 50;
|
|
283
|
+
}
|
|
284
|
+
getPowerRangeAdjustmentWatts(magnitude, increase) {
|
|
285
|
+
const nominal = this.getPowerRangeDeltaVal(magnitude);
|
|
286
|
+
const headroom = increase
|
|
287
|
+
? this.currentLimits.maxPower - this.currentLimits.targetPower
|
|
288
|
+
: this.currentLimits.targetPower - this.currentLimits.minPower;
|
|
289
|
+
return Math.min(nominal, headroom);
|
|
290
|
+
}
|
|
291
|
+
getLoadButtonMode() {
|
|
292
|
+
try {
|
|
293
|
+
const mode = this.getDeviceRide().getCyclingMode();
|
|
294
|
+
return getLoadButtonMode(mode);
|
|
295
|
+
}
|
|
296
|
+
catch (err) {
|
|
297
|
+
this.logError(err, 'getLoadButtonMode');
|
|
298
|
+
return 'power';
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
getGearButtonLabels() {
|
|
302
|
+
return { inc5: '+5', inc1: '+1', dec1: '-1', dec5: '-5' };
|
|
303
|
+
}
|
|
304
|
+
getLoadButtonLabels() {
|
|
305
|
+
const label = (magnitude, increase) => {
|
|
306
|
+
const sign = increase ? '+' : '-';
|
|
307
|
+
const delta = increase ? magnitude : -magnitude;
|
|
308
|
+
return this.isPowerRangeAdjustable(delta)
|
|
309
|
+
? `${sign}${this.getPowerRangeAdjustmentWatts(magnitude, increase)}W`
|
|
310
|
+
: `${sign}${magnitude}%`;
|
|
311
|
+
};
|
|
312
|
+
return {
|
|
313
|
+
inc5: label(5, true),
|
|
314
|
+
inc1: label(1, true),
|
|
315
|
+
dec1: label(1, false),
|
|
316
|
+
dec5: label(5, false),
|
|
317
|
+
};
|
|
318
|
+
}
|
|
269
319
|
powerUp(delta) {
|
|
270
320
|
if (delta < 0)
|
|
271
321
|
return this.powerDown(-delta);
|
|
272
322
|
this.logEvent({ message: 'workout power up', delta });
|
|
273
323
|
try {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
324
|
+
const loadButtonMode = this.getLoadButtonMode();
|
|
325
|
+
if (loadButtonMode === 'hidden')
|
|
326
|
+
return undefined;
|
|
327
|
+
if (loadButtonMode === 'gear') {
|
|
328
|
+
this.gearChange(delta);
|
|
329
|
+
this.logEvent({ message: 'workout gear shift', gearDelta: delta });
|
|
330
|
+
return { type: 'gear', value: delta };
|
|
331
|
+
}
|
|
332
|
+
if (this.isPowerRangeAdjustable(delta)) {
|
|
333
|
+
const deltaVal = this.getPowerRangeDeltaVal(delta);
|
|
279
334
|
this.currentLimits.targetPower = Math.min(this.currentLimits.targetPower + deltaVal, this.currentLimits.maxPower);
|
|
280
335
|
this.logEvent({ message: 'workout target power adjusted', targetPower: this.currentLimits.targetPower });
|
|
281
336
|
this.emit('update', this.getDashboardDisplayProperties());
|
|
@@ -301,11 +356,16 @@ let WorkoutRide = (() => {
|
|
|
301
356
|
powerDown(delta) {
|
|
302
357
|
this.logEvent({ message: 'workout power down', delta });
|
|
303
358
|
try {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
359
|
+
const loadButtonMode = this.getLoadButtonMode();
|
|
360
|
+
if (loadButtonMode === 'hidden')
|
|
361
|
+
return undefined;
|
|
362
|
+
if (loadButtonMode === 'gear') {
|
|
363
|
+
this.gearChange(-delta);
|
|
364
|
+
this.logEvent({ message: 'workout gear shift', gearDelta: -delta });
|
|
365
|
+
return { type: 'gear', value: -delta };
|
|
366
|
+
}
|
|
367
|
+
if (this.isPowerRangeAdjustable(-delta)) {
|
|
368
|
+
const deltaVal = this.getPowerRangeDeltaVal(delta);
|
|
309
369
|
this.currentLimits.targetPower = Math.max(this.currentLimits.targetPower - deltaVal, this.currentLimits.minPower);
|
|
310
370
|
this.logEvent({ message: 'workout target power adjusted', targetPower: this.currentLimits.targetPower });
|
|
311
371
|
this.emit('update', this.getDashboardDisplayProperties());
|
|
@@ -328,6 +388,9 @@ let WorkoutRide = (() => {
|
|
|
328
388
|
return undefined;
|
|
329
389
|
}
|
|
330
390
|
}
|
|
391
|
+
gearChange(gearDelta) {
|
|
392
|
+
this.getDeviceRide().sendUpdate({ gearDelta });
|
|
393
|
+
}
|
|
331
394
|
toggleCyclingMode() {
|
|
332
395
|
const deviceRide = useDeviceRide();
|
|
333
396
|
deviceRide.toggleCyclingMode();
|
|
@@ -341,6 +404,7 @@ let WorkoutRide = (() => {
|
|
|
341
404
|
const { start, stop } = this.getZoomParameters(this.trainingTime);
|
|
342
405
|
const title = this.getStepTitle(this.trainingTime);
|
|
343
406
|
const canShowBackward = Math.round((this.trainingTime ?? 0)) > 0;
|
|
407
|
+
const loadButtonMode = this.getLoadButtonMode();
|
|
344
408
|
const props = {
|
|
345
409
|
workout: this.workout, title,
|
|
346
410
|
ftp: this.settings.ftp,
|
|
@@ -348,7 +412,9 @@ let WorkoutRide = (() => {
|
|
|
348
412
|
start, stop,
|
|
349
413
|
mode: this.getCyclingModeText(),
|
|
350
414
|
canShowBackward,
|
|
351
|
-
canShowForward: true
|
|
415
|
+
canShowForward: true,
|
|
416
|
+
loadButtonMode,
|
|
417
|
+
loadButtons: loadButtonMode === 'power' ? this.getLoadButtonLabels() : this.getGearButtonLabels()
|
|
352
418
|
};
|
|
353
419
|
return props;
|
|
354
420
|
}
|
|
@@ -615,6 +681,9 @@ let WorkoutRide = (() => {
|
|
|
615
681
|
getWorkoutList() {
|
|
616
682
|
return useWorkoutList();
|
|
617
683
|
}
|
|
684
|
+
getDeviceRide() {
|
|
685
|
+
return useDeviceRide();
|
|
686
|
+
}
|
|
618
687
|
};
|
|
619
688
|
return WorkoutRide = _classThis;
|
|
620
689
|
})();
|
|
@@ -11,6 +11,8 @@ export declare class PairingPageStateMachine {
|
|
|
11
11
|
protected selectStateChangeCallback: () => void;
|
|
12
12
|
protected retryTimeout: NodeJS.Timeout | undefined;
|
|
13
13
|
protected selectionTimeout: NodeJS.Timeout | undefined;
|
|
14
|
+
protected pairingPromise: Promise<void> | undefined;
|
|
15
|
+
protected scanningPromise: Promise<void> | undefined;
|
|
14
16
|
constructor();
|
|
15
17
|
get state(): PageState;
|
|
16
18
|
get selectState(): SelectState;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { IncyclistPageService } from "../../../base/pages";
|
|
2
2
|
import type { IObserver } from "../../../base/typedefs";
|
|
3
3
|
import type { CurrentRideState, RideType } from "../../../types";
|
|
4
|
-
import type { PowerAdjustmentResult, WorkoutDisplayProperties } from "../types";
|
|
4
|
+
import type { LoadButtonMode, PowerAdjustmentResult, WorkoutDisplayProperties } from "../types";
|
|
5
5
|
import type { Workout } from "../../base/model";
|
|
6
6
|
import type { StepDefinition } from "../../base/model/types";
|
|
7
7
|
import type { IWorkoutRidePageService, WorkoutDashboardLine, WorkoutGestureHint, WorkoutGraphActuals, WorkoutGraphPlan, WorkoutRideMenuProps, WorkoutRidePageDisplayProps, WorkoutUpcomingSteps } from "./types";
|
|
@@ -38,6 +38,7 @@ export declare class WorkoutRidePageService extends IncyclistPageService impleme
|
|
|
38
38
|
}): void;
|
|
39
39
|
onCancelStart(): void;
|
|
40
40
|
adjustLoad(deltaPct: number): PowerAdjustmentResult | undefined;
|
|
41
|
+
getLoadButtonMode(): LoadButtonMode;
|
|
41
42
|
protected onRideStateUpdate(state: CurrentRideState): void;
|
|
42
43
|
protected onWorkoutUpdate(): void;
|
|
43
44
|
protected onFinished(): void;
|
|
@@ -2,6 +2,7 @@ import type { IObserver } from "../../../base/typedefs";
|
|
|
2
2
|
import type { IPageService } from "../../../base/pages";
|
|
3
3
|
import type { RideMenuProps, RidePageDisplayProps } from "../../../ride/page/types";
|
|
4
4
|
import type { WorkoutGraphPlanBar } from "../../base/graph/types";
|
|
5
|
+
import type { LoadButtonMode } from "../types";
|
|
5
6
|
export interface WorkoutGraphPoint {
|
|
6
7
|
x: number;
|
|
7
8
|
y: number;
|
|
@@ -52,6 +53,7 @@ export interface WorkoutRidePageDisplayProps extends RidePageDisplayProps {
|
|
|
52
53
|
title: string;
|
|
53
54
|
gestureHint: WorkoutGestureHint | null;
|
|
54
55
|
loadIncrement: number;
|
|
56
|
+
loadButtonMode: LoadButtonMode;
|
|
55
57
|
}
|
|
56
58
|
export interface WorkoutRidePageCallbacks {
|
|
57
59
|
onMenuOpen(): void;
|
|
@@ -76,4 +78,5 @@ export interface IWorkoutRidePageService extends WorkoutRidePageCallbacks, IPage
|
|
|
76
78
|
getPageDisplayProps(): WorkoutRidePageDisplayProps;
|
|
77
79
|
getGraphActuals(): WorkoutGraphActuals;
|
|
78
80
|
adjustLoad(deltaPct: number): void;
|
|
81
|
+
getLoadButtonMode(): LoadButtonMode;
|
|
79
82
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { IncyclistService } from "../../base/service";
|
|
2
2
|
import { Observer } from "../../base/types/observer";
|
|
3
|
+
import { DeviceRideService } from "../../devices";
|
|
3
4
|
import { Workout } from "../base/model";
|
|
4
5
|
import { CurrentStep, PowerLimit, StepDefinition } from "../base/model/types";
|
|
5
6
|
import { WorkoutListService } from "../list";
|
|
6
7
|
import { WorkoutSettings } from "../list/cards/types";
|
|
7
|
-
import { ActiveWorkoutLimit, PowerAdjustmentResult, WorkoutDisplayProperties } from "./types";
|
|
8
|
+
import { ActiveWorkoutLimit, LoadButtonMode, PowerAdjustmentResult, WorkoutDisplayProperties } from "./types";
|
|
8
9
|
export declare class WorkoutRide extends IncyclistService {
|
|
9
10
|
protected manualTimeOffset: number;
|
|
10
11
|
protected manualPowerOffset: number;
|
|
@@ -33,8 +34,15 @@ export declare class WorkoutRide extends IncyclistService {
|
|
|
33
34
|
}): void;
|
|
34
35
|
forward(): void;
|
|
35
36
|
backward(): void;
|
|
37
|
+
private isPowerRangeAdjustable;
|
|
38
|
+
private getPowerRangeDeltaVal;
|
|
39
|
+
private getPowerRangeAdjustmentWatts;
|
|
40
|
+
getLoadButtonMode(): LoadButtonMode;
|
|
41
|
+
private getGearButtonLabels;
|
|
42
|
+
private getLoadButtonLabels;
|
|
36
43
|
powerUp(delta: number): PowerAdjustmentResult | undefined;
|
|
37
44
|
powerDown(delta: number): PowerAdjustmentResult | undefined;
|
|
45
|
+
private gearChange;
|
|
38
46
|
toggleCyclingMode(): void;
|
|
39
47
|
getDashboardDisplayProperties(): WorkoutDisplayProperties;
|
|
40
48
|
getCurrentLimits(): ActiveWorkoutLimit;
|
|
@@ -66,6 +74,7 @@ export declare class WorkoutRide extends IncyclistService {
|
|
|
66
74
|
protected getCyclingModeText(): string;
|
|
67
75
|
protected getUserSettings(): import("../../settings").UserSettingsService;
|
|
68
76
|
protected getWorkoutList(): WorkoutListService;
|
|
77
|
+
protected getDeviceRide(): DeviceRideService;
|
|
69
78
|
}
|
|
70
79
|
export declare const useWorkoutRide: () => WorkoutRide;
|
|
71
80
|
export declare const getWorkoutRide: () => WorkoutRide;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Workout } from "../base/model";
|
|
2
2
|
import { StepDefinition } from "../base/model/types";
|
|
3
|
+
import { LoadButtonMode } from "../../devices";
|
|
4
|
+
export type { LoadButtonMode };
|
|
3
5
|
export interface WorkoutRequest {
|
|
4
6
|
time: number;
|
|
5
7
|
minPower?: number;
|
|
@@ -16,7 +18,7 @@ export interface ActiveWorkoutLimit extends WorkoutRequest {
|
|
|
16
18
|
step?: StepDefinition;
|
|
17
19
|
}
|
|
18
20
|
export interface PowerAdjustmentResult {
|
|
19
|
-
type: 'ftp' | 'targetPower';
|
|
21
|
+
type: 'ftp' | 'targetPower' | 'gear';
|
|
20
22
|
value: number;
|
|
21
23
|
}
|
|
22
24
|
export interface WorkoutDisplayProperties {
|
|
@@ -29,4 +31,11 @@ export interface WorkoutDisplayProperties {
|
|
|
29
31
|
mode?: string;
|
|
30
32
|
canShowBackward?: boolean;
|
|
31
33
|
canShowForward?: boolean;
|
|
34
|
+
loadButtonMode?: LoadButtonMode;
|
|
35
|
+
loadButtons?: {
|
|
36
|
+
inc5: string;
|
|
37
|
+
inc1: string;
|
|
38
|
+
dec1: string;
|
|
39
|
+
dec5: string;
|
|
40
|
+
};
|
|
32
41
|
}
|