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
package/lib/antv2/fe/adapter.js
CHANGED
|
@@ -48,7 +48,7 @@ class AntFEAdapter extends adapter_1.default {
|
|
|
48
48
|
return;
|
|
49
49
|
if (this.promiseStop)
|
|
50
50
|
return;
|
|
51
|
-
if (this.promiseSendUpdate) {
|
|
51
|
+
if (this.promiseSendUpdate !== undefined) {
|
|
52
52
|
this.logEvent({ message: 'send bike update skipped', device: this.getName(), request, reason: 'busy' });
|
|
53
53
|
return;
|
|
54
54
|
}
|
|
@@ -13,10 +13,13 @@ export declare class BlePeripheral implements IBlePeripheral {
|
|
|
13
13
|
}>;
|
|
14
14
|
protected disconnecting: boolean;
|
|
15
15
|
protected disconnectedSignalled: boolean;
|
|
16
|
+
protected discoveredServiceUUIds: Array<string>;
|
|
16
17
|
protected onErrorHandler: any;
|
|
17
18
|
constructor(announcement: BlePeripheralAnnouncement);
|
|
18
19
|
get services(): BleService[];
|
|
19
20
|
getPeripheral(): BleRawPeripheral;
|
|
21
|
+
getAnnouncedServices(): string[];
|
|
22
|
+
getDiscoveredServices(): string[];
|
|
20
23
|
getInfo(): BleDeviceIdentifier;
|
|
21
24
|
connect(): Promise<boolean>;
|
|
22
25
|
disconnect(connectionLost?: boolean): Promise<boolean>;
|
|
@@ -29,6 +29,12 @@ class BlePeripheral {
|
|
|
29
29
|
getPeripheral() {
|
|
30
30
|
return this.announcement.peripheral;
|
|
31
31
|
}
|
|
32
|
+
getAnnouncedServices() {
|
|
33
|
+
return this.announcement.serviceUUIDs.map(s => (0, utils_1.beautifyUUID)(s));
|
|
34
|
+
}
|
|
35
|
+
getDiscoveredServices() {
|
|
36
|
+
return this.discoveredServiceUUIds;
|
|
37
|
+
}
|
|
32
38
|
getInfo() {
|
|
33
39
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
34
40
|
return {
|
|
@@ -132,11 +138,13 @@ class BlePeripheral {
|
|
|
132
138
|
if (this.getPeripheral().discoverServicesAsync) {
|
|
133
139
|
this.logEvent({ message: 'discover services', address: this.getPeripheral().address });
|
|
134
140
|
const services = yield this.getPeripheral().discoverServicesAsync([]);
|
|
141
|
+
this.discoveredServiceUUIds = services.map(s => (0, utils_1.beautifyUUID)(s.uuid));
|
|
135
142
|
return services.map(s => s.uuid);
|
|
136
143
|
}
|
|
137
144
|
else {
|
|
138
145
|
this.logEvent({ message: 'discover services and characteristics', address: this.getPeripheral().address });
|
|
139
146
|
const res = yield this.getPeripheral().discoverSomeServicesAndCharacteristicsAsync([], []);
|
|
147
|
+
this.discoveredServiceUUIds = res.services.map(s => (0, utils_1.beautifyUUID)(s.uuid));
|
|
140
148
|
return res.services.map(s => s.uuid);
|
|
141
149
|
}
|
|
142
150
|
});
|
|
@@ -246,6 +254,12 @@ class BlePeripheral {
|
|
|
246
254
|
}
|
|
247
255
|
subscribeSelected(characteristics, callback) {
|
|
248
256
|
return __awaiter(this, void 0, void 0, function* () {
|
|
257
|
+
if (!this.discoveredServiceUUIds) {
|
|
258
|
+
try {
|
|
259
|
+
yield this.discoverServices();
|
|
260
|
+
}
|
|
261
|
+
catch (_a) { }
|
|
262
|
+
}
|
|
249
263
|
try {
|
|
250
264
|
if (Object.keys(this.characteristics).length === 0) {
|
|
251
265
|
yield this.discoverAllCharacteristics();
|
|
@@ -308,6 +322,12 @@ class BlePeripheral {
|
|
|
308
322
|
}
|
|
309
323
|
subscribeAll(callback) {
|
|
310
324
|
return __awaiter(this, void 0, void 0, function* () {
|
|
325
|
+
if (!this.discoveredServiceUUIds) {
|
|
326
|
+
try {
|
|
327
|
+
yield this.discoverServices();
|
|
328
|
+
}
|
|
329
|
+
catch (_a) { }
|
|
330
|
+
}
|
|
311
331
|
const characteristics = yield this.discoverAllCharacteristics();
|
|
312
332
|
const success = yield this.subscribeSelected(characteristics, callback);
|
|
313
333
|
return success;
|
package/lib/ble/base/sensor.d.ts
CHANGED
|
@@ -12,10 +12,12 @@ export declare class TBleSensor extends EventEmitter implements IBleSensor {
|
|
|
12
12
|
constructor(peripheral: IBlePeripheral, props?: {
|
|
13
13
|
logger?: EventLogger;
|
|
14
14
|
});
|
|
15
|
+
getPeripheral(): IBlePeripheral;
|
|
15
16
|
getDetectionPriority(): number;
|
|
16
17
|
getProfile(): LegacyProfile;
|
|
17
18
|
getProtocol(): BleProtocol;
|
|
18
19
|
getServiceUUids(): string[];
|
|
20
|
+
getSupportedServiceUUids(): string[];
|
|
19
21
|
isMatching(serviceUUIDs: string[]): boolean;
|
|
20
22
|
hasPeripheral(): boolean;
|
|
21
23
|
pair(): Promise<boolean>;
|
package/lib/ble/base/sensor.js
CHANGED
|
@@ -28,6 +28,9 @@ class TBleSensor extends events_1.default {
|
|
|
28
28
|
this.reset();
|
|
29
29
|
this.onDataHandler = this.onData.bind(this);
|
|
30
30
|
}
|
|
31
|
+
getPeripheral() {
|
|
32
|
+
return this.peripheral;
|
|
33
|
+
}
|
|
31
34
|
getDetectionPriority() {
|
|
32
35
|
var _a;
|
|
33
36
|
const C = this.constructor;
|
|
@@ -45,6 +48,10 @@ class TBleSensor extends events_1.default {
|
|
|
45
48
|
const C = this.constructor;
|
|
46
49
|
return C['services'];
|
|
47
50
|
}
|
|
51
|
+
getSupportedServiceUUids() {
|
|
52
|
+
var _a;
|
|
53
|
+
return (_a = this.peripheral) === null || _a === void 0 ? void 0 : _a.getDiscoveredServices();
|
|
54
|
+
}
|
|
48
55
|
isMatching(serviceUUIDs) {
|
|
49
56
|
const uuids = serviceUUIDs.map(uuid => (0, utils_2.beautifyUUID)(uuid));
|
|
50
57
|
const required = this.getServiceUUids();
|
package/lib/ble/fm/adapter.d.ts
CHANGED
|
@@ -1,30 +1,36 @@
|
|
|
1
1
|
import BleFitnessMachineDevice from './sensor';
|
|
2
2
|
import BleAdapter from '../base/adapter';
|
|
3
|
-
import ICyclingMode, { CyclingMode } from '../../modes/types';
|
|
3
|
+
import ICyclingMode, { CyclingMode, UpdateRequest } from '../../modes/types';
|
|
4
4
|
import { IndoorBikeData, IndoorBikeFeatures } from './types';
|
|
5
5
|
import { BleDeviceProperties, BleDeviceSettings, BleStartProperties, IBlePeripheral } from '../types';
|
|
6
6
|
import { IAdapter, IncyclistAdapterData, IncyclistBikeData } from '../../types';
|
|
7
7
|
import { LegacyProfile } from '../../antv2/types';
|
|
8
|
+
import { BleZwiftPlaySensor } from '../zwift/play';
|
|
8
9
|
export default class BleFmAdapter extends BleAdapter<IndoorBikeData, BleFitnessMachineDevice> {
|
|
9
10
|
protected static INCYCLIST_PROFILE_NAME: LegacyProfile;
|
|
10
11
|
protected distanceInternal: number;
|
|
11
12
|
protected connectPromise: Promise<boolean>;
|
|
12
13
|
protected requestControlRetryDelay: number;
|
|
14
|
+
protected promiseSendUpdate: Promise<UpdateRequest | void>;
|
|
15
|
+
protected zwiftPlay: BleZwiftPlaySensor;
|
|
16
|
+
protected virtualShiftingSupported: undefined;
|
|
13
17
|
constructor(settings: BleDeviceSettings, props?: BleDeviceProperties);
|
|
14
18
|
updateSensor(peripheral: IBlePeripheral): void;
|
|
15
19
|
isSame(device: IAdapter): boolean;
|
|
16
20
|
isControllable(): boolean;
|
|
21
|
+
supportsVirtualShifting(): boolean;
|
|
17
22
|
getSupportedCyclingModes(): Array<typeof CyclingMode>;
|
|
18
23
|
getDefaultCyclingMode(): ICyclingMode;
|
|
19
24
|
mapData(deviceData: IndoorBikeData): IncyclistBikeData;
|
|
20
25
|
transformData(bikeData: IncyclistBikeData): IncyclistAdapterData;
|
|
21
26
|
protected checkResume(): boolean[];
|
|
27
|
+
protected initVirtualShifting(): Promise<void>;
|
|
22
28
|
protected initControl(_startProps?: BleStartProperties): Promise<void>;
|
|
23
29
|
protected setConstants(): void;
|
|
24
30
|
protected establishControl(): Promise<boolean>;
|
|
25
31
|
protected sendInitialRequest(): Promise<void>;
|
|
26
32
|
protected checkCapabilities(): Promise<void>;
|
|
27
33
|
protected updateCapabilitiesFromFeatures(features: IndoorBikeFeatures): void;
|
|
28
|
-
sendUpdate(request: any, enforced?: boolean): Promise<void>;
|
|
34
|
+
sendUpdate(request: any, enforced?: boolean): Promise<UpdateRequest | void>;
|
|
29
35
|
sendInitCommands(): Promise<boolean>;
|
|
30
36
|
}
|
package/lib/ble/fm/adapter.js
CHANGED
|
@@ -21,6 +21,9 @@ const adapter_1 = __importDefault(require("../base/adapter"));
|
|
|
21
21
|
const consts_1 = require("./consts");
|
|
22
22
|
const types_1 = require("../../types");
|
|
23
23
|
const utils_1 = require("../../utils/utils");
|
|
24
|
+
const utils_2 = require("../utils");
|
|
25
|
+
const play_1 = require("../zwift/play");
|
|
26
|
+
const ZWIFT_PLAY_UUID = '0000000119ca465186e5fa29dcdd09d1';
|
|
24
27
|
class BleFmAdapter extends adapter_1.default {
|
|
25
28
|
constructor(settings, props) {
|
|
26
29
|
super(settings, props);
|
|
@@ -44,6 +47,10 @@ class BleFmAdapter extends adapter_1.default {
|
|
|
44
47
|
isControllable() {
|
|
45
48
|
return true;
|
|
46
49
|
}
|
|
50
|
+
supportsVirtualShifting() {
|
|
51
|
+
var _a, _b;
|
|
52
|
+
return (_b = (_a = this.device) === null || _a === void 0 ? void 0 : _a.getSupportedServiceUUids()) === null || _b === void 0 ? void 0 : _b.some(s => (0, utils_2.matches)(s, ZWIFT_PLAY_UUID));
|
|
53
|
+
}
|
|
47
54
|
getSupportedCyclingModes() {
|
|
48
55
|
var _a, _b;
|
|
49
56
|
const modes = [power_meter_1.default];
|
|
@@ -121,6 +128,18 @@ class BleFmAdapter extends adapter_1.default {
|
|
|
121
128
|
return [wasPaused, true];
|
|
122
129
|
return [wasPaused, false];
|
|
123
130
|
}
|
|
131
|
+
initVirtualShifting() {
|
|
132
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
133
|
+
var _a;
|
|
134
|
+
try {
|
|
135
|
+
this.zwiftPlay = (_a = this.zwiftPlay) !== null && _a !== void 0 ? _a : new play_1.BleZwiftPlaySensor(this.device);
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
this.logEvent({ message: 'could not init virtual shifting', reason: err.message });
|
|
139
|
+
delete this.zwiftPlay;
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
124
143
|
initControl(_startProps) {
|
|
125
144
|
return __awaiter(this, void 0, void 0, function* () {
|
|
126
145
|
if (!this.isStarting())
|
|
@@ -163,6 +182,9 @@ class BleFmAdapter extends adapter_1.default {
|
|
|
163
182
|
resolve(false);
|
|
164
183
|
});
|
|
165
184
|
const waitUntilControl = () => __awaiter(this, void 0, void 0, function* () {
|
|
185
|
+
if (this.supportsVirtualShifting()) {
|
|
186
|
+
yield this.initVirtualShifting();
|
|
187
|
+
}
|
|
166
188
|
while (!hasControl && this.isStarting()) {
|
|
167
189
|
if (tryCnt++ === 0) {
|
|
168
190
|
this.logEvent({ message: 'requesting control', device: this.getName(), interface: this.getInterface() });
|
|
@@ -233,6 +255,10 @@ class BleFmAdapter extends adapter_1.default {
|
|
|
233
255
|
sendUpdate(request_1) {
|
|
234
256
|
return __awaiter(this, arguments, void 0, function* (request, enforced = false) {
|
|
235
257
|
var _a;
|
|
258
|
+
if (this.promiseSendUpdate !== undefined) {
|
|
259
|
+
yield this.promiseSendUpdate;
|
|
260
|
+
this.promiseSendUpdate = undefined;
|
|
261
|
+
}
|
|
236
262
|
if (!enforced && (this.paused || !this.device))
|
|
237
263
|
return;
|
|
238
264
|
if (!enforced && (this.stopped && !this.isStarting()))
|
|
@@ -242,16 +268,37 @@ class BleFmAdapter extends adapter_1.default {
|
|
|
242
268
|
this.logEvent({ message: 'send bike update requested', profile: this.getProfile(), mode: (_a = this.getCyclingMode()) === null || _a === void 0 ? void 0 : _a.getName(), update, request });
|
|
243
269
|
const device = this.getSensor();
|
|
244
270
|
if (this.hasCapability(types_1.IncyclistCapability.Control)) {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
271
|
+
const send = () => __awaiter(this, void 0, void 0, function* () {
|
|
272
|
+
const res = {};
|
|
273
|
+
if (update.slope !== undefined) {
|
|
274
|
+
yield device.setSlope(update.slope);
|
|
275
|
+
res.slope = update.slope;
|
|
276
|
+
}
|
|
277
|
+
if (update.targetPower !== undefined) {
|
|
278
|
+
const tp = update.targetPower > 0 ? update.targetPower : 0;
|
|
279
|
+
yield device.setTargetPower(tp);
|
|
280
|
+
res.targetPower = tp;
|
|
281
|
+
}
|
|
282
|
+
if (update.gearRatio !== undefined) {
|
|
283
|
+
if (!this.zwiftPlay) {
|
|
284
|
+
this.initVirtualShifting();
|
|
285
|
+
}
|
|
286
|
+
if (this.zwiftPlay) {
|
|
287
|
+
const gearRatio = yield this.zwiftPlay.setGearRatio(update.gearRatio);
|
|
288
|
+
yield this.zwiftPlay.setSimulationData({ inclineX100: update.slope * 100 });
|
|
289
|
+
res.gearRatio = gearRatio;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return res;
|
|
293
|
+
});
|
|
294
|
+
this.promiseSendUpdate = send();
|
|
295
|
+
const confirmed = yield this.promiseSendUpdate;
|
|
296
|
+
delete this.promiseSendUpdate;
|
|
297
|
+
return confirmed;
|
|
252
298
|
}
|
|
253
299
|
}
|
|
254
300
|
catch (err) {
|
|
301
|
+
delete this.promiseSendUpdate;
|
|
255
302
|
if (err.message === 'not connected') {
|
|
256
303
|
this.logEvent({ message: 'send bike update failed', reason: 'not connected' });
|
|
257
304
|
}
|
|
@@ -263,12 +310,17 @@ class BleFmAdapter extends adapter_1.default {
|
|
|
263
310
|
return __awaiter(this, void 0, void 0, function* () {
|
|
264
311
|
if (this.started && !this.stopped) {
|
|
265
312
|
try {
|
|
266
|
-
|
|
313
|
+
const mode = this.getCyclingMode();
|
|
314
|
+
if (mode.isERG()) {
|
|
267
315
|
const power = this.data.power;
|
|
268
316
|
const request = power ? { targetPower: power } : this.getCyclingMode().getBikeInitRequest();
|
|
269
317
|
yield this.sendUpdate(request, true);
|
|
270
318
|
return true;
|
|
271
319
|
}
|
|
320
|
+
else if (mode.isSIM() && this.supportsVirtualShifting()) {
|
|
321
|
+
yield this.sendInitialRequest();
|
|
322
|
+
return true;
|
|
323
|
+
}
|
|
272
324
|
}
|
|
273
325
|
catch (_a) {
|
|
274
326
|
return false;
|
package/lib/ble/fm/sensor.js
CHANGED
|
@@ -13,6 +13,8 @@ const consts_1 = require("../consts");
|
|
|
13
13
|
const sensor_1 = require("../base/sensor");
|
|
14
14
|
const utils_1 = require("../utils");
|
|
15
15
|
const consts_2 = require("./consts");
|
|
16
|
+
const task_1 = require("../../utils/task");
|
|
17
|
+
const BLE_COMMAND_TIMEOUT = 800;
|
|
16
18
|
class BleFitnessMachineDevice extends sensor_1.TBleSensor {
|
|
17
19
|
constructor(peripheral, props) {
|
|
18
20
|
super(peripheral, props);
|
|
@@ -145,70 +147,72 @@ class BleFitnessMachineDevice extends sensor_1.TBleSensor {
|
|
|
145
147
|
parseIndoorBikeData(_data) {
|
|
146
148
|
const data = Buffer.from(_data);
|
|
147
149
|
let offset = 2;
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
150
|
+
if (data.length > 2) {
|
|
151
|
+
try {
|
|
152
|
+
const flags = data.readUInt16LE(0);
|
|
153
|
+
if ((flags & consts_2.IndoorBikeDataFlag.MoreData) === 0) {
|
|
154
|
+
this.data.speed = data.readUInt16LE(offset) / 100;
|
|
155
|
+
offset += 2;
|
|
156
|
+
}
|
|
157
|
+
if (flags & consts_2.IndoorBikeDataFlag.AverageSpeedPresent) {
|
|
158
|
+
this.data.averageSpeed = data.readUInt16LE(offset) / 100;
|
|
159
|
+
offset += 2;
|
|
160
|
+
}
|
|
161
|
+
if (flags & consts_2.IndoorBikeDataFlag.InstantaneousCadence) {
|
|
162
|
+
this.data.cadence = data.readUInt16LE(offset) / 2;
|
|
163
|
+
offset += 2;
|
|
164
|
+
}
|
|
165
|
+
if (flags & consts_2.IndoorBikeDataFlag.AverageCadencePresent) {
|
|
166
|
+
this.data.averageCadence = data.readUInt16LE(offset) / 2;
|
|
167
|
+
offset += 2;
|
|
168
|
+
}
|
|
169
|
+
if (flags & consts_2.IndoorBikeDataFlag.TotalDistancePresent) {
|
|
170
|
+
const dvLow = data.readUInt8(offset);
|
|
171
|
+
offset += 1;
|
|
172
|
+
const dvHigh = data.readUInt16LE(offset);
|
|
173
|
+
offset += 2;
|
|
174
|
+
this.data.totalDistance = (dvHigh << 8) + dvLow;
|
|
175
|
+
}
|
|
176
|
+
if (flags & consts_2.IndoorBikeDataFlag.ResistanceLevelPresent) {
|
|
177
|
+
this.data.resistanceLevel = data.readInt16LE(offset);
|
|
178
|
+
offset += 2;
|
|
179
|
+
}
|
|
180
|
+
if (flags & consts_2.IndoorBikeDataFlag.InstantaneousPowerPresent) {
|
|
181
|
+
this.data.instantaneousPower = data.readInt16LE(offset);
|
|
182
|
+
offset += 2;
|
|
183
|
+
}
|
|
184
|
+
if (flags & consts_2.IndoorBikeDataFlag.AveragePowerPresent) {
|
|
185
|
+
this.data.averagePower = data.readInt16LE(offset);
|
|
186
|
+
offset += 2;
|
|
187
|
+
}
|
|
188
|
+
if (flags & consts_2.IndoorBikeDataFlag.ExpendedEnergyPresent) {
|
|
189
|
+
this.data.totalEnergy = data.readUInt16LE(offset);
|
|
190
|
+
offset += 2;
|
|
191
|
+
this.data.energyPerHour = data.readUInt16LE(offset);
|
|
192
|
+
offset += 2;
|
|
193
|
+
this.data.energyPerMinute = data.readUInt8(offset);
|
|
194
|
+
offset += 1;
|
|
195
|
+
}
|
|
196
|
+
if (flags & consts_2.IndoorBikeDataFlag.HeartRatePresent) {
|
|
197
|
+
this.data.heartrate = data.readUInt8(offset);
|
|
198
|
+
offset += 1;
|
|
199
|
+
}
|
|
200
|
+
if (flags & consts_2.IndoorBikeDataFlag.MetabolicEquivalentPresent) {
|
|
201
|
+
this.data.metabolicEquivalent = data.readUInt8(offset) / 10;
|
|
202
|
+
offset += 1;
|
|
203
|
+
}
|
|
204
|
+
if (flags & consts_2.IndoorBikeDataFlag.ElapsedTimePresent) {
|
|
205
|
+
this.data.time = data.readUInt16LE(offset);
|
|
206
|
+
offset += 2;
|
|
207
|
+
}
|
|
208
|
+
if (flags & consts_2.IndoorBikeDataFlag.RemainingTimePresent) {
|
|
209
|
+
this.data.remainingTime = data.readUInt16LE(offset);
|
|
210
|
+
}
|
|
204
211
|
}
|
|
205
|
-
|
|
206
|
-
this.
|
|
212
|
+
catch (err) {
|
|
213
|
+
this.logEvent({ message: 'error', fn: 'parseIndoorBikeData()', data: data.toString('hex'), offset, error: err.message, stack: err.stack });
|
|
207
214
|
}
|
|
208
215
|
}
|
|
209
|
-
catch (err) {
|
|
210
|
-
this.logEvent({ message: 'error', fn: 'parseIndoorBikeData()', data: data.toString('hex'), offset, error: err.message, stack: err.stack });
|
|
211
|
-
}
|
|
212
216
|
return Object.assign(Object.assign({}, this.data), { raw: `2ad2:${data.toString('hex')}` });
|
|
213
217
|
}
|
|
214
218
|
parseFitnessMachineStatus(_data) {
|
|
@@ -276,7 +280,7 @@ class BleFitnessMachineDevice extends sensor_1.TBleSensor {
|
|
|
276
280
|
const services = ((_a = this.peripheral) === null || _a === void 0 ? void 0 : _a.services) || [];
|
|
277
281
|
let power = services.some(s => (0, utils_1.matches)(s.uuid, '1818'));
|
|
278
282
|
let heartrate = services.some(s => (0, utils_1.matches)(s.uuid, '180d'));
|
|
279
|
-
if (buffer) {
|
|
283
|
+
if ((buffer === null || buffer === void 0 ? void 0 : buffer.length) >= 8) {
|
|
280
284
|
const fitnessMachine = buffer.readUInt32LE(0);
|
|
281
285
|
const targetSettings = buffer.readUInt32LE(4);
|
|
282
286
|
power = power || (fitnessMachine & consts_2.FitnessMachineFeatureFlag.PowerMeasurementSupported) !== 0;
|
|
@@ -287,6 +291,10 @@ class BleFitnessMachineDevice extends sensor_1.TBleSensor {
|
|
|
287
291
|
const setPower = (targetSettings & consts_2.TargetSettingFeatureFlag.PowerTargetSettingSupported) !== 0;
|
|
288
292
|
this._features = { fitnessMachine, targetSettings, power, heartrate, cadence, setPower, setSlope };
|
|
289
293
|
this.logEvent({ message: 'supported Features: ', fatures: this._features, power, heartrate, cadence });
|
|
294
|
+
return this._features;
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
return { fitnessMachine: undefined, targetSettings: undefined, power, heartrate };
|
|
290
298
|
}
|
|
291
299
|
}
|
|
292
300
|
catch (err) {
|
|
@@ -299,14 +307,25 @@ class BleFitnessMachineDevice extends sensor_1.TBleSensor {
|
|
|
299
307
|
return __awaiter(this, void 0, void 0, function* () {
|
|
300
308
|
try {
|
|
301
309
|
this.logEvent({ message: 'fmts:write', data: data.toString('hex') });
|
|
302
|
-
|
|
310
|
+
let res;
|
|
311
|
+
let tsStart = Date.now();
|
|
312
|
+
if (props === null || props === void 0 ? void 0 : props.timeout) {
|
|
313
|
+
res = yield new task_1.InteruptableTask(this.write(consts_1.FTMS_CP, data, props), {
|
|
314
|
+
timeout: 800,
|
|
315
|
+
errorOnTimeout: true
|
|
316
|
+
}).run();
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
res = yield this.write(consts_1.FTMS_CP, data, props);
|
|
320
|
+
}
|
|
303
321
|
const responseData = Buffer.from(res);
|
|
304
322
|
const opCode = responseData.readUInt8(0);
|
|
305
323
|
const request = responseData.readUInt8(1);
|
|
306
324
|
const result = responseData.readUInt8(2);
|
|
307
325
|
if (opCode !== 128 || request !== requestedOpCode)
|
|
308
326
|
throw new Error('Illegal response ');
|
|
309
|
-
|
|
327
|
+
const duration = Date.now() - tsStart;
|
|
328
|
+
this.logEvent({ message: 'fmts:write result', res: responseData.toString('hex'), result, duration });
|
|
310
329
|
return result;
|
|
311
330
|
}
|
|
312
331
|
catch (err) {
|
|
@@ -330,7 +349,7 @@ class BleFitnessMachineDevice extends sensor_1.TBleSensor {
|
|
|
330
349
|
const data = Buffer.alloc(3);
|
|
331
350
|
data.writeUInt8(3, 0);
|
|
332
351
|
data.writeInt16LE(Math.round(inclination * 10), 1);
|
|
333
|
-
const res = yield this.writeFtmsMessage(3, data);
|
|
352
|
+
const res = yield this.writeFtmsMessage(3, data, { timeout: BLE_COMMAND_TIMEOUT });
|
|
334
353
|
return (res === 1);
|
|
335
354
|
});
|
|
336
355
|
}
|
|
@@ -350,7 +369,7 @@ class BleFitnessMachineDevice extends sensor_1.TBleSensor {
|
|
|
350
369
|
data.writeInt16LE(Math.round(gradient * 100), 3);
|
|
351
370
|
data.writeUInt8(Math.round(crr * 10000), 5);
|
|
352
371
|
data.writeUInt8(Math.round(cw * 100), 6);
|
|
353
|
-
const res = yield this.writeFtmsMessage(17, data);
|
|
372
|
+
const res = yield this.writeFtmsMessage(17, data, { timeout: BLE_COMMAND_TIMEOUT });
|
|
354
373
|
if (res === 5) {
|
|
355
374
|
this.hasControl = false;
|
|
356
375
|
}
|
package/lib/ble/types.d.ts
CHANGED
|
@@ -160,6 +160,8 @@ export interface IBlePeripheral {
|
|
|
160
160
|
write(characteristicUUID: string, data: Buffer, options?: BleWriteProps): Promise<Buffer>;
|
|
161
161
|
getManufacturerData?(): Buffer;
|
|
162
162
|
getInfo(): BleDeviceIdentifier;
|
|
163
|
+
getAnnouncedServices(): string[];
|
|
164
|
+
getDiscoveredServices(): string[];
|
|
163
165
|
}
|
|
164
166
|
export interface IBleSensor extends EventEmitter {
|
|
165
167
|
startSensor(): Promise<boolean>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HubHelperFactory = void 0;
|
|
4
|
+
const protohelper_1 = require("./protohelper");
|
|
5
|
+
class HubHelperFactory {
|
|
6
|
+
constructor() { }
|
|
7
|
+
static getInstance() {
|
|
8
|
+
if (!HubHelperFactory.instance) {
|
|
9
|
+
HubHelperFactory.instance = new HubHelperFactory();
|
|
10
|
+
}
|
|
11
|
+
return HubHelperFactory.instance;
|
|
12
|
+
}
|
|
13
|
+
create() {
|
|
14
|
+
return new protohelper_1.HubProtoHelper();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.HubHelperFactory = HubHelperFactory;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HubProtoHelper = void 0;
|
|
4
|
+
const zwift_hub_pb_js_1 = require("../../../proto/org/cagnulen/qdomyoszwift/zwift_hub_pb.js");
|
|
5
|
+
const protobuf_1 = require("@bufbuild/protobuf");
|
|
6
|
+
class HubProtoHelper {
|
|
7
|
+
createHubRequest(request) {
|
|
8
|
+
return Buffer.from((0, protobuf_1.toBinary)(zwift_hub_pb_js_1.HubRequestSchema, (0, protobuf_1.fromJson)(zwift_hub_pb_js_1.HubRequestSchema, request)));
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
exports.HubProtoHelper = HubProtoHelper;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { LegacyProfile } from "../../../antv2/types";
|
|
2
|
+
import { HubCommand, HubRequest, SimulationParam } from "../../../proto/zwift_hub";
|
|
2
3
|
import { TBleSensor } from "../../base/sensor";
|
|
3
|
-
import { BleProtocol } from "../../types";
|
|
4
|
+
import { BleProtocol, IBlePeripheral } from "../../types";
|
|
4
5
|
import { EventEmitter } from "events";
|
|
5
6
|
type ButtonState = {
|
|
6
7
|
pressed: boolean;
|
|
@@ -23,15 +24,29 @@ export declare class BleZwiftPlaySensor extends TBleSensor {
|
|
|
23
24
|
protected deviceType: DeviceType;
|
|
24
25
|
protected publicKey: Buffer;
|
|
25
26
|
protected privateKey: Buffer;
|
|
26
|
-
|
|
27
|
+
protected isFM: boolean;
|
|
28
|
+
protected tsLastRidingData: number;
|
|
29
|
+
protected isHubServiceActive: boolean;
|
|
30
|
+
protected isPaired: boolean;
|
|
31
|
+
protected isSubscribed: boolean;
|
|
32
|
+
constructor(peripheral: IBlePeripheral | TBleSensor, props?: any);
|
|
27
33
|
reconnectSensor(): Promise<void>;
|
|
28
34
|
stopSensor(): Promise<boolean>;
|
|
29
35
|
protected getRequiredCharacteristics(): Array<string>;
|
|
30
36
|
onData(characteristic: string, data: Buffer, isNotify?: boolean): boolean;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
requestDataUpdate(dataId: number): Promise<void>;
|
|
38
|
+
setSimulationData(data?: SimulationParam): Promise<void>;
|
|
39
|
+
setGearRatio(gearRatio: number): Promise<number>;
|
|
40
|
+
protected sendPlayCommand(id: number, command: Buffer): Promise<Buffer<ArrayBufferLike>>;
|
|
41
|
+
protected onMeasurement(d: Buffer): boolean;
|
|
42
|
+
initHubService(setSimulation?: boolean): Promise<boolean>;
|
|
43
|
+
sendHubRequest(request: HubRequest): Promise<Buffer<ArrayBufferLike>>;
|
|
44
|
+
sendHubCommand(command: HubCommand): Promise<Buffer<ArrayBufferLike>>;
|
|
45
|
+
protected onRidingData(m: Buffer): void;
|
|
46
|
+
protected onDeviceInformation(m: Buffer): void;
|
|
47
|
+
onClickButtonMessage(d: Buffer): void;
|
|
48
|
+
onPingMessage(message: Buffer): void;
|
|
49
|
+
onResponse(d: Buffer): boolean;
|
|
35
50
|
read(characteristic: string, ignoreErrors?: boolean): Promise<Buffer | null>;
|
|
36
51
|
pair(): Promise<boolean>;
|
|
37
52
|
reset(): void;
|