incyclist-devices 1.4.45 → 1.4.48
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/Device.d.ts +2 -0
- package/lib/Device.js +1 -0
- package/lib/ant/AntAdapter.d.ts +1 -0
- package/lib/ant/AntAdapter.js +6 -0
- package/lib/ble/ble-device.d.ts +9 -7
- package/lib/ble/ble-device.js +83 -121
- package/lib/ble/ble-erg-mode.d.ts +24 -0
- package/lib/ble/ble-erg-mode.js +148 -0
- package/lib/ble/ble-interface.d.ts +13 -0
- package/lib/ble/ble-interface.js +49 -25
- package/lib/ble/ble-peripheral.d.ts +34 -0
- package/lib/ble/ble-peripheral.js +170 -0
- package/lib/ble/ble-st-mode.d.ts +24 -0
- package/lib/ble/ble-st-mode.js +148 -0
- package/lib/ble/ble.d.ts +9 -0
- package/lib/ble/fm.d.ts +7 -1
- package/lib/ble/fm.js +60 -10
- package/lib/ble/hrm.d.ts +1 -1
- package/lib/ble/hrm.js +6 -4
- package/lib/ble/incyclist-protocol.js +9 -1
- package/lib/ble/pwr.d.ts +2 -1
- package/lib/ble/pwr.js +13 -4
- package/lib/daum/DaumAdapter.d.ts +1 -0
- package/lib/daum/DaumAdapter.js +6 -0
- package/lib/kettler/ergo-racer/adapter.d.ts +1 -0
- package/lib/kettler/ergo-racer/adapter.js +6 -0
- package/lib/modes/power-base.js +1 -0
- package/lib/modes/power-meter.js +10 -3
- package/lib/simulator/Simulator.d.ts +1 -0
- package/lib/simulator/Simulator.js +5 -0
- package/package.json +1 -1
package/lib/Device.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ export interface DeviceAdapter extends Device {
|
|
|
28
28
|
isBike(): boolean;
|
|
29
29
|
isPower(): boolean;
|
|
30
30
|
isHrm(): boolean;
|
|
31
|
+
isSame(device: DeviceAdapter): boolean;
|
|
31
32
|
getID(): string;
|
|
32
33
|
getDisplayName(): string;
|
|
33
34
|
getName(): string;
|
|
@@ -58,6 +59,7 @@ export default class IncyclistDevice implements DeviceAdapter {
|
|
|
58
59
|
isBike(): boolean;
|
|
59
60
|
isPower(): boolean;
|
|
60
61
|
isHrm(): boolean;
|
|
62
|
+
isSame(device: DeviceAdapter): boolean;
|
|
61
63
|
getID(): string;
|
|
62
64
|
getDisplayName(): string;
|
|
63
65
|
getName(): string;
|
package/lib/Device.js
CHANGED
|
@@ -13,6 +13,7 @@ class IncyclistDevice {
|
|
|
13
13
|
isBike() { throw new Error('not implemented'); }
|
|
14
14
|
isPower() { throw new Error('not implemented'); }
|
|
15
15
|
isHrm() { throw new Error('not implemented'); }
|
|
16
|
+
isSame(device) { throw new Error('not implemented'); }
|
|
16
17
|
getID() { throw new Error('not implemented'); }
|
|
17
18
|
getDisplayName() { return this.getName(); }
|
|
18
19
|
getName() { throw new Error('not implemented'); }
|
package/lib/ant/AntAdapter.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ export default class AntAdapter extends DeviceAdapter {
|
|
|
18
18
|
lastUpdate?: number;
|
|
19
19
|
updateFrequency: number;
|
|
20
20
|
constructor(protocol: any);
|
|
21
|
+
isSame(device: DeviceAdapter): boolean;
|
|
21
22
|
setSensor(sensor: any): void;
|
|
22
23
|
getID(): string;
|
|
23
24
|
setIgnoreHrm(ignore: any): void;
|
package/lib/ant/AntAdapter.js
CHANGED
|
@@ -22,6 +22,12 @@ class AntAdapter extends Device_1.default {
|
|
|
22
22
|
this.data = {};
|
|
23
23
|
this.updateFrequency = exports.DEFAULT_UPDATE_FREQUENCY;
|
|
24
24
|
}
|
|
25
|
+
isSame(device) {
|
|
26
|
+
if (!(device instanceof AntAdapter))
|
|
27
|
+
return false;
|
|
28
|
+
const adapter = device;
|
|
29
|
+
return (adapter.getName() === this.getName() && adapter.getProfile() === this.getProfile());
|
|
30
|
+
}
|
|
25
31
|
setSensor(sensor) {
|
|
26
32
|
this.sensor = sensor;
|
|
27
33
|
}
|
package/lib/ble/ble-device.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { EventLogger } from "gd-eventlog";
|
|
3
3
|
import { BleInterfaceClass, BleDeviceClass, BlePeripheral, BleDeviceProps, ConnectProps, BleDeviceInfo } from "./ble";
|
|
4
|
+
import BlePeripheralConnector from "./ble-peripheral";
|
|
4
5
|
interface BleDeviceConstructProps extends BleDeviceProps {
|
|
5
6
|
log?: boolean;
|
|
6
7
|
logger?: EventLogger;
|
|
@@ -16,23 +17,24 @@ export declare abstract class BleDevice extends BleDeviceClass {
|
|
|
16
17
|
state?: string;
|
|
17
18
|
logger?: EventLogger;
|
|
18
19
|
deviceInfo: BleDeviceInfo;
|
|
19
|
-
|
|
20
|
+
isInitialized: boolean;
|
|
21
|
+
subscribedCharacteristics: string[];
|
|
20
22
|
constructor(props?: BleDeviceConstructProps);
|
|
21
23
|
logEvent(event: any): void;
|
|
22
24
|
setInterface(ble: BleInterfaceClass): void;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
cleanupListeners(): void;
|
|
26
|
+
onDisconnect(): void;
|
|
25
27
|
waitForConnectFinished(timeout: any): Promise<unknown>;
|
|
26
28
|
hasService(serviceUuid: any): boolean;
|
|
27
29
|
init(): Promise<boolean>;
|
|
30
|
+
connectPeripheral(peripheral: BlePeripheral): Promise<void>;
|
|
31
|
+
subscribeAll(conn?: BlePeripheralConnector): Promise<void>;
|
|
28
32
|
connect(props?: ConnectProps): Promise<boolean>;
|
|
29
33
|
disconnect(): Promise<boolean>;
|
|
30
34
|
abstract getProfile(): string;
|
|
31
35
|
abstract onData(characteristic: string, data: Buffer): void;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
write(characteristicUuid: string, data: Buffer, withoutResponse: boolean): Promise<boolean>;
|
|
35
|
-
read(characteristicUuid: string): Promise<Buffer>;
|
|
36
|
+
write(characteristicUuid: string, data: Buffer, withoutResponse?: boolean): Promise<boolean>;
|
|
37
|
+
read(characteristicUuid: string): Promise<Uint8Array>;
|
|
36
38
|
getDeviceInfo(): Promise<BleDeviceInfo>;
|
|
37
39
|
}
|
|
38
40
|
export {};
|
package/lib/ble/ble-device.js
CHANGED
|
@@ -24,7 +24,8 @@ class BleDevice extends ble_1.BleDeviceClass {
|
|
|
24
24
|
this.services = props.services;
|
|
25
25
|
this.ble = props.ble;
|
|
26
26
|
this.characteristics = [];
|
|
27
|
-
this.
|
|
27
|
+
this.subscribedCharacteristics = [];
|
|
28
|
+
this.isInitialized = false;
|
|
28
29
|
if (props.peripheral) {
|
|
29
30
|
const { id, address, advertisement, state } = props.peripheral;
|
|
30
31
|
this.peripheral = props.peripheral;
|
|
@@ -57,14 +58,14 @@ class BleDevice extends ble_1.BleDeviceClass {
|
|
|
57
58
|
this.characteristics = [];
|
|
58
59
|
}
|
|
59
60
|
else {
|
|
61
|
+
const connector = this.ble.getConnector(this.peripheral);
|
|
60
62
|
this.characteristics.forEach(c => {
|
|
61
|
-
|
|
62
|
-
c.removeAllListeners('data');
|
|
63
|
+
connector.removeAllListeners((0, ble_1.uuid)(c.uuid));
|
|
63
64
|
});
|
|
64
|
-
this.isSubscribed = false;
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
onDisconnect() {
|
|
68
|
+
this.logEvent({ message: 'device disconnected', address: this.address, profile: this.getProfile() });
|
|
68
69
|
this.state = "disconnected";
|
|
69
70
|
if (!this.connectState.isDisconnecting) {
|
|
70
71
|
this.peripheral.state = 'disconnected';
|
|
@@ -98,82 +99,78 @@ class BleDevice extends ble_1.BleDeviceClass {
|
|
|
98
99
|
return this.services && this.services.find(s => s === serviceUuid || (0, ble_1.uuid)(serviceUuid)) !== undefined;
|
|
99
100
|
}
|
|
100
101
|
init() {
|
|
101
|
-
|
|
102
|
+
if (this.isInitialized)
|
|
103
|
+
return Promise.resolve(true);
|
|
104
|
+
return this.getDeviceInfo().then(() => {
|
|
105
|
+
this.emit('deviceInfo', this.deviceInfo);
|
|
106
|
+
this.isInitialized = true;
|
|
107
|
+
return true;
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
connectPeripheral(peripheral) {
|
|
111
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
112
|
+
this.connectState.isConnecting = true;
|
|
113
|
+
try {
|
|
114
|
+
const connector = this.ble.getConnector(peripheral);
|
|
115
|
+
yield connector.connect();
|
|
116
|
+
yield connector.initialize();
|
|
117
|
+
yield this.subscribeAll(connector);
|
|
118
|
+
this.connectState.isConnected = true;
|
|
119
|
+
this.state = "connected";
|
|
120
|
+
this.emit('connected');
|
|
121
|
+
yield this.init();
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
this.logEvent({ message: 'Error', fn: 'connectPeripheral()', error: err.message, stack: err.stack });
|
|
125
|
+
}
|
|
126
|
+
this.connectState.isConnecting = false;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
subscribeAll(conn) {
|
|
130
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
131
|
+
try {
|
|
132
|
+
const connector = conn || this.ble.getConnector(this.peripheral);
|
|
133
|
+
const subscribed = yield connector.subscribeAll((uuid, data) => { this.onData(uuid, data); });
|
|
134
|
+
subscribed.forEach(c => this.subscribedCharacteristics.push(c));
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
this.logEvent({ message: 'Error', fn: 'subscribeAll()', error: err.message, stack: err.stack });
|
|
138
|
+
}
|
|
139
|
+
});
|
|
102
140
|
}
|
|
103
141
|
connect(props) {
|
|
104
142
|
return __awaiter(this, void 0, void 0, function* () {
|
|
105
|
-
const connectPeripheral = (peripheral) => __awaiter(this, void 0, void 0, function* () {
|
|
106
|
-
this.connectState.isConnecting = true;
|
|
107
|
-
const connected = this.ble.findConnected(peripheral);
|
|
108
|
-
if (peripheral.state !== 'connected') {
|
|
109
|
-
this.isSubscribed = false;
|
|
110
|
-
try {
|
|
111
|
-
yield peripheral.connectAsync();
|
|
112
|
-
}
|
|
113
|
-
catch (err) {
|
|
114
|
-
this.logEvent({ message: 'cannot connect', error: err.message || err });
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
try {
|
|
118
|
-
if (!this.characteristics)
|
|
119
|
-
this.characteristics = [];
|
|
120
|
-
if (!connected) {
|
|
121
|
-
if (!this.characteristics || this.characteristics.length === 0) {
|
|
122
|
-
this.logEvent({ message: 'connect: discover characteristics start' });
|
|
123
|
-
const res = yield peripheral.discoverSomeServicesAndCharacteristicsAsync([], []);
|
|
124
|
-
const { characteristics } = res;
|
|
125
|
-
this.logEvent({ message: 'connect: discover characteristics result',
|
|
126
|
-
result: characteristics.map(c => ({ uuid: (0, ble_1.uuid)(c.uuid), properties: c.properties.join(','), service: (0, ble_1.uuid)(c._serviceUuid) }))
|
|
127
|
-
});
|
|
128
|
-
this.characteristics = characteristics;
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
134
|
-
this.characteristics = connected.characteristics;
|
|
135
|
-
}
|
|
136
|
-
let device;
|
|
137
|
-
if (!connected) {
|
|
138
|
-
this.ble.addConnectedDevice(this);
|
|
139
|
-
device = this;
|
|
140
|
-
}
|
|
141
|
-
else {
|
|
142
|
-
device = connected;
|
|
143
|
-
}
|
|
144
|
-
this.peripheral.once('disconnect', () => { this.onDisconnect(); });
|
|
145
|
-
yield this.subscribeAll(device);
|
|
146
|
-
this.connectState.isConnecting = false;
|
|
147
|
-
this.connectState.isConnected = true;
|
|
148
|
-
this.state = "connected";
|
|
149
|
-
this.emit('connected');
|
|
150
|
-
this.init().then((isInitialized) => {
|
|
151
|
-
if (isInitialized)
|
|
152
|
-
this.emit('deviceInfo', this.deviceInfo);
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
catch (err) {
|
|
156
|
-
this.logEvent({ message: 'cannot connect', error: err.message || err });
|
|
157
|
-
this.connectState.isConnecting = false;
|
|
158
|
-
this.connectState.isConnected = false;
|
|
159
|
-
}
|
|
160
|
-
});
|
|
161
143
|
try {
|
|
144
|
+
this.logEvent({ message: 'connect', address: this.peripheral ? this.peripheral.address : this.address, state: this.connectState });
|
|
162
145
|
if (this.connectState.isConnecting) {
|
|
163
146
|
yield this.waitForConnectFinished(CONNECT_WAIT_TIMEOUT);
|
|
164
147
|
}
|
|
165
148
|
if (this.connectState.isConnected) {
|
|
166
|
-
|
|
149
|
+
try {
|
|
167
150
|
yield this.subscribeAll();
|
|
151
|
+
yield this.init();
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
this.logEvent({ message: 'cannot reconnect', error: err.message || err });
|
|
155
|
+
return false;
|
|
168
156
|
}
|
|
169
157
|
return true;
|
|
170
158
|
}
|
|
171
159
|
this.connectState.isConnecting = true;
|
|
160
|
+
if (!this.peripheral) {
|
|
161
|
+
const { id, name, address } = this;
|
|
162
|
+
try {
|
|
163
|
+
this.peripheral = this.ble.findPeripheral({ id, name, address });
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
console.log('~~~ error', err);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
172
169
|
if (this.peripheral) {
|
|
173
170
|
const { id, address, advertisement } = this.peripheral;
|
|
174
171
|
const name = advertisement === null || advertisement === void 0 ? void 0 : advertisement.localName;
|
|
175
172
|
this.logEvent({ message: 'connect requested', mode: 'peripheral', device: { id, name, address: address } });
|
|
176
|
-
yield connectPeripheral(this.peripheral);
|
|
173
|
+
yield this.connectPeripheral(this.peripheral);
|
|
177
174
|
this.logEvent({ message: 'connect result: success', mode: 'peripheral', device: { id, name, address } });
|
|
178
175
|
return true;
|
|
179
176
|
}
|
|
@@ -189,7 +186,7 @@ class BleDevice extends ble_1.BleDeviceClass {
|
|
|
189
186
|
const devices = yield this.ble.scan({ requested: this });
|
|
190
187
|
if (devices && devices.length > 0) {
|
|
191
188
|
this.peripheral = devices[0].peripheral;
|
|
192
|
-
yield connectPeripheral(this.peripheral);
|
|
189
|
+
yield this.connectPeripheral(this.peripheral);
|
|
193
190
|
this.logEvent({ message: 'connect result: success', mode: 'device', device: { id, name, address } });
|
|
194
191
|
return true;
|
|
195
192
|
}
|
|
@@ -238,64 +235,28 @@ class BleDevice extends ble_1.BleDeviceClass {
|
|
|
238
235
|
}
|
|
239
236
|
});
|
|
240
237
|
}
|
|
241
|
-
|
|
238
|
+
write(characteristicUuid, data, withoutResponse = false) {
|
|
242
239
|
return __awaiter(this, void 0, void 0, function* () {
|
|
243
|
-
if (this.
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
if (isNotify) {
|
|
251
|
-
c.on('data', (data, _isNotification) => {
|
|
252
|
-
this.onData((0, ble_1.uuid)(c.uuid), data);
|
|
253
|
-
});
|
|
254
|
-
if (!device.isSubscribed) {
|
|
255
|
-
this.logEvent({ message: 'subscribe', device: this.name, address: this.address, service: c._serviceUuid, characteristic: c.uuid });
|
|
256
|
-
try {
|
|
257
|
-
yield this.subscribe(c.uuid);
|
|
258
|
-
}
|
|
259
|
-
catch (err) {
|
|
260
|
-
this.logEvent({ message: 'cannot subscribe', device: this.name, address: this.address, service: c._serviceUuid, characteristic: c.uuid, error: err.message || err });
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
catch (err) {
|
|
266
|
-
console.log('~~~ error', err);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
this.isSubscribed = true;
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
subscribe(characteristicUuid) {
|
|
273
|
-
return new Promise((resolve, reject) => {
|
|
274
|
-
const characteristic = this.characteristics.find(c => c.uuid === characteristicUuid || (0, ble_1.uuid)(c.uuid) === characteristicUuid);
|
|
275
|
-
if (!characteristic) {
|
|
276
|
-
reject(new Error('Characteristic not found'));
|
|
277
|
-
return;
|
|
240
|
+
if (this.subscribedCharacteristics.find(c => c === characteristicUuid) === undefined) {
|
|
241
|
+
const connector = this.ble.getConnector(this.peripheral);
|
|
242
|
+
connector.on(characteristicUuid, (uuid, data) => {
|
|
243
|
+
this.onData(uuid, data);
|
|
244
|
+
});
|
|
245
|
+
yield connector.subscribe(characteristicUuid);
|
|
246
|
+
this.subscribedCharacteristics.push(characteristicUuid);
|
|
278
247
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
reject(new Error('Characteristic not found'));
|
|
292
|
-
return;
|
|
293
|
-
}
|
|
294
|
-
characteristic.write(data, withoutResponse, (err) => {
|
|
295
|
-
if (err)
|
|
296
|
-
reject(err);
|
|
297
|
-
else
|
|
298
|
-
resolve(true);
|
|
248
|
+
return new Promise((resolve, reject) => {
|
|
249
|
+
const characteristic = this.characteristics.find(c => c.uuid === characteristicUuid || (0, ble_1.uuid)(c.uuid) === characteristicUuid);
|
|
250
|
+
if (!characteristic) {
|
|
251
|
+
reject(new Error('Characteristic not found'));
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
characteristic.write(data, withoutResponse, (err) => {
|
|
255
|
+
if (err)
|
|
256
|
+
reject(err);
|
|
257
|
+
else
|
|
258
|
+
resolve(true);
|
|
259
|
+
});
|
|
299
260
|
});
|
|
300
261
|
});
|
|
301
262
|
}
|
|
@@ -322,7 +283,8 @@ class BleDevice extends ble_1.BleDeviceClass {
|
|
|
322
283
|
const readValue = (c) => __awaiter(this, void 0, void 0, function* () {
|
|
323
284
|
try {
|
|
324
285
|
const b = yield this.read(c);
|
|
325
|
-
|
|
286
|
+
const buffer = b ? Buffer.from(b) : undefined;
|
|
287
|
+
return buffer ? buffer.toString() : undefined;
|
|
326
288
|
}
|
|
327
289
|
catch (_a) {
|
|
328
290
|
return undefined;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import CyclingMode, { CyclingModeProperty, IncyclistBikeData, UpdateRequest } from "../CyclingMode";
|
|
2
|
+
import PowerBasedCyclingModeBase from "../modes/power-base";
|
|
3
|
+
import { FmAdapter } from "./fm";
|
|
4
|
+
export declare type ERGEvent = {
|
|
5
|
+
rpmUpdated?: boolean;
|
|
6
|
+
gearUpdated?: boolean;
|
|
7
|
+
starting?: boolean;
|
|
8
|
+
tsStart?: number;
|
|
9
|
+
};
|
|
10
|
+
export default class ERGCyclingMode extends PowerBasedCyclingModeBase implements CyclingMode {
|
|
11
|
+
prevRequest: UpdateRequest;
|
|
12
|
+
hasBikeUpdate: boolean;
|
|
13
|
+
chain: number[];
|
|
14
|
+
cassette: number[];
|
|
15
|
+
event: ERGEvent;
|
|
16
|
+
constructor(adapter: FmAdapter, props?: any);
|
|
17
|
+
getName(): string;
|
|
18
|
+
getDescription(): string;
|
|
19
|
+
getProperties(): CyclingModeProperty[];
|
|
20
|
+
getProperty(name: string): CyclingModeProperty;
|
|
21
|
+
getBikeInitRequest(): UpdateRequest;
|
|
22
|
+
sendBikeUpdate(request: UpdateRequest): UpdateRequest;
|
|
23
|
+
updateData(bikeData: IncyclistBikeData): any;
|
|
24
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const CyclingMode_1 = require("../CyclingMode");
|
|
7
|
+
const power_base_1 = __importDefault(require("../modes/power-base"));
|
|
8
|
+
const config = {
|
|
9
|
+
name: "ERG",
|
|
10
|
+
description: "Calculates speed based on power and slope. Power targets are set by workout or remain stable throughout the workout",
|
|
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 },
|
|
14
|
+
]
|
|
15
|
+
};
|
|
16
|
+
class ERGCyclingMode extends power_base_1.default {
|
|
17
|
+
constructor(adapter, props) {
|
|
18
|
+
super(adapter, props);
|
|
19
|
+
this.hasBikeUpdate = false;
|
|
20
|
+
this.event = {};
|
|
21
|
+
this.initLogger('ERGMode');
|
|
22
|
+
}
|
|
23
|
+
getName() {
|
|
24
|
+
return config.name;
|
|
25
|
+
}
|
|
26
|
+
getDescription() {
|
|
27
|
+
return config.description;
|
|
28
|
+
}
|
|
29
|
+
getProperties() {
|
|
30
|
+
return config.properties;
|
|
31
|
+
}
|
|
32
|
+
getProperty(name) {
|
|
33
|
+
return config.properties.find(p => p.name === name);
|
|
34
|
+
}
|
|
35
|
+
getBikeInitRequest() {
|
|
36
|
+
const startPower = this.getSetting('startPower');
|
|
37
|
+
return { targetPower: startPower };
|
|
38
|
+
}
|
|
39
|
+
sendBikeUpdate(request) {
|
|
40
|
+
const getData = () => {
|
|
41
|
+
if (!this.data)
|
|
42
|
+
return {};
|
|
43
|
+
const { pedalRpm, slope, power, speed } = this.data;
|
|
44
|
+
return { pedalRpm, slope, power, speed };
|
|
45
|
+
};
|
|
46
|
+
this.logger.logEvent({ message: "processing update request", request, prev: this.prevRequest, data: getData(), event: this.event });
|
|
47
|
+
let newRequest = {};
|
|
48
|
+
try {
|
|
49
|
+
if (!request || request.reset || Object.keys(request).length === 0) {
|
|
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));
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
this.logger.logEvent({ message: "error", fn: 'sendBikeUpdate()', error: err.message || err, stack: err.stack });
|
|
100
|
+
}
|
|
101
|
+
return newRequest;
|
|
102
|
+
}
|
|
103
|
+
updateData(bikeData) {
|
|
104
|
+
const prevData = JSON.parse(JSON.stringify(this.data || {}));
|
|
105
|
+
const prevSpeed = prevData.speed;
|
|
106
|
+
const prevRequest = this.prevRequest || {};
|
|
107
|
+
const data = this.data || {};
|
|
108
|
+
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
|
+
try {
|
|
115
|
+
const rpm = bikeData.pedalRpm || 0;
|
|
116
|
+
let power = bikeData.power || 0;
|
|
117
|
+
const slope = (prevData.slope !== undefined ? prevData.slope : prevRequest.slope || 0);
|
|
118
|
+
const distanceInternal = prevData.distanceInternal || 0;
|
|
119
|
+
if (!bikeData.pedalRpm || bikeData.isPedalling === false) {
|
|
120
|
+
power = 0;
|
|
121
|
+
}
|
|
122
|
+
const m = this.getWeight();
|
|
123
|
+
const t = this.getTimeSinceLastUpdate();
|
|
124
|
+
const { speed, distance } = this.calculateSpeedAndDistance(power, slope, m, t, { bikeType });
|
|
125
|
+
data.speed = parseFloat(speed.toFixed(1));
|
|
126
|
+
data.power = Math.round(power);
|
|
127
|
+
data.distanceInternal = Math.round(distanceInternal + distance);
|
|
128
|
+
data.slope = slope;
|
|
129
|
+
data.pedalRpm = rpm;
|
|
130
|
+
if (data.time !== undefined && !(this.event.starting && !bikeData.pedalRpm))
|
|
131
|
+
data.time += t;
|
|
132
|
+
else
|
|
133
|
+
data.time = 0;
|
|
134
|
+
data.heartrate = bikeData.heartrate;
|
|
135
|
+
data.isPedalling = bikeData.isPedalling;
|
|
136
|
+
if (rpm && rpm !== prevData.pedalRpm) {
|
|
137
|
+
this.event.rpmUpdated = true;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
this.logger.logEvent({ message: 'error', fn: 'updateData()', error: err.message || err });
|
|
142
|
+
}
|
|
143
|
+
this.logger.logEvent({ message: "updateData result", data, bikeData, prevRequest, prevSpeed });
|
|
144
|
+
this.data = data;
|
|
145
|
+
return data;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
exports.default = ERGCyclingMode;
|
|
@@ -65,6 +65,12 @@ export default class BleInterface extends BleInterfaceClass {
|
|
|
65
65
|
getServicesFromDevice(device: BleDeviceClass): string[];
|
|
66
66
|
waitForConnectFinished(timeout: any): Promise<unknown>;
|
|
67
67
|
addPeripheralToCache(peripheral: any, props?: {}): void;
|
|
68
|
+
getConnector(peripheral: BlePeripheral): any;
|
|
69
|
+
findPeripheral(peripheral: BlePeripheral | {
|
|
70
|
+
id?: string;
|
|
71
|
+
address?: string;
|
|
72
|
+
name?: string;
|
|
73
|
+
}): BlePeripheral;
|
|
68
74
|
getCharacteristics(peripheral: any): Promise<any>;
|
|
69
75
|
getDeviceClasses(peripheral: any, props?: {
|
|
70
76
|
deviceTypes?: (typeof BleDeviceClass)[];
|
|
@@ -77,6 +83,13 @@ export default class BleInterface extends BleInterfaceClass {
|
|
|
77
83
|
stopScan(): Promise<boolean>;
|
|
78
84
|
isScanning(): boolean;
|
|
79
85
|
addConnectedDevice(device: BleDeviceClass): void;
|
|
86
|
+
addDeviceToCache(device: BleDeviceClass, isConnected: boolean): void;
|
|
80
87
|
findConnected(device: BleDeviceClass | BlePeripheral): BleDeviceClass;
|
|
88
|
+
findDeviceInCache(device: {
|
|
89
|
+
id?: string;
|
|
90
|
+
address?: string;
|
|
91
|
+
name?: string;
|
|
92
|
+
profile: string;
|
|
93
|
+
}): BleDeviceClass;
|
|
81
94
|
removeConnectedDevice(device: BleDeviceClass): void;
|
|
82
95
|
}
|