expo-beacon 0.6.19 → 0.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.
- package/README.md +115 -0
- package/android/src/main/java/expo/modules/beacon/BeaconConstants.kt +8 -2
- package/android/src/main/java/expo/modules/beacon/BeaconEventReceiver.kt +13 -0
- package/android/src/main/java/expo/modules/beacon/BeaconForegroundService.kt +74 -4
- package/android/src/main/java/expo/modules/beacon/ExpoBeaconModule.kt +108 -1
- package/build/ExpoBeacon.types.d.ts +30 -0
- package/build/ExpoBeacon.types.d.ts.map +1 -1
- package/build/ExpoBeacon.types.js.map +1 -1
- package/build/ExpoBeaconModule.d.ts +10 -1
- package/build/ExpoBeaconModule.d.ts.map +1 -1
- package/build/ExpoBeaconModule.js.map +1 -1
- package/build/ExpoBeaconModule.web.d.ts +3 -1
- package/build/ExpoBeaconModule.web.d.ts.map +1 -1
- package/build/ExpoBeaconModule.web.js +2 -0
- package/build/ExpoBeaconModule.web.js.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js.map +1 -1
- package/ios/ExpoBeaconModule.swift +100 -1
- package/package.json +1 -1
- package/src/ExpoBeacon.types.ts +34 -0
- package/src/ExpoBeaconModule.ts +12 -0
- package/src/ExpoBeaconModule.web.ts +3 -0
- package/src/index.ts +1 -0
package/README.md
CHANGED
|
@@ -52,6 +52,9 @@ An Expo module for scanning, pairing, and monitoring **iBeacons** and **Eddyston
|
|
|
52
52
|
- [getPairedEddystones()](#getpairededdystones)
|
|
53
53
|
- [startMonitoring()](#startmonitoringoptions)
|
|
54
54
|
- [stopMonitoring()](#stopmonitoring)
|
|
55
|
+
- [getMonitoringConfig()](#getmonitoringconfig)
|
|
56
|
+
- [getMonitoredDeviceState()](#getmonitoreddevicestateidentifier)
|
|
57
|
+
- [getMonitoredDeviceStates()](#getmonitoreddevicestates)
|
|
55
58
|
- [setNotificationConfig()](#setnotificationconfigconfig)
|
|
56
59
|
- [enableEventLogging()](#enableeventlogging)
|
|
57
60
|
- [disableEventLogging()](#disableeventlogging)
|
|
@@ -1001,6 +1004,76 @@ await ExpoBeacon.stopMonitoring();
|
|
|
1001
1004
|
|
|
1002
1005
|
---
|
|
1003
1006
|
|
|
1007
|
+
### `getMonitoringConfig()`
|
|
1008
|
+
|
|
1009
|
+
```ts
|
|
1010
|
+
getMonitoringConfig(): MonitoringConfig
|
|
1011
|
+
```
|
|
1012
|
+
|
|
1013
|
+
Returns the current monitoring configuration snapshot, including whether background monitoring is active.
|
|
1014
|
+
|
|
1015
|
+
This reads the native monitoring settings currently persisted by the module. Option fields are omitted when they have not been explicitly set.
|
|
1016
|
+
|
|
1017
|
+
```ts
|
|
1018
|
+
const config = ExpoBeacon.getMonitoringConfig();
|
|
1019
|
+
// {
|
|
1020
|
+
// isMonitoring: true,
|
|
1021
|
+
// maxDistance: 10,
|
|
1022
|
+
// exitDistance: 15,
|
|
1023
|
+
// minRssi: -85,
|
|
1024
|
+
// level: "all"
|
|
1025
|
+
// }
|
|
1026
|
+
```
|
|
1027
|
+
|
|
1028
|
+
---
|
|
1029
|
+
|
|
1030
|
+
### `getMonitoredDeviceState(identifier)`
|
|
1031
|
+
|
|
1032
|
+
```ts
|
|
1033
|
+
getMonitoredDeviceState(identifier: string): MonitoredDeviceState | null
|
|
1034
|
+
```
|
|
1035
|
+
|
|
1036
|
+
Returns the current monitoring-state snapshot for a paired iBeacon or Eddystone with the matching identifier.
|
|
1037
|
+
|
|
1038
|
+
- `state` is `"entered"` or `"exited"`.
|
|
1039
|
+
- `distance` is `null` when the device is currently exited or there is no live reading yet.
|
|
1040
|
+
- Returns `null` when no paired device matches the identifier.
|
|
1041
|
+
|
|
1042
|
+
Identifiers should be unique across all paired monitored devices.
|
|
1043
|
+
|
|
1044
|
+
```ts
|
|
1045
|
+
const lobby = ExpoBeacon.getMonitoredDeviceState("lobby-entrance");
|
|
1046
|
+
// {
|
|
1047
|
+
// kind: "ibeacon",
|
|
1048
|
+
// identifier: "lobby-entrance",
|
|
1049
|
+
// uuid: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0",
|
|
1050
|
+
// major: 1,
|
|
1051
|
+
// minor: 100,
|
|
1052
|
+
// state: "entered",
|
|
1053
|
+
// distance: 2.4
|
|
1054
|
+
// }
|
|
1055
|
+
```
|
|
1056
|
+
|
|
1057
|
+
---
|
|
1058
|
+
|
|
1059
|
+
### `getMonitoredDeviceStates()`
|
|
1060
|
+
|
|
1061
|
+
```ts
|
|
1062
|
+
getMonitoredDeviceStates(): MonitoredDeviceState[]
|
|
1063
|
+
```
|
|
1064
|
+
|
|
1065
|
+
Returns the current monitoring-state snapshots for all paired monitored devices across iBeacon and Eddystone.
|
|
1066
|
+
|
|
1067
|
+
```ts
|
|
1068
|
+
const states = ExpoBeacon.getMonitoredDeviceStates();
|
|
1069
|
+
// [
|
|
1070
|
+
// { kind: "ibeacon", identifier: "lobby-entrance", state: "entered", distance: 2.4, ... },
|
|
1071
|
+
// { kind: "eddystone", identifier: "meeting-room", state: "exited", distance: null, ... }
|
|
1072
|
+
// ]
|
|
1073
|
+
```
|
|
1074
|
+
|
|
1075
|
+
---
|
|
1076
|
+
|
|
1004
1077
|
### `setNotificationConfig(config)`
|
|
1005
1078
|
|
|
1006
1079
|
```ts
|
|
@@ -1390,10 +1463,51 @@ Passed to `startMonitoring()`.
|
|
|
1390
1463
|
type MonitoringOptions = {
|
|
1391
1464
|
maxDistance?: number;
|
|
1392
1465
|
exitDistance?: number;
|
|
1466
|
+
minRssi?: number;
|
|
1467
|
+
level?: "all" | "events";
|
|
1468
|
+
};
|
|
1469
|
+
```
|
|
1470
|
+
|
|
1471
|
+
### `MonitoringConfig`
|
|
1472
|
+
|
|
1473
|
+
Returned by `getMonitoringConfig()`.
|
|
1474
|
+
|
|
1475
|
+
```ts
|
|
1476
|
+
type MonitoringConfig = {
|
|
1477
|
+
isMonitoring: boolean;
|
|
1478
|
+
maxDistance?: number;
|
|
1479
|
+
exitDistance?: number;
|
|
1480
|
+
minRssi?: number;
|
|
1481
|
+
level?: "all" | "events";
|
|
1393
1482
|
notifications?: NotificationConfig;
|
|
1394
1483
|
};
|
|
1395
1484
|
```
|
|
1396
1485
|
|
|
1486
|
+
### `MonitoredDeviceState`
|
|
1487
|
+
|
|
1488
|
+
Returned by `getMonitoredDeviceState()` and `getMonitoredDeviceStates()`.
|
|
1489
|
+
|
|
1490
|
+
```ts
|
|
1491
|
+
type MonitoredDeviceState =
|
|
1492
|
+
| {
|
|
1493
|
+
kind: "ibeacon";
|
|
1494
|
+
identifier: string;
|
|
1495
|
+
uuid: string;
|
|
1496
|
+
major: number;
|
|
1497
|
+
minor: number;
|
|
1498
|
+
state: "entered" | "exited";
|
|
1499
|
+
distance: number | null;
|
|
1500
|
+
}
|
|
1501
|
+
| {
|
|
1502
|
+
kind: "eddystone";
|
|
1503
|
+
identifier: string;
|
|
1504
|
+
namespace: string;
|
|
1505
|
+
instance: string;
|
|
1506
|
+
state: "entered" | "exited";
|
|
1507
|
+
distance: number | null;
|
|
1508
|
+
};
|
|
1509
|
+
```
|
|
1510
|
+
|
|
1397
1511
|
### `NotificationConfig`
|
|
1398
1512
|
|
|
1399
1513
|
Top-level notification configuration.
|
|
@@ -1413,6 +1527,7 @@ type BeaconNotificationConfig = {
|
|
|
1413
1527
|
enabled?: boolean; // Default: true. Set false to suppress.
|
|
1414
1528
|
enterTitle?: string; // Default: "Beacon Entered"
|
|
1415
1529
|
exitTitle?: string; // Default: "Beacon Exited"
|
|
1530
|
+
timeoutTitle?: string; // Default: "Beacon Timeout"
|
|
1416
1531
|
body?: string; // Default: "{identifier} region {event}ed"
|
|
1417
1532
|
// Supports {identifier} and {event} placeholders.
|
|
1418
1533
|
sound?: boolean; // iOS only. Default: true
|
|
@@ -26,8 +26,14 @@ internal const val MONITORING_SCAN_PERIOD_MS = 1100L
|
|
|
26
26
|
*/
|
|
27
27
|
internal const val MONITORING_BETWEEN_SCAN_PERIOD_MS = 1000L
|
|
28
28
|
|
|
29
|
-
/**
|
|
30
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Grace window after the last ranging sighting during which MonitorNotifier.didExitRegion
|
|
31
|
+
* is suppressed. Matched to REGION_EXIT_PERIOD_MS so that a monitor-level exit is only
|
|
32
|
+
* honoured when ranging has *also* been silent for the full exit period — preventing
|
|
33
|
+
* false exits caused by Android 17+'s intermittent BLE scan gaps where ranging may
|
|
34
|
+
* be briefly quiet even though the beacon is still within range.
|
|
35
|
+
*/
|
|
36
|
+
internal const val RECENT_RANGING_SIGHTING_GRACE_MS = REGION_EXIT_PERIOD_MS
|
|
31
37
|
|
|
32
38
|
/**
|
|
33
39
|
* Number of consecutive ranging misses before emitting a distance-based exit event.
|
|
@@ -22,6 +22,19 @@ class BeaconEventReceiver(
|
|
|
22
22
|
|
|
23
23
|
val identifier = intent.getStringExtra("identifier") ?: return
|
|
24
24
|
val eventType = intent.getStringExtra("event") ?: return
|
|
25
|
+
|
|
26
|
+
// Handle error events uniformly — no beacon coordinates needed
|
|
27
|
+
if (eventType == "error") {
|
|
28
|
+
val errorCode = intent.getStringExtra("errorCode") ?: ""
|
|
29
|
+
val errorMessage = intent.getStringExtra("errorMessage") ?: ""
|
|
30
|
+
onEvent("onBeaconError", mapOf(
|
|
31
|
+
"identifier" to identifier,
|
|
32
|
+
"code" to errorCode,
|
|
33
|
+
"message" to errorMessage
|
|
34
|
+
))
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
|
|
25
38
|
val beaconType = intent.getStringExtra("beaconType") ?: "ibeacon"
|
|
26
39
|
val distance = intent.getDoubleExtra("distance", -1.0)
|
|
27
40
|
val rssi = intent.getIntExtra("rssi", 0)
|
|
@@ -29,6 +29,11 @@ private const val ENTER_EXIT_NOTIF_BASE_ID = 2000
|
|
|
29
29
|
|
|
30
30
|
class BeaconForegroundService : Service(), BeaconConsumer {
|
|
31
31
|
|
|
32
|
+
data class MonitoringRuntimeState(
|
|
33
|
+
val isEntered: Boolean,
|
|
34
|
+
val distance: Double?
|
|
35
|
+
)
|
|
36
|
+
|
|
32
37
|
private lateinit var beaconManager: BeaconManager
|
|
33
38
|
private val monitoredRegions = mutableListOf<Region>()
|
|
34
39
|
|
|
@@ -73,6 +78,7 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
73
78
|
|
|
74
79
|
override fun onCreate() {
|
|
75
80
|
super.onCreate()
|
|
81
|
+
activeService = this
|
|
76
82
|
createNotificationChannel()
|
|
77
83
|
apiForwarder = BeaconApiForwarder(this)
|
|
78
84
|
beaconManager = BeaconManager.getInstanceForApplication(this).also { manager ->
|
|
@@ -109,6 +115,7 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
109
115
|
// SecurityException on Android 14+ if BT permissions missing,
|
|
110
116
|
// or other platform-specific issues. Stop gracefully instead of crashing.
|
|
111
117
|
Log.e(TAG, "startForeground failed — stopping service", e)
|
|
118
|
+
sendErrorBroadcast(null, "SERVICE_START_FAILED", "startForeground failed — stopping service: ${e.message}")
|
|
112
119
|
stopSelf()
|
|
113
120
|
return START_NOT_STICKY
|
|
114
121
|
}
|
|
@@ -208,6 +215,12 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
208
215
|
beaconManager.startMonitoringBeaconsInRegion(region)
|
|
209
216
|
} catch (e: RemoteException) {
|
|
210
217
|
Log.e(TAG, "Failed to start monitoring iBeacon region ${region.uniqueId}", e)
|
|
218
|
+
sendErrorBroadcast(region.uniqueId, "MONITORING_FAILED", "Failed to start monitoring iBeacon region ${region.uniqueId}")
|
|
219
|
+
} catch (e: SecurityException) {
|
|
220
|
+
// Android 17+ may throw SecurityException if BLUETOOTH_SCAN/CONNECT were
|
|
221
|
+
// not held at the exact moment monitoring starts.
|
|
222
|
+
Log.e(TAG, "Security exception starting monitoring for ${region.uniqueId} — check BT permissions", e)
|
|
223
|
+
sendErrorBroadcast(region.uniqueId, "SECURITY_EXCEPTION", "Security exception starting monitoring for ${region.uniqueId} — check BT permissions")
|
|
211
224
|
}
|
|
212
225
|
// Start ranging this region for distance logging
|
|
213
226
|
if (distanceLogRegions.add(region)) {
|
|
@@ -216,6 +229,11 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
216
229
|
} catch (e: RemoteException) {
|
|
217
230
|
distanceLogRegions.remove(region)
|
|
218
231
|
Log.e(TAG, "Failed to start ranging iBeacon region ${region.uniqueId}", e)
|
|
232
|
+
sendErrorBroadcast(region.uniqueId, "RANGING_FAILED", "Failed to start ranging iBeacon region ${region.uniqueId}")
|
|
233
|
+
} catch (e: SecurityException) {
|
|
234
|
+
distanceLogRegions.remove(region)
|
|
235
|
+
Log.e(TAG, "Security exception starting ranging for ${region.uniqueId} — check BT permissions", e)
|
|
236
|
+
sendErrorBroadcast(region.uniqueId, "SECURITY_EXCEPTION", "Security exception starting ranging for ${region.uniqueId} — check BT permissions")
|
|
219
237
|
}
|
|
220
238
|
}
|
|
221
239
|
}
|
|
@@ -238,6 +256,10 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
238
256
|
beaconManager.startMonitoringBeaconsInRegion(region)
|
|
239
257
|
} catch (ex: RemoteException) {
|
|
240
258
|
Log.e(TAG, "Failed to start monitoring Eddystone region $identifier", ex)
|
|
259
|
+
sendErrorBroadcast(identifier, "MONITORING_FAILED", "Failed to start monitoring Eddystone region $identifier")
|
|
260
|
+
} catch (ex: SecurityException) {
|
|
261
|
+
Log.e(TAG, "Security exception starting monitoring for Eddystone $identifier — check BT permissions", ex)
|
|
262
|
+
sendErrorBroadcast(identifier, "SECURITY_EXCEPTION", "Security exception starting monitoring for Eddystone $identifier — check BT permissions")
|
|
241
263
|
}
|
|
242
264
|
if (distanceLogRegions.add(region)) {
|
|
243
265
|
try {
|
|
@@ -245,6 +267,11 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
245
267
|
} catch (ex: RemoteException) {
|
|
246
268
|
distanceLogRegions.remove(region)
|
|
247
269
|
Log.e(TAG, "Failed to start ranging Eddystone region $identifier", ex)
|
|
270
|
+
sendErrorBroadcast(identifier, "RANGING_FAILED", "Failed to start ranging Eddystone region $identifier")
|
|
271
|
+
} catch (ex: SecurityException) {
|
|
272
|
+
distanceLogRegions.remove(region)
|
|
273
|
+
Log.e(TAG, "Security exception starting ranging for Eddystone $identifier — check BT permissions", ex)
|
|
274
|
+
sendErrorBroadcast(identifier, "SECURITY_EXCEPTION", "Security exception starting ranging for Eddystone $identifier — check BT permissions")
|
|
248
275
|
}
|
|
249
276
|
}
|
|
250
277
|
}
|
|
@@ -352,10 +379,15 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
352
379
|
HysteresisAction.NONE -> {}
|
|
353
380
|
}
|
|
354
381
|
} else {
|
|
355
|
-
// No valid beacon reading —
|
|
356
|
-
//
|
|
357
|
-
|
|
358
|
-
|
|
382
|
+
// No valid beacon reading — track consecutive misses for disappearance-based
|
|
383
|
+
// exit detection. enterCounters/exitCounters are intentionally NOT reset here.
|
|
384
|
+
// On Android 17+ (API 37) the BLE scan callbacks are more intermittent: valid
|
|
385
|
+
// readings are interspersed with occasional null cycles even when the beacon is
|
|
386
|
+
// nearby. Resetting direction counters on every null would prevent the hysteresis
|
|
387
|
+
// from ever accumulating to HYSTERESIS_COUNT, breaking enter/exit entirely while
|
|
388
|
+
// still allowing distance events (which fire on each individual valid reading).
|
|
389
|
+
// Direction counters are reset by evaluateDistanceHysteresis when a valid reading
|
|
390
|
+
// contradicts the current direction (e.g., in-range reading resets exitCounters).
|
|
359
391
|
val count = (missCounters[region.uniqueId] ?: 0) + 1
|
|
360
392
|
missCounters[region.uniqueId] = count
|
|
361
393
|
|
|
@@ -559,6 +591,24 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
559
591
|
sendBroadcast(intent)
|
|
560
592
|
}
|
|
561
593
|
|
|
594
|
+
private fun sendErrorBroadcast(identifier: String?, code: String, message: String) {
|
|
595
|
+
val params = buildMap<String, Any?> {
|
|
596
|
+
put("identifier", identifier ?: "")
|
|
597
|
+
put("event", "error")
|
|
598
|
+
put("code", code)
|
|
599
|
+
put("message", message)
|
|
600
|
+
}
|
|
601
|
+
logBeaconEvent("onBeaconError", params)
|
|
602
|
+
val intent = Intent(ACTION_BEACON_EVENT).apply {
|
|
603
|
+
putExtra("identifier", identifier ?: "")
|
|
604
|
+
putExtra("event", "error")
|
|
605
|
+
putExtra("errorCode", code)
|
|
606
|
+
putExtra("errorMessage", message)
|
|
607
|
+
setPackage(packageName)
|
|
608
|
+
}
|
|
609
|
+
sendBroadcast(intent)
|
|
610
|
+
}
|
|
611
|
+
|
|
562
612
|
private fun monitoringEventName(isEddystone: Boolean, eventType: String): String? {
|
|
563
613
|
return when (eventType) {
|
|
564
614
|
"enter" -> if (isEddystone) "onEddystoneEnter" else "onBeaconEnter"
|
|
@@ -667,6 +717,18 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
667
717
|
return Companion.buildForegroundNotification(this)
|
|
668
718
|
}
|
|
669
719
|
|
|
720
|
+
private fun snapshotMonitoringRuntimeState(): Map<String, MonitoringRuntimeState> {
|
|
721
|
+
synchronized(distanceLock) {
|
|
722
|
+
val regionIds = monitoredRegionIds.toList()
|
|
723
|
+
return regionIds.associateWith { regionId ->
|
|
724
|
+
MonitoringRuntimeState(
|
|
725
|
+
isEntered = enteredRegions.contains(regionId),
|
|
726
|
+
distance = smoothedDistances[regionId]
|
|
727
|
+
)
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
670
732
|
companion object {
|
|
671
733
|
/** EMA weight for new readings. 0.4 balances responsiveness vs noise rejection. */
|
|
672
734
|
const val DISTANCE_EMA_ALPHA = 0.4
|
|
@@ -674,6 +736,7 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
674
736
|
const val DISTANCE_JUMP_FACTOR = 5.0
|
|
675
737
|
|
|
676
738
|
private const val PREF_IS_MONITORING = "expo.beacon.is_monitoring"
|
|
739
|
+
@Volatile private var activeService: BeaconForegroundService? = null
|
|
677
740
|
|
|
678
741
|
fun start(context: Context) {
|
|
679
742
|
context.getSharedPreferences(PREF_IS_MONITORING, Context.MODE_PRIVATE)
|
|
@@ -698,6 +761,10 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
698
761
|
.getBoolean("active", false)
|
|
699
762
|
}
|
|
700
763
|
|
|
764
|
+
fun getMonitoringRuntimeSnapshot(): Map<String, MonitoringRuntimeState> {
|
|
765
|
+
return activeService?.snapshotMonitoringRuntimeState() ?: emptyMap()
|
|
766
|
+
}
|
|
767
|
+
|
|
701
768
|
/**
|
|
702
769
|
* Ensure the notification channel exists. Must be called before building
|
|
703
770
|
* a notification from a non-service context (e.g. ExpoBeaconModule).
|
|
@@ -767,6 +834,9 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
767
834
|
}
|
|
768
835
|
|
|
769
836
|
override fun onDestroy() {
|
|
837
|
+
if (activeService === this) {
|
|
838
|
+
activeService = null
|
|
839
|
+
}
|
|
770
840
|
serviceConnected = false
|
|
771
841
|
timeoutHandler.removeCallbacksAndMessages(null)
|
|
772
842
|
timeoutRunnables.clear()
|
|
@@ -85,15 +85,17 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
85
85
|
override fun definition() = ModuleDefinition {
|
|
86
86
|
Name("ExpoBeacon")
|
|
87
87
|
|
|
88
|
-
Events("onBeaconEnter", "onBeaconExit", "onBeaconDistance", "onBeaconTimeout", "onBeaconFound", "onEddystoneFound", "onEddystoneEnter", "onEddystoneExit", "onEddystoneDistance", "onEddystoneTimeout")
|
|
88
|
+
Events("onBeaconEnter", "onBeaconExit", "onBeaconDistance", "onBeaconTimeout", "onBeaconFound", "onEddystoneFound", "onEddystoneEnter", "onEddystoneExit", "onEddystoneDistance", "onEddystoneTimeout", "onBeaconError")
|
|
89
89
|
|
|
90
90
|
AsyncFunction("scanForBeaconsAsync") { uuids: List<String>?, scanDurationMs: Int, promise: Promise ->
|
|
91
91
|
if (scanDurationMs <= 0) {
|
|
92
92
|
promise.reject("INVALID_DURATION", "Scan duration must be a positive integer", null)
|
|
93
|
+
sendEvent("onBeaconError", mapOf("identifier" to "", "code" to "INVALID_DURATION", "message" to "Scan duration must be a positive integer"))
|
|
93
94
|
return@AsyncFunction
|
|
94
95
|
}
|
|
95
96
|
if (scanPromise != null || eddystoneScanPromise != null) {
|
|
96
97
|
promise.reject("SCAN_IN_PROGRESS", "A scan is already running", null)
|
|
98
|
+
sendEvent("onBeaconError", mapOf("identifier" to "", "code" to "SCAN_IN_PROGRESS", "message" to "A scan is already running"))
|
|
97
99
|
return@AsyncFunction
|
|
98
100
|
}
|
|
99
101
|
scanResults.clear()
|
|
@@ -120,12 +122,14 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
120
122
|
if (scanPromise != null) {
|
|
121
123
|
cancelActiveScan()
|
|
122
124
|
scanPromise?.reject("SCAN_CANCELLED", "Scan was cancelled", null)
|
|
125
|
+
sendEvent("onBeaconError", mapOf("identifier" to "", "code" to "SCAN_CANCELLED", "message" to "Scan was cancelled"))
|
|
123
126
|
scanPromise = null
|
|
124
127
|
scanUuidFilter = emptySet()
|
|
125
128
|
}
|
|
126
129
|
if (eddystoneScanPromise != null) {
|
|
127
130
|
cancelActiveScan()
|
|
128
131
|
eddystoneScanPromise?.reject("SCAN_CANCELLED", "Scan was cancelled", null)
|
|
132
|
+
sendEvent("onBeaconError", mapOf("identifier" to "", "code" to "SCAN_CANCELLED", "message" to "Scan was cancelled"))
|
|
129
133
|
eddystoneScanPromise = null
|
|
130
134
|
}
|
|
131
135
|
unbindIfIdle()
|
|
@@ -158,10 +162,12 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
158
162
|
AsyncFunction("scanForEddystonesAsync") { scanDurationMs: Int, promise: Promise ->
|
|
159
163
|
if (scanDurationMs <= 0) {
|
|
160
164
|
promise.reject("INVALID_DURATION", "Scan duration must be a positive integer", null)
|
|
165
|
+
sendEvent("onBeaconError", mapOf("identifier" to "", "code" to "INVALID_DURATION", "message" to "Scan duration must be a positive integer"))
|
|
161
166
|
return@AsyncFunction
|
|
162
167
|
}
|
|
163
168
|
if (scanPromise != null || eddystoneScanPromise != null) {
|
|
164
169
|
promise.reject("SCAN_IN_PROGRESS", "A scan is already running", null)
|
|
170
|
+
sendEvent("onBeaconError", mapOf("identifier" to "", "code" to "SCAN_IN_PROGRESS", "message" to "A scan is already running"))
|
|
165
171
|
return@AsyncFunction
|
|
166
172
|
}
|
|
167
173
|
scanResults.clear()
|
|
@@ -277,6 +283,7 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
277
283
|
AsyncFunction("startMonitoring") { options: Any?, promise: Promise ->
|
|
278
284
|
val ctx = appContext.reactContext ?: run {
|
|
279
285
|
promise.reject("NO_CONTEXT", "React context is not available", null)
|
|
286
|
+
sendEvent("onBeaconError", mapOf("identifier" to "", "code" to "NO_CONTEXT", "message" to "React context is not available"))
|
|
280
287
|
return@AsyncFunction
|
|
281
288
|
}
|
|
282
289
|
var maxDistance: Double? = null
|
|
@@ -302,18 +309,22 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
302
309
|
}
|
|
303
310
|
if (maxDistance != null && (!maxDistance.isFinite() || maxDistance <= 0.0)) {
|
|
304
311
|
promise.reject("INVALID_MAX_DISTANCE", "maxDistance must be a finite number greater than 0", null)
|
|
312
|
+
sendEvent("onBeaconError", mapOf("identifier" to "", "code" to "INVALID_MAX_DISTANCE", "message" to "maxDistance must be a finite number greater than 0"))
|
|
305
313
|
return@AsyncFunction
|
|
306
314
|
}
|
|
307
315
|
if (exitDistance != null && (!exitDistance.isFinite() || exitDistance <= 0.0)) {
|
|
308
316
|
promise.reject("INVALID_EXIT_DISTANCE", "exitDistance must be a finite number greater than 0", null)
|
|
317
|
+
sendEvent("onBeaconError", mapOf("identifier" to "", "code" to "INVALID_EXIT_DISTANCE", "message" to "exitDistance must be a finite number greater than 0"))
|
|
309
318
|
return@AsyncFunction
|
|
310
319
|
}
|
|
311
320
|
if (exitDistance != null && maxDistance == null) {
|
|
312
321
|
promise.reject("INVALID_EXIT_DISTANCE", "exitDistance requires maxDistance to be set", null)
|
|
322
|
+
sendEvent("onBeaconError", mapOf("identifier" to "", "code" to "INVALID_EXIT_DISTANCE", "message" to "exitDistance requires maxDistance to be set"))
|
|
313
323
|
return@AsyncFunction
|
|
314
324
|
}
|
|
315
325
|
if (maxDistance != null && exitDistance != null && exitDistance < maxDistance) {
|
|
316
326
|
promise.reject("INVALID_EXIT_DISTANCE", "exitDistance must be greater than or equal to maxDistance", null)
|
|
327
|
+
sendEvent("onBeaconError", mapOf("identifier" to "", "code" to "INVALID_EXIT_DISTANCE", "message" to "exitDistance must be greater than or equal to maxDistance"))
|
|
317
328
|
return@AsyncFunction
|
|
318
329
|
}
|
|
319
330
|
ctx.getSharedPreferences(MONITORING_OPTIONS_PREFS, Context.MODE_PRIVATE)
|
|
@@ -332,6 +343,7 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
332
343
|
ContextCompat.checkSelfPermission(ctx, Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED
|
|
333
344
|
if (!hasLocation || !hasBgLocation) {
|
|
334
345
|
promise.reject("PERMISSION_DENIED", "Location permissions required for background monitoring. Call requestPermissionsAsync() first.", null)
|
|
346
|
+
sendEvent("onBeaconError", mapOf("identifier" to "", "code" to "PERMISSION_DENIED", "message" to "Location permissions required for background monitoring. Call requestPermissionsAsync() first."))
|
|
335
347
|
return@AsyncFunction
|
|
336
348
|
}
|
|
337
349
|
// Android 12+ requires BLUETOOTH_SCAN for BLE operations;
|
|
@@ -341,6 +353,7 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
341
353
|
val hasBtConnect = ContextCompat.checkSelfPermission(ctx, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED
|
|
342
354
|
if (!hasBtScan || !hasBtConnect) {
|
|
343
355
|
promise.reject("PERMISSION_DENIED", "Bluetooth permissions required for beacon monitoring. Call requestPermissionsAsync() first.", null)
|
|
356
|
+
sendEvent("onBeaconError", mapOf("identifier" to "", "code" to "PERMISSION_DENIED", "message" to "Bluetooth permissions required for beacon monitoring. Call requestPermissionsAsync() first."))
|
|
344
357
|
return@AsyncFunction
|
|
345
358
|
}
|
|
346
359
|
}
|
|
@@ -351,6 +364,7 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
351
364
|
} catch (e: Exception) {
|
|
352
365
|
unregisterEventReceiver()
|
|
353
366
|
promise.reject("SERVICE_START_FAILED", "Failed to start monitoring service: ${e.message}", e)
|
|
367
|
+
sendEvent("onBeaconError", mapOf("identifier" to "", "code" to "SERVICE_START_FAILED", "message" to "Failed to start monitoring service: ${e.message}"))
|
|
354
368
|
return@AsyncFunction
|
|
355
369
|
}
|
|
356
370
|
promise.resolve(null)
|
|
@@ -365,6 +379,7 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
365
379
|
AsyncFunction("stopMonitoring") { promise: Promise ->
|
|
366
380
|
val ctx = appContext.reactContext ?: run {
|
|
367
381
|
promise.reject("NO_CONTEXT", "React context is not available", null)
|
|
382
|
+
sendEvent("onBeaconError", mapOf("identifier" to "", "code" to "NO_CONTEXT", "message" to "React context is not available"))
|
|
368
383
|
return@AsyncFunction
|
|
369
384
|
}
|
|
370
385
|
BeaconForegroundService.stop(ctx)
|
|
@@ -490,6 +505,14 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
490
505
|
}
|
|
491
506
|
}
|
|
492
507
|
|
|
508
|
+
Function("getMonitoredDeviceState") { identifier: String ->
|
|
509
|
+
buildMonitoredDeviceState(identifier)
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
Function("getMonitoredDeviceStates") {
|
|
513
|
+
buildMonitoredDeviceStates()
|
|
514
|
+
}
|
|
515
|
+
|
|
493
516
|
// MARK: - Battery Optimization
|
|
494
517
|
|
|
495
518
|
Function("isBatteryOptimizationExempt") {
|
|
@@ -502,6 +525,7 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
502
525
|
AsyncFunction("requestBatteryOptimizationExemption") { promise: Promise ->
|
|
503
526
|
val ctx = appContext.reactContext ?: run {
|
|
504
527
|
promise.reject("NO_CONTEXT", "React context is not available", null)
|
|
528
|
+
sendEvent("onBeaconError", mapOf("identifier" to "", "code" to "NO_CONTEXT", "message" to "React context is not available"))
|
|
505
529
|
return@AsyncFunction
|
|
506
530
|
}
|
|
507
531
|
val pm = ctx.getSystemService(Context.POWER_SERVICE) as? PowerManager
|
|
@@ -524,6 +548,7 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
524
548
|
promise.resolve(true)
|
|
525
549
|
} catch (e: Exception) {
|
|
526
550
|
promise.reject("BATTERY_OPT_ERROR", "Failed to open battery optimization settings: ${e.message}", e)
|
|
551
|
+
sendEvent("onBeaconError", mapOf("identifier" to "", "code" to "BATTERY_OPT_ERROR", "message" to "Failed to open battery optimization settings: ${e.message}"))
|
|
527
552
|
}
|
|
528
553
|
}
|
|
529
554
|
|
|
@@ -787,6 +812,88 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
787
812
|
return map
|
|
788
813
|
}
|
|
789
814
|
|
|
815
|
+
private fun buildMonitoredDeviceState(identifier: String): Map<String, Any?>? {
|
|
816
|
+
val runtimeStates = BeaconForegroundService.getMonitoringRuntimeSnapshot()
|
|
817
|
+
|
|
818
|
+
val beacons = loadPairedBeaconsJson()
|
|
819
|
+
for (i in 0 until beacons.length()) {
|
|
820
|
+
val beacon = beacons.getJSONObject(i)
|
|
821
|
+
if (beacon.getString("identifier") == identifier) {
|
|
822
|
+
return buildIBeaconMonitoringState(beacon, runtimeStates[identifier])
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
val eddystones = loadPairedEddystonesJson()
|
|
827
|
+
for (i in 0 until eddystones.length()) {
|
|
828
|
+
val eddystone = eddystones.getJSONObject(i)
|
|
829
|
+
if (eddystone.getString("identifier") == identifier) {
|
|
830
|
+
return buildEddystoneMonitoringState(eddystone, runtimeStates[identifier])
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
return null
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
private fun buildMonitoredDeviceStates(): List<Map<String, Any?>> {
|
|
838
|
+
val runtimeStates = BeaconForegroundService.getMonitoringRuntimeSnapshot()
|
|
839
|
+
val states = mutableListOf<Map<String, Any?>>()
|
|
840
|
+
|
|
841
|
+
val beacons = loadPairedBeaconsJson()
|
|
842
|
+
for (i in 0 until beacons.length()) {
|
|
843
|
+
val beacon = beacons.getJSONObject(i)
|
|
844
|
+
val identifier = beacon.getString("identifier")
|
|
845
|
+
states.add(buildIBeaconMonitoringState(beacon, runtimeStates[identifier]))
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
val eddystones = loadPairedEddystonesJson()
|
|
849
|
+
for (i in 0 until eddystones.length()) {
|
|
850
|
+
val eddystone = eddystones.getJSONObject(i)
|
|
851
|
+
val identifier = eddystone.getString("identifier")
|
|
852
|
+
states.add(buildEddystoneMonitoringState(eddystone, runtimeStates[identifier]))
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
return states
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
private fun buildIBeaconMonitoringState(
|
|
859
|
+
beacon: JSONObject,
|
|
860
|
+
runtimeState: BeaconForegroundService.MonitoringRuntimeState?
|
|
861
|
+
): Map<String, Any?> {
|
|
862
|
+
val identifier = beacon.getString("identifier")
|
|
863
|
+
return buildMap<String, Any?> {
|
|
864
|
+
put("kind", "ibeacon")
|
|
865
|
+
put("identifier", identifier)
|
|
866
|
+
put("uuid", beacon.getString("uuid"))
|
|
867
|
+
put("major", beacon.getInt("major"))
|
|
868
|
+
put("minor", beacon.getInt("minor"))
|
|
869
|
+
put("state", if (runtimeState?.isEntered == true) "entered" else "exited")
|
|
870
|
+
put("distance", normalizedMonitoringDistance(runtimeState))
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
private fun buildEddystoneMonitoringState(
|
|
875
|
+
eddystone: JSONObject,
|
|
876
|
+
runtimeState: BeaconForegroundService.MonitoringRuntimeState?
|
|
877
|
+
): Map<String, Any?> {
|
|
878
|
+
val identifier = eddystone.getString("identifier")
|
|
879
|
+
return buildMap<String, Any?> {
|
|
880
|
+
put("kind", "eddystone")
|
|
881
|
+
put("identifier", identifier)
|
|
882
|
+
put("namespace", eddystone.getString("namespace"))
|
|
883
|
+
put("instance", eddystone.getString("instance"))
|
|
884
|
+
put("state", if (runtimeState?.isEntered == true) "entered" else "exited")
|
|
885
|
+
put("distance", normalizedMonitoringDistance(runtimeState))
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
private fun normalizedMonitoringDistance(
|
|
890
|
+
runtimeState: BeaconForegroundService.MonitoringRuntimeState?
|
|
891
|
+
): Double? {
|
|
892
|
+
if (runtimeState?.isEntered != true) return null
|
|
893
|
+
val distance = runtimeState.distance ?: return null
|
|
894
|
+
return distance.takeIf { !it.isNaN() && !it.isInfinite() && it >= 0 }
|
|
895
|
+
}
|
|
896
|
+
|
|
790
897
|
// --- Shared Preferences helpers ---
|
|
791
898
|
|
|
792
899
|
/** Removes entries matching [identifier] from a paired JSON array, saves, and invalidates cache. */
|
|
@@ -123,6 +123,25 @@ export type MonitoringConfig = {
|
|
|
123
123
|
level?: 'all' | 'events';
|
|
124
124
|
notifications?: NotificationConfig;
|
|
125
125
|
};
|
|
126
|
+
/** Current state snapshot for a paired monitored device. */
|
|
127
|
+
export type MonitoredDeviceState = {
|
|
128
|
+
kind: "ibeacon";
|
|
129
|
+
identifier: string;
|
|
130
|
+
uuid: string;
|
|
131
|
+
major: number;
|
|
132
|
+
minor: number;
|
|
133
|
+
state: "entered" | "exited";
|
|
134
|
+
/** Current distance in metres, or null when exited or no live reading is available. */
|
|
135
|
+
distance: number | null;
|
|
136
|
+
} | {
|
|
137
|
+
kind: "eddystone";
|
|
138
|
+
identifier: string;
|
|
139
|
+
namespace: string;
|
|
140
|
+
instance: string;
|
|
141
|
+
state: "entered" | "exited";
|
|
142
|
+
/** Current distance in metres, or null when exited or no live reading is available. */
|
|
143
|
+
distance: number | null;
|
|
144
|
+
};
|
|
126
145
|
/** Options accepted by startMonitoring(). */
|
|
127
146
|
export type MonitoringOptions = {
|
|
128
147
|
/**
|
|
@@ -226,6 +245,15 @@ export type EddystoneTimeoutEvent = {
|
|
|
226
245
|
/** Current distance in metres at the time the timeout fired. */
|
|
227
246
|
distance: number;
|
|
228
247
|
};
|
|
248
|
+
/** Payload for native beacon error events (monitoring/ranging failures). */
|
|
249
|
+
export type BeaconErrorEvent = {
|
|
250
|
+
/** Region or constraint identifier, empty string if unavailable. */
|
|
251
|
+
identifier: string;
|
|
252
|
+
/** Machine-readable error code (e.g. "MONITORING_FAILED", "RANGING_FAILED", "SECURITY_EXCEPTION"). */
|
|
253
|
+
code: string;
|
|
254
|
+
/** Human-readable error message from the native layer. */
|
|
255
|
+
message: string;
|
|
256
|
+
};
|
|
229
257
|
/** Module event map. */
|
|
230
258
|
export type ExpoBeaconModuleEvents = {
|
|
231
259
|
onBeaconEnter: (params: BeaconRegionEvent) => void;
|
|
@@ -242,6 +270,8 @@ export type ExpoBeaconModuleEvents = {
|
|
|
242
270
|
onEddystoneDistance: (params: EddystoneDistanceEvent) => void;
|
|
243
271
|
/** Fired once after a paired Eddystone has been continuously in range for its configured `timeoutSeconds`. */
|
|
244
272
|
onEddystoneTimeout: (params: EddystoneTimeoutEvent) => void;
|
|
273
|
+
/** Fired when a native monitoring or ranging failure occurs (logged to DB and forwarded to JS). */
|
|
274
|
+
onBeaconError: (params: BeaconErrorEvent) => void;
|
|
245
275
|
};
|
|
246
276
|
/** Options for filtering event logs. */
|
|
247
277
|
export type EventLogQueryOptions = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoBeacon.types.d.ts","sourceRoot":"","sources":["../src/ExpoBeacon.types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"ExpoBeacon.types.d.ts","sourceRoot":"","sources":["../src/ExpoBeacon.types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoBeacon.types.js","sourceRoot":"","sources":["../src/ExpoBeacon.types.ts"],"names":[],"mappings":"","sourcesContent":["/** Raw beacon discovered during a scan. */\r\nexport type BeaconScanResult = {\r\n uuid: string; // iBeacon proximity UUID (uppercase, formatted)\r\n major: number; // iBeacon major value (0–65535)\r\n minor: number; // iBeacon minor value (0–65535)\r\n rssi: number; // Signal strength in dBm (negative number)\r\n distance: number; // Estimated distance in meters\r\n txPower: number; // Calibrated TX power\r\n /** BLE advertising device name. May be undefined on iOS (CoreLocation does not expose it for iBeacon). */\r\n name?: string;\r\n};\r\n\r\n/**\r\n * A beacon that has been paired/registered for monitoring.\r\n *\r\n * Note: Paired beacon data is stored unencrypted in UserDefaults (iOS) /\r\n * SharedPreferences (Android) and may be included in device backups.\r\n */\r\nexport type PairedBeacon = {\r\n identifier: string; // User-defined label (e.g. \"lobby-door\")\r\n uuid: string;\r\n major: number;\r\n minor: number;\r\n /** BLE advertising device name, if provided at pairing time. */\r\n name?: string;\r\n /**\r\n * Timeout in seconds. When set, the module fires `onBeaconTimeout` once\r\n * after the beacon has been continuously in range for this duration.\r\n * The timer resets if the beacon exits and re-enters range.\r\n *\r\n * The timeout countdown also starts if no BLE readings are received\r\n * for 60 seconds (e.g. due to Doze mode or background throttling).\r\n */\r\n timeoutSeconds?: number;\r\n};\r\n\r\n/** Payload for enter/exit region events. */\r\nexport type BeaconRegionEvent = {\r\n identifier: string; // Matches PairedBeacon.identifier\r\n uuid: string;\r\n major: number;\r\n minor: number;\r\n event: \"enter\" | \"exit\";\r\n /** Measured distance in metres at the time of the event (–1 if unavailable). */\r\n distance: number;\r\n /** Signal strength in dBm at the time of the event (0 if unavailable). */\r\n rssi?: number;\r\n};\r\n\r\n/** Payload for periodic distance update events during monitoring. */\r\nexport type BeaconDistanceEvent = {\r\n identifier: string;\r\n uuid: string;\r\n major: number;\r\n minor: number;\r\n distance: number;\r\n /** Signal strength in dBm (0 if unavailable). */\r\n rssi?: number;\r\n};\r\n\r\n/** Payload for beacon timeout events (beacon in range for configured duration). */\r\nexport type BeaconTimeoutEvent = {\r\n identifier: string;\r\n uuid: string;\r\n major: number;\r\n minor: number;\r\n /** Current distance in metres at the time the timeout fired. */\r\n distance: number;\r\n};\r\n\r\n/** Configuration for beacon enter/exit event notifications. */\r\nexport type BeaconNotificationConfig = {\r\n /** Whether to show enter/exit notifications. Default: true. */\r\n enabled?: boolean;\r\n /** Notification title on beacon enter. Default: \"Beacon Entered\". */\r\n enterTitle?: string;\r\n /** Notification title on beacon exit. Default: \"Beacon Exited\". */\r\n exitTitle?: string;\r\n /** Notification title on beacon timeout. Default: \"Beacon Timeout\". */\r\n timeoutTitle?: string;\r\n /**\r\n * Notification body template. Supports {identifier} and {event} placeholders.\r\n * Default: \"{identifier} region {event}ed\".\r\n */\r\n body?: string;\r\n /** Play a sound with the notification (iOS only). Default: true. */\r\n sound?: boolean;\r\n /** Android drawable resource name for the notification icon (e.g. \"ic_notification\"). */\r\n icon?: string;\r\n};\r\n\r\n/** Configuration for the Android foreground service notification (persistent status bar entry). */\r\nexport type ForegroundServiceConfig = {\r\n /** Title of the persistent notification. Default: \"Beacon Monitoring Active\". */\r\n title?: string;\r\n /** Body text of the persistent notification. Default: \"Monitoring for iBeacons in the background\". */\r\n text?: string;\r\n /** Android drawable resource name for the notification icon. */\r\n icon?: string;\r\n};\r\n\r\n/** Configuration for the Android notification channel. */\r\nexport type NotificationChannelConfig = {\r\n /** Channel display name shown in system settings. Default: \"Beacon Monitoring\". */\r\n name?: string;\r\n /** Channel description shown in system settings. Default: \"Used for background iBeacon region monitoring\". */\r\n description?: string;\r\n /**\r\n * Channel importance level. Default: 'low'.\r\n * Note: Android may ignore decreases in importance after first channel creation until the app is reinstalled.\r\n */\r\n importance?: \"low\" | \"default\" | \"high\";\r\n};\r\n\r\n/** Combined notification configuration for all notification types. */\r\nexport type NotificationConfig = {\r\n /** Settings for beacon enter/exit event notifications. */\r\n beaconEvents?: BeaconNotificationConfig;\r\n /** Settings for the persistent foreground service notification (Android only). */\r\n foregroundService?: ForegroundServiceConfig;\r\n /** Settings for the Android notification channel (Android only). */\r\n channel?: NotificationChannelConfig;\r\n};\r\n\r\n/** Snapshot of the current monitoring configuration and active state. */\r\nexport type MonitoringConfig = {\r\n /** Whether background monitoring is currently active. */\r\n isMonitoring: boolean;\r\n maxDistance?: number;\r\n exitDistance?: number;\r\n minRssi?: number;\r\n level?: 'all' | 'events';\r\n notifications?: NotificationConfig;\r\n};\r\n\r\n/** Options accepted by startMonitoring(). */\r\nexport type MonitoringOptions = {\r\n /**\r\n * Maximum distance in metres for distance-based enter events.\r\n * Exit events are always emitted when the region is lost.\r\n */\r\n maxDistance?: number;\r\n /**\r\n * Distance in metres at which exit events fire (must be ≥ maxDistance).\r\n * Creates a hysteresis band between enter and exit thresholds to prevent\r\n * rapid toggling near the boundary.\r\n *\r\n * Default when omitted: `maxDistance + min(maxDistance × 0.5, 2.5)`.\r\n * Only used when `maxDistance` is set.\r\n */\r\n exitDistance?: number;\r\n /**\r\n * Minimum RSSI (dBm) for a beacon reading to be considered valid.\r\n * Readings below this threshold are discarded as unreliable, preventing\r\n * false detections from reflected or distant signals.\r\n *\r\n * Default: -85. Typical range: -100 (very permissive) to -70 (strict).\r\n */\r\n minRssi?: number;\r\n /**\r\n * Controls which event types are emitted, logged, and forwarded to the API.\r\n *\r\n * - `'all'` (default): distance + enter + exit + timeout events.\r\n * - `'events'`: enter + exit + timeout only (no distance events).\r\n */\r\n level?: 'all' | 'events';\r\n /** Notification configuration overrides to apply for this monitoring session. */\r\n notifications?: NotificationConfig;\r\n};\r\n\r\n/** Eddystone frame type. */\r\nexport type EddystoneFrameType = \"uid\" | \"url\";\r\n\r\n/** Raw Eddystone beacon discovered during a scan. */\r\nexport type EddystoneScanResult = {\r\n frameType: EddystoneFrameType;\r\n /** 10-byte namespace ID as hex string (20 chars). Present for UID frames. */\r\n namespace?: string;\r\n /** 6-byte instance ID as hex string (12 chars). Present for UID frames. */\r\n instance?: string;\r\n /** Decoded URL. Present for URL frames. */\r\n url?: string;\r\n rssi: number;\r\n distance: number;\r\n txPower: number;\r\n /** BLE advertising device name. */\r\n name?: string;\r\n};\r\n\r\n/**\r\n * An Eddystone-UID beacon that has been paired/registered for monitoring.\r\n *\r\n * Note: Paired beacon data is stored unencrypted in UserDefaults (iOS) /\r\n * SharedPreferences (Android) and may be included in device backups.\r\n */\r\nexport type PairedEddystone = {\r\n identifier: string;\r\n /** 10-byte namespace ID as hex string (20 chars). */\r\n namespace: string;\r\n /** 6-byte instance ID as hex string (12 chars). */\r\n instance: string;\r\n /** BLE advertising device name, if provided at pairing time. */\r\n name?: string;\r\n /**\r\n * Timeout in seconds. When set, the module fires `onEddystoneTimeout` once\r\n * after the beacon has been continuously in range for this duration.\r\n * The timer resets if the beacon exits and re-enters range.\r\n *\r\n * The timeout countdown also starts if no BLE readings are received\r\n * for 60 seconds (e.g. due to Doze mode or background throttling).\r\n */\r\n timeoutSeconds?: number;\r\n};\r\n\r\n/** Payload for Eddystone enter/exit region events. */\r\nexport type EddystoneRegionEvent = {\r\n identifier: string;\r\n namespace: string;\r\n instance: string;\r\n event: \"enter\" | \"exit\";\r\n /** Measured distance in metres at the time of the event (–1 if unavailable). */\r\n distance: number;\r\n /** Signal strength in dBm at the time of the event (0 if unavailable). */\r\n rssi?: number;\r\n};\r\n\r\n/** Payload for periodic Eddystone distance update events during monitoring. */\r\nexport type EddystoneDistanceEvent = {\r\n identifier: string;\r\n namespace: string;\r\n instance: string;\r\n distance: number;\r\n /** Signal strength in dBm (0 if unavailable). */\r\n rssi?: number;\r\n};\r\n\r\n/** Payload for Eddystone timeout events (beacon in range for configured duration). */\r\nexport type EddystoneTimeoutEvent = {\r\n identifier: string;\r\n namespace: string;\r\n instance: string;\r\n /** Current distance in metres at the time the timeout fired. */\r\n distance: number;\r\n};\r\n\r\n/** Module event map. */\r\nexport type ExpoBeaconModuleEvents = {\r\n onBeaconEnter: (params: BeaconRegionEvent) => void;\r\n onBeaconExit: (params: BeaconRegionEvent) => void;\r\n onBeaconDistance: (params: BeaconDistanceEvent) => void;\r\n /** Fired once after a paired beacon has been continuously in range for its configured `timeoutSeconds`. */\r\n onBeaconTimeout: (params: BeaconTimeoutEvent) => void;\r\n /** Fired continuously during a live scan as each iBeacon is detected. */\r\n onBeaconFound: (params: BeaconScanResult) => void;\r\n /** Fired continuously during a live scan as each Eddystone beacon is detected. */\r\n onEddystoneFound: (params: EddystoneScanResult) => void;\r\n onEddystoneEnter: (params: EddystoneRegionEvent) => void;\r\n onEddystoneExit: (params: EddystoneRegionEvent) => void;\r\n onEddystoneDistance: (params: EddystoneDistanceEvent) => void;\r\n /** Fired once after a paired Eddystone has been continuously in range for its configured `timeoutSeconds`. */\r\n onEddystoneTimeout: (params: EddystoneTimeoutEvent) => void;\r\n};\r\n\r\n/** Options for filtering event logs. */\r\nexport type EventLogQueryOptions = {\r\n /** Maximum number of log entries to return (default: 1000, max: 10000). */\r\n limit?: number;\r\n /** Filter by event type (e.g. \"onBeaconEnter\", \"onBeaconExit\"). */\r\n eventType?: string;\r\n /** Only return events with timestamp >= this value (ms since epoch). */\r\n sinceTimestamp?: number;\r\n};\r\n\r\n/** A single logged beacon event entry. */\r\nexport type EventLogEntry = {\r\n id: number;\r\n /** Timestamp in milliseconds since epoch. */\r\n timestamp: number;\r\n /** The event type that was logged (e.g. \"onBeaconEnter\"). */\r\n eventType: string;\r\n /** Beacon identifier, if available. */\r\n identifier?: string;\r\n /** The full event payload that was sent to JS. */\r\n data: Record<string, unknown>;\r\n};\r\n"]}
|
|
1
|
+
{"version":3,"file":"ExpoBeacon.types.js","sourceRoot":"","sources":["../src/ExpoBeacon.types.ts"],"names":[],"mappings":"","sourcesContent":["/** Raw beacon discovered during a scan. */\r\nexport type BeaconScanResult = {\r\n uuid: string; // iBeacon proximity UUID (uppercase, formatted)\r\n major: number; // iBeacon major value (0–65535)\r\n minor: number; // iBeacon minor value (0–65535)\r\n rssi: number; // Signal strength in dBm (negative number)\r\n distance: number; // Estimated distance in meters\r\n txPower: number; // Calibrated TX power\r\n /** BLE advertising device name. May be undefined on iOS (CoreLocation does not expose it for iBeacon). */\r\n name?: string;\r\n};\r\n\r\n/**\r\n * A beacon that has been paired/registered for monitoring.\r\n *\r\n * Note: Paired beacon data is stored unencrypted in UserDefaults (iOS) /\r\n * SharedPreferences (Android) and may be included in device backups.\r\n */\r\nexport type PairedBeacon = {\r\n identifier: string; // User-defined label (e.g. \"lobby-door\")\r\n uuid: string;\r\n major: number;\r\n minor: number;\r\n /** BLE advertising device name, if provided at pairing time. */\r\n name?: string;\r\n /**\r\n * Timeout in seconds. When set, the module fires `onBeaconTimeout` once\r\n * after the beacon has been continuously in range for this duration.\r\n * The timer resets if the beacon exits and re-enters range.\r\n *\r\n * The timeout countdown also starts if no BLE readings are received\r\n * for 60 seconds (e.g. due to Doze mode or background throttling).\r\n */\r\n timeoutSeconds?: number;\r\n};\r\n\r\n/** Payload for enter/exit region events. */\r\nexport type BeaconRegionEvent = {\r\n identifier: string; // Matches PairedBeacon.identifier\r\n uuid: string;\r\n major: number;\r\n minor: number;\r\n event: \"enter\" | \"exit\";\r\n /** Measured distance in metres at the time of the event (–1 if unavailable). */\r\n distance: number;\r\n /** Signal strength in dBm at the time of the event (0 if unavailable). */\r\n rssi?: number;\r\n};\r\n\r\n/** Payload for periodic distance update events during monitoring. */\r\nexport type BeaconDistanceEvent = {\r\n identifier: string;\r\n uuid: string;\r\n major: number;\r\n minor: number;\r\n distance: number;\r\n /** Signal strength in dBm (0 if unavailable). */\r\n rssi?: number;\r\n};\r\n\r\n/** Payload for beacon timeout events (beacon in range for configured duration). */\r\nexport type BeaconTimeoutEvent = {\r\n identifier: string;\r\n uuid: string;\r\n major: number;\r\n minor: number;\r\n /** Current distance in metres at the time the timeout fired. */\r\n distance: number;\r\n};\r\n\r\n/** Configuration for beacon enter/exit event notifications. */\r\nexport type BeaconNotificationConfig = {\r\n /** Whether to show enter/exit notifications. Default: true. */\r\n enabled?: boolean;\r\n /** Notification title on beacon enter. Default: \"Beacon Entered\". */\r\n enterTitle?: string;\r\n /** Notification title on beacon exit. Default: \"Beacon Exited\". */\r\n exitTitle?: string;\r\n /** Notification title on beacon timeout. Default: \"Beacon Timeout\". */\r\n timeoutTitle?: string;\r\n /**\r\n * Notification body template. Supports {identifier} and {event} placeholders.\r\n * Default: \"{identifier} region {event}ed\".\r\n */\r\n body?: string;\r\n /** Play a sound with the notification (iOS only). Default: true. */\r\n sound?: boolean;\r\n /** Android drawable resource name for the notification icon (e.g. \"ic_notification\"). */\r\n icon?: string;\r\n};\r\n\r\n/** Configuration for the Android foreground service notification (persistent status bar entry). */\r\nexport type ForegroundServiceConfig = {\r\n /** Title of the persistent notification. Default: \"Beacon Monitoring Active\". */\r\n title?: string;\r\n /** Body text of the persistent notification. Default: \"Monitoring for iBeacons in the background\". */\r\n text?: string;\r\n /** Android drawable resource name for the notification icon. */\r\n icon?: string;\r\n};\r\n\r\n/** Configuration for the Android notification channel. */\r\nexport type NotificationChannelConfig = {\r\n /** Channel display name shown in system settings. Default: \"Beacon Monitoring\". */\r\n name?: string;\r\n /** Channel description shown in system settings. Default: \"Used for background iBeacon region monitoring\". */\r\n description?: string;\r\n /**\r\n * Channel importance level. Default: 'low'.\r\n * Note: Android may ignore decreases in importance after first channel creation until the app is reinstalled.\r\n */\r\n importance?: \"low\" | \"default\" | \"high\";\r\n};\r\n\r\n/** Combined notification configuration for all notification types. */\r\nexport type NotificationConfig = {\r\n /** Settings for beacon enter/exit event notifications. */\r\n beaconEvents?: BeaconNotificationConfig;\r\n /** Settings for the persistent foreground service notification (Android only). */\r\n foregroundService?: ForegroundServiceConfig;\r\n /** Settings for the Android notification channel (Android only). */\r\n channel?: NotificationChannelConfig;\r\n};\r\n\r\n/** Snapshot of the current monitoring configuration and active state. */\r\nexport type MonitoringConfig = {\r\n /** Whether background monitoring is currently active. */\r\n isMonitoring: boolean;\r\n maxDistance?: number;\r\n exitDistance?: number;\r\n minRssi?: number;\r\n level?: 'all' | 'events';\r\n notifications?: NotificationConfig;\r\n};\r\n\r\n/** Current state snapshot for a paired monitored device. */\r\nexport type MonitoredDeviceState =\r\n | {\r\n kind: \"ibeacon\";\r\n identifier: string;\r\n uuid: string;\r\n major: number;\r\n minor: number;\r\n state: \"entered\" | \"exited\";\r\n /** Current distance in metres, or null when exited or no live reading is available. */\r\n distance: number | null;\r\n }\r\n | {\r\n kind: \"eddystone\";\r\n identifier: string;\r\n namespace: string;\r\n instance: string;\r\n state: \"entered\" | \"exited\";\r\n /** Current distance in metres, or null when exited or no live reading is available. */\r\n distance: number | null;\r\n };\r\n\r\n/** Options accepted by startMonitoring(). */\r\nexport type MonitoringOptions = {\r\n /**\r\n * Maximum distance in metres for distance-based enter events.\r\n * Exit events are always emitted when the region is lost.\r\n */\r\n maxDistance?: number;\r\n /**\r\n * Distance in metres at which exit events fire (must be ≥ maxDistance).\r\n * Creates a hysteresis band between enter and exit thresholds to prevent\r\n * rapid toggling near the boundary.\r\n *\r\n * Default when omitted: `maxDistance + min(maxDistance × 0.5, 2.5)`.\r\n * Only used when `maxDistance` is set.\r\n */\r\n exitDistance?: number;\r\n /**\r\n * Minimum RSSI (dBm) for a beacon reading to be considered valid.\r\n * Readings below this threshold are discarded as unreliable, preventing\r\n * false detections from reflected or distant signals.\r\n *\r\n * Default: -85. Typical range: -100 (very permissive) to -70 (strict).\r\n */\r\n minRssi?: number;\r\n /**\r\n * Controls which event types are emitted, logged, and forwarded to the API.\r\n *\r\n * - `'all'` (default): distance + enter + exit + timeout events.\r\n * - `'events'`: enter + exit + timeout only (no distance events).\r\n */\r\n level?: 'all' | 'events';\r\n /** Notification configuration overrides to apply for this monitoring session. */\r\n notifications?: NotificationConfig;\r\n};\r\n\r\n/** Eddystone frame type. */\r\nexport type EddystoneFrameType = \"uid\" | \"url\";\r\n\r\n/** Raw Eddystone beacon discovered during a scan. */\r\nexport type EddystoneScanResult = {\r\n frameType: EddystoneFrameType;\r\n /** 10-byte namespace ID as hex string (20 chars). Present for UID frames. */\r\n namespace?: string;\r\n /** 6-byte instance ID as hex string (12 chars). Present for UID frames. */\r\n instance?: string;\r\n /** Decoded URL. Present for URL frames. */\r\n url?: string;\r\n rssi: number;\r\n distance: number;\r\n txPower: number;\r\n /** BLE advertising device name. */\r\n name?: string;\r\n};\r\n\r\n/**\r\n * An Eddystone-UID beacon that has been paired/registered for monitoring.\r\n *\r\n * Note: Paired beacon data is stored unencrypted in UserDefaults (iOS) /\r\n * SharedPreferences (Android) and may be included in device backups.\r\n */\r\nexport type PairedEddystone = {\r\n identifier: string;\r\n /** 10-byte namespace ID as hex string (20 chars). */\r\n namespace: string;\r\n /** 6-byte instance ID as hex string (12 chars). */\r\n instance: string;\r\n /** BLE advertising device name, if provided at pairing time. */\r\n name?: string;\r\n /**\r\n * Timeout in seconds. When set, the module fires `onEddystoneTimeout` once\r\n * after the beacon has been continuously in range for this duration.\r\n * The timer resets if the beacon exits and re-enters range.\r\n *\r\n * The timeout countdown also starts if no BLE readings are received\r\n * for 60 seconds (e.g. due to Doze mode or background throttling).\r\n */\r\n timeoutSeconds?: number;\r\n};\r\n\r\n/** Payload for Eddystone enter/exit region events. */\r\nexport type EddystoneRegionEvent = {\r\n identifier: string;\r\n namespace: string;\r\n instance: string;\r\n event: \"enter\" | \"exit\";\r\n /** Measured distance in metres at the time of the event (–1 if unavailable). */\r\n distance: number;\r\n /** Signal strength in dBm at the time of the event (0 if unavailable). */\r\n rssi?: number;\r\n};\r\n\r\n/** Payload for periodic Eddystone distance update events during monitoring. */\r\nexport type EddystoneDistanceEvent = {\r\n identifier: string;\r\n namespace: string;\r\n instance: string;\r\n distance: number;\r\n /** Signal strength in dBm (0 if unavailable). */\r\n rssi?: number;\r\n};\r\n\r\n/** Payload for Eddystone timeout events (beacon in range for configured duration). */\r\nexport type EddystoneTimeoutEvent = {\r\n identifier: string;\r\n namespace: string;\r\n instance: string;\r\n /** Current distance in metres at the time the timeout fired. */\r\n distance: number;\r\n};\r\n\r\n/** Payload for native beacon error events (monitoring/ranging failures). */\r\nexport type BeaconErrorEvent = {\r\n /** Region or constraint identifier, empty string if unavailable. */\r\n identifier: string;\r\n /** Machine-readable error code (e.g. \"MONITORING_FAILED\", \"RANGING_FAILED\", \"SECURITY_EXCEPTION\"). */\r\n code: string;\r\n /** Human-readable error message from the native layer. */\r\n message: string;\r\n};\r\n\r\n/** Module event map. */\r\nexport type ExpoBeaconModuleEvents = {\r\n onBeaconEnter: (params: BeaconRegionEvent) => void;\r\n onBeaconExit: (params: BeaconRegionEvent) => void;\r\n onBeaconDistance: (params: BeaconDistanceEvent) => void;\r\n /** Fired once after a paired beacon has been continuously in range for its configured `timeoutSeconds`. */\r\n onBeaconTimeout: (params: BeaconTimeoutEvent) => void;\r\n /** Fired continuously during a live scan as each iBeacon is detected. */\r\n onBeaconFound: (params: BeaconScanResult) => void;\r\n /** Fired continuously during a live scan as each Eddystone beacon is detected. */\r\n onEddystoneFound: (params: EddystoneScanResult) => void;\r\n onEddystoneEnter: (params: EddystoneRegionEvent) => void;\r\n onEddystoneExit: (params: EddystoneRegionEvent) => void;\r\n onEddystoneDistance: (params: EddystoneDistanceEvent) => void;\r\n /** Fired once after a paired Eddystone has been continuously in range for its configured `timeoutSeconds`. */\r\n onEddystoneTimeout: (params: EddystoneTimeoutEvent) => void;\r\n /** Fired when a native monitoring or ranging failure occurs (logged to DB and forwarded to JS). */\r\n onBeaconError: (params: BeaconErrorEvent) => void;\r\n};\r\n\r\n/** Options for filtering event logs. */\r\nexport type EventLogQueryOptions = {\r\n /** Maximum number of log entries to return (default: 1000, max: 10000). */\r\n limit?: number;\r\n /** Filter by event type (e.g. \"onBeaconEnter\", \"onBeaconExit\"). */\r\n eventType?: string;\r\n /** Only return events with timestamp >= this value (ms since epoch). */\r\n sinceTimestamp?: number;\r\n};\r\n\r\n/** A single logged beacon event entry. */\r\nexport type EventLogEntry = {\r\n id: number;\r\n /** Timestamp in milliseconds since epoch. */\r\n timestamp: number;\r\n /** The event type that was logged (e.g. \"onBeaconEnter\"). */\r\n eventType: string;\r\n /** Beacon identifier, if available. */\r\n identifier?: string;\r\n /** The full event payload that was sent to JS. */\r\n data: Record<string, unknown>;\r\n};\r\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NativeModule } from "expo";
|
|
2
|
-
import { ExpoBeaconModuleEvents, BeaconScanResult, EddystoneScanResult, PairedBeacon, PairedEddystone, NotificationConfig, MonitoringOptions, MonitoringConfig, EventLogQueryOptions, EventLogEntry } from "./ExpoBeacon.types";
|
|
2
|
+
import { ExpoBeaconModuleEvents, BeaconScanResult, EddystoneScanResult, PairedBeacon, PairedEddystone, NotificationConfig, MonitoringOptions, MonitoringConfig, MonitoredDeviceState, EventLogQueryOptions, EventLogEntry } from "./ExpoBeacon.types";
|
|
3
3
|
declare class ExpoBeaconModule extends NativeModule<ExpoBeaconModuleEvents> {
|
|
4
4
|
/**
|
|
5
5
|
* Start a one-shot iBeacon scan. Resolves with discovered beacons after scanDuration ms.
|
|
@@ -117,6 +117,15 @@ declare class ExpoBeaconModule extends NativeModule<ExpoBeaconModuleEvents> {
|
|
|
117
117
|
* Option fields are undefined if not explicitly set.
|
|
118
118
|
*/
|
|
119
119
|
getMonitoringConfig(): MonitoringConfig;
|
|
120
|
+
/**
|
|
121
|
+
* Return the current state snapshot for a paired monitored device.
|
|
122
|
+
* Returns null when no paired device matches the identifier.
|
|
123
|
+
*/
|
|
124
|
+
getMonitoredDeviceState(identifier: string): MonitoredDeviceState | null;
|
|
125
|
+
/**
|
|
126
|
+
* Return the current state snapshot for all paired monitored devices.
|
|
127
|
+
*/
|
|
128
|
+
getMonitoredDeviceStates(): MonitoredDeviceState[];
|
|
120
129
|
/**
|
|
121
130
|
* Return the current API forwarding configuration.
|
|
122
131
|
* Each field is `null` if not set.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoBeaconModule.d.ts","sourceRoot":"","sources":["../src/ExpoBeaconModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,MAAM,CAAC;AAEzD,OAAO,EACL,sBAAsB,EACtB,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,EACZ,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACd,MAAM,oBAAoB,CAAC;AAE5B,OAAO,OAAO,gBAAiB,SAAQ,YAAY,CAAC,sBAAsB,CAAC;IACzE;;;;;;;;;;;OAWG;IACH,mBAAmB,CACjB,KAAK,CAAC,EAAE,MAAM,EAAE,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAE9B;;;;;OAKG;IACH,sBAAsB,CACpB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAEjC;;OAEG;IACH,UAAU,CACR,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,MAAM,EACb,cAAc,CAAC,EAAE,MAAM,GACtB,IAAI;IAEP;;OAEG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAEtC;;OAEG;IACH,gBAAgB,IAAI,YAAY,EAAE;IAElC;;OAEG;IACH,aAAa,CACX,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,MAAM,EACb,cAAc,CAAC,EAAE,MAAM,GACtB,IAAI;IAEP;;OAEG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAEzC;;OAEG;IACH,mBAAmB,IAAI,eAAe,EAAE;IAExC;;;OAGG;IACH,qBAAqB,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI;IAEvD;;;;;;;OAOG;IACH,eAAe,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAEpE;;OAEG;IACH,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAE/B;;;OAGG;IACH,mBAAmB,IAAI,IAAI;IAE3B,iEAAiE;IACjE,kBAAkB,IAAI,IAAI;IAE1B;;;OAGG;IACH,UAAU,IAAI,IAAI;IAElB,yEAAyE;IACzE,uBAAuB,IAAI,OAAO,CAAC,OAAO,CAAC;IAE3C;;;OAGG;IACH,2BAA2B,IAAI,OAAO;IAEtC;;;;;OAKG;IACH,mCAAmC,IAAI,OAAO,CAAC,OAAO,CAAC;IAEvD,4FAA4F;IAC5F,kBAAkB,IAAI,IAAI;IAE1B,oEAAoE;IACpE,mBAAmB,IAAI,IAAI;IAE3B;;;OAGG;IACH,YAAY,CAAC,OAAO,CAAC,EAAE,oBAAoB,GAAG,aAAa,EAAE;IAE7D,kDAAkD;IAClD,cAAc,IAAI,IAAI;IAEtB,mEAAmE;IACnE,gBAAgB,IAAI,IAAI;IAExB;;;;;;;;OAQG;IACH,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IAE/D;;;OAGG;IACH,mBAAmB,IAAI,gBAAgB;IAEvC;;;OAGG;IACH,cAAc,IAAI;QAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;CACnF;;AAED,wBAAmE"}
|
|
1
|
+
{"version":3,"file":"ExpoBeaconModule.d.ts","sourceRoot":"","sources":["../src/ExpoBeaconModule.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoBeaconModule.js","sourceRoot":"","sources":["../src/ExpoBeaconModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"ExpoBeaconModule.js","sourceRoot":"","sources":["../src/ExpoBeaconModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAiMzD,eAAe,mBAAmB,CAAmB,YAAY,CAAC,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from \"expo\";\r\n\r\nimport {\r\n ExpoBeaconModuleEvents,\r\n BeaconScanResult,\r\n EddystoneScanResult,\r\n PairedBeacon,\r\n PairedEddystone,\r\n NotificationConfig,\r\n MonitoringOptions,\r\n MonitoringConfig,\r\n MonitoredDeviceState,\r\n EventLogQueryOptions,\r\n EventLogEntry,\r\n} from \"./ExpoBeacon.types\";\r\n\r\ndeclare class ExpoBeaconModule extends NativeModule<ExpoBeaconModuleEvents> {\r\n /**\r\n * Start a one-shot iBeacon scan. Resolves with discovered beacons after scanDuration ms.\r\n *\r\n * Pass one or more UUIDs to scan for specific beacons (uses CoreLocation on iOS).\r\n * On iOS, at least one UUID is required — Apple strips iBeacon data from BLE\r\n * advertisements, making wildcard discovery impossible. When you pass an empty\r\n * array, the module automatically uses UUIDs from paired beacons.\r\n * On Android, pass an empty array to discover all nearby iBeacons.\r\n *\r\n * @param uuids Proximity UUIDs to filter by. Empty/omitted = use paired UUIDs (iOS) or wildcard (Android).\r\n * @param scanDuration Duration in ms (default 5000)\r\n */\r\n scanForBeaconsAsync(\r\n uuids?: string[],\r\n scanDuration?: number,\r\n ): Promise<BeaconScanResult[]>;\r\n\r\n /**\r\n * Start a one-shot Eddystone beacon scan using BLE.\r\n * Discovers Eddystone-UID and Eddystone-URL frames.\r\n *\r\n * @param scanDuration Duration in ms (default 5000)\r\n */\r\n scanForEddystonesAsync(\r\n scanDuration?: number,\r\n ): Promise<EddystoneScanResult[]>;\r\n\r\n /**\r\n * Register a beacon for persistent region monitoring.\r\n */\r\n pairBeacon(\r\n identifier: string,\r\n uuid: string,\r\n major: number,\r\n minor: number,\r\n name?: string,\r\n timeoutSeconds?: number,\r\n ): void;\r\n\r\n /**\r\n * Remove a previously paired beacon.\r\n */\r\n unpairBeacon(identifier: string): void;\r\n\r\n /**\r\n * Return all currently paired beacons.\r\n */\r\n getPairedBeacons(): PairedBeacon[];\r\n\r\n /**\r\n * Register an Eddystone-UID beacon for persistent monitoring.\r\n */\r\n pairEddystone(\r\n identifier: string,\r\n namespace: string,\r\n instance: string,\r\n name?: string,\r\n timeoutSeconds?: number,\r\n ): void;\r\n\r\n /**\r\n * Remove a previously paired Eddystone beacon.\r\n */\r\n unpairEddystone(identifier: string): void;\r\n\r\n /**\r\n * Return all currently paired Eddystone beacons.\r\n */\r\n getPairedEddystones(): PairedEddystone[];\r\n\r\n /**\r\n * Set persistent notification configuration. Settings are saved and applied to all\r\n * subsequent monitoring sessions until explicitly changed.\r\n */\r\n setNotificationConfig(config: NotificationConfig): void;\r\n\r\n /**\r\n * Start background region monitoring for all paired beacons.\r\n * On Android starts a foreground service.\r\n * On iOS starts CLLocationManager region monitoring.\r\n *\r\n * Accepts a plain number (backward-compatible maxDistance shorthand) or a\r\n * MonitoringOptions object with maxDistance and/or notification overrides.\r\n */\r\n startMonitoring(options?: MonitoringOptions | number): Promise<void>;\r\n\r\n /**\r\n * Stop background region monitoring.\r\n */\r\n stopMonitoring(): Promise<void>;\r\n\r\n /**\r\n * Start a continuous BLE scan. Fires `onBeaconFound` events as beacons are detected.\r\n * Call stopContinuousScan() to end the scan.\r\n */\r\n startContinuousScan(): void;\r\n\r\n /** Stop the continuous scan started by startContinuousScan(). */\r\n stopContinuousScan(): void;\r\n\r\n /**\r\n * Cancel any in-progress one-shot scan (iBeacon or Eddystone).\r\n * The pending promise will be rejected with code \"SCAN_CANCELLED\".\r\n */\r\n cancelScan(): void;\r\n\r\n /** Request Bluetooth + Location permissions. Returns true if granted. */\r\n requestPermissionsAsync(): Promise<boolean>;\r\n\r\n /**\r\n * Check whether the app is exempt from Android battery optimizations.\r\n * Always returns true on iOS and web (no equivalent concept).\r\n */\r\n isBatteryOptimizationExempt(): boolean;\r\n\r\n /**\r\n * Request exemption from Android battery optimizations.\r\n * Opens the system dialog asking the user to whitelist this app.\r\n * Returns true if the dialog was shown (or already exempt), false on failure.\r\n * Always resolves true on iOS and web.\r\n */\r\n requestBatteryOptimizationExemption(): Promise<boolean>;\r\n\r\n /** Enable SQLite event logging. All beacon events will be persisted to a local database. */\r\n enableEventLogging(): void;\r\n\r\n /** Disable event logging. Previously logged events are retained. */\r\n disableEventLogging(): void;\r\n\r\n /**\r\n * Retrieve logged beacon events from the SQLite database.\r\n * @param options Optional filters (limit, eventType, sinceTimestamp).\r\n */\r\n getEventLogs(options?: EventLogQueryOptions): EventLogEntry[];\r\n\r\n /** Delete all logged events from the database. */\r\n clearEventLogs(): void;\r\n\r\n /** Delete the entire event log database. Also disables logging. */\r\n destroyEventLogs(): void;\r\n\r\n /**\r\n * Configure a remote API endpoint for native event forwarding.\r\n * Once set, beacon events are POSTed directly from native code,\r\n * ensuring delivery even when the JS bridge is not active (app backgrounded).\r\n *\r\n * @param url The API endpoint URL to POST events to.\r\n * @param apiKey Optional API key sent as X-CSFR-Token header.\r\n * @param id Optional identifier appended to every forwarded event payload.\r\n */\r\n setApiEndpoint(url: string, apiKey?: string, id?: string): void;\r\n\r\n /**\r\n * Return the current monitoring configuration and active state.\r\n * Option fields are undefined if not explicitly set.\r\n */\r\n getMonitoringConfig(): MonitoringConfig;\r\n\r\n /**\r\n * Return the current state snapshot for a paired monitored device.\r\n * Returns null when no paired device matches the identifier.\r\n */\r\n getMonitoredDeviceState(identifier: string): MonitoredDeviceState | null;\r\n\r\n /**\r\n * Return the current state snapshot for all paired monitored devices.\r\n */\r\n getMonitoredDeviceStates(): MonitoredDeviceState[];\r\n\r\n /**\r\n * Return the current API forwarding configuration.\r\n * Each field is `null` if not set.\r\n */\r\n getApiEndpoint(): { url: string | null; apiKey: string | null; id: string | null };\r\n}\r\n\r\nexport default requireNativeModule<ExpoBeaconModule>(\"ExpoBeacon\");\r\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ExpoBeaconModuleEvents, BeaconScanResult, EddystoneScanResult, PairedBeacon, PairedEddystone, EventLogQueryOptions, EventLogEntry } from "./ExpoBeacon.types";
|
|
1
|
+
import type { ExpoBeaconModuleEvents, BeaconScanResult, EddystoneScanResult, PairedBeacon, PairedEddystone, MonitoredDeviceState, EventLogQueryOptions, EventLogEntry } from "./ExpoBeacon.types";
|
|
2
2
|
declare const stub: {
|
|
3
3
|
scanForBeaconsAsync: (_uuids: string[], _scanDuration?: number) => Promise<BeaconScanResult[]>;
|
|
4
4
|
scanForEddystonesAsync: (_scanDuration?: number) => Promise<EddystoneScanResult[]>;
|
|
@@ -21,6 +21,8 @@ declare const stub: {
|
|
|
21
21
|
clearEventLogs: () => void;
|
|
22
22
|
destroyEventLogs: () => void;
|
|
23
23
|
getMonitoringConfig: () => never;
|
|
24
|
+
getMonitoredDeviceState: (_identifier: string) => MonitoredDeviceState | null;
|
|
25
|
+
getMonitoredDeviceStates: () => MonitoredDeviceState[];
|
|
24
26
|
getApiEndpoint: () => {
|
|
25
27
|
url: string | null;
|
|
26
28
|
apiKey: string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoBeaconModule.web.d.ts","sourceRoot":"","sources":["../src/ExpoBeaconModule.web.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,sBAAsB,EACtB,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,EACZ,eAAe,EACf,oBAAoB,EACpB,aAAa,EACd,MAAM,oBAAoB,CAAC;AAM5B,QAAA,MAAM,IAAI;kCAEE,MAAM,EAAE,kBACA,MAAM,KACrB,OAAO,CAAC,gBAAgB,EAAE,CAAC;6CAEZ,MAAM,KACrB,OAAO,CAAC,mBAAmB,EAAE,CAAC;8BAElB,MAAM,SACZ,MAAM,UACL,MAAM,UACN,MAAM,KACb,IAAI;gCACqB,MAAM,KAAG,IAAI;4BACnB,YAAY,EAAE;iCAErB,MAAM,cACP,MAAM,aACP,MAAM,KAChB,IAAI;mCACwB,MAAM,KAAG,IAAI;+BACnB,eAAe,EAAE;2BACrB,OAAO,CAAC,IAAI,CAAC;0BACd,OAAO,CAAC,IAAI,CAAC;+BACR,IAAI;8BACL,IAAI;sBACZ,IAAI;qCACa,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,IAAI;mCAClC,OAAO,CAAC,OAAO,CAAC;8BACrB,IAAI;+BACH,IAAI;8BACH,oBAAoB,KAAG,aAAa,EAAE;0BAC5C,IAAI;4BACF,IAAI;;
|
|
1
|
+
{"version":3,"file":"ExpoBeaconModule.web.d.ts","sourceRoot":"","sources":["../src/ExpoBeaconModule.web.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,sBAAsB,EACtB,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,EACZ,eAAe,EACf,oBAAoB,EACpB,oBAAoB,EACpB,aAAa,EACd,MAAM,oBAAoB,CAAC;AAM5B,QAAA,MAAM,IAAI;kCAEE,MAAM,EAAE,kBACA,MAAM,KACrB,OAAO,CAAC,gBAAgB,EAAE,CAAC;6CAEZ,MAAM,KACrB,OAAO,CAAC,mBAAmB,EAAE,CAAC;8BAElB,MAAM,SACZ,MAAM,UACL,MAAM,UACN,MAAM,KACb,IAAI;gCACqB,MAAM,KAAG,IAAI;4BACnB,YAAY,EAAE;iCAErB,MAAM,cACP,MAAM,aACP,MAAM,KAChB,IAAI;mCACwB,MAAM,KAAG,IAAI;+BACnB,eAAe,EAAE;2BACrB,OAAO,CAAC,IAAI,CAAC;0BACd,OAAO,CAAC,IAAI,CAAC;+BACR,IAAI;8BACL,IAAI;sBACZ,IAAI;qCACa,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,IAAI;mCAClC,OAAO,CAAC,OAAO,CAAC;8BACrB,IAAI;+BACH,IAAI;8BACH,oBAAoB,KAAG,aAAa,EAAE;0BAC5C,IAAI;4BACF,IAAI;;2CAEa,MAAM,KAAG,oBAAoB,GAAG,IAAI;oCAC7C,oBAAoB,EAAE;0BAChC;QAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;uCACnD,OAAO;+CACC,OAAO,CAAC,OAAO,CAAC;8BAC/B,MAAM,sBAAsB,aAAa,GAAG;;;qCAGrC,MAAM,sBAAsB;CAC9D,CAAC;AAEF,eAAe,IAAI,CAAC"}
|
|
@@ -23,6 +23,8 @@ const stub = {
|
|
|
23
23
|
clearEventLogs: () => notSupported(),
|
|
24
24
|
destroyEventLogs: () => notSupported(),
|
|
25
25
|
getMonitoringConfig: () => notSupported(),
|
|
26
|
+
getMonitoredDeviceState: (_identifier) => notSupported(),
|
|
27
|
+
getMonitoredDeviceStates: () => notSupported(),
|
|
26
28
|
getApiEndpoint: () => notSupported(),
|
|
27
29
|
isBatteryOptimizationExempt: () => true,
|
|
28
30
|
requestBatteryOptimizationExemption: () => Promise.resolve(true),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoBeaconModule.web.js","sourceRoot":"","sources":["../src/ExpoBeaconModule.web.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ExpoBeaconModule.web.js","sourceRoot":"","sources":["../src/ExpoBeaconModule.web.ts"],"names":[],"mappings":"AAWA,MAAM,YAAY,GAAG,GAAU,EAAE;IAC/B,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;AAC1D,CAAC,CAAC;AAEF,MAAM,IAAI,GAAG;IACX,mBAAmB,EAAE,CACnB,MAAgB,EAChB,aAAsB,EACO,EAAE,CAAC,YAAY,EAAE;IAChD,sBAAsB,EAAE,CACtB,aAAsB,EACU,EAAE,CAAC,YAAY,EAAE;IACnD,UAAU,EAAE,CACV,WAAmB,EACnB,KAAa,EACb,MAAc,EACd,MAAc,EACR,EAAE,CAAC,YAAY,EAAE;IACzB,YAAY,EAAE,CAAC,WAAmB,EAAQ,EAAE,CAAC,YAAY,EAAE;IAC3D,gBAAgB,EAAE,GAAmB,EAAE,CAAC,YAAY,EAAE;IACtD,aAAa,EAAE,CACb,WAAmB,EACnB,UAAkB,EAClB,SAAiB,EACX,EAAE,CAAC,YAAY,EAAE;IACzB,eAAe,EAAE,CAAC,WAAmB,EAAQ,EAAE,CAAC,YAAY,EAAE;IAC9D,mBAAmB,EAAE,GAAsB,EAAE,CAAC,YAAY,EAAE;IAC5D,eAAe,EAAE,GAAkB,EAAE,CAAC,YAAY,EAAE;IACpD,cAAc,EAAE,GAAkB,EAAE,CAAC,YAAY,EAAE;IACnD,mBAAmB,EAAE,GAAS,EAAE,CAAC,YAAY,EAAE;IAC/C,kBAAkB,EAAE,GAAS,EAAE,CAAC,YAAY,EAAE;IAC9C,UAAU,EAAE,GAAS,EAAE,CAAC,YAAY,EAAE;IACtC,qBAAqB,EAAE,CAAC,OAAgC,EAAQ,EAAE,CAAC,YAAY,EAAE;IACjF,uBAAuB,EAAE,GAAqB,EAAE,CAAC,YAAY,EAAE;IAC/D,kBAAkB,EAAE,GAAS,EAAE,CAAC,YAAY,EAAE;IAC9C,mBAAmB,EAAE,GAAS,EAAE,CAAC,YAAY,EAAE;IAC/C,YAAY,EAAE,CAAC,QAA+B,EAAmB,EAAE,CAAC,YAAY,EAAE;IAClF,cAAc,EAAE,GAAS,EAAE,CAAC,YAAY,EAAE;IAC1C,gBAAgB,EAAE,GAAS,EAAE,CAAC,YAAY,EAAE;IAC5C,mBAAmB,EAAE,GAAG,EAAE,CAAC,YAAY,EAAE;IACzC,uBAAuB,EAAE,CAAC,WAAmB,EAA+B,EAAE,CAAC,YAAY,EAAE;IAC7F,wBAAwB,EAAE,GAA2B,EAAE,CAAC,YAAY,EAAE;IACtE,cAAc,EAAE,GAAqE,EAAE,CAAC,YAAY,EAAE;IACtG,2BAA2B,EAAE,GAAY,EAAE,CAAC,IAAI;IAChD,mCAAmC,EAAE,GAAqB,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;IAClF,WAAW,EAAE,CAAC,UAAwC,EAAE,SAAc,EAAE,EAAE,CAAC,CAAC;QAC1E,MAAM,EAAE,GAAG,EAAE,GAAE,CAAC;KACjB,CAAC;IACF,kBAAkB,EAAE,CAAC,UAAwC,EAAE,EAAE,GAAE,CAAC;CACrE,CAAC;AAEF,eAAe,IAAI,CAAC","sourcesContent":["import type {\r\n ExpoBeaconModuleEvents,\r\n BeaconScanResult,\r\n EddystoneScanResult,\r\n PairedBeacon,\r\n PairedEddystone,\r\n MonitoredDeviceState,\r\n EventLogQueryOptions,\r\n EventLogEntry,\r\n} from \"./ExpoBeacon.types\";\r\n\r\nconst notSupported = (): never => {\r\n throw new Error(\"expo-beacon is not supported on web.\");\r\n};\r\n\r\nconst stub = {\r\n scanForBeaconsAsync: (\r\n _uuids: string[],\r\n _scanDuration?: number,\r\n ): Promise<BeaconScanResult[]> => notSupported(),\r\n scanForEddystonesAsync: (\r\n _scanDuration?: number,\r\n ): Promise<EddystoneScanResult[]> => notSupported(),\r\n pairBeacon: (\r\n _identifier: string,\r\n _uuid: string,\r\n _major: number,\r\n _minor: number,\r\n ): void => notSupported(),\r\n unpairBeacon: (_identifier: string): void => notSupported(),\r\n getPairedBeacons: (): PairedBeacon[] => notSupported(),\r\n pairEddystone: (\r\n _identifier: string,\r\n _namespace: string,\r\n _instance: string,\r\n ): void => notSupported(),\r\n unpairEddystone: (_identifier: string): void => notSupported(),\r\n getPairedEddystones: (): PairedEddystone[] => notSupported(),\r\n startMonitoring: (): Promise<void> => notSupported(),\r\n stopMonitoring: (): Promise<void> => notSupported(),\r\n startContinuousScan: (): void => notSupported(),\r\n stopContinuousScan: (): void => notSupported(),\r\n cancelScan: (): void => notSupported(),\r\n setNotificationConfig: (_config: Record<string, unknown>): void => notSupported(),\r\n requestPermissionsAsync: (): Promise<boolean> => notSupported(),\r\n enableEventLogging: (): void => notSupported(),\r\n disableEventLogging: (): void => notSupported(),\r\n getEventLogs: (_options?: EventLogQueryOptions): EventLogEntry[] => notSupported(),\r\n clearEventLogs: (): void => notSupported(),\r\n destroyEventLogs: (): void => notSupported(),\r\n getMonitoringConfig: () => notSupported(),\r\n getMonitoredDeviceState: (_identifier: string): MonitoredDeviceState | null => notSupported(),\r\n getMonitoredDeviceStates: (): MonitoredDeviceState[] => notSupported(),\r\n getApiEndpoint: (): { url: string | null; apiKey: string | null; id: string | null } => notSupported(),\r\n isBatteryOptimizationExempt: (): boolean => true,\r\n requestBatteryOptimizationExemption: (): Promise<boolean> => Promise.resolve(true),\r\n addListener: (_eventName: keyof ExpoBeaconModuleEvents, _listener: any) => ({\r\n remove: () => {},\r\n }),\r\n removeAllListeners: (_eventName: keyof ExpoBeaconModuleEvents) => {},\r\n};\r\n\r\nexport default stub;\r\n"]}
|
package/build/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { default } from "./ExpoBeaconModule";
|
|
2
|
-
export type { BeaconScanResult, PairedBeacon, BeaconRegionEvent, BeaconDistanceEvent, BeaconTimeoutEvent, ExpoBeaconModuleEvents, NotificationConfig, MonitoringOptions, MonitoringConfig, BeaconNotificationConfig, ForegroundServiceConfig, NotificationChannelConfig, EddystoneFrameType, EddystoneScanResult, PairedEddystone, EddystoneRegionEvent, EddystoneDistanceEvent, EddystoneTimeoutEvent, EventLogQueryOptions, EventLogEntry, } from "./ExpoBeacon.types";
|
|
2
|
+
export type { BeaconScanResult, PairedBeacon, BeaconRegionEvent, BeaconDistanceEvent, BeaconTimeoutEvent, ExpoBeaconModuleEvents, NotificationConfig, MonitoringOptions, MonitoringConfig, MonitoredDeviceState, BeaconNotificationConfig, ForegroundServiceConfig, NotificationChannelConfig, EddystoneFrameType, EddystoneScanResult, PairedEddystone, EddystoneRegionEvent, EddystoneDistanceEvent, EddystoneTimeoutEvent, EventLogQueryOptions, EventLogEntry, } from "./ExpoBeacon.types";
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAG7C,YAAY,EACV,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,EACtB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,wBAAwB,EACxB,uBAAuB,EACvB,yBAAyB,EACzB,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,oBAAoB,EACpB,aAAa,GACd,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAG7C,YAAY,EACV,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,EACtB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,EACpB,wBAAwB,EACxB,uBAAuB,EACvB,yBAAyB,EACzB,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,oBAAoB,EACpB,aAAa,GACd,MAAM,oBAAoB,CAAC"}
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC","sourcesContent":["// Native module (default export)\r\nexport { default } from \"./ExpoBeaconModule\";\r\n\r\n// All public types\r\nexport type {\r\n BeaconScanResult,\r\n PairedBeacon,\r\n BeaconRegionEvent,\r\n BeaconDistanceEvent,\r\n BeaconTimeoutEvent,\r\n ExpoBeaconModuleEvents,\r\n NotificationConfig,\r\n MonitoringOptions,\r\n MonitoringConfig,\r\n BeaconNotificationConfig,\r\n ForegroundServiceConfig,\r\n NotificationChannelConfig,\r\n EddystoneFrameType,\r\n EddystoneScanResult,\r\n PairedEddystone,\r\n EddystoneRegionEvent,\r\n EddystoneDistanceEvent,\r\n EddystoneTimeoutEvent,\r\n EventLogQueryOptions,\r\n EventLogEntry,\r\n} from \"./ExpoBeacon.types\";\r\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC","sourcesContent":["// Native module (default export)\r\nexport { default } from \"./ExpoBeaconModule\";\r\n\r\n// All public types\r\nexport type {\r\n BeaconScanResult,\r\n PairedBeacon,\r\n BeaconRegionEvent,\r\n BeaconDistanceEvent,\r\n BeaconTimeoutEvent,\r\n ExpoBeaconModuleEvents,\r\n NotificationConfig,\r\n MonitoringOptions,\r\n MonitoringConfig,\r\n MonitoredDeviceState,\r\n BeaconNotificationConfig,\r\n ForegroundServiceConfig,\r\n NotificationChannelConfig,\r\n EddystoneFrameType,\r\n EddystoneScanResult,\r\n PairedEddystone,\r\n EddystoneRegionEvent,\r\n EddystoneDistanceEvent,\r\n EddystoneTimeoutEvent,\r\n EventLogQueryOptions,\r\n EventLogEntry,\r\n} from \"./ExpoBeacon.types\";\r\n"]}
|
|
@@ -145,17 +145,19 @@ public class ExpoBeaconModule: Module {
|
|
|
145
145
|
self.migrateUserDefaultsIfNeeded()
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
Events("onBeaconEnter", "onBeaconExit", "onBeaconDistance", "onBeaconTimeout", "onBeaconFound", "onEddystoneFound", "onEddystoneEnter", "onEddystoneExit", "onEddystoneDistance", "onEddystoneTimeout")
|
|
148
|
+
Events("onBeaconEnter", "onBeaconExit", "onBeaconDistance", "onBeaconTimeout", "onBeaconFound", "onEddystoneFound", "onEddystoneEnter", "onEddystoneExit", "onEddystoneDistance", "onEddystoneTimeout", "onBeaconError")
|
|
149
149
|
|
|
150
150
|
// MARK: - Scan
|
|
151
151
|
|
|
152
152
|
AsyncFunction("scanForBeaconsAsync") { (uuids: [String], scanDurationMs: Int, promise: Promise) in
|
|
153
153
|
guard scanDurationMs > 0 else {
|
|
154
154
|
promise.reject("INVALID_DURATION", "Scan duration must be a positive integer")
|
|
155
|
+
self.sendLoggedEvent("onBeaconError", ["identifier": "", "code": "INVALID_DURATION", "message": "Scan duration must be a positive integer"])
|
|
155
156
|
return
|
|
156
157
|
}
|
|
157
158
|
guard self.scanPromise == nil else {
|
|
158
159
|
promise.reject("SCAN_IN_PROGRESS", "A scan is already in progress")
|
|
160
|
+
self.sendLoggedEvent("onBeaconError", ["identifier": "", "code": "SCAN_IN_PROGRESS", "message": "A scan is already in progress"])
|
|
159
161
|
return
|
|
160
162
|
}
|
|
161
163
|
|
|
@@ -181,6 +183,7 @@ public class ExpoBeaconModule: Module {
|
|
|
181
183
|
promise.reject("WILDCARD_NOT_SUPPORTED",
|
|
182
184
|
"iOS does not support wildcard iBeacon scanning. " +
|
|
183
185
|
"Provide at least one proximity UUID, or pair beacons first.")
|
|
186
|
+
self.sendLoggedEvent("onBeaconError", ["identifier": "", "code": "WILDCARD_NOT_SUPPORTED", "message": "iOS does not support wildcard iBeacon scanning. Provide at least one proximity UUID, or pair beacons first."])
|
|
184
187
|
self.scanPromise = nil
|
|
185
188
|
return
|
|
186
189
|
}
|
|
@@ -188,6 +191,7 @@ public class ExpoBeaconModule: Module {
|
|
|
188
191
|
for uuidStr in uuids {
|
|
189
192
|
guard let uuid = UUID(uuidString: uuidStr) else {
|
|
190
193
|
promise.reject("INVALID_UUID", "Invalid UUID: \(uuidStr)")
|
|
194
|
+
self.sendLoggedEvent("onBeaconError", ["identifier": "", "code": "INVALID_UUID", "message": "Invalid UUID: \(uuidStr)"])
|
|
191
195
|
self.scanPromise = nil
|
|
192
196
|
return
|
|
193
197
|
}
|
|
@@ -201,6 +205,7 @@ public class ExpoBeaconModule: Module {
|
|
|
201
205
|
self.requestLocationPermission { granted in
|
|
202
206
|
guard granted else {
|
|
203
207
|
promise.reject("PERMISSION_DENIED", "Location permission required for beacon scanning")
|
|
208
|
+
self.sendLoggedEvent("onBeaconError", ["identifier": "", "code": "PERMISSION_DENIED", "message": "Location permission required for beacon scanning"])
|
|
204
209
|
self.scanPromise = nil
|
|
205
210
|
return
|
|
206
211
|
}
|
|
@@ -356,18 +361,22 @@ public class ExpoBeaconModule: Module {
|
|
|
356
361
|
}
|
|
357
362
|
if let dist = maxDistance, (!dist.isFinite || dist <= 0) {
|
|
358
363
|
promise.reject("INVALID_MAX_DISTANCE", "maxDistance must be a finite number greater than 0")
|
|
364
|
+
self.sendLoggedEvent("onBeaconError", ["identifier": "", "code": "INVALID_MAX_DISTANCE", "message": "maxDistance must be a finite number greater than 0"])
|
|
359
365
|
return
|
|
360
366
|
}
|
|
361
367
|
if let exitDist = exitDistance, (!exitDist.isFinite || exitDist <= 0) {
|
|
362
368
|
promise.reject("INVALID_EXIT_DISTANCE", "exitDistance must be a finite number greater than 0")
|
|
369
|
+
self.sendLoggedEvent("onBeaconError", ["identifier": "", "code": "INVALID_EXIT_DISTANCE", "message": "exitDistance must be a finite number greater than 0"])
|
|
363
370
|
return
|
|
364
371
|
}
|
|
365
372
|
if exitDistance != nil && maxDistance == nil {
|
|
366
373
|
promise.reject("INVALID_EXIT_DISTANCE", "exitDistance requires maxDistance to be set")
|
|
374
|
+
self.sendLoggedEvent("onBeaconError", ["identifier": "", "code": "INVALID_EXIT_DISTANCE", "message": "exitDistance requires maxDistance to be set"])
|
|
367
375
|
return
|
|
368
376
|
}
|
|
369
377
|
if let dist = maxDistance, let exitDist = exitDistance, exitDist < dist {
|
|
370
378
|
promise.reject("INVALID_EXIT_DISTANCE", "exitDistance must be greater than or equal to maxDistance")
|
|
379
|
+
self.sendLoggedEvent("onBeaconError", ["identifier": "", "code": "INVALID_EXIT_DISTANCE", "message": "exitDistance must be greater than or equal to maxDistance"])
|
|
371
380
|
return
|
|
372
381
|
}
|
|
373
382
|
if let dist = maxDistance {
|
|
@@ -391,6 +400,7 @@ public class ExpoBeaconModule: Module {
|
|
|
391
400
|
self.requestLocationPermission { granted in
|
|
392
401
|
guard granted else {
|
|
393
402
|
promise.reject("PERMISSION_DENIED", "Location permission required for monitoring")
|
|
403
|
+
self.sendLoggedEvent("onBeaconError", ["identifier": "", "code": "PERMISSION_DENIED", "message": "Location permission required for monitoring"])
|
|
394
404
|
return
|
|
395
405
|
}
|
|
396
406
|
// Request Always authorization non-blockingly for background support.
|
|
@@ -452,10 +462,12 @@ public class ExpoBeaconModule: Module {
|
|
|
452
462
|
AsyncFunction("scanForEddystonesAsync") { (scanDurationMs: Int, promise: Promise) in
|
|
453
463
|
guard scanDurationMs > 0 else {
|
|
454
464
|
promise.reject("INVALID_DURATION", "Scan duration must be a positive integer")
|
|
465
|
+
self.sendLoggedEvent("onBeaconError", ["identifier": "", "code": "INVALID_DURATION", "message": "Scan duration must be a positive integer"])
|
|
455
466
|
return
|
|
456
467
|
}
|
|
457
468
|
guard self.eddystoneScanPromise == nil else {
|
|
458
469
|
promise.reject("SCAN_IN_PROGRESS", "An Eddystone scan is already in progress")
|
|
470
|
+
self.sendLoggedEvent("onBeaconError", ["identifier": "", "code": "SCAN_IN_PROGRESS", "message": "An Eddystone scan is already in progress"])
|
|
459
471
|
return
|
|
460
472
|
}
|
|
461
473
|
self.eddystoneScanPromise = promise
|
|
@@ -535,6 +547,14 @@ public class ExpoBeaconModule: Module {
|
|
|
535
547
|
return result
|
|
536
548
|
}
|
|
537
549
|
|
|
550
|
+
Function("getMonitoredDeviceState") { (identifier: String) -> [String: Any?]? in
|
|
551
|
+
return self.buildMonitoredDeviceState(identifier: identifier)
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
Function("getMonitoredDeviceStates") { () -> [[String: Any?]] in
|
|
555
|
+
return self.buildMonitoredDeviceStates()
|
|
556
|
+
}
|
|
557
|
+
|
|
538
558
|
// MARK: - Battery Optimization (Android-only; no-op on iOS)
|
|
539
559
|
|
|
540
560
|
Function("isBatteryOptimizationExempt") { () -> Bool in
|
|
@@ -632,6 +652,7 @@ public class ExpoBeaconModule: Module {
|
|
|
632
652
|
let maxRegions = 20
|
|
633
653
|
if beacons.count > maxRegions {
|
|
634
654
|
print("[ExpoBeacon] Warning: \(beacons.count) paired beacons exceeds the iOS limit of \(maxRegions) monitored regions. Only the first \(maxRegions) will be monitored.")
|
|
655
|
+
sendLoggedEvent("onBeaconError", ["identifier": "", "code": "REGION_LIMIT_EXCEEDED", "message": "\(beacons.count) paired beacons exceeds the iOS limit of \(maxRegions) monitored regions. Only the first \(maxRegions) will be monitored."])
|
|
635
656
|
}
|
|
636
657
|
|
|
637
658
|
for b in beacons.prefix(maxRegions) {
|
|
@@ -1057,6 +1078,63 @@ public class ExpoBeaconModule: Module {
|
|
|
1057
1078
|
return value
|
|
1058
1079
|
}
|
|
1059
1080
|
|
|
1081
|
+
private func buildMonitoredDeviceState(identifier: String) -> [String: Any?]? {
|
|
1082
|
+
if let pairedBeacon = loadPairedBeaconsRaw().first(where: { ($0["identifier"] as? String) == identifier }) {
|
|
1083
|
+
return makeMonitoredIBeaconState(from: pairedBeacon)
|
|
1084
|
+
}
|
|
1085
|
+
if let pairedEddystone = loadPairedEddystonesRaw().first(where: { ($0["identifier"] as? String) == identifier }) {
|
|
1086
|
+
return makeMonitoredEddystoneState(from: pairedEddystone)
|
|
1087
|
+
}
|
|
1088
|
+
return nil
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
private func buildMonitoredDeviceStates() -> [[String: Any?]] {
|
|
1092
|
+
let beaconStates = loadPairedBeaconsRaw().map { makeMonitoredIBeaconState(from: $0) }
|
|
1093
|
+
let eddystoneStates = loadPairedEddystonesRaw().map { makeMonitoredEddystoneState(from: $0) }
|
|
1094
|
+
return beaconStates + eddystoneStates
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
private func makeMonitoredIBeaconState(from paired: [String: Any]) -> [String: Any?] {
|
|
1098
|
+
let identifier = paired["identifier"] as? String ?? ""
|
|
1099
|
+
let isEntered = enteredRegions.contains(identifier)
|
|
1100
|
+
let major = (paired["major"] as? Int) ?? (paired["major"] as? NSNumber)?.intValue ?? 0
|
|
1101
|
+
let minor = (paired["minor"] as? Int) ?? (paired["minor"] as? NSNumber)?.intValue ?? 0
|
|
1102
|
+
|
|
1103
|
+
return [
|
|
1104
|
+
"kind": "ibeacon",
|
|
1105
|
+
"identifier": identifier,
|
|
1106
|
+
"uuid": paired["uuid"] as? String ?? "",
|
|
1107
|
+
"major": major,
|
|
1108
|
+
"minor": minor,
|
|
1109
|
+
"state": isEntered ? "entered" : "exited",
|
|
1110
|
+
"distance": normalizedMonitoringDistance(identifier: identifier, isEntered: isEntered)
|
|
1111
|
+
]
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
private func makeMonitoredEddystoneState(from paired: [String: Any]) -> [String: Any?] {
|
|
1115
|
+
let identifier = paired["identifier"] as? String ?? ""
|
|
1116
|
+
let isEntered = eddystoneEnteredRegions.contains(identifier)
|
|
1117
|
+
|
|
1118
|
+
return [
|
|
1119
|
+
"kind": "eddystone",
|
|
1120
|
+
"identifier": identifier,
|
|
1121
|
+
"namespace": paired["namespace"] as? String ?? "",
|
|
1122
|
+
"instance": paired["instance"] as? String ?? "",
|
|
1123
|
+
"state": isEntered ? "entered" : "exited",
|
|
1124
|
+
"distance": normalizedMonitoringDistance(identifier: identifier, isEntered: isEntered)
|
|
1125
|
+
]
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
private func normalizedMonitoringDistance(identifier: String, isEntered: Bool) -> Double? {
|
|
1129
|
+
guard isEntered,
|
|
1130
|
+
let distance = smoothedDistances[identifier],
|
|
1131
|
+
distance.isFinite,
|
|
1132
|
+
distance >= 0 else {
|
|
1133
|
+
return nil
|
|
1134
|
+
}
|
|
1135
|
+
return distance
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1060
1138
|
private func migrateUserDefaultsIfNeeded() {
|
|
1061
1139
|
let migrationKey = "expo.beacon.migrated_to_suite_v1"
|
|
1062
1140
|
guard !defaults.bool(forKey: migrationKey) else { return }
|
|
@@ -1298,6 +1376,7 @@ public class ExpoBeaconModule: Module {
|
|
|
1298
1376
|
let data = json.data(using: .utf8) else { return [:] }
|
|
1299
1377
|
guard let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
|
|
1300
1378
|
print("[ExpoBeacon] Warning: failed to parse notification config JSON")
|
|
1379
|
+
sendLoggedEvent("onBeaconError", ["identifier": "", "code": "CONFIG_PARSE_ERROR", "message": "Failed to parse notification config JSON"])
|
|
1301
1380
|
return [:]
|
|
1302
1381
|
}
|
|
1303
1382
|
return dict
|
|
@@ -1561,10 +1640,20 @@ public class ExpoBeaconModule: Module {
|
|
|
1561
1640
|
let id = region?.identifier ?? "unknown"
|
|
1562
1641
|
Logger(subsystem: "expo.modules.beacon", category: "monitoring")
|
|
1563
1642
|
.error("Monitoring failed for region \(id, privacy: .public): \(error.localizedDescription, privacy: .public)")
|
|
1643
|
+
sendLoggedEvent("onBeaconError", [
|
|
1644
|
+
"identifier": id,
|
|
1645
|
+
"code": "MONITORING_FAILED",
|
|
1646
|
+
"message": error.localizedDescription
|
|
1647
|
+
])
|
|
1564
1648
|
}
|
|
1565
1649
|
|
|
1566
1650
|
fileprivate func handleDidFailRanging(for constraint: CLBeaconIdentityConstraint, error: Error) {
|
|
1567
1651
|
print("[ExpoBeacon] Ranging failed for constraint \(constraint.uuid): \(error.localizedDescription)")
|
|
1652
|
+
sendLoggedEvent("onBeaconError", [
|
|
1653
|
+
"identifier": constraint.uuid.uuidString,
|
|
1654
|
+
"code": "RANGING_FAILED",
|
|
1655
|
+
"message": error.localizedDescription
|
|
1656
|
+
])
|
|
1568
1657
|
|
|
1569
1658
|
// If a one-shot scan is active and this constraint belongs to it, reject the promise
|
|
1570
1659
|
if scanPromise != nil && scanConstraints.contains(where: { $0 == constraint }) {
|
|
@@ -1578,6 +1667,14 @@ public class ExpoBeaconModule: Module {
|
|
|
1578
1667
|
scanPromise = nil
|
|
1579
1668
|
}
|
|
1580
1669
|
}
|
|
1670
|
+
|
|
1671
|
+
fileprivate func handleBluetoothStateError(code: String, message: String) {
|
|
1672
|
+
sendLoggedEvent("onBeaconError", [
|
|
1673
|
+
"identifier": "",
|
|
1674
|
+
"code": code,
|
|
1675
|
+
"message": message
|
|
1676
|
+
])
|
|
1677
|
+
}
|
|
1581
1678
|
}
|
|
1582
1679
|
|
|
1583
1680
|
// MARK: - CLLocationManagerDelegate
|
|
@@ -1630,10 +1727,12 @@ private class BluetoothDelegate: NSObject, CBCentralManagerDelegate {
|
|
|
1630
1727
|
case .unauthorized:
|
|
1631
1728
|
print("[ExpoBeacon] Bluetooth authorization denied — Eddystone scanning/monitoring unavailable. " +
|
|
1632
1729
|
"Ensure NSBluetoothAlwaysUsageDescription is set in Info.plist.")
|
|
1730
|
+
module?.handleBluetoothStateError(code: "BLUETOOTH_UNAUTHORIZED", message: "Bluetooth authorization denied — Eddystone scanning/monitoring unavailable")
|
|
1633
1731
|
module?.eddystoneScanPromise?.reject("BLUETOOTH_UNAUTHORIZED", "Bluetooth permission denied")
|
|
1634
1732
|
module?.eddystoneScanPromise = nil
|
|
1635
1733
|
case .poweredOff:
|
|
1636
1734
|
print("[ExpoBeacon] Bluetooth is powered off — Eddystone scanning/monitoring unavailable.")
|
|
1735
|
+
module?.handleBluetoothStateError(code: "BLUETOOTH_OFF", message: "Bluetooth is powered off — Eddystone scanning/monitoring unavailable")
|
|
1637
1736
|
module?.eddystoneScanPromise?.reject("BLUETOOTH_OFF", "Bluetooth is powered off")
|
|
1638
1737
|
module?.eddystoneScanPromise = nil
|
|
1639
1738
|
default:
|
package/package.json
CHANGED
package/src/ExpoBeacon.types.ts
CHANGED
|
@@ -133,6 +133,28 @@ export type MonitoringConfig = {
|
|
|
133
133
|
notifications?: NotificationConfig;
|
|
134
134
|
};
|
|
135
135
|
|
|
136
|
+
/** Current state snapshot for a paired monitored device. */
|
|
137
|
+
export type MonitoredDeviceState =
|
|
138
|
+
| {
|
|
139
|
+
kind: "ibeacon";
|
|
140
|
+
identifier: string;
|
|
141
|
+
uuid: string;
|
|
142
|
+
major: number;
|
|
143
|
+
minor: number;
|
|
144
|
+
state: "entered" | "exited";
|
|
145
|
+
/** Current distance in metres, or null when exited or no live reading is available. */
|
|
146
|
+
distance: number | null;
|
|
147
|
+
}
|
|
148
|
+
| {
|
|
149
|
+
kind: "eddystone";
|
|
150
|
+
identifier: string;
|
|
151
|
+
namespace: string;
|
|
152
|
+
instance: string;
|
|
153
|
+
state: "entered" | "exited";
|
|
154
|
+
/** Current distance in metres, or null when exited or no live reading is available. */
|
|
155
|
+
distance: number | null;
|
|
156
|
+
};
|
|
157
|
+
|
|
136
158
|
/** Options accepted by startMonitoring(). */
|
|
137
159
|
export type MonitoringOptions = {
|
|
138
160
|
/**
|
|
@@ -243,6 +265,16 @@ export type EddystoneTimeoutEvent = {
|
|
|
243
265
|
distance: number;
|
|
244
266
|
};
|
|
245
267
|
|
|
268
|
+
/** Payload for native beacon error events (monitoring/ranging failures). */
|
|
269
|
+
export type BeaconErrorEvent = {
|
|
270
|
+
/** Region or constraint identifier, empty string if unavailable. */
|
|
271
|
+
identifier: string;
|
|
272
|
+
/** Machine-readable error code (e.g. "MONITORING_FAILED", "RANGING_FAILED", "SECURITY_EXCEPTION"). */
|
|
273
|
+
code: string;
|
|
274
|
+
/** Human-readable error message from the native layer. */
|
|
275
|
+
message: string;
|
|
276
|
+
};
|
|
277
|
+
|
|
246
278
|
/** Module event map. */
|
|
247
279
|
export type ExpoBeaconModuleEvents = {
|
|
248
280
|
onBeaconEnter: (params: BeaconRegionEvent) => void;
|
|
@@ -259,6 +291,8 @@ export type ExpoBeaconModuleEvents = {
|
|
|
259
291
|
onEddystoneDistance: (params: EddystoneDistanceEvent) => void;
|
|
260
292
|
/** Fired once after a paired Eddystone has been continuously in range for its configured `timeoutSeconds`. */
|
|
261
293
|
onEddystoneTimeout: (params: EddystoneTimeoutEvent) => void;
|
|
294
|
+
/** Fired when a native monitoring or ranging failure occurs (logged to DB and forwarded to JS). */
|
|
295
|
+
onBeaconError: (params: BeaconErrorEvent) => void;
|
|
262
296
|
};
|
|
263
297
|
|
|
264
298
|
/** Options for filtering event logs. */
|
package/src/ExpoBeaconModule.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
NotificationConfig,
|
|
10
10
|
MonitoringOptions,
|
|
11
11
|
MonitoringConfig,
|
|
12
|
+
MonitoredDeviceState,
|
|
12
13
|
EventLogQueryOptions,
|
|
13
14
|
EventLogEntry,
|
|
14
15
|
} from "./ExpoBeacon.types";
|
|
@@ -172,6 +173,17 @@ declare class ExpoBeaconModule extends NativeModule<ExpoBeaconModuleEvents> {
|
|
|
172
173
|
*/
|
|
173
174
|
getMonitoringConfig(): MonitoringConfig;
|
|
174
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Return the current state snapshot for a paired monitored device.
|
|
178
|
+
* Returns null when no paired device matches the identifier.
|
|
179
|
+
*/
|
|
180
|
+
getMonitoredDeviceState(identifier: string): MonitoredDeviceState | null;
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Return the current state snapshot for all paired monitored devices.
|
|
184
|
+
*/
|
|
185
|
+
getMonitoredDeviceStates(): MonitoredDeviceState[];
|
|
186
|
+
|
|
175
187
|
/**
|
|
176
188
|
* Return the current API forwarding configuration.
|
|
177
189
|
* Each field is `null` if not set.
|
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
EddystoneScanResult,
|
|
5
5
|
PairedBeacon,
|
|
6
6
|
PairedEddystone,
|
|
7
|
+
MonitoredDeviceState,
|
|
7
8
|
EventLogQueryOptions,
|
|
8
9
|
EventLogEntry,
|
|
9
10
|
} from "./ExpoBeacon.types";
|
|
@@ -48,6 +49,8 @@ const stub = {
|
|
|
48
49
|
clearEventLogs: (): void => notSupported(),
|
|
49
50
|
destroyEventLogs: (): void => notSupported(),
|
|
50
51
|
getMonitoringConfig: () => notSupported(),
|
|
52
|
+
getMonitoredDeviceState: (_identifier: string): MonitoredDeviceState | null => notSupported(),
|
|
53
|
+
getMonitoredDeviceStates: (): MonitoredDeviceState[] => notSupported(),
|
|
51
54
|
getApiEndpoint: (): { url: string | null; apiKey: string | null; id: string | null } => notSupported(),
|
|
52
55
|
isBatteryOptimizationExempt: (): boolean => true,
|
|
53
56
|
requestBatteryOptimizationExemption: (): Promise<boolean> => Promise.resolve(true),
|