Skip to content

Commit

Permalink
Update to new SubscribeAttribute API (#13015)
Browse files Browse the repository at this point in the history
  • Loading branch information
austinh0 authored Dec 15, 2021
1 parent 8e521b3 commit c52bc7b
Show file tree
Hide file tree
Showing 11 changed files with 15,405 additions and 25,310 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import kotlinx.android.synthetic.main.on_off_client_fragment.view.readBtn
import kotlinx.android.synthetic.main.on_off_client_fragment.view.showSubscribeDialogBtn
import kotlinx.android.synthetic.main.on_off_client_fragment.view.toggleBtn
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch

class OnOffClientFragment : Fragment() {
Expand Down Expand Up @@ -108,7 +107,9 @@ class OnOffClientFragment : Fragment() {
minIntervalEd.text.toString().toInt(),
maxIntervalEd.text.toString().toInt()
)
dialog.dismiss()
requireActivity().runOnUiThread {
dialog.dismiss()
}
}
}
dialog.show()
Expand All @@ -117,26 +118,20 @@ class OnOffClientFragment : Fragment() {
private suspend fun sendSubscribeOnOffClick(minInterval: Int, maxInterval: Int) {
val onOffCluster = getOnOffClusterForDevice()

val subscribeCallback = object : ChipClusters.DefaultClusterCallback {
override fun onSuccess() {
val message = "Subscribe on/off success"
Log.v(TAG, message)
showMessage(message)

onOffCluster.reportOnOffAttribute(object : ChipClusters.BooleanAttributeCallback {
override fun onSuccess(on: Boolean) {
Log.v(TAG, "Report on/off attribute value: $on")
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"}"

val formatter = SimpleDateFormat("HH:mm:ss", Locale.getDefault())
val time = formatter.format(Calendar.getInstance(Locale.getDefault()).time)
showReportMessage("Report on/off at $time: ${if (on) "ON" else "OFF"}")
}
Log.v(TAG, message)
showReportMessage(message)
}

override fun onError(ex: Exception) {
Log.e(TAG, "Error reporting on/off attribute", ex)
showReportMessage("Error reporting on/off attribute: $ex")
}
})
override fun onSubscriptionEstablished() {
val message = "Subscription for on/off established"
Log.v(TAG, message)
showMessage(message)
}

override fun onError(ex: Exception) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,8 @@ import kotlinx.android.synthetic.main.sensor_client_fragment.clusterNameSpinner
import kotlinx.android.synthetic.main.sensor_client_fragment.deviceIdEd
import kotlinx.android.synthetic.main.sensor_client_fragment.endpointIdEd
import kotlinx.android.synthetic.main.sensor_client_fragment.lastValueTv
import kotlinx.android.synthetic.main.sensor_client_fragment.readSensorBtn
import kotlinx.android.synthetic.main.sensor_client_fragment.sensorGraph
import kotlinx.android.synthetic.main.sensor_client_fragment.view.clusterNameSpinner
import kotlinx.android.synthetic.main.sensor_client_fragment.view.readSensorBtn
import kotlinx.android.synthetic.main.sensor_client_fragment.view.sensorGraph
import kotlinx.android.synthetic.main.sensor_client_fragment.view.watchSensorBtn
import kotlinx.android.synthetic.main.sensor_client_fragment.watchSensorBtn
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
Expand All @@ -52,53 +49,56 @@ class SensorClientFragment : Fragment() {
savedInstanceState: Bundle?
): View {
scope = viewLifecycleOwner.lifecycleScope
return inflater.inflate(R.layout.sensor_client_fragment, container, false)
}

return inflater.inflate(R.layout.sensor_client_fragment, container, false).apply {
ChipClient.getDeviceController(requireContext()).setCompletionListener(null)
deviceIdEd.setOnEditorActionListener { textView, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE) {
updateAddress(textView.text.toString())
resetSensorGraph() // reset the graph on device change
}
actionId == EditorInfo.IME_ACTION_DONE
}
endpointIdEd.setOnEditorActionListener { textView, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE)
resetSensorGraph() // reset the graph on endpoint change
actionId == EditorInfo.IME_ACTION_DONE
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

ChipClient.getDeviceController(requireContext()).setCompletionListener(null)
deviceIdEd.setOnEditorActionListener { textView, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE) {
updateAddress(textView.text.toString())
resetSensorGraph() // reset the graph on device change
}
clusterNameSpinner.adapter = makeClusterNamesAdapter()
clusterNameSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) = Unit
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
resetSensorGraph() // reset the graph on cluster change
}
actionId == EditorInfo.IME_ACTION_DONE
}
endpointIdEd.setOnEditorActionListener { textView, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE)
resetSensorGraph() // reset the graph on endpoint change
actionId == EditorInfo.IME_ACTION_DONE
}
clusterNameSpinner.adapter = makeClusterNamesAdapter()
clusterNameSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) = Unit
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
resetSensorGraph() // reset the graph on cluster change
}
}

