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.
@@ -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;
@@ -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>;
@@ -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();
@@ -5,21 +5,26 @@ 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;
13
14
  protected promiseSendUpdate: Promise<UpdateRequest | void>;
15
+ protected zwiftPlay: BleZwiftPlaySensor;
16
+ protected virtualShiftingSupported: undefined;
14
17
  constructor(settings: BleDeviceSettings, props?: BleDeviceProperties);
15
18
  updateSensor(peripheral: IBlePeripheral): void;
16
19
  isSame(device: IAdapter): boolean;
17
20
  isControllable(): boolean;
21
+ supportsVirtualShifting(): boolean;
18
22
  getSupportedCyclingModes(): Array<typeof CyclingMode>;
19
23
  getDefaultCyclingMode(): ICyclingMode;
20
24
  mapData(deviceData: IndoorBikeData): IncyclistBikeData;
21
25
  transformData(bikeData: IncyclistBikeData): IncyclistAdapterData;
22
26
  protected checkResume(): boolean[];
27
+ protected initVirtualShifting(): Promise<void>;
23
28
  protected initControl(_startProps?: BleStartProperties): Promise<void>;
24
29
  protected setConstants(): void;
25
30
  protected establishControl(): Promise<boolean>;
@@ -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() });
@@ -257,6 +279,16 @@ class BleFmAdapter extends adapter_1.default {
257
279
  yield device.setTargetPower(tp);
258
280
  res.targetPower = tp;
259
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
+ }
260
292
  return res;
261
293
  });
262
294
  this.promiseSendUpdate = send();
