diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 4c8a11fd42f7f1..da4e2c4171171e 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -43,7 +43,7 @@ RUN apt-get update \ && : RUN groupadd -g $USER_GID $USERNAME \ - && useradd -s /bin/bash -u $USER_UID -g $USER_GID -G docker,sudo -m $USERNAME \ + && useradd --no-log-init -s /bin/bash -u $USER_UID -g $USER_GID -G docker,sudo -m $USERNAME \ && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \ && chmod 0440 /etc/sudoers.d/$USERNAME \ && : diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b55417cdba78ed..8c618416fba4c1 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -54,7 +54,6 @@ updates: - dependency-name: "third_party/open-iot-sdk/repo" - dependency-name: "third_party/ot-br-posix/repo" - dependency-name: "third_party/perfetto/repo" - - dependency-name: "third_party/pybind11/repo" - dependency-name: "third_party/qpg_sdk/repo" - dependency-name: "third_party/silabs/repo" - dependency-name: "third_party/simw-top-mini/repo" diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 3ec8fd379c3fe7..58e6d14cd8bb0e 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -308,7 +308,7 @@ jobs: - name: Setup Build, Run Build and Run Tests run: | - scripts/build/gn_gen.sh --args="enable_rtti=true enable_pylib=true chip_config_memory_debug_checks=false chip_config_memory_debug_dmalloc=false chip_generate_link_map_file=false" + scripts/build/gn_gen.sh --args="enable_rtti=true chip_config_memory_debug_checks=false chip_config_memory_debug_dmalloc=false chip_generate_link_map_file=false" scripts/run_in_build_env.sh "ninja -C ./out" scripts/tests/gn_tests.sh - name: Setup test python environment @@ -414,7 +414,6 @@ jobs: # "host clang" build, which uses the pigweed # clang. "default") GN_ARGS='target_os="all" is_asan=true enable_host_clang_build=false';; - "python_lib") GN_ARGS='enable_rtti=true enable_pylib=true';; esac BUILD_TYPE=$BUILD_TYPE scripts/build/gn_gen.sh --args="$GN_ARGS" --export-compile-commands scripts/run_in_build_env.sh "ninja -C ./out/$BUILD_TYPE" diff --git a/.github/workflows/third-party-check.yaml b/.github/workflows/third-party-check.yaml index 575e7e3c9c7137..beda0af41b0622 100644 --- a/.github/workflows/third-party-check.yaml +++ b/.github/workflows/third-party-check.yaml @@ -17,7 +17,7 @@ name: Check for Unintentional Submodule Updates on: pull_request: branches-ignore: - - 'dependabot/**' + - "dependabot/**" paths: - "third_party/**" - ".gitmodules" @@ -26,9 +26,14 @@ jobs: check-submodule-update-label: name: Check For Submodule Update Label runs-on: ubuntu-latest - if: "!contains(github.event.pull_request.labels.*.name, 'changing-submodules-on-purpose')" steps: - - name: Error Message - run: echo This pull request attempts to update submodules without the changing-submodules-on-purpose label. Please apply that label if the changes are intentional, or remove those changes. - - name: Fail Job - run: exit 1 + - if: ${{ !contains(github.event.pull_request.labels.*.name, 'changing-submodules-on-purpose') }} + name: Fail + run: | + echo This pull request attempts to update submodules without the changing-submodules-on-purpose label. Please apply that label if the changes are intentional, or remove those changes. + exit 1 + - if: ${{ contains(github.event.pull_request.labels.*.name, 'changing-submodules-on-purpose') }} + name: Success + run: | + echo PR looks good. + exit 0 diff --git a/.gitmodules b/.gitmodules index 1f4129f9f2df9d..0b015f8d14cd16 100644 --- a/.gitmodules +++ b/.gitmodules @@ -165,10 +165,6 @@ path = third_party/infineon/psoc6/psoc6_sdk/libs/recipe-make-cat1a url = https://github.com/Infineon/recipe-make-cat1a platforms = infineon -[submodule "third_party/pybind11/repo"] - path = third_party/pybind11/repo - url = https://github.com/pybind/pybind11 - branch = stable [submodule "p6/lwip"] path = third_party/infineon/psoc6/psoc6_sdk/libs/lwip url = https://github.com/lwip-tcpip/lwip.git diff --git a/BUILD.gn b/BUILD.gn index 8a78d8f55f3bd0..c8e41976599173 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -44,7 +44,6 @@ if (current_toolchain != "${dir_pw_toolchain}/default:default") { chip_enable_python_modules = (current_os == "mac" || current_os == "linux") && (host_cpu == "x64" || host_cpu == "arm64" || host_cpu == "arm") - enable_pylib = false # Build the Linux all clusters app example with default group chip_build_all_clusters_app = false @@ -112,9 +111,6 @@ if (current_toolchain != "${dir_pw_toolchain}/default:default") { "${chip_root}/src/controller/python:chip-repl", "${chip_root}/src/python_testing/matter_testing_infrastructure:metadata_parser.wheel", ] - if (enable_pylib) { - deps += [ "${chip_root}/src/pybindings/pycontroller" ] - } } } @@ -214,9 +210,6 @@ if (current_toolchain != "${dir_pw_toolchain}/default:default") { data_deps = [ "${chip_root}/examples/chip-tool" ] if (chip_enable_python_modules) { - if (enable_pylib) { - data_deps += [ "${chip_root}/src/pybindings/pycontroller" ] - } data_deps += [ "${chip_root}/src/controller/python:chip-repl" ] } diff --git a/config/esp32/components/chip/CMakeLists.txt b/config/esp32/components/chip/CMakeLists.txt index ea2316851b534b..7e9894a8d66065 100644 --- a/config/esp32/components/chip/CMakeLists.txt +++ b/config/esp32/components/chip/CMakeLists.txt @@ -483,6 +483,12 @@ endif() if (NOT EXECUTABLE_COMPONENT_NAME) set(EXECUTABLE_COMPONENT_NAME "main") endif() + +if (CONFIG_ENABLE_DELTA_OTA) + idf_component_get_property(esp_delta_ota_lib espressif__esp_delta_ota COMPONENT_LIB) + list(APPEND chip_libraries $) +endif() + idf_component_get_property(main_lib ${EXECUTABLE_COMPONENT_NAME} COMPONENT_LIB) list(APPEND chip_libraries $) diff --git a/config/esp32/components/chip/Kconfig b/config/esp32/components/chip/Kconfig index 5cd2700bb972b6..d3f621f98f4024 100644 --- a/config/esp32/components/chip/Kconfig +++ b/config/esp32/components/chip/Kconfig @@ -256,6 +256,15 @@ menu "CHIP Core" help Enable this option to use the pre encrypted OTA image + config ENABLE_DELTA_OTA + bool "Enable delta OTA" + depends on ENABLE_OTA_REQUESTOR + default n + help + Enable this option for delta OTA image updates. + Delta OTA updates allow for smaller, more efficient updates by only + sending the changes between the current and new firmware versions. + config OTA_AUTO_REBOOT_ON_APPLY bool "Reboot the device after applying the OTA image" depends on ENABLE_OTA_REQUESTOR diff --git a/config/esp32/components/chip/idf_component.yml b/config/esp32/components/chip/idf_component.yml index 8ef6302c5dbbeb..b56040ea3aaa07 100644 --- a/config/esp32/components/chip/idf_component.yml +++ b/config/esp32/components/chip/idf_component.yml @@ -30,3 +30,9 @@ dependencies: version: "1.0.3" rules: - if: "idf_version >=5.0" + + espressif/esp_delta_ota: + version: "^1.1.0" + require: public + rules: + - if: "idf_version >=4.3" diff --git a/config/nxp/chip-cmake-freertos/CMakeLists.txt b/config/nxp/chip-cmake-freertos/CMakeLists.txt new file mode 100644 index 00000000000000..b639e23ff65034 --- /dev/null +++ b/config/nxp/chip-cmake-freertos/CMakeLists.txt @@ -0,0 +1,62 @@ +# +# 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(ExternalProject) + +if(NOT CHIP_ROOT) + get_filename_component(CHIP_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../../.. REALPATH) +endif() + +get_filename_component(GN_ROOT_TARGET ${CHIP_ROOT}/config/nxp/chip-gn-freertos REALPATH) + +include(${CHIP_ROOT}/config/nxp/cmake/common.cmake) +include(${CHIP_ROOT}/src/app/chip_data_model.cmake) + +# Prepare compiler flags +matter_add_cflags(${CMAKE_C_FLAGS}) +matter_add_cxxflags(${CMAKE_CXX_FLAGS}) + +matter_get_compiler_flags_from_targets("${CONFIG_CHIP_EXTERNAL_TARGETS}") + +# ============================================================================== +# Generate configuration for CHIP GN build system +# ============================================================================== +matter_add_gn_arg_string("nxp_nvm_component" ${CONFIG_CHIP_NVM_COMPONENT}) +matter_add_gn_arg_string("nxp_platform" ${CONFIG_CHIP_NXP_PLATFORM}) +matter_add_gn_arg("chip_with_factory_data" ${CONFIG_CHIP_FACTORY_DATA}) +matter_add_gn_arg("chip_enable_secure_dac_private_key_storage" ${CONFIG_CHIP_SECURE_DAC_PRIVATE_KEY_STORAGE}) +matter_add_gn_arg("chip_enable_secure_whole_factory_data" ${CONFIG_CHIP_ENABLE_SECURE_WHOLE_FACTORY_DATA}) + +matter_common_gn_args( + DEBUG CONFIG_DEBUG + LIB_SHELL CONFIG_CHIP_LIB_SHELL + LIB_TESTS CONFIG_CHIP_BUILD_TESTS + PROJECT_CONFIG ${CONFIG_CHIP_PROJECT_CONFIG} + PROJECT_CONFIG_INC_DIR ${CONFIG_CHIP_PROJECT_CONFIG_INCLUDE_DIRS} + DEVICE_INFO_EXAMPLE_PROVIDER CONFIG_CHIP_EXAMPLE_DEVICE_INFO_PROVIDER +) + +matter_generate_args_tmp_file() + +# ============================================================================== +# Build chip library +# ============================================================================== +matter_build(chip + LIB_SHELL ${CONFIG_CHIP_LIB_SHELL} + LIB_TESTS ${CONFIG_CHIP_BUILD_TESTS} + DEVICE_INFO_EXAMPLE_PROVIDER ${CONFIG_CHIP_EXAMPLE_DEVICE_INFO_PROVIDER} + GN_DEPENDENCIES ${CONFIG_GN_DEPENDENCIES} +) diff --git a/config/nxp/chip-gn-freertos/.gn b/config/nxp/chip-gn-freertos/.gn new file mode 100644 index 00000000000000..15dde062d89de3 --- /dev/null +++ b/config/nxp/chip-gn-freertos/.gn @@ -0,0 +1,29 @@ +# 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. + +import("//build_overrides/build.gni") +import("//build_overrides/chip.gni") + +# The location of the build configuration file. +buildconfig = "//build/config/BUILDCONFIG.gn" + +# CHIP uses angle bracket includes. +check_system_includes = true + +default_args = { + target_cpu = "arm" + target_os = "freertos" + + import("${chip_root}/config/nxp/chip-gn-freertos/args.gni") +} diff --git a/config/nxp/chip-gn-freertos/BUILD.gn b/config/nxp/chip-gn-freertos/BUILD.gn new file mode 100644 index 00000000000000..3c508666c833d1 --- /dev/null +++ b/config/nxp/chip-gn-freertos/BUILD.gn @@ -0,0 +1,42 @@ +# 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. + +import("//build_overrides/build.gni") +import("//build_overrides/chip.gni") + +import("${chip_root}/build/chip/tests.gni") + +assert(current_os == "freertos") + +declare_args() { + chip_build_example_providers = false + chip_enable_ethernet = false + chip_malloc_sys_heap = false +} + +group("nxp_freertos") { + deps = [ "${chip_root}/src/lib" ] + + if (chip_build_tests) { + deps += [ "${chip_root}/src:tests" ] + } + + if (chip_build_example_providers) { + deps += [ "${chip_root}/examples/providers:device_info_provider" ] + } +} + +group("default") { + deps = [ ":nxp_freertos" ] +} diff --git a/config/nxp/chip-gn-freertos/args.gni b/config/nxp/chip-gn-freertos/args.gni new file mode 100644 index 00000000000000..be31f22fcbdcf1 --- /dev/null +++ b/config/nxp/chip-gn-freertos/args.gni @@ -0,0 +1,31 @@ +# 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. + +import("//build_overrides/chip.gni") +import("//build_overrides/nxp_sdk.gni") + +nxp_external_sdk = true +nxp_build_matter_standalone_lib = true +chip_device_platform = "nxp" +lwip_platform = "nxp" + +# project config files +chip_device_project_config_include = "" +chip_project_config_include = "" +chip_inet_project_config_include = "" +chip_system_project_config_include = "" +chip_ble_project_config_include = "" + +# mbedtls is built as part of the NXP SDK build process +mbedtls_target = "${nxp_sdk_build_root}:nxp_mbedtls" diff --git a/config/nxp/chip-module/CMakeLists.txt b/config/nxp/chip-module/CMakeLists.txt index 6b8266970dc883..32a85a606c71e0 100644 --- a/config/nxp/chip-module/CMakeLists.txt +++ b/config/nxp/chip-module/CMakeLists.txt @@ -42,12 +42,8 @@ if (NOT CHIP_ROOT) get_filename_component(CHIP_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../../.. REALPATH) endif() get_filename_component(GN_ROOT_TARGET ${CHIP_ROOT}/config/nxp/chip-gn REALPATH) -get_filename_component(COMMON_CMAKE_SOURCE_DIR ${CHIP_ROOT}/config/common/cmake REALPATH) -# Get common Cmake sources - -include(${COMMON_CMAKE_SOURCE_DIR}/chip_gn_args.cmake) -include(${COMMON_CMAKE_SOURCE_DIR}/chip_gn.cmake) +include(${CHIP_ROOT}/config/nxp/cmake/common.cmake) # Prepare compiler flags if (CHIP_CFLAGS) @@ -109,30 +105,6 @@ matter_common_gn_args( matter_add_gn_arg_string("zephyr_ar" ${CMAKE_AR}) matter_add_gn_arg_string("zephyr_cc" ${CMAKE_C_COMPILER}) matter_add_gn_arg_string("zephyr_cxx" ${CMAKE_CXX_COMPILER}) -matter_add_gn_arg_bool ("chip_logging" CONFIG_LOG) -matter_add_gn_arg_bool ("chip_enable_openthread" CONFIG_NET_L2_OPENTHREAD) -matter_add_gn_arg_bool ("chip_openthread_ftd" CONFIG_OPENTHREAD_FTD) -matter_add_gn_arg_bool ("chip_config_network_layer_ble" CONFIG_BT) -matter_add_gn_arg_bool ("chip_inet_config_enable_ipv4" CONFIG_CHIP_IPV4) -matter_add_gn_arg_bool ("chip_persist_subscriptions" CONFIG_CHIP_PERSISTENT_SUBSCRIPTIONS) -matter_add_gn_arg_bool ("chip_monolithic_tests" CONFIG_CHIP_BUILD_TESTS) -matter_add_gn_arg_bool ("chip_inet_config_enable_tcp_endpoint" CONFIG_CHIP_BUILD_TESTS) -matter_add_gn_arg_bool ("chip_error_logging" CONFIG_MATTER_LOG_LEVEL GREATER_EQUAL 1) -matter_add_gn_arg_bool ("chip_progress_logging" CONFIG_MATTER_LOG_LEVEL GREATER_EQUAL 3) -matter_add_gn_arg_bool ("chip_detail_logging" CONFIG_MATTER_LOG_LEVEL GREATER_EQUAL 4) -matter_add_gn_arg_bool ("chip_automation_logging" FALSE) -matter_add_gn_arg_bool ("chip_malloc_sys_heap" CONFIG_CHIP_MALLOC_SYS_HEAP) -matter_add_gn_arg_bool ("chip_enable_wifi" CONFIG_CHIP_WIFI) -matter_add_gn_arg_bool ("chip_system_config_provide_statistics" CONFIG_CHIP_STATISTICS) -matter_add_gn_arg_bool ("chip_enable_icd_server" CONFIG_CHIP_ENABLE_ICD_SUPPORT) -matter_add_gn_arg_bool ("enable_eventlist_attribute" TRUE) -matter_add_gn_arg_bool ("chip_enable_ota_requestor" CONFIG_CHIP_OTA_REQUESTOR) - -if(CONFIG_DEBUG) - matter_add_gn_arg_bool("optimize_debug" true) - matter_add_gn_arg_string("optimize_debug_level" "0") - matter_add_gn_arg_string("symbol_level" "2") -endif() if (CONFIG_CHIP_FACTORY_DATA) matter_add_gn_arg_bool("chip_use_transitional_commissionable_data_provider" FALSE) @@ -141,11 +113,6 @@ elseif (CONFIG_CHIP_FACTORY_DATA_CUSTOM_BACKEND) matter_add_gn_arg_bool("chip_use_transitional_commissionable_data_provider" FALSE) endif() -if (CONFIG_CHIP_ROTATING_DEVICE_ID) - matter_add_gn_arg_bool("chip_enable_rotating_device_id" TRUE) - matter_add_gn_arg_bool("chip_enable_additional_data_advertising" TRUE) -endif() - if (CONFIG_NET_L2_OPENTHREAD) matter_add_gn_arg_string("chip_mdns" "platform") elseif(CONFIG_WIFI_NXP) @@ -154,9 +121,6 @@ else() matter_add_gn_arg_string("chip_mdns" "none") endif() -if (CONFIG_CHIP_CRYPTO_PSA) - matter_add_gn_arg_string("chip_crypto" "psa") -endif() # if (BOARD STREQUAL "native_posix") # matter_add_gn_arg_string("target_cpu" "x86") @@ -164,10 +128,6 @@ endif() # matter_add_gn_arg_string("target_cpu" "x64") # endif() -if (NOT CONFIG_CHIP_DEBUG_SYMBOLS) - matter_add_gn_arg_string("symbol_level" "0") -endif() - # if (CHIP_COMPILER_LAUNCHER) # matter_add_gn_arg_string("pw_command_launcher" ${CHIP_COMPILER_LAUNCHER}) # endif() diff --git a/config/nxp/cmake/common.cmake b/config/nxp/cmake/common.cmake new file mode 100644 index 00000000000000..8b0f379f502d09 --- /dev/null +++ b/config/nxp/cmake/common.cmake @@ -0,0 +1,69 @@ +# +# 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. +# + +# +# @file +# CMake for CHIP library configuration common to NXP platforms +# + +include(${CHIP_ROOT}/config/nxp/chip-module/generate_factory_data.cmake) + +get_filename_component(COMMON_CMAKE_SOURCE_DIR ${CHIP_ROOT}/config/common/cmake REALPATH) + +# Get common Cmake sources +include(${COMMON_CMAKE_SOURCE_DIR}/chip_gn_args.cmake) +include(${COMMON_CMAKE_SOURCE_DIR}/chip_gn.cmake) + +# ============================================================================== +# Generate configuration for CHIP GN build system +# ============================================================================== +matter_add_gn_arg_bool ("chip_logging" CONFIG_LOG) +matter_add_gn_arg_bool ("chip_enable_openthread" CONFIG_NET_L2_OPENTHREAD) +matter_add_gn_arg_bool ("chip_openthread_ftd" CONFIG_OPENTHREAD_FTD) +matter_add_gn_arg_bool ("chip_config_network_layer_ble" CONFIG_BT) +matter_add_gn_arg_bool ("chip_inet_config_enable_ipv4" CONFIG_CHIP_IPV4) +matter_add_gn_arg_bool ("chip_persist_subscriptions" CONFIG_CHIP_PERSISTENT_SUBSCRIPTIONS) +matter_add_gn_arg_bool ("chip_monolithic_tests" CONFIG_CHIP_BUILD_TESTS) +matter_add_gn_arg_bool ("chip_inet_config_enable_tcp_endpoint" CONFIG_CHIP_BUILD_TESTS) +matter_add_gn_arg_bool ("chip_error_logging" CONFIG_MATTER_LOG_LEVEL GREATER_EQUAL 1) +matter_add_gn_arg_bool ("chip_progress_logging" CONFIG_MATTER_LOG_LEVEL GREATER_EQUAL 3) +matter_add_gn_arg_bool ("chip_detail_logging" CONFIG_MATTER_LOG_LEVEL GREATER_EQUAL 4) +matter_add_gn_arg_bool ("chip_automation_logging" FALSE) +matter_add_gn_arg_bool ("chip_malloc_sys_heap" CONFIG_CHIP_MALLOC_SYS_HEAP) +matter_add_gn_arg_bool ("chip_enable_wifi" CONFIG_CHIP_WIFI) +matter_add_gn_arg_bool ("chip_system_config_provide_statistics" CONFIG_CHIP_STATISTICS) +matter_add_gn_arg_bool ("chip_enable_icd_server" CONFIG_CHIP_ENABLE_ICD_SUPPORT) +matter_add_gn_arg_bool ("enable_eventlist_attribute" TRUE) +matter_add_gn_arg_bool ("chip_enable_ota_requestor" CONFIG_CHIP_OTA_REQUESTOR) + +if(CONFIG_DEBUG) + matter_add_gn_arg_bool("optimize_debug" true) + matter_add_gn_arg_string("optimize_debug_level" "0") + matter_add_gn_arg_string("symbol_level" "2") +endif() + +if (CONFIG_CHIP_ROTATING_DEVICE_ID) + matter_add_gn_arg_bool("chip_enable_rotating_device_id" TRUE) + matter_add_gn_arg_bool("chip_enable_additional_data_advertising" TRUE) +endif() + +if (CONFIG_CHIP_CRYPTO_PSA) + matter_add_gn_arg_string("chip_crypto" "psa") +endif() + +if (NOT CONFIG_CHIP_DEBUG_SYMBOLS) + matter_add_gn_arg_string("symbol_level" "0") +endif() diff --git a/docs/guides/darwin.md b/docs/guides/darwin.md index 2a84067da92ed0..d44e44cc04afd9 100644 --- a/docs/guides/darwin.md +++ b/docs/guides/darwin.md @@ -1,8 +1,21 @@ -# Testing with Apple Devices +# Matter Development and Testing with/on Apple Devices -### Matter is the foundation for connected things. +### Overview + +- To develop a Matter Application, learn more about how to develop with Matter + on Apple's platforms [here](https://developer.apple.com/apple-home/matter/) + + - Additional documentation about the Matter Support API is + [here](https://developer.apple.com/documentation/mattersupport) -Learn more about Matter [here](https://buildwithmatter.com/) +### Filing Issues + +- An issues related to Apple Home, or Development on Apple's Platforms should + be reported using Feedback Assistant as described + [below](#providing-feedback-to-apple) + +- Any issues related to the Matter SDK should be reported to the project + [here](https://github.com/project-chip/connectedhomeip/issues) ## Source Compatibility @@ -12,20 +25,18 @@ changes are present in the release for testing. Listed are the Current SHAs: -- iOS/iPadOS/tvOS 16.1: - [`33f6a910cd9a8a0cfdd7088e2f43efd2f7f566a7`](https://github.com/project-chip/connectedhomeip/commits/33f6a910cd9a8a0cfdd7088e2f43efd2f7f566a7) - -- iOS/iPadOS/tvOS 16.2 and 16.3: - [`c279578c5bc37f117335aa96cec6c5552f070cc0`](https://github.com/project-chip/connectedhomeip/commits/c279578c5bc37f117335aa96cec6c5552f070cc0) +- Latest macOS/tvOS/iOS/iPadOS: + [`2ee90eba27676950fa2f4ef96597d9696f510d5d`](https://github.com/project-chip/connectedhomeip/commits/2ee90eba27676950fa2f4ef96597d9696f510d5d) +- Larger list of SHAs are [here](#release-to-sha-mappings) ## Supported Platforms for Matter Device Testing - Matter is supported by iOS/tvOS/iPadOS/watchOS/HomePod/AppleTV starting with 16.1 - - To test your Matter device with Apple's platforms, all you need is a device with a supported OS -- Pairing via QR Code or Setup Code is supported directly from the Home App +- Pairing via QR Code or Setup Code is supported directly from the Home App, + or any third party Application - Please proceed to [this section](#testing-your-matter-device-with-apple-home) if you're developing a new device @@ -46,14 +57,6 @@ For Context: This is a mapping of platform to OS - HomePod _(tvOS)_ - Apple Watch _(watchOS)_ -### Overview - -- To develop a Matter Application, learn more about how to develop with Matter - on Apple's platforms [here](https://developer.apple.com/apple-home/matter/) - - - Additional documentation about the Matter Support API is - [here](https://developer.apple.com/documentation/mattersupport) - ### Setup Requirements for Application Development - Devices must support BLE pairing, and have it enabled @@ -122,9 +125,7 @@ For Context: This is a mapping of platform to OS ### Enable Developer Mode on your Apple Device -##### Note: Developer mode is a great way to get logs from your device as well as - -enables other useful developer tools +##### Note: Developer mode is a great way to get logs from your device as well as enable other useful developer tools To enable developer mode, please follow the instructions [here](https://developer.apple.com/documentation/xcode/enabling-developer-mode-on-a-device) @@ -137,30 +138,96 @@ chip-tool will require installing the on MacOS or iOS/iPadOS. - Download the Bluetooth Central Matter Client Developer Mode profile and - install it on a iOS/iPadOS 16.1 beta 3 and MacOS 13.1 beta 3 or later - system. + install it on a supported system. - For _macOS_, Profile can be installed via Settings->Privacy & Security->Profiles - For _iOS/iPadOS_, If necessary, email the profile or use AirDrop to - transfer the profile to the _iOS/iPadOS 16 beta 3_ device. + transfer the profile to the _iOS/iPadOS_ device. - Restart your system - For _iOS/iPadOS_, enable Developer Mode. Refer to [this developer page](https://developer.apple.com/documentation/xcode/enabling-developer-mode-on-a-device) - Compile chip-tool for macOS or CHIP Tool for iOS +## Supported Device Types in Apple Home + +##### Note: Not exhaustive, and may be out of date, see [below](#apple-home-development-guide) for more information + +| Type | Decimal | HEX | Notes | +| ----------------------- | ------- | ---- | ------------------------------------------------ | +| On/Off Light | 256 | 0100 | | +| Dimming Light | 257 | 0101 | | +| On/Off Plug-In Unit | 266 | 010A | | +| Dimmable Plug-In Unit | 267 | 010B | | +| On/Off Light Switch | 259 | 0103 | Requires both On/Off Client and Server | +| Dimmer Switch | 260 | 0104 | Requires both On/Off Client and Server | +| Generic Switch (button) | 15 | 000F | Supports momentary switch only, and not latching | +| Contact Sensor | 21 | 0015 | | +| Light Sensor | 262 | 0106 | | +| Occupancy Sensor | 263 | 0107 | | +| Temperature Sensor | 770 | 0302 | | +| Humidity Sensor | 775 | 0307 | | +| Air Quality Sensor | 44 | 002C | | +| Door Lock | 10 | 000A | | +| Window Covering | 514 | 0202 | | +| Heating/Cooling Unit | 768 | 0300 | | +| Thermostat | 769 | 0301 | | +| Fan | 43 | 002B | | +| Air Purifier | 45 | 002D | | +| Temperature Color Light | 268 | 010C | | +| Enhanced Color Light | 269 | 010D | | +| Bridges | 14 | 000E | | +| Robot Vacuum Cleaner | 116 | 0074 | Announced, not yet supported | + +## Additional Device Type Support on Apple's Platforms + +Apple's platforms support all device types available in the Matter SDK, so +developers can use Matter.framework to develop their own applications that can +add devices to Apple Home, or create their own Fabric to manage devices as well. + +Please see documentation about `-[HMAccessory matterNodeID]` +[here](https://developer.apple.com/documentation/homekit/hmaccessory/matternodeid-5zfqo) +which allows you to use Matter.framework to interact with Matter Devices +directly that are paired into Apple Home. + +## Apple Home Development Guide + +Please see [here](https://developer.apple.com/apple-home/) for more general +information about developing an Application or a Device for Apple Home. This +includes information about +[best practices](https://developer.apple.com/apple-home/downloads/Matter-Accessory-Best-Practices-for-Apple-Home.pdf), +[platform developer API](https://developer.apple.com/apple-home/matter/), +[OTA Updates](https://developer.apple.com/accessories/Apple-Matter-OTA-User-Guide.pdf), +general adoption Q&A, and the "Works with Apple Home" badge. + +## General Matter Platform Development Guide + +##### Getting the SDK Ready + +##### Note: Most platforms have very similar, if not the same configuration requirements + +1. Checkout and setup + [Matter repo](https://github.com/project-chip/connectedhomeip.git) as per the + instructions above. +2. Find and edit one of the platform + [examples](https://github.com/project-chip/connectedhomeip/tree/master/examples) + to support the fixed device types above. +3. Read the [platform guides](README.md) on how set up the hardware + - There is a list of more detailed guides [here](#platform-guides) + ## Testing your Matter Device with Apple Home -1. Clone the [Matter repo](https://github.com/project-chip/connectedhomeip.git) +1. Clone the + [Matter repository](https://github.com/project-chip/connectedhomeip.git) 2. Checkout the specific commit hash (from [above](#source-compatibility)) for maximum compatibility with your installed release: - - Example command for SHA `c279578c5bc37f117335aa96cec6c5552f070cc0`: - `$ git checkout c279578c5bc37f117335aa96cec6c5552f070cc0` + - Example command for SHA `2ee90eba27676950fa2f4ef96597d9696f510d5d`: + `$ git checkout 2ee90eba27676950fa2f4ef96597d9696f510d5d` -In order to work with iOS/iPadOS/tvOS 16.1 or greater, device types as defined -in the Matter Device Library spec are used to determine accessory categories. -Ensure the right device type is set for each endpoint. +In order to work with iOS/iPadOS/tvOS, device types as defined in the Matter +Device Library spec are used to determine accessory categories. Ensure the right +device type is set for each endpoint. - For the `all-clusters-app` as an example, this can be set in `FIXED_DEVICE_TYPES`, `FIXED_DEVICE_TYPE_OFFSETS`, and @@ -190,29 +257,11 @@ Example: } ``` -- Supported device types are (not exhaustive): - -| Type | Decimal | HEX | -| ------------------ | ------- | ---- | -| Lightbulb | 256 | 0100 | -| Lightbulb + Dimmer | 257 | 0101 | -| Switch | 259 | 0103 | -| Contact Sensor | 21 | 0015 | -| Door Lock | 10 | 000A | -| Light Sensor | 262 | 0106 | -| Occupancy Sensor | 263 | 0107 | -| Outlet | 266 | 010A | -| Color Bulb | 268 | 010C | -| Window Covering | 514 | 0202 | -| Thermostat | 769 | 0301 | -| Temperature Sensor | 770 | 0302 | -| Flow Sensor | 774 | 0306 | - -#### Examples of how to setup devices +### Examples of how to setup devices ##### Case study 1: Configuring a development M5Stack, as a multi-device to work with iOS/iPadOS/tvOS -##### Note: These instructions are specific to getting started with the (Matter-provided) `all-clusters-app` on an ESP32-based M5Stack, however can be generalised to work on most platforms ([more listed below](#guides)) +##### Note: These instructions are specific to getting started with the (Matter-provided) `all-clusters-app` on an ESP32-based M5Stack, however can be generalised to work on most platforms ([more listed below](#platform-guides)) 1. Checkout and setup [Matter repo](https://github.com/project-chip/connectedhomeip.git) as per the @@ -235,21 +284,7 @@ Example: initialize your development environment, compile the firmware and flash your hardware. -#### General Matter Platform Development Guide - -##### Getting the SDK Ready - -##### Note: Most platforms have very similar, if not the same configuration requirements - -1. Checkout and setup - [Matter repo](https://github.com/project-chip/connectedhomeip.git) as per the - instructions above. -2. Find and edit one of the platform - [examples](https://github.com/project-chip/connectedhomeip/tree/master/examples) - to support the fixed device types above. -3. Read the [platform guides](README.md) on how set up the hardware - -##### Guides +### Platform Guides - [Bouffalo Lab](/examples/lighting-app/bouffalolab/README.md) - [EFR32 Window Covering](/examples/window-app/silabs/README.md) @@ -300,26 +335,20 @@ Example: - [Apple Home Profile](https://developer.apple.com/bug-reporting/profiles-and-logs/?platform=tvos&name=homekit) - [Network Profile](https://developer.apple.com/bug-reporting/profiles-and-logs/?platform=tvos&name=network) - [mDNS Profile](https://developer.apple.com/bug-reporting/profiles-and-logs/?platform=tvos&name=mdns) - -### Release Notes & Known Issues - -- Please refer to the iOS/iPadOS 16.1 - [Release Notes](https://developer.apple.com/documentation/ios-ipados-release-notes/ios-16_1-release-notes) - for currently known issues. -- Please refer to the iOS/iPadOS 16.2 - [Release Notes](https://developer.apple.com/documentation/ios-ipados-release-notes/ios-ipados-16_2-release-notes) - for currently known issues -- Please refer to the iOS & iPadOS 16.3 - [Release Notes](https://developer.apple.com/documentation/ios-ipados-release-notes/ios-ipados-16_3-release-notes) - for currently known issues -- Please refer to the iOS & iPadOS 16.4 - [Release Notes](https://developer.apple.com/documentation/ios-ipados-release-notes/ios-ipados-16_4-release-notes) - for currently known issues -- Please refer to the iOS & iPadOS 16.5 - [Release Notes](https://developer.apple.com/documentation/ios-ipados-release-notes/ios-ipados-16_5-release-notes) - for currently known issues -- An issues related to Apple Home integration should be reported - [feedback](#providing-feedback-to-apple) as described in this section - -- Any issues related to the Matter SDK should be reported to the project - [here](https://github.com/project-chip/connectedhomeip/issues) + - Any Thread device: + [Thread Profile](https://developer.apple.com/bug-reporting/profiles-and-logs/?name=HomeThread) + +### Release to SHA Mappings + +| Platform | Release Version | SHA | +| -------- | --------------- | ------------------------------------------ | +| iOS | 17.6.1 | `2ee90eba27676950fa2f4ef96597d9696f510d5d` | +| iPadOS | 17.6.1 | `2ee90eba27676950fa2f4ef96597d9696f510d5d` | +| tvOS | 17.6 | `2ee90eba27676950fa2f4ef96597d9696f510d5d` | +| macOS | 14.6.1 | `2ee90eba27676950fa2f4ef96597d9696f510d5d` | +| iOS | 17.5.1 | `d09b5ac98f4d7d8b9f2c307f55ab5462576623a5` | +| iPadOS | 17.5.1 | `d09b5ac98f4d7d8b9f2c307f55ab5462576623a5` | +| tvOS | 17.5.1 | `d09b5ac98f4d7d8b9f2c307f55ab5462576623a5` | +| macOS | 14.5 | `d09b5ac98f4d7d8b9f2c307f55ab5462576623a5` | +| iOS | 16.7.8 | `83f7a2fe136e0b746db09f1d19e36c693a634b66` | +| iPadOS | 16.7.8 | `83f7a2fe136e0b746db09f1d19e36c693a634b66` | diff --git a/docs/guides/esp32/ota.md b/docs/guides/esp32/ota.md index 7c9c388b422cb9..ae43e09bc08264 100644 --- a/docs/guides/esp32/ota.md +++ b/docs/guides/esp32/ota.md @@ -121,3 +121,66 @@ Please follow the steps below to generate an application image for OTA upgrades: ``` 3. Use the `lighting-app-encrypted-ota.bin` file with the OTA Provider app. + +## Delta OTA + +Delta OTA Updates is a feature that enables Over-the-Air (OTA) firmware update +with compressed delta binaries. Patch files have smaller size than the original +firmware file, which reduces the time and network usage to download the file +from the server. Also, no additional storage partition is required for the +"patch". + +### Firmware Changes + +- Enable configuration options for OTA requestor and Delta OTA: + + ``` + CONFIG_ENABLE_OTA_REQUESTOR=y + CONFIG_ENABLE_DELTA_OTA=y + ``` + +Please follow the steps below to generate an application image for OTA upgrades: + +1. Delta binary needs to be generated using binary delta encoding in Python + 3.6+. You can install `detools` using the following command. + + ``` + pip install detools>=0.49.0 + ``` + +2. Generate delta binary. + + ``` + python managed_components/espressif__esp_delta_ota/examples/https_delta_ota/tools/esp_delta_ota_patch_gen.py --chip --base_binary --new_binary --patch_file_name + ``` + +3. Append the Matter OTA header: + `src/app/ota_image_tool.py create --vendor-id 0xFFF1 --product-id 0x8000 --version 2 --version-str "v2.0" -da sha256 lighting-app-delta-ota.bin` + +4. Use the `lighting-app-delta-ota.bin` file with the OTA Provider app. + +## Encrypted Delta OTA + +To use encrypted delta OTA, follow the below steps. + +### Firmware Changes + +- Enable configuration options for OTA requestor, Delta OTA and Encrypted OTA: + + ``` + CONFIG_ENABLE_OTA_REQUESTOR=y + CONFIG_ENABLE_DELTA_OTA=y + CONFIG_ENABLE_ENCRYPTED_OTA=y + ``` + +1. Follow the step of `Generate a new RSA-3072 key pair or use an existing one` + of + [Encrypted OTA](https://github.com/project-chip/connectedhomeip/blob/master/docs/guides/esp32/ota.md#encrypted-ota) + to build your binary. + +2. Create delta binary as shown in `Generate delta binary` of + [Delta OTA](https://github.com/project-chip/connectedhomeip/blob/master/docs/guides/esp32/ota.md#delta-ota) + +3. Encrypt the application binary. + +4. Append the Matter OTA header. 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 776b241c6ed958..46debb8a6cd363 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 @@ -4386,7 +4386,7 @@ cluster ElectricalEnergyMeasurement = 145 { /** This cluster is used to allow clients to control the operation of a hot water heating appliance so that it can be used with energy management. */ provisional cluster WaterHeaterManagement = 148 { - revision 1; + revision 2; enum BoostStateEnum : enum8 { kInactive = 0; @@ -4398,7 +4398,7 @@ provisional cluster WaterHeaterManagement = 148 { kTankPercent = 0x2; } - bitmap WaterHeaterDemandBitmap : bitmap8 { + bitmap WaterHeaterHeatSourceBitmap : bitmap8 { kImmersionElement1 = 0x1; kImmersionElement2 = 0x2; kHeatPump = 0x4; @@ -4406,16 +4406,24 @@ provisional cluster WaterHeaterManagement = 148 { kOther = 0x10; } - bitmap WaterHeaterTypeBitmap : bitmap8 { - kImmersionElement1 = 0x1; - kImmersionElement2 = 0x2; - kHeatPump = 0x4; - kBoiler = 0x8; - kOther = 0x10; + struct WaterHeaterBoostInfoStruct { + elapsed_s duration = 0; + optional boolean oneShot = 1; + optional boolean emergencyBoost = 2; + optional temperature temporarySetpoint = 3; + optional percent targetPercentage = 4; + optional percent targetReheat = 5; } - readonly attribute WaterHeaterTypeBitmap heaterTypes = 0; - readonly attribute WaterHeaterDemandBitmap heatDemand = 1; + info event BoostStarted = 0 { + WaterHeaterBoostInfoStruct boostInfo = 0; + } + + info event BoostEnded = 1 { + } + + readonly attribute WaterHeaterHeatSourceBitmap heaterTypes = 0; + readonly attribute WaterHeaterHeatSourceBitmap heatDemand = 1; readonly attribute optional int16u tankVolume = 2; readonly attribute optional energy_mwh estimatedHeatRequired = 3; readonly attribute optional percent tankPercentage = 4; @@ -4428,12 +4436,7 @@ provisional cluster WaterHeaterManagement = 148 { readonly attribute int16u clusterRevision = 65533; request struct BoostRequest { - elapsed_s duration = 0; - optional boolean oneShot = 1; - optional boolean emergencyBoost = 2; - optional temperature temporarySetpoint = 3; - optional percent targetPercentage = 4; - optional percent targetReheat = 5; + WaterHeaterBoostInfoStruct boostInfo = 0; } /** Allows a client to request that the water heater is put into a Boost state. */ @@ -9068,7 +9071,7 @@ endpoint 1 { callback attribute eventList; callback attribute attributeList; ram attribute featureMap default = 0x0123; - ram attribute clusterRevision default = 6; + ram attribute clusterRevision default = 7; handle command SetpointRaiseLower; handle command SetActiveScheduleRequest; 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 f817d147160ef9..9a736eea56fa30 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 @@ -16918,7 +16918,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "6", + "defaultValue": "7", "reportable": 1, "minInterval": 0, "maxInterval": 65344, diff --git a/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h b/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h index ad73239e77990d..27d8cd7cd96ecf 100644 --- a/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h +++ b/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h @@ -56,30 +56,52 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate *********************************************************************************/ /** - * @brief Delegate should implement a handler to start boosting the water temperature as required. - * Upon receipt, the Water Heater SHALL transition into the BOOST state, which SHALL cause the - * water in the tank (or the TargetPercentage of the water, if included) to be heated towards - * the set point (or the TemporarySetpoint, if included), which in turn may cause a call for heat, - * even if the mode is OFF, or is TIMED and it is during one of the Off periods. + * @brief Delegate should implement a handler to start boosting the water + * temperature as required. Upon receipt, the Water Heater SHALL + * transition into the BOOST state, which SHALL cause the water in + * the tank (or the TargetPercentage of the water, if included) to be + * heated towards the set point (or the TemporarySetpoint, if + * included), which in turn may cause a call for heat, even if the + * mode is OFF, or is TIMED and it is during one of the Off periods. * - * @param duration Indicates the time period in seconds for which the BOOST state is activated before it - * automatically reverts to the previous mode (e.g. OFF, MANUAL or TIMED). - * @param oneShot Indicates whether the BOOST state should be automatically canceled once the hot water has - * first reached the set point temperature (or the TemporarySetpoint temperature, if specified) - * for the TargetPercentage (if specified). - * @param emergencyBoost Indicates that the consumer wants the water to be heated as quickly as practicable. - * This MAY cause multiple heat sources to be activated (e.g. a heat pump and direct - * electric heating element). - * @param temporarySetpoint Indicates the target temperature to which to heat the hot water for this Boost command. - * It SHALL be used instead of the normal set point temperature whilst the BOOST state is active. - * @param targetPercentage If the tank supports the TankPercent feature, this field indicates the amount of water - * that SHALL be heated by this Boost command before the heater is switched off. - * @param targetReheat If the tank supports the TankPercent feature, and the heating by this Boost command has ceased - * because the TargetPercentage of the water in the tank has been heated to the set point (or - * TemporarySetpoint if included), this field indicates the percentage to which the hot water in - * the tank SHALL be allowed to fall before again beginning to reheat it. + * @param duration Indicates the time period in seconds for which + * the BOOST state is activated before it + * automatically reverts to the previous mode + * (e.g. OFF, MANUAL or TIMED). * - * @return Success if the boost command is accepted; otherwise the command SHALL be rejected with appropriate error. + * @param oneShot Indicates whether the BOOST state should be + * automatically canceled once the hot water has + * first reached the set point temperature (or the + * TemporarySetpoint temperature, if specified) + * for the TargetPercentage (if specified). + * + * @param emergencyBoost Indicates that the consumer wants the water to + * be heated as quickly as practicable. This MAY + * cause multiple heat sources to be activated + * (e.g. a heat pump and direct electric heating + * element). + * + * @param temporarySetpoint Indicates the target temperature to which to + * heat the hot water for this Boost command. It + * SHALL be used instead of the normal set point + * temperature whilst the BOOST state is active. + * + * @param targetPercentage If the tank supports the TankPercent feature, + * this field indicates the amount of water that + * SHALL be heated by this Boost command before + * the heater is switched off. + * + * @param targetReheat If the tank supports the TankPercent feature, + * and the heating by this Boost command has + * ceased because the TargetPercentage of the + * water in the tank has been heated to the set + * point (or TemporarySetpoint if included), this + * field indicates the percentage to which the hot + * water in the tank SHALL be allowed to fall + * before again beginning to reheat it. + * + * @return Success if the boost command is accepted; otherwise the command + * SHALL be rejected with appropriate error. */ Protocols::InteractionModel::Status HandleBoost(uint32_t duration, Optional oneShot, Optional emergencyBoost, Optional temporarySetpoint, Optional targetPercentage, @@ -87,8 +109,8 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate /** * @brief Delegate should implement a handler to cancel a boost command. - * Upon receipt, the Water Heater SHALL transition back from the BOOST state to the previous mode - * (e.g. OFF, MANUAL or TIMED). + * Upon receipt, the Water Heater SHALL transition back from the + * BOOST state to the previous mode (e.g. OFF, MANUAL or TIMED). * * @return It should report SUCCESS if successful and FAILURE otherwise. */ @@ -96,8 +118,8 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate // ------------------------------------------------------------------ // Get attribute methods - BitMask GetHeaterTypes() override; - BitMask GetHeatDemand() override; + BitMask GetHeaterTypes() override; + BitMask GetHeatDemand() override; uint16_t GetTankVolume() override; int64_t GetEstimatedHeatRequired() override; Percent GetTankPercentage() override; @@ -105,18 +127,18 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate // ------------------------------------------------------------------ // Set attribute methods - void SetHeaterTypes(BitMask heaterTypes); - void SetHeatDemand(BitMask heatDemand); + void SetHeaterTypes(BitMask heaterTypes); + void SetHeatDemand(BitMask heatDemand); void SetTankVolume(uint16_t tankVolume); void SetEstimatedHeatRequired(int64_t estimatedHeatRequired); void SetTankPercentage(Percent tankPercentage); void SetBoostState(BoostStateEnum boostState); - /********************************************************************************* + /*************************************************************************** * * WaterHeaterManagementDelegate specific methods * - *********************************************************************************/ + ***************************************************************************/ /** * @brief Set the Water Header Mode and act accordingly. @@ -155,36 +177,44 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate void HandleBoostTimerExpiry(); /** - * Determines whether the tank water temperature has reached the target temperature. + * Determines whether the tank water temperature has reached the target + * temperature. * - * @return Returns True is tank water temperature has reached the target temperature, False otherwise. + * @return Returns True is tank water temperature has reached the target + * temperature, False otherwise. */ bool HasWaterTemperatureReachedTarget() const; /** * Simulates water being drawn from the water tank. * - * @param percentageReplaced The % of water being replaced with water with a temperature of replacedWaterTemperature. - * @param replacedWaterTemperature The temperature of the percentageReplaced water. + * @param percentageReplaced The % of water being replaced with water with + * a temperature of replacedWaterTemperature. + * + * @param replacedWaterTemperature The temperature of the + * percentageReplaced water. */ void DrawOffHotWater(chip::Percent percentageReplaced, uint16_t replacedWaterTemperature); private: /** - * @brief Determine whether heating needs to be turned on or off or left as is. + * @brief Determine whether heating needs to be turned on or off or left as + * is. * - * @param heatingOp[out] Set as determined whether heating needs to be turned on/off or left unchanged. + * @param heatingOp[out] Set as determined whether heating needs to be + * turned on/off or left unchanged. * - * @return Success if the heating operation could be determined otherwise returns with appropriate error. + * @return Success if the heating operation could be determined otherwise + * returns with appropriate error. */ Protocols::InteractionModel::Status DetermineIfChangingHeatingState(HeatingOp & heatingOp); private: - /********************************************************************************* + /*************************************************************************** * * WaterHeaterManagementDelegate specific attributes * - *********************************************************************************/ + ***************************************************************************/ // Need the following so can determine which features are supported WaterHeaterManagement::Instance * mpWhmInstance; @@ -203,34 +233,44 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate // Boost command parameters - // This field SHALL indicate whether the BOOST state should be automatically canceled once the hot water has first reached the - // set point temperature (or the TemporarySetpoint temperature, if specified) for the TargetPercentage (if specified). + // This field SHALL indicate whether the BOOST state should be automatically + // canceled once the hot water has first reached the set point temperature + // (or the TemporarySetpoint temperature, if specified) for the + // TargetPercentage (if specified). Optional mBoostOneShot; - // This field indicates that the consumer wants the water to be heated as quickly as practicable. This MAY cause multiple heat - // sources to be activated (e.g. a heat pump and direct electric heating element). + // This field indicates that the consumer wants the water to be heated as + // quickly as practicable. This MAY cause multiple heat sources to be + // activated (e.g. a heat pump and direct electric heating element). Optional mBoostEmergencyBoost; - // This field indicates the target temperature to which to heat the hot water for this Boost command. It SHALL be used instead - // of the normal set point temperature whilst the BOOST state is active. + // This field indicates the target temperature to which to heat the hot + // water for this Boost command. It SHALL be used instead of the normal set + // point temperature whilst the BOOST state is active. Optional mBoostTemporarySetpoint; - // If the tank supports the TankPercent feature, this field indicates the amount of water that SHALL be heated by this Boost - // command before the heater is switched off. This field is optional, however it SHALL be included if the TargetReheat field is - // included. + // If the tank supports the TankPercent feature, this field indicates the + // amount of water that SHALL be heated by this Boost command before the + // heater is switched off. This field is optional, however it SHALL be + // included if the TargetReheat field is included. Optional mBoostTargetPercentage; - // If the tank supports the TankPercent feature, and the heating by this Boost command has ceased because the TargetPercentage - // of the water in the tank has been heated to the set point (or TemporarySetpoint if included), this field indicates the - // percentage to which the hot water in the tank SHALL be allowed to fall before again beginning to reheat it. For example if - // the TargetPercentage was 80%, and the TargetReheat was 40%, then after initial heating to 80% hot water, the tank may have - // hot water drawn off until only 40% hot water remains. At this point the heater will begin to heat back up to 80% of hot - // water. If this field and the OneShot field were both omitted, heating would begin again after any water draw which reduced - // the TankPercentage below 80%. + // If the tank supports the TankPercent feature, and the heating by this + // Boost command has ceased because the TargetPercentage of the water in the + // tank has been heated to the set point (or TemporarySetpoint if included), + // this field indicates the percentage to which the hot water in the tank + // SHALL be allowed to fall before again beginning to reheat it. For example + // if the TargetPercentage was 80%, and the TargetReheat was 40%, then after + // initial heating to 80% hot water, the tank may have hot water drawn off + // until only 40% hot water remains. At this point the heater will begin to + // heat back up to 80% of hot water. If this field and the OneShot field + // were both omitted, heating would begin again after any water draw which + // reduced the TankPercentage below 80%. Optional mBoostTargetReheat; - // Track whether the water temperature has reached the water temperature specified in the boost command. Used in conjunction - // with the boost command boostTargetReheat parameter + // Track whether the water temperature has reached the water temperature + // specified in the boost command. Used in conjunction with the boost + // command boostTargetReheat parameter bool mBoostTargetTemperatureReached; /********************************************************************************* @@ -239,28 +279,35 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate * *********************************************************************************/ - // This attribute SHALL indicate the methods to call for heat that the controller supports. If a bit is set then the controller - // supports the corresponding method. - BitMask mHeaterTypes; + // This attribute SHALL indicate the methods to call for heat that the + // controller supports. If a bit is set then the controller supports the + // corresponding method. + BitMask mHeaterTypes; - // This attribute SHALL indicate if the controller is asking for heat. If a bit is set then the corresponding call for heat is - // active. - BitMask mHeatDemand; + // This attribute SHALL indicate if the controller is asking for heat. If a + // bit is set then the corresponding call for heat is active. + BitMask mHeatDemand; - // This attribute SHALL indicate the volume of water that the hot water tank can hold (in units of Litres). This allows an - // energy management system to estimate the required heating energy needed to reach the target temperature. + // This attribute SHALL indicate the volume of water that the hot water tank + // can hold (in units of Litres). This allows an energy management system to + // estimate the required heating energy needed to reach the target + // temperature. uint16_t mTankVolume; - // This attribute SHALL indicate the estimated heat energy needed to raise the water temperature to the target setpoint. This - // can be computed by taking the specific heat capacity of water (4182 J/kg °C) and by knowing the current temperature of the - // water, the tank volume and target temperature. + // This attribute SHALL indicate the estimated heat energy needed to raise + // the water temperature to the target setpoint. This can be computed by + // taking the specific heat capacity of water (4182 J/kg °C) and by knowing + // the current temperature of the water, the tank volume and target + // temperature. int64_t mEstimatedHeatRequired; - // This attribute SHALL indicate an approximate level of hot water stored in the tank, which may help consumers understand the - // amount of hot water remaining in the tank. + // This attribute SHALL indicate an approximate level of hot water stored in + // the tank, which may help consumers understand the amount of hot water + // remaining in the tank. Percent mTankPercentage; - // This attribute SHALL indicate if the BOOST state, as triggered by a Boost command, is currently active. + // This attribute SHALL indicate if the BOOST state, as triggered by a Boost + // command, is currently active. BoostStateEnum mBoostState; }; diff --git a/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h b/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h index d51e6a62f2021c..b56aa24abf491f 100644 --- a/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h +++ b/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h @@ -62,7 +62,7 @@ class WhmManufacturer /** * @brief Called to determine which heating sources to use, */ - BitMask DetermineHeatingSources(); + BitMask DetermineHeatingSources(); /** * @brief Turn the heating of the water tank on. @@ -83,23 +83,44 @@ class WhmManufacturer /** * @brief Called to handle a boost command. * - * @param duration Indicates the time period in seconds for which the BOOST state is activated before it automatically reverts - * to the previous mode (e.g. OFF, MANUAL or TIMED). - * @param oneShot Indicates whether the BOOST state should be automatically canceled once the hot water has first reached the - * set point temperature (or the TemporarySetpoint temperature, if specified) for the TargetPercentage (if - * specified). - * @param emergencyBoost Indicates that the consumer wants the water to be heated as quickly as practicable. This MAY cause - * multiple heat sources to be activated (e.g. a heat pump and direct electric heating element). - * @param temporarySetpoint Indicates the target temperature to which to heat the hot water for this Boost command. It SHALL be - * used instead of the normal set point temperature whilst the BOOST state is active. - * @param targetPercentage If the tank supports the TankPercent feature, this field indicates the amount of water that SHALL be - * heated by this Boost command before the heater is switched off. - * @param targetReheat If the tank supports the TankPercent feature, and the heating by this Boost command has ceased because - * the TargetPercentage of the water in the tank has been heated to the set point (or TemporarySetpoint if - * included), this field indicates the percentage to which the hot water in the tank SHALL be allowed to fall before again - * beginning to reheat it. + * @param duration Indicates the time period in seconds for which + * the BOOST state is activated before it + * automatically reverts to the previous mode + * (e.g. OFF, MANUAL or TIMED). * - * @return Success if the boost command is successful; otherwise return the appropriate error. + * @param oneShot Indicates whether the BOOST state should be + * automatically canceled once the hot water has + * first reached the set point temperature (or the + * TemporarySetpoint temperature, if specified) + * for the TargetPercentage (if specified). + * + * @param emergencyBoost Indicates that the consumer wants the water to + * be heated as quickly as practicable. This MAY + * cause multiple heat sources to be activated + * (e.g. a heat pump and direct electric heating + * element). + * + * @param temporarySetpoint Indicates the target temperature to which to + * heat the hot water for this Boost command. It + * SHALL be used instead of the normal set point + * temperature whilst the BOOST state is active. + * + * @param targetPercentage If the tank supports the TankPercent feature, + * this field indicates the amount of water that + * SHALL be heated by this Boost command before + * the heater is switched off. + * + * @param targetReheat If the tank supports the TankPercent feature, + * and the heating by this Boost command has + * ceased because the TargetPercentage of the + * water in the tank has been heated to the set + * point (or TemporarySetpoint if included), this + * field indicates the percentage to which the hot + * water in the tank SHALL be allowed to fall + * before again beginning to reheat it. + * + * @return Success if the boost command is successful; otherwise return the + * appropriate error. */ Protocols::InteractionModel::Status BoostCommandStarted(uint32_t duration, Optional oneShot, Optional emergencyBoost, Optional temporarySetpoint, diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp index 0568e5c9c7d002..af09a13a0dab2d 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp @@ -50,12 +50,12 @@ void WaterHeaterManagementDelegate::SetWhmManufacturer(WhmManufacturer & whmManu * *********************************************************************************/ -BitMask WaterHeaterManagementDelegate::GetHeaterTypes() +BitMask WaterHeaterManagementDelegate::GetHeaterTypes() { return mHeaterTypes; } -BitMask WaterHeaterManagementDelegate::GetHeatDemand() +BitMask WaterHeaterManagementDelegate::GetHeatDemand() { return mHeatDemand; } @@ -80,7 +80,7 @@ BoostStateEnum WaterHeaterManagementDelegate::GetBoostState() return mBoostState; } -void WaterHeaterManagementDelegate::SetHeaterTypes(BitMask heaterTypes) +void WaterHeaterManagementDelegate::SetHeaterTypes(BitMask heaterTypes) { if (mHeaterTypes != heaterTypes) { @@ -90,7 +90,7 @@ void WaterHeaterManagementDelegate::SetHeaterTypes(BitMask heatDemand) +void WaterHeaterManagementDelegate::SetHeatDemand(BitMask heatDemand) { if (mHeatDemand != heatDemand) { diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp index dd8452a5c816ca..8865501fcca6fe 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp @@ -43,8 +43,8 @@ CHIP_ERROR WhmManufacturer::Init() return CHIP_ERROR_UNINITIALIZED; } - dg->SetHeaterTypes(BitMask(WaterHeaterTypeBitmap::kImmersionElement1)); - dg->SetHeatDemand(BitMask(WaterHeaterDemandBitmap::kImmersionElement1)); + dg->SetHeaterTypes(BitMask(WaterHeaterHeatSourceBitmap::kImmersionElement1)); + dg->SetHeatDemand(BitMask(WaterHeaterHeatSourceBitmap::kImmersionElement1)); dg->SetEstimatedHeatRequired(10000); return CHIP_NO_ERROR; @@ -55,36 +55,36 @@ CHIP_ERROR WhmManufacturer::Shutdown() return CHIP_NO_ERROR; } -BitMask WhmManufacturer::DetermineHeatingSources() +BitMask WhmManufacturer::DetermineHeatingSources() { WaterHeaterManagementDelegate * dg = GetWhmManufacturer()->GetWhmDelegate(); if (dg == nullptr) { ChipLogError(AppServer, "WhmDelegate is not initialized"); - return BitMask(0); + return BitMask(0); } // A list of valid heaterTypes uint8_t waterHeaterTypeValues[] = { - static_cast(WaterHeaterTypeBitmap::kImmersionElement1), - static_cast(WaterHeaterTypeBitmap::kImmersionElement2), - static_cast(WaterHeaterTypeBitmap::kHeatPump), - static_cast(WaterHeaterTypeBitmap::kBoiler), - static_cast(WaterHeaterTypeBitmap::kOther), + static_cast(WaterHeaterHeatSourceBitmap::kImmersionElement1), + static_cast(WaterHeaterHeatSourceBitmap::kImmersionElement2), + static_cast(WaterHeaterHeatSourceBitmap::kHeatPump), + static_cast(WaterHeaterHeatSourceBitmap::kBoiler), + static_cast(WaterHeaterHeatSourceBitmap::kOther), }; // The corresponding list of valid headerDemands uint8_t waterHeaterDemandValues[] = { - static_cast(WaterHeaterDemandBitmap::kImmersionElement1), - static_cast(WaterHeaterDemandBitmap::kImmersionElement2), - static_cast(WaterHeaterDemandBitmap::kHeatPump), - static_cast(WaterHeaterDemandBitmap::kBoiler), - static_cast(WaterHeaterDemandBitmap::kOther), + static_cast(WaterHeaterHeatSourceBitmap::kImmersionElement1), + static_cast(WaterHeaterHeatSourceBitmap::kImmersionElement2), + static_cast(WaterHeaterHeatSourceBitmap::kHeatPump), + static_cast(WaterHeaterHeatSourceBitmap::kBoiler), + static_cast(WaterHeaterHeatSourceBitmap::kOther), }; // Iterate across the valid waterHeaterTypes seeing which heating sources are available based on heaterTypes. // Set the corresponding bit in the heaterDemand bitmap. - BitMask heaterTypes = dg->GetHeaterTypes(); + BitMask heaterTypes = dg->GetHeaterTypes(); uint8_t heaterDemandMask = 0; for (uint16_t idx = 0; idx < static_cast(sizeof(waterHeaterTypeValues) / sizeof(waterHeaterTypeValues[0])); idx++) @@ -96,7 +96,7 @@ BitMask WhmManufacturer::DetermineHeatingSources() } } - return BitMask(heaterDemandMask); + return BitMask(heaterDemandMask); } Status WhmManufacturer::TurnHeatingOn(bool emergencyBoost) @@ -111,12 +111,12 @@ Status WhmManufacturer::TurnHeatingOn(bool emergencyBoost) { // emergencyBoost that the consumer wants the water to be heated as quickly as practicable. // Thus, cause multiple heat sources to be activated - dg->SetHeatDemand(BitMask(WaterHeaterDemandBitmap::kImmersionElement1, - WaterHeaterDemandBitmap::kImmersionElement2)); + dg->SetHeatDemand(BitMask(WaterHeaterHeatSourceBitmap::kImmersionElement1, + WaterHeaterHeatSourceBitmap::kImmersionElement2)); } else { - dg->SetHeatDemand(BitMask(WaterHeaterDemandBitmap::kImmersionElement1)); + dg->SetHeatDemand(BitMask(WaterHeaterHeatSourceBitmap::kImmersionElement1)); } return status; @@ -130,7 +130,7 @@ Status WhmManufacturer::TurnHeatingOff() WaterHeaterManagementDelegate * dg = GetWhmDelegate(); - dg->SetHeatDemand(BitMask(0)); + dg->SetHeatDemand(BitMask(0)); return status; } @@ -167,7 +167,7 @@ void SetTestEventTrigger_BasicInstallationTestEvent() // Simulate installation in a 100L tank full of water at 20C, with a target temperature of 60C, in OFF mode dg->SetTankVolume(100); dg->SetTargetWaterTemperature(6000); - dg->SetHeaterTypes(BitMask(WaterHeaterTypeBitmap::kImmersionElement1)); + dg->SetHeaterTypes(BitMask(WaterHeaterHeatSourceBitmap::kImmersionElement1)); dg->DrawOffHotWater(100, 2000); } diff --git a/examples/chip-tool/commands/pairing/OpenCommissioningWindowCommand.h b/examples/chip-tool/commands/pairing/OpenCommissioningWindowCommand.h index 3a199745601c93..9da6e937ee5e2b 100644 --- a/examples/chip-tool/commands/pairing/OpenCommissioningWindowCommand.h +++ b/examples/chip-tool/commands/pairing/OpenCommissioningWindowCommand.h @@ -38,7 +38,7 @@ class OpenCommissioningWindowCommand : public CHIPCommand "Time, in seconds, before the commissioning window closes."); AddArgument("iteration", chip::Crypto::kSpake2p_Min_PBKDF_Iterations, chip::Crypto::kSpake2p_Max_PBKDF_Iterations, &mIteration, "Number of PBKDF iterations to use to derive the verifier. Ignored if 'option' is 0."); - AddArgument("discriminator", 0, 4096, &mDiscriminator, "Discriminator to use for advertising. Ignored if 'option' is 0."); + AddArgument("discriminator", 0, 4095, &mDiscriminator, "Discriminator to use for advertising. Ignored if 'option' is 0."); AddArgument("timeout", 0, UINT16_MAX, &mTimeout, "Time, in seconds, before this command is considered to have timed out."); } diff --git a/examples/energy-management-app/silabs/.gn b/examples/energy-management-app/silabs/.gn new file mode 100644 index 00000000000000..b05216fc9d7eae --- /dev/null +++ b/examples/energy-management-app/silabs/.gn @@ -0,0 +1,29 @@ +# 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") + +# The location of the build configuration file. +buildconfig = "${build_root}/config/BUILDCONFIG.gn" + +# CHIP uses angle bracket includes. +check_system_includes = true + +default_args = { + target_cpu = "arm" + target_os = "freertos" + chip_openthread_ftd = true + + import("//openthread.gni") +} diff --git a/examples/energy-management-app/silabs/BUILD.gn b/examples/energy-management-app/silabs/BUILD.gn new file mode 100644 index 00000000000000..b51ed59de6f52c --- /dev/null +++ b/examples/energy-management-app/silabs/BUILD.gn @@ -0,0 +1,251 @@ +# 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_overrides/chip.gni") +import("//build_overrides/efr32_sdk.gni") +import("//build_overrides/pigweed.gni") + +import("${build_root}/config/defaults.gni") +import("${efr32_sdk_build_root}/silabs_executable.gni") + +import("${chip_root}/examples/common/pigweed/pigweed_rpcs.gni") +import("${chip_root}/src/platform/device.gni") +import("${chip_root}/third_party/silabs/silabs_board.gni") + +if (chip_enable_pw_rpc) { + import("//build_overrides/pigweed.gni") + import("$dir_pw_build/target_types.gni") +} + +assert(current_os == "freertos") + +silabs_project_dir = "${chip_root}/examples/energy-management-app/silabs" +examples_common_plat_dir = "${chip_root}/examples/platform/silabs" + +if (wifi_soc) { + import("${chip_root}/third_party/silabs/SiWx917_sdk.gni") + examples_plat_dir = "${chip_root}/examples/platform/silabs/SiWx917" +} else { + import("${efr32_sdk_build_root}/efr32_sdk.gni") + examples_plat_dir = "${chip_root}/examples/platform/silabs/efr32" +} + +import("${examples_common_plat_dir}/args.gni") + +declare_args() { + # Dump memory usage at link time. + chip_print_memory_usage = false +} + +if (slc_generate) { + # Generate Project Specific config (Board, hardware used etc..) + print(exec_script("${chip_root}/third_party/silabs/slc_gen/run_slc.py", + [ + rebase_path(chip_root), + "${silabs_board}", + "${disable_lcd}", + "${use_wstk_buttons}", + "${use_wstk_leds}", + "${use_external_flash}", + "${silabs_mcu}", + rebase_path(slc_gen_path), + ], + "list lines")) +} + +if (wifi_soc) { + siwx917_sdk("sdk") { + sources = [ + "${examples_common_plat_dir}/FreeRTOSConfig.h", + "${silabs_project_dir}/include/CHIPProjectConfig.h", + ] + + include_dirs = [ + "${chip_root}/src/platform/silabs/SiWx917", + "${silabs_project_dir}/include", + "${examples_plat_dir}", + "${chip_root}/src/lib", + "${examples_common_plat_dir}", + ] + + defines = [] + if (chip_enable_pw_rpc) { + defines += [ + "HAL_VCOM_ENABLE=1", + "PW_RPC_ENABLED", + ] + } + } +} else { + efr32_sdk("sdk") { + sources = [ + "${examples_common_plat_dir}/FreeRTOSConfig.h", + "${silabs_project_dir}/include/CHIPProjectConfig.h", + ] + + include_dirs = [ + "${chip_root}/src/platform/silabs/efr32", + "${silabs_project_dir}/include", + "${examples_plat_dir}", + "${chip_root}/src/lib", + "${examples_common_plat_dir}", + "../energy-management-common/include", + ] + + if (use_wf200) { + # TODO efr32_sdk should not need a header from this location + include_dirs += [ "${examples_plat_dir}/wf200" ] + } + + if (chip_enable_ble_rs911x) { + # TODO efr32_sdk should not need a header from this location + include_dirs += [ + "${examples_plat_dir}/rs911x", + "${examples_plat_dir}/rs911x/hal", + ] + } + + defines = [] + if (chip_enable_pw_rpc) { + defines += [ + "HAL_VCOM_ENABLE=1", + "PW_RPC_ENABLED", + ] + } + } +} +silabs_executable("energy-management-app") { + output_name = "matter-silabs-energy-management-example.out" + include_dirs = [ "include" ] + defines = [] + + if (silabs_board == "BRD2704A") { + defines += [ "SL_STATUS_LED=0" ] + } + + sources = [ + "${chip_root}/examples/energy-management-app/energy-management-common/src/ChargingTargetsMemMgr.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/DEMTestEventTriggers.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementManager.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/ElectricalPowerMeasurementDelegate.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseEventTriggers.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseMain.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseTargetsStore.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyReportingEventTriggers.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyTimeUtils.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/FakeReadings.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/PowerTopologyDelegate.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/device-energy-management-mode.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/energy-evse-mode.cpp", + "${examples_common_plat_dir}/main.cpp", + "src/AppTask.cpp", + ] + + deps = [ + ":sdk", + app_data_model, + ] + + if (wifi_soc) { + deps += [ "${examples_plat_dir}:siwx917-common" ] + } else { + deps += [ "${examples_plat_dir}:efr32-common" ] + } + + if (chip_enable_pw_rpc) { + defines += [ + "PW_RPC_ENABLED", + "PW_RPC_ATTRIBUTE_SERVICE=1", + "PW_RPC_BUTTON_SERVICE=1", + "PW_RPC_DESCRIPTOR_SERVICE=1", + "PW_RPC_DEVICE_SERVICE=1", + "PW_RPC_LIGHTING_SERVICE=1", + "PW_RPC_OTCLI_SERVICE=1", + "PW_RPC_THREAD_SERVICE=1", + "PW_RPC_TRACING_SERVICE=1", + ] + + sources += [ + "${chip_root}/examples/common/pigweed/RpcService.cpp", + "${chip_root}/examples/common/pigweed/efr32/PigweedLoggerMutex.cpp", + "${examples_common_plat_dir}/PigweedLogger.cpp", + "${examples_common_plat_dir}/Rpc.cpp", + ] + + deps += [ + "$dir_pw_hdlc:rpc_channel_output", + "$dir_pw_stream:sys_io_stream", + "$dir_pw_trace", + "$dir_pw_trace_tokenized", + "$dir_pw_trace_tokenized:trace_rpc_service", + "${chip_root}/config/efr32/lib/pw_rpc:pw_rpc", + "${chip_root}/examples/common/pigweed:attributes_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:button_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:descriptor_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:device_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:lighting_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:ot_cli_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:thread_service.nanopb_rpc", + ] + + if (wifi_soc) { + deps += [ "${examples_plat_dir}/pw_sys_io:pw_sys_io_siwx917" ] + } else { + deps += [ "${examples_common_plat_dir}/pw_sys_io:pw_sys_io_silabs" ] + } + + deps += pw_build_LINK_DEPS + + include_dirs += [ + "${chip_root}/examples/common", + "${chip_root}/examples/common/pigweed/efr32", + ] + } + + ldscript = "${examples_common_plat_dir}/ldscripts/${silabs_family}.ld" + + inputs = [ ldscript ] + + ldflags = [ "-T" + rebase_path(ldscript, root_build_dir) ] + + if (chip_print_memory_usage) { + ldflags += [ + "-Wl,--print-memory-usage", + "-fstack-usage", + ] + } + + # WiFi Settings + if (chip_enable_wifi) { + ldflags += [ + "-Wl,--defsym", + "-Wl,SILABS_WIFI=1", + ] + } + + output_dir = root_out_dir +} + +group("silabs") { + deps = [ ":energy-management-app" ] +} + +group("default") { + deps = [ ":silabs" ] +} diff --git a/examples/energy-management-app/silabs/README.md b/examples/energy-management-app/silabs/README.md new file mode 100644 index 00000000000000..06fc6f5c46b176 --- /dev/null +++ b/examples/energy-management-app/silabs/README.md @@ -0,0 +1,364 @@ +# Matter EFR32 Energy Management Example + +An example showing the use of CHIP on the Silicon Labs EFR32 MG12 and MG24. + +
+ +- [Matter EFR32 Energy Management Example](#matter-efr32-energy-management-example) + - [Introduction](#introduction) + - [Building](#building) + - [Flashing the Application](#flashing-the-application) + - [Viewing Logging Output](#viewing-logging-output) + - [Running the Complete Example](#running-the-complete-example) + - [Notes](#notes) + - [Running RPC console](#running-rpc-console) + - [Device Tracing](#device-tracing) + - [Memory settings](#memory-settings) + - [OTA Software Update](#ota-software-update) + - [Group Communication (Multicast)](#group-communication-multicast) + - [Building options](#building-options) + - [Disabling logging](#disabling-logging) + - [Debug build / release build](#debug-build--release-build) + - [Disabling LCD](#disabling-lcd) + - [KVS maximum entry count](#kvs-maximum-entry-count) + +
+ +> **NOTE:** Silicon Laboratories now maintains a public matter GitHub repo with +> frequent releases thoroughly tested and validated. Developers looking to +> develop matter products with silabs hardware are encouraged to use our latest +> release with added tools and documentation. +> [Silabs Matter Github](https://github.com/SiliconLabs/matter/releases) + +## Introduction + +The EFR32 Energy Management example provides a baseline demonstration of a EVSE +device, built using Matter and the Silicon Labs gecko SDK. It can be controlled +by a Chip controller over an Openthread or Wifi network.. + +The EFR32 device can be commissioned over Bluetooth Low Energy where the device +and the Chip controller will exchange security information with the Rendez-vous +procedure. If using Thread, Thread Network credentials are then provided to the +EFR32 device which will then join the Thread network. + +If the LCD is enabled, the LCD on the Silabs WSTK shows a QR Code containing the +needed commissioning information for the BLE connection and starting the +Rendez-vous procedure. + +The EVSE example is intended to serve both as a means to explore the workings of +Matter as well as a template for creating real products based on the Silicon +Labs platform. + +## Building + +- Download the + [Simplicity Commander](https://www.silabs.com/mcu/programming-options) + command line tool, and ensure that `commander` is your shell search path. + (For Mac OS X, `commander` is located inside + `Commander.app/Contents/MacOS/`.) + +- Download and install a suitable ARM gcc tool chain: + [GNU Arm Embedded Toolchain 9-2019-q4-major](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads) + +- Install some additional tools (likely already present for CHIP developers): + + - Linux: `sudo apt-get install git ninja-build` + + - Mac OS X: `brew install ninja` + +- Supported hardware: + + - > For the latest supported hardware please refer to the + > [Hardware Requirements](https://github.com/SiliconLabs/matter/blob/latest/docs/silabs/general/HARDWARE_REQUIREMENTS.md) + > in the Silicon Labs Matter Github Repo + + MG24 boards : + + - BRD2601B / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@10dBm + - BRD2703A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@10dBm + - BRD4186A / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@10dBm + - BRD4186C / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@10dBm + - BRD4187A / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@20dBm + - BRD4187C / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@20dBm + - BRD2703A / MG24 Explorer Kit + - BRD2704A / SparkFun Thing Plus MGM240P board + +* Build the example application: + + cd ~/connectedhomeip + ./scripts/examples/gn_silabs_example.sh ./examples/energy-management-app/silabs/ ./out/energy-management-app BRD4187C + +- To delete generated executable, libraries and object files use: + + $ cd ~/connectedhomeip + $ rm -rf ./out/ + + OR use GN/Ninja directly + + $ cd ~/connectedhomeip/examples/energy-management-app/silabs + $ git submodule update --init + $ source third_party/connectedhomeip/scripts/activate.sh + $ export SILABS_BOARD=BRD4187C + $ gn gen out/debug + $ ninja -C out/debug + +- To delete generated executable, libraries and object files use: + + $ cd ~/connectedhomeip/examples/energy-management-app/silabs + $ rm -rf out/ + +* Build the example as Intermittently Connected Device (ICD) + + $ ./scripts/examples/gn_silabs_example.sh ./examples/energy-management-app/silabs/ ./out/energy-management-app_ICD BRD4187C --icd + + or use gn as previously mentioned but adding the following arguments: + + $ gn gen out/debug '--args=SILABS_BOARD="BRD4187C" enable_sleepy_device=true chip_openthread_ftd=false' + +* Build the example with pigweed RPC + + $ ./scripts/examples/gn_silabs_example.sh examples/energy-management-app/silabs/ out/energy_management_app_rpc BRD4187C 'import("//with_pw_rpc.gni")' + + or use GN/Ninja Directly + + $ cd ~/connectedhomeip/examples/energy-management-app/silabs + $ git submodule update --init + $ source third_party/connectedhomeip/scripts/activate.sh + $ export SILABS_BOARD=BRD4187C + $ gn gen out/debug --args='import("//with_pw_rpc.gni")' + $ ninja -C out/debug + + [Running Pigweed RPC console](#running-rpc-console) + +For more build options, help is provided when running the build script without +arguments + + ./scripts/examples/gn_silabs_example.sh + +## Flashing the Application + +- On the command line: + + $ cd ~/connectedhomeip/examples/energy-management-app/silabs + $ python3 out/debug/matter-silabs-energy-management-example.flash.py + +- Or with the Ozone debugger, just load the .out file. + +All EFR32 boards require a bootloader, see Silicon Labs documentation for more +info. Pre-built bootloader binaries are available in the Assets section of the +Releases page on +[Silabs Matter Github](https://github.com/SiliconLabs/matter/releases) . + +## Viewing Logging Output + +The example application is built to use the SEGGER Real Time Transfer (RTT) +facility for log output. RTT is a feature built-in to the J-Link Interface MCU +on the WSTK development board. It allows bi-directional communication with an +embedded application without the need for a dedicated UART. + +Using the RTT facility requires downloading and installing the _SEGGER J-Link +Software and Documentation Pack_ +([web site](https://www.segger.com/downloads/jlink#J-LinkSoftwareAndDocumentationPack)). + +Alternatively, SEGGER Ozone J-Link debugger can be used to view RTT logs too +after flashing the .out file. + +- Download the J-Link installer by navigating to the appropriate URL and + agreeing to the license agreement. + +- [JLink_Linux_x86_64.deb](https://www.segger.com/downloads/jlink/JLink_Linux_x86_64.deb) +- [JLink_MacOSX.pkg](https://www.segger.com/downloads/jlink/JLink_MacOSX.pkg) + +* Install the J-Link software + + $ cd ~/Downloads + $ sudo dpkg -i JLink_Linux_V*_x86_64.deb + +* In Linux, grant the logged in user the ability to talk to the development + hardware via the linux tty device (/dev/ttyACMx) by adding them to the + dialout group. + + $ sudo usermod -a -G dialout ${USER} + +Once the above is complete, log output can be viewed using the JLinkExe tool in +combination with JLinkRTTClient as follows: + +- Run the JLinkExe tool with arguments to autoconnect to the WSTK board: + + For MG12 use: + + $ JLinkExe -device EFR32MG12PXXXF1024 -if JTAG -speed 4000 -autoconnect 1 + + For MG21 use: + + $ JLinkExe -device EFR32MG21AXXXF1024 -if SWD -speed 4000 -autoconnect 1 + +- In a second terminal, run the JLinkRTTClient to view logs: + + $ JLinkRTTClient + +## Running the Complete Example + +- It is assumed here that you already have an OpenThread border router + configured and running. If not see the following guide + [Openthread_border_router](https://github.com/project-chip/connectedhomeip/blob/master/docs/guides/openthread_border_router_pi.md) + for more information on how to setup a border router on a raspberryPi. + + Take note that the RCP code is available directly through + [Simplicity Studio 5](https://www.silabs.com/products/development-tools/software/simplicity-studio/simplicity-studio-5) + under File->New->Project Wizard->Examples->Thread : ot-rcp + +- User interface : **LCD** The LCD on Silabs WSTK shows a QR Code. This QR + Code is be scanned by the CHIP Tool app For the Rendez-vous procedure over + BLE + + * On devices that do not have or support the LCD Display like the BRD4166A Thunderboard Sense 2, + a URL can be found in the RTT logs. + + [SVR] Copy/paste the below URL in a browser to see the QR Code: + [SVR] https://project-chip.github.io/connectedhomeip/qrcode.html?data=CH%3AI34NM%20-00%200C9SS0 + + **LED 0** shows the overall state of the device and its connectivity. The + following states are possible: + + - _Short Flash On (50 ms on/950 ms off)_ ; The device is in the + unprovisioned (unpaired) state and is waiting for a commissioning + application to connect. + + - _Rapid Even Flashing_ ; (100 ms on/100 ms off)_ — The device is in the + unprovisioned state and a commissioning application is connected through + Bluetooth LE. + + - _Short Flash Off_ ; (950ms on/50ms off)_ — The device is fully + provisioned, but does not yet have full Thread network or service + connectivity. + + - _Solid On_ ; The device is fully provisioned and has full Thread + network and service connectivity. + + **Push Button 0** + + - _Press and Release_ : Start, or restart, BLE advertisement in fast mode. It will advertise in this mode + for 30 seconds. The device will then switch to a slower interval advertisement. + After 15 minutes, the advertisement stops. + + - _Pressed and hold for 6 s_ : Initiates the factory reset of the device. + Releasing the button within the 6-second window cancels the factory reset + procedure. **LEDs** blink in unison when the factory reset procedure is + initiated. + +* You can provision and control the Chip device using the python controller, + Chip tool standalone, Android or iOS app + +* You can provision and control the Chip device using the python controller, + Chip tool standalone, Android or iOS app + + [CHIPTool](https://github.com/project-chip/connectedhomeip/blob/master/examples/chip-tool/README.md) + + Here is an example with the chip-tool: + + $ chip-tool pairing ble-thread 1 hex: 20202021 3840 + +### Notes + +- Depending on your network settings your router might not provide native ipv6 + addresses to your devices (Border router / PC). If this is the case, you + need to add a static ipv6 addresses on both device and then an ipv6 route to + the border router on your PC + + - On Border Router: `sudo ip addr add dev 2002::2/64` + + - On PC(Linux): `sudo ip addr add dev 2002::1/64` + + - Add Ipv6 route on PC(Linux) + `sudo ip route add /64 via 2002::2` + +## Running RPC console + +- As part of building the example with RPCs enabled the chip_rpc python + interactive console is installed into your venv. The python wheel files are + also created in the output folder: out/debug/chip_rpc_console_wheels. To + install the wheel files without rebuilding: + `pip3 install out/debug/chip_rpc_console_wheels/*.whl` + +- To use the chip-rpc console after it has been installed run: + `chip-console --device /dev/tty. -b 115200 -o //pw_log.out` + +- Then you can simulate a button press or release using the following command + where : idx = 0 or 1 for Button PB0 or PB1 action = 0 for PRESSED, 1 for + RELEASE Test toggling the LED with + `rpcs.chip.rpc.Button.Event(idx=1, pushed=True)` + +## Device Tracing + +Device tracing is available to analyze the device performance. To turn on +tracing, build with RPC enabled. See Build the example with pigweed RPC. + +Obtain tracing json file. + + $ ./{PIGWEED_REPO}/pw_trace_tokenized/py/pw_trace_tokenized/get_trace.py -d {PORT} -o {OUTPUT_FILE} \ + -t {ELF_FILE} {PIGWEED_REPO}/pw_trace_tokenized/pw_trace_protos/trace_rpc.proto + +## Memory settings + +While most of the RAM usage in CHIP is static, allowing easier debugging and +optimization with symbols analysis, we still need some HEAP for the crypto and +OpenThread. Size of the HEAP can be modified by changing the value of the +`configTOTAL_HEAP_SIZE` define inside of the FreeRTOSConfig.h file of this +example. Please take note that a HEAP size smaller than 13k can and will cause a +Mbedtls failure during the BLE rendez-vous or CASE session + +To track memory usage you can set `enable_heap_monitoring = true` either in the +BUILD.gn file or pass it as a build argument to gn. This will print on the RTT +console the RAM usage of each individual task and the number of Memory +allocation and Free. While this is not extensive monitoring you're welcome to +modify `examples/platform/silabs/MemMonitoring.cpp` to add your own memory +tracking code inside the `trackAlloc` and `trackFree` function + +## OTA Software Update + +For the description of Software Update process with EFR32 example applications +see +[EFR32 OTA Software Update](../../../docs/guides/silabs_efr32_software_update.md) + +## Group Communication (Multicast) + +With this Energy Management example you can also use group communication to send +Energy Management commands to multiples devices at once. Please refer to the +[chip-tool documentation](../../chip-tool/README.md) _Configuring the server +side for Group Commands_ and _Using the Client to Send Group (Multicast) Matter +Commands_ + +## Building options + +All of Silabs's examples within the Matter repo have all the features enabled by +default, as to provide the best end user experience. However some of those +features can easily be toggled on or off. Here is a short list of options to be +passed to the build scripts. + +### Disabling logging + +`chip_progress_logging, chip_detail_logging, chip_automation_logging` + + $ ./scripts/examples/gn_silabs_example.sh ./examples/energy-management-app/silabs ./out/energy-management-app BRD4164A "chip_detail_logging=false chip_automation_logging=false chip_progress_logging=false" + +### Debug build / release build + +`is_debug` + + $ ./scripts/examples/gn_silabs_example.sh ./examples/energy-management-app/silabs ./out/energy-management-app BRD4164A "is_debug=false" + +### Disabling LCD + +`show_qr_code` + + $ ./scripts/examples/gn_silabs_example.sh ./examples/energy-management-app/silabs ./out/energy-management-app BRD4164A "show_qr_code=false" + +### KVS maximum entry count + +`kvs_max_entries` + + Set the maximum Kvs entries that can be stored in NVM (Default 75) + Thresholds: 30 <= kvs_max_entries <= 255 + + $ ./scripts/examples/gn_silabs_example.sh ./examples/energy-management-app/silabs ./out/energy-management-app BRD4164A kvs_max_entries=50 diff --git a/examples/energy-management-app/silabs/build_for_wifi_args.gni b/examples/energy-management-app/silabs/build_for_wifi_args.gni new file mode 100644 index 00000000000000..bfded904a34176 --- /dev/null +++ b/examples/energy-management-app/silabs/build_for_wifi_args.gni @@ -0,0 +1,24 @@ +# 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/chip.gni") +import("${chip_root}/config/standalone/args.gni") + +silabs_sdk_target = get_label_info(":sdk", "label_no_toolchain") +chip_enable_openthread = false +import("${chip_root}/src/platform/silabs/wifi_args.gni") + +chip_enable_ota_requestor = false +disable_lcd = true +app_data_model = + "${chip_root}/examples/energy-management-app/energy-management-common" diff --git a/examples/energy-management-app/silabs/build_for_wifi_gnfile.gn b/examples/energy-management-app/silabs/build_for_wifi_gnfile.gn new file mode 100644 index 00000000000000..d391814190d09f --- /dev/null +++ b/examples/energy-management-app/silabs/build_for_wifi_gnfile.gn @@ -0,0 +1,28 @@ +# 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") + +# The location of the build configuration file. +buildconfig = "${build_root}/config/BUILDCONFIG.gn" + +# CHIP uses angle bracket includes. +check_system_includes = true + +default_args = { + target_cpu = "arm" + target_os = "freertos" + chip_enable_wifi = true + import("//build_for_wifi_args.gni") +} diff --git a/examples/energy-management-app/silabs/build_overrides b/examples/energy-management-app/silabs/build_overrides new file mode 120000 index 00000000000000..e578e73312ebd1 --- /dev/null +++ b/examples/energy-management-app/silabs/build_overrides @@ -0,0 +1 @@ +../../build_overrides \ No newline at end of file diff --git a/examples/energy-management-app/silabs/include/AppConfig.h b/examples/energy-management-app/silabs/include/AppConfig.h new file mode 100644 index 00000000000000..bbd4d201124fbf --- /dev/null +++ b/examples/energy-management-app/silabs/include/AppConfig.h @@ -0,0 +1,28 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Google LLC. + * All rights reserved. + * + * 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. + */ + +#pragma once + +#include "silabs_utils.h" + +// ---- EVSE Example App Config ---- + +#define APP_TASK_NAME "EVSE" + +#define BLE_DEV_NAME "SiLabs-EVSE" diff --git a/examples/energy-management-app/silabs/include/AppEvent.h b/examples/energy-management-app/silabs/include/AppEvent.h new file mode 100644 index 00000000000000..7f28962b50633f --- /dev/null +++ b/examples/energy-management-app/silabs/include/AppEvent.h @@ -0,0 +1,54 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2018 Nest Labs, Inc. + * All rights reserved. + * + * 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. + */ + +#pragma once + +struct AppEvent; +typedef void (*EventHandler)(AppEvent *); + +struct AppEvent +{ + enum AppEventTypes + { + kEventType_Button = 0, + kEventType_Timer, + kEventType_Install, + }; + + uint16_t Type; + + union + { + struct + { + uint8_t Action; + } ButtonEvent; + struct + { + void * Context; + } TimerEvent; + struct + { + uint8_t Action; + int32_t Actor; + } EvseEvent; + }; + + EventHandler Handler; +}; diff --git a/examples/energy-management-app/silabs/include/AppTask.h b/examples/energy-management-app/silabs/include/AppTask.h new file mode 100644 index 00000000000000..99114610948952 --- /dev/null +++ b/examples/energy-management-app/silabs/include/AppTask.h @@ -0,0 +1,110 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Google LLC. + * All rights reserved. + * + * 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. + */ + +#pragma once + +/********************************************************** + * Includes + *********************************************************/ + +#include +#include + +#include "AppEvent.h" +#include "BaseApplication.h" +#include "EnergyEvseManager.h" +#include "FreeRTOS.h" +#include "timers.h" // provides FreeRTOS timer support +#include +#include +#include + +/********************************************************** + * Defines + *********************************************************/ + +// Application-defined error codes in the CHIP_ERROR space. +#define APP_ERROR_EVENT_QUEUE_FAILED CHIP_APPLICATION_ERROR(0x01) +#define APP_ERROR_CREATE_TASK_FAILED CHIP_APPLICATION_ERROR(0x02) +#define APP_ERROR_UNHANDLED_EVENT CHIP_APPLICATION_ERROR(0x03) +#define APP_ERROR_CREATE_TIMER_FAILED CHIP_APPLICATION_ERROR(0x04) +#define APP_ERROR_START_TIMER_FAILED CHIP_APPLICATION_ERROR(0x05) +#define APP_ERROR_STOP_TIMER_FAILED CHIP_APPLICATION_ERROR(0x06) + +/********************************************************** + * AppTask Declaration + *********************************************************/ + +class AppTask : public BaseApplication +{ + +public: + AppTask() = default; + + static AppTask & GetAppTask() { return sAppTask; } + + /** + * @brief AppTask task main loop function + * + * @param pvParameter FreeRTOS task parameter + */ + static void AppTaskMain(void * pvParameter); + + CHIP_ERROR StartAppTask(); + + /** + * @brief Event handler when a button is pressed + * Function posts an event for button processing + * + * @param buttonHandle APP_FUNCTION_BUTTON + * @param btnAction button action - SL_SIMPLE_BUTTON_PRESSED, + * SL_SIMPLE_BUTTON_RELEASED or SL_SIMPLE_BUTTON_DISABLED + */ + static void ButtonEventHandler(uint8_t button, uint8_t btnAction); + +private: + static AppTask sAppTask; + static void EvseActionEventHandler(AppEvent * aEvent); + + static void UpdateClusterState(intptr_t context); + + /** + * @brief AppTask initialisation function + * + * @return CHIP_ERROR + */ + CHIP_ERROR Init(); + + /** + * @brief PB0 Button event processing function + * Press and hold will trigger a factory reset timer start + * Press and release will restart BLEAdvertising if not commisionned + * + * @param aEvent button event being processed + */ + static void ButtonHandler(AppEvent * aEvent); + + /** + * @brief PB1 Button event processing function + * Function triggers a switch action sent to the CHIP task + * + * @param aEvent button event being processed + */ + static void SwitchActionEventHandler(AppEvent * aEvent); +}; diff --git a/examples/energy-management-app/silabs/include/CHIPProjectConfig.h b/examples/energy-management-app/silabs/include/CHIPProjectConfig.h new file mode 100644 index 00000000000000..62943f4eef1b63 --- /dev/null +++ b/examples/energy-management-app/silabs/include/CHIPProjectConfig.h @@ -0,0 +1,102 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Google LLC. + * All rights reserved. + * + * 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. + */ + +/** + * @file + * Example project configuration file for CHIP. + * + * This is a place to put application or project-specific overrides + * to the default configuration values for general CHIP features. + * + */ + +#pragma once + +// Use a default pairing code if one hasn't been provisioned in flash. +#ifndef CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE +#define CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE 20202021 +#endif + +#ifndef CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR +#define CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR 0xF00 +#endif + +// For convenience, Chip Security Test Mode can be enabled and the +// requirement for authentication in various protocols can be disabled. +// +// WARNING: These options make it possible to circumvent basic Chip security functionality, +// including message encryption. Because of this they MUST NEVER BE ENABLED IN PRODUCTION BUILDS. +// +#define CHIP_CONFIG_SECURITY_TEST_MODE 0 + +/** + * CHIP_DEVICE_CONFIG_DEVICE_VENDOR_ID + * + * 0xFFF1: Test vendor + */ +#define CHIP_DEVICE_CONFIG_DEVICE_VENDOR_ID 0xFFF1 + +/** + * CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_ID + * + * 0x8005: example EVSE app + */ +#define CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_ID 0x8005 + +/** + * CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE + * + * Enable support for Chip-over-BLE (CHIPoBLE). + */ +#define CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE 1 + +/** + * CHIP_DEVICE_CONFIG_TEST_SERIAL_NUMBER + * + * Enables the use of a hard-coded default serial number if none + * is found in Chip NV storage. + */ +#define CHIP_DEVICE_CONFIG_TEST_SERIAL_NUMBER "TEST_SN" + +/** + * CHIP_DEVICE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS + * + * Enable recording UTC timestamps. + */ +#define CHIP_DEVICE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS 1 + +/** + * CHIP_DEVICE_CONFIG_EVENT_LOGGING_DEBUG_BUFFER_SIZE + * + * A size, in bytes, of the individual debug event logging buffer. + */ +#define CHIP_DEVICE_CONFIG_EVENT_LOGGING_DEBUG_BUFFER_SIZE (512) + +/** + * @def CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL + * + * @brief + * Active retransmit interval, or time to wait before retransmission after + * subsequent failures in milliseconds. + * + * This is the default value, that might be adjusted by end device depending on its + * needs (e.g. sleeping period) using Service Discovery TXT record CRA key. + * + */ +#define CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL (2000_ms32) diff --git a/examples/energy-management-app/silabs/openthread.gn b/examples/energy-management-app/silabs/openthread.gn new file mode 100644 index 00000000000000..b05216fc9d7eae --- /dev/null +++ b/examples/energy-management-app/silabs/openthread.gn @@ -0,0 +1,29 @@ +# 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") + +# The location of the build configuration file. +buildconfig = "${build_root}/config/BUILDCONFIG.gn" + +# CHIP uses angle bracket includes. +check_system_includes = true + +default_args = { + target_cpu = "arm" + target_os = "freertos" + chip_openthread_ftd = true + + import("//openthread.gni") +} diff --git a/examples/energy-management-app/silabs/openthread.gni b/examples/energy-management-app/silabs/openthread.gni new file mode 100644 index 00000000000000..2813a5a1fb0bda --- /dev/null +++ b/examples/energy-management-app/silabs/openthread.gni @@ -0,0 +1,28 @@ +# 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/chip.gni") +import("${chip_root}/config/standalone/args.gni") +import("${chip_root}/src/platform/silabs/efr32/args.gni") + +silabs_sdk_target = get_label_info(":sdk", "label_no_toolchain") + +app_data_model = + "${chip_root}/examples/energy-management-app/energy-management-common" +chip_enable_ota_requestor = false +chip_enable_openthread = true +disable_lcd = true + +openthread_external_platform = + "${chip_root}/third_party/openthread/platforms/efr32:libopenthread-efr32" diff --git a/examples/energy-management-app/silabs/src/AppTask.cpp b/examples/energy-management-app/silabs/src/AppTask.cpp new file mode 100644 index 00000000000000..4ed73efb28711e --- /dev/null +++ b/examples/energy-management-app/silabs/src/AppTask.cpp @@ -0,0 +1,212 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Google LLC. + * All rights reserved. + * + * 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 "AppTask.h" +#include "AppConfig.h" +#include "AppEvent.h" +#include "LEDWidget.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include + +#if (defined(SL_CATALOG_SIMPLE_LED_LED1_PRESENT) || defined(SIWX_917)) +#define EVSE_LED 1 +#else +#define EVSE_LED 0 +#endif + +#define APP_FUNCTION_BUTTON 0 +#define APP_EVSE_SWITCH 1 + +namespace { +LEDWidget sEvseLED; +} + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::DeviceEnergyManagement; +using namespace chip::app::Clusters::DeviceEnergyManagement::Attributes; +using namespace ::chip::DeviceLayer; +using namespace ::chip::DeviceLayer::Silabs; +using namespace ::chip::DeviceLayer::Internal; +using namespace chip::TLV; + +namespace chip { +namespace app { +namespace Clusters { +namespace DeviceEnergyManagement { + +// Keep track of the parsed featureMap option +static chip::BitMask sFeatureMap(Feature::kPowerAdjustment, Feature::kPowerForecastReporting, + Feature::kStateForecastReporting, Feature::kStartTimeAdjustment, Feature::kPausable, + Feature::kForecastAdjustment, Feature::kConstraintBasedAdjustment); + +chip::BitMask GetFeatureMapFromCmdLine() +{ + return sFeatureMap; +} + +} // namespace DeviceEnergyManagement +} // namespace Clusters +} // namespace app +} // namespace chip + +AppTask AppTask::sAppTask; + +void ApplicationInit() +{ + chip::DeviceLayer::PlatformMgr().LockChipStack(); + EvseApplicationInit(); + sEvseLED.Init(EVSE_LED); + chip::DeviceLayer::PlatformMgr().UnlockChipStack(); +} +void ApplicationShutdown() +{ + chip::DeviceLayer::PlatformMgr().LockChipStack(); + EvseApplicationShutdown(); + chip::DeviceLayer::PlatformMgr().UnlockChipStack(); +} + +CHIP_ERROR AppTask::Init() +{ + CHIP_ERROR err = CHIP_NO_ERROR; + chip::DeviceLayer::Silabs::GetPlatform().SetButtonsCb(AppTask::ButtonEventHandler); + +#ifdef DISPLAY_ENABLED + GetLCD().Init((uint8_t *) "energy-management-App"); +#endif + + err = BaseApplication::Init(); + if (err != CHIP_NO_ERROR) + { + SILABS_LOG("BaseApplication::Init() failed"); + appError(err); + } + + ApplicationInit(); + +// Update the LCD with the Stored value. Show QR Code if not provisioned +#ifdef DISPLAY_ENABLED + GetLCD().WriteDemoUI(LightMgr().IsLightOn()); +#ifdef QR_CODE_ENABLED +#ifdef SL_WIFI + if (!chip::DeviceLayer::ConnectivityMgr().IsWiFiStationProvisioned()) +#else + if (!chip::DeviceLayer::ConnectivityMgr().IsThreadProvisioned()) +#endif /* !SL_WIFI */ + { + GetLCD().ShowQRCode(true); + } +#endif // QR_CODE_ENABLED +#endif + + return err; +} + +CHIP_ERROR AppTask::StartAppTask() +{ + return BaseApplication::StartAppTask(AppTaskMain); +} + +void AppTask::AppTaskMain(void * pvParameter) +{ + AppEvent event; + osMessageQueueId_t sAppEventQueue = *(static_cast(pvParameter)); + + CHIP_ERROR err = sAppTask.Init(); + if (err != CHIP_NO_ERROR) + { + SILABS_LOG("AppTask.Init() failed"); + appError(err); + } + + SILABS_LOG("App Task started"); + + while (true) + { + osStatus_t eventReceived = osMessageQueueGet(sAppEventQueue, &event, NULL, osWaitForever); + while (eventReceived == osOK) + { + sAppTask.DispatchEvent(&event); + eventReceived = osMessageQueueGet(sAppEventQueue, &event, NULL, 0); + } + } +} + +void AppTask::EvseActionEventHandler(AppEvent * aEvent) +{ + bool initiated = false; + int32_t actor; + CHIP_ERROR err = CHIP_NO_ERROR; + + if (aEvent->Type == AppEvent::kEventType_Button) + { + actor = AppEvent::kEventType_Button; + SILABS_LOG("button event %d ", actor); + } + else + { + err = APP_ERROR_UNHANDLED_EVENT; + } + + if (err == CHIP_NO_ERROR) + { + if (!initiated) + { + SILABS_LOG("Action is already in progress or active."); + } + } +} + +void AppTask::ButtonEventHandler(uint8_t button, uint8_t btnAction) +{ + AppEvent button_event = {}; + button_event.Type = AppEvent::kEventType_Button; + button_event.ButtonEvent.Action = btnAction; + + if (button == APP_EVSE_SWITCH && btnAction == static_cast(SilabsPlatform::ButtonAction::ButtonPressed)) + { + button_event.Handler = EvseActionEventHandler; + AppTask::GetAppTask().PostEvent(&button_event); + } + else if (button == APP_FUNCTION_BUTTON) + { + button_event.Handler = BaseApplication::ButtonHandler; + AppTask::GetAppTask().PostEvent(&button_event); + } +} diff --git a/examples/energy-management-app/silabs/third_party/connectedhomeip b/examples/energy-management-app/silabs/third_party/connectedhomeip new file mode 120000 index 00000000000000..c866b86874994d --- /dev/null +++ b/examples/energy-management-app/silabs/third_party/connectedhomeip @@ -0,0 +1 @@ +../../../.. \ No newline at end of file diff --git a/examples/energy-management-app/silabs/with_pw_rpc.gni b/examples/energy-management-app/silabs/with_pw_rpc.gni new file mode 100644 index 00000000000000..ac5d9953a3b924 --- /dev/null +++ b/examples/energy-management-app/silabs/with_pw_rpc.gni @@ -0,0 +1,30 @@ +# Copyright (c) 2021 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. + +# add this gni as import in your build args to use pigweed in the example +# 'import("//with_pw_rpc.gni")' + +import("//build_overrides/chip.gni") +import("${chip_root}/config/efr32/lib/pw_rpc/pw_rpc.gni") +import("${chip_root}/examples/platform/silabs/args.gni") + +silabs_sdk_target = get_label_info(":sdk", "label_no_toolchain") + +app_data_model = + "${chip_root}/examples/energy-management-app/energy-management-common" +chip_enable_pw_rpc = true +chip_enable_openthread = true + +# Light app on EFR enables tracing server +pw_trace_BACKEND = "$dir_pw_trace_tokenized" diff --git a/examples/fabric-admin/commands/clusters/ReportCommand.cpp b/examples/fabric-admin/commands/clusters/ReportCommand.cpp index 8eb2cc7c33ae0d..ff52a30e661342 100644 --- a/examples/fabric-admin/commands/clusters/ReportCommand.cpp +++ b/examples/fabric-admin/commands/clusters/ReportCommand.cpp @@ -46,7 +46,7 @@ void ReportCommand::OnAttributeData(const app::ConcreteDataAttributePath & path, LogErrorOnFailure(RemoteDataModelLogger::LogAttributeAsJSON(path, data)); - DeviceMgr().HandleAttributeChange(path, data); + DeviceMgr().HandleAttributeData(path, data); } void ReportCommand::OnEventData(const app::EventHeader & eventHeader, TLV::TLVReader * data, const app::StatusIB * status) @@ -73,11 +73,5 @@ void ReportCommand::OnEventData(const app::EventHeader & eventHeader, TLV::TLVRe LogErrorOnFailure(RemoteDataModelLogger::LogEventAsJSON(eventHeader, data)); - CHIP_ERROR error = DataModelLogger::LogEvent(eventHeader, data); - if (CHIP_NO_ERROR != error) - { - ChipLogError(NotSpecified, "Response Failure: Can not decode Data"); - mError = error; - return; - } + DeviceMgr().HandleEventData(eventHeader, data); } diff --git a/examples/fabric-admin/commands/common/CHIPCommand.h b/examples/fabric-admin/commands/common/CHIPCommand.h index af833f5f718652..6711b3e4f9aa3d 100644 --- a/examples/fabric-admin/commands/common/CHIPCommand.h +++ b/examples/fabric-admin/commands/common/CHIPCommand.h @@ -51,6 +51,8 @@ inline constexpr char kIdentityGamma[] = "gamma"; // (CASE) communcation. inline constexpr char kIdentityNull[] = "null-fabric-commissioner"; +constexpr uint16_t kMaxCommandSize = 384; + class CHIPCommand : public Command { public: diff --git a/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.cpp b/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.cpp index 3e050e90904867..dade5932fba758 100644 --- a/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.cpp +++ b/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.cpp @@ -35,9 +35,11 @@ namespace { // Constants constexpr uint32_t kCommissionPrepareTimeMs = 500; constexpr uint16_t kMaxManaulCodeLength = 21; -constexpr uint16_t kSubscribeMinInterval = 0; -constexpr uint16_t kSubscribeMaxInterval = 60; -constexpr uint16_t kRemoteBridgePort = 5540; + +void CheckFabricBridgeSynchronizationSupport(intptr_t ignored) +{ + DeviceMgr().ReadSupportedDeviceCategories(); +} } // namespace @@ -45,8 +47,16 @@ void FabricSyncAddBridgeCommand::OnCommissioningComplete(chip::NodeId deviceId, { if (mBridgeNodeId != deviceId) { - ChipLogProgress(NotSpecified, "Commissioning complete for non-bridge device: NodeId: " ChipLogFormatX64, - ChipLogValueX64(deviceId)); + if (err != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "Failed to pair non-bridge device (0x:" ChipLogFormatX64 ") with error: %" CHIP_ERROR_FORMAT, + ChipLogValueX64(deviceId), err.Format()); + } + else + { + ChipLogProgress(NotSpecified, "Commissioning complete for non-bridge device: NodeId: " ChipLogFormatX64, + ChipLogValueX64(deviceId)); + } return; } @@ -56,11 +66,15 @@ void FabricSyncAddBridgeCommand::OnCommissioningComplete(chip::NodeId deviceId, ChipLogProgress(NotSpecified, "Successfully paired bridge device: NodeId: " ChipLogFormatX64, ChipLogValueX64(mBridgeNodeId)); - char command[kMaxCommandSize]; - snprintf(command, sizeof(command), "descriptor subscribe parts-list %d %d %ld %d", kSubscribeMinInterval, - kSubscribeMaxInterval, mBridgeNodeId, kAggragatorEndpointId); + DeviceMgr().SubscribeRemoteFabricBridge(); - PushCommand(command); + // After successful commissioning of the Commissionee, initiate Reverse Commissioning + // via the Commissioner Control Cluster. However, we must first verify that the + // remote Fabric-Bridge supports Fabric Synchronization. + // + // Note: The Fabric-Admin MUST NOT send the RequestCommissioningApproval command + // if the remote Fabric-Bridge lacks Fabric Synchronization support. + DeviceLayer::PlatformMgr().ScheduleWork(CheckFabricBridgeSynchronizationSupport, 0); } else { @@ -80,10 +94,6 @@ CHIP_ERROR FabricSyncAddBridgeCommand::RunCommand(NodeId remoteId) return CHIP_NO_ERROR; } - char command[kMaxCommandSize]; - snprintf(command, sizeof(command), "pairing already-discovered %ld %d %s %d", remoteId, kSetupPinCode, - reinterpret_cast(mRemoteAddr.data()), kRemoteBridgePort); - PairingCommand * pairingCommand = static_cast(CommandMgr().GetCommandByName("pairing", "already-discovered")); if (pairingCommand == nullptr) @@ -95,7 +105,7 @@ CHIP_ERROR FabricSyncAddBridgeCommand::RunCommand(NodeId remoteId) pairingCommand->RegisterCommissioningDelegate(this); mBridgeNodeId = remoteId; - PushCommand(command); + DeviceMgr().PairRemoteFabricBridge(remoteId, reinterpret_cast(mRemoteAddr.data())); return CHIP_NO_ERROR; } @@ -136,9 +146,6 @@ CHIP_ERROR FabricSyncRemoveBridgeCommand::RunCommand() mBridgeNodeId = bridgeNodeId; - char command[kMaxCommandSize]; - snprintf(command, sizeof(command), "pairing unpair %ld", mBridgeNodeId); - PairingCommand * pairingCommand = static_cast(CommandMgr().GetCommandByName("pairing", "unpair")); if (pairingCommand == nullptr) @@ -149,13 +156,15 @@ CHIP_ERROR FabricSyncRemoveBridgeCommand::RunCommand() pairingCommand->RegisterPairingDelegate(this); - PushCommand(command); + DeviceMgr().UnpairRemoteFabricBridge(); return CHIP_NO_ERROR; } void FabricSyncDeviceCommand::OnCommissioningWindowOpened(NodeId deviceId, CHIP_ERROR err, chip::SetupPayload payload) { + ChipLogProgress(NotSpecified, "FabricSyncDeviceCommand::OnCommissioningWindowOpened"); + if (err == CHIP_NO_ERROR) { char payloadBuffer[kMaxManaulCodeLength + 1]; @@ -163,9 +172,7 @@ void FabricSyncDeviceCommand::OnCommissioningWindowOpened(NodeId deviceId, CHIP_ CHIP_ERROR error = ManualSetupPayloadGenerator(payload).payloadDecimalStringRepresentation(manualCode); if (error == CHIP_NO_ERROR) { - char command[kMaxCommandSize]; NodeId nodeId = DeviceMgr().GetNextAvailableNodeId(); - snprintf(command, sizeof(command), "pairing code %ld %s", nodeId, payloadBuffer); PairingCommand * pairingCommand = static_cast(CommandMgr().GetCommandByName("pairing", "code")); @@ -180,7 +187,7 @@ void FabricSyncDeviceCommand::OnCommissioningWindowOpened(NodeId deviceId, CHIP_ usleep(kCommissionPrepareTimeMs * 1000); - PushCommand(command); + DeviceMgr().PairRemoteDevice(nodeId, payloadBuffer); } else { @@ -224,10 +231,6 @@ CHIP_ERROR FabricSyncDeviceCommand::RunCommand(EndpointId remoteId) return CHIP_NO_ERROR; } - char command[kMaxCommandSize]; - snprintf(command, sizeof(command), "pairing open-commissioning-window %ld %d %d %d %d %d", DeviceMgr().GetRemoteBridgeNodeId(), - remoteId, kEnhancedCommissioningMethod, kWindowTimeout, kIteration, kDiscriminator); - OpenCommissioningWindowCommand * openCommand = static_cast(CommandMgr().GetCommandByName("pairing", "open-commissioning-window")); @@ -238,7 +241,7 @@ CHIP_ERROR FabricSyncDeviceCommand::RunCommand(EndpointId remoteId) openCommand->RegisterDelegate(this); - PushCommand(command); + DeviceMgr().OpenRemoteDeviceCommissioningWindow(remoteId); return CHIP_NO_ERROR; } diff --git a/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.h b/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.h index 038094583c8554..ca1b105207f832 100644 --- a/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.h +++ b/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.h @@ -22,14 +22,6 @@ #include #include -constexpr uint32_t kSetupPinCode = 20202021; -constexpr uint16_t kMaxCommandSize = 64; -constexpr uint16_t kDiscriminator = 3840; -constexpr uint16_t kWindowTimeout = 300; -constexpr uint16_t kIteration = 1000; -constexpr uint16_t kAggragatorEndpointId = 1; -constexpr uint8_t kEnhancedCommissioningMethod = 1; - class FabricSyncAddBridgeCommand : public CHIPCommand, public CommissioningDelegate { public: diff --git a/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.cpp b/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.cpp index b2d811fdc8b114..53073160108036 100644 --- a/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.cpp +++ b/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.cpp @@ -55,6 +55,7 @@ CHIP_ERROR OpenCommissioningWindowCommand::RunCommand() .SetTimeout(mCommissioningWindowTimeout) .SetIteration(mIteration) .SetDiscriminator(mDiscriminator) + .SetSetupPIN(mSetupPIN) .SetSalt(mSalt) .SetReadVIDPIDAttributes(true) .SetCallback(&mOnOpenCommissioningWindowCallback), diff --git a/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.h b/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.h index 09788507210aaf..7edcdba7115665 100644 --- a/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.h +++ b/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.h @@ -46,8 +46,9 @@ class OpenCommissioningWindowCommand : public CHIPCommand "Time, in seconds, before the commissioning window closes."); AddArgument("iteration", chip::Crypto::kSpake2p_Min_PBKDF_Iterations, chip::Crypto::kSpake2p_Max_PBKDF_Iterations, &mIteration, "Number of PBKDF iterations to use to derive the verifier. Ignored if 'option' is 0."); - AddArgument("discriminator", 0, 4096, &mDiscriminator, "Discriminator to use for advertising. Ignored if 'option' is 0."); + AddArgument("discriminator", 0, 4095, &mDiscriminator, "Discriminator to use for advertising. Ignored if 'option' is 0."); AddArgument("timeout", 0, UINT16_MAX, &mTimeout, "Time, in seconds, before this command is considered to have timed out."); + AddArgument("setup-pin", 1, chip::kSetupPINCodeMaximumValue, &mSetupPIN, "The setup PIN (Passcode) to use."); AddArgument("salt", &mSalt, "Salt payload encoded in hexadecimal. Random salt will be generated if absent. " "This needs to be present if verifier is provided, corresponding to salt used for generating verifier"); @@ -76,6 +77,7 @@ class OpenCommissioningWindowCommand : public CHIPCommand uint16_t mDiscriminator; chip::Optional mTimeout; + chip::Optional mSetupPIN; chip::Optional mSalt; chip::Optional mVerifier; diff --git a/examples/fabric-admin/device_manager/DeviceManager.cpp b/examples/fabric-admin/device_manager/DeviceManager.cpp index 4ea0b033c47c7f..4207378670f44e 100644 --- a/examples/fabric-admin/device_manager/DeviceManager.cpp +++ b/examples/fabric-admin/device_manager/DeviceManager.cpp @@ -19,6 +19,8 @@ #include "DeviceManager.h" #include +#include +#include #include #include @@ -26,6 +28,21 @@ using namespace chip; using namespace chip::app::Clusters; +namespace { + +// Constants +constexpr uint32_t kSetupPinCode = 20202021; +constexpr uint16_t kRemoteBridgePort = 5540; +constexpr uint16_t kWindowTimeout = 300; +constexpr uint16_t kIteration = 1000; +constexpr uint16_t kSubscribeMinInterval = 0; +constexpr uint16_t kSubscribeMaxInterval = 60; +constexpr uint16_t kAggragatorEndpointId = 1; +constexpr uint16_t kMaxDiscriminatorLength = 4095; +constexpr uint8_t kEnhancedCommissioningMethod = 1; + +} // namespace + // Define the static member DeviceManager DeviceManager::sInstance; @@ -97,8 +114,159 @@ void DeviceManager::RemoveSyncedDevice(NodeId nodeId) ChipLogValueX64(device->GetNodeId()), device->GetEndpointId()); } -void DeviceManager::HandleAttributeChange(const app::ConcreteDataAttributePath & path, TLV::TLVReader * data) +void DeviceManager::OpenDeviceCommissioningWindow(NodeId nodeId, uint32_t commissioningTimeout, uint32_t iterations, + uint32_t discriminator, const char * saltHex, const char * verifierHex) +{ + // Open the commissioning window of a device within its own fabric. + StringBuilder commandBuilder; + + commandBuilder.Add("pairing open-commissioning-window "); + commandBuilder.AddFormat("%lu %d %d %d %d %d --salt hex:%s --verifier hex:%s", nodeId, kRootEndpointId, + kEnhancedCommissioningMethod, commissioningTimeout, iterations, discriminator, saltHex, verifierHex); + + PushCommand(commandBuilder.c_str()); +} + +void DeviceManager::OpenRemoteDeviceCommissioningWindow(EndpointId remoteEndpointId) +{ + // Open the commissioning window of a device from another fabric via its fabric bridge. + // This method constructs and sends a command to open the commissioning window for a device + // that is part of a different fabric, accessed through a fabric bridge. + StringBuilder<512> commandBuilder; + + // Use random discriminator to have less chance of collission. + uint16_t discriminator = + Crypto::GetRandU16() % (kMaxDiscriminatorLength + 1); // Include the upper limit kMaxDiscriminatorLength + + commandBuilder.Add("pairing open-commissioning-window "); + commandBuilder.AddFormat("%lu %d %d %d %d %d", mRemoteBridgeNodeId, remoteEndpointId, kEnhancedCommissioningMethod, + kWindowTimeout, kIteration, discriminator); + commandBuilder.Add(" --setup-pin 20202021"); + + PushCommand(commandBuilder.c_str()); +} + +void DeviceManager::PairRemoteFabricBridge(NodeId nodeId, const char * deviceRemoteIp) { + StringBuilder commandBuilder; + + commandBuilder.Add("pairing already-discovered "); + commandBuilder.AddFormat("%lu %d %s %d", nodeId, kSetupPinCode, deviceRemoteIp, kRemoteBridgePort); + + PushCommand(commandBuilder.c_str()); +} + +void DeviceManager::PairRemoteDevice(chip::NodeId nodeId, const char * payload) +{ + StringBuilder commandBuilder; + + commandBuilder.Add("pairing code "); + commandBuilder.AddFormat("%lu %s", nodeId, payload); + + PushCommand(commandBuilder.c_str()); +} + +void DeviceManager::UnpairRemoteFabricBridge() +{ + StringBuilder commandBuilder; + + commandBuilder.Add("pairing unpair "); + commandBuilder.AddFormat("%lu", mRemoteBridgeNodeId); + + PushCommand(commandBuilder.c_str()); +} + +void DeviceManager::SubscribeRemoteFabricBridge() +{ + // Listen to the state changes of the remote fabric bridge. + StringBuilder commandBuilder; + + // Prepare and push the descriptor subscribe command + commandBuilder.Add("descriptor subscribe parts-list "); + commandBuilder.AddFormat("%d %d %lu %d", kSubscribeMinInterval, kSubscribeMaxInterval, mRemoteBridgeNodeId, + kAggragatorEndpointId); + PushCommand(commandBuilder.c_str()); + + // Clear the builder for the next command + commandBuilder.Reset(); + + // Prepare and push the commissioner control subscribe command + commandBuilder.Add("commissionercontrol subscribe-event commissioning-request-result "); + commandBuilder.AddFormat("%d %d %lu %d --is-urgent true", kSubscribeMinInterval, kSubscribeMaxInterval, mRemoteBridgeNodeId, + kRootEndpointId); + PushCommand(commandBuilder.c_str()); +} + +void DeviceManager::ReadSupportedDeviceCategories() +{ + if (!IsFabricSyncReady()) + { + // print to console + fprintf(stderr, "Remote Fabric Bridge is not configured yet.\n"); + return; + } + + StringBuilder commandBuilder; + + commandBuilder.Add("commissionercontrol read supported-device-categories "); + commandBuilder.AddFormat("%ld ", mRemoteBridgeNodeId); + commandBuilder.AddFormat("%d", kRootEndpointId); + + PushCommand(commandBuilder.c_str()); +} + +void DeviceManager::StartReverseCommissioning() +{ + ChipLogProgress(NotSpecified, "Starting reverse commissioning for bridge device: NodeId: " ChipLogFormatX64, + ChipLogValueX64(mRemoteBridgeNodeId)); + + uint64_t requestId = Crypto::GetRandU64(); + uint16_t vendorId = static_cast(CHIP_DEVICE_CONFIG_DEVICE_VENDOR_ID); + uint16_t productId = static_cast(CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_ID); + + StringBuilder commandBuilder; + commandBuilder.Add("commissionercontrol request-commissioning-approval "); + commandBuilder.AddFormat("%lu %u %u %lu %d", requestId, vendorId, productId, mRemoteBridgeNodeId, kRootEndpointId); + + mRequestId = requestId; + PushCommand(commandBuilder.c_str()); +} + +void DeviceManager::CommissionApprovedRequest(uint64_t requestId, uint16_t responseTimeoutSeconds) +{ + ChipLogProgress(NotSpecified, "Request the Commissioner Control Server to begin commissioning a previously approved request."); + + StringBuilder commandBuilder; + commandBuilder.Add("commissionercontrol commission-node "); + commandBuilder.AddFormat("%lu %u %lu %d", requestId, responseTimeoutSeconds, mRemoteBridgeNodeId, kRootEndpointId); + + PushCommand(commandBuilder.c_str()); +} + +void DeviceManager::HandleAttributeData(const app::ConcreteDataAttributePath & path, TLV::TLVReader * data) +{ + if (path.mClusterId == CommissionerControl::Id && + path.mAttributeId == CommissionerControl::Attributes::SupportedDeviceCategories::Id) + { + ChipLogProgress(NotSpecified, "Attribute SupportedDeviceCategories detected."); + + BitMask value; + CHIP_ERROR error = app::DataModel::Decode(*data, value); + if (error != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "Failed to decode attribute value. Error: %" CHIP_ERROR_FORMAT, error.Format()); + return; + } + + if (value.Has(CommissionerControl::SupportedDeviceCategoryBitmap::kFabricSynchronization)) + { + ChipLogProgress(NotSpecified, "Remote Fabric-Bridge supports Fabric Synchronization, start reverse commissioning."); + StartReverseCommissioning(); + } + + return; + } + if (path.mClusterId != Descriptor::Id || path.mAttributeId != Descriptor::Attributes::PartsList::Id) { return; @@ -167,9 +335,10 @@ void DeviceManager::HandleAttributeChange(const app::ConcreteDataAttributePath & if (mAutoSyncEnabled) { - char command[64]; - snprintf(command, sizeof(command), "fabricsync sync-device %d", endpoint); - PushCommand(command); + StringBuilder commandBuilder; + commandBuilder.Add("fabricsync sync-device "); + commandBuilder.AddFormat("%d", endpoint); + PushCommand(commandBuilder.c_str()); } } @@ -188,8 +357,9 @@ void DeviceManager::HandleAttributeChange(const app::ConcreteDataAttributePath & if (mAutoSyncEnabled) { - char command[64]; - snprintf(command, sizeof(command), "pairing unpair %ld", device->GetNodeId()); + StringBuilder commandBuilder; + commandBuilder.Add("pairing unpair "); + commandBuilder.AddFormat("%lu", device->GetNodeId()); PairingCommand * pairingCommand = static_cast(CommandMgr().GetCommandByName("pairing", "unpair")); @@ -200,11 +370,46 @@ void DeviceManager::HandleAttributeChange(const app::ConcreteDataAttributePath & } pairingCommand->RegisterPairingDelegate(this); - PushCommand(command); + PushCommand(commandBuilder.c_str()); } } } +void DeviceManager::HandleEventData(const chip::app::EventHeader & header, chip::TLV::TLVReader * data) +{ + if (header.mPath.mClusterId != CommissionerControl::Id || + header.mPath.mEventId != CommissionerControl::Events::CommissioningRequestResult::Id) + { + return; + } + + ChipLogProgress(NotSpecified, "CommissioningRequestResult event received."); + + CommissionerControl::Events::CommissioningRequestResult::DecodableType value; + CHIP_ERROR error = app::DataModel::Decode(*data, value); + if (error != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "Failed to decode event value. Error: %" CHIP_ERROR_FORMAT, error.Format()); + return; + } + + if (value.requestId != mRequestId) + { + ChipLogError(NotSpecified, "The RequestId does not match the RequestId provided to RequestCommissioningApproval"); + return; + } + + if (value.statusCode != static_cast(Protocols::InteractionModel::Status::Success)) + { + ChipLogError(NotSpecified, "The server is not ready to begin commissioning the requested device"); + return; + } + + // The server is ready to begin commissioning the requested device, request the Commissioner Control Server to begin + // commissioning a previously approved request. + CommissionApprovedRequest(value.requestId, kResponseTimeoutSeconds); +} + void DeviceManager::OnDeviceRemoved(NodeId deviceId, CHIP_ERROR err) { if (err != CHIP_NO_ERROR) diff --git a/examples/fabric-admin/device_manager/DeviceManager.h b/examples/fabric-admin/device_manager/DeviceManager.h index 6270cfd40e9df0..cad573a9311421 100644 --- a/examples/fabric-admin/device_manager/DeviceManager.h +++ b/examples/fabric-admin/device_manager/DeviceManager.h @@ -24,6 +24,8 @@ #include +constexpr uint16_t kResponseTimeoutSeconds = 30; + class Device { public: @@ -67,7 +69,76 @@ class DeviceManager : public PairingDelegate void RemoveSyncedDevice(chip::NodeId nodeId); - void HandleAttributeChange(const chip::app::ConcreteDataAttributePath & path, chip::TLV::TLVReader * data); + /** + * @brief Open the commissioning window for a specific device within its own fabric. + * + * This function initiates the process to open the commissioning window for a device identified by the given node ID. + * + * @param nodeId The ID of the node that should open the commissioning window. + * @param commissioningTimeout The time in seconds before the commissioning window closes. This value determines + * how long the commissioning window remains open for incoming connections. + * @param iterations The number of PBKDF (Password-Based Key Derivation Function) iterations to use + * for deriving the PAKE (Password Authenticated Key Exchange) verifier. + * @param discriminator The device-specific discriminator, determined during commissioning, which helps + * to uniquely identify the device among others. + * @param saltHex The hexadecimal-encoded salt used in the cryptographic operations for commissioning. + * @param verifierHex The hexadecimal-encoded PAKE verifier used to authenticate the commissioning process. + * + */ + void OpenDeviceCommissioningWindow(chip::NodeId nodeId, uint32_t commissioningTimeout, uint32_t iterations, + uint32_t discriminator, const char * saltHex, const char * verifierHex); + + /** + * @brief Open the commissioning window of a device from another fabric via its fabric bridge. + * + * This function initiates the process to open the commissioning window for a device that belongs to another + * fabric, accessed through a fabric bridge. + * + * @param remoteEndpointId The endpoint ID of the remote device that should open the commissioning window. + * This endpoint is associated with the device in the other fabric, accessed via the + * fabric bridge. + * + * @note This function is used when the device to be commissioned is part of a different fabric and must be + * accessed through an intermediary fabric bridge. + */ + void OpenRemoteDeviceCommissioningWindow(chip::EndpointId remoteEndpointId); + + /** + * @brief Pair a remote fabric bridge with a given node ID. + * + * This function initiates the pairing process for a remote fabric bridge using the specified parameters. + + * @param nodeId The user-defined ID for the node being commissioned. It doesn’t need to be the same ID, + * as for the first fabric. + * @param deviceRemoteIp The IP address of the remote device that is being paired as part of the fabric bridge. + */ + void PairRemoteFabricBridge(chip::NodeId nodeId, const char * deviceRemoteIp); + + /** + * @brief Pair a remote Matter device to the current fabric. + * + * This function initiates the pairing process for a remote device using the specified parameters. + + * @param nodeId The user-defined ID for the node being commissioned. It doesn’t need to be the same ID, + * as for the first fabric. + * @param payload The the QR code payload or a manual pairing code generated by the first commissioner + * instance when opened commissioning window. + */ + void PairRemoteDevice(chip::NodeId nodeId, const char * payload); + + void UnpairRemoteFabricBridge(); + + void SubscribeRemoteFabricBridge(); + + void StartReverseCommissioning(); + + void ReadSupportedDeviceCategories(); + + void CommissionApprovedRequest(uint64_t requestId, uint16_t responseTimeoutSeconds); + + void HandleAttributeData(const chip::app::ConcreteDataAttributePath & path, chip::TLV::TLVReader * data); + + void HandleEventData(const chip::app::EventHeader & header, chip::TLV::TLVReader * data); void OnDeviceRemoved(chip::NodeId deviceId, CHIP_ERROR err) override; @@ -81,6 +152,7 @@ class DeviceManager : public PairingDelegate std::set mSyncedDevices; bool mAutoSyncEnabled = false; bool mInitialized = false; + uint64_t mRequestId = 0; Device * FindDeviceByEndpoint(chip::EndpointId endpointId); Device * FindDeviceByNode(chip::NodeId nodeId); diff --git a/examples/fabric-admin/rpc/RpcServer.cpp b/examples/fabric-admin/rpc/RpcServer.cpp index cb06b359d68a71..16ff695e159712 100644 --- a/examples/fabric-admin/rpc/RpcServer.cpp +++ b/examples/fabric-admin/rpc/RpcServer.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -54,12 +55,7 @@ class FabricAdmin final : public rpc::FabricAdmin ChipLogProgress(NotSpecified, "Received OpenCommissioningWindow request: 0x%lx", nodeId); - char command[512]; - snprintf(command, sizeof(command), "pairing open-commissioning-window %ld %d %d %d %d %d --salt hex:%s --verifier hex:%s", - nodeId, kRootEndpointId, kEnhancedCommissioningMethod, commissioningTimeout, iterations, discriminator, saltHex, - verifierHex); - - PushCommand(command); + DeviceMgr().OpenDeviceCommissioningWindow(nodeId, commissioningTimeout, iterations, discriminator, saltHex, verifierHex); response.success = true; diff --git a/examples/fabric-bridge-app/fabric-bridge-common/BUILD.gn b/examples/fabric-bridge-app/fabric-bridge-common/BUILD.gn index 7f2fbcbbfe0556..b7b07d3be37bc3 100644 --- a/examples/fabric-bridge-app/fabric-bridge-common/BUILD.gn +++ b/examples/fabric-bridge-app/fabric-bridge-common/BUILD.gn @@ -45,9 +45,11 @@ source_set("fabric-bridge-lib") { "include/BridgedDeviceBasicInformationImpl.h", "include/BridgedDeviceManager.h", "include/CHIPProjectAppConfig.h", + "include/CommissionerControl.h", "src/BridgedDevice.cpp", "src/BridgedDeviceBasicInformationImpl.cpp", "src/BridgedDeviceManager.cpp", + "src/CommissionerControl.cpp", "src/ZCLCallbacks.cpp", ] diff --git a/examples/fabric-bridge-app/fabric-bridge-common/include/CommissionerControl.h b/examples/fabric-bridge-app/fabric-bridge-common/include/CommissionerControl.h new file mode 100644 index 00000000000000..b5caa6a2ce1f3d --- /dev/null +++ b/examples/fabric-bridge-app/fabric-bridge-common/include/CommissionerControl.h @@ -0,0 +1,57 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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. + */ +#pragma once + +#include +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace CommissionerControl { + +class CommissionerControlDelegate : public Delegate +{ +public: + CHIP_ERROR HandleCommissioningApprovalRequest(const CommissioningApprovalRequest & request) override; + CHIP_ERROR ValidateCommissionNodeCommand(NodeId clientNodeId, uint64_t requestId) override; + CHIP_ERROR GetCommissioningWindowParams(CommissioningWindowParams & outParams) override; + CHIP_ERROR ReverseCommissionNode(const CommissioningWindowParams & params, const Optional & ipAddress, + const Optional & port) override; + + ~CommissionerControlDelegate() = default; + +private: + static constexpr size_t kLabelBufferSize = 64; + + uint64_t mRequestId = 0; + NodeId mClientNodeId = kUndefinedNodeId; + VendorId mVendorId = VendorId::Unspecified; + uint16_t mProductId = 0; + char mLabelBuffer[kLabelBufferSize + 1]; + Optional mLabel; +}; + +} // namespace CommissionerControl +} // namespace Clusters +} // namespace app +} // namespace chip + +CHIP_ERROR CommissionerControlInit(); +CHIP_ERROR CommissionerControlShutdown(); diff --git a/examples/fabric-bridge-app/fabric-bridge-common/src/CommissionerControl.cpp b/examples/fabric-bridge-app/fabric-bridge-common/src/CommissionerControl.cpp new file mode 100644 index 00000000000000..076ced816596d2 --- /dev/null +++ b/examples/fabric-bridge-app/fabric-bridge-common/src/CommissionerControl.cpp @@ -0,0 +1,175 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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 "CommissionerControl.h" + +#include +#include +#include +#include + +using namespace chip; +using namespace chip::app; + +namespace { + +std::unique_ptr sCommissionerControlDelegate; + +} // namespace + +namespace chip { +namespace app { +namespace Clusters { +namespace CommissionerControl { + +CHIP_ERROR CommissionerControlDelegate::HandleCommissioningApprovalRequest(const CommissioningApprovalRequest & request) +{ + CommissionerControl::Events::CommissioningRequestResult::Type result; + result.requestId = request.requestId; + result.clientNodeId = request.clientNodeId; + result.fabricIndex = request.fabricIndex; + result.statusCode = static_cast(Protocols::InteractionModel::Status::Success); + + mRequestId = request.requestId; + mClientNodeId = request.clientNodeId; + mVendorId = request.vendorId; + mProductId = request.productId; + + if (request.label.HasValue()) + { + const CharSpan & labelSpan = request.label.Value(); + size_t labelLength = labelSpan.size(); + + if (labelLength >= kLabelBufferSize) + { + ChipLogError(Zcl, "Label too long to fit in buffer"); + return CHIP_ERROR_BUFFER_TOO_SMALL; + } + + if (labelLength == 0) + { + mLabel.ClearValue(); + } + else + { + memcpy(mLabelBuffer, labelSpan.data(), labelLength); + mLabelBuffer[labelLength] = '\0'; // Null-terminate the copied string + mLabel.SetValue(CharSpan(mLabelBuffer, labelLength)); + } + } + else + { + mLabel.ClearValue(); + } + + return CommissionerControlServer::Instance().GenerateCommissioningRequestResultEvent(result); +} + +CHIP_ERROR CommissionerControlDelegate::ValidateCommissionNodeCommand(NodeId clientNodeId, uint64_t requestId) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + // Verify if the CommissionNode command is sent from the same NodeId as the RequestCommissioningApproval. + VerifyOrExit(mClientNodeId == clientNodeId, err = CHIP_ERROR_WRONG_NODE_ID); + + // Verify if the provided RequestId matches the value provided to the RequestCommissioningApproval. + VerifyOrExit(mRequestId == requestId, err = CHIP_ERROR_INCORRECT_STATE); + +exit: + return err; +} + +CHIP_ERROR CommissionerControlDelegate::GetCommissioningWindowParams(CommissioningWindowParams & outParams) +{ + // TODO: Populate outParams with the required details. + // outParams.commissioningWindowParams.iterations = mIterations; + // outParams.commissioningWindowParams.commissioningTimeout = mCommissioningTimeout; + // outParams.commissioningWindowParams.discriminator = mDiscriminator; + // outParams.commissioningWindowParams.PAKEPasscodeVerifier = mPAKEPasscodeVerifier; + // outParams.commissioningWindowParams.salt = mSalt; + + // outParams.ipAddress = mIpAddress; + // outParams.port = mPort; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR CommissionerControlDelegate::ReverseCommissionNode(const CommissioningWindowParams & params, + const Optional & ipAddress, const Optional & port) +{ + return CHIP_NO_ERROR; +} + +} // namespace CommissionerControl +} // namespace Clusters +} // namespace app +} // namespace chip + +CHIP_ERROR CommissionerControlInit() +{ + CHIP_ERROR err; + + if (sCommissionerControlDelegate) + { + ChipLogError(NotSpecified, "Commissioner Control Delegate already exists."); + return CHIP_ERROR_INCORRECT_STATE; + } + + sCommissionerControlDelegate = std::make_unique(); + if (!sCommissionerControlDelegate) + { + ChipLogError(NotSpecified, "Failed to allocate memory for Commissioner Control Delegate."); + return CHIP_ERROR_NO_MEMORY; + } + + err = Clusters::CommissionerControl::CommissionerControlServer::Instance().Init(*sCommissionerControlDelegate); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "Initialization failed on Commissioner Control Delegate."); + sCommissionerControlDelegate.reset(); + return err; + } + + ChipLogProgress(Zcl, "Initializing SupportedDeviceCategories of Commissioner Control Cluster for this device."); + + BitMask supportedDeviceCategories; + supportedDeviceCategories.SetField(Clusters::CommissionerControl::SupportedDeviceCategoryBitmap::kFabricSynchronization, 1); + + Protocols::InteractionModel::Status status = + Clusters::CommissionerControl::CommissionerControlServer::Instance().SetSupportedDeviceCategoriesValue( + kRootEndpointId, supportedDeviceCategories); + + if (status != Protocols::InteractionModel::Status::Success) + { + ChipLogError(NotSpecified, "Failed to set SupportedDeviceCategories: %d", static_cast(status)); + sCommissionerControlDelegate.reset(); + return CHIP_ERROR_INTERNAL; + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR CommissionerControlShutdown() +{ + if (sCommissionerControlDelegate) + { + sCommissionerControlDelegate.reset(); + } + + return CHIP_NO_ERROR; +} diff --git a/examples/fabric-bridge-app/linux/RpcServer.cpp b/examples/fabric-bridge-app/linux/RpcServer.cpp index 932d3288b9cb27..bae007ed484935 100644 --- a/examples/fabric-bridge-app/linux/RpcServer.cpp +++ b/examples/fabric-bridge-app/linux/RpcServer.cpp @@ -118,8 +118,11 @@ pw::Status FabricBridge::AddSynchronizedDevice(const chip_rpc_SynchronizedDevice return pw::Status::Unknown(); } + BridgedDevice * addedDevice = BridgeDeviceMgr().GetDeviceByNodeId(nodeId); + VerifyOrDie(addedDevice); + CHIP_ERROR err = EcosystemInformation::EcosystemInformationServer::Instance().AddEcosystemInformationClusterToEndpoint( - device->GetEndpointId()); + addedDevice->GetEndpointId()); VerifyOrDie(err == CHIP_NO_ERROR); return pw::OkStatus(); diff --git a/examples/fabric-bridge-app/linux/main.cpp b/examples/fabric-bridge-app/linux/main.cpp index 1678ec59a81a1d..57907db8872b0a 100644 --- a/examples/fabric-bridge-app/linux/main.cpp +++ b/examples/fabric-bridge-app/linux/main.cpp @@ -22,6 +22,7 @@ #include "BridgedDeviceBasicInformationImpl.h" #include "BridgedDeviceManager.h" #include "CommissionableInit.h" +#include "CommissionerControl.h" #include #include @@ -257,11 +258,19 @@ void ApplicationInit() pollingThread.detach(); BridgeDeviceMgr().Init(); + + VerifyOrDieWithMsg(CommissionerControlInit() == CHIP_NO_ERROR, NotSpecified, + "Failed to initialize Commissioner Control Server"); } void ApplicationShutdown() { ChipLogDetail(NotSpecified, "Fabric-Bridge: ApplicationShutdown()"); + + if (CommissionerControlShutdown() != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "Failed to shutdown Commissioner Control Server"); + } } int main(int argc, char * argv[]) diff --git a/examples/lock-app/lock-common/src/LockManager.cpp b/examples/lock-app/lock-common/src/LockManager.cpp index 8af66051883aa4..cb7011899c9487 100644 --- a/examples/lock-app/lock-common/src/LockManager.cpp +++ b/examples/lock-app/lock-common/src/LockManager.cpp @@ -18,7 +18,6 @@ #include "LockManager.h" -#include #include #include diff --git a/examples/network-manager-app/linux/tbrm.cpp b/examples/network-manager-app/linux/tbrm.cpp index 908666ee61d8a0..eada060526add7 100644 --- a/examples/network-manager-app/linux/tbrm.cpp +++ b/examples/network-manager-app/linux/tbrm.cpp @@ -94,15 +94,23 @@ class FakeBorderRouterDelegate final : public ThreadBorderRouterManagement::Dele mActivateDatasetCallback = callback; mActivateDatasetSequence = sequenceNum; - DeviceLayer::SystemLayer().StartTimer(System::Clock::Milliseconds32(3000), CompleteDatasetActivation, this); + DeviceLayer::SystemLayer().StartTimer(System::Clock::Milliseconds32(1000), ActivateActiveDataset, this); } CHIP_ERROR CommitActiveDataset() override { return CHIP_NO_ERROR; } CHIP_ERROR RevertActiveDataset() override { return CHIP_ERROR_NOT_IMPLEMENTED; } - CHIP_ERROR SetPendingDataset(const Thread::OperationalDataset & pendingDataset) override { return CHIP_ERROR_NOT_IMPLEMENTED; } + + CHIP_ERROR SetPendingDataset(const Thread::OperationalDataset & pendingDataset) override + { + ReturnErrorOnFailure(mPendingDataset.Init(pendingDataset.AsByteSpan())); + uint32_t delayTimerMillis; + ReturnErrorOnFailure(mPendingDataset.GetDelayTimer(delayTimerMillis)); + DeviceLayer::SystemLayer().StartTimer(System::Clock::Milliseconds32(delayTimerMillis), ActivatePendingDataset, this); + return CHIP_NO_ERROR; + } private: - static void CompleteDatasetActivation(System::Layer *, void * context) + static void ActivateActiveDataset(System::Layer *, void * context) { auto * self = static_cast(context); auto * callback = self->mActivateDatasetCallback; @@ -111,6 +119,18 @@ class FakeBorderRouterDelegate final : public ThreadBorderRouterManagement::Dele callback->OnActivateDatasetComplete(sequenceNum, CHIP_NO_ERROR); } + static void ActivatePendingDataset(System::Layer *, void * context) + { + auto * self = static_cast(context); + self->mActiveDataset.Init(self->mPendingDataset.AsByteSpan()); + self->mPendingDataset.Clear(); + // This could just call MatterReportingAttributeChangeCallback directly + self->mAttributeChangeCallback->ReportAttributeChanged( + ThreadBorderRouterManagement::Attributes::ActiveDatasetTimestamp::Id); + self->mAttributeChangeCallback->ReportAttributeChanged( + ThreadBorderRouterManagement::Attributes::PendingDatasetTimestamp::Id); + } + AttributeChangeCallback * mAttributeChangeCallback; Thread::OperationalDataset mActiveDataset; Thread::OperationalDataset mPendingDataset; diff --git a/examples/network-manager-app/network-manager-common/network-manager-app.matter b/examples/network-manager-app/network-manager-common/network-manager-app.matter index 3f24e4be5744c6..627838288db0d1 100644 --- a/examples/network-manager-app/network-manager-common/network-manager-app.matter +++ b/examples/network-manager-app/network-manager-common/network-manager-app.matter @@ -1892,6 +1892,7 @@ endpoint 1 { handle command GetPendingDatasetRequest; handle command DatasetResponse; handle command SetActiveDatasetRequest; + handle command SetPendingDatasetRequest; } server cluster ThreadNetworkDirectory { diff --git a/examples/network-manager-app/network-manager-common/network-manager-app.zap b/examples/network-manager-app/network-manager-common/network-manager-app.zap index 7c1445ac46c190..1d27e3c346f320 100644 --- a/examples/network-manager-app/network-manager-common/network-manager-app.zap +++ b/examples/network-manager-app/network-manager-common/network-manager-app.zap @@ -3407,6 +3407,14 @@ "source": "client", "isIncoming": 1, "isEnabled": 1 + }, + { + "name": "SetPendingDatasetRequest", + "code": 4, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 } ], "attributes": [ @@ -3500,7 +3508,7 @@ "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, diff --git a/examples/platform/silabs/BaseApplication.cpp b/examples/platform/silabs/BaseApplication.cpp index beda9d3a6981f1..99123c5bf764d1 100644 --- a/examples/platform/silabs/BaseApplication.cpp +++ b/examples/platform/silabs/BaseApplication.cpp @@ -188,6 +188,17 @@ void BaseApplicationDelegate::OnCommissioningWindowClosed() } } #endif // CHIP_CONFIG_ENABLE_ICD_SERVER && SLI_SI917 + if (BaseApplication::GetProvisionStatus()) + { +#ifdef DISPLAY_ENABLED +#ifdef QR_CODE_ENABLED + SilabsLCD::Screen_e screen; + slLCD.GetScreen(screen); + VerifyOrReturn(screen == SilabsLCD::Screen_e::QRCodeScreen); + slLCD.SetScreen(SilabsLCD::Screen_e::DemoScreen); +#endif // QR_CODE_ENABLED +#endif // DISPLAY_ENABLED + } } void BaseApplicationDelegate::OnFabricCommitted(const FabricTable & fabricTable, FabricIndex fabricIndex) diff --git a/examples/platform/silabs/OTAConfig.h b/examples/platform/silabs/OTAConfig.h index 2b7ed9a45fa257..c97202e401f266 100644 --- a/examples/platform/silabs/OTAConfig.h +++ b/examples/platform/silabs/OTAConfig.h @@ -29,6 +29,10 @@ #include #endif +#if (SL_MATTER_GN_BUILD == 0) +#include "sl_matter_ota_config.h" +#endif + class OTAConfig { public: diff --git a/examples/platform/silabs/SiWx917/BUILD.gn b/examples/platform/silabs/SiWx917/BUILD.gn index 8acee7c3b9cfcd..1a4bc788637603 100644 --- a/examples/platform/silabs/SiWx917/BUILD.gn +++ b/examples/platform/silabs/SiWx917/BUILD.gn @@ -210,7 +210,7 @@ source_set("siwx917-common") { ] if (chip_enable_pw_rpc || chip_build_libshell || sl_uart_log_output) { - sources += [ "uart.cpp" ] + sources += [ "${silabs_common_plat_dir}/uart.cpp" ] } if (chip_enable_ota_requestor) { diff --git a/examples/platform/silabs/SiWx917/SiWx917/sl_wifi_if.cpp b/examples/platform/silabs/SiWx917/SiWx917/sl_wifi_if.cpp index 59bf52bbc0f30f..bfdedbfe82bb13 100644 --- a/examples/platform/silabs/SiWx917/SiWx917/sl_wifi_if.cpp +++ b/examples/platform/silabs/SiWx917/SiWx917/sl_wifi_if.cpp @@ -19,6 +19,10 @@ #include #include +#if (SL_MATTER_GN_BUILD == 0) +#include "sl_matter_wifi_config.h" +#endif // SL_MATTER_GN_BUILD + #include "sl_status.h" #include #include @@ -52,9 +56,15 @@ extern "C" { #include "sl_si91x_m4_ps.h" } +namespace { // TODO: should be removed once we are getting the press interrupt for button 0 with sleep #define BUTTON_PRESSED 1 bool btn0_pressed = false; + +#ifdef ENABLE_CHIP_SHELL +bool ps_requirement_added = false; +#endif // ENABLE_CHIP_SHELL +} // namespace #endif // CHIP_CONFIG_ENABLE_ICD_SERVER && SLI_SI91X_MCU_INTERFACE #include "dhcp_client.h" @@ -288,6 +298,26 @@ void sl_si91x_invoke_btn_press_event() { btn0_pressed = false; } + +#ifdef ENABLE_CHIP_SHELL + // Checking the UULP PIN 1 status to reinit the UART and not allow the device to go to sleep + if (RSI_NPSSGPIO_GetPin(RTE_UULP_GPIO_1_PIN)) + { + if (!ps_requirement_added) + { + sl_si91x_power_manager_add_ps_requirement(SL_SI91X_POWER_MANAGER_PS4); + ps_requirement_added = true; + } + } + else + { + if (ps_requirement_added) + { + sl_si91x_power_manager_remove_ps_requirement(SL_SI91X_POWER_MANAGER_PS4); + ps_requirement_added = false; + } + } +#endif // ENABLE_CHIP_SHELL } /****************************************************************** @@ -426,6 +456,16 @@ static sl_status_t wfx_rsi_init(void) ChipLogError(DeviceLayer, "sl_si91x_m4_ta_secure_handshake failed: 0x%lx", static_cast(status)); return status; } +#ifdef ENABLE_CHIP_SHELL + // While using the matter shell with the ICD server, the GPIO 1 is used to check the UULP PIN 1 status + // since UART doesn't act as a wakeup source in the UULP mode + /*Configuring the NPS GPIO 1*/ + RSI_NPSSGPIO_SetPinMux(RTE_UULP_GPIO_1_PIN, 0); + /*Configure the NPSS GPIO direction to input */ + RSI_NPSSGPIO_SetDir(RTE_UULP_GPIO_1_PIN, 1); + /*Enable the REN*/ + RSI_NPSSGPIO_InputBufferEn(RTE_UULP_GPIO_1_PIN, 1); +#endif // ENABLE_CHIP_SHELL #endif /* CHIP_CONFIG_ENABLE_ICD_SERVER */ #endif /* SLI_SI91X_MCU_INTERFACE */ diff --git a/examples/platform/silabs/SiWx917/uart.cpp b/examples/platform/silabs/SiWx917/uart.cpp deleted file mode 100644 index 863beef67f7f04..00000000000000 --- a/examples/platform/silabs/SiWx917/uart.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* - * - * Copyright (c) 2021 Project CHIP Authors - * All rights reserved. - * - * 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 "AppConfig.h" -#include "USART.h" -#include "matter_shell.h" -#include "rsi_rom_egpio.h" -#include "silabs_utils.h" -#include "sl_si91x_usart.h" -#ifdef __cplusplus -extern "C" { -#endif -#include "assert.h" -#include "rsi_board.h" -#include "rsi_debug.h" -#include "uart.h" -#include -#include - -#define USART_BAUDRATE 115200 // Baud rate <9600-7372800> -#define UART_CONSOLE_ERR -1 // Negative value in case of UART Console action failed. Triggers a failure for PW_RPC - -sl_usart_handle_t usart_handle; - -void callback_event(uint32_t event); - -/******************************************************************************* - * Callback function triggered on data Transfer and reception - ******************************************************************************/ -void callback_event(uint32_t event) -{ - switch (event) - { - case SL_USART_EVENT_SEND_COMPLETE: - break; - case SL_USART_EVENT_RECEIVE_COMPLETE: -#ifdef ENABLE_CHIP_SHELL - chip::NotifyShellProcess(); -#endif - case SL_USART_EVENT_TRANSFER_COMPLETE: - break; - } -} - -void uartConsoleInit(void) -{ - int32_t status = 0; - - sl_si91x_usart_control_config_t usart_config; - usart_config.baudrate = USART_BAUDRATE; - usart_config.mode = SL_USART_MODE_ASYNCHRONOUS; - usart_config.parity = SL_USART_NO_PARITY; - usart_config.stopbits = SL_USART_STOP_BITS_1; - usart_config.hwflowcontrol = SL_USART_FLOW_CONTROL_NONE; - usart_config.databits = SL_USART_DATA_BITS_8; - usart_config.misc_control = SL_USART_MISC_CONTROL_NONE; - usart_config.usart_module = USART_0; - usart_config.config_enable = ENABLE; - usart_config.synch_mode = DISABLE; - sl_si91x_usart_control_config_t get_config; - - // Initialize the UART - status = sl_si91x_usart_init((usart_peripheral_t) usart_config.usart_module, &usart_handle); - if (status != SL_STATUS_OK) - { - SILABS_LOG("sl_si91x_usart_initialize: Error Code : %lu \n", status); - } - - // Configure the USART configurations - status = sl_si91x_usart_set_configuration(usart_handle, &usart_config); - if (status != SL_STATUS_OK) - { - SILABS_LOG("sl_si91x_usart_set_configuration: Error Code : %lu \n", status); - } - - // Register user callback function - status = sl_si91x_usart_register_event_callback(callback_event); - if (status != SL_STATUS_OK) - { - SILABS_LOG("sl_si91x_usart_register_event_callback: Error Code : %lu \n", status); - } - - NVIC_EnableIRQ(USART0_IRQn); - NVIC_SetPriority(USART0_IRQn, 7); -} - -/* - * @brief Read the data available from the console Uart - * @param Buffer that contains the data to write, number bytes to write. - * @return Amount of bytes written or ERROR (-1) - */ -int16_t uartConsoleWrite(const char * Buf, uint16_t BufLength) -{ - int32_t status = 0; - if (Buf == NULL || BufLength < 1) - { - return UART_CONSOLE_ERR; - } - - status = sl_si91x_usart_send_data(usart_handle, Buf, BufLength); - if (status != SL_STATUS_OK) - { - return status; - } - return BufLength; -} - -/** - * @brief Write Logs to the Uart. Appends a return character - * - * @param log pointer to the logs - * @param length number of bytes to write - * @return int16_t Amount of bytes written or ERROR (-1) - */ -int16_t uartLogWrite(const char * log, uint16_t length) -{ - if (log == NULL || length == 0) - { - return UART_CONSOLE_ERR; - } - for (uint16_t i = 0; i < length; i++) - { - Board_UARTPutChar(log[i]); - } - // To print next log in new line with proper formatting - Board_UARTPutChar('\r'); - Board_UARTPutChar('\n'); - - return length + 2; -} - -/* - * @brief Read the data available from the console Uart - * @param Buffer for the data to be read, number bytes to read. - * @return Amount of bytes that was read from the rx fifo or ERROR (-1) - */ -int16_t uartConsoleRead(char * Buf, uint16_t NbBytesToRead) -{ - int32_t status = 0; - if (Buf == NULL || NbBytesToRead < 1) - { - return UART_CONSOLE_ERR; - } - - status = sl_si91x_usart_receive_data(usart_handle, Buf, NbBytesToRead); - if (status != SL_STATUS_OK) - { - return status; - } - return NbBytesToRead; -} - -#ifdef __cplusplus -} -#endif diff --git a/examples/platform/silabs/SoftwareFaultReports.cpp b/examples/platform/silabs/SoftwareFaultReports.cpp index fc7b6a9c5e3d76..8245b1bf454307 100644 --- a/examples/platform/silabs/SoftwareFaultReports.cpp +++ b/examples/platform/silabs/SoftwareFaultReports.cpp @@ -26,17 +26,18 @@ #include #include -#ifndef BRD4325A +#if !defined(SLI_SI91X_MCU_INTERFACE) || !defined(SLI_SI91X_ENABLE_BLE) #include "rail_types.h" #ifdef RAIL_ASSERT_DEBUG_STRING #include "rail_assert_error_codes.h" #endif -#endif // BRD4325A +#endif // !defined(SLI_SI91X_MCU_INTERFACE) || !defined(SLI_SI91X_ENABLE_BLE) + +#if defined(SLI_SI91X_MCU_INTERFACE) && SLI_SI91X_MCU_INTERFACE -#ifdef BRD4325A // For SiWx917 Platform only #include "core_cm4.h" -#endif +#endif // defined(SLI_SI91X_MCU_INTERFACE) && SLI_SI91X_MCU_INTERFACE // Technically FaultRecording is an octstr up to 1024 bytes. // We currently only report short strings. 100 char will more than enough for now. @@ -227,7 +228,7 @@ extern "C" void vApplicationGetTimerTaskMemory(StaticTask_t ** ppxTimerTaskTCBBu *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH; } -#ifndef BRD4325A +#if !defined(SLI_SI91X_MCU_INTERFACE) || !defined(SLI_SI91X_ENABLE_BLE) extern "C" void RAILCb_AssertFailed(RAIL_Handle_t railHandle, uint32_t errorCode) { char faultMessage[kMaxFaultStringLen] = { 0 }; @@ -251,6 +252,5 @@ extern "C" void RAILCb_AssertFailed(RAIL_Handle_t railHandle, uint32_t errorCode chipAbort(); } -#endif // BRD4325A - +#endif // !defined(SLI_SI91X_MCU_INTERFACE) || !defined(SLI_SI91X_ENABLE_BLE) #endif // HARD_FAULT_LOG_ENABLE diff --git a/examples/platform/silabs/efr32/BUILD.gn b/examples/platform/silabs/efr32/BUILD.gn index 6cf43897f43b10..bff7d1e37be125 100644 --- a/examples/platform/silabs/efr32/BUILD.gn +++ b/examples/platform/silabs/efr32/BUILD.gn @@ -248,7 +248,7 @@ source_set("efr32-common") { ] if (chip_enable_pw_rpc || chip_build_libshell || enable_openthread_cli) { - sources += [ "uart.cpp" ] + sources += [ "${silabs_common_plat_dir}/uart.cpp" ] } if (chip_enable_ota_requestor) { diff --git a/examples/platform/silabs/efr32/uart.cpp b/examples/platform/silabs/uart.cpp similarity index 89% rename from examples/platform/silabs/efr32/uart.cpp rename to examples/platform/silabs/uart.cpp index 80d2dbba63b3a8..7d6dad912314db 100644 --- a/examples/platform/silabs/efr32/uart.cpp +++ b/examples/platform/silabs/uart.cpp @@ -18,14 +18,31 @@ #include "AppConfig.h" #include "matter_shell.h" #include +#include #include #ifdef __cplusplus extern "C" { #endif -#include "assert.h" + +#include "uart.h" +#include +#include + +#define UART_CONSOLE_ERR -1 // Negative value in case of UART Console action failed. Triggers a failure for PW_RPC +#define MAX_BUFFER_SIZE 256 +#define MAX_DMA_BUFFER_SIZE (MAX_BUFFER_SIZE / 2) + +#if SLI_SI91X_MCU_INTERFACE +#include "USART.h" +#include "rsi_board.h" +#include "rsi_debug.h" +#include "rsi_rom_egpio.h" +#include "sl_si91x_usart.h" +#else // For EFR32 #include "em_core.h" #include "em_usart.h" +#include "uartdrv.h" #ifdef SL_BOARD_NAME #include "sl_board_control.h" #endif @@ -38,11 +55,7 @@ extern "C" { #endif #ifdef SL_CATALOG_UARTDRV_USART_PRESENT #include "sl_uartdrv_usart_vcom_config.h" -#endif // EFR32MG24 -#include "uart.h" -#include "uartdrv.h" -#include -#include +#endif // SL_CATALOG_UARTDRV_USART_PRESENT #if defined(SL_CATALOG_POWER_MANAGER_PRESENT) #include "sl_power_manager.h" @@ -79,6 +92,16 @@ extern "C" { #define vcom_handle sl_uartdrv_usart_vcom_handle #endif // EFR32MG24 +namespace { +// In order to reduce the probability of data loss during the dmaFull callback handler we use +// two duplicate receive buffers so we can always have one "active" receive queue. +uint8_t sRxDmaBuffer[MAX_DMA_BUFFER_SIZE] = { 0 }; +uint8_t sRxDmaBuffer2[MAX_DMA_BUFFER_SIZE] = { 0 }; +uint16_t lastCount = 0; // Nb of bytes already processed from the active dmaBuffer +} // namespace + +#endif // SLI_SI91X_MCU_INTERFACE + typedef struct { // The data buffer @@ -91,15 +114,6 @@ typedef struct uint16_t MaxSize; } Fifo_t; -#define UART_CONSOLE_ERR -1 // Negative value in case of UART Console action failed. Triggers a failure for PW_RPC -#define MAX_BUFFER_SIZE 256 -#define MAX_DMA_BUFFER_SIZE (MAX_BUFFER_SIZE / 2) -// In order to reduce the probability of data loss during the dmaFull callback handler we use -// two duplicate receive buffers so we can always have one "active" receive queue. -static uint8_t sRxDmaBuffer[MAX_DMA_BUFFER_SIZE]; -static uint8_t sRxDmaBuffer2[MAX_DMA_BUFFER_SIZE]; -static uint16_t lastCount; // Nb of bytes already processed from the active dmaBuffer - // uart transmit #if SILABS_LOG_OUT_UART #define UART_MAX_QUEUE_SIZE 125 @@ -144,7 +158,9 @@ constexpr osMessageQueueAttr_t kUartTxQueueAttr = { .cb_mem = &sUartTxQueueStru static uint8_t sRxFifoBuffer[MAX_BUFFER_SIZE]; static Fifo_t sReceiveFifo; +#if SLI_SI91X_MCU_INTERFACE == 0 static void UART_rx_callback(UARTDRV_Handle_t handle, Ecode_t transferStatus, uint8_t * data, UARTDRV_Count_t transferCount); +#endif // SLI_SI91X_MCU_INTERFACE == 0 static void uartSendBytes(uint8_t * buffer, uint16_t nbOfBytes); static bool InitFifo(Fifo_t * fifo, uint8_t * pDataBuffer, uint16_t bufferSize) @@ -196,9 +212,9 @@ static uint16_t RemainingSpace(Fifo_t * fifo) */ static void WriteToFifo(Fifo_t * fifo, uint8_t * pDataToWrite, uint16_t SizeToWrite) { - assert(fifo); - assert(pDataToWrite); - assert(SizeToWrite <= fifo->MaxSize); + VerifyOrDie(fifo != nullptr); + VerifyOrDie(pDataToWrite != nullptr); + VerifyOrDie(SizeToWrite <= fifo->MaxSize); // Overwrite is not allowed if (RemainingSpace(fifo) >= SizeToWrite) @@ -227,9 +243,9 @@ static void WriteToFifo(Fifo_t * fifo, uint8_t * pDataToWrite, uint16_t SizeToWr */ static uint16_t RetrieveFromFifo(Fifo_t * fifo, uint8_t * pData, uint16_t SizeToRead) { - assert(fifo); - assert(pData); - assert(SizeToRead <= fifo->MaxSize); + VerifyOrDie(fifo != nullptr); + VerifyOrDie(pData != nullptr); + VerifyOrDie(SizeToRead <= fifo->MaxSize); uint16_t ReadSize = MIN(SizeToRead, AvailableDataCount(fifo)); uint16_t nBytesBeforWrap = (fifo->MaxSize - fifo->Head); @@ -263,22 +279,24 @@ void uartConsoleInit(void) return; } + sUartTxQueue = osMessageQueueNew(UART_MAX_QUEUE_SIZE, sizeof(UartTxStruct_t), &kUartTxQueueAttr); + sUartTaskHandle = osThreadNew(uartMainLoop, nullptr, &kUartTaskAttr); + + // Init a fifo for the data received on the uart + InitFifo(&sReceiveFifo, sRxFifoBuffer, MAX_BUFFER_SIZE); + + VerifyOrDie(sUartTaskHandle != nullptr); + VerifyOrDie(sUartTxQueue != nullptr); + +#if SLI_SI91X_MCU_INTERFACE == 0 #ifdef SL_BOARD_NAME sl_board_enable_vcom(); #endif - // Init a fifo for the data received on the uart - InitFifo(&sReceiveFifo, sRxFifoBuffer, MAX_BUFFER_SIZE); // Activate 2 dma queues to always have one active UARTDRV_Receive(vcom_handle, sRxDmaBuffer, MAX_DMA_BUFFER_SIZE, UART_rx_callback); UARTDRV_Receive(vcom_handle, sRxDmaBuffer2, MAX_DMA_BUFFER_SIZE, UART_rx_callback); - sUartTxQueue = osMessageQueueNew(UART_MAX_QUEUE_SIZE, sizeof(UartTxStruct_t), &kUartTxQueueAttr); - sUartTaskHandle = osThreadNew(uartMainLoop, nullptr, &kUartTaskAttr); - - assert(sUartTaskHandle); - assert(sUartTxQueue); - // Enable USART0/EUSART0 interrupt to wake OT task when data arrives NVIC_ClearPendingIRQ(USART_IRQ); NVIC_EnableIRQ(USART_IRQ); @@ -295,8 +313,24 @@ void uartConsoleInit(void) #else USART_IntEnable(SL_UARTDRV_USART_VCOM_PERIPHERAL, USART_IF_RXDATAV); #endif // EFR32MG24 +#endif // SLI_SI91X_MCU_INTERFACE == 0 +} + +#if SLI_SI91X_MCU_INTERFACE +void cache_uart_rx_data(char character) +{ + if (RemainingSpace(&sReceiveFifo) >= 1) + { + WriteToFifo(&sReceiveFifo, (uint8_t *) &character, 1); + } +#ifdef ENABLE_CHIP_SHELL + chip::NotifyShellProcess(); +#endif // ENABLE_CHIP_SHELL } +#endif // SLI_SI91X_MCU_INTERFACE +#if SLI_SI91X_MCU_INTERFACE == 0 +// For EFR32 void USART_IRQHandler(void) { #ifdef ENABLE_CHIP_SHELL @@ -346,6 +380,7 @@ static void UART_rx_callback(UARTDRV_Handle_t handle, Ecode_t transferStatus, ui otSysEventSignalPending(); #endif } +#endif // SLI_SI91X_MCU_INTERFACE == 0 /** * @brief Read the data available from the console Uart @@ -420,15 +455,15 @@ int16_t uartLogWrite(const char * log, uint16_t length) int16_t uartConsoleRead(char * Buf, uint16_t NbBytesToRead) { uint8_t * data; - UARTDRV_Count_t count, remaining; if (Buf == NULL || NbBytesToRead < 1) { return UART_CONSOLE_ERR; } - +#if SLI_SI91X_MCU_INTERFACE == 0 if (NbBytesToRead > AvailableDataCount(&sReceiveFifo)) { + UARTDRV_Count_t count, remaining; // Not enough data available in the fifo for the read size request // If there is data available in dma buffer, get it now. CORE_ATOMIC_SECTION(UARTDRV_GetReceiveStatus(vcom_handle, &data, &count, &remaining); if (count > lastCount) { @@ -436,6 +471,7 @@ int16_t uartConsoleRead(char * Buf, uint16_t NbBytesToRead) lastCount = count; }) } +#endif // SLI_SI91X_MCU_INTERFACE == 0 return (int16_t) RetrieveFromFifo(&sReceiveFifo, (uint8_t *) Buf, NbBytesToRead); } @@ -464,6 +500,14 @@ void uartMainLoop(void * args) */ void uartSendBytes(uint8_t * buffer, uint16_t nbOfBytes) { +#if SLI_SI91X_MCU_INTERFACE + // ensuring null termination of buffer + if (nbOfBytes != CHIP_SHELL_MAX_LINE_SIZE && buffer[nbOfBytes - 1] != '\0') + { + buffer[nbOfBytes] = '\0'; + } + Board_UARTPutSTR(reinterpret_cast(buffer)); +#else #if defined(SL_CATALOG_POWER_MANAGER_PRESENT) sl_power_manager_add_em_requirement(SL_POWER_MANAGER_EM1); #endif // SL_CATALOG_POWER_MANAGER_PRESENT @@ -489,6 +533,7 @@ void uartSendBytes(uint8_t * buffer, uint16_t nbOfBytes) #if defined(SL_CATALOG_POWER_MANAGER_PRESENT) sl_power_manager_remove_em_requirement(SL_POWER_MANAGER_EM1); #endif // SL_CATALOG_POWER_MANAGER_PRESENT +#endif // SLI_SI91X_MCU_INTERFACE } #ifdef __cplusplus diff --git a/examples/rvc-app/README.md b/examples/rvc-app/README.md index 2943f54103f828..da9317f508fe39 100644 --- a/examples/rvc-app/README.md +++ b/examples/rvc-app/README.md @@ -20,6 +20,46 @@ must have a `"Name"` key that contains the command name. This name is shown in the state machine diagram above. Example `echo '{"Name": "Charged"}' > /tmp/chip_rvc_fifo_42`. +### ServiceArea related messages + +#### `AddMap` message + +This message adds a map to the SupportedMaps attribute of the Service Area +cluster. This message requires the following extra keys. + +- `MapId` This is an `int` setting the ID of the new map. +- `MapName` This is a `string` setting the name of the new map. + +#### `AddArea` message + +This message adds a new area to the SupportedAreas attribute of the Service Area +cluster. This message requires the following extra keys, most of which are +optional. Consult the `SupportedAreas` attribute spec for more information on +what are valid areas. + +- `AreaId` This is an `int` setting the ID of the area. +- `MapId` This is an `int` sitting the map ID the area is associated with. +- `LocationName` This is a `string` setting the location's name. +- `FloorNumber` This is an `int` setting the floor number of the area. +- `AreaType` This is an `int` setting the area type tag. +- `LandmarkTag` This is an `int` setting the landmark tag. +- `PositianTag` This is an `int` setting the position tag. + +#### `RemoveMap` message + +This message removes a map with the given map ID. This message requires the +`int` key `MapId`. + +#### `RemoveArea` message + +This message removes an area with the given area ID. This message requires the +`int` key `AreaId`. + +#### `AreaComplete` message + +This indicates that the area currently being serviced as indicated by the +service area cluster is now complete. + ### `ErrorEvent` message The error event message requires the additional key `"Error"` which specifies @@ -37,10 +77,14 @@ and setting up the testing environment, python tests can be executed with `./scripts/tests/run_python_test.py --script src/python_testing/.py --script-args "--storage-path admin_storage.json --PICS examples/rvc-app/rvc-common/pics/RVC_App_Test_Plan.txt --int-arg "` **Note:** If the testing environment has not been commissioned with the RVC app, -use chip-tool to switch on the commissioning window -`chip-tool pairing open-commissioning-window`, and add the following flags to -the `--script-args` above. -`--commissioning-method on-network --discriminator XXXX --passcode XXXX`. + +1. use chip-tool to switch on the commissioning window + `out/debug/chip-tool pairing open-commissioning-window 0x1230 1 180 1000 42` +2. Get the manual pairing code. This will look something like + `Manual pairing code: [01073112097]`. +3. Run any one of the tests with the `--commission-only` and `--manual-code` + flags: + `./scripts/tests/run_python_test.py --script src/python_testing/TC_RVCCLEANM_1_2.py --script-args "--commissioning-method on-network --manual-code 01073112097 --commission-only"` Below are the PIXIT definitions required for the different python tests. @@ -127,3 +171,33 @@ to transition to the required states. After commissioning the device, all the yaml tests can be run by running the `run_all_yaml_tests.sh` script from the root dir with the node ID that the device was commissioned with. + +### Service Area Cluster + +### TC 1.2 + +Example command: +`./scripts/tests/run_python_test.py --script src/python_testing/TC_SEAR_1_2.py --script-args "--storage-path admin_storage.json --PICS examples/rvc-app/rvc-common/pics/rvc-app-pics-values --endpoint 1"` + +### TC 1.3 + +todo once the +[test plan issue](https://github.com/CHIP-Specifications/chip-test-plans/issues/4454) +is resolved, add the required PIXIT and set the +PICS`SEAR.S.M.VALID_STATE_FOR_SELECT_AREAS=1`. PIXIT: `` Example command: +`./scripts/tests/run_python_test.py --script src/python_testing/TC_SEAR_1_3.py --script-args "--storage-path admin_storage.json --PICS examples/rvc-app/rvc-common/pics/rvc-app-pics-values --endpoint 1` + +### TC 1.4 + +Example command: +`./scripts/tests/run_python_test.py --script src/python_testing/TC_SEAR_1_4.py --script-args "--storage-path admin_storage.json --PICS examples/rvc-app/rvc-common/pics/rvc-app-pics-values --endpoint 1` + +#### TC 1.5 + +Example command: +`./scripts/tests/run_python_test.py --script src/python_testing/TC_SEAR_1_5.py --script-args "--storage-path admin_storage.json --PICS examples/rvc-app/rvc-common/pics/rvc-app-pics-values --endpoint 1"` + +#### TC 1.6 + +Example command: +`./scripts/tests/run_python_test.py --script src/python_testing/TC_SEAR_1_6.py --script-args "--storage-path admin_storage.json --PICS examples/rvc-app/rvc-common/pics/rvc-app-pics-values --endpoint 1` diff --git a/examples/rvc-app/linux/RvcAppCommandDelegate.cpp b/examples/rvc-app/linux/RvcAppCommandDelegate.cpp index 58610e1b11d112..791df4d482cfe8 100644 --- a/examples/rvc-app/linux/RvcAppCommandDelegate.cpp +++ b/examples/rvc-app/linux/RvcAppCommandDelegate.cpp @@ -17,6 +17,7 @@ */ #include "RvcAppCommandDelegate.h" +#include #include #include "rvc-device.h" @@ -24,6 +25,7 @@ #include using namespace chip; +using namespace chip::app; using namespace chip::app::Clusters; RvcAppCommandHandler * RvcAppCommandHandler::FromJSON(const char * json) @@ -83,6 +85,29 @@ void RvcAppCommandHandler::HandleCommand(intptr_t context) { self->OnActivityCompleteHandler(); } + else if (name == "AreaComplete") + { + self->OnAreaCompleteHandler(); + } + else if (name == "AddMap") + { + self->OnAddServiceAreaMap(self->mJsonValue); + } + else if (name == "AddArea") + { + VerifyOrExit(self->mJsonValue.isMember("AreaId"), ChipLogError(NotSpecified, "RVC App: AreaId key is missing")); + self->OnAddServiceAreaArea(self->mJsonValue); + } + else if (name == "RemoveMap") + { + VerifyOrExit(self->mJsonValue.isMember("MapId"), ChipLogError(NotSpecified, "RVC App: MapId key is missing")); + self->OnRemoveServiceAreaMap(self->mJsonValue["MapId"].asUInt()); + } + else if (name == "RemoveArea") + { + VerifyOrExit(self->mJsonValue.isMember("AreaId"), ChipLogError(NotSpecified, "RVC App: AreaId key is missing")); + self->OnRemoveServiceAreaArea(self->mJsonValue["AreaId"].asUInt()); + } else if (name == "ErrorEvent") { std::string error = self->mJsonValue["Error"].asString(); @@ -140,6 +165,78 @@ void RvcAppCommandHandler::OnActivityCompleteHandler() mRvcDevice->HandleActivityCompleteEvent(); } +void RvcAppCommandHandler::OnAreaCompleteHandler() +{ + mRvcDevice->HandleAreaCompletedEvent(); +} + +void RvcAppCommandHandler::OnAddServiceAreaMap(Json::Value jsonValue) +{ + // Find if self->mJsonValue has the MapId and MapName Keys + if (jsonValue.isMember("MapId") && jsonValue.isMember("MapName")) + { + uint32_t mapId = jsonValue["MapId"].asUInt(); + std::string mapName = jsonValue["MapName"].asString(); + mRvcDevice->HandleAddServiceAreaMap(mapId, CharSpan(mapName.data(), mapName.size())); + } + else + { + ChipLogError(NotSpecified, "RVC App: MapId and MapName keys are missing"); + } +} + +void RvcAppCommandHandler::OnAddServiceAreaArea(Json::Value jsonValue) +{ + ServiceArea::AreaStructureWrapper area; + area.SetAreaId(jsonValue["AreaId"].asUInt()); + if (jsonValue.isMember("MapId")) + { + area.SetMapId(jsonValue["MapId"].asUInt()); + } + + // Set the location info + if (jsonValue.isMember("LocationName") || jsonValue.isMember("FloorNumber") || jsonValue.isMember("AreaType")) + { + DataModel::Nullable floorNumber = DataModel::NullNullable; + if (jsonValue.isMember("FloorNumber")) + { + floorNumber = jsonValue["FloorNumber"].asInt(); + } + DataModel::Nullable areaType = DataModel::NullNullable; + if (jsonValue.isMember("AreaType")) + { + areaType = Globals::AreaTypeTag(jsonValue["AreaType"].asUInt()); + } + auto locationName = jsonValue["LocationName"].asString(); + + area.SetLocationInfo(CharSpan(locationName.data(), locationName.size()), floorNumber, areaType); + } + + // Set landmark info + if (jsonValue.isMember("LandmarkTag")) + { + DataModel::Nullable relativePositionTag = DataModel::NullNullable; + if (jsonValue.isMember("PositionTag")) + { + relativePositionTag = Globals::RelativePositionTag(jsonValue["PositionTag"].asUInt()); + } + + area.SetLandmarkInfo(Globals::LandmarkTag(jsonValue["LandmarkTag"].asUInt()), relativePositionTag); + } + + mRvcDevice->HandleAddServiceAreaArea(area); +} + +void RvcAppCommandHandler::OnRemoveServiceAreaMap(uint32_t mapId) +{ + mRvcDevice->HandleRemoveServiceAreaMap(mapId); +} + +void RvcAppCommandHandler::OnRemoveServiceAreaArea(uint32_t areaId) +{ + mRvcDevice->HandleRemoveServiceAreaArea(areaId); +} + void RvcAppCommandHandler::OnErrorEventHandler(const std::string & error) { mRvcDevice->HandleErrorEvent(error); diff --git a/examples/rvc-app/linux/RvcAppCommandDelegate.h b/examples/rvc-app/linux/RvcAppCommandDelegate.h index 366772bf0842ab..6f896641dab1c7 100644 --- a/examples/rvc-app/linux/RvcAppCommandDelegate.h +++ b/examples/rvc-app/linux/RvcAppCommandDelegate.h @@ -55,6 +55,16 @@ class RvcAppCommandHandler void OnActivityCompleteHandler(); + void OnAreaCompleteHandler(); + + void OnAddServiceAreaMap(Json::Value jsonValue); + + void OnAddServiceAreaArea(Json::Value jsonValue); + + void OnRemoveServiceAreaMap(uint32_t mapId); + + void OnRemoveServiceAreaArea(uint32_t areaId); + void OnErrorEventHandler(const std::string & error); void OnClearErrorHandler(); diff --git a/examples/rvc-app/rvc-common/include/rvc-device.h b/examples/rvc-app/rvc-common/include/rvc-device.h index da03422ffbb34a..b66ce3251feba6 100644 --- a/examples/rvc-app/rvc-common/include/rvc-device.h +++ b/examples/rvc-app/rvc-common/include/rvc-device.h @@ -59,6 +59,11 @@ class RvcDevice mOperationalStateDelegate.SetPauseCallback(&RvcDevice::HandleOpStatePauseCallback, this); mOperationalStateDelegate.SetResumeCallback(&RvcDevice::HandleOpStateResumeCallback, this); mOperationalStateDelegate.SetGoHomeCallback(&RvcDevice::HandleOpStateGoHomeCallback, this); + + mServiceAreaDelegate.SetIsSetSelectedAreasAllowedCallback(&RvcDevice::SaIsSetSelectedAreasAllowed, this); + mServiceAreaDelegate.SetHandleSkipCurrentAreaCallback(&RvcDevice::SaHandleSkipCurrentArea, this); + mServiceAreaDelegate.SetIsSupportedAreasChangeAllowedCallback(&RvcDevice::SaIsSupportedAreasChangeAllowed, this); + mServiceAreaDelegate.SetIsSupportedMapChangeAllowedCallback(&RvcDevice::SaIsSupportedMapChangeAllowed, this); } /** @@ -97,6 +102,14 @@ class RvcDevice */ void HandleOpStateGoHomeCallback(Clusters::OperationalState::GenericOperationalError & err); + bool SaIsSetSelectedAreasAllowed(MutableCharSpan & statusText); + + bool SaHandleSkipCurrentArea(uint32_t skippedArea, MutableCharSpan & skipStatusText); + + bool SaIsSupportedAreasChangeAllowed(); + + bool SaIsSupportedMapChangeAllowed(); + /** * Updates the state machine when the device becomes fully-charged. */ @@ -112,6 +125,16 @@ class RvcDevice void HandleActivityCompleteEvent(); + void HandleAreaCompletedEvent(); + + void HandleAddServiceAreaMap(uint32_t mapId, const CharSpan & mapName); + + void HandleAddServiceAreaArea(ServiceArea::AreaStructureWrapper & area); + + void HandleRemoveServiceAreaMap(uint32_t mapId); + + void HandleRemoveServiceAreaArea(uint32_t areaId); + /** * Sets the device to an error state with the error state ID matching the error name given. * @param error The error name. Could be one of UnableToStartOrResume, UnableToCompleteOperation, CommandInvalidInState, @@ -123,6 +146,12 @@ class RvcDevice void HandleClearErrorMessage(); void HandleResetMessage(); + + /** + * Updates the Service area progress elements when an activity has ended. + * Sets any remaining Operating or Pending states to Skipped. + */ + void UpdateServiceAreaProgressOnExit(); }; } // namespace Clusters diff --git a/examples/rvc-app/rvc-common/include/rvc-service-area-delegate.h b/examples/rvc-app/rvc-common/include/rvc-service-area-delegate.h index 5397e3096c14ae..4c13fcd493d7eb 100644 --- a/examples/rvc-app/rvc-common/include/rvc-service-area-delegate.h +++ b/examples/rvc-app/rvc-common/include/rvc-service-area-delegate.h @@ -29,6 +29,10 @@ namespace Clusters { class RvcDevice; +typedef bool (RvcDevice::*IsSetSelectedAreasAllowedCallback)(MutableCharSpan & statusText); +typedef bool (RvcDevice::*HandleSkipCurrentAreaCallback)(uint32_t skippedArea, MutableCharSpan & skipStatusText); +typedef bool (RvcDevice::*IsChangeAllowedSimpleCallback)(); + namespace ServiceArea { class RvcServiceAreaDelegate : public Delegate @@ -40,16 +44,45 @@ class RvcServiceAreaDelegate : public Delegate std::vector mSelectedAreas; std::vector mProgressList; + RvcDevice * mIsSetSelectedAreasAllowedDeviceInstance; + IsSetSelectedAreasAllowedCallback mIsSetSelectedAreasAllowedCallback; + RvcDevice * mHandleSkipCurrentAreaDeviceInstance; + HandleSkipCurrentAreaCallback mHandleSkipCurrentAreaCallback; + RvcDevice * mIsSupportedAreasChangeAllowedDeviceInstance; + IsChangeAllowedSimpleCallback mIsSupportedAreasChangeAllowedCallback; + RvcDevice * mIsSupportedMapChangeAllowedDeviceInstance; + IsChangeAllowedSimpleCallback mIsSupportedMapChangeAllowedCallback; + + // hardcoded values for SUPPORTED MAPS. + const uint32_t supportedMapId_XX = 3; + const uint32_t supportedMapId_YY = 245; + + // hardcoded values for SUPPORTED AREAS. + const uint32_t supportedAreaID_A = 7; + const uint32_t supportedAreaID_B = 1234567; + const uint32_t supportedAreaID_C = 10050; + const uint32_t supportedAreaID_D = 0x88888888; + public: + /** + * Set the SupportedMaps and SupportedAreas where the SupportedMaps is not null. + */ + void SetMapTopology(); + + /** + * Set the SupportedMaps and SupportedAreas where the SupportedMaps is null. + */ + void SetNoMapTopology(); + CHIP_ERROR Init() override; // command support - bool IsSetSelectedAreasAllowed(MutableCharSpan statusText) override; + bool IsSetSelectedAreasAllowed(MutableCharSpan & statusText) override; bool IsValidSelectAreasSet(const ServiceArea::Commands::SelectAreas::DecodableType & req, - ServiceArea::SelectAreasStatus & areaStatus, MutableCharSpan statusText) override; + ServiceArea::SelectAreasStatus & areaStatus, MutableCharSpan & statusText) override; - bool HandleSkipCurrentArea(uint32_t skippedArea, MutableCharSpan skipStatusText) override; + bool HandleSkipCurrentArea(uint32_t skippedArea, MutableCharSpan & skipStatusText) override; //************************************************************************* // Supported Areas accessors @@ -68,6 +101,18 @@ class RvcServiceAreaDelegate : public Delegate bool ClearSupportedAreas() override; + /** + * This is a more sophisticated way of ensuring that we all attributes are still valid when a supported area is removed. + * Rather than clearing all the attributes that depend on the supported aeras, we only remove the elements that point to + * the removed supported areas. + */ + void HandleSupportedAreasUpdated() override; + + /** + * Note: Call the HandleSupportedAreasUpdated() method when finished removing supported areas. + */ + bool RemoveSupportedArea(uint32_t areaId); + //************************************************************************* // Supported Maps accessors @@ -85,6 +130,8 @@ class RvcServiceAreaDelegate : public Delegate bool ClearSupportedMaps() override; + bool RemoveSupportedMap(uint32_t mapId); + //************************************************************************* // Selected Areas accessors @@ -112,6 +159,50 @@ class RvcServiceAreaDelegate : public Delegate const ServiceArea::Structs::ProgressStruct::Type & modifiedProgressElement) override; bool ClearProgress() override; + + //************************************************************************* + // RVC device callback setters + + void SetIsSetSelectedAreasAllowedCallback(IsSetSelectedAreasAllowedCallback callback, RvcDevice * instance) + { + mIsSetSelectedAreasAllowedCallback = callback; + mIsSetSelectedAreasAllowedDeviceInstance = instance; + } + + void SetHandleSkipCurrentAreaCallback(HandleSkipCurrentAreaCallback callback, RvcDevice * instance) + { + mHandleSkipCurrentAreaCallback = callback; + mHandleSkipCurrentAreaDeviceInstance = instance; + } + + void SetIsSupportedAreasChangeAllowedCallback(IsChangeAllowedSimpleCallback callback, RvcDevice * instance) + { + mIsSupportedAreasChangeAllowedCallback = callback; + mIsSupportedAreasChangeAllowedDeviceInstance = instance; + } + + void SetIsSupportedMapChangeAllowedCallback(IsChangeAllowedSimpleCallback callback, RvcDevice * instance) + { + mIsSupportedMapChangeAllowedCallback = callback; + mIsSupportedMapChangeAllowedDeviceInstance = instance; + } + + //************************************************************************* + // Helper methods for setting service area attributes. + + /** + * Sets the service area attributes at the start of a clean. + * This includes the current area an progress attributes. + */ + void SetAttributesAtCleanStart(); + + /** + * Go to the next area in the list of selected areas. + * @param currentAreaOpState The operational state to be set in the Status field of the Progress attribute for the current area. + * This can only be Completed or Skipped. + * @param finished true if there are no more areas to clean an we should end the clean. + */ + void GoToNextArea(OperationalStatusEnum currentAreaOpState, bool & finished); }; } // namespace ServiceArea diff --git a/examples/rvc-app/rvc-common/pics/rvc-app-pics-values b/examples/rvc-app/rvc-common/pics/rvc-app-pics-values index 669abd26c33f94..c38f0c1b7e00a0 100644 --- a/examples/rvc-app/rvc-common/pics/rvc-app-pics-values +++ b/examples/rvc-app/rvc-common/pics/rvc-app-pics-values @@ -50,3 +50,31 @@ RVCRUNM.S.C01.Tx=1 RVCRUNM.S.F00=0 RVCRUNM.S.M.CAN_TEST_MODE_FAILURE=1 RVCRUNM.S.M.CAN_MANUALLY_CONTROLLED=1 + +SEAR.S=1 +SEAR.S.F00=0 +SEAR.S.F01=1 +SEAR.S.F02=1 +SEAR.S.A0000=1 +SEAR.S.A0001=1 +SEAR.S.A0002=1 +SEAR.S.A0003=1 +SEAR.S.A0004=1 +SEAR.S.A0005=1 +SEAR.S.C00.Rsp=1 +SEAR.S.C02.Rsp=1 +SEAR.S.C01.Tx=1 +SEAR.S.C03.Tx=1 +SEAR.S.M.REMOVE_AREA=1 +SEAR.S.M.ADD_AREA=1 +SEAR.S.M.REMOVE_MAP=1 +SEAR.S.M.ADD_MAP=1 +SEAR.S.M.INVALID_STATE_FOR_SELECT_AREAS=1 +SEAR.S.M.VALID_STATE_FOR_SELECT_AREAS=0 +SEAR.S.M.SELECT_AREAS_WHILE_NON_IDLE=1 +SEAR.S.M.HAS_MANUAL_SELAREA_STATE_CONTROL=1 +SEAR.S.M.HAS_MANUAL_SKIP_STATE_CONTROL=1 +SEAR.S.M.INVALID_STATE_FOR_SKIP=1 +SEAR.S.M.NO_SELAREA_FOR_SKIP=1 +SEAR.S.M.VALID_STATE_FOR_SKIP=1 +SEAR.S.M.HAS_MANUAL_OPERATING_STATE_CONTROL=1 \ No newline at end of file diff --git a/examples/rvc-app/rvc-common/src/rvc-device.cpp b/examples/rvc-app/rvc-common/src/rvc-device.cpp index e018e0929301c7..623c95963f8834 100644 --- a/examples/rvc-app/rvc-common/src/rvc-device.cpp +++ b/examples/rvc-app/rvc-common/src/rvc-device.cpp @@ -52,6 +52,7 @@ void RvcDevice::HandleRvcRunChangeToMode(uint8_t newMode, ModeBase::Commands::Ch mDocked = false; mRunModeInstance.UpdateCurrentMode(newMode); mOperationalStateInstance.SetOperationalState(to_underlying(OperationalState::OperationalStateEnum::kRunning)); + mServiceAreaDelegate.SetAttributesAtCleanStart(); response.status = to_underlying(ModeBase::StatusCode::kSuccess); return; } @@ -68,6 +69,8 @@ void RvcDevice::HandleRvcRunChangeToMode(uint8_t newMode, ModeBase::Commands::Ch mRunModeInstance.UpdateCurrentMode(newMode); mOperationalStateInstance.SetOperationalState(to_underlying(RvcOperationalState::OperationalStateEnum::kSeekingCharger)); response.status = to_underlying(ModeBase::StatusCode::kSuccess); + + UpdateServiceAreaProgressOnExit(); return; } break; @@ -161,6 +164,55 @@ void RvcDevice::HandleOpStateGoHomeCallback(Clusters::OperationalState::GenericO } } +bool RvcDevice::SaIsSetSelectedAreasAllowed(MutableCharSpan & statusText) +{ + if (mOperationalStateInstance.GetCurrentOperationalState() == to_underlying(OperationalState::OperationalStateEnum::kRunning)) + { + CopyCharSpanToMutableCharSpan("cannot set the Selected Areas while the device is running"_span, statusText); + return false; + } + return true; +} + +bool RvcDevice::SaHandleSkipCurrentArea(uint32_t skippedArea, MutableCharSpan & skipStatusText) +{ + if (mServiceAreaInstance.GetCurrentArea() != skippedArea) + { + // This device only supports skipping the current location. + CopyCharSpanToMutableCharSpan("the skipped area does not match the current area"_span, skipStatusText); + return false; + } + + if (mOperationalStateInstance.GetCurrentOperationalState() != to_underlying(OperationalState::OperationalStateEnum::kRunning)) + { + // This device only accepts the skip are command while in the running state + CopyCharSpanToMutableCharSpan("skip area is only accepted when the device is running"_span, skipStatusText); + return false; + } + + bool finished; + mServiceAreaDelegate.GoToNextArea(ServiceArea::OperationalStatusEnum::kSkipped, finished); + + if (finished) + { + HandleActivityCompleteEvent(); + } + + return true; +} + +bool RvcDevice::SaIsSupportedAreasChangeAllowed() +{ + return mOperationalStateInstance.GetCurrentOperationalState() != + to_underlying(OperationalState::OperationalStateEnum::kRunning); +} + +bool RvcDevice::SaIsSupportedMapChangeAllowed() +{ + return mOperationalStateInstance.GetCurrentOperationalState() != + to_underlying(OperationalState::OperationalStateEnum::kRunning); +} + void RvcDevice::HandleChargedMessage() { if (mOperationalStateInstance.GetCurrentOperationalState() != @@ -258,6 +310,41 @@ void RvcDevice::HandleActivityCompleteEvent() mOperationalStateInstance.OnOperationCompletionDetected(0, a, b); mOperationalStateInstance.SetOperationalState(to_underlying(RvcOperationalState::OperationalStateEnum::kSeekingCharger)); + + mServiceAreaInstance.SetCurrentArea(DataModel::NullNullable); + mServiceAreaInstance.SetEstimatedEndTime(DataModel::NullNullable); + UpdateServiceAreaProgressOnExit(); +} + +void RvcDevice::HandleAreaCompletedEvent() +{ + bool finished; + mServiceAreaDelegate.GoToNextArea(ServiceArea::OperationalStatusEnum::kCompleted, finished); + + if (finished) + { + HandleActivityCompleteEvent(); + } +} + +void RvcDevice::HandleAddServiceAreaMap(uint32_t mapId, const CharSpan & mapName) +{ + mServiceAreaInstance.AddSupportedMap(mapId, mapName); +} + +void RvcDevice::HandleAddServiceAreaArea(ServiceArea::AreaStructureWrapper & area) +{ + mServiceAreaInstance.AddSupportedArea(area); +} + +void RvcDevice::HandleRemoveServiceAreaMap(uint32_t mapId) +{ + mServiceAreaDelegate.RemoveSupportedMap(mapId); +} + +void RvcDevice::HandleRemoveServiceAreaArea(uint32_t areaId) +{ + mServiceAreaDelegate.RemoveSupportedArea(areaId); } void RvcDevice::HandleErrorEvent(const std::string & error) @@ -334,4 +421,31 @@ void RvcDevice::HandleResetMessage() mRunModeInstance.UpdateCurrentMode(RvcRunMode::ModeIdle); mOperationalStateInstance.SetOperationalState(to_underlying(OperationalState::OperationalStateEnum::kStopped)); mCleanModeInstance.UpdateCurrentMode(RvcCleanMode::ModeQuick); + + mServiceAreaInstance.ClearSelectedAreas(); + mServiceAreaInstance.ClearProgress(); + mServiceAreaInstance.SetCurrentArea(DataModel::NullNullable); + mServiceAreaInstance.SetEstimatedEndTime(DataModel::NullNullable); + + mServiceAreaDelegate.SetMapTopology(); +} + +void RvcDevice::UpdateServiceAreaProgressOnExit() +{ + if (!mServiceAreaInstance.HasFeature(ServiceArea::Feature::kProgressReporting)) + { + return; + } + + uint32_t i = 0; + ServiceArea::Structs::ProgressStruct::Type progressElement; + while (mServiceAreaDelegate.GetProgressElementByIndex(i, progressElement)) + { + if (progressElement.status == ServiceArea::OperationalStatusEnum::kOperating || + progressElement.status == ServiceArea::OperationalStatusEnum::kPending) + { + mServiceAreaInstance.SetProgressStatus(progressElement.areaID, ServiceArea::OperationalStatusEnum::kSkipped); + } + i++; + } } diff --git a/examples/rvc-app/rvc-common/src/rvc-service-area-delegate.cpp b/examples/rvc-app/rvc-common/src/rvc-service-area-delegate.cpp index 1bc60baa0774cd..984472977a0356 100644 --- a/examples/rvc-app/rvc-common/src/rvc-service-area-delegate.cpp +++ b/examples/rvc-app/rvc-common/src/rvc-service-area-delegate.cpp @@ -23,21 +23,13 @@ using namespace chip; using namespace chip::app::Clusters; using namespace chip::app::Clusters::ServiceArea; -CHIP_ERROR RvcServiceAreaDelegate::Init() +void RvcServiceAreaDelegate::SetMapTopology() { - // hardcoded fill of SUPPORTED MAPS for prototyping - uint32_t supportedMapId_XX = 3; - uint32_t supportedMapId_YY = 245; + GetInstance()->ClearSupportedMaps(); GetInstance()->AddSupportedMap(supportedMapId_XX, "My Map XX"_span); GetInstance()->AddSupportedMap(supportedMapId_YY, "My Map YY"_span); - // hardcoded fill of SUPPORTED AREAS for prototyping - uint32_t supportedAreaID_A = 7; - uint32_t supportedAreaID_B = 1234567; - uint32_t supportedAreaID_C = 10050; - uint32_t supportedAreaID_D = 0x88888888; - // Area A has name, floor number, uses map XX auto areaA = AreaStructureWrapper{} @@ -47,7 +39,7 @@ CHIP_ERROR RvcServiceAreaDelegate::Init() // Area B has name, uses map XX auto areaB = AreaStructureWrapper{} - .SetMapId(supportedAreaID_B) + .SetAreaId(supportedAreaID_B) .SetMapId(supportedMapId_XX) .SetLocationInfo("My Location B"_span, DataModel::NullNullable, DataModel::NullNullable); @@ -69,6 +61,45 @@ CHIP_ERROR RvcServiceAreaDelegate::Init() GetInstance()->AddSupportedArea(areaB); GetInstance()->AddSupportedArea(areaC); GetInstance()->AddSupportedArea(areaD); +} + +void RvcServiceAreaDelegate::SetNoMapTopology() +{ + GetInstance()->ClearSupportedMaps(); + + // Area A has name, floor number. + auto areaA = + AreaStructureWrapper{} + .SetAreaId(supportedAreaID_A) + .SetLocationInfo("My Location A"_span, DataModel::Nullable(4), DataModel::Nullable()); + + // Area B has name. + auto areaB = AreaStructureWrapper{} + .SetAreaId(supportedAreaID_B) + .SetLocationInfo("My Location B"_span, DataModel::NullNullable, DataModel::NullNullable); + + // Area C has full SemData, no name. + auto areaC = AreaStructureWrapper{} + .SetAreaId(supportedAreaID_C) + .SetLocationInfo(""_span, -1, Globals::AreaTypeTag::kPlayRoom) + .SetLandmarkInfo(Globals::LandmarkTag::kBackDoor, Globals::RelativePositionTag::kNextTo); + + // Area D has null values for all landmark fields. + auto areaD = AreaStructureWrapper{} + .SetAreaId(supportedAreaID_D) + .SetLocationInfo("My Location D"_span, DataModel::NullNullable, DataModel::NullNullable) + .SetLandmarkInfo(Globals::LandmarkTag::kCouch, Globals::RelativePositionTag::kNextTo); + + GetInstance()->AddSupportedArea(areaA); + GetInstance()->AddSupportedArea(areaB); + GetInstance()->AddSupportedArea(areaC); + GetInstance()->AddSupportedArea(areaD); +} + +CHIP_ERROR RvcServiceAreaDelegate::Init() +{ + SetMapTopology(); + GetInstance()->SetCurrentArea(supportedAreaID_C); return CHIP_NO_ERROR; @@ -77,23 +108,81 @@ CHIP_ERROR RvcServiceAreaDelegate::Init() //************************************************************************* // command support -bool RvcServiceAreaDelegate::IsSetSelectedAreasAllowed(MutableCharSpan statusText) +bool RvcServiceAreaDelegate::IsSetSelectedAreasAllowed(MutableCharSpan & statusText) { - // TODO IMPLEMENT - return true; + return (mIsSetSelectedAreasAllowedDeviceInstance->*mIsSetSelectedAreasAllowedCallback)(statusText); }; bool RvcServiceAreaDelegate::IsValidSelectAreasSet(const Commands::SelectAreas::DecodableType & req, SelectAreasStatus & areaStatus, - MutableCharSpan statusText) + MutableCharSpan & statusText) { - // TODO IMPLEMENT + // if req is empty list return true. + { + size_t reqSize; + if (req.newAreas.ComputeSize(&reqSize) != CHIP_NO_ERROR) + { + areaStatus = SelectAreasStatus::kInvalidSet; // todo Not sure this is the correct error to use here + CopyCharSpanToMutableCharSpan("error computing number of selected areas"_span, statusText); + return false; + } + + if (reqSize == 0) + { + return true; + } + } + + // If there is 1 or 0 supported maps, any combination of areas is valid. + if (!GetInstance()->HasFeature(Feature::kMaps) || GetNumberOfSupportedMaps() <= 1) + { + return true; + } + + // Check that all the requested areas are in the same map. + auto newAreasIter = req.newAreas.begin(); + newAreasIter.Next(); + + AreaStructureWrapper tempArea; + uint32_t ignoredIndex; + if (!GetSupportedAreaById(newAreasIter.GetValue(), ignoredIndex, tempArea)) + { + areaStatus = SelectAreasStatus::kUnsupportedArea; + CopyCharSpanToMutableCharSpan("unable to find selected area in supported areas"_span, statusText); + return false; + } + + auto mapId = tempArea.mapID.Value(); // It is safe to call `.Value()` as we confirmed that there are at least 2 maps. + + while (newAreasIter.Next()) + { + if (!GetSupportedAreaById(newAreasIter.GetValue(), ignoredIndex, tempArea)) + { + areaStatus = SelectAreasStatus::kUnsupportedArea; + CopyCharSpanToMutableCharSpan("unable to find selected area in supported areas"_span, statusText); + return false; + } + + if (tempArea.mapID.Value() != mapId) + { + areaStatus = SelectAreasStatus::kInvalidSet; + CopyCharSpanToMutableCharSpan("all selected areas must be in the same map"_span, statusText); + return false; + } + } + + if (CHIP_NO_ERROR != newAreasIter.GetStatus()) + { + areaStatus = SelectAreasStatus::kInvalidSet; + CopyCharSpanToMutableCharSpan("error processing new areas."_span, statusText); + return false; + } + return true; }; -bool RvcServiceAreaDelegate::HandleSkipCurrentArea(uint32_t skippedArea, MutableCharSpan skipStatusText) +bool RvcServiceAreaDelegate::HandleSkipCurrentArea(uint32_t skippedArea, MutableCharSpan & skipStatusText) { - // TODO IMPLEMENT - return true; + return (mHandleSkipCurrentAreaDeviceInstance->*mHandleSkipCurrentAreaCallback)(skippedArea, skipStatusText); }; //************************************************************************* @@ -101,8 +190,7 @@ bool RvcServiceAreaDelegate::HandleSkipCurrentArea(uint32_t skippedArea, Mutable bool RvcServiceAreaDelegate::IsSupportedAreasChangeAllowed() { - // TODO IMPLEMENT - return true; + return (mIsSupportedAreasChangeAllowedDeviceInstance->*mIsSupportedAreasChangeAllowedCallback)(); } uint32_t RvcServiceAreaDelegate::GetNumberOfSupportedAreas() @@ -191,13 +279,26 @@ bool RvcServiceAreaDelegate::ClearSupportedAreas() return false; } +bool RvcServiceAreaDelegate::RemoveSupportedArea(uint32_t areaId) +{ + for (auto it = mSupportedAreas.begin(); it != mSupportedAreas.end(); ++it) + { + if (it->areaID == areaId) + { + mSupportedAreas.erase(it); + return true; + } + } + + return false; +} + //************************************************************************* // Supported Maps accessors bool RvcServiceAreaDelegate::IsSupportedMapChangeAllowed() { - // TODO IMPLEMENT - return true; + return (mIsSupportedMapChangeAllowedDeviceInstance->*mIsSupportedMapChangeAllowedCallback)(); } uint32_t RvcServiceAreaDelegate::GetNumberOfSupportedMaps() @@ -285,6 +386,114 @@ bool RvcServiceAreaDelegate::ClearSupportedMaps() return false; } +void RvcServiceAreaDelegate::HandleSupportedAreasUpdated() +{ + // Get a list of supported area IDs as `supportedAreaIDs` + std::vector supportedAreaIDs; + for (const auto & supportedArea : mSupportedAreas) + { + supportedAreaIDs.push_back(supportedArea.areaID); + } + + if (supportedAreaIDs.empty()) + { + // Clear all selected areas, current area, and progress if there are no supported areas. + GetInstance()->ClearSelectedAreas(); + GetInstance()->SetCurrentArea(DataModel::NullNullable); + GetInstance()->ClearProgress(); + return; + } + + // Remove mSelectedArea elements that do not exist is `supportedAreaIDs` + { + for (auto it = mSelectedAreas.begin(); it != mSelectedAreas.end();) + { + if (std::find(supportedAreaIDs.begin(), supportedAreaIDs.end(), *it) == supportedAreaIDs.end()) + { + it = mSelectedAreas.erase(it); + } + else + { + ++it; + } + } + } + + // Set current Area to null if current area is not in `supportedAreaIDs` + { + auto currentAreaId = GetInstance()->GetCurrentArea(); + if (!currentAreaId.IsNull() && + std::find(supportedAreaIDs.begin(), supportedAreaIDs.end(), currentAreaId.Value()) == supportedAreaIDs.end()) + { + GetInstance()->SetCurrentArea(DataModel::NullNullable); + } + } + + // Remove mProgress elements that do not exist is `supportedAreaIDs` + { + for (auto it = mProgressList.begin(); it != mProgressList.end();) + { + if (std::find(supportedAreaIDs.begin(), supportedAreaIDs.end(), it->areaID) == supportedAreaIDs.end()) + { + it = mProgressList.erase(it); + } + else + { + ++it; + } + } + } +} + +bool RvcServiceAreaDelegate::RemoveSupportedMap(uint32_t mapId) +{ + bool removedEntry = false; + for (auto it = mSupportedMaps.begin(); it != mSupportedMaps.end(); ++it) + { + if (it->mapID == mapId) + { + mSupportedMaps.erase(it); + removedEntry = true; + } + } + + if (!removedEntry) + { + return false; + } + + // If there are no supported maps left, none of the supported areas are vaild and their MapID needs to be null. + if (GetNumberOfSupportedMaps() == 0) + { + ClearSupportedAreas(); + return true; + } + + // Get the supported area IDs where the map ID matches the removed map ID + std::vector supportedAreaIds; + { + for (const auto & supportedArea : mSupportedAreas) + { + if (supportedArea.mapID == mapId) + { + supportedAreaIds.push_back(supportedArea.areaID); + } + } + } + + // Remove the supported areas with the matching map ID + if (!supportedAreaIds.empty()) + { + for (const auto & supportedAreaId : supportedAreaIds) + { + RemoveSupportedArea(supportedAreaId); + } + HandleSupportedAreasUpdated(); + } + + return true; +} + //************************************************************************* // Selected areas accessors @@ -397,8 +606,15 @@ bool RvcServiceAreaDelegate::AddProgressElement(const Structs::ProgressStruct::T bool RvcServiceAreaDelegate::ModifyProgressElement(uint32_t listIndex, const Structs::ProgressStruct::Type & modifiedProgressElement) { - // TODO IMPLEMENT - return false; + if (modifiedProgressElement.areaID != mProgressList[listIndex].areaID) + { + ChipLogError(Zcl, "ModifyProgressElement - areaID's do not match, new areaID %u, existing areaID %u", + modifiedProgressElement.areaID, mProgressList[listIndex].areaID); + return false; + } + + mProgressList[listIndex] = modifiedProgressElement; + return true; } bool RvcServiceAreaDelegate::ClearProgress() @@ -411,3 +627,125 @@ bool RvcServiceAreaDelegate::ClearProgress() return false; } + +void RvcServiceAreaDelegate::SetAttributesAtCleanStart() +{ + if (GetNumberOfSupportedAreas() == 0) + { + return; + } + + if (GetNumberOfSelectedAreas() == 0) + { + AreaStructureWrapper firstArea; + GetSupportedAreaByIndex(0, firstArea); + + GetInstance()->SetCurrentArea(firstArea.areaID); + + if (GetInstance()->HasFeature(Feature::kProgressReporting)) + { + GetInstance()->AddPendingProgressElement(firstArea.areaID); + GetInstance()->SetProgressStatus(firstArea.areaID, OperationalStatusEnum::kOperating); + } + } + else + { + uint32_t areaId; + GetSelectedAreaByIndex(0, areaId); + + GetInstance()->SetCurrentArea(areaId); + + if (GetInstance()->HasFeature(Feature::kProgressReporting)) + { + GetInstance()->AddPendingProgressElement(areaId); + GetInstance()->SetProgressStatus(areaId, OperationalStatusEnum::kOperating); + + uint32_t i = 1; + while (GetSelectedAreaByIndex(i, areaId)) + { + GetInstance()->AddPendingProgressElement(areaId); + i++; + } + } + } +} + +void RvcServiceAreaDelegate::GoToNextArea(OperationalStatusEnum currentAreaOpState, bool & finished) +{ + AreaStructureWrapper currentArea; + auto currentAreaIdN = GetInstance()->GetCurrentArea(); + + if (currentAreaIdN.IsNull()) + { + ChipLogError(Zcl, "GoToNextArea: Cannot go to the next area when the current area is null."); + return; + } + + if (currentAreaOpState != OperationalStatusEnum::kCompleted && currentAreaOpState != OperationalStatusEnum::kSkipped) + { + ChipLogError(Zcl, "GoToNextArea: currentAreaOpState must be either completed or skipped."); + return; + } + + auto currentAreaId = currentAreaIdN.Value(); + uint32_t currentAreaIndex; + GetSupportedAreaById(currentAreaId, currentAreaIndex, currentArea); + auto currentAreaMapId = currentArea.mapID; + finished = true; + + if (GetInstance()->HasFeature(Feature::kProgressReporting)) + { + GetInstance()->SetProgressStatus(currentAreaId, currentAreaOpState); + } + + if (GetNumberOfSelectedAreas() == 0) + { + AreaStructureWrapper nextArea; + uint32_t nextIndex = currentAreaIndex + 1; + while (GetSupportedAreaByIndex(nextIndex, nextArea)) + { + if (!currentAreaMapId.IsNull() && nextArea.mapID == currentAreaMapId.Value()) + { + GetInstance()->SetCurrentArea(nextArea.areaID); + + if (GetInstance()->HasFeature(Feature::kProgressReporting)) + { + GetInstance()->SetProgressStatus(nextArea.areaID, OperationalStatusEnum::kOperating); + } + + finished = false; + return; + } + + ++nextIndex; + } + } + else + { + uint32_t selectedAreaId; + uint32_t selectedAreaIndex = 0; + while (GetSelectedAreaByIndex(selectedAreaIndex, selectedAreaId)) + { + if (selectedAreaId == currentAreaId) + { + break; + } + ++selectedAreaIndex; + } + + uint32_t nextSelectedAreaId; + uint32_t nextSelectedAreaIndex = selectedAreaIndex + 1; + if (GetSelectedAreaByIndex(nextSelectedAreaIndex, nextSelectedAreaId)) + { + GetInstance()->SetCurrentArea(nextSelectedAreaId); + + if (GetInstance()->HasFeature(Feature::kProgressReporting)) + { + GetInstance()->SetProgressStatus(nextSelectedAreaId, OperationalStatusEnum::kOperating); + } + + finished = false; + return; + } + } +} diff --git a/examples/smoke-co-alarm-app/silabs/src/AppTask.cpp b/examples/smoke-co-alarm-app/silabs/src/AppTask.cpp index dc99728ccf90d5..105a3c9e5b6d9c 100644 --- a/examples/smoke-co-alarm-app/silabs/src/AppTask.cpp +++ b/examples/smoke-co-alarm-app/silabs/src/AppTask.cpp @@ -31,7 +31,7 @@ #include #include -#if (defined(SL_CATALOG_SIMPLE_LED_LED1_PRESENT) || defined(BRD4325B)) +#if (defined(SL_CATALOG_SIMPLE_LED_LED1_PRESENT)) #define LIGHT_LED 1 #else #define LIGHT_LED 0 diff --git a/examples/thermostat/linux/include/thermostat-delegate-impl.h b/examples/thermostat/linux/include/thermostat-delegate-impl.h index f559977f341949..8252f2274f9d79 100644 --- a/examples/thermostat/linux/include/thermostat-delegate-impl.h +++ b/examples/thermostat/linux/include/thermostat-delegate-impl.h @@ -44,6 +44,10 @@ class ThermostatDelegate : public Delegate public: static inline ThermostatDelegate & GetInstance() { return sInstance; } + std::optional + GetAtomicWriteTimeout(DataModel::DecodableList attributeRequests, + System::Clock::Milliseconds16 timeoutRequest) override; + CHIP_ERROR GetPresetTypeAtIndex(size_t index, Structs::PresetTypeStruct::Type & presetType) override; uint8_t GetNumberOfPresets() override; diff --git a/examples/thermostat/linux/thermostat-delegate-impl.cpp b/examples/thermostat/linux/thermostat-delegate-impl.cpp index 61d496f233b408..c39a757a8b0644 100644 --- a/examples/thermostat/linux/thermostat-delegate-impl.cpp +++ b/examples/thermostat/linux/thermostat-delegate-impl.cpp @@ -148,6 +148,47 @@ CHIP_ERROR ThermostatDelegate::SetActivePresetHandle(const DataModel::Nullable +ThermostatDelegate::GetAtomicWriteTimeout(DataModel::DecodableList attributeRequests, + System::Clock::Milliseconds16 timeoutRequest) +{ + auto attributeIdsIter = attributeRequests.begin(); + bool requestedPresets = false, requestedSchedules = false; + while (attributeIdsIter.Next()) + { + auto & attributeId = attributeIdsIter.GetValue(); + + switch (attributeId) + { + case Attributes::Presets::Id: + requestedPresets = true; + break; + case Attributes::Schedules::Id: + requestedSchedules = true; + break; + default: + return System::Clock::Milliseconds16(0); + } + } + if (attributeIdsIter.GetStatus() != CHIP_NO_ERROR) + { + return System::Clock::Milliseconds16(0); + } + auto timeout = System::Clock::Milliseconds16(0); + if (requestedPresets) + { + // If the client expects to edit the presets, then we'll give it 3 seconds to do so + timeout += std::chrono::milliseconds(3000); + } + if (requestedSchedules) + { + // If the client expects to edit the schedules, then we'll give it 9 seconds to do so + timeout += std::chrono::milliseconds(9000); + } + // If the client requested an even smaller timeout, then use that one + return std::min(timeoutRequest, timeout); +} + void ThermostatDelegate::InitializePendingPresets() { mNextFreeIndexInPendingPresetsList = 0; diff --git a/examples/thermostat/thermostat-common/thermostat.matter b/examples/thermostat/thermostat-common/thermostat.matter index a53e3cdc692952..c53651c537894e 100644 --- a/examples/thermostat/thermostat-common/thermostat.matter +++ b/examples/thermostat/thermostat-common/thermostat.matter @@ -2801,7 +2801,7 @@ endpoint 1 { callback attribute acceptedCommandList; callback attribute attributeList; ram attribute featureMap default = 0x123; - ram attribute clusterRevision default = 6; + ram attribute clusterRevision default = 7; handle command SetpointRaiseLower; handle command SetActiveScheduleRequest; diff --git a/examples/thermostat/thermostat-common/thermostat.zap b/examples/thermostat/thermostat-common/thermostat.zap index 8b2fe4f5c82318..ffb9da2b136de9 100644 --- a/examples/thermostat/thermostat-common/thermostat.zap +++ b/examples/thermostat/thermostat-common/thermostat.zap @@ -5088,7 +5088,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "6", + "defaultValue": "7", "reportable": 1, "minInterval": 0, "maxInterval": 65344, diff --git a/examples/thread-br-app/esp32/main/main.cpp b/examples/thread-br-app/esp32/main/main.cpp index d419e2a73505b3..dcafcc1c74ae40 100644 --- a/examples/thread-br-app/esp32/main/main.cpp +++ b/examples/thread-br-app/esp32/main/main.cpp @@ -88,6 +88,8 @@ static void InitServer(intptr_t context) sThreadBRDelegate = chip::Platform::New(storageDelegate); sThreadBRMgmtInstance = chip::Platform::New( kThreadBRMgmtEndpoint, sThreadBRDelegate, chip::Server::GetInstance().GetFailSafeContext()); + char borderRouterName[] = "Espressif-ThreadBR"; + sThreadBRDelegate->SetThreadBorderRouterName(CharSpan(borderRouterName)); sThreadBRMgmtInstance->Init(); } diff --git a/examples/thread-br-app/esp32/partitions.csv b/examples/thread-br-app/esp32/partitions.csv index c451874965912a..745321df3785fc 100644 --- a/examples/thread-br-app/esp32/partitions.csv +++ b/examples/thread-br-app/esp32/partitions.csv @@ -4,5 +4,5 @@ nvs, data, nvs, , 0xC000, otadata, data, ota, , 0x2000, phy_init, data, phy, , 0x1000, ota_0, app, ota_0, , 1800K, -ota_1, app, ota_1, , 1200K, -rcp_fw, data, spiffs, , 640K, +ota_1, app, ota_1, , 1800K, +rcp_fw, data, spiffs, , 300K, diff --git a/examples/tv-app/android/java/ContentAppCommandDelegate.cpp b/examples/tv-app/android/java/ContentAppCommandDelegate.cpp index 8d459588a7a8d6..02b4a7e806fefb 100644 --- a/examples/tv-app/android/java/ContentAppCommandDelegate.cpp +++ b/examples/tv-app/android/java/ContentAppCommandDelegate.cpp @@ -49,6 +49,37 @@ using Status = chip::Protocols::InteractionModel::Status; const std::string FAILURE_KEY = "PlatformError"; const std::string FAILURE_STATUS_KEY = "Status"; +bool isValidJson(const char * response) +{ + Json::Reader reader; + + Json::CharReaderBuilder readerBuilder; + std::string errors; + + Json::Value value; + std::unique_ptr testReader(readerBuilder.newCharReader()); + + if (!testReader->parse(response, response + std::strlen(response), &value, &errors)) + { + ChipLogError(Zcl, "Failed to parse JSON: %s\n", errors.c_str()); + return false; + } + + // Validate and access JSON data safely + if (!value.isObject()) + { + ChipLogError(Zcl, "Invalid JSON structure: not an object"); + return false; + } + + if (!reader.parse(response, value)) + { + return false; + } + + return true; +} + void ContentAppCommandDelegate::InvokeCommand(CommandHandlerInterface::HandlerContext & handlerContext) { if (handlerContext.mRequestPath.mEndpointId >= FIXED_ENDPOINT_COUNT) @@ -94,7 +125,15 @@ void ContentAppCommandDelegate::InvokeCommand(CommandHandlerInterface::HandlerCo { JniUtfString respStr(env, resp); ChipLogProgress(Zcl, "ContentAppCommandDelegate::InvokeCommand got response %s", respStr.c_str()); - FormatResponseData(handlerContext, respStr.c_str()); + if (isValidJson(respStr.c_str())) + { + FormatResponseData(handlerContext, respStr.c_str()); + } + else + { + // return dummy value in case JSON is invalid + FormatResponseData(handlerContext, "{\"value\":{}}"); + } } env->DeleteLocalRef(resp); } @@ -141,7 +180,6 @@ Status ContentAppCommandDelegate::InvokeCommand(EndpointId epId, ClusterId clust if (!testReader->parse(respStr.c_str(), respStr.c_str() + std::strlen(respStr.c_str()), &value, &errors)) { ChipLogError(Zcl, "Failed to parse JSON: %s\n", errors.c_str()); - env->DeleteLocalRef(resp); return chip::Protocols::InteractionModel::Status::Failure; } @@ -149,14 +187,12 @@ Status ContentAppCommandDelegate::InvokeCommand(EndpointId epId, ClusterId clust if (!value.isObject()) { ChipLogError(Zcl, "Invalid JSON structure: not an object"); - env->DeleteLocalRef(resp); return chip::Protocols::InteractionModel::Status::Failure; } Json::Reader reader; if (!reader.parse(respStr.c_str(), value)) { - env->DeleteLocalRef(resp); return chip::Protocols::InteractionModel::Status::Failure; } } @@ -185,31 +221,7 @@ Status ContentAppCommandDelegate::InvokeCommand(EndpointId epId, ClusterId clust void ContentAppCommandDelegate::FormatResponseData(CommandHandlerInterface::HandlerContext & handlerContext, const char * response) { handlerContext.SetCommandHandled(); - Json::Reader reader; - - Json::CharReaderBuilder readerBuilder; - std::string errors; - Json::Value value; - std::unique_ptr testReader(readerBuilder.newCharReader()); - - if (!testReader->parse(response, response + std::strlen(response), &value, &errors)) - { - ChipLogError(Zcl, "Failed to parse JSON: %s\n", errors.c_str()); - return; - } - - // Validate and access JSON data safely - if (!value.isObject()) - { - ChipLogError(Zcl, "Invalid JSON structure: not an object"); - return; - } - - if (!reader.parse(response, value)) - { - return; - } // handle errors from platform-app if (!value[FAILURE_KEY].empty()) diff --git a/integrations/docker/images/base/chip-build/version b/integrations/docker/images/base/chip-build/version index 885f3e39127977..5b9870bc97994b 100644 --- a/integrations/docker/images/base/chip-build/version +++ b/integrations/docker/images/base/chip-build/version @@ -1 +1 @@ -69 : [Infineon] Update ModusToolbox version to 3.2 +71 : [NXP] Update k32w1 SDK as it will use common NXP SDK diff --git a/integrations/docker/images/stage-2/chip-build-k32w/Dockerfile b/integrations/docker/images/stage-2/chip-build-k32w/Dockerfile index 83f9f7561321b4..c4cf71927d59fe 100644 --- a/integrations/docker/images/stage-2/chip-build-k32w/Dockerfile +++ b/integrations/docker/images/stage-2/chip-build-k32w/Dockerfile @@ -14,24 +14,20 @@ RUN set -x \ WORKDIR /opt/sdk RUN set -x \ - && python3 -m pip install --break-system-packages -U --no-cache-dir west==1.0.0 \ + && python3 -m pip install --break-system-packages -U --no-cache-dir west==1.2.0 \ + && : # last line + +RUN set -x \ && west init -m https://github.com/nxp-mcuxpresso/mcux-sdk --mr "MCUX_2.6.14_K32W0" \ - && west update \ + && west update -o=--depth=1 -n -f smart \ && chmod +x core/tools/imagetool/sign_images.sh \ && ln -sf ../rtos core \ && ln -sf ../middleware core \ && cp -R examples/* core/boards && rm -rf examples \ && : # last line -RUN set -x \ - && mkdir -p k32w1 \ - && wget https://cache.nxp.com/lgfiles/bsps/SDK_2_12_7_K32W148-EVK.zip \ - && unzip SDK_2_12_7_K32W148-EVK.zip -d k32w1 \ - && rm -rf SDK_2_12_7_K32W148-EVK.zip - FROM ghcr.io/project-chip/chip-build:${VERSION} COPY --from=build /opt/sdk/ /opt/sdk/ ENV NXP_K32W0_SDK_ROOT=/opt/sdk/core -ENV NXP_K32W1_SDK_ROOT=/opt/sdk/k32w1 diff --git a/integrations/docker/images/stage-2/chip-build-nxp/Dockerfile b/integrations/docker/images/stage-2/chip-build-nxp/Dockerfile new file mode 100644 index 00000000000000..537919fb77bc21 --- /dev/null +++ b/integrations/docker/images/stage-2/chip-build-nxp/Dockerfile @@ -0,0 +1,23 @@ +ARG VERSION=1 +FROM ghcr.io/project-chip/chip-build:${VERSION} AS build +LABEL org.opencontainers.image.source=https://github.com/project-chip/connectedhomeip + +RUN set -x \ + && apt-get update \ + && DEBIAN_FRONTEND=noninteractive apt-get install -fy --no-install-recommends \ + xz-utils \ + && : # last line + +WORKDIR /opt/nxp/ + +RUN set -x \ + && git clone --branch v1.4.0-pvw1 https://github.com/NXP/nxp_matter_support.git \ + && pip3 install --break-system-packages -U --no-cache-dir west \ + && ./nxp_matter_support/scripts/update_nxp_sdk.py --platform common \ + && : # last line + +FROM ghcr.io/project-chip/chip-build:${VERSION} + +COPY --from=build /opt/nxp/ /opt/nxp/ + +ENV NXP_UPDATE_SDK_SCRIPT_DOCKER=/opt/nxp/nxp_matter_support/scripts/update_nxp_sdk.py diff --git a/integrations/docker/images/stage-2/chip-build-nxp/build.sh b/integrations/docker/images/stage-2/chip-build-nxp/build.sh new file mode 120000 index 00000000000000..46b20313461454 --- /dev/null +++ b/integrations/docker/images/stage-2/chip-build-nxp/build.sh @@ -0,0 +1 @@ +../../../build.sh \ No newline at end of file diff --git a/integrations/docker/images/stage-2/chip-build-nxp/run.sh b/integrations/docker/images/stage-2/chip-build-nxp/run.sh new file mode 120000 index 00000000000000..9bbfad86d46e50 --- /dev/null +++ b/integrations/docker/images/stage-2/chip-build-nxp/run.sh @@ -0,0 +1 @@ +../../../run.sh \ No newline at end of file diff --git a/integrations/docker/images/stage-2/chip-build-nxp/version b/integrations/docker/images/stage-2/chip-build-nxp/version new file mode 120000 index 00000000000000..a40ba48b0188a8 --- /dev/null +++ b/integrations/docker/images/stage-2/chip-build-nxp/version @@ -0,0 +1 @@ +../../base/chip-build/version \ No newline at end of file diff --git a/integrations/docker/images/vscode/chip-build-vscode/Dockerfile b/integrations/docker/images/vscode/chip-build-vscode/Dockerfile index 4dda050b9c496a..9d6e24b1d259ea 100644 --- a/integrations/docker/images/vscode/chip-build-vscode/Dockerfile +++ b/integrations/docker/images/vscode/chip-build-vscode/Dockerfile @@ -11,6 +11,7 @@ FROM ghcr.io/project-chip/chip-build-crosscompile:${VERSION} AS crosscompile FROM ghcr.io/project-chip/chip-build-ameba:${VERSION} AS ameba FROM ghcr.io/project-chip/chip-build-k32w:${VERSION} AS k32w FROM ghcr.io/project-chip/chip-build-rw61x:${VERSION} AS rw61x +FROM ghcr.io/project-chip/chip-build-nxp:${VERSION} AS nxp FROM ghcr.io/project-chip/chip-build-nxp-zephyr:${VERSION} AS nxpzephyr FROM ghcr.io/project-chip/chip-build-imx:${VERSION} AS imx FROM ghcr.io/project-chip/chip-build-ti:${VERSION} AS ti @@ -51,6 +52,8 @@ COPY --from=k32w /opt/sdk /opt/k32w COPY --from=rw61x /opt/sdk /opt/nxp-sdk +COPY --from=nxp /opt/nxp /opt/nxp + COPY --from=nxpzephyr /opt/nxp-zephyr/zephyr-sdk-0.16.5/ /opt/nxp-zephyr/zephyr-sdk-0.16.5/ COPY --from=nxpzephyr /opt/nxp-zephyr/zephyrproject/ /opt/nxp-zephyr/zephyrproject/ @@ -139,6 +142,7 @@ ENV ZEPHYR_SDK_INSTALL_DIR=/opt/NordicSemiconductor/nRF5_tools/zephyr-sdk-0.16.5 ENV ZEPHYR_TOOLCHAIN_VARIANT=gnuarmemb ENV ZEPHYR_NXP_BASE=/opt/nxp-zephyr/zephyrproject/zephyr ENV ZEPHYR_NXP_SDK_INSTALL_DIR=/opt/nxp-zephyr/zephyr-sdk-0.16.5 +ENV NXP_UPDATE_SDK_SCRIPT_DOCKER=/opt/nxp/nxp_matter_support/scripts/update_nxp_sdk.py ENV TIZEN_VERSION 7.0 ENV TIZEN_SDK_ROOT /opt/tizen-sdk diff --git a/scripts/build_python.sh b/scripts/build_python.sh index e70b220130935d..9bb919605e229c 100755 --- a/scripts/build_python.sh +++ b/scripts/build_python.sh @@ -39,7 +39,6 @@ OUTPUT_ROOT="$CHIP_ROOT/out/python_lib" declare enable_ble=true declare chip_detail_logging=false -declare enable_pybindings=false declare chip_mdns declare case_retry_delta declare install_virtual_env @@ -49,7 +48,7 @@ declare install_jupyterlab=no help() { - echo "Usage: $file_name [ options ... ] [ -chip_detail_logging ChipDetailLoggingValue ] [ -chip_mdns ChipMDNSValue ] [-enable_pybindings EnableValue]" + echo "Usage: $file_name [ options ... ] [ -chip_detail_logging ChipDetailLoggingValue ] [ -chip_mdns ChipMDNSValue ]" echo "General Options: -h, --help Display this information. @@ -59,8 +58,6 @@ Input Options: By default it is false. -m, --chip_mdns ChipMDNSValue Specify ChipMDNSValue as platform or minimal. By default it is minimal. - -p, --enable_pybindings Specify whether to enable pybindings as python controller. - -t --time_between_case_retries MRPActiveRetryInterval Specify MRPActiveRetryInterval value Default is 300 ms -i, --install_virtual_env Create a virtual environment with the wheels installed @@ -104,14 +101,6 @@ while (($#)); do chip_mdns=$2 shift ;; - --enable_pybindings | -p) - enable_pybindings=$2 - if [[ "$enable_pybindings" != "true" && "$enable_pybindings" != "false" ]]; then - echo "enable_pybindings should have a true/false value, not '$enable_pybindings'" - exit - fi - shift - ;; --time_between_case_retries | -t) chip_case_retry_delta=$2 shift @@ -157,7 +146,7 @@ while (($#)); do done # Print input values -echo "Input values: chip_detail_logging = $chip_detail_logging , chip_mdns = \"$chip_mdns\", enable_pybindings = $enable_pybindings, chip_case_retry_delta=\"$chip_case_retry_delta\", pregen_dir=\"$pregen_dir\", enable_ble=\"$enable_ble\"" +echo "Input values: chip_detail_logging = $chip_detail_logging , chip_mdns = \"$chip_mdns\", chip_case_retry_delta=\"$chip_case_retry_delta\", pregen_dir=\"$pregen_dir\", enable_ble=\"$enable_ble\"" # Ensure we have a compilation environment source "$CHIP_ROOT/scripts/activate.sh" @@ -184,7 +173,7 @@ export SYSTEM_VERSION_COMPAT=0 # Make all possible human redable tracing available. tracing_options="matter_log_json_payload_hex=true matter_log_json_payload_decode_full=true matter_enable_tracing_support=true" -gn --root="$CHIP_ROOT" gen "$OUTPUT_ROOT" --args="$tracing_options chip_detail_logging=$chip_detail_logging enable_pylib=$enable_pybindings enable_rtti=$enable_pybindings chip_project_config_include_dirs=[\"//config/python\"] $chip_mdns_arg $chip_case_retry_arg $pregen_dir_arg chip_config_network_layer_ble=$enable_ble chip_enable_ble=$enable_ble chip_crypto=\"boringssl\"" +gn --root="$CHIP_ROOT" gen "$OUTPUT_ROOT" --args="$tracing_options chip_detail_logging=$chip_detail_logging chip_project_config_include_dirs=[\"//config/python\"] $chip_mdns_arg $chip_case_retry_arg $pregen_dir_arg chip_config_network_layer_ble=$enable_ble chip_enable_ble=$enable_ble chip_crypto=\"boringssl\"" function ninja_target() { # Print the ninja target required to build a gn label. @@ -206,11 +195,7 @@ function wheel_output_dir() { ninja -C "$OUTPUT_ROOT" python_wheels # Add wheels from chip_python_wheel_action templates. -if [ "$enable_pybindings" == true ]; then - WHEEL=("$OUTPUT_ROOT"/pybindings/pycontroller/pychip-*.whl) -else - WHEEL=("$OUTPUT_ROOT"/controller/python/chip*.whl) -fi +WHEEL=("$OUTPUT_ROOT"/controller/python/chip*.whl) # Add the matter_testing_infrastructure wheel WHEEL+=("$OUTPUT_ROOT"/python/obj/src/python_testing/matter_testing_infrastructure/metadata_parser._build_wheel/metadata_parser-*.whl) diff --git a/scripts/build_python_device.sh b/scripts/build_python_device.sh index 894ea1bad61c8b..9b21d28954c409 100755 --- a/scripts/build_python_device.sh +++ b/scripts/build_python_device.sh @@ -39,12 +39,11 @@ OUTPUT_ROOT="$CHIP_ROOT/out/python_lib" ENVIRONMENT_ROOT="$CHIP_ROOT/out/python_env" declare chip_detail_logging=false -declare enable_pybindings=false declare chip_mdns help() { - echo "Usage: $file_name [ options ... ] [ -chip_detail_logging ChipDetailLoggingValue ] [ -chip_mdns ChipMDNSValue ] [-enable_pybindings EnableValue]" + echo "Usage: $file_name [ options ... ] [ -chip_detail_logging ChipDetailLoggingValue ] [ -chip_mdns ChipMDNSValue ]" echo "General Options: -h, --help Display this information. @@ -53,7 +52,6 @@ Input Options: By default it is false. -m, --chip_mdns ChipMDNSValue Specify ChipMDNSValue as platform or minimal. By default it is minimal. - -p, --enable_pybindings EnableValue Specify whether to enable pybindings as python controller. " } @@ -73,10 +71,6 @@ while (($#)); do chip_mdns=$2 shift ;; - --enable_pybindings | -p) - enable_pybindings=$2 - shift - ;; -*) help echo "Unknown Option \"$1\"" @@ -87,7 +81,7 @@ while (($#)); do done # Print input values -echo "Input values: chip_detail_logging = $chip_detail_logging , chip_mdns = \"$chip_mdns\", enable_pybindings = $enable_pybindings" +echo "Input values: chip_detail_logging = $chip_detail_logging , chip_mdns = \"$chip_mdns\"" # Ensure we have a compilation environment source "$CHIP_ROOT/scripts/activate.sh" @@ -97,26 +91,17 @@ source "$CHIP_ROOT/scripts/activate.sh" chip_data_model_arg="chip_data_model=\"///examples/lighting-app/lighting-common\"" -gn --root="$CHIP_ROOT" gen "$OUTPUT_ROOT" --args="chip_detail_logging=$chip_detail_logging enable_pylib=$enable_pybindings enable_rtti=$enable_pybindings $chip_mdns_arg chip_controller=false $chip_data_model_arg" +gn --root="$CHIP_ROOT" gen "$OUTPUT_ROOT" --args="chip_detail_logging=$chip_detail_logging $chip_mdns_arg chip_controller=false $chip_data_model_arg" # Compiles python files -# Check pybindings was requested -if [ "$enable_pybindings" == true ]; then - ninja -v -C "$OUTPUT_ROOT" pycontroller -else - ninja -v -C "$OUTPUT_ROOT" chip-core -fi +ninja -v -C "$OUTPUT_ROOT" chip-core # Create a virtual environment that has access to the built python tools virtualenv --clear "$ENVIRONMENT_ROOT" # Activate the new environment to register the python WHL -if [ "$enable_pybindings" == true ]; then - WHEEL=("$OUTPUT_ROOT"/pybindings/pycontroller/pychip-*.whl) -else - WHEEL=("$OUTPUT_ROOT"/controller/python/chip_core*.whl) -fi +WHEEL=("$OUTPUT_ROOT"/controller/python/chip_core*.whl) source "$ENVIRONMENT_ROOT"/bin/activate "$ENVIRONMENT_ROOT"/bin/python -m pip install --upgrade pip diff --git a/scripts/py_matter_idl/BUILD.gn b/scripts/py_matter_idl/BUILD.gn index 13624f99d112b6..29e6becb4eba77 100644 --- a/scripts/py_matter_idl/BUILD.gn +++ b/scripts/py_matter_idl/BUILD.gn @@ -31,6 +31,9 @@ pw_python_package("matter_idl") { # Dependency grammar "matter_idl/matter_grammar.lark", + #marker file to indicate to mypy that matter_idl is type-annotated + "matter_idl/py.typed", + # Unit test data "matter_idl/tests/available_tests.yaml", "matter_idl/tests/inputs/cluster_struct_attribute.matter", diff --git a/scripts/py_matter_idl/matter_idl/py.typed b/scripts/py_matter_idl/matter_idl/py.typed new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/scripts/py_matter_idl/setup.cfg b/scripts/py_matter_idl/setup.cfg index 3eb3510aeda209..6fed9ed20f6523 100644 --- a/scripts/py_matter_idl/setup.cfg +++ b/scripts/py_matter_idl/setup.cfg @@ -39,3 +39,4 @@ matter_idl = generators/java/ClusterIDMapping.jinja generators/java/ClusterReadMapping.jinja generators/java/ClusterWriteMapping.jinja + py.typed diff --git a/scripts/py_matter_yamltests/BUILD.gn b/scripts/py_matter_yamltests/BUILD.gn index f8fa027672b868..604c6a41b19382 100644 --- a/scripts/py_matter_yamltests/BUILD.gn +++ b/scripts/py_matter_yamltests/BUILD.gn @@ -43,6 +43,7 @@ pw_python_package("matter_yamltests") { "matter_yamltests/pseudo_clusters/clusters/system_commands.py", "matter_yamltests/pseudo_clusters/pseudo_cluster.py", "matter_yamltests/pseudo_clusters/pseudo_clusters.py", + "matter_yamltests/py.typed", "matter_yamltests/runner.py", "matter_yamltests/websocket_runner.py", "matter_yamltests/yaml_loader.py", diff --git a/scripts/py_matter_yamltests/matter_yamltests/py.typed b/scripts/py_matter_yamltests/matter_yamltests/py.typed new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/scripts/py_matter_yamltests/setup.cfg b/scripts/py_matter_yamltests/setup.cfg index a3c7983e7299cd..fa1479e44f7449 100644 --- a/scripts/py_matter_yamltests/setup.cfg +++ b/scripts/py_matter_yamltests/setup.cfg @@ -25,3 +25,7 @@ install_requires= diskcache lark websockets + +[options.package_data] +matter_yamltests = + py.typed \ No newline at end of file diff --git a/scripts/setup/constraints.txt b/scripts/setup/constraints.txt index a5af1b462fb335..3f396a83c7ac68 100644 --- a/scripts/setup/constraints.txt +++ b/scripts/setup/constraints.txt @@ -273,7 +273,7 @@ wcwidth==0.2.6 # via prompt-toolkit websockets==10.4 # via -r requirements.all.txt -west==1.0.0 +west==1.2.0 # via -r requirements.zephyr.txt wheel==0.38.4 ; sys_platform == "linux" # via diff --git a/scripts/tests/requirements.txt b/scripts/tests/requirements.txt index d3c26e9cba23c6..f942e0f542b63f 100644 --- a/scripts/tests/requirements.txt +++ b/scripts/tests/requirements.txt @@ -3,3 +3,4 @@ click colorama diskcache websockets +mypy==1.10.1 \ No newline at end of file diff --git a/scripts/tools/check_includes_config.py b/scripts/tools/check_includes_config.py index 8790faf1666b08..2af375d7c4bdfd 100644 --- a/scripts/tools/check_includes_config.py +++ b/scripts/tools/check_includes_config.py @@ -29,7 +29,6 @@ '/java/', '/Jni', '/mock/', - '/pybindings/', '/python/', '/Test', '/tests/', diff --git a/scripts/tools/generate_esp32_chip_factory_bin.py b/scripts/tools/generate_esp32_chip_factory_bin.py index b19748ca31b197..892f0609927cd2 100755 --- a/scripts/tools/generate_esp32_chip_factory_bin.py +++ b/scripts/tools/generate_esp32_chip_factory_bin.py @@ -276,6 +276,7 @@ def validate_args(args): check_int_range(args.product_id, 0x0000, 0xFFFF, 'Product id') check_int_range(args.vendor_id, 0x0000, 0xFFFF, 'Vendor id') check_int_range(args.hw_ver, 0x0000, 0xFFFF, 'Hardware version') + check_int_range(args.discovery_mode, 0b000, 0b111, 'Discovery-Mode') check_str_range(args.serial_num, 1, 32, 'Serial number') check_str_range(args.vendor_name, 1, 32, 'Vendor name') @@ -566,9 +567,10 @@ def any_base_int(s): return int(s, 0) parser.add_argument('-cf', '--commissioning-flow', type=any_base_int, default=0, help='Device commissioning flow, 0:Standard, 1:User-Intent, 2:Custom. \ Default is 0.', choices=[0, 1, 2]) - parser.add_argument('-dm', '--discovery-mode', type=any_base_int, default=1, - help='Commissionable device discovery networking technology. \ - 0:WiFi-SoftAP, 1:BLE, 2:On-network. Default is BLE.', choices=[0, 1, 2]) + parser.add_argument('-dm', '--discovery-mode', type=any_base_int, default=2, + help='3-bit bitmap representing discovery modes for commissionable device discovery \ + Bit 0:WiFi-SoftAP, Bit 1:BLE, Bit 2:On-network. Default is BLE. Specify values between 0-7') + parser.set_defaults(generate_bin=True) return parser.parse_args() diff --git a/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h b/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h index bb4e1a77c14f59..3146dd902ffb68 100644 --- a/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h +++ b/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h @@ -2217,6 +2217,7 @@ }; \ const EmberAfGenericClusterFunction chipFuncArrayThermostatServer[] = { \ (EmberAfGenericClusterFunction) emberAfThermostatClusterServerInitCallback, \ + (EmberAfGenericClusterFunction) MatterThermostatClusterServerShutdownCallback, \ (EmberAfGenericClusterFunction) MatterThermostatClusterServerPreAttributeChangedCallback, \ }; \ const EmberAfGenericClusterFunction chipFuncArrayFanControlServer[] = { \ @@ -3755,7 +3756,7 @@ .attributes = ZAP_ATTRIBUTE_INDEX(616), \ .attributeCount = 26, \ .clusterSize = 72, \ - .mask = ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION) | ZAP_CLUSTER_MASK(PRE_ATTRIBUTE_CHANGED_FUNCTION), \ + .mask = ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION) | ZAP_CLUSTER_MASK(SHUTDOWN_FUNCTION) | ZAP_CLUSTER_MASK(PRE_ATTRIBUTE_CHANGED_FUNCTION), \ .functions = chipFuncArrayThermostatServer, \ .acceptedCommandList = ZAP_GENERATED_COMMANDS_INDEX( 241 ), \ .generatedCommandList = ZAP_GENERATED_COMMANDS_INDEX( 246 ), \ diff --git a/src/access/AccessControl.cpp b/src/access/AccessControl.cpp index 70921599fca84a..fcb5a43d975f8e 100644 --- a/src/access/AccessControl.cpp +++ b/src/access/AccessControl.cpp @@ -171,6 +171,23 @@ char GetPrivilegeStringForLogging(Privilege privilege) return 'u'; } +char GetRequestTypeStringForLogging(RequestType requestType) +{ + switch (requestType) + { + case RequestType::kAttributeReadRequest: + return 'r'; + case RequestType::kAttributeWriteRequest: + return 'w'; + case RequestType::kCommandInvokeRequest: + return 'i'; + case RequestType::kEventReadOrSubscribeRequest: + return 'e'; + default: + return '?'; + } +} + #endif // CHIP_PROGRESS_LOGGING && CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 1 } // namespace @@ -306,6 +323,11 @@ void AccessControl::RemoveEntryListener(EntryListener & listener) } } +bool AccessControl::IsAccessRestrictionListSupported() const +{ + return false; // not yet supported +} + CHIP_ERROR AccessControl::Check(const SubjectDescriptor & subjectDescriptor, const RequestPath & requestPath, Privilege requestPrivilege) { @@ -316,14 +338,20 @@ CHIP_ERROR AccessControl::Check(const SubjectDescriptor & subjectDescriptor, con constexpr size_t kMaxCatsToLog = 6; char catLogBuf[kMaxCatsToLog * kCharsPerCatForLogging]; ChipLogProgress(DataManagement, - "AccessControl: checking f=%u a=%c s=0x" ChipLogFormatX64 " t=%s c=" ChipLogFormatMEI " e=%u p=%c", + "AccessControl: checking f=%u a=%c s=0x" ChipLogFormatX64 " t=%s c=" ChipLogFormatMEI " e=%u p=%c r=%c", subjectDescriptor.fabricIndex, GetAuthModeStringForLogging(subjectDescriptor.authMode), ChipLogValueX64(subjectDescriptor.subject), GetCatStringForLogging(catLogBuf, sizeof(catLogBuf), subjectDescriptor.cats), - ChipLogValueMEI(requestPath.cluster), requestPath.endpoint, GetPrivilegeStringForLogging(requestPrivilege)); + ChipLogValueMEI(requestPath.cluster), requestPath.endpoint, GetPrivilegeStringForLogging(requestPrivilege), + GetRequestTypeStringForLogging(requestPath.requestType)); } #endif // CHIP_PROGRESS_LOGGING && CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 1 + if (IsAccessRestrictionListSupported()) + { + VerifyOrReturnError(requestPath.requestType != RequestType::kRequestTypeUnknown, CHIP_ERROR_INVALID_ARGUMENT); + } + { CHIP_ERROR result = mDelegate->Check(subjectDescriptor, requestPath, requestPrivilege); if (result != CHIP_ERROR_NOT_IMPLEMENTED) diff --git a/src/access/AccessControl.h b/src/access/AccessControl.h index 6186c33d44597a..a7c3472f5d99b4 100644 --- a/src/access/AccessControl.h +++ b/src/access/AccessControl.h @@ -627,6 +627,13 @@ class AccessControl // Removes a listener from the listener list, if in the list. void RemoveEntryListener(EntryListener & listener); + /** + * Check whether or not Access Restriction List is supported. + * + * @retval true if Access Restriction List is supported. + */ + bool IsAccessRestrictionListSupported() const; + /** * Check whether access (by a subject descriptor, to a request path, * requiring a privilege) should be allowed or denied. diff --git a/src/access/RequestPath.h b/src/access/RequestPath.h index 966f27afe8f716..af791d73eb151d 100644 --- a/src/access/RequestPath.h +++ b/src/access/RequestPath.h @@ -19,15 +19,29 @@ #pragma once #include +#include namespace chip { namespace Access { +enum class RequestType : uint8_t +{ + kRequestTypeUnknown, + kAttributeReadRequest, + kAttributeWriteRequest, + kCommandInvokeRequest, + kEventReadOrSubscribeRequest +}; + struct RequestPath { // NOTE: eventually this will likely also contain node, for proxying - ClusterId cluster = 0; - EndpointId endpoint = 0; + ClusterId cluster = 0; + EndpointId endpoint = 0; + RequestType requestType = RequestType::kRequestTypeUnknown; + + // entityId represents an attribute, command, or event ID, which is determined by the requestType. Wildcard if omitted. + std::optional entityId; }; } // namespace Access diff --git a/src/app/CommandHandlerImpl.cpp b/src/app/CommandHandlerImpl.cpp index b1843d23e8b0cd..1945e7e5e69dc3 100644 --- a/src/app/CommandHandlerImpl.cpp +++ b/src/app/CommandHandlerImpl.cpp @@ -402,7 +402,10 @@ Status CommandHandlerImpl::ProcessCommandDataIB(CommandDataIB::Parser & aCommand { Access::SubjectDescriptor subjectDescriptor = GetSubjectDescriptor(); - Access::RequestPath requestPath{ .cluster = concretePath.mClusterId, .endpoint = concretePath.mEndpointId }; + Access::RequestPath requestPath{ .cluster = concretePath.mClusterId, + .endpoint = concretePath.mEndpointId, + .requestType = Access::RequestType::kCommandInvokeRequest, + .entityId = concretePath.mCommandId }; Access::Privilege requestPrivilege = RequiredPrivilege::ForInvokeCommand(concretePath); err = Access::GetAccessControl().Check(subjectDescriptor, requestPath, requestPrivilege); if (err != CHIP_NO_ERROR) @@ -548,7 +551,10 @@ Status CommandHandlerImpl::ProcessGroupCommandDataIB(CommandDataIB::Parser & aCo { Access::SubjectDescriptor subjectDescriptor = GetSubjectDescriptor(); - Access::RequestPath requestPath{ .cluster = concretePath.mClusterId, .endpoint = concretePath.mEndpointId }; + Access::RequestPath requestPath{ .cluster = concretePath.mClusterId, + .endpoint = concretePath.mEndpointId, + .requestType = Access::RequestType::kCommandInvokeRequest, + .entityId = concretePath.mCommandId }; Access::Privilege requestPrivilege = RequiredPrivilege::ForInvokeCommand(concretePath); err = Access::GetAccessControl().Check(subjectDescriptor, requestPath, requestPrivilege); if (err != CHIP_NO_ERROR) diff --git a/src/app/EventManagement.cpp b/src/app/EventManagement.cpp index 8e6d53c24c9636..4c5faab3cdcda4 100644 --- a/src/app/EventManagement.cpp +++ b/src/app/EventManagement.cpp @@ -554,7 +554,10 @@ CHIP_ERROR EventManagement::CheckEventContext(EventLoadOutContext * eventLoadOut ReturnErrorOnFailure(ret); - Access::RequestPath requestPath{ .cluster = event.mClusterId, .endpoint = event.mEndpointId }; + Access::RequestPath requestPath{ .cluster = event.mClusterId, + .endpoint = event.mEndpointId, + .requestType = Access::RequestType::kEventReadOrSubscribeRequest, + .entityId = event.mEventId }; Access::Privilege requestPrivilege = RequiredPrivilege::ForReadEvent(path); CHIP_ERROR accessControlError = Access::GetAccessControl().Check(eventLoadOutContext->mSubjectDescriptor, requestPath, requestPrivilege); diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index 80312a57873f43..64d30bc6c0e42f 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -494,7 +494,10 @@ CHIP_ERROR InteractionModelEngine::ParseAttributePaths(const Access::SubjectDesc // AttributePathExpandIterator. So we just need to check the ACL bits. for (; pathIterator.Get(readPath); pathIterator.Next()) { - Access::RequestPath requestPath{ .cluster = readPath.mClusterId, .endpoint = readPath.mEndpointId }; + // leave requestPath.entityId optional value unset to indicate wildcard + Access::RequestPath requestPath{ .cluster = readPath.mClusterId, + .endpoint = readPath.mEndpointId, + .requestType = Access::RequestType::kAttributeReadRequest }; err = Access::GetAccessControl().Check(aSubjectDescriptor, requestPath, RequiredPrivilege::ForReadAttribute(readPath)); if (err == CHIP_NO_ERROR) @@ -510,7 +513,10 @@ CHIP_ERROR InteractionModelEngine::ParseAttributePaths(const Access::SubjectDesc paramsList.mValue.mAttributeId); if (ConcreteAttributePathExists(concretePath)) { - Access::RequestPath requestPath{ .cluster = concretePath.mClusterId, .endpoint = concretePath.mEndpointId }; + Access::RequestPath requestPath{ .cluster = concretePath.mClusterId, + .endpoint = concretePath.mEndpointId, + .requestType = Access::RequestType::kAttributeReadRequest, + .entityId = paramsList.mValue.mAttributeId }; err = Access::GetAccessControl().Check(aSubjectDescriptor, requestPath, RequiredPrivilege::ForReadAttribute(concretePath)); @@ -532,17 +538,27 @@ CHIP_ERROR InteractionModelEngine::ParseAttributePaths(const Access::SubjectDesc return err; } -static bool CanAccess(const Access::SubjectDescriptor & aSubjectDescriptor, const ConcreteClusterPath & aPath, - Access::Privilege aNeededPrivilege) +#if !CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE +static bool CanAccessEvent(const Access::SubjectDescriptor & aSubjectDescriptor, const ConcreteClusterPath & aPath, + Access::Privilege aNeededPrivilege) { - Access::RequestPath requestPath{ .cluster = aPath.mClusterId, .endpoint = aPath.mEndpointId }; + Access::RequestPath requestPath{ .cluster = aPath.mClusterId, + .endpoint = aPath.mEndpointId, + .requestType = Access::RequestType::kEventReadOrSubscribeRequest }; + // leave requestPath.entityId optional value unset to indicate wildcard CHIP_ERROR err = Access::GetAccessControl().Check(aSubjectDescriptor, requestPath, aNeededPrivilege); return (err == CHIP_NO_ERROR); } +#endif -static bool CanAccess(const Access::SubjectDescriptor & aSubjectDescriptor, const ConcreteEventPath & aPath) +static bool CanAccessEvent(const Access::SubjectDescriptor & aSubjectDescriptor, const ConcreteEventPath & aPath) { - return CanAccess(aSubjectDescriptor, aPath, RequiredPrivilege::ForReadEvent(aPath)); + Access::RequestPath requestPath{ .cluster = aPath.mClusterId, + .endpoint = aPath.mEndpointId, + .requestType = Access::RequestType::kEventReadOrSubscribeRequest, + .entityId = aPath.mEventId }; + CHIP_ERROR err = Access::GetAccessControl().Check(aSubjectDescriptor, requestPath, RequiredPrivilege::ForReadEvent(aPath)); + return (err == CHIP_NO_ERROR); } /** @@ -559,7 +575,7 @@ static bool HasValidEventPathForEndpointAndCluster(EndpointId aEndpoint, const E { ConcreteEventPath path(aEndpoint, aCluster->clusterId, aCluster->eventList[idx]); // If we get here, the path exists. We just have to do an ACL check for it. - bool isValid = CanAccess(aSubjectDescriptor, path); + bool isValid = CanAccessEvent(aSubjectDescriptor, path); if (isValid) { return true; @@ -571,7 +587,7 @@ static bool HasValidEventPathForEndpointAndCluster(EndpointId aEndpoint, const E // We have no way to expand wildcards. Just assume that we would need // View permissions for whatever events are involved. ConcreteClusterPath clusterPath(aEndpoint, aCluster->clusterId); - return CanAccess(aSubjectDescriptor, clusterPath, Access::Privilege::kView); + return CanAccessEvent(aSubjectDescriptor, clusterPath, Access::Privilege::kView); #endif } @@ -581,7 +597,7 @@ static bool HasValidEventPathForEndpointAndCluster(EndpointId aEndpoint, const E // Not an existing event path. return false; } - return CanAccess(aSubjectDescriptor, path); + return CanAccessEvent(aSubjectDescriptor, path); } /** diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp index 98616f9e0e281b..25f6153959a22e 100644 --- a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp @@ -236,15 +236,15 @@ bool emberAfCommissionerControlClusterCommissionNodeCallback( // Set IP address and port in the CommissionNodeInfo struct commissionNodeInfo->port = commandData.port; err = commissionNodeInfo->ipAddress.SetIPAddress(commandData.ipAddress); - SuccessOrExit(err == CHIP_NO_ERROR); + SuccessOrExit(err); // Validate the commission node command. err = delegate->ValidateCommissionNodeCommand(sourceNodeId, requestId); - SuccessOrExit(err == CHIP_NO_ERROR); + SuccessOrExit(err); // Populate the parameters for the commissioning window err = delegate->GetCommissioningWindowParams(commissionNodeInfo->params); - SuccessOrExit(err == CHIP_NO_ERROR); + SuccessOrExit(err); // Add the response for the commissioning window. AddReverseOpenCommissioningWindowResponse(commandObj, commandPath, commissionNodeInfo->params); diff --git a/src/app/clusters/icd-management-server/icd-management-server.cpp b/src/app/clusters/icd-management-server/icd-management-server.cpp index 8445cad117c2a3..c2625b050d4661 100644 --- a/src/app/clusters/icd-management-server/icd-management-server.cpp +++ b/src/app/clusters/icd-management-server/icd-management-server.cpp @@ -240,7 +240,10 @@ CHIP_ERROR IcdManagementAttributeAccess::ReadMaximumCheckInBackOff(EndpointId en */ CHIP_ERROR CheckAdmin(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, bool & isClientAdmin) { - RequestPath requestPath{ .cluster = commandPath.mClusterId, .endpoint = commandPath.mEndpointId }; + RequestPath requestPath{ .cluster = commandPath.mClusterId, + .endpoint = commandPath.mEndpointId, + .requestType = RequestType::kCommandInvokeRequest, + .entityId = commandPath.mCommandId }; CHIP_ERROR err = GetAccessControl().Check(commandObj->GetSubjectDescriptor(), requestPath, Privilege::kAdminister); if (CHIP_NO_ERROR == err) { diff --git a/src/app/clusters/service-area-server/service-area-delegate.h b/src/app/clusters/service-area-server/service-area-delegate.h index e31abf98f9e745..01df2faeb06488 100644 --- a/src/app/clusters/service-area-server/service-area-delegate.h +++ b/src/app/clusters/service-area-server/service-area-delegate.h @@ -72,7 +72,7 @@ class Delegate * @note The statusText field SHOULD indicate why the request is not allowed, given the current mode * of the device, which may involve other clusters. */ - virtual bool IsSetSelectedAreasAllowed(MutableCharSpan statusText) = 0; + virtual bool IsSetSelectedAreasAllowed(MutableCharSpan & statusText) = 0; /** * Given a set of locations to be set to the SelectedAreas attribute, this method should check that @@ -92,7 +92,7 @@ class Delegate * device must stop. */ virtual bool IsValidSelectAreasSet(const Commands::SelectAreas::DecodableType & req, SelectAreasStatus & locationStatus, - MutableCharSpan statusText) = 0; + MutableCharSpan & statusText) = 0; /** * @brief The server instance ensures that the SelectedAreas and CurrentArea attributes are not null before @@ -104,24 +104,23 @@ class Delegate * * @note skipStatusText must be filled out by the function on failure. * - * @note If the device successfully accepts the request and the ListOrder feature is set to 1: - * The server SHALL stop operating at the current location. - * The server SHALL attempt to operate at the remaining locations on the SelectedAreas attribute list, starting with - * the next entry. If the end of the SelectedAreas attribute list is reached, the server SHALL stop operating. - * - * @note If the device successfully accepts the request and the ListOrder feature is set to 0: - * The server SHALL stop operating at the current location. - * The server SHALL attempt to operate at the locations on the SelectedAreas attribute list where operating has not - * been completed, using a vendor defined order. If the server has completed operating at all locations on the SelectedAreas - * attribute list, the server SHALL stop operating. + * @note If the device accepts the request: + * - If the device is currently operating at the area identified by SkippedArea, as indicated by either the CurrentArea or + * the Progress attributes, if implemented, the device SHALL stop operating at that area. + * - If the Progress attribute is implemented, the entry corresponding to SkippedArea SHALL be updated to indicate that the + * area was skipped. + * - The server SHALL attempt to operate only at the areas in the SelectedAreas attribute list where operating has not been + * skipped or completed, using a vendor defined order. + * - If the server has either skipped or completed operating at all areas on the SelectedAreas attribute list, the server + * SHALL stop operating. * * @note If the Status field is set to InvalidAreaList, the StatusText field SHALL be an empty string. * If the Status field is not set to Success, or InvalidAreaList, the StatusText field SHALL include a vendor defined - * error description which can be used to explain the error to the user. For example, if the Status field is set to - * InvalidInMode, the StatusText field SHOULD indicate why the request is not allowed, given the current mode of the device, - * which may involve other clusters. + * error description which can be used to explain the error to the user. For example, if the Status field is set to + * InvalidInMode, the StatusText field SHOULD indicate why the request is not allowed, given the current mode of the + * device, which may involve other clusters. */ - virtual bool HandleSkipCurrentArea(uint32_t skippedArea, MutableCharSpan skipStatusText) + virtual bool HandleSkipCurrentArea(uint32_t skippedArea, MutableCharSpan & skipStatusText) { // device support of this command is optional CopyCharSpanToMutableCharSpan("Skip Current Area command not supported by device"_span, skipStatusText); @@ -262,7 +261,7 @@ class Delegate /** * This method is called by the server instance to modify an existing map in the list. * The server instance will ensure that the modifiedMap is a valid, unique map. - * @param[in] listIndexThe index of the map being modified. + * @param[in] listIndex The index of the map being modified. * @param[in] modifiedMapA map with the modified contents. * @return true if successful, false otherwise. * diff --git a/src/app/clusters/service-area-server/service-area-server.cpp b/src/app/clusters/service-area-server/service-area-server.cpp index d745c04b5d468e..0e562c78754d5b 100644 --- a/src/app/clusters/service-area-server/service-area-server.cpp +++ b/src/app/clusters/service-area-server/service-area-server.cpp @@ -399,6 +399,8 @@ void Instance::HandleSkipCurrentAreaCmd(HandlerContext & ctx, const Commands::Sk exitResponse(SkipAreaStatus::kInvalidInMode, skipStatusText); return; } + + exitResponse(SkipAreaStatus::kSuccess, ""_span); } //************************************************************************* @@ -469,35 +471,28 @@ bool Instance::IsValidSupportedArea(const AreaStructureWrapper & aArea) } // The mapID field SHALL be null if SupportedMaps is not supported or SupportedMaps is an empty list. - bool shouldMapsBeNull = false; - if (mFeature.Has(Feature::kMaps)) + if (mFeature.Has(Feature::kMaps) && (mDelegate->GetNumberOfSupportedMaps() > 0)) { - if (mDelegate->GetNumberOfSupportedMaps() == 0) + if (aArea.mapID.IsNull()) { - shouldMapsBeNull = true; + ChipLogDetail(Zcl, "IsValidSupportedArea %u - map Id should not be null when there are supported maps", aArea.areaID); + return false; } - } - else - { - shouldMapsBeNull = true; - } - if (shouldMapsBeNull) - { - if (!aArea.mapID.IsNull()) + // If the SupportedMaps attribute is not null, mapID SHALL be the ID of an entry from the SupportedMaps attribute. + if (!IsSupportedMap(aArea.mapID.Value())) { - ChipLogDetail(Zcl, "IsValidSupportedArea %u - map Id %u is not in empty supported map list", aArea.areaID, - aArea.mapID.Value()); + ChipLogError(Zcl, "IsValidSupportedArea %u - map Id %u is not in supported map list", aArea.areaID, + aArea.mapID.Value()); return false; } } else { - // If the SupportedMaps attribute is not null, mapID SHALL be the ID of an entry from the SupportedMaps attribute. - if (!IsSupportedMap(aArea.mapID.Value())) + if (!aArea.mapID.IsNull()) { - ChipLogError(Zcl, "IsValidSupportedArea %u - map Id %u is not in supported map list", aArea.areaID, - aArea.mapID.Value()); + ChipLogDetail(Zcl, "IsValidSupportedArea %u - map Id %u is not in empty supported map list", aArea.areaID, + aArea.mapID.Value()); return false; } } @@ -1005,7 +1000,7 @@ bool Instance::SetProgressStatus(uint32_t aAreaId, OperationalStatusEnum opStatu // TotalOperationalTime SHALL be null if the Status field is not set to Completed or Skipped. if ((opStatus != OperationalStatusEnum::kCompleted) && (opStatus != OperationalStatusEnum::kSkipped)) { - progressElement.totalOperationalTime.Value().SetNull(); + progressElement.totalOperationalTime.Emplace(DataModel::NullNullable); } // add the updated element to the progress attribute diff --git a/src/app/clusters/thermostat-server/thermostat-delegate.h b/src/app/clusters/thermostat-server/thermostat-delegate.h index 0c09b9dd4d70fc..c8c21d898af167 100644 --- a/src/app/clusters/thermostat-server/thermostat-delegate.h +++ b/src/app/clusters/thermostat-server/thermostat-delegate.h @@ -38,6 +38,17 @@ class Delegate virtual ~Delegate() = default; + /** + * @brief Get the maximum timeout for atomically writing to a set of attributes + * + * @param[in] attributeRequests The list of attributes to write to. + * @param[out] timeoutRequest The timeout proposed by the client. + * @return The maximum allowed timeout; zero if the request is invalid. + */ + virtual std::optional + GetAtomicWriteTimeout(DataModel::DecodableList attributeRequests, + System::Clock::Milliseconds16 timeoutRequest) = 0; + /** * @brief Get the preset type at a given index in the PresetTypes attribute * diff --git a/src/app/clusters/thermostat-server/thermostat-server.cpp b/src/app/clusters/thermostat-server/thermostat-server.cpp index 6b8a50a6ffe7e0..91c045c5ff540e 100644 --- a/src/app/clusters/thermostat-server/thermostat-server.cpp +++ b/src/app/clusters/thermostat-server/thermostat-server.cpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include @@ -116,8 +118,7 @@ void TimerExpiredCallback(System::Layer * systemLayer, void * callbackContext) VerifyOrReturn(delegate != nullptr, ChipLogError(Zcl, "Delegate is null. Unable to handle timer expired")); delegate->ClearPendingPresetList(); - gThermostatAttrAccess.SetAtomicWrite(endpoint, false); - gThermostatAttrAccess.SetAtomicWriteScopedNodeId(endpoint, ScopedNodeId()); + gThermostatAttrAccess.SetAtomicWrite(endpoint, ScopedNodeId(), kAtomicWriteState_Closed); } /** @@ -205,8 +206,7 @@ void resetAtomicWrite(Delegate * delegate, EndpointId endpoint) delegate->ClearPendingPresetList(); } ClearTimer(endpoint); - gThermostatAttrAccess.SetAtomicWrite(endpoint, false); - gThermostatAttrAccess.SetAtomicWriteScopedNodeId(endpoint, ScopedNodeId()); + gThermostatAttrAccess.SetAtomicWrite(endpoint, ScopedNodeId(), kAtomicWriteState_Closed); } /** @@ -605,14 +605,16 @@ void SetDefaultDelegate(EndpointId endpoint, Delegate * delegate) } } -void ThermostatAttrAccess::SetAtomicWrite(EndpointId endpoint, bool inProgress) +void ThermostatAttrAccess::SetAtomicWrite(EndpointId endpoint, ScopedNodeId originatorNodeId, AtomicWriteState state) { uint16_t ep = emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); - if (ep < ArraySize(mAtomicWriteState)) + if (ep < ArraySize(mAtomicWriteSessions)) { - mAtomicWriteState[ep] = inProgress; + mAtomicWriteSessions[ep].state = state; + mAtomicWriteSessions[ep].endpointId = endpoint; + mAtomicWriteSessions[ep].nodeId = originatorNodeId; } } @@ -622,9 +624,9 @@ bool ThermostatAttrAccess::InAtomicWrite(EndpointId endpoint) uint16_t ep = emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); - if (ep < ArraySize(mAtomicWriteState)) + if (ep < ArraySize(mAtomicWriteSessions)) { - inAtomicWrite = mAtomicWriteState[ep]; + inAtomicWrite = (mAtomicWriteSessions[ep].state == kAtomicWriteState_Open); } return inAtomicWrite; } @@ -649,26 +651,15 @@ bool ThermostatAttrAccess::InAtomicWrite(CommandHandler * commandObj, EndpointId return GetAtomicWriteScopedNodeId(endpoint) == sourceNodeId; } -void ThermostatAttrAccess::SetAtomicWriteScopedNodeId(EndpointId endpoint, ScopedNodeId originatorNodeId) -{ - uint16_t ep = - emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); - - if (ep < ArraySize(mAtomicWriteNodeIds)) - { - mAtomicWriteNodeIds[ep] = originatorNodeId; - } -} - ScopedNodeId ThermostatAttrAccess::GetAtomicWriteScopedNodeId(EndpointId endpoint) { ScopedNodeId originatorNodeId = ScopedNodeId(); uint16_t ep = emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); - if (ep < ArraySize(mAtomicWriteNodeIds)) + if (ep < ArraySize(mAtomicWriteSessions)) { - originatorNodeId = mAtomicWriteNodeIds[ep]; + originatorNodeId = mAtomicWriteSessions[ep].nodeId; } return originatorNodeId; } @@ -704,7 +695,7 @@ CHIP_ERROR ThermostatAttrAccess::Read(const ConcreteReadAttributePath & aPath, A } break; case PresetTypes::Id: { - Delegate * delegate = GetDelegate(aPath.mEndpointId); + auto delegate = GetDelegate(aPath.mEndpointId); VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is null")); return aEncoder.EncodeList([delegate](const auto & encoder) -> CHIP_ERROR { @@ -723,14 +714,14 @@ CHIP_ERROR ThermostatAttrAccess::Read(const ConcreteReadAttributePath & aPath, A } break; case NumberOfPresets::Id: { - Delegate * delegate = GetDelegate(aPath.mEndpointId); + auto delegate = GetDelegate(aPath.mEndpointId); VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is null")); ReturnErrorOnFailure(aEncoder.Encode(delegate->GetNumberOfPresets())); } break; case Presets::Id: { - Delegate * delegate = GetDelegate(aPath.mEndpointId); + auto delegate = GetDelegate(aPath.mEndpointId); VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is null")); auto & subjectDescriptor = aEncoder.GetSubjectDescriptor(); @@ -766,7 +757,7 @@ CHIP_ERROR ThermostatAttrAccess::Read(const ConcreteReadAttributePath & aPath, A } break; case ActivePresetHandle::Id: { - Delegate * delegate = GetDelegate(aPath.mEndpointId); + auto delegate = GetDelegate(aPath.mEndpointId); VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is null")); uint8_t buffer[kPresetHandleSize]; @@ -812,7 +803,7 @@ CHIP_ERROR ThermostatAttrAccess::Write(const ConcreteDataAttributePath & aPath, { case Presets::Id: { - Delegate * delegate = GetDelegate(endpoint); + auto delegate = GetDelegate(endpoint); VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is null")); // Presets are not editable, return INVALID_IN_STATE. @@ -897,7 +888,7 @@ CHIP_ERROR ThermostatAttrAccess::Write(const ConcreteDataAttributePath & aPath, return CHIP_NO_ERROR; } -CHIP_ERROR ThermostatAttrAccess::AppendPendingPreset(Delegate * delegate, const PresetStruct::Type & preset) +CHIP_ERROR ThermostatAttrAccess::AppendPendingPreset(Thermostat::Delegate * delegate, const PresetStruct::Type & preset) { if (!IsValidPresetEntry(preset)) { @@ -951,6 +942,23 @@ CHIP_ERROR ThermostatAttrAccess::AppendPendingPreset(Delegate * delegate, const return delegate->AppendToPendingPresetList(preset); } +void ThermostatAttrAccess::OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) +{ + for (size_t i = 0; i < ArraySize(mAtomicWriteSessions); ++i) + { + auto atomicWriteState = mAtomicWriteSessions[i]; + if (atomicWriteState.state == kAtomicWriteState_Open && atomicWriteState.nodeId.GetFabricIndex() == fabricIndex) + { + auto delegate = GetDelegate(atomicWriteState.endpointId); + if (delegate == nullptr) + { + continue; + } + resetAtomicWrite(delegate, atomicWriteState.endpointId); + } + } +} + } // namespace Thermostat } // namespace Clusters } // namespace app @@ -1394,8 +1402,6 @@ void handleAtomicBegin(CommandHandler * commandObj, const ConcreteCommandPath & return; } - auto timeout = commandData.timeout.Value(); - if (!validAtomicAttributes(commandData, false)) { commandObj->AddStatus(commandPath, imcode::InvalidCommand); @@ -1412,13 +1418,18 @@ void handleAtomicBegin(CommandHandler * commandObj, const ConcreteCommandPath & // needs to keep track of a pending preset list now. delegate->InitializePendingPresets(); - uint16_t maxTimeout = 5000; - timeout = std::min(timeout, maxTimeout); + auto timeout = + delegate->GetAtomicWriteTimeout(commandData.attributeRequests, System::Clock::Milliseconds16(commandData.timeout.Value())); - ScheduleTimer(endpoint, System::Clock::Milliseconds16(timeout)); - gThermostatAttrAccess.SetAtomicWrite(endpoint, true); - gThermostatAttrAccess.SetAtomicWriteScopedNodeId(endpoint, GetSourceScopedNodeId(commandObj)); - sendAtomicResponse(commandObj, commandPath, imcode::Success, imcode::Success, imcode::Success, MakeOptional(timeout)); + if (!timeout.has_value()) + { + commandObj->AddStatus(commandPath, imcode::InvalidCommand); + return; + } + ScheduleTimer(endpoint, timeout.value()); + gThermostatAttrAccess.SetAtomicWrite(endpoint, GetSourceScopedNodeId(commandObj), kAtomicWriteState_Open); + sendAtomicResponse(commandObj, commandPath, imcode::Success, imcode::Success, imcode::Success, + MakeOptional(timeout.value().count())); } imcode commitPresets(Delegate * delegate, EndpointId endpoint) @@ -1868,5 +1879,17 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co void MatterThermostatPluginServerInitCallback() { + Server::GetInstance().GetFabricTable().AddFabricDelegate(&gThermostatAttrAccess); AttributeAccessInterfaceRegistry::Instance().Register(&gThermostatAttrAccess); } + +void MatterThermostatClusterServerShutdownCallback(EndpointId endpoint) +{ + ChipLogProgress(Zcl, "Shutting down thermostat server cluster on endpoint %d", endpoint); + Delegate * delegate = GetDelegate(endpoint); + + if (delegate != nullptr) + { + resetAtomicWrite(delegate, endpoint); + } +} diff --git a/src/app/clusters/thermostat-server/thermostat-server.h b/src/app/clusters/thermostat-server/thermostat-server.h index 306a2a625c57b6..ddede8a9bb13f9 100644 --- a/src/app/clusters/thermostat-server/thermostat-server.h +++ b/src/app/clusters/thermostat-server/thermostat-server.h @@ -37,10 +37,15 @@ namespace Thermostat { static constexpr size_t kThermostatEndpointCount = MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; +enum AtomicWriteState +{ + kAtomicWriteState_Closed = 0, + kAtomicWriteState_Open, +}; /** * @brief Thermostat Attribute Access Interface. */ -class ThermostatAttrAccess : public chip::app::AttributeAccessInterface +class ThermostatAttrAccess : public chip::app::AttributeAccessInterface, public chip::FabricTable::Delegate { public: ThermostatAttrAccess() : AttributeAccessInterface(Optional::Missing(), Thermostat::Id) {} @@ -48,15 +53,6 @@ class ThermostatAttrAccess : public chip::app::AttributeAccessInterface CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; CHIP_ERROR Write(const ConcreteDataAttributePath & aPath, chip::app::AttributeValueDecoder & aDecoder) override; - /** - * @brief Sets the scoped node id of the originator that sent the last successful - * AtomicRequest of type BeginWrite for the given endpoint. - * - * @param[in] endpoint The endpoint. - * @param[in] originatorNodeId The originator scoped node id. - */ - void SetAtomicWriteScopedNodeId(EndpointId endpoint, ScopedNodeId originatorNodeId); - /** * @brief Gets the scoped node id of the originator that sent the last successful * AtomicRequest of type BeginWrite for the given endpoint. @@ -68,12 +64,13 @@ class ThermostatAttrAccess : public chip::app::AttributeAccessInterface ScopedNodeId GetAtomicWriteScopedNodeId(EndpointId endpoint); /** - * @brief Sets whether an atomic write is in progress for the given endpoint + * @brief Sets the atomic write state for the given endpoint and originatorNodeId * * @param[in] endpoint The endpoint. - * @param[in] inProgress Whether or not an atomic write is in progress. + * @param[in] originatorNodeId The originator scoped node id. + * @param[in] state Whether or not an atomic write is open or closed. */ - void SetAtomicWrite(EndpointId endpoint, bool inProgress); + void SetAtomicWrite(EndpointId endpoint, ScopedNodeId originatorNodeId, AtomicWriteState state); /** * @brief Gets whether an atomic write is in progress for the given endpoint @@ -105,10 +102,18 @@ class ThermostatAttrAccess : public chip::app::AttributeAccessInterface bool InAtomicWrite(CommandHandler * commandObj, EndpointId endpoint); private: - CHIP_ERROR AppendPendingPreset(Delegate * delegate, const Structs::PresetStruct::Type & preset); + CHIP_ERROR AppendPendingPreset(Thermostat::Delegate * delegate, const Structs::PresetStruct::Type & preset); + + void OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override; + + struct AtomicWriteSession + { + AtomicWriteState state = kAtomicWriteState_Closed; + ScopedNodeId nodeId; + EndpointId endpointId = kInvalidEndpointId; + }; - ScopedNodeId mAtomicWriteNodeIds[kThermostatEndpointCount]; - bool mAtomicWriteState[kThermostatEndpointCount]; + AtomicWriteSession mAtomicWriteSessions[kThermostatEndpointCount]; }; /** diff --git a/src/app/clusters/water-heater-management-server/water-heater-management-server.cpp b/src/app/clusters/water-heater-management-server/water-heater-management-server.cpp index 0806638dd7ee4d..3cbe76f60d38f4 100644 --- a/src/app/clusters/water-heater-management-server/water-heater-management-server.cpp +++ b/src/app/clusters/water-heater-management-server/water-heater-management-server.cpp @@ -117,12 +117,12 @@ void Instance::InvokeCommand(HandlerContext & handlerContext) void Instance::HandleBoost(HandlerContext & ctx, const Commands::Boost::DecodableType & commandData) { - uint32_t duration = commandData.duration; - Optional oneShot = commandData.oneShot; - Optional emergencyBoost = commandData.emergencyBoost; - Optional temporarySetpoint = commandData.temporarySetpoint; - Optional targetPercentage = commandData.targetPercentage; - Optional targetReheat = commandData.targetReheat; + uint32_t duration = commandData.boostInfo.duration; + Optional oneShot = commandData.boostInfo.oneShot; + Optional emergencyBoost = commandData.boostInfo.emergencyBoost; + Optional temporarySetpoint = commandData.boostInfo.temporarySetpoint; + Optional targetPercentage = commandData.boostInfo.targetPercentage; + Optional targetReheat = commandData.boostInfo.targetReheat; // Notify the appliance if the appliance hardware cannot be adjusted, then return Failure if (HasFeature(WaterHeaterManagement::Feature::kTankPercent)) diff --git a/src/app/clusters/water-heater-management-server/water-heater-management-server.h b/src/app/clusters/water-heater-management-server/water-heater-management-server.h index a0a48ab900c200..71a52de9b45eef 100644 --- a/src/app/clusters/water-heater-management-server/water-heater-management-server.h +++ b/src/app/clusters/water-heater-management-server/water-heater-management-server.h @@ -43,29 +43,52 @@ class Delegate void SetEndpointId(EndpointId aEndpoint) { mEndpointId = aEndpoint; } /** - * @brief Delegate should implement a handler to start boosting the water temperature as required. - * Upon receipt, the Water Heater SHALL transition into the BOOST state, which SHALL cause the water in the - * tank (or the TargetPercentage of the water, if included) to be heated towards the set point (or the - * TemporarySetpoint, if included), which in turn may cause a call for heat, even if the mode is OFF, or - * is TIMED and it is during one of the Off periods. + * @brief Delegate should implement a handler to start boosting the water + * temperature as required. Upon receipt, the Water Heater SHALL + * transition into the BOOST state, which SHALL cause the water in + * the tank (or the TargetPercentage of the water, if included) to be + * heated towards the set point (or the TemporarySetpoint, if + * included), which in turn may cause a call for heat, even if the + * mode is OFF, or is TIMED and it is during one of the Off periods. * - * @param duration Indicates the time period in seconds for which the BOOST state is activated before it automatically reverts - * to the previous mode (e.g. OFF, MANUAL or TIMED). - * @param oneShot Indicates whether the BOOST state should be automatically canceled once the hot water has first reached the - * set point temperature (or the TemporarySetpoint temperature, if specified) for the TargetPercentage (if - * specified). - * @param emergencyBoost Indicates that the consumer wants the water to be heated as quickly as practicable. This MAY cause - * multiple heat sources to be activated (e.g. a heat pump and direct electric heating element). - * @param temporarySetpoint Indicates the target temperature to which to heat the hot water for this Boost command. It SHALL be - * used instead of the normal set point temperature whilst the BOOST state is active. - * @param targetPercentage If the tank supports the TankPercent feature, this field indicates the amount of water that SHALL be - * heated by this Boost command before the heater is switched off. - * @param targetReheat If the tank supports the TankPercent feature, and the heating by this Boost command has ceased because - * the TargetPercentage of the water in the tank has been heated to the set point (or TemporarySetpoint if - * included), this field indicates the percentage to which the hot water in the tank SHALL be allowed to - * fall before again beginning to reheat it. + * @param duration Indicates the time period in seconds for which + * the BOOST state is activated before it + * automatically reverts to the previous mode + * (e.g. OFF, MANUAL or TIMED). * - * @return Success if the boost command is accepted; otherwise the command SHALL be rejected with appropriate error. + * @param oneShot Indicates whether the BOOST state should be + * automatically canceled once the hot water has + * first reached the set point temperature (or the + * TemporarySetpoint temperature, if specified) + * for the TargetPercentage (if specified). + * + * @param emergencyBoost Indicates that the consumer wants the water to + * be heated as quickly as practicable. This MAY + * cause multiple heat sources to be activated + * (e.g. a heat pump and direct electric heating + * element). + * + * @param temporarySetpoint Indicates the target temperature to which to + * heat the hot water for this Boost command. It + * SHALL be used instead of the normal set point + * temperature whilst the BOOST state is active. + * + * @param targetPercentage If the tank supports the TankPercent feature, + * this field indicates the amount of water that + * SHALL be heated by this Boost command before + * the heater is switched off. + * + * @param targetReheat If the tank supports the TankPercent feature, + * and the heating by this Boost command has + * ceased because the TargetPercentage of the + * water in the tank has been heated to the set + * point (or TemporarySetpoint if included), this + * field indicates the percentage to which the hot + * water in the tank SHALL be allowed to fall + * before again beginning to reheat it. + * + * @return Success if the boost command is accepted; otherwise the command + * SHALL be rejected with appropriate error. */ virtual Protocols::InteractionModel::Status HandleBoost(uint32_t duration, Optional oneShot, Optional emergencyBoost, Optional temporarySetpoint, @@ -73,8 +96,8 @@ class Delegate /** * @brief Delegate should implement a handler to cancel a boost command. - * Upon receipt, the Water Heater SHALL transition back from the BOOST state to the previous mode (e.g. OFF, - * MANUAL or TIMED). + * Upon receipt, the Water Heater SHALL transition back from the + * BOOST state to the previous mode (e.g. OFF, MANUAL or TIMED). * * @return It should report SUCCESS if successful and FAILURE otherwise. */ @@ -82,12 +105,12 @@ class Delegate // ------------------------------------------------------------------ // Get attribute methods - virtual BitMask GetHeaterTypes() = 0; - virtual BitMask GetHeatDemand() = 0; - virtual uint16_t GetTankVolume() = 0; - virtual int64_t GetEstimatedHeatRequired() = 0; - virtual Percent GetTankPercentage() = 0; - virtual BoostStateEnum GetBoostState() = 0; + virtual BitMask GetHeaterTypes() = 0; + virtual BitMask GetHeatDemand() = 0; + virtual uint16_t GetTankVolume() = 0; + virtual int64_t GetEstimatedHeatRequired() = 0; + virtual Percent GetTankPercentage() = 0; + virtual BoostStateEnum GetBoostState() = 0; protected: EndpointId mEndpointId = 0; diff --git a/src/app/codegen-data-model-provider/CodegenDataModelProvider_Read.cpp b/src/app/codegen-data-model-provider/CodegenDataModelProvider_Read.cpp index 9f87e4c8f187fe..de17d1059be127 100644 --- a/src/app/codegen-data-model-provider/CodegenDataModelProvider_Read.cpp +++ b/src/app/codegen-data-model-provider/CodegenDataModelProvider_Read.cpp @@ -273,7 +273,10 @@ DataModel::ActionReturnStatus CodegenDataModelProvider::ReadAttribute(const Data { ReturnErrorCodeIf(!request.subjectDescriptor.has_value(), CHIP_ERROR_INVALID_ARGUMENT); - Access::RequestPath requestPath{ .cluster = request.path.mClusterId, .endpoint = request.path.mEndpointId }; + Access::RequestPath requestPath{ .cluster = request.path.mClusterId, + .endpoint = request.path.mEndpointId, + .requestType = Access::RequestType::kAttributeReadRequest, + .entityId = request.path.mAttributeId }; CHIP_ERROR err = Access::GetAccessControl().Check(*request.subjectDescriptor, requestPath, RequiredPrivilege::ForReadAttribute(request.path)); if (err != CHIP_NO_ERROR) diff --git a/src/app/codegen-data-model-provider/CodegenDataModelProvider_Write.cpp b/src/app/codegen-data-model-provider/CodegenDataModelProvider_Write.cpp index 37371043aa323f..333c9e27e4bf9f 100644 --- a/src/app/codegen-data-model-provider/CodegenDataModelProvider_Write.cpp +++ b/src/app/codegen-data-model-provider/CodegenDataModelProvider_Write.cpp @@ -278,7 +278,10 @@ DataModel::ActionReturnStatus CodegenDataModelProvider::WriteAttribute(const Dat { ReturnErrorCodeIf(!request.subjectDescriptor.has_value(), Status::UnsupportedAccess); - Access::RequestPath requestPath{ .cluster = request.path.mClusterId, .endpoint = request.path.mEndpointId }; + Access::RequestPath requestPath{ .cluster = request.path.mClusterId, + .endpoint = request.path.mEndpointId, + .requestType = Access::RequestType::kAttributeWriteRequest, + .entityId = request.path.mAttributeId }; CHIP_ERROR err = Access::GetAccessControl().Check(*request.subjectDescriptor, requestPath, RequiredPrivilege::ForWriteAttribute(request.path)); diff --git a/src/app/common/templates/config-data.yaml b/src/app/common/templates/config-data.yaml index d8d276f53a0700..660ca0b3aa1df4 100644 --- a/src/app/common/templates/config-data.yaml +++ b/src/app/common/templates/config-data.yaml @@ -81,6 +81,7 @@ ClustersWithShutdownFunctions: - Color Control - Sample MEI - Scenes Management + - Thermostat ClustersWithPreAttributeChangeFunctions: - Door Lock diff --git a/src/app/reporting/Engine.cpp b/src/app/reporting/Engine.cpp index b5621e94bd9f47..6d79e9e7b34af0 100644 --- a/src/app/reporting/Engine.cpp +++ b/src/app/reporting/Engine.cpp @@ -339,7 +339,10 @@ CHIP_ERROR Engine::CheckAccessDeniedEventPaths(TLV::TLVWriter & aWriter, bool & aHasEncodedData = true; } - Access::RequestPath requestPath{ .cluster = current->mValue.mClusterId, .endpoint = current->mValue.mEndpointId }; + Access::RequestPath requestPath{ .cluster = current->mValue.mClusterId, + .endpoint = current->mValue.mEndpointId, + .requestType = RequestType::kEventReadOrSubscribeRequest, + .entityId = current->mValue.mEventId }; Access::Privilege requestPrivilege = RequiredPrivilege::ForReadEvent(path); err = Access::GetAccessControl().Check(apReadHandler->GetSubjectDescriptor(), requestPath, requestPrivilege); diff --git a/src/app/tests/suites/certification/PICS.yaml b/src/app/tests/suites/certification/PICS.yaml index 9d8011ceee2172..1fc182bd9ba867 100644 --- a/src/app/tests/suites/certification/PICS.yaml +++ b/src/app/tests/suites/certification/PICS.yaml @@ -6525,7 +6525,7 @@ PICS: - label: "Does the device implement receiving the AtomicRequest command for Thermostat?" - id: TSTAT.S.CFE.Rsp + id: TSTAT.S.Cfe.Rsp # # server / commandsGenerated @@ -6567,6 +6567,9 @@ PICS: - label: "Supports a local temperature not exposed" id: TSTAT.S.F06 + - label: "Supports enhanced schedules" + id: TSTAT.S.F07 + - label: "Supports setpoint presets" id: TSTAT.S.F08 @@ -6616,7 +6619,7 @@ PICS: id: TSTAT.C.C06.Tx - label: "Does the device implement sending the AtomicRequest command?" - id: TSTAT.C.CFE.Tx + id: TSTAT.C.Cfe.Tx # # client / manually @@ -10298,6 +10301,17 @@ PICS: - label: "Does the device implement the ActiveEndpoints attribute?" id: PWRTL.S.A0001 + # + # Thread Border Router Management Cluster + # + - label: + "Does the device implement the Thread Border Router Management cluster + as a server?" + id: TBRM.S + + - label: "Does the device support the PanChange feature?" + id: TBRM.S.F00 + # # Thread Network Directory Cluster # diff --git a/src/app/tests/suites/certification/Test_TC_ICDM_3_4.yaml b/src/app/tests/suites/certification/Test_TC_ICDM_3_4.yaml deleted file mode 100644 index a4eb232b98e157..00000000000000 --- a/src/app/tests/suites/certification/Test_TC_ICDM_3_4.yaml +++ /dev/null @@ -1,201 +0,0 @@ -# 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. - -name: 217.1.6. [TC-ICDM-3.4] ICDCounter persistence with DUT as Server - -PICS: - - ICDM.S - -config: - nodeId: 0x12344321 - cluster: "ICD Management" - endpoint: 0 - - CheckInNodeID1: - type: node_id - defaultValue: 1 - MonitorSubID1: - type: subject-id - defaultValue: 2 - Key1: - type: octstr - defaultValue: "hex:1234567890abcdef1234567890abcdef" - IdleModedurationValue: - type: int16u - defaultValue: 5 - -tests: - - label: - "Precondition: Commission DUT to TH (can be skipped if done in a - preceding test)" - cluster: "DelayCommands" - command: "WaitForCommissionee" - arguments: - values: - - name: "nodeId" - value: nodeId - - - label: - "Precondition: TH reads the RegisteredClients attribute from the DUT" - PICS: ICDM.S.A0003 - command: "readAttribute" - attribute: "RegisteredClients" - response: - saveAs: RegisteredClientsIDs - constraints: - type: list - - - label: - "Precondition: If list of registered clients is not empty, unregister - existing client(s)" - verification: | - Please send the 'unregister client' command using the check-in nodeID received from the previous read - ./chip-tool icdmanagement unregister-client 112233 1 0 - - [1704888949.629057][71657:71659] CHIP:DMG: - [1704888949.629066][71657:71659] CHIP:DMG: InteractionModelRevision = 11 - [1704888949.629074][71657:71659] CHIP:DMG: }, - [1704888949.629125][71657:71659] CHIP:DMG: Received Command Response Status for Endpoint=0 Cluster=0x0000_0046 Command=0x0000_0002 Status=0x0 - [1704888949.629153][71657:71659] CHIP:DMG: ICR moving to [AwaitingDe] - cluster: "LogCommands" - command: "UserPrompt" - PICS: PICS_USER_PROMPT && ICDM.S.C02.Rsp - arguments: - values: - - name: "message" - value: "Please enter 'y' for success" - - name: "expectedValue" - value: "y" - - - label: - "Precondition: TH reads from the DUT the RegisteredClients attribute. - Verify that the DUT response contains empty list of registered - clients." - PICS: ICDM.S.A0003 && PICS_USER_PROMPT - command: "readAttribute" - attribute: "RegisteredClients" - response: - value: [] - constraints: - type: list - - - label: "Step 1: TH reads from the DUT the IdleModeDuration attribute" - PICS: ICDM.S.A0000 - command: "readAttribute" - attribute: "IdleModeDuration" - response: - saveAs: IdleModeDuration1 - constraints: - type: int32u - minValue: 1 - maxValue: 64800 - - - label: "Step 2: TH reads from the DUT the ICDCounter attribute." - PICS: ICDM.S.A0004 - command: "readAttribute" - attribute: "ICDCounter" - response: - saveAs: ICDCounter1 - constraints: - type: int32u - minValue: 0 - maxValue: 4294967295 - - - label: - "Step 3a & 3b: TH sends RegisterClient command. - CheckInNodeID: - registering clients node ID (CheckInNodeID1) - MonitoredSubject: - monitored subject ID (MonitorSubID1) - Key: shared secret between the - client and the ICD (Key1). Verify DUT responds w/ status - SUCCESS(0x00); Verify that the DUT response contains IcdCounter1" - PICS: ICDM.S.C00.Rsp && ICDM.S.C01.Tx - command: "RegisterClient" - arguments: - values: - - name: "CheckInNodeID" - value: CheckInNodeID1 - - name: "MonitoredSubject" - value: MonitorSubID1 - - name: "Key" - value: Key1 - # Adding input for the test to pass for now - Full test script needs to be updated - - name: "ClientType" - value: ClientTypeEnum.Permanent - response: - values: - - name: "ICDCounter" - value: ICDCounter1 - constraints: - type: int32u - - - label: "Step 4: Wait for 1 or more Idle Mode duration." - cluster: "DelayCommands" - command: "WaitForMs" - arguments: - values: - - name: "ms" - value: ( IdleModedurationValue * 1000 ) - - #Issue https://github.com/project-chip/connectedhomeip/issues/31297 - - label: "Step 5: TH reads from the DUT the ICDCounter attribute." - verification: | - ./chip-tool icdmanagement read icdcounter 1 0 - - [1702421000.635860][1431:1433] CHIP:TOO: Endpoint: 0 Cluster: 0x0000_0046 Attribute 0x0000_0004 DataVersion: 2255930497 - [1702421000.635970][1431:1433] CHIP:TOO: ICDCounter: 1706188496 - cluster: "LogCommands" - command: "UserPrompt" - PICS: PICS_USER_PROMPT && ICDM.S.A0004 - arguments: - values: - - name: "message" - value: "Please enter 'y' for success" - - name: "expectedValue" - value: "y" - - - label: "Step 6: Reboot DUT" - PICS: PICS_SDK_CI_ONLY - cluster: "SystemCommands" - endpoint: 0 - command: "Reboot" - - - label: "Step Reboot target device(DUT)" - verification: | - Did the DUT successfully reboot? - cluster: "LogCommands" - command: "UserPrompt" - PICS: PICS_SKIP_SAMPLE_APP - arguments: - values: - - name: "message" - value: "Please reboot the DUT and enter 'y' after DUT starts" - - name: "expectedValue" - value: "y" - - - label: "Wait for the commissioned device to be retrieved" - cluster: "DelayCommands" - command: "WaitForCommissionee" - arguments: - values: - - name: "nodeId" - value: nodeId - - - label: "Step 7: TH reads from the DUT the ICDCounter attribute." - PICS: ICDM.S.A0004 && PICS_USER_PROMPT - command: "readAttribute" - attribute: "ICDCounter" - response: - constraints: - type: int32u - minValue: ICDCounter2 - maxValue: 4294967295 diff --git a/src/app/tests/suites/certification/Test_TC_TBRM_2_2.yaml b/src/app/tests/suites/certification/Test_TC_TBRM_2_2.yaml new file mode 100644 index 00000000000000..852e18a73adc8a --- /dev/null +++ b/src/app/tests/suites/certification/Test_TC_TBRM_2_2.yaml @@ -0,0 +1,116 @@ +# 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. + +name: "[TC-TBRM-2.2] Initial Dataset configuration of Thread Border Router" + +PICS: + - TBRM.S + +config: + nodeId: 0x12344321 + cluster: Thread Border Router Management + endpoint: 1 + PIXIT.TBRM.THREAD_ACTIVE_DATASET: + type: octet_string + defaultValue: "hex:0e080000000000010000000300001235060004001fffe002082ad51c02fe8f64f20708fddb8af85255f93a051083e2b9b2cc609b00125adbf823ea2ab20102c4d904100a133626c411d7de02a570ca3c3d80470c0402a0f7f8031054687265616441637469766554657374" + # Active Timestamp ----^^^^^^^^^^^^^^^^ + PIXIT.TBRM.THREAD_ACTIVE_DATASET.ACTIVE_TIMESTAMP: 0x10000 + PIXIT.TBRM.THREAD_INVALID_DATASET: + type: octet_string + defaultValue: "hex:00112233" + +# Precondition: +# The DUT does not have an Active Dataset + +tests: + - label: "Wait for the commissioned device to be retrieved" + cluster: DelayCommands + command: WaitForCommissionee + arguments: + values: + - name: nodeId + value: nodeId + + - label: "TH reads the ActiveDatasetTimestamp attribute from the DUT" + command: readAttribute + attribute: ActiveDatasetTimestamp + response: + value: null + + - label: "TH reads the PendingDatasetTimestamp attribute from the DUT" + command: readAttribute + attribute: PendingDatasetTimestamp + response: + value: null + + - label: + "TH sends a valid ActiveDatasetRequest command to the DUT without + having armed the fail-safe" + command: SetActiveDatasetRequest + arguments: + values: + - name: ActiveDataset + value: PIXIT.TBRM.THREAD_ACTIVE_DATASET + response: + error: FAILSAFE_REQUIRED + + - label: "TH sends ArmFailSafe command to the DUT" + cluster: General Commissioning + command: ArmFailSafe + endpoint: 0 + arguments: + values: + - name: ExpiryLengthSeconds + value: 60 + - name: Breadcrumb + value: 1 + + - label: "TH sends an invalid ActiveDatasetRequest command to the DUT" + command: SetActiveDatasetRequest + arguments: + values: + - name: ActiveDataset + value: PIXIT.TBRM.THREAD_INVALID_DATASET + response: + error: INVALID_COMMAND + + - label: "TH sends a valid ActiveDatasetRequest command to the DUT" + command: SetActiveDatasetRequest + arguments: + values: + - name: ActiveDataset + value: PIXIT.TBRM.THREAD_ACTIVE_DATASET + + - label: "TH reads the InterfaceEnabled attribute from the DUT" + command: readAttribute + attribute: InterfaceEnabled + response: + value: true + + - label: "TH reads the ActiveDatasetTimestamp attribute from the DUT" + command: readAttribute + attribute: ActiveDatasetTimestamp + response: + value: PIXIT.TBRM.THREAD_ACTIVE_DATASET.ACTIVE_TIMESTAMP + constraints: + type: int64u + + - label: "TH sends a valid GetActiveDatasetRequest command to the DUT" + command: GetActiveDatasetRequest + response: + values: + - name: Dataset + value: PIXIT.TBRM.THREAD_ACTIVE_DATASET + constraints: + type: octet_string diff --git a/src/app/tests/suites/certification/Test_TC_TBRM_2_3.yaml b/src/app/tests/suites/certification/Test_TC_TBRM_2_3.yaml new file mode 100644 index 00000000000000..7a55191f471f0b --- /dev/null +++ b/src/app/tests/suites/certification/Test_TC_TBRM_2_3.yaml @@ -0,0 +1,166 @@ +# 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. + +name: "[TC-TBRM-2.3] Change dataset configuration of Thread Border Router" + +PICS: + - TBRM.S + - TBRM.S.F00 + +config: + nodeId: 0x12344321 + cluster: Thread Border Router Management + endpoint: 1 + PIXIT.TBRM.THREAD_ACTIVE_DATASET: + type: octet_string + defaultValue: "hex:0e080000000000010000000300001235060004001fffe002082ad51c02fe8f64f20708fddb8af85255f93a051083e2b9b2cc609b00125adbf823ea2ab20102c4d904100a133626c411d7de02a570ca3c3d80470c0402a0f7f8031054687265616441637469766554657374" + PIXIT.TBRM.THREAD_PENDING_DATASET: + type: octet_string + defaultValue: "hex:0e08000000000002000033080000000000010000340400004e2035060004001fffe002082ad51c02fe8f64f20708fddb8af85255f93a051083e2b9b2cc609b00125adbf823ea2ab20102c4d904100a133626c411d7de02a570ca3c3d80470c0402a0f7f8030d54687265616450656e64696e67000300000c" + # Active Timestamp ----^^^^^^^^^^^^^^^^ + # Pending Timestamp -----------------------^^^^^^^^^^^^^^^^ + # Delay Timer -------------------------------------------------^^^^^^^^ == 20000ms Note: waitForReport has a hard-coded 30 second timeout + +tests: + - label: "Wait for the commissioned device to be retrieved" + cluster: DelayCommands + command: WaitForCommissionee + arguments: + values: + - name: nodeId + value: nodeId + # Step 1 + - label: "TH reads the ActiveDatasetTimestamp attribute from the DUT" + command: readAttribute + attribute: ActiveDatasetTimestamp + response: + saveAs: initialActiveTimestamp + constraints: + type: int64u + + - label: "If the ActiveDatasetTimestamp attribute not null, go to step 4" + cluster: EqualityCommands + command: UnsignedNumberEquals + arguments: + values: + - name: Value1 + value: initialActiveTimestamp + - name: Value2 + value: null + response: + - values: + - name: Equals + saveAs: noActiveDataset + + # Step 2 + - label: "TH sends ArmFailSafe command to the DUT" + runIf: noActiveDataset + cluster: General Commissioning + command: ArmFailSafe + endpoint: 0 + arguments: + values: + - name: ExpiryLengthSeconds + value: 60 + - name: Breadcrumb + value: 1 + + # Step 3 + - label: "TH sends a valid ActiveDatasetRequest command to the DUT" + runIf: noActiveDataset + command: SetActiveDatasetRequest + arguments: + values: + - name: ActiveDataset + value: PIXIT.TBRM.THREAD_ACTIVE_DATASET + + # Step 4 + - label: "TH reads the PendingDatasetTimestamp attribute from the DUT" + command: readAttribute + attribute: PendingDatasetTimestamp + response: + saveAs: initialPendingTimestamp + constraints: + type: int64u + + # Step 5 + - label: "TH sends a SetPendingDatasetRequest command to the DUT" + command: SetPendingDatasetRequest + arguments: + values: + - name: PendingDataset + value: PIXIT.TBRM.THREAD_PENDING_DATASET + + # Step 6 + - label: "TH sends a GetPendingDatasetRequest command to the DUT" + command: GetPendingDatasetRequest + response: + values: + - name: Dataset + constraints: + type: octet_string + # TODO: This should be PIXIT.TBRM.THREAD_PENDING_DATASET but ignoring the Delay Timer element if present + + # Step 7 + - label: "TH reads the PendingDatasetTimestamp attribute from the DUT" + command: readAttribute + attribute: PendingDatasetTimestamp + response: + constraints: + type: int64u + notValue: initialPendingTimestamp + + # Step 8 + - label: + "TH subscribes to the ActiveDatasetTimestamp attribute from the DUT" + command: subscribeAttribute + attribute: ActiveDatasetTimestamp + minInterval: 1 + maxInterval: 70 + response: + constraints: + type: int64u + hasValue: true # not null + + - label: "TH waits for an ActiveDatasetTimestamp report" + command: waitForReport + attribute: ActiveDatasetTimestamp + # TODO: waitForReport uses a hard-coded timeout of 30s, should be configurable? + response: + constraints: + hasValue: true + + # Step 9 + - label: "TH reads the PendingDatasetTimestamp attribute from the DUT" + command: readAttribute + attribute: PendingDatasetTimestamp + response: + value: null + + # Step 10 + - label: "TH sends a valid GetActiveDatasetRequest command to the DUT" + command: GetActiveDatasetRequest + response: + values: + - name: Dataset + # TODO: This should be PIXIT.TBRM.THREAD_PENDING_DATASET without the Delay Timer and Pending Timestamp elements + constraints: + type: octet_string + + # Step 11 + - label: "TH reads the InterfaceEnabled attribute from the DUT" + command: readAttribute + attribute: InterfaceEnabled + response: + value: true diff --git a/src/app/tests/suites/certification/Test_TC_TSTAT_1_1.yaml b/src/app/tests/suites/certification/Test_TC_TSTAT_1_1.yaml index fe6b89ae930517..789f477667010b 100644 --- a/src/app/tests/suites/certification/Test_TC_TSTAT_1_1.yaml +++ b/src/app/tests/suites/certification/Test_TC_TSTAT_1_1.yaml @@ -37,7 +37,7 @@ tests: command: "readAttribute" attribute: "ClusterRevision" response: - value: 6 + value: 7 constraints: type: int16u diff --git a/src/app/tests/suites/certification/Test_TC_TSTAT_4_1.yaml b/src/app/tests/suites/certification/Test_TC_TSTAT_4_1.yaml index aa12c7bec5e6f0..50037d682d9faa 100644 --- a/src/app/tests/suites/certification/Test_TC_TSTAT_4_1.yaml +++ b/src/app/tests/suites/certification/Test_TC_TSTAT_4_1.yaml @@ -70,7 +70,7 @@ tests: saveAs: SchedulesValue - label: "Step 2: TH reads the PresetTypes attribute from the DUT" - PICS: TSTAT.S.F08 & TSTAT.S.A0048 + PICS: TSTAT.S.F08 && TSTAT.S.A0048 command: "readAttribute" attribute: "PresetTypes" response: @@ -78,7 +78,7 @@ tests: type: list - label: "Step 3: TH reads the NumberOfPresets attribute from the DUT" - PICS: TSTAT.S.F08 & TSTAT.S.A004a + PICS: TSTAT.S.F08 && TSTAT.S.A004a command: "readAttribute" attribute: "NumberOfPresets" response: @@ -86,7 +86,7 @@ tests: type: int8u - label: "Step 4: TH reads the ActivePresetHandle attribute from the DUT" - PICS: TSTAT.S.F08 & TSTAT.S.A004e + PICS: TSTAT.S.F08 && TSTAT.S.A004e command: "readAttribute" attribute: "ActivePresetHandle" response: @@ -94,7 +94,7 @@ tests: type: octstr - label: "Step 5: TH reads the Presets attribute from the DUT" - PICS: TSTAT.S.F08 & TSTAT.S.A0050 + PICS: TSTAT.S.F08 && TSTAT.S.A0050 command: "readAttribute" attribute: "Presets" response: @@ -102,7 +102,7 @@ tests: type: list - label: "Step 6: TH reads the Schedules attribute from the DUT" - PICS: TSTAT.S.F07 & TSTAT.S.A0051 + PICS: TSTAT.S.F07 && TSTAT.S.A0051 command: "readAttribute" attribute: "Schedules" response: diff --git a/src/app/tests/suites/certification/ci-pics-values b/src/app/tests/suites/certification/ci-pics-values index 70a0e79760be3a..f64941bb688890 100644 --- a/src/app/tests/suites/certification/ci-pics-values +++ b/src/app/tests/suites/certification/ci-pics-values @@ -1931,6 +1931,7 @@ TSTAT.S.F03=0 TSTAT.S.F04=0 TSTAT.S.F05=1 TSTAT.S.F06=0 +TSTAT.S.F07=0 TSTAT.S.F08=1 TSTAT.S.A0000=1 @@ -1987,7 +1988,7 @@ TSTAT.S.A0048=1 TSTAT.S.A004a=1 TSTAT.S.A004e=1 TSTAT.S.A0050=1 -TSTAT.S.A0051=1 +TSTAT.S.A0051=0 TSTAT.S.M.MinSetpointDeadBandWritable=1 TSTAT.S.M.HVACSystemTypeConfigurationWritable=0 @@ -1999,7 +2000,7 @@ TSTAT.S.C02.Rsp=0 TSTAT.S.C03.Rsp=0 TSTAT.S.C04.Rsp=0 TSTAT.S.C06.Rsp=1 -TSTAT.S.CFE.Rsp=1 +TSTAT.S.Cfe.Rsp=1 TSTAT.S.CFD.Tx=1 # Client @@ -2016,7 +2017,7 @@ TSTAT.C.C03.Tx=0 TSTAT.S.C00.Tx=0 TSTAT.S.C01.Tx=0 TSTAT.C.C06.Tx=1 -TSTAT.C.CFE.Tx=1 +TSTAT.C.Cfe.Tx=1 # Client Commands TSTAT.C.C00.Tx=1 @@ -3017,6 +3018,10 @@ PWRTL.S.F01=0 PWRTL.S.F02=1 PWRTL.S.F03=1 +# Thread Border Router Management Cluster +TBRM.S=1 +TBRM.S.F00=1 + # Thread Network Directory Cluster THNETDIR.S=1 diff --git a/src/app/tests/suites/ciTests.json b/src/app/tests/suites/ciTests.json index 1e2410650bf1bc..d976d7623244fd 100644 --- a/src/app/tests/suites/ciTests.json +++ b/src/app/tests/suites/ciTests.json @@ -71,7 +71,6 @@ "IcdManagement": [ "TestIcdManagementCluster", "Test_TC_ICDM_1_1", - "Test_TC_ICDM_3_4", "Test_TC_ICDM_4_1" ], "Identify": ["Test_TC_I_2_1", "Test_TC_I_2_2", "Test_TC_I_2_3"], diff --git a/src/app/util/ember-compatibility-functions.cpp b/src/app/util/ember-compatibility-functions.cpp index d556d8816c2145..ad0542a9ff0614 100644 --- a/src/app/util/ember-compatibility-functions.cpp +++ b/src/app/util/ember-compatibility-functions.cpp @@ -294,7 +294,10 @@ CHIP_ERROR ReadSingleClusterData(const SubjectDescriptor & aSubjectDescriptor, b // depending on whether the path was expanded. { - Access::RequestPath requestPath{ .cluster = aPath.mClusterId, .endpoint = aPath.mEndpointId }; + Access::RequestPath requestPath{ .cluster = aPath.mClusterId, + .endpoint = aPath.mEndpointId, + .requestType = Access::RequestType::kAttributeReadRequest, + .entityId = aPath.mAttributeId }; Access::Privilege requestPrivilege = RequiredPrivilege::ForReadAttribute(aPath); CHIP_ERROR err = Access::GetAccessControl().Check(aSubjectDescriptor, requestPath, requestPrivilege); if (err != CHIP_NO_ERROR) @@ -686,7 +689,10 @@ CHIP_ERROR WriteSingleClusterData(const SubjectDescriptor & aSubjectDescriptor, } { - Access::RequestPath requestPath{ .cluster = aPath.mClusterId, .endpoint = aPath.mEndpointId }; + Access::RequestPath requestPath{ .cluster = aPath.mClusterId, + .endpoint = aPath.mEndpointId, + .requestType = Access::RequestType::kAttributeWriteRequest, + .entityId = aPath.mAttributeId }; Access::Privilege requestPrivilege = RequiredPrivilege::ForWriteAttribute(aPath); CHIP_ERROR err = CHIP_NO_ERROR; if (!apWriteHandler->ACLCheckCacheHit({ aPath, requestPrivilege })) diff --git a/src/app/zap-templates/zcl/data-model/chip/water-heater-management-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/water-heater-management-cluster.xml index 0c5a9a1efea2fa..8246930e5b4dab 100644 --- a/src/app/zap-templates/zcl/data-model/chip/water-heater-management-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/water-heater-management-cluster.xml @@ -16,16 +16,7 @@ limitations under the License. --> - - - - - - - - - - + @@ -40,6 +31,16 @@ limitations under the License. + + + + + + + + + + Energy Management Water Heater Management @@ -51,26 +52,21 @@ limitations under the License. - + true - - HeaterTypes - HeatDemand + + HeaterTypes + HeatDemand TankVolume - EstimatedHeatRequired + EstimatedHeatRequired TankPercentage BoostState Allows a client to request that the water heater is put into a Boost state. - - - - - - + @@ -79,5 +75,14 @@ limitations under the License. + + BoostStarted + + + + + BoostEnded + + diff --git a/src/controller/data_model/controller-clusters.matter b/src/controller/data_model/controller-clusters.matter index 2e41cad5a77fe2..02408e1ecf74b0 100644 --- a/src/controller/data_model/controller-clusters.matter +++ b/src/controller/data_model/controller-clusters.matter @@ -4647,7 +4647,7 @@ cluster ElectricalEnergyMeasurement = 145 { /** This cluster is used to allow clients to control the operation of a hot water heating appliance so that it can be used with energy management. */ provisional cluster WaterHeaterManagement = 148 { - revision 1; + revision 2; enum BoostStateEnum : enum8 { kInactive = 0; @@ -4659,7 +4659,7 @@ provisional cluster WaterHeaterManagement = 148 { kTankPercent = 0x2; } - bitmap WaterHeaterDemandBitmap : bitmap8 { + bitmap WaterHeaterHeatSourceBitmap : bitmap8 { kImmersionElement1 = 0x1; kImmersionElement2 = 0x2; kHeatPump = 0x4; @@ -4667,16 +4667,24 @@ provisional cluster WaterHeaterManagement = 148 { kOther = 0x10; } - bitmap WaterHeaterTypeBitmap : bitmap8 { - kImmersionElement1 = 0x1; - kImmersionElement2 = 0x2; - kHeatPump = 0x4; - kBoiler = 0x8; - kOther = 0x10; + struct WaterHeaterBoostInfoStruct { + elapsed_s duration = 0; + optional boolean oneShot = 1; + optional boolean emergencyBoost = 2; + optional temperature temporarySetpoint = 3; + optional percent targetPercentage = 4; + optional percent targetReheat = 5; + } + + info event BoostStarted = 0 { + WaterHeaterBoostInfoStruct boostInfo = 0; } - readonly attribute WaterHeaterTypeBitmap heaterTypes = 0; - readonly attribute WaterHeaterDemandBitmap heatDemand = 1; + info event BoostEnded = 1 { + } + + readonly attribute WaterHeaterHeatSourceBitmap heaterTypes = 0; + readonly attribute WaterHeaterHeatSourceBitmap heatDemand = 1; readonly attribute optional int16u tankVolume = 2; readonly attribute optional energy_mwh estimatedHeatRequired = 3; readonly attribute optional percent tankPercentage = 4; @@ -4689,12 +4697,7 @@ provisional cluster WaterHeaterManagement = 148 { readonly attribute int16u clusterRevision = 65533; request struct BoostRequest { - elapsed_s duration = 0; - optional boolean oneShot = 1; - optional boolean emergencyBoost = 2; - optional temperature temporarySetpoint = 3; - optional percent targetPercentage = 4; - optional percent targetReheat = 5; + WaterHeaterBoostInfoStruct boostInfo = 0; } /** Allows a client to request that the water heater is put into a Boost state. */ diff --git a/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java b/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java index 5944e05356d9c5..8b112f53c6f36d 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java @@ -30541,37 +30541,17 @@ public long initWithDevice(long devicePtr, int endpointId) { return 0L; } - public void boost(DefaultClusterCallback callback, Long duration, Optional oneShot, Optional emergencyBoost, Optional temporarySetpoint, Optional targetPercentage, Optional targetReheat) { - boost(callback, duration, oneShot, emergencyBoost, temporarySetpoint, targetPercentage, targetReheat, 0); + public void boost(DefaultClusterCallback callback, ChipStructs.WaterHeaterManagementClusterWaterHeaterBoostInfoStruct boostInfo) { + boost(callback, boostInfo, 0); } - public void boost(DefaultClusterCallback callback, Long duration, Optional oneShot, Optional emergencyBoost, Optional temporarySetpoint, Optional targetPercentage, Optional targetReheat, int timedInvokeTimeoutMs) { + public void boost(DefaultClusterCallback callback, ChipStructs.WaterHeaterManagementClusterWaterHeaterBoostInfoStruct boostInfo, int timedInvokeTimeoutMs) { final long commandId = 0L; ArrayList elements = new ArrayList<>(); - final long durationFieldID = 0L; - BaseTLVType durationtlvValue = new UIntType(duration); - elements.add(new StructElement(durationFieldID, durationtlvValue)); - - final long oneShotFieldID = 1L; - BaseTLVType oneShottlvValue = oneShot.map((nonOptionaloneShot) -> new BooleanType(nonOptionaloneShot)).orElse(new EmptyType()); - elements.add(new StructElement(oneShotFieldID, oneShottlvValue)); - - final long emergencyBoostFieldID = 2L; - BaseTLVType emergencyBoosttlvValue = emergencyBoost.map((nonOptionalemergencyBoost) -> new BooleanType(nonOptionalemergencyBoost)).orElse(new EmptyType()); - elements.add(new StructElement(emergencyBoostFieldID, emergencyBoosttlvValue)); - - final long temporarySetpointFieldID = 3L; - BaseTLVType temporarySetpointtlvValue = temporarySetpoint.map((nonOptionaltemporarySetpoint) -> new IntType(nonOptionaltemporarySetpoint)).orElse(new EmptyType()); - elements.add(new StructElement(temporarySetpointFieldID, temporarySetpointtlvValue)); - - final long targetPercentageFieldID = 4L; - BaseTLVType targetPercentagetlvValue = targetPercentage.map((nonOptionaltargetPercentage) -> new UIntType(nonOptionaltargetPercentage)).orElse(new EmptyType()); - elements.add(new StructElement(targetPercentageFieldID, targetPercentagetlvValue)); - - final long targetReheatFieldID = 5L; - BaseTLVType targetReheattlvValue = targetReheat.map((nonOptionaltargetReheat) -> new UIntType(nonOptionaltargetReheat)).orElse(new EmptyType()); - elements.add(new StructElement(targetReheatFieldID, targetReheattlvValue)); + final long boostInfoFieldID = 0L; + BaseTLVType boostInfotlvValue = boostInfo.encodeTlv(); + elements.add(new StructElement(boostInfoFieldID, boostInfotlvValue)); StructType commandArgs = new StructType(elements); invoke(new InvokeCallbackImpl(callback) { diff --git a/src/controller/java/generated/java/chip/devicecontroller/ChipEventStructs.java b/src/controller/java/generated/java/chip/devicecontroller/ChipEventStructs.java index 7a7634b395cb38..4e3c6ce296c7b7 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ChipEventStructs.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ChipEventStructs.java @@ -3808,6 +3808,80 @@ public String toString() { return output.toString(); } } +public static class WaterHeaterManagementClusterBoostStartedEvent { + public ChipStructs.WaterHeaterManagementClusterWaterHeaterBoostInfoStruct boostInfo; + private static final long BOOST_INFO_ID = 0L; + + public WaterHeaterManagementClusterBoostStartedEvent( + ChipStructs.WaterHeaterManagementClusterWaterHeaterBoostInfoStruct boostInfo + ) { + this.boostInfo = boostInfo; + } + + public StructType encodeTlv() { + ArrayList values = new ArrayList<>(); + values.add(new StructElement(BOOST_INFO_ID, boostInfo.encodeTlv())); + + return new StructType(values); + } + + public static WaterHeaterManagementClusterBoostStartedEvent decodeTlv(BaseTLVType tlvValue) { + if (tlvValue == null || tlvValue.type() != TLVType.Struct) { + return null; + } + ChipStructs.WaterHeaterManagementClusterWaterHeaterBoostInfoStruct boostInfo = null; + for (StructElement element: ((StructType)tlvValue).value()) { + if (element.contextTagNum() == BOOST_INFO_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.Struct) { + StructType castingValue = element.value(StructType.class); + boostInfo = ChipStructs.WaterHeaterManagementClusterWaterHeaterBoostInfoStruct.decodeTlv(castingValue); + } + } + } + return new WaterHeaterManagementClusterBoostStartedEvent( + boostInfo + ); + } + + @Override + public String toString() { + StringBuilder output = new StringBuilder(); + output.append("WaterHeaterManagementClusterBoostStartedEvent {\n"); + output.append("\tboostInfo: "); + output.append(boostInfo); + output.append("\n"); + output.append("}\n"); + return output.toString(); + } +} +public static class WaterHeaterManagementClusterBoostEndedEvent { + + public WaterHeaterManagementClusterBoostEndedEvent( + ) { + } + + public StructType encodeTlv() { + ArrayList values = new ArrayList<>(); + + return new StructType(values); + } + + public static WaterHeaterManagementClusterBoostEndedEvent decodeTlv(BaseTLVType tlvValue) { + if (tlvValue == null || tlvValue.type() != TLVType.Struct) { + return null; + } + return new WaterHeaterManagementClusterBoostEndedEvent( + ); + } + + @Override + public String toString() { + StringBuilder output = new StringBuilder(); + output.append("WaterHeaterManagementClusterBoostEndedEvent {\n"); + output.append("}\n"); + return output.toString(); + } +} public static class DemandResponseLoadControlClusterLoadControlEventStatusChangeEvent { public byte[] eventID; public @Nullable Integer transitionIndex; diff --git a/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java b/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java index 4e07b6d86182d9..2871071475fe69 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java @@ -6744,6 +6744,127 @@ public String toString() { return output.toString(); } } +public static class WaterHeaterManagementClusterWaterHeaterBoostInfoStruct { + public Long duration; + public Optional oneShot; + public Optional emergencyBoost; + public Optional temporarySetpoint; + public Optional targetPercentage; + public Optional targetReheat; + private static final long DURATION_ID = 0L; + private static final long ONE_SHOT_ID = 1L; + private static final long EMERGENCY_BOOST_ID = 2L; + private static final long TEMPORARY_SETPOINT_ID = 3L; + private static final long TARGET_PERCENTAGE_ID = 4L; + private static final long TARGET_REHEAT_ID = 5L; + + public WaterHeaterManagementClusterWaterHeaterBoostInfoStruct( + Long duration, + Optional oneShot, + Optional emergencyBoost, + Optional temporarySetpoint, + Optional targetPercentage, + Optional targetReheat + ) { + this.duration = duration; + this.oneShot = oneShot; + this.emergencyBoost = emergencyBoost; + this.temporarySetpoint = temporarySetpoint; + this.targetPercentage = targetPercentage; + this.targetReheat = targetReheat; + } + + public StructType encodeTlv() { + ArrayList values = new ArrayList<>(); + values.add(new StructElement(DURATION_ID, new UIntType(duration))); + values.add(new StructElement(ONE_SHOT_ID, oneShot.map((nonOptionaloneShot) -> new BooleanType(nonOptionaloneShot)).orElse(new EmptyType()))); + values.add(new StructElement(EMERGENCY_BOOST_ID, emergencyBoost.map((nonOptionalemergencyBoost) -> new BooleanType(nonOptionalemergencyBoost)).orElse(new EmptyType()))); + values.add(new StructElement(TEMPORARY_SETPOINT_ID, temporarySetpoint.map((nonOptionaltemporarySetpoint) -> new IntType(nonOptionaltemporarySetpoint)).orElse(new EmptyType()))); + values.add(new StructElement(TARGET_PERCENTAGE_ID, targetPercentage.map((nonOptionaltargetPercentage) -> new UIntType(nonOptionaltargetPercentage)).orElse(new EmptyType()))); + values.add(new StructElement(TARGET_REHEAT_ID, targetReheat.map((nonOptionaltargetReheat) -> new UIntType(nonOptionaltargetReheat)).orElse(new EmptyType()))); + + return new StructType(values); + } + + public static WaterHeaterManagementClusterWaterHeaterBoostInfoStruct decodeTlv(BaseTLVType tlvValue) { + if (tlvValue == null || tlvValue.type() != TLVType.Struct) { + return null; + } + Long duration = null; + Optional oneShot = Optional.empty(); + Optional emergencyBoost = Optional.empty(); + Optional temporarySetpoint = Optional.empty(); + Optional targetPercentage = Optional.empty(); + Optional targetReheat = Optional.empty(); + for (StructElement element: ((StructType)tlvValue).value()) { + if (element.contextTagNum() == DURATION_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.UInt) { + UIntType castingValue = element.value(UIntType.class); + duration = castingValue.value(Long.class); + } + } else if (element.contextTagNum() == ONE_SHOT_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.Boolean) { + BooleanType castingValue = element.value(BooleanType.class); + oneShot = Optional.of(castingValue.value(Boolean.class)); + } + } else if (element.contextTagNum() == EMERGENCY_BOOST_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.Boolean) { + BooleanType castingValue = element.value(BooleanType.class); + emergencyBoost = Optional.of(castingValue.value(Boolean.class)); + } + } else if (element.contextTagNum() == TEMPORARY_SETPOINT_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.Int) { + IntType castingValue = element.value(IntType.class); + temporarySetpoint = Optional.of(castingValue.value(Integer.class)); + } + } else if (element.contextTagNum() == TARGET_PERCENTAGE_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.UInt) { + UIntType castingValue = element.value(UIntType.class); + targetPercentage = Optional.of(castingValue.value(Integer.class)); + } + } else if (element.contextTagNum() == TARGET_REHEAT_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.UInt) { + UIntType castingValue = element.value(UIntType.class); + targetReheat = Optional.of(castingValue.value(Integer.class)); + } + } + } + return new WaterHeaterManagementClusterWaterHeaterBoostInfoStruct( + duration, + oneShot, + emergencyBoost, + temporarySetpoint, + targetPercentage, + targetReheat + ); + } + + @Override + public String toString() { + StringBuilder output = new StringBuilder(); + output.append("WaterHeaterManagementClusterWaterHeaterBoostInfoStruct {\n"); + output.append("\tduration: "); + output.append(duration); + output.append("\n"); + output.append("\toneShot: "); + output.append(oneShot); + output.append("\n"); + output.append("\temergencyBoost: "); + output.append(emergencyBoost); + output.append("\n"); + output.append("\ttemporarySetpoint: "); + output.append(temporarySetpoint); + output.append("\n"); + output.append("\ttargetPercentage: "); + output.append(targetPercentage); + output.append("\n"); + output.append("\ttargetReheat: "); + output.append(targetReheat); + output.append("\n"); + output.append("}\n"); + return output.toString(); + } +} public static class DemandResponseLoadControlClusterHeatingSourceControlStruct { public Integer heatingSource; private static final long HEATING_SOURCE_ID = 0L; diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java index c6677b6aa93f47..5dbefbab1da2b7 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java @@ -9415,7 +9415,9 @@ public static Attribute value(long id) throws NoSuchFieldError { } } - public enum Event {; + public enum Event { + BoostStarted(0L), + BoostEnded(1L),; private final long id; Event(long id) { this.id = id; @@ -9455,7 +9457,7 @@ public static Command value(long id) throws NoSuchFieldError { } throw new NoSuchFieldError(); } - }public enum BoostCommandField {Duration(0),OneShot(1),EmergencyBoost(2),TemporarySetpoint(3),TargetPercentage(4),TargetReheat(5),; + }public enum BoostCommandField {BoostInfo(0),; private final int id; BoostCommandField(int id) { this.id = id; diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java index 986d59f3635af1..f58ac0f6e47798 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java @@ -25754,39 +25754,12 @@ public Map> getCommandMap() { Map waterHeaterManagementboostCommandParams = new LinkedHashMap(); - CommandParameterInfo waterHeaterManagementboostdurationCommandParameterInfo = new CommandParameterInfo("duration", Long.class, Long.class); - waterHeaterManagementboostCommandParams.put("duration",waterHeaterManagementboostdurationCommandParameterInfo); - - CommandParameterInfo waterHeaterManagementboostoneShotCommandParameterInfo = new CommandParameterInfo("oneShot", Optional.class, Boolean.class); - waterHeaterManagementboostCommandParams.put("oneShot",waterHeaterManagementboostoneShotCommandParameterInfo); - - CommandParameterInfo waterHeaterManagementboostemergencyBoostCommandParameterInfo = new CommandParameterInfo("emergencyBoost", Optional.class, Boolean.class); - waterHeaterManagementboostCommandParams.put("emergencyBoost",waterHeaterManagementboostemergencyBoostCommandParameterInfo); - - CommandParameterInfo waterHeaterManagementboosttemporarySetpointCommandParameterInfo = new CommandParameterInfo("temporarySetpoint", Optional.class, Integer.class); - waterHeaterManagementboostCommandParams.put("temporarySetpoint",waterHeaterManagementboosttemporarySetpointCommandParameterInfo); - - CommandParameterInfo waterHeaterManagementboosttargetPercentageCommandParameterInfo = new CommandParameterInfo("targetPercentage", Optional.class, Integer.class); - waterHeaterManagementboostCommandParams.put("targetPercentage",waterHeaterManagementboosttargetPercentageCommandParameterInfo); - - CommandParameterInfo waterHeaterManagementboosttargetReheatCommandParameterInfo = new CommandParameterInfo("targetReheat", Optional.class, Integer.class); - waterHeaterManagementboostCommandParams.put("targetReheat",waterHeaterManagementboosttargetReheatCommandParameterInfo); InteractionInfo waterHeaterManagementboostInteractionInfo = new InteractionInfo( (cluster, callback, commandArguments) -> { ((ChipClusters.WaterHeaterManagementCluster) cluster) .boost((DefaultClusterCallback) callback - , (Long) - commandArguments.get("duration") - , (Optional) - commandArguments.get("oneShot") - , (Optional) - commandArguments.get("emergencyBoost") - , (Optional) - commandArguments.get("temporarySetpoint") - , (Optional) - commandArguments.get("targetPercentage") - , (Optional) - commandArguments.get("targetReheat") + , (ChipStructs.WaterHeaterManagementClusterWaterHeaterBoostInfoStruct) + commandArguments.get("boostInfo") ); }, () -> new DelegatedDefaultClusterCallback(), diff --git a/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/WaterHeaterManagementClusterBoostStartedEvent.kt b/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/WaterHeaterManagementClusterBoostStartedEvent.kt new file mode 100644 index 00000000000000..f8a5e55e2e7894 --- /dev/null +++ b/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/WaterHeaterManagementClusterBoostStartedEvent.kt @@ -0,0 +1,57 @@ +/* + * + * Copyright (c) 2023 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. + */ +package chip.devicecontroller.cluster.eventstructs + +import chip.devicecontroller.cluster.* +import matter.tlv.ContextSpecificTag +import matter.tlv.Tag +import matter.tlv.TlvReader +import matter.tlv.TlvWriter + +class WaterHeaterManagementClusterBoostStartedEvent( + val boostInfo: + chip.devicecontroller.cluster.structs.WaterHeaterManagementClusterWaterHeaterBoostInfoStruct +) { + override fun toString(): String = buildString { + append("WaterHeaterManagementClusterBoostStartedEvent {\n") + append("\tboostInfo : $boostInfo\n") + append("}\n") + } + + fun toTlv(tlvTag: Tag, tlvWriter: TlvWriter) { + tlvWriter.apply { + startStructure(tlvTag) + boostInfo.toTlv(ContextSpecificTag(TAG_BOOST_INFO), this) + endStructure() + } + } + + companion object { + private const val TAG_BOOST_INFO = 0 + + fun fromTlv(tlvTag: Tag, tlvReader: TlvReader): WaterHeaterManagementClusterBoostStartedEvent { + tlvReader.enterStructure(tlvTag) + val boostInfo = + chip.devicecontroller.cluster.structs.WaterHeaterManagementClusterWaterHeaterBoostInfoStruct + .fromTlv(ContextSpecificTag(TAG_BOOST_INFO), tlvReader) + + tlvReader.exitContainer() + + return WaterHeaterManagementClusterBoostStartedEvent(boostInfo) + } + } +} diff --git a/src/controller/java/generated/java/chip/devicecontroller/cluster/files.gni b/src/controller/java/generated/java/chip/devicecontroller/cluster/files.gni index ca6223a2c1f96e..9665346b3d04b2 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/cluster/files.gni +++ b/src/controller/java/generated/java/chip/devicecontroller/cluster/files.gni @@ -156,6 +156,7 @@ structs_sources = [ "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/UnitTestingClusterTestGlobalStruct.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/UnitTestingClusterTestListStructOctet.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/UserLabelClusterLabelStruct.kt", + "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/WaterHeaterManagementClusterWaterHeaterBoostInfoStruct.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/WaterHeaterModeClusterModeOptionStruct.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/WaterHeaterModeClusterModeTagStruct.kt", ] @@ -241,6 +242,7 @@ eventstructs_sources = [ "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/UnitTestingClusterTestFabricScopedEventEvent.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/ValveConfigurationAndControlClusterValveFaultEvent.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/ValveConfigurationAndControlClusterValveStateChangedEvent.kt", + "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/WaterHeaterManagementClusterBoostStartedEvent.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/WiFiNetworkDiagnosticsClusterAssociationFailureEvent.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/WiFiNetworkDiagnosticsClusterConnectionStatusEvent.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/WiFiNetworkDiagnosticsClusterDisconnectionEvent.kt", diff --git a/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/WaterHeaterManagementClusterWaterHeaterBoostInfoStruct.kt b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/WaterHeaterManagementClusterWaterHeaterBoostInfoStruct.kt new file mode 100644 index 00000000000000..c9f95799bd18ba --- /dev/null +++ b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/WaterHeaterManagementClusterWaterHeaterBoostInfoStruct.kt @@ -0,0 +1,130 @@ +/* + * + * Copyright (c) 2023 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. + */ +package chip.devicecontroller.cluster.structs + +import chip.devicecontroller.cluster.* +import java.util.Optional +import matter.tlv.ContextSpecificTag +import matter.tlv.Tag +import matter.tlv.TlvReader +import matter.tlv.TlvWriter + +class WaterHeaterManagementClusterWaterHeaterBoostInfoStruct( + val duration: ULong, + val oneShot: Optional, + val emergencyBoost: Optional, + val temporarySetpoint: Optional, + val targetPercentage: Optional, + val targetReheat: Optional, +) { + override fun toString(): String = buildString { + append("WaterHeaterManagementClusterWaterHeaterBoostInfoStruct {\n") + append("\tduration : $duration\n") + append("\toneShot : $oneShot\n") + append("\temergencyBoost : $emergencyBoost\n") + append("\ttemporarySetpoint : $temporarySetpoint\n") + append("\ttargetPercentage : $targetPercentage\n") + append("\ttargetReheat : $targetReheat\n") + append("}\n") + } + + fun toTlv(tlvTag: Tag, tlvWriter: TlvWriter) { + tlvWriter.apply { + startStructure(tlvTag) + put(ContextSpecificTag(TAG_DURATION), duration) + if (oneShot.isPresent) { + val optoneShot = oneShot.get() + put(ContextSpecificTag(TAG_ONE_SHOT), optoneShot) + } + if (emergencyBoost.isPresent) { + val optemergencyBoost = emergencyBoost.get() + put(ContextSpecificTag(TAG_EMERGENCY_BOOST), optemergencyBoost) + } + if (temporarySetpoint.isPresent) { + val opttemporarySetpoint = temporarySetpoint.get() + put(ContextSpecificTag(TAG_TEMPORARY_SETPOINT), opttemporarySetpoint) + } + if (targetPercentage.isPresent) { + val opttargetPercentage = targetPercentage.get() + put(ContextSpecificTag(TAG_TARGET_PERCENTAGE), opttargetPercentage) + } + if (targetReheat.isPresent) { + val opttargetReheat = targetReheat.get() + put(ContextSpecificTag(TAG_TARGET_REHEAT), opttargetReheat) + } + endStructure() + } + } + + companion object { + private const val TAG_DURATION = 0 + private const val TAG_ONE_SHOT = 1 + private const val TAG_EMERGENCY_BOOST = 2 + private const val TAG_TEMPORARY_SETPOINT = 3 + private const val TAG_TARGET_PERCENTAGE = 4 + private const val TAG_TARGET_REHEAT = 5 + + fun fromTlv( + tlvTag: Tag, + tlvReader: TlvReader, + ): WaterHeaterManagementClusterWaterHeaterBoostInfoStruct { + tlvReader.enterStructure(tlvTag) + val duration = tlvReader.getULong(ContextSpecificTag(TAG_DURATION)) + val oneShot = + if (tlvReader.isNextTag(ContextSpecificTag(TAG_ONE_SHOT))) { + Optional.of(tlvReader.getBoolean(ContextSpecificTag(TAG_ONE_SHOT))) + } else { + Optional.empty() + } + val emergencyBoost = + if (tlvReader.isNextTag(ContextSpecificTag(TAG_EMERGENCY_BOOST))) { + Optional.of(tlvReader.getBoolean(ContextSpecificTag(TAG_EMERGENCY_BOOST))) + } else { + Optional.empty() + } + val temporarySetpoint = + if (tlvReader.isNextTag(ContextSpecificTag(TAG_TEMPORARY_SETPOINT))) { + Optional.of(tlvReader.getInt(ContextSpecificTag(TAG_TEMPORARY_SETPOINT))) + } else { + Optional.empty() + } + val targetPercentage = + if (tlvReader.isNextTag(ContextSpecificTag(TAG_TARGET_PERCENTAGE))) { + Optional.of(tlvReader.getUInt(ContextSpecificTag(TAG_TARGET_PERCENTAGE))) + } else { + Optional.empty() + } + val targetReheat = + if (tlvReader.isNextTag(ContextSpecificTag(TAG_TARGET_REHEAT))) { + Optional.of(tlvReader.getUInt(ContextSpecificTag(TAG_TARGET_REHEAT))) + } else { + Optional.empty() + } + + tlvReader.exitContainer() + + return WaterHeaterManagementClusterWaterHeaterBoostInfoStruct( + duration, + oneShot, + emergencyBoost, + temporarySetpoint, + targetPercentage, + targetReheat, + ) + } + } +} diff --git a/src/controller/java/generated/java/matter/controller/cluster/clusters/WaterHeaterManagementCluster.kt b/src/controller/java/generated/java/matter/controller/cluster/clusters/WaterHeaterManagementCluster.kt index 7d0ce89e2a612e..5f7fb25dd58f77 100644 --- a/src/controller/java/generated/java/matter/controller/cluster/clusters/WaterHeaterManagementCluster.kt +++ b/src/controller/java/generated/java/matter/controller/cluster/clusters/WaterHeaterManagementCluster.kt @@ -86,12 +86,7 @@ class WaterHeaterManagementCluster( } suspend fun boost( - duration: UInt, - oneShot: Boolean?, - emergencyBoost: Boolean?, - temporarySetpoint: Short?, - targetPercentage: UByte?, - targetReheat: UByte?, + boostInfo: WaterHeaterManagementClusterWaterHeaterBoostInfoStruct, timedInvokeTimeout: Duration? = null, ) { val commandId: UInt = 0u @@ -99,29 +94,8 @@ class WaterHeaterManagementCluster( val tlvWriter = TlvWriter() tlvWriter.startStructure(AnonymousTag) - val TAG_DURATION_REQ: Int = 0 - tlvWriter.put(ContextSpecificTag(TAG_DURATION_REQ), duration) - - val TAG_ONE_SHOT_REQ: Int = 1 - oneShot?.let { tlvWriter.put(ContextSpecificTag(TAG_ONE_SHOT_REQ), oneShot) } - - val TAG_EMERGENCY_BOOST_REQ: Int = 2 - emergencyBoost?.let { - tlvWriter.put(ContextSpecificTag(TAG_EMERGENCY_BOOST_REQ), emergencyBoost) - } - - val TAG_TEMPORARY_SETPOINT_REQ: Int = 3 - temporarySetpoint?.let { - tlvWriter.put(ContextSpecificTag(TAG_TEMPORARY_SETPOINT_REQ), temporarySetpoint) - } - - val TAG_TARGET_PERCENTAGE_REQ: Int = 4 - targetPercentage?.let { - tlvWriter.put(ContextSpecificTag(TAG_TARGET_PERCENTAGE_REQ), targetPercentage) - } - - val TAG_TARGET_REHEAT_REQ: Int = 5 - targetReheat?.let { tlvWriter.put(ContextSpecificTag(TAG_TARGET_REHEAT_REQ), targetReheat) } + val TAG_BOOST_INFO_REQ: Int = 0 + boostInfo.toTlv(ContextSpecificTag(TAG_BOOST_INFO_REQ), tlvWriter) tlvWriter.endStructure() val request: InvokeRequest = diff --git a/src/controller/java/generated/java/matter/controller/cluster/eventstructs/WaterHeaterManagementClusterBoostStartedEvent.kt b/src/controller/java/generated/java/matter/controller/cluster/eventstructs/WaterHeaterManagementClusterBoostStartedEvent.kt new file mode 100644 index 00000000000000..44e51796c1fa50 --- /dev/null +++ b/src/controller/java/generated/java/matter/controller/cluster/eventstructs/WaterHeaterManagementClusterBoostStartedEvent.kt @@ -0,0 +1,57 @@ +/* + * + * Copyright (c) 2023 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. + */ +package matter.controller.cluster.eventstructs + +import matter.controller.cluster.* +import matter.tlv.ContextSpecificTag +import matter.tlv.Tag +import matter.tlv.TlvReader +import matter.tlv.TlvWriter + +class WaterHeaterManagementClusterBoostStartedEvent( + val boostInfo: + matter.controller.cluster.structs.WaterHeaterManagementClusterWaterHeaterBoostInfoStruct +) { + override fun toString(): String = buildString { + append("WaterHeaterManagementClusterBoostStartedEvent {\n") + append("\tboostInfo : $boostInfo\n") + append("}\n") + } + + fun toTlv(tlvTag: Tag, tlvWriter: TlvWriter) { + tlvWriter.apply { + startStructure(tlvTag) + boostInfo.toTlv(ContextSpecificTag(TAG_BOOST_INFO), this) + endStructure() + } + } + + companion object { + private const val TAG_BOOST_INFO = 0 + + fun fromTlv(tlvTag: Tag, tlvReader: TlvReader): WaterHeaterManagementClusterBoostStartedEvent { + tlvReader.enterStructure(tlvTag) + val boostInfo = + matter.controller.cluster.structs.WaterHeaterManagementClusterWaterHeaterBoostInfoStruct + .fromTlv(ContextSpecificTag(TAG_BOOST_INFO), tlvReader) + + tlvReader.exitContainer() + + return WaterHeaterManagementClusterBoostStartedEvent(boostInfo) + } + } +} diff --git a/src/controller/java/generated/java/matter/controller/cluster/files.gni b/src/controller/java/generated/java/matter/controller/cluster/files.gni index 844c6692f5b9ad..62769c99e0bb89 100644 --- a/src/controller/java/generated/java/matter/controller/cluster/files.gni +++ b/src/controller/java/generated/java/matter/controller/cluster/files.gni @@ -156,6 +156,7 @@ matter_structs_sources = [ "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/UnitTestingClusterTestGlobalStruct.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/UnitTestingClusterTestListStructOctet.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/UserLabelClusterLabelStruct.kt", + "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/WaterHeaterManagementClusterWaterHeaterBoostInfoStruct.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/WaterHeaterModeClusterModeOptionStruct.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/WaterHeaterModeClusterModeTagStruct.kt", ] @@ -241,6 +242,7 @@ matter_eventstructs_sources = [ "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/eventstructs/UnitTestingClusterTestFabricScopedEventEvent.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/eventstructs/ValveConfigurationAndControlClusterValveFaultEvent.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/eventstructs/ValveConfigurationAndControlClusterValveStateChangedEvent.kt", + "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/eventstructs/WaterHeaterManagementClusterBoostStartedEvent.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/eventstructs/WiFiNetworkDiagnosticsClusterAssociationFailureEvent.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/eventstructs/WiFiNetworkDiagnosticsClusterConnectionStatusEvent.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/eventstructs/WiFiNetworkDiagnosticsClusterDisconnectionEvent.kt", diff --git a/src/controller/java/generated/java/matter/controller/cluster/structs/WaterHeaterManagementClusterWaterHeaterBoostInfoStruct.kt b/src/controller/java/generated/java/matter/controller/cluster/structs/WaterHeaterManagementClusterWaterHeaterBoostInfoStruct.kt new file mode 100644 index 00000000000000..0b2fede4f1537a --- /dev/null +++ b/src/controller/java/generated/java/matter/controller/cluster/structs/WaterHeaterManagementClusterWaterHeaterBoostInfoStruct.kt @@ -0,0 +1,130 @@ +/* + * + * Copyright (c) 2023 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. + */ +package matter.controller.cluster.structs + +import java.util.Optional +import matter.controller.cluster.* +import matter.tlv.ContextSpecificTag +import matter.tlv.Tag +import matter.tlv.TlvReader +import matter.tlv.TlvWriter + +class WaterHeaterManagementClusterWaterHeaterBoostInfoStruct( + val duration: UInt, + val oneShot: Optional, + val emergencyBoost: Optional, + val temporarySetpoint: Optional, + val targetPercentage: Optional, + val targetReheat: Optional, +) { + override fun toString(): String = buildString { + append("WaterHeaterManagementClusterWaterHeaterBoostInfoStruct {\n") + append("\tduration : $duration\n") + append("\toneShot : $oneShot\n") + append("\temergencyBoost : $emergencyBoost\n") + append("\ttemporarySetpoint : $temporarySetpoint\n") + append("\ttargetPercentage : $targetPercentage\n") + append("\ttargetReheat : $targetReheat\n") + append("}\n") + } + + fun toTlv(tlvTag: Tag, tlvWriter: TlvWriter) { + tlvWriter.apply { + startStructure(tlvTag) + put(ContextSpecificTag(TAG_DURATION), duration) + if (oneShot.isPresent) { + val optoneShot = oneShot.get() + put(ContextSpecificTag(TAG_ONE_SHOT), optoneShot) + } + if (emergencyBoost.isPresent) { + val optemergencyBoost = emergencyBoost.get() + put(ContextSpecificTag(TAG_EMERGENCY_BOOST), optemergencyBoost) + } + if (temporarySetpoint.isPresent) { + val opttemporarySetpoint = temporarySetpoint.get() + put(ContextSpecificTag(TAG_TEMPORARY_SETPOINT), opttemporarySetpoint) + } + if (targetPercentage.isPresent) { + val opttargetPercentage = targetPercentage.get() + put(ContextSpecificTag(TAG_TARGET_PERCENTAGE), opttargetPercentage) + } + if (targetReheat.isPresent) { + val opttargetReheat = targetReheat.get() + put(ContextSpecificTag(TAG_TARGET_REHEAT), opttargetReheat) + } + endStructure() + } + } + + companion object { + private const val TAG_DURATION = 0 + private const val TAG_ONE_SHOT = 1 + private const val TAG_EMERGENCY_BOOST = 2 + private const val TAG_TEMPORARY_SETPOINT = 3 + private const val TAG_TARGET_PERCENTAGE = 4 + private const val TAG_TARGET_REHEAT = 5 + + fun fromTlv( + tlvTag: Tag, + tlvReader: TlvReader, + ): WaterHeaterManagementClusterWaterHeaterBoostInfoStruct { + tlvReader.enterStructure(tlvTag) + val duration = tlvReader.getUInt(ContextSpecificTag(TAG_DURATION)) + val oneShot = + if (tlvReader.isNextTag(ContextSpecificTag(TAG_ONE_SHOT))) { + Optional.of(tlvReader.getBoolean(ContextSpecificTag(TAG_ONE_SHOT))) + } else { + Optional.empty() + } + val emergencyBoost = + if (tlvReader.isNextTag(ContextSpecificTag(TAG_EMERGENCY_BOOST))) { + Optional.of(tlvReader.getBoolean(ContextSpecificTag(TAG_EMERGENCY_BOOST))) + } else { + Optional.empty() + } + val temporarySetpoint = + if (tlvReader.isNextTag(ContextSpecificTag(TAG_TEMPORARY_SETPOINT))) { + Optional.of(tlvReader.getShort(ContextSpecificTag(TAG_TEMPORARY_SETPOINT))) + } else { + Optional.empty() + } + val targetPercentage = + if (tlvReader.isNextTag(ContextSpecificTag(TAG_TARGET_PERCENTAGE))) { + Optional.of(tlvReader.getUByte(ContextSpecificTag(TAG_TARGET_PERCENTAGE))) + } else { + Optional.empty() + } + val targetReheat = + if (tlvReader.isNextTag(ContextSpecificTag(TAG_TARGET_REHEAT))) { + Optional.of(tlvReader.getUByte(ContextSpecificTag(TAG_TARGET_REHEAT))) + } else { + Optional.empty() + } + + tlvReader.exitContainer() + + return WaterHeaterManagementClusterWaterHeaterBoostInfoStruct( + duration, + oneShot, + emergencyBoost, + temporarySetpoint, + targetPercentage, + targetReheat, + ) + } + } +} diff --git a/src/controller/java/zap-generated/CHIPEventTLVValueDecoder.cpp b/src/controller/java/zap-generated/CHIPEventTLVValueDecoder.cpp index 89a4a256880158..b9d5e76c909135 100644 --- a/src/controller/java/zap-generated/CHIPEventTLVValueDecoder.cpp +++ b/src/controller/java/zap-generated/CHIPEventTLVValueDecoder.cpp @@ -5050,6 +5050,193 @@ jobject DecodeEventValue(const app::ConcreteEventPath & aPath, TLV::TLVReader & using namespace app::Clusters::WaterHeaterManagement; switch (aPath.mEventId) { + case Events::BoostStarted::Id: { + Events::BoostStarted::DecodableType cppValue; + *aError = app::DataModel::Decode(aReader, cppValue); + if (*aError != CHIP_NO_ERROR) + { + return nullptr; + } + jobject value_boostInfo; + jobject value_boostInfo_duration; + std::string value_boostInfo_durationClassName = "java/lang/Long"; + std::string value_boostInfo_durationCtorSignature = "(J)V"; + jlong jnivalue_boostInfo_duration = static_cast(cppValue.boostInfo.duration); + chip::JniReferences::GetInstance().CreateBoxedObject(value_boostInfo_durationClassName.c_str(), + value_boostInfo_durationCtorSignature.c_str(), + jnivalue_boostInfo_duration, value_boostInfo_duration); + jobject value_boostInfo_oneShot; + if (!cppValue.boostInfo.oneShot.HasValue()) + { + chip::JniReferences::GetInstance().CreateOptional(nullptr, value_boostInfo_oneShot); + } + else + { + jobject value_boostInfo_oneShotInsideOptional; + std::string value_boostInfo_oneShotInsideOptionalClassName = "java/lang/Boolean"; + std::string value_boostInfo_oneShotInsideOptionalCtorSignature = "(Z)V"; + jboolean jnivalue_boostInfo_oneShotInsideOptional = static_cast(cppValue.boostInfo.oneShot.Value()); + chip::JniReferences::GetInstance().CreateBoxedObject( + value_boostInfo_oneShotInsideOptionalClassName.c_str(), + value_boostInfo_oneShotInsideOptionalCtorSignature.c_str(), jnivalue_boostInfo_oneShotInsideOptional, + value_boostInfo_oneShotInsideOptional); + chip::JniReferences::GetInstance().CreateOptional(value_boostInfo_oneShotInsideOptional, value_boostInfo_oneShot); + } + jobject value_boostInfo_emergencyBoost; + if (!cppValue.boostInfo.emergencyBoost.HasValue()) + { + chip::JniReferences::GetInstance().CreateOptional(nullptr, value_boostInfo_emergencyBoost); + } + else + { + jobject value_boostInfo_emergencyBoostInsideOptional; + std::string value_boostInfo_emergencyBoostInsideOptionalClassName = "java/lang/Boolean"; + std::string value_boostInfo_emergencyBoostInsideOptionalCtorSignature = "(Z)V"; + jboolean jnivalue_boostInfo_emergencyBoostInsideOptional = + static_cast(cppValue.boostInfo.emergencyBoost.Value()); + chip::JniReferences::GetInstance().CreateBoxedObject( + value_boostInfo_emergencyBoostInsideOptionalClassName.c_str(), + value_boostInfo_emergencyBoostInsideOptionalCtorSignature.c_str(), + jnivalue_boostInfo_emergencyBoostInsideOptional, value_boostInfo_emergencyBoostInsideOptional); + chip::JniReferences::GetInstance().CreateOptional(value_boostInfo_emergencyBoostInsideOptional, + value_boostInfo_emergencyBoost); + } + jobject value_boostInfo_temporarySetpoint; + if (!cppValue.boostInfo.temporarySetpoint.HasValue()) + { + chip::JniReferences::GetInstance().CreateOptional(nullptr, value_boostInfo_temporarySetpoint); + } + else + { + jobject value_boostInfo_temporarySetpointInsideOptional; + std::string value_boostInfo_temporarySetpointInsideOptionalClassName = "java/lang/Integer"; + std::string value_boostInfo_temporarySetpointInsideOptionalCtorSignature = "(I)V"; + jint jnivalue_boostInfo_temporarySetpointInsideOptional = + static_cast(cppValue.boostInfo.temporarySetpoint.Value()); + chip::JniReferences::GetInstance().CreateBoxedObject( + value_boostInfo_temporarySetpointInsideOptionalClassName.c_str(), + value_boostInfo_temporarySetpointInsideOptionalCtorSignature.c_str(), + jnivalue_boostInfo_temporarySetpointInsideOptional, value_boostInfo_temporarySetpointInsideOptional); + chip::JniReferences::GetInstance().CreateOptional(value_boostInfo_temporarySetpointInsideOptional, + value_boostInfo_temporarySetpoint); + } + jobject value_boostInfo_targetPercentage; + if (!cppValue.boostInfo.targetPercentage.HasValue()) + { + chip::JniReferences::GetInstance().CreateOptional(nullptr, value_boostInfo_targetPercentage); + } + else + { + jobject value_boostInfo_targetPercentageInsideOptional; + std::string value_boostInfo_targetPercentageInsideOptionalClassName = "java/lang/Integer"; + std::string value_boostInfo_targetPercentageInsideOptionalCtorSignature = "(I)V"; + jint jnivalue_boostInfo_targetPercentageInsideOptional = + static_cast(cppValue.boostInfo.targetPercentage.Value()); + chip::JniReferences::GetInstance().CreateBoxedObject( + value_boostInfo_targetPercentageInsideOptionalClassName.c_str(), + value_boostInfo_targetPercentageInsideOptionalCtorSignature.c_str(), + jnivalue_boostInfo_targetPercentageInsideOptional, value_boostInfo_targetPercentageInsideOptional); + chip::JniReferences::GetInstance().CreateOptional(value_boostInfo_targetPercentageInsideOptional, + value_boostInfo_targetPercentage); + } + jobject value_boostInfo_targetReheat; + if (!cppValue.boostInfo.targetReheat.HasValue()) + { + chip::JniReferences::GetInstance().CreateOptional(nullptr, value_boostInfo_targetReheat); + } + else + { + jobject value_boostInfo_targetReheatInsideOptional; + std::string value_boostInfo_targetReheatInsideOptionalClassName = "java/lang/Integer"; + std::string value_boostInfo_targetReheatInsideOptionalCtorSignature = "(I)V"; + jint jnivalue_boostInfo_targetReheatInsideOptional = static_cast(cppValue.boostInfo.targetReheat.Value()); + chip::JniReferences::GetInstance().CreateBoxedObject( + value_boostInfo_targetReheatInsideOptionalClassName.c_str(), + value_boostInfo_targetReheatInsideOptionalCtorSignature.c_str(), jnivalue_boostInfo_targetReheatInsideOptional, + value_boostInfo_targetReheatInsideOptional); + chip::JniReferences::GetInstance().CreateOptional(value_boostInfo_targetReheatInsideOptional, + value_boostInfo_targetReheat); + } + + jclass waterHeaterBoostInfoStructStructClass_0; + err = chip::JniReferences::GetInstance().GetLocalClassRef( + env, "chip/devicecontroller/ChipStructs$WaterHeaterManagementClusterWaterHeaterBoostInfoStruct", + waterHeaterBoostInfoStructStructClass_0); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Could not find class ChipStructs$WaterHeaterManagementClusterWaterHeaterBoostInfoStruct"); + return nullptr; + } + + jmethodID waterHeaterBoostInfoStructStructCtor_0; + err = chip::JniReferences::GetInstance().FindMethod(env, waterHeaterBoostInfoStructStructClass_0, "", + "(Ljava/lang/Long;Ljava/util/Optional;Ljava/util/Optional;Ljava/" + "util/Optional;Ljava/util/Optional;Ljava/util/Optional;)V", + &waterHeaterBoostInfoStructStructCtor_0); + if (err != CHIP_NO_ERROR || waterHeaterBoostInfoStructStructCtor_0 == nullptr) + { + ChipLogError(Zcl, "Could not find ChipStructs$WaterHeaterManagementClusterWaterHeaterBoostInfoStruct constructor"); + return nullptr; + } + + value_boostInfo = + env->NewObject(waterHeaterBoostInfoStructStructClass_0, waterHeaterBoostInfoStructStructCtor_0, + value_boostInfo_duration, value_boostInfo_oneShot, value_boostInfo_emergencyBoost, + value_boostInfo_temporarySetpoint, value_boostInfo_targetPercentage, value_boostInfo_targetReheat); + + jclass boostStartedStructClass; + err = chip::JniReferences::GetInstance().GetLocalClassRef( + env, "chip/devicecontroller/ChipEventStructs$WaterHeaterManagementClusterBoostStartedEvent", + boostStartedStructClass); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Could not find class ChipEventStructs$WaterHeaterManagementClusterBoostStartedEvent"); + return nullptr; + } + + jmethodID boostStartedStructCtor; + err = chip::JniReferences::GetInstance().FindMethod( + env, boostStartedStructClass, "", + "(Lchip/devicecontroller/ChipStructs$WaterHeaterManagementClusterWaterHeaterBoostInfoStruct;)V", + &boostStartedStructCtor); + if (err != CHIP_NO_ERROR || boostStartedStructCtor == nullptr) + { + ChipLogError(Zcl, "Could not find ChipEventStructs$WaterHeaterManagementClusterBoostStartedEvent constructor"); + return nullptr; + } + + jobject value = env->NewObject(boostStartedStructClass, boostStartedStructCtor, value_boostInfo); + + return value; + } + case Events::BoostEnded::Id: { + Events::BoostEnded::DecodableType cppValue; + *aError = app::DataModel::Decode(aReader, cppValue); + if (*aError != CHIP_NO_ERROR) + { + return nullptr; + } + jclass boostEndedStructClass; + err = chip::JniReferences::GetInstance().GetLocalClassRef( + env, "chip/devicecontroller/ChipEventStructs$WaterHeaterManagementClusterBoostEndedEvent", boostEndedStructClass); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Could not find class ChipEventStructs$WaterHeaterManagementClusterBoostEndedEvent"); + return nullptr; + } + + jmethodID boostEndedStructCtor; + err = chip::JniReferences::GetInstance().FindMethod(env, boostEndedStructClass, "", "()V", &boostEndedStructCtor); + if (err != CHIP_NO_ERROR || boostEndedStructCtor == nullptr) + { + ChipLogError(Zcl, "Could not find ChipEventStructs$WaterHeaterManagementClusterBoostEndedEvent constructor"); + return nullptr; + } + + jobject value = env->NewObject(boostEndedStructClass, boostEndedStructCtor); + + return value; + } default: *aError = CHIP_ERROR_IM_MALFORMED_EVENT_PATH_IB; break; diff --git a/src/controller/python/ChipDeviceController-ScriptBinding.cpp b/src/controller/python/ChipDeviceController-ScriptBinding.cpp index d9b557bb62be67..b31cb5b7b8a4e6 100644 --- a/src/controller/python/ChipDeviceController-ScriptBinding.cpp +++ b/src/controller/python/ChipDeviceController-ScriptBinding.cpp @@ -839,7 +839,11 @@ PyChipError pychip_IsSessionOverTCPConnection(chip::OperationalDeviceProxy * dev VerifyOrReturnError(deviceProxy->GetSecureSession().HasValue(), ToPyChipError(CHIP_ERROR_MISSING_SECURE_SESSION)); VerifyOrReturnError(isSessionOverTCP != nullptr, ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT)); +#if INET_CONFIG_ENABLE_TCP_ENDPOINT *isSessionOverTCP = deviceProxy->GetSecureSession().Value()->AsSecureSession()->GetTCPConnection() != nullptr; +#else + *isSessionOverTCP = false; +#endif return ToPyChipError(CHIP_NO_ERROR); } @@ -859,6 +863,7 @@ PyChipError pychip_IsActiveSession(chip::OperationalDeviceProxy * deviceProxy, b PyChipError pychip_CloseTCPConnectionWithPeer(chip::OperationalDeviceProxy * deviceProxy) { +#if INET_CONFIG_ENABLE_TCP_ENDPOINT VerifyOrReturnError(deviceProxy->GetSecureSession().HasValue(), ToPyChipError(CHIP_ERROR_MISSING_SECURE_SESSION)); VerifyOrReturnError(deviceProxy->GetSecureSession().Value()->AsSecureSession()->AllowsLargePayload(), ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT)); @@ -867,6 +872,9 @@ PyChipError pychip_CloseTCPConnectionWithPeer(chip::OperationalDeviceProxy * dev deviceProxy->GetSecureSession().Value()->AsSecureSession()->GetTCPConnection(), /* shouldAbort = */ false); return ToPyChipError(CHIP_NO_ERROR); +#else + return ToPyChipError(CHIP_ERROR_NOT_IMPLEMENTED); +#endif } PyChipError pychip_FreeOperationalDeviceProxy(chip::OperationalDeviceProxy * deviceProxy) diff --git a/src/controller/python/chip/CertificateAuthority.py b/src/controller/python/chip/CertificateAuthority.py index 0fbfcfdbdb93b8..23c12698ec2cf4 100644 --- a/src/controller/python/chip/CertificateAuthority.py +++ b/src/controller/python/chip/CertificateAuthority.py @@ -21,7 +21,7 @@ import ctypes import logging from ctypes import c_void_p -from typing import List +from typing import List, Optional import chip.exceptions from chip import ChipStack, FabricAdmin @@ -92,7 +92,7 @@ def __init__(self, chipStack: ChipStack.ChipStack, caIndex: int, persistentStora raise ValueError("Encountered error initializing OpCreds adapter") self._isActive = True - self._activeAdmins = [] + self._activeAdmins: List[FabricAdmin.FabricAdmin] = [] def LoadFabricAdminsFromStorage(self): ''' If FabricAdmins had been setup previously, this re-creates them using information from persistent storage. @@ -221,14 +221,13 @@ def __init__(self, chipStack: ChipStack.ChipStack, persistentStorage: Persistent persistentStorage: If provided, over-rides the default instance in the provided chipStack when initializing CertificateAuthority instances. ''' - self._activeCaIndexList = [] self._chipStack = chipStack if (persistentStorage is None): persistentStorage = self._chipStack.GetStorageManager() self._persistentStorage = persistentStorage - self._activeCaList = [] + self._activeCaList: List[CertificateAuthority] = [] self._isActive = True def _AllocateNextCaIndex(self): @@ -259,7 +258,7 @@ def LoadAuthoritiesFromStorage(self): ca = self.NewCertificateAuthority(int(caIndex)) ca.LoadFabricAdminsFromStorage() - def NewCertificateAuthority(self, caIndex: int = None, maximizeCertChains: bool = False): + def NewCertificateAuthority(self, caIndex: Optional[int] = None, maximizeCertChains: bool = False): ''' Creates a new CertificateAuthority instance with the provided CA Index and the PersistentStorage instance previously setup in the constructor. diff --git a/src/controller/python/chip/ChipBluezMgr.py b/src/controller/python/chip/ChipBluezMgr.py index bacf383710eccd..493d0f783f4264 100644 --- a/src/controller/python/chip/ChipBluezMgr.py +++ b/src/controller/python/chip/ChipBluezMgr.py @@ -33,18 +33,18 @@ import uuid from ctypes import CFUNCTYPE, PYFUNCTYPE, c_int, c_void_p, cast, pythonapi -import dbus -import dbus.mainloop.glib -import dbus.service +import dbus # type: ignore +import dbus.mainloop.glib # type: ignore +import dbus.service # type: ignore from .ChipBleBase import ChipBleBase from .ChipBleUtility import BLE_ERROR_REMOTE_DEVICE_DISCONNECTED, BleDisconnectEvent, ParseServiceData try: - from gi.repository import GObject + from gi.repository import GObject # type: ignore except Exception: logging.exception("Unable to find GObject from gi.repository") - from pgi.repository import GObject + from pgi.repository import GObject # type: ignore chip_service = uuid.UUID("0000FFF6-0000-1000-8000-00805F9B34FB") chip_tx = uuid.UUID("18EE2EF5-263D-4559-959F-4F9C429F9D11") diff --git a/src/controller/python/chip/ChipCommissionableNodeCtrl.py b/src/controller/python/chip/ChipCommissionableNodeCtrl.py index f487ffd5edf7f7..c41dfc51c31d24 100644 --- a/src/controller/python/chip/ChipCommissionableNodeCtrl.py +++ b/src/controller/python/chip/ChipCommissionableNodeCtrl.py @@ -26,6 +26,7 @@ from __future__ import absolute_import, print_function from ctypes import CDLL, POINTER, c_void_p, pointer +from typing import Any from .ChipStack import ChipStack from .native import PyChipError @@ -49,7 +50,7 @@ class ChipCommissionableNodeController(object): def __init__(self, chipStack: ChipStack): self.commissionableNodeCtrl = None self._ChipStack = chipStack - self._dmLib = None + self._dmLib: Any = None self._InitLib() diff --git a/src/controller/python/chip/ChipStack.py b/src/controller/python/chip/ChipStack.py index b717859c70351d..e029b77f0a7476 100644 --- a/src/controller/python/chip/ChipStack.py +++ b/src/controller/python/chip/ChipStack.py @@ -31,6 +31,7 @@ import os from ctypes import CFUNCTYPE, Structure, c_bool, c_char_p, c_uint16, c_uint32, c_void_p, py_object, pythonapi from threading import Condition, Lock +from typing import Any, Optional import chip.native from chip.native import PyChipError @@ -90,7 +91,7 @@ def __call__(self): self._cv.notify_all() pythonapi.Py_DecRef(py_object(self)) - def Wait(self, timeoutMs: int = None): + def Wait(self, timeoutMs: Optional[int] = None): timeout = None if timeoutMs is not None: timeout = float(timeoutMs) / 1000 @@ -143,7 +144,7 @@ class ChipStack(object): def __init__(self, persistentStoragePath: str, enableServerInteractions=True): builtins.enableDebugMode = False - self._ChipStackLib = None + self._ChipStackLib: Any = None self._chipDLLPath = None self.devMgr = None self._enableServerInteractions = enableServerInteractions @@ -209,14 +210,14 @@ def Shutdown(self): delattr(builtins, "chipStack") - def Call(self, callFunct, timeoutMs: int = None): + def Call(self, callFunct, timeoutMs: Optional[int] = None): '''Run a Python function on CHIP stack, and wait for the response. This function is a wrapper of PostTaskOnChipThread, which includes some handling of application specific logics. Calling this function on CHIP on CHIP mainloop thread will cause deadlock. ''' return self.PostTaskOnChipThread(callFunct).Wait(timeoutMs) - async def CallAsyncWithResult(self, callFunct, timeoutMs: int = None): + async def CallAsyncWithResult(self, callFunct, timeoutMs: Optional[int] = None): '''Run a Python function on CHIP stack, and wait for the response. This function will post a task on CHIP mainloop and waits for the call response in a asyncio friendly manner. ''' @@ -232,7 +233,7 @@ async def CallAsyncWithResult(self, callFunct, timeoutMs: int = None): return await asyncio.wait_for(callObj.future, timeoutMs / 1000 if timeoutMs else None) - async def CallAsync(self, callFunct, timeoutMs: int = None) -> None: + async def CallAsync(self, callFunct, timeoutMs: Optional[int] = None) -> None: '''Run a Python function on CHIP stack, and wait for the response.''' res: PyChipError = await self.CallAsyncWithResult(callFunct, timeoutMs) res.raise_on_error() diff --git a/src/controller/python/chip/FabricAdmin.py b/src/controller/python/chip/FabricAdmin.py index d9e2e35cb2bcc4..a7c5fb86166339 100644 --- a/src/controller/python/chip/FabricAdmin.py +++ b/src/controller/python/chip/FabricAdmin.py @@ -19,7 +19,7 @@ from __future__ import annotations import logging -from typing import List +from typing import List, Optional from chip import CertificateAuthority, ChipDeviceCtrl from chip.crypto import p256keypair @@ -61,9 +61,9 @@ def __init__(self, certificateAuthority: CertificateAuthority.CertificateAuthori LOGGER.info(f"New FabricAdmin: FabricId: 0x{self._fabricId:016X}, VendorId = 0x{self.vendorId:04X}") self._isActive = True - self._activeControllers = [] + self._activeControllers: List[ChipDeviceCtrl.ChipDeviceController] = [] - def NewController(self, nodeId: int = None, paaTrustStorePath: str = "", + def NewController(self, nodeId: Optional[int] = None, paaTrustStorePath: str = "", useTestCommissioner: bool = False, catTags: List[int] = [], keypair: p256keypair.P256Keypair = None): ''' Create a new chip.ChipDeviceCtrl.ChipDeviceController instance on this fabric. diff --git a/src/controller/python/chip/ble/scan_devices.py b/src/controller/python/chip/ble/scan_devices.py index 86cc0a321e8c7d..91d9778e92238f 100644 --- a/src/controller/python/chip/ble/scan_devices.py +++ b/src/controller/python/chip/ble/scan_devices.py @@ -31,12 +31,12 @@ def ScanFoundCallback(closure, address: str, discriminator: int, vendor: int, @ScanDoneCallback -def ScanDoneCallback(closure): +def ScanIsDoneCallback(closure): closure.OnScanComplete() @ScanErrorCallback -def ScanErrorCallback(closure, errorCode: int): +def ScanHasErrorCallback(closure, errorCode: int): closure.OnScanError(errorCode) @@ -106,7 +106,7 @@ def DiscoverSync(timeoutMs: int, adapter=None) -> Generator[DeviceInfo, None, No scanner = handle.pychip_ble_scanner_start( ctypes.py_object(receiver), handle.pychip_ble_adapter_list_get_raw_adapter(nativeList), - timeoutMs, ScanFoundCallback, ScanDoneCallback, ScanErrorCallback) + timeoutMs, ScanFoundCallback, ScanIsDoneCallback, ScanHasErrorCallback) if scanner == 0: raise Exception('Failed to start BLE scan') diff --git a/src/controller/python/chip/clusters/CHIPClusters.py b/src/controller/python/chip/clusters/CHIPClusters.py index c573d69dc78e6c..028810afd4eb77 100644 --- a/src/controller/python/chip/clusters/CHIPClusters.py +++ b/src/controller/python/chip/clusters/CHIPClusters.py @@ -6658,12 +6658,7 @@ class ChipClusters: "commandId": 0x00000000, "commandName": "Boost", "args": { - "duration": "int", - "oneShot": "bool", - "emergencyBoost": "bool", - "temporarySetpoint": "int", - "targetPercentage": "int", - "targetReheat": "int", + "boostInfo": "WaterHeaterBoostInfoStruct", }, }, 0x00000001: { diff --git a/src/controller/python/chip/clusters/Command.py b/src/controller/python/chip/clusters/Command.py index 785bb3d3daf47f..e99718cae269e4 100644 --- a/src/controller/python/chip/clusters/Command.py +++ b/src/controller/python/chip/clusters/Command.py @@ -382,7 +382,7 @@ async def SendBatchCommands(future: Future, eventLoop, device, commands: List[In ''' handle = chip.native.GetLibraryHandle() - responseTypes = [] + responseTypes: List[Type] = [] pyBatchCommandsData = _BuildPyInvokeRequestData(commands, timedRequestTimeoutMs, responseTypes) transaction = AsyncBatchCommandsTransaction(future, eventLoop, responseTypes) @@ -417,7 +417,7 @@ def TestOnlySendBatchCommands(future: Future, eventLoop, device, commands: List[ handle = chip.native.GetLibraryHandle() - responseTypes = [] + responseTypes: List[Type] = [] pyBatchCommandsData = _BuildPyInvokeRequestData(commands, timedRequestTimeoutMs, responseTypes, suppressTimedRequestMessage=suppressTimedRequestMessage) diff --git a/src/controller/python/chip/clusters/Objects.py b/src/controller/python/chip/clusters/Objects.py index 405d8b8872bcba..cff7adf4ae6fbb 100644 --- a/src/controller/python/chip/clusters/Objects.py +++ b/src/controller/python/chip/clusters/Objects.py @@ -23909,28 +23909,16 @@ class Feature(IntFlag): kEnergyManagement = 0x1 kTankPercent = 0x2 - class WaterHeaterDemandBitmap(IntFlag): + class WaterHeaterHeatSourceBitmap(IntFlag): kImmersionElement1 = 0x1 kImmersionElement2 = 0x2 kHeatPump = 0x4 kBoiler = 0x8 kOther = 0x10 - class WaterHeaterTypeBitmap(IntFlag): - kImmersionElement1 = 0x1 - kImmersionElement2 = 0x2 - kHeatPump = 0x4 - kBoiler = 0x8 - kOther = 0x10 - - class Commands: + class Structs: @dataclass - class Boost(ClusterCommand): - cluster_id: typing.ClassVar[int] = 0x00000094 - command_id: typing.ClassVar[int] = 0x00000000 - is_client: typing.ClassVar[bool] = True - response_type: typing.ClassVar[str] = None - + class WaterHeaterBoostInfoStruct(ClusterObject): @ChipUtility.classproperty def descriptor(cls) -> ClusterObjectDescriptor: return ClusterObjectDescriptor( @@ -23950,6 +23938,23 @@ def descriptor(cls) -> ClusterObjectDescriptor: targetPercentage: 'typing.Optional[uint]' = None targetReheat: 'typing.Optional[uint]' = None + class Commands: + @dataclass + class Boost(ClusterCommand): + cluster_id: typing.ClassVar[int] = 0x00000094 + command_id: typing.ClassVar[int] = 0x00000000 + is_client: typing.ClassVar[bool] = True + response_type: typing.ClassVar[str] = None + + @ChipUtility.classproperty + def descriptor(cls) -> ClusterObjectDescriptor: + return ClusterObjectDescriptor( + Fields=[ + ClusterObjectFieldDescriptor(Label="boostInfo", Tag=0, Type=WaterHeaterManagement.Structs.WaterHeaterBoostInfoStruct), + ]) + + boostInfo: 'WaterHeaterManagement.Structs.WaterHeaterBoostInfoStruct' = field(default_factory=lambda: WaterHeaterManagement.Structs.WaterHeaterBoostInfoStruct()) + @dataclass class CancelBoost(ClusterCommand): cluster_id: typing.ClassVar[int] = 0x00000094 @@ -24156,6 +24161,42 @@ def attribute_type(cls) -> ClusterObjectFieldDescriptor: value: 'uint' = 0 + class Events: + @dataclass + class BoostStarted(ClusterEvent): + @ChipUtility.classproperty + def cluster_id(cls) -> int: + return 0x00000094 + + @ChipUtility.classproperty + def event_id(cls) -> int: + return 0x00000000 + + @ChipUtility.classproperty + def descriptor(cls) -> ClusterObjectDescriptor: + return ClusterObjectDescriptor( + Fields=[ + ClusterObjectFieldDescriptor(Label="boostInfo", Tag=0, Type=WaterHeaterManagement.Structs.WaterHeaterBoostInfoStruct), + ]) + + boostInfo: 'WaterHeaterManagement.Structs.WaterHeaterBoostInfoStruct' = field(default_factory=lambda: WaterHeaterManagement.Structs.WaterHeaterBoostInfoStruct()) + + @dataclass + class BoostEnded(ClusterEvent): + @ChipUtility.classproperty + def cluster_id(cls) -> int: + return 0x00000094 + + @ChipUtility.classproperty + def event_id(cls) -> int: + return 0x00000001 + + @ChipUtility.classproperty + def descriptor(cls) -> ClusterObjectDescriptor: + return ClusterObjectDescriptor( + Fields=[ + ]) + @dataclass class DemandResponseLoadControl(Cluster): diff --git a/src/controller/python/chip/commissioning/__init__.py b/src/controller/python/chip/commissioning/__init__.py index 2bf37ba74e2edf..c1105511c67b85 100644 --- a/src/controller/python/chip/commissioning/__init__.py +++ b/src/controller/python/chip/commissioning/__init__.py @@ -19,7 +19,7 @@ import dataclasses import enum import os -from typing import Set, Tuple, Union +from typing import Optional, Set, Tuple, Union ROOT_ENDPOINT_ID = 0 @@ -118,8 +118,8 @@ class GetCommissioneeCredentialsResponse: ipk: bytes case_admin_node: int admin_vendor_id: int - node_id: int = None - fabric_id: int = None + node_id: Optional[int] = None + fabric_id: Optional[int] = None class CredentialProvider: @@ -137,4 +137,4 @@ async def get_commissionee_credentials(self, request: GetCommissioneeCredentials class ExampleCredentialProvider: async def get_commissionee_credentials(self, request: GetCommissioneeCredentialsRequest) -> GetCommissioneeCredentialsResponse: - pass + raise NotImplementedError("This method needs to be implemented.") diff --git a/src/controller/python/chip/configuration/__init__.py b/src/controller/python/chip/configuration/__init__.py index c4c65bcf7f171e..67067ca43d07d5 100644 --- a/src/controller/python/chip/configuration/__init__.py +++ b/src/controller/python/chip/configuration/__init__.py @@ -14,7 +14,7 @@ # limitations under the License. # -from typing import Optional +from typing import Optional, cast # Represents the node ID that is to be used when creating device # controllers/commissioning devices @@ -45,7 +45,8 @@ def GetLocalNodeId() -> int: if _local_node_id is None: SetLocalNodeId(DEFAULT_LOCAL_NODE_ID) - return _local_node_id + # cast is used to tell mypy typechecker that _local_node_id will always be an int from this point onwards + return cast(int, _local_node_id) def SetCommissionerCAT(cat: int): @@ -68,4 +69,4 @@ def GetCommissionerCAT() -> int: if _local_cat is None: SetCommissionerCAT(DEFAULT_COMMISSIONER_CAT) - return _local_cat + return cast(int, _local_cat) diff --git a/src/controller/python/chip/discovery/__init__.py b/src/controller/python/chip/discovery/__init__.py index c818b5e22358ac..ba3ea815f81764 100644 --- a/src/controller/python/chip/discovery/__init__.py +++ b/src/controller/python/chip/discovery/__init__.py @@ -78,24 +78,24 @@ class PendingDiscovery: @dataclass class CommissionableNode(): - instanceName: str = None - hostName: str = None - port: int = None - longDiscriminator: int = None - vendorId: int = None - productId: int = None - commissioningMode: int = None - deviceType: int = None - deviceName: str = None - pairingInstruction: str = None - pairingHint: int = None - mrpRetryIntervalIdle: int = None - mrpRetryIntervalActive: int = None - mrpRetryActiveThreshold: int = None - supportsTcpClient: bool = None - supportsTcpServer: bool = None - isICDOperatingAsLIT: bool = None - addresses: List[str] = None + instanceName: Optional[str] = None + hostName: Optional[str] = None + port: Optional[int] = None + longDiscriminator: Optional[int] = None + vendorId: Optional[int] = None + productId: Optional[int] = None + commissioningMode: Optional[int] = None + deviceType: Optional[int] = None + deviceName: Optional[str] = None + pairingInstruction: Optional[str] = None + pairingHint: Optional[int] = None + mrpRetryIntervalIdle: Optional[int] = None + mrpRetryIntervalActive: Optional[int] = None + mrpRetryActiveThreshold: Optional[int] = None + supportsTcpClient: Optional[bool] = None + supportsTcpServer: Optional[bool] = None + isICDOperatingAsLIT: Optional[bool] = None + addresses: Optional[List[str]] = None rotatingId: Optional[str] = None diff --git a/src/controller/python/chip/internal/thread.py b/src/controller/python/chip/internal/thread.py index 9aef917279dbe3..2d608e74e850f4 100644 --- a/src/controller/python/chip/internal/thread.py +++ b/src/controller/python/chip/internal/thread.py @@ -18,7 +18,7 @@ # Generally thread credentials are assumed to be binary objects, however for # testing purposes, we expose the internal structure here. -from construct import Byte, Bytes, Int16ul, Int64ul, PaddedString, Struct +from construct import Byte, Bytes, Int16ul, Int64ul, PaddedString, Struct # type: ignore ThreadNetworkInfo = Struct( "ActiveTimestamp" / Int64ul, diff --git a/src/controller/python/chip/storage/__init__.py b/src/controller/python/chip/storage/__init__.py index 20432dcd01870b..4d49284229040f 100644 --- a/src/controller/python/chip/storage/__init__.py +++ b/src/controller/python/chip/storage/__init__.py @@ -24,7 +24,7 @@ import json import logging from ctypes import CFUNCTYPE, POINTER, c_bool, c_char, c_char_p, c_uint16, c_void_p, py_object -from typing import Dict +from typing import IO, Dict, Optional import chip.exceptions import chip.native @@ -39,12 +39,12 @@ @_SyncSetKeyValueCbFunct -def _OnSyncSetKeyValueCb(storageObj, key: str, value, size): +def _OnSyncSetKeyValueCb(storageObj, key: bytes, value, size): storageObj.SetSdkKey(key.decode("utf-8"), ctypes.string_at(value, size)) @_SyncGetKeyValueCbFunct -def _OnSyncGetKeyValueCb(storageObj, key: str, value, size, is_found): +def _OnSyncGetKeyValueCb(storageObj, key: bytes, value, size, is_found): ''' This does not adhere to the API requirements of PersistentStorageDelegate::SyncGetKeyValue, but that is okay since the C++ storage binding layer is capable of adapting results from @@ -94,7 +94,7 @@ class PersistentStorage: Object must be resident before the Matter stack starts up and last past its shutdown. ''' - def __init__(self, path: str = None, jsonData: Dict = None): + def __init__(self, path: Optional[str] = None, jsonData: Optional[Dict] = None): ''' Initializes the object with either a path to a JSON file that contains the configuration OR a JSON dictionary that contains an in-memory representation of the configuration. @@ -118,7 +118,7 @@ def __init__(self, path: str = None, jsonData: Dict = None): if (self._path): try: - self._file = open(path, 'r') + self._file: Optional[IO[str]] = open(path, 'r') self._file.seek(0, 2) size = self._file.tell() self._file.seek(0) diff --git a/src/controller/python/chip/yaml/runner.py b/src/controller/python/chip/yaml/runner.py index 494e6f964aa33f..84f0e06ff7fddf 100644 --- a/src/controller/python/chip/yaml/runner.py +++ b/src/controller/python/chip/yaml/runner.py @@ -20,10 +20,11 @@ from abc import ABC, abstractmethod from dataclasses import dataclass, field from enum import Enum, IntEnum +from typing import Any, Optional, Tuple import chip.interaction_model import chip.yaml.format_converter as Converter -import stringcase +import stringcase # type: ignore from chip.ChipDeviceCtrl import ChipDeviceController, discovery from chip.clusters import ClusterObjects from chip.clusters.Attribute import (AttributeStatus, EventReadResult, SubscriptionTransaction, TypedAttributePath, @@ -63,7 +64,7 @@ class EventResponse: @dataclass class _ActionResult: status: _ActionStatus - response: object + response: Any @dataclass @@ -83,7 +84,7 @@ class _EventSubscriptionCallbackResult: class _ExecutionContext: ''' Objects that is commonly passed around this file that are vital to test execution.''' # Data model lookup to get python attribute, cluster, command object. - data_model_lookup: DataModelLookup = None + data_model_lookup: DataModelLookup # List of subscriptions. subscriptions: list = field(default_factory=list) # The key is the attribute/event name, and the value is a queue of subscription callback results @@ -672,7 +673,7 @@ class DiscoveryCommandAction(BaseAction): """DiscoveryCommand implementation (FindCommissionable* methods).""" @staticmethod - def _filter_for_step(test_step) -> (discovery.FilterType, any): + def _filter_for_step(test_step) -> Tuple[discovery.FilterType, Any]: """Given a test step, figure out the correct filters to give to DiscoverCommissionableNodes. """ @@ -834,8 +835,8 @@ def _default_pseudo_cluster(self, test_step): except ActionCreationError: return None - def encode(self, request) -> BaseAction: - action = None + def encode(self, request) -> Optional[BaseAction]: + action: Optional[BaseAction] = None cluster = request.cluster.replace(' ', '').replace('/', '').replace('.', '') command = request.command if cluster == 'CommissionerCommands': diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h index 9e864b458de94d..fab6862dd96a67 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h @@ -18913,20 +18913,12 @@ typedef NS_OPTIONS(uint32_t, MTRWaterHeaterManagementFeature) { MTRWaterHeaterManagementFeatureTankPercent MTR_PROVISIONALLY_AVAILABLE = 0x2, } MTR_PROVISIONALLY_AVAILABLE; -typedef NS_OPTIONS(uint8_t, MTRWaterHeaterManagementWaterHeaterDemandBitmap) { - MTRWaterHeaterManagementWaterHeaterDemandBitmapImmersionElement1 MTR_PROVISIONALLY_AVAILABLE = 0x1, - MTRWaterHeaterManagementWaterHeaterDemandBitmapImmersionElement2 MTR_PROVISIONALLY_AVAILABLE = 0x2, - MTRWaterHeaterManagementWaterHeaterDemandBitmapHeatPump MTR_PROVISIONALLY_AVAILABLE = 0x4, - MTRWaterHeaterManagementWaterHeaterDemandBitmapBoiler MTR_PROVISIONALLY_AVAILABLE = 0x8, - MTRWaterHeaterManagementWaterHeaterDemandBitmapOther MTR_PROVISIONALLY_AVAILABLE = 0x10, -} MTR_PROVISIONALLY_AVAILABLE; - -typedef NS_OPTIONS(uint8_t, MTRWaterHeaterManagementWaterHeaterTypeBitmap) { - MTRWaterHeaterManagementWaterHeaterTypeBitmapImmersionElement1 MTR_PROVISIONALLY_AVAILABLE = 0x1, - MTRWaterHeaterManagementWaterHeaterTypeBitmapImmersionElement2 MTR_PROVISIONALLY_AVAILABLE = 0x2, - MTRWaterHeaterManagementWaterHeaterTypeBitmapHeatPump MTR_PROVISIONALLY_AVAILABLE = 0x4, - MTRWaterHeaterManagementWaterHeaterTypeBitmapBoiler MTR_PROVISIONALLY_AVAILABLE = 0x8, - MTRWaterHeaterManagementWaterHeaterTypeBitmapOther MTR_PROVISIONALLY_AVAILABLE = 0x10, +typedef NS_OPTIONS(uint8_t, MTRWaterHeaterManagementWaterHeaterHeatSourceBitmap) { + MTRWaterHeaterManagementWaterHeaterHeatSourceBitmapImmersionElement1 MTR_PROVISIONALLY_AVAILABLE = 0x1, + MTRWaterHeaterManagementWaterHeaterHeatSourceBitmapImmersionElement2 MTR_PROVISIONALLY_AVAILABLE = 0x2, + MTRWaterHeaterManagementWaterHeaterHeatSourceBitmapHeatPump MTR_PROVISIONALLY_AVAILABLE = 0x4, + MTRWaterHeaterManagementWaterHeaterHeatSourceBitmapBoiler MTR_PROVISIONALLY_AVAILABLE = 0x8, + MTRWaterHeaterManagementWaterHeaterHeatSourceBitmapOther MTR_PROVISIONALLY_AVAILABLE = 0x10, } MTR_PROVISIONALLY_AVAILABLE; typedef NS_ENUM(uint8_t, MTRDemandResponseLoadControlCriticalityLevel) { diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRClusterConstants.h b/src/darwin/Framework/CHIP/zap-generated/MTRClusterConstants.h index 2e5c8cc7dc1b44..33251439bbc237 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRClusterConstants.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRClusterConstants.h @@ -7372,6 +7372,10 @@ typedef NS_ENUM(uint32_t, MTREventIDType) { MTREventIDTypeClusterElectricalEnergyMeasurementEventCumulativeEnergyMeasuredID MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6)) = 0x00000000, MTREventIDTypeClusterElectricalEnergyMeasurementEventPeriodicEnergyMeasuredID MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6)) = 0x00000001, + // Cluster WaterHeaterManagement events + MTREventIDTypeClusterWaterHeaterManagementEventBoostStartedID MTR_PROVISIONALLY_AVAILABLE = 0x00000000, + MTREventIDTypeClusterWaterHeaterManagementEventBoostEndedID MTR_PROVISIONALLY_AVAILABLE = 0x00000001, + // Cluster DemandResponseLoadControl events MTREventIDTypeClusterDemandResponseLoadControlEventLoadControlEventStatusChangeID MTR_PROVISIONALLY_AVAILABLE = 0x00000000, diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRClusterNames.mm b/src/darwin/Framework/CHIP/zap-generated/MTRClusterNames.mm index fa08192303b8fd..d3715b49d0d250 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRClusterNames.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRClusterNames.mm @@ -10355,6 +10355,15 @@ switch (eventID) { + // Cluster WaterHeaterManagement events + case MTREventIDTypeClusterWaterHeaterManagementEventBoostStartedID: + result = @"BoostStarted"; + break; + + case MTREventIDTypeClusterWaterHeaterManagementEventBoostEndedID: + result = @"BoostEnded"; + break; + default: result = [NSString stringWithFormat:@"", eventID]; break; diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h index b276bb8b1986ac..5c869616268ecc 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h @@ -5495,17 +5495,7 @@ MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6)) MTR_PROVISIONALLY_AVAILABLE @interface MTRWaterHeaterManagementClusterBoostParams : NSObject -@property (nonatomic, copy) NSNumber * _Nonnull duration MTR_PROVISIONALLY_AVAILABLE; - -@property (nonatomic, copy) NSNumber * _Nullable oneShot MTR_PROVISIONALLY_AVAILABLE; - -@property (nonatomic, copy) NSNumber * _Nullable emergencyBoost MTR_PROVISIONALLY_AVAILABLE; - -@property (nonatomic, copy) NSNumber * _Nullable temporarySetpoint MTR_PROVISIONALLY_AVAILABLE; - -@property (nonatomic, copy) NSNumber * _Nullable targetPercentage MTR_PROVISIONALLY_AVAILABLE; - -@property (nonatomic, copy) NSNumber * _Nullable targetReheat MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) MTRWaterHeaterManagementClusterWaterHeaterBoostInfoStruct * _Nonnull boostInfo MTR_PROVISIONALLY_AVAILABLE; /** * Controls whether the command is a timed command (using Timed Invoke). * diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm index 7aa736277256f6..976d5a0ef241be 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm @@ -15466,17 +15466,7 @@ - (instancetype)init { if (self = [super init]) { - _duration = @(0); - - _oneShot = nil; - - _emergencyBoost = nil; - - _temporarySetpoint = nil; - - _targetPercentage = nil; - - _targetReheat = nil; + _boostInfo = [MTRWaterHeaterManagementClusterWaterHeaterBoostInfoStruct new]; _timedInvokeTimeoutMs = nil; _serverSideProcessingTimeout = nil; } @@ -15487,12 +15477,7 @@ - (id)copyWithZone:(NSZone * _Nullable)zone; { auto other = [[MTRWaterHeaterManagementClusterBoostParams alloc] init]; - other.duration = self.duration; - other.oneShot = self.oneShot; - other.emergencyBoost = self.emergencyBoost; - other.temporarySetpoint = self.temporarySetpoint; - other.targetPercentage = self.targetPercentage; - other.targetReheat = self.targetReheat; + other.boostInfo = self.boostInfo; other.timedInvokeTimeoutMs = self.timedInvokeTimeoutMs; other.serverSideProcessingTimeout = self.serverSideProcessingTimeout; @@ -15501,7 +15486,7 @@ - (id)copyWithZone:(NSZone * _Nullable)zone; - (NSString *)description { - NSString * descriptionString = [NSString stringWithFormat:@"<%@: duration:%@; oneShot:%@; emergencyBoost:%@; temporarySetpoint:%@; targetPercentage:%@; targetReheat:%@; >", NSStringFromClass([self class]), _duration, _oneShot, _emergencyBoost, _temporarySetpoint, _targetPercentage, _targetReheat]; + NSString * descriptionString = [NSString stringWithFormat:@"<%@: boostInfo:%@; >", NSStringFromClass([self class]), _boostInfo]; return descriptionString; } @@ -15514,36 +15499,26 @@ - (CHIP_ERROR)_encodeToTLVReader:(chip::System::PacketBufferTLVReader &)reader chip::app::Clusters::WaterHeaterManagement::Commands::Boost::Type encodableStruct; ListFreer listFreer; { - encodableStruct.duration = self.duration.unsignedIntValue; - } - { - if (self.oneShot != nil) { - auto & definedValue_0 = encodableStruct.oneShot.Emplace(); - definedValue_0 = self.oneShot.boolValue; + encodableStruct.boostInfo.duration = self.boostInfo.duration.unsignedIntValue; + if (self.boostInfo.oneShot != nil) { + auto & definedValue_1 = encodableStruct.boostInfo.oneShot.Emplace(); + definedValue_1 = self.boostInfo.oneShot.boolValue; } - } - { - if (self.emergencyBoost != nil) { - auto & definedValue_0 = encodableStruct.emergencyBoost.Emplace(); - definedValue_0 = self.emergencyBoost.boolValue; + if (self.boostInfo.emergencyBoost != nil) { + auto & definedValue_1 = encodableStruct.boostInfo.emergencyBoost.Emplace(); + definedValue_1 = self.boostInfo.emergencyBoost.boolValue; } - } - { - if (self.temporarySetpoint != nil) { - auto & definedValue_0 = encodableStruct.temporarySetpoint.Emplace(); - definedValue_0 = self.temporarySetpoint.shortValue; + if (self.boostInfo.temporarySetpoint != nil) { + auto & definedValue_1 = encodableStruct.boostInfo.temporarySetpoint.Emplace(); + definedValue_1 = self.boostInfo.temporarySetpoint.shortValue; } - } - { - if (self.targetPercentage != nil) { - auto & definedValue_0 = encodableStruct.targetPercentage.Emplace(); - definedValue_0 = self.targetPercentage.unsignedCharValue; + if (self.boostInfo.targetPercentage != nil) { + auto & definedValue_1 = encodableStruct.boostInfo.targetPercentage.Emplace(); + definedValue_1 = self.boostInfo.targetPercentage.unsignedCharValue; } - } - { - if (self.targetReheat != nil) { - auto & definedValue_0 = encodableStruct.targetReheat.Emplace(); - definedValue_0 = self.targetReheat.unsignedCharValue; + if (self.boostInfo.targetReheat != nil) { + auto & definedValue_1 = encodableStruct.boostInfo.targetReheat.Emplace(); + definedValue_1 = self.boostInfo.targetReheat.unsignedCharValue; } } diff --git a/src/darwin/Framework/CHIP/zap-generated/MTREventTLVValueDecoder.mm b/src/darwin/Framework/CHIP/zap-generated/MTREventTLVValueDecoder.mm index 821b8f6d58173d..cb2e1800de750f 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTREventTLVValueDecoder.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTREventTLVValueDecoder.mm @@ -2807,6 +2807,60 @@ static id _Nullable DecodeEventPayloadForWaterHeaterManagementCluster(EventId aE { using namespace Clusters::WaterHeaterManagement; switch (aEventId) { + case Events::BoostStarted::Id: { + Events::BoostStarted::DecodableType cppValue; + *aError = DataModel::Decode(aReader, cppValue); + if (*aError != CHIP_NO_ERROR) { + return nil; + } + + __auto_type * value = [MTRWaterHeaterManagementClusterBoostStartedEvent new]; + + do { + MTRWaterHeaterManagementClusterWaterHeaterBoostInfoStruct * _Nonnull memberValue; + memberValue = [MTRWaterHeaterManagementClusterWaterHeaterBoostInfoStruct new]; + memberValue.duration = [NSNumber numberWithUnsignedInt:cppValue.boostInfo.duration]; + if (cppValue.boostInfo.oneShot.HasValue()) { + memberValue.oneShot = [NSNumber numberWithBool:cppValue.boostInfo.oneShot.Value()]; + } else { + memberValue.oneShot = nil; + } + if (cppValue.boostInfo.emergencyBoost.HasValue()) { + memberValue.emergencyBoost = [NSNumber numberWithBool:cppValue.boostInfo.emergencyBoost.Value()]; + } else { + memberValue.emergencyBoost = nil; + } + if (cppValue.boostInfo.temporarySetpoint.HasValue()) { + memberValue.temporarySetpoint = [NSNumber numberWithShort:cppValue.boostInfo.temporarySetpoint.Value()]; + } else { + memberValue.temporarySetpoint = nil; + } + if (cppValue.boostInfo.targetPercentage.HasValue()) { + memberValue.targetPercentage = [NSNumber numberWithUnsignedChar:cppValue.boostInfo.targetPercentage.Value()]; + } else { + memberValue.targetPercentage = nil; + } + if (cppValue.boostInfo.targetReheat.HasValue()) { + memberValue.targetReheat = [NSNumber numberWithUnsignedChar:cppValue.boostInfo.targetReheat.Value()]; + } else { + memberValue.targetReheat = nil; + } + value.boostInfo = memberValue; + } while (0); + + return value; + } + case Events::BoostEnded::Id: { + Events::BoostEnded::DecodableType cppValue; + *aError = DataModel::Decode(aReader, cppValue); + if (*aError != CHIP_NO_ERROR) { + return nil; + } + + __auto_type * value = [MTRWaterHeaterManagementClusterBoostEndedEvent new]; + + return value; + } default: { break; } diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h index 8590079420a854..35df5cc3e32495 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h @@ -1235,6 +1235,25 @@ MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6)) @property (nonatomic, copy) MTRElectricalEnergyMeasurementClusterEnergyMeasurementStruct * _Nullable energyExported MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6)); @end +MTR_PROVISIONALLY_AVAILABLE +@interface MTRWaterHeaterManagementClusterWaterHeaterBoostInfoStruct : NSObject +@property (nonatomic, copy) NSNumber * _Nonnull duration MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) NSNumber * _Nullable oneShot MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) NSNumber * _Nullable emergencyBoost MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) NSNumber * _Nullable temporarySetpoint MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) NSNumber * _Nullable targetPercentage MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) NSNumber * _Nullable targetReheat MTR_PROVISIONALLY_AVAILABLE; +@end + +MTR_PROVISIONALLY_AVAILABLE +@interface MTRWaterHeaterManagementClusterBoostStartedEvent : NSObject +@property (nonatomic, copy) MTRWaterHeaterManagementClusterWaterHeaterBoostInfoStruct * _Nonnull boostInfo MTR_PROVISIONALLY_AVAILABLE; +@end + +MTR_PROVISIONALLY_AVAILABLE +@interface MTRWaterHeaterManagementClusterBoostEndedEvent : NSObject +@end + MTR_PROVISIONALLY_AVAILABLE @interface MTRDemandResponseLoadControlClusterHeatingSourceControlStruct : NSObject @property (nonatomic, copy) NSNumber * _Nonnull heatingSource MTR_PROVISIONALLY_AVAILABLE; diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm index 02aac9e24ec0ae..6c3fc19e9b68bf 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm @@ -5029,6 +5029,98 @@ - (NSString *)description @end +@implementation MTRWaterHeaterManagementClusterWaterHeaterBoostInfoStruct +- (instancetype)init +{ + if (self = [super init]) { + + _duration = @(0); + + _oneShot = nil; + + _emergencyBoost = nil; + + _temporarySetpoint = nil; + + _targetPercentage = nil; + + _targetReheat = nil; + } + return self; +} + +- (id)copyWithZone:(NSZone * _Nullable)zone +{ + auto other = [[MTRWaterHeaterManagementClusterWaterHeaterBoostInfoStruct alloc] init]; + + other.duration = self.duration; + other.oneShot = self.oneShot; + other.emergencyBoost = self.emergencyBoost; + other.temporarySetpoint = self.temporarySetpoint; + other.targetPercentage = self.targetPercentage; + other.targetReheat = self.targetReheat; + + return other; +} + +- (NSString *)description +{ + NSString * descriptionString = [NSString stringWithFormat:@"<%@: duration:%@; oneShot:%@; emergencyBoost:%@; temporarySetpoint:%@; targetPercentage:%@; targetReheat:%@; >", NSStringFromClass([self class]), _duration, _oneShot, _emergencyBoost, _temporarySetpoint, _targetPercentage, _targetReheat]; + return descriptionString; +} + +@end + +@implementation MTRWaterHeaterManagementClusterBoostStartedEvent +- (instancetype)init +{ + if (self = [super init]) { + + _boostInfo = [MTRWaterHeaterManagementClusterWaterHeaterBoostInfoStruct new]; + } + return self; +} + +- (id)copyWithZone:(NSZone * _Nullable)zone +{ + auto other = [[MTRWaterHeaterManagementClusterBoostStartedEvent alloc] init]; + + other.boostInfo = self.boostInfo; + + return other; +} + +- (NSString *)description +{ + NSString * descriptionString = [NSString stringWithFormat:@"<%@: boostInfo:%@; >", NSStringFromClass([self class]), _boostInfo]; + return descriptionString; +} + +@end + +@implementation MTRWaterHeaterManagementClusterBoostEndedEvent +- (instancetype)init +{ + if (self = [super init]) { + } + return self; +} + +- (id)copyWithZone:(NSZone * _Nullable)zone +{ + auto other = [[MTRWaterHeaterManagementClusterBoostEndedEvent alloc] init]; + + return other; +} + +- (NSString *)description +{ + NSString * descriptionString = [NSString stringWithFormat:@"<%@: >", NSStringFromClass([self class])]; + return descriptionString; +} + +@end + @implementation MTRDemandResponseLoadControlClusterHeatingSourceControlStruct - (instancetype)init { diff --git a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj index 10884a64f4ffe4..bb5a8ce1801631 100644 --- a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj +++ b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj @@ -2319,6 +2319,7 @@ INSTALLHDRS_SCRIPT_PHASE = YES; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IS_ZIPPERED = YES; LIBRARY_SEARCH_PATHS = "$(TEMP_DIR)/out/lib"; OTHER_CFLAGS = "-fmacro-prefix-map=$(SRCROOT)/CHIP/="; OTHER_CPLUSPLUSFLAGS = ( @@ -2488,6 +2489,7 @@ INSTALLHDRS_SCRIPT_PHASE = YES; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IS_ZIPPERED = YES; LIBRARY_SEARCH_PATHS = "$(TEMP_DIR)/out/lib"; OTHER_CFLAGS = "-fmacro-prefix-map=$(SRCROOT)/CHIP/="; OTHER_CPLUSPLUSFLAGS = ( diff --git a/src/lib/shell/MainLoopSilabs.cpp b/src/lib/shell/MainLoopSilabs.cpp index 83ba4ea00ce619..8a8d60af2d6716 100644 --- a/src/lib/shell/MainLoopSilabs.cpp +++ b/src/lib/shell/MainLoopSilabs.cpp @@ -53,24 +53,12 @@ void ReadLine(char * buffer, size_t max) break; } -#ifdef BRD4325A - // for 917 SoC board, we need to create a rx event before we wait for the shell activity - // NotifyShellProcess() is called once the buffer is filled - while (streamer_read(streamer_get(), buffer + read, 1) == 1) - { - // Count how many characters were read; usually one but could be copy/paste - read++; - } -#endif chip::WaitForShellActivity(); -#ifndef BRD4325A - // for EFR32 boards while (streamer_read(streamer_get(), buffer + read, 1) == 1) { // Count how many characters were read; usually one but could be copy/paste read++; } -#endif // Process all characters that were read until we run out or exceed max char limit while (line_sz < read && line_sz < max) { @@ -218,10 +206,6 @@ void ProcessShellLine(intptr_t args) } } MemoryFree(line); -#ifdef BRD4325A - // small delay for uart print - vTaskDelay(1); -#endif streamer_printf(streamer_get(), kShellPrompt); } diff --git a/src/lib/support/ThreadOperationalDataset.cpp b/src/lib/support/ThreadOperationalDataset.cpp index 1c11712166cfa4..39ba6b3b33b196 100644 --- a/src/lib/support/ThreadOperationalDataset.cpp +++ b/src/lib/support/ThreadOperationalDataset.cpp @@ -55,6 +55,7 @@ class ThreadTLV final kMeshLocalPrefix = 7, kSecurityPolicy = 12, kActiveTimestamp = 14, + kDelayTimer = 52, kChannelMask = 53, }; @@ -426,6 +427,24 @@ CHIP_ERROR OperationalDataset::SetSecurityPolicy(uint32_t aSecurityPolicy) return CHIP_NO_ERROR; } +CHIP_ERROR OperationalDataset::GetDelayTimer(uint32_t & aDelayMillis) const +{ + const ThreadTLV * tlv = Locate(ThreadTLV::kDelayTimer); + VerifyOrReturnError(tlv != nullptr, CHIP_ERROR_TLV_TAG_NOT_FOUND); + VerifyOrReturnError(tlv->GetLength() == sizeof(aDelayMillis), CHIP_ERROR_INVALID_TLV_ELEMENT); + tlv->Get32(aDelayMillis); + return CHIP_NO_ERROR; +} + +CHIP_ERROR OperationalDataset::SetDelayTimer(uint32_t aDelayMillis) +{ + ThreadTLV * tlv = MakeRoom(ThreadTLV::kDelayTimer, sizeof(*tlv) + sizeof(aDelayMillis)); + VerifyOrReturnError(tlv != nullptr, CHIP_ERROR_NO_MEMORY); + tlv->Set32(aDelayMillis); + mLength = static_cast(mLength + tlv->GetSize()); + return CHIP_NO_ERROR; +} + void OperationalDataset::UnsetMasterKey() { Remove(ThreadTLV::kMasterKey); diff --git a/src/lib/support/ThreadOperationalDataset.h b/src/lib/support/ThreadOperationalDataset.h index 5cd57df6a79db6..a19d9827bbd610 100644 --- a/src/lib/support/ThreadOperationalDataset.h +++ b/src/lib/support/ThreadOperationalDataset.h @@ -294,6 +294,23 @@ class OperationalDataset */ CHIP_ERROR SetSecurityPolicy(uint32_t aSecurityPolicy); + /** + * Retrieves the delay timer from the dataset. + * + * @retval CHIP_NO_ERROR on success. + * @retval CHIP_ERROR_TLV_TAG_NOT_FOUND if no security policy is present in the dataset. + * @retval CHIP_ERROR_INVALID_TLV_ELEMENT if the TLV element is invalid. + */ + CHIP_ERROR GetDelayTimer(uint32_t & aDelayMillis) const; + + /** + * This method sets the delay timer within the dataset. + * + * @retval CHIP_NO_ERROR on success. + * @retval CHIP_ERROR_NO_MEMORY if there is insufficient space within the dataset. + */ + CHIP_ERROR SetDelayTimer(uint32_t aDelayMillis); + /** * This method clears all data stored in the dataset. */ diff --git a/src/platform/BUILD.gn b/src/platform/BUILD.gn index 38becc7ede7471..fc68f13a3ab4bf 100644 --- a/src/platform/BUILD.gn +++ b/src/platform/BUILD.gn @@ -611,6 +611,9 @@ if (chip_device_platform != "none") { group("platform") { public_deps = [ ":platform_buildconfig" ] } + + source_set("platform_base") { + } } source_set("syscalls_stub") { diff --git a/src/platform/ESP32/OTAImageProcessorImpl.cpp b/src/platform/ESP32/OTAImageProcessorImpl.cpp index 73ba759c87c599..a35ca9a274ca67 100644 --- a/src/platform/ESP32/OTAImageProcessorImpl.cpp +++ b/src/platform/ESP32/OTAImageProcessorImpl.cpp @@ -21,6 +21,7 @@ #include #include "OTAImageProcessorImpl.h" +#include "esp_app_format.h" #include "esp_err.h" #include "esp_log.h" #include "esp_ota_ops.h" @@ -31,7 +32,17 @@ #include #endif // CONFIG_ENABLE_ENCRYPTED_OTA +#ifdef CONFIG_ENABLE_DELTA_OTA +#include +#endif // CONFIG_ENABLE_DELTA_OTA + #define TAG "OTAImageProcessor" + +#ifdef CONFIG_ENABLE_DELTA_OTA +#define PATCH_HEADER_SIZE 64 +#define DIGEST_SIZE 32 +#endif // CONFIG_ENABLE_DELTA_OTA + using namespace chip::System; using namespace ::chip::DeviceLayer::Internal; @@ -123,6 +134,152 @@ CHIP_ERROR OTAImageProcessorImpl::ProcessBlock(ByteSpan & block) return CHIP_NO_ERROR; } +#ifdef CONFIG_ENABLE_DELTA_OTA +bool OTAImageProcessorImpl::VerifyChipId(esp_chip_id_t chipId) +{ + if (chipId != CONFIG_IDF_FIRMWARE_CHIP_ID) + { + ESP_LOGE(TAG, "Mismatch chip id, expected %d, found %d", CONFIG_IDF_FIRMWARE_CHIP_ID, chipId); + return false; + } + return true; +} + +bool OTAImageProcessorImpl::VerifyPatchHeader(void * imgHeaderData) +{ + const uint32_t espDeltaOtaMagic = 0xfccdde10; + if (!imgHeaderData) + { + return false; + } + uint32_t recvMagic = *(uint32_t *) imgHeaderData; + uint8_t * digest = (uint8_t *) ((uint8_t *) imgHeaderData + 4); + if (recvMagic != espDeltaOtaMagic) + { + ESP_LOGE(TAG, "Invalid magic word in patch"); + return false; + } + uint8_t sha_256[DIGEST_SIZE] = { 0 }; + esp_partition_get_sha256(esp_ota_get_running_partition(), sha_256); + if (memcmp(sha_256, digest, DIGEST_SIZE) != 0) + { + ESP_LOGE(TAG, "SHA256 of current firmware differs from than in patch header. Invalid patch for current firmware"); + return false; + } + return true; +} + +esp_err_t OTAImageProcessorImpl::VerifyHeaderData(const uint8_t * buf, size_t size, int * index) +{ + static char patchHeader[PATCH_HEADER_SIZE]; + static int headerDataRead = 0; + if (!patchHeaderVerified) + { + if (headerDataRead + size < PATCH_HEADER_SIZE) + { + memcpy(patchHeader + headerDataRead, buf, size); + headerDataRead += size; + return ESP_OK; + } + else + { + *index = PATCH_HEADER_SIZE - headerDataRead; + memcpy(patchHeader + headerDataRead, buf, *index); + if (!VerifyPatchHeader(patchHeader)) + { + return ESP_ERR_INVALID_VERSION; + } + headerDataRead = 0; + *index = PATCH_HEADER_SIZE; + patchHeaderVerified = true; + } + } + return ESP_OK; +} + +void OTAImageProcessorImpl::DeltaOTACleanUp(intptr_t context) +{ + auto * imageProcessor = reinterpret_cast(context); + if (imageProcessor == nullptr) + { + ChipLogError(SoftwareUpdate, "ImageProcessor context is null"); + return; + } + imageProcessor->patchHeaderVerified = false; + imageProcessor->chipIdVerified = false; + return; +} + +esp_err_t OTAImageProcessorImpl::DeltaOTAReadCallback(uint8_t * buf, size_t size, int srcOffset) +{ + if (size <= 0 || buf == NULL) + { + return ESP_ERR_INVALID_ARG; + } + + const esp_partition_t * currentPartition = esp_ota_get_running_partition(); + if (currentPartition == NULL) + { + return ESP_FAIL; + } + + esp_err_t err = esp_partition_read(currentPartition, srcOffset, buf, size); + + if (err != ESP_OK) + { + ESP_LOGE(TAG, "esp_partition_read failed (%s)!", esp_err_to_name(err)); + } + + return err; +} + +esp_err_t OTAImageProcessorImpl::DeltaOTAWriteCallback(const uint8_t * buf, size_t size, void * arg) +{ + auto * imageProcessor = reinterpret_cast(arg); + if (size <= 0 || buf == NULL) + { + return ESP_ERR_INVALID_ARG; + } + + int index = 0; + static int headerDataRead = 0; + static char headerData[IMG_HEADER_LEN]; + + if (!imageProcessor->chipIdVerified) + { + if (headerDataRead + size - index <= IMG_HEADER_LEN) + { + memcpy(headerData + headerDataRead, buf, size - index); + headerDataRead += size - index; + return ESP_OK; + } + else + { + index = IMG_HEADER_LEN - headerDataRead; + memcpy(headerData + headerDataRead, buf, index); + + esp_image_header_t * header = (esp_image_header_t *) headerData; + if (!VerifyChipId(header->chip_id)) + { + return ESP_ERR_INVALID_VERSION; + } + imageProcessor->chipIdVerified = true; + + // Write data in headerData buffer. + return esp_ota_write(imageProcessor->mOTAUpdateHandle, headerData, IMG_HEADER_LEN); + } + } + + esp_err_t err = esp_ota_write(imageProcessor->mOTAUpdateHandle, buf + index, size - index); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "esp_ota_write failed (%s)!", esp_err_to_name(err)); + } + + return err; +} +#endif // CONFIG_ENABLE_DELTA_OTA + void OTAImageProcessorImpl::HandlePrepareDownload(intptr_t context) { auto * imageProcessor = reinterpret_cast(context); @@ -142,13 +299,32 @@ void OTAImageProcessorImpl::HandlePrepareDownload(intptr_t context) ChipLogError(SoftwareUpdate, "OTA partition not found"); return; } +#ifdef CONFIG_ENABLE_DELTA_OTA + // New image size is unknown for delta OTA, so we use OTA_SIZE_UNKNOWN flag. + esp_err_t err = esp_ota_begin(imageProcessor->mOTAUpdatePartition, OTA_SIZE_UNKNOWN, &(imageProcessor->mOTAUpdateHandle)); +#else esp_err_t err = esp_ota_begin(imageProcessor->mOTAUpdatePartition, OTA_WITH_SEQUENTIAL_WRITES, &(imageProcessor->mOTAUpdateHandle)); +#endif // CONFIG_ENABLE_DELTA_OTA + if (err != ESP_OK) { imageProcessor->mDownloader->OnPreparedForDownload(ESP32Utils::MapError(err)); return; } +#ifdef CONFIG_ENABLE_DELTA_OTA + imageProcessor->deltaOtaCfg.user_data = imageProcessor, + imageProcessor->deltaOtaCfg.read_cb = &(imageProcessor->DeltaOTAReadCallback), + imageProcessor->deltaOtaCfg.write_cb_with_user_data = &(imageProcessor->DeltaOTAWriteCallback), + + imageProcessor->mDeltaOTAUpdateHandle = esp_delta_ota_init(&imageProcessor->deltaOtaCfg); + if (imageProcessor->mDeltaOTAUpdateHandle == NULL) + { + ChipLogError(SoftwareUpdate, "esp_delta_ota_init failed"); + imageProcessor->mDownloader->OnPreparedForDownload(CHIP_ERROR_INTERNAL); + return; + } +#endif // CONFIG_ENABLE_DELTA_OTA #ifdef CONFIG_ENABLE_ENCRYPTED_OTA CHIP_ERROR chipError = imageProcessor->DecryptStart(); @@ -182,7 +358,30 @@ void OTAImageProcessorImpl::HandleFinalize(intptr_t context) } #endif // CONFIG_ENABLE_ENCRYPTED_OTA +#ifdef CONFIG_ENABLE_DELTA_OTA + esp_err_t err = esp_delta_ota_finalize(imageProcessor->mDeltaOTAUpdateHandle); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "esp_delta_ota_finalize() failed (%s)!", esp_err_to_name(err)); + esp_ota_abort(imageProcessor->mOTAUpdateHandle); + imageProcessor->ReleaseBlock(); + PostOTAStateChangeEvent(DeviceLayer::kOtaDownloadFailed); + } + + err = esp_delta_ota_deinit(imageProcessor->mDeltaOTAUpdateHandle); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "esp_delta_ota_deinit() failed (%s)!", esp_err_to_name(err)); + esp_ota_abort(imageProcessor->mOTAUpdateHandle); + imageProcessor->ReleaseBlock(); + PostOTAStateChangeEvent(DeviceLayer::kOtaDownloadFailed); + } + + err = esp_ota_end(imageProcessor->mOTAUpdateHandle); + DeltaOTACleanUp(reinterpret_cast(imageProcessor)); +#else esp_err_t err = esp_ota_end(imageProcessor->mOTAUpdateHandle); +#endif // CONFIG_ENABLE_DELTA_OTA if (err != ESP_OK) { if (err == ESP_ERR_OTA_VALIDATE_FAILED) @@ -217,6 +416,10 @@ void OTAImageProcessorImpl::HandleAbort(intptr_t context) imageProcessor->DecryptAbort(); #endif // CONFIG_ENABLE_ENCRYPTED_OTA +#ifdef CONFIG_ENABLE_DELTA_OTA + DeltaOTACleanUp(reinterpret_cast(imageProcessor)); +#endif // CONFIG_ENABLE_DELTA_OTA + if (esp_ota_abort(imageProcessor->mOTAUpdateHandle) != ESP_OK) { ESP_LOGE(TAG, "ESP OTA abort failed"); @@ -264,7 +467,24 @@ void OTAImageProcessorImpl::HandleProcessBlock(intptr_t context) } #endif // CONFIG_ENABLE_ENCRYPTED_OTA - err = esp_ota_write(imageProcessor->mOTAUpdateHandle, blockToWrite.data(), blockToWrite.size()); +#ifdef CONFIG_ENABLE_DELTA_OTA + + int index = 0; + err = imageProcessor->VerifyHeaderData(blockToWrite.data(), blockToWrite.size(), &index); + + if (err != ESP_OK) + { + ESP_LOGE(TAG, "Header data verification failed (%s)", esp_err_to_name(err)); + imageProcessor->mDownloader->EndDownload(CHIP_ERROR_INVALID_SIGNATURE); + PostOTAStateChangeEvent(DeviceLayer::kOtaDownloadFailed); + return; + } + + // Apply the patch and writes that data to the passive partition. + err = esp_delta_ota_feed_patch(imageProcessor->mDeltaOTAUpdateHandle, blockToWrite.data() + index, blockToWrite.size() - index); +#else + err = esp_ota_write(imageProcessor->mOTAUpdateHandle, blockToWrite.data(), blockToWrite.size()); +#endif // CONFIG_ENABLE_DELTA_OTA #ifdef CONFIG_ENABLE_ENCRYPTED_OTA free((void *) (blockToWrite.data())); diff --git a/src/platform/ESP32/OTAImageProcessorImpl.h b/src/platform/ESP32/OTAImageProcessorImpl.h index c33407dad74511..3414f90b425505 100644 --- a/src/platform/ESP32/OTAImageProcessorImpl.h +++ b/src/platform/ESP32/OTAImageProcessorImpl.h @@ -27,6 +27,12 @@ #include #endif // CONFIG_ENABLE_ENCRYPTED_OTA +#ifdef CONFIG_ENABLE_DELTA_OTA +#include "esp_app_format.h" +#include +#define IMG_HEADER_LEN sizeof(esp_image_header_t) +#endif // CONFIG_ENABLE_DELTA_OTA + namespace chip { class OTAImageProcessorImpl : public OTAImageProcessorInterface @@ -64,6 +70,19 @@ class OTAImageProcessorImpl : public OTAImageProcessorInterface MutableByteSpan mBlock; const esp_partition_t * mOTAUpdatePartition = nullptr; esp_ota_handle_t mOTAUpdateHandle; +#ifdef CONFIG_ENABLE_DELTA_OTA + esp_delta_ota_handle_t mDeltaOTAUpdateHandle; + esp_delta_ota_cfg_t deltaOtaCfg; + bool patchHeaderVerified = false; + bool chipIdVerified = false; + + static void DeltaOTACleanUp(intptr_t context); + static bool VerifyChipId(esp_chip_id_t chipId); + static bool VerifyPatchHeader(void * imgHeaderData); + esp_err_t VerifyHeaderData(const uint8_t * buf, size_t size, int * index); + static esp_err_t DeltaOTAReadCallback(uint8_t * buf_p, size_t size, int src_offset); + static esp_err_t DeltaOTAWriteCallback(const uint8_t * buf_p, size_t size, void * arg); +#endif // CONFIG_ENABLE_DELTA_OTA OTAImageHeaderParser mHeaderParser; #ifdef CONFIG_ENABLE_ENCRYPTED_OTA diff --git a/src/platform/Infineon/crypto/trustm/CHIPCryptoPALHsm_P256_trustm.cpp b/src/platform/Infineon/crypto/trustm/CHIPCryptoPALHsm_P256_trustm.cpp index 23856be89d93c1..485ff22186447b 100644 --- a/src/platform/Infineon/crypto/trustm/CHIPCryptoPALHsm_P256_trustm.cpp +++ b/src/platform/Infineon/crypto/trustm/CHIPCryptoPALHsm_P256_trustm.cpp @@ -78,7 +78,6 @@ extern CHIP_ERROR ECDSA_validate_msg_signature_H(const P256PublicKey * public_ke extern CHIP_ERROR ECDSA_validate_hash_signature_H(const P256PublicKey * public_key, const uint8_t * hash, const size_t hash_length, const P256ECDSASignature & signature); -#if (ENABLE_TRUSTM_GENERATE_EC_KEY || ENABLE_TRUSTM_ECDSA_VERIFY) static CHIP_ERROR get_trustm_keyid_from_keypair(const P256KeypairContext mKeypair, uint32_t * key_id) { if (0 != memcmp(&mKeypair.mBytes[0], trustm_magic_no, sizeof(trustm_magic_no))) @@ -87,36 +86,21 @@ static CHIP_ERROR get_trustm_keyid_from_keypair(const P256KeypairContext mKeypai } *key_id += (mKeypair.mBytes[CRYPTO_KEYPAIR_KEYID_OFFSET]) | (mKeypair.mBytes[CRYPTO_KEYPAIR_KEYID_OFFSET + 1] << 8); - return CHIP_NO_ERROR; } -#endif // #if (ENABLE_TRUSTM_GENERATE_EC_KEY || ENABLE_TRUSTM_ECDSA_VERIFY) P256Keypair::~P256Keypair() { - // Add method to get the keyid if (CHIP_NO_ERROR != get_trustm_keyid_from_keypair(mKeypair, &keyid)) { Clear(); } - else - { - // Delete the key in SE - } } CHIP_ERROR P256Keypair::Initialize(ECPKeyTarget key_target) { CHIP_ERROR error = CHIP_ERROR_INTERNAL; -#if !ENABLE_TRUSTM_GENERATE_EC_KEY - if (CHIP_NO_ERROR == Initialize_H(this, &mPublicKey, &mKeypair)) - { - mInitialized = true; - } - error = CHIP_NO_ERROR; - return error; -#else uint8_t pubkey[128] = { 0, }; @@ -136,11 +120,20 @@ CHIP_ERROR P256Keypair::Initialize(ECPKeyTarget key_target) } else { +#if !ENABLE_TRUSTM_NOC_KEYGEN + error = Initialize_H(this, &mPublicKey, &mKeypair); + if (CHIP_NO_ERROR == error) + { + mInitialized = true; + } + return error; +#else // Add the logic to use different keyid keyid = TRUSTM_NODE_OID_KEY_START; // Trust M ECC 256 Key Gen ChipLogDetail(Crypto, "Generating NIST256 key in TrustM !"); key_usage = (optiga_key_usage_t) (OPTIGA_KEY_USAGE_SIGN | OPTIGA_KEY_USAGE_AUTHENTICATION); +#endif //! ENABLE_TRUSTM_NOC_KEYGEN } // Trust M init trustm_Open(); @@ -167,14 +160,13 @@ CHIP_ERROR P256Keypair::Initialize(ECPKeyTarget key_target) trustm_close(); } return error; -#endif } CHIP_ERROR P256Keypair::ECDSA_sign_msg(const uint8_t * msg, size_t msg_length, P256ECDSASignature & out_signature) const { -#if !ENABLE_TRUSTM_GENERATE_EC_KEY - return ECDSA_sign_msg_H(&mKeypair, msg, msg_length, out_signature); -#else + VerifyOrReturnError(mInitialized, CHIP_ERROR_UNINITIALIZED); + uint16_t keyid = (mKeypair.mBytes[CRYPTO_KEYPAIR_KEYID_OFFSET]) | (mKeypair.mBytes[CRYPTO_KEYPAIR_KEYID_OFFSET + 1] << 8); + CHIP_ERROR error = CHIP_ERROR_INTERNAL; optiga_lib_status_t return_status = OPTIGA_LIB_BUSY; @@ -188,20 +180,31 @@ CHIP_ERROR P256Keypair::ECDSA_sign_msg(const uint8_t * msg, size_t msg_length, P VerifyOrReturnError(msg != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(msg_length > 0, CHIP_ERROR_INVALID_ARGUMENT); - ChipLogDetail(Crypto, "TrustM: ECDSA_sign_msg"); // Trust M Init trustm_Open(); // Hash to get the digest Hash_SHA256(msg, msg_length, &digest[0]); - uint16_t keyid = (mKeypair.mBytes[CRYPTO_KEYPAIR_KEYID_OFFSET]) | (mKeypair.mBytes[CRYPTO_KEYPAIR_KEYID_OFFSET + 1] << 8); - // Api call to calculate the signature - if (keyid == OPTIGA_KEY_ID_E0F2) + + if (keyid == OPTIGA_KEY_ID_E0F0) { - return_status = trustm_ecdsa_sign(OPTIGA_KEY_ID_E0F2, digest, digest_length, signature_trustm, &signature_trustm_len); + ChipLogDetail(Crypto, "TrustM: ECDSA_sign_msg"); + + // Api call to calculate the signature + return_status = trustm_ecdsa_sign(OPTIGA_KEY_ID_E0F0, digest, digest_length, signature_trustm, &signature_trustm_len); } else { - return_status = trustm_ecdsa_sign(OPTIGA_KEY_ID_E0F0, digest, digest_length, signature_trustm, &signature_trustm_len); +#if !ENABLE_TRUSTM_NOC_KEYGEN + // Use the mbedtls based method + ChipLogDetail(Crypto, "ECDSA sing msg mbedtls"); + return ECDSA_sign_msg_H(&mKeypair, msg, msg_length, out_signature); +#else + if (keyid == OPTIGA_KEY_ID_E0F2) + { + ChipLogDetail(Crypto, "TrustM: ECDSA_sign_msg"); + return_status = trustm_ecdsa_sign(OPTIGA_KEY_ID_E0F2, digest, digest_length, signature_trustm, &signature_trustm_len); + } +#endif //! ENABLE_TRUSTM_NOC_KEYGEN } VerifyOrExit(return_status == OPTIGA_LIB_SUCCESS, error = CHIP_ERROR_INTERNAL); @@ -220,14 +223,10 @@ CHIP_ERROR P256Keypair::ECDSA_sign_msg(const uint8_t * msg, size_t msg_length, P trustm_close(); } return error; -#endif } CHIP_ERROR P256Keypair::ECDH_derive_secret(const P256PublicKey & remote_public_key, P256ECDHDerivedSecret & out_secret) const { -#if !ENABLE_TRUSTM_GENERATE_EC_KEY - return ECDH_derive_secret_H(&mKeypair, remote_public_key, out_secret); -#else CHIP_ERROR error = CHIP_ERROR_INTERNAL; optiga_lib_status_t return_status = OPTIGA_LIB_BUSY; size_t secret_length = (out_secret.Length() == 0) ? out_secret.Capacity() : out_secret.Length(); @@ -262,7 +261,6 @@ CHIP_ERROR P256Keypair::ECDH_derive_secret(const P256PublicKey & remote_public_k trustm_close(); } return error; -#endif } CHIP_ERROR P256PublicKey::ECDSA_validate_hash_signature(const uint8_t * hash, size_t hash_length, @@ -313,6 +311,12 @@ CHIP_ERROR P256Keypair::Serialize(P256SerializedKeypair & output) const 0, }; + if (0 != memcmp(&mKeypair.mBytes[0], trustm_magic_no, sizeof(trustm_magic_no))) + { + VerifyOrReturnError(mInitialized, CHIP_ERROR_UNINITIALIZED); + return Serialize_H(mKeypair, mPublicKey, output); + } + /* Set the public key */ P256PublicKey & public_key = const_cast(Pubkey()); bbuf.Put(Uint8::to_uchar(public_key), public_key.Length()); @@ -358,16 +362,11 @@ CHIP_ERROR P256Keypair::Deserialize(P256SerializedKeypair & input) } else { -#if !ENABLE_TRUSTM_KEY_IMPORT if (CHIP_NO_ERROR == (error = Deserialize_H(this, &mPublicKey, &mKeypair, input))) { mInitialized = true; } return error; -#else - // Add in code for Trust M - return CHIP_NO_ERROR; -#endif } } @@ -430,9 +429,6 @@ static void add_tlv(uint8_t * buf, size_t buf_index, uint8_t tag, size_t len, ui CHIP_ERROR P256Keypair::NewCertificateSigningRequest(uint8_t * csr, size_t & csr_length) const { -#if !ENABLE_TRUSTM_GENERATE_EC_KEY - return NewCertificateSigningRequest_H(&mKeypair, csr, csr_length); -#else CHIP_ERROR error = CHIP_ERROR_INTERNAL; optiga_lib_status_t return_status = OPTIGA_LIB_BUSY; @@ -585,8 +581,6 @@ CHIP_ERROR P256Keypair::NewCertificateSigningRequest(uint8_t * csr, size_t & csr trustm_close(); } return error; - -#endif } } // namespace Crypto diff --git a/src/platform/Infineon/crypto/trustm/CHIPCryptoPALHsm_config_trustm.h b/src/platform/Infineon/crypto/trustm/CHIPCryptoPALHsm_config_trustm.h index 44d841c5d8fb6b..56c1c78da946f7 100644 --- a/src/platform/Infineon/crypto/trustm/CHIPCryptoPALHsm_config_trustm.h +++ b/src/platform/Infineon/crypto/trustm/CHIPCryptoPALHsm_config_trustm.h @@ -32,11 +32,6 @@ */ #define ENABLE_TRUSTM_ECDSA_VERIFY 1 -/* - * Enable Key Import for trustm - */ -#define ENABLE_TRUSTM_KEY_IMPORT 0 - /* * Enable trustm for HKDF SHA256 */ @@ -51,3 +46,8 @@ * Enable trustm for DA */ #define ENABLE_TRUSTM_DEVICE_ATTESTATION 1 + +/* + * Enable trustm for NOC key-pair generation + */ +#define ENABLE_TRUSTM_NOC_KEYGEN 0 diff --git a/src/platform/Infineon/crypto/trustm/CHIPCryptoPALHsm_utils_trustm.h b/src/platform/Infineon/crypto/trustm/CHIPCryptoPALHsm_utils_trustm.h index 4efd6ade24d37e..be74bf4f3dec09 100644 --- a/src/platform/Infineon/crypto/trustm/CHIPCryptoPALHsm_utils_trustm.h +++ b/src/platform/Infineon/crypto/trustm/CHIPCryptoPALHsm_utils_trustm.h @@ -51,7 +51,7 @@ extern optiga_util_t * p_local_util; } static const uint8_t trustm_magic_no[] = IFX_CRYPTO_KEY_MAGIC; -static const uint8_t DA_KEY_ID[] = { 0xE0, 0xF0 }; +static const uint8_t DA_KEY_ID[] = { 0xF0, 0xE0 }; // OID --> 0xE0F0 /* Open session to trustm */ void trustm_Open(void); void read_certificate_from_optiga(uint16_t optiga_oid, char * cert_pem, uint16_t * cert_pem_length); diff --git a/src/platform/Linux/CHIPLinuxStorageIni.cpp b/src/platform/Linux/CHIPLinuxStorageIni.cpp index 1a968876282e1a..7186fe3d477fbf 100644 --- a/src/platform/Linux/CHIPLinuxStorageIni.cpp +++ b/src/platform/Linux/CHIPLinuxStorageIni.cpp @@ -98,17 +98,13 @@ CHIP_ERROR ChipLinuxStorageIni::CommitConfig(const std::string & configFile) if (fd != -1) { std::ofstream ofs; - - ChipLogProgress(DeviceLayer, "writing settings to file (%s)", tmpPath.c_str()); - ofs.open(tmpPath, std::ofstream::out | std::ofstream::trunc); mConfigStore.generate(ofs); - close(fd); if (rename(tmpPath.c_str(), configFile.c_str()) == 0) { - ChipLogProgress(DeviceLayer, "renamed tmp file to file (%s)", configFile.c_str()); + ChipLogDetail(DeviceLayer, "wrote settings to %s", configFile.c_str()); } else { diff --git a/src/platform/android/AndroidChipPlatform-JNI.cpp b/src/platform/android/AndroidChipPlatform-JNI.cpp index a5863a0c739e32..45f54835d44e08 100644 --- a/src/platform/android/AndroidChipPlatform-JNI.cpp +++ b/src/platform/android/AndroidChipPlatform-JNI.cpp @@ -28,12 +28,15 @@ #include #include #include +#include #include #include #include #include #include +#include + #include "AndroidChipPlatform-JNI.h" #include "BLEManagerImpl.h" #include "BleConnectCallback-JNI.h" @@ -45,6 +48,8 @@ using namespace chip; #define JNI_METHOD(RETURN, METHOD_NAME) extern "C" JNIEXPORT RETURN JNICALL Java_chip_platform_AndroidChipPlatform_##METHOD_NAME +#define JNI_LOGGING_METHOD(RETURN, METHOD_NAME) \ + extern "C" JNIEXPORT RETURN JNICALL Java_chip_platform_AndroidChipLogging_##METHOD_NAME #define JNI_MDNSCALLBACK_METHOD(RETURN, METHOD_NAME) \ extern "C" JNIEXPORT RETURN JNICALL Java_chip_platform_ChipMdnsCallbackImpl_##METHOD_NAME @@ -245,6 +250,30 @@ JNI_METHOD(void, nativeSetDnssdDelegates)(JNIEnv * env, jclass self, jobject res chip::Dnssd::InitializeWithObjects(resolver, browser, chipMdnsCallback); } +JNI_LOGGING_METHOD(void, setLogFilter)(JNIEnv * env, jclass clazz, jint level) +{ + using namespace chip::Logging; + + uint8_t category = kLogCategory_Detail; + switch (level) + { + case ANDROID_LOG_VERBOSE: + case ANDROID_LOG_DEBUG: + category = kLogCategory_Detail; + break; + case ANDROID_LOG_INFO: + category = kLogCategory_Progress; + break; + case ANDROID_LOG_WARN: + case ANDROID_LOG_ERROR: + category = kLogCategory_Error; + break; + default: + break; + } + SetLogFilter(category); +} + JNI_MDNSCALLBACK_METHOD(void, handleServiceResolve) (JNIEnv * env, jclass self, jstring instanceName, jstring serviceType, jstring hostName, jstring address, jint port, jobject attributes, jlong callbackHandle, jlong contextHandle) diff --git a/src/platform/android/BUILD.gn b/src/platform/android/BUILD.gn index aa6f59ab5110e4..640b52490078eb 100644 --- a/src/platform/android/BUILD.gn +++ b/src/platform/android/BUILD.gn @@ -114,6 +114,7 @@ android_library("java") { sources = [ "java/chip/platform/AndroidBleManager.java", + "java/chip/platform/AndroidChipLogging.java", "java/chip/platform/AndroidChipPlatform.java", "java/chip/platform/AndroidChipPlatformException.java", "java/chip/platform/BleCallback.java", diff --git a/src/platform/android/CHIPPlatformConfig.h b/src/platform/android/CHIPPlatformConfig.h index 19ba3d113517ef..352dc41f41a0b5 100644 --- a/src/platform/android/CHIPPlatformConfig.h +++ b/src/platform/android/CHIPPlatformConfig.h @@ -48,7 +48,7 @@ using CHIP_CONFIG_PERSISTED_STORAGE_KEY_TYPE = const char *; #endif // CHIP_CONFIG_MAX_EXCHANGE_CONTEXTS #ifndef CHIP_LOG_FILTERING -#define CHIP_LOG_FILTERING 0 +#define CHIP_LOG_FILTERING 1 #endif // CHIP_LOG_FILTERING #ifndef CHIP_CONFIG_BDX_MAX_NUM_TRANSFERS diff --git a/src/platform/android/Logging.cpp b/src/platform/android/Logging.cpp index 404c76f803ba12..ccdaa8dffca4ac 100644 --- a/src/platform/android/Logging.cpp +++ b/src/platform/android/Logging.cpp @@ -12,7 +12,21 @@ namespace Platform { void LogV(const char * module, uint8_t category, const char * msg, va_list v) { - int priority = (category == kLogCategory_Error) ? ANDROID_LOG_ERROR : ANDROID_LOG_DEBUG; + int priority = ANDROID_LOG_DEBUG; + switch (category) + { + case kLogCategory_Error: + priority = ANDROID_LOG_ERROR; + break; + case kLogCategory_Progress: + priority = ANDROID_LOG_INFO; + break; + case kLogCategory_Detail: + priority = ANDROID_LOG_DEBUG; + break; + default: + break; + } __android_log_vprint(priority, module, msg, v); } diff --git a/src/platform/android/java/chip/platform/AndroidChipLogging.java b/src/platform/android/java/chip/platform/AndroidChipLogging.java new file mode 100644 index 00000000000000..4f41b21b78b732 --- /dev/null +++ b/src/platform/android/java/chip/platform/AndroidChipLogging.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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. + * + */ +package chip.platform; + +public class AndroidChipLogging { + // logging level is in android.util.Log class + public static native void setLogFilter(int level); +} diff --git a/src/platform/logging/impl/Syslog.cpp b/src/platform/logging/impl/Syslog.cpp index 163e6d7398aefe..638cffeb94215e 100644 --- a/src/platform/logging/impl/Syslog.cpp +++ b/src/platform/logging/impl/Syslog.cpp @@ -23,6 +23,14 @@ #include #include +#ifndef CHIP_SYSLOG_IDENT +#define CHIP_SYSLOG_IDENT nullptr +#endif + +#ifndef CHIP_SYSLOG_FACILITY +#define CHIP_SYSLOG_FACILITY LOG_DAEMON +#endif + namespace chip { namespace Logging { namespace Platform { @@ -51,7 +59,7 @@ void LogV(const char * module, uint8_t category, const char * msg, va_list v) if (!sInitialized) { - openlog(nullptr, 0, LOG_DAEMON); + openlog(CHIP_SYSLOG_IDENT, LOG_CONS | LOG_PID, CHIP_SYSLOG_FACILITY); sInitialized = true; } diff --git a/src/platform/silabs/CHIPPlatformConfig.h b/src/platform/silabs/CHIPPlatformConfig.h index 93faf6b791b697..20b4678c18b682 100644 --- a/src/platform/silabs/CHIPPlatformConfig.h +++ b/src/platform/silabs/CHIPPlatformConfig.h @@ -24,8 +24,15 @@ #pragma once +#include #include +#if (SL_MATTER_GN_BUILD == 0) +#if defined(CHIP_CONFIG_ENABLE_ICD_SERVER) && (CHIP_CONFIG_ENABLE_ICD_SERVER == 1) +#include "sl_matter_icd_config.h" +#endif // defined(CHIP_CONFIG_ENABLE_ICD_SERVER) && (CHIP_CONFIG_ENABLE_ICD_SERVER == 1) +#endif // SL_MATTER_GN_BUILD + // ==================== General Platform Adaptations ==================== #define CHIP_CONFIG_ABORT() abort() @@ -45,7 +52,8 @@ #if CHIP_HAVE_CONFIG_H #include #endif -#if (CHIP_CRYPTO_PLATFORM == 1) + +#if (CHIP_CRYPTO_PLATFORM == 1) && !defined(SL_MBEDTLS_USE_TINYCRYPT) #include "psa/crypto.h" #if !defined(CHIP_CONFIG_SHA256_CONTEXT_SIZE) @@ -56,7 +64,7 @@ #define CHIP_CONFIG_SHA256_CONTEXT_ALIGN psa_hash_operation_t #endif -#endif // CHIP_CRYPTO_PLATFORM +#endif // (CHIP_CRYPTO_PLATFORM == 1) && !defined(SL_MBEDTLS_USE_TINYCRYPT) // ==================== General Configuration Overrides ==================== @@ -92,7 +100,7 @@ #define CHIP_CONFIG_MAX_FABRICS 5 // 4 fabrics + 1 for rotation slack #endif -#ifdef SL_ICD_ENABLED +#if defined(CHIP_CONFIG_ENABLE_ICD_SERVER) && CHIP_CONFIG_ENABLE_ICD_SERVER #ifndef CHIP_CONFIG_ICD_IDLE_MODE_DURATION_SEC #define CHIP_CONFIG_ICD_IDLE_MODE_DURATION_SEC SL_IDLE_MODE_DURATION_S @@ -110,7 +118,16 @@ #define CHIP_CONFIG_ICD_CLIENTS_SUPPORTED_PER_FABRIC SL_ICD_SUPPORTED_CLIENTS_PER_FABRIC #endif // CHIP_CONFIG_ICD_CLIENTS_SUPPORTED_PER_FABRIC -#endif // SL_ICD_ENABLED +#endif // defined(CHIP_CONFIG_ENABLE_ICD_SERVER) && CHIP_CONFIG_ENABLE_ICD_SERVER + +/** + * @brief CHIP_SHELL_MAX_LINE_SIZE + * + * @brief Platform maximum line for the Matter Shell + */ +#ifndef CHIP_SHELL_MAX_LINE_SIZE +#define CHIP_SHELL_MAX_LINE_SIZE 256 +#endif // CHIP_SHELL_MAX_LINE_SIZE // ==================== FreeRTOS Configuration Overrides ==================== #ifndef CHIP_CONFIG_FREERTOS_USE_STATIC_TASK diff --git a/src/platform/silabs/Logging.cpp b/src/platform/silabs/Logging.cpp index 3777c96148fb75..339bc8ae078cd6 100644 --- a/src/platform/silabs/Logging.cpp +++ b/src/platform/silabs/Logging.cpp @@ -15,13 +15,6 @@ #include #include -#ifndef BRD4325A - -#ifdef RAIL_ASSERT_DEBUG_STRING -#include "rail_assert_error_codes.h" -#endif -#endif // BRD4325A - #ifdef PW_RPC_ENABLED #include "PigweedLogger.h" #endif diff --git a/src/pybindings/pycontroller/BUILD.gn b/src/pybindings/pycontroller/BUILD.gn deleted file mode 100644 index 0949f7baf7661a..00000000000000 --- a/src/pybindings/pycontroller/BUILD.gn +++ /dev/null @@ -1,175 +0,0 @@ -# Copyright (c) 2021 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_overrides/chip.gni") -import("//build_overrides/pigweed.gni") - -import("$dir_pw_build/python.gni") - -import("${chip_root}/build/chip/tools.gni") -import("${chip_root}/src/platform/device.gni") -import("${dir_pw_unit_test}/test.gni") - -if (current_os == "mac") { - import("${build_root}/config/mac/mac_sdk.gni") -} - -config("controller_wno_deprecate") { - cflags = [ - "-Wno-deprecated-declarations", - "-Wno-shadow", - "-Wno-unused-result", - "-Wsign-compare", - "-Wunreachable-code", - "-Wno-macro-redefined", - ] -} - -shared_library("CHIPController") { - configs -= [ "//build/config/compiler:exceptions_default" ] - - output_name = "PyChip" - output_dir = "${target_out_dir}/pychip" - - # TODO: Update to use GN tools to get actual paths - include_dirs = [ "${chip_root}/third_party/pybind11/repo/include" ] - if (current_os == "mac") { - include_dirs += - [ "${chip_root}/.environment/cipd/packages/python/include/python3.9" ] - } else if (current_os == "linux") { - include_dirs += [ "/usr/include/python3.9" ] - } else { - assert(false, "OS not supported.") - } - - sources = [ - "ControllerBindings/PyChip_ChipError.cpp", - "ControllerBindings/PyChip_ErrorStr.cpp", - "ControllerBindings/PyChip_Main.cpp", - ] - - public_deps = [ - "${chip_root}/src/app", - "${chip_root}/src/controller/data_model", - "${chip_root}/src/lib", - "${chip_root}/src/lib/core", - "${chip_root}/src/lib/dnssd", - "${chip_root}/src/lib/support", - "${chip_root}/src/platform", - "${chip_root}/src/platform/logging:default", - "${chip_root}/src/setup_payload", - "${chip_root}/src/tracing/json", - "${chip_root}/src/tracing/perfetto", - "${chip_root}/src/tracing/perfetto:file_output", - "${chip_root}/src/transport", - "${chip_root}/third_party/jsoncpp", - ] - deps = [ - "${chip_root}/src/tracing/perfetto:event_storage", - "${chip_root}/src/tracing/perfetto:simple_initialization", - ] - configs += [ ":controller_wno_deprecate" ] - if (current_os == "mac") { - ldflags = [ - "-undefined", - "dynamic_lookup", - ] - } - if (current_os == "linux") { - libs = [ "python3.9" ] - } -} - -pw_python_action("pycontroller") { - script = "build-chip-wheel.py" - - _py_manifest_files = [ - { - src_dir = "." - sources = [ "pychip/__init__.py" ] - }, - { - src_dir = target_out_dir - sources = [ "${target_out_dir}/pychip/PyChip.so" ] - }, - { - src_dir = "//" - sources = [ "//LICENSE" ] - }, - ] - - _py_manifest_file = "${target_gen_dir}/${target_name}.py_manifest.json" - - inputs = [] - _py_manifest_files_rebased = [] - foreach(_manifest_entry, _py_manifest_files) { - inputs += _manifest_entry.sources - _py_manifest_files_rebased += [ - { - src_dir = rebase_path(_manifest_entry.src_dir, - get_path_info(_py_manifest_file, "dir")) - sources = rebase_path(_manifest_entry.sources, _manifest_entry.src_dir) - }, - ] - } - - _py_manifest = { - files = _py_manifest_files_rebased - } - - write_file(_py_manifest_file, _py_manifest, "json") - - _dist_dir = "${root_out_dir}/pybindings/pycontroller" - - if (current_cpu == "x64") { - cpu_tag = "x86_64" - } else if (current_cpu == "arm64") { - cpu_tag = "aarch64" - } else { - cpu_tag = current_cpu - } - - if (current_os == "mac") { - platform_tag = string_replace( - string_replace(mac_deployment_target, "macos", "macosx."), - ".", - "_") - } else { - platform_tag = current_os - } - - platform_tag = platform_tag + "_" + cpu_tag - - tags = "cp37-" + platform_tag - - args = [ - "--package_name", - "pychip", - "--build_number", - "0.0", - "--build_dir", - rebase_path("${target_gen_dir}/${target_name}.py_build", root_build_dir), - "--dist_dir", - rebase_path(_dist_dir, root_build_dir), - "--manifest", - rebase_path(_py_manifest_file, root_build_dir), - "--plat-name", - platform_tag, - ] - - public_deps = [ ":CHIPController" ] - - output_name = "pychip-0.0.dist-info-0.0-${tags}.whl" - outputs = [ "${_dist_dir}/$output_name" ] -} diff --git a/src/pybindings/pycontroller/ControllerBindings/PyChip_ChipError.cpp b/src/pybindings/pycontroller/ControllerBindings/PyChip_ChipError.cpp deleted file mode 100644 index d1fc3cf67b11ac..00000000000000 --- a/src/pybindings/pycontroller/ControllerBindings/PyChip_ChipError.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2021 Project CHIP Authors - * All rights reserved. - * - * 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 -#include -#include - -#ifndef BINDER_PYBIND11_TYPE_CASTER -#define BINDER_PYBIND11_TYPE_CASTER -PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr) -PYBIND11_DECLARE_HOLDER_TYPE(T, T *) -PYBIND11_MAKE_OPAQUE(std::shared_ptr) -#endif - -void bind_PyChip_ChipError(std::function & M) -{ - { - pybind11::class_> cl( - M("chip"), "ChipError", - "This is a helper class for managing `CHIP_ERROR` numbers.\n\n At the top level, an error belongs to a `Range` and has " - "an integral Value whose meaning depends on the `Range`.\n One, `Range::kSDK`, is used for the CHIP SDK's own errors; " - "others encapsulate error codes from external sources\n (e.g. libraries, OS) into a `CHIP_ERROR`.\n\n CHIP SDK errors " - "inside `Range::kSDK` consist of a component identifier given by `SdkPart` and an arbitrary small\n integer Code."); - cl.def(pybind11::init([]() { return new chip::ChipError(); })); - cl.def(pybind11::init(), pybind11::arg("error")); - - cl.def(pybind11::init([](chip::ChipError const & o) { return new chip::ChipError(o); })); - - pybind11::enum_(cl, "Range", "Top-level error classification.") - .value("kSDK", chip::ChipError::Range::kSDK) - .value("kOS", chip::ChipError::Range::kOS) - .value("kPOSIX", chip::ChipError::Range::kPOSIX) - .value("kLwIP", chip::ChipError::Range::kLwIP) - .value("kOpenThread", chip::ChipError::Range::kOpenThread) - .value("kPlatform", chip::ChipError::Range::kPlatform); - - pybind11::enum_(cl, "SdkPart", "Secondary classification of errors in `Range::kSDK`.") - .value("kCore", chip::ChipError::SdkPart::kCore) - .value("kInet", chip::ChipError::SdkPart::kInet) - .value("kDevice", chip::ChipError::SdkPart::kDevice) - .value("kASN1", chip::ChipError::SdkPart::kASN1) - .value("kBLE", chip::ChipError::SdkPart::kBLE) - .value("kApplication", chip::ChipError::SdkPart::kApplication); - - cl.def("__eq__", (bool(chip::ChipError::*)(const class chip::ChipError &) const) & chip::ChipError::operator==, - "C++: chip::ChipError::operator==(const class chip::ChipError &) const --> bool", pybind11::arg("other")); - cl.def("__ne__", (bool(chip::ChipError::*)(const class chip::ChipError &) const) & chip::ChipError::operator!=, - "C++: chip::ChipError::operator!=(const class chip::ChipError &) const --> bool", pybind11::arg("other")); - cl.def_static("IsSuccess", (bool (*)(unsigned int)) & chip::ChipError::IsSuccess, - "C++: chip::ChipError::IsSuccess(unsigned int) --> bool", pybind11::arg("error")); - cl.def_static("IsSuccess", (bool (*)(class chip::ChipError)) & chip::ChipError::IsSuccess, - "C++: chip::ChipError::IsSuccess(class chip::ChipError) --> bool", pybind11::arg("error")); - } - M("chip").def("RegisterCHIPLayerErrorFormatter", (void (*)()) & chip::RegisterCHIPLayerErrorFormatter, - "C++: chip::RegisterCHIPLayerErrorFormatter() --> void"); - - M("chip").def("FormatCHIPError", (bool (*)(char *, unsigned short, unsigned int)) & chip::FormatCHIPError, - "C++: chip::FormatCHIPError(char *, unsigned short, unsigned int) --> bool", pybind11::arg("buf"), - pybind11::arg("bufSize"), pybind11::arg("err")); -} diff --git a/src/pybindings/pycontroller/ControllerBindings/PyChip_ErrorStr.cpp b/src/pybindings/pycontroller/ControllerBindings/PyChip_ErrorStr.cpp deleted file mode 100644 index b59424d10d087d..00000000000000 --- a/src/pybindings/pycontroller/ControllerBindings/PyChip_ErrorStr.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2021 Project CHIP Authors - * All rights reserved. - * - * 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 - -#include -#include -#include - -#ifndef BINDER_PYBIND11_TYPE_CASTER -#define BINDER_PYBIND11_TYPE_CASTER -PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr) -PYBIND11_DECLARE_HOLDER_TYPE(T, T *) -PYBIND11_MAKE_OPAQUE(std::shared_ptr) -#endif - -void bind_PyChip_ErrorStr(std::function & M) -{ - { - pybind11::class_> cl(M("chip"), "ErrorFormatter", ""); - cl.def(pybind11::init([]() { return new chip::ErrorFormatter(); })); - } - M("chip").def("ErrorStr", (const char * (*) (CHIP_ERROR)) & chip::ErrorStr, - "C++: chip::ErrorStr(unsigned int) --> const char *", pybind11::return_value_policy::automatic, - pybind11::arg("err")); - - M("chip").def("RegisterErrorFormatter", (void (*)(struct chip::ErrorFormatter *)) & chip::RegisterErrorFormatter, - "C++: chip::RegisterErrorFormatter(struct chip::ErrorFormatter *) --> void", pybind11::arg("errFormatter")); - - M("chip").def("DeregisterErrorFormatter", (void (*)(struct chip::ErrorFormatter *)) & chip::DeregisterErrorFormatter, - "C++: chip::DeregisterErrorFormatter(struct chip::ErrorFormatter *) --> void", pybind11::arg("errFormatter")); - - M("chip").def("FormatError", (void (*)(char *, unsigned short, const char *, unsigned int, const char *)) & chip::FormatError, - "C++: chip::FormatError(char *, unsigned short, const char *, unsigned int, const char *) --> void", - pybind11::arg("buf"), pybind11::arg("bufSize"), pybind11::arg("subsys"), pybind11::arg("err"), - pybind11::arg("desc")); -} diff --git a/src/pybindings/pycontroller/ControllerBindings/PyChip_Main.cpp b/src/pybindings/pycontroller/ControllerBindings/PyChip_Main.cpp deleted file mode 100644 index d7cc4effcb3197..00000000000000 --- a/src/pybindings/pycontroller/ControllerBindings/PyChip_Main.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2021 Project CHIP Authors - * All rights reserved. - * - * 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 -#include - -#include - -typedef std::function ModuleGetter; - -void bind_PyChip_ErrorStr(std::function & M); -void bind_PyChip_ChipError(std::function & M); - -PYBIND11_MODULE(PyChip, root_module) -{ - root_module.doc() = "PyChip module"; - - std::map modules; - ModuleGetter M = [&](std::string const & namespace_) -> pybind11::module & { - auto it = modules.find(namespace_); - if (it == modules.end()) - throw std::runtime_error("Attempt to access pybind11::module for namespace " + namespace_ + " that does not exist!!!"); - return it->second; - }; - - modules[""] = root_module; - - std::vector> sub_modules{ { "", "chip" } }; - for (auto & p : sub_modules) - modules[p.first.size() ? p.first + "::" + p.second : p.second] = - modules[p.first].def_submodule(p.second.c_str(), ("Bindings for " + p.first + "::" + p.second + " namespace").c_str()); - - bind_PyChip_ErrorStr(M); - bind_PyChip_ChipError(M); -} diff --git a/src/pybindings/pycontroller/build-chip-wheel.py b/src/pybindings/pycontroller/build-chip-wheel.py deleted file mode 100644 index 61bdf373e8615c..00000000000000 --- a/src/pybindings/pycontroller/build-chip-wheel.py +++ /dev/null @@ -1,191 +0,0 @@ -# -# Copyright (c) 2020 Project CHIP Authors -# Copyright (c) 2019 Google LLC. -# All rights reserved. -# -# 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. -# - -# -# Description: -# Builds a Python wheel package for CHIP. -# - -from __future__ import absolute_import - -import argparse -import json -import os -import platform -import shutil - -from setuptools import setup -from wheel.bdist_wheel import bdist_wheel - -parser = argparse.ArgumentParser( - description='build the pip package for chip using chip components generated during the build and python source code') -parser.add_argument('--package_name', default='chip', - help='configure the python package name') -parser.add_argument('--build_number', default='0.0', - help='configure the chip build number') -parser.add_argument('--build_dir', help='directory to build in') -parser.add_argument('--dist_dir', help='directory to place distribution in') -parser.add_argument('--manifest', help='list of files to package') -parser.add_argument( - '--plat-name', help='platform name to embed in generated filenames') - -args = parser.parse_args() - - -class InstalledScriptInfo: - """Information holder about a script that is to be installed.""" - - def __init__(self, name): - self.name = name - self.installName = os.path.splitext(name)[0] - - -chipDLLName = 'PyChip.so' -packageName = args.package_name -chipPackageVer = args.build_number - -installScripts = [ - # InstalledScriptInfo('chip-repl.py'), -] - -# Record the current directory at the start of execution. -curDir = os.curdir - -manifestFile = os.path.abspath(args.manifest) -buildDir = os.path.abspath(args.build_dir) -distDir = os.path.abspath(args.dist_dir) - -# Use a temporary directory within the build directory to assemble the components -# for the installable package. -tmpDir = os.path.join(buildDir, 'pychip-wheel-components') - -manifest = json.load(open(manifestFile, 'r')) - -try: - - # - # Perform a series of setup steps prior to creating the chip package... - # - - # Create the temporary components directory. - if os.path.isdir(tmpDir): - shutil.rmtree(tmpDir) - os.makedirs(tmpDir, exist_ok=True) - - # Switch to the temporary directory. (Foolishly, setuptools relies on the current directory - # for many of its features.) - os.chdir(tmpDir) - - manifestBase = os.path.dirname(manifestFile) - for entry in manifest['files']: - srcDir = os.path.join(manifestBase, entry['src_dir']) - for path in entry['sources']: - srcFile = os.path.join(srcDir, path) - dstFile = os.path.join(tmpDir, path) - os.makedirs(os.path.dirname(dstFile), exist_ok=True) - shutil.copyfile(srcFile, dstFile) - - for script in installScripts: - os.rename(os.path.join(tmpDir, script.name), - os.path.join(tmpDir, script.installName)) - - # Define a custom version of the bdist_wheel command that configures the - # resultant wheel as platform-specific (i.e. not "pure"). - class bdist_wheel_override(bdist_wheel): - def finalize_options(self): - bdist_wheel.finalize_options(self) - self.root_is_pure = False - - requiredPackages = [ - "coloredlogs", - 'construct', - 'ipython', - ] - - if platform.system() == 'Darwin': - requiredPackages.append('pyobjc-framework-corebluetooth') - - if platform.system() == 'Linux': - requiredPackages.append('pygobject') - - # - # Build the chip package... - # - packages = [ - 'pychip', - ] - - # Invoke the setuptools 'bdist_wheel' command to generate a wheel containing - # the CHIP python packages, shared libraries and scripts. - setup( - name=packageName, - version=chipPackageVer, - description='Python-base APIs and tools for CHIP.', - url='https://github.com/project-chip/connectedhomeip', - license='Apache', - classifiers=[ - 'Intended Audience :: Developers', - 'License :: OSI Approved :: Apache Software License', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - ], - python_requires='>=2.7', - packages=packages, - package_dir={ - # By default, look in the tmp directory for packages/modules to be included. - '': tmpDir, - }, - package_data={ - packageName: [ - # Include the wrapper DLL as package data in the "chip" package. - chipDLLName - ] - }, - scripts=[name for name in map( - lambda script: os.path.join(tmpDir, script.installName), - installScripts - )], - install_requires=requiredPackages, - options={ - 'bdist_wheel': { - 'universal': False, - # Place the generated .whl in the dist directory. - 'dist_dir': distDir, - 'py_limited_api': 'cp37', - 'plat_name': args.plat_name, - }, - 'egg_info': { - # Place the .egg-info subdirectory in the tmp directory. - 'egg_base': tmpDir - } - }, - cmdclass={ - 'bdist_wheel': bdist_wheel_override - }, - script_args=['clean', '--all', 'bdist_wheel'] - ) - -finally: - - # Switch back to the initial current directory. - os.chdir(curDir) - - # Remove the temporary directory. - if os.path.isdir(tmpDir): - shutil.rmtree(tmpDir) diff --git a/src/pybindings/pycontroller/pychip/__init__.py b/src/pybindings/pycontroller/pychip/__init__.py deleted file mode 100644 index c2020be2dc7a4f..00000000000000 --- a/src/pybindings/pycontroller/pychip/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ - -from .PyChip import chip - -__all__ = [chip] diff --git a/src/python_testing/TC_CCTRL_2_2.py b/src/python_testing/TC_CCTRL_2_2.py index 8e1c8c5adb43a8..a2d209f0545400 100644 --- a/src/python_testing/TC_CCTRL_2_2.py +++ b/src/python_testing/TC_CCTRL_2_2.py @@ -161,7 +161,7 @@ async def test_TC_CCTRL_2_2(self): self.step(9) cmd = Clusters.AdministratorCommissioning.Commands.RevokeCommissioning() # If no exception is raised, this is success - await self.send_single_cmd(cmd) + await self.send_single_cmd(cmd, timedRequestTimeoutMs=5000) self.step(10) if not events: @@ -182,7 +182,7 @@ async def test_TC_CCTRL_2_2(self): self.step(12) if not self.is_ci: - self.wait_for_use_input("Approve Commissioning approval request using manufacturer specified mechanism") + self.wait_for_user_input("Approve Commissioning approval request using manufacturer specified mechanism") self.step(13) if not events: @@ -257,7 +257,7 @@ async def test_TC_CCTRL_2_2(self): self.step(23) if not self.is_ci: - self.wait_for_use_input("Approve Commissioning approval request using manufacturer specified mechanism") + self.wait_for_user_input("Approve Commissioning approval request using manufacturer specified mechanism") self.step(24) events = new_event diff --git a/src/python_testing/TC_DRLK_2_13.py b/src/python_testing/TC_DRLK_2_13.py new file mode 100644 index 00000000000000..5e1cdf6dcf6601 --- /dev/null +++ b/src/python_testing/TC_DRLK_2_13.py @@ -0,0 +1,655 @@ +# +# Copyright (c) 2024 Project CHIP Authors +# All rights reserved. +# +# 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. + +# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments +# for details about the block below. +# +# === BEGIN CI TEST ARGUMENTS === +# test-runner-runs: run1 +# test-runner-run/run1/app: ${CHIP_LOCK_APP} +# test-runner-run/run1/factoryreset: True +# test-runner-run/run1/quiet: True +# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json +# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto +# === END CI TEST ARGUMENTS === + +import logging +import random +from dataclasses import dataclass + +import chip.clusters as Clusters +from chip.clusters.Attribute import EventPriority +from chip.clusters.Types import NullValue +from chip.interaction_model import InteractionModelError, Status +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main, type_matches +from mobly import asserts + +logger = logging.getLogger(__name__) + +cluster = Clusters.DoorLock + + +@dataclass +class AliroAttributeVerify: + th_step: str + attribute: Clusters.DoorLock.Attributes + attribute_value: bytes + + +class TC_DRLK_2_13(MatterBaseTest): + + def steps_TC_DRLK_2_13(self) -> list[TestStep]: + steps = [ + TestStep("1a", "TH reads OperationalCredentials cluster's CurrentFabricIndex and save the attribute", + "TH Reads Attribute Successfully"), + TestStep("1b", "TH sends ClearUser Command to DUT with the UserIndex as 0xFFFE to clear all the users", + "DUT responds with Success response"), + TestStep("1c", "TH sends ClearCredential Command to DUT to clear all the credentials", + "Verify that the DUT sends SUCCESS response"), + TestStep("2a", + "TH sends ClearAliroReaderConfig Command to DUT for clearing any existing Aliro Configuration", + "Verify that the DUT sends SUCCESS response"), + TestStep("2b", ",TH sends SetAliroReaderConfig Command to DUT without GroupResolvingKey", + "DUT sends success response"), + TestStep("2c", "TH sends SetAliroReaderConfig Command to DUT with GroupResolvingKey", + "DUT sends success response"), + TestStep("3", "TH reads AliroReaderVerificationKey attribute from DUT", + "Verify that AliroReaderVerificationKey value is same as 'verificationkey'"), + TestStep("4", "TH reads AliroReaderGroupIdentifier attribute from DUT", + "Verify that AliroReaderGroupIdentifier value is same as 'groupidentifier'"), + TestStep("5", "TH reads AliroGroupResolvingKey attribute from DUT", + "Verify that AliroGroupResolvingKey value is same 'groupresolvingkey'"), + TestStep("6a", ",TH sends SetAliroReaderConfig Command to DUT without GroupResolvingKey", + "Verify that the DUT sends INVALID_IN_STATE response"), + TestStep("6b", "TH sends SetAliroReaderConfig Command to DUT with GroupResolvingKey", + "Verify that the DUT sends INVALID_IN_STATE response"), + TestStep("7", "TH sends ClearAliroReaderConfig Command to DUT", + "Verify that the DUT sends SUCCESS response"), + TestStep("8", "TH reads AliroReaderVerificationKey attribute from DUT", + "Verify that AliroReaderVerificationKey value is null"), + TestStep("9", "TH reads AliroReaderGroupIdentifier attribute from DUT", + "Verify that AliroReaderGroupIdentifier value is null"), + TestStep("10", "TH reads AliroGroupResolvingKey attribute from DUT", + "Verify that AliroGroupResolvingKey value is null"), + TestStep("11a", ",TH sends SetAliroReaderConfig Command to DUT without GroupResolvingKey", + "DUT sends success response"), + TestStep("11b", "TH sends SetAliroReaderConfig Command to DUT with GroupResolvingKey", + "DUT sends success response"), + TestStep("12a", + "TH reads NumberOfAliroEndpointKeysSupported store as 'max_aliro_keys_supported' if max_aliro_keys_supported>= 2 continue with the next steps," + "Verify that Read operation is successful"), + TestStep("12b", + "TH sends SetUser Command to DUT to create an Aliro user using username as 'AliroUser' and unique id 111", + "Verify that the DUT sends SUCCESS response"""), + TestStep("13a", "TH sends SetCredential Command Credential as (6, 2)", + "Verify that the DUT responds with SetCredentialResponse command, DUT responds with status success in SetCredentialResponse"), + TestStep("13b", "TH sends SetCredential Command with Credential as (6, 1)", + "Verify that the DUT responds with SetCredentialResponse command, DUT responds with status success in SetCredentialResponse"), + TestStep("14", "TH reads the LockUserChange event list from DUT", + "Verify list has an event LockDataType: 11 as latest event with DataOperationType: 0(DataOperationTypeEnum.Add) along with other information"), + TestStep("15", "TH sends GetCredentialStatus Command with Credential as (6,1)", + "Verify DUT responds with GetCredentialStatusResponse having CredentialExists is true and UserIndex as 1"), + TestStep("16", + "TH sends ClearCredential Command to DUT to clear the CredentialType of AliroCredentialIssuerKey", + "Verify that the DUT sends SUCCESS response"), + TestStep("17", "TH reads the LockUserChange event list from DUT", + "Verify list has an event LockDataType: 11 as latest event with DataOperationType: 1(DataOperationTypeEnum.Clear) along with other information"), + TestStep("18", "TH sends SetCredential Command to DUT with CredentialType as AliroEvictableEndpointKey", + "Verify that the DUT responds with SetCredentialResponse command, DUT responds with status success in SetCredentialResponse"), + TestStep("19", "TH reads the LockUserChange event list from DUT", + "Verify list has an event LockDataType: 11 as latest event with DataOperationType: 0(DataOperationTypeEnum.Add) along with other information"), + TestStep("20", "TH sends GetCredentialStatus Command with Credential as 7 1", + "Verify DUT responds with GetCredentialStatusResponse having CredentialExists is true and UserIndex as 1"), + TestStep("21", + "TH sends ClearCredential Command to DUT to clear the CredentialType of AliroEvictableEndpointKey", + "Verify that the DUT sends SUCCESS response"), + TestStep("22", "TH reads the LockUserChange event list from DUT", + "Verify list has an event LockDataType: 12 as latest event with DataOperationType: 1(DataOperationTypeEnum.Clear) along with other information"), + TestStep("23", "TH sends SetCredential Command to DUT with CredentialType as AliroNonEvictableEndpointKey ", + "Verify that the DUT responds with SetCredentialResponse command and Status success."), + TestStep("24", "TH reads the LockUserChange event list from DUT", + "Verify list has an event LockDataType: 12 as latest event with DataOperationType: 0(DataOperationTypeEnum.Add) along with other information"), + TestStep("25", "TH sends GetCredentialStatus Command with Credential as 8 1", + "Verify DUT responds with GetCredentialStatusResponse having CredentialExists is true and UserIndex as 1"), + TestStep("26", + "TH sends ClearCredential Command to DUT to clear the CredentialType of AliroNonEvictableEndpointKey", + "DUT sends SUCCESS response"), + TestStep("27", "TH reads the LockUserChange event list from DUT", + "Verify list has an event LockDataType: 13 as latest event with DataOperationType: 1(DataOperationTypeEnum.Clear) along with other information"), + TestStep("28a", "Th Reads NumberOfCredentialsSupportedPerUser saves as numberofcredentialsupportedperuser," + "Read operation is successful"), + TestStep("28b", "TH sends ClearCredential Command to DUT to clear all the credentials of Aliro type " + "TH sends ClearUser Command with UserIndex as 1 to DUT to clear alirouser" + "Executing steps 29 to 35 only when 'max_aliro_keys_supported <= numberofcredentialsupportedperuser' else exit script", + "Verify that Read operation is successful"), + TestStep("29a", "TH sends SetUser Command to DUT to create an Aliro user", + "DUT sends SUCCESS response"), + TestStep("29b", + "TH performs repeated number of SetCredential commands with credentials as 8 'startcredentialindex' until 'max_aliro_keys_supported - 1', startcredentialindex initially has value 1 .", + "Verify that the DUT responds with SetCredentialResponse command and Status success."), + TestStep("30", + "TH sends SetCredential Command to DUT with CredentialType as AliroEvictableEndpointKey for the 'alirouser' ", + "Verify that the DUT responds with SetCredentialResponse command and Status success. This step will fill the last slot with credentialType as AliroEvictableEndpointKey"), + TestStep("31", + "TH sends SetCredential Command to DUT with CredentialType as AliroNonEvictableEndpointKey and number of credentials for 'alirouser' exceeds the max_aliro_keys_supported", + "Verify that the DUT responds with SetCredentialResponse command and Status success."), + TestStep("32", "TH sends GetCredentialStatus Command with Credential as 7 startcredentialindex+1", + "DUT responds with GetCredentialStatusResponse Command and CredentialExists is false"), + TestStep("33", "TH sends GetCredentialStatus Command with Credential as 8 startcredentialindex+1", + "Verify DUT responds with GetCredentialStatusResponse having CredentialExists is true and UserIndex as 1"), + TestStep("34", "TH sends ClearCredential Command to DUT to clear the ALIRO CredentialType", + "Verify that the DUT sends SUCCESS response"), + TestStep("35", "TH sends ClearUser Command to DUT with the UserIndex as 1", + "Verify that the DUT sends SUCCESS response"), + ] + + return steps + + async def read_attributes_from_dut(self, endpoint, cluster, attribute, expected_status: Status = Status.Success): + try: + attribute_value = await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, + attribute=attribute) + asserts.assert_equal(expected_status, Status.Success) + return attribute_value + except Exception as e: + logging.error(e) + asserts.assert_equal(expected_status, Status.Success, + f"Error reading attributes, response={attribute_value}") + + async def aliro_attribute_verifiers(self, aliro_attribute_verify_steps: list[AliroAttributeVerify]): + + for aliro_attribute_step in aliro_attribute_verify_steps: + th_step = aliro_attribute_step.th_step + self.step(th_step) + if aliro_attribute_step.attribute == Clusters.DoorLock.Attributes.AliroReaderVerificationKey: + pics_condition = self.pics_guard(self.check_pics("DRLK.S.F0d") and self.check_pics("DRLK.S.A0080")) + elif aliro_attribute_step.attribute == Clusters.DoorLock.Attributes.AliroReaderGroupIdentifier: + pics_condition = self.pics_guard(self.check_pics("DRLK.S.F0d") and self.check_pics("DRLK.S.A0081")) + elif aliro_attribute_step.attribute == Clusters.DoorLock.Attributes.AliroGroupResolvingKey: + pics_condition = self.pics_guard(self.check_pics("DRLK.S.F0d") and self.check_pics("DRLK.S.A0084")) + else: + pics_condition = False + + if pics_condition: + dut_aliro_key = await self.read_attributes_from_dut(endpoint=self.app_cluster_endpoint, + cluster=Clusters.Objects.DoorLock, + attribute=aliro_attribute_step.attribute) + asserts.assert_equal(dut_aliro_key, aliro_attribute_step.attribute_value, + f"Aliro Attribute key verification Failed, readAttributeResponse{dut_aliro_key}") + + def pics_TC_DRLK_2_13(self) -> list[str]: + return ["DRLK.S.F0d"] + + async def send_clear_user_cmd(self, user_index, expected_status: Status = Status.Success): + try: + await self.send_single_cmd(cmd=Clusters.DoorLock.Commands.ClearUser(userIndex=user_index), + endpoint=self.app_cluster_endpoint, + timedRequestTimeoutMs=1000) + asserts.assert_equal(expected_status, Status.Success) + except InteractionModelError as e: + asserts.assert_equal(e.status, expected_status, f"Unexpected error returned: {e}") + + async def send_clear_aliro_reader_config_cmd(self, expected_status: Status = Status.Success): + try: + await self.send_single_cmd(cmd=Clusters.DoorLock.Commands.ClearAliroReaderConfig(), + endpoint=self.app_cluster_endpoint, + timedRequestTimeoutMs=1000) + asserts.assert_equal(expected_status, Status.Success) + except InteractionModelError as e: + asserts.assert_equal(e.status, expected_status, f"Unexpected error returned: {e}") + + async def send_set_aliro_reader_config_cmd(self, use_group_resolving_key: bool, + expected_status: Status = Status.Success): + try: + # Checks Pics condition + if use_group_resolving_key is False: + pics_check = self.pics_guard(self.check_pics("DRLK.S.F0d") and not self.check_pics("DRLK.S.F0e") and + self.check_pics("DRLK.S.C28.Rsp")) + + else: + pics_check = self.pics_guard(self.check_pics("DRLK.S.F0e") and self.check_pics("DRLK.S.C28.Rsp")) + + if not use_group_resolving_key and pics_check: + await self.send_single_cmd(cmd=Clusters.DoorLock.Commands.SetAliroReaderConfig( + signingKey=self.signingKey, + verificationKey=self.verificationKey, + groupIdentifier=self.groupIdentifier), + endpoint=self.app_cluster_endpoint, + timedRequestTimeoutMs=1000) + asserts.assert_equal(expected_status, Status.Success) + elif use_group_resolving_key and pics_check: + await self.send_single_cmd(cmd=Clusters.DoorLock.Commands.SetAliroReaderConfig( + signingKey=self.signingKey, + verificationKey=self.verificationKey, + groupIdentifier=self.groupIdentifier, + groupResolvingKey=self.groupResolvingKey), + endpoint=self.app_cluster_endpoint, + timedRequestTimeoutMs=1000) + asserts.assert_equal(expected_status, Status.Success) + except InteractionModelError as e: + logging.exception(f"Got exception when performing SetAliroReaderConfig {e}") + asserts.assert_equal(e.status, expected_status, f"Unexpected error returned: {e}") + + async def get_credentials_status(self, credentialIndex: int, credentialType: cluster.Enums.CredentialTypeEnum, + step, userIndex, credential_exists=True): + if step: + self.step(step) + try: + flags = ["DRLK.S.F0d", "DRLK.S.C24.Rsp", "DRLK.S.C25.Tx"] + if not self.pics_guard(all([self.check_pics(p) for p in flags])): + credentials_struct = cluster.Structs.CredentialStruct(credentialIndex=credentialIndex, + credentialType=credentialType) + response = await self.send_single_cmd(endpoint=self.app_cluster_endpoint, timedRequestTimeoutMs=1000, + cmd=cluster.Commands.GetCredentialStatus( + credential=credentials_struct)) + asserts.assert_true(type_matches(response, Clusters.DoorLock.Commands.GetCredentialStatusResponse), + "Unexpected return type for GetCredentialStatus") + asserts.assert_true(response.credentialExists == credential_exists, + "Error when executing GetCredentialStatus command, credentialExists={}".format( + str(response.credentialExists))) + asserts.assert_equal(userIndex, response.userIndex, + f"User Index is not matching, UserIndex={response.userIndex}") + return response + except InteractionModelError as e: + logging.error(e) + asserts.assert_equal(e.status, Status.Success, f"Unexpected error returned: {e}") + + async def set_credential_cmd(self, credential_enum: Clusters.DoorLock.Enums.CredentialTypeEnum, credentialIndex, + operationType, userIndex, credentialData, userStatus, userType, step=None): + if step: + self.step(step) + credentials = cluster.Structs.CredentialStruct( + credentialType=credential_enum, + credentialIndex=credentialIndex) + if self.pics_guard(self.check_pics("DRLK.S.F0d") and self.check_pics("DRLK.S.C22.Rsp")): + try: + response = await self.send_single_cmd(cmd=Clusters.Objects.DoorLock.Commands.SetCredential( + operationType=operationType, + credential=credentials, + credentialData=credentialData, + userStatus=userStatus, + userType=userType, + userIndex=userIndex), + endpoint=self.app_cluster_endpoint, + timedRequestTimeoutMs=1000) + asserts.assert_true(type_matches(response, Clusters.Objects.DoorLock.Commands.SetCredentialResponse), + "Unexpected return type for SetCredential") + asserts.assert_true(response.status == Status.Success, + "Error sending SetCredential command, status={}".format(str(response.status))) + except InteractionModelError as e: + logging.exception(e) + asserts.assert_equal(e.status, Status.Success, f"Unexpected error returned: {e}") + + async def read_and_validate_lock_event_change(self, verify_lock_data_type, priority, operation_source, + data_type_operation, fabric_index, node_id, data_index, user_index): + response_event_list = await self.default_controller.ReadEvent(self.dut_node_id, + [1, cluster.Events.LockUserChange]) + for event_read_result in reversed(response_event_list): + if not hasattr(event_read_result.Data, "lockDataType"): + continue + if event_read_result.Data.lockDataType == verify_lock_data_type: + asserts.assert_equal(event_read_result.Header.Priority, priority, + f"Priority mismatch, Priority={event_read_result.Header.Priority}") + asserts.assert_equal(event_read_result.Data.operationSource, operation_source, + f"OperationSource not matching, operationSource={operation_source}") + asserts.assert_equal(event_read_result.Data.fabricIndex, fabric_index, + f"FabricIndex not matching, fabricIndex={fabric_index}") + asserts.assert_equal(event_read_result.Data.dataIndex, data_index, + f"DataIndex not matching, dataIndex={data_index}") + asserts.assert_equal(event_read_result.Data.sourceNode, node_id, + f"SourceNode not matching, sourceNode={node_id}") + asserts.assert_equal(event_read_result.Data.userIndex, user_index, + f"UserIndex not matching, userIndex={user_index}") + asserts.assert_equal(event_read_result.Data.dataOperationType, data_type_operation, + f"DataOperationType not matching, dataOperationType={data_type_operation}") + break + + async def clear_credentials_cmd(self, credential, step=None, expected_status: Status = Status.Success): + try: + if step is not None: + self.step(step) + if self.pics_guard(self.check_pics("DRLK.S.F0d") and self.check_pics("DRLK.S.C26.Rsp")): + await self.send_single_cmd(cmd=Clusters.DoorLock.Commands.ClearCredential(credential=credential), + endpoint=self.app_cluster_endpoint, + timedRequestTimeoutMs=1000) + asserts.assert_equal(expected_status, Status.Success) + except InteractionModelError as e: + logging.exception(e) + asserts.assert_equal(e.status, expected_status, f"Unexpected error returned: {e}") + + async def clear_all_aliro_credential(self): + all_aliro_cred_list = [] + aliro_cred_issuer_credentials = cluster.Structs.CredentialStruct(credentialIndex=1, + credentialType=Clusters.DoorLock.Enums.CredentialTypeEnum.kAliroCredentialIssuerKey) + all_aliro_cred_list.append(aliro_cred_issuer_credentials) + aliro_evict_endpoint_credentials = cluster.Structs.CredentialStruct(credentialIndex=1, + credentialType=Clusters.DoorLock.Enums.CredentialTypeEnum.kAliroEvictableEndpointKey) + all_aliro_cred_list.append(aliro_evict_endpoint_credentials) + aliro_non_evict_endpoint_credentials = cluster.Structs.CredentialStruct(credentialIndex=1, + credentialType=Clusters.DoorLock.Enums.CredentialTypeEnum.kAliroNonEvictableEndpointKey) + all_aliro_cred_list.append(aliro_non_evict_endpoint_credentials) + for creds in all_aliro_cred_list: + await self.clear_credentials_cmd(credential=creds) + + def generate_unique_octbytes(self, length=65) -> bytes: + return ''.join(random.choices('01234567', k=length)).encode() + + @async_test_body + async def test_TC_DRLK_2_13(self): + self.max_aliro_keys_supported = None + self.numberofcredentialsupportedperuser = None + self.signingKey = bytes.fromhex("89d085fc302ca53e279bfcdecdf3c4adb2f5d9bc9ea6c49e9566d144367df3ff") + self.verificationKey = bytes.fromhex( + "047a4c992d753924cdf3779a3c84fec2debaa6f0b3084450878acc7ddcce7856ae57b1ebbe2561015103dd7474c2a183675378ec55f1e465ac3436bf3dd5ca54d4") + self.groupIdentifier = bytes.fromhex("89d085fc302ca53e279bfcdecdf3c4ad") + self.groupResolvingKey = bytes.fromhex("89d0859bfcdecdf3c4adfc302ca53e27") + self.common_cluster_endpoint = 0 + self.app_cluster_endpoint = 1 + self.alirouser = "AliroUser" + self.alirocredentialissuerkey = bytes.fromhex( + "047a4c882d753924cdf3779a3c84fec2debaa6f0b3084450878acc7ddcce7856ae57b1ebbe2561015103dd7474c2a183675378ec55f1e465ac3436bf3dd5ca54d4") + self.alirocredentialissuerkey_secondary = bytes.fromhex( + "047a4c112d753924cdf3779a3c84fec2debaa6f0b3084450878acc7ddcce7856ae57b1ebbe2561015103dd7474c2a183675378ec55f1e465ac3436bf3dd5ca54d4") + self.aliroevictableendpointkey = bytes.fromhex( + "047a4c772d753924cdf3779a3c84fec2debaa6f0b3084450878acc7ddcce7856ae57b1ebbe2561015103dd7474c2a183675378ec55f1e465ac3436bf3dd5ca54d4") + self.alirononevictableendpointkey = bytes.fromhex( + "047a4c662d753924cdf3779a3c84fec2debaa6f0b3084450878acc7ddcce7856ae57b1ebbe2561015103dd7474c2a183675378ec55f1e465ac3436bf3dd5ca54d4") + self.alirononevictableendpointkey1 = bytes.fromhex( + "047a4c552d753924cdf3779a3c84fec2debaa6f0b3084450878acc7ddcce7856ae57b1ebbe2561015103dd7474c2a183675378ec55f1e465ac3436bf3dd5ca54d4") + # step 1 TH reads DUT Endpoint 0 OperationalCredentials cluster CurrentFabricIndex attribute + self.step("1a") + self.fabric_idx1 = await self.read_attributes_from_dut(endpoint=self.common_cluster_endpoint, + cluster=Clusters.Objects.OperationalCredentials, + attribute=Clusters.OperationalCredentials.Attributes.CurrentFabricIndex + ) + self.step("1b") + if self.pics_guard(self.check_pics("DRLK.S.C1d.Rsp")): + await self.send_clear_user_cmd(user_index=int(0xFFFE)) + + self.step("1c") + if self.pics_guard(self.check_pics("DRLK.S.C26.Rsp")): + await self.clear_credentials_cmd(credential=NullValue) + + # step 2 + self.step("2a") + if self.pics_guard(self.check_pics("DRLK.S.C26.Rsp")): + await self.send_clear_aliro_reader_config_cmd() + self.step("2b") + await self.send_set_aliro_reader_config_cmd(use_group_resolving_key=False, expected_status=Status.Success) + self.step("2c") + await self.send_set_aliro_reader_config_cmd(use_group_resolving_key=True, expected_status=Status.Success) + # step 3,4,5 + aliro_attribute_verify_steps = [ + AliroAttributeVerify(th_step="3", attribute=Clusters.Objects.DoorLock.Attributes.AliroReaderVerificationKey, + attribute_value=self.verificationKey), + AliroAttributeVerify(th_step="4", attribute=Clusters.Objects.DoorLock.Attributes.AliroReaderGroupIdentifier, + attribute_value=self.groupIdentifier), + AliroAttributeVerify(th_step="5", attribute=Clusters.Objects.DoorLock.Attributes.AliroGroupResolvingKey, + attribute_value=self.groupResolvingKey) + ] + await self.aliro_attribute_verifiers(aliro_attribute_verify_steps=aliro_attribute_verify_steps) + + # step 6 + self.step("6a") + await self.send_set_aliro_reader_config_cmd(use_group_resolving_key=False, + expected_status=Status.InvalidInState) + self.step("6b") + await self.send_set_aliro_reader_config_cmd(use_group_resolving_key=True, expected_status=Status.InvalidInState) + # step 7 + self.step("7") + if self.check_pics("DRLK.S.C26.Rsp"): + await self.send_clear_aliro_reader_config_cmd() + + # steps 8,9,10 + aliro_attribute_verify_steps = [ + AliroAttributeVerify(th_step="8", attribute=Clusters.Objects.DoorLock.Attributes.AliroReaderVerificationKey, + attribute_value=NullValue), + AliroAttributeVerify(th_step="9", attribute=Clusters.Objects.DoorLock.Attributes.AliroReaderGroupIdentifier, + attribute_value=NullValue), + AliroAttributeVerify(th_step="10", attribute=Clusters.Objects.DoorLock.Attributes.AliroGroupResolvingKey, + attribute_value=NullValue) + ] + await self.aliro_attribute_verifiers(aliro_attribute_verify_steps=aliro_attribute_verify_steps) + + # step 11 + self.step("11a") + await self.send_set_aliro_reader_config_cmd(use_group_resolving_key=False, expected_status=Status.Success) + self.step("11b") + await self.send_set_aliro_reader_config_cmd(use_group_resolving_key=True, expected_status=Status.Success) + + # step 12 Setting User + self.step("12a") + if self.pics_guard(self.check_pics("DRLK.S.A0088")): + self.max_aliro_keys_supported = await self.read_attributes_from_dut(endpoint=self.app_cluster_endpoint, + cluster=Clusters.Objects.DoorLock, + attribute=Clusters.DoorLock.Attributes.NumberOfAliroEndpointKeysSupported) + if self.max_aliro_keys_supported < 2: + self.skip_all_remaining_steps("13") + return + self.step("12b") + if self.pics_guard(self.check_pics("DRLK.S.F08") and self.check_pics("DRLK.S.C1a.Rsp")): + try: + await self.send_single_cmd(cmd=Clusters.Objects.DoorLock.Commands.SetUser( + operationType=Clusters.DoorLock.Enums.DataOperationTypeEnum.kAdd, + userIndex=1, + userName=self.alirouser, + userUniqueID=111, + userStatus=Clusters.DoorLock.Enums.UserStatusEnum.kOccupiedEnabled, + userType=Clusters.DoorLock.Enums.UserTypeEnum.kUnrestrictedUser, + credentialRule=Clusters.DoorLock.Enums.CredentialRuleEnum.kSingle), + endpoint=self.app_cluster_endpoint, + timedRequestTimeoutMs=1000) + except InteractionModelError as e: + logging.exception(e) + asserts.assert_equal(e.status, Status.Success, f"Unexpected error returned: {e}") + + # step 13 + self.step("13a") + # The first SetCredentialsCommand is done as a placeholder for the user slot as ClearCredentials removes user info as well + await self.set_credential_cmd(credentialData=self.alirononevictableendpointkey1, + operationType=cluster.Enums.DataOperationTypeEnum.kAdd, + credential_enum=cluster.Enums.CredentialTypeEnum.kAliroNonEvictableEndpointKey, + credentialIndex=2, userIndex=1, userStatus=NullValue, userType=NullValue) + self.step("13b") + await self.set_credential_cmd(credentialData=self.alirocredentialissuerkey, + operationType=cluster.Enums.DataOperationTypeEnum.kAdd, + credential_enum=cluster.Enums.CredentialTypeEnum.kAliroCredentialIssuerKey, + credentialIndex=1, userIndex=1, userStatus=NullValue, userType=NullValue) + + # step 14 + self.step("14") + await self.read_and_validate_lock_event_change( + verify_lock_data_type=Clusters.DoorLock.Enums.LockDataTypeEnum.kAliroCredentialIssuerKey, + operation_source=Clusters.DoorLock.Enums.OperationSourceEnum.kRemote, user_index=1, + data_index=1, fabric_index=self.fabric_idx1, node_id=self.matter_test_config.controller_node_id, + data_type_operation=Clusters.DoorLock.Enums.DataOperationTypeEnum.kAdd, priority=EventPriority.INFO + ) + + # step 15 + self.step("15") + await self.get_credentials_status(credentialIndex=1, + credentialType=cluster.Enums.CredentialTypeEnum.kAliroCredentialIssuerKey, + step=None, userIndex=1) + + # step 16 + credentials = cluster.Structs.CredentialStruct(credentialIndex=1, + credentialType=cluster.Enums.CredentialTypeEnum.kAliroCredentialIssuerKey) + await self.clear_credentials_cmd(step="16", credential=credentials) + + # step 17 + self.step("17") + await self.read_and_validate_lock_event_change( + verify_lock_data_type=Clusters.DoorLock.Enums.LockDataTypeEnum.kAliroCredentialIssuerKey, + operation_source=Clusters.DoorLock.Enums.OperationSourceEnum.kRemote, user_index=1, + data_index=1, fabric_index=self.fabric_idx1, node_id=self.matter_test_config.controller_node_id, + data_type_operation=Clusters.DoorLock.Enums.DataOperationTypeEnum.kClear, priority=EventPriority.INFO + ) + + # step 18 + await self.set_credential_cmd(step="18", credentialData=self.aliroevictableendpointkey, + operationType=cluster.Enums.DataOperationTypeEnum.kAdd, + credential_enum=cluster.Enums.CredentialTypeEnum.kAliroEvictableEndpointKey, + credentialIndex=1, userIndex=1, userStatus=NullValue, userType=NullValue) + # step 19 + self.step("19") + await self.read_and_validate_lock_event_change( + verify_lock_data_type=Clusters.DoorLock.Enums.LockDataTypeEnum.kAliroEvictableEndpointKey, + operation_source=Clusters.DoorLock.Enums.OperationSourceEnum.kRemote, user_index=1, + data_index=1, fabric_index=self.fabric_idx1, node_id=self.matter_test_config.controller_node_id, + data_type_operation=Clusters.DoorLock.Enums.DataOperationTypeEnum.kAdd, priority=EventPriority.INFO + ) + # step 20 + await self.get_credentials_status(credentialIndex=1, + credentialType=cluster.Enums.CredentialTypeEnum.kAliroEvictableEndpointKey, + step="20", userIndex=1) + # step 21 + evictable_credentials = cluster.Structs.CredentialStruct(credentialIndex=1, + credentialType=cluster.Enums.CredentialTypeEnum.kAliroEvictableEndpointKey) + await self.clear_credentials_cmd(step="21", credential=evictable_credentials) + + # step 22 + self.step("22") + await self.read_and_validate_lock_event_change( + verify_lock_data_type=Clusters.DoorLock.Enums.LockDataTypeEnum.kAliroEvictableEndpointKey, + operation_source=Clusters.DoorLock.Enums.OperationSourceEnum.kRemote, user_index=1, + data_index=1, fabric_index=self.fabric_idx1, node_id=self.matter_test_config.controller_node_id, + data_type_operation=Clusters.DoorLock.Enums.DataOperationTypeEnum.kClear, priority=EventPriority.INFO + ) + + # step 23 + await self.set_credential_cmd(step="23", credentialData=self.alirononevictableendpointkey, + operationType=cluster.Enums.DataOperationTypeEnum.kAdd, + credential_enum=cluster.Enums.CredentialTypeEnum.kAliroNonEvictableEndpointKey, + credentialIndex=1, userIndex=1, userStatus=NullValue, userType=NullValue) + # step 24 + self.step("24") + await self.read_and_validate_lock_event_change( + verify_lock_data_type=Clusters.DoorLock.Enums.LockDataTypeEnum.kAliroNonEvictableEndpointKey, + operation_source=Clusters.DoorLock.Enums.OperationSourceEnum.kRemote, user_index=1, + data_index=1, fabric_index=self.fabric_idx1, node_id=self.matter_test_config.controller_node_id, + data_type_operation=Clusters.DoorLock.Enums.DataOperationTypeEnum.kAdd, priority=EventPriority.INFO + ) + + # step 25 + await self.get_credentials_status(step="25", credentialIndex=1, + credentialType=cluster.Enums.CredentialTypeEnum.kAliroNonEvictableEndpointKey, + userIndex=1) + + # step 26 + non_evictable_credentials = cluster.Structs.CredentialStruct(credentialIndex=1, + credentialType=cluster.Enums.CredentialTypeEnum.kAliroNonEvictableEndpointKey) + await self.clear_credentials_cmd(step="26", credential=non_evictable_credentials) + + # step 27 + self.step("27") + await self.read_and_validate_lock_event_change( + verify_lock_data_type=Clusters.DoorLock.Enums.LockDataTypeEnum.kAliroNonEvictableEndpointKey, + operation_source=Clusters.DoorLock.Enums.OperationSourceEnum.kRemote, user_index=1, + data_index=1, fabric_index=self.fabric_idx1, node_id=self.matter_test_config.controller_node_id, + data_type_operation=Clusters.DoorLock.Enums.DataOperationTypeEnum.kClear, priority=EventPriority.INFO + ) + + # step 28 + self.step("28a") + if self.check_pics("DRLK.S.A001c"): + self.numberofcredentialsupportedperuser = await self.read_attributes_from_dut( + endpoint=self.app_cluster_endpoint, + cluster=Clusters.Objects.DoorLock, + attribute=Clusters.DoorLock.Attributes.NumberOfCredentialsSupportedPerUser) + self.step("28b") + await self.clear_all_aliro_credential() + await self.send_clear_user_cmd(user_index=1) + if self.pics_guard(self.check_pics("DRLK.S.A001c") and self.check_pics("DRLK.S.A0088")): + if self.max_aliro_keys_supported > self.numberofcredentialsupportedperuser: + logging.info( + "Skipping execution from Step 29a to step 35 since 'max_aliro_keys_supported > numberofcredentialsupportedperuser' as per test plan spec") + self.skip_all_remaining_steps("29a") + else: + # Perform setUser as we have removed user in previous step and SetCredentials always expects a user with index to be present + self.step("29a") + if self.pics_guard(self.check_pics("DRLK.S.F08") and self.check_pics("DRLK.S.C1a.Rsp")): + try: + await self.send_single_cmd(cmd=Clusters.Objects.DoorLock.Commands.SetUser( + operationType=Clusters.DoorLock.Enums.DataOperationTypeEnum.kAdd, + userIndex=1, + userName="alirouser", + userUniqueID=111, + userStatus=Clusters.DoorLock.Enums.UserStatusEnum.kOccupiedEnabled, + userType=Clusters.DoorLock.Enums.UserTypeEnum.kUnrestrictedUser, + credentialRule=Clusters.DoorLock.Enums.CredentialRuleEnum.kSingle), + endpoint=self.app_cluster_endpoint, + timedRequestTimeoutMs=1000) + except InteractionModelError as e: + logging.exception(e) + asserts.assert_equal(e.status, Status.Success, f"Unexpected error returned: {e}") + self.step("29b") + logging.info("setting 'start_credential_index' to value 1 ") + start_credential_index = 1 + credentials_data = self.alirononevictableendpointkey + while 1: + if start_credential_index <= (self.max_aliro_keys_supported - 2): + if start_credential_index != 1: + credentials_data = self.generate_unique_octbytes() + + await self.set_credential_cmd(credentialData=credentials_data, + operationType=cluster.Enums.DataOperationTypeEnum.kAdd, + credential_enum=cluster.Enums.CredentialTypeEnum.kAliroNonEvictableEndpointKey, + credentialIndex=start_credential_index, userIndex=1, + userStatus=NullValue, + userType=NullValue) + start_credential_index += 1 + logging.info(f"The updated value of start_credential_index is {start_credential_index}") + else: + break + + # step 30 + logging.info(f"the value of start_credential_index is {start_credential_index} for step 30") + self.step("30") + await self.set_credential_cmd(credentialData=self.alirononevictableendpointkey, + operationType=cluster.Enums.DataOperationTypeEnum.kAdd, + credential_enum=cluster.Enums.CredentialTypeEnum.kAliroEvictableEndpointKey, + credentialIndex=start_credential_index, userIndex=1, userStatus=NullValue, + userType=NullValue) + # step 31 + self.step("31") + await self.set_credential_cmd(credentialData=self.alirononevictableendpointkey1, + operationType=cluster.Enums.DataOperationTypeEnum.kAdd, + credential_enum=cluster.Enums.CredentialTypeEnum.kAliroNonEvictableEndpointKey, + credentialIndex=start_credential_index, userIndex=1, userStatus=NullValue, + userType=NullValue) + # step 32 + dut_get_cred_response = await self.get_credentials_status(step="32", + credentialIndex=start_credential_index, + credentialType=cluster.Enums.CredentialTypeEnum.kAliroEvictableEndpointKey, + credential_exists=False, userIndex=NullValue) + asserts.assert_equal(dut_get_cred_response.credentialExists, NullValue, + f"Error when comparing for {dut_get_cred_response} of null") + + # step 33 + await self.get_credentials_status(step="33", credentialIndex=start_credential_index, + credentialType=cluster.Enums.CredentialTypeEnum.kAliroNonEvictableEndpointKey, + credential_exists=True, userIndex=1) + # step 34 + self.step("34") + await self.clear_all_aliro_credential() + + # step 35 + self.step("35") + await self.send_clear_user_cmd(user_index=1) + + +if __name__ == '__main__': + default_matter_test_main() diff --git a/src/python_testing/TC_EWATERHTRBase.py b/src/python_testing/TC_EWATERHTRBase.py index 0bf42a7eb4d0a0..6aba67edd20140 100644 --- a/src/python_testing/TC_EWATERHTRBase.py +++ b/src/python_testing/TC_EWATERHTRBase.py @@ -42,15 +42,16 @@ async def send_boost_command(self, duration: int, one_shot: typing.Optional[bool endpoint: int = None, timedRequestTimeoutMs: int = 3000, expected_status: Status = Status.Success): try: - await self.send_single_cmd(cmd=Clusters.WaterHeaterManagement.Commands.Boost( - duration=duration, - oneShot=one_shot, - emergencyBoost=emergency_boost, - temporarySetpoint=temporary_setpoint, - targetPercentage=target_percentage, - targetReheat=target_reheat), - endpoint=endpoint, - timedRequestTimeoutMs=timedRequestTimeoutMs) + boostInfo = Clusters.WaterHeaterManagement.Structs.WaterHeaterBoostInfoStruct(duration=duration, + oneShot=one_shot, + emergencyBoost=emergency_boost, + temporarySetpoint=temporary_setpoint, + targetPercentage=target_percentage, + targetReheat=target_reheat) + + await self.send_single_cmd(cmd=Clusters.WaterHeaterManagement.Commands.Boost(boostInfo=boostInfo), + endpoint=endpoint, + timedRequestTimeoutMs=timedRequestTimeoutMs) asserts.assert_equal(expected_status, Status.Success) diff --git a/src/python_testing/TC_EWATERHTR_2_1.py b/src/python_testing/TC_EWATERHTR_2_1.py index 7400133c7a7345..055b5923cf6f07 100644 --- a/src/python_testing/TC_EWATERHTR_2_1.py +++ b/src/python_testing/TC_EWATERHTR_2_1.py @@ -53,9 +53,9 @@ def steps_TC_EWATERHTR_2_1(self) -> list[TestStep]: TestStep("1", "Commissioning, already done", is_commissioning=True), TestStep("2", "TH reads HeaterTypes attribute.", - "DUT as Server replies with a WaterHeaterTypeBitmap (enum8) greater than 0x00 (at least one type supported), and less than 0x20 (no undefined types supported)."), + "DUT as Server replies with a WaterHeaterHeatSourceBitmap (enum8) greater than 0x00 (at least one type supported), and less than 0x20 (no undefined types supported)."), TestStep("3", "TH reads HeatDemand attribute.", - "DUT as Server replies with a WaterHeaterDemandBitmap (enum8)."), + "DUT as Server replies with a WaterHeaterHeatSourceBitmap (enum8)."), TestStep("4", "TH reads TankVolume attribute.", "DUT as Server replies with a uint16 value."), TestStep("5", "TH reads EstimatedHeatRequired attribute.", @@ -81,15 +81,15 @@ async def test_TC_EWATERHTR_2_1(self): heaterTypes = await self.read_whm_attribute_expect_success(attribute="HeaterTypes") asserts.assert_greater(heaterTypes, 0, f"Unexpected HeaterTypes value - expected {heaterTypes} > 0") - asserts.assert_less_equal(heaterTypes, Clusters.WaterHeaterManagement.Bitmaps.WaterHeaterTypeBitmap.kOther, - f"Unexpected HeaterTypes value - expected {heaterTypes} <= WaterHeaterTypeBitmap.kOther") + asserts.assert_less_equal(heaterTypes, Clusters.WaterHeaterManagement.Bitmaps.WaterHeaterHeatSourceBitmap.kOther, + f"Unexpected HeaterTypes value - expected {heaterTypes} <= WaterHeaterHeatSourceBitmap.kOther") self.step("3") heatDemand = await self.read_whm_attribute_expect_success(attribute="HeatDemand") asserts.assert_greater(heatDemand, 0, f"Unexpected HeatDemand value - expected {heatDemand} > 0") - asserts.assert_less_equal(heatDemand, Clusters.WaterHeaterManagement.Bitmaps.WaterHeaterDemandBitmap.kOther, - f"Unexpected HeatDemand value - expected {heatDemand} <= WaterHeaterDemandBitmap.kOther") + asserts.assert_less_equal(heatDemand, Clusters.WaterHeaterManagement.Bitmaps.WaterHeaterHeatSourceBitmap.kOther, + f"Unexpected HeatDemand value - expected {heatDemand} <= WaterHeaterHeatSourceBitmap.kOther") self.step("4") if em_supported: diff --git a/src/python_testing/TC_EWATERHTR_2_2.py b/src/python_testing/TC_EWATERHTR_2_2.py index c1d23adc58f69d..5fd40e4130a64e 100644 --- a/src/python_testing/TC_EWATERHTR_2_2.py +++ b/src/python_testing/TC_EWATERHTR_2_2.py @@ -191,8 +191,8 @@ async def test_TC_EWATERHTR_2_2(self): heaterTypes = await self.read_whm_attribute_expect_success(attribute="HeaterTypes") asserts.assert_greater(heaterTypes, 0, f"Unexpected HeaterTypes value - expected {heaterTypes} > 0") - asserts.assert_less_equal(heaterTypes, Clusters.WaterHeaterManagement.Bitmaps.WaterHeaterTypeBitmap.kOther, - f"Unexpected HeaterTypes value - expected {heaterTypes} <= WaterHeaterTypeBitmap.kOther") + asserts.assert_less_equal(heaterTypes, Clusters.WaterHeaterManagement.Bitmaps.WaterHeaterHeatSourceBitmap.kOther, + f"Unexpected HeaterTypes value - expected {heaterTypes} <= WaterHeaterHeatSourceBitmap.kOther") self.step("4") await self.send_test_event_trigger_manual_mode_test_event() diff --git a/src/python_testing/TC_ICDM_3_4.py b/src/python_testing/TC_ICDM_3_4.py new file mode 100644 index 00000000000000..61230532178a08 --- /dev/null +++ b/src/python_testing/TC_ICDM_3_4.py @@ -0,0 +1,119 @@ + +# +# Copyright (c) 2023 Project CHIP Authors +# All rights reserved. +# +# 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. +# + +# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments +# for details about the block below. +# +# === BEGIN CI TEST ARGUMENTS === +# test-runner-runs: run1 +# test-runner-run/run1/app: ${LIT_ICD_APP} +# test-runner-run/run1/factoryreset: True +# test-runner-run/run1/quiet: True +# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json +# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto +# === END CI TEST ARGUMENTS === + +import logging +import time + +import chip.clusters as Clusters +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from mobly import asserts + +logger = logging.getLogger(__name__) + +kRootEndpointId = 0 +cluster = Clusters.Objects.IcdManagement +attributes = cluster.Attributes + + +class TC_ICDM_3_4(MatterBaseTest): + + # + # Class Helper functions + # + async def _read_icdm_attribute_expect_success(self, attribute): + return await self.read_single_attribute_check_success(endpoint=kRootEndpointId, cluster=cluster, attribute=attribute) + + # + # Test Harness Helpers + # + + def desc_TC_ICDM_3_4(self) -> str: + """Returns a description of this test""" + return "[TC-ICDM-3.4] ICDCounter Persistence with DUT as Server" + + def steps_TC_ICDM_3_4(self) -> list[TestStep]: + steps = [ + TestStep(0, "Commissioning, already done", is_commissioning=True), + TestStep(1, "TH reads from the DUT the ICDCounter attribute."), + TestStep("2a", "Reboot DUT."), + TestStep("2b", "TH waits for {PIXIT.WAITTIME.REBOOT}"), + TestStep(3, "Verify that the DUT response contains value of ICDCounter and stores in IcdCounter2. \ + IcdCounter2 is greater or equal to IcdCounter1. \ + ICDCounter attribute can roll over. If the attribute rolls over, it will be greater or equal to 0.") + ] + return steps + + def pics_TC_ICDM_3_4(self) -> list[str]: + """ This function returns a list of PICS for this test case that must be True for the test to be run""" + pics = [ + "ICDM.S", + "ICDM.S.F00" + ] + return pics + + # + # ICDM 3.4 Test Body + # + + @async_test_body + async def test_TC_ICDM_3_4(self): + is_ci = self.check_pics("PICS_SDK_CI_ONLY") + + if not is_ci: + asserts.assert_true('PIXIT.WAITTIME.REBOOT' in self.matter_test_config.global_test_params, + "PIXIT.WAITTIME.REBOOT must be included on the command line in " + "the --int-arg flag as PIXIT.WAITTIME.REBOOT:") + + wait_time_reboot = self.matter_test_config.global_test_params['PIXIT.WAITTIME.REBOOT'] + if wait_time_reboot == 0: + asserts.fail("PIXIT.WAITTIME.REBOOT shall be higher than 0.") + + # Pre-Condition: Commissioning + self.step(0) + + self.step(1) + icdCounter1 = await self._read_icdm_attribute_expect_success(attribute=attributes.ICDCounter) + + self.step("2a") + if not is_ci: + self.wait_for_user_input(prompt_msg="Restart DUT. Press Enter when restart has been initiated.") + + self.step("2b") + if not is_ci: + time.sleep(wait_time_reboot) + + self.step(3) + icdCounter2 = await self._read_icdm_attribute_expect_success(attribute=attributes.ICDCounter) + asserts.assert_greater_equal(icdCounter2, icdCounter1, + "ICDCounter have reboot is not greater or equal to the ICDCounter read before the reboot.") + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_LVL_2_3.py b/src/python_testing/TC_LVL_2_3.py index 8d31b8641c3f7e..f3fac953b4d715 100644 --- a/src/python_testing/TC_LVL_2_3.py +++ b/src/python_testing/TC_LVL_2_3.py @@ -43,15 +43,18 @@ def steps_TC_LVL_2_3(self) -> list[TestStep]: THRead = "TH reads" THcommand = "TH sends the command" return [TestStep(1, test_plan_support.commission_if_required(), is_commissioning=True), - TestStep(2, f"{THRead} FeatureMap attribute."), - TestStep(3, f"{THRead} MaxLevel attribute and store value as maxLevel", test_plan_support.verify_success()), - TestStep(4, f"{THcommand} MoveWithOnOff with MoveMode field set to Down and remaining fields set to 0", + TestStep(2, f"{THRead} FeatureMap attribute and the AttributeList value"), + TestStep( + "3a", f"If the MaxLevel attribute is in the AttributeList, {THRead} MaxLevel attribute and store value as maxLevel, otherwise set maxLevel to 254", test_plan_support.verify_success()), + TestStep( + "3b", f"If the MinLevel attribute is in the AttributeList, {THRead} MinLevel attribute and store value as minLevel, otherwise set minLevel to 1", test_plan_support.verify_success()), + TestStep(4, f"{THcommand} MoveWithOnOff with MoveMode field set to Down and rate set to 254 and remaining fields set to 0", test_plan_support.verify_success()), TestStep(5, f"{THRead} CurrentLevel attribute and store value as startCurrentLevel", test_plan_support.verify_success()), TestStep(6, "Set up a subscription wildcard subscription for the Level Control Cluster, with MinIntervalFloor set to 0, MaxIntervalCeiling set to 30 and KeepSubscriptions set to false", "Subscription successfully established"), - TestStep(7, f"{THcommand} MoveToLevel with Level field set to maxLevel, TransitionTime field set to 100 (10s) and remaining fields set to 0", + TestStep(7, f"{THcommand} MoveToLevelWithOnOff with Level field set to maxLevel, TransitionTime field set to 100 (10s) and remaining fields set to 0", test_plan_support.verify_success()), TestStep(8, "TH stores the reported values of CurrentLevel in all incoming reports for CurrentLevel attribute, that contains data in reportedCurrentLevelValuesList, over a period of 30 seconds."), TestStep(9, "TH verifies that reportedCurrentLevelValuesList does not contain more than 10 entries for CurrentLevel", @@ -90,16 +93,28 @@ async def test_TC_LVL_2_3(self): self.step(2) feature_map = await self.read_single_attribute_check_success(cluster=lvl, attribute=lvl.Attributes.FeatureMap) + attributes = await self.read_single_attribute_check_success(cluster=lvl, attribute=lvl.Attributes.AttributeList) - self.step(3) - max_level = await self.read_single_attribute_check_success(cluster=lvl, attribute=lvl.Attributes.MaxLevel) + self.step("3a") + attributes = await self.read_single_attribute_check_success(cluster=lvl, attribute=lvl.Attributes.AttributeList) + + if lvl.Attributes.MaxLevel.attribute_id in attributes: + max_level = await self.read_single_attribute_check_success(cluster=lvl, attribute=lvl.Attributes.MaxLevel) + else: + max_level = 254 + + self.step("3b") + if lvl.Attributes.MinLevel.attribute_id in attributes: + min_level = await self.read_single_attribute_check_success(cluster=lvl, attribute=lvl.Attributes.MinLevel) + else: + min_level = 1 self.step(4) - cmd = Clusters.LevelControl.Commands.MoveWithOnOff(moveMode=lvl.Enums.MoveModeEnum.kDown) + cmd = Clusters.LevelControl.Commands.MoveToLevelWithOnOff(level=min_level, transitionTime=0) await self.send_single_cmd(cmd) # NOTE: added this sleep to let the DUT have some time to move - logging.info("Test waits for 5 seconds") - time.sleep(5) + logging.info("Test waits for 1 seconds") + time.sleep(1) self.step(5) start_current_level = await self.read_single_attribute_check_success(cluster=lvl, attribute=lvl.Attributes.CurrentLevel) @@ -109,8 +124,6 @@ async def test_TC_LVL_2_3(self): await sub_handler.start(self.default_controller, self.dut_node_id, self.matter_test_config.endpoint) self.step(7) - # NOTE: had to use the WithOnOff version of this command because the dut is off at this point thanks to the above command - # TODO: Need to check above and here that the on/off cluster is actually implemented. cmd = lvl.Commands.MoveToLevelWithOnOff(level=max_level, transitionTime=100, optionsMask=0, optionsOverride=0) await self.send_single_cmd(cmd) @@ -135,6 +148,7 @@ async def test_TC_LVL_2_3(self): last_value = start_current_level for e in sub_handler.attribute_reports[lvl.Attributes.CurrentLevel]: asserts.assert_greater_equal(e.value, last_value, "Values are not increasing") + last_value = e.value self.step(12) asserts.assert_equal(e.value, max_level, "Last entry is not max value") diff --git a/src/python_testing/TC_MCORE_FS_1_1.py b/src/python_testing/TC_MCORE_FS_1_1.py index 629dae67fc6d3e..50f4dc55c7399b 100755 --- a/src/python_testing/TC_MCORE_FS_1_1.py +++ b/src/python_testing/TC_MCORE_FS_1_1.py @@ -37,7 +37,7 @@ class TC_MCORE_FS_1_1(MatterBaseTest): async def setup_class(self): super().setup_class() # TODO: confirm whether we can open processes like this on the TH - app = self.matter_test_config.user_params.get("th_server_app_path", None) + app = self.user_params.get("th_server_app_path", None) if not app: asserts.fail('This test requires a TH_SERVER app. Specify app path with --string-arg th_server_app_path:') @@ -102,7 +102,7 @@ async def test_TC_MCORE_FS_1_1(self): await self.send_single_cmd(cmd, endpoint=dut_commissioning_control_endpoint) if not self.is_ci: - self.wait_for_use_input("Approve Commissioning approval request on DUT using manufacturer specified mechanism") + self.wait_for_user_input("Approve Commissioning approval request on DUT using manufacturer specified mechanism") if not events: new_event = await self.default_controller.ReadEvent(nodeid=self.dut_node_id, events=event_path) diff --git a/src/python_testing/TC_MCORE_FS_1_2.py b/src/python_testing/TC_MCORE_FS_1_2.py index 0af8cbedb30345..1085b0fac0a07c 100644 --- a/src/python_testing/TC_MCORE_FS_1_2.py +++ b/src/python_testing/TC_MCORE_FS_1_2.py @@ -118,7 +118,7 @@ async def test_TC_MCORE_FS_1_2(self): self.step(4) if not self.is_ci: - self.wait_for_use_input( + self.wait_for_user_input( "Commission TH_SED_DUT onto DUT_FSA’s fabric using the manufacturer specified mechanism. (ensure Synchronization is enabled.)") else: logging.info("Stopping after step 3 while running in CI to avoid manual steps.") diff --git a/src/python_testing/TC_SEAR_1_2.py b/src/python_testing/TC_SEAR_1_2.py index 4ebb3342ee9bfc..083533277a5e6d 100644 --- a/src/python_testing/TC_SEAR_1_2.py +++ b/src/python_testing/TC_SEAR_1_2.py @@ -59,7 +59,7 @@ async def read_and_validate_supported_maps(self, step): self.print_step(step, "Read SupportedMaps attribute") supported_maps = await self.read_sear_attribute_expect_success( endpoint=self.endpoint, attribute=Clusters.ServiceArea.Attributes.SupportedMaps) - logging.info("SupportedMaps: %s" % (supported_maps)) + logging.info("SupportedMaps: %s" % supported_maps) asserts.assert_less_equal(len(supported_maps), 255, "SupportedMaps should have max 255 entries") @@ -69,14 +69,14 @@ async def read_and_validate_supported_maps(self, step): name_list = [m.name for m in supported_maps] asserts.assert_true(len(set(name_list)) == len(name_list), "SupportedMaps must have unique Name values!") - # save so other methods can use this if neeeded + # save so other methods can use this if needed self.mapid_list = mapid_list async def read_and_validate_supported_areas(self, step): self.print_step(step, "Read SupportedAreas attribute") supported_areas = await self.read_sear_attribute_expect_success( endpoint=self.endpoint, attribute=Clusters.ServiceArea.Attributes.SupportedAreas) - logging.info("SupportedAreas: %s" % (supported_areas)) + logging.info("SupportedAreas: %s" % supported_areas) asserts.assert_less_equal(len(supported_areas), 255, "SupportedAreas should have max 255 entries") areaid_list = [] @@ -89,8 +89,8 @@ async def read_and_validate_supported_areas(self, step): if len(self.mapid_list) > 0: asserts.assert_is_not(a.mapID, NullValue, f"SupportedAreas entry with AreaID({a.areaID}) should not have null MapID") - asserts.assert_is(a.mapID in self.mapid_list, - f"SupportedAreas entry with AreaID({a.areaID}) has unknown MapID({a.mapID})") + asserts.assert_true(a.mapID in self.mapid_list, + f"SupportedAreas entry with AreaID({a.areaID}) has unknown MapID({a.mapID})") k = f"mapID:{a.mapID} areaDesc:{a.areaDesc}" asserts.assert_true(k not in areadesc_s, f"SupportedAreas must have unique MapID({a.mapID}) + AreaDesc({a.areaDesc}) values!") @@ -103,15 +103,16 @@ async def read_and_validate_supported_areas(self, step): asserts.assert_true(k not in areadesc_s, f"SupportedAreas must have unique AreaDesc({a.areaDesc}) values!") areadesc_s.add(k) - if a.locationInfo is NullValue and a.landmarkTag is NullValue: + if a.areaDesc.locationInfo is NullValue and a.areaDesc.landmarkInfo is NullValue: asserts.assert_true( - f"SupportedAreas entry with AreaID({a.areaID}) should not have null LocationInfo and null LandmarkTag") - if a.landmarkTag is not NullValue: - asserts.assert_true(a.landmarkTag <= self.MAX_LANDMARK_ID, - f"SupportedAreas entry with AreaID({a.areaID}) has invalid LandmarkTag({a.landmarkTag})") - asserts.assert_true(a.positionTag is NullValue or a.positionTag in range(0, self.MAX_RELPOS_ID), - f"SupportedAreas entry with AreaID({a.areaID}) has invalid PositionTag({a.positionTag})") - # save so other methods can use this if neeeded + f"SupportedAreas entry with AreaID({a.areaID}) should not have null LocationInfo and null LandmarkInfo") + if a.areaDesc.landmarkInfo is not NullValue: + asserts.assert_true(a.areaDesc.landmarkInfo.landmarkTag <= self.MAX_LANDMARK_ID, + f"SupportedAreas entry with AreaID({a.areaID}) has invalid LandmarkTag({a.areaDesc.landmarkInfo.landmarkTag})") + asserts.assert_true(a.areaDesc.landmarkInfo.positionTag is NullValue or a + .areaDesc.landmarkInfo.positionTag in range(0, self.MAX_RELPOS_ID), + f"SupportedAreas entry with AreaID({a.areaID}) has invalid PositionTag({a.areaDesc.landmarkInfo.positionTag})") + # save so other methods can use this if needed self.areaid_list = areaid_list async def read_and_validate_selected_areas(self, step): @@ -130,7 +131,7 @@ async def read_and_validate_selected_areas(self, step): for a in selected_areas: asserts.assert_true(a in self.areaid_list, f"SelectedAreas entry {a} has invalid value") - # save so other methods can use this if neeeded + # save so other methods can use this if needed self.selareaid_list = selected_areas async def read_and_validate_current_area(self, step): @@ -143,7 +144,7 @@ async def read_and_validate_current_area(self, step): or current_area in self.selareaid_list, f"CurrentArea {current_area} is invalid. SelectedAreas is {self.selareaid_list}.") - # save so other methods can use this if neeeded + # save so other methods can use this if needed self.current_area = current_area async def read_and_validate_estimated_end_time(self, step): @@ -244,7 +245,9 @@ async def test_TC_SEAR_1_2(self): test_step = "Manually intervene to remove one or more entries in the SupportedMaps list" self.print_step("10", test_step) - if not self.is_ci: + if self.is_ci: + self.write_to_app_pipe('{"Name": "RemoveMap", "MapId": 3}') + else: self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n") await self.read_and_validate_supported_maps(step=11) @@ -277,7 +280,9 @@ async def test_TC_SEAR_1_2(self): test_step = "Manually intervene to add one or more entries to the SupportedMaps list" self.print_step("14", test_step) - if not self.is_ci: + if self.is_ci: + self.write_to_app_pipe('{"Name": "AddMap", "MapId": 1, "MapName": "NewTestMap1"}') + else: self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n") await self.read_and_validate_supported_maps(step=15) @@ -310,7 +315,9 @@ async def test_TC_SEAR_1_2(self): test_step = "Manually intervene to remove one or more entries from the SupportedAreas list" self.print_step("18", test_step) - if not self.is_ci: + if self.is_ci: + self.write_to_app_pipe('{"Name": "RemoveArea", "AreaId": 10050}') + else: self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n") await self.read_and_validate_supported_areas(step=19) @@ -342,7 +349,9 @@ async def test_TC_SEAR_1_2(self): test_step = "Manually intervene to add one or more entries to the SupportedAreas list" self.print_step("22", test_step) - if not self.is_ci: + if self.is_ci: + self.write_to_app_pipe('{"Name": "AddArea", "AreaId": 42, "MapId": 1, "LocationName": "NewTestArea1"}') + else: self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n") await self.read_and_validate_supported_areas(step=23) diff --git a/src/python_testing/TC_SEAR_1_3.py b/src/python_testing/TC_SEAR_1_3.py index 0acee1b34cdeb8..2ea2e86b7d198d 100644 --- a/src/python_testing/TC_SEAR_1_3.py +++ b/src/python_testing/TC_SEAR_1_3.py @@ -51,7 +51,9 @@ async def read_supported_areas(self, step): self.print_step(step, "Read SupportedAreas attribute") supported_areas = await self.read_sear_attribute_expect_success( endpoint=self.endpoint, attribute=Clusters.ServiceArea.Attributes.SupportedAreas) - logging.info("SupportedAreas: %s" % (supported_areas)) + logging.info("SupportedAreas: %s" % supported_areas) + + self.supported_areas = supported_areas return [a.areaID for a in supported_areas] @@ -68,16 +70,16 @@ async def send_cmd_select_areas_expect_response(self, step, new_areas, expected_ ret = await self.send_single_cmd(cmd=Clusters.Objects.ServiceArea.Commands.SelectAreas(newAreas=new_areas), endpoint=self.endpoint) - asserts.assert_equal(ret.commandResponseState.errorStateID, + asserts.assert_equal(ret.status, expected_response, - f"Command response ({ret.commandResponseState}) doesn't match the expected one") + f"Command response ({ret.status}) doesn't match the expected one") # Sends and out-of-band command to the rvc-app def write_to_app_pipe(self, command): with open(self.app_pipe, "w") as app_pipe: app_pipe.write(command + "\n") # Allow some time for the command to take effect. - # This removes the test flakyness which is very annoying for everyone in CI. + # This removes the test flakiness which is very annoying for everyone in CI. sleep(0.001) def TC_SEAR_1_3(self) -> list[str]: @@ -108,47 +110,54 @@ async def test_TC_SEAR_1_3(self): duplicated_areas = [valid_area_id, valid_area_id] # FIXME need to check if this is the correct name of this status code - await self.send_cmd_select_areas_expect_response(step=3, new_areas=duplicated_areas, expected_response=Clusters.ServiceArea.SelectAreasStatus.kDuplicatedAreas) + await self.send_cmd_select_areas_expect_response(step=3, new_areas=duplicated_areas, expected_response=Clusters.ServiceArea.Enums.SelectAreasStatus.kDuplicatedAreas) - await self.send_cmd_select_areas_expect_response(step=4, new_areas=[], expected_response=Clusters.ServiceArea.SelectAreasStatus.kSuccess) + await self.send_cmd_select_areas_expect_response(step=4, new_areas=[], expected_response=Clusters.ServiceArea.Enums.SelectAreasStatus.kSuccess) selected_areas = await self.read_selected_areas(step=5) asserts.assert_true(len(selected_areas) == 0, "SelectedAreas should be empty") - await self.send_cmd_select_areas_expect_response(step=6, new_areas=[invalid_area_id], expected_response=Clusters.ServiceArea.SelectAreasStatus.kUnsupportedArea) + await self.send_cmd_select_areas_expect_response(step=6, new_areas=[invalid_area_id], expected_response=Clusters.ServiceArea.Enums.SelectAreasStatus.kUnsupportedArea) if self.check_pics("SEAR.S.M.INVALID_STATE_FOR_SELECT_AREAS") and self.check_pics("SEAR.S.M.HAS_MANUAL_SELAREA_STATE_CONTROL"): test_step = "Manually intervene to put the device in a state that prevents it from executing the SelectAreas command" self.print_step("7", test_step) - if not self.is_ci: + if self.is_ci: + await self.send_single_cmd(cmd=Clusters.Objects.RvcRunMode.Commands.ChangeToMode(newMode=1), endpoint=self.endpoint) + else: self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n") - await self.send_cmd_select_areas_expect_response(step=8, new_areas=[valid_area_id], expected_response=Clusters.ServiceArea.SelectAreasStatus.kInvalidInMode) + await self.send_cmd_select_areas_expect_response(step=8, new_areas=[valid_area_id], expected_response=Clusters.ServiceArea.Enums.SelectAreasStatus.kInvalidInMode) if self.check_pics("SEAR.S.M.VALID_STATE_FOR_SELECT_AREAS") and self.check_pics("SEAR.S.M.HAS_MANUAL_SELAREA_STATE_CONTROL"): test_step = f"Manually intervene to put the device in a state that allows it to execute the SelectAreas({supported_area_ids}) command" self.print_step("9", test_step) - if not self.is_ci: + if self.is_ci: + self.write_to_app_pipe('{"Name": "Reset"}') + else: self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n") - await self.send_cmd_select_areas_expect_response(step=10, new_areas=supported_area_ids, expected_response=Clusters.ServiceArea.SelectAreasStatus.kSuccess) + await self.send_cmd_select_areas_expect_response(step=10, new_areas=supported_area_ids, expected_response=Clusters.ServiceArea.Enums.SelectAreasStatus.kSuccess) selected_areas = await self.read_selected_areas(step=11) asserts.assert_true(len(selected_areas) == len(supported_area_ids), f"SelectedAreas({selected_areas}) should match SupportedAreas({supported_area_ids})") - await self.send_cmd_select_areas_expect_response(step=12, new_areas=supported_area_ids, expected_response=Clusters.ServiceArea.SelectAreasStatus.kSuccess) + await self.send_cmd_select_areas_expect_response(step=12, new_areas=supported_area_ids, expected_response=Clusters.ServiceArea.Enums.SelectAreasStatus.kSuccess) if self.check_pics("SEAR.S.M.VALID_STATE_FOR_SELECT_AREAS") and self.check_pics("SEAR.S.M.HAS_MANUAL_SELAREA_STATE_CONTROL") and self.check_pics("SEAR.S.M.SELECT_AREAS_WHILE_NON_IDLE"): test_step = f"Manually intervene to put the device in a state that allows it to execute the SelectAreas({valid_area_id}) command, and put the device in a non-idle state" self.print_step("13", test_step) - if not self.is_ci: + if self.is_ci: + self.write_to_app_pipe('{"Name": "Reset"}') + await self.send_single_cmd(cmd=Clusters.Objects.RvcRunMode.Commands.ChangeToMode(newMode=1), endpoint=self.endpoint) + else: self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n") if self.check_pics("SEAR.S.F00"): - await self.send_cmd_select_areas_expect_response(step=14, new_areas=[valid_area_id], expected_response=Clusters.ServiceArea.SelectAreasStatus.kSuccess) + await self.send_cmd_select_areas_expect_response(step=14, new_areas=[valid_area_id], expected_response=Clusters.ServiceArea.Enums.SelectAreasStatus.kSuccess) else: - await self.send_cmd_select_areas_expect_response(step=14, new_areas=[valid_area_id], expected_response=Clusters.ServiceArea.SelectAreasStatus.kInvalidInMode) + await self.send_cmd_select_areas_expect_response(step=14, new_areas=[valid_area_id], expected_response=Clusters.ServiceArea.Enums.SelectAreasStatus.kInvalidInMode) if __name__ == "__main__": diff --git a/src/python_testing/TC_SEAR_1_4.py b/src/python_testing/TC_SEAR_1_4.py index a6326559beaa20..d31dcc2b137ad0 100644 --- a/src/python_testing/TC_SEAR_1_4.py +++ b/src/python_testing/TC_SEAR_1_4.py @@ -62,10 +62,6 @@ async def test_TC_SEAR_1_4(self): self.print_step(1, "Commissioning, already done") - # Ensure that the device is in the correct state - if self.is_ci: - self.write_to_app_pipe('{"Name": "Reset"}') - attribute_list = await self.read_sear_attribute_expect_success( endpoint=self.endpoint, attribute=Clusters.ServiceArea.Attributes.AttributeList) logging.info("AttributeList: %s" % (attribute_list)) diff --git a/src/python_testing/TC_SEAR_1_5.py b/src/python_testing/TC_SEAR_1_5.py index adcf8341c93389..a90c1feaf9c329 100644 --- a/src/python_testing/TC_SEAR_1_5.py +++ b/src/python_testing/TC_SEAR_1_5.py @@ -52,7 +52,7 @@ async def read_supported_areas(self, step): self.print_step(step, "Read SupportedAreas attribute") supported_areas = await self.read_sear_attribute_expect_success( endpoint=self.endpoint, attribute=Clusters.ServiceArea.Attributes.SupportedAreas) - logging.info("SupportedAreas: %s" % (supported_areas)) + logging.info("SupportedAreas: %s" % supported_areas) return [a.areaID for a in supported_areas] @@ -85,9 +85,9 @@ async def send_cmd_skip_area_expect_response(self, step, skipped_area, expected_ ret = await self.send_single_cmd(cmd=Clusters.Objects.ServiceArea.Commands.SkipArea(skippedArea=skipped_area), endpoint=self.endpoint) - asserts.assert_equal(ret.commandResponseState.errorStateID, + asserts.assert_equal(ret.status, expected_response, - f"Command response ({ret.commandResponseState}) doesn't match the expected one") + f"Command response ({ret.status}) doesn't match the expected one") # Sends and out-of-band command to the rvc-app @@ -95,7 +95,7 @@ def write_to_app_pipe(self, command): with open(self.app_pipe, "w") as app_pipe: app_pipe.write(command + "\n") # Allow some time for the command to take effect. - # This removes the test flakyness which is very annoying for everyone in CI. + # This removes the test flakiness which is very annoying for everyone in CI. sleep(0.001) def TC_SEAR_1_5(self) -> list[str]: @@ -131,26 +131,35 @@ async def test_TC_SEAR_1_5(self): self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n") await self.send_cmd_skip_area_expect_response(step=4, skipped_area=valid_area_id, - expected_response=Clusters.ServiceArea.SkipAreaStatus.kInvalidInMode) + expected_response=Clusters.ServiceArea.Enums.SkipAreaStatus.kInvalidInMode) if self.check_pics("SEAR.S.M.NO_SELAREA_FOR_SKIP") and self.check_pics("SEAR.S.M.HAS_MANUAL_SKIP_STATE_CONTROL"): test_step = "Manually intervene to put the device in a state where the state would allow it to execute the SkipArea command, \ if SelectedAreas wasn't empty, and SelectedAreas is empty" self.print_step("5", test_step) - if not self.is_ci: + if self.is_ci: + await self.send_single_cmd(cmd=Clusters.Objects.RvcRunMode.Commands.ChangeToMode(newMode=1), + endpoint=self.endpoint) + else: self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n") await self.send_cmd_skip_area_expect_response(step=6, skipped_area=valid_area_id, - expected_response=Clusters.ServiceArea.SkipAreaStatus.kInvalidAreaList) + expected_response=Clusters.ServiceArea.Enums.SkipAreaStatus.kInvalidAreaList) if self.check_pics("SEAR.S.M.VALID_STATE_FOR_SKIP") and self.check_pics("SEAR.S.M.HAS_MANUAL_SKIP_STATE_CONTROL"): test_step = "Manually intervene to put the device in a state that allows it to execute the SkipArea command" self.print_step("7", test_step) - if not self.is_ci: + if self.is_ci: + self.write_to_app_pipe('{"Name": "Reset"}') + await self.send_single_cmd(cmd=Clusters.Objects.ServiceArea.Commands.SelectAreas(newAreas=[7, 1234567]), + endpoint=self.endpoint) + await self.send_single_cmd(cmd=Clusters.Objects.RvcRunMode.Commands.ChangeToMode(newMode=1), + endpoint=self.endpoint) + else: self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n") await self.send_cmd_skip_area_expect_response(step=8, skipped_area=invalid_area_id, - expected_response=Clusters.ServiceArea.SkipAreaStatus.kInvalidSkippedArea) + expected_response=Clusters.ServiceArea.Enums.SkipAreaStatus.kInvalidSkippedArea) if not self.check_pics("SEAR.S.M.VALID_STATE_FOR_SKIP"): return @@ -169,7 +178,7 @@ async def test_TC_SEAR_1_5(self): self.print_step("12", "") if old_current_area is not NullValue: await self.send_cmd_skip_area_expect_response(step=13, skipped_area=old_current_area, - expected_response=Clusters.ServiceArea.SkipAreaStatus.kSuccess) + expected_response=Clusters.ServiceArea.Enums.SkipAreaStatus.kSuccess) if self.check_pics("SEAR.S.M.HAS_MANUAL_SKIP_STATE_CONTROL"): test_step = "(Manual operation) wait for the device to skip the current area, and start operating at\ the next one it should process, or stop operating" @@ -189,7 +198,7 @@ async def test_TC_SEAR_1_5(self): new_current_area = await self.read_current_area(step=16) for p in new_progress_list: if p.areaID == old_current_area: - asserts.assert_true(p.status == Clusters.ServiceArea.OperationalStatusEnum.kSkipped, + asserts.assert_true(p.status == Clusters.ServiceArea.Enums.OperationalStatusEnum.kSkipped, "Progress for areaID({old_current_area}) should be Skipped") break test_step = "Indicate whether the device has stopped operating (y/n)" @@ -199,7 +208,7 @@ async def test_TC_SEAR_1_5(self): if ret != "y": for p in new_progress_list: if p.areaID == new_current_area: - asserts.assert_true(p.status == Clusters.ServiceArea.OperationalStatusEnum.kOperating, + asserts.assert_true(p.status == Clusters.ServiceArea.Enums.OperationalStatusEnum.kOperating, "Progress for areaID({new_current_area}) should be Operating") break @@ -208,8 +217,8 @@ async def test_TC_SEAR_1_5(self): was_only_skipped_or_completed = True for p in old_progress_list: if p.areaID != old_current_area: - if p.status not in (Clusters.ServiceArea.OperationalStatusEnum.kSkipped, - Clusters.ServiceArea.OperationalStatusEnum.kCompleted): + if p.status not in (Clusters.ServiceArea.Enums.OperationalStatusEnum.kSkipped, + Clusters.ServiceArea.Enums.OperationalStatusEnum.kCompleted): was_only_skipped_or_completed = False break if was_only_skipped_or_completed: @@ -224,7 +233,13 @@ async def test_TC_SEAR_1_5(self): if self.check_pics("SEAR.S.M.HAS_MANUAL_SKIP_STATE_CONTROL"): test_step = "Manually intervene to put the device in a state that allows it to execute the SkipArea command" self.print_step("18", test_step) - if not self.is_ci: + if self.is_ci: + self.write_to_app_pipe('{"Name": "Reset"}') + await self.send_single_cmd(cmd=Clusters.Objects.ServiceArea.Commands.SelectAreas(newAreas=[7, 1234567]), + endpoint=self.endpoint) + await self.send_single_cmd(cmd=Clusters.Objects.RvcRunMode.Commands.ChangeToMode(newMode=1), + endpoint=self.endpoint) + else: self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n") self.print_step("19", "") @@ -234,8 +249,8 @@ async def test_TC_SEAR_1_5(self): area_to_skip = NullValue self.print_step("20", "") for p in old_progress_list: - if p.status in (Clusters.ServiceArea.OperationalStatusEnum.kPending, - Clusters.ServiceArea.OperationalStatusEnum.kOperating): + if p.status in (Clusters.ServiceArea.Enums.OperationalStatusEnum.kPending, + Clusters.ServiceArea.Enums.OperationalStatusEnum.kOperating): area_to_skip = p.areaID break @@ -243,7 +258,7 @@ async def test_TC_SEAR_1_5(self): return await self.send_cmd_skip_area_expect_response(step=21, skipped_area=area_to_skip, - expected_response=Clusters.ServiceArea.SkipAreaStatus.kSuccess) + expected_response=Clusters.ServiceArea.Enums.SkipAreaStatus.kSuccess) if self.check_pics("SEAR.S.M.HAS_MANUAL_SKIP_STATE_CONTROL"): test_step = "(Manual operation) wait for the device to update Progress or to stop operating" @@ -256,7 +271,7 @@ async def test_TC_SEAR_1_5(self): for p in new_progress_list: if p.areaID == area_to_skip: - asserts.assert_true(p.status == Clusters.ServiceArea.OperationalStatusEnum.kSkipped, + asserts.assert_true(p.status == Clusters.ServiceArea.Enums.OperationalStatusEnum.kSkipped, "Progress for areaID({new_current_area}) should be Skipped") break @@ -266,15 +281,15 @@ async def test_TC_SEAR_1_5(self): was_only_skipped_or_completed = True for p in old_progress_list: if p.areaID != area_to_skip: - if p.status not in (Clusters.ServiceArea.OperationalStatusEnum.kSkipped, - Clusters.ServiceArea.OperationalStatusEnum.kCompleted): + if p.status not in (Clusters.ServiceArea.Enums.OperationalStatusEnum.kSkipped, + Clusters.ServiceArea.Enums.OperationalStatusEnum.kCompleted): was_only_skipped_or_completed = False break if was_only_skipped_or_completed: asserts.assert_true(ret == "y", "The device should not be operating") for p in new_progress_list: if p.areaID == old_current_area: - asserts.assert_true(p.status == Clusters.ServiceArea.OperationalStatusEnum.kSkipped, + asserts.assert_true(p.status == Clusters.ServiceArea.Enums.OperationalStatusEnum.kSkipped, "Progress for areaID({old_current_area}) should be Skipped") break diff --git a/src/python_testing/TC_SEAR_1_6.py b/src/python_testing/TC_SEAR_1_6.py index 02a0fcdd33133a..aaa6ac788dd427 100644 --- a/src/python_testing/TC_SEAR_1_6.py +++ b/src/python_testing/TC_SEAR_1_6.py @@ -52,7 +52,7 @@ async def read_supported_areas(self, step): self.print_step(step, "Read SupportedAreas attribute") supported_areas = await self.read_sear_attribute_expect_success( endpoint=self.endpoint, attribute=Clusters.ServiceArea.Attributes.SupportedAreas) - logging.info("SupportedAreas: %s" % (supported_areas)) + logging.info("SupportedAreas: %s" % supported_areas) return [a.areaID for a in supported_areas] @@ -77,7 +77,7 @@ def write_to_app_pipe(self, command): with open(self.app_pipe, "w") as app_pipe: app_pipe.write(command + "\n") # Allow some time for the command to take effect. - # This removes the test flakyness which is very annoying for everyone in CI. + # This removes the test flakiness which is very annoying for everyone in CI. sleep(0.001) def TC_SEAR_1_6(self) -> list[str]: @@ -102,7 +102,10 @@ async def test_TC_SEAR_1_6(self): test_step = "Manually intervene to put the device in the idle state and ensure SupportedAreas and SelectedAreas are not empty" self.print_step("2", test_step) - if not self.is_ci: + if self.is_ci: + await self.send_single_cmd(cmd=Clusters.Objects.ServiceArea.Commands.SelectAreas(newAreas=[7, 1234567]), + endpoint=self.endpoint) + else: self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n") supported_area_ids = await self.read_supported_areas(step=3) @@ -113,7 +116,10 @@ async def test_TC_SEAR_1_6(self): test_step = "Manually intervene to put the device in the operating state" self.print_step("5", test_step) - if not self.is_ci: + if self.is_ci: + await self.send_single_cmd(cmd=Clusters.Objects.RvcRunMode.Commands.ChangeToMode(newMode=1), + endpoint=self.endpoint) + else: self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n") progress_list_operating = await self.read_progress(step=6) @@ -122,16 +128,20 @@ async def test_TC_SEAR_1_6(self): for p in progress_list_operating: asserts.assert_true(p.areaID in selected_areas, f"Progress entry with unknown AreaID({p.areaID})") - asserts.assert_true(p.status in (Clusters.ServiceArea.OperationalStatusEnum.kPending, - Clusters.ServiceArea.OperationalStatusEnum.kOperating), + asserts.assert_true(p.status in (Clusters.ServiceArea.Enums.OperationalStatusEnum.kPending, + Clusters.ServiceArea.Enums.OperationalStatusEnum.kOperating), f"Progress entry with unexpected Status({p.status})") - asserts.assert_true(p.TotalOperationalTime is NullValue, "Progress entry with non-null TotalOperationalTime") + asserts.assert_true(type(p.totalOperationalTime) in [type(None), type( + NullValue)], "Progress.TotalOperationalTime has a value") test_step = "While all entries in Progress show the Pending or Operating status (i.e. \ before any area is skipped or completed), manually intervene to put the device \ in the idle state, by ending the operation unexpectedly (e.g. force an error)" self.print_step("7", test_step) - if not self.is_ci: + if self.is_ci: + await self.send_single_cmd(cmd=Clusters.Objects.RvcRunMode.Commands.ChangeToMode(newMode=0), + endpoint=self.endpoint) + else: self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n") progress_list_idle = await self.read_progress(step=8) @@ -140,7 +150,7 @@ async def test_TC_SEAR_1_6(self): for p in progress_list_idle: asserts.assert_true(p.areaID in selected_areas, f"Progress entry with unknown AreaID({p.areaID})") - asserts.assert_true(p.status == Clusters.ServiceArea.OperationalStatusEnum.kSkipped, + asserts.assert_true(p.status == Clusters.ServiceArea.Enums.OperationalStatusEnum.kSkipped, f"Progress entry with unexpected Status({p.status})") diff --git a/src/python_testing/TC_TSTAT_4_2.py b/src/python_testing/TC_TSTAT_4_2.py index 58d78ef4491a73..5c289ced50604e 100644 --- a/src/python_testing/TC_TSTAT_4_2.py +++ b/src/python_testing/TC_TSTAT_4_2.py @@ -30,6 +30,7 @@ import logging import chip.clusters as Clusters +from chip import ChipDeviceCtrl # Needed before chip.FabricAdmin from chip.clusters import Globals from chip.clusters.Types import NullValue from chip.interaction_model import InteractionModelError, Status @@ -58,53 +59,76 @@ class TC_TSTAT_4_2(MatterBaseTest): - async def write_presets(self, endpoint, presets) -> Status: - result = await self.default_controller.WriteAttribute(self.dut_node_id, [(endpoint, cluster.Attributes.Presets(presets))]) - return result[0].Status - - async def send_edit_atomic_request_begin_command(self, - endpoint: int = None, - expected_status: Status = Status.Success): + def check_atomic_response(self, response: object, expected_status: Status = Status.Success, + expected_overall_status: Status = Status.Success, + expected_preset_status: Status = Status.Success): + asserts.assert_equal(expected_status, Status.Success, "We expected we had a valid response") + asserts.assert_equal(response.statusCode, expected_overall_status, "Response should have the right overall status") + found_preset_status = False + for attrStatus in response.attributeStatus: + if attrStatus.attributeID == cluster.Attributes.Presets.attribute_id: + asserts.assert_equal(attrStatus.statusCode, expected_preset_status, + "Preset attribute should have the right status") + found_preset_status = True + asserts.assert_true(found_preset_status, "Preset attribute should have a status") + + async def write_presets(self, + endpoint, + presets, + dev_ctrl: ChipDeviceCtrl = None, + expected_status: Status = Status.Success) -> Status: + if dev_ctrl is None: + dev_ctrl = self.default_controller + result = await dev_ctrl.WriteAttribute(self.dut_node_id, [(endpoint, cluster.Attributes.Presets(presets))]) + status = result[0].Status + asserts.assert_equal(status, expected_status, f"Presets write returned {status.name}; expected {expected_status.name}") + return status + + async def send_atomic_request_begin_command(self, + dev_ctrl: ChipDeviceCtrl = None, + endpoint: int = None, + expected_status: Status = Status.Success, + expected_overall_status: Status = Status.Success, + expected_preset_status: Status = Status.Success): try: - await self.send_single_cmd(cmd=cluster.Commands.AtomicRequest(requestType=Globals.Enums.AtomicRequestTypeEnum.kBeginWrite, - attributeRequests=[ - cluster.Attributes.Presets.attribute_id], - timeout=1800), - endpoint=endpoint) - asserts.assert_equal(expected_status, Status.Success) + response = await self.send_single_cmd(cmd=cluster.Commands.AtomicRequest(requestType=Globals.Enums.AtomicRequestTypeEnum.kBeginWrite, + attributeRequests=[ + cluster.Attributes.Presets.attribute_id], + timeout=1800), + dev_ctrl=dev_ctrl, + endpoint=endpoint) + self.check_atomic_response(response, expected_status, expected_overall_status, expected_preset_status) except InteractionModelError as e: asserts.assert_equal(e.status, expected_status, "Unexpected error returned") - async def send_edit_atomic_request_commit_command(self, - endpoint: int = None, - expected_status: Status = Status.Success, - expected_overall_status: Status = Status.Success, - expected_preset_status: Status = Status.Success): + async def send_atomic_request_commit_command(self, + dev_ctrl: ChipDeviceCtrl = None, + endpoint: int = None, + expected_status: Status = Status.Success, + expected_overall_status: Status = Status.Success, + expected_preset_status: Status = Status.Success): try: response = await self.send_single_cmd(cmd=cluster.Commands.AtomicRequest(requestType=Globals.Enums.AtomicRequestTypeEnum.kCommitWrite, attributeRequests=[cluster.Attributes.Presets.attribute_id, cluster.Attributes.Schedules.attribute_id]), + dev_ctrl=dev_ctrl, endpoint=endpoint) - asserts.assert_equal(expected_status, Status.Success, "We expected we had a valid commit command") - asserts.assert_equal(response.statusCode, expected_overall_status, "Commit should have the right overall status") - found_preset_status = False - for attrStatus in response.attributeStatus: - if attrStatus.attributeID == cluster.Attributes.Presets.attribute_id: - asserts.assert_equal(attrStatus.statusCode, expected_preset_status, - "Preset attribute commit should have the right status") - found_preset_status = True - asserts.assert_true(found_preset_status, "Preset attribute commit should have a status") + self.check_atomic_response(response, expected_status, expected_overall_status, expected_preset_status) except InteractionModelError as e: asserts.assert_equal(e.status, expected_status, "Unexpected error returned") - async def send_edit_atomic_request_rollback_command(self, - endpoint: int = None, - expected_status: Status = Status.Success): + async def send_atomic_request_rollback_command(self, + dev_ctrl: ChipDeviceCtrl = None, + endpoint: int = None, + expected_status: Status = Status.Success, + expected_overall_status: Status = Status.Success, + expected_preset_status: Status = Status.Success): try: - await self.send_single_cmd(cmd=cluster.Commands.AtomicRequest(requestType=Globals.Enums.AtomicRequestTypeEnum.kRollbackWrite, - attributeRequests=[cluster.Attributes.Presets.attribute_id, cluster.Attributes.Schedules.attribute_id]), - endpoint=endpoint) - asserts.assert_equal(expected_status, Status.Success) + response = await self.send_single_cmd(cmd=cluster.Commands.AtomicRequest(requestType=Globals.Enums.AtomicRequestTypeEnum.kRollbackWrite, + attributeRequests=[cluster.Attributes.Presets.attribute_id, cluster.Attributes.Schedules.attribute_id]), + dev_ctrl=dev_ctrl, + endpoint=endpoint) + self.check_atomic_response(response, expected_status, expected_overall_status, expected_preset_status) except InteractionModelError as e: asserts.assert_equal(e.status, expected_status, "Unexpected error returned") @@ -134,39 +158,57 @@ def steps_TC_TSTAT_4_2(self) -> list[TestStep]: is_commissioning=True), TestStep("2", "TH writes to the Presets attribute without calling the AtomicRequest command", " Verify that the write request returns INVALID_IN_STATE error since the client didn't send a request to edit the presets by calling AtomicRequest command."), - TestStep("3", "TH writes to the Presets attribute after calling the AtomicRequest command but doesn't call CommitPresetsSchedulesRequest to commit", - "Verify that the Presets attribute was not updated since CommitPresetsSchedulesRequest command was not called."), - TestStep("4", "TH writes to the Presets attribute after calling the AtomicRequest command and calls CommitPresetsSchedulesRequest to commit", + TestStep("3", "TH writes to the Presets attribute after calling the AtomicRequest begin command but doesn't call AtomicRequest commit", + "Verify that the Presets attribute was not updated since AtomicRequest commit command was not called."), + TestStep("4", "TH writes to the Presets attribute after calling the AtomicRequest begin command and calls AtomicRequest commit", "Verify that the Presets attribute was updated with new presets."), TestStep("5", "TH writes to the Presets attribute with a built-in preset removed", - "Verify that the CommitPresetsSchedulesRequest returned UNSUPPORTED_ACCESS (0x7e)."), + "Verify that the AtomicRequest commit returned CONSTRAINT_ERROR (0x87)."), TestStep("6", "TH writes to the Presets attribute with a preset removed whose handle matches the value in the ActivePresetHandle attribute", - "Verify that the CommitPresetsSchedulesRequest returned INVALID_IN_STATE (0xcb)."), + "Verify that the AtomicRequest commit returned INVALID_IN_STATE (0xcb)."), TestStep("7", "TH writes to the Presets attribute with a built-in preset modified to be not built-in", - "Verify that the CommitPresetsSchedulesRequest returned UNSUPPORTED_ACCESS (0x7e)."), + "Verify that the AtomicRequest commit returned CONSTRAINT_ERROR (0x87)."), TestStep("8", "TH writes to the Presets attribute with a new preset having builtIn set to true", - "Verify that the CommitPresetsSchedulesRequest returned CONSTRAINT_ERROR (0x87)."), + "Verify that the AtomicRequest commit returned CONSTRAINT_ERROR (0x87)."), TestStep("9", "TH writes to the Presets attribute with a new preset having a preset handle that doesn't exist in the Presets attribute", - "Verify that the CommitPresetsSchedulesRequest returned NOT_FOUND (0x8b)."), + "Verify that the AtomicRequest commit returned NOT_FOUND (0x8b)."), TestStep("10", "TH writes to the Presets attribute with duplicate presets", - "Verify that the CommitPresetsSchedulesRequest returned CONSTRAINT_ERROR (0x87)."), + "Verify that the AtomicRequest commit returned CONSTRAINT_ERROR (0x87)."), TestStep("11", "TH writes to the Presets attribute with a non built-in preset modified to be built-in", - "Verify that the CommitPresetsSchedulesRequest returned UNSUPPORTED_ACCESS (0x7e)."), + "Verify that the AtomicRequest commit returned CONSTRAINT_ERROR (0x87)."), TestStep("12", "TH writes to the Presets attribute with a preset that doesn't support names in the PresetTypeFeatures bitmap but has a name", - "Verify that the CommitPresetsSchedulesRequest returned CONSTRAINT_ERROR (0x87)."), - TestStep("13", "TH writes to the Presets attribute but calls the CancelPresetsSchedulesEditRequest command to cancel the edit request", - "Verify that the edit request was cancelled"), + "Verify that the AtomicRequest commit returned CONSTRAINT_ERROR (0x87)."), + TestStep("13", "TH writes to the Presets attribute but calls the AtomicRequest rollback command to cancel the edit request", + "Verify that the edit request was rolled back"), + TestStep("14", "TH starts an atomic write, and TH2 attempts to open an atomic write before TH is complete", + "Verify that the atomic request is rejected"), + TestStep("15", "TH starts an atomic write, and TH2 attempts to write to presets", + "Verify that the write request is rejected"), + TestStep("16", "TH starts an atomic write, and before it's complete, TH2 removes TH's fabric; TH2 then opens an atomic write", + "Verify that the atomic request is successful"), ] return steps - @async_test_body + @ async_test_body async def test_TC_TSTAT_4_2(self): endpoint = self.user_params.get("endpoint", 1) self.step("1") # Commission DUT - already done + logger.info("Commissioning under second controller") + params = await self.default_controller.OpenCommissioningWindow( + nodeid=self.dut_node_id, timeout=600, iteration=10000, discriminator=1234, option=1) + + secondary_authority = self.certificate_authority_manager.NewCertificateAuthority() + secondary_fabric_admin = secondary_authority.NewFabricAdmin(vendorId=0xFFF1, fabricId=2) + secondary_controller = secondary_fabric_admin.NewController(nodeId=112233) + + await secondary_controller.CommissionOnNetwork( + nodeId=self.dut_node_id, setupPinCode=params.setupPinCode, + filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=1234) + self.step("2") if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050")): presets = await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=cluster.Attributes.Presets) @@ -174,13 +216,12 @@ async def test_TC_TSTAT_4_2(self): asserts.assert_equal(presets, initial_presets, "Presets do not match initial value") # Write to the presets attribute without calling AtomicRequest command - status = await self.write_presets(endpoint=endpoint, presets=new_presets) - status_ok = (status == Status.InvalidInState) - asserts.assert_true(status_ok, "Presets write did not return InvalidInState as expected") + await self.write_presets(endpoint=endpoint, presets=new_presets, expected_status=Status.InvalidInState) self.step("3") - if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.CFE.Rsp")): - await self.send_edit_atomic_request_begin_command() + + if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): + await self.send_atomic_request_begin_command() # Write to the presets attribute after calling AtomicRequest command status = await self.write_presets(endpoint=endpoint, presets=new_presets) @@ -192,25 +233,23 @@ async def test_TC_TSTAT_4_2(self): logger.info(f"Rx'd Presets: {presets}") asserts.assert_equal(presets, new_presets_with_handle, "Presets were updated, as expected") - await self.send_edit_atomic_request_rollback_command() + await self.send_atomic_request_rollback_command() # Read the presets attribute and verify it has been properly rolled back presets = await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=cluster.Attributes.Presets) asserts.assert_equal(presets, initial_presets, "Presets were updated which is not expected") self.step("4") - if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.CFE.Rsp")): + if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): # Send the AtomicRequest begin command - await self.send_edit_atomic_request_begin_command() + await self.send_atomic_request_begin_command() # Write to the presets attribute after calling AtomicRequest command - status = await self.write_presets(endpoint=endpoint, presets=new_presets) - status_ok = (status == Status.Success) - asserts.assert_true(status_ok, "Presets write did not return Success as expected") + await self.write_presets(endpoint=endpoint, presets=new_presets) # Send the AtomicRequest commit command - await self.send_edit_atomic_request_commit_command() + await self.send_atomic_request_commit_command() # Read the presets attribute and verify it was updated since AtomicRequest commit was called after writing presets presets = await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=cluster.Attributes.Presets) @@ -218,23 +257,21 @@ async def test_TC_TSTAT_4_2(self): asserts.assert_equal(presets, new_presets_with_handle, "Presets were not updated which is not expected") self.step("5") - if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.CFE.Rsp")): + if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): # Send the AtomicRequest begin command - await self.send_edit_atomic_request_begin_command() + await self.send_atomic_request_begin_command() # Write to the presets attribute after removing a built in preset from the list. Remove the first entry. test_presets = new_presets_with_handle.copy() test_presets.pop(0) - status = await self.write_presets(endpoint=endpoint, presets=test_presets) - status_ok = (status == Status.Success) - asserts.assert_true(status_ok, "Presets write did not return Success as expected") + await self.write_presets(endpoint=endpoint, presets=test_presets) # Send the AtomicRequest commit command and expect ConstraintError for presets. - await self.send_edit_atomic_request_commit_command(expected_overall_status=Status.Failure, expected_preset_status=Status.ConstraintError) + await self.send_atomic_request_commit_command(expected_overall_status=Status.Failure, expected_preset_status=Status.ConstraintError) self.step("6") - if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.C06.Rsp") and self.check_pics("TSTAT.S.CFE.Rsp")): + if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.C06.Rsp") and self.check_pics("TSTAT.S.Cfe.Rsp")): # Send the SetActivePresetRequest command await self.send_set_active_preset_handle_request_command(value=b'\x03') @@ -245,144 +282,164 @@ async def test_TC_TSTAT_4_2(self): asserts.assert_equal(activePresetHandle, b'\x03', "Active preset handle was not updated as expected") # Send the AtomicRequest begin command - await self.send_edit_atomic_request_begin_command() + await self.send_atomic_request_begin_command() # Write to the presets attribute after removing the preset that was set as the active preset handle. Remove the last entry with preset handle (b'\x03') test_presets = new_presets_with_handle.copy() del test_presets[-1] - status = await self.write_presets(endpoint=endpoint, presets=test_presets) - status_ok = (status == Status.Success) - asserts.assert_true(status_ok, "Presets write did not return Success as expected") + await self.write_presets(endpoint=endpoint, presets=test_presets) # Send the AtomicRequest commit command and expect InvalidInState for presets. - await self.send_edit_atomic_request_commit_command(expected_overall_status=Status.Failure, expected_preset_status=Status.InvalidInState) + await self.send_atomic_request_commit_command(expected_overall_status=Status.Failure, expected_preset_status=Status.InvalidInState) self.step("7") - if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.CFE.Rsp")): + if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): # Send the AtomicRequest begin command - await self.send_edit_atomic_request_begin_command() + await self.send_atomic_request_begin_command() # Write to the presets attribute after setting the builtIn flag to False for preset with handle (b'\x01') test_presets = copy.deepcopy(new_presets_with_handle) test_presets[0].builtIn = False - status = await self.write_presets(endpoint=endpoint, presets=test_presets) - asserts.assert_equal(status, Status.ConstraintError, - "Presets write should return ConstraintError, because BuiltIn values do not match") + await self.write_presets(endpoint=endpoint, presets=test_presets, expected_status=Status.ConstraintError) # Clear state for next test. - await self.send_edit_atomic_request_rollback_command() + await self.send_atomic_request_rollback_command() self.step("8") - if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.CFE.Rsp")): + if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): # Send the AtomicRequest begin command - await self.send_edit_atomic_request_begin_command() + await self.send_atomic_request_begin_command() # Write to the presets attribute after adding a preset with builtIn set to True test_presets = copy.deepcopy(new_presets_with_handle) test_presets.append(cluster.Structs.PresetStruct(presetHandle=NullValue, presetScenario=cluster.Enums.PresetScenarioEnum.kWake, name="Wake", coolingSetpoint=2800, heatingSetpoint=1800, builtIn=True)) - status = await self.write_presets(endpoint=endpoint, presets=test_presets) - status_ok = (status == Status.Success) - asserts.assert_equal(status, Status.ConstraintError, - "Presets write should return ConstraintError, since we are trying to add a new built-in preset") + status = await self.write_presets(endpoint=endpoint, presets=test_presets, expected_status=Status.ConstraintError) # Clear state for next test. - await self.send_edit_atomic_request_rollback_command() + await self.send_atomic_request_rollback_command() self.step("9") - if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.CFE.Rsp")): + if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): # Send the AtomicRequest begin command - await self.send_edit_atomic_request_begin_command() + await self.send_atomic_request_begin_command() # Write to the presets attribute after adding a preset with a preset handle that doesn't exist in Presets attribute test_presets = copy.deepcopy(new_presets_with_handle) test_presets.append(cluster.Structs.PresetStruct(presetHandle=b'\x08', presetScenario=cluster.Enums.PresetScenarioEnum.kWake, name="Wake", coolingSetpoint=2800, heatingSetpoint=1800, builtIn=True)) - status = await self.write_presets(endpoint=endpoint, presets=test_presets) - asserts.assert_equal(status, Status.NotFound, - "Presets write should return NotFound, since we are trying to modify non-existent preset") + status = await self.write_presets(endpoint=endpoint, presets=test_presets, expected_status=Status.NotFound) # Clear state for next test. - await self.send_edit_atomic_request_rollback_command() + await self.send_atomic_request_rollback_command() self.step("10") - if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.CFE.Rsp")): + if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): # Send the AtomicRequest begin command - await self.send_edit_atomic_request_begin_command() + await self.send_atomic_request_begin_command() # Write to the presets attribute after adding a duplicate preset with handle (b'\x03') test_presets = copy.deepcopy(new_presets_with_handle) test_presets.append(cluster.Structs.PresetStruct( presetHandle=b'\x03', presetScenario=cluster.Enums.PresetScenarioEnum.kSleep, name="Sleep", coolingSetpoint=2700, heatingSetpoint=1900, builtIn=False)) - status = await self.write_presets(endpoint=endpoint, presets=test_presets) - asserts.assert_equal(status, Status.ConstraintError, - "Presets write should return ConstraintError, since we have duplicated presets") + await self.write_presets(endpoint=endpoint, presets=test_presets, expected_status=Status.ConstraintError) # Clear state for next test. - await self.send_edit_atomic_request_rollback_command() + await self.send_atomic_request_rollback_command() self.step("11") - if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.CFE.Rsp")): + if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): # Send the AtomicRequest begin command - await self.send_edit_atomic_request_begin_command() + await self.send_atomic_request_begin_command() # Write to the presets attribute after setting the builtIn flag to True for preset with handle (b'\x03') test_presets = copy.deepcopy(new_presets_with_handle) test_presets[2].builtIn = True - status = await self.write_presets(endpoint=endpoint, presets=test_presets) - asserts.assert_equal(status, Status.ConstraintError, - "Presets write should return ConstraintError, since we are trying to change whether a preset is BuiltIn") + await self.write_presets(endpoint=endpoint, presets=test_presets, expected_status=Status.ConstraintError) # Clear state for next test. - await self.send_edit_atomic_request_rollback_command() + await self.send_atomic_request_rollback_command() self.step("12") - if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.CFE.Rsp")): + if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): # Send the AtomicRequest begin command - await self.send_edit_atomic_request_begin_command() + await self.send_atomic_request_begin_command() # Write to the presets attribute after setting a name for preset with handle (b'\x01') that doesn't support names test_presets = copy.deepcopy(new_presets_with_handle) test_presets[0].name = "Occupied" - status = await self.write_presets(endpoint=endpoint, presets=test_presets) - asserts.assert_equal(status, Status.ConstraintError, - "Presets write should return ConstraintError, since we are trying to set a name for a preset that does not support that") + await self.write_presets(endpoint=endpoint, presets=test_presets, expected_status=Status.ConstraintError) # Clear state for next test. - await self.send_edit_atomic_request_rollback_command() + await self.send_atomic_request_rollback_command() self.step("13") - if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.CFE.Rsp")): + if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): # Send the AtomicRequest begin command - await self.send_edit_atomic_request_begin_command() + await self.send_atomic_request_begin_command() # Write to the presets attribute with a new valid preset added test_presets = copy.deepcopy(new_presets_with_handle) test_presets.append(cluster.Structs.PresetStruct(presetHandle=NullValue, presetScenario=cluster.Enums.PresetScenarioEnum.kWake, name="Wake", coolingSetpoint=2800, heatingSetpoint=1800, builtIn=False)) - status = await self.write_presets(endpoint=endpoint, presets=test_presets) - status_ok = (status == Status.Success) - asserts.assert_equal(status, Status.Success, "Presets write did not return Success as expected") + await self.write_presets(endpoint=endpoint, presets=test_presets) # Roll back - await self.send_edit_atomic_request_rollback_command() + await self.send_atomic_request_rollback_command() # Send the AtomicRequest commit command and expect InvalidInState as the previous edit request was cancelled - await self.send_edit_atomic_request_commit_command(expected_status=Status.InvalidInState) + await self.send_atomic_request_commit_command(expected_status=Status.InvalidInState) + + self.step("14") + if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): + + # Send the AtomicRequest begin command + await self.send_atomic_request_begin_command() + + # Send the AtomicRequest begin command from separate controller, which should receive busy + status = await self.send_atomic_request_begin_command(dev_ctrl=secondary_controller, expected_overall_status=Status.Failure, expected_preset_status=Status.Busy) + + # Roll back + await self.send_atomic_request_rollback_command() + + self.step("15") + if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): + # Send the AtomicRequest begin command from the secondary controller + await self.send_atomic_request_begin_command() + + await self.write_presets(endpoint=endpoint, presets=test_presets, dev_ctrl=secondary_controller, expected_status=Status.Busy) + + # Roll back + await self.send_atomic_request_rollback_command() + + self.step("16") + if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): + + # Send the AtomicRequest begin command from the secondary controller + await self.send_atomic_request_begin_command(dev_ctrl=secondary_controller) + + # Primary controller removes the second fabric + await self.send_single_cmd(Clusters.OperationalCredentials.Commands.RemoveFabric(fabricIndex=2), endpoint=0) + + # Send the AtomicRequest begin command from primary controller, which should succeed, as the secondary controller's atomic write state has been cleared + status = await self.send_atomic_request_begin_command() + + # Roll back + await self.send_atomic_request_rollback_command() # TODO: Add tests for the total number of Presets exceeds the NumberOfPresets supported. Also Add tests for adding presets with preset scenario not present in PresetTypes. diff --git a/src/test_driver/efr32/BUILD.gn b/src/test_driver/efr32/BUILD.gn index 947ed482f4622e..b15b16864548c9 100644 --- a/src/test_driver/efr32/BUILD.gn +++ b/src/test_driver/efr32/BUILD.gn @@ -73,7 +73,7 @@ silabs_executable("efr32_device_tests") { "${examples_common_plat_dir}/provision/ProvisionStorageCustom.cpp", "${examples_common_plat_dir}/provision/ProvisionStorageDefault.cpp", "${examples_common_plat_dir}/syscalls_stubs.cpp", - "${examples_plat_dir}/uart.cpp", + "${examples_common_plat_dir}/uart.cpp", "src/main.cpp", ] diff --git a/third_party/infineon/psoc6/psoc6_sdk/configs/mbedtls_user_config.h b/third_party/infineon/psoc6/psoc6_sdk/configs/mbedtls_user_config.h index 45a1fd8fd09430..38f4d3bf327b39 100644 --- a/third_party/infineon/psoc6/psoc6_sdk/configs/mbedtls_user_config.h +++ b/third_party/infineon/psoc6/psoc6_sdk/configs/mbedtls_user_config.h @@ -73,13 +73,13 @@ * Uncomment a macro to enable alternate implementation of specific base * platform function */ -//#define MBEDTLS_PLATFORM_EXIT_ALT +// #define MBEDTLS_PLATFORM_EXIT_ALT #define MBEDTLS_PLATFORM_TIME_ALT -//#define MBEDTLS_PLATFORM_FPRINTF_ALT -//#define MBEDTLS_PLATFORM_PRINTF_ALT -//#define MBEDTLS_PLATFORM_SNPRINTF_ALT -//#define MBEDTLS_PLATFORM_NV_SEED_ALT -//#define MBEDTLS_PLATFORM_SETUP_TEARDOWN_ALT +// #define MBEDTLS_PLATFORM_FPRINTF_ALT +// #define MBEDTLS_PLATFORM_PRINTF_ALT +// #define MBEDTLS_PLATFORM_SNPRINTF_ALT +// #define MBEDTLS_PLATFORM_NV_SEED_ALT +// #define MBEDTLS_PLATFORM_SETUP_TEARDOWN_ALT /** * \def MBEDTLS_ENTROPY_HARDWARE_ALT @@ -103,7 +103,7 @@ */ #undef MBEDTLS_ECP_DP_SECP192R1_ENABLED #undef MBEDTLS_ECP_DP_SECP224R1_ENABLED -//#define MBEDTLS_ECP_DP_SECP256R1_ENABLED +// #define MBEDTLS_ECP_DP_SECP256R1_ENABLED #undef MBEDTLS_ECP_DP_SECP384R1_ENABLED #undef MBEDTLS_ECP_DP_SECP521R1_ENABLED #undef MBEDTLS_ECP_DP_SECP192K1_ENABLED @@ -112,7 +112,7 @@ #undef MBEDTLS_ECP_DP_BP256R1_ENABLED #undef MBEDTLS_ECP_DP_BP384R1_ENABLED #undef MBEDTLS_ECP_DP_BP512R1_ENABLED -//#undef MBEDTLS_ECP_DP_CURVE25519_ENABLED +// #undef MBEDTLS_ECP_DP_CURVE25519_ENABLED #undef MBEDTLS_ECP_DP_CURVE448_ENABLED /** @@ -246,7 +246,7 @@ * * Uncomment this macro to enable support for SSLv2 Client Hello messages. */ -//#define MBEDTLS_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO +// #define MBEDTLS_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO /** * \def MBEDTLS_SSL_PROTO_TLS1 @@ -468,7 +468,7 @@ * * This module is required for X.509 CRL parsing. */ -//#undef MBEDTLS_X509_CRL_PARSE_C +// #undef MBEDTLS_X509_CRL_PARSE_C /** * \def MBEDTLS_X509_CSR_PARSE_C @@ -482,7 +482,7 @@ * * This module is used for reading X.509 certificate request. */ -//#undef MBEDTLS_X509_CSR_PARSE_C +// #undef MBEDTLS_X509_CSR_PARSE_C /** * \def MBEDTLS_X509_CREATE_C @@ -495,7 +495,7 @@ * * This module is the basis for creating X.509 certificates and CSRs. */ -//#undef MBEDTLS_X509_CREATE_C +// #undef MBEDTLS_X509_CREATE_C /** * \def MBEDTLS_X509_CSR_WRITE_C @@ -508,7 +508,7 @@ * * This module is required for X.509 certificate request writing. */ -//#undef MBEDTLS_X509_CSR_WRITE_C +// #undef MBEDTLS_X509_CSR_WRITE_C /** * \def MBEDTLS_X509_CRT_WRITE_C @@ -521,7 +521,7 @@ * * This module is required for X.509 certificate creation. */ -//#undef MBEDTLS_X509_CRT_WRITE_C +// #undef MBEDTLS_X509_CRT_WRITE_C /** * \def MBEDTLS_CERTS_C @@ -779,4 +779,10 @@ */ #define MBEDTLS_DEPRECATED_REMOVED +#define MBEDTLS_ASN1_WRITE_C +#define MBEDTLS_ECDSA_C +#define MBEDTLS_PK_WRITE_C +#define MBEDTLS_X509_CREATE_C +#define MBEDTLS_X509_CSR_WRITE_C + #endif /* MBEDTLS_USER_CONFIG_HEADER */ diff --git a/third_party/pybind11/repo b/third_party/pybind11/repo deleted file mode 160000 index 8b03ffa7c06cd9..00000000000000 --- a/third_party/pybind11/repo +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8b03ffa7c06cd9c8a38297b1c8923695d1ff1b07 diff --git a/third_party/silabs/SiWx917_sdk.gni b/third_party/silabs/SiWx917_sdk.gni index 5faba08acd3d3b..8c7635ad9235d3 100644 --- a/third_party/silabs/SiWx917_sdk.gni +++ b/third_party/silabs/SiWx917_sdk.gni @@ -215,7 +215,6 @@ template("siwx917_sdk") { "ROMDRIVER_PRESENT=1", "SL_CATALOG_FREERTOS_KERNEL_PRESENT=1", "SLI_SI91X_MCU_CONFIG_RADIO_BOARD_BASE_VER=1", - "BRD4325A", #TODO: should be removed after SoC macro clean-up "SLI_SI917B0=1", "SLI_SI91X_MCU_COMMON_FLASH_MODE=1", "SLI_SI91X_MCU_CONFIG_RADIO_BOARD_VER2=1", @@ -259,7 +258,10 @@ template("siwx917_sdk") { } if (chip_build_libshell) { - defines += [ "ENABLE_CHIP_SHELL" ] + defines += [ + "ENABLE_CHIP_SHELL", + "SLI_SI91X_MCU_INTR_BASED_RX_ON_UART=1", + ] } defines += [ "LWIP_NETIF_API=1" ] @@ -301,7 +303,6 @@ template("siwx917_sdk") { if (!disable_lcd) { defines += [ - "CONFIG_ENABLE_UART", "SYSCALLS_WRITE", "SPI_MULTI_SLAVE", "SL_ULP_TIMER", @@ -310,12 +311,7 @@ template("siwx917_sdk") { # Enabling led interface if (use_wstk_leds) { - defines += [ - "ENABLE_WSTK_LEDS", - - # TODO: remove this when it is added to the board config from wifi sdk - "SL_CATALOG_SIMPLE_LED_LED1_PRESENT", - ] + defines += [ "ENABLE_WSTK_LEDS" ] } if (chip_enable_icd_server) { diff --git a/third_party/silabs/matter_support b/third_party/silabs/matter_support index 9084b6fa2f9407..8f476b30f9c604 160000 --- a/third_party/silabs/matter_support +++ b/third_party/silabs/matter_support @@ -1 +1 @@ -Subproject commit 9084b6fa2f9407396a7f2db12975e6e89fe07a8c +Subproject commit 8f476b30f9c6041de334abadcdb6852ade77790e diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h b/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h index a6121b3681348e..571439625e121a 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h @@ -2631,18 +2631,8 @@ enum class Feature : uint32_t kTankPercent = 0x2, }; -// Bitmap for WaterHeaterDemandBitmap -enum class WaterHeaterDemandBitmap : uint8_t -{ - kImmersionElement1 = 0x1, - kImmersionElement2 = 0x2, - kHeatPump = 0x4, - kBoiler = 0x8, - kOther = 0x10, -}; - -// Bitmap for WaterHeaterTypeBitmap -enum class WaterHeaterTypeBitmap : uint8_t +// Bitmap for WaterHeaterHeatSourceBitmap +enum class WaterHeaterHeatSourceBitmap : uint8_t { kImmersionElement1 = 0x1, kImmersionElement2 = 0x2, diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp index 90eddd9b2f1f0d..20fdd126514076 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp @@ -15168,9 +15168,9 @@ CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) } // namespace ElectricalEnergyMeasurement namespace WaterHeaterManagement { +namespace Structs { -namespace Commands { -namespace Boost { +namespace WaterHeaterBoostInfoStruct { CHIP_ERROR Type::Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const { DataModel::WrappedStructEncoder encoder{ aWriter, aTag }; @@ -15228,6 +15228,44 @@ CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) ReturnErrorOnFailure(err); } } + +} // namespace WaterHeaterBoostInfoStruct +} // namespace Structs + +namespace Commands { +namespace Boost { +CHIP_ERROR Type::Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const +{ + DataModel::WrappedStructEncoder encoder{ aWriter, aTag }; + encoder.Encode(to_underlying(Fields::kBoostInfo), boostInfo); + return encoder.Finalize(); +} + +CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) +{ + detail::StructDecodeIterator __iterator(reader); + while (true) + { + auto __element = __iterator.Next(); + if (std::holds_alternative(__element)) + { + return std::get(__element); + } + + CHIP_ERROR err = CHIP_NO_ERROR; + const uint8_t __context_tag = std::get(__element); + + if (__context_tag == to_underlying(Fields::kBoostInfo)) + { + err = DataModel::Decode(reader, boostInfo); + } + else + { + } + + ReturnErrorOnFailure(err); + } +} } // namespace Boost. namespace CancelBoost { CHIP_ERROR Type::Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const @@ -15286,7 +15324,64 @@ CHIP_ERROR TypeInfo::DecodableType::Decode(TLV::TLVReader & reader, const Concre } } // namespace Attributes -namespace Events {} // namespace Events +namespace Events { +namespace BoostStarted { +CHIP_ERROR Type::Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const +{ + TLV::TLVType outer; + ReturnErrorOnFailure(aWriter.StartContainer(aTag, TLV::kTLVType_Structure, outer)); + ReturnErrorOnFailure(DataModel::Encode(aWriter, TLV::ContextTag(Fields::kBoostInfo), boostInfo)); + return aWriter.EndContainer(outer); +} + +CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) +{ + detail::StructDecodeIterator __iterator(reader); + while (true) + { + auto __element = __iterator.Next(); + if (std::holds_alternative(__element)) + { + return std::get(__element); + } + + CHIP_ERROR err = CHIP_NO_ERROR; + const uint8_t __context_tag = std::get(__element); + + if (__context_tag == to_underlying(Fields::kBoostInfo)) + { + err = DataModel::Decode(reader, boostInfo); + } + else + { + } + + ReturnErrorOnFailure(err); + } +} +} // namespace BoostStarted. +namespace BoostEnded { +CHIP_ERROR Type::Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const +{ + TLV::TLVType outer; + ReturnErrorOnFailure(aWriter.StartContainer(aTag, TLV::kTLVType_Structure, outer)); + return aWriter.EndContainer(outer); +} + +CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) +{ + detail::StructDecodeIterator __iterator(reader); + while (true) + { + auto __element = __iterator.Next(); + if (std::holds_alternative(__element)) + { + return std::get(__element); + } + } +} +} // namespace BoostEnded. +} // namespace Events } // namespace WaterHeaterManagement namespace DemandResponseLoadControl { diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h index 27ee806af6e9a9..f0f1dea1de536d 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h @@ -21449,6 +21449,39 @@ struct DecodableType } // namespace Events } // namespace ElectricalEnergyMeasurement namespace WaterHeaterManagement { +namespace Structs { +namespace WaterHeaterBoostInfoStruct { +enum class Fields : uint8_t +{ + kDuration = 0, + kOneShot = 1, + kEmergencyBoost = 2, + kTemporarySetpoint = 3, + kTargetPercentage = 4, + kTargetReheat = 5, +}; + +struct Type +{ +public: + uint32_t duration = static_cast(0); + Optional oneShot; + Optional emergencyBoost; + Optional temporarySetpoint; + Optional targetPercentage; + Optional targetReheat; + + CHIP_ERROR Decode(TLV::TLVReader & reader); + + static constexpr bool kIsFabricScoped = false; + + CHIP_ERROR Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const; +}; + +using DecodableType = Type; + +} // namespace WaterHeaterBoostInfoStruct +} // namespace Structs namespace Commands { // Forward-declarations so we can reference these later. @@ -21469,12 +21502,7 @@ namespace Commands { namespace Boost { enum class Fields : uint8_t { - kDuration = 0, - kOneShot = 1, - kEmergencyBoost = 2, - kTemporarySetpoint = 3, - kTargetPercentage = 4, - kTargetReheat = 5, + kBoostInfo = 0, }; struct Type @@ -21484,12 +21512,7 @@ struct Type static constexpr CommandId GetCommandId() { return Commands::Boost::Id; } static constexpr ClusterId GetClusterId() { return Clusters::WaterHeaterManagement::Id; } - uint32_t duration = static_cast(0); - Optional oneShot; - Optional emergencyBoost; - Optional temporarySetpoint; - Optional targetPercentage; - Optional targetReheat; + Structs::WaterHeaterBoostInfoStruct::Type boostInfo; CHIP_ERROR Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const; @@ -21504,12 +21527,7 @@ struct DecodableType static constexpr CommandId GetCommandId() { return Commands::Boost::Id; } static constexpr ClusterId GetClusterId() { return Clusters::WaterHeaterManagement::Id; } - uint32_t duration = static_cast(0); - Optional oneShot; - Optional emergencyBoost; - Optional temporarySetpoint; - Optional targetPercentage; - Optional targetReheat; + Structs::WaterHeaterBoostInfoStruct::DecodableType boostInfo; CHIP_ERROR Decode(TLV::TLVReader & reader); }; }; // namespace Boost @@ -21548,9 +21566,9 @@ namespace Attributes { namespace HeaterTypes { struct TypeInfo { - using Type = chip::BitMask; - using DecodableType = chip::BitMask; - using DecodableArgType = chip::BitMask; + using Type = chip::BitMask; + using DecodableType = chip::BitMask; + using DecodableArgType = chip::BitMask; static constexpr ClusterId GetClusterId() { return Clusters::WaterHeaterManagement::Id; } static constexpr AttributeId GetAttributeId() { return Attributes::HeaterTypes::Id; } @@ -21560,9 +21578,9 @@ struct TypeInfo namespace HeatDemand { struct TypeInfo { - using Type = chip::BitMask; - using DecodableType = chip::BitMask; - using DecodableArgType = chip::BitMask; + using Type = chip::BitMask; + using DecodableType = chip::BitMask; + using DecodableArgType = chip::BitMask; static constexpr ClusterId GetClusterId() { return Clusters::WaterHeaterManagement::Id; } static constexpr AttributeId GetAttributeId() { return Attributes::HeatDemand::Id; } @@ -21663,9 +21681,9 @@ struct TypeInfo CHIP_ERROR Decode(TLV::TLVReader & reader, const ConcreteAttributePath & path); Attributes::HeaterTypes::TypeInfo::DecodableType heaterTypes = - static_cast>(0); + static_cast>(0); Attributes::HeatDemand::TypeInfo::DecodableType heatDemand = - static_cast>(0); + static_cast>(0); Attributes::TankVolume::TypeInfo::DecodableType tankVolume = static_cast(0); Attributes::EstimatedHeatRequired::TypeInfo::DecodableType estimatedHeatRequired = static_cast(0); Attributes::TankPercentage::TypeInfo::DecodableType tankPercentage = static_cast(0); @@ -21680,6 +21698,69 @@ struct TypeInfo }; }; } // namespace Attributes +namespace Events { +namespace BoostStarted { +static constexpr PriorityLevel kPriorityLevel = PriorityLevel::Info; + +enum class Fields : uint8_t +{ + kBoostInfo = 0, +}; + +struct Type +{ +public: + static constexpr PriorityLevel GetPriorityLevel() { return kPriorityLevel; } + static constexpr EventId GetEventId() { return Events::BoostStarted::Id; } + static constexpr ClusterId GetClusterId() { return Clusters::WaterHeaterManagement::Id; } + static constexpr bool kIsFabricScoped = false; + + Structs::WaterHeaterBoostInfoStruct::Type boostInfo; + + CHIP_ERROR Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const; +}; + +struct DecodableType +{ +public: + static constexpr PriorityLevel GetPriorityLevel() { return kPriorityLevel; } + static constexpr EventId GetEventId() { return Events::BoostStarted::Id; } + static constexpr ClusterId GetClusterId() { return Clusters::WaterHeaterManagement::Id; } + + Structs::WaterHeaterBoostInfoStruct::DecodableType boostInfo; + + CHIP_ERROR Decode(TLV::TLVReader & reader); +}; +} // namespace BoostStarted +namespace BoostEnded { +static constexpr PriorityLevel kPriorityLevel = PriorityLevel::Info; + +enum class Fields : uint8_t +{ +}; + +struct Type +{ +public: + static constexpr PriorityLevel GetPriorityLevel() { return kPriorityLevel; } + static constexpr EventId GetEventId() { return Events::BoostEnded::Id; } + static constexpr ClusterId GetClusterId() { return Clusters::WaterHeaterManagement::Id; } + static constexpr bool kIsFabricScoped = false; + + CHIP_ERROR Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const; +}; + +struct DecodableType +{ +public: + static constexpr PriorityLevel GetPriorityLevel() { return kPriorityLevel; } + static constexpr EventId GetEventId() { return Events::BoostEnded::Id; } + static constexpr ClusterId GetClusterId() { return Clusters::WaterHeaterManagement::Id; } + + CHIP_ERROR Decode(TLV::TLVReader & reader); +}; +} // namespace BoostEnded +} // namespace Events } // namespace WaterHeaterManagement namespace DemandResponseLoadControl { namespace Structs { diff --git a/zzz_generated/app-common/app-common/zap-generated/ids/Events.h b/zzz_generated/app-common/app-common/zap-generated/ids/Events.h index f8243c506f906d..4227a6ab527ba2 100644 --- a/zzz_generated/app-common/app-common/zap-generated/ids/Events.h +++ b/zzz_generated/app-common/app-common/zap-generated/ids/Events.h @@ -443,6 +443,20 @@ static constexpr EventId Id = 0x00000001; } // namespace Events } // namespace ElectricalEnergyMeasurement +namespace WaterHeaterManagement { +namespace Events { + +namespace BoostStarted { +static constexpr EventId Id = 0x00000000; +} // namespace BoostStarted + +namespace BoostEnded { +static constexpr EventId Id = 0x00000001; +} // namespace BoostEnded + +} // namespace Events +} // namespace WaterHeaterManagement + namespace DemandResponseLoadControl { namespace Events { diff --git a/zzz_generated/chip-tool/zap-generated/cluster/Commands.h b/zzz_generated/chip-tool/zap-generated/cluster/Commands.h index 85a45a057b7eab..57259e33d69d44 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/Commands.h +++ b/zzz_generated/chip-tool/zap-generated/cluster/Commands.h @@ -6731,6 +6731,8 @@ class ValveConfigurationAndControlClose : public ClusterCommand | * ClusterRevision | 0xFFFD | |------------------------------------------------------------------------------| | Events: | | +| * BoostStarted | 0x0000 | +| * BoostEnded | 0x0001 | \*----------------------------------------------------------------------------*/ /* @@ -6739,14 +6741,10 @@ class ValveConfigurationAndControlClose : public ClusterCommand class WaterHeaterManagementBoost : public ClusterCommand { public: - WaterHeaterManagementBoost(CredentialIssuerCommands * credsIssuerConfig) : ClusterCommand("boost", credsIssuerConfig) + WaterHeaterManagementBoost(CredentialIssuerCommands * credsIssuerConfig) : + ClusterCommand("boost", credsIssuerConfig), mComplex_BoostInfo(&mRequest.boostInfo) { - AddArgument("Duration", 0, UINT32_MAX, &mRequest.duration); - AddArgument("OneShot", 0, 1, &mRequest.oneShot); - AddArgument("EmergencyBoost", 0, 1, &mRequest.emergencyBoost); - AddArgument("TemporarySetpoint", INT16_MIN, INT16_MAX, &mRequest.temporarySetpoint); - AddArgument("TargetPercentage", 0, UINT8_MAX, &mRequest.targetPercentage); - AddArgument("TargetReheat", 0, UINT8_MAX, &mRequest.targetReheat); + AddArgument("BoostInfo", &mComplex_BoostInfo); ClusterCommand::AddArguments(); } @@ -6773,6 +6771,7 @@ class WaterHeaterManagementBoost : public ClusterCommand private: chip::app::Clusters::WaterHeaterManagement::Commands::Boost::Type mRequest; + TypedComplexArgument mComplex_BoostInfo; }; /* @@ -21293,9 +21292,9 @@ void registerClusterWaterHeaterManagement(Commands & commands, CredentialIssuerC make_unique(Id, "feature-map", Attributes::FeatureMap::Id, credsIssuerConfig), // make_unique(Id, "cluster-revision", Attributes::ClusterRevision::Id, credsIssuerConfig), // make_unique>(Id, credsIssuerConfig), // - make_unique>>( + make_unique>>( Id, "heater-types", 0, UINT8_MAX, Attributes::HeaterTypes::Id, WriteCommandType::kForceWrite, credsIssuerConfig), // - make_unique>>( + make_unique>>( Id, "heat-demand", 0, UINT8_MAX, Attributes::HeatDemand::Id, WriteCommandType::kForceWrite, credsIssuerConfig), // make_unique>(Id, "tank-volume", 0, UINT16_MAX, Attributes::TankVolume::Id, WriteCommandType::kForceWrite, credsIssuerConfig), // @@ -21335,8 +21334,12 @@ void registerClusterWaterHeaterManagement(Commands & commands, CredentialIssuerC // // Events // - make_unique(Id, credsIssuerConfig), // - make_unique(Id, credsIssuerConfig), // + make_unique(Id, credsIssuerConfig), // + make_unique(Id, "boost-started", Events::BoostStarted::Id, credsIssuerConfig), // + make_unique(Id, "boost-ended", Events::BoostEnded::Id, credsIssuerConfig), // + make_unique(Id, credsIssuerConfig), // + make_unique(Id, "boost-started", Events::BoostStarted::Id, credsIssuerConfig), // + make_unique(Id, "boost-ended", Events::BoostEnded::Id, credsIssuerConfig), // }; commands.RegisterCluster(clusterName, clusterCommands); diff --git a/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.cpp b/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.cpp index d4f67568a7f746..202a6efcb06a01 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.cpp +++ b/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.cpp @@ -2897,6 +2897,73 @@ void ComplexArgumentParser::Finalize( ComplexArgumentParser::Finalize(request.endSystime); } +CHIP_ERROR +ComplexArgumentParser::Setup(const char * label, + chip::app::Clusters::WaterHeaterManagement::Structs::WaterHeaterBoostInfoStruct::Type & request, + Json::Value & value) +{ + VerifyOrReturnError(value.isObject(), CHIP_ERROR_INVALID_ARGUMENT); + + // Copy to track which members we already processed. + Json::Value valueCopy(value); + + ReturnErrorOnFailure( + ComplexArgumentParser::EnsureMemberExist("WaterHeaterBoostInfoStruct.duration", "duration", value.isMember("duration"))); + + char labelWithMember[kMaxLabelLength]; + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "duration"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.duration, value["duration"])); + valueCopy.removeMember("duration"); + + if (value.isMember("oneShot")) + { + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "oneShot"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.oneShot, value["oneShot"])); + } + valueCopy.removeMember("oneShot"); + + if (value.isMember("emergencyBoost")) + { + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "emergencyBoost"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.emergencyBoost, value["emergencyBoost"])); + } + valueCopy.removeMember("emergencyBoost"); + + if (value.isMember("temporarySetpoint")) + { + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "temporarySetpoint"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.temporarySetpoint, value["temporarySetpoint"])); + } + valueCopy.removeMember("temporarySetpoint"); + + if (value.isMember("targetPercentage")) + { + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "targetPercentage"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.targetPercentage, value["targetPercentage"])); + } + valueCopy.removeMember("targetPercentage"); + + if (value.isMember("targetReheat")) + { + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "targetReheat"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.targetReheat, value["targetReheat"])); + } + valueCopy.removeMember("targetReheat"); + + return ComplexArgumentParser::EnsureNoMembersRemaining(label, valueCopy); +} + +void ComplexArgumentParser::Finalize( + chip::app::Clusters::WaterHeaterManagement::Structs::WaterHeaterBoostInfoStruct::Type & request) +{ + ComplexArgumentParser::Finalize(request.duration); + ComplexArgumentParser::Finalize(request.oneShot); + ComplexArgumentParser::Finalize(request.emergencyBoost); + ComplexArgumentParser::Finalize(request.temporarySetpoint); + ComplexArgumentParser::Finalize(request.targetPercentage); + ComplexArgumentParser::Finalize(request.targetReheat); +} + CHIP_ERROR ComplexArgumentParser::Setup(const char * label, chip::app::Clusters::DemandResponseLoadControl::Structs::HeatingSourceControlStruct::Type & request, diff --git a/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.h b/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.h index dfca56f4c3afba..ccce33ece63cd8 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.h +++ b/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.h @@ -343,6 +343,12 @@ static CHIP_ERROR Setup(const char * label, static void Finalize(chip::app::Clusters::ElectricalEnergyMeasurement::Structs::EnergyMeasurementStruct::Type & request); +static CHIP_ERROR Setup(const char * label, + chip::app::Clusters::WaterHeaterManagement::Structs::WaterHeaterBoostInfoStruct::Type & request, + Json::Value & value); + +static void Finalize(chip::app::Clusters::WaterHeaterManagement::Structs::WaterHeaterBoostInfoStruct::Type & request); + static CHIP_ERROR Setup(const char * label, chip::app::Clusters::DemandResponseLoadControl::Structs::HeatingSourceControlStruct::Type & request, Json::Value & value); diff --git a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp index 2eddf2f356bd07..aa122cb62da046 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp +++ b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp @@ -2583,6 +2583,64 @@ CHIP_ERROR DataModelLogger::LogValue( return CHIP_NO_ERROR; } +CHIP_ERROR DataModelLogger::LogValue( + const char * label, size_t indent, + const chip::app::Clusters::WaterHeaterManagement::Structs::WaterHeaterBoostInfoStruct::DecodableType & value) +{ + DataModelLogger::LogString(label, indent, "{"); + { + CHIP_ERROR err = LogValue("Duration", indent + 1, value.duration); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'Duration'"); + return err; + } + } + { + CHIP_ERROR err = LogValue("OneShot", indent + 1, value.oneShot); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'OneShot'"); + return err; + } + } + { + CHIP_ERROR err = LogValue("EmergencyBoost", indent + 1, value.emergencyBoost); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'EmergencyBoost'"); + return err; + } + } + { + CHIP_ERROR err = LogValue("TemporarySetpoint", indent + 1, value.temporarySetpoint); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'TemporarySetpoint'"); + return err; + } + } + { + CHIP_ERROR err = LogValue("TargetPercentage", indent + 1, value.targetPercentage); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'TargetPercentage'"); + return err; + } + } + { + CHIP_ERROR err = LogValue("TargetReheat", indent + 1, value.targetReheat); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'TargetReheat'"); + return err; + } + } + DataModelLogger::LogString(indent, "}"); + + return CHIP_NO_ERROR; +} + CHIP_ERROR DataModelLogger::LogValue( const char * label, size_t indent, const chip::app::Clusters::DemandResponseLoadControl::Structs::HeatingSourceControlStruct::DecodableType & value) @@ -6891,6 +6949,30 @@ CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, return CHIP_NO_ERROR; } +CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, + const WaterHeaterManagement::Events::BoostStarted::DecodableType & value) +{ + DataModelLogger::LogString(label, indent, "{"); + { + CHIP_ERROR err = DataModelLogger::LogValue("BoostInfo", indent + 1, value.boostInfo); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Event truncated due to invalid value for 'BoostInfo'"); + return err; + } + } + DataModelLogger::LogString(indent, "}"); + + return CHIP_NO_ERROR; +} +CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, + const WaterHeaterManagement::Events::BoostEnded::DecodableType & value) +{ + DataModelLogger::LogString(label, indent, "{"); + DataModelLogger::LogString(indent, "}"); + + return CHIP_NO_ERROR; +} CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, const DemandResponseLoadControl::Events::LoadControlEventStatusChange::DecodableType & value) { @@ -13547,12 +13629,12 @@ CHIP_ERROR DataModelLogger::LogAttribute(const chip::app::ConcreteDataAttributeP switch (path.mAttributeId) { case WaterHeaterManagement::Attributes::HeaterTypes::Id: { - chip::BitMask value; + chip::BitMask value; ReturnErrorOnFailure(chip::app::DataModel::Decode(*data, value)); return DataModelLogger::LogValue("HeaterTypes", 1, value); } case WaterHeaterManagement::Attributes::HeatDemand::Id: { - chip::BitMask value; + chip::BitMask value; ReturnErrorOnFailure(chip::app::DataModel::Decode(*data, value)); return DataModelLogger::LogValue("HeatDemand", 1, value); } @@ -20629,6 +20711,22 @@ CHIP_ERROR DataModelLogger::LogEvent(const chip::app::EventHeader & header, chip } break; } + case WaterHeaterManagement::Id: { + switch (header.mPath.mEventId) + { + case WaterHeaterManagement::Events::BoostStarted::Id: { + chip::app::Clusters::WaterHeaterManagement::Events::BoostStarted::DecodableType value; + ReturnErrorOnFailure(chip::app::DataModel::Decode(*data, value)); + return DataModelLogger::LogValue("BoostStarted", 1, value); + } + case WaterHeaterManagement::Events::BoostEnded::Id: { + chip::app::Clusters::WaterHeaterManagement::Events::BoostEnded::DecodableType value; + ReturnErrorOnFailure(chip::app::DataModel::Decode(*data, value)); + return DataModelLogger::LogValue("BoostEnded", 1, value); + } + } + break; + } case DemandResponseLoadControl::Id: { switch (header.mPath.mEventId) { diff --git a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.h b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.h index 60398d0f3d2911..5646abd25a011a 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.h +++ b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.h @@ -214,6 +214,10 @@ static CHIP_ERROR LogValue(const char * label, size_t indent, const chip::app::Clusters::ElectricalEnergyMeasurement::Structs::EnergyMeasurementStruct::DecodableType & value); +static CHIP_ERROR +LogValue(const char * label, size_t indent, + const chip::app::Clusters::WaterHeaterManagement::Structs::WaterHeaterBoostInfoStruct::DecodableType & value); + static CHIP_ERROR LogValue(const char * label, size_t indent, const chip::app::Clusters::DemandResponseLoadControl::Structs::HeatingSourceControlStruct::DecodableType & value); @@ -574,6 +578,10 @@ LogValue(const char * label, size_t indent, static CHIP_ERROR LogValue(const char * label, size_t indent, const chip::app::Clusters::ElectricalEnergyMeasurement::Events::PeriodicEnergyMeasured::DecodableType & value); +static CHIP_ERROR LogValue(const char * label, size_t indent, + const chip::app::Clusters::WaterHeaterManagement::Events::BoostStarted::DecodableType & value); +static CHIP_ERROR LogValue(const char * label, size_t indent, + const chip::app::Clusters::WaterHeaterManagement::Events::BoostEnded::DecodableType & value); static CHIP_ERROR LogValue(const char * label, size_t indent, const chip::app::Clusters::DemandResponseLoadControl::Events::LoadControlEventStatusChange::DecodableType & value); diff --git a/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h b/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h index 67f8781db8fd5d..a61f2a6fa6b27e 100644 --- a/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h +++ b/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h @@ -79923,6 +79923,8 @@ class SubscribeAttributeElectricalEnergyMeasurementClusterRevision : public Subs | * ClusterRevision | 0xFFFD | |------------------------------------------------------------------------------| | Events: | | +| * BoostStarted | 0x0000 | +| * BoostEnded | 0x0001 | \*----------------------------------------------------------------------------*/ #if MTR_ENABLE_PROVISIONAL @@ -79933,24 +79935,10 @@ class WaterHeaterManagementBoost : public ClusterCommand { public: WaterHeaterManagementBoost() : ClusterCommand("boost") + , mComplex_BoostInfo(&mRequest.boostInfo) { #if MTR_ENABLE_PROVISIONAL - AddArgument("Duration", 0, UINT32_MAX, &mRequest.duration); -#endif // MTR_ENABLE_PROVISIONAL -#if MTR_ENABLE_PROVISIONAL - AddArgument("OneShot", 0, 1, &mRequest.oneShot); -#endif // MTR_ENABLE_PROVISIONAL -#if MTR_ENABLE_PROVISIONAL - AddArgument("EmergencyBoost", 0, 1, &mRequest.emergencyBoost); -#endif // MTR_ENABLE_PROVISIONAL -#if MTR_ENABLE_PROVISIONAL - AddArgument("TemporarySetpoint", INT16_MIN, INT16_MAX, &mRequest.temporarySetpoint); -#endif // MTR_ENABLE_PROVISIONAL -#if MTR_ENABLE_PROVISIONAL - AddArgument("TargetPercentage", 0, UINT8_MAX, &mRequest.targetPercentage); -#endif // MTR_ENABLE_PROVISIONAL -#if MTR_ENABLE_PROVISIONAL - AddArgument("TargetReheat", 0, UINT8_MAX, &mRequest.targetReheat); + AddArgument("BoostInfo", &mComplex_BoostInfo); #endif // MTR_ENABLE_PROVISIONAL ClusterCommand::AddArguments(); } @@ -79967,41 +79955,32 @@ class WaterHeaterManagementBoost : public ClusterCommand { __auto_type * params = [[MTRWaterHeaterManagementClusterBoostParams alloc] init]; params.timedInvokeTimeoutMs = mTimedInteractionTimeoutMs.HasValue() ? [NSNumber numberWithUnsignedShort:mTimedInteractionTimeoutMs.Value()] : nil; #if MTR_ENABLE_PROVISIONAL - params.duration = [NSNumber numberWithUnsignedInt:mRequest.duration]; -#endif // MTR_ENABLE_PROVISIONAL -#if MTR_ENABLE_PROVISIONAL - if (mRequest.oneShot.HasValue()) { - params.oneShot = [NSNumber numberWithBool:mRequest.oneShot.Value()]; + params.boostInfo = [MTRWaterHeaterManagementClusterWaterHeaterBoostInfoStruct new]; + params.boostInfo.duration = [NSNumber numberWithUnsignedInt:mRequest.boostInfo.duration]; + if (mRequest.boostInfo.oneShot.HasValue()) { + params.boostInfo.oneShot = [NSNumber numberWithBool:mRequest.boostInfo.oneShot.Value()]; } else { - params.oneShot = nil; + params.boostInfo.oneShot = nil; } -#endif // MTR_ENABLE_PROVISIONAL -#if MTR_ENABLE_PROVISIONAL - if (mRequest.emergencyBoost.HasValue()) { - params.emergencyBoost = [NSNumber numberWithBool:mRequest.emergencyBoost.Value()]; + if (mRequest.boostInfo.emergencyBoost.HasValue()) { + params.boostInfo.emergencyBoost = [NSNumber numberWithBool:mRequest.boostInfo.emergencyBoost.Value()]; } else { - params.emergencyBoost = nil; + params.boostInfo.emergencyBoost = nil; } -#endif // MTR_ENABLE_PROVISIONAL -#if MTR_ENABLE_PROVISIONAL - if (mRequest.temporarySetpoint.HasValue()) { - params.temporarySetpoint = [NSNumber numberWithShort:mRequest.temporarySetpoint.Value()]; + if (mRequest.boostInfo.temporarySetpoint.HasValue()) { + params.boostInfo.temporarySetpoint = [NSNumber numberWithShort:mRequest.boostInfo.temporarySetpoint.Value()]; } else { - params.temporarySetpoint = nil; + params.boostInfo.temporarySetpoint = nil; } -#endif // MTR_ENABLE_PROVISIONAL -#if MTR_ENABLE_PROVISIONAL - if (mRequest.targetPercentage.HasValue()) { - params.targetPercentage = [NSNumber numberWithUnsignedChar:mRequest.targetPercentage.Value()]; + if (mRequest.boostInfo.targetPercentage.HasValue()) { + params.boostInfo.targetPercentage = [NSNumber numberWithUnsignedChar:mRequest.boostInfo.targetPercentage.Value()]; } else { - params.targetPercentage = nil; + params.boostInfo.targetPercentage = nil; } -#endif // MTR_ENABLE_PROVISIONAL -#if MTR_ENABLE_PROVISIONAL - if (mRequest.targetReheat.HasValue()) { - params.targetReheat = [NSNumber numberWithUnsignedChar:mRequest.targetReheat.Value()]; + if (mRequest.boostInfo.targetReheat.HasValue()) { + params.boostInfo.targetReheat = [NSNumber numberWithUnsignedChar:mRequest.boostInfo.targetReheat.Value()]; } else { - params.targetReheat = nil; + params.boostInfo.targetReheat = nil; } #endif // MTR_ENABLE_PROVISIONAL uint16_t repeatCount = mRepeatCount.ValueOr(1); @@ -80025,6 +80004,7 @@ class WaterHeaterManagementBoost : public ClusterCommand { private: chip::app::Clusters::WaterHeaterManagement::Commands::Boost::Type mRequest; + TypedComplexArgument mComplex_BoostInfo; }; #endif // MTR_ENABLE_PROVISIONAL @@ -195156,6 +195136,8 @@ void registerClusterWaterHeaterManagement(Commands & commands) make_unique(), // make_unique(), // #endif // MTR_ENABLE_PROVISIONAL + make_unique(Id), // + make_unique(Id), // }; commands.RegisterCluster(clusterName, clusterCommands);