incyclist-devices 2.3.33 → 2.3.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/antv2/fe/adapter.js +1 -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 +8 -2
- package/lib/ble/fm/adapter.js +60 -8
- package/lib/ble/fm/sensor.js +85 -66
- 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 +1 -1
- package/lib/modes/antble-smarttrainer.js +81 -22
- package/lib/modes/power-base.d.ts +1 -1
- package/lib/modes/types.d.ts +4 -0
- package/lib/modes/types.js +3 -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) {
|
|
@@ -48,8 +48,8 @@ export default class SmartTrainerCyclingMode extends PowerBasedCyclingModeBase i
|
|
|
48
48
|
protected checkEmptyRequest(newRequest: UpdateRequest): void;
|
|
49
49
|
protected getVirtualShiftMode(): VirtshiftMode;
|
|
50
50
|
updateData(bikeData: IncyclistBikeData, log?: boolean): IncyclistBikeData;
|
|
51
|
+
getData(): Partial<IncyclistBikeData>;
|
|
51
52
|
sendBikeUpdate(incoming: UpdateRequest): UpdateRequest;
|
|
52
|
-
protected setInitialGear(data: IncyclistBikeData): void;
|
|
53
53
|
protected getGearString(): string;
|
|
54
54
|
}
|
|
55
55
|
export {};
|
|
@@ -51,6 +51,12 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
51
51
|
this.initLogger('SmartTrainerMode');
|
|
52
52
|
}
|
|
53
53
|
getBikeInitRequest() {
|
|
54
|
+
const virtshiftMode = this.getVirtualShiftMode();
|
|
55
|
+
if (virtshiftMode === 'Adapter') {
|
|
56
|
+
this.gear = this.getSetting('startGear');
|
|
57
|
+
const gearRatio = this.gearRatios[this.gear - 1];
|
|
58
|
+
return { slope: 0, gearRatio };
|
|
59
|
+
}
|
|
54
60
|
this.prevRequest = { slope: 0 };
|
|
55
61
|
return { slope: 0 };
|
|
56
62
|
}
|
|
@@ -66,13 +72,19 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
66
72
|
getConfig() {
|
|
67
73
|
const config = super.getConfig();
|
|
68
74
|
let virtshift = config.properties.find(p => p.key === 'virtshift');
|
|
75
|
+
let startGear = config.properties.find(p => p.key === 'startGear');
|
|
69
76
|
if (!virtshift) {
|
|
70
|
-
virtshift = { key: 'virtshift', name: 'Virtual Shifting', description: 'Enable virtual shifting', type: types_1.CyclingModeProperyType.SingleSelect, options: ['Disabled', '
|
|
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
|
}
|
|
@@ -133,6 +145,7 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
133
145
|
const m = (_c = (_b = this.adapter) === null || _b === void 0 ? void 0 : _b.getWeight()) !== null && _c !== void 0 ? _c : 85;
|
|
134
146
|
this.simPower = calculations_1.default.calculatePower(m, virtualSpeed, (_d = this.simSlope) !== null && _d !== void 0 ? _d : 0);
|
|
135
147
|
this.verifySimPower();
|
|
148
|
+
this.logger.logEvent({ message: 'set simulater power', power: this.simPower, gear: this.gear, simSlope: this.simSlope, routeSlope: this.data.slope });
|
|
136
149
|
}
|
|
137
150
|
}
|
|
138
151
|
newRequest.targetPower = this.simPower;
|
|
@@ -169,7 +182,8 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
169
182
|
case 'Simulated':
|
|
170
183
|
if (request.gearDelta !== undefined) {
|
|
171
184
|
if (this.gear === undefined) {
|
|
172
|
-
|
|
185
|
+
const initialGear = this.getSetting('startGear');
|
|
186
|
+
this.gear = initialGear + request.gearDelta;
|
|
173
187
|
}
|
|
174
188
|
else {
|
|
175
189
|
this.gear += request.gearDelta;
|
|
@@ -185,9 +199,53 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
185
199
|
const m = (_b = (_a = this.adapter) === null || _a === void 0 ? void 0 : _a.getWeight()) !== null && _b !== void 0 ? _b : 85;
|
|
186
200
|
this.simPower = calculations_1.default.calculatePower(m, virtualSpeed, (_d = (_c = this.simSlope) !== null && _c !== void 0 ? _c : this.data.slope) !== null && _d !== void 0 ? _d : 0);
|
|
187
201
|
this.verifySimPower();
|
|
202
|
+
this.logger.logEvent({ message: 'set simulater power', power: this.simPower, gear: this.gear, simSlope: this.simSlope, routeSlope: this.data.slope });
|
|
203
|
+
this.adapter.sendUpdate({ targetPower: this.simPower }).then(() => {
|
|
204
|
+
});
|
|
188
205
|
}
|
|
189
206
|
break;
|
|
190
207
|
case 'Adapter':
|
|
208
|
+
if (request.gearRatio !== undefined) {
|
|
209
|
+
newRequest.gearRatio = request.gearRatio;
|
|
210
|
+
if (this.gear === undefined) {
|
|
211
|
+
const requestedRatio = request.gearRatio;
|
|
212
|
+
let closestIndex = 0;
|
|
213
|
+
let minDiff = Math.abs(this.gearRatios[0] - requestedRatio);
|
|
214
|
+
for (let i = 1; i < this.gearRatios.length; i++) {
|
|
215
|
+
const diff = Math.abs(this.gearRatios[i] - requestedRatio);
|
|
216
|
+
if (diff < minDiff) {
|
|
217
|
+
minDiff = diff;
|
|
218
|
+
closestIndex = i;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
this.gear = closestIndex + 1;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
else if (request.gearDelta !== undefined) {
|
|
225
|
+
if (this.gear === undefined) {
|
|
226
|
+
const initialGear = this.getSetting('startGear');
|
|
227
|
+
this.gear = initialGear + request.gearDelta;
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
this.gear += request.gearDelta;
|
|
231
|
+
}
|
|
232
|
+
if (this.gear < 1) {
|
|
233
|
+
this.gear = 1;
|
|
234
|
+
}
|
|
235
|
+
if (this.gear > this.gearRatios.length) {
|
|
236
|
+
this.gear = this.gearRatios.length;
|
|
237
|
+
}
|
|
238
|
+
delete request.gearDelta;
|
|
239
|
+
newRequest.gearRatio = this.gearRatios[this.gear];
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
if (this.gear === undefined) {
|
|
243
|
+
const initialGear = this.getSetting('startGear');
|
|
244
|
+
this.gear = initialGear + request.gearDelta;
|
|
245
|
+
}
|
|
246
|
+
newRequest.gearRatio = this.gearRatios[this.gear];
|
|
247
|
+
}
|
|
248
|
+
break;
|
|
191
249
|
case 'Disabled':
|
|
192
250
|
default:
|
|
193
251
|
break;
|
|
@@ -214,17 +272,22 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
214
272
|
if (virtshiftMode === 'Disabled') {
|
|
215
273
|
return 'Disabled';
|
|
216
274
|
}
|
|
217
|
-
if (
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
275
|
+
else if (virtshiftMode === 'Incyclist') {
|
|
276
|
+
return 'Simulated';
|
|
277
|
+
}
|
|
278
|
+
else if (virtshiftMode === 'SmartTrainer') {
|
|
279
|
+
return 'Adapter';
|
|
221
280
|
}
|
|
222
|
-
else {
|
|
223
|
-
return
|
|
281
|
+
else if (virtshiftMode === 'Mixed') {
|
|
282
|
+
return 'SlopeDelta';
|
|
224
283
|
}
|
|
284
|
+
else if (virtshiftMode === 'Enabled') {
|
|
285
|
+
return this.adapter.supportsVirtualShifting() ? 'Adapter' : 'Simulated';
|
|
286
|
+
}
|
|
287
|
+
return 'Disabled';
|
|
225
288
|
}
|
|
226
289
|
updateData(bikeData, log) {
|
|
227
|
-
var _a, _b, _c, _d;
|
|
290
|
+
var _a, _b, _c, _d, _e;
|
|
228
291
|
const prev = Object.assign({}, this.data);
|
|
229
292
|
const data = super.updateData(bikeData, log);
|
|
230
293
|
const mode = this.getVirtualShiftMode();
|
|
@@ -237,19 +300,24 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
237
300
|
this.tsStart = Date.now();
|
|
238
301
|
}
|
|
239
302
|
if (this.gear === undefined && this.tsStart && data.power > 0 && (Date.now() - this.tsStart > 3000)) {
|
|
240
|
-
this.
|
|
303
|
+
this.gear = (_a = this.getSetting('startGear')) !== null && _a !== void 0 ? _a : 0;
|
|
241
304
|
data.gearStr = this.getGearString();
|
|
242
305
|
}
|
|
243
306
|
else if (this.gear !== undefined) {
|
|
244
307
|
if (prev.power !== data.power || prev.pedalRpm !== data.pedalRpm) {
|
|
245
308
|
virtualSpeed = (0, calculations_1.calculateVirtualSpeed)(data.pedalRpm, this.gearRatios[this.gear - 1]);
|
|
246
|
-
const m = (
|
|
247
|
-
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);
|
|
248
311
|
this.verifySimPower();
|
|
249
312
|
}
|
|
250
313
|
}
|
|
251
314
|
return data;
|
|
252
315
|
}
|
|
316
|
+
getData() {
|
|
317
|
+
const gearStr = this.getGearString();
|
|
318
|
+
const data = super.getData();
|
|
319
|
+
return Object.assign(Object.assign({}, data), { gearStr });
|
|
320
|
+
}
|
|
253
321
|
sendBikeUpdate(incoming) {
|
|
254
322
|
this.logger.logEvent({ message: "processing update request", request: incoming, prev: this.prevRequest, data: this.getData() });
|
|
255
323
|
let newRequest = {};
|
|
@@ -270,15 +338,6 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
270
338
|
}
|
|
271
339
|
return newRequest;
|
|
272
340
|
}
|
|
273
|
-
setInitialGear(data) {
|
|
274
|
-
var _a, _b;
|
|
275
|
-
const m = (_b = (_a = this.adapter) === null || _a === void 0 ? void 0 : _a.getWeight()) !== null && _b !== void 0 ? _b : 85;
|
|
276
|
-
const virtualSpeeds = this.gearRatios.map(r => (0, calculations_1.calculateVirtualSpeed)(data.pedalRpm, r));
|
|
277
|
-
const requiredPowers = virtualSpeeds.map(v => { var _a; return calculations_1.default.calculatePower(m, v, (_a = data.slope) !== null && _a !== void 0 ? _a : 0); });
|
|
278
|
-
const deltas = requiredPowers.map((p, i) => ({ gear: i + 1, delta: Math.abs(p - data.power) }));
|
|
279
|
-
deltas.sort((a, b) => a.delta - b.delta);
|
|
280
|
-
this.gear = deltas[0].gear;
|
|
281
|
-
}
|
|
282
341
|
getGearString() {
|
|
283
342
|
const mode = this.getVirtualShiftMode();
|
|
284
343
|
if (mode === "Disabled")
|
|
@@ -9,7 +9,7 @@ export default class PowerBasedCyclingModeBase extends CyclingModeBase {
|
|
|
9
9
|
prevRequest: UpdateRequest | undefined;
|
|
10
10
|
protected static config: CyclingModeConfig;
|
|
11
11
|
constructor(adapter: IAdapter, props?: Settings);
|
|
12
|
-
getData(): IncyclistBikeData
|
|
12
|
+
getData(): Partial<IncyclistBikeData>;
|
|
13
13
|
getSlope(): number;
|
|
14
14
|
initLogger(defaultLogName: any): void;
|
|
15
15
|
getWeight(): number;
|