readSensorBtn.setOnClickListener { scope.launch { readSensorCluster() } }
watchSensorBtn.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
scope.launch { subscribeSensorCluster() }
} else {
unsubscribeSensorCluster()
}
readSensorBtn.setOnClickListener { scope.launch { readSensorCluster() } }
watchSensorBtn.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
scope.launch { subscribeSensorCluster() }
} else {
unsubscribeSensorCluster()
}
}

val currentTime = Calendar.getInstance().time.time
sensorGraph.addSeries(sensorData)
sensorGraph.viewport.isXAxisBoundsManual = true
sensorGraph.viewport.setMinX(currentTime.toDouble())
sensorGraph.viewport.setMaxX(currentTime.toDouble() + MIN_REFRESH_PERIOD_S * 1000 * MAX_DATA_POINTS)
sensorGraph.gridLabelRenderer.padding = 20
sensorGraph.gridLabelRenderer.numHorizontalLabels = 4
sensorGraph.gridLabelRenderer.setHorizontalLabelsAngle(150)
sensorGraph.gridLabelRenderer.labelFormatter = object : LabelFormatter {
override fun setViewport(viewport: Viewport?) = Unit
override fun formatLabel(value: Double, isValueX: Boolean): String {
if (!isValueX)
return "%.2f".format(value)
return SimpleDateFormat("H:mm:ss").format(Date(value.toLong())).toString()
}
val currentTime = Calendar.getInstance().time.time
sensorGraph.addSeries(sensorData)
sensorGraph.viewport.isXAxisBoundsManual = true
sensorGraph.viewport.setMinX(currentTime.toDouble())
sensorGraph.viewport.setMaxX(currentTime.toDouble() + MIN_REFRESH_PERIOD_S * 1000 * MAX_DATA_POINTS)
sensorGraph.gridLabelRenderer.padding = 20
sensorGraph.gridLabelRenderer.numHorizontalLabels = 4
sensorGraph.gridLabelRenderer.setHorizontalLabelsAngle(150)
sensorGraph.gridLabelRenderer.labelFormatter = object : LabelFormatter {
override fun setViewport(viewport: Viewport?) = Unit
override fun formatLabel(value: Double, isValueX: Boolean): String {
if (!isValueX)
return "%.2f".format(value)
return SimpleDateFormat("H:mm:ss").format(Date(value.toLong())).toString()
}
}
}
Expand Down Expand Up @@ -243,13 +243,9 @@ class SensorClientFragment : Fragment() {
},
"subscribe" to { device: Long, endpointId: Int, callback: ReadCallback ->
val cluster = ChipClusters.TemperatureMeasurementCluster(device, endpointId)
cluster.reportMeasuredValueAttribute(callback)
cluster.subscribeMeasuredValueAttribute(object : ChipClusters.DefaultClusterCallback {
override fun onSuccess() = Unit
override fun onError(ex: Exception) {
callback.onError(ex)
}
}, MIN_REFRESH_PERIOD_S, MAX_REFRESH_PERIOD_S)
cluster.subscribeMeasuredValueAttribute(callback,
MIN_REFRESH_PERIOD_S,
MAX_REFRESH_PERIOD_S)
},
"unitValue" to 0.01,
"unitSymbol" to "\u00B0C"
Expand All @@ -261,13 +257,9 @@ class SensorClientFragment : Fragment() {
},
"subscribe" to { device: Long, endpointId: Int, callback: ReadCallback ->
val cluster = ChipClusters.PressureMeasurementCluster(device, endpointId)
cluster.reportMeasuredValueAttribute(callback)
cluster.subscribeMeasuredValueAttribute(object : ChipClusters.DefaultClusterCallback {
override fun onSuccess() = Unit
override fun onError(ex: Exception) {
callback.onError(ex)
}
}, MIN_REFRESH_PERIOD_S, MAX_REFRESH_PERIOD_S)
cluster.subscribeMeasuredValueAttribute(callback,
MIN_REFRESH_PERIOD_S,
MAX_REFRESH_PERIOD_S)
},
"unitValue" to 1.0,
"unitSymbol" to "hPa"
Expand All @@ -279,13 +271,9 @@ class SensorClientFragment : Fragment() {
},
"subscribe" to { device: Long, endpointId: Int, callback: ReadCallback ->
val cluster = ChipClusters.RelativeHumidityMeasurementCluster(device, endpointId)
cluster.reportMeasuredValueAttribute(callback)
cluster.subscribeMeasuredValueAttribute(object : ChipClusters.DefaultClusterCallback {
override fun onSuccess() = Unit
override fun onError(ex: Exception) {
callback.onError(ex)
}
}, MIN_REFRESH_PERIOD_S, MAX_REFRESH_PERIOD_S)
cluster.subscribeMeasuredValueAttribute(callback,
MIN_REFRESH_PERIOD_S,
MAX_REFRESH_PERIOD_S)
},
"unitValue" to 0.01,
"unitSymbol" to "%"
Expand Down
35 changes: 16 additions & 19 deletions src/controller/java/templates/CHIPClusters-JNI.zapt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <zap-generated/CHIPClusters.h>
#include <zap-generated/CHIPClientCallbacks.h>

