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 a58617131a035d..0602b735d36b88 100644 --- a/.github/workflows/darwin.yaml +++ b/.github/workflows/darwin.yaml @@ -119,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 f19d451fbc2df6..8d21ca0bd4c56d 100644 --- a/.github/workflows/java-tests.yaml +++ b/.github/workflows/java-tests.yaml @@ -132,6 +132,17 @@ jobs: --tool-cluster "im" \ --tool-args "onnetwork-long-im-invoke --nodeid 1 --setup-pin-code 20202021 --discriminator 3840 -t 1000" \ --factoryreset \ + ' + - name: Run IM Extendable Invoke Test + run: | + scripts/run_in_python_env.sh out/venv \ + './scripts/tests/run_java_test.py \ + --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app \ + --app-args "--discriminator 3840 --interface-id -1" \ + --tool-path out/linux-x64-java-matter-controller \ + --tool-cluster "im" \ + --tool-args "onnetwork-long-im-extendable-invoke --nodeid 1 --setup-pin-code 20202021 --discriminator 3840 -t 1000" \ + --factoryreset \ ' - name: Run IM Read Test run: | diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 69613217033e8b..a5398ccb7567a2 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -96,7 +96,6 @@ jobs: --known-failure app/CommandHandler.h \ --known-failure app/CommandHandlerInterface.h \ --known-failure app/CommandSenderLegacyCallback.h \ - --known-failure app/CompatEnumNames.h \ --known-failure app/data-model/ListLargeSystemExtensions.h \ --known-failure app/EventHeader.h \ --known-failure app/EventLoggingTypes.h \ @@ -114,7 +113,6 @@ jobs: --known-failure app/util/attribute-table.h \ --known-failure app/util/binding-table.cpp \ --known-failure app/util/binding-table.h \ - --known-failure app/util/common.h \ --known-failure app/util/config.h \ --known-failure app/util/DataModelHandler.cpp \ --known-failure app/util/DataModelHandler.h \ @@ -205,7 +203,7 @@ jobs: # TODO: TLVDebug should ideally not be excluded here. # TODO: protocol_decoder.cpp should ideally not be excluded here. # TODO: PersistentStorageMacros.h should ideally not be excluded here. - git grep -I -n "PRI.64" -- './*' ':(exclude).github/workflows/lint.yml' ':(exclude)examples/chip-tool' ':(exclude)examples/tv-casting-app' ':(exclude)src/app/MessageDef/MessageDefHelper.cpp' ':(exclude)src/app/tests/integration/chip_im_initiator.cpp' ':(exclude)src/lib/core/TLVDebug.cpp' ':(exclude)src/lib/dnssd/tests/TestTxtFields.cpp' ':(exclude)src/lib/format/protocol_decoder.cpp' ':(exclude)src/lib/support/PersistentStorageMacros.h' ':(exclude)src/messaging/tests/echo/echo_requester.cpp' ':(exclude)src/platform/Linux' ':(exclude)src/platform/Ameba' ':(exclude)src/platform/ESP32' ':(exclude)src/platform/webos' ':(exclude)zzz_generated/chip-tool' ':(exclude)src/tools/chip-cert/Cmd_PrintCert.cpp' && exit 1 || exit 0 + git grep -I -n "PRI.64" -- './*' ':(exclude).github/workflows/lint.yml' ':(exclude)examples/chip-tool' ':(exclude)examples/tv-casting-app' ':(exclude)src/app/MessageDef/MessageDefHelper.cpp' ':(exclude)src/app/tests/integration/chip_im_initiator.cpp' ':(exclude)src/lib/core/TLVDebug.cpp' ':(exclude)src/lib/dnssd/tests/TestTxtFields.cpp' ':(exclude)src/lib/format/protocol_decoder.cpp' ':(exclude)src/lib/support/PersistentStorageMacros.h' ':(exclude)src/messaging/tests/echo/echo_requester.cpp' ':(exclude)src/platform/Linux' ':(exclude)src/platform/Ameba' ':(exclude)src/platform/ESP32' ':(exclude)src/platform/Darwin' ':(exclude)src/darwin' ':(exclude)src/platform/webos' ':(exclude)zzz_generated/chip-tool' ':(exclude)src/tools/chip-cert/Cmd_PrintCert.cpp' && exit 1 || exit 0 # git grep exits with 0 if it finds a match, but we want # to fail (exit nonzero) on match. And we want to exclude this file, 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 02acd08150aae8..08d9f17da6c1be 100644 --- a/build/chip/chip_codegen.gni +++ b/build/chip/chip_codegen.gni @@ -152,7 +152,7 @@ template("_chip_build_time_zapgen") { _output_subdir = "zap-generated" } - pw_python_action("${_name}_zap_pregen") { + pw_python_action("${_name}_zap") { script = "${chip_root}/scripts/tools/zap/generate.py" # TODO: this seems to touch internals. Is this ok? speeds up builds! @@ -165,7 +165,7 @@ template("_chip_build_time_zapgen") { "--templates", _template_path, "--output-dir", - rebase_path(target_gen_dir) + "/zap_pregen/" + _output_subdir, + rebase_path(target_gen_dir) + "/zapgen/" + _output_subdir, # TODO: lock file support should be removed as this serializes zap # (slower), however this is currently done because on Darwin zap startup @@ -188,54 +188,10 @@ template("_chip_build_time_zapgen") { sources = [ _idl_file ] - outputs = [] - foreach(name, invoker.outputs) { - outputs += [ "${target_gen_dir}/zap_pregen/${name}" ] - } - - forward_variables_from(invoker, [ "prune_outputs" ]) - if (defined(prune_outputs)) { - foreach(name, prune_outputs) { - outputs += [ "${target_gen_dir}/zap_pregen/${name}" ] - } - } - } - - # This action ensures that any "extra" files generated by zap codegen - # are actually deleted. - # - # This is to avoid double-codegen of configurations like endpoint config - # or access credentials being generated for both "controller client" and - # application-specific - pw_python_action("${_name}_files") { - # TODO: this seems to touch internals. Is this ok? speeds up builds! - _pw_internal_run_in_venv = false - - script = "${chip_root}/scripts/tools/zap/prune_outputs.py" - - _keep_file = rebase_path("${target_gen_dir}/${_name}.keep.outputs") - write_file(_keep_file, invoker.outputs, "list lines") - - args = [ - "--keep", - _keep_file, - "--input-dir", - rebase_path("${target_gen_dir}/zap_pregen/"), - "--output-dir", - rebase_path("${target_gen_dir}/zapgen/"), - ] - - inputs = [] - foreach(name, invoker.outputs) { - inputs += [ "${target_gen_dir}/zap_pregen/${name}" ] - } - outputs = [] foreach(name, invoker.outputs) { outputs += [ "${target_gen_dir}/zapgen/${name}" ] } - - deps = [ ":${_name}_zap_pregen" ] } source_set(_name) { @@ -255,10 +211,7 @@ template("_chip_build_time_zapgen") { if (!defined(public_deps)) { public_deps = [] } - public_deps += [ - ":${_name}_files", - ":${_name}_zap_pregen", - ] + public_deps += [ ":${_name}_zap" ] } } @@ -420,7 +373,6 @@ template("chip_zapgen") { "input", "outputs", "public_configs", - "prune_outputs", ]) } } else { @@ -482,7 +434,6 @@ template("chip_zapgen") { [ "deps", "public_configs", - "prune_outputs", ]) if (!defined(public_configs)) { public_configs = [] 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/config/esp32/components/chip/CMakeLists.txt b/config/esp32/components/chip/CMakeLists.txt index 7f62ce6cc0087a..14594a03b74352 100644 --- a/config/esp32/components/chip/CMakeLists.txt +++ b/config/esp32/components/chip/CMakeLists.txt @@ -131,6 +131,25 @@ endif() if(CONFIG_ENABLE_ICD_SERVER) chip_gn_arg_append("chip_enable_icd_server" "true") + if(CONFIG_ICD_ENFORCE_SIT_SLOW_POLL_LIMIT) + chip_gn_arg_append("icd_enforce_sit_slow_poll_limit" "true") + endif() + if(CONFIG_ICD_REPORT_ON_ACTIVE_MODE) + chip_gn_arg_append("chip_icd_report_on_active_mode" "true") + endif() + if(CONFIG_ENABLE_ICD_LIT) + chip_gn_arg_append("chip_enable_icd_lit" "true") + if(CONFIG_ENABLE_ICD_CIP) + chip_gn_arg_append("chip_enable_icd_checkin" "true") + else() + chip_gn_arg_append("chip_enable_icd_checkin" "false") + endif() + if(CONFIG_ENABLE_ICD_USER_ACTIVE_MODE_TRIGGER) + chip_gn_arg_append("chip_enable_icd_user_active_mode_trigger" "true") + else() + chip_gn_arg_append("chip_enable_icd_user_active_mode_trigger" "false") + endif() + endif() endif() if(CONFIG_ENABLE_PW_RPC) diff --git a/config/esp32/components/chip/Kconfig b/config/esp32/components/chip/Kconfig index 0dce48b5f720be..b39105d1285fac 100644 --- a/config/esp32/components/chip/Kconfig +++ b/config/esp32/components/chip/Kconfig @@ -340,13 +340,27 @@ menu "CHIP Device Layer" help Enables or Disables the support for Commissionable Device Type. - config ENABLE_ICD_SERVER + menuconfig ENABLE_ICD_SERVER bool "Enable ICD server" depends on OPENTHREAD_MTD default n help Enables or Disables ICD server + config ICD_ENFORCE_SIT_SLOW_POLL_LIMIT + bool "Enforce SIT Slow Polling Max value to 15 seconds" + depends on ENABLE_ICD_SERVER + default n + help + Set to true to enforce SIT Slow Polling Max value to 15seconds + + config ICD_REPORT_ON_ACTIVE_MODE + bool "Emit a report on entering active mode" + depends on ENABLE_ICD_SERVER + default n + help + Make the ICD manager emit a report on entering active mode + config ICD_SLOW_POLL_INTERVAL_MS int "ICD Slow Polling Interval(ms)" depends on ENABLE_ICD_SERVER @@ -389,14 +403,48 @@ menu "CHIP Device Layer" This value indicates the minimum amount of time in milliseconds the server typically will stay active after network activity when in active mode. + config ENABLE_ICD_LIT + bool "Enable Long Idle Time ICD" + depends on ENABLE_ICD_SERVER + default n + help + Enables or Disables LIT ICD + + config ENABLE_ICD_CIP + bool "Enable Check-in protocol" + depends on ENABLE_ICD_LIT + default y + help + Enables or Disables ICD Check-in protocol + config ICD_CLIENTS_SUPPORTED_PER_FABRIC int "ICD Clients Number Supported Per Fabric" - depends on ENABLE_ICD_SERVER + depends on ENABLE_ICD_CIP default 1 help This value indicates the maximum number of entries that the ICD server is able to store for each fabric in the RegisteredClients attribute. + config ICD_MAX_NOTIFICATION_SUBSCRIBERS + int "Max ICD notification subscribers" + depends on ENABLE_ICD_SERVER + default 1 + help + The ICDManager implements the ICDListener functions and is always subscribed to the ICDNotifier + This allows other Matter modules to inform the ICDManager that it needs to go and may have to stay in Active Mode, + outside of its standard ActiveModeDuration and IdleModeDuration, without being tightly coupled the application + data model + + This implementation also allows other modules to implement an ICDListener and subscribe to ICDNotifier + to couple behaviours with the ICD cycles. In such cases, ICD_MAX_NOTIFICATION_SUBSCRIBERS need to be adjusted + + config ENABLE_ICD_USER_ACTIVE_MODE_TRIGGER + bool "Enable User Active Mode Trigger feature" + depends on ENABLE_ICD_LIT + default y + help + Enables or Disables ICD User Active Mode Trigger feature + config ENABLE_BG_EVENT_PROCESSING bool "Enable Background event processing" default n diff --git a/data_model/clusters/DoorLock.xml b/data_model/clusters/DoorLock.xml index 6284b973eb63e1..16d26420fe2009 100644 --- a/data_model/clusters/DoorLock.xml +++ b/data_model/clusters/DoorLock.xml @@ -57,7 +57,7 @@ Davis, CA 95616, USA :xrefstyle: basic --> - + @@ -66,7 +66,6 @@ Davis, CA 95616, USA - @@ -104,19 +103,14 @@ Davis, CA 95616, USA - - - - - - - - - - - - - + + + + + + + + @@ -130,14 +124,6 @@ Davis, CA 95616, USA - - - - - - - - @@ -220,23 +206,6 @@ Davis, CA 95616, USA - - - - - - - - - - - - - - - @@ -370,21 +339,6 @@ Davis, CA 95616, USA - - - - - - - - - - - - - - - @@ -533,11 +487,6 @@ Davis, CA 95616, USA - - - - - @@ -1109,81 +1058,6 @@ Davis, CA 95616, USA - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -2059,13 +1933,6 @@ Davis, CA 95616, USA - - - - - - - @@ -2092,36 +1959,6 @@ Davis, CA 95616, USA - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/data_model/clusters/ICDManagement.xml b/data_model/clusters/ICDManagement.xml index 2a770a4f97f2b4..2de6842d1e0dd9 100644 --- a/data_model/clusters/ICDManagement.xml +++ b/data_model/clusters/ICDManagement.xml @@ -57,10 +57,9 @@ Davis, CA 95616, USA // Update Name --> - + - @@ -69,26 +68,12 @@ Davis, CA 95616, USA - - - - - - - - - - - + - - - - @@ -153,25 +138,6 @@ Davis, CA 95616, USA - - - - - - - - - - - - - - - - - - - diff --git a/data_model/clusters/Messages.xml b/data_model/clusters/Messages.xml index cea28ac15f54c9..5bc3ce81fa2a0d 100644 --- a/data_model/clusters/Messages.xml +++ b/data_model/clusters/Messages.xml @@ -216,7 +216,7 @@ Davis, CA 95616, USA - + diff --git a/data_model/clusters/NetworkCommissioningCluster.xml b/data_model/clusters/NetworkCommissioningCluster.xml index 86c7a01ce9f951..745a659f1b900b 100644 --- a/data_model/clusters/NetworkCommissioningCluster.xml +++ b/data_model/clusters/NetworkCommissioningCluster.xml @@ -59,8 +59,7 @@ Davis, CA 95616, USA - + @@ -76,11 +75,6 @@ Added support for Wi-Fi Per-Device Credentials (PDC feature; QueryIdentity and Q - - - - - @@ -177,9 +171,6 @@ Added support for Wi-Fi Per-Device Credentials (PDC feature; QueryIdentity and Q - - - @@ -189,22 +180,6 @@ Added support for Wi-Fi Per-Device Credentials (PDC feature; QueryIdentity and Q - - - - - - - - - - - - - - - - @@ -431,18 +406,6 @@ Added support for Wi-Fi Per-Device Credentials (PDC feature; QueryIdentity and Q - - - - - - - - - - - - @@ -492,18 +455,6 @@ Added support for Wi-Fi Per-Device Credentials (PDC feature; QueryIdentity and Q - - - - - - - - - - - - @@ -559,32 +510,5 @@ Added support for Wi-Fi Per-Device Credentials (PDC feature; QueryIdentity and Q - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/docs/cluster_and_device_type_dev/cluster_and_device_type_dev.md b/docs/cluster_and_device_type_dev/cluster_and_device_type_dev.md index 9e34e961ae6d87..ca8e295a1cabf9 100644 --- a/docs/cluster_and_device_type_dev/cluster_and_device_type_dev.md +++ b/docs/cluster_and_device_type_dev/cluster_and_device_type_dev.md @@ -238,7 +238,9 @@ pure CommandHandlerInterface implementations. - **MatterReportingAttributeChangeCallback** - **Events** - No direct ember support - - Call LogEvent function in EventLogging.h + - Call LogEvent function in EventLogging.h. Caller has to either lock the + Matter stack lock or queue the event to the Matter event queue when + using LogEvent. #### A note on Dynamic Endpoints 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/air-quality-sensor-app/linux/README.md b/examples/air-quality-sensor-app/linux/README.md index 73139ef4aeeacb..38a0adfb2fe7bf 100644 --- a/examples/air-quality-sensor-app/linux/README.md +++ b/examples/air-quality-sensor-app/linux/README.md @@ -165,3 +165,21 @@ value. ``` $ echo '{"Name":"NitrogenDioxideConcentrationMeasurement","NewValue":1}' > /tmp/chip_air_quality_fifo_ ``` + +Generate event `Pm1ConcentrationMeasurement`, to change the PM1 value. + +``` +echo '{"Name":"Pm1ConcentrationMeasurement","NewValue":1}' > /tmp/chip_air_quality_fifo_ +``` + +Generate event `Pm25ConcentrationMeasurement`, to change the PM2.5 value. + +``` +echo '{"Name":"Pm25ConcentrationMeasurement","NewValue":2.5}' > /tmp/chip_air_quality_fifo_ +``` + +Generate event `Pm10ConcentrationMeasurement`, to change the PM10 value. + +``` +echo '{"Name":"Pm10ConcentrationMeasurement","NewValue":10}' > /tmp/chip_air_quality_fifo_ +``` 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