incyclist-devices 2.3.29 → 2.3.31

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.
@@ -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
  }
@@ -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,28 @@ 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;
33
47
  protected checkEmptyRequest(newRequest: UpdateRequest): void;
48
+ protected getVirtualShiftMode(): VirtshiftMode;
49
+ updateData(bikeData: IncyclistBikeData, log?: boolean): IncyclistBikeData;
34
50
  sendBikeUpdate(incoming: UpdateRequest): UpdateRequest;
51
+ protected setInitialGear(data: IncyclistBikeData): void;
52
+ protected getGearString(): string;
35
53
  }
54
+ export {};
@@ -1,13 +1,52 @@
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"));
8
42
  class SmartTrainerCyclingMode extends power_base_1.default {
9
43
  constructor(adapter, props) {
10
44
  super(adapter, props);
45
+ this.gearDelta = 0;
46
+ this.gearRatios = [
47
+ 0.75, 0.87, 0.99, 1.11, 1.23, 1.38, 1.53, 1.68, 1.86, 2.04, 2.22, 2.40,
48
+ 2.61, 2.82, 3.03, 3.24, 3.49, 3.74, 3.99, 4.24, 4.54, 4.84, 5.14, 5.49
49
+ ];
11
50
  this.initLogger('SmartTrainerMode');
12
51
  }
13
52
  getBikeInitRequest() {
@@ -23,7 +62,20 @@ class SmartTrainerCyclingMode extends power_base_1.default {
23
62
  return this.prevRequest;
24
63
  }
25
64
  }
26
- checkSlope(request, newRequest = {}) {
65
+ getConfig() {
66
+ const config = super.getConfig();
67
+ let virtshift = config.properties.find(p => p.key === 'virtshift');
68
+ if (!virtshift) {
69
+ virtshift = { key: 'virtshift', name: 'Virtual Shifting', description: 'Enable virtual shifting', type: types_1.CyclingModeProperyType.SingleSelect, options: ['Disabled', 'Enabled', 'Mixed'], default: 'Disabled' };
70
+ config.properties.push(virtshift);
71
+ }
72
+ if (this.adapter.supportsVirtualShifting()) {
73
+ virtshift.default = 'Enabled';
74
+ virtshift.options = ['Disabled', 'Enabled'];
75
+ }
76
+ return config;
77
+ }
78
+ checkSlopeNoShiftig(request, newRequest = {}) {
27
79
  if (request.slope !== undefined) {
28
80
  const targetSlope = newRequest.slope = parseFloat(request.slope.toFixed(1));
29
81
  this.data.slope = newRequest.slope;
@@ -36,6 +88,116 @@ class SmartTrainerCyclingMode extends power_base_1.default {
36
88
  }
37
89
  }
38
90
  }
91
+ checkSlopeWithAdapterShifting(request, newRequest = {}) {
92
+ return this.checkSlopeNoShiftig(request, newRequest);
93
+ }
94
+ getSlopeDelta() {
95
+ return this.gearDelta * 0.5;
96
+ }
97
+ checkSlopeWithSlopeDelta(request, newRequest = {}) {
98
+ if (request.slope !== undefined) {
99
+ const targetSlope = newRequest.slope = parseFloat(request.slope.toFixed(1));
100
+ this.data.slope = newRequest.slope;
101
+ const requestedSlope = targetSlope + this.getSlopeDelta();
102
+ try {
103
+ const slopeAdj = requestedSlope >= 0 ? this.getSetting('slopeAdj') : this.getSetting('slopeAdjDown');
104
+ newRequest.slope = slopeAdj !== undefined ? requestedSlope * slopeAdj / 100 : requestedSlope;
105
+ }
106
+ catch (_a) {
107
+ console.error('# Error calculating requested slope');
108
+ }
109
+ }
110
+ }
111
+ checkSlopeWithSimulatedShifting(request, newRequest = {}) {
112
+ var _a, _b, _c, _d;
113
+ if (this.gear === undefined) {
114
+ return this.checkSlopeNoShiftig(request, newRequest);
115
+ }
116
+ if (request.slope !== undefined) {
117
+ const prev = (_a = this.data.slope) !== null && _a !== void 0 ? _a : 0;
118
+ this.data.slope = parseFloat(request.slope.toFixed(1));
119
+ delete request.slope;
120
+ delete newRequest.slope;
121
+ this.simSlope = this.data.slope;
122
+ try {
123
+ const slopeAdj = this.simSlope >= 0 ? this.getSetting('slopeAdj') : this.getSetting('slopeAdjDown');
124
+ if (slopeAdj !== undefined)
125
+ this.simSlope = this.simSlope * slopeAdj / 100;
126
+ }
127
+ catch (_e) {
128
+ }
129
+ if (this.data.slope === prev) {
130
+ newRequest.targetPower = this.simPower;
131
+ return;
132
+ }
133
+ else {
134
+ const virtualSpeed = (0, calculations_1.calculateVirtualSpeed)(this.data.pedalRpm, this.gearRatios[this.gear - 1]);
135
+ const m = (_c = (_b = this.adapter) === null || _b === void 0 ? void 0 : _b.getWeight()) !== null && _c !== void 0 ? _c : 85;
136
+ this.simPower = calculations_1.default.calculatePower(m, virtualSpeed, (_d = this.simSlope) !== null && _d !== void 0 ? _d : 0);
137
+ newRequest.targetPower = this.simPower;
138
+ }
139
+ if (this.simPower < 0)
140
+ this.simPower = 0;
141
+ }
142
+ else {
143
+ newRequest.targetPower = this.simPower;
144
+ }
145
+ }
146
+ checkSlope(request, newRequest = {}) {
147
+ const virtshiftMode = this.getVirtualShiftMode();
148
+ switch (virtshiftMode) {
149
+ case 'SlopeDelta':
150
+ this.checkSlopeWithSlopeDelta(request, newRequest);
151
+ break;
152
+ case 'Simulated':
153
+ this.checkSlopeWithSimulatedShifting(request, newRequest);
154
+ break;
155
+ case 'Adapter':
156
+ this.checkSlopeWithAdapterShifting(request, newRequest);
157
+ break;
158
+ case 'Disabled':
159
+ default:
160
+ this.checkSlopeNoShiftig(request, newRequest);
161
+ break;
162
+ }
163
+ }
164
+ checkGearChange(request, newRequest = {}) {
165
+ var _a, _b, _c, _d;
166
+ const virtshiftMode = this.getVirtualShiftMode();
167
+ switch (virtshiftMode) {
168
+ case 'SlopeDelta':
169
+ if (request.gearDelta !== undefined) {
170
+ this.gearDelta += request.gearDelta;
171
+ request.slope = this.data.slope;
172
+ delete request.gearDelta;
173
+ }
174
+ break;
175
+ case 'Simulated':
176
+ if (request.gearDelta !== undefined) {
177
+ if (this.gear === undefined) {
178
+ this.gear = 10 + request.gearDelta;
179
+ }
180
+ else {
181
+ this.gear += request.gearDelta;
182
+ }
183
+ if (this.gear < 1) {
184
+ this.gear = 1;
185
+ }
186
+ if (this.gear > this.gearRatios.length) {
187
+ this.gear = this.gearRatios.length;
188
+ }
189
+ delete request.gearDelta;
190
+ const virtualSpeed = (0, calculations_1.calculateVirtualSpeed)(this.data.pedalRpm, this.gearRatios[this.gear - 1]);
191
+ const m = (_b = (_a = this.adapter) === null || _a === void 0 ? void 0 : _a.getWeight()) !== null && _b !== void 0 ? _b : 85;
192
+ 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);
193
+ }
194
+ break;
195
+ case 'Adapter':
196
+ case 'Disabled':
197
+ default:
198
+ break;
199
+ }
200
+ }
39
201
  checkEmptyRequest(newRequest) {
40
202
  if (Object.keys(newRequest).length === 0) {
41
203
  if (this.prevRequest) {
@@ -44,14 +206,56 @@ class SmartTrainerCyclingMode extends power_base_1.default {
44
206
  }
45
207
  }
46
208
  }
209
+ getVirtualShiftMode() {
210
+ const virtshiftMode = this.getSetting('virtshift');
211
+ if (virtshiftMode === 'Disabled') {
212
+ return 'Disabled';
213
+ }
214
+ if (this.adapter.supportsVirtualShifting()) {
215
+ if (virtshiftMode === 'Enabled') {
216
+ return 'Adapter';
217
+ }
218
+ }
219
+ else {
220
+ return virtshiftMode === 'Mixed' ? 'SlopeDelta' : 'Simulated';
221
+ }
222
+ }
223
+ updateData(bikeData, log) {
224
+ var _a, _b, _c, _d;
225
+ const prev = Object.assign({}, this.data);
226
+ const data = super.updateData(bikeData, log);
227
+ const mode = this.getVirtualShiftMode();
228
+ let virtualSpeed;
229
+ data.gearStr = this.getGearString();
230
+ if (mode !== 'Simulated') {
231
+ return data;
232
+ }
233
+ if (data.power > 0 && !this.tsStart) {
234
+ this.tsStart = Date.now();
235
+ }
236
+ if (this.gear === undefined && this.tsStart && data.power > 0 && (Date.now() - this.tsStart > 3000)) {
237
+ this.setInitialGear(data);
238
+ data.gearStr = this.getGearString();
239
+ }
240
+ else if (this.gear !== undefined) {
241
+ if (prev.power !== data.power || prev.pedalRpm !== data.pedalRpm) {
242
+ virtualSpeed = (0, calculations_1.calculateVirtualSpeed)(data.pedalRpm, this.gearRatios[this.gear - 1]);
243
+ const m = (_b = (_a = this.adapter) === null || _a === void 0 ? void 0 : _a.getWeight()) !== null && _b !== void 0 ? _b : 85;
244
+ 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);
245
+ }
246
+ }
247
+ return data;
248
+ }
47
249
  sendBikeUpdate(incoming) {
48
250
  this.logger.logEvent({ message: "processing update request", request: incoming, prev: this.prevRequest, data: this.getData() });
49
251
  let newRequest = {};
50
252
  const request = Object.assign({}, incoming);
51
253
  try {
52
254
  const req = this.checkForResetOrEmpty(request);
53
- if (req)
255
+ if (req) {
54
256
  return req;
257
+ }
258
+ this.checkGearChange(request, newRequest);
55
259
  this.checkSlope(request, newRequest);
56
260
  this.checkEmptyRequest(newRequest);
57
261
  this.prevRequest = JSON.parse(JSON.stringify(newRequest));
@@ -62,6 +266,25 @@ class SmartTrainerCyclingMode extends power_base_1.default {
62
266
  }
63
267
  return newRequest;
64
268
  }
269
+ setInitialGear(data) {
270
+ var _a, _b;
271
+ const m = (_b = (_a = this.adapter) === null || _a === void 0 ? void 0 : _a.getWeight()) !== null && _b !== void 0 ? _b : 85;
272
+ const virtualSpeeds = this.gearRatios.map(r => (0, calculations_1.calculateVirtualSpeed)(data.pedalRpm, r));
273
+ const requiredPowers = virtualSpeeds.map(v => { var _a; return calculations_1.default.calculatePower(m, v, (_a = data.slope) !== null && _a !== void 0 ? _a : 0); });
274
+ const deltas = requiredPowers.map((p, i) => ({ gear: i + 1, delta: Math.abs(p - data.power) }));
275
+ deltas.sort((a, b) => a.delta - b.delta);
276
+ this.gear = deltas[0].gear;
277
+ }
278
+ getGearString() {
279
+ const mode = this.getVirtualShiftMode();
280
+ if (mode === "Disabled")
281
+ return undefined;
282
+ if (this.gear === undefined || this.gear === null)
283
+ return '';
284
+ if (mode === "SlopeDelta")
285
+ return this.gear > 0 ? `+${this.gear}` : `${this.gear}`;
286
+ return this.gear.toString();
287
+ }
65
288
  }
66
289
  SmartTrainerCyclingMode.config = {
67
290
  name: "Smart Trainer",
@@ -70,7 +293,7 @@ SmartTrainerCyclingMode.config = {
70
293
  properties: [
71
294
  { key: 'bikeType', name: 'Bike Type', description: '', type: types_1.CyclingModeProperyType.SingleSelect, options: ['Race', 'Mountain', 'Triathlon'], default: 'Race' },
72
295
  { 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 }
296
+ { 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
297
  ]
75
298
  };
76
299
  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.31",
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"