From 77fd34991a8f6919a0240c34902f21fcd3daa3bc Mon Sep 17 00:00:00 2001 From: Rohit Jadhav Date: Thu, 3 Oct 2024 15:43:50 +0530 Subject: [PATCH] examples/light_switch: Add demonstration of auto subscription from on/off client to remote on/off server --- examples/light_switch/README.md | 5 +- examples/light_switch/main/Kconfig.projbuild | 7 ++ examples/light_switch/main/app_driver.cpp | 109 +++++++++++++++---- examples/light_switch/main/app_main.cpp | 47 ++++++++ examples/light_switch/sdkconfig.defaults | 3 + 5 files changed, 148 insertions(+), 23 deletions(-) diff --git a/examples/light_switch/README.md b/examples/light_switch/README.md index 59e821964..0cd34a172 100644 --- a/examples/light_switch/README.md +++ b/examples/light_switch/README.md @@ -9,7 +9,10 @@ See the [docs](https://docs.espressif.com/projects/esp-matter/en/latest/esp32/de ## 1. Additional Environment Setup -No additional setup is required. +This example demonstrates auto subscription to only one remote on/off server from on/off client after binding of light to switch. +For example, switch that have a led indicator which indicates the on-off state of the bound light. The subscription can keep the indicator on the switch sync with the light node. +- Enable SUBSCRIBE_TO_ON_OFF_SERVER_AFTER_BINDING to enable this funcationality. +Please check [Bind light to switch](#21-bind-light-to-switch) for more details. ## 2. Post Commissioning Setup diff --git a/examples/light_switch/main/Kconfig.projbuild b/examples/light_switch/main/Kconfig.projbuild index 0db7e6d9f..6e3d76f02 100644 --- a/examples/light_switch/main/Kconfig.projbuild +++ b/examples/light_switch/main/Kconfig.projbuild @@ -1,4 +1,5 @@ menu "Example Configuration" + visible if CUSTOM_COMMISSIONABLE_DATA_PROVIDER config DYNAMIC_PASSCODE_COMMISSIONABLE_DATA_PROVIDER @@ -29,4 +30,10 @@ menu "Example Configuration" help Fixed salt in custom dynamic passcode commissionable data provider. It should be a Base64-Encoded string. + config SUBSCRIBE_TO_ON_OFF_SERVER_AFTER_BINDING + bool "Enable subscribe to on/off server after binding" + default n + help + "Enables auto subscription to on/off server from client on change in binding" + endmenu diff --git a/examples/light_switch/main/app_driver.cpp b/examples/light_switch/main/app_driver.cpp index 8d77c29f8..9c424569c 100644 --- a/examples/light_switch/main/app_driver.cpp +++ b/examples/light_switch/main/app_driver.cpp @@ -23,6 +23,12 @@ #include #include +#ifdef CONFIG_SUBSCRIBE_TO_ON_OFF_SERVER_AFTER_BINDING +#include +#include +#include +#endif + using chip::kInvalidClusterId; static constexpr chip::CommandId kInvalidCommandId = 0xFFFF'FFFF; @@ -33,6 +39,53 @@ using namespace esp_matter::cluster; static const char *TAG = "app_driver"; extern uint16_t switch_endpoint_id; +#ifdef CONFIG_SUBSCRIBE_TO_ON_OFF_SERVER_AFTER_BINDING +class MyReadClientCallback : public chip::app::ReadClient::Callback { +public: + void OnAttributeData(const chip::app::ConcreteDataAttributePath &aPath, + chip::TLV::TLVReader *aReader, + const chip::app::StatusIB &aStatus) override { + // Handle the attribute data + if (aPath.mClusterId == chip::app::Clusters::OnOff::Id) { + if (aPath.mAttributeId == chip::app::Clusters::OnOff::Attributes::OnOff::Id) { + ESP_LOGI(TAG, "Received OnOff attribute"); + } + } + } + + void OnEventData(const chip::app::EventHeader &aEventHeader, chip::TLV::TLVReader * apData, + const chip::app::StatusIB *aStatus) override { + // Handle event data + } + + void OnError(CHIP_ERROR aError) override { + // Handle the error + ESP_LOGI(TAG, "ReadClient Error: %s", ErrorStr(aError)); + } + + void OnDone(chip::app::ReadClient * apReadClient) override { + // Cleanup after done + ESP_LOGI(TAG, "ReadClient Done"); + } +}; +MyReadClientCallback readClientCb; + +void app_client_subscribe_command_callback(client::peer_device_t *peer_device, client::request_handle_t *req_handle, + void *priv_data) +{ + uint16_t min_interval = 5; + uint16_t max_interval = 10; + bool keep_subscription = true; + bool auto_resubscribe = true; + chip::Platform::ScopedMemoryBufferWithSize attrb_path; + attrb_path.Alloc(1); + client::interaction::subscribe::send_request(peer_device, &req_handle->attribute_path, attrb_path.AllocatedSize(), + &req_handle->event_path, 0, min_interval, max_interval, keep_subscription, + auto_resubscribe, readClientCb); +} + +#endif + #if CONFIG_ENABLE_CHIP_SHELL static char console_buffer[101] = {0}; static esp_err_t app_driver_bound_console_handler(int argc, char **argv) @@ -180,34 +233,46 @@ static void send_command_failure_callback(void *context, CHIP_ERROR error) void app_driver_client_invoke_command_callback(client::peer_device_t *peer_device, client::request_handle_t *req_handle, void *priv_data) { - if (req_handle->type != esp_matter::client::INVOKE_CMD) { - return; - } - char command_data_str[32]; - // on_off light switch should support on_off cluster and identify cluster commands sending. - if (req_handle->command_path.mClusterId == OnOff::Id) { - strcpy(command_data_str, "{}"); - } else if (req_handle->command_path.mClusterId == Identify::Id) { - if (req_handle->command_path.mCommandId == Identify::Commands::Identify::Id) { - if (((char *)req_handle->request_data)[0] != 1) { - ESP_LOGE(TAG, "Number of parameters error"); + if (req_handle->type == esp_matter::client::INVOKE_CMD) { + char command_data_str[32]; + // on_off light switch should support on_off cluster and identify cluster commands sending. + if (req_handle->command_path.mClusterId == OnOff::Id) { + strcpy(command_data_str, "{}"); + } else if (req_handle->command_path.mClusterId == Identify::Id) { + if (req_handle->command_path.mCommandId == Identify::Commands::Identify::Id) { + if (((char *)req_handle->request_data)[0] != 1) { + ESP_LOGE(TAG, "Number of parameters error"); + return; + } + snprintf(command_data_str, sizeof(command_data_str), "{\"0:U16\": %ld}", + strtoul((const char *)(req_handle->request_data) + 1, NULL, 16)); + } else { + ESP_LOGE(TAG, "Unsupported command"); return; } - sprintf(command_data_str, "{\"0:U16\": %ld}", - strtoul((const char *)(req_handle->request_data) + 1, NULL, 16)); } else { - ESP_LOGE(TAG, "Unsupported command"); + ESP_LOGE(TAG, "Unsupported cluster"); return; } - } else { - ESP_LOGE(TAG, "Unsupported cluster"); - return; + client::interaction::invoke::send_request(NULL, peer_device, req_handle->command_path, command_data_str, + send_command_success_callback, send_command_failure_callback, + chip::NullOptional); } - client::interaction::invoke::send_request(NULL, peer_device, req_handle->command_path, command_data_str, - send_command_success_callback, send_command_failure_callback, - chip::NullOptional); + return; } +void app_driver_client_callback(client::peer_device_t *peer_device, client::request_handle_t *req_handle, + void *priv_data) +{ + if (req_handle->type == esp_matter::client::INVOKE_CMD) { + app_driver_client_invoke_command_callback(peer_device, req_handle, priv_data); +#ifdef CONFIG_SUBSCRIBE_TO_ON_OFF_SERVER_AFTER_BINDING + } else if (req_handle->type == esp_matter::client::SUBSCRIBE_ATTR) { + app_client_subscribe_command_callback(peer_device, req_handle, priv_data); +#endif + } + return; +} void app_driver_client_group_invoke_command_callback(uint8_t fabric_index, client::request_handle_t *req_handle, void *priv_data) { @@ -224,7 +289,7 @@ void app_driver_client_group_invoke_command_callback(uint8_t fabric_index, clien ESP_LOGE(TAG, "Number of parameters error"); return; } - sprintf(command_data_str, "{\"0:U16\": %ld}", + snprintf(command_data_str, sizeof(command_data_str), "{\"0:U16\": %ld}", strtoul((const char *)(req_handle->request_data) + 1, NULL, 16)); } else { ESP_LOGE(TAG, "Unsupported command"); @@ -262,7 +327,7 @@ app_driver_handle_t app_driver_switch_init() #if CONFIG_ENABLE_CHIP_SHELL app_driver_register_commands(); #endif // CONFIG_ENABLE_CHIP_SHELL - client::set_request_callback(app_driver_client_invoke_command_callback, + client::set_request_callback(app_driver_client_callback, app_driver_client_group_invoke_command_callback, NULL); return (app_driver_handle_t)btns[0]; diff --git a/examples/light_switch/main/app_main.cpp b/examples/light_switch/main/app_main.cpp index f8d519409..82c538420 100644 --- a/examples/light_switch/main/app_main.cpp +++ b/examples/light_switch/main/app_main.cpp @@ -18,6 +18,14 @@ #include #include #include +#if CONFIG_SUBSCRIBE_TO_ON_OFF_SERVER_AFTER_BINDING +#include +#include +#include +#include +#include +#include +#endif #if CHIP_DEVICE_CONFIG_ENABLE_THREAD #include #endif @@ -39,6 +47,10 @@ using namespace esp_matter::endpoint; dynamic_commissionable_data_provider g_dynamic_passcode_provider; #endif +#if CONFIG_SUBSCRIBE_TO_ON_OFF_SERVER_AFTER_BINDING +static bool do_subscribe = true; +#endif + static void app_event_cb(const ChipDeviceEvent *event, intptr_t arg) { switch (event->Type) { @@ -70,6 +82,41 @@ static void app_event_cb(const ChipDeviceEvent *event, intptr_t arg) ESP_LOGI(TAG, "Commissioning window closed"); break; + case chip::DeviceLayer::DeviceEventType::kBindingsChangedViaCluster: { + ESP_LOGI(TAG, "Binding entry changed"); +#if CONFIG_SUBSCRIBE_TO_ON_OFF_SERVER_AFTER_BINDING + if(do_subscribe) { + for (const auto & binding : chip::BindingTable::GetInstance()) + { + ESP_LOGI( + TAG, + "Read cached binding type=%d fabrixIndex=%d nodeId=0x" ChipLogFormatX64 + " groupId=%d local endpoint=%d remote endpoint=%d cluster=" ChipLogFormatMEI, + binding.type, binding.fabricIndex, ChipLogValueX64(binding.nodeId), binding.groupId, binding.local, + binding.remote, ChipLogValueMEI(binding.clusterId.value_or(0))); + if (binding.type == MATTER_UNICAST_BINDING && event->BindingsChanged.fabricIndex == binding.fabricIndex) + { + ESP_LOGI( + TAG, + "Matched accessingFabricIndex with nodeId=0x" ChipLogFormatX64, + ChipLogValueX64(binding.nodeId)); + + uint32_t attribute_id = chip::app::Clusters::OnOff::Attributes::OnOff::Id; + client::request_handle_t req_handle; + req_handle.type = esp_matter::client::SUBSCRIBE_ATTR; + req_handle.attribute_path = {binding.remote, binding.clusterId.value(), attribute_id}; + auto &server = chip::Server::GetInstance(); + client::connect(server.GetCASESessionManager(), binding.fabricIndex, binding.nodeId, &req_handle); + break; + } + } + do_subscribe = false; + } +#endif + } + break; + + default: break; } diff --git a/examples/light_switch/sdkconfig.defaults b/examples/light_switch/sdkconfig.defaults index cb41938f1..7bb1601b2 100644 --- a/examples/light_switch/sdkconfig.defaults +++ b/examples/light_switch/sdkconfig.defaults @@ -40,3 +40,6 @@ CONFIG_MBEDTLS_HKDF_C=y # Increase LwIP IPv6 address number to 6 (MAX_FABRIC + 1) # unique local addresses for fabrics(MAX_FABRIC), a link local address(1) CONFIG_LWIP_IPV6_NUM_ADDRESSES=6 + +# Buttons +CONFIG_BSP_BUTTONS_NUM=1