incyclist-devices 1.4.48 → 1.4.51
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/DeviceSupport.d.ts +2 -1
- package/lib/DeviceSupport.js +3 -1
- package/lib/ble/ble-device.d.ts +11 -2
- package/lib/ble/ble-device.js +28 -5
- package/lib/ble/ble-erg-mode.d.ts +1 -8
- package/lib/ble/ble-erg-mode.js +20 -33
- package/lib/ble/ble-st-mode.d.ts +1 -10
- package/lib/ble/ble-st-mode.js +33 -76
- package/lib/ble/fm.d.ts +25 -2
- package/lib/ble/fm.js +234 -19
- package/lib/ble/hrm.js +1 -0
- package/lib/ble/pwr.js +1 -0
- package/lib/ble/wahoo-kickr.d.ts +73 -0
- package/lib/ble/wahoo-kickr.js +372 -0
- package/lib/modes/power-base.js +0 -1
- package/lib/modes/power-meter.js +1 -1
- package/package.json +1 -1
package/lib/DeviceSupport.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ import BleInterface from './ble/ble-interface';
|
|
|
12
12
|
import BleHrmDevice from './ble/hrm';
|
|
13
13
|
import BleCyclingPowerDevice from './ble/pwr';
|
|
14
14
|
import BleFitnessMachineDevice from './ble/fm';
|
|
15
|
+
import WahooAdvancedFitnessMachineDevice from './ble/wahoo-kickr';
|
|
15
16
|
declare const Protocols: {
|
|
16
17
|
SimulatorProtocol: typeof SimulatorProtocol;
|
|
17
18
|
DaumClassicProtocol: typeof DaumClassicProtocol;
|
|
@@ -19,4 +20,4 @@ declare const Protocols: {
|
|
|
19
20
|
KettlerRacerProtocol: typeof KettlerRacerProtocol;
|
|
20
21
|
BleProtocol: typeof BleProtocol;
|
|
21
22
|
};
|
|
22
|
-
export { DeviceProtocolBase, DeviceProtocol, DeviceRegistry, INTERFACE, DeviceAdapter as Device, Protocols, AntScanner, BleProtocol, CyclingModeProperyType, BleInterface, BleHrmDevice, BleCyclingPowerDevice, BleFitnessMachineDevice };
|
|
23
|
+
export { DeviceProtocolBase, DeviceProtocol, DeviceRegistry, INTERFACE, DeviceAdapter as Device, Protocols, AntScanner, BleProtocol, CyclingModeProperyType, BleInterface, BleHrmDevice, BleCyclingPowerDevice, BleFitnessMachineDevice, WahooAdvancedFitnessMachineDevice };
|
package/lib/DeviceSupport.js
CHANGED
|
@@ -26,7 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
26
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.BleFitnessMachineDevice = exports.BleCyclingPowerDevice = exports.BleHrmDevice = exports.BleInterface = exports.CyclingModeProperyType = exports.BleProtocol = exports.AntScanner = exports.Protocols = exports.Device = exports.INTERFACE = exports.DeviceRegistry = exports.DeviceProtocolBase = void 0;
|
|
29
|
+
exports.WahooAdvancedFitnessMachineDevice = exports.BleFitnessMachineDevice = exports.BleCyclingPowerDevice = exports.BleHrmDevice = exports.BleInterface = exports.CyclingModeProperyType = exports.BleProtocol = exports.AntScanner = exports.Protocols = exports.Device = exports.INTERFACE = exports.DeviceRegistry = exports.DeviceProtocolBase = void 0;
|
|
30
30
|
const DeviceRegistry_1 = __importDefault(require("./DeviceRegistry"));
|
|
31
31
|
exports.DeviceRegistry = DeviceRegistry_1.default;
|
|
32
32
|
const Device_1 = __importDefault(require("./Device"));
|
|
@@ -52,6 +52,8 @@ const pwr_1 = __importDefault(require("./ble/pwr"));
|
|
|
52
52
|
exports.BleCyclingPowerDevice = pwr_1.default;
|
|
53
53
|
const fm_1 = __importDefault(require("./ble/fm"));
|
|
54
54
|
exports.BleFitnessMachineDevice = fm_1.default;
|
|
55
|
+
const wahoo_kickr_1 = __importDefault(require("./ble/wahoo-kickr"));
|
|
56
|
+
exports.WahooAdvancedFitnessMachineDevice = wahoo_kickr_1.default;
|
|
55
57
|
const Protocols = {
|
|
56
58
|
SimulatorProtocol: Simulator_1.default,
|
|
57
59
|
DaumClassicProtocol: DaumClassicProtocol_1.default,
|
package/lib/ble/ble-device.d.ts
CHANGED
|
@@ -6,6 +6,13 @@ interface BleDeviceConstructProps extends BleDeviceProps {
|
|
|
6
6
|
log?: boolean;
|
|
7
7
|
logger?: EventLogger;
|
|
8
8
|
}
|
|
9
|
+
declare type CommandQueueItem = {
|
|
10
|
+
uuid: string;
|
|
11
|
+
data: Buffer;
|
|
12
|
+
resolve: any;
|
|
13
|
+
reject: any;
|
|
14
|
+
timeout: any;
|
|
15
|
+
};
|
|
9
16
|
export declare abstract class BleDevice extends BleDeviceClass {
|
|
10
17
|
id: string;
|
|
11
18
|
address: string;
|
|
@@ -19,8 +26,10 @@ export declare abstract class BleDevice extends BleDeviceClass {
|
|
|
19
26
|
deviceInfo: BleDeviceInfo;
|
|
20
27
|
isInitialized: boolean;
|
|
21
28
|
subscribedCharacteristics: string[];
|
|
29
|
+
writeQueue: CommandQueueItem[];
|
|
22
30
|
constructor(props?: BleDeviceConstructProps);
|
|
23
31
|
logEvent(event: any): void;
|
|
32
|
+
setLogger(logger: EventLogger): void;
|
|
24
33
|
setInterface(ble: BleInterfaceClass): void;
|
|
25
34
|
cleanupListeners(): void;
|
|
26
35
|
onDisconnect(): void;
|
|
@@ -32,8 +41,8 @@ export declare abstract class BleDevice extends BleDeviceClass {
|
|
|
32
41
|
connect(props?: ConnectProps): Promise<boolean>;
|
|
33
42
|
disconnect(): Promise<boolean>;
|
|
34
43
|
abstract getProfile(): string;
|
|
35
|
-
|
|
36
|
-
write(characteristicUuid: string, data: Buffer, withoutResponse?: boolean): Promise<
|
|
44
|
+
onData(characteristic: string, data: Buffer): void;
|
|
45
|
+
write(characteristicUuid: string, data: Buffer, withoutResponse?: boolean): Promise<ArrayBuffer>;
|
|
37
46
|
read(characteristicUuid: string): Promise<Uint8Array>;
|
|
38
47
|
getDeviceInfo(): Promise<BleDeviceInfo>;
|
|
39
48
|
}
|
package/lib/ble/ble-device.js
CHANGED
|
@@ -26,6 +26,7 @@ class BleDevice extends ble_1.BleDeviceClass {
|
|
|
26
26
|
this.characteristics = [];
|
|
27
27
|
this.subscribedCharacteristics = [];
|
|
28
28
|
this.isInitialized = false;
|
|
29
|
+
this.writeQueue = [];
|
|
29
30
|
if (props.peripheral) {
|
|
30
31
|
const { id, address, advertisement, state } = props.peripheral;
|
|
31
32
|
this.peripheral = props.peripheral;
|
|
@@ -38,7 +39,7 @@ class BleDevice extends ble_1.BleDeviceClass {
|
|
|
38
39
|
if (props.logger) {
|
|
39
40
|
this.logger = props.logger;
|
|
40
41
|
}
|
|
41
|
-
else if (props.log) {
|
|
42
|
+
else if (props.log !== false) {
|
|
42
43
|
this.logger = new gd_eventlog_1.EventLogger('BleDevice');
|
|
43
44
|
}
|
|
44
45
|
}
|
|
@@ -50,6 +51,9 @@ class BleDevice extends ble_1.BleDeviceClass {
|
|
|
50
51
|
console.log('~~~BLE:', event);
|
|
51
52
|
}
|
|
52
53
|
}
|
|
54
|
+
setLogger(logger) {
|
|
55
|
+
this.logger = logger;
|
|
56
|
+
}
|
|
53
57
|
setInterface(ble) {
|
|
54
58
|
this.ble = ble;
|
|
55
59
|
}
|
|
@@ -103,6 +107,7 @@ class BleDevice extends ble_1.BleDeviceClass {
|
|
|
103
107
|
return Promise.resolve(true);
|
|
104
108
|
return this.getDeviceInfo().then(() => {
|
|
105
109
|
this.emit('deviceInfo', this.deviceInfo);
|
|
110
|
+
this.logEvent(Object.assign({ message: 'ftms device init done' }, this.deviceInfo));
|
|
106
111
|
this.isInitialized = true;
|
|
107
112
|
return true;
|
|
108
113
|
});
|
|
@@ -235,9 +240,20 @@ class BleDevice extends ble_1.BleDeviceClass {
|
|
|
235
240
|
}
|
|
236
241
|
});
|
|
237
242
|
}
|
|
243
|
+
onData(characteristic, data) {
|
|
244
|
+
if (this.writeQueue.length > 0) {
|
|
245
|
+
const writeIdx = this.writeQueue.findIndex(i => i.uuid === characteristic.toLocaleLowerCase());
|
|
246
|
+
if (writeIdx !== -1) {
|
|
247
|
+
const writeItem = this.writeQueue[writeIdx];
|
|
248
|
+
this.writeQueue.splice(writeIdx, 1);
|
|
249
|
+
if (writeItem.resolve)
|
|
250
|
+
writeItem.resolve(data);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
238
254
|
write(characteristicUuid, data, withoutResponse = false) {
|
|
239
255
|
return __awaiter(this, void 0, void 0, function* () {
|
|
240
|
-
if (this.subscribedCharacteristics.find(c => c === characteristicUuid) === undefined) {
|
|
256
|
+
if (!withoutResponse && this.subscribedCharacteristics.find(c => c === characteristicUuid) === undefined) {
|
|
241
257
|
const connector = this.ble.getConnector(this.peripheral);
|
|
242
258
|
connector.on(characteristicUuid, (uuid, data) => {
|
|
243
259
|
this.onData(uuid, data);
|
|
@@ -251,11 +267,18 @@ class BleDevice extends ble_1.BleDeviceClass {
|
|
|
251
267
|
reject(new Error('Characteristic not found'));
|
|
252
268
|
return;
|
|
253
269
|
}
|
|
270
|
+
if (withoutResponse) {
|
|
271
|
+
characteristic.write(data, withoutResponse);
|
|
272
|
+
resolve(new ArrayBuffer(0));
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const writeId = this.writeQueue.length;
|
|
276
|
+
this.writeQueue.push({ uuid: characteristicUuid.toLocaleLowerCase(), data, resolve, reject, timeout: Date.now() + 1000 });
|
|
254
277
|
characteristic.write(data, withoutResponse, (err) => {
|
|
255
|
-
if (err)
|
|
278
|
+
if (err) {
|
|
279
|
+
this.writeQueue.splice(writeId, 1);
|
|
256
280
|
reject(err);
|
|
257
|
-
|
|
258
|
-
resolve(true);
|
|
281
|
+
}
|
|
259
282
|
});
|
|
260
283
|
});
|
|
261
284
|
});
|
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
import CyclingMode, { CyclingModeProperty, IncyclistBikeData, UpdateRequest } from "../CyclingMode";
|
|
2
2
|
import PowerBasedCyclingModeBase from "../modes/power-base";
|
|
3
3
|
import { FmAdapter } from "./fm";
|
|
4
|
-
export
|
|
5
|
-
rpmUpdated?: boolean;
|
|
6
|
-
gearUpdated?: boolean;
|
|
7
|
-
starting?: boolean;
|
|
8
|
-
tsStart?: number;
|
|
9
|
-
};
|
|
10
|
-
export default class ERGCyclingMode extends PowerBasedCyclingModeBase implements CyclingMode {
|
|
4
|
+
export default class BleERGCyclingMode extends PowerBasedCyclingModeBase implements CyclingMode {
|
|
11
5
|
prevRequest: UpdateRequest;
|
|
12
6
|
hasBikeUpdate: boolean;
|
|
13
7
|
chain: number[];
|
|
14
8
|
cassette: number[];
|
|
15
|
-
event: ERGEvent;
|
|
16
9
|
constructor(adapter: FmAdapter, props?: any);
|
|
17
10
|
getName(): string;
|
|
18
11
|
getDescription(): string;
|
package/lib/ble/ble-erg-mode.js
CHANGED
|
@@ -13,11 +13,11 @@ const config = {
|
|
|
13
13
|
{ key: 'startPower', name: 'Starting Power', description: 'Initial power in Watts at start of training', type: CyclingMode_1.CyclingModeProperyType.Integer, default: 50, min: 25, max: 800 },
|
|
14
14
|
]
|
|
15
15
|
};
|
|
16
|
-
|
|
16
|
+
const MIN_SPEED = 10;
|
|
17
|
+
class BleERGCyclingMode extends power_base_1.default {
|
|
17
18
|
constructor(adapter, props) {
|
|
18
19
|
super(adapter, props);
|
|
19
20
|
this.hasBikeUpdate = false;
|
|
20
|
-
this.event = {};
|
|
21
21
|
this.initLogger('ERGMode');
|
|
22
22
|
}
|
|
23
23
|
getName() {
|
|
@@ -34,7 +34,7 @@ class ERGCyclingMode extends power_base_1.default {
|
|
|
34
34
|
}
|
|
35
35
|
getBikeInitRequest() {
|
|
36
36
|
const startPower = this.getSetting('startPower');
|
|
37
|
-
return { targetPower: startPower };
|
|
37
|
+
return { slope: 0, targetPower: startPower };
|
|
38
38
|
}
|
|
39
39
|
sendBikeUpdate(request) {
|
|
40
40
|
const getData = () => {
|
|
@@ -43,25 +43,18 @@ class ERGCyclingMode extends power_base_1.default {
|
|
|
43
43
|
const { pedalRpm, slope, power, speed } = this.data;
|
|
44
44
|
return { pedalRpm, slope, power, speed };
|
|
45
45
|
};
|
|
46
|
-
this.logger.logEvent({ message: "processing update request", request, prev: this.prevRequest, data: getData()
|
|
46
|
+
this.logger.logEvent({ message: "processing update request", request, prev: this.prevRequest, data: getData() });
|
|
47
47
|
let newRequest = {};
|
|
48
48
|
try {
|
|
49
49
|
if (!request || request.reset || Object.keys(request).length === 0) {
|
|
50
50
|
this.prevRequest = {};
|
|
51
|
-
return request
|
|
51
|
+
return request.reset ? { reset: true } : {};
|
|
52
52
|
}
|
|
53
53
|
const prevData = this.data || {};
|
|
54
54
|
if (request.targetPower !== undefined) {
|
|
55
55
|
delete request.slope;
|
|
56
56
|
delete request.refresh;
|
|
57
57
|
}
|
|
58
|
-
if (this.event.starting && request.targetPower === undefined) {
|
|
59
|
-
newRequest.targetPower = this.getSetting('startPower');
|
|
60
|
-
if (this.event.tsStart && Date.now() - this.event.tsStart > 5000) {
|
|
61
|
-
delete this.event.starting;
|
|
62
|
-
delete this.event.tsStart;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
58
|
if (request.refresh) {
|
|
66
59
|
delete request.refresh;
|
|
67
60
|
newRequest.targetPower = this.prevRequest.targetPower;
|
|
@@ -70,14 +63,12 @@ class ERGCyclingMode extends power_base_1.default {
|
|
|
70
63
|
if (!this.data)
|
|
71
64
|
this.data = {};
|
|
72
65
|
this.data.slope = request.slope;
|
|
66
|
+
delete request.slope;
|
|
73
67
|
}
|
|
74
68
|
if (request.maxPower !== undefined && request.minPower !== undefined && request.maxPower === request.minPower) {
|
|
75
69
|
request.targetPower = request.maxPower;
|
|
76
|
-
}
|
|
77
|
-
if (request.targetPower !== undefined) {
|
|
78
70
|
newRequest.targetPower = request.targetPower;
|
|
79
71
|
}
|
|
80
|
-
delete request.slope;
|
|
81
72
|
if (request.maxPower !== undefined) {
|
|
82
73
|
if (newRequest.targetPower !== undefined && newRequest.targetPower > request.maxPower) {
|
|
83
74
|
newRequest.targetPower = request.maxPower;
|
|
@@ -89,9 +80,8 @@ class ERGCyclingMode extends power_base_1.default {
|
|
|
89
80
|
newRequest.targetPower = request.minPower;
|
|
90
81
|
}
|
|
91
82
|
newRequest.minPower = request.minPower;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
delete newRequest.targetPower;
|
|
83
|
+
if (prevData.power && prevData.power < request.minPower)
|
|
84
|
+
newRequest.targetPower = request.minPower;
|
|
95
85
|
}
|
|
96
86
|
this.prevRequest = JSON.parse(JSON.stringify(request));
|
|
97
87
|
}
|
|
@@ -106,36 +96,33 @@ class ERGCyclingMode extends power_base_1.default {
|
|
|
106
96
|
const prevRequest = this.prevRequest || {};
|
|
107
97
|
const data = this.data || {};
|
|
108
98
|
const bikeType = this.getSetting('bikeType').toLowerCase();
|
|
109
|
-
delete this.event.rpmUpdated;
|
|
110
|
-
if (prevData === {} || prevData.speed === undefined || prevData.speed === 0) {
|
|
111
|
-
this.event.starting = true;
|
|
112
|
-
this.event.tsStart = Date.now();
|
|
113
|
-
}
|
|
114
99
|
try {
|
|
115
|
-
const rpm = bikeData.pedalRpm || 0;
|
|
116
100
|
let power = bikeData.power || 0;
|
|
117
101
|
const slope = (prevData.slope !== undefined ? prevData.slope : prevRequest.slope || 0);
|
|
118
102
|
const distanceInternal = prevData.distanceInternal || 0;
|
|
119
|
-
if (
|
|
103
|
+
if (bikeData.pedalRpm === 0 || bikeData.isPedalling === false) {
|
|
120
104
|
power = 0;
|
|
121
105
|
}
|
|
122
106
|
const m = this.getWeight();
|
|
123
107
|
const t = this.getTimeSinceLastUpdate();
|
|
124
108
|
const { speed, distance } = this.calculateSpeedAndDistance(power, slope, m, t, { bikeType });
|
|
125
|
-
|
|
109
|
+
if (power === 0 && speed < MIN_SPEED) {
|
|
110
|
+
data.speed = Math.round(prevData.speed - 1) < 0 ? 0 : Math.round(prevData.speed - 1);
|
|
111
|
+
data.distanceInternal = Math.round(distanceInternal + data.speed / 3.6 * t);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
data.speed = (power === 0 && speed < MIN_SPEED) ? 0 : speed;
|
|
115
|
+
data.distanceInternal = (power === 0 && speed < MIN_SPEED) ? Math.round(distanceInternal) : Math.round(distanceInternal + distance);
|
|
116
|
+
}
|
|
126
117
|
data.power = Math.round(power);
|
|
127
|
-
data.distanceInternal = Math.round(distanceInternal + distance);
|
|
128
118
|
data.slope = slope;
|
|
129
|
-
data.pedalRpm =
|
|
130
|
-
if (data.time !== undefined &&
|
|
119
|
+
data.pedalRpm = bikeData.pedalRpm || 0;
|
|
120
|
+
if (data.time !== undefined && data.speed > 0)
|
|
131
121
|
data.time += t;
|
|
132
122
|
else
|
|
133
123
|
data.time = 0;
|
|
134
124
|
data.heartrate = bikeData.heartrate;
|
|
135
125
|
data.isPedalling = bikeData.isPedalling;
|
|
136
|
-
if (rpm && rpm !== prevData.pedalRpm) {
|
|
137
|
-
this.event.rpmUpdated = true;
|
|
138
|
-
}
|
|
139
126
|
}
|
|
140
127
|
catch (err) {
|
|
141
128
|
this.logger.logEvent({ message: 'error', fn: 'updateData()', error: err.message || err });
|
|
@@ -145,4 +132,4 @@ class ERGCyclingMode extends power_base_1.default {
|
|
|
145
132
|
return data;
|
|
146
133
|
}
|
|
147
134
|
}
|
|
148
|
-
exports.default =
|
|
135
|
+
exports.default = BleERGCyclingMode;
|
package/lib/ble/ble-st-mode.d.ts
CHANGED
|
@@ -1,18 +1,9 @@
|
|
|
1
1
|
import CyclingMode, { CyclingModeProperty, IncyclistBikeData, UpdateRequest } from "../CyclingMode";
|
|
2
2
|
import PowerBasedCyclingModeBase from "../modes/power-base";
|
|
3
3
|
import { FmAdapter } from "./fm";
|
|
4
|
-
export
|
|
5
|
-
rpmUpdated?: boolean;
|
|
6
|
-
gearUpdated?: boolean;
|
|
7
|
-
starting?: boolean;
|
|
8
|
-
tsStart?: number;
|
|
9
|
-
};
|
|
10
|
-
export default class ERGCyclingMode extends PowerBasedCyclingModeBase implements CyclingMode {
|
|
4
|
+
export default class FtmsCyclingMode extends PowerBasedCyclingModeBase implements CyclingMode {
|
|
11
5
|
prevRequest: UpdateRequest;
|
|
12
6
|
hasBikeUpdate: boolean;
|
|
13
|
-
chain: number[];
|
|
14
|
-
cassette: number[];
|
|
15
|
-
event: ERGEvent;
|
|
16
7
|
constructor(adapter: FmAdapter, props?: any);
|
|
17
8
|
getName(): string;
|
|
18
9
|
getDescription(): string;
|
package/lib/ble/ble-st-mode.js
CHANGED
|
@@ -9,16 +9,15 @@ const config = {
|
|
|
9
9
|
name: "Smart Trainer",
|
|
10
10
|
description: "Calculates speed based on power and slope. Slope is set to the device",
|
|
11
11
|
properties: [
|
|
12
|
-
{ key: 'bikeType', name: 'Bike Type', description: '', type: CyclingMode_1.CyclingModeProperyType.SingleSelect, options: ['Race', 'Mountain', 'Triathlon'], default: 'Race' }
|
|
13
|
-
{ key: 'startPower', name: 'Starting Power', description: 'Initial power in Watts at start of training', type: CyclingMode_1.CyclingModeProperyType.Integer, default: 50, min: 25, max: 800 },
|
|
12
|
+
{ key: 'bikeType', name: 'Bike Type', description: '', type: CyclingMode_1.CyclingModeProperyType.SingleSelect, options: ['Race', 'Mountain', 'Triathlon'], default: 'Race' }
|
|
14
13
|
]
|
|
15
14
|
};
|
|
16
|
-
|
|
15
|
+
const MIN_SPEED = 10;
|
|
16
|
+
class FtmsCyclingMode extends power_base_1.default {
|
|
17
17
|
constructor(adapter, props) {
|
|
18
18
|
super(adapter, props);
|
|
19
19
|
this.hasBikeUpdate = false;
|
|
20
|
-
this.
|
|
21
|
-
this.initLogger('ERGMode');
|
|
20
|
+
this.initLogger('FtmsMode');
|
|
22
21
|
}
|
|
23
22
|
getName() {
|
|
24
23
|
return config.name;
|
|
@@ -33,71 +32,32 @@ class ERGCyclingMode extends power_base_1.default {
|
|
|
33
32
|
return config.properties.find(p => p.name === name);
|
|
34
33
|
}
|
|
35
34
|
getBikeInitRequest() {
|
|
36
|
-
|
|
37
|
-
return { targetPower: startPower };
|
|
35
|
+
return { slope: 0 };
|
|
38
36
|
}
|
|
39
37
|
sendBikeUpdate(request) {
|
|
40
38
|
const getData = () => {
|
|
41
39
|
if (!this.data)
|
|
42
40
|
return {};
|
|
43
|
-
const { pedalRpm, slope, power, speed } = this.data;
|
|
44
|
-
return { pedalRpm, slope, power, speed };
|
|
41
|
+
const { gear, pedalRpm, slope, power, speed } = this.data;
|
|
42
|
+
return { gear, pedalRpm, slope, power, speed };
|
|
45
43
|
};
|
|
46
|
-
|
|
44
|
+
const event = {};
|
|
45
|
+
if (this.data === undefined)
|
|
46
|
+
event.noData = true;
|
|
47
|
+
if (request.slope !== undefined && (event.noData || Math.abs(request.slope - this.data.slope) >= 0.1))
|
|
48
|
+
event.slopeUpdate = true;
|
|
49
|
+
if (this.prevRequest === undefined)
|
|
50
|
+
event.initialCall = true;
|
|
51
|
+
this.logger.logEvent({ message: "processing update request", request, prev: this.prevRequest, data: getData(), event });
|
|
47
52
|
let newRequest = {};
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
this.prevRequest = {};
|
|
51
|
-
return request || {};
|
|
52
|
-
}
|
|
53
|
-
const prevData = this.data || {};
|
|
54
|
-
if (request.targetPower !== undefined) {
|
|
55
|
-
delete request.slope;
|
|
56
|
-
delete request.refresh;
|
|
57
|
-
}
|
|
58
|
-
if (this.event.starting && request.targetPower === undefined) {
|
|
59
|
-
newRequest.targetPower = this.getSetting('startPower');
|
|
60
|
-
if (this.event.tsStart && Date.now() - this.event.tsStart > 5000) {
|
|
61
|
-
delete this.event.starting;
|
|
62
|
-
delete this.event.tsStart;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
if (request.refresh) {
|
|
66
|
-
delete request.refresh;
|
|
67
|
-
newRequest.targetPower = this.prevRequest.targetPower;
|
|
68
|
-
}
|
|
69
|
-
if (request.slope !== undefined) {
|
|
70
|
-
if (!this.data)
|
|
71
|
-
this.data = {};
|
|
72
|
-
this.data.slope = request.slope;
|
|
73
|
-
}
|
|
74
|
-
if (request.maxPower !== undefined && request.minPower !== undefined && request.maxPower === request.minPower) {
|
|
75
|
-
request.targetPower = request.maxPower;
|
|
76
|
-
}
|
|
77
|
-
if (request.targetPower !== undefined) {
|
|
78
|
-
newRequest.targetPower = request.targetPower;
|
|
79
|
-
}
|
|
80
|
-
delete request.slope;
|
|
81
|
-
if (request.maxPower !== undefined) {
|
|
82
|
-
if (newRequest.targetPower !== undefined && newRequest.targetPower > request.maxPower) {
|
|
83
|
-
newRequest.targetPower = request.maxPower;
|
|
84
|
-
}
|
|
85
|
-
newRequest.maxPower = request.maxPower;
|
|
86
|
-
}
|
|
87
|
-
if (request.minPower !== undefined) {
|
|
88
|
-
if (newRequest.targetPower !== undefined && newRequest.targetPower < request.minPower) {
|
|
89
|
-
newRequest.targetPower = request.minPower;
|
|
90
|
-
}
|
|
91
|
-
newRequest.minPower = request.minPower;
|
|
92
|
-
}
|
|
93
|
-
if (newRequest.targetPower !== undefined && prevData.power !== undefined && newRequest.targetPower === prevData.power) {
|
|
94
|
-
delete newRequest.targetPower;
|
|
95
|
-
}
|
|
96
|
-
this.prevRequest = JSON.parse(JSON.stringify(request));
|
|
53
|
+
if (request.slope === undefined && request.refresh && this.prevRequest) {
|
|
54
|
+
return this.prevRequest;
|
|
97
55
|
}
|
|
98
|
-
|
|
99
|
-
|
|
56
|
+
if (request.slope !== undefined) {
|
|
57
|
+
newRequest.slope = parseFloat(request.slope.toFixed(1));
|
|
58
|
+
this.data.slope = newRequest.slope;
|
|
100
59
|
}
|
|
60
|
+
this.prevRequest = JSON.parse(JSON.stringify(newRequest));
|
|
101
61
|
return newRequest;
|
|
102
62
|
}
|
|
103
63
|
updateData(bikeData) {
|
|
@@ -106,36 +66,33 @@ class ERGCyclingMode extends power_base_1.default {
|
|
|
106
66
|
const prevRequest = this.prevRequest || {};
|
|
107
67
|
const data = this.data || {};
|
|
108
68
|
const bikeType = this.getSetting('bikeType').toLowerCase();
|
|
109
|
-
delete this.event.rpmUpdated;
|
|
110
|
-
if (prevData === {} || prevData.speed === undefined || prevData.speed === 0) {
|
|
111
|
-
this.event.starting = true;
|
|
112
|
-
this.event.tsStart = Date.now();
|
|
113
|
-
}
|
|
114
69
|
try {
|
|
115
|
-
const rpm = bikeData.pedalRpm || 0;
|
|
116
70
|
let power = bikeData.power || 0;
|
|
117
71
|
const slope = (prevData.slope !== undefined ? prevData.slope : prevRequest.slope || 0);
|
|
118
72
|
const distanceInternal = prevData.distanceInternal || 0;
|
|
119
|
-
if (
|
|
73
|
+
if (bikeData.pedalRpm === 0 || bikeData.isPedalling === false) {
|
|
120
74
|
power = 0;
|
|
121
75
|
}
|
|
122
76
|
const m = this.getWeight();
|
|
123
77
|
const t = this.getTimeSinceLastUpdate();
|
|
124
78
|
const { speed, distance } = this.calculateSpeedAndDistance(power, slope, m, t, { bikeType });
|
|
125
|
-
|
|
79
|
+
if (power === 0 && speed < MIN_SPEED) {
|
|
80
|
+
data.speed = Math.round(prevData.speed - 1) < 0 ? 0 : Math.round(prevData.speed - 1);
|
|
81
|
+
data.distanceInternal = Math.round(distanceInternal + data.speed / 3.6 * t);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
data.speed = (power === 0 && speed < MIN_SPEED) ? 0 : speed;
|
|
85
|
+
data.distanceInternal = (power === 0 && speed < MIN_SPEED) ? Math.round(distanceInternal) : Math.round(distanceInternal + distance);
|
|
86
|
+
}
|
|
126
87
|
data.power = Math.round(power);
|
|
127
|
-
data.distanceInternal = Math.round(distanceInternal + distance);
|
|
128
88
|
data.slope = slope;
|
|
129
|
-
data.pedalRpm =
|
|
130
|
-
if (data.time !== undefined
|
|
89
|
+
data.pedalRpm = bikeData.pedalRpm || 0;
|
|
90
|
+
if (data.time !== undefined)
|
|
131
91
|
data.time += t;
|
|
132
92
|
else
|
|
133
93
|
data.time = 0;
|
|
134
94
|
data.heartrate = bikeData.heartrate;
|
|
135
95
|
data.isPedalling = bikeData.isPedalling;
|
|
136
|
-
if (rpm && rpm !== prevData.pedalRpm) {
|
|
137
|
-
this.event.rpmUpdated = true;
|
|
138
|
-
}
|
|
139
96
|
}
|
|
140
97
|
catch (err) {
|
|
141
98
|
this.logger.logEvent({ message: 'error', fn: 'updateData()', error: err.message || err });
|
|
@@ -145,4 +102,4 @@ class ERGCyclingMode extends power_base_1.default {
|
|
|
145
102
|
return data;
|
|
146
103
|
}
|
|
147
104
|
}
|
|
148
|
-
exports.default =
|
|
105
|
+
exports.default = FtmsCyclingMode;
|
package/lib/ble/fm.d.ts
CHANGED
|
@@ -31,6 +31,9 @@ declare type IndoorBikeData = {
|
|
|
31
31
|
time?: number;
|
|
32
32
|
remainingTime?: number;
|
|
33
33
|
raw?: string;
|
|
34
|
+
targetPower?: number;
|
|
35
|
+
targetInclination?: number;
|
|
36
|
+
status?: string;
|
|
34
37
|
};
|
|
35
38
|
declare type IndoorBikeFeatures = {
|
|
36
39
|
fitnessMachine: number;
|
|
@@ -43,6 +46,10 @@ export default class BleFitnessMachineDevice extends BleDevice {
|
|
|
43
46
|
features: IndoorBikeFeatures;
|
|
44
47
|
hasControl: boolean;
|
|
45
48
|
isCPSubscribed: boolean;
|
|
49
|
+
crr: number;
|
|
50
|
+
cw: number;
|
|
51
|
+
windSpeed: number;
|
|
52
|
+
wheelSize: number;
|
|
46
53
|
constructor(props?: any);
|
|
47
54
|
init(): Promise<boolean>;
|
|
48
55
|
onDisconnect(): void;
|
|
@@ -52,11 +59,25 @@ export default class BleFitnessMachineDevice extends BleDevice {
|
|
|
52
59
|
isPower(): boolean;
|
|
53
60
|
isHrm(): boolean;
|
|
54
61
|
parseHrm(_data: Uint8Array): IndoorBikeData;
|
|
62
|
+
setCrr(crr: number): void;
|
|
63
|
+
getCrr(): number;
|
|
64
|
+
setCw(cw: number): void;
|
|
65
|
+
getCw(): number;
|
|
66
|
+
setWindSpeed(windSpeed: number): void;
|
|
67
|
+
getWindSpeed(): number;
|
|
55
68
|
parseIndoorBikeData(_data: Uint8Array): IndoorBikeData;
|
|
69
|
+
parseFitnessMachineStatus(_data: Uint8Array): IndoorBikeData;
|
|
56
70
|
getFitnessMachineFeatures(): Promise<IndoorBikeFeatures>;
|
|
57
71
|
onData(characteristic: string, data: Buffer): void;
|
|
72
|
+
writeFtmsMessage(requestedOpCode: any, data: any): Promise<number>;
|
|
58
73
|
requestControl(): Promise<boolean>;
|
|
59
|
-
setTargetPower(power: number): Promise<
|
|
74
|
+
setTargetPower(power: number): Promise<boolean>;
|
|
75
|
+
setSlope(slope: any): Promise<boolean>;
|
|
76
|
+
setTargetInclination(inclination: number): Promise<boolean>;
|
|
77
|
+
setIndoorBikeSimulation(windSpeed: number, gradient: number, crr: number, cw: number): Promise<boolean>;
|
|
78
|
+
startRequest(): Promise<boolean>;
|
|
79
|
+
stopRequest(): Promise<boolean>;
|
|
80
|
+
PauseRequest(): Promise<boolean>;
|
|
60
81
|
reset(): void;
|
|
61
82
|
}
|
|
62
83
|
export declare class FmAdapter extends DeviceAdapter {
|
|
@@ -66,7 +87,7 @@ export declare class FmAdapter extends DeviceAdapter {
|
|
|
66
87
|
protocol: DeviceProtocol;
|
|
67
88
|
paused: boolean;
|
|
68
89
|
logger: EventLogger;
|
|
69
|
-
|
|
90
|
+
cyclingMode: CyclingMode;
|
|
70
91
|
distanceInternal: number;
|
|
71
92
|
prevDataTS: number;
|
|
72
93
|
constructor(device: BleDeviceClass, protocol: BleProtocol);
|
|
@@ -77,6 +98,8 @@ export declare class FmAdapter extends DeviceAdapter {
|
|
|
77
98
|
getProfile(): string;
|
|
78
99
|
getName(): string;
|
|
79
100
|
getDisplayName(): string;
|
|
101
|
+
getSupportedCyclingModes(): Array<any>;
|
|
102
|
+
setCyclingMode(mode: string | CyclingMode, settings?: any): void;
|
|
80
103
|
getCyclingMode(): CyclingMode;
|
|
81
104
|
getDefaultCyclingMode(): CyclingMode;
|
|
82
105
|
getPort(): string;
|