incyclist-devices 2.3.0-beta.6 → 2.3.0-beta.8

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.
@@ -37,6 +37,7 @@ export default class IncyclistDevice<P extends DeviceProperties> extends EventEm
37
37
  resume(): Promise<boolean>;
38
38
  connect(): Promise<boolean>;
39
39
  close(): Promise<boolean>;
40
+ resetData(): void;
40
41
  getControllerInfo(): ControllerConfig | undefined;
41
42
  isControllable(): boolean;
42
43
  getCapabilities(): IncyclistCapability[];
@@ -100,6 +100,9 @@ class IncyclistDevice extends events_1.default {
100
100
  }
101
101
  connect() { throw new Error('not implemented'); }
102
102
  close() { throw new Error('not implemented'); }
103
+ resetData() {
104
+ delete this.lastUpdate;
105
+ }
103
106
  getControllerInfo() {
104
107
  const a = this.constructor;
105
108
  const config = a.controllers;
@@ -1,5 +1,5 @@
1
1
  import IncyclistDevice from "../../base/adpater";
2
- import { BleDeviceProperties, BleDeviceSettings, BleStartProperties, IBlePeripheral } from "../types";
2
+ import { BleDeviceProperties, BleDeviceSettings, BleStartProperties, IBleInterface, IBlePeripheral } from "../types";
3
3
  import { IAdapter, IncyclistBikeData, IncyclistAdapterData, DeviceProperties } from "../../types";
4
4
  import { BleDeviceData } from "./types";
5
5
  import { LegacyProfile } from "../../antv2/types";
@@ -51,6 +51,7 @@ export default class BleAdapter<TDeviceData extends BleDeviceData, TDevice exten
51
51
  stop(): Promise<boolean>;
52
52
  pause(): Promise<boolean>;
53
53
  resume(): Promise<boolean>;
54
+ protected getBle(): IBleInterface<any>;
54
55
  update(): void;
55
56
  setCyclingMode(mode: string | ICyclingMode, settings?: any, sendInitCommands?: boolean): void;
56
57
  }
@@ -38,20 +38,20 @@ class BleAdapter extends adpater_1.default {
38
38
  }
39
39
  connect() {
40
40
  return __awaiter(this, void 0, void 0, function* () {
41
- const iface = interface_factory_1.BleMultiTransportInterfaceFactory.createInstance(this.getInterface());
42
- return yield iface.connect();
41
+ const ble = this.getBle();
42
+ return yield ble.connect();
43
43
  });
44
44
  }
45
45
  getPeripheral() {
46
- const iface = interface_factory_1.BleMultiTransportInterfaceFactory.createInstance(this.getInterface());
47
- const p = iface === null || iface === void 0 ? void 0 : iface.createPeripheralFromSettings(this.settings);
46
+ const ble = this.getBle();
47
+ const p = ble === null || ble === void 0 ? void 0 : ble.createPeripheralFromSettings(this.settings);
48
48
  return p;
49
49
  }
50
50
  waitForPeripheral() {
51
51
  return __awaiter(this, void 0, void 0, function* () {
52
52
  this.logEvent({ message: 'waiting for sensor ...', device: this.getName(), interface: this.getInterface() });
53
- const iface = interface_factory_1.BleMultiTransportInterfaceFactory.createInstance(this.getInterface());
54
- const peripheral = yield iface.waitForPeripheral(this.settings);
53
+ const ble = this.getBle();
54
+ const peripheral = yield ble.waitForPeripheral(this.settings);
55
55
  this.updateSensor(peripheral);
56
56
  });
57
57
  }
@@ -87,6 +87,7 @@ class BleAdapter extends adpater_1.default {
87
87
  return (_a = this.device) === null || _a === void 0 ? void 0 : _a.isConnected();
88
88
  }
