Commit 376a76ae authored by Anon's avatar Anon
Browse files

Merge branch 'single-connection' into 'master'

Share one connection

See merge request card10/companion-app-android!19
parents 3624ec85 9d7e21ee
...@@ -37,6 +37,10 @@ val SINGLE_LED_CHARACTERISTIC_UUID = UUID.fromString("42230211-2342-2342-2342-23 ...@@ -37,6 +37,10 @@ val SINGLE_LED_CHARACTERISTIC_UUID = UUID.fromString("42230211-2342-2342-2342-23
val LIGHT_SENSOR_CHARACTERISTIC_UUID = UUID.fromString("422302f0-2342-2342-2342-234223422342") val LIGHT_SENSOR_CHARACTERISTIC_UUID = UUID.fromString("422302f0-2342-2342-2342-234223422342")
val TIME_CHARACTERISTIC_UUID = UUID.fromString("42230201-2342-2342-2342-234223422342") val TIME_CHARACTERISTIC_UUID = UUID.fromString("42230201-2342-2342-2342-234223422342")
val FILE_SERVICE_UUID = UUID.fromString("42230100-2342-2342-2342-234223422342")
val FILE_TX_UUID = UUID.fromString("42230101-2342-2342-2342-234223422342")
val FILE_RX_UUID = UUID.fromString("42230102-2342-2342-2342-234223422342")
const val UPDATE_CLOCK_FREQUENCY_MINS = 5 const val UPDATE_CLOCK_FREQUENCY_MINS = 5
const val HATCHERY_BASE_URL = "https://badge.team" const val HATCHERY_BASE_URL = "https://badge.team"
...@@ -27,6 +27,7 @@ import android.content.Context ...@@ -27,6 +27,7 @@ import android.content.Context
import android.util.Log import android.util.Log
import de.ccc.events.badge.card10.CARD10_BLUETOOTH_MAC_PREFIX import de.ccc.events.badge.card10.CARD10_BLUETOOTH_MAC_PREFIX
import de.ccc.events.badge.card10.CARD10_SERVICE_UUID import de.ccc.events.badge.card10.CARD10_SERVICE_UUID
import de.ccc.events.badge.card10.FILE_SERVICE_UUID
import de.ccc.events.badge.card10.R import de.ccc.events.badge.card10.R
import de.ccc.events.badge.card10.filetransfer.LowEffortService import de.ccc.events.badge.card10.filetransfer.LowEffortService
import de.ccc.events.badge.card10.time.Card10Service import de.ccc.events.badge.card10.time.Card10Service
...@@ -47,8 +48,6 @@ object ConnectionService { ...@@ -47,8 +48,6 @@ object ConnectionService {
private var connectionState = BluetoothGatt.STATE_DISCONNECTED private var connectionState = BluetoothGatt.STATE_DISCONNECTED
private var gattListeners = mutableMapOf<String, GattListener>() private var gattListeners = mutableMapOf<String, GattListener>()
private val fileServiceUuid = UUID.fromString("42230100-2342-2342-2342-234223422342")
val deviceName: String? val deviceName: String?
get() = device?.name get() = device?.name
...@@ -99,7 +98,7 @@ object ConnectionService { ...@@ -99,7 +98,7 @@ object ConnectionService {
for (service in gatt.services) { for (service in gatt.services) {
Log.d(TAG, "Found service: ${service.uuid}") Log.d(TAG, "Found service: ${service.uuid}")
if (service.uuid == fileServiceUuid) { if (service.uuid == FILE_SERVICE_UUID) {
leService = LowEffortService(service) leService = LowEffortService(service)
} else if (service.uuid == CARD10_SERVICE_UUID) { } else if (service.uuid == CARD10_SERVICE_UUID) {
card10Service = Card10Service(service) card10Service = Card10Service(service)
......
...@@ -57,8 +57,7 @@ class BatchTransferFragment : Fragment(), FileTransferListener, GattListener { ...@@ -57,8 +57,7 @@ class BatchTransferFragment : Fragment(), FileTransferListener, GattListener {
inflater.inflate(R.layout.batch_transfer_fragment, container, false) inflater.inflate(R.layout.batch_transfer_fragment, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
label_status.text = getString(R.string.batch_transfer_label_initializing) progress.max = queue.size
progress.max = 5
button_cancel.setOnClickListener { button_cancel.setOnClickListener {
isCancelled = true isCancelled = true
...@@ -72,13 +71,7 @@ class BatchTransferFragment : Fragment(), FileTransferListener, GattListener { ...@@ -72,13 +71,7 @@ class BatchTransferFragment : Fragment(), FileTransferListener, GattListener {
.commit() .commit()
} }
initConnection() startTransfer()
}
private fun initConnection() {
val ctx = context ?: throw IllegalStateException()
ConnectionService.addGattListener("batchfiletransfer", this)
ConnectionService.connect(ctx)
} }
private fun startTransfer() { private fun startTransfer() {
...@@ -123,10 +116,6 @@ class BatchTransferFragment : Fragment(), FileTransferListener, GattListener { ...@@ -123,10 +116,6 @@ class BatchTransferFragment : Fragment(), FileTransferListener, GattListener {
} }
} }
override fun onConnectionReady() {
startTransfer()
}
override fun onError() { override fun onError() {
activity?.runOnUiThread { activity?.runOnUiThread {
label_status.text = getString(R.string.batch_transfer_label_error) label_status.text = getString(R.string.batch_transfer_label_error)
......
...@@ -80,19 +80,12 @@ class FileTransferFragment : Fragment(), GattListener, FileTransferListener{ ...@@ -80,19 +80,12 @@ class FileTransferFragment : Fragment(), GattListener, FileTransferListener{
buttonStartStop = view.findViewById(R.id.button_start_stop_transfer) buttonStartStop = view.findViewById(R.id.button_start_stop_transfer)
try { try {
initConnection()
toggleControls() toggleControls()
} catch (e: ConnectionException) { } catch (e: ConnectionException) {
showError(e.message) showError(e.message)
} catch (e: Exception) { } catch (e: Exception) {
showError(getString(R.string.connection_error_generic)) showError(getString(R.string.connection_error_generic))
} }
}
private fun initConnection() {
val ctx = context ?: throw IllegalStateException()
ConnectionService.connect(ctx)
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
......
...@@ -26,6 +26,8 @@ import android.bluetooth.BluetoothGatt ...@@ -26,6 +26,8 @@ import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCharacteristic import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattService import android.bluetooth.BluetoothGattService
import android.util.Log import android.util.Log
import de.ccc.events.badge.card10.FILE_RX_UUID
import de.ccc.events.badge.card10.FILE_TX_UUID
import de.ccc.events.badge.card10.common.ConnectionService import de.ccc.events.badge.card10.common.ConnectionService
import de.ccc.events.badge.card10.common.GattListener import de.ccc.events.badge.card10.common.GattListener
import de.ccc.events.badge.card10.filetransfer.protocol.Packet import de.ccc.events.badge.card10.filetransfer.protocol.Packet
...@@ -39,15 +41,12 @@ class LowEffortService( ...@@ -39,15 +41,12 @@ class LowEffortService(
private val centralTx: BluetoothGattCharacteristic private val centralTx: BluetoothGattCharacteristic
private val centralRx: BluetoothGattCharacteristic private val centralRx: BluetoothGattCharacteristic
private val centralTxCharacteristicUuid = UUID.fromString("42230101-2342-2342-2342-234223422342")
private val centralRxCharacteristicUuid = UUID.fromString("42230102-2342-2342-2342-234223422342")
private var notifyEnabled = false private var notifyEnabled = false
private var listener: OnPacketReceivedListener? = null private var listener: OnPacketReceivedListener? = null
init { init {
val tx = service.getCharacteristic(centralTxCharacteristicUuid) val tx = service.getCharacteristic(FILE_TX_UUID)
val rx = service.getCharacteristic(centralRxCharacteristicUuid) val rx = service.getCharacteristic(FILE_RX_UUID)
if (tx == null || rx == null) { if (tx == null || rx == null) {
throw IllegalStateException() throw IllegalStateException()
......
...@@ -28,12 +28,14 @@ import android.view.LayoutInflater ...@@ -28,12 +28,14 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Button import android.widget.Button
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import de.ccc.events.badge.card10.CARD10_BLUETOOTH_MAC_PREFIX import de.ccc.events.badge.card10.CARD10_BLUETOOTH_MAC_PREFIX
import de.ccc.events.badge.card10.R import de.ccc.events.badge.card10.R
import de.ccc.events.badge.card10.common.ConnectionService import de.ccc.events.badge.card10.common.ConnectionService
import de.ccc.events.badge.card10.common.GattListener
import de.ccc.events.badge.card10.filetransfer.FileTransferFragment import de.ccc.events.badge.card10.filetransfer.FileTransferFragment
import de.ccc.events.badge.card10.hatchery.AppListFragment import de.ccc.events.badge.card10.hatchery.AppListFragment
import de.ccc.events.badge.card10.mood.MoodFragment import de.ccc.events.badge.card10.mood.MoodFragment
...@@ -41,8 +43,10 @@ import de.ccc.events.badge.card10.scanner.ScannerFragment ...@@ -41,8 +43,10 @@ import de.ccc.events.badge.card10.scanner.ScannerFragment
import de.ccc.events.badge.card10.sparkle.BeautifulFragment import de.ccc.events.badge.card10.sparkle.BeautifulFragment
import de.ccc.events.badge.card10.time.TimeUpdateDialog import de.ccc.events.badge.card10.time.TimeUpdateDialog
import kotlinx.android.synthetic.main.main_fragment.* import kotlinx.android.synthetic.main.main_fragment.*
import java.lang.IllegalStateException
import java.sql.Connection
class MainFragment : Fragment() { class MainFragment : Fragment(), GattListener {
private val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter() private val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) = override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) =
...@@ -62,16 +66,18 @@ class MainFragment : Fragment() { ...@@ -62,16 +66,18 @@ class MainFragment : Fragment() {
dialogFragment.dismiss() dialogFragment.dismiss()
} }
ConnectionService.addGattListener("main", this)
val bondedCard10s = val bondedCard10s =
bluetoothAdapter.bondedDevices.filter { it.address.startsWith(CARD10_BLUETOOTH_MAC_PREFIX, true) } bluetoothAdapter.bondedDevices.filter { it.address.startsWith(CARD10_BLUETOOTH_MAC_PREFIX, true) }
if (bondedCard10s.isNotEmpty()) { if (bondedCard10s.isNotEmpty()) {
val device = bondedCard10s[0] val ctx = activity ?: throw IllegalStateException()
label_status.text = getString(R.string.main_label_paired, device.name, device.address) ConnectionService.connect(ctx)
showConnectedView(view) showConnectingView()
} else { } else {
label_status.text = getString(R.string.main_label_not_connected) label_status.text = getString(R.string.main_label_not_connected)
showDisconnectedView(view) showDisconnectedView()
} }
} }
...@@ -82,14 +88,37 @@ class MainFragment : Fragment() { ...@@ -82,14 +88,37 @@ class MainFragment : Fragment() {
.commit() .commit()
} }
private fun showConnectedView(view: View) { private fun showConnectedView() {
view.findViewById<ConstraintLayout>(R.id.container_connected).visibility = View.VISIBLE activity?.runOnUiThread {
view.findViewById<ConstraintLayout>(R.id.container_disconnected).visibility = View.GONE container_connected.visibility = View.VISIBLE
view.findViewById<Button>(R.id.button_pair).text = getString(R.string.main_button_manage_pairings) container_disconnected.visibility = View.GONE
button_pair.text = getString(R.string.main_button_manage_pairings)
button_hatchery.isEnabled = true
button_send.isEnabled = true
button_mood.isEnabled = true
button_beautiful.isEnabled = true
button_set_time.isEnabled = true
val device = ConnectionService.device
label_status.text = getString(R.string.main_label_connected, device?.name, device?.address)
}
}
private fun showConnectingView() {
val device = ConnectionService.device
label_status.text = getString(R.string.main_label_connecting, device?.name, device?.address)
button_pair.text = getString(R.string.main_button_manage_pairings)
}
private fun showDisconnectedView() {
container_connected.visibility = View.GONE
container_disconnected.visibility = View.VISIBLE
button_pair.text = getString(R.string.main_button_pair)
} }
private fun showDisconnectedView(view: View) { override fun onConnectionReady() {
view.findViewById<ConstraintLayout>(R.id.container_connected).visibility = View.GONE showConnectedView()
view.findViewById<ConstraintLayout>(R.id.container_disconnected).visibility = View.VISIBLE
} }
} }
...@@ -22,60 +22,23 @@ ...@@ -22,60 +22,23 @@
package de.ccc.events.badge.card10.mood package de.ccc.events.badge.card10.mood
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCallback
import android.bluetooth.BluetoothGattCharacteristic
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import de.ccc.events.badge.card10.CARD10_BLUETOOTH_MAC_PREFIX
import de.ccc.events.badge.card10.CARD10_SERVICE_UUID
import de.ccc.events.badge.card10.R import de.ccc.events.badge.card10.R
import de.ccc.events.badge.card10.ROCKETS_CHARACTERISTIC_UUID import de.ccc.events.badge.card10.common.ConnectionService
import de.ccc.events.badge.card10.time.Card10Service
import kotlinx.android.synthetic.main.mood_fragment.* import kotlinx.android.synthetic.main.mood_fragment.*
import java.util.concurrent.CountDownLatch
class MoodFragment : Fragment() { class MoodFragment : Fragment() {
private var card10Service: Card10Service? = null
private val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
private lateinit var gatt: BluetoothGatt
private var rocketsCharacteristic: BluetoothGattCharacteristic? = null
private var writeLatch: CountDownLatch? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val callback = object : BluetoothGattCallback() { card10Service = ConnectionService.card10Service
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
System.out.println("===== onConnectionStateChange " + gatt + " / " + status + " / " + newState)
if (newState == BluetoothGatt.STATE_CONNECTED)
gatt.discoverServices()
}
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
System.out.println("===== onServicesDiscovered " + gatt + " / " + status)
val card10Service = gatt.getService(CARD10_SERVICE_UUID)
rocketsCharacteristic = card10Service.getCharacteristic(ROCKETS_CHARACTERISTIC_UUID)
}
override fun onCharacteristicWrite(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
status: Int
) {
System.out.println("===== onCharacteristicWrite " + characteristic.uuid.toString() + " / " + characteristic.value + " / " + status)
writeLatch?.countDown();
}
}
val remoteDevices =
bluetoothAdapter.bondedDevices.filter { it.address.startsWith(CARD10_BLUETOOTH_MAC_PREFIX, true) }
if (remoteDevices.isEmpty())
activity!!.finish()
gatt = remoteDevices.get(0).connectGatt(activity, false, callback)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
...@@ -83,28 +46,14 @@ class MoodFragment : Fragment() { ...@@ -83,28 +46,14 @@ class MoodFragment : Fragment() {
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
mood_good.setOnClickListener({ mood_good.setOnClickListener{
writeGatt(rocketsCharacteristic!!, ubyteArrayOf(0xffu, 0x00u, 0x00u).toByteArray()) card10Service?.setRocketValue(ubyteArrayOf(0xffu, 0x00u, 0x00u).toByteArray())
})
mood_neutral.setOnClickListener({
writeGatt(rocketsCharacteristic!!, ubyteArrayOf(0x00u, 0xffu, 0x00u).toByteArray())
})
mood_bad.setOnClickListener({
writeGatt(rocketsCharacteristic!!, ubyteArrayOf(0x00u, 0x00u, 0xffu).toByteArray())
})
} }
mood_neutral.setOnClickListener{
fun writeGatt(characteristic: BluetoothGattCharacteristic, values: ByteArray) { card10Service?.setRocketValue(ubyteArrayOf(0x00u, 0xffu, 0x00u).toByteArray())
characteristic.value = values }
val init = gatt.writeCharacteristic(rocketsCharacteristic) mood_bad.setOnClickListener{
if (!init) card10Service?.setRocketValue(ubyteArrayOf(0x00u, 0x00u, 0xffu).toByteArray())
System.out.println("Failed to initiate writing GATT attribute")
writeLatch = CountDownLatch(1)
writeLatch!!.await();
} }
override fun onDestroy() {
gatt.close();
super.onDestroy()
} }
} }
...@@ -22,84 +22,40 @@ ...@@ -22,84 +22,40 @@
package de.ccc.events.badge.card10.sparkle package de.ccc.events.badge.card10.sparkle
import android.bluetooth.*
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import de.ccc.events.badge.card10.CARD10_BLUETOOTH_MAC_PREFIX
import de.ccc.events.badge.card10.CARD10_SERVICE_UUID
import de.ccc.events.badge.card10.LEDS_ABOVE_CHARACTERISTIC_UUID
import de.ccc.events.badge.card10.R import de.ccc.events.badge.card10.R
import java.util.concurrent.CountDownLatch import de.ccc.events.badge.card10.common.ConnectionService
import de.ccc.events.badge.card10.time.Card10Service
import kotlin.random.Random import kotlin.random.Random
class BeautifulFragment : Fragment(), Runnable { class BeautifulFragment : Fragment(), Runnable {
private val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
private lateinit var gatt: BluetoothGatt
private var ledsAboveCharacteristic: BluetoothGattCharacteristic? = null
private @Volatile var writeLatch: CountDownLatch? = null
private val handler = Handler() private val handler = Handler()
private var card10Service: Card10Service? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val callback = object : BluetoothGattCallback() { card10Service = ConnectionService.card10Service
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
println("===== onConnectionStateChange ${gatt} ${if (status == BluetoothGatt.GATT_SUCCESS) "SUCCESS" else status} ${if (newState == BluetoothProfile.STATE_CONNECTED) "CONNECTED" else if (newState == BluetoothProfile.STATE_DISCONNECTED) "DISCONNECTED" else newState}")
if (newState == BluetoothGatt.STATE_CONNECTED)
gatt.requestMtu(64)
}
override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
println("===== onMtuChanged ${gatt} ${if (status == BluetoothGatt.GATT_SUCCESS) "SUCCESS" else status} ${mtu}")
gatt.discoverServices()
}
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
println("===== onServicesDiscovered ${gatt} ${if (status == BluetoothGatt.GATT_SUCCESS) "SUCCESS" else status}")
val card10Service = gatt.getService(CARD10_SERVICE_UUID)
ledsAboveCharacteristic = card10Service.getCharacteristic(LEDS_ABOVE_CHARACTERISTIC_UUID)
handler.post(this@BeautifulFragment) handler.post(this@BeautifulFragment)
} }
override fun onCharacteristicWrite(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
status: Int
) {
println("=== onCharacteristicWrite ${gatt} ${if (status == BluetoothGatt.GATT_SUCCESS) "SUCCESS" else status} ${characteristic.uuid} ${characteristic.value}")
writeLatch?.countDown();
}
}
val remoteDevices =
bluetoothAdapter.bondedDevices.filter { it.address.startsWith(CARD10_BLUETOOTH_MAC_PREFIX, true) }
if (remoteDevices.isEmpty())
activity!!.finish()
gatt = remoteDevices.get(0).connectGatt(activity, false, callback)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.beautiful_fragment, container, false) return inflater.inflate(R.layout.beautiful_fragment, container, false)
} }
override fun run() { override fun run() {
ledsAboveCharacteristic!!.value = Random.nextBytes(33); card10Service?.setLeds(Random.nextBytes(33))
writeLatch = CountDownLatch(1)
val init = gatt.writeCharacteristic(ledsAboveCharacteristic)
if (!init)
println("=== Failed to initiate writing GATT attribute")
writeLatch!!.await();
handler.postDelayed(this, 100) handler.postDelayed(this, 100)
} }
override fun onDestroy() { override fun onDestroy() {
handler.removeCallbacksAndMessages(null) handler.removeCallbacksAndMessages(null)
gatt.close();
super.onDestroy() super.onDestroy()
} }
} }
...@@ -24,6 +24,8 @@ package de.ccc.events.badge.card10.time ...@@ -24,6 +24,8 @@ package de.ccc.events.badge.card10.time
import android.bluetooth.BluetoothGattCharacteristic import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattService import android.bluetooth.BluetoothGattService
import de.ccc.events.badge.card10.LEDS_ABOVE_CHARACTERISTIC_UUID
import de.ccc.events.badge.card10.ROCKETS_CHARACTERISTIC_UUID
import de.ccc.events.badge.card10.TIME_CHARACTERISTIC_UUID import de.ccc.events.badge.card10.TIME_CHARACTERISTIC_UUID
import de.ccc.events.badge.card10.common.ConnectionService import de.ccc.events.badge.card10.common.ConnectionService
import java.nio.ByteBuffer import java.nio.ByteBuffer
...@@ -31,11 +33,14 @@ import java.nio.ByteBuffer ...@@ -31,11 +33,14 @@ import java.nio.ByteBuffer
class Card10Service( class Card10Service(
service: BluetoothGattService service: BluetoothGattService
) { ) {
private val timeCharacteristic: BluetoothGattCharacteristic private val timeCharacteristic = service.getCharacteristic(TIME_CHARACTERISTIC_UUID)
private val rocketsCharacteristic = service.getCharacteristic(ROCKETS_CHARACTERISTIC_UUID)
private var ledsAboveCharacteristic = service.getCharacteristic(LEDS_ABOVE_CHARACTERISTIC_UUID)
init { init {
timeCharacteristic = service.getCharacteristic(TIME_CHARACTERISTIC_UUID)
timeCharacteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE timeCharacteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
rocketsCharacteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
ledsAboveCharacteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
} }