incyclist-devices 3.0.5 → 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'));
@@ -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'));
@@ -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;
@@ -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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "incyclist-devices",
3
- "version": "3.0.5",
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",