incyclist-devices 1.4.63 → 1.4.64

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.
@@ -11,7 +11,6 @@ declare type CommandQueueItem = {
11
11
  data: Buffer;
12
12
  resolve: any;
13
13
  reject: any;
14
- timeout: any;
15
14
  };
16
15
  export declare abstract class BleDevice extends BleDeviceClass {
17
16
  id: string;
@@ -47,9 +47,7 @@ class BleDevice extends ble_1.BleDeviceClass {
47
47
  if (this.logger) {
48
48
  this.logger.logEvent(event);
49
49
  }
50
- if (process.env.BLE_DEBUG) {
51
- console.log('~~~BLE:', event);
52
- }
50
+ console.log('~~~BLE:', event);
53
51
  }
54
52
  setLogger(logger) {
55
53
  this.logger = logger;
@@ -246,6 +244,7 @@ class BleDevice extends ble_1.BleDeviceClass {
246
244
  if (writeIdx !== -1) {
247
245
  const writeItem = this.writeQueue[writeIdx];
248
246
  this.writeQueue.splice(writeIdx, 1);
247
+ console.log('~~~ write queue', this.writeQueue);
249
248
  if (writeItem.resolve)
250
249
  writeItem.resolve(data);
251
250
  }
@@ -253,34 +252,58 @@ class BleDevice extends ble_1.BleDeviceClass {
253
252
  }
254
253
  write(characteristicUuid, data, withoutResponse = false) {
255
254
  return __awaiter(this, void 0, void 0, function* () {
256
- if (!withoutResponse && this.subscribedCharacteristics.find(c => c === characteristicUuid) === undefined) {
255
+ try {
257
256
  const connector = this.ble.getConnector(this.peripheral);
258
- connector.on(characteristicUuid, (uuid, data) => {
259
- this.onData(uuid, data);
260
- });
261
- yield connector.subscribe(characteristicUuid);
262
- this.subscribedCharacteristics.push(characteristicUuid);
263
- }
264
- return new Promise((resolve, reject) => {
265
- const characteristic = this.characteristics.find(c => c.uuid === characteristicUuid || (0, ble_1.uuid)(c.uuid) === characteristicUuid);
266
- if (!characteristic) {
267
- reject(new Error('Characteristic not found'));
268
- return;
269
- }
270
- if (withoutResponse) {
271
- characteristic.write(data, withoutResponse);
272
- resolve(new ArrayBuffer(0));
273
- return;
257
+ const isAlreadySubscribed = connector.isSubscribed(characteristicUuid);
258
+ console.log('~~~ write ', characteristicUuid, data.toString('hex'), isAlreadySubscribed, this.subscribedCharacteristics);
259
+ if (!withoutResponse && !isAlreadySubscribed) {
260
+ const connector = this.ble.getConnector(this.peripheral);
261
+ connector.on(characteristicUuid, (uuid, data) => {
262
+ this.onData(uuid, data);
263
+ });
264
+ this.logEvent({ message: 'write:subscribing ', characteristic: characteristicUuid });
265
+ yield connector.subscribe(characteristicUuid);
266
+ this.subscribedCharacteristics.push(characteristicUuid);
274
267
  }
275
- const writeId = this.writeQueue.length;
276
- this.writeQueue.push({ uuid: characteristicUuid.toLocaleLowerCase(), data, resolve, reject, timeout: Date.now() + 1000 });
277
- characteristic.write(data, withoutResponse, (err) => {
278
- if (err) {
279
- this.writeQueue.splice(writeId, 1);
280
- reject(err);
268
+ return new Promise((resolve, reject) => {
269
+ const characteristic = this.characteristics.find(c => c.uuid === characteristicUuid || (0, ble_1.uuid)(c.uuid) === characteristicUuid);
270
+ if (!characteristic) {
271
+ reject(new Error('Characteristic not found'));
272
+ return;
273
+ }
274
+ if (withoutResponse) {
275
+ this.logEvent({ message: 'writing' });
276
+ characteristic.write(data, withoutResponse);
277
+ resolve(new ArrayBuffer(0));
278
+ return;
279
+ }
280
+ else {
281
+ const writeId = this.writeQueue.length;
282
+ let messageDeleted = false;
283
+ this.writeQueue.push({ uuid: characteristicUuid.toLocaleLowerCase(), data, resolve, reject });
284
+ const to = setTimeout(() => {
285
+ console.log('~~~ write timeout');
286
+ if (this.writeQueue.length > writeId && !messageDeleted)
287
+ this.writeQueue.splice(writeId, 1);
288
+ this.logEvent({ message: 'writing response', err: 'timeout' });
289
+ reject(new Error('timeout'));
290
+ }, 1000);
291
+ this.logEvent({ message: 'writing' });
292
+ characteristic.write(data, withoutResponse, (err) => {
293
+ clearTimeout(to);
294
+ this.logEvent({ message: 'writing response', err });
295
+ if (err) {
296
+ this.writeQueue.splice(writeId, 1);
297
+ messageDeleted = true;
298
+ reject(err);
299
+ }
300
+ });
281
301
  }
282
302
  });
283
- });
303
+ }
304
+ catch (err) {
305
+ this.logEvent({ message: 'error', fn: '', error: err.message || err, stack: err.stack });
306
+ }
284
307
  });
285
308
  }
286
309
  read(characteristicUuid) {
@@ -22,6 +22,7 @@ export default class BlePeripheralConnector {
22
22
  reconnect(): Promise<void>;
23
23
  onDisconnect(): void;
24
24
  initialize(enforce?: boolean): Promise<void>;
25
+ isSubscribed(characteristicUuid: string): boolean;
25
26
  subscribeAll(callback: (characteristicUuid: string, data: any) => void): Promise<string[]>;
26
27
  subscribe(characteristicUuid: string): Promise<boolean>;
27
28
  onData(characteristicUuid: string, data: any): void;
@@ -31,9 +31,7 @@ class BlePeripheralConnector {
31
31
  if (this.logger) {
32
32
  this.logger.logEvent(event);
33
33
  }
34
- if (process.env.BLE_DEBUG) {
35
- console.log('~~~BLE:', event);
36
- }
34
+ console.log('~~~BLE:', event);
37
35
  }
38
36
  connect() {
39
37
  return __awaiter(this, void 0, void 0, function* () {
@@ -86,6 +84,9 @@ class BlePeripheralConnector {
86
84
  this.state.isInitialized = this.characteristics !== undefined && this.services !== undefined;
87
85
  });
88
86
  }
87
+ isSubscribed(characteristicUuid) {
88
+ return this.state.subscribed.find(c => c === characteristicUuid || (0, ble_1.uuid)(c) === characteristicUuid || c === (0, ble_1.uuid)(characteristicUuid)) !== undefined;
89
+ }
89
90
  subscribeAll(callback) {
90
91
  return __awaiter(this, void 0, void 0, function* () {
91
92
  const cnt = this.characteristics.length;
@@ -127,21 +128,34 @@ class BlePeripheralConnector {
127
128
  });
128
129
  }
129
130
  subscribe(characteristicUuid) {
131
+ this.logEvent({ message: 'subscribe', characteristic: characteristicUuid, characteristics: this.characteristics.map(c => ({ characteristic: c.uuid, uuid: (0, ble_1.uuid)(c.uuid) })) });
130
132
  return new Promise((resolve, reject) => {
131
- const characteristic = this.characteristics.find(c => c.uuid === characteristicUuid || (0, ble_1.uuid)(c.uuid) === characteristicUuid);
132
- if (!characteristic) {
133
- reject(new Error('Characteristic not found'));
134
- return;
133
+ try {
134
+ const characteristic = this.characteristics.find(c => c.uuid === characteristicUuid || (0, ble_1.uuid)(c.uuid) === characteristicUuid);
135
+ this.logEvent({ message: 'subscribe', peripheral: this.peripheral.address, characteristic: characteristic.uuid, uuid: (0, ble_1.uuid)(characteristic.uuid) });
136
+ if (!characteristic) {
137
+ reject(new Error('Characteristic not found'));
138
+ return;
139
+ }
140
+ characteristic.on('data', (data, _isNotification) => {
141
+ this.onData(characteristicUuid, data);
142
+ });
143
+ const to = setTimeout(() => {
144
+ this.logEvent({ message: 'subscribe result', characteristic: characteristicUuid, error: 'timeout' });
145
+ reject(new Error('timeout'));
146
+ }, 3000);
147
+ characteristic.subscribe((err) => {
148
+ clearTimeout(to);
149
+ this.logEvent({ message: 'subscribe result', characteristic: characteristicUuid, error: err });
150
+ if (err)
151
+ reject(err);
152
+ else
153
+ resolve(true);
154
+ });
155
+ }
156
+ catch (err) {
157
+ this.logEvent({ message: 'error', error: err.message || err, stack: err.stack });
135
158
  }
136
- characteristic.on('data', (data, _isNotification) => {
137
- this.onData(characteristicUuid, data);
138
- });
139
- characteristic.subscribe((err) => {
140
- if (err)
141
- reject(err);
142
- else
143
- resolve(true);
144
- });
145
159
  });
146
160
  }
147
161
  onData(characteristicUuid, data) {
package/lib/ble/fm.d.ts CHANGED
@@ -45,6 +45,7 @@ export default class BleFitnessMachineDevice extends BleDevice {
45
45
  data: IndoorBikeData;
46
46
  features: IndoorBikeFeatures;
47
47
  hasControl: boolean;
48
+ isCheckingControl: boolean;
48
49
  isCPSubscribed: boolean;
49
50
  crr: number;
50
51
  cw: number;
package/lib/ble/fm.js CHANGED
@@ -86,6 +86,7 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
86
86
  super(props);
87
87
  this.features = undefined;
88
88
  this.hasControl = false;
89
+ this.isCheckingControl = false;
89
90
  this.isCPSubscribed = false;
90
91
  this.crr = 0.0033;
91
92
  this.cw = 0.6;
@@ -300,6 +301,7 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
300
301
  writeFtmsMessage(requestedOpCode, data) {
301
302
  return __awaiter(this, void 0, void 0, function* () {
302
303
  try {
304
+ this.logEvent({ message: 'fmts:write', data: data.toString('hex') });
303
305
  const res = yield this.write(FTMS_CP, data);
304
306
  const responseData = Buffer.from(res);
305
307
  const opCode = responseData.readUInt8(0);
@@ -307,19 +309,25 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
307
309
  const result = responseData.readUInt8(2);
308
310
  if (opCode !== 128 || request !== requestedOpCode)
309
311
  throw new Error('Illegal response ');
312
+ this.logEvent({ message: 'fmts:write result', res, result });
310
313
  return result;
311
314
  }
312
315
  catch (err) {
313
- this.logEvent({ message: 'writeFtmsMessage failed', opCode: requestedOpCode, reason: err.message });
316
+ this.logEvent({ message: 'fmts:write failed', opCode: requestedOpCode, reason: err.message });
314
317
  return 4;
315
318
  }
316
319
  });
317
320
  }
318
321
  requestControl() {
319
322
  return __awaiter(this, void 0, void 0, function* () {
323
+ let to = undefined;
324
+ if (this.isCheckingControl) {
325
+ to = setTimeout(() => { }, 3500);
326
+ }
320
327
  if (this.hasControl)
321
328
  return true;
322
329
  this.logEvent({ message: 'requestControl' });
330
+ this.isCheckingControl = true;
323
331
  const data = Buffer.alloc(1);
324
332
  data.writeUInt8(0, 0);
325
333
  const res = yield this.writeFtmsMessage(0, data);
@@ -329,6 +337,9 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
329
337
  else {
330
338
  this.logEvent({ message: 'requestControl failed' });
331
339
  }
340
+ this.isCheckingControl = false;
341
+ if (to)
342
+ clearTimeout(to);
332
343
  return this.hasControl;
333
344
  });
334
345
  }
@@ -340,6 +351,10 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
340
351
  if (!this.hasControl)
341
352
  return;
342
353
  const hasControl = yield this.requestControl();
354
+ if (!hasControl) {
355
+ this.logEvent({ message: 'setTargetPower failed', reason: 'control is disabled' });
356
+ return true;
357
+ }
343
358
  const data = Buffer.alloc(3);
344
359
  data.writeUInt8(5, 0);
345
360
  data.writeInt16LE(Math.round(power), 1);
@@ -350,7 +365,8 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
350
365
  setSlope(slope) {
351
366
  return __awaiter(this, void 0, void 0, function* () {
352
367
  this.logEvent({ message: 'setSlope', slope });
353
- if (!this.hasControl)
368
+ const hasControl = yield this.requestControl();
369
+ if (!hasControl)
354
370
  return;
355
371
  const { windSpeed, crr, cw } = this;
356
372
  return yield this.setIndoorBikeSimulation(windSpeed, slope, crr, cw);
@@ -582,7 +598,7 @@ class FmAdapter extends Device_1.default {
582
598
  break;
583
599
  }
584
600
  }
585
- this.device.requestControl();
601
+ yield this.device.requestControl();
586
602
  const startRequest = this.getCyclingMode().getBikeInitRequest();
587
603
  yield this.sendUpdate(startRequest);
588
604
  bleDevice.on('data', (data) => {
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const CyclingMode_1 = require("../CyclingMode");
7
7
  const calculations_1 = __importDefault(require("../calculations"));
8
8
  const power_base_1 = __importDefault(require("../modes/power-base"));
9
+ const MIN_SPEED = 10;
9
10
  const config = {
10
11
  name: "ERG",
11
12
  description: "Calculates speed based on power and slope. Power is either set by workout or calculated based on gear and cadence",
@@ -147,9 +148,15 @@ class ERGCyclingMode extends power_base_1.default {
147
148
  const m = this.getWeight();
148
149
  const t = this.getTimeSinceLastUpdate();
149
150
  const { speed, distance } = this.calculateSpeedAndDistance(power, slope, m, t, { bikeType });
150
- data.speed = speed;
151
+ if (power === 0 && speed < MIN_SPEED) {
152
+ data.speed = Math.round(prevData.speed - 1) < 0 ? 0 : Math.round(prevData.speed - 1);
153
+ data.distanceInternal = distanceInternal + data.speed / 3.6 * t;
154
+ }
155
+ else {
156
+ data.speed = speed;
157
+ data.distanceInternal = distanceInternal + distance;
158
+ }
151
159
  data.power = Math.round(power);
152
- data.distanceInternal = distanceInternal + distance;
153
160
  data.slope = slope;
154
161
  data.pedalRpm = rpm;
155
162
  data.gear = gear;
@@ -62,8 +62,8 @@ class PowerMeterCyclingMode extends power_base_1.default {
62
62
  data.distanceInternal = distanceInternal + data.speed / 3.6 * t;
63
63
  }
64
64
  else {
65
- data.speed = (power === 0 && speed < MIN_SPEED) ? 0 : speed;
66
- data.distanceInternal = (power === 0 && speed < MIN_SPEED) ? distanceInternal : distanceInternal + distance;
65
+ data.speed = speed;
66
+ data.distanceInternal = distanceInternal + distance;
67
67
  }
68
68
  if (props.log)
69
69
  this.logger.logEvent({ message: "updateData result", data, bikeData, prevSpeed: prevData.speed, stopped: speed < MIN_SPEED });
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const gd_eventlog_1 = require("gd-eventlog");
7
7
  const CyclingMode_1 = require("../CyclingMode");
8
8
  const power_base_1 = __importDefault(require("./power-base"));
9
+ const MIN_SPEED = 10;
9
10
  const config = {
10
11
  name: "Simulator",
11
12
  description: "Simulates a ride with constant speed or power output",
@@ -106,6 +107,14 @@ class SimulatorCyclingMode extends power_base_1.default {
106
107
  speed = res.speed;
107
108
  distance = res.distance;
108
109
  }
110
+ if (power === 0 && speed < MIN_SPEED) {
111
+ data.speed = Math.round(prevData.speed - 1) < 0 ? 0 : Math.round(prevData.speed - 1);
112
+ data.distanceInternal = distanceInternal + data.speed / 3.6 * t;
113
+ }
114
+ else {
115
+ data.speed = speed;
116
+ data.distanceInternal = distanceInternal + distance;
117
+ }
109
118
  data.speed = speed;
110
119
  data.power = Math.round(power);
111
120
  data.distanceInternal = distanceInternal + distance;
@@ -218,6 +218,8 @@ class Simulator extends Device_1.default {
218
218
  return;
219
219
  }
220
220
  const prevDist = this.data.distanceInternal;
221
+ const d = this.data;
222
+ const prevTime = d.deviceTime;
221
223
  this.data = this.getCyclingMode().updateData(this.data);
222
224
  let data = {
223
225
  speed: this.data.speed,
@@ -230,6 +232,9 @@ class Simulator extends Device_1.default {
230
232
  deviceTime: (Date.now() - this.startTS) / 1000,
231
233
  deviceDistanceCounter: this.data.distanceInternal
232
234
  };
235
+ if (this.isBot) {
236
+ this.logger.logEvent(Object.assign({ message: 'Coach update', prevDist, prevTime }, data));
237
+ }
233
238
  this.paused = (this.data.speed === 0);
234
239
  if (this.ignoreHrm)
235
240
  delete data.heartrate;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "incyclist-devices",
3
- "version": "1.4.63",
3
+ "version": "1.4.64",
4
4
  "dependencies": {
5
5
  "@serialport/parser-byte-length": "^9.0.1",
6
6
  "@serialport/parser-delimiter": "^9.0.1",