react-native-flic2 2.0.0-beta.2 → 2.0.0-beta.21

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/Flic2.podspec CHANGED
@@ -13,10 +13,20 @@ Pod::Spec.new do |s|
13
13
  s.platforms = { :ios => min_ios_version_supported }
14
14
  s.source = { :git => "https://github.com/X-Guard/react-native-flic2.git", :tag => "#{s.version}" }
15
15
 
16
- s.source_files = "ios/**/*.{h,m,mm,cpp}"
17
- s.private_header_files = "ios/**/*.h"
16
+ install_modules_dependencies(s)
18
17
 
19
- s.ios.vendored_frameworks = 'ios/flic2lib.framework'
18
+ # Keep the root spec codegen-only so consumers can opt into
19
+ # Device/Simulator implementations explicitly.
20
+ s.default_subspecs = []
20
21
 
21
- install_modules_dependencies(s)
22
+ s.subspec "Device" do |sp|
23
+ sp.source_files = "ios/Flic2.{h,mm}"
24
+ sp.private_header_files = "ios/Flic2.h"
25
+ sp.ios.vendored_frameworks = "ios/flic2lib.framework"
26
+ end
27
+
28
+ s.subspec "Simulator" do |sp|
29
+ sp.source_files = "ios/Flic2SimulatorStub.{h,mm}"
30
+ sp.private_header_files = "ios/Flic2SimulatorStub.h"
31
+ end
22
32
  end
@@ -0,0 +1,21 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "Flic2Device"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["homepage"]
10
+ s.license = package["license"]
11
+ s.authors = package["author"]
12
+
13
+ s.platforms = { :ios => min_ios_version_supported }
14
+ s.source = { :git => "https://github.com/X-Guard/react-native-flic2.git", :tag => "#{s.version}" }
15
+
16
+ install_modules_dependencies(s)
17
+
18
+ s.source_files = "ios/Flic2.{h,mm}"
19
+ s.private_header_files = "ios/Flic2.h"
20
+ s.ios.vendored_frameworks = "ios/flic2lib.framework"
21
+ end
@@ -0,0 +1,20 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "Flic2Simulator"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["homepage"]
10
+ s.license = package["license"]
11
+ s.authors = package["author"]
12
+
13
+ s.platforms = { :ios => min_ios_version_supported }
14
+ s.source = { :git => "https://github.com/X-Guard/react-native-flic2.git", :tag => "#{s.version}" }
15
+
16
+ install_modules_dependencies(s)
17
+
18
+ s.source_files = "ios/Flic2SimulatorStub.{h,mm}"
19
+ s.private_header_files = "ios/Flic2SimulatorStub.h"
20
+ end
package/README.md CHANGED
@@ -37,6 +37,15 @@ npm install react-native-flic2
37
37
  cd ios && pod install && cd ..
38
38
  ```
39
39
 
40
+ **Need simulator builds?**
41
+ See `Troubleshooting` -> `Running on iOS Simulator` for a full configuration
42
+ example and launch commands.
43
+
44
+ **Important (iOS pod wiring):**
45
+ The autolinked root `Flic2` pod is codegen-only in the 2.x beta setup.
46
+ To get a real iOS runtime implementation, add `Flic2Device` in your Podfile
47
+ (and `Flic2Simulator` for simulator-specific configurations).
48
+
40
49
  2. **Add Bluetooth permissions to `Info.plist`:**
41
50
  Add the following keys to your `ios/YourApp/Info.plist`:
42
51
  ```xml
