incyclist-services 1.7.49 → 1.7.50
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/esm/activities/base/convert/fit/local-fit.js +11 -3
- package/lib/esm/activities/base/convert/fit/remote-fit.js +2 -2
- package/lib/esm/activities/base/convert/tcx/tcx.js +10 -1
- package/lib/esm/activities/base/model/index.js +5 -0
- package/lib/esm/activities/base/repo/db.js +1 -1
- package/lib/esm/activities/base/utils/activity.js +3 -2
- package/lib/esm/activities/base/utils/helpers.js +3 -2
- package/lib/esm/activities/list/utils.js +1 -1
- package/lib/esm/activities/ride/service.js +81 -43
- package/lib/esm/devices/ride/service.js +13 -2
- package/lib/esm/ride/route/RouteDisplayService.js +2 -2
- package/lib/types/activities/base/convert/fit/local-fit.d.ts +2 -0
- package/lib/types/activities/base/convert/tcx/tcx.d.ts +2 -0
- package/lib/types/activities/base/model/index.d.ts +4 -0
- package/lib/types/activities/base/repo/db.d.ts +1 -1
- package/lib/types/activities/ride/service.d.ts +10 -5
- package/lib/types/devices/ride/service.d.ts +2 -1
- package/lib/types/devices/types.d.ts +2 -0
- package/lib/types/index.d.ts +1 -1
- package/lib/types/ride/display/service.d.ts +1 -1
- package/lib/types/routes/list/cards/RouteCard.d.ts +1 -1
- package/package.json +2 -2
|
@@ -65,7 +65,7 @@ let LocalFitConverter = (() => {
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
getFitActivity(activity) {
|
|
68
|
-
const { id, title, time, timeTotal, timePause, distance } = activity;
|
|
68
|
+
const { id, title, time, timeTotal, timePause, distance, sport = 'cycling' } = activity;
|
|
69
69
|
const status = 'created';
|
|
70
70
|
const startTime = new Date(activity.startTime).toISOString();
|
|
71
71
|
const logs = activity.logs.map(this.mapLogToFit.bind(this));
|
|
@@ -75,7 +75,7 @@ let LocalFitConverter = (() => {
|
|
|
75
75
|
id: this.getUserSettings().get('uuid', undefined),
|
|
76
76
|
weight: activity.user.weight
|
|
77
77
|
};
|
|
78
|
-
return { id, title, status, logs, laps, startTime, time, timeTotal, timePause, distance, user, screenshots };
|
|
78
|
+
return { id, title, status, logs, laps, startTime, time, timeTotal, timePause, distance, user, screenshots, sport };
|
|
79
79
|
}
|
|
80
80
|
mapLapsToFit(laps, activityStartTime) {
|
|
81
81
|
const activityStartMs = new Date(activityStartTime).getTime();
|
|
@@ -181,7 +181,7 @@ let LocalFitConverter = (() => {
|
|
|
181
181
|
totalElapsedTime: activity.timeTotal,
|
|
182
182
|
totalTimerTime: activity.time,
|
|
183
183
|
totalDistance: activity.distance,
|
|
184
|
-
sport:
|
|
184
|
+
sport: this.mapSport(activity.sport),
|
|
185
185
|
subSport: 'virtualActivity',
|
|
186
186
|
event: 'session',
|
|
187
187
|
eventType: 'stopDisableAll',
|
|
@@ -197,6 +197,14 @@ let LocalFitConverter = (() => {
|
|
|
197
197
|
const uint8Array = encoder.close();
|
|
198
198
|
return uint8Array.buffer;
|
|
199
199
|
}
|
|
200
|
+
mapSport(incyclistSport) {
|
|
201
|
+
if (!incyclistSport || incyclistSport === 'cycling')
|
|
202
|
+
return 'cycling';
|
|
203
|
+
if (incyclistSport === 'running')
|
|
204
|
+
return 'running';
|
|
205
|
+
if (incyclistSport === 'rowing')
|
|
206
|
+
return 'rowing';
|
|
207
|
+
}
|
|
200
208
|
getUserSettings() {
|
|
201
209
|
return useUserSettings();
|
|
202
210
|
}
|
|
@@ -26,7 +26,7 @@ export class RemoteFitConverter {
|
|
|
26
26
|
return this.api;
|
|
27
27
|
}
|
|
28
28
|
getFitActivity(activity) {
|
|
29
|
-
const { id, title, time, timeTotal, timePause, distance } = activity;
|
|
29
|
+
const { id, title, time, timeTotal, timePause, distance, sport = 'cycling' } = activity;
|
|
30
30
|
const status = 'created';
|
|
31
31
|
const startTime = new Date(activity.startTime).toISOString();
|
|
32
32
|
const logs = activity.logs.map(this.mapLogToFit.bind(this));
|
|
@@ -36,7 +36,7 @@ export class RemoteFitConverter {
|
|
|
36
36
|
id: this.getUserSettings().get('uuid', undefined),
|
|
37
37
|
weight: activity.user.weight
|
|
38
38
|
};
|
|
39
|
-
return { id, title, status, logs, laps, startTime, time, timeTotal, timePause, distance, user, screenshots };
|
|
39
|
+
return { id, title, status, logs, laps, startTime, time, timeTotal, timePause, distance, user, screenshots, sport };
|
|
40
40
|
}
|
|
41
41
|
mapLogToFit(log) {
|
|
42
42
|
const { time, speed, slope, cadence: cadenceOrg, heartrate: heartrateOrg, distance, power: powerOrg, lat, lng, elevation } = log;
|
|
@@ -85,7 +85,8 @@ export class TcxConverter {
|
|
|
85
85
|
const trackPoints = this.creatTrackPoints(activity, startTime);
|
|
86
86
|
const laps = this.createLaps(startTime, activity, trackPoints);
|
|
87
87
|
const creator = new IncyclistAttribution();
|
|
88
|
-
const
|
|
88
|
+
const sport = this.mapSport(activity.sport);
|
|
89
|
+
const tcxActivity = new Activity(sport, { Id: startTime, Notes: 'Incyclist Ride', Laps: laps, Creator: creator });
|
|
89
90
|
const activityList = new ActivityList({ activity: [tcxActivity] });
|
|
90
91
|
const tcxObj = new TrainingCenterDatabase({ activities: activityList });
|
|
91
92
|
const xml = tcxObj.toXml();
|
|
@@ -97,6 +98,14 @@ export class TcxConverter {
|
|
|
97
98
|
throw err;
|
|
98
99
|
}
|
|
99
100
|
}
|
|
101
|
+
mapSport(incyclistSport) {
|
|
102
|
+
if (!incyclistSport || incyclistSport === 'cycling')
|
|
103
|
+
return 'Biking';
|
|
104
|
+
if (incyclistSport === 'running')
|
|
105
|
+
return 'Running';
|
|
106
|
+
if (incyclistSport === 'rowing')
|
|
107
|
+
return 'Other';
|
|
108
|
+
}
|
|
100
109
|
createLaps(startTime, activity, trackPoints) {
|
|
101
110
|
if (!activity.workout && !activity.laps)
|
|
102
111
|
return this.createActivityLap(startTime, activity, trackPoints);
|
|
@@ -42,7 +42,7 @@ import { useRouteList } from "../../../routes";
|
|
|
42
42
|
import { ActivitiesDBMigratorFactory } from "./migration/factory";
|
|
43
43
|
import { Injectable } from "../../../base/decorators/Injection";
|
|
44
44
|
import { useUnitConverter } from "../../../i18n";
|
|
45
|
-
export const DB_VERSION = '
|
|
45
|
+
export const DB_VERSION = '5';
|
|
46
46
|
export const DB_NAME = 'db';
|
|
47
47
|
let ActivitiesRepository = (() => {
|
|
48
48
|
let _classDecorators = [Singleton];
|
|
@@ -33,7 +33,7 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
|
|
|
33
33
|
done = true;
|
|
34
34
|
};
|
|
35
35
|
import { useRouteList } from "../../../routes";
|
|
36
|
-
import { DEFAULT_ACTIVITY_TITLE } from "../model";
|
|
36
|
+
import { DEFAULT_ACTIVITY_TITLE, DEFAULT_SPORT_ACTIVITY_TITLE } from "../model";
|
|
37
37
|
import { ActivitiesRepository, DB_VERSION } from "../repo";
|
|
38
38
|
import { getBindings } from "../../../api";
|
|
39
39
|
import { useOnlineStatusMonitoring } from "../../../monitoring";
|
|
@@ -115,7 +115,8 @@ let Activity = (() => {
|
|
|
115
115
|
}
|
|
116
116
|
getTitle() {
|
|
117
117
|
let title = this.info.summary.title;
|
|
118
|
-
|
|
118
|
+
const sport = this.details?.sport ?? 'cycling';
|
|
119
|
+
if (title === DEFAULT_ACTIVITY_TITLE || title === DEFAULT_SPORT_ACTIVITY_TITLE[sport]) {
|
|
119
120
|
if (this.details?.route?.title) {
|
|
120
121
|
title = this.details?.route.title;
|
|
121
122
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export * from './activity';
|
|
2
|
-
import { DEFAULT_ACTIVITY_TITLE } from "../model";
|
|
2
|
+
import { DEFAULT_ACTIVITY_TITLE, DEFAULT_SPORT_ACTIVITY_TITLE } from "../model";
|
|
3
3
|
export const buildSummary = (activity, proposedName) => {
|
|
4
4
|
const { id, route, screenshots, startTime: startTimeUTC, time: rideTime, distance, startPos, endPos, realityFactor = 100, links, laps, fileName } = activity;
|
|
5
5
|
let name = proposedName ?? activity.name;
|
|
@@ -10,7 +10,8 @@ export const buildSummary = (activity, proposedName) => {
|
|
|
10
10
|
const previewImage = preview?.fileName;
|
|
11
11
|
const totalElevation = activity.totalElevation;
|
|
12
12
|
let title = activity.title;
|
|
13
|
-
|
|
13
|
+
const sport = activity.sport ?? 'cycling';
|
|
14
|
+
if (title === DEFAULT_ACTIVITY_TITLE || title === DEFAULT_SPORT_ACTIVITY_TITLE[sport]) {
|
|
14
15
|
if (activity.routeType === 'Video' && activity.route)
|
|
15
16
|
title = activity.route.title ?? activity.route.name;
|
|
16
17
|
else if (activity.route)
|
|
@@ -40,7 +40,7 @@ import { useUserSettings } from "../../settings";
|
|
|
40
40
|
import { formatDateTime, formatNumber, formatTime, getLegacyInterface, waitNextTick } from "../../utils";
|
|
41
41
|
import { IncyclistCapability } from "incyclist-devices";
|
|
42
42
|
import { useDeviceConfiguration, useDeviceRide } from "../../devices";
|
|
43
|
-
import { ActivitiesRepository, ActivityConverter, ActivityConverterFactory, DB_VERSION,
|
|
43
|
+
import { ActivitiesRepository, ActivityConverter, ActivityConverterFactory, DB_VERSION, DEFAULT_SPORT_ACTIVITY_TITLE } from "../base";
|
|
44
44
|
import { v4 as generateUUID } from 'uuid';
|
|
45
45
|
import { addDetails, checkIsLoop, getElevationGainAt, getNextPosition, getPosition, getRouteHash, validateRoute } from "../../routes/base/utils/route";
|
|
46
46
|
import { Route } from "../../routes/base/model/route";
|
|
@@ -56,7 +56,6 @@ import { Injectable } from "../../base/decorators";
|
|
|
56
56
|
import { useUnitConverter } from "../../i18n";
|
|
57
57
|
import { createUIActivityDetails } from "../list/utils";
|
|
58
58
|
import { sleep } from "../../utils/sleep";
|
|
59
|
-
import { EventEmitter } from "node:events";
|
|
60
59
|
const SAVE_INTERVAL = 5000;
|
|
61
60
|
let ActivityRideService = (() => {
|
|
62
61
|
let _classDecorators = [Singleton];
|
|
@@ -100,6 +99,11 @@ let ActivityRideService = (() => {
|
|
|
100
99
|
deviceDataHandler = this.onDeviceData.bind(this);
|
|
101
100
|
gearChangeHandler = this.onGearChange.bind(this);
|
|
102
101
|
dataHealthHandler = this.onDeviceHealthUpdate.bind(this);
|
|
102
|
+
speedHandler = {
|
|
103
|
+
'cycling': this.getCyclingSpeed.bind(this),
|
|
104
|
+
'rowing': this.getRowingPace.bind(this),
|
|
105
|
+
'running': this.getRunningPace.bind(this)
|
|
106
|
+
};
|
|
103
107
|
current;
|
|
104
108
|
constructor() {
|
|
105
109
|
super('ActivityRide');
|
|
@@ -162,7 +166,7 @@ let ActivityRideService = (() => {
|
|
|
162
166
|
this.isSaveDone = false;
|
|
163
167
|
this.isDonateShown = false;
|
|
164
168
|
this.isSummaryShown = false;
|
|
165
|
-
this.logEvent({ message: 'activity started' });
|
|
169
|
+
this.logEvent({ message: 'activity started', sport: this.activity.sport ?? 'cycling' });
|
|
166
170
|
this.startWorker();
|
|
167
171
|
this.enableDeviceHealthCheck();
|
|
168
172
|
this.emit('started');
|
|
@@ -260,6 +264,34 @@ let ActivityRideService = (() => {
|
|
|
260
264
|
return [];
|
|
261
265
|
}
|
|
262
266
|
}
|
|
267
|
+
getRowingPace(speed) {
|
|
268
|
+
if (speed > 0) {
|
|
269
|
+
const value = 30 / speed;
|
|
270
|
+
const min = Math.floor(value);
|
|
271
|
+
const sec = Math.floor((value - min) * 60);
|
|
272
|
+
const p = sec < 10 ? '0' : '';
|
|
273
|
+
return { value: `${min}:${p}${sec}`, unit: '' };
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
return { value: '', unit: '' };
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
getRunningPace(speed) {
|
|
280
|
+
if (speed > 0) {
|
|
281
|
+
const value = 60 / speed;
|
|
282
|
+
const min = Math.floor(value);
|
|
283
|
+
const sec = Math.floor((value - min) * 60);
|
|
284
|
+
const p = sec < 10 ? '0' : '';
|
|
285
|
+
return { value: `${min}:${p}${sec}`, unit: '' };
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
return { value: '', unit: '' };
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
getCyclingSpeed(speed) {
|
|
292
|
+
const [C, U] = this.getUnitConversionShortcuts();
|
|
293
|
+
return { value: C(speed, 'speed')?.toFixed(1) ?? '', unit: U('speed') };
|
|
294
|
+
}
|
|
263
295
|
buildDashboardInfo(currentValues, avgMaxStats, display) {
|
|
264
296
|
const { distance, time, speed, power, slope, heartrate, cadence, distanceRemaining, timeRemaining, gear } = currentValues;
|
|
265
297
|
const { speedDetails, powerDetails, elevationGain, heartrateDetails, cadenceDetails } = avgMaxStats;
|
|
@@ -277,9 +309,10 @@ let ActivityRideService = (() => {
|
|
|
277
309
|
{ value: distanceRemaining === undefined ? undefined : `-${C(distanceRemaining, 'distance', { from: 'km' }).toFixed(2)}` }
|
|
278
310
|
]
|
|
279
311
|
});
|
|
312
|
+
const sport = this.activity.sport ?? 'cycling';
|
|
280
313
|
info.push({ title: 'Speed',
|
|
281
314
|
data: [
|
|
282
|
-
|
|
315
|
+
this.speedHandler[sport](speed),
|
|
283
316
|
speedDetails
|
|
284
317
|
],
|
|
285
318
|
dataState: this.current.dataState?.speed
|
|
@@ -291,7 +324,9 @@ let ActivityRideService = (() => {
|
|
|
291
324
|
info.push({ title: 'Slope', data: [{ value: formatNumber(slope, 1), unit: '%', info: slopeInfo }, elevationGain] });
|
|
292
325
|
}
|
|
293
326
|
info.push({ title: 'Heartrate', data: [{ value: formatNumber(heartrate, 0), unit: 'bpm' }, heartrateDetails], dataState: this.current.dataState?.heartrate });
|
|
294
|
-
|
|
327
|
+
const cadenceUnit = this.activity.sport === 'rowing' ? 'spm' : 'rpm';
|
|
328
|
+
const cadenceInfo = { title: 'Cadence', data: [{ value: formatNumber(cadence, 0), unit: cadenceUnit }, cadenceDetails], dataState: this.current.dataState?.cadence };
|
|
329
|
+
info.push(cadenceInfo);
|
|
295
330
|
if (gear) {
|
|
296
331
|
info.push({ title: 'Gear', data: [{ value: gear }] });
|
|
297
332
|
}
|
|
@@ -328,7 +363,11 @@ let ActivityRideService = (() => {
|
|
|
328
363
|
const [C, U] = this.getUnitConversionShortcuts();
|
|
329
364
|
const stats = this.activity?.stats;
|
|
330
365
|
const maxSpeed = stats?.speed?.max;
|
|
331
|
-
const
|
|
366
|
+
const sport = this.activity.sport ?? 'cycling';
|
|
367
|
+
const data = this.speedHandler[sport](maxSpeed);
|
|
368
|
+
const speedDetails = this.activity.sport === 'cycling' ?
|
|
369
|
+
{ value: formatNumber(C(maxSpeed, 'speed'), 1), label: 'max' } :
|
|
370
|
+
{ value: data.value, label: 'max' };
|
|
332
371
|
const powerDetails = { value: formatNumber(stats?.power?.max, 0), label: 'max' };
|
|
333
372
|
const heartrateDetails = { value: formatNumber(stats?.hrm?.max, 0), label: 'max' };
|
|
334
373
|
const cadenceDetails = { value: formatNumber(stats?.cadence?.max, 0), label: 'max' };
|
|
@@ -345,7 +384,11 @@ let ActivityRideService = (() => {
|
|
|
345
384
|
const [C, U] = this.getUnitConversionShortcuts();
|
|
346
385
|
const stats = this.activity?.stats;
|
|
347
386
|
const avgSpeed = stats?.speed?.avg;
|
|
348
|
-
const
|
|
387
|
+
const sport = this.activity.sport ?? 'cycling';
|
|
388
|
+
const data = this.speedHandler[sport](avgSpeed);
|
|
389
|
+
const speedDetails = this.activity.sport === 'cycling' ?
|
|
390
|
+
{ value: formatNumber(C(avgSpeed, 'speed'), 1), label: 'avg' } :
|
|
391
|
+
{ value: data.value, label: 'max' };
|
|
349
392
|
const powerDetails = { value: formatNumber(stats?.power?.avg, 0), label: 'avg' };
|
|
350
393
|
const heartrateDetails = { value: formatNumber(stats?.hrm?.avg, 0), label: 'avg' };
|
|
351
394
|
const cadenceDetails = { value: formatNumber(stats?.cadence?.avg, 0), label: 'avg' };
|
|
@@ -460,47 +503,17 @@ let ActivityRideService = (() => {
|
|
|
460
503
|
};
|
|
461
504
|
const run = async () => {
|
|
462
505
|
await sleep(5);
|
|
463
|
-
let cntCompleted = 0;
|
|
464
506
|
let success = false;
|
|
465
|
-
const localEmitter = new EventEmitter();
|
|
466
|
-
const incCompleted = () => {
|
|
467
|
-
cntCompleted++;
|
|
468
|
-
if (cntCompleted >= 2) {
|
|
469
|
-
localEmitter.emit('completed');
|
|
470
|
-
}
|
|
471
|
-
};
|
|
472
|
-
localEmitter.once('completed', () => {
|
|
473
|
-
sleep(0).then(() => {
|
|
474
|
-
delete this.saveObserver;
|
|
475
|
-
});
|
|
476
|
-
});
|
|
477
507
|
try {
|
|
478
508
|
emit('start', success);
|
|
479
509
|
await this._save();
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
if (convertSuccess) {
|
|
485
|
-
format = 'TCX';
|
|
486
|
-
this.convert('FIT')
|
|
487
|
-
.then(incCompleted)
|
|
488
|
-
.catch(incCompleted);
|
|
489
|
-
}
|
|
490
|
-
else {
|
|
491
|
-
convertSuccess = await this.convert('FIT');
|
|
492
|
-
cntCompleted++;
|
|
493
|
-
if (convertSuccess)
|
|
494
|
-
format = 'FIT';
|
|
495
|
-
}
|
|
496
|
-
if (convertSuccess) {
|
|
497
|
-
uploadSuccess = await this.upload(format);
|
|
498
|
-
}
|
|
499
|
-
success = convertSuccess && uploadSuccess;
|
|
510
|
+
success = await this._convertAndUpload();
|
|
511
|
+
sleep(0).then(() => {
|
|
512
|
+
delete this.saveObserver;
|
|
513
|
+
});
|
|
500
514
|
this.isSaveDone = true;
|
|
501
515
|
}
|
|
502
516
|
catch {
|
|
503
|
-
cntCompleted++;
|
|
504
517
|
success = false;
|
|
505
518
|
}
|
|
506
519
|
emit('done', success);
|
|
@@ -830,6 +843,29 @@ let ActivityRideService = (() => {
|
|
|
830
843
|
this.logError(err, '_save');
|
|
831
844
|
}
|
|
832
845
|
}
|
|
846
|
+
async _convertAndUpload() {
|
|
847
|
+
const successes = [];
|
|
848
|
+
const promises = [
|
|
849
|
+
this.convert('TCX').then(() => successes.push('tcx')),
|
|
850
|
+
this.convert('FIT').then(() => successes.push('fit')),
|
|
851
|
+
];
|
|
852
|
+
await Promise.allSettled(promises);
|
|
853
|
+
this.logEvent({ message: 'conversion finished, ' });
|
|
854
|
+
const convertSuccess = successes.length > 0;
|
|
855
|
+
let uploadSuccess;
|
|
856
|
+
let format = 'tcx';
|
|
857
|
+
if (this.activity.sport === 'rowing') {
|
|
858
|
+
if (successes.some(e => e === 'fit'))
|
|
859
|
+
format = 'fit';
|
|
860
|
+
}
|
|
861
|
+
else if (!successes.some(e => e === 'tcx')) {
|
|
862
|
+
format = 'fit';
|
|
863
|
+
}
|
|
864
|
+
if (convertSuccess) {
|
|
865
|
+
uploadSuccess = await this.upload(format);
|
|
866
|
+
}
|
|
867
|
+
return convertSuccess && uploadSuccess;
|
|
868
|
+
}
|
|
833
869
|
getSaveInterval() {
|
|
834
870
|
return SAVE_INTERVAL;
|
|
835
871
|
}
|
|
@@ -997,7 +1033,8 @@ let ActivityRideService = (() => {
|
|
|
997
1033
|
this.current.position = undefined;
|
|
998
1034
|
}
|
|
999
1035
|
}
|
|
1000
|
-
const
|
|
1036
|
+
const sport = this.getDeviceRide().getSport() ?? 'cycling';
|
|
1037
|
+
const title = DEFAULT_SPORT_ACTIVITY_TITLE[sport];
|
|
1001
1038
|
const id = requestedId ?? generateUUID();
|
|
1002
1039
|
const date = formatDateTime(new Date(), "%Y%m%d%H%M%S", false);
|
|
1003
1040
|
const name = `${title}-${date}`;
|
|
@@ -1024,7 +1061,8 @@ let ActivityRideService = (() => {
|
|
|
1024
1061
|
totalElevation: 0,
|
|
1025
1062
|
logs: [],
|
|
1026
1063
|
startPos, endPos, segment, realityFactor,
|
|
1027
|
-
fileName
|
|
1064
|
+
fileName,
|
|
1065
|
+
sport
|
|
1028
1066
|
};
|
|
1029
1067
|
return activity;
|
|
1030
1068
|
}
|
|
@@ -129,7 +129,6 @@ let DeviceRideService = (() => {
|
|
|
129
129
|
return this.data;
|
|
130
130
|
}
|
|
131
131
|
pauseLogging() {
|
|
132
|
-
console.log('# PAUSE LOGGING');
|
|
133
132
|
this.logPaused = true;
|
|
134
133
|
const interfaces = this.getEnabledInterfaces();
|
|
135
134
|
interfaces.forEach(i => i.pauseLogging());
|
|
@@ -139,7 +138,6 @@ let DeviceRideService = (() => {
|
|
|
139
138
|
.forEach(a => a.pauseLogging());
|
|
140
139
|
}
|
|
141
140
|
resumeLogging() {
|
|
142
|
-
console.log('# RESUME LOGGING');
|
|
143
141
|
this.logPaused = false;
|
|
144
142
|
const interfaces = this.getEnabledInterfaces();
|
|
145
143
|
interfaces.forEach(i => i.resumeLogging());
|
|
@@ -921,6 +919,19 @@ let DeviceRideService = (() => {
|
|
|
921
919
|
this.storeOriginalCyclingMode();
|
|
922
920
|
return this.startAdapters(adapters, 'start', props);
|
|
923
921
|
}
|
|
922
|
+
getSport() {
|
|
923
|
+
try {
|
|
924
|
+
const info = this.getControlAdapter();
|
|
925
|
+
const adapter = info?.adapter;
|
|
926
|
+
const sports = adapter?.getSupportedSports?.() ?? ['cycling'];
|
|
927
|
+
this.logEvent({ message: '[DeviceRide] getSports', adapter: info.adapter.getName(), sports: sports.join(',') });
|
|
928
|
+
return sports[0];
|
|
929
|
+
}
|
|
930
|
+
catch (err) {
|
|
931
|
+
this.logError(err, 'getSports');
|
|
932
|
+
return 'cycling';
|
|
933
|
+
}
|
|
934
|
+
}
|
|
924
935
|
async startRetry(props) {
|
|
925
936
|
await this.lazyInit();
|
|
926
937
|
const selected = this.getRideAdapters();
|
|
@@ -200,8 +200,8 @@ let RouteDisplayService = (() => {
|
|
|
200
200
|
const nearbyRides = this.getNearbyRidesProps(props);
|
|
201
201
|
const [C, U] = this.getUnitConversionShortcuts();
|
|
202
202
|
const isLoop = this.currentRoute?.description?.isLoop;
|
|
203
|
-
const xScale = { value: C(1, 'distance', { from:
|
|
204
|
-
const yScale = { value: C(1, 'elevation', { from:
|
|
203
|
+
const xScale = { value: C(1, 'distance', { from: 'm' }), unit: U('distance') };
|
|
204
|
+
const yScale = { value: C(1, 'elevation', { from: 'm' }), unit: U('elevation') };
|
|
205
205
|
const mapStartPos = (isLoop && !loopOverwrite) ? undefined : startPos;
|
|
206
206
|
return {
|
|
207
207
|
...parent,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { EventLogger } from 'gd-eventlog';
|
|
2
2
|
import { ActivityDetails, ActivityLogRecord, FitExportActivity, FitLapEntry, FitLogEntry, LapSummary } from '../../model';
|
|
3
|
+
import { Sport } from 'incyclist-devices';
|
|
3
4
|
export declare class LocalFitConverter {
|
|
4
5
|
protected logger: EventLogger;
|
|
5
6
|
constructor();
|
|
@@ -8,5 +9,6 @@ export declare class LocalFitConverter {
|
|
|
8
9
|
protected mapLapsToFit(laps: LapSummary[], activityStartTime: string): FitLapEntry[];
|
|
9
10
|
protected mapLogToFit(log: ActivityLogRecord): FitLogEntry;
|
|
10
11
|
protected encode(activity: FitExportActivity): ArrayBuffer;
|
|
12
|
+
protected mapSport(incyclistSport?: Sport): string;
|
|
11
13
|
protected getUserSettings(): import("../../../../settings").UserSettingsService;
|
|
12
14
|
}
|
|
@@ -4,6 +4,7 @@ import { IActivityConverter } from '../types';
|
|
|
4
4
|
import { ActivityDetails } from '../../model';
|
|
5
5
|
import { TcxLapMarker } from './types';
|
|
6
6
|
import { Step } from '../../../../workouts';
|
|
7
|
+
import { Sport } from 'incyclist-devices';
|
|
7
8
|
export declare class IncyclistAttribution extends AbstractSource {
|
|
8
9
|
toXml(): string;
|
|
9
10
|
constructor();
|
|
@@ -14,6 +15,7 @@ export declare class TcxConverter implements IActivityConverter {
|
|
|
14
15
|
protected logger: EventLogger;
|
|
15
16
|
constructor();
|
|
16
17
|
convert(activity: ActivityDetails): Promise<string>;
|
|
18
|
+
protected mapSport(incyclistSport: Sport): 'Running' | 'Biking' | 'Other';
|
|
17
19
|
protected createLaps(startTime: Date, activity: ActivityDetails, trackPoints: TrackPoint[]): ActivityLap[];
|
|
18
20
|
protected createActivityLap(startTime: Date, activity: ActivityDetails, trackPoints: TrackPoint[]): ActivityLap[];
|
|
19
21
|
protected getWorkoutLapMarkers(activity: ActivityDetails): Array<TcxLapMarker>;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { Sport } from "incyclist-devices";
|
|
1
2
|
import { FormattedNumber } from "../../../i18n";
|
|
2
3
|
import { RoutePoint } from "../../../routes/base/types";
|
|
3
4
|
import { Workout } from "../../../workouts";
|
|
4
5
|
export declare const DEFAULT_ACTIVITY_TITLE = "Incyclist Ride";
|
|
6
|
+
export declare const DEFAULT_SPORT_ACTIVITY_TITLE: Record<Sport, string>;
|
|
5
7
|
export type ActivityUser = {
|
|
6
8
|
uuid?: string;
|
|
7
9
|
weight: number;
|
|
@@ -141,6 +143,7 @@ export type FitExportActivity = {
|
|
|
141
143
|
href?: string;
|
|
142
144
|
user: FitUser;
|
|
143
145
|
screenshots: Array<FitScreenshots>;
|
|
146
|
+
sport?: Sport;
|
|
144
147
|
};
|
|
145
148
|
interface ActivityDetailsBase {
|
|
146
149
|
type?: ActivityType;
|
|
@@ -170,6 +173,7 @@ interface ActivityDetailsBase {
|
|
|
170
173
|
fitFileName?: string;
|
|
171
174
|
links?: ActivityAppLinks;
|
|
172
175
|
workout?: Workout;
|
|
176
|
+
sport?: Sport;
|
|
173
177
|
}
|
|
174
178
|
export interface ActivityDetails extends ActivityDetailsBase {
|
|
175
179
|
distance: number;
|
|
@@ -3,7 +3,7 @@ import { JsonRepository } from "../../../api";
|
|
|
3
3
|
import { Observer, PromiseObserver } from "../../../base/types/observer";
|
|
4
4
|
import { ActivityDetails, ActivityInfo } from "../model";
|
|
5
5
|
import { ActivitySearchCriteria } from "./types";
|
|
6
|
-
export declare const DB_VERSION = "
|
|
6
|
+
export declare const DB_VERSION = "5";
|
|
7
7
|
export declare const DB_NAME = "db";
|
|
8
8
|
export declare class ActivitiesRepository {
|
|
9
9
|
protected repo: JsonRepository;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { IncyclistService } from "../../base/service";
|
|
2
2
|
import { Observer, PromiseObserver } from "../../base/types/observer";
|
|
3
|
-
import { DeviceData } from "incyclist-devices";
|
|
3
|
+
import { DeviceData, Sport } from "incyclist-devices";
|
|
4
4
|
import { ExtendedIncyclistCapability, HealthStatus } from "../../devices";
|
|
5
5
|
import { ActivitiesRepository, ActivityConverterFactory, ActivityDetails, ActivityInfo, ActivityLogRecord, ActivityRouteType, ScreenShotInfo } from "../base";
|
|
6
6
|
import { FreeRideStartSettings } from "../../routes/list/types";
|
|
7
7
|
import { RouteSettings } from "../../routes/list/cards/types";
|
|
8
8
|
import { RoutePoint } from "../../routes/base/types";
|
|
9
|
-
import { ActivityState, ActivitySummaryDisplayProperties } from "./types";
|
|
9
|
+
import { ActivityDashboardDataItem, ActivityState, ActivitySummaryDisplayProperties } from "./types";
|
|
10
10
|
import { Route } from "../../routes/base/model/route";
|
|
11
11
|
import { ActivityStatsCalculator } from "./stats";
|
|
12
12
|
import { ActivityDuration } from "./duration";
|
|
@@ -37,6 +37,7 @@ export declare class ActivityRideService extends IncyclistService {
|
|
|
37
37
|
protected deviceDataHandler: any;
|
|
38
38
|
protected gearChangeHandler: any;
|
|
39
39
|
protected dataHealthHandler: any;
|
|
40
|
+
protected speedHandler: Record<Sport, (speed: number) => ActivityDashboardDataItem>;
|
|
40
41
|
protected current: {
|
|
41
42
|
route?: Route;
|
|
42
43
|
position?: RoutePoint;
|
|
@@ -67,6 +68,9 @@ export declare class ActivityRideService extends IncyclistService {
|
|
|
67
68
|
resume(requester?: 'user' | 'system'): void;
|
|
68
69
|
ignoreEndPos(): void;
|
|
69
70
|
getDashboardDisplayProperties(): any[];
|
|
71
|
+
protected getRowingPace(speed: number): ActivityDashboardDataItem;
|
|
72
|
+
protected getRunningPace(speed: number): ActivityDashboardDataItem;
|
|
73
|
+
protected getCyclingSpeed(speed: number): ActivityDashboardDataItem;
|
|
70
74
|
protected buildDashboardInfo(currentValues: any, avgMaxStats: any, display: any): any[];
|
|
71
75
|
getCurrentValues(): {
|
|
72
76
|
position: {};
|
|
@@ -85,7 +89,7 @@ export declare class ActivityRideService extends IncyclistService {
|
|
|
85
89
|
};
|
|
86
90
|
protected getAverageValues(): {
|
|
87
91
|
speedDetails: {
|
|
88
|
-
value: string;
|
|
92
|
+
value: string | number;
|
|
89
93
|
label: string;
|
|
90
94
|
};
|
|
91
95
|
powerDetails: {
|
|
@@ -114,7 +118,7 @@ export declare class ActivityRideService extends IncyclistService {
|
|
|
114
118
|
};
|
|
115
119
|
protected getMaximumValues(): {
|
|
116
120
|
speedDetails: {
|
|
117
|
-
value: string;
|
|
121
|
+
value: string | number;
|
|
118
122
|
label: string;
|
|
119
123
|
};
|
|
120
124
|
powerDetails: {
|
|
@@ -173,6 +177,7 @@ export declare class ActivityRideService extends IncyclistService {
|
|
|
173
177
|
protected onDeviceHealthUpdate(udid: string, status: HealthStatus, capabilities: Array<ExtendedIncyclistCapability>): void;
|
|
174
178
|
emit(eventName: string | symbol, ...args: any[]): boolean;
|
|
175
179
|
protected _save(): Promise<void>;
|
|
180
|
+
protected _convertAndUpload(): Promise<boolean>;
|
|
176
181
|
protected getSaveInterval(): number;
|
|
177
182
|
protected updateRepo(): Promise<void>;
|
|
178
183
|
protected getTargetFileName(format: string): Promise<string>;
|
|
@@ -188,7 +193,7 @@ export declare class ActivityRideService extends IncyclistService {
|
|
|
188
193
|
protected update(): void;
|
|
189
194
|
protected isLoop(): boolean;
|
|
190
195
|
protected createFreeRide(settings: FreeRideStartSettings): Route;
|
|
191
|
-
protected getMode(routeType: ActivityRouteType): "
|
|
196
|
+
protected getMode(routeType: ActivityRouteType): "workout" | "video" | "follow route" | "free ride";
|
|
192
197
|
protected updateActivityTime(): void;
|
|
193
198
|
getRideProps(): any;
|
|
194
199
|
protected logActivityUpdateMessage(): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AdapterInfo, IncyclistDeviceSettings } from "../configuration";
|
|
2
2
|
import { AdapterRideInfo, AdapterStateInfo, LegacyRoute, PreparedRoute, RideServiceCheckFilter, RideServiceDeviceProperties } from "./types";
|
|
3
|
-
import { CyclingMode, DeviceData, DeviceSettings, ICyclingMode, IncyclistCapability, IncyclistDeviceAdapter, IncyclistInterface, UpdateRequest } from "incyclist-devices";
|
|
3
|
+
import { CyclingMode, DeviceData, DeviceSettings, ICyclingMode, IncyclistCapability, IncyclistDeviceAdapter, IncyclistInterface, Sport, UpdateRequest } from "incyclist-devices";
|
|
4
4
|
import { IncyclistService } from "../../base/service";
|
|
5
5
|
import { Route } from "../../routes/base/model/route";
|
|
6
6
|
import { RouteApiDetail } from "../../routes/base/api/types";
|
|
@@ -92,6 +92,7 @@ export declare class DeviceRideService extends IncyclistService {
|
|
|
92
92
|
protected stopDuringInterfaceRestart(unhealthy: AdapterRideInfo): Promise<void>;
|
|
93
93
|
private reconnectSingle;
|
|
94
94
|
start(props: RideServiceDeviceProperties): Promise<boolean>;
|
|
95
|
+
getSport(): Sport;
|
|
95
96
|
startRetry(props: RideServiceDeviceProperties): Promise<boolean>;
|
|
96
97
|
cancelStart(): Promise<boolean>;
|
|
97
98
|
registerOnDataHandler(adapters: AdapterRideInfo[]): void;
|
package/lib/types/index.d.ts
CHANGED
|
@@ -120,7 +120,7 @@ export declare class RideDisplayService extends IncyclistService implements ICur
|
|
|
120
120
|
protected isSimulator(): boolean;
|
|
121
121
|
protected getBike(): string;
|
|
122
122
|
protected getBikeInterface(): string;
|
|
123
|
-
protected getLogRideMode(): "
|
|
123
|
+
protected getLogRideMode(): "workout" | "video" | "free-ride" | "follow-route";
|
|
124
124
|
protected isDebug(): boolean;
|
|
125
125
|
protected isVirtualShiftingEnabled(): boolean;
|
|
126
126
|
protected getActivityRide(): import("../../activities").ActivityRideService;
|
|
@@ -81,7 +81,7 @@ export declare class RouteCard extends BaseCard implements Card<Route> {
|
|
|
81
81
|
getCurrentDownload(): Observer;
|
|
82
82
|
getVideoDir(): string;
|
|
83
83
|
setVideoDir(dir: string): void;
|
|
84
|
-
onVideoSelected(info: FileInfo): Promise<"
|
|
84
|
+
onVideoSelected(info: FileInfo): Promise<"Unsupported video format - Please select MP4 or AVI" | "Could not open file">;
|
|
85
85
|
download(): Observer;
|
|
86
86
|
stopDownload(immediate?: boolean): void;
|
|
87
87
|
deleteDownload(): Promise<void>;
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "incyclist-services",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.50",
|
|
4
4
|
"peerDependencies": {
|
|
5
5
|
"gd-eventlog": "^0.1.27"
|
|
6
6
|
},
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"@garmin/fitsdk": "^21.200.0",
|
|
9
9
|
"axios": "^1.15.0",
|
|
10
|
-
"incyclist-devices": "^3.0.
|
|
10
|
+
"incyclist-devices": "^3.0.13",
|
|
11
11
|
"promise.any": "^2.0.6",
|
|
12
12
|
"semver": "^7.7.4",
|
|
13
13
|
"tcx-builder": "^1.1.1",
|