incyclist-devices 2.3.37 → 2.3.39

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.
Files changed (49) hide show
  1. package/lib/antv2/base/adapter.js +5 -5
  2. package/lib/antv2/fe/adapter.js +2 -2
  3. package/lib/antv2/pwr/adapter.js +1 -1
  4. package/lib/base/adpater.js +2 -3
  5. package/lib/ble/cp/adapter.js +1 -1
  6. package/lib/ble/cp/sensor.js +1 -1
  7. package/lib/ble/fm/adapter.d.ts +1 -0
  8. package/lib/ble/fm/adapter.js +8 -2
  9. package/lib/ble/hr/sensor.js +1 -1
  10. package/lib/ble/utils.js +1 -1
  11. package/lib/ble/zwift/play/sensor.js +1 -2
  12. package/lib/direct-connect/base/peripheral.js +0 -1
  13. package/lib/direct-connect/messages/DiscoverCharacteristics.d.ts +1 -1
  14. package/lib/direct-connect/messages/DiscoverCharacteristics.js +3 -2
  15. package/lib/direct-connect/messages/DiscoverServices.d.ts +1 -1
  16. package/lib/direct-connect/messages/DiscoverServices.js +5 -3
  17. package/lib/direct-connect/messages/message.d.ts +1 -1
  18. package/lib/direct-connect/messages/message.js +2 -1
  19. package/lib/features/features.d.ts +11 -0
  20. package/lib/features/features.js +29 -0
  21. package/lib/features/index.d.ts +1 -0
  22. package/lib/features/index.js +17 -0
  23. package/lib/index.d.ts +1 -0
  24. package/lib/index.js +1 -0
  25. package/lib/modes/antble-smarttrainer.d.ts +5 -0
  26. package/lib/modes/antble-smarttrainer.js +82 -27
  27. package/lib/modes/base.d.ts +3 -0
  28. package/lib/modes/base.js +20 -0
  29. package/lib/modes/daum-erg.d.ts +1 -0
  30. package/lib/modes/daum-erg.js +3 -0
  31. package/lib/modes/daum-premium-standard.js +1 -1
  32. package/lib/modes/daum-smarttrainer.js +7 -7
  33. package/lib/modes/power-base.d.ts +1 -0
  34. package/lib/modes/power-base.js +7 -2
  35. package/lib/modes/types.d.ts +2 -2
  36. package/lib/modes/types.js +1 -1
  37. package/lib/serial/bindings/tcp.js +8 -8
  38. package/lib/serial/daum/DaumAdapter.js +2 -2
  39. package/lib/serial/daum/classic/adapter.d.ts +1 -1
  40. package/lib/serial/daum/classic/adapter.js +9 -10
  41. package/lib/serial/daum/classic/mock.js +1 -1
  42. package/lib/serial/daum/premium/comms.js +5 -5
  43. package/lib/serial/daum/premium/mock.js +1 -1
  44. package/lib/serial/daum/premium/utils.js +12 -12
  45. package/lib/serial/kettler/ergo-racer/adapter.d.ts +1 -1
  46. package/lib/serial/kettler/ergo-racer/adapter.js +43 -50
  47. package/lib/simulator/Simulator.js +1 -1
  48. package/lib/utils/utils.js +5 -5
  49. package/package.json +2 -2
@@ -171,7 +171,7 @@ class AntAdapter extends adpater_1.default {
171
171
  clearInterval(iv);
172
172
  resolve(true);
173
173
  }
