spotny-sdk 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of spotny-sdk might be problematic. Click here for more details.
|
@@ -45,6 +45,8 @@ class SpotnySdkModule(private val reactContext: ReactApplicationContext) :
|
|
|
45
45
|
// backendURL is fixed — not overridable by consumers.
|
|
46
46
|
private val backendURL = "https://api.spotny.app"
|
|
47
47
|
private var maxDetectionDistance = 8.0 // metres
|
|
48
|
+
/** Multiplier applied to raw BLE distance. Default 0.5 matches Kontakt.io -12 dBm beacons. */
|
|
49
|
+
private var distanceCorrectionFactor = 0.5
|
|
48
50
|
/** Company/brand name identifying the SDK consumer (e.g. "nike"). */
|
|
49
51
|
private var source: String? = null
|
|
50
52
|
|
|
@@ -140,6 +142,9 @@ class SpotnySdkModule(private val reactContext: ReactApplicationContext) :
|
|
|
140
142
|
config?.getDouble("maxDetectionDistance").takeIf { config?.hasKey("maxDetectionDistance") == true }
|
|
141
143
|
?.let { maxDetectionDistance = it; Log.d(TAG, "maxDetectionDistance = $it m") }
|
|
142
144
|
config?.getString("source")?.let { source = it; Log.d(TAG, "source = $it") }
|
|
145
|
+
config?.getDouble("distanceCorrectionFactor")
|
|
146
|
+
.takeIf { config?.hasKey("distanceCorrectionFactor") == true && it > 0 }
|
|
147
|
+
?.let { distanceCorrectionFactor = it; Log.d(TAG, "distanceCorrectionFactor = $it") }
|
|
143
148
|
promise.resolve("Configuration updated")
|
|
144
149
|
}
|
|
145
150
|
|
|
@@ -210,7 +215,7 @@ class SpotnySdkModule(private val reactContext: ReactApplicationContext) :
|
|
|
210
215
|
val jsBeacons = WritableNativeArray()
|
|
211
216
|
for (beacon in beacons) {
|
|
212
217
|
val raw = beacon.distance
|
|
213
|
-
val adjusted = raw *
|
|
218
|
+
val adjusted = raw * distanceCorrectionFactor
|
|
214
219
|
if (adjusted <= 0 || adjusted > maxDetectionDistance) continue
|
|
215
220
|
|
|
216
221
|
val b = WritableNativeMap().apply {
|
|
@@ -236,7 +241,7 @@ class SpotnySdkModule(private val reactContext: ReactApplicationContext) :
|
|
|
236
241
|
val major = beacon.id2?.toInt() ?: continue
|
|
237
242
|
val minor = beacon.id3?.toInt() ?: continue
|
|
238
243
|
val key = beaconKey(major, minor)
|
|
239
|
-
val distance = beacon.distance *
|
|
244
|
+
val distance = beacon.distance * distanceCorrectionFactor
|
|
240
245
|
|
|
241
246
|
if (distance <= 0 || distance > maxDetectionDistance) continue
|
|
242
247
|
|
|
@@ -360,7 +365,13 @@ class SpotnySdkModule(private val reactContext: ReactApplicationContext) :
|
|
|
360
365
|
|
|
361
366
|
private fun cleanupAllState() {
|
|
362
367
|
val deviceId = getDeviceId()
|
|
363
|
-
|
|
368
|
+
// Stagger exit events to avoid firing N simultaneous network requests
|
|
369
|
+
val keys = activeCampaigns.keys.toList()
|
|
370
|
+
keys.forEachIndexed { index, key ->
|
|
371
|
+
android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
|
|
372
|
+
cleanupBeacon(key, deviceId)
|
|
373
|
+
}, index * 300L)
|
|
374
|
+
}
|
|
364
375
|
}
|
|
365
376
|
|
|
366
377
|
// ── Backend API ───────────────────────────────────────────────────────────
|
|
@@ -50,9 +50,17 @@ public class SpotnyBeaconScanner: NSObject {
|
|
|
50
50
|
private let backendURL: String = "https://api.spotny.app"
|
|
51
51
|
private let kontaktAPIKey: String = "mgrz08TOKNHafeY02cWIs9mxUHbynNQJ"
|
|
52
52
|
private var maxDetectionDistance: Double = 8.0
|
|
53
|
+
/// Multiplier applied to raw RSSI-derived distance to compensate for low TX power.
|
|
54
|
+
/// Overridable via configure(). Default 0.5 matches Kontakt.io -12 dBm beacons.
|
|
55
|
+
private var distanceCorrectionFactor: Double = 0.5
|
|
53
56
|
/// Company/brand name that identifies the SDK consumer (e.g. "nike").
|
|
54
57
|
private var source: String?
|
|
55
58
|
|
|
59
|
+
// ── Session TTL ────────────────────────────────────────────────────────────
|
|
60
|
+
/// Sessions older than this when the app resumes are considered stale (crash / force-quit).
|
|
61
|
+
private let sessionTTL: TimeInterval = 24 * 3600 // 24 hours
|
|
62
|
+
private var lastSessionHeartbeat: Date = .distantPast
|
|
63
|
+
|
|
56
64
|
// ── Beacon UUID (standard Kontakt.io default) ─────────────────────────────
|
|
57
65
|
private let beaconUUID = UUID(uuidString: "f7826da6-4fa2-4e98-8024-bc5b71e0893e")!
|
|
58
66
|
|
|
@@ -107,6 +115,15 @@ public class SpotnyBeaconScanner: NSObject {
|
|
|
107
115
|
|
|
108
116
|
private func resumeStoredSession() {
|
|
109
117
|
guard let stored = UserDefaults.standard.string(forKey: "SpotnySDK_userUUID") else { return }
|
|
118
|
+
// Discard sessions older than sessionTTL — stale after a crash / force-quit
|
|
119
|
+
if let ts = UserDefaults.standard.object(forKey: "SpotnySDK_sessionTimestamp") as? Double {
|
|
120
|
+
let age = Date().timeIntervalSince1970 - ts
|
|
121
|
+
if age > sessionTTL {
|
|
122
|
+
print("⚠️ SpotnySDK: Stale session (\(Int(age / 3600))h old) — discarding")
|
|
123
|
+
clearStoredSession()
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
}
|
|
110
127
|
currentUserUUID = stored
|
|
111
128
|
userId = UserDefaults.standard.object(forKey: "SpotnySDK_userId") as? Int
|
|
112
129
|
print("🔄 SpotnySDK: Resuming session for UUID: \(stored)")
|
|
@@ -114,6 +131,13 @@ public class SpotnyBeaconScanner: NSObject {
|
|
|
114
131
|
scanning = true
|
|
115
132
|
}
|
|
116
133
|
|
|
134
|
+
private func clearStoredSession() {
|
|
135
|
+
UserDefaults.standard.removeObject(forKey: "SpotnySDK_userUUID")
|
|
136
|
+
UserDefaults.standard.removeObject(forKey: "SpotnySDK_userId")
|
|
137
|
+
UserDefaults.standard.removeObject(forKey: "SpotnySDK_sessionTimestamp")
|
|
138
|
+
UserDefaults.standard.synchronize()
|
|
139
|
+
}
|
|
140
|
+
|
|
117
141
|
// MARK: - ObjC-Exposed Methods (called from SpotnySdk.mm)
|
|
118
142
|
|
|
119
143
|
@objc
|
|
@@ -137,6 +161,7 @@ public class SpotnyBeaconScanner: NSObject {
|
|
|
137
161
|
} else {
|
|
138
162
|
UserDefaults.standard.removeObject(forKey: "SpotnySDK_userId")
|
|
139
163
|
}
|
|
164
|
+
UserDefaults.standard.set(Date().timeIntervalSince1970, forKey: "SpotnySDK_sessionTimestamp")
|
|
140
165
|
UserDefaults.standard.synchronize()
|
|
141
166
|
|
|
142
167
|
let status = locationManager.authorizationStatus
|
|
@@ -159,9 +184,7 @@ public class SpotnyBeaconScanner: NSObject {
|
|
|
159
184
|
beaconManager.stopRangingBeaconsInAllRegions()
|
|
160
185
|
cleanupAllProximityState()
|
|
161
186
|
|
|
162
|
-
|
|
163
|
-
UserDefaults.standard.removeObject(forKey: "SpotnySDK_userId")
|
|
164
|
-
UserDefaults.standard.synchronize()
|
|
187
|
+
clearStoredSession()
|
|
165
188
|
|
|
166
189
|
scanning = false
|
|
167
190
|
currentUserUUID = nil
|
|
@@ -193,6 +216,10 @@ public class SpotnyBeaconScanner: NSObject {
|
|
|
193
216
|
source = src
|
|
194
217
|
print("⚙️ SpotnySDK: source = \(src)")
|
|
195
218
|
}
|
|
219
|
+
if let factor = config["distanceCorrectionFactor"] as? Double, factor > 0 {
|
|
220
|
+
distanceCorrectionFactor = factor
|
|
221
|
+
print("⚙️ SpotnySDK: distanceCorrectionFactor = \(factor)")
|
|
222
|
+
}
|
|
196
223
|
resolve("Configuration updated")
|
|
197
224
|
}
|
|
198
225
|
|
|
@@ -578,7 +605,13 @@ public class SpotnyBeaconScanner: NSObject {
|
|
|
578
605
|
|
|
579
606
|
private func cleanupAllProximityState() {
|
|
580
607
|
let deviceId = getDeviceId()
|
|
581
|
-
|
|
608
|
+
// Stagger exit events to avoid firing N simultaneous network requests
|
|
609
|
+
let keys = Array(activeCampaigns.keys)
|
|
610
|
+
for (index, key) in keys.enumerated() {
|
|
611
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + Double(index) * 0.3) { [weak self] in
|
|
612
|
+
self?.cleanupBeacon(key, deviceId: deviceId)
|
|
613
|
+
}
|
|
614
|
+
}
|
|
582
615
|
}
|
|
583
616
|
}
|
|
584
617
|
|
|
@@ -608,10 +641,16 @@ extension SpotnyBeaconScanner: KTKBeaconManagerDelegate {
|
|
|
608
641
|
let deviceId = getDeviceId()
|
|
609
642
|
let now = Date()
|
|
610
643
|
|
|
644
|
+
// Refresh session heartbeat every 60 s to prevent TTL expiry on a live session
|
|
645
|
+
if now.timeIntervalSince(lastSessionHeartbeat) >= 60 {
|
|
646
|
+
UserDefaults.standard.set(now.timeIntervalSince1970, forKey: "SpotnySDK_sessionTimestamp")
|
|
647
|
+
lastSessionHeartbeat = now
|
|
648
|
+
}
|
|
649
|
+
|
|
611
650
|
// Build the JS event payload for ALL ranged beacons
|
|
612
651
|
let beaconPayload: [[String: Any]] = beacons.compactMap { beacon in
|
|
613
652
|
let raw = beacon.accuracy
|
|
614
|
-
let adjusted = raw *
|
|
653
|
+
let adjusted = raw * distanceCorrectionFactor
|
|
615
654
|
guard adjusted > 0 && adjusted <= maxDetectionDistance else { return nil }
|
|
616
655
|
return [
|
|
617
656
|
"uuid": beacon.proximityUUID.uuidString,
|
|
@@ -633,7 +672,7 @@ extension SpotnyBeaconScanner: KTKBeaconManagerDelegate {
|
|
|
633
672
|
let minor = beacon.minor.intValue
|
|
634
673
|
let key = beaconKey(major: major, minor: minor)
|
|
635
674
|
let raw = beacon.accuracy
|
|
636
|
-
let distance = raw *
|
|
675
|
+
let distance = raw * distanceCorrectionFactor
|
|
637
676
|
|
|
638
677
|
guard distance > 0 && distance <= maxDetectionDistance else { continue }
|
|
639
678
|
|
|
@@ -678,8 +717,6 @@ extension SpotnyBeaconScanner: KTKBeaconManagerDelegate {
|
|
|
678
717
|
var bg: UIBackgroundTaskIdentifier = .invalid
|
|
679
718
|
bg = UIApplication.shared.beginBackgroundTask { UIApplication.shared.endBackgroundTask(bg); bg = .invalid }
|
|
680
719
|
|
|
681
|
-
beaconManager.startRangingBeacons(in: region)
|
|
682
|
-
|
|
683
720
|
// Parse major/minor from named regions (e.g. "SpotnySDK_52885_35127")
|
|
684
721
|
let parts = region.identifier.components(separatedBy: "_")
|
|
685
722
|
if parts.count == 3, let major = Int(parts[1]), let minor = Int(parts[2]) {
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["NativeEventEmitter","NativeModules","NativeSpotnySdk","SpotnyEvents","ON_BEACONS_RANGED","ON_BEACON_REGION_EVENT","eventEmitter","SpotnySdk","startScanner","userUUID","userId","stopScanner","isScanning","configure","config","requestNotificationPermissions","getDebugLogs","clearDebugLogs","setDebounceInterval","interval","clearDebounceCache","getDebounceStatus","addBeaconsRangedListener","callback","addListener","addBeaconRegionListener"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,kBAAkB,EAAEC,aAAa,QAAQ,cAAc;AAChE,OAAOC,eAAe,MAAM,sBAAmB;;AAE/C;AACA,OAAO,MAAMC,YAAY,GAAG;EAC1BC,iBAAiB,EAAE,iBAAiB;EACpCC,sBAAsB,EAAE;AAC1B,CAAU;;AAEV;;
|
|
1
|
+
{"version":3,"names":["NativeEventEmitter","NativeModules","NativeSpotnySdk","SpotnyEvents","ON_BEACONS_RANGED","ON_BEACON_REGION_EVENT","eventEmitter","SpotnySdk","startScanner","userUUID","userId","stopScanner","isScanning","configure","config","requestNotificationPermissions","getDebugLogs","clearDebugLogs","setDebounceInterval","interval","clearDebounceCache","getDebounceStatus","addBeaconsRangedListener","callback","addListener","addBeaconRegionListener"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,kBAAkB,EAAEC,aAAa,QAAQ,cAAc;AAChE,OAAOC,eAAe,MAAM,sBAAmB;;AAE/C;AACA,OAAO,MAAMC,YAAY,GAAG;EAC1BC,iBAAiB,EAAE,iBAAiB;EACpCC,sBAAsB,EAAE;AAC1B,CAAU;;AAEV;;AAqCA;AACA,MAAMC,YAAY,GAAG,IAAIN,kBAAkB,CAACC,aAAa,CAACM,SAAS,CAAC;;AAEpE;;AAEA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,YAAYA,CAC1BC,QAAgB,EAChBC,MAAsB,EACL;EACjB,OAAOR,eAAe,CAACM,YAAY,CAACC,QAAQ,EAAEC,MAAM,IAAI,IAAI,CAAC;AAC/D;;AAEA;AACA,OAAO,SAASC,WAAWA,CAAA,EAAoB;EAC7C,OAAOT,eAAe,CAACS,WAAW,CAAC,CAAC;AACtC;;AAEA;AACA,OAAO,SAASC,UAAUA,CAAA,EAAqB;EAC7C,OAAOV,eAAe,CAACU,UAAU,CAAC,CAAC;AACrC;;AAEA;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASC,SAASA,CAACC,MAAuB,EAAmB;EAClE,OAAOZ,eAAe,CAACW,SAAS,CAACC,MAAgB,CAAC;AACpD;;AAEA;;AAEA;AACA,OAAO,SAASC,8BAA8BA,CAAA,EAAoB;EAChE,OAAOb,eAAe,CAACa,8BAA8B,CAAC,CAAC;AACzD;;AAEA;;AAEA;AACA,OAAO,SAASC,YAAYA,CAAA,EAAoB;EAC9C,OAAOd,eAAe,CAACc,YAAY,CAAC,CAAC;AACvC;;AAEA;AACA,OAAO,SAASC,cAAcA,CAAA,EAAoB;EAChD,OAAOf,eAAe,CAACe,cAAc,CAAC,CAAC;AACzC;;AAEA;;AAEA;AACA,OAAO,SAASC,mBAAmBA,CAACC,QAAgB,EAAmB;EACrE,OAAOjB,eAAe,CAACgB,mBAAmB,CAACC,QAAQ,CAAC;AACtD;;AAEA;AACA,OAAO,SAASC,kBAAkBA,CAAA,EAAoB;EACpD,OAAOlB,eAAe,CAACkB,kBAAkB,CAAC,CAAC;AAC7C;;AAEA;AACA,OAAO,SAASC,iBAAiBA,CAAA,EAAoB;EACnD,OAAOnB,eAAe,CAACmB,iBAAiB,CAAC,CAAC;AAC5C;;AAEA;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASC,wBAAwBA,CACtCC,QAA4C,EAC5C;EACA,OAAOjB,YAAY,CAACkB,WAAW,CAC7BrB,YAAY,CAACC,iBAAiB,EAC9BmB,QACF,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASE,uBAAuBA,CACrCF,QAA4C,EAC5C;EACA,OAAOjB,YAAY,CAACkB,WAAW,CAC7BrB,YAAY,CAACE,sBAAsB,EACnCkB,QACF,CAAC;AACH","ignoreList":[]}
|
|
@@ -27,6 +27,11 @@ export type SpotnySdkConfig = {
|
|
|
27
27
|
maxDetectionDistance?: number;
|
|
28
28
|
/** Identifier for your brand or app (e.g. 'nike'). */
|
|
29
29
|
source?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Multiplier applied to raw RSSI-derived distance to compensate for device
|
|
32
|
+
* TX power variance (default: 0.5, tuned for Kontakt.io -12 dBm beacons).
|
|
33
|
+
*/
|
|
34
|
+
distanceCorrectionFactor?: number;
|
|
30
35
|
};
|
|
31
36
|
/**
|
|
32
37
|
* Start beacon scanning.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAIA,eAAO,MAAM,YAAY;;;CAGf,CAAC;AAIX,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,YAAY,CAAC;IACvC,KAAK,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;CAC1C,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,8DAA8D;IAC9D,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAIA,eAAO,MAAM,YAAY;;;CAGf,CAAC;AAIX,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,YAAY,CAAC;IACvC,KAAK,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;CAC1C,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,8DAA8D;IAC9D,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC,CAAC;AAOF;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,GACrB,OAAO,CAAC,MAAM,CAAC,CAEjB;AAED,mDAAmD;AACnD,wBAAgB,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC,CAE7C;AAED,uDAAuD;AACvD,wBAAgB,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC,CAE7C;AAID;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAElE;AAID,gEAAgE;AAChE,wBAAgB,8BAA8B,IAAI,OAAO,CAAC,MAAM,CAAC,CAEhE;AAID,yCAAyC;AACzC,wBAAgB,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAE9C;AAED,2CAA2C;AAC3C,wBAAgB,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,CAEhD;AAID,gEAAgE;AAChE,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAErE;AAED,2DAA2D;AAC3D,wBAAgB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAEpD;AAED,kEAAkE;AAClE,wBAAgB,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC,CAEnD;AAID;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,4CAM7C;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,4CAM7C"}
|
package/package.json
CHANGED
package/src/index.tsx
CHANGED
|
@@ -37,6 +37,11 @@ export type SpotnySdkConfig = {
|
|
|
37
37
|
maxDetectionDistance?: number;
|
|
38
38
|
/** Identifier for your brand or app (e.g. 'nike'). */
|
|
39
39
|
source?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Multiplier applied to raw RSSI-derived distance to compensate for device
|
|
42
|
+
* TX power variance (default: 0.5, tuned for Kontakt.io -12 dBm beacons).
|
|
43
|
+
*/
|
|
44
|
+
distanceCorrectionFactor?: number;
|
|
40
45
|
};
|
|
41
46
|
|
|
42
47
|
// ── Internal event emitter ───────────────────────────────────────────────────
|