diff --git a/.github/actions/checkout-submodules-and-bootstrap/action.yaml b/.github/actions/checkout-submodules-and-bootstrap/action.yaml index 8ddaec14e7bc47..b514b8dd795509 100644 --- a/.github/actions/checkout-submodules-and-bootstrap/action.yaml +++ b/.github/actions/checkout-submodules-and-bootstrap/action.yaml @@ -15,6 +15,8 @@ inputs: runs: using: "composite" steps: + - name: Maximize runner disk + uses: ./.github/actions/maximize-runner-disk - name: Dump disk info uses: ./.github/actions/dump-disk-info - name: Set git safe directory for local act runs diff --git a/.github/actions/maximize-runner-disk/action.yaml b/.github/actions/maximize-runner-disk/action.yaml new file mode 100644 index 00000000000000..fe5f95352aa53e --- /dev/null +++ b/.github/actions/maximize-runner-disk/action.yaml @@ -0,0 +1,50 @@ +name: Maximize runner disk +description: Free up disk space on the github runner +runs: + using: "composite" + steps: + - name: Free up disk space on the github runner + if: ${{ !env.ACT }} + shell: bash + run: | + # maximize-runner-disk + if [[ "$RUNNER_OS" == Linux ]]; then + # Directories to prune to free up space. Candidates: + # 1.6G /usr/share/dotnet + # 1.1G /usr/local/lib/android/sdk/platforms + # 1000M /usr/local/lib/android/sdk/build-tools + # 8.9G /usr/local/lib/android/sdk + # This list can be amended later to change the trade-off between the amount of + # disk space freed up, and how long it takes to do so (deleting many files is slow). + prune=(/usr/share/dotnet /usr/local/lib/android/sdk/platforms /usr/local/lib/android/sdk/build-tools) + + if [[ "$UID" -eq 0 && -d /__w ]]; then + root=/runner-root-volume + if [[ ! -d "$root" ]]; then + echo "Unable to maximize disk space, job is running inside a container and $root is not mounted" + exit 0 + fi + function sudo() { "$@"; } # we're already root (and sudo is probably unavailable) + elif [[ "$UID" -ne 0 && "$RUNNER_ENVIRONMENT" == github-hosted ]]; then + root= + else + echo "Unable to maximize disk space, unknown runner environment" + exit 0 + fi + + echo "Freeing up runner disk space on ${root:-/}" + function avail() { df -k --output=avail "${root:-/}" | grep '^[0-9]*$'; } + function now() { date '+%s'; } + before="$(avail)" start="$(now)" + for dir in "${prune[@]}"; do + if [[ -d "${root}${dir}" ]]; then + echo "- $dir" + # du -sh -- "${root}${dir}" + sudo rm -rf -- "${root}${dir}" + else + echo "- $dir (not found)" + fi + done + after="$(avail)" end="$(now)" + echo "Done, freed up $(( (after - before) / 1024 ))M of disk space in $(( end - start )) seconds." + fi diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 23dd3d3eb0f309..effec464b45aee 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -42,6 +42,7 @@ jobs: container: image: ghcr.io/project-chip/chip-build:35 volumes: + - "/:/runner-root-volume" - "/tmp/log_output:/tmp/test_logs" options: --privileged --sysctl "net.ipv6.conf.all.disable_ipv6=0 net.ipv4.conf.all.forwarding=1 net.ipv6.conf.all.forwarding=1" @@ -137,6 +138,7 @@ jobs: container: image: ghcr.io/project-chip/chip-build:35 volumes: + - "/:/runner-root-volume" - "/tmp/log_output:/tmp/test_logs" options: --privileged --sysctl "net.ipv6.conf.all.disable_ipv6=0 net.ipv4.conf.all.forwarding=1 net.ipv6.conf.all.forwarding=1" @@ -285,6 +287,7 @@ jobs: container: image: ghcr.io/project-chip/chip-build:35 volumes: + - "/:/runner-root-volume" - "/tmp/log_output:/tmp/test_logs" options: --sysctl "net.ipv6.conf.all.disable_ipv6=0 net.ipv4.conf.all.forwarding=1 net.ipv6.conf.all.forwarding=1" @@ -345,6 +348,7 @@ jobs: container: image: ghcr.io/project-chip/chip-build:35 volumes: + - "/:/runner-root-volume" - "/tmp/log_output:/tmp/test_logs" options: --sysctl "net.ipv6.conf.all.disable_ipv6=0 net.ipv4.conf.all.forwarding=1 net.ipv6.conf.all.forwarding=1" @@ -453,6 +457,7 @@ jobs: container: image: ghcr.io/project-chip/chip-build:35 volumes: + - "/:/runner-root-volume" - "/tmp/log_output:/tmp/test_logs" options: --privileged --sysctl "net.ipv6.conf.all.disable_ipv6=0 net.ipv4.conf.all.forwarding=1 net.ipv6.conf.all.forwarding=1" diff --git a/.github/workflows/darwin.yaml b/.github/workflows/darwin.yaml index 2662853b1f790b..0602b735d36b88 100644 --- a/.github/workflows/darwin.yaml +++ b/.github/workflows/darwin.yaml @@ -103,8 +103,7 @@ jobs: # target versions instead? run: | mkdir -p /tmp/darwin/framework-tests - echo "This is a simple log" > /tmp/darwin/framework-tests/end_user_support_log.txt - ../../../out/debug/chip-all-clusters-app --interface-id -1 --end_user_support_log /tmp/darwin/framework-tests/end_user_support_log.txt > >(tee /tmp/darwin/framework-tests/all-cluster-app.log) 2> >(tee /tmp/darwin/framework-tests/all-cluster-app-err.log >&2) & + ../../../out/debug/chip-all-clusters-app --interface-id -1 > >(tee /tmp/darwin/framework-tests/all-cluster-app.log) 2> >(tee /tmp/darwin/framework-tests/all-cluster-app-err.log >&2) & ../../../out/debug/chip-all-clusters-app --interface-id -1 --dac_provider ../../../credentials/development/commissioner_dut/struct_cd_origin_pid_vid_correct/test_case_vector.json --product-id 32768 --discriminator 3839 --secured-device-port 5539 --KVS /tmp/chip-all-clusters-app-kvs2 > >(tee /tmp/darwin/framework-tests/all-cluster-app-origin-vid.log) 2> >(tee /tmp/darwin/framework-tests/all-cluster-app-origin-vid-err.log >&2) & # Disable BLE (CHIP_IS_BLE=NO) because the app does not have the permission to use it and that may crash the CI. @@ -120,13 +119,10 @@ jobs: run: | xcodebuild -target "MatterTvCastingBridge" -sdk iphoneos working-directory: examples/tv-casting-app/darwin/MatterTvCastingBridge - - name: Uploading .ips files in Xcode derived data to debug the failure - uses: actions/upload-artifact@v4 - if: ${{ failure() && !env.ACT }} - with: - name: darwin-framework-derived-data - path: ~/Library/Developer/Xcode/DerivedData/**/*.ips - retention-days: 5 + - name: Collect crash logs + run: | + mkdir -p /tmp/darwin/framework-tests + find ~/Library/Developer/Xcode/DerivedData /Library/Logs/DiagnosticReports -name '*.ips' -print0 | xargs -0 -J % cp % /tmp/darwin/framework-tests - name: Uploading log files uses: actions/upload-artifact@v4 if: ${{ failure() && !env.ACT }} diff --git a/.github/workflows/java-tests.yaml b/.github/workflows/java-tests.yaml index 3aa6935d1735c4..8d21ca0bd4c56d 100644 --- a/.github/workflows/java-tests.yaml +++ b/.github/workflows/java-tests.yaml @@ -133,7 +133,7 @@ jobs: --tool-args "onnetwork-long-im-invoke --nodeid 1 --setup-pin-code 20202021 --discriminator 3840 -t 1000" \ --factoryreset \ ' - - name: Run IM Batch Invoke Test + - name: Run IM Extendable Invoke Test run: | scripts/run_in_python_env.sh out/venv \ './scripts/tests/run_java_test.py \ @@ -141,7 +141,7 @@ jobs: --app-args "--discriminator 3840 --interface-id -1" \ --tool-path out/linux-x64-java-matter-controller \ --tool-cluster "im" \ - --tool-args "onnetwork-long-im-batch-invoke --nodeid 1 --setup-pin-code 20202021 --discriminator 3840 -t 1000" \ + --tool-args "onnetwork-long-im-extendable-invoke --nodeid 1 --setup-pin-code 20202021 --discriminator 3840 -t 1000" \ --factoryreset \ ' - name: Run IM Read Test diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a5398ccb7567a2..dca562c57bf2f1 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -123,12 +123,10 @@ jobs: --known-failure app/util/generic-callback-stubs.cpp \ --known-failure app/util/im-client-callbacks.h \ --known-failure app/util/MatterCallbacks.h \ - --known-failure app/util/message.cpp \ --known-failure app/util/odd-sized-integers.h \ --known-failure app/util/util.cpp \ --known-failure app/util/util.h \ --known-failure app/WriteHandler.h \ - --known-failure lib/core/CHIPVendorIdentifiers.hpp \ --known-failure platform/DeviceSafeQueue.cpp \ --known-failure platform/DeviceSafeQueue.h \ --known-failure platform/GLibTypeDeleter.h \ diff --git a/.github/workflows/smoketest-android.yaml b/.github/workflows/smoketest-android.yaml index 396fb3ae153a46..64efaeb82ff20f 100644 --- a/.github/workflows/smoketest-android.yaml +++ b/.github/workflows/smoketest-android.yaml @@ -39,6 +39,7 @@ jobs: container: image: ghcr.io/project-chip/chip-build-android:35 volumes: + - "/:/runner-root-volume" - "/tmp/log_output:/tmp/test_logs" steps: diff --git a/.github/workflows/unit_integration_test.yaml b/.github/workflows/unit_integration_test.yaml index 62231116bb92c1..15199d4d6532e3 100644 --- a/.github/workflows/unit_integration_test.yaml +++ b/.github/workflows/unit_integration_test.yaml @@ -39,6 +39,7 @@ jobs: container: image: ghcr.io/project-chip/chip-build:35 volumes: + - "/:/runner-root-volume" - "/tmp/log_output:/tmp/test_logs" options: --privileged --sysctl "net.ipv6.conf.all.disable_ipv6=0 net.ipv4.conf.all.forwarding=1 net.ipv6.conf.all.forwarding=1" diff --git a/build/chip/chip_codegen.gni b/build/chip/chip_codegen.gni index 08d9f17da6c1be..07f24bdc3e6bc0 100644 --- a/build/chip/chip_codegen.gni +++ b/build/chip/chip_codegen.gni @@ -35,7 +35,7 @@ template("_chip_build_time_codegen") { include_dirs = [ target_gen_dir ] } - pw_python_action("${_name}_codegen") { + pw_python_action("${_name}_generate") { script = "${chip_root}/scripts/codegen.py" # TODO: this seems to touch internals. Is this ok? speeds up builds! @@ -99,7 +99,7 @@ template("_chip_build_time_codegen") { if (!defined(deps)) { deps = [] } - deps += [ ":${_name}_codegen" ] + deps += [ ":${_name}_generate" ] } } @@ -152,7 +152,7 @@ template("_chip_build_time_zapgen") { _output_subdir = "zap-generated" } - pw_python_action("${_name}_zap") { + pw_python_action("${_name}_generate") { script = "${chip_root}/scripts/tools/zap/generate.py" # TODO: this seems to touch internals. Is this ok? speeds up builds! @@ -211,7 +211,7 @@ template("_chip_build_time_zapgen") { if (!defined(public_deps)) { public_deps = [] } - public_deps += [ ":${_name}_zap" ] + public_deps += [ ":${_name}_generate" ] } } diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn index 01626726e0b9af..7eb09b79c4c22d 100644 --- a/build/config/BUILDCONFIG.gn +++ b/build/config/BUILDCONFIG.gn @@ -76,11 +76,11 @@ if (_chip_defaults.custom_toolchain != "") { } else { _target_compiler = "gcc" } - _default_toolchain = "${_build_overrides.build_root}/toolchain/linux:linux_${target_cpu}_${_target_compiler}" -} else if (target_os == host_os && - (target_cpu == host_cpu || - (target_cpu == "arm64e" && host_cpu == "arm64"))) { +} else if (host_os == "mac" && (target_os == "mac" || target_os == "ios")) { + # On Mac the host toolchain supports building for all mac and ios targets + _default_toolchain = host_toolchain +} else if (target_os == host_os && target_cpu == host_cpu) { _default_toolchain = host_toolchain } else if (target_os == "freertos") { if (_chip_defaults.is_clang) { @@ -113,9 +113,6 @@ if (_chip_defaults.custom_toolchain != "") { } else { assert(false, "Unsupported target_cpu: ${current_cpu}") } -} else if (target_os == "ios") { - _default_toolchain = - "${_build_overrides.build_root}/toolchain/ios:ios_${target_cpu}" } else if (target_os == "tizen") { _default_toolchain = "${_build_overrides.build_root}/toolchain/tizen:tizen_${target_cpu}" diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn index 7ada1348f797b4..df85153297cad7 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn @@ -343,7 +343,7 @@ config("cosmetic_default") { } config("runtime_default") { - if (is_clang) { + if (is_clang) { # Using Pigweed clang instead of Darwin host clang configs = [ "$dir_pw_toolchain/host_clang:no_system_libcpp", "$dir_pw_toolchain/host_clang:xcode_sysroot", @@ -402,7 +402,7 @@ config("sanitize_address") { ] ldflags = cflags - if (target_os == "mac" || target_os == "ios") { + if ((target_os == "mac" || target_os == "ios") && !is_clang) { defines += [ "_LIBCPP_HAS_NO_ASAN" ] } } diff --git a/build/toolchain/ios/BUILD.gn b/build/toolchain/ios/BUILD.gn deleted file mode 100644 index c86dfcb83b38d5..00000000000000 --- a/build/toolchain/ios/BUILD.gn +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright 2020 The Pigweed Authors -# Copyright (c) 2020 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. - -import("//build_overrides/build.gni") - -import("${build_root}/toolchain/gcc_toolchain.gni") - -gcc_toolchain("ios_arm") { - toolchain_args = { - current_os = "ios" - current_cpu = "arm" - is_clang = false - } -} - -gcc_toolchain("ios_arm64") { - toolchain_args = { - current_os = "ios" - current_cpu = "arm64" - is_clang = false - } -} - -gcc_toolchain("ios_x64") { - toolchain_args = { - current_os = "ios" - current_cpu = "x86_64" - is_clang = false - } -} diff --git a/docs/cluster_and_device_type_dev/img/cluster_commands.png b/docs/cluster_and_device_type_dev/img/cluster_commands.png index 959774e071b717..c04dbe7279b28b 100644 Binary files a/docs/cluster_and_device_type_dev/img/cluster_commands.png and b/docs/cluster_and_device_type_dev/img/cluster_commands.png differ diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter index 5a7b510b05b0c8..89823f18e7896f 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter @@ -7761,7 +7761,7 @@ endpoint 1 { callback attribute eventList; callback attribute attributeList; ram attribute featureMap default = 0x0001; - ram attribute clusterRevision default = 5; + ram attribute clusterRevision default = 6; handle command Off; handle command On; @@ -9134,7 +9134,7 @@ endpoint 2 { callback attribute eventList; callback attribute attributeList; ram attribute featureMap default = 0x0001; - ram attribute clusterRevision default = 5; + ram attribute clusterRevision default = 6; handle command Off; handle command On; diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap b/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap index 21bcfee37b5e0d..9dde80962f52f1 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap @@ -6659,7 +6659,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "5", + "defaultValue": "6", "reportable": 1, "minInterval": 0, "maxInterval": 65344, @@ -6895,7 +6895,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "5", + "defaultValue": "6", "reportable": 1, "minInterval": 0, "maxInterval": 65344, @@ -23993,7 +23993,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "5", + "defaultValue": "6", "reportable": 1, "minInterval": 0, "maxInterval": 65344, diff --git a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/WildcardFragment.kt b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/WildcardFragment.kt index 5b49a6d9ab05ab..2ffbec50c746a2 100644 --- a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/WildcardFragment.kt +++ b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/WildcardFragment.kt @@ -16,7 +16,7 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import chip.devicecontroller.ChipDeviceController import chip.devicecontroller.ChipIdLookup -import chip.devicecontroller.InvokeCallback +import chip.devicecontroller.ExtendableInvokeCallback import chip.devicecontroller.ReportCallback import chip.devicecontroller.ResubscriptionAttemptCallback import chip.devicecontroller.SubscriptionEstablishedCallback @@ -26,6 +26,8 @@ import chip.devicecontroller.model.ChipAttributePath import chip.devicecontroller.model.ChipEventPath import chip.devicecontroller.model.ChipPathId import chip.devicecontroller.model.InvokeElement +import chip.devicecontroller.model.InvokeResponseData +import chip.devicecontroller.model.NoInvokeResponseData import chip.devicecontroller.model.NodeState import chip.devicecontroller.model.Status import com.google.chip.chiptool.ChipClient @@ -57,6 +59,8 @@ class WildcardFragment : Fragment() { private val attributePath = ArrayList() private val eventPath = ArrayList() + private val writePath = ArrayList() + private val invokePath = ArrayList() private val subscribeIdList = ArrayList() private val reportCallback = @@ -89,29 +93,47 @@ class WildcardFragment : Fragment() { private val writeAttributeCallback = object : WriteAttributesCallback { + var viewText = "" + override fun onError(attributePath: ChipAttributePath?, ex: Exception?) { Log.e(TAG, "Report error for $attributePath: $ex") + viewText += "onError : $attributePath - $ex\n" } override fun onResponse(attributePath: ChipAttributePath, status: Status) { - val text = "$attributePath : Write response: $status" - requireActivity().runOnUiThread { binding.outputTv.text = text } + viewText += "$attributePath : Write response: $status\n" } override fun onDone() { - Log.i(TAG, "write attribute Done") + requireActivity().runOnUiThread { + binding.outputTv.text = viewText + viewText = "" + } } } - private val invokeCallback = - object : InvokeCallback { + private val extendableInvokeCallback = + object : ExtendableInvokeCallback { + var viewText = "" + override fun onError(e: java.lang.Exception?) { + viewText += "Invoke onError : $e\n" Log.e(TAG, "Report error", e) } - override fun onResponse(invokeElement: InvokeElement?, successCode: Long) { - val text = "Invoke Response : $invokeElement, $successCode" - requireActivity().runOnUiThread { binding.outputTv.text = text } + override fun onResponse(invokeResponseData: InvokeResponseData?) { + viewText += "Invoke Response : $invokeResponseData\n" + } + + override fun onNoResponse(noInvokeResponseData: NoInvokeResponseData?) { + viewText += "Invoke onNoResponse : $noInvokeResponseData\n" + } + + override fun onDone() { + requireActivity().runOnUiThread { + binding.outputTv.text = viewText + viewText = "" + } } } @@ -123,41 +145,27 @@ class WildcardFragment : Fragment() { _binding = WildcardFragmentBinding.inflate(inflater, container, false) scope = viewLifecycleOwner.lifecycleScope - binding.selectTypeRadioGroup.setOnCheckedChangeListener { _, i -> - val readBtnOn = (i == R.id.readRadioBtn) - val subscribeBtnOn = (i == R.id.subscribeRadioBtn) - val writeBtnOn = (i == R.id.writeRadioBtn) - val invokeBtnOn = (i == R.id.invokeRadioBtn) - - binding.addLayout.visibility = getVisibility(readBtnOn || subscribeBtnOn) - binding.attributeIdLabel.visibility = getVisibility(readBtnOn || subscribeBtnOn || writeBtnOn) - binding.attributeIdEd.visibility = getVisibility(readBtnOn || subscribeBtnOn || writeBtnOn) - binding.eventIdLabel.visibility = getVisibility(readBtnOn || subscribeBtnOn) - binding.eventIdEd.visibility = getVisibility(readBtnOn || subscribeBtnOn) - binding.commandIdLabel.visibility = getVisibility(invokeBtnOn) - binding.commandIdEd.visibility = getVisibility(invokeBtnOn) - binding.isUrgentLabel.visibility = getVisibility(subscribeBtnOn) - binding.isUrgentSp.visibility = getVisibility(subscribeBtnOn) - binding.shutdownSubscriptionBtn.visibility = getVisibility(subscribeBtnOn) - } + val writeTypeSpinnerAdapter = + ArrayAdapter( + requireActivity(), + android.R.layout.simple_spinner_dropdown_item, + TLV_MAP.keys.toList() + ) + binding.writeValueTypeSp.adapter = writeTypeSpinnerAdapter - binding.sendBtn.setOnClickListener { - if (binding.readRadioBtn.isChecked) { - showReadDialog() - } else if (binding.subscribeRadioBtn.isChecked) { - showSubscribeDialog() - } else if (binding.writeRadioBtn.isChecked) { - showWriteDialog() - } else if (binding.invokeRadioBtn.isChecked) { - showInvokeDialog() - } + binding.selectTypeRadioGroup.setOnCheckedChangeListener { _, radioBtnId -> + setVisibilityEachView(radioBtnId) } + binding.sendBtn.setOnClickListener { showDialog() } + binding.shutdownSubscriptionBtn.setOnClickListener { showShutdownSubscriptionDialog() } - binding.addAttributeBtn.setOnClickListener { addPathList(ATTRIBUTE) } - binding.addEventBtn.setOnClickListener { addPathList(EVENT) } + binding.addAttributeBtn.setOnClickListener { addPathList(SendType.ATTRIBUTE) } + binding.addEventBtn.setOnClickListener { addPathList(SendType.EVENT) } + binding.addListBtn.setOnClickListener { addRequest() } binding.resetBtn.setOnClickListener { resetPath() } + binding.writeInvokeresetBtn.setOnClickListener { resetPath() } addressUpdateFragment = childFragmentManager.findFragmentById(R.id.addressUpdateFragment) as AddressUpdateFragment @@ -165,6 +173,58 @@ class WildcardFragment : Fragment() { return binding.root } + private fun setVisibilityEachView(radioBtnId: Int) { + val readBtnOn = (radioBtnId == R.id.readRadioBtn) + val subscribeBtnOn = (radioBtnId == R.id.subscribeRadioBtn) + val writeBtnOn = (radioBtnId == R.id.writeRadioBtn) + val invokeBtnOn = (radioBtnId == R.id.invokeRadioBtn) + + binding.addAttributeBtn.visibility = getVisibility(readBtnOn || subscribeBtnOn) + binding.addEventBtn.visibility = getVisibility(readBtnOn || subscribeBtnOn) + binding.resetBtn.visibility = getVisibility(readBtnOn || subscribeBtnOn) + binding.attributeIdLabel.visibility = getVisibility(readBtnOn || subscribeBtnOn || writeBtnOn) + binding.attributeIdEd.visibility = getVisibility(readBtnOn || subscribeBtnOn || writeBtnOn) + binding.eventIdLabel.visibility = getVisibility(readBtnOn || subscribeBtnOn) + binding.eventIdEd.visibility = getVisibility(readBtnOn || subscribeBtnOn) + binding.addListBtn.visibility = getVisibility(writeBtnOn || invokeBtnOn) + binding.commandIdLabel.visibility = getVisibility(invokeBtnOn) + binding.commandIdEd.visibility = getVisibility(invokeBtnOn) + binding.isUrgentLabel.visibility = getVisibility(subscribeBtnOn) + binding.isUrgentSp.visibility = getVisibility(subscribeBtnOn) + binding.writeValueLabel.visibility = getVisibility(writeBtnOn) + binding.writeValueEd.visibility = getVisibility(writeBtnOn) + binding.writeValueTypeLabel.visibility = getVisibility(writeBtnOn) + binding.writeValueTypeSp.visibility = getVisibility(writeBtnOn) + binding.dataVersionLabel.visibility = getVisibility(writeBtnOn) + binding.dataVersionEd.visibility = getVisibility(writeBtnOn) + binding.invokeValueLabel.visibility = getVisibility(invokeBtnOn) + binding.invokeValueEd.visibility = getVisibility(invokeBtnOn) + binding.shutdownSubscriptionBtn.visibility = getVisibility(subscribeBtnOn) + binding.writeInvokeresetBtn.visibility = getVisibility(writeBtnOn || invokeBtnOn) + + resetPath() + } + + private fun showDialog() { + if (binding.readRadioBtn.isChecked) { + showReadDialog() + } else if (binding.subscribeRadioBtn.isChecked) { + showSubscribeDialog() + } else if (binding.writeRadioBtn.isChecked) { + showWriteDialog() + } else if (binding.invokeRadioBtn.isChecked) { + showInvokeDialog() + } + } + + private fun addRequest() { + if (binding.writeRadioBtn.isChecked) { + addWriteRequest() + } else { + addInvokeRequest() + } + } + private fun getVisibility(isShowing: Boolean): Int { return if (isShowing) { View.VISIBLE @@ -173,7 +233,7 @@ class WildcardFragment : Fragment() { } } - private fun addPathList(type: Int) { + private fun addPathList(type: SendType) { val endpointId = getChipPathIdForText(binding.endpointIdEd.text.toString()) val clusterId = getChipPathIdForText(binding.clusterIdEd.text.toString()) val attributeId = getChipPathIdForText(binding.attributeIdEd.text.toString()) @@ -183,9 +243,9 @@ class WildcardFragment : Fragment() { (binding.subscribeRadioBtn.isChecked) && (binding.isUrgentSp.selectedItem.toString().toBoolean()) - if (type == ATTRIBUTE) { + if (type == SendType.ATTRIBUTE) { attributePath.add(ChipAttributePath.newInstance(endpointId, clusterId, attributeId)) - } else if (type == EVENT) { + } else if (type == SendType.EVENT) { eventPath.add(ChipEventPath.newInstance(endpointId, clusterId, eventId, isUrgent)) } updateAddListView() @@ -194,6 +254,8 @@ class WildcardFragment : Fragment() { private fun resetPath() { attributePath.clear() eventPath.clear() + writePath.clear() + invokePath.clear() updateAddListView() } @@ -205,6 +267,12 @@ class WildcardFragment : Fragment() { for (event in eventPath) { builder.append("event($event)\n") } + for (write in writePath) { + builder.append("WritePath($write)\n") + } + for (invoke in invokePath) { + builder.append("InvokePath($invoke)\n") + } binding.sendListView.text = builder.toString() } @@ -312,13 +380,11 @@ class WildcardFragment : Fragment() { ) } - private suspend fun write( - writeValueType: String, - writeValue: String, - dataVersion: Int?, - timedRequestTimeoutMs: Int, - imTimeoutMs: Int - ) { + private fun addWriteRequest() { + val writeValue = binding.writeValueEd.text.toString() + val writeValueType = binding.writeValueTypeSp.selectedItem.toString() + val dataVersion = binding.dataVersionEd.text.toString() + val endpointId = if (!addressUpdateFragment.isGroupId()) { getChipPathIdForText(binding.endpointIdEd.text.toString()) @@ -329,10 +395,10 @@ class WildcardFragment : Fragment() { val attributeId = getChipPathIdForText(binding.attributeIdEd.text.toString()) val version = - if (dataVersion == null) { + if (dataVersion.isEmpty()) { Optional.empty() } else { - Optional.of(dataVersion) + Optional.of(dataVersion.toInt()) } lateinit var writeRequest: AttributeWriteRequest @@ -364,26 +430,15 @@ class WildcardFragment : Fragment() { version ) } - val devicePtr = - try { - addressUpdateFragment.getDevicePointer(requireContext()) - } catch (e: IllegalStateException) { - Log.d(TAG, "getDevicePointer exception", e) - return - } - deviceController.write( - writeAttributeCallback, - devicePtr, - listOf(writeRequest), - timedRequestTimeoutMs, - imTimeoutMs - ) + writePath.add(writeRequest) + updateAddListView() } - private suspend fun invoke(invokeJson: String, timedRequestTimeoutMs: Int, imTimeoutMs: Int) { + private fun addInvokeRequest() { val endpointId = getChipPathIdForText(binding.endpointIdEd.text.toString()) val clusterId = getChipPathIdForText(binding.clusterIdEd.text.toString()) val commandId = getChipPathIdForText(binding.commandIdEd.text.toString()) + val invokeJson = binding.invokeValueEd.text.toString() val jsonString = invokeJson.ifEmpty { "{}" } val invokeElement = @@ -398,6 +453,28 @@ class WildcardFragment : Fragment() { } else { InvokeElement.newInstance(endpointId, clusterId, commandId, null, jsonString) } + invokePath.add(invokeElement) + updateAddListView() + } + + private suspend fun write(timedRequestTimeoutMs: Int, imTimeoutMs: Int) { + val devicePtr = + try { + addressUpdateFragment.getDevicePointer(requireContext()) + } catch (e: IllegalStateException) { + Log.d(TAG, "getDevicePointer exception", e) + return + } + deviceController.write( + writeAttributeCallback, + devicePtr, + writePath, + timedRequestTimeoutMs, + imTimeoutMs + ) + } + + private suspend fun invoke(timedRequestTimeoutMs: Int, imTimeoutMs: Int) { val devicePtr = try { addressUpdateFragment.getDevicePointer(requireContext()) @@ -405,10 +482,10 @@ class WildcardFragment : Fragment() { Log.d(TAG, "getDevicePointer exception", e) return } - deviceController.invoke( - invokeCallback, + deviceController.extendableInvoke( + extendableInvokeCallback, devicePtr, - invokeElement, + invokePath, timedRequestTimeoutMs, imTimeoutMs ) @@ -453,31 +530,14 @@ class WildcardFragment : Fragment() { private fun showWriteDialog() { binding.outputTv.text = "" val dialogView = requireActivity().layoutInflater.inflate(R.layout.write_dialog, null) - val writeValueTypeSp = dialogView.findViewById(R.id.writeValueTypeSp) - val spinnerAdapter = - ArrayAdapter( - requireActivity(), - android.R.layout.simple_spinner_dropdown_item, - TLV_MAP.keys.toList() - ) - writeValueTypeSp.adapter = spinnerAdapter val dialog = AlertDialog.Builder(requireContext()).apply { setView(dialogView) }.create() dialogView.findViewById