incyclist-devices 2.3.33 → 2.3.35

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.
@@ -10,13 +10,23 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.BleZwiftPlaySensor = void 0;
13
+ const zwift_hub_1 = require("../../../proto/zwift_hub");
13
14
  const sensor_1 = require("../../base/sensor");
14
15
  const utils_1 = require("../../utils");
15
16
  const crypto_1 = require("crypto");
16
17
  const events_1 = require("events");
17
18
  class BleZwiftPlaySensor extends sensor_1.TBleSensor {
18
19
  constructor(peripheral, props) {
19
- super(peripheral, props);
20
+ if (peripheral &&
21
+ 'startSensor' in peripheral && typeof peripheral.startSensor === 'function' &&
22
+ 'getPeripheral' in peripheral && typeof peripheral.getPeripheral === 'function') {
23
+ super(peripheral.getPeripheral(), props);
24
+ this.isFM = true;
25
+ }
26
+ else {
27
+ super(peripheral, props);
28
+ this.isFM = false;
29
+ }
20
30
  this.emitter = new events_1.EventEmitter();
21
31
  this.setInitialState();
22
32
  }
@@ -42,18 +52,66 @@ class BleZwiftPlaySensor extends sensor_1.TBleSensor {
42
52
  }
43
53
  onData(characteristic, data, isNotify) {
44
54
  const uuid = (0, utils_1.beautifyUUID)(characteristic).toLowerCase();
55
+ console.log('# data', uuid, data === null || data === void 0 ? void 0 : data.toString('hex'));
45
56
  if (uuid === '00000002-19ca-4651-86e5-fa29dcdd09d1') {
46
- this.onPlayMeasurement(data);
57
+ this.onMeasurement(data);
47
58
  }
48
59
  else if (uuid === '00000004-19ca-4651-86e5-fa29dcdd09d1') {
49
- this.onPairResponse(data);
60
+ this.onResponse(data);
50
61
  }
51
62
  else {
52
- console.log('data received ', isNotify ? 'N' : 'I', uuid, data.toString('hex'));
53
63
  }
54
64
  return true;
55
65
  }
56
- onPlayMeasurement(d) {
66
+ requestDataUpdate(dataId) {
67
+ return __awaiter(this, void 0, void 0, function* () {
68
+ yield this.sendHubRequest({ dataId });
69
+ });
70
+ }
71
+ setSimulationData(data) {
72
+ return __awaiter(this, void 0, void 0, function* () {
73
+ var _a, _b, _c, _d;
74
+ if (!this.isHubServiceActive) {
75
+ yield this.initHubService(false);
76
+ }
77
+ const crrx100000 = Math.round((_a = data === null || data === void 0 ? void 0 : data.crrx100000) !== null && _a !== void 0 ? _a : 5100);
78
+ const cWax10000 = Math.round((_b = data === null || data === void 0 ? void 0 : data.cWax10000) !== null && _b !== void 0 ? _b : 400);
79
+ const windx100 = Math.round((_c = data === null || data === void 0 ? void 0 : data.windx100) !== null && _c !== void 0 ? _c : 0);
80
+ const inclineX100 = Math.round((_d = data === null || data === void 0 ? void 0 : data.inclineX100) !== null && _d !== void 0 ? _d : 0);
81
+ const simulation = { inclineX100, crrx100000, cWax10000, windx100 };
82
+ yield this.sendHubCommand({ simulation });
83
+ yield this.requestDataUpdate(512);
84
+ });
85
+ }
86
+ setGearRatio(gearRatio) {
87
+ return __awaiter(this, void 0, void 0, function* () {
88
+ try {
89
+ console.log('# set gear ratio', gearRatio);
90
+ if (!this.isHubServiceActive) {
91
+ yield this.initHubService();
92
+ }
93
+ const gearRatioX10000 = Math.round(gearRatio * 10000);
94
+ const bikeWeightx100 = 10 * 100;
95
+ const riderWeightx100 = 75 * 100;
96
+ const command = {
97
+ physical: { bikeWeightx100, gearRatioX10000, riderWeightx100 }
98
+ };
99
+ yield this.sendHubCommand(command);
100
+ yield this.requestDataUpdate(512);
101
+ }
102
+ catch (err) {
103
+ console.log('# set gear ratio failed', err);
104
+ }
105
+ return gearRatio;
106
+ });
107
+ }
108
+ sendPlayCommand(id, command) {
109
+ return __awaiter(this, void 0, void 0, function* () {
110
+ const data = Buffer.concat([Buffer.from([id]), command]);
111
+ return yield this.write('00000003-19ca-4651-86e5-fa29dcdd09d1', data, { withoutResponse: true });
112
+ });
113
+ }
114
+ onMeasurement(d) {
57
115
  const data = Buffer.from(d);
58
116
  if ((data === null || data === void 0 ? void 0 : data.length) < 1) {
59
117
  console.log('Invalid click measurement data', data.toString('hex'));
@@ -62,84 +120,183 @@ class BleZwiftPlaySensor extends sensor_1.TBleSensor {
62
120
  const type = data.readUInt8(0);
63
121
  const message = data.subarray(1);
64
122
  if (type === 0x37) {
65
- this.onButtonMessage(message);
123
+ this.onClickButtonMessage(message);
66
124
  }
67
125
  else if (type === 0x19) {
68
126
  this.onPingMessage(message);
69
127
  }
128
+ else if (type === 0x42) {
129
+ console.log('# init confirmed');
130
+ }
131
+ else if (type === 0x03) {
132
+ this.onRidingData(message);
133
+ }
134
+ else if (type === 0x3c) {
135
+ this.onDeviceInformation(message);
136
+ }
70
137
  else {
71
138
  console.log('Unknown click measurement type', type, message.toString('hex'));
72
139
  this.emit('data', { raw: data.toString('hex') });
73
140
  }
74
141
  return true;
75
142
  }
76
- onButtonMessage(d) {
77
- var _a, _b;
78
- const message = Buffer.from(d);
143
+ initHubService() {
144
+ return __awaiter(this, arguments, void 0, function* (setSimulation = true) {
145
+ console.log('# init Hub Service');
146
+ if (this.isHubServiceActive)
147
+ return true;
148
+ if (!this.isPaired) {
149
+ yield this.pair();
150
+ }
151
+ if (!this.isSubscribed) {
152
+ yield this.subscribe();
153
+ this.isSubscribed = true;
154
+ }
155
+ return new Promise((done) => {
156
+ let timeout = setTimeout(() => {
157
+ done(false);
158
+ }, 2000);
159
+ this.once('hub-riding-data', () => {
160
+ if (timeout) {
161
+ clearTimeout(timeout);
162
+ timeout = undefined;
163
+ }
164
+ this.isHubServiceActive = true;
165
+ console.log('# init hub service completed');
166
+ if (setSimulation) {
167
+ this.setSimulationData().then(() => {
168
+ done(true);
169
+ })
170
+ .catch(() => {
171
+ done(true);
172
+ });
173
+ }
174
+ else {
175
+ done(true);
176
+ }
177
+ });
178
+ this.sendPlayCommand(0x41, Buffer.from([0x08, 0x05]))
179
+ .catch(err => {
180
+ if (timeout) {
181
+ clearTimeout(timeout);
182
+ timeout = undefined;
183
+ }
184
+ console.log('# init hub service timeout');
185
+ done(false);
186
+ this.logEvent({ message: 'could not init hub service', reason: err.message });
187
+ });
188
+ });
189
+ });
190
+ }
191
+ sendHubRequest(request) {
192
+ return __awaiter(this, void 0, void 0, function* () {
193
+ const message = Buffer.from(zwift_hub_1.HubRequest.toBinary(request));
194
+ console.log('# sending hub request', request, message);
195
+ this.logEvent({ mesage: 'send zwift hub request', request });
196
+ return yield this.sendPlayCommand(0x00, message);
197
+ });
198
+ }
199
+ sendHubCommand(command) {
200
+ return __awaiter(this, void 0, void 0, function* () {
201
+ const message = Buffer.from(zwift_hub_1.HubCommand.toBinary(command));
202
+ console.log('# sending hub command', command, message);
203
+ this.logEvent({ mesage: 'send zwift hub command', command });
204
+ return yield this.sendPlayCommand(0x04, message);
205
+ });
206
+ }
207
+ onRidingData(m) {
79
208
  try {
80
- if (message.readUInt8(0) === 0x8 && message.length === 4) {
81
- const value = Buffer.from(message.subarray(1));
82
- const messageStr = value.toString('hex');
83
- if (messageStr === this.prevClickMessage) {
84
- return;
209
+ this.tsLastRidingData = Date.now();
210
+ const data = zwift_hub_1.HubRidingData.fromBinary(m);
211
+ this.emit('hub-riding-data', data);
212
+ console.log('#riding data', data);
213
+ this.logEvent({ message: 'riding data received', power: data.power, cadence: data.cadence, Speed: data.speedX100 / 100, heartrate: data.hR, unknown1: data.unknown1, unknown2: data.unknown2 });
214
+ }
215
+ catch (err) {
216
+ this.logEvent({ message: 'Error', fn: 'onRidingData', error: err.message, stack: err.stack });
217
+ }
218
+ }
219
+ onDeviceInformation(m) {
220
+ try {
221
+ const envelope = zwift_hub_1.DeviceDataEnvelope.fromBinary(m);
222
+ const { messageType, payload } = envelope;
223
+ if (messageType < 16) {
224
+ const deviceInfo = zwift_hub_1.DeviceInformationContent.fromBinary(payload);
225
+ console.log('# deviceInfo', deviceInfo);
226
+ this.emit('hub-device-info', deviceInfo);
227
+ this.logEvent({ message: 'hub device info update', deviceInfo });
228
+ }
229
+ else if (messageType >= 512 && messageType < 526) {
230
+ const si = zwift_hub_1.DeviceSettings.fromBinary(payload);
231
+ console.log('# settings', si === null || si === void 0 ? void 0 : si.subContent);
232
+ this.emit('hub-settings', si === null || si === void 0 ? void 0 : si.subContent);
233
+ this.logEvent({ message: 'hub settings update', settings: si === null || si === void 0 ? void 0 : si.subContent });
234
+ }
235
+ }
236
+ catch (err) {
237
+ let payload = 'unknown';
238
+ try {
239
+ payload = m.toString('hex');
240
+ }
241
+ catch (_a) { }
242
+ this.logEvent({ message: 'Error', fn: 'onRidingData', error: err.message, stack: err.stack, payload });
243
+ }
244
+ }
245
+ onClickButtonMessage(d) {
246
+ try {
247
+ const message = Buffer.from(d);
248
+ const messageStr = message.toString('hex');
249
+ if (messageStr === this.prevClickMessage) {
250
+ return;
251
+ }
252
+ const status = zwift_hub_1.ClickKeyPadStatus.fromBinary(message);
253
+ if (status.buttonPlus === zwift_hub_1.PlayButtonStatus.OFF) {
254
+ const prev = Object.assign({}, this.upState);
255
+ this.upState = { pressed: false, timestamp: Date.now() };
256
+ if (prev.pressed) {
257
+ this.emit('key-pressed', { key: 'up', duration: this.upState.timestamp - prev.timestamp, deviceType: this.deviceType });
85
258
  }
86
- switch (messageStr) {
87
- case '001001':
88
- this.upState = { pressed: true, timestamp: Date.now() };
89
- break;
90
- case '011001':
91
- if ((_a = this.upState) === null || _a === void 0 ? void 0 : _a.pressed) {
92
- const prev = Object.assign({}, this.upState);
93
- this.upState = { pressed: false, timestamp: Date.now() };
94
- this.emit('key-pressed', { key: 'up', duration: this.upState.timestamp - prev.timestamp, deviceType: this.deviceType });
95
- }
96
- else if ((_b = this.downState) === null || _b === void 0 ? void 0 : _b.pressed) {
97
- const prev = Object.assign({}, this.downState);
98
- this.downState = { pressed: false, timestamp: Date.now() };
99
- this.emit('key-pressed', { key: 'down', duration: this.downState.timestamp - prev.timestamp, deviceType: this.deviceType });
100
- }
101
- else {
102
- this.upState = { pressed: false, timestamp: Date.now() };
103
- this.downState = { pressed: false, timestamp: Date.now() };
104
- }
105
- break;
106
- case '011000':
107
- this.downState = { pressed: true, timestamp: Date.now() };
108
- break;
259
+ }
260
+ else {
261
+ this.upState = { pressed: true, timestamp: Date.now() };
262
+ }
263
+ if (status.buttonMinus === zwift_hub_1.PlayButtonStatus.OFF) {
264
+ const prev = Object.assign({}, this.downState);
265
+ this.downState = { pressed: false, timestamp: Date.now() };
266
+ if (prev.pressed) {
267
+ this.emit('key-pressed', { key: 'down', duration: this.downState.timestamp - prev.timestamp, deviceType: this.deviceType });
109
268
  }
110
- this.prevClickMessage = messageStr;
111
269
  }
112
270
  else {
113
- this.logEvent({ message: 'Click measurement received', raw: message.toString('hex') });
271
+ this.downState = { pressed: true, timestamp: Date.now() };
114
272
  }
273
+ this.prevClickMessage = messageStr;
115
274
  }
116
275
  catch (err) {
117
276
  this.logEvent({ message: 'error', fn: 'onButtonMessage', error: err.message, stack: err.stack });
118
277
  }
119
278
  }
120
- onPingMessage(d) {
279
+ onPingMessage(message) {
280
+ const idle = zwift_hub_1.Idle.fromBinary(message);
281
+ console.log('# ping message', idle);
121
282
  this.emit('data', { deviceType: this.deviceType, paired: this.paired, alive: true, ts: Date.now() });
122
283
  }
123
- onPairResponse(d) {
284
+ onResponse(d) {
124
285
  const data = Buffer.from(d);
125
- const len = data.length;
126
- if (len === 6 && data.toString() === 'RideOn') {
127
- this.encrypted = false;
128
- this.paired = true;
129
- this.emitter.emit('paired');
130
- try {
131
- this.emit('data', { deviceType: this.deviceType, paired: true, ts: Date.now() });
132
- }
133
- catch (err) {
134
- this.logEvent({ message: 'error', fn: 'onPairResponse', error: err.message, stack: err.stack });
135
- }
286
+ if ((data === null || data === void 0 ? void 0 : data.length) < 1) {
287
+ console.log('Invalid response data', data.toString('hex'));
288
+ return false;
289
+ }
290
+ const type = data.readUInt8(0);
291
+ const message = data.subarray(1);
292
+ if (type === 0x3c) {
293
+ this.onDeviceInformation(message);
136
294
  }
137
- else if (len > 8 && Buffer.from(data.subarray(0, 6)).toString() === 'RideOn') {
138
- const message = Buffer.from(data.subarray(6, 2)).toString('hex');
139
- if (message === '0900') {
140
- this.encrypted = true;
295
+ else {
296
+ const len = data.length;
297
+ if (len === 6 && data.toString() === 'RideOn') {
298
+ this.encrypted = false;
141
299
  this.paired = true;
142
- this.deviceKey = data.subarray(8);
143
300
  this.emitter.emit('paired');
144
301
  try {
145
302
  this.emit('data', { deviceType: this.deviceType, paired: true, ts: Date.now() });
@@ -148,11 +305,26 @@ class BleZwiftPlaySensor extends sensor_1.TBleSensor {
148
305
  this.logEvent({ message: 'error', fn: 'onPairResponse', error: err.message, stack: err.stack });
149
306
  }
150
307
  }
151
- else {
152
- this.logEvent({ message: 'Pairing failed! ', reason: 'unknown message', raw: message });
308
+ else if (len > 8 && Buffer.from(data.subarray(0, 6)).toString() === 'RideOn') {
309
+ const message = Buffer.from(data.subarray(6, 2)).toString('hex');
310
+ if (message === '0900') {
311
+ this.encrypted = true;
312
+ this.paired = true;
313
+ this.deviceKey = data.subarray(8);
314
+ this.emitter.emit('paired');
315
+ try {
316
+ this.emit('data', { deviceType: this.deviceType, paired: true, ts: Date.now() });
317
+ }
318
+ catch (err) {
319
+ this.logEvent({ message: 'error', fn: 'onPairResponse', error: err.message, stack: err.stack });
320
+ }
321
+ }
322
+ else {
323
+ this.logEvent({ message: 'Pairing failed! ', reason: 'unknown message', raw: message });
324
+ }
153
325
  }
326
+ return true;
154
327
  }
155
- return true;
156
328
  }
157
329
  read(characteristic_1) {
158
330
  const _super = Object.create(null, {
@@ -172,6 +344,8 @@ class BleZwiftPlaySensor extends sensor_1.TBleSensor {
172
344
  pair() {
173
345
  return __awaiter(this, void 0, void 0, function* () {
174
346
  var _a, _b;
347
+ if (this.isPaired)
348
+ return true;
175
349
  let manufacturerData;
176
350
  try {
177
351
  if (this.peripheral.getManufacturerData) {
@@ -197,7 +371,7 @@ class BleZwiftPlaySensor extends sensor_1.TBleSensor {
197
371
  this.logEvent({ message: 'Play protocol pairing info', deviceType: this.deviceType, encrypted: this.encrypted, manufacturerData });
198
372
  let message;
199
373
  if (!this.encrypted) {
200
- message = Buffer.from('RideOn');
374
+ message = this.isFM ? Buffer.concat([Buffer.from('RideOn'), Buffer.from([0x02, 0x01])]) : Buffer.from('RideOn');
201
375
  }
202
376
  else {
203
377
  const { publicKey, privateKey } = this.createKeyPair();
@@ -206,10 +380,12 @@ class BleZwiftPlaySensor extends sensor_1.TBleSensor {
206
380
  message = Buffer.concat([Buffer.from('RideOn'), Buffer.from([0x01, 0x02]), this.publicKey]);
207
381
  }
208
382
  yield this.write((0, utils_1.fullUUID)('00000003-19ca-4651-86e5-fa29dcdd09d1'), message, { withoutResponse: true });
383
+ this.isPaired = true;
209
384
  return true;
210
385
  }
211
386
  catch (err) {
212
387
  this.logEvent({ message: 'error', fn: 'pair', error: err.message, stack: err.stack });
388
+ this.isPaired = false;
213
389
  return false;
214
390
  }
215
391
  });
@@ -0,0 +1,6 @@
1
+ export type HubRequest = {
2
+ DataId: number;
3
+ };
4
+ export type IHubHelper = {
5
+ createHubRequest(request: HubRequest): any;
6
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -18,9 +18,12 @@ export declare class DirectConnectPeripheral implements IBlePeripheral {
18
18
  protected eventEmitter: EventEmitter<[never]>;
19
19
  protected subscribed: Array<string>;
20
20
  protected onDisconnectHandler: () => void;
21
+ protected discoveredServiceUUIds: Array<string>;
21
22
  constructor(announcement: MulticastDnsAnnouncement);
22
23
  getInfo(): BleDeviceIdentifier;
23
24
  get services(): BleService[];
25
+ getAnnouncedServices(): string[];
26
+ getDiscoveredServices(): string[];
24
27
  connect(): Promise<boolean>;
25
28
  disconnect(connectionLost?: boolean): Promise<boolean>;
26
29
  isConnected(): boolean;
@@ -51,6 +51,13 @@ class DirectConnectPeripheral {
51
51
  const services = (_b = (_a = this.announcement) === null || _a === void 0 ? void 0 : _a.serviceUUIDs) !== null && _b !== void 0 ? _b : [];
52
52
  return services.map(s => ({ uuid: s }));
53
53
  }
54
+ getAnnouncedServices() {
55
+ var _a;
56
+ return (_a = this.announcement) === null || _a === void 0 ? void 0 : _a.serviceUUIDs.map(s => (0, utils_1.beautifyUUID)(s));
57
+ }
58
+ getDiscoveredServices() {
59
+ return this.discoveredServiceUUIds;
60
+ }
54
61
  connect() {
55
62
  return __awaiter(this, void 0, void 0, function* () {
56
63
  if (this.isConnected())
@@ -104,6 +111,7 @@ class DirectConnectPeripheral {
104
111
  return [];
105
112
  }
106
113
  this.logEvent({ message: 'DiscoverServices response', path: this.getPath(), rc, uuids, raw: response.toString('hex') });
114
+ this.discoveredServiceUUIds = res.body.serviceDefinitions.map(s => (0, utils_1.beautifyUUID)(s.serviceUUID));
107
115
  return res.body.serviceDefinitions.map(s => s.serviceUUID);
108
116
  }
109
117
  catch (err) {
@@ -48,8 +48,8 @@ export default class SmartTrainerCyclingMode extends PowerBasedCyclingModeBase i
48
48
  protected checkEmptyRequest(newRequest: UpdateRequest): void;
49
49
  protected getVirtualShiftMode(): VirtshiftMode;
50
50
  updateData(bikeData: IncyclistBikeData, log?: boolean): IncyclistBikeData;
51
+ getData(): Partial<IncyclistBikeData>;
51
52
  sendBikeUpdate(incoming: UpdateRequest): UpdateRequest;
52
- protected setInitialGear(data: IncyclistBikeData): void;
53
53
  protected getGearString(): string;
54
54
  }
55
55
  export {};
@@ -51,6 +51,12 @@ class SmartTrainerCyclingMode extends power_base_1.default {
51
51
  this.initLogger('SmartTrainerMode');
52
52
  }
53
53
  getBikeInitRequest() {
54
+ const virtshiftMode = this.getVirtualShiftMode();
55
+ if (virtshiftMode === 'Adapter') {
56
+ this.gear = this.getSetting('startGear');
57
+ const gearRatio = this.gearRatios[this.gear - 1];
58
+ return { slope: 0, gearRatio };
59
+ }
54
60
  this.prevRequest = { slope: 0 };
55
61
  return { slope: 0 };
56
62
  }
@@ -66,13 +72,19 @@ class SmartTrainerCyclingMode extends power_base_1.default {
66
72
  getConfig() {
67
73
  const config = super.getConfig();
68
74
  let virtshift = config.properties.find(p => p.key === 'virtshift');
75
+ let startGear = config.properties.find(p => p.key === 'startGear');
69
76
  if (!virtshift) {
70
- virtshift = { key: 'virtshift', name: 'Virtual Shifting', description: 'Enable virtual shifting', type: types_1.CyclingModeProperyType.SingleSelect, options: ['Disabled', 'Enabled', 'Mixed'], default: 'Disabled' };
77
+ virtshift = { key: 'virtshift', name: 'Virtual Shifting', description: 'Enable virtual shifting', type: types_1.CyclingModeProperyType.SingleSelect, options: ['Disabled', 'Incyclist', 'Mixed'], default: 'Disabled' };
71
78
  config.properties.push(virtshift);
72
79
  }
73
80
  if (this.adapter.supportsVirtualShifting()) {
74
81
  virtshift.default = 'Enabled';
75
- virtshift.options = ['Disabled', 'Enabled'];
82
+ virtshift.options = ['Disabled', 'Incyclist', 'SmartTrainer', 'Mixed'];
83
+ virtshift.default = 'SmartTrainer';
84
+ }
85
+ if (!startGear) {
86
+ startGear = { key: 'startGear', name: 'Initial Gear', description: 'Initial Gear', type: types_1.CyclingModeProperyType.Integer, default: 10, min: 1, max: 24, condition: (s) => (s === null || s === void 0 ? void 0 : s.virtshift) === 'Incyclist' || (s === null || s === void 0 ? void 0 : s.virtshift) === 'SmartTrainer' };
87
+ config.properties.push(startGear);
76
88
  }
77
89
  return config;
78
90
  }
@@ -133,6 +145,7 @@ class SmartTrainerCyclingMode extends power_base_1.default {
133
145
  const m = (_c = (_b = this.adapter) === null || _b === void 0 ? void 0 : _b.getWeight()) !== null && _c !== void 0 ? _c : 85;
134
146
  this.simPower = calculations_1.default.calculatePower(m, virtualSpeed, (_d = this.simSlope) !== null && _d !== void 0 ? _d : 0);
135
147
  this.verifySimPower();
148
+ this.logger.logEvent({ message: 'set simulater power', power: this.simPower, gear: this.gear, simSlope: this.simSlope, routeSlope: this.data.slope });
136
149
  }
137
150
  }
138
151
  newRequest.targetPower = this.simPower;
@@ -169,7 +182,8 @@ class SmartTrainerCyclingMode extends power_base_1.default {
169
182
  case 'Simulated':
170
183
  if (request.gearDelta !== undefined) {
171
184
  if (this.gear === undefined) {
172
- this.gear = 10 + request.gearDelta;
185
+ const initialGear = this.getSetting('startGear');
186
+ this.gear = initialGear + request.gearDelta;
173
187
  }
174
188
  else {
175
189
  this.gear += request.gearDelta;
@@ -185,9 +199,53 @@ class SmartTrainerCyclingMode extends power_base_1.default {
185
199
  const m = (_b = (_a = this.adapter) === null || _a === void 0 ? void 0 : _a.getWeight()) !== null && _b !== void 0 ? _b : 85;
186
200
  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
201
  this.verifySimPower();
202
+ this.logger.logEvent({ message: 'set simulater power', power: this.simPower, gear: this.gear, simSlope: this.simSlope, routeSlope: this.data.slope });
203
+ this.adapter.sendUpdate({ targetPower: this.simPower }).then(() => {
204
+ });
188
205
  }
189
206
  break;
190
207
  case 'Adapter':
208
+ if (request.gearRatio !== undefined) {
209
+ newRequest.gearRatio = request.gearRatio;
210
+ if (this.gear === undefined) {
211
+ const requestedRatio = request.gearRatio;
212
+ let closestIndex = 0;
213
+ let minDiff = Math.abs(this.gearRatios[0] - requestedRatio);
214
+ for (let i = 1; i < this.gearRatios.length; i++) {
215
+ const diff = Math.abs(this.gearRatios[i] - requestedRatio);
216
+ if (diff < minDiff) {
217
+ minDiff = diff;
218
+ closestIndex = i;
219
+ }
220
+ }
221
+ this.gear = closestIndex + 1;
222
+ }
223
+ }
224
+ else if (request.gearDelta !== undefined) {
225
+ if (this.gear === undefined) {
226
+ const initialGear = this.getSetting('startGear');
227
+ this.gear = initialGear + request.gearDelta;
228
+ }
229
+ else {
230
+ this.gear += request.gearDelta;
231
+ }
232
+ if (this.gear < 1) {
233
+ this.gear = 1;
234
+ }
235
+ if (this.gear > this.gearRatios.length) {
236
+ this.gear = this.gearRatios.length;
237
+ }
238
+ delete request.gearDelta;
239
+ newRequest.gearRatio = this.gearRatios[this.gear];
240
+ }
241
+ else {
242
+ if (this.gear === undefined) {
243
+ const initialGear = this.getSetting('startGear');
244
+ this.gear = initialGear + request.gearDelta;
245
+ }
246
+ newRequest.gearRatio = this.gearRatios[this.gear];
247
+ }
248
+ break;
191
249
  case 'Disabled':
192
250
  default:
193
251
  break;
@@ -214,17 +272,22 @@ class SmartTrainerCyclingMode extends power_base_1.default {
214
272
  if (virtshiftMode === 'Disabled') {
215
273
  return 'Disabled';
216
274
  }
217
- if (this.adapter.supportsVirtualShifting()) {
218
- if (virtshiftMode === 'Enabled') {
219
- return 'Adapter';
220
- }
275
+ else if (virtshiftMode === 'Incyclist') {
276
+ return 'Simulated';
277
+ }
278
+ else if (virtshiftMode === 'SmartTrainer') {
279
+ return 'Adapter';
221
280
  }
222
- else {
223
- return virtshiftMode === 'Mixed' ? 'SlopeDelta' : 'Simulated';
281
+ else if (virtshiftMode === 'Mixed') {
282
+ return 'SlopeDelta';
224
283
  }
284
+ else if (virtshiftMode === 'Enabled') {
285
+ return this.adapter.supportsVirtualShifting() ? 'Adapter' : 'Simulated';
286
+ }
287
+ return 'Disabled';
225
288
  }
226
289
  updateData(bikeData, log) {
227
- var _a, _b, _c, _d;
290
+ var _a, _b, _c, _d, _e;
228
291
  const prev = Object.assign({}, this.data);
229
292
  const data = super.updateData(bikeData, log);
230
293
  const mode = this.getVirtualShiftMode();
@@ -237,19 +300,24 @@ class SmartTrainerCyclingMode extends power_base_1.default {
237
300
  this.tsStart = Date.now();
238
301
  }
239
302
  if (this.gear === undefined && this.tsStart && data.power > 0 && (Date.now() - this.tsStart > 3000)) {
240
- this.setInitialGear(data);
303
+ this.gear = (_a = this.getSetting('startGear')) !== null && _a !== void 0 ? _a : 0;
241
304
  data.gearStr = this.getGearString();
242
305
  }
243
306
  else if (this.gear !== undefined) {
244
307
  if (prev.power !== data.power || prev.pedalRpm !== data.pedalRpm) {
245
308
  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);
309
+ const m = (_c = (_b = this.adapter) === null || _b === void 0 ? void 0 : _b.getWeight()) !== null && _c !== void 0 ? _c : 85;
310
+ 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);
248
311
  this.verifySimPower();
249
312
  }
250
313
  }
251
314
  return data;
252
315
  }
316
+ getData() {
317
+ const gearStr = this.getGearString();
318
+ const data = super.getData();
319
+ return Object.assign(Object.assign({}, data), { gearStr });
320
+ }
253
321
  sendBikeUpdate(incoming) {
254
322
  this.logger.logEvent({ message: "processing update request", request: incoming, prev: this.prevRequest, data: this.getData() });
255
323
  let newRequest = {};
@@ -270,15 +338,6 @@ class SmartTrainerCyclingMode extends power_base_1.default {
270
338
  }
271
339
  return newRequest;
272
340
  }
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
341
  getGearString() {
283
342
  const mode = this.getVirtualShiftMode();
284
343
  if (mode === "Disabled")
@@ -9,7 +9,7 @@ export default class PowerBasedCyclingModeBase extends CyclingModeBase {
9
9
  prevRequest: UpdateRequest | undefined;
10
10
  protected static config: CyclingModeConfig;
11
11
  constructor(adapter: IAdapter, props?: Settings);
12
- getData(): IncyclistBikeData;
12
+ getData(): Partial<IncyclistBikeData>;
13
13
  getSlope(): number;
14
14
  initLogger(defaultLogName: any): void;
15
15
  getWeight(): number;