@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.
Files changed (97) hide show
  1. package/README.md +393 -650
  2. package/examples/advertisement-discovery.js +57 -48
  3. package/examples/connect-address.js +59 -34
  4. package/examples/echo.js +59 -69
  5. package/examples/enter-exit.js +55 -49
  6. package/examples/multiple-bindings.js +53 -0
  7. package/examples/peripheral-explorer-async.js +39 -21
  8. package/examples/peripheral-explorer.ts +52 -0
  9. package/index.d.ts +249 -209
  10. package/index.js +4 -1
  11. package/jest.config.js +4 -0
  12. package/lib/characteristic.js +153 -127
  13. package/lib/{win/src/callbacks.h → common/include/Emit.h} +17 -14
  14. package/lib/common/include/Peripheral.h +31 -0
  15. package/lib/common/include/ThreadSafeCallback.h +95 -0
  16. package/lib/{win/src/callbacks.cc → common/src/Emit.cc} +111 -68
  17. package/lib/descriptor.js +57 -54
  18. package/lib/hci-socket/acl-stream.js +2 -4
  19. package/lib/hci-socket/bindings.js +96 -73
  20. package/lib/hci-socket/gap.js +2 -3
  21. package/lib/hci-socket/gatt.js +2 -5
  22. package/lib/hci-socket/hci.js +19 -7
  23. package/lib/hci-socket/signaling.js +2 -3
  24. package/lib/hci-socket/smp.js +2 -3
  25. package/lib/hci-socket/vs.js +1 -0
  26. package/lib/mac/binding.gyp +5 -7
  27. package/lib/mac/bindings.js +1 -3
  28. package/lib/mac/src/ble_manager.h +1 -8
  29. package/lib/mac/src/ble_manager.mm +87 -44
  30. package/lib/mac/src/napi_objc.h +1 -0
  31. package/lib/mac/src/napi_objc.mm +0 -6
  32. package/lib/mac/src/noble_mac.h +5 -3
  33. package/lib/mac/src/noble_mac.mm +99 -57
  34. package/lib/mac/src/objc_cpp.h +3 -2
  35. package/lib/mac/src/objc_cpp.mm +0 -6
  36. package/lib/noble.js +579 -488
  37. package/lib/peripheral.js +171 -174
  38. package/lib/resolve-bindings.js +37 -30
  39. package/lib/service.js +58 -55
  40. package/lib/win/binding.gyp +4 -11
  41. package/lib/win/bindings.js +1 -3
  42. package/lib/win/src/ble_manager.cc +291 -166
  43. package/lib/win/src/ble_manager.h +11 -13
  44. package/lib/win/src/napi_winrt.cc +1 -7
  45. package/lib/win/src/napi_winrt.h +1 -1
  46. package/lib/win/src/noble_winrt.cc +88 -61
  47. package/lib/win/src/noble_winrt.h +5 -3
  48. package/lib/win/src/notify_map.cc +0 -7
  49. package/lib/win/src/notify_map.h +1 -8
  50. package/lib/win/src/peripheral_winrt.cc +29 -11
  51. package/lib/win/src/peripheral_winrt.h +1 -1
  52. package/lib/win/src/radio_watcher.cc +79 -69
  53. package/lib/win/src/radio_watcher.h +30 -11
  54. package/lib/win/src/winrt_cpp.cc +1 -1
  55. package/lib/win/src/winrt_cpp.h +3 -0
  56. package/package.json +14 -17
  57. package/prebuilds/darwin-x64+arm64/@stoprocent+noble.node +0 -0
  58. package/prebuilds/win32-ia32/@stoprocent+noble.node +0 -0
  59. package/prebuilds/win32-x64/@stoprocent+noble.node +0 -0
  60. package/test/lib/characteristic.test.js +202 -322
  61. package/test/lib/descriptor.test.js +62 -95
  62. package/test/lib/hci-socket/acl-stream.test.js +112 -108
  63. package/test/lib/hci-socket/bindings.test.js +576 -365
  64. package/test/lib/hci-socket/hci.test.js +442 -473
  65. package/test/lib/hci-socket/signaling.test.js +45 -48
  66. package/test/lib/hci-socket/smp.test.js +144 -142
  67. package/test/lib/hci-socket/vs.test.js +193 -18
  68. package/test/lib/peripheral.test.js +492 -322
  69. package/test/lib/resolve-bindings.test.js +207 -82
  70. package/test/lib/service.test.js +79 -88
  71. package/test/noble.test.js +381 -1085
  72. package/.editorconfig +0 -11
  73. package/.nycrc.json +0 -4
  74. package/codecov.yml +0 -5
  75. package/examples/cache-gatt-discovery.js +0 -198
  76. package/examples/cache-gatt-reconnect.js +0 -164
  77. package/examples/ext-advertisement-discovery.js +0 -65
  78. package/examples/peripheral-explorer.js +0 -225
  79. package/examples/pizza/central.js +0 -194
  80. package/examples/pizza/pizza.js +0 -60
  81. package/examples/test/test.custom.js +0 -131
  82. package/examples/uart-bind-params.js +0 -28
  83. package/lib/distributed/bindings.js +0 -326
  84. package/lib/mac/src/callbacks.cc +0 -222
  85. package/lib/mac/src/callbacks.h +0 -84
  86. package/lib/mac/src/peripheral.h +0 -23
  87. package/lib/resolve-bindings-web.js +0 -9
  88. package/lib/webbluetooth/bindings.js +0 -368
  89. package/lib/websocket/bindings.js +0 -321
  90. package/lib/win/src/peripheral.h +0 -23
  91. package/test/lib/distributed/bindings.test.js +0 -918
  92. package/test/lib/webbluetooth/bindings.test.js +0 -190
  93. package/test/lib/websocket/bindings.test.js +0 -456
  94. package/test/mocha.setup.js +0 -0
  95. package/with-bindings.js +0 -5
  96. package/with-custom-binding.js +0 -6
  97. 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 events = require('events');
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
- function Noble (bindings) {
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
- this._cleanupPeriperals();
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
- // or lazy init bindings if someone attempts to get state first
67
- Object.defineProperties(this, {
68
- state: {
69
- get: function () {
70
- if (!this.initialized) {
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
- Noble.prototype.onStateChange = function (state) {
91
- debug(`stateChange ${state}`);
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
- Noble.prototype.onAddressChange = function (address) {
103
- debug(`addressChange ${address}`);
29
+ this.on('warning', message => console.warn(`noble: ${message}`));
104
30
 
105
- this.address = address;
106
- };
107
-
108
- Noble.prototype.setScanParameters = function (interval, window, callback) {
109
- if (callback) {
110
- this.once('scanParametersSet', callback);
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
- Noble.prototype.onScanParametersSet = function () {
116
- debug('scanParametersSet');
117
- this.emit('scanParametersSet');
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
- Noble.prototype.setAddress = function (address) {
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
- let listener;
135
- listener = (state) => {
136
- clearTimeout(timeoutId);
137
- if (state === 'poweredOn') {
138
- resolve();
139
- } else {
140
- this.once('stateChange', listener);
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
- this.once('stateChange', listener);
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
- if (callback) {
168
- this.once('scanStart', filterDuplicates => {
169
- callback(null, filterDuplicates);
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
- // if bindings still not init, do it now
181
- if (!this.initialized) {
182
- this.initialized = true;
125
+ _onStateChange (state) {
126
+ debug(`stateChange ${state}`);
183
127
 
184
- this._bindings.init();
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.once('stateChange', scan.bind(this));
187
- } else {
188
- scan.call(this, this._state);
133
+ this._state = state;
134
+ this.emit('stateChange', state);
189
135
  }
190
- };
191
136
 
192
- Noble.prototype.startScanning = startScanning;
193
- Noble.prototype.startScanningAsync = function (serviceUUIDs, allowDuplicates) {
194
- return util.promisify((callback) => this.startScanning(serviceUUIDs, allowDuplicates, callback))();
195
- };
137
+ _onAddressChange (address) {
138
+ debug(`addressChange ${address}`);
139
+ this._address = address;
140
+ this.emit('addressChange', address);
141
+ }
196
142
 
197
- Noble.prototype.onScanStart = function (filterDuplicates) {
198
- debug('scanStart');
199
- this.emit('scanStart', filterDuplicates);
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
- const stopScanning = function (callback) {
203
- if (callback) {
204
- this.once('scanStop', callback);
150
+ _onScanParametersSet () {
151
+ debug('scanParametersSet');
152
+ this.emit('scanParametersSet');
205
153
  }
206
- if (this._bindings && this.initialized) {
207
- this._bindings.stopScanning();
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
- Noble.prototype.stopScanning = stopScanning;
212
- Noble.prototype.stopScanningAsync = util.promisify(stopScanning);
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
- Noble.prototype.onScanStop = function () {
215
- debug('scanStop');
216
- this.emit('scanStop');
217
- };
218
-
219
- Noble.prototype.reset = function () {
220
- if (this._bindings.reset) {
221
- this._bindings.reset();
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
- Noble.prototype.stop = function () {
226
- if (this._bindings.stop) {
227
- this._bindings.stop();
178
+ this.once('stateChange', listener);
179
+ });
228
180
  }
229
- };
230
181
 
231
- Noble.prototype.onDiscover = function (uuid, address, addressType, connectable, advertisement, rssi, scannable) {
232
- let peripheral = this._peripherals[uuid];
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
- if (!peripheral) {
235
- peripheral = new Peripheral(this, uuid, address, addressType, connectable, advertisement, rssi, scannable);
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
- this._peripherals[uuid] = peripheral;
238
- this._services[uuid] = {};
239
- this._characteristics[uuid] = {};
240
- this._descriptors[uuid] = {};
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
- const previouslyDiscoverd = this._discoveredPeripheralUUids[uuid] === true;
255
-
256
- if (!previouslyDiscoverd) {
257
- this._discoveredPeripheralUUids[uuid] = true;
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
- if (this._allowDuplicates || !previouslyDiscoverd || (!scannable && !connectable)) {
261
- this.emit('discover', peripheral);
220
+ _onScanStart (filterDuplicates) {
221
+ debug('scanStart');
222
+ this.emit('scanStart', filterDuplicates);
262
223
  }
263
- };
264
224
 
265
- Noble.prototype.connect = function (peripheralUuid, parameters, callback) {
266
- // Check if callback is a function
267
- if (typeof callback === 'function') {
268
- // Create a unique event name using the peripheral UUID
269
- const eventName = `connect:${peripheralUuid}`;
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
- // Add a one-time listener for this specific event
272
- this.once(eventName, (error) => {
273
- callback(error, this._peripherals[peripheralUuid]);
236
+ async stopScanningAsync () {
237
+ return new Promise((resolve, reject) => {
238
+ this.stopScanning(error => error ? reject(error) : resolve());
274
239
  });
275
240
  }
276
241
 
277
- // Proceed to initiate the connection
278
- this._bindings.connect(peripheralUuid, parameters);
279
- };
280
- Noble.prototype.connectAsync = function (peripheralUuid, parameters) {
281
- return util.promisify((callback) => this.connect(peripheralUuid, parameters, callback))();
282
- };
283
-
284
- Noble.prototype.onConnect = function (peripheralUuid, error) {
285
- const peripheral = this._peripherals[peripheralUuid];
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
- if (peripheral) {
288
- // Emit a unique connect event for the specific peripheral
289
- this.emit(`connect:${peripheralUuid}`, error);
265
+ await this.stopScanningAsync();
266
+ }
290
267
 
291
- peripheral.state = error ? 'error' : 'connected';
292
- // Also emit the general 'connect' event for a peripheral
293
- peripheral.emit('connect', error);
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
- Noble.prototype.cancelConnect = function (peripheralUuid, parameters) {
300
- this._bindings.cancelConnect(peripheralUuid, parameters);
301
- };
273
+ reset () {
274
+ if (typeof this._bindings.reset !== 'function') { return; }
275
+ this._bindings.reset();
276
+ }
302
277
 
303
- Noble.prototype.disconnect = function (peripheralUuid) {
304
- this._bindings.disconnect(peripheralUuid);
305
- };
278
+ stop () {
279
+ if (typeof this._bindings.stop !== 'function') { return; }
280
+ this._bindings.stop();
281
+ }
306
282
 
307
- Noble.prototype.onDisconnect = function (peripheralUuid, reason) {
308
- const peripheral = this._peripherals[peripheralUuid];
283
+ _onDiscover (uuid, address, addressType, connectable, advertisement, rssi, scannable) {
284
+ let peripheral = this._peripherals.get(uuid);
309
285
 
310
- if (peripheral) {
311
- peripheral.state = 'disconnected';
312
- peripheral.emit('disconnect', reason);
313
- } else {
314
- this.emit('warning', `unknown peripheral ${peripheralUuid} disconnected!`);
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
- Noble.prototype.updateRssi = function (peripheralUuid) {
319
- this._bindings.updateRssi(peripheralUuid);
320
- };
296
+ peripheral.connectable = connectable;
297
+ peripheral.scannable = scannable;
298
+ peripheral.rssi = rssi;
299
+ }
321
300
 
322
- Noble.prototype.onRssiUpdate = function (peripheralUuid, rssi) {
323
- const peripheral = this._peripherals[peripheralUuid];
301
+ const previouslyDiscoverd = this._discoveredPeripherals.has(uuid);
324
302
 
325
- if (peripheral) {
326
- peripheral.rssi = rssi;
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
- peripheral.emit('rssiUpdate', rssi);
329
- } else {
330
- this.emit('warning', `unknown peripheral ${peripheralUuid} RSSI update!`);
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
- /// add an array of service objects (as retrieved via the servicesDiscovered event)
335
- Noble.prototype.addServices = function (peripheralUuid, services) {
336
- const servObjs = [];
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
- for (let i = 0; i < services.length; i++) {
339
- const o = this.addService(peripheralUuid, services[i]);
340
- servObjs.push(o);
334
+ // Proceed to initiate the connection
335
+ this._bindings.connect(identifier, parameters);
341
336
  }
342
- return servObjs;
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
- /// service is a ServiceObject { uuid, startHandle, endHandle,..}
346
- Noble.prototype.addService = function (peripheralUuid, service) {
347
- const peripheral = this._peripherals[peripheralUuid];
347
+ if (peripheral) {
348
+ // Emit a unique connect event for the specific peripheral
349
+ this.emit(`connect:${peripheralId}`, error);
348
350
 
349
- // pass on to lower layers (gatt)
350
- if (this._bindings.addService) {
351
- this._bindings.addService(peripheralUuid, service);
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
- if (!peripheral.services) {
355
- peripheral.services = [];
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
- this._services[peripheralUuid][service.uuid] = serv;
361
- this._characteristics[peripheralUuid][service.uuid] = {};
362
- this._descriptors[peripheralUuid][service.uuid] = {};
374
+ disconnect (peripheralId) {
375
+ // Disconnect the peripheral
376
+ this._bindings.disconnect(peripheralId);
377
+ }
363
378
 
364
- peripheral.services.push(serv);
379
+ _onDisconnect (peripheralId, reason) {
380
+ const peripheral = this._peripherals.get(peripheralId);
365
381
 
366
- return serv;
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
- /// callback receiving a list of service objects from the gatt layer
370
- Noble.prototype.onServicesDiscovered = function (peripheralUuid, services) {
371
- const peripheral = this._peripherals[peripheralUuid];
391
+ updateRssi (peripheralId) {
392
+ this._bindings.updateRssi(peripheralId);
393
+ }
372
394
 
373
- if (peripheral) { peripheral.emit('servicesDiscovered', peripheral, services); } // pass on to higher layers
374
- };
395
+ _onRssiUpdate (peripheralId, rssi, error) {
396
+ const peripheral = this._peripherals.get(peripheralId);
375
397
 
376
- Noble.prototype.discoverServices = function (peripheralUuid, uuids) {
377
- this._bindings.discoverServices(peripheralUuid, uuids);
378
- };
398
+ if (peripheral) {
399
+ peripheral.rssi = rssi;
379
400
 
380
- Noble.prototype.onServicesDiscover = function (peripheralUuid, serviceUuids) {
381
- const peripheral = this._peripherals[peripheralUuid];
401
+ peripheral.emit('rssiUpdate', rssi, error);
402
+ } else {
403
+ this.emit('warning', `unknown peripheral ${peripheralId} RSSI update!`);
404
+ }
405
+ }
382
406
 
383
- if (peripheral) {
384
- const services = [];
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 < serviceUuids.length; i++) {
387
- const serviceUuid = serviceUuids[i];
388
- const service = new Service(this, peripheralUuid, serviceUuid);
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
- this._services[peripheralUuid][serviceUuid] = service;
391
- this._characteristics[peripheralUuid][serviceUuid] = {};
392
- this._descriptors[peripheralUuid][serviceUuid] = {};
418
+ /// service is a ServiceObject { uuid, startHandle, endHandle,..}
419
+ addService (peripheralId, service) {
420
+ const peripheral = this._peripherals.get(peripheralId);
393
421
 
394
- services.push(service);
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 = 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
- peripheral.emit('servicesDiscover', services);
400
- } else {
401
- this.emit('warning', `unknown peripheral ${peripheralUuid} services discover!`);
402
- }
403
- };
433
+ this._services[peripheralId][service.uuid] = serv;
434
+ this._characteristics[peripheralId][service.uuid] = {};
435
+ this._descriptors[peripheralId][service.uuid] = {};
404
436
 
405
- Noble.prototype.discoverIncludedServices = function (peripheralUuid, serviceUuid, serviceUuids) {
406
- this._bindings.discoverIncludedServices(peripheralUuid, serviceUuid, serviceUuids);
407
- };
437
+ peripheral.services.push(serv);
408
438
 
409
- Noble.prototype.onIncludedServicesDiscover = function (peripheralUuid, serviceUuid, includedServiceUuids) {
410
- const service = this._services[peripheralUuid][serviceUuid];
439
+ return serv;
440
+ }
411
441
 
412
- if (service) {
413
- service.includedServiceUuids = includedServiceUuids;
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
- service.emit('includedServicesDiscover', includedServiceUuids);
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
- /// add characteristics to the peripheral; returns an array of initialized Characteristics objects
422
- Noble.prototype.addCharacteristics = function (peripheralUuid, serviceUuid, characteristics) {
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
- const service = this._services[peripheralUuid][serviceUuid];
429
- if (!service) {
430
- this.emit('warning', `unknown service ${peripheralUuid}, ${serviceUuid} characteristics discover!`);
431
- return;
432
- }
453
+ _onServicesDiscover (peripheralId, serviceUuids, error) {
454
+ const peripheral = this._peripherals.get(peripheralId);
455
+
456
+ if (peripheral) {
457
+ const services = [];
433
458
 
434
- const characteristics_ = [];
435
- for (let i = 0; i < characteristics.length; i++) {
436
- const characteristicUuid = characteristics[i].uuid;
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
- const characteristic = new Characteristic(
439
- this,
440
- peripheralUuid,
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
- this._characteristics[peripheralUuid][serviceUuid][characteristicUuid] = characteristic;
447
- this._descriptors[peripheralUuid][serviceUuid][characteristicUuid] = {};
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
- characteristics_.push(characteristic);
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
- Noble.prototype.onCharacteristicsDiscovered = function (peripheralUuid, serviceUuid, characteristics) {
456
- const service = this._services[peripheralUuid][serviceUuid];
482
+ _onIncludedServicesDiscover (peripheralId, serviceUuid, includedServiceUuids, error) {
483
+ const service = this._services[peripheralId][serviceUuid];
457
484
 
458
- service.emit('characteristicsDiscovered', characteristics);
459
- };
485
+ if (service) {
486
+ service.includedServiceUuids = includedServiceUuids;
460
487
 
461
- Noble.prototype.discoverCharacteristics = function (peripheralUuid, serviceUuid, characteristicUuids) {
462
- this._bindings.discoverCharacteristics(peripheralUuid, serviceUuid, characteristicUuids);
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
- Noble.prototype.onCharacteristicsDiscover = function (peripheralUuid, serviceUuid, characteristics) {
466
- const service = this._services[peripheralUuid][serviceUuid];
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
- if (service) {
469
- const characteristics_ = [];
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
- peripheralUuid,
513
+ peripheralId,
477
514
  serviceUuid,
478
515
  characteristicUuid,
479
516
  characteristics[i].properties
480
517
  );
481
518
 
482
- this._characteristics[peripheralUuid][serviceUuid][characteristicUuid] = characteristic;
483
- this._descriptors[peripheralUuid][serviceUuid][characteristicUuid] = {};
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
- service.emit('characteristicsDiscover', characteristics_);
491
- } else {
492
- this.emit('warning', `unknown peripheral ${peripheralUuid}, ${serviceUuid} characteristics discover!`);
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
- Noble.prototype.read = function (peripheralUuid, serviceUuid, characteristicUuid) {
497
- this._bindings.read(peripheralUuid, serviceUuid, characteristicUuid);
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
- Noble.prototype.onRead = function (peripheralUuid, serviceUuid, characteristicUuid, data, isNotification) {
501
- const characteristic = this._characteristics[peripheralUuid][serviceUuid][characteristicUuid];
555
+ this._characteristics[peripheralId][serviceUuid][characteristicUuid] = characteristic;
556
+ this._descriptors[peripheralId][serviceUuid][characteristicUuid] = {};
502
557
 
503
- if (characteristic) {
504
- characteristic.emit('data', data, isNotification);
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
- characteristic.emit('read', data, isNotification); // for backwards compatbility
507
- } else {
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
- Noble.prototype.write = function (peripheralUuid, serviceUuid, characteristicUuid, data, withoutResponse) {
513
- this._bindings.write(peripheralUuid, serviceUuid, characteristicUuid, data, withoutResponse);
514
- };
573
+ _onRead (peripheralId, serviceUuid, characteristicUuid, data, isNotification, error) {
574
+ const characteristic = this._characteristics[peripheralId][serviceUuid][characteristicUuid];
515
575
 
516
- Noble.prototype.onWrite = function (peripheralUuid, serviceUuid, characteristicUuid) {
517
- const characteristic = this._characteristics[peripheralUuid][serviceUuid][characteristicUuid];
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
- if (characteristic) {
520
- characteristic.emit('write');
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
- Noble.prototype.broadcast = function (peripheralUuid, serviceUuid, characteristicUuid, broadcast) {
527
- this._bindings.broadcast(peripheralUuid, serviceUuid, characteristicUuid, broadcast);
528
- };
587
+ _onWrite (peripheralId, serviceUuid, characteristicUuid, error) {
588
+ const characteristic = this._characteristics[peripheralId][serviceUuid][characteristicUuid];
529
589
 
530
- Noble.prototype.onBroadcast = function (peripheralUuid, serviceUuid, characteristicUuid, state) {
531
- const characteristic = this._characteristics[peripheralUuid][serviceUuid][characteristicUuid];
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
- if (characteristic) {
534
- characteristic.emit('broadcast', state);
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
- Noble.prototype.notify = function (peripheralUuid, serviceUuid, characteristicUuid, notify) {
541
- this._bindings.notify(peripheralUuid, serviceUuid, characteristicUuid, notify);
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
- Noble.prototype.onNotify = function (peripheralUuid, serviceUuid, characteristicUuid, state) {
545
- const characteristic = this._characteristics[peripheralUuid][serviceUuid][characteristicUuid];
611
+ notify (peripheralId, serviceUuid, characteristicUuid, notify) {
612
+ this._bindings.notify(peripheralId, serviceUuid, characteristicUuid, notify);
613
+ }
546
614
 
547
- if (characteristic) {
548
- characteristic.emit('notify', state);
549
- } else {
550
- this.emit('warning', `unknown peripheral ${peripheralUuid}, ${serviceUuid}, ${characteristicUuid} notify!`);
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
- Noble.prototype.discoverDescriptors = function (peripheralUuid, serviceUuid, characteristicUuid) {
555
- this._bindings.discoverDescriptors(peripheralUuid, serviceUuid, characteristicUuid);
556
- };
624
+ discoverDescriptors (peripheralId, serviceUuid, characteristicUuid) {
625
+ this._bindings.discoverDescriptors(peripheralId, serviceUuid, characteristicUuid);
626
+ }
557
627
 
558
- Noble.prototype.onDescriptorsDiscover = function (peripheralUuid, serviceUuid, characteristicUuid, descriptors) {
559
- const characteristic = this._characteristics[peripheralUuid][serviceUuid][characteristicUuid];
628
+ _onDescriptorsDiscover (peripheralId, serviceUuid, characteristicUuid, descriptors, error) {
629
+ const characteristic = this._characteristics[peripheralId][serviceUuid][characteristicUuid];
560
630
 
561
- if (characteristic) {
562
- const descriptors_ = [];
631
+ if (characteristic) {
632
+ const descriptors_ = [];
563
633
 
564
- for (let i = 0; i < descriptors.length; i++) {
565
- const descriptorUuid = descriptors[i];
634
+ for (let i = 0; i < descriptors.length; i++) {
635
+ const descriptorUuid = descriptors[i];
566
636
 
567
- const descriptor = new Descriptor(
568
- this,
569
- peripheralUuid,
570
- serviceUuid,
571
- characteristicUuid,
572
- descriptorUuid
573
- );
637
+ const descriptor = new Descriptor(
638
+ this,
639
+ peripheralId,
640
+ serviceUuid,
641
+ characteristicUuid,
642
+ descriptorUuid
643
+ );
574
644
 
575
- this._descriptors[peripheralUuid][serviceUuid][characteristicUuid][descriptorUuid] = descriptor;
645
+ this._descriptors[peripheralId][serviceUuid][characteristicUuid][descriptorUuid] = descriptor;
576
646
 
577
- descriptors_.push(descriptor);
578
- }
647
+ descriptors_.push(descriptor);
648
+ }
579
649
 
580
- characteristic.descriptors = descriptors_;
650
+ characteristic.descriptors = descriptors_;
581
651
 
582
- characteristic.emit('descriptorsDiscover', descriptors_);
583
- } else {
584
- this.emit('warning', `unknown peripheral ${peripheralUuid}, ${serviceUuid}, ${characteristicUuid} descriptors discover!`);
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
- Noble.prototype.readValue = function (peripheralUuid, serviceUuid, characteristicUuid, descriptorUuid) {
589
- this._bindings.readValue(peripheralUuid, serviceUuid, characteristicUuid, descriptorUuid);
590
- };
658
+ readValue (peripheralId, serviceUuid, characteristicUuid, descriptorUuid) {
659
+ this._bindings.readValue(peripheralId, serviceUuid, characteristicUuid, descriptorUuid);
660
+ }
591
661
 
592
- Noble.prototype.onValueRead = function (peripheralUuid, serviceUuid, characteristicUuid, descriptorUuid, data) {
593
- const descriptor = this._descriptors[peripheralUuid][serviceUuid][characteristicUuid][descriptorUuid];
662
+ _onValueRead (peripheralId, serviceUuid, characteristicUuid, descriptorUuid, data, error) {
663
+ const descriptor = this._descriptors[peripheralId][serviceUuid][characteristicUuid][descriptorUuid];
594
664
 
595
- if (descriptor) {
596
- descriptor.emit('valueRead', data);
597
- } else {
598
- this.emit('warning', `unknown peripheral ${peripheralUuid}, ${serviceUuid}, ${characteristicUuid}, ${descriptorUuid} value read!`);
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
- Noble.prototype.writeValue = function (peripheralUuid, serviceUuid, characteristicUuid, descriptorUuid, data) {
603
- this._bindings.writeValue(peripheralUuid, serviceUuid, characteristicUuid, descriptorUuid, data);
604
- };
672
+ writeValue (peripheralId, serviceUuid, characteristicUuid, descriptorUuid, data) {
673
+ this._bindings.writeValue(peripheralId, serviceUuid, characteristicUuid, descriptorUuid, data);
674
+ }
605
675
 
606
- Noble.prototype.onValueWrite = function (peripheralUuid, serviceUuid, characteristicUuid, descriptorUuid) {
607
- const descriptor = this._descriptors[peripheralUuid][serviceUuid][characteristicUuid][descriptorUuid];
676
+ _onValueWrite (peripheralId, serviceUuid, characteristicUuid, descriptorUuid, error) {
677
+ const descriptor = this._descriptors[peripheralId][serviceUuid][characteristicUuid][descriptorUuid];
608
678
 
609
- if (descriptor) {
610
- descriptor.emit('valueWrite');
611
- } else {
612
- this.emit('warning', `unknown peripheral ${peripheralUuid}, ${serviceUuid}, ${characteristicUuid}, ${descriptorUuid} value write!`);
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
- Noble.prototype.readHandle = function (peripheralUuid, handle) {
617
- this._bindings.readHandle(peripheralUuid, handle);
618
- };
686
+ readHandle (peripheralId, handle) {
687
+ this._bindings.readHandle(peripheralId, handle);
688
+ }
619
689
 
620
- Noble.prototype.onHandleRead = function (peripheralUuid, handle, data) {
621
- const peripheral = this._peripherals[peripheralUuid];
690
+ _onHandleRead (peripheralId, handle, data, error) {
691
+ const peripheral = this._peripherals.get(peripheralId);
622
692
 
623
- if (peripheral) {
624
- peripheral.emit(`handleRead${handle}`, data);
625
- } else {
626
- this.emit('warning', `unknown peripheral ${peripheralUuid} handle read!`);
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
- Noble.prototype.writeHandle = function (peripheralUuid, handle, data, withoutResponse) {
631
- this._bindings.writeHandle(peripheralUuid, handle, data, withoutResponse);
632
- };
700
+ writeHandle (peripheralId, handle, data, withoutResponse) {
701
+ this._bindings.writeHandle(peripheralId, handle, data, withoutResponse);
702
+ }
633
703
 
634
- Noble.prototype.onHandleWrite = function (peripheralUuid, handle) {
635
- const peripheral = this._peripherals[peripheralUuid];
704
+ _onHandleWrite (peripheralId, handle, error) {
705
+ const peripheral = this._peripherals.get(peripheralId);
636
706
 
637
- if (peripheral) {
638
- peripheral.emit(`handleWrite${handle}`);
639
- } else {
640
- this.emit('warning', `unknown peripheral ${peripheralUuid} handle write!`);
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
- Noble.prototype.onHandleNotify = function (peripheralUuid, handle, data) {
645
- const peripheral = this._peripherals[peripheralUuid];
714
+ _onHandleNotify (peripheralId, handle, data, error) {
715
+ const peripheral = this._peripherals.get(peripheralId);
646
716
 
647
- if (peripheral) {
648
- peripheral.emit('handleNotify', handle, data);
649
- } else {
650
- this.emit('warning', `unknown peripheral ${peripheralUuid} handle notify!`);
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
- Noble.prototype.onMtu = function (peripheralUuid, mtu) {
655
- const peripheral = this._peripherals[peripheralUuid];
656
- if (peripheral && mtu) peripheral.mtu = mtu;
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;