mirror of
https://github.com/espressif/esp-matter.git
synced 2026-04-27 19:13:13 +00:00
examples/light_switch: Add demonstration of auto subscription from on/off client to remote on/off server
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -23,6 +23,12 @@
|
||||
#include <app/server/Server.h>
|
||||
#include <lib/core/Optional.h>
|
||||
|
||||
#ifdef CONFIG_SUBSCRIBE_TO_ON_OFF_SERVER_AFTER_BINDING
|
||||
#include <app/AttributePathParams.h>
|
||||
#include <app/ConcreteAttributePath.h>
|
||||
#include <lib/core/TLVReader.h>
|
||||
#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<chip::app::AttributePathParams> 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];
|
||||
|
||||
@@ -18,6 +18,14 @@
|
||||
#include <common_macros.h>
|
||||
#include <app_priv.h>
|
||||
#include <app_reset.h>
|
||||
#if CONFIG_SUBSCRIBE_TO_ON_OFF_SERVER_AFTER_BINDING
|
||||
#include <app/util/binding-table.h>
|
||||
#include <esp_matter_client.h>
|
||||
#include <app/AttributePathParams.h>
|
||||
#include <app/ConcreteAttributePath.h>
|
||||
#include <lib/core/TLVReader.h>
|
||||
#include <app/server/Server.h>
|
||||
#endif
|
||||
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
|
||||
#include <platform/ESP32/OpenthreadLauncher.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user