incyclist-devices 2.3.0-beta.19 → 2.3.0-beta.20
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/README.MD +55 -0
- package/lib/ble/adapter-factory.d.ts +24 -20
- package/lib/ble/adapter-factory.js +36 -13
- package/lib/ble/base/comms.d.ts +74 -2
- package/lib/ble/base/comms.js +596 -3
- package/lib/ble/ble-interface.d.ts +4 -7
- package/lib/ble/ble-interface.js +2 -16
- package/lib/ble/ble-peripheral.d.ts +0 -1
- package/lib/ble/ble-peripheral.js +11 -7
- package/lib/ble/characteristics/csc/features.d.ts +10 -0
- package/lib/ble/characteristics/csc/features.js +19 -0
- package/lib/ble/characteristics/csc/measurement.d.ts +33 -0
- package/lib/ble/characteristics/csc/measurement.js +109 -0
- package/lib/ble/characteristics/types.d.ts +6 -0
- package/lib/ble/characteristics/types.js +2 -0
- package/lib/ble/consts.d.ts +1 -0
- package/lib/ble/consts.js +2 -1
- package/lib/ble/cp/comm.d.ts +1 -1
- package/lib/ble/cp/comm.js +2 -2
- package/lib/ble/csc/adapter.d.ts +17 -0
- package/lib/ble/csc/adapter.js +66 -0
- package/lib/ble/csc/index.d.ts +3 -0
- package/lib/ble/csc/index.js +19 -0
- package/lib/ble/csc/sensor.d.ts +21 -0
- package/lib/ble/csc/sensor.js +64 -0
- package/lib/ble/csc/types.d.ts +6 -0
- package/lib/ble/csc/types.js +2 -0
- package/lib/ble/elite/comms.d.ts +1 -1
- package/lib/ble/elite/comms.js +2 -2
- package/lib/ble/fm/comms.d.ts +1 -1
- package/lib/ble/fm/comms.js +3 -3
- package/lib/ble/hr/comm.d.ts +1 -1
- package/lib/ble/hr/comm.js +2 -2
- package/lib/ble/index.js +2 -0
- package/lib/ble/tacx/comms.d.ts +1 -1
- package/lib/ble/tacx/comms.js +2 -2
- package/lib/ble/tacx/sensor.js +1 -1
- package/lib/ble/types.d.ts +1 -1
- package/lib/ble/utils.d.ts +1 -0
- package/lib/ble/utils.js +5 -1
- package/lib/ble/wahoo/comms.d.ts +1 -1
- package/lib/ble/wahoo/comms.js +2 -2
- package/package.json +1 -1
package/lib/ble/base/comms.js
CHANGED
|
@@ -1,6 +1,599 @@
|
|
|
1
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
|
+
};
|
|
2
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
|
|
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
|
+
}
|
|
5
597
|
}
|
|
6
|
-
exports.
|
|
598
|
+
exports.BleComms = BleComms;
|
|
599
|
+
BleComms.services = [];
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { EventLogger } from 'gd-eventlog';
|
|
2
2
|
import BleAdapterFactory from './adapter-factory';
|
|
3
|
-
import { BleInterfaceProps, BlePeripheral, BleDeviceSettings, BleProtocol, BleBinding, BleInterfaceState, BleScanProps, BleCharacteristic
|
|
3
|
+
import { BleInterfaceProps, BlePeripheral, BleDeviceSettings, BleProtocol, BleBinding, BleInterfaceState, BleScanProps, BleCharacteristic } from './types';
|
|
4
4
|
import { BleComms } from './base/comms';
|
|
5
|
-
import { IncyclistScanProps } from '../types';
|
|
5
|
+
import { IncyclistInterface, IncyclistScanProps } from '../types';
|
|
6
6
|
import BlePeripheralCache from './peripheral-cache';
|
|
7
7
|
import EventEmitter from 'events';
|
|
8
8
|
export interface ScanState {
|
|
@@ -31,7 +31,7 @@ export interface BleDeviceClassInfo {
|
|
|
31
31
|
services: string[];
|
|
32
32
|
id: string;
|
|
33
33
|
}
|
|
34
|
-
export default class BleInterface extends EventEmitter implements
|
|
34
|
+
export default class BleInterface extends EventEmitter implements IncyclistInterface {
|
|
35
35
|
scanState: ScanState;
|
|
36
36
|
connectState: ConnectState;
|
|
37
37
|
peripheralCache: BlePeripheralCache;
|
|
@@ -62,8 +62,7 @@ export default class BleInterface extends EventEmitter implements IBleInterface
|
|
|
62
62
|
pauseLogging(debugOnly?: boolean): void;
|
|
63
63
|
resumeLogging(): void;
|
|
64
64
|
protected isDebugEnabled(): boolean;
|
|
65
|
-
logEvent(event: any): void;
|
|
66
|
-
logError(err: Error, fn: string, args?: any): void;
|
|
65
|
+
protected logEvent(event: any): void;
|
|
67
66
|
protected onStateChange(state: BleInterfaceState): void;
|
|
68
67
|
protected onError(err: any): void;
|
|
69
68
|
connect(to?: number): Promise<boolean>;
|
|
@@ -80,8 +79,6 @@ export default class BleInterface extends EventEmitter implements IBleInterface
|
|
|
80
79
|
}): Promise<any>;
|
|
81
80
|
scanForDevice(comms: BleComms, props: IncyclistScanProps): Promise<BlePeripheral>;
|
|
82
81
|
scan(props?: BleScanProps): Promise<BleDeviceSettings[]>;
|
|
83
|
-
protected legacyScan(props?: BleScanProps): Promise<BleDeviceSettings[]>;
|
|
84
82
|
stopScan(): Promise<boolean>;
|
|
85
83
|
isScanning(): boolean;
|
|
86
|
-
getLogger(): EventLogger;
|
|
87
84
|
}
|
package/lib/ble/ble-interface.js
CHANGED
|
@@ -118,10 +118,6 @@ class BleInterface extends events_1.default {
|
|
|
118
118
|
console.log('~~~ BLE', event);
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
|
-
logError(err, fn, args) {
|
|
122
|
-
const logInfo = args || {};
|
|
123
|
-
this.logEvent(Object.assign(Object.assign({ message: 'Error', fn }, logInfo), { error: err.message, stack: err.stack }));
|
|
124
|
-
}
|
|
125
121
|
onStateChange(state) {
|
|
126
122
|
if (state !== 'poweredOn') {
|
|
127
123
|
this.logEvent({ message: 'Ble disconnected', });
|
|
@@ -146,12 +142,10 @@ class BleInterface extends events_1.default {
|
|
|
146
142
|
this.resumeLogging();
|
|
147
143
|
const timeout = this.props.timeout || to || 2000;
|
|
148
144
|
const connect = new Promise((resolve, reject) => {
|
|
149
|
-
this.logEvent({ message: 'Ble connect request'
|
|
145
|
+
this.logEvent({ message: 'Ble connect request' });
|
|
150
146
|
if (!this.getBinding())
|
|
151
147
|
return reject(new Error('no binding defined'));
|
|
152
|
-
this.connectState.isConnecting = true;
|
|
153
148
|
this.connectState.timeout = setTimeout(() => {
|
|
154
|
-
console.log('connect timeout');
|
|
155
149
|
this.connectState.isConnected = false;
|
|
156
150
|
this.connectState.isConnecting = false;
|
|
157
151
|
this.connectState.timeout = null;
|
|
@@ -500,13 +494,8 @@ class BleInterface extends events_1.default {
|
|
|
500
494
|
}
|
|
501
495
|
scan() {
|
|
502
496
|
return __awaiter(this, arguments, void 0, function* (props = {}) {
|
|
503
|
-
return yield this.legacyScan(props);
|
|
504
|
-
});
|
|
505
|
-
}
|
|
506
|
-
legacyScan() {
|
|
507
|
-
return __awaiter(this, arguments, void 0, function* (props = {}) {
|
|
508
|
-
this.resumeLogging();
|
|
509
497
|
this.logEvent({ message: 'starting scan ..' });
|
|
498
|
+
this.resumeLogging();
|
|
510
499
|
const { timeout, protocol, protocols } = props;
|
|
511
500
|
const requestedProtocols = protocols || [];
|
|
512
501
|
if (protocol && !requestedProtocols.find(p => p === protocol))
|
|
@@ -629,8 +618,5 @@ class BleInterface extends events_1.default {
|
|
|
629
618
|
isScanning() {
|
|
630
619
|
return this.scanState.isScanning === true;
|
|
631
620
|
}
|
|
632
|
-
getLogger() {
|
|
633
|
-
return this.logger;
|
|
634
|
-
}
|
|
635
621
|
}
|
|
636
622
|
exports.default = BleInterface;
|