blob: a0db80097b12b59c18a26d5d0ddb91ead49b8c6d [file] [log] [blame]
package com.google.chip.chiptool.clusterclient
import android.app.AlertDialog
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import chip.devicecontroller.ChipDeviceController
import chip.devicecontroller.ChipIdLookup
import chip.devicecontroller.ReportCallback
import chip.devicecontroller.ReportEventCallback
import chip.devicecontroller.ResubscriptionAttemptCallback
import chip.devicecontroller.SubscriptionEstablishedCallback
import chip.devicecontroller.model.ChipAttributePath
import chip.devicecontroller.model.ChipEventPath
import chip.devicecontroller.model.ChipPathId
import chip.devicecontroller.model.NodeState
import com.google.chip.chiptool.ChipClient
import com.google.chip.chiptool.R
import java.lang.StringBuilder
import kotlinx.android.synthetic.main.wildcard_fragment.attributeIdEd
import kotlinx.android.synthetic.main.wildcard_fragment.clusterIdEd
import kotlinx.android.synthetic.main.wildcard_fragment.endpointIdEd
import kotlinx.android.synthetic.main.wildcard_fragment.eventIdEd
import kotlinx.android.synthetic.main.wildcard_fragment.outputTv
import kotlinx.android.synthetic.main.wildcard_fragment.view.readBtn
import kotlinx.android.synthetic.main.wildcard_fragment.view.readEventBtn
import kotlinx.android.synthetic.main.wildcard_fragment.view.subscribeBtn
import kotlinx.android.synthetic.main.wildcard_fragment.view.subscribeEventBtn
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
class WildcardFragment : Fragment() {
private val deviceController: ChipDeviceController
get() = ChipClient.getDeviceController(requireContext())
private lateinit var scope: CoroutineScope
private lateinit var addressUpdateFragment: AddressUpdateFragment
private val reportCallback = object : ReportCallback {
override fun onError(attributePath: ChipAttributePath?, eventPath: ChipEventPath?, ex: Exception) {
if (attributePath != null)
{
Log.e(TAG, "Report error for $attributePath: $ex")
}
if (eventPath != null)
{
Log.e(TAG, "Report error for $eventPath: $ex")
}
}
override fun onReport(nodeState: NodeState) {
Log.i(TAG, "Received wildcard report")
val debugString = nodeStateToDebugString(nodeState)
Log.i(TAG, debugString)
requireActivity().runOnUiThread { outputTv.text = debugString }
}
override fun onDone() {
Log.i(TAG, "wildcard report Done")
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
scope = viewLifecycleOwner.lifecycleScope
return inflater.inflate(R.layout.wildcard_fragment, container, false).apply {
subscribeBtn.setOnClickListener { scope.launch { showSubscribeDialog(ATTRIBUTE) } }
readBtn.setOnClickListener { scope.launch { showReadDialog(ATTRIBUTE) } }
subscribeEventBtn.setOnClickListener { scope.launch { showSubscribeDialog(EVENT) } }
readEventBtn.setOnClickListener { scope.launch { showReadDialog(EVENT) } }
addressUpdateFragment =
childFragmentManager.findFragmentById(R.id.addressUpdateFragment) as AddressUpdateFragment
}
}
private fun nodeStateToDebugString(nodeState: NodeState): String {
val stringBuilder = StringBuilder()
nodeState.endpointStates.forEach { (endpointId, endpointState) ->
stringBuilder.append("Endpoint $endpointId: {\n")
endpointState.clusterStates.forEach { (clusterId, clusterState) ->
stringBuilder.append("\t${ChipIdLookup.clusterIdToName(clusterId)}Cluster: {\n")
clusterState.attributeStates.forEach { (attributeId, attributeState) ->
val attributeName = ChipIdLookup.attributeIdToName(clusterId, attributeId)
stringBuilder.append("\t\t$attributeName: ${attributeState.value}\n")
}
clusterState.eventStates.forEach { (eventId, events) ->
for (event in events)
{
stringBuilder.append("\t\teventNumber: ${event.eventNumber}\n")
stringBuilder.append("\t\tpriorityLevel: ${event.priorityLevel}\n")
stringBuilder.append("\t\tsystemTimeStamp: ${event.systemTimeStamp}\n")
val eventName = ChipIdLookup.eventIdToName(clusterId, eventId)
stringBuilder.append("\t\t$eventName: ${event.value}\n")
}
}
stringBuilder.append("\t}\n")
}
stringBuilder.append("}\n")
}
return stringBuilder.toString()
}
private suspend fun subscribe(type: Int, minInterval: Int, maxInterval: Int, keepSubscriptions: Boolean, isFabricFiltered: Boolean) {
val subscriptionEstablishedCallback =
SubscriptionEstablishedCallback { Log.i(TAG, "Subscription to device established") }
val resubscriptionAttemptCallback =
ResubscriptionAttemptCallback { terminationCause, nextResubscribeIntervalMsec
-> Log.i(TAG, "ResubscriptionAttempt terminationCause:$terminationCause, nextResubscribeIntervalMsec:$nextResubscribeIntervalMsec") }
val endpointId = getChipPathIdForText(endpointIdEd.text.toString())
val clusterId = getChipPathIdForText(clusterIdEd.text.toString())
val attributeId = getChipPathIdForText(attributeIdEd.text.toString())
val eventId = getChipPathIdForText(eventIdEd.text.toString())
if (type == ATTRIBUTE) {
val attributePath = ChipAttributePath.newInstance(endpointId, clusterId, attributeId)
deviceController.subscribeToPath(subscriptionEstablishedCallback,
resubscriptionAttemptCallback,
reportCallback,
ChipClient.getConnectedDevicePointer(requireContext(),
addressUpdateFragment.deviceId),
listOf(attributePath),
null,
minInterval,
maxInterval,
keepSubscriptions,
isFabricFiltered)
} else if (type == EVENT) {
val eventPath = ChipEventPath.newInstance(endpointId, clusterId, eventId)
deviceController.subscribeToPath(subscriptionEstablishedCallback,
resubscriptionAttemptCallback,
reportCallback,
ChipClient.getConnectedDevicePointer(requireContext(),
addressUpdateFragment.deviceId),
null,
listOf(eventPath),
minInterval,
maxInterval,
keepSubscriptions,
isFabricFiltered)
}
}
private suspend fun read(type: Int, isFabricFiltered: Boolean) {
val endpointId = getChipPathIdForText(endpointIdEd.text.toString())
val clusterId = getChipPathIdForText(clusterIdEd.text.toString())
val attributeId = getChipPathIdForText(attributeIdEd.text.toString())
val eventId = getChipPathIdForText(eventIdEd.text.toString())
if (type == ATTRIBUTE) {
val attributePath = ChipAttributePath.newInstance(endpointId, clusterId, attributeId)
deviceController.readPath(reportCallback,
ChipClient.getConnectedDevicePointer(requireContext(),
addressUpdateFragment.deviceId),
listOf(attributePath),
null,
isFabricFiltered)
} else if (type == EVENT) {
val eventPath = ChipEventPath.newInstance(endpointId, clusterId, eventId)
deviceController.readPath(reportCallback,
ChipClient.getConnectedDevicePointer(requireContext(),
addressUpdateFragment.deviceId),
null,
listOf(eventPath),
isFabricFiltered)
}
}
private fun showReadDialog(type: Int) {
val dialogView = requireActivity().layoutInflater.inflate(R.layout.read_dialog, null)
val dialog = AlertDialog.Builder(requireContext()).apply {
setView(dialogView)
}.create()
val isFabricFilteredEd = dialogView.findViewById<EditText>(R.id.isFabricFilteredEd)
dialogView.findViewById<Button>(R.id.readBtn).setOnClickListener {
scope.launch {
read(type, isFabricFilteredEd.text.toString().toBoolean())
requireActivity().runOnUiThread { dialog.dismiss() }
}
}
dialog.show()
}
private fun showSubscribeDialog(type: Int) {
val dialogView = requireActivity().layoutInflater.inflate(R.layout.subscribe_dialog, null)
val dialog = AlertDialog.Builder(requireContext()).apply {
setView(dialogView)
}.create()
val minIntervalEd = dialogView.findViewById<EditText>(R.id.minIntervalEd)
val maxIntervalEd = dialogView.findViewById<EditText>(R.id.maxIntervalEd)
val keepSubscriptionsEd = dialogView.findViewById<EditText>(R.id.keepSubscriptionsEd)
val isFabricFilteredEd = dialogView.findViewById<EditText>(R.id.isFabricFilteredEd)
dialogView.findViewById<Button>(R.id.subscribeBtn).setOnClickListener {
scope.launch {
subscribe(type, minIntervalEd.text.toString().toInt(), maxIntervalEd.text.toString().toInt(), keepSubscriptionsEd.text.toString().toBoolean(), isFabricFilteredEd.text.toString().toBoolean())
requireActivity().runOnUiThread { dialog.dismiss() }
}
}
dialog.show()
}
private fun getChipPathIdForText(text: String): ChipPathId {
return if (text.isEmpty()) ChipPathId.forWildcard() else ChipPathId.forId(text.toLong())
}
companion object {
private const val TAG = "WildcardFragment"
private const val ATTRIBUTE = 1
private const val EVENT = 2
fun newInstance(): WildcardFragment = WildcardFragment()
}
}