react-native-flic2 2.0.0-alpha.39 → 2.0.0-beta.10

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
@@ -1,27 +1,815 @@
1
1
  # react-native-flic2
2
2
 
3
- React Native Flic plugin made with the Flic2 SDK (unofficial)
3
+ React Native library for integrating Flic2 buttons into your React Native application. This library provides a complete interface to discover, connect, and interact with Flic2 buttons on both iOS and Android platforms.
4
4
 
5
- ## Installation
5
+ ## Features
6
+
7
+ - 🔍 Scan and discover Flic2 buttons
8
+ - 🔗 Connect and manage multiple buttons
9
+ - 📱 Receive button events (click, double click, hold)
10
+ - 🔋 Monitor battery status
11
+ - 🏷️ Set custom nicknames for buttons
12
+ - ⚙️ Configure trigger and latency modes
13
+ - 📡 Background connection support
14
+
15
+ > **⚠️ Important Notice**
16
+ >
17
+ > Parts of this project, including documentation and code examples, may have been generated with the assistance of AI tools and may contain errors. Please review all code and documentation carefully before use.
18
+ >
19
+ > This software is provided "AS IS" without warranty of any kind. Please refer to the [LICENSE](LICENSE) file for complete liability disclaimers. In no event shall the authors or copyright holders be liable for any claim, damages, or other liability arising from the use of this software.
20
+
21
+ ## Version Information
22
+
23
+ **This is a complete rewrite of react-native-flic2 (version 2.x.x).** This version requires React Native 0.81.x or higher. We do not provide a breaking changes list or migration guide. If you are upgrading from version 1.x.x, you should restart your implementation based on the examples and documentation provided in this README. The API and architecture have been completely redesigned.
24
+
25
+ If you need support for older React Native versions, please use version 1.x.x of this package instead.
6
26
 
27
+ ## Installation
7
28
 
