incyclist-devices 1.4.67 → 1.4.70

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.
@@ -9,6 +9,7 @@ interface BleDeviceConstructProps extends BleDeviceProps {
9
9
  declare type CommandQueueItem = {
10
10
  uuid: string;
11
11
  data: Buffer;
12
+ timeout: number;
12
13
  resolve: any;
13
14
  reject: any;
14
15
  };
@@ -26,6 +27,7 @@ export declare abstract class BleDevice extends BleDeviceClass {
26
27
  isInitialized: boolean;
27
28
  subscribedCharacteristics: string[];
28
29
  writeQueue: CommandQueueItem[];
30
+ workerIv: NodeJS.Timeout;
29
31
  constructor(props?: BleDeviceConstructProps);
30
32
  logEvent(event: any): void;
31
33
  setLogger(logger: EventLogger): void;
@@ -36,12 +38,16 @@ export declare abstract class BleDevice extends BleDeviceClass {
36
38
  waitForConnectFinished(timeout: any): Promise<unknown>;
37
39
  hasService(serviceUuid: any): boolean;
38
40
  init(): Promise<boolean>;
41
+ initDevice(): Promise<boolean>;
39
42
  connectPeripheral(peripheral: BlePeripheral): Promise<void>;
40
43
  subscribeAll(conn?: BlePeripheralConnector): Promise<void>;
41
44
  connect(props?: ConnectProps): Promise<boolean>;
42
45
  disconnect(): Promise<boolean>;
43
46
  abstract getProfile(): string;
44
47
  onData(characteristic: string, data: Buffer): void;
48
+ timeoutCheck(): void;
49
+ startWorker(): void;
50
+ stopWorker(): void;
45
51
  write(characteristicUuid: string, data: Buffer, withoutResponse?: boolean): Promise<ArrayBuffer>;
46
52
  read(characteristicUuid: string): Promise<Uint8Array>;
47
53
  getDeviceInfo(): Promise<BleDeviceInfo>;
@@ -27,6 +27,7 @@ class BleDevice extends ble_1.BleDeviceClass {
27
27
  this.subscribedCharacteristics = [];
28
28
  this.isInitialized = false;
29
29
  this.writeQueue = [];
30
+ this.workerIv = null;
30
31
  if (props.peripheral) {
31
32
  const { id, address, advertisement, state } = props.peripheral;
32
33
  this.peripheral = props.peripheral;
@@ -106,11 +107,15 @@ class BleDevice extends ble_1.BleDeviceClass {
106
107
  return this.services && this.services.find(s => s === serviceUuid || (0, ble_1.uuid)(serviceUuid)) !== undefined;
107
108
  }
108
109
  init() {
109
- if (this.isInitialized)
110
- return Promise.resolve(true);
110
+ return __awaiter(this, void 0, void 0, function* () {
111
+ return yield this.initDevice();
112
+ });
113
+ }
114
+ initDevice() {
115
+ this.logEvent({ message: 'get device info' });
111
116
  return this.getDeviceInfo().then(() => {
112
117
  this.emit('deviceInfo', this.deviceInfo);
113
- this.logEvent(Object.assign({ message: 'ftms device init done' }, this.deviceInfo));
118
+ this.logEvent(Object.assign({ message: 'device init done' }, this.deviceInfo));
114
119
  this.isInitialized = true;
115
120
  return true;
116
121
  });
@@ -223,6 +228,9 @@ class BleDevice extends ble_1.BleDeviceClass {
223
228
  const { id, name, address } = this;
224
229
  this.logEvent({ message: 'disconnect requested', device: { id, name, address } });
225
230
  this.connectState.isDisconnecting = true;
231
+ if (this.workerIv) {
232
+ this.stopWorker();
233
+ }
226
234
  if (!this.connectState.isConnecting && !this.connectState.isConnected) {
227
235
  this.connectState.isDisconnecting = false;
228
236
  this.logEvent({ message: 'disconnect result: success', device: { id, name, address } });
@@ -254,11 +262,43 @@ class BleDevice extends ble_1.BleDeviceClass {
254
262
  }
255
263
  }
256
264
  }
265
+ timeoutCheck() {
266
+ const now = Date.now();
267
+ const updatedQueue = [];
268
+ let hasTimeout = false;
269
+ this.writeQueue.forEach(writeItem => {
270
+ if (writeItem.timeout && writeItem.timeout < now) {
271
+ if (writeItem.reject) {
272
+ hasTimeout = true;
273
+ writeItem.reject(new Error('timeout'));
274
+ }
275
+ }
276
+ else {
277
+ updatedQueue.push(writeItem);
278
+ }
279
+ });
280
+ if (hasTimeout)
281
+ this.writeQueue = updatedQueue;
282
+ }
283
+ startWorker() {
284
+ if (this.workerIv)
285
+ return;
286
+ this.workerIv = setInterval(() => { this.timeoutCheck(); }, 100);
287
+ }
288
+ stopWorker() {
289
+ if (!this.workerIv)
290
+ return;
291
+ clearInterval(this.workerIv);
292
+ this.workerIv = null;
293
+ }
257
294
  write(characteristicUuid, data, withoutResponse = false) {
258
295
  return __awaiter(this, void 0, void 0, function* () {
259
296
  try {
260
297
  const connector = this.ble.getConnector(this.peripheral);
261
298
  const isAlreadySubscribed = connector.isSubscribed(characteristicUuid);
299
+ if (!withoutResponse && !this.workerIv) {
300
+ this.startWorker();
301
+ }
262
302
  if (!withoutResponse && !isAlreadySubscribed) {
263
303
  const connector = this.ble.getConnector(this.peripheral);
264
304
  connector.removeAllListeners(characteristicUuid);
@@ -284,7 +324,7 @@ class BleDevice extends ble_1.BleDeviceClass {
284
324
  else {
285
325
  const writeId = this.writeQueue.length;
286
326
  let messageDeleted = false;
287
- this.writeQueue.push({ uuid: characteristicUuid.toLocaleLowerCase(), data, resolve, reject });
327
+ this.writeQueue.push({ uuid: characteristicUuid.toLocaleLowerCase(), data, timeout: Date.now() + 2500, resolve, reject });
288
328
  const to = setTimeout(() => {
289
329
  if (this.writeQueue.length > writeId && !messageDeleted)
290
330
  this.writeQueue.splice(writeId, 1);
@@ -590,14 +590,14 @@ class BleInterface extends ble_1.BleInterfaceClass {
590
590
  cntFound++;
591
591
  const existing = devicesProcessed.find(device => device.id === d.id && device.getProfile() === d.getProfile());
592
592
  if (!scanForDevice && cntFound > 0 && !existing) {
593
- this.logEvent({ message: `${opStr}: device found`, device: d.name, address: d.address, services: d.services.join(',') });
593
+ this.logEvent({ message: `${opStr}: device found`, device: d.name, profile: d.getProfile(), address: d.address, services: d.services.join(',') });
594
594
  this.addDeviceToCache(d, peripheral.state === 'connected');
595
595
  devicesProcessed.push(d);
596
596
  this.emit('device', d);
597
597
  return;
598
598
  }
599
599
  if (scanForDevice && cntFound > 0) {
600
- this.logEvent({ message: `${opStr}: device found`, device: d.name, address: d.address, services: d.services.join(',') });
600
+ this.logEvent({ message: `${opStr}: device found`, device: d.name, profile: d.getProfile(), address: d.address, services: d.services.join(',') });
601
601
  this.addDeviceToCache(d, peripheral.state === 'connected');
602
602
  devicesProcessed.push(d);
603
603
  this.emit('device', d);
package/lib/ble/fm.d.ts CHANGED
@@ -53,6 +53,7 @@ export default class BleFitnessMachineDevice extends BleDevice {
53
53
  wheelSize: number;
54
54
  constructor(props?: any);
55
55
  isMatching(characteristics: string[]): boolean;
56
+ subscribeWriteResponse(cuuid: string): Promise<void>;
56
57
  init(): Promise<boolean>;
57
58
  onDisconnect(): void;
58
59
  getProfile(): string;
package/lib/ble/fm.js CHANGED
@@ -104,35 +104,40 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
104
104
  const hasIndoorBike = characteristics.find(c => c === INDOOR_BIKE_DATA) !== undefined;
105
105
  return hasStatus && hasCP && hasIndoorBike;
106
106
  }
107
+ subscribeWriteResponse(cuuid) {
108
+ return __awaiter(this, void 0, void 0, function* () {
109
+ const connector = this.ble.getConnector(this.peripheral);
110
+ const isAlreadySubscribed = connector.isSubscribed(cuuid);
111
+ if (!isAlreadySubscribed) {
112
+ connector.removeAllListeners(cuuid);
113
+ let prev = undefined;
114
+ let prevTS = undefined;
115
+ connector.on(cuuid, (uuid, data) => {
116
+ const message = data.toString('hex');
117
+ if (prevTS && prev && message === prev && Date.now() - prevTS < 500) {
118
+ return;
119
+ }
120
+ prevTS = Date.now();
121
+ prev = message;
122
+ this.onData(uuid, data);
123
+ });
124
+ yield connector.subscribe(cuuid);
125
+ }
126
+ });
127
+ }
107
128
  init() {
108
129
  const _super = Object.create(null, {
109
- init: { get: () => super.init }
130
+ initDevice: { get: () => super.initDevice }
110
131
  });
111
132
  return __awaiter(this, void 0, void 0, function* () {
112
133
  try {
113
- const connector = this.ble.getConnector(this.peripheral);
114
- const isAlreadySubscribed = connector.isSubscribed(FTMS_CP);
115
- if (!isAlreadySubscribed) {
116
- connector.removeAllListeners(FTMS_CP);
117
- let prev = undefined;
118
- let prevTS = undefined;
119
- connector.on(FTMS_CP, (uuid, data) => {
120
- const message = data.toString('hex');
121
- if (prevTS && prev && message === prev && Date.now() - prevTS < 500) {
122
- return;
123
- }
124
- prevTS = Date.now();
125
- prev = message;
126
- this.onData(uuid, data);
127
- });
128
- yield connector.subscribe(FTMS_CP);
129
- }
130
- this.logEvent({ message: 'get device info' });
131
- yield _super.init.call(this);
134
+ yield this.subscribeWriteResponse(FTMS_CP);
135
+ yield _super.initDevice.call(this);
132
136
  yield this.getFitnessMachineFeatures();
133
137
  this.logEvent({ message: 'device info', deviceInfo: this.deviceInfo, features: this.features });
134
138
  }
135
139
  catch (err) {
140
+ this.logEvent({ message: 'error', fn: 'BleFitnessMachineDevice.init()', error: err.message || err, stack: err.stack });
136
141
  return Promise.resolve(false);
137
142
  }
138
143
  });
@@ -419,9 +424,11 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
419
424
  }
420
425
  setIndoorBikeSimulation(windSpeed, gradient, crr, cw) {
421
426
  return __awaiter(this, void 0, void 0, function* () {
422
- if (!this.hasControl)
423
- return;
424
427
  const hasControl = yield this.requestControl();
428
+ if (!hasControl) {
429
+ this.logEvent({ message: 'setIndoorBikeSimulation failed', reason: 'control is disabled' });
430
+ return false;
431
+ }
425
432
  const data = Buffer.alloc(7);
426
433
  data.writeUInt8(17, 0);
427
434
  data.writeInt16LE(Math.round(windSpeed * 1000), 1);
@@ -504,7 +511,8 @@ class FmAdapter extends Device_1.default {
504
511
  return (adapter.getName() === this.getName() && adapter.getProfile() === this.getProfile());
505
512
  }
506
513
  getProfile() {
507
- return 'Smart Trainer';
514
+ const profile = this.device ? this.device.getProfile() : undefined;
515
+ return profile || 'Smart Trainer';
508
516
  }
509
517
  getName() {
510
518
  return `${this.device.name}`;
package/lib/ble/tacx.js CHANGED
@@ -42,7 +42,7 @@ const TACX_FE_C_TX = '6e40fec3';
42
42
  const SYNC_BYTE = 0xA4;
43
43
  const DEFAULT_CHANNEL = 5;
44
44
  const ACKNOWLEDGED_DATA = 0x4F;
45
- const PROFILE_ID = 'Tacx FE-C over BLE';
45
+ const PROFILE_ID = 'Tacx SmartTrainer';
46
46
  const cwABike = {
47
47
  race: 0.35,
48
48
  triathlon: 0.29,
@@ -79,16 +79,16 @@ class TacxAdvancedFitnessMachineDevice extends fm_1.default {
79
79
  }
80
80
  init() {
81
81
  const _super = Object.create(null, {
82
- init: { get: () => super.init }
82
+ initDevice: { get: () => super.initDevice }
83
83
  });
84
84
  return __awaiter(this, void 0, void 0, function* () {
85
85
  try {
86
- this.logEvent({ message: 'get device info' });
87
- yield _super.init.call(this);
88
- this.logEvent({ message: 'device info', deviceInfo: this.deviceInfo, features: this.features });
86
+ yield _super.initDevice.call(this);
87
+ return true;
89
88
  }
90
89
  catch (err) {
91
- return Promise.resolve(false);
90
+ this.logEvent({ message: 'error', fn: 'TacxAdvancedFitnessMachineDevice.init()', error: err.message || err, stack: err.stack });
91
+ return false;
92
92
  }
93
93
  });
94
94
  }
@@ -34,6 +34,7 @@ export default class WahooAdvancedFitnessMachineDevice extends BleFitnessMachine
34
34
  currentCrankData: CrankData;
35
35
  timeOffset: number;
36
36
  tsPrevWrite: any;
37
+ prevSlope: any;
37
38
  constructor(props?: any);
38
39
  isMatching(characteristics: string[]): boolean;
39
40
  init(): Promise<boolean>;
@@ -37,7 +37,7 @@ const Device_1 = require("../Device");
37
37
  const gd_eventlog_1 = require("gd-eventlog");
38
38
  const fm_1 = __importStar(require("./fm"));
39
39
  const WAHOO_ADVANCED_FTMS = 'a026e00b';
40
- const WAHOO_ADVANCED_TRAINER_CP = 'a026e037';
40
+ const WAHOO_ADVANCED_TRAINER_CP = 'a026e005';
41
41
  const cwABike = {
42
42
  race: 0.35,
43
43
  triathlon: 0.29,
@@ -52,6 +52,7 @@ class WahooAdvancedFitnessMachineDevice extends fm_1.default {
52
52
  this.currentCrankData = undefined;
53
53
  this.timeOffset = 0;
54
54
  this.tsPrevWrite = undefined;
55
+ this.prevSlope = undefined;
55
56
  this.data = {};
56
57
  }
57
58
  isMatching(characteristics) {
@@ -62,33 +63,17 @@ class WahooAdvancedFitnessMachineDevice extends fm_1.default {
62
63
  }
63
64
  init() {
64
65
  const _super = Object.create(null, {
65
- init: { get: () => super.init }
66
+ initDevice: { get: () => super.initDevice }
66
67
  });
67
68
  return __awaiter(this, void 0, void 0, function* () {
68
69
  try {
69
- const connector = this.ble.getConnector(this.peripheral);
70
- const isAlreadySubscribed = connector.isSubscribed(WAHOO_ADVANCED_TRAINER_CP);
71
- if (!isAlreadySubscribed) {
72
- connector.removeAllListeners(WAHOO_ADVANCED_TRAINER_CP);
73
- let prev = undefined;
74
- let prevTS = undefined;
75
- connector.on(WAHOO_ADVANCED_TRAINER_CP, (uuid, data) => {
76
- const message = data.toString('hex');
77
- if (prevTS && prev && message === prev && Date.now() - prevTS < 500) {
78
- return;
79
- }
80
- prevTS = Date.now();
81
- prev = message;
82
- this.onData(uuid, data);
83
- });
84
- yield connector.subscribe(WAHOO_ADVANCED_TRAINER_CP);
85
- }
86
- this.logEvent({ message: 'get device info' });
87
- yield _super.init.call(this);
88
- this.logEvent({ message: 'device info', deviceInfo: this.deviceInfo, features: this.features });
70
+ yield this.subscribeWriteResponse(WAHOO_ADVANCED_TRAINER_CP);
71
+ yield _super.initDevice.call(this);
72
+ return true;
89
73
  }
90
74
  catch (err) {
91
- return Promise.resolve(false);
75
+ this.logEvent({ message: 'error', fn: 'WahooAdvancedFitnessMachineDevice.init()', error: err.message || err, stack: err.stack });
76
+ return false;
92
77
  }
93
78
  });
94
79
  }
@@ -195,7 +180,7 @@ class WahooAdvancedFitnessMachineDevice extends fm_1.default {
195
180
  const opcode = Buffer.alloc(1);
196
181
  opcode.writeUInt8(requestedOpCode, 0);
197
182
  const message = Buffer.concat([opcode, data]);
198
- const res = yield this.write(WAHOO_ADVANCED_FTMS, message);
183
+ const res = yield this.write(WAHOO_ADVANCED_TRAINER_CP, message);
199
184
  const responseData = Buffer.from(res);
200
185
  const result = responseData.readUInt8(0);
201
186
  this.logEvent({ message: 'response', opCode: requestedOpCode, response: responseData.toString('hex') });
@@ -311,8 +296,23 @@ class WahooAdvancedFitnessMachineDevice extends fm_1.default {
311
296
  setSlope(slope) {
312
297
  return __awaiter(this, void 0, void 0, function* () {
313
298
  this.logEvent({ message: 'setSlope', slope });
314
- const { windSpeed, crr, cw } = this;
315
- return yield this.setIndoorBikeSimulation(windSpeed, slope, crr, cw);
299
+ if (this.prevSlope !== undefined && slope === this.prevSlope)
300
+ return;
301
+ try {
302
+ const hasControl = yield this.requestControl();
303
+ if (!hasControl) {
304
+ this.logEvent({ message: 'setTargetPower failed', reason: 'control is disabled' });
305
+ return false;
306
+ }
307
+ const res = yield this.setSimGrade(slope);
308
+ this.logEvent({ message: 'setSlope result', res });
309
+ this.prevSlope = slope;
310
+ return res;
311
+ }
312
+ catch (err) {
313
+ this.logEvent({ message: 'setSlope failed', reason: err.message || err });
314
+ this.prevSlope = undefined;
315
+ }
316
316
  });
317
317
  }
318
318
  reset() {
@@ -320,7 +320,7 @@ class WahooAdvancedFitnessMachineDevice extends fm_1.default {
320
320
  }
321
321
  }
322
322
  exports.default = WahooAdvancedFitnessMachineDevice;
323
- WahooAdvancedFitnessMachineDevice.services = ['a026ee0b'];
323
+ WahooAdvancedFitnessMachineDevice.services = ['1818'];
324
324
  WahooAdvancedFitnessMachineDevice.characteristics = ['2acc', '2ad2', '2ad6', '2ad8', '2ad9', '2ada', WAHOO_ADVANCED_TRAINER_CP];
325
325
  ble_interface_1.default.register('WahooAdvancedFitnessMachineDevice', 'wahoo-fm', WahooAdvancedFitnessMachineDevice, WahooAdvancedFitnessMachineDevice.services);
326
326
  class WahooAdvancedFmAdapter extends fm_1.FmAdapter {
@@ -1,7 +1,7 @@
1
1
  export declare const DEFAULT_AGE = 30;
2
2
  export declare const DEFAULT_USER_WEIGHT = 75;
3
3
  export declare const DEFAULT_BIKE_WEIGHT = 10;
4
- export declare function getCockpit(c: any): "Cardio" | "Fitness" | "Vita De Luxe" | "8008" | "8008 TRS" | "8080" | "Therapie" | "8008 TRS Pro" | "8008 TRS3" | "Unknown";
4
+ export declare function getCockpit(c: any): "Cardio" | "Fitness" | "Vita De Luxe" | "8008" | "8008 TRS" | "8080" | "Therapie" | "8008 TRS Pro" | "8008 TRS3" | "ergo_lyps Cardio Pro" | "Unknown";
5
5
  export declare function getBikeType(type: any): 1 | 0;
6
6
  export declare function getGender(sex: any): 1 | 2;
7
7
  export declare function getLength(length: any): number;
@@ -24,6 +24,8 @@ function getCockpit(c) {
24
24
  return "8008 TRS Pro";
25
25
  case 160:
26
26
  return "8008 TRS3";
27
+ case 0x8D:
28
+ return "ergo_lyps Cardio Pro";
27
29
  default:
28
30
  return "Unknown";
29
31
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "incyclist-devices",
3
- "version": "1.4.67",
3
+ "version": "1.4.70",
4
4
  "dependencies": {
5
5
  "@serialport/parser-byte-length": "^9.0.1",
6
6
  "@serialport/parser-delimiter": "^9.0.1",