incyclist-devices 3.0.16 → 3.0.17
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.
- package/lib/cjs/ble/base/adapter.js +2 -1
- package/lib/cjs/ble/base/interface.js +3 -3
- package/lib/cjs/ble/fm/adapter.js +0 -6
- package/lib/cjs/ble/zwift/play/adapter.js +33 -4
- package/lib/cjs/ble/zwift/play/sensor.js +74 -26
- package/lib/cjs/modes/antble-smarttrainer.js +27 -21
- package/lib/esm/ble/base/adapter.js +2 -1
- package/lib/esm/ble/base/interface.js +3 -3
- package/lib/esm/ble/fm/adapter.js +0 -6
- package/lib/esm/ble/zwift/play/adapter.js +33 -4
- package/lib/esm/ble/zwift/play/sensor.js +74 -26
- package/lib/esm/modes/antble-smarttrainer.js +27 -21
- package/lib/types/ble/zwift/play/adapter.d.ts +5 -0
- package/lib/types/ble/zwift/play/sensor.d.ts +3 -2
- package/package.json +1 -1
|
@@ -29,7 +29,8 @@ class BleAdapter extends adpater_js_1.default {
|
|
|
29
29
|
if (settings.name?.match(/\d/g) || settings.address === undefined)
|
|
30
30
|
return this.getName();
|
|
31
31
|
else {
|
|
32
|
-
const
|
|
32
|
+
const id = (settings.id ?? settings.address ?? '').replace(/[:\-]/g, '');
|
|
33
|
+
const addressHash = id.length > 4 ? id.slice(-4).toUpperCase() : id.toUpperCase();
|
|
33
34
|
return `${this.getName()} ${addressHash}`;
|
|
34
35
|
}
|
|
35
36
|
}
|
|
@@ -267,7 +267,7 @@ class BleInterface extends node_events_1.EventEmitter {
|
|
|
267
267
|
return new peripheral_js_1.BlePeripheral(announcement);
|
|
268
268
|
}
|
|
269
269
|
createPeripheralFromSettings(settings) {
|
|
270
|
-
const info = this.getAll().find(a => a.service?.
|
|
270
|
+
const info = this.getAll().find(a => a.service?.peripheral?.address === settings.address);
|
|
271
271
|
if (!info?.service)
|
|
272
272
|
return null;
|
|
273
273
|
return this.createPeripheral(info.service);
|
|
@@ -281,7 +281,7 @@ class BleInterface extends node_events_1.EventEmitter {
|
|
|
281
281
|
if (!wasDiscovering)
|
|
282
282
|
this.startPeripheralScan();
|
|
283
283
|
const onDevice = (device) => {
|
|
284
|
-
if (device.
|
|
284
|
+
if (device.address === settings.address) {
|
|
285
285
|
const peripheral = this.createPeripheralFromSettings(device);
|
|
286
286
|
if (peripheral) {
|
|
287
287
|
this.off('device', onDevice);
|
|
@@ -588,7 +588,7 @@ class BleInterface extends node_events_1.EventEmitter {
|
|
|
588
588
|
return supported.length > 0;
|
|
589
589
|
}
|
|
590
590
|
find(service) {
|
|
591
|
-
return this.services.find(a => a.service.name === service.name && a.ts > Date.now() - BLE_EXPIRATION_TIMEOUT);
|
|
591
|
+
return this.services.find(a => a.service.name === service.name && a.service?.peripheral?.address && a.ts > Date.now() - BLE_EXPIRATION_TIMEOUT);
|
|
592
592
|
}
|
|
593
593
|
getAll() {
|
|
594
594
|
return this.services.filter(a => a.ts > Date.now() - BLE_EXPIRATION_TIMEOUT);
|
|
@@ -235,11 +235,9 @@ 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;
|
|
239
238
|
if (!sensor.features) {
|
|
240
239
|
try {
|
|
241
240
|
await sensor.getFitnessMachineFeatures();
|
|
242
|
-
updateRequired = true;
|
|
243
241
|
}
|
|
244
242
|
catch (err) {
|
|
245
243
|
this.logEvent({ message: 'error getting fitness machine features', device: this.getName(), interface: this.getInterface(), error: err });
|
|
@@ -252,10 +250,6 @@ class BleFmAdapter extends adapter_js_1.default {
|
|
|
252
250
|
if (before !== after) {
|
|
253
251
|
this.logEvent({ message: 'device capabilities updated', name: this.getSettings().name, interface: this.getSettings().interface, capabilities: this.capabilities });
|
|
254
252
|
this.emit('device-info', this.getSettings(), { capabilities: this.capabilities });
|
|
255
|
-
updateRequired = true;
|
|
256
|
-
}
|
|
257
|
-
if (updateRequired) {
|
|
258
|
-
this.updateCyclingModeConfig();
|
|
259
253
|
}
|
|
260
254
|
}
|
|
261
255
|
updateCapabilitiesFromFeatures(features) {
|
|
@@ -11,6 +11,7 @@ const index_js_1 = require("../../../types/index.js");
|
|
|
11
11
|
class ZwiftPlayAdapter extends adapter_js_1.default {
|
|
12
12
|
static INCYCLIST_PROFILE_NAME = 'Controller';
|
|
13
13
|
static CAPABILITIES = [index_js_1.IncyclistCapability.AppControl];
|
|
14
|
+
keyPressedHandler;
|
|
14
15
|
constructor(settings, props) {
|
|
15
16
|
super(settings, props);
|
|
16
17
|
this.logger = new gd_eventlog_1.EventLogger('ZwiftPlay');
|
|
@@ -25,9 +26,13 @@ class ZwiftPlayAdapter extends adapter_js_1.default {
|
|
|
25
26
|
let connected = await super.startSensor();
|
|
26
27
|
if (connected) {
|
|
27
28
|
const sensor = this.getSensor();
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
29
|
+
if (this.keyPressedHandler) {
|
|
30
|
+
sensor.off('key-pressed', this.keyPressedHandler);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
this.keyPressedHandler = this.onKeyPressed.bind(this);
|
|
34
|
+
}
|
|
35
|
+
sensor.on('key-pressed', this.keyPressedHandler);
|
|
31
36
|
}
|
|
32
37
|
return connected;
|
|
33
38
|
}
|
|
@@ -36,10 +41,18 @@ class ZwiftPlayAdapter extends adapter_js_1.default {
|
|
|
36
41
|
return false;
|
|
37
42
|
}
|
|
38
43
|
}
|
|
44
|
+
onKeyPressed(event) {
|
|
45
|
+
this.emit('key-pressed', this.getSettings(), event);
|
|
46
|
+
}
|
|
47
|
+
isEqual(settings) {
|
|
48
|
+
const equal = super.isEqual(settings) &&
|
|
49
|
+
settings.address == this.settings.address;
|
|
50
|
+
return equal;
|
|
51
|
+
}
|
|
39
52
|
isSame(adapter) {
|
|
40
53
|
if (!(adapter instanceof ZwiftPlayAdapter))
|
|
41
54
|
return false;
|
|
42
|
-
return this.isEqual(adapter.settings);
|
|
55
|
+
return this.isEqual(adapter.settings) && this.getUniqueName() === adapter.getUniqueName();
|
|
43
56
|
}
|
|
44
57
|
updateSensor(peripheral) {
|
|
45
58
|
this.device = new sensor_js_1.BleZwiftPlaySensor(peripheral, { logger: this.logger });
|
|
@@ -47,6 +60,22 @@ class ZwiftPlayAdapter extends adapter_js_1.default {
|
|
|
47
60
|
getProfile() {
|
|
48
61
|
return ZwiftPlayAdapter.INCYCLIST_PROFILE_NAME;
|
|
49
62
|
}
|
|
63
|
+
getUniqueName() {
|
|
64
|
+
return this.getName();
|
|
65
|
+
}
|
|
66
|
+
getName() {
|
|
67
|
+
const settings = this.settings;
|
|
68
|
+
let name = settings.name;
|
|
69
|
+
if (settings.name === 'Zwift-Ride') {
|
|
70
|
+
if (this.device.getDeviceType() === 'ride-left')
|
|
71
|
+
name = name + '-L';
|
|
72
|
+
if (this.device.getDeviceType() === 'ride-right')
|
|
73
|
+
name = name + '-R';
|
|
74
|
+
}
|
|
75
|
+
const id = (settings.id ?? settings.address ?? '').replace(/[:\-]/g, '');
|
|
76
|
+
const addressHash = id.length > 4 ? id.slice(-4).toUpperCase() : id.toUpperCase();
|
|
77
|
+
return `${name} ${addressHash}`;
|
|
78
|
+
}
|
|
50
79
|
getDisplayName() {
|
|
51
80
|
return this.getName();
|
|
52
81
|
}
|
|
@@ -67,6 +67,42 @@ class BleZwiftPlaySensor extends sensor_js_1.TBleSensor {
|
|
|
67
67
|
getRequiredCharacteristics() {
|
|
68
68
|
return ['00000002-19ca-4651-86e5-fa29dcdd09d1', '00000004-19ca-4651-86e5-fa29dcdd09d1'];
|
|
69
69
|
}
|
|
70
|
+
getDeviceType() {
|
|
71
|
+
if (this.deviceType)
|
|
72
|
+
return this.deviceType;
|
|
73
|
+
if (this.isFM) {
|
|
74
|
+
this.deviceType = 'hub';
|
|
75
|
+
}
|
|
76
|
+
else if (this.peripheral?.getManufacturerData) {
|
|
77
|
+
const manufacturerData = this.getManufacturerData();
|
|
78
|
+
if (manufacturerData?.startsWith('4a09')) {
|
|
79
|
+
const typeVal = Number('0x' + manufacturerData.substring(2, 4));
|
|
80
|
+
if (typeVal === 9) {
|
|
81
|
+
this.deviceType = 'click';
|
|
82
|
+
this.encrypted = false;
|
|
83
|
+
}
|
|
84
|
+
else if (typeVal === 2) {
|
|
85
|
+
this.deviceType = 'right';
|
|
86
|
+
}
|
|
87
|
+
else if (typeVal === 3) {
|
|
88
|
+
this.deviceType = 'left';
|
|
89
|
+
}
|
|
90
|
+
else if (typeVal === 7) {
|
|
91
|
+
this.deviceType = 'ride-right';
|
|
92
|
+
}
|
|
93
|
+
else if (typeVal === 8) {
|
|
94
|
+
this.deviceType = 'ride-left';
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
console.log('# [ZwiftPlay] device type ', this.deviceType, this.peripheral?.getInfo().name, this.peripheral?.getInfo().address);
|
|
99
|
+
if (!this.deviceType && !this.encryptedSupported()) {
|
|
100
|
+
this.deviceType = 'click';
|
|
101
|
+
this.encrypted = false;
|
|
102
|
+
}
|
|
103
|
+
this.deviceType = this.deviceType ?? 'click';
|
|
104
|
+
return this.deviceType;
|
|
105
|
+
}
|
|
70
106
|
onData(characteristic, data, isNotify) {
|
|
71
107
|
const uuid = (0, utils_js_1.beautifyUUID)(characteristic).toLowerCase();
|
|
72
108
|
if (uuid === '00000002-19ca-4651-86e5-fa29dcdd09d1') {
|
|
@@ -154,7 +190,6 @@ class BleZwiftPlaySensor extends sensor_js_1.TBleSensor {
|
|
|
154
190
|
}
|
|
155
191
|
onMeasurement(d) {
|
|
156
192
|
const data = Buffer.from(d);
|
|
157
|
-
this.logEvent({ message: 'got hub notification', raw: data.toString('hex') });
|
|
158
193
|
if (data?.length < 1) {
|
|
159
194
|
console.log('Invalid click measurement data', data.toString('hex'));
|
|
160
195
|
return false;
|
|
@@ -182,9 +217,10 @@ class BleZwiftPlaySensor extends sensor_js_1.TBleSensor {
|
|
|
182
217
|
else if (type === 0x3c) {
|
|
183
218
|
this.onDeviceInformation(message);
|
|
184
219
|
}
|
|
220
|
+
else if (type === 0x15) {
|
|
221
|
+
}
|
|
185
222
|
else {
|
|
186
|
-
|
|
187
|
-
this.emit('data', { raw: data.toString('hex') });
|
|
223
|
+
this.logEvent({ message: 'got hub notification', raw: data.toString('hex') });
|
|
188
224
|
}
|
|
189
225
|
return true;
|
|
190
226
|
}
|
|
@@ -293,17 +329,27 @@ class BleZwiftPlaySensor extends sensor_js_1.TBleSensor {
|
|
|
293
329
|
try {
|
|
294
330
|
const data = zwift_hub_js_1.RideKeyPadStatus.fromBinary(m);
|
|
295
331
|
const buttonNames = new Map([
|
|
332
|
+
[0x01, 'left'],
|
|
333
|
+
[0x02, 'up'],
|
|
334
|
+
[0x04, 'right'],
|
|
335
|
+
[0x08, 'down'],
|
|
296
336
|
[0x10, 'a'],
|
|
297
337
|
[0x20, 'b'],
|
|
298
338
|
[0x40, 'y'],
|
|
299
339
|
[0x80, 'z'],
|
|
340
|
+
[0x0100, 'l-shift-up'],
|
|
341
|
+
[0x0200, 'l-shift-down'],
|
|
300
342
|
[0x1000, 'r-shift-up'],
|
|
301
343
|
[0x2000, 'r-shift-down'],
|
|
302
|
-
[
|
|
303
|
-
[
|
|
344
|
+
[0x0400, 'l-power-up'],
|
|
345
|
+
[0x4000, 'r-power-up'],
|
|
346
|
+
[0x0800, 'l-power'],
|
|
347
|
+
[0x8000, 'r-power'],
|
|
304
348
|
]);
|
|
305
349
|
const buttonMap = data.buttonMap ?? 0;
|
|
306
350
|
const currentPresses = new Set();
|
|
351
|
+
const address = this.peripheral?.getInfo()?.address;
|
|
352
|
+
const name = this.peripheral?.getInfo()?.name;
|
|
307
353
|
buttonNames.forEach((name, bit) => {
|
|
308
354
|
const isPressed = (buttonMap & bit) === 0;
|
|
309
355
|
if (isPressed) {
|
|
@@ -318,19 +364,11 @@ class BleZwiftPlaySensor extends sensor_js_1.TBleSensor {
|
|
|
318
364
|
if (!currentPresses.has(bit) && state.pressed) {
|
|
319
365
|
const keyName = buttonNames.get(bit);
|
|
320
366
|
const duration = Date.now() - state.timestamp;
|
|
367
|
+
this.logEvent({ message: 'key pressed', key: keyName, name, address, duration, deviceType: this.deviceType });
|
|
321
368
|
this.emit('key-pressed', { key: keyName, duration, deviceType: this.deviceType });
|
|
322
369
|
this.rideKeyPadStates.set(bit, { pressed: false, timestamp: Date.now() });
|
|
323
370
|
}
|
|
324
371
|
});
|
|
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
372
|
}
|
|
335
373
|
catch (err) {
|
|
336
374
|
this.logEvent({ message: 'Error', fn: 'onRideKeyPadStatus', error: err.message, stack: err.stack });
|
|
@@ -363,6 +401,8 @@ class BleZwiftPlaySensor extends sensor_js_1.TBleSensor {
|
|
|
363
401
|
}
|
|
364
402
|
onClickButtonMessage(d) {
|
|
365
403
|
try {
|
|
404
|
+
const address = this.peripheral?.getInfo()?.address;
|
|
405
|
+
const name = this.peripheral?.getInfo()?.name;
|
|
366
406
|
const message = Buffer.from(d);
|
|
367
407
|
const messageStr = message.toString('hex');
|
|
368
408
|
if (messageStr === this.prevClickMessage) {
|
|
@@ -373,6 +413,7 @@ class BleZwiftPlaySensor extends sensor_js_1.TBleSensor {
|
|
|
373
413
|
const prev = { ...this.upState };
|
|
374
414
|
this.upState = { pressed: false, timestamp: Date.now() };
|
|
375
415
|
if (prev.pressed) {
|
|
416
|
+
this.logEvent({ message: 'key pressed', key: 'up', name, address, duration: this.upState.timestamp - prev.timestamp, deviceType: this.deviceType });
|
|
376
417
|
this.emit('key-pressed', { key: 'up', duration: this.upState.timestamp - prev.timestamp, deviceType: this.deviceType });
|
|
377
418
|
}
|
|
378
419
|
}
|
|
@@ -383,6 +424,7 @@ class BleZwiftPlaySensor extends sensor_js_1.TBleSensor {
|
|
|
383
424
|
const prev = { ...this.downState };
|
|
384
425
|
this.downState = { pressed: false, timestamp: Date.now() };
|
|
385
426
|
if (prev.pressed) {
|
|
427
|
+
this.logEvent({ message: 'key pressed', key: 'down', name, address, duration: this.downState.timestamp - prev.timestamp, deviceType: this.deviceType });
|
|
386
428
|
this.emit('key-pressed', { key: 'down', duration: this.downState.timestamp - prev.timestamp, deviceType: this.deviceType });
|
|
387
429
|
}
|
|
388
430
|
}
|
|
@@ -481,7 +523,7 @@ class BleZwiftPlaySensor extends sensor_js_1.TBleSensor {
|
|
|
481
523
|
this.deviceType = 'hub';
|
|
482
524
|
this.encrypted = false;
|
|
483
525
|
}
|
|
484
|
-
else if (this.peripheral
|
|
526
|
+
else if (this.peripheral?.getManufacturerData) {
|
|
485
527
|
manufacturerData = this.getManufacturerData();
|
|
486
528
|
if (manufacturerData?.startsWith('4a09')) {
|
|
487
529
|
const typeVal = Number('0x' + manufacturerData.substring(2, 4));
|
|
@@ -495,6 +537,12 @@ class BleZwiftPlaySensor extends sensor_js_1.TBleSensor {
|
|
|
495
537
|
else if (typeVal === 3) {
|
|
496
538
|
this.deviceType = 'left';
|
|
497
539
|
}
|
|
540
|
+
else if (typeVal === 7) {
|
|
541
|
+
this.deviceType = 'ride-right';
|
|
542
|
+
}
|
|
543
|
+
else if (typeVal === 8) {
|
|
544
|
+
this.deviceType = 'ride-left';
|
|
545
|
+
}
|
|
498
546
|
}
|
|
499
547
|
}
|
|
500
548
|
if (!this.deviceType && !this.encryptedSupported()) {
|
|
@@ -502,7 +550,7 @@ class BleZwiftPlaySensor extends sensor_js_1.TBleSensor {
|
|
|
502
550
|
this.encrypted = false;
|
|
503
551
|
}
|
|
504
552
|
this.deviceType = this.deviceType ?? 'click';
|
|
505
|
-
this.logEvent({ message: 'Play protocol pairing info', deviceType: this.deviceType, encrypted: this.encrypted, manufacturerData });
|
|
553
|
+
this.logEvent({ message: 'Play protocol pairing info', deviceType: this.deviceType, encrypted: this.encrypted, manufacturerData: this.getManufacturerData() });
|
|
506
554
|
let message;
|
|
507
555
|
if (this.isFM) {
|
|
508
556
|
message = Buffer.concat([Buffer.from('RideOn'), Buffer.from([0x02, 0x01])]);
|
|
@@ -519,7 +567,7 @@ class BleZwiftPlaySensor extends sensor_js_1.TBleSensor {
|
|
|
519
567
|
this.logEvent({ message: `send rideOn` });
|
|
520
568
|
await this.write((0, utils_js_1.fullUUID)('00000003-19ca-4651-86e5-fa29dcdd09d1'), message, { withoutResponse: true });
|
|
521
569
|
this.isHubServicePaired = true;
|
|
522
|
-
this.logEvent({ message: 'pairing done', deviceType: this.deviceType, encrypted: this.encrypted, manufacturerData });
|
|
570
|
+
this.logEvent({ message: 'pairing done', deviceType: this.deviceType, encrypted: this.encrypted, manufacturerData: this.getManufacturerData() });
|
|
523
571
|
return true;
|
|
524
572
|
}
|
|
525
573
|
catch (err) {
|
|
@@ -530,6 +578,15 @@ class BleZwiftPlaySensor extends sensor_js_1.TBleSensor {
|
|
|
530
578
|
}
|
|
531
579
|
reset() {
|
|
532
580
|
}
|
|
581
|
+
getManufacturerData() {
|
|
582
|
+
const data = this.peripheral?.getManufacturerData?.();
|
|
583
|
+
if (typeof data === 'string')
|
|
584
|
+
return data;
|
|
585
|
+
if (Buffer.isBuffer(data)) {
|
|
586
|
+
return data.toString('hex');
|
|
587
|
+
}
|
|
588
|
+
return undefined;
|
|
589
|
+
}
|
|
533
590
|
getCrypto() {
|
|
534
591
|
let crypto = index_js_1.BindingsFactory.getInstance()?.getBinding()?.crypto;
|
|
535
592
|
if (!crypto)
|
|
@@ -559,14 +616,5 @@ class BleZwiftPlaySensor extends sensor_js_1.TBleSensor {
|
|
|
559
616
|
delete this.prevHubSettings;
|
|
560
617
|
this.rideKeyPadStates = new Map();
|
|
561
618
|
}
|
|
562
|
-
getManufacturerData() {
|
|
563
|
-
const data = this.peripheral.getManufacturerData();
|
|
564
|
-
if (typeof data === 'string')
|
|
565
|
-
return data;
|
|
566
|
-
if (Buffer.isBuffer(data)) {
|
|
567
|
-
return data.toString('hex');
|
|
568
|
-
}
|
|
569
|
-
return undefined;
|
|
570
|
-
}
|
|
571
619
|
}
|
|
572
620
|
exports.BleZwiftPlaySensor = BleZwiftPlaySensor;
|
|
@@ -99,34 +99,40 @@ class SmartTrainerCyclingMode extends power_base_js_1.default {
|
|
|
99
99
|
if (startGearIdx !== -1) {
|
|
100
100
|
config.properties.splice(startGearIdx, 1);
|
|
101
101
|
}
|
|
102
|
+
try {
|
|
103
|
+
const device = this.adapter?.getName();
|
|
104
|
+
this.logger.logEvent({ message: 'reset config', device });
|
|
105
|
+
}
|
|
106
|
+
catch { }
|
|
102
107
|
}
|
|
103
108
|
getConfig() {
|
|
104
109
|
const config = super.getConfig();
|
|
105
|
-
const virtShiftEnabled = this.getFeatureToogle().has('VirtualShifting');
|
|
106
110
|
let virtshift = config.properties.find(p => p.key === 'virtshift');
|
|
107
111
|
let startGear = config.properties.find(p => p.key === 'startGear');
|
|
108
|
-
if (!virtshift
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
[
|
|
112
|
+
if (!virtshift) {
|
|
113
|
+
const device = this.adapter?.getName();
|
|
114
|
+
const supportsZwift = this.adapter?.supportsVirtualShifting();
|
|
115
|
+
this.logger.logEvent({ message: 'prepare gear settings config', device, supportsZwift });
|
|
116
|
+
if (supportsZwift) {
|
|
117
|
+
const options = [
|
|
115
118
|
'Disabled',
|
|
116
|
-
{ key: '
|
|
119
|
+
{ key: 'Incyclist', display: 'App only (beta)' },
|
|
120
|
+
{ key: 'Mixed', display: 'App + Bike' },
|
|
121
|
+
{ key: 'SmartTrainer', display: 'SmartTreiner (beta)' }
|
|
117
122
|
];
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
123
|
+
virtshift = { key: 'virtshift', name: 'Virtual Shifting', description: 'Enable virtual shifting', type: types_js_1.CyclingModeProperyType.SingleSelect, options, default: 'Mixed' };
|
|
124
|
+
config.properties.push(virtshift);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
const options = [
|
|
128
|
+
'Disabled',
|
|
129
|
+
{ key: 'Incyclist', display: 'App only (beta)' },
|
|
130
|
+
{ key: 'Mixed', display: 'App + Bike' }
|
|
131
|
+
];
|
|
132
|
+
virtshift = { key: 'virtshift', name: 'Virtual Shifting', description: 'Enable virtual shifting', type: types_js_1.CyclingModeProperyType.SingleSelect, options, default: 'Disabled' };
|
|
133
|
+
config.properties.push(virtshift);
|
|
134
|
+
}
|
|
135
|
+
this.logger.logEvent({ message: 'gear settings config', config: config.properties });
|
|
130
136
|
}
|
|
131
137
|
if (virtshift && !startGear) {
|
|
132
138
|
startGear = { key: 'startGear', name: 'Initial Gear', description: 'Initial Gear', type: types_js_1.CyclingModeProperyType.Integer, default: 12, min: 1, max: 24, condition: (s) => s?.virtshift === 'Incyclist' || s?.virtshift === 'SmartTrainer' };
|
|
@@ -24,7 +24,8 @@ export default class BleAdapter extends IncyclistDevice {
|
|
|
24
24
|
if (settings.name?.match(/\d/g) || settings.address === undefined)
|
|
25
25
|
return this.getName();
|
|
26
26
|
else {
|
|
27
|
-
const
|
|
27
|
+
const id = (settings.id ?? settings.address ?? '').replace(/[:\-]/g, '');
|
|
28
|
+
const addressHash = id.length > 4 ? id.slice(-4).toUpperCase() : id.toUpperCase();
|
|
28
29
|
return `${this.getName()} ${addressHash}`;
|
|
29
30
|
}
|
|
30
31
|
}
|
|
@@ -264,7 +264,7 @@ export class BleInterface extends EventEmitter {
|
|
|
264
264
|
return new BlePeripheral(announcement);
|
|
265
265
|
}
|
|
266
266
|
createPeripheralFromSettings(settings) {
|
|
267
|
-
const info = this.getAll().find(a => a.service?.
|
|
267
|
+
const info = this.getAll().find(a => a.service?.peripheral?.address === settings.address);
|
|
268
268
|
if (!info?.service)
|
|
269
269
|
return null;
|
|
270
270
|
return this.createPeripheral(info.service);
|
|
@@ -278,7 +278,7 @@ export class BleInterface extends EventEmitter {
|
|
|
278
278
|
if (!wasDiscovering)
|
|
279
279
|
this.startPeripheralScan();
|
|
280
280
|
const onDevice = (device) => {
|
|
281
|
-
if (device.
|
|
281
|
+
if (device.address === settings.address) {
|
|
282
282
|
const peripheral = this.createPeripheralFromSettings(device);
|
|
283
283
|
if (peripheral) {
|
|
284
284
|
this.off('device', onDevice);
|
|
@@ -585,7 +585,7 @@ export class BleInterface extends EventEmitter {
|
|
|
585
585
|
return supported.length > 0;
|
|
586
586
|
}
|
|
587
587
|
find(service) {
|
|
588
|
-
return this.services.find(a => a.service.name === service.name && a.ts > Date.now() - BLE_EXPIRATION_TIMEOUT);
|
|
588
|
+
return this.services.find(a => a.service.name === service.name && a.service?.peripheral?.address && a.ts > Date.now() - BLE_EXPIRATION_TIMEOUT);
|
|
589
589
|
}
|
|
590
590
|
getAll() {
|
|
591
591
|
return this.services.filter(a => a.ts > Date.now() - BLE_EXPIRATION_TIMEOUT);
|
|
@@ -230,11 +230,9 @@ 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;
|
|
234
233
|
if (!sensor.features) {
|
|
235
234
|
try {
|
|
236
235
|
await sensor.getFitnessMachineFeatures();
|
|
237
|
-
updateRequired = true;
|
|
238
236
|
}
|
|
239
237
|
catch (err) {
|
|
240
238
|
this.logEvent({ message: 'error getting fitness machine features', device: this.getName(), interface: this.getInterface(), error: err });
|
|
@@ -247,10 +245,6 @@ export default class BleFmAdapter extends BleAdapter {
|
|
|
247
245
|
if (before !== after) {
|
|
248
246
|
this.logEvent({ message: 'device capabilities updated', name: this.getSettings().name, interface: this.getSettings().interface, capabilities: this.capabilities });
|
|
249
247
|
this.emit('device-info', this.getSettings(), { capabilities: this.capabilities });
|
|
250
|
-
updateRequired = true;
|
|
251
|
-
}
|
|
252
|
-
if (updateRequired) {
|
|
253
|
-
this.updateCyclingModeConfig();
|
|
254
248
|
}
|
|
255
249
|
}
|
|
256
250
|
updateCapabilitiesFromFeatures(features) {
|
|
@@ -5,6 +5,7 @@ import { IncyclistCapability } from '../../../types/index.js';
|
|
|
5
5
|
export class ZwiftPlayAdapter extends BleAdapter {
|
|
6
6
|
static INCYCLIST_PROFILE_NAME = 'Controller';
|
|
7
7
|
static CAPABILITIES = [IncyclistCapability.AppControl];
|
|
8
|
+
keyPressedHandler;
|
|
8
9
|
constructor(settings, props) {
|
|
9
10
|
super(settings, props);
|
|
10
11
|
this.logger = new EventLogger('ZwiftPlay');
|
|
@@ -19,9 +20,13 @@ export class ZwiftPlayAdapter extends BleAdapter {
|
|
|
19
20
|
let connected = await super.startSensor();
|
|
20
21
|
if (connected) {
|
|
21
22
|
const sensor = this.getSensor();
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
23
|
+
if (this.keyPressedHandler) {
|
|
24
|
+
sensor.off('key-pressed', this.keyPressedHandler);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
this.keyPressedHandler = this.onKeyPressed.bind(this);
|
|
28
|
+
}
|
|
29
|
+
sensor.on('key-pressed', this.keyPressedHandler);
|
|
25
30
|
}
|
|
26
31
|
return connected;
|
|
27
32
|
}
|
|
@@ -30,10 +35,18 @@ export class ZwiftPlayAdapter extends BleAdapter {
|
|
|
30
35
|
return false;
|
|
31
36
|
}
|
|
32
37
|
}
|
|
38
|
+
onKeyPressed(event) {
|
|
39
|
+
this.emit('key-pressed', this.getSettings(), event);
|
|
40
|
+
}
|
|
41
|
+
isEqual(settings) {
|
|
42
|
+
const equal = super.isEqual(settings) &&
|
|
43
|
+
settings.address == this.settings.address;
|
|
44
|
+
return equal;
|
|
45
|
+
}
|
|
33
46
|
isSame(adapter) {
|
|
34
47
|
if (!(adapter instanceof ZwiftPlayAdapter))
|
|
35
48
|
return false;
|
|
36
|
-
return this.isEqual(adapter.settings);
|
|
49
|
+
return this.isEqual(adapter.settings) && this.getUniqueName() === adapter.getUniqueName();
|
|
37
50
|
}
|
|
38
51
|
updateSensor(peripheral) {
|
|
39
52
|
this.device = new BleZwiftPlaySensor(peripheral, { logger: this.logger });
|
|
@@ -41,6 +54,22 @@ export class ZwiftPlayAdapter extends BleAdapter {
|
|
|
41
54
|
getProfile() {
|
|
42
55
|
return ZwiftPlayAdapter.INCYCLIST_PROFILE_NAME;
|
|
43
56
|
}
|
|
57
|
+
getUniqueName() {
|
|
58
|
+
return this.getName();
|
|
59
|
+
}
|
|
60
|
+
getName() {
|
|
61
|
+
const settings = this.settings;
|
|
62
|
+
let name = settings.name;
|
|
63
|
+
if (settings.name === 'Zwift-Ride') {
|
|
64
|
+
if (this.device.getDeviceType() === 'ride-left')
|
|
65
|
+
name = name + '-L';
|
|
66
|
+
if (this.device.getDeviceType() === 'ride-right')
|
|
67
|
+
name = name + '-R';
|
|
68
|
+
}
|
|
69
|
+
const id = (settings.id ?? settings.address ?? '').replace(/[:\-]/g, '');
|
|
70
|
+
const addressHash = id.length > 4 ? id.slice(-4).toUpperCase() : id.toUpperCase();
|
|
71
|
+
return `${name} ${addressHash}`;
|
|
72
|
+
}
|
|
44
73
|
getDisplayName() {
|
|
45
74
|
return this.getName();
|
|
46
75
|
}
|
|
@@ -64,6 +64,42 @@ export class BleZwiftPlaySensor extends TBleSensor {
|
|
|
64
64
|
getRequiredCharacteristics() {
|
|
65
65
|
return ['00000002-19ca-4651-86e5-fa29dcdd09d1', '00000004-19ca-4651-86e5-fa29dcdd09d1'];
|
|
66
66
|
}
|
|
67
|
+
getDeviceType() {
|
|
68
|
+
if (this.deviceType)
|
|
69
|
+
return this.deviceType;
|
|
70
|
+
if (this.isFM) {
|
|
71
|
+
this.deviceType = 'hub';
|
|
72
|
+
}
|
|
73
|
+
else if (this.peripheral?.getManufacturerData) {
|
|
74
|
+
const manufacturerData = this.getManufacturerData();
|
|
75
|
+
if (manufacturerData?.startsWith('4a09')) {
|
|
76
|
+
const typeVal = Number('0x' + manufacturerData.substring(2, 4));
|
|
77
|
+
if (typeVal === 9) {
|
|
78
|
+
this.deviceType = 'click';
|
|
79
|
+
this.encrypted = false;
|
|
80
|
+
}
|
|
81
|
+
else if (typeVal === 2) {
|
|
82
|
+
this.deviceType = 'right';
|
|
83
|
+
}
|
|
84
|
+
else if (typeVal === 3) {
|
|
85
|
+
this.deviceType = 'left';
|
|
86
|
+
}
|
|
87
|
+
else if (typeVal === 7) {
|
|
88
|
+
this.deviceType = 'ride-right';
|
|
89
|
+
}
|
|
90
|
+
else if (typeVal === 8) {
|
|
91
|
+
this.deviceType = 'ride-left';
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
console.log('# [ZwiftPlay] device type ', this.deviceType, this.peripheral?.getInfo().name, this.peripheral?.getInfo().address);
|
|
96
|
+
if (!this.deviceType && !this.encryptedSupported()) {
|
|
97
|
+
this.deviceType = 'click';
|
|
98
|
+
this.encrypted = false;
|
|
99
|
+
}
|
|
100
|
+
this.deviceType = this.deviceType ?? 'click';
|
|
101
|
+
return this.deviceType;
|
|
102
|
+
}
|
|
67
103
|
onData(characteristic, data, isNotify) {
|
|
68
104
|
const uuid = beautifyUUID(characteristic).toLowerCase();
|
|
69
105
|
if (uuid === '00000002-19ca-4651-86e5-fa29dcdd09d1') {
|
|
@@ -151,7 +187,6 @@ export class BleZwiftPlaySensor extends TBleSensor {
|
|
|
151
187
|
}
|
|
152
188
|
onMeasurement(d) {
|
|
153
189
|
const data = Buffer.from(d);
|
|
154
|
-
this.logEvent({ message: 'got hub notification', raw: data.toString('hex') });
|
|
155
190
|
if (data?.length < 1) {
|
|
156
191
|
console.log('Invalid click measurement data', data.toString('hex'));
|
|
157
192
|
return false;
|
|
@@ -179,9 +214,10 @@ export class BleZwiftPlaySensor extends TBleSensor {
|
|
|
179
214
|
else if (type === 0x3c) {
|
|
180
215
|
this.onDeviceInformation(message);
|
|
181
216
|
}
|
|
217
|
+
else if (type === 0x15) {
|
|
218
|
+
}
|
|
182
219
|
else {
|
|
183
|
-
|
|
184
|
-
this.emit('data', { raw: data.toString('hex') });
|
|
220
|
+
this.logEvent({ message: 'got hub notification', raw: data.toString('hex') });
|
|
185
221
|
}
|
|
186
222
|
return true;
|
|
187
223
|
}
|
|
@@ -290,17 +326,27 @@ export class BleZwiftPlaySensor extends TBleSensor {
|
|
|
290
326
|
try {
|
|
291
327
|
const data = RideKeyPadStatus.fromBinary(m);
|
|
292
328
|
const buttonNames = new Map([
|
|
329
|
+
[0x01, 'left'],
|
|
330
|
+
[0x02, 'up'],
|
|
331
|
+
[0x04, 'right'],
|
|
332
|
+
[0x08, 'down'],
|
|
293
333
|
[0x10, 'a'],
|
|
294
334
|
[0x20, 'b'],
|
|
295
335
|
[0x40, 'y'],
|
|
296
336
|
[0x80, 'z'],
|
|
337
|
+
[0x0100, 'l-shift-up'],
|
|
338
|
+
[0x0200, 'l-shift-down'],
|
|
297
339
|
[0x1000, 'r-shift-up'],
|
|
298
340
|
[0x2000, 'r-shift-down'],
|
|
299
|
-
[
|
|
300
|
-
[
|
|
341
|
+
[0x0400, 'l-power-up'],
|
|
342
|
+
[0x4000, 'r-power-up'],
|
|
343
|
+
[0x0800, 'l-power'],
|
|
344
|
+
[0x8000, 'r-power'],
|
|
301
345
|
]);
|
|
302
346
|
const buttonMap = data.buttonMap ?? 0;
|
|
303
347
|
const currentPresses = new Set();
|
|
348
|
+
const address = this.peripheral?.getInfo()?.address;
|
|
349
|
+
const name = this.peripheral?.getInfo()?.name;
|
|
304
350
|
buttonNames.forEach((name, bit) => {
|
|
305
351
|
const isPressed = (buttonMap & bit) === 0;
|
|
306
352
|
if (isPressed) {
|
|
@@ -315,19 +361,11 @@ export class BleZwiftPlaySensor extends TBleSensor {
|
|
|
315
361
|
if (!currentPresses.has(bit) && state.pressed) {
|
|
316
362
|
const keyName = buttonNames.get(bit);
|
|
317
363
|
const duration = Date.now() - state.timestamp;
|
|
364
|
+
this.logEvent({ message: 'key pressed', key: keyName, name, address, duration, deviceType: this.deviceType });
|
|
318
365
|
this.emit('key-pressed', { key: keyName, duration, deviceType: this.deviceType });
|
|
319
366
|
this.rideKeyPadStates.set(bit, { pressed: false, timestamp: Date.now() });
|
|
320
367
|
}
|
|
321
368
|
});
|
|
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
369
|
}
|
|
332
370
|
catch (err) {
|
|
333
371
|
this.logEvent({ message: 'Error', fn: 'onRideKeyPadStatus', error: err.message, stack: err.stack });
|
|
@@ -360,6 +398,8 @@ export class BleZwiftPlaySensor extends TBleSensor {
|
|
|
360
398
|
}
|
|
361
399
|
onClickButtonMessage(d) {
|
|
362
400
|
try {
|
|
401
|
+
const address = this.peripheral?.getInfo()?.address;
|
|
402
|
+
const name = this.peripheral?.getInfo()?.name;
|
|
363
403
|
const message = Buffer.from(d);
|
|
364
404
|
const messageStr = message.toString('hex');
|
|
365
405
|
if (messageStr === this.prevClickMessage) {
|
|
@@ -370,6 +410,7 @@ export class BleZwiftPlaySensor extends TBleSensor {
|
|
|
370
410
|
const prev = { ...this.upState };
|
|
371
411
|
this.upState = { pressed: false, timestamp: Date.now() };
|
|
372
412
|
if (prev.pressed) {
|
|
413
|
+
this.logEvent({ message: 'key pressed', key: 'up', name, address, duration: this.upState.timestamp - prev.timestamp, deviceType: this.deviceType });
|
|
373
414
|
this.emit('key-pressed', { key: 'up', duration: this.upState.timestamp - prev.timestamp, deviceType: this.deviceType });
|
|
374
415
|
}
|
|
375
416
|
}
|
|
@@ -380,6 +421,7 @@ export class BleZwiftPlaySensor extends TBleSensor {
|
|
|
380
421
|
const prev = { ...this.downState };
|
|
381
422
|
this.downState = { pressed: false, timestamp: Date.now() };
|
|
382
423
|
if (prev.pressed) {
|
|
424
|
+
this.logEvent({ message: 'key pressed', key: 'down', name, address, duration: this.downState.timestamp - prev.timestamp, deviceType: this.deviceType });
|
|
383
425
|
this.emit('key-pressed', { key: 'down', duration: this.downState.timestamp - prev.timestamp, deviceType: this.deviceType });
|
|
384
426
|
}
|
|
385
427
|
}
|
|
@@ -478,7 +520,7 @@ export class BleZwiftPlaySensor extends TBleSensor {
|
|
|
478
520
|
this.deviceType = 'hub';
|
|
479
521
|
this.encrypted = false;
|
|
480
522
|
}
|
|
481
|
-
else if (this.peripheral
|
|
523
|
+
else if (this.peripheral?.getManufacturerData) {
|
|
482
524
|
manufacturerData = this.getManufacturerData();
|
|
483
525
|
if (manufacturerData?.startsWith('4a09')) {
|
|
484
526
|
const typeVal = Number('0x' + manufacturerData.substring(2, 4));
|
|
@@ -492,6 +534,12 @@ export class BleZwiftPlaySensor extends TBleSensor {
|
|
|
492
534
|
else if (typeVal === 3) {
|
|
493
535
|
this.deviceType = 'left';
|
|
494
536
|
}
|
|
537
|
+
else if (typeVal === 7) {
|
|
538
|
+
this.deviceType = 'ride-right';
|
|
539
|
+
}
|
|
540
|
+
else if (typeVal === 8) {
|
|
541
|
+
this.deviceType = 'ride-left';
|
|
542
|
+
}
|
|
495
543
|
}
|
|
496
544
|
}
|
|
497
545
|
if (!this.deviceType && !this.encryptedSupported()) {
|
|
@@ -499,7 +547,7 @@ export class BleZwiftPlaySensor extends TBleSensor {
|
|
|
499
547
|
this.encrypted = false;
|
|
500
548
|
}
|
|
501
549
|
this.deviceType = this.deviceType ?? 'click';
|
|
502
|
-
this.logEvent({ message: 'Play protocol pairing info', deviceType: this.deviceType, encrypted: this.encrypted, manufacturerData });
|
|
550
|
+
this.logEvent({ message: 'Play protocol pairing info', deviceType: this.deviceType, encrypted: this.encrypted, manufacturerData: this.getManufacturerData() });
|
|
503
551
|
let message;
|
|
504
552
|
if (this.isFM) {
|
|
505
553
|
message = Buffer.concat([Buffer.from('RideOn'), Buffer.from([0x02, 0x01])]);
|
|
@@ -516,7 +564,7 @@ export class BleZwiftPlaySensor extends TBleSensor {
|
|
|
516
564
|
this.logEvent({ message: `send rideOn` });
|
|
517
565
|
await this.write(fullUUID('00000003-19ca-4651-86e5-fa29dcdd09d1'), message, { withoutResponse: true });
|
|
518
566
|
this.isHubServicePaired = true;
|
|
519
|
-
this.logEvent({ message: 'pairing done', deviceType: this.deviceType, encrypted: this.encrypted, manufacturerData });
|
|
567
|
+
this.logEvent({ message: 'pairing done', deviceType: this.deviceType, encrypted: this.encrypted, manufacturerData: this.getManufacturerData() });
|
|
520
568
|
return true;
|
|
521
569
|
}
|
|
522
570
|
catch (err) {
|
|
@@ -527,6 +575,15 @@ export class BleZwiftPlaySensor extends TBleSensor {
|
|
|
527
575
|
}
|
|
528
576
|
reset() {
|
|
529
577
|
}
|
|
578
|
+
getManufacturerData() {
|
|
579
|
+
const data = this.peripheral?.getManufacturerData?.();
|
|
580
|
+
if (typeof data === 'string')
|
|
581
|
+
return data;
|
|
582
|
+
if (Buffer.isBuffer(data)) {
|
|
583
|
+
return data.toString('hex');
|
|
584
|
+
}
|
|
585
|
+
return undefined;
|
|
586
|
+
}
|
|
530
587
|
getCrypto() {
|
|
531
588
|
let crypto = BindingsFactory.getInstance()?.getBinding()?.crypto;
|
|
532
589
|
if (!crypto)
|
|
@@ -556,13 +613,4 @@ export class BleZwiftPlaySensor extends TBleSensor {
|
|
|
556
613
|
delete this.prevHubSettings;
|
|
557
614
|
this.rideKeyPadStates = new Map();
|
|
558
615
|
}
|
|
559
|
-
getManufacturerData() {
|
|
560
|
-
const data = this.peripheral.getManufacturerData();
|
|
561
|
-
if (typeof data === 'string')
|
|
562
|
-
return data;
|
|
563
|
-
if (Buffer.isBuffer(data)) {
|
|
564
|
-
return data.toString('hex');
|
|
565
|
-
}
|
|
566
|
-
return undefined;
|
|
567
|
-
}
|
|
568
616
|
}
|
|
@@ -61,34 +61,40 @@ export default class SmartTrainerCyclingMode extends PowerBasedCyclingModeBase {
|
|
|
61
61
|
if (startGearIdx !== -1) {
|
|
62
62
|
config.properties.splice(startGearIdx, 1);
|
|
63
63
|
}
|
|
64
|
+
try {
|
|
65
|
+
const device = this.adapter?.getName();
|
|
66
|
+
this.logger.logEvent({ message: 'reset config', device });
|
|
67
|
+
}
|
|
68
|
+
catch { }
|
|
64
69
|
}
|
|
65
70
|
getConfig() {
|
|
66
71
|
const config = super.getConfig();
|
|
67
|
-
const virtShiftEnabled = this.getFeatureToogle().has('VirtualShifting');
|
|
68
72
|
let virtshift = config.properties.find(p => p.key === 'virtshift');
|
|
69
73
|
let startGear = config.properties.find(p => p.key === 'startGear');
|
|
70
|
-
if (!virtshift
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
[
|
|
74
|
+
if (!virtshift) {
|
|
75
|
+
const device = this.adapter?.getName();
|
|
76
|
+
const supportsZwift = this.adapter?.supportsVirtualShifting();
|
|
77
|
+
this.logger.logEvent({ message: 'prepare gear settings config', device, supportsZwift });
|
|
78
|
+
if (supportsZwift) {
|
|
79
|
+
const options = [
|
|
77
80
|
'Disabled',
|
|
78
|
-
{ key: '
|
|
81
|
+
{ key: 'Incyclist', display: 'App only (beta)' },
|
|
82
|
+
{ key: 'Mixed', display: 'App + Bike' },
|
|
83
|
+
{ key: 'SmartTrainer', display: 'SmartTreiner (beta)' }
|
|
79
84
|
];
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
85
|
+
virtshift = { key: 'virtshift', name: 'Virtual Shifting', description: 'Enable virtual shifting', type: CyclingModeProperyType.SingleSelect, options, default: 'Mixed' };
|
|
86
|
+
config.properties.push(virtshift);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
const options = [
|
|
90
|
+
'Disabled',
|
|
91
|
+
{ key: 'Incyclist', display: 'App only (beta)' },
|
|
92
|
+
{ key: 'Mixed', display: 'App + Bike' }
|
|
93
|
+
];
|
|
94
|
+
virtshift = { key: 'virtshift', name: 'Virtual Shifting', description: 'Enable virtual shifting', type: CyclingModeProperyType.SingleSelect, options, default: 'Disabled' };
|
|
95
|
+
config.properties.push(virtshift);
|
|
96
|
+
}
|
|
97
|
+
this.logger.logEvent({ message: 'gear settings config', config: config.properties });
|
|
92
98
|
}
|
|
93
99
|
if (virtshift && !startGear) {
|
|
94
100
|
startGear = { key: 'startGear', name: 'Initial Gear', description: 'Initial Gear', type: CyclingModeProperyType.Integer, default: 12, min: 1, max: 24, condition: (s) => s?.virtshift === 'Incyclist' || s?.virtshift === 'SmartTrainer' };
|
|
@@ -7,12 +7,17 @@ import { BleDeviceSettings, IBlePeripheral } from '../../types.js';
|
|
|
7
7
|
export declare class ZwiftPlayAdapter extends BleAdapter<BleDeviceData, BleZwiftPlaySensor> {
|
|
8
8
|
protected static INCYCLIST_PROFILE_NAME: LegacyProfile;
|
|
9
9
|
protected static CAPABILITIES: IncyclistCapability[];
|
|
10
|
+
protected keyPressedHandler?: (event: any) => void;
|
|
10
11
|
constructor(settings: BleDeviceSettings, props?: DeviceProperties);
|
|
11
12
|
protected checkCapabilities(): Promise<void>;
|
|
12
13
|
startSensor(): Promise<boolean>;
|
|
14
|
+
protected onKeyPressed(event: any): void;
|
|
15
|
+
isEqual(settings: BleDeviceSettings): boolean;
|
|
13
16
|
isSame(adapter: IAdapter): boolean;
|
|
14
17
|
updateSensor(peripheral: IBlePeripheral): void;
|
|
15
18
|
getProfile(): LegacyProfile;
|
|
19
|
+
getUniqueName(): string;
|
|
20
|
+
getName(): string;
|
|
16
21
|
getDisplayName(): string;
|
|
17
22
|
mapData(deviceData: BleDeviceData): IncyclistAdapterData;
|
|
18
23
|
}
|
|
@@ -13,7 +13,7 @@ type BleZwiftPlaySensorProps = {
|
|
|
13
13
|
logger?: EventLogger;
|
|
14
14
|
isTrainer?: boolean;
|
|
15
15
|
};
|
|
16
|
-
type DeviceType = 'left' | 'right' | 'click' | 'hub';
|
|
16
|
+
type DeviceType = 'left' | 'right' | 'click' | 'hub' | 'ride-left' | 'ride-right';
|
|
17
17
|
export declare class BleZwiftPlaySensor extends TBleSensor {
|
|
18
18
|
static readonly profile: LegacyProfile;
|
|
19
19
|
static readonly protocol: BleProtocol;
|
|
@@ -45,6 +45,7 @@ export declare class BleZwiftPlaySensor extends TBleSensor {
|
|
|
45
45
|
reconnectSensor(): Promise<boolean>;
|
|
46
46
|
stopSensor(): Promise<boolean>;
|
|
47
47
|
protected getRequiredCharacteristics(): Array<string>;
|
|
48
|
+
getDeviceType(): DeviceType;
|
|
48
49
|
onData(characteristic: string, data: Buffer, isNotify?: boolean): boolean;
|
|
49
50
|
requestDataUpdate(dataId: number): Promise<void>;
|
|
50
51
|
setSimulationData(data?: SimulationParam): Promise<void>;
|
|
@@ -65,10 +66,10 @@ export declare class BleZwiftPlaySensor extends TBleSensor {
|
|
|
65
66
|
read(characteristic: string, ignoreErrors?: boolean): Promise<Buffer | null>;
|
|
66
67
|
pair(): Promise<boolean>;
|
|
67
68
|
reset(): void;
|
|
69
|
+
getManufacturerData(): string;
|
|
68
70
|
protected getCrypto(): ICryptoBinding;
|
|
69
71
|
protected encryptedSupported(): boolean;
|
|
70
72
|
protected createKeyPair(): any;
|
|
71
73
|
protected setInitialState(): void;
|
|
72
|
-
protected getManufacturerData(): string;
|
|
73
74
|
}
|
|
74
75
|
export {};
|