incyclist-devices 3.0.15 → 3.0.16

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.
@@ -298,6 +298,11 @@ class BleInterface extends node_events_1.EventEmitter {
298
298
  const { peripheral } = service;
299
299
  if (peripheral.address === undefined || peripheral.address === '')
300
300
  peripheral.address = peripheral.id || peripheral.name;
301
+ if (service.name === 'Zwift Ride' && service.serviceUUIDs.some(uuid => (0, utils_js_1.matches)(uuid, 'FC82'))) {
302
+ const protocol = 'zwift-play';
303
+ const { id, name, address } = (0, utils_js_1.getPeripheralInfo)(peripheral);
304
+ return { interface: BleInterface.INTERFACE_NAME, protocol, id, name, address };
305
+ }
301
306
  const protocol = this.getAdapterFactory().getProtocol(service.serviceUUIDs);
302
307
  const { id, name, address } = (0, utils_js_1.getPeripheralInfo)(peripheral);
303
308
  return { interface: BleInterface.INTERFACE_NAME, protocol, id, name, address };
@@ -573,6 +578,8 @@ class BleInterface extends node_events_1.EventEmitter {
573
578
  return false;
574
579
  const found = service.serviceUUIDs.map(utils_js_1.parseUUID);
575
580
  const expected = this.expectedServices.map(utils_js_1.parseUUID);
581
+ if (service.name.startsWith('Zwift'))
582
+ return true;
576
583
  const supported = found.filter(uuid => expected.includes(uuid)) ?? [];
577
584
  if (!supported.length) {
578
585
  this.logEvent({ message: 'peripheral not supported', name: service.name, uuids: service.serviceUUIDs });
@@ -235,9 +235,11 @@ class BleFmAdapter extends adapter_js_1.default {
235
235
  async checkCapabilities() {
236
236
  const before = this.capabilities.join(',');
237
237
  const sensor = this.getSensor();
238
+ let updateRequired = false;
238
239
  if (!sensor.features) {
239
240
  try {
240
241
  await sensor.getFitnessMachineFeatures();
242
+ updateRequired = true;
241
243
  }
242
244
  catch (err) {
243
245
  this.logEvent({ message: 'error getting fitness machine features', device: this.getName(), interface: this.getInterface(), error: err });
@@ -250,6 +252,9 @@ class BleFmAdapter extends adapter_js_1.default {
250
252
  if (before !== after) {
251
253
  this.logEvent({ message: 'device capabilities updated', name: this.getSettings().name, interface: this.getSettings().interface, capabilities: this.capabilities });
252
254
  this.emit('device-info', this.getSettings(), { capabilities: this.capabilities });
255
+ updateRequired = true;
256
+ }
257
+ if (updateRequired) {
253
258
  this.updateCyclingModeConfig();
254
259
  }
255
260
  }
@@ -19,6 +19,7 @@ class BleZwiftPlaySensor extends sensor_js_1.TBleSensor {
19
19
  prevClickMessage;
20
20
  upState;
21
21
  downState;
22
+ rideKeyPadStates;
22
23
  deviceType;
23
24
  publicKey;
24
25
  privateKey;
@@ -172,6 +173,9 @@ class BleZwiftPlaySensor extends sensor_js_1.TBleSensor {
172
173
  else if (type === 0x03) {
173
174
  this.onRidingData(message);
174
175
  }
176
+ else if (type === 0x23) {
177
+ this.onRideKeyPadStatus(message);
178
+ }
175
179
  else if (type === 0x2A) {
176
180
  this.onTrainerResponse(message);
177
181
  }
@@ -285,6 +289,53 @@ class BleZwiftPlaySensor extends sensor_js_1.TBleSensor {
285
289
  this.logEvent({ message: 'Error', fn: 'onRidingData', error: err.message, stack: err.stack });
286
290
  }
287
291
  }
292
+ onRideKeyPadStatus(m) {
293
+ try {
294
+ const data = zwift_hub_js_1.RideKeyPadStatus.fromBinary(m);
295
+ const buttonNames = new Map([
296
+ [0x10, 'a'],
297
+ [0x20, 'b'],
298
+ [0x40, 'y'],
299
+ [0x80, 'z'],
300
+ [0x1000, 'r-shift-up'],
301
+ [0x2000, 'r-shift-down'],
302
+ [0x4000, 'ride-on'],
303
+ [0x8000, 'r-power']
304
+ ]);
305
+ const buttonMap = data.buttonMap ?? 0;
306
+ const currentPresses = new Set();
307
+ buttonNames.forEach((name, bit) => {
308
+ const isPressed = (buttonMap & bit) === 0;
309
+ if (isPressed) {
310
+ currentPresses.add(bit);
311
+ const prevState = this.rideKeyPadStates.get(bit);
312
+ if (!prevState || !prevState.pressed) {
313
+ this.rideKeyPadStates.set(bit, { pressed: true, timestamp: Date.now() });
314
+ }
315
+ }
316
+ });
317
+ this.rideKeyPadStates.forEach((state, bit) => {
318
+ if (!currentPresses.has(bit) && state.pressed) {
319
+ const keyName = buttonNames.get(bit);
320
+ const duration = Date.now() - state.timestamp;
321
+ this.emit('key-pressed', { key: keyName, duration, deviceType: this.deviceType });
322
+ this.rideKeyPadStates.set(bit, { pressed: false, timestamp: Date.now() });
323
+ }
324
+ });
325
+ const pressedButtons = Array.from(currentPresses).map(bit => ({
326
+ bit: `0x${bit.toString(16).padStart(8, '0')}`,
327
+ name: buttonNames.get(bit)
328
+ }));
329
+ this.logEvent({
330
+ message: 'ride keypad status received',
331
+ buttonMap: `0x${buttonMap.toString(16)}`,
332
+ buttons: pressedButtons
333
+ });
334
+ }
335
+ catch (err) {
336
+ this.logEvent({ message: 'Error', fn: 'onRideKeyPadStatus', error: err.message, stack: err.stack });
337
+ }
338
+ }
288
339
  onDeviceInformation(m) {
289
340
  try {
290
341
  const envelope = zwift_hub_js_1.DeviceDataEnvelope.fromBinary(m);
@@ -506,6 +557,7 @@ class BleZwiftPlaySensor extends sensor_js_1.TBleSensor {
506
557
  this.isHubServiceActive = false;
507
558
  delete this.initHubServicePromise;
508
559
  delete this.prevHubSettings;
560
+ this.rideKeyPadStates = new Map();
509
561
  }
510
562
  getManufacturerData() {
511
563
  const data = this.peripheral.getManufacturerData();
@@ -234,10 +234,13 @@ class SmartTrainerCyclingMode extends power_base_js_1.default {
234
234
  const m = this.adapter?.getWeight() ?? 85;
235
235
  const vCurrent = this.data.speed * 1000 / 3600;
236
236
  const eKinCurrent = m * vCurrent * vCurrent / 2;
237
- if (this.data.pedalRpm > 0) {
238
- const virtualSpeed = (0, calculations_js_1.calculateVirtualSpeed)(this.data.pedalRpm, this.gearRatios[this.gear - 1]) * 3.6;
237
+ const cadence = this.data?.pedalRpm ?? 0;
238
+ const gear = this.gear ?? 1;
239
+ const simSlope = this.simSlope ?? 0;
240
+ if (cadence > 0) {
241
+ const virtualSpeed = (0, calculations_js_1.calculateVirtualSpeed)(cadence, this.gearRatios[gear - 1]) * 3.6;
239
242
  const v = virtualSpeed / 3.6;
240
- const newPower = calculations_js_1.default.calculatePower(m, virtualSpeed / 3.6, this.simSlope ?? 0);
243
+ const newPower = calculations_js_1.default.calculatePower(m, virtualSpeed / 3.6, simSlope);
241
244
  const prevPower = this.data.power;
242
245
  const prev = (this.prevSimPower ?? prevPower ?? 0);
243
246
  const delta = newPower - prev;
@@ -253,7 +256,7 @@ class SmartTrainerCyclingMode extends power_base_js_1.default {
253
256
  this.logEvent({ message: 'set simulated power (gear change)', target: this.simPower, gear: this.gear, simSlope: this.simSlope, routeSlope: this.data.slope, prevTarget: this.prevSimPower, actualPower: prevPower, newPower });
254
257
  }
255
258
  else if (changed === 'slope' || changed === 'cadence') {
256
- const adjustTime = this.simSlope < 0 ? 5 : 3;
259
+ const adjustTime = (this.simSlope ?? 0) < 0 ? 5 : 3;
257
260
  this.simPower = prev + delta / adjustTime;
258
261
  this.logEvent({ message: `set simulated power (${changed} change)`, target: this.simPower, gear: this.gear, simSlope: this.simSlope, routeSlope: this.data.slope, prevTarget: this.prevSimPower, actualPower: prevPower, newPower });
259
262
  }
@@ -344,9 +347,13 @@ class SmartTrainerCyclingMode extends power_base_js_1.default {
344
347
  else {
345
348
  if (this.gear === undefined) {
346
349
  const initialGear = (0, utils_js_1.intVal)(this.getSetting('startGear'));
347
- this.gear = initialGear + request.gearDelta;
350
+ this.gear = initialGear + (request.gearDelta ?? 0);
348
351
  }
349
- newRequest.gearRatio = this.gearRatios[this.gear];
352
+ if (this.gear < 1)
353
+ this.gear = 1;
354
+ if (this.gear > this.gearRatios.length - 1)
355
+ this.gear = this.gearRatios.length - 1;
356
+ newRequest.gearRatio = this.gearRatios[this.gear - 1];
350
357
  this.logEvent({ message: 'gear initialized', gear: this.gear, gearRatio: newRequest.gearRatio });
351
358
  }
352
359
  break;
@@ -356,10 +363,10 @@ class SmartTrainerCyclingMode extends power_base_js_1.default {
356
363
  }
357
364
  }
358
365
  verifySimPower() {
359
- if (this.simPower < 0) {
366
+ if ((this.simPower ?? 0) < 0) {
360
367
  this.simPower = 0;
361
368
  }
362
- if (this.data.pedalRpm > 0 && this.simPower < MIN_POWER) {
369
+ if ((this.data.pedalRpm ?? 0) > 0 && (this.simPower ?? 0) < MIN_POWER) {
363
370
  this.simPower = MIN_POWER;
364
371
  }
365
372
  if (!this.data.isPedalling) {
@@ -70,5 +70,7 @@ class CyclingMode {
70
70
  }
71
71
  confirmed(request) {
72
72
  }
73
+ onAdapterCapabilitiesChanged() {
74
+ }
73
75
  }
74
76
  exports.CyclingMode = CyclingMode;
@@ -672,11 +672,12 @@ class RideKeyPadStatus$Type extends runtime_4.MessageType {
672
672
  constructor() {
673
673
  super("org.cagnulen.qdomyoszwift.RideKeyPadStatus", [
674
674
  { no: 1, name: "ButtonMap", kind: "scalar", jsonName: "ButtonMap", opt: true, T: 13 },
675
- { no: 2, name: "AnalogButtons", kind: "message", jsonName: "AnalogButtons", T: () => exports.RideAnalogKeyGroup }
675
+ { no: 3, name: "AnalogButtons", kind: "message", jsonName: "AnalogButtons", repeat: 2, T: () => exports.RideAnalogKeyPress }
676
676
  ]);
677
677
  }
678
678
  create(value) {
679
679
  const message = globalThis.Object.create((this.messagePrototype));
680
+ message.analogButtons = [];
680
681
  if (value !== undefined)
681
682
  (0, runtime_3.reflectionMergePartial)(this, message, value);
682
683
  return message;
@@ -689,8 +690,8 @@ class RideKeyPadStatus$Type extends runtime_4.MessageType {
689
690
  case 1:
690
691
  message.buttonMap = reader.uint32();
691
692
  break;
692
- case 2:
693
- message.analogButtons = exports.RideAnalogKeyGroup.internalBinaryRead(reader, reader.uint32(), options, message.analogButtons);
693
+ case 3:
694
+ message.analogButtons.push(exports.RideAnalogKeyPress.internalBinaryRead(reader, reader.uint32(), options));
694
695
  break;
695
696
  default:
696
697
  let u = options.readUnknownField;
@@ -706,8 +707,8 @@ class RideKeyPadStatus$Type extends runtime_4.MessageType {
706
707
  internalBinaryWrite(message, writer, options) {
707
708
  if (message.buttonMap !== undefined)
708
709
  writer.tag(1, runtime_1.WireType.Varint).uint32(message.buttonMap);
709
- if (message.analogButtons)
710
- exports.RideAnalogKeyGroup.internalBinaryWrite(message.analogButtons, writer.tag(2, runtime_1.WireType.LengthDelimited).fork(), options).join();
710
+ for (let i = 0; i < message.analogButtons.length; i++)
711
+ exports.RideAnalogKeyPress.internalBinaryWrite(message.analogButtons[i], writer.tag(3, runtime_1.WireType.LengthDelimited).fork(), options).join();
711
712
  let u = options.writeUnknownFields;
712
713
  if (u !== false)
713
714
  (u == true ? runtime_2.UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
@@ -2,7 +2,7 @@ import { EventEmitter } from "node:events";
2
2
  import { EventLogger } from "gd-eventlog";
3
3
  import { InteruptableTask } from "../../utils/task.js";
4
4
  import { BlePeripheral } from "./peripheral.js";
5
- import { beautifyUUID, getPeripheralInfo, parseUUID } from "../utils.js";
5
+ import { beautifyUUID, getPeripheralInfo, matches, parseUUID } from "../utils.js";
6
6
  import { InterfaceFactory } from "./types.js";
7
7
  import { BleAdapterFactory } from "../factories/index.js";
8
8
  const BLE_EXPIRATION_TIMEOUT = 10 * 1000 * 60;
@@ -295,6 +295,11 @@ export class BleInterface extends EventEmitter {
295
295
  const { peripheral } = service;
296
296
  if (peripheral.address === undefined || peripheral.address === '')
297
297
  peripheral.address = peripheral.id || peripheral.name;
298
+ if (service.name === 'Zwift Ride' && service.serviceUUIDs.some(uuid => matches(uuid, 'FC82'))) {
299
+ const protocol = 'zwift-play';
300
+ const { id, name, address } = getPeripheralInfo(peripheral);
301
+ return { interface: BleInterface.INTERFACE_NAME, protocol, id, name, address };
302
+ }
298
303
  const protocol = this.getAdapterFactory().getProtocol(service.serviceUUIDs);
299
304
  const { id, name, address } = getPeripheralInfo(peripheral);
300
305
  return { interface: BleInterface.INTERFACE_NAME, protocol, id, name, address };
@@ -570,6 +575,8 @@ export class BleInterface extends EventEmitter {
570
575
  return false;
571
576
  const found = service.serviceUUIDs.map(parseUUID);
572
577
  const expected = this.expectedServices.map(parseUUID);
578
+ if (service.name.startsWith('Zwift'))
579
+ return true;
573
580
  const supported = found.filter(uuid => expected.includes(uuid)) ?? [];
574
581
  if (!supported.length) {
575
582
  this.logEvent({ message: 'peripheral not supported', name: service.name, uuids: service.serviceUUIDs });
@@ -230,9 +230,11 @@ export default class BleFmAdapter extends BleAdapter {
230
230
  async checkCapabilities() {
231
231
  const before = this.capabilities.join(',');
232
232
  const sensor = this.getSensor();
233
+ let updateRequired = false;
233
234
  if (!sensor.features) {
234
235
  try {
235
236
  await sensor.getFitnessMachineFeatures();
237
+ updateRequired = true;
236
238
  }
237
239
  catch (err) {
238
240
  this.logEvent({ message: 'error getting fitness machine features', device: this.getName(), interface: this.getInterface(), error: err });
@@ -245,6 +247,9 @@ export default class BleFmAdapter extends BleAdapter {
245
247
  if (before !== after) {
246
248
  this.logEvent({ message: 'device capabilities updated', name: this.getSettings().name, interface: this.getSettings().interface, capabilities: this.capabilities });
247
249
  this.emit('device-info', this.getSettings(), { capabilities: this.capabilities });
250
+ updateRequired = true;
251
+ }
252
+ if (updateRequired) {
248
253
  this.updateCyclingModeConfig();
249
254
  }
250
255
  }
@@ -1,4 +1,4 @@
1
- import { ClickKeyPadStatus, DeviceDataEnvelope, DeviceInformationContent, DeviceSettings, HubCommand, HubRequest, HubRidingData, Idle, PlayButtonStatus, TrainerResponse } from "../../../proto/zwift_hub.js";
1
+ import { ClickKeyPadStatus, DeviceDataEnvelope, DeviceInformationContent, DeviceSettings, HubCommand, HubRequest, HubRidingData, Idle, PlayButtonStatus, RideKeyPadStatus, TrainerResponse } from "../../../proto/zwift_hub.js";
2
2
  import { TBleSensor } from "../../base/sensor.js";
3
3
  import { beautifyUUID, fullUUID } from "../../utils.js";
4
4
  import { EventEmitter } from "node:events";
@@ -16,6 +16,7 @@ export class BleZwiftPlaySensor extends TBleSensor {
16
16
  prevClickMessage;
17
17
  upState;
18
18
  downState;
19
+ rideKeyPadStates;
19
20
  deviceType;
20
21
  publicKey;
21
22
  privateKey;
@@ -169,6 +170,9 @@ export class BleZwiftPlaySensor extends TBleSensor {
169
170
  else if (type === 0x03) {
170
171
  this.onRidingData(message);
171
172
  }
173
+ else if (type === 0x23) {
174
+ this.onRideKeyPadStatus(message);
175
+ }
172
176
  else if (type === 0x2A) {
173
177
  this.onTrainerResponse(message);
174
178
  }
@@ -282,6 +286,53 @@ export class BleZwiftPlaySensor extends TBleSensor {
282
286
  this.logEvent({ message: 'Error', fn: 'onRidingData', error: err.message, stack: err.stack });
283
287
  }
284
288
  }
289
+ onRideKeyPadStatus(m) {
290
+ try {
291
+ const data = RideKeyPadStatus.fromBinary(m);
292
+ const buttonNames = new Map([
293
+ [0x10, 'a'],
294
+ [0x20, 'b'],
295
+ [0x40, 'y'],
296
+ [0x80, 'z'],
297
+ [0x1000, 'r-shift-up'],
298
+ [0x2000, 'r-shift-down'],
299
+ [0x4000, 'ride-on'],
300
+ [0x8000, 'r-power']
301
+ ]);
302
+ const buttonMap = data.buttonMap ?? 0;
303
+ const currentPresses = new Set();
304
+ buttonNames.forEach((name, bit) => {
305
+ const isPressed = (buttonMap & bit) === 0;
306
+ if (isPressed) {
307
+ currentPresses.add(bit);
308
+ const prevState = this.rideKeyPadStates.get(bit);
309
+ if (!prevState || !prevState.pressed) {
310
+ this.rideKeyPadStates.set(bit, { pressed: true, timestamp: Date.now() });
311
+ }
312
+ }
313
+ });
314
+ this.rideKeyPadStates.forEach((state, bit) => {
315
+ if (!currentPresses.has(bit) && state.pressed) {
316
+ const keyName = buttonNames.get(bit);
317
+ const duration = Date.now() - state.timestamp;
318
+ this.emit('key-pressed', { key: keyName, duration, deviceType: this.deviceType });
319
+ this.rideKeyPadStates.set(bit, { pressed: false, timestamp: Date.now() });
320
+ }
321
+ });
322
+ const pressedButtons = Array.from(currentPresses).map(bit => ({
323
+ bit: `0x${bit.toString(16).padStart(8, '0')}`,
324
+ name: buttonNames.get(bit)
325
+ }));
326
+ this.logEvent({
327
+ message: 'ride keypad status received',
328
+ buttonMap: `0x${buttonMap.toString(16)}`,
329
+ buttons: pressedButtons
330
+ });
331
+ }
332
+ catch (err) {
333
+ this.logEvent({ message: 'Error', fn: 'onRideKeyPadStatus', error: err.message, stack: err.stack });
334
+ }
335
+ }
285
336
  onDeviceInformation(m) {
286
337
  try {
287
338
  const envelope = DeviceDataEnvelope.fromBinary(m);
@@ -503,6 +554,7 @@ export class BleZwiftPlaySensor extends TBleSensor {
503
554
  this.isHubServiceActive = false;
504
555
  delete this.initHubServicePromise;
505
556
  delete this.prevHubSettings;
557
+ this.rideKeyPadStates = new Map();
506
558
  }
507
559
  getManufacturerData() {
508
560
  const data = this.peripheral.getManufacturerData();
@@ -196,10 +196,13 @@ export default class SmartTrainerCyclingMode extends PowerBasedCyclingModeBase {
196
196
  const m = this.adapter?.getWeight() ?? 85;
197
197
  const vCurrent = this.data.speed * 1000 / 3600;
198
198
  const eKinCurrent = m * vCurrent * vCurrent / 2;
199
- if (this.data.pedalRpm > 0) {
200
- const virtualSpeed = calculateVirtualSpeed(this.data.pedalRpm, this.gearRatios[this.gear - 1]) * 3.6;
199
+ const cadence = this.data?.pedalRpm ?? 0;
200
+ const gear = this.gear ?? 1;
201
+ const simSlope = this.simSlope ?? 0;
202
+ if (cadence > 0) {
203
+ const virtualSpeed = calculateVirtualSpeed(cadence, this.gearRatios[gear - 1]) * 3.6;
201
204
  const v = virtualSpeed / 3.6;
202
- const newPower = calc.calculatePower(m, virtualSpeed / 3.6, this.simSlope ?? 0);
205
+ const newPower = calc.calculatePower(m, virtualSpeed / 3.6, simSlope);
203
206
  const prevPower = this.data.power;
204
207
  const prev = (this.prevSimPower ?? prevPower ?? 0);
205
208
  const delta = newPower - prev;
@@ -215,7 +218,7 @@ export default class SmartTrainerCyclingMode extends PowerBasedCyclingModeBase {
215
218
  this.logEvent({ message: 'set simulated power (gear change)', target: this.simPower, gear: this.gear, simSlope: this.simSlope, routeSlope: this.data.slope, prevTarget: this.prevSimPower, actualPower: prevPower, newPower });
216
219
  }
217
220
  else if (changed === 'slope' || changed === 'cadence') {
218
- const adjustTime = this.simSlope < 0 ? 5 : 3;
221
+ const adjustTime = (this.simSlope ?? 0) < 0 ? 5 : 3;
219
222
  this.simPower = prev + delta / adjustTime;
220
223
  this.logEvent({ message: `set simulated power (${changed} change)`, target: this.simPower, gear: this.gear, simSlope: this.simSlope, routeSlope: this.data.slope, prevTarget: this.prevSimPower, actualPower: prevPower, newPower });
221
224
  }
@@ -306,9 +309,13 @@ export default class SmartTrainerCyclingMode extends PowerBasedCyclingModeBase {
306
309
  else {
307
310
  if (this.gear === undefined) {
308
311
  const initialGear = intVal(this.getSetting('startGear'));
309
- this.gear = initialGear + request.gearDelta;
312
+ this.gear = initialGear + (request.gearDelta ?? 0);
310
313
  }
311
- newRequest.gearRatio = this.gearRatios[this.gear];
314
+ if (this.gear < 1)
315
+ this.gear = 1;
316
+ if (this.gear > this.gearRatios.length - 1)
317
+ this.gear = this.gearRatios.length - 1;
318
+ newRequest.gearRatio = this.gearRatios[this.gear - 1];
312
319
  this.logEvent({ message: 'gear initialized', gear: this.gear, gearRatio: newRequest.gearRatio });
313
320
  }
314
321
  break;
@@ -318,10 +325,10 @@ export default class SmartTrainerCyclingMode extends PowerBasedCyclingModeBase {
318
325
  }
319
326
  }
320
327
  verifySimPower() {
321
- if (this.simPower < 0) {
328
+ if ((this.simPower ?? 0) < 0) {
322
329
  this.simPower = 0;
323
330
  }
324
- if (this.data.pedalRpm > 0 && this.simPower < MIN_POWER) {
331
+ if ((this.data.pedalRpm ?? 0) > 0 && (this.simPower ?? 0) < MIN_POWER) {
325
332
  this.simPower = MIN_POWER;
326
333
  }
327
334
  if (!this.data.isPedalling) {
@@ -67,4 +67,6 @@ export class CyclingMode {
67
67
  }
68
68
  confirmed(request) {
69
69
  }
70
+ onAdapterCapabilitiesChanged() {
71
+ }
70
72
  }
@@ -669,11 +669,12 @@ class RideKeyPadStatus$Type extends MessageType {
669
669
  constructor() {
670
670
  super("org.cagnulen.qdomyoszwift.RideKeyPadStatus", [
671
671
  { no: 1, name: "ButtonMap", kind: "scalar", jsonName: "ButtonMap", opt: true, T: 13 },
672
- { no: 2, name: "AnalogButtons", kind: "message", jsonName: "AnalogButtons", T: () => RideAnalogKeyGroup }
672
+ { no: 3, name: "AnalogButtons", kind: "message", jsonName: "AnalogButtons", repeat: 2, T: () => RideAnalogKeyPress }
673
673
  ]);
674
674
  }
675
675
  create(value) {
676
676
  const message = globalThis.Object.create((this.messagePrototype));
677
+ message.analogButtons = [];
677
678
  if (value !== undefined)
678
679
  reflectionMergePartial(this, message, value);
679
680
  return message;
@@ -686,8 +687,8 @@ class RideKeyPadStatus$Type extends MessageType {
686
687
  case 1:
687
688
  message.buttonMap = reader.uint32();
688
689
  break;
689
- case 2:
690
- message.analogButtons = RideAnalogKeyGroup.internalBinaryRead(reader, reader.uint32(), options, message.analogButtons);
690
+ case 3:
691
+ message.analogButtons.push(RideAnalogKeyPress.internalBinaryRead(reader, reader.uint32(), options));
691
692
  break;
692
693
  default:
693
694
  let u = options.readUnknownField;
@@ -703,8 +704,8 @@ class RideKeyPadStatus$Type extends MessageType {
703
704
  internalBinaryWrite(message, writer, options) {
704
705
  if (message.buttonMap !== undefined)
705
706
  writer.tag(1, WireType.Varint).uint32(message.buttonMap);
706
- if (message.analogButtons)
707
- RideAnalogKeyGroup.internalBinaryWrite(message.analogButtons, writer.tag(2, WireType.LengthDelimited).fork(), options).join();
707
+ for (let i = 0; i < message.analogButtons.length; i++)
708
+ RideAnalogKeyPress.internalBinaryWrite(message.analogButtons[i], writer.tag(3, WireType.LengthDelimited).fork(), options).join();
708
709
  let u = options.writeUnknownFields;
709
710
  if (u !== false)
710
711
  (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
@@ -27,6 +27,7 @@ export declare class BleZwiftPlaySensor extends TBleSensor {
27
27
  protected prevClickMessage: string;
28
28
  protected upState: ButtonState;
29
29
  protected downState: ButtonState;
30
+ protected rideKeyPadStates: Map<number, ButtonState>;
30
31
  protected deviceType: DeviceType;
31
32
  protected publicKey: Buffer;
32
33
  protected privateKey: Buffer;
@@ -56,6 +57,7 @@ export declare class BleZwiftPlaySensor extends TBleSensor {
56
57
  sendHubCommand(command: HubCommand): Promise<Buffer<ArrayBufferLike>>;
57
58
  protected onTrainerResponse(m: Buffer): void;
58
59
  protected onRidingData(m: Buffer): void;
60
+ protected onRideKeyPadStatus(m: Buffer): void;
59
61
  protected onDeviceInformation(m: Buffer): void;
60
62
  onClickButtonMessage(d: Buffer): void;
61
63
  onPingMessage(message: Buffer): void;
@@ -28,14 +28,14 @@ export default class SmartTrainerCyclingMode extends PowerBasedCyclingModeBase i
28
28
  })[];
29
29
  };
30
30
  protected gearDelta: number;
31
- protected gear: number;
32
- protected tsStart: number;
33
- protected simPower: number;
34
- protected simSlope: number;
35
- protected maintainPower: number;
36
- protected prevData: any;
37
- protected prevEkin: number;
38
- protected prevSimPower: number;
31
+ protected gear?: number;
32
+ protected tsStart?: number;
33
+ protected simPower?: number;
34
+ protected simSlope?: number;
35
+ protected maintainPower?: number;
36
+ protected prevData?: any;
37
+ protected prevEkin?: number;
38
+ protected prevSimPower?: number;
39
39
  protected readonly gearRatios: number[];
40
40
  constructor(adapter: IAdapter, props?: any);
41
41
  getBikeInitRequest(): UpdateRequest;
@@ -58,6 +58,6 @@ export default class SmartTrainerCyclingMode extends PowerBasedCyclingModeBase i
58
58
  getData(): Partial<IncyclistBikeData>;
59
59
  protected updateRequired(request?: UpdateRequest): boolean;
60
60
  sendBikeUpdate(incoming: UpdateRequest): UpdateRequest;
61
- protected getGearString(): string;
61
+ protected getGearString(): string | undefined;
62
62
  protected getFeatureToogle(): import("../features/features.js").FeatureToggle;
63
63
  }
@@ -5,7 +5,7 @@ export declare abstract class CyclingModeBase extends CyclingMode implements ICy
5
5
  adapter: IAdapter;
6
6
  settings: Settings;
7
7
  properties: Settings;
8
- localConfig: CyclingModeConfig;
8
+ localConfig?: CyclingModeConfig;
9
9
  protected static config: CyclingModeConfig;
10
10
  protected static isERG: boolean;
11
11
  protected prevUpdate: UpdateRequest;
@@ -47,7 +47,7 @@ export default interface ICyclingMode {
47
47
  buildUpdate(request: UpdateRequest): UpdateRequest;
48
48
  confirmed(request: UpdateRequest): void;
49
49
  updateData(data: IncyclistBikeData): IncyclistBikeData;
50
- setSettings(settings: any): any;
50
+ setSettings(settings: any): void;
51
51
  setSetting(name: string, value: any): void;
52
52
  getSetting(name: string): any;
53
53
  getSettings(): Settings;
@@ -88,4 +88,5 @@ export declare class CyclingMode implements ICyclingMode {
88
88
  resetConfig(): void;
89
89
  getData(): Partial<IncyclistBikeData>;
90
90
  confirmed(request: UpdateRequest): void;
91
+ onAdapterCapabilitiesChanged(): void;
91
92
  }
@@ -65,7 +65,7 @@ export interface RideAnalogKeyGroup {
65
65
  }
66
66
  export interface RideKeyPadStatus {
67
67
  buttonMap?: number;
68
- analogButtons?: RideAnalogKeyGroup;
68
+ analogButtons: RideAnalogKeyPress[];
69
69
  }
70
70
  export interface TrainerResponse {
71
71
  unknown?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "incyclist-devices",
3
- "version": "3.0.15",
3
+ "version": "3.0.16",
4
4
  "scripts": {
5
5
  "lint": "eslint . --ext .ts",
6
6
  "build": "npm run build:esm && npm run build:cjs",
@@ -31,6 +31,7 @@
31
31
  "@serialport/binding-mock": "^10.2.2",
32
32
  "@serialport/bindings-cpp": "^13.0.1",
33
33
  "@stoprocent/noble": "^2.4.0",
34
+ "@types/jest": "^30.0.0",
34
35
  "@types/node": "^25.6.0",
35
36
  "@typescript-eslint/eslint-plugin": "^8.59.0",
36
37
  "@typescript-eslint/parser": "^8.59.0",