Skip to content

Commit 77fd349

Browse files
examples/light_switch: Add demonstration of auto subscription from on/off client to remote on/off server
1 parent 305d81f commit 77fd349

File tree

5 files changed

+148
-23
lines changed

5 files changed

+148
-23
lines changed

examples/light_switch/README.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ See the [docs](https://docs.espressif.com/projects/esp-matter/en/latest/esp32/de
99

1010
## 1. Additional Environment Setup
1111

12-
No additional setup is required.
12+
This example demonstrates auto subscription to only one remote on/off server from on/off client after binding of light to switch.
13+
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.
14+
- Enable SUBSCRIBE_TO_ON_OFF_SERVER_AFTER_BINDING to enable this funcationality.
15+
Please check [Bind light to switch](#21-bind-light-to-switch) for more details.
1316

1417
## 2. Post Commissioning Setup
1518

examples/light_switch/main/Kconfig.projbuild

+7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
menu "Example Configuration"
2+
23
visible if CUSTOM_COMMISSIONABLE_DATA_PROVIDER
34

45
config DYNAMIC_PASSCODE_COMMISSIONABLE_DATA_PROVIDER
@@ -29,4 +30,10 @@ menu "Example Configuration"
2930
help
3031
Fixed salt in custom dynamic passcode commissionable data provider. It should be a Base64-Encoded string.
3132

33+
config SUBSCRIBE_TO_ON_OFF_SERVER_AFTER_BINDING
34+
bool "Enable subscribe to on/off server after binding"
35+
default n
36+
help
37+
"Enables auto subscription to on/off server from client on change in binding"
38+
3239
endmenu

examples/light_switch/main/app_driver.cpp

+87-22
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@
2323
#include <app/server/Server.h>
2424
#include <lib/core/Optional.h>
2525

26+
#ifdef CONFIG_SUBSCRIBE_TO_ON_OFF_SERVER_AFTER_BINDING
27+
#include <app/AttributePathParams.h>
28+
#include <app/ConcreteAttributePath.h>
29+
#include <lib/core/TLVReader.h>
30+
#endif
31+
2632
using chip::kInvalidClusterId;
2733
static constexpr chip::CommandId kInvalidCommandId = 0xFFFF'FFFF;
2834

@@ -33,6 +39,53 @@ using namespace esp_matter::cluster;
3339
static const char *TAG = "app_driver";
3440
extern uint16_t switch_endpoint_id;
3541

42+
#ifdef CONFIG_SUBSCRIBE_TO_ON_OFF_SERVER_AFTER_BINDING
43+
class MyReadClientCallback : public chip::app::ReadClient::Callback {
44+
public:
45+
void OnAttributeData(const chip::app::ConcreteDataAttributePath &aPath,
46+
chip::TLV::TLVReader *aReader,
47+
const chip::app::StatusIB &aStatus) override {
48+
// Handle the attribute data
49+
if (aPath.mClusterId == chip::app::Clusters::OnOff::Id) {
50+
if (aPath.mAttributeId == chip::app::Clusters::OnOff::Attributes::OnOff::Id) {
51+
ESP_LOGI(TAG, "Received OnOff attribute");
52+
}
53+
}
54+
}
55+
56+
void OnEventData(const chip::app::EventHeader &aEventHeader, chip::TLV::TLVReader * apData,
57+
const chip::app::StatusIB *aStatus) override {
58+
// Handle event data
59+
}
60+
61+
void OnError(CHIP_ERROR aError) override {
62+
// Handle the error
63+
ESP_LOGI(TAG, "ReadClient Error: %s", ErrorStr(aError));
64+
}
65+
66+
void OnDone(chip::app::ReadClient * apReadClient) override {
67+
// Cleanup after done
68+
ESP_LOGI(TAG, "ReadClient Done");
69+
}
70+
};
71+
MyReadClientCallback readClientCb;
72+
73+
void app_client_subscribe_command_callback(client::peer_device_t *peer_device, client::request_handle_t *req_handle,
74+
void *priv_data)
75+
{
76+
uint16_t min_interval = 5;
77+
uint16_t max_interval = 10;
78+
bool keep_subscription = true;
79+
bool auto_resubscribe = true;
80+
chip::Platform::ScopedMemoryBufferWithSize<chip::app::AttributePathParams> attrb_path;
81+
attrb_path.Alloc(1);
82+
client::interaction::subscribe::send_request(peer_device, &req_handle->attribute_path, attrb_path.AllocatedSize(),
83+
&req_handle->event_path, 0, min_interval, max_interval, keep_subscription,
84+
auto_resubscribe, readClientCb);
85+
}
86+
87+
#endif
88+
3689
#if CONFIG_ENABLE_CHIP_SHELL
3790
static char console_buffer[101] = {0};
3891
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)
180233
void app_driver_client_invoke_command_callback(client::peer_device_t *peer_device, client::request_handle_t *req_handle,
181234
void *priv_data)
182235
{
183-
if (req_handle->type != esp_matter::client::INVOKE_CMD) {
184-
return;
185-
}
186-
char command_data_str[32];
187-
// on_off light switch should support on_off cluster and identify cluster commands sending.
188-
if (req_handle->command_path.mClusterId == OnOff::Id) {
189-
strcpy(command_data_str, "{}");
190-
} else if (req_handle->command_path.mClusterId == Identify::Id) {
191-
if (req_handle->command_path.mCommandId == Identify::Commands::Identify::Id) {
192-
if (((char *)req_handle->request_data)[0] != 1) {
193-
ESP_LOGE(TAG, "Number of parameters error");
236+
if (req_handle->type == esp_matter::client::INVOKE_CMD) {
237+
char command_data_str[32];
238+
// on_off light switch should support on_off cluster and identify cluster commands sending.
239+
if (req_handle->command_path.mClusterId == OnOff::Id) {
240+
strcpy(command_data_str, "{}");
241+
} else if (req_handle->command_path.mClusterId == Identify::Id) {
242+
if (req_handle->command_path.mCommandId == Identify::Commands::Identify::Id) {
243+
if (((char *)req_handle->request_data)[0] != 1) {
244+
ESP_LOGE(TAG, "Number of parameters error");
245+
return;
246+
}
247+
snprintf(command_data_str, sizeof(command_data_str), "{\"0:U16\": %ld}",
248+
strtoul((const char *)(req_handle->request_data) + 1, NULL, 16));
249+
} else {
250+
ESP_LOGE(TAG, "Unsupported command");
194251
return;
195252
}
196-
sprintf(command_data_str, "{\"0:U16\": %ld}",
197-
strtoul((const char *)(req_handle->request_data) + 1, NULL, 16));
198253
} else {
199-
ESP_LOGE(TAG, "Unsupported command");
254+
ESP_LOGE(TAG, "Unsupported cluster");
200255
return;
201256
}
202-
} else {
203-
ESP_LOGE(TAG, "Unsupported cluster");
204-
return;
257+
client::interaction::invoke::send_request(NULL, peer_device, req_handle->command_path, command_data_str,
258+
send_command_success_callback, send_command_failure_callback,
259+
chip::NullOptional);
205260
}
206-
client::interaction::invoke::send_request(NULL, peer_device, req_handle->command_path, command_data_str,
207-
send_command_success_callback, send_command_failure_callback,
208-
chip::NullOptional);
261+
return;
209262
}
210263

264+
void app_driver_client_callback(client::peer_device_t *peer_device, client::request_handle_t *req_handle,
265+
void *priv_data)
266+
{
267+
if (req_handle->type == esp_matter::client::INVOKE_CMD) {
268+
app_driver_client_invoke_command_callback(peer_device, req_handle, priv_data);
269+
#ifdef CONFIG_SUBSCRIBE_TO_ON_OFF_SERVER_AFTER_BINDING
270+
} else if (req_handle->type == esp_matter::client::SUBSCRIBE_ATTR) {
271+
app_client_subscribe_command_callback(peer_device, req_handle, priv_data);
272+
#endif
273+
}
274+
return;
275+
}
211276
void app_driver_client_group_invoke_command_callback(uint8_t fabric_index, client::request_handle_t *req_handle,
212277
void *priv_data)
213278
{
@@ -224,7 +289,7 @@ void app_driver_client_group_invoke_command_callback(uint8_t fabric_index, clien
224289
ESP_LOGE(TAG, "Number of parameters error");
225290
return;
226291
}
227-
sprintf(command_data_str, "{\"0:U16\": %ld}",
292+
snprintf(command_data_str, sizeof(command_data_str), "{\"0:U16\": %ld}",
228293
strtoul((const char *)(req_handle->request_data) + 1, NULL, 16));
229294
} else {
230295
ESP_LOGE(TAG, "Unsupported command");
@@ -262,7 +327,7 @@ app_driver_handle_t app_driver_switch_init()
262327
#if CONFIG_ENABLE_CHIP_SHELL
263328
app_driver_register_commands();
264329
#endif // CONFIG_ENABLE_CHIP_SHELL
265-
client::set_request_callback(app_driver_client_invoke_command_callback,
330+
client::set_request_callback(app_driver_client_callback,
266331
app_driver_client_group_invoke_command_callback, NULL);
267332

268333
return (app_driver_handle_t)btns[0];

examples/light_switch/main/app_main.cpp

+47
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@
1818
#include <common_macros.h>
1919
#include <app_priv.h>
2020
#include <app_reset.h>
21+
#if CONFIG_SUBSCRIBE_TO_ON_OFF_SERVER_AFTER_BINDING
22+
#include <app/util/binding-table.h>
23+
#include <esp_matter_client.h>
24+
#include <app/AttributePathParams.h>
25+
#include <app/ConcreteAttributePath.h>
26+
#include <lib/core/TLVReader.h>
27+
#include <app/server/Server.h>
28+
#endif
2129
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
2230
#include <platform/ESP32/OpenthreadLauncher.h>
2331
#endif
@@ -39,6 +47,10 @@ using namespace esp_matter::endpoint;
3947
dynamic_commissionable_data_provider g_dynamic_passcode_provider;
4048
#endif
4149

50+
#if CONFIG_SUBSCRIBE_TO_ON_OFF_SERVER_AFTER_BINDING
51+
static bool do_subscribe = true;
52+
#endif
53+
4254
static void app_event_cb(const ChipDeviceEvent *event, intptr_t arg)
4355
{
4456
switch (event->Type) {
@@ -70,6 +82,41 @@ static void app_event_cb(const ChipDeviceEvent *event, intptr_t arg)
7082
ESP_LOGI(TAG, "Commissioning window closed");
7183
break;
7284

85+
case chip::DeviceLayer::DeviceEventType::kBindingsChangedViaCluster: {
86+
ESP_LOGI(TAG, "Binding entry changed");
87+
#if CONFIG_SUBSCRIBE_TO_ON_OFF_SERVER_AFTER_BINDING
88+
if(do_subscribe) {
89+
for (const auto & binding : chip::BindingTable::GetInstance())
90+
{
91+
ESP_LOGI(
92+
TAG,
93+
"Read cached binding type=%d fabrixIndex=%d nodeId=0x" ChipLogFormatX64
94+
" groupId=%d local endpoint=%d remote endpoint=%d cluster=" ChipLogFormatMEI,
95+
binding.type, binding.fabricIndex, ChipLogValueX64(binding.nodeId), binding.groupId, binding.local,
96+
binding.remote, ChipLogValueMEI(binding.clusterId.value_or(0)));
97+
if (binding.type == MATTER_UNICAST_BINDING && event->BindingsChanged.fabricIndex == binding.fabricIndex)
98+
{
99+
ESP_LOGI(
100+
TAG,
101+
"Matched accessingFabricIndex with nodeId=0x" ChipLogFormatX64,
102+
ChipLogValueX64(binding.nodeId));
103+
104+
uint32_t attribute_id = chip::app::Clusters::OnOff::Attributes::OnOff::Id;
105+
client::request_handle_t req_handle;
106+
req_handle.type = esp_matter::client::SUBSCRIBE_ATTR;
107+
req_handle.attribute_path = {binding.remote, binding.clusterId.value(), attribute_id};
108+
auto &server = chip::Server::GetInstance();
109+
client::connect(server.GetCASESessionManager(), binding.fabricIndex, binding.nodeId, &req_handle);
110+
break;
111+
}
112+
}
113+
do_subscribe = false;
114+
}
115+
#endif
116+
}
117+
break;
118+
119+
73120
default:
74121
break;
75122
}

examples/light_switch/sdkconfig.defaults

+3
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,6 @@ CONFIG_MBEDTLS_HKDF_C=y
4040
# Increase LwIP IPv6 address number to 6 (MAX_FABRIC + 1)
4141
# unique local addresses for fabrics(MAX_FABRIC), a link local address(1)
4242
CONFIG_LWIP_IPV6_NUM_ADDRESSES=6
43+
44+
# Buttons
45+
CONFIG_BSP_BUTTONS_NUM=1

0 commit comments

Comments
 (0)