incyclist-services 1.7.49-beta → 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/cjs/activities/active-rides/service.js +1 -1
- package/lib/cjs/activities/base/convert/converter.js +1 -1
- package/lib/cjs/activities/base/convert/fit/index.js +1 -0
- package/lib/cjs/activities/base/convert/fit/local-fit.js +208 -0
- package/lib/cjs/ride/route/RLVDisplayService.js +1 -1
- package/lib/cjs/routes/base/parsers/utils.js +1 -1
- package/lib/cjs/utils/formatting.js +1 -1
- package/lib/cjs/utils/geo.js +2 -1
- package/lib/cjs/utils/xml.js +1 -1
- package/lib/cjs/workouts/base/parsers/zwo/zwo.js +1 -1
- package/lib/esm/activities/active-rides/service.js +1 -1
- package/lib/esm/activities/base/convert/converter.js +2 -2
- package/lib/esm/activities/base/convert/fit/index.js +1 -0
- package/lib/esm/activities/base/convert/fit/local-fit.js +213 -0
- 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/RLVDisplayService.js +1 -1
- package/lib/esm/ride/route/RouteDisplayService.js +2 -2
- package/lib/esm/routes/base/parsers/utils.js +1 -1
- package/lib/esm/utils/formatting.js +1 -1
- package/lib/esm/utils/geo.js +2 -1
- package/lib/esm/utils/xml.js +1 -1
- package/lib/esm/workouts/base/parsers/zwo/zwo.js +1 -1
- package/lib/types/activities/base/convert/fit/index.d.ts +1 -0
- package/lib/types/activities/base/convert/fit/local-fit.d.ts +14 -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 +3 -2
|
@@ -392,7 +392,7 @@ let ActiveRidesService = (() => {
|
|
|
392
392
|
return 'Anonymous';
|
|
393
393
|
const names = ['Alex', 'Bart', 'Cosmas', 'Dirk', 'Ernesto', 'Frank', 'Guido', 'Hans', 'Irene', 'John', 'Kai', 'Lorenzo', 'Martin', 'Naijb', 'Oswaldo', 'Pete', 'Quentin', 'Rachel', 'Sophia', 'Trevor', 'Ute', 'Vivian', 'Wil', 'Xaver', 'Younes', 'Zoe'];
|
|
394
394
|
const fnKey = id.charAt(0).toLowerCase();
|
|
395
|
-
const idx = !Number.isNaN(Number.parseInt(fnKey)) ? Number.parseInt(fnKey) : fnKey.
|
|
395
|
+
const idx = !Number.isNaN(Number.parseInt(fnKey)) ? Number.parseInt(fnKey) : (fnKey.codePointAt(0) ?? 0) - 96;
|
|
396
396
|
const lnKey = id.charAt(0).toUpperCase();
|
|
397
397
|
const ln = !Number.isNaN(Number.parseInt(lnKey)) ? '' : lnKey;
|
|
398
398
|
return `${names[idx]}${ln}`;
|
|
@@ -9,7 +9,7 @@ class ActivityConverter {
|
|
|
9
9
|
static async convert(activity, format) {
|
|
10
10
|
if (!ActivityConverter.factory) {
|
|
11
11
|
ActivityConverter.factory = new factory_1.ActivityConverterFactory();
|
|
12
|
-
ActivityConverter.factory.add('fit', new fit_1.
|
|
12
|
+
ActivityConverter.factory.add('fit', new fit_1.LocalFitConverter());
|
|
13
13
|
ActivityConverter.factory.add('tcx', new tcx_1.TcxConverter());
|
|
14
14
|
}
|
|
15
15
|
return await ActivityConverter.factory.convert(activity, format);
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
3
|
+
var useValue = arguments.length > 2;
|
|
4
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
5
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
6
|
+
}
|
|
7
|
+
return useValue ? value : void 0;
|
|
8
|
+
};
|
|
9
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
10
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
11
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
12
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
13
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
14
|
+
var _, done = false;
|
|
15
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
16
|
+
var context = {};
|
|
17
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
18
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
19
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
20
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
21
|
+
if (kind === "accessor") {
|
|
22
|
+
if (result === void 0) continue;
|
|
23
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
24
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
25
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
26
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
27
|
+
}
|
|
28
|
+
else if (_ = accept(result)) {
|
|
29
|
+
if (kind === "field") initializers.unshift(_);
|
|
30
|
+
else descriptor[key] = _;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
34
|
+
done = true;
|
|
35
|
+
};
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.LocalFitConverter = void 0;
|
|
38
|
+
const fitsdk_1 = require("@garmin/fitsdk");
|
|
39
|
+
const gd_eventlog_1 = require("gd-eventlog");
|
|
40
|
+
const settings_1 = require("../../../../settings");
|
|
41
|
+
const decorators_1 = require("../../../../base/decorators");
|
|
42
|
+
const DEG_TO_SEMICIRCLES = (2 ** 31) / 180;
|
|
43
|
+
let LocalFitConverter = (() => {
|
|
44
|
+
let _instanceExtraInitializers = [];
|
|
45
|
+
let _getUserSettings_decorators;
|
|
46
|
+
return class LocalFitConverter {
|
|
47
|
+
static {
|
|
48
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
49
|
+
_getUserSettings_decorators = [decorators_1.Injectable];
|
|
50
|
+
__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);
|
|
51
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
52
|
+
}
|
|
53
|
+
logger = __runInitializers(this, _instanceExtraInitializers);
|
|
54
|
+
constructor() {
|
|
55
|
+
this.logger = new gd_eventlog_1.EventLogger('LocalFitExporter');
|
|
56
|
+
}
|
|
57
|
+
async convert(activity) {
|
|
58
|
+
try {
|
|
59
|
+
this.logger.logEvent({ message: 'convert start', format: 'FIT' });
|
|
60
|
+
const fitActivity = this.getFitActivity(activity);
|
|
61
|
+
const data = this.encode(fitActivity);
|
|
62
|
+
this.logger.logEvent({ message: 'convert success', format: 'FIT' });
|
|
63
|
+
return data;
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
this.logger.logEvent({ message: 'convert result', format: 'FIT', result: 'error', reason: err.message });
|
|
67
|
+
throw err;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
getFitActivity(activity) {
|
|
71
|
+
const { id, title, time, timeTotal, timePause, distance } = activity;
|
|
72
|
+
const status = 'created';
|
|
73
|
+
const startTime = new Date(activity.startTime).toISOString();
|
|
74
|
+
const logs = activity.logs.map(this.mapLogToFit.bind(this));
|
|
75
|
+
const screenshots = [];
|
|
76
|
+
const laps = this.mapLapsToFit(activity.laps ?? [], activity.startTime);
|
|
77
|
+
const user = {
|
|
78
|
+
id: this.getUserSettings().get('uuid', undefined),
|
|
79
|
+
weight: activity.user.weight
|
|
80
|
+
};
|
|
81
|
+
return { id, title, status, logs, laps, startTime, time, timeTotal, timePause, distance, user, screenshots };
|
|
82
|
+
}
|
|
83
|
+
mapLapsToFit(laps, activityStartTime) {
|
|
84
|
+
const activityStartMs = new Date(activityStartTime).getTime();
|
|
85
|
+
return laps.map((lap, i) => {
|
|
86
|
+
const prevDistance = i > 0 ? laps[i - 1].distance : 0;
|
|
87
|
+
return {
|
|
88
|
+
lapNo: lap.num,
|
|
89
|
+
startTime: new Date(lap.startTime).toISOString(),
|
|
90
|
+
stopTime: new Date(lap.startTime + lap.rideTime * 1000).toISOString(),
|
|
91
|
+
lapDistance: lap.distance - prevDistance,
|
|
92
|
+
totalDistance: lap.distance,
|
|
93
|
+
lapTime: lap.rideTime,
|
|
94
|
+
totalTime: (lap.startTime - activityStartMs) / 1000 + lap.rideTime,
|
|
95
|
+
};
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
mapLogToFit(log) {
|
|
99
|
+
const { time, speed, slope, cadence: cadenceOrg, heartrate: heartrateOrg, distance, power: powerOrg, lat, lng, elevation } = log;
|
|
100
|
+
const cadence = Math.round(cadenceOrg);
|
|
101
|
+
const heartrate = Math.round(heartrateOrg);
|
|
102
|
+
const power = Math.round(powerOrg);
|
|
103
|
+
return { time, speed, slope, cadence, heartrate, distance, power, lat, lon: lng, elevation };
|
|
104
|
+
}
|
|
105
|
+
encode(activity) {
|
|
106
|
+
const encoder = new fitsdk_1.Encoder();
|
|
107
|
+
const startTime = new Date(activity.startTime);
|
|
108
|
+
encoder.onMesg(fitsdk_1.Profile.MesgNum.FILE_ID, {
|
|
109
|
+
type: 'activity',
|
|
110
|
+
manufacturer: 'development',
|
|
111
|
+
product: 0,
|
|
112
|
+
timeCreated: startTime,
|
|
113
|
+
});
|
|
114
|
+
encoder.onMesg(fitsdk_1.Profile.MesgNum.EVENT, {
|
|
115
|
+
timestamp: startTime,
|
|
116
|
+
event: 'timer',
|
|
117
|
+
eventType: 'start',
|
|
118
|
+
data: 0,
|
|
119
|
+
});
|
|
120
|
+
let lastTimestampMs = startTime.getTime();
|
|
121
|
+
activity.logs.forEach((log) => {
|
|
122
|
+
if (log.time != null) {
|
|
123
|
+
lastTimestampMs = startTime.getTime() + log.time * 1000;
|
|
124
|
+
}
|
|
125
|
+
const record = {
|
|
126
|
+
timestamp: new Date(lastTimestampMs),
|
|
127
|
+
};
|
|
128
|
+
if (log.lat != null && log.lon != null) {
|
|
129
|
+
record.positionLat = Math.round(log.lat * DEG_TO_SEMICIRCLES);
|
|
130
|
+
record.positionLong = Math.round(log.lon * DEG_TO_SEMICIRCLES);
|
|
131
|
+
}
|
|
132
|
+
if (log.elevation != null)
|
|
133
|
+
record.altitude = log.elevation;
|
|
134
|
+
if (log.heartrate != null)
|
|
135
|
+
record.heartRate = log.heartrate;
|
|
136
|
+
if (log.cadence != null)
|
|
137
|
+
record.cadence = log.cadence;
|
|
138
|
+
if (log.distance != null)
|
|
139
|
+
record.distance = log.distance;
|
|
140
|
+
if (log.speed != null)
|
|
141
|
+
record.speed = log.speed / 3.6;
|
|
142
|
+
if (log.power != null)
|
|
143
|
+
record.power = log.power;
|
|
144
|
+
if (log.slope != null)
|
|
145
|
+
record.grade = log.slope;
|
|
146
|
+
encoder.onMesg(fitsdk_1.Profile.MesgNum.RECORD, record);
|
|
147
|
+
});
|
|
148
|
+
const endTime = new Date(lastTimestampMs);
|
|
149
|
+
encoder.onMesg(fitsdk_1.Profile.MesgNum.EVENT, {
|
|
150
|
+
timestamp: endTime,
|
|
151
|
+
event: 'timer',
|
|
152
|
+
eventType: 'stopAll',
|
|
153
|
+
data: 0,
|
|
154
|
+
});
|
|
155
|
+
if (activity.laps.length > 0) {
|
|
156
|
+
activity.laps.forEach(lap => {
|
|
157
|
+
const lapStart = new Date(lap.startTime);
|
|
158
|
+
const lapEnd = new Date(lap.stopTime);
|
|
159
|
+
encoder.onMesg(fitsdk_1.Profile.MesgNum.LAP, {
|
|
160
|
+
timestamp: lapEnd,
|
|
161
|
+
startTime: lapStart,
|
|
162
|
+
totalElapsedTime: lap.lapTime,
|
|
163
|
+
totalTimerTime: lap.lapTime,
|
|
164
|
+
totalDistance: lap.lapDistance,
|
|
165
|
+
event: 'lap',
|
|
166
|
+
eventType: 'stop',
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
encoder.onMesg(fitsdk_1.Profile.MesgNum.LAP, {
|
|
172
|
+
timestamp: endTime,
|
|
173
|
+
startTime,
|
|
174
|
+
totalElapsedTime: activity.timeTotal,
|
|
175
|
+
totalTimerTime: activity.time,
|
|
176
|
+
totalDistance: activity.distance,
|
|
177
|
+
event: 'lap',
|
|
178
|
+
eventType: 'stop',
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
encoder.onMesg(fitsdk_1.Profile.MesgNum.SESSION, {
|
|
182
|
+
timestamp: endTime,
|
|
183
|
+
startTime,
|
|
184
|
+
totalElapsedTime: activity.timeTotal,
|
|
185
|
+
totalTimerTime: activity.time,
|
|
186
|
+
totalDistance: activity.distance,
|
|
187
|
+
sport: 'cycling',
|
|
188
|
+
subSport: 'virtualActivity',
|
|
189
|
+
event: 'session',
|
|
190
|
+
eventType: 'stopDisableAll',
|
|
191
|
+
});
|
|
192
|
+
encoder.onMesg(fitsdk_1.Profile.MesgNum.ACTIVITY, {
|
|
193
|
+
timestamp: endTime,
|
|
194
|
+
totalTimerTime: activity.time,
|
|
195
|
+
numSessions: 1,
|
|
196
|
+
type: 'manual',
|
|
197
|
+
event: 'activity',
|
|
198
|
+
eventType: 'stop',
|
|
199
|
+
});
|
|
200
|
+
const uint8Array = encoder.close();
|
|
201
|
+
return uint8Array.buffer;
|
|
202
|
+
}
|
|
203
|
+
getUserSettings() {
|
|
204
|
+
return (0, settings_1.useUserSettings)();
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
})();
|
|
208
|
+
exports.LocalFitConverter = LocalFitConverter;
|
|
@@ -6,7 +6,7 @@ const formatDateTime = (date, fstr = '%Y%m%d%H%M%S', utc = false) => {
|
|
|
6
6
|
if (!(0, valid_1.valid)(date) || !(date instanceof Date))
|
|
7
7
|
return;
|
|
8
8
|
const prefix = utc ? 'getUTC' : 'get';
|
|
9
|
-
return fstr.
|
|
9
|
+
return fstr.replaceAll(/%[YmdHMS]/g, (m) => {
|
|
10
10
|
switch (m) {
|
|
11
11
|
case '%Y': return date[prefix + 'FullYear']();
|
|
12
12
|
case '%m':
|
package/lib/cjs/utils/geo.js
CHANGED
|
@@ -17,7 +17,8 @@ const calculateDistance = (lat1, lon1, lat2, lon2, radius = rEarth * 1000) => {
|
|
|
17
17
|
return distance;
|
|
18
18
|
};
|
|
19
19
|
exports.calculateDistance = calculateDistance;
|
|
20
|
-
const
|
|
20
|
+
const DEFAULT_DISTANCE_PROPS = { abs: true, latLng: true };
|
|
21
|
+
const distanceBetween = (p1, p2, props = DEFAULT_DISTANCE_PROPS) => {
|
|
21
22
|
if (p1 === undefined)
|
|
22
23
|
return 0;
|
|
23
24
|
let dist;
|
package/lib/cjs/utils/xml.js
CHANGED
|
@@ -17,7 +17,7 @@ const removeUTFBom = (str) => {
|
|
|
17
17
|
if (!str || typeof (str) !== 'string') {
|
|
18
18
|
return str;
|
|
19
19
|
}
|
|
20
|
-
while (str.
|
|
20
|
+
while (str.codePointAt(0) === 0 || str.codePointAt(0) === 0xFEFF || str.codePointAt(0) === 0xFFFD)
|
|
21
21
|
str = str.substring(1);
|
|
22
22
|
return str;
|
|
23
23
|
};
|
|
@@ -216,7 +216,7 @@ class ZwoParser {
|
|
|
216
216
|
let tag;
|
|
217
217
|
parser.parseString(data, (err, result) => {
|
|
218
218
|
if (err) {
|
|
219
|
-
err.message = 'File contains error(s): ' + err.message.
|
|
219
|
+
err.message = 'File contains error(s): ' + err.message.replaceAll('\n', ' ');
|
|
220
220
|
this.logger.logEvent({ message: 'error', fn: 'parse()', error: err.message, stack: err.stack });
|
|
221
221
|
return reject(err);
|
|
222
222
|
}
|
|
@@ -389,7 +389,7 @@ let ActiveRidesService = (() => {
|
|
|
389
389
|
return 'Anonymous';
|
|
390
390
|
const names = ['Alex', 'Bart', 'Cosmas', 'Dirk', 'Ernesto', 'Frank', 'Guido', 'Hans', 'Irene', 'John', 'Kai', 'Lorenzo', 'Martin', 'Naijb', 'Oswaldo', 'Pete', 'Quentin', 'Rachel', 'Sophia', 'Trevor', 'Ute', 'Vivian', 'Wil', 'Xaver', 'Younes', 'Zoe'];
|
|
391
391
|
const fnKey = id.charAt(0).toLowerCase();
|
|
392
|
-
const idx = !Number.isNaN(Number.parseInt(fnKey)) ? Number.parseInt(fnKey) : fnKey.
|
|
392
|
+
const idx = !Number.isNaN(Number.parseInt(fnKey)) ? Number.parseInt(fnKey) : (fnKey.codePointAt(0) ?? 0) - 96;
|
|
393
393
|
const lnKey = id.charAt(0).toUpperCase();
|
|
394
394
|
const ln = !Number.isNaN(Number.parseInt(lnKey)) ? '' : lnKey;
|
|
395
395
|
return `${names[idx]}${ln}`;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { ActivityConverterFactory } from "./factory";
|
|
2
|
-
import {
|
|
2
|
+
import { LocalFitConverter } from "./fit";
|
|
3
3
|
import { TcxConverter } from "./tcx";
|
|
4
4
|
export class ActivityConverter {
|
|
5
5
|
static factory;
|
|
6
6
|
static async convert(activity, format) {
|
|
7
7
|
if (!ActivityConverter.factory) {
|
|
8
8
|
ActivityConverter.factory = new ActivityConverterFactory();
|
|
9
|
-
ActivityConverter.factory.add('fit', new
|
|
9
|
+
ActivityConverter.factory.add('fit', new LocalFitConverter());
|
|
10
10
|
ActivityConverter.factory.add('tcx', new TcxConverter());
|
|
11
11
|
}
|
|
12
12
|
return await ActivityConverter.factory.convert(activity, format);
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
2
|
+
var useValue = arguments.length > 2;
|
|
3
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
4
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
5
|
+
}
|
|
6
|
+
return useValue ? value : void 0;
|
|
7
|
+
};
|
|
8
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
9
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
10
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
11
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
12
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
13
|
+
var _, done = false;
|
|
14
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
15
|
+
var context = {};
|
|
16
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
17
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
18
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
19
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
20
|
+
if (kind === "accessor") {
|
|
21
|
+
if (result === void 0) continue;
|
|
22
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
23
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
24
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
25
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
26
|
+
}
|
|
27
|
+
else if (_ = accept(result)) {
|
|
28
|
+
if (kind === "field") initializers.unshift(_);
|
|
29
|
+
else descriptor[key] = _;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
33
|
+
done = true;
|
|
34
|
+
};
|
|
35
|
+
import { Encoder, Profile } from '@garmin/fitsdk';
|
|
36
|
+
import { EventLogger } from 'gd-eventlog';
|
|
37
|
+
import { useUserSettings } from '../../../../settings';
|
|
38
|
+
import { Injectable } from '../../../../base/decorators';
|
|
39
|
+
const DEG_TO_SEMICIRCLES = (2 ** 31) / 180;
|
|
40
|
+
let LocalFitConverter = (() => {
|
|
41
|
+
let _instanceExtraInitializers = [];
|
|
42
|
+
let _getUserSettings_decorators;
|
|
43
|
+
return class LocalFitConverter {
|
|
44
|
+
static {
|
|
45
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
46
|
+
_getUserSettings_decorators = [Injectable];
|
|
47
|
+
__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);
|
|
48
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
49
|
+
}
|
|
50
|
+
logger = __runInitializers(this, _instanceExtraInitializers);
|
|
51
|
+
constructor() {
|
|
52
|
+
this.logger = new EventLogger('LocalFitExporter');
|
|
53
|
+
}
|
|
54
|
+
async convert(activity) {
|
|
55
|
+
try {
|
|
56
|
+
this.logger.logEvent({ message: 'convert start', format: 'FIT' });
|
|
57
|
+
const fitActivity = this.getFitActivity(activity);
|
|
58
|
+
const data = this.encode(fitActivity);
|
|
59
|
+
this.logger.logEvent({ message: 'convert success', format: 'FIT' });
|
|
60
|
+
return data;
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
this.logger.logEvent({ message: 'convert result', format: 'FIT', result: 'error', reason: err.message });
|
|
64
|
+
throw err;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
getFitActivity(activity) {
|
|
68
|
+
const { id, title, time, timeTotal, timePause, distance, sport = 'cycling' } = activity;
|
|
69
|
+
const status = 'created';
|
|
70
|
+
const startTime = new Date(activity.startTime).toISOString();
|
|
71
|
+
const logs = activity.logs.map(this.mapLogToFit.bind(this));
|
|
72
|
+
const screenshots = [];
|
|
73
|
+
const laps = this.mapLapsToFit(activity.laps ?? [], activity.startTime);
|
|
74
|
+
const user = {
|
|
75
|
+
id: this.getUserSettings().get('uuid', undefined),
|
|
76
|
+
weight: activity.user.weight
|
|
77
|
+
};
|
|
78
|
+
return { id, title, status, logs, laps, startTime, time, timeTotal, timePause, distance, user, screenshots, sport };
|
|
79
|
+
}
|
|
80
|
+
mapLapsToFit(laps, activityStartTime) {
|
|
81
|
+
const activityStartMs = new Date(activityStartTime).getTime();
|
|
82
|
+
return laps.map((lap, i) => {
|
|
83
|
+
const prevDistance = i > 0 ? laps[i - 1].distance : 0;
|
|
84
|
+
return {
|
|
85
|
+
lapNo: lap.num,
|
|
86
|
+
startTime: new Date(lap.startTime).toISOString(),
|
|
87
|
+
stopTime: new Date(lap.startTime + lap.rideTime * 1000).toISOString(),
|
|
88
|
+
lapDistance: lap.distance - prevDistance,
|
|
89
|
+
totalDistance: lap.distance,
|
|
90
|
+
lapTime: lap.rideTime,
|
|
91
|
+
totalTime: (lap.startTime - activityStartMs) / 1000 + lap.rideTime,
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
mapLogToFit(log) {
|
|
96
|
+
const { time, speed, slope, cadence: cadenceOrg, heartrate: heartrateOrg, distance, power: powerOrg, lat, lng, elevation } = log;
|
|
97
|
+
const cadence = Math.round(cadenceOrg);
|
|
98
|
+
const heartrate = Math.round(heartrateOrg);
|
|
99
|
+
const power = Math.round(powerOrg);
|
|
100
|
+
return { time, speed, slope, cadence, heartrate, distance, power, lat, lon: lng, elevation };
|
|
101
|
+
}
|
|
102
|
+
encode(activity) {
|
|
103
|
+
const encoder = new Encoder();
|
|
104
|
+
const startTime = new Date(activity.startTime);
|
|
105
|
+
encoder.onMesg(Profile.MesgNum.FILE_ID, {
|
|
106
|
+
type: 'activity',
|
|
107
|
+
manufacturer: 'development',
|
|
108
|
+
product: 0,
|
|
109
|
+
timeCreated: startTime,
|
|
110
|
+
});
|
|
111
|
+
encoder.onMesg(Profile.MesgNum.EVENT, {
|
|
112
|
+
timestamp: startTime,
|
|
113
|
+
event: 'timer',
|
|
114
|
+
eventType: 'start',
|
|
115
|
+
data: 0,
|
|
116
|
+
});
|
|
117
|
+
let lastTimestampMs = startTime.getTime();
|
|
118
|
+
activity.logs.forEach((log) => {
|
|
119
|
+
if (log.time != null) {
|
|
120
|
+
lastTimestampMs = startTime.getTime() + log.time * 1000;
|
|
121
|
+
}
|
|
122
|
+
const record = {
|
|
123
|
+
timestamp: new Date(lastTimestampMs),
|
|
124
|
+
};
|
|
125
|
+
if (log.lat != null && log.lon != null) {
|
|
126
|
+
record.positionLat = Math.round(log.lat * DEG_TO_SEMICIRCLES);
|
|
127
|
+
record.positionLong = Math.round(log.lon * DEG_TO_SEMICIRCLES);
|
|
128
|
+
}
|
|
129
|
+
if (log.elevation != null)
|
|
130
|
+
record.altitude = log.elevation;
|
|
131
|
+
if (log.heartrate != null)
|
|
132
|
+
record.heartRate = log.heartrate;
|
|
133
|
+
if (log.cadence != null)
|
|
134
|
+
record.cadence = log.cadence;
|
|
135
|
+
if (log.distance != null)
|
|
136
|
+
record.distance = log.distance;
|
|
137
|
+
if (log.speed != null)
|
|
138
|
+
record.speed = log.speed / 3.6;
|
|
139
|
+
if (log.power != null)
|
|
140
|
+
record.power = log.power;
|
|
141
|
+
if (log.slope != null)
|
|
142
|
+
record.grade = log.slope;
|
|
143
|
+
encoder.onMesg(Profile.MesgNum.RECORD, record);
|
|
144
|
+
});
|
|
145
|
+
const endTime = new Date(lastTimestampMs);
|
|
146
|
+
encoder.onMesg(Profile.MesgNum.EVENT, {
|
|
147
|
+
timestamp: endTime,
|
|
148
|
+
event: 'timer',
|
|
149
|
+
eventType: 'stopAll',
|
|
150
|
+
data: 0,
|
|
151
|
+
});
|
|
152
|
+
if (activity.laps.length > 0) {
|
|
153
|
+
activity.laps.forEach(lap => {
|
|
154
|
+
const lapStart = new Date(lap.startTime);
|
|
155
|
+
const lapEnd = new Date(lap.stopTime);
|
|
156
|
+
encoder.onMesg(Profile.MesgNum.LAP, {
|
|
157
|
+
timestamp: lapEnd,
|
|
158
|
+
startTime: lapStart,
|
|
159
|
+
totalElapsedTime: lap.lapTime,
|
|
160
|
+
totalTimerTime: lap.lapTime,
|
|
161
|
+
totalDistance: lap.lapDistance,
|
|
162
|
+
event: 'lap',
|
|
163
|
+
eventType: 'stop',
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
encoder.onMesg(Profile.MesgNum.LAP, {
|
|
169
|
+
timestamp: endTime,
|
|
170
|
+
startTime,
|
|
171
|
+
totalElapsedTime: activity.timeTotal,
|
|
172
|
+
totalTimerTime: activity.time,
|
|
173
|
+
totalDistance: activity.distance,
|
|
174
|
+
event: 'lap',
|
|
175
|
+
eventType: 'stop',
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
encoder.onMesg(Profile.MesgNum.SESSION, {
|
|
179
|
+
timestamp: endTime,
|
|
180
|
+
startTime,
|
|
181
|
+
totalElapsedTime: activity.timeTotal,
|
|
182
|
+
totalTimerTime: activity.time,
|
|
183
|
+
totalDistance: activity.distance,
|
|
184
|
+
sport: this.mapSport(activity.sport),
|
|
185
|
+
subSport: 'virtualActivity',
|
|
186
|
+
event: 'session',
|
|
187
|
+
eventType: 'stopDisableAll',
|
|
188
|
+
});
|
|
189
|
+
encoder.onMesg(Profile.MesgNum.ACTIVITY, {
|
|
190
|
+
timestamp: endTime,
|
|
191
|
+
totalTimerTime: activity.time,
|
|
192
|
+
numSessions: 1,
|
|
193
|
+
type: 'manual',
|
|
194
|
+
event: 'activity',
|
|
195
|
+
eventType: 'stop',
|
|
196
|
+
});
|
|
197
|
+
const uint8Array = encoder.close();
|
|
198
|
+
return uint8Array.buffer;
|
|
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
|
+
}
|
|
208
|
+
getUserSettings() {
|
|
209
|
+
return useUserSettings();
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
})();
|
|
213
|
+
export { LocalFitConverter };
|
|
@@ -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,
|
|
@@ -3,7 +3,7 @@ export const formatDateTime = (date, fstr = '%Y%m%d%H%M%S', utc = false) => {
|
|
|
3
3
|
if (!valid(date) || !(date instanceof Date))
|
|
4
4
|
return;
|
|
5
5
|
const prefix = utc ? 'getUTC' : 'get';
|
|
6
|
-
return fstr.
|
|
6
|
+
return fstr.replaceAll(/%[YmdHMS]/g, (m) => {
|
|
7
7
|
switch (m) {
|
|
8
8
|
case '%Y': return date[prefix + 'FullYear']();
|
|
9
9
|
case '%m':
|
package/lib/esm/utils/geo.js
CHANGED
|
@@ -12,7 +12,8 @@ export const calculateDistance = (lat1, lon1, lat2, lon2, radius = rEarth * 1000
|
|
|
12
12
|
const distance = abs(R * c);
|
|
13
13
|
return distance;
|
|
14
14
|
};
|
|
15
|
-
|
|
15
|
+
const DEFAULT_DISTANCE_PROPS = { abs: true, latLng: true };
|
|
16
|
+
export const distanceBetween = (p1, p2, props = DEFAULT_DISTANCE_PROPS) => {
|
|
16
17
|
if (p1 === undefined)
|
|
17
18
|
return 0;
|
|
18
19
|
let dist;
|
package/lib/esm/utils/xml.js
CHANGED
|
@@ -13,7 +13,7 @@ export const removeUTFBom = (str) => {
|
|
|
13
13
|
if (!str || typeof (str) !== 'string') {
|
|
14
14
|
return str;
|
|
15
15
|
}
|
|
16
|
-
while (str.
|
|
16
|
+
while (str.codePointAt(0) === 0 || str.codePointAt(0) === 0xFEFF || str.codePointAt(0) === 0xFFFD)
|
|
17
17
|
str = str.substring(1);
|
|
18
18
|
return str;
|
|
19
19
|
};
|
|
@@ -210,7 +210,7 @@ export class ZwoParser {
|
|
|
210
210
|
let tag;
|
|
211
211
|
parser.parseString(data, (err, result) => {
|
|
212
212
|
if (err) {
|
|
213
|
-
err.message = 'File contains error(s): ' + err.message.
|
|
213
|
+
err.message = 'File contains error(s): ' + err.message.replaceAll('\n', ' ');
|
|
214
214
|
this.logger.logEvent({ message: 'error', fn: 'parse()', error: err.message, stack: err.stack });
|
|
215
215
|
return reject(err);
|
|
216
216
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { EventLogger } from 'gd-eventlog';
|
|
2
|
+
import { ActivityDetails, ActivityLogRecord, FitExportActivity, FitLapEntry, FitLogEntry, LapSummary } from '../../model';
|
|
3
|
+
import { Sport } from 'incyclist-devices';
|
|
4
|
+
export declare class LocalFitConverter {
|
|
5
|
+
protected logger: EventLogger;
|
|
6
|
+
constructor();
|
|
7
|
+
convert(activity: ActivityDetails): Promise<ArrayBuffer>;
|
|
8
|
+
protected getFitActivity(activity: ActivityDetails): FitExportActivity;
|
|
9
|
+
protected mapLapsToFit(laps: LapSummary[], activityStartTime: string): FitLapEntry[];
|
|
10
|
+
protected mapLogToFit(log: ActivityLogRecord): FitLogEntry;
|
|
11
|
+
protected encode(activity: FitExportActivity): ArrayBuffer;
|
|
12
|
+
protected mapSport(incyclistSport?: Sport): string;
|
|
13
|
+
protected getUserSettings(): import("../../../../settings").UserSettingsService;
|
|
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,12 +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
|
+
"@garmin/fitsdk": "^21.200.0",
|
|
8
9
|
"axios": "^1.15.0",
|
|
9
|
-
"incyclist-devices": "^3.0.
|
|
10
|
+
"incyclist-devices": "^3.0.13",
|
|
10
11
|
"promise.any": "^2.0.6",
|
|
11
12
|
"semver": "^7.7.4",
|
|
12
13
|
"tcx-builder": "^1.1.1",
|