@@ -66,6 +75,65 @@ The library automatically includes the necessary permissions in `AndroidManifest
66
75
 
67
76
  You'll need to request these permissions before scanning for buttons. Use a library like `react-native-permissions` or implement permission requests manually.
68
77
 
78
+ #### Customizing the Foreground Service Notification (Android)
79
+
80
+ The library runs a foreground service to keep Flic2 buttons connected in the background. You can customize the notification appearance by adding metadata to your app's `AndroidManifest.xml`:
81
+
82
+ ```xml
83
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
84
+ <application>
85
+ <!-- Your existing application configuration -->
86
+
87
+ <!-- Customize Flic2 foreground service notification -->
88
+ <meta-data
89
+ android:name="nl.xguard.flic2.notification_title"
90
+ android:value="My Flic2 Service" />
91
+ <meta-data
92
+ android:name="nl.xguard.flic2.notification_text"
93
+ android:value="Flic2 buttons are active" />
94
+ <meta-data
95
+ android:name="nl.xguard.flic2.notification_icon"
96
+ android:resource="@drawable/ic_notification" />
97
+ <meta-data
98
+ android:name="nl.xguard.flic2.notification_channel_name"
99
+ android:value="Flic2 Notifications" />
100
+ <meta-data
101
+ android:name="nl.xguard.flic2.notification_channel_description"
102
+ android:value="Notifications for Flic2 button connections" />
103
+ <meta-data
104
+ android:name="nl.xguard.flic2.notification_id"
105
+ android:value="123321" />
106
+ <meta-data
107
+ android:name="nl.xguard.flic2.notification_channel_id"
108
+ android:value="my_custom_channel_id" />
109
+ </application>
110
+ </manifest>
111
+ ```
112
+
113
+ **Available Configuration Options:**
114
+
115
+ - `nl.xguard.flic2.notification_title` - Notification title (default: "Flic 2")
116
+ - `nl.xguard.flic2.notification_text` - Notification text (default: "Flic 2 service is running")
117
+ - `nl.xguard.flic2.notification_icon` - Notification icon resource ID (default: system info icon)
118
+ - Use `@drawable/your_icon_name` or `@mipmap/your_icon_name` format
119
+ - `nl.xguard.flic2.notification_channel_name` - Notification channel name (default: "Flic2Channel")
120
+ - `nl.xguard.flic2.notification_channel_description` - Notification channel description (default: "Flic2Channel")
121
+ - `nl.xguard.flic2.notification_id` - Notification ID integer (default: 123321)
122
+ - `nl.xguard.flic2.notification_channel_id` - Notification channel ID string (default: "Notification_Channel_Flic2Service")
123
+
124
+ **Example with Custom Icon:**
125
+
126
+ 1. Add your notification icon to `android/app/src/main/res/drawable/` (e.g., `ic_flic2_notification.png`)
127
+
128
+ 2. Add metadata to `AndroidManifest.xml`:
129
+ ```xml
130
+ <meta-data
131
+ android:name="nl.xguard.flic2.notification_icon"
132
+ android:resource="@drawable/ic_flic2_notification" />
133
+ ```
134
+
135
+ **Note:** The notification icon must be a white/transparent icon suitable for Android notifications. If you don't specify a custom icon, the system default info icon will be used.
136
+
69
137
  ## Basic Usage
70
138
 
71
139
  ### 1. Initialize the Library (Global Setup)
@@ -390,7 +458,7 @@ export default Flic2Example;
390
458
 
391
459
  ### Initialization
392
460
 
393
- #### `initialize(): Promise<boolean>`
461
+ #### `initialize(): Promise<void>`
394
462
 
395
463
  Initialize the Flic2 manager. This must be called before using any other methods.
396
464
 
@@ -400,7 +468,7 @@ await Flic2.initialize();
400
468
 
401
469
  ### Scanning
402
470
 
403
- #### `startScan(): Promise<{ success: boolean; message: string }>`
471
+ #### `startScan(): Promise<void>`
404
472
 
405
473
  Start scanning for new Flic2 buttons. The scan will emit `scanStatusChange` events.
406
474
 
@@ -408,7 +476,7 @@ Start scanning for new Flic2 buttons. The scan will emit `scanStatusChange` even
408
476
  await Flic2.startScan();
409
477
  ```
410
478
 
411
- #### `stopScan(): Promise<{ success: boolean; message: string }>`
479
+ #### `stopScan(): Promise<void>`
412
480
 
413
481
  Stop an ongoing scan.
414
482
 
@@ -442,7 +510,7 @@ Get a specific button by UUID.
442
510
  const button = await Flic2.getButton('button-uuid');
443
511
  ```
444
512
 
445
- #### `connectAllKnownButtons(): Promise<{ success: boolean; message: string }>`
513
+ #### `connectAllKnownButtons(): Promise<void>`
446
514
 
447
515
  Connect to all previously known buttons.
448
516
 
@@ -450,7 +518,7 @@ Connect to all previously known buttons.
450
518
  await Flic2.connectAllKnownButtons();
451
519
  ```
452
520
 
