diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index dad1d2c6463644..6052e01c70078e 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,12 +1,18 @@ -> !!!!!!!!!! Please delete the instructions below and replace with PR description +> !!!!!!!!!! Please delete the instructions below and replace with PR +> description > -> If you have an issue number, please use a syntax of -> `Fixes #12345` and a brief change description +> If you have an issue number, please use a syntax of `Fixes #12345` and a brief +> change description > -> If you do not have an issue number, please have a good description of -> the problem and the fix. Help the reviewer understand what to expect. +> If you do not have an issue number, please have a good description of the +> problem and the fix. Help the reviewer understand what to expect. +> +> Complete/append to the `### Testing` section below, to describe how testing +> was done. See +> > > Make sure you delete these instructions (to prove you have read them). > > !!!!!!!!!! Instructions end +#### Testing diff --git a/.github/labeler.yml b/.github/labeler.yml index 68d976ecd5b0df..cb4d0839816965 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -119,15 +119,6 @@ test driver: - src/test_driver/* - src/test_driver/**/* -# Cert tests touched: add current milestone delta-tracking label. -# TODO: Change after Aug 15, 2024 -matter-1.4-te2-script-change: - - changed-files: - - any-glob-to-any-file: - - src/python_testing/* - - src/python_testing/**/* - - src/app/tests/suites/certification/* - ############################################################ # Source Code ############################################################ diff --git a/.github/workflows/java-tests.yaml b/.github/workflows/java-tests.yaml index df39538cbeced9..7d5170c39bc5a3 100644 --- a/.github/workflows/java-tests.yaml +++ b/.github/workflows/java-tests.yaml @@ -94,6 +94,7 @@ jobs: --target linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test \ --target linux-x64-java-matter-controller \ --target linux-x64-lit-icd-ipv6only \ + --target linux-x64-ota-requestor \ build \ " - name: Build Kotlin Matter Controller @@ -259,6 +260,19 @@ jobs: --tool-args "onnetwork-long --nodeid 1 --setup-pin-code 20202021 --discriminator 3840 -t 1000" \ --factoryreset \ ' + - name: Run Pairing ICD Onnetwork and OTA test + # Generally completes in seconds + timeout-minutes: 2 + run: | + scripts/run_in_python_env.sh out/venv \ + './scripts/tests/run_java_test.py \ + --app out/linux-x64-ota-requestor/chip-ota-requestor-app \ + --app-args "--discriminator 3840 --interface-id -1" \ + --tool-path out/linux-x64-java-matter-controller \ + --tool-cluster "ota" \ + --tool-args "onnetwork-long-ota-over-bdx --nodeid 1 --setup-pin-code 20202021 --discriminator 3840 -t 1000" \ + --factoryreset \ + ' - name: Run Pairing Onnetwork and get diagnostic log Test run: | scripts/run_in_python_env.sh out/venv \ diff --git a/.github/workflows/pr-validation.yaml b/.github/workflows/pr-validation.yaml new file mode 100644 index 00000000000000..d34867f7accec3 --- /dev/null +++ b/.github/workflows/pr-validation.yaml @@ -0,0 +1,69 @@ +name: PR validity + +on: + pull_request: + types: [opened, synchronize, reopened, edited] + +jobs: + check_testing_header: + runs-on: ubuntu-latest + steps: + - name: Check for `### Testing` section in PR + id: check-testing + continue-on-error: true + run: | + cat >/tmp/pr-summary.txt << "EndMarkerForPrSummary" + ${{ github.event.pull_request.body }} + EndMarkerForPrSummary + + python -c 'import sys; pr_summary = open("/tmp/pr-summary.txt", "rt").read(); sys.exit(0 if "### Testing" in pr_summary else 1)' + + - name: Check for PR starting instructions + id: check-instructions + continue-on-error: true + run: | + cat >/tmp/pr-summary.txt << "EndMarkerForPrSummary" + ${{ github.event.pull_request.body }} + EndMarkerForPrSummary + + python -c 'import sys; pr_summary = open("/tmp/pr-summary.txt", "rt").read(); sys.exit(1 if "Make sure you delete these instructions" in pr_summary else 0)' + + # NOTE: comments disabled for now as separate permissions are required + # failing CI step may be sufficient to start (although it contains less information about why it failed) + + # - name: Add comment (missing instructions) + # if: steps.check-instructions.outcome == 'failure' + # uses: actions/github-script@v6 + # with: + # github-token: ${{ secrets.GITHUB_TOKEN }} + # script: | + # github.rest.issues.createComment({ + # issue_number: context.issue.number, + # owner: context.repo.owner, + # repo: context.repo.repo, + # body: 'Please make sure to delete starter instructions from your PR summary and replace them with a descriptive summary.' + # }) + + - name: Fail if PR instructions were not deleted + if: steps.check-instructions.outcome == 'failure' + run: | + python -c 'import sys; print("PR instructions were not replaced"); sys.exit(1)' + + # - name: Add comment (missing testing) + # if: steps.check-testing.outcome == 'failure' + # uses: actions/github-script@v6 + # with: + # github-token: ${{ secrets.GITHUB_TOKEN }} + # script: | + # github.rest.issues.createComment({ + # issue_number: context.issue.number, + # owner: context.repo.owner, + # repo: context.repo.repo, + # body: 'Please add a `### Testing` section to your PR summary describing the testing performed. See https://github.com/project-chip/connectedhomeip/blob/master/CONTRIBUTING.md#pull-requests' + # }) + + - name: Fail if `### Testing` section not in PR + if: steps.check-testing.outcome == 'failure' + run: | + python -c 'import sys; print("Testing section missing (test failed)"); sys.exit(1)' + diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 38811c423a9ced..1ed2a01bdbc018 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -111,6 +111,7 @@ jobs: src/app/zap-templates/zcl/data-model/chip/boolean-state-cluster.xml \ src/app/zap-templates/zcl/data-model/chip/actions-cluster.xml \ src/app/zap-templates/zcl/data-model/chip/bridged-device-basic-information.xml \ + src/app/zap-templates/zcl/data-model/chip/camera-av-settings-user-level-management-cluster.xml \ src/app/zap-templates/zcl/data-model/chip/camera-av-stream-management-cluster.xml \ src/app/zap-templates/zcl/data-model/chip/chip-ota.xml \ src/app/zap-templates/zcl/data-model/chip/chip-types.xml \ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a71219f560671b..68e28dae951fde 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -160,6 +160,99 @@ This will trigger the continuous-integration checks. You can view the results in the respective services. Note that the integration checks will report failures on occasion. +#### Pull requests + +Aim to make pull requests easy to read both when viewed in a list (title only) +as well as clear in content within the description. + +##### Title formatting + +Describe the change as a one-line in some descriptive manner. Add sufficient +context for a reader to understand what is improved. If platform-specific +consider adding the platform as a prefix, like `[Android]` or any other tags may +be useful for quick filtering like `[TC-ABC-1.2]` to tag test changes. + +Examples of descriptive titles: + +- `[Silabs] Fix compile of SiWx917 if LED and BUTTON are disabled` +- `[Telink] Update build Dockerfile with new Zeprhy SHA: c05c4.....` +- `General Commissioning Cluster: use AttributeAccessInterface/CommandHandlerInterface for processing` +- `Scenes Management/CopyScene: set access as manage instead of default to match the spec` +- `Fix build errors due to ChipDeviceEvent default constructor not being available` +- `Fix crash during DNSSD processing due to malformed packet` +- `[NRF] Fix crash due to stack overflow during logging for PW-RPC builds` +- `[TC-ABC-2.3] added new python test case based on test plan` +- `[TC-ABC] migrate tests from yaml to python` + +Examples of titles that are vague (not clear what the change is, one would need +to open the pull request for details or open additional issue in GitHub) + +- `Work on issue 1234` +- `Fix android JniTypeWrappers` +- `Fix segfault in BLE` +- `Fix TC-ABC-1.2` +- `Update Readme` + +##### Summary contents + +Ensure that there is sufficient detail in issue summaries to make the content of +the PR clear: + +- a `TLDR` of the change content. This is a judgment call on details, + generally you should include a what was changed and why. The change is + trivial/short, this can be very short (i.e. "fixed typos" is perfectly + acceptable, however if changing 100-1000s of line, the areas of changes + should be explained) +- If a crash/error is fixed, explain the root cause and if the fix is not + obvious (again, judgment call), explain why the given approach was taken. +- Help the reviewer out with any notable information (specific platform + issues, extra thoughts or requests for feedback or gotchas on tricky code, + followup work or PR dependencies) +- TIP: use the syntax of `Fixes #....` to mark issues completed on PR merge or + use `#...` to reference issues that are addressed. +- TIP: prefer adding some brief description (especially about the content of + the changes) instead of just referencing an issue (helps reviewers get + context faster without extra clicks). + +##### Testing section + +All Pull Requests **MUST** contain a `#### Testing` section that describes how +the pull request was tested. Ideally every test should have automated testing, +however for platform specific changes or hardware-specific issues we may not be +able to have such tests (e.g. we may not BLE or NFC capability in CI). As such, +manual testing is acceptable, however the description has to be detailed +intentionally to avoid a bias towards marking pull requests as "manually tested" +out of convenience. + +- Automated testing + + **AWESOME**. You can say "unit tests added/updated" or "Integration tests + updated to cover functionality" or "existing tests already cover this" (make + sure they do. Integration tests often only cover happy paths). + + Add any notes on not covered things. It is a judgment call on how much can + be covered as 100% sounds great however not always possible. + +- Manual testing + + Describe why automated testing is impossible in the current CI environment + or difficult to add. If adding later, reference the issue to add automation + and a timeline for adding such automation. + + Describe in **DETAIL** how manual testing was done: what environment, what + builds were used (`build-example` names are ok such as + `flashed qpg-qpg6105-light` and `used linux-x64-chip-tool-clang`). Describe + commands ran (often chip-tool) and physical interaction and what was + observed. + +- Trivial/obvious change + + In rare cases the change is trivial (e.g. fixing a typo in a `Readme.md`). + Scripts still require a `#### Testing` section however you can be brief like + `N/A` or `checked new URL opens`. Note that these cases are rare - e.g. + fixing a typo in an ID still requires some description on how you checked + that the new ID takes effect. + ### Review Requirements #### Documentation Best Practices diff --git a/docs/ids_and_codes/zap_clusters.md b/docs/ids_and_codes/zap_clusters.md index b37e8b2b8b7781..a805a9436c29dd 100644 --- a/docs/ids_and_codes/zap_clusters.md +++ b/docs/ids_and_codes/zap_clusters.md @@ -131,6 +131,7 @@ Generally regenerate using one of: | 1296 | 0x510 | ContentAppObserver | | 1360 | 0x550 | ZoneManagement | | 1361 | 0x551 | CameraAvStreamManagement | +| 1362 | 0x552 | CameraAvSettingsUserLevelManagement | | 1363 | 0x553 | WebRTCTransportProvider | | 1364 | 0x554 | WebRTCTransportRequestor | | 1366 | 0x556 | Chime | diff --git a/examples/all-clusters-app/all-clusters-common/src/software-diagnostics-stub.cpp b/examples/all-clusters-app/all-clusters-common/src/software-diagnostics-stub.cpp new file mode 100644 index 00000000000000..8bdf305b6d60a5 --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/src/software-diagnostics-stub.cpp @@ -0,0 +1,68 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +using namespace chip; +using namespace chip::app; +using namespace chip::DeviceLayer; + +namespace { + +void SetTestEventTrigger_SoftwareFaultOccurred() +{ + Clusters::SoftwareDiagnostics::Events::SoftwareFault::Type softwareFault; + char threadName[kMaxThreadNameLength + 1]; + + softwareFault.id = static_cast(getpid()); + Platform::CopyString(threadName, std::to_string(softwareFault.id).c_str()); + + softwareFault.name.SetValue(CharSpan::fromCharString(threadName)); + + std::time_t result = std::time(nullptr); + // Using size of 50 as it is double the expected 25 characters "Www Mmm dd hh:mm:ss yyyy\n". + char timeChar[50]; + if (std::strftime(timeChar, sizeof(timeChar), "%c", std::localtime(&result))) + { + softwareFault.faultRecording.SetValue(ByteSpan(Uint8::from_const_char(timeChar), strlen(timeChar))); + } + + Clusters::SoftwareDiagnosticsServer::Instance().OnSoftwareFaultDetect(softwareFault); +} + +} // namespace + +bool HandleSoftwareDiagnosticsTestEventTrigger(uint64_t eventTrigger) +{ + SoftwareDiagnosticsTrigger trigger = static_cast(eventTrigger); + + switch (trigger) + { + case SoftwareDiagnosticsTrigger::kSoftwareFault: + ChipLogProgress(Support, "[Software-Diagnostics-Test-Event] => Software Fault occurred"); + SetTestEventTrigger_SoftwareFaultOccurred(); + break; + default: + + return false; + } + + return true; +} diff --git a/examples/all-clusters-app/linux/BUILD.gn b/examples/all-clusters-app/linux/BUILD.gn index 4de95821692fae..d13c51a6502cba 100644 --- a/examples/all-clusters-app/linux/BUILD.gn +++ b/examples/all-clusters-app/linux/BUILD.gn @@ -56,6 +56,7 @@ source_set("chip-all-clusters-common") { "${chip_root}/examples/all-clusters-app/all-clusters-common/src/rvc-modes.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/rvc-operational-state-delegate-impl.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/smco-stub.cpp", + "${chip_root}/examples/all-clusters-app/all-clusters-common/src/software-diagnostics-stub.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-modes-manager.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-temperature-levels.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/tcc-mode.cpp", diff --git a/examples/all-clusters-app/linux/args.gni b/examples/all-clusters-app/linux/args.gni index 92d01ea3358b30..d98ceaa0eca989 100644 --- a/examples/all-clusters-app/linux/args.gni +++ b/examples/all-clusters-app/linux/args.gni @@ -30,3 +30,4 @@ matter_log_json_payload_hex = true chip_enable_smoke_co_trigger = true chip_enable_boolean_state_configuration_trigger = true chip_enable_water_heater_management_trigger = true +chip_enable_software_diagnostics_trigger = true diff --git a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/SelectActionFragment.kt b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/SelectActionFragment.kt index fb8141e72d33a8..09c3f85a454963 100644 --- a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/SelectActionFragment.kt +++ b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/SelectActionFragment.kt @@ -76,6 +76,7 @@ class SelectActionFragment : Fragment() { binding.groupSettingBtn.setOnClickListener { handleGroupSettingClicked() } binding.otaProviderBtn.setOnClickListener { handleOTAProviderClicked() } binding.icdBtn.setOnClickListener { handleICDClicked() } + binding.modeSelectBtn.setOnClickListener { handleModeSelectClicked() } return binding.root } @@ -255,6 +256,10 @@ class SelectActionFragment : Fragment() { showFragment(ICDFragment.newInstance()) } + private fun handleModeSelectClicked() { + showFragment(ModeSelectClientFragment.newInstance()) + } + companion object { @JvmStatic fun newInstance() = SelectActionFragment() diff --git a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/ModeSelectClientFragment.kt b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/ModeSelectClientFragment.kt new file mode 100644 index 00000000000000..e19e0fe0a58682 --- /dev/null +++ b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/ModeSelectClientFragment.kt @@ -0,0 +1,297 @@ +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.widget.ArrayAdapter +import android.widget.EditText +import android.widget.TextView +import android.widget.Toast +import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope +import chip.devicecontroller.ChipClusters +import chip.devicecontroller.ChipDeviceController +import chip.devicecontroller.ClusterIDMapping +import chip.devicecontroller.ClusterIDMapping.ModeSelect +import chip.devicecontroller.ReportCallback +import chip.devicecontroller.WriteAttributesCallback +import chip.devicecontroller.cluster.structs.ModeSelectClusterModeOptionStruct +import chip.devicecontroller.model.AttributeState +import chip.devicecontroller.model.AttributeWriteRequest +import chip.devicecontroller.model.ChipAttributePath +import chip.devicecontroller.model.ChipEventPath +import chip.devicecontroller.model.ChipPathId +import chip.devicecontroller.model.NodeState +import chip.devicecontroller.model.Status +import com.google.chip.chiptool.ChipClient +import com.google.chip.chiptool.R +import com.google.chip.chiptool.databinding.ModeSelectFragmentBinding +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 ModeSelectClientFragment : Fragment() { + private val deviceController: ChipDeviceController + get() = ChipClient.getDeviceController(requireContext()) + + private lateinit var scope: CoroutineScope + + private lateinit var addressUpdateFragment: AddressUpdateFragment + + private var _binding: ModeSelectFragmentBinding? = null + + private val startUpMode: UInt + get() = binding.startUpModeEd.text.toString().toUIntOrNull() ?: 0U + + private val onMode: UInt + get() = binding.onModeEd.text.toString().toUIntOrNull() ?: 0U + + private val currentMode: Int + get() = binding.supportedModesSp.selectedItem.toString().split("-")[0].toInt() + + private val binding + get() = _binding!! + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = ModeSelectFragmentBinding.inflate(inflater, container, false) + scope = viewLifecycleOwner.lifecycleScope + + addressUpdateFragment = + childFragmentManager.findFragmentById(R.id.addressUpdateFragment) as AddressUpdateFragment + + binding.readAttributeBtn.setOnClickListener { scope.launch { readAttributeBtnClick() } } + binding.changeToModeBtn.setOnClickListener { scope.launch { changeToModeBtnClick() } } + binding.onModeWriteBtn.setOnClickListener { + scope.launch { writeAttributeBtnClick(ClusterIDMapping.ModeSelect.Attribute.OnMode, onMode) } + } + binding.startUpModeWriteBtn.setOnClickListener { + scope.launch { + writeAttributeBtnClick(ClusterIDMapping.ModeSelect.Attribute.StartUpMode, startUpMode) + } + } + + return binding.root + } + + private suspend fun readAttributeBtnClick() { + val endpointId = addressUpdateFragment.endpointId + val clusterId = ModeSelect.ID + val attributeId = ChipPathId.forWildcard().id + val path = ChipAttributePath.newInstance(endpointId, clusterId, attributeId) + val devicePtr = + try { + ChipClient.getConnectedDevicePointer(requireContext(), addressUpdateFragment.deviceId) + } catch (e: IllegalStateException) { + Log.d(TAG, "getConnectedDevicePointer exception", e) + showMessage("Get DevicePointer fail!") + return + } + deviceController.readAttributePath( + object : ReportCallback { + override fun onError( + attributePath: ChipAttributePath?, + eventPath: ChipEventPath?, + e: Exception + ) { + requireActivity().runOnUiThread { + Toast.makeText( + requireActivity(), + R.string.ota_provider_invalid_attribute, + Toast.LENGTH_SHORT + ) + .show() + } + } + + override fun onReport(nodeState: NodeState?) { + val attributeStates = + nodeState?.getEndpointState(endpointId)?.getClusterState(clusterId)?.attributeStates + ?: return + + requireActivity().runOnUiThread { + val description = attributeStates[ClusterIDMapping.ModeSelect.Attribute.Description.id] + binding.descriptionEd.setText(description?.value?.toString()) + + val standardNamespace = + attributeStates[ClusterIDMapping.ModeSelect.Attribute.StandardNamespace.id] + binding.standardNamespaceEd.setText(standardNamespace?.value?.toString()) + + val currentMode = attributeStates[ClusterIDMapping.ModeSelect.Attribute.CurrentMode.id] + binding.currentModeEd.setText(currentMode?.value?.toString()) + + setVisibility( + attributeStates[ClusterIDMapping.ModeSelect.Attribute.StartUpMode.id], + binding.startUpModeEd, + binding.startUpModeTv, + binding.startUpModeWriteBtn + ) + setVisibility( + attributeStates[ClusterIDMapping.ModeSelect.Attribute.OnMode.id], + binding.onModeEd, + binding.onModeTv, + binding.onModeWriteBtn + ) + + val supportedModesTlv = + attributeStates[ClusterIDMapping.ModeSelect.Attribute.SupportedModes.id]?.tlv + + supportedModesTlv?.let { + setSupportedModeSpinner(it, currentMode?.value?.toString()?.toUInt()) + } + } + } + }, + devicePtr, + listOf(path), + 0 + ) + } + + private fun setVisibility( + attribute: AttributeState?, + modeEd: EditText, + modeTv: TextView, + writeBtn: TextView + ) { + val modeVisibility = + if (attribute != null) { + modeEd.setText(attribute.value?.toString() ?: "NULL") + View.VISIBLE + } else { + View.GONE + } + modeEd.visibility = modeVisibility + modeTv.visibility = modeVisibility + writeBtn.visibility = modeVisibility + } + + private fun setSupportedModeSpinner(supportedModesTlv: ByteArray, currentModeValue: UInt?) { + var pos = 0 + var currentItemId = 0 + val modeOptionStructList: List + TlvReader(supportedModesTlv).also { + modeOptionStructList = buildList { + it.enterArray(AnonymousTag) + while (!it.isEndOfContainer()) { + val struct = ModeSelectClusterModeOptionStruct.fromTlv(AnonymousTag, it) + add(struct) + if (currentModeValue != null && struct.mode == currentModeValue) { + currentItemId = pos + } + pos++ + } + it.exitContainer() + } + binding.supportedModesSp.adapter = + ArrayAdapter( + requireContext(), + android.R.layout.simple_spinner_dropdown_item, + modeOptionStructList.map { it.show() } + ) + binding.supportedModesSp.setSelection(currentItemId) + binding.currentModeEd.setText(binding.supportedModesSp.selectedItem.toString()) + } + } + + private suspend fun changeToModeBtnClick() { + val devicePtr = + try { + ChipClient.getConnectedDevicePointer(requireContext(), addressUpdateFragment.deviceId) + } catch (e: IllegalStateException) { + Log.d(TAG, "getConnectedDevicePointer exception", e) + showMessage("Get DevicePointer fail!") + return + } + ChipClusters.ModeSelectCluster(devicePtr, addressUpdateFragment.endpointId) + .changeToMode( + object : ChipClusters.DefaultClusterCallback { + override fun onError(error: java.lang.Exception?) { + Log.d(TAG, "onError", error) + showMessage("Error : ${error.toString()}") + } + + override fun onSuccess() { + showMessage("Change Success") + scope.launch { readAttributeBtnClick() } + } + }, + currentMode + ) + } + + private suspend fun writeAttributeBtnClick(attribute: ModeSelect.Attribute, value: UInt) { + val clusterId = ModeSelect.ID + val devicePtr = + try { + ChipClient.getConnectedDevicePointer(requireContext(), addressUpdateFragment.deviceId) + } catch (e: IllegalStateException) { + Log.d(TAG, "getConnectedDevicePointer exception", e) + showMessage("Get DevicePointer fail!") + return + } + deviceController.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, + TlvWriter().put(AnonymousTag, value).getEncoded(), + Optional.empty() + ) + ), + 0, + 0 + ) + } + + private fun ModeSelectClusterModeOptionStruct.show(): String { + val value = this + return StringBuilder() + .apply { + append("${value.mode}-${value.label}") + append("[") + for (semanticTag in value.semanticTags) { + append("${semanticTag.value}:${semanticTag.mfgCode}") + append(",") + } + append("]") + } + .toString() + } + + override fun onDestroyView() { + super.onDestroyView() + deviceController.finishOTAProvider() + _binding = null + } + + private fun showMessage(msg: String) { + requireActivity().runOnUiThread { binding.commandStatusTv.text = msg } + } + + companion object { + private const val TAG = "ModeSelectClientFragment" + + fun newInstance(): ModeSelectClientFragment = ModeSelectClientFragment() + } +} diff --git a/examples/android/CHIPTool/app/src/main/res/layout/mode_select_fragment.xml b/examples/android/CHIPTool/app/src/main/res/layout/mode_select_fragment.xml new file mode 100644 index 00000000000000..3f13ca9b654d36 --- /dev/null +++ b/examples/android/CHIPTool/app/src/main/res/layout/mode_select_fragment.xml @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/android/CHIPTool/app/src/main/res/layout/select_action_fragment.xml b/examples/android/CHIPTool/app/src/main/res/layout/select_action_fragment.xml index db999600999741..e2f5ce8f1d7db0 100644 --- a/examples/android/CHIPTool/app/src/main/res/layout/select_action_fragment.xml +++ b/examples/android/CHIPTool/app/src/main/res/layout/select_action_fragment.xml @@ -143,6 +143,14 @@ android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:text="@string/icd_btn_text" /> + +