feat: add version read, OTA characteristic, BLE MTU 512 in BleRelayManager

This commit is contained in:
eddy 2026-07-30 17:14:25 +02:00
commit 5235057b83

View file

@ -23,23 +23,27 @@ import java.nio.ByteOrder
import java.util.UUID
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import kotlin.text.Charsets
class BleRelayManager(
private val context: Context,
private val callbacks: BleCallbacks
) {
companion object {
var instance: BleRelayManager? = null
private const val TAG = "BleRelayManager"
private val SERVICE_UUID: UUID = UUID.fromString("4fafc201-1fb5-459e-8fcc-c5c9c331914b")
private val CHARACTERISTIC_UUID: UUID = UUID.fromString("beb5483e-36e1-4688-b7f5-ea07361f26d8")
private val UUID_CHAR_UUID: UUID = UUID.fromString("beb5483e-36e1-4688-b7f5-ea07361f26d9")
private val VERSION_UUID: UUID = UUID.fromString("beb5483e-36e1-4688-b7f5-ea07361f26db")
private val OTA_UUID: UUID = UUID.fromString("beb5483e-36e1-4688-b7f5-ea07361f26da")
private const val SCAN_TIMEOUT_MS = 30000L
private const val RECONNECT_DELAY_MS = 2000L
private const val MAX_SCAN_RETRIES = 3
private const val HMAC_PKT_SIZE = 37
private const val HMAC_LEN = 32
private const val BLE_MTU = 256
private const val BLE_MTU = 512
}
private var bluetoothGatt: BluetoothGatt? = null
@ -52,6 +56,13 @@ class BleRelayManager(
private var hmacKey: ByteArray? = null
private var deviceUuid: ByteArray? = null
private var triedAddresses = mutableSetOf<String>()
private var otaCharacteristic: BluetoothGattCharacteristic? = null
private var espVersion: String? = null
private var appVersion: String = ""
init {
instance = this
}
private val scanRunnable = Runnable { startScan() }
private var scanTimeoutRunnable: Runnable? = null
@ -170,6 +181,22 @@ class BleRelayManager(
stopScan()
bluetoothGatt?.disconnect()
}
@SuppressLint("MissingPermission")
fun sendOtaData(data: ByteArray) {
val char = otaCharacteristic ?: return
val gatt = bluetoothGatt ?: return
char.value = data
char.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
gatt.writeCharacteristic(char)
}
@SuppressLint("MissingPermission")
fun enableOtaNotifications(): Boolean {
val char = otaCharacteristic ?: return false
val gatt = bluetoothGatt ?: return false
return gatt.setCharacteristicNotification(char, true)
}
fun updateKeys() {
loadState()
}
@ -182,6 +209,10 @@ class BleRelayManager(
return bluetoothGatt != null
}
fun setAppVersion(version: String) {
appVersion = version
}
@SuppressLint("MissingPermission")
private fun connectToDevice(device: android.bluetooth.BluetoothDevice) {
handler.post { callbacks.onStatusChanged(BleStatus.Connecting) }
@ -230,7 +261,9 @@ class BleRelayManager(
val service = gatt.getService(SERVICE_UUID)
if (service != null) {
relayCharacteristic = service.getCharacteristic(CHARACTERISTIC_UUID)
otaCharacteristic = service.getCharacteristic(OTA_UUID)
val uuidChar = service.getCharacteristic(UUID_CHAR_UUID)
val versionChar = service.getCharacteristic(VERSION_UUID)
if (relayCharacteristic != null && uuidChar != null) {
Log.d(TAG, "Reading device UUID characteristic...")
gatt.readCharacteristic(uuidChar)
@ -249,14 +282,26 @@ class BleRelayManager(
if (characteristic.uuid == UUID_CHAR_UUID && status == BluetoothGatt.GATT_SUCCESS) {
val storedUuid = deviceUuid
if (storedUuid != null && value.contentEquals(storedUuid)) {
Log.d(TAG, "UUID verified, setting up relay")
handler.post { callbacks.onStatusChanged(BleStatus.Connected) }
Log.d(TAG, "UUID verified, reading version...")
val service = gatt.getService(SERVICE_UUID)
val versionChar = service?.getCharacteristic(VERSION_UUID)
if (versionChar != null) {
gatt.readCharacteristic(versionChar)
} else {
handler.post { callbacks.onStatusChanged(BleStatus.Connected) }
}
} else {
Log.w(TAG, "UUID mismatch -- disconnecting")
triedAddresses.add(gatt.device.address)
handler.post { callbacks.onStatusChanged(BleStatus.WrongDevice) }
gatt.disconnect()
}
} else if (characteristic.uuid == VERSION_UUID && status == BluetoothGatt.GATT_SUCCESS) {
val version = String(value, Charsets.UTF_8).trim()
espVersion = version
Log.d(TAG, "ESP firmware version: $version, app bundled: $appVersion")
handler.post { callbacks.onFirmwareVersion(version, appVersion) }
handler.post { callbacks.onStatusChanged(BleStatus.Connected) }
}
}