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

[Android] Implement using generic IM(invoke,read,write,subscribe) APIs #25956

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import chip.devicecontroller.ChipDeviceController
import chip.devicecontroller.ChipIdLookup
import com.google.chip.chiptool.ChipClient
import com.google.chip.chiptool.R
import com.google.chip.chiptool.databinding.AddressUpdateFragmentBinding
import com.google.chip.chiptool.util.DeviceIdUtil

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import chip.devicecontroller.ChipClusters
import chip.devicecontroller.ChipDeviceController
import chip.devicecontroller.ClusterIDMapping
import chip.devicecontroller.InvokeCallback
import chip.devicecontroller.OpenCommissioningCallback
import chip.devicecontroller.model.InvokeElement
import chip.tlv.AnonymousTag
import chip.tlv.TlvWriter
import com.google.chip.chiptool.ChipClient
import com.google.chip.chiptool.GenericChipDeviceListener
import com.google.chip.chiptool.R
Expand Down Expand Up @@ -129,22 +133,31 @@ class MultiAdminClientFragment : Fragment() {

private suspend fun sendRevokeCommandClick() {
val timedInvokeTimeout = 10000
getAdministratorCommissioningClusterForDevice().revokeCommissioning(object : ChipClusters.DefaultClusterCallback {
override fun onSuccess() {
showMessage("Revoke Commissioning success")
}

override fun onError(ex: Exception) {
// TODO : Need to be implement poj-to-tlv
val tlvWriter = TlvWriter()
tlvWriter.startStructure(AnonymousTag)
tlvWriter.endStructure()
val invokeElement = InvokeElement.newInstance(0
, ClusterIDMapping.AdministratorCommissioning.ID
, ClusterIDMapping.AdministratorCommissioning.Command.RevokeCommissioning.id
, tlvWriter.getEncoded(), null)

deviceController.invoke(object: InvokeCallback {
override fun onError(ex: Exception?) {
showMessage("Revoke Commissioning failure $ex")
Log.e(TAG, "Revoke Commissioning failure", ex)
}
}, timedInvokeTimeout)

override fun onResponse(invokeElement: InvokeElement?, successCode: Long) {
Log.e(TAG, "onResponse : $invokeElement, Code : $successCode")
showMessage("Revoke Commissioning success")
}

}, getConnectedDevicePointer(), invokeElement, timedInvokeTimeout, 0)
}

private suspend fun getAdministratorCommissioningClusterForDevice(): ChipClusters.AdministratorCommissioningCluster {
return ChipClusters.AdministratorCommissioningCluster(
ChipClient.getConnectedDevicePointer(requireContext(), addressUpdateFragment.deviceId), 0
)
private suspend fun getConnectedDevicePointer(): Long {
return ChipClient.getConnectedDevicePointer(requireContext(), addressUpdateFragment.deviceId)
}

private fun showMessage(msg: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,31 @@ import android.widget.SeekBar
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import chip.devicecontroller.ChipClusters
import chip.devicecontroller.ChipClusters.OnOffCluster
import chip.devicecontroller.ChipDeviceController
import chip.devicecontroller.InvokeCallback
import chip.devicecontroller.ReportCallback
import chip.devicecontroller.ResubscriptionAttemptCallback
import chip.devicecontroller.SubscriptionEstablishedCallback
import chip.devicecontroller.model.ChipAttributePath
import chip.devicecontroller.model.ChipEventPath
import chip.devicecontroller.model.InvokeElement
import chip.devicecontroller.model.NodeState
import chip.tlv.TlvWriter
import com.google.chip.chiptool.ChipClient
import com.google.chip.chiptool.GenericChipDeviceListener
import com.google.chip.chiptool.R
import com.google.chip.chiptool.databinding.OnOffClientFragmentBinding
import com.google.chip.chiptool.util.TlvParseUtil
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

import chip.devicecontroller.ClusterIDMapping.*
import chip.tlv.AnonymousTag
import chip.tlv.ContextSpecificTag

class OnOffClientFragment : Fragment() {
private val deviceController: ChipDeviceController
get() = ChipClient.getDeviceController(requireContext())
Expand All @@ -49,9 +61,9 @@ class OnOffClientFragment : Fragment() {
addressUpdateFragment =
childFragmentManager.findFragmentById(R.id.addressUpdateFragment) as AddressUpdateFragment

binding.onBtn.setOnClickListener { scope.launch { sendOnCommandClick() } }
binding.offBtn.setOnClickListener { scope.launch { sendOffCommandClick() } }
binding.toggleBtn.setOnClickListener { scope.launch { sendToggleCommandClick() } }
binding.onBtn.setOnClickListener { scope.launch { sendOnOffClusterCommand(OnOff.Command.On) } }
binding.offBtn.setOnClickListener { scope.launch { sendOnOffClusterCommand(OnOff.Command.Off) } }
binding.toggleBtn.setOnClickListener { scope.launch { sendOnOffClusterCommand(OnOff.Command.Toggle) } }
binding.readBtn.setOnClickListener { scope.launch { sendReadOnOffClick() } }
binding.showSubscribeDialogBtn.setOnClickListener { showSubscribeDialog() }

Expand Down Expand Up @@ -82,16 +94,24 @@ class OnOffClientFragment : Fragment() {
}

private suspend fun sendReadOnOffClick() {
getOnOffClusterForDevice().readOnOffAttribute(object : ChipClusters.BooleanAttributeCallback {
override fun onSuccess(on: Boolean) {
Log.v(TAG, "On/Off attribute value: $on")
showMessage("On/Off attribute value: $on")
}
val endpointId = addressUpdateFragment.endpointId
val clusterId = OnOff.ID
val attributeId = OnOff.Attribute.OnOff.id

override fun onError(ex: Exception) {
val attributePath = ChipAttributePath.newInstance(endpointId.toLong(), clusterId, attributeId)

ChipClient.getDeviceController(requireContext()).readPath(object: ReportCallback {
override fun onError(attributePath: ChipAttributePath?, eventPath: ChipEventPath?, ex: java.lang.Exception) {
Log.e(TAG, "Error reading onOff attribute", ex)
}
})

override fun onReport(nodeState: NodeState?) {
val value = nodeState?.getEndpointState(endpointId)?.getClusterState(clusterId)?.getAttributeState(attributeId)?.value ?: "null"
Log.v(TAG, "On/Off attribute value: $value")
showMessage("On/Off attribute value: $value")
}

}, getConnectedDevicePointer(), listOf(attributePath), null, false, 0 /* imTimeoutMs */)
}

private fun showSubscribeDialog() {
Expand All @@ -115,31 +135,53 @@ class OnOffClientFragment : Fragment() {
}
dialog.show()
}

private suspend fun sendSubscribeOnOffClick(minInterval: Int, maxInterval: Int) {
val onOffCluster = getOnOffClusterForDevice()

val subscribeCallback = object : ChipClusters.BooleanAttributeCallback {
override fun onSuccess(value: Boolean) {
val formatter = SimpleDateFormat("HH:mm:ss", Locale.getDefault())
val time = formatter.format(Calendar.getInstance(Locale.getDefault()).time)
val message = "Subscribed on/off value at $time: ${if (value) "ON" else "OFF"}"

Log.v(TAG, message)
showReportMessage(message)
}

override fun onSubscriptionEstablished(subscriptionId: Long) {
val message = "Subscription for on/off established with subscriptionId: $subscriptionId"
Log.v(TAG, message)
showMessage(message)
}

override fun onError(ex: Exception) {
Log.e(TAG, "Error configuring on/off attribute", ex)
}
}
onOffCluster.subscribeOnOffAttribute(subscribeCallback, minInterval, maxInterval)
val endpointId = addressUpdateFragment.endpointId
val clusterId = OnOff.ID
val attributeId = OnOff.Attribute.OnOff.id

val attributePath = ChipAttributePath.newInstance(endpointId.toLong(), clusterId, attributeId)

val subscriptionEstablishedCallback =
SubscriptionEstablishedCallback {
subscriptionId ->
Log.i(TAG, "Subscription to device established : ${subscriptionId.toULong()}")
requireActivity().runOnUiThread {
Toast.makeText(requireActivity(), "${getString(R.string.wildcard_subscribe_established_toast_message)} : $subscriptionId", Toast.LENGTH_SHORT).show()
}
}

val resubscriptionAttemptCallback =
ResubscriptionAttemptCallback { terminationCause, nextResubscribeIntervalMsec
-> Log.i(TAG, "ResubscriptionAttempt terminationCause:$terminationCause, nextResubscribeIntervalMsec:$nextResubscribeIntervalMsec") }

deviceController.subscribeToPath(subscriptionEstablishedCallback,
resubscriptionAttemptCallback,
object: ReportCallback {
override fun onError(attributePath: ChipAttributePath?, eventPath: ChipEventPath?, ex: Exception) {
Log.e(TAG, "Error configuring on/off attribute", ex)
}

override fun onReport(nodeState: NodeState?) {
val tlv = nodeState?.getEndpointState(endpointId)?.getClusterState(clusterId)?.getAttributeState(attributeId)?.tlv ?: return
// TODO : Need to be implement poj-to-tlv
val value = TlvParseUtil.decodeBoolean(tlv)
val formatter = SimpleDateFormat("HH:mm:ss", Locale.getDefault())
val time = formatter.format(Calendar.getInstance(Locale.getDefault()).time)
val message = "Subscribed on/off value at $time: ${if (value) "ON" else "OFF"}"

Log.v(TAG, message)
showReportMessage(message)
}
},
getConnectedDevicePointer(),
listOf(attributePath),
null,
minInterval,
maxInterval,
false,
false,
/* imTimeoutMs= */ 0)
}

inner class ChipControllerCallback : GenericChipDeviceListener() {
Expand All @@ -164,69 +206,60 @@ class OnOffClientFragment : Fragment() {
}

private suspend fun sendLevelCommandClick() {
val cluster = ChipClusters.LevelControlCluster(
ChipClient.getConnectedDevicePointer(requireContext(), addressUpdateFragment.deviceId),
addressUpdateFragment.endpointId
)
cluster.moveToLevel(object : ChipClusters.DefaultClusterCallback {
override fun onSuccess() {
showMessage("MoveToLevel command success")
}

override fun onError(ex: Exception) {
// TODO : Need to be implement poj-to-tlv
val tlvWriter = TlvWriter()
tlvWriter.startStructure(AnonymousTag)
tlvWriter.put(ContextSpecificTag(0), binding.levelBar.progress.toUInt())
tlvWriter.put(ContextSpecificTag(1), 0u)
tlvWriter.put(ContextSpecificTag(2), 0u)
tlvWriter.put(ContextSpecificTag(3), 0u)
tlvWriter.endStructure()

val invokeElement = InvokeElement.newInstance(addressUpdateFragment.endpointId.toLong()
, LevelControl.ID
, LevelControl.Command.MoveToLevel.id
, tlvWriter.getEncoded(), null)

deviceController.invoke(object: InvokeCallback {
override fun onError(ex: Exception?) {
showMessage("MoveToLevel command failure $ex")
Log.e(TAG, "MoveToLevel command failure", ex)
}

}, binding.levelBar.progress, 0, 0, 0)
}

private suspend fun sendOnCommandClick() {
getOnOffClusterForDevice().on(object : ChipClusters.DefaultClusterCallback {
override fun onSuccess() {
showMessage("ON command success")
}

override fun onError(ex: Exception) {
showMessage("ON command failure $ex")
Log.e(TAG, "ON command failure", ex)
override fun onResponse(invokeElement: InvokeElement?, successCode: Long) {
Log.e(TAG, "onResponse : $invokeElement, Code : $successCode")
showMessage("MoveToLevel command success")
}

})
}, getConnectedDevicePointer(), invokeElement, 0, 0)
}

private suspend fun sendOffCommandClick() {
getOnOffClusterForDevice().off(object : ChipClusters.DefaultClusterCallback {
override fun onSuccess() {
showMessage("OFF command success")
}

override fun onError(ex: Exception) {
showMessage("OFF command failure $ex")
Log.e(TAG, "OFF command failure", ex)
private suspend fun sendOnOffClusterCommand(commandId: OnOff.Command) {
// TODO : Need to be implement poj-to-tlv
val tlvWriter = TlvWriter()
tlvWriter.startStructure(AnonymousTag)
tlvWriter.endStructure()
val invokeElement = InvokeElement.newInstance(addressUpdateFragment.endpointId.toLong()
, OnOff.ID
, commandId.id
, tlvWriter.getEncoded(), null)

deviceController.invoke(object: InvokeCallback {
override fun onError(ex: Exception?) {
showMessage("${commandId.name} command failure $ex")
Log.e(TAG, "${commandId.name} command failure", ex)
}
})
}

private suspend fun sendToggleCommandClick() {
getOnOffClusterForDevice().toggle(object : ChipClusters.DefaultClusterCallback {
override fun onSuccess() {
showMessage("TOGGLE command success")
override fun onResponse(invokeElement: InvokeElement?, successCode: Long) {
Log.e(TAG, "onResponse : $invokeElement, Code : $successCode")
showMessage("${commandId.name} command success")
}

override fun onError(ex: Exception) {
showMessage("TOGGLE command failure $ex")
Log.e(TAG, "TOGGLE command failure", ex)
}
})
}, getConnectedDevicePointer(), invokeElement, 0, 0)
}

private suspend fun getOnOffClusterForDevice(): OnOffCluster {
return OnOffCluster(

ChipClient.getConnectedDevicePointer(requireContext(), addressUpdateFragment.deviceId),
addressUpdateFragment.endpointId
)
private suspend fun getConnectedDevicePointer(): Long {
return ChipClient.getConnectedDevicePointer(requireContext(), addressUpdateFragment.deviceId)
}

private fun showMessage(msg: String) {
Expand Down
Loading