Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Notification does not clear after setup a device #557

Open
KevinMartinezC opened this issue Apr 19, 2024 · 3 comments
Open

Notification does not clear after setup a device #557

KevinMartinezC opened this issue Apr 19, 2024 · 3 comments

Comments

@KevinMartinezC
Copy link

KevinMartinezC commented Apr 19, 2024

class MyBleManager(
   @ApplicationContext context: Context
) : BleManager(context), AltaBLE {
    override val firmwareVersion: MutableStateFlow<String?> = MutableStateFlow(EMPTY_STRING)
    override val serialNumber: MutableStateFlow<String?> = MutableStateFlow(EMPTY_STRING)
    override val pubKeyValue: MutableStateFlow<String?> = MutableStateFlow(EMPTY_STRING)
    override val isConnected: MutableStateFlow<Boolean> = MutableStateFlow(false)
    private val scope = CoroutineScope(Dispatchers.IO)
    private var serialNumberCharacteristic: BluetoothGattCharacteristic? = null
    private var firmwareRevisionCharacteristic: BluetoothGattCharacteristic? = null
    private var readChar: BluetoothGattCharacteristic? = null
    private var txCtlChar: BluetoothGattCharacteristic? = null
    private var writeChar: BluetoothGattCharacteristic? = null
    private var rxCredits: Int = 0x1000
    private var rxData: ByteArray = ByteArray(0)
    private var pubKey: ByteArray = ByteArray(0)
    private var pupkey: String? = null
    private var txData = ByteArray(0)

    override fun resetValues (){
        disableNotifications(readChar).enqueue()
        rxCredits = 0x1000
        pupkey = null
        txData = ByteArray(0)
        pubKey = ByteArray(0)
        isConnected.value = false
        firmwareVersion.value = null
        serialNumber.value = null
        pubKeyValue.value = null
        onServicesInvalidated()
        release()
    }

    override val state = stateAsFlow()
        .map {
            when (it) {
                is ConnectionState.Connecting,
                is ConnectionState.Initializing -> AltaBLE.State.LOADING
                is ConnectionState.Ready -> AltaBLE.State.READY
                is ConnectionState.Disconnecting,
                is ConnectionState.Disconnected -> AltaBLE.State.NOT_AVAILABLE
            }
        }
        .stateIn(scope, SharingStarted.Lazily, AltaBLE.State.NOT_AVAILABLE)

    override suspend fun connectDevice(device: BluetoothDevice) {
        Log.wtf("kevin","get connect device ${device.address}")
        connect(device)
            .retry(3, 600)
            .useAutoConnect(false)
            .timeout(6000)
            .suspend()
    }

    override fun release() {
        // Cancel all coroutines.
        scope.cancel()
        disconnect().enqueue()
    }

    override fun log(priority: Int, message: String) {
        Timber.log(priority, message)
    }

    override fun getMinLogPriority(): Int {
        // By default, the library logs only INFO or
        // higher priority messages. You may change it here.
        return Log.VERBOSE
    }

    override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean {
        val switchService = gatt.getService(SWITCH_SERVICE)
        firmwareRevisionCharacteristic = switchService?.getCharacteristic(FIRMWARE_CHARACTERISTIC)
        serialNumberCharacteristic = switchService?.getCharacteristic(SERIAL_CHARACTERISTIC)

        val sppService = gatt.getService(SPP_UUID)
        readChar = sppService?.getCharacteristic(READ_UUID)
        txCtlChar = sppService?.getCharacteristic(TX_CTL_UUID)
        writeChar = sppService?.getCharacteristic(WRITE_UUID)

        return switchService != null && firmwareRevisionCharacteristic != null && serialNumberCharacteristic != null &&
                sppService != null && readChar != null && txCtlChar != null && writeChar != null
    }

    @OptIn(ExperimentalUnsignedTypes::class)
    private fun sendCredits() {
        rxCredits = 0x1000
        val data =
            ubyteArrayOf((rxCredits and 0xff).toUByte(), (rxCredits ushr 8).toUByte()).toByteArray()

        writeCharacteristic(txCtlChar, data, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT)
            .with { _, _ -> rxCredits = 0 }
            .fail { _, status ->
                Timber.tag(ALTA_BLE_TAG).e("Failed to send credits, status: %s", status)
            }.enqueue()
    }


    override fun initialize() {
        requestMtu(REQUEST_MTU_DEFAULT).enqueue()

        readCharacteristic(serialNumberCharacteristic).with { _, data ->
            serialNumber.value = data.getStringValue(0)
            Timber.tag(ALTA_BLE_TAG).wtf("get serial number %s", serialNumber.value)
        }.enqueue()

        readCharacteristic(firmwareRevisionCharacteristic).with { device, data ->
            val firmwareRevision = data.getStringValue(0)
            firmwareVersion.value = firmwareRevision
            Timber.tag(ALTA_BLE_TAG).wtf("get firmware %s", firmwareRevision)
        }.enqueue()

        setNotificationCallback(readChar).with { device, data ->
            val value = data.value
            if (value != null) {
                collectDataKey(value)
            }
        }

        enableNotifications(readChar)
            .done {
            }.enqueue()

        sendCredits()
    }

    override fun onServicesInvalidated() {
        serialNumberCharacteristic = null
        firmwareRevisionCharacteristic = null
        txCtlChar = null
        readChar = null
        writeChar = null
    }

    private fun collectDataKey(value: ByteArray?) {
        value?.size?.let {
            rxCredits += it
        }
        if (value != null) {
            rxData += value
            Timber.tag("DEVICE-CHANGED-KEY").d("Value ${value.size}")
        }
        Timber.tag("DEVICE-CHANGED-KEY").d("RXData ${rxData.size}")
        if (rxData.size < THREE_INT) return

        val total: Int =
            rxData.copyOfRange(ONE_INT, THREE_INT).fold(ZER0_INT) { a, b -> (a shl 8) + b.toInt() }

        if (rxData.size < total + THREE_INT) return
        val pktType = rxData[ZER0_INT]
        val payload = rxData.copyOfRange(THREE_INT, THREE_INT + total)
        rxData = rxData.copyOfRange(THREE_INT + total, rxData.size)

        when (pktType.toUByte()) {
            DEVICE_PUB_KEY -> {
                pubKey = payload
                val pubKeyStr = pubKey.decodeToString()

                pupkey = pubKeyStr
                Timber.tag(ALTA_BLE_TAG).wtf("get pup key %s", pubKeyStr)
                if (pubKey.isNotEmpty()) {
                    pubKeyValue.value = pubKeyStr
                }
                Timber.tag("DEVICE-CHANGED-KEY").d(pubKeyStr)
            }

            DEVICE_STATE -> {
                val state = payload[ZER0_INT].toInt()
                val inet = if (state and (ONE_INT shl ZER0_INT) != ZER0_INT) INET else EMPTY_STRING
                val mesh = if (state and (ONE_INT shl ONE_INT) != ZER0_INT) MESH else EMPTY_STRING
                val cloud = if (state and (ONE_INT shl TWO_INT) != ZER0_INT) CLOUD else EMPTY_STRING
                val setup =
                    if (state and (ONE_INT shl THREE_INT) != ZER0_INT) SET_UP else EMPTY_STRING
                if (state and (ONE_INT shl THREE_INT) != ZER0_INT) {
                    Log.wtf("kevin","completed")
                    isConnected.value = true
                }
                Timber.tag("DEVICE-STATUS").d("state: $inet $mesh $cloud $setup")
            }

            else -> {}
        }
    }

    override suspend fun confirmSetup(setUpKey: String) = runBlocking {
        Timber.tag("DEVICE-CHANGED-STATE").d("internalkkey: $setUpKey")
        val decodedBytes = Base64.decode(setUpKey, Base64.DEFAULT)
        val data = decodedBytes.copyOfRange(ZER0_INT, decodedBytes.size)
        val count = data.size
        val prefixBytes =
            byteArrayOf(altaSetup.toByte(), (count shr 8).toByte(), (count and 0xff).toByte())
        val combined = prefixBytes + data
        txData += combined
        if (txData.isNotEmpty()) {
            flushData()
        }
    }

    private fun flushData() = runBlocking {
        while (txData.isNotEmpty()) {
            flushDataTxData().join()
        }
    }

    private fun flushDataTxData() = scope.launch {
        var mtu = min(mtu, REQUEST_MTU_DEFAULT) - REDUCE_BYTES_HEADER

        if (mtu < CHECK_NEGOTIATED_MTU_SWITCHES_VALUE) {
            mtu = min(MTU_SWITCHES, REQUEST_MTU_DEFAULT)
        }

        var txDataCopy = txData.copyOf()
        if (txDataCopy.size > mtu) {
            txDataCopy = txDataCopy.copyOfRange(ZER0_INT, mtu)
            txData = txData.copyOfRange(mtu, txData.size)
        } else {
            txData = ByteArray(ZER0_INT)
        }

        writeChar?.let { characteristic ->
            val writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
            writeCharacteristic(characteristic, txDataCopy, writeType)
                .with { device, data ->
                    Timber.tag(TAG_BLE_MANAGER).d("WRITE CHAR FLUSH $txDataCopy --- ${txDataCopy.size}")
                    Timber.tag(TAG_BLE_MANAGER).d("Data written to ${device.address}")
                }
                .fail { device, status ->
                    Timber.tag(TAG_BLE_MANAGER)
                        .e("Failed to write data to ${device.address}, status: $status")
                }
                .enqueue()
        } ?: run {
            Timber.tag(TAG_BLE_MANAGER).d("Characteristic for writing not found")
        }
    }
`
@KevinMartinezC
Copy link
Author

i am using the kotlin library but i have an issue when i have 2 devices to setup. it works with the first one but when try to setup a second one i get the firmware and serial number but it does not complete the process for getting the keys since like it does not clear the notification and detect it as setup

@KevinMartinezC
Copy link
Author

KevinMartinezC commented Apr 19, 2024

Services discovered
Primary service found
Requesting new MTU...
gatt.requestMtu(517)
configureMTU() - device: XX:XX:XX:XX:01:59 mtu: 517
onConfigureMTU() - Device=FC:B9:23:80:01:59 mtu=247 status=0
MTU changed to: 247
Reading characteristic 00002a25-0000-1000-8000-00805f9b34fb
gatt.readCharacteristic(00002a25-0000-1000-8000-00805f9b34fb)
Read Response received from 00002a25-0000-1000-8000-00805f9b34fb, value: (0x) 62-63-62-39-32-33-38-30-30-31-35-38
Reading characteristic 00002a26-0000-1000-8000-00805f9b34fb
gatt.readCharacteristic(00002a26-0000-1000-8000-00805f9b34fb)
onConnectionUpdated() - Device=FC:B9:23:80:01:59 interval=36 latency=0 timeout=500 status=0
Connection parameters updated (interval: 45.0ms, latency: 0, timeout: 5000ms)
unable to parse scan record: [2, 1, 6, 2, -1, 50, 5, 9, 65, 108, 116, 97]
unable to parse scan record: [2, 1, 6, 2, -1, 50, 5, 9, 65, 108, 116, 97]
Read Response received from 00002a26-0000-1000-8000-00805f9b34fb, value: (0x) 32-2E-31-65
gatt.setCharacteristicNotification(0734594a-a8e7-4b1a-a6b1-cd5243059a57, true)
setCharacteristicNotification() - uuid: 0734594a-a8e7-4b1a-a6b1-cd5243059a57 enable: true
Enabling notifications for 0734594a-a8e7-4b1a-a6b1-cd5243059a57
gatt.writeDescriptor(00002902-0000-1000-8000-00805f9b34fb, value=0x01-00)
get firmware 2.1e
unable to parse scan record: [2, 1, 6, 2, -1, 50, 5, 9, 65, 108, 116, 97]
unable to parse scan record: [2, 1, 6, 2, -1, 50, 5, 9, 65, 108, 116, 97]
Data written to descr. 00002902-0000-1000-8000-00805f9b34fb
Writing characteristic ba04c4b2-892b-43be-b69c-5d13f2195392 (WRITE REQUEST)
gatt.writeCharacteristic(ba04c4b2-892b-43be-b69c-5d13f2195392, value=0x0010, WRITE REQUEST)
Data written to ba04c4b2-892b-43be-b69c-5d13f2195392
Notification received from 0734594a-a8e7-4b1a-a6b1-cd5243059a57, value: (0x) 01-01-74-41-41-41-41-42-33-4E-7A-61-43-31-79-63-32-45-41-41-41-41-44-41-51-41-42-41-41-41-42-41-51-43-54-34-4B-42-5A-2B-47-76-72-33-4C-43-71-68-51-54-52-73-37-77-78-69-41-62-4C-48-67-70-6E-6B-48-48-58-6E-34-6D-31-71-53-66-61-4A-70-52-59-4B-34-68-51-6E-73-37-4A-41-37-69-33-78-6A-62-41-6D-46-4A-72-67-6A-77-4A-2F-66-4A-68-42-6D-68-5A-77-6E-41-79-6C-41-6A-61-64-35-62-34-42-69-4B-4E-47-2B-2F-4F-37-68-35-38-48-66-6F-6C-4E-6D-52-42-68-4C-30-45-36-30-31-2F-70-2F-32-56-45-42-4D-77-74-6C-4A-74-6A-65-6E-68-70-4D-46-37-70-79-67-66-55-50-34-39-34-79-41-41-45-61-54-76-57-45-44-69-68-6E-30-47-55-58-72-4A-67-4F-77-4B-53-4C-68-52-50-73-36-6A-6A-43-30-6C-72-61-55-77-6D-68-66-69-30-4D-4F-42-4F-31-2B-4A-2F-38-75-47-48-36-4D-39-43
Value 240
RXData 248
completed
get into is connected
gatt.setCharacteristicNotification(0734594a-a8e7-4b1a-a6b1-cd5243059a57, false)
tagSocket(208) with statsTag=0xffffffff, statsUid=-1
setCharacteristicNotification() - uuid: 0734594a-a8e7-4b1a-a6b1-cd5243059a57 enable: false
Disabling notifications and indications for 0734594a-a8e7-4b1a-a6b1-cd5243059a57
gatt.writeDescriptor(00002902-0000-1000-8000-00805f9b34fb, value=0x00-00)
get into is connected

it does not continue with the process of the notifications

@philips77 philips77 transferred this issue from NordicSemiconductor/Kotlin-BLE-Library Apr 19, 2024
@philips77
Copy link
Member

I think we solved it in another issue and can close this one?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants