ilabs-flir 1.0.2 → 1.0.4

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 (126) 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/libs/flir-stubs.jar +0 -0
  5. package/android/Flir/src/main/AndroidManifest.xml +31 -31
  6. package/android/Flir/src/main/java/com/flir/thermalsdk/ErrorCode.java +13 -0
  7. package/android/Flir/src/main/java/com/flir/thermalsdk/ErrorCodeException.java +14 -0
  8. package/android/Flir/src/main/java/com/flir/thermalsdk/ThermalSdkAndroid.java +16 -0
  9. package/android/Flir/src/main/java/com/flir/thermalsdk/androidsdk/image/BitmapAndroid.java +20 -0
  10. package/android/Flir/src/main/java/com/flir/thermalsdk/image/ImageBuffer.java +11 -0
  11. package/android/Flir/src/main/java/com/flir/thermalsdk/image/JavaImageBuffer.java +35 -0
  12. package/android/Flir/src/main/java/com/flir/thermalsdk/image/Palette.java +15 -0
  13. package/android/Flir/src/main/java/com/flir/thermalsdk/image/PaletteManager.java +16 -0
  14. package/android/Flir/src/main/java/com/flir/thermalsdk/image/Point.java +11 -0
  15. package/android/Flir/src/main/java/com/flir/thermalsdk/image/ThermalImage.java +23 -0
  16. package/android/Flir/src/main/java/com/flir/thermalsdk/image/ThermalValue.java +9 -0
  17. package/android/Flir/src/main/java/com/flir/thermalsdk/live/Camera.java +26 -0
  18. package/android/Flir/src/main/java/com/flir/thermalsdk/live/CameraType.java +8 -0
  19. package/android/Flir/src/main/java/com/flir/thermalsdk/live/CommunicationInterface.java +16 -0
  20. package/android/Flir/src/main/java/com/flir/thermalsdk/live/ConnectParameters.java +16 -0
  21. package/android/Flir/src/main/java/com/flir/thermalsdk/live/Identity.java +23 -0
  22. package/android/Flir/src/main/java/com/flir/thermalsdk/live/IpSettings.java +9 -0
  23. package/android/Flir/src/main/java/com/flir/thermalsdk/live/RemoteControl.java +16 -0
  24. package/android/Flir/src/main/java/com/flir/thermalsdk/live/connectivity/ConnectionStatusListener.java +7 -0
  25. package/android/Flir/src/main/java/com/flir/thermalsdk/live/discovery/DiscoveryEventListener.java +14 -0
  26. package/android/Flir/src/main/java/com/flir/thermalsdk/live/discovery/DiscoveryFactory.java +33 -0
  27. package/android/Flir/src/main/java/com/flir/thermalsdk/live/remote/OnReceived.java +5 -0
  28. package/android/Flir/src/main/java/com/flir/thermalsdk/live/remote/OnRemoteError.java +7 -0
  29. package/android/Flir/src/main/java/com/flir/thermalsdk/live/streaming/Stream.java +8 -0
  30. package/android/Flir/src/main/java/com/flir/thermalsdk/live/streaming/ThermalStreamer.java +28 -0
  31. package/android/Flir/src/main/java/com/flir/thermalsdk/live/streaming/VisualStreamer.java +18 -0
  32. package/android/Flir/src/main/java/flir/android/FlirCommands.java +136 -0
  33. package/android/Flir/src/main/java/flir/android/FlirDownloadManager.kt +76 -75
  34. package/android/Flir/src/main/java/flir/android/FlirDownloadPackage.kt +16 -16
  35. package/android/Flir/src/main/java/flir/android/FlirFrameCache.kt +6 -6
  36. package/android/Flir/src/main/java/flir/android/FlirManager.kt +477 -248
  37. package/android/Flir/src/main/java/flir/android/FlirModule.kt +74 -74
  38. package/android/Flir/src/main/java/flir/android/FlirPackage.kt +19 -19
  39. package/android/Flir/src/main/java/flir/android/FlirSDKLoader.kt +165 -117
  40. package/android/Flir/src/main/java/flir/android/FlirSdkManager.java +1511 -0
  41. package/android/Flir/src/main/java/flir/android/FlirStatus.kt +12 -12
  42. package/android/Flir/src/main/java/flir/android/FlirView.kt +48 -48
  43. package/android/Flir/src/main/java/flir/android/FlirViewManager.kt +13 -13
  44. package/app.plugin.js +264 -264
  45. package/expo-module.config.json +5 -5
  46. package/ios/Flir/Framework/ThermalSDK/FLIRBattery.h +76 -76
  47. package/ios/Flir/Framework/ThermalSDK/FLIRCalibration.h +108 -108
  48. package/ios/Flir/Framework/ThermalSDK/FLIRCamera.h +156 -156
  49. package/ios/Flir/Framework/ThermalSDK/FLIRCameraDeviceInfo.h +53 -53
  50. package/ios/Flir/Framework/ThermalSDK/FLIRCameraEvent.h +132 -132
  51. package/ios/Flir/Framework/ThermalSDK/FLIRCameraImport.h +204 -204
  52. package/ios/Flir/Framework/ThermalSDK/FLIRColorDistributionSettings.h +204 -204
  53. package/ios/Flir/Framework/ThermalSDK/FLIRColorizer.h +82 -82
  54. package/ios/Flir/Framework/ThermalSDK/FLIRDiscoveredCamera.h +44 -44
  55. package/ios/Flir/Framework/ThermalSDK/FLIRDiscovery.h +132 -132
  56. package/ios/Flir/Framework/ThermalSDK/FLIRDisplaySettings.h +29 -29
  57. package/ios/Flir/Framework/ThermalSDK/FLIRFocus.h +70 -70
  58. package/ios/Flir/Framework/ThermalSDK/FLIRFusion.h +192 -192
  59. package/ios/Flir/Framework/ThermalSDK/FLIRFusionController.h +136 -136
  60. package/ios/Flir/Framework/ThermalSDK/FLIRFusionTransformation.h +35 -35
  61. package/ios/Flir/Framework/ThermalSDK/FLIRIdentity.h +264 -264
  62. package/ios/Flir/Framework/ThermalSDK/FLIRImageBase.h +196 -196
  63. package/ios/Flir/Framework/ThermalSDK/FLIRImageColorizer.h +26 -26
  64. package/ios/Flir/Framework/ThermalSDK/FLIRImageStatistics.h +61 -61
  65. package/ios/Flir/Framework/ThermalSDK/FLIRIsotherms.h +208 -208
  66. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementArea.h +38 -38
  67. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementCollection.h +147 -147
  68. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementDelta.h +62 -62
  69. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementDimensions.h +33 -33
  70. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementEllipse.h +49 -49
  71. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementLine.h +66 -66
  72. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementMarker.h +69 -69
  73. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementParameters.h +41 -41
  74. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementRectangle.h +36 -36
  75. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementReference.h +27 -27
  76. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementShape.h +46 -46
  77. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementSpot.h +33 -33
  78. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementsController.h +160 -160
  79. package/ios/Flir/Framework/ThermalSDK/FLIRMeterLinkSensorPoll.h +247 -247
  80. package/ios/Flir/Framework/ThermalSDK/FLIROverlayController.h +27 -27
  81. package/ios/Flir/Framework/ThermalSDK/FLIRPalette.h +60 -60
  82. package/ios/Flir/Framework/ThermalSDK/FLIRPaletteController.h +36 -36
  83. package/ios/Flir/Framework/ThermalSDK/FLIRPaletteManager.h +97 -97
  84. package/ios/Flir/Framework/ThermalSDK/FLIRQuantification.h +55 -55
  85. package/ios/Flir/Framework/ThermalSDK/FLIRRemoteControl.h +393 -393
  86. package/ios/Flir/Framework/ThermalSDK/FLIRRenderer.h +35 -35
  87. package/ios/Flir/Framework/ThermalSDK/FLIRRendererImpl.h +17 -17
  88. package/ios/Flir/Framework/ThermalSDK/FLIRScale.h +99 -99
  89. package/ios/Flir/Framework/ThermalSDK/FLIRScaleController.h +44 -44
  90. package/ios/Flir/Framework/ThermalSDK/FLIRStream.h +109 -109
  91. package/ios/Flir/Framework/ThermalSDK/FLIRStreamer.h +124 -124
  92. package/ios/Flir/Framework/ThermalSDK/FLIRSystem.h +40 -40
  93. package/ios/Flir/Framework/ThermalSDK/FLIRTemperatureRange.h +43 -43
  94. package/ios/Flir/Framework/ThermalSDK/FLIRThermalDelta.h +77 -77
  95. package/ios/Flir/Framework/ThermalSDK/FLIRThermalImage.h +331 -331
  96. package/ios/Flir/Framework/ThermalSDK/FLIRThermalImageFile.h +56 -56
  97. package/ios/Flir/Framework/ThermalSDK/FLIRThermalParameters.h +31 -31
  98. package/ios/Flir/Framework/ThermalSDK/FLIRThermalValue.h +92 -92
  99. package/ios/Flir/Framework/ThermalSDK/FLIRWirelessCameraDetails.h +88 -88
  100. package/ios/Flir/Framework/ThermalSDK/ThermalSDK.h +73 -73
  101. package/ios/Flir/SDKLoader/FlirSDKLoader.m +13 -13
  102. package/ios/Flir/SDKLoader/FlirSDKLoader.swift +175 -175
  103. package/ios/Flir/src/FlirEventEmitter.h +12 -12
  104. package/ios/Flir/src/FlirEventEmitter.m +33 -33
  105. package/ios/Flir/src/FlirModule.h +10 -10
  106. package/ios/Flir/src/FlirModule.m +381 -381
  107. package/ios/Flir/src/FlirPreviewView.h +13 -13
  108. package/ios/Flir/src/FlirPreviewView.m +24 -24
  109. package/ios/Flir/src/FlirState.h +20 -20
  110. package/ios/Flir/src/FlirState.m +79 -79
  111. package/ios/Flir/src/FlirViewManager.h +9 -9
  112. package/ios/Flir/src/FlirViewManager.m +16 -16
  113. package/package.json +60 -60
  114. package/react-native.config.js +14 -14
  115. package/scripts/copy_ios_libs.sh +32 -32
  116. package/scripts/create_stubs.py +174 -174
  117. package/scripts/download-sdk.js +62 -62
  118. package/scripts/prepare-binaries.sh +171 -171
  119. package/sdk-manifest.json +37 -30
  120. package/src/FlirDownload.ts +78 -78
  121. package/src/index.d.ts +17 -17
  122. package/src/index.js +7 -7
  123. package/src/index.ts +7 -7
  124. package/android/Flir/src/main/java/flir/android/CameraHandler.java +0 -194
  125. package/android/Flir/src/main/java/flir/android/FlirController.kt +0 -11
  126. package/android/Flir/src/main/java/flir/android/FrameDataHolder.java +0 -14
