blob: 5378f65da9d44e6920329ebf0fd415cb6044e840 [file] [log] [blame]
package com.google.chip.chiptool.clusterclient
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.widget.ArrayAdapter
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import chip.devicecontroller.ChipDeviceController
import chip.devicecontroller.ClusterIDMapping.*
import chip.devicecontroller.ReportCallback
import chip.devicecontroller.WriteAttributesCallback
import chip.devicecontroller.model.AttributeWriteRequest
import chip.devicecontroller.model.ChipAttributePath
import chip.devicecontroller.model.ChipEventPath
import chip.devicecontroller.model.NodeState
import chip.devicecontroller.model.Status
import com.google.chip.chiptool.ChipClient
import com.google.chip.chiptool.GenericChipDeviceListener
import com.google.chip.chiptool.R
import com.google.chip.chiptool.databinding.BasicClientFragmentBinding
import com.google.chip.chiptool.util.toAny
import java.util.Optional
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import matter.tlv.AnonymousTag
import matter.tlv.TlvReader
import matter.tlv.TlvWriter
class BasicClientFragment : Fragment() {
private val deviceController: ChipDeviceController
get() = ChipClient.getDeviceController(requireContext())
private lateinit var scope: CoroutineScope
private lateinit var addressUpdateFragment: AddressUpdateFragment
private var _binding: BasicClientFragmentBinding? = null
private val binding
get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = BasicClientFragmentBinding.inflate(inflater, container, false)
scope = viewLifecycleOwner.lifecycleScope
deviceController.setCompletionListener(ChipControllerCallback())
addressUpdateFragment =
childFragmentManager.findFragmentById(R.id.addressUpdateFragment) as AddressUpdateFragment
binding.writeNodeLabelBtn.setOnClickListener {
scope.launch {
// TODO : Need to be implement poj-to-tlv
sendWriteAttribute(
BasicInformation.Attribute.NodeLabel,
TlvWriter().put(AnonymousTag, binding.nodeLabelEd.text.toString()).getEncoded()
)
binding.nodeLabelEd.onEditorAction(EditorInfo.IME_ACTION_DONE)
}
}
binding.writeLocationBtn.setOnClickListener {
scope.launch {
// TODO : Need to be implement poj-to-tlv
sendWriteAttribute(
BasicInformation.Attribute.Location,
TlvWriter().put(AnonymousTag, binding.locationEd.text.toString()).getEncoded()
)
binding.locationEd.onEditorAction(EditorInfo.IME_ACTION_DONE)
}
}
binding.writeLocalConfigDisabledSwitch.setOnCheckedChangeListener { _, isChecked ->
scope.launch {
// TODO : Need to be implement poj-to-tlv
sendWriteAttribute(
BasicInformation.Attribute.LocalConfigDisabled,
TlvWriter().put(AnonymousTag, isChecked).getEncoded()
)
}
}
makeAttributeList()
binding.attributeNameSpinner.adapter = makeAttributeNamesAdapter()
binding.readAttributeBtn.setOnClickListener { scope.launch { readAttributeButtonClick() } }
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
inner class ChipControllerCallback : GenericChipDeviceListener() {
override fun onConnectDeviceComplete() {}
override fun onCommissioningComplete(nodeId: Long, errorCode: Long) {
Log.d(TAG, "onCommissioningComplete for nodeId $nodeId: $errorCode")
}
override fun onNotifyChipConnectionClosed() {
Log.d(TAG, "onNotifyChipConnectionClosed")
}
override fun onCloseBleComplete() {
Log.d(TAG, "onCloseBleComplete")
}
override fun onError(error: Throwable?) {
Log.d(TAG, "onError: $error")
}
}
private fun makeAttributeNamesAdapter(): ArrayAdapter<String> {
return ArrayAdapter(
requireContext(),
android.R.layout.simple_spinner_dropdown_item,
ATTRIBUTES.toList()
)
.apply { setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) }
}
private suspend fun readAttributeButtonClick() {
try {
readBasicClusters(binding.attributeNameSpinner.selectedItemPosition)
} catch (ex: Exception) {
showMessage("readBasicCluster failed: $ex")
}
}
private suspend fun readBasicClusters(itemIndex: Int) {
val endpointId = addressUpdateFragment.endpointId
val clusterId = BasicInformation.ID
val attributeName = ATTRIBUTES[itemIndex]
val attributeId = BasicInformation.Attribute.valueOf(attributeName).id
val devicePtr =
try {
ChipClient.getConnectedDevicePointer(requireContext(), addressUpdateFragment.deviceId)
} catch (e: IllegalStateException) {
Log.d(TAG, "getConnectedDevicePointer exception", e)
showMessage("Get DevicePointer fail!")
return
}
ChipClient.getDeviceController(requireContext())
.readPath(
object : ReportCallback {
override fun onError(
attributePath: ChipAttributePath?,
eventPath: ChipEventPath?,
ex: java.lang.Exception
) {
showMessage("Read $attributeName failure $ex")
Log.e(TAG, "Read $attributeName failure", ex)
}
override fun onReport(nodeState: NodeState?) {
val tlv =
nodeState
?.getEndpointState(endpointId)
?.getClusterState(clusterId)
?.getAttributeState(attributeId)
?.tlv
val value = tlv?.let { TlvReader(it).toAny() }
Log.i(TAG, "[Read Success] $attributeName: $value")
showMessage("[Read Success] $attributeName: $value")
}
},
devicePtr,
listOf(ChipAttributePath.newInstance(endpointId, clusterId, attributeId)),
null,
false,
0 /* imTimeoutMs */
)
}
private fun makeAttributeList() {
for (attribute in BasicInformation.Attribute.values()) {
ATTRIBUTES.add(attribute.name)
}
}
private suspend fun sendWriteAttribute(attribute: BasicInformation.Attribute, tlv: ByteArray) {
val clusterId = BasicInformation.ID
val devicePtr =
try {
ChipClient.getConnectedDevicePointer(requireContext(), addressUpdateFragment.deviceId)
} catch (e: IllegalStateException) {
Log.d(TAG, "getConnectedDevicePointer exception", e)
showMessage("Get DevicePointer fail!")
return
}
ChipClient.getDeviceController(requireContext())
.write(
object : WriteAttributesCallback {
override fun onError(attributePath: ChipAttributePath?, ex: java.lang.Exception?) {
showMessage("Write ${attribute.name} failure $ex")
Log.e(TAG, "Write ${attribute.name} failure", ex)
}
override fun onResponse(attributePath: ChipAttributePath, status: Status) {
showMessage("Write ${attribute.name} response: $status")
}
},
devicePtr,
listOf(
AttributeWriteRequest.newInstance(
addressUpdateFragment.endpointId,
clusterId,
attribute.id,
tlv,
Optional.empty()
)
),
0,
0
)
}
private fun showMessage(msg: String) {
requireActivity().runOnUiThread { binding.basicClusterCommandStatus.text = msg }
}
override fun onResume() {
super.onResume()
addressUpdateFragment.endpointId = ENDPOINT
}
companion object {
private const val TAG = "BasicClientFragment"
private const val ENDPOINT = 0
private val ATTRIBUTES: MutableList<String> = mutableListOf()
fun newInstance(): BasicClientFragment = BasicClientFragment()
}
}