incyclist-devices 1.4.64 → 1.4.67

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.
@@ -30,6 +30,7 @@ export declare abstract class BleDevice extends BleDeviceClass {
30
30
  logEvent(event: any): void;
31
31
  setLogger(logger: EventLogger): void;
32
32
  setInterface(ble: BleInterfaceClass): void;
33
+ isMatching(characteristics: string[]): boolean;
33
34
  cleanupListeners(): void;
34
35
  onDisconnect(): void;
35
36
  waitForConnectFinished(timeout: any): Promise<unknown>;
@@ -47,7 +47,9 @@ class BleDevice extends ble_1.BleDeviceClass {
47
47
  if (this.logger) {
48
48
  this.logger.logEvent(event);
49
49
  }
50
- console.log('~~~BLE:', event);
50
+ if (process.env.BLE_DEBUG) {
51
+ console.log('~~~BLE:', event);
52
+ }
51
53
  }
52
54
  setLogger(logger) {
53
55
  this.logger = logger;
@@ -55,6 +57,9 @@ class BleDevice extends ble_1.BleDeviceClass {
55
57
  setInterface(ble) {
56
58
  this.ble = ble;
57
59
  }
60
+ isMatching(characteristics) {
61
+ return true;
62
+ }
58
63
  cleanupListeners() {
59
64
  if (this.characteristics === undefined) {
60
65
  this.characteristics = [];
@@ -244,7 +249,6 @@ class BleDevice extends ble_1.BleDeviceClass {
244
249
  if (writeIdx !== -1) {
245
250
  const writeItem = this.writeQueue[writeIdx];
246
251
  this.writeQueue.splice(writeIdx, 1);
247
- console.log('~~~ write queue', this.writeQueue);
248
252
  if (writeItem.resolve)
249
253
  writeItem.resolve(data);
250
254
  }
@@ -255,9 +259,9 @@ class BleDevice extends ble_1.BleDeviceClass {
255
259
  try {
256
260
  const connector = this.ble.getConnector(this.peripheral);
257
261
  const isAlreadySubscribed = connector.isSubscribed(characteristicUuid);
258
- console.log('~~~ write ', characteristicUuid, data.toString('hex'), isAlreadySubscribed, this.subscribedCharacteristics);
259
262
  if (!withoutResponse && !isAlreadySubscribed) {
260
263
  const connector = this.ble.getConnector(this.peripheral);
264
+ connector.removeAllListeners(characteristicUuid);
261
265
  connector.on(characteristicUuid, (uuid, data) => {
262
266
  this.onData(uuid, data);
263
267
  });
@@ -282,12 +286,11 @@ class BleDevice extends ble_1.BleDeviceClass {
282
286
  let messageDeleted = false;
283
287
  this.writeQueue.push({ uuid: characteristicUuid.toLocaleLowerCase(), data, resolve, reject });
284
288
  const to = setTimeout(() => {
285
- console.log('~~~ write timeout');
286
289
  if (this.writeQueue.length > writeId && !messageDeleted)
287
290
  this.writeQueue.splice(writeId, 1);
288
291
  this.logEvent({ message: 'writing response', err: 'timeout' });
289
292
  reject(new Error('timeout'));
290
- }, 1000);
293
+ }, 5000);
291
294
  this.logEvent({ message: 'writing' });
292
295
  characteristic.write(data, withoutResponse, (err) => {
293
296
  clearTimeout(to);
@@ -77,6 +77,7 @@ export default class BleInterface extends BleInterfaceClass {
77
77
  getDeviceClasses(peripheral: any, props?: {
78
78
  deviceTypes?: (typeof BleDeviceClass)[];
79
79
  profile?: string;
80
+ services?: string[];
80
81
  }): (typeof BleDeviceClass)[];
81
82
  createDevice(DeviceClass: (typeof BleDeviceClass), peripheral: BlePeripheral, characteristics?: BleCharacteristic[]): any;
82
83
  connectDevice(requested: BleDeviceClass | BleDeviceDescription, timeout?: number): Promise<BleDeviceClass>;
@@ -336,13 +336,13 @@ class BleInterface extends ble_1.BleInterfaceClass {
336
336
  }
337
337
  getDeviceClasses(peripheral, props = {}) {
338
338
  let DeviceClasses;
339
- const { deviceTypes, profile } = props;
339
+ const { deviceTypes, profile, services = peripheral.advertisement.serviceUuids } = props;
340
340
  if ((!deviceTypes || deviceTypes.length === 0)) {
341
341
  const classes = BleInterface.deviceClasses.map(c => c.Class);
342
- DeviceClasses = this.getDevicesFromServices(classes, peripheral.advertisement.serviceUuids);
342
+ DeviceClasses = this.getDevicesFromServices(classes, services);
343
343
  }
344
344
  else {
345
- DeviceClasses = this.getDevicesFromServices(deviceTypes, peripheral.advertisement.serviceUuids);
345
+ DeviceClasses = this.getDevicesFromServices(deviceTypes, services);
346
346
  }
347
347
  if (profile && DeviceClasses && DeviceClasses.length > 0) {
348
348
  DeviceClasses = DeviceClasses.filter(C => {
@@ -355,15 +355,26 @@ class BleInterface extends ble_1.BleInterfaceClass {
355
355
  return DeviceClasses;
356
356
  }
357
357
  createDevice(DeviceClass, peripheral, characteristics) {
358
- this.logEvent({ message: 'trying to create device', peripheral: peripheral.address, characteristics });
359
- const C = DeviceClass;
360
- const device = new C({ peripheral });
361
- const existingDevice = this.devices.find(i => i.device.id === device.id && i.device.getProfile() === device.getProfile());
362
- if (existingDevice)
363
- return existingDevice;
364
- device.setInterface(this);
365
- device.characteristics = characteristics;
366
- return device;
358
+ try {
359
+ const C = DeviceClass;
360
+ const device = new C({ peripheral });
361
+ const cids = characteristics ? characteristics.map(c => (0, ble_1.uuid)(c.uuid)) : [];
362
+ this.logEvent({ message: 'trying to create device', peripheral: peripheral.address, characteristics: cids, profile: device.getProfile() });
363
+ const existingDevice = this.devices.find(i => i.device.id === device.id && i.device.getProfile() === device.getProfile());
364
+ if (existingDevice)
365
+ return existingDevice;
366
+ device.setInterface(this);
367
+ if (characteristics && device.isMatching(cids)) {
368
+ device.characteristics = characteristics;
369
+ return device;
370
+ }
371
+ else {
372
+ this.logEvent({ message: 'failed to create device', peripheral: peripheral.address, profile: device.getProfile() });
373
+ }
374
+ }
375
+ catch (err) {
376
+ this.logEvent({ message: 'error', fn: '', error: err.message || err, stack: err.stack });
377
+ }
367
378
  }
368
379
  connectDevice(requested, timeout = DEFAULT_SCAN_TIMEOUT + CONNECT_TIMEOUT) {
369
380
  return __awaiter(this, void 0, void 0, function* () {
@@ -540,8 +551,8 @@ class BleInterface extends ble_1.BleInterfaceClass {
540
551
  if (fromCache)
541
552
  this.logEvent({ message: 'adding from Cache', peripheral: peripheral.address });
542
553
  else {
543
- const { id, name, address } = peripheral;
544
- this.logEvent({ message: 'BLE scan: found device', peripheral: { id, name, address } });
554
+ const { id, name, address, advertisement = {} } = peripheral;
555
+ this.logEvent({ message: 'BLE scan: found device', peripheral: { id, name, address, services: advertisement.serviceUuids } });
545
556
  }
546
557
  if (!peripheral || !peripheral.advertisement || !peripheral.advertisement.serviceUuids || peripheral.advertisement.serviceUuids.length === 0)
547
558
  return;
@@ -552,7 +563,13 @@ class BleInterface extends ble_1.BleInterfaceClass {
552
563
  return;
553
564
  peripheralsProcessed.push(peripheral.address);
554
565
  const characteristics = yield this.getCharacteristics(peripheral);
555
- const DeviceClasses = this.getDeviceClasses(peripheral, { profile });
566
+ const connector = this.getConnector(peripheral);
567
+ const connectedServices = connector.getServices();
568
+ const services = connectedServices ? connectedServices.map(cs => cs.uuid) : undefined;
569
+ const connectedPeripheral = connector.getPeripheral();
570
+ const { id, name, address, advertisement = {} } = connectedPeripheral;
571
+ const DeviceClasses = this.getDeviceClasses(connectedPeripheral, { profile, services });
572
+ this.logEvent({ message: 'BLE scan: device connected', peripheral: { id, name, address, services: advertisement.serviceUuids }, services, classes: DeviceClasses.map(c => c.prototype.constructor.name) });
556
573
  let cntFound = 0;
557
574
  DeviceClasses.forEach((DeviceClass) => __awaiter(this, void 0, void 0, function* () {
558
575
  if (!DeviceClass)
@@ -560,6 +577,8 @@ class BleInterface extends ble_1.BleInterfaceClass {
560
577
  if (scanForDevice && cntFound > 0)
561
578
  return;
562
579
  const d = this.createDevice(DeviceClass, peripheral, characteristics);
580
+ if (!d)
581
+ return;
563
582
  yield d.connect();
564
583
  if (scanForDevice) {
565
584
  if ((id && id !== '' && d.id === id) ||
@@ -32,4 +32,5 @@ export default class BlePeripheralConnector {
32
32
  getState(): string;
33
33
  getCharachteristics(): BleCharacteristic[];
34
34
  getServices(): string[];
35
+ getPeripheral(): BlePeripheral;
35
36
  }
@@ -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* () {
@@ -110,7 +112,6 @@ class BlePeripheralConnector {
110
112
  try {
111
113
  yield this.subscribe(c.uuid);
112
114
  subscribed.push(c.uuid);
113
- this.state.subscribed.push(c.uuid);
114
115
  }
115
116
  catch (err) {
116
117
  this.logEvent({ message: 'cannot subscribe', peripheral: this.peripheral.address, characteristic: c.uuid, error: err.message || err });
@@ -137,6 +138,7 @@ class BlePeripheralConnector {
137
138
  reject(new Error('Characteristic not found'));
138
139
  return;
139
140
  }
141
+ characteristic.removeAllListeners('data');
140
142
  characteristic.on('data', (data, _isNotification) => {
141
143
  this.onData(characteristicUuid, data);
142
144
  });
@@ -149,8 +151,10 @@ class BlePeripheralConnector {
149
151
  this.logEvent({ message: 'subscribe result', characteristic: characteristicUuid, error: err });
150
152
  if (err)
151
153
  reject(err);
152
- else
154
+ else {
155
+ this.state.subscribed.push(characteristicUuid);
153
156
  resolve(true);
157
+ }
154
158
  });
155
159
  }
156
160
  catch (err) {
@@ -181,5 +185,8 @@ class BlePeripheralConnector {
181
185
  getServices() {
182
186
  return this.services;
183
187
  }
188
+ getPeripheral() {
189
+ return this.peripheral;
190
+ }
184
191
  }
185
192
  exports.default = BlePeripheralConnector;
package/lib/ble/fm.d.ts CHANGED
@@ -52,6 +52,7 @@ export default class BleFitnessMachineDevice extends BleDevice {
52
52
  windSpeed: number;
53
53
  wheelSize: number;
54
54
  constructor(props?: any);
55
+ isMatching(characteristics: string[]): boolean;
55
56
  init(): Promise<boolean>;
56
57
  onDisconnect(): void;
57
58
  getProfile(): string;
package/lib/ble/fm.js CHANGED
@@ -21,6 +21,8 @@ const power_meter_1 = __importDefault(require("../modes/power-meter"));
21
21
  const ble_st_mode_1 = __importDefault(require("./ble-st-mode"));
22
22
  const ble_erg_mode_1 = __importDefault(require("./ble-erg-mode"));
23
23
  const FTMS_CP = '2ad9';
24
+ const FTMS_STATUS = '2ada';
25
+ const INDOOR_BIKE_DATA = '2ad2';
24
26
  const cwABike = {
25
27
  race: 0.35,
26
28
  triathlon: 0.29,
@@ -94,12 +96,37 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
94
96
  this.wheelSize = 2100;
95
97
  this.data = {};
96
98
  }
99
+ isMatching(characteristics) {
100
+ if (!characteristics)
101
+ return false;
102
+ const hasStatus = characteristics.find(c => c === FTMS_STATUS) !== undefined;
103
+ const hasCP = characteristics.find(c => c === FTMS_CP) !== undefined;
104
+ const hasIndoorBike = characteristics.find(c => c === INDOOR_BIKE_DATA) !== undefined;
105
+ return hasStatus && hasCP && hasIndoorBike;
106
+ }
97
107
  init() {
98
108
  const _super = Object.create(null, {
99
109
  init: { get: () => super.init }
100
110
  });
101
111
  return __awaiter(this, void 0, void 0, function* () {
102
112
  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
+ }
103
130
  this.logEvent({ message: 'get device info' });
104
131
  yield _super.init.call(this);
105
132
  yield this.getFitnessMachineFeatures();
@@ -278,13 +305,13 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
278
305
  const uuid = characteristic.toLocaleLowerCase();
279
306
  let res = undefined;
280
307
  switch (uuid) {
281
- case '2ad2':
308
+ case INDOOR_BIKE_DATA:
282
309
  res = this.parseIndoorBikeData(data);
283
310
  break;
284
311
  case '2a37':
285
312
  res = this.parseHrm(data);
286
313
  break;
287
- case '2ada':
314
+ case FTMS_STATUS:
288
315
  res = this.parseFitnessMachineStatus(data);
289
316
  break;
290
317
  case '2a63':
@@ -452,7 +479,7 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
452
479
  }
453
480
  exports.default = BleFitnessMachineDevice;
454
481
  BleFitnessMachineDevice.services = ['1826'];
455
- BleFitnessMachineDevice.characteristics = ['2acc', '2ad2', '2ad6', '2ad8', '2ad9', '2ada'];
482
+ BleFitnessMachineDevice.characteristics = ['2acc', INDOOR_BIKE_DATA, '2ad6', '2ad8', FTMS_CP, FTMS_STATUS];
456
483
  ble_interface_1.default.register('BleFitnessMachineDevice', 'fm', BleFitnessMachineDevice, BleFitnessMachineDevice.services);
457
484
  class FmAdapter extends Device_1.default {
458
485
  constructor(device, protocol) {
package/lib/ble/pwr.d.ts CHANGED
@@ -33,6 +33,7 @@ export default class BleCyclingPowerDevice extends BleDevice {
33
33
  currentCrankData: CrankData;
34
34
  prevCrankData: CrankData;
35
35
  constructor(props?: any);
36
+ isMatching(characteristics: string[]): boolean;
36
37
  init(): Promise<boolean>;
37
38
  getProfile(): string;
38
39
  getServiceUUids(): string[];
package/lib/ble/pwr.js CHANGED
@@ -37,6 +37,8 @@ const ble_interface_1 = __importDefault(require("./ble-interface"));
37
37
  const Device_1 = __importStar(require("../Device"));
38
38
  const gd_eventlog_1 = require("gd-eventlog");
39
39
  const power_meter_1 = __importDefault(require("../modes/power-meter"));
40
+ const CP_MEASUREMENT = '2a63';
41
+ const CP_FEATURE = '2a65';
40
42
  class BleCyclingPowerDevice extends ble_device_1.BleDevice {
41
43
  constructor(props) {
42
44
  super(props);
@@ -49,6 +51,13 @@ class BleCyclingPowerDevice extends ble_device_1.BleDevice {
49
51
  this.currentCrankData = undefined;
50
52
  this.prevCrankData = undefined;
51
53
  }
54
+ isMatching(characteristics) {
55
+ if (!characteristics)
56
+ return false;
57
+ const hasCPMeasurement = characteristics.find(c => c === CP_MEASUREMENT) !== undefined;
58
+ const hasCPFeature = characteristics.find(c => c === CP_FEATURE) !== undefined;
59
+ return hasCPMeasurement && hasCPFeature;
60
+ }
52
61
  init() {
53
62
  const _super = Object.create(null, {
54
63
  init: { get: () => super.init }
@@ -131,7 +140,7 @@ class BleCyclingPowerDevice extends ble_device_1.BleDevice {
131
140
  }
132
141
  onData(characteristic, data) {
133
142
  super.onData(characteristic, data);
134
- if (characteristic.toLocaleLowerCase() === '2a63') {
143
+ if (characteristic.toLocaleLowerCase() === CP_MEASUREMENT) {
135
144
  const res = this.parsePower(data);
136
145
  this.emit('data', res);
137
146
  }
@@ -149,7 +158,7 @@ class BleCyclingPowerDevice extends ble_device_1.BleDevice {
149
158
  }
150
159
  exports.default = BleCyclingPowerDevice;
151
160
  BleCyclingPowerDevice.services = ['1818'];
152
- BleCyclingPowerDevice.characteristics = ['2a63', '2a65', '2a5d', '2a3c'];
161
+ BleCyclingPowerDevice.characteristics = [CP_MEASUREMENT, CP_FEATURE, '2a5d', '2a3c'];
153
162
  ble_interface_1.default.register('BleCyclingPowerDevice', 'cp', BleCyclingPowerDevice, BleCyclingPowerDevice.services);
154
163
  class PwrAdapter extends Device_1.default {
155
164
  constructor(device, protocol) {
@@ -35,6 +35,7 @@ export default class WahooAdvancedFitnessMachineDevice extends BleFitnessMachine
35
35
  timeOffset: number;
36
36
  tsPrevWrite: any;
37
37
  constructor(props?: any);
38
+ isMatching(characteristics: string[]): boolean;
38
39
  init(): Promise<boolean>;
39
40
  getProfile(): string;
40
41
  getServiceUUids(): string[];
@@ -36,7 +36,7 @@ const ble_interface_1 = __importDefault(require("./ble-interface"));
36
36
  const Device_1 = require("../Device");
37
37
  const gd_eventlog_1 = require("gd-eventlog");
38
38
  const fm_1 = __importStar(require("./fm"));
39
- const WAHOO_ADVANCED_FTMS = 'a026e005';
39
+ const WAHOO_ADVANCED_FTMS = 'a026e00b';
40
40
  const WAHOO_ADVANCED_TRAINER_CP = 'a026e037';
41
41
  const cwABike = {
42
42
  race: 0.35,
@@ -54,12 +54,35 @@ class WahooAdvancedFitnessMachineDevice extends fm_1.default {
54
54
  this.tsPrevWrite = undefined;
55
55
  this.data = {};
56
56
  }
57
+ isMatching(characteristics) {
58
+ if (!characteristics)
59
+ return false;
60
+ const hasWahooCP = characteristics.find(c => c === WAHOO_ADVANCED_TRAINER_CP) !== undefined;
61
+ return hasWahooCP;
62
+ }
57
63
  init() {
58
64
  const _super = Object.create(null, {
59
65
  init: { get: () => super.init }
60
66
  });
61
67
  return __awaiter(this, void 0, void 0, function* () {
62
68
  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
+ }
63
86
  this.logEvent({ message: 'get device info' });
64
87
  yield _super.init.call(this);
65
88
  this.logEvent({ message: 'device info', deviceInfo: this.deviceInfo, features: this.features });
@@ -298,7 +321,7 @@ class WahooAdvancedFitnessMachineDevice extends fm_1.default {
298
321
  }
299
322
  exports.default = WahooAdvancedFitnessMachineDevice;
300
323
  WahooAdvancedFitnessMachineDevice.services = ['a026ee0b'];
301
- WahooAdvancedFitnessMachineDevice.characteristics = ['2acc', '2ad2', '2ad6', '2ad8', '2ad9', '2ada', WAHOO_ADVANCED_FTMS, WAHOO_ADVANCED_TRAINER_CP];
324
+ WahooAdvancedFitnessMachineDevice.characteristics = ['2acc', '2ad2', '2ad6', '2ad8', '2ad9', '2ada', WAHOO_ADVANCED_TRAINER_CP];
302
325
  ble_interface_1.default.register('WahooAdvancedFitnessMachineDevice', 'wahoo-fm', WahooAdvancedFitnessMachineDevice, WahooAdvancedFitnessMachineDevice.services);
303
326
  class WahooAdvancedFmAdapter extends fm_1.FmAdapter {
304
327
  constructor(device, protocol) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "incyclist-devices",
3
- "version": "1.4.64",
3
+ "version": "1.4.67",
4
4
  "dependencies": {
5
5
  "@serialport/parser-byte-length": "^9.0.1",
6
6
  "@serialport/parser-delimiter": "^9.0.1",