8
29
  ```sh
9
30
  npm install react-native-flic2
31
+ ```
32
+
33
+ ### iOS Setup
34
+
35
+ 1. **Install CocoaPods dependencies:**
36
+ ```sh
37
+ cd ios && pod install && cd ..
38
+ ```
39
+
40
+ 2. **Add Bluetooth permissions to `Info.plist`:**
41
+ Add the following keys to your `ios/YourApp/Info.plist`:
42
+ ```xml
43
+ <key>NSBluetoothPeripheralUsageDescription</key>
44
+ <string>This app needs Bluetooth to connect to Flic buttons</string>
45
+ <key>NSBluetoothAlwaysUsageDescription</key>
46
+ <string>This app needs Bluetooth to connect to Flic buttons in the background</string>
47
+ ```
48
+
49
+ 3. **Enable Background Modes:**
50
+ - Open your project in Xcode
51
+ - Select your app target
52
+ - Go to "Signing & Capabilities"
53
+ - Add "Background Modes" capability if not already added
54
+ - Check "Uses Bluetooth LE accessories"
55
+
56
+ ### Android Setup
57
+
58
+ The library automatically includes the necessary permissions in `AndroidManifest.xml`. However, you need to request runtime permissions in your app:
59
+
60
+ **For Android 12+ (API 31+):**
61
+ - `BLUETOOTH_SCAN`
62
+ - `BLUETOOTH_CONNECT`
63
+
64
+ **For Android 11 and below:**
65
+ - `ACCESS_FINE_LOCATION`
66
+
67
+ You'll need to request these permissions before scanning for buttons. Use a library like `react-native-permissions` or implement permission requests manually.
68
+
69
+ #### Customizing the Foreground Service Notification (Android)
70
+
71
+ 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`:
72
+
73
+ ```xml
74
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
75
+ <application>
76
+ <!-- Your existing application configuration -->
77
+
78
+ <!-- Customize Flic2 foreground service notification -->
79
+ <meta-data
80
+ android:name="nl.xguard.flic2.notification_title"
81
+ android:value="My Flic2 Service" />
82
+ <meta-data
83
+ android:name="nl.xguard.flic2.notification_text"
84
+ android:value="Flic2 buttons are active" />
85
+ <meta-data
86
+ android:name="nl.xguard.flic2.notification_icon"
87
+ android:resource="@drawable/ic_notification" />
88
+ <meta-data
89
+ android:name="nl.xguard.flic2.notification_channel_name"
90
+ android:value="Flic2 Notifications" />
91
+ <meta-data
92
+ android:name="nl.xguard.flic2.notification_channel_description"
93
+ android:value="Notifications for Flic2 button connections" />
94
+ <meta-data
95
+ android:name="nl.xguard.flic2.notification_id"
96
+ android:value="123321" />
97
+ <meta-data
98
+ android:name="nl.xguard.flic2.notification_channel_id"
99
+ android:value="my_custom_channel_id" />
100
+ </application>
101
+ </manifest>
102
+ ```
103
+
104
+ **Available Configuration Options:**
105
+
106
+ - `nl.xguard.flic2.notification_title` - Notification title (default: "Flic 2")
107
+ - `nl.xguard.flic2.notification_text` - Notification text (default: "Flic 2 service is running")
108
+ - `nl.xguard.flic2.notification_icon` - Notification icon resource ID (default: system info icon)
109
+ - Use `@drawable/your_icon_name` or `@mipmap/your_icon_name` format
110
+ - `nl.xguard.flic2.notification_channel_name` - Notification channel name (default: "Flic2Channel")
111
+ - `nl.xguard.flic2.notification_channel_description` - Notification channel description (default: "Flic2Channel")
112
+ - `nl.xguard.flic2.notification_id` - Notification ID integer (default: 123321)
113
+ - `nl.xguard.flic2.notification_channel_id` - Notification channel ID string (default: "Notification_Channel_Flic2Service")
114
+
115
+ **Example with Custom Icon:**
116
+
117
+ 1. Add your notification icon to `android/app/src/main/res/drawable/` (e.g., `ic_flic2_notification.png`)
118
+
119
+ 2. Add metadata to `AndroidManifest.xml`:
120
+ ```xml
121
+ <meta-data
122
+ android:name="nl.xguard.flic2.notification_icon"
123
+ android:resource="@drawable/ic_flic2_notification" />
124
+ ```
125
+
126
+ **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.
127
+
128
+ ## Basic Usage
129
+
130
+ ### 1. Initialize the Library (Global Setup)
131
+
132
+ **Important:** For background usage, initialize Flic2 at the global level (outside of React components), typically in your app's entry point (e.g., `index.js` or `App.js`). Initializing in a `useEffect` is too late for background functionality.
133
+
134
+ ```tsx
135
+ // index.js or App.js (global level, outside components)
136
+ import Flic2, {
137
+ ButtonEvent,
138
+ ManagerStateChangeEvent,
139
+ } from 'react-native-flic2';
140
+
141
+ // Initialize the Flic2 manager when app starts
142
+ (async () => {
143
+ try {
144
+ await Flic2.initialize();
145
+ console.log('Flic2 initialized');
146
+
147
+ // Connect to all previously known buttons
148
+ Flic2.connectAllKnownButtons();
149
+
150
+ // Set up global event listeners for background usage
151
+ Flic2.eventEmitter.on('buttonEvent', (event: ButtonEvent) => {
152
+ console.log('Button event:', event.event, event.button.name);
153
+
154
+ // Handle button events that need to work in background
155
+ if (event.event === 'click') {
156
+ // Your background logic here (e.g., send notification, update database)
157
+ }
158
+ });
159
+
160
+ Flic2.eventEmitter.on('managerStateChange', (event: ManagerStateChangeEvent) => {
161
+ console.log('Manager state:', event.stateName);
162
+ });
163
+ } catch (error) {
164
+ console.error('Failed to initialize Flic2:', error);
165
+ }
166
+ })();
167
+ ```
168
+
169
+ ### 2. Set Up Component-Level Event Listeners (Optional)
170
+
171
+ If you need to update UI based on button events, you can add additional listeners in your components using `useEffect`. These listeners are in addition to the global ones and are useful for UI-specific updates:
172
+
173
+ ```tsx
174
+ import React, { useEffect } from 'react';
175
+ import { Alert } from 'react-native';
176
+ import Flic2, {
177
+ ButtonEvent,
178
+ ScanStatusChangeEvent,
179
+ } from 'react-native-flic2';
180
+
181
+ const MyComponent = () => {
182
+ useEffect(() => {
183
+ // UI-specific listener (only needed if you want to show UI updates)
184
+ const buttonSubscription = Flic2.eventEmitter.on(
185
+ 'buttonEvent',
186
+ (event: ButtonEvent) => {
187
+ if (event.event === 'click' || event.event === 'doubleClick' || event.event === 'hold') {
188
+ // Show UI alert when component is mounted
189
+ Alert.alert(
190
+ event.button.nickname || event.button.name,
191
+ `${event.event} at ${new Date().toLocaleTimeString()}`
192
+ );
193
+ }
194
+ }
195
+ );
196
+
197
+ const scanSubscription = Flic2.eventEmitter.on(
198
+ 'scanStatusChange',
199
+ (event: ScanStatusChangeEvent) => {
200
+ // Update UI based on scan status
201
+ if (event.event === 'started') {
202
+ console.log('Scan started');
203
+ } else if (event.event === 'completion') {
204
+ console.log('Scan completed');
205
+ }
206
+ }
207
+ );
208
+
209
+ // Cleanup subscriptions on unmount
210
+ return () => {
211
+ buttonSubscription.remove();
212
+ scanSubscription.remove();
213
+ };
214
+ }, []);
215
+
216
+ // ... rest of component
217
+ };
218
+ ```
219
+
220
+ **Note:** Global listeners (set up outside components) will continue to work even when components unmount, which is essential for background functionality. Component-level listeners are only active when the component is mounted.
221
+
222
+ ### 3. Complete Example
223
+
224
+ This example shows a component that manages the UI for Flic2 buttons. **Note:** Flic2 should be initialized globally (see section 1) before this component is used. This component only handles UI-specific functionality.
225
+
226
+ **Important:** This is just an example demonstrating the library's API. Some parts (like `Alert.prompt` used in `renameButton`) are iOS-only and need platform-specific implementations for Android. Adapt the UI components to your needs and platform requirements.
227
+
228
+ ```tsx
229
+ import React, { useState, useEffect } from 'react';
230
+ import { View, Text, Button, Alert, StyleSheet } from 'react-native';
231
+ import Flic2, {
232
+ ButtonEvent,
233
+ FlicButton,
234
+ ScanStatusChangeEvent,
235
+ } from 'react-native-flic2';
236
+
237
+ const Flic2Example = () => {
238
+ const [buttons, setButtons] = useState<FlicButton[]>([]);
239
+ const [isScanning, setIsScanning] = useState(false);
240
+
241
+ useEffect(() => {
242
+ // Load existing buttons when component mounts
243
+ // (Flic2 should already be initialized globally)
244
+ loadButtons();
245
+
246
+ // Set up UI-specific event listeners
247
+ // Note: Global listeners should be set up outside components for background usage
248
+ const buttonSubscription = Flic2.eventEmitter.on(
249
+ 'buttonEvent',
250
+ (event: ButtonEvent) => {
251
+ console.log('Button event:', event.event, event.button.name);
252
+
253
+ // Show UI alerts when component is mounted
254
+ if (event.event === 'click' || event.event === 'doubleClick' || event.event === 'hold') {
255
+ Alert.alert(
256
+ event.button.nickname || event.button.name,
257
+ `${event.event} at ${new Date().toLocaleTimeString()}`
258
+ );
259
+ }
260
+ }
261
+ );
262
+
263
+ const scanSubscription = Flic2.eventEmitter.on(
264
+ 'scanStatusChange',
265
+ (event: ScanStatusChangeEvent) => {
266
+ console.log('Scan status:', event.event);
267
+
268
+ // Update UI state based on scan status
269
+ if (event.event === 'started') {
270
+ setIsScanning(true);
271
+ } else if (event.event === 'completion') {
272
+ setIsScanning(false);
273
+ loadButtons(); // Refresh button list after scan
274
+ }
275
+ }
276
+ );
277
+
278
+ // Cleanup subscriptions on unmount
279
+ return () => {
280
+ buttonSubscription.remove();
281
+ scanSubscription.remove();
282
+ };
283
+ }, []);
284
+
285
+ const loadButtons = async () => {
286
+ try {
287
+ const buttonList = await Flic2.getButtons();
288
+ setButtons(buttonList);
289
+ console.log('Loaded buttons:', buttonList.length);
290
+ } catch (error) {
291
+ console.error('Failed to load buttons:', error);
292
+ }
293
+ };
294
+
295
+ const startScan = async () => {
296
+ try {
297
+ // Check if already scanning
298
+ const scanning = await Flic2.isScanning();
299
+ if (scanning) {
300
+ console.log('Already scanning');
301
+ return;
302
+ }
303
+
304
+ // Request permissions here if needed (see Platform Setup)
305
+
306
+ await Flic2.startScan();
307
+ console.log('Scan started');
308
+ } catch (error) {
309
+ console.error('Failed to start scan:', error);
310
+ Alert.alert('Error', 'Failed to start scanning for buttons');
311
+ }
312
+ };
313
+
314
+ const stopScan = async () => {
315
+ try {
316
+ await Flic2.stopScan();
317
+ console.log('Scan stopped');
318
+ } catch (error) {
319
+ console.error('Failed to stop scan:', error);
320
+ }
321
+ };
322
+
323
+ const renameButton = (button: FlicButton) => {
324
+ // Note: Alert.prompt is iOS-only. On Android, use a TextInput in a Modal
325
+ // or a library like react-native-prompt-android for cross-platform support.
326
+ // This is just an example - implement appropriately for your platform needs.
327
+ Alert.prompt(
328
+ 'Rename Button',
329
+ 'Enter a new name for the button',
330
+ [
331
+ { text: 'Cancel', style: 'cancel' },
332
+ {
333
+ text: 'Save',
334
+ onPress: async (value) => {
335
+ if (!value) return;
336
+ try {
337
+ await Flic2.buttonSetNickname(button.uuid, value);
338
+ loadButtons(); // Refresh button list
339
+ } catch (error) {
340
+ console.error('Failed to rename button:', error);
341
+ }
342
+ },
343
+ },
344
+ ],
345
+ 'plain-text',
346
+ button.nickname
347
+ );
348
+ };
349
+
350
+ const forgetButton = (button: FlicButton) => {
351
+ Alert.alert(
352
+ 'Delete Button',
353
+ 'Are you sure you want to delete this button?',
354
+ [
355
+ { text: 'Cancel', style: 'cancel' },
356
+ {
357
+ text: 'Delete',
358
+ style: 'destructive',
359
+ onPress: async () => {
360
+ try {
361
+ await Flic2.forgetButton(button.uuid);
362
+ loadButtons(); // Refresh button list
363
+ } catch (error) {
364
+ console.error('Failed to forget button:', error);
365
+ }
366
+ },
367
+ },
368
+ ]
369
+ );
370
+ };
371
+
372
+ const showButtonOptions = (button: FlicButton) => {
373
+ Alert.alert(
374
+ 'Button Options',
375
+ button.nickname || button.name,
376
+ [
377
+ { text: 'Rename', onPress: () => renameButton(button) },
378
+ { text: 'Delete', onPress: () => forgetButton(button) },
379
+ { text: 'Cancel', style: 'cancel' },
380
+ ]
381
+ );
382
+ };
383
+
384
+ return (
385
+ <View style={styles.container}>
386
+ <Text style={styles.title}>Flic2 Example</Text>
387
+
388
+ <Button
389
+ title={isScanning ? 'Stop Scanning' : 'Scan for Buttons'}
390
+ onPress={isScanning ? stopScan : startScan}
391
+ />
392
+
393
+ <Button title="Refresh Buttons" onPress={loadButtons} />
394
+
395
+ <Text style={styles.count}>
396
+ Buttons: {buttons.length}
397
+ </Text>
398
+
399
+ {buttons.map((button) => (
400
+ <View key={button.uuid} style={styles.buttonItem}>
401
+ <Text style={styles.buttonName}>
402
+ {button.nickname || button.name}
403
+ </Text>
404
+ <Text style={styles.buttonUuid}>{button.uuid}</Text>
405
+ <Button
406
+ title="Options"
407
+ onPress={() => showButtonOptions(button)}
408
+ />
409
+ </View>
410
+ ))}
411
+ </View>
412
+ );
413
+ };
414
+
415
+ const styles = StyleSheet.create({
416
+ container: {
417
+ flex: 1,
418
+ padding: 20,
419
+ },
420
+ title: {
421
+ fontSize: 24,
422
+ fontWeight: 'bold',
423
+ marginBottom: 20,
424
+ },
425
+ count: {
426
+ fontSize: 18,
427
+ marginVertical: 10,
428
+ },
429
+ buttonItem: {
430
+ padding: 10,
431
+ marginVertical: 5,
432
+ backgroundColor: '#f0f0f0',
433
+ borderRadius: 5,
434
+ },
435
+ buttonName: {
436
+ fontSize: 16,
437
+ fontWeight: 'bold',
438
+ },
439
+ buttonUuid: {
440
+ fontSize: 12,
441
+ color: '#666',
442
+ },
443
+ });
444
+
445
+ export default Flic2Example;
446
+ ```
447
+
448
+ ## API Reference
449
+
450
+ ### Initialization
451
+
452
+ #### `initialize(): Promise<void>`
453
+
454
+ Initialize the Flic2 manager. This must be called before using any other methods.
455
+
456
+ ```tsx
457
+ await Flic2.initialize();
458
+ ```
459
+
460
+ ### Scanning
461
+
462
+ #### `startScan(): Promise<void>`
463
+
464
+ Start scanning for new Flic2 buttons. The scan will emit `scanStatusChange` events.
465
+
466
+ ```tsx
467
+ await Flic2.startScan();
468
+ ```
469
+
470
+ #### `stopScan(): Promise<void>`
471
+
472
+ Stop an ongoing scan.
473
+
474
+ ```tsx
475
+ await Flic2.stopScan();
476
+ ```
477
+
478
+ #### `isScanning(): Promise<boolean>`
479
+
480
+ Check if a scan is currently running.
481
+
482
+ ```tsx
483
+ const scanning = await Flic2.isScanning();
484
+ ```
485
+
486
+ ### Button Management
10
487
 
488
+ #### `getButtons(): Promise<FlicButton[]>`
489
+
490
+ Get all known buttons.
491
+
492
+ ```tsx
493
+ const buttons = await Flic2.getButtons();
494
+ ```
495
+
496
+ #### `getButton(uuid: string): Promise<FlicButton | null>`
497
+
498
+ Get a specific button by UUID.
499
+
500
+ ```tsx
501
+ const button = await Flic2.getButton('button-uuid');
502
+ ```
503
+
504
+ #### `connectAllKnownButtons(): Promise<void>`
505
+
506
+ Connect to all previously known buttons.
507
+
508
+ ```tsx
509
+ await Flic2.connectAllKnownButtons();
510
+ ```
511
+
512
+ #### `disconnectAllKnownButtons(): Promise<void>`
513
+
514
+ Disconnect all connected buttons.
515
+
516
+ ```tsx
517
+ await Flic2.disconnectAllKnownButtons();
518
+ ```
519
+
520
+ #### `forgetButton(uuid: string): Promise<void>`
521
+
522
+ Forget (unpair) a specific button.
523
+
524
+ ```tsx
525
+ await Flic2.forgetButton('button-uuid');
526
+ ```
527
+
528
+ #### `forgetAllButtons(): Promise<void>`
529
+
530
+ Forget all buttons.
531
+
532
+ ```tsx
533
+ await Flic2.forgetAllButtons();
534
+ ```
535
+
536
+ ### Button Configuration
537
+
538
+ #### `buttonConnect(uuid: string): Promise<FlicButton>`
539
+
540
+ Connect to a specific button. Returns the button object.
541
+
542
+ ```tsx
543
+ const button = await Flic2.buttonConnect('button-uuid');
544
+ ```
545
+
546
+ #### `buttonDisconnect(uuid: string): Promise<FlicButton>`
547
+
548
+ Disconnect a specific button. Returns the button object.
549
+
550
+ ```tsx
551
+ const button = await Flic2.buttonDisconnect('button-uuid');
552
+ ```
553
+
554
+ #### `buttonSetNickname(uuid: string, nickname: string): Promise<FlicButton>`
555
+
556
+ Set a custom nickname for a button. Returns the updated button object.
557
+
558
+ ```tsx
559
+ const button = await Flic2.buttonSetNickname('button-uuid', 'My Button');
560
+ ```
561
+
562
+ #### `buttonSetTriggerMode(uuid: string, mode: TriggerModeType): Promise<FlicButton>`
563
+
564
+ Set the trigger mode for a button. Returns the updated button object. Modes:
565
+ - `0`: Click and Hold
566
+ - `1`: Click and Double Click
567
+ - `2`: Click and Double Click and Hold
568
+ - `3`: Click only
569
+
570
+ **Note:** This method is only supported on iOS. On Android, it will reject with an error.
571
+
572
+ ```tsx
573
+ const button = await Flic2.buttonSetTriggerMode('button-uuid', 3); // Click only
574
+ ```
575
+
576
+ #### `buttonSetLatencyMode(uuid: string, mode: LatencyModeType): Promise<FlicButton>`
577
+
578
+ Set the latency mode for a button. Returns the updated button object. Modes:
579
+ - `0`: Normal latency
580
+ - `1`: Low latency
581
+
582
+ **Note:** This method is only supported on iOS. On Android, it will reject with an error.
583
+
584
+ ```tsx
585
+ const button = await Flic2.buttonSetLatencyMode('button-uuid', 1); // Low latency
11
586
  ```
12
587
 
588
+ #### `getBatteryHealth(uuid: string): Promise<boolean>`
589
+
590
+ Get the battery health status of a button. Returns `true` if battery voltage is above 2.65V.
591
+
592
+ ```tsx
593
+ const isHealthy = await Flic2.getBatteryHealth('button-uuid');
594
+ ```
13
595
 
14
- ## Usage
596
+ ## Events
15
597
 
598
+ The library uses an event emitter pattern to notify your app of button events and state changes.
16
599
 
17
- ```js
18
- import { multiply } from 'react-native-flic2';
600
+ ### `buttonEvent`
19
601
 
