react-native-ble-nitro 1.0.0-alpha.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.
Files changed (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +298 -0
  3. package/android/build.gradle +55 -0
  4. package/android/src/main/AndroidManifest.xml +23 -0
  5. package/android/src/main/kotlin/co/zyke/ble/BleNitroBleManager.kt +651 -0
  6. package/android/src/main/kotlin/co/zyke/ble/BleNitroPackage.kt +37 -0
  7. package/ios/BleNitro.podspec +37 -0
  8. package/ios/BleNitroBleManager.swift +509 -0
  9. package/ios/BleNitroModule.swift +31 -0
  10. package/lib/BleManagerCompatFactory.d.ts +53 -0
  11. package/lib/BleManagerCompatFactory.js +191 -0
  12. package/lib/BleManagerFactory.d.ts +12 -0
  13. package/lib/BleManagerFactory.js +22 -0
  14. package/lib/compatibility/constants.d.ts +49 -0
  15. package/lib/compatibility/constants.js +50 -0
  16. package/lib/compatibility/deviceWrapper.d.ts +99 -0
  17. package/lib/compatibility/deviceWrapper.js +259 -0
  18. package/lib/compatibility/enums.d.ts +43 -0
  19. package/lib/compatibility/enums.js +124 -0
  20. package/lib/compatibility/index.d.ts +11 -0
  21. package/lib/compatibility/index.js +12 -0
  22. package/lib/compatibility/serviceData.d.ts +51 -0
  23. package/lib/compatibility/serviceData.js +70 -0
  24. package/lib/errors/BleError.d.ts +59 -0
  25. package/lib/errors/BleError.js +120 -0
  26. package/lib/index.d.ts +7 -0
  27. package/lib/index.js +12 -0
  28. package/lib/specs/BleManager.nitro.d.ts +36 -0
  29. package/lib/specs/BleManager.nitro.js +1 -0
  30. package/lib/specs/Characteristic.nitro.d.ts +26 -0
  31. package/lib/specs/Characteristic.nitro.js +1 -0
  32. package/lib/specs/Descriptor.nitro.d.ts +17 -0
  33. package/lib/specs/Descriptor.nitro.js +1 -0
  34. package/lib/specs/Device.nitro.d.ts +37 -0
  35. package/lib/specs/Device.nitro.js +1 -0
  36. package/lib/specs/Service.nitro.d.ts +19 -0
  37. package/lib/specs/Service.nitro.js +1 -0
  38. package/lib/specs/types.d.ts +228 -0
  39. package/lib/specs/types.js +146 -0
  40. package/lib/utils/base64.d.ts +25 -0
  41. package/lib/utils/base64.js +80 -0
  42. package/lib/utils/index.d.ts +2 -0
  43. package/lib/utils/index.js +2 -0
  44. package/lib/utils/uuid.d.ts +9 -0
  45. package/lib/utils/uuid.js +37 -0
  46. package/nitro.json +15 -0
  47. package/package.json +102 -0
  48. package/plugin/build/index.d.ts +28 -0
  49. package/plugin/build/index.js +29 -0
  50. package/plugin/build/withBleNitro.d.ts +31 -0
  51. package/plugin/build/withBleNitro.js +87 -0
  52. package/react-native.config.js +13 -0
  53. package/src/BleManagerCompatFactory.ts +373 -0
  54. package/src/BleManagerFactory.ts +30 -0
  55. package/src/__tests__/BleManager.test.ts +327 -0
  56. package/src/__tests__/compatibility/deviceWrapper.test.ts +563 -0
  57. package/src/__tests__/compatibility/enums.test.ts +254 -0
  58. package/src/compatibility/constants.ts +71 -0
  59. package/src/compatibility/deviceWrapper.ts +427 -0
  60. package/src/compatibility/enums.ts +160 -0
  61. package/src/compatibility/index.ts +24 -0
  62. package/src/compatibility/serviceData.ts +85 -0
  63. package/src/errors/BleError.ts +193 -0
  64. package/src/index.ts +30 -0
  65. package/src/specs/BleManager.nitro.ts +152 -0
  66. package/src/specs/Characteristic.nitro.ts +61 -0
  67. package/src/specs/Descriptor.nitro.ts +28 -0
  68. package/src/specs/Device.nitro.ts +104 -0
  69. package/src/specs/Service.nitro.ts +64 -0
  70. package/src/specs/types.ts +259 -0
  71. package/src/utils/base64.ts +80 -0
  72. package/src/utils/index.ts +2 -0
  73. package/src/utils/uuid.ts +45 -0
@@ -0,0 +1,427 @@
1
+ /**
2
+ * Device wrapper for compatibility
3
+ *
4
+ * Wraps Nitro Device objects to provide the original react-native-ble-plx API
5
+ */
6
+
7
+ import type { Device as NitroDevice } from '../specs/Device.nitro';
8
+ import type {
9
+ NativeDevice,
10
+ UUID,
11
+ Base64,
12
+ DeviceId,
13
+ TransactionId,
14
+ ConnectionPriority,
15
+ ConnectionOptions,
16
+ ServiceDataEntry,
17
+ NativeService,
18
+ NativeCharacteristic,
19
+ NativeDescriptor,
20
+ CharacteristicSubscriptionType,
21
+ Subscription
22
+ } from '../specs/types';
23
+ import { serviceDataArrayToMap } from './serviceData';
24
+ import {
25
+ normalizeCharacteristicSubscriptionType,
26
+ stateToString,
27
+ characteristicSubscriptionTypeToString
28
+ } from './enums';
29
+
30
+ /**
31
+ * Device wrapper that provides react-native-ble-plx compatibility
32
+ * Maps Nitro device properties to the expected API surface
33
+ */
34
+ export class DeviceWrapper {
35
+ constructor(private nitroDevice: NitroDevice | any) {}
36
+
37
+ // Device identification
38
+ get id(): DeviceId {
39
+ return this.nitroDevice.id;
40
+ }
41
+
42
+ // Map deviceName back to name for compatibility
43
+ get name(): string | null {
44
+ return this.nitroDevice.deviceName || null;
45
+ }
46
+
47
+ get rssi(): number | null {
48
+ return this.nitroDevice.rssi || null;
49
+ }
50
+
51
+ get mtu(): number {
52
+ return this.nitroDevice.mtu;
53
+ }
54
+
55
+ // Advertisement data
56
+ get manufacturerData(): Base64 | null {
57
+ return this.nitroDevice.manufacturerData || null;
58
+ }
59
+
60
+ get rawScanRecord(): Base64 {
61
+ return this.nitroDevice.rawScanRecord;
62
+ }
63
+
64
+ // Convert ServiceDataEntry[] back to { [uuid: string]: Base64 }
65
+ get serviceData(): { [uuid: string]: Base64 } | null {
66
+ return serviceDataArrayToMap(this.nitroDevice.serviceData || null);
67
+ }
68
+
69
+ get serviceUUIDs(): UUID[] | null {
70
+ return this.nitroDevice.serviceUUIDs || null;
71
+ }
72
+
73
+ get localName(): string | null {
74
+ return this.nitroDevice.localName || null;
75
+ }
76
+
77
+ get txPowerLevel(): number | null {
78
+ return this.nitroDevice.txPowerLevel || null;
79
+ }
80
+
81
+ get solicitedServiceUUIDs(): UUID[] | null {
82
+ return this.nitroDevice.solicitedServiceUUIDs || null;
83
+ }
84
+
85
+ get isConnectable(): boolean | null {
86
+ return this.nitroDevice.isConnectable || null;
87
+ }
88
+
89
+ get overflowServiceUUIDs(): UUID[] | null {
90
+ return this.nitroDevice.overflowServiceUUIDs || null;
91
+ }
92
+
93
+ // Connection management methods
94
+ async requestConnectionPriority(
95
+ connectionPriority: ConnectionPriority,
96
+ transactionId?: TransactionId
97
+ ): Promise<DeviceWrapper> {
98
+ const result = await this.nitroDevice.requestConnectionPriority(connectionPriority, transactionId);
99
+ return new DeviceWrapper(result);
100
+ }
101
+
102
+ async readRSSI(transactionId?: TransactionId): Promise<DeviceWrapper> {
103
+ const result = await this.nitroDevice.readRSSI(transactionId);
104
+ return new DeviceWrapper(result);
105
+ }
106
+
107
+ async requestMTU(mtu: number, transactionId?: TransactionId): Promise<DeviceWrapper> {
108
+ const result = await this.nitroDevice.requestMTU(mtu, transactionId);
109
+ return new DeviceWrapper(result);
110
+ }
111
+
112
+ async connect(options?: Partial<ConnectionOptions>): Promise<DeviceWrapper> {
113
+ // Provide defaults for required fields in Nitro interface
114
+ const connectionOptions: ConnectionOptions = {
115
+ autoConnect: options?.autoConnect ?? false,
116
+ requestMTU: options?.requestMTU ?? 23,
117
+ timeout: options?.timeout ?? 0,
118
+ };
119
+
120
+ const result = await this.nitroDevice.connect(connectionOptions);
121
+ return new DeviceWrapper(result);
122
+ }
123
+
124
+ async cancelConnection(): Promise<DeviceWrapper> {
125
+ const result = await this.nitroDevice.cancelConnection();
126
+ return new DeviceWrapper(result);
127
+ }
128
+
129
+ async isConnected(): Promise<boolean> {
130
+ return await this.nitroDevice.isConnected();
131
+ }
132
+
133
+ onDisconnected(listener: (error: any | null, device: DeviceWrapper) => void): Subscription {
134
+ return this.nitroDevice.onDisconnected((error: any, device: any) => {
135
+ listener(error, new DeviceWrapper(device));
136
+ });
137
+ }
138
+
139
+ // Service discovery
140
+ async discoverAllServicesAndCharacteristics(transactionId?: TransactionId): Promise<DeviceWrapper> {
141
+ const result = await this.nitroDevice.discoverAllServicesAndCharacteristics(transactionId);
142
+ return new DeviceWrapper(result);
143
+ }
144
+
145
+ async services(): Promise<ServiceWrapper[]> {
146
+ const services = await this.nitroDevice.services();
147
+ return services.map((service: any) => new ServiceWrapper(service, this.nitroDevice));
148
+ }
149
+
150
+ // Characteristic operations
151
+ async characteristicsForService(serviceUUID: UUID): Promise<CharacteristicWrapper[]> {
152
+ const characteristics = await this.nitroDevice.characteristicsForService(serviceUUID);
153
+ return characteristics.map((char: any) => new CharacteristicWrapper(char, this.nitroDevice));
154
+ }
155
+
156
+ async readCharacteristicForService(
157
+ serviceUUID: UUID,
158
+ characteristicUUID: UUID,
159
+ transactionId?: TransactionId
160
+ ): Promise<CharacteristicWrapper> {
161
+ const result = await this.nitroDevice.readCharacteristicForService(
162
+ serviceUUID,
163
+ characteristicUUID,
164
+ transactionId
165
+ );
166
+ return new CharacteristicWrapper(result, this.nitroDevice);
167
+ }
168
+
169
+ async writeCharacteristicWithResponseForService(
170
+ serviceUUID: UUID,
171
+ characteristicUUID: UUID,
172
+ valueBase64: Base64,
173
+ transactionId?: TransactionId
174
+ ): Promise<CharacteristicWrapper> {
175
+ const result = await this.nitroDevice.writeCharacteristicWithResponseForService(
176
+ serviceUUID,
177
+ characteristicUUID,
178
+ valueBase64,
179
+ transactionId
180
+ );
181
+ return new CharacteristicWrapper(result, this.nitroDevice);
182
+ }
183
+
184
+ async writeCharacteristicWithoutResponseForService(
185
+ serviceUUID: UUID,
186
+ characteristicUUID: UUID,
187
+ valueBase64: Base64,
188
+ transactionId?: TransactionId
189
+ ): Promise<CharacteristicWrapper> {
190
+ const result = await this.nitroDevice.writeCharacteristicWithoutResponseForService(
191
+ serviceUUID,
192
+ characteristicUUID,
193
+ valueBase64,
194
+ transactionId
195
+ );
196
+ return new CharacteristicWrapper(result, this.nitroDevice);
197
+ }
198
+
199
+ monitorCharacteristicForService(
200
+ serviceUUID: UUID,
201
+ characteristicUUID: UUID,
202
+ listener: (error: any | null, characteristic: CharacteristicWrapper | null) => void,
203
+ transactionId?: TransactionId,
204
+ subscriptionType?: 'notification' | 'indication'
205
+ ): Subscription {
206
+ const nitroSubscriptionType = subscriptionType
207
+ ? normalizeCharacteristicSubscriptionType(subscriptionType)
208
+ : undefined;
209
+
210
+ return this.nitroDevice.monitorCharacteristicForService(
211
+ serviceUUID,
212
+ characteristicUUID,
213
+ (error: any, characteristic: any) => {
214
+ listener(
215
+ error,
216
+ characteristic ? new CharacteristicWrapper(characteristic, this.nitroDevice) : null
217
+ );
218
+ },
219
+ transactionId,
220
+ nitroSubscriptionType
221
+ );
222
+ }
223
+
224
+ // Descriptor operations
225
+ async descriptorsForService(
226
+ serviceUUID: UUID,
227
+ characteristicUUID: UUID
228
+ ): Promise<DescriptorWrapper[]> {
229
+ const descriptors = await this.nitroDevice.descriptorsForService(serviceUUID, characteristicUUID);
230
+ return descriptors.map((desc: any) => new DescriptorWrapper(desc, this.nitroDevice));
231
+ }
232
+
233
+ async readDescriptorForService(
234
+ serviceUUID: UUID,
235
+ characteristicUUID: UUID,
236
+ descriptorUUID: UUID,
237
+ transactionId?: TransactionId
238
+ ): Promise<DescriptorWrapper> {
239
+ const result = await this.nitroDevice.readDescriptorForService(
240
+ serviceUUID,
241
+ characteristicUUID,
242
+ descriptorUUID,
243
+ transactionId
244
+ );
245
+ return new DescriptorWrapper(result, this.nitroDevice);
246
+ }
247
+
248
+ async writeDescriptorForService(
249
+ serviceUUID: UUID,
250
+ characteristicUUID: UUID,
251
+ descriptorUUID: UUID,
252
+ valueBase64: Base64,
253
+ transactionId?: TransactionId
254
+ ): Promise<DescriptorWrapper> {
255
+ const result = await this.nitroDevice.writeDescriptorForService(
256
+ serviceUUID,
257
+ characteristicUUID,
258
+ descriptorUUID,
259
+ valueBase64,
260
+ transactionId
261
+ );
262
+ return new DescriptorWrapper(result, this.nitroDevice);
263
+ }
264
+ }
265
+
266
+ /**
267
+ * Service wrapper for compatibility
268
+ */
269
+ export class ServiceWrapper {
270
+ constructor(
271
+ private nativeService: NativeService,
272
+ private nitroDevice: NitroDevice
273
+ ) {}
274
+
275
+ get id(): number {
276
+ return this.nativeService.id;
277
+ }
278
+
279
+ get uuid(): UUID {
280
+ return this.nativeService.uuid;
281
+ }
282
+
283
+ get deviceID(): DeviceId {
284
+ return this.nativeService.deviceID;
285
+ }
286
+
287
+ get isPrimary(): boolean {
288
+ return this.nativeService.isPrimary;
289
+ }
290
+
291
+ // Delegate to device methods
292
+ async characteristics(): Promise<CharacteristicWrapper[]> {
293
+ const device = new DeviceWrapper(this.nitroDevice);
294
+ return await device.characteristicsForService(this.uuid);
295
+ }
296
+
297
+ async readCharacteristic(
298
+ characteristicUUID: UUID,
299
+ transactionId?: TransactionId
300
+ ): Promise<CharacteristicWrapper> {
301
+ const device = new DeviceWrapper(this.nitroDevice);
302
+ return await device.readCharacteristicForService(this.uuid, characteristicUUID, transactionId);
303
+ }
304
+
305
+ // ... other service methods would delegate similarly
306
+ }
307
+
308
+ /**
309
+ * Characteristic wrapper for compatibility
310
+ */
311
+ export class CharacteristicWrapper {
312
+ constructor(
313
+ private nativeCharacteristic: NativeCharacteristic,
314
+ private nitroDevice: NitroDevice
315
+ ) {}
316
+
317
+ get id(): number {
318
+ return this.nativeCharacteristic.id;
319
+ }
320
+
321
+ get uuid(): UUID {
322
+ return this.nativeCharacteristic.uuid;
323
+ }
324
+
325
+ get serviceID(): number {
326
+ return this.nativeCharacteristic.serviceID;
327
+ }
328
+
329
+ get serviceUUID(): UUID {
330
+ return this.nativeCharacteristic.serviceUUID;
331
+ }
332
+
333
+ get deviceID(): DeviceId {
334
+ return this.nativeCharacteristic.deviceID;
335
+ }
336
+
337
+ get isReadable(): boolean {
338
+ return this.nativeCharacteristic.isReadable;
339
+ }
340
+
341
+ get isWritableWithResponse(): boolean {
342
+ return this.nativeCharacteristic.isWritableWithResponse;
343
+ }
344
+
345
+ get isWritableWithoutResponse(): boolean {
346
+ return this.nativeCharacteristic.isWritableWithoutResponse;
347
+ }
348
+
349
+ get isNotifiable(): boolean {
350
+ return this.nativeCharacteristic.isNotifiable;
351
+ }
352
+
353
+ get isNotifying(): boolean {
354
+ return this.nativeCharacteristic.isNotifying;
355
+ }
356
+
357
+ get isIndicatable(): boolean {
358
+ return this.nativeCharacteristic.isIndicatable;
359
+ }
360
+
361
+ get value(): Base64 | null {
362
+ return this.nativeCharacteristic.value;
363
+ }
364
+
365
+ // Delegate to device methods
366
+ async read(transactionId?: TransactionId): Promise<CharacteristicWrapper> {
367
+ const device = new DeviceWrapper(this.nitroDevice);
368
+ return await device.readCharacteristicForService(this.serviceUUID, this.uuid, transactionId);
369
+ }
370
+
371
+ // ... other characteristic methods would delegate similarly
372
+ }
373
+
374
+ /**
375
+ * Descriptor wrapper for compatibility
376
+ */
377
+ export class DescriptorWrapper {
378
+ constructor(
379
+ private nativeDescriptor: NativeDescriptor,
380
+ private nitroDevice: NitroDevice
381
+ ) {}
382
+
383
+ get id(): number {
384
+ return this.nativeDescriptor.id;
385
+ }
386
+
387
+ get uuid(): UUID {
388
+ return this.nativeDescriptor.uuid;
389
+ }
390
+
391
+ get characteristicID(): number {
392
+ return this.nativeDescriptor.characteristicID;
393
+ }
394
+
395
+ get characteristicUUID(): UUID {
396
+ return this.nativeDescriptor.characteristicUUID;
397
+ }
398
+
399
+ get serviceID(): number {
400
+ return this.nativeDescriptor.serviceID;
401
+ }
402
+
403
+ get serviceUUID(): UUID {
404
+ return this.nativeDescriptor.serviceUUID;
405
+ }
406
+
407
+ get deviceID(): DeviceId {
408
+ return this.nativeDescriptor.deviceID;
409
+ }
410
+
411
+ get value(): Base64 | null {
412
+ return this.nativeDescriptor.value;
413
+ }
414
+
415
+ // Delegate to device methods
416
+ async read(transactionId?: TransactionId): Promise<DescriptorWrapper> {
417
+ const device = new DeviceWrapper(this.nitroDevice);
418
+ return await device.readDescriptorForService(
419
+ this.serviceUUID,
420
+ this.characteristicUUID,
421
+ this.uuid,
422
+ transactionId
423
+ );
424
+ }
425
+
426
+ // ... other descriptor methods would delegate similarly
427
+ }
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Enum compatibility layer
3
+ *
4
+ * Provides conversion between Nitro's numeric enums and the original
5
+ * string-based enums from react-native-ble-plx
6
+ */
7
+
8
+ import {
9
+ State,
10
+ LogLevel,
11
+ CharacteristicSubscriptionType,
12
+ RefreshGattMoment,
13
+ } from '../specs/types';
14
+
15
+ // String mappings for backward compatibility
16
+ export const StateString = {
17
+ [State.Unknown]: 'Unknown',
18
+ [State.Resetting]: 'Resetting',
19
+ [State.Unsupported]: 'Unsupported',
20
+ [State.Unauthorized]: 'Unauthorized',
21
+ [State.PoweredOff]: 'PoweredOff',
22
+ [State.PoweredOn]: 'PoweredOn',
23
+ } as const;
24
+
25
+ export const LogLevelString = {
26
+ [LogLevel.None]: 'None',
27
+ [LogLevel.Verbose]: 'Verbose',
28
+ [LogLevel.Debug]: 'Debug',
29
+ [LogLevel.Info]: 'Info',
30
+ [LogLevel.Warning]: 'Warning',
31
+ [LogLevel.Error]: 'Error',
32
+ } as const;
33
+
34
+ export const CharacteristicSubscriptionTypeString = {
35
+ [CharacteristicSubscriptionType.Notification]: 'notification',
36
+ [CharacteristicSubscriptionType.Indication]: 'indication',
37
+ } as const;
38
+
39
+ export const RefreshGattMomentString = {
40
+ [RefreshGattMoment.OnConnected]: 'OnConnected',
41
+ } as const;
42
+
43
+ // Reverse mappings for converting strings back to numeric enums
44
+ const StringToState: { [key: string]: State } = {};
45
+ const StringToLogLevel: { [key: string]: LogLevel } = {};
46
+ const StringToCharacteristicSubscriptionType: { [key: string]: CharacteristicSubscriptionType } = {};
47
+ const StringToRefreshGattMoment: { [key: string]: RefreshGattMoment } = {};
48
+
49
+ // Build reverse mappings
50
+ Object.entries(StateString).forEach(([num, str]) => {
51
+ StringToState[str] = parseInt(num) as State;
52
+ });
53
+
54
+ Object.entries(LogLevelString).forEach(([num, str]) => {
55
+ StringToLogLevel[str] = parseInt(num) as LogLevel;
56
+ });
57
+
58
+ Object.entries(CharacteristicSubscriptionTypeString).forEach(([num, str]) => {
59
+ StringToCharacteristicSubscriptionType[str] = parseInt(num) as CharacteristicSubscriptionType;
60
+ });
61
+
62
+ Object.entries(RefreshGattMomentString).forEach(([num, str]) => {
63
+ StringToRefreshGattMoment[str] = parseInt(num) as RefreshGattMoment;
64
+ });
65
+
66
+ // Conversion functions
67
+ export function stateToString(state: State): string {
68
+ return StateString[state] ?? 'Unknown';
69
+ }
70
+
71
+ export function stringToState(stateString: string): State {
72
+ // Handle case insensitive lookup
73
+ const lowerString = stateString.toLowerCase();
74
+ for (const [key, value] of Object.entries(StringToState)) {
75
+ if (key.toLowerCase() === lowerString) {
76
+ return value;
77
+ }
78
+ }
79
+ return State.Unknown;
80
+ }
81
+
82
+ export function logLevelToString(logLevel: LogLevel): string {
83
+ return LogLevelString[logLevel] ?? 'None';
84
+ }
85
+
86
+ export function stringToLogLevel(logLevelString: string): LogLevel {
87
+ // Handle case insensitive lookup
88
+ const lowerString = logLevelString.toLowerCase();
89
+ for (const [key, value] of Object.entries(StringToLogLevel)) {
90
+ if (key.toLowerCase() === lowerString) {
91
+ return value;
92
+ }
93
+ }
94
+ return LogLevel.None;
95
+ }
96
+
97
+ export function characteristicSubscriptionTypeToString(
98
+ type: CharacteristicSubscriptionType
99
+ ): string {
100
+ return CharacteristicSubscriptionTypeString[type] ?? 'notification';
101
+ }
102
+
103
+ export function stringToCharacteristicSubscriptionType(
104
+ typeString: string
105
+ ): CharacteristicSubscriptionType {
106
+ // Handle case insensitive lookup
107
+ const lowerString = typeString.toLowerCase();
108
+ for (const [key, value] of Object.entries(StringToCharacteristicSubscriptionType)) {
109
+ if (key.toLowerCase() === lowerString) {
110
+ return value;
111
+ }
112
+ }
113
+ return CharacteristicSubscriptionType.Notification;
114
+ }
115
+
116
+ export function refreshGattMomentToString(moment: RefreshGattMoment): 'OnConnected' {
117
+ return RefreshGattMomentString[moment] as 'OnConnected';
118
+ }
119
+
120
+ export function stringToRefreshGattMoment(momentString: 'OnConnected'): RefreshGattMoment {
121
+ return StringToRefreshGattMoment[momentString] ?? RefreshGattMoment.OnConnected;
122
+ }
123
+
124
+ // Helper function to detect if a value is a string enum vs numeric enum
125
+ export function isStringEnumValue(value: any): boolean {
126
+ return typeof value === 'string';
127
+ }
128
+
129
+ // Generic converter that handles both string and numeric enum values
130
+ export function normalizeState(state: State | string): State {
131
+ if (typeof state === 'string') {
132
+ return stringToState(state);
133
+ }
134
+ return state;
135
+ }
136
+
137
+ export function normalizeLogLevel(logLevel: LogLevel | string): LogLevel {
138
+ if (typeof logLevel === 'string') {
139
+ return stringToLogLevel(logLevel);
140
+ }
141
+ return logLevel;
142
+ }
143
+
144
+ export function normalizeCharacteristicSubscriptionType(
145
+ type: CharacteristicSubscriptionType | 'notification' | 'indication'
146
+ ): CharacteristicSubscriptionType {
147
+ if (typeof type === 'string') {
148
+ return stringToCharacteristicSubscriptionType(type);
149
+ }
150
+ return type;
151
+ }
152
+
153
+ export function normalizeRefreshGattMoment(
154
+ moment: RefreshGattMoment | 'OnConnected'
155
+ ): RefreshGattMoment {
156
+ if (typeof moment === 'string') {
157
+ return stringToRefreshGattMoment(moment);
158
+ }
159
+ return moment;
160
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Compatibility layer for react-native-ble-nitro
3
+ *
4
+ * This module provides compatibility shims and converters to maintain
5
+ * 100% API compatibility with react-native-ble-plx while working with
6
+ * Nitro's type system constraints.
7
+ */
8
+
9
+ export * from './serviceData';
10
+ export * from './deviceWrapper';
11
+ export * from './constants';
12
+
13
+ // Explicitly export enum utilities to avoid conflicts
14
+ export {
15
+ stateToString,
16
+ stringToState,
17
+ logLevelToString,
18
+ stringToLogLevel,
19
+ characteristicSubscriptionTypeToString,
20
+ stringToCharacteristicSubscriptionType,
21
+ normalizeState,
22
+ normalizeLogLevel,
23
+ normalizeCharacteristicSubscriptionType,
24
+ } from './enums';
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Service Data compatibility layer
3
+ *
4
+ * Provides conversion between Nitro's structured ServiceDataEntry[] format
5
+ * and the original { [uuid: string]: Base64 } format from react-native-ble-plx
6
+ */
7
+
8
+ import type { ServiceDataEntry, UUID, Base64 } from '../specs/types';
9
+
10
+ /**
11
+ * Convert ServiceDataEntry array to the original index signature format
12
+ */
13
+ export function serviceDataArrayToMap(entries: ServiceDataEntry[] | null): { [uuid: string]: Base64 } | null {
14
+ if (!entries || entries.length === 0) {
15
+ return null;
16
+ }
17
+
18
+ const result: { [uuid: string]: Base64 } = {};
19
+ entries.forEach(entry => {
20
+ result[entry.uuid] = entry.data;
21
+ });
22
+ return result;
23
+ }
24
+
25
+ /**
26
+ * Convert the original index signature format to ServiceDataEntry array
27
+ */
28
+ export function serviceDataMapToArray(map: { [uuid: string]: Base64 } | null): ServiceDataEntry[] | null {
29
+ if (!map || Object.keys(map).length === 0) {
30
+ return null;
31
+ }
32
+
33
+ return Object.entries(map).map(([uuid, data]) => ({
34
+ uuid: uuid as UUID,
35
+ data,
36
+ }));
37
+ }
38
+
39
+ /**
40
+ * Merge two service data maps (used in device updates)
41
+ */
42
+ export function mergeServiceDataMaps(
43
+ existing: { [uuid: string]: Base64 } | null,
44
+ updates: { [uuid: string]: Base64 } | null
45
+ ): { [uuid: string]: Base64 } | null {
46
+ if (!existing && !updates) return null;
47
+ if (!existing) return updates;
48
+ if (!updates) return existing;
49
+
50
+ return { ...existing, ...updates };
51
+ }
52
+
53
+ /**
54
+ * Merge two service data arrays (used in native updates)
55
+ */
56
+ export function mergeServiceDataArrays(
57
+ existing: ServiceDataEntry[] | null,
58
+ updates: ServiceDataEntry[] | null
59
+ ): ServiceDataEntry[] | null {
60
+ const existingMap = serviceDataArrayToMap(existing);
61
+ const updatesMap = serviceDataArrayToMap(updates);
62
+ const mergedMap = mergeServiceDataMaps(existingMap, updatesMap);
63
+ return serviceDataMapToArray(mergedMap);
64
+ }
65
+
66
+ /**
67
+ * Check if service data contains a specific service UUID
68
+ */
69
+ export function hasServiceUUID(serviceData: { [uuid: string]: Base64 } | null, uuid: UUID): boolean {
70
+ return serviceData ? uuid in serviceData : false;
71
+ }
72
+
73
+ /**
74
+ * Get service data for a specific UUID
75
+ */
76
+ export function getServiceData(serviceData: { [uuid: string]: Base64 } | null, uuid: UUID): Base64 | null {
77
+ return serviceData?.[uuid] || null;
78
+ }
79
+
80
+ /**
81
+ * Get all service UUIDs from service data
82
+ */
83
+ export function getServiceUUIDs(serviceData: { [uuid: string]: Base64 } | null): UUID[] {
84
+ return serviceData ? Object.keys(serviceData) as UUID[] : [];
85
+ }