incyclist-devices 2.3.34 → 2.3.36
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/adapter.d.ts +1 -0
- package/lib/ble/base/adapter.js +5 -1
- 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 +42 -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 +22 -6
- package/lib/ble/zwift/play/sensor.js +251 -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 +9 -0
- package/lib/modes/antble-smarttrainer.d.ts +0 -1
- package/lib/modes/antble-smarttrainer.js +84 -27
- 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,69 @@ 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 : 400);
|
|
78
|
+
const cWax10000 = Math.round((_b = data === null || data === void 0 ? void 0 : data.cWax10000) !== null && _b !== void 0 ? _b : 5100);
|
|
79
|
+
const windx100 = Math.round((_c = data === null || data === void 0 ? void 0 : data.windx100) !== null && _c !== void 0 ? _c : 0);
|
|
80
|
+
let inclineX100 = Math.round((_d = data === null || data === void 0 ? void 0 : data.inclineX100) !== null && _d !== void 0 ? _d : 1);
|
|
81
|
+
if (inclineX100 === 0 || Number.isNaN(inclineX100))
|
|
82
|
+
inclineX100 = 1;
|
|
83
|
+
const simulation = { inclineX100, crrx100000, cWax10000, windx100 };
|
|
84
|
+
console.log('# set simulation data', simulation);
|
|
85
|
+
yield this.sendHubCommand({ simulation });
|
|
86
|
+
yield this.requestDataUpdate(512);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
setGearRatio(gearRatio) {
|
|
90
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
91
|
+
try {
|
|
92
|
+
console.log('# set gear ratio', gearRatio);
|
|
93
|
+
if (!this.isHubServiceActive) {
|
|
94
|
+
yield this.initHubService();
|
|
95
|
+
}
|
|
96
|
+
const gearRatioX10000 = Math.round(gearRatio * 10000);
|
|
97
|
+
const bikeWeightx100 = 10 * 100;
|
|
98
|
+
const riderWeightx100 = 75 * 100;
|
|
99
|
+
const command = {
|
|
100
|
+
physical: { bikeWeightx100, gearRatioX10000, riderWeightx100 }
|
|
101
|
+
};
|
|
102
|
+
yield this.sendHubCommand(command);
|
|
103
|
+
yield this.requestDataUpdate(512);
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
console.log('# set gear ratio failed', err);
|
|
107
|
+
}
|
|
108
|
+
return gearRatio;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
sendPlayCommand(id, command) {
|
|
112
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
113
|
+
const data = Buffer.concat([Buffer.from([id]), command]);
|
|
114
|
+
return yield this.write('00000003-19ca-4651-86e5-fa29dcdd09d1', data, { withoutResponse: true });
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
onMeasurement(d) {
|
|
57
118
|
const data = Buffer.from(d);
|
|
58
119
|
if ((data === null || data === void 0 ? void 0 : data.length) < 1) {
|
|
59
120
|
console.log('Invalid click measurement data', data.toString('hex'));
|
|
@@ -62,84 +123,190 @@ class BleZwiftPlaySensor extends sensor_1.TBleSensor {
|
|
|
62
123
|
const type = data.readUInt8(0);
|
|
63
124
|
const message = data.subarray(1);
|
|
64
125
|
if (type === 0x37) {
|
|
65
|
-
this.
|
|
126
|
+
this.onClickButtonMessage(message);
|
|
66
127
|
}
|
|
67
128
|
else if (type === 0x19) {
|
|
68
129
|
this.onPingMessage(message);
|
|
69
130
|
}
|
|
131
|
+
else if (type === 0x42) {
|
|
132
|
+
console.log('# init confirmed');
|
|
133
|
+
}
|
|
134
|
+
else if (type === 0x03) {
|
|
135
|
+
this.onRidingData(message);
|
|
136
|
+
}
|
|
137
|
+
else if (type === 0x3c) {
|
|
138
|
+
this.onDeviceInformation(message);
|
|
139
|
+
}
|
|
70
140
|
else {
|
|
71
141
|
console.log('Unknown click measurement type', type, message.toString('hex'));
|
|
72
142
|
this.emit('data', { raw: data.toString('hex') });
|
|
73
143
|
}
|
|
74
144
|
return true;
|
|
75
145
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
146
|
+
initHubService() {
|
|
147
|
+
return __awaiter(this, arguments, void 0, function* (setSimulation = true) {
|
|
148
|
+
if (!this.isHubServiceActive && this.initHubServicePromise !== undefined) {
|
|
149
|
+
yield this.initHubServicePromise;
|
|
150
|
+
}
|
|
151
|
+
if (this.isHubServiceActive)
|
|
152
|
+
return true;
|
|
153
|
+
this.logEvent({ message: 'init hub service', paired: this.paired, subscribed: this.isHubServiceSubscribed });
|
|
154
|
+
console.log('# init Hub Service');
|
|
155
|
+
if (!this.isHubServicePaired) {
|
|
156
|
+
yield this.pair();
|
|
157
|
+
}
|
|
158
|
+
if (!this.isHubServiceSubscribed) {
|
|
159
|
+
yield this.subscribe();
|
|
160
|
+
this.isHubServiceSubscribed = true;
|
|
161
|
+
}
|
|
162
|
+
this.initHubServicePromise = new Promise((done) => {
|
|
163
|
+
let timeout = setTimeout(() => {
|
|
164
|
+
done(false);
|
|
165
|
+
}, 2000);
|
|
166
|
+
this.once('hub-riding-data', () => {
|
|
167
|
+
if (timeout) {
|
|
168
|
+
clearTimeout(timeout);
|
|
169
|
+
timeout = undefined;
|
|
170
|
+
}
|
|
171
|
+
this.isHubServiceActive = true;
|
|
172
|
+
console.log('# init hub service completed');
|
|
173
|
+
this.logEvent({ message: 'init hub service done', paired: this.paired, subscribed: this.isHubServiceSubscribed });
|
|
174
|
+
if (setSimulation) {
|
|
175
|
+
this.logEvent({ message: 'hub: send initial simulation data' });
|
|
176
|
+
this.setSimulationData().then(() => {
|
|
177
|
+
done(true);
|
|
178
|
+
})
|
|
179
|
+
.catch(() => {
|
|
180
|
+
done(true);
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
done(true);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
this.sendPlayCommand(0x41, Buffer.from([0x08, 0x05]))
|
|
188
|
+
.catch(err => {
|
|
189
|
+
if (timeout) {
|
|
190
|
+
clearTimeout(timeout);
|
|
191
|
+
timeout = undefined;
|
|
192
|
+
}
|
|
193
|
+
console.log('# init hub service timeout');
|
|
194
|
+
done(false);
|
|
195
|
+
this.logEvent({ message: 'could not init hub service', reason: err.message });
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
return this.initHubServicePromise;
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
sendHubRequest(request) {
|
|
202
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
203
|
+
const message = Buffer.from(zwift_hub_1.HubRequest.toBinary(request));
|
|
204
|
+
console.log('# sending hub request', request, message);
|
|
205
|
+
this.logEvent({ mesage: 'send zwift hub request', request });
|
|
206
|
+
return yield this.sendPlayCommand(0x00, message);
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
sendHubCommand(command) {
|
|
210
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
211
|
+
const message = Buffer.from(zwift_hub_1.HubCommand.toBinary(command));
|
|
212
|
+
console.log('# sending hub command', command, message);
|
|
213
|
+
this.logEvent({ mesage: 'send zwift hub command', command });
|
|
214
|
+
return yield this.sendPlayCommand(0x04, message);
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
onRidingData(m) {
|
|
79
218
|
try {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
219
|
+
this.tsLastRidingData = Date.now();
|
|
220
|
+
const data = zwift_hub_1.HubRidingData.fromBinary(m);
|
|
221
|
+
this.emit('hub-riding-data', data);
|
|
222
|
+
console.log('#riding data', data);
|
|
223
|
+
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 });
|
|
224
|
+
}
|
|
225
|
+
catch (err) {
|
|
226
|
+
this.logEvent({ message: 'Error', fn: 'onRidingData', error: err.message, stack: err.stack });
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
onDeviceInformation(m) {
|
|
230
|
+
try {
|
|
231
|
+
const envelope = zwift_hub_1.DeviceDataEnvelope.fromBinary(m);
|
|
232
|
+
const { messageType, payload } = envelope;
|
|
233
|
+
if (messageType < 16) {
|
|
234
|
+
const deviceInfo = zwift_hub_1.DeviceInformationContent.fromBinary(payload);
|
|
235
|
+
console.log('# deviceInfo', deviceInfo);
|
|
236
|
+
this.emit('hub-device-info', deviceInfo);
|
|
237
|
+
this.logEvent({ message: 'hub device info update', deviceInfo });
|
|
238
|
+
}
|
|
239
|
+
else if (messageType >= 512 && messageType < 526) {
|
|
240
|
+
const si = zwift_hub_1.DeviceSettings.fromBinary(payload);
|
|
241
|
+
console.log('# settings', si === null || si === void 0 ? void 0 : si.subContent);
|
|
242
|
+
this.emit('hub-settings', si === null || si === void 0 ? void 0 : si.subContent);
|
|
243
|
+
this.logEvent({ message: 'hub settings update', settings: si === null || si === void 0 ? void 0 : si.subContent });
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
catch (err) {
|
|
247
|
+
let payload = 'unknown';
|
|
248
|
+
try {
|
|
249
|
+
payload = m.toString('hex');
|
|
250
|
+
}
|
|
251
|
+
catch (_a) { }
|
|
252
|
+
this.logEvent({ message: 'Error', fn: 'onRidingData', error: err.message, stack: err.stack, payload });
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
onClickButtonMessage(d) {
|
|
256
|
+
try {
|
|
257
|
+
const message = Buffer.from(d);
|
|
258
|
+
const messageStr = message.toString('hex');
|
|
259
|
+
if (messageStr === this.prevClickMessage) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const status = zwift_hub_1.ClickKeyPadStatus.fromBinary(message);
|
|
263
|
+
if (status.buttonPlus === zwift_hub_1.PlayButtonStatus.OFF) {
|
|
264
|
+
const prev = Object.assign({}, this.upState);
|
|
265
|
+
this.upState = { pressed: false, timestamp: Date.now() };
|
|
266
|
+
if (prev.pressed) {
|
|
267
|
+
this.emit('key-pressed', { key: 'up', duration: this.upState.timestamp - prev.timestamp, deviceType: this.deviceType });
|
|
85
268
|
}
|
|
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;
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
this.upState = { pressed: true, timestamp: Date.now() };
|
|
272
|
+
}
|
|
273
|
+
if (status.buttonMinus === zwift_hub_1.PlayButtonStatus.OFF) {
|
|
274
|
+
const prev = Object.assign({}, this.downState);
|
|
275
|
+
this.downState = { pressed: false, timestamp: Date.now() };
|
|
276
|
+
if (prev.pressed) {
|
|
277
|
+
this.emit('key-pressed', { key: 'down', duration: this.downState.timestamp - prev.timestamp, deviceType: this.deviceType });
|
|
109
278
|
}
|
|
110
|
-
this.prevClickMessage = messageStr;
|
|
111
279
|
}
|
|
112
280
|
else {
|
|
113
|
-
this.
|
|
281
|
+
this.downState = { pressed: true, timestamp: Date.now() };
|
|
114
282
|
}
|
|
283
|
+
this.prevClickMessage = messageStr;
|
|
115
284
|
}
|
|
116
285
|
catch (err) {
|
|
117
286
|
this.logEvent({ message: 'error', fn: 'onButtonMessage', error: err.message, stack: err.stack });
|
|
118
287
|
}
|
|
119
288
|
}
|
|
120
|
-
onPingMessage(
|
|
289
|
+
onPingMessage(message) {
|
|
290
|
+
const idle = zwift_hub_1.Idle.fromBinary(message);
|
|
291
|
+
console.log('# ping message', idle);
|
|
121
292
|
this.emit('data', { deviceType: this.deviceType, paired: this.paired, alive: true, ts: Date.now() });
|
|
122
293
|
}
|
|
123
|
-
|
|
294
|
+
onResponse(d) {
|
|
124
295
|
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
|
-
}
|
|
296
|
+
if ((data === null || data === void 0 ? void 0 : data.length) < 1) {
|
|
297
|
+
console.log('Invalid response data', data.toString('hex'));
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
const type = data.readUInt8(0);
|
|
301
|
+
const message = data.subarray(1);
|
|
302
|
+
if (type === 0x3c) {
|
|
303
|
+
this.onDeviceInformation(message);
|
|
136
304
|
}
|
|
137
|
-
else
|
|
138
|
-
const
|
|
139
|
-
if (
|
|
140
|
-
this.encrypted =
|
|
305
|
+
else {
|
|
306
|
+
const len = data.length;
|
|
307
|
+
if (len === 6 && data.toString() === 'RideOn') {
|
|
308
|
+
this.encrypted = false;
|
|
141
309
|
this.paired = true;
|
|
142
|
-
this.deviceKey = data.subarray(8);
|
|
143
310
|
this.emitter.emit('paired');
|
|
144
311
|
try {
|
|
145
312
|
this.emit('data', { deviceType: this.deviceType, paired: true, ts: Date.now() });
|
|
@@ -148,11 +315,26 @@ class BleZwiftPlaySensor extends sensor_1.TBleSensor {
|
|
|
148
315
|
this.logEvent({ message: 'error', fn: 'onPairResponse', error: err.message, stack: err.stack });
|
|
149
316
|
}
|
|
150
317
|
}
|
|
151
|
-
else {
|
|
152
|
-
|
|
318
|
+
else if (len > 8 && Buffer.from(data.subarray(0, 6)).toString() === 'RideOn') {
|
|
319
|
+
const message = Buffer.from(data.subarray(6, 2)).toString('hex');
|
|
320
|
+
if (message === '0900') {
|
|
321
|
+
this.encrypted = true;
|
|
322
|
+
this.paired = true;
|
|
323
|
+
this.deviceKey = data.subarray(8);
|
|
324
|
+
this.emitter.emit('paired');
|
|
325
|
+
try {
|
|
326
|
+
this.emit('data', { deviceType: this.deviceType, paired: true, ts: Date.now() });
|
|
327
|
+
}
|
|
328
|
+
catch (err) {
|
|
329
|
+
this.logEvent({ message: 'error', fn: 'onPairResponse', error: err.message, stack: err.stack });
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
this.logEvent({ message: 'Pairing failed! ', reason: 'unknown message', raw: message });
|
|
334
|
+
}
|
|
153
335
|
}
|
|
336
|
+
return true;
|
|
154
337
|
}
|
|
155
|
-
return true;
|
|
156
338
|
}
|
|
157
339
|
read(characteristic_1) {
|
|
158
340
|
const _super = Object.create(null, {
|
|
@@ -172,6 +354,8 @@ class BleZwiftPlaySensor extends sensor_1.TBleSensor {
|
|
|
172
354
|
pair() {
|
|
173
355
|
return __awaiter(this, void 0, void 0, function* () {
|
|
174
356
|
var _a, _b;
|
|
357
|
+
if (this.isHubServicePaired)
|
|
358
|
+
return true;
|
|
175
359
|
let manufacturerData;
|
|
176
360
|
try {
|
|
177
361
|
if (this.peripheral.getManufacturerData) {
|
|
@@ -197,7 +381,7 @@ class BleZwiftPlaySensor extends sensor_1.TBleSensor {
|
|
|
197
381
|
this.logEvent({ message: 'Play protocol pairing info', deviceType: this.deviceType, encrypted: this.encrypted, manufacturerData });
|
|
198
382
|
let message;
|
|
199
383
|
if (!this.encrypted) {
|
|
200
|
-
message = Buffer.from('RideOn');
|
|
384
|
+
message = this.isFM ? Buffer.concat([Buffer.from('RideOn'), Buffer.from([0x02, 0x01])]) : Buffer.from('RideOn');
|
|
201
385
|
}
|
|
202
386
|
else {
|
|
203
387
|
const { publicKey, privateKey } = this.createKeyPair();
|
|
@@ -206,10 +390,12 @@ class BleZwiftPlaySensor extends sensor_1.TBleSensor {
|
|
|
206
390
|
message = Buffer.concat([Buffer.from('RideOn'), Buffer.from([0x01, 0x02]), this.publicKey]);
|
|
207
391
|
}
|
|
208
392
|
yield this.write((0, utils_1.fullUUID)('00000003-19ca-4651-86e5-fa29dcdd09d1'), message, { withoutResponse: true });
|
|
393
|
+
this.isHubServicePaired = true;
|
|
209
394
|
return true;
|
|
210
395
|
}
|
|
211
396
|
catch (err) {
|
|
212
397
|
this.logEvent({ message: 'error', fn: 'pair', error: err.message, stack: err.stack });
|
|
398
|
+
this.isHubServicePaired = false;
|
|
213
399
|
return false;
|
|
214
400
|
}
|
|
215
401
|
});
|
|
@@ -230,6 +416,10 @@ class BleZwiftPlaySensor extends sensor_1.TBleSensor {
|
|
|
230
416
|
this.paired = false;
|
|
231
417
|
this.encrypted = false;
|
|
232
418
|
this.deviceKey = null;
|
|
419
|
+
this.isHubServicePaired = false;
|
|
420
|
+
this.isHubServiceSubscribed = false;
|
|
421
|
+
this.isHubServiceActive = false;
|
|
422
|
+
delete this.initHubServicePromise;
|
|
233
423
|
}
|
|
234
424
|
getManufacturerData() {
|
|
235
425
|
const data = this.peripheral.getManufacturerData();
|
|
@@ -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) {
|
|
@@ -146,6 +154,7 @@ class DirectConnectPeripheral {
|
|
|
146
154
|
if (this.subscribed.includes(uuid)) {
|
|
147
155
|
return true;
|
|
148
156
|
}
|
|
157
|
+
console.log('# subscribe ', characteristicUUID);
|
|
149
158
|
const seqNo = this.getNextSeqNo();
|
|
150
159
|
const message = new messages_1.EnableCharacteristicNotificationsMessage();
|
|
151
160
|
const request = message.createRequest(seqNo, { characteristicUUID: (0, utils_1.parseUUID)(characteristicUUID), enable: true });
|
|
@@ -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: 12, 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
|
}
|
|
@@ -90,7 +102,8 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
90
102
|
}
|
|
91
103
|
}
|
|
92
104
|
checkSlopeWithAdapterShifting(request, newRequest = {}) {
|
|
93
|
-
|
|
105
|
+
this.checkSlopeNoShiftig(request, newRequest);
|
|
106
|
+
newRequest.gearRatio = this.gearRatios[this.gear];
|
|
94
107
|
}
|
|
95
108
|
getSlopeDelta() {
|
|
96
109
|
return this.gearDelta * 0.5;
|
|
@@ -170,7 +183,8 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
170
183
|
case 'Simulated':
|
|
171
184
|
if (request.gearDelta !== undefined) {
|
|
172
185
|
if (this.gear === undefined) {
|
|
173
|
-
|
|
186
|
+
const initialGear = this.getSetting('startGear');
|
|
187
|
+
this.gear = initialGear + request.gearDelta;
|
|
174
188
|
}
|
|
175
189
|
else {
|
|
176
190
|
this.gear += request.gearDelta;
|
|
@@ -192,6 +206,47 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
192
206
|
}
|
|
193
207
|
break;
|
|
194
208
|
case 'Adapter':
|
|
209
|
+
if (request.gearRatio !== undefined) {
|
|
210
|
+
newRequest.gearRatio = request.gearRatio;
|
|
211
|
+
if (this.gear === undefined) {
|
|
212
|
+
const requestedRatio = request.gearRatio;
|
|
213
|
+
let closestIndex = 0;
|
|
214
|
+
let minDiff = Math.abs(this.gearRatios[0] - requestedRatio);
|
|
215
|
+
for (let i = 1; i < this.gearRatios.length; i++) {
|
|
216
|
+
const diff = Math.abs(this.gearRatios[i] - requestedRatio);
|
|
217
|
+
if (diff < minDiff) {
|
|
218
|
+
minDiff = diff;
|
|
219
|
+
closestIndex = i;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
this.gear = closestIndex + 1;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
else if (request.gearDelta !== undefined) {
|
|
226
|
+
if (this.gear === undefined) {
|
|
227
|
+
const initialGear = this.getSetting('startGear');
|
|
228
|
+
this.gear = initialGear + request.gearDelta;
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
this.gear += request.gearDelta;
|
|
232
|
+
}
|
|
233
|
+
if (this.gear < 1) {
|
|
234
|
+
this.gear = 1;
|
|
235
|
+
}
|
|
236
|
+
if (this.gear > this.gearRatios.length) {
|
|
237
|
+
this.gear = this.gearRatios.length;
|
|
238
|
+
}
|
|
239
|
+
delete request.gearDelta;
|
|
240
|
+
newRequest.gearRatio = this.gearRatios[this.gear];
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
if (this.gear === undefined) {
|
|
244
|
+
const initialGear = this.getSetting('startGear');
|
|
245
|
+
this.gear = initialGear + request.gearDelta;
|
|
246
|
+
}
|
|
247
|
+
newRequest.gearRatio = this.gearRatios[this.gear];
|
|
248
|
+
}
|
|
249
|
+
break;
|
|
195
250
|
case 'Disabled':
|
|
196
251
|
default:
|
|
197
252
|
break;
|
|
@@ -218,17 +273,22 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
218
273
|
if (virtshiftMode === 'Disabled') {
|
|
219
274
|
return 'Disabled';
|
|
220
275
|
}
|
|
221
|
-
if (
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
276
|
+
else if (virtshiftMode === 'Incyclist') {
|
|
277
|
+
return 'Simulated';
|
|
278
|
+
}
|
|
279
|
+
else if (virtshiftMode === 'SmartTrainer') {
|
|
280
|
+
return 'Adapter';
|
|
225
281
|
}
|
|
226
|
-
else {
|
|
227
|
-
return
|
|
282
|
+
else if (virtshiftMode === 'Mixed') {
|
|
283
|
+
return 'SlopeDelta';
|
|
228
284
|
}
|
|
285
|
+
else if (virtshiftMode === 'Enabled') {
|
|
286
|
+
return this.adapter.supportsVirtualShifting() ? 'Adapter' : 'Simulated';
|
|
287
|
+
}
|
|
288
|
+
return 'Disabled';
|
|
229
289
|
}
|
|
230
290
|
updateData(bikeData, log) {
|
|
231
|
-
var _a, _b, _c, _d;
|
|
291
|
+
var _a, _b, _c, _d, _e;
|
|
232
292
|
const prev = Object.assign({}, this.data);
|
|
233
293
|
const data = super.updateData(bikeData, log);
|
|
234
294
|
const mode = this.getVirtualShiftMode();
|
|
@@ -241,14 +301,14 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
241
301
|
this.tsStart = Date.now();
|
|
242
302
|
}
|
|
243
303
|
if (this.gear === undefined && this.tsStart && data.power > 0 && (Date.now() - this.tsStart > 3000)) {
|
|
244
|
-
this.
|
|
304
|
+
this.gear = (_a = this.getSetting('startGear')) !== null && _a !== void 0 ? _a : 0;
|
|
245
305
|
data.gearStr = this.getGearString();
|
|
246
306
|
}
|
|
247
307
|
else if (this.gear !== undefined) {
|
|
248
308
|
if (prev.power !== data.power || prev.pedalRpm !== data.pedalRpm) {
|
|
249
309
|
virtualSpeed = (0, calculations_1.calculateVirtualSpeed)(data.pedalRpm, this.gearRatios[this.gear - 1]);
|
|
250
|
-
const m = (
|
|
251
|
-
this.simPower = calculations_1.default.calculatePower(m, virtualSpeed, (
|
|
310
|
+
const m = (_c = (_b = this.adapter) === null || _b === void 0 ? void 0 : _b.getWeight()) !== null && _c !== void 0 ? _c : 85;
|
|
311
|
+
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
312
|
this.verifySimPower();
|
|
253
313
|
}
|
|
254
314
|
}
|
|
@@ -279,25 +339,22 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
279
339
|
}
|
|
280
340
|
return newRequest;
|
|
281
341
|
}
|
|
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
342
|
getGearString() {
|
|
343
|
+
var _a, _b, _c, _d, _e;
|
|
293
344
|
const mode = this.getVirtualShiftMode();
|
|
294
345
|
if (mode === "Disabled")
|
|
295
346
|
return undefined;
|
|
296
|
-
if (mode === 'Simulated'
|
|
297
|
-
|
|
347
|
+
if (mode === 'Simulated') {
|
|
348
|
+
this.gear = (_b = (_a = this.gear) !== null && _a !== void 0 ? _a : this.getSetting('startGear')) !== null && _b !== void 0 ? _b : 0;
|
|
349
|
+
return this.gear.toString();
|
|
350
|
+
}
|
|
298
351
|
if (mode === "SlopeDelta")
|
|
299
352
|
return this.gearDelta > 0 ? `+${this.gearDelta}` : `${this.gearDelta}`;
|
|
300
|
-
|
|
353
|
+
if (mode === 'Adapter') {
|
|
354
|
+
this.gear = (_d = (_c = this.gear) !== null && _c !== void 0 ? _c : this.getSetting('startGear')) !== null && _d !== void 0 ? _d : 0;
|
|
355
|
+
return this.gear.toString();
|
|
356
|
+
}
|
|
357
|
+
return (_e = this.gear) === null || _e === void 0 ? void 0 : _e.toString();
|
|
301
358
|
}
|
|
302
359
|
}
|
|
303
360
|
SmartTrainerCyclingMode.config = {
|