453
- #### `disconnectAllKnownButtons(): Promise<{ success: boolean; message: string }>`
521
+ #### `disconnectAllKnownButtons(): Promise<void>`
454
522
 
455
523
  Disconnect all connected buttons.
456
524
 
@@ -458,7 +526,7 @@ Disconnect all connected buttons.
458
526
  await Flic2.disconnectAllKnownButtons();
459
527
  ```
460
528
 
461
- #### `forgetButton(uuid: string): Promise<{ success: boolean; message: string }>`
529
+ #### `forgetButton(uuid: string): Promise<void>`
462
530
 
463
531
  Forget (unpair) a specific button.
464
532
 
@@ -466,7 +534,7 @@ Forget (unpair) a specific button.
466
534
  await Flic2.forgetButton('button-uuid');
467
535
  ```
468
536
 
469
- #### `forgetAllButtons(): Promise<{ success: boolean; message: string }>`
537
+ #### `forgetAllButtons(): Promise<void>`
470
538
 
471
539
  Forget all buttons.
472
540
 
@@ -476,50 +544,54 @@ await Flic2.forgetAllButtons();
476
544
 
477
545
  ### Button Configuration
478
546
 
479
- #### `buttonConnect(uuid: string): Promise<{ success: boolean; message: string }>`
547
+ #### `buttonConnect(uuid: string): Promise<FlicButton>`
480
548
 
481
- Connect to a specific button.
549
+ Connect to a specific button. Returns the button object.
482
550
 
483
551
  ```tsx
484
- await Flic2.buttonConnect('button-uuid');
552
+ const button = await Flic2.buttonConnect('button-uuid');
485
553
  ```
486
554
 
487
- #### `buttonDisconnect(uuid: string): Promise<{ success: boolean; message: string }>`
555
+ #### `buttonDisconnect(uuid: string): Promise<FlicButton>`
488
556
 
489
- Disconnect a specific button.
557
+ Disconnect a specific button. Returns the button object.
490
558
 
491
559
  ```tsx
492
- await Flic2.buttonDisconnect('button-uuid');
560
+ const button = await Flic2.buttonDisconnect('button-uuid');
493
561
  ```
494
562
 
495
- #### `buttonSetNickname(uuid: string, nickname: string): Promise<{ success: boolean; message: string }>`
563
+ #### `buttonSetNickname(uuid: string, nickname: string): Promise<FlicButton>`
496
564
 
497
- Set a custom nickname for a button.
565
+ Set a custom nickname for a button. Returns the updated button object.
498
566
 
499
567
  ```tsx
500
- await Flic2.buttonSetNickname('button-uuid', 'My Button');
568
+ const button = await Flic2.buttonSetNickname('button-uuid', 'My Button');
501
569
  ```
502
570
 
503
- #### `buttonSetTriggerMode(uuid: string, mode: TriggerModeType): Promise<{ success: boolean; message: string }>`
571
+ #### `buttonSetTriggerMode(uuid: string, mode: TriggerModeType): Promise<FlicButton>`
504
572
 
505
- Set the trigger mode for a button. Modes:
573
+ Set the trigger mode for a button. Returns the updated button object. Modes:
506
574
  - `0`: Click and Hold
507
575
  - `1`: Click and Double Click
508
576
  - `2`: Click and Double Click and Hold
509
577
  - `3`: Click only
510
578
 
579
+ **Note:** This method is only supported on iOS. On Android, it will reject with an error.
580
+
511
581
  ```tsx
512
- await Flic2.buttonSetTriggerMode('button-uuid', 3); // Click only
582
+ const button = await Flic2.buttonSetTriggerMode('button-uuid', 3); // Click only
513
583
  ```
514
584
 
515
- #### `buttonSetLatencyMode(uuid: string, mode: LatencyModeType): Promise<{ success: boolean; message: string }>`
585
+ #### `buttonSetLatencyMode(uuid: string, mode: LatencyModeType): Promise<FlicButton>`
516
586
 
517
- Set the latency mode for a button. Modes:
587
+ Set the latency mode for a button. Returns the updated button object. Modes:
518
588
  - `0`: Normal latency
519
589
  - `1`: Low latency
520
590
 
591
+ **Note:** This method is only supported on iOS. On Android, it will reject with an error.
592
+
521
593
  ```tsx
522
- await Flic2.buttonSetLatencyMode('button-uuid', 1); // Low latency
594
+ const button = await Flic2.buttonSetLatencyMode('button-uuid', 1); // Low latency
523
595
  ```
