react-native-ble-nitro 1.5.0 → 1.7.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 (86) hide show
  1. package/README.md +56 -12
  2. package/android/src/main/java/com/margelo/nitro/co/zyke/ble/BleNitroBleManager.kt +12 -5
  3. package/android/src/main/java/com/margelo/nitro/co/zyke/ble/BleNitroBleManagerFactory.kt +21 -0
  4. package/ios/BleNitroBleManager.swift +37 -24
  5. package/ios/BleNitroBleManagerFactory.swift +19 -0
  6. package/lib/commonjs/index.d.ts +2 -5
  7. package/lib/commonjs/index.d.ts.map +1 -1
  8. package/lib/commonjs/index.js +7 -15
  9. package/lib/commonjs/index.js.map +1 -1
  10. package/lib/commonjs/manager.d.ts +43 -12
  11. package/lib/commonjs/manager.d.ts.map +1 -1
  12. package/lib/commonjs/manager.js +107 -43
  13. package/lib/commonjs/manager.js.map +1 -1
  14. package/lib/commonjs/singleton.d.ts +10 -0
  15. package/lib/commonjs/singleton.d.ts.map +1 -0
  16. package/lib/commonjs/singleton.js +20 -0
  17. package/lib/commonjs/singleton.js.map +1 -0
  18. package/lib/commonjs/specs/NativeBleNitro.d.ts +1 -1
  19. package/lib/commonjs/specs/NativeBleNitro.d.ts.map +1 -1
  20. package/lib/commonjs/specs/NativeBleNitro.js +1 -0
  21. package/lib/commonjs/specs/NativeBleNitro.js.map +1 -1
  22. package/lib/commonjs/specs/NativeBleNitro.nitro.d.ts +3 -1
  23. package/lib/commonjs/specs/NativeBleNitro.nitro.d.ts.map +1 -1
  24. package/lib/commonjs/specs/NativeBleNitro.nitro.js.map +1 -1
  25. package/lib/commonjs/specs/NativeBleNitroFactory.d.ts +5 -0
  26. package/lib/commonjs/specs/NativeBleNitroFactory.d.ts.map +1 -0
  27. package/lib/commonjs/specs/NativeBleNitroFactory.js +23 -0
  28. package/lib/commonjs/specs/NativeBleNitroFactory.js.map +1 -0
  29. package/lib/commonjs/specs/NativeBleNitroFactory.nitro.d.ts +9 -0
  30. package/lib/commonjs/specs/NativeBleNitroFactory.nitro.d.ts.map +1 -0
  31. package/lib/commonjs/specs/NativeBleNitroFactory.nitro.js +3 -0
  32. package/lib/commonjs/specs/NativeBleNitroFactory.nitro.js.map +1 -0
  33. package/lib/index.d.ts +2 -5
  34. package/lib/index.js +2 -11
  35. package/lib/manager.d.ts +43 -12
  36. package/lib/manager.js +100 -38
  37. package/lib/singleton.d.ts +9 -0
  38. package/lib/singleton.js +15 -0
  39. package/lib/specs/NativeBleNitro.d.ts +1 -1
  40. package/lib/specs/NativeBleNitro.js +1 -0
  41. package/lib/specs/NativeBleNitro.nitro.d.ts +3 -1
  42. package/lib/specs/NativeBleNitroFactory.d.ts +4 -0
  43. package/lib/specs/NativeBleNitroFactory.js +6 -0
  44. package/lib/specs/NativeBleNitroFactory.nitro.d.ts +8 -0
  45. package/lib/specs/NativeBleNitroFactory.nitro.js +1 -0
  46. package/nitro.json +4 -0
  47. package/nitrogen/generated/android/BleNitro+autolinking.cmake +2 -0
  48. package/nitrogen/generated/android/BleNitroOnLoad.cpp +11 -0
  49. package/nitrogen/generated/android/c++/JBLEDevice.hpp +6 -2
  50. package/nitrogen/generated/android/c++/JFunc_void_std__vector_BLEDevice_.hpp +12 -12
  51. package/nitrogen/generated/android/c++/JHybridNativeBleNitroFactorySpec.cpp +71 -0
  52. package/nitrogen/generated/android/c++/JHybridNativeBleNitroFactorySpec.hpp +64 -0
  53. package/nitrogen/generated/android/c++/JHybridNativeBleNitroSpec.cpp +14 -6
  54. package/nitrogen/generated/android/c++/JHybridNativeBleNitroSpec.hpp +3 -2
  55. package/nitrogen/generated/android/kotlin/com/margelo/nitro/co/zyke/ble/BLEDevice.kt +4 -1
  56. package/nitrogen/generated/android/kotlin/com/margelo/nitro/co/zyke/ble/Func_void_std__vector_BLEDevice_.kt +9 -9
  57. package/nitrogen/generated/android/kotlin/com/margelo/nitro/co/zyke/ble/HybridNativeBleNitroFactorySpec.kt +57 -0
  58. package/nitrogen/generated/android/kotlin/com/margelo/nitro/co/zyke/ble/HybridNativeBleNitroSpec.kt +8 -4
  59. package/nitrogen/generated/ios/BleNitro-Swift-Cxx-Bridge.cpp +17 -0
  60. package/nitrogen/generated/ios/BleNitro-Swift-Cxx-Bridge.hpp +53 -9
  61. package/nitrogen/generated/ios/BleNitro-Swift-Cxx-Umbrella.hpp +5 -0
  62. package/nitrogen/generated/ios/BleNitroAutolinking.mm +8 -0
  63. package/nitrogen/generated/ios/BleNitroAutolinking.swift +15 -0
  64. package/nitrogen/generated/ios/c++/HybridNativeBleNitroFactorySpecSwift.cpp +11 -0
  65. package/nitrogen/generated/ios/c++/HybridNativeBleNitroFactorySpecSwift.hpp +94 -0
  66. package/nitrogen/generated/ios/c++/HybridNativeBleNitroSpecSwift.hpp +11 -5
  67. package/nitrogen/generated/ios/swift/BLEDevice.swift +13 -2
  68. package/nitrogen/generated/ios/swift/Func_void_std__vector_BLEDevice_.swift +5 -5
  69. package/nitrogen/generated/ios/swift/HybridNativeBleNitroFactorySpec.swift +49 -0
  70. package/nitrogen/generated/ios/swift/HybridNativeBleNitroFactorySpec_cxx.swift +149 -0
  71. package/nitrogen/generated/ios/swift/HybridNativeBleNitroSpec.swift +2 -2
  72. package/nitrogen/generated/ios/swift/HybridNativeBleNitroSpec_cxx.swift +24 -3
  73. package/nitrogen/generated/shared/c++/BLEDevice.hpp +6 -2
  74. package/nitrogen/generated/shared/c++/HybridNativeBleNitroFactorySpec.cpp +21 -0
  75. package/nitrogen/generated/shared/c++/HybridNativeBleNitroFactorySpec.hpp +71 -0
  76. package/nitrogen/generated/shared/c++/HybridNativeBleNitroSpec.cpp +2 -0
  77. package/nitrogen/generated/shared/c++/HybridNativeBleNitroSpec.hpp +5 -4
  78. package/package.json +9 -1
  79. package/src/__tests__/index.test.ts +53 -43
  80. package/src/index.ts +2 -13
  81. package/src/manager.ts +107 -38
  82. package/src/singleton.ts +17 -0
  83. package/src/specs/NativeBleNitro.nitro.ts +3 -1
  84. package/src/specs/NativeBleNitro.ts +2 -1
  85. package/src/specs/NativeBleNitroFactory.nitro.ts +6 -0
  86. package/src/specs/NativeBleNitroFactory.ts +9 -0
