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.
@@ -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;
@@ -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();
@@ -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
  }
@@ -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
- if (update.slope !== undefined) {
246
- yield device.setSlope(update.slope);
247
- }
248
- if (update.targetPower !== undefined) {
249
- const tp = update.targetPower > 0 ? update.targetPower : 0;
250
- yield device.setTargetPower(tp);
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
- if (this.getCyclingMode() instanceof antble_erg_1.default) {
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;
@@ -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
- try {
149
- const flags = data.readUInt16LE(0);
150
- if ((flags & consts_2.IndoorBikeDataFlag.MoreData) === 0) {
151
- this.data.speed = data.readUInt16LE(offset) / 100;
152
- offset += 2;
153
- }
154
- if (flags & consts_2.IndoorBikeDataFlag.AverageSpeedPresent) {
155
- this.data.averageSpeed = data.readUInt16LE(offset) / 100;
156
- offset += 2;
157
- }
158
- if (flags & consts_2.IndoorBikeDataFlag.InstantaneousCadence) {
159
- this.data.cadence = data.readUInt16LE(offset) / 2;
160
- offset += 2;
161
- }
162
- if (flags & consts_2.IndoorBikeDataFlag.AverageCadencePresent) {
163
- this.data.averageCadence = data.readUInt16LE(offset) / 2;
164
- offset += 2;
165
- }
166
- if (flags & consts_2.IndoorBikeDataFlag.TotalDistancePresent) {
167
- const dvLow = data.readUInt8(offset);
168
- offset += 1;
169
- const dvHigh = data.readUInt16LE(offset);
170
- offset += 2;
171
- this.data.totalDistance = (dvHigh << 8) + dvLow;
172
- }
173
- if (flags & consts_2.IndoorBikeDataFlag.ResistanceLevelPresent) {
174
- this.data.resistanceLevel = data.readInt16LE(offset);
175
- offset += 2;
176
- }
177
- if (flags & consts_2.IndoorBikeDataFlag.InstantaneousPowerPresent) {
178
- this.data.instantaneousPower = data.readInt16LE(offset);
179
- offset += 2;
180
- }
181
- if (flags & consts_2.IndoorBikeDataFlag.AveragePowerPresent) {
182
- this.data.averagePower = data.readInt16LE(offset);
183
- offset += 2;
184
- }
185
- if (flags & consts_2.IndoorBikeDataFlag.ExpendedEnergyPresent) {
186
- this.data.totalEnergy = data.readUInt16LE(offset);
187
- offset += 2;
188
- this.data.energyPerHour = data.readUInt16LE(offset);
189
- offset += 2;
190
- this.data.energyPerMinute = data.readUInt8(offset);
191
- offset += 1;
192
- }
193
- if (flags & consts_2.IndoorBikeDataFlag.HeartRatePresent) {
194
- this.data.heartrate = data.readUInt8(offset);
195
- offset += 1;
196
- }
197
- if (flags & consts_2.IndoorBikeDataFlag.MetabolicEquivalentPresent) {
198
- this.data.metabolicEquivalent = data.readUInt8(offset) / 10;
199
- offset += 1;
200
- }
201
- if (flags & consts_2.IndoorBikeDataFlag.ElapsedTimePresent) {
202
- this.data.time = data.readUInt16LE(offset);
203
- 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
+ }
204
211
  }
205
- if (flags & consts_2.IndoorBikeDataFlag.RemainingTimePresent) {
206
- 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 });
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
- const res = yield this.write(consts_1.FTMS_CP, data, props);
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
- this.logEvent({ message: 'fmts:write result', res, result });
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
  }
@@ -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;