From 62ce221860e7fb35994d4ded995375e84275a8db Mon Sep 17 00:00:00 2001 From: Stefan Zollinger Date: Thu, 11 Apr 2024 15:13:16 +0200 Subject: [PATCH] fix: properly handle connecting to bonded devices --- app/build.gradle | 1 + .../vc/kirbytest/ConnectionManager.kt | 2 + .../com/logitech/vc/kirbytest/DecoderIaq.kt | 4 +- .../com/logitech/vc/kirbytest/KirbyDevice.kt | 25 +++++++-- .../com/logitech/vc/kirbytest/MainActivity.kt | 56 ++++++++++++++++--- 5 files changed, 72 insertions(+), 16 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 5d734d4..130538d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -59,6 +59,7 @@ dependencies { implementation 'androidx.recyclerview:recyclerview:1.2.0' implementation 'com.google.code.gson:gson:2.8.8' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0' implementation group: 'commons-codec', name: 'commons-codec', version: '1.16.0' diff --git a/app/src/main/java/com/logitech/vc/kirbytest/ConnectionManager.kt b/app/src/main/java/com/logitech/vc/kirbytest/ConnectionManager.kt index 86775a6..b171c11 100644 --- a/app/src/main/java/com/logitech/vc/kirbytest/ConnectionManager.kt +++ b/app/src/main/java/com/logitech/vc/kirbytest/ConnectionManager.kt @@ -394,9 +394,11 @@ class ConnectionManager(val context: Context, bleAdapter: BluetoothAdapter) { @Synchronized private fun enqueueOperation(operation: BleOperationType) { + /* if (isScanning) { stopScan() } + */ operationQueue.add(operation) notifyListenersOfQueueChange(operation.device.address) if (pendingOperation == null) { diff --git a/app/src/main/java/com/logitech/vc/kirbytest/DecoderIaq.kt b/app/src/main/java/com/logitech/vc/kirbytest/DecoderIaq.kt index e28d350..03bcad1 100644 --- a/app/src/main/java/com/logitech/vc/kirbytest/DecoderIaq.kt +++ b/app/src/main/java/com/logitech/vc/kirbytest/DecoderIaq.kt @@ -84,6 +84,7 @@ object DecoderIaq { } data class Measurement ( + var deviceId: String? = null, var msgType: Number? = null, var co2: Number? = null, var voc: Number? = null, @@ -110,8 +111,5 @@ object DecoderIaq { '}' } - fun toCsv() : String { - return "${msgType},${co2},${voc},${humidity},${temperature},${pressure}${pm25},${pm10},${occupancy}" - } } } \ No newline at end of file diff --git a/app/src/main/java/com/logitech/vc/kirbytest/KirbyDevice.kt b/app/src/main/java/com/logitech/vc/kirbytest/KirbyDevice.kt index aba33e3..560a356 100644 --- a/app/src/main/java/com/logitech/vc/kirbytest/KirbyDevice.kt +++ b/app/src/main/java/com/logitech/vc/kirbytest/KirbyDevice.kt @@ -45,16 +45,18 @@ class KirbyDevice( private val queue: RequestQueue = Volley.newRequestQueue(context) fun subscribe() { - - connectionManager.enableNotification( - bleDevice, SERVICE_UUID, CHAR_UUID - ) + if(statuses.contains(DeviceStatus.CONNECTED)) { + connectionManager.enableNotification( + bleDevice, SERVICE_UUID, CHAR_UUID + ) + } } fun readIaq() { connectionManager.readChar(bleDevice, SERVICE_UUID, CHAR_UUID) } + override fun onSuccessfulCharRead( gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic @@ -66,13 +68,20 @@ class KirbyDevice( override fun onScanResult(callbackType: Int, result: ScanResult) { rssi = result.rssi onStateChange(this) + + if(result.isConnectable) { + connectionManager.connect(bleDevice) + connectionManager.discoverServices(bleDevice) + } } override fun onConnect(gatt: BluetoothGatt) { statuses.add(DeviceStatus.CONNECTED) statuses.remove(DeviceStatus.MISSING) + subscribe() onStateChange(this) + } override fun onConnectToBondedFailed(gatt: BluetoothGatt) { @@ -136,12 +145,13 @@ class KirbyDevice( if (measurement == null) { payload = Payload(hexPayload) } else { + measurement.deviceId = bleDevice.address payload = Payload(measurement.toString()) Log.i("BleListener", "Char received: $payload") val base64Payload = Base64.getEncoder().encodeToString(characteristic.value) publishMeasurement(base64Payload) - loggerDb.writeLog( measurement) + loggerDb.writeLog(measurement) } measurements.add(payload) @@ -155,6 +165,11 @@ class KirbyDevice( private fun publishMeasurement(payload: String) { val accessKey = BuildConfig.API_KEY val url = BuildConfig.API_BASE_URL + + if(url.isEmpty()) { + return + } + val eui = "0000${bleDevice.address.replace(":", "")}" val postData = JSONObject() diff --git a/app/src/main/java/com/logitech/vc/kirbytest/MainActivity.kt b/app/src/main/java/com/logitech/vc/kirbytest/MainActivity.kt index 892cd6e..a7cea79 100644 --- a/app/src/main/java/com/logitech/vc/kirbytest/MainActivity.kt +++ b/app/src/main/java/com/logitech/vc/kirbytest/MainActivity.kt @@ -15,20 +15,29 @@ import android.content.pm.PackageManager import android.net.Uri import android.os.Build import android.os.Bundle +import android.os.Handler import android.util.Log import android.view.Menu import android.view.MenuItem import android.view.View.INVISIBLE import android.view.View.VISIBLE +import android.widget.Toast import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts +import androidx.activity.result.contract.ActivityResultContracts.CreateDocument import androidx.annotation.RequiresApi import androidx.appcompat.app.AppCompatActivity import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.core.view.WindowCompat +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.SimpleItemAnimator import com.logitech.vc.kirbytest.databinding.ActivityMainBinding +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.time.LocalDateTime import java.time.format.DateTimeFormatter @@ -108,12 +117,33 @@ class MainActivity : AppCompatActivity() { setupDevicesList() createFileLauncher = - registerForActivityResult(ActivityResultContracts.CreateDocument()) { uri: Uri? -> + registerForActivityResult(CreateDocument("text/csv")) { uri: Uri? -> uri?.let { // Use the URI to write your CSV content - loggerDb.exportToUri(uri) + CoroutineScope(Dispatchers.IO).launch { + loggerDb.exportToUri(uri) + + withContext(Dispatchers.Main) { + Toast.makeText( + this@MainActivity, + "Log export completed.", + Toast.LENGTH_SHORT + ).show() + } + } } } + + + connectionManager.startScan() + + lifecycleScope.launch { + + withContext(Dispatchers.Main) { + delay(5000L) + connectionManager.stopScan() + } + } } private fun toggleScanning(): Unit { @@ -156,11 +186,11 @@ class MainActivity : AppCompatActivity() { private fun addBondedDevices(): Unit { bluetoothAdapter.bondedDevices.filter { isKirbyDevice(it) }.forEach { - val kirbyDevice = newKirbyDevice(it) - kirbyDevice.subscribe() + newKirbyDevice(it) } } + @SuppressLint("NotifyDataSetChanged") private fun addDummyDevices() { for (i in 0..14) { @@ -183,7 +213,7 @@ class MainActivity : AppCompatActivity() { } override fun onScanResult(callbackType: Int, result: ScanResult) { - if (isKirbyDevice(result.device)) { + if (isKirbyDevice(result.device) && result.isConnectable) { Log.i( "ScanCallback", @@ -200,7 +230,10 @@ class MainActivity : AppCompatActivity() { return mngr } - private fun newKirbyDevice(bleDevice: BluetoothDevice): KirbyDevice { + private fun newKirbyDevice( + bleDevice: BluetoothDevice, + autoConnect: Boolean = false + ): KirbyDevice { val device = KirbyDevice(this.applicationContext, connectionManager, bleDevice, loggerDb) { val i = kirbyDevices.indexOfFirst { d -> d === it } @@ -208,9 +241,16 @@ class MainActivity : AppCompatActivity() { deviceListAdapter.notifyItemChanged(i) } } + Log.i("MainActivity", bleDevice.address) connectionManager.register(device) - connectionManager.connect(bleDevice) - connectionManager.discoverServices(bleDevice) + + if (autoConnect) { + connectionManager.connect(bleDevice) + connectionManager.discoverServices(bleDevice) + + } + + kirbyDevices.add(device) deviceListAdapter.notifyItemInserted(kirbyDevices.size - 1) return device