incyclist-devices 2.3.13 → 2.3.14
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.
- package/lib/ble/adapter-factory.d.ts +34 -0
- package/lib/ble/adapter-factory.js +110 -0
- package/lib/ble/base/comms-utils.d.ts +7 -0
- package/lib/ble/base/comms-utils.js +90 -0
- package/lib/ble/base/comms.d.ts +75 -0
- package/lib/ble/base/comms.js +599 -0
- package/lib/ble/ble-interface.d.ts +84 -0
- package/lib/ble/ble-interface.js +622 -0
- package/lib/ble/ble-peripheral.d.ts +39 -0
- package/lib/ble/ble-peripheral.js +252 -0
- package/lib/ble/cp/comm.d.ts +30 -0
- package/lib/ble/cp/comm.js +126 -0
- package/lib/ble/elite/adapter.d.ts +21 -0
- package/lib/ble/elite/adapter.js +118 -0
- package/lib/ble/elite/comms.d.ts +31 -0
- package/lib/ble/elite/comms.js +127 -0
- package/lib/ble/elite/index.d.ts +3 -0
- package/lib/ble/elite/index.js +10 -0
- package/lib/ble/fm/comms.d.ts +49 -0
- package/lib/ble/fm/comms.js +506 -0
- package/lib/ble/hr/comm.d.ts +19 -0
- package/lib/ble/hr/comm.js +65 -0
- package/lib/ble/peripheral-cache.d.ts +45 -0
- package/lib/ble/peripheral-cache.js +109 -0
- package/lib/ble/tacx/comms.d.ts +59 -0
- package/lib/ble/tacx/comms.js +634 -0
- package/lib/ble/wahoo/comms.d.ts +64 -0
- package/lib/ble/wahoo/comms.js +399 -0
- package/lib/direct-connect/base/comms.d.ts +3 -0
- package/lib/direct-connect/base/comms.js +7 -0
- package/lib/direct-connect/base/sensor.d.ts +3 -0
- package/lib/direct-connect/base/sensor.js +7 -0
- package/lib/direct-connect/utils.d.ts +5 -0
- package/lib/direct-connect/utils.js +73 -0
- package/lib/factories/index.d.ts +3 -0
- package/lib/factories/index.js +10 -0
- package/lib/modes/power-base.js +8 -5
- package/lib/utils/operation.d.ts +17 -0
- package/lib/utils/operation.js +20 -0
- package/package.json +1 -1
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.BleComms = void 0;
|
|
16
|
+
const events_1 = __importDefault(require("events"));
|
|
17
|
+
const gd_eventlog_1 = require("gd-eventlog");
|
|
18
|
+
const utils_1 = require("../../utils/utils");
|
|
19
|
+
const ble_interface_1 = __importDefault(require("../ble-interface"));
|
|
20
|
+
const utils_2 = require("../utils");
|
|
21
|
+
const CONNECT_WAIT_TIMEOUT = 10000;
|
|
22
|
+
const BLE_TIMEOUT = 1000;
|
|
23
|
+
class BleComms extends events_1.default {
|
|
24
|
+
constructor(props) {
|
|
25
|
+
super();
|
|
26
|
+
this.characteristics = [];
|
|
27
|
+
this.deviceInfo = {};
|
|
28
|
+
this.connectState = { isConnecting: false, isConnected: false, isDisconnecting: false };
|
|
29
|
+
this.id = props.id;
|
|
30
|
+
this.address = props.address;
|
|
31
|
+
this.name = props.name;
|
|
32
|
+
this.services = props.services;
|
|
33
|
+
this.ble = ble_interface_1.default.getInstance();
|
|
34
|
+
this.characteristics = [];
|
|
35
|
+
this.subscribedCharacteristics = [];
|
|
36
|
+
this.isInitialized = false;
|
|
37
|
+
this.writeQueue = [];
|
|
38
|
+
this.workerIv = null;
|
|
39
|
+
this.prevMessages = [];
|
|
40
|
+
this.paused = false;
|
|
41
|
+
if (props.peripheral) {
|
|
42
|
+
const { id, address, advertisement, state } = props.peripheral;
|
|
43
|
+
this.peripheral = props.peripheral;
|
|
44
|
+
this.id = id;
|
|
45
|
+
this.address = address;
|
|
46
|
+
this.name = advertisement.localName;
|
|
47
|
+
this.services = advertisement.serviceUuids;
|
|
48
|
+
this.state = state;
|
|
49
|
+
}
|
|
50
|
+
if (props.logger) {
|
|
51
|
+
this.logger = props.logger;
|
|
52
|
+
}
|
|
53
|
+
else if (props.log !== false) {
|
|
54
|
+
this.logger = new gd_eventlog_1.EventLogger('BleDevice');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
getConnectState() {
|
|
58
|
+
return this.connectState;
|
|
59
|
+
}
|
|
60
|
+
isConnected() {
|
|
61
|
+
return this.connectState.isConnected;
|
|
62
|
+
}
|
|
63
|
+
pause() {
|
|
64
|
+
this.ble.pauseLogging();
|
|
65
|
+
this.paused = true;
|
|
66
|
+
}
|
|
67
|
+
resume() {
|
|
68
|
+
this.paused = false;
|
|
69
|
+
this.ble.resumeLogging();
|
|
70
|
+
}
|
|
71
|
+
getServiceUUids() {
|
|
72
|
+
throw new Error("Method not implemented.");
|
|
73
|
+
}
|
|
74
|
+
getProfile() {
|
|
75
|
+
throw new Error("Method not implemented.");
|
|
76
|
+
}
|
|
77
|
+
getProtocol() {
|
|
78
|
+
throw new Error("Method not implemented.");
|
|
79
|
+
}
|
|
80
|
+
getSettings() {
|
|
81
|
+
const { id, address, name } = this;
|
|
82
|
+
return { id, name, address, interface: 'ble', protocol: this.getProtocol() };
|
|
83
|
+
}
|
|
84
|
+
getServices() {
|
|
85
|
+
return this.services;
|
|
86
|
+
}
|
|
87
|
+
logEvent(event) {
|
|
88
|
+
if (this.paused)
|
|
89
|
+
return;
|
|
90
|
+
if (this.logger) {
|
|
91
|
+
this.logger.logEvent(event);
|
|
92
|
+
}
|
|
93
|
+
const w = global.window;
|
|
94
|
+
if ((w === null || w === void 0 ? void 0 : w.DEVICE_DEBUG) || process.env.BLE_DEBUG) {
|
|
95
|
+
console.log('~~~ BLE', event);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
setLogger(logger) {
|
|
99
|
+
this.logger = logger;
|
|
100
|
+
}
|
|
101
|
+
setInterface(ble) {
|
|
102
|
+
this.ble = ble;
|
|
103
|
+
}
|
|
104
|
+
static isMatching(characteristics) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
reset() {
|
|
108
|
+
if (this.connectState.isConnecting)
|
|
109
|
+
this.ble.stopConnectSensor();
|
|
110
|
+
}
|
|
111
|
+
cleanupListeners() {
|
|
112
|
+
if (this.characteristics === undefined) {
|
|
113
|
+
this.characteristics = [];
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
if (this.peripheral) {
|
|
117
|
+
const connector = this.ble.peripheralCache.getConnector(this.peripheral);
|
|
118
|
+
this.characteristics.forEach(c => {
|
|
119
|
+
connector.removeAllListeners((0, utils_2.uuid)(c.uuid));
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
this.characteristics = [];
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
onDisconnect() {
|
|
128
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
129
|
+
this.logEvent({ message: 'device disconnected', address: this.address, profile: this.getProfile() });
|
|
130
|
+
this.state = "disconnected";
|
|
131
|
+
const wasConnecting = this.connectState.isConnecting;
|
|
132
|
+
this.connectState.isConnecting = false;
|
|
133
|
+
this.connectState.isConnected = false;
|
|
134
|
+
if (!this.connectState.isDisconnecting) {
|
|
135
|
+
this.peripheral.state = 'disconnected';
|
|
136
|
+
this.cleanupListeners();
|
|
137
|
+
this.subscribedCharacteristics = [];
|
|
138
|
+
this.ble.onDisconnect(this.peripheral);
|
|
139
|
+
if (wasConnecting) {
|
|
140
|
+
this.emit('connection-failed');
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
this.connect({ reconnect: true });
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
waitForConnectFinished(timeout) {
|
|
149
|
+
const waitStart = Date.now();
|
|
150
|
+
const waitTimeout = waitStart + timeout;
|
|
151
|
+
return new Promise((resolve, reject) => {
|
|
152
|
+
const waitIv = setInterval(() => {
|
|
153
|
+
try {
|
|
154
|
+
if (this.connectState.isConnecting && Date.now() > waitTimeout) {
|
|
155
|
+
clearInterval(waitIv);
|
|
156
|
+
return reject(new Error('connection already in progress'));
|
|
157
|
+
}
|
|
158
|
+
if (!this.connectState.isConnecting) {
|
|
159
|
+
clearInterval(waitIv);
|
|
160
|
+
return resolve(true);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
this.logEvent({ message: 'error', fn: '', error: err.message, stack: err.stack });
|
|
165
|
+
}
|
|
166
|
+
}, 100);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
hasService(serviceUuid) {
|
|
170
|
+
return this.services && this.services.find(s => s === serviceUuid || (0, utils_2.uuid)(serviceUuid)) !== undefined;
|
|
171
|
+
}
|
|
172
|
+
init() {
|
|
173
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
174
|
+
return yield this.initDevice();
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
initDevice() {
|
|
178
|
+
this.logEvent({ message: 'get device info' });
|
|
179
|
+
return this.getDeviceInfo().then((info) => {
|
|
180
|
+
this.emit('deviceInfo', info);
|
|
181
|
+
this.logEvent(Object.assign({ message: 'device init done' }, info));
|
|
182
|
+
this.isInitialized = true;
|
|
183
|
+
return true;
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
connectPeripheral(peripheral) {
|
|
187
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
188
|
+
this.connectState.isConnecting = true;
|
|
189
|
+
let disconnectSignalled = false;
|
|
190
|
+
return new Promise((done) => __awaiter(this, void 0, void 0, function* () {
|
|
191
|
+
try {
|
|
192
|
+
const connector = this.ble.peripheralCache.getConnector(peripheral);
|
|
193
|
+
const disconnectHandler = () => {
|
|
194
|
+
this.logEvent({ message: 'device disconnected', address: this.address, profile: this.getProfile() });
|
|
195
|
+
this.state = "disconnected";
|
|
196
|
+
disconnectSignalled = true;
|
|
197
|
+
this.cleanupListeners();
|
|
198
|
+
this.subscribedCharacteristics = [];
|
|
199
|
+
this.connectState.isConnecting = false;
|
|
200
|
+
this.connectState.isConnected = false;
|
|
201
|
+
this.ble.onDisconnect(this.peripheral);
|
|
202
|
+
done(false);
|
|
203
|
+
};
|
|
204
|
+
connector.removeAllListeners('disconnect');
|
|
205
|
+
connector.once('disconnect', disconnectHandler);
|
|
206
|
+
yield connector.connect();
|
|
207
|
+
if (disconnectSignalled)
|
|
208
|
+
return;
|
|
209
|
+
const initialized = yield connector.initialize();
|
|
210
|
+
if (!initialized || disconnectSignalled)
|
|
211
|
+
return done(false);
|
|
212
|
+
yield this.subscribeAll(connector);
|
|
213
|
+
if (disconnectSignalled)
|
|
214
|
+
return done(false);
|
|
215
|
+
this.connectState.isConnected = true;
|
|
216
|
+
this.state = "connected";
|
|
217
|
+
this.emit('connected');
|
|
218
|
+
connector.removeAllListeners('disconnect');
|
|
219
|
+
connector.on('disconnect', this.onDisconnect.bind(this));
|
|
220
|
+
const success = yield this.init();
|
|
221
|
+
done(success);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
catch (err) {
|
|
225
|
+
this.logEvent({ message: 'Error', fn: 'connectPeripheral()', error: err.message, stack: err.stack });
|
|
226
|
+
}
|
|
227
|
+
this.connectState.isConnecting = false;
|
|
228
|
+
if (disconnectSignalled)
|
|
229
|
+
return;
|
|
230
|
+
done(true);
|
|
231
|
+
}));
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
subscribeMultiple(characteristics, conn) {
|
|
235
|
+
this.logEvent({ message: 'subscribe', characteristics });
|
|
236
|
+
return new Promise(resolve => {
|
|
237
|
+
try {
|
|
238
|
+
const connector = conn || this.ble.peripheralCache.getConnector(this.peripheral);
|
|
239
|
+
const subscribeSingle = (c) => {
|
|
240
|
+
connector.removeAllListeners(c);
|
|
241
|
+
connector.on(c, (uuid, data) => {
|
|
242
|
+
this.onData(uuid, data);
|
|
243
|
+
});
|
|
244
|
+
return connector.subscribe(c).then(res => {
|
|
245
|
+
this.subscribedCharacteristics.push(c);
|
|
246
|
+
return res;
|
|
247
|
+
}).catch(err => {
|
|
248
|
+
this.logEvent({ message: 'subscription failed', characteristic: c, error: err.message });
|
|
249
|
+
});
|
|
250
|
+
};
|
|
251
|
+
const promises = characteristics
|
|
252
|
+
.filter(c => {
|
|
253
|
+
if (this.characteristics) {
|
|
254
|
+
const existing = this.characteristics.find(rc => rc.uuid === c || (0, utils_2.uuid)(rc.uuid) === c);
|
|
255
|
+
if (!existing)
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
const isAlreadySubscribed = connector.isSubscribed(c);
|
|
259
|
+
return !isAlreadySubscribed;
|
|
260
|
+
})
|
|
261
|
+
.map(c => subscribeSingle(c));
|
|
262
|
+
Promise.all(promises).then(() => resolve());
|
|
263
|
+
}
|
|
264
|
+
catch (err) {
|
|
265
|
+
this.logEvent({ message: 'Error', fn: 'subscribeMultiple()', error: err.message, stack: err.stack });
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
subscribeAll(conn) {
|
|
270
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
271
|
+
try {
|
|
272
|
+
const connector = conn || this.ble.peripheralCache.getConnector(this.peripheral);
|
|
273
|
+
const subscribed = yield connector.subscribeAll((uuid, data) => { this.onData(uuid, data); });
|
|
274
|
+
subscribed.forEach(c => this.subscribedCharacteristics.push(c));
|
|
275
|
+
}
|
|
276
|
+
catch (err) {
|
|
277
|
+
this.logEvent({ message: 'Error', fn: 'subscribeAll()', error: err.message, stack: err.stack });
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
unsubscribeAll(conn) {
|
|
282
|
+
const connector = conn || this.ble.peripheralCache.getConnector(this.peripheral);
|
|
283
|
+
connector.unsubscribeAll();
|
|
284
|
+
}
|
|
285
|
+
connect(props) {
|
|
286
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
287
|
+
this.ble.resumeLogging();
|
|
288
|
+
if (!this.ble.isConnected()) {
|
|
289
|
+
try {
|
|
290
|
+
yield this.ble.connect();
|
|
291
|
+
if (!this.ble.isConnected())
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
catch (err) {
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
yield (0, utils_1.sleep)(Math.random() * 100);
|
|
299
|
+
yield this.ble.waitForSensorConnectionFinish();
|
|
300
|
+
this.ble.startConnectSensor();
|
|
301
|
+
const { reconnect } = props || {};
|
|
302
|
+
try {
|
|
303
|
+
this.logEvent({ message: reconnect ? 'reconnect' : 'connect', name: this.name, id: this.id, address: this.peripheral ? this.peripheral.address : this.address, state: this.connectState });
|
|
304
|
+
if (!reconnect && this.connectState.isConnecting) {
|
|
305
|
+
yield this.waitForConnectFinished(CONNECT_WAIT_TIMEOUT);
|
|
306
|
+
}
|
|
307
|
+
if (this.connectState.isConnected) {
|
|
308
|
+
try {
|
|
309
|
+
yield this.subscribeAll();
|
|
310
|
+
yield this.init();
|
|
311
|
+
}
|
|
312
|
+
catch (err) {
|
|
313
|
+
this.logEvent({ message: 'cannot reconnect', error: err.message || err });
|
|
314
|
+
this.ble.stopConnectSensor();
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
this.ble.stopConnectSensor();
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
this.connectState.isConnecting = true;
|
|
321
|
+
if (!this.peripheral) {
|
|
322
|
+
const { id, name, address } = this;
|
|
323
|
+
try {
|
|
324
|
+
this.peripheral = this.ble.peripheralCache.getPeripheral({ id, name, address });
|
|
325
|
+
const connector = this.ble.peripheralCache.getConnector(this.peripheral);
|
|
326
|
+
if (!connector) {
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
catch (err) {
|
|
330
|
+
this.logEvent({ message: 'error', fn: 'connect()', error: err.message, stack: err.stack });
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (this.peripheral) {
|
|
334
|
+
const { id, address, advertisement } = this.peripheral;
|
|
335
|
+
const name = advertisement === null || advertisement === void 0 ? void 0 : advertisement.localName;
|
|
336
|
+
this.logEvent({ message: 'connect requested', mode: 'peripheral', device: { id, name, address: address } });
|
|
337
|
+
const connected = yield this.connectPeripheral(this.peripheral);
|
|
338
|
+
this.logEvent({ message: 'connect result: ', connected, mode: 'peripheral', device: { id, name, address } });
|
|
339
|
+
this.connectState.isConnecting = false;
|
|
340
|
+
this.connectState.isConnected = connected;
|
|
341
|
+
this.ble.stopConnectSensor();
|
|
342
|
+
return connected;
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
const { id, name, address } = this;
|
|
346
|
+
let error;
|
|
347
|
+
if (this.address || this.id || this.name) {
|
|
348
|
+
this.logEvent({ message: 'connect requested', mode: 'device', device: { id, name, address } });
|
|
349
|
+
try {
|
|
350
|
+
if (this.ble.isScanning()) {
|
|
351
|
+
yield this.ble.stopScan();
|
|
352
|
+
}
|
|
353
|
+
const peripheral = yield this.ble.scanForDevice(this, {});
|
|
354
|
+
if (peripheral) {
|
|
355
|
+
this.peripheral = peripheral;
|
|
356
|
+
const connected = yield this.connectPeripheral(this.peripheral);
|
|
357
|
+
this.logEvent({ message: 'connect result: ', connected, mode: 'device', device: { id, name, address } });
|
|
358
|
+
this.connectState.isConnecting = false;
|
|
359
|
+
this.connectState.isConnected = connected;
|
|
360
|
+
this.ble.stopConnectSensor();
|
|
361
|
+
return connected;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
catch (err) {
|
|
365
|
+
error = err;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
this.logEvent({ message: 'connect result: failure', mode: 'device', device: { id, name, address }, error: error.message });
|
|
369
|
+
this.connectState.isConnecting = false;
|
|
370
|
+
this.connectState.isConnected = false;
|
|
371
|
+
this.ble.stopConnectSensor();
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
catch (err) {
|
|
376
|
+
this.connectState.isConnecting = false;
|
|
377
|
+
this.connectState.isConnected = false;
|
|
378
|
+
this.logEvent({ message: 'connect result: error', error: err.message, stack: err.stack });
|
|
379
|
+
this.ble.stopConnectSensor();
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
disconnect() {
|
|
385
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
386
|
+
const { id, name, address } = this;
|
|
387
|
+
this.logEvent({ message: 'disconnect requested', device: { id, name, address } });
|
|
388
|
+
this.ble.pauseLogging();
|
|
389
|
+
this.connectState.isDisconnecting = true;
|
|
390
|
+
if (this.workerIv) {
|
|
391
|
+
this.stopWorker();
|
|
392
|
+
}
|
|
393
|
+
if (!this.connectState.isConnecting && !this.connectState.isConnected) {
|
|
394
|
+
this.connectState.isDisconnecting = false;
|
|
395
|
+
this.connectState.isConnecting = false;
|
|
396
|
+
this.connectState.isConnected = false;
|
|
397
|
+
this.logEvent({ message: 'disconnect result: success', device: { id, name, address } });
|
|
398
|
+
return true;
|
|
399
|
+
}
|
|
400
|
+
if (this.connectState.isConnecting) {
|
|
401
|
+
this.cleanupListeners();
|
|
402
|
+
setTimeout(() => { this.connectState.isDisconnecting = false; }, 1000);
|
|
403
|
+
this.logEvent({ message: 'disconnect result: unclear - connect ongoing', device: { id, name, address } });
|
|
404
|
+
this.connectState.isConnecting = false;
|
|
405
|
+
this.connectState.isConnected = false;
|
|
406
|
+
return true;
|
|
407
|
+
}
|
|
408
|
+
if (this.connectState.isConnected) {
|
|
409
|
+
this.cleanupListeners();
|
|
410
|
+
this.unsubscribeAll();
|
|
411
|
+
this.logEvent({ message: 'disconnect result: success', device: { id, name, address } });
|
|
412
|
+
this.connectState.isDisconnecting = false;
|
|
413
|
+
this.connectState.isConnecting = false;
|
|
414
|
+
this.connectState.isConnected = false;
|
|
415
|
+
return true;
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
checkForDuplicate(characteristic, data) {
|
|
420
|
+
const prev = this.prevMessages.find(i => i.uuid === characteristic);
|
|
421
|
+
if (prev) {
|
|
422
|
+
if (prev.data === data.toString('hex') && prev.timestamp > Date.now() - 500) {
|
|
423
|
+
prev.timestamp = Date.now();
|
|
424
|
+
return true;
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
prev.data = data.toString('hex');
|
|
428
|
+
prev.timestamp = Date.now();
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
this.prevMessages.push({ uuid: characteristic, timestamp: Date.now(), data: data.toString('hex') });
|
|
433
|
+
}
|
|
434
|
+
return false;
|
|
435
|
+
}
|
|
436
|
+
onData(characteristic, _data) {
|
|
437
|
+
const data = Buffer.from(_data);
|
|
438
|
+
const isDuplicate = this.checkForDuplicate(characteristic, data);
|
|
439
|
+
if (isDuplicate) {
|
|
440
|
+
return false;
|
|
441
|
+
}
|
|
442
|
+
this.logEvent({ message: 'got data', characteristic, data: data.toString('hex'), writeQueue: this.writeQueue.length });
|
|
443
|
+
if (this.writeQueue.length > 0) {
|
|
444
|
+
const writeIdx = this.writeQueue.findIndex(i => (0, utils_2.matches)(i.uuid, characteristic));
|
|
445
|
+
if (writeIdx !== -1) {
|
|
446
|
+
const writeItem = this.writeQueue[writeIdx];
|
|
447
|
+
this.writeQueue.splice(writeIdx, 1);
|
|
448
|
+
if (writeItem.resolve)
|
|
449
|
+
writeItem.resolve(data);
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return true;
|
|
454
|
+
}
|
|
455
|
+
timeoutCheck() {
|
|
456
|
+
const now = Date.now();
|
|
457
|
+
const updatedQueue = [];
|
|
458
|
+
let hasTimeout = false;
|
|
459
|
+
this.writeQueue.forEach(writeItem => {
|
|
460
|
+
if (writeItem.timeout && writeItem.timeout < now) {
|
|
461
|
+
if (writeItem.reject) {
|
|
462
|
+
hasTimeout = true;
|
|
463
|
+
writeItem.reject(new Error('timeout'));
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
else {
|
|
467
|
+
updatedQueue.push(writeItem);
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
if (hasTimeout)
|
|
471
|
+
this.writeQueue = updatedQueue;
|
|
472
|
+
}
|
|
473
|
+
startWorker() {
|
|
474
|
+
if (this.workerIv)
|
|
475
|
+
return;
|
|
476
|
+
this.workerIv = setInterval(() => { this.timeoutCheck(); }, 100);
|
|
477
|
+
}
|
|
478
|
+
stopWorker() {
|
|
479
|
+
if (!this.workerIv)
|
|
480
|
+
return;
|
|
481
|
+
clearInterval(this.workerIv);
|
|
482
|
+
this.workerIv = null;
|
|
483
|
+
}
|
|
484
|
+
write(characteristicUuid, data, props) {
|
|
485
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
486
|
+
if (!this.isConnected())
|
|
487
|
+
throw new Error('not connected');
|
|
488
|
+
try {
|
|
489
|
+
const { withoutResponse, timeout } = props || {};
|
|
490
|
+
let fireAndForget = withoutResponse;
|
|
491
|
+
const connector = this.ble.peripheralCache.getConnector(this.peripheral);
|
|
492
|
+
const isAlreadySubscribed = connector.isSubscribed(characteristicUuid);
|
|
493
|
+
if (!withoutResponse && !this.workerIv) {
|
|
494
|
+
this.startWorker();
|
|
495
|
+
}
|
|
496
|
+
if (!withoutResponse && !isAlreadySubscribed) {
|
|
497
|
+
const connector = this.ble.peripheralCache.getConnector(this.peripheral);
|
|
498
|
+
connector.removeAllListeners(characteristicUuid);
|
|
499
|
+
connector.on(characteristicUuid, (uuid, data) => {
|
|
500
|
+
this.onData(uuid, data);
|
|
501
|
+
});
|
|
502
|
+
this.logEvent({ message: 'write:subscribing ', characteristic: characteristicUuid });
|
|
503
|
+
try {
|
|
504
|
+
yield connector.subscribe(characteristicUuid);
|
|
505
|
+
this.subscribedCharacteristics.push(characteristicUuid);
|
|
506
|
+
}
|
|
507
|
+
catch (err) {
|
|
508
|
+
this.logEvent({ message: 'write:subscribing failed', characteristic: characteristicUuid, error: err.message });
|
|
509
|
+
fireAndForget = true;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return new Promise((resolve, reject) => {
|
|
513
|
+
const characteristic = this.characteristics.find(c => c.uuid === characteristicUuid || (0, utils_2.uuid)(c.uuid) === characteristicUuid);
|
|
514
|
+
if (!characteristic) {
|
|
515
|
+
this.logEvent({ message: 'write: Characteristic not found', characteristicUuid, characteristics: this.characteristics.map(c => c.uuid) });
|
|
516
|
+
reject(new Error(`Characteristic not found`));
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
if (fireAndForget) {
|
|
520
|
+
this.logEvent({ message: 'writing', data: data.toString('hex'), withoutResponse: 'true' });
|
|
521
|
+
characteristic.write(data, true);
|
|
522
|
+
resolve(new ArrayBuffer(0));
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
else {
|
|
526
|
+
const writeId = this.writeQueue.length;
|
|
527
|
+
let messageDeleted = false;
|
|
528
|
+
const writeTimeout = timeout !== undefined ? timeout : BLE_TIMEOUT;
|
|
529
|
+
this.writeQueue.push({ uuid: characteristicUuid.toLocaleLowerCase(), data, timeout: Date.now() + writeTimeout, resolve, reject });
|
|
530
|
+
const to = setTimeout(() => {
|
|
531
|
+
if (this.writeQueue.length > writeId && !messageDeleted)
|
|
532
|
+
this.writeQueue.splice(writeId, 1);
|
|
533
|
+
this.logEvent({ message: 'writing response', err: 'timeout' });
|
|
534
|
+
reject(new Error('timeout'));
|
|
535
|
+
}, 5000);
|
|
536
|
+
this.logEvent({ message: 'writing' });
|
|
537
|
+
characteristic.write(data, withoutResponse, (err) => {
|
|
538
|
+
clearTimeout(to);
|
|
539
|
+
this.logEvent({ message: 'writing response', err });
|
|
540
|
+
if (err) {
|
|
541
|
+
this.writeQueue.splice(writeId, 1);
|
|
542
|
+
messageDeleted = true;
|
|
543
|
+
reject(err);
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
catch (err) {
|
|
550
|
+
this.logEvent({ message: 'error', fn: 'write', error: err.message || err, stack: err.stack });
|
|
551
|
+
}
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
read(characteristicUuid) {
|
|
555
|
+
return new Promise((resolve, reject) => {
|
|
556
|
+
if (!this.isConnected())
|
|
557
|
+
return reject(new Error('not connected'));
|
|
558
|
+
const characteristic = this.characteristics.find(c => c.uuid === characteristicUuid || (0, utils_2.uuid)(c.uuid) === characteristicUuid);
|
|
559
|
+
if (!characteristic) {
|
|
560
|
+
this.logEvent({ message: 'read: Characteristic not found', characteristicUuid, characteristics: this.characteristics.map(c => c.uuid) });
|
|
561
|
+
reject(new Error('Characteristic not found'));
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
characteristic.read((err, data) => {
|
|
565
|
+
if (err && data instanceof Error)
|
|
566
|
+
reject(err);
|
|
567
|
+
else if (data instanceof Error)
|
|
568
|
+
reject(data);
|
|
569
|
+
else
|
|
570
|
+
resolve(data);
|
|
571
|
+
});
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
getDeviceInfo() {
|
|
575
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
576
|
+
const info = this.deviceInfo;
|
|
577
|
+
const readValue = (c) => __awaiter(this, void 0, void 0, function* () {
|
|
578
|
+
try {
|
|
579
|
+
const b = yield this.read(c);
|
|
580
|
+
const buffer = b ? Buffer.from(b) : undefined;
|
|
581
|
+
return buffer ? buffer.toString() : undefined;
|
|
582
|
+
}
|
|
583
|
+
catch (_a) {
|
|
584
|
+
return undefined;
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
info.model = info.model || (yield readValue('2a24'));
|
|
588
|
+
info.serialNo = info.serialNo || (yield readValue('2a25'));
|
|
589
|
+
info.fwRevision = info.fwRevision || (yield readValue('2a26'));
|
|
590
|
+
info.hwRevision = info.hwRevision || (yield readValue('2a27'));
|
|
591
|
+
info.swRevision = info.swRevision || (yield readValue('2a28'));
|
|
592
|
+
info.manufacturer = info.manufacturer || (yield readValue('2a29'));
|
|
593
|
+
this.deviceInfo = info;
|
|
594
|
+
return info;
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
exports.BleComms = BleComms;
|
|
599
|
+
BleComms.services = [];
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { EventLogger } from 'gd-eventlog';
|
|
2
|
+
import BleAdapterFactory from './adapter-factory';
|
|
3
|
+
import { BleInterfaceProps, BlePeripheral, BleDeviceSettings, BleProtocol, BleBinding, BleInterfaceState, BleScanProps, BleCharacteristic } from './types';
|
|
4
|
+
import { BleComms } from './base/comms';
|
|
5
|
+
import { IncyclistInterface, IncyclistScanProps } from '../types';
|
|
6
|
+
import BlePeripheralCache from './peripheral-cache';
|
|
7
|
+
import EventEmitter from 'events';
|
|
8
|
+
export interface ScanState {
|
|
9
|
+
isScanning: boolean;
|
|
10
|
+
isConnecting: boolean;
|
|
11
|
+
isBackgroundScan: boolean;
|
|
12
|
+
timeout?: NodeJS.Timeout;
|
|
13
|
+
peripherals?: Map<string, BlePeripheral>;
|
|
14
|
+
detected?: string[];
|
|
15
|
+
emitter?: EventEmitter;
|
|
16
|
+
}
|
|
17
|
+
export interface ConnectState {
|
|
18
|
+
isConnecting: boolean;
|
|
19
|
+
isConnected: boolean;
|
|
20
|
+
timeout?: NodeJS.Timeout;
|
|
21
|
+
isInitSuccess: boolean;
|
|
22
|
+
connectPromise?: Promise<boolean>;
|
|
23
|
+
}
|
|
24
|
+
export interface BleDeviceInfo {
|
|
25
|
+
device: BleComms;
|
|
26
|
+
isConnected: boolean;
|
|
27
|
+
}
|
|
28
|
+
export interface BleDeviceClassInfo {
|
|
29
|
+
Class: typeof BleComms;
|
|
30
|
+
type: string;
|
|
31
|
+
services: string[];
|
|
32
|
+
id: string;
|
|
33
|
+
}
|
|
34
|
+
export default class BleInterface extends EventEmitter implements IncyclistInterface {
|
|
35
|
+
scanState: ScanState;
|
|
36
|
+
connectState: ConnectState;
|
|
37
|
+
peripheralCache: BlePeripheralCache;
|
|
38
|
+
logger: EventLogger;
|
|
39
|
+
props: BleInterfaceProps;
|
|
40
|
+
binding: BleBinding;
|
|
41
|
+
sensorIsConnecting: boolean;
|
|
42
|
+
emittingAdapters: {
|
|
43
|
+
comms: BleComms;
|
|
44
|
+
cb: (data: any) => void;
|
|
45
|
+
}[];
|
|
46
|
+
loggingPaused: boolean;
|
|
47
|
+
static _instance: BleInterface;
|
|
48
|
+
static getInstance(props?: {
|
|
49
|
+
binding?: BleBinding;
|
|
50
|
+
log?: boolean;
|
|
51
|
+
logger?: EventLogger;
|
|
52
|
+
}): BleInterface;
|
|
53
|
+
constructor(props?: BleInterfaceProps);
|
|
54
|
+
getBinding(): BleBinding;
|
|
55
|
+
setBinding(binding: BleBinding): void;
|
|
56
|
+
getName(): string;
|
|
57
|
+
protected getReconnectPause(): number;
|
|
58
|
+
startConnectSensor(): void;
|
|
59
|
+
stopConnectSensor(): void;
|
|
60
|
+
waitForSensorConnectionFinish(): Promise<void>;
|
|
61
|
+
getAdapterFactory(): BleAdapterFactory;
|
|
62
|
+
pauseLogging(debugOnly?: boolean): void;
|
|
63
|
+
resumeLogging(): void;
|
|
64
|
+
protected isDebugEnabled(): boolean;
|
|
65
|
+
protected logEvent(event: any): void;
|
|
66
|
+
protected onStateChange(state: BleInterfaceState): void;
|
|
67
|
+
protected onError(err: any): void;
|
|
68
|
+
connect(to?: number): Promise<boolean>;
|
|
69
|
+
disconnect(): Promise<boolean>;
|
|
70
|
+
isConnected(): boolean;
|
|
71
|
+
onDisconnect(peripheral: any): void;
|
|
72
|
+
protected scannerWaitForConnection(tsTimeoutExpired?: number): Promise<void>;
|
|
73
|
+
protected getCharacteristics(peripheral: BlePeripheral): Promise<BleCharacteristic[]>;
|
|
74
|
+
protected waitForScanFinished(timeout: any): Promise<unknown>;
|
|
75
|
+
protected onPeripheralFound(p: BlePeripheral, callback: any, props?: {
|
|
76
|
+
request?: BleDeviceSettings;
|
|
77
|
+
comms?: BleComms;
|
|
78
|
+
protocolFilter?: BleProtocol[] | null;
|
|
79
|
+
}): Promise<any>;
|
|
80
|
+
scanForDevice(comms: BleComms, props: IncyclistScanProps): Promise<BlePeripheral>;
|
|
81
|
+
scan(props?: BleScanProps): Promise<BleDeviceSettings[]>;
|
|
82
|
+
stopScan(): Promise<boolean>;
|
|
83
|
+
isScanning(): boolean;
|
|
84
|
+
}
|