ilabs-flir 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/Flir.podspec +31 -31
  2. package/README.md +1271 -1271
  3. package/android/Flir/build.gradle.kts +85 -80
  4. package/android/Flir/src/main/AndroidManifest.xml +31 -31
  5. package/android/Flir/src/main/java/com/flir/thermalsdk/ErrorCodeException.java +14 -0
  6. package/android/Flir/src/main/java/com/flir/thermalsdk/image/ImageBuffer.java +11 -0
  7. package/android/Flir/src/main/java/com/flir/thermalsdk/image/JavaImageBuffer.java +35 -0
  8. package/android/Flir/src/main/java/com/flir/thermalsdk/image/Palette.java +15 -0
  9. package/android/Flir/src/main/java/com/flir/thermalsdk/image/PaletteManager.java +16 -0
  10. package/android/Flir/src/main/java/com/flir/thermalsdk/image/Point.java +11 -0
  11. package/android/Flir/src/main/java/com/flir/thermalsdk/image/ThermalImage.java +23 -0
  12. package/android/Flir/src/main/java/com/flir/thermalsdk/image/ThermalValue.java +9 -0
  13. package/android/Flir/src/main/java/com/flir/thermalsdk/live/CameraType.java +8 -0
  14. package/android/Flir/src/main/java/com/flir/thermalsdk/live/CommunicationInterface.java +16 -0
  15. package/android/Flir/src/main/java/com/flir/thermalsdk/live/Identity.java +23 -0
  16. package/android/Flir/src/main/java/com/flir/thermalsdk/live/IpSettings.java +9 -0
  17. package/android/Flir/src/main/java/com/flir/thermalsdk/live/connectivity/ConnectionStatusListener.java +7 -0
  18. package/android/Flir/src/main/java/com/flir/thermalsdk/live/remote/OnReceived.java +5 -0
  19. package/android/Flir/src/main/java/com/flir/thermalsdk/live/remote/OnRemoteError.java +7 -0
  20. package/android/Flir/src/main/java/flir/android/CameraHandler.java +224 -194
  21. package/android/Flir/src/main/java/flir/android/FlirCommands.java +111 -0
  22. package/android/Flir/src/main/java/flir/android/FlirConnectionManager.java +354 -0
  23. package/android/Flir/src/main/java/flir/android/FlirController.kt +11 -11
  24. package/android/Flir/src/main/java/flir/android/FlirDiscoveryManager.java +236 -0
  25. package/android/Flir/src/main/java/flir/android/FlirDownloadManager.kt +75 -75
  26. package/android/Flir/src/main/java/flir/android/FlirDownloadPackage.kt +16 -16
  27. package/android/Flir/src/main/java/flir/android/FlirFrameCache.kt +6 -6
  28. package/android/Flir/src/main/java/flir/android/FlirManager.kt +254 -248
  29. package/android/Flir/src/main/java/flir/android/FlirModule.kt +74 -74
  30. package/android/Flir/src/main/java/flir/android/FlirPackage.kt +19 -19
  31. package/android/Flir/src/main/java/flir/android/FlirSDKLoader.kt +195 -195
  32. package/android/Flir/src/main/java/flir/android/FlirSdkManager.java +890 -0
  33. package/android/Flir/src/main/java/flir/android/FlirStatus.kt +12 -12
  34. package/android/Flir/src/main/java/flir/android/FlirView.kt +48 -48
  35. package/android/Flir/src/main/java/flir/android/FlirViewManager.kt +13 -13
  36. package/android/Flir/src/main/java/flir/android/FrameDataHolder.java +14 -14
  37. package/app.plugin.js +264 -264
  38. package/expo-module.config.json +5 -5
  39. package/ios/Flir/Framework/ThermalSDK/FLIRBattery.h +76 -76
  40. package/ios/Flir/Framework/ThermalSDK/FLIRCalibration.h +108 -108
  41. package/ios/Flir/Framework/ThermalSDK/FLIRCamera.h +156 -156
  42. package/ios/Flir/Framework/ThermalSDK/FLIRCameraDeviceInfo.h +53 -53
  43. package/ios/Flir/Framework/ThermalSDK/FLIRCameraEvent.h +132 -132
  44. package/ios/Flir/Framework/ThermalSDK/FLIRCameraImport.h +204 -204
  45. package/ios/Flir/Framework/ThermalSDK/FLIRColorDistributionSettings.h +204 -204
  46. package/ios/Flir/Framework/ThermalSDK/FLIRColorizer.h +82 -82
  47. package/ios/Flir/Framework/ThermalSDK/FLIRDiscoveredCamera.h +44 -44
  48. package/ios/Flir/Framework/ThermalSDK/FLIRDiscovery.h +132 -132
  49. package/ios/Flir/Framework/ThermalSDK/FLIRDisplaySettings.h +29 -29
  50. package/ios/Flir/Framework/ThermalSDK/FLIRFocus.h +70 -70
  51. package/ios/Flir/Framework/ThermalSDK/FLIRFusion.h +192 -192
  52. package/ios/Flir/Framework/ThermalSDK/FLIRFusionController.h +136 -136
  53. package/ios/Flir/Framework/ThermalSDK/FLIRFusionTransformation.h +35 -35
  54. package/ios/Flir/Framework/ThermalSDK/FLIRIdentity.h +264 -264
  55. package/ios/Flir/Framework/ThermalSDK/FLIRImageBase.h +196 -196
  56. package/ios/Flir/Framework/ThermalSDK/FLIRImageColorizer.h +26 -26
  57. package/ios/Flir/Framework/ThermalSDK/FLIRImageStatistics.h +61 -61
  58. package/ios/Flir/Framework/ThermalSDK/FLIRIsotherms.h +208 -208
  59. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementArea.h +38 -38
  60. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementCollection.h +147 -147
  61. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementDelta.h +62 -62
  62. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementDimensions.h +33 -33
  63. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementEllipse.h +49 -49
  64. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementLine.h +66 -66
  65. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementMarker.h +69 -69
  66. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementParameters.h +41 -41
  67. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementRectangle.h +36 -36
  68. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementReference.h +27 -27
  69. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementShape.h +46 -46
  70. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementSpot.h +33 -33
  71. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementsController.h +160 -160
  72. package/ios/Flir/Framework/ThermalSDK/FLIRMeterLinkSensorPoll.h +247 -247
  73. package/ios/Flir/Framework/ThermalSDK/FLIROverlayController.h +27 -27
  74. package/ios/Flir/Framework/ThermalSDK/FLIRPalette.h +60 -60
  75. package/ios/Flir/Framework/ThermalSDK/FLIRPaletteController.h +36 -36
  76. package/ios/Flir/Framework/ThermalSDK/FLIRPaletteManager.h +97 -97
  77. package/ios/Flir/Framework/ThermalSDK/FLIRQuantification.h +55 -55
  78. package/ios/Flir/Framework/ThermalSDK/FLIRRemoteControl.h +393 -393
  79. package/ios/Flir/Framework/ThermalSDK/FLIRRenderer.h +35 -35
  80. package/ios/Flir/Framework/ThermalSDK/FLIRRendererImpl.h +17 -17
  81. package/ios/Flir/Framework/ThermalSDK/FLIRScale.h +99 -99
  82. package/ios/Flir/Framework/ThermalSDK/FLIRScaleController.h +44 -44
  83. package/ios/Flir/Framework/ThermalSDK/FLIRStream.h +109 -109
  84. package/ios/Flir/Framework/ThermalSDK/FLIRStreamer.h +124 -124
  85. package/ios/Flir/Framework/ThermalSDK/FLIRSystem.h +40 -40
  86. package/ios/Flir/Framework/ThermalSDK/FLIRTemperatureRange.h +43 -43
  87. package/ios/Flir/Framework/ThermalSDK/FLIRThermalDelta.h +77 -77
  88. package/ios/Flir/Framework/ThermalSDK/FLIRThermalImage.h +331 -331
  89. package/ios/Flir/Framework/ThermalSDK/FLIRThermalImageFile.h +56 -56
  90. package/ios/Flir/Framework/ThermalSDK/FLIRThermalParameters.h +31 -31
  91. package/ios/Flir/Framework/ThermalSDK/FLIRThermalValue.h +92 -92
  92. package/ios/Flir/Framework/ThermalSDK/FLIRWirelessCameraDetails.h +88 -88
  93. package/ios/Flir/Framework/ThermalSDK/ThermalSDK.h +73 -73
  94. package/ios/Flir/SDKLoader/FlirSDKLoader.m +13 -13
  95. package/ios/Flir/SDKLoader/FlirSDKLoader.swift +175 -175
  96. package/ios/Flir/src/FlirEventEmitter.h +12 -12
  97. package/ios/Flir/src/FlirEventEmitter.m +33 -33
  98. package/ios/Flir/src/FlirModule.h +10 -10
  99. package/ios/Flir/src/FlirModule.m +381 -381
  100. package/ios/Flir/src/FlirPreviewView.h +13 -13
  101. package/ios/Flir/src/FlirPreviewView.m +24 -24
  102. package/ios/Flir/src/FlirState.h +20 -20
  103. package/ios/Flir/src/FlirState.m +79 -79
  104. package/ios/Flir/src/FlirViewManager.h +9 -9
  105. package/ios/Flir/src/FlirViewManager.m +16 -16
  106. package/package.json +60 -60
  107. package/react-native.config.js +14 -14
  108. package/scripts/copy_ios_libs.sh +32 -32
  109. package/scripts/create_stubs.py +174 -174
  110. package/scripts/download-sdk.js +62 -62
  111. package/scripts/prepare-binaries.sh +171 -171
  112. package/sdk-manifest.json +30 -30
  113. package/src/FlirDownload.ts +78 -78
  114. package/src/index.d.ts +17 -17
  115. package/src/index.js +7 -7
  116. package/src/index.ts +7 -7