20
- // ...
602
+ Emitted when a button event occurs (click, double click, hold, connection, etc.).
21
603
 
22
- const result = multiply(3, 7);
604
+ ```tsx
605
+ Flic2.eventEmitter.on('buttonEvent', (event: ButtonEvent) => {
606
+ console.log('Event:', event.event);
607
+ console.log('Button:', event.button.name);
608
+ console.log('UUID:', event.uuid);
609
+ });
23
610
  ```
24
611
 
612
+ **Event Types:**
613
+ - `'discovered'` - Button was discovered during scan
614
+ - `'connected'` - Button connected successfully
615
+ - `'ready'` - Button is ready to receive events
616
+ - `'disconnected'` - Button disconnected
617
+ - `'connectionFailed'` - Connection attempt failed
618
+ - `'buttonDown'` - Button was pressed down
619
+ - `'buttonUp'` - Button was released
620
+ - `'click'` - Single click detected
621
+ - `'doubleClick'` - Double click detected
622
+ - `'hold'` - Button held down
623
+ - `'unpaired'` - Button was unpaired
624
+ - `'batteryUpdate'` - Battery status updated
625
+ - `'nicknameUpdate'` - Nickname was updated
626
+
627
+ **Event Object:**
628
+ ```tsx
629
+ type ButtonEvent = {
630
+ uuid: string;
631
+ event: string;
632
+ queued?: boolean;
633
+ age?: number;
634
+ nickname?: string;
635
+ voltage?: number;
636
+ batteryVoltageOk?: boolean;
637
+ error?: {
638
+ code: number;
639
+ message: string;
640
+ };
641
+ button: FlicButton;
642
+ };
643
+ ```
644
+
645
+ ### `managerStateChange`
646
+
647
+ Emitted when the Flic2 manager state changes (Bluetooth state, etc.).
648
+
649
+ ```tsx
650
+ Flic2.eventEmitter.on('managerStateChange', (event: ManagerStateChangeEvent) => {
651
+ console.log('State:', event.stateName);
652
+ console.log('State code:', event.state);
653
+ });
654
+ ```
655
+
656
+ **Event Object:**
657
+ ```tsx
658
+ type ManagerStateChangeEvent = {
659
+ event: 'restored' | 'stateChanged';
660
+ state?: number;
661
+ stateName?: string;
662
+ message?: string;
663
+ };
664
+ ```
665
+
666
+ **State Names:**
667
+ - `'unknown'`
668
+ - `'resetting'`
669
+ - `'unsupported'`
670
+ - `'unauthorized'`
671
+ - `'poweredOff'`
672
+ - `'poweredOn'`
673
+
674
+ ### `scanStatusChange`
675
+
676
+ Emitted when the scan status changes.
677
+
678
+ ```tsx
679
+ Flic2.eventEmitter.on('scanStatusChange', (event: ScanStatusChangeEvent) => {
680
+ if (event.event === 'started') {
681
+ console.log('Scan started');
682
+ } else if (event.event === 'completion') {
683
+ console.log('Scan completed', event.result);
684
+ }
685
+ });
686
+ ```
687
+
688
+ **Event Object:**
689
+ ```tsx
690
+ type ScanStatusChangeEvent = {
691
+ event: 'started' | 'completion';
692
+ eventName: 'started' | 'completion';
693
+ result?: ScanResult;
694
+ };
695
+ ```
696
+
697
+ ## Types
698
+
699
+ ### `FlicButton`
700
+
701
+ ```tsx
702
+ type FlicButton = {
703
+ uuid: string;
704
+ identifier: string;
705
+ name: string;
706
+ nickname: string;
707
+ bluetoothAddress: string;
708
+ serialNumber: string;
709
+ state: number;
710
+ stateName: string;
711
+ triggerMode: number; // iOS only, 0 on Android
712
+ triggerModeName: string; // iOS only, empty on Android
713
+ latencyMode: number; // iOS only, 0 on Android
714
+ latencyModeName: string; // iOS only, empty on Android
715
+ pressCount: number;
716
+ firmwareRevision: number;
717
+ isReady: boolean;
718
+ batteryVoltage: number;
719
+ isUnpaired: boolean;
720
+ };
721
+ ```
722
+
723
+ ## Common Use Cases
724
+
725
+ ### Connecting to Buttons on App Start
726
+
727
+ Initialize Flic2 globally when your app starts (not in a component):
728
+
729
+ ```tsx
730
+ // index.js or App.js (global level)
731
+ import Flic2 from 'react-native-flic2';
732
+
733
+ (async () => {
734
+ await Flic2.initialize();
735
+ Flic2.connectAllKnownButtons();
736
+ })();
737
+ ```
738
+
739
+ ### Handling Button Clicks
740
+
741
+ Set up button event listeners globally for background usage:
742
+
743
+ ```tsx
744
+ // Global level (e.g., index.js or App.js)
745
+ Flic2.eventEmitter.on('buttonEvent', (event) => {
746
+ if (event.event === 'click') {
747
+ // Handle single click (works in background)
748
+ console.log('Button clicked:', event.button.name);
749
+ } else if (event.event === 'doubleClick') {
750
+ // Handle double click
751
+ console.log('Button double clicked:', event.button.name);
752
+ } else if (event.event === 'hold') {
753
+ // Handle hold
754
+ console.log('Button held:', event.button.name);
755
+ }
756
+ });
757
+ ```
758
+
759
+ ### Monitoring Battery Status
760
+
761
+ ```tsx
762
+ Flic2.eventEmitter.on('buttonEvent', (event) => {
763
+ if (event.event === 'batteryUpdate') {
764
+ const isHealthy = event.batteryVoltageOk;
765
+ console.log('Battery healthy:', isHealthy);
766
+ console.log('Battery voltage:', event.voltage);
767
+ }
768
+ });
769
+ ```
770
+
771
+ ### Scanning for New Buttons
772
+
773
+ ```tsx
774
+ const scanForButtons = async () => {
775
+ // Request permissions first (see Platform Setup)
776
+
777
+ Flic2.eventEmitter.on('scanStatusChange', (event) => {
778
+ if (event.event === 'completion') {
779
+ if (event.result === 0) { // ScanResult.SUCCESS
780
+ console.log('Button found and paired!');
781
+ loadButtons();
782
+ } else {
783
+ console.log('Scan failed:', event.result);
784
+ }
785
+ }
786
+ });
787
+
788
+ await Flic2.startScan();
789
+ };
790
+ ```
791
+
792
+ ## Troubleshooting
793
+
794
+ ### Buttons not connecting
795
+
796
+ - Ensure Bluetooth is enabled on the device
797
+ - Check that you've requested the necessary permissions
798
+ - Verify the button is in pairing mode (press and hold for 7 seconds)
799
+ - Make sure the button isn't already connected to another device
800
+
801
+ ### Scan not starting
802
+
803
+ - Verify runtime permissions are granted (especially location permission on Android)
804
+ - Check that Bluetooth is enabled
805
+ - Ensure you're not already scanning (check with `isScanning()`)
806
+
807
+ ### Events not firing
808
+
809
+ - Make sure you've called `initialize()` globally (outside components) before setting up event listeners
810
+ - For background usage, set up listeners at the global level, not in `useEffect`
811
+ - Verify the button is connected and ready (`button.isReady === true`)
812
+ - Check that the button's trigger mode supports the event you're listening for
25
813
 
26
814
  ## Contributing
27
815
 
@@ -31,9 +819,4 @@ const result = multiply(3, 7);
31
819
 
32
820
  ## License
33
821
 
34
- MIT
35
-
36
- ---
37
-
38
- Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
39
-
822
+ See [LICENSE](LICENSE)