524
596
 
525
597
  #### `getBatteryHealth(uuid: string): Promise<boolean>`
@@ -728,6 +800,52 @@ const scanForButtons = async () => {
728
800
 
729
801
  ## Troubleshooting
730
802
 
803
+ ### Running on iOS Simulator
804
+
805
+ `flic2lib` is device-only. For simulator builds, use the no-op simulator pod and a
806
+ dedicated simulator build configuration.
807
+
808
+ 1. Add a simulator configuration in Xcode (for example `DebugSimulator`) and a scheme that uses it.
809
+ 2. Map pods by configuration in your app `Podfile`:
810
+
811
+ ```ruby
812
+ # Example
813
+ project 'YourApp.xcodeproj', {
814
+ 'Debug' => :debug,
815
+ 'DebugSimulator' => :debug,
816
+ 'Release' => :release,
817
+ }
818
+
819
+ target 'YourApp' do
820
+ # ... your existing use_react_native! setup
821
+
822
+ pod 'Flic2Device',
823
+ :path => '../node_modules/react-native-flic2',
824
+ :configurations => ['Debug', 'Release']
825
+
826
+ pod 'Flic2Simulator',
827
+ :path => '../node_modules/react-native-flic2',
828
+ :configurations => ['DebugSimulator']
829
+ end
830
+ ```
831
+
832
+ 3. Install pods normally (no env flags):
833
+
834
+ ```sh
835
+ cd ios && bundle exec pod install && cd ..
836
+ ```
837
+
838
+ 4. Launch simulator build (example):
839
+
840
+ ```sh
841
+ npx react-native run-ios --scheme YourAppSimulator --mode DebugSimulator --simulator "iPhone 17"
842
+ ```
843
+
844
+ Expected behavior on simulator:
845
+ - App builds and launches.
846
+ - Flic calls are no-op fallback behavior.
847
+ - Real button communication works only on physical devices.
848
+
731
849
  ### Buttons not connecting
732
850
 
733
851
  - Ensure Bluetooth is enabled on the device
@@ -26,7 +26,7 @@ def getExtOrIntegerDefault(name) {
26
26
  }
27
27
 
28
28
  android {
29
- namespace "com.flic2"
29
+ namespace "nl.xguard.flic2"
30
30
 
31
31
  compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
32
32
 
@@ -1,4 +1,4 @@
1
- package com.flic2
1
+ package nl.xguard.flic2
2
2
 
3
3
  import android.app.ActivityManager
4
4
  import android.content.Context
@@ -1,4 +1,4 @@
1
- package com.flic2
1
+ package nl.xguard.flic2
2
2
 
3
3
  import com.facebook.react.bridge.Arguments
4
4
  import com.facebook.react.bridge.WritableMap
@@ -47,7 +47,15 @@ class Flic2ButtonEventListener(
47
47
  val event = if (isDown) "buttonDown" else "buttonUp"
48
48
  emitEvent(createButtonEvent(button, event).apply {
49
49
  putBoolean("queued", wasQueued)
50
- putDouble("age", System.currentTimeMillis() - timestamp.toDouble())
50
+ // Match old Android implementation and iOS:
51
+ // - Age is in seconds
52
+ // - Only meaningful for queued events; 0 for real-time events
53
+ val ageSeconds = if (wasQueued) {
54
+ (button.getReadyTimestamp() - timestamp) / 1000.0
55
+ } else {
56
+ 0.0
57
+ }
58
+ putDouble("age", ageSeconds)
51
59
  })
52
60
  }
53
61
 
@@ -94,7 +102,15 @@ class Flic2ButtonEventListener(
94
102
  }
95
103
  emitEvent(createButtonEvent(button, event).apply {
96
104
  putBoolean("queued", wasQueued)
97
- putDouble("age", System.currentTimeMillis() - timestamp.toDouble())
105
+ // Match old Android implementation and iOS:
106
+ // - Age is in seconds
107
+ // - Only meaningful for queued events; 0 for real-time events
108
+ val ageSeconds = if (wasQueued) {
109
+ (button.getReadyTimestamp() - timestamp) / 1000.0
110
+ } else {
111
+ 0.0
112
+ }
113
+ putDouble("age", ageSeconds)
98
114
  })
99
115
  }
100
116
 
@@ -1,4 +1,4 @@
1
- package com.flic2
1
+ package nl.xguard.flic2
2
2
 
3
3
  import com.facebook.react.bridge.Arguments
4
4
  import com.facebook.react.bridge.WritableArray
@@ -31,8 +31,7 @@ object Flic2Converter {
31
31
  putInt("firmwareRevision", button.getFirmwareVersion())
32
32
 
33
33
  // Check if ready by comparing connection state
34
- val isReady = connState == Flic2Button.CONNECTION_STATE_CONNECTED_READY
35
- putBoolean("isReady", isReady)
34
+ putBoolean("isReady", connState == Flic2Button.CONNECTION_STATE_CONNECTED_READY)
36
35
 
37
36
  // Get battery level from BatteryLevel object
38
37
  val batteryLevel = button.getLastKnownBatteryLevel()
@@ -1,4 +1,4 @@
1
- package com.flic2
1
+ package nl.xguard.flic2
2
2
 
3
3
  import android.content.ComponentName
4
4
  import android.content.Context
@@ -48,8 +48,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
48
48
  private val serviceConnection = object : ServiceConnection {
49
49
  override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
50
50
  Log.d(TAG, "Service connected")
51
- val binder = service as Flic2Service.Flic2ServiceBinder
52
- flic2Service = binder.getService()
51
+ flic2Service = (service as Flic2Service.Flic2ServiceBinder).getService()
53
52
  serviceBound = true
54
53
 
55
54
  // Set up listeners for existing buttons
@@ -63,10 +62,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
63
62
 
64
63
  // Resolve the initialize promise if pending
65
64
  initializePromise?.let { promise ->
66
- promise.resolve(Arguments.createMap().apply {
67
- putBoolean("success", true)
68
- putString("message", "Manager initialized successfully")
69
- })
65
+ promise.resolve(null)
70
66
  initializePromise = null
71
67
  }
72
68
  }
@@ -90,6 +86,31 @@ class Flic2Module(reactContext: ReactApplicationContext) :
90
86
 
91
87
  override fun invalidate() {
92
88
  super.invalidate()
89
+
90
+ // Remove all button listeners before cleanup to prevent callbacks after teardown
91
+ try {
92
+ val manager = flic2Service?.getManager()
93
+ if (manager != null) {
94
+ buttonListeners.forEach { (uuid, listener) ->
95
+ try {
96
+ // Find the button and remove the listener
97
+ val button = manager.buttons.find { it.uuid == uuid }
98
+ if (button != null) {
99
+ button.removeListener(listener)
100
+ Log.d(TAG, "Removed listener for button during invalidate: $uuid")
101
+ }
102
+ } catch (e: Exception) {
103
+ Log.w(TAG, "Failed to remove listener for button during invalidate: $uuid", e)
104
+ }
105
+ }
106
+ }
107
+ } catch (e: Exception) {
108
+ Log.w(TAG, "Error during listener cleanup in invalidate", e)
109
+ }
110
+
111
+ // Clear listeners map
112
+ buttonListeners.clear()
113
+
93
114
  moduleScope.cancel()
94
115
  if (serviceBound) {
95
116
  reactApplicationContext.unbindService(serviceConnection)
@@ -107,8 +128,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
107
128
  val intent = Intent(reactApplicationContext, Flic2Service::class.java)
108
129
 
109
130
  // Check if service is already running
110
- val isRunning = ActivityUtil.isServiceRunning(reactApplicationContext, Flic2Service::class.java)
111
- if (!isRunning) {
131
+ if (!ActivityUtil.isServiceRunning(reactApplicationContext, Flic2Service::class.java)) {
112
132
  // Start service
113
133
  ActivityUtil.startForegroundService(reactApplicationContext, intent)
114
134
  }
@@ -182,8 +202,6 @@ class Flic2Module(reactContext: ReactApplicationContext) :
182
202
  override fun onComplete(result: Int, subCode: Int, button: Flic2Button?) {
183
203
  Log.d(TAG, "Scan complete: result=$result, button=${button?.uuid}")
184
204
 
185
- val resultCode = mapScanResultToCode(result)
186
-
187
205
  if (result == Flic2ScanCallback.RESULT_SUCCESS && button != null) {
188
206
  // Auto-connect (trigger mode not available in Android v1.1.0+)
189
207
  button.connect()
@@ -202,24 +220,20 @@ class Flic2Module(reactContext: ReactApplicationContext) :
202
220
  putMap("button", Flic2Converter.buttonToMap(button))
203
221
  })
204
222
  } else {
205
- val errorCode = Flic2Converter.scanResultToString(result)
206
- Log.e(TAG, "Scan failed with error code: $errorCode")
223
+ Log.e(TAG, "Scan failed with error code: ${Flic2Converter.scanResultToString(result)}")
207
224
  }
208
225
 
209
226
  // Emit scan completion with result code
210
227
  emitOnScanStatusChange(Arguments.createMap().apply {
211
228
  putString("event", "completion")
212
229
  putString("eventName", "completion")
213
- putInt("result", resultCode)
230
+ putInt("result", mapScanResultToCode(result))
214
231
  })
215
232
  }
216
233
  })
217
234
 
218
235
  // Return immediately - scan results will come through events
219
- promise.resolve(Arguments.createMap().apply {
220
- putBoolean("success", true)
221
- putString("message", "Scan started")
222
- })
236
+ promise.resolve(null)
223
237
  }
