From 0890dbc8d9eaadc18dd6200e5b93e1e7e70458db Mon Sep 17 00:00:00 2001 From: WanqQixiang Date: Thu, 8 Dec 2022 11:13:20 +0800 Subject: [PATCH 1/2] controller: Add groupsettings command controller: Add invoking commands and writing attributes commands for group_key_management cluster controller: Add invoking commands for groups cluster --- components/esp_matter/esp_matter_client.cpp | 73 +++++- components/esp_matter/esp_matter_client.h | 36 +++ .../esp_matter_controller_cluster_command.cpp | 222 ++++++++++++++++- .../esp_matter_controller_cluster_command.h | 14 +- .../esp_matter_controller_console.cpp | 120 +++++++-- .../esp_matter_controller_group_settings.cpp | 229 ++++++++++++++++++ .../esp_matter_controller_group_settings.h | 40 +++ .../esp_matter_controller_write_command.cpp | 72 ++++++ .../logger/DataModelLogger.h | 1 - 9 files changed, 776 insertions(+), 31 deletions(-) create mode 100644 components/esp_matter_controller/esp_matter_controller_group_settings.cpp create mode 100644 components/esp_matter_controller/esp_matter_controller_group_settings.h diff --git a/components/esp_matter/esp_matter_client.cpp b/components/esp_matter/esp_matter_client.cpp index 26173d59a..282709b95 100644 --- a/components/esp_matter/esp_matter_client.cpp +++ b/components/esp_matter/esp_matter_client.cpp @@ -935,7 +935,6 @@ esp_err_t group_send_step_color_temperature(uint8_t fabric_index, uint16_t group return ESP_OK; } - } // namespace command } // namespace color_control @@ -965,5 +964,77 @@ esp_err_t group_send_identify(uint8_t fabric_index, uint16_t group_id, uint16_t } // namespace command } // namespace identify +namespace group_key_management { +namespace command { + +esp_err_t send_keyset_write(peer_device_t *remote_device, uint16_t remote_endpoint_id, group_keyset_struct group_keyset) +{ + GroupKeyManagement::Commands::KeySetWrite::Type command_data; + command_data.groupKeySet = group_keyset; + + chip::Controller::GroupKeyManagementCluster cluster(*remote_device->GetExchangeManager(), + remote_device->GetSecureSession().Value(), remote_endpoint_id); + cluster.InvokeCommand(command_data, NULL, send_command_success_callback, send_command_failure_callback); + return ESP_OK; +} + +esp_err_t send_keyset_read(peer_device_t *remote_device, uint16_t remote_endpoint_id, uint16_t keyset_id, + keyset_read_callback keyset_read_cb) +{ + GroupKeyManagement::Commands::KeySetRead::Type command_data; + command_data.groupKeySetID = keyset_id; + + chip::Controller::GroupKeyManagementCluster cluster(*remote_device->GetExchangeManager(), + remote_device->GetSecureSession().Value(), remote_endpoint_id); + cluster.InvokeCommand(command_data, NULL, keyset_read_cb, send_command_failure_callback); + return ESP_OK; +} + +} // namespace command +} // namespace group_key_management + +namespace groups { +namespace command { + +esp_err_t send_add_group(peer_device_t *remote_device, uint16_t remote_endpoint_id, uint16_t group_id, char *group_name, + add_group_callback add_group_cb) +{ + Groups::Commands::AddGroup::Type command_data; + command_data.groupId = group_id; + command_data.groupName = chip::CharSpan(group_name, strnlen(group_name, 16)); + + chip::Controller::GroupsCluster cluster(*remote_device->GetExchangeManager(), + remote_device->GetSecureSession().Value(), remote_endpoint_id); + cluster.InvokeCommand(command_data, NULL, add_group_cb, send_command_failure_callback); + return ESP_OK; +} + +esp_err_t send_view_group(peer_device_t *remote_device, uint16_t remote_endpoint_id, uint16_t group_id, + view_group_callback view_group_cb) +{ + Groups::Commands::ViewGroup::Type command_data; + command_data.groupId = group_id; + + chip::Controller::GroupsCluster cluster(*remote_device->GetExchangeManager(), + remote_device->GetSecureSession().Value(), remote_endpoint_id); + cluster.InvokeCommand(command_data, NULL, view_group_cb, send_command_failure_callback); + return ESP_OK; +} + +esp_err_t send_remove_group(peer_device_t *remote_device, uint16_t remote_endpoint_id, uint16_t group_id, + remove_group_callback remove_group_cb) +{ + Groups::Commands::RemoveGroup::Type command_data; + command_data.groupId = group_id; + + chip::Controller::GroupsCluster cluster(*remote_device->GetExchangeManager(), + remote_device->GetSecureSession().Value(), remote_endpoint_id); + cluster.InvokeCommand(command_data, NULL, remove_group_cb, send_command_failure_callback); + return ESP_OK; +} + +} // namespace command +} // namespace groups + } // namespace cluster } // namespace esp_matter diff --git a/components/esp_matter/esp_matter_client.h b/components/esp_matter/esp_matter_client.h index 91e0a765e..c857696e9 100644 --- a/components/esp_matter/esp_matter_client.h +++ b/components/esp_matter/esp_matter_client.h @@ -156,5 +156,41 @@ esp_err_t group_send_identify(uint8_t fabric_index, uint16_t group_id, uint16_t } // namespace command } // namespace identify +namespace group_key_management { +namespace command { +using group_keyset_struct = chip::app::Clusters::GroupKeyManagement::Structs::GroupKeySetStruct::Type; +using keyset_read_callback = + void (*)(void *, const chip::app::Clusters::GroupKeyManagement::Commands::KeySetRead::Type::ResponseType &); + +esp_err_t send_keyset_write(peer_device_t *remote_device, uint16_t remote_endpoint_id, + group_keyset_struct group_keyset); + +esp_err_t send_keyset_read(peer_device_t *remote_device, uint16_t remote_endpoint_id, uint16_t keyset_id, + keyset_read_callback read_callback); + +} // namespace command +} // namespace group_key_management + +namespace groups { +namespace command { +using add_group_callback = void (*)(void *, + const chip::app::Clusters::Groups::Commands::AddGroup::Type::ResponseType &); +using view_group_callback = void (*)(void *, + const chip::app::Clusters::Groups::Commands::ViewGroup::Type::ResponseType &); +using remove_group_callback = void (*)(void *, + const chip::app::Clusters::Groups::Commands::RemoveGroup::Type::ResponseType &); + +esp_err_t send_add_group(peer_device_t *remote_device, uint16_t remote_endpoint_id, uint16_t group_id, char *group_name, + add_group_callback add_group_cb); + +esp_err_t send_view_group(peer_device_t *remote_device, uint16_t remote_endpoint_id, uint16_t group_id, + view_group_callback view_group_cb); + +esp_err_t send_remove_group(peer_device_t *remote_device, uint16_t remote_endpoint_id, uint16_t group_id, + remove_group_callback remove_group_cb); + +} // namespace command +} // namespace groups + } // namespace cluster } // namespace esp_matter diff --git a/components/esp_matter_controller/esp_matter_controller_cluster_command.cpp b/components/esp_matter_controller/esp_matter_controller_cluster_command.cpp index a485a19f6..43b7407d7 100644 --- a/components/esp_matter_controller/esp_matter_controller_cluster_command.cpp +++ b/components/esp_matter_controller/esp_matter_controller_cluster_command.cpp @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include #if CONFIG_ESP_MATTER_COMMISSIONER_ENABLE #include @@ -20,6 +21,7 @@ #endif #include #include +#include using namespace chip::app::Clusters; static const char *TAG = "cluster_command"; @@ -49,6 +51,28 @@ static esp_err_t send_command(command_data_t *command_data, peer_device_t *remot return ESP_ERR_NOT_SUPPORTED; } +static esp_err_t send_group_command(command_data_t *command_data, uint16_t group_id) +{ + if (command_data->command_data_count != 0) { + return ESP_ERR_INVALID_ARG; + } + uint8_t fabric_index = commissioner::get_device_commissioner()->GetFabricIndex(); + switch (command_data->command_id) { + case OnOff::Commands::On::Id: + return esp_matter::cluster::on_off::command::group_send_on(fabric_index, group_id); + break; + case OnOff::Commands::Off::Id: + return esp_matter::cluster::on_off::command::group_send_off(fabric_index, group_id); + break; + case OnOff::Commands::Toggle::Id: + return esp_matter::cluster::on_off::command::group_send_toggle(fabric_index, group_id); + break; + default: + break; + } + return ESP_ERR_NOT_SUPPORTED; +} + } // namespace on_off namespace level_control { @@ -80,7 +104,7 @@ static esp_err_t send_command(command_data_t *command_data, peer_device_t *remot /* level */ string_to_uint8(command_data->command_data_str[0]), /* transition_time */ string_to_uint16(command_data->command_data_str[1]), /* option_mask */ string_to_uint8(command_data->command_data_str[2]), - /* option_override */string_to_uint8(command_data->command_data_str[3]) ); + /* option_override */ string_to_uint8(command_data->command_data_str[3])); break; case LevelControl::Commands::Step::Id: if (command_data->command_data_count != 5) { @@ -174,6 +198,172 @@ static esp_err_t send_command(command_data_t *command_data, peer_device_t *remot } // namespace color_control +namespace group_key_management { +using chip::app::Clusters::GroupKeyManagement::GroupKeySecurityPolicy; +using cluster::group_key_management::command::group_keyset_struct; + +constexpr size_t k_epoch_key_bytes_len = chip::Credentials::GroupDataProvider::EpochKey::kLengthBytes; + +typedef struct epoch_keys { + uint8_t epoch_key0_bytes[k_epoch_key_bytes_len]; + uint8_t epoch_key1_bytes[k_epoch_key_bytes_len]; + uint8_t epoch_key2_bytes[k_epoch_key_bytes_len]; +} epoch_keys_t; + +static bool parse_group_keyset(group_keyset_struct *keyset, char *json_str, epoch_keys_t *keys) +{ + jparse_ctx_t jctx; + if (json_parse_start(&jctx, json_str, strlen(json_str)) != 0) { + return false; + } + int int_val; + int64_t int64_val; + char epoch_key_oct_str[k_epoch_key_bytes_len * 2 + 1]; + // groupKeySetID + if (json_obj_get_int(&jctx, "groupKeySetID", &int_val) != 0) { + return false; + } + keyset->groupKeySetID = int_val; + // groupKeySecurityPolicy + if (json_obj_get_int(&jctx, "groupKeySecurityPolicy", &int_val) != 0) { + return false; + } + keyset->groupKeySecurityPolicy = static_cast(int_val); + // epochKey0 & epochStartTime0 + if (json_obj_get_int64(&jctx, "epochStartTime0", &int64_val) == 0 && + json_obj_get_string(&jctx, "epochKey0", epoch_key_oct_str, k_epoch_key_bytes_len * 2 + 1) == 0 && + oct_str_to_byte_arr(epoch_key_oct_str, keys->epoch_key0_bytes) == k_epoch_key_bytes_len) { + keyset->epochKey0.SetNonNull(chip::ByteSpan(keys->epoch_key0_bytes, k_epoch_key_bytes_len)); + keyset->epochStartTime0.SetNonNull(int64_val); + } else { + keyset->epochKey0.SetNull(); + keyset->epochStartTime0.SetNull(); + } + // epochKey1 & epochStartTime1 + if (json_obj_get_int64(&jctx, "epochStartTime1", &int64_val) == 0 && + json_obj_get_string(&jctx, "epochKey1", epoch_key_oct_str, k_epoch_key_bytes_len * 2 + 1) == 0 && + oct_str_to_byte_arr(epoch_key_oct_str, keys->epoch_key1_bytes) == k_epoch_key_bytes_len) { + keyset->epochKey1.SetNonNull(chip::ByteSpan(keys->epoch_key1_bytes, k_epoch_key_bytes_len)); + keyset->epochStartTime1.SetNonNull(int64_val); + } else { + keyset->epochKey1.SetNull(); + keyset->epochStartTime1.SetNull(); + } + // epochKey2 & epochStartTime2 + if (json_obj_get_int64(&jctx, "epochStartTime2", &int64_val) == 0 && + json_obj_get_string(&jctx, "epochKey2", epoch_key_oct_str, k_epoch_key_bytes_len * 2 + 1) == 0 && + oct_str_to_byte_arr(epoch_key_oct_str, keys->epoch_key2_bytes) == k_epoch_key_bytes_len) { + keyset->epochKey2.SetNonNull(chip::ByteSpan(keys->epoch_key2_bytes, k_epoch_key_bytes_len)); + keyset->epochStartTime2.SetNonNull(int64_val); + } else { + keyset->epochKey2.SetNull(); + keyset->epochStartTime2.SetNull(); + } + return true; +} + +static void keyset_read_response_callback(void *ctx, + const GroupKeyManagement::Commands::KeySetRead::Type::ResponseType &response) +{ + DataModelLogger::LogValue("groupKeySet", 1, response); +} + +static esp_err_t send_command(command_data_t *command_data, peer_device_t *remote_device, uint16_t remote_endpoint_id) +{ + switch (command_data->command_id) { + case GroupKeyManagement::Commands::KeySetWrite::Id: { + if (command_data->command_data_count != 1) { + ESP_LOGE(TAG, "The command date should in following order: group_keyset"); + return ESP_ERR_INVALID_ARG; + } + group_keyset_struct keyset_struct; + epoch_keys_t keys; + if (!parse_group_keyset(&keyset_struct, command_data->command_data_str[0], &keys)) { + ESP_LOGE(TAG, "Failed to parse the group_keyset json string"); + return ESP_ERR_INVALID_ARG; + } + return esp_matter::cluster::group_key_management::command::send_keyset_write(remote_device, remote_endpoint_id, + keyset_struct); + break; + } + case GroupKeyManagement::Commands::KeySetRead::Id: { + if (command_data->command_data_count != 1) { + ESP_LOGE(TAG, "The command date should in following order: group_keyset_id"); + return ESP_ERR_INVALID_ARG; + } + return esp_matter::cluster::group_key_management::command::send_keyset_read( + remote_device, remote_endpoint_id, + /* group_keyset_id */ string_to_uint16(command_data->command_data_str[0]), keyset_read_response_callback); + break; + } + default: + return ESP_ERR_NOT_SUPPORTED; + break; + } + return ESP_ERR_NOT_SUPPORTED; +} + +} // namespace group_key_management + +namespace groups { + +static void add_group_response_callback(void *ctx, const Groups::Commands::AddGroup::Type::ResponseType &response) +{ + DataModelLogger::LogValue("addGroupResponse", 1, response); +} + +static void view_group_response_callback(void *ctx, const Groups::Commands::ViewGroup::Type::ResponseType &response) +{ + DataModelLogger::LogValue("viewGroupResponse", 1, response); +} + +static void remove_group_response_callback(void *ctx, const Groups::Commands::RemoveGroup::Type::ResponseType &response) +{ + DataModelLogger::LogValue("removeGroupResponse", 1, response); +} + +static esp_err_t send_command(command_data_t *command_data, peer_device_t *remote_device, uint16_t remote_endpoint_id) +{ + switch (command_data->command_id) { + case Groups::Commands::AddGroup::Id: { + if (command_data->command_data_count != 2) { + ESP_LOGE(TAG, "The command date should in following order: group_id group_name"); + return ESP_ERR_INVALID_ARG; + } + return esp_matter::cluster::groups::command::send_add_group( + remote_device, remote_endpoint_id, + /* group_id */ string_to_uint16(command_data->command_data_str[0]), + /* group_name */ command_data->command_data_str[1], add_group_response_callback); + break; + } + case Groups::Commands::ViewGroup::Id: { + if (command_data->command_data_count != 1) { + ESP_LOGE(TAG, "The command date should in following order: group_id"); + return ESP_ERR_INVALID_ARG; + } + return esp_matter::cluster::groups::command::send_view_group( + remote_device, remote_endpoint_id, + /* group_id */ string_to_uint16(command_data->command_data_str[0]), view_group_response_callback); + break; + } + case Groups::Commands::RemoveGroup::Id: { + if (command_data->command_data_count != 1) { + ESP_LOGE(TAG, "The command date should in following order: group_id"); + return ESP_ERR_INVALID_ARG; + } + return esp_matter::cluster::groups::command::send_remove_group( + remote_device, remote_endpoint_id, + /* group_id */ string_to_uint16(command_data->command_data_str[0]), remove_group_response_callback); + break; + } + default: + break; + } + return ESP_ERR_NOT_SUPPORTED; +} + +} // namespace groups + } // namespace clusters void cluster_command::on_device_connected_fcn(void *context, ExchangeManager &exchangeMgr, SessionHandle &sessionHandle) @@ -190,6 +380,12 @@ void cluster_command::on_device_connected_fcn(void *context, ExchangeManager &ex case ColorControl::Id: clusters::color_control::send_command(cmd->m_command_data, &device_proxy, cmd->m_endpoint_id); break; + case GroupKeyManagement::Id: + clusters::group_key_management::send_command(cmd->m_command_data, &device_proxy, cmd->m_endpoint_id); + break; + case Groups::Id: + clusters::groups::send_command(cmd->m_command_data, &device_proxy, cmd->m_endpoint_id); + break; default: break; } @@ -204,11 +400,28 @@ void cluster_command::on_device_connection_failure_fcn(void *context, const Scop return; } +esp_err_t cluster_command::dispatch_group_command(void *context) +{ + cluster_command *cmd = reinterpret_cast(context); + uint16_t group_id = cmd->m_destination_id & 0xFFFF; + switch (cmd->m_command_data->cluster_id) { + case OnOff::Id: + return clusters::on_off::send_group_command(cmd->m_command_data, group_id); + break; + default: + break; + } + return ESP_ERR_NOT_SUPPORTED; +} + esp_err_t cluster_command::send_command() { + if (is_group_command()) { + return dispatch_group_command(reinterpret_cast(this)); + } #if CONFIG_ESP_MATTER_COMMISSIONER_ENABLE if (CHIP_NO_ERROR == - commissioner::get_device_commissioner()->GetConnectedDevice(m_node_id, &on_device_connected_cb, + commissioner::get_device_commissioner()->GetConnectedDevice(m_destination_id, &on_device_connected_cb, &on_device_connection_failure_cb)) { return ESP_OK; } @@ -222,7 +435,8 @@ esp_err_t cluster_command::send_command() return ESP_FAIL; } -esp_err_t send_invoke_cluster_command(uint64_t node_id, uint16_t endpoint_id, int cmd_data_argc, char **cmd_data_argv) +esp_err_t send_invoke_cluster_command(uint64_t destination_id, uint16_t endpoint_id, int cmd_data_argc, + char **cmd_data_argv) { command_data_t *command_data = (command_data_t *)calloc(1, sizeof(command_data_t)); if (!command_data) { @@ -238,7 +452,7 @@ esp_err_t send_invoke_cluster_command(uint64_t node_id, uint16_t endpoint_id, in strnlen(cmd_data_argv[2 + idx], k_max_command_data_str_len)); command_data->command_data_str[idx][controller::k_max_command_data_str_len - 1] = 0; } - cluster_command *cmd = chip::Platform::New(node_id, endpoint_id, command_data); + cluster_command *cmd = chip::Platform::New(destination_id, endpoint_id, command_data); if (!cmd) { ESP_LOGE(TAG, "Failed to alloc memory for cluster_command"); return ESP_ERR_NO_MEM; diff --git a/components/esp_matter_controller/esp_matter_controller_cluster_command.h b/components/esp_matter_controller/esp_matter_controller_cluster_command.h index c7b14c1b2..f6e138ccc 100644 --- a/components/esp_matter_controller/esp_matter_controller_cluster_command.h +++ b/components/esp_matter_controller/esp_matter_controller_cluster_command.h @@ -25,7 +25,7 @@ using chip::SessionHandle; using chip::Messaging::ExchangeManager; using esp_matter::client::peer_device_t; -constexpr size_t k_max_command_data_str_len = 16; +constexpr size_t k_max_command_data_str_len = 256; constexpr size_t k_max_command_data_size = 8; typedef struct command_data { @@ -37,8 +37,8 @@ typedef struct command_data { class cluster_command { public: - cluster_command(uint64_t node_id, uint16_t endpoint_id, command_data_t *command_data) - : m_node_id(node_id) + cluster_command(uint64_t destination_id, uint16_t endpoint_id, command_data_t *command_data) + : m_destination_id(destination_id) , m_endpoint_id(endpoint_id) , m_command_data(command_data) , on_device_connected_cb(on_device_connected_fcn, this) @@ -55,14 +55,20 @@ public: esp_err_t send_command(); + bool is_group_command() { + return chip::IsGroupId(m_destination_id); + } + private: - uint64_t m_node_id; + uint64_t m_destination_id; uint16_t m_endpoint_id; command_data_t *m_command_data; static void on_device_connected_fcn(void *context, ExchangeManager &exchangeMgr, SessionHandle &sessionHandle); static void on_device_connection_failure_fcn(void *context, const ScopedNodeId &peerId, CHIP_ERROR error); + static esp_err_t dispatch_group_command(void *context); + chip::Callback::Callback on_device_connected_cb; chip::Callback::Callback on_device_connection_failure_cb; }; diff --git a/components/esp_matter_controller/esp_matter_controller_console.cpp b/components/esp_matter_controller/esp_matter_controller_console.cpp index 4e7f68f08..6acb5574d 100644 --- a/components/esp_matter_controller/esp_matter_controller_console.cpp +++ b/components/esp_matter_controller/esp_matter_controller_console.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -38,6 +39,8 @@ using chip::Inet::IPAddress; using chip::Transport::PeerAddress; using esp_matter::controller::command_data_t; +const static char *TAG = "controller_console"; + namespace esp_matter { namespace console { @@ -71,6 +74,71 @@ static esp_err_t controller_pairing_handler(int argc, char **argv) return ESP_ERR_INVALID_ARG; } +static esp_err_t controller_group_settings_handler(int argc, char **argv) +{ + if (argc >= 1) { + if (strncmp(argv[0], "show-groups", sizeof("show-groups")) == 0) { + return controller::group_settings::show_groups(); + } else if (strncmp(argv[0], "add-group", sizeof("add-group")) == 0) { + if (argc != 3) { + return ESP_ERR_INVALID_ARG; + } + uint16_t group_id = string_to_uint16(argv[1]); + char *group_name = argv[2]; + return controller::group_settings::add_group(group_name, group_id); + } else if (strncmp(argv[0], "remove-group", sizeof("remove-group")) == 0) { + if (argc != 2) { + return ESP_ERR_INVALID_ARG; + } + uint16_t group_id = string_to_uint16(argv[1]); + return controller::group_settings::remove_group(group_id); + } else if (strncmp(argv[0], "show-keysets", sizeof("show-keysets")) == 0) { + return controller::group_settings::show_keysets(); + } else if (strncmp(argv[0], "add-keyset", sizeof("add-keyset")) == 0) { + if (argc != 5) { + return ESP_ERR_INVALID_ARG; + } + uint16_t keyset_id = string_to_uint16(argv[1]); + uint8_t key_policy = string_to_uint8(argv[2]); + uint64_t validity_time = string_to_uint64(argv[3]); + char *epoch_key_oct_str = argv[4]; + return controller::group_settings::add_keyset(keyset_id, key_policy, validity_time, epoch_key_oct_str); + } else if (strncmp(argv[0], "remove-keyset", sizeof("remove_keyset")) == 0) { + if (argc != 2) { + return ESP_ERR_INVALID_ARG; + } + uint16_t keyset_id = string_to_uint16(argv[1]); + return controller::group_settings::remove_keyset(keyset_id); + } else if (strncmp(argv[0], "bind-keyset", sizeof("bind_keyset")) == 0) { + if (argc != 3) { + return ESP_ERR_INVALID_ARG; + } + uint16_t group_id = string_to_uint16(argv[1]); + uint16_t keyset_id = string_to_uint16(argv[2]); + return controller::group_settings::bind_keyset(group_id, keyset_id); + } else if (strncmp(argv[0], "unbind-keyset", sizeof("unbind_keyset")) == 0) { + if (argc != 3) { + return ESP_ERR_INVALID_ARG; + } + uint16_t group_id = string_to_uint16(argv[1]); + uint16_t keyset_id = string_to_uint16(argv[2]); + return controller::group_settings::unbind_keyset(group_id, keyset_id); + } + } + ESP_LOGI(TAG, "Subcommands of group-settings:"); + ESP_LOGI(TAG, "Show groups : controller group-settings show-groups"); + ESP_LOGI(TAG, "Add group : controller group-settings add-group "); + ESP_LOGI(TAG, "Remove group : controller group-settings remove-group "); + ESP_LOGI(TAG, "Show keysets : controller group-settings show-keysets"); + ESP_LOGI(TAG, + "Add keyset : controller group-settings add-keyset " + ""); + ESP_LOGI(TAG, "Remove keyset : controller group-settings remove-keyset "); + ESP_LOGI(TAG, "Bind keyset : controller group-settings bind-keyset "); + ESP_LOGI(TAG, "Unbind keyset : controller group-settings unbind-keyset "); + return ESP_OK; +} + static esp_err_t controller_invoke_command_handler(int argc, char **argv) { if (argc < 4) { @@ -188,62 +256,72 @@ esp_err_t controller_register_commands() #if CONFIG_ESP_MATTER_COMMISSIONER_ENABLE { .name = "pairing", - .description = "Pairing a node. " - "Usage: controller pairing onnetwork [nodeid] [pincode] Or " - "controller pairing ble-wifi [nodeid] [pincode] [discriminator] [ssid] [password] Or " - "controller pairing ble-thread [nodeid] [pincode] [discriminator] [dataset]", + .description = "Pairing a node.\n" + "\tUsage: controller pairing onnetwork [nodeid] [pincode] Or\n" + "\tcontroller pairing ble-wifi [nodeid] [pincode] [discriminator] [ssid] [password] Or\n" + "\tcontroller pairing ble-thread [nodeid] [pincode] [discriminator] [dataset]", .handler = controller_pairing_handler, }, #endif + { + .name = "group-settings", + .description = "Managing the groups and keysets of the controller.\n" + "\tUsage: controller group-settings ", + .handler = controller_group_settings_handler, + }, { .name = "invoke-cmd", - .description = "Send command to the nodes. " - "Usage: controller invoke-cmd [node-id] [endpoint-id] [cluster-id] [command-id] [payload]", + .description = + "Send command to the nodes.\n" + "\tUsage: controller invoke-cmd [node-id|group-id] [endpoint-id] [cluster-id] [command-id] [payload]\n" + "\tNotes: group-id should start with prefix '0xFFFFFFFFFFFF', endpoint-id will be ignored if the fist " + "parameter is group-id.", .handler = controller_invoke_command_handler, }, { .name = "read-attr", - .description = "Read attributes of the nodes. " - "Usage: controller read-attr [node-id] [endpoint-id] [cluster-id] [attr-id]", + .description = "Read attributes of the nodes.\n" + "\tUsage: controller read-attr [node-id] [endpoint-id] [cluster-id] [attr-id]", .handler = controller_read_attr_handler, }, { .name = "write-attr", - .description = "Write attributes of the nodes. " - "Usage: controller write-attr [node-id] [endpoint-id] [cluster-id] [attr-id] [attr-value]", + .description = + "Write attributes of the nodes.\n" + "\tUsage: controller write-attr [node-id|group-id] [endpoint-id] [cluster-id] [attr-id] [attr-value]", .handler = controller_write_attr_handler, }, { .name = "read-event", - .description = "Read events of the nodes. " - "Usage: controller read-event [node-id] [endpoint-id] [cluster-id] [event-id]", + .description = "Read events of the nodes.\n" + "\tUsage: controller read-event [node-id] [endpoint-id] [cluster-id] [event-id]", .handler = controller_read_event_handler, }, { .name = "subs-attr", - .description = "Subscribe attributes of the nodes. " - "Usage: controller subs-attr [node-id] [endpoint-id] [cluster-id] [attr-id] [min-interval] " - "[max-interval]", + .description = "Subscribe attributes of the nodes.\n" + "\tUsage: controller subs-attr [node-id] [endpoint-id] [cluster-id] [attr-id] " + "[min-interval] [max-interval]", .handler = controller_subscribe_attr_handler, }, { .name = "subs-event", - .description = "Subscribe events of the nodes. " - "Usage: controller subs-attr [node-id] [endpoint-id] [cluster-id] [event-id] [min-interval] " - "[max-interval]", + .description = "Subscribe events of the nodes.\n" + "\tUsage: controller subs-attr [node-id] [endpoint-id] [cluster-id] [event-id] " + "[min-interval] [max-interval]", .handler = controller_subscribe_event_handler, }, { .name = "shutdown-subs", - .description = "Shutdown subscription." - "Usage: controller shutdown-subs [node-id] [subscription-id]", + .description = "Shutdown subscription.\n" + "\tUsage: controller shutdown-subs [node-id] [subscription-id]", .handler = controller_shutdown_subscription_handler, }, }; const static command_t controller_command = { .name = "controller", - .description = "Controller commands. Usage: matter controller [command_name]", + .description = "Controller commands. Usage: matter esp controller [command_name]", .handler = controller_dispatch, }; // Register the controller commands diff --git a/components/esp_matter_controller/esp_matter_controller_group_settings.cpp b/components/esp_matter_controller/esp_matter_controller_group_settings.cpp new file mode 100644 index 000000000..38750e32a --- /dev/null +++ b/components/esp_matter_controller/esp_matter_controller_group_settings.cpp @@ -0,0 +1,229 @@ +// Copyright 2022 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +using chip::FabricIndex; +using chip::KeysetId; +using chip::Credentials::GroupDataProvider; + +constexpr char TAG[] = "groupsettings"; + +namespace esp_matter { +namespace controller { +namespace group_settings { + +static bool find_keyset_id(FabricIndex fabric_index, uint16_t group_id, KeysetId &keyset_id) +{ + GroupDataProvider *group_data_provider = chip::Credentials::GetGroupDataProvider(); + auto iter = group_data_provider->IterateGroupKeys(fabric_index); + GroupDataProvider::GroupKey group_key; + while (iter->Next(group_key)) { + if (group_key.group_id == group_id) { + keyset_id = group_key.keyset_id; + iter->Release(); + return true; + } + } + iter->Release(); + return false; +} + +esp_err_t show_groups() +{ + ESP_LOGI(TAG, " +-------------------------------------------------------------------------------------+"); + ESP_LOGI(TAG, " | Available Groups : |"); + ESP_LOGI(TAG, " +-------------------------------------------------------------------------------------+"); + ESP_LOGI(TAG, " | Group Id | KeySet Id | Group Name |"); + FabricIndex fabric_index = commissioner::get_device_commissioner()->GetFabricIndex(); + GroupDataProvider *group_data_provider = chip::Credentials::GetGroupDataProvider(); + auto iter = group_data_provider->IterateGroupInfo(fabric_index); + GroupDataProvider::GroupInfo group_info; + if (iter) { + while (iter->Next(group_info)) { + chip::KeysetId keyset_id; + if (find_keyset_id(fabric_index, group_info.group_id, keyset_id)) { + ESP_LOGI(TAG, " | 0x%-12x 0x%-13x %-50s |", group_info.group_id, keyset_id, group_info.name); + } else { + ESP_LOGI(TAG, " | 0x%-12x %-15s %-50s |", group_info.group_id, "None", group_info.name); + } + } + iter->Release(); + } + ESP_LOGI(TAG, " +-------------------------------------------------------------------------------------+"); + return ESP_OK; +} + +esp_err_t add_group(char *group_name, uint16_t group_id) +{ + if (strlen(group_name) > CHIP_CONFIG_MAX_GROUP_NAME_LENGTH || group_id == chip::kUndefinedGroupId) { + return ESP_ERR_INVALID_ARG; + } + + FabricIndex fabric_index = commissioner::get_device_commissioner()->GetFabricIndex(); + GroupDataProvider *group_data_provider = chip::Credentials::GetGroupDataProvider(); + GroupDataProvider::GroupInfo group_info; + + group_info.SetName(group_name); + group_info.group_id = group_id; + ESP_RETURN_ON_FALSE(CHIP_NO_ERROR == group_data_provider->SetGroupInfo(fabric_index, group_info), ESP_FAIL, TAG, + "Failed to set the group info"); + return ESP_OK; +} + +esp_err_t remove_group(uint16_t group_id) +{ + if (group_id == chip::kUndefinedGroupId) { + return ESP_ERR_INVALID_ARG; + } + + FabricIndex fabric_index = commissioner::get_device_commissioner()->GetFabricIndex(); + GroupDataProvider *group_data_provider = chip::Credentials::GetGroupDataProvider(); + ESP_RETURN_ON_FALSE(CHIP_NO_ERROR == group_data_provider->RemoveGroupInfo(fabric_index, group_id), ESP_FAIL, TAG, + "Failed to remove the group info"); + return ESP_OK; +} + +esp_err_t show_keysets() +{ + FabricIndex fabric_index = commissioner::get_device_commissioner()->GetFabricIndex(); + GroupDataProvider *group_data_provider = chip::Credentials::GetGroupDataProvider(); + GroupDataProvider::KeySet keyset; + + ESP_LOGI(TAG, " +-------------------------------------------------------------------------------------+"); + ESP_LOGI(TAG, " | Available KeySets : |"); + ESP_LOGI(TAG, " +-------------------------------------------------------------------------------------+"); + ESP_LOGI(TAG, " | KeySet Id | Key Policy |"); + + auto iter = group_data_provider->IterateKeySets(fabric_index); + if (iter) { + while (iter->Next(keyset)) { + ESP_LOGI(TAG, " | 0x%-12x %-66s |", keyset.keyset_id, + (keyset.policy == GroupDataProvider::SecurityPolicy::kCacheAndSync) ? "Cache and Sync" + : "Trust First"); + } + iter->Release(); + } + ESP_LOGI(TAG, " +-------------------------------------------------------------------------------------+"); + return ESP_OK; +} + +esp_err_t bind_keyset(uint16_t group_id, uint16_t keyset_id) +{ + size_t current_count = 0; + FabricIndex fabric_index = commissioner::get_device_commissioner()->GetFabricIndex(); + GroupDataProvider *group_data_provider = chip::Credentials::GetGroupDataProvider(); + + auto iter = group_data_provider->IterateGroupKeys(fabric_index); + current_count = iter->Count(); + iter->Release(); + + if (CHIP_NO_ERROR != + group_data_provider->SetGroupKeyAt(fabric_index, current_count, + GroupDataProvider::GroupKey(group_id, keyset_id))) { + ESP_LOGE(TAG, "Failed to bind keyset"); + return ESP_FAIL; + } + return ESP_OK; +} + +esp_err_t unbind_keyset(uint16_t group_id, uint16_t keyset_id) +{ + size_t index = 0; + FabricIndex fabric_index = commissioner::get_device_commissioner()->GetFabricIndex(); + GroupDataProvider *group_data_provider = chip::Credentials::GetGroupDataProvider(); + + auto iter = group_data_provider->IterateGroupKeys(fabric_index); + size_t max_count = iter->Count(); + GroupDataProvider::GroupKey group_key; + while (iter->Next(group_key)) { + if (group_key.group_id == group_id && group_key.keyset_id == keyset_id) { + break; + } + index++; + } + iter->Release(); + ESP_RETURN_ON_FALSE(index < max_count, ESP_ERR_NOT_FOUND, TAG, "Failed to find the group key"); + ESP_RETURN_ON_FALSE(CHIP_NO_ERROR == group_data_provider->RemoveGroupKeyAt(fabric_index, index), ESP_FAIL, TAG, + "Failed to remove the group key"); + return ESP_OK; +} + +esp_err_t add_keyset(uint16_t keyset_id, uint8_t key_policy, uint64_t validity_time, char *epoch_key_oct_str) +{ + FabricIndex fabric_index = commissioner::get_device_commissioner()->GetFabricIndex(); + GroupDataProvider *group_data_provider = chip::Credentials::GetGroupDataProvider(); + uint8_t compressed_fabric_id[sizeof(uint64_t)]; + chip::MutableByteSpan compressed_fabric_id_span(compressed_fabric_id); + if (CHIP_NO_ERROR != + commissioner::get_device_commissioner()->GetCompressedFabricIdBytes(compressed_fabric_id_span)) { + ESP_LOGE(TAG, "Failed to get the compressed fabric_id"); + return ESP_FAIL; + } + if (key_policy != 0 && key_policy != 1) { + return ESP_ERR_INVALID_ARG; + } + GroupDataProvider::KeySet keyset(keyset_id, GroupDataProvider::SecurityPolicy(key_policy), 1); + GroupDataProvider::EpochKey epoch_key; + epoch_key.start_time = validity_time; + uint8_t epoch_key_buf[GroupDataProvider::EpochKey::kLengthBytes]; + if (oct_str_to_byte_arr(epoch_key_oct_str, epoch_key_buf) != GroupDataProvider::EpochKey::kLengthBytes) { + ESP_LOGE(TAG, "The epoch key octstring is wrong"); + return ESP_ERR_INVALID_ARG; + } + memcpy(epoch_key.key, epoch_key_buf, GroupDataProvider::EpochKey::kLengthBytes); + memcpy(keyset.epoch_keys, &epoch_key, sizeof(GroupDataProvider::EpochKey)); + if (CHIP_NO_ERROR != group_data_provider->SetKeySet(fabric_index, compressed_fabric_id_span, keyset)) { + ESP_LOGE(TAG, "Failed to set keyset"); + return ESP_FAIL; + } + + return ESP_OK; +} + +esp_err_t remove_keyset(uint16_t keyset_id) +{ + FabricIndex fabric_index = commissioner::get_device_commissioner()->GetFabricIndex(); + GroupDataProvider *group_data_provider = chip::Credentials::GetGroupDataProvider(); + + size_t index = 0; + CHIP_ERROR err = CHIP_NO_ERROR; + auto iter = group_data_provider->IterateGroupKeys(fabric_index); + GroupDataProvider::GroupKey group_key; + while (iter->Next(group_key)) { + if (group_key.keyset_id == keyset_id) { + err = group_data_provider->RemoveGroupKeyAt(fabric_index, index); + if (err != CHIP_NO_ERROR) { + break; + } + } + index++; + } + iter->Release(); + + if (err == CHIP_NO_ERROR) { + return ESP_OK; + } + ESP_RETURN_ON_FALSE(CHIP_NO_ERROR == group_data_provider->RemoveKeySet(fabric_index, keyset_id), ESP_FAIL, TAG, + "Failed to remove the keyset"); + + return ESP_OK; +} + +} // namespace group_settings +} // namespace controller +} // namespace esp_matter diff --git a/components/esp_matter_controller/esp_matter_controller_group_settings.h b/components/esp_matter_controller/esp_matter_controller_group_settings.h new file mode 100644 index 000000000..2e033c22f --- /dev/null +++ b/components/esp_matter_controller/esp_matter_controller_group_settings.h @@ -0,0 +1,40 @@ +// Copyright 2022 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +namespace esp_matter { +namespace controller { +namespace group_settings { + +esp_err_t show_groups(); + +esp_err_t add_group(char *group_name, uint16_t group_id); + +esp_err_t remove_group(uint16_t group_id); + +esp_err_t show_keysets(); + +esp_err_t bind_keyset(uint16_t group_id, uint16_t keyset_id); + +esp_err_t unbind_keyset(uint16_t group_id, uint16_t keyset_id); + +esp_err_t add_keyset(uint16_t keyset_id, uint8_t key_policy, uint64_t validity_time, char *epoch_key_oct_str); + +esp_err_t remove_keyset(uint16_t keyset_id); + +} // namespace group_settings +} // namespace controller +} // namespace esp_matter diff --git a/components/esp_matter_controller/esp_matter_controller_write_command.cpp b/components/esp_matter_controller/esp_matter_controller_write_command.cpp index 68c60ffa3..50a5181f4 100644 --- a/components/esp_matter_controller/esp_matter_controller_write_command.cpp +++ b/components/esp_matter_controller/esp_matter_controller_write_command.cpp @@ -465,6 +465,75 @@ static esp_err_t write_attribute(uint64_t node_id, uint16_t endpoint_id, uint32_ } // namespace binding +namespace group_key_management { + +using group_key_map_obj = GroupKeyManagement::Structs::GroupKeyMapStruct::Type; + +constexpr size_t k_max_group_key_map_size = CHIP_CONFIG_MAX_GROUP_KEYS_PER_FABRIC; + +typedef struct group_key_map_attr { + group_key_map_obj group_key_map_array[k_max_group_key_map_size]; +} group_key_map_attr_t; + +static void group_key_map_attr_free(void *ctx) +{ + group_key_map_attr_t *attr_ptr = reinterpret_cast(ctx); + chip::Platform::Delete(attr_ptr); +} + +static esp_err_t parse_group_key_map_json(char *json_str, group_key_map_attr_t *group_key_map, + size_t *group_key_map_size) +{ + jparse_ctx_t jctx; + ESP_RETURN_ON_FALSE(json_parse_start(&jctx, json_str, strlen(json_str)) == 0, ESP_ERR_INVALID_ARG, TAG, + "Group key map json string is wrong"); + size_t index = 0; + while (index < k_max_group_key_map_size && json_arr_get_object(&jctx, index) == 0) { + int int_val; + // Fabric + if (json_obj_get_int(&jctx, "fabricIndex", &int_val) == 0) { + group_key_map->group_key_map_array[index].fabricIndex = int_val; + } + ESP_RETURN_ON_FALSE(json_obj_get_int(&jctx, "groupId", &int_val) == 0, ESP_ERR_INVALID_ARG, TAG, + "Failed to get groupId"); + group_key_map->group_key_map_array[index].groupId = int_val; + ESP_RETURN_ON_FALSE(json_obj_get_int(&jctx, "groupKeySetID", &int_val) == 0, ESP_ERR_INVALID_ARG, TAG, + "Failed to get groupKeySetId"); + group_key_map->group_key_map_array[index].groupKeySetID = int_val; + json_arr_leave_object(&jctx); + index++; + } + *group_key_map_size = index; + return ESP_OK; +} + +static esp_err_t write_attribute(uint64_t node_id, uint16_t endpoint_id, uint32_t attribute_id, char *attribute_val_str) +{ + esp_err_t err = ESP_OK; + switch (attribute_id) { + case GroupKeyManagement::Attributes::GroupKeyMap::Id: { + size_t group_key_map_size = 0; + group_key_map_attr_t *attr_val = chip::Platform::New(); + ESP_RETURN_ON_FALSE(attr_val, ESP_ERR_NO_MEM, TAG, "Failed to alloc group_key_map_attr_t"); + ESP_RETURN_ON_ERROR(parse_group_key_map_json(attribute_val_str, attr_val, &group_key_map_size), TAG, + "Failed to parse group_key_map json string"); + List group_key_map_list(attr_val->group_key_map_array, group_key_map_size); + write_command> *cmd = New>>( + node_id, endpoint_id, GroupKeyManagement::Id, attribute_id, group_key_map_list); + ESP_RETURN_ON_FALSE(cmd, ESP_ERR_NO_MEM, TAG, "Failed to alloc memory for write_command"); + cmd->set_attribute_free_handler(group_key_map_attr_free, attr_val); + return cmd->send_command(); + break; + } + default: + err = ESP_ERR_NOT_SUPPORTED; + break; + } + return err; +} + +} // namespace group_key_management + } // namespace clusters esp_err_t send_write_attr_command(uint64_t node_id, uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, @@ -486,6 +555,9 @@ esp_err_t send_write_attr_command(uint64_t node_id, uint16_t endpoint_id, uint32 case Binding::Id: return clusters::binding::write_attribute(node_id, endpoint_id, attribute_id, attribute_val_str); break; + case GroupKeyManagement::Id: + return clusters::group_key_management::write_attribute(node_id, endpoint_id, attribute_id, attribute_val_str); + break; default: return ESP_ERR_NOT_SUPPORTED; } diff --git a/components/esp_matter_controller/logger/DataModelLogger.h b/components/esp_matter_controller/logger/DataModelLogger.h index 46a0c327f..e64a3ab3e 100644 --- a/components/esp_matter_controller/logger/DataModelLogger.h +++ b/components/esp_matter_controller/logger/DataModelLogger.h @@ -34,7 +34,6 @@ public: static CHIP_ERROR LogCommand(const chip::app::ConcreteCommandPath & path, chip::TLV::TLVReader * data); static CHIP_ERROR LogEvent(const chip::app::EventHeader & header, chip::TLV::TLVReader * data); -private: static CHIP_ERROR LogValue(const char * label, size_t indent, bool value) { DataModelLogger::LogString(label, indent, value ? "TRUE" : "FALSE"); From 56daa63509034b810e0157524fe4c1d4cd376419 Mon Sep 17 00:00:00 2001 From: WanqQixiang Date: Wed, 18 Jan 2023 11:52:11 +0800 Subject: [PATCH 2/2] docs: Update docs for controller updates --- docs/en/developing.rst | 59 ++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/docs/en/developing.rst b/docs/en/developing.rst index 89899c952..52b1a011f 100644 --- a/docs/en/developing.rst +++ b/docs/en/developing.rst @@ -371,12 +371,6 @@ The console on the device can be used to run commands for testing. It is configu matter wifi mode [disable|ap|sta] -- Wi-Fi connect: Connect to the Access Point - - :: - - matter wifi connect - - Device configuration: Dump the device static configuration: :: @@ -427,6 +421,12 @@ Additional Matter specific commands: matter esp diagnostics mem-dump +- Wi-Fi + + :: + + matter esp wifi connect + 2.4 Developing your Product --------------------------- @@ -766,7 +766,7 @@ As an example, you can build *light* example on ``ESP32_custom`` platform with f 2.4.5 Controller Example ~~~~~~~~~~~~~~~~~~~~~~~~ -This section introduces the Matter controller example. Now this example supports 4 features of the standard Matter controller, including onnetwork-pairing, unicast-cluster-commands(onoff, levelcontrol, colorcontrol), read-attributes-commands, and unicast-write-attributes-commands(onoff, levelcontrol, colorcontrol). +This section introduces the Matter controller example. Now this example supports 8 features of the standard Matter controller, including onnetwork-pairing, invoke-cluster-commands, read-attributes-commands, write-attributes-commands, read-events-commands, subscribe-attributes-commands, subscribe-events-commands, and groupsettings-command. 2.4.5.1 Starting with device console ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -774,12 +774,13 @@ After you flash the controller example to the device, you can use `device consol 2.4.5.2 Pairing commands ^^^^^^^^^^^^^^^^^^^^^^^^ -The *pairing* command is used for commissioning the end-devices. Here are three standard pairing methods: +The ``pairing`` command is used for commissioning the end-devices. Here are three standard pairing methods: -- Onnetwork pairing. Before you execute this commissioning method, you should connect both controller and end-device to the same network and ensure the commissioning window of the end-device is opened. You can use the command *matter wifi connect* to complete this process. Then we can start the pairing. +- Onnetwork pairing. Before you execute this commissioning method, you should connect both controller and end-device to the same network and ensure the commissioning window of the end-device is opened. You can use the command ``matter esp wifi connect`` to complete this process. Then we can start the pairing. :: + matter esp wifi connect matter esp controller pairing onnetwork - Ble-wifi pairing. This commissioning method is still not supported on current controller example. @@ -788,17 +789,19 @@ The *pairing* command is used for commissioning the end-devices. Here are three 2.4.5.3 Cluster commands ^^^^^^^^^^^^^^^^^^^^^^^^ -The *invoke-cmd* command is used for sending cluster commands to the end-devices. Currently the controller only supports commands of on-off, level-control, and color-control clusters. +The ``invoke-cmd`` command is used for sending cluster commands to the end-devices. Currently the controller only supports commands of on-off, level-control, and color-control clusters. The on-off cluster supports both unicast and multicast sending, and the other two clusters only support unicast sending. - Send the cluster command: :: - matter esp controller invoke-cmd + matter esp controller invoke-cmd + +Notes: ``group-id`` should start with the ``0xFFFFFFFFFFFF`` prefix, and ``endpoint-id`` will be ignored for multicast commands. 2.4.5.4 Read attribute commands ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The *read-attr* command is used for sending the commands of reading attributes on the end-device. +The ``read-attr`` command is used for sending the commands of reading attributes on the end-device. - Send the read-attribute command: @@ -806,9 +809,9 @@ The *read-attr* command is used for sending the commands of reading attributes o matter esp controller read-attr -2.4.5.4 Read event commands +2.4.5.5 Read event commands ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The *read-event* command is used for sending the commands of reading events on the end-device. +The ``read-event`` command is used for sending the commands of reading events on the end-device. - Send the read-event command: @@ -816,9 +819,9 @@ The *read-event* command is used for sending the commands of reading events on t matter esp controller read-event -2.4.5.5 Write attribute commands +2.4.5.6 Write attribute commands ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The *write-attr* command is used for sending the commands of writing attributes on the end-device. Currently the controller only supports unicast-attributes-writing of on-off, level-control, and color-control clusters. +The ``write-attr`` command is used for sending the commands of writing attributes on the end-device. Currently the controller only supports unicast-attributes-writing of on-off, level-control, color-control, access-control, binding, and group-key-management clusters. - Send the write-attribute command: @@ -826,9 +829,9 @@ The *write-attr* command is used for sending the commands of writing attributes matter esp controller write-attr -2.4.5.6 Subscribe attribute commands +2.4.5.7 Subscribe attribute commands ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The *subs-attr* command is used for sending the commands of subscribing attributes on the end-device. +The ``subs-attr`` command is used for sending the commands of subscribing attributes on the end-device. - Send the subscribe-attribute command: @@ -836,9 +839,9 @@ The *subs-attr* command is used for sending the commands of subscribing attribut matter esp controller subs-attr -2.4.5.7 Subscribe event commands +2.4.5.8 Subscribe event commands ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The *subs-event* command is used for sending the commands of subscribing events on the end-device. +The ``subs-event`` command is used for sending the commands of subscribing events on the end-device. - Send the subscribe-event command: @@ -846,6 +849,22 @@ The *subs-event* command is used for sending the commands of subscribing events matter esp controller subs-event +2.4.5.9 Group settings commands +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The ``group-settings`` command is used for setting group information of the controller. The controller should be the same group with the end-device if it wants to send multicast commands to the end-device. + +- Set group information of the controller: + + :: + + matter esp controller group-settings show-groups + matter esp controller group-settings add-group + matter esp controller group-settings remove-group + matter esp controller group-settings show-keysets + matter esp controller group-settings add-keyset + matter esp controller group-settings remove-keyset + matter esp controller group-settings bind-keyset + matter esp controller group-settings unbind-keyset 2.5 Using esp_secure_cert partition -----------------------------------