incyclist-devices 1.4.92 → 1.4.94

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 CHANGED
@@ -55,7 +55,8 @@ export default class IncyclistDevice implements DeviceAdapter {
55
55
  detected: boolean;
56
56
  selected: boolean;
57
57
  onDataFn: OnDeviceDataCallback;
58
- constructor(proto: DeviceProtocol);
58
+ settings: any;
59
+ constructor(proto: DeviceProtocol, settings?: any);
59
60
  isBike(): boolean;
60
61
  isPower(): boolean;
61
62
  isHrm(): boolean;
@@ -66,7 +67,10 @@ export default class IncyclistDevice implements DeviceAdapter {
66
67
  getPort(): string;
67
68
  getProtocol(): DeviceProtocol;
68
69
  getProtocolName(): string | undefined;
70
+ getSupportedCyclingModes(): any[];
71
+ getDefaultCyclingMode(): CyclingMode;
69
72
  setCyclingMode(mode: CyclingMode | string, settings?: any): void;
73
+ getCyclingMode(): CyclingMode;
70
74
  setIgnoreHrm(ignore: any): void;
71
75
  setIgnorePower(ignore: any): void;
72
76
  setIgnoreBike(ignore: any): void;
package/lib/Device.js CHANGED
@@ -4,11 +4,12 @@ exports.DEFAULT_USER_WEIGHT = exports.DEFAULT_BIKE_WEIGHT = void 0;
4
4
  exports.DEFAULT_BIKE_WEIGHT = 10;
5
5
  exports.DEFAULT_USER_WEIGHT = 75;
6
6
  class IncyclistDevice {
7
- constructor(proto) {
7
+ constructor(proto, settings) {
8
8
  this.protocol = proto;
9
9
  this.detected = false;
10
10
  this.selected = false;
11
11
  this.onDataFn = undefined;
12
+ this.settings = settings || {};
12
13
  }
13
14
  isBike() { throw new Error('not implemented'); }
14
15
  isPower() { throw new Error('not implemented'); }
@@ -22,7 +23,30 @@ class IncyclistDevice {
22
23
  getProtocolName() {
23
24
  return this.protocol ? this.protocol.getName() : undefined;
24
25
  }
25
- setCyclingMode(mode, settings) { }
26
+ getSupportedCyclingModes() { throw new Error('not implemented'); }
27
+ getDefaultCyclingMode() { throw new Error('not implemented'); }
28
+ setCyclingMode(mode, settings) {
29
+ let selectedMode;
30
+ if (typeof mode === 'string') {
31
+ const supported = this.getSupportedCyclingModes();
32
+ const CyclingModeClass = supported.find(M => { const m = new M(this); return m.getName() === mode; });
33
+ if (CyclingModeClass) {
34
+ this.settings.cyclingMode = new CyclingModeClass(this, settings);
35
+ return;
36
+ }
37
+ selectedMode = this.getDefaultCyclingMode();
38
+ }
39
+ else {
40
+ selectedMode = mode;
41
+ }
42
+ this.settings.cyclingMode = selectedMode;
43
+ this.settings.cyclingMode.setSettings(settings);
44
+ }
45
+ getCyclingMode() {
46
+ if (!this.settings.cyclingMode)
47
+ this.setCyclingMode(this.getDefaultCyclingMode());
48
+ return this.settings.cyclingMode;
49
+ }
26
50
  setIgnoreHrm(ignore) { }
27
51
  setIgnorePower(ignore) { }
28
52
  setIgnoreBike(ignore) { }
@@ -36,6 +36,7 @@ export default class AntFEAdapter extends AntAdapter {
36
36
  getDisplayName(): string;
37
37
  getCyclingMode(): CyclingMode;
38
38
  getDefaultCyclingMode(): CyclingMode;
39
+ getSupportedCyclingModes(): any[];
39
40
  onAttached(): void;
40
41
  getLogData(data: any, excludeList: any): any;
41
42
  onDeviceData(deviceData: AntPwrData): void;
@@ -58,6 +58,9 @@ class AntFEAdapter extends AntAdapter_1.default {
58
58
  getDefaultCyclingMode() {
59
59
  return new power_meter_1.default(this);
60
60
  }
61
+ getSupportedCyclingModes() {
62
+ return [power_meter_1.default];
63
+ }
61
64
  onAttached() {
62
65
  this.logger.logEvent({ message: 'Device connected' });
63
66
  this.connected = true;
@@ -35,6 +35,7 @@ export declare abstract class BleDevice extends BleDeviceClass {
35
35
  workerIv: NodeJS.Timeout;
36
36
  prevMessages: MessageLog[];
37
37
  constructor(props?: BleDeviceConstructProps);
38
+ getServices(): string[];
38
39
  logEvent(event: any): void;
39
40
  setLogger(logger: EventLogger): void;
40
41
  setInterface(ble: BleInterfaceClass): void;
@@ -51,7 +52,7 @@ export declare abstract class BleDevice extends BleDeviceClass {
51
52
  disconnect(): Promise<boolean>;
52
53
  abstract getProfile(): string;
53
54
  checkForDuplicate(characteristic: string, data: Buffer): boolean;
54
- onData(characteristic: string, data: Buffer): void;
55
+ onData(characteristic: string, _data: Buffer): boolean;
55
56
  timeoutCheck(): void;
56
57
  startWorker(): void;
57
58
  stopWorker(): void;
@@ -47,13 +47,14 @@ class BleDevice extends ble_1.BleDeviceClass {
47
47
  this.logger = new gd_eventlog_1.EventLogger('BleDevice');
48
48
  }
49
49
  }
50
+ getServices() {
51
+ return this.services;
52
+ }
50
53
  logEvent(event) {
51
54
  if (this.logger) {
52
55
  this.logger.logEvent(event);
53
56
  }
54
- if (process.env.BLE_DEBUG) {
55
- console.log('~~~BLE:', event);
56
- }
57
+ console.log('~~~BLE:', event);
57
58
  }
58
59
  setLogger(logger) {
59
60
  this.logger = logger;
@@ -80,8 +81,12 @@ class BleDevice extends ble_1.BleDeviceClass {
80
81
  this.state = "disconnected";
81
82
  if (!this.connectState.isDisconnecting) {
82
83
  this.peripheral.state = 'disconnected';
84
+ this.connectState.isConnecting = false;
83
85
  this.connectState.isConnected = false;
84
- this.connect();
86
+ this.cleanupListeners();
87
+ this.subscribedCharacteristics = [];
88
+ this.ble.onDisconnect(this.peripheral);
89
+ this.connect({ reconnect: true });
85
90
  }
86
91
  this.emit('disconnected');
87
92
  }
@@ -128,6 +133,7 @@ class BleDevice extends ble_1.BleDeviceClass {
128
133
  this.connectState.isConnecting = true;
129
134
  try {
130
135
  const connector = this.ble.getConnector(peripheral);
136
+ connector.on('disconnect', () => { this.onDisconnect(); });
131
137
  yield connector.connect();
132
138
  yield connector.initialize();
133
139
  yield this.subscribeAll(connector);
@@ -156,9 +162,10 @@ class BleDevice extends ble_1.BleDeviceClass {
156
162
  }
157
163
  connect(props) {
158
164
  return __awaiter(this, void 0, void 0, function* () {
165
+ const { reconnect } = props || {};
159
166
  try {
160
- this.logEvent({ message: 'connect', address: this.peripheral ? this.peripheral.address : this.address, state: this.connectState });
161
- if (this.connectState.isConnecting) {
167
+ this.logEvent({ message: reconnect ? 'reconnect' : 'connect', address: this.peripheral ? this.peripheral.address : this.address, state: this.connectState });
168
+ if (!reconnect && this.connectState.isConnecting) {
162
169
  yield this.waitForConnectFinished(CONNECT_WAIT_TIMEOUT);
163
170
  }
164
171
  if (this.connectState.isConnected) {
@@ -238,6 +245,8 @@ class BleDevice extends ble_1.BleDeviceClass {
238
245
  }
239
246
  if (!this.connectState.isConnecting && !this.connectState.isConnected) {
240
247
  this.connectState.isDisconnecting = false;
248
+ this.connectState.isConnecting = false;
249
+ this.connectState.isConnected = false;
241
250
  this.logEvent({ message: 'disconnect result: success', device: { id, name, address } });
242
251
  return true;
243
252
  }
@@ -245,13 +254,17 @@ class BleDevice extends ble_1.BleDeviceClass {
245
254
  this.cleanupListeners();
246
255
  setTimeout(() => { this.connectState.isDisconnecting = false; }, 1000);
247
256
  this.logEvent({ message: 'disconnect result: unclear - connect ongoing', device: { id, name, address } });
257
+ this.connectState.isConnecting = false;
258
+ this.connectState.isConnected = false;
248
259
  return true;
249
260
  }
250
261
  if (this.connectState.isConnected) {
251
262
  this.ble.removeConnectedDevice(this);
252
263
  this.cleanupListeners();
253
- setTimeout(() => { this.connectState.isDisconnecting = false; }, 1000);
254
264
  this.logEvent({ message: 'disconnect result: success', device: { id, name, address } });
265
+ this.connectState.isDisconnecting = false;
266
+ this.connectState.isConnecting = false;
267
+ this.connectState.isConnected = false;
255
268
  return true;
256
269
  }
257
270
  });
@@ -273,10 +286,11 @@ class BleDevice extends ble_1.BleDeviceClass {
273
286
  }
274
287
  return false;
275
288
  }
276
- onData(characteristic, data) {
289
+ onData(characteristic, _data) {
290
+ const data = Buffer.from(_data);
277
291
  const isDuplicate = this.checkForDuplicate(characteristic, data);
278
292
  if (isDuplicate) {
279
- return;
293
+ return false;
280
294
  }
281
295
  this.logEvent({ message: 'got data', characteristic, data: data.toString('hex'), writeQueue: this.writeQueue.length });
282
296
  if (this.writeQueue.length > 0) {
@@ -286,8 +300,10 @@ class BleDevice extends ble_1.BleDeviceClass {
286
300
  this.writeQueue.splice(writeIdx, 1);
287
301
  if (writeItem.resolve)
288
302
  writeItem.resolve(data);
303
+ return false;
289
304
  }
290
305
  }
306
+ return true;
291
307
  }
292
308
  timeoutCheck() {
293
309
  const now = Date.now();
@@ -321,6 +337,8 @@ class BleDevice extends ble_1.BleDeviceClass {
321
337
  write(characteristicUuid, data, props) {
322
338
  return __awaiter(this, void 0, void 0, function* () {
323
339
  try {
340
+ if (!this.isConnected())
341
+ throw new Error('not connected');
324
342
  const { withoutResponse, timeout } = props || {};
325
343
  const connector = this.ble.getConnector(this.peripheral);
326
344
  const isAlreadySubscribed = connector.isSubscribed(characteristicUuid);
@@ -380,6 +398,8 @@ class BleDevice extends ble_1.BleDeviceClass {
380
398
  }
381
399
  read(characteristicUuid) {
382
400
  return new Promise((resolve, reject) => {
401
+ if (!this.isConnected())
402
+ return reject(new Error('not connected'));
383
403
  const characteristic = this.characteristics.find(c => c.uuid === characteristicUuid || (0, ble_1.uuid)(c.uuid) === characteristicUuid);
384
404
  if (!characteristic) {
385
405
  reject(new Error('Characteristic not found'));
@@ -67,6 +67,7 @@ export default class BleInterface extends BleInterfaceClass {
67
67
  getServicesFromDevice(device: BleDeviceClass): string[];
68
68
  waitForConnectFinished(timeout: any): Promise<unknown>;
69
69
  addPeripheralToCache(peripheral: any, props?: {}): void;
70
+ onDisconnect(peripheral: any): void;
70
71
  getConnector(peripheral: BlePeripheral): any;
71
72
  findPeripheral(peripheral: BlePeripheral | {
72
73
  id?: string;
@@ -290,6 +290,11 @@ class BleInterface extends ble_1.BleInterfaceClass {
290
290
  const connector = info && info.connector ? info.connector : new ble_peripheral_1.default(this, peripheral);
291
291
  this.peripheralCache.push(Object.assign({ address: peripheral.address, ts: Date.now(), peripheral, connector }, props));
292
292
  }
293
+ onDisconnect(peripheral) {
294
+ const idx = this.peripheralCache.findIndex(i => i.address === peripheral.address);
295
+ if (idx !== -1)
296
+ this.peripheralCache.splice(idx, 1);
297
+ }
293
298
  getConnector(peripheral) {
294
299
  const info = this.peripheralCache.find(i => i.address === peripheral.address);
295
300
  if (!info) {
@@ -411,7 +416,8 @@ class BleInterface extends ble_1.BleInterfaceClass {
411
416
  if (devices && devices.length > 0) {
412
417
  for (let i = 0; i < devices.length; i++) {
413
418
  const idx = this.devices.push({ device: devices[i], isConnected: false }) - 1;
414
- yield devices[i].connect();
419
+ if (!devices[i].isConnected())
420
+ yield devices[i].connect();
415
421
  this.devices[idx].isConnected = true;
416
422
  }
417
423
  }
@@ -558,14 +564,14 @@ class BleInterface extends ble_1.BleInterfaceClass {
558
564
  });
559
565
  };
560
566
  const onPeripheralFound = (peripheral, fromCache = false) => __awaiter(this, void 0, void 0, function* () {
567
+ if (!peripheral || !peripheral.advertisement || !peripheral.advertisement.localName || !peripheral.advertisement.serviceUuids || peripheral.advertisement.serviceUuids.length === 0)
568
+ return;
561
569
  if (fromCache)
562
570
  this.logEvent({ message: 'adding from Cache', peripheral: peripheral.address });
563
571
  else {
564
572
  const { id, name, address, advertisement = {} } = peripheral;
565
573
  this.logEvent({ message: 'BLE scan: found device', peripheral: { id, name, address, services: advertisement.serviceUuids } });
566
574
  }
567
- if (!peripheral || !peripheral.advertisement || !peripheral.advertisement.serviceUuids || peripheral.advertisement.serviceUuids.length === 0)
568
- return;
569
575
  if (peripheral.address === undefined || peripheral.address === '')
570
576
  peripheral.address = peripheral.id;
571
577
  const isPeripheralProcessed = peripheralsProcessed.find(p => p === peripheral.address) !== undefined;
@@ -638,7 +644,14 @@ class BleInterface extends ble_1.BleInterfaceClass {
638
644
  this.peripheralCache.forEach(i => {
639
645
  onPeripheralFound(i.peripheral, true);
640
646
  });
641
- bleBinding.startScanning([], true, (err) => {
647
+ let services = [];
648
+ if (scanForDevice) {
649
+ if (props.requested instanceof ble_1.BleDeviceClass) {
650
+ const device = props.requested;
651
+ services = (device.getServices()) || [];
652
+ }
653
+ }
654
+ bleBinding.startScanning(services, false, (err) => {
642
655
  if (err) {
643
656
  this.logEvent({ message: `${opStr} result: error`, requested: scanForDevice ? { name, address, profile } : undefined, error: err.message });
644
657
  this.scanState.isScanning = false;
@@ -24,7 +24,7 @@ export default class BlePeripheralConnector {
24
24
  initialize(enforce?: boolean): Promise<void>;
25
25
  isSubscribed(characteristicUuid: string): boolean;
26
26
  subscribeAll(callback: (characteristicUuid: string, data: any) => void): Promise<string[]>;
27
- subscribe(characteristicUuid: string): Promise<boolean>;
27
+ subscribe(characteristicUuid: string, timeout?: number): Promise<boolean>;
28
28
  onData(characteristicUuid: string, data: any): void;
29
29
  on(characteristicUuid: string, callback: (characteristicUuid: string, data: any) => void): void;
30
30
  off(characteristicUuid: string, callback: (characteristicUuid: string, data: any) => void): void;
@@ -22,7 +22,7 @@ class BlePeripheralConnector {
22
22
  this.emitter = new events_1.default();
23
23
  if (!this.peripheral || !this.ble)
24
24
  throw new Error('Illegal Arguments');
25
- this.state = { isConnected: false, isConnecting: false, isInitialized: false, isInitializing: false, isSubscribing: false };
25
+ this.state = { subscribed: [], isConnected: false, isConnecting: false, isInitialized: false, isInitializing: false, isSubscribing: false };
26
26
  this.services = undefined;
27
27
  this.characteristics = undefined;
28
28
  this.logger = new gd_eventlog_1.EventLogger('BLE');
@@ -31,7 +31,9 @@ class BlePeripheralConnector {
31
31
  if (this.logger) {
32
32
  this.logger.logEvent(event);
33
33
  }
34
- console.log('~~~BLE:', event);
34
+ if (process.env.BLE_DEBUG) {
35
+ console.log('~~~BLE:', event);
36
+ }
35
37
  }
36
38
  connect() {
37
39
  return __awaiter(this, void 0, void 0, function* () {
@@ -40,9 +42,10 @@ class BlePeripheralConnector {
40
42
  this.logEvent({ message: 'connect', peripheral: this.peripheral.address, state: this.state });
41
43
  this.state.isConnecting = true;
42
44
  try {
45
+ this.peripheral.on('connect', () => { console.log('~~~ peripheral connected', this.peripheral); });
46
+ this.peripheral.on('disconnect', () => { this.onDisconnect(); });
43
47
  if (!this.state.isConnected || (this.peripheral && this.peripheral.state !== 'connected')) {
44
48
  yield this.peripheral.connectAsync();
45
- this.peripheral.once('disconnect', this.onDisconnect.bind(this));
46
49
  }
47
50
  this.state.isConnected = this.peripheral.state === 'connected';
48
51
  }
@@ -58,9 +61,12 @@ class BlePeripheralConnector {
58
61
  });
59
62
  }
60
63
  onDisconnect() {
64
+ this.peripheral.removeAllListeners('connect');
65
+ this.peripheral.removeAllListeners('disconnect');
61
66
  this.logEvent({ message: 'onDisconnected', peripheral: this.peripheral.address, state: this.state });
62
67
  this.state.isConnected = false;
63
- this.reconnect();
68
+ this.state.isConnecting = false;
69
+ this.emitter.emit('disconnect');
64
70
  }
65
71
  initialize(enforce = false) {
66
72
  return __awaiter(this, void 0, void 0, function* () {
@@ -108,7 +114,7 @@ class BlePeripheralConnector {
108
114
  this.logEvent({ message: 'subscribe', peripheral: this.peripheral.address, characteristic: c.uuid, uuid: (0, ble_1.uuid)(c.uuid) });
109
115
  if (this.state.subscribed.find(uuid => uuid === c.uuid) === undefined) {
110
116
  try {
111
- yield this.subscribe(c.uuid);
117
+ yield this.subscribe(c.uuid, 3000);
112
118
  subscribed.push(c.uuid);
113
119
  }
114
120
  catch (err) {
@@ -126,8 +132,8 @@ class BlePeripheralConnector {
126
132
  return subscribed;
127
133
  });
128
134
  }
129
- subscribe(characteristicUuid) {
130
- this.logEvent({ message: 'subscribe', characteristic: characteristicUuid, characteristics: this.characteristics.map(c => ({ characteristic: c.uuid, uuid: (0, ble_1.uuid)(c.uuid) })) });
135
+ subscribe(characteristicUuid, timeout) {
136
+ this.logEvent({ message: 'subscribe attempt', characteristic: characteristicUuid, characteristics: this.characteristics.map(c => ({ characteristic: c.uuid, uuid: (0, ble_1.uuid)(c.uuid) })) });
131
137
  return new Promise((resolve, reject) => {
132
138
  try {
133
139
  const characteristic = this.characteristics.find(c => (0, ble_1.uuid)(c.uuid) === (0, ble_1.uuid)(characteristicUuid) || (0, ble_1.uuid)(c.uuid) === (0, ble_1.uuid)(characteristicUuid));
@@ -135,17 +141,21 @@ class BlePeripheralConnector {
135
141
  reject(new Error('Characteristic not found'));
136
142
  return;
137
143
  }
138
- this.logEvent({ message: 'subscribe', peripheral: this.peripheral.address, characteristic: characteristic.uuid, uuid: (0, ble_1.uuid)(characteristic.uuid) });
144
+ this.logEvent({ message: 'subscribe', peripheral: this.peripheral.address, characteristic: characteristic.uuid });
139
145
  characteristic.removeAllListeners('data');
140
146
  characteristic.on('data', (data, _isNotification) => {
141
147
  this.onData((0, ble_1.uuid)(characteristicUuid), data);
142
148
  });
143
- const to = setTimeout(() => {
144
- this.logEvent({ message: 'subscribe result', characteristic: characteristicUuid, error: 'timeout' });
145
- reject(new Error('timeout'));
146
- }, 3000);
149
+ let to;
150
+ if (timeout) {
151
+ to = setTimeout(() => {
152
+ this.logEvent({ message: 'subscribe result', characteristic: characteristicUuid, error: 'timeout' });
153
+ reject(new Error('timeout'));
154
+ }, timeout);
155
+ }
147
156
  characteristic.subscribe((err) => {
148
- clearTimeout(to);
157
+ if (to)
158
+ clearTimeout(to);
149
159
  this.logEvent({ message: 'subscribe result', characteristic: characteristicUuid, error: err });
150
160
  if (err)
151
161
  reject(err);
package/lib/ble/ble.d.ts CHANGED
@@ -3,6 +3,7 @@ import EventEmitter from "events";
3
3
  import BlePeripheralConnector from "./ble-peripheral";
4
4
  export declare type ConnectProps = {
5
5
  timeout?: number;
6
+ reconnect?: boolean;
6
7
  };
7
8
  export interface BleDeviceIdentifier {
8
9
  id?: string;
@@ -42,6 +43,7 @@ export declare abstract class BleDeviceClass extends EventEmitter {
42
43
  abstract connect(props?: ConnectProps): Promise<boolean>;
43
44
  abstract disconnect(): Promise<boolean>;
44
45
  abstract getDeviceInfo(): Promise<BleDeviceInfo>;
46
+ abstract getServices(): string[];
45
47
  setCharacteristicUUIDs(uuids: string[]): void;
46
48
  }
47
49
  export interface BleWriteProps {
@@ -78,6 +80,7 @@ export declare abstract class BleInterfaceClass extends EventEmitter {
78
80
  abstract scan(props: ScanProps): Promise<BleDeviceClass[]>;
79
81
  abstract stopScan(): Promise<boolean>;
80
82
  abstract disconnect(): Promise<boolean>;
83
+ abstract onDisconnect(peripheral: BlePeripheral): void;
81
84
  abstract isScanning(): boolean;
82
85
  abstract addConnectedDevice(device: BleDeviceClass): void;
83
86
  abstract removeConnectedDevice(device: BleDeviceClass): void;
package/lib/ble/fm.d.ts CHANGED
@@ -8,6 +8,7 @@ import { DeviceProtocol } from '../DeviceProtocol';
8
8
  import { EventLogger } from 'gd-eventlog';
9
9
  import CyclingMode from '../CyclingMode';
10
10
  import { IncyclistBikeData } from '../CyclingMode';
11
+ import BlePeripheralConnector from './ble-peripheral';
11
12
  declare type PowerData = {
12
13
  instantaneousPower?: number;
13
14
  balance?: number;
@@ -25,7 +26,9 @@ export declare type IndoorBikeData = {
25
26
  resistanceLevel?: number;
26
27
  instantaneousPower?: number;
27
28
  averagePower?: number;
28
- expendedEnergy?: number;
29
+ totalEnergy?: number;
30
+ energyPerHour?: number;
31
+ energyPerMinute?: number;
29
32
  heartrate?: number;
30
33
  metabolicEquivalent?: number;
31
34
  time?: number;
@@ -54,6 +57,7 @@ export default class BleFitnessMachineDevice extends BleDevice {
54
57
  constructor(props?: any);
55
58
  isMatching(characteristics: string[]): boolean;
56
59
  subscribeWriteResponse(cuuid: string): Promise<void>;
60
+ subscribeAll(conn?: BlePeripheralConnector): Promise<void>;
57
61
  init(): Promise<boolean>;
58
62
  onDisconnect(): void;
59
63
  getProfile(): string;
@@ -71,7 +75,7 @@ export default class BleFitnessMachineDevice extends BleDevice {
71
75
  parseIndoorBikeData(_data: Uint8Array): IndoorBikeData;
72
76
  parseFitnessMachineStatus(_data: Uint8Array): IndoorBikeData;
73
77
  getFitnessMachineFeatures(): Promise<IndoorBikeFeatures>;
74
- onData(characteristic: string, data: Buffer): void;
78
+ onData(characteristic: string, data: Buffer): boolean;
75
79
  writeFtmsMessage(requestedOpCode: any, data: any, props?: BleWriteProps): Promise<number>;
76
80
  requestControl(): Promise<boolean>;
77
81
  setTargetPower(power: number): Promise<boolean>;
package/lib/ble/fm.js CHANGED
@@ -93,6 +93,7 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
93
93
  this.windSpeed = 0;
94
94
  this.wheelSize = 2100;
95
95
  this.data = {};
96
+ this.services = BleFitnessMachineDevice.services;
96
97
  }
97
98
  isMatching(characteristics) {
98
99
  if (!characteristics)
@@ -125,13 +126,44 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
125
126
  }
126
127
  });
127
128
  }
129
+ subscribeAll(conn) {
130
+ return new Promise(resolve => {
131
+ const characteristics = [consts_1.INDOOR_BIKE_DATA, consts_1.FTMS_STATUS, consts_1.FTMS_CP];
132
+ const timeout = Date.now() + 5500;
133
+ const iv = setInterval(() => {
134
+ const subscriptionStatus = characteristics.map(c => this.subscribedCharacteristics.find(s => s === c) !== undefined);
135
+ const done = subscriptionStatus.filter(s => s === true).length === characteristics.length;
136
+ if (done || Date.now() > timeout) {
137
+ clearInterval(iv);
138
+ resolve();
139
+ }
140
+ }, 100);
141
+ try {
142
+ const connector = conn || this.ble.getConnector(this.peripheral);
143
+ for (let i = 0; i < characteristics.length; i++) {
144
+ const c = characteristics[i];
145
+ const isAlreadySubscribed = connector.isSubscribed(c);
146
+ if (!isAlreadySubscribed) {
147
+ connector.removeAllListeners(c);
148
+ connector.on(c, (uuid, data) => {
149
+ this.onData(uuid, data);
150
+ });
151
+ connector.subscribe(c);
152
+ this.subscribedCharacteristics.push(c);
153
+ }
154
+ }
155
+ }
156
+ catch (err) {
157
+ this.logEvent({ message: 'Error', fn: 'subscribeAll()', error: err.message, stack: err.stack });
158
+ }
159
+ });
160
+ }
128
161
  init() {
129
162
  const _super = Object.create(null, {
130
163
  initDevice: { get: () => super.initDevice }
131
164
  });
132
165
  return __awaiter(this, void 0, void 0, function* () {
133
166
  try {
134
- yield this.subscribeWriteResponse(consts_1.FTMS_CP);
135
167
  yield _super.initDevice.call(this);
136
168
  yield this.getFitnessMachineFeatures();
137
169
  this.logEvent({ message: 'device info', deviceInfo: this.deviceInfo, features: this.features });
@@ -180,6 +212,7 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
180
212
  }
181
213
  }
182
214
  catch (err) {
215
+ this.logEvent({ message: 'error', fn: 'parseHrm()', error: err.message | err, stack: err.stack });
183
216
  }
184
217
  return Object.assign(Object.assign({}, this.data), { raw: `2a37:${data.toString('hex')}` });
185
218
  }
@@ -191,99 +224,113 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
191
224
  getWindSpeed() { return this.windSpeed; }
192
225
  parseIndoorBikeData(_data) {
193
226
  const data = Buffer.from(_data);
194
- const flags = data.readUInt16LE(0);
195
- let offset = 2;
196
- if ((flags & IndoorBikeDataFlag.MoreData) === 0) {
197
- this.data.speed = data.readUInt16LE(offset) / 100;
198
- offset += 2;
199
- }
200
- if (flags & IndoorBikeDataFlag.AverageSpeedPresent) {
201
- this.data.averageSpeed = data.readUInt16LE(offset) / 100;
202
- offset += 2;
203
- }
204
- if (flags & IndoorBikeDataFlag.InstantaneousCadence) {
205
- this.data.cadence = data.readUInt16LE(offset) / 2;
206
- offset += 2;
207
- }
208
- if (flags & IndoorBikeDataFlag.AverageCadencePresent) {
209
- this.data.averageCadence = data.readUInt16LE(offset) / 2;
210
- offset += 2;
211
- }
212
- if (flags & IndoorBikeDataFlag.TotalDistancePresent) {
213
- const dvLow = data.readUInt8(offset);
214
- offset += 1;
215
- const dvHigh = data.readUInt16LE(offset);
216
- offset += 2;
217
- this.data.totalDistance = (dvHigh << 8) + dvLow;
218
- }
219
- if (flags & IndoorBikeDataFlag.ResistanceLevelPresent) {
220
- this.data.resistanceLevel = data.readInt16LE(offset);
221
- offset += 2;
222
- }
223
- if (flags & IndoorBikeDataFlag.InstantaneousPowerPresent) {
224
- this.data.instantaneousPower = data.readInt16LE(offset);
225
- offset += 2;
226
- }
227
- if (flags & IndoorBikeDataFlag.AveragePowerPresent) {
228
- this.data.averagePower = data.readInt16LE(offset);
229
- offset += 2;
230
- }
231
- if (flags & IndoorBikeDataFlag.ExpendedEnergyPresent) {
232
- this.data.expendedEnergy = data.readUInt16LE(offset);
233
- offset += 2;
234
- }
235
- if (flags & IndoorBikeDataFlag.HeartRatePresent) {
236
- this.data.heartrate = data.readUInt16LE(offset);
237
- offset += 2;
238
- }
239
- if (flags & IndoorBikeDataFlag.MetabolicEquivalentPresent) {
240
- this.data.metabolicEquivalent = data.readUInt16LE(offset) / 10;
241
- offset += 2;
242
- }
243
- if (flags & IndoorBikeDataFlag.ElapsedTimePresent) {
244
- this.data.time = data.readUInt16LE(offset);
245
- offset += 2;
227
+ try {
228
+ const flags = data.readUInt16LE(0);
229
+ let offset = 2;
230
+ if ((flags & IndoorBikeDataFlag.MoreData) === 0) {
231
+ this.data.speed = data.readUInt16LE(offset) / 100;
232
+ offset += 2;
233
+ }
234
+ if (flags & IndoorBikeDataFlag.AverageSpeedPresent) {
235
+ this.data.averageSpeed = data.readUInt16LE(offset) / 100;
236
+ offset += 2;
237
+ }
238
+ if (flags & IndoorBikeDataFlag.InstantaneousCadence) {
239
+ this.data.cadence = data.readUInt16LE(offset) / 2;
240
+ offset += 2;
241
+ }
242
+ if (flags & IndoorBikeDataFlag.AverageCadencePresent) {
243
+ this.data.averageCadence = data.readUInt16LE(offset) / 2;
244
+ offset += 2;
245
+ }
246
+ if (flags & IndoorBikeDataFlag.TotalDistancePresent) {
247
+ const dvLow = data.readUInt8(offset);
248
+ offset += 1;
249
+ const dvHigh = data.readUInt16LE(offset);
250
+ offset += 2;
251
+ this.data.totalDistance = (dvHigh << 8) + dvLow;
252
+ }
253
+ if (flags & IndoorBikeDataFlag.ResistanceLevelPresent) {
254
+ this.data.resistanceLevel = data.readInt8(offset);
255
+ offset += 1;
256
+ }
257
+ if (flags & IndoorBikeDataFlag.InstantaneousPowerPresent) {
258
+ this.data.instantaneousPower = data.readInt16LE(offset);
259
+ offset += 2;
260
+ }
261
+ if (flags & IndoorBikeDataFlag.AveragePowerPresent) {
262
+ this.data.averagePower = data.readInt16LE(offset);
263
+ offset += 2;
264
+ }
265
+ if (flags & IndoorBikeDataFlag.ExpendedEnergyPresent) {
266
+ this.data.totalEnergy = data.readUInt16LE(offset);
267
+ offset += 2;
268
+ this.data.energyPerHour = data.readUInt16LE(offset);
269
+ offset += 2;
270
+ this.data.energyPerMinute = data.readUInt8(offset);
271
+ offset += 1;
272
+ }
273
+ if (flags & IndoorBikeDataFlag.HeartRatePresent) {
274
+ this.data.heartrate = data.readUInt8(offset);
275
+ offset += 1;
276
+ }
277
+ if (flags & IndoorBikeDataFlag.MetabolicEquivalentPresent) {
278
+ this.data.metabolicEquivalent = data.readUInt8(offset) / 10;
279
+ offset += 2;
280
+ }
281
+ if (flags & IndoorBikeDataFlag.ElapsedTimePresent) {
282
+ this.data.time = data.readUInt16LE(offset);
283
+ offset += 2;
284
+ }
285
+ if (flags & IndoorBikeDataFlag.RemainingTimePresent) {
286
+ this.data.remainingTime = data.readUInt16LE(offset);
287
+ offset += 2;
288
+ }
246
289
  }
247
- if (flags & IndoorBikeDataFlag.RemainingTimePresent) {
248
- this.data.remainingTime = data.readUInt16LE(offset);
249
- offset += 2;
290
+ catch (err) {
291
+ this.logEvent({ message: 'error', fn: 'parseIndoorBikeData()', error: err.message | err, stack: err.stack });
250
292
  }
251
293
  return Object.assign(Object.assign({}, this.data), { raw: `2ad2:${data.toString('hex')}` });
252
294
  }
253
295
  parseFitnessMachineStatus(_data) {
254
296
  const data = Buffer.from(_data);
255
- const OpCode = data.readUInt8(0);
256
- switch (OpCode) {
257
- case 8:
258
- this.data.targetPower = data.readInt16LE(1);
259
- break;
260
- case 6:
261
- this.data.targetInclination = data.readInt16LE(1) / 10;
262
- break;
263
- case 4:
264
- this.data.status = "STARTED";
265
- break;
266
- case 3:
267
- case 2:
268
- this.data.status = "STOPPED";
269
- break;
270
- case 20:
271
- const spinDownStatus = data.readUInt8(1);
272
- switch (spinDownStatus) {
273
- case 1:
274
- this.data.status = "SPIN DOWN REQUESTED";
275
- break;
276
- case 2:
277
- this.data.status = "SPIN DOWN SUCCESS";
278
- break;
279
- case 3:
280
- this.data.status = "SPIN DOWN ERROR";
281
- break;
282
- case 4:
283
- this.data.status = "STOP PEDALING";
284
- break;
285
- default: break;
286
- }
297
+ try {
298
+ const OpCode = data.readUInt8(0);
299
+ switch (OpCode) {
300
+ case 8:
301
+ this.data.targetPower = data.readInt16LE(1);
302
+ break;
303
+ case 6:
304
+ this.data.targetInclination = data.readInt16LE(1) / 10;
305
+ break;
306
+ case 4:
307
+ this.data.status = "STARTED";
308
+ break;
309
+ case 3:
310
+ case 2:
311
+ this.data.status = "STOPPED";
312
+ break;
313
+ case 20:
314
+ const spinDownStatus = data.readUInt8(1);
315
+ switch (spinDownStatus) {
316
+ case 1:
317
+ this.data.status = "SPIN DOWN REQUESTED";
318
+ break;
319
+ case 2:
320
+ this.data.status = "SPIN DOWN SUCCESS";
321
+ break;
322
+ case 3:
323
+ this.data.status = "SPIN DOWN ERROR";
324
+ break;
325
+ case 4:
326
+ this.data.status = "STOP PEDALING";
327
+ break;
328
+ default: break;
329
+ }
330
+ }
331
+ }
332
+ catch (err) {
333
+ this.logEvent({ message: 'error', fn: 'parseFitnessMachineStatus()', error: err.message | err, stack: err.stack });
287
334
  }
288
335
  return Object.assign(Object.assign({}, this.data), { raw: `2ada:${data.toString('hex')}` });
289
336
  }
@@ -298,6 +345,7 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
298
345
  const fitnessMachine = buffer.readUInt32LE(0);
299
346
  const targetSettings = buffer.readUInt32LE(4);
300
347
  this.features = { fitnessMachine, targetSettings };
348
+ this.logEvent({ message: 'supported Features: ', fatures: this.features });
301
349
  }
302
350
  }
303
351
  catch (err) {
@@ -306,7 +354,9 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
306
354
  });
307
355
  }
308
356
  onData(characteristic, data) {
309
- super.onData(characteristic, data);
357
+ const hasData = super.onData(characteristic, data);
358
+ if (!hasData)
359
+ return false;
310
360
  const uuid = characteristic.toLocaleLowerCase();
311
361
  let res = undefined;
312
362
  switch (uuid) {
@@ -326,8 +376,11 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
326
376
  default:
327
377
  break;
328
378
  }
329
- if (res)
379
+ if (res) {
330
380
  this.emit('data', res);
381
+ return false;
382
+ }
383
+ return true;
331
384
  }
332
385
  writeFtmsMessage(requestedOpCode, data, props) {
333
386
  return __awaiter(this, void 0, void 0, function* () {
package/lib/ble/hrm.d.ts CHANGED
@@ -20,7 +20,7 @@ export default class BleHrmDevice extends BleDevice {
20
20
  getProfile(): string;
21
21
  getServiceUUids(): string[];
22
22
  parseHrm(_data: Uint8Array): HrmData;
23
- onData(characteristic: string, data: Buffer): void;
23
+ onData(characteristic: string, data: Buffer): boolean;
24
24
  }
25
25
  export declare class HrmAdapter extends DeviceAdapter {
26
26
  device: BleHrmDevice;
package/lib/ble/hrm.js CHANGED
@@ -51,14 +51,15 @@ class BleHrmDevice extends ble_device_1.BleDevice {
51
51
  return { heartrate, rr, raw: data.toString('hex') };
52
52
  }
53
53
  onData(characteristic, data) {
54
- super.onData(characteristic, data);
55
- const isDuplicate = this.checkForDuplicate(characteristic, data);
56
- if (isDuplicate)
54
+ const hasData = super.onData(characteristic, data);
55
+ if (!hasData)
57
56
  return;
58
57
  if (characteristic.toLocaleLowerCase() === '2a37') {
59
58
  const res = this.parseHrm(data);
60
59
  this.emit('data', res);
60
+ return false;
61
61
  }
62
+ return true;
62
63
  }
63
64
  }
64
65
  exports.default = BleHrmDevice;
package/lib/ble/pwr.d.ts CHANGED
@@ -42,7 +42,7 @@ export default class BleCyclingPowerDevice extends BleDevice {
42
42
  time: any;
43
43
  };
44
44
  parsePower(_data: Uint8Array): PowerData;
45
- onData(characteristic: string, data: Buffer): void;
45
+ onData(characteristic: string, data: Buffer): boolean;
46
46
  reset(): void;
47
47
  }
48
48
  export declare class PwrAdapter extends DeviceAdapter {
@@ -71,6 +71,7 @@ export declare class PwrAdapter extends DeviceAdapter {
71
71
  getDisplayName(): string;
72
72
  getCyclingMode(): CyclingMode;
73
73
  getDefaultCyclingMode(): CyclingMode;
74
+ getSupportedCyclingModes(): any[];
74
75
  getPort(): string;
75
76
  getWeight(): number;
76
77
  setIgnoreBike(ignore: any): void;
package/lib/ble/pwr.js CHANGED
@@ -34,6 +34,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
34
34
  exports.PwrAdapter = void 0;
35
35
  const ble_device_1 = require("./ble-device");
36
36
  const ble_interface_1 = __importDefault(require("./ble-interface"));
37
+ const ble_1 = require("./ble");
37
38
  const Device_1 = __importStar(require("../Device"));
38
39
  const gd_eventlog_1 = require("gd-eventlog");
39
40
  const consts_1 = require("./consts");
@@ -138,14 +139,15 @@ class BleCyclingPowerDevice extends ble_device_1.BleDevice {
138
139
  return { instantaneousPower, balance, accTorque, rpm, time, raw: `2a63:${data.toString('hex')}` };
139
140
  }
140
141
  onData(characteristic, data) {
141
- super.onData(characteristic, data);
142
- const isDuplicate = this.checkForDuplicate(characteristic, data);
143
- if (isDuplicate)
144
- return;
145
- if (characteristic.toLocaleLowerCase() === consts_1.CSP_MEASUREMENT) {
142
+ const hasData = super.onData(characteristic, data);
143
+ if (!hasData)
144
+ return false;
145
+ if ((0, ble_1.matches)(characteristic, consts_1.CSP_MEASUREMENT)) {
146
146
  const res = this.parsePower(data);
147
147
  this.emit('data', res);
148
+ return false;
148
149
  }
150
+ return true;
149
151
  }
150
152
  reset() {
151
153
  this.instantaneousPower = undefined;
@@ -201,6 +203,9 @@ class PwrAdapter extends Device_1.default {
201
203
  getDefaultCyclingMode() {
202
204
  return new power_meter_1.default(this);
203
205
  }
206
+ getSupportedCyclingModes() {
207
+ return [power_meter_1.default];
208
+ }
204
209
  getPort() {
205
210
  return 'ble';
206
211
  }
package/lib/ble/tacx.d.ts CHANGED
@@ -63,7 +63,7 @@ export default class TacxAdvancedFitnessMachineDevice extends BleFitnessMachineD
63
63
  parseTrainerData(data: Buffer): BleFeBikeData;
64
64
  parseProductInformation(data: Buffer): BleFeBikeData;
65
65
  parseFECMessage(_data: Buffer): BleFeBikeData;
66
- onData(characteristic: string, data: Buffer): any;
66
+ onData(characteristic: string, data: Buffer): boolean;
67
67
  getChecksum(message: any[]): number;
68
68
  buildMessage(payload?: number[], msgID?: number): Buffer;
69
69
  sendMessage(message: Buffer): Promise<boolean>;
package/lib/ble/tacx.js CHANGED
@@ -164,10 +164,10 @@ class TacxAdvancedFitnessMachineDevice extends fm_1.default {
164
164
  return { rpm, time: this.timeOffset + c.time };
165
165
  }
166
166
  parseCSC(_data, logOnly = false) {
167
- this.logEvent({ message: 'BLE CSC message', data: _data.toString('hex') });
167
+ const data = Buffer.from(_data);
168
+ this.logEvent({ message: 'BLE CSC message', data: data.toString('hex') });
168
169
  if (logOnly)
169
170
  return this.data;
170
- const data = Buffer.from(_data);
171
171
  let offset = 0;
172
172
  const flags = data.readUInt8(offset);
173
173
  offset++;
@@ -187,10 +187,10 @@ class TacxAdvancedFitnessMachineDevice extends fm_1.default {
187
187
  return this.data;
188
188
  }
189
189
  parsePower(_data, logOnly = false) {
190
- this.logEvent({ message: 'BLE CSP message', data: _data.toString('hex') });
190
+ const data = Buffer.from(_data);
191
+ this.logEvent({ message: 'BLE CSP message', data: data.toString('hex') });
191
192
  if (logOnly)
192
193
  return this.data;
193
- const data = Buffer.from(_data);
194
194
  try {
195
195
  let offset = 4;
196
196
  const flags = data.readUInt16LE(0);
@@ -70,7 +70,7 @@ export default class WahooAdvancedFitnessMachineDevice extends BleFitnessMachine
70
70
  time: any;
71
71
  };
72
72
  parsePower(_data: Buffer): IndoorBikeData;
73
- onData(characteristic: string, data: Buffer): void;
73
+ onData(characteristic: string, data: Buffer): boolean;
74
74
  writeWahooFtmsMessage(requestedOpCode: number, data: Buffer, props?: BleWriteProps): Promise<boolean>;
75
75
  requestControl(): Promise<boolean>;
76
76
  setPowerAdjusting(): void;
@@ -170,7 +170,9 @@ class WahooAdvancedFitnessMachineDevice extends fm_1.default {
170
170
  return { instantaneousPower, cadence, time, raw: data.toString('hex') };
171
171
  }
172
172
  onData(characteristic, data) {
173
- super.onData(characteristic, data);
173
+ const hasData = super.onData(characteristic, data);
174
+ if (!hasData)
175
+ return false;
174
176
  const uuid = characteristic.toLowerCase();
175
177
  let res = undefined;
176
178
  switch (uuid) {
@@ -190,8 +192,11 @@ class WahooAdvancedFitnessMachineDevice extends fm_1.default {
190
192
  this.logEvent({ message: 'data', uuid, data: data.toString('hex') });
191
193
  break;
192
194
  }
193
- if (res)
195
+ if (res) {
194
196
  this.emit('data', res);
197
+ return false;
198
+ }
199
+ return true;
195
200
  }
196
201
  writeWahooFtmsMessage(requestedOpCode, data, props) {
197
202
  return __awaiter(this, void 0, void 0, function* () {
@@ -0,0 +1,24 @@
1
+ import CyclingMode, { CyclingModeProperty, IncyclistBikeData, UpdateRequest } from '../../CyclingMode';
2
+ import KettlerAdapter from "./adapter";
3
+ import PowerBasedCyclingModeBase from '../../modes/power-base';
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: KettlerAdapter, 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,144 @@
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 MIN_SPEED = 10;
9
+ const config = {
10
+ name: "ERG",
11
+ description: "Calculates speed based on power and slope. Power is either set by a workout",
12
+ properties: [
13
+ { key: 'bikeType', name: 'Bike Type', description: '', type: CyclingMode_1.CyclingModeProperyType.SingleSelect, options: ['Race', 'Mountain', 'Triathlon'], default: 'Race' },
14
+ { 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 },
15
+ ]
16
+ };
17
+ class ERGCyclingMode extends power_base_1.default {
18
+ constructor(adapter, props) {
19
+ super(adapter, props);
20
+ this.hasBikeUpdate = false;
21
+ this.event = {};
22
+ this.initLogger('ERGMode');
23
+ }
24
+ getName() {
25
+ return config.name;
26
+ }
27
+ getDescription() {
28
+ return config.description;
29
+ }
30
+ getProperties() {
31
+ return config.properties;
32
+ }
33
+ getProperty(name) {
34
+ return config.properties.find(p => p.name === name);
35
+ }
36
+ getBikeInitRequest() {
37
+ const startPower = this.getSetting('startPower');
38
+ return { targetPower: startPower };
39
+ }
40
+ sendBikeUpdate(request) {
41
+ const getData = () => {
42
+ if (!this.data)
43
+ return {};
44
+ const { pedalRpm, slope, power, speed } = this.data;
45
+ return { pedalRpm, slope, power, speed };
46
+ };
47
+ this.logger.logEvent({ message: "processing update request", request, prev: this.prevRequest, data: getData() });
48
+ let newRequest = {};
49
+ try {
50
+ if (!request || request.reset || Object.keys(request).length === 0) {
51
+ this.prevRequest = {};
52
+ return request.reset ? { reset: true } : {};
53
+ }
54
+ const prevData = this.data || {};
55
+ if (request.targetPower !== undefined) {
56
+ delete request.slope;
57
+ delete request.refresh;
58
+ }
59
+ if (request.refresh) {
60
+ delete request.refresh;
61
+ if (this.prevRequest)
62
+ newRequest.targetPower = this.prevRequest.targetPower;
63
+ else {
64
+ this.prevRequest = this.getBikeInitRequest();
65
+ newRequest.targetPower = this.prevRequest.targetPower;
66
+ }
67
+ }
68
+ if (request.slope !== undefined) {
69
+ if (!this.data)
70
+ this.data = {};
71
+ this.data.slope = request.slope;
72
+ delete request.slope;
73
+ }
74
+ if (request.maxPower !== undefined && request.minPower !== undefined && request.maxPower === request.minPower) {
75
+ request.targetPower = request.maxPower;
76
+ newRequest.targetPower = request.targetPower;
77
+ }
78
+ if (request.maxPower !== undefined) {
79
+ if (newRequest.targetPower !== undefined && newRequest.targetPower > request.maxPower) {
80
+ newRequest.targetPower = request.maxPower;
81
+ }
82
+ newRequest.maxPower = request.maxPower;
83
+ }
84
+ if (request.minPower !== undefined) {
85
+ if (newRequest.targetPower !== undefined && newRequest.targetPower < request.minPower) {
86
+ newRequest.targetPower = request.minPower;
87
+ }
88
+ newRequest.minPower = request.minPower;
89
+ if (prevData.power && prevData.power < request.minPower)
90
+ newRequest.targetPower = request.minPower;
91
+ }
92
+ if (!newRequest.targetPower && newRequest.maxPower && prevData.power > newRequest.maxPower) {
93
+ newRequest.targetPower = newRequest.maxPower;
94
+ }
95
+ if (!newRequest.targetPower && newRequest.minPower && prevData.power < newRequest.minPower) {
96
+ newRequest.targetPower = newRequest.minPower;
97
+ }
98
+ this.prevRequest = JSON.parse(JSON.stringify(request));
99
+ }
100
+ catch (err) {
101
+ this.logger.logEvent({ message: "error", fn: 'sendBikeUpdate()', error: err.message || err, stack: err.stack });
102
+ }
103
+ return newRequest;
104
+ }
105
+ updateData(bikeData) {
106
+ const prevData = JSON.parse(JSON.stringify(this.data || {}));
107
+ const prevSpeed = prevData.speed;
108
+ const prevRequest = this.prevRequest || {};
109
+ const data = this.data || {};
110
+ const bikeType = this.getSetting('bikeType').toLowerCase();
111
+ try {
112
+ let power = bikeData.power || 0;
113
+ const slope = (prevData.slope !== undefined ? prevData.slope : prevRequest.slope || 0);
114
+ const distanceInternal = prevData.distanceInternal || 0;
115
+ const m = this.getWeight();
116
+ const t = this.getTimeSinceLastUpdate();
117
+ const { speed, distance } = this.calculateSpeedAndDistance(power, slope, m, t, { bikeType });
118
+ if (power === 0 && speed < MIN_SPEED) {
119
+ data.speed = Math.round(prevData.speed - 1) < 0 ? 0 : Math.round(prevData.speed - 1);
120
+ data.distanceInternal = distanceInternal + data.speed / 3.6 * t;
121
+ }
122
+ else {
123
+ data.speed = (power === 0 && speed < MIN_SPEED) ? 0 : speed;
124
+ data.distanceInternal = (power === 0 && speed < MIN_SPEED) ? distanceInternal : distanceInternal + distance;
125
+ }
126
+ data.power = Math.round(power);
127
+ data.slope = slope;
128
+ data.pedalRpm = bikeData.pedalRpm || 0;
129
+ if (data.time !== undefined && data.speed > 0)
130
+ data.time += t;
131
+ else
132
+ data.time = 0;
133
+ data.heartrate = bikeData.heartrate;
134
+ data.isPedalling = bikeData.isPedalling;
135
+ }
136
+ catch (err) {
137
+ this.logger.logEvent({ message: 'error', fn: 'updateData()', error: err.message || err });
138
+ }
139
+ this.logger.logEvent({ message: "updateData result", data, bikeData, prevRequest, prevSpeed });
140
+ this.data = data;
141
+ return data;
142
+ }
143
+ }
144
+ exports.default = ERGCyclingMode;
@@ -28,7 +28,6 @@ export interface KettlerDeviceSettings extends DeviceSettings {
28
28
  }
29
29
  export default class KettlerRacerAdapter extends DeviceAdapterBase implements DeviceAdapter, Bike {
30
30
  private id;
31
- private settings;
32
31
  private ignoreHrm;
33
32
  private ignoreBike;
34
33
  private ignorePower;
@@ -43,7 +42,7 @@ export default class KettlerRacerAdapter extends DeviceAdapterBase implements De
43
42
  private requestBusy;
44
43
  private comms;
45
44
  private prevDistance;
46
- constructor(protocol: DeviceProtocol, settings: DeviceSettings);
45
+ constructor(protocol: DeviceProtocol, settings: KettlerDeviceSettings);
47
46
  isBike(): boolean;
48
47
  isPower(): boolean;
49
48
  isHrm(): boolean;
@@ -96,8 +95,6 @@ export default class KettlerRacerAdapter extends DeviceAdapterBase implements De
96
95
  waitForOpened(retries?: boolean): Promise<boolean>;
97
96
  waitForClosed(): Promise<boolean>;
98
97
  getSupportedCyclingModes(): any[];
99
- setCyclingMode(mode: CyclingMode | string, settings?: any): void;
100
- getCyclingMode(): CyclingMode;
101
98
  getDefaultCyclingMode(): CyclingMode;
102
99
  setUserSettings(userSettings: any): void;
103
100
  setBikeSettings(bikeSettings: any): void;
@@ -36,12 +36,12 @@ const gd_eventlog_1 = require("gd-eventlog");
36
36
  const comms_1 = __importDefault(require("../comms"));
37
37
  const utils_1 = require("../../utils");
38
38
  const power_meter_1 = __importDefault(require("../../modes/power-meter"));
39
+ const ERGCyclingMode_1 = __importDefault(require("./ERGCyclingMode"));
39
40
  class KettlerRacerAdapter extends Device_1.default {
40
41
  constructor(protocol, settings) {
41
- super(protocol);
42
+ super(protocol, settings);
42
43
  this.requests = [];
43
44
  this.logger = new gd_eventlog_1.EventLogger('KettlerRacer');
44
- this.settings = settings;
45
45
  this.ignoreHrm = false;
46
46
  this.ignorePower = false;
47
47
  this.ignoreBike = false;
@@ -624,32 +624,10 @@ class KettlerRacerAdapter extends Device_1.default {
624
624
  });
625
625
  }
626
626
  getSupportedCyclingModes() {
627
- return [power_meter_1.default];
628
- }
629
- setCyclingMode(mode, settings) {
630
- let selectedMode;
631
- if (typeof mode === 'string') {
632
- const supported = this.getSupportedCyclingModes();
633
- const CyclingModeClass = supported.find(M => { const m = new M(this); return m.getName() === mode; });
634
- if (CyclingModeClass) {
635
- this.settings.cyclingMode = new CyclingModeClass(this, settings);
636
- return;
637
- }
638
- selectedMode = this.getDefaultCyclingMode();
639
- }
640
- else {
641
- selectedMode = mode;
642
- }
643
- this.settings.cyclingMode = selectedMode;
644
- this.settings.cyclingMode.setSettings(settings);
645
- }
646
- getCyclingMode() {
647
- if (!this.settings.cyclingMode)
648
- this.setCyclingMode(this.getDefaultCyclingMode());
649
- return this.settings.cyclingMode;
627
+ return [power_meter_1.default, ERGCyclingMode_1.default];
650
628
  }
651
629
  getDefaultCyclingMode() {
652
- return new power_meter_1.default(this);
630
+ return new ERGCyclingMode_1.default(this);
653
631
  }
654
632
  setUserSettings(userSettings) {
655
633
  this.settings.userSettings = userSettings;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "incyclist-devices",
3
- "version": "1.4.92",
3
+ "version": "1.4.94",
4
4
  "dependencies": {
5
5
  "@serialport/parser-byte-length": "^9.0.1",
6
6
  "@serialport/parser-delimiter": "^9.0.1",