@@ -1,248 +1,254 @@
1
- package flir.android
2
-
3
- import android.content.Context
4
- import android.graphics.Bitmap
5
- import android.graphics.BitmapFactory
6
- import android.graphics.Color
7
- import android.util.Base64
8
- import com.facebook.react.bridge.Arguments
9
- import com.facebook.react.bridge.WritableMap
10
- import com.facebook.react.modules.core.DeviceEventManagerModule
11
- import com.facebook.react.uimanager.ThemedReactContext
12
- import java.io.ByteArrayOutputStream
13
- import java.io.File
14
- import java.io.FileOutputStream
15
- import java.util.concurrent.atomic.AtomicLong
16
-
17
- object FlirManager {
18
- private val cameraHandler: CameraHandler = CameraHandler()
19
- private val lastEmitMs = AtomicLong(0)
20
- private val minEmitIntervalMs = 333L // ~3 fps
21
- private var discoveryStarted = false
22
- private var reactContext: ThemedReactContext? = null
23
-
24
- // Emulator and device state tracking
25
- private var isEmulatorMode = false
26
- private var isPhysicalDeviceConnected = false
27
- private var connectedIdentity: com.flir.thermalsdk.live.Identity? = null
28
-
29
- // GL texture callback support for native filters
30
- interface TextureUpdateCallback {
31
- fun onTextureUpdate(bitmap: Bitmap, textureUnit: Int)
32
- }
33
-
34
- interface TemperatureCallback {
35
- fun onTemperatureData(temperature: Double, x: Int, y: Int)
36
- }
37
-
38
- private var textureCallback: TextureUpdateCallback? = null
39
- private var temperatureCallback: TemperatureCallback? = null
40
- private var latestBitmap: Bitmap? = null
41
-
42
- fun setTextureCallback(callback: TextureUpdateCallback?) {
43
- textureCallback = callback
44
- }
45
-
46
- fun setTemperatureCallback(callback: TemperatureCallback?) {
47
- temperatureCallback = callback
48
- }
49
-
50
- fun getLatestBitmap(): Bitmap? = latestBitmap
51
-
52
- fun getTemperatureAtPoint(x: Int, y: Int): Double? {
53
- return try {
54
- cameraHandler.getTemperatureAt(x, y)
55
- } catch (t: Throwable) {
56
- null
57
- }
58
- }
59
-
60
- /**
61
- * Check if currently running in emulator mode (no physical FLIR device)
62
- */
63
- fun isEmulator(): Boolean = isEmulatorMode
64
-
65
- /**
66
- * Check if a physical FLIR device is connected
67
- */
68
- fun isDeviceConnected(): Boolean = isPhysicalDeviceConnected
69
-
70
- /**
71
- * Get information about the connected device
72
- */
73
- fun getConnectedDeviceInfo(): String {
74
- return when {
75
- connectedIdentity == null -> "Not connected"
76
- isEmulatorMode -> "Emulator (${connectedIdentity?.deviceId})"
77
- else -> "Physical device (${connectedIdentity?.deviceId})"
78
- }
79
- }
80
-
81
- fun init(context: Context) {
82
- try {
83
- val cls = Class.forName("com.flir.thermalsdk.live.ThermalSdkAndroid")
84
- val method = cls.getMethod("init", Context::class.java)
85
- method.invoke(null, context.applicationContext)
86
- } catch (ignored: Throwable) {
87
- }
88
- }
89
-
90
- fun startDiscoveryAndConnect(context: ThemedReactContext) {
91
- if (discoveryStarted) return
92
- discoveryStarted = true
93
- reactContext = context
94
-
95
- emitDeviceState("discovering", false)
96
-
97
- cameraHandler.startDiscovery(object : com.flir.thermalsdk.live.discovery.DiscoveryEventListener {
98
- override fun onCameraFound(discoveredCamera: com.flir.thermalsdk.live.discovery.DiscoveredCamera) {
99
- cameraHandler.add(discoveredCamera.identity)
100
-
101
- // Prioritize real device over emulator
102
- val realDevice = cameraHandler.getFlirOne()
103
- val emulatorDevice = cameraHandler.getFlirOneEmulator() ?: cameraHandler.getCppEmulator()
104
- val toConnect = realDevice ?: emulatorDevice
105
-
106
- if (toConnect != null) {
107
- // Determine if we're connecting to emulator
108
- isEmulatorMode = (realDevice == null)
109
- isPhysicalDeviceConnected = (realDevice != null)
110
- connectedIdentity = toConnect
111
-
112
- Thread {
113
- try {
114
- cameraHandler.connect(toConnect, com.flir.thermalsdk.live.connectivity.ConnectionStatusListener { errorCode ->
115
- emitDeviceState("disconnected", false)
116
- isPhysicalDeviceConnected = false
117
- connectedIdentity = null
118
- })
119
-
120
- val deviceType = if (isEmulatorMode) "emulator" else "device"
121
- emitDeviceState("connected", true, mapOf("deviceType" to deviceType, "isEmulator" to isEmulatorMode))
122
- FlirStatus.flirConnected = true
123
-
124
- cameraHandler.startStream(object : CameraHandler.StreamDataListener {
125
- override fun images(dataHolder: FrameDataHolder) {
126
- handleIncomingFrames(dataHolder.msxBitmap, dataHolder.dcBitmap, context)
127
- }
128
-
129
- override fun images(msxBitmap: Bitmap?, dcBitmap: Bitmap?) {
130
- handleIncomingFrames(msxBitmap, dcBitmap, context)
131
- }
132
- })
133
- } catch (e: Exception) {
134
- emitDeviceState("disconnected", false)
135
- FlirStatus.flirConnected = false
136
- isPhysicalDeviceConnected = false
137
- connectedIdentity = null
138
- }
139
- }.start()
140
- }
141
- }
142
-
143
- override fun onDiscoveryError(communicationInterface: com.flir.thermalsdk.live.CommunicationInterface, errorCode: com.flir.thermalsdk.ErrorCode) {
144
- emitDeviceState("discovery_error", false)
145
- }
146
- }, object : CameraHandler.DiscoveryStatus {
147
- override fun started() {}
148
- override fun stopped() {}
149
- })
150
- }
151
-
152
- fun stop() {
153
- try {
154
- cameraHandler.disconnect()
155
- } catch (ignored: Throwable) {}
156
- FlirStatus.flirConnected = false
157
- FlirStatus.flirStreaming = false
158
- discoveryStarted = false
159
- reactContext = null
160
- }
161
-
162
- fun getLatestFramePath(): String? {
163
- return FlirStatus.latestFramePath
164
- }
165
-
166
- fun getTemperatureAt(x: Int, y: Int): Double? {
167
- return try {
168
- cameraHandler.getTemperatureAt(x, y)
169
- } catch (t: Throwable) {
170
- null
171
- }
172
- }
173
-
174
- private fun handleIncomingFrames(msxBitmap: Bitmap?, dcBitmap: Bitmap?, ctx: ThemedReactContext) {
175
- val now = System.currentTimeMillis()
176
- if (now - lastEmitMs.get() < minEmitIntervalMs) return
177
- lastEmitMs.set(now)
178
-
179
- val bmp = msxBitmap ?: dcBitmap ?: return
180
- latestBitmap = bmp
181
-
182
- // Invoke texture callback for native GL/Metal filters (texture unit 7)
183
- textureCallback?.onTextureUpdate(bmp, 7)
184
-
185
- // Get and emit temperature data
186
- try {
187
- val temp = cameraHandler.getTemperatureAt(80, 60) // center point
188
- if (temp != null) {
189
- temperatureCallback?.onTemperatureData(temp, 80, 60)
190
- }
191
- } catch (ignored: Throwable) {}
192
-
193
- try {
194
- val cacheDir = ctx.cacheDir
195
- val outFile = File(cacheDir, "flir_latest_frame.png")
196
- val fos = FileOutputStream(outFile)
197
- bmp.compress(Bitmap.CompressFormat.PNG, 90, fos)
198
- fos.flush()
199
- fos.close()
200
- FlirStatus.latestFramePath = outFile.absolutePath
201
- FlirFrameCache.latestFramePath = outFile.absolutePath
202
- FlirStatus.flirStreaming = true
203
-
204
- val baos = ByteArrayOutputStream()
205
- bmp.compress(Bitmap.CompressFormat.PNG, 70, baos)
206
- val pngBytes = baos.toByteArray()
207
- val base64 = Base64.encodeToString(pngBytes, Base64.NO_WRAP)
208
-
209
- val params: WritableMap = Arguments.createMap().apply {
210
- putString("type", "frame")
211
- putString("path", outFile.absolutePath)
212
- putString("base64", "data:image/png;base64," + base64)
213
- putDouble("timestamp", (now / 1000.0))
214
- }
215
- try {
216
- ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
217
- .emit("FlirFrame", params)
218
- } catch (e: Exception) {}
219
-
220
- } catch (e: Exception) {
221
- FlirStatus.flirStreaming = false
222
- }
223
- }
224
-
225
- private fun emitDeviceState(state: String, connected: Boolean, extras: Map<String, Any> = emptyMap()) {
226
- FlirStatus.flirConnected = connected
227
- val ctx = reactContext ?: return
228
- val params: WritableMap = Arguments.createMap().apply {
229
- putString("state", state)
230
- putBoolean("connected", connected)
231
- putString("message", if (connected) "FLIR device connected" else state)
232
-
233
- // Add any extra parameters
234
- extras.forEach { (key, value) ->
235
- when (value) {
236
- is String -> putString(key, value)
237
- is Boolean -> putBoolean(key, value)
238
- is Int -> putInt(key, value)
239
- is Double -> putDouble(key, value)
240
- }
241
- }
242
- }
243
- try {
244
- ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
245
- .emit("FlirDeviceConnected", params)
246
- } catch (e: Exception) {}
247
- }
248
- }
1
+ package flir.android
2
+
3
+ import android.content.Context
4
+ import android.graphics.Bitmap
5
+ import android.util.Log
6
+ import com.facebook.react.bridge.Arguments
7
+ import com.facebook.react.bridge.WritableMap
8
+ import com.facebook.react.modules.core.DeviceEventManagerModule
9
+ import com.facebook.react.uimanager.ThemedReactContext
10
+ import java.io.File
11
+ import java.io.FileOutputStream
12
+ import java.util.concurrent.atomic.AtomicLong
13
+
14
+ object FlirManager {
15
+ private val TAG = "FlirManager"
16
+ private var sdkManager: FlirSdkManager? = null
17
+ private val lastEmitMs = AtomicLong(0)
18
+ private val minEmitIntervalMs = 333L // ~3 fps
19
+ private var discoveryStarted = false
20
+ private var reactContext: ThemedReactContext? = null
21
+
22
+ // Emulator and device state tracking
23
+ private var isEmulatorMode = false
24
+ private var isPhysicalDeviceConnected = false
25
+ private var connectedDeviceName: String? = null
26
+
27
+ // GL texture callback support for native filters
28
+ interface TextureUpdateCallback {
29
+ fun onTextureUpdate(bitmap: Bitmap, textureUnit: Int)
30
+ }
31
+
32
+ interface TemperatureCallback {
33
+ fun onTemperatureData(temperature: Double, x: Int, y: Int)
34
+ }
35
+
36
+ private var textureCallback: TextureUpdateCallback? = null
37
+ private var temperatureCallback: TemperatureCallback? = null
38
+ private var latestBitmap: Bitmap? = null
39
+
40
+ fun setTextureCallback(callback: TextureUpdateCallback?) {
41
+ textureCallback = callback
42
+ }
43
+
44
+ fun setTemperatureCallback(callback: TemperatureCallback?) {
45
+ temperatureCallback = callback
46
+ }
47
+
48
+ fun getLatestBitmap(): Bitmap? = latestBitmap
49
+
50
+ fun getTemperatureAtPoint(x: Int, y: Int): Double? {
51
+ return try {
52
+ sdkManager?.getTemperatureAtPoint(x, y)?.takeIf { !it.isNaN() }
53
+ } catch (t: Throwable) {
54
+ null
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Check if currently running in emulator mode (no physical FLIR device)
60
+ */
61
+ fun isEmulator(): Boolean = isEmulatorMode
62
+
63
+ /**
64
+ * Check if a physical FLIR device is connected
65
+ */
66
+ fun isDeviceConnected(): Boolean = isPhysicalDeviceConnected
67
+
68
+ /**
69
+ * Get information about the connected device
70
+ */
71
+ fun getConnectedDeviceInfo(): String {
72
+ return when {
73
+ connectedDeviceName == null -> "Not connected"
74
+ isEmulatorMode -> "Emulator ($connectedDeviceName)"
75
+ else -> "Physical device ($connectedDeviceName)"
76
+ }
77
+ }
78
+
79
+ fun init(context: Context) {
80
+ try {
81
+ // Initialize SDK manager with listener
82
+ sdkManager = FlirSdkManager(object : FlirSdkManager.Listener {
83
+ override fun onFrame(bitmap: Bitmap) {
84
+ latestBitmap = bitmap
85
+ textureCallback?.onTextureUpdate(bitmap, 0)
86
+ emitFrameToReactNative(bitmap)
87
+ }
88
+
89
+ override fun onTemperature(temp: Double, x: Int, y: Int) {
90
+ temperatureCallback?.onTemperatureData(temp, x, y)
91
+ }
92
+
93
+ override fun onDeviceFound(name: String) {
94
+ connectedDeviceName = name
95
+ isPhysicalDeviceConnected = true
96
+ emitDeviceState("connected", true)
97
+ }
98
+
99
+ override fun onEmulatorEnabled() {
100
+ isEmulatorMode = true
101
+ emitDeviceState("emulator", false)
102
+ }
103
+
104
+ override fun onStreamKindChanged(kind: String) {
105
+ Log.d(TAG, "Stream kind changed: $kind")
106
+ }
107
+ })
108
+
109
+ // Try to initialize SDK via reflection
110
+ try {
111
+ val cls = Class.forName("com.flir.thermalsdk.live.ThermalSdkAndroid")
112
+ val method = cls.getMethod("init", Context::class.java)
113
+ method.invoke(null, context.applicationContext)
114
+ Log.i(TAG, "FLIR SDK initialized via reflection")
115
+ } catch (e: ClassNotFoundException) {
116
+ Log.w(TAG, "FLIR SDK not found on classpath, will attempt DexClassLoader")
117
+ } catch (e: Throwable) {
118
+ Log.w(TAG, "FLIR SDK init failed: ${e.message}")
119
+ }
120
+ } catch (t: Throwable) {
121
+ Log.e(TAG, "FlirManager init failed", t)
122
+ }
123
+ }
124
+
125
+ fun startDiscoveryAndConnect(context: ThemedReactContext) {
126
+ if (discoveryStarted) return
127
+ discoveryStarted = true
128
+ reactContext = context
129
+
130
+ emitDeviceState("discovering", false)
131
+
132
+ try {
133
+ val started = sdkManager?.startSdkEmulator() ?: false
134
+ if (!started) {
135
+ Log.w(TAG, "Failed to start SDK emulator")
136
+ emitDeviceState("error", false)
137
+ }
138
+ } catch (t: Throwable) {
139
+ Log.e(TAG, "startDiscoveryAndConnect failed", t)
140
+ emitDeviceState("error", false)
141
+ }
142
+ }
143
+
144
+ fun stop() {
145
+ discoveryStarted = false
146
+ isPhysicalDeviceConnected = false
147
+ isEmulatorMode = false
148
+ connectedDeviceName = null
149
+ latestBitmap = null
150
+ // Note: FlirSdkManager doesn't have a stop method, but we reset our state
151
+ }
152
+
153
+ fun getLatestFramePath(): String? {
154
+ val bitmap = latestBitmap ?: return null
155
+ return try {
156
+ val file = File.createTempFile("flir_frame_", ".jpg")
157
+ FileOutputStream(file).use { out ->
158
+ bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out)
159
+ }
160
+ file.absolutePath
161
+ } catch (t: Throwable) {
162
+ null
163
+ }
164
+ }
165
+
166
+ fun getTemperatureAt(x: Int, y: Int): Double? {
167
+ return getTemperatureAtPoint(x, y)
168
+ }
169
+
170
+ private fun emitFrameToReactNative(bitmap: Bitmap) {
171
+ val now = System.currentTimeMillis()
172
+ if (now - lastEmitMs.get() < minEmitIntervalMs) return
173
+ lastEmitMs.set(now)
174
+
175
+ val ctx = reactContext ?: return
176
+ try {
177
+ val params = Arguments.createMap()
178
+ params.putInt("width", bitmap.width)
179
+ params.putInt("height", bitmap.height)
180
+ params.putDouble("timestamp", now.toDouble())
181
+
182
+ ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
183
+ .emit("FlirFrameReceived", params)
184
+ } catch (e: Exception) {
185
+ // Silently ignore
186
+ }
187
+ }
188
+
189
+ private fun emitDeviceState(state: String, isPhysical: Boolean) {
190
+ val ctx = reactContext ?: return
191
+ try {
192
+ val params = Arguments.createMap()
193
+ params.putString("state", state)
194
+ params.putBoolean("isPhysical", isPhysical)
195
+ params.putBoolean("isEmulator", isEmulatorMode)
196
+ params.putBoolean("isConnected", isPhysicalDeviceConnected)
197
+
198
+ connectedDeviceName?.let {
199
+ params.putString("deviceName", it)
200
+ }
201
+
202
+ ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
203
+ .emit("FlirDeviceConnected", params)
204
+ } catch (e: Exception) {}
205
+ }
206
+
207
+ // Compatibility for Java / FlirHelper
208
+ @JvmStatic
209
+ fun getInstance(): FlirManager = this
210
+
211
+ interface DiscoveryCallback {
212
+ fun onDeviceFound(deviceName: String)
213
+ fun onDiscoveryTimeout()
214
+ fun onEmulatorEnabled()
215
+ }
216
+
217
+ private var discoveryCallback: DiscoveryCallback? = null
218
+
219
+ fun setDiscoveryCallback(callback: DiscoveryCallback?) {
220
+ discoveryCallback = callback
221
+ }
222
+
223
+ fun setPalette(name: String) {
224
+ sdkManager?.setPalette(name)
225
+ }
226
+
227
+ fun setEmulatorMode(enabled: Boolean) {
228
+ isEmulatorMode = enabled
229
+ if (enabled) {
230
+ discoveryCallback?.onEmulatorEnabled()
231
+ }
232
+ }
233
+
234
+ fun updateAcol(value: Float) {
235
+ // No-op for now - palette changes handled by setPalette
236
+ }
237
+
238
+ fun startDiscovery(retry: Boolean) {
239
+ if (reactContext != null) {
240
+ startDiscoveryAndConnect(reactContext!!)
241
+ } else {
242
+ // Fallback: try to start without React context
243
+ try {
244
+ sdkManager?.startSdkEmulator()
245
+ } catch (t: Throwable) {
246
+ Log.e(TAG, "startDiscovery failed", t)
247
+ }
248
+ }
249
+ }
250
+
251
+ fun enableEmulatorMode() {
252
+ setEmulatorMode(true)
253
+ }
254
+ }