@stoprocent/bleno 0.11.4 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/bleno.js +3 -0
- 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
|
@@ -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
|