chore: rename project
This commit is contained in:
390
app/src/main/java/com/logitech/vc/kirbytest/KirbyDevice.kt
Normal file
390
app/src/main/java/com/logitech/vc/kirbytest/KirbyDevice.kt
Normal file
@@ -0,0 +1,390 @@
|
||||
package com.logitech.vc.kirbytest
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import android.bluetooth.BluetoothDevice.BOND_BONDED
|
||||
import android.bluetooth.BluetoothGatt
|
||||
import android.bluetooth.BluetoothGattCharacteristic
|
||||
import android.bluetooth.BluetoothGattDescriptor
|
||||
import android.bluetooth.le.ScanResult
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import com.android.volley.Request
|
||||
import com.android.volley.RequestQueue
|
||||
import com.android.volley.toolbox.JsonObjectRequest
|
||||
import com.android.volley.toolbox.Volley
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.Base64
|
||||
import java.util.EnumSet
|
||||
import java.util.UUID
|
||||
import java.util.stream.Collectors
|
||||
|
||||
|
||||
private val SERVICE_UUID = UUID.fromString(BuildConfig.SERVICE_UUID)
|
||||
private val CHAR_UUID = UUID.fromString(BuildConfig.CHAR_UUID)
|
||||
|
||||
enum class DeviceStatus {
|
||||
CONNECTED, BONDED, SUBSCRIBED, MISSING
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
class KirbyDevice(
|
||||
private val context: Context,
|
||||
private val connectionManager: ConnectionManager,
|
||||
private val bleDevice: BluetoothDevice,
|
||||
private val loggerDb: LoggerContract.LoggerDb,
|
||||
private val onStateChange: (device: KirbyDevice) -> Unit,
|
||||
|
||||
|
||||
) : BleListener(bleDevice.address), DeviceListEntry {
|
||||
private val queue: RequestQueue = Volley.newRequestQueue(context)
|
||||
|
||||
fun subscribe() {
|
||||
|
||||
connectionManager.enableNotification(
|
||||
bleDevice, SERVICE_UUID, CHAR_UUID
|
||||
)
|
||||
}
|
||||
|
||||
fun readIaq() {
|
||||
connectionManager.readChar(bleDevice, SERVICE_UUID, CHAR_UUID)
|
||||
}
|
||||
|
||||
override fun onSuccessfulCharRead(
|
||||
gatt: BluetoothGatt,
|
||||
characteristic: BluetoothGattCharacteristic
|
||||
) {
|
||||
addMeasurement(characteristic)
|
||||
onStateChange(this)
|
||||
}
|
||||
|
||||
override fun onScanResult(callbackType: Int, result: ScanResult) {
|
||||
rssi = result.rssi
|
||||
onStateChange(this)
|
||||
}
|
||||
|
||||
override fun onConnect(gatt: BluetoothGatt) {
|
||||
statuses.add(DeviceStatus.CONNECTED)
|
||||
statuses.remove(DeviceStatus.MISSING)
|
||||
|
||||
onStateChange(this)
|
||||
}
|
||||
|
||||
override fun onConnectToBondedFailed(gatt: BluetoothGatt) {
|
||||
statuses.add(DeviceStatus.MISSING)
|
||||
onStateChange(this)
|
||||
}
|
||||
|
||||
override fun onDisconnect(gatt: BluetoothGatt) {
|
||||
statuses.remove(DeviceStatus.CONNECTED)
|
||||
statuses.remove(DeviceStatus.SUBSCRIBED)
|
||||
onStateChange(this)
|
||||
}
|
||||
|
||||
override fun onCharChange(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
|
||||
addMeasurement(characteristic)
|
||||
onStateChange(this)
|
||||
}
|
||||
|
||||
override fun onSubscribe(
|
||||
gatt: BluetoothGatt,
|
||||
descriptor: BluetoothGattDescriptor,
|
||||
) {
|
||||
|
||||
statuses.add(DeviceStatus.SUBSCRIBED)
|
||||
onStateChange(this)
|
||||
}
|
||||
|
||||
override fun onUnsubscribe(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor) {
|
||||
statuses.remove(DeviceStatus.SUBSCRIBED)
|
||||
onStateChange(this)
|
||||
}
|
||||
|
||||
override fun onQueueSizeChange(groupedOps: Map<String, List<BleOperationType>>) {
|
||||
hasRunningOp = groupedOps.getOrDefault(bleDevice.address, emptyList()).isNotEmpty()
|
||||
onStateChange(this)
|
||||
}
|
||||
|
||||
override fun onBonded(device: BluetoothDevice) {
|
||||
statuses.add(DeviceStatus.BONDED)
|
||||
onStateChange(this)
|
||||
}
|
||||
|
||||
override fun onUnbonded(device: BluetoothDevice) {
|
||||
statuses.remove(DeviceStatus.BONDED)
|
||||
onStateChange(this)
|
||||
}
|
||||
|
||||
override fun onReadRemoteRssi(gatt: BluetoothGatt, rssi: Int) {
|
||||
this.rssi = rssi
|
||||
onStateChange(this)
|
||||
}
|
||||
|
||||
override var hasRunningOp: Boolean = false
|
||||
|
||||
override var rssi: Int? = null
|
||||
|
||||
private fun addMeasurement(characteristic: BluetoothGattCharacteristic) {
|
||||
val hexPayload = characteristic.value.toHexString().substring(2)
|
||||
val measurement = DecoderIaq.parseMeasurement(hexPayload)
|
||||
var payload : Payload
|
||||
if (measurement == null) {
|
||||
payload = Payload(hexPayload)
|
||||
} else {
|
||||
payload = Payload(measurement.toString())
|
||||
Log.i("BleListener", "Char received: $payload")
|
||||
val base64Payload = Base64.getEncoder().encodeToString(characteristic.value)
|
||||
publishMeasurement(base64Payload)
|
||||
|
||||
loggerDb.writeLog( measurement)
|
||||
}
|
||||
|
||||
measurements.add(payload)
|
||||
|
||||
if (measurements.size > maxMeasurements) {
|
||||
measurements.removeFirst()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun publishMeasurement(payload: String) {
|
||||
val accessKey = BuildConfig.API_KEY
|
||||
val url = BuildConfig.API_BASE_URL
|
||||
val eui = "0000${bleDevice.address.replace(":", "")}"
|
||||
|
||||
val postData = JSONObject()
|
||||
|
||||
try {
|
||||
Log.i("POST", "Transmitting for $eui: $payload")
|
||||
postData.put("accessKey", accessKey)
|
||||
postData.put("metricPayload", payload)
|
||||
postData.put("eui", eui)
|
||||
} catch (e: JSONException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
val request = JsonObjectRequest(
|
||||
Request.Method.POST, url, postData,
|
||||
{ response ->
|
||||
Log.i("sendDataResponse", "Response is: $response")
|
||||
}
|
||||
) { error -> error.printStackTrace() }
|
||||
|
||||
queue.add(request)
|
||||
}
|
||||
|
||||
private val measurements = ArrayList<Payload>()
|
||||
private val maxMeasurements = 20
|
||||
|
||||
private val statuses = EnumSet.noneOf(DeviceStatus::class.java)
|
||||
|
||||
init {
|
||||
if (bleDevice.bondState == BOND_BONDED) {
|
||||
statuses.add(DeviceStatus.BONDED)
|
||||
}
|
||||
}
|
||||
|
||||
override val address: String
|
||||
get() = bleDevice.address
|
||||
|
||||
|
||||
override val name: String?
|
||||
get() = bleDevice.name
|
||||
|
||||
override val status: String?
|
||||
get() = statuses.stream().map { it.name }.collect(Collectors.joining(", "))
|
||||
|
||||
override fun getMeasurements(): List<Measurement> {
|
||||
val result = mutableListOf<Measurement>()
|
||||
|
||||
measurements.reversed().forEach { m -> result.addAll(payloadToMeasurements(m))}
|
||||
|
||||
/*
|
||||
var pl = Payload(payload = "006b04ab74a1ed0d101404", ts = "2000")
|
||||
result.addAll(payloadToMeasurements(pl))
|
||||
result.addAll(payloadToMeasurements(pl))
|
||||
*/
|
||||
return result
|
||||
}
|
||||
|
||||
override fun getActions(): List<Action> {
|
||||
val actions = mutableListOf<Action>()
|
||||
if (!statuses.contains(DeviceStatus.BONDED)) {
|
||||
actions.add(object : Action {
|
||||
override fun getLabel(): String {
|
||||
return "Bond"
|
||||
}
|
||||
|
||||
override fun getIcon(): Int {
|
||||
return R.drawable.action_icon_bond
|
||||
}
|
||||
|
||||
override fun execute() {
|
||||
connectionManager.bond(bleDevice)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (!statuses.contains(DeviceStatus.CONNECTED)
|
||||
) {
|
||||
actions.add(object : Action {
|
||||
override fun getLabel(): String {
|
||||
return "Connect"
|
||||
}
|
||||
|
||||
override fun getIcon(): Int {
|
||||
return R.drawable.action_icon_connect
|
||||
}
|
||||
|
||||
override fun execute() {
|
||||
connectionManager.connect(bleDevice)
|
||||
connectionManager.readRemoteRssi(bleDevice)
|
||||
connectionManager.discoverServices(bleDevice)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (statuses.contains(DeviceStatus.CONNECTED)) {
|
||||
|
||||
actions.add(object : Action {
|
||||
override fun getLabel(): String {
|
||||
return "Disconnect"
|
||||
}
|
||||
|
||||
override fun getIcon(): Int {
|
||||
return R.drawable.action_icon_disconnect
|
||||
}
|
||||
|
||||
override fun execute() {
|
||||
connectionManager.teardownConnection(bleDevice)
|
||||
}
|
||||
})
|
||||
|
||||
actions.add(object : Action {
|
||||
override fun getLabel(): String {
|
||||
return "Fetch Measurement"
|
||||
}
|
||||
|
||||
override fun getIcon(): Int {
|
||||
return R.drawable.action_icon_fetch_measurement
|
||||
}
|
||||
|
||||
override fun execute() {
|
||||
readIaq()
|
||||
}
|
||||
})
|
||||
|
||||
if (!statuses.contains(DeviceStatus.SUBSCRIBED)) {
|
||||
actions.add(object : Action {
|
||||
override fun getLabel(): String {
|
||||
return "Subscribe"
|
||||
}
|
||||
|
||||
override fun getIcon(): Int {
|
||||
return R.drawable.action_icon_subscribe
|
||||
}
|
||||
|
||||
override fun execute() {
|
||||
subscribe()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
actions.add(object : Action {
|
||||
override fun getLabel(): String {
|
||||
return "Update Signal Strength"
|
||||
}
|
||||
|
||||
override fun getIcon(): Int {
|
||||
return R.drawable.action_icon_update_signal_strength
|
||||
}
|
||||
|
||||
override fun execute() {
|
||||
connectionManager.readRemoteRssi(bleDevice)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
if (statuses.contains(DeviceStatus.SUBSCRIBED)) {
|
||||
actions.add(object : Action {
|
||||
override fun getLabel(): String {
|
||||
return "Unsubscribe"
|
||||
}
|
||||
|
||||
override fun getIcon(): Int {
|
||||
return R.drawable.action_icon_subscribe_disable
|
||||
}
|
||||
|
||||
override fun execute() {
|
||||
connectionManager.disableNotification(
|
||||
bleDevice, SERVICE_UUID, CHAR_UUID
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (statuses.contains(DeviceStatus.BONDED)
|
||||
) {
|
||||
actions.add(object : Action {
|
||||
override fun getLabel(): String {
|
||||
return "Unbond"
|
||||
}
|
||||
|
||||
override fun getIcon(): Int {
|
||||
return R.drawable.action_icon_unbond
|
||||
}
|
||||
|
||||
override fun execute() {
|
||||
connectionManager.unbond(bleDevice)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
}
|
||||
|
||||
data class Payload(
|
||||
val payload: String,
|
||||
val ts: String = LocalDateTime.now()
|
||||
.format(DateTimeFormatter.ofPattern("dd.MM.yy HH:mm:ss"))
|
||||
)
|
||||
|
||||
fun bytesToUInt16(arr: ByteArray, start: Int): Int {
|
||||
return ByteBuffer.wrap(arr, start, 2)
|
||||
.order(ByteOrder.LITTLE_ENDIAN).short.toInt() and 0xFFFF
|
||||
}
|
||||
|
||||
fun bytesToInt16(arr: ByteArray, start: Int): Short {
|
||||
return ByteBuffer.wrap(arr, start, 2)
|
||||
.order(ByteOrder.LITTLE_ENDIAN).short
|
||||
}
|
||||
|
||||
fun bytesToInt32(arr: ByteArray, start: Int): Int {
|
||||
return ByteBuffer.wrap(arr, start, 4)
|
||||
.order(ByteOrder.LITTLE_ENDIAN).int
|
||||
}
|
||||
|
||||
|
||||
private fun payloadToMeasurements(payload: Payload): List<Measurement> {
|
||||
return listOf(object : Measurement {
|
||||
override fun getLabel(): String {
|
||||
return payload.ts.toString()
|
||||
}
|
||||
|
||||
override fun getFormattedValue(): String {
|
||||
return payload.payload
|
||||
}
|
||||
|
||||
override fun getIcon(): Int? {
|
||||
return R.drawable.baseline_numbers_24
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user