224
238
 
225
239
  override fun stopScan(promise: Promise) {
@@ -233,10 +247,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
233
247
  scanJob?.cancel()
234
248
  manager.stopScan()
235
249
 
236
- promise.resolve(Arguments.createMap().apply {
237
- putBoolean("success", true)
238
- putString("message", "Scan stopped")
239
- })
250
+ promise.resolve(null)
240
251
  } catch (e: Exception) {
241
252
  Log.e(TAG, "Failed to stop scan", e)
242
253
  promise.reject("STOP_SCAN_ERROR", "Failed to stop scan: ${e.message}", e)
@@ -260,7 +271,18 @@ class Flic2Module(reactContext: ReactApplicationContext) :
260
271
  // Disconnect before forgetting like iOS
261
272
  button.disconnectOrAbortPendingConnection()
262
273
 
263
- // Remove listener
274
+ // Explicitly remove listener from button before forgetting (matches old implementation)
275
+ val listener = buttonListeners[uuid]
276
+ if (listener != null) {
277
+ try {
278
+ button.removeListener(listener)
279
+ Log.d(TAG, "Removed listener for button: $uuid")
280
+ } catch (e: Exception) {
281
+ Log.w(TAG, "Failed to remove listener for button: $uuid", e)
282
+ }
283
+ }
284
+
285
+ // Remove listener from map
264
286
  buttonListeners.remove(uuid)
265
287
 
266
288
  // Forget button
@@ -269,10 +291,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
269
291
  // Update foreground service state after removing button
270
292
  updateForegroundServiceState(manager.buttons.size)
271
293
 
272
- promise.resolve(Arguments.createMap().apply {
273
- putBoolean("success", true)
274
- putString("message", "Button forgotten")
275
- })
294
+ promise.resolve(null)
276
295
  } catch (e: Exception) {
277
296
  Log.e(TAG, "Failed to forget button", e)
278
297
  promise.reject("FORGET_ERROR", "Failed to forget button: ${e.message}", e)
@@ -291,10 +310,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
291
310
 
292
311
  button.connect()
293
312
 
294
- promise.resolve(Arguments.createMap().apply {
295
- putBoolean("success", true)
296
- putString("message", "Connection initiated")
297
- })
313
+ promise.resolve(Flic2Converter.buttonToMap(button))
298
314
  } catch (e: Exception) {
299
315
  Log.e(TAG, "Failed to connect button", e)
300
316
  promise.reject("CONNECT_ERROR", "Failed to connect: ${e.message}", e)
@@ -311,10 +327,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
311
327
 
312
328
  button.disconnectOrAbortPendingConnection()
313
329
 
314
- promise.resolve(Arguments.createMap().apply {
315
- putBoolean("success", true)
316
- putString("message", "Disconnection initiated")
317
- })
330
+ promise.resolve(Flic2Converter.buttonToMap(button))
318
331
  } catch (e: Exception) {
319
332
  Log.e(TAG, "Failed to disconnect button", e)
320
333
  promise.reject("DISCONNECT_ERROR", "Failed to disconnect: ${e.message}", e)
@@ -346,10 +359,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
346
359
  // v1.1.0 uses setName() method instead of property
347
360
  button.setName(nickname)
348
361
 
349
- promise.resolve(Arguments.createMap().apply {
350
- putBoolean("success", true)
351
- putString("message", "Nickname set")
352
- })
362
+ promise.resolve(Flic2Converter.buttonToMap(button))
353
363
  } catch (e: Exception) {
354
364
  Log.e(TAG, "Failed to set nickname", e)
355
365
  promise.reject("SET_NICKNAME_ERROR", "Failed to set nickname: ${e.message}", e)
@@ -372,10 +382,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
372
382
  button.connect()
373
383
  }
