incyclist-devices 1.4.36 → 1.4.39
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/ble-device.d.ts +3 -8
- package/lib/ble/ble-device.js +79 -23
- package/lib/ble/ble-interface.d.ts +8 -1
- package/lib/ble/ble-interface.js +239 -67
- package/lib/ble/ble.d.ts +10 -0
- package/lib/ble/ble.js +10 -0
- package/lib/ble/hrm.js +3 -2
- package/lib/ble/pwr.d.ts +1 -0
- package/lib/ble/pwr.js +7 -3
- package/package.json +1 -1
package/lib/ble/ble-device.d.ts
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { EventLogger } from "gd-eventlog";
|
|
3
|
-
import { BleInterfaceClass, BleDeviceClass, BlePeripheral,
|
|
4
|
-
interface ConnectState {
|
|
5
|
-
isConnecting: boolean;
|
|
6
|
-
isConnected: boolean;
|
|
7
|
-
isDisconnecting: boolean;
|
|
8
|
-
}
|
|
3
|
+
import { BleInterfaceClass, BleDeviceClass, BlePeripheral, BleDeviceProps, ConnectProps } from "./ble";
|
|
9
4
|
interface BleDeviceConstructProps extends BleDeviceProps {
|
|
10
5
|
log?: boolean;
|
|
11
6
|
logger?: EventLogger;
|
|
@@ -17,15 +12,15 @@ export declare abstract class BleDevice extends BleDeviceClass {
|
|
|
17
12
|
services: string[];
|
|
18
13
|
ble: BleInterfaceClass;
|
|
19
14
|
peripheral?: BlePeripheral;
|
|
20
|
-
characteristics:
|
|
15
|
+
characteristics: any[];
|
|
21
16
|
state?: string;
|
|
22
|
-
connectState: ConnectState;
|
|
23
17
|
logger?: EventLogger;
|
|
24
18
|
constructor(props?: BleDeviceConstructProps);
|
|
25
19
|
logEvent(event: any): void;
|
|
26
20
|
setInterface(ble: BleInterfaceClass): void;
|
|
27
21
|
private cleanupListeners;
|
|
28
22
|
private onDisconnect;
|
|
23
|
+
waitForConnectFinished(timeout: any): Promise<unknown>;
|
|
29
24
|
connect(props?: ConnectProps): Promise<boolean>;
|
|
30
25
|
disconnect(): Promise<boolean>;
|
|
31
26
|
abstract getProfile(): string;
|
package/lib/ble/ble-device.js
CHANGED
|
@@ -11,11 +11,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
const gd_eventlog_1 = require("gd-eventlog");
|
|
13
13
|
const ble_1 = require("./ble");
|
|
14
|
+
const CONNECT_WAIT_TIMEOUT = 10000;
|
|
14
15
|
class BleDevice extends ble_1.BleDeviceClass {
|
|
15
16
|
constructor(props) {
|
|
16
17
|
super();
|
|
17
18
|
this.characteristics = [];
|
|
18
|
-
this.connectState = { isConnecting: false, isConnected: false, isDisconnecting: false };
|
|
19
19
|
this.id = props.id;
|
|
20
20
|
this.address = props.address;
|
|
21
21
|
this.name = props.name;
|
|
@@ -43,7 +43,7 @@ class BleDevice extends ble_1.BleDeviceClass {
|
|
|
43
43
|
this.logger.logEvent(event);
|
|
44
44
|
}
|
|
45
45
|
if (process.env.BLE_DEBUG) {
|
|
46
|
-
console.log(event);
|
|
46
|
+
console.log('~~~BLE:', event);
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
setInterface(ble) {
|
|
@@ -55,9 +55,9 @@ class BleDevice extends ble_1.BleDeviceClass {
|
|
|
55
55
|
}
|
|
56
56
|
else {
|
|
57
57
|
this.characteristics.forEach(c => {
|
|
58
|
+
c.unsubscribe();
|
|
58
59
|
c.removeAllListeners('data');
|
|
59
60
|
});
|
|
60
|
-
this.characteristics = [];
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
onDisconnect() {
|
|
@@ -67,42 +67,96 @@ class BleDevice extends ble_1.BleDeviceClass {
|
|
|
67
67
|
}
|
|
68
68
|
this.emit('disconnected');
|
|
69
69
|
}
|
|
70
|
+
waitForConnectFinished(timeout) {
|
|
71
|
+
const waitStart = Date.now();
|
|
72
|
+
const waitTimeout = waitStart + timeout;
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
const waitIv = setInterval(() => {
|
|
75
|
+
try {
|
|
76
|
+
if (this.connectState.isConnecting && Date.now() > waitTimeout) {
|
|
77
|
+
clearInterval(waitIv);
|
|
78
|
+
return reject(new Error('connection already in progress'));
|
|
79
|
+
}
|
|
80
|
+
if (!this.connectState.isConnecting) {
|
|
81
|
+
clearInterval(waitIv);
|
|
82
|
+
return resolve(true);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
console.log('~~~ error', err);
|
|
87
|
+
}
|
|
88
|
+
}, 100);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
70
91
|
connect(props) {
|
|
71
92
|
return __awaiter(this, void 0, void 0, function* () {
|
|
72
93
|
const connectPeripheral = (peripheral) => __awaiter(this, void 0, void 0, function* () {
|
|
73
94
|
this.connectState.isConnecting = true;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
95
|
+
const connected = this.ble.findConnected(peripheral);
|
|
96
|
+
if (!connected) {
|
|
97
|
+
try {
|
|
98
|
+
yield peripheral.connectAsync();
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
this.logEvent({ message: 'cannot connect', error: err.message || err });
|
|
102
|
+
}
|
|
79
103
|
}
|
|
80
|
-
this.connectState.isConnecting = false;
|
|
81
|
-
this.connectState.isConnected = true;
|
|
82
|
-
this.state = "connected";
|
|
83
|
-
this.emit('connected');
|
|
84
|
-
this.cleanupListeners();
|
|
85
|
-
this.ble.addConnectedDevice(this);
|
|
86
|
-
this.peripheral.once('disconnect', () => { this.onDisconnect(); });
|
|
87
104
|
try {
|
|
88
|
-
|
|
89
|
-
|
|
105
|
+
this.cleanupListeners();
|
|
106
|
+
if (!connected) {
|
|
107
|
+
this.logEvent({ message: 'connect: discover characteristics start' });
|
|
108
|
+
const res = yield peripheral.discoverSomeServicesAndCharacteristicsAsync(this.services || [], []);
|
|
109
|
+
const { characteristics } = res;
|
|
110
|
+
this.logEvent({ message: 'connect: discover characteristics result',
|
|
111
|
+
result: characteristics.map(c => ({ uuid: ble_1.uuid(c.uuid), properties: c.properties.join(','), service: ble_1.uuid(c._serviceUuid) }))
|
|
112
|
+
});
|
|
113
|
+
this.characteristics = characteristics;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
this.characteristics = connected.characteristics;
|
|
117
|
+
}
|
|
118
|
+
this.connectState.isConnecting = false;
|
|
119
|
+
this.connectState.isConnected = true;
|
|
120
|
+
this.state = "connected";
|
|
121
|
+
this.emit('connected');
|
|
122
|
+
this.ble.addConnectedDevice(this);
|
|
123
|
+
this.peripheral.once('disconnect', () => { this.onDisconnect(); });
|
|
124
|
+
this.characteristics.forEach(c => {
|
|
90
125
|
if (c.properties.find(p => p === 'notify')) {
|
|
91
126
|
c.on('data', (data, _isNotification) => {
|
|
92
127
|
this.onData(ble_1.uuid(c.uuid), data);
|
|
93
128
|
});
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
129
|
+
if (!connected) {
|
|
130
|
+
this.logEvent({ message: 'subscribe', device: this.name, address: this.address, service: c._serviceUuid, characteristic: c.uuid });
|
|
131
|
+
c.subscribe((err) => {
|
|
132
|
+
if (err)
|
|
133
|
+
this.logEvent({ message: 'cannot subscribe', device: this.name, address: this.address, service: c._serviceUuid, characteristic: c.uuid, error: err.message || err });
|
|
134
|
+
});
|
|
135
|
+
}
|
|
98
136
|
}
|
|
99
137
|
});
|
|
100
138
|
}
|
|
101
139
|
catch (err) {
|
|
102
140
|
this.logEvent({ message: 'cannot connect', error: err.message || err });
|
|
141
|
+
this.connectState.isConnecting = false;
|
|
142
|
+
this.connectState.isConnected = false;
|
|
103
143
|
}
|
|
104
144
|
});
|
|
105
145
|
try {
|
|
146
|
+
if (this.connectState.isConnecting) {
|
|
147
|
+
yield this.waitForConnectFinished(CONNECT_WAIT_TIMEOUT);
|
|
148
|
+
}
|
|
149
|
+
if (this.connectState.isConnected) {
|
|
150
|
+
this.characteristics.forEach(c => {
|
|
151
|
+
if (c.properties.find(p => p === 'notify')) {
|
|
152
|
+
c.on('data', (data, _isNotification) => {
|
|
153
|
+
this.onData(ble_1.uuid(c.uuid), data);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
this.connectState.isConnecting = true;
|
|
106
160
|
if (this.peripheral) {
|
|
107
161
|
const { id, address, advertisement } = this.peripheral;
|
|
108
162
|
const name = advertisement === null || advertisement === void 0 ? void 0 : advertisement.localName;
|
|
@@ -113,8 +167,8 @@ class BleDevice extends ble_1.BleDeviceClass {
|
|
|
113
167
|
}
|
|
114
168
|
else {
|
|
115
169
|
const { id, name, address } = this;
|
|
170
|
+
let error;
|
|
116
171
|
if (this.address || this.id || this.name) {
|
|
117
|
-
this.connectState.isConnecting = true;
|
|
118
172
|
this.logEvent({ message: 'connect requested', mode: 'device', device: { id, name, address } });
|
|
119
173
|
try {
|
|
120
174
|
if (this.ble.isScanning()) {
|
|
@@ -129,9 +183,11 @@ class BleDevice extends ble_1.BleDeviceClass {
|
|
|
129
183
|
}
|
|
130
184
|
}
|
|
131
185
|
catch (err) {
|
|
186
|
+
console.log('~~~ error', err);
|
|
187
|
+
error = err;
|
|
132
188
|
}
|
|
133
189
|
}
|
|
134
|
-
this.logEvent({ message: 'connect result: failure', mode: 'device', device: { id, name, address } });
|
|
190
|
+
this.logEvent({ message: 'connect result: failure', mode: 'device', device: { id, name, address }, error: error.message, stack: error.stack });
|
|
135
191
|
this.connectState.isConnecting = false;
|
|
136
192
|
this.connectState.isConnected = false;
|
|
137
193
|
return false;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { EventLogger } from 'gd-eventlog';
|
|
3
|
-
import { BleInterfaceClass, ConnectProps, ScanProps, BleDeviceClass, BleBinding } from './ble';
|
|
3
|
+
import { BleInterfaceClass, ConnectProps, ScanProps, BleDeviceClass, BlePeripheral, BleBinding } from './ble';
|
|
4
4
|
export interface ScanState {
|
|
5
5
|
isScanning: boolean;
|
|
6
|
+
isConnecting: boolean;
|
|
7
|
+
isBackgroundScan: boolean;
|
|
6
8
|
timeout?: NodeJS.Timeout;
|
|
7
9
|
}
|
|
8
10
|
export interface ConnectState {
|
|
@@ -26,6 +28,7 @@ export default class BleInterface extends BleInterfaceClass {
|
|
|
26
28
|
connectState: ConnectState;
|
|
27
29
|
devices: BleDeviceInfo[];
|
|
28
30
|
logger: EventLogger;
|
|
31
|
+
deviceCache: any[];
|
|
29
32
|
static deviceClasses: BleDeviceClassInfo[];
|
|
30
33
|
static _instance: BleInterface;
|
|
31
34
|
static getInstance(props?: {
|
|
@@ -48,10 +51,14 @@ export default class BleInterface extends BleInterfaceClass {
|
|
|
48
51
|
getDevicesFromServices(deviceTypes: (typeof BleDeviceClass)[], services: string | string[]): (typeof BleDeviceClass)[];
|
|
49
52
|
getServicesFromDeviceTypes(deviceTypes: (typeof BleDeviceClass)[]): string[];
|
|
50
53
|
getServicesFromDevice(device: BleDeviceClass): string[];
|
|
54
|
+
waitForConnectFinished(timeout: any): Promise<unknown>;
|
|
51
55
|
connectDevice(requested: BleDeviceClass, timeout?: number): Promise<BleDeviceClass>;
|
|
56
|
+
waitForScanFinished(timeout: any): Promise<unknown>;
|
|
57
|
+
addPeripheralToCache(peripheral: BlePeripheral): void;
|
|
52
58
|
scan(props: ScanProps): Promise<BleDeviceClass[]>;
|
|
53
59
|
stopScan(): Promise<boolean>;
|
|
54
60
|
isScanning(): boolean;
|
|
55
61
|
addConnectedDevice(device: BleDeviceClass): void;
|
|
62
|
+
findConnected(device: BleDeviceClass | BlePeripheral): BleDeviceClass;
|
|
56
63
|
removeConnectedDevice(device: BleDeviceClass): void;
|
|
57
64
|
}
|
package/lib/ble/ble-interface.js
CHANGED
|
@@ -12,12 +12,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
const gd_eventlog_1 = require("gd-eventlog");
|
|
13
13
|
const utils_1 = require("../utils");
|
|
14
14
|
const ble_1 = require("./ble");
|
|
15
|
+
const CONNECT_TIMEOUT = 5000;
|
|
15
16
|
class BleInterface extends ble_1.BleInterfaceClass {
|
|
16
17
|
constructor(props = {}) {
|
|
17
18
|
super(props);
|
|
18
|
-
this.scanState = { isScanning: false, timeout: undefined };
|
|
19
|
+
this.scanState = { isScanning: false, isConnecting: false, timeout: undefined, isBackgroundScan: false };
|
|
19
20
|
this.connectState = { isConnecting: false, isConnected: false, isInitSuccess: false };
|
|
20
21
|
this.devices = [];
|
|
22
|
+
this.deviceCache = [];
|
|
21
23
|
if (props.logger)
|
|
22
24
|
this.logger = props.logger;
|
|
23
25
|
else if (props.log) {
|
|
@@ -51,7 +53,7 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
51
53
|
this.logger.logEvent(event);
|
|
52
54
|
}
|
|
53
55
|
if (process.env.BLE_DEBUG) {
|
|
54
|
-
console.log(event);
|
|
56
|
+
console.log('~~BLE:', event);
|
|
55
57
|
}
|
|
56
58
|
}
|
|
57
59
|
onStateChange(state) {
|
|
@@ -67,11 +69,21 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
67
69
|
}
|
|
68
70
|
connect(props = {}) {
|
|
69
71
|
const timeout = props.timeout || 2000;
|
|
72
|
+
const runBackgroundScan = () => {
|
|
73
|
+
this.scanState.isBackgroundScan = true;
|
|
74
|
+
this.scan({ timeout: 5000, isBackgroundScan: true })
|
|
75
|
+
.then(() => {
|
|
76
|
+
this.scanState.isBackgroundScan = false;
|
|
77
|
+
})
|
|
78
|
+
.catch(() => {
|
|
79
|
+
this.scanState.isBackgroundScan = false;
|
|
80
|
+
});
|
|
81
|
+
};
|
|
70
82
|
return new Promise((resolve, reject) => {
|
|
71
83
|
if (this.connectState.isConnected) {
|
|
72
84
|
return resolve(true);
|
|
73
85
|
}
|
|
74
|
-
this.logEvent({ message: 'connect request' });
|
|
86
|
+
this.logEvent({ message: 'connect request', });
|
|
75
87
|
if (!this.getBinding())
|
|
76
88
|
return Promise.reject(new Error('no binding defined'));
|
|
77
89
|
this.connectState.timeout = setTimeout(() => {
|
|
@@ -114,7 +126,9 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
114
126
|
this.connectState.isConnected = true;
|
|
115
127
|
this.connectState.isConnecting = false;
|
|
116
128
|
this.logEvent({ message: 'connect result: success' });
|
|
117
|
-
|
|
129
|
+
resolve(true);
|
|
130
|
+
runBackgroundScan();
|
|
131
|
+
return;
|
|
118
132
|
}
|
|
119
133
|
else {
|
|
120
134
|
this.getBinding().once('error', (err) => {
|
|
@@ -133,6 +147,7 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
133
147
|
this.connectState.isConnected = true;
|
|
134
148
|
this.connectState.isConnecting = false;
|
|
135
149
|
this.logEvent({ message: 'connect result: success' });
|
|
150
|
+
runBackgroundScan();
|
|
136
151
|
return resolve(true);
|
|
137
152
|
}
|
|
138
153
|
else {
|
|
@@ -229,16 +244,37 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
229
244
|
});
|
|
230
245
|
return services;
|
|
231
246
|
}
|
|
232
|
-
|
|
247
|
+
waitForConnectFinished(timeout) {
|
|
248
|
+
const waitStart = Date.now();
|
|
249
|
+
const waitTimeout = waitStart + timeout;
|
|
250
|
+
return new Promise((resolve, reject) => {
|
|
251
|
+
const waitIv = setInterval(() => {
|
|
252
|
+
if (this.scanState.isConnecting && Date.now() > waitTimeout) {
|
|
253
|
+
clearInterval(waitIv);
|
|
254
|
+
return reject(new Error('Connecting already in progress'));
|
|
255
|
+
}
|
|
256
|
+
if (!this.scanState.isConnecting) {
|
|
257
|
+
clearInterval(waitIv);
|
|
258
|
+
return resolve(true);
|
|
259
|
+
}
|
|
260
|
+
}, 100);
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
connectDevice(requested, timeout = CONNECT_TIMEOUT) {
|
|
233
264
|
return __awaiter(this, void 0, void 0, function* () {
|
|
234
|
-
const { id, name, address } = requested;
|
|
235
|
-
|
|
265
|
+
const { id, name, address, getProfile } = requested;
|
|
266
|
+
const profile = getProfile && typeof (getProfile) === 'function' ? getProfile() : undefined;
|
|
267
|
+
this.logEvent({ message: 'connectDevice', id, name, address, profile, isbusy: this.scanState.isConnecting });
|
|
268
|
+
if (this.scanState.isConnecting) {
|
|
269
|
+
yield this.waitForConnectFinished(10000);
|
|
270
|
+
}
|
|
271
|
+
this.scanState.isConnecting = true;
|
|
236
272
|
let devices = [];
|
|
237
273
|
let retry = false;
|
|
238
274
|
let retryCount = 0;
|
|
239
275
|
do {
|
|
240
276
|
if (retryCount > 0) {
|
|
241
|
-
this.logEvent({ message: 'retry connect device', retryCount });
|
|
277
|
+
this.logEvent({ message: 'retry connect device', id, name, address, profile, retryCount });
|
|
242
278
|
}
|
|
243
279
|
try {
|
|
244
280
|
devices = yield this.scan({ timeout, device: requested });
|
|
@@ -249,28 +285,66 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
249
285
|
}
|
|
250
286
|
catch (err) {
|
|
251
287
|
if (err.message === 'scanning already in progress') {
|
|
252
|
-
this.logEvent({ message: 'scan busy' });
|
|
253
288
|
yield utils_1.sleep(1000);
|
|
254
289
|
retryCount++;
|
|
255
290
|
retry = retryCount < 5;
|
|
256
291
|
continue;
|
|
257
292
|
}
|
|
293
|
+
this.scanState.isConnecting = false;
|
|
258
294
|
throw err;
|
|
259
295
|
}
|
|
260
296
|
} while (devices.length === 0 && retry);
|
|
261
|
-
if (devices.length === 0)
|
|
297
|
+
if (devices.length === 0) {
|
|
298
|
+
this.logEvent({ message: 'connectDevice failure', id, name, address, profile, error: 'device not found' });
|
|
299
|
+
this.scanState.isConnecting = false;
|
|
262
300
|
throw new Error('device not found');
|
|
301
|
+
}
|
|
263
302
|
if (devices[0]) {
|
|
303
|
+
this.logEvent({ message: 'connectDevice connecting', id, name, address, profile });
|
|
264
304
|
const connected = yield devices[0].connect();
|
|
305
|
+
this.scanState.isConnecting = false;
|
|
265
306
|
if (connected) {
|
|
307
|
+
this.logEvent({ message: 'connectDevice success', id, name, address, profile });
|
|
266
308
|
return devices[0];
|
|
267
309
|
}
|
|
268
310
|
else {
|
|
311
|
+
this.logEvent({ message: 'connectDevice failure', id, name, address, profile });
|
|
269
312
|
throw new Error('connect failed');
|
|
270
313
|
}
|
|
271
314
|
}
|
|
272
315
|
});
|
|
273
316
|
}
|
|
317
|
+
waitForScanFinished(timeout) {
|
|
318
|
+
const waitStart = Date.now();
|
|
319
|
+
const waitTimeout = waitStart + timeout;
|
|
320
|
+
return new Promise((resolve, reject) => {
|
|
321
|
+
const waitIv = setInterval(() => {
|
|
322
|
+
if (this.scanState.isScanning && Date.now() > waitTimeout) {
|
|
323
|
+
clearInterval(waitIv);
|
|
324
|
+
return reject(new Error('scanning already in progress'));
|
|
325
|
+
}
|
|
326
|
+
if (!this.scanState.isScanning) {
|
|
327
|
+
clearInterval(waitIv);
|
|
328
|
+
return resolve(true);
|
|
329
|
+
}
|
|
330
|
+
}, 100);
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
addPeripheralToCache(peripheral) {
|
|
334
|
+
try {
|
|
335
|
+
this.logEvent({ message: 'adding device to cache', device: { address: peripheral.address, name: peripheral.advertisement ? peripheral.advertisement.localName : '' } });
|
|
336
|
+
const existing = this.deviceCache.find(p => p.address === peripheral.address);
|
|
337
|
+
if (!existing)
|
|
338
|
+
this.deviceCache.push(peripheral);
|
|
339
|
+
else {
|
|
340
|
+
if (peripheral.advertisement && peripheral.advertisement.localName !== '' && existing.advertisement && existing.advertisement.localName === '')
|
|
341
|
+
existing.advertisement.localName = peripheral.advertisement.localName;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
catch (err) {
|
|
345
|
+
console.log('~~~ error', err);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
274
348
|
scan(props) {
|
|
275
349
|
return __awaiter(this, void 0, void 0, function* () {
|
|
276
350
|
const { timeout = 5000, deviceTypes = [], device } = props;
|
|
@@ -282,84 +356,175 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
282
356
|
if (!this.isConnected()) {
|
|
283
357
|
yield this.connect();
|
|
284
358
|
}
|
|
359
|
+
this.logEvent({ message: 'scan()', props, scanState: this.scanState, cache: this.deviceCache.map(p => ({ name: p.advertisement ? p.advertisement.localName : '', address: p.address })) });
|
|
360
|
+
if (!props.isBackgroundScan && this.scanState.isBackgroundScan) {
|
|
361
|
+
yield this.stopScan();
|
|
362
|
+
this.scanState.isBackgroundScan = false;
|
|
363
|
+
}
|
|
285
364
|
const detectedPeripherals = {};
|
|
286
|
-
|
|
365
|
+
let opStr;
|
|
287
366
|
if (scanForDevice) {
|
|
367
|
+
opStr = 'search device';
|
|
288
368
|
const { id, address, name } = device;
|
|
289
369
|
this.logEvent({ message: 'search device request', device: { id, address, name }, deviceTypes });
|
|
290
370
|
}
|
|
291
|
-
else
|
|
371
|
+
else {
|
|
372
|
+
opStr = 'scan';
|
|
292
373
|
this.logEvent({ message: 'scan start', services });
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
374
|
+
}
|
|
375
|
+
if (this.scanState.isScanning) {
|
|
376
|
+
try {
|
|
377
|
+
yield this.waitForScanFinished(timeout);
|
|
378
|
+
}
|
|
379
|
+
catch (err) {
|
|
380
|
+
this.logEvent({ message: `${opStr} result: already scanning` });
|
|
381
|
+
return Promise.reject(err);
|
|
297
382
|
}
|
|
383
|
+
}
|
|
384
|
+
return new Promise((resolve, reject) => {
|
|
298
385
|
this.scanState.isScanning = true;
|
|
299
|
-
|
|
300
|
-
if (
|
|
301
|
-
this.
|
|
302
|
-
|
|
303
|
-
|
|
386
|
+
if (scanForDevice && device instanceof ble_1.BleDeviceClass) {
|
|
387
|
+
if (this.devices && this.devices.length > 0) {
|
|
388
|
+
const connectedDevices = this.devices.map(i => ({ name: i.device.name, address: i.device.address, isConnected: i.isConnected, connectState: i.device.getConnectState() }));
|
|
389
|
+
const { name, address } = device;
|
|
390
|
+
this.logEvent({ message: `${opStr}: check if already registered`, device: { name, address }, connectedDevices });
|
|
391
|
+
const existing = this.devices.find(i => (i.device.address === device.address || i.device.name === device.name));
|
|
392
|
+
if (existing) {
|
|
393
|
+
const d = device;
|
|
394
|
+
const linkedDevice = existing.device;
|
|
395
|
+
d.peripheral = existing.device.peripheral;
|
|
396
|
+
if (d.setInterface && typeof (d.setInterface) === 'function')
|
|
397
|
+
d.setInterface(this);
|
|
398
|
+
setTimeout(() => {
|
|
399
|
+
let connectState = linkedDevice.getConnectState();
|
|
400
|
+
this.logEvent({ message: `${opStr}: device already registered`, device: device.name, address: device.address, connectState });
|
|
401
|
+
if (connectState.isConnecting) {
|
|
402
|
+
const waitStart = Date.now();
|
|
403
|
+
const waitTimeout = waitStart + timeout;
|
|
404
|
+
const waitIv = setInterval(() => {
|
|
405
|
+
try {
|
|
406
|
+
connectState = linkedDevice.getConnectState();
|
|
407
|
+
if (connectState.isConnecting && Date.now() > waitTimeout) {
|
|
408
|
+
clearInterval(waitIv);
|
|
409
|
+
this.scanState.isScanning = false;
|
|
410
|
+
return resolve([]);
|
|
411
|
+
}
|
|
412
|
+
if (!connectState.isConnecting) {
|
|
413
|
+
clearInterval(waitIv);
|
|
414
|
+
this.scanState.isScanning = false;
|
|
415
|
+
return resolve([device]);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
catch (err) {
|
|
419
|
+
console.log('~~~ error', err);
|
|
420
|
+
}
|
|
421
|
+
}, 100);
|
|
422
|
+
}
|
|
423
|
+
else if (connectState.isConnected) {
|
|
424
|
+
this.scanState.isScanning = false;
|
|
425
|
+
resolve([device]);
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
this.scanState.isScanning = false;
|
|
429
|
+
resolve([]);
|
|
430
|
+
}
|
|
431
|
+
}, 100);
|
|
432
|
+
}
|
|
304
433
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
434
|
+
}
|
|
435
|
+
const onPeripheralFound = (peripheral, fromCache = false) => {
|
|
436
|
+
if (fromCache)
|
|
437
|
+
this.logEvent({ message: 'adding from Cache', peripheral: peripheral.address });
|
|
438
|
+
if (!peripheral || !peripheral.advertisement)
|
|
439
|
+
return;
|
|
440
|
+
if (!detectedPeripherals[peripheral.id]) {
|
|
441
|
+
if (process.env.BLE_DEBUG)
|
|
442
|
+
console.log('discovered', peripheral);
|
|
443
|
+
detectedPeripherals[peripheral.id] = peripheral;
|
|
444
|
+
this.addPeripheralToCache(peripheral);
|
|
445
|
+
let DeviceClasses;
|
|
446
|
+
if (scanForDevice && (!deviceTypes || deviceTypes.length === 0)) {
|
|
447
|
+
const classes = BleInterface.deviceClasses.map(c => c.Class);
|
|
448
|
+
DeviceClasses = this.getDevicesFromServices(classes, peripheral.advertisement.serviceUuids);
|
|
449
|
+
}
|
|
450
|
+
else {
|
|
451
|
+
DeviceClasses = this.getDevicesFromServices(deviceTypes, peripheral.advertisement.serviceUuids);
|
|
452
|
+
}
|
|
453
|
+
DeviceClasses.forEach(DeviceClass => {
|
|
454
|
+
let cntFound = 0;
|
|
455
|
+
if (!DeviceClass)
|
|
456
|
+
return;
|
|
457
|
+
if (scanForDevice && cntFound > 0)
|
|
458
|
+
return;
|
|
459
|
+
const C = DeviceClass;
|
|
460
|
+
const d = new C({ peripheral });
|
|
461
|
+
if (device && device.getProfile && device.getProfile() !== d.getProfile())
|
|
462
|
+
return;
|
|
463
|
+
d.setInterface(this);
|
|
464
|
+
if (scanForDevice) {
|
|
465
|
+
if ((device.id && device.id !== '' && d.id === device.id) ||
|
|
466
|
+
(device.address && device.address !== '' && d.address === device.address) ||
|
|
467
|
+
(device.name && device.name !== '' && d.name === device.name))
|
|
468
|
+
cntFound++;
|
|
314
469
|
}
|
|
315
|
-
else
|
|
316
|
-
|
|
470
|
+
else
|
|
471
|
+
cntFound++;
|
|
472
|
+
const existing = this.devices.find(i => i.device.id === d.id && i.device.getProfile() === d.getProfile());
|
|
473
|
+
if (!scanForDevice && cntFound > 0 && !existing) {
|
|
474
|
+
this.logEvent({ message: `${opStr}: device found`, device: d.name, address: d.address, services: d.services.join(',') });
|
|
475
|
+
this.devices.push({ device: d, isConnected: false });
|
|
476
|
+
this.emit('device', d);
|
|
317
477
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
478
|
+
if (scanForDevice && cntFound > 0) {
|
|
479
|
+
if (fromCache) {
|
|
480
|
+
resolve([d]);
|
|
321
481
|
return;
|
|
322
|
-
if (scanForDevice && cntFound > 0)
|
|
323
|
-
return;
|
|
324
|
-
const C = DeviceClass;
|
|
325
|
-
const d = new C({ peripheral });
|
|
326
|
-
d.setInterface(this);
|
|
327
|
-
if (scanForDevice) {
|
|
328
|
-
if ((device.id && device.id !== '' && d.id === device.id) ||
|
|
329
|
-
(device.address && device.address !== '' && d.address === device.address) ||
|
|
330
|
-
(device.name && device.name !== '' && d.name === device.name))
|
|
331
|
-
cntFound++;
|
|
332
482
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
}
|
|
341
|
-
if (scanForDevice && cntFound > 0) {
|
|
342
|
-
if (this.scanState.timeout) {
|
|
343
|
-
clearTimeout(this.scanState.timeout);
|
|
344
|
-
this.scanState.timeout = null;
|
|
345
|
-
bleBinding.stopScanning(() => {
|
|
346
|
-
this.scanState.isScanning = false;
|
|
347
|
-
resolve([d]);
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
else {
|
|
483
|
+
if (this.scanState.timeout) {
|
|
484
|
+
clearTimeout(this.scanState.timeout);
|
|
485
|
+
this.scanState.timeout = null;
|
|
486
|
+
this.logEvent({ message: `${opStr}: stop scanning`, requested: scanForDevice ? { name: device.name, address: device.address } : undefined, });
|
|
487
|
+
bleBinding.stopScanning(() => {
|
|
488
|
+
this.getBinding().removeAllListeners('discover');
|
|
489
|
+
this.scanState.isScanning = false;
|
|
351
490
|
resolve([d]);
|
|
352
|
-
}
|
|
491
|
+
});
|
|
353
492
|
}
|
|
354
|
-
|
|
355
|
-
|
|
493
|
+
else {
|
|
494
|
+
resolve([d]);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
this.logEvent({ message: `${opStr}: start scanning`, requested: scanForDevice ? { name: device.name, address: device.address } : undefined, timeout });
|
|
503
|
+
this.deviceCache.forEach(peripheral => {
|
|
504
|
+
onPeripheralFound(peripheral, true);
|
|
505
|
+
});
|
|
506
|
+
bleBinding.startScanning([], true, (err) => {
|
|
507
|
+
if (err) {
|
|
508
|
+
this.logEvent({ message: `${opStr} result: error`, requested: scanForDevice ? { name: device.name, address: device.address } : undefined, error: err.message });
|
|
509
|
+
this.scanState.isScanning = false;
|
|
510
|
+
return reject(err);
|
|
511
|
+
}
|
|
512
|
+
bleBinding.on('discover', (p) => {
|
|
513
|
+
console.log('~~~ discovered:', p.address, p.advertisement ? p.advertisement.localName : '');
|
|
514
|
+
onPeripheralFound(p);
|
|
356
515
|
});
|
|
357
516
|
});
|
|
358
517
|
this.scanState.timeout = setTimeout(() => {
|
|
359
518
|
this.scanState.timeout = null;
|
|
360
|
-
this.logEvent({ message:
|
|
519
|
+
this.logEvent({ message: `${opStr} result: devices found`, requested: scanForDevice ? { name: device.name, address: device.address } : undefined, devices: this.devices.map(i => i.device.name + (!i.device.name || i.device.name === '') ? `addr=${i.device.address}` : '') });
|
|
520
|
+
this.getBinding().removeAllListeners('discover');
|
|
521
|
+
this.logEvent({ message: `${opStr}: stop scanning`, requested: scanForDevice ? { name: device.name, address: device.address } : undefined, });
|
|
361
522
|
bleBinding.stopScanning(() => {
|
|
362
523
|
this.scanState.isScanning = false;
|
|
524
|
+
if (scanForDevice) {
|
|
525
|
+
reject(new Error('device not found'));
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
363
528
|
resolve(this.devices.map(i => i.device));
|
|
364
529
|
});
|
|
365
530
|
}, timeout);
|
|
@@ -372,6 +537,7 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
372
537
|
}
|
|
373
538
|
if (!this.getBinding())
|
|
374
539
|
return Promise.reject(new Error('no binding defined'));
|
|
540
|
+
this.getBinding().removeAllListeners('discover');
|
|
375
541
|
this.logEvent({ message: 'scan stop request' });
|
|
376
542
|
return new Promise(resolve => {
|
|
377
543
|
this.getBinding().stopScanning(() => {
|
|
@@ -392,6 +558,12 @@ class BleInterface extends ble_1.BleInterfaceClass {
|
|
|
392
558
|
}
|
|
393
559
|
this.devices.push({ device, isConnected: true });
|
|
394
560
|
}
|
|
561
|
+
findConnected(device) {
|
|
562
|
+
const connected = this.devices.find(i => i.device.id === device.id && i.isConnected);
|
|
563
|
+
if (connected)
|
|
564
|
+
return connected.device;
|
|
565
|
+
return undefined;
|
|
566
|
+
}
|
|
395
567
|
removeConnectedDevice(device) {
|
|
396
568
|
const existigDevice = this.devices.find(i => i.device.id === device.id);
|
|
397
569
|
if (existigDevice) {
|
package/lib/ble/ble.d.ts
CHANGED
|
@@ -8,12 +8,20 @@ export interface BleDeviceIdentifier {
|
|
|
8
8
|
address?: string;
|
|
9
9
|
name?: string;
|
|
10
10
|
}
|
|
11
|
+
export interface ConnectState {
|
|
12
|
+
isConnecting: boolean;
|
|
13
|
+
isConnected: boolean;
|
|
14
|
+
isDisconnecting: boolean;
|
|
15
|
+
}
|
|
11
16
|
export declare abstract class BleDeviceClass extends EventEmitter {
|
|
12
17
|
static services: string[];
|
|
13
18
|
id?: string;
|
|
14
19
|
address?: string;
|
|
15
20
|
name?: string;
|
|
16
21
|
peripheral?: BlePeripheral;
|
|
22
|
+
connectState: ConnectState;
|
|
23
|
+
getConnectState(): ConnectState;
|
|
24
|
+
isConnected(): boolean;
|
|
17
25
|
abstract getProfile(): string;
|
|
18
26
|
abstract getServiceUUids(): string[];
|
|
19
27
|
abstract connect(props?: ConnectProps): Promise<boolean>;
|
|
@@ -29,6 +37,7 @@ export declare type ScanProps = {
|
|
|
29
37
|
timeout?: number;
|
|
30
38
|
deviceTypes?: (typeof BleDeviceClass)[];
|
|
31
39
|
device?: BleDeviceClass;
|
|
40
|
+
isBackgroundScan?: boolean;
|
|
32
41
|
};
|
|
33
42
|
export declare class BleBindingWrapper {
|
|
34
43
|
protected binding: BleBinding;
|
|
@@ -51,6 +60,7 @@ export declare abstract class BleInterfaceClass extends EventEmitter {
|
|
|
51
60
|
abstract isScanning(): boolean;
|
|
52
61
|
abstract addConnectedDevice(device: BleDeviceClass): void;
|
|
53
62
|
abstract removeConnectedDevice(device: BleDeviceClass): void;
|
|
63
|
+
abstract findConnected(device: BleDeviceClass | BlePeripheral): BleDeviceClass;
|
|
54
64
|
getBinding(): BleBinding;
|
|
55
65
|
setBinding(binding: BleBinding): void;
|
|
56
66
|
}
|
package/lib/ble/ble.js
CHANGED
|
@@ -5,6 +5,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const events_1 = __importDefault(require("events"));
|
|
7
7
|
class BleDeviceClass extends events_1.default {
|
|
8
|
+
constructor() {
|
|
9
|
+
super(...arguments);
|
|
10
|
+
this.connectState = { isConnecting: false, isConnected: false, isDisconnecting: false };
|
|
11
|
+
}
|
|
12
|
+
getConnectState() {
|
|
13
|
+
return this.connectState;
|
|
14
|
+
}
|
|
15
|
+
isConnected() {
|
|
16
|
+
return this.connectState.isConnected;
|
|
17
|
+
}
|
|
8
18
|
}
|
|
9
19
|
exports.BleDeviceClass = BleDeviceClass;
|
|
10
20
|
BleDeviceClass.services = [];
|
package/lib/ble/hrm.js
CHANGED
|
@@ -99,7 +99,7 @@ class HrmAdapter extends Device_1.default {
|
|
|
99
99
|
}
|
|
100
100
|
start(props) {
|
|
101
101
|
return __awaiter(this, void 0, void 0, function* () {
|
|
102
|
-
this.logger.logEvent({ message: 'start requested', props });
|
|
102
|
+
this.logger.logEvent({ message: 'start requested', profile: this.getProfile(), props });
|
|
103
103
|
try {
|
|
104
104
|
const bleDevice = yield this.ble.connectDevice(this.device);
|
|
105
105
|
if (bleDevice) {
|
|
@@ -112,13 +112,14 @@ class HrmAdapter extends Device_1.default {
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
catch (err) {
|
|
115
|
-
this.logger.logEvent({ message: 'start result: error', error: err.message });
|
|
115
|
+
this.logger.logEvent({ message: 'start result: error', error: err.message, profile: this.getProfile() });
|
|
116
116
|
throw new Error(`could not start device, reason:${err.message}`);
|
|
117
117
|
}
|
|
118
118
|
});
|
|
119
119
|
}
|
|
120
120
|
stop() {
|
|
121
121
|
return __awaiter(this, void 0, void 0, function* () {
|
|
122
|
+
this.logger.logEvent({ message: 'stop requested', profile: this.getProfile() });
|
|
122
123
|
return this.device.disconnect();
|
|
123
124
|
});
|
|
124
125
|
}
|
package/lib/ble/pwr.d.ts
CHANGED
package/lib/ble/pwr.js
CHANGED
|
@@ -30,7 +30,7 @@ class BleCyclingPowerDevice extends ble_device_1.BleDevice {
|
|
|
30
30
|
this.prevCrankData = undefined;
|
|
31
31
|
}
|
|
32
32
|
getProfile() {
|
|
33
|
-
return '
|
|
33
|
+
return 'Power Meter';
|
|
34
34
|
}
|
|
35
35
|
getServiceUUids() {
|
|
36
36
|
return BleCyclingPowerDevice.services;
|
|
@@ -166,6 +166,9 @@ class PwrAdapter extends Device_1.default {
|
|
|
166
166
|
this.ignore = ignore;
|
|
167
167
|
}
|
|
168
168
|
onDeviceData(deviceData) {
|
|
169
|
+
if (this.prevDataTS && Date.now() - this.prevDataTS < 1000)
|
|
170
|
+
return;
|
|
171
|
+
this.prevDataTS = Date.now();
|
|
169
172
|
this.logger.logEvent({ message: 'onDeviceData', data: deviceData });
|
|
170
173
|
let incyclistData = this.mapData(deviceData);
|
|
171
174
|
incyclistData = this.getCyclingMode().updateData(incyclistData);
|
|
@@ -214,7 +217,7 @@ class PwrAdapter extends Device_1.default {
|
|
|
214
217
|
}
|
|
215
218
|
start(props) {
|
|
216
219
|
return __awaiter(this, void 0, void 0, function* () {
|
|
217
|
-
this.logger.logEvent({ message: 'start requested', props });
|
|
220
|
+
this.logger.logEvent({ message: 'start requested', profile: this.getProfile(), props });
|
|
218
221
|
try {
|
|
219
222
|
const bleDevice = yield this.ble.connectDevice(this.device);
|
|
220
223
|
if (bleDevice) {
|
|
@@ -226,13 +229,14 @@ class PwrAdapter extends Device_1.default {
|
|
|
226
229
|
}
|
|
227
230
|
}
|
|
228
231
|
catch (err) {
|
|
229
|
-
this.logger.logEvent({ message: 'start result: error', error: err.message });
|
|
232
|
+
this.logger.logEvent({ message: 'start result: error', error: err.message, profile: this.getProfile() });
|
|
230
233
|
throw new Error(`could not start device, reason:${err.message}`);
|
|
231
234
|
}
|
|
232
235
|
});
|
|
233
236
|
}
|
|
234
237
|
stop() {
|
|
235
238
|
return __awaiter(this, void 0, void 0, function* () {
|
|
239
|
+
this.logger.logEvent({ message: 'stop requested', profile: this.getProfile() });
|
|
236
240
|
this.distanceInternal = 0;
|
|
237
241
|
this.device.reset();
|
|
238
242
|
return this.device.disconnect();
|