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