@@ -1,248 +1,477 @@
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.WritableArray
8
+ import com.facebook.react.bridge.WritableMap
9
+ import com.facebook.react.modules.core.DeviceEventManagerModule
10
+ import com.facebook.react.uimanager.ThemedReactContext
11
+ import java.io.File
12
+ import java.io.FileOutputStream
13
+ import java.util.concurrent.atomic.AtomicLong
14
+
15
+ object FlirManager {
16
+ private val TAG = "FlirManager"
17
+ private val FLOW_TAG = "FLIR_FLOW" // Matching FlirSdkManager flow tag
18
+ private var sdkManager: FlirSdkManager? = null
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
+ private var appContext: Context? = null
24
+ private var frameCount = 0 // For tracking first frame
25
+
26
+ // Emulator and device state tracking
27
+ private var isEmulatorMode = false
28
+ private var isPhysicalDeviceConnected = false
29
+ private var connectedDeviceName: String? = null
30
+ private var connectedDeviceId: String? = null
31
+ private var isStreaming = false
32
+
33
+ // Current emulator type preference
34
+ private var preferredEmulatorType = FlirSdkManager.EmulatorType.FLIR_ONE_EDGE
35
+
36
+ // Discovered devices cache
37
+ private var discoveredDevices: List<FlirSdkManager.DeviceInfo> = emptyList()
38
+
39
+ // GL texture callback support for native filters
40
+ interface TextureUpdateCallback {
41
+ fun onTextureUpdate(bitmap: Bitmap, textureUnit: Int)
42
+ }
43
+
44
+ interface TemperatureCallback {
45
+ fun onTemperatureData(temperature: Double, x: Int, y: Int)
46
+ }
47
+
48
+ private var textureCallback: TextureUpdateCallback? = null
49
+ private var temperatureCallback: TemperatureCallback? = null
50
+ private var latestBitmap: Bitmap? = null
51
+
52
+ fun setTextureCallback(callback: TextureUpdateCallback?) {
53
+ textureCallback = callback
54
+ }
55
+
56
+ fun setTemperatureCallback(callback: TemperatureCallback?) {
57
+ temperatureCallback = callback
58
+ }
59
+
60
+ fun getLatestBitmap(): Bitmap? = latestBitmap
61
+
62
+ fun getTemperatureAtPoint(x: Int, y: Int): Double? {
63
+ return try {
64
+ sdkManager?.getTemperatureAtPoint(x, y)?.takeIf { !it.isNaN() }
65
+ } catch (t: Throwable) {
66
+ null
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Check if currently running in emulator mode (no physical FLIR device)
72
+ */
73
+ fun isEmulator(): Boolean = isEmulatorMode
74
+
75
+ /**
76
+ * Check if a physical FLIR device is connected
77
+ */
78
+ fun isDeviceConnected(): Boolean = isPhysicalDeviceConnected || isEmulatorMode
79
+
80
+ /**
81
+ * Check if currently streaming
82
+ */
83
+ fun isStreaming(): Boolean = isStreaming
84
+
85
+ /**
86
+ * Get list of discovered devices
87
+ */
88
+ fun getDiscoveredDevices(): List<FlirSdkManager.DeviceInfo> = discoveredDevices
89
+
90
+ /**
91
+ * Get information about the connected device
92
+ */
93
+ fun getConnectedDeviceInfo(): String {
94
+ return when {
95
+ connectedDeviceName == null -> "Not connected"
96
+ isEmulatorMode -> "Emulator ($connectedDeviceName)"
97
+ else -> "Physical device ($connectedDeviceName)"
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Set preferred emulator type (FLIR_ONE_EDGE or FLIR_ONE)
103
+ */
104
+ fun setPreferredEmulatorType(type: String) {
105
+ preferredEmulatorType = when (type.uppercase()) {
106
+ "FLIR_ONE" -> FlirSdkManager.EmulatorType.FLIR_ONE
107
+ else -> FlirSdkManager.EmulatorType.FLIR_ONE_EDGE
108
+ }
109
+ Log.d(TAG, "Preferred emulator type set to: $preferredEmulatorType")
110
+ sdkManager?.setEmulatorType(preferredEmulatorType)
111
+ }
112
+
113
+ fun init(context: Context) {
114
+ // Avoid re-initialization if already done
115
+ if (sdkManager != null) {
116
+ Log.i(FLOW_TAG, "[FlirManager] init() called but already initialized, skipping")
117
+ return
118
+ }
119
+
120
+ appContext = context.applicationContext
121
+ Log.i(FLOW_TAG, "[FlirManager] init() called - initializing SDK Manager")
122
+ try {
123
+ // Initialize SDK manager with listener matching FlirSdkManager.Listener interface
124
+ sdkManager = FlirSdkManager(object : FlirSdkManager.Listener {
125
+ override fun onFrame(bitmap: Bitmap) {
126
+ latestBitmap = bitmap
127
+ if (frameCount == 0) {
128
+ Log.i(FLOW_TAG, "[FlirManager] FIRST FRAME received: ${bitmap.width}x${bitmap.height}")
129
+ }
130
+ frameCount++
131
+ textureCallback?.onTextureUpdate(bitmap, 0)
132
+ emitFrameToReactNative(bitmap)
133
+ }
134
+
135
+ override fun onTemperature(temp: Double, x: Int, y: Int) {
136
+ temperatureCallback?.onTemperatureData(temp, x, y)
137
+ }
138
+
139
+ override fun onDeviceFound(deviceId: String, deviceName: String, isEmulator: Boolean) {
140
+ Log.i(FLOW_TAG, "[FlirManager] Device found: $deviceName (id=$deviceId, emulator=$isEmulator)")
141
+ Log.d(TAG, "Device found: $deviceName (id=$deviceId, emulator=$isEmulator)")
142
+ discoveryCallback?.onDeviceFound(deviceName)
143
+ }
144
+
145
+ override fun onDeviceListUpdated(devices: MutableList<FlirSdkManager.DeviceInfo>) {
146
+ discoveredDevices = devices.toList()
147
+ Log.i(FLOW_TAG, "[FlirManager] Device list updated: ${devices.size} devices")
148
+ Log.i(TAG, "Device list updated: ${devices.size} devices")
149
+ devices.forEach {
150
+ Log.d(TAG, " - ${it.deviceName} (${it.commInterface}, emu=${it.isEmulator})")
151
+ }
152
+ emitDevicesFound(devices)
153
+
154
+ // Auto-connect to first device if available
155
+ if (devices.isNotEmpty()) {
156
+ val first = devices[0]
157
+ connectedDeviceName = first.deviceName
158
+ connectedDeviceId = first.deviceId
159
+ isEmulatorMode = first.isEmulator
160
+ isPhysicalDeviceConnected = !first.isEmulator
161
+
162
+ Log.i(FLOW_TAG, "[FlirManager] Auto-connecting to first device: ${first.deviceName}")
163
+ // Connect to the device
164
+ sdkManager?.connectToDevice(first.deviceId)
165
+ }
166
+ }
167
+
168
+ override fun onDeviceConnected(deviceId: String, deviceName: String, isEmulator: Boolean) {
169
+ connectedDeviceId = deviceId
170
+ connectedDeviceName = deviceName
171
+ this@FlirManager.isEmulatorMode = isEmulator
172
+ isPhysicalDeviceConnected = !isEmulator
173
+
174
+ Log.i(TAG, "Device connected: $deviceName (id=$deviceId, emulator=$isEmulator)")
175
+ emitDeviceState("connected", !isEmulator)
176
+ }
177
+
178
+ override fun onDeviceDisconnected() {
179
+ Log.i(TAG, "Device disconnected")
180
+ isPhysicalDeviceConnected = false
181
+ isEmulatorMode = false
182
+ isStreaming = false
183
+ connectedDeviceId = null
184
+ connectedDeviceName = null
185
+ emitDeviceState("disconnected", false)
186
+ }
187
+
188
+ override fun onDiscoveryStarted() {
189
+ Log.i(TAG, "Discovery started")
190
+ emitDeviceState("discovering", false)
191
+ }
192
+
193
+ override fun onDiscoveryTimeout() {
194
+ Log.w(TAG, "Discovery timeout - no devices found")
195
+ discoveryCallback?.onDiscoveryTimeout()
196
+ emitDeviceState("discovery_timeout", false)
197
+ }
198
+
199
+ override fun onStreamStarted(streamType: String) {
200
+ isStreaming = true
201
+ Log.i(TAG, "Streaming started: $streamType")
202
+ emitDeviceState("streaming", isPhysicalDeviceConnected)
203
+ }
204
+
205
+ override fun onError(error: String) {
206
+ Log.e(TAG, "SDK Error: $error")
207
+ emitError(error)
208
+ }
209
+ }, context)
210
+
211
+ // Set emulator type preference
212
+ sdkManager?.setEmulatorType(preferredEmulatorType)
213
+
214
+ Log.i(TAG, "FlirManager initialized with comprehensive SDK manager")
215
+ } catch (t: Throwable) {
216
+ Log.e(TAG, "FlirManager init failed", t)
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Start discovery with the specified parameters
222
+ * @param isEmuMode If true, immediately start emulator without physical device discovery
223
+ */
224
+ fun startDiscoveryAndConnect(context: ThemedReactContext, isEmuMode: Boolean = false) {
225
+ Log.i(FLOW_TAG, "[FlirManager] startDiscoveryAndConnect called: isEmuMode=$isEmuMode, discoveryStarted=$discoveryStarted, preferredEmulatorType=$preferredEmulatorType")
226
+
227
+ if (discoveryStarted && !isEmuMode) {
228
+ Log.i(FLOW_TAG, "[FlirManager] Discovery already started, skipping")
229
+ return
230
+ }
231
+ discoveryStarted = true
232
+ reactContext = context
233
+ frameCount = 0 // Reset frame counter
234
+
235
+ emitDeviceState("discovering", false)
236
+
237
+ try {
238
+ // Set emulator type preference before discovery
239
+ Log.i(FLOW_TAG, "[FlirManager] Setting emulator type: $preferredEmulatorType")
240
+ sdkManager?.setEmulatorType(preferredEmulatorType)
241
+
242
+ // Start discovery (forceEmulator=isEmuMode)
243
+ Log.i(FLOW_TAG, "[FlirManager] Calling sdkManager.startDiscovery(forceEmulator=$isEmuMode)")
244
+ Log.i(TAG, "Starting discovery (forceEmulator=$isEmuMode)")
245
+ sdkManager?.startDiscovery(isEmuMode)
246
+ } catch (t: Throwable) {
247
+ Log.e(FLOW_TAG, "[FlirManager] startDiscoveryAndConnect failed: ${t.message}")
248
+ Log.e(TAG, "startDiscoveryAndConnect failed", t)
249
+ emitDeviceState("error", false)
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Legacy overload for backward compatibility
255
+ */
256
+ fun startDiscoveryAndConnect(context: ThemedReactContext) {
257
+ startDiscoveryAndConnect(context, isEmuMode = false)
258
+ }
259
+
260
+ /**
261
+ * Switch to a specific device by ID
262
+ */
263
+ fun switchToDevice(deviceId: String) {
264
+ if (deviceId == connectedDeviceId) {
265
+ Log.d(TAG, "Already connected to device: $deviceId")
266
+ return
267
+ }
268
+
269
+ Log.i(TAG, "Switching to device: $deviceId")
270
+ sdkManager?.connectToDevice(deviceId)
271
+ }
272
+
273
+ /**
274
+ * Start emulator mode
275
+ */
276
+ fun startEmulator(type: FlirSdkManager.EmulatorType = preferredEmulatorType) {
277
+ Log.i(TAG, "Starting emulator: $type")
278
+ sdkManager?.setEmulatorType(type)
279
+ sdkManager?.startDiscovery(true) // forceEmulator = true
280
+ }
281
+
282
+ fun stop() {
283
+ Log.i(TAG, "Stopping FlirManager")
284
+
285
+ // Stop the SDK manager
286
+ sdkManager?.stop()
287
+
288
+ // Reset state
289
+ discoveryStarted = false
290
+ isPhysicalDeviceConnected = false
291
+ isEmulatorMode = false
292
+ isStreaming = false
293
+ connectedDeviceName = null
294
+ connectedDeviceId = null
295
+ latestBitmap = null
296
+ discoveredDevices = emptyList()
297
+ }
298
+
299
+ fun getLatestFramePath(): String? {
300
+ val bitmap = latestBitmap ?: return null
301
+ return try {
302
+ val file = File.createTempFile("flir_frame_", ".jpg")
303
+ FileOutputStream(file).use { out ->
304
+ bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out)
305
+ }
306
+ file.absolutePath
307
+ } catch (t: Throwable) {
308
+ null
309
+ }
310
+ }
311
+
312
+ fun getTemperatureAt(x: Int, y: Int): Double? {
313
+ return getTemperatureAtPoint(x, y)
314
+ }
315
+
316
+ private fun emitFrameToReactNative(bitmap: Bitmap) {
317
+ val now = System.currentTimeMillis()
318
+ if (now - lastEmitMs.get() < minEmitIntervalMs) return
319
+ lastEmitMs.set(now)
320
+
321
+ val ctx = reactContext ?: return
322
+ try {
323
+ val params = Arguments.createMap()
324
+ params.putInt("width", bitmap.width)
325
+ params.putInt("height", bitmap.height)
326
+ params.putDouble("timestamp", now.toDouble())
327
+
328
+ ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
329
+ .emit("FlirFrameReceived", params)
330
+ } catch (e: Exception) {
331
+ // Silently ignore
332
+ }
333
+ }
334
+
335
+ private fun emitDeviceState(state: String, isPhysical: Boolean) {
336
+ val ctx = reactContext ?: return
337
+ try {
338
+ val params = Arguments.createMap()
339
+ params.putString("state", state)
340
+ params.putBoolean("isPhysical", isPhysical)
341
+ params.putBoolean("isEmulator", isEmulatorMode)
342
+ params.putBoolean("isConnected", isPhysicalDeviceConnected || isEmulatorMode)
343
+ params.putBoolean("isStreaming", isStreaming)
344
+
345
+ connectedDeviceName?.let {
346
+ params.putString("deviceName", it)
347
+ }
348
+ connectedDeviceId?.let {
349
+ params.putString("deviceId", it)
350
+ }
351
+
352
+ ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
353
+ .emit("FlirDeviceConnected", params)
354
+ } catch (e: Exception) {
355
+ Log.e(TAG, "Failed to emit device state", e)
356
+ }
357
+ }
358
+
359
+ private fun emitDevicesFound(devices: List<FlirSdkManager.DeviceInfo>) {
360
+ val ctx = reactContext ?: return
361
+ try {
362
+ val params = Arguments.createMap()
363
+ val devicesArray: WritableArray = Arguments.createArray()
364
+
365
+ devices.forEach { device ->
366
+ val deviceMap: WritableMap = Arguments.createMap()
367
+ deviceMap.putString("id", device.deviceId)
368
+ deviceMap.putString("name", device.deviceName)
369
+ deviceMap.putString("communicationType", device.commInterface.name)
370
+ deviceMap.putBoolean("isEmulator", device.isEmulator)
371
+ devicesArray.pushMap(deviceMap)
372
+ }
373
+
374
+ params.putArray("devices", devicesArray)
375
+ params.putInt("count", devices.size)
376
+
377
+ ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
378
+ .emit("FlirDevicesFound", params)
379
+ } catch (e: Exception) {
380
+ Log.e(TAG, "Failed to emit devices found", e)
381
+ }
382
+ }
383
+
384
+ private fun emitError(message: String) {
385
+ val ctx = reactContext ?: return
386
+ try {
387
+ val params = Arguments.createMap()
388
+ params.putString("error", message)
389
+
390
+ ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
391
+ .emit("FlirError", params)
392
+ } catch (e: Exception) {
393
+ Log.e(TAG, "Failed to emit error", e)
394
+ }
395
+ }
396
+
397
+ // Compatibility for Java / FlirHelper
398
+ @JvmStatic
399
+ fun getInstance(): FlirManager = this
400
+
401
+ interface DiscoveryCallback {
402
+ fun onDeviceFound(deviceName: String)
403
+ fun onDiscoveryTimeout()
404
+ fun onEmulatorEnabled()
405
+ }
406
+
407
+ private var discoveryCallback: DiscoveryCallback? = null
408
+
409
+ fun setDiscoveryCallback(callback: DiscoveryCallback?) {
410
+ discoveryCallback = callback
411
+ }
412
+
413
+ fun setPalette(name: String) {
414
+ sdkManager?.setPalette(name)
415
+ }
416
+
417
+ fun setEmulatorMode(enabled: Boolean) {
418
+ if (enabled) {
419
+ isEmulatorMode = true
420
+ discoveryCallback?.onEmulatorEnabled()
421
+ startEmulator(preferredEmulatorType)
422
+ }
423
+ }
424
+
425
+ fun updateAcol(value: Float) {
426
+ // No-op for now - palette changes handled by setPalette
427
+ }
428
+
429
+ fun startDiscovery(retry: Boolean) {
430
+ Log.i(FLOW_TAG, "[FlirManager] startDiscovery($retry) called, sdkManager=${if (sdkManager != null) "present" else "NULL"}, appContext=${if (appContext != null) "present" else "NULL"}")
431
+
432
+ // Auto-init if not initialized
433
+ if (sdkManager == null && appContext != null) {
434
+ Log.i(FLOW_TAG, "[FlirManager] Auto-initializing from startDiscovery()")
435
+ init(appContext!!)
436
+ }
437
+
438
+ val ctx = reactContext
439
+ if (ctx != null) {
440
+ // Reset discovery state if retrying
441
+ if (retry) {
442
+ discoveryStarted = false
443
+ }
444
+ startDiscoveryAndConnect(ctx, isEmuMode = false)
445
+ } else if (appContext != null) {
446
+ // Fallback: try to start discovery without React context
447
+ try {
448
+ Log.w(TAG, "Starting discovery without React context")
449
+ Log.i(FLOW_TAG, "[FlirManager] Calling sdkManager.startDiscovery(false) without React context")
450
+ sdkManager?.startDiscovery(false)
451
+ } catch (t: Throwable) {
452
+ Log.e(TAG, "startDiscovery failed", t)
453
+ Log.e(FLOW_TAG, "[FlirManager] startDiscovery failed: ${t.message}")
454
+ }
455
+ } else {
456
+ Log.e(FLOW_TAG, "[FlirManager] Cannot startDiscovery - no context available! Call init(context) first.")
457
+ }
458
+ }
459
+
460
+ fun enableEmulatorMode() {
461
+ setEmulatorMode(true)
462
+ }
463
+
464
+ /**
465
+ * Force start with emulator (useful for testing)
466
+ */
467
+ fun forceEmulatorMode(type: String = "FLIR_ONE_EDGE") {
468
+ setPreferredEmulatorType(type)
469
+ val ctx = reactContext
470
+ if (ctx != null) {
471
+ discoveryStarted = false
472
+ startDiscoveryAndConnect(ctx, isEmuMode = true)
473
+ } else {
474
+ startEmulator(preferredEmulatorType)
475
+ }
476
+ }
477
+ }