@@ -278,12 +310,17 @@ class BleFmAdapter extends adapter_1.default {
278
310
  return __awaiter(this, void 0, void 0, function* () {
279
311
  if (this.started && !this.stopped) {
280
312
  try {
281
- if (this.getCyclingMode() instanceof antble_erg_1.default) {
313
+ const mode = this.getCyclingMode();
314
+ if (mode.isERG()) {
282
315
  const power = this.data.power;
283
316
  const request = power ? { targetPower: power } : this.getCyclingMode().getBikeInitRequest();
284
317
  yield this.sendUpdate(request, true);
285
318
  return true;
286
319
  }
320
+ else if (mode.isSIM() && this.supportsVirtualShifting()) {
321
+ yield this.sendInitialRequest();
322
+ return true;
323
+ }
287
324
  }
288
325
  catch (_a) {
289
326
  return false;
@@ -147,70 +147,72 @@ class BleFitnessMachineDevice extends sensor_1.TBleSensor {
147
147
  parseIndoorBikeData(_data) {
148
148
  const data = Buffer.from(_data);
149
149
  let offset = 2;
150
- try {
151
- const flags = data.readUInt16LE(0);
152
- if ((flags & consts_2.IndoorBikeDataFlag.MoreData) === 0) {
153
- this.data.speed = data.readUInt16LE(offset) / 100;
154
- offset += 2;
155
- }
156
- if (flags & consts_2.IndoorBikeDataFlag.AverageSpeedPresent) {
157
- this.data.averageSpeed = data.readUInt16LE(offset) / 100;
158
- offset += 2;
159
- }
160
- if (flags & consts_2.IndoorBikeDataFlag.InstantaneousCadence) {
161
- this.data.cadence = data.readUInt16LE(offset) / 2;
162
- offset += 2;
163
- }
164
- if (flags & consts_2.IndoorBikeDataFlag.AverageCadencePresent) {
165
- this.data.averageCadence = data.readUInt16LE(offset) / 2;
166
- offset += 2;
167
- }
168
- if (flags & consts_2.IndoorBikeDataFlag.TotalDistancePresent) {
169
- const dvLow = data.readUInt8(offset);
170
- offset += 1;
171
- const dvHigh = data.readUInt16LE(offset);
172
- offset += 2;
173
- this.data.totalDistance = (dvHigh << 8) + dvLow;
174
- }
175
- if (flags & consts_2.IndoorBikeDataFlag.ResistanceLevelPresent) {
176
- this.data.resistanceLevel = data.readInt16LE(offset);
177
- offset += 2;
178
- }
179
- if (flags & consts_2.IndoorBikeDataFlag.InstantaneousPowerPresent) {
180
- this.data.instantaneousPower = data.readInt16LE(offset);
181
- offset += 2;
182
- }
183
- if (flags & consts_2.IndoorBikeDataFlag.AveragePowerPresent) {
184
- this.data.averagePower = data.readInt16LE(offset);
185
- offset += 2;
186
- }
187
- if (flags & consts_2.IndoorBikeDataFlag.ExpendedEnergyPresent) {
188
- this.data.totalEnergy = data.readUInt16LE(offset);
189
- offset += 2;
190
- this.data.energyPerHour = data.readUInt16LE(offset);
191
- offset += 2;
192
- this.data.energyPerMinute = data.readUInt8(offset);
193
- offset += 1;
194
- }
195
- if (flags & consts_2.IndoorBikeDataFlag.HeartRatePresent) {
196
- this.data.heartrate = data.readUInt8(offset);
197
- offset += 1;
198
- }
199
- if (flags & consts_2.IndoorBikeDataFlag.MetabolicEquivalentPresent) {
200
- this.data.metabolicEquivalent = data.readUInt8(offset) / 10;
201
- offset += 1;
202
- }
203
- if (flags & consts_2.IndoorBikeDataFlag.ElapsedTimePresent) {
204
- this.data.time = data.readUInt16LE(offset);
205
- offset += 2;
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
+ }
206
211
  }
207
- if (flags & consts_2.IndoorBikeDataFlag.RemainingTimePresent) {
208
- this.data.remainingTime = data.readUInt16LE(offset);
212
+ catch (err) {
213
+ this.logEvent({ message: 'error', fn: 'parseIndoorBikeData()', data: data.toString('hex'), offset, error: err.message, stack: err.stack });
209
214
  }
210
215
  }
211
- catch (err) {
212
- this.logEvent({ message: 'error', fn: 'parseIndoorBikeData()', data: data.toString('hex'), offset, error: err.message, stack: err.stack });
213
- }
214
216
  return Object.assign(Object.assign({}, this.data), { raw: `2ad2:${data.toString('hex')}` });
215
217
  }
216
218
  parseFitnessMachineStatus(_data) {
@@ -278,7 +280,7 @@ class BleFitnessMachineDevice extends sensor_1.TBleSensor {
278
280
  const services = ((_a = this.peripheral) === null || _a === void 0 ? void 0 : _a.services) || [];
279
281
  let power = services.some(s => (0, utils_1.matches)(s.uuid, '1818'));
280
282
  let heartrate = services.some(s => (0, utils_1.matches)(s.uuid, '180d'));
281
- if (buffer) {
283
+ if ((buffer === null || buffer === void 0 ? void 0 : buffer.length) >= 8) {
282
284
  const fitnessMachine = buffer.readUInt32LE(0);
283
285
  const targetSettings = buffer.readUInt32LE(4);
284
286
  power = power || (fitnessMachine & consts_2.FitnessMachineFeatureFlag.PowerMeasurementSupported) !== 0;
@@ -289,6 +291,10 @@ class BleFitnessMachineDevice extends sensor_1.TBleSensor {
289
291
  const setPower = (targetSettings & consts_2.TargetSettingFeatureFlag.PowerTargetSettingSupported) !== 0;
290
292
  this._features = { fitnessMachine, targetSettings, power, heartrate, cadence, setPower, setSlope };
291
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 };
292
298
  }
293
299
  }
294
300
  catch (err) {
@@ -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,7 @@
1
+ import { IHubHelper } from "./types";
2
+ export declare class HubHelperFactory {
3
+ private static instance;
4
+ private constructor();
5
+ static getInstance(): HubHelperFactory;
6
+ create(): IHubHelper;
7
+ }
@@ -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,4 @@
1
+ import { HubRequest, IHubHelper } from "./types";
2
+ export declare class HubProtoHelper implements IHubHelper {
3
+ createHubRequest(request: HubRequest): Buffer<ArrayBuffer>;
4
+ }
@@ -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
- constructor(peripheral: any, props?: any);
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
- onPlayMeasurement(d: Buffer): boolean;
32
- onButtonMessage(d: Buffer): void;
33
- onPingMessage(d: Buffer): void;
34
- onPairResponse(d: Buffer): boolean;
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;