89
89
  resetData() {
90
+ super.resetData();
90
91
  this.dataMsgCount = 0;
91
92
  this.deviceData = {};
92
93
  this.data = {};
@@ -183,10 +184,7 @@ class BleAdapter extends adpater_1.default {
183
184
  this.stopped = false;
184
185
  if (wasPaused)
185
186
  this.resume();
186
- if (this.started && !wasPaused && !wasStopped) {
187
- return 'done';
188
- }
189
- if (this.started && wasPaused) {
187
+ if (this.started && !wasStopped) {
190
188
  return 'done';
191
189
  }
192
190
  const connected = yield this.connect();
@@ -256,11 +254,12 @@ class BleAdapter extends adpater_1.default {
256
254
  startAdapter(startProps) {
257
255
  return __awaiter(this, void 0, void 0, function* () {
258
256
  const props = this.getStartProps(startProps);
259
- const { timeout = this.getDefaultStartupTimeout() } = startProps;
257
+ const { timeout = this.getDefaultStartupTimeout() } = startProps !== null && startProps !== void 0 ? startProps : {};
260
258
  const wasPaused = this.paused;
261
259
  const preCheckResult = yield this.startPreChecks(props);
262
260
  if (preCheckResult === 'done') {
263
261
  yield (0, utils_1.resolveNextTick)();
262
+ this.logEvent({ message: `start result: ${this.started ? 'success' : 'failed'}`, preCheckResult, device: this.getName(), interface: this.getInterface(), protocol: this.getProtocolName() });
264
263
  return this.started;
265
264
  }
266
265
  if (preCheckResult === 'connection-failed') {
@@ -270,27 +269,37 @@ class BleAdapter extends adpater_1.default {
270
269
  }
271
270
  this.logEvent({ message: 'starting device', device: this.getName(), interface: this.getInterface(), props, isStarted: this.started });
272
271
  try {
272
+ this.resetData();
273
+ this.stopped = false;
273
274
  const connected = yield this.startSensor();
274
275
  if (connected) {
275
276
  this.logEvent({ message: 'peripheral connected', device: this.getName(), interface: this.getInterface(), props });
276
277
  }
277
278
  else {
278
279
  this.logEvent({ message: 'peripheral connection failed', device: this.getName(), interface: this.getInterface(), reason: 'unknown', props });
280
+ this.stopped = true;
279
281
  return false;
280
282
  }
281
283
  yield this.waitForInitialData(timeout);
282
284
  this.checkCapabilities();
283
285
  if (this.hasCapability(types_1.IncyclistCapability.Control))
284
286
  yield this.initControl(startProps);
285
- this.resetData();
286
287
  this.stopped = false;
287
288
  this.started = true;
288
289
  if (wasPaused)
289
290
  this.resume();
291
+ if (!this.isStarting()) {
292
+ this.started = false;
293
+ this.stopped = true;
294
+ return false;
295
+ }
296
+ this.logEvent({ message: 'start result: success', device: this.getName(), interface: this.getInterface(), protocol: this.getProtocolName() });
290
297
  return true;
291
298
  }
292
299
  catch (err) {
293
300
  this.logEvent({ message: 'start result: error', error: err.message, device: this.getName(), interface: this.getInterface(), protocol: this.getProtocolName() });
301
+ this.started = false;
302
+ this.stopped = true;
294
303
  return false;
295
304
  }
296
305
  });
@@ -319,6 +328,8 @@ class BleAdapter extends adpater_1.default {
319
328
  if (this.isStarting()) {
320
329
  yield this.startTask.stop();
321
330
  }
331
+ this.started = false;
332
+ this.resetData();
322
333
  let reason = 'unknown';
323
334
  let stopped = false;
324
335
  if (!this.getComms()) {
@@ -338,8 +349,9 @@ class BleAdapter extends adpater_1.default {
338
349
  else {
339
350
  this.logEvent({ message: 'stopping device failed', device: this.getName(), interface: this.getInterface(), reason });
340
351
  }
341
- this.started = false;
352
+ this.resetData();
342
353
  this.stopped = true;
354
+ this.started = false;
343
355
  return stopped;
344
356
  });
345
357
  }
@@ -349,8 +361,8 @@ class BleAdapter extends adpater_1.default {
349
361
  });
350
362
  return __awaiter(this, void 0, void 0, function* () {
351
363
  const res = yield _super.pause.call(this);
352
- const iface = interface_factory_1.BleMultiTransportInterfaceFactory.createInstance(this.getInterface());
353
- iface.pauseLogging();
364
+ const ble = this.getBle();
365
+ ble.pauseLogging();
354
366
  return res;
355
367
  });
356
368
  }
@@ -359,12 +371,15 @@ class BleAdapter extends adpater_1.default {
359
371
  resume: { get: () => super.resume }
360
372
  });
361
373
  return __awaiter(this, void 0, void 0, function* () {
362
- const iface = interface_factory_1.BleMultiTransportInterfaceFactory.createInstance(this.getInterface());
363
- iface.resumeLogging();
374
+ const ble = this.getBle();
375
+ ble.resumeLogging();
364
376
  const res = yield _super.resume.call(this);
365
377
  return res;
366
378
  });
367
379
  }
380
+ getBle() {
381
+ return interface_factory_1.BleMultiTransportInterfaceFactory.createInstance(this.getInterface());
382
+ }
368
383
  update() {
369
384
  }
370
385
  setCyclingMode(mode, settings, sendInitCommands) {
@@ -7,8 +7,9 @@ import { IAdapter, IncyclistAdapterData, IncyclistBikeData } from '../../types';
7
7
  import { LegacyProfile } from '../../antv2/types';
8
8
  export default class BleFmAdapter extends BleAdapter<IndoorBikeData, BleFitnessMachineDevice> {
9
9
  protected static INCYCLIST_PROFILE_NAME: LegacyProfile;
10
- distanceInternal: number;
11
- connectPromise: Promise<boolean>;
10
+ protected distanceInternal: number;
11
+ protected connectPromise: Promise<boolean>;
12
+ protected requestControlRetryDelay: number;
12
13
  constructor(settings: BleDeviceSettings, props?: BleDeviceProperties);
13
14
  updateSensor(peripheral: IBlePeripheral): void;
14
15
  isSame(device: IAdapter): boolean;
@@ -20,11 +20,12 @@ const sensor_1 = __importDefault(require("./sensor"));
20
20
  const adapter_1 = __importDefault(require("../base/adapter"));
21
21
  const consts_1 = require("./consts");
22
22
  const types_1 = require("../../types");
23
- const task_1 = require("../../utils/task");
23
+ const utils_1 = require("../../utils/utils");
24
24
  class BleFmAdapter extends adapter_1.default {
25
25
  constructor(settings, props) {
26
26
  super(settings, props);
27
27
  this.distanceInternal = 0;
28
+ this.requestControlRetryDelay = 1000;
28
29
  this.logger = new gd_eventlog_1.EventLogger('BLE-FM');
29
30
  this.device = new sensor_1.default(this.getPeripheral(), { logger: this.logger });
30
31
  this.capabilities = [
@@ -117,6 +118,8 @@ class BleFmAdapter extends adapter_1.default {
117
118
  }
118
119
  initControl(_startProps) {
119
120
  return __awaiter(this, void 0, void 0, function* () {
121
+ if (!this.isStarting())
122
+ return;
120
123
  this.setConstants();
121
124
  yield this.establishControl();
122
125
  this.setConstants();
@@ -146,38 +149,30 @@ class BleFmAdapter extends adapter_1.default {
146
149
  return __awaiter(this, void 0, void 0, function* () {
147
150
  if (!this.isStarting())
148
151
  return false;
149
- let waitTask;
150
- let iv;
152
+ let hasControl = false;
153
+ let tryCnt = 0;
151
154
  const sensor = this.getComms();
152
- const wait = () => {
153
- const res = new Promise((resolve) => {
154
- iv = setInterval(() => __awaiter(this, void 0, void 0, function* () {
155
- if (!this.isStarting() || !(waitTask === null || waitTask === void 0 ? void 0 : waitTask.isRunning)) {
156
- resolve(false);
157
- clearInterval(iv);
158
- return;
155
+ return new Promise((resolve) => {
156
+ this.startTask.notifyOnStop(() => {
157
+ resolve(false);
158
+ });
159
+ const waitUntilControl = () => __awaiter(this, void 0, void 0, function* () {
160
+ while (!hasControl && this.isStarting()) {
161
+ if (tryCnt++ === 0) {
162
+ this.logEvent({ message: 'requesting control', device: this.getName(), interface: this.getInterface() });
159
163
  }
160
- const hasControl = yield sensor.requestControl();
164
+ hasControl = yield sensor.requestControl();
161
165
  if (hasControl) {
162
- clearInterval(iv);
163
- resolve(true);
166
+ this.logEvent({ message: 'control granted', device: this.getName(), interface: this.getInterface() });
167
+ resolve(this.isStarting());
164
168
  }
165
- else if (!this.isStarting() || !(waitTask === null || waitTask === void 0 ? void 0 : waitTask.isRunning)) {
166
- resolve(false);
167
- clearInterval(iv);
169
+ else {
170
+ yield (0, utils_1.sleep)(this.requestControlRetryDelay);
168
171
  }
169
- }), 1000);
172
+ }
170
173
  });
171
- return res;
172
- };
173
- waitTask = new task_1.InteruptableTask(wait(), {
174
- errorOnTimeout: false,
175
- timeout: 10000
174
+ waitUntilControl();
176
175
  });
177
- const hasControl = yield waitTask.run();
178
- clearInterval(iv);
179
- if (!hasControl && this.isStarting())
180
- throw new Error('could not establish control');
181
176
  });
182
177
  }
183
178
  sendInitialRequest() {
@@ -212,7 +207,7 @@ class BleFmAdapter extends adapter_1.default {
212
207
  try {
213
208
  const update = this.getCyclingMode().sendBikeUpdate(request);
214
209
  this.logEvent({ message: 'send bike update requested', profile: this.getProfile(), update, request });
215
- const device = this.device;
210
+ const device = this.getComms();
216
211
  if (update.slope !== undefined) {
217
212
  yield device.setSlope(update.slope);
218
213
  }
@@ -27,7 +27,7 @@ export declare class DirectConnectPeripheral implements IBlePeripheral {
27
27
  onDisconnect(callback: () => void): void;
28
28
  discoverServices(): Promise<string[]>;
29
29
  discoverCharacteristics(serviceUUID: string): Promise<BleCharacteristic[]>;
30
- subscribe(characteristicUUID: string, callback: (characteristicUuid: string, data: Buffer) => void): Promise<boolean>;
30
+ subscribe(characteristicUUID: string, callback?: (characteristicUuid: string, data: Buffer) => void): Promise<boolean>;
31
31
  unsubscribe(characteristicUUID: string): Promise<boolean>;
32
32
  subscribeAll(callback: (characteristicUuid: string, data: Buffer) => void): Promise<boolean>;
33
33
  subscribeSelected(characteristics: string[], callback: (characteristicUuid: string, data: Buffer) => void): Promise<boolean>;
@@ -133,6 +133,10 @@ class DirectConnectPeripheral {
133
133
  subscribe(characteristicUUID, callback) {
134
134
  return __awaiter(this, void 0, void 0, function* () {
135
135
  var _a, _b;
136
+ const uuid = (0, utils_1.parseUUID)(characteristicUUID);
137
+ if (this.subscribed.includes(uuid)) {
138
+ return true;
139
+ }
136
140
  const seqNo = this.getNextSeqNo();
137
141
  const message = new messages_1.EnableCharacteristicNotificationsMessage();
138
142
  const request = message.createRequest(seqNo, { characteristicUUID: (0, utils_1.parseUUID)(characteristicUUID), enable: true });
@@ -149,10 +153,12 @@ class DirectConnectPeripheral {
149
153
  this.logEvent({ message: 'EnableCharacteristicNotifications response', path: this.getPath(), rc, characteristic: (0, utils_1.beautifyUUID)(res.body.characteristicUUID), raw: response.toString('hex') });
150
154
  const confirmed = res.body.characteristicUUID;
151
155
  if ((0, utils_1.parseUUID)(confirmed) === (0, utils_1.parseUUID)(characteristicUUID)) {
152
- this.subscribed.push(characteristicUUID);
153
- this.eventEmitter.on((0, utils_1.parseUUID)(characteristicUUID), (data) => {
154
- callback(characteristicUUID, data);
155
- });
156
+ this.subscribed.push((0, utils_1.parseUUID)(characteristicUUID));
157
+ if (callback) {
158
+ this.eventEmitter.on((0, utils_1.parseUUID)(characteristicUUID), (data) => {
159
+ callback(characteristicUUID, data);
160
+ });
161
+ }
156
162
  return true;
157
163
  }
158
164
  return false;
@@ -183,7 +189,7 @@ class DirectConnectPeripheral {
183
189
  this.logEvent({ message: 'EnableCharacteristicNotifications response', path: this.getPath(), rc, characteristic: (0, utils_1.beautifyUUID)(res.body.characteristicUUID), raw: response.toString('hex') });
184
190
  const confirmed = res.body.characteristicUUID;
185
191
  if ((0, utils_1.parseUUID)(confirmed) === (0, utils_1.parseUUID)(characteristicUUID)) {
186
- this.subscribed.splice(this.subscribed.indexOf(characteristicUUID), 1);
192
+ this.subscribed.splice(this.subscribed.indexOf((0, utils_1.parseUUID)(characteristicUUID)), 1);
187
193
  this.eventEmitter.removeAllListeners((0, utils_1.parseUUID)(characteristicUUID));
188
194
  return true;
189
195
  }
@@ -282,7 +288,7 @@ class DirectConnectPeripheral {
282
288
  return __awaiter(this, void 0, void 0, function* () {
283
289
  const promises = [];
284
290
  this.subscribed.forEach(characteristicUUID => {
285
- promises.push(this.unsubscribe(characteristicUUID));
291
+ promises.push(this.unsubscribe((0, utils_1.parseUUID)(characteristicUUID)));
286
292
  });
287
293
  yield Promise.allSettled(promises);
288
294
  return true;
@@ -318,17 +324,18 @@ class DirectConnectPeripheral {
318
324
  write(characteristicUUID, data, options) {
319
325
  return __awaiter(this, void 0, void 0, function* () {
320
326
  return new Promise(resolve => {
327
+ const seqNo = this.getNextSeqNo();
328
+ const message = new messages_1.WriteCharacteristicMessage();
329
+ const request = message.createRequest(seqNo, { characteristicUUID: (0, utils_1.parseUUID)(characteristicUUID), characteristicData: data });
330
+ let response;
331
+ let characteristic = characteristicUUID;
321
332
  if (!(options === null || options === void 0 ? void 0 : options.withoutResponse)) {
333
+ this.subscribe(characteristicUUID);
322
334
  const uuid = (0, utils_1.parseUUID)(characteristicUUID);
323
335
  this.eventEmitter.once(uuid, (data) => {
324
336
  resolve(data);
325
337
  });
326
338
  }
327
- const seqNo = this.getNextSeqNo();
328
- const message = new messages_1.WriteCharacteristicMessage();
329
- const request = message.createRequest(seqNo, { characteristicUUID: (0, utils_1.parseUUID)(characteristicUUID), characteristicData: data });
330
- let response;
331
- let characteristic = characteristicUUID;
332
339
  this.logEvent({ message: 'WriteCharacteristic request', path: this.getPath(), characteristic: (0, utils_1.beautifyUUID)(characteristicUUID),
333
340
  data: data.toString('hex'),
334
341
  raw: request.toString('hex') });
@@ -46,5 +46,6 @@ export interface IAdapter extends EventEmitter, IBike, ISensor {
46
46
  resume(): Promise<boolean>;
47
47
  connect(): Promise<boolean>;
48
48
  close(): Promise<boolean>;
49
+ resetData(): void;
49
50
  onData(callback: OnDeviceDataCallback): any;
50
51
  }
@@ -30,15 +30,18 @@ export declare class InteruptableTask<T extends TaskState, P> {
30
30
  protected props?: TaskProps<T, P>;
31
31
  protected internalEvents: EventEmitter<[never]>;
32
32
  protected promise?: Promise<P>;
33
+ protected onStopNotifiers: Array<() => void>;
33
34
  constructor(promise: Promise<any>, props?: TaskProps<T, P>);
34
35
  getPromise(): Promise<P>;
35
36
  getState(): T;
36
37
  run(): Promise<P>;
38
+ notifyOnStop(cb: () => void): void;
37
39
  start(): void;
38
40
  stop(): Promise<boolean>;
39
41
  isRunning(): boolean;
40
42
  protected clearTimeout(): void;
41
43
  protected onTimeout(): void;
44
+ protected sendStopNotification(): void;
42
45
  protected logEvent(event: any): void;
43
46
  }
44
47
  export {};
package/lib/utils/task.js CHANGED
@@ -20,6 +20,7 @@ class InteruptableTask {
20
20
  var _a;
21
21
  this.internalState = { isRunning: false };
22
22
  this.internalEvents = new events_1.default();
23
+ this.onStopNotifiers = [];
23
24
  this.state = ((_a = props === null || props === void 0 ? void 0 : props.state) !== null && _a !== void 0 ? _a : {});
24
25
  this.props = props;
25
26
  delete this.props.state;
@@ -39,6 +40,9 @@ class InteruptableTask {
39
40
  return this.internalState.promise;
40
41
  });
41
42
  }
43
+ notifyOnStop(cb) {
44
+ this.onStopNotifiers.push(cb);
45
+ }
42
46
  start() {
43
47
  this.internalState.promise = new Promise((resolve, reject) => {
44
48
  var _a;
@@ -59,6 +63,7 @@ class InteruptableTask {
59
63
  isRunning: false,
60
64
  };
61
65
  this.internalEvents.removeAllListeners();
66
+ this.sendStopNotification();
62
67
  if (this.getState().result === 'completed' || this.getState().result === 'error')
63
68
  return;
64
69
  if (this.props.onDone)
@@ -119,6 +124,12 @@ class InteruptableTask {
119
124
  else
120
125
  resolve(null);
121
126
  }
127
+ sendStopNotification() {
128
+ this.onStopNotifiers.forEach((cb) => {
129
+ if (typeof cb === 'function')
130
+ cb();
131
+ });
132
+ }
122
133
  logEvent(event) {
123
134
  if (this.props.log)
124
135
  this.props.log(event);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "incyclist-devices",
3
- "version": "2.3.0-beta.6",
3
+ "version": "2.3.0-beta.8",
4
4
  "dependencies": {
5
5
  "@serialport/bindings-interface": "^1.2.2",
6
6
  "@serialport/parser-byte-length": "^9.0.1",