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 a19cb4716eb880..e8ce132cf396d4 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 @@ -9329,6 +9329,14 @@ "incoming": 1, "outgoing": 0 }, + { + "name": "AddOrUpdateThreadNetwork", + "code": 3, + "mfgCode": null, + "source": "client", + "incoming": 1, + "outgoing": 0 + }, { "name": "RemoveNetwork", "code": 4, @@ -9378,7 +9386,7 @@ "mfgCode": null, "define": "NETWORK_COMMISSIONING_CLUSTER", "side": "server", - "enabled": 0, + "enabled": 1, "commands": [ { "name": "ScanNetworksResponse", @@ -9406,6 +9414,141 @@ } ], "attributes": [ + { + "name": "MaxNetworks", + "code": 0, + "mfgCode": null, + "side": "server", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 0, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "Networks", + "code": 1, + "mfgCode": null, + "side": "server", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 0, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ScanMaxTimeSeconds", + "code": 2, + "mfgCode": null, + "side": "server", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 0, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ConnectMaxTimeSeconds", + "code": 3, + "mfgCode": null, + "side": "server", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 0, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "InterfaceEnabled", + "code": 4, + "mfgCode": null, + "side": "server", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 0, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "LastNetworkingStatus", + "code": 5, + "mfgCode": null, + "side": "server", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 0, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "LastNetworkID", + "code": 6, + "mfgCode": null, + "side": "server", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 0, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "LastConnectErrorValue", + "code": 7, + "mfgCode": null, + "side": "server", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 0, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 0, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, { "name": "ClusterRevision", "code": 65533, @@ -19785,4 +19928,4 @@ } ], "log": [] -} \ No newline at end of file +} diff --git a/examples/all-clusters-app/esp32/main/CMakeLists.txt b/examples/all-clusters-app/esp32/main/CMakeLists.txt index f9cd39ea9a23ed..78a223a25b1090 100644 --- a/examples/all-clusters-app/esp32/main/CMakeLists.txt +++ b/examples/all-clusters-app/esp32/main/CMakeLists.txt @@ -48,7 +48,7 @@ set(SRC_DIRS_LIST "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/barrier-control-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/window-covering-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/general-commissioning-server" - "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/network-commissioning" + "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/network-commissioning-old" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/groups-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/color-control-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/content-launch-server" diff --git a/examples/all-clusters-app/linux/main.cpp b/examples/all-clusters-app/linux/main.cpp index 3c8d5faa5a8e39..bfb174c2a811c2 100644 --- a/examples/all-clusters-app/linux/main.cpp +++ b/examples/all-clusters-app/linux/main.cpp @@ -18,27 +18,33 @@ #include #include +#include #include +#include #include "AppMain.h" +using namespace chip; +using namespace chip::app; +using namespace chip::DeviceLayer; + bool emberAfBasicClusterMfgSpecificPingCallback(chip::app::CommandHandler * commandObj) { emberAfSendDefaultResponse(emberAfCurrentCommand(), EMBER_ZCL_STATUS_SUCCESS); return true; } -void OnIdentifyStart(Identify *) +void OnIdentifyStart(::Identify *) { ChipLogProgress(Zcl, "OnIdentifyStart"); } -void OnIdentifyStop(Identify *) +void OnIdentifyStop(::Identify *) { ChipLogProgress(Zcl, "OnIdentifyStop"); } -void OnTriggerEffect(Identify * identify) +void OnTriggerEffect(::Identify * identify) { switch (identify->mCurrentEffectIdentifier) { @@ -68,6 +74,34 @@ static Identify gIdentify1 = { chip::EndpointId{ 1 }, OnIdentifyStart, OnIdentifyStop, EMBER_ZCL_IDENTIFY_IDENTIFY_TYPE_VISIBLE_LED, OnTriggerEffect, }; +// Network commissioning +namespace { +// This file is being used by platforms other than Linux, so we need this check to disable related features since we only +// implemented them on linux. +#if CHIP_DEVICE_LAYER_TARGET_LINUX +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD +NetworkCommissioning::LinuxThreadDriver sLinuxThreadDriver; +Clusters::NetworkCommissioning::Instance sThreadNetworkCommissioningInstance(0 /* Endpoint Id */, &sLinuxThreadDriver); +#endif +#if CHIP_DEVICE_CONFIG_ENABLE_WPA +NetworkCommissioning::LinuxWiFiDriver sLinuxWiFiDriver; +Clusters::NetworkCommissioning::Instance sWiFiNetworkCommissioningInstance(1 /* Endpoint Id */, &sLinuxWiFiDriver); +#endif +#endif +} // namespace + +void ApplicationInit() +{ +#if CHIP_DEVICE_LAYER_TARGET_LINUX && defined(ZCL_USING_LEVEL_CONTROL_CLUSTER_SERVER) +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD + sThreadNetworkCommissioningInstance.Init(); +#endif +#if CHIP_DEVICE_CONFIG_ENABLE_WPA + sWiFiNetworkCommissioningInstance.Init(); +#endif +#endif +} + int main(int argc, char * argv[]) { VerifyOrDie(ChipLinuxAppInit(argc, argv) == 0); diff --git a/examples/all-clusters-app/mbed/CMakeLists.txt b/examples/all-clusters-app/mbed/CMakeLists.txt index e2f809e5d30eac..fe62bb9e9a441c 100644 --- a/examples/all-clusters-app/mbed/CMakeLists.txt +++ b/examples/all-clusters-app/mbed/CMakeLists.txt @@ -114,8 +114,8 @@ target_sources(${APP_TARGET} PRIVATE ${APP_CLUSTERS}/media-input-server/media-input-server.cpp ${APP_CLUSTERS}/media-playback-server/media-playback-server.cpp ${APP_CLUSTERS}/mode-select-server/mode-select-server.cpp - ${APP_CLUSTERS}/network-commissioning/network-commissioning-ember.cpp - ${APP_CLUSTERS}/network-commissioning/network-commissioning.cpp + ${APP_CLUSTERS}/network-commissioning-old/network-commissioning-ember.cpp + ${APP_CLUSTERS}/network-commissioning-old/network-commissioning-old.cpp ${APP_CLUSTERS}/on-off-server/on-off-server.cpp ${APP_CLUSTERS}/ota-provider/ota-provider.cpp ${APP_CLUSTERS}/scenes/scenes.cpp diff --git a/examples/bridge-app/esp32/main/CMakeLists.txt b/examples/bridge-app/esp32/main/CMakeLists.txt index 4031f15de317f9..ee2ca2f5cab616 100644 --- a/examples/bridge-app/esp32/main/CMakeLists.txt +++ b/examples/bridge-app/esp32/main/CMakeLists.txt @@ -39,7 +39,7 @@ idf_component_register(PRIV_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/general-diagnostics-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/group-key-mgmt-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/descriptor" - "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/network-commissioning" + "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/network-commissioning-old" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/on-off-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/operational-credentials-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/general-commissioning-server" diff --git a/examples/bridge-app/linux/main.cpp b/examples/bridge-app/linux/main.cpp index 63c575acbcdf30..f6d7814d38f250 100644 --- a/examples/bridge-app/linux/main.cpp +++ b/examples/bridge-app/linux/main.cpp @@ -566,6 +566,8 @@ CHIP_ERROR PrintQRCodeContent() } } // namespace +void ApplicationInit() {} + int main(int argc, char * argv[]) { CHIP_ERROR err = CHIP_NO_ERROR; diff --git a/examples/lighting-app/linux/main.cpp b/examples/lighting-app/linux/main.cpp index 5cfb79ffd2298c..06e05b7cbdd1c3 100644 --- a/examples/lighting-app/linux/main.cpp +++ b/examples/lighting-app/linux/main.cpp @@ -60,6 +60,8 @@ void emberAfOnOffClusterInitCallback(EndpointId endpoint) // TODO: implement any additional Cluster Server init actions } +void ApplicationInit() {} + int main(int argc, char * argv[]) { if (ChipLinuxAppInit(argc, argv) != 0) diff --git a/examples/lighting-app/mbed/CMakeLists.txt b/examples/lighting-app/mbed/CMakeLists.txt index 811d5990b95fd3..e79b8e443772e9 100644 --- a/examples/lighting-app/mbed/CMakeLists.txt +++ b/examples/lighting-app/mbed/CMakeLists.txt @@ -94,8 +94,8 @@ target_sources(${APP_TARGET} PRIVATE ${CHIP_ROOT}/src/app/clusters/level-control/level-control.cpp ${CHIP_ROOT}/src/app/clusters/color-control-server/color-control-server.cpp ${CHIP_ROOT}/src/app/clusters/occupancy-sensor-server/occupancy-sensor-server.cpp - ${CHIP_ROOT}/src/app/clusters/network-commissioning/network-commissioning-ember.cpp - ${CHIP_ROOT}/src/app/clusters/network-commissioning/network-commissioning.cpp + ${CHIP_ROOT}/src/app/clusters/network-commissioning-old/network-commissioning-ember.cpp + ${CHIP_ROOT}/src/app/clusters/network-commissioning-old/network-commissioning-old.cpp ) target_link_libraries(${APP_TARGET} mbed-os-posix-socket mbed-os mbed-ble mbed-events mbed-netsocket mbed-storage mbed-storage-kv-global-api mbed-mbedtls mbed-emac chip) diff --git a/examples/lighting-app/telink/CMakeLists.txt b/examples/lighting-app/telink/CMakeLists.txt index 0ae36a67418eb3..2f367d1c761593 100644 --- a/examples/lighting-app/telink/CMakeLists.txt +++ b/examples/lighting-app/telink/CMakeLists.txt @@ -94,6 +94,6 @@ target_sources(app PRIVATE ${CHIP_ROOT}/src/app/clusters/level-control/level-control.cpp ${CHIP_ROOT}/src/app/clusters/color-control-server/color-control-server.cpp ${CHIP_ROOT}/src/app/clusters/occupancy-sensor-server/occupancy-sensor-server.cpp - ${CHIP_ROOT}/src/app/clusters/network-commissioning/network-commissioning-ember.cpp - ${CHIP_ROOT}/src/app/clusters/network-commissioning/network-commissioning.cpp + ${CHIP_ROOT}/src/app/clusters/network-commissioning-old/network-commissioning-ember.cpp + ${CHIP_ROOT}/src/app/clusters/network-commissioning-old/network-commissioning-old.cpp ) diff --git a/examples/lock-app/esp32/main/CMakeLists.txt b/examples/lock-app/esp32/main/CMakeLists.txt index 59972099fdd693..0659b890b8668a 100644 --- a/examples/lock-app/esp32/main/CMakeLists.txt +++ b/examples/lock-app/esp32/main/CMakeLists.txt @@ -44,7 +44,7 @@ idf_component_register(INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/bindings" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/descriptor" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/fixed-label-server" - "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/network-commissioning" + "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/network-commissioning-old" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/on-off-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/operational-credentials-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/general-commissioning-server" @@ -146,6 +146,7 @@ idf_component_register(PRIV_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/general-diagnostics-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/group-key-mgmt-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/network-commissioning" + "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/network-commissioning-old" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/on-off-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/operational-credentials-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/general-commissioning-server" diff --git a/examples/lock-app/mbed/CMakeLists.txt b/examples/lock-app/mbed/CMakeLists.txt index ddc6fb8b144a7e..39619ca596a658 100644 --- a/examples/lock-app/mbed/CMakeLists.txt +++ b/examples/lock-app/mbed/CMakeLists.txt @@ -86,8 +86,8 @@ target_sources(${APP_TARGET} PRIVATE ${CHIP_ROOT}/src/app/clusters/general-diagnostics-server/general-diagnostics-server.cpp ${CHIP_ROOT}/src/app/clusters/group-key-mgmt-server/group-key-mgmt-server.cpp ${CHIP_ROOT}/src/app/clusters/general-commissioning-server/general-commissioning-server.cpp - ${CHIP_ROOT}/src/app/clusters/network-commissioning/network-commissioning-ember.cpp - ${CHIP_ROOT}/src/app/clusters/network-commissioning/network-commissioning.cpp + ${CHIP_ROOT}/src/app/clusters/network-commissioning-old/network-commissioning-ember.cpp + ${CHIP_ROOT}/src/app/clusters/network-commissioning-old/network-commissioning-old.cpp ${CHIP_ROOT}/src/app/clusters/operational-credentials-server/operational-credentials-server.cpp ${CHIP_ROOT}/src/app/clusters/on-off-server/on-off-server.cpp ${CHIP_ROOT}/src/app/clusters/power-source-configuration-server/power-source-configuration-server.cpp diff --git a/examples/ota-provider-app/esp32/main/CMakeLists.txt b/examples/ota-provider-app/esp32/main/CMakeLists.txt index 10219078b5ff9b..ee8077e9c8ff7a 100644 --- a/examples/ota-provider-app/esp32/main/CMakeLists.txt +++ b/examples/ota-provider-app/esp32/main/CMakeLists.txt @@ -43,7 +43,7 @@ idf_component_register(PRIV_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/general-diagnostics-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/group-key-mgmt-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/general-commissioning-server" - "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/network-commissioning" + "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/network-commissioning-old" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/operational-credentials-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/ota-provider" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/ota-provider-app/ota-provider-common" diff --git a/examples/ota-requestor-app/esp32/main/CMakeLists.txt b/examples/ota-requestor-app/esp32/main/CMakeLists.txt index a3d8d6876e3b1b..4b65e80c847aba 100644 --- a/examples/ota-requestor-app/esp32/main/CMakeLists.txt +++ b/examples/ota-requestor-app/esp32/main/CMakeLists.txt @@ -43,7 +43,7 @@ idf_component_register(PRIV_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/general-diagnostics-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/group-key-mgmt-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/general-commissioning-server" - "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/network-commissioning" + "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/network-commissioning-old" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/operational-credentials-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/ota-requestor" PRIV_REQUIRES chip QRCode bt console app_update) diff --git a/examples/placeholder/linux/main.cpp b/examples/placeholder/linux/main.cpp index 78744a6849ec67..74f4fb5ac3de38 100644 --- a/examples/placeholder/linux/main.cpp +++ b/examples/placeholder/linux/main.cpp @@ -19,6 +19,8 @@ #include "AppMain.h" #include "MatterCallbacks.h" +void ApplicationInit() {} + int main(int argc, char * argv[]) { VerifyOrDie(ChipLinuxAppInit(argc, argv) == 0); diff --git a/examples/platform/linux/AppMain.cpp b/examples/platform/linux/AppMain.cpp index e6fbd495fb2ee9..302f4a1df1f30e 100644 --- a/examples/platform/linux/AppMain.cpp +++ b/examples/platform/linux/AppMain.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -54,6 +55,7 @@ #include #endif +#include "AppMain.h" #include "Options.h" using namespace chip; @@ -427,6 +429,8 @@ void ChipLinuxAppMainLoop() #endif // defined(ENABLE_CHIP_SHELL) #endif // CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE + ApplicationInit(); + chip::DeviceLayer::PlatformMgr().RunEventLoop(); #if CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE diff --git a/examples/platform/linux/AppMain.h b/examples/platform/linux/AppMain.h index 520cd3af514147..914499e4f851eb 100644 --- a/examples/platform/linux/AppMain.h +++ b/examples/platform/linux/AppMain.h @@ -41,3 +41,6 @@ CHIP_ERROR CommissionerPairUDC(uint32_t pincode, size_t index); DeviceCommissioner * GetDeviceCommissioner(); #endif // CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE + +// For extra init calls, the function will be called right before running Matter main loop. +void ApplicationInit(); diff --git a/examples/temperature-measurement-app/esp32/main/CMakeLists.txt b/examples/temperature-measurement-app/esp32/main/CMakeLists.txt index 2c396c4be9f2ec..47439a37fe78a9 100644 --- a/examples/temperature-measurement-app/esp32/main/CMakeLists.txt +++ b/examples/temperature-measurement-app/esp32/main/CMakeLists.txt @@ -43,7 +43,7 @@ idf_component_register(PRIV_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/general-diagnostics-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/group-key-mgmt-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/general-commissioning-server" - "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/network-commissioning" + "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/network-commissioning-old" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/operational-credentials-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/platform/esp32/route_hook" PRIV_REQUIRES chip QRCode bt) diff --git a/examples/thermostat/linux/main.cpp b/examples/thermostat/linux/main.cpp index 8ad5cb5033d1af..68598a48ad8c47 100644 --- a/examples/thermostat/linux/main.cpp +++ b/examples/thermostat/linux/main.cpp @@ -87,6 +87,8 @@ static Identify gIdentify1 = { chip::EndpointId{ 1 }, OnIdentifyStart, OnIdentifyStop, EMBER_ZCL_IDENTIFY_IDENTIFY_TYPE_VISIBLE_LED, OnTriggerEffect, }; +void ApplicationInit() {} + int main(int argc, char * argv[]) { VerifyOrDie(ChipLinuxAppInit(argc, argv) == 0); diff --git a/examples/tv-app/linux/main.cpp b/examples/tv-app/linux/main.cpp index 0ef78d0c907cbd..41e61a257a3cd3 100644 --- a/examples/tv-app/linux/main.cpp +++ b/examples/tv-app/linux/main.cpp @@ -57,6 +57,8 @@ static ContentLauncherManager contentLauncherManager; constexpr chip::EndpointId kContentLauncherEndpoint = 1; } // namespace +void ApplicationInit() {} + int main(int argc, char * argv[]) { CHIP_ERROR err = CHIP_NO_ERROR; diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni index b8552565a575a3..a19f120daa9660 100644 --- a/src/app/chip_data_model.gni +++ b/src/app/chip_data_model.gni @@ -88,7 +88,7 @@ template("chip_data_model") { "${_app_root}/clusters/level-control/level-control.h", "${_app_root}/clusters/messaging-client/messaging-client.h", "${_app_root}/clusters/messaging-server/messaging-server.h", - "${_app_root}/clusters/network-commissioning/network-commissioning.h", + "${_app_root}/clusters/network-commissioning-old/network-commissioning.h", "${_app_root}/clusters/on-off-server/on-off-server.h", "${_app_root}/clusters/scenes/scenes-tokens.h", "${_app_root}/clusters/scenes/scenes.h", @@ -132,10 +132,10 @@ template("chip_data_model") { foreach(cluster, _cluster_sources) { if (cluster == "door-lock-server") { sources += [ "${_app_root}/clusters/${cluster}/door-lock-server.cpp" ] - } else if (cluster == "network-commissioning") { + } else if (cluster == "network-commissioning-old") { sources += [ - "${_app_root}/clusters/${cluster}/${cluster}-ember.cpp", - "${_app_root}/clusters/${cluster}/${cluster}.cpp", + "${_app_root}/clusters/${cluster}/network-commissioning-ember.cpp", + "${_app_root}/clusters/${cluster}/network-commissioning-old.cpp", ] } else if (cluster == "mode-select-server") { sources += [ diff --git a/src/app/clusters/network-commissioning/network-commissioning-ember.cpp b/src/app/clusters/network-commissioning-old/network-commissioning-ember.cpp similarity index 100% rename from src/app/clusters/network-commissioning/network-commissioning-ember.cpp rename to src/app/clusters/network-commissioning-old/network-commissioning-ember.cpp diff --git a/src/app/clusters/network-commissioning-old/network-commissioning-old.cpp b/src/app/clusters/network-commissioning-old/network-commissioning-old.cpp new file mode 100644 index 00000000000000..556ad93e61a648 --- /dev/null +++ b/src/app/clusters/network-commissioning-old/network-commissioning-old.cpp @@ -0,0 +1,295 @@ + + +/* + * + * Copyright (c) 2021 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "network-commissioning.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD +#include +#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD + +// Include DeviceNetworkProvisioningDelegateImpl for WiFi provisioning. +// TODO: Enable wifi network should be done by ConnectivityManager. (Or other platform neutral interfaces) +#if defined(CHIP_DEVICE_LAYER_TARGET) +#define DEVICENETWORKPROVISIONING_HEADER +#include DEVICENETWORKPROVISIONING_HEADER +#endif + +// TODO: Configuration should move to build-time configuration +#ifndef CHIP_CLUSTER_NETWORK_COMMISSIONING_MAX_NETWORKS +#define CHIP_CLUSTER_NETWORK_COMMISSIONING_MAX_NETWORKS 4 +#endif // CHIP_CLUSTER_NETWORK_COMMISSIONING_MAX_NETWORKS + +using namespace chip; +using namespace chip::app; + +namespace chip { +namespace app { +namespace Clusters { +namespace NetworkCommissioning { + +constexpr uint8_t kMaxNetworkIDLen = 32; +constexpr uint8_t kMaxThreadDatasetLen = 254; // As defined in Thread spec. +constexpr uint8_t kMaxWiFiSSIDLen = 32; +constexpr uint8_t kMaxWiFiCredentialsLen = 64; +constexpr uint8_t kMaxNetworks = CHIP_CLUSTER_NETWORK_COMMISSIONING_MAX_NETWORKS; + +enum class NetworkType : uint8_t +{ + kUndefined = 0, + kWiFi = 1, + kThread = 2, + kEthernet = 3, +}; + +struct ThreadNetworkInfo +{ + uint8_t mDataset[kMaxThreadDatasetLen]; + uint8_t mDatasetLen; +}; + +struct WiFiNetworkInfo +{ + uint8_t mSSID[kMaxWiFiSSIDLen + 1]; + uint8_t mSSIDLen; + uint8_t mCredentials[kMaxWiFiCredentialsLen]; + uint8_t mCredentialsLen; +}; + +struct NetworkInfo +{ + uint8_t mNetworkID[kMaxNetworkIDLen]; + uint8_t mNetworkIDLen; + uint8_t mEnabled; + NetworkType mNetworkType; + union NetworkData + { +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD + Thread::OperationalDataset mThread; +#endif +#if defined(CHIP_DEVICE_LAYER_TARGET) + WiFiNetworkInfo mWiFi; +#endif + } mData; +}; + +namespace { +// The internal network info containing credentials. Need to find some better place to save these info. +NetworkInfo sNetworks[kMaxNetworks]; +} // namespace + +void OnAddOrUpdateThreadNetworkCommandCallbackInternal(app::CommandHandler * apCommandHandler, + const app::ConcreteCommandPath & commandPath, ByteSpan operationalDataset, + uint64_t breadcrumb, uint32_t timeoutMs) +{ + Commands::NetworkConfigResponse::Type response; +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD + NetworkCommissioningStatus err = NetworkCommissioningStatus::kBoundsExceeded; + + for (size_t i = 0; i < kMaxNetworks; i++) + { + if (sNetworks[i].mNetworkType == NetworkType::kUndefined) + { + Thread::OperationalDataset & dataset = sNetworks[i].mData.mThread; + CHIP_ERROR error = dataset.Init(operationalDataset); + + if (error != CHIP_NO_ERROR) + { + ChipLogDetail(Zcl, "Failed to parse Thread operational dataset: %s", ErrorStr(error)); + err = NetworkCommissioningStatus::kUnknownError; + break; + } + + uint8_t extendedPanId[Thread::kSizeExtendedPanId]; + + static_assert(sizeof(sNetworks[i].mNetworkID) >= sizeof(extendedPanId), + "Network ID must be larger than Thread extended PAN ID!"); + SuccessOrExit(dataset.GetExtendedPanId(extendedPanId)); + memcpy(sNetworks[i].mNetworkID, extendedPanId, sizeof(extendedPanId)); + sNetworks[i].mNetworkIDLen = sizeof(extendedPanId); + + sNetworks[i].mNetworkType = NetworkType::kThread; + sNetworks[i].mEnabled = false; + + err = NetworkCommissioningStatus::kSuccess; + break; + } + } + +exit: + // TODO: We should encode response command here. + + ChipLogDetail(Zcl, "AddOrUpdateThreadNetwork: %" PRIu8, to_underlying(err)); + response.networkingStatus = err; +#else + // The target does not supports ThreadNetwork. We should not add AddOrUpdateThreadNetwork command in that case then the upper + // layer will return "Command not found" error. + response.networkingStatus = NetworkCommissioningStatus::kUnknownError; +#endif + apCommandHandler->AddResponseData(commandPath, response); +} + +void OnAddOrUpdateWiFiNetworkCommandCallbackInternal(app::CommandHandler * apCommandHandler, + const app::ConcreteCommandPath & commandPath, ByteSpan ssid, + ByteSpan credentials, uint64_t breadcrumb, uint32_t timeoutMs) +{ + Commands::NetworkConfigResponse::Type response; +#if defined(CHIP_DEVICE_LAYER_TARGET) + NetworkCommissioningStatus err = NetworkCommissioningStatus::kBoundsExceeded; + + for (size_t i = 0; i < kMaxNetworks; i++) + { + if (sNetworks[i].mNetworkType == NetworkType::kUndefined) + { + VerifyOrExit(ssid.size() <= sizeof(sNetworks[i].mData.mWiFi.mSSID), err = NetworkCommissioningStatus::kOutOfRange); + memcpy(sNetworks[i].mData.mWiFi.mSSID, ssid.data(), ssid.size()); + + using WiFiSSIDLenType = decltype(sNetworks[i].mData.mWiFi.mSSIDLen); + VerifyOrExit(CanCastTo(ssid.size()), err = NetworkCommissioningStatus::kOutOfRange); + sNetworks[i].mData.mWiFi.mSSIDLen = static_cast(ssid.size()); + + VerifyOrExit(credentials.size() <= sizeof(sNetworks[i].mData.mWiFi.mCredentials), + err = NetworkCommissioningStatus::kOutOfRange); + memcpy(sNetworks[i].mData.mWiFi.mCredentials, credentials.data(), credentials.size()); + + using WiFiCredentialsLenType = decltype(sNetworks[i].mData.mWiFi.mCredentialsLen); + VerifyOrExit(CanCastTo(ssid.size()), err = NetworkCommissioningStatus::kOutOfRange); + sNetworks[i].mData.mWiFi.mCredentialsLen = static_cast(credentials.size()); + + VerifyOrExit(ssid.size() <= sizeof(sNetworks[i].mNetworkID), err = NetworkCommissioningStatus::kOutOfRange); + memcpy(sNetworks[i].mNetworkID, sNetworks[i].mData.mWiFi.mSSID, ssid.size()); + + using NetworkIDLenType = decltype(sNetworks[i].mNetworkIDLen); + VerifyOrExit(CanCastTo(ssid.size()), err = NetworkCommissioningStatus::kOutOfRange); + sNetworks[i].mNetworkIDLen = static_cast(ssid.size()); + + sNetworks[i].mNetworkType = NetworkType::kWiFi; + sNetworks[i].mEnabled = false; + + err = NetworkCommissioningStatus::kSuccess; + break; + } + } + + VerifyOrExit(err == NetworkCommissioningStatus::kSuccess, ); + + ChipLogDetail(Zcl, "WiFi provisioning data: SSID: %.*s", static_cast(ssid.size()), ssid.data()); +exit: + // TODO: We should encode response command here. + + ChipLogDetail(Zcl, "AddOrUpdateWiFiNetwork: %" PRIu8, to_underlying(err)); + response.networkingStatus = err; +#else + // The target does not supports WiFiNetwork. + // return "Command not found" error. + response.networkingStatus = NetworkCommissioningStatus::kUnknownError; +#endif + apCommandHandler->AddResponseData(commandPath, response); +} + +namespace { +CHIP_ERROR DoConnectNetwork(NetworkInfo * network) +{ + switch (network->mNetworkType) + { + case NetworkType::kThread: +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD +// TODO: On linux, we are using Reset() instead of Detach() to disable thread network, which is not expected. +// Upstream issue: https://github.com/openthread/ot-br-posix/issues/755 +#if !CHIP_DEVICE_LAYER_TARGET_LINUX + ReturnErrorOnFailure(DeviceLayer::ThreadStackMgr().SetThreadEnabled(false)); +#endif + ReturnErrorOnFailure(DeviceLayer::ThreadStackMgr().SetThreadProvision(network->mData.mThread.AsByteSpan())); + ReturnErrorOnFailure(DeviceLayer::ThreadStackMgr().SetThreadEnabled(true)); +#else + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; +#endif + break; + case NetworkType::kWiFi: +#if defined(CHIP_DEVICE_LAYER_TARGET) + { + // TODO: Currently, DeviceNetworkProvisioningDelegateImpl assumes that ssid and credentials are null terminated strings, + // which is not correct, this should be changed once we have better method for commissioning wifi networks. + DeviceLayer::DeviceNetworkProvisioningDelegateImpl deviceDelegate; + ReturnErrorOnFailure(deviceDelegate.ProvisionWiFi(reinterpret_cast(network->mData.mWiFi.mSSID), + reinterpret_cast(network->mData.mWiFi.mCredentials))); + break; + } +#else + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; +#endif + break; + case NetworkType::kEthernet: + case NetworkType::kUndefined: + default: + return CHIP_ERROR_NOT_IMPLEMENTED; + } + network->mEnabled = true; + return CHIP_NO_ERROR; +} +} // namespace + +void OnConnectNetworkCommandCallbackInternal(app::CommandHandler * apCommandHandler, const app::ConcreteCommandPath & commandPath, + ByteSpan networkID, uint64_t breadcrumb, uint32_t timeoutMs) +{ + Commands::ConnectNetworkResponse::Type response; + size_t networkSeq; + NetworkCommissioningStatus err = NetworkCommissioningStatus::kNetworkIDNotFound; + + for (networkSeq = 0; networkSeq < kMaxNetworks; networkSeq++) + { + if (sNetworks[networkSeq].mNetworkIDLen == networkID.size() && + sNetworks[networkSeq].mNetworkType != NetworkType::kUndefined && + memcmp(sNetworks[networkSeq].mNetworkID, networkID.data(), networkID.size()) == 0) + { + // TODO: Currently, we cannot figure out the detailed error from network provisioning on DeviceLayer, we should + // implement this in device layer. + VerifyOrExit(DoConnectNetwork(&sNetworks[networkSeq]) == CHIP_NO_ERROR, + err = NetworkCommissioningStatus::kUnknownError); + ExitNow(err = NetworkCommissioningStatus::kSuccess); + } + } + // TODO: We should encode response command here. +exit: + if (err == NetworkCommissioningStatus::kSuccess) + { + DeviceLayer::DeviceControlServer::DeviceControlSvr().ConnectNetworkForOperational(networkID); + } + response.networkingStatus = err; + apCommandHandler->AddResponseData(commandPath, response); +} + +} // namespace NetworkCommissioning +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/clusters/network-commissioning-old/network-commissioning.h b/src/app/clusters/network-commissioning-old/network-commissioning.h new file mode 100644 index 00000000000000..6b389d8324c724 --- /dev/null +++ b/src/app/clusters/network-commissioning-old/network-commissioning.h @@ -0,0 +1,40 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace NetworkCommissioning { +void OnAddOrUpdateThreadNetworkCommandCallbackInternal(app::CommandHandler *, const app::ConcreteCommandPath & commandPath, + ByteSpan operationalDataset, uint64_t breadcrumb, uint32_t timeoutMs); +void OnAddOrUpdateWiFiNetworkCommandCallbackInternal(app::CommandHandler *, const app::ConcreteCommandPath & commandPath, + ByteSpan ssid, ByteSpan credentials, uint64_t breadcrumb, uint32_t timeoutMs); +void OnConnectNetworkCommandCallbackInternal(app::CommandHandler *, const app::ConcreteCommandPath & commandPath, + ByteSpan networkID, uint64_t breadcrumb, uint32_t timeoutMs); +} // namespace NetworkCommissioning + +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/clusters/network-commissioning/network-commissioning.cpp b/src/app/clusters/network-commissioning/network-commissioning.cpp index 3ecdbda015eeed..6034e57690ab51 100644 --- a/src/app/clusters/network-commissioning/network-commissioning.cpp +++ b/src/app/clusters/network-commissioning/network-commissioning.cpp @@ -18,273 +18,415 @@ #include "network-commissioning.h" -#include -#include - -#include +#include +#include +#include +#include #include -#include #include -#include -#include -#include #include - -#include - -#if CHIP_DEVICE_CONFIG_ENABLE_THREAD -#include -#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD - -// Include DeviceNetworkProvisioningDelegateImpl for WiFi provisioning. -// TODO: Enable wifi network should be done by ConnectivityManager. (Or other platform neutral interfaces) -#if defined(CHIP_DEVICE_LAYER_TARGET) -#define DEVICENETWORKPROVISIONING_HEADER -#include DEVICENETWORKPROVISIONING_HEADER -#endif - -// TODO: Configuration should move to build-time configuration -#ifndef CHIP_CLUSTER_NETWORK_COMMISSIONING_MAX_NETWORKS -#define CHIP_CLUSTER_NETWORK_COMMISSIONING_MAX_NETWORKS 4 -#endif // CHIP_CLUSTER_NETWORK_COMMISSIONING_MAX_NETWORKS - -using namespace chip; -using namespace chip::app; +#include +#include namespace chip { namespace app { namespace Clusters { namespace NetworkCommissioning { -constexpr uint8_t kMaxNetworkIDLen = 32; -constexpr uint8_t kMaxThreadDatasetLen = 254; // As defined in Thread spec. -constexpr uint8_t kMaxWiFiSSIDLen = 32; -constexpr uint8_t kMaxWiFiCredentialsLen = 64; -constexpr uint8_t kMaxNetworks = CHIP_CLUSTER_NETWORK_COMMISSIONING_MAX_NETWORKS; +using namespace DeviceLayer::NetworkCommissioning; -enum class NetworkType : uint8_t -{ - kUndefined = 0, - kWiFi = 1, - kThread = 2, - kEthernet = 3, -}; +namespace { +// For WiFi and Thread scan results, each item will cose ~60 bytes in TLV, thus 15 is a safe upper bound of scan results. +constexpr size_t kMaxNetworksInScanResponse = 15; -struct ThreadNetworkInfo +NetworkCommissioningStatus ToClusterObjectEnum(Status status) { - uint8_t mDataset[kMaxThreadDatasetLen]; - uint8_t mDatasetLen; -}; + // clang-format off + static_assert(to_underlying(NetworkCommissioningStatus::kSuccess ) == to_underlying(Status::kSuccess ), "kSuccess value mismatch."); + static_assert(to_underlying(NetworkCommissioningStatus::kOutOfRange ) == to_underlying(Status::kOutOfRange ), "kOutOfRange value mismatch."); + static_assert(to_underlying(NetworkCommissioningStatus::kBoundsExceeded ) == to_underlying(Status::kBoundsExceeded ), "kBoundsExceeded value mismatch."); + static_assert(to_underlying(NetworkCommissioningStatus::kNetworkIDNotFound ) == to_underlying(Status::kNetworkIDNotFound ), "kNetworkIDNotFound value mismatch."); + static_assert(to_underlying(NetworkCommissioningStatus::kDuplicateNetworkID ) == to_underlying(Status::kDuplicateNetworkID ), "kDuplicateNetworkID value mismatch."); + static_assert(to_underlying(NetworkCommissioningStatus::kNetworkNotFound ) == to_underlying(Status::kNetworkNotFound ), "kNetworkNotFound value mismatch."); + static_assert(to_underlying(NetworkCommissioningStatus::kRegulatoryError ) == to_underlying(Status::kRegulatoryError ), "kRegulatoryError value mismatch."); + static_assert(to_underlying(NetworkCommissioningStatus::kAuthFailure ) == to_underlying(Status::kAuthFailure ), "kAuthFailure value mismatch."); + static_assert(to_underlying(NetworkCommissioningStatus::kUnsupportedSecurity ) == to_underlying(Status::kUnsupportedSecurity ), "kUnsupportedSecurity value mismatch."); + static_assert(to_underlying(NetworkCommissioningStatus::kOtherConnectionFailure) == to_underlying(Status::kOtherConnectionFailure), "kOtherConnectionFailure value mismatch."); + static_assert(to_underlying(NetworkCommissioningStatus::kIPV6Failed ) == to_underlying(Status::kIPV6Failed ), "kIPV6Failed value mismatch."); + static_assert(to_underlying(NetworkCommissioningStatus::kIPBindFailed ) == to_underlying(Status::kIPBindFailed ), "kIPBindFailed value mismatch."); + static_assert(to_underlying(NetworkCommissioningStatus::kUnknownError ) == to_underlying(Status::kUnknownError ), "kUnknownError value mismatch."); + // clang-format on + return static_cast(to_underlying(status)); +} -struct WiFiNetworkInfo +NetworkCommissioning::WiFiBand ToClusterObjectEnum(DeviceLayer::NetworkCommissioning::WiFiBand band) { - uint8_t mSSID[kMaxWiFiSSIDLen + 1]; - uint8_t mSSIDLen; - uint8_t mCredentials[kMaxWiFiCredentialsLen]; - uint8_t mCredentialsLen; -}; + using ClusterObject = NetworkCommissioning::WiFiBand; + using PlatfromInterface = DeviceLayer::NetworkCommissioning::WiFiBand; -struct NetworkInfo -{ - uint8_t mNetworkID[kMaxNetworkIDLen]; - uint8_t mNetworkIDLen; - uint8_t mEnabled; - NetworkType mNetworkType; - union NetworkData - { -#if CHIP_DEVICE_CONFIG_ENABLE_THREAD - Thread::OperationalDataset mThread; -#endif -#if defined(CHIP_DEVICE_LAYER_TARGET) - WiFiNetworkInfo mWiFi; -#endif - } mData; -}; + static_assert(to_underlying(ClusterObject::k2g4) == to_underlying(PlatfromInterface::k2g4), "k2g4 valus mismatch."); + static_assert(to_underlying(ClusterObject::k3g65) == to_underlying(PlatfromInterface::k3g65), "k3g65 valus mismatch."); + static_assert(to_underlying(ClusterObject::k5g) == to_underlying(PlatfromInterface::k5g), "k5g valus mismatch."); + static_assert(to_underlying(ClusterObject::k6g) == to_underlying(PlatfromInterface::k6g), "k6g valus mismatch."); + static_assert(to_underlying(ClusterObject::k60g) == to_underlying(PlatfromInterface::k60g), "k60g valus mismatch."); + + return static_cast(to_underlying(band)); +} -namespace { -// The internal network info containing credentials. Need to find some better place to save these info. -NetworkInfo sNetworks[kMaxNetworks]; } // namespace -void OnAddOrUpdateThreadNetworkCommandCallbackInternal(app::CommandHandler * apCommandHandler, - const app::ConcreteCommandPath & commandPath, ByteSpan operationalDataset, - uint64_t breadcrumb, uint32_t timeoutMs) +CHIP_ERROR Instance::Init() { - Commands::NetworkConfigResponse::Type response; -#if CHIP_DEVICE_CONFIG_ENABLE_THREAD - NetworkCommissioningStatus err = NetworkCommissioningStatus::kBoundsExceeded; + ReturnErrorOnFailure(chip::app::InteractionModelEngine::GetInstance()->RegisterCommandHandler(this)); + VerifyOrReturnError(registerAttributeAccessOverride(this), CHIP_ERROR_INCORRECT_STATE); + ReturnErrorOnFailure( + DeviceLayer::PlatformMgrImpl().AddEventHandler(_OnCommissioningComplete, reinterpret_cast(this))); + ReturnErrorOnFailure(mpBaseDriver->Init()); + return CHIP_NO_ERROR; +} + +CHIP_ERROR Instance::Shutdown() +{ + ReturnErrorOnFailure(mpBaseDriver->Shutdown()); + return CHIP_NO_ERROR; +} - for (size_t i = 0; i < kMaxNetworks; i++) +void Instance::InvokeCommand(HandlerContext & ctxt) +{ + if (mAsyncCommandHandle.Get() != nullptr) { - if (sNetworks[i].mNetworkType == NetworkType::kUndefined) - { - Thread::OperationalDataset & dataset = sNetworks[i].mData.mThread; - CHIP_ERROR error = dataset.Init(operationalDataset); + // We have a command processing in the backend, reject all incoming commands. + ctxt.mCommandHandler.AddStatus(ctxt.mRequestPath, Protocols::InteractionModel::Status::Busy); + ctxt.SetCommandHandled(); + return; + } + + // Since mPath is used for building the response command, and we have checked that we are not pending the response of another + // command above. So it is safe to set the mPath here and not clear it when return. + mPath = ctxt.mRequestPath; + + switch (ctxt.mRequestPath.mCommandId) + { + case Commands::ScanNetworks::Id: + VerifyOrReturn((mType & kFeatureMapWireless) != 0); + HandleCommand( + ctxt, [this](HandlerContext & ctx, const auto & req) { HandleScanNetworks(ctx, req); }); + return; + + case Commands::AddOrUpdateWiFiNetwork::Id: + VerifyOrReturn(mType == kFeatureMapWiFi); + HandleCommand( + ctxt, [this](HandlerContext & ctx, const auto & req) { HandleAddOrUpdateWiFiNetwork(ctx, req); }); + return; + + case Commands::AddOrUpdateThreadNetwork::Id: + VerifyOrReturn(mType == kFeatureMapThread); + HandleCommand( + ctxt, [this](HandlerContext & ctx, const auto & req) { HandleAddOrUpdateThreadNetwork(ctx, req); }); + return; + + case Commands::RemoveNetwork::Id: + VerifyOrReturn((mType & kFeatureMapWireless) != 0); + HandleCommand( + ctxt, [this](HandlerContext & ctx, const auto & req) { HandleRemoveNetwork(ctx, req); }); + return; + + case Commands::ConnectNetwork::Id: + VerifyOrReturn((mType & kFeatureMapWireless) != 0); + HandleCommand( + ctxt, [this](HandlerContext & ctx, const auto & req) { HandleConnectNetwork(ctx, req); }); + return; + + case Commands::ReorderNetwork::Id: + VerifyOrReturn((mType & kFeatureMapWireless) != 0); + HandleCommand( + ctxt, [this](HandlerContext & ctx, const auto & req) { HandleReorderNetwork(ctx, req); }); + return; + } +} - if (error != CHIP_NO_ERROR) +CHIP_ERROR Instance::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) +{ + switch (aPath.mAttributeId) + { + case Attributes::MaxNetworks::Id: + return aEncoder.Encode(mpBaseDriver->GetMaxNetworks()); + + case Attributes::Networks::Id: + return aEncoder.EncodeList([this](const auto & encoder) { + auto networks = mpBaseDriver->GetNetworks(); + CHIP_ERROR err = CHIP_NO_ERROR; + Structs::NetworkInfo::Type networkForEncode; + NetworkCommissioning::Network network; + for (; networks != nullptr && networks->Next(network);) { - ChipLogDetail(Zcl, "Failed to parse Thread operational dataset: %s", ErrorStr(error)); - err = NetworkCommissioningStatus::kUnknownError; - break; + networkForEncode.networkID = ByteSpan(network.networkID, network.networkIDLen); + networkForEncode.connected = network.connected; + SuccessOrExit(err = encoder.Encode(networkForEncode)); + } + exit: + if (networks != nullptr) + { + networks->Release(); } - uint8_t extendedPanId[Thread::kSizeExtendedPanId]; - - static_assert(sizeof(sNetworks[i].mNetworkID) >= sizeof(extendedPanId), - "Network ID must be larger than Thread extended PAN ID!"); - SuccessOrExit(dataset.GetExtendedPanId(extendedPanId)); - memcpy(sNetworks[i].mNetworkID, extendedPanId, sizeof(extendedPanId)); - sNetworks[i].mNetworkIDLen = sizeof(extendedPanId); + return err; + }); - sNetworks[i].mNetworkType = NetworkType::kThread; - sNetworks[i].mEnabled = false; + case Attributes::ScanMaxTimeSeconds::Id: + if (mType & kFeatureMapWireless) + { + return aEncoder.Encode(mpWirelessDriver->GetScanNetworkTimeoutSeconds()); + } + return CHIP_NO_ERROR; - err = NetworkCommissioningStatus::kSuccess; - break; + case Attributes::ConnectMaxTimeSeconds::Id: + if (mType & kFeatureMapWireless) + { + return aEncoder.Encode(mpWirelessDriver->GetConnectNetworkTimeoutSeconds()); } + return CHIP_NO_ERROR; + + case Attributes::InterfaceEnabled::Id: + return aEncoder.Encode(mpBaseDriver->GetEnabled()); + + // TODO: Add support to the following attributes + case Attributes::LastNetworkingStatus::Id: + return aEncoder.Encode(NetworkCommissioningStatus::kSuccess); + + case Attributes::LastNetworkID::Id: + return aEncoder.Encode(ByteSpan()); + + case Attributes::LastConnectErrorValue::Id: + return aEncoder.Encode(Attributes::LastConnectErrorValue::TypeInfo::Type(0)); + + case Attributes::FeatureMap::Id: + return aEncoder.Encode(mType); + + default: + return CHIP_NO_ERROR; } +} -exit: - // TODO: We should encode response command here. - - ChipLogDetail(Zcl, "AddOrUpdateThreadNetwork: %" PRIu8, to_underlying(err)); - response.networkingStatus = err; -#else - // The target does not supports ThreadNetwork. We should not add AddOrUpdateThreadNetwork command in that case then the upper - // layer will return "Command not found" error. - response.networkingStatus = NetworkCommissioningStatus::kUnknownError; -#endif - apCommandHandler->AddResponseData(commandPath, response); +CHIP_ERROR Instance::Write(const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder) +{ + switch (aPath.mAttributeId) + { + case Attributes::InterfaceEnabled::Id: + bool value; + ReturnErrorOnFailure(aDecoder.Decode(value)); + return mpBaseDriver->SetEnabled(value); + default: + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + } } -void OnAddOrUpdateWiFiNetworkCommandCallbackInternal(app::CommandHandler * apCommandHandler, - const app::ConcreteCommandPath & commandPath, ByteSpan ssid, - ByteSpan credentials, uint64_t breadcrumb, uint32_t timeoutMs) +void Instance::HandleScanNetworks(HandlerContext & ctx, const Commands::ScanNetworks::DecodableType & req) +{ + switch (mType) + { + case kFeatureMapWiFi: + mAsyncCommandHandle = app::CommandHandler::Handle(&ctx.mCommandHandler); + mpDriver.Get()->ScanNetworks(req.ssid, this); + return; + case kFeatureMapThread: + mAsyncCommandHandle = app::CommandHandler::Handle(&ctx.mCommandHandler); + mpDriver.Get()->ScanNetworks(this); + return; + default: + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Protocols::InteractionModel::Status::UnsupportedCommand); + return; + } +} + +void Instance::HandleAddOrUpdateWiFiNetwork(HandlerContext & ctx, const Commands::AddOrUpdateWiFiNetwork::DecodableType & req) { Commands::NetworkConfigResponse::Type response; -#if defined(CHIP_DEVICE_LAYER_TARGET) - NetworkCommissioningStatus err = NetworkCommissioningStatus::kBoundsExceeded; + response.networkingStatus = ToClusterObjectEnum(mpDriver.Get()->AddOrUpdateNetwork(req.ssid, req.credentials)); + ctx.mCommandHandler.AddResponseData(ctx.mRequestPath, response); +} - for (size_t i = 0; i < kMaxNetworks; i++) - { - if (sNetworks[i].mNetworkType == NetworkType::kUndefined) - { - VerifyOrExit(ssid.size() <= sizeof(sNetworks[i].mData.mWiFi.mSSID), err = NetworkCommissioningStatus::kOutOfRange); - memcpy(sNetworks[i].mData.mWiFi.mSSID, ssid.data(), ssid.size()); +void Instance::HandleAddOrUpdateThreadNetwork(HandlerContext & ctx, const Commands::AddOrUpdateThreadNetwork::DecodableType & req) +{ + Commands::NetworkConfigResponse::Type response; + response.networkingStatus = ToClusterObjectEnum(mpDriver.Get()->AddOrUpdateNetwork(req.operationalDataset)); + ctx.mCommandHandler.AddResponseData(ctx.mRequestPath, response); +} - using WiFiSSIDLenType = decltype(sNetworks[i].mData.mWiFi.mSSIDLen); - VerifyOrExit(CanCastTo(ssid.size()), err = NetworkCommissioningStatus::kOutOfRange); - sNetworks[i].mData.mWiFi.mSSIDLen = static_cast(ssid.size()); +void Instance::HandleRemoveNetwork(HandlerContext & ctx, const Commands::RemoveNetwork::DecodableType & req) +{ + Commands::NetworkConfigResponse::Type response; + response.networkingStatus = ToClusterObjectEnum(mpWirelessDriver->RemoveNetwork(req.networkID)); + ctx.mCommandHandler.AddResponseData(ctx.mRequestPath, response); +} - VerifyOrExit(credentials.size() <= sizeof(sNetworks[i].mData.mWiFi.mCredentials), - err = NetworkCommissioningStatus::kOutOfRange); - memcpy(sNetworks[i].mData.mWiFi.mCredentials, credentials.data(), credentials.size()); +void Instance::HandleConnectNetwork(HandlerContext & ctx, const Commands::ConnectNetwork::DecodableType & req) +{ + Commands::ConnectNetworkResponse::Type response; - using WiFiCredentialsLenType = decltype(sNetworks[i].mData.mWiFi.mCredentialsLen); - VerifyOrExit(CanCastTo(ssid.size()), err = NetworkCommissioningStatus::kOutOfRange); - sNetworks[i].mData.mWiFi.mCredentialsLen = static_cast(credentials.size()); + mAsyncCommandHandle = app::CommandHandler::Handle(&ctx.mCommandHandler); + mpWirelessDriver->ConnectNetwork(req.networkID, this); +} - VerifyOrExit(ssid.size() <= sizeof(sNetworks[i].mNetworkID), err = NetworkCommissioningStatus::kOutOfRange); - memcpy(sNetworks[i].mNetworkID, sNetworks[i].mData.mWiFi.mSSID, ssid.size()); +void Instance::HandleReorderNetwork(HandlerContext & ctx, const Commands::ReorderNetwork::DecodableType & req) +{ + Commands::NetworkConfigResponse::Type response; + response.networkingStatus = ToClusterObjectEnum(mpWirelessDriver->ReorderNetwork(req.networkID, req.networkIndex)); + ctx.mCommandHandler.AddResponseData(ctx.mRequestPath, response); +} - using NetworkIDLenType = decltype(sNetworks[i].mNetworkIDLen); - VerifyOrExit(CanCastTo(ssid.size()), err = NetworkCommissioningStatus::kOutOfRange); - sNetworks[i].mNetworkIDLen = static_cast(ssid.size()); +void Instance::OnResult(Status commissioningError, CharSpan errorText, int32_t interfaceStatus) +{ + auto commandHandleRef = std::move(mAsyncCommandHandle); + auto commandHandle = commandHandleRef.Get(); + if (commandHandle == nullptr) + { + // When the platform shutted down, interaction model engine will invalidate all commandHandle to avoid dangling references. + // We may receive the callback after it and should make it noop. + return; + } - sNetworks[i].mNetworkType = NetworkType::kWiFi; - sNetworks[i].mEnabled = false; + Commands::ConnectNetworkResponse::Type response; + response.networkingStatus = ToClusterObjectEnum(commissioningError); + response.debugText = errorText; + response.errorValue = interfaceStatus; + commandHandle->AddResponseData(mPath, response); + if (commissioningError == Status::kSuccess) + { + // TODO: Pass the actual network id to device control server. + DeviceLayer::DeviceControlServer::DeviceControlSvr().ConnectNetworkForOperational(ByteSpan()); + } +} - err = NetworkCommissioningStatus::kSuccess; - break; - } +void Instance::OnFinished(Status status, CharSpan debugText, ThreadScanResponseIterator * networks) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + auto commandHandleRef = std::move(mAsyncCommandHandle); + auto commandHandle = commandHandleRef.Get(); + if (commandHandle == nullptr) + { + // When the platform shutted down, interaction model engine will invalidate all commandHandle to avoid dangling references. + // We may receive the callback after it and should make it noop. + return; } - VerifyOrExit(err == NetworkCommissioningStatus::kSuccess, ); + TLV::TLVWriter * writer; + TLV::TLVType listContainerType; + ThreadScanResponse scanResponse; + size_t networksEncoded = 0; + + SuccessOrExit(err = commandHandle->PrepareCommand( + ConcreteCommandPath(mPath.mEndpointId, NetworkCommissioning::Id, Commands::ScanNetworksResponse::Id))); + VerifyOrExit((writer = commandHandle->GetCommandDataIBTLVWriter()) != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + + SuccessOrExit(err = writer->Put(TLV::ContextTag(to_underlying(Commands::ScanNetworksResponse::Fields::kNetworkingStatus)), + ToClusterObjectEnum(status))); + SuccessOrExit(err = DataModel::Encode( + *writer, TLV::ContextTag(to_underlying(Commands::ScanNetworksResponse::Fields::kDebugText)), debugText)); + SuccessOrExit( + err = writer->StartContainer(TLV::ContextTag(to_underlying(Commands::ScanNetworksResponse::Fields::kThreadScanResults)), + TLV::TLVType::kTLVType_Array, listContainerType)); + + for (; networks != nullptr && networks->Next(scanResponse) && networksEncoded < kMaxNetworksInScanResponse; networksEncoded++) + { + Structs::ThreadInterfaceScanResult::Type result; + result.panId = scanResponse.panId; + result.extendedPanId = scanResponse.extendedPanId; + result.networkName = CharSpan(scanResponse.networkName, scanResponse.networkNameLen); + result.channel = scanResponse.channel; + result.version = scanResponse.version; + result.extendedAddress = scanResponse.extendedAddress; + result.rssi = scanResponse.rssi; + result.lqi = scanResponse.lqi; + SuccessOrExit(err = DataModel::Encode(*writer, TLV::AnonymousTag, result)); + } + + SuccessOrExit(err = writer->EndContainer(listContainerType)); + SuccessOrExit(err = commandHandle->FinishCommand()); - ChipLogDetail(Zcl, "WiFi provisioning data: SSID: %.*s", static_cast(ssid.size()), ssid.data()); exit: - // TODO: We should encode response command here. - - ChipLogDetail(Zcl, "AddOrUpdateWiFiNetwork: %" PRIu8, to_underlying(err)); - response.networkingStatus = err; -#else - // The target does not supports WiFiNetwork. - // return "Command not found" error. - response.networkingStatus = NetworkCommissioningStatus::kUnknownError; -#endif - apCommandHandler->AddResponseData(commandPath, response); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Failed to encode response: %s", err.AsString()); + } + networks->Release(); } -namespace { -CHIP_ERROR DoConnectNetwork(NetworkInfo * network) +void Instance::OnFinished(Status status, CharSpan debugText, WiFiScanResponseIterator * networks) { - switch (network->mNetworkType) + CHIP_ERROR err = CHIP_NO_ERROR; + auto commandHandleRef = std::move(mAsyncCommandHandle); + auto commandHandle = commandHandleRef.Get(); + if (commandHandle == nullptr) { - case NetworkType::kThread: -#if CHIP_DEVICE_CONFIG_ENABLE_THREAD -// TODO: On linux, we are using Reset() instead of Detach() to disable thread network, which is not expected. -// Upstream issue: https://github.com/openthread/ot-br-posix/issues/755 -#if !CHIP_DEVICE_LAYER_TARGET_LINUX - ReturnErrorOnFailure(DeviceLayer::ThreadStackMgr().SetThreadEnabled(false)); -#endif - ReturnErrorOnFailure(DeviceLayer::ThreadStackMgr().SetThreadProvision(network->mData.mThread.AsByteSpan())); - ReturnErrorOnFailure(DeviceLayer::ThreadStackMgr().SetThreadEnabled(true)); -#else - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; -#endif - break; - case NetworkType::kWiFi: -#if defined(CHIP_DEVICE_LAYER_TARGET) + // When the platform shutted down, interaction model engine will invalidate all commandHandle to avoid dangling references. + // We may receive the callback after it and should make it noop. + return; + } + + TLV::TLVWriter * writer; + TLV::TLVType listContainerType; + WiFiScanResponse scanResponse; + size_t networksEncoded = 0; + + SuccessOrExit(err = commandHandle->PrepareCommand( + ConcreteCommandPath(mPath.mEndpointId, NetworkCommissioning::Id, Commands::ScanNetworksResponse::Id))); + VerifyOrExit((writer = commandHandle->GetCommandDataIBTLVWriter()) != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + + SuccessOrExit(err = writer->Put(TLV::ContextTag(to_underlying(Commands::ScanNetworksResponse::Fields::kNetworkingStatus)), + ToClusterObjectEnum(status))); + SuccessOrExit(err = DataModel::Encode( + *writer, TLV::ContextTag(to_underlying(Commands::ScanNetworksResponse::Fields::kDebugText)), debugText)); + SuccessOrExit( + err = writer->StartContainer(TLV::ContextTag(to_underlying(Commands::ScanNetworksResponse::Fields::kWiFiScanResults)), + TLV::TLVType::kTLVType_Array, listContainerType)); + + for (; networks != nullptr && networks->Next(scanResponse) && networksEncoded < kMaxNetworksInScanResponse; networksEncoded++) { - // TODO: Currently, DeviceNetworkProvisioningDelegateImpl assumes that ssid and credentials are null terminated strings, - // which is not correct, this should be changed once we have better method for commissioning wifi networks. - DeviceLayer::DeviceNetworkProvisioningDelegateImpl deviceDelegate; - ReturnErrorOnFailure(deviceDelegate.ProvisionWiFi(reinterpret_cast(network->mData.mWiFi.mSSID), - reinterpret_cast(network->mData.mWiFi.mCredentials))); - break; + Structs::WiFiInterfaceScanResult::Type result; + result.security = scanResponse.security; + result.ssid = ByteSpan(scanResponse.ssid, scanResponse.ssidLen); + result.bssid = ByteSpan(scanResponse.bssid, sizeof(scanResponse.bssid)); + result.channel = scanResponse.channel; + result.wiFiBand = ToClusterObjectEnum(scanResponse.wiFiBand); + result.rssi = scanResponse.rssi; + SuccessOrExit(err = DataModel::Encode(*writer, TLV::AnonymousTag, result)); } -#else - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; -#endif - break; - case NetworkType::kEthernet: - case NetworkType::kUndefined: - default: - return CHIP_ERROR_NOT_IMPLEMENTED; + + SuccessOrExit(err = writer->EndContainer(listContainerType)); + SuccessOrExit(err = commandHandle->FinishCommand()); + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Failed to encode response: %s", err.AsString()); + } + if (networks != nullptr) + { + networks->Release(); } - network->mEnabled = true; - return CHIP_NO_ERROR; } -} // namespace -void OnConnectNetworkCommandCallbackInternal(app::CommandHandler * apCommandHandler, const app::ConcreteCommandPath & commandPath, - ByteSpan networkID, uint64_t breadcrumb, uint32_t timeoutMs) +void Instance::_OnCommissioningComplete(const DeviceLayer::ChipDeviceEvent * event, intptr_t arg) { - Commands::ConnectNetworkResponse::Type response; - size_t networkSeq; - NetworkCommissioningStatus err = NetworkCommissioningStatus::kNetworkIDNotFound; + Instance * this_ = reinterpret_cast(arg); + VerifyOrReturn(event->Type == DeviceLayer::DeviceEventType::kCommissioningComplete); + this_->OnCommissioningComplete(event->CommissioningComplete.status); +} - for (networkSeq = 0; networkSeq < kMaxNetworks; networkSeq++) +void Instance::OnCommissioningComplete(CHIP_ERROR err) +{ + VerifyOrReturn((mType & kFeatureMapWireless) != 0); + if (err == CHIP_NO_ERROR) { - if (sNetworks[networkSeq].mNetworkIDLen == networkID.size() && - sNetworks[networkSeq].mNetworkType != NetworkType::kUndefined && - memcmp(sNetworks[networkSeq].mNetworkID, networkID.data(), networkID.size()) == 0) - { - // TODO: Currently, we cannot figure out the detailed error from network provisioning on DeviceLayer, we should - // implement this in device layer. - VerifyOrExit(DoConnectNetwork(&sNetworks[networkSeq]) == CHIP_NO_ERROR, - err = NetworkCommissioningStatus::kUnknownError); - ExitNow(err = NetworkCommissioningStatus::kSuccess); - } + ChipLogDetail(Zcl, "Commissioning complete, notify platform driver to persist network credentails."); + mpWirelessDriver->CommitConfiguration(); } - // TODO: We should encode response command here. -exit: - if (err == NetworkCommissioningStatus::kSuccess) + else { - DeviceLayer::DeviceControlServer::DeviceControlSvr().ConnectNetworkForOperational(networkID); + ChipLogDetail(Zcl, "Failsafe timeout, tell platform driver to revert network credentails."); + mpWirelessDriver->RevertConfiguration(); } - response.networkingStatus = err; - apCommandHandler->AddResponseData(commandPath, response); } } // namespace NetworkCommissioning diff --git a/src/app/clusters/network-commissioning/network-commissioning.h b/src/app/clusters/network-commissioning/network-commissioning.h index 6b389d8324c724..705b3879648f93 100644 --- a/src/app/clusters/network-commissioning/network-commissioning.h +++ b/src/app/clusters/network-commissioning/network-commissioning.h @@ -18,23 +18,109 @@ #pragma once -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include namespace chip { namespace app { namespace Clusters { namespace NetworkCommissioning { -void OnAddOrUpdateThreadNetworkCommandCallbackInternal(app::CommandHandler *, const app::ConcreteCommandPath & commandPath, - ByteSpan operationalDataset, uint64_t breadcrumb, uint32_t timeoutMs); -void OnAddOrUpdateWiFiNetworkCommandCallbackInternal(app::CommandHandler *, const app::ConcreteCommandPath & commandPath, - ByteSpan ssid, ByteSpan credentials, uint64_t breadcrumb, uint32_t timeoutMs); -void OnConnectNetworkCommandCallbackInternal(app::CommandHandler *, const app::ConcreteCommandPath & commandPath, - ByteSpan networkID, uint64_t breadcrumb, uint32_t timeoutMs); -} // namespace NetworkCommissioning +// TODO: Use macro to disable some wifi or thread +class Instance : public CommandHandlerInterface, + public AttributeAccessInterface, + public DeviceLayer::NetworkCommissioning::Internal::WirelessDriver::ConnectCallback, + public DeviceLayer::NetworkCommissioning::WiFiDriver::ScanCallback, + public DeviceLayer::NetworkCommissioning::ThreadDriver::ScanCallback +{ +public: + /** + * Register will register the network commissioning instance to the attribute and command dispatching route. + */ + CHIP_ERROR Init(); + CHIP_ERROR Shutdown(); + + // CommandHandlerInterface + void InvokeCommand(HandlerContext & ctx) override; + + // AttributeAccessInterface + CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; + CHIP_ERROR Write(const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder) override; + + // WirelessDriver::ConnectCallback + void OnResult(DeviceLayer::NetworkCommissioning::Status commissioningError, CharSpan errorText, + int32_t interfaceStatus) override; + + // WiFiDriver::ScanCallback + void OnFinished(DeviceLayer::NetworkCommissioning::Status err, CharSpan debugText, + DeviceLayer::NetworkCommissioning::WiFiScanResponseIterator * networks) override; + + // ThreadDriver::ScanCallback + void OnFinished(DeviceLayer::NetworkCommissioning::Status err, CharSpan debugText, + DeviceLayer::NetworkCommissioning::ThreadScanResponseIterator * networks) override; + +private: + static constexpr uint32_t kFeatureMapWiFi = 0b0000'0001; + static constexpr uint32_t kFeatureMapThread = 0b0000'0010; + static constexpr uint32_t kFeatureMapEthernet = 0b0000'0100; + + static constexpr uint32_t kFeatureMapWireless = 0b0000'0011; + + static void _OnCommissioningComplete(const DeviceLayer::ChipDeviceEvent * event, intptr_t arg); + void OnCommissioningComplete(CHIP_ERROR err); + + const uint32_t mType; + + DeviceLayer::NetworkCommissioning::Internal::WirelessDriver * const mpWirelessDriver; + DeviceLayer::NetworkCommissioning::Internal::BaseDriver * const mpBaseDriver; + + Variant mpDriver; + + app::CommandHandler::Handle mAsyncCommandHandle; + + ConcreteCommandPath mPath = ConcreteCommandPath(0, 0, 0); + + // Actual handlers of the commands + void HandleScanNetworks(HandlerContext & ctx, const Commands::ScanNetworks::DecodableType & req); + void HandleAddOrUpdateWiFiNetwork(HandlerContext & ctx, const Commands::AddOrUpdateWiFiNetwork::DecodableType & req); + void HandleAddOrUpdateThreadNetwork(HandlerContext & ctx, const Commands::AddOrUpdateThreadNetwork::DecodableType & req); + void HandleRemoveNetwork(HandlerContext & ctx, const Commands::RemoveNetwork::DecodableType & req); + void HandleConnectNetwork(HandlerContext & ctx, const Commands::ConnectNetwork::DecodableType & req); + void HandleReorderNetwork(HandlerContext & ctx, const Commands::ReorderNetwork::DecodableType & req); + +public: + Instance(EndpointId aEndpointId, DeviceLayer::NetworkCommissioning::WiFiDriver * apDelegate) : + CommandHandlerInterface(Optional(aEndpointId), Id), + AttributeAccessInterface(Optional(aEndpointId), Id), mType(kFeatureMapWiFi), mpWirelessDriver(apDelegate), + mpBaseDriver(apDelegate) + { + mpDriver.Set(apDelegate); + } + + Instance(EndpointId aEndpointId, DeviceLayer::NetworkCommissioning::ThreadDriver * apDelegate) : + CommandHandlerInterface(Optional(aEndpointId), Id), + AttributeAccessInterface(Optional(aEndpointId), Id), mType(kFeatureMapThread), mpWirelessDriver(apDelegate), + mpBaseDriver(apDelegate) + { + mpDriver.Set(apDelegate); + } + + Instance(EndpointId aEndpointId, DeviceLayer::NetworkCommissioning::EthernetDriver * apDelegate) : + CommandHandlerInterface(Optional(aEndpointId), Id), + AttributeAccessInterface(Optional(aEndpointId), Id), mType(kFeatureMapEthernet), mpWirelessDriver(nullptr), + mpBaseDriver(apDelegate) + {} + + virtual ~Instance() = default; +}; + +} // namespace NetworkCommissioning } // namespace Clusters } // namespace app } // namespace chip diff --git a/src/app/zap-templates/zcl/data-model/chip/commissioning.xml b/src/app/zap-templates/zcl/data-model/chip/commissioning.xml index c46e0c537ae901..24c265f66c6c8c 100644 --- a/src/app/zap-templates/zcl/data-model/chip/commissioning.xml +++ b/src/app/zap-templates/zcl/data-model/chip/commissioning.xml @@ -32,6 +32,14 @@ limitations under the License. + + + + + + + + @@ -44,8 +52,8 @@ limitations under the License. - - + + diff --git a/src/app/zap_cluster_list.py b/src/app/zap_cluster_list.py index a39395f2499dc2..0397a65369b7a9 100755 --- a/src/app/zap_cluster_list.py +++ b/src/app/zap_cluster_list.py @@ -51,7 +51,8 @@ 'MEDIA_INPUT_CLUSTER': ['media-input-server'], 'MEDIA_PLAYBACK_CLUSTER': ['media-playback-server'], 'MODE_SELECT_CLUSTER': ['mode-select-server'], - 'NETWORK_COMMISSIONING_CLUSTER': ['network-commissioning'], + # network-commissioning-old is the old, not spec compliant implementation. + 'NETWORK_COMMISSIONING_CLUSTER': ['network-commissioning', 'network-commissioning-old'], 'OCCUPANCY_SENSING_CLUSTER': ['occupancy-sensor-server'], 'ON_OFF_CLUSTER': ['on-off-server'], 'ON_OFF_SWITCH_CONFIG_CLUSTER': [], diff --git a/src/controller/python/chip/clusters/Objects.py b/src/controller/python/chip/clusters/Objects.py index 24b51078cee60f..4ecc61568a0c0b 100644 --- a/src/controller/python/chip/clusters/Objects.py +++ b/src/controller/python/chip/clusters/Objects.py @@ -8234,6 +8234,13 @@ class NetworkCommissioningStatus(IntEnum): kIPBindFailed = 0x0B kUnknownError = 0x0C + class WiFiBand(IntEnum): + k2g4 = 0x00 + k3g65 = 0x01 + k5g = 0x02 + k6g = 0x03 + k60g = 0x04 + class Structs: @dataclass @@ -8284,7 +8291,7 @@ def descriptor(cls) -> ClusterObjectDescriptor: ClusterObjectFieldDescriptor(Label="ssid", Tag=2, Type=bytes), ClusterObjectFieldDescriptor(Label="bssid", Tag=3, Type=bytes), ClusterObjectFieldDescriptor(Label="channel", Tag=4, Type=uint), - ClusterObjectFieldDescriptor(Label="wiFiBand", Tag=5, Type=uint), + ClusterObjectFieldDescriptor(Label="wiFiBand", Tag=5, Type=NetworkCommissioning.Enums.WiFiBand), ClusterObjectFieldDescriptor(Label="rssi", Tag=6, Type=int), ]) @@ -8292,7 +8299,7 @@ def descriptor(cls) -> ClusterObjectDescriptor: ssid: 'bytes' = b"" bssid: 'bytes' = b"" channel: 'uint' = 0 - wiFiBand: 'uint' = 0 + wiFiBand: 'NetworkCommissioning.Enums.WiFiBand' = 0 rssi: 'int' = 0 diff --git a/src/controller/python/test/test_scripts/mobile-device-test.py b/src/controller/python/test/test_scripts/mobile-device-test.py index 865e31c242a6f7..a2d290336d1633 100755 --- a/src/controller/python/test/test_scripts/mobile-device-test.py +++ b/src/controller/python/test/test_scripts/mobile-device-test.py @@ -22,7 +22,8 @@ import sys from optparse import OptionParser from base import TestFail, TestTimeout, BaseTestHelper, FailIfNot, logger -from cluster_objects import ClusterObjectTests +from cluster_objects import NODE_ID, ClusterObjectTests +from network_commissioning import NetworkCommissioningTests import asyncio # The thread network dataset tlv for testing, splited into T-L-V. @@ -95,6 +96,11 @@ def main(): FailIfNot(test.TestResolve(nodeid=1), "Failed to resolve nodeid") + # Still test network commissioning + logger.info("Testing network commissioning") + FailIfNot(asyncio.run(NetworkCommissioningTests(devCtrl=test.devCtrl, nodeid=1).run()), + "Failed to finish network commissioning") + logger.info("Testing on off cluster") FailIfNot(test.TestOnOffCluster(nodeid=1, endpoint=LIGHTING_ENDPOINT_ID, diff --git a/src/controller/python/test/test_scripts/network_commissioning.py b/src/controller/python/test/test_scripts/network_commissioning.py new file mode 100644 index 00000000000000..a76236a7b6da66 --- /dev/null +++ b/src/controller/python/test/test_scripts/network_commissioning.py @@ -0,0 +1,252 @@ +# +# Copyright (c) 2021 Project CHIP Authors +# All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import chip.clusters as Clusters +import logging +from chip.clusters.Attribute import AttributePath, AttributeReadResult, AttributeStatus, ValueDecodeFailure +import chip.interaction_model +import asyncio + +logger = logging.getLogger('NetworkCommissioning') +logger.setLevel(logging.INFO) + +TEST_THREAD_NETWORK_DATASET_TLVS = [bytes.fromhex("0e080000000000010000" + + "000300000c" + + "35060004001fffe0" + + "0208fedcba9876543210" + + "0708fd00000000001234" + + "0510ffeeddccbbaa99887766554433221100" + + "030e54657374696e674e6574776f726b" + + "0102d252" + + "041081cb3b2efa781cc778397497ff520fa50c0302a0ff"), + # End of first TLV + ] +# Network id, for the thread network, current a const value, will be changed to XPANID of the thread network. +TEST_THREAD_NETWORK_IDS = [ + bytes.fromhex("fedcba9876543210"), +] + +TEST_WIFI_SSID = "TestSSID" +TEST_WIFI_PASS = "TestPass" + +WIFI_NETWORK_FEATURE_MAP = 1 +THREAD_NETWORK_FEATURE_MAP = 2 + + +class NetworkCommissioningTests: + def __init__(self, devCtrl, nodeid): + self._devCtrl = devCtrl + self._nodeid = nodeid + + def log_interface_basic_info(self, values): + logger.info(f"The interface supports {values.maxNetworks} networks.") + logger.info( + f"ScanNetworks should take no more than {values.scanMaxTimeSeconds} seconds.") + logger.info( + f"ConnectNetwork should take no more than {values.connectMaxTimeSeconds} seconds.") + logger.info( + f"The feature map of this endpoint is {values.featureMap}.") + + async def test_wifi(self, endpointId): + logger.info(f"Get basic information of the endpoint") + res = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[ + (endpointId, + Clusters.NetworkCommissioning.Attributes.ConnectMaxTimeSeconds), + (endpointId, + Clusters.NetworkCommissioning.Attributes.ScanMaxTimeSeconds), + (endpointId, Clusters.NetworkCommissioning.Attributes.MaxNetworks), + (endpointId, Clusters.NetworkCommissioning.Attributes.FeatureMap)], + returnClusterObject=True) + self.log_interface_basic_info( + res[endpointId][Clusters.NetworkCommissioning]) + logger.info(f"Finished getting basic information of the endpoint") + + # Scan networks + logger.info(f"Scan networks") + req = Clusters.NetworkCommissioning.Commands.ScanNetworks( + ssid=b'', breadcrumb=0) + res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=endpointId, payload=req) + logger.info(f"Received response: {res}") + if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess: + raise AssertionError(f"Unexpected result: {res.networkingStatus}") + + # Remove existing network + logger.info(f"Check network list") + res = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[(endpointId, Clusters.NetworkCommissioning.Attributes.Networks)], returnClusterObject=True) + networkList = res[endpointId][Clusters.NetworkCommissioning].networks + logger.info(f"Got network list: {networkList}") + if len(networkList) != 0: + logger.info(f"Removing existing network") + req = Clusters.NetworkCommissioning.Commands.RemoveNetwork( + networkID=networkList[0].networkID, breadcrumb=0) + res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=endpointId, payload=req) + logger.info(f"Received response: {res}") + if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess: + raise AssertionError( + f"Unexpected result: {res.networkingStatus}") + + # Add first network + logger.info(f"Adding first test network") + req = Clusters.NetworkCommissioning.Commands.AddOrUpdateWiFiNetwork( + ssid=TEST_WIFI_SSID.encode(), credentials=TEST_WIFI_PASS.encode(), breadcrumb=0) + res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=endpointId, payload=req) + logger.info(f"Received response: {res}") + if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess: + raise AssertionError(f"Unexpected result: {res.networkingStatus}") + + logger.info(f"Check network list") + res = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[(endpointId, Clusters.NetworkCommissioning.Attributes.Networks)], returnClusterObject=True) + networkList = res[endpointId][Clusters.NetworkCommissioning].networks + logger.info(f"Got network list: {networkList}") + if len(networkList) != 1: + raise AssertionError( + f"Unexpected result: expect 1 networks, but {len(networkList)} networks received") + if networkList[0].networkID != TEST_WIFI_SSID.encode(): + raise AssertionError( + f"Unexpected result: first network ID should be 'TestSSID' got {networkList[0].networkID}") + + logger.info(f"Connect to a network") + req = Clusters.NetworkCommissioning.Commands.ConnectNetwork( + networkID=TEST_WIFI_SSID.encode(), breadcrumb=0) + res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=endpointId, payload=req) + logger.info(f"Got response: {res}") + if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess: + raise AssertionError(f"Unexpected result: {res.networkingStatus}") + logger.info(f"Device connected to a network.") + + # Note: On Linux, when connecting to a connected network, it will return immediately, however, it will try a reconnect. This will make the below attribute read return false negative values. + await asyncio.sleep(5) + + logger.info(f"Check network is connected") + res = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[(endpointId, Clusters.NetworkCommissioning.Attributes.Networks)], returnClusterObject=True) + networkList = res[endpointId][Clusters.NetworkCommissioning].networks + logger.info(f"Got network list: {networkList}") + if len(networkList) != 1: + raise AssertionError( + f"Unexpected result: expect 1 networks, but {len(networkList)} networks received") + if networkList[0].networkID != TEST_WIFI_SSID.encode(): + raise AssertionError( + f"Unexpected result: first network ID should be 'TestSSID' got {networkList[0].networkID}") + if not networkList[0].connected: + raise AssertionError( + f"Unexpected result: network is not marked as connected") + + async def test_thread(self, endpointId): + logger.info(f"Get basic information of the endpoint") + res = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[ + (endpointId, + Clusters.NetworkCommissioning.Attributes.ConnectMaxTimeSeconds), + (endpointId, + Clusters.NetworkCommissioning.Attributes.ScanMaxTimeSeconds), + (endpointId, Clusters.NetworkCommissioning.Attributes.MaxNetworks), + (endpointId, Clusters.NetworkCommissioning.Attributes.FeatureMap)], + returnClusterObject=True) + self.log_interface_basic_info( + res[endpointId][Clusters.NetworkCommissioning]) + logger.info(f"Finished getting basic information of the endpoint") + + # Scan networks + logger.info(f"Scan networks") + req = Clusters.NetworkCommissioning.Commands.ScanNetworks( + ssid=b'', breadcrumb=0) + res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=endpointId, payload=req) + logger.info(f"Received response: {res}") + if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess: + raise AssertionError(f"Unexpected result: {res.networkingStatus}") + + # Remove existing network + logger.info(f"Check network list") + res = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[(endpointId, Clusters.NetworkCommissioning.Attributes.Networks)], returnClusterObject=True) + networkList = res[endpointId][Clusters.NetworkCommissioning].networks + logger.info(f"Got network list: {networkList}") + if len(networkList) != 0: + logger.info(f"Removing existing network") + req = Clusters.NetworkCommissioning.Commands.RemoveNetwork( + networkID=networkList[0].networkID, breadcrumb=0) + res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=endpointId, payload=req) + logger.info(f"Received response: {res}") + if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess: + raise AssertionError( + f"Unexpected result: {res.networkingStatus}") + + # Add first network + logger.info(f"Adding first test network") + req = Clusters.NetworkCommissioning.Commands.AddOrUpdateThreadNetwork( + operationalDataset=TEST_THREAD_NETWORK_DATASET_TLVS[0], breadcrumb=0) + res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=endpointId, payload=req) + logger.info(f"Received response: {res}") + if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess: + raise AssertionError(f"Unexpected result: {res.networkingStatus}") + + logger.info(f"Check network list") + res = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[(endpointId, Clusters.NetworkCommissioning.Attributes.Networks)], returnClusterObject=True) + networkList = res[endpointId][Clusters.NetworkCommissioning].networks + logger.info(f"Got network list: {networkList}") + if len(networkList) != 1: + raise AssertionError( + f"Unexpected result: expect 1 networks, but {len(networkList.Data.value)} networks received") + if networkList[0].networkID != TEST_THREAD_NETWORK_IDS[0]: + raise AssertionError( + f"Unexpected result: first network ID should be {TEST_THREAD_NETWORK_IDS[0]} got {networkList[0].networkID}") + + logger.info(f"Connect to a network") + req = Clusters.NetworkCommissioning.Commands.ConnectNetwork( + networkID=TEST_THREAD_NETWORK_IDS[0], breadcrumb=0) + res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=endpointId, payload=req) + logger.info(f"Got response: {res}") + if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess: + raise AssertionError(f"Unexpected result: {res.networkingStatus}") + logger.info(f"Device connected to a network.") + + # TODO: Linux Thread driver cannot get infomation of current connected networks. + ''' + logger.info(f"Check network list") + res = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[(endpointId, Clusters.NetworkCommissioning.Attributes.Networks)], returnClusterObject=True) + networkList = res[endpointId][Clusters.NetworkCommissioning].networks + logger.info(f"Got network list: {networkList}") + if len(networkList) != 1: + raise AssertionError( + f"Unexpected result: expect 1 networks, but {len(networkList.Data.value)} networks received") + if networkList[0].networkID != TEST_THREAD_NETWORK_IDS[0]: + raise AssertionError( + f"Unexpected result: first network ID should be {TEST_THREAD_NETWORK_IDS[0]} got {networkList[0].networkID}") + if not networkList[0].connected: + raise AssertionError( + f"Unexpected result: network is not marked as connected") + ''' + + async def run(self): + try: + endpoints = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[(Clusters.NetworkCommissioning.Attributes.FeatureMap)], returnClusterObject=True) + logger.info(endpoints) + for endpoint, obj in endpoints.items(): + clus = obj[Clusters.NetworkCommissioning] + if clus.featureMap == WIFI_NETWORK_FEATURE_MAP: + logger.info( + f"Endpoint {endpoint} is configured as WiFi network, run WiFi commissioning test.") + await self.test_wifi(endpoint) + elif clus.featureMap == THREAD_NETWORK_FEATURE_MAP: + logger.info( + f"Endpoint {endpoint} is configured as Thread network, run Thread commissioning test.") + await self.test_thread(endpoint) + else: + logger.info( + f"Skip endpoint {endpoint} with featureMap {clus.featureMap}") + except Exception as ex: + logger.exception(ex) + return False + return True diff --git a/src/darwin/Framework/CHIP/zap-generated/CHIPCallbackBridge.mm b/src/darwin/Framework/CHIP/zap-generated/CHIPCallbackBridge.mm index a34d7e3f39f3f8..335f3b7cb78b4f 100644 --- a/src/darwin/Framework/CHIP/zap-generated/CHIPCallbackBridge.mm +++ b/src/darwin/Framework/CHIP/zap-generated/CHIPCallbackBridge.mm @@ -5633,8 +5633,8 @@ newElement_0.security = [NSNumber numberWithUnsignedChar:entry_0.security]; newElement_0.ssid = [NSData dataWithBytes:entry_0.ssid.data() length:entry_0.ssid.size()]; newElement_0.bssid = [NSData dataWithBytes:entry_0.bssid.data() length:entry_0.bssid.size()]; - newElement_0.channel = [NSNumber numberWithUnsignedChar:entry_0.channel]; - newElement_0.wiFiBand = [NSNumber numberWithUnsignedInt:entry_0.wiFiBand]; + newElement_0.channel = [NSNumber numberWithUnsignedShort:entry_0.channel]; + newElement_0.wiFiBand = [NSNumber numberWithUnsignedChar:chip::to_underlying(entry_0.wiFiBand)]; newElement_0.rssi = [NSNumber numberWithChar:entry_0.rssi]; [array_0 addObject:newElement_0]; } @@ -7545,6 +7545,58 @@ } } +void CHIPNetworkCommissioningClusterWiFiBandAttributeCallbackBridge::OnSuccessFn( + void * context, chip::app::Clusters::NetworkCommissioning::WiFiBand value) +{ + NSNumber * _Nonnull objCValue; + objCValue = [NSNumber numberWithUnsignedChar:chip::to_underlying(value)]; + DispatchSuccess(context, objCValue); +}; + +void CHIPNetworkCommissioningClusterWiFiBandAttributeCallbackSubscriptionBridge::OnSubscriptionEstablished(void * context) +{ + auto * self = static_cast(context); + if (!self->mQueue) { + return; + } + + if (self->mEstablishedHandler != nil) { + dispatch_async(self->mQueue, self->mEstablishedHandler); + // On failure, mEstablishedHandler will be cleaned up by our destructor, + // but we can clean it up earlier on successful subscription + // establishment. + self->mEstablishedHandler = nil; + } +} + +void CHIPNullableNetworkCommissioningClusterWiFiBandAttributeCallbackBridge::OnSuccessFn( + void * context, const chip::app::DataModel::Nullable & value) +{ + NSNumber * _Nullable objCValue; + if (value.IsNull()) { + objCValue = nil; + } else { + objCValue = [NSNumber numberWithUnsignedChar:chip::to_underlying(value.Value())]; + } + DispatchSuccess(context, objCValue); +}; + +void CHIPNullableNetworkCommissioningClusterWiFiBandAttributeCallbackSubscriptionBridge::OnSubscriptionEstablished(void * context) +{ + auto * self = static_cast(context); + if (!self->mQueue) { + return; + } + + if (self->mEstablishedHandler != nil) { + dispatch_async(self->mQueue, self->mEstablishedHandler); + // On failure, mEstablishedHandler will be cleaned up by our destructor, + // but we can clean it up earlier on successful subscription + // establishment. + self->mEstablishedHandler = nil; + } +} + void CHIPDiagnosticLogsClusterLogsIntentAttributeCallbackBridge::OnSuccessFn( void * context, chip::app::Clusters::DiagnosticLogs::LogsIntent value) { diff --git a/src/darwin/Framework/CHIP/zap-generated/CHIPCallbackBridge_internal.h b/src/darwin/Framework/CHIP/zap-generated/CHIPCallbackBridge_internal.h index 653f8e0fa3bf2e..2e6b6c2402c76c 100644 --- a/src/darwin/Framework/CHIP/zap-generated/CHIPCallbackBridge_internal.h +++ b/src/darwin/Framework/CHIP/zap-generated/CHIPCallbackBridge_internal.h @@ -241,6 +241,9 @@ typedef void (*NetworkCommissioningClusterNetworkCommissioningStatusAttributeCal void *, chip::app::Clusters::NetworkCommissioning::NetworkCommissioningStatus); typedef void (*NullableNetworkCommissioningClusterNetworkCommissioningStatusAttributeCallback)( void *, const chip::app::DataModel::Nullable &); +typedef void (*NetworkCommissioningClusterWiFiBandAttributeCallback)(void *, chip::app::Clusters::NetworkCommissioning::WiFiBand); +typedef void (*NullableNetworkCommissioningClusterWiFiBandAttributeCallback)( + void *, const chip::app::DataModel::Nullable &); typedef void (*DiagnosticLogsClusterLogsIntentAttributeCallback)(void *, chip::app::Clusters::DiagnosticLogs::LogsIntent); typedef void (*NullableDiagnosticLogsClusterLogsIntentAttributeCallback)( void *, const chip::app::DataModel::Nullable &); @@ -6375,6 +6378,64 @@ class CHIPNullableNetworkCommissioningClusterNetworkCommissioningStatusAttribute SubscriptionEstablishedHandler mEstablishedHandler; }; +class CHIPNetworkCommissioningClusterWiFiBandAttributeCallbackBridge + : public CHIPCallbackBridge +{ +public: + CHIPNetworkCommissioningClusterWiFiBandAttributeCallbackBridge(dispatch_queue_t queue, ResponseHandler handler, + CHIPActionBlock action, bool keepAlive = false) : + CHIPCallbackBridge(queue, handler, action, OnSuccessFn, keepAlive){}; + + static void OnSuccessFn(void * context, chip::app::Clusters::NetworkCommissioning::WiFiBand value); +}; + +class CHIPNetworkCommissioningClusterWiFiBandAttributeCallbackSubscriptionBridge + : public CHIPNetworkCommissioningClusterWiFiBandAttributeCallbackBridge +{ +public: + CHIPNetworkCommissioningClusterWiFiBandAttributeCallbackSubscriptionBridge(dispatch_queue_t queue, ResponseHandler handler, + CHIPActionBlock action, + SubscriptionEstablishedHandler establishedHandler) : + CHIPNetworkCommissioningClusterWiFiBandAttributeCallbackBridge(queue, handler, action, true), + mEstablishedHandler(establishedHandler) + {} + + static void OnSubscriptionEstablished(void * context); + +private: + SubscriptionEstablishedHandler mEstablishedHandler; +}; + +class CHIPNullableNetworkCommissioningClusterWiFiBandAttributeCallbackBridge + : public CHIPCallbackBridge +{ +public: + CHIPNullableNetworkCommissioningClusterWiFiBandAttributeCallbackBridge(dispatch_queue_t queue, ResponseHandler handler, + CHIPActionBlock action, bool keepAlive = false) : + CHIPCallbackBridge(queue, handler, action, OnSuccessFn, + keepAlive){}; + + static void OnSuccessFn(void * context, + const chip::app::DataModel::Nullable & value); +}; + +class CHIPNullableNetworkCommissioningClusterWiFiBandAttributeCallbackSubscriptionBridge + : public CHIPNullableNetworkCommissioningClusterWiFiBandAttributeCallbackBridge +{ +public: + CHIPNullableNetworkCommissioningClusterWiFiBandAttributeCallbackSubscriptionBridge( + dispatch_queue_t queue, ResponseHandler handler, CHIPActionBlock action, + SubscriptionEstablishedHandler establishedHandler) : + CHIPNullableNetworkCommissioningClusterWiFiBandAttributeCallbackBridge(queue, handler, action, true), + mEstablishedHandler(establishedHandler) + {} + + static void OnSubscriptionEstablished(void * context); + +private: + SubscriptionEstablishedHandler mEstablishedHandler; +}; + class CHIPDiagnosticLogsClusterLogsIntentAttributeCallbackBridge : public CHIPCallbackBridge { diff --git a/src/include/platform/NetworkCommissioning.h b/src/include/platform/NetworkCommissioning.h new file mode 100644 index 00000000000000..9095ae3d6714df --- /dev/null +++ b/src/include/platform/NetworkCommissioning.h @@ -0,0 +1,297 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * This file defines the CHIP Device Network Provisioning object. + * + */ + +#pragma once + +#include +#include +#include +#include + +#include + +namespace chip { +namespace DeviceLayer { +/** + * We are using a namespace here, for most use cases, this namespace will be used by `using DeviceLayer::NetworkCommissioning`, but + * this still worth a dedicated namespace since: + * + * - The BaseDriver / WirelessDriver is not expected to be implemented directly by users, the only occurrence is in the network + * commissioning cluster. + * - We can safely name the drivers as WiFiDriver / ThreadDriver, it should not be ambiguous for most cases + * - We can safely name the Status enum to Status, and some other structs -- if we are using using, then we should in the context of + * writing something dedicated to network commissioning, then a single word Status should be clear enough. + */ +namespace NetworkCommissioning { + +constexpr size_t kMaxNetworkIDLen = 32; + +// TODO: This is exactly the same as the one in GroupDataProvider, this could be moved to src/lib/support +template +class Iterator +{ +public: + virtual ~Iterator() = default; + /** + * @retval The number of entries in total that will be iterated. + */ + virtual size_t Count() = 0; + /** + * @param[out] item Value associated with the next element in the iteration. + * @retval true if the next entry is successfully retrieved. + * @retval false if no more entries can be found. + */ + virtual bool Next(T & item) = 0; + /** + * Release the memory allocated by this iterator. + * Must be called before the pointer goes out of scope. + */ + virtual void Release() = 0; + +protected: + Iterator() = default; +}; + +/** + * The content should match the one in zap_generated/cluster-objects.h. + * Matching is validated by cluster code. + */ +enum class Status : uint8_t +{ + kSuccess = 0x00, + kOutOfRange = 0x01, + kBoundsExceeded = 0x02, + kNetworkIDNotFound = 0x03, + kDuplicateNetworkID = 0x04, + kNetworkNotFound = 0x05, + kRegulatoryError = 0x06, + kAuthFailure = 0x07, + kUnsupportedSecurity = 0x08, + kOtherConnectionFailure = 0x09, + kIPV6Failed = 0x0A, + kIPBindFailed = 0x0B, + kUnknownError = 0x0C, +}; + +enum class WiFiBand : uint8_t +{ + k2g4 = 0x00, + k3g65 = 0x01, + k5g = 0x02, + k6g = 0x03, + k60g = 0x04, +}; + +// The following structs follows the generated cluster object structs. +struct Network +{ + uint8_t networkID[kMaxNetworkIDLen]; + uint8_t networkIDLen; + bool connected; +}; + +static_assert(sizeof(Network::networkID) <= std::numeric_limits::max(), + "Max length of networkID ssid exceeds the limit of networkIDLen field"); + +struct WiFiScanResponse +{ +public: + uint8_t security; + uint8_t ssid[DeviceLayer::Internal::kMaxWiFiSSIDLength]; + uint8_t ssidLen; + uint8_t bssid[6]; + uint16_t channel; + WiFiBand wiFiBand; + int8_t rssi; +}; + +static_assert(sizeof(WiFiScanResponse::ssid) <= std::numeric_limits::max(), + "Max length of WiFi ssid exceeds the limit of ssidLen field"); + +struct ThreadScanResponse +{ + uint64_t panId; + uint64_t extendedPanId; + char networkName[16]; + uint8_t networkNameLen; + uint16_t channel; + uint8_t version; + uint64_t extendedAddress; + int8_t rssi; + uint8_t lqi; +}; + +static_assert(sizeof(ThreadScanResponse::networkName) <= std::numeric_limits::max(), + "Max length of WiFi credentials exceeds the limit of credentialsLen field"); + +using NetworkIterator = Iterator; +using WiFiScanResponseIterator = Iterator; +using ThreadScanResponseIterator = Iterator; + +// BaseDriver and WirelessDriver are the common interfaces for a network driver, platform drivers should not implement this +// directly, instead, users are expected to implement WiFiDriver, ThreadDriver and EthernetDriver. +namespace Internal { +class BaseDriver +{ +public: + /** + * @brief Initializes the driver, this function will be called when initializing the network commissioning cluster. + */ + virtual CHIP_ERROR Init() { return CHIP_NO_ERROR; } + + /** + * @brief Shuts down the driver, this function will be called when shutting down the network commissioning cluster. + */ + virtual CHIP_ERROR Shutdown() { return CHIP_NO_ERROR; } + + /** + * @brief Returns maximum number of network configs can be added to the driver. + */ + virtual uint8_t GetMaxNetworks() = 0; + + /** + * @brief Returns an iterator for reading the networks, the user will always call NetworkIterator::Release. The iterator should + * be consumed in the same context as calling GetNetworks(). Users must call Release() when the iterator goes out of scope. + */ + virtual NetworkIterator * GetNetworks() = 0; + + /** + * @brief Sets the status of the interface, this is an optional feature of a network driver. + */ + virtual CHIP_ERROR SetEnabled(bool enabled) { return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; } + + /** + * @brief Returns the status of the interface, this is an optional feature of a network driver the driver will be enabled by + * default. + */ + virtual bool GetEnabled() { return true; }; + + virtual ~BaseDriver() = default; +}; + +class WirelessDriver : public Internal::BaseDriver +{ +public: + class ConnectCallback + { + public: + virtual void OnResult(Status commissioningError, CharSpan debugText, int32_t connectStatus) = 0; + + virtual ~ConnectCallback() = default; + }; + + /** + * @brief Persists the network configurations. This function is expected to be called when CommissioningComplete event is fired. + */ + virtual CHIP_ERROR CommitConfiguration() = 0; + + /** + * @brief Reverts the network configurations to the last committed one. This function is expected to be called when failsafe + * timeout reached. + */ + virtual CHIP_ERROR RevertConfiguration() = 0; + + virtual uint8_t GetScanNetworkTimeoutSeconds() = 0; + virtual uint8_t GetConnectNetworkTimeoutSeconds() = 0; + + virtual Status RemoveNetwork(ByteSpan networkId) = 0; + virtual Status ReorderNetwork(ByteSpan networkId, uint8_t index) = 0; + + /** + * @brief Initializes a network join. callback->OnResult must be called, on both success and error. Callback can be + * called inside ConnectNetwork. + */ + virtual void ConnectNetwork(ByteSpan networkId, ConnectCallback * callback) = 0; +}; +} // namespace Internal + +class WiFiDriver : public Internal::WirelessDriver +{ +public: + class ScanCallback + { + public: + /** + * Indicates the scan is finished, and accepts a iterator of networks discovered. + * - networks can be nullptr when no networks discovered, or error occurred during scanning the networks. + * OnFinished() must be called in a thread-safe manner with CHIP stack. (e.g. using ScheduleWork or ScheduleLambda) + * - Users can assume the networks will always be used (and Release will be called) inside this function call. However, the + * iterator might be not fully consumed (i.e. There are too many networks scanned to fit in the buffer for scan response + * message.) + */ + virtual void OnFinished(Status status, CharSpan debugText, WiFiScanResponseIterator * networks) = 0; + + virtual ~ScanCallback() = default; + }; + + virtual Status AddOrUpdateNetwork(ByteSpan ssid, ByteSpan credentials) = 0; + + /** + * @brief Initializes a WiFi network scan. callback->OnFinished must be called, on both success and error. Callback can + * be called inside ScanNetworks. + * + * @param ssid The interested SSID, the scanning MAY be restricted to to the given SSID. + */ + virtual void ScanNetworks(ByteSpan ssid, ScanCallback * callback) = 0; + + virtual ~WiFiDriver() = default; +}; + +class ThreadDriver : public Internal::WirelessDriver +{ +public: + class ScanCallback + { + public: + /** + * Indicates the scan is finished, and accepts a iterator of networks discovered. + * - networks can be nullptr when no networks discovered, or error occurred during scanning the networks. + * OnFinished() must be called in a thread-safe manner with CHIP stack. (e.g. using ScheduleWork or ScheduleLambda) + * - Users can assume the networks will always be used (and Release will be called) inside this function call. However, the + * iterator might be not fully consumed (i.e. There are too many networks scanned to fit in the buffer for scan response + * message.) + */ + virtual void OnFinished(Status err, CharSpan debugText, ThreadScanResponseIterator * networks) = 0; + + virtual ~ScanCallback() = default; + }; + + virtual Status AddOrUpdateNetwork(ByteSpan operationalDataset) = 0; + + /** + * @brief Initializes a Thread network scan. callback->OnFinished must be called, on both success and error. Callback can + * be called inside ScanNetworks. + */ + virtual void ScanNetworks(ScanCallback * callback) = 0; + + virtual ~ThreadDriver() = default; +}; + +class EthernetDriver : public Internal::BaseDriver +{ + // Ethernet driver does not have any special operations. +}; + +} // namespace NetworkCommissioning +} // namespace DeviceLayer +} // namespace chip diff --git a/src/include/platform/ThreadStackManager.h b/src/include/platform/ThreadStackManager.h index b9b3c49145018a..f9db5e3df3c6e7 100644 --- a/src/include/platform/ThreadStackManager.h +++ b/src/include/platform/ThreadStackManager.h @@ -88,6 +88,7 @@ class ThreadStackManager bool TryLockThreadStack(); void UnlockThreadStack(); bool HaveRouteToAddress(const chip::Inet::IPAddress & destAddr); + CHIP_ERROR GetThreadProvision(ByteSpan & netInfo); CHIP_ERROR GetAndLogThreadStatsCounters(); CHIP_ERROR GetAndLogThreadTopologyMinimal(); CHIP_ERROR GetAndLogThreadTopologyFull(); @@ -147,7 +148,6 @@ class ThreadStackManager bool IsThreadEnabled(); bool IsThreadProvisioned(); bool IsThreadAttached(); - CHIP_ERROR GetThreadProvision(ByteSpan & netInfo); void ErasePersistentInfo(); ConnectivityManager::ThreadDeviceType GetThreadDeviceType(); CHIP_ERROR SetThreadDeviceType(ConnectivityManager::ThreadDeviceType threadRole); diff --git a/src/include/platform/internal/DeviceNetworkInfo.h b/src/include/platform/internal/DeviceNetworkInfo.h index 6f47c19d4d113f..7e218914066e76 100644 --- a/src/include/platform/internal/DeviceNetworkInfo.h +++ b/src/include/platform/internal/DeviceNetworkInfo.h @@ -32,6 +32,7 @@ namespace Internal { // ---- WiFi-specific Limits ---- constexpr size_t kMaxWiFiSSIDLength = 32; constexpr size_t kMaxWiFiKeyLength = 64; +constexpr size_t kWiFiBSSIDLength = 6; /** * Ids for well-known network provision types. diff --git a/src/platform/Linux/BUILD.gn b/src/platform/Linux/BUILD.gn index 8803940a7b5d21..f5bdc54851a6e8 100644 --- a/src/platform/Linux/BUILD.gn +++ b/src/platform/Linux/BUILD.gn @@ -61,6 +61,9 @@ static_library("Linux") { "KeyValueStoreManagerImpl.cpp", "KeyValueStoreManagerImpl.h", "Logging.cpp", + "NetworkCommissioningDriver.h", + "NetworkCommissioningThreadDriver.cpp", + "NetworkCommissioningWiFiDriver.cpp", "PlatformManagerImpl.cpp", "PlatformManagerImpl.h", "PosixConfig.cpp", @@ -81,6 +84,7 @@ static_library("Linux") { deps = [ "${chip_root}/src/setup_payload" ] public_deps = [ + "${chip_root}/src/app/common:cluster-objects", "${chip_root}/src/platform:platform_base", "${chip_root}/third_party/inipp", ] diff --git a/src/platform/Linux/ConnectivityManagerImpl.cpp b/src/platform/Linux/ConnectivityManagerImpl.cpp index 8cc3fc1b9f933f..1496f04dad35e1 100644 --- a/src/platform/Linux/ConnectivityManagerImpl.cpp +++ b/src/platform/Linux/ConnectivityManagerImpl.cpp @@ -22,11 +22,15 @@ #include #include #include +#include #include #include #include #include +#include +#include +#include #include #include @@ -60,6 +64,8 @@ using namespace ::chip::DeviceLayer::Internal; using namespace ::chip::app::Clusters::GeneralDiagnostics; using namespace ::chip::app::Clusters::WiFiNetworkDiagnostics; +using namespace ::chip::DeviceLayer::NetworkCommissioning; + namespace chip { namespace DeviceLayer { @@ -69,12 +75,17 @@ ConnectivityManagerImpl ConnectivityManagerImpl::sInstance; char ConnectivityManagerImpl::sWiFiIfName[]; #endif +WiFiDriver::ScanCallback * ConnectivityManagerImpl::mpScanCallback; +NetworkCommissioning::Internal::WirelessDriver::ConnectCallback * ConnectivityManagerImpl::mpConnectCallback; + CHIP_ERROR ConnectivityManagerImpl::_Init() { #if CHIP_DEVICE_CONFIG_ENABLE_WPA mWiFiStationMode = kWiFiStationMode_Disabled; mWiFiStationReconnectInterval = System::Clock::Milliseconds32(CHIP_DEVICE_CONFIG_WIFI_STATION_RECONNECT_INTERVAL); #endif + mpConnectCallback = nullptr; + mpScanCallback = nullptr; if (ConnectivityUtils::GetEthInterfaceName(mEthIfName, IFNAMSIZ) == CHIP_NO_ERROR) { @@ -427,6 +438,7 @@ void ConnectivityManagerImpl::_OnWpaInterfaceProxyReady(GObject * source_object, ChipLogProgress(DeviceLayer, "wpa_supplicant: connected to wpa_supplicant interface proxy"); g_signal_connect(mWpaSupplicant.iface, "properties-changed", G_CALLBACK(_OnWpaPropertiesChanged), NULL); + g_signal_connect(mWpaSupplicant.iface, "scan-done", G_CALLBACK(_OnWpaInterfaceScanDone), NULL); } else { @@ -632,6 +644,17 @@ void ConnectivityManagerImpl::_OnWpaProxyReady(GObject * source_object, GAsyncRe mWpaSupplicant.state = GDBusWpaSupplicant::WPA_NOT_CONNECTED; } + // We need to stop auto scan or it will block our network scan. + DeviceLayer::SystemLayer().ScheduleLambda([]() { + std::lock_guard innerLock(mWpaSupplicantMutex); + ChipLogDetail(DeviceLayer, "Disabling auto scan"); + CHIP_ERROR errInner = StopAutoScan(); + if (errInner != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "Failed to stop auto scan"); + } + }); + if (err != nullptr) g_error_free(err); } @@ -864,6 +887,126 @@ void ConnectivityManagerImpl::DriveAPState(::chip::System::Layer * aLayer, void sInstance.DriveAPState(); } +CHIP_ERROR +ConnectivityManagerImpl::ConnectWiFiNetworkAsync(ByteSpan ssid, ByteSpan credentials, + NetworkCommissioning::Internal::WirelessDriver::ConnectCallback * apCallback) +{ + CHIP_ERROR ret = CHIP_NO_ERROR; + GError * err = nullptr; + GVariant * args = nullptr; + GVariantBuilder builder; + gboolean result; + char ssidStr[kMaxWiFiSSIDLength] = { 0 }; + char keyStr[kMaxWiFiKeyLength] = { 0 }; + // There is another ongoing connect request, reject the new one. + VerifyOrReturnError(mpConnectCallback == nullptr, CHIP_ERROR_INCORRECT_STATE); + + // Clean up current network if exists + if (mWpaSupplicant.networkPath) + { + GError * error = nullptr; + + result = wpa_fi_w1_wpa_supplicant1_interface_call_remove_network_sync(mWpaSupplicant.iface, mWpaSupplicant.networkPath, + nullptr, &error); + + if (result) + { + ChipLogProgress(DeviceLayer, "wpa_supplicant: removed network: %s", mWpaSupplicant.networkPath); + g_free(mWpaSupplicant.networkPath); + mWpaSupplicant.networkPath = nullptr; + } + else + { + ChipLogProgress(DeviceLayer, "wpa_supplicant: failed to stop AP mode with error: %s", + error ? error->message : "unknown error"); + ret = CHIP_ERROR_INTERNAL; + } + + if (error != nullptr) + g_error_free(error); + + SuccessOrExit(ret); + } + + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + memcpy(ssidStr, ssid.data(), ssid.size()); + memcpy(keyStr, credentials.data(), credentials.size()); + g_variant_builder_add(&builder, "{sv}", "ssid", g_variant_new_string(ssidStr)); + g_variant_builder_add(&builder, "{sv}", "psk", g_variant_new_string(keyStr)); + g_variant_builder_add(&builder, "{sv}", "key_mgmt", g_variant_new_string("WPA-PSK")); + args = g_variant_builder_end(&builder); + + result = wpa_fi_w1_wpa_supplicant1_interface_call_add_network_sync(mWpaSupplicant.iface, args, &mWpaSupplicant.networkPath, + nullptr, &err); + + if (result) + { + // Note: wpa_supplicant will return immediately if the network is already connected, but it will still try reconnect in the + // background. The client still need to wait for a few seconds for this reconnect operation. So we always disconnect from + // the network we are connected and ignore any errors. + wpa_fi_w1_wpa_supplicant1_interface_call_disconnect_sync(mWpaSupplicant.iface, nullptr, nullptr); + ChipLogProgress(DeviceLayer, "wpa_supplicant: added network: %s", mWpaSupplicant.networkPath); + + wpa_fi_w1_wpa_supplicant1_interface_call_select_network(mWpaSupplicant.iface, mWpaSupplicant.networkPath, nullptr, + _ConnectWiFiNetworkAsyncCallback, this); + mpConnectCallback = apCallback; + } + else + { + ChipLogProgress(DeviceLayer, "wpa_supplicant: failed to add network: %s", err ? err->message : "unknown error"); + + if (mWpaSupplicant.networkPath) + { + g_object_unref(mWpaSupplicant.networkPath); + mWpaSupplicant.networkPath = nullptr; + } + + ret = CHIP_ERROR_INTERNAL; + } + +exit: + if (err != nullptr) + g_error_free(err); + + return ret; +} + +void ConnectivityManagerImpl::_ConnectWiFiNetworkAsyncCallback(GObject * source_object, GAsyncResult * res, gpointer user_data) +{ + ConnectivityManagerImpl * this_ = reinterpret_cast(user_data); + std::unique_ptr attachRes; + std::unique_ptr err; + { + gboolean result = wpa_fi_w1_wpa_supplicant1_interface_call_select_network_finish(mWpaSupplicant.iface, res, + &MakeUniquePointerReceiver(err).Get()); + if (!result) + { + ChipLogError(DeviceLayer, "Failed to perform connect network: %s", err == nullptr ? "unknown error" : err->message); + DeviceLayer::SystemLayer().ScheduleLambda([this_]() { + if (mpConnectCallback != nullptr) + { + // TODO: Replace this with actual thread attach result. + this_->mpConnectCallback->OnResult(NetworkCommissioning::Status::kUnknownError, CharSpan(), 0); + this_->mpConnectCallback = nullptr; + } + mpConnectCallback = nullptr; + }); + } + else + { + DeviceLayer::SystemLayer().ScheduleLambda([this_]() { + if (this_->mpConnectCallback != nullptr) + { + // TODO: Replace this with actual thread attach result. + this_->mpConnectCallback->OnResult(NetworkCommissioning::Status::kSuccess, CharSpan(), 0); + this_->mpConnectCallback = nullptr; + } + this_->PostNetworkConnect(); + }); + } + } +} + CHIP_ERROR ConnectivityManagerImpl::ProvisionWiFiNetwork(const char * ssid, const char * key) { CHIP_ERROR ret = CHIP_NO_ERROR; @@ -937,45 +1080,7 @@ CHIP_ERROR ConnectivityManagerImpl::ProvisionWiFiNetwork(const char * ssid, cons if (gerror != nullptr) g_error_free(gerror); - // Iterate on the network interface to see if we already have beed assigned addresses. - // The temporary hack for getting IP address change on linux for network provisioning in the rendezvous session. - // This should be removed or find a better place once we depercate the rendezvous session. - for (chip::Inet::InterfaceAddressIterator it; it.HasCurrent(); it.Next()) - { - char ifName[chip::Inet::InterfaceId::kMaxIfNameLength]; - if (it.IsUp() && CHIP_NO_ERROR == it.GetInterfaceName(ifName, sizeof(ifName)) && - strncmp(ifName, sWiFiIfName, sizeof(ifName)) == 0) - { - chip::Inet::IPAddress addr; - if ((it.GetAddress(addr) == CHIP_NO_ERROR) && addr.IsIPv4()) - { - ChipDeviceEvent event; - event.Type = DeviceEventType::kInternetConnectivityChange; - event.InternetConnectivityChange.IPv4 = kConnectivity_Established; - event.InternetConnectivityChange.IPv6 = kConnectivity_NoChange; - addr.ToString(event.InternetConnectivityChange.address); - - ChipLogDetail(DeviceLayer, "Got IP address on interface: %s IP: %s", ifName, - event.InternetConnectivityChange.address); - - PlatformMgr().PostEventOrDie(&event); - } - } - } - - // Run dhclient for IP on WiFi. - // TODO: The wifi can be managed by networkmanager on linux so we don't have to care about this. - char cmdBuffer[128]; - sprintf(cmdBuffer, CHIP_DEVICE_CONFIG_LINUX_DHCPC_CMD, sWiFiIfName); - int dhclientSystemRet = system(cmdBuffer); - if (dhclientSystemRet != 0) - { - ChipLogError(DeviceLayer, "Failed to run dhclient, system() returns %d", dhclientSystemRet); - } - else - { - ChipLogProgress(DeviceLayer, "dhclient is running on the %s interface.", sWiFiIfName); - } + PostNetworkConnect(); // Return success as long as the device is connected to the network ret = CHIP_NO_ERROR; @@ -1011,6 +1116,69 @@ CHIP_ERROR ConnectivityManagerImpl::ProvisionWiFiNetwork(const char * ssid, cons return ret; } +void ConnectivityManagerImpl::PostNetworkConnect() +{ + // Iterate on the network interface to see if we already have beed assigned addresses. + // The temporary hack for getting IP address change on linux for network provisioning in the rendezvous session. + // This should be removed or find a better place once we depercate the rendezvous session. + for (chip::Inet::InterfaceAddressIterator it; it.HasCurrent(); it.Next()) + { + char ifName[chip::Inet::InterfaceId::kMaxIfNameLength]; + if (it.IsUp() && CHIP_NO_ERROR == it.GetInterfaceName(ifName, sizeof(ifName)) && + strncmp(ifName, sWiFiIfName, sizeof(ifName)) == 0) + { + chip::Inet::IPAddress addr; + if ((it.GetAddress(addr) == CHIP_NO_ERROR) && addr.IsIPv4()) + { + ChipDeviceEvent event; + event.Type = DeviceEventType::kInternetConnectivityChange; + event.InternetConnectivityChange.IPv4 = kConnectivity_Established; + event.InternetConnectivityChange.IPv6 = kConnectivity_NoChange; + addr.ToString(event.InternetConnectivityChange.address); + + ChipLogDetail(DeviceLayer, "Got IP address on interface: %s IP: %s", ifName, + event.InternetConnectivityChange.address); + + PlatformMgr().PostEventOrDie(&event); + } + } + } + + // Run dhclient for IP on WiFi. + // TODO: The wifi can be managed by networkmanager on linux so we don't have to care about this. + char cmdBuffer[128]; + sprintf(cmdBuffer, CHIP_DEVICE_CONFIG_LINUX_DHCPC_CMD, sWiFiIfName); + int dhclientSystemRet = system(cmdBuffer); + if (dhclientSystemRet != 0) + { + ChipLogError(DeviceLayer, "Failed to run dhclient, system() returns %d", dhclientSystemRet); + } + else + { + ChipLogProgress(DeviceLayer, "dhclient is running on the %s interface.", sWiFiIfName); + } +} + +CHIP_ERROR ConnectivityManagerImpl::CommitConfig() +{ + gboolean result; + std::unique_ptr err; + + ChipLogProgress(DeviceLayer, "wpa_supplicant: connected to network"); + + result = wpa_fi_w1_wpa_supplicant1_interface_call_save_config_sync(mWpaSupplicant.iface, nullptr, + &MakeUniquePointerReceiver(err).Get()); + + if (!result) + { + ChipLogProgress(DeviceLayer, "wpa_supplicant: failed to save config: %s", err ? err->message : "unknown error"); + return CHIP_ERROR_INTERNAL; + } + + ChipLogProgress(DeviceLayer, "wpa_supplicant: save config succeeded!"); + return CHIP_NO_ERROR; +} + CHIP_ERROR ConnectivityManagerImpl::GetWiFiBssId(ByteSpan & value) { CHIP_ERROR err = CHIP_ERROR_READ_FAILED; @@ -1110,6 +1278,368 @@ CHIP_ERROR ConnectivityManagerImpl::GetWiFiVersion(uint8_t & wiFiVersion) return CHIP_NO_ERROR; } +CHIP_ERROR ConnectivityManagerImpl::GetConnectedNetwork(NetworkCommissioning::Network & network) +{ + std::lock_guard lock(mWpaSupplicantMutex); + std::unique_ptr err; + + const gchar * networkPath = wpa_fi_w1_wpa_supplicant1_interface_get_current_network(mWpaSupplicant.iface); + + std::unique_ptr networkInfo( + wpa_fi_w1_wpa_supplicant1_network_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, + kWpaSupplicantServiceName, networkPath, nullptr, + &MakeUniquePointerReceiver(err).Get())); + if (networkInfo == nullptr) + { + return CHIP_ERROR_INTERNAL; + } + + network.connected = wpa_fi_w1_wpa_supplicant1_network_get_enabled(networkInfo.get()); + GVariant * properties = wpa_fi_w1_wpa_supplicant1_network_get_properties(networkInfo.get()); + GVariant * ssid = g_variant_lookup_value(properties, "ssid", nullptr); + gsize length; + const gchar * ssidStr = g_variant_get_string(ssid, &length); + if (length > sizeof(network.networkID)) + { + return CHIP_ERROR_INTERNAL; + } + // TODO: wpa_supplicant will return ssid with quotes! We should have a better way to get the actual ssid in bytes. + ChipLogDetail(DeviceLayer, "Current connected network: %s", ssidStr); + memcpy(network.networkID, ssidStr + 1, length - 2); + network.networkIDLen = length - 2; + return CHIP_NO_ERROR; +} + +CHIP_ERROR ConnectivityManagerImpl::StopAutoScan() +{ + std::unique_ptr err; + + gboolean result; + result = wpa_fi_w1_wpa_supplicant1_interface_call_auto_scan_sync( + mWpaSupplicant.iface, "" /* empty string means disabling auto scan */, nullptr, &MakeUniquePointerReceiver(err).Get()); + if (!result) + { + ChipLogError(DeviceLayer, "wpa_supplicant: Failed to stop auto network scan: %s", err ? err->message : "unknown"); + return CHIP_ERROR_INTERNAL; + } + return CHIP_NO_ERROR; +} + +CHIP_ERROR ConnectivityManagerImpl::StartWiFiScan(ByteSpan ssid, WiFiDriver::ScanCallback * callback) +{ + std::lock_guard lock(mWpaSupplicantMutex); + VerifyOrReturnError(mWpaSupplicant.iface != nullptr, CHIP_ERROR_INCORRECT_STATE); + // There is another ongoing scan request, reject the new one. + VerifyOrReturnError(mpScanCallback == nullptr, CHIP_ERROR_INCORRECT_STATE); + + CHIP_ERROR ret = CHIP_NO_ERROR; + GError * err = nullptr; + GVariant * args = nullptr; + GVariantBuilder builder; + gboolean result; + + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add(&builder, "{sv}", "Type", g_variant_new_string("active")); + args = g_variant_builder_end(&builder); + + result = wpa_fi_w1_wpa_supplicant1_interface_call_scan_sync(mWpaSupplicant.iface, args, nullptr, &err); + + if (result) + { + ChipLogProgress(DeviceLayer, "wpa_supplicant: initialized network scan."); + mpScanCallback = callback; + } + else + { + ChipLogProgress(DeviceLayer, "wpa_supplicant: failed to start network scan: %s", err ? err->message : "unknown error"); + ret = CHIP_ERROR_INTERNAL; + } + + if (err != nullptr) + { + g_error_free(err); + } + return ret; +} + +namespace { +// wpa_supplicant's scan results don't contains the channel infomation, so we need this lookup table for resolving the band and +// channel infomation. +std::pair GetBandAndChannelFromFrequency(uint32_t freq) +{ + std::pair ret = std::make_pair(WiFiBand::k2g4, 0); + if (freq <= 2472) + { + ret.second = static_cast((freq - 2412) / 5 + 1); + } + else if (freq == 2484) + { + ret.second = 14; + } + else if (freq >= 3600 && freq <= 3700) + { + // Note: There are not many devices supports this band, and this band contains rational frequency in MHz, need to figure out + // the behavior of wpa_supplicant in this case. + ret.first = WiFiBand::k3g65; + } + else if (freq >= 5035 && freq <= 5945) + { + ret.first = WiFiBand::k5g; + ret.second = static_cast((freq - 5000) / 5); + } + else if (freq == 5960 || freq == 5980) + { + ret.first = WiFiBand::k5g; + ret.second = static_cast((freq - 5000) / 5); + } + else if (freq >= 5955) + { + ret.first = WiFiBand::k6g; + ret.second = static_cast((freq - 5950) / 5); + } + else if (freq >= 58000) + { + ret.first = WiFiBand::k60g; + // Note: Some channel has the same center frequency but different bandwidth. Should figure out wpa_supplicant's behavior in + // this case. Also, wpa_supplicant's frequency property is uint16 infact. + switch (freq) + { + case 58'320: + ret.second = 1; + break; + case 60'480: + ret.second = 2; + break; + case 62'640: + ret.second = 3; + break; + case 64'800: + ret.second = 4; + break; + case 66'960: + ret.second = 5; + break; + case 69'120: + ret.second = 6; + break; + case 59'400: + ret.second = 9; + break; + case 61'560: + ret.second = 10; + break; + case 63'720: + ret.second = 11; + break; + case 65'880: + ret.second = 12; + break; + case 68'040: + ret.second = 13; + break; + } + } + return ret; +} +} // namespace + +bool ConnectivityManagerImpl::_GetBssInfo(const gchar * bssPath, NetworkCommissioning::WiFiScanResponse & result) +{ + std::unique_ptr err; + std::unique_ptr bss( + wpa_fi_w1_wpa_supplicant1_bss_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, kWpaSupplicantServiceName, + bssPath, nullptr, &MakeUniquePointerReceiver(err).Get())); + + if (bss == nullptr) + { + return false; + } + + WpaFiW1Wpa_supplicant1BSSProxy * bssProxy = WPA_FI_W1_WPA_SUPPLICANT1_BSS_PROXY(bss.get()); + + std::unique_ptr ssid(g_dbus_proxy_get_cached_property(G_DBUS_PROXY(bssProxy), "SSID")); + std::unique_ptr bssid(g_dbus_proxy_get_cached_property(G_DBUS_PROXY(bssProxy), "BSSID")); + + const guchar * ssidStr = nullptr; + const guchar * bssidBuf = nullptr; + char bssidStr[2 * 6 + 5 + 1] = { 0 }; + gsize ssidLen = 0; + gsize bssidLen = 0; + gint16 signal = wpa_fi_w1_wpa_supplicant1_bss_get_signal(bss.get()); + guint16 frequency = wpa_fi_w1_wpa_supplicant1_bss_get_frequency(bss.get()); + + ssidStr = reinterpret_cast(g_variant_get_fixed_array(ssid.get(), &ssidLen, sizeof(guchar))); + bssidBuf = reinterpret_cast(g_variant_get_fixed_array(bssid.get(), &bssidLen, sizeof(guchar))); + + if (bssidLen == 6) + { + snprintf(bssidStr, sizeof(bssidStr), "%02x:%02x:%02x:%02x:%02x:%02x", bssidBuf[0], bssidBuf[1], bssidBuf[2], bssidBuf[3], + bssidBuf[4], bssidBuf[5]); + } + else + { + bssidLen = 0; + ChipLogError(DeviceLayer, "Got a network with bssid not equals to 6"); + } + ChipLogDetail(DeviceLayer, "Network Found: %.*s (%s) Signal:%" PRId16, int(ssidLen), ssidStr, bssidStr, signal); + + // A flag for enterprise encryption option to avoid returning open for these networks by mistake + // TODO: The following code will mistakenly recognize WEP encryption as OPEN network, this should be fixed by reading + // IEs (information elements) field instead of reading cooked data. + + static constexpr uint8_t kEAP = (1 << 7); + + auto IsNetworkWPAPSK = [](GVariant * wpa) -> uint8_t { + if (wpa == nullptr) + { + return 0; + } + + GVariant * keyMgmt = g_variant_lookup_value(wpa, "KeyMgmt", nullptr); + if (keyMgmt == nullptr) + { + return 0; + } + const gchar ** keyMgmts = g_variant_get_strv(keyMgmt, nullptr); + const gchar ** keyMgmtsForFree = keyMgmts; + uint8_t res = 0; + for (const gchar * keyMgmtVal = (keyMgmts != nullptr ? *keyMgmts : nullptr); keyMgmtVal != nullptr; + keyMgmtVal = *(++keyMgmts)) + { + if (g_strcasecmp(keyMgmtVal, "wpa-psk") == 0 || g_strcasecmp(keyMgmtVal, "wpa-none") == 0) + { + res |= (1 << 2); // SecurityType::WPA_PERSONAL + } + else if (g_strcasecmp(keyMgmtVal, "wpa-eap")) + { + res |= (kEAP); + } + } + g_variant_unref(keyMgmt); + g_free(keyMgmtsForFree); + return res; + }; + auto IsNetworkWPA2PSK = [](GVariant * rsn) -> uint8_t { + if (rsn == nullptr) + { + return 0; + } + GVariant * keyMgmt = g_variant_lookup_value(rsn, "KeyMgmt", nullptr); + if (keyMgmt == nullptr) + { + return 0; + } + const gchar ** keyMgmts = g_variant_get_strv(keyMgmt, nullptr); + const gchar ** keyMgmtsForFree = keyMgmts; + uint8_t res = 0; + for (const gchar * keyMgmtVal = (keyMgmts != nullptr ? *keyMgmts : nullptr); keyMgmtVal != nullptr; + keyMgmtVal = *(++keyMgmts)) + { + if (g_strcasecmp(keyMgmtVal, "wpa-psk") == 0 || g_strcasecmp(keyMgmtVal, "wpa-psk-sha256") == 0 || + g_strcasecmp(keyMgmtVal, "wpa-ft-psk") == 0) + { + res |= (1 << 3); // SecurityType::WPA2_PERSONAL + } + else if (g_strcasecmp(keyMgmtVal, "wpa-eap") == 0 || g_strcasecmp(keyMgmtVal, "wpa-eap-sha256") == 0 || + g_strcasecmp(keyMgmtVal, "wpa-ft-eap") == 0) + { + res |= kEAP; + } + else if (g_strcasecmp(keyMgmtVal, "sae") == 0) + { + // wpa_supplicant will include "sae" in KeyMgmt field for WPA3 WiFi, this is not included in the wpa_supplicant + // document. + res |= (1 << 4); // SecurityType::WPA3_PERSONAL + } + } + g_variant_unref(keyMgmt); + g_free(keyMgmtsForFree); + return res; + }; + auto GetNetworkSecurityType = [IsNetworkWPAPSK, IsNetworkWPA2PSK](WpaFiW1Wpa_supplicant1BSSProxy * proxy) -> uint8_t { + std::unique_ptr wpa(g_dbus_proxy_get_cached_property(G_DBUS_PROXY(proxy), "WPA")); + std::unique_ptr rsn(g_dbus_proxy_get_cached_property(G_DBUS_PROXY(proxy), "RSN")); + + uint8_t res = IsNetworkWPAPSK(wpa.get()) | IsNetworkWPA2PSK(rsn.get()); + if (res == 0) + { + res = 1; // Open + } + return res & (0x7F); + }; + + // Drop the network if its SSID or BSSID is illegal. + VerifyOrReturnError(ssidLen <= kMaxWiFiSSIDLength, false); + VerifyOrReturnError(bssidLen == kWiFiBSSIDLength, false); + memcpy(result.ssid, ssidStr, ssidLen); + memcpy(result.bssid, bssidBuf, bssidLen); + result.ssidLen = ssidLen; + if (signal < INT8_MIN) + { + result.rssi = INT8_MIN; + } + else if (signal > INT8_MAX) + { + result.rssi = INT8_MAX; + } + else + { + result.rssi = static_cast(signal); + } + + auto bandInfo = GetBandAndChannelFromFrequency(frequency); + result.wiFiBand = bandInfo.first; + result.channel = bandInfo.second; + result.security = GetNetworkSecurityType(bssProxy); + + return true; +} + +void ConnectivityManagerImpl::_OnWpaInterfaceScanDone(GObject * source_object, GAsyncResult * res, gpointer user_data) +{ + ChipLogProgress(DeviceLayer, "wpa_supplicant: network scan done"); + gchar ** bsss = wpa_fi_w1_wpa_supplicant1_interface_dup_bsss(mWpaSupplicant.iface); + gchar ** oldBsss = bsss; + if (bsss == nullptr) + { + ChipLogProgress(DeviceLayer, "wpa_supplicant: no network found"); + DeviceLayer::SystemLayer().ScheduleLambda([]() { + if (mpScanCallback != nullptr) + { + mpScanCallback->OnFinished(Status::kSuccess, CharSpan(), nullptr); + mpScanCallback = nullptr; + } + }); + return; + } + + std::vector * networkScanned = new std::vector(); + for (const gchar * bssPath = (bsss != nullptr ? *bsss : nullptr); bssPath != nullptr; bssPath = *(++bsss)) + { + WiFiScanResponse network; + if (_GetBssInfo(bssPath, network)) + { + networkScanned->push_back(network); + } + } + + DeviceLayer::SystemLayer().ScheduleLambda([networkScanned]() { + // Note: We cannot post a event in ScheduleLambda since std::vector is not trivial copiable. This results in the use of + // const_cast but should be fine for almost all cases, since we actually handled the ownership of this element to this + // lambda. + if (mpScanCallback != nullptr) + { + LinuxScanResponseIterator iter(const_cast *>(networkScanned)); + mpScanCallback->OnFinished(Status::kSuccess, CharSpan(), &iter); + mpScanCallback = nullptr; + } + + delete const_cast *>(networkScanned); + }); + + g_strfreev(oldBsss); +} + #endif // CHIP_DEVICE_CONFIG_ENABLE_WPA } // namespace DeviceLayer diff --git a/src/platform/Linux/ConnectivityManagerImpl.h b/src/platform/Linux/ConnectivityManagerImpl.h index 39b7aec2db2467..555aa2ae28802e 100644 --- a/src/platform/Linux/ConnectivityManagerImpl.h +++ b/src/platform/Linux/ConnectivityManagerImpl.h @@ -45,6 +45,10 @@ #include #endif +#include +#include +#include + namespace chip { namespace Inet { class IPAddress; @@ -110,11 +114,19 @@ class ConnectivityManagerImpl final : public ConnectivityManager, public: #if CHIP_DEVICE_CONFIG_ENABLE_WPA CHIP_ERROR ProvisionWiFiNetwork(const char * ssid, const char * key); + CHIP_ERROR ConnectWiFiNetworkAsync(ByteSpan ssid, ByteSpan credentials, + NetworkCommissioning::Internal::WirelessDriver::ConnectCallback * connectCallback); + void PostNetworkConnect(); + static void _ConnectWiFiNetworkAsyncCallback(GObject * source_object, GAsyncResult * res, gpointer user_data); + CHIP_ERROR CommitConfig(); + void StartWiFiManagement(); bool IsWiFiManagementStarted(); CHIP_ERROR GetWiFiBssId(ByteSpan & value); CHIP_ERROR GetWiFiSecurityType(uint8_t & securityType); CHIP_ERROR GetWiFiVersion(uint8_t & wiFiVersion); + CHIP_ERROR GetConnectedNetwork(NetworkCommissioning::Network & network); + CHIP_ERROR StartWiFiScan(ByteSpan ssid, NetworkCommissioning::WiFiDriver::ScanCallback * callback); #endif const char * GetEthernetIfName() { return (mEthIfName[0] == '\0') ? nullptr : mEthIfName; } @@ -126,6 +138,18 @@ class ConnectivityManagerImpl final : public ConnectivityManager, private: // ===== Members that implement the ConnectivityManager abstract interface. + struct WiFiNetworkScanned + { + // The fields matches WiFiInterfaceScanResult::Type. + uint8_t ssid[Internal::kMaxWiFiSSIDLength]; + uint8_t ssidLen; + uint8_t bssid[6]; + int8_t rssi; + uint16_t frequencyBand; + uint8_t channel; + uint8_t security; + }; + CHIP_ERROR _Init(); void _OnPlatformEvent(const ChipDeviceEvent * event); @@ -150,6 +174,7 @@ class ConnectivityManagerImpl final : public ConnectivityManager, void _MaintainOnDemandWiFiAP(); System::Clock::Timeout _GetWiFiAPIdleTimeout(); void _SetWiFiAPIdleTimeout(System::Clock::Timeout val); + static CHIP_ERROR StopAutoScan(); static void _OnWpaProxyReady(GObject * source_object, GAsyncResult * res, gpointer user_data); static void _OnWpaInterfaceRemoved(WpaFiW1Wpa_supplicant1 * proxy, const gchar * path, GVariant * properties, @@ -160,6 +185,9 @@ class ConnectivityManagerImpl final : public ConnectivityManager, static void _OnWpaInterfaceReady(GObject * source_object, GAsyncResult * res, gpointer user_data); static void _OnWpaInterfaceProxyReady(GObject * source_object, GAsyncResult * res, gpointer user_data); static void _OnWpaBssProxyReady(GObject * source_object, GAsyncResult * res, gpointer user_data); + static void _OnWpaInterfaceScanDone(GObject * source_object, GAsyncResult * res, gpointer user_data); + + static bool _GetBssInfo(const gchar * bssPath, NetworkCommissioning::WiFiScanResponse & result); static bool mAssociattionStarted; static BitFlags mConnectivityFlag; @@ -199,6 +227,9 @@ class ConnectivityManagerImpl final : public ConnectivityManager, #if CHIP_DEVICE_CONFIG_ENABLE_WIFI static char sWiFiIfName[IFNAMSIZ]; #endif + + static NetworkCommissioning::WiFiDriver::ScanCallback * mpScanCallback; + static NetworkCommissioning::Internal::WirelessDriver::ConnectCallback * mpConnectCallback; }; #if CHIP_DEVICE_CONFIG_ENABLE_WPA diff --git a/src/platform/Linux/NetworkCommissioningDriver.h b/src/platform/Linux/NetworkCommissioningDriver.h new file mode 100644 index 00000000000000..41daa19ef04a18 --- /dev/null +++ b/src/platform/Linux/NetworkCommissioningDriver.h @@ -0,0 +1,159 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +namespace chip { +namespace DeviceLayer { +namespace NetworkCommissioning { + +template +class LinuxScanResponseIterator : public Iterator +{ +public: + LinuxScanResponseIterator(std::vector * apScanResponse) : mpScanResponse(apScanResponse) {} + size_t Count() override { return mpScanResponse != nullptr ? mpScanResponse->size() : 0; } + bool Next(T & item) override + { + if (mpScanResponse == nullptr || currentIterating >= mpScanResponse->size()) + { + return false; + } + item = (*mpScanResponse)[currentIterating]; + currentIterating++; + return true; + } + void Release() override + { /* nothing to do, we don't hold the ownership of the vector, and users is not expected to hold the ownership in OnFinished for + scan. */ + } + +private: + size_t currentIterating = 0; + // Note: We cannot post a event in ScheduleLambda since std::vector is not trivial copiable. + std::vector * mpScanResponse; +}; + +#if CHIP_DEVICE_CONFIG_ENABLE_WPA +class LinuxWiFiDriver final : public WiFiDriver +{ +public: + class WiFiNetworkIterator final : public NetworkIterator + { + public: + WiFiNetworkIterator(LinuxWiFiDriver * aDriver) : driver(aDriver) {} + size_t Count() override; + bool Next(Network & item) override; + void Release() override { delete this; } + ~WiFiNetworkIterator() = default; + + private: + LinuxWiFiDriver * driver; + bool exhausted = false; + }; + + struct WiFiNetwork + { + uint8_t ssid[DeviceLayer::Internal::kMaxWiFiSSIDLength]; + uint8_t ssidLen = 0; + uint8_t credentials[DeviceLayer::Internal::kMaxWiFiKeyLength]; + uint8_t credentialsLen = 0; + }; + + // BaseDriver + NetworkIterator * GetNetworks() override { return new WiFiNetworkIterator(this); } + CHIP_ERROR Init() override; + CHIP_ERROR Shutdown() override { return CHIP_NO_ERROR; } // Nothing to do on linux for shutdown. + + // WirelessDriver + uint8_t GetMaxNetworks() override { return 1; } + uint8_t GetScanNetworkTimeoutSeconds() override { return 10; } + uint8_t GetConnectNetworkTimeoutSeconds() override { return 20; } + + CHIP_ERROR CommitConfiguration() override; + CHIP_ERROR RevertConfiguration() override; + + Status RemoveNetwork(ByteSpan networkId) override; + Status ReorderNetwork(ByteSpan networkId, uint8_t index) override; + void ConnectNetwork(ByteSpan networkId, ConnectCallback * callback) override; + + // WiFiDriver + Status AddOrUpdateNetwork(ByteSpan ssid, ByteSpan credentials) override; + void ScanNetworks(ByteSpan ssid, ScanCallback * callback) override; + +private: + bool NetworkMatch(const WiFiNetwork & network, ByteSpan networkId); + + WiFiNetworkIterator mWiFiIterator = WiFiNetworkIterator(this); + WiFiNetwork mSavedNetwork; + WiFiNetwork mStagingNetwork; +}; +#endif // CHIP_DEVICE_CONFIG_ENABLE_WPA + +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD +class LinuxThreadDriver final : public ThreadDriver +{ +public: + class ThreadNetworkIterator final : public NetworkIterator + { + public: + ThreadNetworkIterator(LinuxThreadDriver * aDriver) : driver(aDriver) {} + size_t Count() override; + bool Next(Network & item) override; + void Release() override { delete this; } + ~ThreadNetworkIterator() = default; + + private: + LinuxThreadDriver * driver; + bool exhausted = false; + }; + + // BaseDriver + NetworkIterator * GetNetworks() override { return new ThreadNetworkIterator(this); } + CHIP_ERROR Init() override; + CHIP_ERROR Shutdown() override { return CHIP_NO_ERROR; } // Nothing to do on linux for shutdown. + + // WirelessDriver + uint8_t GetMaxNetworks() override { return 1; } + uint8_t GetScanNetworkTimeoutSeconds() override { return 10; } + uint8_t GetConnectNetworkTimeoutSeconds() override { return 20; } + + CHIP_ERROR CommitConfiguration() override; + CHIP_ERROR RevertConfiguration() override; + + Status RemoveNetwork(ByteSpan networkId) override; + Status ReorderNetwork(ByteSpan networkId, uint8_t index) override; + void ConnectNetwork(ByteSpan networkId, ConnectCallback * callback) override; + + // ThreadDriver + Status AddOrUpdateNetwork(ByteSpan operationalDataset) override; + void ScanNetworks(ScanCallback * callback) override; + +private: + ThreadNetworkIterator mThreadIterator = ThreadNetworkIterator(this); + Thread::OperationalDataset mSavedNetwork; + Thread::OperationalDataset mStagingNetwork; +}; + +#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD + +} // namespace NetworkCommissioning +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/Linux/NetworkCommissioningThreadDriver.cpp b/src/platform/Linux/NetworkCommissioningThreadDriver.cpp new file mode 100644 index 00000000000000..abbeeaa3f0af84 --- /dev/null +++ b/src/platform/Linux/NetworkCommissioningThreadDriver.cpp @@ -0,0 +1,196 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace chip; +using namespace chip::Thread; + +namespace chip { +namespace DeviceLayer { +namespace NetworkCommissioning { + +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD + +// NOTE: For ThreadDriver, we uses two network configs, one is mSavedNetwork, and another is mStagingNetwork, during init, it will +// load the network config from otbr-agent, and loads it into both mSavedNetwork and mStagingNetwork. When updating the networks, +// all changed are made on the staging network. +// TODO: The otbr-posix does not actually maintains its own networking states, it will always persist the last network connected. +// This should not be an issue for most cases, but we should implement the code for maintaining the states by ourselves. + +CHIP_ERROR LinuxThreadDriver::Init() +{ + ByteSpan currentProvision; + VerifyOrReturnError(ConnectivityMgrImpl().IsThreadAttached(), CHIP_NO_ERROR); + VerifyOrReturnError(ThreadStackMgrImpl().GetThreadProvision(currentProvision) == CHIP_NO_ERROR, CHIP_NO_ERROR); + + mSavedNetwork.Init(currentProvision); + mStagingNetwork.Init(currentProvision); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR LinuxThreadDriver::CommitConfiguration() +{ + // Note: otbr-agent will persist the networks by their own, we don't have much to do for saving the networks (see Init() above, + // we just loads the saved dataset from otbr-agent.) + mSavedNetwork = mStagingNetwork; + return CHIP_NO_ERROR; +} + +CHIP_ERROR LinuxThreadDriver::RevertConfiguration() +{ + mStagingNetwork = mSavedNetwork; + return CHIP_NO_ERROR; +} + +Status LinuxThreadDriver::AddOrUpdateNetwork(ByteSpan operationalDataset) +{ + uint8_t extpanid[kSizeExtendedPanId]; + uint8_t newExtpanid[kSizeExtendedPanId]; + Thread::OperationalDataset newDataset; + + newDataset.Init(operationalDataset); + VerifyOrReturnError(newDataset.IsCommissioned(), Status::kOutOfRange); + + VerifyOrReturnError(!mStagingNetwork.IsCommissioned() || memcmp(extpanid, newExtpanid, kSizeExtendedPanId) == 0, + Status::kBoundsExceeded); + + mStagingNetwork = newDataset; + return Status::kSuccess; +} + +Status LinuxThreadDriver::RemoveNetwork(ByteSpan networkId) +{ + uint8_t extpanid[kSizeExtendedPanId]; + if (!mStagingNetwork.IsCommissioned()) + { + return Status::kNetworkNotFound; + } + else if (mStagingNetwork.GetExtendedPanId(extpanid) != CHIP_NO_ERROR) + { + return Status::kUnknownError; + } + + VerifyOrReturnError(networkId.size() == kSizeExtendedPanId && memcmp(networkId.data(), extpanid, kSizeExtendedPanId) == 0, + Status::kNetworkNotFound); + mStagingNetwork.Clear(); + return Status::kSuccess; +} + +Status LinuxThreadDriver::ReorderNetwork(ByteSpan networkId, uint8_t index) +{ + uint8_t extpanid[kSizeExtendedPanId]; + if (!mStagingNetwork.IsCommissioned()) + { + return Status::kNetworkNotFound; + } + else if (mStagingNetwork.GetExtendedPanId(extpanid) != CHIP_NO_ERROR) + { + return Status::kUnknownError; + } + + VerifyOrReturnError(networkId.size() == kSizeExtendedPanId && memcmp(networkId.data(), extpanid, kSizeExtendedPanId) == 0, + Status::kNetworkNotFound); + + return Status::kSuccess; +} + +void LinuxThreadDriver::ConnectNetwork(ByteSpan networkId, ConnectCallback * callback) +{ + NetworkCommissioning::Status status = Status::kSuccess; + uint8_t extpanid[kSizeExtendedPanId]; + if (!mStagingNetwork.IsCommissioned()) + { + ExitNow(status = Status::kNetworkNotFound); + } + else if (mStagingNetwork.GetExtendedPanId(extpanid) != CHIP_NO_ERROR) + { + ExitNow(status = Status::kUnknownError); + } + + VerifyOrExit((networkId.size() == kSizeExtendedPanId && memcmp(networkId.data(), extpanid, kSizeExtendedPanId) == 0), + status = Status::kNetworkNotFound); + + VerifyOrExit(DeviceLayer::ThreadStackMgrImpl().AttachToThreadNetwork(mStagingNetwork.AsByteSpan(), callback) == CHIP_NO_ERROR, + status = Status::kUnknownError); + +exit: + if (status != Status::kSuccess) + { + callback->OnResult(status, CharSpan(), 0); + } +} + +void LinuxThreadDriver::ScanNetworks(ThreadDriver::ScanCallback * callback) +{ + CHIP_ERROR err = DeviceLayer::ThreadStackMgrImpl().StartThreadScan(callback); + if (err != CHIP_NO_ERROR) + { + callback->OnFinished(Status::kUnknownError, CharSpan(), nullptr); + } +} + +size_t LinuxThreadDriver::ThreadNetworkIterator::Count() +{ + return driver->mStagingNetwork.IsCommissioned() ? 1 : 0; +} + +bool LinuxThreadDriver::ThreadNetworkIterator::Next(Network & item) +{ + if (exhausted || !driver->mStagingNetwork.IsCommissioned()) + { + return false; + } + uint8_t extpanid[kSizeExtendedPanId]; + VerifyOrReturnError(driver->mStagingNetwork.GetExtendedPanId(extpanid) == CHIP_NO_ERROR, false); + memcpy(item.networkID, extpanid, kSizeExtendedPanId); + item.networkIDLen = kSizeExtendedPanId; + item.connected = false; + exhausted = true; + + ByteSpan currentProvision; + Thread::OperationalDataset currentDataset; + uint8_t enabledExtPanId[Thread::kSizeExtendedPanId]; + + // The Thread network is not actually enabled. + VerifyOrReturnError(ConnectivityMgrImpl().IsThreadAttached(), true); + VerifyOrReturnError(ThreadStackMgrImpl().GetThreadProvision(currentProvision) == CHIP_NO_ERROR, true); + VerifyOrReturnError(currentDataset.Init(currentProvision) == CHIP_NO_ERROR, true); + // The Thread network is not enabled, but has a different extended pan id. + VerifyOrReturnError(currentDataset.GetExtendedPanId(enabledExtPanId) == CHIP_NO_ERROR, true); + VerifyOrReturnError(memcmp(extpanid, enabledExtPanId, kSizeExtendedPanId) == 0, true); + // The Thread network is enabled and has the same extended pan id as the one in our record. + item.connected = true; + + return true; +} + +#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD + +} // namespace NetworkCommissioning +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/Linux/NetworkCommissioningWiFiDriver.cpp b/src/platform/Linux/NetworkCommissioningWiFiDriver.cpp new file mode 100644 index 00000000000000..5483715eb3b5dc --- /dev/null +++ b/src/platform/Linux/NetworkCommissioningWiFiDriver.cpp @@ -0,0 +1,207 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include +#include +#include + +using namespace chip; +using namespace chip::Thread; + +namespace chip { +namespace DeviceLayer { +namespace NetworkCommissioning { + +#if CHIP_DEVICE_CONFIG_ENABLE_WPA +// TODO: Here, most interfaces are just calling ConnectivityManager interfaces, this is because the ConnectivityProvides some +// bootstrap code for the wpa_supplicant. However, we can wrap the wpa_supplicant dbus api directly (and remove the related code in +// ConnectivityManagerImpl). +namespace { +constexpr char kWiFiSSIDKeyName[] = "wifi-ssid"; +constexpr char kWiFiCredentialsKeyName[] = "wifi-pass"; +} // namespace + +// NOTE: For WiFiDriver, we uses two network configs, one is mSavedNetwork, and another is mStagingNetwork, during init, it will +// load the network config from k-v storage, and loads it into both mSavedNetwork and mStagingNetwork. When updating the networks, +// all changed are made on the staging network, and when the network is committed, it will update the mSavedNetwork to +// mStagingNetwork and persist the changes. + +// NOTE: LinuxWiFiDriver uses network config with empty ssid (ssidLen = 0) for empty network config. + +// NOTE: For now, the LinuxWiFiDriver only supports one network, this can be fixed by using the wpa_supplicant API directly (then +// wpa_supplicant will manage the networks for us.) + +CHIP_ERROR LinuxWiFiDriver::Init() +{ + CHIP_ERROR err; + size_t ssidLen = 0; + size_t credentialsLen = 0; + + err = PersistedStorage::KeyValueStoreMgr().Get(kWiFiCredentialsKeyName, mSavedNetwork.credentials, + sizeof(mSavedNetwork.credentials), &credentialsLen); + if (err == CHIP_ERROR_KEY_NOT_FOUND) + { + return CHIP_NO_ERROR; + } + + err = PersistedStorage::KeyValueStoreMgr().Get(kWiFiSSIDKeyName, mSavedNetwork.ssid, sizeof(mSavedNetwork.ssid), &ssidLen); + if (err == CHIP_ERROR_KEY_NOT_FOUND) + { + return CHIP_NO_ERROR; + } + + mSavedNetwork.credentialsLen = credentialsLen; + mSavedNetwork.ssidLen = ssidLen; + + mStagingNetwork = mSavedNetwork; + return CHIP_NO_ERROR; +} + +CHIP_ERROR LinuxWiFiDriver::CommitConfiguration() +{ + ReturnErrorOnFailure(PersistedStorage::KeyValueStoreMgr().Put(kWiFiSSIDKeyName, mStagingNetwork.ssid, mStagingNetwork.ssidLen)); + ReturnErrorOnFailure(PersistedStorage::KeyValueStoreMgr().Put(kWiFiCredentialsKeyName, mStagingNetwork.credentials, + mStagingNetwork.credentialsLen)); + ReturnErrorOnFailure(ConnectivityMgrImpl().CommitConfig()); + mSavedNetwork = mStagingNetwork; + return CHIP_NO_ERROR; +} + +CHIP_ERROR LinuxWiFiDriver::RevertConfiguration() +{ + mStagingNetwork = mSavedNetwork; + return CHIP_NO_ERROR; +} + +bool LinuxWiFiDriver::NetworkMatch(const WiFiNetwork & network, ByteSpan networkId) +{ + return networkId.size() == network.ssidLen && memcmp(networkId.data(), network.ssid, network.ssidLen) == 0; +} + +Status LinuxWiFiDriver::AddOrUpdateNetwork(ByteSpan ssid, ByteSpan credentials) +{ + VerifyOrReturnError(mStagingNetwork.ssidLen == 0 || NetworkMatch(mStagingNetwork, ssid), Status::kBoundsExceeded); + + static_assert(sizeof(WiFiNetwork::ssid) <= std::numeric_limits::max(), + "Max length of WiFi ssid exceeds the limit of ssidLen field"); + static_assert(sizeof(WiFiNetwork::credentials) <= std::numeric_limits::max(), + "Max length of WiFi credentials exceeds the limit of credentialsLen field"); + + // Do the check before setting the values, so the data is not updated on error. + VerifyOrReturnError(credentials.size() <= sizeof(mStagingNetwork.credentials), Status::kOutOfRange); + VerifyOrReturnError(ssid.size() <= sizeof(mStagingNetwork.ssid), Status::kOutOfRange); + + memcpy(mStagingNetwork.credentials, credentials.data(), credentials.size()); + mStagingNetwork.credentialsLen = static_cast(credentials.size()); + + memcpy(mStagingNetwork.ssid, ssid.data(), ssid.size()); + mStagingNetwork.ssidLen = static_cast(ssid.size()); + + return Status::kSuccess; +} + +Status LinuxWiFiDriver::RemoveNetwork(ByteSpan networkId) +{ + VerifyOrReturnError(NetworkMatch(mStagingNetwork, networkId), Status::kNetworkIDNotFound); + + // Use empty ssid for representing invalid network + mStagingNetwork.ssidLen = 0; + return Status::kSuccess; +} + +Status LinuxWiFiDriver::ReorderNetwork(ByteSpan networkId, uint8_t index) +{ + VerifyOrReturnError(NetworkMatch(mStagingNetwork, networkId), Status::kNetworkIDNotFound); + // We only support one network, so reorder is actually no-op. + + return Status::kSuccess; +} + +void LinuxWiFiDriver::ConnectNetwork(ByteSpan networkId, ConnectCallback * callback) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + Status networkingStatus = Status::kSuccess; + + VerifyOrExit(NetworkMatch(mStagingNetwork, networkId), networkingStatus = Status::kNetworkIDNotFound); + + ChipLogProgress(NetworkProvisioning, "LinuxNetworkCommissioningDelegate: SSID: %s", networkId.data()); + + err = ConnectivityMgrImpl().ConnectWiFiNetworkAsync(ByteSpan(mStagingNetwork.ssid, mStagingNetwork.ssidLen), + ByteSpan(mStagingNetwork.credentials, mStagingNetwork.credentialsLen), + callback); +exit: + if (err != CHIP_NO_ERROR) + { + networkingStatus = Status::kUnknownError; + } + + if (networkingStatus != Status::kSuccess) + { + ChipLogError(NetworkProvisioning, "Failed to connect to WiFi network: %s", chip::ErrorStr(err)); + callback->OnResult(networkingStatus, CharSpan(), 0); + } +} + +void LinuxWiFiDriver::ScanNetworks(ByteSpan ssid, WiFiDriver::ScanCallback * callback) +{ + CHIP_ERROR err = DeviceLayer::ConnectivityMgrImpl().StartWiFiScan(ssid, callback); + if (err != CHIP_NO_ERROR) + { + callback->OnFinished(Status::kUnknownError, CharSpan(), nullptr); + } +} + +size_t LinuxWiFiDriver::WiFiNetworkIterator::Count() +{ + return driver->mStagingNetwork.ssidLen == 0 ? 0 : 1; +} + +bool LinuxWiFiDriver::WiFiNetworkIterator::Next(Network & item) +{ + if (exhausted || driver->mStagingNetwork.ssidLen == 0) + { + return false; + } + memcpy(item.networkID, driver->mStagingNetwork.ssid, driver->mStagingNetwork.ssidLen); + item.networkIDLen = driver->mStagingNetwork.ssidLen; + item.connected = false; + exhausted = true; + + Network connectedNetwork; + CHIP_ERROR err = DeviceLayer::ConnectivityMgrImpl().GetConnectedNetwork(connectedNetwork); + if (err == CHIP_NO_ERROR) + { + if (connectedNetwork.networkIDLen == item.networkIDLen && + memcmp(connectedNetwork.networkID, item.networkID, item.networkIDLen) == 0) + { + item.connected = true; + } + } + + return true; +} + +#endif // CHIP_DEVICE_CONFIG_ENABLE_WPA + +} // namespace NetworkCommissioning +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/Linux/ThreadStackManagerImpl.cpp b/src/platform/Linux/ThreadStackManagerImpl.cpp index 2fc6614eeba39d..d50aed7be164c4 100644 --- a/src/platform/Linux/ThreadStackManagerImpl.cpp +++ b/src/platform/Linux/ThreadStackManagerImpl.cpp @@ -21,11 +21,17 @@ #include #include #include +#include #include #include +#include +#include +#include + using namespace ::chip::app; using namespace ::chip::app::Clusters; +using namespace chip::DeviceLayer::NetworkCommissioning; namespace chip { namespace DeviceLayer { @@ -232,8 +238,15 @@ CHIP_ERROR ThreadStackManagerImpl::_GetThreadProvision(ByteSpan & netInfo) VerifyOrReturnError(mProxy, CHIP_ERROR_INCORRECT_STATE); { + // TODO: The following code does not works actually, since otbr-posix does not emit signals for properties changes. Which is + // required for gdbus to caching properties. std::unique_ptr value( openthread_io_openthread_border_router_dup_active_dataset_tlvs(mProxy.get())); + if (value == nullptr) + { + netInfo = ByteSpan(); + return CHIP_ERROR_KEY_NOT_FOUND; + } GBytes * bytes = g_variant_get_data_as_bytes(value.get()); gsize size; const uint8_t * data = reinterpret_cast(g_bytes_get_data(bytes, &size)); @@ -276,20 +289,7 @@ CHIP_ERROR ThreadStackManagerImpl::_SetThreadEnabled(bool val) VerifyOrReturnError(mProxy, CHIP_ERROR_INCORRECT_STATE); if (val) { - std::unique_ptr err; - gboolean result = - openthread_io_openthread_border_router_call_attach_sync(mProxy.get(), nullptr, &MakeUniquePointerReceiver(err).Get()); - if (err) - { - ChipLogError(DeviceLayer, "openthread: _SetThreadEnabled calling %s failed: %s", "Attach", err->message); - return CHIP_ERROR_INTERNAL; - } - - if (!result) - { - ChipLogError(DeviceLayer, "openthread: _SetThreadEnabled calling %s failed: %s", "Attach", "return false"); - return CHIP_ERROR_INTERNAL; - } + openthread_io_openthread_border_router_call_attach(mProxy.get(), nullptr, _OnThreadAttachFinished, this); } else { @@ -311,6 +311,41 @@ CHIP_ERROR ThreadStackManagerImpl::_SetThreadEnabled(bool val) return CHIP_NO_ERROR; } +void ThreadStackManagerImpl::_OnThreadAttachFinished(GObject * source_object, GAsyncResult * res, gpointer user_data) +{ + ThreadStackManagerImpl * this_ = reinterpret_cast(user_data); + std::unique_ptr attachRes; + std::unique_ptr err; + { + gboolean result = openthread_io_openthread_border_router_call_attach_finish(this_->mProxy.get(), res, + &MakeUniquePointerReceiver(err).Get()); + if (!result) + { + ChipLogError(DeviceLayer, "Failed to perform finish Thread network scan: %s", + err == nullptr ? "unknown error" : err->message); + DeviceLayer::SystemLayer().ScheduleLambda([this_]() { + if (this_->mpConnectCallback != nullptr) + { + // TODO: Replace this with actual thread attach result. + this_->mpConnectCallback->OnResult(NetworkCommissioning::Status::kUnknownError, CharSpan(), 0); + this_->mpConnectCallback = nullptr; + } + }); + } + else + { + DeviceLayer::SystemLayer().ScheduleLambda([this_]() { + if (this_->mpConnectCallback != nullptr) + { + // TODO: Replace this with actual thread attach result. + this_->mpConnectCallback->OnResult(NetworkCommissioning::Status::kSuccess, CharSpan(), 0); + this_->mpConnectCallback = nullptr; + } + }); + } + } +} + ConnectivityManager::ThreadDeviceType ThreadStackManagerImpl::_GetThreadDeviceType() { ConnectivityManager::ThreadDeviceType type = ConnectivityManager::ThreadDeviceType::kThreadDeviceType_NotSupported; @@ -472,6 +507,109 @@ CHIP_ERROR ThreadStackManagerImpl::_JoinerStart() return CHIP_ERROR_NOT_IMPLEMENTED; } +CHIP_ERROR ThreadStackManagerImpl::StartThreadScan(ThreadDriver::ScanCallback * callback) +{ + // There is another ongoing scan request, reject the new one. + VerifyOrReturnError(mpScanCallback == nullptr, CHIP_ERROR_INCORRECT_STATE); + mpScanCallback = callback; + openthread_io_openthread_border_router_call_scan(mProxy.get(), nullptr, _OnNetworkScanFinished, this); + return CHIP_NO_ERROR; +} + +void ThreadStackManagerImpl::_OnNetworkScanFinished(GObject * source_object, GAsyncResult * res, gpointer user_data) +{ + ThreadStackManagerImpl * this_ = reinterpret_cast(user_data); + this_->_OnNetworkScanFinished(res); +} + +void ThreadStackManagerImpl::_OnNetworkScanFinished(GAsyncResult * res) +{ + std::unique_ptr scan_result; + std::unique_ptr err; + { + gboolean result = openthread_io_openthread_border_router_call_scan_finish( + mProxy.get(), &MakeUniquePointerReceiver(scan_result).Get(), res, &MakeUniquePointerReceiver(err).Get()); + if (!result) + { + ChipLogError(DeviceLayer, "Failed to perform finish Thread network scan: %s", + err == nullptr ? "unknown error" : err->message); + DeviceLayer::SystemLayer().ScheduleLambda([this]() { + if (mpScanCallback != nullptr) + { + LinuxScanResponseIterator iter(nullptr); + mpScanCallback->OnFinished(Status::kUnknownError, CharSpan(), &iter); + } + mpScanCallback = nullptr; + }); + } + } + + std::vector * scanResult = + new std::vector(); + + if (g_variant_n_children(scan_result.get()) > 0) + { + std::unique_ptr iter; + g_variant_get(scan_result.get(), "a(tstayqqyyyybb)", &MakeUniquePointerReceiver(iter).Get()); + if (!iter) + return; + + guint64 ext_address; + const gchar * network_name; + guint64 ext_panid; + const gchar * steering_data; + guint16 panid; + guint16 joiner_udp_port; + guint8 channel; + guint8 rssi; + guint8 lqi; + guint8 version; + gboolean is_native; + gboolean is_joinable; + + while (g_variant_iter_loop(iter.get(), "(tstayqqyyyybb)", &ext_address, &network_name, &ext_panid, &steering_data, &panid, + &joiner_udp_port, &channel, &rssi, &lqi, &version, &is_native, &is_joinable)) + { + ChipLogProgress(DeviceLayer, + "Thread Network: %s (%016" PRIx64 ") ExtPanId(%016" PRIx64 ") RSSI %" PRIu16 " LQI %" PRIu8 + " Version %" PRIu8, + network_name, ext_address, ext_panid, rssi, lqi, version); + NetworkCommissioning::ThreadScanResponse networkScanned; + networkScanned.panId = panid; + networkScanned.extendedPanId = ext_panid; + size_t networkNameLen = strlen(network_name); + if (networkNameLen > 16) + { + ChipLogProgress(DeviceLayer, "Network name is too long, ignore it."); + continue; + } + networkScanned.networkNameLen = static_cast(networkNameLen); + memcpy(networkScanned.networkName, network_name, networkNameLen); + networkScanned.channel = channel; + networkScanned.version = version; + networkScanned.extendedAddress = 0; + networkScanned.rssi = rssi; + networkScanned.lqi = lqi; + + scanResult->push_back(networkScanned); + } + } + + DeviceLayer::SystemLayer().ScheduleLambda([this, scanResult]() { + // Note: We cannot post a event in ScheduleLambda since std::vector is not trivial copiable. This results in the use of + // const_cast but should be fine for almost all cases, since we actually handled the ownership of this element to this + // lambda. + if (mpScanCallback != nullptr) + { + LinuxScanResponseIterator iter( + const_cast *>(scanResult)); + mpScanCallback->OnFinished(Status::kSuccess, CharSpan(), &iter); + mpScanCallback = nullptr; + } + delete const_cast *>(scanResult); + }); +} + void ThreadStackManagerImpl::_ResetThreadNetworkDiagnosticsCounts() {} CHIP_ERROR ThreadStackManagerImpl::_WriteThreadNetworkDiagnosticAttributeToTlv(AttributeId attributeId, @@ -498,6 +636,19 @@ CHIP_ERROR ThreadStackManagerImpl::_WriteThreadNetworkDiagnosticAttributeToTlv(A return err; } +CHIP_ERROR +ThreadStackManagerImpl::AttachToThreadNetwork(ByteSpan netInfo, + NetworkCommissioning::Internal::WirelessDriver::ConnectCallback * callback) +{ + // There is another ongoing connect request, reject the new one. + VerifyOrReturnError(mpConnectCallback == nullptr, CHIP_ERROR_INCORRECT_STATE); + ReturnErrorOnFailure(DeviceLayer::ThreadStackMgr().SetThreadEnabled(false)); + ReturnErrorOnFailure(DeviceLayer::ThreadStackMgr().SetThreadProvision(netInfo)); + ReturnErrorOnFailure(DeviceLayer::ThreadStackMgr().SetThreadEnabled(true)); + mpConnectCallback = callback; + return CHIP_NO_ERROR; +} + ThreadStackManager & ThreadStackMgr() { return chip::DeviceLayer::ThreadStackManagerImpl::sInstance; diff --git a/src/platform/Linux/ThreadStackManagerImpl.h b/src/platform/Linux/ThreadStackManagerImpl.h index 3bbd29867ab811..caac53708b9922 100644 --- a/src/platform/Linux/ThreadStackManagerImpl.h +++ b/src/platform/Linux/ThreadStackManagerImpl.h @@ -18,11 +18,13 @@ #pragma once #include +#include #include #include #include #include +#include #include #include @@ -50,6 +52,11 @@ class ThreadStackManagerImpl : public ThreadStackManager CHIP_ERROR _SetThreadProvision(ByteSpan netInfo); + void _OnNetworkScanFinished(GAsyncResult * res); + static void _OnNetworkScanFinished(GObject * source_object, GAsyncResult * res, gpointer user_data); + + CHIP_ERROR GetExtendedPanId(uint8_t extPanId[Thread::kSizeExtendedPanId]); + void _ErasePersistentInfo(); bool _IsThreadProvisioned(); @@ -58,7 +65,10 @@ class ThreadStackManagerImpl : public ThreadStackManager bool _IsThreadAttached(); + CHIP_ERROR AttachToThreadNetwork(ByteSpan netInfo, NetworkCommissioning::Internal::WirelessDriver::ConnectCallback * callback); + CHIP_ERROR _SetThreadEnabled(bool val); + static void _OnThreadAttachFinished(GObject * source_object, GAsyncResult * res, gpointer user_data); ConnectivityManager::ThreadDeviceType _GetThreadDeviceType(); @@ -90,6 +100,8 @@ class ThreadStackManagerImpl : public ThreadStackManager CHIP_ERROR _WriteThreadNetworkDiagnosticAttributeToTlv(AttributeId attributeId, app::AttributeValueEncoder & encoder); + CHIP_ERROR StartThreadScan(NetworkCommissioning::ThreadDriver::ScanCallback * callback); + ~ThreadStackManagerImpl() = default; static ThreadStackManagerImpl sInstance; @@ -106,6 +118,19 @@ class ThreadStackManagerImpl : public ThreadStackManager static constexpr char kPropertyDeviceRole[] = "DeviceRole"; + struct ThreadNetworkScanned + { + uint16_t panId; + uint64_t extendedPanId; + uint8_t networkName[16]; + uint8_t networkNameLen; + uint16_t channel; + uint8_t version; + uint64_t extendedAddress; + int8_t rssi; + uint8_t lqi; + }; + std::unique_ptr mProxy; static void OnDbusPropertiesChanged(OpenthreadIoOpenthreadBorderRouter * proxy, GVariant * changed_properties, @@ -114,6 +139,9 @@ class ThreadStackManagerImpl : public ThreadStackManager Thread::OperationalDataset mDataset = {}; + NetworkCommissioning::ThreadDriver::ScanCallback * mpScanCallback; + NetworkCommissioning::Internal::WirelessDriver::ConnectCallback * mpConnectCallback; + bool mAttached; }; diff --git a/src/platform/Linux/dbus/openthread/introspect.xml b/src/platform/Linux/dbus/openthread/introspect.xml index e92b3acf4c57aa..e583697e1f63ec 100644 --- a/src/platform/Linux/dbus/openthread/introspect.xml +++ b/src/platform/Linux/dbus/openthread/introspect.xml @@ -30,8 +30,8 @@ limitations under the License. uint8[] steering_data uint16 panid uint16 joiner_udp_port - uint16 channel - uint16 rssi + uint8 channel + uint8 rssi uint8 lqi uint8 version bool is_native @@ -40,7 +40,7 @@ limitations under the License. --> - + diff --git a/src/platform/Linux/dbus/wpa/DBusWpaBss.xml b/src/platform/Linux/dbus/wpa/DBusWpaBss.xml index b6e8fee4633745..8ec5c4aa749dcb 100644 --- a/src/platform/Linux/dbus/wpa/DBusWpaBss.xml +++ b/src/platform/Linux/dbus/wpa/DBusWpaBss.xml @@ -2,8 +2,18 @@ - - + + + + + + + + + + + + diff --git a/zzz_generated/all-clusters-app/zap-generated/endpoint_config.h b/zzz_generated/all-clusters-app/zap-generated/endpoint_config.h index 201e01ef030e3f..51722ad886d700 100644 --- a/zzz_generated/all-clusters-app/zap-generated/endpoint_config.h +++ b/zzz_generated/all-clusters-app/zap-generated/endpoint_config.h @@ -292,16 +292,27 @@ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ /* 652 - FeatureMap, */ \ + 0x00, 0x00, 0x00, 0x00, \ + \ + /* Endpoint: 1, Cluster: Network Commissioning (server), big-endian */ \ + \ + /* 656 - Networks, */ \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + \ + /* 668 - LastConnectErrorValue, */ \ + 0x00, 0x00, 0x00, 0x00, \ + \ + /* 672 - FeatureMap, */ \ 0x00, 0x00, 0x00, 0x00, \ \ /* Endpoint: 1, Cluster: Switch (server), big-endian */ \ \ - /* 656 - FeatureMap, */ \ + /* 676 - FeatureMap, */ \ 0x00, 0x00, 0x00, 0x00, \ \ /* Endpoint: 1, Cluster: Fixed Label (server), big-endian */ \ \ - /* 660 - label list, */ \ + /* 680 - label list, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ @@ -319,49 +330,49 @@ \ /* Endpoint: 1, Cluster: Mode Select (server), big-endian */ \ \ - /* 914 - Description, */ \ + /* 934 - Description, */ \ 6, 'C', 'o', 'f', 'f', 'e', 'e', \ \ /* Endpoint: 1, Cluster: Door Lock (server), big-endian */ \ \ - /* 921 - Language, */ \ + /* 941 - Language, */ \ 2, 'e', 'n', \ \ - /* 924 - AutoRelockTime, */ \ + /* 944 - AutoRelockTime, */ \ 0x00, 0x00, 0x00, 0x60, \ \ /* Endpoint: 1, Cluster: Window Covering (server), big-endian */ \ \ - /* 928 - FeatureMap, */ \ + /* 948 - FeatureMap, */ \ 0x00, 0x00, 0x00, 0x00, \ \ /* Endpoint: 1, Cluster: Pump Configuration and Control (server), big-endian */ \ \ - /* 932 - LifetimeRunningHours, */ \ + /* 952 - LifetimeRunningHours, */ \ 0x00, 0x00, 0x00, \ \ - /* 935 - Power, */ \ + /* 955 - Power, */ \ 0x00, 0x00, 0x00, \ \ - /* 938 - LifetimeEnergyConsumed, */ \ + /* 958 - LifetimeEnergyConsumed, */ \ 0x00, 0x00, 0x00, 0x00, \ \ - /* 942 - FeatureMap, */ \ + /* 962 - FeatureMap, */ \ 0x00, 0x00, 0x00, 0x00, \ \ /* Endpoint: 1, Cluster: Thermostat (server), big-endian */ \ \ - /* 946 - FeatureMap, */ \ + /* 966 - FeatureMap, */ \ 0x00, 0x00, 0x00, 0x0B, \ \ /* Endpoint: 1, Cluster: IAS Zone (server), big-endian */ \ \ - /* 950 - IAS CIE address, */ \ + /* 970 - IAS CIE address, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ /* Endpoint: 1, Cluster: Channel (server), big-endian */ \ \ - /* 958 - channel list, */ \ + /* 978 - channel list, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ @@ -379,7 +390,7 @@ \ /* Endpoint: 1, Cluster: Target Navigator (server), big-endian */ \ \ - /* 1212 - target navigator list, */ \ + /* 1232 - target navigator list, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ @@ -397,24 +408,24 @@ \ /* Endpoint: 1, Cluster: Media Playback (server), big-endian */ \ \ - /* 1466 - start time, */ \ + /* 1486 - start time, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, \ \ - /* 1474 - duration, */ \ + /* 1494 - duration, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 1482 - playback speed, */ \ + /* 1502 - playback speed, */ \ 0x00, 0x00, 0x00, 0x00, \ \ - /* 1486 - seek range end, */ \ + /* 1506 - seek range end, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 1494 - seek range start, */ \ + /* 1514 - seek range start, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ /* Endpoint: 1, Cluster: Media Input (server), big-endian */ \ \ - /* 1502 - media input list, */ \ + /* 1522 - media input list, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ @@ -432,7 +443,7 @@ \ /* Endpoint: 1, Cluster: Content Launcher (server), big-endian */ \ \ - /* 1756 - accept header list, */ \ + /* 1776 - accept header list, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ @@ -448,12 +459,12 @@ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 2010 - supported streaming protocols, */ \ + /* 2030 - supported streaming protocols, */ \ 0x00, 0x00, 0x00, 0x00, \ \ /* Endpoint: 1, Cluster: Audio Output (server), big-endian */ \ \ - /* 2014 - audio output list, */ \ + /* 2034 - audio output list, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ @@ -471,7 +482,7 @@ \ /* Endpoint: 1, Cluster: Application Launcher (server), big-endian */ \ \ - /* 2268 - application launcher list, */ \ + /* 2288 - application launcher list, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ @@ -489,61 +500,61 @@ \ /* Endpoint: 1, Cluster: Test Cluster (server), big-endian */ \ \ - /* 2522 - bitmap32, */ \ + /* 2542 - bitmap32, */ \ 0x00, 0x00, 0x00, 0x00, \ \ - /* 2526 - bitmap64, */ \ + /* 2546 - bitmap64, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 2534 - int24u, */ \ + /* 2554 - int24u, */ \ 0x00, 0x00, 0x00, \ \ - /* 2537 - int32u, */ \ + /* 2557 - int32u, */ \ 0x00, 0x00, 0x00, 0x00, \ \ - /* 2541 - int40u, */ \ + /* 2561 - int40u, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 2546 - int48u, */ \ + /* 2566 - int48u, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 2552 - int56u, */ \ + /* 2572 - int56u, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 2559 - int64u, */ \ + /* 2579 - int64u, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 2567 - int24s, */ \ + /* 2587 - int24s, */ \ 0x00, 0x00, 0x00, \ \ - /* 2570 - int32s, */ \ + /* 2590 - int32s, */ \ 0x00, 0x00, 0x00, 0x00, \ \ - /* 2574 - int40s, */ \ + /* 2594 - int40s, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 2579 - int48s, */ \ + /* 2599 - int48s, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 2585 - int56s, */ \ + /* 2605 - int56s, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 2592 - int64s, */ \ + /* 2612 - int64s, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 2600 - float_single, */ \ + /* 2620 - float_single, */ \ 0x00, 0x00, 0x00, 0x00, \ \ - /* 2604 - float_double, */ \ + /* 2624 - float_double, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 2612 - epoch_us, */ \ + /* 2632 - epoch_us, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 2620 - epoch_s, */ \ + /* 2640 - epoch_s, */ \ 0x00, 0x00, 0x00, 0x00, \ \ - /* 2624 - list_long_octet_string, */ \ + /* 2644 - list_long_octet_string, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ @@ -598,65 +609,65 @@ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 3624 - nullable_bitmap32, */ \ + /* 3644 - nullable_bitmap32, */ \ 0x00, 0x00, 0x00, 0x00, \ \ - /* 3628 - nullable_bitmap64, */ \ + /* 3648 - nullable_bitmap64, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 3636 - nullable_int24u, */ \ + /* 3656 - nullable_int24u, */ \ 0x00, 0x00, 0x00, \ \ - /* 3639 - nullable_int32u, */ \ + /* 3659 - nullable_int32u, */ \ 0x00, 0x00, 0x00, 0x00, \ \ - /* 3643 - nullable_int40u, */ \ + /* 3663 - nullable_int40u, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 3648 - nullable_int48u, */ \ + /* 3668 - nullable_int48u, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 3654 - nullable_int56u, */ \ + /* 3674 - nullable_int56u, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 3661 - nullable_int64u, */ \ + /* 3681 - nullable_int64u, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 3669 - nullable_int24s, */ \ + /* 3689 - nullable_int24s, */ \ 0x00, 0x00, 0x00, \ \ - /* 3672 - nullable_int32s, */ \ + /* 3692 - nullable_int32s, */ \ 0x00, 0x00, 0x00, 0x00, \ \ - /* 3676 - nullable_int40s, */ \ + /* 3696 - nullable_int40s, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 3681 - nullable_int48s, */ \ + /* 3701 - nullable_int48s, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 3687 - nullable_int56s, */ \ + /* 3707 - nullable_int56s, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 3694 - nullable_int64s, */ \ + /* 3714 - nullable_int64s, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 3702 - nullable_float_single, */ \ + /* 3722 - nullable_float_single, */ \ 0x00, 0x00, 0x00, 0x00, \ \ - /* 3706 - nullable_float_double, */ \ + /* 3726 - nullable_float_double, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ /* Endpoint: 1, Cluster: Electrical Measurement (server), big-endian */ \ \ - /* 3714 - measurement type, */ \ + /* 3734 - measurement type, */ \ 0x00, 0x00, 0x00, 0x00, \ \ - /* 3718 - total active power, */ \ + /* 3738 - total active power, */ \ 0x00, 0x00, 0x00, 0x00, \ \ /* Endpoint: 2, Cluster: On/Off (server), big-endian */ \ \ - /* 3722 - FeatureMap, */ \ + /* 3742 - FeatureMap, */ \ 0x00, 0x00, 0x00, 0x00, \ } @@ -929,16 +940,27 @@ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ /* 652 - FeatureMap, */ \ + 0x00, 0x00, 0x00, 0x00, \ + \ + /* Endpoint: 1, Cluster: Network Commissioning (server), little-endian */ \ + \ + /* 656 - Networks, */ \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + \ + /* 668 - LastConnectErrorValue, */ \ + 0x00, 0x00, 0x00, 0x00, \ + \ + /* 672 - FeatureMap, */ \ 0x00, 0x00, 0x00, 0x00, \ \ /* Endpoint: 1, Cluster: Switch (server), little-endian */ \ \ - /* 656 - FeatureMap, */ \ + /* 676 - FeatureMap, */ \ 0x00, 0x00, 0x00, 0x00, \ \ /* Endpoint: 1, Cluster: Fixed Label (server), little-endian */ \ \ - /* 660 - label list, */ \ + /* 680 - label list, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ @@ -956,49 +978,49 @@ \ /* Endpoint: 1, Cluster: Mode Select (server), little-endian */ \ \ - /* 914 - Description, */ \ + /* 934 - Description, */ \ 6, 'C', 'o', 'f', 'f', 'e', 'e', \ \ /* Endpoint: 1, Cluster: Door Lock (server), little-endian */ \ \ - /* 921 - Language, */ \ + /* 941 - Language, */ \ 2, 'e', 'n', \ \ - /* 924 - AutoRelockTime, */ \ + /* 944 - AutoRelockTime, */ \ 0x60, 0x00, 0x00, 0x00, \ \ /* Endpoint: 1, Cluster: Window Covering (server), little-endian */ \ \ - /* 928 - FeatureMap, */ \ + /* 948 - FeatureMap, */ \ 0x00, 0x00, 0x00, 0x00, \ \ /* Endpoint: 1, Cluster: Pump Configuration and Control (server), little-endian */ \ \ - /* 932 - LifetimeRunningHours, */ \ + /* 952 - LifetimeRunningHours, */ \ 0x00, 0x00, 0x00, \ \ - /* 935 - Power, */ \ + /* 955 - Power, */ \ 0x00, 0x00, 0x00, \ \ - /* 938 - LifetimeEnergyConsumed, */ \ + /* 958 - LifetimeEnergyConsumed, */ \ 0x00, 0x00, 0x00, 0x00, \ \ - /* 942 - FeatureMap, */ \ + /* 962 - FeatureMap, */ \ 0x00, 0x00, 0x00, 0x00, \ \ /* Endpoint: 1, Cluster: Thermostat (server), little-endian */ \ \ - /* 946 - FeatureMap, */ \ + /* 966 - FeatureMap, */ \ 0x0B, 0x00, 0x00, 0x00, \ \ /* Endpoint: 1, Cluster: IAS Zone (server), little-endian */ \ \ - /* 950 - IAS CIE address, */ \ + /* 970 - IAS CIE address, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ /* Endpoint: 1, Cluster: Channel (server), little-endian */ \ \ - /* 958 - channel list, */ \ + /* 978 - channel list, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ @@ -1016,7 +1038,7 @@ \ /* Endpoint: 1, Cluster: Target Navigator (server), little-endian */ \ \ - /* 1212 - target navigator list, */ \ + /* 1232 - target navigator list, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ @@ -1034,24 +1056,24 @@ \ /* Endpoint: 1, Cluster: Media Playback (server), little-endian */ \ \ - /* 1466 - start time, */ \ + /* 1486 - start time, */ \ 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 1474 - duration, */ \ + /* 1494 - duration, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 1482 - playback speed, */ \ + /* 1502 - playback speed, */ \ 0x00, 0x00, 0x00, 0x00, \ \ - /* 1486 - seek range end, */ \ + /* 1506 - seek range end, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 1494 - seek range start, */ \ + /* 1514 - seek range start, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ /* Endpoint: 1, Cluster: Media Input (server), little-endian */ \ \ - /* 1502 - media input list, */ \ + /* 1522 - media input list, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ @@ -1069,7 +1091,7 @@ \ /* Endpoint: 1, Cluster: Content Launcher (server), little-endian */ \ \ - /* 1756 - accept header list, */ \ + /* 1776 - accept header list, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ @@ -1085,12 +1107,12 @@ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 2010 - supported streaming protocols, */ \ + /* 2030 - supported streaming protocols, */ \ 0x00, 0x00, 0x00, 0x00, \ \ /* Endpoint: 1, Cluster: Audio Output (server), little-endian */ \ \ - /* 2014 - audio output list, */ \ + /* 2034 - audio output list, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ @@ -1108,7 +1130,7 @@ \ /* Endpoint: 1, Cluster: Application Launcher (server), little-endian */ \ \ - /* 2268 - application launcher list, */ \ + /* 2288 - application launcher list, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ @@ -1126,61 +1148,61 @@ \ /* Endpoint: 1, Cluster: Test Cluster (server), little-endian */ \ \ - /* 2522 - bitmap32, */ \ + /* 2542 - bitmap32, */ \ 0x00, 0x00, 0x00, 0x00, \ \ - /* 2526 - bitmap64, */ \ + /* 2546 - bitmap64, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 2534 - int24u, */ \ + /* 2554 - int24u, */ \ 0x00, 0x00, 0x00, \ \ - /* 2537 - int32u, */ \ + /* 2557 - int32u, */ \ 0x00, 0x00, 0x00, 0x00, \ \ - /* 2541 - int40u, */ \ + /* 2561 - int40u, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 2546 - int48u, */ \ + /* 2566 - int48u, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 2552 - int56u, */ \ + /* 2572 - int56u, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 2559 - int64u, */ \ + /* 2579 - int64u, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 2567 - int24s, */ \ + /* 2587 - int24s, */ \ 0x00, 0x00, 0x00, \ \ - /* 2570 - int32s, */ \ + /* 2590 - int32s, */ \ 0x00, 0x00, 0x00, 0x00, \ \ - /* 2574 - int40s, */ \ + /* 2594 - int40s, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 2579 - int48s, */ \ + /* 2599 - int48s, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 2585 - int56s, */ \ + /* 2605 - int56s, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 2592 - int64s, */ \ + /* 2612 - int64s, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 2600 - float_single, */ \ + /* 2620 - float_single, */ \ 0x00, 0x00, 0x00, 0x00, \ \ - /* 2604 - float_double, */ \ + /* 2624 - float_double, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 2612 - epoch_us, */ \ + /* 2632 - epoch_us, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 2620 - epoch_s, */ \ + /* 2640 - epoch_s, */ \ 0x00, 0x00, 0x00, 0x00, \ \ - /* 2624 - list_long_octet_string, */ \ + /* 2644 - list_long_octet_string, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ @@ -1235,71 +1257,71 @@ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 3624 - nullable_bitmap32, */ \ + /* 3644 - nullable_bitmap32, */ \ 0x00, 0x00, 0x00, 0x00, \ \ - /* 3628 - nullable_bitmap64, */ \ + /* 3648 - nullable_bitmap64, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 3636 - nullable_int24u, */ \ + /* 3656 - nullable_int24u, */ \ 0x00, 0x00, 0x00, \ \ - /* 3639 - nullable_int32u, */ \ + /* 3659 - nullable_int32u, */ \ 0x00, 0x00, 0x00, 0x00, \ \ - /* 3643 - nullable_int40u, */ \ + /* 3663 - nullable_int40u, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 3648 - nullable_int48u, */ \ + /* 3668 - nullable_int48u, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 3654 - nullable_int56u, */ \ + /* 3674 - nullable_int56u, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 3661 - nullable_int64u, */ \ + /* 3681 - nullable_int64u, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 3669 - nullable_int24s, */ \ + /* 3689 - nullable_int24s, */ \ 0x00, 0x00, 0x00, \ \ - /* 3672 - nullable_int32s, */ \ + /* 3692 - nullable_int32s, */ \ 0x00, 0x00, 0x00, 0x00, \ \ - /* 3676 - nullable_int40s, */ \ + /* 3696 - nullable_int40s, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 3681 - nullable_int48s, */ \ + /* 3701 - nullable_int48s, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 3687 - nullable_int56s, */ \ + /* 3707 - nullable_int56s, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 3694 - nullable_int64s, */ \ + /* 3714 - nullable_int64s, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ - /* 3702 - nullable_float_single, */ \ + /* 3722 - nullable_float_single, */ \ 0x00, 0x00, 0x00, 0x00, \ \ - /* 3706 - nullable_float_double, */ \ + /* 3726 - nullable_float_double, */ \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ \ /* Endpoint: 1, Cluster: Electrical Measurement (server), little-endian */ \ \ - /* 3714 - measurement type, */ \ + /* 3734 - measurement type, */ \ 0x00, 0x00, 0x00, 0x00, \ \ - /* 3718 - total active power, */ \ + /* 3738 - total active power, */ \ 0x00, 0x00, 0x00, 0x00, \ \ /* Endpoint: 2, Cluster: On/Off (server), little-endian */ \ \ - /* 3722 - FeatureMap, */ \ + /* 3742 - FeatureMap, */ \ 0x00, 0x00, 0x00, 0x00, \ } #endif // BIGENDIAN_CPU -#define GENERATED_DEFAULTS_COUNT (140) +#define GENERATED_DEFAULTS_COUNT (143) #define ZAP_TYPE(type) ZCL_##type##_ATTRIBUTE_TYPE #define ZAP_LONG_DEFAULTS_INDEX(index) \ @@ -1392,7 +1414,7 @@ #define ZAP_ATTRIBUTE_MASK(mask) ATTRIBUTE_MASK_##mask // This is an array of EmberAfAttributeMetadata structures. -#define GENERATED_ATTRIBUTE_COUNT 590 +#define GENERATED_ATTRIBUTE_COUNT 600 #define GENERATED_ATTRIBUTES \ { \ \ @@ -1736,6 +1758,18 @@ { 0xFFFC, ZAP_TYPE(BITMAP32), 4, 0, ZAP_LONG_DEFAULTS_INDEX(652) }, /* FeatureMap */ \ { 0xFFFD, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(1) }, /* ClusterRevision */ \ \ + /* Endpoint: 1, Cluster: Network Commissioning (server) */ \ + { 0x0000, ZAP_TYPE(INT8U), 1, 0, ZAP_EMPTY_DEFAULT() }, /* MaxNetworks */ \ + { 0x0001, ZAP_TYPE(ARRAY), 12, 0, ZAP_LONG_DEFAULTS_INDEX(656) }, /* Networks */ \ + { 0x0002, ZAP_TYPE(INT8U), 1, 0, ZAP_EMPTY_DEFAULT() }, /* ScanMaxTimeSeconds */ \ + { 0x0003, ZAP_TYPE(INT8U), 1, 0, ZAP_EMPTY_DEFAULT() }, /* ConnectMaxTimeSeconds */ \ + { 0x0004, ZAP_TYPE(BOOLEAN), 1, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_EMPTY_DEFAULT() }, /* InterfaceEnabled */ \ + { 0x0005, ZAP_TYPE(ENUM8), 1, 0, ZAP_EMPTY_DEFAULT() }, /* LastNetworkingStatus */ \ + { 0x0006, ZAP_TYPE(OCTET_STRING), 33, 0, ZAP_EMPTY_DEFAULT() }, /* LastNetworkID */ \ + { 0x0007, ZAP_TYPE(INT32U), 4, 0, ZAP_LONG_DEFAULTS_INDEX(668) }, /* LastConnectErrorValue */ \ + { 0xFFFC, ZAP_TYPE(BITMAP32), 4, 0, ZAP_LONG_DEFAULTS_INDEX(672) }, /* FeatureMap */ \ + { 0xFFFD, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(0x0001) }, /* ClusterRevision */ \ + \ /* Endpoint: 1, Cluster: Bridged Device Basic (server) */ \ { 0xFFFD, ZAP_TYPE(INT16U), 2, ZAP_ATTRIBUTE_MASK(SINGLETON), ZAP_SIMPLE_DEFAULT(1) }, /* ClusterRevision */ \ \ @@ -1743,11 +1777,11 @@ { 0x0000, ZAP_TYPE(INT8U), 1, 0, ZAP_SIMPLE_DEFAULT(2) }, /* number of positions */ \ { 0x0001, ZAP_TYPE(INT8U), 1, 0, ZAP_EMPTY_DEFAULT() }, /* current position */ \ { 0x0002, ZAP_TYPE(INT8U), 1, 0, ZAP_SIMPLE_DEFAULT(2) }, /* multi press max */ \ - { 0xFFFC, ZAP_TYPE(BITMAP32), 4, 0, ZAP_LONG_DEFAULTS_INDEX(656) }, /* FeatureMap */ \ + { 0xFFFC, ZAP_TYPE(BITMAP32), 4, 0, ZAP_LONG_DEFAULTS_INDEX(676) }, /* FeatureMap */ \ { 0xFFFD, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(0x0001) }, /* ClusterRevision */ \ \ /* Endpoint: 1, Cluster: Fixed Label (server) */ \ - { 0x0000, ZAP_TYPE(ARRAY), 254, 0, ZAP_LONG_DEFAULTS_INDEX(660) }, /* label list */ \ + { 0x0000, ZAP_TYPE(ARRAY), 254, 0, ZAP_LONG_DEFAULTS_INDEX(680) }, /* label list */ \ { 0xFFFD, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(0x0001) }, /* ClusterRevision */ \ \ /* Endpoint: 1, Cluster: User Label (server) */ \ @@ -1764,7 +1798,7 @@ { 0x0001, ZAP_TYPE(ARRAY), 0, ZAP_ATTRIBUTE_MASK(EXTERNAL_STORAGE), ZAP_SIMPLE_DEFAULT(0) }, /* SupportedModes */ \ { 0x0002, ZAP_TYPE(INT8U), 1, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_SIMPLE_DEFAULT(0) }, /* OnMode */ \ { 0x0003, ZAP_TYPE(INT8U), 1, 0, ZAP_SIMPLE_DEFAULT(0) }, /* StartUpMode */ \ - { 0x0004, ZAP_TYPE(CHAR_STRING), 33, 0, ZAP_LONG_DEFAULTS_INDEX(914) }, /* Description */ \ + { 0x0004, ZAP_TYPE(CHAR_STRING), 33, 0, ZAP_LONG_DEFAULTS_INDEX(934) }, /* Description */ \ { 0xFFFD, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(1) }, /* ClusterRevision */ \ \ /* Endpoint: 1, Cluster: Door Lock (server) */ \ @@ -1777,8 +1811,8 @@ { 0x0017, ZAP_TYPE(INT8U), 1, 0, ZAP_SIMPLE_DEFAULT(6) }, /* MaxPINCodeLength */ \ { 0x0018, ZAP_TYPE(INT8U), 1, 0, ZAP_SIMPLE_DEFAULT(6) }, /* MinPINCodeLength */ \ { 0x001B, ZAP_TYPE(BITMAP8), 1, 0, ZAP_SIMPLE_DEFAULT(1) }, /* CredentialRulesSupport */ \ - { 0x0021, ZAP_TYPE(CHAR_STRING), 4, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(921) }, /* Language */ \ - { 0x0023, ZAP_TYPE(INT32U), 4, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(924) }, /* AutoRelockTime */ \ + { 0x0021, ZAP_TYPE(CHAR_STRING), 4, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(941) }, /* Language */ \ + { 0x0023, ZAP_TYPE(INT32U), 4, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(944) }, /* AutoRelockTime */ \ { 0x0024, ZAP_TYPE(INT8U), 1, ZAP_ATTRIBUTE_MASK(MIN_MAX) | ZAP_ATTRIBUTE_MASK(WRITABLE), \ ZAP_MIN_MAX_DEFAULTS_INDEX(5) }, /* SoundVolume */ \ { 0x0025, ZAP_TYPE(ENUM8), 1, ZAP_ATTRIBUTE_MASK(MIN_MAX) | ZAP_ATTRIBUTE_MASK(WRITABLE), \ @@ -1813,7 +1847,7 @@ { 0x0017, ZAP_TYPE(BITMAP8), 1, ZAP_ATTRIBUTE_MASK(MIN_MAX) | ZAP_ATTRIBUTE_MASK(WRITABLE), \ ZAP_MIN_MAX_DEFAULTS_INDEX(9) }, /* Mode */ \ { 0x001A, ZAP_TYPE(BITMAP16), 2, 0, ZAP_SIMPLE_DEFAULT(0x00) }, /* SafetyStatus */ \ - { 0xFFFC, ZAP_TYPE(BITMAP32), 4, 0, ZAP_LONG_DEFAULTS_INDEX(928) }, /* FeatureMap */ \ + { 0xFFFC, ZAP_TYPE(BITMAP32), 4, 0, ZAP_LONG_DEFAULTS_INDEX(948) }, /* FeatureMap */ \ { 0xFFFD, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(5) }, /* ClusterRevision */ \ \ /* Endpoint: 1, Cluster: Barrier Control (server) */ \ @@ -1843,16 +1877,16 @@ { 0x0013, ZAP_TYPE(INT16S), 2, 0, ZAP_EMPTY_DEFAULT() }, /* Capacity */ \ { 0x0014, ZAP_TYPE(INT16U), 2, 0, ZAP_EMPTY_DEFAULT() }, /* Speed */ \ { 0x0015, ZAP_TYPE(INT24U), 3, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ - ZAP_LONG_DEFAULTS_INDEX(932) }, /* LifetimeRunningHours */ \ - { 0x0016, ZAP_TYPE(INT24U), 3, 0, ZAP_LONG_DEFAULTS_INDEX(935) }, /* Power */ \ + ZAP_LONG_DEFAULTS_INDEX(952) }, /* LifetimeRunningHours */ \ + { 0x0016, ZAP_TYPE(INT24U), 3, 0, ZAP_LONG_DEFAULTS_INDEX(955) }, /* Power */ \ { 0x0017, ZAP_TYPE(INT32U), 4, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ - ZAP_LONG_DEFAULTS_INDEX(938) }, /* LifetimeEnergyConsumed */ \ + ZAP_LONG_DEFAULTS_INDEX(958) }, /* LifetimeEnergyConsumed */ \ { 0x0020, ZAP_TYPE(ENUM8), 1, ZAP_ATTRIBUTE_MASK(MIN_MAX) | ZAP_ATTRIBUTE_MASK(WRITABLE), \ ZAP_MIN_MAX_DEFAULTS_INDEX(10) }, /* OperationMode */ \ { 0x0021, ZAP_TYPE(ENUM8), 1, ZAP_ATTRIBUTE_MASK(MIN_MAX) | ZAP_ATTRIBUTE_MASK(WRITABLE), \ ZAP_MIN_MAX_DEFAULTS_INDEX(11) }, /* ControlMode */ \ { 0x0022, ZAP_TYPE(BITMAP16), 2, 0, ZAP_EMPTY_DEFAULT() }, /* AlarmMask */ \ - { 0xFFFC, ZAP_TYPE(BITMAP32), 4, 0, ZAP_LONG_DEFAULTS_INDEX(942) }, /* FeatureMap */ \ + { 0xFFFC, ZAP_TYPE(BITMAP32), 4, 0, ZAP_LONG_DEFAULTS_INDEX(962) }, /* FeatureMap */ \ { 0xFFFD, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(0x0001) }, /* ClusterRevision */ \ \ /* Endpoint: 1, Cluster: Thermostat (server) */ \ @@ -1882,7 +1916,7 @@ { 0x0020, ZAP_TYPE(ENUM8), 1, 0, ZAP_SIMPLE_DEFAULT(0) }, /* start of week */ \ { 0x0021, ZAP_TYPE(INT8U), 1, 0, ZAP_SIMPLE_DEFAULT(7) }, /* number of weekly transitions */ \ { 0x0022, ZAP_TYPE(INT8U), 1, 0, ZAP_SIMPLE_DEFAULT(4) }, /* number of daily transitions */ \ - { 0xFFFC, ZAP_TYPE(BITMAP32), 4, 0, ZAP_LONG_DEFAULTS_INDEX(946) }, /* FeatureMap */ \ + { 0xFFFC, ZAP_TYPE(BITMAP32), 4, 0, ZAP_LONG_DEFAULTS_INDEX(966) }, /* FeatureMap */ \ { 0xFFFD, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(3) }, /* ClusterRevision */ \ \ /* Endpoint: 1, Cluster: Thermostat User Interface Configuration (server) */ \ @@ -2003,7 +2037,7 @@ { 0x0000, ZAP_TYPE(ENUM8), 1, 0, ZAP_SIMPLE_DEFAULT(0x00) }, /* zone state */ \ { 0x0001, ZAP_TYPE(ENUM16), 2, 0, ZAP_EMPTY_DEFAULT() }, /* zone type */ \ { 0x0002, ZAP_TYPE(BITMAP16), 2, 0, ZAP_SIMPLE_DEFAULT(0x0000) }, /* zone status */ \ - { 0x0010, ZAP_TYPE(NODE_ID), 8, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(950) }, /* IAS CIE address */ \ + { 0x0010, ZAP_TYPE(NODE_ID), 8, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(970) }, /* IAS CIE address */ \ { 0x0011, ZAP_TYPE(INT8U), 1, 0, ZAP_SIMPLE_DEFAULT(0xff) }, /* Zone ID */ \ { 0xFFFD, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(2) }, /* ClusterRevision */ \ \ @@ -2012,25 +2046,25 @@ { 0xFFFD, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(0x0001) }, /* ClusterRevision */ \ \ /* Endpoint: 1, Cluster: Channel (server) */ \ - { 0x0000, ZAP_TYPE(ARRAY), 254, 0, ZAP_LONG_DEFAULTS_INDEX(958) }, /* channel list */ \ + { 0x0000, ZAP_TYPE(ARRAY), 254, 0, ZAP_LONG_DEFAULTS_INDEX(978) }, /* channel list */ \ { 0xFFFD, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(0x0001) }, /* ClusterRevision */ \ \ /* Endpoint: 1, Cluster: Target Navigator (server) */ \ - { 0x0000, ZAP_TYPE(ARRAY), 254, 0, ZAP_LONG_DEFAULTS_INDEX(1212) }, /* target navigator list */ \ + { 0x0000, ZAP_TYPE(ARRAY), 254, 0, ZAP_LONG_DEFAULTS_INDEX(1232) }, /* target navigator list */ \ { 0x0001, ZAP_TYPE(INT8U), 1, 0, ZAP_SIMPLE_DEFAULT(0x00) }, /* current navigator target */ \ { 0xFFFD, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(0x0001) }, /* ClusterRevision */ \ \ /* Endpoint: 1, Cluster: Media Playback (server) */ \ { 0x0000, ZAP_TYPE(ENUM8), 1, 0, ZAP_SIMPLE_DEFAULT(0x00) }, /* playback state */ \ - { 0x0001, ZAP_TYPE(INT64U), 8, 0, ZAP_LONG_DEFAULTS_INDEX(1466) }, /* start time */ \ - { 0x0002, ZAP_TYPE(INT64U), 8, 0, ZAP_LONG_DEFAULTS_INDEX(1474) }, /* duration */ \ - { 0x0004, ZAP_TYPE(SINGLE), 4, 0, ZAP_LONG_DEFAULTS_INDEX(1482) }, /* playback speed */ \ - { 0x0005, ZAP_TYPE(INT64U), 8, 0, ZAP_LONG_DEFAULTS_INDEX(1486) }, /* seek range end */ \ - { 0x0006, ZAP_TYPE(INT64U), 8, 0, ZAP_LONG_DEFAULTS_INDEX(1494) }, /* seek range start */ \ + { 0x0001, ZAP_TYPE(INT64U), 8, 0, ZAP_LONG_DEFAULTS_INDEX(1486) }, /* start time */ \ + { 0x0002, ZAP_TYPE(INT64U), 8, 0, ZAP_LONG_DEFAULTS_INDEX(1494) }, /* duration */ \ + { 0x0004, ZAP_TYPE(SINGLE), 4, 0, ZAP_LONG_DEFAULTS_INDEX(1502) }, /* playback speed */ \ + { 0x0005, ZAP_TYPE(INT64U), 8, 0, ZAP_LONG_DEFAULTS_INDEX(1506) }, /* seek range end */ \ + { 0x0006, ZAP_TYPE(INT64U), 8, 0, ZAP_LONG_DEFAULTS_INDEX(1514) }, /* seek range start */ \ { 0xFFFD, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(0x0001) }, /* ClusterRevision */ \ \ /* Endpoint: 1, Cluster: Media Input (server) */ \ - { 0x0000, ZAP_TYPE(ARRAY), 254, 0, ZAP_LONG_DEFAULTS_INDEX(1502) }, /* media input list */ \ + { 0x0000, ZAP_TYPE(ARRAY), 254, 0, ZAP_LONG_DEFAULTS_INDEX(1522) }, /* media input list */ \ { 0x0001, ZAP_TYPE(INT8U), 1, 0, ZAP_SIMPLE_DEFAULT(0x00) }, /* current media input */ \ { 0xFFFD, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(0x0001) }, /* ClusterRevision */ \ \ @@ -2041,18 +2075,18 @@ { 0xFFFD, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(0x0001) }, /* ClusterRevision */ \ \ /* Endpoint: 1, Cluster: Content Launcher (server) */ \ - { 0x0000, ZAP_TYPE(ARRAY), 254, 0, ZAP_LONG_DEFAULTS_INDEX(1756) }, /* accept header list */ \ + { 0x0000, ZAP_TYPE(ARRAY), 254, 0, ZAP_LONG_DEFAULTS_INDEX(1776) }, /* accept header list */ \ { 0x0001, ZAP_TYPE(BITMAP32), 4, ZAP_ATTRIBUTE_MASK(WRITABLE), \ - ZAP_LONG_DEFAULTS_INDEX(2010) }, /* supported streaming protocols */ \ + ZAP_LONG_DEFAULTS_INDEX(2030) }, /* supported streaming protocols */ \ { 0xFFFD, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(0x0001) }, /* ClusterRevision */ \ \ /* Endpoint: 1, Cluster: Audio Output (server) */ \ - { 0x0000, ZAP_TYPE(ARRAY), 254, 0, ZAP_LONG_DEFAULTS_INDEX(2014) }, /* audio output list */ \ + { 0x0000, ZAP_TYPE(ARRAY), 254, 0, ZAP_LONG_DEFAULTS_INDEX(2034) }, /* audio output list */ \ { 0x0001, ZAP_TYPE(INT8U), 1, 0, ZAP_SIMPLE_DEFAULT(0x00) }, /* current audio output */ \ { 0xFFFD, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(0x0001) }, /* ClusterRevision */ \ \ /* Endpoint: 1, Cluster: Application Launcher (server) */ \ - { 0x0000, ZAP_TYPE(ARRAY), 254, 0, ZAP_LONG_DEFAULTS_INDEX(2268) }, /* application launcher list */ \ + { 0x0000, ZAP_TYPE(ARRAY), 254, 0, ZAP_LONG_DEFAULTS_INDEX(2288) }, /* application launcher list */ \ { 0xFFFD, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(0x0001) }, /* ClusterRevision */ \ \ /* Endpoint: 1, Cluster: Application Basic (server) */ \ @@ -2071,28 +2105,28 @@ { 0x0000, ZAP_TYPE(BOOLEAN), 1, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_SIMPLE_DEFAULT(false) }, /* boolean */ \ { 0x0001, ZAP_TYPE(BITMAP8), 1, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_SIMPLE_DEFAULT(0) }, /* bitmap8 */ \ { 0x0002, ZAP_TYPE(BITMAP16), 2, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_SIMPLE_DEFAULT(0) }, /* bitmap16 */ \ - { 0x0003, ZAP_TYPE(BITMAP32), 4, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2522) }, /* bitmap32 */ \ - { 0x0004, ZAP_TYPE(BITMAP64), 8, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2526) }, /* bitmap64 */ \ + { 0x0003, ZAP_TYPE(BITMAP32), 4, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2542) }, /* bitmap32 */ \ + { 0x0004, ZAP_TYPE(BITMAP64), 8, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2546) }, /* bitmap64 */ \ { 0x0005, ZAP_TYPE(INT8U), 1, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_SIMPLE_DEFAULT(0) }, /* int8u */ \ { 0x0006, ZAP_TYPE(INT16U), 2, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_SIMPLE_DEFAULT(0) }, /* int16u */ \ - { 0x0007, ZAP_TYPE(INT24U), 3, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2534) }, /* int24u */ \ - { 0x0008, ZAP_TYPE(INT32U), 4, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2537) }, /* int32u */ \ - { 0x0009, ZAP_TYPE(INT40U), 5, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2541) }, /* int40u */ \ - { 0x000A, ZAP_TYPE(INT48U), 6, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2546) }, /* int48u */ \ - { 0x000B, ZAP_TYPE(INT56U), 7, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2552) }, /* int56u */ \ - { 0x000C, ZAP_TYPE(INT64U), 8, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2559) }, /* int64u */ \ + { 0x0007, ZAP_TYPE(INT24U), 3, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2554) }, /* int24u */ \ + { 0x0008, ZAP_TYPE(INT32U), 4, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2557) }, /* int32u */ \ + { 0x0009, ZAP_TYPE(INT40U), 5, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2561) }, /* int40u */ \ + { 0x000A, ZAP_TYPE(INT48U), 6, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2566) }, /* int48u */ \ + { 0x000B, ZAP_TYPE(INT56U), 7, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2572) }, /* int56u */ \ + { 0x000C, ZAP_TYPE(INT64U), 8, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2579) }, /* int64u */ \ { 0x000D, ZAP_TYPE(INT8S), 1, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_SIMPLE_DEFAULT(0) }, /* int8s */ \ { 0x000E, ZAP_TYPE(INT16S), 2, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_SIMPLE_DEFAULT(0) }, /* int16s */ \ - { 0x000F, ZAP_TYPE(INT24S), 3, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2567) }, /* int24s */ \ - { 0x0010, ZAP_TYPE(INT32S), 4, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2570) }, /* int32s */ \ - { 0x0011, ZAP_TYPE(INT40S), 5, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2574) }, /* int40s */ \ - { 0x0012, ZAP_TYPE(INT48S), 6, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2579) }, /* int48s */ \ - { 0x0013, ZAP_TYPE(INT56S), 7, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2585) }, /* int56s */ \ - { 0x0014, ZAP_TYPE(INT64S), 8, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2592) }, /* int64s */ \ + { 0x000F, ZAP_TYPE(INT24S), 3, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2587) }, /* int24s */ \ + { 0x0010, ZAP_TYPE(INT32S), 4, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2590) }, /* int32s */ \ + { 0x0011, ZAP_TYPE(INT40S), 5, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2594) }, /* int40s */ \ + { 0x0012, ZAP_TYPE(INT48S), 6, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2599) }, /* int48s */ \ + { 0x0013, ZAP_TYPE(INT56S), 7, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2605) }, /* int56s */ \ + { 0x0014, ZAP_TYPE(INT64S), 8, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2612) }, /* int64s */ \ { 0x0015, ZAP_TYPE(ENUM8), 1, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_SIMPLE_DEFAULT(0) }, /* enum8 */ \ { 0x0016, ZAP_TYPE(ENUM16), 2, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_SIMPLE_DEFAULT(0) }, /* enum16 */ \ - { 0x0017, ZAP_TYPE(SINGLE), 4, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2600) }, /* float_single */ \ - { 0x0018, ZAP_TYPE(DOUBLE), 8, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2604) }, /* float_double */ \ + { 0x0017, ZAP_TYPE(SINGLE), 4, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2620) }, /* float_single */ \ + { 0x0018, ZAP_TYPE(DOUBLE), 8, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2624) }, /* float_double */ \ { 0x0019, ZAP_TYPE(OCTET_STRING), 11, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_EMPTY_DEFAULT() }, /* octet_string */ \ { 0x001A, ZAP_TYPE(ARRAY), 0, ZAP_ATTRIBUTE_MASK(EXTERNAL_STORAGE) | ZAP_ATTRIBUTE_MASK(WRITABLE), \ ZAP_EMPTY_DEFAULT() }, /* list_int8u */ \ @@ -2105,8 +2139,8 @@ { 0x001E, ZAP_TYPE(CHAR_STRING), 11, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_EMPTY_DEFAULT() }, /* char_string */ \ { 0x001F, ZAP_TYPE(LONG_CHAR_STRING), 1002, ZAP_ATTRIBUTE_MASK(WRITABLE), \ ZAP_EMPTY_DEFAULT() }, /* long_char_string */ \ - { 0x0020, ZAP_TYPE(EPOCH_US), 8, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2612) }, /* epoch_us */ \ - { 0x0021, ZAP_TYPE(EPOCH_S), 4, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2620) }, /* epoch_s */ \ + { 0x0020, ZAP_TYPE(EPOCH_US), 8, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2632) }, /* epoch_us */ \ + { 0x0021, ZAP_TYPE(EPOCH_S), 4, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_LONG_DEFAULTS_INDEX(2640) }, /* epoch_s */ \ { 0x0022, ZAP_TYPE(VENDOR_ID), 2, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_EMPTY_DEFAULT() }, /* vendor_id */ \ { 0x0023, ZAP_TYPE(ARRAY), 0, ZAP_ATTRIBUTE_MASK(EXTERNAL_STORAGE), \ ZAP_EMPTY_DEFAULT() }, /* list_nullables_and_optionals_struct */ \ @@ -2121,7 +2155,7 @@ ZAP_MIN_MAX_DEFAULTS_INDEX(33) }, /* range_restricted_int16u */ \ { 0x0029, ZAP_TYPE(INT16S), 2, ZAP_ATTRIBUTE_MASK(MIN_MAX) | ZAP_ATTRIBUTE_MASK(WRITABLE), \ ZAP_MIN_MAX_DEFAULTS_INDEX(34) }, /* range_restricted_int16s */ \ - { 0x002A, ZAP_TYPE(ARRAY), 1000, 0, ZAP_LONG_DEFAULTS_INDEX(2624) }, /* list_long_octet_string */ \ + { 0x002A, ZAP_TYPE(ARRAY), 1000, 0, ZAP_LONG_DEFAULTS_INDEX(2644) }, /* list_long_octet_string */ \ { 0x0030, ZAP_TYPE(BOOLEAN), 1, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(MUST_USE_TIMED_WRITE), \ ZAP_EMPTY_DEFAULT() }, /* timed_write_boolean */ \ { 0x8000, ZAP_TYPE(BOOLEAN), 1, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ @@ -2131,49 +2165,49 @@ { 0x8002, ZAP_TYPE(BITMAP16), 2, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ ZAP_SIMPLE_DEFAULT(0) }, /* nullable_bitmap16 */ \ { 0x8003, ZAP_TYPE(BITMAP32), 4, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ - ZAP_LONG_DEFAULTS_INDEX(3624) }, /* nullable_bitmap32 */ \ + ZAP_LONG_DEFAULTS_INDEX(3644) }, /* nullable_bitmap32 */ \ { 0x8004, ZAP_TYPE(BITMAP64), 8, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ - ZAP_LONG_DEFAULTS_INDEX(3628) }, /* nullable_bitmap64 */ \ + ZAP_LONG_DEFAULTS_INDEX(3648) }, /* nullable_bitmap64 */ \ { 0x8005, ZAP_TYPE(INT8U), 1, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ ZAP_SIMPLE_DEFAULT(0) }, /* nullable_int8u */ \ { 0x8006, ZAP_TYPE(INT16U), 2, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ ZAP_SIMPLE_DEFAULT(0) }, /* nullable_int16u */ \ { 0x8007, ZAP_TYPE(INT24U), 3, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ - ZAP_LONG_DEFAULTS_INDEX(3636) }, /* nullable_int24u */ \ + ZAP_LONG_DEFAULTS_INDEX(3656) }, /* nullable_int24u */ \ { 0x8008, ZAP_TYPE(INT32U), 4, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ - ZAP_LONG_DEFAULTS_INDEX(3639) }, /* nullable_int32u */ \ + ZAP_LONG_DEFAULTS_INDEX(3659) }, /* nullable_int32u */ \ { 0x8009, ZAP_TYPE(INT40U), 5, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ - ZAP_LONG_DEFAULTS_INDEX(3643) }, /* nullable_int40u */ \ + ZAP_LONG_DEFAULTS_INDEX(3663) }, /* nullable_int40u */ \ { 0x800A, ZAP_TYPE(INT48U), 6, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ - ZAP_LONG_DEFAULTS_INDEX(3648) }, /* nullable_int48u */ \ + ZAP_LONG_DEFAULTS_INDEX(3668) }, /* nullable_int48u */ \ { 0x800B, ZAP_TYPE(INT56U), 7, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ - ZAP_LONG_DEFAULTS_INDEX(3654) }, /* nullable_int56u */ \ + ZAP_LONG_DEFAULTS_INDEX(3674) }, /* nullable_int56u */ \ { 0x800C, ZAP_TYPE(INT64U), 8, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ - ZAP_LONG_DEFAULTS_INDEX(3661) }, /* nullable_int64u */ \ + ZAP_LONG_DEFAULTS_INDEX(3681) }, /* nullable_int64u */ \ { 0x800D, ZAP_TYPE(INT8S), 1, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ ZAP_SIMPLE_DEFAULT(0) }, /* nullable_int8s */ \ { 0x800E, ZAP_TYPE(INT16S), 2, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ ZAP_SIMPLE_DEFAULT(0) }, /* nullable_int16s */ \ { 0x800F, ZAP_TYPE(INT24S), 3, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ - ZAP_LONG_DEFAULTS_INDEX(3669) }, /* nullable_int24s */ \ + ZAP_LONG_DEFAULTS_INDEX(3689) }, /* nullable_int24s */ \ { 0x8010, ZAP_TYPE(INT32S), 4, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ - ZAP_LONG_DEFAULTS_INDEX(3672) }, /* nullable_int32s */ \ + ZAP_LONG_DEFAULTS_INDEX(3692) }, /* nullable_int32s */ \ { 0x8011, ZAP_TYPE(INT40S), 5, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ - ZAP_LONG_DEFAULTS_INDEX(3676) }, /* nullable_int40s */ \ + ZAP_LONG_DEFAULTS_INDEX(3696) }, /* nullable_int40s */ \ { 0x8012, ZAP_TYPE(INT48S), 6, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ - ZAP_LONG_DEFAULTS_INDEX(3681) }, /* nullable_int48s */ \ + ZAP_LONG_DEFAULTS_INDEX(3701) }, /* nullable_int48s */ \ { 0x8013, ZAP_TYPE(INT56S), 7, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ - ZAP_LONG_DEFAULTS_INDEX(3687) }, /* nullable_int56s */ \ + ZAP_LONG_DEFAULTS_INDEX(3707) }, /* nullable_int56s */ \ { 0x8014, ZAP_TYPE(INT64S), 8, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ - ZAP_LONG_DEFAULTS_INDEX(3694) }, /* nullable_int64s */ \ + ZAP_LONG_DEFAULTS_INDEX(3714) }, /* nullable_int64s */ \ { 0x8015, ZAP_TYPE(ENUM8), 1, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ ZAP_SIMPLE_DEFAULT(0) }, /* nullable_enum8 */ \ { 0x8016, ZAP_TYPE(ENUM16), 2, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ ZAP_SIMPLE_DEFAULT(0) }, /* nullable_enum16 */ \ { 0x8017, ZAP_TYPE(SINGLE), 4, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ - ZAP_LONG_DEFAULTS_INDEX(3702) }, /* nullable_float_single */ \ + ZAP_LONG_DEFAULTS_INDEX(3722) }, /* nullable_float_single */ \ { 0x8018, ZAP_TYPE(DOUBLE), 8, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ - ZAP_LONG_DEFAULTS_INDEX(3706) }, /* nullable_float_double */ \ + ZAP_LONG_DEFAULTS_INDEX(3726) }, /* nullable_float_double */ \ { 0x8019, ZAP_TYPE(OCTET_STRING), 11, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ ZAP_EMPTY_DEFAULT() }, /* nullable_octet_string */ \ { 0x801E, ZAP_TYPE(CHAR_STRING), 11, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ @@ -2198,8 +2232,8 @@ { 0xFFFD, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(0x0001) }, /* ClusterRevision */ \ \ /* Endpoint: 1, Cluster: Electrical Measurement (server) */ \ - { 0x0000, ZAP_TYPE(BITMAP32), 4, 0, ZAP_LONG_DEFAULTS_INDEX(3714) }, /* measurement type */ \ - { 0x0304, ZAP_TYPE(INT32S), 4, 0, ZAP_LONG_DEFAULTS_INDEX(3718) }, /* total active power */ \ + { 0x0000, ZAP_TYPE(BITMAP32), 4, 0, ZAP_LONG_DEFAULTS_INDEX(3734) }, /* measurement type */ \ + { 0x0304, ZAP_TYPE(INT32S), 4, 0, ZAP_LONG_DEFAULTS_INDEX(3738) }, /* total active power */ \ { 0x0505, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(0xffff) }, /* rms voltage */ \ { 0x0506, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(0x8000) }, /* rms voltage min */ \ { 0x0507, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(0x8000) }, /* rms voltage max */ \ @@ -2221,7 +2255,7 @@ { 0x4001, ZAP_TYPE(INT16U), 2, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_SIMPLE_DEFAULT(0) }, /* OnTime */ \ { 0x4002, ZAP_TYPE(INT16U), 2, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_SIMPLE_DEFAULT(0) }, /* OffWaitTime */ \ { 0x4003, ZAP_TYPE(ENUM8), 1, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_EMPTY_DEFAULT() }, /* StartUpOnOff */ \ - { 0xFFFC, ZAP_TYPE(BITMAP32), 4, 0, ZAP_LONG_DEFAULTS_INDEX(3722) }, /* FeatureMap */ \ + { 0xFFFC, ZAP_TYPE(BITMAP32), 4, 0, ZAP_LONG_DEFAULTS_INDEX(3742) }, /* FeatureMap */ \ { 0xFFFD, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(4) }, /* ClusterRevision */ \ \ /* Endpoint: 2, Cluster: Descriptor (server) */ \ @@ -2288,7 +2322,7 @@ }; #define ZAP_CLUSTER_MASK(mask) CLUSTER_MASK_##mask -#define GENERATED_CLUSTER_COUNT 72 +#define GENERATED_CLUSTER_COUNT 73 #define GENERATED_CLUSTERS \ { \ { 0x0003, \ @@ -2418,150 +2452,153 @@ 0x002F, ZAP_ATTRIBUTE_INDEX(237), 11, 88, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Power Source (server) */ \ { \ - 0x0039, ZAP_ATTRIBUTE_INDEX(248), 1, 2, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0031, ZAP_ATTRIBUTE_INDEX(248), 10, 60, ZAP_CLUSTER_MASK(SERVER), NULL \ + }, /* Endpoint: 1, Cluster: Network Commissioning (server) */ \ + { \ + 0x0039, ZAP_ATTRIBUTE_INDEX(258), 1, 2, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Bridged Device Basic (server) */ \ { \ - 0x003B, ZAP_ATTRIBUTE_INDEX(249), 5, 9, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x003B, ZAP_ATTRIBUTE_INDEX(259), 5, 9, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Switch (server) */ \ { \ - 0x0040, ZAP_ATTRIBUTE_INDEX(254), 2, 256, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0040, ZAP_ATTRIBUTE_INDEX(264), 2, 256, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Fixed Label (server) */ \ { \ - 0x0041, ZAP_ATTRIBUTE_INDEX(256), 2, 2, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0041, ZAP_ATTRIBUTE_INDEX(266), 2, 2, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: User Label (server) */ \ { \ - 0x0045, ZAP_ATTRIBUTE_INDEX(258), 2, 3, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0045, ZAP_ATTRIBUTE_INDEX(268), 2, 3, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Boolean State (server) */ \ { \ - 0x0050, ZAP_ATTRIBUTE_INDEX(260), 6, 38, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0050, ZAP_ATTRIBUTE_INDEX(270), 6, 38, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Mode Select (server) */ \ { 0x0101, \ - ZAP_ATTRIBUTE_INDEX(266), \ + ZAP_ATTRIBUTE_INDEX(276), \ 19, \ 29, \ ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(ATTRIBUTE_CHANGED_FUNCTION), \ chipFuncArrayDoorLockServer }, /* Endpoint: 1, Cluster: Door Lock (server) */ \ { \ - 0x0102, ZAP_ATTRIBUTE_INDEX(285), 20, 35, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0102, ZAP_ATTRIBUTE_INDEX(295), 20, 35, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Window Covering (server) */ \ { \ - 0x0103, ZAP_ATTRIBUTE_INDEX(305), 5, 7, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0103, ZAP_ATTRIBUTE_INDEX(315), 5, 7, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Barrier Control (server) */ \ { \ 0x0200, \ - ZAP_ATTRIBUTE_INDEX(310), \ + ZAP_ATTRIBUTE_INDEX(320), \ 26, \ 54, \ ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION) | ZAP_CLUSTER_MASK(ATTRIBUTE_CHANGED_FUNCTION), \ chipFuncArrayPumpConfigurationAndControlServer \ }, /* Endpoint: 1, Cluster: Pump Configuration and Control (server) */ \ { 0x0201, \ - ZAP_ATTRIBUTE_INDEX(336), \ + ZAP_ATTRIBUTE_INDEX(346), \ 19, \ 34, \ ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION), \ chipFuncArrayThermostatServer }, /* Endpoint: 1, Cluster: Thermostat (server) */ \ { \ 0x0204, \ - ZAP_ATTRIBUTE_INDEX(355), \ + ZAP_ATTRIBUTE_INDEX(365), \ 4, \ 5, \ ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(PRE_ATTRIBUTE_CHANGED_FUNCTION), \ chipFuncArrayThermostatUserInterfaceConfigurationServer \ }, /* Endpoint: 1, Cluster: Thermostat User Interface Configuration (server) */ \ { 0x0300, \ - ZAP_ATTRIBUTE_INDEX(359), \ + ZAP_ATTRIBUTE_INDEX(369), \ 53, \ 341, \ ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION), \ chipFuncArrayColorControlServer }, /* Endpoint: 1, Cluster: Color Control (server) */ \ { \ - 0x0400, ZAP_ATTRIBUTE_INDEX(412), 6, 11, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0400, ZAP_ATTRIBUTE_INDEX(422), 6, 11, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Illuminance Measurement (server) */ \ { \ - 0x0402, ZAP_ATTRIBUTE_INDEX(418), 5, 10, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0402, ZAP_ATTRIBUTE_INDEX(428), 5, 10, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Temperature Measurement (server) */ \ { \ - 0x0403, ZAP_ATTRIBUTE_INDEX(423), 4, 8, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0403, ZAP_ATTRIBUTE_INDEX(433), 4, 8, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Pressure Measurement (server) */ \ { \ - 0x0404, ZAP_ATTRIBUTE_INDEX(427), 5, 10, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0404, ZAP_ATTRIBUTE_INDEX(437), 5, 10, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Flow Measurement (server) */ \ { \ - 0x0405, ZAP_ATTRIBUTE_INDEX(432), 5, 10, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0405, ZAP_ATTRIBUTE_INDEX(442), 5, 10, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Relative Humidity Measurement (server) */ \ { 0x0406, \ - ZAP_ATTRIBUTE_INDEX(437), \ + ZAP_ATTRIBUTE_INDEX(447), \ 4, \ 5, \ ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION), \ chipFuncArrayOccupancySensingServer }, /* Endpoint: 1, Cluster: Occupancy Sensing (server) */ \ { 0x0500, \ - ZAP_ATTRIBUTE_INDEX(441), \ + ZAP_ATTRIBUTE_INDEX(451), \ 6, \ 16, \ ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION) | ZAP_CLUSTER_MASK(PRE_ATTRIBUTE_CHANGED_FUNCTION) | \ ZAP_CLUSTER_MASK(MESSAGE_SENT_FUNCTION), \ chipFuncArrayIasZoneServer }, /* Endpoint: 1, Cluster: IAS Zone (server) */ \ { \ - 0x0503, ZAP_ATTRIBUTE_INDEX(447), 2, 35, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0503, ZAP_ATTRIBUTE_INDEX(457), 2, 35, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Wake on LAN (server) */ \ { \ - 0x0504, ZAP_ATTRIBUTE_INDEX(449), 2, 256, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0504, ZAP_ATTRIBUTE_INDEX(459), 2, 256, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Channel (server) */ \ { \ - 0x0505, ZAP_ATTRIBUTE_INDEX(451), 3, 257, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0505, ZAP_ATTRIBUTE_INDEX(461), 3, 257, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Target Navigator (server) */ \ { \ - 0x0506, ZAP_ATTRIBUTE_INDEX(454), 7, 39, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0506, ZAP_ATTRIBUTE_INDEX(464), 7, 39, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Media Playback (server) */ \ { \ - 0x0507, ZAP_ATTRIBUTE_INDEX(461), 3, 257, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0507, ZAP_ATTRIBUTE_INDEX(471), 3, 257, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Media Input (server) */ \ { \ - 0x0508, ZAP_ATTRIBUTE_INDEX(464), 1, 2, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0508, ZAP_ATTRIBUTE_INDEX(474), 1, 2, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Low Power (server) */ \ { \ - 0x0509, ZAP_ATTRIBUTE_INDEX(465), 1, 2, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0509, ZAP_ATTRIBUTE_INDEX(475), 1, 2, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Keypad Input (server) */ \ { \ - 0x050A, ZAP_ATTRIBUTE_INDEX(466), 3, 260, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x050A, ZAP_ATTRIBUTE_INDEX(476), 3, 260, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Content Launcher (server) */ \ { \ - 0x050B, ZAP_ATTRIBUTE_INDEX(469), 3, 257, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x050B, ZAP_ATTRIBUTE_INDEX(479), 3, 257, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Audio Output (server) */ \ { \ - 0x050C, ZAP_ATTRIBUTE_INDEX(472), 2, 256, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x050C, ZAP_ATTRIBUTE_INDEX(482), 2, 256, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Application Launcher (server) */ \ { \ - 0x050D, ZAP_ATTRIBUTE_INDEX(474), 7, 106, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x050D, ZAP_ATTRIBUTE_INDEX(484), 7, 106, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Application Basic (server) */ \ { \ - 0x050E, ZAP_ATTRIBUTE_INDEX(481), 1, 2, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x050E, ZAP_ATTRIBUTE_INDEX(491), 1, 2, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Account Login (server) */ \ { \ - 0x050F, ZAP_ATTRIBUTE_INDEX(482), 78, 3285, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x050F, ZAP_ATTRIBUTE_INDEX(492), 78, 3285, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Test Cluster (server) */ \ { \ - 0x0B04, ZAP_ATTRIBUTE_INDEX(560), 12, 28, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0B04, ZAP_ATTRIBUTE_INDEX(570), 12, 28, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Electrical Measurement (server) */ \ { 0x0004, \ - ZAP_ATTRIBUTE_INDEX(572), \ + ZAP_ATTRIBUTE_INDEX(582), \ 2, \ 3, \ ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION), \ chipFuncArrayGroupsServer }, /* Endpoint: 2, Cluster: Groups (server) */ \ { 0x0006, \ - ZAP_ATTRIBUTE_INDEX(574), \ + ZAP_ATTRIBUTE_INDEX(584), \ 7, \ 13, \ ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION), \ chipFuncArrayOnOffServer }, /* Endpoint: 2, Cluster: On/Off (server) */ \ { \ - 0x001D, ZAP_ATTRIBUTE_INDEX(581), 5, 0, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x001D, ZAP_ATTRIBUTE_INDEX(591), 5, 0, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 2, Cluster: Descriptor (server) */ \ { 0x0406, \ - ZAP_ATTRIBUTE_INDEX(586), \ + ZAP_ATTRIBUTE_INDEX(596), \ 4, \ 5, \ ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION), \ @@ -2573,7 +2610,7 @@ // This is an array of EmberAfEndpointType structures. #define GENERATED_ENDPOINT_TYPES \ { \ - { ZAP_CLUSTER_INDEX(0), 23, 1465 }, { ZAP_CLUSTER_INDEX(23), 45, 6078 }, { ZAP_CLUSTER_INDEX(68), 4, 21 }, \ + { ZAP_CLUSTER_INDEX(0), 23, 1465 }, { ZAP_CLUSTER_INDEX(23), 46, 6138 }, { ZAP_CLUSTER_INDEX(69), 4, 21 }, \ } // Largest attribute size is needed for various buffers @@ -2583,7 +2620,7 @@ #define ATTRIBUTE_SINGLETONS_SIZE (689) // Total size of attribute storage -#define ATTRIBUTE_MAX_SIZE (7564) +#define ATTRIBUTE_MAX_SIZE (7624) // Number of fixed endpoints #define FIXED_ENDPOINT_COUNT (3) diff --git a/zzz_generated/all-clusters-app/zap-generated/gen_config.h b/zzz_generated/all-clusters-app/zap-generated/gen_config.h index f3d653e43ab817..0484734aa48c82 100644 --- a/zzz_generated/all-clusters-app/zap-generated/gen_config.h +++ b/zzz_generated/all-clusters-app/zap-generated/gen_config.h @@ -65,7 +65,7 @@ #define EMBER_AF_MEDIA_INPUT_CLUSTER_SERVER_ENDPOINT_COUNT (1) #define EMBER_AF_MEDIA_PLAYBACK_CLUSTER_SERVER_ENDPOINT_COUNT (1) #define EMBER_AF_MODE_SELECT_CLUSTER_SERVER_ENDPOINT_COUNT (1) -#define EMBER_AF_NETWORK_COMMISSIONING_CLUSTER_SERVER_ENDPOINT_COUNT (1) +#define EMBER_AF_NETWORK_COMMISSIONING_CLUSTER_SERVER_ENDPOINT_COUNT (2) #define EMBER_AF_OTA_PROVIDER_CLUSTER_SERVER_ENDPOINT_COUNT (1) #define EMBER_AF_OTA_REQUESTOR_CLUSTER_SERVER_ENDPOINT_COUNT (1) #define EMBER_AF_OCCUPANCY_SENSING_CLUSTER_SERVER_ENDPOINT_COUNT (2) diff --git a/zzz_generated/app-common/app-common/zap-generated/af-structs.h b/zzz_generated/app-common/app-common/zap-generated/af-structs.h index f4537e7123e05a..f8b76f34721cba 100644 --- a/zzz_generated/app-common/app-common/zap-generated/af-structs.h +++ b/zzz_generated/app-common/app-common/zap-generated/af-structs.h @@ -547,8 +547,8 @@ typedef struct _WiFiInterfaceScanResult uint8_t Security; chip::ByteSpan SSID; chip::ByteSpan BSSID; - uint8_t Channel; - uint32_t WiFiBand; + uint16_t Channel; + uint8_t WiFiBand; int8_t RSSI; } WiFiInterfaceScanResult; 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 0754d9e47499a1..1ea87e95ed5316 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 @@ -9959,6 +9959,15 @@ enum class NetworkCommissioningStatus : uint8_t kIPBindFailed = 0x0B, kUnknownError = 0x0C, }; +// Enum for WiFiBand +enum class WiFiBand : uint8_t +{ + k2g4 = 0x00, + k3g65 = 0x01, + k5g = 0x02, + k6g = 0x03, + k60g = 0x04, +}; namespace Structs { namespace NetworkInfo { @@ -10030,8 +10039,8 @@ struct Type uint8_t security; chip::ByteSpan ssid; chip::ByteSpan bssid; - uint8_t channel; - uint32_t wiFiBand; + uint16_t channel; + WiFiBand wiFiBand; int8_t rssi; CHIP_ERROR Encode(TLV::TLVWriter & writer, TLV::Tag tag) const;