#include <controller/java/AndroidCallbacks.h>
#include <controller/java/AndroidClusterExceptions.h>
#include <controller/java/CHIPDefaultCallbacks.h>
#include <lib/support/JniReferences.h>
Expand Down Expand Up @@ -125,38 +126,34 @@ JNI_METHOD(void, {{asUpperCamelCase ../name}}Cluster, write{{asUpperCamelCase na
JNI_METHOD(void, {{asCamelCased ../name false}}Cluster, subscribe{{asCamelCased name false}}Attribute)(JNIEnv * env, jobject self, jlong clusterPtr, jobject callback, jint minInterval, jint maxInterval)
{
chip::DeviceLayer::StackLock lock;
std::unique_ptr<CHIPDefaultSuccessCallback, void (*)(CHIPDefaultSuccessCallback *)> onSuccess(Platform::New<CHIPDefaultSuccessCallback>(callback), Platform::Delete<CHIPDefaultSuccessCallback>);
{{~#*inline "callbackName"~}}
{{~#if_in_global_responses~}}
CHIP{{chipCallback.name}}AttributeCallback
{{~else~}}
CHIP{{asCamelCased parent.name false}}{{asCamelCased name false}}AttributeCallback
{{~/if_in_global_responses~}}
{{~/inline}}

std::unique_ptr<{{>callbackName}}, void (*)({{>callbackName}} *)> onSuccess(Platform::New<{{>callbackName}}>(callback, true), chip::Platform::Delete<{{>callbackName}}>);
VerifyOrReturn(onSuccess.get() != nullptr, chip::AndroidClusterExceptions::GetInstance().ReturnIllegalStateException(env, callback, "Error creating native success callback", CHIP_ERROR_NO_MEMORY));

std::unique_ptr<CHIPDefaultFailureCallback, void (*)(CHIPDefaultFailureCallback *)> onFailure(Platform::New<CHIPDefaultFailureCallback>(callback), Platform::Delete<CHIPDefaultFailureCallback>);
std::unique_ptr<CHIPDefaultFailureCallback, void (*)(CHIPDefaultFailureCallback *)> onFailure(Platform::New<CHIPDefaultFailureCallback>(callback), chip::Platform::Delete<CHIPDefaultFailureCallback>);
VerifyOrReturn(onFailure.get() != nullptr, chip::AndroidClusterExceptions::GetInstance().ReturnIllegalStateException(env, callback, "Error creating native failure callback", CHIP_ERROR_NO_MEMORY));

CHIP_ERROR err = CHIP_NO_ERROR;
{{asCamelCased ../name false}}Cluster * cppCluster = reinterpret_cast<{{asCamelCased ../name false}}Cluster *>(clusterPtr);
VerifyOrReturn(cppCluster != nullptr, chip::AndroidClusterExceptions::GetInstance().ReturnIllegalStateException(env, callback, "Could not get native cluster", CHIP_ERROR_INCORRECT_STATE));

err = cppCluster->SubscribeAttribute{{asCamelCased name false}}(onSuccess->Cancel(), onFailure->Cancel(), static_cast<uint16_t>(minInterval), static_cast<uint16_t>(maxInterval));
using TypeInfo = chip::app::Clusters::{{asUpperCamelCase parent.name}}::Attributes::{{asUpperCamelCase name}}::TypeInfo;
auto successFn = chip::Callback::Callback<CHIP{{asUpperCamelCase parent.name}}Cluster{{asUpperCamelCase name}}AttributeCallbackType>::FromCancelable(onSuccess->Cancel());
auto failureFn = chip::Callback::Callback<CHIPDefaultFailureCallbackType>::FromCancelable(onFailure->Cancel());

err = cppCluster->SubscribeAttribute<TypeInfo>(onSuccess->mContext, successFn->mCall, failureFn->mCall, static_cast<uint16_t>(minInterval), static_cast<uint16_t>(maxInterval), {{>callbackName}}::OnSubscriptionEstablished);
VerifyOrReturn(err == CHIP_NO_ERROR, chip::AndroidClusterExceptions::GetInstance().ReturnIllegalStateException(env, callback, "Error subscribing to attribute", err));

onSuccess.release();
onFailure.release();
}

JNI_METHOD(void, {{asCamelCased ../name false}}Cluster, report{{asCamelCased name false}}Attribute)(JNIEnv * env, jobject self, jlong clusterPtr, jobject callback)
{
chip::DeviceLayer::StackLock lock;
std::unique_ptr<CHIP{{chipCallback.name}}AttributeCallback, void (*)(CHIP{{chipCallback.name}}AttributeCallback *)> onReport(Platform::New<CHIP{{chipCallback.name}}AttributeCallback>(callback, true), Platform::Delete<CHIP{{chipCallback.name}}AttributeCallback>);
VerifyOrReturn(onReport.get() != nullptr, chip::AndroidClusterExceptions::GetInstance().ReturnIllegalStateException(env, callback, "Error creating native report callback", CHIP_ERROR_NO_MEMORY));

CHIP_ERROR err = CHIP_NO_ERROR;
{{asCamelCased ../name false}}Cluster * cppCluster = reinterpret_cast<{{asCamelCased ../name false}}Cluster *>(clusterPtr);
VerifyOrReturn(cppCluster != nullptr, chip::AndroidClusterExceptions::GetInstance().ReturnIllegalStateException(env, callback, "Could not get native cluster", CHIP_ERROR_INCORRECT_STATE));

err = cppCluster->ReportAttribute{{asCamelCased name false}}(onReport->Cancel());
VerifyOrReturn(err == CHIP_NO_ERROR, chip::AndroidClusterExceptions::GetInstance().ReturnIllegalStateException(env, callback, "Error registering for attribute reporting", err));

onReport.release();
}
{{/unless}}
{{/if}}
{{/unless}}
Expand Down
2 changes: 1 addition & 1 deletion src/controller/java/templates/CHIPReadCallbacks-src.zapt
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

#include <zap-generated/CHIPClientCallbacks.h>

#include <jni.h>
#include <lib/support/JniReferences.h>
#include <lib/support/JniTypeWrappers.h>
#include <jni.h>
#include <lib/support/CodeUtils.h>
#include <platform/PlatformManager.h>

Expand Down
11 changes: 11 additions & 0 deletions src/controller/java/templates/CHIPReadCallbacks.zapt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
{{#if (chip_has_client_clusters)}}
#include "CHIPCallbackTypes.h"

#include <controller/java/AndroidCallbacks.h>
#include <jni.h>
#include <lib/support/ErrorStr.h>
#include <lib/support/JniReferences.h>
#include <zap-generated/CHIPClientCallbacks.h>

{{#chip_server_global_responses}}
Expand All @@ -21,6 +24,10 @@ public:
}

static void CallbackFn(void * context, {{chipCallback.type}} value);
static void OnSubscriptionEstablished(void * context) {
CHIP_ERROR err = chip::JniReferences::GetInstance().CallSubscriptionEstablished(reinterpret_cast<CHIP{{chipCallback.name}}AttributeCallback *>(context)->javaCallbackRef);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Zcl, "Error calling onSubscriptionEstablished: %s", ErrorStr(err)));
};

private:
jobject javaCallbackRef;
Expand Down Expand Up @@ -49,6 +56,10 @@ public:
}

static void CallbackFn(void * context, {{zapTypeToDecodableClusterObjectType type ns=parent.name isArgument=true}} {{#if isList}}list{{else}}value{{/if}});
static void OnSubscriptionEstablished(void * context) {
CHIP_ERROR err = chip::JniReferences::GetInstance().CallSubscriptionEstablished(reinterpret_cast<CHIP{{asUpperCamelCase parent.name}}{{asUpperCamelCase name}}AttributeCallback *>(context)->javaCallbackRef);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Zcl, "Error calling onSubscriptionEstablished: %s", ErrorStr(err)));
};

private:
jobject javaCallbackRef;
Expand Down
Loading

0 comments on commit c52bc7b

Please sign in to comment.