bluetooth-serial-port 3.0.4 → 3.0.5

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 CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  This piece of open source software is free (as in speech) to [use, modify and distribute](LICENSE.md), no strings attached. If this software saves you some cash and you want to do something for the good please consider making a donation to a [charity](https://www.doctorswithoutborders.org). Any charity will do. 🙏
6
6
 
7
- > MacOS support has (temporarily) been dropped as of version v3.0.0 of the module. If you use the module on a Mac please stay on v2 of this module.
7
+ > experimental macOS support is available again. Feedback (also when it is working as expected) would be highly apreciated.
8
8
 
9
9
  > _DEPRECATED_ I am no longer actively maintaining this project. If you want to help out by adding support please contact me. It was a great experience maintaining this project for almost 8 years. It was great to see people step in to improve this project. Thank you all!
10
10
 
@@ -18,6 +18,7 @@ If you have any problems make sure to [checkout the FAQ](https://github.com/eelc
18
18
  ## New in the last releases
19
19
 
20
20
  * Installs precompiled native artifacts
21
+ * Re-enables MacOS support
21
22
  * Bumps dependencies
22
23
  * Fixes memory leaks.
23
24
  * Adds support for node 20, 22 and 24
@@ -60,11 +61,16 @@ sudo systemctl restart bluetooth
60
61
  - Needs Visual Studio (Visual C++) and its command line tools for source builds.
61
62
  - Needs Python installed and accessible from the command line path.
62
63
 
64
+ ## Prerequisites on macOS
65
+
66
+ - Build prerequisites are only needed when no matching prebuilt binary is available.
67
+ - Install Xcode and Command Line Tools.
68
+
63
69
  ## Install
64
70
 
65
71
  `npm install bluetooth-serial-port`
66
72
 
67
- For Linux x64 and Windows x64 on Node 18, 20, 22 and 24, installation first downloads a prebuilt native binary from GitHub Releases.
73
+ For Linux x64/arm64, Windows x64, and macOS x64/arm64 on Node 18, 20, 22 and 24, installation first downloads a prebuilt native binary from GitHub Releases.
68
74
  If a matching prebuild is unavailable, install falls back to a local `node-gyp` build.
69
75
 
70
76
  ## Test build Linux using docker
package/binding.gyp CHANGED
@@ -11,6 +11,14 @@
11
11
  'libraries': ['-lbluetooth'],
12
12
  'cflags':['-std=c++11']
13
13
  }],
14
+ [ 'OS=="mac"', {
15
+ 'sources': ['src/macos/DeviceINQ.mm', 'src/macos/BluetoothWorker.mm', 'src/macos/pipe.c', 'src/macos/BluetoothDeviceResources.mm', 'src/macos/BluetoothSerialPort.mm', 'src/macos/BTSerialPortBinding.mm'],
16
+ 'include_dirs' : [ "<!(node -e \"require('nan')\")", 'src', 'src/macos' ],
17
+ 'libraries':['-framework Foundation', '-framework IOBluetooth', '-fobjc-arc'],
18
+ 'cflags!': [ '-fno-exceptions' ],
19
+ 'cflags_cc!': [ '-fno-exceptions' ],
20
+ 'xcode_settings': { 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES', 'OTHER_CFLAGS': [ '-Wno-unused-function' ] }
21
+ }],
14
22
  [ 'OS=="win"', {
15
23
  'sources': [ 'src/windows/BluetoothSerialPort.cc', 'src/windows/DeviceINQ.cc', 'src/windows/BTSerialPortBinding.cc', 'src/windows/BluetoothHelpers.cc' ],
16
24
  'include_dirs' : [ "<!(node -e \"require('nan')\")", 'src', 'src/windows' ],
@@ -32,4 +40,3 @@
32
40
  }
33
41
  ] # end targets
34
42
  }
35
-
@@ -51,6 +51,14 @@
51
51
  };
52
52
 
53
53
  BluetoothSerialPort.prototype.inquire = function () {
54
+ if (process.platform === 'darwin') {
55
+ var self = this;
56
+ setImmediate(function () {
57
+ self.inq.inquireSync(self.found, self.finish);
58
+ });
59
+ return;
60
+ }
61
+
54
62
  this.inq.inquire(this.found, this.finish);
55
63
  };
56
64
 
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "bluetooth-serial-port",
3
- "version": "3.0.4",
3
+ "version": "3.0.5",
4
4
  "description": "Bluetooth serial port communication for Node.js",
5
5
  "author": "Eelco Cramer <eelco@hailendal.org>",
