incyclist-devices 2.3.34 → 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.
- package/lib/ble/base/peripheral.d.ts +3 -0
- package/lib/ble/base/peripheral.js +20 -0
- package/lib/ble/base/sensor.d.ts +2 -0
- package/lib/ble/base/sensor.js +7 -0
- package/lib/ble/fm/adapter.d.ts +5 -0
- package/lib/ble/fm/adapter.js +38 -1
- package/lib/ble/fm/sensor.js +68 -62
- package/lib/ble/types.d.ts +2 -0
- package/lib/ble/zwift/play/helperfactory.d.ts +7 -0
- package/lib/ble/zwift/play/helperfactory.js +17 -0
- package/lib/ble/zwift/play/protohelper.d.ts +4 -0
- package/lib/ble/zwift/play/protohelper.js +11 -0
- package/lib/ble/zwift/play/sensor.d.ts +21 -6
- package/lib/ble/zwift/play/sensor.js +237 -61
- package/lib/ble/zwift/play/types.d.ts +6 -0
- package/lib/ble/zwift/play/types.js +2 -0
- package/lib/direct-connect/base/peripheral.d.ts +3 -0
- package/lib/direct-connect/base/peripheral.js +8 -0
- package/lib/modes/antble-smarttrainer.d.ts +0 -1
- package/lib/modes/antble-smarttrainer.js +72 -23
- package/lib/modes/types.d.ts +2 -0
- package/lib/proto/org/cagnulen/qdomyoszwift/zwift_hub_pb.d.ts +156 -0
- package/lib/proto/org/cagnulen/qdomyoszwift/zwift_hub_pb.js +59 -0
- package/lib/proto/zwift_hub.d.ts +282 -0
- package/lib/proto/zwift_hub.js +1146 -0
- package/package.json +7 -2
|
@@ -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
|
-
|
|
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.
|
|
57
|
+
this.onMeasurement(data);
|
|
47
58
|
}
|
|
48
59
|
else if (uuid === '00000004-19ca-4651-86e5-fa29dcdd09d1') {
|
|
49
|
-
this.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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.
|
|
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(
|
|
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
|
-
|
|
284
|
+
onResponse(d) {
|
|
124
285
|
const data = Buffer.from(d);
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
|
138
|
-
const
|
|
139
|
-
if (
|
|
140
|
-
this.encrypted =
|
|
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
|
-
|
|
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
|
});
|
|
@@ -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) {
|
|
@@ -50,7 +50,6 @@ export default class SmartTrainerCyclingMode extends PowerBasedCyclingModeBase i
|
|
|
50
50
|
updateData(bikeData: IncyclistBikeData, log?: boolean): IncyclistBikeData;
|
|
51
51
|
getData(): Partial<IncyclistBikeData>;
|
|
52
52
|
sendBikeUpdate(incoming: UpdateRequest): UpdateRequest;
|
|
53
|
-
protected setInitialGear(data: IncyclistBikeData): void;
|
|
54
53
|
protected getGearString(): string;
|
|
55
54
|
}
|
|
56
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', '
|
|
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', '
|
|
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
|
}
|
|
@@ -170,7 +182,8 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
170
182
|
case 'Simulated':
|
|
171
183
|
if (request.gearDelta !== undefined) {
|
|
172
184
|
if (this.gear === undefined) {
|
|
173
|
-
|
|
185
|
+
const initialGear = this.getSetting('startGear');
|
|
186
|
+
this.gear = initialGear + request.gearDelta;
|
|
174
187
|
}
|
|
175
188
|
else {
|
|
176
189
|
this.gear += request.gearDelta;
|
|
@@ -192,6 +205,47 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
192
205
|
}
|
|
193
206
|
break;
|
|
194
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;
|
|
195
249
|
case 'Disabled':
|
|
196
250
|
default:
|
|
197
251
|
break;
|
|
@@ -218,17 +272,22 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
218
272
|
if (virtshiftMode === 'Disabled') {
|
|
219
273
|
return 'Disabled';
|
|
220
274
|
}
|
|
221
|
-
if (
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
275
|
+
else if (virtshiftMode === 'Incyclist') {
|
|
276
|
+
return 'Simulated';
|
|
277
|
+
}
|
|
278
|
+
else if (virtshiftMode === 'SmartTrainer') {
|
|
279
|
+
return 'Adapter';
|
|
280
|
+
}
|
|
281
|
+
else if (virtshiftMode === 'Mixed') {
|
|
282
|
+
return 'SlopeDelta';
|
|
225
283
|
}
|
|
226
|
-
else {
|
|
227
|
-
return
|
|
284
|
+
else if (virtshiftMode === 'Enabled') {
|
|
285
|
+
return this.adapter.supportsVirtualShifting() ? 'Adapter' : 'Simulated';
|
|
228
286
|
}
|
|
287
|
+
return 'Disabled';
|
|
229
288
|
}
|
|
230
289
|
updateData(bikeData, log) {
|
|
231
|
-
var _a, _b, _c, _d;
|
|
290
|
+
var _a, _b, _c, _d, _e;
|
|
232
291
|
const prev = Object.assign({}, this.data);
|
|
233
292
|
const data = super.updateData(bikeData, log);
|
|
234
293
|
const mode = this.getVirtualShiftMode();
|
|
@@ -241,14 +300,14 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
241
300
|
this.tsStart = Date.now();
|
|
242
301
|
}
|
|
243
302
|
if (this.gear === undefined && this.tsStart && data.power > 0 && (Date.now() - this.tsStart > 3000)) {
|
|
244
|
-
this.
|
|
303
|
+
this.gear = (_a = this.getSetting('startGear')) !== null && _a !== void 0 ? _a : 0;
|
|
245
304
|
data.gearStr = this.getGearString();
|
|
246
305
|
}
|
|
247
306
|
else if (this.gear !== undefined) {
|
|
248
307
|
if (prev.power !== data.power || prev.pedalRpm !== data.pedalRpm) {
|
|
249
308
|
virtualSpeed = (0, calculations_1.calculateVirtualSpeed)(data.pedalRpm, this.gearRatios[this.gear - 1]);
|
|
250
|
-
const m = (
|
|
251
|
-
this.simPower = calculations_1.default.calculatePower(m, virtualSpeed, (
|
|
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);
|
|
252
311
|
this.verifySimPower();
|
|
253
312
|
}
|
|
254
313
|
}
|
|
@@ -279,16 +338,6 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
279
338
|
}
|
|
280
339
|
return newRequest;
|
|
281
340
|
}
|
|
282
|
-
setInitialGear(data) {
|
|
283
|
-
var _a, _b;
|
|
284
|
-
const m = (_b = (_a = this.adapter) === null || _a === void 0 ? void 0 : _a.getWeight()) !== null && _b !== void 0 ? _b : 85;
|
|
285
|
-
const virtualSpeeds = this.gearRatios.map(r => (0, calculations_1.calculateVirtualSpeed)(data.pedalRpm, r));
|
|
286
|
-
const requiredPowers = virtualSpeeds.map(v => { var _a; return calculations_1.default.calculatePower(m, v, (_a = data.slope) !== null && _a !== void 0 ? _a : 0); });
|
|
287
|
-
const deltas = requiredPowers.map((p, i) => ({ gear: i + 1, delta: Math.abs(p - data.power) }));
|
|
288
|
-
deltas.sort((a, b) => a.delta - b.delta);
|
|
289
|
-
this.gear = deltas[0].gear;
|
|
290
|
-
this.logger.logEvent({ message: 'set initial gear', gear: this.gear, m, rpm: data.pedalRpm, power: data.power });
|
|
291
|
-
}
|
|
292
341
|
getGearString() {
|
|
293
342
|
const mode = this.getVirtualShiftMode();
|
|
294
343
|
if (mode === "Disabled")
|
package/lib/modes/types.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export type UpdateRequest = {
|
|
|
10
10
|
refresh?: boolean;
|
|
11
11
|
init?: boolean;
|
|
12
12
|
gear?: number;
|
|
13
|
+
gearRatio?: number;
|
|
13
14
|
forced?: boolean;
|
|
14
15
|
};
|
|
15
16
|
export declare enum CyclingModeProperyType {
|
|
@@ -29,6 +30,7 @@ export type CyclingModeProperty = {
|
|
|
29
30
|
max?: number;
|
|
30
31
|
default?: any;
|
|
31
32
|
options?: any[];
|
|
33
|
+
condition?: (setting: any) => boolean;
|
|
32
34
|
};
|
|
33
35
|
export type Settings = {
|
|
34
36
|
[key: string]: any;
|