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.
- package/lib/cjs/ble/base/adapter.js +14 -2
- package/lib/cjs/ble/base/peripheral.js +12 -3
- package/lib/cjs/ble/base/sensor.js +29 -2
- package/lib/cjs/ble/fm/adapter.js +14 -15
- package/lib/cjs/ble/fm/sensor.js +11 -0
- package/lib/esm/ble/base/adapter.js +14 -2
- package/lib/esm/ble/base/peripheral.js +12 -3
- package/lib/esm/ble/base/sensor.js +29 -2
- package/lib/esm/ble/fm/adapter.js +14 -15
- package/lib/esm/ble/fm/sensor.js +11 -0
- package/lib/types/ble/base/peripheral.d.ts +2 -2
- package/lib/types/ble/base/sensor.d.ts +9 -3
- package/lib/types/ble/fm/adapter.d.ts +5 -5
- package/lib/types/ble/fm/sensor.d.ts +2 -1
- package/lib/types/ble/zwift/play/sensor.d.ts +1 -1
- package/lib/types/types/data.d.ts +1 -1
- package/package.json +1 -1
|
@@ -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
|
|
354
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
348
|
-
return false;
|
|
349
|
-
}
|
|
348
|
+
return false;
|
|
350
349
|
}
|
|
351
350
|
getFeatureToggle() {
|
|
352
351
|
return (0, index_js_3.useFeatureToggle)();
|
package/lib/cjs/ble/fm/sensor.js
CHANGED
|
@@ -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
|
|
349
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
343
|
-
return false;
|
|
344
|
-
}
|
|
343
|
+
return false;
|
|
345
344
|
}
|
|
346
345
|
getFeatureToggle() {
|
|
347
346
|
return useFeatureToggle();
|
package/lib/esm/ble/fm/sensor.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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:
|
|
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<
|
|
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;
|