@stoprocent/noble 1.19.0 → 2.0.0
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 +393 -650
- package/examples/advertisement-discovery.js +57 -48
- package/examples/connect-address.js +59 -34
- package/examples/echo.js +59 -69
- package/examples/enter-exit.js +55 -49
- package/examples/multiple-bindings.js +53 -0
- package/examples/peripheral-explorer-async.js +39 -21
- package/examples/peripheral-explorer.ts +52 -0
- package/index.d.ts +249 -209
- package/index.js +4 -1
- package/jest.config.js +4 -0
- package/lib/characteristic.js +153 -127
- package/lib/{win/src/callbacks.h → common/include/Emit.h} +17 -14
- package/lib/common/include/Peripheral.h +31 -0
- package/lib/common/include/ThreadSafeCallback.h +95 -0
- package/lib/{win/src/callbacks.cc → common/src/Emit.cc} +111 -68
- package/lib/descriptor.js +57 -54
- package/lib/hci-socket/acl-stream.js +2 -4
- package/lib/hci-socket/bindings.js +96 -73
- package/lib/hci-socket/gap.js +2 -3
- package/lib/hci-socket/gatt.js +2 -5
- package/lib/hci-socket/hci.js +19 -7
- package/lib/hci-socket/signaling.js +2 -3
- package/lib/hci-socket/smp.js +2 -3
- package/lib/hci-socket/vs.js +1 -0
- package/lib/mac/binding.gyp +5 -7
- package/lib/mac/bindings.js +1 -3
- package/lib/mac/src/ble_manager.h +1 -8
- package/lib/mac/src/ble_manager.mm +87 -44
- package/lib/mac/src/napi_objc.h +1 -0
- package/lib/mac/src/napi_objc.mm +0 -6
- package/lib/mac/src/noble_mac.h +5 -3
- package/lib/mac/src/noble_mac.mm +99 -57
- package/lib/mac/src/objc_cpp.h +3 -2
- package/lib/mac/src/objc_cpp.mm +0 -6
- package/lib/noble.js +579 -488
- package/lib/peripheral.js +171 -174
- package/lib/resolve-bindings.js +37 -30
- package/lib/service.js +58 -55
- package/lib/win/binding.gyp +4 -11
- package/lib/win/bindings.js +1 -3
- package/lib/win/src/ble_manager.cc +291 -166
- package/lib/win/src/ble_manager.h +11 -13
- package/lib/win/src/napi_winrt.cc +1 -7
- package/lib/win/src/napi_winrt.h +1 -1
- package/lib/win/src/noble_winrt.cc +88 -61
- package/lib/win/src/noble_winrt.h +5 -3
- package/lib/win/src/notify_map.cc +0 -7
- package/lib/win/src/notify_map.h +1 -8
- package/lib/win/src/peripheral_winrt.cc +29 -11
- package/lib/win/src/peripheral_winrt.h +1 -1
- package/lib/win/src/radio_watcher.cc +79 -69
- package/lib/win/src/radio_watcher.h +30 -11
- package/lib/win/src/winrt_cpp.cc +1 -1
- package/lib/win/src/winrt_cpp.h +3 -0
- package/package.json +14 -17
- package/prebuilds/darwin-x64+arm64/@stoprocent+noble.node +0 -0
- package/prebuilds/win32-ia32/@stoprocent+noble.node +0 -0
- package/prebuilds/win32-x64/@stoprocent+noble.node +0 -0
- package/test/lib/characteristic.test.js +202 -322
- package/test/lib/descriptor.test.js +62 -95
- package/test/lib/hci-socket/acl-stream.test.js +112 -108
- package/test/lib/hci-socket/bindings.test.js +576 -365
- package/test/lib/hci-socket/hci.test.js +442 -473
- package/test/lib/hci-socket/signaling.test.js +45 -48
- package/test/lib/hci-socket/smp.test.js +144 -142
- package/test/lib/hci-socket/vs.test.js +193 -18
- package/test/lib/peripheral.test.js +492 -322
- package/test/lib/resolve-bindings.test.js +207 -82
- package/test/lib/service.test.js +79 -88
- package/test/noble.test.js +381 -1085
- package/.editorconfig +0 -11
- package/.nycrc.json +0 -4
- package/codecov.yml +0 -5
- package/examples/cache-gatt-discovery.js +0 -198
- package/examples/cache-gatt-reconnect.js +0 -164
- package/examples/ext-advertisement-discovery.js +0 -65
- package/examples/peripheral-explorer.js +0 -225
- package/examples/pizza/central.js +0 -194
- package/examples/pizza/pizza.js +0 -60
- package/examples/test/test.custom.js +0 -131
- package/examples/uart-bind-params.js +0 -28
- package/lib/distributed/bindings.js +0 -326
- package/lib/mac/src/callbacks.cc +0 -222
- package/lib/mac/src/callbacks.h +0 -84
- package/lib/mac/src/peripheral.h +0 -23
- package/lib/resolve-bindings-web.js +0 -9
- package/lib/webbluetooth/bindings.js +0 -368
- package/lib/websocket/bindings.js +0 -321
- package/lib/win/src/peripheral.h +0 -23
- package/test/lib/distributed/bindings.test.js +0 -918
- package/test/lib/webbluetooth/bindings.test.js +0 -190
- package/test/lib/websocket/bindings.test.js +0 -456
- package/test/mocha.setup.js +0 -0
- package/with-bindings.js +0 -5
- package/with-custom-binding.js +0 -6
- package/ws-slave.js +0 -404
package/lib/noble.js
CHANGED
|
@@ -1,659 +1,750 @@
|
|
|
1
1
|
const debug = require('debug')('noble');
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const util = require('util');
|
|
3
|
+
const { EventEmitter } = require('events');
|
|
5
4
|
|
|
6
5
|
const Peripheral = require('./peripheral');
|
|
7
6
|
const Service = require('./service');
|
|
8
7
|
const Characteristic = require('./characteristic');
|
|
9
8
|
const Descriptor = require('./descriptor');
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
this.initialized = false;
|
|
13
|
-
|
|
14
|
-
this.address = 'unknown';
|
|
15
|
-
this._state = 'unknown';
|
|
16
|
-
this._bindings = bindings;
|
|
10
|
+
class Noble extends EventEmitter {
|
|
17
11
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
this._bindings.on('stateChange', this.onStateChange.bind(this));
|
|
21
|
-
this._bindings.on('addressChange', this.onAddressChange.bind(this));
|
|
22
|
-
this._bindings.on('scanParametersSet', this.onScanParametersSet.bind(this));
|
|
23
|
-
this._bindings.on('scanStart', this.onScanStart.bind(this));
|
|
24
|
-
this._bindings.on('scanStop', this.onScanStop.bind(this));
|
|
25
|
-
this._bindings.on('discover', this.onDiscover.bind(this));
|
|
26
|
-
this._bindings.on('connect', this.onConnect.bind(this));
|
|
27
|
-
this._bindings.on('disconnect', this.onDisconnect.bind(this));
|
|
28
|
-
this._bindings.on('rssiUpdate', this.onRssiUpdate.bind(this));
|
|
29
|
-
this._bindings.on('servicesDiscover', this.onServicesDiscover.bind(this));
|
|
30
|
-
this._bindings.on('servicesDiscovered', this.onServicesDiscovered.bind(this));
|
|
31
|
-
this._bindings.on('includedServicesDiscover', this.onIncludedServicesDiscover.bind(this));
|
|
32
|
-
this._bindings.on('characteristicsDiscover', this.onCharacteristicsDiscover.bind(this));
|
|
33
|
-
this._bindings.on('characteristicsDiscovered', this.onCharacteristicsDiscovered.bind(this));
|
|
34
|
-
this._bindings.on('read', this.onRead.bind(this));
|
|
35
|
-
this._bindings.on('write', this.onWrite.bind(this));
|
|
36
|
-
this._bindings.on('broadcast', this.onBroadcast.bind(this));
|
|
37
|
-
this._bindings.on('notify', this.onNotify.bind(this));
|
|
38
|
-
this._bindings.on('descriptorsDiscover', this.onDescriptorsDiscover.bind(this));
|
|
39
|
-
this._bindings.on('valueRead', this.onValueRead.bind(this));
|
|
40
|
-
this._bindings.on('valueWrite', this.onValueWrite.bind(this));
|
|
41
|
-
this._bindings.on('handleRead', this.onHandleRead.bind(this));
|
|
42
|
-
this._bindings.on('handleWrite', this.onHandleWrite.bind(this));
|
|
43
|
-
this._bindings.on('handleNotify', this.onHandleNotify.bind(this));
|
|
44
|
-
this._bindings.on('onMtu', this.onMtu.bind(this));
|
|
45
|
-
|
|
46
|
-
this.on('warning', (message) => {
|
|
47
|
-
if (this.listeners('warning').length === 1) {
|
|
48
|
-
console.warn(`noble: ${message}`);
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
// lazy init bindings on first new listener, should be on stateChange
|
|
53
|
-
this.on('newListener', (event) => {
|
|
54
|
-
if (event === 'stateChange' && !this.initialized) {
|
|
55
|
-
this.initialized = true;
|
|
56
|
-
process.nextTick(() => {
|
|
57
|
-
try {
|
|
58
|
-
this._bindings.init();
|
|
59
|
-
} catch (error) {
|
|
60
|
-
this.emit('warning', 'Initialization of USB device failed: ' + error.message);
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
});
|
|
12
|
+
constructor (bindings) {
|
|
13
|
+
super();
|
|
65
14
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
this.initialized = true;
|
|
72
|
-
this._bindings.init();
|
|
73
|
-
}
|
|
74
|
-
return this._state;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
util.inherits(Noble, events.EventEmitter);
|
|
81
|
-
|
|
82
|
-
Noble.prototype._cleanupPeriperals = function () {
|
|
83
|
-
this._peripherals = {};
|
|
84
|
-
this._services = {};
|
|
85
|
-
this._characteristics = {};
|
|
86
|
-
this._descriptors = {};
|
|
87
|
-
this._discoveredPeripheralUUids = {};
|
|
88
|
-
};
|
|
15
|
+
this._address = 'unknown';
|
|
16
|
+
this._state = 'unknown';
|
|
17
|
+
|
|
18
|
+
this._initialized = false;
|
|
19
|
+
this._bindings = bindings;
|
|
89
20
|
|
|
90
|
-
|
|
91
|
-
|
|
21
|
+
this._discoveredPeripherals = new Set();
|
|
22
|
+
this._peripherals = new Map();
|
|
23
|
+
this._services = {};
|
|
24
|
+
this._characteristics = {};
|
|
25
|
+
this._descriptors = {};
|
|
92
26
|
|
|
93
|
-
// If the state is poweredOff and the previous state was poweredOn, clean up the peripherals
|
|
94
|
-
if (state === 'poweredOff' && this._state === 'poweredOn') {
|
|
95
27
|
this._cleanupPeriperals();
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
this._state = state;
|
|
99
|
-
this.emit('stateChange', state);
|
|
100
|
-
};
|
|
101
28
|
|
|
102
|
-
|
|
103
|
-
debug(`addressChange ${address}`);
|
|
29
|
+
this.on('warning', message => console.warn(`noble: ${message}`));
|
|
104
30
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
31
|
+
this.on('newListener', event => {
|
|
32
|
+
if (event === 'stateChange' && this._initialized === false) {
|
|
33
|
+
this._initialized = true;
|
|
34
|
+
process.nextTick(this._initializeBindings.bind(this));
|
|
35
|
+
}
|
|
36
|
+
});
|
|
111
37
|
}
|
|
112
|
-
this._bindings.setScanParameters(interval, window);
|
|
113
|
-
};
|
|
114
38
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
39
|
+
get state () {
|
|
40
|
+
if (this._initialized === false) {
|
|
41
|
+
this._initializeBindings();
|
|
42
|
+
}
|
|
43
|
+
return this._state;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
get address () {
|
|
47
|
+
return this._address;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
_initializeBindings () {
|
|
51
|
+
this._initialized = true;
|
|
52
|
+
this._registerListeners();
|
|
53
|
+
this._bindings.start();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
_registerListeners () {
|
|
57
|
+
this._bindings.on('stateChange', this._onStateChange.bind(this));
|
|
58
|
+
this._bindings.on('addressChange', this._onAddressChange.bind(this));
|
|
59
|
+
this._bindings.on('scanParametersSet', this._onScanParametersSet.bind(this));
|
|
60
|
+
this._bindings.on('scanStart', this._onScanStart.bind(this));
|
|
61
|
+
this._bindings.on('scanStop', this._onScanStop.bind(this));
|
|
62
|
+
this._bindings.on('discover', this._onDiscover.bind(this));
|
|
63
|
+
this._bindings.on('connect', this._onConnect.bind(this));
|
|
64
|
+
this._bindings.on('disconnect', this._onDisconnect.bind(this));
|
|
65
|
+
this._bindings.on('rssiUpdate', this._onRssiUpdate.bind(this));
|
|
66
|
+
this._bindings.on('servicesDiscover', this._onServicesDiscover.bind(this));
|
|
67
|
+
this._bindings.on('servicesDiscovered', this._onServicesDiscovered.bind(this));
|
|
68
|
+
this._bindings.on('includedServicesDiscover', this._onIncludedServicesDiscover.bind(this));
|
|
69
|
+
this._bindings.on('characteristicsDiscover', this._onCharacteristicsDiscover.bind(this));
|
|
70
|
+
this._bindings.on('characteristicsDiscovered', this._onCharacteristicsDiscovered.bind(this));
|
|
71
|
+
this._bindings.on('read', this._onRead.bind(this));
|
|
72
|
+
this._bindings.on('write', this._onWrite.bind(this));
|
|
73
|
+
this._bindings.on('broadcast', this._onBroadcast.bind(this));
|
|
74
|
+
this._bindings.on('notify', this._onNotify.bind(this));
|
|
75
|
+
this._bindings.on('descriptorsDiscover', this._onDescriptorsDiscover.bind(this));
|
|
76
|
+
this._bindings.on('valueRead', this._onValueRead.bind(this));
|
|
77
|
+
this._bindings.on('valueWrite', this._onValueWrite.bind(this));
|
|
78
|
+
this._bindings.on('handleRead', this._onHandleRead.bind(this));
|
|
79
|
+
this._bindings.on('handleWrite', this._onHandleWrite.bind(this));
|
|
80
|
+
this._bindings.on('handleNotify', this._onHandleNotify.bind(this));
|
|
81
|
+
this._bindings.on('onMtu', this._onMtu.bind(this));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
_createPeripheral (uuid, address, addressType, connectable, advertisement, rssi, scannable) {
|
|
85
|
+
const peripheral = new Peripheral(this, uuid, address, addressType, connectable, advertisement, rssi, scannable);
|
|
86
|
+
|
|
87
|
+
this._peripherals.set(uuid, peripheral);
|
|
88
|
+
this._services[uuid] = {};
|
|
89
|
+
this._characteristics[uuid] = {};
|
|
90
|
+
this._descriptors[uuid] = {};
|
|
119
91
|
|
|
120
|
-
|
|
121
|
-
if (this._bindings.setAddress) {
|
|
122
|
-
this._bindings.setAddress(address);
|
|
123
|
-
} else {
|
|
124
|
-
this.emit('warning', 'current binding does not implement setAddress method.');
|
|
92
|
+
return peripheral;
|
|
125
93
|
}
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
Noble.prototype.waitForPoweredOn = function (timeout = 10000) {
|
|
129
|
-
return new Promise((resolve, reject) => {
|
|
130
|
-
const timeoutId = setTimeout(() => {
|
|
131
|
-
reject(new Error('Timeout waiting for Noble to be powered on'));
|
|
132
|
-
}, timeout);
|
|
133
94
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
|
|
95
|
+
_cleanupPeriperals (uuid = null) {
|
|
96
|
+
const terminateConnection = (peripheral) => {
|
|
97
|
+
if (peripheral.state === 'connecting') {
|
|
98
|
+
// To unblock the async connect call
|
|
99
|
+
this._onConnect(peripheral.id, new Error('cleanup'));
|
|
100
|
+
}
|
|
101
|
+
else if (peripheral.state !== 'disconnected') {
|
|
102
|
+
this._onDisconnect(peripheral.id, 'cleanup');
|
|
141
103
|
}
|
|
142
104
|
};
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
const startScanning = function (serviceUuids, allowDuplicates, callback) {
|
|
149
|
-
if (typeof serviceUuids === 'function') {
|
|
150
|
-
this.emit('warning', 'calling startScanning(callback) is deprecated');
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (typeof allowDuplicates === 'function') {
|
|
154
|
-
this.emit('warning', 'calling startScanning(serviceUuids, callback) is deprecated');
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const scan = function (state) {
|
|
158
|
-
if (state !== 'poweredOn') {
|
|
159
|
-
const error = new Error(`Could not start scanning, state is ${state} (not poweredOn)`);
|
|
160
|
-
|
|
161
|
-
if (typeof callback === 'function') {
|
|
162
|
-
callback(error);
|
|
163
|
-
} else {
|
|
164
|
-
throw error;
|
|
105
|
+
if (uuid) {
|
|
106
|
+
const peripheral = this._peripherals.get(uuid);
|
|
107
|
+
if (peripheral) {
|
|
108
|
+
terminateConnection(peripheral);
|
|
165
109
|
}
|
|
110
|
+
this._peripherals.delete(uuid);
|
|
111
|
+
this._discoveredPeripherals.delete(uuid);
|
|
112
|
+
delete this._services[uuid];
|
|
113
|
+
delete this._characteristics[uuid];
|
|
114
|
+
delete this._descriptors[uuid];
|
|
166
115
|
} else {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
this._discoveredPeripheralUUids = {};
|
|
174
|
-
this._allowDuplicates = allowDuplicates;
|
|
175
|
-
|
|
176
|
-
this._bindings.startScanning(serviceUuids, allowDuplicates);
|
|
116
|
+
this._peripherals.forEach(peripheral => terminateConnection(peripheral));
|
|
117
|
+
this._peripherals.clear();
|
|
118
|
+
this._discoveredPeripherals.clear();
|
|
119
|
+
this._services = {};
|
|
120
|
+
this._characteristics = {};
|
|
121
|
+
this._descriptors = {};
|
|
177
122
|
}
|
|
178
|
-
}
|
|
123
|
+
}
|
|
179
124
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
this.initialized = true;
|
|
125
|
+
_onStateChange (state) {
|
|
126
|
+
debug(`stateChange ${state}`);
|
|
183
127
|
|
|
184
|
-
|
|
128
|
+
// If the state is poweredOff and the previous state was poweredOn, clean up the peripherals
|
|
129
|
+
if (state === 'poweredOff' && this._state === 'poweredOn') {
|
|
130
|
+
this._cleanupPeriperals();
|
|
131
|
+
}
|
|
185
132
|
|
|
186
|
-
this.
|
|
187
|
-
|
|
188
|
-
scan.call(this, this._state);
|
|
133
|
+
this._state = state;
|
|
134
|
+
this.emit('stateChange', state);
|
|
189
135
|
}
|
|
190
|
-
};
|
|
191
136
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
137
|
+
_onAddressChange (address) {
|
|
138
|
+
debug(`addressChange ${address}`);
|
|
139
|
+
this._address = address;
|
|
140
|
+
this.emit('addressChange', address);
|
|
141
|
+
}
|
|
196
142
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
143
|
+
setScanParameters (interval, window, callback) {
|
|
144
|
+
if (callback) {
|
|
145
|
+
this.once('scanParametersSet', callback);
|
|
146
|
+
}
|
|
147
|
+
this._bindings.setScanParameters(interval, window);
|
|
148
|
+
}
|
|
201
149
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
this.
|
|
150
|
+
_onScanParametersSet () {
|
|
151
|
+
debug('scanParametersSet');
|
|
152
|
+
this.emit('scanParametersSet');
|
|
205
153
|
}
|
|
206
|
-
|
|
207
|
-
|
|
154
|
+
|
|
155
|
+
setAddress (address) {
|
|
156
|
+
if (this._bindings.setAddress) {
|
|
157
|
+
this._bindings.setAddress(address);
|
|
158
|
+
} else {
|
|
159
|
+
this.emit('warning', 'current binding does not implement setAddress method.');
|
|
160
|
+
}
|
|
208
161
|
}
|
|
209
|
-
};
|
|
210
162
|
|
|
211
|
-
|
|
212
|
-
|
|
163
|
+
async waitForPoweredOnAsync (timeout = 10000) {
|
|
164
|
+
return new Promise((resolve, reject) => {
|
|
165
|
+
const timeoutId = setTimeout(() => {
|
|
166
|
+
reject(new Error('Timeout waiting for Noble to be powered on'));
|
|
167
|
+
}, timeout);
|
|
213
168
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
};
|
|
169
|
+
const listener = (state) => {
|
|
170
|
+
clearTimeout(timeoutId);
|
|
171
|
+
if (state === 'poweredOn') {
|
|
172
|
+
resolve();
|
|
173
|
+
} else {
|
|
174
|
+
this.once('stateChange', listener);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
224
177
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
this._bindings.stop();
|
|
178
|
+
this.once('stateChange', listener);
|
|
179
|
+
});
|
|
228
180
|
}
|
|
229
|
-
};
|
|
230
181
|
|
|
231
|
-
|
|
232
|
-
|
|
182
|
+
startScanning (serviceUuids, allowDuplicates, callback) {
|
|
183
|
+
const self = this;
|
|
184
|
+
const scan = (state) => {
|
|
185
|
+
if (state !== 'poweredOn') {
|
|
186
|
+
self.once('stateChange', scan.bind(self));
|
|
187
|
+
const error = new Error(`Could not start scanning, state is ${state} (not poweredOn)`);
|
|
233
188
|
|
|
234
|
-
|
|
235
|
-
|
|
189
|
+
if (typeof callback === 'function') {
|
|
190
|
+
callback(error);
|
|
191
|
+
} else {
|
|
192
|
+
throw error;
|
|
193
|
+
}
|
|
194
|
+
} else {
|
|
195
|
+
if (callback) {
|
|
196
|
+
this.once('scanStart', filterDuplicates => callback(null, filterDuplicates));
|
|
197
|
+
}
|
|
236
198
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
} else {
|
|
242
|
-
// "or" the advertisment data with existing
|
|
243
|
-
for (const i in advertisement) {
|
|
244
|
-
if (advertisement[i] !== undefined) {
|
|
245
|
-
peripheral.advertisement[i] = advertisement[i];
|
|
199
|
+
this._discoveredPeripherals.clear();
|
|
200
|
+
this._allowDuplicates = allowDuplicates;
|
|
201
|
+
|
|
202
|
+
this._bindings.startScanning(serviceUuids, allowDuplicates);
|
|
246
203
|
}
|
|
204
|
+
};
|
|
205
|
+
// if bindings still not init, do it now
|
|
206
|
+
if (this._initialized === false) {
|
|
207
|
+
this.once('stateChange', scan.bind(this));
|
|
208
|
+
this._initializeBindings();
|
|
209
|
+
} else {
|
|
210
|
+
scan.call(this, this._state);
|
|
247
211
|
}
|
|
248
|
-
|
|
249
|
-
peripheral.connectable = connectable;
|
|
250
|
-
peripheral.scannable = scannable;
|
|
251
|
-
peripheral.rssi = rssi;
|
|
252
212
|
}
|
|
253
213
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
214
|
+
async startScanningAsync (serviceUUIDs, allowDuplicates) {
|
|
215
|
+
return new Promise((resolve, reject) => {
|
|
216
|
+
this.startScanning(serviceUUIDs, allowDuplicates, error => error ? reject(error) : resolve());
|
|
217
|
+
});
|
|
258
218
|
}
|
|
259
219
|
|
|
260
|
-
|
|
261
|
-
|
|
220
|
+
_onScanStart (filterDuplicates) {
|
|
221
|
+
debug('scanStart');
|
|
222
|
+
this.emit('scanStart', filterDuplicates);
|
|
262
223
|
}
|
|
263
|
-
};
|
|
264
224
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
225
|
+
stopScanning (callback) {
|
|
226
|
+
if (this._initialized === false || this._bindings === null) {
|
|
227
|
+
callback(new Error('Bindings are not initialized'));
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if (callback) {
|
|
231
|
+
this.once('scanStop', callback);
|
|
232
|
+
}
|
|
233
|
+
this._bindings.stopScanning();
|
|
234
|
+
}
|
|
270
235
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
236
|
+
async stopScanningAsync () {
|
|
237
|
+
return new Promise((resolve, reject) => {
|
|
238
|
+
this.stopScanning(error => error ? reject(error) : resolve());
|
|
274
239
|
});
|
|
275
240
|
}
|
|
276
241
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
242
|
+
async *discoverAsync () {
|
|
243
|
+
const deviceQueue = [];
|
|
244
|
+
let scanning = true;
|
|
245
|
+
|
|
246
|
+
this.once('scanStop', () => scanning = false);
|
|
247
|
+
|
|
248
|
+
const listener = peripheral => deviceQueue.push(peripheral);
|
|
249
|
+
this.on('discover', listener);
|
|
250
|
+
|
|
251
|
+
await this.startScanningAsync();
|
|
252
|
+
|
|
253
|
+
while (scanning || deviceQueue.length > 0) {
|
|
254
|
+
if (deviceQueue.length > 0) {
|
|
255
|
+
yield deviceQueue.shift();
|
|
256
|
+
} else {
|
|
257
|
+
await new Promise(resolve => {
|
|
258
|
+
const tempListener = () => resolve();
|
|
259
|
+
this.once('discover', tempListener);
|
|
260
|
+
setTimeout(tempListener, 1000);
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
}
|
|
286
264
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
this.emit(`connect:${peripheralUuid}`, error);
|
|
265
|
+
await this.stopScanningAsync();
|
|
266
|
+
}
|
|
290
267
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
} else {
|
|
295
|
-
this.emit('warning', `unknown peripheral ${peripheralUuid} connected!`);
|
|
268
|
+
_onScanStop () {
|
|
269
|
+
debug('scanStop');
|
|
270
|
+
this.emit('scanStop');
|
|
296
271
|
}
|
|
297
|
-
};
|
|
298
272
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
273
|
+
reset () {
|
|
274
|
+
if (typeof this._bindings.reset !== 'function') { return; }
|
|
275
|
+
this._bindings.reset();
|
|
276
|
+
}
|
|
302
277
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
278
|
+
stop () {
|
|
279
|
+
if (typeof this._bindings.stop !== 'function') { return; }
|
|
280
|
+
this._bindings.stop();
|
|
281
|
+
}
|
|
306
282
|
|
|
307
|
-
|
|
308
|
-
|
|
283
|
+
_onDiscover (uuid, address, addressType, connectable, advertisement, rssi, scannable) {
|
|
284
|
+
let peripheral = this._peripherals.get(uuid);
|
|
309
285
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
286
|
+
if (!peripheral) {
|
|
287
|
+
peripheral = this._createPeripheral(uuid, address, addressType, connectable, advertisement, rssi, scannable);
|
|
288
|
+
} else {
|
|
289
|
+
// "or" the advertisment data with existing
|
|
290
|
+
for (const i in advertisement) {
|
|
291
|
+
if (advertisement[i] !== undefined) {
|
|
292
|
+
peripheral.advertisement[i] = advertisement[i];
|
|
293
|
+
}
|
|
294
|
+
}
|
|
317
295
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
296
|
+
peripheral.connectable = connectable;
|
|
297
|
+
peripheral.scannable = scannable;
|
|
298
|
+
peripheral.rssi = rssi;
|
|
299
|
+
}
|
|
321
300
|
|
|
322
|
-
|
|
323
|
-
const peripheral = this._peripherals[peripheralUuid];
|
|
301
|
+
const previouslyDiscoverd = this._discoveredPeripherals.has(uuid);
|
|
324
302
|
|
|
325
|
-
|
|
326
|
-
|
|
303
|
+
if (!previouslyDiscoverd) {
|
|
304
|
+
this._discoveredPeripherals.add(uuid);
|
|
305
|
+
}
|
|
306
|
+
if (this._allowDuplicates || !previouslyDiscoverd || (!scannable && !connectable)) {
|
|
307
|
+
this.emit('discover', peripheral);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
327
310
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
311
|
+
_getPeripheralId (idOrAddress) {
|
|
312
|
+
let identifier;
|
|
313
|
+
// Convert the peripheralId to an identifier
|
|
314
|
+
if (/^[0-9A-Fa-f]+$/.test(idOrAddress) === false) {
|
|
315
|
+
identifier = this._bindings.addressToId(idOrAddress);
|
|
316
|
+
if (identifier === null) {
|
|
317
|
+
throw new Error(`Invalid peripheral ID or Address ${idOrAddress}`);
|
|
318
|
+
}
|
|
319
|
+
} else {
|
|
320
|
+
identifier = idOrAddress;
|
|
321
|
+
}
|
|
322
|
+
return identifier;
|
|
331
323
|
}
|
|
332
|
-
};
|
|
333
324
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
325
|
+
connect (idOrAddress, parameters, callback) {
|
|
326
|
+
// Get the identifier for the peripheral
|
|
327
|
+
const identifier = this._getPeripheralId(idOrAddress);
|
|
328
|
+
// Check if callback is a function
|
|
329
|
+
if (typeof callback === 'function') {
|
|
330
|
+
// Add a one-time listener for this specific event
|
|
331
|
+
this.once(`connect:${identifier}`, error => callback(error, this._peripherals.get(identifier)));
|
|
332
|
+
}
|
|
337
333
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
servObjs.push(o);
|
|
334
|
+
// Proceed to initiate the connection
|
|
335
|
+
this._bindings.connect(identifier, parameters);
|
|
341
336
|
}
|
|
342
|
-
|
|
343
|
-
|
|
337
|
+
|
|
338
|
+
async connectAsync (idOrAddress, parameters) {
|
|
339
|
+
return new Promise((resolve, reject) => {
|
|
340
|
+
this.connect(idOrAddress, parameters, (error, peripheral) => error ? reject(error) : resolve(peripheral));
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
_onConnect (peripheralId, error) {
|
|
345
|
+
const peripheral = this._peripherals.get(peripheralId);
|
|
344
346
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
347
|
+
if (peripheral) {
|
|
348
|
+
// Emit a unique connect event for the specific peripheral
|
|
349
|
+
this.emit(`connect:${peripheralId}`, error);
|
|
348
350
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
351
|
+
peripheral.state = error ? 'error' : 'connected';
|
|
352
|
+
// Also emit the general 'connect' event for a peripheral
|
|
353
|
+
peripheral.emit('connect', error);
|
|
354
|
+
} else {
|
|
355
|
+
this.emit('warning', `unknown peripheral ${peripheralId} connected!`);
|
|
356
|
+
}
|
|
352
357
|
}
|
|
353
358
|
|
|
354
|
-
|
|
355
|
-
|
|
359
|
+
cancelConnect (idOrAddress, parameters) {
|
|
360
|
+
// Get the identifier for the peripheral
|
|
361
|
+
const identifier = this._getPeripheralId(idOrAddress);
|
|
362
|
+
|
|
363
|
+
// Check if the peripheral is connecting
|
|
364
|
+
const peripheral = this._peripherals.get(identifier);
|
|
365
|
+
if (peripheral && peripheral.state === 'connecting') {
|
|
366
|
+
peripheral.state = 'disconnected';
|
|
367
|
+
}
|
|
368
|
+
// Emit a unique connect event for the specific peripheral
|
|
369
|
+
this.emit(`connect:${identifier}`, new Error('connection canceled!'));
|
|
370
|
+
// Cancel the connection
|
|
371
|
+
this._bindings.cancelConnect(identifier, parameters);
|
|
356
372
|
}
|
|
357
|
-
// allocate internal service object and return
|
|
358
|
-
const serv = new Service(this, peripheralUuid, service.uuid);
|
|
359
373
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
374
|
+
disconnect (peripheralId) {
|
|
375
|
+
// Disconnect the peripheral
|
|
376
|
+
this._bindings.disconnect(peripheralId);
|
|
377
|
+
}
|
|
363
378
|
|
|
364
|
-
|
|
379
|
+
_onDisconnect (peripheralId, reason) {
|
|
380
|
+
const peripheral = this._peripherals.get(peripheralId);
|
|
365
381
|
|
|
366
|
-
|
|
367
|
-
|
|
382
|
+
if (peripheral) {
|
|
383
|
+
peripheral.state = 'disconnected';
|
|
384
|
+
peripheral.emit('disconnect', reason);
|
|
385
|
+
this.emit(`disconnect:${peripheralId}`, reason);
|
|
386
|
+
} else {
|
|
387
|
+
this.emit('warning', `unknown peripheral ${peripheralId} disconnected!`);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
368
390
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
391
|
+
updateRssi (peripheralId) {
|
|
392
|
+
this._bindings.updateRssi(peripheralId);
|
|
393
|
+
}
|
|
372
394
|
|
|
373
|
-
|
|
374
|
-
|
|
395
|
+
_onRssiUpdate (peripheralId, rssi, error) {
|
|
396
|
+
const peripheral = this._peripherals.get(peripheralId);
|
|
375
397
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
};
|
|
398
|
+
if (peripheral) {
|
|
399
|
+
peripheral.rssi = rssi;
|
|
379
400
|
|
|
380
|
-
|
|
381
|
-
|
|
401
|
+
peripheral.emit('rssiUpdate', rssi, error);
|
|
402
|
+
} else {
|
|
403
|
+
this.emit('warning', `unknown peripheral ${peripheralId} RSSI update!`);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
382
406
|
|
|
383
|
-
|
|
384
|
-
|
|
407
|
+
/// add an array of service objects (as retrieved via the servicesDiscovered event)
|
|
408
|
+
addServices (peripheralId, services) {
|
|
409
|
+
const servObjs = [];
|
|
385
410
|
|
|
386
|
-
for (let i = 0; i <
|
|
387
|
-
const
|
|
388
|
-
|
|
411
|
+
for (let i = 0; i < services.length; i++) {
|
|
412
|
+
const o = this.addService(peripheralId, services[i]);
|
|
413
|
+
servObjs.push(o);
|
|
414
|
+
}
|
|
415
|
+
return servObjs;
|
|
416
|
+
}
|
|
389
417
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
418
|
+
/// service is a ServiceObject { uuid, startHandle, endHandle,..}
|
|
419
|
+
addService (peripheralId, service) {
|
|
420
|
+
const peripheral = this._peripherals.get(peripheralId);
|
|
393
421
|
|
|
394
|
-
|
|
422
|
+
// pass on to lower layers (gatt)
|
|
423
|
+
if (this._bindings.addService) {
|
|
424
|
+
this._bindings.addService(peripheralId, service);
|
|
395
425
|
}
|
|
396
426
|
|
|
397
|
-
peripheral.services
|
|
427
|
+
if (!peripheral.services) {
|
|
428
|
+
peripheral.services = [];
|
|
429
|
+
}
|
|
430
|
+
// allocate internal service object and return
|
|
431
|
+
const serv = new Service(this, peripheralId, service.uuid);
|
|
398
432
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
this.
|
|
402
|
-
}
|
|
403
|
-
};
|
|
433
|
+
this._services[peripheralId][service.uuid] = serv;
|
|
434
|
+
this._characteristics[peripheralId][service.uuid] = {};
|
|
435
|
+
this._descriptors[peripheralId][service.uuid] = {};
|
|
404
436
|
|
|
405
|
-
|
|
406
|
-
this._bindings.discoverIncludedServices(peripheralUuid, serviceUuid, serviceUuids);
|
|
407
|
-
};
|
|
437
|
+
peripheral.services.push(serv);
|
|
408
438
|
|
|
409
|
-
|
|
410
|
-
|
|
439
|
+
return serv;
|
|
440
|
+
}
|
|
411
441
|
|
|
412
|
-
|
|
413
|
-
|
|
442
|
+
/// callback receiving a list of service objects from the gatt layer
|
|
443
|
+
_onServicesDiscovered (peripheralId, services) {
|
|
444
|
+
const peripheral = this._peripherals.get(peripheralId);
|
|
414
445
|
|
|
415
|
-
|
|
416
|
-
} else {
|
|
417
|
-
this.emit('warning', `unknown peripheral ${peripheralUuid}, ${serviceUuid} included services discover!`);
|
|
446
|
+
if (peripheral) { peripheral.emit('servicesDiscovered', peripheral, services); } // pass on to higher layers
|
|
418
447
|
}
|
|
419
|
-
};
|
|
420
448
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
// first, initialize gatt layer:
|
|
424
|
-
if (this._bindings.addCharacteristics) {
|
|
425
|
-
this._bindings.addCharacteristics(peripheralUuid, serviceUuid, characteristics);
|
|
449
|
+
discoverServices (peripheralId, uuids) {
|
|
450
|
+
this._bindings.discoverServices(peripheralId, uuids);
|
|
426
451
|
}
|
|
427
452
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
453
|
+
_onServicesDiscover (peripheralId, serviceUuids, error) {
|
|
454
|
+
const peripheral = this._peripherals.get(peripheralId);
|
|
455
|
+
|
|
456
|
+
if (peripheral) {
|
|
457
|
+
const services = [];
|
|
433
458
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
459
|
+
for (let i = 0; i < serviceUuids.length; i++) {
|
|
460
|
+
const serviceUuid = serviceUuids[i];
|
|
461
|
+
const service = new Service(this, peripheralId, serviceUuid);
|
|
437
462
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
serviceUuid,
|
|
442
|
-
characteristicUuid,
|
|
443
|
-
characteristics[i].properties
|
|
444
|
-
);
|
|
463
|
+
this._services[peripheralId][serviceUuid] = service;
|
|
464
|
+
this._characteristics[peripheralId][serviceUuid] = {};
|
|
465
|
+
this._descriptors[peripheralId][serviceUuid] = {};
|
|
445
466
|
|
|
446
|
-
|
|
447
|
-
|
|
467
|
+
services.push(service);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
peripheral.services = services;
|
|
471
|
+
|
|
472
|
+
peripheral.emit('servicesDiscover', services, error);
|
|
473
|
+
} else {
|
|
474
|
+
this.emit('warning', `unknown peripheral ${peripheralId} services discover!`);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
448
477
|
|
|
449
|
-
|
|
478
|
+
discoverIncludedServices (peripheralId, serviceUuid, serviceUuids) {
|
|
479
|
+
this._bindings.discoverIncludedServices(peripheralId, serviceUuid, serviceUuids);
|
|
450
480
|
}
|
|
451
|
-
service.characteristics = characteristics_;
|
|
452
|
-
return characteristics_;
|
|
453
|
-
};
|
|
454
481
|
|
|
455
|
-
|
|
456
|
-
|
|
482
|
+
_onIncludedServicesDiscover (peripheralId, serviceUuid, includedServiceUuids, error) {
|
|
483
|
+
const service = this._services[peripheralId][serviceUuid];
|
|
457
484
|
|
|
458
|
-
|
|
459
|
-
|
|
485
|
+
if (service) {
|
|
486
|
+
service.includedServiceUuids = includedServiceUuids;
|
|
460
487
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
};
|
|
488
|
+
service.emit('includedServicesDiscover', includedServiceUuids, error);
|
|
489
|
+
} else {
|
|
490
|
+
this.emit('warning', `unknown peripheral ${peripheralId}, ${serviceUuid} included services discover!`);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
464
493
|
|
|
465
|
-
|
|
466
|
-
|
|
494
|
+
/// add characteristics to the peripheral; returns an array of initialized Characteristics objects
|
|
495
|
+
addCharacteristics (peripheralId, serviceUuid, characteristics) {
|
|
496
|
+
// first, initialize gatt layer:
|
|
497
|
+
if (this._bindings.addCharacteristics) {
|
|
498
|
+
this._bindings.addCharacteristics(peripheralId, serviceUuid, characteristics);
|
|
499
|
+
}
|
|
467
500
|
|
|
468
|
-
|
|
469
|
-
|
|
501
|
+
const service = this._services[peripheralId][serviceUuid];
|
|
502
|
+
if (!service) {
|
|
503
|
+
this.emit('warning', `unknown service ${peripheralId}, ${serviceUuid} characteristics discover!`);
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
470
506
|
|
|
507
|
+
const characteristics_ = [];
|
|
471
508
|
for (let i = 0; i < characteristics.length; i++) {
|
|
472
509
|
const characteristicUuid = characteristics[i].uuid;
|
|
473
510
|
|
|
474
511
|
const characteristic = new Characteristic(
|
|
475
512
|
this,
|
|
476
|
-
|
|
513
|
+
peripheralId,
|
|
477
514
|
serviceUuid,
|
|
478
515
|
characteristicUuid,
|
|
479
516
|
characteristics[i].properties
|
|
480
517
|
);
|
|
481
518
|
|
|
482
|
-
this._characteristics[
|
|
483
|
-
this._descriptors[
|
|
519
|
+
this._characteristics[peripheralId][serviceUuid][characteristicUuid] = characteristic;
|
|
520
|
+
this._descriptors[peripheralId][serviceUuid][characteristicUuid] = {};
|
|
484
521
|
|
|
485
522
|
characteristics_.push(characteristic);
|
|
486
523
|
}
|
|
487
|
-
|
|
488
524
|
service.characteristics = characteristics_;
|
|
525
|
+
return characteristics_;
|
|
526
|
+
}
|
|
489
527
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
528
|
+
_onCharacteristicsDiscovered (peripheralId, serviceUuid, characteristics) {
|
|
529
|
+
const service = this._services[peripheralId][serviceUuid];
|
|
530
|
+
|
|
531
|
+
service.emit('characteristicsDiscovered', characteristics);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
discoverCharacteristics (peripheralId, serviceUuid, characteristicUuids) {
|
|
535
|
+
this._bindings.discoverCharacteristics(peripheralId, serviceUuid, characteristicUuids);
|
|
493
536
|
}
|
|
494
|
-
};
|
|
495
537
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
538
|
+
_onCharacteristicsDiscover (peripheralId, serviceUuid, characteristics, error) {
|
|
539
|
+
const service = this._services[peripheralId][serviceUuid];
|
|
540
|
+
|
|
541
|
+
if (service) {
|
|
542
|
+
const characteristics_ = [];
|
|
543
|
+
|
|
544
|
+
for (let i = 0; i < characteristics.length; i++) {
|
|
545
|
+
const characteristicUuid = characteristics[i].uuid;
|
|
546
|
+
|
|
547
|
+
const characteristic = new Characteristic(
|
|
548
|
+
this,
|
|
549
|
+
peripheralId,
|
|
550
|
+
serviceUuid,
|
|
551
|
+
characteristicUuid,
|
|
552
|
+
characteristics[i].properties
|
|
553
|
+
);
|
|
499
554
|
|
|
500
|
-
|
|
501
|
-
|
|
555
|
+
this._characteristics[peripheralId][serviceUuid][characteristicUuid] = characteristic;
|
|
556
|
+
this._descriptors[peripheralId][serviceUuid][characteristicUuid] = {};
|
|
502
557
|
|
|
503
|
-
|
|
504
|
-
|
|
558
|
+
characteristics_.push(characteristic);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
service.characteristics = characteristics_;
|
|
562
|
+
|
|
563
|
+
service.emit('characteristicsDiscover', characteristics_, error);
|
|
564
|
+
} else {
|
|
565
|
+
this.emit('warning', `unknown peripheral ${peripheralId}, ${serviceUuid} characteristics discover!`);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
505
568
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
this.emit('warning', `unknown peripheral ${peripheralUuid}, ${serviceUuid}, ${characteristicUuid} read!`);
|
|
569
|
+
read (peripheralId, serviceUuid, characteristicUuid) {
|
|
570
|
+
this._bindings.read(peripheralId, serviceUuid, characteristicUuid);
|
|
509
571
|
}
|
|
510
|
-
};
|
|
511
572
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
};
|
|
573
|
+
_onRead (peripheralId, serviceUuid, characteristicUuid, data, isNotification, error) {
|
|
574
|
+
const characteristic = this._characteristics[peripheralId][serviceUuid][characteristicUuid];
|
|
515
575
|
|
|
516
|
-
|
|
517
|
-
|
|
576
|
+
if (characteristic) {
|
|
577
|
+
characteristic.emit('read', data, isNotification, error);
|
|
578
|
+
} else {
|
|
579
|
+
this.emit('warning', `unknown peripheral ${peripheralId}, ${serviceUuid}, ${characteristicUuid} read!`);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
518
582
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
} else {
|
|
522
|
-
this.emit('warning', `unknown peripheral ${peripheralUuid}, ${serviceUuid}, ${characteristicUuid} write!`);
|
|
583
|
+
write (peripheralId, serviceUuid, characteristicUuid, data, withoutResponse) {
|
|
584
|
+
this._bindings.write(peripheralId, serviceUuid, characteristicUuid, data, withoutResponse);
|
|
523
585
|
}
|
|
524
|
-
};
|
|
525
586
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
};
|
|
587
|
+
_onWrite (peripheralId, serviceUuid, characteristicUuid, error) {
|
|
588
|
+
const characteristic = this._characteristics[peripheralId][serviceUuid][characteristicUuid];
|
|
529
589
|
|
|
530
|
-
|
|
531
|
-
|
|
590
|
+
if (characteristic) {
|
|
591
|
+
characteristic.emit('write', error);
|
|
592
|
+
} else {
|
|
593
|
+
this.emit('warning', `unknown peripheral ${peripheralId}, ${serviceUuid}, ${characteristicUuid} write!`);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
532
596
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
} else {
|
|
536
|
-
this.emit('warning', `unknown peripheral ${peripheralUuid}, ${serviceUuid}, ${characteristicUuid} broadcast!`);
|
|
597
|
+
broadcast (peripheralId, serviceUuid, characteristicUuid, broadcast) {
|
|
598
|
+
this._bindings.broadcast(peripheralId, serviceUuid, characteristicUuid, broadcast);
|
|
537
599
|
}
|
|
538
|
-
};
|
|
539
600
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
601
|
+
_onBroadcast (peripheralId, serviceUuid, characteristicUuid, state) {
|
|
602
|
+
const characteristic = this._characteristics[peripheralId][serviceUuid][characteristicUuid];
|
|
603
|
+
|
|
604
|
+
if (characteristic) {
|
|
605
|
+
characteristic.emit('broadcast', state);
|
|
606
|
+
} else {
|
|
607
|
+
this.emit('warning', `unknown peripheral ${peripheralId}, ${serviceUuid}, ${characteristicUuid} broadcast!`);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
543
610
|
|
|
544
|
-
|
|
545
|
-
|
|
611
|
+
notify (peripheralId, serviceUuid, characteristicUuid, notify) {
|
|
612
|
+
this._bindings.notify(peripheralId, serviceUuid, characteristicUuid, notify);
|
|
613
|
+
}
|
|
546
614
|
|
|
547
|
-
|
|
548
|
-
characteristic.
|
|
549
|
-
|
|
550
|
-
|
|
615
|
+
_onNotify (peripheralId, serviceUuid, characteristicUuid, state, error) {
|
|
616
|
+
const characteristic = this._characteristics[peripheralId][serviceUuid][characteristicUuid];
|
|
617
|
+
if (characteristic) {
|
|
618
|
+
characteristic.emit('notify', state, error);
|
|
619
|
+
} else {
|
|
620
|
+
this.emit('warning', `unknown peripheral ${peripheralId}, ${serviceUuid}, ${characteristicUuid} notify!`);
|
|
621
|
+
}
|
|
551
622
|
}
|
|
552
|
-
};
|
|
553
623
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
}
|
|
624
|
+
discoverDescriptors (peripheralId, serviceUuid, characteristicUuid) {
|
|
625
|
+
this._bindings.discoverDescriptors(peripheralId, serviceUuid, characteristicUuid);
|
|
626
|
+
}
|
|
557
627
|
|
|
558
|
-
|
|
559
|
-
|
|
628
|
+
_onDescriptorsDiscover (peripheralId, serviceUuid, characteristicUuid, descriptors, error) {
|
|
629
|
+
const characteristic = this._characteristics[peripheralId][serviceUuid][characteristicUuid];
|
|
560
630
|
|
|
561
|
-
|
|
562
|
-
|
|
631
|
+
if (characteristic) {
|
|
632
|
+
const descriptors_ = [];
|
|
563
633
|
|
|
564
|
-
|
|
565
|
-
|
|
634
|
+
for (let i = 0; i < descriptors.length; i++) {
|
|
635
|
+
const descriptorUuid = descriptors[i];
|
|
566
636
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
637
|
+
const descriptor = new Descriptor(
|
|
638
|
+
this,
|
|
639
|
+
peripheralId,
|
|
640
|
+
serviceUuid,
|
|
641
|
+
characteristicUuid,
|
|
642
|
+
descriptorUuid
|
|
643
|
+
);
|
|
574
644
|
|
|
575
|
-
|
|
645
|
+
this._descriptors[peripheralId][serviceUuid][characteristicUuid][descriptorUuid] = descriptor;
|
|
576
646
|
|
|
577
|
-
|
|
578
|
-
|
|
647
|
+
descriptors_.push(descriptor);
|
|
648
|
+
}
|
|
579
649
|
|
|
580
|
-
|
|
650
|
+
characteristic.descriptors = descriptors_;
|
|
581
651
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
652
|
+
characteristic.emit('descriptorsDiscover', descriptors_, error);
|
|
653
|
+
} else {
|
|
654
|
+
this.emit('warning', `unknown peripheral ${peripheralId}, ${serviceUuid}, ${characteristicUuid} descriptors discover!`);
|
|
655
|
+
}
|
|
585
656
|
}
|
|
586
|
-
};
|
|
587
657
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
}
|
|
658
|
+
readValue (peripheralId, serviceUuid, characteristicUuid, descriptorUuid) {
|
|
659
|
+
this._bindings.readValue(peripheralId, serviceUuid, characteristicUuid, descriptorUuid);
|
|
660
|
+
}
|
|
591
661
|
|
|
592
|
-
|
|
593
|
-
|
|
662
|
+
_onValueRead (peripheralId, serviceUuid, characteristicUuid, descriptorUuid, data, error) {
|
|
663
|
+
const descriptor = this._descriptors[peripheralId][serviceUuid][characteristicUuid][descriptorUuid];
|
|
594
664
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
665
|
+
if (descriptor) {
|
|
666
|
+
descriptor.emit('valueRead', data, error);
|
|
667
|
+
} else {
|
|
668
|
+
this.emit('warning', `unknown peripheral ${peripheralId}, ${serviceUuid}, ${characteristicUuid}, ${descriptorUuid} value read!`);
|
|
669
|
+
}
|
|
599
670
|
}
|
|
600
|
-
};
|
|
601
671
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
}
|
|
672
|
+
writeValue (peripheralId, serviceUuid, characteristicUuid, descriptorUuid, data) {
|
|
673
|
+
this._bindings.writeValue(peripheralId, serviceUuid, characteristicUuid, descriptorUuid, data);
|
|
674
|
+
}
|
|
605
675
|
|
|
606
|
-
|
|
607
|
-
|
|
676
|
+
_onValueWrite (peripheralId, serviceUuid, characteristicUuid, descriptorUuid, error) {
|
|
677
|
+
const descriptor = this._descriptors[peripheralId][serviceUuid][characteristicUuid][descriptorUuid];
|
|
608
678
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
679
|
+
if (descriptor) {
|
|
680
|
+
descriptor.emit('valueWrite', error);
|
|
681
|
+
} else {
|
|
682
|
+
this.emit('warning', `unknown peripheral ${peripheralId}, ${serviceUuid}, ${characteristicUuid}, ${descriptorUuid} value write!`);
|
|
683
|
+
}
|
|
613
684
|
}
|
|
614
|
-
};
|
|
615
685
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
}
|
|
686
|
+
readHandle (peripheralId, handle) {
|
|
687
|
+
this._bindings.readHandle(peripheralId, handle);
|
|
688
|
+
}
|
|
619
689
|
|
|
620
|
-
|
|
621
|
-
|
|
690
|
+
_onHandleRead (peripheralId, handle, data, error) {
|
|
691
|
+
const peripheral = this._peripherals.get(peripheralId);
|
|
622
692
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
693
|
+
if (peripheral) {
|
|
694
|
+
peripheral.emit(`handleRead${handle}`, data, error);
|
|
695
|
+
} else {
|
|
696
|
+
this.emit('warning', `unknown peripheral ${peripheralId} handle read!`);
|
|
697
|
+
}
|
|
627
698
|
}
|
|
628
|
-
};
|
|
629
699
|
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
}
|
|
700
|
+
writeHandle (peripheralId, handle, data, withoutResponse) {
|
|
701
|
+
this._bindings.writeHandle(peripheralId, handle, data, withoutResponse);
|
|
702
|
+
}
|
|
633
703
|
|
|
634
|
-
|
|
635
|
-
|
|
704
|
+
_onHandleWrite (peripheralId, handle, error) {
|
|
705
|
+
const peripheral = this._peripherals.get(peripheralId);
|
|
636
706
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
707
|
+
if (peripheral) {
|
|
708
|
+
peripheral.emit(`handleWrite${handle}`, error);
|
|
709
|
+
} else {
|
|
710
|
+
this.emit('warning', `unknown peripheral ${peripheralId} handle write!`);
|
|
711
|
+
}
|
|
641
712
|
}
|
|
642
|
-
};
|
|
643
713
|
|
|
644
|
-
|
|
645
|
-
|
|
714
|
+
_onHandleNotify (peripheralId, handle, data, error) {
|
|
715
|
+
const peripheral = this._peripherals.get(peripheralId);
|
|
646
716
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
717
|
+
if (peripheral) {
|
|
718
|
+
peripheral.emit('handleNotify', handle, data, error);
|
|
719
|
+
} else {
|
|
720
|
+
this.emit('warning', `unknown peripheral ${peripheralId} handle notify!`);
|
|
721
|
+
}
|
|
651
722
|
}
|
|
652
|
-
};
|
|
653
723
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
724
|
+
_onMtu (peripheralId, mtu) {
|
|
725
|
+
const peripheral = this._peripherals.get(peripheralId);
|
|
726
|
+
if (peripheral && mtu) {
|
|
727
|
+
peripheral.mtu = mtu;
|
|
728
|
+
peripheral.emit('mtu', mtu);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
async _withDisconnectHandler (peripheralId, operation) {
|
|
733
|
+
return new Promise((resolve, reject) => {
|
|
734
|
+
const disconnectListener = error => reject(error);
|
|
735
|
+
this.once(`disconnect:${peripheralId}`, disconnectListener);
|
|
736
|
+
|
|
737
|
+
Promise.resolve(operation())
|
|
738
|
+
.then(result => {
|
|
739
|
+
this.removeListener(`disconnect:${peripheralId}`, disconnectListener);
|
|
740
|
+
resolve(result);
|
|
741
|
+
})
|
|
742
|
+
.catch(error => {
|
|
743
|
+
this.removeListener(`disconnect:${peripheralId}`, disconnectListener);
|
|
744
|
+
reject(error);
|
|
745
|
+
});
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
}
|
|
658
749
|
|
|
659
750
|
module.exports = Noble;
|