@stoprocent/bleno 0.11.4 → 0.12.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/lib/bleno.js +3 -0
- package/lib/hci-socket/hci.js +1 -4
- package/lib/mac/src/ble_peripheral_manager.h +10 -0
- package/lib/mac/src/ble_peripheral_manager.mm +114 -2
- package/lib/mac/src/bleno_mac.mm +3 -0
- package/lib/mac/src/callbacks.h +2 -0
- package/lib/mac/src/callbacks.mm +12 -0
- package/package.json +1 -1
- package/prebuilds/android-arm/@stoprocent+bleno.armv7.node +0 -0
- package/prebuilds/android-arm64/@stoprocent+bleno.armv8.node +0 -0
- package/prebuilds/darwin-x64+arm64/@stoprocent+bleno.node +0 -0
- package/prebuilds/linux-x64/@stoprocent+bleno.musl.node +0 -0
- package/prebuilds/win32-ia32/@stoprocent+bleno.node +0 -0
- package/prebuilds/win32-x64/@stoprocent+bleno.node +0 -0
package/lib/bleno.js
CHANGED
package/lib/hci-socket/hci.js
CHANGED
|
@@ -133,7 +133,6 @@ class Hci extends EventEmitter {
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
resetBuffers () {
|
|
136
|
-
this._mainHandle = null;
|
|
137
136
|
this._handleAclsInProgress = {};
|
|
138
137
|
this._handleBuffers = {};
|
|
139
138
|
this._aclOutQueue = [];
|
|
@@ -527,7 +526,7 @@ class Hci extends EventEmitter {
|
|
|
527
526
|
if (subEventType === EVT_DISCONN_COMPLETE) {
|
|
528
527
|
handle = data.readUInt16LE(4);
|
|
529
528
|
debug('\t\thandle = ' + handle);
|
|
530
|
-
if (handle
|
|
529
|
+
if (this._handleAclsInProgress[handle] === undefined) {
|
|
531
530
|
debug('\tignoring event because handle is unknown to bleno.');
|
|
532
531
|
debug('This might be OK in a multi role scenario in which the handle is part of a noble connection.');
|
|
533
532
|
return;
|
|
@@ -543,7 +542,6 @@ class Hci extends EventEmitter {
|
|
|
543
542
|
Controller for the returned Handle have been flushed, and that the
|
|
544
543
|
corresponding data buffers have been freed. */
|
|
545
544
|
delete this._handleAclsInProgress[handle];
|
|
546
|
-
this._mainHandle = null;
|
|
547
545
|
const aclOutQueue = [];
|
|
548
546
|
let discarded = 0;
|
|
549
547
|
for (const i in this._aclOutQueue) {
|
|
@@ -789,7 +787,6 @@ class Hci extends EventEmitter {
|
|
|
789
787
|
debug('\t\t\tsupervision timeout = ' + supervisionTimeout);
|
|
790
788
|
debug('\t\t\tmaster clock accuracy = ' + masterClockAccuracy);
|
|
791
789
|
|
|
792
|
-
this._mainHandle = handle;
|
|
793
790
|
this._handleAclsInProgress[handle] = 0;
|
|
794
791
|
|
|
795
792
|
this.emit('leConnComplete', status, handle, role, addressType, address, interval, latency, supervisionTimeout, masterClockAccuracy);
|
|
@@ -8,20 +8,30 @@
|
|
|
8
8
|
#pragma once
|
|
9
9
|
|
|
10
10
|
#include <map>
|
|
11
|
+
#include <set>
|
|
11
12
|
#include "callbacks.h"
|
|
12
13
|
|
|
13
14
|
#import <CoreBluetooth/CoreBluetooth.h>
|
|
14
15
|
|
|
16
|
+
// Restoration identifier for state restoration across restarts
|
|
17
|
+
static NSString * _Nonnull const kBlenoRestorationIdentifier = @"com.bleno.peripheral.manager";
|
|
18
|
+
|
|
15
19
|
@interface BLEPeripheralManager : NSObject {
|
|
16
20
|
@public Emit emit;
|
|
17
21
|
@public std::map<CBUUID *, EmitCharacteristic> emitters;
|
|
18
22
|
}
|
|
19
23
|
|
|
24
|
+
// Track connected centrals by their identifier
|
|
25
|
+
@property (nonatomic, strong, readonly) NSMutableSet<NSUUID *> * _Nullable connectedCentrals;
|
|
26
|
+
// Track current services for proper cleanup
|
|
27
|
+
@property (nonatomic, strong, readonly) NSMutableArray<CBMutableService *> * _Nullable currentServices;
|
|
28
|
+
|
|
20
29
|
- (nonnull instancetype)init NS_DESIGNATED_INITIALIZER;
|
|
21
30
|
- (void)start;
|
|
22
31
|
- (void)startAdvertising:(NSString * _Nonnull)name serviceUUIDs:(NSArray<CBUUID *> * _Nonnull)serviceUUIDs;
|
|
23
32
|
- (void)stopAdvertising;
|
|
24
33
|
- (void)setServices:(NSArray<CBMutableService *> * _Nonnull)services;
|
|
34
|
+
- (void)removeAllServices;
|
|
25
35
|
- (void)disconnect;
|
|
26
36
|
- (void)updateRssi;
|
|
27
37
|
|
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
@property (nonatomic, strong) dispatch_queue_t processingQueue;
|
|
15
15
|
@property (nonatomic, strong) NSMutableArray<NSDictionary *> *pendingNotifications;
|
|
16
16
|
@property (nonatomic, strong) CBPeripheralManager *peripheralManager;
|
|
17
|
+
@property (nonatomic, strong, readwrite) NSMutableSet<NSUUID *> *connectedCentrals;
|
|
18
|
+
@property (nonatomic, strong, readwrite) NSMutableArray<CBMutableService *> *currentServices;
|
|
19
|
+
@property (nonatomic, assign) BOOL triedWithRestoration;
|
|
20
|
+
@property (nonatomic, assign) BOOL retriedWithoutRestoration;
|
|
17
21
|
@end
|
|
18
22
|
|
|
19
23
|
@implementation BLEPeripheralManager
|
|
@@ -23,14 +27,19 @@
|
|
|
23
27
|
if (self = [super init]) {
|
|
24
28
|
self.processingQueue = dispatch_queue_create("com.bleno.processing.queue", DISPATCH_QUEUE_SERIAL);
|
|
25
29
|
self.pendingNotifications = [NSMutableArray array];
|
|
30
|
+
self.connectedCentrals = [NSMutableSet set];
|
|
31
|
+
self.currentServices = [NSMutableArray array];
|
|
26
32
|
}
|
|
27
33
|
return self;
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
- (void)dealloc
|
|
31
37
|
{
|
|
38
|
+
[self removeAllServices];
|
|
32
39
|
self.peripheralManager.delegate = nil;
|
|
33
40
|
[self.pendingNotifications removeAllObjects];
|
|
41
|
+
[self.connectedCentrals removeAllObjects];
|
|
42
|
+
[self.currentServices removeAllObjects];
|
|
34
43
|
}
|
|
35
44
|
|
|
36
45
|
#pragma mark - Notification Management
|
|
@@ -76,8 +85,36 @@
|
|
|
76
85
|
|
|
77
86
|
- (void)start
|
|
78
87
|
{
|
|
88
|
+
[self startWithRestoration:YES];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
- (void)startWithRestoration:(BOOL)useRestoration
|
|
92
|
+
{
|
|
93
|
+
// Clean up any existing peripheral manager
|
|
94
|
+
if (self.peripheralManager) {
|
|
95
|
+
self.peripheralManager.delegate = nil;
|
|
96
|
+
self.peripheralManager = nil;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
NSDictionary *options;
|
|
100
|
+
if (useRestoration) {
|
|
101
|
+
// Initialize with state restoration to recover from previous state
|
|
102
|
+
options = @{
|
|
103
|
+
CBPeripheralManagerOptionRestoreIdentifierKey: kBlenoRestorationIdentifier,
|
|
104
|
+
CBPeripheralManagerOptionShowPowerAlertKey: @YES
|
|
105
|
+
};
|
|
106
|
+
self.triedWithRestoration = YES;
|
|
107
|
+
} else {
|
|
108
|
+
// Initialize without state restoration
|
|
109
|
+
options = @{
|
|
110
|
+
CBPeripheralManagerOptionShowPowerAlertKey: @YES
|
|
111
|
+
};
|
|
112
|
+
self.triedWithRestoration = NO;
|
|
113
|
+
}
|
|
114
|
+
|
|
79
115
|
self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self
|
|
80
|
-
queue:self.processingQueue
|
|
116
|
+
queue:self.processingQueue
|
|
117
|
+
options:options];
|
|
81
118
|
}
|
|
82
119
|
|
|
83
120
|
- (void)startAdvertising:(nonnull NSString *)name serviceUUIDs:(nonnull NSArray<CBUUID *> *)serviceUUIDs
|
|
@@ -99,11 +136,23 @@
|
|
|
99
136
|
|
|
100
137
|
- (void)setServices:(NSArray<CBMutableService *> *)services
|
|
101
138
|
{
|
|
139
|
+
// Store services for cleanup later
|
|
140
|
+
[self.currentServices addObjectsFromArray:services];
|
|
141
|
+
|
|
102
142
|
for (CBMutableService *service in services) {
|
|
103
143
|
[self.peripheralManager addService:service];
|
|
104
144
|
}
|
|
105
145
|
}
|
|
106
146
|
|
|
147
|
+
- (void)removeAllServices
|
|
148
|
+
{
|
|
149
|
+
if (self.peripheralManager) {
|
|
150
|
+
[self.peripheralManager removeAllServices];
|
|
151
|
+
}
|
|
152
|
+
[self.currentServices removeAllObjects];
|
|
153
|
+
emitters.clear();
|
|
154
|
+
}
|
|
155
|
+
|
|
107
156
|
- (void)disconnect
|
|
108
157
|
{
|
|
109
158
|
|
|
@@ -117,7 +166,20 @@
|
|
|
117
166
|
#pragma mark - CBPeripheralManagerDelegate
|
|
118
167
|
|
|
119
168
|
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral
|
|
120
|
-
{
|
|
169
|
+
{
|
|
170
|
+
// Check if state is unsupported and we tried with restoration - retry without it
|
|
171
|
+
if (peripheral.state == CBManagerStateUnsupported &&
|
|
172
|
+
self.triedWithRestoration &&
|
|
173
|
+
!self.retriedWithoutRestoration) {
|
|
174
|
+
self.retriedWithoutRestoration = YES;
|
|
175
|
+
|
|
176
|
+
// Dispatch async to avoid modifying peripheral manager during delegate callback
|
|
177
|
+
dispatch_async(self.processingQueue, ^{
|
|
178
|
+
[self startWithRestoration:NO];
|
|
179
|
+
});
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
121
183
|
auto state = StringFromCBPeripheralState(peripheral.state);
|
|
122
184
|
emit.StateChange(state);
|
|
123
185
|
}
|
|
@@ -137,6 +199,13 @@
|
|
|
137
199
|
|
|
138
200
|
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBMutableCharacteristic *)characteristic
|
|
139
201
|
{
|
|
202
|
+
// Track connected centrals and emit accept event for new connections
|
|
203
|
+
BOOL isNewConnection = ![self.connectedCentrals containsObject:central.identifier];
|
|
204
|
+
if (isNewConnection) {
|
|
205
|
+
[self.connectedCentrals addObject:central.identifier];
|
|
206
|
+
emit.Accept(central.identifier);
|
|
207
|
+
}
|
|
208
|
+
|
|
140
209
|
for (auto it = emitters.begin(); it != emitters.end(); ++it) {
|
|
141
210
|
if ([it->first isEqual:characteristic.UUID] == NO) { continue; }
|
|
142
211
|
auto cb = [weakSelf = self, characteristic, central](NSData *data) {
|
|
@@ -156,6 +225,13 @@
|
|
|
156
225
|
|
|
157
226
|
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request
|
|
158
227
|
{
|
|
228
|
+
// Track connected centrals on read requests too
|
|
229
|
+
BOOL isNewConnection = ![self.connectedCentrals containsObject:request.central.identifier];
|
|
230
|
+
if (isNewConnection) {
|
|
231
|
+
[self.connectedCentrals addObject:request.central.identifier];
|
|
232
|
+
emit.Accept(request.central.identifier);
|
|
233
|
+
}
|
|
234
|
+
|
|
159
235
|
for (auto it = emitters.begin(); it != emitters.end(); ++it) {
|
|
160
236
|
if ([it->first isEqual:request.characteristic.UUID] == NO) { continue; }
|
|
161
237
|
auto cb = [peripheral, request](int result, NSData *data) {
|
|
@@ -169,6 +245,13 @@
|
|
|
169
245
|
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests
|
|
170
246
|
{
|
|
171
247
|
for (CBATTRequest *request in requests) {
|
|
248
|
+
// Track connected centrals on write requests too
|
|
249
|
+
BOOL isNewConnection = ![self.connectedCentrals containsObject:request.central.identifier];
|
|
250
|
+
if (isNewConnection) {
|
|
251
|
+
[self.connectedCentrals addObject:request.central.identifier];
|
|
252
|
+
emit.Accept(request.central.identifier);
|
|
253
|
+
}
|
|
254
|
+
|
|
172
255
|
CBCharacteristic *characteristic = request.characteristic;
|
|
173
256
|
for (auto it = emitters.begin(); it != emitters.end(); ++it) {
|
|
174
257
|
if ([it->first isEqual:characteristic.UUID] == NO) { continue; }
|
|
@@ -193,4 +276,33 @@
|
|
|
193
276
|
[self processNotificationQueue];
|
|
194
277
|
}
|
|
195
278
|
|
|
279
|
+
#pragma mark - State Restoration
|
|
280
|
+
|
|
281
|
+
- (void)peripheralManager:(CBPeripheralManager *)peripheral willRestoreState:(NSDictionary<NSString *, id> *)dict
|
|
282
|
+
{
|
|
283
|
+
// Restore services that were previously registered
|
|
284
|
+
NSArray<CBMutableService *> *restoredServices = dict[CBPeripheralManagerRestoredStateServicesKey];
|
|
285
|
+
if (restoredServices) {
|
|
286
|
+
[self.currentServices addObjectsFromArray:restoredServices];
|
|
287
|
+
|
|
288
|
+
// Iterate through restored services to find subscribed centrals
|
|
289
|
+
for (CBMutableService *service in restoredServices) {
|
|
290
|
+
if (service.characteristics) {
|
|
291
|
+
for (CBMutableCharacteristic *characteristic in service.characteristics) {
|
|
292
|
+
// Check for subscribed centrals on this characteristic
|
|
293
|
+
if (characteristic.subscribedCentrals && characteristic.subscribedCentrals.count > 0) {
|
|
294
|
+
for (CBCentral *central in characteristic.subscribedCentrals) {
|
|
295
|
+
if (![self.connectedCentrals containsObject:central.identifier]) {
|
|
296
|
+
[self.connectedCentrals addObject:central.identifier];
|
|
297
|
+
// Emit accept event for the restored connection
|
|
298
|
+
emit.Accept(central.identifier);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
196
308
|
@end
|
package/lib/mac/src/bleno_mac.mm
CHANGED
|
@@ -54,6 +54,9 @@ Napi::Value BlenoMac::Init(const Napi::CallbackInfo& info) {
|
|
|
54
54
|
|
|
55
55
|
Napi::Value BlenoMac::Stop(const Napi::CallbackInfo& info) {
|
|
56
56
|
CHECK_MANAGER()
|
|
57
|
+
// Properly clean up: stop advertising, remove all services
|
|
58
|
+
[peripheralManager stopAdvertising];
|
|
59
|
+
[peripheralManager removeAllServices];
|
|
57
60
|
peripheralManager = nil;
|
|
58
61
|
return info.Env().Undefined();
|
|
59
62
|
}
|
package/lib/mac/src/callbacks.h
CHANGED
|
@@ -15,6 +15,8 @@ public:
|
|
|
15
15
|
void AdvertisingStop();
|
|
16
16
|
void ServicesSet(NSError * _Nullable error);
|
|
17
17
|
void StateChange(const std::string& state);
|
|
18
|
+
void Accept(NSUUID *centralUuid);
|
|
19
|
+
void Disconnect(NSUUID *centralUuid);
|
|
18
20
|
protected:
|
|
19
21
|
std::shared_ptr<ThreadSafeCallback> mCallback;
|
|
20
22
|
};
|
package/lib/mac/src/callbacks.mm
CHANGED
|
@@ -60,6 +60,18 @@ void Emit::StateChange(const std::string& state) {
|
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
void Emit::Accept(NSUUID *centralUuid) {
|
|
64
|
+
mCallback->call([centralUuid](Napi::Env env, std::vector<napi_value>& args) {
|
|
65
|
+
args = { _s("accept"), _a(centralUuid), _a(centralUuid) };
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
void Emit::Disconnect(NSUUID *centralUuid) {
|
|
70
|
+
mCallback->call([centralUuid](Napi::Env env, std::vector<napi_value>& args) {
|
|
71
|
+
args = { _s("disconnect"), _a(centralUuid) };
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
63
75
|
void EmitCharacteristic::Wrap(const Napi::Value& receiver, const Napi::Function& callback) {
|
|
64
76
|
mCallback = std::make_shared<ThreadSafeCallback>(receiver, callback);
|
|
65
77
|
}
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|