incyclist-devices 3.0.27 → 3.0.29

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.
@@ -345,7 +345,10 @@ class BleAdapter extends adpater_js_1.default {
345
345
  }
346
346
  const sensor = this.getSensor();
347
347
  let connected = await sensor.startSensor();
348
- await sensor.subscribe();
348
+ const subscribed = await sensor.subscribe();
349
+ if (connected && !subscribed) {
350
+ connected = false;
351
+ }
349
352
  if (connected) {
350
353
  sensor.on('data', this.onDeviceDataHandler);
351
354
  sensor.on('disconnected', this.onDeviceDisconnectHandler);
@@ -15,6 +15,7 @@ class BlePeripheral {
15
15
  disconnecting = false;
16
16
  disconnectedSignalled = false;
17
17
  discoveredServiceUUIds;
18
+ serviceDiscoveryIncomplete = false;
18
19
  discoverServicesPromise;
19
20
  discoverCharacteristicsPromise = {};
20
21
  onErrorHandler = this.onPeripheralError.bind(this);
@@ -159,8 +160,11 @@ class BlePeripheral {
159
160
  const res = await promise;
160
161
  (0, utils_js_1.sleep)(0).then(() => { delete this.discoverServicesPromise; });
161
162
  const isComplete = this.checkAnnouncedServices(res);
163
+ this.serviceDiscoveryIncomplete = !isComplete;
162
164
  if (!isComplete) {
163
- this.logEvent({ message: 'service data incomplete - disconnecting' });
165
+ const { name, address } = this.getInfo();
166
+ this.logEvent({ message: 'service data incomplete - disconnecting', name, address,
167
+ announced: this.getAnnouncedServices(), discovered: this.getDiscoveredServices() });
164
168
  }
165
169
  return res;
166
170
  }
@@ -324,6 +328,14 @@ class BlePeripheral {
324
328
  }
325
329
  catch { }
326
330
  }
331
+ if (this.serviceDiscoveryIncomplete) {
332
+ this.logEvent({ message: 'peripheral subscribe selected failed', uuids, reason: 'service discovery incomplete',
333
+ announced: this.getAnnouncedServices(), discovered: this.getDiscoveredServices() });
334
+ this.discoveredServiceUUIds = undefined;
335
+ this.serviceDiscoveryIncomplete = false;
336
+ await this.disconnect().catch(() => { });
337
+ return false;
338
+ }
327
339
  try {
328
340
  if (Object.keys(this.characteristics).length === 0) {
329
341
  await this.discoverAllCharacteristics();
@@ -364,6 +376,11 @@ class BlePeripheral {
364
376
  const res = await this.getPeripheral().discoverSomeServicesAndCharacteristicsAsync([], []);
365
377
  const found = [];
366
378
  const uuids = [];
379
+ const freshUuids = new Set(res.characteristics.map(c => (0, utils_js_2.beautifyUUID)(c.uuid)));
380
+ Object.keys(this.characteristics).forEach(existing => {
381
+ if (!freshUuids.has(existing))
382
+ delete this.characteristics[existing];
383
+ });
367
384
  res.characteristics.forEach(c => {
368
385
  this.characteristics[(0, utils_js_2.beautifyUUID)(c.uuid)] = c;
369
386
  found.push(c.uuid);
@@ -382,6 +399,12 @@ class BlePeripheral {
382
399
  const target = characteristics.map(c => (0, utils_js_2.fullUUID)(c));
383
400
  const res = await this.getPeripheral().discoverSomeServicesAndCharacteristicsAsync([], target);
384
401
  const found = [];
402
+ const freshUuids = new Set(res.characteristics.map(c => (0, utils_js_2.beautifyUUID)(c.uuid)));
403
+ characteristics.forEach(requested => {
404
+ const requestedUuid = (0, utils_js_2.beautifyUUID)(requested);
405
+ if (!freshUuids.has(requestedUuid))
406
+ delete this.characteristics[requestedUuid];
407
+ });
385
408
  res.characteristics.forEach(c => {
386
409
  this.characteristics[(0, utils_js_2.beautifyUUID)(c.uuid)] = c;
387
410
  found.push(c.uuid);
@@ -79,6 +79,7 @@ class TBleSensor extends node_events_1.EventEmitter {
79
79
  }
80
80
  this.connectPromise = this.connectPromise ?? this.peripheral.connect();
81
81
  const connected = await this.connectPromise;
82
+ delete this.connectPromise;
82
83
  if (!connected)
83
84
  return false;
84
85
  if (!reconnect)
@@ -8,6 +8,8 @@ const consts_js_2 = require("../consts.js");
8
8
  const consts_js_3 = require("./consts.js");
9
9
  const sensor_js_1 = __importDefault(require("../fm/sensor.js"));
10
10
  const utils_js_1 = require("../utils.js");
11
+ const task_js_1 = require("../../utils/task.js");
12
+ const WAHOO_CP_WRITE_TIMEOUT = 800;
11
13
  class BleWahooDevice extends sensor_js_1.default {
12
14
  static profile = 'Smart Trainer';
13
15
  static protocol = 'wahoo';
@@ -234,7 +236,22 @@ class BleWahooDevice extends sensor_js_1.default {
234
236
  opcode.writeUInt8(requestedOpCode, 0);
235
237
  const message = Buffer.concat([opcode, data]);
236
238
  this.logEvent({ message: 'wahoo cp:write', data: message.toString('hex') });
237
- const res = await this.write(this.wahooCP, message, props);
239
+ let res;
240
+ if (props?.timeout) {
241
+ const internalAbort = new AbortController();
242
+ const combined = new AbortController();
243
+ props.signal?.addEventListener('abort', () => combined.abort(), { once: true });
244
+ internalAbort.signal.addEventListener('abort', () => combined.abort(), { once: true });
245
+ const signal = combined.signal;
246
+ res = await new task_js_1.InteruptableTask(this.write(this.wahooCP, message, { ...props, signal }), {
247
+ timeout: props.timeout,
248
+ errorOnTimeout: true,
249
+ onCancel: () => internalAbort.abort()
250
+ }).run();
251
+ }
252
+ else {
253
+ res = await this.write(this.wahooCP, message, props);
254
+ }
238
255
  const responseData = Buffer.from(res);
239
256
  const result = responseData.readUInt8(0);
240
257
  return result === 1;
@@ -263,7 +280,7 @@ class BleWahooDevice extends sensor_js_1.default {
263
280
  return false;
264
281
  const data = Buffer.alloc(2);
265
282
  data.writeInt16LE(Math.round(power), 0);
266
- const res = await this.writeWahooFtmsMessage(66, data);
283
+ const res = await this.writeWahooFtmsMessage(66, data, { timeout: WAHOO_CP_WRITE_TIMEOUT });
267
284
  if (res === true) {
268
285
  this.setPowerAdjusting();
269
286
  this.data.targetPower = power;
@@ -298,7 +315,7 @@ class BleWahooDevice extends sensor_js_1.default {
298
315
  data.writeInt16LE(Math.round(weight * 100), 0);
299
316
  data.writeInt16LE(Math.round(crr * 10000), 2);
300
317
  data.writeInt16LE(Math.round(cw * 1000), 4);
301
- const res = await this.writeWahooFtmsMessage(67, data);
318
+ const res = await this.writeWahooFtmsMessage(67, data, { timeout: WAHOO_CP_WRITE_TIMEOUT });
302
319
  this.isSimMode = true;
303
320
  this.simModeSettings = { weight, crr, cw };
304
321
  return res;
@@ -313,7 +330,7 @@ class BleWahooDevice extends sensor_js_1.default {
313
330
  try {
314
331
  const data = Buffer.alloc(2);
315
332
  data.writeInt16LE(Math.round(crr * 10000), 0);
316
- const res = await this.writeWahooFtmsMessage(68, data);
333
+ const res = await this.writeWahooFtmsMessage(68, data, { timeout: WAHOO_CP_WRITE_TIMEOUT });
317
334
  return res;
318
335
  }
319
336
  catch (err) {
@@ -326,7 +343,7 @@ class BleWahooDevice extends sensor_js_1.default {
326
343
  try {
327
344
  const data = Buffer.alloc(2);
328
345
  data.writeInt16LE(Math.round(cw * 1000), 0);
329
- const res = await this.writeWahooFtmsMessage(69, data);
346
+ const res = await this.writeWahooFtmsMessage(69, data, { timeout: WAHOO_CP_WRITE_TIMEOUT });
330
347
  return res;
331
348
  }
332
349
  catch (err) {
@@ -345,7 +362,7 @@ class BleWahooDevice extends sensor_js_1.default {
345
362
  const slopeVal = Math.min(Math.round((1 + s / 100) * 65535 / 2.0), 65535);
346
363
  const data = Buffer.alloc(2);
347
364
  data.writeUInt16LE(slopeVal, 0);
348
- const res = await this.writeWahooFtmsMessage(70, data);
365
+ const res = await this.writeWahooFtmsMessage(70, data, { timeout: WAHOO_CP_WRITE_TIMEOUT });
349
366
  return res;
350
367
  }
351
368
  catch (err) {
@@ -359,7 +376,7 @@ class BleWahooDevice extends sensor_js_1.default {
359
376
  const value = (Math.max(-32.767, Math.min(32.767, v)) + 32.767) * 1000;
360
377
  const data = Buffer.alloc(2);
361
378
  data.writeInt16LE(Math.round(value), 0);
362
- const res = await this.writeWahooFtmsMessage(71, data);
379
+ const res = await this.writeWahooFtmsMessage(71, data, { timeout: WAHOO_CP_WRITE_TIMEOUT });
363
380
  return res;
364
381
  }
365
382
  catch (err) {
@@ -373,5 +390,20 @@ class BleWahooDevice extends sensor_js_1.default {
373
390
  getWheelCircumference() {
374
391
  return this.wheelCircumference;
375
392
  }
393
+ async startRequest() {
394
+ return true;
395
+ }
396
+ async stopRequest() {
397
+ return true;
398
+ }
399
+ async PauseRequest() {
400
+ return true;
401
+ }
402
+ async setTargetInclination(_inclination) {
403
+ return true;
404
+ }
405
+ async setTargetResistanceLevel(_resistanceLevel) {
406
+ return true;
407
+ }
376
408
  }
377
409
  exports.default = BleWahooDevice;
@@ -340,7 +340,10 @@ export default class BleAdapter extends IncyclistDevice {
340
340
  }
341
341
  const sensor = this.getSensor();
342
342
  let connected = await sensor.startSensor();
343
- await sensor.subscribe();
343
+ const subscribed = await sensor.subscribe();
344
+ if (connected && !subscribed) {
345
+ connected = false;
346
+ }
344
347
  if (connected) {
345
348
  sensor.on('data', this.onDeviceDataHandler);
346
349
  sensor.on('disconnected', this.onDeviceDisconnectHandler);
@@ -12,6 +12,7 @@ export class BlePeripheral {
12
12
  disconnecting = false;
13
13
  disconnectedSignalled = false;
14
14
  discoveredServiceUUIds;
15
+ serviceDiscoveryIncomplete = false;
15
16
  discoverServicesPromise;
16
17
  discoverCharacteristicsPromise = {};
17
18
  onErrorHandler = this.onPeripheralError.bind(this);
@@ -156,8 +157,11 @@ export class BlePeripheral {
156
157
  const res = await promise;
157
158
  sleep(0).then(() => { delete this.discoverServicesPromise; });
158
159
  const isComplete = this.checkAnnouncedServices(res);
160
+ this.serviceDiscoveryIncomplete = !isComplete;
159
161
  if (!isComplete) {
160
- this.logEvent({ message: 'service data incomplete - disconnecting' });
162
+ const { name, address } = this.getInfo();
163
+ this.logEvent({ message: 'service data incomplete - disconnecting', name, address,
164
+ announced: this.getAnnouncedServices(), discovered: this.getDiscoveredServices() });
161
165
  }
162
166
  return res;
163
167
  }
@@ -321,6 +325,14 @@ export class BlePeripheral {
321
325
  }
322
326
  catch { }
323
327
  }
328
+ if (this.serviceDiscoveryIncomplete) {
329
+ this.logEvent({ message: 'peripheral subscribe selected failed', uuids, reason: 'service discovery incomplete',
330
+ announced: this.getAnnouncedServices(), discovered: this.getDiscoveredServices() });
331
+ this.discoveredServiceUUIds = undefined;
332
+ this.serviceDiscoveryIncomplete = false;
333
+ await this.disconnect().catch(() => { });
334
+ return false;
335
+ }
324
336
  try {
325
337
  if (Object.keys(this.characteristics).length === 0) {
326
338
  await this.discoverAllCharacteristics();
@@ -361,6 +373,11 @@ export class BlePeripheral {
361
373
  const res = await this.getPeripheral().discoverSomeServicesAndCharacteristicsAsync([], []);
362
374
  const found = [];
363
375
  const uuids = [];
376
+ const freshUuids = new Set(res.characteristics.map(c => beautifyUUID(c.uuid)));
377
+ Object.keys(this.characteristics).forEach(existing => {
378
+ if (!freshUuids.has(existing))
379
+ delete this.characteristics[existing];
380
+ });
364
381
  res.characteristics.forEach(c => {
365
382
  this.characteristics[beautifyUUID(c.uuid)] = c;
366
383
  found.push(c.uuid);
@@ -379,6 +396,12 @@ export class BlePeripheral {
379
396
  const target = characteristics.map(c => fullUUID(c));
380
397
  const res = await this.getPeripheral().discoverSomeServicesAndCharacteristicsAsync([], target);
381
398
  const found = [];
399
+ const freshUuids = new Set(res.characteristics.map(c => beautifyUUID(c.uuid)));
400
+ characteristics.forEach(requested => {
401
+ const requestedUuid = beautifyUUID(requested);
402
+ if (!freshUuids.has(requestedUuid))
403
+ delete this.characteristics[requestedUuid];
404
+ });
382
405
  res.characteristics.forEach(c => {
383
406
  this.characteristics[beautifyUUID(c.uuid)] = c;
384
407
  found.push(c.uuid);
@@ -76,6 +76,7 @@ export class TBleSensor extends EventEmitter {
76
76
  }
77
77
  this.connectPromise = this.connectPromise ?? this.peripheral.connect();
78
78
  const connected = await this.connectPromise;
79
+ delete this.connectPromise;
79
80
  if (!connected)
80
81
  return false;
81
82
  if (!reconnect)
@@ -3,6 +3,8 @@ import { CSP, CSP_MEASUREMENT, FTMS_CP, FTMS_FEATURE, FTMS_STATUS, HR_MEASUREMEN
3
3
  import { WAHOO_ADVANCED_FTMS, WAHOO_ADVANCED_TRAINER_CP, ErgWriteDelay } from './consts.js';
4
4
  import BleFitnessMachineDevice from "../fm/sensor.js";
5
5
  import { beautifyUUID } from "../utils.js";
6
+ import { InteruptableTask } from "../../utils/task.js";
7
+ const WAHOO_CP_WRITE_TIMEOUT = 800;
6
8
  export default class BleWahooDevice extends BleFitnessMachineDevice {
7
9
  static profile = 'Smart Trainer';
8
10
  static protocol = 'wahoo';
@@ -229,7 +231,22 @@ export default class BleWahooDevice extends BleFitnessMachineDevice {
229
231
  opcode.writeUInt8(requestedOpCode, 0);
230
232
  const message = Buffer.concat([opcode, data]);
231
233
  this.logEvent({ message: 'wahoo cp:write', data: message.toString('hex') });
232
- const res = await this.write(this.wahooCP, message, props);
234
+ let res;
235
+ if (props?.timeout) {
236
+ const internalAbort = new AbortController();
237
+ const combined = new AbortController();
238
+ props.signal?.addEventListener('abort', () => combined.abort(), { once: true });
239
+ internalAbort.signal.addEventListener('abort', () => combined.abort(), { once: true });
240
+ const signal = combined.signal;
241
+ res = await new InteruptableTask(this.write(this.wahooCP, message, { ...props, signal }), {
242
+ timeout: props.timeout,
243
+ errorOnTimeout: true,
244
+ onCancel: () => internalAbort.abort()
245
+ }).run();
246
+ }
247
+ else {
248
+ res = await this.write(this.wahooCP, message, props);
249
+ }
233
250
  const responseData = Buffer.from(res);
234
251
  const result = responseData.readUInt8(0);
235
252
  return result === 1;
@@ -258,7 +275,7 @@ export default class BleWahooDevice extends BleFitnessMachineDevice {
258
275
  return false;
259
276
  const data = Buffer.alloc(2);
260
277
  data.writeInt16LE(Math.round(power), 0);
261
- const res = await this.writeWahooFtmsMessage(66, data);
278
+ const res = await this.writeWahooFtmsMessage(66, data, { timeout: WAHOO_CP_WRITE_TIMEOUT });
262
279
  if (res === true) {
263
280
  this.setPowerAdjusting();
264
281
  this.data.targetPower = power;
@@ -293,7 +310,7 @@ export default class BleWahooDevice extends BleFitnessMachineDevice {
293
310
  data.writeInt16LE(Math.round(weight * 100), 0);
294
311
  data.writeInt16LE(Math.round(crr * 10000), 2);
295
312
  data.writeInt16LE(Math.round(cw * 1000), 4);
296
- const res = await this.writeWahooFtmsMessage(67, data);
313
+ const res = await this.writeWahooFtmsMessage(67, data, { timeout: WAHOO_CP_WRITE_TIMEOUT });
297
314
  this.isSimMode = true;
298
315
  this.simModeSettings = { weight, crr, cw };
299
316
  return res;
@@ -308,7 +325,7 @@ export default class BleWahooDevice extends BleFitnessMachineDevice {
308
325
  try {
309
326
  const data = Buffer.alloc(2);
310
327
  data.writeInt16LE(Math.round(crr * 10000), 0);
311
- const res = await this.writeWahooFtmsMessage(68, data);
328
+ const res = await this.writeWahooFtmsMessage(68, data, { timeout: WAHOO_CP_WRITE_TIMEOUT });
312
329
  return res;
313
330
  }
314
331
  catch (err) {
@@ -321,7 +338,7 @@ export default class BleWahooDevice extends BleFitnessMachineDevice {
321
338
  try {
322
339
  const data = Buffer.alloc(2);
323
340
  data.writeInt16LE(Math.round(cw * 1000), 0);
324
- const res = await this.writeWahooFtmsMessage(69, data);
341
+ const res = await this.writeWahooFtmsMessage(69, data, { timeout: WAHOO_CP_WRITE_TIMEOUT });
325
342
  return res;
326
343
  }
327
344
  catch (err) {
@@ -340,7 +357,7 @@ export default class BleWahooDevice extends BleFitnessMachineDevice {
340
357
  const slopeVal = Math.min(Math.round((1 + s / 100) * 65535 / 2.0), 65535);
341
358
  const data = Buffer.alloc(2);
342
359
  data.writeUInt16LE(slopeVal, 0);
343
- const res = await this.writeWahooFtmsMessage(70, data);
360
+ const res = await this.writeWahooFtmsMessage(70, data, { timeout: WAHOO_CP_WRITE_TIMEOUT });
344
361
  return res;
345
362
  }
346
363
  catch (err) {
@@ -354,7 +371,7 @@ export default class BleWahooDevice extends BleFitnessMachineDevice {
354
371
  const value = (Math.max(-32.767, Math.min(32.767, v)) + 32.767) * 1000;
355
372
  const data = Buffer.alloc(2);
356
373
  data.writeInt16LE(Math.round(value), 0);
357
- const res = await this.writeWahooFtmsMessage(71, data);
374
+ const res = await this.writeWahooFtmsMessage(71, data, { timeout: WAHOO_CP_WRITE_TIMEOUT });
358
375
  return res;
359
376
  }
360
377
  catch (err) {
@@ -368,4 +385,19 @@ export default class BleWahooDevice extends BleFitnessMachineDevice {
368
385
  getWheelCircumference() {
369
386
  return this.wheelCircumference;
370
387
  }
388
+ async startRequest() {
389
+ return true;
390
+ }
391
+ async stopRequest() {
392
+ return true;
393
+ }
394
+ async PauseRequest() {
395
+ return true;
396
+ }
397
+ async setTargetInclination(_inclination) {
398
+ return true;
399
+ }
400
+ async setTargetResistanceLevel(_resistanceLevel) {
401
+ return true;
402
+ }
371
403
  }
@@ -14,6 +14,7 @@ export declare class BlePeripheral implements IBlePeripheral {
14
14
  protected disconnecting: boolean;
15
15
  protected disconnectedSignalled: boolean;
16
16
  protected discoveredServiceUUIds: Array<string> | undefined;
17
+ protected serviceDiscoveryIncomplete: boolean;
17
18
  protected discoverServicesPromise: Promise<string[]> | undefined;
18
19
  protected discoverCharacteristicsPromise: Record<string, Promise<BleCharacteristic[]> | undefined>;
19
20
  protected onErrorHandler: any;
@@ -51,4 +51,9 @@ export default class BleWahooDevice extends BleFitnessMachineDevice {
51
51
  setSimWindSpeed(v: number): Promise<boolean>;
52
52
  setWheelCircumference(wheelCircumference: number): Promise<void>;
53
53
  getWheelCircumference(): number;
54
+ startRequest(): Promise<boolean>;
55
+ stopRequest(): Promise<boolean>;
56
+ PauseRequest(): Promise<boolean>;
57
+ setTargetInclination(_inclination: number): Promise<boolean>;
58
+ setTargetResistanceLevel(_resistanceLevel: number): Promise<boolean>;
54
59
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "incyclist-devices",
3
- "version": "3.0.27",
3
+ "version": "3.0.29",
4
4
  "scripts": {
5
5
  "lint": "eslint . --ext .ts",
6
6
  "build": "npm run build:esm && npm run build:cjs",