6
6
  "keywords": [
7
7
  "bluetooth",
8
8
  "serial port",
9
9
  "rfcomm",
10
+ "darwin",
10
11
  "linux",
11
12
  "windows"
12
13
  ],
@@ -29,6 +30,7 @@
29
30
  "prebuilds/"
30
31
  ],
31
32
  "os": [
33
+ "darwin",
32
34
  "linux",
33
35
  "win32"
34
36
  ],
@@ -216,7 +216,7 @@ void BTSerialPortBinding::Init(Local<Object> target) {
216
216
  t->InstanceTemplate()->SetInternalFieldCount(1);
217
217
  t->SetClassName(Nan::New("BTSerialPortBinding").ToLocalChecked());
218
218
 
219
- Isolate *isolate = target->GetIsolate();
219
+ Isolate *isolate = Isolate::GetCurrent();
220
220
  Local<Context> ctx = isolate->GetCurrentContext();
221
221
 
222
222
  Nan::SetPrototypeMethod(t, "write", Write);
@@ -19,6 +19,7 @@
19
19
 
20
20
  #include <v8.h>
21
21
  #include <node.h>
22
+ #include <stdio.h>
22
23
  #include <string.h>
23
24
  #include <stdlib.h>
24
25
  #include <unistd.h>
@@ -33,6 +34,8 @@
33
34
  using namespace node;
34
35
  using namespace v8;
35
36
 
37
+ static const CFTimeInterval INQUIRY_TIMEOUT_SECONDS = 20.0;
38
+
36
39
  /** Private class for wrapping a pipe */
37
40
  @interface Pipe : NSObject {
38
41
  pipe_t *pipe;
@@ -60,6 +63,49 @@ using namespace v8;
60
63
  @synthesize address;
61
64
  @end
62
65
 
66
+ /** Stops the current run loop when inquiry completes. */
67
+ @interface DeviceInquiryRunLoopStopper : NSObject <IOBluetoothDeviceInquiryDelegate> {
68
+ @private
69
+ BOOL completed;
70
+ }
71
+
72
+ - (BOOL)isCompleted;
73
+
74
+ @end
75
+
76
+ @implementation DeviceInquiryRunLoopStopper
77
+
78
+ - (id)init
79
+ {
80
+ self = [super init];
81
+ if (self != nil) {
82
+ completed = NO;
83
+ }
84
+
85
+ return self;
86
+ }
87
+
88
+ - (BOOL)isCompleted
89
+ {
90
+ return completed;
91
+ }
92
+
93
+ - (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry *)sender device:(IOBluetoothDevice *)device
94
+ {
95
+ (void)sender;
96
+ (void)device;
97
+ }
98
+
99
+ - (void)deviceInquiryComplete:(IOBluetoothDeviceInquiry *)sender error:(IOReturn)error aborted:(BOOL)aborted
100
+ {
101
+ (void)sender;
102
+ (void)error;
103
+ (void)aborted;
104
+ completed = YES;
105
+ }
106
+
107
+ @end
108
+
63
109
  static NSMutableDictionary *instanceWorkers = nil;
64
110
  static NSLock *globalConnectLock = nil;
65
111
 
@@ -260,21 +306,57 @@ static NSLock *globalConnectLock = nil;
260
306
  - (void) inquireWithPipe: (pipe_t *)pipe
261
307
  {
262
308
  @synchronized(self) {
263
- inquiryProducer = pipe_producer_new(pipe);
264
- dispatch_async(worker_queue, ^{
265
- [self inquiryTask];
266
- });
309
+ if (inquiryProducer != NULL) {
310
+ pipe_producer_free(inquiryProducer);
311
+ inquiryProducer = NULL;
312
+ }
313
+ inquiryProducer = pipe_producer_new(pipe);
314
+ }
315
+
316
+ if ([NSThread isMainThread]) {
317
+ [self inquiryTask];
318
+ } else {
319
+ dispatch_sync(worker_queue, ^{
320
+ [self inquiryTask];
321
+ });
267
322
  }
268
323
  }
269
324
 
270
325
  /** Worker task to the the inquiry */
271
326
  - (void) inquiryTask
272
327
  {
273
- IOBluetoothDeviceInquiry *bdi = [IOBluetoothDeviceInquiry inquiryWithDelegate:self];
274
- // bdi.inquiryLength = 30;
275
- [bdi start];
276
- // [NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 31]];
277
- // [bdi stop];
328
+ @autoreleasepool {
329
+ DeviceInquiryRunLoopStopper *stopper = [[DeviceInquiryRunLoopStopper alloc] init];
330
+ IOBluetoothDeviceInquiry *inquiry = [[IOBluetoothDeviceInquiry alloc] initWithDelegate:stopper];
331
+
332
+ if (inquiry == nil) {
333
+ [self completeInquiry];
334
+ [stopper release];
335
+ return;
336
+ }
337
+
338
+ [inquiry setInquiryLength:10];
339
+ IOReturn startStatus = [inquiry start];
340
+
341
+ if (startStatus == kIOReturnSuccess) {
342
+ NSDate *deadline = [NSDate dateWithTimeIntervalSinceNow:INQUIRY_TIMEOUT_SECONDS];
343
+ while (![stopper isCompleted] && [deadline timeIntervalSinceNow] > 0) {
344
+ [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:deadline];
345
+ }
346
+ }
347
+
348
+ [inquiry stop];
349
+ [inquiry setDelegate:nil];
350
+
351
+ NSArray *foundDevices = [inquiry foundDevices];
352
+ for (IOBluetoothDevice *device in foundDevices) {
353
+ [self emitInquiryDevice:device];
354
+ }
355
+
356
+ [self completeInquiry];
357
+ [inquiry release];
358
+ [stopper release];
359
+ }
278
360
  }
279
361
 
280
362
  /** Get the RFCOMM channel for a given device */
@@ -360,30 +442,48 @@ static NSLock *globalConnectLock = nil;
360
442
 
361
443
  /** Called when the device inquiry completes */
362
444
  - (void) deviceInquiryComplete: (IOBluetoothDeviceInquiry *) sender error: (IOReturn) error aborted: (BOOL) aborted
445
+ {
446
+ (void)sender;
447
+ (void)error;
448
+ (void)aborted;
449
+ [self completeInquiry];
450
+ }
451
+
452
+ /** Called when a device has been found */
453
+ - (void) deviceInquiryDeviceFound: (IOBluetoothDeviceInquiry*) sender device: (IOBluetoothDevice*) device
454
+ {
455
+ (void)sender;
456
+ [self emitInquiryDevice:device];
457
+ }
458
+
459
+ - (void)completeInquiry
363
460
  {
364
461
  @synchronized(self) {
365
462
  if (inquiryProducer != NULL) {
366
- // free the producer so the main thread is signaled that the inquiry has been completed.
367
463
  pipe_producer_free(inquiryProducer);
368
464
  inquiryProducer = NULL;
369
465
  }
370
466
  }
371
467
  }
372
468
 
373
- /** Called when a device has been found */
374
- - (void) deviceInquiryDeviceFound: (IOBluetoothDeviceInquiry*) sender device: (IOBluetoothDevice*) device
469
+ - (void)emitInquiryDevice:(IOBluetoothDevice *)device
375
470
  {
376
471
  @synchronized(self) {
377
- if (inquiryProducer != NULL) {
378
- device_info_t *info = new device_info_t;
379
- strcpy(info->address, [[device addressString] UTF8String]);
380
- strcpy(info->name, [[device addressString] UTF8String]);
472
+ if (inquiryProducer == NULL || device == nil) {
473
+ return;
474
+ }
381
475
 
382
- // push the device data into the pipe to notify the main thread
383
- pipe_push(inquiryProducer, info, 1);
476
+ device_info_t info;
477
+ memset(&info, 0, sizeof(device_info_t));
384
478
 
385
- delete info;
386
- }
479
+ const char *address = [[device addressString] UTF8String];
480
+ const char *name = [[device nameOrAddress] UTF8String];
481
+
482
+ snprintf(info.address, sizeof(info.address), "%s", address != NULL ? address : "");
483
+ snprintf(info.name, sizeof(info.name), "%s", name != NULL ? name : info.address);
484
+
485
+ // push the device data into the pipe to notify the main thread
486
+ pipe_push(inquiryProducer, &info, 1);
387
487
  }
388
488
  }
389
489
 
@@ -126,7 +126,7 @@ void DeviceINQ::Init(Local<Object> target) {
126
126
  t->InstanceTemplate()->SetInternalFieldCount(1);
127
127
  t->SetClassName(Nan::New("DeviceINQ").ToLocalChecked());
128
128
 
129
- Isolate *isolate = target->GetIsolate();
129
+ Isolate *isolate = Isolate::GetCurrent();
130
130
  Local<Context> ctx = isolate->GetCurrentContext();
131
131
 
132
132
  Nan::SetPrototypeMethod(t, "inquireSync", InquireSync);