174
- if (!this.promiseWaitForData || this.stopped) {
174
+ if (this.promiseWaitForData === undefined || this.promiseWaitForData === null || this.stopped) {
175
175
  resolve(false);
176
176
  clearInterval(iv);
177
177
  }
@@ -186,7 +186,7 @@ class AntAdapter extends adpater_1.default {
186
186
  return true;
187
187
  }
188
188
  const tsStart = Date.now();
189
- if (this.promiseWaitForData) {
189
+ if (this.promiseWaitForData !== undefined && this.promiseWaitForData !== null) {
190
190
  let hasData = false;
191
191
  try {
192
192
  hasData = yield this.promiseWaitForData;
@@ -201,7 +201,7 @@ class AntAdapter extends adpater_1.default {
201
201
  this.promiseWaitForData = null;
202
202
  return hasData;
203
203
  }
204
- catch (err) {
204
+ catch (_b) {
205
205
  this.promiseWaitForData = null;
206
206
  return false;
207
207
  }
@@ -257,7 +257,7 @@ class AntAdapter extends adpater_1.default {
257
257
  try {
258
258
  return yield this.start();
259
259
  }
260
- catch (error) {
260
+ catch (_a) {
261
261
  return false;
262
262
  }
263
263
  });
@@ -350,7 +350,7 @@ class AntAdapter extends adpater_1.default {
350
350
  return __awaiter(this, void 0, void 0, function* () {
351
351
  var _a, _b;
352
352
  this.startStatus.sensorStarted = this.sensorConnected;
353
- if (this.startStatus.sensorStarted || this.startStatus.sensorStarted)
353
+ if (this.startStatus.sensorStarted)
354
354
  return;
355
355
  this.logEvent({ message: 'start sensor', device: this.getName(), props });
356
356
  try {
@@ -53,7 +53,7 @@ class AntFEAdapter extends adapter_1.default {
53
53
  return;
54
54
  }
55
55
  let isReset = request.reset && Object.keys(request).length === 1;
56
- const update = isReset ? this.getCyclingMode().getBikeInitRequest() : this.getCyclingMode().sendBikeUpdate(request);
56
+ const update = isReset ? this.getCyclingMode().getBikeInitRequest() : this.getCyclingMode().buildUpdate(request);
57
57
  this.logEvent({ message: 'send bike update requested', device: this.getName(), update, request });
58
58
  try {
59
59
  const fe = this.sensor;
@@ -76,7 +76,7 @@ class AntFEAdapter extends adapter_1.default {
76
76
  yield this.reconnect();
77
77
  }
78
78
  }
79
- this.logEvent({ message: 'sendBikeUpdate() error', device: this.getName(), error: err.message });
79
+ this.logEvent({ message: 'sendUpdate() error', device: this.getName(), error: err.message });
80
80
  }
81
81
  });
82
82
  }
@@ -61,7 +61,7 @@ class AntPwrAdapter extends adapter_1.default {
61
61
  try {
62
62
  if ((this.isPaused() || this.isStopped()) && !request.forced)
63
63
  return;
64
- return yield this.getCyclingMode().sendBikeUpdate(request);
64
+ return yield this.getCyclingMode().buildUpdate(request);
65
65
  }
66
66
  catch (err) {
67
67
  this.logEvent({ message: 'Error', fn: 'sendUpdate', error: err.message });
@@ -214,10 +214,9 @@ class IncyclistDevice extends events_1.default {
214
214
  return;
215
215
  if (!request.enforced && (this.isPaused() || this.isStopped()))
216
216
  return;
217
- if (!this.hasCapability(types_1.IncyclistCapability.Control))
218
- return this.getCyclingMode().sendBikeUpdate(request);
219
- else
217
+ if (this.hasCapability(types_1.IncyclistCapability.Control))
220
218
  throw new Error('method not implemented');
219
+ return this.getCyclingMode().buildUpdate(request);
221
220
  });
222
221
  }
223
222
  setUser(user) {
@@ -92,7 +92,7 @@ class PwrAdapter extends adapter_1.default {
92
92
  try {
93
93
  if (this.isPaused() || this.isStopped())
94
94
  return;
95
- return this.getCyclingMode().sendBikeUpdate(request);
95
+ return this.getCyclingMode().buildUpdate(request);
96
96
  }
97
97
  catch (err) {
98
98
  this.logEvent({ message: 'Error', fn: 'BleCP:sendUpdate', error: err.message });
@@ -69,7 +69,7 @@ class BleCyclingPowerDevice extends sensor_1.TBleSensor {
69
69
  this.time = time;
70
70
  }
71
71
  }
72
- catch (err) {
72
+ catch (_a) {
73
73
  }
74
74
  const { instantaneousPower, balance, accTorque, rpm, time } = this;
75
75
  return { instantaneousPower, balance, accTorque, rpm, time, raw: `2a63:${data.toString('hex')}` };
@@ -33,4 +33,5 @@ export default class BleFmAdapter extends BleAdapter<IndoorBikeData, BleFitnessM
33
33
  protected updateCapabilitiesFromFeatures(features: IndoorBikeFeatures): void;
34
34
  sendUpdate(request: any, enforced?: boolean): Promise<UpdateRequest | void>;
35
35
  sendInitCommands(): Promise<boolean>;
36
+ protected getFeatureToggle(): import("../../features").FeatureToggle;
36
37
  }
@@ -23,6 +23,7 @@ const types_1 = require("../../types");
23
23
  const utils_1 = require("../../utils/utils");
24
24
  const utils_2 = require("../utils");
25
25
  const play_1 = require("../zwift/play");
26
+ const features_1 = require("../../features");
26
27
  const ZWIFT_PLAY_UUID = '0000000119ca465186e5fa29dcdd09d1';
27
28
  class BleFmAdapter extends adapter_1.default {
28
29
  constructor(settings, props) {
@@ -49,6 +50,8 @@ class BleFmAdapter extends adapter_1.default {
49
50
  }
50
51
  supportsVirtualShifting() {
51
52
  var _a, _b;
53
+ if (!this.getFeatureToggle().has('VirtualShifting'))
54
+ return false;
52
55
  return (_b = (_a = this.device) === null || _a === void 0 ? void 0 : _a.getSupportedServiceUUids()) === null || _b === void 0 ? void 0 : _b.some(s => (0, utils_2.matches)(s, ZWIFT_PLAY_UUID));
53
56
  }
54
57
  getSupportedCyclingModes() {
@@ -264,7 +267,7 @@ class BleFmAdapter extends adapter_1.default {
264
267
  if (!enforced && (this.stopped && !this.isStarting()))
265
268
  return;
266
269
  try {
267
- const update = this.getCyclingMode().sendBikeUpdate(request);
270
+ const update = this.getCyclingMode().buildUpdate(request);
268
271
  this.logEvent({ message: 'send bike update requested', profile: this.getProfile(), mode: (_a = this.getCyclingMode()) === null || _a === void 0 ? void 0 : _a.getName(), update, request });
269
272
  const device = this.getSensor();
270
273
  if (this.hasCapability(types_1.IncyclistCapability.Control)) {
@@ -284,7 +287,7 @@ class BleFmAdapter extends adapter_1.default {
284
287
  if (!this.zwiftPlay) {
285
288
  this.initVirtualShifting();
286
289
  }
287
- if (this.zwiftPlay && !isNaN(update.gearRatio)) {
290
+ if (this.zwiftPlay && !Number.isNaN(update.gearRatio)) {
288
291
  let slope = (_a = update.slope) !== null && _a !== void 0 ? _a : 0;
289
292
  if (slope === 0)
290
293
  slope = 0.01;
@@ -335,6 +338,9 @@ class BleFmAdapter extends adapter_1.default {
335
338
  }
336
339
  });
337
340
  }
341
+ getFeatureToggle() {
342
+ return (0, features_1.useFeatureToggle)();
343
+ }
338
344
  }
339
345
  BleFmAdapter.INCYCLIST_PROFILE_NAME = 'Smart Trainer';
340
346
  exports.default = BleFmAdapter;
@@ -20,7 +20,7 @@ class BleHrmDevice extends sensor_1.TBleSensor {
20
20
  this.rr = (data.readUInt16LE(offset)) / 1024;
21
21
  }
22
22
  }
23
- catch (err) {
23
+ catch (_a) {
24
24
  }
25
25
  const { heartrate, rr } = this;
26
26
  return { heartrate, rr, raw: data.toString('hex') };
package/lib/ble/utils.js CHANGED
@@ -24,7 +24,7 @@ function uuid(s) {
24
24
  if (s) {
25
25
  if (s.includes('-')) {
26
26
  const parts = s.split('-');
27
- const uuidNo = parseInt('0x' + parts[0]);
27
+ const uuidNo = Number.parseInt('0x' + parts[0]);
28
28
  return uuidNo.toString(16).toLowerCase();
29
29
  }
30
30
  return s;
@@ -52,7 +52,6 @@ class BleZwiftPlaySensor extends sensor_1.TBleSensor {
52
52
  }
53
53
  onData(characteristic, data, isNotify) {
54
54
  const uuid = (0, utils_1.beautifyUUID)(characteristic).toLowerCase();
55
- console.log('# data', uuid, data === null || data === void 0 ? void 0 : data.toString('hex'));
56
55
  if (uuid === '00000002-19ca-4651-86e5-fa29dcdd09d1') {
57
56
  this.onMeasurement(data);
58
57
  }
@@ -387,7 +386,7 @@ class BleZwiftPlaySensor extends sensor_1.TBleSensor {
387
386
  const { publicKey, privateKey } = this.createKeyPair();
388
387
  this.privateKey = (_a = this.privateKey) !== null && _a !== void 0 ? _a : Buffer.from(privateKey);
389
388
  this.publicKey = (_b = this.publicKey) !== null && _b !== void 0 ? _b : Buffer.from(publicKey);
390
- message = Buffer.concat([Buffer.from('RideOn'), Buffer.from([0x01, 0x02]), this.publicKey]);
389
+ message = Buffer.concat([Buffer.from('RideOn'), Buffer.from([0x02, 0x03]), this.publicKey]);
391
390
  }
392
391
  yield this.write((0, utils_1.fullUUID)('00000003-19ca-4651-86e5-fa29dcdd09d1'), message, { withoutResponse: true });
393
392
  this.isHubServicePaired = true;
@@ -154,7 +154,6 @@ class DirectConnectPeripheral {
154
154
  if (this.subscribed.includes(uuid)) {
155
155
  return true;
156
156
  }
157
- console.log('# subscribe ', characteristicUUID);
158
157
  const seqNo = this.getNextSeqNo();
159
158
  const message = new messages_1.EnableCharacteristicNotificationsMessage();
160
159
  const request = message.createRequest(seqNo, { characteristicUUID: (0, utils_1.parseUUID)(characteristicUUID), enable: true });
@@ -18,5 +18,5 @@ export declare class DiscoverCharacteristicsMessage extends Message<TDCDiscoverC
18
18
  parseRequestBody(body: Buffer): TDCDiscoverCharacteristicsRequestBody;
19
19
  buildRequestBody(body: TDCDiscoverCharacteristicsRequestBody): Buffer;
20
20
  buildResponseBody(body: TDCDiscoverCharacteristicsResponseBody): Buffer;
21
- parseResponseBody(body: Buffer): TDCDiscoverCharacteristicsResponseBody;
21
+ parseResponseBody(responseBody: Buffer): TDCDiscoverCharacteristicsResponseBody;
22
22
  }
@@ -25,9 +25,10 @@ class DiscoverCharacteristicsMessage extends message_1.Message {
25
25
  ]));
26
26
  return Buffer.concat([serviceUUIDBuffer, ...characteristicBuffers]);
27
27
  }
28
- parseResponseBody(body) {
28
+ parseResponseBody(responseBody) {
29
+ const body = Buffer.from(responseBody);
29
30
  const length = body.length;
30
- const serviceUUID = body.subarray(0, 16).toString("hex");
31
+ const serviceUUID = Buffer.from(body.subarray(0, 16)).toString("hex");
31
32
  const characteristicDefinitions = [];
32
33
  for (let i = 16; i < length; i += 17) {
33
34
  const characteristicUUID = Buffer.from(body.subarray(i, i + 16)).toString("hex");
@@ -10,6 +10,6 @@ export interface TDCDiscoverServicesResponseBody extends TDCBody {
10
10
  export declare class DiscoverServiceMessage extends Message<EmptyBody, TDCDiscoverServicesResponseBody> {
11
11
  constructor();
12
12
  buildResponseBody(body: TDCDiscoverServicesResponseBody): Buffer;
13
- parseResponseBody(body: Buffer): TDCDiscoverServicesResponseBody;
13
+ parseResponseBody(responseBody: Buffer): TDCDiscoverServicesResponseBody;
14
14
  verifyHeader(header: TDCMessageHeader): boolean;
15
15
  }
@@ -12,13 +12,15 @@ class DiscoverServiceMessage extends message_1.Message {
12
12
  const serviceBuffers = body.serviceDefinitions.map((service) => {
13
13
  return Buffer.from(service.serviceUUID, "hex");
14
14
  });
15
- return Buffer.concat(serviceBuffers);
15
+ const response = Buffer.concat(serviceBuffers);
16
+ return response;
16
17
  }
17
- parseResponseBody(body) {
18
+ parseResponseBody(responseBody) {
19
+ const body = Buffer.from(responseBody);
18
20
  const serviceDefinitions = [];
19
21
  const length = body.length;
20
22
  for (let i = 0; i < length; i += 16) {
21
- const serviceUUID = body.subarray(i, i + 16).toString("hex");
23
+ const serviceUUID = Buffer.from(body.subarray(i, i + 16)).toString("hex");
22
24
  serviceDefinitions.push({ serviceUUID });
23
25
  }
24
26
  return { serviceDefinitions };
@@ -9,7 +9,7 @@ export declare class Message<TReq extends TDCBody, TRes extends TDCBody> {
9
9
  createRequest(seqNum: any, body: TReq): Buffer;
10
10
  parseRequest(buffer: Buffer): TDCRequest<TReq>;
11
11
  prepareResponse(request: TDCRequest<TReq>, respCode: number, body: TRes): TDCResponse<TRes>;
12
- parseResponse(buffer: Buffer): TDCResponse<TRes>;
12
+ parseResponse(response: Buffer): TDCResponse<TRes>;
13
13
  buildHeader(header: TDCMessageHeader, length: number): Buffer;
14
14
  buildResponseBody(body: TRes): Buffer;
15
15
  buildRequestBody(body: TReq): Buffer;
@@ -40,7 +40,8 @@ class Message {
40
40
  const header = Object.assign(Object.assign({}, request.header), { respCode });
41
41
  return { header, body };
42
42
  }
43
- parseResponse(buffer) {
43
+ parseResponse(response) {
44
+ const buffer = Buffer.from(response);
44
45
  const header = (0, exports.parseHeader)(buffer);
45
46
  this.verifyHeader(header);
46
47
  const bodyBuffer = buffer.subarray(6);
@@ -0,0 +1,11 @@
1
+ export type Feature = 'VirtualShifting';
2
+ export declare class FeatureToggle {
3
+ protected static _instance: FeatureToggle;
4
+ static getInstance(): FeatureToggle;
5
+ protected enabled: Array<string>;
6
+ constructor();
7
+ add(feature: Feature): void;
8
+ remove(feature: Feature): void;
9
+ has(feature: Feature): boolean;
10
+ }
11
+ export declare const useFeatureToggle: () => FeatureToggle;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useFeatureToggle = exports.FeatureToggle = void 0;
4
+ class FeatureToggle {
5
+ static getInstance() {
6
+ if (!FeatureToggle._instance)
7
+ FeatureToggle._instance = new FeatureToggle();
8
+ return FeatureToggle._instance;
9
+ }
10
+ constructor() {
11
+ this.enabled = [];
12
+ }
13
+ add(feature) {
14
+ this.enabled.push(feature);
15
+ }
16
+ remove(feature) {
17
+ const index = this.enabled.indexOf(feature);
18
+ if (index > -1)
19
+ this.enabled.splice(index, 1);
20
+ }
21
+ has(feature) {
22
+ return this.enabled.includes(feature);
23
+ }
24
+ }
25
+ exports.FeatureToggle = FeatureToggle;
26
+ const useFeatureToggle = () => {
27
+ return FeatureToggle.getInstance();
28
+ };
29
+ exports.useFeatureToggle = useFeatureToggle;
@@ -0,0 +1 @@
1
+ export * from './features';
@@ -0,0 +1,17 @@
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./features"), exports);
package/lib/index.d.ts CHANGED
@@ -13,4 +13,5 @@ export * from './serial';
13
13
  export * from './ble';
14
14
  export * from './antv2';
15
15
  export * from './direct-connect';
16
+ export * from './features';
16
17
  export { IAdapter, IncyclistDevice, IncyclistDeviceAdapter, DeviceSettings, DeviceProperties, AdapterFactory, InterfaceFactory, IncyclistInterface, INTERFACE, InterfaceProps, ICyclingMode, IncyclistAdapterData as DeviceData, IncyclistCapability, calc, };
package/lib/index.js CHANGED
@@ -35,3 +35,4 @@ __exportStar(require("./serial"), exports);
35
35
  __exportStar(require("./ble"), exports);
36
36
  __exportStar(require("./antv2"), exports);
37
37
  __exportStar(require("./direct-connect"), exports);
38
+ __exportStar(require("./features"), exports);
@@ -32,6 +32,7 @@ export default class SmartTrainerCyclingMode extends PowerBasedCyclingModeBase i
32
32
  protected tsStart: number;
33
33
  protected simPower: number;
34
34
  protected simSlope: number;
35
+ protected prevData: any;
35
36
  protected readonly gearRatios: number[];
36
37
  constructor(adapter: IAdapter, props?: any);
37
38
  getBikeInitRequest(): UpdateRequest;
@@ -43,13 +44,17 @@ export default class SmartTrainerCyclingMode extends PowerBasedCyclingModeBase i
43
44
  protected checkSlopeWithSlopeDelta(request: UpdateRequest, newRequest?: UpdateRequest): void;
44
45
  protected checkSlopeWithSimulatedShifting(request: UpdateRequest, newRequest?: UpdateRequest): void;
45
46
  checkSlope(request: UpdateRequest, newRequest?: UpdateRequest): void;
47
+ checkCadenceChange(request: UpdateRequest, newRequest?: UpdateRequest): void;
48
+ calculateSimulatedPower(request: UpdateRequest): void;
46
49
  checkGearChange(request: UpdateRequest, newRequest?: UpdateRequest): void;
47
50
  protected verifySimPower(): void;
48
51
  protected checkEmptyRequest(newRequest: UpdateRequest): void;
49
52
  protected getVirtualShiftMode(): VirtshiftMode;
50
53
  updateData(bikeData: IncyclistBikeData, log?: boolean): IncyclistBikeData;
51
54
  getData(): Partial<IncyclistBikeData>;
55
+ protected updateRequired(request?: UpdateRequest): boolean;
52
56
  sendBikeUpdate(incoming: UpdateRequest): UpdateRequest;
53
57
  protected getGearString(): string;
58
+ protected getFeatureToogle(): import("../features").FeatureToggle;
54
59
  }
55
60
  export {};
@@ -39,6 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  const types_1 = require("./types");
40
40
  const power_base_1 = __importDefault(require("./power-base"));
41
41
  const calculations_1 = __importStar(require("../utils/calculations"));
42
+ const features_1 = require("../features");
42
43
  const MIN_POWER = 25;
43
44
  class SmartTrainerCyclingMode extends power_base_1.default {
44
45
  constructor(adapter, props) {
@@ -71,6 +72,9 @@ class SmartTrainerCyclingMode extends power_base_1.default {
71
72
  }
72
73
  getConfig() {
73
74
  const config = super.getConfig();
75
+ if (!this.getFeatureToogle().has('VirtualShifting')) {
76
+ return config;
77
+ }
74
78
  let virtshift = config.properties.find(p => p.key === 'virtshift');
75
79
  let startGear = config.properties.find(p => p.key === 'startGear');
76
80
  if (!virtshift) {
@@ -90,7 +94,7 @@ class SmartTrainerCyclingMode extends power_base_1.default {
90
94
  }
91
95
  checkSlopeNoShiftig(request, newRequest = {}) {
92
96
  if (request.slope !== undefined) {
93
- const targetSlope = newRequest.slope = parseFloat(request.slope.toFixed(1));
97
+ const targetSlope = newRequest.slope = Number.parseFloat(request.slope.toFixed(1));
94
98
  this.data.slope = newRequest.slope;
95
99
  try {
96
100
  const slopeAdj = targetSlope >= 0 ? this.getSetting('slopeAdj') : this.getSetting('slopeAdjDown');
@@ -112,7 +116,7 @@ class SmartTrainerCyclingMode extends power_base_1.default {
112
116
  }
113
117
  checkSlopeWithSlopeDelta(request, newRequest = {}) {
114
118
  if (request.slope !== undefined) {
115
- const targetSlope = newRequest.slope = parseFloat(request.slope.toFixed(1));
119
+ const targetSlope = newRequest.slope = Number.parseFloat(request.slope.toFixed(1));
116
120
  this.data.slope = newRequest.slope;
117
121
  const requestedSlope = targetSlope + this.getSlopeDelta();
118
122
  try {
@@ -130,9 +134,13 @@ class SmartTrainerCyclingMode extends power_base_1.default {
130
134
  this.checkSlopeNoShiftig(request, newRequest);
131
135
  return;
132
136
  }
133
- if (request.slope !== undefined) {
137
+ if (request.slope === undefined) {
138
+ console.log('# set simulated power (same slope):', { simPower: this.simPower, gear: this.gear, simSlope: this.simSlope, routeSlope: this.data.slope, cadence: this.data.pedalRpm, power: this.data.power });
139
+ this.logger.logEvent({ message: 'set simulated power (same slope)', simPower: this.simPower, gear: this.gear, simSlope: this.simSlope, routeSlope: this.data.slope, cadence: this.data.pedalRpm, power: this.data.power });
140
+ }
141
+ else {
134
142
  const prev = (_a = this.data.slope) !== null && _a !== void 0 ? _a : 0;
135
- this.data.slope = parseFloat(request.slope.toFixed(1));
143
+ this.data.slope = Number.parseFloat(request.slope.toFixed(1));
136
144
  delete request.slope;
137
145
  delete newRequest.slope;
138
146
  this.simSlope = this.data.slope;
@@ -151,11 +159,13 @@ class SmartTrainerCyclingMode extends power_base_1.default {
151
159
  const prevPower = this.data.power;
152
160
  if (this.data.speed < 10 && this.data.isPedalling && (this.data.slope < 1 || this.data.speed === 0)) {
153
161
  this.simPower = Math.max(newPower, prevPower);
154
- this.logger.logEvent({ message: 'set simulater power', power: this.simPower, gear: this.gear, simSlope: this.simSlope, routeSlope: this.data.slope, prevPower, newPower });
162
+ console.log('# set simulated power', { power: this.simPower, gear: this.gear, simSlope: this.simSlope, routeSlope: this.data.slope, prevPower, newPower });
163
+ this.logger.logEvent({ message: 'set simulated power', power: this.simPower, gear: this.gear, simSlope: this.simSlope, routeSlope: this.data.slope, prevPower, newPower });
155
164
  }
156
165
  else if (this.data.slope === prev && newPower < prevPower) {
157
166
  this.simPower = prevPower;
158
- this.logger.logEvent({ message: 'set simulater power', power: this.simPower, gear: this.gear, simSlope: this.simSlope, routeSlope: this.data.slope, prevPower, newPower });
167
+ console.log('# set simulated power:', { power: this.simPower, gear: this.gear, simSlope: this.simSlope, routeSlope: this.data.slope, prevPower, newPower });
168
+ this.logger.logEvent({ message: 'set simulated power', power: this.simPower, gear: this.gear, simSlope: this.simSlope, routeSlope: this.data.slope, prevPower, newPower });
159
169
  }
160
170
  else {
161
171
  const powerDiff = newPower - prevPower;
@@ -166,7 +176,8 @@ class SmartTrainerCyclingMode extends power_base_1.default {
166
176
  const eKinAfter1sec = eKinPrev - powerDiff * 1;
167
177
  const vAfter1sec = Math.sqrt(2 * eKinAfter1sec / m) * 3600 / 1000;
168
178
  this.simPower = calculations_1.default.calculatePower(m, vAfter1sec / 3.6, (_e = this.simSlope) !== null && _e !== void 0 ? _e : 0);
169
- this.logger.logEvent({ message: 'set simulater power', power: this.simPower, gear: this.gear, simSlope: this.simSlope, routeSlope: this.data.slope, eKinPrev, eKinTarget, delta, prevPower, newPower });
179
+ console.log('# set simulated power (Ekin):', { power: this.simPower, gear: this.gear, simSlope: this.simSlope, routeSlope: this.data.slope, eKinPrev, eKinTarget, delta, prevPower, newPower });
180
+ this.logger.logEvent({ message: 'set simulated power (Ekin)', power: this.simPower, gear: this.gear, simSlope: this.simSlope, routeSlope: this.data.slope, eKinPrev, eKinTarget, delta, prevPower, newPower });
170
181
  }
171
182
  this.verifySimPower();
172
183
  }
@@ -190,8 +201,53 @@ class SmartTrainerCyclingMode extends power_base_1.default {
190
201
  break;
191
202
  }
192
203
  }
193
- checkGearChange(request, newRequest = {}) {
204
+ checkCadenceChange(request, newRequest = {}) {
205
+ const virtshiftMode = this.getVirtualShiftMode();
206
+ if (virtshiftMode !== 'Simulated') {
207
+ return;
208
+ }
209
+ if (request.targetPower !== undefined || request.targetPowerDelta !== undefined || request.gearDelta !== undefined || request.gearRatio !== undefined) {
210
+ console.log('# cadence change ignored due to other power/gear request', request);
211
+ return;
212
+ }
213
+ if (this.data.pedalRpm !== this.prevData.pedalRpm) {
214
+ console.log('# cadence changed ', { cadence: this.data.pedalRpm, prevCadence: this.prevData.pedalRpm });
215
+ this.calculateSimulatedPower(request);
216
+ delete request.slope;
217
+ }
218
+ }
219
+ calculateSimulatedPower(request) {
194
220
  var _a, _b, _c, _d;
221
+ const m = (_b = (_a = this.adapter) === null || _a === void 0 ? void 0 : _a.getWeight()) !== null && _b !== void 0 ? _b : 85;
222
+ const vCurrent = this.data.speed * 1000 / 3600;
223
+ const eKinCurrent = m * vCurrent * vCurrent / 2;
224
+ if (this.data.pedalRpm > 0) {
225
+ const virtualSpeed = (0, calculations_1.calculateVirtualSpeed)(this.data.pedalRpm, this.gearRatios[this.gear - 1]);
226
+ const newPower = calculations_1.default.calculatePower(m, virtualSpeed, (_c = this.simSlope) !== null && _c !== void 0 ? _c : 0);
227
+ const prevPower = this.data.power;
228
+ if (this.data.speed < 10 && this.data.isPedalling && (this.data.slope < 1 || this.data.speed === 0)) {
229
+ this.simPower = Math.max(newPower, prevPower);
230
+ console.log('# set simulated power (starting)', { power: this.simPower, gear: this.gear, simSlope: this.simSlope, routeSlope: this.data.slope, prevPower, newPower });
231
+ this.logger.logEvent({ message: 'set simulated power (starting)', power: this.simPower, gear: this.gear, simSlope: this.simSlope, routeSlope: this.data.slope, prevPower, newPower });
232
+ }
233
+ else {
234
+ const powerDiff = newPower - prevPower;
235
+ const vTarget = virtualSpeed * 1000 / 3600;
236
+ const eKinTarget = m * vTarget * vTarget / 2;
237
+ const eKinPrev = eKinCurrent;
238
+ const delta = eKinTarget - eKinPrev;
239
+ const eKinAfter1sec = eKinPrev - powerDiff * 1;
240
+ const vAfter1sec = Math.sqrt(2 * eKinAfter1sec / m) * 3600 / 1000;
241
+ this.simPower = calculations_1.default.calculatePower(m, vAfter1sec / 3.6, (_d = this.simSlope) !== null && _d !== void 0 ? _d : 0);
242
+ console.log('# set simulated power (Ekin):', { power: this.simPower, gear: this.gear, simSlope: this.simSlope, routeSlope: this.data.slope, eKinPrev, eKinTarget, delta, prevPower, newPower });
243
+ this.logger.logEvent({ message: 'set simulated power (Ekin)', power: this.simPower, gear: this.gear, simSlope: this.simSlope, routeSlope: this.data.slope, eKinPrev, eKinTarget, delta, prevPower, newPower });
244
+ }
245
+ }
246
+ else {
247
+ delete this.simPower;
248
+ }
249
+ }
250
+ checkGearChange(request, newRequest = {}) {
195
251
  const virtshiftMode = this.getVirtualShiftMode();
196
252
  switch (virtshiftMode) {
197
253
  case 'SlopeDelta':
@@ -217,17 +273,10 @@ class SmartTrainerCyclingMode extends power_base_1.default {
217
273
  this.gear = this.gearRatios.length;
218
274
  }
219
275
  delete request.gearDelta;
220
- if (this.data.pedalRpm > 0) {
221
- const virtualSpeed = (0, calculations_1.calculateVirtualSpeed)(this.data.pedalRpm, this.gearRatios[this.gear - 1]);
222
- const m = (_b = (_a = this.adapter) === null || _a === void 0 ? void 0 : _a.getWeight()) !== null && _b !== void 0 ? _b : 85;
223
- 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);
224
- this.verifySimPower();
225
- this.logger.logEvent({ message: 'set simulater power', power: this.simPower, gear: this.gear, simSlope: this.simSlope, routeSlope: this.data.slope });
276
+ this.calculateSimulatedPower(request);
277
+ if (this.simPower !== undefined) {
226
278
  this.adapter.sendUpdate({ targetPower: this.simPower }).then(() => { });
227
279
  }
228
- else {
229
- delete this.simPower;
230
- }
231
280
  }
232
281
  break;
233
282
  case 'Adapter':
@@ -297,6 +346,9 @@ class SmartTrainerCyclingMode extends power_base_1.default {
297
346
  }
298
347
  }
299
348
  getVirtualShiftMode() {
349
+ if (!this.getFeatureToogle().has('VirtualShifting')) {
350
+ return 'Disabled';
351
+ }
300
352
  const virtshiftMode = this.getSetting('virtshift');
301
353
  if (virtshiftMode === 'Disabled') {
302
354
  return 'Disabled';
@@ -316,8 +368,8 @@ class SmartTrainerCyclingMode extends power_base_1.default {
316
368
  return 'Disabled';
317
369
  }
318
370
  updateData(bikeData, log) {
319
- var _a, _b, _c, _d, _e;
320
- const prev = Object.assign({}, this.data);
371
+ var _a;
372
+ const prev = this.prevData = Object.assign({}, this.data);
321
373
  const data = super.updateData(bikeData, log);
322
374
  const mode = this.getVirtualShiftMode();
323
375
  let virtualSpeed;
@@ -332,14 +384,6 @@ class SmartTrainerCyclingMode extends power_base_1.default {
332
384
  this.gear = (_a = this.getSetting('startGear')) !== null && _a !== void 0 ? _a : 0;
333
385
  data.gearStr = this.getGearString();
334
386
  }
335
- else if (this.gear !== undefined) {
336
- if (prev.power !== data.power || prev.pedalRpm !== data.pedalRpm) {
337
- virtualSpeed = (0, calculations_1.calculateVirtualSpeed)(data.pedalRpm, this.gearRatios[this.gear - 1]);
338
- const m = (_c = (_b = this.adapter) === null || _b === void 0 ? void 0 : _b.getWeight()) !== null && _c !== void 0 ? _c : 85;
339
- this.simPower = calculations_1.default.calculatePower(m, virtualSpeed, (_e = (_d = this.simSlope) !== null && _d !== void 0 ? _d : data.slope) !== null && _e !== void 0 ? _e : 0);
340
- this.verifySimPower();
341
- }
342
- }
343
387
  return data;
344
388
  }
345
389
  getData() {
@@ -347,6 +391,13 @@ class SmartTrainerCyclingMode extends power_base_1.default {
347
391
  const data = super.getData();
348
392
  return Object.assign(Object.assign({}, data), { gearStr });
349
393
  }
394
+ updateRequired(request) {
395
+ const virtshiftMode = this.getVirtualShiftMode();
396
+ if (virtshiftMode === 'Adapter' || virtshiftMode === 'Simulated') {
397
+ return true;
398
+ }
399
+ return super.updateRequired(request);
400
+ }
350
401
  sendBikeUpdate(incoming) {
351
402
  this.logger.logEvent({ message: "processing update request", request: incoming, prev: this.prevRequest, data: this.getData() });
352
403
  let newRequest = {};
@@ -356,6 +407,7 @@ class SmartTrainerCyclingMode extends power_base_1.default {
356
407
  if (req) {
357
408
  return req;
358
409
  }
410
+ this.checkCadenceChange(request, newRequest);
359
411
  this.checkGearChange(request, newRequest);
360
412
  this.checkSlope(request, newRequest);
361
413
  this.checkEmptyRequest(newRequest);
@@ -384,6 +436,9 @@ class SmartTrainerCyclingMode extends power_base_1.default {
384
436
  }
385
437
  return (_d = this.gear) === null || _d === void 0 ? void 0 : _d.toString();
386
438
  }
439
+ getFeatureToogle() {
440
+ return (0, features_1.useFeatureToggle)();
441
+ }
387
442
  }
388
443
  SmartTrainerCyclingMode.config = {
389
444
  name: "Smart Trainer",
@@ -7,6 +7,7 @@ export declare abstract class CyclingModeBase extends CyclingMode implements ICy
7
7
  localConfig: CyclingModeConfig;
8
8
  protected static config: CyclingModeConfig;
9
9
  protected static isERG: boolean;
10
+ protected prevUpdate: UpdateRequest;
10
11
  static supportsERGMode(): boolean;
11
12
  constructor(adapter: IAdapter, props?: any);
12
13
  setAdapter(adapter: IAdapter): void;
@@ -23,6 +24,8 @@ export declare abstract class CyclingModeBase extends CyclingMode implements ICy
23
24
  getSettings(): Settings;
24
25
  setModeProperty(name: string, value: any): void;
25
26
  getModeProperty(name: string): any;
27
+ protected updateRequired(request?: UpdateRequest): boolean;
28
+ buildUpdate(request?: UpdateRequest): UpdateRequest;
26
29
  abstract getBikeInitRequest(): UpdateRequest;
27
30
  abstract sendBikeUpdate(request: UpdateRequest): UpdateRequest;
28
31
  abstract updateData(data: IncyclistBikeData): IncyclistBikeData;