@@ -24,12 +24,12 @@ namespace margelo::nitro::co::zyke::ble { enum class BLEState; }
24
24
  // Forward declaration of `OperationResult` to properly resolve imports.
25
25
  namespace margelo::nitro::co::zyke::ble { struct OperationResult; }
26
26
 
27
+ #include <string>
28
+ #include <optional>
27
29
  #include "BLEDevice.hpp"
28
30
  #include <vector>
29
31
  #include <functional>
30
32
  #include "ScanFilter.hpp"
31
- #include <optional>
32
- #include <string>
33
33
  #include <NitroModules/ArrayBuffer.hpp>
34
34
  #include "BLEState.hpp"
35
35
  #include "OperationResult.hpp"
@@ -62,7 +62,8 @@ namespace margelo::nitro::co::zyke::ble {
62
62
 
63
63
  public:
64
64
  // Properties
65
-
65
+ virtual std::optional<std::string> getRestoreStateIdentifier() = 0;
66
+ virtual void setRestoreStateIdentifier(const std::optional<std::string>& restoreStateIdentifier) = 0;
66
67
 
67
68
  public:
68
69
  // Methods
@@ -71,7 +72,7 @@ namespace margelo::nitro::co::zyke::ble {
71
72
  virtual bool stopScan() = 0;
72
73
  virtual bool isScanning() = 0;
73
74
  virtual std::vector<BLEDevice> getConnectedDevices(const std::vector<std::string>& services) = 0;
74
- virtual void connect(const std::string& deviceId, const std::function<void(bool /* success */, const std::string& /* deviceId */, const std::string& /* error */)>& callback, const std::optional<std::function<void(const std::string& /* deviceId */, bool /* interrupted */, const std::string& /* error */)>>& disconnectCallback) = 0;
75
+ virtual void connect(const std::string& deviceId, const std::function<void(bool /* success */, const std::string& /* deviceId */, const std::string& /* error */)>& callback, const std::optional<std::function<void(const std::string& /* deviceId */, bool /* interrupted */, const std::string& /* error */)>>& disconnectCallback, std::optional<bool> autoConnectAndroid) = 0;
75
76
  virtual void disconnect(const std::string& deviceId, const std::function<void(bool /* success */, const std::string& /* error */)>& callback) = 0;
76
77
  virtual bool isConnected(const std::string& deviceId) = 0;
77
78
  virtual double requestMTU(const std::string& deviceId, double mtu) = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-ble-nitro",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "description": "High-performance React Native BLE library built on Nitro Modules",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -23,6 +23,14 @@
23
23
  "types": "./lib/manager.d.ts",
24
24
  "default": "./src/manager.ts"
25
25
  },
26
+ "./singleton": {
27
+ "react-native": "./src/singleton.ts",
28
+ "typescript": "./src/singleton.ts",
29
+ "import": "./lib/singleton.js",
30
+ "require": "./lib/commonjs/singleton.js",
31
+ "types": "./lib/singleton.d.ts",
32
+ "default": "./src/singleton.ts"
33
+ },
26
34
  "./plugin": {
27
35
  "import": "./plugin/build/index.js",
28
36
  "require": "./plugin/build/index.js",
@@ -1,31 +1,34 @@
1
1
  // Mock the native module import
2
+ const mockNativeInstance = {
3
+ setRestoreStateCallback: jest.fn(),
4
+ startScan: jest.fn(),
5
+ stopScan: jest.fn(),
6
+ isScanning: jest.fn(),
7
+ connect: jest.fn(),
8
+ disconnect: jest.fn(),
9
+ isConnected: jest.fn(),
10
+ requestMTU: jest.fn(),
11
+ readRSSI: jest.fn(),
12
+ discoverServices: jest.fn(),
13
+ getServices: jest.fn(),
14
+ getCharacteristics: jest.fn(),
15
+ readCharacteristic: jest.fn(),
16
+ writeCharacteristic: jest.fn(),
17
+ subscribeToCharacteristic: jest.fn(),
18
+ unsubscribeFromCharacteristic: jest.fn(),
19
+ getConnectedDevices: jest.fn(),
20
+ requestBluetoothEnable: jest.fn(),
21
+ state: jest.fn(),
22
+ subscribeToStateChange: jest.fn(),
23
+ unsubscribeFromStateChange: jest.fn(),
24
+ openSettings: jest.fn(),
25
+ restoreStateIdentifier: null,
26
+ };
27
+
2
28
  jest.mock('../specs/NativeBleNitro', () => ({
3
29
  __esModule: true,
4
- default: {
5
- setRestoreStateCallback: jest.fn(),
6
- startScan: jest.fn(),
7
- stopScan: jest.fn(),
8
- isScanning: jest.fn(),
9
- connect: jest.fn(),
10
- disconnect: jest.fn(),
11
- isConnected: jest.fn(),
12
- requestMTU: jest.fn(),
13
- readRSSI: jest.fn(),
14
- discoverServices: jest.fn(),
15
- getServices: jest.fn(),
16
- getCharacteristics: jest.fn(),
17
- readCharacteristic: jest.fn(),
18
- writeCharacteristic: jest.fn(),
19
- subscribeToCharacteristic: jest.fn(),
20
- unsubscribeFromCharacteristic: jest.fn(),
21
- getConnectedDevices: jest.fn(),
22
- requestBluetoothEnable: jest.fn(),
23
- state: jest.fn(),
24
- subscribeToStateChange: jest.fn(),
25
- unsubscribeFromStateChange: jest.fn(),
26
- openSettings: jest.fn(),
27
- },
28
- BLEState: {
30
+ default: mockNativeInstance,
31
+ BLEState: {
29
32
  Unknown: 0,
30
33
  Resetting: 1,
31
34
  Unsupported: 2,
@@ -41,10 +44,17 @@ jest.mock('../specs/NativeBleNitro', () => ({
41
44
  },
42
45
  }));
43
46
 
47
+ jest.mock('../specs/NativeBleNitroFactory', () => ({
48
+ __esModule: true,
49
+ default: {
50
+ create: jest.fn(() => mockNativeInstance),
51
+ },
52
+ }));
53
+
44
54
  import { BleNitro } from '../index';
45
55
 
46
56
  // Get reference to the mocked module
47
- const mockNative = require('../specs/NativeBleNitro').default; // eslint-disable-line @typescript-eslint/no-var-requires
57
+ const mockNative = mockNativeInstance;
48
58
 
49
59
  // Get BLE instance
50
60
  const BleManager = BleNitro.instance();
@@ -97,8 +107,8 @@ describe('BleNitro', () => {
97
107
  });
98
108
 
99
109
  const result = await BleManager.connect(deviceId);
100
-
101
- expect(mockNative.connect).toHaveBeenCalledWith(deviceId, expect.any(Function), undefined);
110
+
111
+ expect(mockNative.connect).toHaveBeenCalledWith(deviceId, expect.any(Function), undefined, false);
102
112
  expect(result).toBe(deviceId);
103
113
  });
104
114
 
@@ -142,7 +152,7 @@ describe('BleNitro', () => {
142
152
 
143
153
  const data = [1, 2, 3];
144
154
  const result = await BleManager.writeCharacteristic('device-write', 'service', 'char', data, false);
145
-
155
+
146
156
  expect(mockNative.writeCharacteristic).toHaveBeenCalledWith(
147
157
  'device-write',
148
158
  '0service-0000-1000-8000-00805f9b34fb',
@@ -170,7 +180,7 @@ describe('BleNitro', () => {
170
180
 
171
181
  const data = [1, 2, 3];
172
182
  const result = await BleManager.writeCharacteristic('device-write-resp', 'service', 'char', data, true);
173
-
183
+
174
184
  expect(mockNative.writeCharacteristic).toHaveBeenCalledWith(
175
185
  'device-write-resp',
176
186
  '0service-0000-1000-8000-00805f9b34fb',
@@ -196,15 +206,15 @@ describe('BleNitro', () => {
196
206
  });
197
207
 
198
208
  const result = await BleManager.readCharacteristic('device', 'service', 'char');
199
-
209
+
200
210
  // UUIDs should be normalized in the call
201
211
  expect(mockNative.readCharacteristic).toHaveBeenCalledWith(
202
- 'device',
212
+ 'device',
203
213
  '0service-0000-1000-8000-00805f9b34fb', // 'service' padded to 8 chars
204
214
  '0000char-0000-1000-8000-00805f9b34fb', // 'char' padded to 8 chars
205
215
  expect.any(Function)
206
216
  );
207
-
217
+
208
218
  // Result should be number array (ByteArray)
209
219
  expect(Array.isArray(result)).toBe(true);
210
220
  expect(result).toEqual([85]);
@@ -223,7 +233,7 @@ describe('BleNitro', () => {
223
233
  });
224
234
 
225
235
  const result = await BleManager.disconnect('device');
226
-
236
+
227
237
  expect(mockNative.disconnect).toHaveBeenCalledWith('device', expect.any(Function));
228
238
  expect(result).toBe(undefined);
229
239
  });
@@ -245,10 +255,10 @@ describe('BleNitro', () => {
245
255
 
246
256
  const notificationCallback = jest.fn();
247
257
  const subscription = BleManager.subscribeToCharacteristic('device', 'service', 'char', notificationCallback);
248
-
258
+
249
259
  expect(mockNative.subscribeToCharacteristic).toHaveBeenCalled();
250
260
  expect(notificationCallback).toHaveBeenCalledWith('char-id', [1, 2, 3]);
251
-
261
+
252
262
  // Verify subscription object
253
263
  expect(subscription).toHaveProperty('remove');
254
264
  expect(typeof subscription.remove).toBe('function');
@@ -257,10 +267,10 @@ describe('BleNitro', () => {
257
267
  test('connect with disconnect event callback', async () => {
258
268
  const deviceId = 'test-device-2'; // Use different device ID to avoid state conflicts
259
269
  const onDisconnect = jest.fn();
260
-
270
+
261
271
  mockNative.connect.mockImplementation((id: string, callback: (success: boolean, deviceId: string, error: string) => void, disconnectCallback?: (deviceId: string, interrupted: boolean, error: string) => void) => {
262
272
  callback(true, id, '');
263
- // Simulate a disconnect event later
273
+ // Simulate a disconnect event later
264
274
  if (disconnectCallback) {
265
275
  setTimeout(() => {
266
276
  disconnectCallback(id, true, 'Connection lost'); // interrupted = true
@@ -269,10 +279,10 @@ describe('BleNitro', () => {
269
279
  });
270
280
 
271
281
  const result = await BleManager.connect(deviceId, onDisconnect);
272
-
273
- expect(mockNative.connect).toHaveBeenCalledWith(deviceId, expect.any(Function), expect.any(Function));
282
+
283
+ expect(mockNative.connect).toHaveBeenCalledWith(deviceId, expect.any(Function), expect.any(Function), false);
274
284
  expect(result).toBe(deviceId);
275
-
285
+
276
286
  // Wait for disconnect callback
277
287
  await new Promise(resolve => setTimeout(resolve, 30));
278
288
  expect(onDisconnect).toHaveBeenCalledWith(deviceId, true, 'Connection lost');
@@ -297,7 +307,7 @@ describe('BleNitro', () => {
297
307
  });
298
308
 
299
309
  const rssi = await BleManager.readRSSI('device-rssi');
300
-
310
+
301
311
  expect(mockNative.readRSSI).toHaveBeenCalledWith(
302
312
  'device-rssi',
303
313
  expect.any(Function)
@@ -318,7 +328,7 @@ describe('BleNitro', () => {
318
328
  });
319
329
 
320
330
  await expect(BleManager.readRSSI('device-rssi-fail')).rejects.toThrow('RSSI read failed');
321
-
331
+
322
332
  expect(mockNative.readRSSI).toHaveBeenCalledWith(
323
333
  'device-rssi-fail',
324
334
  expect.any(Function)
package/src/index.ts CHANGED
@@ -1,5 +1,3 @@
1
- import { BleNitroManager } from "./manager";
2
-
3
1
  export {
4
2
  type ByteArray,
5
3
  type ScanFilter,
@@ -12,19 +10,10 @@ export {
12
10
  type OperationCallback,
13
11
  type CharacteristicUpdateCallback,
14
12
  type Subscription,
15
- type BleNitroManager,
16
13
  type BleNitroManagerOptions,
17
14
  BLEState,
18
15
  AndroidScanMode,
16
+ BleNitroManager,
19
17
  } from "./manager";
20
18
 
21
- let _instance: BleNitroManager;
22
-
23
- export class BleNitro extends BleNitroManager {
24
- public static instance() {
25
- if (!_instance) {
26
- _instance = new BleNitro();
27
- }
28
- return _instance;
29
- }
30
- }
19
+ export { BleNitro } from './singleton';
package/src/manager.ts CHANGED
@@ -1,4 +1,4 @@
1
- import BleNitroNative from './specs/NativeBleNitro';
1
+ import BleNitroNativeFactory, { NativeBleNitro } from './specs/NativeBleNitroFactory';
2
2
  import {
3
3
  ScanFilter as NativeScanFilter,
4
4
  BLEDevice as NativeBLEDevice,
@@ -32,6 +32,7 @@ export interface BLEDevice {
32
32
  manufacturerData: ManufacturerData;
33
33
  serviceUUIDs: string[];
34
34
  isConnectable: boolean;
35
+ isConnected: boolean;
35
36
  }
36
37
 
37
38
  export type ScanCallback = (device: BLEDevice) => void;
@@ -73,6 +74,7 @@ export enum AndroidScanMode {
73
74
  }
74
75
 
75
76
  export type BleNitroManagerOptions = {
77
+ restoreIdentifier?: string;
76
78
  onRestoredState?: RestoreStateCallback;
77
79
  };
78
80
 
@@ -123,16 +125,24 @@ export class BleNitroManager {
123
125
  private _isScanning: boolean = false;
124
126
  private _connectedDevices: { [deviceId: string]: boolean } = {};
125
127
 
126
- private _restoredStateCallback: RestoreStateCallback | null = null;
128
+ private _restoredStateCallback: RestoreStateCallback | null;
127
129
  private _restoredState: BLEDevice[] | null = null;
130
+ private _restoreStateIdentifier: string | null = null;
131
+
132
+ private Instance: NativeBleNitro;
128
133
 
129
134
  constructor(options?: BleNitroManagerOptions) {
130
- this._restoredStateCallback = options?.onRestoredState || null;
131
- BleNitroNative.setRestoreStateCallback((peripherals: NativeBLEDevice[]) => this.onNativeRestoreStateCallback(peripherals));
135
+ this._restoredStateCallback = options?.onRestoredState ?? null;
136
+ this._restoreStateIdentifier = options?.restoreIdentifier ?? null;
137
+ this.Instance = BleNitroNativeFactory.create(options?.restoreIdentifier, (peripherals: NativeBLEDevice[]) => this.onNativeRestoreStateCallback(peripherals));
132
138
  }
133
139
 
134
140
  private onNativeRestoreStateCallback(peripherals: NativeBLEDevice[]) {
141
+ if (!this._restoreStateIdentifier) return;
135
142
  const bleDevices = peripherals.map((peripheral) => convertNativeBleDeviceToBleDevice(peripheral));
143
+ bleDevices.forEach((device) => {
144
+ this._connectedDevices[device.id] = device.isConnected;
145
+ });
136
146
  if (this._restoredStateCallback) {
137
147
  this._restoredStateCallback(bleDevices);
138
148
  } else {
@@ -140,7 +150,13 @@ export class BleNitroManager {
140
150
  }
141
151
  }
142
152
 
153
+ /**
154
+ *
155
+ * Registers callback and returns restored peripheral state in it. Not working from 1.7.x upwards for singleton implementation!
156
+ * @deprecated This method is deprecated and will be removed in 2.x, use onRestoredState option in BleNitroManageroptions instead!
157
+ */
143
158
  public onRestoredState(callback: RestoreStateCallback) {
159
+ if (!this._restoreStateIdentifier) return;
144
160
  if (this._restoredState) {
145
161
  callback(this._restoredState);
146
162
  this._restoredState = null;
@@ -176,7 +192,7 @@ export class BleNitroManager {
176
192
  * Start scanning for Bluetooth devices
177
193
  * @param filter Optional scan filter
178
194
  * @param callback Callback function called when a device is found
179
- * @returns Promise resolving to success state
195
+ * @returns void
180
196
  */
181
197
  public startScan(
182
198
  filter: ScanFilter = {},
@@ -209,29 +225,29 @@ export class BleNitroManager {
209
225
  };
210
226
 
211
227
  // Start scan
212
- BleNitroNative.startScan(nativeFilter, scanCallback);
228
+ this.Instance.startScan(nativeFilter, scanCallback);
213
229
  this._isScanning = true;
214
230
  }
215
231
 
216
232
  /**
217
233
  * Stop scanning for Bluetooth devices
218
- * @returns Promise resolving to success state
234
+ * @returns void
219
235
  */
220
236
  public stopScan(): void {
221
237
  if (!this._isScanning) {
222
238
  return;
223
239
  }
224
240
 
225
- BleNitroNative.stopScan();
241
+ this.Instance.stopScan();
226
242
  this._isScanning = false;
227
243
  }
228
244
 
229
245
  /**
230
246
  * Check if currently scanning for devices
231
- * @returns Promise resolving to scanning state
247
+ * @returns Boolean indicating if currently scanning
232
248
  */
233
249
  public isScanning(): boolean {
234
- this._isScanning = BleNitroNative.isScanning();
250
+ this._isScanning = this.Instance.isScanning();
235
251
  return this._isScanning;
236
252
  }
237
253
 
@@ -241,7 +257,7 @@ export class BleNitroManager {
241
257
  * @returns Array of connected devices
242
258
  */
243
259
  public getConnectedDevices(services?: string[]): BLEDevice[] {
244
- const devices = BleNitroNative.getConnectedDevices(services || []);
260
+ const devices = this.Instance.getConnectedDevices(services || []);
245
261
  // Normalize service UUIDs - manufacturer data already comes as ArrayBuffers
246
262
  return devices.map(device => convertNativeBleDeviceToBleDevice(device));
247
263
  }
@@ -250,11 +266,12 @@ export class BleNitroManager {
250
266
  * Connect to a Bluetooth device
251
267
  * @param deviceId ID of the device to connect to
252
268
  * @param onDisconnect Optional callback for disconnect events
253
- * @returns Promise resolving when connected
269
+ * @returns Promise resolving deviceId when connected
254
270
  */
255
271
  public connect(
256
272
  deviceId: string,
257
- onDisconnect?: DisconnectEventCallback
273
+ onDisconnect?: DisconnectEventCallback,
274
+ autoConnectAndroid?: boolean,
258
275
  ): Promise<string> {
259
276
  return new Promise((resolve, reject) => {
260
277
  // Check if already connected
@@ -263,7 +280,7 @@ export class BleNitroManager {
263
280
  return;
264
281
  }
265
282
 
266
- BleNitroNative.connect(
283
+ this.Instance.connect(
267
284
  deviceId,
268
285
  (success: boolean, connectedDeviceId: string, error: string) => {
269
286
  if (success) {
@@ -277,11 +294,45 @@ export class BleNitroManager {
277
294
  // Remove from connected devices when disconnected
278
295
  delete this._connectedDevices[deviceId];
279
296
  onDisconnect(deviceId, interrupted, error);
280
- } : undefined
297
+ } : undefined,
298
+ autoConnectAndroid ?? false,
281
299
  );
282
300
  });
283
301
  }
284
302
 
303
+ /**
304
+ * Scans for a device and connects to it
305
+ * @param deviceId ID of the device to connect to
306
+ * @param scanTimeout Optional timeout for the scan in milliseconds (default: 5000ms)
307
+ * @returns Promise resolving deviceId when connected
308
+ */
309
+ public findAndConnect(deviceId: string, options?: { scanTimeout?: number, autoConnectAndroid?: boolean, onDisconnect?: DisconnectEventCallback }): Promise<string> {
310
+ const isConnected = this.isConnected(deviceId);
311
+ if (isConnected) {
312
+ return Promise.resolve(deviceId);
313
+ }
314
+ if (this._isScanning) {
315
+ this.stopScan();
316
+ }
317
+ return new Promise((resolve, reject) => {
318
+ const timeoutScan = setTimeout(() => {
319
+ this.stopScan();
320
+ reject(new Error('Scan timed out'));
321
+ }, options?.scanTimeout ?? 5000);
322
+ this.startScan(undefined, (device) => {
323
+ if (device.id === deviceId) {
324
+ this.stopScan();
325
+ clearTimeout(timeoutScan);
326
+ this.connect(deviceId, options?.onDisconnect, options?.autoConnectAndroid).then(async (connectedDeviceId) => {
327
+ resolve(connectedDeviceId);
328
+ }).catch((error) => {
329
+ reject(error);
330
+ });
331
+ }
332
+ });
333
+ });
334
+ }
335
+
285
336
  /**
286
337
  * Disconnect from a Bluetooth device
287
338
  * @param deviceId ID of the device to disconnect from
@@ -295,7 +346,7 @@ export class BleNitroManager {
295
346
  return;
296
347
  }
297
348
 
298
- BleNitroNative.disconnect(
349
+ this.Instance.disconnect(
299
350
  deviceId,
300
351
  (success: boolean, error: string) => {
301
352
  if (success) {
@@ -312,10 +363,10 @@ export class BleNitroManager {
312
363
  /**
313
364
  * Check if connected to a device
314
365
  * @param deviceId ID of the device to check
315
- * @returns Promise resolving to connection state
366
+ * @returns Boolean indicating if device is connected
316
367
  */
317
368
  public isConnected(deviceId: string): boolean {
318
- return BleNitroNative.isConnected(deviceId);
369
+ return this.Instance.isConnected(deviceId);
319
370
  }
320
371
 
321
372
  /**
@@ -326,7 +377,7 @@ export class BleNitroManager {
326
377
  */
327
378
  public requestMTU(deviceId: string, mtu: number): number {
328
379
  mtu = parseInt(mtu.toString(), 10);
329
- const deviceMtu = BleNitroNative.requestMTU(deviceId, mtu);
380
+ const deviceMtu = this.Instance.requestMTU(deviceId, mtu);
330
381
  return deviceMtu;
331
382
  }
332
383
 
@@ -343,7 +394,7 @@ export class BleNitroManager {
343
394
  return;
344
395
  }
345
396
 
346
- BleNitroNative.readRSSI(
397
+ this.Instance.readRSSI(
347
398
  deviceId,
348
399
  (success: boolean, rssi: number, error: string) => {
349
400
  if (success) {
@@ -369,7 +420,7 @@ export class BleNitroManager {
369
420
  return;
370
421
  }
371
422
 
372
- BleNitroNative.discoverServices(
423
+ this.Instance.discoverServices(
373
424
  deviceId,
374
425
  (success: boolean, error: string) => {
375
426
  if (success) {
@@ -400,7 +451,7 @@ export class BleNitroManager {
400
451
  reject(new Error('Failed to discover services'));
401
452
  return;
402
453
  }
403
- const services = BleNitroNative.getServices(deviceId);
454
+ const services = this.Instance.getServices(deviceId);
404
455
  resolve(BleNitroManager.normalizeGattUUIDs(services));
405
456
  });
406
457
  }
@@ -409,7 +460,7 @@ export class BleNitroManager {
409
460
  * Get characteristics for a service
410
461
  * @param deviceId ID of the device
411
462
  * @param serviceId ID of the service
412
- * @returns Promise resolving to array of characteristic UUIDs
463
+ * @returns array of characteristic UUIDs
413
464
  */
414
465
  public getCharacteristics(
415
466
  deviceId: string,
@@ -419,19 +470,37 @@ export class BleNitroManager {
419
470
  throw new Error('Device not connected');
420
471
  }
421
472
 
422
- const characteristics = BleNitroNative.getCharacteristics(
473
+ const characteristics = this.Instance.getCharacteristics(
423
474
  deviceId,
424
475
  BleNitroManager.normalizeGattUUID(serviceId),
425
476
  );
426
477
  return BleNitroManager.normalizeGattUUIDs(characteristics);
427
478
  }
428
479
 
480
+ /**
481
+ * Get services and characteristics for a connected device
482
+ * @param deviceId ID of the device
483
+ * @returns Promise resolving to array of service and characteristic UUIDs
484
+ * @see getServices
485
+ * @see getCharacteristics
486
+ */
487
+ public async getServicesWithCharacteristics(deviceId: string): Promise<{ uuid: string; characteristics: string[] }[]> {
488
+ await this.discoverServices(deviceId);
489
+ const services = await this.getServices(deviceId);
490
+ return services.map((service) => {
491
+ return {
492
+ uuid: service,
493
+ characteristics: this.getCharacteristics(deviceId, service),
494
+ };
495
+ });
496
+ }
497
+
429
498
  /**
430
499
  * Read a characteristic value
431
500
  * @param deviceId ID of the device
432
501
  * @param serviceId ID of the service
433
502
  * @param characteristicId ID of the characteristic
434
- * @returns Promise resolving to the characteristic data as ArrayBuffer
503
+ * @returns Promise resolving to the characteristic data as ByteArray
435
504
  */
436
505
  public readCharacteristic(
437
506
  deviceId: string,
@@ -445,7 +514,7 @@ export class BleNitroManager {
445
514
  return;
446
515
  }
447
516
 
448
- BleNitroNative.readCharacteristic(
517
+ this.Instance.readCharacteristic(
449
518
  deviceId,
450
519
  BleNitroManager.normalizeGattUUID(serviceId),
451
520
  BleNitroManager.normalizeGattUUID(characteristicId),
@@ -483,7 +552,7 @@ export class BleNitroManager {
483
552
  return;
484
553
  }
485
554
 
486
- BleNitroNative.writeCharacteristic(
555
+ this.Instance.writeCharacteristic(
487
556
  deviceId,
488
557
  BleNitroManager.normalizeGattUUID(serviceId),
489
558
  BleNitroManager.normalizeGattUUID(characteristicId),
@@ -508,7 +577,7 @@ export class BleNitroManager {
508
577
  * @param serviceId ID of the service
509
578
  * @param characteristicId ID of the characteristic
510
579
  * @param callback Callback function called when notification is received
511
- * @returns Promise resolving when subscription is complete
580
+ * @returns Subscription
512
581
  */
513
582
  public subscribeToCharacteristic(
514
583
  deviceId: string,
@@ -523,7 +592,7 @@ export class BleNitroManager {
523
592
 
524
593
  let _success = false;
525
594
 
526
- BleNitroNative.subscribeToCharacteristic(
595
+ this.Instance.subscribeToCharacteristic(
527
596
  deviceId,
528
597
  BleNitroManager.normalizeGattUUID(serviceId),
529
598
  BleNitroManager.normalizeGattUUID(characteristicId),
@@ -571,7 +640,7 @@ export class BleNitroManager {
571
640
  return;
572
641
  }
573
642
 
574
- BleNitroNative.unsubscribeFromCharacteristic(
643
+ this.Instance.unsubscribeFromCharacteristic(
575
644
  deviceId,
576
645
  BleNitroManager.normalizeGattUUID(serviceId),
577
646
  BleNitroManager.normalizeGattUUID(characteristicId),
@@ -588,7 +657,7 @@ export class BleNitroManager {
588
657
 
589
658
  /**
590
659
  * Check if Bluetooth is enabled
591
- * @returns Promise resolving to Bluetooth state
660
+ * @returns returns Boolean according to Bluetooth state
592
661
  */
593
662
  public isBluetoothEnabled(): boolean {
594
663
  return this.state() === BLEState.PoweredOn;
@@ -600,7 +669,7 @@ export class BleNitroManager {
600
669
  */
601
670
  public requestBluetoothEnable(): Promise<boolean> {
602
671
  return new Promise((resolve, reject) => {
603
- BleNitroNative.requestBluetoothEnable(
672
+ this.Instance.requestBluetoothEnable(
604
673
  (success: boolean, error: string) => {
605
674
  if (success) {
606
675
  resolve(true);
@@ -614,18 +683,18 @@ export class BleNitroManager {
614
683
 
615
684
  /**
616
685
  * Get the current Bluetooth state
617
- * @returns Promise resolving to Bluetooth state
686
+ * @returns Bluetooth state
618
687
  * @see BLEState
619
688
  */
620
689
  public state(): BLEState {
621
- return mapNativeBLEStateToBLEState(BleNitroNative.state());
690
+ return mapNativeBLEStateToBLEState(this.Instance.state());
622
691
  }
623
692
 
624
693
  /**
625
694
  * Subscribe to Bluetooth state changes
626
695
  * @param callback Callback function called when state changes
627
696
  * @param emitInitial Whether to emit initial state callback
628
- * @returns Promise resolving when subscription is complete
697
+ * @returns Subscription
629
698
  * @see BLEState
630
699
  */
631
700
  public subscribeToStateChange(callback: (state: BLEState) => void, emitInitial = false): Subscription {
@@ -634,13 +703,13 @@ export class BleNitroManager {
634
703
  callback(state);
635
704
  }
636
705
 
637
- BleNitroNative.subscribeToStateChange((nativeState: NativeBLEState) => {
706
+ this.Instance.subscribeToStateChange((nativeState: NativeBLEState) => {
638
707
  callback(mapNativeBLEStateToBLEState(nativeState));
639
708
  });
640
709
 
641
710
  return {
642
711
  remove: () => {
643
- BleNitroNative.unsubscribeFromStateChange();
712
+ this.Instance.unsubscribeFromStateChange();
644
713
  },
645
714
  };
646
715
  }
@@ -650,6 +719,6 @@ export class BleNitroManager {
650
719
  * @returns Promise resolving when settings are opened
651
720
  */
652
721
  public openSettings(): Promise<void> {
653
- return BleNitroNative.openSettings();
722
+ return this.Instance.openSettings();
654
723
  }
655
724
  }
@@ -0,0 +1,17 @@
1
+ import { BleNitroManager } from "./manager";
2
+
3
+ let _instance: BleNitroManager;
4
+
5
+ export class BleNitro extends BleNitroManager {
6
+ /**
7
+ * Get a singleton instance of BleNitro, will create one if it does not exist.
8
+ * Singleton implementation does not allow to use state restoration on iOS!
9
+ * @returns {BleNitroManager} An instance of BleNitro
10
+ */
11
+ public static instance(): BleNitroManager {
12
+ if (!_instance) {
13
+ _instance = new BleNitro();
14
+ }
15
+ return _instance;
16
+ }
17
+ }