374
384
 
375
- promise.resolve(Arguments.createMap().apply {
376
- putBoolean("success", true)
377
- putString("message", "All buttons connection initiated")
378
- })
385
+ promise.resolve(null)
379
386
  } catch (e: Exception) {
380
387
  Log.e(TAG, "Failed to connect all buttons", e)
381
388
  promise.reject("CONNECT_ALL_ERROR", "Failed to connect all buttons: ${e.message}", e)
@@ -397,10 +404,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
397
404
  button.disconnectOrAbortPendingConnection()
398
405
  }
399
406
 
400
- promise.resolve(Arguments.createMap().apply {
401
- putBoolean("success", true)
402
- putString("message", "All buttons disconnection initiated")
403
- })
407
+ promise.resolve(null)
404
408
  } catch (e: Exception) {
405
409
  Log.e(TAG, "Failed to disconnect all buttons", e)
406
410
  promise.reject("DISCONNECT_ALL_ERROR", "Failed to disconnect all buttons: ${e.message}", e)
@@ -419,18 +423,31 @@ class Flic2Module(reactContext: ReactApplicationContext) :
419
423
  val buttons = manager.buttons.toList()
420
424
 
421
425
  buttons.forEach { button ->
426
+ // Explicitly remove listener from button before forgetting (matches old implementation)
427
+ val listener = buttonListeners[button.uuid]
428
+ if (listener != null) {
429
+ try {
430
+ button.removeListener(listener)
431
+ Log.d(TAG, "Removed listener for button: ${button.uuid}")
432
+ } catch (e: Exception) {
433
+ Log.w(TAG, "Failed to remove listener for button: ${button.uuid}", e)
434
+ }
435
+ }
436
+
437
+ // Remove listener from map
422
438
  buttonListeners.remove(button.uuid)
439
+
440
+ // Disconnect before forgetting
423
441
  button.disconnectOrAbortPendingConnection()
442
+
443
+ // Forget button
424
444
  manager.forgetButton(button)
425
445
  }
426
446
 
427
447
  // Update foreground service state after removing all buttons
428
448
  updateForegroundServiceState(manager.buttons.size)
429
449
 
430
- promise.resolve(Arguments.createMap().apply {
431
- putBoolean("success", true)
432
- putString("message", "All buttons forgotten")
433
- })
450
+ promise.resolve(null)
434
451
  } catch (e: Exception) {
435
452
  Log.e(TAG, "Failed to forget all buttons", e)
436
453
  promise.reject("FORGET_ALL_ERROR", "Failed to forget all buttons: ${e.message}", e)
@@ -445,8 +462,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
445
462
  return
446
463
  }
447
464
 
448
- val scanning = (scanJob != null && scanJob?.isActive == true)
449
- promise.resolve(scanning)
465
+ promise.resolve(scanJob != null && scanJob?.isActive == true)
450
466
  } catch (e: Exception) {
451
467
  Log.e(TAG, "Failed to check scanning status", e)
452
468
  promise.reject("IS_SCANNING_ERROR", "Failed to check scanning status: ${e.message}", e)
@@ -502,3 +518,4 @@ class Flic2Module(reactContext: ReactApplicationContext) :
502
518
  }
503
519
  }
504
520
  }
521
+
@@ -1,4 +1,4 @@
1
- package com.flic2
1
+ package nl.xguard.flic2
2
2
 
3
3
  import com.facebook.react.BaseReactPackage
4
4
  import com.facebook.react.bridge.NativeModule
@@ -31,3 +31,4 @@ class Flic2Package : BaseReactPackage() {
31
31
  }
32
32
  }
33
33
  }
34
+