incyclist-devices 2.3.29 → 2.3.32

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