ilabs-flir 1.0.5 → 1.0.7

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.
@@ -4,39 +4,45 @@ import android.content.Context
4
4
  import android.graphics.Bitmap
5
5
  import android.util.Log
6
6
  import com.facebook.react.bridge.Arguments
7
+ import com.facebook.react.bridge.ReactApplicationContext
8
+ import com.facebook.react.bridge.ReactContext
7
9
  import com.facebook.react.bridge.WritableArray
8
10
  import com.facebook.react.bridge.WritableMap
9
11
  import com.facebook.react.modules.core.DeviceEventManagerModule
10
12
  import com.facebook.react.uimanager.ThemedReactContext
13
+ import com.flir.thermalsdk.live.Identity
11
14
  import java.io.File
12
15
  import java.io.FileOutputStream
13
16
  import java.util.concurrent.atomic.AtomicLong
14
17
 
18
+ /**
19
+ * Simplified FlirManager - bridge between React Native and FlirSdkManager
20
+ * No filtering - returns ALL discovered devices (USB, Network, Emulator)
21
+ * Let React Native handle any filtering logic
22
+ */
15
23
  object FlirManager {
16
- private val TAG = "FlirManager"
17
- private val FLOW_TAG = "FLIR_FLOW" // Matching FlirSdkManager flow tag
24
+ private const val TAG = "FlirManager"
25
+
18
26
  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
27
+ private var reactContext: ReactContext? = null
23
28
  private var appContext: Context? = null
24
- private var frameCount = 0 // For tracking first frame
25
29
 
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
30
+ // Frame rate limiting
31
+ private val lastEmitMs = AtomicLong(0)
32
+ private val minEmitIntervalMs = 100L // ~10 fps max for RN events
32
33
 
33
- // Current emulator type preference
34
- private var preferredEmulatorType = FlirSdkManager.EmulatorType.FLIR_ONE_EDGE
34
+ // State
35
+ private var isInitialized = false
36
+ private var isScanning = false
37
+ private var isConnected = false
38
+ private var isStreaming = false
39
+ private var connectedDeviceId: String? = null
40
+ private var connectedDeviceName: String? = null
35
41
 
36
- // Discovered devices cache
37
- private var discoveredDevices: List<FlirSdkManager.DeviceInfo> = emptyList()
42
+ // Latest bitmap for texture updates
43
+ private var latestBitmap: Bitmap? = null
38
44
 
39
- // GL texture callback support for native filters
45
+ // Callbacks
40
46
  interface TextureUpdateCallback {
41
47
  fun onTextureUpdate(bitmap: Bitmap, textureUnit: Int)
42
48
  }
@@ -47,7 +53,6 @@ object FlirManager {
47
53
 
48
54
  private var textureCallback: TextureUpdateCallback? = null
49
55
  private var temperatureCallback: TemperatureCallback? = null
50
- private var latestBitmap: Bitmap? = null
51
56
 
52
57
  fun setTextureCallback(callback: TextureUpdateCallback?) {
53
58
  textureCallback = callback
@@ -59,243 +64,203 @@ object FlirManager {
59
64
 
60
65
  fun getLatestBitmap(): Bitmap? = latestBitmap
61
66
 
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
67
  /**
71
- * Check if currently running in emulator mode (no physical FLIR device)
68
+ * Initialize the FLIR SDK
72
69
  */
73
- fun isEmulator(): Boolean = isEmulatorMode
70
+ fun init(context: Context) {
71
+ // Store react context for event emission if it's a React context
72
+ // Always update if we get a valid ReactContext (in case previous was stale)
73
+ if (context is ReactContext) {
74
+ Log.d(TAG, "Storing ReactContext for event emission: ${context.javaClass.simpleName}")
75
+ reactContext = context
76
+ } else {
77
+ Log.d(TAG, "Context is not ReactContext: ${context.javaClass.simpleName}")
78
+ }
79
+
80
+ if (isInitialized) {
81
+ Log.d(TAG, "Already initialized")
82
+ return
83
+ }
84
+
85
+ appContext = context.applicationContext
86
+
87
+ sdkManager = FlirSdkManager.getInstance(context)
88
+ sdkManager?.setListener(sdkListener)
89
+ sdkManager?.initialize()
90
+
91
+ isInitialized = true
92
+ Log.i(TAG, "FlirManager initialized")
93
+ }
74
94
 
75
95
  /**
76
- * Check if a physical FLIR device is connected
96
+ * Start scanning for devices (USB, Network, Emulator - ALL types)
77
97
  */
78
- fun isDeviceConnected(): Boolean = isPhysicalDeviceConnected || isEmulatorMode
98
+ fun startDiscovery(retry: Boolean = false) {
99
+ Log.i(TAG, "startDiscovery(retry=$retry)")
100
+
101
+ if (!isInitialized && appContext != null) {
102
+ init(appContext!!)
103
+ }
104
+
105
+ if (isScanning && !retry) {
106
+ Log.d(TAG, "Already scanning")
107
+ return
108
+ }
109
+
110
+ isScanning = true
111
+ emitDeviceState("discovering")
112
+ sdkManager?.scan()
113
+ }
79
114
 
80
115
  /**
81
- * Check if currently streaming
116
+ * Start discovery with React context
82
117
  */
83
- fun isStreaming(): Boolean = isStreaming
118
+ fun startDiscoveryAndConnect(context: ThemedReactContext, isEmuMode: Boolean = false) {
119
+ reactContext = context
120
+ startDiscovery(retry = false)
121
+ }
84
122
 
85
123
  /**
86
- * Get list of discovered devices
124
+ * Stop scanning
87
125
  */
88
- fun getDiscoveredDevices(): List<FlirSdkManager.DeviceInfo> = discoveredDevices
126
+ fun stopDiscovery() {
127
+ Log.i(TAG, "stopDiscovery")
128
+ sdkManager?.stop()
129
+ isScanning = false
130
+ }
89
131
 
90
132
  /**
91
- * Get information about the connected device
133
+ * Connect to a device by ID
92
134
  */
93
- fun getConnectedDeviceInfo(): String {
94
- return when {
95
- connectedDeviceName == null -> "Not connected"
96
- isEmulatorMode -> "Emulator ($connectedDeviceName)"
97
- else -> "Physical device ($connectedDeviceName)"
135
+ fun connectToDevice(deviceId: String) {
136
+ Log.i(TAG, "connectToDevice: $deviceId")
137
+
138
+ val devices = sdkManager?.discoveredDevices ?: emptyList()
139
+ val identity = devices.find { it.deviceId == deviceId }
140
+
141
+ if (identity != null) {
142
+ sdkManager?.connect(identity)
143
+ } else {
144
+ Log.e(TAG, "Device not found: $deviceId")
145
+ emitError("Device not found: $deviceId")
98
146
  }
99
147
  }
100
148
 
101
149
  /**
102
- * Set preferred emulator type (FLIR_ONE_EDGE or FLIR_ONE)
150
+ * Switch to a different device
103
151
  */
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")
152
+ fun switchToDevice(deviceId: String) {
153
+ if (deviceId == connectedDeviceId) {
154
+ Log.d(TAG, "Already connected to: $deviceId")
117
155
  return
118
156
  }
119
157
 
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)
158
+ // Disconnect current and connect new
159
+ if (isConnected) {
160
+ sdkManager?.disconnect()
217
161
  }
162
+ connectToDevice(deviceId)
218
163
  }
219
-
164
+
220
165
  /**
221
- * Start discovery with the specified parameters
222
- * @param isEmuMode If true, immediately start emulator without physical device discovery
166
+ * Start streaming from connected device
223
167
  */
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
- }
168
+ fun startStream() {
169
+ Log.i(TAG, "startStream")
170
+ sdkManager?.startStream()
251
171
  }
252
172
 
253
173
  /**
254
- * Legacy overload for backward compatibility
174
+ * Stop streaming
255
175
  */
256
- fun startDiscoveryAndConnect(context: ThemedReactContext) {
257
- startDiscoveryAndConnect(context, isEmuMode = false)
176
+ fun stopStream() {
177
+ Log.i(TAG, "stopStream")
178
+ sdkManager?.stopStream()
179
+ isStreaming = false
258
180
  }
259
-
181
+
260
182
  /**
261
- * Switch to a specific device by ID
183
+ * Disconnect from current device
262
184
  */
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)
185
+ fun disconnect() {
186
+ Log.i(TAG, "disconnect")
187
+ sdkManager?.disconnect()
188
+ isConnected = false
189
+ isStreaming = false
190
+ connectedDeviceId = null
191
+ connectedDeviceName = null
271
192
  }
272
193
 
273
194
  /**
274
- * Start emulator mode
195
+ * Stop everything
275
196
  */
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
197
  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
198
+ Log.i(TAG, "stop")
199
+ stopStream()
200
+ disconnect()
201
+ stopDiscovery()
295
202
  latestBitmap = null
296
- discoveredDevices = emptyList()
297
203
  }
298
-
204
+
205
+ /**
206
+ * Get temperature at point in image coordinates
207
+ */
208
+ fun getTemperatureAt(x: Int, y: Int): Double? {
209
+ return sdkManager?.getTemperatureAt(x, y)?.takeIf { !it.isNaN() }
210
+ }
211
+
212
+ /**
213
+ * Get temperature at normalized coordinates (0.0 to 1.0)
214
+ */
215
+ fun getTemperatureAtNormalized(normalizedX: Double, normalizedY: Double): Double? {
216
+ return sdkManager?.getTemperatureAtNormalized(normalizedX, normalizedY)?.takeIf { !it.isNaN() }
217
+ }
218
+
219
+ /**
220
+ * Alias for getTemperatureAt
221
+ */
222
+ fun getTemperatureAtPoint(x: Int, y: Int): Double? = getTemperatureAt(x, y)
223
+
224
+ /**
225
+ * Set palette
226
+ */
227
+ fun setPalette(name: String) {
228
+ Log.d(TAG, "setPalette: $name")
229
+ sdkManager?.setPalette(name)
230
+ }
231
+
232
+ /**
233
+ * Get available palettes
234
+ */
235
+ fun getAvailablePalettes(): List<String> {
236
+ return sdkManager?.availablePalettes ?: emptyList()
237
+ }
238
+
239
+ /**
240
+ * Get list of discovered devices
241
+ */
242
+ fun getDiscoveredDevices(): List<Identity> {
243
+ return sdkManager?.discoveredDevices ?: emptyList()
244
+ }
245
+
246
+ /**
247
+ * Check states
248
+ */
249
+ fun isConnected(): Boolean = isConnected
250
+ fun isStreaming(): Boolean = isStreaming
251
+ fun isEmulator(): Boolean = connectedDeviceName?.contains("EMULAT", ignoreCase = true) == true
252
+ fun isDeviceConnected(): Boolean = isConnected
253
+
254
+ /**
255
+ * Get connected device info
256
+ */
257
+ fun getConnectedDeviceInfo(): String {
258
+ return connectedDeviceName ?: "Not connected"
259
+ }
260
+
261
+ /**
262
+ * Get latest frame as file path (for RN)
263
+ */
299
264
  fun getLatestFramePath(): String? {
300
265
  val bitmap = latestBitmap ?: return null
301
266
  return try {
@@ -305,50 +270,109 @@ object FlirManager {
305
270
  }
306
271
  file.absolutePath
307
272
  } catch (t: Throwable) {
273
+ Log.e(TAG, "Failed to save frame", t)
308
274
  null
309
275
  }
310
276
  }
311
-
312
- fun getTemperatureAt(x: Int, y: Int): Double? {
313
- return getTemperatureAtPoint(x, y)
277
+
278
+ // SDK Listener
279
+ private val sdkListener = object : FlirSdkManager.Listener {
280
+ override fun onDeviceFound(identity: Identity) {
281
+ Log.i(TAG, "Device found: ${identity.deviceId}")
282
+ }
283
+
284
+ override fun onDeviceListUpdated(devices: List<Identity>) {
285
+ Log.i(TAG, "Devices updated: ${devices.size} found")
286
+ devices.forEach {
287
+ Log.d(TAG, " - ${it.deviceId} (${it.communicationInterface})")
288
+ }
289
+ emitDevicesFound(devices)
290
+ }
291
+
292
+ override fun onConnected(identity: Identity?) {
293
+ Log.i(TAG, "Connected to: ${identity?.deviceId}")
294
+ isConnected = true
295
+ connectedDeviceId = identity?.deviceId
296
+ connectedDeviceName = identity?.deviceId
297
+ emitDeviceState("connected")
298
+
299
+ // Auto-start streaming when connected
300
+ startStream()
301
+ }
302
+
303
+ override fun onDisconnected() {
304
+ Log.i(TAG, "Disconnected")
305
+ isConnected = false
306
+ isStreaming = false
307
+ connectedDeviceId = null
308
+ connectedDeviceName = null
309
+ emitDeviceState("disconnected")
310
+ }
311
+
312
+ override fun onFrame(bitmap: Bitmap) {
313
+ if (bitmap.isRecycled || bitmap.width <= 0 || bitmap.height <= 0) {
314
+ return
315
+ }
316
+
317
+ latestBitmap = bitmap
318
+ isStreaming = true
319
+
320
+ // Notify texture callback (for GL rendering)
321
+ if (textureCallback != null) {
322
+ textureCallback?.onTextureUpdate(bitmap, 0)
323
+ } else {
324
+ // Log only occasionally to avoid spam
325
+ if (System.currentTimeMillis() % 5000 < 100) {
326
+ Log.w(TAG, "⚠️ Frame received but textureCallback is null - texture won't update!")
327
+ }
328
+ }
329
+
330
+ // Rate-limited RN event
331
+ emitFrameToReactNative(bitmap)
332
+ }
333
+
334
+ override fun onError(message: String) {
335
+ Log.e(TAG, "Error: $message")
336
+ emitError(message)
337
+ }
314
338
  }
315
-
339
+
340
+ // React Native event emitters
316
341
  private fun emitFrameToReactNative(bitmap: Bitmap) {
317
342
  val now = System.currentTimeMillis()
318
343
  if (now - lastEmitMs.get() < minEmitIntervalMs) return
319
344
  lastEmitMs.set(now)
320
-
345
+
321
346
  val ctx = reactContext ?: return
322
347
  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
-
348
+ val params = Arguments.createMap().apply {
349
+ putInt("width", bitmap.width)
350
+ putInt("height", bitmap.height)
351
+ putDouble("timestamp", now.toDouble())
352
+ }
328
353
  ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
329
354
  .emit("FlirFrameReceived", params)
330
355
  } catch (e: Exception) {
331
- // Silently ignore
356
+ // Ignore
332
357
  }
333
358
  }
334
-
335
- private fun emitDeviceState(state: String, isPhysical: Boolean) {
336
- val ctx = reactContext ?: return
359
+
360
+ private fun emitDeviceState(state: String) {
361
+ val ctx = reactContext
362
+ if (ctx == null) {
363
+ Log.e(TAG, "Cannot emit FlirDeviceConnected($state) - reactContext is null!")
364
+ return
365
+ }
366
+ Log.d(TAG, "Emitting FlirDeviceConnected: $state")
337
367
  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)
368
+ val params = Arguments.createMap().apply {
369
+ putString("state", state)
370
+ putBoolean("isConnected", isConnected)
371
+ putBoolean("isStreaming", isStreaming)
372
+ putBoolean("isEmulator", isEmulator())
373
+ connectedDeviceName?.let { putString("deviceName", it) }
374
+ connectedDeviceId?.let { putString("deviceId", it) }
350
375
  }
351
-
352
376
  ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
353
377
  .emit("FlirDeviceConnected", params)
354
378
  } catch (e: Exception) {
@@ -356,26 +380,33 @@ object FlirManager {
356
380
  }
357
381
  }
358
382
 
359
- private fun emitDevicesFound(devices: List<FlirSdkManager.DeviceInfo>) {
360
- val ctx = reactContext ?: return
383
+ private fun emitDevicesFound(devices: List<Identity>) {
384
+ val ctx = reactContext
385
+ if (ctx == null) {
386
+ Log.e(TAG, "Cannot emit FlirDevicesFound - reactContext is null!")
387
+ return
388
+ }
389
+ Log.d(TAG, "Emitting FlirDevicesFound with ${devices.size} devices")
361
390
  try {
362
391
  val params = Arguments.createMap()
363
392
  val devicesArray: WritableArray = Arguments.createArray()
364
393
 
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)
394
+ devices.forEach { identity ->
395
+ val deviceMap: WritableMap = Arguments.createMap().apply {
396
+ putString("id", identity.deviceId)
397
+ putString("name", identity.deviceId)
398
+ putString("communicationType", identity.communicationInterface.name)
399
+ putBoolean("isEmulator", identity.communicationInterface.name == "EMULATOR")
400
+ }
371
401
  devicesArray.pushMap(deviceMap)
372
402
  }
373
403
 
374
404
  params.putArray("devices", devicesArray)
375
405
  params.putInt("count", devices.size)
376
-
406
+
377
407
  ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
378
408
  .emit("FlirDevicesFound", params)
409
+ Log.d(TAG, "Successfully emitted FlirDevicesFound")
379
410
  } catch (e: Exception) {
380
411
  Log.e(TAG, "Failed to emit devices found", e)
381
412
  }
@@ -384,94 +415,62 @@ object FlirManager {
384
415
  private fun emitError(message: String) {
385
416
  val ctx = reactContext ?: return
386
417
  try {
387
- val params = Arguments.createMap()
388
- params.putString("error", message)
389
-
418
+ val params = Arguments.createMap().apply {
419
+ putString("error", message)
420
+ }
390
421
  ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
391
422
  .emit("FlirError", params)
392
423
  } catch (e: Exception) {
393
424
  Log.e(TAG, "Failed to emit error", e)
394
425
  }
395
426
  }
396
-
397
- // Compatibility for Java / FlirHelper
427
+
428
+ // Legacy compatibility
398
429
  @JvmStatic
399
430
  fun getInstance(): FlirManager = this
400
-
431
+
401
432
  interface DiscoveryCallback {
402
433
  fun onDeviceFound(deviceName: String)
403
434
  fun onDiscoveryTimeout()
404
435
  fun onEmulatorEnabled()
405
436
  }
406
-
437
+
407
438
  private var discoveryCallback: DiscoveryCallback? = null
408
-
439
+
409
440
  fun setDiscoveryCallback(callback: DiscoveryCallback?) {
410
441
  discoveryCallback = callback
411
442
  }
412
-
413
- fun setPalette(name: String) {
414
- sdkManager?.setPalette(name)
415
- }
416
-
443
+
444
+ // Legacy methods - no-ops or simple forwards
417
445
  fun setEmulatorMode(enabled: Boolean) {
446
+ Log.d(TAG, "setEmulatorMode($enabled) - legacy, use startDiscovery() instead")
418
447
  if (enabled) {
419
- isEmulatorMode = true
420
- discoveryCallback?.onEmulatorEnabled()
421
- startEmulator(preferredEmulatorType)
448
+ startDiscovery(retry = true)
422
449
  }
423
450
  }
424
-
425
- fun updateAcol(value: Float) {
426
- // No-op for now - palette changes handled by setPalette
451
+
452
+ fun enableEmulatorMode() = setEmulatorMode(true)
453
+
454
+ fun forceEmulatorMode(type: String = "FLIR_ONE_EDGE") {
455
+ Log.d(TAG, "forceEmulatorMode($type) - legacy, use startDiscovery() instead")
456
+ startDiscovery(retry = true)
427
457
  }
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
+ fun setPreferredEmulatorType(type: String) {
460
+ Log.d(TAG, "setPreferredEmulatorType($type) - legacy, no longer used")
458
461
  }
459
462
 
460
- fun enableEmulatorMode() {
461
- setEmulatorMode(true)
463
+ fun updateAcol(value: Float) {
464
+ // No-op - not used in simplified version
462
465
  }
463
466
 
464
467
  /**
465
- * Force start with emulator (useful for testing)
468
+ * Cleanup
466
469
  */
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
- }
470
+ fun destroy() {
471
+ stop()
472
+ sdkManager?.destroy()
473
+ sdkManager = null
474
+ isInitialized = false
476
475
  }
477
476
  }