incyclist-devices 3.0.4 → 3.0.6

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.
@@ -349,9 +349,21 @@ class BleAdapter extends adpater_js_1.default {
349
349
  }
350
350
  async restart(pause) {
351
351
  const sensor = this.getSensor();
352
+ sensor.off('data', this.onDeviceDataHandler);
353
+ let connected = false;
354
+ if (sensor.isReconnectBusy()) {
355
+ connected = await sensor.reconnectSensor();
356
+ }
357
+ if (connected) {
358
+ sensor.on('data', this.onDeviceDataHandler);
359
+ return true;
360
+ }
352
361
  await sensor.getPeripheral().disconnect();
353
- const res = await super.restart(pause);
354
- return res;
362
+ const success = await super.restart(pause);
363
+ if (success) {
364
+ sensor.on('data', this.onDeviceDataHandler);
365
+ }
366
+ return success;
355
367
  }
356
368
  async stop() {
357
369
  this.logEvent({ message: 'stopping device', device: this.getName(), interface: this.getInterface() });
@@ -29,7 +29,7 @@ class BlePeripheral {
29
29
  return this.announcement.serviceUUIDs.map(s => (0, utils_js_1.beautifyUUID)(s));
30
30
  }
31
31
  getDiscoveredServices() {
32
- return this.discoveredServiceUUIds;
32
+ return this.discoveredServiceUUIds ?? [];
33
33
  }
34
34
  getInfo() {
35
35
  return {
@@ -47,13 +47,16 @@ class BlePeripheral {
47
47
  this.connectPromise = new Promise((done) => {
48
48
  const peripheral = this.getPeripheral();
49
49
  this.connected = false;
50
+ if (!peripheral?.id)
51
+ return done();
52
+ const peripheralId = peripheral.id;
50
53
  this.ble.unregisterConnected(peripheral.id);
51
54
  if (!this.ble.isConnected()) {
52
55
  return done();
53
56
  }
54
57
  this.logEvent({ message: 'connect peripheral', address: peripheral.address });
55
58
  peripheral.connectAsync().then(() => {
56
- this.ble.registerConnected(this, peripheral.id);
59
+ this.ble.registerConnected(this, peripheralId);
57
60
  peripheral.once('disconnect', () => { this.onPeripheralDisconnect(); });
58
61
  peripheral.on('error', this.onErrorHandler);
59
62
  this.connected = true;
@@ -131,14 +134,20 @@ class BlePeripheral {
131
134
  return [];
132
135
  if (this.getPeripheral().discoverServicesAsync) {
133
136
  this.logEvent({ message: 'discover services', address: this.getPeripheral().address });
134
- const services = await this.getPeripheral().discoverServicesAsync([]);
137
+ const peripheral = this.getPeripheral();
138
+ let services = [];
139
+ if (peripheral?.discoverServicesAsync) {
140
+ services = await peripheral.discoverServicesAsync([]);
141
+ }
135
142
  this.discoveredServiceUUIds = services.map(s => (0, utils_js_1.beautifyUUID)(s.uuid));
143
+ this.logEvent({ message: 'discover services result', address: this.getPeripheral().address, services: this.discoveredServiceUUIds });
136
144
  return services.map(s => s.uuid);
137
145
  }
138
146
  else {
139
147
  this.logEvent({ message: 'discover services and characteristics', address: this.getPeripheral().address });
140
148
  const res = await this.getPeripheral().discoverSomeServicesAndCharacteristicsAsync([], []);
141
149
  this.discoveredServiceUUIds = res.services.map(s => (0, utils_js_1.beautifyUUID)(s.uuid));
150
+ this.logEvent({ message: 'discover services result', address: this.getPeripheral().address, services: this.discoveredServiceUUIds });
142
151
  return res.services.map(s => s.uuid);
143
152
  }
144
153
  }
@@ -9,7 +9,9 @@ class TBleSensor extends node_events_1.EventEmitter {
9
9
  peripheral;
10
10
  static protocol;
11
11
  logger;
12
- stopRequested;
12
+ stopRequested = false;
13
+ subscribeSuccess = false;
14
+ reconnectPromise;
13
15
  onDataHandler;
14
16
  logEvent(event, ...args) {
15
17
  this.logger.logEvent(event, ...args);
@@ -82,23 +84,40 @@ class TBleSensor extends node_events_1.EventEmitter {
82
84
  async subscribe() {
83
85
  const selected = this.getRequiredCharacteristics();
84
86
  if (selected === null) {
85
- const res = await this.peripheral.subscribeAll(this.onDataHandler);
87
+ const res = this.peripheral?.subscribeAll ? await this.peripheral.subscribeAll(this.onDataHandler) : false;
88
+ this.subscribeSuccess = res;
86
89
  return res;
87
90
  }
88
91
  if (selected.length === 0) {
92
+ this.subscribeSuccess = true;
89
93
  return true;
90
94
  }
91
95
  const res = await this.peripheral.subscribeSelected(selected, this.onDataHandler);
96
+ this.subscribeSuccess = res;
92
97
  return res;
93
98
  }
94
99
  async stopSensor() {
100
+ this.onDisconnect();
95
101
  this.removeAllListeners();
96
102
  if (!this.peripheral)
97
103
  return true;
98
104
  this.stopRequested = true;
99
105
  return await this.peripheral.disconnect();
100
106
  }
107
+ isReconnectBusy() {
108
+ return (this.reconnectPromise !== undefined);
109
+ }
101
110
  async reconnectSensor() {
111
+ if (this.reconnectPromise !== undefined) {
112
+ return await this.reconnectPromise;
113
+ }
114
+ this.reconnectPromise = this.doReconnectSensor();
115
+ const res = await this.reconnectPromise;
116
+ delete this.reconnectPromise;
117
+ return res;
118
+ }
119
+ async doReconnectSensor() {
120
+ this.onDisconnect();
102
121
  this.logEvent({ message: 'reconnect sensor' });
103
122
  let connected = false;
104
123
  let subscribed = false;
@@ -115,6 +134,8 @@ class TBleSensor extends node_events_1.EventEmitter {
115
134
  await (0, utils_js_1.sleep)(1000);
116
135
  }
117
136
  } while (!success || this.stopRequested);
137
+ this.logEvent({ message: 'reconnect sensor completed', success, stopRequested: this.stopRequested });
138
+ return success;
118
139
  }
119
140
  reset() {
120
141
  throw new Error("Method not implemented.");
@@ -122,6 +143,12 @@ class TBleSensor extends node_events_1.EventEmitter {
122
143
  isConnected() {
123
144
  return this.peripheral?.isConnected();
124
145
  }
146
+ isSubscribed() {
147
+ return this.subscribeSuccess;
148
+ }
149
+ onDisconnect() {
150
+ this.subscribeSuccess = false;
151
+ }
125
152
  read(characteristicUUID) {
126
153
  if (!this.isConnected()) {
127
154
  return Promise.reject(new Error('not connected'));
@@ -22,7 +22,7 @@ class BleFmAdapter extends adapter_js_1.default {
22
22
  requestControlRetryDelay = 1000;
23
23
  promiseSendUpdate;
24
24
  zwiftPlay;
25
- virtualShiftingSupported;
25
+ isHubInitialized = false;
26
26
  constructor(settings, props) {
27
27
  super(settings, props);
28
28
  this.logger = new gd_eventlog_1.EventLogger('BLE-FM');
@@ -95,7 +95,7 @@ class BleFmAdapter extends adapter_js_1.default {
95
95
  data.power = deviceData?.instantaneousPower ?? data.power;
96
96
  data.pedalRpm = deviceData?.cadence ?? data.pedalRpm;
97
97
  data.time = deviceData?.time ?? data.time;
98
- data.isPedalling = data.pedalRpm > 0 || (data.pedalRpm === undefined && data.power > 0);
98
+ data.isPedalling = (data.pedalRpm ?? 0) > 0 || (data.pedalRpm === undefined && data.power > 0);
99
99
  data.heartrate = deviceData.heartrate || data.heartrate;
100
100
  const features = this.getSensor()?.features;
101
101
  if (features?.setResistance || features?.fmInfo?.includes('resistanceLevel')) {
@@ -134,15 +134,19 @@ class BleFmAdapter extends adapter_js_1.default {
134
134
  return [wasPaused, true];
135
135
  return [wasPaused, false];
136
136
  }
137
- async initVirtualShifting() {
138
- this.logEvent({ message: 'init virtual shifting', hasSensor: this.zwiftPlay !== undefined });
137
+ async initVirtualShifting(initHub = false) {
138
+ if (!this.zwiftPlay || initHub === true)
139
+ this.logEvent({ message: 'init virtual shifting', hasSensor: this.zwiftPlay !== undefined });
139
140
  try {
140
141
  this.zwiftPlay = this.zwiftPlay ?? new index_js_2.BleZwiftPlaySensor(this.device, { logger: this.logger, isTrainer: true });
141
- this.zwiftPlay.initHubService(false);
142
+ if (initHub && this.zwiftPlay && !this.isHubInitialized) {
143
+ this.isHubInitialized = await this.zwiftPlay.initHubService(false);
144
+ }
142
145
  }
143
146
  catch (err) {
144
147
  this.logEvent({ message: 'could not init virtual shifting', reason: err.message });
145
148
  delete this.zwiftPlay;
149
+ this.isHubInitialized = false;
146
150
  }
147
151
  }
148
152
  async initControl(_startProps) {
@@ -276,9 +280,7 @@ class BleFmAdapter extends adapter_js_1.default {
276
280
  const res = {};
277
281
  if (update.slope !== undefined) {
278
282
  if (update.isHub) {
279
- if (!this.zwiftPlay) {
280
- this.initVirtualShifting();
281
- }
283
+ await this.initVirtualShifting(true);
282
284
  if (this.zwiftPlay) {
283
285
  await this.zwiftPlay.setIncline(update.slope);
284
286
  }
@@ -298,9 +300,7 @@ class BleFmAdapter extends adapter_js_1.default {
298
300
  res.targetResistance = update.targetResistance;
299
301
  }
300
302
  if (update.gearRatio !== undefined) {
301
- if (!this.zwiftPlay) {
302
- this.initVirtualShifting();
303
- }
303
+ await this.initVirtualShifting(true);
304
304
  if (this.zwiftPlay && !Number.isNaN(update.gearRatio)) {
305
305
  const gearRatio = await this.zwiftPlay.setGearRatio(update.gearRatio);
306
306
  res.gearRatio = gearRatio;
@@ -317,7 +317,8 @@ class BleFmAdapter extends adapter_js_1.default {
317
317
  return confirmed;
318
318
  }
319
319
  }
320
- catch (err) {
320
+ catch (error) {
321
+ const err = error;
321
322
  delete this.promiseSendUpdate;
322
323
  if (err.message === 'not connected') {
323
324
  this.logEvent({ message: 'send bike update failed', reason: 'not connected' });
@@ -344,9 +345,7 @@ class BleFmAdapter extends adapter_js_1.default {
344
345
  return false;
345
346
  }
346
347
  }
347
- else {
348
- return false;
349
- }
348
+ return false;
350
349
  }
351
350
  getFeatureToggle() {
352
351
  return (0, index_js_3.useFeatureToggle)();
@@ -76,10 +76,15 @@ class BleFitnessMachineDevice extends sensor_js_1.TBleSensor {
76
76
  getCw() { return this.cw; }
77
77
  setWindSpeed(windSpeed) { this.windSpeed = windSpeed; }
78
78
  getWindSpeed() { return this.windSpeed; }
79
+ onDisconnect() {
80
+ this.hasControl = false;
81
+ }
79
82
  async requestControl() {
80
83
  if (this.hasControl) {
81
84
  return true;
82
85
  }
86
+ if (!this.isSubscribed())
87
+ return false;
83
88
  if (this.features?.setPower === false && this.features?.setSlope === false && this.features?.setResistance === false) {
84
89
  return true;
85
90
  }
@@ -98,6 +103,8 @@ class BleFitnessMachineDevice extends sensor_js_1.TBleSensor {
98
103
  return this.hasControl;
99
104
  }
100
105
  async setTargetPower(power) {
106
+ if (!this.isSubscribed())
107
+ return false;
101
108
  this.logEvent({ message: 'setTargetPower', device: this.getName(), power, skip: (this.data.targetPower !== undefined && this.data.targetPower === power) });
102
109
  if (this.data.targetPower !== undefined && this.data.targetPower === power)
103
110
  return true;
@@ -116,6 +123,8 @@ class BleFitnessMachineDevice extends sensor_js_1.TBleSensor {
116
123
  return (res === 1);
117
124
  }
118
125
  async setTargetResistanceLevel(resistanceLevel) {
126
+ if (!this.isSubscribed())
127
+ return false;
119
128
  this.logEvent({ message: 'setTargetResistanceLevel', device: this.getName(), resistanceLevel, skip: (this.data.resistanceLevel !== undefined && this.data.resistanceLevel === resistanceLevel) });
120
129
  if (this.data.resistanceLevel !== undefined && this.data.resistanceLevel === resistanceLevel)
121
130
  return true;
@@ -136,6 +145,8 @@ class BleFitnessMachineDevice extends sensor_js_1.TBleSensor {
136
145
  return (res === 1);
137
146
  }
138
147
  async setSlope(slope) {
148
+ if (!this.isSubscribed())
149
+ return false;
139
150
  this.logEvent({ message: 'setSlope', device: this.getName(), slope });
140
151
  const { windSpeed, crr, cw } = this;
141
152
  return await this.setIndoorBikeSimulation(windSpeed, slope, crr, cw);
@@ -344,9 +344,21 @@ export default class BleAdapter extends IncyclistDevice {
344
344
  }
345
345
  async restart(pause) {
346
346
  const sensor = this.getSensor();
347
+ sensor.off('data', this.onDeviceDataHandler);
348
+ let connected = false;
349
+ if (sensor.isReconnectBusy()) {
350
+ connected = await sensor.reconnectSensor();
351
+ }
352
+ if (connected) {
353
+ sensor.on('data', this.onDeviceDataHandler);
354
+ return true;
355
+ }
347
356
  await sensor.getPeripheral().disconnect();
348
- const res = await super.restart(pause);
349
- return res;
357
+ const success = await super.restart(pause);
358
+ if (success) {
359
+ sensor.on('data', this.onDeviceDataHandler);
360
+ }
361
+ return success;
350
362
  }
351
363
  async stop() {
352
364
  this.logEvent({ message: 'stopping device', device: this.getName(), interface: this.getInterface() });
@@ -26,7 +26,7 @@ export class BlePeripheral {
26
26
  return this.announcement.serviceUUIDs.map(s => beautifyUUID(s));
27
27
  }
28
28
  getDiscoveredServices() {
29
- return this.discoveredServiceUUIds;
29
+ return this.discoveredServiceUUIds ?? [];
30
30
  }
31
31
  getInfo() {
32
32
  return {
@@ -44,13 +44,16 @@ export class BlePeripheral {
44
44
  this.connectPromise = new Promise((done) => {
45
45
  const peripheral = this.getPeripheral();
46
46
  this.connected = false;
47
+ if (!peripheral?.id)
48
+ return done();
49
+ const peripheralId = peripheral.id;
47
50
  this.ble.unregisterConnected(peripheral.id);
48
51
  if (!this.ble.isConnected()) {
49
52
  return done();
50
53
  }
51
54
  this.logEvent({ message: 'connect peripheral', address: peripheral.address });
52
55
  peripheral.connectAsync().then(() => {
53
- this.ble.registerConnected(this, peripheral.id);
56
+ this.ble.registerConnected(this, peripheralId);
54
57
  peripheral.once('disconnect', () => { this.onPeripheralDisconnect(); });
55
58
  peripheral.on('error', this.onErrorHandler);
56
59
  this.connected = true;
@@ -128,14 +131,20 @@ export class BlePeripheral {
128
131
  return [];
129
132
  if (this.getPeripheral().discoverServicesAsync) {
130
133
  this.logEvent({ message: 'discover services', address: this.getPeripheral().address });
131
- const services = await this.getPeripheral().discoverServicesAsync([]);
134
+ const peripheral = this.getPeripheral();
135
+ let services = [];
136
+ if (peripheral?.discoverServicesAsync) {
137
+ services = await peripheral.discoverServicesAsync([]);
138
+ }
132
139
  this.discoveredServiceUUIds = services.map(s => beautifyUUID(s.uuid));
140
+ this.logEvent({ message: 'discover services result', address: this.getPeripheral().address, services: this.discoveredServiceUUIds });
133
141
  return services.map(s => s.uuid);
134
142
  }
135
143
  else {
136
144
  this.logEvent({ message: 'discover services and characteristics', address: this.getPeripheral().address });
137
145
  const res = await this.getPeripheral().discoverSomeServicesAndCharacteristicsAsync([], []);
138
146
  this.discoveredServiceUUIds = res.services.map(s => beautifyUUID(s.uuid));
147
+ this.logEvent({ message: 'discover services result', address: this.getPeripheral().address, services: this.discoveredServiceUUIds });
139
148
  return res.services.map(s => s.uuid);
140
149
  }
141
150
  }
@@ -6,7 +6,9 @@ export class TBleSensor extends EventEmitter {
6
6
  peripheral;
7
7
  static protocol;
8
8
  logger;
9
- stopRequested;
9
+ stopRequested = false;
10
+ subscribeSuccess = false;
11
+ reconnectPromise;
10
12
  onDataHandler;
11
13
  logEvent(event, ...args) {
12
14
  this.logger.logEvent(event, ...args);
@@ -79,23 +81,40 @@ export class TBleSensor extends EventEmitter {
79
81
  async subscribe() {
80
82
  const selected = this.getRequiredCharacteristics();
81
83
  if (selected === null) {
82
- const res = await this.peripheral.subscribeAll(this.onDataHandler);
84
+ const res = this.peripheral?.subscribeAll ? await this.peripheral.subscribeAll(this.onDataHandler) : false;
85
+ this.subscribeSuccess = res;
83
86
  return res;
84
87
  }
85
88
  if (selected.length === 0) {
89
+ this.subscribeSuccess = true;
86
90
  return true;
87
91
  }
88
92
  const res = await this.peripheral.subscribeSelected(selected, this.onDataHandler);
93
+ this.subscribeSuccess = res;
89
94
  return res;
90
95
  }
91
96
  async stopSensor() {
97
+ this.onDisconnect();
92
98
  this.removeAllListeners();
93
99
  if (!this.peripheral)
94
100
  return true;
95
101
  this.stopRequested = true;
96
102
  return await this.peripheral.disconnect();
97
103
  }
104
+ isReconnectBusy() {
105
+ return (this.reconnectPromise !== undefined);
106
+ }
98
107
  async reconnectSensor() {
108
+ if (this.reconnectPromise !== undefined) {
109
+ return await this.reconnectPromise;
110
+ }
111
+ this.reconnectPromise = this.doReconnectSensor();
112
+ const res = await this.reconnectPromise;
113
+ delete this.reconnectPromise;
114
+ return res;
115
+ }
116
+ async doReconnectSensor() {
117
+ this.onDisconnect();
99
118
  this.logEvent({ message: 'reconnect sensor' });
100
119
  let connected = false;
101
120
  let subscribed = false;
@@ -112,6 +131,8 @@ export class TBleSensor extends EventEmitter {
112
131
  await sleep(1000);
113
132
  }
114
133
  } while (!success || this.stopRequested);
134
+ this.logEvent({ message: 'reconnect sensor completed', success, stopRequested: this.stopRequested });
135
+ return success;
115
136
  }
116
137
  reset() {
117
138
  throw new Error("Method not implemented.");
@@ -119,6 +140,12 @@ export class TBleSensor extends EventEmitter {
119
140
  isConnected() {
120
141
  return this.peripheral?.isConnected();
121
142
  }
143
+ isSubscribed() {
144
+ return this.subscribeSuccess;
145
+ }
146
+ onDisconnect() {
147
+ this.subscribeSuccess = false;
148
+ }
122
149
  read(characteristicUUID) {
123
150
  if (!this.isConnected()) {
124
151
  return Promise.reject(new Error('not connected'));
@@ -17,7 +17,7 @@ export default class BleFmAdapter extends BleAdapter {
17
17
  requestControlRetryDelay = 1000;
18
18
  promiseSendUpdate;
19
19
  zwiftPlay;
20
- virtualShiftingSupported;
20
+ isHubInitialized = false;
21
21
  constructor(settings, props) {
22
22
  super(settings, props);
23
23
  this.logger = new EventLogger('BLE-FM');
@@ -90,7 +90,7 @@ export default class BleFmAdapter extends BleAdapter {
90
90
  data.power = deviceData?.instantaneousPower ?? data.power;
91
91
  data.pedalRpm = deviceData?.cadence ?? data.pedalRpm;
92
92
  data.time = deviceData?.time ?? data.time;
93
- data.isPedalling = data.pedalRpm > 0 || (data.pedalRpm === undefined && data.power > 0);
93
+ data.isPedalling = (data.pedalRpm ?? 0) > 0 || (data.pedalRpm === undefined && data.power > 0);
94
94
  data.heartrate = deviceData.heartrate || data.heartrate;
95
95
  const features = this.getSensor()?.features;
96
96
  if (features?.setResistance || features?.fmInfo?.includes('resistanceLevel')) {
@@ -129,15 +129,19 @@ export default class BleFmAdapter extends BleAdapter {
129
129
  return [wasPaused, true];
130
130
  return [wasPaused, false];
131
131
  }
132
- async initVirtualShifting() {
133
- this.logEvent({ message: 'init virtual shifting', hasSensor: this.zwiftPlay !== undefined });
132
+ async initVirtualShifting(initHub = false) {
133
+ if (!this.zwiftPlay || initHub === true)
134
+ this.logEvent({ message: 'init virtual shifting', hasSensor: this.zwiftPlay !== undefined });
134
135
  try {
135
136
  this.zwiftPlay = this.zwiftPlay ?? new BleZwiftPlaySensor(this.device, { logger: this.logger, isTrainer: true });
136
- this.zwiftPlay.initHubService(false);
137
+ if (initHub && this.zwiftPlay && !this.isHubInitialized) {
138
+ this.isHubInitialized = await this.zwiftPlay.initHubService(false);
139
+ }
137
140
  }
138
141
  catch (err) {
139
142
  this.logEvent({ message: 'could not init virtual shifting', reason: err.message });
140
143
  delete this.zwiftPlay;
144
+ this.isHubInitialized = false;
141
145
  }
142
146
  }
143
147
  async initControl(_startProps) {
@@ -271,9 +275,7 @@ export default class BleFmAdapter extends BleAdapter {
271
275
  const res = {};
272
276
  if (update.slope !== undefined) {
273
277
  if (update.isHub) {
274
- if (!this.zwiftPlay) {
275
- this.initVirtualShifting();
276
- }
278
+ await this.initVirtualShifting(true);
277
279
  if (this.zwiftPlay) {
278
280
  await this.zwiftPlay.setIncline(update.slope);
279
281
  }
@@ -293,9 +295,7 @@ export default class BleFmAdapter extends BleAdapter {
293
295
  res.targetResistance = update.targetResistance;
294
296
  }
295
297
  if (update.gearRatio !== undefined) {
296
- if (!this.zwiftPlay) {
297
- this.initVirtualShifting();
298
- }
298
+ await this.initVirtualShifting(true);
299
299
  if (this.zwiftPlay && !Number.isNaN(update.gearRatio)) {
300
300
  const gearRatio = await this.zwiftPlay.setGearRatio(update.gearRatio);
301
301
  res.gearRatio = gearRatio;
@@ -312,7 +312,8 @@ export default class BleFmAdapter extends BleAdapter {
312
312
  return confirmed;
313
313
  }
314
314
  }
315
- catch (err) {
315
+ catch (error) {
316
+ const err = error;
316
317
  delete this.promiseSendUpdate;
317
318
  if (err.message === 'not connected') {
318
319
  this.logEvent({ message: 'send bike update failed', reason: 'not connected' });
@@ -339,9 +340,7 @@ export default class BleFmAdapter extends BleAdapter {
339
340
  return false;
340
341
  }
341
342
  }
342
- else {
343
- return false;
344
- }
343
+ return false;
345
344
  }
346
345
  getFeatureToggle() {
347
346
  return useFeatureToggle();
@@ -74,10 +74,15 @@ export default class BleFitnessMachineDevice extends TBleSensor {
74
74
  getCw() { return this.cw; }
75
75
  setWindSpeed(windSpeed) { this.windSpeed = windSpeed; }
76
76
  getWindSpeed() { return this.windSpeed; }
77
+ onDisconnect() {
78
+ this.hasControl = false;
79
+ }
77
80
  async requestControl() {
78
81
  if (this.hasControl) {
79
82
  return true;
80
83
  }
84
+ if (!this.isSubscribed())
85
+ return false;
81
86
  if (this.features?.setPower === false && this.features?.setSlope === false && this.features?.setResistance === false) {
82
87
  return true;
83
88
  }
@@ -96,6 +101,8 @@ export default class BleFitnessMachineDevice extends TBleSensor {
96
101
  return this.hasControl;
97
102
  }
98
103
  async setTargetPower(power) {
104
+ if (!this.isSubscribed())
105
+ return false;
99
106
  this.logEvent({ message: 'setTargetPower', device: this.getName(), power, skip: (this.data.targetPower !== undefined && this.data.targetPower === power) });
100
107
  if (this.data.targetPower !== undefined && this.data.targetPower === power)
101
108
  return true;
@@ -114,6 +121,8 @@ export default class BleFitnessMachineDevice extends TBleSensor {
114
121
  return (res === 1);
115
122
  }
116
123
  async setTargetResistanceLevel(resistanceLevel) {
124
+ if (!this.isSubscribed())
125
+ return false;
117
126
  this.logEvent({ message: 'setTargetResistanceLevel', device: this.getName(), resistanceLevel, skip: (this.data.resistanceLevel !== undefined && this.data.resistanceLevel === resistanceLevel) });
118
127
  if (this.data.resistanceLevel !== undefined && this.data.resistanceLevel === resistanceLevel)
119
128
  return true;
@@ -134,6 +143,8 @@ export default class BleFitnessMachineDevice extends TBleSensor {
134
143
  return (res === 1);
135
144
  }
136
145
  async setSlope(slope) {
146
+ if (!this.isSubscribed())
147
+ return false;
137
148
  this.logEvent({ message: 'setSlope', device: this.getName(), slope });
138
149
  const { windSpeed, crr, cw } = this;
139
150
  return await this.setIndoorBikeSimulation(windSpeed, slope, crr, cw);
@@ -3,7 +3,7 @@ import { BleInterface } from "./interface.js";
3
3
  export declare class BlePeripheral implements IBlePeripheral {
4
4
  protected announcement: BlePeripheralAnnouncement;
5
5
  protected connected: boolean;
6
- protected connectPromise: Promise<void>;
6
+ protected connectPromise: Promise<void> | undefined;
7
7
  protected characteristics: Record<string, BleRawCharacteristic>;
8
8
  protected onDisconnectHandler?: () => void;
9
9
  protected ble: BleInterface;
@@ -13,7 +13,7 @@ export declare class BlePeripheral implements IBlePeripheral {
13
13
  }>;
14
14
  protected disconnecting: boolean;
15
15
  protected disconnectedSignalled: boolean;
16
- protected discoveredServiceUUIds: Array<string>;
16
+ protected discoveredServiceUUIds: Array<string> | undefined;
17
17
  protected onErrorHandler: any;
18
18
  constructor(announcement: BlePeripheralAnnouncement);
19
19
  get services(): BleService[];
@@ -7,8 +7,10 @@ export declare class TBleSensor extends EventEmitter implements IBleSensor {
7
7
  static readonly protocol: BleProtocol;
8
8
  protected logger: EventLogger;
9
9
  protected stopRequested: boolean;
10
+ protected subscribeSuccess: boolean;
11
+ protected reconnectPromise: Promise<boolean> | undefined;
10
12
  protected onDataHandler: any;
11
- logEvent(event: any, ...args: any[]): void;
13
+ logEvent(event: any, ...args: any): void;
12
14
  constructor(peripheral: IBlePeripheral, props?: {
13
15
  logger?: EventLogger;
14
16
  });
@@ -22,12 +24,16 @@ export declare class TBleSensor extends EventEmitter implements IBleSensor {
22
24
  hasPeripheral(): boolean;
23
25
  pair(): Promise<boolean>;
24
26
  startSensor(reconnect?: boolean): Promise<boolean>;
25
- protected getRequiredCharacteristics(): Array<string>;
27
+ protected getRequiredCharacteristics(): Array<string> | null;
26
28
  subscribe(): Promise<boolean>;
27
29
  stopSensor(): Promise<boolean>;
28
- reconnectSensor(): Promise<void>;
30
+ isReconnectBusy(): boolean;
31
+ reconnectSensor(): Promise<boolean>;
32
+ doReconnectSensor(): Promise<boolean>;
29
33
  reset(): void;
30
34
  isConnected(): boolean;
35
+ isSubscribed(): boolean;
36
+ protected onDisconnect(): void;
31
37
  read(characteristicUUID: string): Promise<Buffer>;
32
38
  write(characteristicUUID: string, data: Buffer, options?: BleWriteProps): Promise<Buffer>;
33
39
  onData(characteristic: string, data: Buffer): boolean;
@@ -9,11 +9,11 @@ import { BleZwiftPlaySensor } from '../zwift/play/index.js';
9
9
  export default class BleFmAdapter extends BleAdapter<IndoorBikeData, BleFitnessMachineDevice> {
10
10
  protected static INCYCLIST_PROFILE_NAME: LegacyProfile;
11
11
  protected distanceInternal: number;
12
- protected connectPromise: Promise<boolean>;
12
+ protected connectPromise: Promise<boolean> | undefined;
13
13
  protected requestControlRetryDelay: number;
14
- protected promiseSendUpdate: Promise<UpdateRequest | void>;
15
- protected zwiftPlay: BleZwiftPlaySensor;
16
- protected virtualShiftingSupported: undefined;
14
+ protected promiseSendUpdate: Promise<UpdateRequest | void> | undefined;
15
+ protected zwiftPlay: BleZwiftPlaySensor | undefined;
16
+ protected isHubInitialized: boolean;
17
17
  constructor(settings: BleDeviceSettings, props?: BleDeviceProperties);
18
18
  updateSensor(peripheral: IBlePeripheral): void;
19
19
  isSame(device: IAdapter): boolean;
@@ -24,7 +24,7 @@ export default class BleFmAdapter extends BleAdapter<IndoorBikeData, BleFitnessM
24
24
  mapData(deviceData: IndoorBikeData): IncyclistBikeData;
25
25
  transformData(bikeData: IncyclistBikeData): IncyclistAdapterData;
26
26
  protected checkResume(): boolean[];
27
- protected initVirtualShifting(): Promise<void>;
27
+ protected initVirtualShifting(initHub?: boolean): Promise<void>;
28
28
  protected initControl(_startProps?: BleStartProperties): Promise<void>;
29
29
  protected setConstants(): void;
30
30
  protected establishControl(): Promise<boolean>;
@@ -28,10 +28,11 @@ export default class BleFitnessMachineDevice extends TBleSensor {
28
28
  getCw(): number;
29
29
  setWindSpeed(windSpeed: number): void;
30
30
  getWindSpeed(): number;
31
+ protected onDisconnect(): void;
31
32
  requestControl(): Promise<boolean>;
32
33
  setTargetPower(power: number): Promise<boolean>;
33
34
  setTargetResistanceLevel(resistanceLevel: number): Promise<boolean>;
34
- setSlope(slope: any): Promise<boolean>;
35
+ setSlope(slope: number): Promise<boolean>;
35
36
  protected parseHrm(_data: Uint8Array): IndoorBikeData;
36
37
  protected parseIndoorBikeData(_data: Uint8Array): IndoorBikeData;
37
38
  protected parseFitnessMachineStatus(_data: Uint8Array): IndoorBikeData;
@@ -40,7 +40,7 @@ export declare class BleZwiftPlaySensor extends TBleSensor {
40
40
  protected prevHubSettings: DeviceSettingsSubContent | undefined;
41
41
  protected prevcWax10000: number;
42
42
  constructor(peripheral: IBlePeripheral | TBleSensor, props?: BleZwiftPlaySensorProps);
43
- reconnectSensor(): Promise<void>;
43
+ reconnectSensor(): Promise<boolean>;
44
44
  stopSensor(): Promise<boolean>;
45
45
  protected getRequiredCharacteristics(): Array<string>;
46
46
  onData(characteristic: string, data: Buffer, isNotify?: boolean): boolean;
@@ -14,7 +14,7 @@ export type IncyclistAdapterData = {
14
14
  export type IncyclistBikeData = {
15
15
  isPedalling?: boolean;
16
16
  power: number;
17
- pedalRpm: number;
17
+ pedalRpm?: number;
18
18
  speed: number;
19
19
  heartrate?: number;
20
20
  distanceInternal?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "incyclist-devices",
3
- "version": "3.0.4",
3
+ "version": "3.0.6",
4
4
  "scripts": {
5
5
  "lint": "eslint . --ext .ts",
6
6
  "build": "npm run build:esm && npm run build:cjs",