diff --git a/.github/.wordlist.txt b/.github/.wordlist.txt index 25c9ef8daa6594..ecfcb937e5b986 100644 --- a/.github/.wordlist.txt +++ b/.github/.wordlist.txt @@ -402,6 +402,7 @@ DevKitM devtype df dfe +DFILE dfu DgDxsfHx dhclient @@ -1354,6 +1355,7 @@ SVR SWD SWU symlinks +sysbuild sysconfdir SysConfig sysctl diff --git a/.github/workflows/darwin-tests.yaml b/.github/workflows/darwin-tests.yaml index a916250572bb6e..815293204b09de 100644 --- a/.github/workflows/darwin-tests.yaml +++ b/.github/workflows/darwin-tests.yaml @@ -95,6 +95,7 @@ jobs: --target darwin-x64-lit-icd-${BUILD_VARIANT} \ --target darwin-x64-microwave-oven-${BUILD_VARIANT} \ --target darwin-x64-rvc-${BUILD_VARIANT} \ + --target darwin-x64-network-manager-${BUILD_VARIANT} \ build \ --copy-artifacts-to objdir-clone \ " @@ -116,6 +117,7 @@ jobs: --bridge-app ./out/darwin-x64-bridge-${BUILD_VARIANT}/chip-bridge-app \ --microwave-oven-app ./out/darwin-x64-microwave-oven-${BUILD_VARIANT}/chip-microwave-oven-app \ --rvc-app ./out/darwin-x64-rvc-${BUILD_VARIANT}/chip-rvc-app \ + --network-manager-app ./out/darwin-x64-network-manager-${BUILD_VARIANT}/matter-network-manager-app \ " - name: Run OTA Test run: | diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 6a228404b083d8..2385d479e09f8a 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -188,6 +188,7 @@ jobs: src/app/zap-templates/zcl/data-model/chip/thermostat-cluster.xml \ src/app/zap-templates/zcl/data-model/chip/thread-border-router-management-cluster.xml \ src/app/zap-templates/zcl/data-model/chip/thread-network-diagnostics-cluster.xml \ + src/app/zap-templates/zcl/data-model/chip/thread-network-directory-cluster.xml \ src/app/zap-templates/zcl/data-model/chip/time-format-localization-cluster.xml \ src/app/zap-templates/zcl/data-model/chip/time-synchronization-cluster.xml \ src/app/zap-templates/zcl/data-model/chip/timer-cluster.xml \ @@ -225,6 +226,7 @@ jobs: --target linux-x64-lit-icd-${BUILD_VARIANT} \ --target linux-x64-microwave-oven-${BUILD_VARIANT} \ --target linux-x64-rvc-${BUILD_VARIANT} \ + --target linux-x64-network-manager-${BUILD_VARIANT} \ build \ --copy-artifacts-to objdir-clone \ " @@ -247,6 +249,7 @@ jobs: --lit-icd-app ./out/linux-x64-lit-icd-${BUILD_VARIANT}/lit-icd-app \ --microwave-oven-app ./out/linux-x64-microwave-oven-${BUILD_VARIANT}/chip-microwave-oven-app \ --rvc-app ./out/linux-x64-rvc-${BUILD_VARIANT}/chip-rvc-app \ + --network-manager-app ./out/linux-x64-network-manager-${BUILD_VARIANT}/matter-network-manager-app \ " - name: Run purposeful failure tests using the python parser sending commands to chip-tool @@ -288,6 +291,7 @@ jobs: --lit-icd-app ./out/linux-x64-lit-icd-${BUILD_VARIANT}/lit-icd-app \ --microwave-oven-app ./out/linux-x64-microwave-oven-${BUILD_VARIANT}/chip-microwave-oven-app \ --rvc-app ./out/linux-x64-rvc-${BUILD_VARIANT}/chip-rvc-app \ + --network-manager-app ./out/linux-x64-network-manager-${BUILD_VARIANT}/matter-network-manager-app \ " - name: Run Tests using chip-repl (including slow) if: github.event_name == 'push' @@ -307,6 +311,7 @@ jobs: --lit-icd-app ./out/linux-x64-lit-icd-${BUILD_VARIANT}/lit-icd-app \ --microwave-oven-app ./out/linux-x64-microwave-oven-${BUILD_VARIANT}/chip-microwave-oven-app \ --rvc-app ./out/linux-x64-rvc-${BUILD_VARIANT}/chip-rvc-app \ + --network-manager-app ./out/linux-x64-network-manager-${BUILD_VARIANT}/matter-network-manager-app \ " - name: Uploading core files uses: actions/upload-artifact@v4 @@ -375,6 +380,7 @@ jobs: --target darwin-x64-lit-icd-${BUILD_VARIANT} \ --target darwin-x64-microwave-oven-${BUILD_VARIANT} \ --target darwin-x64-rvc-${BUILD_VARIANT} \ + --target darwin-x64-network-manager-${BUILD_VARIANT} \ build \ --copy-artifacts-to objdir-clone \ " @@ -398,6 +404,7 @@ jobs: --lit-icd-app ./out/darwin-x64-lit-icd-${BUILD_VARIANT}/lit-icd-app \ --microwave-oven-app ./out/darwin-x64-microwave-oven-${BUILD_VARIANT}/chip-microwave-oven-app \ --rvc-app ./out/darwin-x64-rvc-${BUILD_VARIANT}/chip-rvc-app \ + --network-manager-app ./out/darwin-x64-network-manager-${BUILD_VARIANT}/matter-network-manager-app \ " - name: Run purposeful failure tests using the python parser sending commands to chip-tool diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn index 1e634152a61d49..93333078f6f477 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn @@ -14,6 +14,7 @@ import("//build_overrides/build.gni") import("//build_overrides/pigweed.gni") +import("//build_overrides/pigweed_environment.gni") import("${build_root}/chip/java/config.gni") import("${build_root}/config/compiler/compiler.gni") import("${build_root}/config/sysroot.gni") @@ -348,7 +349,14 @@ config("cosmetic_default") { } config("runtime_default") { - if (is_clang) { # Using Pigweed clang instead of Darwin host clang + if (is_clang && + current_os == "mac") { # Using Pigweed clang instead of Darwin host clang + # Without pw_env_setup_CIPD_PIGWEED defined the hostclang:no_system_libcpp + # config silently uses the system libc++, usually resulting in linker errors. + assert( + defined(pw_env_setup_CIPD_PIGWEED), + "//build_overrides/pigweed_environment.gni must define pw_env_setup_CIPD_PIGWEED when using pigweed clang") + configs = [ "$dir_pw_toolchain/host_clang:no_system_libcpp", "$dir_pw_toolchain/host_clang:xcode_sysroot", diff --git a/config/esp32/components/chip/CMakeLists.txt b/config/esp32/components/chip/CMakeLists.txt index ab3d02023f67e1..7929e8bbbd14aa 100644 --- a/config/esp32/components/chip/CMakeLists.txt +++ b/config/esp32/components/chip/CMakeLists.txt @@ -237,6 +237,12 @@ else() chip_gn_arg_append("chip_openthread_ftd" "false") endif() +if (CONFIG_OPENTHREAD_BORDER_ROUTER) + chip_gn_arg_append("chip_openthread_border_router" "true") +else() + chip_gn_arg_append("chip_openthread_border_router" "false") +endif() + if (CONFIG_ENABLE_OTA_REQUESTOR) chip_gn_arg_append("chip_enable_ota_requestor" "true") endif() diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_70mai_Matter_PAA_vid_0x140B.der b/credentials/development/paa-root-certs/dcld_mirror_CN_70mai_Matter_PAA_vid_0x140B.der new file mode 100644 index 00000000000000..eb604af4036b25 Binary files /dev/null and b/credentials/development/paa-root-certs/dcld_mirror_CN_70mai_Matter_PAA_vid_0x140B.der differ diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_70mai_Matter_PAA_vid_0x140B.pem b/credentials/development/paa-root-certs/dcld_mirror_CN_70mai_Matter_PAA_vid_0x140B.pem new file mode 100644 index 00000000000000..64725b6729e88f --- /dev/null +++ b/credentials/development/paa-root-certs/dcld_mirror_CN_70mai_Matter_PAA_vid_0x140B.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBxjCCAWygAwIBAgIRANBskmmy8YAEmHofOp/xYmcwCgYIKoZIzj0EAwIwMTEZ +MBcGA1UEAwwQNzBtYWkgTWF0dGVyIFBBQTEUMBIGCisGAQQBgqJ8AgEMBDE0MEIw +IBcNMjMwNzI0MDcwMTMxWhgPMjUyMzAzMjUwODAxMzFaMDExGTAXBgNVBAMMEDcw +bWFpIE1hdHRlciBQQUExFDASBgorBgEEAYKifAIBDAQxNDBCMFkwEwYHKoZIzj0C +AQYIKoZIzj0DAQcDQgAEh7YSUJl3gJtgkIssE/7ZO9pRO5XbfS73b1zF2/Wjbi9r +igxSqAyIMVpObAMsfdin8f8+WwBeG4deexmwq5jzhKNjMGEwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUFFpRtZs7j1lqjNmWQvB1DDSYOG8wDgYDVR0PAQH/BAQD +AgGGMB8GA1UdIwQYMBaAFBRaUbWbO49ZaozZlkLwdQw0mDhvMAoGCCqGSM49BAMC +A0gAMEUCIEACwkh/UMcjnV6noWfg7pjYQt1Z9tB5S32YvDKUQg44AiEAkXj2t5TC +7huhrQmdHeBr3e1+Eci4q068qd2nI4zECM0= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Anker_Innovations_Matter_PAA_vid_0x1533.der b/credentials/development/paa-root-certs/dcld_mirror_CN_Anker_Innovations_Matter_PAA_vid_0x1533.der new file mode 100644 index 00000000000000..1300a90a351e31 Binary files /dev/null and b/credentials/development/paa-root-certs/dcld_mirror_CN_Anker_Innovations_Matter_PAA_vid_0x1533.der differ diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Anker_Innovations_Matter_PAA_vid_0x1533.pem b/credentials/development/paa-root-certs/dcld_mirror_CN_Anker_Innovations_Matter_PAA_vid_0x1533.pem new file mode 100644 index 00000000000000..19b6468c3f20b5 --- /dev/null +++ b/credentials/development/paa-root-certs/dcld_mirror_CN_Anker_Innovations_Matter_PAA_vid_0x1533.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIB0zCCAXqgAwIBAgIEEAAACTAKBggqhkjOPQQDAjA9MRQwEgYKKwYBBAGConwC +AQwEMTUzMzElMCMGA1UEAwwcQW5rZXIgSW5ub3ZhdGlvbnMgTWF0dGVyIFBBQTAg +Fw0yNDA1MTYwOTI2MjhaGA85OTk5MTIzMDA5MjYyOFowPTEUMBIGCisGAQQBgqJ8 +AgEMBDE1MzMxJTAjBgNVBAMMHEFua2VyIElubm92YXRpb25zIE1hdHRlciBQQUEw +WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQGMvtbso2avjK1ARPNTFoXX/3kpKmu +e8i4zfRjCFjs8mxZkOrmbS4IOw3wlWgaL91danRizORhbWilHsdyDiKOo2YwZDAS +BgNVHRMBAf8ECDAGAQH/AgEBMB8GA1UdIwQYMBaAFF0RbnEDG5pPw1S1mMx4V6Su +ULKGMB0GA1UdDgQWBBRdEW5xAxuaT8NUtZjMeFekrlCyhjAOBgNVHQ8BAf8EBAMC +AQYwCgYIKoZIzj0EAwIDRwAwRAIgfLXElnmPFMYwytT/mOOkp7enq1G6a/zpYKQu +oSJ6EUoCIDdhtSd9z6h+/dN4mSly8OIRE2Zo6z9bFpQVZFt6BzBy +-----END CERTIFICATE----- \ No newline at end of file diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Aqara_Matter_PAA_01_O_Lumi_United_Technology_Co__Ltd.der b/credentials/development/paa-root-certs/dcld_mirror_CN_Aqara_Matter_PAA_01_O_Lumi_United_Technology_Co__Ltd.der new file mode 100644 index 00000000000000..6887a91e999698 Binary files /dev/null and b/credentials/development/paa-root-certs/dcld_mirror_CN_Aqara_Matter_PAA_01_O_Lumi_United_Technology_Co__Ltd.der differ diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Aqara_Matter_PAA_01_O_Lumi_United_Technology_Co__Ltd.pem b/credentials/development/paa-root-certs/dcld_mirror_CN_Aqara_Matter_PAA_01_O_Lumi_United_Technology_Co__Ltd.pem new file mode 100644 index 00000000000000..1df7012d14a480 --- /dev/null +++ b/credentials/development/paa-root-certs/dcld_mirror_CN_Aqara_Matter_PAA_01_O_Lumi_United_Technology_Co__Ltd.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB9TCCAZygAwIBAgIRAJ4ckIzu1/EXWiYXT/3PzuEwCgYIKoZIzj0EAwIwSTEo +MCYGA1UECgwfTHVtaSBVbml0ZWQgVGVjaG5vbG9neSBDby4sIEx0ZDEdMBsGA1UE +AwwUQXFhcmEgTWF0dGVyIFBBQSAjMDEwIBcNMjMwNjIwMDgxMjM3WhgPOTk5OTEy +MzEyMzU5NTlaMEkxKDAmBgNVBAoMH0x1bWkgVW5pdGVkIFRlY2hub2xvZ3kgQ28u +LCBMdGQxHTAbBgNVBAMMFEFxYXJhIE1hdHRlciBQQUEgIzAxMFkwEwYHKoZIzj0C +AQYIKoZIzj0DAQcDQgAEy3JmW4dkIzN5HbDL3v9Kr3ZY9c9an7DUCwY6CyKRyzxE +QE6z5Yy3TzannQBoomkMbnK9wkCPJJTNnR8NOr9S8qNjMGEwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUmpf77hNiX3/vVXQL+TXxdHHeS3YwDgYDVR0PAQH/BAQD +AgGGMB8GA1UdIwQYMBaAFJqX++4TYl9/71V0C/k18XRx3kt2MAoGCCqGSM49BAMC +A0cAMEQCIEWIzz9hCLfUsSNzuDWrWgI+omq1E9NlP2heP4ugfB2PAiB21Wzrf7m/ +jk0rmvAP1eqJjCwh9uvI34offdV04/bHfA== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_BouffaloLab_Matter_PAA_vid_0x130D.der b/credentials/development/paa-root-certs/dcld_mirror_CN_BouffaloLab_Matter_PAA_vid_0x130D.der new file mode 100644 index 00000000000000..af9aa7f1c2d153 Binary files /dev/null and b/credentials/development/paa-root-certs/dcld_mirror_CN_BouffaloLab_Matter_PAA_vid_0x130D.der differ diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_BouffaloLab_Matter_PAA_vid_0x130D.pem b/credentials/development/paa-root-certs/dcld_mirror_CN_BouffaloLab_Matter_PAA_vid_0x130D.pem new file mode 100644 index 00000000000000..a7553c76ab1361 --- /dev/null +++ b/credentials/development/paa-root-certs/dcld_mirror_CN_BouffaloLab_Matter_PAA_vid_0x130D.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIB0TCCAXegAwIBAgIQaITTalFdhyvSwC28BV1ngjAKBggqhkjOPQQDAjA3MR8w +HQYDVQQDDBZCb3VmZmFsb0xhYiBNYXR0ZXIgUEFBMRQwEgYKKwYBBAGConwCAQwE +MTMwRDAgFw0yMzA2MTkwNDI3MDVaGA8yMTIyMDYxOTA1MjcwNVowNzEfMB0GA1UE +AwwWQm91ZmZhbG9MYWIgTWF0dGVyIFBBQTEUMBIGCisGAQQBgqJ8AgEMBDEzMEQw +WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAT9qtlVs5p+5yzS91xz5hmcfXOglIDW +p7fqYE9uFmq2x8IthHxwXF9PXG4LbMlgaq3cL/ijr9kqfSjQ1q+3iaY3o2MwYTAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRltKiB/t9YUSOvkqlSypCPRj116zAO +BgNVHQ8BAf8EBAMCAYYwHwYDVR0jBBgwFoAUZbSogf7fWFEjr5KpUsqQj0Y9desw +CgYIKoZIzj0EAwIDSAAwRQIgOXeXdhQxz1kKq5W+Pt0QDPVfR4OqRvFW4GZ3Kj0K +fFoCIQC7qxHppHx023BdMTCyAXoDDqKQ5z5BFNw1k/Xil2Q8lw== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_DSC_Matter_PAA_O_Dream_Security_Co__Ltd_C_KR.der b/credentials/development/paa-root-certs/dcld_mirror_CN_DSC_Matter_PAA_O_Dream_Security_Co__Ltd_C_KR.der new file mode 100644 index 00000000000000..38871931650057 Binary files /dev/null and b/credentials/development/paa-root-certs/dcld_mirror_CN_DSC_Matter_PAA_O_Dream_Security_Co__Ltd_C_KR.der differ diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_DSC_Matter_PAA_O_Dream_Security_Co__Ltd_C_KR.pem b/credentials/development/paa-root-certs/dcld_mirror_CN_DSC_Matter_PAA_O_Dream_Security_Co__Ltd_C_KR.pem new file mode 100644 index 00000000000000..f2d5c366c63bab --- /dev/null +++ b/credentials/development/paa-root-certs/dcld_mirror_CN_DSC_Matter_PAA_O_Dream_Security_Co__Ltd_C_KR.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB/DCCAaKgAwIBAgIUAUlzLLkOLfiBQM1Mtdd715RoIpcwCgYIKoZIzj0EAwIw +STELMAkGA1UEBhMCS1IxITAfBgNVBAoMGERyZWFtIFNlY3VyaXR5IENvLiwgTHRk +LjEXMBUGA1UEAwwORFNDIE1hdHRlciBQQUEwIBcNMjMxMDEzMDM0ODUzWhgPOTk5 +OTEyMzExNDU5NTlaMEkxCzAJBgNVBAYTAktSMSEwHwYDVQQKDBhEcmVhbSBTZWN1 +cml0eSBDby4sIEx0ZC4xFzAVBgNVBAMMDkRTQyBNYXR0ZXIgUEFBMFkwEwYHKoZI +zj0CAQYIKoZIzj0DAQcDQgAEwna+mEy1wfCZ0iDK4hoKP4js4HE9XmiRe7pZ1RLp +ahkjnkABlV+R4CixltIwnagyn0HuyhEWRFXhf2Na6nGORqNmMGQwHwYDVR0jBBgw +FoAUSvBR2pNudxvmFRNxKN5AJTNeaY0wHQYDVR0OBBYEFErwUdqTbncb5hUTcSje +QCUzXmmNMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEBMAoGCCqG +SM49BAMCA0gAMEUCIBFvRkkx9tuuocwTIBdNWg7or7XqaNp0pNK0BRKfEOkRAiEA +ykq93kYuFbX6lSzl3n0eJJ8Wo3L7b/l1mHtYTdhBQzE= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Ecovacs_Matter_PAA_O_ECOVACS_IOT_vid_0x1405.der b/credentials/development/paa-root-certs/dcld_mirror_CN_Ecovacs_Matter_PAA_O_ECOVACS_IOT_vid_0x1405.der new file mode 100644 index 00000000000000..ecf361dae6cc39 Binary files /dev/null and b/credentials/development/paa-root-certs/dcld_mirror_CN_Ecovacs_Matter_PAA_O_ECOVACS_IOT_vid_0x1405.der differ diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Ecovacs_Matter_PAA_O_ECOVACS_IOT_vid_0x1405.pem b/credentials/development/paa-root-certs/dcld_mirror_CN_Ecovacs_Matter_PAA_O_ECOVACS_IOT_vid_0x1405.pem new file mode 100644 index 00000000000000..8cef24133c1c0c --- /dev/null +++ b/credentials/development/paa-root-certs/dcld_mirror_CN_Ecovacs_Matter_PAA_O_ECOVACS_IOT_vid_0x1405.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIB1DCCAXqgAwIBAgIQW09Lv9ptt+IhqXXbbGzKTzAKBggqhkjOPQQDAjBJMRsw +GQYDVQQDDBJFY292YWNzIE1hdHRlciBQQUExFDASBgorBgEEAYKifAIBDAQxNDA1 +MRQwEgYDVQQKDAtFQ09WQUNTIElPVDAgFw0yNDAyMjEwNjM0MzlaGA85OTk5MTIz +MTIzNTk1OVowSTEbMBkGA1UEAwwSRWNvdmFjcyBNYXR0ZXIgUEFBMRQwEgYKKwYB +BAGConwCAQwEMTQwNTEUMBIGA1UECgwLRUNPVkFDUyBJT1QwWTATBgcqhkjOPQIB +BggqhkjOPQMBBwNCAAT/Qmenu+SAjEForNL9yxVa9swLcalKRiuZaH/DzH1yLP4X +JCmLbx6G67zSQoQSfpp77LqgutIbSJ8r7+oqANY5o0IwQDAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBQiJnIKCNppY2KxFpI8tHQtEus6UDAOBgNVHQ8BAf8EBAMC +AYYwCgYIKoZIzj0EAwIDSAAwRQIhAOGDpMmCrjYdlz102PIkf2DHsIg1rdocfPTc +340dgyNxAiA9GmencY7sOJp5KjYmHgppFtuKkY6/k7x1xD0GlCOcyQ== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Energy_Magic_Cube_Matter_PAA_001_vid_0x1462.der b/credentials/development/paa-root-certs/dcld_mirror_CN_Energy_Magic_Cube_Matter_PAA_001_vid_0x1462.der new file mode 100644 index 00000000000000..6438781dc45163 Binary files /dev/null and b/credentials/development/paa-root-certs/dcld_mirror_CN_Energy_Magic_Cube_Matter_PAA_001_vid_0x1462.der differ diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Energy_Magic_Cube_Matter_PAA_001_vid_0x1462.pem b/credentials/development/paa-root-certs/dcld_mirror_CN_Energy_Magic_Cube_Matter_PAA_001_vid_0x1462.pem new file mode 100644 index 00000000000000..67697392583be6 --- /dev/null +++ b/credentials/development/paa-root-certs/dcld_mirror_CN_Energy_Magic_Cube_Matter_PAA_001_vid_0x1462.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB5jCCAYygAwIBAgIRAPlpOvhByCYEtN9DBypV0jQwCgYIKoZIzj0EAwIwQTEp +MCcGA1UEAwwgRW5lcmd5IE1hZ2ljIEN1YmUgTWF0dGVyIFBBQSAwMDExFDASBgor +BgEEAYKifAIBDAQxNDYyMCAXDTIzMDcyNzEzMDA1NloYDzIyOTcwNTEwMTQwMDU2 +WjBBMSkwJwYDVQQDDCBFbmVyZ3kgTWFnaWMgQ3ViZSBNYXR0ZXIgUEFBIDAwMTEU +MBIGCisGAQQBgqJ8AgEMBDE0NjIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARe +uoz+NvT1NIHBc0aNiaHp1Vcw2ysyUq6Pgi61CSJMLQ6seo1WhSVMCYof6NCJNGan +M8YeVOQINZI4h0iXKnwDo2MwYTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSu +akG67t30MGPMHc7nzcgTP8ynODAOBgNVHQ8BAf8EBAMCAYYwHwYDVR0jBBgwFoAU +rmpBuu7d9DBjzB3O583IEz/MpzgwCgYIKoZIzj0EAwIDSAAwRQIgXd+gekuzZQlD +nEnpvn16oe8qf83XWNLOBU7kpYvXjjYCIQDhbZRrEq6U7skYoqJn478a+b4EEexA +4790Zm8JHbhRlQ== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Espressif_Matter_Open_PAA_O_Espressif_Systems.der b/credentials/development/paa-root-certs/dcld_mirror_CN_Espressif_Matter_Open_PAA_O_Espressif_Systems.der new file mode 100644 index 00000000000000..7ec9d617be0fc5 Binary files /dev/null and b/credentials/development/paa-root-certs/dcld_mirror_CN_Espressif_Matter_Open_PAA_O_Espressif_Systems.der differ diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Espressif_Matter_Open_PAA_O_Espressif_Systems.pem b/credentials/development/paa-root-certs/dcld_mirror_CN_Espressif_Matter_Open_PAA_O_Espressif_Systems.pem new file mode 100644 index 00000000000000..459570181a08b3 --- /dev/null +++ b/credentials/development/paa-root-certs/dcld_mirror_CN_Espressif_Matter_Open_PAA_O_Espressif_Systems.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB6DCCAY2gAwIBAgIRANvFEo4HItdgmPRAoZzjBm0wCgYIKoZIzj0EAwIwQDEi +MCAGA1UEAwwZRXNwcmVzc2lmIE1hdHRlciBPcGVuIFBBQTEaMBgGA1UECgwRRXNw +cmVzc2lmIFN5c3RlbXMwIBcNMjQwMTA1MDUxMzI1WhgPOTk5OTEyMzEyMzU5NTla +MEAxIjAgBgNVBAMMGUVzcHJlc3NpZiBNYXR0ZXIgT3BlbiBQQUExGjAYBgNVBAoM +EUVzcHJlc3NpZiBTeXN0ZW1zMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOXAK +KrklPSU2LY/wvSf2GWxEHXqyHPm5cid870KN3R8LSHWshC9kH54QFUmoNHcTviE3 +5DkjJwhNL5OR/ccfq6NmMGQwEgYDVR0TAQH/BAgwBgEB/wIBATAfBgNVHSMEGDAW +gBRmTMGwLTMc15FUbp4UiDMxib1v0DAdBgNVHQ4EFgQUZkzBsC0zHNeRVG6eFIgz +MYm9b9AwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA0kAMEYCIQD+igSgqYgP +rKC7jLCWGQ8NydYpU591po+wJ6Vc6PkWWgIhANPW0FQv9FuBMXP8zp0l8eAW8h6A +4GB6+MmFQeq97qcz +-----END CERTIFICATE----- \ No newline at end of file diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Feit_Electric_PAA_vid_0x1423.der b/credentials/development/paa-root-certs/dcld_mirror_CN_Feit_Electric_PAA_vid_0x1423.der new file mode 100644 index 00000000000000..94dcffaedae0f6 Binary files /dev/null and b/credentials/development/paa-root-certs/dcld_mirror_CN_Feit_Electric_PAA_vid_0x1423.der differ diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Feit_Electric_PAA_vid_0x1423.pem b/credentials/development/paa-root-certs/dcld_mirror_CN_Feit_Electric_PAA_vid_0x1423.pem new file mode 100644 index 00000000000000..963d6673897371 --- /dev/null +++ b/credentials/development/paa-root-certs/dcld_mirror_CN_Feit_Electric_PAA_vid_0x1423.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIByzCCAXGgAwIBAgIUe3w51hygpwHd6Tlxj2adk6IF5pswCgYIKoZIzj0EAwIw +MjEaMBgGA1UEAwwRRmVpdCBFbGVjdHJpYyBQQUExFDASBgorBgEEAYKifAIBDAQx +NDIzMCAXDTIzMDcyMTA1MzM1MFoYDzgzMjMwOTA3MDUzMzUwWjAyMRowGAYDVQQD +DBFGZWl0IEVsZWN0cmljIFBBQTEUMBIGCisGAQQBgqJ8AgEMBDE0MjMwWTATBgcq +hkjOPQIBBggqhkjOPQMBBwNCAAQT516150jQlChLACUsk9Y+KWG6i53J9UFXF8hS +QeD91fdRMagNnT9fvq67FaQxw+I8ZJlhr0S6dmWcJ2mg/l+io2MwYTAPBgNVHRMB +Af8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUBp6Gv3HbUvnfPOp9 +5CkMn1uZaPEwHwYDVR0jBBgwFoAUBp6Gv3HbUvnfPOp95CkMn1uZaPEwCgYIKoZI +zj0EAwIDSAAwRQIgL85qVK45czuhx53cqICB+NUVZTnyx5n+Yxc1hjZjrt4CIQDQ ++vG34+tSW9Yu870l08ArF9MDmh2slLXKEOlXKXZ4Ew== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Freedompro_vid_0x1411_vid_0x1411.der b/credentials/development/paa-root-certs/dcld_mirror_CN_Freedompro_vid_0x1411_vid_0x1411.der new file mode 100644 index 00000000000000..f05b1fa37a5a0d Binary files /dev/null and b/credentials/development/paa-root-certs/dcld_mirror_CN_Freedompro_vid_0x1411_vid_0x1411.der differ diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Freedompro_vid_0x1411_vid_0x1411.pem b/credentials/development/paa-root-certs/dcld_mirror_CN_Freedompro_vid_0x1411_vid_0x1411.pem new file mode 100644 index 00000000000000..40c6b0ce9c2459 --- /dev/null +++ b/credentials/development/paa-root-certs/dcld_mirror_CN_Freedompro_vid_0x1411_vid_0x1411.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIByzCCAXCgAwIBAgIIGBv/c3jnq0YwCgYIKoZIzj0EAwIwNjEeMBwGA1UEAwwV +RnJlZWRvbXBybyx2aWQ9MHgxNDExMRQwEgYKKwYBBAGConwCAQwEMTQxMTAgFw0y +NDAxMDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowNjEeMBwGA1UEAwwVRnJlZWRv +bXBybyx2aWQ9MHgxNDExMRQwEgYKKwYBBAGConwCAQwEMTQxMTBZMBMGByqGSM49 +AgEGCCqGSM49AwEHA0IABJZ76CU2fZUWA7xdv+qJmsHWVH8BhZGeaph0IdSGYhc2 +EHuKTqnwHt/RdfJFwx23BLAWAkCIZajILqxSo8CCqKSjZjBkMBIGA1UdEwEB/wQI +MAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBQ8wKeoW2LBxfThfQZH +KREr2wkGwzAfBgNVHSMEGDAWgBQ8wKeoW2LBxfThfQZHKREr2wkGwzAKBggqhkjO +PQQDAgNJADBGAiEA3RyF7r8Us70mn2qUBWuSqLZqHxfc5Vf+ojENI7jaGOoCIQDd +RFsNUBD5e8rGbBENvySrN9JpRu4s8T6xiqB0bJUwbA== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_HOPERF_Matter_PAA_01_vid_0x1470.der b/credentials/development/paa-root-certs/dcld_mirror_CN_HOPERF_Matter_PAA_01_vid_0x1470.der new file mode 100644 index 00000000000000..db85defaeed865 Binary files /dev/null and b/credentials/development/paa-root-certs/dcld_mirror_CN_HOPERF_Matter_PAA_01_vid_0x1470.der differ diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_HOPERF_Matter_PAA_01_vid_0x1470.pem b/credentials/development/paa-root-certs/dcld_mirror_CN_HOPERF_Matter_PAA_01_vid_0x1470.pem new file mode 100644 index 00000000000000..85255162c07d1b --- /dev/null +++ b/credentials/development/paa-root-certs/dcld_mirror_CN_HOPERF_Matter_PAA_01_vid_0x1470.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBzjCCAXSgAwIBAgIRAMB+YpaZQeu05JIuF/AMsiYwCgYIKoZIzj0EAwIwNTEd +MBsGA1UEAwwUSE9QRVJGIE1hdHRlciBQQUEgMDExFDASBgorBgEEAYKifAIBDAQx +NDcwMCAXDTIzMDgyNTA1Mjk1N1oYDzIyMjMwNzA4MDYyOTU3WjA1MR0wGwYDVQQD +DBRIT1BFUkYgTWF0dGVyIFBBQSAwMTEUMBIGCisGAQQBgqJ8AgEMBDE0NzAwWTAT +BgcqhkjOPQIBBggqhkjOPQMBBwNCAATVxJPVN3fr7vg9sOX24AO3WyMLWN/O9u5Z +pjjquJNYiPXVziNj1Yq7o1fFT+JJ/V8gEkq3az3CMfTgr5A3DhHko2MwYTAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBTpFg3EF/dBnJUyC782VnGTP/MSIjAOBgNV +HQ8BAf8EBAMCAYYwHwYDVR0jBBgwFoAU6RYNxBf3QZyVMgu/NlZxkz/zEiIwCgYI +KoZIzj0EAwIDSAAwRQIhAMghj3vry4WnuZhyPK8ZGqyFG2aNdKkJCqwy/4SkcHT7 +AiBxxCLcAC5bDcze/6tJcCuLX5vWaVQYw6IVwBwciEo+rw== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_HuaCheng_vid_0x1517.der b/credentials/development/paa-root-certs/dcld_mirror_CN_HuaCheng_vid_0x1517.der new file mode 100644 index 00000000000000..70f88707babe26 Binary files /dev/null and b/credentials/development/paa-root-certs/dcld_mirror_CN_HuaCheng_vid_0x1517.der differ diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_HuaCheng_vid_0x1517.pem b/credentials/development/paa-root-certs/dcld_mirror_CN_HuaCheng_vid_0x1517.pem new file mode 100644 index 00000000000000..46c19d08a6ebaf --- /dev/null +++ b/credentials/development/paa-root-certs/dcld_mirror_CN_HuaCheng_vid_0x1517.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBtDCCAVugAwIBAgIQMTnvQ8Ysuuff3Cmdn0VyATAKBggqhkjOPQQDAjApMREw +DwYDVQQDDAhIdWFDaGVuZzEUMBIGCisGAQQBgqJ8AgEMBDE1MTcwIBcNMjQwNjA1 +MDcyMTEwWhgPMjA1NDA1MjkwODIxMTBaMCkxETAPBgNVBAMMCEh1YUNoZW5nMRQw +EgYKKwYBBAGConwCAQwEMTUxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABO6k +hF5Vx6fsRTeVPGUif/NVZ5oehv2GaTvLCubOPs5JmoTkFold5kbDRIYHQ9CjKZRR +HnvMX7bnXfceXrgg8vyjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJbG +HZztCtKeBCkmjjgkc8j432nNMA4GA1UdDwEB/wQEAwIBhjAfBgNVHSMEGDAWgBSW +xh2c7QrSngQpJo44JHPI+N9pzTAKBggqhkjOPQQDAgNHADBEAiBzn5SFeh8DbTI8 +73sMeGFpwfgGOyBnhPxHQW/U6al/3QIgJqYZB8V0Kp+IpFP6BGrrqeqXBCvEOsdh +r83FJwSB0E0= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_IKEA_of_Sweden_Matter_PAA_G1_vid_0x117C.der b/credentials/development/paa-root-certs/dcld_mirror_CN_IKEA_of_Sweden_Matter_PAA_G1_vid_0x117C.der new file mode 100644 index 00000000000000..acfdd885ebfe9f Binary files /dev/null and b/credentials/development/paa-root-certs/dcld_mirror_CN_IKEA_of_Sweden_Matter_PAA_G1_vid_0x117C.der differ diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_IKEA_of_Sweden_Matter_PAA_G1_vid_0x117C.pem b/credentials/development/paa-root-certs/dcld_mirror_CN_IKEA_of_Sweden_Matter_PAA_G1_vid_0x117C.pem new file mode 100644 index 00000000000000..d275a9a64f8ec0 --- /dev/null +++ b/credentials/development/paa-root-certs/dcld_mirror_CN_IKEA_of_Sweden_Matter_PAA_G1_vid_0x117C.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB4zCCAYqgAwIBAgIUPqEhcfoGwW/yt/qRsBs+jf7MklswCgYIKoZIzj0EAwIw +PTElMCMGA1UEAwwcSUtFQSBvZiBTd2VkZW4gTWF0dGVyIFBBQSBHMTEUMBIGCisG +AQQBgqJ8AgEMBDExN0MwIBcNMjQwMjE0MTQ1ODMzWhgPMjA5OTAxMjYxNDU4MzJa +MD0xJTAjBgNVBAMMHElLRUEgb2YgU3dlZGVuIE1hdHRlciBQQUEgRzExFDASBgor +BgEEAYKifAIBDAQxMTdDMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE6/PMOZHP +VJqqJbBLzK6Q5+9kjTEnjCnJ6Ba9+/3kCQPRZmiZnXYHB0Z0cUYmenTFXPlGfUCp +blSOtmL48AtPF6NmMGQwEgYDVR0TAQH/BAgwBgEB/wIBATAfBgNVHSMEGDAWgBRr +MYz86Zkg1zKPX0NtXqMyuLddmjAdBgNVHQ4EFgQUazGM/OmZINcyj19DbV6jMri3 +XZowDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA0cAMEQCIETlgc4Us5SE7gPJ +TY9W4D9Fh27DGjtosGP/l99EWrmYAiAXFqbpGtFV1FAx2GxDNa5zYssd4ZpLoewg +rJdUrJOP/g== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Nexus_Matter_PAA_G1_O_Technology_Nexus_SBS_AB_C_SE.der b/credentials/development/paa-root-certs/dcld_mirror_CN_Nexus_Matter_PAA_G1_O_Technology_Nexus_SBS_AB_C_SE.der new file mode 100644 index 00000000000000..cae1616ad4858b Binary files /dev/null and b/credentials/development/paa-root-certs/dcld_mirror_CN_Nexus_Matter_PAA_G1_O_Technology_Nexus_SBS_AB_C_SE.der differ diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Nexus_Matter_PAA_G1_O_Technology_Nexus_SBS_AB_C_SE.pem b/credentials/development/paa-root-certs/dcld_mirror_CN_Nexus_Matter_PAA_G1_O_Technology_Nexus_SBS_AB_C_SE.pem new file mode 100644 index 00000000000000..aba32c22d0e4a9 --- /dev/null +++ b/credentials/development/paa-root-certs/dcld_mirror_CN_Nexus_Matter_PAA_G1_O_Technology_Nexus_SBS_AB_C_SE.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIICATCCAaagAwIBAgIQQbk6g1NwvzyrSGCoTI9JgDAKBggqhkjOPQQDAjBNMQsw +CQYDVQQGEwJTRTEgMB4GA1UEChMXVGVjaG5vbG9neSBOZXh1cyBTQlMgQUIxHDAa +BgNVBAMTE05leHVzIE1hdHRlciBQQUEgRzEwIBcNMjMxMjA2MDkxODUyWhgPOTk5 +OTEyMzEyMzU5NTlaME0xCzAJBgNVBAYTAlNFMSAwHgYDVQQKExdUZWNobm9sb2d5 +IE5leHVzIFNCUyBBQjEcMBoGA1UEAxMTTmV4dXMgTWF0dGVyIFBBQSBHMTBZMBMG +ByqGSM49AgEGCCqGSM49AwEHA0IABBvboumawHfPKRmcmdS+qMiSiSb2JdGnbcge +qGGDIfs+iwLVIy3mX8LQNNUftI8MGcfr+wlxZBcbsDeIfRr6oUCjZjBkMBIGA1Ud +EwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFKjZp1YgGMB2xrt8vfZ3dEZB3ranMB8G +A1UdIwQYMBaAFKjZp1YgGMB2xrt8vfZ3dEZB3ranMA4GA1UdDwEB/wQEAwIBBjAK +BggqhkjOPQQDAgNJADBGAiEA58S/WXdqoF5E23LJ4B00fZNEZY1NumThNsDV18Td +YCkCIQC8Rt3z9E1Gb6aa6/l6tYCTHjuJSTLXU1fU06tYgXuFVw== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Quectel_Matter_CA_PAA_vid_0x1410.der b/credentials/development/paa-root-certs/dcld_mirror_CN_Quectel_Matter_CA_PAA_vid_0x1410.der new file mode 100644 index 00000000000000..c328fa914b5514 Binary files /dev/null and b/credentials/development/paa-root-certs/dcld_mirror_CN_Quectel_Matter_CA_PAA_vid_0x1410.der differ diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Quectel_Matter_CA_PAA_vid_0x1410.pem b/credentials/development/paa-root-certs/dcld_mirror_CN_Quectel_Matter_CA_PAA_vid_0x1410.pem new file mode 100644 index 00000000000000..9d55c08f1fa355 --- /dev/null +++ b/credentials/development/paa-root-certs/dcld_mirror_CN_Quectel_Matter_CA_PAA_vid_0x1410.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIB0DCCAXagAwIBAgIRAIEvACmo2CmWcJt7n1iIlgUwCgYIKoZIzj0EAwIwNjEe +MBwGA1UEAwwVUXVlY3RlbCBNYXR0ZXIgQ0EgUEFBMRQwEgYKKwYBBAGConwCAQwE +MTQxMDAgFw0yMzA3MDMwMjQyMDlaGA85OTk5MDcwMzAzNDIwOVowNjEeMBwGA1UE +AwwVUXVlY3RlbCBNYXR0ZXIgQ0EgUEFBMRQwEgYKKwYBBAGConwCAQwEMTQxMDBZ +MBMGByqGSM49AgEGCCqGSM49AwEHA0IABHbK+6EZuOMXSfvxUMciJk195OMMkqqo +ni2yuhqViOOoUbIrYORv3KUOEGEQXv90jBNztsY6GqITxulM0rQnzr6jYzBhMA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFACMIPa55crFKDvbu/C8KWu4f4BTMA4G +A1UdDwEB/wQEAwIBhjAfBgNVHSMEGDAWgBQAjCD2ueXKxSg727vwvClruH+AUzAK +BggqhkjOPQQDAgNIADBFAiEAi5IdUgcMX656APT93NrxihH6nHtC4Xq6xm1ok5Jb +sI0CIF/e32TmBhYfaJGuy3aTt3uf64/u7dwrtyJJdinAAAvT +-----END CERTIFICATE----- \ No newline at end of file diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Safemo_Matter_PAA_vid_0x1506.der b/credentials/development/paa-root-certs/dcld_mirror_CN_Safemo_Matter_PAA_vid_0x1506.der new file mode 100644 index 00000000000000..aa0fee19220bf6 Binary files /dev/null and b/credentials/development/paa-root-certs/dcld_mirror_CN_Safemo_Matter_PAA_vid_0x1506.der differ diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Safemo_Matter_PAA_vid_0x1506.pem b/credentials/development/paa-root-certs/dcld_mirror_CN_Safemo_Matter_PAA_vid_0x1506.pem new file mode 100644 index 00000000000000..74cf5e689e0856 --- /dev/null +++ b/credentials/development/paa-root-certs/dcld_mirror_CN_Safemo_Matter_PAA_vid_0x1506.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIByDCCAW6gAwIBAgIRAJbGQ0wgEtKWe2yXGITJcggwCgYIKoZIzj0EAwIwMjEa +MBgGA1UEAwwRU2FmZW1vIE1hdHRlciBQQUExFDASBgorBgEEAYKifAIBDAQxNTA2 +MCAXDTIzMTIxOTAyNTExNVoYDzk5OTkxMjMxMjM1OTU5WjAyMRowGAYDVQQDDBFT +YWZlbW8gTWF0dGVyIFBBQTEUMBIGCisGAQQBgqJ8AgEMBDE1MDYwWTATBgcqhkjO +PQIBBggqhkjOPQMBBwNCAASnyGSMLdXvtFpLCf3ER4b2cWnzsDIuGDqV5qdOqwHV +MMgOOMf3CIIG1eIapDIIynEGH814Kgo2MwmACe3Sl7mJo2MwYTAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBQrACL5tHEfXSTZbQ8Dc6oJDqXbkzAOBgNVHQ8BAf8E +BAMCAYYwHwYDVR0jBBgwFoAUKwAi+bRxH10k2W0PA3OqCQ6l25MwCgYIKoZIzj0E +AwIDSAAwRQIhAO4k9enw9QkBsaNJiqaYsq4GOXCNY5KwEHsijJ/R72dtAiAqLfFC +QLytRarca/d8H1ebYil3mTULKIw+T9SdF2DTtQ== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Schneider_Electric_Matter_PAA_01_vid_0x105E.der b/credentials/development/paa-root-certs/dcld_mirror_CN_Schneider_Electric_Matter_PAA_01_vid_0x105E.der new file mode 100644 index 00000000000000..a4fb97ac768bd0 Binary files /dev/null and b/credentials/development/paa-root-certs/dcld_mirror_CN_Schneider_Electric_Matter_PAA_01_vid_0x105E.der differ diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Schneider_Electric_Matter_PAA_01_vid_0x105E.pem b/credentials/development/paa-root-certs/dcld_mirror_CN_Schneider_Electric_Matter_PAA_01_vid_0x105E.pem new file mode 100644 index 00000000000000..0ff7d45e7e6b66 --- /dev/null +++ b/credentials/development/paa-root-certs/dcld_mirror_CN_Schneider_Electric_Matter_PAA_01_vid_0x105E.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB6jCCAY+gAwIBAgIUTLcD1z2L5rjjLDgXGe9eaRjDJbIwCgYIKoZIzj0EAwIw +QTEpMCcGA1UEAwwgU2NobmVpZGVyIEVsZWN0cmljIE1hdHRlciBQQUEgMDExFDAS +BgorBgEEAYKifAIBDAQxMDVFMCAXDTIzMDUzMDA4NTkzNVoYDzk5OTkxMjMxMjM1 +OTU5WjBBMSkwJwYDVQQDDCBTY2huZWlkZXIgRWxlY3RyaWMgTWF0dGVyIFBBQSAw +MTEUMBIGCisGAQQBgqJ8AgEMBDEwNUUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC +AASeiLYu4iC/FuzAZky9ZRnekYyoAQp3mFJlvSk45uOzShg0H3atpNOjHnLJsT3c +y33TIL8d2QECHk3lxh32KQ3Uo2MwYTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQY +MBaAFKrUcaKhFhHJQSjxqzXLwmy0pksAMB0GA1UdDgQWBBSq1HGioRYRyUEo8as1 +y8JstKZLADAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwIDSQAwRgIhAJFw9Pzr +wPbnxoI+kmjiyVJWTbbEdEj851UVqUQzQyyzAiEA39kgmOdpo8i+IQK0JeybWT1O +y5R/tH3fkBE1UmdU9vs= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Siterwell_Matter_PAA_vid_0x1280.der b/credentials/development/paa-root-certs/dcld_mirror_CN_Siterwell_Matter_PAA_vid_0x1280.der new file mode 100644 index 00000000000000..3bef6e3cd241ed Binary files /dev/null and b/credentials/development/paa-root-certs/dcld_mirror_CN_Siterwell_Matter_PAA_vid_0x1280.der differ diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Siterwell_Matter_PAA_vid_0x1280.pem b/credentials/development/paa-root-certs/dcld_mirror_CN_Siterwell_Matter_PAA_vid_0x1280.pem new file mode 100644 index 00000000000000..058ad61c6bbd4e --- /dev/null +++ b/credentials/development/paa-root-certs/dcld_mirror_CN_Siterwell_Matter_PAA_vid_0x1280.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBrDCCAVKgAwIBAgIQRiyCdXmivf3OlrNzD0ppxzAKBggqhkjOPQQDAjA1MR0w +GwYDVQQDDBRTaXRlcndlbGwgTWF0dGVyIFBBQTEUMBIGCisGAQQBgqJ8AgEMBDEy +ODAwIBcNMjMwNzMxMDUxOTExWhgPMjA3MzA3MzEwNjE4NThaMDUxHTAbBgNVBAMM +FFNpdGVyd2VsbCBNYXR0ZXIgUEFBMRQwEgYKKwYBBAGConwCAQwEMTI4MDBZMBMG +ByqGSM49AgEGCCqGSM49AwEHA0IABKqK8wz3+8y4v2tI4y5yLxS7MgELWsbOD+7M +BnwC5bnvMmep7k1J/Izj+w5csov3X6DXfnM/2pAWdkW5zNEdOjmjQjBAMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFFRB4Wuy7Mz+27DfvgBZeJfvUQpCMA4GA1Ud +DwEB/wQEAwIBhjAKBggqhkjOPQQDAgNIADBFAiBDG8NgmpCao831XgOF2Z3O68Xi +Rfjt/aIMJP75T7aZAwIhAIPnZzUVnXzqjSwvwjAs7NoN8GFHR2IMtTPX/eCfQ9Mb +-----END CERTIFICATE----- \ No newline at end of file diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Snowball_Matter_PAA_01.der b/credentials/development/paa-root-certs/dcld_mirror_CN_Snowball_Matter_PAA_01.der new file mode 100644 index 00000000000000..1918a5ab7a88a6 Binary files /dev/null and b/credentials/development/paa-root-certs/dcld_mirror_CN_Snowball_Matter_PAA_01.der differ diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Snowball_Matter_PAA_01.pem b/credentials/development/paa-root-certs/dcld_mirror_CN_Snowball_Matter_PAA_01.pem new file mode 100644 index 00000000000000..a16459e91393b3 --- /dev/null +++ b/credentials/development/paa-root-certs/dcld_mirror_CN_Snowball_Matter_PAA_01.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBpjCCAUygAwIBAgIRALcJaooAL54UjCdg4g1WTlkwCgYIKoZIzj0EAwIwITEf +MB0GA1UEAwwWU25vd2JhbGwgTWF0dGVyIFBBQSAwMTAgFw0yMzA1MjYwMjQ2NDZa +GA85OTk5MTIzMTIzNTk1OVowITEfMB0GA1UEAwwWU25vd2JhbGwgTWF0dGVyIFBB +QSAwMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABIg+JlpiMQ4B2XmkKsdifmVg +VTUyynb14BHfL+QYMtJOUh9OD+gncUsI/kUMpsZIsfQZMvmjVcAhFyCyofgXAR+j +YzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFPMKfvfKk0OqvqSAG79FSE/+ ++B5lMA4GA1UdDwEB/wQEAwIBhjAfBgNVHSMEGDAWgBTzCn73ypNDqr6kgBu/RUhP +/vgeZTAKBggqhkjOPQQDAgNIADBFAiBIqFNj7y0/uczzcen/QRBBtaCEx95taH/Y +/L6AGycY3AIhAJsGJzv3Dp79e89XvqjUIpZ6Gc26YYBjIslm1RiPJqOh +-----END CERTIFICATE----- \ No newline at end of file diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_SwitchBot_Matter_PAA_vid_0x1397.der b/credentials/development/paa-root-certs/dcld_mirror_CN_SwitchBot_Matter_PAA_vid_0x1397.der new file mode 100644 index 00000000000000..c790f5f8275d0b Binary files /dev/null and b/credentials/development/paa-root-certs/dcld_mirror_CN_SwitchBot_Matter_PAA_vid_0x1397.der differ diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_SwitchBot_Matter_PAA_vid_0x1397.pem b/credentials/development/paa-root-certs/dcld_mirror_CN_SwitchBot_Matter_PAA_vid_0x1397.pem new file mode 100644 index 00000000000000..3357104a819302 --- /dev/null +++ b/credentials/development/paa-root-certs/dcld_mirror_CN_SwitchBot_Matter_PAA_vid_0x1397.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBrTCCAVOgAwIBAgIRAKH/1ohrnkBd4iGtRVxQypEwCgYIKoZIzj0EAwIwNTEd +MBsGA1UEAwwUU3dpdGNoQm90IE1hdHRlciBQQUExFDASBgorBgEEAYKifAIBDAQx +Mzk3MCAXDTI0MDUyMDA2NTM1NloYDzk5OTkxMjMxMjM1OTU5WjA1MR0wGwYDVQQD +DBRTd2l0Y2hCb3QgTWF0dGVyIFBBQTEUMBIGCisGAQQBgqJ8AgEMBDEzOTcwWTAT +BgcqhkjOPQIBBggqhkjOPQMBBwNCAASEiZlGTnuCQv6nMESGdy6k8Mmfjx594NlF +6LbfNAy+7lzF9kwWHNe8LJ8tfy0K2s/lhj6nE5+lGJDiyx6J6GOUo0IwQDAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBTDlqbmlIzEa0Kigk0qb2lHl+pgBDAOBgNV +HQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwIDSAAwRQIhAIA5Bmac8qsY0Nn0As13JtAS +NiTo10DyhJgcH4JTAkHmAiBvDxfGuxxmPcBd3Z32m+vWvRJWdRdAxeUWOThq/VEc +Gw== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Tuya_Global_Matter_PAA.der b/credentials/development/paa-root-certs/dcld_mirror_CN_Tuya_Global_Matter_PAA.der new file mode 100644 index 00000000000000..5fbe23e150fbe5 Binary files /dev/null and b/credentials/development/paa-root-certs/dcld_mirror_CN_Tuya_Global_Matter_PAA.der differ diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_Tuya_Global_Matter_PAA.pem b/credentials/development/paa-root-certs/dcld_mirror_CN_Tuya_Global_Matter_PAA.pem new file mode 100644 index 00000000000000..c7acc15eff6d6e --- /dev/null +++ b/credentials/development/paa-root-certs/dcld_mirror_CN_Tuya_Global_Matter_PAA.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBqTCCAU+gAwIBAgIRAjag4Z9E4rs66DU81lWM2BowCgYIKoZIzj0EAwIwITEf +MB0GA1UEAwwWVHV5YSBHbG9iYWwgTWF0dGVyIFBBQTAgFw0yMzA1MTEwMzM3NTJa +GA85OTk5MTIzMTIzNTk1OVowITEfMB0GA1UEAwwWVHV5YSBHbG9iYWwgTWF0dGVy +IFBBQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABH4CeKD2gYwer2ruMLHlaHHQ +rAOojboW2PVJgF1yNaoWJX7kJpMvL3l2g95Ia687IpmYCbtoH6Q4hZwwaSICO/Gj +ZjBkMBIGA1UdEwEB/wQIMAYBAf8CAQEwHwYDVR0jBBgwFoAUhLmLZqwa/Za2RQ8j +07uWTU6KnGYwHQYDVR0OBBYEFIS5i2asGv2WtkUPI9O7lk1OipxmMA4GA1UdDwEB +/wQEAwIBBjAKBggqhkjOPQQDAgNIADBFAiEA/b0F0/4AiOXOexodWOFMjHDTc3Wu +IyZPa6pbKDgROdsCIGBPa6+9WufbJfEDqMG6APR6y4UL6EJ38aQ0nzfvGL7X +-----END CERTIFICATE----- \ No newline at end of file diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_U-tec_Group_Matter_PAA_vid_0x147F.der b/credentials/development/paa-root-certs/dcld_mirror_CN_U-tec_Group_Matter_PAA_vid_0x147F.der new file mode 100644 index 00000000000000..ea374038f5f675 Binary files /dev/null and b/credentials/development/paa-root-certs/dcld_mirror_CN_U-tec_Group_Matter_PAA_vid_0x147F.der differ diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_U-tec_Group_Matter_PAA_vid_0x147F.pem b/credentials/development/paa-root-certs/dcld_mirror_CN_U-tec_Group_Matter_PAA_vid_0x147F.pem new file mode 100644 index 00000000000000..2b9a9f28045d8a --- /dev/null +++ b/credentials/development/paa-root-certs/dcld_mirror_CN_U-tec_Group_Matter_PAA_vid_0x147F.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIB0TCCAXegAwIBAgIQcW2ozBN3DXDRxu5WB+7pQDAKBggqhkjOPQQDAjA3MR8w +HQYDVQQDDBZVLXRlYyBHcm91cCBNYXR0ZXIgUEFBMRQwEgYKKwYBBAGConwCAQwE +MTQ3RjAgFw0yMzA5MjgwODIwMjVaGA85OTk5MTIzMTIzNTk1OVowNzEfMB0GA1UE +AwwWVS10ZWMgR3JvdXAgTWF0dGVyIFBBQTEUMBIGCisGAQQBgqJ8AgEMBDE0N0Yw +WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQwlTnGXMTv/ti+2RiWW+jo5YAnQZCQ +O5YRB/H2CsJxaPGd8vMO+dkAojDGZ75WeX9i6BANsumrTVPxQEMEHF6Oo2MwYTAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSd4k9reLK0iTyCYxhrD2lOaLdwQDAO +BgNVHQ8BAf8EBAMCAYYwHwYDVR0jBBgwFoAUneJPa3iytIk8gmMYaw9pTmi3cEAw +CgYIKoZIzj0EAwIDSAAwRQIgeXVod4h2ZTejsAUFkqLHqhJoRl+6SMAIdx30vb3J +oAICIQCBsHNxeUOBR5Wr1mAislT44BDbwxg+1ImdAmcmicK/Qw== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_heiman_Matter_Protocol_PAA_vid_0x120B.der b/credentials/development/paa-root-certs/dcld_mirror_CN_heiman_Matter_Protocol_PAA_vid_0x120B.der new file mode 100644 index 00000000000000..306b4606d06e99 Binary files /dev/null and b/credentials/development/paa-root-certs/dcld_mirror_CN_heiman_Matter_Protocol_PAA_vid_0x120B.der differ diff --git a/credentials/development/paa-root-certs/dcld_mirror_CN_heiman_Matter_Protocol_PAA_vid_0x120B.pem b/credentials/development/paa-root-certs/dcld_mirror_CN_heiman_Matter_Protocol_PAA_vid_0x120B.pem new file mode 100644 index 00000000000000..9a310f1fa5f28d --- /dev/null +++ b/credentials/development/paa-root-certs/dcld_mirror_CN_heiman_Matter_Protocol_PAA_vid_0x120B.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIB2zCCAYCgAwIBAgIRAPzT02W5Sj7DtbZGJF4Hm9cwCgYIKoZIzj0EAwIwOzEj +MCEGA1UEAwwaaGVpbWFuIE1hdHRlciBQcm90b2NvbCBQQUExFDASBgorBgEEAYKi +fAIBDAQxMjBCMCAXDTIzMDYwNjA1MTcyOFoYDzIyOTcwMzIwMDYxNzI4WjA7MSMw +IQYDVQQDDBpoZWltYW4gTWF0dGVyIFByb3RvY29sIFBBQTEUMBIGCisGAQQBgqJ8 +AgEMBDEyMEIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAR8ZlgiorZXXfbaRRJr +qfcK8VMnJHVAmn1nx112Z7NAgrib9rBtXdga8llOxxxxHcEw463RFRkNKl3BioIz +V4Xao2MwYTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTZElNw6DDRCBGCv5UH +zVBPY1tnTzAOBgNVHQ8BAf8EBAMCAYYwHwYDVR0jBBgwFoAU2RJTcOgw0QgRgr+V +B81QT2NbZ08wCgYIKoZIzj0EAwIDSQAwRgIhAPoAVZ3kzmh3VI2pEDxx/7Gj0raO +9qPmjQ+fiEj+FOFSAiEAvzSukdMbjA26I55CG23E/LAfpzzQFFfJEN+r5G4bWk8= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/credentials/development/paa-root-certs/dcld_mirror_SERIALNUMBER_63709330400004_CN_NXP_Matter_PAA_G2.der b/credentials/development/paa-root-certs/dcld_mirror_SERIALNUMBER_63709330400004_CN_NXP_Matter_PAA_G2.der new file mode 100644 index 00000000000000..660cda4956548c Binary files /dev/null and b/credentials/development/paa-root-certs/dcld_mirror_SERIALNUMBER_63709330400004_CN_NXP_Matter_PAA_G2.der differ diff --git a/credentials/development/paa-root-certs/dcld_mirror_SERIALNUMBER_63709330400004_CN_NXP_Matter_PAA_G2.pem b/credentials/development/paa-root-certs/dcld_mirror_SERIALNUMBER_63709330400004_CN_NXP_Matter_PAA_G2.pem new file mode 100644 index 00000000000000..2a5978727a7756 --- /dev/null +++ b/credentials/development/paa-root-certs/dcld_mirror_SERIALNUMBER_63709330400004_CN_NXP_Matter_PAA_G2.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIB0TCCAXagAwIBAgIQPG2i2IIJe1nGYTjIGE2dRDAKBggqhkjOPQQDAjA1MRow +GAYDVQQDDBFOWFAgTWF0dGVyIFBBQSBHMjEXMBUGA1UEBRMONjM3MDkzMzA0MDAw +MDQwIBcNMjQwMzI1MTIwMDAwWhgPOTk5OTEyMzEyMzU5NTlaMDUxGjAYBgNVBAMM +EU5YUCBNYXR0ZXIgUEFBIEcyMRcwFQYDVQQFEw42MzcwOTMzMDQwMDAwNDBZMBMG +ByqGSM49AgEGCCqGSM49AwEHA0IABFQG9AzXs7Vj4mSfBcAotpFch33eiMtSmSPh +ppZWEcu+536+jhBeMcTqAxQKVxXYWVSVrSO8eAM/qnDav0LKmumjZjBkMBIGA1Ud +EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBREifg5wyJ0 +XTDaZrzD4bI7HzjPCDAfBgNVHSMEGDAWgBREifg5wyJ0XTDaZrzD4bI7HzjPCDAK +BggqhkjOPQQDAgNJADBGAiEA6Y7u2GyMhQ6m01NW7k/mSo1LNOXKZvSHwDYhoyS4 +jNECIQDofYDdYnwlCiGvhO05iTTO91Nfs1ZPBcBpQwWWV19SdA== +-----END CERTIFICATE----- \ No newline at end of file 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 4e9d7c221e11f7..1f2ac2eddc4bf1 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 @@ -4092,6 +4092,64 @@ cluster ElectricalEnergyMeasurement = 145 { readonly attribute int16u clusterRevision = 65533; } +/** 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; + + enum BoostStateEnum : enum8 { + kInactive = 0; + kActive = 1; + } + + bitmap Feature : bitmap32 { + kEnergyManagement = 0x1; + kTankPercent = 0x2; + } + + bitmap WaterHeaterDemandBitmap : bitmap8 { + kImmersionElement1 = 0x1; + kImmersionElement2 = 0x2; + kHeatPump = 0x4; + kBoiler = 0x8; + kOther = 0x10; + } + + bitmap WaterHeaterTypeBitmap : bitmap8 { + kImmersionElement1 = 0x1; + kImmersionElement2 = 0x2; + kHeatPump = 0x4; + kBoiler = 0x8; + kOther = 0x10; + } + + readonly attribute WaterHeaterTypeBitmap heaterTypes = 0; + readonly attribute WaterHeaterDemandBitmap heatDemand = 1; + readonly attribute optional int16u tankVolume = 2; + readonly attribute optional energy_mwh estimatedHeatRequired = 3; + readonly attribute optional percent tankPercentage = 4; + readonly attribute BoostStateEnum boostState = 5; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + 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; + } + + /** Allows a client to request that the water heater is put into a Boost state. */ + command access(invoke: manage) Boost(BoostRequest): DefaultSuccess = 0; + /** Allows a client to cancel an ongoing Boost operation. */ + command access(invoke: manage) CancelBoost(): DefaultSuccess = 1; +} + /** This cluster allows a client to manage the power draw of a device. An example of such a client could be an Energy Management System (EMS) which controls an Energy Smart Appliance (ESA). */ provisional cluster DeviceEnergyManagement = 152 { revision 4; @@ -8439,6 +8497,24 @@ endpoint 1 { ram attribute clusterRevision default = 1; } + server cluster WaterHeaterManagement { + callback attribute heaterTypes; + callback attribute heatDemand; + callback attribute tankVolume; + callback attribute estimatedHeatRequired; + callback attribute tankPercentage; + callback attribute boostState; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + callback attribute featureMap; + callback attribute clusterRevision; + + handle command Boost; + handle command CancelBoost; + } + server cluster DeviceEnergyManagement { emits event PowerAdjustStart; emits event PowerAdjustEnd; 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 cc48d168f0f3dd..d54b1986169992 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 @@ -13413,6 +13413,227 @@ } ] }, + { + "name": "Water Heater Management", + "code": 148, + "mfgCode": null, + "define": "WATER_HEATER_MANAGEMENT_CLUSTER", + "side": "server", + "enabled": 1, + "apiMaturity": "provisional", + "commands": [ + { + "name": "Boost", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "CancelBoost", + "code": 1, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "HeaterTypes", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "WaterHeaterTypeBitmap", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "HeatDemand", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "WaterHeaterDemandBitmap", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "TankVolume", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "EstimatedHeatRequired", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "energy_mwh", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "TankPercentage", + "code": 4, + "mfgCode": null, + "side": "server", + "type": "percent", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BoostState", + "code": 5, + "mfgCode": null, + "side": "server", + "type": "BoostStateEnum", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "GeneratedCommandList", + "code": 65528, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AcceptedCommandList", + "code": 65529, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "EventList", + "code": 65530, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AttributeList", + "code": 65531, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, { "name": "Device Energy Management", "code": 152, diff --git a/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h b/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h new file mode 100644 index 00000000000000..ecabfe22792f25 --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h @@ -0,0 +1,270 @@ +/* + * + * 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 WaterHeaterManagement { + +class WhmManufacturer; + +enum HeatingOp +{ + TurnHeatingOn, + TurnHeatingOff, + LeaveHeatingUnchanged +}; + +// This is an application level delegate to handle operational state commands according to the specific business logic. +class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate +{ +public: + WaterHeaterManagementDelegate(EndpointId clustersEndpoint); + + virtual ~WaterHeaterManagementDelegate() = default; + + void SetWaterHeaterManagementInstance(WaterHeaterManagement::Instance & instance); + + void SetWhmManufacturer(WhmManufacturer & whmManufacturer); + + /********************************************************************************* + * + * Methods implementing the WaterHeaterManagement::Delegate interface + * + *********************************************************************************/ + + /** + * @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. + * + * @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, + Optional targetReheat) override; + + /** + * @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). + * + * @return It should report SUCCESS if successful and FAILURE otherwise. + */ + Protocols::InteractionModel::Status HandleCancelBoost() override; + + // ------------------------------------------------------------------ + // Get attribute methods + BitMask GetHeaterTypes() override; + BitMask GetHeatDemand() override; + uint16_t GetTankVolume() override; + int64_t GetEstimatedHeatRequired() override; + Percent GetTankPercentage() override; + BoostStateEnum GetBoostState() override; + + // ------------------------------------------------------------------ + // Set attribute methods + 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. + * + * @param mode The Water Heater Mode (e.g. OFF, MANUAL or TIMED). + */ + Protocols::InteractionModel::Status SetWaterHeaterMode(uint8_t mode); + + /** + * @brief Set the water temperature of the tank + * + * @param waterTemperature The water temperature in 100th's Celsius + */ + void SetWaterTemperature(uint16_t waterTemperature); + + /** + * @brief Set the target water temperature of the tank + * + * @param targetWaterTemperature The water temperature in 100th's Celsius + */ + void SetTargetWaterTemperature(uint16_t targetWaterTemperature); + + /** + * @brief Determine whether the heating sources need to be turned on or off + */ + Protocols::InteractionModel::Status CheckIfHeatNeedsToBeTurnedOnOrOff(); + + /** + * @brief Static timer callback for when Boost timer expires. + */ + static void BoostTimerExpiry(System::Layer * systemLayer, void * delegate); + + /** + * @brief Object timer callback for when Boost timer expires. + */ + void HandleBoostTimerExpiry(); + + /** + * 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. + */ + 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. + */ + void DrawOffHotWater(uint8_t percentageReplaced, uint16_t replacedWaterTemperature); + +private: + /** + * @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. + * + * @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; + + // Pointer to the manufacturer specific object which understand the hardware + WhmManufacturer * mpWhmManufacturer; + + // Target water temperature in 100ths of a C + uint16_t mTargetWaterTemperature; + + // Actual water temperature in 100ths of a C + uint16_t mWaterTemperature; + + // The % of water at temperature mReplacedWaterTemperature + uint16_t mReplacedWaterTemperature; + + // 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). + 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). + 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. + 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. + 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%. + 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 + bool mBoostTargetTemperatureReached; + + /********************************************************************************* + * + * Member variables implementing the WaterHeaterManagement::Delegate interface + * + *********************************************************************************/ + + // 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 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. + 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. + Percent mTankPercentage; + + // This attribute SHALL indicate if the BOOST state, as triggered by a Boost command, is currently active. + BoostStateEnum mBoostState; +}; + +} // namespace WaterHeaterManagement +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/examples/all-clusters-app/all-clusters-common/include/WhmInstance.h b/examples/all-clusters-app/all-clusters-common/include/WhmInstance.h new file mode 100644 index 00000000000000..a776bd92b99534 --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/include/WhmInstance.h @@ -0,0 +1,58 @@ +/* + * + * 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 +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace WaterHeaterManagement { +using namespace chip::app::Clusters::WaterHeaterManagement; + +class WaterHeaterManagementInstance : public Instance +{ +public: + WaterHeaterManagementInstance(EndpointId aEndpointId, WaterHeaterManagementDelegate & aDelegate, Feature aFeature) : + WaterHeaterManagement::Instance(aEndpointId, aDelegate, aFeature) + { + mDelegate = &aDelegate; + } + + // Delete copy constructor and assignment operator. + WaterHeaterManagementInstance(const WaterHeaterManagementInstance &) = delete; + WaterHeaterManagementInstance(const WaterHeaterManagementInstance &&) = delete; + WaterHeaterManagementInstance & operator=(const WaterHeaterManagementInstance &) = delete; + + CHIP_ERROR Init(); + void Shutdown(); + + WaterHeaterManagementDelegate * GetDelegate() { return mDelegate; }; + +private: + WaterHeaterManagementDelegate * mDelegate; +}; + +} // namespace WaterHeaterManagement +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/examples/all-clusters-app/all-clusters-common/include/WhmMain.h b/examples/all-clusters-app/all-clusters-common/include/WhmMain.h new file mode 100644 index 00000000000000..21dc9d86519307 --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/include/WhmMain.h @@ -0,0 +1,32 @@ +/* + * + * 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 + +namespace chip { +namespace app { +namespace Clusters { +namespace WaterHeaterManagement { + +void WhmApplicationInit(); +void WhmApplicationShutdown(); + +} // namespace WaterHeaterManagement +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h b/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h new file mode 100644 index 00000000000000..d51e6a62f2021c --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h @@ -0,0 +1,139 @@ +/* + * + * 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 + +namespace chip { +namespace app { +namespace Clusters { +namespace WaterHeaterManagement { + +/** + * The WhmManufacturer example class + * + * Helps with handling the test triggers. + */ + +class WhmManufacturer +{ +public: + WhmManufacturer(WaterHeaterManagementInstance * whmInstance) { mWhmInstance = whmInstance; } + + WaterHeaterManagementInstance * GetWhmInstance() { return mWhmInstance; } + + WaterHeaterManagementDelegate * GetWhmDelegate() + { + if (mWhmInstance) + { + return mWhmInstance->GetDelegate(); + } + + return nullptr; + } + + /** + * @brief Called at start up to apply hardware settings + */ + CHIP_ERROR Init(); + + /** + * @brief Called at shutdown + */ + CHIP_ERROR Shutdown(); + + /** + * @brief Called to determine which heating sources to use, + */ + BitMask DetermineHeatingSources(); + + /** + * @brief Turn the heating of the water tank on. + * + * @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). + * @return An error if a problem occurs turning the heating on + */ + Protocols::InteractionModel::Status TurnHeatingOn(bool emergencyBoost); + + /** + * @brief Turn the heating of the water tank off. + * + * @return An error if a problem occurs turning the heating off + */ + Protocols::InteractionModel::Status TurnHeatingOff(); + + /** + * @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. + * + * @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, + Optional targetPercentage, + Optional targetReheat); + + /** + * @brief Called when the Boost command has been cancelled. + * + * @return It should report SUCCESS if successful and FAILURE otherwise. + */ + Protocols::InteractionModel::Status BoostCommandCancelled(); + + /** + * @brief Called when a boost command has completed. + */ + void BoostCommandFinished(); + +private: + WaterHeaterManagementInstance * mWhmInstance; +}; + +/** @brief Helper function to return the singleton WhmManufacturer instance + * + * This is needed by the WhmManufacturer class to support TestEventTriggers + * which are called outside of any class context. This allows the WhmManufacturer + * class to return the relevant Delegate instance in which to invoke the test + * events on. + * + * This function is typically found in main.cpp or wherever the singleton is created. + */ +WhmManufacturer * GetWhmManufacturer(); + +} // namespace WaterHeaterManagement +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/examples/all-clusters-app/all-clusters-common/include/water-heater-mode.h b/examples/all-clusters-app/all-clusters-common/include/water-heater-mode.h old mode 100644 new mode 100755 diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp new file mode 100644 index 00000000000000..fd19a3c59d740b --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp @@ -0,0 +1,461 @@ +/* + * + * 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 + +#include +#include +#include + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::WaterHeaterManagement; + +using Protocols::InteractionModel::Status; + +WaterHeaterManagementDelegate::WaterHeaterManagementDelegate(EndpointId clustersEndpoint) : + mpWhmInstance(nullptr), mpWhmManufacturer(nullptr), mWaterTemperature(0), mReplacedWaterTemperature(0), + mBoostTargetTemperatureReached(false), mTankVolume(0), mEstimatedHeatRequired(0), mTankPercentage(0), + mBoostState(BoostStateEnum::kInactive) +{} + +void WaterHeaterManagementDelegate::SetWaterHeaterManagementInstance(WaterHeaterManagement::Instance & instance) +{ + mpWhmInstance = &instance; +} + +void WaterHeaterManagementDelegate::SetWhmManufacturer(WhmManufacturer & whmManufacturer) +{ + mpWhmManufacturer = &whmManufacturer; +} + +/********************************************************************************* + * + * Methods implementing the WaterHeaterManagement::Delegate interace + * + *********************************************************************************/ + +BitMask WaterHeaterManagementDelegate::GetHeaterTypes() +{ + return mHeaterTypes; +} + +BitMask WaterHeaterManagementDelegate::GetHeatDemand() +{ + return mHeatDemand; +} + +uint16_t WaterHeaterManagementDelegate::GetTankVolume() +{ + return mTankVolume; +} + +int64_t WaterHeaterManagementDelegate::GetEstimatedHeatRequired() +{ + return mEstimatedHeatRequired; +} + +Percent WaterHeaterManagementDelegate::GetTankPercentage() +{ + return mTankPercentage; +} + +BoostStateEnum WaterHeaterManagementDelegate::GetBoostState() +{ + return mBoostState; +} + +void WaterHeaterManagementDelegate::SetHeaterTypes(BitMask heaterTypes) +{ + if (mHeaterTypes != heaterTypes) + { + mHeaterTypes = heaterTypes; + + MatterReportingAttributeChangeCallback(mEndpointId, WaterHeaterManagement::Id, Attributes::HeaterTypes::Id); + } +} + +void WaterHeaterManagementDelegate::SetHeatDemand(BitMask heatDemand) +{ + if (mHeatDemand != heatDemand) + { + mHeatDemand = heatDemand; + + MatterReportingAttributeChangeCallback(mEndpointId, WaterHeaterManagement::Id, Attributes::HeatDemand::Id); + } +} + +void WaterHeaterManagementDelegate::SetTankVolume(uint16_t tankVolume) +{ + if (mTankVolume != tankVolume) + { + mTankVolume = tankVolume; + + MatterReportingAttributeChangeCallback(mEndpointId, WaterHeaterManagement::Id, Attributes::TankVolume::Id); + } +} + +void WaterHeaterManagementDelegate::SetEstimatedHeatRequired(int64_t estimatedHeatRequired) +{ + if (mEstimatedHeatRequired != estimatedHeatRequired) + { + mEstimatedHeatRequired = estimatedHeatRequired; + + MatterReportingAttributeChangeCallback(mEndpointId, WaterHeaterManagement::Id, Attributes::EstimatedHeatRequired::Id); + } +} + +void WaterHeaterManagementDelegate::SetTankPercentage(Percent tankPercentage) +{ + if (mpWhmInstance != nullptr && mpWhmInstance->HasFeature(Feature::kTankPercent)) + { + if (mTankPercentage != tankPercentage) + { + mTankPercentage = tankPercentage; + + CheckIfHeatNeedsToBeTurnedOnOrOff(); + + MatterReportingAttributeChangeCallback(mEndpointId, WaterHeaterManagement::Id, Attributes::TankPercentage::Id); + } + } +} + +void WaterHeaterManagementDelegate::SetBoostState(BoostStateEnum boostState) +{ + if (mBoostState != boostState) + { + mBoostState = boostState; + + MatterReportingAttributeChangeCallback(mEndpointId, WaterHeaterManagement::Id, Attributes::BoostState::Id); + } +} + +/** + * @brief Handles the boost command + * + * 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. + */ +Status WaterHeaterManagementDelegate::HandleBoost(uint32_t durationS, Optional oneShot, Optional emergencyBoost, + Optional temporarySetpoint, Optional targetPercentage, + Optional targetReheat) +{ + Status status = Status::Success; + + ChipLogProgress(AppServer, "HandleBoost"); + + // Keep track of the boost command parameters + mBoostOneShot = oneShot; + mBoostEmergencyBoost = emergencyBoost; + mBoostTemporarySetpoint = temporarySetpoint; + mBoostTargetPercentage = targetPercentage; + mBoostTargetReheat = targetReheat; + + mBoostTargetTemperatureReached = false; + + // If a timer is running, cancel it so we can start a new boost command with the new duration + if (mBoostState == BoostStateEnum::kActive) + { + DeviceLayer::SystemLayer().CancelTimer(BoostTimerExpiry, this); + } + + CHIP_ERROR err = DeviceLayer::SystemLayer().StartTimer(System::Clock::Seconds32(durationS), BoostTimerExpiry, this); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "HandleBoost: Unable to start a Boost timer: %" CHIP_ERROR_FORMAT, err.Format()); + + // Not a lot we can do -> just set the boost state to inactive + SetBoostState(BoostStateEnum::kInactive); + + return Status::Failure; + } + + // Now running a boost command + SetBoostState(BoostStateEnum::kActive); + + if (mpWhmManufacturer != nullptr) + { + status = mpWhmManufacturer->BoostCommandStarted(durationS, oneShot, emergencyBoost, temporarySetpoint, targetPercentage, + targetReheat); + } + else + { + status = Status::InvalidInState; + ChipLogError(AppServer, "HandleBoost: mpWhmManufacturer == nullptr"); + } + + if (status == Status::Success) + { + // See if the heat needs to be turned on or off as a result of this boost command + status = CheckIfHeatNeedsToBeTurnedOnOrOff(); + } + + return status; +} + +void WaterHeaterManagementDelegate::BoostTimerExpiry(System::Layer * systemLayer, void * delegate) +{ + WaterHeaterManagementDelegate * dg = static_cast(delegate); + + dg->HandleBoostTimerExpiry(); +} + +/** + * @brief Timer for handling the completion of a boost command + */ +void WaterHeaterManagementDelegate::HandleBoostTimerExpiry() +{ + ChipLogError(AppServer, "HandleBoostTimerExpiry"); + + // The PowerAdjustment is no longer in progress + SetBoostState(BoostStateEnum::kInactive); + + if (mpWhmManufacturer != nullptr) + { + mpWhmManufacturer->BoostCommandFinished(); + } + else + { + ChipLogError(AppServer, "HandleBoostTimerExpiry: mpWhmManufacturer == nullptr"); + } + + CheckIfHeatNeedsToBeTurnedOnOrOff(); +} + +/** + * @brief Cancels 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). + */ +Status WaterHeaterManagementDelegate::HandleCancelBoost() +{ + ChipLogProgress(AppServer, "HandleCancelBoost"); + + if (mBoostState == BoostStateEnum::kActive) + { + SetBoostState(BoostStateEnum::kInactive); + mBoostEmergencyBoost.ClearValue(); + + DeviceLayer::SystemLayer().CancelTimer(BoostTimerExpiry, this); + + VerifyOrReturnValue(mpWhmManufacturer != nullptr, Status::InvalidInState); + + Status status = mpWhmManufacturer->BoostCommandCancelled(); + VerifyOrReturnValue(status == Status::Success, status); + + status = CheckIfHeatNeedsToBeTurnedOnOrOff(); + VerifyOrReturnValue(status == Status::Success, status); + } + + return Status::Success; +} + +/********************************************************************************* + * + * WaterHeaterManagementDelegate specific methods + * + *********************************************************************************/ + +void WaterHeaterManagementDelegate::SetWaterTemperature(uint16_t waterTemperature) +{ + mWaterTemperature = waterTemperature; + + if (mpWhmInstance != nullptr && mpWhmInstance->HasFeature(Feature::kTankPercent)) + { + mTankPercentage = 100; + } + + // See if the heat needs to be turned on or off + CheckIfHeatNeedsToBeTurnedOnOrOff(); +} + +void WaterHeaterManagementDelegate::SetTargetWaterTemperature(uint16_t targetWaterTemperature) +{ + mTargetWaterTemperature = targetWaterTemperature; + + // See if the heat needs to be turned on or off + CheckIfHeatNeedsToBeTurnedOnOrOff(); +} + +void WaterHeaterManagementDelegate::DrawOffHotWater(uint8_t percentageReplaced, uint16_t replacedWaterTemperature) +{ + // Only supported in the kTankPercent is supported. + // Replaces percentageReplaced% of the water in the tank with water of a temperature replacedWaterTemperature + if (mpWhmInstance != nullptr && mpWhmInstance->HasFeature(Feature::kTankPercent)) + { + // See if all of the water has now been replaced with replacedWaterTemperature + if (mTankPercentage >= percentageReplaced) + { + mTankPercentage -= percentageReplaced; + } + else + { + mTankPercentage = 0; + } + + mReplacedWaterTemperature = replacedWaterTemperature; + + CheckIfHeatNeedsToBeTurnedOnOrOff(); + } +} + +bool WaterHeaterManagementDelegate::HasWaterTemperatureReachedTarget() const +{ + // Determine the target temperature. If a boost command is in progress and has a mBoostTemporarySetpoint value use that as the + // target temperature. + // Note, in practise the actual heating is likely to be controlled by the thermostat's occupiedHeatingSetpoint most of the + // time, and the TemporarySetpoint (if not null) would be overiding the thermostat's occupiedHeatingSetpoint. + // However, this code doesn't rely upon the thermostat cluster. + uint16_t targetTemperature = (mBoostState == BoostStateEnum::kActive && mBoostTemporarySetpoint.HasValue()) + ? static_cast(mBoostTemporarySetpoint.Value()) + : mTargetWaterTemperature; + + VerifyOrReturnValue(mWaterTemperature >= targetTemperature, false); + + if (mBoostState == BoostStateEnum::kActive) + { + if (mBoostTargetTemperatureReached && mBoostTargetReheat.HasValue()) + { + // 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), + // mBoostTargetReheat 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 this field is included then the TargetPercentage field SHALL also be included, and the OneShot excluded. + VerifyOrReturnValue(mTankPercentage >= mBoostTargetReheat.Value(), false); + } + else if (mBoostTargetPercentage.HasValue()) + { + // If tank percentage is supported AND the targetPercentage.HasValue() then use target percentage to heat up. + VerifyOrReturnValue(mTankPercentage >= mBoostTargetPercentage.Value(), false); + } + } + + // Must have reached the right temperature + return true; +} + +Status WaterHeaterManagementDelegate::CheckIfHeatNeedsToBeTurnedOnOrOff() +{ + VerifyOrReturnError(mpWhmManufacturer != nullptr, Status::InvalidInState); + + HeatingOp heatingOp = HeatingOp::LeaveHeatingUnchanged; + + Status status = DetermineIfChangingHeatingState(heatingOp); + + VerifyOrReturnError(status == Status::Success, status); + + if (heatingOp == HeatingOp::TurnHeatingOn) + { + status = mpWhmManufacturer->TurnHeatingOn(mBoostEmergencyBoost.HasValue() ? mBoostEmergencyBoost.Value() : false); + } + else if (heatingOp == HeatingOp::TurnHeatingOff) + { + // If running a boost command with the oneShot parameter and turning heat off, then must have + // reached the boost command target temperature -> that's the boost command complete. + if (mBoostState == BoostStateEnum::kActive && mBoostOneShot.HasValue() && mBoostOneShot.Value()) + { + SetBoostState(BoostStateEnum::kInactive); + + DeviceLayer::SystemLayer().CancelTimer(BoostTimerExpiry, this); + + mBoostEmergencyBoost.ClearValue(); + + status = mpWhmManufacturer->BoostCommandCancelled(); + } + + // Turn the heating off + status = mpWhmManufacturer->TurnHeatingOff(); + } + + return status; +} + +Status WaterHeaterManagementDelegate::DetermineIfChangingHeatingState(HeatingOp & heatingOp) +{ + heatingOp = LeaveHeatingUnchanged; + + if (!HasWaterTemperatureReachedTarget()) + { + VerifyOrReturnError(WaterHeaterMode::Instance() != nullptr, Status::InvalidInState); + + uint8_t mode = WaterHeaterMode::Instance()->GetCurrentMode(); + + // The water in the tank is not at the target temperature. See if heating is currently off + if (mHeatDemand.Raw() == 0) + { + // Need to track whether the water temperature has reached the target temperature for the boost + // command when a oneShot option has been applied. + if (mBoostState == BoostStateEnum::kActive) + { + mBoostTargetTemperatureReached = false; + } + + // If a boost command is in progress or in manual mode, find a heating source and "turn it on". + if (mBoostState == BoostStateEnum::kActive || mode == WaterHeaterMode::kModeManual) + { + heatingOp = HeatingOp::TurnHeatingOn; + } + } + else if (mBoostState == BoostStateEnum::kInactive && mode == WaterHeaterMode::kModeOff) + { + // The water temperature is not at the target temperature but there is no boost command in progress and the mode is Off + // so need to ensure the heating is turned off. + ChipLogError(AppServer, "DetermineIfChangingHeatingState turning heating off due to no boost cmd and kModeOff"); + + heatingOp = HeatingOp::TurnHeatingOff; + } + } + else if (mHeatDemand.Raw() != 0) + { + // The water in the tank has reached the target temperature - need to turn the heating off + heatingOp = HeatingOp::TurnHeatingOff; + + // If a boost command is in progress, record that the target temperature has been reached. + mBoostTargetTemperatureReached = (mBoostState == BoostStateEnum::kActive); + } + + return Status::Success; +} + +Status WaterHeaterManagementDelegate::SetWaterHeaterMode(uint8_t modeValue) +{ + VerifyOrReturnError(WaterHeaterMode::Instance() != nullptr, Status::InvalidInState); + + if (!WaterHeaterMode::Instance()->IsSupportedMode(modeValue)) + { + ChipLogError(AppServer, "SetWaterHeaterMode bad mode"); + return Status::ConstraintError; + } + + Status status = WaterHeaterMode::Instance()->UpdateCurrentMode(modeValue); + if (status != Status::Success) + { + ChipLogError(AppServer, "SetWaterHeaterMode updateMode failed 0x%02x", to_underlying(status)); + return status; + } + + return CheckIfHeatNeedsToBeTurnedOnOrOff(); +} diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmInstance.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmInstance.cpp new file mode 100644 index 00000000000000..9d4ad58fefd976 --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/src/WhmInstance.cpp @@ -0,0 +1,34 @@ +/* + * + * 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 + +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::WaterHeaterManagement; + +CHIP_ERROR WaterHeaterManagementInstance::Init() +{ + ChipLogDetail(AppServer, "WaterHeaterManagementInstance::Init()"); + return Instance::Init(); +} + +void WaterHeaterManagementInstance::Shutdown() +{ + Instance::Shutdown(); +} diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmMain.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmMain.cpp new file mode 100644 index 00000000000000..ea59fef2d095f5 --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/src/WhmMain.cpp @@ -0,0 +1,194 @@ +/* + * + * 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 +#include + +#include +#include +#include +#include + +static constexpr int WHM_ENDPOINT = 1; + +using namespace chip; +using namespace chip::app; +using namespace chip::app::DataModel; +using namespace chip::app::Clusters; + +namespace chip { +namespace app { +namespace Clusters { +namespace WaterHeaterManagement { + +static std::unique_ptr gWhmDelegate; +static std::unique_ptr gWhmInstance; + +static std::unique_ptr gWhmManufacturer; + +WhmManufacturer * GetWhmManufacturer() +{ + return gWhmManufacturer.get(); +} + +/* + * @brief Creates a Delegate and Instance for Water Heater Management cluster + * + * The Instance is a container around the Delegate, so + * create the Delegate first, then wrap it in the Instance + * Then call the Instance->Init() to register the attribute and command handlers + */ +CHIP_ERROR WhmInit() +{ + CHIP_ERROR err; + + if (gWhmDelegate || gWhmInstance) + { + ChipLogError(AppServer, "WaterHeaterManager Instance or Delegate already exist."); + return CHIP_ERROR_INCORRECT_STATE; + } + + gWhmDelegate = std::make_unique(WHM_ENDPOINT); + if (!gWhmDelegate) + { + ChipLogError(AppServer, "Failed to allocate memory for WaterHeaterManagementDelegate"); + return CHIP_ERROR_NO_MEMORY; + } + + /* Manufacturer may optionally not support all features, commands & attributes */ + gWhmInstance = std::make_unique( + EndpointId(WHM_ENDPOINT), *gWhmDelegate, BitMask(Feature::kEnergyManagement, Feature::kTankPercent)); + if (!gWhmInstance) + { + ChipLogError(AppServer, "Failed to allocate memory for WaterHeaterManagementInstance"); + gWhmDelegate.reset(); + return CHIP_ERROR_NO_MEMORY; + } + + /* Register Attribute & Command handlers */ + err = gWhmInstance->Init(); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "gWhmInstance->Init failed %s", chip::ErrorStr(err)); + gWhmInstance.reset(); + gWhmDelegate.reset(); + return err; + } + + gWhmDelegate->SetWaterHeaterManagementInstance(*gWhmInstance); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR WhmShutdown() +{ + /* Do this in the order Instance first, then delegate + * Ensure we call the Instance->Shutdown to free attribute & command handlers first + */ + if (gWhmInstance) + { + /* Deregister attribute & command handlers */ + gWhmInstance->Shutdown(); + gWhmInstance.reset(); + } + + if (gWhmDelegate) + { + gWhmDelegate.reset(); + } + + return CHIP_NO_ERROR; +} + +/* + * @brief Creates a WhmManufacturer class to hold the Whm cluster + * + * The Instance is a container around the Delegate, so + * create the Delegate first, then wrap it in the Instance + * Then call the Instance->Init() to register the attribute and command handlers + */ +CHIP_ERROR WhmManufacturerInit() +{ + CHIP_ERROR err; + + if (gWhmManufacturer) + { + ChipLogError(AppServer, "WhmManufacturer already exist."); + return CHIP_ERROR_INCORRECT_STATE; + } + + /* Now create WhmManufacturer */ + gWhmManufacturer = std::make_unique(gWhmInstance.get()); + if (!gWhmManufacturer) + { + ChipLogError(AppServer, "Failed to allocate memory for WhmManufacturer"); + return CHIP_ERROR_NO_MEMORY; + } + + /* Call Manufacturer specific init */ + err = gWhmManufacturer->Init(); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "Init failed on gWhmManufacturer"); + gWhmManufacturer.reset(); + return err; + } + + // Let the WhmDelegate know about the WhmManufacturer object. + gWhmDelegate->SetWhmManufacturer(*gWhmManufacturer); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR WhmManufacturerShutdown() +{ + if (gWhmManufacturer) + { + /* Shutdown the WhmManufacturer */ + gWhmManufacturer->Shutdown(); + gWhmManufacturer.reset(); + } + + return CHIP_NO_ERROR; +} + +void WhmApplicationInit() +{ + if (WhmInit() != CHIP_NO_ERROR) + { + return; + } + + /* Do this last so that the instances for other clusters can be wrapped inside */ + if (WhmManufacturerInit() != CHIP_NO_ERROR) + { + WhmShutdown(); + return; + } +} + +void WhmApplicationShutdown() +{ + /* Shutdown in reverse order that they were created */ + WhmManufacturerShutdown(); +} + +} // namespace WaterHeaterManagement +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp new file mode 100644 index 00000000000000..dd8452a5c816ca --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp @@ -0,0 +1,286 @@ +/* + * + * 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 +#include +#include + +#include +#include +#include + +using namespace chip; +using namespace chip::app::Clusters::WaterHeaterManagement; + +using Protocols::InteractionModel::Status; + +namespace chip { +namespace app { +namespace Clusters { +namespace WaterHeaterManagement { + +CHIP_ERROR WhmManufacturer::Init() +{ + WaterHeaterManagementDelegate * dg = GetWhmManufacturer()->GetWhmDelegate(); + if (dg == nullptr) + { + ChipLogError(AppServer, "WhmDelegate is not initialized"); + return CHIP_ERROR_UNINITIALIZED; + } + + dg->SetHeaterTypes(BitMask(WaterHeaterTypeBitmap::kImmersionElement1)); + dg->SetHeatDemand(BitMask(WaterHeaterDemandBitmap::kImmersionElement1)); + dg->SetEstimatedHeatRequired(10000); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR WhmManufacturer::Shutdown() +{ + return CHIP_NO_ERROR; +} + +BitMask WhmManufacturer::DetermineHeatingSources() +{ + WaterHeaterManagementDelegate * dg = GetWhmManufacturer()->GetWhmDelegate(); + if (dg == nullptr) + { + ChipLogError(AppServer, "WhmDelegate is not initialized"); + 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), + }; + + // 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), + }; + + // 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(); + + uint8_t heaterDemandMask = 0; + for (uint16_t idx = 0; idx < static_cast(sizeof(waterHeaterTypeValues) / sizeof(waterHeaterTypeValues[0])); idx++) + { + // Is this heating source being used? + if (heaterTypes.Raw() & waterHeaterTypeValues[idx]) + { + heaterDemandMask |= waterHeaterDemandValues[idx]; + } + } + + return BitMask(heaterDemandMask); +} + +Status WhmManufacturer::TurnHeatingOn(bool emergencyBoost) +{ + Status status = Status::Success; + + ChipLogProgress(AppServer, "WhmManufacturer::TurnHeatingOn"); + + WaterHeaterManagementDelegate * dg = GetWhmDelegate(); + + if (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)); + } + else + { + dg->SetHeatDemand(BitMask(WaterHeaterDemandBitmap::kImmersionElement1)); + } + + return status; +} + +Status WhmManufacturer::TurnHeatingOff() +{ + Status status = Status::Success; + + ChipLogProgress(AppServer, "WhmManufacturer::TurnHeatingOff"); + + WaterHeaterManagementDelegate * dg = GetWhmDelegate(); + + dg->SetHeatDemand(BitMask(0)); + + return status; +} + +Status WhmManufacturer::BoostCommandStarted(uint32_t duration, Optional oneShot, Optional emergencyBoost, + Optional temporarySetpoint, Optional targetPercentage, + Optional targetReheat) +{ + return Status::Success; +} + +Status WhmManufacturer::BoostCommandCancelled() +{ + return Status::Success; +} + +void WhmManufacturer::BoostCommandFinished() {} + +WaterHeaterManagementDelegate * GetWhmDelegate() +{ + WhmManufacturer * mn = GetWhmManufacturer(); + VerifyOrDieWithMsg(mn != nullptr, AppServer, "WhmManufacturer is null"); + + WaterHeaterManagementDelegate * wg = mn->GetWhmDelegate(); + VerifyOrDieWithMsg(wg != nullptr, AppServer, "WhmDelegate is null"); + + return wg; +} + +void SetTestEventTrigger_BasicInstallationTestEvent() +{ + WaterHeaterManagementDelegate * dg = GetWhmDelegate(); + + // 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->DrawOffHotWater(100, 2000); +} + +void SetTestEventTrigger_BasicInstallationTestEventClear() {} + +void SetTestEventTrigger_WaterTemperature20CTestEvent() +{ + WaterHeaterManagementDelegate * dg = GetWhmDelegate(); + + // Simulate 100% of the water in the tank being at 20C + dg->SetWaterTemperature(2000); +} + +void SetTestEventTrigger_WaterTemperature61CTestEvent() +{ + WaterHeaterManagementDelegate * dg = GetWhmDelegate(); + + // Simulate 100% of the water in the tank being at 61C + dg->SetWaterTemperature(6100); +} + +void SetTestEventTrigger_WaterTemperature66CTestEvent() +{ + WaterHeaterManagementDelegate * dg = GetWhmDelegate(); + + // Simulate 100% of the water in the tank being at 66C + dg->SetWaterTemperature(6600); +} + +void SetTestEventTrigger_ManualModeTestEvent() +{ + WaterHeaterManagementDelegate * dg = GetWhmDelegate(); + + // Simulate the Water Heater Mode being set to MANUAL + Status status = dg->SetWaterHeaterMode(WaterHeaterMode::kModeManual); + if (status != Status::Success) + { + ChipLogError(Zcl, "SetTestEventTrigger_OffModeTestEvent setting mode -> KModeManual failed 0x%02x", to_underlying(status)); + } +} + +void SetTestEventTrigger_OffModeTestEvent() +{ + WaterHeaterManagementDelegate * dg = GetWhmDelegate(); + + // Simulate the Water Heater Mode being set to OFF + Status status = dg->SetWaterHeaterMode(WaterHeaterMode::kModeOff); + if (status != Status::Success) + { + ChipLogError(Zcl, "SetTestEventTrigger_OffModeTestEvent setting mode -> KModeOff failed 0x%02x", to_underlying(status)); + } +} + +void SetTestEventTrigger_DrawOffHotWaterTestEvent() +{ + WaterHeaterManagementDelegate * dg = GetWhmDelegate(); + + // Simulate drawing off 25% of the tank volume of hot water, replaced with water at 20C + dg->DrawOffHotWater(25, 2000); +} + +} // namespace WaterHeaterManagement +} // namespace Clusters +} // namespace app +} // namespace chip + +using namespace chip::app::Clusters::WaterHeaterManagement; + +bool HandleWaterHeaterManagementTestEventTrigger(uint64_t eventTrigger) +{ + WaterHeaterManagementTrigger trigger = static_cast(eventTrigger); + + switch (trigger) + { + case WaterHeaterManagementTrigger::kBasicInstallationTestEvent: + ChipLogProgress(Support, + "[Whm::kBasicInstallationTestEvent] => Simulate installation in a 100L tank full of water at 20C, with a " + "target temperature of 60C, in OFF mode"); + SetTestEventTrigger_BasicInstallationTestEvent(); + break; + case WaterHeaterManagementTrigger::kBasicInstallationTestEventClear: + ChipLogProgress(Support, "[Whm::kBasicInstallationTestEventClear] => End simulation of installation"); + SetTestEventTrigger_BasicInstallationTestEventClear(); + break; + case WaterHeaterManagementTrigger::kWaterTemperature20CTestEvent: + ChipLogProgress(Support, "[Whm::kWaterTemperature20CTestEvent] => Simulate 100%% of the water in the tank being at 20C"); + SetTestEventTrigger_WaterTemperature20CTestEvent(); + break; + case WaterHeaterManagementTrigger::kWaterTemperature61CTestEvent: + ChipLogProgress(Support, "[Whm::kWaterTemperature61CTestEvent] => Simulate 100%% of the water in the tank being at 61C"); + SetTestEventTrigger_WaterTemperature61CTestEvent(); + break; + case WaterHeaterManagementTrigger::kWaterTemperature66CTestEvent: + ChipLogProgress(Support, "[Whm::kWaterTemperature66CTestEvent] => Simulate 100%% of the water in the tank being at 66C"); + SetTestEventTrigger_WaterTemperature66CTestEvent(); + break; + case WaterHeaterManagementTrigger::kManualModeTestEvent: + ChipLogProgress(Support, "[Whm::kManualModeTestEvent] => Simulate the Water Heater Mode being set to MANUAL"); + SetTestEventTrigger_ManualModeTestEvent(); + break; + case WaterHeaterManagementTrigger::kOffModeTestEvent: + ChipLogProgress(Support, "[Whm::kOffModeTestEvent] => Simulate the Water Heater Mode being set to OFF"); + SetTestEventTrigger_OffModeTestEvent(); + break; + case WaterHeaterManagementTrigger::kDrawOffHotWaterTestEvent: + ChipLogProgress(Support, + "[Whm::kDrawOffHotWaterTestEvent] => Simulate drawing off 25%% of the tank volume of hot water, replaced " + "with water at 20C"); + SetTestEventTrigger_DrawOffHotWaterTestEvent(); + break; + default: + return false; + } + + return true; +} diff --git a/examples/all-clusters-app/all-clusters-common/src/water-heater-mode.cpp b/examples/all-clusters-app/all-clusters-common/src/water-heater-mode.cpp old mode 100644 new mode 100755 diff --git a/examples/all-clusters-app/linux/BUILD.gn b/examples/all-clusters-app/linux/BUILD.gn index 3564153a482cca..0bf2e1445028b6 100644 --- a/examples/all-clusters-app/linux/BUILD.gn +++ b/examples/all-clusters-app/linux/BUILD.gn @@ -29,6 +29,10 @@ if (chip_enable_pw_rpc) { source_set("chip-all-clusters-common") { sources = [ + "${chip_root}/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp", + "${chip_root}/examples/all-clusters-app/all-clusters-common/src/WhmInstance.cpp", + "${chip_root}/examples/all-clusters-app/all-clusters-common/src/WhmMain.cpp", + "${chip_root}/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/air-quality-instance.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/binding-handler.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/boolcfg-stub.cpp", diff --git a/examples/all-clusters-app/linux/args.gni b/examples/all-clusters-app/linux/args.gni index d414ad5dedaf5d..92d01ea3358b30 100644 --- a/examples/all-clusters-app/linux/args.gni +++ b/examples/all-clusters-app/linux/args.gni @@ -29,3 +29,4 @@ matter_log_json_payload_decode_full = true matter_log_json_payload_hex = true chip_enable_smoke_co_trigger = true chip_enable_boolean_state_configuration_trigger = true +chip_enable_water_heater_management_trigger = true diff --git a/examples/all-clusters-app/linux/main-common.cpp b/examples/all-clusters-app/linux/main-common.cpp index ddbcaee06b889c..bc547d5416377c 100644 --- a/examples/all-clusters-app/linux/main-common.cpp +++ b/examples/all-clusters-app/linux/main-common.cpp @@ -63,6 +63,8 @@ #include +#include + using namespace chip; using namespace chip::app; using namespace chip::DeviceLayer; @@ -246,6 +248,8 @@ void ApplicationInit() Clusters::ValveConfigurationAndControl::SetDefaultDelegate(chip::EndpointId(1), &sValveDelegate); Clusters::TimeSynchronization::SetDefaultDelegate(&sTimeSyncDelegate); + Clusters::WaterHeaterManagement::WhmApplicationInit(); + SetTagList(/* endpoint= */ 0, Span(gEp0TagList)); SetTagList(/* endpoint= */ 1, Span(gEp1TagList)); SetTagList(/* endpoint= */ 2, Span(gEp2TagList)); @@ -275,6 +279,9 @@ void ApplicationShutdown() Clusters::EnergyEvseMode::Shutdown(); Clusters::WaterHeaterMode::Shutdown(); + Clusters::WaterHeaterManagement::WhmApplicationShutdown(); + Clusters::WaterHeaterMode::Shutdown(); + if (sChipNamedPipeCommands.Stop() != CHIP_NO_ERROR) { ChipLogError(NotSpecified, "Failed to stop CHIP NamedPipeCommands"); diff --git a/examples/all-clusters-app/nrfconnect/README.md b/examples/all-clusters-app/nrfconnect/README.md index 18df16f5e11654..3a58e448d76254 100644 --- a/examples/all-clusters-app/nrfconnect/README.md +++ b/examples/all-clusters-app/nrfconnect/README.md @@ -1,5 +1,21 @@ # Matter nRF Connect All Clusters Example Application +> **Note:** This example is intended only to perform smoke tests of a Matter +> solution integrated with nRF Connect SDK platform. The example quality is not +> production ready and it may contain minor bugs or use not optimal +> configuration. It is not recommended to use this example as a basis for +> creating a market ready product. +> +> For the production ready and optimized Matter samples, see +> [nRF Connect SDK samples](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/samples/matter.html). +> The Matter samples in nRF Connect SDK use various additional software +> components and provide multiple optional features that improve the developer +> and user experience. To read more about it, see +> [Matter support in nRF Connect SDK](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/protocols/matter/index.html#ug-matter) +> page. Using Matter samples from nRF Connect SDK allows you to get a full +> Nordic technical support via [DevZone](https://devzone.nordicsemi.com/) +> portal. + The nRF All Clusters Example Application implements various ZCL clusters populated on three endpoints. You can use this example as a reference for creating your own application. @@ -85,10 +101,10 @@ The example supports building and running on the following devices: | Hardware platform | Build target | Platform image | | ------------------------------------------------------------------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [nRF52840 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK) | `nrf52840dk_nrf52840` |
nRF52840 DKnRF52840 DK
| -| [nRF5340 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF5340-DK) | `nrf5340dk_nrf5340_cpuapp` |
nRF5340 DKnRF5340 DK
| -| [nRF52840 Dongle](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-Dongle) | `nrf52840dongle_nrf52840` |
nRF52840 DonglenRF52840 Dongle
| -| [nRF7002 DK](https://www.nordicsemi.com/Products/Development-hardware/nRF7002-DK) | `nrf7002dk_nrf5340_cpuapp` |
nRF7002 DKnRF7002 DK
| +| [nRF52840 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK) | `nrf52840dk/nrf52840` |
nRF52840 DKnRF52840 DK
| +| [nRF5340 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF5340-DK) | `nrf5340dk/nrf5340/cpuapp` |
nRF5340 DKnRF5340 DK
| +| [nRF52840 Dongle](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-Dongle) | `nrf52840dongle/nrf52840` |
nRF52840 DonglenRF52840 Dongle
| +| [nRF7002 DK](https://www.nordicsemi.com/Products/Development-hardware/nRF7002-DK) | `nrf7002dk/nrf5340/cpuapp` |
nRF7002 DKnRF7002 DK
|
@@ -97,9 +113,9 @@ The example supports building and running on the following devices: The development kits for this sample offer the following IPv6 network support for Matter: -- Matter over Thread is supported for `nrf52840dk_nrf52840` and - `nrf5340dk_nrf5340_cpuapp`. -- Matter over Wi-Fi is supported for `nrf7002dk_nrf5340_cpuapp`. +- Matter over Thread is supported for `nrf52840dk/nrf52840` and + `nrf5340dk/nrf5340/cpuapp`. +- Matter over Wi-Fi is supported for `nrf7002dk/nrf5340/cpuapp`. ## Device UI @@ -248,14 +264,15 @@ Complete the following steps to build the sample: 2. Run the following command to build the example, with _build-target_ replaced with the build target name of the Nordic Semiconductor's kit you own, for - example `nrf52840dk_nrf52840`: + example `nrf52840dk/nrf52840`: - $ west build -b build-target + $ west build -b build-target --sysbuild You only need to specify the build target on the first build. See [Requirements](#requirements) for the build target names of compatible kits. -The output `zephyr.hex` file will be available in the `build/zephyr/` directory. +The output `zephyr.hex` file will be available in the `build/nrfconnect/zephyr/` +directory. ### Removing build artifacts @@ -270,7 +287,7 @@ following command: To build the example with release configuration that disables the diagnostic features like logs and command-line interface, run the following command: - $ west build -b build-target -- -DCONF_FILE=prj_release.conf + $ west build -b build-target --sysbuild -- -DFILE_SUFFIX=release Remember to replace _build-target_ with the build target name of the Nordic Semiconductor's kit you own. @@ -281,9 +298,9 @@ Support for DFU using Matter OTA is disabled by default. To build the example with configuration that supports DFU, run the following command with _build-target_ replaced with the build target name of the Nordic -Semiconductor kit you are using (for example `nrf52840dk_nrf52840`): +Semiconductor kit you are using (for example `nrf52840dk/nrf52840`): - $ west build -b build-target -- -DCONF_FILE=prj_dfu.conf + $ west build -b build-target --sysbuild -- -DFILE_SUFFIX=dfu > **Note**: > @@ -299,7 +316,7 @@ Semiconductor kit you are using (for example `nrf52840dk_nrf52840`): #### Changing bootloader configuration To change the default MCUboot configuration, edit the `prj.conf` file located in -the `child_image/mcuboot` directory. +the `sysbuild/mcuboot` directory. #### Changing flash memory settings @@ -311,8 +328,9 @@ purposes. You can change these settings by defining This example uses this option to define using an external flash. To modify the flash settings of your board (that is, your _build-target_, for -example `nrf52840dk_nrf52840`), edit the `pm_static_dfu.yml` file located in the -`configuration/build-target/` directory. +example `nrf52840dk/nrf52840`), edit the `pm_static_.yml` file +(for example `pm_static_nrf52840dk_nrf52840.yml`), located in the main +application directory.
@@ -324,7 +342,7 @@ using the menuconfig utility. To open the menuconfig utility, run the following command from the example directory: - $ west build -b build-target -t menuconfig + $ west build -b build-target --sysbuild -t menuconfig Remember to replace _build-target_ with the build target name of the Nordic Semiconductor's kit you own. diff --git a/examples/all-clusters-minimal-app/nrfconnect/README.md b/examples/all-clusters-minimal-app/nrfconnect/README.md index a015c7a61ed115..932db10a2f2c89 100644 --- a/examples/all-clusters-minimal-app/nrfconnect/README.md +++ b/examples/all-clusters-minimal-app/nrfconnect/README.md @@ -1,5 +1,21 @@ # Matter nRF Connect All Clusters Example Application +> **Note:** This example is intended only to perform smoke tests of a Matter +> solution integrated with nRF Connect SDK platform. The example quality is not +> production ready and it may contain minor bugs or use not optimal +> configuration. It is not recommended to use this example as a basis for +> creating a market ready product. +> +> For the production ready and optimized Matter samples, see +> [nRF Connect SDK samples](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/samples/matter.html). +> The Matter samples in nRF Connect SDK use various additional software +> components and provide multiple optional features that improve the developer +> and user experience. To read more about it, see +> [Matter support in nRF Connect SDK](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/protocols/matter/index.html#ug-matter) +> page. Using Matter samples from nRF Connect SDK allows you to get a full +> Nordic technical support via [DevZone](https://devzone.nordicsemi.com/) +> portal. + The nRF All Clusters Example Application implements various ZCL clusters populated on three endpoints. You can use this example as a reference for creating your own application. @@ -77,9 +93,9 @@ The example supports building and running on the following devices: | Hardware platform | Build target | Platform image | | ------------------------------------------------------------------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [nRF52840 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK) | `nrf52840dk_nrf52840` |
nRF52840 DKnRF52840 DK
| -| [nRF5340 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF5340-DK) | `nrf5340dk_nrf5340_cpuapp` |
nRF5340 DKnRF5340 DK
| -| [nRF52840 Dongle](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-Dongle) | `nrf52840dongle_nrf52840` |
nRF52840 DonglenRF52840 Dongle
| +| [nRF52840 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK) | `nrf52840dk/nrf52840` |
nRF52840 DKnRF52840 DK
| +| [nRF5340 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF5340-DK) | `nrf5340dk/nrf5340/cpuapp` |
nRF5340 DKnRF5340 DK
| +| [nRF52840 Dongle](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-Dongle) | `nrf52840dongle/nrf52840` |
nRF52840 DonglenRF52840 Dongle
|
@@ -240,14 +256,15 @@ environment: 2. Run the following command to build the example, with _build-target_ replaced with the build target name of the Nordic Semiconductor's kit you own, for - example `nrf52840dk_nrf52840`: + example `nrf52840dk/nrf52840`: - $ west build -b build-target + $ west build -b build-target --sysbuild You only need to specify the build target on the first build. See [Requirements](#requirements) for the build target names of compatible kits. -The output `zephyr.hex` file will be available in the `build/zephyr/` directory. +The output `zephyr.hex` file will be available in the `build/nrfconnect/zephyr/` +directory. ### Removing build artifacts @@ -262,7 +279,7 @@ following command: To build the example with release configuration that disables the diagnostic features like logs and command-line interface, run the following command: - $ west build -b build-target -- -DCONF_FILE=prj_release.conf + $ west build -b build-target --sysbuild -- -DFILE_SUFFIX=release Remember to replace _build-target_ with the build target name of the Nordic Semiconductor's kit you own. @@ -273,9 +290,9 @@ Support for DFU using Matter OTA is disabled by default. To build the example with configuration that supports DFU, run the following command with _build-target_ replaced with the build target name of the Nordic -Semiconductor kit you are using (for example `nrf52840dk_nrf52840`): +Semiconductor kit you are using (for example `nrf52840dk/nrf52840`): - $ west build -b build-target -- -DCONF_FILE=prj_dfu.conf + $ west build -b build-target --sysbuild -- -DFILE_SUFFIX=dfu > **Note**: > @@ -291,7 +308,7 @@ Semiconductor kit you are using (for example `nrf52840dk_nrf52840`): #### Changing bootloader configuration To change the default MCUboot configuration, edit the `prj.conf` file located in -the `child_image/mcuboot` directory. +the `sysbuild/mcuboot` directory. #### Changing flash memory settings @@ -303,8 +320,9 @@ purposes. You can change these settings by defining This example uses this option to define using an external flash. To modify the flash settings of your board (that is, your _build-target_, for -example `nrf52840dk_nrf52840`), edit the `pm_static_dfu.yml` file located in the -`configuration/build-target/` directory. +example `nrf52840dk/nrf52840`), edit the `pm_static_.yml` file +(for example `pm_static_nrf52840dk_nrf52840.yml`), located in the main +application directory.
@@ -316,7 +334,7 @@ using the menuconfig utility. To open the menuconfig utility, run the following command from the example directory: - $ west build -b build-target -t menuconfig + $ west build -b build-target --sysbuild -t menuconfig Remember to replace _build-target_ with the build target name of the Nordic Semiconductor's kit you own. diff --git a/examples/chef/nrfconnect/README.md b/examples/chef/nrfconnect/README.md index 8e17340c92069b..ca59a609946baf 100644 --- a/examples/chef/nrfconnect/README.md +++ b/examples/chef/nrfconnect/README.md @@ -1,5 +1,21 @@ # CHIP nRF Connect SDK Shell Application +> **Note:** This example is intended only to perform smoke tests of a Matter +> solution integrated with nRF Connect SDK platform. The example quality is not +> production ready and it may contain minor bugs or use not optimal +> configuration. It is not recommended to use this example as a basis for +> creating a market ready product. +> +> For the production ready and optimized Matter samples, see +> [nRF Connect SDK samples](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/samples/matter.html). +> The Matter samples in nRF Connect SDK use various additional software +> components and provide multiple optional features that improve the developer +> and user experience. To read more about it, see +> [Matter support in nRF Connect SDK](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/protocols/matter/index.html#ug-matter) +> page. Using Matter samples from nRF Connect SDK allows you to get a full +> Nordic technical support via [DevZone](https://devzone.nordicsemi.com/) +> portal. + A [chip-shell](../README.md) project for the Nordic nRF52840 and nRF5340 development kits, built using the nRF Connect SDK. diff --git a/examples/chip-tool/BUILD.gn b/examples/chip-tool/BUILD.gn index b5917f3bfaca62..94e1eebbde7508 100644 --- a/examples/chip-tool/BUILD.gn +++ b/examples/chip-tool/BUILD.gn @@ -109,6 +109,7 @@ static_library("chip-tool-utils") { "${chip_root}/src/app/tests/suites/commands/interaction_model", "${chip_root}/src/controller/data_model", "${chip_root}/src/credentials:file_attestation_trust_store", + "${chip_root}/src/credentials:test_dac_revocation_delegate", "${chip_root}/src/lib", "${chip_root}/src/lib/core:types", "${chip_root}/src/lib/support/jsontlv", diff --git a/examples/chip-tool/commands/common/CHIPCommand.cpp b/examples/chip-tool/commands/common/CHIPCommand.cpp index 7e871f8e781e14..1c3df517bd89d0 100644 --- a/examples/chip-tool/commands/common/CHIPCommand.cpp +++ b/examples/chip-tool/commands/common/CHIPCommand.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -48,7 +49,9 @@ constexpr chip::FabricId kIdentityOtherFabricId = 4; constexpr char kPAATrustStorePathVariable[] = "CHIPTOOL_PAA_TRUST_STORE_PATH"; constexpr char kCDTrustStorePathVariable[] = "CHIPTOOL_CD_TRUST_STORE_PATH"; -const chip::Credentials::AttestationTrustStore * CHIPCommand::sTrustStore = nullptr; +const chip::Credentials::AttestationTrustStore * CHIPCommand::sTrustStore = nullptr; +chip::Credentials::DeviceAttestationRevocationDelegate * CHIPCommand::sRevocationDelegate = nullptr; + chip::Credentials::GroupDataProviderImpl CHIPCommand::sGroupDataProvider{ kMaxGroupsPerFabric, kMaxGroupKeysPerFabric }; // All fabrics share the same ICD client storage. chip::app::DefaultICDClientStorage CHIPCommand::sICDClientStorage; @@ -87,6 +90,20 @@ CHIP_ERROR GetAttestationTrustStore(const char * paaTrustStorePath, const chip:: return CHIP_NO_ERROR; } +CHIP_ERROR GetAttestationRevocationDelegate(const char * revocationSetPath, + chip::Credentials::DeviceAttestationRevocationDelegate ** revocationDelegate) +{ + if (revocationSetPath == nullptr) + { + return CHIP_NO_ERROR; + } + + static chip::Credentials::TestDACRevocationDelegateImpl testDacRevocationDelegate; + ReturnErrorOnFailure(testDacRevocationDelegate.SetDeviceAttestationRevocationSetPath(revocationSetPath)); + *revocationDelegate = &testDacRevocationDelegate; + return CHIP_NO_ERROR; +} + } // namespace CHIP_ERROR CHIPCommand::MaybeSetUpStack() @@ -151,6 +168,8 @@ CHIP_ERROR CHIPCommand::MaybeSetUpStack() ReturnErrorOnFailure(GetAttestationTrustStore(mPaaTrustStorePath.ValueOr(nullptr), &sTrustStore)); + ReturnLogErrorOnFailure(GetAttestationRevocationDelegate(mDacRevocationSetPath.ValueOr(nullptr), &sRevocationDelegate)); + auto engine = chip::app::InteractionModelEngine::GetInstance(); VerifyOrReturnError(engine != nullptr, CHIP_ERROR_INCORRECT_STATE); ReturnLogErrorOnFailure(ChipToolCheckInDelegate()->Init(&sICDClientStorage, engine)); @@ -450,7 +469,7 @@ CHIP_ERROR CHIPCommand::InitializeCommissioner(CommissionerIdentity & identity, std::unique_ptr commissioner = std::make_unique(); chip::Controller::SetupParams commissionerParams; - ReturnLogErrorOnFailure(mCredIssuerCmds->SetupDeviceAttestation(commissionerParams, sTrustStore)); + ReturnLogErrorOnFailure(mCredIssuerCmds->SetupDeviceAttestation(commissionerParams, sTrustStore, sRevocationDelegate)); chip::Crypto::P256Keypair ephemeralKey; diff --git a/examples/chip-tool/commands/common/CHIPCommand.h b/examples/chip-tool/commands/common/CHIPCommand.h index 50ab851d284502..b48455ebed6821 100644 --- a/examples/chip-tool/commands/common/CHIPCommand.h +++ b/examples/chip-tool/commands/common/CHIPCommand.h @@ -86,6 +86,10 @@ class CHIPCommand : public Command AddArgument("only-allow-trusted-cd-keys", 0, 1, &mOnlyAllowTrustedCdKeys, "Only allow trusted CD verifying keys (disallow test keys). If not provided or 0 (\"false\"), untrusted CD " "verifying keys are allowed. If 1 (\"true\"), test keys are disallowed."); + AddArgument("dac-revocation-set-path", &mDacRevocationSetPath, + "Path to JSON file containing the device attestation revocation set. " + "This argument caches the path to the revocation set. Once set, this will be used by all commands in " + "interactive mode."); #if CHIP_CONFIG_TRANSPORT_TRACE_ENABLED AddArgument("trace_file", &mTraceFile); AddArgument("trace_log", 0, 1, &mTraceLog); @@ -222,11 +226,16 @@ class CHIPCommand : public Command chip::Optional mCDTrustStorePath; chip::Optional mUseMaxSizedCerts; chip::Optional mOnlyAllowTrustedCdKeys; + chip::Optional mDacRevocationSetPath; // Cached trust store so commands other than the original startup command // can spin up commissioners as needed. static const chip::Credentials::AttestationTrustStore * sTrustStore; + // Cached DAC revocation delegate, this can be set using "--dac-revocation-set-path" argument + // Once set this will be used by all commands. + static chip::Credentials::DeviceAttestationRevocationDelegate * sRevocationDelegate; + static void RunQueuedCommand(intptr_t commandArg); typedef decltype(RunQueuedCommand) MatterWorkCallback; static void RunCommandCleanup(intptr_t commandArg); diff --git a/examples/chip-tool/commands/common/CredentialIssuerCommands.h b/examples/chip-tool/commands/common/CredentialIssuerCommands.h index fd096b31835723..f8e225afec4c5e 100644 --- a/examples/chip-tool/commands/common/CredentialIssuerCommands.h +++ b/examples/chip-tool/commands/common/CredentialIssuerCommands.h @@ -57,10 +57,13 @@ class CredentialIssuerCommands * Verifier. * @param[in] trustStore A pointer to the PAA trust store to use to find valid PAA roots. * + * @param[in] revocationDelegate A pointer to the Device Attestation Revocation Delegate for checking revoked DACs and PAIs. + * * @return CHIP_ERROR CHIP_NO_ERROR on success, or corresponding error code. */ virtual CHIP_ERROR SetupDeviceAttestation(chip::Controller::SetupParams & setupParams, - const chip::Credentials::AttestationTrustStore * trustStore) = 0; + const chip::Credentials::AttestationTrustStore * trustStore, + chip::Credentials::DeviceAttestationRevocationDelegate * revocationDelegate) = 0; /** * @brief Add a list of additional non-default CD verifying keys (by certificate) diff --git a/examples/chip-tool/commands/example/ExampleCredentialIssuerCommands.h b/examples/chip-tool/commands/example/ExampleCredentialIssuerCommands.h index a23b45eae10627..495ae8d7a544d6 100644 --- a/examples/chip-tool/commands/example/ExampleCredentialIssuerCommands.h +++ b/examples/chip-tool/commands/example/ExampleCredentialIssuerCommands.h @@ -34,16 +34,18 @@ class ExampleCredentialIssuerCommands : public CredentialIssuerCommands return mOpCredsIssuer.Initialize(storage); } CHIP_ERROR SetupDeviceAttestation(chip::Controller::SetupParams & setupParams, - const chip::Credentials::AttestationTrustStore * trustStore) override + const chip::Credentials::AttestationTrustStore * trustStore, + chip::Credentials::DeviceAttestationRevocationDelegate * revocationDelegate) override { chip::Credentials::SetDeviceAttestationCredentialsProvider(chip::Credentials::Examples::GetExampleDACProvider()); - mDacVerifier = chip::Credentials::GetDefaultDACVerifier(trustStore); + mDacVerifier = chip::Credentials::GetDefaultDACVerifier(trustStore, revocationDelegate); setupParams.deviceAttestationVerifier = mDacVerifier; mDacVerifier->EnableCdTestKeySupport(mAllowTestCdSigningKey); return CHIP_NO_ERROR; } + chip::Controller::OperationalCredentialsDelegate * GetCredentialIssuer() override { return &mOpCredsIssuer; } void SetCredentialIssuerCATValues(chip::CATValues cats) override { mOpCredsIssuer.SetCATValuesForNextNOCRequest(cats); } CHIP_ERROR GenerateControllerNOCChain(chip::NodeId nodeId, chip::FabricId fabricId, const chip::CATValues & cats, diff --git a/examples/light-switch-app/nrfconnect/README.md b/examples/light-switch-app/nrfconnect/README.md index ee78d91c2f7bd8..943759735f2bc2 100644 --- a/examples/light-switch-app/nrfconnect/README.md +++ b/examples/light-switch-app/nrfconnect/README.md @@ -1,5 +1,21 @@ # Matter nRF Connect Light Switch Example Application +> **Note:** This example is intended only to perform smoke tests of a Matter +> solution integrated with nRF Connect SDK platform. The example quality is not +> production ready and it may contain minor bugs or use not optimal +> configuration. It is not recommended to use this example as a basis for +> creating a market ready product. +> +> For the production ready and optimized Matter samples, see +> [nRF Connect SDK samples](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/samples/matter.html). +> The Matter samples in nRF Connect SDK use various additional software +> components and provide multiple optional features that improve the developer +> and user experience. To read more about it, see +> [Matter support in nRF Connect SDK](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/protocols/matter/index.html#ug-matter) +> page. Using Matter samples from nRF Connect SDK allows you to get a full +> Nordic technical support via [DevZone](https://devzone.nordicsemi.com/) +> portal. + The nRF Connect Light Switch Example demonstrates how to remotely control a lighting devices such as light bulbs or LEDs. The application should be used together with the @@ -199,18 +215,18 @@ The example supports building and running on the following devices: | Hardware platform | Build target | Platform image | | ----------------------------------------------------------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | -| [nRF52840 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK) | `nrf52840dk_nrf52840` |
nRF52840 DKnRF52840 DK
| -| [nRF5340 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF5340-DK) | `nrf5340dk_nrf5340_cpuapp` |
nRF5340 DKnRF5340 DK
| -| [nRF7002 DK](https://www.nordicsemi.com/Products/Development-hardware/nRF7002-DK) | `nrf7002dk_nrf5340_cpuapp` |
nRF7002 DKnRF7002 DK
| +| [nRF52840 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK) | `nrf52840dk/nrf52840` |
nRF52840 DKnRF52840 DK
| +| [nRF5340 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF5340-DK) | `nrf5340dk/nrf5340/cpuapp` |
nRF5340 DKnRF5340 DK
| +| [nRF7002 DK](https://www.nordicsemi.com/Products/Development-hardware/nRF7002-DK) | `nrf7002dk/nrf5340/cpuapp` |
nRF7002 DKnRF7002 DK
| ### IPv6 network support The development kits for this sample offer the following IPv6 network support for Matter: -- Matter over Thread is supported for `nrf52840dk_nrf52840` and - `nrf5340dk_nrf5340_cpuapp`. -- Matter over Wi-Fi is supported for `nrf7002dk_nrf5340_cpuapp`. +- Matter over Thread is supported for `nrf52840dk/nrf52840` and + `nrf5340dk/nrf5340/cpuapp`. +- Matter over Wi-Fi is supported for `nrf7002dk/nrf5340/cpuapp`. ### Additional requirements for testing @@ -341,9 +357,9 @@ connection to Nordic Semiconductor's kit. To enable the Matter CLI, you must compile the Light Switch Example application with the additional option **-DCONFIG_CHIP_LIB_SHELL=y**. Run the following command with _build-target_ replaced with the build target name of Nordic -Semiconductor's kit you are using (for example, `nrf52840dk_nrf52840`): +Semiconductor's kit you are using (for example, `nrf52840dk/nrf52840`): - west build -b build-target -- -DCONFIG_CHIP_LIB_SHELL=y + west build -b build-target --sysbuild -- -DCONFIG_CHIP_LIB_SHELL=y You can use the following commands to control a device that is programmed with the Light Switch Example application by using the Matter CLI: @@ -441,14 +457,15 @@ Complete the following steps to build the sample: 2. Run the following command to build the example, with _build-target_ replaced with the build target name of the Nordic Semiconductor's kit you own, for - example `nrf52840dk_nrf52840`: + example `nrf52840dk/nrf52840`: - $ west build -b build-target + $ west build -b build-target --sysbuild You only need to specify the build target on the first build. See [Requirements](#requirements) for the build target names of compatible kits. -The output `zephyr.hex` file will be available in the `build/zephyr/` directory. +The output `zephyr.hex` file will be available in the `build/nrfconnect/zephyr/` +directory. ### Removing build artifacts @@ -463,7 +480,7 @@ following command: To build the example with release configuration that disables the diagnostic features like logs and command-line interface, run the following command: - $ west build -b build-target -- -DCONF_FILE=prj_release.conf + $ west build -b build-target --sysbuild -- -DFILE_SUFFIX=release Remember to replace _build-target_ with the build target name of the Nordic Semiconductor's kit you own. @@ -474,15 +491,9 @@ Support for DFU using Matter OTA is enabled by default. To enable DFU over Bluetooth LE, run the following command with _build-target_ replaced with the build target name of the Nordic Semiconductor kit you are -using (for example `nrf52840dk_nrf52840`): - - $ west build -b build-target -- -DCONFIG_CHIP_DFU_OVER_BT_SMP=y - -To completely disable support for both DFU methods, run the following command -with _build-target_ replaced with the build target name of the Nordic -Semiconductor kit you are using (for example `nrf52840dk_nrf52840`): +using (for example `nrf52840dk/nrf52840`): - $ west build -b build-target -- -DCONF_FILE=prj_no_dfu.conf + $ west build -b build-target --sysbuild -- -DCONFIG_CHIP_DFU_OVER_BT_SMP=y > **Note**: > @@ -496,7 +507,7 @@ Semiconductor kit you are using (for example `nrf52840dk_nrf52840`): #### Changing bootloader configuration To change the default MCUboot configuration, edit the `prj.conf` file located in -the `child_image/mcuboot` directory. +the `sysbuild/mcuboot` directory. Make sure to keep the configuration consistent with changes made to the application configuration. This is necessary for the configuration to work, as @@ -513,8 +524,9 @@ purposes. You can change these settings by defining This example uses this option to define using an external flash. To modify the flash settings of your board (that is, your _build-target_, for -example `nrf52840dk_nrf52840`), edit the `pm_static_dfu.yml` file located in the -`configuration/build-target/` directory. +example `nrf52840dk/nrf52840`), edit the `pm_static_.yml` file +(for example `pm_static_nrf52840dk_nrf52840.yml`), located in the main +application directory.
@@ -526,7 +538,7 @@ using the menuconfig utility. To open the menuconfig utility, run the following command from the example directory: - $ west build -b build-target -t menuconfig + $ west build -b build-target --sysbuild -t menuconfig Remember to replace _build-target_ with the build target name of the Nordic Semiconductor's kit you own. @@ -556,9 +568,6 @@ depending on the selected board: command-line shell. - release -- Release version of the application - can be used to enable only the necessary application functionalities to optimize its performance. -- no_dfu -- Debug version of the application without Device Firmware Upgrade - feature support - can be used only for the nRF52840 DK and nRF5340 DK, as - those platforms have DFU enabled by default. For more information, see the [Configuring nRF Connect SDK examples](../../../docs/guides/nrfconnect_examples_configuration.md) diff --git a/examples/lighting-app/nrfconnect/README.md b/examples/lighting-app/nrfconnect/README.md index b5ef3d7024a0f6..3ba3c74f56d70f 100644 --- a/examples/lighting-app/nrfconnect/README.md +++ b/examples/lighting-app/nrfconnect/README.md @@ -1,5 +1,21 @@ # Matter nRF Connect Lighting Example Application +> **Note:** This example is intended only to perform smoke tests of a Matter +> solution integrated with nRF Connect SDK platform. The example quality is not +> production ready and it may contain minor bugs or use not optimal +> configuration. It is not recommended to use this example as a basis for +> creating a market ready product. +> +> For the production ready and optimized Matter samples, see +> [nRF Connect SDK samples](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/samples/matter.html). +> The Matter samples in nRF Connect SDK use various additional software +> components and provide multiple optional features that improve the developer +> and user experience. To read more about it, see +> [Matter support in nRF Connect SDK](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/protocols/matter/index.html#ug-matter) +> page. Using Matter samples from nRF Connect SDK allows you to get a full +> Nordic technical support via [DevZone](https://devzone.nordicsemi.com/) +> portal. + The nRF Connect Lighting Example demonstrates how to remotely control a white dimmable light bulb. It uses buttons to test changing the lighting and device states and LEDs to show the state of these changes. You can use this example as @@ -153,10 +169,10 @@ The example supports building and running on the following devices: | Hardware platform | Build target | Platform image | | ------------------------------------------------------------------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [nRF52840 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK) | `nrf52840dk_nrf52840` |
nRF52840 DKnRF52840 DK
| -| [nRF5340 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF5340-DK) | `nrf5340dk_nrf5340_cpuapp` |
nRF5340 DKnRF5340 DK
| -| [nRF52840 Dongle](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-Dongle) | `nrf52840dongle_nrf52840` |
nRF52840 DonglenRF52840 Dongle
| -| [nRF7002 DK](https://www.nordicsemi.com/Products/Development-hardware/nRF7002-DK) | `nrf7002dk_nrf5340_cpuapp` |
nRF7002 DKnRF7002 DK
| +| [nRF52840 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK) | `nrf52840dk/nrf52840` |
nRF52840 DKnRF52840 DK
| +| [nRF5340 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF5340-DK) | `nrf5340dk/nrf5340/cpuapp` |
nRF5340 DKnRF5340 DK
| +| [nRF52840 Dongle](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-Dongle) | `nrf52840dongle/nrf52840` |
nRF52840 DonglenRF52840 Dongle
| +| [nRF7002 DK](https://www.nordicsemi.com/Products/Development-hardware/nRF7002-DK) | `nrf7002dk/nrf5340/cpuapp` |
nRF7002 DKnRF7002 DK
|
@@ -165,9 +181,9 @@ The example supports building and running on the following devices: The development kits for this sample offer the following IPv6 network support for Matter: -- Matter over Thread is supported for `nrf52840dk_nrf52840` and - `nrf5340dk_nrf5340_cpuapp`. -- Matter over Wi-Fi is supported for `nrf7002dk_nrf5340_cpuapp`. +- Matter over Thread is supported for `nrf52840dk/nrf52840` and + `nrf5340dk/nrf5340/cpuapp`. +- Matter over Wi-Fi is supported for `nrf7002dk/nrf5340/cpuapp`. ## Device UI @@ -339,16 +355,17 @@ Complete the following steps to build the sample: 2. Run the following command to build the example, with _build-target_ replaced with the build target name of the Nordic Semiconductor's kit you own, for - example `nrf52840dk_nrf52840`: + example `nrf52840dk/nrf52840`: ``` - $ west build -b build-target + $ west build -b build-target --sysbuild ``` You only need to specify the build target on the first build. See [Requirements](#requirements) for the build target names of compatible kits. -The output `zephyr.hex` file will be available in the `build/zephyr/` directory. +The output `zephyr.hex` file will be available in the `build/nrfconnect/zephyr/` +directory. ### Removing build artifacts @@ -366,7 +383,7 @@ To build the example with release configuration that disables the diagnostic features like logs and command-line interface, run the following command: ``` - $ west build -b build-target -- -DCONF_FILE=prj_release.conf + $ west build -b build-target --sysbuild -- -DFILE_SUFFIX=release ``` Remember to replace _build-target_ with the build target name of the Nordic @@ -381,7 +398,7 @@ command with _build-target_ replaced with the build target name of the Nordic Semiconductor's kit you own: ``` - $ west build -b build-target -- -DOVERLAY_CONFIG=rpc.overlay + $ west build -b build-target --sysbuild -- -DOVERLAY_CONFIG=rpc.overlay ``` ### Building with Device Firmware Upgrade support @@ -390,18 +407,10 @@ Support for DFU using Matter OTA is enabled by default. To enable DFU over Bluetooth LE, run the following command with _build-target_ replaced with the build target name of the Nordic Semiconductor kit you are -using (for example `nrf52840dk_nrf52840`): - - ``` - $ west build -b build-target -- -DCONFIG_CHIP_DFU_OVER_BT_SMP=y - ``` - -To completely disable support for both DFU methods, run the following command -with _build-target_ replaced with the build target name of the Nordic -Semiconductor kit you are using (for example `nrf52840dk_nrf52840`): +using (for example `nrf52840dk/nrf52840`): ``` - $ west build -b build-target -- -DCONF_FILE=prj_no_dfu.conf + $ west build -b build-target --sysbuild -- -DCONFIG_CHIP_DFU_OVER_BT_SMP=y ``` > **Note**: @@ -419,7 +428,7 @@ Semiconductor kit you are using (for example `nrf52840dk_nrf52840`): #### Changing bootloader configuration To change the default MCUboot configuration, edit the `prj.conf` file located in -the `child_image/mcuboot` directory. +the `sysbuild/mcuboot` directory. Make sure to keep the configuration consistent with changes made to the application configuration. This is necessary for the configuration to work, as @@ -436,8 +445,9 @@ purposes. You can change these settings by defining This example uses this option to define using an external flash. To modify the flash settings of your board (that is, your _build-target_, for -example `nrf52840dk_nrf52840`), edit the `pm_static_dfu.yml` file located in the -`configuration/build-target/` directory. +example `nrf52840dk/nrf52840`), edit the `pm_static_.yml` file +(for example `pm_static_nrf52840dk_nrf52840.yml`), located in the main +application directory.
@@ -450,7 +460,7 @@ To open the menuconfig utility, run the following command from the example directory: ``` - $ west build -b build-target -t menuconfig + $ west build -b build-target --sysbuild -t menuconfig ``` Remember to replace _build-target_ with the build target name of the Nordic @@ -484,9 +494,6 @@ depending on the selected board: the necessary application functionalities to optimize its performance. It can be used only for the nRF52840 DK and nRF5340 DK, as those platforms have DFU enabled by default. -- no_dfu -- Debug version of the application without Device Firmware Upgrade - feature support - can be used for the nRF52840 DK, nRF5340 DK and nRF52840 - Dongle. For more information, see the [Configuring nRF Connect SDK examples](../../../docs/guides/nrfconnect_examples_configuration.md) diff --git a/examples/lit-icd-app/nrfconnect/README.md b/examples/lit-icd-app/nrfconnect/README.md index 704950fdcac63e..1ff58274962fd9 100644 --- a/examples/lit-icd-app/nrfconnect/README.md +++ b/examples/lit-icd-app/nrfconnect/README.md @@ -1,5 +1,21 @@ # Matter nRF Connect LIT ICD Example Application +> **Note:** This example is intended only to perform smoke tests of a Matter +> solution integrated with nRF Connect SDK platform. The example quality is not +> production ready and it may contain minor bugs or use not optimal +> configuration. It is not recommended to use this example as a basis for +> creating a market ready product. +> +> For the production ready and optimized Matter samples, see +> [nRF Connect SDK samples](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/samples/matter.html). +> The Matter samples in nRF Connect SDK use various additional software +> components and provide multiple optional features that improve the developer +> and user experience. To read more about it, see +> [Matter support in nRF Connect SDK](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/protocols/matter/index.html#ug-matter) +> page. Using Matter samples from nRF Connect SDK allows you to get a full +> Nordic technical support via [DevZone](https://devzone.nordicsemi.com/) +> portal. + The nRF Connect LIT ICD Example allows to test the device that utilizes Long Idle Time feature from the Intermittently Connected Device Management cluster. It uses buttons to change the device states and LEDs to show the state of these @@ -115,8 +131,8 @@ The example supports building and running on the following devices: | Hardware platform | Build target | Platform image | | ----------------------------------------------------------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | -| [nRF52840 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK) | `nrf52840dk_nrf52840` |
nRF52840 DKnRF52840 DK
| -| [nRF5340 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF5340-DK) | `nrf5340dk_nrf5340_cpuapp` |
nRF5340 DKnRF5340 DK
| +| [nRF52840 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK) | `nrf52840dk/nrf52840` |
nRF52840 DKnRF52840 DK
| +| [nRF5340 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF5340-DK) | `nrf5340dk/nrf5340/cpuapp` |
nRF5340 DKnRF5340 DK
|
@@ -248,14 +264,15 @@ Complete the following steps to build the sample: 2. Run the following command to build the example, with _build-target_ replaced with the build target name of the Nordic Semiconductor's kit you own, for - example `nrf52840dk_nrf52840`: + example `nrf52840dk/nrf52840`: - $ west build -b build-target + $ west build -b build-target --sysbuild You only need to specify the build target on the first build. See [Requirements](#requirements) for the build target names of compatible kits. -The output `zephyr.hex` file will be available in the `build/zephyr/` directory. +The output `zephyr.hex` file will be available in the `build/nrfconnect/zephyr/` +directory. ### Removing build artifacts @@ -270,7 +287,7 @@ following command: To build the example with release configuration that disables the diagnostic features like logs and command-line interface, run the following command: - $ west build -b build-target -- -DCONF_FILE=prj_release.conf + $ west build -b build-target --sysbuild -- -DFILE_SUFFIX=release Remember to replace _build-target_ with the build target name of the Nordic Semiconductor's kit you own. @@ -279,12 +296,6 @@ Semiconductor's kit you own. Support for DFU using Matter OTA is enabled by default. -To completely disable support for DFU, run the following command with -_build-target_ replaced with the build target name of the Nordic Semiconductor -kit you are using (for example `nrf52840dk_nrf52840`): - - $ west build -b build-target -- -DCONF_FILE=prj_no_dfu.conf - > **Note**: > > There are two types of Device Firmware Upgrade modes: single-image DFU and @@ -297,7 +308,7 @@ kit you are using (for example `nrf52840dk_nrf52840`): #### Changing bootloader configuration To change the default MCUboot configuration, edit the `prj.conf` file located in -the `child_image/mcuboot` directory. +the `sysbuild/mcuboot` directory. Make sure to keep the configuration consistent with changes made to the application configuration. This is necessary for the configuration to work, as @@ -314,8 +325,9 @@ purposes. You can change these settings by defining This example uses this option to define using an external flash. To modify the flash settings of your board (that is, your _build-target_, for -example `nrf52840dk_nrf52840`), edit the `pm_static_dfu.yml` file located in the -`configuration/build-target/` directory. +example `nrf52840dk/nrf52840`), edit the `pm_static_.yml` file +(for example `pm_static_nrf52840dk_nrf52840.yml`), located in the main +application directory.
@@ -327,7 +339,7 @@ using the menuconfig utility. To open the menuconfig utility, run the following command from the example directory: - $ west build -b build-target -t menuconfig + $ west build -b build-target --sysbuild -t menuconfig Remember to replace _build-target_ with the build target name of the Nordic Semiconductor's kit you own. @@ -357,8 +369,6 @@ depending on the selected board: command-line shell. - release -- Release version of the application - can be used to enable only the necessary application functionalities to optimize its performance. -- no_dfu -- Debug version of the application without Device Firmware Upgrade - feature support. For more information, see the [Configuring nRF Connect SDK examples](../../../docs/guides/nrfconnect_examples_configuration.md) diff --git a/examples/lock-app/nrfconnect/README.md b/examples/lock-app/nrfconnect/README.md index 654f6988c8056f..14385357bc56df 100644 --- a/examples/lock-app/nrfconnect/README.md +++ b/examples/lock-app/nrfconnect/README.md @@ -1,5 +1,21 @@ # Matter nRF Connect Lock Example Application +> **Note:** This example is intended only to perform smoke tests of a Matter +> solution integrated with nRF Connect SDK platform. The example quality is not +> production ready and it may contain minor bugs or use not optimal +> configuration. It is not recommended to use this example as a basis for +> creating a market ready product. +> +> For the production ready and optimized Matter samples, see +> [nRF Connect SDK samples](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/samples/matter.html). +> The Matter samples in nRF Connect SDK use various additional software +> components and provide multiple optional features that improve the developer +> and user experience. To read more about it, see +> [Matter support in nRF Connect SDK](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/protocols/matter/index.html#ug-matter) +> page. Using Matter samples from nRF Connect SDK allows you to get a full +> Nordic technical support via [DevZone](https://devzone.nordicsemi.com/) +> portal. + The nRF Connect Lock Example demonstrates how to remotely control a door lock device with one basic bolt. It uses buttons to test changing the lock and device states and LEDs to show the state of these changes. You can use this example as @@ -148,9 +164,9 @@ The example supports building and running on the following devices: | Hardware platform | Build target | Platform image | | ----------------------------------------------------------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | -| [nRF52840 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK) | `nrf52840dk_nrf52840` |
nRF52840 DKnRF52840 DK
| -| [nRF5340 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF5340-DK) | `nrf5340dk_nrf5340_cpuapp` |
nRF5340 DKnRF5340 DK
| -| [nRF7002 DK](https://www.nordicsemi.com/Products/Development-hardware/nRF7002-DK) | `nrf7002dk_nrf5340_cpuapp` |
nRF7002 DKnRF7002 DK
| +| [nRF52840 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK) | `nrf52840dk/nrf52840` |
nRF52840 DKnRF52840 DK
| +| [nRF5340 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF5340-DK) | `nrf5340dk/nrf5340/cpuapp` |
nRF5340 DKnRF5340 DK
| +| [nRF7002 DK](https://www.nordicsemi.com/Products/Development-hardware/nRF7002-DK) | `nrf7002dk/nrf5340/cpuapp` |
nRF7002 DKnRF7002 DK
|
@@ -159,9 +175,9 @@ The example supports building and running on the following devices: The development kits for this sample offer the following IPv6 network support for Matter: -- Matter over Thread is supported for `nrf52840dk_nrf52840` and - `nrf5340dk_nrf5340_cpuapp`. -- Matter over Wi-Fi is supported for `nrf7002dk_nrf5340_cpuapp`. +- Matter over Thread is supported for `nrf52840dk/nrf52840` and + `nrf5340dk/nrf5340/cpuapp`. +- Matter over Wi-Fi is supported for `nrf7002dk/nrf5340/cpuapp`. ## Device UI @@ -325,14 +341,15 @@ Complete the following steps to build the sample: 2. Run the following command to build the example, with _build-target_ replaced with the build target name of the Nordic Semiconductor's kit you own, for - example `nrf52840dk_nrf52840`: + example `nrf52840dk/nrf52840`: - $ west build -b build-target + $ west build -b build-target --sysbuild You only need to specify the build target on the first build. See [Requirements](#requirements) for the build target names of compatible kits. -The output `zephyr.hex` file will be available in the `build/zephyr/` directory. +The output `zephyr.hex` file will be available in the `build/nrfconnect/zephyr/` +directory. ### Removing build artifacts @@ -347,7 +364,7 @@ following command: To build the example with release configuration that disables the diagnostic features like logs and command-line interface, run the following command: - $ west build -b build-target -- -DCONF_FILE=prj_release.conf + $ west build -b build-target --sysbuild -- -DFILE_SUFFIX=release Remember to replace _build-target_ with the build target name of the Nordic Semiconductor's kit you own. @@ -358,15 +375,9 @@ Support for DFU using Matter OTA is enabled by default. To enable DFU over Bluetooth LE, run the following command with _build-target_ replaced with the build target name of the Nordic Semiconductor kit you are -using (for example `nrf52840dk_nrf52840`): - - $ west build -b build-target -- -DCONFIG_CHIP_DFU_OVER_BT_SMP=y - -To completely disable support for both DFU methods, run the following command -with _build-target_ replaced with the build target name of the Nordic -Semiconductor kit you are using (for example `nrf52840dk_nrf52840`): +using (for example `nrf52840dk/nrf52840`): - $ west build -b build-target -- -DCONF_FILE=prj_no_dfu.conf + $ west build -b build-target --sysbuild -- -DCONFIG_CHIP_DFU_OVER_BT_SMP=y > **Note**: > @@ -380,7 +391,7 @@ Semiconductor kit you are using (for example `nrf52840dk_nrf52840`): #### Changing bootloader configuration To change the default MCUboot configuration, edit the `prj.conf` file located in -the `child_image/mcuboot` directory. +the `sysbuild/mcuboot` directory. Make sure to keep the configuration consistent with changes made to the application configuration. This is necessary for the configuration to work, as @@ -397,8 +408,9 @@ purposes. You can change these settings by defining This example uses this option to define using an external flash. To modify the flash settings of your board (that is, your _build-target_, for -example `nrf52840dk_nrf52840`), edit the `pm_static_dfu.yml` file located in the -`configuration/build-target/` directory. +example `nrf52840dk/nrf52840`), edit the `pm_static_.yml` file +(for example `pm_static_nrf52840dk_nrf52840.yml`), located in the main +application directory.
@@ -410,7 +422,7 @@ using the menuconfig utility. To open the menuconfig utility, run the following command from the example directory: - $ west build -b build-target -t menuconfig + $ west build -b build-target --sysbuild -t menuconfig Remember to replace _build-target_ with the build target name of the Nordic Semiconductor's kit you own. @@ -440,9 +452,6 @@ depending on the selected board: command-line shell. - release -- Release version of the application - can be used to enable only the necessary application functionalities to optimize its performance. -- no_dfu -- Debug version of the application without Device Firmware Upgrade - feature support - can be used only for the nRF52840 DK and nRF5340 DK, as - those platforms have DFU enabled by default. For more information, see the [Configuring nRF Connect SDK examples](../../../docs/guides/nrfconnect_examples_configuration.md) diff --git a/examples/network-manager-app/linux/BUILD.gn b/examples/network-manager-app/linux/BUILD.gn index 1dee694c38616e..ea973edfbfd15c 100644 --- a/examples/network-manager-app/linux/BUILD.gn +++ b/examples/network-manager-app/linux/BUILD.gn @@ -15,7 +15,7 @@ import("//build_overrides/build.gni") import("//build_overrides/chip.gni") -executable("network-manager-app") { +executable("matter-network-manager-app") { sources = [ "include/CHIPProjectAppConfig.h", "main.cpp", @@ -32,7 +32,7 @@ executable("network-manager-app") { } group("linux") { - deps = [ ":network-manager-app" ] + deps = [ ":matter-network-manager-app" ] } group("default") { 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 dd37ffc02a635c..039b20292786b7 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 @@ -1193,10 +1193,11 @@ cluster GroupKeyManagement = 63 { } /** Functionality to retrieve operational information about a managed Wi-Fi network. */ -cluster WiFiNetworkManagement = 1105 { +provisional cluster WiFiNetworkManagement = 1105 { revision 1; - readonly attribute nullable octet_string<32> ssid = 1; + readonly attribute nullable octet_string<32> ssid = 0; + readonly attribute access(read: manage) nullable int64u passphraseSurrogate = 1; readonly attribute command_id generatedCommandList[] = 65528; readonly attribute command_id acceptedCommandList[] = 65529; readonly attribute event_id eventList[] = 65530; @@ -1209,11 +1210,11 @@ cluster WiFiNetworkManagement = 1105 { } /** Request the current WPA-Personal passphrase or PSK associated with the managed Wi-Fi network. */ - command access(invoke: administer) NetworkPassphraseRequest(): NetworkPassphraseResponse = 0; + command access(invoke: manage) NetworkPassphraseRequest(): NetworkPassphraseResponse = 0; } /** Manages the names and credentials of Thread networks visible to the user. */ -cluster ThreadNetworkDirectory = 1107 { +provisional cluster ThreadNetworkDirectory = 1107 { revision 1; struct ThreadNetworkStruct { @@ -1254,7 +1255,7 @@ cluster ThreadNetworkDirectory = 1107 { /** Removes an entry from the ThreadNetworks list. */ timed command access(invoke: manage) RemoveNetwork(RemoveNetworkRequest): DefaultSuccess = 1; /** Retrieves a Thread Operational Dataset from the ThreadNetworks list. */ - timed command GetOperationalDataset(GetOperationalDatasetRequest): OperationalDatasetResponse = 2; + command GetOperationalDataset(GetOperationalDatasetRequest): OperationalDatasetResponse = 2; } endpoint 0 { @@ -1525,6 +1526,7 @@ endpoint 1 { server cluster WiFiNetworkManagement { callback attribute ssid; + callback attribute passphraseSurrogate; callback attribute generatedCommandList; callback attribute acceptedCommandList; callback attribute eventList; 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 ec8316ce2ad3c0..64113c969dd774 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 @@ -3238,7 +3238,7 @@ "attributes": [ { "name": "SSID", - "code": 1, + "code": 0, "mfgCode": null, "side": "server", "type": "octet_string", @@ -3252,6 +3252,22 @@ "maxInterval": 65534, "reportableChange": 0 }, + { + "name": "PassphraseSurrogate", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "int64u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, { "name": "GeneratedCommandList", "code": 65528, diff --git a/examples/platform/esp32/external_platform/ESP32_custom/BUILD.gn b/examples/platform/esp32/external_platform/ESP32_custom/BUILD.gn index d8d2f92b542ac7..93b00ef8f81617 100644 --- a/examples/platform/esp32/external_platform/ESP32_custom/BUILD.gn +++ b/examples/platform/esp32/external_platform/ESP32_custom/BUILD.gn @@ -29,6 +29,7 @@ declare_args() { chip_bt_bluedroid_enabled = true chip_max_discovered_ip_addresses = 5 chip_enable_route_hook = false + chip_enable_thread_border_router = false } buildconfig_header("custom_buildconfig") { diff --git a/examples/platform/linux/BUILD.gn b/examples/platform/linux/BUILD.gn index a63175b51200eb..410b1a189245d2 100644 --- a/examples/platform/linux/BUILD.gn +++ b/examples/platform/linux/BUILD.gn @@ -54,6 +54,10 @@ source_set("energy-reporting-test-event-trigger") { sources = [ "${chip_root}/src/app/clusters/electrical-energy-measurement-server/EnergyReportingTestEventTriggerHandler.h" ] } +source_set("water-heater-management-test-event-trigger") { + sources = [ "${chip_root}/src/app/clusters/water-heater-management-server/WaterHeaterManagementTestEventTriggerHandler.h" ] +} + source_set("device-energy-management-test-event-trigger") { sources = [ "${chip_root}/src/app/clusters/device-energy-management-server/DeviceEnergyManagementTestEventTriggerHandler.h" ] } @@ -85,6 +89,7 @@ source_set("app-main") { ":energy-evse-test-event-trigger", ":energy-reporting-test-event-trigger", ":smco-test-event-trigger", + ":water-heater-management-test-event-trigger", "${chip_root}/src/controller:controller", "${chip_root}/src/controller:gen_check_chip_controller_headers", "${chip_root}/src/lib", diff --git a/examples/pump-app/nrfconnect/README.md b/examples/pump-app/nrfconnect/README.md index 50e8a1b0e6d0a0..16fbc2f0143c2c 100644 --- a/examples/pump-app/nrfconnect/README.md +++ b/examples/pump-app/nrfconnect/README.md @@ -1,5 +1,21 @@ # Matter nRF Connect Pump Example Application +> **Note:** This example is intended only to perform smoke tests of a Matter +> solution integrated with nRF Connect SDK platform. The example quality is not +> production ready and it may contain minor bugs or use not optimal +> configuration. It is not recommended to use this example as a basis for +> creating a market ready product. +> +> For the production ready and optimized Matter samples, see +> [nRF Connect SDK samples](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/samples/matter.html). +> The Matter samples in nRF Connect SDK use various additional software +> components and provide multiple optional features that improve the developer +> and user experience. To read more about it, see +> [Matter support in nRF Connect SDK](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/protocols/matter/index.html#ug-matter) +> page. Using Matter samples from nRF Connect SDK allows you to get a full +> Nordic technical support via [DevZone](https://devzone.nordicsemi.com/) +> portal. + The nRF Connect Pump Example demonstrates how to remotely control a pump device with basic start/stop functionality. It uses buttons to test changing the pump state and device states and LEDs to show the state of these changes. This @@ -139,8 +155,8 @@ The example supports building and running on the following devices: | Hardware platform | Build target | Platform image | | ----------------------------------------------------------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | -| [nRF52840 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK) | `nrf52840dk_nrf52840` |
nRF52840 DKnRF52840 DK
| -| [nRF5340 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF5340-DK) | `nrf5340dk_nrf5340_cpuapp` |
nRF5340 DKnRF5340 DK
| +| [nRF52840 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK) | `nrf52840dk/nrf52840` |
nRF52840 DKnRF52840 DK
| +| [nRF5340 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF5340-DK) | `nrf5340dk/nrf5340/cpuapp` |
nRF5340 DKnRF5340 DK
|
@@ -283,14 +299,15 @@ Complete the following steps to build the sample: 2. Run the following command to build the example, with _build-target_ replaced with the build target name of the Nordic Semiconductor's kit you own, for - example `nrf52840dk_nrf52840`: + example `nrf52840dk/nrf52840`: - $ west build -b build-target + $ west build -b build-target --sysbuild You only need to specify the build target on the first build. See [Requirements](#requirements) for the build target names of compatible kits. -The output `zephyr.hex` file will be available in the `build/zephyr/` directory. +The output `zephyr.hex` file will be available in the `build/nrfconnect/zephyr/` +directory. ### Removing build artifacts @@ -305,7 +322,7 @@ following command: To build the example with release configuration that disables the diagnostic features like logs and command-line interface, run the following command: - $ west build -b build-target -- -DCONF_FILE=prj_release.conf + $ west build -b build-target --sysbuild -- -DFILE_SUFFIX=release Remember to replace _build-target_ with the build target name of the Nordic Semiconductor's kit you own. @@ -316,15 +333,9 @@ Support for DFU using Matter OTA is enabled by default. To enable DFU over Bluetooth LE, run the following command with _build-target_ replaced with the build target name of the Nordic Semiconductor kit you are -using (for example `nrf52840dk_nrf52840`): - - $ west build -b build-target -- -DCONFIG_CHIP_DFU_OVER_BT_SMP=y - -To completely disable support for both DFU methods, run the following command -with _build-target_ replaced with the build target name of the Nordic -Semiconductor kit you are using (for example `nrf52840dk_nrf52840`): +using (for example `nrf52840dk/nrf52840`): - $ west build -b build-target -- -DCONF_FILE=prj_no_dfu.conf + $ west build -b build-target --sysbuild -- -DCONFIG_CHIP_DFU_OVER_BT_SMP=y > **Note**: > @@ -341,7 +352,7 @@ Semiconductor kit you are using (for example `nrf52840dk_nrf52840`): #### Changing bootloader configuration To change the default MCUboot configuration, edit the `prj.conf` file located in -the `child_image/mcuboot` directory. +the `sysbuild/mcuboot` directory. Make sure to keep the configuration consistent with changes made to the application configuration. This is necessary for the configuration to work, as @@ -358,8 +369,9 @@ purposes. You can change these settings by defining This example uses this option to define using an external flash. To modify the flash settings of your board (that is, your _build-target_, for -example `nrf52840dk_nrf52840`), edit the `pm_static_dfu.yml` file located in the -`configuration/build-target/` directory. +example `nrf52840dk/nrf52840`), edit the `pm_static_.yml` file +(for example `pm_static_nrf52840dk_nrf52840.yml`), located in the main +application directory.
@@ -371,7 +383,7 @@ using the menuconfig utility. To open the menuconfig utility, run the following command from the example directory: - $ west build -b build-target -t menuconfig + $ west build -b build-target --sysbuild -t menuconfig Remember to replace _build-target_ with the build target name of the Nordic Semiconductor's kit you own. @@ -401,9 +413,6 @@ depending on the selected board: command-line shell. - release -- Release version of the application - can be used to enable only the necessary application functionalities to optimize its performance. -- no_dfu -- Debug version of the application without Device Firmware Upgrade - feature support - can be used only for the nRF52840 DK and nRF5340 DK, as - those platforms have DFU enabled by default. For more information, see the [Configuring nRF Connect SDK examples](../../../docs/guides/nrfconnect_examples_configuration.md) diff --git a/examples/pump-controller-app/nrfconnect/README.md b/examples/pump-controller-app/nrfconnect/README.md index 948aa20f54d0d8..64f37c4bb6a7aa 100644 --- a/examples/pump-controller-app/nrfconnect/README.md +++ b/examples/pump-controller-app/nrfconnect/README.md @@ -1,5 +1,21 @@ # Matter nRF Connect Pump Example Application +> **Note:** This example is intended only to perform smoke tests of a Matter +> solution integrated with nRF Connect SDK platform. The example quality is not +> production ready and it may contain minor bugs or use not optimal +> configuration. It is not recommended to use this example as a basis for +> creating a market ready product. +> +> For the production ready and optimized Matter samples, see +> [nRF Connect SDK samples](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/samples/matter.html). +> The Matter samples in nRF Connect SDK use various additional software +> components and provide multiple optional features that improve the developer +> and user experience. To read more about it, see +> [Matter support in nRF Connect SDK](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/protocols/matter/index.html#ug-matter) +> page. Using Matter samples from nRF Connect SDK allows you to get a full +> Nordic technical support via [DevZone](https://devzone.nordicsemi.com/) +> portal. + The nRF Connect Pump Controller Example demonstrates how to implement a pump controller client device with basic start/stop functionality. It uses buttons to test changing the pump state and device states and LEDs to show the state of @@ -140,8 +156,8 @@ The example supports building and running on the following devices: | Hardware platform | Build target | Platform image | | ----------------------------------------------------------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | -| [nRF52840 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK) | `nrf52840dk_nrf52840` |
nRF52840 DKnRF52840 DK
| -| [nRF5340 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF5340-DK) | `nrf5340dk_nrf5340_cpuapp` |
nRF5340 DKnRF5340 DK
| +| [nRF52840 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK) | `nrf52840dk/nrf52840` |
nRF52840 DKnRF52840 DK
| +| [nRF5340 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF5340-DK) | `nrf5340dk/nrf5340/cpuapp` |
nRF5340 DKnRF5340 DK
|
@@ -283,14 +299,15 @@ Complete the following steps to build the sample: 2. Run the following command to build the example, with _build-target_ replaced with the build target name of the Nordic Semiconductor's kit you own, for - example `nrf52840dk_nrf52840`: + example `nrf52840dk/nrf52840`: - $ west build -b build-target + $ west build -b build-target --sysbuild You only need to specify the build target on the first build. See [Requirements](#requirements) for the build target names of compatible kits. -The output `zephyr.hex` file will be available in the `build/zephyr/` directory. +The output `zephyr.hex` file will be available in the `build/nrfconnect/zephyr/` +directory. ### Removing build artifacts @@ -305,7 +322,7 @@ following command: To build the example with release configuration that disables the diagnostic features like logs and command-line interface, run the following command: - $ west build -b build-target -- -DCONF_FILE=prj_release.conf + $ west build -b build-target --sysbuild -- -DFILE_SUFFIX=release Remember to replace _build-target_ with the build target name of the Nordic Semiconductor's kit you own. @@ -316,15 +333,9 @@ Support for DFU using Matter OTA is enabled by default. To enable DFU over Bluetooth LE, run the following command with _build-target_ replaced with the build target name of the Nordic Semiconductor kit you are -using (for example `nrf52840dk_nrf52840`): - - $ west build -b build-target -- -DCONFIG_CHIP_DFU_OVER_BT_SMP=y - -To completely disable support for both DFU methods, run the following command -with _build-target_ replaced with the build target name of the Nordic -Semiconductor kit you are using (for example `nrf52840dk_nrf52840`): +using (for example `nrf52840dk/nrf52840`): - $ west build -b build-target -- -DCONF_FILE=prj_no_dfu.conf + $ west build -b build-target --sysbuild -- -DCONFIG_CHIP_DFU_OVER_BT_SMP=y > **Note**: > @@ -341,7 +352,7 @@ Semiconductor kit you are using (for example `nrf52840dk_nrf52840`): #### Changing bootloader configuration To change the default MCUboot configuration, edit the `prj.conf` file located in -the `child_image/mcuboot` directory. +the `sysbuild/mcuboot` directory. Make sure to keep the configuration consistent with changes made to the application configuration. This is necessary for the configuration to work, as @@ -358,8 +369,9 @@ purposes. You can change these settings by defining This example uses this option to define using an external flash. To modify the flash settings of your board (that is, your _build-target_, for -example `nrf52840dk_nrf52840`), edit the `pm_static_dfu.yml` file located in the -`configuration/build-target/` directory. +example `nrf52840dk/nrf52840`), edit the `pm_static_.yml` file +(for example `pm_static_nrf52840dk_nrf52840.yml`), located in the main +application directory.
@@ -371,7 +383,7 @@ using the menuconfig utility. To open the menuconfig utility, run the following command from the example directory: - $ west build -b build-target -t menuconfig + $ west build -b build-target --sysbuild -t menuconfig Remember to replace _build-target_ with the build target name of the Nordic Semiconductor's kit you own. @@ -401,9 +413,6 @@ depending on the selected board: command-line shell. - release -- Release version of the application - can be used to enable only the necessary application functionalities to optimize its performance. -- no_dfu -- Debug version of the application without Device Firmware Upgrade - feature support - can be used only for the nRF52840 DK and nRF5340 DK, as - those platforms have DFU enabled by default. For more information, see the [Configuring nRF Connect SDK examples](../../../docs/guides/nrfconnect_examples_configuration.md) diff --git a/examples/shell/nrfconnect/README.md b/examples/shell/nrfconnect/README.md index f0abf5e312462c..551e0d67c052a1 100644 --- a/examples/shell/nrfconnect/README.md +++ b/examples/shell/nrfconnect/README.md @@ -1,5 +1,21 @@ # Matter nRF Connect SDK Shell Application +> **Note:** This example is intended only to perform smoke tests of a Matter +> solution integrated with nRF Connect SDK platform. The example quality is not +> production ready and it may contain minor bugs or use not optimal +> configuration. It is not recommended to use this example as a basis for +> creating a market ready product. +> +> For the production ready and optimized Matter samples, see +> [nRF Connect SDK samples](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/samples/matter.html). +> The Matter samples in nRF Connect SDK use various additional software +> components and provide multiple optional features that improve the developer +> and user experience. To read more about it, see +> [Matter support in nRF Connect SDK](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/protocols/matter/index.html#ug-matter) +> page. Using Matter samples from nRF Connect SDK allows you to get a full +> Nordic technical support via [DevZone](https://devzone.nordicsemi.com/) +> portal. + A [Matter shell](../README.md) project for the Nordic nRF52840 and nRF5340 development kits, built using the nRF Connect SDK. diff --git a/examples/window-app/nrfconnect/README.md b/examples/window-app/nrfconnect/README.md index b61f05dddfb15e..964af0c265cb7e 100644 --- a/examples/window-app/nrfconnect/README.md +++ b/examples/window-app/nrfconnect/README.md @@ -1,5 +1,21 @@ # Matter nRF Connect Window Covering Example Application +> **Note:** This example is intended only to perform smoke tests of a Matter +> solution integrated with nRF Connect SDK platform. The example quality is not +> production ready and it may contain minor bugs or use not optimal +> configuration. It is not recommended to use this example as a basis for +> creating a market ready product. +> +> For the production ready and optimized Matter samples, see +> [nRF Connect SDK samples](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/samples/matter.html). +> The Matter samples in nRF Connect SDK use various additional software +> components and provide multiple optional features that improve the developer +> and user experience. To read more about it, see +> [Matter support in nRF Connect SDK](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/protocols/matter/index.html#ug-matter) +> page. Using Matter samples from nRF Connect SDK allows you to get a full +> Nordic technical support via [DevZone](https://devzone.nordicsemi.com/) +> portal. + The nRF Connect Window Covering Example demonstrates how to remotely control a window shutter device. It uses buttons to test changing cover position and device states and LEDs to show the state of these changes. You can use this @@ -131,8 +147,8 @@ The example supports building and running on the following devices: | Hardware platform | Build target | Platform image | | ----------------------------------------------------------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | -| [nRF52840 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK) | `nrf52840dk_nrf52840` |
nRF52840 DKnRF52840 DK
| -| [nRF5340 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF5340-DK) | `nrf5340dk_nrf5340_cpuapp` |
nRF5340 DKnRF5340 DK
| +| [nRF52840 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK) | `nrf52840dk/nrf52840` |
nRF52840 DKnRF52840 DK
| +| [nRF5340 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF5340-DK) | `nrf5340dk/nrf5340/cpuapp` |
nRF5340 DKnRF5340 DK
|
@@ -299,14 +315,15 @@ Complete the following steps to build the sample: 2. Run the following command to build the example, with _build-target_ replaced with the build target name of the Nordic Semiconductor's kit you own, for - example `nrf52840dk_nrf52840`: + example `nrf52840dk/nrf52840`: - $ west build -b build-target + $ west build -b build-target --sysbuild You only need to specify the build target on the first build. See [Requirements](#requirements) for the build target names of compatible kits. -The output `zephyr.hex` file will be available in the `build/zephyr/` directory. +The output `zephyr.hex` file will be available in the `build/nrfconnect/zephyr/` +directory. ### Removing build artifacts @@ -321,7 +338,7 @@ following command: To build the example with release configuration that disables the diagnostic features like logs and command-line interface, run the following command: - $ west build -b build-target -- -DCONF_FILE=prj_release.conf + $ west build -b build-target --sysbuild -- -DFILE_SUFFIX=release Remember to replace _build-target_ with the build target name of the Nordic Semiconductor's kit you own. @@ -332,18 +349,12 @@ Support for DFU using Matter OTA is enabled by default. To enable DFU over Bluetooth LE, run the following command with _build-target_ replaced with the build target name of the Nordic Semiconductor kit you are -using (for example `nrf52840dk_nrf52840`): +using (for example `nrf52840dk/nrf52840`): ``` - $ west build -b build-target -- -DCONFIG_CHIP_DFU_OVER_BT_SMP=y + $ west build -b build-target --sysbuild -- -DCONFIG_CHIP_DFU_OVER_BT_SMP=y ``` -To completely disable support for DFU, run the following command with -_build-target_ replaced with the build target name of the Nordic Semiconductor -kit you are using (for example `nrf52840dk_nrf52840`): - - $ west build -b build-target -- -DCONF_FILE=prj_no_dfu.conf - > **Note**: > > There are two types of Device Firmware Upgrade modes: single-image DFU and @@ -356,7 +367,7 @@ kit you are using (for example `nrf52840dk_nrf52840`): #### Changing bootloader configuration To change the default MCUboot configuration, edit the `prj.conf` file located in -the `child_image/mcuboot` directory. +the `sysbuild/mcuboot` directory. Make sure to keep the configuration consistent with changes made to the application configuration. This is necessary for the configuration to work, as @@ -373,8 +384,9 @@ purposes. You can change these settings by defining This example uses this option to define using an external flash. To modify the flash settings of your board (that is, your _build-target_, for -example `nrf52840dk_nrf52840`), edit the `pm_static_dfu.yml` file located in the -`configuration/build-target/` directory. +example `nrf52840dk/nrf52840`), edit the `pm_static_.yml` file +(for example `pm_static_nrf52840dk_nrf52840.yml`), located in the main +application directory.
@@ -386,7 +398,7 @@ using the menuconfig utility. To open the menuconfig utility, run the following command from the example directory: - $ west build -b build-target -t menuconfig + $ west build -b build-target --sysbuild -t menuconfig Remember to replace _build-target_ with the build target name of the Nordic Semiconductor's kit you own. @@ -416,9 +428,6 @@ depending on the selected board: command-line shell. - release — Release version of the application - can be used to enable only the necessary application functionalities to optimize its performance. -- no_dfu — Debug version of the application without Device Firmware - Upgrade feature support - can be used only for the nRF52840 DK and nRF5340 - DK, as those platforms have DFU enabled by default. For more information, see the [Configuring nRF Connect SDK examples](../../../docs/guides/nrfconnect_examples_configuration.md) diff --git a/scripts/build/builders/host.py b/scripts/build/builders/host.py index 50695772fff354..4ea5a9aae0f836 100644 --- a/scripts/build/builders/host.py +++ b/scripts/build/builders/host.py @@ -256,6 +256,9 @@ def OutputNames(self): elif self == HostApp.LIT_ICD: yield 'lit-icd-app' yield 'lit-icd-app.map' + elif self == HostApp.NETWORK_MANAGER: + yield 'matter-network-manager-app' + yield 'matter-network-manager-app.map' elif self == HostApp.ENERGY_MANAGEMENT: yield 'chip-energy-management-app' yield 'chip-energy-management-app.map' diff --git a/scripts/py_matter_idl/matter_idl/test_idl_generator.py b/scripts/py_matter_idl/matter_idl/test_idl_generator.py index 9f40b111bff96f..b030f8b8387a88 100755 --- a/scripts/py_matter_idl/matter_idl/test_idl_generator.py +++ b/scripts/py_matter_idl/matter_idl/test_idl_generator.py @@ -55,8 +55,8 @@ def ReadMatterIdl(repo_path: str) -> str: return stream.read() -def ParseMatterIdl(repo_path: str, skip_meta: bool) -> Idl: - return CreateParser(skip_meta=skip_meta).parse(ReadMatterIdl(repo_path)) +def ParseMatterIdl(repo_path: str, skip_meta: bool, merge_globals: bool) -> Idl: + return CreateParser(skip_meta=skip_meta, merge_globals=merge_globals).parse(ReadMatterIdl(repo_path)) def RenderAsIdlTxt(idl: Idl) -> str: @@ -106,8 +106,10 @@ def test_client_clusters(self): # Files MUST be identical except the header comments which are different original = SkipLeadingComments(ReadMatterIdl(path), also_strip=[ " // NOTE: Default/not specifically set"]) + # Do not merge globals because ZAP generated matter files do not contain + # the merge global data (will not render global reference comments). generated = SkipLeadingComments(RenderAsIdlTxt( - ParseMatterIdl(path, skip_meta=False))) + ParseMatterIdl(path, skip_meta=False, merge_globals=False))) self.assertTextEqual(original, generated) @@ -126,9 +128,9 @@ def test_app_rendering(self): ] for path in test_paths: - idl = ParseMatterIdl(path, skip_meta=True) + idl = ParseMatterIdl(path, skip_meta=True, merge_globals=True) txt = RenderAsIdlTxt(idl) - idl2 = CreateParser(skip_meta=True).parse(txt) + idl2 = CreateParser(skip_meta=True, merge_globals=True).parse(txt) # checks that data types and content is the same self.assertEqual(idl, idl2) diff --git a/scripts/py_matter_idl/matter_idl/test_zapxml.py b/scripts/py_matter_idl/matter_idl/test_zapxml.py index 6e6cd3d8761ca9..5ea15e27e8f9c2 100755 --- a/scripts/py_matter_idl/matter_idl/test_zapxml.py +++ b/scripts/py_matter_idl/matter_idl/test_zapxml.py @@ -234,6 +234,36 @@ def testFabricScopedAndSensitive(self): qualities=StructQuality.FABRIC_SCOPED)], )])) + def testGlobalEnum(self): + idl = XmlToIdl(''' + + + + + + + + + + + ''') + e1 = Enum( + name='One', + base_type="ENUM8", + entries=[ + ConstantEntry(name="Three", code=3), + ] + ) + e2 = Enum( + name='Two', + base_type="ENUM8", + entries=[ + ConstantEntry(name="Big", code=100), + ConstantEntry(name="Bigger", code=2000), + ] + ) + self.assertEqual(idl, Idl(global_enums=[e1, e2])) + def testEnum(self): idl = XmlToIdl(''' @@ -308,6 +338,28 @@ def testFeatures(self): Cluster(name='TestFeatures', code=20, bitmaps=[bitmap]) ])), + def testGlobalStruct(self): + idl = XmlToIdl(''' + + + + + + + + ''') + struct = Struct( + name='SomeStruct', + qualities=StructQuality.FABRIC_SCOPED, + fields=[ + Field(data_type=DataType(name='int16u'), + code=0, name='FirstMember'), + Field(data_type=DataType(name='int32u'), + code=1, name='SecondMember') + ] + ) + self.assertEqual(idl, Idl(global_structs=[struct])) + def testStruct(self): idl = XmlToIdl(''' diff --git a/scripts/py_matter_idl/matter_idl/zapxml/handlers/handlers.py b/scripts/py_matter_idl/matter_idl/zapxml/handlers/handlers.py index 866ce71f914214..cc5ccd9483091d 100644 --- a/scripts/py_matter_idl/matter_idl/zapxml/handlers/handlers.py +++ b/scripts/py_matter_idl/matter_idl/zapxml/handlers/handlers.py @@ -225,7 +225,7 @@ def FinalizeProcessing(self, idl: Idl): # - inside a cluster if a code exists # - inside top level if no codes were associated if not self._cluster_codes: - LOGGER.error('Struct %s has no cluster codes' % self._struct.name) + idl.global_structs.append(self._struct) return for code in self._cluster_codes: @@ -270,9 +270,9 @@ def GetNextProcessor(self, name, attrs): def FinalizeProcessing(self, idl: Idl): if not self._cluster_codes: - LOGGER.error("Found enum without a cluster code: %s" % - (self._enum.name)) + idl.global_enums.append(self._enum) return + found = set() for c in idl.clusters: if c.code in self._cluster_codes: @@ -313,14 +313,11 @@ def GetNextProcessor(self, name, attrs): return BaseHandler(self.context) def FinalizeProcessing(self, idl: Idl): - # We have two choices of adding an enum: + # We have two choices of adding a bitmap: # - inside a cluster if a code exists # - inside top level if a code does not exist if not self._cluster_codes: - # Log only instead of critical, as not our XML is well formed. - # For example at the time of writing this, SwitchFeature in switch-cluster.xml - # did not have a code associated with it. - LOGGER.error("Bitmap %r has no cluster codes" % self._bitmap) + idl.global_bitmaps.append(self._bitmap) return for code in self._cluster_codes: diff --git a/scripts/tests/chiptest/__init__.py b/scripts/tests/chiptest/__init__.py index a250bab52e9de4..8d0950f379bae1 100644 --- a/scripts/tests/chiptest/__init__.py +++ b/scripts/tests/chiptest/__init__.py @@ -286,6 +286,8 @@ def target_for_name(name: str): return TestTarget.MWO if name.startswith("Test_TC_RVCRUNM_") or name.startswith("Test_TC_RVCCLEANM_") or name.startswith("Test_TC_RVCOPSTATE_"): return TestTarget.RVC + if name.startswith("Test_TC_THNETDIR_") or name.startswith("Test_TC_WIFINM_"): + return TestTarget.NETWORK_MANAGER return TestTarget.ALL_CLUSTERS diff --git a/scripts/tests/chiptest/linux.py b/scripts/tests/chiptest/linux.py index 3dbd851be26b47..0a6df4cd671671 100644 --- a/scripts/tests/chiptest/linux.py +++ b/scripts/tests/chiptest/linux.py @@ -184,6 +184,7 @@ def PathsWithNetworkNamespaces(paths: ApplicationPaths) -> ApplicationPaths: lit_icd_app='ip netns exec app'.split() + paths.lit_icd_app, microwave_oven_app='ip netns exec app'.split() + paths.microwave_oven_app, rvc_app='ip netns exec app'.split() + paths.rvc_app, + network_manager_app='ip netns exec app'.split() + paths.network_manager_app, bridge_app='ip netns exec app'.split() + paths.bridge_app, chip_repl_yaml_tester_cmd='ip netns exec tool'.split() + paths.chip_repl_yaml_tester_cmd, chip_tool_with_python_cmd='ip netns exec tool'.split() + paths.chip_tool_with_python_cmd, diff --git a/scripts/tests/chiptest/test_definition.py b/scripts/tests/chiptest/test_definition.py index 970c098f1354bb..57c43c27a1cbca 100644 --- a/scripts/tests/chiptest/test_definition.py +++ b/scripts/tests/chiptest/test_definition.py @@ -177,6 +177,7 @@ class TestTarget(Enum): LIT_ICD = auto() MWO = auto() RVC = auto() + NETWORK_MANAGER = auto() @dataclass @@ -193,10 +194,11 @@ class ApplicationPaths: chip_repl_yaml_tester_cmd: typing.List[str] chip_tool_with_python_cmd: typing.List[str] rvc_app: typing.List[str] + network_manager_app: typing.List[str] def items(self): return [self.chip_tool, self.all_clusters_app, self.lock_app, self.ota_provider_app, self.ota_requestor_app, - self.tv_app, self.bridge_app, self.lit_icd_app, self.microwave_oven_app, self.chip_repl_yaml_tester_cmd, self.chip_tool_with_python_cmd, self.rvc_app] + self.tv_app, self.bridge_app, self.lit_icd_app, self.microwave_oven_app, self.chip_repl_yaml_tester_cmd, self.chip_tool_with_python_cmd, self.rvc_app, self.network_manager_app] @dataclass @@ -309,6 +311,8 @@ def Run(self, runner, apps_register, paths: ApplicationPaths, pics_file: str, target_app = paths.microwave_oven_app elif self.target == TestTarget.RVC: target_app = paths.rvc_app + elif self.target == TestTarget.NETWORK_MANAGER: + target_app = paths.network_manager_app else: raise Exception("Unknown test target - " "don't know which application to run") diff --git a/scripts/tests/run_test_suite.py b/scripts/tests/run_test_suite.py index c22b4abff92f09..d546bb0803b6b9 100755 --- a/scripts/tests/run_test_suite.py +++ b/scripts/tests/run_test_suite.py @@ -260,6 +260,9 @@ def cmd_list(context): @click.option( '--rvc-app', help='what rvc app to use') +@click.option( + '--network-manager-app', + help='what network-manager app to use') @click.option( '--chip-repl-yaml-tester', help='what python script to use for running yaml tests using chip-repl as controller') @@ -291,7 +294,7 @@ def cmd_list(context): help='Number of tests that are expected to fail in each iteration. Overall test will pass if the number of failures matches this. Nonzero values require --keep-going') @click.pass_context def cmd_run(context, iterations, all_clusters_app, lock_app, ota_provider_app, ota_requestor_app, - tv_app, bridge_app, lit_icd_app, microwave_oven_app, rvc_app, chip_repl_yaml_tester, chip_tool_with_python, pics_file, keep_going, test_timeout_seconds, expected_failures): + tv_app, bridge_app, lit_icd_app, microwave_oven_app, rvc_app, network_manager_app, chip_repl_yaml_tester, chip_tool_with_python, pics_file, keep_going, test_timeout_seconds, expected_failures): if expected_failures != 0 and not keep_going: logging.exception(f"'--expected-failures {expected_failures}' used without '--keep-going'") sys.exit(2) @@ -327,6 +330,9 @@ def cmd_run(context, iterations, all_clusters_app, lock_app, ota_provider_app, o if rvc_app is None: rvc_app = paths_finder.get('chip-rvc-app') + if network_manager_app is None: + network_manager_app = paths_finder.get('matter-network-manager-app') + if chip_repl_yaml_tester is None: chip_repl_yaml_tester = paths_finder.get('yamltest_with_chip_repl_tester.py') @@ -348,6 +354,7 @@ def cmd_run(context, iterations, all_clusters_app, lock_app, ota_provider_app, o lit_icd_app=[lit_icd_app], microwave_oven_app=[microwave_oven_app], rvc_app=[rvc_app], + network_manager_app=[network_manager_app], chip_repl_yaml_tester_cmd=['python3'] + [chip_repl_yaml_tester], chip_tool_with_python_cmd=['python3'] + [chip_tool_with_python], ) diff --git a/scripts/tools/check_includes_config.py b/scripts/tools/check_includes_config.py index d897c86e32cfe8..8790faf1666b08 100644 --- a/scripts/tools/check_includes_config.py +++ b/scripts/tools/check_includes_config.py @@ -139,6 +139,7 @@ 'src/credentials/attestation_verifier/FileAttestationTrustStore.h': {'vector'}, 'src/credentials/attestation_verifier/FileAttestationTrustStore.cpp': {'string'}, + 'src/credentials/attestation_verifier/TestDACRevocationDelegateImpl.cpp': {'fstream'}, 'src/setup_payload/AdditionalDataPayload.h': {'string'}, 'src/setup_payload/AdditionalDataPayloadParser.cpp': {'vector', 'string'}, diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni index 098d291b3311f3..ae7bb84948b3be 100644 --- a/src/app/chip_data_model.gni +++ b/src/app/chip_data_model.gni @@ -398,6 +398,12 @@ template("chip_data_model") { "${_app_root}/clusters/${cluster}/thread-network-diagnostics-provider.cpp", "${_app_root}/clusters/${cluster}/thread-network-diagnostics-provider.h", ] + } else if (cluster == "thread-border-router-management-server") { + sources += [ + "${_app_root}/clusters/${cluster}/thread-border-router-management-server.cpp", + "${_app_root}/clusters/${cluster}/thread-border-router-management-server.h", + "${_app_root}/clusters/${cluster}/thread-br-delegate.h", + ] } else if (cluster == "water-heater-management-server") { sources += [ "${_app_root}/clusters/${cluster}/${cluster}.cpp", diff --git a/src/app/clusters/scenes-server/scenes-server.cpp b/src/app/clusters/scenes-server/scenes-server.cpp index e657a82f4654fe..7c215c20446948 100644 --- a/src/app/clusters/scenes-server/scenes-server.cpp +++ b/src/app/clusters/scenes-server/scenes-server.cpp @@ -376,9 +376,10 @@ void AddSceneParse(CommandHandlerInterface::HandlerContext & ctx, const CommandD response.sceneID = req.sceneID; // Verify the attributes are respecting constraints - if (req.transitionTime > scenes::kScenesMaxTransitionTime || req.sceneName.size() > scenes::kSceneNameMaxLength) + if (req.transitionTime > scenes::kScenesMaxTransitionTime || req.sceneName.size() > scenes::kSceneNameMaxLength || + req.sceneID == scenes::kUndefinedSceneId) { - response.status = to_underlying(Protocols::InteractionModel::Status::InvalidCommand); + response.status = to_underlying(Protocols::InteractionModel::Status::ConstraintError); ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); return; } @@ -483,6 +484,14 @@ void ViewSceneParse(HandlerContext & ctx, const CommandData & req, GroupDataProv response.groupID = req.groupID; response.sceneID = req.sceneID; + // Verify the attributes are respecting constraints + if (req.sceneID == scenes::kUndefinedSceneId) + { + response.status = to_underlying(Protocols::InteractionModel::Status::ConstraintError); + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); + return; + } + // Verify Endpoint in group VerifyOrReturn(nullptr != groupProvider); if (0 != req.groupID && @@ -830,6 +839,14 @@ void ScenesServer::HandleRemoveScene(HandlerContext & ctx, const Commands::Remov response.groupID = req.groupID; response.sceneID = req.sceneID; + // Verify the attributes are respecting constraints + if (req.sceneID == scenes::kUndefinedSceneId) + { + response.status = to_underlying(Protocols::InteractionModel::Status::ConstraintError); + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); + return; + } + // Scene Table interface data SceneTableEntry scene(SceneStorageId(req.sceneID, req.groupID)); @@ -930,6 +947,14 @@ void ScenesServer::HandleStoreScene(HandlerContext & ctx, const Commands::StoreS response.groupID = req.groupID; response.sceneID = req.sceneID; + // Verify the attributes are respecting constraints + if (req.sceneID == scenes::kUndefinedSceneId) + { + response.status = to_underlying(Protocols::InteractionModel::Status::ConstraintError); + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); + return; + } + CHIP_ERROR err = StoreSceneParse(ctx.mCommandHandler.GetAccessingFabricIndex(), ctx.mRequestPath.mEndpointId, req.groupID, req.sceneID, mGroupProvider); @@ -943,6 +968,14 @@ void ScenesServer::HandleStoreScene(HandlerContext & ctx, const Commands::StoreS void ScenesServer::HandleRecallScene(HandlerContext & ctx, const Commands::RecallScene::DecodableType & req) { MATTER_TRACE_SCOPE("RecallScene", "Scenes"); + + // Verify the attributes are respecting constraints + if (req.sceneID == scenes::kUndefinedSceneId) + { + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Protocols::InteractionModel::Status::ConstraintError); + return; + } + CHIP_ERROR err = RecallSceneParse(ctx.mCommandHandler.GetAccessingFabricIndex(), ctx.mRequestPath.mEndpointId, req.groupID, req.sceneID, req.transitionTime, mGroupProvider); @@ -1025,6 +1058,14 @@ void ScenesServer::HandleCopyScene(HandlerContext & ctx, const Commands::CopySce response.groupIdentifierFrom = req.groupIdentifierFrom; response.sceneIdentifierFrom = req.sceneIdentifierFrom; + // Verify the attributes are respecting constraints + if (req.sceneIdentifierFrom == scenes::kUndefinedSceneId || req.sceneIdentifierTo == scenes::kUndefinedSceneId) + { + response.status = to_underlying(Protocols::InteractionModel::Status::ResourceExhausted); + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); + return; + } + // Verify Endpoint in group VerifyOrReturn(nullptr != mGroupProvider); if ((0 != req.groupIdentifierFrom && diff --git a/src/app/clusters/thread-border-router-management-server/thread-border-router-management-server.cpp b/src/app/clusters/thread-border-router-management-server/thread-border-router-management-server.cpp new file mode 100644 index 00000000000000..5592da410100c2 --- /dev/null +++ b/src/app/clusters/thread-border-router-management-server/thread-border-router-management-server.cpp @@ -0,0 +1,357 @@ +/* + * + * 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 "thread-border-router-management-server.h" + +#include "app-common/zap-generated/cluster-objects.h" +#include "app-common/zap-generated/ids/Attributes.h" +#include "app-common/zap-generated/ids/Clusters.h" +#include "app-common/zap-generated/ids/Commands.h" +#include "app/AttributeAccessInterfaceRegistry.h" +#include "app/AttributeValueEncoder.h" +#include "app/CommandHandler.h" +#include "app/CommandHandlerInterface.h" +#include "app/CommandHandlerInterfaceRegistry.h" +#include "app/InteractionModelEngine.h" +#include "app/MessageDef/StatusIB.h" +#include "app/clusters/general-commissioning-server/general-commissioning-server.h" +#include "app/data-model/Nullable.h" +#include "lib/core/CHIPError.h" +#include "lib/core/Optional.h" +#include "lib/support/CodeUtils.h" +#include "lib/support/Span.h" +#include "lib/support/ThreadOperationalDataset.h" +#include "platform/CHIPDeviceEvent.h" +#include "platform/PlatformManager.h" +#include "protocols/interaction_model/StatusCode.h" + +namespace chip { +namespace app { +namespace Clusters { +namespace ThreadBorderRouterManagement { + +using Protocols::InteractionModel::Status; + +static bool IsCommandOverCASESession(CommandHandlerInterface::HandlerContext & ctx) +{ + Messaging::ExchangeContext * exchangeCtx = ctx.mCommandHandler.GetExchangeContext(); + return exchangeCtx && exchangeCtx->HasSessionHandle() && exchangeCtx->GetSessionHandle()->IsSecureSession() && + exchangeCtx->GetSessionHandle()->AsSecureSession()->GetSecureSessionType() == Transport::SecureSession::Type::kCASE; +} + +Status ServerInstance::HandleGetDatasetRequest(bool isOverCASESession, Delegate::DatasetType type, + Thread::OperationalDataset & dataset) +{ + VerifyOrDie(mDelegate); + if (!isOverCASESession) + { + return Status::UnsupportedAccess; + } + + CHIP_ERROR err = mDelegate->GetDataset(dataset, type); + if (err != CHIP_NO_ERROR) + { + return err == CHIP_IM_GLOBAL_STATUS(NotFound) ? StatusIB(err).mStatus : Status::Failure; + } + return Status::Success; +} + +Status ServerInstance::HandleSetActiveDatasetRequest(CommandHandler * commandHandler, + const Commands::SetActiveDatasetRequest::DecodableType & req) +{ + // The SetActiveDatasetRequest command SHALL be FailSafeArmed. Upon receiving this command, the Thread BR will set its + // active dataset. If the dataset is set successfully, OnActivateDatasetComplete will be called with CHIP_NO_ERROR, prompting + // the Thread BR to respond with a success status. If an error occurs while setting the active dataset, the Thread BR should + // respond with a failure status. In this case, when the FailSafe timer expires, the active dataset set by this command will be + // reverted. If the FailSafe timer expires before the Thread BR responds, the Thread BR will respond with a timeout status and + // the active dataset should also be reverted. + VerifyOrDie(mDelegate); + VerifyOrReturnValue(mFailsafeContext.IsFailSafeArmed(commandHandler->GetAccessingFabricIndex()), Status::FailsafeRequired); + + Thread::OperationalDataset activeDataset; + Thread::OperationalDataset currentActiveDataset; + uint64_t currentActiveDatasetTimestamp = 0; + // If any of the parameters in the ActiveDataset is invalid, the command SHALL fail with a status code + // of INVALID_COMMAND. + VerifyOrReturnValue(activeDataset.Init(req.activeDataset) == CHIP_NO_ERROR, Status::InvalidCommand); + + // If this command is invoked when the ActiveDatasetTimestamp attribute is not null, the command SHALL + // fail with a status code of INVALID_IN_STATE. + if ((mDelegate->GetDataset(currentActiveDataset, Delegate::DatasetType::kActive) == CHIP_NO_ERROR) && + (currentActiveDataset.GetActiveTimestamp(currentActiveDatasetTimestamp) == CHIP_NO_ERROR)) + { + return Status::InvalidInState; + } + // If there is a back end command process, return status BUSY. + if (mAsyncCommandHandle.Get()) + { + return Status::Busy; + } + commandHandler->FlushAcksRightAwayOnSlowCommand(); + mAsyncCommandHandle = CommandHandler::Handle(commandHandler); + mBreadcrumb = req.breadcrumb; + mSetActiveDatasetSequenceNumber++; + mDelegate->SetActiveDataset(activeDataset, mSetActiveDatasetSequenceNumber, this); + return Status::Success; +} + +Status ServerInstance::HandleSetPendingDatasetRequest(const Commands::SetPendingDatasetRequest::DecodableType & req) +{ + VerifyOrDie(mDelegate); + if (!mDelegate->GetPanChangeSupported()) + { + return Status::UnsupportedCommand; + } + Thread::OperationalDataset pendingDataset; + // If any of the parameters in the PendingDataset is invalid, the command SHALL fail with a status code + // of INVALID_COMMAND. + ReturnErrorCodeIf(pendingDataset.Init(req.pendingDataset) != CHIP_NO_ERROR, Status::InvalidCommand); + CHIP_ERROR err = mDelegate->SetPendingDataset(pendingDataset); + return StatusIB(err).mStatus; +} + +void AddDatasetResponse(CommandHandlerInterface::HandlerContext & ctx, Status status, const Thread::OperationalDataset & dataset) +{ + if (status != Status::Success) + { + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); + return; + } + Commands::DatasetResponse::Type response; + response.dataset = dataset.AsByteSpan(); + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); +} + +void ServerInstance::InvokeCommand(HandlerContext & ctxt) +{ + switch (ctxt.mRequestPath.mCommandId) + { + case Commands::GetActiveDatasetRequest::Id: + HandleCommand(ctxt, [this](HandlerContext & ctx, const auto & req) { + Thread::OperationalDataset dataset; + Status status = HandleGetActiveDatasetRequest(IsCommandOverCASESession(ctx), dataset); + AddDatasetResponse(ctx, status, dataset); + }); + break; + case Commands::GetPendingDatasetRequest::Id: + HandleCommand(ctxt, [this](HandlerContext & ctx, const auto & req) { + Thread::OperationalDataset dataset; + Status status = HandleGetPendingDatasetRequest(IsCommandOverCASESession(ctx), dataset); + AddDatasetResponse(ctx, status, dataset); + }); + break; + case Commands::SetActiveDatasetRequest::Id: + HandleCommand(ctxt, [this](HandlerContext & ctx, const auto & req) { + mPath = ctx.mRequestPath; + Status status = HandleSetActiveDatasetRequest(&ctx.mCommandHandler, req); + if (status != Status::Success) + { + // If status is not Success, we should immediately report the status. Otherwise the async work will report the + // status to the client. + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); + } + }); + break; + case Commands::SetPendingDatasetRequest::Id: + HandleCommand(ctxt, [this](HandlerContext & ctx, const auto & req) { + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, HandleSetPendingDatasetRequest(req)); + }); + break; + default: + break; + } +} + +void ServerInstance::ReadFeatureMap(BitFlags & outFeatureMap) +{ + if (mDelegate->GetPanChangeSupported()) + { + outFeatureMap.Set(Feature::kPANChange); + } +} + +CHIP_ERROR ServerInstance::ReadBorderRouterName(MutableCharSpan & outBorderRouterName) +{ + mDelegate->GetBorderRouterName(outBorderRouterName); + VerifyOrReturnValue(outBorderRouterName.size() <= kBorderRouterNameMaxLength, CHIP_IM_GLOBAL_STATUS(Failure)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR ServerInstance::ReadBorderAgentID(MutableByteSpan & outBorderAgentId) +{ + VerifyOrReturnValue((mDelegate->GetBorderAgentId(outBorderAgentId) == CHIP_NO_ERROR) && + (outBorderAgentId.size() == kBorderAgentIdLength), + CHIP_IM_GLOBAL_STATUS(Failure)); + return CHIP_NO_ERROR; +} + +Optional ServerInstance::ReadActiveDatasetTimestamp() +{ + uint64_t activeDatasetTimestampValue = 0; + Thread::OperationalDataset activeDataset; + if ((mDelegate->GetDataset(activeDataset, Delegate::DatasetType::kActive) == CHIP_NO_ERROR) && + (activeDataset.GetActiveTimestamp(activeDatasetTimestampValue) == CHIP_NO_ERROR)) + { + return MakeOptional(activeDatasetTimestampValue); + } + return NullOptional; +} + +CHIP_ERROR ServerInstance::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) +{ + if (aPath.mClusterId != ThreadBorderRouterManagement::Id) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + VerifyOrDie(mDelegate); + CHIP_ERROR status = CHIP_NO_ERROR; + switch (aPath.mAttributeId) + { + case Globals::Attributes::FeatureMap::Id: { + BitFlags featureMap; + ReadFeatureMap(featureMap); + status = aEncoder.Encode(featureMap); + break; + } + case Attributes::BorderRouterName::Id: { + char borderRouterNameBuf[kBorderRouterNameMaxLength] = { 0 }; + MutableCharSpan borderRouterName(borderRouterNameBuf); + status = ReadBorderRouterName(borderRouterName); + // If there are any internal errors, the status will be returned and the client will get an error report. + if (status == CHIP_NO_ERROR) + { + status = aEncoder.Encode(borderRouterName); + } + break; + } + case Attributes::BorderAgentID::Id: { + uint8_t borderAgentIDBuf[kBorderAgentIdLength] = { 0 }; + MutableByteSpan borderAgentID(borderAgentIDBuf); + status = ReadBorderAgentID(borderAgentID); + if (status == CHIP_NO_ERROR) + { + status = aEncoder.Encode(borderAgentID); + } + break; + } + case Attributes::ThreadVersion::Id: { + uint16_t threadVersion = mDelegate->GetThreadVersion(); + status = aEncoder.Encode(threadVersion); + break; + } + case Attributes::InterfaceEnabled::Id: { + bool interfaceEnabled = mDelegate->GetInterfaceEnabled(); + status = aEncoder.Encode(interfaceEnabled); + break; + } + case Attributes::ActiveDatasetTimestamp::Id: { + Optional activeDatasetTimestamp = ReadActiveDatasetTimestamp(); + status = activeDatasetTimestamp.HasValue() ? aEncoder.Encode(DataModel::MakeNullable(activeDatasetTimestamp.Value())) + : aEncoder.EncodeNull(); + break; + } + default: + break; + } + return status; +} + +void ServerInstance::CommitSavedBreadcrumb() +{ + if (mBreadcrumb.HasValue()) + { + GeneralCommissioning::SetBreadcrumb(mBreadcrumb.Value()); + } + mBreadcrumb.ClearValue(); +} + +void ServerInstance::OnActivateDatasetComplete(uint32_t sequenceNum, CHIP_ERROR error) +{ + auto commandHandleRef = std::move(mAsyncCommandHandle); + auto commandHandle = commandHandleRef.Get(); + if (commandHandle == nullptr) + { + return; + } + if (mSetActiveDatasetSequenceNumber != sequenceNum) + { + // Previous SetActiveDatasetRequest was handled. + return; + } + if (error == CHIP_NO_ERROR) + { + // TODO: SPEC Issue #10022 + CommitSavedBreadcrumb(); + } + else + { + ChipLogError(Zcl, "Failed on activating the active dataset for Thread BR: %" CHIP_ERROR_FORMAT, error.Format()); + } + commandHandle->AddStatus(mPath, StatusIB(error).mStatus); +} + +void ServerInstance::ReportAttributeChanged(AttributeId attributeId) +{ + MatterReportingAttributeChangeCallback(mServerEndpointId, Id, attributeId); +} + +void ServerInstance::OnFailSafeTimerExpired() +{ + if (mDelegate) + { + mDelegate->RevertActiveDataset(); + } + auto commandHandleRef = std::move(mAsyncCommandHandle); + auto commandHandle = commandHandleRef.Get(); + if (commandHandle == nullptr) + { + return; + } + commandHandle->AddStatus(mPath, Status::Timeout); +} + +void ServerInstance::OnPlatformEventHandler(const DeviceLayer::ChipDeviceEvent * event, intptr_t arg) +{ + ServerInstance * _this = reinterpret_cast(arg); + if (event->Type == DeviceLayer::DeviceEventType::kFailSafeTimerExpired) + { + _this->OnFailSafeTimerExpired(); + } + else if (event->Type == DeviceLayer::DeviceEventType::kCommissioningComplete) + { + _this->mDelegate->CommitActiveDataset(); + } +} + +CHIP_ERROR ServerInstance::Init() +{ + ReturnErrorCodeIf(!mDelegate, CHIP_ERROR_INVALID_ARGUMENT); + ReturnErrorOnFailure(CommandHandlerInterfaceRegistry::RegisterCommandHandler(this)); + VerifyOrReturnError(registerAttributeAccessOverride(this), CHIP_ERROR_INCORRECT_STATE); + ReturnErrorOnFailure(DeviceLayer::PlatformMgrImpl().AddEventHandler(OnPlatformEventHandler, reinterpret_cast(this))); + return mDelegate->Init(this); +} + +} // namespace ThreadBorderRouterManagement +} // namespace Clusters +} // namespace app +} // namespace chip + +void MatterThreadBorderRouterManagementPluginServerInitCallback() +{ + // Nothing to do, the server init routine will be done in Instance::Init() +} diff --git a/src/app/clusters/thread-border-router-management-server/thread-border-router-management-server.h b/src/app/clusters/thread-border-router-management-server/thread-border-router-management-server.h new file mode 100644 index 00000000000000..a2b6d7949ffdff --- /dev/null +++ b/src/app/clusters/thread-border-router-management-server/thread-border-router-management-server.h @@ -0,0 +1,106 @@ +/* + * + * 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. + */ + +#pragma once + +#include "thread-br-delegate.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace ThreadBorderRouterManagement { + +class ServerInstance : public CommandHandlerInterface, + public AttributeAccessInterface, + public Delegate::ActivateDatasetCallback, + public Delegate::AttributeChangeCallback +{ +public: + using Status = Protocols::InteractionModel::Status; + ServerInstance(EndpointId endpointId, Delegate * delegate, FailSafeContext & failSafeContext) : + CommandHandlerInterface(Optional(endpointId), Id), + AttributeAccessInterface(Optional(endpointId), Id), mDelegate(delegate), mServerEndpointId(endpointId), + mFailsafeContext(failSafeContext) + {} + virtual ~ServerInstance() = default; + + CHIP_ERROR Init(); + + // CommandHanlerInterface + void InvokeCommand(HandlerContext & ctx) override; + + // AttributeAccessInterface + CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; + + // ActivateDatasetCallback + void OnActivateDatasetComplete(uint32_t sequenceNum, CHIP_ERROR error) override; + + // AttributeChangeCallback + void ReportAttributeChanged(AttributeId attributeId) override; + +private: + // TODO: Split the business logic from the unit test class + friend class TestThreadBorderRouterManagementCluster; + // Command Handlers + Status HandleGetActiveDatasetRequest(bool isOverCASESession, Thread::OperationalDataset & dataset) + { + return HandleGetDatasetRequest(isOverCASESession, Delegate::DatasetType::kActive, dataset); + } + Status HandleGetPendingDatasetRequest(bool isOverCASESession, Thread::OperationalDataset & dataset) + { + return HandleGetDatasetRequest(isOverCASESession, Delegate::DatasetType::kPending, dataset); + } + Status HandleSetActiveDatasetRequest(CommandHandler * commandHandler, + const Commands::SetActiveDatasetRequest::DecodableType & req); + Status HandleSetPendingDatasetRequest(const Commands::SetPendingDatasetRequest::DecodableType & req); + Status HandleGetDatasetRequest(bool isOverCASESession, Delegate::DatasetType type, Thread::OperationalDataset & dataset); + + // Attribute Read handlers + void ReadFeatureMap(BitFlags & feature); + Optional ReadActiveDatasetTimestamp(); + CHIP_ERROR ReadBorderRouterName(MutableCharSpan & borderRouterName); + CHIP_ERROR ReadBorderAgentID(MutableByteSpan & borderAgentId); + + static void OnPlatformEventHandler(const DeviceLayer::ChipDeviceEvent * event, intptr_t arg); + void OnFailSafeTimerExpired(); + void CommitSavedBreadcrumb(); + + Delegate * mDelegate; + app::CommandHandler::Handle mAsyncCommandHandle; + ConcreteCommandPath mPath = ConcreteCommandPath(0, 0, 0); + Optional mBreadcrumb; + uint32_t mSetActiveDatasetSequenceNumber = 0; + EndpointId mServerEndpointId; + FailSafeContext & mFailsafeContext; +}; + +} // namespace ThreadBorderRouterManagement +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/clusters/thread-border-router-management-server/thread-br-delegate.h b/src/app/clusters/thread-border-router-management-server/thread-br-delegate.h new file mode 100644 index 00000000000000..201540af96ecff --- /dev/null +++ b/src/app/clusters/thread-border-router-management-server/thread-br-delegate.h @@ -0,0 +1,115 @@ +/* + * + * 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. + */ + +#pragma once + +#include +#include +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace ThreadBorderRouterManagement { + +constexpr size_t kBorderRouterNameMaxLength = 63; +constexpr size_t kBorderAgentIdLength = 16; + +class Delegate +{ +public: + Delegate() = default; + virtual ~Delegate() = default; + + class ActivateDatasetCallback + { + public: + ActivateDatasetCallback() = default; + virtual ~ActivateDatasetCallback() = default; + // If the dataset is set successfully, OnActivateDatasetComplete should be called with CHIP_NO_ERROR when the + // Border Router is attached to the Thread network. + // If an error occurs while setting the active dataset, this callback should be called with the error. + // The error input of this function could be SDK-range error for CHIP error or OpenThread-range error for Thread error. + virtual void OnActivateDatasetComplete(uint32_t sequenceNum, CHIP_ERROR error) = 0; + }; + + class AttributeChangeCallback + { + public: + AttributeChangeCallback() = default; + virtual ~AttributeChangeCallback() = default; + // If the attributes of the Thread Border Router Management is changed, ReportAttributeChanged should be called. + virtual void ReportAttributeChanged(AttributeId attributeId) = 0; + }; + + enum class DatasetType : uint8_t + { + kActive, + kPending, + }; + + virtual CHIP_ERROR Init(AttributeChangeCallback * attributeChangeCallback) = 0; + + // Get whether PanChange feature is supported for the Thread BR. + virtual bool GetPanChangeSupported() = 0; + + // Get the BorderRouterName of the Thread BR, which will also be the service name of Thread BR's MeshCOP service. + virtual void GetBorderRouterName(MutableCharSpan & borderRouterName) = 0; + + // Get the BorderAgentId of the Thread BR. + // @return + // -IncorrectState When Thread stack is not initialized. + // -InvalidArgument When the size of borderAgentId is not 16 bytes. + // -ThreadErrors When failing to get BorderAgentId. + virtual CHIP_ERROR GetBorderAgentId(MutableByteSpan & borderAgentId) = 0; + + // Get the Thread version which matches the value mapping defined in the "Version TLV" section of the Thread specification. + virtual uint16_t GetThreadVersion() = 0; + + // Get whether the associated IEEE 802.15.4 Thread interface is enabled or disabled. + virtual bool GetInterfaceEnabled() = 0; + + // Get the active dataset or the pending dataset. + // @return + // -IncorrectState When Thread stack is not initialized. + // -NotFound when failing to get the dataset. + virtual CHIP_ERROR GetDataset(Thread::OperationalDataset & dataset, DatasetType type) = 0; + + // There should be no active dataset configured when calling this API, otherwise we should use SetPendingDataset. + // The Delegate implementation must store the sequence number and pass it to OnActivateDatasetComplete. + virtual void SetActiveDataset(const Thread::OperationalDataset & activeDataset, uint32_t sequenceNum, + ActivateDatasetCallback * callback) = 0; + + // This function will check save whether there is active dataset configured. + virtual CHIP_ERROR CommitActiveDataset() = 0; + + // The function is called when Failsafe timer is triggered or when the Border Router reboots with a previous Failsafe timer + // started but not disarmed before reboot. The delegate implementation should check whether there is a previous SetActiveDataset + // request and revert the active dataset set by the previous SetActiveDataset. Since there should be no configured dataset when + // calling SetActiveDataset, this function will clear the active dataset to allow trying again a new SetActiveDataset operation. + // The delegate is allowed to call OnActivateDatasetComplete for the previous SetActiveDataset request even after this function + // is called as the sequence number passed to OnActivateDatasetComplete will be different. + virtual CHIP_ERROR RevertActiveDataset() = 0; + + virtual CHIP_ERROR SetPendingDataset(const Thread::OperationalDataset & pendingDataset) = 0; +}; + +} // namespace ThreadBorderRouterManagement +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/clusters/thread-network-directory-server/thread-network-directory-server.cpp b/src/app/clusters/thread-network-directory-server/thread-network-directory-server.cpp index d1f3de7c58bbc8..384686693b0a69 100644 --- a/src/app/clusters/thread-network-directory-server/thread-network-directory-server.cpp +++ b/src/app/clusters/thread-network-directory-server/thread-network-directory-server.cpp @@ -274,8 +274,10 @@ void ThreadNetworkDirectoryServer::HandleOperationalDatasetRequest( uint8_t datasetBuffer[kSizeOperationalDataset]; MutableByteSpan datasetSpan(datasetBuffer); + OperationalDatasetResponse::Type response; SuccessOrExit(err = mStorage.GetNetworkDataset(ExtendedPanId(req.extendedPanID), datasetSpan)); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, IMStatus::Success); + response.operationalDataset = datasetSpan; + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); return; exit: ChipLogError(Zcl, "GetOperationalDataset: %" CHIP_ERROR_FORMAT, err.Format()); 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 447a6646766452..6af249370e8fb8 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 @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -39,7 +40,7 @@ constexpr uint16_t kClusterRevision = 1; CHIP_ERROR Instance::Init() { - ReturnErrorOnFailure(InteractionModelEngine::GetInstance()->RegisterCommandHandler(this)); + ReturnErrorOnFailure(CommandHandlerInterfaceRegistry::RegisterCommandHandler(this)); VerifyOrReturnError(registerAttributeAccessOverride(this), CHIP_ERROR_INCORRECT_STATE); return CHIP_NO_ERROR; @@ -47,7 +48,7 @@ CHIP_ERROR Instance::Init() void Instance::Shutdown() { - InteractionModelEngine::GetInstance()->UnregisterCommandHandler(this); + CommandHandlerInterfaceRegistry::UnregisterCommandHandler(this); unregisterAttributeAccessOverride(this); } diff --git a/src/app/clusters/wifi-network-management-server/wifi-network-management-server.cpp b/src/app/clusters/wifi-network-management-server/wifi-network-management-server.cpp index f99ccdf5aafad3..43048498602ff8 100644 --- a/src/app/clusters/wifi-network-management-server/wifi-network-management-server.cpp +++ b/src/app/clusters/wifi-network-management-server/wifi-network-management-server.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -99,11 +100,20 @@ CHIP_ERROR WiFiNetworkManagementServer::SetNetworkCredentials(ByteSpan ssid, Byt VerifyOrDie(mPassphrase.SetLength(passphrase.size()) == CHIP_NO_ERROR); memcpy(mPassphrase.Bytes(), passphrase.data(), passphrase.size()); - // Note: The spec currently defines no way to signal a passphrase change if (ssidChanged) { MatterReportingAttributeChangeCallback(GetEndpointId(), WiFiNetworkManagement::Id, Ssid::Id); } + if (passphraseChanged) + { + mPassphraseSurrogate++; + System::Clock::Milliseconds64 realtime; + if (System::SystemClock().GetClock_RealTimeMS(realtime) == CHIP_NO_ERROR) + { + mPassphraseSurrogate = std::max(mPassphraseSurrogate, realtime.count()); + } + MatterReportingAttributeChangeCallback(GetEndpointId(), WiFiNetworkManagement::Id, PassphraseSurrogate::Id); + } return CHIP_NO_ERROR; } @@ -113,6 +123,8 @@ CHIP_ERROR WiFiNetworkManagementServer::Read(const ConcreteReadAttributePath & a { case Ssid::Id: return HaveNetworkCredentials() ? aEncoder.Encode(SsidSpan()) : aEncoder.EncodeNull(); + case PassphraseSurrogate::Id: + return HaveNetworkCredentials() ? aEncoder.Encode(mPassphraseSurrogate) : aEncoder.EncodeNull(); } return CHIP_NO_ERROR; } diff --git a/src/app/clusters/wifi-network-management-server/wifi-network-management-server.h b/src/app/clusters/wifi-network-management-server/wifi-network-management-server.h index ce438edf79ff73..c538c585e74899 100644 --- a/src/app/clusters/wifi-network-management-server/wifi-network-management-server.h +++ b/src/app/clusters/wifi-network-management-server/wifi-network-management-server.h @@ -57,6 +57,7 @@ class WiFiNetworkManagementServer : private AttributeAccessInterface, private Co static_assert(std::numeric_limits::max() >= sizeof(mSsid)); ByteSpan SsidSpan() const { return ByteSpan(mSsid, mSsidLen); } + uint64_t mPassphraseSurrogate = 0; Crypto::SensitiveDataBuffer<64> mPassphrase; ByteSpan PassphraseSpan() const { return mPassphrase.Span(); } diff --git a/src/app/common/templates/config-data.yaml b/src/app/common/templates/config-data.yaml index a3ae2dc621fd03..d77db76970818c 100644 --- a/src/app/common/templates/config-data.yaml +++ b/src/app/common/templates/config-data.yaml @@ -42,7 +42,9 @@ CommandHandlerInterfaceOnlyClusters: - Electrical Power Measurement - Electrical Energy Measurement - Wi-Fi Network Management + - Thread Border Router Management - Thread Network Directory + - Water Heater Management - Water Heater Mode # We need a more configurable way of deciding which clusters have which init functions.... diff --git a/src/app/tests/BUILD.gn b/src/app/tests/BUILD.gn index 59d8f2c325fd8c..e600fbeb3f1987 100644 --- a/src/app/tests/BUILD.gn +++ b/src/app/tests/BUILD.gn @@ -139,6 +139,17 @@ source_set("operational-state-test-srcs") { ] } +source_set("thread-border-router-management-test-srcs") { + sources = [ "${chip_root}/src/app/clusters/thread-border-router-management-server/thread-border-router-management-server.cpp" ] + + public_deps = [ + "${chip_root}/src/app", + "${chip_root}/src/app/common:cluster-objects", + "${chip_root}/src/app/util/mock:mock_ember", + "${chip_root}/src/lib/core", + ] +} + source_set("app-test-stubs") { sources = [ "test-ember-api.cpp", @@ -249,6 +260,9 @@ chip_test_suite("tests") { "${chip_root}/src/app/server", "${chip_root}/src/messaging/tests/echo:common", ] + } else if (!chip_fake_platform) { + test_sources += [ "TestThreadBorderRouterManagementCluster.cpp" ] + public_deps += [ ":thread-border-router-management-test-srcs" ] } if (!chip_fake_platform) { diff --git a/src/app/tests/TestThreadBorderRouterManagementCluster.cpp b/src/app/tests/TestThreadBorderRouterManagementCluster.cpp new file mode 100644 index 00000000000000..a6915824fb2ea2 --- /dev/null +++ b/src/app/tests/TestThreadBorderRouterManagementCluster.cpp @@ -0,0 +1,336 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace app { +namespace Clusters { + +namespace GeneralCommissioning { +// Mock function +void SetBreadcrumb(Attributes::Breadcrumb::TypeInfo::Type breadcrumb) {} +} // namespace GeneralCommissioning + +namespace ThreadBorderRouterManagement { + +class TestDelegate : public Delegate +{ +public: + TestDelegate() = default; + ~TestDelegate() = default; + + CHIP_ERROR Init(AttributeChangeCallback * callback) override { return CHIP_NO_ERROR; } + + bool GetPanChangeSupported() override { return mPanChangeSupported; } + + void GetBorderRouterName(MutableCharSpan & borderRouterName) override + { + size_t nameIndex = mUseInvalidBorderRouterName ? 1 : 0; + if (borderRouterName.size() >= strlen(kTestName[nameIndex])) + { + CopyCharSpanToMutableCharSpan(CharSpan(kTestName[nameIndex], strlen(kTestName[nameIndex])), borderRouterName); + } + } + + CHIP_ERROR GetBorderAgentId(MutableByteSpan & borderAgentId) override + { + if (borderAgentId.size() >= mTestBorderAgentIdLen) + { + CopySpanToMutableSpan(ByteSpan(kTestBorderAgentId, mTestBorderAgentIdLen), borderAgentId); + return CHIP_NO_ERROR; + } + return CHIP_ERROR_BUFFER_TOO_SMALL; + } + + uint16_t GetThreadVersion() override { return kTestThreadVersion; } + + bool GetInterfaceEnabled() override { return mInterfaceEnabled; } + + CHIP_ERROR GetDataset(Thread::OperationalDataset & dataset, DatasetType type) override + { + if (type == DatasetType::kActive && mStoredActiveDatasetLen) + { + dataset.Init(ByteSpan(mStoredActiveDataset, mStoredActiveDatasetLen)); + return CHIP_NO_ERROR; + } + if (type == DatasetType::kPending && mPendingDatasetLen) + { + dataset.Init(ByteSpan(mPendingDataset, mPendingDatasetLen)); + return CHIP_NO_ERROR; + } + return CHIP_IM_GLOBAL_STATUS(NotFound); + } + + void SetActiveDataset(const Thread::OperationalDataset & activeDataset, uint32_t sequenceNumber, + ActivateDatasetCallback * callback) override + { + memcpy(mActiveDataset, activeDataset.AsByteSpan().data(), activeDataset.AsByteSpan().size()); + mActiveDatasetLen = activeDataset.AsByteSpan().size(); + mCallback = callback; + mSetActiveDatasetCommandSequenceNum = sequenceNumber; + } + + CHIP_ERROR CommitActiveDataset() override { return CHIP_NO_ERROR; } + + CHIP_ERROR RevertActiveDataset() override + { + mStoredActiveDatasetLen = 0; + mInterfaceEnabled = false; + mCallback = nullptr; + return CHIP_NO_ERROR; + } + + CHIP_ERROR SetPendingDataset(const Thread::OperationalDataset & pendingDataset) override + { + memcpy(mPendingDataset, pendingDataset.AsByteSpan().data(), pendingDataset.AsByteSpan().size()); + mPendingDatasetLen = pendingDataset.AsByteSpan().size(); + return CHIP_NO_ERROR; + } + + void ActivateActiveDataset() + { + memcpy(mStoredActiveDataset, mActiveDataset, Thread::kSizeOperationalDataset); + mStoredActiveDatasetLen = mActiveDatasetLen; + mInterfaceEnabled = true; + if (mCallback) + { + mCallback->OnActivateDatasetComplete(mSetActiveDatasetCommandSequenceNum, CHIP_NO_ERROR); + } + mCallback = nullptr; + } + + bool mPanChangeSupported = true; + const char * kTestName[2] = { "TestName", "TestNameLength64________________________________________________" }; + const uint8_t kTestBorderAgentId[kBorderAgentIdLength] = { 0x0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }; + const uint16_t kTestThreadVersion = 4; + uint8_t mActiveDataset[Thread::kSizeOperationalDataset] = { 0 }; + size_t mActiveDatasetLen = 0; + uint8_t mStoredActiveDataset[Thread::kSizeOperationalDataset] = { 0 }; + size_t mStoredActiveDatasetLen = 0; + uint8_t mPendingDataset[Thread::kSizeOperationalDataset] = { 0 }; + size_t mPendingDatasetLen = 0; + bool mUseInvalidBorderRouterName = true; + size_t mTestBorderAgentIdLen = kBorderAgentIdLength - 1; + bool mInterfaceEnabled = false; + uint32_t mSetActiveDatasetCommandSequenceNum = 0; + ActivateDatasetCallback * mCallback = nullptr; +}; + +constexpr EndpointId kTestEndpointId = 1; +constexpr FabricIndex kTestAccessingFabricIndex = 1; +static FailSafeContext sTestFailsafeContext; +static TestDelegate sTestDelegate; +static ServerInstance sTestSeverInstance(kTestEndpointId, &sTestDelegate, sTestFailsafeContext); + +class TestSetActiveDatasetCommandHandler : public CommandHandler +{ +public: + TestSetActiveDatasetCommandHandler() : mClusterStatus(Protocols::InteractionModel::Status::Success) {} + CHIP_ERROR FallibleAddStatus(const ConcreteCommandPath & aRequestCommandPath, + const Protocols::InteractionModel::ClusterStatusCode & aStatus, const char * context = nullptr) + { + return CHIP_NO_ERROR; + } + + void AddStatus(const ConcreteCommandPath & aRequestCommandPath, const Protocols::InteractionModel::ClusterStatusCode & aStatus, + const char * context = nullptr) + { + mClusterStatus = aStatus; + } + + FabricIndex GetAccessingFabricIndex() const { return kTestAccessingFabricIndex; } + + CHIP_ERROR AddResponseData(const ConcreteCommandPath & aRequestCommandPath, CommandId aResponseCommandId, + const DataModel::EncodableToTLV & aEncodable) + { + return CHIP_NO_ERROR; + } + + void AddResponse(const ConcreteCommandPath & aRequestCommandPath, CommandId aResponseCommandId, + const DataModel::EncodableToTLV & aEncodable) + {} + + bool IsTimedInvoke() const { return false; } + + void FlushAcksRightAwayOnSlowCommand() {} + + Access::SubjectDescriptor GetSubjectDescriptor() const + { + Access::SubjectDescriptor subjectDescriptor = { kUndefinedFabricIndex, Access::AuthMode::kNone, kUndefinedNodeId, + kUndefinedCATs }; + return subjectDescriptor; + } + + Messaging::ExchangeContext * GetExchangeContext() const { return nullptr; } + + Protocols::InteractionModel::ClusterStatusCode mClusterStatus; +}; + +TestSetActiveDatasetCommandHandler sTestCommandHandler; + +class TestThreadBorderRouterManagementCluster : public ::testing::Test +{ +public: + static void SetUpTestSuite() + { + ASSERT_EQ(Platform::MemoryInit(), CHIP_NO_ERROR); + ASSERT_EQ(DeviceLayer::PlatformMgr().InitChipStack(), CHIP_NO_ERROR); + } + + static void TearDownTestSuite() + { + DeviceLayer::PlatformMgr().Shutdown(); + Platform::MemoryShutdown(); + } + + void TestAttributeRead(); + void TestCommandHandle(); +}; + +// Test ReadXX functions in ThreadBorderRouterManagement ServerInstance +TEST_F_FROM_FIXTURE(TestThreadBorderRouterManagementCluster, TestAttributeRead) +{ + // FeatureMap attribute + BitFlags featureMap = BitFlags(); + // Make the PAN change feature supported in Test delegate. + sTestDelegate.mPanChangeSupported = true; + sTestSeverInstance.ReadFeatureMap(featureMap); + EXPECT_TRUE(featureMap.Has(Feature::kPANChange)); + // Make the PAN change feature unsupported in Test delegate. + sTestDelegate.mPanChangeSupported = false; + featureMap.ClearAll(); + sTestSeverInstance.ReadFeatureMap(featureMap); + EXPECT_FALSE(featureMap.Has(Feature::kPANChange)); + // BorderRouterName attribute + // Use invalid BR name + sTestDelegate.mUseInvalidBorderRouterName = true; + char borderRouterName[kBorderRouterNameMaxLength + 10] = { 0 }; + MutableCharSpan nameSpan(borderRouterName); + EXPECT_EQ(sTestSeverInstance.ReadBorderRouterName(nameSpan), CHIP_IM_GLOBAL_STATUS(Failure)); + nameSpan = MutableCharSpan(borderRouterName); + // Use valid BR name + sTestDelegate.mUseInvalidBorderRouterName = false; + EXPECT_EQ(sTestSeverInstance.ReadBorderRouterName(nameSpan), CHIP_NO_ERROR); + EXPECT_TRUE(nameSpan.data_equal(CharSpan("TestName", strlen("TestName")))); + // BorderAgentId attribute + uint8_t borderAgentId[kBorderAgentIdLength] = { 0 }; + MutableByteSpan agentIdSpan(borderAgentId); + // Use invalid border agent id + sTestDelegate.mTestBorderAgentIdLen = kBorderAgentIdLength - 1; + EXPECT_EQ(sTestSeverInstance.ReadBorderAgentID(agentIdSpan), CHIP_IM_GLOBAL_STATUS(Failure)); + agentIdSpan = MutableByteSpan(borderAgentId); + // Use valid border agent id + sTestDelegate.mTestBorderAgentIdLen = kBorderAgentIdLength; + EXPECT_EQ(sTestSeverInstance.ReadBorderAgentID(agentIdSpan), CHIP_NO_ERROR); + EXPECT_TRUE(agentIdSpan.data_equal(ByteSpan(sTestDelegate.kTestBorderAgentId))); + // ActiveDatasetTimestamp attribute + // The active dataset timestamp should be null when no active dataset is configured + Optional timestamp = sTestSeverInstance.ReadActiveDatasetTimestamp(); + EXPECT_FALSE(timestamp.HasValue()); +} + +TEST_F_FROM_FIXTURE(TestThreadBorderRouterManagementCluster, TestCommandHandle) +{ + // Test GetActiveDatasetRequest and GetPendingDatasetRequest commands + Thread::OperationalDataset dataset; + using DatasetType = Delegate::DatasetType; + using Status = Protocols::InteractionModel::Status; + // The GetDataset requests should over CASE session. + EXPECT_EQ(sTestSeverInstance.HandleGetDatasetRequest(false /* isOverCASESession */, DatasetType::kActive, dataset), + Status::UnsupportedAccess); + EXPECT_EQ(sTestSeverInstance.HandleGetDatasetRequest(false, DatasetType::kPending, dataset), Status::UnsupportedAccess); + // The GetDataset should return NotFound when no dataset is configured. + EXPECT_EQ(sTestSeverInstance.HandleGetDatasetRequest(true, DatasetType::kActive, dataset), Status::NotFound); + EXPECT_EQ(sTestSeverInstance.HandleGetDatasetRequest(true, DatasetType::kPending, dataset), Status::NotFound); + // Test SetActiveDatasetRequest + ThreadBorderRouterManagement::Commands::SetActiveDatasetRequest::DecodableType req1; + uint8_t invalidDataset[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 }; + uint8_t validDataset[] = { 0x0e, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0b, 0x35, 0x06, + 0x00, 0x04, 0x00, 0x1f, 0xff, 0xe0, 0x02, 0x08, 0xde, 0xaa, 0x00, 0xbe, 0xef, 0x00, 0xca, 0xef, 0x07, + 0x08, 0xfd, 0xde, 0xad, 0x00, 0xbe, 0xef, 0x00, 0x00, 0x05, 0x10, 0xb7, 0x28, 0x08, 0x04, 0x85, 0xcf, + 0xc5, 0x25, 0x7f, 0x68, 0x4c, 0x54, 0x9d, 0x6a, 0x57, 0x5e, 0x03, 0x0a, 0x4f, 0x70, 0x65, 0x6e, 0x54, + 0x68, 0x72, 0x65, 0x61, 0x64, 0x01, 0x02, 0xc1, 0x15, 0x04, 0x10, 0xcb, 0x13, 0x47, 0xeb, 0x0c, 0xd4, + 0xb3, 0x5c, 0xd1, 0x42, 0xda, 0x5e, 0x6d, 0xf1, 0x8b, 0x88, 0x0c, 0x04, 0x02, 0xa0, 0xf7, 0xf8 }; + Optional activeDatasetTimestamp = chip::NullOptional; + activeDatasetTimestamp = sTestSeverInstance.ReadActiveDatasetTimestamp(); + EXPECT_FALSE(activeDatasetTimestamp.HasValue()); + req1.activeDataset = ByteSpan(invalidDataset); + // SetActiveDatasetRequest is FailsafeRequired. + EXPECT_EQ(sTestSeverInstance.HandleSetActiveDatasetRequest(&sTestCommandHandler, req1), Status::FailsafeRequired); + EXPECT_EQ(sTestFailsafeContext.ArmFailSafe(kTestAccessingFabricIndex, System::Clock::Seconds16(1)), CHIP_NO_ERROR); + // SetActiveDatasetRequest should return InvalidCommand when dataset is invalid. + EXPECT_EQ(sTestSeverInstance.HandleSetActiveDatasetRequest(&sTestCommandHandler, req1), Status::InvalidCommand); + req1.activeDataset = ByteSpan(validDataset); + EXPECT_EQ(sTestSeverInstance.HandleSetActiveDatasetRequest(&sTestCommandHandler, req1), Status::Success); + // When the Server is handling a SetActiveDatasetRequest command, it should return Busy after receiving another one. + EXPECT_EQ(sTestSeverInstance.HandleSetActiveDatasetRequest(&sTestCommandHandler, req1), Status::Busy); + EXPECT_FALSE(sTestDelegate.mInterfaceEnabled); + EXPECT_EQ(sTestDelegate.mSetActiveDatasetCommandSequenceNum, static_cast(1)); + // Activate the dataset. + sTestDelegate.ActivateActiveDataset(); + EXPECT_EQ(sTestCommandHandler.mClusterStatus, + Protocols::InteractionModel::ClusterStatusCode(Protocols::InteractionModel::Status::Success)); + sTestFailsafeContext.DisarmFailSafe(); + // The Dataset should be updated. + EXPECT_EQ(sTestSeverInstance.HandleGetDatasetRequest(true, DatasetType::kActive, dataset), Status::Success); + EXPECT_TRUE(dataset.AsByteSpan().data_equal(ByteSpan(validDataset))); + EXPECT_TRUE(sTestDelegate.mInterfaceEnabled); + activeDatasetTimestamp = sTestSeverInstance.ReadActiveDatasetTimestamp(); + // activeDatasetTimestamp should have value. + EXPECT_TRUE(activeDatasetTimestamp.HasValue()); + EXPECT_EQ(sTestFailsafeContext.ArmFailSafe(kTestAccessingFabricIndex, System::Clock::Seconds16(1)), CHIP_NO_ERROR); + // When ActiveDatasetTimestamp is not null, the set active dataset request should return InvalidInState. + EXPECT_EQ(sTestSeverInstance.HandleSetActiveDatasetRequest(&sTestCommandHandler, req1), Status::InvalidInState); + sTestFailsafeContext.DisarmFailSafe(); + // Test SetPendingDatasetRequest command + Commands::SetPendingDatasetRequest::DecodableType req2; + sTestDelegate.mPanChangeSupported = false; + req2.pendingDataset = ByteSpan(validDataset); + // SetPendingDatasetRequest is supported when PANChange feature is enabled. + EXPECT_EQ(sTestSeverInstance.HandleSetPendingDatasetRequest(req2), Status::UnsupportedCommand); + sTestDelegate.mPanChangeSupported = true; + req2.pendingDataset = ByteSpan(invalidDataset); + // SetPendingDatasetRequest should return InvalidCommand when dataset is invalid. + EXPECT_EQ(sTestSeverInstance.HandleSetPendingDatasetRequest(req2), Status::InvalidCommand); + req2.pendingDataset = ByteSpan(validDataset); + // Success SetPendingDatasetRequest + EXPECT_EQ(sTestSeverInstance.HandleSetPendingDatasetRequest(req2), Status::Success); + EXPECT_EQ(sTestSeverInstance.HandleGetDatasetRequest(true, DatasetType::kPending, dataset), Status::Success); + EXPECT_TRUE(dataset.AsByteSpan().data_equal(ByteSpan(validDataset))); +} + +} // namespace ThreadBorderRouterManagement +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/tests/suites/certification/PICS.yaml b/src/app/tests/suites/certification/PICS.yaml index cf0159804aeee3..3edf69e6a3b739 100644 --- a/src/app/tests/suites/certification/PICS.yaml +++ b/src/app/tests/suites/certification/PICS.yaml @@ -10199,3 +10199,19 @@ PICS: - label: "Does the device implement the ActiveEndpoints attribute?" id: PWRTL.S.A0001 + + # + # Thread Network Directory Cluster + # + - label: + "Does the device implement the Thread Network Directory cluster as a + server" + id: THNETDIR.S + + # + # Wi-Fi Network Management Cluster + # + - label: + "Does the device implement the Wi-Fi Network Management cluster as a + server" + id: WIFINM.S diff --git a/src/app/tests/suites/certification/Test_TC_S_2_2.yaml b/src/app/tests/suites/certification/Test_TC_S_2_2.yaml index 81011a13251a70..bdb3d2b8ad90db 100644 --- a/src/app/tests/suites/certification/Test_TC_S_2_2.yaml +++ b/src/app/tests/suites/certification/Test_TC_S_2_2.yaml @@ -302,6 +302,27 @@ tests: - name: "SceneID" value: 0x01 + - label: + "Step 2d: TH sends a StoreScene command to DUT with the GroupID field + set to G1 and the SceneID field set to 0xFF, which is outside of the + constraints for a SceneID." + PICS: S.S.C04.Rsp + command: "StoreScene" + arguments: + values: + - name: "GroupID" + value: GI + - name: "SceneID" + value: 0xFF + response: + values: + - name: "Status" + value: 0x87 + - name: "GroupID" + value: GI + - name: "SceneID" + value: 0xFF + - label: "Step 3a: TH configures AC2 on DUT for all implemented application clusters supporting scenes." @@ -444,6 +465,21 @@ tests: - name: "SceneID" value: 0x01 + - label: + "Step 4e: TH sends a RecallScene command to DUT with the GroupID field + set to G1 and the SceneID field set to 0xFF, which is outside of the + constraints for a SceneID." + PICS: S.S.C05.Rsp + command: "RecallScene" + arguments: + values: + - name: "GroupID" + value: G1 + - name: "SceneID" + value: 0xFF + response: + error: CONSTRAINT_ERROR + - label: "Step 5a: TH sends a ViewScene command to DUT with the GroupID field set to G1 and the SceneID field set to 0x01." @@ -555,6 +591,27 @@ tests: - name: "SceneID" value: 0xFE + - label: + "Step 5d: TH sends a ViewScene command to DUT with the GroupID field + set to G1 and the SceneID field set to 0xFF, which is outside of the + constraints for a SceneID." + PICS: S.S.C01.Rsp + command: "ViewScene" + arguments: + values: + - name: "GroupID" + value: G1 + - name: "SceneID" + value: 0xFF + response: + values: + - name: "Status" + value: 0x87 + - name: "GroupID" + value: G1 + - name: "SceneID" + value: 0xFF + - label: "Step 6: TH sends a GetSceneMembership command to DUT with the GroupID field set to G1." @@ -734,7 +791,7 @@ tests: "Step 8d: TH sends a AddScene command to DUT with the GroupID field set to G1, the SceneID field set to 0x01, the TransitionTime field set to 70 000 000 (70 000s) and no extension field sets. This should fail - and return a status of 0x85 (INVALID_COMMAND)." + and return a status of 0x87 (CONSTRAINT_ERROR)." PICS: S.S.C00.Rsp command: "AddScene" arguments: @@ -752,7 +809,7 @@ tests: response: values: - name: "Status" - value: 0x85 + value: 0x87 - name: "GroupID" value: G1 - name: "SceneID" @@ -762,7 +819,7 @@ tests: "Step 8e: TH sends a AddScene command to DUT with the GroupID field set to G1, the SceneID field set to 0x01, the TransitionTime field set to 60 000 001 (60 000.001s) and no extension field sets. This should - fail and return a status of 0x85 (INVALID_COMMAND)." + fail and return a status of 0x87 (CONSTRAINT_ERROR)." PICS: S.S.C00.Rsp command: "AddScene" arguments: @@ -780,12 +837,41 @@ tests: response: values: - name: "Status" - value: 0x85 + value: 0x87 - name: "GroupID" value: G1 - name: "SceneID" value: 0x01 + - label: + "Step 8f: TH sends a AddScene command to DUT with the GroupID field + set to G1, the SceneID field set to 0xFF, which is outside of the + constraints for a SceneID, the TransitionTime field set to 1000 (1s) + and no extension field sets. This should fail and return a status of + 0x87 (CONSTRAINT_ERROR)." + PICS: S.S.C00.Rsp + command: "AddScene" + arguments: + values: + - name: "GroupID" + value: G1 + - name: "SceneID" + value: 0xFF + - name: "TransitionTime" + value: 1000 + - name: "SceneName" + value: "Scene1" + - name: "ExtensionFieldSets" + value: [] + response: + values: + - name: "Status" + value: 0x87 + - name: "GroupID" + value: G1 + - name: "SceneID" + value: 0xFF + - label: "Step 9a: TH sends a RemoveScene command to DUT with the GroupID field set to G1 and the SceneID field set to 0x01." @@ -848,7 +934,28 @@ tests: value: 0x01 - label: - "Step 9d: TH sends a GetSceneMembership command to DUT with the + "Step 9d: TH sends a RemoveScene command to DUT with the GroupID field + set to G1 and the SceneID field set to 0xFF, which is outside of the + constraints for a SceneID." + PICS: S.S.C02.Rsp + command: "RemoveScene" + arguments: + values: + - name: "GroupID" + value: GI + - name: "SceneID" + value: 0xFF + response: + values: + - name: "Status" + value: 0x87 + - name: "GroupID" + value: GI + - name: "SceneID" + value: 0xFF + + - label: + "Step 9e: TH sends a GetSceneMembership command to DUT with the GroupID field set to G1." PICS: S.S.C06.Rsp command: "GetSceneMembership" diff --git a/src/app/tests/suites/certification/Test_TC_THNETDIR_2_1.yaml b/src/app/tests/suites/certification/Test_TC_THNETDIR_2_1.yaml new file mode 100644 index 00000000000000..3a2925dc424175 --- /dev/null +++ b/src/app/tests/suites/certification/Test_TC_THNETDIR_2_1.yaml @@ -0,0 +1,51 @@ +# 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-THNETDIR-2.1] Simple Attributes check with DUT as Server" + +PICS: + - THNETDIR.S + +config: + nodeId: 0x12344321 + cluster: Thread Network Directory + endpoint: 1 + +tests: + - label: "Wait for the commissioned device to be retrieved" + cluster: DelayCommands + command: WaitForCommissionee + arguments: + values: + - name: nodeId + value: nodeId + + - label: "TH reads SupportedFabrics attribute from DUT" + command: readAttribute + endpoint: 0 + cluster: Operational Credentials + attribute: SupportedFabrics + response: + saveAs: supportedFabrics + constraints: + type: int8u + + - label: "TH reads ThreadNetworkTableSize attribute from DUT" + command: readAttribute + attribute: ThreadNetworkTableSize + response: + constraints: + type: int8u + minValue: 10 # assume 5 supported fabrics + # python: value >= 2 * supportedFabrics diff --git a/src/app/tests/suites/certification/Test_TC_THNETDIR_2_2.yaml b/src/app/tests/suites/certification/Test_TC_THNETDIR_2_2.yaml new file mode 100644 index 00000000000000..be0ec89952d25e --- /dev/null +++ b/src/app/tests/suites/certification/Test_TC_THNETDIR_2_2.yaml @@ -0,0 +1,225 @@ +# 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-THNETDIR-2.2] Verification for Add/Remove/Get commands" + +PICS: + - THNETDIR.S + +config: + nodeId: 0x12344321 + cluster: Thread Network Directory + endpoint: 1 + + # Note: TestNetwork* values need to match what's encoded in TestNetworkDataset + TestNetworkDataset: + type: octet_string + defaultValue: "hex:0e080000000000000001000300000f350407fff800020839758ec8144b07fb0708fdf1f1add0797dc00510f366cec7a446bab978d90d27abe38f23030f4f70656e5468726561642d353933380102593804103ca67c969efb0d0c74a4d8ee923b576c0c0402a0f7f8" + TestNetworkExtendedPanId: + type: octet_string + defaultValue: "hex:39758ec8144b07fb" + TestNetworkName: "OpenThread-5938" + TestNetworkChannel: 15 + TestNetworkActiveTimestamp: 1 + +tests: + - label: "Wait for the commissioned device to be retrieved" + cluster: DelayCommands + command: WaitForCommissionee + arguments: + values: + - name: nodeId + value: nodeId + + - label: "TH reads ThreadNetworks attribute from DUT" + command: readAttribute + attribute: ThreadNetworks + response: + saveAs: initialNetworks + constraints: + type: list + + - label: "TH reads PreferredExtendedPanID attribute from DUT" + command: readAttribute + attribute: PreferredExtendedPanID + response: + saveAs: initialPreferredExtendedPanID + constraints: + type: octet_string + minLength: 8 + maxLength: 8 + notValue: TestNetworkExtendedPanId + + - label: + "TH writes ExtendedPanID from TestNetwork to PreferredExtendedPanID on + DUT" + command: writeAttribute + attribute: PreferredExtendedPanID + arguments: + value: TestNetworkExtendedPanId + response: + error: CONSTRAINT_ERROR # TestNetwork is not in ThreadNetworks + + - label: + "TH reads PreferredExtendedPanID attribute from DUT, verifying that + the value did not change" + command: readAttribute + attribute: PreferredExtendedPanID + response: + value: initialPreferredExtendedPanID + + - label: + "TH sends GetOperationalDataset command to DUT with ExtendedPanID of + TestNetwork" + command: GetOperationalDataset + arguments: + values: + - name: ExtendedPanID + value: TestNetworkExtendedPanId + response: + error: NOT_FOUND + + # TODO: Currently fails with darwin-framework-tool because it automatically performs a timed invoke + # - label: "TH sends AddNetwork command to DUT without a timed interaction" + # command: AddNetwork + # arguments: + # values: + # - name: OperationalDataset + # value: TestNetworkDataset + # response: + # error: NEEDS_TIMED_INTERACTION + + - label: "TH sends AddNetwork command to DUT with TestNetwork dataset" + command: AddNetwork + timedInteractionTimeoutMs: 2000 + arguments: + values: + - name: OperationalDataset + value: TestNetworkDataset + + - label: + "TH reads ThreadNetworks attribute from DUT, verifying that the + network has been added" + command: readAttribute + attribute: ThreadNetworks + response: + constraints: + type: list + # python: | + # # Split the list into test (our TestNetwork) and rest (everything else) + # test = next((n for n in value if n['ExtendedPanID'] == TestNetworkExtendedPanId), None) + # rest = [n for n in value if n != test] + # # Check test has the expected values and rest == initialNetworks (ignoring order) + # return (test is not None and + # test['NetworkName'] == TestNetworkName and + # test['Channel'] == TestNetworkChannel and + # test['ActiveTimestamp'] == TestNetworkActiveTimestamp and + # len(value) == len(initialNetworks) + 1 and + # len(rest) == len(initialNetworks) and + # all(n in initialNetworks for n in rest)) + + - label: + "TH sends GetOperationalDataset command to DUT with ExtendedPanID from + TestNetwork" + command: GetOperationalDataset + arguments: + values: + - name: ExtendedPanID + value: TestNetworkExtendedPanId + response: + values: + - name: OperationalDataset + value: TestNetworkDataset + + - label: + "TH writes ExtendedPanID from TestNetwork to PreferredExtendedPanID on + DUT" + command: writeAttribute + attribute: PreferredExtendedPanID + arguments: + value: TestNetworkExtendedPanId + + - label: + "TH reads PreferredExtendedPanID attribute from DUT, verifying that + the value was written" + command: readAttribute + attribute: PreferredExtendedPanID + response: + value: TestNetworkExtendedPanId + + # TODO: Currently fails with darwin-framework-tool because it automatically performs a timed invoke + # - label: "TH sends RemoveNetwork command to DUT without a timed interaction" + # command: RemoveNetwork + # arguments: + # values: + # - name: ExtendedPanID + # value: TestNetworkExtendedPanId + # response: + # error: NEEDS_TIMED_INTERACTION + + - label: + "TH sends RemoveNetwork command to DUT with ExtendedPanID of + TestNetwork while it is the preferred network" + command: RemoveNetwork + timedInteractionTimeoutMs: 2000 + arguments: + values: + - name: ExtendedPanID + value: TestNetworkExtendedPanId + response: + error: CONSTRAINT_ERROR # Preferred network cannot be removed + + - label: "TH writes null to PreferredExtendedPanID on DUT" + command: writeAttribute + attribute: PreferredExtendedPanID + arguments: + value: null + + - label: + "TH reads PreferredExtendedPanID attribute from DUT, verifying that + the value was cleared" + command: readAttribute + attribute: PreferredExtendedPanID + response: + value: null + + - label: + "TH sends RemoveNetwork command to DUT with ExtendedPanID of + TestNetwork" + command: RemoveNetwork + timedInteractionTimeoutMs: 2000 + arguments: + values: + - name: ExtendedPanID + value: TestNetworkExtendedPanId + + - label: + "TH reads ThreadNetworks attribute from DUT, verifying that the + network was removed" + command: readAttribute + attribute: ThreadNetworks + response: + constraints: + type: list + # python: | + # # value == initialNetworks (ignoring order) + # return (len(value) == len(initialNetworks) and + # all(n in initialNetworks for n in value)) + + - label: + "TH writes PreferredExtendedPanID to DUT, restoring the initial value" + command: writeAttribute + attribute: PreferredExtendedPanID + arguments: + value: initialPreferredExtendedPanID diff --git a/src/app/tests/suites/certification/Test_TC_WIFINM_2_1.yaml b/src/app/tests/suites/certification/Test_TC_WIFINM_2_1.yaml new file mode 100644 index 00000000000000..0f9f71157de0ea --- /dev/null +++ b/src/app/tests/suites/certification/Test_TC_WIFINM_2_1.yaml @@ -0,0 +1,63 @@ +# 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-WIFINM-2.1] Attributes and commands with DUT as Server" + +PICS: + - WIFINM.S + +config: + nodeId: 0x12344321 + cluster: WiFi Network Management + endpoint: 1 + +# Note: This test assumes the DUT has an active Wi-Fi network, i.e. +# SSID is not null and NetworkPassphraseRequest returns a passphrase. +tests: + - label: "Wait for the commissioned device to be retrieved" + cluster: DelayCommands + command: WaitForCommissionee + arguments: + values: + - name: nodeId + value: nodeId + + - label: "TH reads the SSID attribute from the DUT" + command: readAttribute + attribute: SSID + response: + constraints: + type: octet_string + hasValue: true + minLength: 1 + maxLength: 32 + + - label: "TH reads the PassphraseSurrogate attribute from the DUT" + command: readAttribute + attribute: PassphraseSurrogate + response: + constraints: + type: int64u + hasValue: true + + - label: "TH sends the NetworkPassphraseRequest command to the DUT" + command: NetworkPassphraseRequest + response: + values: + - name: Passphrase + constraints: + type: octet_string + hasValue: true + minLength: 8 + maxLength: 64 diff --git a/src/app/tests/suites/certification/ci-pics-values b/src/app/tests/suites/certification/ci-pics-values index 207999a74778b1..9ea99c627f16f4 100644 --- a/src/app/tests/suites/certification/ci-pics-values +++ b/src/app/tests/suites/certification/ci-pics-values @@ -2987,4 +2987,10 @@ PWRTL.S.A0001=1 PWRTL.S.F00=0 PWRTL.S.F01=0 PWRTL.S.F02=1 -PWRTL.S.F03=1 \ No newline at end of file +PWRTL.S.F03=1 + +# Thread Network Directory Cluster +THNETDIR.S=1 + +# Wi-Fi Network Management Cluster +WIFINM.S=1 diff --git a/src/app/zap-templates/zcl/data-model/chip/thread-network-directory-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/thread-network-directory-cluster.xml index e97cbc931e6b0e..3a6445b15bbad3 100644 --- a/src/app/zap-templates/zcl/data-model/chip/thread-network-directory-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/thread-network-directory-cluster.xml @@ -17,7 +17,7 @@ limitations under the License. - + @@ -25,7 +25,7 @@ limitations under the License. - + Network Infrastructure Thread Network Directory 0x0453 @@ -59,7 +59,7 @@ limitations under the License. - + Retrieves a Thread Operational Dataset from the ThreadNetworks list. diff --git a/src/app/zap-templates/zcl/data-model/chip/wifi-network-management-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/wifi-network-management-cluster.xml index f05faad4dae4d2..8c8b4d40c3abdd 100644 --- a/src/app/zap-templates/zcl/data-model/chip/wifi-network-management-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/wifi-network-management-cluster.xml @@ -17,7 +17,7 @@ limitations under the License. - + Network Infrastructure Wi-Fi Network Management 0x0451 @@ -30,11 +30,15 @@ limitations under the License. - SSID + SSID + + PassphraseSurrogate + + Request the current WPA-Personal passphrase or PSK associated with the managed Wi-Fi network. - + This is the response to a NetworkPassphraseRequest. diff --git a/src/app/zap-templates/zcl/zcl-with-test-extensions.json b/src/app/zap-templates/zcl/zcl-with-test-extensions.json index b37ee8e36c2324..dc7242fc7fdb7d 100644 --- a/src/app/zap-templates/zcl/zcl-with-test-extensions.json +++ b/src/app/zap-templates/zcl/zcl-with-test-extensions.json @@ -303,6 +303,14 @@ "general_error_boolean", "cluster_error_boolean" ], + "Thread Border Router Management": [ + "BorderRouterName", + "BorderAgentID", + "ThreadVersion", + "InterfaceEnabled", + "ActiveDatasetTimestamp", + "FeatureMap" + ], "Thread Network Diagnostics": [ "Channel", "RoutingRole", @@ -658,7 +666,7 @@ "ClusterRevision" ], "Water Heater Mode": ["SupportedModes", "CurrentMode", "FeatureMap"], - "Wi-Fi Network Management": ["SSID"], + "Wi-Fi Network Management": ["SSID", "PassphraseSurrogate"], "Thread Network Directory": [ "PreferredExtendedPanID", "ThreadNetworks", diff --git a/src/app/zap-templates/zcl/zcl.json b/src/app/zap-templates/zcl/zcl.json index 8f73dfa063c941..9f0c3cccb9fcef 100644 --- a/src/app/zap-templates/zcl/zcl.json +++ b/src/app/zap-templates/zcl/zcl.json @@ -301,6 +301,14 @@ "general_error_boolean", "cluster_error_boolean" ], + "Thread Border Router Management": [ + "BorderRouterName", + "BorderAgentID", + "ThreadVersion", + "InterfaceEnabled", + "ActiveDatasetTimestamp", + "FeatureMap" + ], "Thread Network Diagnostics": [ "Channel", "RoutingRole", @@ -656,7 +664,7 @@ "ClusterRevision" ], "Water Heater Mode": ["SupportedModes", "CurrentMode", "FeatureMap"], - "Wi-Fi Network Management": ["SSID"], + "Wi-Fi Network Management": ["SSID", "PassphraseSurrogate"], "Thread Network Directory": [ "PreferredExtendedPanID", "ThreadNetworks", diff --git a/src/app/zap_cluster_list.json b/src/app/zap_cluster_list.json index eb9db222477ffe..212246eefd4a21 100644 --- a/src/app/zap_cluster_list.json +++ b/src/app/zap_cluster_list.json @@ -294,7 +294,9 @@ "THERMOSTAT_USER_INTERFACE_CONFIGURATION_CLUSTER": [ "thermostat-user-interface-configuration-server" ], - "THREAD_BORDER_ROUTER_MANAGEMENT_CLUSTER": [], + "THREAD_BORDER_ROUTER_MANAGEMENT_CLUSTER": [ + "thread-border-router-management-server" + ], "THREAD_NETWORK_DIAGNOSTICS_CLUSTER": [ "thread-network-diagnostics-server" ], diff --git a/src/controller/data_model/controller-clusters.matter b/src/controller/data_model/controller-clusters.matter index b65a7372d20e00..0b4f3055386c96 100644 --- a/src/controller/data_model/controller-clusters.matter +++ b/src/controller/data_model/controller-clusters.matter @@ -8126,10 +8126,11 @@ cluster RadonConcentrationMeasurement = 1071 { } /** Functionality to retrieve operational information about a managed Wi-Fi network. */ -cluster WiFiNetworkManagement = 1105 { +provisional cluster WiFiNetworkManagement = 1105 { revision 1; - readonly attribute nullable octet_string<32> ssid = 1; + readonly attribute nullable octet_string<32> ssid = 0; + readonly attribute access(read: manage) nullable int64u passphraseSurrogate = 1; readonly attribute command_id generatedCommandList[] = 65528; readonly attribute command_id acceptedCommandList[] = 65529; readonly attribute event_id eventList[] = 65530; @@ -8142,7 +8143,7 @@ cluster WiFiNetworkManagement = 1105 { } /** Request the current WPA-Personal passphrase or PSK associated with the managed Wi-Fi network. */ - command access(invoke: administer) NetworkPassphraseRequest(): NetworkPassphraseResponse = 0; + command access(invoke: manage) NetworkPassphraseRequest(): NetworkPassphraseResponse = 0; } /** Manage the Thread network of Thread Border Router */ @@ -8189,7 +8190,7 @@ provisional cluster ThreadBorderRouterManagement = 1106 { } /** Manages the names and credentials of Thread networks visible to the user. */ -cluster ThreadNetworkDirectory = 1107 { +provisional cluster ThreadNetworkDirectory = 1107 { revision 1; struct ThreadNetworkStruct { @@ -8230,7 +8231,7 @@ cluster ThreadNetworkDirectory = 1107 { /** Removes an entry from the ThreadNetworks list. */ timed command access(invoke: manage) RemoveNetwork(RemoveNetworkRequest): DefaultSuccess = 1; /** Retrieves a Thread Operational Dataset from the ThreadNetworks list. */ - timed command GetOperationalDataset(GetOperationalDatasetRequest): OperationalDatasetResponse = 2; + command GetOperationalDataset(GetOperationalDatasetRequest): OperationalDatasetResponse = 2; } /** This cluster provides an interface for managing low power mode on a device that supports the Wake On LAN protocol. */ diff --git a/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java b/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java index 552651bd4c5b3f..2a97c61a724b31 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java @@ -54065,7 +54065,8 @@ public void onSuccess(byte[] tlv) { public static class WiFiNetworkManagementCluster extends BaseChipCluster { public static final long CLUSTER_ID = 1105L; - private static final long SSID_ATTRIBUTE_ID = 1L; + private static final long SSID_ATTRIBUTE_ID = 0L; + private static final long PASSPHRASE_SURROGATE_ATTRIBUTE_ID = 1L; private static final long GENERATED_COMMAND_LIST_ATTRIBUTE_ID = 65528L; private static final long ACCEPTED_COMMAND_LIST_ATTRIBUTE_ID = 65529L; private static final long EVENT_LIST_ATTRIBUTE_ID = 65530L; @@ -54117,6 +54118,10 @@ public interface SsidAttributeCallback extends BaseAttributeCallback { void onSuccess(@Nullable byte[] value); } + public interface PassphraseSurrogateAttributeCallback extends BaseAttributeCallback { + void onSuccess(@Nullable Long value); + } + public interface GeneratedCommandListAttributeCallback extends BaseAttributeCallback { void onSuccess(List value); } @@ -54159,6 +54164,32 @@ public void onSuccess(byte[] tlv) { }, SSID_ATTRIBUTE_ID, minInterval, maxInterval); } + public void readPassphraseSurrogateAttribute( + PassphraseSurrogateAttributeCallback callback) { + ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, PASSPHRASE_SURROGATE_ATTRIBUTE_ID); + + readAttribute(new ReportCallbackImpl(callback, path) { + @Override + public void onSuccess(byte[] tlv) { + @Nullable Long value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv); + callback.onSuccess(value); + } + }, PASSPHRASE_SURROGATE_ATTRIBUTE_ID, true); + } + + public void subscribePassphraseSurrogateAttribute( + PassphraseSurrogateAttributeCallback callback, int minInterval, int maxInterval) { + ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, PASSPHRASE_SURROGATE_ATTRIBUTE_ID); + + subscribeAttribute(new ReportCallbackImpl(callback, path) { + @Override + public void onSuccess(byte[] tlv) { + @Nullable Long value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv); + callback.onSuccess(value); + } + }, PASSPHRASE_SURROGATE_ATTRIBUTE_ID, minInterval, maxInterval); + } + public void readGeneratedCommandListAttribute( GeneratedCommandListAttributeCallback callback) { ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, GENERATED_COMMAND_LIST_ATTRIBUTE_ID); @@ -54805,6 +54836,9 @@ public void onResponse(StructType invokeStructValue) { }}, commandId, commandArgs, timedInvokeTimeoutMs); } + public void getOperationalDataset(OperationalDatasetResponseCallback callback, byte[] extendedPanID) { + getOperationalDataset(callback, extendedPanID, 0); + } public void getOperationalDataset(OperationalDatasetResponseCallback callback, byte[] extendedPanID, int timedInvokeTimeoutMs) { final long commandId = 2L; diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java index 5435ed6bad2e36..cac008043786d7 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java @@ -14697,7 +14697,8 @@ public long getID() { } public enum Attribute { - Ssid(1L), + Ssid(0L), + PassphraseSurrogate(1L), GeneratedCommandList(65528L), AcceptedCommandList(65529L), EventList(65530L), diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java index f23d7f1e097b43..6c9b752a0efc25 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java @@ -17882,6 +17882,27 @@ public void onError(Exception ex) { } } + public static class DelegatedWiFiNetworkManagementClusterPassphraseSurrogateAttributeCallback implements ChipClusters.WiFiNetworkManagementCluster.PassphraseSurrogateAttributeCallback, DelegatedClusterCallback { + private ClusterCommandCallback callback; + @Override + public void setCallbackDelegate(ClusterCommandCallback callback) { + this.callback = callback; + } + + @Override + public void onSuccess(@Nullable Long value) { + Map responseValues = new LinkedHashMap<>(); + CommandResponseInfo commandResponseInfo = new CommandResponseInfo("value", "Long"); + responseValues.put(commandResponseInfo, value); + callback.onSuccess(responseValues); + } + + @Override + public void onError(Exception ex) { + callback.onFailure(ex); + } + } + public static class DelegatedWiFiNetworkManagementClusterGeneratedCommandListAttributeCallback implements ChipClusters.WiFiNetworkManagementCluster.GeneratedCommandListAttributeCallback, DelegatedClusterCallback { private ClusterCommandCallback callback; @Override @@ -27841,7 +27862,7 @@ public Map> getCommandMap() { , (byte[]) commandArguments.get("extendedPanID") - , 10000); + ); }, () -> new DelegatedThreadNetworkDirectoryClusterOperationalDatasetResponseCallback(), threadNetworkDirectorygetOperationalDatasetCommandParams diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterReadMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterReadMapping.java index c851578b3ab69d..b650b69b54737c 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ClusterReadMapping.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterReadMapping.java @@ -16916,6 +16916,17 @@ private static Map readWiFiNetworkManagementInteraction readWiFiNetworkManagementSsidCommandParams ); result.put("readSsidAttribute", readWiFiNetworkManagementSsidAttributeInteractionInfo); + Map readWiFiNetworkManagementPassphraseSurrogateCommandParams = new LinkedHashMap(); + InteractionInfo readWiFiNetworkManagementPassphraseSurrogateAttributeInteractionInfo = new InteractionInfo( + (cluster, callback, commandArguments) -> { + ((ChipClusters.WiFiNetworkManagementCluster) cluster).readPassphraseSurrogateAttribute( + (ChipClusters.WiFiNetworkManagementCluster.PassphraseSurrogateAttributeCallback) callback + ); + }, + () -> new ClusterInfoMapping.DelegatedWiFiNetworkManagementClusterPassphraseSurrogateAttributeCallback(), + readWiFiNetworkManagementPassphraseSurrogateCommandParams + ); + result.put("readPassphraseSurrogateAttribute", readWiFiNetworkManagementPassphraseSurrogateAttributeInteractionInfo); Map readWiFiNetworkManagementGeneratedCommandListCommandParams = new LinkedHashMap(); InteractionInfo readWiFiNetworkManagementGeneratedCommandListAttributeInteractionInfo = new InteractionInfo( (cluster, callback, commandArguments) -> { diff --git a/src/controller/java/generated/java/matter/controller/cluster/clusters/ThreadNetworkDirectoryCluster.kt b/src/controller/java/generated/java/matter/controller/cluster/clusters/ThreadNetworkDirectoryCluster.kt index e479f0c79ef0f2..b5af2b1db2895a 100644 --- a/src/controller/java/generated/java/matter/controller/cluster/clusters/ThreadNetworkDirectoryCluster.kt +++ b/src/controller/java/generated/java/matter/controller/cluster/clusters/ThreadNetworkDirectoryCluster.kt @@ -154,7 +154,7 @@ class ThreadNetworkDirectoryCluster( suspend fun getOperationalDataset( extendedPanID: ByteArray, - timedInvokeTimeout: Duration, + timedInvokeTimeout: Duration? = null, ): OperationalDatasetResponse { val commandId: UInt = 2u diff --git a/src/controller/java/generated/java/matter/controller/cluster/clusters/WiFiNetworkManagementCluster.kt b/src/controller/java/generated/java/matter/controller/cluster/clusters/WiFiNetworkManagementCluster.kt index 32f8edab8d7b69..48efb1cea1f4b2 100644 --- a/src/controller/java/generated/java/matter/controller/cluster/clusters/WiFiNetworkManagementCluster.kt +++ b/src/controller/java/generated/java/matter/controller/cluster/clusters/WiFiNetworkManagementCluster.kt @@ -55,6 +55,16 @@ class WiFiNetworkManagementCluster( object SubscriptionEstablished : SsidAttributeSubscriptionState() } + class PassphraseSurrogateAttribute(val value: ULong?) + + sealed class PassphraseSurrogateAttributeSubscriptionState { + data class Success(val value: ULong?) : PassphraseSurrogateAttributeSubscriptionState() + + data class Error(val exception: Exception) : PassphraseSurrogateAttributeSubscriptionState() + + object SubscriptionEstablished : PassphraseSurrogateAttributeSubscriptionState() + } + class GeneratedCommandListAttribute(val value: List) sealed class GeneratedCommandListAttributeSubscriptionState { @@ -139,7 +149,7 @@ class WiFiNetworkManagementCluster( } suspend fun readSsidAttribute(): SsidAttribute { - val ATTRIBUTE_ID: UInt = 1u + val ATTRIBUTE_ID: UInt = 0u val attributePath = AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) @@ -179,7 +189,7 @@ class WiFiNetworkManagementCluster( minInterval: Int, maxInterval: Int, ): Flow { - val ATTRIBUTE_ID: UInt = 1u + val ATTRIBUTE_ID: UInt = 0u val attributePaths = listOf( AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) @@ -231,6 +241,101 @@ class WiFiNetworkManagementCluster( } } + suspend fun readPassphraseSurrogateAttribute(): PassphraseSurrogateAttribute { + val ATTRIBUTE_ID: UInt = 1u + + val attributePath = + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + + val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath)) + + val response = controller.read(readRequest) + + if (response.successes.isEmpty()) { + logger.log(Level.WARNING, "Read command failed") + throw IllegalStateException("Read command failed with failures: ${response.failures}") + } + + logger.log(Level.FINE, "Read command succeeded") + + val attributeData = + response.successes.filterIsInstance().firstOrNull { + it.path.attributeId == ATTRIBUTE_ID + } + + requireNotNull(attributeData) { "Passphrasesurrogate attribute not found in response" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: ULong? = + if (!tlvReader.isNull()) { + tlvReader.getULong(AnonymousTag) + } else { + tlvReader.getNull(AnonymousTag) + null + } + + return PassphraseSurrogateAttribute(decodedValue) + } + + suspend fun subscribePassphraseSurrogateAttribute( + minInterval: Int, + maxInterval: Int, + ): Flow { + val ATTRIBUTE_ID: UInt = 1u + val attributePaths = + listOf( + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + ) + + val subscribeRequest: SubscribeRequest = + SubscribeRequest( + eventPaths = emptyList(), + attributePaths = attributePaths, + minInterval = Duration.ofSeconds(minInterval.toLong()), + maxInterval = Duration.ofSeconds(maxInterval.toLong()), + ) + + return controller.subscribe(subscribeRequest).transform { subscriptionState -> + when (subscriptionState) { + is SubscriptionState.SubscriptionErrorNotification -> { + emit( + PassphraseSurrogateAttributeSubscriptionState.Error( + Exception( + "Subscription terminated with error code: ${subscriptionState.terminationCause}" + ) + ) + ) + } + is SubscriptionState.NodeStateUpdate -> { + val attributeData = + subscriptionState.updateState.successes + .filterIsInstance() + .firstOrNull { it.path.attributeId == ATTRIBUTE_ID } + + requireNotNull(attributeData) { + "Passphrasesurrogate attribute not found in Node State update" + } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: ULong? = + if (!tlvReader.isNull()) { + tlvReader.getULong(AnonymousTag) + } else { + tlvReader.getNull(AnonymousTag) + null + } + + decodedValue?.let { emit(PassphraseSurrogateAttributeSubscriptionState.Success(it)) } + } + SubscriptionState.SubscriptionEstablished -> { + emit(PassphraseSurrogateAttributeSubscriptionState.SubscriptionEstablished) + } + } + } + } + suspend fun readGeneratedCommandListAttribute(): GeneratedCommandListAttribute { val ATTRIBUTE_ID: UInt = 65528u diff --git a/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp b/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp index e4ddb358eb4411..592af081e16e06 100644 --- a/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp +++ b/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp @@ -38358,6 +38358,29 @@ jobject DecodeAttributeValue(const app::ConcreteAttributePath & aPath, TLV::TLVR } return value; } + case Attributes::PassphraseSurrogate::Id: { + using TypeInfo = Attributes::PassphraseSurrogate::TypeInfo; + TypeInfo::DecodableType cppValue; + *aError = app::DataModel::Decode(aReader, cppValue); + if (*aError != CHIP_NO_ERROR) + { + return nullptr; + } + jobject value; + if (cppValue.IsNull()) + { + value = nullptr; + } + else + { + std::string valueClassName = "java/lang/Long"; + std::string valueCtorSignature = "(J)V"; + jlong jnivalue = static_cast(cppValue.Value()); + chip::JniReferences::GetInstance().CreateBoxedObject(valueClassName.c_str(), valueCtorSignature.c_str(), + jnivalue, value); + } + return value; + } case Attributes::GeneratedCommandList::Id: { using TypeInfo = Attributes::GeneratedCommandList::TypeInfo; TypeInfo::DecodableType cppValue; diff --git a/src/controller/python/chip/clusters/CHIPClusters.py b/src/controller/python/chip/clusters/CHIPClusters.py index 4e787228619688..d9b1d1699d5d39 100644 --- a/src/controller/python/chip/clusters/CHIPClusters.py +++ b/src/controller/python/chip/clusters/CHIPClusters.py @@ -11799,12 +11799,18 @@ class ChipClusters: }, }, "attributes": { - 0x00000001: { + 0x00000000: { "attributeName": "Ssid", - "attributeId": 0x00000001, + "attributeId": 0x00000000, "type": "bytes", "reportable": True, }, + 0x00000001: { + "attributeName": "PassphraseSurrogate", + "attributeId": 0x00000001, + "type": "int", + "reportable": True, + }, 0x0000FFF8: { "attributeName": "GeneratedCommandList", "attributeId": 0x0000FFF8, diff --git a/src/controller/python/chip/clusters/Objects.py b/src/controller/python/chip/clusters/Objects.py index f14b7c8d04c139..a179fbffddb323 100644 --- a/src/controller/python/chip/clusters/Objects.py +++ b/src/controller/python/chip/clusters/Objects.py @@ -41560,7 +41560,8 @@ class WiFiNetworkManagement(Cluster): def descriptor(cls) -> ClusterObjectDescriptor: return ClusterObjectDescriptor( Fields=[ - ClusterObjectFieldDescriptor(Label="ssid", Tag=0x00000001, Type=typing.Union[Nullable, bytes]), + ClusterObjectFieldDescriptor(Label="ssid", Tag=0x00000000, Type=typing.Union[Nullable, bytes]), + ClusterObjectFieldDescriptor(Label="passphraseSurrogate", Tag=0x00000001, Type=typing.Union[Nullable, uint]), ClusterObjectFieldDescriptor(Label="generatedCommandList", Tag=0x0000FFF8, Type=typing.List[uint]), ClusterObjectFieldDescriptor(Label="acceptedCommandList", Tag=0x0000FFF9, Type=typing.List[uint]), ClusterObjectFieldDescriptor(Label="eventList", Tag=0x0000FFFA, Type=typing.List[uint]), @@ -41570,6 +41571,7 @@ def descriptor(cls) -> ClusterObjectDescriptor: ]) ssid: 'typing.Union[Nullable, bytes]' = None + passphraseSurrogate: 'typing.Union[Nullable, uint]' = None generatedCommandList: 'typing.List[uint]' = None acceptedCommandList: 'typing.List[uint]' = None eventList: 'typing.List[uint]' = None @@ -41616,7 +41618,7 @@ def cluster_id(cls) -> int: @ChipUtility.classproperty def attribute_id(cls) -> int: - return 0x00000001 + return 0x00000000 @ChipUtility.classproperty def attribute_type(cls) -> ClusterObjectFieldDescriptor: @@ -41624,6 +41626,22 @@ def attribute_type(cls) -> ClusterObjectFieldDescriptor: value: 'typing.Union[Nullable, bytes]' = NullValue + @dataclass + class PassphraseSurrogate(ClusterAttributeDescriptor): + @ChipUtility.classproperty + def cluster_id(cls) -> int: + return 0x00000451 + + @ChipUtility.classproperty + def attribute_id(cls) -> int: + return 0x00000001 + + @ChipUtility.classproperty + def attribute_type(cls) -> ClusterObjectFieldDescriptor: + return ClusterObjectFieldDescriptor(Type=typing.Union[Nullable, uint]) + + value: 'typing.Union[Nullable, uint]' = NullValue + @dataclass class GeneratedCommandList(ClusterAttributeDescriptor): @ChipUtility.classproperty @@ -42115,10 +42133,6 @@ def descriptor(cls) -> ClusterObjectDescriptor: ClusterObjectFieldDescriptor(Label="extendedPanID", Tag=0, Type=bytes), ]) - @ChipUtility.classproperty - def must_use_timed_invoke(cls) -> bool: - return True - extendedPanID: 'bytes' = b"" @dataclass diff --git a/src/credentials/BUILD.gn b/src/credentials/BUILD.gn index df7afc0c1025ba..670cefcd209c10 100644 --- a/src/credentials/BUILD.gn +++ b/src/credentials/BUILD.gn @@ -13,6 +13,7 @@ # limitations under the License. import("//build_overrides/chip.gni") +import("//build_overrides/jsoncpp.gni") import("//build_overrides/nlassert.gni") import("${chip_root}/src/crypto/crypto.gni") import("${chip_root}/src/lib/core/core.gni") @@ -185,3 +186,17 @@ static_library("file_attestation_trust_store") { "${nlassert_root}:nlassert", ] } + +static_library("test_dac_revocation_delegate") { + output_name = "libTestDACRevocationDelegate" + + sources = [ + "attestation_verifier/TestDACRevocationDelegateImpl.cpp", + "attestation_verifier/TestDACRevocationDelegateImpl.h", + ] + + public_deps = [ + ":credentials", + jsoncpp_root, + ] +} diff --git a/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.cpp b/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.cpp index f3444b0c303940..14759d850ffc40 100644 --- a/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.cpp +++ b/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.cpp @@ -610,11 +610,14 @@ CHIP_ERROR DefaultDACVerifier::VerifyNodeOperationalCSRInformation(const ByteSpa void DefaultDACVerifier::CheckForRevokedDACChain(const AttestationInfo & info, Callback::Callback * onCompletion) { - AttestationVerificationResult attestationError = AttestationVerificationResult::kSuccess; - - // TODO(#33124): Implement default version of CheckForRevokedDACChain - - onCompletion->mCall(onCompletion->mContext, info, attestationError); + if (mRevocationDelegate != nullptr) + { + mRevocationDelegate->CheckForRevokedDACChain(info, onCompletion); + } + else + { + onCompletion->mCall(onCompletion->mContext, info, AttestationVerificationResult::kSuccess); + } } bool CsaCdKeysTrustStore::IsCdTestKey(const ByteSpan & kid) const @@ -693,9 +696,10 @@ const AttestationTrustStore * GetTestAttestationTrustStore() return &gTestAttestationTrustStore.get(); } -DeviceAttestationVerifier * GetDefaultDACVerifier(const AttestationTrustStore * paaRootStore) +DeviceAttestationVerifier * GetDefaultDACVerifier(const AttestationTrustStore * paaRootStore, + DeviceAttestationRevocationDelegate * revocationDelegate) { - static DefaultDACVerifier defaultDACVerifier{ paaRootStore }; + static DefaultDACVerifier defaultDACVerifier{ paaRootStore, revocationDelegate }; return &defaultDACVerifier; } diff --git a/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.h b/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.h index 346d098a58a337..7e0fc1c4378848 100644 --- a/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.h +++ b/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.h @@ -59,6 +59,10 @@ class DefaultDACVerifier : public DeviceAttestationVerifier public: DefaultDACVerifier(const AttestationTrustStore * paaRootStore) : mAttestationTrustStore(paaRootStore) {} + DefaultDACVerifier(const AttestationTrustStore * paaRootStore, DeviceAttestationRevocationDelegate * revocationDelegate) : + mAttestationTrustStore(paaRootStore), mRevocationDelegate(revocationDelegate) + {} + void VerifyAttestationInformation(const DeviceAttestationVerifier::AttestationInfo & info, Callback::Callback * onCompletion) override; @@ -79,11 +83,17 @@ class DefaultDACVerifier : public DeviceAttestationVerifier CsaCdKeysTrustStore * GetCertificationDeclarationTrustStore() override { return &mCdKeysTrustStore; } + void SetRevocationDelegate(DeviceAttestationRevocationDelegate * revocationDelegate) + { + mRevocationDelegate = revocationDelegate; + } + protected: DefaultDACVerifier() {} CsaCdKeysTrustStore mCdKeysTrustStore; const AttestationTrustStore * mAttestationTrustStore; + DeviceAttestationRevocationDelegate * mRevocationDelegate = nullptr; }; /** @@ -112,7 +122,8 @@ const AttestationTrustStore * GetTestAttestationTrustStore(); * process lifetime. In particular, after the first call it's not * possible to change which AttestationTrustStore is used by this verifier. */ -DeviceAttestationVerifier * GetDefaultDACVerifier(const AttestationTrustStore * paaRootStore); +DeviceAttestationVerifier * GetDefaultDACVerifier(const AttestationTrustStore * paaRootStore, + DeviceAttestationRevocationDelegate * revocationDelegate = nullptr); } // namespace Credentials } // namespace chip diff --git a/src/credentials/attestation_verifier/DeviceAttestationVerifier.h b/src/credentials/attestation_verifier/DeviceAttestationVerifier.h index f45ceae06c23fe..e6915931a73b68 100644 --- a/src/credentials/attestation_verifier/DeviceAttestationVerifier.h +++ b/src/credentials/attestation_verifier/DeviceAttestationVerifier.h @@ -47,6 +47,7 @@ enum class AttestationVerificationResult : uint16_t kPaiVendorIdMismatch = 205, kPaiAuthorityNotFound = 206, kPaiMissing = 207, + kPaiAndDacRevoked = 208, kDacExpired = 300, kDacSignatureInvalid = 301, @@ -418,6 +419,28 @@ class DeviceAttestationVerifier bool mEnableCdTestKeySupport = true; }; +/** + * @brief Interface for checking the device attestation revocation status + * + */ +class DeviceAttestationRevocationDelegate +{ +public: + DeviceAttestationRevocationDelegate() = default; + virtual ~DeviceAttestationRevocationDelegate() = default; + + /** + * @brief Verify whether or not the given DAC chain is revoked. + * + * @param[in] info All of the information required to check for revoked DAC chain. + * @param[in] onCompletion Callback handler to provide Attestation Information Verification result to the caller of + * CheckForRevokedDACChain(). + */ + virtual void + CheckForRevokedDACChain(const DeviceAttestationVerifier::AttestationInfo & info, + Callback::Callback * onCompletion) = 0; +}; + /** * Instance getter for the global DeviceAttestationVerifier. * diff --git a/src/credentials/attestation_verifier/TestDACRevocationDelegateImpl.cpp b/src/credentials/attestation_verifier/TestDACRevocationDelegateImpl.cpp new file mode 100644 index 00000000000000..4e1978525e7327 --- /dev/null +++ b/src/credentials/attestation_verifier/TestDACRevocationDelegateImpl.cpp @@ -0,0 +1,219 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace chip::Crypto; + +namespace chip { +namespace Credentials { + +namespace { +CHIP_ERROR BytesToHexStr(const ByteSpan & bytes, MutableCharSpan & outHexStr) +{ + Encoding::HexFlags flags = Encoding::HexFlags::kUppercase; + ReturnErrorOnFailure(BytesToHex(bytes.data(), bytes.size(), outHexStr.data(), outHexStr.size(), flags)); + outHexStr.reduce_size(2 * bytes.size()); + return CHIP_NO_ERROR; +} +} // anonymous namespace + +CHIP_ERROR TestDACRevocationDelegateImpl::SetDeviceAttestationRevocationSetPath(std::string_view path) +{ + VerifyOrReturnError(path.empty() != true, CHIP_ERROR_INVALID_ARGUMENT); + mDeviceAttestationRevocationSetPath = path; + return CHIP_NO_ERROR; +} + +void TestDACRevocationDelegateImpl::ClearDeviceAttestationRevocationSetPath() +{ + // clear the string_view + mDeviceAttestationRevocationSetPath = mDeviceAttestationRevocationSetPath.substr(0, 0); +} + +// This method parses the below JSON Scheme +// [ +// { +// "type": "revocation_set", +// "issuer_subject_key_id": "", +// "issuer_name": "", +// "revoked_serial_numbers: [ +// "serial1 bytes as base64", +// "serial2 bytes as base64" +// ] +// } +// ] +// +bool TestDACRevocationDelegateImpl::IsEntryInRevocationSet(const CharSpan & akidHexStr, const CharSpan & issuerNameBase64Str, + const CharSpan & serialNumberHexStr) +{ + std::ifstream file(mDeviceAttestationRevocationSetPath.data()); + if (!file.is_open()) + { + ChipLogError(NotSpecified, "Failed to open file: %s", mDeviceAttestationRevocationSetPath.data()); + return false; + } + + // Parse the JSON data incrementally + Json::CharReaderBuilder readerBuilder; + Json::Value jsonData; + std::string errs; + + bool parsingSuccessful = Json::parseFromStream(readerBuilder, file, &jsonData, &errs); + + // Close the file as it's no longer needed + file.close(); + + if (!parsingSuccessful) + { + ChipLogError(NotSpecified, "Failed to parse JSON: %s", errs.c_str()); + return false; + } + + std::string issuerName = std::string(issuerNameBase64Str.data(), issuerNameBase64Str.size()); + std::string serialNumber = std::string(serialNumberHexStr.data(), serialNumberHexStr.size()); + std::string akid = std::string(akidHexStr.data(), akidHexStr.size()); + + for (const auto & revokedSet : jsonData) + { + if (revokedSet["issuer_name"].asString() != issuerName) + { + continue; + } + if (revokedSet["issuer_subject_key_id"].asString() != akid) + { + continue; + } + for (const auto & revokedSerialNumber : revokedSet["revoked_serial_numbers"]) + { + if (revokedSerialNumber.asString() == serialNumber) + { + return true; + } + } + } + return false; +} + +CHIP_ERROR TestDACRevocationDelegateImpl::GetAKIDHexStr(const ByteSpan & certDer, MutableCharSpan & outAKIDHexStr) +{ + uint8_t akidBuf[kAuthorityKeyIdentifierLength]; + MutableByteSpan akid(akidBuf); + + ReturnErrorOnFailure(ExtractAKIDFromX509Cert(certDer, akid)); + + return BytesToHexStr(akid, outAKIDHexStr); +} + +CHIP_ERROR TestDACRevocationDelegateImpl::GetSerialNumberHexStr(const ByteSpan & certDer, MutableCharSpan & outSerialNumberHexStr) +{ + uint8_t serialNumberBuf[kMaxCertificateSerialNumberLength] = { 0 }; + MutableByteSpan serialNumber(serialNumberBuf); + + ReturnErrorOnFailure(ExtractSerialNumberFromX509Cert(certDer, serialNumber)); + return BytesToHexStr(serialNumber, outSerialNumberHexStr); +} + +CHIP_ERROR TestDACRevocationDelegateImpl::GetIssuerNameBase64Str(const ByteSpan & certDer, + MutableCharSpan & outIssuerNameBase64String) +{ + uint8_t issuerBuf[kMaxCertificateDistinguishedNameLength] = { 0 }; + MutableByteSpan issuer(issuerBuf); + + ReturnErrorOnFailure(ExtractIssuerFromX509Cert(certDer, issuer)); + VerifyOrReturnError(outIssuerNameBase64String.size() >= BASE64_ENCODED_LEN(issuer.size()), CHIP_ERROR_BUFFER_TOO_SMALL); + + uint16_t encodedLen = Base64Encode(issuer.data(), static_cast(issuer.size()), outIssuerNameBase64String.data()); + outIssuerNameBase64String.reduce_size(encodedLen); + return CHIP_NO_ERROR; +} + +bool TestDACRevocationDelegateImpl::IsCertificateRevoked(const ByteSpan & certDer) +{ + static constexpr uint32_t maxIssuerBase64Len = BASE64_ENCODED_LEN(kMaxCertificateDistinguishedNameLength); + + char issuerNameBuffer[maxIssuerBase64Len] = { 0 }; + char serialNumberHexStrBuffer[2 * kMaxCertificateSerialNumberLength] = { 0 }; + char akidHexStrBuffer[2 * kAuthorityKeyIdentifierLength] = { 0 }; + + MutableCharSpan issuerName(issuerNameBuffer); + MutableCharSpan serialNumber(serialNumberHexStrBuffer); + MutableCharSpan akid(akidHexStrBuffer); + + VerifyOrReturnValue(CHIP_NO_ERROR == GetIssuerNameBase64Str(certDer, issuerName), false); + ChipLogDetail(NotSpecified, "Issuer: %.*s", static_cast(issuerName.size()), issuerName.data()); + + VerifyOrReturnValue(CHIP_NO_ERROR == GetSerialNumberHexStr(certDer, serialNumber), false); + ChipLogDetail(NotSpecified, "Serial Number: %.*s", static_cast(serialNumber.size()), serialNumber.data()); + + VerifyOrReturnValue(CHIP_NO_ERROR == GetAKIDHexStr(certDer, akid), false); + ChipLogDetail(NotSpecified, "AKID: %.*s", static_cast(akid.size()), akid.data()); + + // TODO: Cross-validate the CRLSignerCertificate and CRLSignerDelegator per spec: #34587 + + return IsEntryInRevocationSet(akid, issuerName, serialNumber); +} + +void TestDACRevocationDelegateImpl::CheckForRevokedDACChain( + const DeviceAttestationVerifier::AttestationInfo & info, + Callback::Callback * onCompletion) +{ + AttestationVerificationResult attestationError = AttestationVerificationResult::kSuccess; + + if (mDeviceAttestationRevocationSetPath.empty()) + { + + onCompletion->mCall(onCompletion->mContext, info, attestationError); + } + + ChipLogDetail(NotSpecified, "Checking for revoked DAC in %s", mDeviceAttestationRevocationSetPath.data()); + + if (IsCertificateRevoked(info.dacDerBuffer)) + { + ChipLogProgress(NotSpecified, "Found revoked DAC in %s", mDeviceAttestationRevocationSetPath.data()); + attestationError = AttestationVerificationResult::kDacRevoked; + } + + ChipLogDetail(NotSpecified, "Checking for revoked PAI in %s", mDeviceAttestationRevocationSetPath.data()); + + if (IsCertificateRevoked(info.paiDerBuffer)) + { + ChipLogProgress(NotSpecified, "Found revoked PAI in %s", mDeviceAttestationRevocationSetPath.data()); + + if (attestationError == AttestationVerificationResult::kDacRevoked) + { + attestationError = AttestationVerificationResult::kPaiAndDacRevoked; + } + else + { + attestationError = AttestationVerificationResult::kPaiRevoked; + } + } + + onCompletion->mCall(onCompletion->mContext, info, attestationError); +} + +} // namespace Credentials +} // namespace chip diff --git a/src/credentials/attestation_verifier/TestDACRevocationDelegateImpl.h b/src/credentials/attestation_verifier/TestDACRevocationDelegateImpl.h new file mode 100644 index 00000000000000..c820e56f5f6ce3 --- /dev/null +++ b/src/credentials/attestation_verifier/TestDACRevocationDelegateImpl.h @@ -0,0 +1,65 @@ +/* + * + * 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. + */ + +#pragma once + +#include +#include +#include + +namespace chip { +namespace Credentials { + +class TestDACRevocationDelegateImpl : public DeviceAttestationRevocationDelegate +{ +public: + TestDACRevocationDelegateImpl() = default; + ~TestDACRevocationDelegateImpl() = default; + + /** + * @brief Verify whether or not the given DAC chain is revoked. + * + * @param[in] info All of the information required to check for revoked DAC chain. + * @param[in] onCompletion Callback handler to provide Attestation Information Verification result to the caller of + * CheckForRevokedDACChain(). + */ + void CheckForRevokedDACChain( + const DeviceAttestationVerifier::AttestationInfo & info, + Callback::Callback * onCompletion) override; + + // Set the path to the device attestation revocation set JSON file. + // revocation set can be generated using credentials/generate-revocation-set.py script + // This API returns CHIP_ERROR_INVALID_ARGUMENT if the path is null. + CHIP_ERROR SetDeviceAttestationRevocationSetPath(std::string_view path); + + // Clear the path to the device attestation revocation set JSON file. + // This can be used to skip the revocation check + void ClearDeviceAttestationRevocationSetPath(); + +private: + CHIP_ERROR GetAKIDHexStr(const ByteSpan & certDer, MutableCharSpan & outAKIDHexStr); + CHIP_ERROR GetSerialNumberHexStr(const ByteSpan & certDer, MutableCharSpan & outSerialNumberHexStr); + CHIP_ERROR GetIssuerNameBase64Str(const ByteSpan & certDer, MutableCharSpan & outIssuerNameBase64String); + bool IsEntryInRevocationSet(const CharSpan & akidHexStr, const CharSpan & issuerNameBase64Str, + const CharSpan & serialNumberHexStr); + bool IsCertificateRevoked(const ByteSpan & certDer); + + std::string_view mDeviceAttestationRevocationSetPath; +}; + +} // namespace Credentials +} // namespace chip diff --git a/src/credentials/tests/BUILD.gn b/src/credentials/tests/BUILD.gn index 99cb1e8e20d322..393b246ef20ee3 100644 --- a/src/credentials/tests/BUILD.gn +++ b/src/credentials/tests/BUILD.gn @@ -68,6 +68,7 @@ chip_test_suite("tests") { "${chip_root}/src/controller:controller", "${chip_root}/src/credentials", "${chip_root}/src/credentials:default_attestation_verifier", + "${chip_root}/src/credentials:test_dac_revocation_delegate", "${chip_root}/src/lib/core", "${chip_root}/src/lib/core:string-builder-adapters", "${chip_root}/src/lib/support:testing", diff --git a/src/credentials/tests/TestDeviceAttestationCredentials.cpp b/src/credentials/tests/TestDeviceAttestationCredentials.cpp index 85a5d4edfa58a2..0f80df9fa9f6f4 100644 --- a/src/credentials/tests/TestDeviceAttestationCredentials.cpp +++ b/src/credentials/tests/TestDeviceAttestationCredentials.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,8 @@ #include "CHIPAttCert_test_vectors.h" +#include + using namespace chip; using namespace chip::Crypto; using namespace chip::Credentials; @@ -413,3 +416,160 @@ TEST_F(TestDeviceAttestationCredentials, TestAttestationTrustStore) } } } + +static void WriteTestRevokedData(const char * jsonData, const char * fileName) +{ + // TODO: Add option to load test data from the test without using file. #34588 + + // write data to /tmp/sample_revoked_set.json using fstream APIs + std::ofstream file; + file.open(fileName, std::ofstream::out | std::ofstream::trunc); + file << jsonData; + file.close(); +} + +TEST_F(TestDeviceAttestationCredentials, TestDACRevocationDelegateImpl) +{ + uint8_t attestationElementsTestVector[] = { 0 }; + uint8_t attestationChallengeTestVector[] = { 0 }; + uint8_t attestationSignatureTestVector[] = { 0 }; + uint8_t attestationNonceTestVector[] = { 0 }; + + // Details for TestCerts::sTestCert_DAC_FFF1_8000_0004_Cert + // Issuer: MEYxGDAWBgNVBAMMD01hdHRlciBUZXN0IFBBSTEUMBIGCisGAQQBgqJ8AgEMBEZGRjExFDASBgorBgEEAYKifAICDAQ4MDAw + // AKID: AF42B7094DEBD515EC6ECF33B81115225F325288 + // Serial Number: 0C694F7F866067B2 + // + // Details for TestCerts::sTestCert_PAI_FFF1_8000_Cert + // Issuer: MDAxGDAWBgNVBAMMD01hdHRlciBUZXN0IFBBQTEUMBIGCisGAQQBgqJ8AgEMBEZGRjE= + // AKID: 6AFD22771F511FECBF1641976710DCDC31A1717E + // Serial Number: 3E6CE6509AD840CD1 + Credentials::DeviceAttestationVerifier::AttestationInfo info( + ByteSpan(attestationElementsTestVector), ByteSpan(attestationChallengeTestVector), ByteSpan(attestationSignatureTestVector), + TestCerts::sTestCert_PAI_FFF1_8000_Cert, TestCerts::sTestCert_DAC_FFF1_8000_0004_Cert, ByteSpan(attestationNonceTestVector), + static_cast(0xFFF1), 0x8000); + + AttestationVerificationResult attestationResult = AttestationVerificationResult::kNotImplemented; + + Callback::Callback attestationInformationVerificationCallback( + OnAttestationInformationVerificationCallback, &attestationResult); + + TestDACRevocationDelegateImpl revocationDelegateImpl; + + // Test without revocation set + revocationDelegateImpl.CheckForRevokedDACChain(info, &attestationInformationVerificationCallback); + EXPECT_EQ(attestationResult, AttestationVerificationResult::kSuccess); + + const char * tmpJsonFile = "/tmp/sample_revoked_set.json"; + revocationDelegateImpl.SetDeviceAttestationRevocationSetPath(tmpJsonFile); + + // Test empty json + WriteTestRevokedData("", tmpJsonFile); + revocationDelegateImpl.CheckForRevokedDACChain(info, &attestationInformationVerificationCallback); + EXPECT_EQ(attestationResult, AttestationVerificationResult::kSuccess); + + // Test DAC is revoked + const char * jsonData = R"( + [{ + "type": "revocation_set", + "issuer_subject_key_id": "AF42B7094DEBD515EC6ECF33B81115225F325288", + "issuer_name": "MEYxGDAWBgNVBAMMD01hdHRlciBUZXN0IFBBSTEUMBIGCisGAQQBgqJ8AgEMBEZGRjExFDASBgorBgEEAYKifAICDAQ4MDAw", + "revoked_serial_numbers": ["0C694F7F866067B2"] + }] + )"; + WriteTestRevokedData(jsonData, tmpJsonFile); + revocationDelegateImpl.CheckForRevokedDACChain(info, &attestationInformationVerificationCallback); + EXPECT_EQ(attestationResult, AttestationVerificationResult::kDacRevoked); + + // Test PAI is revoked + jsonData = R"( + [{ + "type": "revocation_set", + "issuer_subject_key_id": "6AFD22771F511FECBF1641976710DCDC31A1717E", + "issuer_name": "MDAxGDAWBgNVBAMMD01hdHRlciBUZXN0IFBBQTEUMBIGCisGAQQBgqJ8AgEMBEZGRjE=", + "revoked_serial_numbers": ["3E6CE6509AD840CD"] + }] + )"; + WriteTestRevokedData(jsonData, tmpJsonFile); + revocationDelegateImpl.CheckForRevokedDACChain(info, &attestationInformationVerificationCallback); + EXPECT_EQ(attestationResult, AttestationVerificationResult::kPaiRevoked); + + // Test DAC and PAI both revoked + jsonData = R"( + [{ + "type": "revocation_set", + "issuer_subject_key_id": "AF42B7094DEBD515EC6ECF33B81115225F325288", + "issuer_name": "MEYxGDAWBgNVBAMMD01hdHRlciBUZXN0IFBBSTEUMBIGCisGAQQBgqJ8AgEMBEZGRjExFDASBgorBgEEAYKifAICDAQ4MDAw", + "revoked_serial_numbers": ["0C694F7F866067B2"] + }, + { + "type": "revocation_set", + "issuer_subject_key_id": "6AFD22771F511FECBF1641976710DCDC31A1717E", + "issuer_name": "MDAxGDAWBgNVBAMMD01hdHRlciBUZXN0IFBBQTEUMBIGCisGAQQBgqJ8AgEMBEZGRjE=", + "revoked_serial_numbers": ["3E6CE6509AD840CD"] + }] + )"; + WriteTestRevokedData(jsonData, tmpJsonFile); + revocationDelegateImpl.CheckForRevokedDACChain(info, &attestationInformationVerificationCallback); + EXPECT_EQ(attestationResult, AttestationVerificationResult::kPaiAndDacRevoked); + + // Test with another test DAC and PAI + Credentials::DeviceAttestationVerifier::AttestationInfo FFF2_8001_info( + ByteSpan(attestationElementsTestVector), ByteSpan(attestationChallengeTestVector), ByteSpan(attestationSignatureTestVector), + TestCerts::sTestCert_PAI_FFF2_8001_Cert, TestCerts::sTestCert_DAC_FFF2_8001_0008_Cert, ByteSpan(attestationNonceTestVector), + static_cast(0xFFF2), 0x8001); + revocationDelegateImpl.CheckForRevokedDACChain(FFF2_8001_info, &attestationInformationVerificationCallback); + EXPECT_EQ(attestationResult, AttestationVerificationResult::kSuccess); + + // Test issuer does not match + jsonData = R"( + [{ + "type": "revocation_set", + "issuer_subject_key_id": "BF42B7094DEBD515EC6ECF33B81115225F325289", + "issuer_name": "MEYxGDAWBgNVBAMMD01hdHRlciBUZXN0IFBBSTEUMBIGCisGAQQBgqJ8AgEMBEZGRjExFDASBgorBgEEAYKifAICDAQ4MDAw", + "revoked_serial_numbers": ["0C694F7F866067B2"] + }] + )"; + WriteTestRevokedData(jsonData, tmpJsonFile); + revocationDelegateImpl.CheckForRevokedDACChain(info, &attestationInformationVerificationCallback); + EXPECT_EQ(attestationResult, AttestationVerificationResult::kSuccess); + + // Test subject key ID does not match + jsonData = R"( + [{ + "type": "revocation_set", + "issuer_subject_key_id": "BF42B7094DEBD515EC6ECF33B81115225F325289", + "issuer_name": "MEYxGDAWBgNVBAMMD01hdHRlciBUZXN0IFBBSTEUMBIGCisGAQQBgqJ8AgEMBEZGRjExFDASBgorBgEEAYKifAICDAQ4MDAw", + "revoked_serial_numbers": ["0C694F7F866067B2"] + }] + )"; + WriteTestRevokedData(jsonData, tmpJsonFile); + revocationDelegateImpl.CheckForRevokedDACChain(info, &attestationInformationVerificationCallback); + EXPECT_EQ(attestationResult, AttestationVerificationResult::kSuccess); + + // Test serial number does not match + jsonData = R"( + [{ + "type": "revocation_set", + "issuer_subject_key_id": "AF42B7094DEBD515EC6ECF33B81115225F325288", + "issuer_name": "MEYxGDAWBgNVBAMMD01hdHRlciBUZXN0IFBBSTEUMBIGCisGAQQBgqJ8AgEMBEZGRjExFDASBgorBgEEAYKifAICDAQ4MDAw", + "revoked_serial_numbers": ["3E6CE6509AD840CD1", "BC694F7F866067B1"] + }] + )"; + WriteTestRevokedData(jsonData, tmpJsonFile); + revocationDelegateImpl.CheckForRevokedDACChain(info, &attestationInformationVerificationCallback); + EXPECT_EQ(attestationResult, AttestationVerificationResult::kSuccess); + + // Test starting serial number bytes match but not all + jsonData = R"( + [{ + "type": "revocation_set", + "issuer_subject_key_id": "AF42B7094DEBD515EC6ECF33B81115225F325288", + "issuer_name": "MEYxGDAWBgNVBAMMD01hdHRlciBUZXN0IFBBSTEUMBIGCisGAQQBgqJ8AgEMBEZGRjExFDASBgorBgEEAYKifAICDAQ4MDAw", + "revoked_serial_numbers": ["0C694F7F866067B21234"] + }] + )"; + WriteTestRevokedData(jsonData, tmpJsonFile); + revocationDelegateImpl.CheckForRevokedDACChain(info, &attestationInformationVerificationCallback); + EXPECT_EQ(attestationResult, AttestationVerificationResult::kSuccess); +} diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRAttributeSpecifiedCheck.mm b/src/darwin/Framework/CHIP/zap-generated/MTRAttributeSpecifiedCheck.mm index dd7f39efec2844..0492c7b2753c22 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRAttributeSpecifiedCheck.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRAttributeSpecifiedCheck.mm @@ -5424,6 +5424,9 @@ static BOOL AttributeIsSpecifiedInWiFiNetworkManagementCluster(AttributeId aAttr case Attributes::Ssid::Id: { return YES; } + case Attributes::PassphraseSurrogate::Id: { + return YES; + } case Attributes::GeneratedCommandList::Id: { return YES; } diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm b/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm index 1a69014502ff6f..b96a1adcbafbac 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm @@ -15644,6 +15644,21 @@ static id _Nullable DecodeAttributeValueForWiFiNetworkManagementCluster(Attribut } return value; } + case Attributes::PassphraseSurrogate::Id: { + using TypeInfo = Attributes::PassphraseSurrogate::TypeInfo; + TypeInfo::DecodableType cppValue; + *aError = DataModel::Decode(aReader, cppValue); + if (*aError != CHIP_NO_ERROR) { + return nil; + } + NSNumber * _Nullable value; + if (cppValue.IsNull()) { + value = nil; + } else { + value = [NSNumber numberWithUnsignedLongLong:cppValue.Value()]; + } + return value; + } default: { break; } diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h index bb9139ddf4ef78..540568b0806d5e 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h @@ -13314,6 +13314,12 @@ MTR_PROVISIONALLY_AVAILABLE reportHandler:(void (^)(NSData * _Nullable value, NSError * _Nullable error))reportHandler MTR_PROVISIONALLY_AVAILABLE; + (void)readAttributeSSIDWithClusterStateCache:(MTRClusterStateCacheContainer *)clusterStateCacheContainer endpoint:(NSNumber *)endpoint queue:(dispatch_queue_t)queue completion:(void (^)(NSData * _Nullable value, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; +- (void)readAttributePassphraseSurrogateWithCompletion:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; +- (void)subscribeAttributePassphraseSurrogateWithParams:(MTRSubscribeParams *)params + subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished + reportHandler:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))reportHandler MTR_PROVISIONALLY_AVAILABLE; ++ (void)readAttributePassphraseSurrogateWithClusterStateCache:(MTRClusterStateCacheContainer *)clusterStateCacheContainer endpoint:(NSNumber *)endpoint queue:(dispatch_queue_t)queue completion:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; + - (void)readAttributeGeneratedCommandListWithCompletion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; - (void)subscribeAttributeGeneratedCommandListWithParams:(MTRSubscribeParams *)params subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.mm b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.mm index ca2b47d0c5562b..98adcf07900b4e 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.mm @@ -94340,6 +94340,42 @@ + (void)readAttributeSSIDWithClusterStateCache:(MTRClusterStateCacheContainer *) completion:completion]; } +- (void)readAttributePassphraseSurrogateWithCompletion:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))completion +{ + using TypeInfo = WiFiNetworkManagement::Attributes::PassphraseSurrogate::TypeInfo; + [self.device _readKnownAttributeWithEndpointID:self.endpointID + clusterID:@(TypeInfo::GetClusterId()) + attributeID:@(TypeInfo::GetAttributeId()) + params:nil + queue:self.callbackQueue + completion:completion]; +} + +- (void)subscribeAttributePassphraseSurrogateWithParams:(MTRSubscribeParams * _Nonnull)params + subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished + reportHandler:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))reportHandler +{ + using TypeInfo = WiFiNetworkManagement::Attributes::PassphraseSurrogate::TypeInfo; + [self.device _subscribeToKnownAttributeWithEndpointID:self.endpointID + clusterID:@(TypeInfo::GetClusterId()) + attributeID:@(TypeInfo::GetAttributeId()) + params:params + queue:self.callbackQueue + reportHandler:reportHandler + subscriptionEstablished:subscriptionEstablished]; +} + ++ (void)readAttributePassphraseSurrogateWithClusterStateCache:(MTRClusterStateCacheContainer *)clusterStateCacheContainer endpoint:(NSNumber *)endpoint queue:(dispatch_queue_t)queue completion:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))completion +{ + using TypeInfo = WiFiNetworkManagement::Attributes::PassphraseSurrogate::TypeInfo; + [clusterStateCacheContainer + _readKnownCachedAttributeWithEndpointID:static_cast([endpoint unsignedShortValue]) + clusterID:TypeInfo::GetClusterId() + attributeID:TypeInfo::GetAttributeId() + queue:queue + completion:completion]; +} + - (void)readAttributeGeneratedCommandListWithCompletion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion { using TypeInfo = WiFiNetworkManagement::Attributes::GeneratedCommandList::TypeInfo; @@ -95131,9 +95167,6 @@ - (void)getOperationalDatasetWithParams:(MTRThreadNetworkDirectoryClusterGetOper }; auto * timedInvokeTimeoutMs = params.timedInvokeTimeoutMs; - if (timedInvokeTimeoutMs == nil) { - timedInvokeTimeoutMs = @(MTR_DEFAULT_TIMED_INTERACTION_TIMEOUT_MS); - } using RequestType = ThreadNetworkDirectory::Commands::GetOperationalDataset::Type; [self.device _invokeKnownCommandWithEndpointID:self.endpointID diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRClusterConstants.h b/src/darwin/Framework/CHIP/zap-generated/MTRClusterConstants.h index 16d74df3e0756f..aad53d580e51ab 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRClusterConstants.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRClusterConstants.h @@ -4410,7 +4410,8 @@ typedef NS_ENUM(uint32_t, MTRAttributeIDType) { MTRAttributeIDTypeClusterRadonConcentrationMeasurementAttributeClusterRevisionID MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6)) = MTRAttributeIDTypeGlobalAttributeClusterRevisionID, // Cluster WiFiNetworkManagement attributes - MTRAttributeIDTypeClusterWiFiNetworkManagementAttributeSSIDID MTR_PROVISIONALLY_AVAILABLE = 0x00000001, + MTRAttributeIDTypeClusterWiFiNetworkManagementAttributeSSIDID MTR_PROVISIONALLY_AVAILABLE = 0x00000000, + MTRAttributeIDTypeClusterWiFiNetworkManagementAttributePassphraseSurrogateID MTR_PROVISIONALLY_AVAILABLE = 0x00000001, MTRAttributeIDTypeClusterWiFiNetworkManagementAttributeGeneratedCommandListID MTR_PROVISIONALLY_AVAILABLE = MTRAttributeIDTypeGlobalAttributeGeneratedCommandListID, MTRAttributeIDTypeClusterWiFiNetworkManagementAttributeAcceptedCommandListID MTR_PROVISIONALLY_AVAILABLE = MTRAttributeIDTypeGlobalAttributeAcceptedCommandListID, MTRAttributeIDTypeClusterWiFiNetworkManagementAttributeEventListID MTR_PROVISIONALLY_AVAILABLE = MTRAttributeIDTypeGlobalAttributeEventListID, diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRClusterNames.mm b/src/darwin/Framework/CHIP/zap-generated/MTRClusterNames.mm index 3dc8d24df64cfd..2c9c0009d4449d 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRClusterNames.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRClusterNames.mm @@ -7411,6 +7411,10 @@ result = @"SSID"; break; + case MTRAttributeIDTypeClusterWiFiNetworkManagementAttributePassphraseSurrogateID: + result = @"PassphraseSurrogate"; + break; + case MTRAttributeIDTypeClusterWiFiNetworkManagementAttributeGeneratedCommandListID: result = @"GeneratedCommandList"; break; diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRClusters.h b/src/darwin/Framework/CHIP/zap-generated/MTRClusters.h index cc61d5762fb4f4..2ff2998f69d41b 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRClusters.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRClusters.h @@ -6158,6 +6158,8 @@ MTR_PROVISIONALLY_AVAILABLE - (NSDictionary * _Nullable)readAttributeSSIDWithParams:(MTRReadParams * _Nullable)params MTR_PROVISIONALLY_AVAILABLE; +- (NSDictionary * _Nullable)readAttributePassphraseSurrogateWithParams:(MTRReadParams * _Nullable)params MTR_PROVISIONALLY_AVAILABLE; + - (NSDictionary * _Nullable)readAttributeGeneratedCommandListWithParams:(MTRReadParams * _Nullable)params MTR_PROVISIONALLY_AVAILABLE; - (NSDictionary * _Nullable)readAttributeAcceptedCommandListWithParams:(MTRReadParams * _Nullable)params MTR_PROVISIONALLY_AVAILABLE; diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRClusters.mm b/src/darwin/Framework/CHIP/zap-generated/MTRClusters.mm index ba3ddfc6da8f38..d712da63d0a015 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRClusters.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRClusters.mm @@ -17075,6 +17075,11 @@ - (void)networkPassphraseRequestWithParams:(MTRWiFiNetworkManagementClusterNetwo return [self.device readAttributeWithEndpointID:self.endpointID clusterID:@(MTRClusterIDTypeWiFiNetworkManagementID) attributeID:@(MTRAttributeIDTypeClusterWiFiNetworkManagementAttributeSSIDID) params:params]; } +- (NSDictionary * _Nullable)readAttributePassphraseSurrogateWithParams:(MTRReadParams * _Nullable)params +{ + return [self.device readAttributeWithEndpointID:self.endpointID clusterID:@(MTRClusterIDTypeWiFiNetworkManagementID) attributeID:@(MTRAttributeIDTypeClusterWiFiNetworkManagementAttributePassphraseSurrogateID) params:params]; +} + - (NSDictionary * _Nullable)readAttributeGeneratedCommandListWithParams:(MTRReadParams * _Nullable)params { return [self.device readAttributeWithEndpointID:self.endpointID clusterID:@(MTRClusterIDTypeWiFiNetworkManagementID) attributeID:@(MTRAttributeIDTypeClusterWiFiNetworkManagementAttributeGeneratedCommandListID) params:params]; @@ -17356,9 +17361,6 @@ - (void)getOperationalDatasetWithParams:(MTRThreadNetworkDirectoryClusterGetOper }; auto * timedInvokeTimeoutMs = params.timedInvokeTimeoutMs; - if (timedInvokeTimeoutMs == nil) { - timedInvokeTimeoutMs = @(MTR_DEFAULT_TIMED_INTERACTION_TIMEOUT_MS); - } using RequestType = ThreadNetworkDirectory::Commands::GetOperationalDataset::Type; [self.device _invokeKnownCommandWithEndpointID:self.endpointID diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRCommandTimedCheck.mm b/src/darwin/Framework/CHIP/zap-generated/MTRCommandTimedCheck.mm index 6fdc015ad0aac3..6dc2c38f8447ef 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRCommandTimedCheck.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRCommandTimedCheck.mm @@ -1011,9 +1011,6 @@ static BOOL CommandNeedsTimedInvokeInThreadNetworkDirectoryCluster(AttributeId a case Commands::RemoveNetwork::Id: { return YES; } - case Commands::GetOperationalDataset::Id: { - return YES; - } default: { return NO; } diff --git a/src/platform/ESP32/BUILD.gn b/src/platform/ESP32/BUILD.gn index e835896fb59092..587388e3f969be 100644 --- a/src/platform/ESP32/BUILD.gn +++ b/src/platform/ESP32/BUILD.gn @@ -172,6 +172,12 @@ static_library("ESP32") { "../OpenThread/OpenThreadDnssdImpl.h", ] } + if (chip_openthread_border_router) { + sources += [ + "../OpenThread/GenericThreadBorderRouterDelegate.cpp", + "../OpenThread/GenericThreadBorderRouterDelegate.h", + ] + } configs -= [ "${chip_root}/build/config/compiler:warnings_default" ] } diff --git a/src/platform/OpenThread/GenericThreadBorderRouterDelegate.cpp b/src/platform/OpenThread/GenericThreadBorderRouterDelegate.cpp new file mode 100644 index 00000000000000..e1efc5357520af --- /dev/null +++ b/src/platform/OpenThread/GenericThreadBorderRouterDelegate.cpp @@ -0,0 +1,224 @@ +/* + * + * 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 "GenericThreadBorderRouterDelegate.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace app { +namespace Clusters { + +namespace ThreadBorderRouterManagement { + +class ScopedThreadLock +{ +public: + ScopedThreadLock() { DeviceLayer::ThreadStackMgr().LockThreadStack(); } + ~ScopedThreadLock() { DeviceLayer::ThreadStackMgr().UnlockThreadStack(); } +}; + +CHIP_ERROR GenericOpenThreadBorderRouterDelegate::Init(AttributeChangeCallback * callback) +{ + mpActivateDatasetCallback = nullptr; + mpAttributeChangeCallback = callback; + ReturnErrorOnFailure(DeviceLayer::PlatformMgrImpl().AddEventHandler(OnPlatformEventHandler, reinterpret_cast(this))); + // When the Thread Border Router is reboot during SetActiveDataset, we need to revert the active dateset. + RevertActiveDataset(); + return CHIP_NO_ERROR; +} + +CHIP_ERROR GenericOpenThreadBorderRouterDelegate::GetBorderAgentId(MutableByteSpan & borderAgentIdSpan) +{ + otInstance * otInst = DeviceLayer::ThreadStackMgrImpl().OTInstance(); + VerifyOrReturnError(otInst, CHIP_ERROR_INCORRECT_STATE); + otBorderAgentId borderAgentId; + if (borderAgentIdSpan.size() != sizeof(borderAgentId.mId)) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + otError otErr = OT_ERROR_NONE; + { + ScopedThreadLock threadLock; + otErr = otBorderAgentGetId(otInst, &borderAgentId); + } + if (otErr == OT_ERROR_NONE) + { + CopySpanToMutableSpan(ByteSpan(borderAgentId.mId), borderAgentIdSpan); + return CHIP_NO_ERROR; + } + return DeviceLayer::Internal::MapOpenThreadError(otErr); +} + +uint16_t GenericOpenThreadBorderRouterDelegate::GetThreadVersion() +{ + return otThreadGetVersion(); +} + +bool GenericOpenThreadBorderRouterDelegate::GetInterfaceEnabled() +{ + otInstance * otInst = DeviceLayer::ThreadStackMgrImpl().OTInstance(); + VerifyOrReturnValue(otInst, false); + ScopedThreadLock threadLock; + return otIp6IsEnabled(otInst); +} + +CHIP_ERROR GenericOpenThreadBorderRouterDelegate::GetDataset(Thread::OperationalDataset & dataset, DatasetType type) +{ + otInstance * otInst = DeviceLayer::ThreadStackMgrImpl().OTInstance(); + VerifyOrReturnError(otInst, CHIP_ERROR_INCORRECT_STATE); + + otError otErr = OT_ERROR_NONE; + otOperationalDatasetTlvs datasetTlvs; + { + ScopedThreadLock threadLock; + if (type == DatasetType::kActive) + { + otErr = otDatasetGetActiveTlvs(otInst, &datasetTlvs); + } + else + { + otErr = otDatasetGetPendingTlvs(otInst, &datasetTlvs); + } + } + if (otErr == OT_ERROR_NONE) + { + return dataset.Init(ByteSpan(datasetTlvs.mTlvs, datasetTlvs.mLength)); + } + return DeviceLayer::Internal::MapOpenThreadError(otErr); +} + +void GenericOpenThreadBorderRouterDelegate::SetActiveDataset(const Thread::OperationalDataset & activeDataset, uint32_t sequenceNum, + ActivateDatasetCallback * callback) +{ + // This function will never be invoked when there is an Active Dataset already configured. + CHIP_ERROR err = SaveActiveDatasetConfigured(false); + if (err == CHIP_NO_ERROR) + { + err = DeviceLayer::ThreadStackMgrImpl().AttachToThreadNetwork(activeDataset, nullptr); + } + if (err != CHIP_NO_ERROR) + { + callback->OnActivateDatasetComplete(sequenceNum, err); + return; + } + mSequenceNum = sequenceNum; + mpActivateDatasetCallback = callback; +} + +void GenericOpenThreadBorderRouterDelegate::OnPlatformEventHandler(const DeviceLayer::ChipDeviceEvent * event, intptr_t arg) +{ + GenericOpenThreadBorderRouterDelegate * delegate = reinterpret_cast(arg); + if (delegate) + { + if ((event->Type == DeviceLayer::DeviceEventType::kThreadConnectivityChange) && + (event->ThreadConnectivityChange.Result == DeviceLayer::kConnectivity_Established) && + delegate->mpActivateDatasetCallback) + { + delegate->mpActivateDatasetCallback->OnActivateDatasetComplete(delegate->mSequenceNum, CHIP_NO_ERROR); + delegate->mpActivateDatasetCallback = nullptr; + } + } + if (event->Type == DeviceLayer::DeviceEventType::kThreadStateChange) + { + if (event->ThreadStateChange.OpenThread.Flags & OT_CHANGED_THREAD_NETIF_STATE) + { + DeviceLayer::SystemLayer().ScheduleLambda( + [delegate]() { delegate->mpAttributeChangeCallback->ReportAttributeChanged(Attributes::InterfaceEnabled::Id); }); + } + if (event->ThreadStateChange.OpenThread.Flags & OT_CHANGED_ACTIVE_DATASET) + { + DeviceLayer::SystemLayer().ScheduleLambda([delegate]() { + delegate->mpAttributeChangeCallback->ReportAttributeChanged(Attributes::ActiveDatasetTimestamp::Id); + }); + } + } +} + +CHIP_ERROR GenericOpenThreadBorderRouterDelegate::SaveActiveDatasetConfigured(bool configured) +{ + VerifyOrReturnError(mStorage, CHIP_ERROR_INTERNAL); + return mStorage->SyncSetKeyValue(kFailsafeActiveDatasetConfigured, &configured, sizeof(bool)); +} + +CHIP_ERROR GenericOpenThreadBorderRouterDelegate::CommitActiveDataset() +{ + return SaveActiveDatasetConfigured(DeviceLayer::ThreadStackMgrImpl().IsThreadAttached()); +} + +CHIP_ERROR GenericOpenThreadBorderRouterDelegate::RevertActiveDataset() +{ + // The FailSafe Timer is triggered and the previous command request should be handled, so reset the callback. + mpActivateDatasetCallback = nullptr; + bool activeDatasetConfigured = true; + uint16_t activeDatasetConfiguredLen = sizeof(bool); + VerifyOrReturnError(mStorage, CHIP_ERROR_INTERNAL); + mStorage->SyncGetKeyValue(kFailsafeActiveDatasetConfigured, &activeDatasetConfigured, activeDatasetConfiguredLen); + VerifyOrDie(activeDatasetConfiguredLen == sizeof(bool)); + if (!activeDatasetConfigured) + { + // The active dataset should be no configured after calling this function, so we will try to attach an empty Thread dataset + // and that will clear the one stored in the Thread stack since the SetActiveDataset operation fails and FailSafe timer is + // triggered. + Thread::OperationalDataset emptyDataset = {}; + CHIP_ERROR err = DeviceLayer::ThreadStackMgrImpl().AttachToThreadNetwork(emptyDataset, nullptr); + SaveActiveDatasetConfigured(false); + return err; + } + return CHIP_NO_ERROR; +} + +CHIP_ERROR GenericOpenThreadBorderRouterDelegate::SetPendingDataset(const Thread::OperationalDataset & pendingDataset) +{ + otInstance * otInst = DeviceLayer::ThreadStackMgrImpl().OTInstance(); + VerifyOrReturnError(otInst, CHIP_ERROR_INCORRECT_STATE); + + otOperationalDatasetTlvs datasetTlvs; + memcpy(datasetTlvs.mTlvs, pendingDataset.AsByteSpan().data(), pendingDataset.AsByteSpan().size()); + datasetTlvs.mLength = pendingDataset.AsByteSpan().size(); + { + ScopedThreadLock threadLock; + ReturnErrorCodeIf(otDatasetSetPendingTlvs(otInst, &datasetTlvs) != OT_ERROR_NONE, CHIP_ERROR_INTERNAL); + } + return CHIP_NO_ERROR; +} + +} // namespace ThreadBorderRouterManagement +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/platform/OpenThread/GenericThreadBorderRouterDelegate.h b/src/platform/OpenThread/GenericThreadBorderRouterDelegate.h new file mode 100644 index 00000000000000..079f1bd6e38edd --- /dev/null +++ b/src/platform/OpenThread/GenericThreadBorderRouterDelegate.h @@ -0,0 +1,104 @@ +/* + * + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace app { +namespace Clusters { + +namespace ThreadBorderRouterManagement { + +class GenericOpenThreadBorderRouterDelegate : public Delegate +{ +public: + static constexpr char kFailsafeActiveDatasetConfigured[] = "g/fs/tbradc"; + GenericOpenThreadBorderRouterDelegate(PersistentStorageDelegate * storage) : mStorage(storage) {} + ~GenericOpenThreadBorderRouterDelegate() = default; + + CHIP_ERROR Init(AttributeChangeCallback * callback) override; + + bool GetPanChangeSupported() override { return true; } + + void GetBorderRouterName(MutableCharSpan & borderRouterName) override + { + CopyCharSpanToMutableCharSpan(CharSpan(mThreadBorderRouterName, strlen(mThreadBorderRouterName)), borderRouterName); + } + + CHIP_ERROR GetBorderAgentId(MutableByteSpan & borderAgentId) override; + + uint16_t GetThreadVersion() override; + + bool GetInterfaceEnabled() override; + + CHIP_ERROR GetDataset(Thread::OperationalDataset & dataset, DatasetType type) override; + + void SetActiveDataset(const Thread::OperationalDataset & activeDataset, uint32_t sequenceNum, + ActivateDatasetCallback * callback) override; + + CHIP_ERROR CommitActiveDataset() override; + + CHIP_ERROR RevertActiveDataset() override; + + CHIP_ERROR SetPendingDataset(const Thread::OperationalDataset & pendingDataset) override; + + static void OnPlatformEventHandler(const DeviceLayer::ChipDeviceEvent * event, intptr_t arg); + + void SetThreadBorderRouterName(const CharSpan & name) + { + MutableCharSpan borderRouterName(mThreadBorderRouterName); + CopyCharSpanToMutableCharSpan(name, borderRouterName); + if (mpAttributeChangeCallback) + { + DeviceLayer::SystemLayer().ScheduleLambda( + [this]() { mpAttributeChangeCallback->ReportAttributeChanged(Attributes::BorderRouterName::Id); }); + } + } + + void NotifyBorderAgentIdChanged() + { + if (mpAttributeChangeCallback) + { + // OpenThread doesn't have callback or event for BorderAgentId change, we can only change the BorderAgentId with + // otBorderAgentSetId(). Please call this function with otBorderAgentSetId(). + DeviceLayer::SystemLayer().ScheduleLambda( + [this]() { mpAttributeChangeCallback->ReportAttributeChanged(Attributes::BorderAgentID::Id); }); + } + } + +private: + CHIP_ERROR SaveActiveDatasetConfigured(bool configured); + ActivateDatasetCallback * mpActivateDatasetCallback = nullptr; + uint32_t mSequenceNum = 0; + char mThreadBorderRouterName[kBorderRouterNameMaxLength + 1]; + PersistentStorageDelegate * mStorage; + AttributeChangeCallback * mpAttributeChangeCallback = nullptr; +}; +} // namespace ThreadBorderRouterManagement +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/python_testing/TC_EWATERHTRBase.py b/src/python_testing/TC_EWATERHTRBase.py new file mode 100644 index 00000000000000..0bf42a7eb4d0a0 --- /dev/null +++ b/src/python_testing/TC_EWATERHTRBase.py @@ -0,0 +1,94 @@ +# +# 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. + + +import logging +import typing + +import chip.clusters as Clusters +from chip.interaction_model import InteractionModelError, Status +from mobly import asserts + +logger = logging.getLogger(__name__) + + +class EWATERHTRBase: + + async def read_whm_attribute_expect_success(self, endpoint: int = None, attribute: str = ""): + cluster = Clusters.Objects.WaterHeaterManagement + full_attr = getattr(cluster.Attributes, attribute) + return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=full_attr) + + async def check_whm_attribute(self, attribute, expected_value, endpoint: int = None): + value = await self.read_whm_attribute_expect_success(endpoint=endpoint, attribute=attribute) + asserts.assert_equal(value, expected_value, + f"Unexpected '{attribute}' value - expected {expected_value}, was {value}") + + async def send_boost_command(self, duration: int, one_shot: typing.Optional[bool] = None, emergency_boost: typing.Optional[bool] = None, + temporary_setpoint: typing.Optional[int] = None, target_percentage: typing.Optional[int] = None, target_reheat: typing.Optional[int] = None, + 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) + + asserts.assert_equal(expected_status, Status.Success) + + except InteractionModelError as e: + asserts.assert_equal(e.status, expected_status, "Unexpected error returned") + + async def send_cancel_boost_command(self, endpoint: int = None, timedRequestTimeoutMs: int = 3000, + expected_status: Status = Status.Success): + try: + await self.send_single_cmd(cmd=Clusters.WaterHeaterManagement.Commands.CancelBoost(), + endpoint=endpoint, + timedRequestTimeoutMs=timedRequestTimeoutMs) + + asserts.assert_equal(expected_status, Status.Success) + + except InteractionModelError as e: + asserts.assert_equal(e.status, expected_status, "Unexpected error returned") + + async def send_test_event_trigger_basic_installation_test_event(self): + await self.send_test_event_triggers(eventTrigger=0x0094000000000000) + + async def send_test_event_trigger_basic_installation_test_event_clear(self): + await self.send_test_event_triggers(eventTrigger=0x0094000000000001) + + async def send_test_event_trigger_water_temperature20C_test_event(self): + await self.send_test_event_triggers(eventTrigger=0x0094000000000002) + + async def send_test_event_trigger_water_temperature61C_test_event(self): + await self.send_test_event_triggers(eventTrigger=0x0094000000000003) + + async def send_test_event_trigger_water_temperature66C_test_event(self): + await self.send_test_event_triggers(eventTrigger=0x0094000000000004) + + async def send_test_event_trigger_manual_mode_test_event(self): + await self.send_test_event_triggers(eventTrigger=0x0094000000000005) + + async def send_test_event_trigger_off_mode_test_event(self): + await self.send_test_event_triggers(eventTrigger=0x0094000000000006) + + async def send_test_event_trigger_draw_off_hot_water_test_event(self): + await self.send_test_event_triggers(eventTrigger=0x0094000000000007) diff --git a/src/python_testing/TC_EWATERHTR_2_1.py b/src/python_testing/TC_EWATERHTR_2_1.py new file mode 100644 index 00000000000000..c278da6c094010 --- /dev/null +++ b/src/python_testing/TC_EWATERHTR_2_1.py @@ -0,0 +1,121 @@ +# +# 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: ${ALL_CLUSTERS_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 --enable-key 000102030405060708090a0b0c0d0e0f --featureSet 0x03 +# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --hex-arg enableKey:000102030405060708090a0b0c0d0e0f --endpoint 1 --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto --int-arg PIXIT.EWATERHTR.EM:1 PIXIT.EWATERHTR.TP:2 +# === END CI TEST ARGUMENTS === + +import logging + +import chip.clusters as Clusters +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from mobly import asserts +from TC_EWATERHTRBase import EWATERHTRBase + +logger = logging.getLogger(__name__) + + +class TC_EWATERHTR_2_1(MatterBaseTest, EWATERHTRBase): + + def desc_TC_EWATERHTR_2_1(self) -> str: + """Returns a description of this test""" + return "[TC-EWATERHTR-2.1] Attributes with attributes with DUT as Server\n" \ + "This test case verifies the non-global attributes of the Water Heater Management cluster server." + + def pics_TC_EWATERHTR_2_1(self): + """ This function returns a list of PICS for this test case that must be True for the test to be run""" + return ["EWATERHTR.S"] + + def steps_TC_EWATERHTR_2_1(self) -> list[TestStep]: + steps = [ + 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)."), + TestStep("3", "TH reads HeatDemand attribute.", + "DUT as Server replies with a WaterHeaterDemandBitmap (enum8)."), + TestStep("4", "TH reads TankVolume attribute.", + "DUT as Server replies with a uint16 value."), + TestStep("5", "TH reads EstimatedHeatRequired attribute.", + "DUT as Server replies with an energy-mWh value."), + TestStep("6", "TH reads TankPercentage attribute.", + "DUT as Server replies with a percent value."), + TestStep("7", "TH reads BoostState attribute.", + "DUT as Server replies with a BoostStateEnum (enum8) value."), + ] + + return steps + + @async_test_body + async def test_TC_EWATERHTR_2_1(self): + + em_supported = self.matter_test_config.global_test_params['PIXIT.EWATERHTR.EM'] + tp_supported = self.matter_test_config.global_test_params['PIXIT.EWATERHTR.TP'] + + self.step("1") + # Commission DUT - already done + + self.step("2") + 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") + + 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") + + self.step("4") + if em_supported: + value = await self.read_whm_attribute_expect_success(attribute="TankVolume") + else: + logging.info("Skipping step 4 as PIXIT.EWATERHTR.EM not supported") + + self.step("5") + if em_supported: + value = await self.read_whm_attribute_expect_success(attribute="EstimatedHeatRequired") + asserts.assert_greater_equal(value, 0, f"Unexpected EstimatedHeatRequired value - expected {value} >= 0") + else: + logging.info("Skipping step 5 as PIXIT.EWATERHTR.EM not supported") + + self.step("6") + if tp_supported: + value = await self.read_whm_attribute_expect_success(attribute="TankPercentage") + asserts.assert_greater_equal(value, 0, f"Unexpected TankPercentage value - expected {value} >= 0") + asserts.assert_less_equal(value, 100, f"Unexpected TankPercentage value - expected {value} <= 100") + else: + logging.info("Skipping step 6 as PIXIT.EWATERHTR.TP not supported") + + self.step("7") + boost_state = await self.read_whm_attribute_expect_success(attribute="BoostState") + asserts.assert_less_equal(boost_state, Clusters.WaterHeaterManagement.Enums.BoostStateEnum.kInactive, + f"Unexpected BoostState value - expected {boost_state} should be BoostStateEnum (enum8) value in range 0x00 to 0x01") + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_EWATERHTR_2_2.py b/src/python_testing/TC_EWATERHTR_2_2.py new file mode 100644 index 00000000000000..0d20e1d4af97f4 --- /dev/null +++ b/src/python_testing/TC_EWATERHTR_2_2.py @@ -0,0 +1,378 @@ +# +# 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: ${ALL_CLUSTERS_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 --enable-key 000102030405060708090a0b0c0d0e0f --featureSet 0x00 +# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --hex-arg enableKey:000102030405060708090a0b0c0d0e0f --endpoint 1 --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 +from TC_EWATERHTRBase import EWATERHTRBase + +logger = logging.getLogger(__name__) + + +class TC_EWATERHTR_2_2(MatterBaseTest, EWATERHTRBase): + + def desc_TC_EWATERHTR_2_2(self) -> str: + """Returns a description of this test""" + return "[TC-EWATERHTR-2.2] Basic functionality with attributes with DUT as Server." \ + "This test case verifies the primary functionality of the Water Heater Management cluster server." + + def pics_TC_EWATERHTR_2_2(self): + """ This function returns a list of PICS for this test case that must be True for the test to be run""" + return ["EWATERHTR.S"] + + def steps_TC_EWATERHTR_2_2(self) -> list[TestStep]: + steps = [ + TestStep("1", "Commissioning, already done", + is_commissioning=True), + TestStep("2", "TH reads TestEventTriggersEnabled attribute from General Diagnostics Cluster.", + "Verify value is 1 (True)"), + TestStep("3", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER for Basic installation Test Event.", + "Verify Command response is Success"), + TestStep("3a", "TH reads HeatDemand attribute.", + "Verify value is 0x00 (no demand on any source)"), + TestStep("3b", "TH reads BoostState attribute.", + "Verify value is 0 (Inactive)"), + TestStep("3c", "TH reads HeaterTypes attribute.", + "Verify value is greater than 0x00 (at least one type supported) and store the value as HeaterTypes"), + TestStep("4", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER for Manual mode Test Event.", + "Verify Command response is Success"), + TestStep("4a", "TH reads HeatDemand attribute.", + "Verify value is greater than 0x00 (demand on at least one source) and (HeaterDemand & (!HeaterTypes)) is zero (demand is only from declared supported types)"), + TestStep("5", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER for Water Temperature 61C Test Event.", + "Verify Command response is Success"), + TestStep("5a", "TH reads HeatDemand attribute.", + "Verify value is 0x00 (no demand on any source)"), + TestStep("6", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER for Water Temperature 20C Test Event.", + "Verify Command response is Success"), + TestStep("6a", "TH reads HeatDemand attribute.", + "Verify value is greater than 0x00 (demand on at least one source)"), + TestStep("7", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER for Off mode Test Event.", + "Verify Command response is Success"), + TestStep("7a", "TH reads HeatDemand attribute.", + "Verify value is 0x00 (no demand on any source)"), + TestStep("8", "TH sends Boost with Duration=5s,OneShot=True.", + "Verify Command response is Success"), + TestStep("8a", "TH reads HeatDemand attribute.", + "Verify value is greater than 0x00 (demand on at least one source) and (HeaterDemand & (!HeaterTypes)) is zero (demand is only from declared supported types)"), + TestStep("8b", "TH reads BoostState attribute.", + "Verify value is 1 (Active)"), + TestStep("9", "Wait 6 seconds"), + TestStep("9a", "TH reads HeatDemand attribute.", + "Verify value is 0x00 (no demand on any source)"), + TestStep("9b", "TH reads BoostState attribute.", + "Verify value is 0 (Inactive)"), + TestStep("10", "TH sends Boost with Duration=600s,OneShot=True.", + "Verify Command response is Success"), + TestStep("10a", "TH reads HeatDemand attribute.", + "Verify value is greater than 0x00 (demand on at least one source) and (HeaterDemand & (!HeaterTypes)) is zero (demand is only from declared supported types)"), + TestStep("10b", "TH reads BoostState attribute.", + "Verify value is 1 (Active)"), + TestStep("11", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER for Water Temperature 61C Test Event.", + "Verify Command response is Success"), + TestStep("11a", "TH reads HeatDemand attribute.", + "Verify value is 0x00 (no demand on any source)"), + TestStep("11b", "TH reads BoostState attribute.", + "Verify value is 0 (Inactive)"), + TestStep("12", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER for Water Temperature 20C Test Event.", + "Verify Command response is Success"), + TestStep("12a", "TH reads HeatDemand attribute.", + "Verify value is 0x00 (no demand on any source)"), + TestStep("13", "TH sends Boost with Duration=600s.", + "Verify Command response is Success"), + TestStep("13a", "TH reads HeatDemand attribute.", + "Verify value is greater than 0x00 (demand on at least one source) and (HeaterDemand & (!HeaterTypes)) is zero (demand is only from declared supported types)"), + TestStep("13b", "TH reads BoostState attribute.", + "Verify value is 1 (Active)"), + TestStep("14", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER for Water Temperature 61C Test Event.", + "Verify Command response is Success"), + TestStep("14a", "TH reads HeatDemand attribute.", + "Verify value is 0x00 (no demand on any source)"), + TestStep("14b", "TH reads BoostState attribute.", + "Verify value is 1 (Active)"), + TestStep("15", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER for Water Temperature 20C Test Event.", + "Verify Command response is Success"), + TestStep("15a", "TH reads HeatDemand attribute.", + "Verify value is greater than 0x00 (demand on at least one source) and (HeaterDemand & (!HeaterTypes)) is zero (demand is only from declared supported types)"), + TestStep("15b", "TH reads BoostState attribute.", + "Verify value is 1 (Active)"), + TestStep("16", "TH sends CancelBoost.", + "Verify Command response is Success"), + TestStep("16a", "TH reads HeatDemand attribute.", + "Verify value is 0x00 (no demand on any source)"), + TestStep("16b", "TH reads BoostState attribute.", + "Verify value is 0 (Inactive)"), + TestStep("17", "TH sends Boost with Duration=600s,TemporarySetpoint=65C.", + "Verify Command response is Success"), + TestStep("17a", "TH reads HeatDemand attribute.", + "Verify value is greater than 0x00 (demand on at least one source) and (HeaterDemand & (!HeaterTypes)) is zero (demand is only from declared supported types)"), + TestStep("17b", "TH reads BoostState attribute.", + "Verify value is 1 (Active)"), + TestStep("18", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER for Water Temperature 61C Test Event.", + "Verify Command response is Success"), + TestStep("18a", "TH reads HeatDemand attribute.", + "Verify value is greater than 0x00 (demand on at least one source) and (HeaterDemand & (!HeaterTypes)) is zero (demand is only from declared supported types)"), + TestStep("18b", "TH reads BoostState attribute.", + "Verify value is 1 (Active)"), + TestStep("19", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER for Water Temperature 66C Test Event.", + "Verify Command response is Success"), + TestStep("19a", "TH reads HeatDemand attribute.", + "Verify value is 0x00 (no demand on any source)"), + TestStep("19b", "TH reads BoostState attribute.", + "Verify value is 1 (Active)"), + TestStep("20", "TH sends Boost with Duration=600s,TemporarySetpoint=70C.", + "Verify Command response is Success"), + TestStep("20a", "TH reads HeatDemand attribute.", + "Verify value is greater than 0x00 (demand on at least one source) and (HeaterDemand & (!HeaterTypes)) is zero (demand is only from declared supported types)"), + TestStep("20b", "TH reads BoostState attribute.", + "Verify value is 1 (Active)"), + TestStep("21", "TH sends CancelBoost.", + "Verify Command response is Success"), + TestStep("21a", "TH reads HeatDemand attribute.", + "Verify value is 0x00 (no demand on any source)"), + TestStep("21b", "TH reads BoostState attribute.", + "Verify value is 0 (Inactive)"), + TestStep("22", "TH sends CancelBoost.", + "Verify Command response is Success"), + TestStep("23", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER for Basic installation Test Event Clear.", + "Verify Command response is Success"), + ] + + return steps + + @async_test_body + async def test_TC_EWATERHTR_2_2(self): + + self.step("1") + # Commission DUT - already done + + self.step("2") + await self.check_test_event_triggers_enabled() + + self.step("3") + await self.send_test_event_trigger_basic_installation_test_event() + + self.step("3a") + await self.check_whm_attribute("HeatDemand", 0) + + self.step("3b") + await self.check_whm_attribute("BoostState", Clusters.WaterHeaterManagement.Enums.BoostStateEnum.kInactive) + + self.step("3c") + 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") + + self.step("4") + await self.send_test_event_trigger_manual_mode_test_event() + + self.step("4a") + heatDemand = await self.read_whm_attribute_expect_success(attribute="HeatDemand") + asserts.assert_greater(heatDemand, 0) + asserts.assert_equal(heatDemand & (~heaterTypes), 0, "heatDemand should only be from declared supported types"), + + self.step("5") + await self.send_test_event_trigger_water_temperature61C_test_event() + + self.step("5a") + heatDemand = await self.read_whm_attribute_expect_success(attribute="HeatDemand") + asserts.assert_equal(heatDemand, 0) + + self.step("6") + await self.send_test_event_trigger_water_temperature20C_test_event() + + self.step("6a") + heatDemand = await self.read_whm_attribute_expect_success(attribute="HeatDemand") + asserts.assert_greater(heatDemand, 0) + asserts.assert_equal(heatDemand & (~heaterTypes), 0, "heatDemand should only be from declared supported types"), + + self.step("7") + await self.send_test_event_trigger_off_mode_test_event() + + self.step("7a") + heatDemand = await self.read_whm_attribute_expect_success(attribute="HeatDemand") + asserts.assert_equal(heatDemand, 0) + + self.step("8") + await self.send_boost_command(duration=5, one_shot=True) + + self.step("8a") + heatDemand = await self.read_whm_attribute_expect_success(attribute="HeatDemand") + asserts.assert_greater(heatDemand, 0) + asserts.assert_equal(heatDemand & (~heaterTypes), 0, "heatDemand should only be from declared supported types"), + + self.step("8b") + await self.check_whm_attribute("BoostState", Clusters.WaterHeaterManagement.Enums.BoostStateEnum.kActive) + + self.step("9") + time.sleep(6) + + self.step("9a") + heatDemand = await self.read_whm_attribute_expect_success(attribute="HeatDemand") + asserts.assert_equal(heatDemand, 0) + + self.step("9b") + await self.check_whm_attribute("BoostState", Clusters.WaterHeaterManagement.Enums.BoostStateEnum.kInactive) + + self.step("10") + await self.send_boost_command(duration=600, one_shot=True) + + self.step("10a") + heatDemand = await self.read_whm_attribute_expect_success(attribute="HeatDemand") + asserts.assert_greater(heatDemand, 0) + asserts.assert_equal(heatDemand & (~heaterTypes), 0, "heatDemand should only be from declared supported types"), + + self.step("10b") + await self.check_whm_attribute("BoostState", Clusters.WaterHeaterManagement.Enums.BoostStateEnum.kActive) + + self.step("11") + await self.send_test_event_trigger_water_temperature61C_test_event() + + self.step("11a") + heatDemand = await self.read_whm_attribute_expect_success(attribute="HeatDemand") + asserts.assert_equal(heatDemand, 0) + + self.step("11b") + await self.check_whm_attribute("BoostState", Clusters.WaterHeaterManagement.Enums.BoostStateEnum.kInactive) + + self.step("12") + await self.send_test_event_trigger_water_temperature20C_test_event() + + self.step("12a") + heatDemand = await self.read_whm_attribute_expect_success(attribute="HeatDemand") + asserts.assert_equal(heatDemand, 0) + + self.step("13") + await self.send_boost_command(duration=600) + + self.step("13a") + heatDemand = await self.read_whm_attribute_expect_success(attribute="HeatDemand") + asserts.assert_greater(heatDemand, 0) + asserts.assert_equal(heatDemand & (~heaterTypes), 0, "heatDemand should only be from declared supported types"), + + self.step("13b") + await self.check_whm_attribute("BoostState", Clusters.WaterHeaterManagement.Enums.BoostStateEnum.kActive) + + self.step("14") + await self.send_test_event_trigger_water_temperature61C_test_event() + + self.step("14a") + heatDemand = await self.read_whm_attribute_expect_success(attribute="HeatDemand") + asserts.assert_equal(heatDemand, 0) + + self.step("14b") + await self.check_whm_attribute("BoostState", Clusters.WaterHeaterManagement.Enums.BoostStateEnum.kActive) + + self.step("15") + await self.send_test_event_trigger_water_temperature20C_test_event() + + self.step("15a") + heatDemand = await self.read_whm_attribute_expect_success(attribute="HeatDemand") + asserts.assert_greater(heatDemand, 0) + asserts.assert_equal(heatDemand & (~heaterTypes), 0, "heatDemand should only be from declared supported types"), + + self.step("15b") + await self.check_whm_attribute("BoostState", Clusters.WaterHeaterManagement.Enums.BoostStateEnum.kActive) + + self.step("16") + await self.send_cancel_boost_command() + + self.step("16a") + heatDemand = await self.read_whm_attribute_expect_success(attribute="HeatDemand") + asserts.assert_equal(heatDemand, 0) + + self.step("16b") + await self.check_whm_attribute("BoostState", Clusters.WaterHeaterManagement.Enums.BoostStateEnum.kInactive) + + self.step("17") + await self.send_boost_command(duration=600, temporary_setpoint=6500) + + self.step("17a") + heatDemand = await self.read_whm_attribute_expect_success(attribute="HeatDemand") + asserts.assert_greater(heatDemand, 0) + asserts.assert_equal(heatDemand & (~heaterTypes), 0, "heatDemand should only be from declared supported types"), + + self.step("17b") + await self.check_whm_attribute("BoostState", Clusters.WaterHeaterManagement.Enums.BoostStateEnum.kActive) + + self.step("18") + await self.send_test_event_trigger_water_temperature61C_test_event() + + self.step("18a") + heatDemand = await self.read_whm_attribute_expect_success(attribute="HeatDemand") + asserts.assert_greater(heatDemand, 0) + asserts.assert_equal(heatDemand & (~heaterTypes), 0, "heatDemand should only be from declared supported types"), + + self.step("18b") + await self.check_whm_attribute("BoostState", Clusters.WaterHeaterManagement.Enums.BoostStateEnum.kActive) + + self.step("19") + await self.send_test_event_trigger_water_temperature66C_test_event() + + self.step("19a") + heatDemand = await self.read_whm_attribute_expect_success(attribute="HeatDemand") + asserts.assert_equal(heatDemand, 0) + + self.step("19b") + await self.check_whm_attribute("BoostState", Clusters.WaterHeaterManagement.Enums.BoostStateEnum.kActive) + + self.step("20") + await self.send_boost_command(duration=600, temporary_setpoint=7000) + + self.step("20a") + heatDemand = await self.read_whm_attribute_expect_success(attribute="HeatDemand") + asserts.assert_greater(heatDemand, 0) + asserts.assert_equal(heatDemand & (~heaterTypes), 0, "heatDemand should only be from declared supported types"), + + self.step("20b") + await self.check_whm_attribute("BoostState", Clusters.WaterHeaterManagement.Enums.BoostStateEnum.kActive) + + self.step("21") + await self.send_cancel_boost_command() + + self.step("21a") + heatDemand = await self.read_whm_attribute_expect_success(attribute="HeatDemand") + asserts.assert_equal(heatDemand, 0) + + self.step("21b") + await self.check_whm_attribute("BoostState", Clusters.WaterHeaterManagement.Enums.BoostStateEnum.kInactive) + + self.step("22") + await self.send_cancel_boost_command() + + self.step("23") + await self.send_test_event_trigger_basic_installation_test_event_clear() + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_EWATERHTR_2_3.py b/src/python_testing/TC_EWATERHTR_2_3.py new file mode 100644 index 00000000000000..b9572c9520d843 --- /dev/null +++ b/src/python_testing/TC_EWATERHTR_2_3.py @@ -0,0 +1,285 @@ +# +# 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: ${ALL_CLUSTERS_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 --enable-key 000102030405060708090a0b0c0d0e0f --featureSet 0x03 +# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --hex-arg enableKey:000102030405060708090a0b0c0d0e0f --endpoint 1 --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto +# === END CI TEST ARGUMENTS === + + +import logging + +import chip.clusters as Clusters +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from mobly import asserts +from TC_EWATERHTRBase import EWATERHTRBase + +logger = logging.getLogger(__name__) + + +class TC_EWATERHTR_2_3(MatterBaseTest, EWATERHTRBase): + + def desc_TC_EWATERHTR_2_3(self) -> str: + """Returns a description of this test""" + return "[TC-EWATERHTR-2.3] This test case verifies the functionality of the Water Heater Management cluster server with the TankPercentage feature." + + def pics_TC_EWATERHTR_2_3(self): + """ This function returns a list of PICS for this test case that must be True for the test to be run""" + return ["EWATERHTR.S", "EWATERHTR.S.F01"] + + def steps_TC_EWATERHTR_2_3(self) -> list[TestStep]: + steps = [ + TestStep("1", "Commissioning, already done", + is_commissioning=True), + TestStep("2", "TH reads TestEventTriggersEnabled attribute from General Diagnostics Cluster.", + "Verify value is 1 (True)"), + TestStep("3", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER for Basic installation Test Event.", + "Verify Command response is Success"), + TestStep("3a", "TH reads HeatDemand attribute.", + "Verify value is 0x00 (no demand on any source)"), + TestStep("3b", "TH reads BoostState attribute.", + "Verify value is 0 (Inactive)"), + TestStep("3c", "TH reads TankPercentage attribute.", + "Verify value is 0%"), + TestStep("3d", "TH reads HeaterTypes attribute.", + "Verify value is greater than 0x00 (at least one type supported) and store the value as HeaterTypes"), + TestStep("4", "TH sends Boost with Duration=600s,TargetPercentage=100%.", + "Verify Command response is Success"), + TestStep("4a", "TH reads HeatDemand attribute.", + "Verify value is greater than 0x00 (demand on at least one source) and (HeaterDemand & (!HeaterTypes)) is zero (demand is only from declared supported types)"), + TestStep("4b", "TH reads BoostState attribute.", + "Verify value is 1 (Active)"), + TestStep("5", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER for Water Temperature 61C Test Event.", + "Verify Command response is Success"), + TestStep("5a", "TH reads HeatDemand attribute.", + "Verify value is 0x00 (no demand on any source)"), + TestStep("5b", "TH reads BoostState attribute.", + "Verify value is 1 (Active)"), + TestStep("5c", "TH reads TankPercentage attribute.", + "Verify value is 100%"), + TestStep("6", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER for Draw off hot water Test Event.", + "Verify Command response is Success"), + TestStep("6a", "TH reads HeatDemand attribute.", + "Verify value is greater than 0x00 (demand on at least one source) and (HeaterDemand & (!HeaterTypes)) is zero (demand is only from declared supported types)"), + TestStep("6b", "TH reads BoostState attribute.", + "Verify value is 1 (Active)"), + TestStep("6c", "TH reads TankPercentage attribute.", + "Verify value is 75%"), + TestStep("7", "TH sends CancelBoost.", + "Verify Command response is Success"), + TestStep("7a", "TH reads HeatDemand attribute.", + "Verify value is 0x00 (no demand on any source)"), + TestStep("7b", "TH reads BoostState attribute.", + "Verify value is 0 (Inactive)"), + TestStep("7c", "TH reads TankPercentage attribute.", + "Verify value is 75%"), + TestStep("8", "TH sends Boost with Duration=600s,TargetPercentage=100%,TargetReheat=65%.", + "Verify Command response is Success"), + TestStep("8a", "TH reads HeatDemand attribute.", + "Verify value is greater than 0x00 (demand on at least one source) and (HeaterDemand & (!HeaterTypes)) is zero (demand is only from declared supported types)"), + TestStep("8b", "TH reads BoostState attribute.", + "Verify value is 1 (Active)"), + TestStep("8c", "TH reads TankPercentage attribute.", + "Verify value is 75%"), + TestStep("9", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER for Water Temperature 61C Test Event.", + "Verify Command response is Success"), + TestStep("9a", "TH reads HeatDemand attribute.", + "Verify value is 0x00 (no demand on any source)"), + TestStep("9b", "TH reads BoostState attribute.", + "Verify value is 1 (Active)"), + TestStep("9c", "TH reads TankPercentage attribute.", + "Verify value is 100%"), + TestStep("10", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER for Draw off hot water Test Event.", + "Verify Command response is Success"), + TestStep("10a", "TH reads HeatDemand attribute.", + "Verify value is 0x00 (no demand on any source)"), + TestStep("10b", "TH reads BoostState attribute.", + "Verify value is 1 (Active)"), + TestStep("10c", "TH reads TankPercentage attribute.", + "Verify value is 75%"), + TestStep("11", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER for Draw off hot water Test Event.", + "Verify Command response is Success"), + TestStep("11a", "TH reads HeatDemand attribute.", + "Verify value is greater than 0x00 (demand on at least one source) and (HeaterDemand & (!HeaterTypes)) is zero (demand is only from declared supported types)"), + TestStep("11b", "TH reads BoostState attribute.", + "Verify value is 1 (Active)"), + TestStep("11c", "TH reads TankPercentage attribute.", + "Verify value is 50%"), + TestStep("12", "TH sends CancelBoost.", + "Verify Command response is Success"), + TestStep("12a", "TH reads HeatDemand attribute.", + "Verify value is 0x00 (no demand on any source)"), + TestStep("12b", "TH reads BoostState attribute.", + "Verify value is 0 (Inactive)"), + TestStep("12c", "TH reads TankPercentage attribute.", + "Verify value is 50%"), + TestStep("13", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EWATERHTR.TEST_EVENT_TRIGGER for Basic installation Test Event Clear.", + "Verify Command response is Success"), + ] + + return steps + + @async_test_body + async def test_TC_EWATERHTR_2_3(self): + + self.step("1") + # Commission DUT - already done + + self.step("2") + await self.check_test_event_triggers_enabled() + + self.step("3") + await self.send_test_event_trigger_basic_installation_test_event() + + self.step("3a") + await self.check_whm_attribute("HeatDemand", 0) + + self.step("3b") + await self.check_whm_attribute("BoostState", Clusters.WaterHeaterManagement.Enums.BoostStateEnum.kInactive) + + self.step("3c") + await self.check_whm_attribute("TankPercentage", 0) + + self.step("3d") + heaterTypes = await self.read_whm_attribute_expect_success(attribute="HeaterTypes") + asserts.assert_greater(heaterTypes, 0) + + self.step("4") + await self.send_boost_command(duration=600, target_percentage=100) + + self.step("4a") + heatDemand = await self.read_whm_attribute_expect_success(attribute="HeatDemand") + asserts.assert_greater(heatDemand, 0) + asserts.assert_equal(heatDemand & (~heaterTypes), 0, "heatDemand should only be from declared supported types"), + + self.step("4b") + await self.check_whm_attribute("BoostState", Clusters.WaterHeaterManagement.Enums.BoostStateEnum.kActive) + + self.step("5") + await self.send_test_event_trigger_water_temperature61C_test_event() + + self.step("5a") + heatDemand = await self.read_whm_attribute_expect_success(attribute="HeatDemand") + asserts.assert_equal(heatDemand, 0) + + self.step("5b") + await self.check_whm_attribute("BoostState", Clusters.WaterHeaterManagement.Enums.BoostStateEnum.kActive) + + self.step("5c") + await self.check_whm_attribute("TankPercentage", 100) + + self.step("6") + await self.send_test_event_trigger_draw_off_hot_water_test_event() + + self.step("6a") + heatDemand = await self.read_whm_attribute_expect_success(attribute="HeatDemand") + asserts.assert_greater(heatDemand, 0) + asserts.assert_equal(heatDemand & (~heaterTypes), 0, "heatDemand should only be from declared supported types"), + + self.step("6b") + await self.check_whm_attribute("BoostState", Clusters.WaterHeaterManagement.Enums.BoostStateEnum.kActive) + + self.step("6c") + await self.check_whm_attribute("TankPercentage", 75) + + self.step("7") + await self.send_cancel_boost_command() + + self.step("7a") + await self.check_whm_attribute("HeatDemand", 0) + + self.step("7b") + await self.check_whm_attribute("BoostState", Clusters.WaterHeaterManagement.Enums.BoostStateEnum.kInactive) + + self.step("7c") + await self.check_whm_attribute("TankPercentage", 75) + + self.step("8") + await self.send_boost_command(duration=600, target_percentage=100, target_reheat=65) + + self.step("8a") + heatDemand = await self.read_whm_attribute_expect_success(attribute="HeatDemand") + asserts.assert_greater(heatDemand, 0) + asserts.assert_equal(heatDemand & (~heaterTypes), 0, "heatDemand should only be from declared supported types"), + + self.step("8b") + await self.check_whm_attribute("BoostState", Clusters.WaterHeaterManagement.Enums.BoostStateEnum.kActive) + + self.step("8c") + await self.check_whm_attribute("TankPercentage", 75) + + self.step("9") + await self.send_test_event_trigger_water_temperature61C_test_event() + + self.step("9a") + await self.check_whm_attribute("HeatDemand", 0) + + self.step("9b") + await self.check_whm_attribute("BoostState", Clusters.WaterHeaterManagement.Enums.BoostStateEnum.kActive) + + self.step("9c") + await self.check_whm_attribute("TankPercentage", 100) + + self.step("10") + await self.send_test_event_trigger_draw_off_hot_water_test_event() + + self.step("10a") + await self.check_whm_attribute("HeatDemand", 0) + + self.step("10b") + await self.check_whm_attribute("BoostState", Clusters.WaterHeaterManagement.Enums.BoostStateEnum.kActive) + + self.step("10c") + await self.check_whm_attribute("TankPercentage", 75) + + self.step("11") + await self.send_test_event_trigger_draw_off_hot_water_test_event() + + self.step("11a") + heatDemand = await self.read_whm_attribute_expect_success(attribute="HeatDemand") + asserts.assert_greater(heatDemand, 0) + asserts.assert_equal(heatDemand & (~heaterTypes), 0, "heatDemand should only be from declared supported types"), + + self.step("11b") + await self.check_whm_attribute("BoostState", Clusters.WaterHeaterManagement.Enums.BoostStateEnum.kActive) + + self.step("11c") + await self.check_whm_attribute("TankPercentage", 50) + + self.step("12") + await self.send_cancel_boost_command() + + self.step("12a") + await self.check_whm_attribute("HeatDemand", 0) + + self.step("12b") + await self.check_whm_attribute("BoostState", Clusters.WaterHeaterManagement.Enums.BoostStateEnum.kInactive) + + self.step("12c") + await self.check_whm_attribute("TankPercentage", 50) + + self.step("13") + await self.send_test_event_trigger_basic_installation_test_event_clear() + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.cpp b/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.cpp index 7a81f296f9d926..bffec4a84cee77 100644 --- a/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.cpp +++ b/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.cpp @@ -35135,333 +35135,6 @@ Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint16_t valu namespace ThreadBorderRouterManagement { namespace Attributes { -namespace BorderRouterName { - -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, chip::MutableCharSpan & value) -{ - uint8_t zclString[63 + 1]; - Protocols::InteractionModel::Status status = - emberAfReadAttribute(endpoint, Clusters::ThreadBorderRouterManagement::Id, Id, zclString, sizeof(zclString)); - VerifyOrReturnError(Protocols::InteractionModel::Status::Success == status, status); - size_t length = emberAfStringLength(zclString); - if (length == NumericAttributeTraits::kNullValue) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - - VerifyOrReturnError(value.size() == 63, Protocols::InteractionModel::Status::InvalidDataType); - memcpy(value.data(), &zclString[1], 63); - value.reduce_size(length); - return status; -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, chip::CharSpan value, MarkAttributeDirty markDirty) -{ - - static_assert(63 < NumericAttributeTraits::kNullValue, "value.size() might be too big"); - VerifyOrReturnError(value.size() <= 63, Protocols::InteractionModel::Status::ConstraintError); - uint8_t zclString[63 + 1]; - auto length = static_cast(value.size()); - Encoding::Put8(zclString, length); - memcpy(&zclString[1], value.data(), value.size()); - return emberAfWriteAttribute(endpoint, Clusters::ThreadBorderRouterManagement::Id, Id, zclString, - ZCL_CHAR_STRING_ATTRIBUTE_TYPE, markDirty); -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, chip::CharSpan value) -{ - - static_assert(63 < NumericAttributeTraits::kNullValue, "value.size() might be too big"); - VerifyOrReturnError(value.size() <= 63, Protocols::InteractionModel::Status::ConstraintError); - uint8_t zclString[63 + 1]; - auto length = static_cast(value.size()); - Encoding::Put8(zclString, length); - memcpy(&zclString[1], value.data(), value.size()); - return emberAfWriteAttribute(endpoint, Clusters::ThreadBorderRouterManagement::Id, Id, zclString, - ZCL_CHAR_STRING_ATTRIBUTE_TYPE); -} - -} // namespace BorderRouterName - -namespace BorderAgentID { - -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, chip::MutableByteSpan & value) -{ - uint8_t zclString[254 + 1]; - Protocols::InteractionModel::Status status = - emberAfReadAttribute(endpoint, Clusters::ThreadBorderRouterManagement::Id, Id, zclString, sizeof(zclString)); - VerifyOrReturnError(Protocols::InteractionModel::Status::Success == status, status); - size_t length = emberAfStringLength(zclString); - if (length == NumericAttributeTraits::kNullValue) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - - VerifyOrReturnError(value.size() == 254, Protocols::InteractionModel::Status::InvalidDataType); - memcpy(value.data(), &zclString[1], 254); - value.reduce_size(length); - return status; -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, chip::ByteSpan value, MarkAttributeDirty markDirty) -{ - - static_assert(254 < NumericAttributeTraits::kNullValue, "value.size() might be too big"); - VerifyOrReturnError(value.size() <= 254, Protocols::InteractionModel::Status::ConstraintError); - uint8_t zclString[254 + 1]; - auto length = static_cast(value.size()); - Encoding::Put8(zclString, length); - memcpy(&zclString[1], value.data(), value.size()); - return emberAfWriteAttribute(endpoint, Clusters::ThreadBorderRouterManagement::Id, Id, zclString, - ZCL_OCTET_STRING_ATTRIBUTE_TYPE, markDirty); -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, chip::ByteSpan value) -{ - - static_assert(254 < NumericAttributeTraits::kNullValue, "value.size() might be too big"); - VerifyOrReturnError(value.size() <= 254, Protocols::InteractionModel::Status::ConstraintError); - uint8_t zclString[254 + 1]; - auto length = static_cast(value.size()); - Encoding::Put8(zclString, length); - memcpy(&zclString[1], value.data(), value.size()); - return emberAfWriteAttribute(endpoint, Clusters::ThreadBorderRouterManagement::Id, Id, zclString, - ZCL_OCTET_STRING_ATTRIBUTE_TYPE); -} - -} // namespace BorderAgentID - -namespace ThreadVersion { - -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, uint16_t * value) -{ - using Traits = NumericAttributeTraits; - Traits::StorageType temp; - uint8_t * readable = Traits::ToAttributeStoreRepresentation(temp); - Protocols::InteractionModel::Status status = - emberAfReadAttribute(endpoint, Clusters::ThreadBorderRouterManagement::Id, Id, readable, sizeof(temp)); - VerifyOrReturnError(Protocols::InteractionModel::Status::Success == status, status); - if (!Traits::CanRepresentValue(/* isNullable = */ false, temp)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - *value = Traits::StorageToWorking(temp); - return status; -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint16_t value, MarkAttributeDirty markDirty) -{ - using Traits = NumericAttributeTraits; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::ThreadBorderRouterManagement::Id, Id, writable, ZCL_INT16U_ATTRIBUTE_TYPE, - markDirty); -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint16_t value) -{ - using Traits = NumericAttributeTraits; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::ThreadBorderRouterManagement::Id, Id, writable, ZCL_INT16U_ATTRIBUTE_TYPE); -} - -} // namespace ThreadVersion - -namespace InterfaceEnabled { - -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, bool * value) -{ - using Traits = NumericAttributeTraits; - Traits::StorageType temp; - uint8_t * readable = Traits::ToAttributeStoreRepresentation(temp); - Protocols::InteractionModel::Status status = - emberAfReadAttribute(endpoint, Clusters::ThreadBorderRouterManagement::Id, Id, readable, sizeof(temp)); - VerifyOrReturnError(Protocols::InteractionModel::Status::Success == status, status); - if (!Traits::CanRepresentValue(/* isNullable = */ false, temp)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - *value = Traits::StorageToWorking(temp); - return status; -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, bool value, MarkAttributeDirty markDirty) -{ - using Traits = NumericAttributeTraits; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::ThreadBorderRouterManagement::Id, Id, writable, ZCL_BOOLEAN_ATTRIBUTE_TYPE, - markDirty); -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, bool value) -{ - using Traits = NumericAttributeTraits; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::ThreadBorderRouterManagement::Id, Id, writable, ZCL_BOOLEAN_ATTRIBUTE_TYPE); -} - -} // namespace InterfaceEnabled - -namespace ActiveDatasetTimestamp { - -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, DataModel::Nullable & value) -{ - using Traits = NumericAttributeTraits; - Traits::StorageType temp; - uint8_t * readable = Traits::ToAttributeStoreRepresentation(temp); - Protocols::InteractionModel::Status status = - emberAfReadAttribute(endpoint, Clusters::ThreadBorderRouterManagement::Id, Id, readable, sizeof(temp)); - VerifyOrReturnError(Protocols::InteractionModel::Status::Success == status, status); - if (Traits::IsNullValue(temp)) - { - value.SetNull(); - } - else - { - value.SetNonNull() = Traits::StorageToWorking(temp); - } - return status; -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint64_t value, MarkAttributeDirty markDirty) -{ - using Traits = NumericAttributeTraits; - if (!Traits::CanRepresentValue(/* isNullable = */ true, value)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::ThreadBorderRouterManagement::Id, Id, writable, ZCL_INT64U_ATTRIBUTE_TYPE, - markDirty); -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint64_t value) -{ - using Traits = NumericAttributeTraits; - if (!Traits::CanRepresentValue(/* isNullable = */ true, value)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::ThreadBorderRouterManagement::Id, Id, writable, ZCL_INT64U_ATTRIBUTE_TYPE); -} - -Protocols::InteractionModel::Status SetNull(chip::EndpointId endpoint, MarkAttributeDirty markDirty) -{ - using Traits = NumericAttributeTraits; - Traits::StorageType value; - Traits::SetNull(value); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(value); - return emberAfWriteAttribute(endpoint, Clusters::ThreadBorderRouterManagement::Id, Id, writable, ZCL_INT64U_ATTRIBUTE_TYPE, - markDirty); -} - -Protocols::InteractionModel::Status SetNull(chip::EndpointId endpoint) -{ - using Traits = NumericAttributeTraits; - Traits::StorageType value; - Traits::SetNull(value); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(value); - return emberAfWriteAttribute(endpoint, Clusters::ThreadBorderRouterManagement::Id, Id, writable, ZCL_INT64U_ATTRIBUTE_TYPE); -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, const chip::app::DataModel::Nullable & value, - MarkAttributeDirty markDirty) -{ - if (value.IsNull()) - { - return SetNull(endpoint, markDirty); - } - - return Set(endpoint, value.Value(), markDirty); -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, const chip::app::DataModel::Nullable & value) -{ - if (value.IsNull()) - { - return SetNull(endpoint); - } - - return Set(endpoint, value.Value()); -} - -} // namespace ActiveDatasetTimestamp - -namespace FeatureMap { - -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, uint32_t * value) -{ - using Traits = NumericAttributeTraits; - Traits::StorageType temp; - uint8_t * readable = Traits::ToAttributeStoreRepresentation(temp); - Protocols::InteractionModel::Status status = - emberAfReadAttribute(endpoint, Clusters::ThreadBorderRouterManagement::Id, Id, readable, sizeof(temp)); - VerifyOrReturnError(Protocols::InteractionModel::Status::Success == status, status); - if (!Traits::CanRepresentValue(/* isNullable = */ false, temp)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - *value = Traits::StorageToWorking(temp); - return status; -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint32_t value, MarkAttributeDirty markDirty) -{ - using Traits = NumericAttributeTraits; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::ThreadBorderRouterManagement::Id, Id, writable, ZCL_BITMAP32_ATTRIBUTE_TYPE, - markDirty); -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint32_t value) -{ - using Traits = NumericAttributeTraits; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::ThreadBorderRouterManagement::Id, Id, writable, ZCL_BITMAP32_ATTRIBUTE_TYPE); -} - -} // namespace FeatureMap - namespace ClusterRevision { Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, uint16_t * value) diff --git a/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.h b/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.h index d52d593369f80a..9cc038bbb3ca2d 100644 --- a/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.h +++ b/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.h @@ -5412,47 +5412,6 @@ Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint16_t valu namespace ThreadBorderRouterManagement { namespace Attributes { -namespace BorderRouterName { -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, chip::MutableCharSpan & value); // char_string -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, chip::CharSpan value); -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, chip::CharSpan value, MarkAttributeDirty markDirty); -} // namespace BorderRouterName - -namespace BorderAgentID { -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, chip::MutableByteSpan & value); // octet_string -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, chip::ByteSpan value); -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, chip::ByteSpan value, MarkAttributeDirty markDirty); -} // namespace BorderAgentID - -namespace ThreadVersion { -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, uint16_t * value); // int16u -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint16_t value); -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint16_t value, MarkAttributeDirty markDirty); -} // namespace ThreadVersion - -namespace InterfaceEnabled { -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, bool * value); // boolean -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, bool value); -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, bool value, MarkAttributeDirty markDirty); -} // namespace InterfaceEnabled - -namespace ActiveDatasetTimestamp { -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, DataModel::Nullable & value); // int64u -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint64_t value); -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint64_t value, MarkAttributeDirty markDirty); -Protocols::InteractionModel::Status SetNull(chip::EndpointId endpoint); -Protocols::InteractionModel::Status SetNull(chip::EndpointId endpoint, MarkAttributeDirty markDirty); -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, const chip::app::DataModel::Nullable & value); -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, const chip::app::DataModel::Nullable & value, - MarkAttributeDirty markDirty); -} // namespace ActiveDatasetTimestamp - -namespace FeatureMap { -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, uint32_t * value); // bitmap32 -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint32_t value); -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint32_t value, MarkAttributeDirty markDirty); -} // namespace FeatureMap - namespace ClusterRevision { Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, uint16_t * value); // int16u Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint16_t value); diff --git a/zzz_generated/app-common/app-common/zap-generated/callback.h b/zzz_generated/app-common/app-common/zap-generated/callback.h index 7095622e698e75..1b456edbc45c46 100644 --- a/zzz_generated/app-common/app-common/zap-generated/callback.h +++ b/zzz_generated/app-common/app-common/zap-generated/callback.h @@ -6059,18 +6059,6 @@ bool emberAfValveConfigurationAndControlClusterOpenCallback( bool emberAfValveConfigurationAndControlClusterCloseCallback( chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, const chip::app::Clusters::ValveConfigurationAndControl::Commands::Close::DecodableType & commandData); -/** - * @brief Water Heater Management Cluster Boost Command callback (from client) - */ -bool emberAfWaterHeaterManagementClusterBoostCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::WaterHeaterManagement::Commands::Boost::DecodableType & commandData); -/** - * @brief Water Heater Management Cluster CancelBoost Command callback (from client) - */ -bool emberAfWaterHeaterManagementClusterCancelBoostCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::WaterHeaterManagement::Commands::CancelBoost::DecodableType & commandData); /** * @brief Demand Response Load Control Cluster RegisterLoadControlProgramRequest Command callback (from client) */ @@ -6468,30 +6456,6 @@ bool emberAfColorControlClusterMoveColorTemperatureCallback( bool emberAfColorControlClusterStepColorTemperatureCallback( chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, const chip::app::Clusters::ColorControl::Commands::StepColorTemperature::DecodableType & commandData); -/** - * @brief Thread Border Router Management Cluster GetActiveDatasetRequest Command callback (from client) - */ -bool emberAfThreadBorderRouterManagementClusterGetActiveDatasetRequestCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::ThreadBorderRouterManagement::Commands::GetActiveDatasetRequest::DecodableType & commandData); -/** - * @brief Thread Border Router Management Cluster GetPendingDatasetRequest Command callback (from client) - */ -bool emberAfThreadBorderRouterManagementClusterGetPendingDatasetRequestCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::ThreadBorderRouterManagement::Commands::GetPendingDatasetRequest::DecodableType & commandData); -/** - * @brief Thread Border Router Management Cluster SetActiveDatasetRequest Command callback (from client) - */ -bool emberAfThreadBorderRouterManagementClusterSetActiveDatasetRequestCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::ThreadBorderRouterManagement::Commands::SetActiveDatasetRequest::DecodableType & commandData); -/** - * @brief Thread Border Router Management Cluster SetPendingDatasetRequest Command callback (from client) - */ -bool emberAfThreadBorderRouterManagementClusterSetPendingDatasetRequestCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::ThreadBorderRouterManagement::Commands::SetPendingDatasetRequest::DecodableType & commandData); /** * @brief Channel Cluster ChangeChannel Command callback (from client) */ 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 d28337a26c582b..b0788a47476cff 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 @@ -23789,6 +23789,8 @@ CHIP_ERROR TypeInfo::DecodableType::Decode(TLV::TLVReader & reader, const Concre { case Attributes::Ssid::TypeInfo::GetAttributeId(): return DataModel::Decode(reader, ssid); + case Attributes::PassphraseSurrogate::TypeInfo::GetAttributeId(): + return DataModel::Decode(reader, passphraseSurrogate); case Attributes::GeneratedCommandList::TypeInfo::GetAttributeId(): return DataModel::Decode(reader, generatedCommandList); case Attributes::AcceptedCommandList::TypeInfo::GetAttributeId(): @@ -31781,7 +31783,6 @@ bool CommandNeedsTimedInvoke(ClusterId aCluster, CommandId aCommand) { case Clusters::ThreadNetworkDirectory::Commands::AddNetwork::Id: case Clusters::ThreadNetworkDirectory::Commands::RemoveNetwork::Id: - case Clusters::ThreadNetworkDirectory::Commands::GetOperationalDataset::Id: return true; default: return false; 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 05abe388afa297..6ba472e34fd598 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 @@ -35905,6 +35905,18 @@ struct TypeInfo static constexpr size_t MaxLength() { return 32; } }; } // namespace Ssid +namespace PassphraseSurrogate { +struct TypeInfo +{ + using Type = chip::app::DataModel::Nullable; + using DecodableType = chip::app::DataModel::Nullable; + using DecodableArgType = const chip::app::DataModel::Nullable &; + + static constexpr ClusterId GetClusterId() { return Clusters::WiFiNetworkManagement::Id; } + static constexpr AttributeId GetAttributeId() { return Attributes::PassphraseSurrogate::Id; } + static constexpr bool MustUseTimedWrite() { return false; } +}; +} // namespace PassphraseSurrogate namespace GeneratedCommandList { struct TypeInfo : public Clusters::Globals::Attributes::GeneratedCommandList::TypeInfo { @@ -35951,6 +35963,7 @@ struct TypeInfo CHIP_ERROR Decode(TLV::TLVReader & reader, const ConcreteAttributePath & path); Attributes::Ssid::TypeInfo::DecodableType ssid; + Attributes::PassphraseSurrogate::TypeInfo::DecodableType passphraseSurrogate; Attributes::GeneratedCommandList::TypeInfo::DecodableType generatedCommandList; Attributes::AcceptedCommandList::TypeInfo::DecodableType acceptedCommandList; Attributes::EventList::TypeInfo::DecodableType eventList; @@ -36415,7 +36428,7 @@ struct Type using ResponseType = Clusters::ThreadNetworkDirectory::Commands::OperationalDatasetResponse::DecodableType; - static constexpr bool MustUseTimedInvoke() { return true; } + static constexpr bool MustUseTimedInvoke() { return false; } }; struct DecodableType diff --git a/zzz_generated/app-common/app-common/zap-generated/ids/Attributes.h b/zzz_generated/app-common/app-common/zap-generated/ids/Attributes.h index 30702c3e8c731b..01bee8c1ac982a 100644 --- a/zzz_generated/app-common/app-common/zap-generated/ids/Attributes.h +++ b/zzz_generated/app-common/app-common/zap-generated/ids/Attributes.h @@ -6741,9 +6741,13 @@ namespace WiFiNetworkManagement { namespace Attributes { namespace Ssid { -static constexpr AttributeId Id = 0x00000001; +static constexpr AttributeId Id = 0x00000000; } // namespace Ssid +namespace PassphraseSurrogate { +static constexpr AttributeId Id = 0x00000001; +} // namespace PassphraseSurrogate + namespace GeneratedCommandList { static constexpr AttributeId Id = Globals::Attributes::GeneratedCommandList::Id; } // namespace GeneratedCommandList diff --git a/zzz_generated/chip-tool/zap-generated/cluster/Commands.h b/zzz_generated/chip-tool/zap-generated/cluster/Commands.h index 4406fd4ea6f5bc..d411fb93555fb7 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/Commands.h +++ b/zzz_generated/chip-tool/zap-generated/cluster/Commands.h @@ -11182,7 +11182,8 @@ class ColorControlStepColorTemperature : public ClusterCommand | * NetworkPassphraseRequest | 0x00 | |------------------------------------------------------------------------------| | Attributes: | | -| * Ssid | 0x0001 | +| * Ssid | 0x0000 | +| * PassphraseSurrogate | 0x0001 | | * GeneratedCommandList | 0xFFF8 | | * AcceptedCommandList | 0xFFF9 | | * EventList | 0xFFFA | @@ -25440,6 +25441,7 @@ void registerClusterWiFiNetworkManagement(Commands & commands, CredentialIssuerC // make_unique(Id, credsIssuerConfig), // make_unique(Id, "ssid", Attributes::Ssid::Id, credsIssuerConfig), // + make_unique(Id, "passphrase-surrogate", Attributes::PassphraseSurrogate::Id, credsIssuerConfig), // make_unique(Id, "generated-command-list", Attributes::GeneratedCommandList::Id, credsIssuerConfig), // make_unique(Id, "accepted-command-list", Attributes::AcceptedCommandList::Id, credsIssuerConfig), // make_unique(Id, "event-list", Attributes::EventList::Id, credsIssuerConfig), // @@ -25449,6 +25451,9 @@ void registerClusterWiFiNetworkManagement(Commands & commands, CredentialIssuerC make_unique>(Id, credsIssuerConfig), // make_unique>>( Id, "ssid", Attributes::Ssid::Id, WriteCommandType::kForceWrite, credsIssuerConfig), // + make_unique>>(Id, "passphrase-surrogate", 0, UINT64_MAX, + Attributes::PassphraseSurrogate::Id, + WriteCommandType::kForceWrite, credsIssuerConfig), // make_unique>>( Id, "generated-command-list", Attributes::GeneratedCommandList::Id, WriteCommandType::kForceWrite, credsIssuerConfig), // @@ -25464,6 +25469,7 @@ void registerClusterWiFiNetworkManagement(Commands & commands, CredentialIssuerC WriteCommandType::kForceWrite, credsIssuerConfig), // make_unique(Id, credsIssuerConfig), // make_unique(Id, "ssid", Attributes::Ssid::Id, credsIssuerConfig), // + make_unique(Id, "passphrase-surrogate", Attributes::PassphraseSurrogate::Id, credsIssuerConfig), // make_unique(Id, "generated-command-list", Attributes::GeneratedCommandList::Id, credsIssuerConfig), // make_unique(Id, "accepted-command-list", Attributes::AcceptedCommandList::Id, credsIssuerConfig), // make_unique(Id, "event-list", Attributes::EventList::Id, credsIssuerConfig), // 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 8ea51c97fa2289..cdcbcacd66db22 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp +++ b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp @@ -16932,6 +16932,11 @@ CHIP_ERROR DataModelLogger::LogAttribute(const chip::app::ConcreteDataAttributeP ReturnErrorOnFailure(chip::app::DataModel::Decode(*data, value)); return DataModelLogger::LogValue("SSID", 1, value); } + case WiFiNetworkManagement::Attributes::PassphraseSurrogate::Id: { + chip::app::DataModel::Nullable value; + ReturnErrorOnFailure(chip::app::DataModel::Decode(*data, value)); + return DataModelLogger::LogValue("PassphraseSurrogate", 1, value); + } case WiFiNetworkManagement::Attributes::GeneratedCommandList::Id: { chip::app::DataModel::DecodableList value; ReturnErrorOnFailure(chip::app::DataModel::Decode(*data, 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 1814916095d85d..9f240b61a5c1f5 100644 --- a/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h +++ b/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h @@ -146408,7 +146408,8 @@ class SubscribeAttributeRadonConcentrationMeasurementClusterRevision : public Su | * NetworkPassphraseRequest | 0x00 | |------------------------------------------------------------------------------| | Attributes: | | -| * Ssid | 0x0001 | +| * Ssid | 0x0000 | +| * PassphraseSurrogate | 0x0001 | | * GeneratedCommandList | 0xFFF8 | | * AcceptedCommandList | 0xFFF9 | | * EventList | 0xFFFA | @@ -146559,6 +146560,91 @@ class SubscribeAttributeWiFiNetworkManagementSsid : public SubscribeAttribute { #endif // MTR_ENABLE_PROVISIONAL #if MTR_ENABLE_PROVISIONAL +/* + * Attribute PassphraseSurrogate + */ +class ReadWiFiNetworkManagementPassphraseSurrogate : public ReadAttribute { +public: + ReadWiFiNetworkManagementPassphraseSurrogate() + : ReadAttribute("passphrase-surrogate") + { + } + + ~ReadWiFiNetworkManagementPassphraseSurrogate() + { + } + + CHIP_ERROR SendCommand(MTRBaseDevice * device, chip::EndpointId endpointId) override + { + constexpr chip::ClusterId clusterId = chip::app::Clusters::WiFiNetworkManagement::Id; + constexpr chip::AttributeId attributeId = chip::app::Clusters::WiFiNetworkManagement::Attributes::PassphraseSurrogate::Id; + + ChipLogProgress(chipTool, "Sending cluster (0x%08" PRIX32 ") ReadAttribute (0x%08" PRIX32 ") on endpoint %u", endpointId, clusterId, attributeId); + + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL); + __auto_type * cluster = [[MTRBaseClusterWiFiNetworkManagement alloc] initWithDevice:device endpointID:@(endpointId) queue:callbackQueue]; + [cluster readAttributePassphraseSurrogateWithCompletion:^(NSNumber * _Nullable value, NSError * _Nullable error) { + NSLog(@"WiFiNetworkManagement.PassphraseSurrogate response %@", [value description]); + if (error == nil) { + RemoteDataModelLogger::LogAttributeAsJSON(@(endpointId), @(clusterId), @(attributeId), value); + } else { + LogNSError("WiFiNetworkManagement PassphraseSurrogate read Error", error); + RemoteDataModelLogger::LogAttributeErrorAsJSON(@(endpointId), @(clusterId), @(attributeId), error); + } + SetCommandExitStatus(error); + }]; + return CHIP_NO_ERROR; + } +}; + +class SubscribeAttributeWiFiNetworkManagementPassphraseSurrogate : public SubscribeAttribute { +public: + SubscribeAttributeWiFiNetworkManagementPassphraseSurrogate() + : SubscribeAttribute("passphrase-surrogate") + { + } + + ~SubscribeAttributeWiFiNetworkManagementPassphraseSurrogate() + { + } + + CHIP_ERROR SendCommand(MTRBaseDevice * device, chip::EndpointId endpointId) override + { + constexpr chip::ClusterId clusterId = chip::app::Clusters::WiFiNetworkManagement::Id; + constexpr chip::CommandId attributeId = chip::app::Clusters::WiFiNetworkManagement::Attributes::PassphraseSurrogate::Id; + + ChipLogProgress(chipTool, "Sending cluster (0x%08" PRIX32 ") ReportAttribute (0x%08" PRIX32 ") on endpoint %u", clusterId, attributeId, endpointId); + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL); + __auto_type * cluster = [[MTRBaseClusterWiFiNetworkManagement alloc] initWithDevice:device endpointID:@(endpointId) queue:callbackQueue]; + __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:@(mMinInterval) maxInterval:@(mMaxInterval)]; + if (mKeepSubscriptions.HasValue()) { + params.replaceExistingSubscriptions = !mKeepSubscriptions.Value(); + } + if (mFabricFiltered.HasValue()) { + params.filterByFabric = mFabricFiltered.Value(); + } + if (mAutoResubscribe.HasValue()) { + params.resubscribeAutomatically = mAutoResubscribe.Value(); + } + [cluster subscribeAttributePassphraseSurrogateWithParams:params + subscriptionEstablished:^() { mSubscriptionEstablished = YES; } + reportHandler:^(NSNumber * _Nullable value, NSError * _Nullable error) { + NSLog(@"WiFiNetworkManagement.PassphraseSurrogate response %@", [value description]); + if (error == nil) { + RemoteDataModelLogger::LogAttributeAsJSON(@(endpointId), @(clusterId), @(attributeId), value); + } else { + RemoteDataModelLogger::LogAttributeErrorAsJSON(@(endpointId), @(clusterId), @(attributeId), error); + } + SetCommandExitStatus(error); + }]; + + return CHIP_NO_ERROR; + } +}; + +#endif // MTR_ENABLE_PROVISIONAL +#if MTR_ENABLE_PROVISIONAL + /* * Attribute GeneratedCommandList */ @@ -195948,6 +196034,10 @@ void registerClusterWiFiNetworkManagement(Commands & commands) make_unique(), // make_unique(), // #endif // MTR_ENABLE_PROVISIONAL +#if MTR_ENABLE_PROVISIONAL + make_unique(), // + make_unique(), // +#endif // MTR_ENABLE_PROVISIONAL #if MTR_ENABLE_PROVISIONAL make_unique(), // make_unique(), //