mirror of
https://github.com/espressif/esp-matter.git
synced 2026-04-27 19:13:13 +00:00
857 lines
38 KiB
C++
857 lines
38 KiB
C++
// 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 <esp_check.h>
|
||
#include <esp_matter_controller_utils.h>
|
||
#include <esp_matter_controller_write_command.h>
|
||
#include <json_parser.h>
|
||
#if CONFIG_ESP_MATTER_COMMISSIONER_ENABLE
|
||
#include <esp_matter_commissioner.h>
|
||
#else
|
||
#include <app/server/Server.h>
|
||
#endif
|
||
|
||
using namespace chip::app::Clusters;
|
||
using chip::ByteSpan;
|
||
using chip::DeviceProxy;
|
||
using chip::app::DataModel::List;
|
||
using chip::Platform::New;
|
||
|
||
static const char *TAG = "write_command";
|
||
|
||
namespace esp_matter {
|
||
namespace controller {
|
||
|
||
template <class T>
|
||
void write_command<T>::on_device_connected_fcn(void *context, ExchangeManager &exchangeMgr,
|
||
const SessionHandle &sessionHandle)
|
||
{
|
||
write_command<T> *cmd = (write_command<T> *)context;
|
||
CHIP_ERROR err = CHIP_NO_ERROR;
|
||
WriteClient *write_client =
|
||
New<WriteClient>(&exchangeMgr, &(cmd->get_chunked_write_callback()), chip::NullOptional, false);
|
||
if (!write_client) {
|
||
ESP_LOGE(TAG, "Failed to alloc memory for WriteClient");
|
||
chip::Platform::Delete(cmd);
|
||
}
|
||
err = write_client->EncodeAttribute(cmd->get_attribute_path(), cmd->get_attribute_val(), chip::NullOptional);
|
||
if (err != CHIP_NO_ERROR) {
|
||
ESP_LOGE(TAG, "Failed to Encode Attribute for WriteClient");
|
||
chip::Platform::Delete(cmd);
|
||
chip::Platform::Delete(write_client);
|
||
return;
|
||
}
|
||
|
||
err = write_client->SendWriteRequest(sessionHandle);
|
||
if (err != CHIP_NO_ERROR) {
|
||
ESP_LOGE(TAG, "Failed to Send Write Request");
|
||
chip::Platform::Delete(cmd);
|
||
chip::Platform::Delete(write_client);
|
||
}
|
||
return;
|
||
}
|
||
|
||
template <class T>
|
||
void write_command<T>::on_device_connection_failure_fcn(void *context, const ScopedNodeId &peerId, CHIP_ERROR error)
|
||
{
|
||
write_command<T> *cmd = (write_command<T> *)context;
|
||
delete cmd;
|
||
return;
|
||
}
|
||
|
||
template <class T>
|
||
esp_err_t write_command<T>::send_command()
|
||
{
|
||
#if CONFIG_ESP_MATTER_COMMISSIONER_ENABLE
|
||
if (CHIP_NO_ERROR ==
|
||
commissioner::get_device_commissioner()->GetConnectedDevice(m_node_id, &on_device_connected_cb,
|
||
&on_device_connection_failure_cb)) {
|
||
return ESP_OK;
|
||
}
|
||
#else
|
||
chip::Server *server = &(chip::Server::GetInstance());
|
||
server->GetCASESessionManager()->FindOrEstablishSession(ScopedNodeId(m_node_id, get_fabric_index()),
|
||
&on_device_connected_cb, &on_device_connection_failure_cb);
|
||
return ESP_OK;
|
||
#endif
|
||
chip::Platform::Delete(this);
|
||
return ESP_FAIL;
|
||
}
|
||
|
||
namespace clusters {
|
||
namespace on_off {
|
||
|
||
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 OnOff::Attributes::OnTime::Id:
|
||
case OnOff::Attributes::OffWaitTime::Id: {
|
||
write_command<uint16_t> *cmd = New<write_command<uint16_t>>(node_id, endpoint_id, OnOff::Id, attribute_id,
|
||
string_to_uint16(attribute_val_str));
|
||
ESP_RETURN_ON_FALSE(cmd, ESP_ERR_NO_MEM, TAG, "Failed to alloc memory for write_command");
|
||
return cmd->send_command();
|
||
break;
|
||
}
|
||
case OnOff::Attributes::StartUpOnOff::Id: {
|
||
write_command<uint8_t> *cmd = New<write_command<uint8_t>>(node_id, endpoint_id, OnOff::Id, attribute_id,
|
||
string_to_uint8(attribute_val_str));
|
||
ESP_RETURN_ON_FALSE(cmd, ESP_ERR_NO_MEM, TAG, "Failed to alloc memory for write_command");
|
||
return cmd->send_command();
|
||
break;
|
||
}
|
||
default:
|
||
err = ESP_ERR_NOT_SUPPORTED;
|
||
break;
|
||
}
|
||
return err;
|
||
}
|
||
|
||
} // namespace on_off
|
||
|
||
namespace level_control {
|
||
|
||
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 LevelControl::Attributes::OnOffTransitionTime::Id:
|
||
case LevelControl::Attributes::OnTransitionTime::Id:
|
||
case LevelControl::Attributes::OffTransitionTime::Id: {
|
||
write_command<uint16_t> *cmd = New<write_command<uint16_t>>(node_id, endpoint_id, LevelControl::Id,
|
||
attribute_id, string_to_uint16(attribute_val_str));
|
||
ESP_RETURN_ON_FALSE(cmd, ESP_ERR_NO_MEM, TAG, "Failed to alloc memory for write_command");
|
||
return cmd->send_command();
|
||
break;
|
||
}
|
||
case LevelControl::Attributes::OnLevel::Id:
|
||
case LevelControl::Attributes::DefaultMoveRate::Id:
|
||
case LevelControl::Attributes::Options::Id:
|
||
case LevelControl::Attributes::StartUpCurrentLevel::Id: {
|
||
write_command<uint8_t> *cmd = New<write_command<uint8_t>>(node_id, endpoint_id, LevelControl::Id, attribute_id,
|
||
string_to_uint8(attribute_val_str));
|
||
ESP_RETURN_ON_FALSE(cmd, ESP_ERR_NO_MEM, TAG, "Failed to alloc memory for write_command");
|
||
return cmd->send_command();
|
||
break;
|
||
}
|
||
default:
|
||
err = ESP_ERR_NOT_SUPPORTED;
|
||
break;
|
||
}
|
||
return err;
|
||
}
|
||
|
||
} // namespace level_control
|
||
|
||
namespace color_control {
|
||
|
||
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 ColorControl::Attributes::StartUpColorTemperatureMireds::Id: {
|
||
write_command<uint16_t> *cmd = New<write_command<uint16_t>>(node_id, endpoint_id, ColorControl::Id,
|
||
attribute_id, string_to_uint16(attribute_val_str));
|
||
ESP_RETURN_ON_FALSE(cmd, ESP_ERR_NO_MEM, TAG, "Failed to alloc memory for write_command");
|
||
return cmd->send_command();
|
||
break;
|
||
}
|
||
case ColorControl::Attributes::Options::Id: {
|
||
write_command<uint8_t> *cmd = New<write_command<uint8_t>>(node_id, endpoint_id, ColorControl::Id, attribute_id,
|
||
string_to_uint8(attribute_val_str));
|
||
ESP_RETURN_ON_FALSE(cmd, ESP_ERR_NO_MEM, TAG, "Failed to alloc memory for write_command");
|
||
return cmd->send_command();
|
||
break;
|
||
}
|
||
default:
|
||
err = ESP_ERR_NOT_SUPPORTED;
|
||
break;
|
||
}
|
||
return err;
|
||
}
|
||
} // namespace color_control
|
||
|
||
namespace access_control {
|
||
|
||
using AccessControl::AccessControlEntryAuthModeEnum;
|
||
using AccessControl::AccessControlEntryPrivilegeEnum;
|
||
|
||
constexpr size_t k_max_acl_entries = CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_ENTRIES_PER_FABRIC;
|
||
constexpr size_t k_max_subjects_per_acl = CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_SUBJECTS_PER_ENTRY;
|
||
constexpr size_t k_max_targets_per_acl = CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_TARGETS_PER_ENTRY;
|
||
|
||
using acl_obj = AccessControl::Structs::AccessControlEntryStruct::Type;
|
||
using acl_target_obj = AccessControl::Structs::AccessControlTargetStruct::Type;
|
||
typedef struct acl_attr {
|
||
acl_obj acl_array[k_max_acl_entries];
|
||
uint64_t subjects_array[k_max_acl_entries][k_max_subjects_per_acl];
|
||
acl_target_obj targets_array[k_max_acl_entries][k_max_targets_per_acl];
|
||
} acl_attr_t;
|
||
|
||
static void acl_attr_free(void *ctx)
|
||
{
|
||
acl_attr_t *attr_ptr = reinterpret_cast<acl_attr_t *>(ctx);
|
||
chip::Platform::Delete(attr_ptr);
|
||
}
|
||
|
||
static esp_err_t parse_acl_json(char *json_str, acl_attr_t *acl, size_t *acl_size)
|
||
{
|
||
jparse_ctx_t jctx;
|
||
ESP_RETURN_ON_FALSE(json_parse_start(&jctx, json_str, strlen(json_str)) == 0, ESP_ERR_INVALID_ARG, TAG,
|
||
"Failed to parse the ACL json string on json_parse_start");
|
||
size_t acl_index = 0;
|
||
while (acl_index < k_max_acl_entries && json_arr_get_object(&jctx, acl_index) == 0) {
|
||
int int_val;
|
||
// FabricIndex
|
||
if (json_obj_get_int(&jctx, "fabricIndex", &int_val) == 0) {
|
||
acl->acl_array[acl_index].fabricIndex = int_val;
|
||
}
|
||
// Privilege
|
||
ESP_RETURN_ON_FALSE(json_obj_get_int(&jctx, "privilege", &int_val) == 0, ESP_ERR_INVALID_ARG, TAG,
|
||
"Failed to get privilege from the ACL json string");
|
||
acl->acl_array[acl_index].privilege = AccessControlEntryPrivilegeEnum(int_val);
|
||
// AuthMode
|
||
ESP_RETURN_ON_FALSE(json_obj_get_int(&jctx, "authMode", &int_val) == 0, ESP_ERR_INVALID_ARG, TAG,
|
||
"Failed to get authMode from the ACL json string");
|
||
acl->acl_array[acl_index].authMode = AccessControlEntryAuthModeEnum(int_val);
|
||
// Subjects
|
||
int subjects_num = 0;
|
||
if (json_obj_get_array(&jctx, "subjects", &subjects_num) == 0 && subjects_num > 0) {
|
||
ESP_RETURN_ON_FALSE(subjects_num <= k_max_subjects_per_acl, ESP_ERR_INVALID_ARG, TAG,
|
||
"Failed to get subjects from the ACL json string: Error on subjects_num");
|
||
for (size_t subj_index = 0; subj_index < subjects_num; ++subj_index) {
|
||
int64_t subject_val;
|
||
ESP_RETURN_ON_FALSE(json_arr_get_int64(&jctx, subj_index, &subject_val) == 0, ESP_ERR_INVALID_ARG, TAG,
|
||
"Failed to get subjects from the ACL json string: Error on subject-%u value",
|
||
subj_index);
|
||
acl->subjects_array[acl_index][subj_index] = subject_val;
|
||
}
|
||
acl->acl_array[acl_index].subjects.SetNonNull(acl->subjects_array[acl_index], subjects_num);
|
||
json_obj_leave_array(&jctx);
|
||
} else {
|
||
acl->acl_array[acl_index].subjects.SetNull();
|
||
}
|
||
// Targets
|
||
int targets_num = 0;
|
||
if (json_obj_get_array(&jctx, "targets", &targets_num) == 0 && targets_num > 0) {
|
||
ESP_RETURN_ON_FALSE(targets_num <= k_max_targets_per_acl, ESP_ERR_INVALID_ARG, TAG,
|
||
"Failed to get targets from the ACL json string: Error on targets length");
|
||
for (size_t targ_index = 0; targ_index < targets_num; ++targ_index) {
|
||
ESP_RETURN_ON_FALSE(json_arr_get_object(&jctx, targ_index) == 0, ESP_ERR_INVALID_ARG, TAG,
|
||
"Failed to get targets from the ACL json string: Error on targets-%u value",
|
||
targ_index);
|
||
int64_t cluster_val, device_type_val;
|
||
int endpoint_val;
|
||
bool exist_cluster, exist_endpoint, exist_device_type;
|
||
|
||
exist_cluster = json_obj_get_int64(&jctx, "cluster", &cluster_val) == 0;
|
||
exist_endpoint = json_obj_get_int(&jctx, "endpoint", &endpoint_val) == 0;
|
||
exist_device_type = json_obj_get_int64(&jctx, "deviceType", &device_type_val) == 0;
|
||
if ((!exist_cluster && !exist_endpoint && !exist_device_type) ||
|
||
(exist_endpoint && exist_device_type)) {
|
||
ESP_LOGE(TAG, "Target-%u value is invalid, skip it", targ_index);
|
||
json_arr_leave_object(&jctx);
|
||
continue;
|
||
}
|
||
// Cluster
|
||
if (exist_cluster) {
|
||
acl->targets_array[acl_index][targ_index].cluster.SetNonNull(static_cast<uint32_t>(cluster_val));
|
||
} else {
|
||
acl->targets_array[acl_index][targ_index].cluster.SetNull();
|
||
}
|
||
// Endpoint
|
||
if (exist_endpoint) {
|
||
acl->targets_array[acl_index][targ_index].endpoint.SetNonNull(static_cast<uint16_t>(endpoint_val));
|
||
} else {
|
||
acl->targets_array[acl_index][targ_index].endpoint.SetNull();
|
||
}
|
||
// DeviceType
|
||
if (exist_device_type) {
|
||
acl->targets_array[acl_index][targ_index].deviceType.SetNonNull(
|
||
static_cast<uint32_t>(device_type_val));
|
||
} else {
|
||
acl->targets_array[acl_index][targ_index].deviceType.SetNull();
|
||
}
|
||
json_arr_leave_object(&jctx);
|
||
}
|
||
acl->acl_array[acl_index].targets.SetNonNull(acl->targets_array[acl_index], targets_num);
|
||
json_obj_leave_array(&jctx);
|
||
} else {
|
||
acl->acl_array[acl_index].targets.SetNull();
|
||
}
|
||
// Leave Object
|
||
json_arr_leave_object(&jctx);
|
||
acl_index++;
|
||
}
|
||
*acl_size = acl_index;
|
||
return ESP_OK;
|
||
}
|
||
|
||
// The extension data may be used to store arbitrary TLV-encoded data related to a fabric’s ACL Entries.
|
||
constexpr size_t k_max_extension_entries = CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_ENTRIES_PER_FABRIC;
|
||
constexpr size_t k_max_extension_data_len = 128;
|
||
|
||
using extension_obj = AccessControl::Structs::AccessControlExtensionStruct::Type;
|
||
typedef struct extension_attr {
|
||
extension_obj extension_array[k_max_extension_entries];
|
||
uint8_t data_array[k_max_extension_entries][k_max_extension_data_len];
|
||
} extension_attr_t;
|
||
|
||
static void extension_attr_free(void *ctx)
|
||
{
|
||
extension_attr_t *attr = reinterpret_cast<extension_attr_t *>(ctx);
|
||
chip::Platform::Delete(attr);
|
||
}
|
||
|
||
static esp_err_t parse_extension_json(char *json_str, extension_attr_t *extension, size_t *extension_size)
|
||
{
|
||
jparse_ctx_t jctx;
|
||
ESP_RETURN_ON_FALSE(json_parse_start(&jctx, json_str, strlen(json_str)) == 0, ESP_ERR_INVALID_ARG, TAG,
|
||
"Failed to parse the Extension json string on json_parse_start");
|
||
size_t index = 0;
|
||
while (index < k_max_extension_entries && json_arr_get_object(&jctx, index) == 0) {
|
||
int fabric_index;
|
||
if (json_obj_get_int(&jctx, "fabricIndex", &fabric_index) == 0) {
|
||
extension->extension_array[index].fabricIndex = fabric_index;
|
||
}
|
||
|
||
char data_oct_str[k_max_extension_data_len * 2 + 1] = {0};
|
||
if (json_obj_get_string(&jctx, "data", data_oct_str, k_max_extension_data_len * 2 + 1) != 0) {
|
||
ESP_LOGE(TAG, "Failed to get data from the Extension json string");
|
||
return ESP_ERR_INVALID_ARG;
|
||
} else {
|
||
size_t data_len = oct_str_to_byte_arr(data_oct_str, extension->data_array[index]);
|
||
ESP_RETURN_ON_FALSE(data_len > 0, ESP_ERR_INVALID_ARG, TAG,
|
||
"Failed to convert the data octstring to byte array");
|
||
extension->extension_array[index].data = ByteSpan(extension->data_array[index], data_len);
|
||
}
|
||
json_arr_leave_object(&jctx);
|
||
index++;
|
||
}
|
||
*extension_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 AccessControl::Attributes::Acl::Id: {
|
||
size_t acl_size = 0;
|
||
acl_attr_t *attr_val = New<acl_attr_t>();
|
||
ESP_RETURN_ON_FALSE(attr_val, ESP_ERR_NO_MEM, TAG, "Failed to alloc acl_attr_t");
|
||
ESP_RETURN_ON_ERROR(parse_acl_json(attribute_val_str, attr_val, &acl_size), TAG,
|
||
"Failed to parse the ACL json string");
|
||
List<acl_obj> access_control_list(attr_val->acl_array, acl_size);
|
||
write_command<List<acl_obj>> *cmd = New<write_command<List<acl_obj>>>(node_id, endpoint_id, AccessControl::Id,
|
||
attribute_id, access_control_list);
|
||
ESP_RETURN_ON_FALSE(cmd, ESP_ERR_NO_MEM, TAG, "Failed to alloc memory for write_command");
|
||
cmd->set_attribute_free_handler(acl_attr_free, attr_val);
|
||
return cmd->send_command();
|
||
break;
|
||
}
|
||
case AccessControl::Attributes::Extension::Id: {
|
||
size_t extension_size = 0;
|
||
extension_attr_t *attr_val = New<extension_attr_t>();
|
||
ESP_RETURN_ON_FALSE(attr_val, ESP_ERR_NO_MEM, TAG, "Failed to alloc extension_attr_t");
|
||
ESP_RETURN_ON_ERROR(parse_extension_json(attribute_val_str, attr_val, &extension_size), TAG,
|
||
"Failed to parse the Extension json string");
|
||
List<extension_obj> extension_list(attr_val->extension_array, extension_size);
|
||
write_command<List<extension_obj>> *cmd = New<write_command<List<extension_obj>>>(
|
||
node_id, endpoint_id, AccessControl::Id, attribute_id, extension_list);
|
||
ESP_RETURN_ON_FALSE(cmd, ESP_ERR_NO_MEM, TAG, "Failed to alloc memory for write_command");
|
||
cmd->set_attribute_free_handler(extension_attr_free, attr_val);
|
||
return cmd->send_command();
|
||
break;
|
||
}
|
||
default:
|
||
err = ESP_ERR_NOT_SUPPORTED;
|
||
break;
|
||
}
|
||
return err;
|
||
}
|
||
|
||
} // namespace access_control
|
||
|
||
namespace binding {
|
||
using binding_obj = Binding::Structs::TargetStruct::Type;
|
||
typedef struct binding_attr {
|
||
binding_obj binding_array[CONFIG_MAX_BINDINGS];
|
||
} binding_attr_t;
|
||
|
||
static void binding_attr_free(void *ctx)
|
||
{
|
||
binding_attr_t *attr_ptr = reinterpret_cast<binding_attr_t *>(ctx);
|
||
chip::Platform::Delete(attr_ptr);
|
||
}
|
||
|
||
static esp_err_t parse_binding_json(char *json_str, binding_attr_t *binding, size_t *binding_size)
|
||
{
|
||
jparse_ctx_t jctx;
|
||
ESP_RETURN_ON_FALSE(json_parse_start(&jctx, json_str, strlen(json_str)) == 0, ESP_ERR_INVALID_ARG, TAG,
|
||
"Failed to parse the Binding json string on json_parse_start");
|
||
size_t index = 0;
|
||
while (index < CONFIG_MAX_BINDINGS && json_arr_get_object(&jctx, index) == 0) {
|
||
int int_val;
|
||
int64_t int64_val;
|
||
// Fabric
|
||
if (json_obj_get_int(&jctx, "fabricIndex", &int_val) == 0) {
|
||
binding->binding_array[index].fabricIndex = int_val;
|
||
}
|
||
|
||
if (json_obj_get_int64(&jctx, "node", &int64_val) == 0) {
|
||
// Unicast Binding
|
||
binding->binding_array[index].node.SetValue(int64_val);
|
||
binding->binding_array[index].group.ClearValue();
|
||
|
||
ESP_RETURN_ON_FALSE(json_obj_get_int(&jctx, "endpoint", &int_val) == 0, ESP_ERR_INVALID_ARG, TAG,
|
||
"Failed to get endpoint from the Binding json string");
|
||
binding->binding_array[index].endpoint.SetValue(int_val);
|
||
|
||
ESP_RETURN_ON_FALSE(json_obj_get_int(&jctx, "cluster", &int_val) == 0, ESP_ERR_INVALID_ARG, TAG,
|
||
"Failed to get cluster from the Binding json string");
|
||
binding->binding_array[index].cluster.SetValue(int_val);
|
||
} else if (json_obj_get_int64(&jctx, "group", &int64_val) == 0) {
|
||
// Group binding
|
||
binding->binding_array[index].group.SetValue(int64_val);
|
||
binding->binding_array[index].node.ClearValue();
|
||
binding->binding_array[index].endpoint.ClearValue();
|
||
binding->binding_array[index].cluster.ClearValue();
|
||
} else {
|
||
ESP_LOGE(TAG, "The Binding json string is invalid");
|
||
return ESP_ERR_INVALID_ARG;
|
||
}
|
||
json_arr_leave_object(&jctx);
|
||
index++;
|
||
}
|
||
*binding_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 Binding::Attributes::Binding::Id: {
|
||
size_t binding_size = 0;
|
||
binding_attr_t *attr_val = chip::Platform::New<binding_attr_t>();
|
||
ESP_RETURN_ON_FALSE(attr_val, ESP_ERR_NO_MEM, TAG, "Failed to alloc binding_attr_t");
|
||
ESP_RETURN_ON_ERROR(parse_binding_json(attribute_val_str, attr_val, &binding_size), TAG,
|
||
"Failed to parse the Binding json string");
|
||
List<binding_obj> binding_list(attr_val->binding_array, binding_size);
|
||
write_command<List<binding_obj>> *cmd =
|
||
New<write_command<List<binding_obj>>>(node_id, endpoint_id, Binding::Id, attribute_id, binding_list);
|
||
ESP_RETURN_ON_FALSE(cmd, ESP_ERR_NO_MEM, TAG, "Failed to alloc memory for write_command");
|
||
cmd->set_attribute_free_handler(binding_attr_free, attr_val);
|
||
return cmd->send_command();
|
||
break;
|
||
}
|
||
default:
|
||
err = ESP_ERR_NOT_SUPPORTED;
|
||
break;
|
||
}
|
||
return err;
|
||
}
|
||
|
||
} // 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<group_key_map_attr_t *>(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<group_key_map_attr_t>();
|
||
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_obj> group_key_map_list(attr_val->group_key_map_array, group_key_map_size);
|
||
write_command<List<group_key_map_obj>> *cmd = New<write_command<List<group_key_map_obj>>>(
|
||
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 identify {
|
||
|
||
static esp_err_t write_attribute(uint64_t node_id, uint16_t endpoint_id, uint32_t attribute_id, char *attribute_val_str)
|
||
{
|
||
if (attribute_id == Identify::Attributes::IdentifyTime::Id) {
|
||
write_command<uint16_t> *cmd = New<write_command<uint16_t>>(node_id, endpoint_id, Identify::Id, attribute_id,
|
||
string_to_uint16(attribute_val_str));
|
||
ESP_RETURN_ON_FALSE(cmd, ESP_ERR_NO_MEM, TAG, "Failed to alloc memory for write_command");
|
||
return cmd->send_command();
|
||
}
|
||
return ESP_ERR_NOT_SUPPORTED;
|
||
}
|
||
|
||
} // namespace identify
|
||
|
||
namespace thermostat {
|
||
|
||
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) {
|
||
// uint8 value type
|
||
case Thermostat::Attributes::HVACSystemTypeConfiguration::Id:
|
||
case Thermostat::Attributes::RemoteSensing::Id:
|
||
case Thermostat::Attributes::ControlSequenceOfOperation::Id:
|
||
case Thermostat::Attributes::SystemMode::Id:
|
||
case Thermostat::Attributes::TemperatureSetpointHold::Id:
|
||
case Thermostat::Attributes::ThermostatProgrammingOperationMode::Id:
|
||
case Thermostat::Attributes::OccupiedSetback::Id:
|
||
case Thermostat::Attributes::UnoccupiedSetback::Id:
|
||
case Thermostat::Attributes::EmergencyHeatDelta::Id:
|
||
case Thermostat::Attributes::ACType::Id:
|
||
case Thermostat::Attributes::ACRefrigerantType::Id:
|
||
case Thermostat::Attributes::ACCompressorType::Id:
|
||
case Thermostat::Attributes::ACLouverPosition::Id:
|
||
case Thermostat::Attributes::ACCapacityformat::Id: {
|
||
write_command<uint8_t> *cmd = New<write_command<uint8_t>>(node_id, endpoint_id, Thermostat::Id, attribute_id,
|
||
string_to_uint8(attribute_val_str));
|
||
ESP_RETURN_ON_FALSE(cmd, ESP_ERR_NO_MEM, TAG, "Failed to alloc memory for write_command");
|
||
err = cmd->send_command();
|
||
break;
|
||
}
|
||
// int8 value type
|
||
case Thermostat::Attributes::LocalTemperatureCalibration::Id:
|
||
case Thermostat::Attributes::MinSetpointDeadBand::Id: {
|
||
write_command<int8_t> *cmd = New<write_command<int8_t>>(node_id, endpoint_id, Thermostat::Id, attribute_id,
|
||
string_to_int8(attribute_val_str));
|
||
ESP_RETURN_ON_FALSE(cmd, ESP_ERR_NO_MEM, TAG, "Failed to alloc memory for write_command");
|
||
err = cmd->send_command();
|
||
break;
|
||
}
|
||
// int16 value type
|
||
case Thermostat::Attributes::OccupiedCoolingSetpoint::Id:
|
||
case Thermostat::Attributes::OccupiedHeatingSetpoint::Id:
|
||
case Thermostat::Attributes::UnoccupiedCoolingSetpoint::Id:
|
||
case Thermostat::Attributes::UnoccupiedHeatingSetpoint::Id:
|
||
case Thermostat::Attributes::MinHeatSetpointLimit::Id:
|
||
case Thermostat::Attributes::MaxHeatSetpointLimit::Id:
|
||
case Thermostat::Attributes::MinCoolSetpointLimit::Id:
|
||
case Thermostat::Attributes::MaxCoolSetpointLimit::Id: {
|
||
write_command<int16_t> *cmd = New<write_command<int16_t>>(node_id, endpoint_id, Thermostat::Id, attribute_id,
|
||
string_to_int16(attribute_val_str));
|
||
ESP_RETURN_ON_FALSE(cmd, ESP_ERR_NO_MEM, TAG, "Failed to alloc memory for write_command");
|
||
err = cmd->send_command();
|
||
break;
|
||
}
|
||
// uint16 value type
|
||
case Thermostat::Attributes::TemperatureSetpointHoldDuration::Id:
|
||
case Thermostat::Attributes::ACCapacity::Id: {
|
||
write_command<uint16_t> *cmd = New<write_command<uint16_t>>(node_id, endpoint_id, Thermostat::Id, attribute_id,
|
||
string_to_uint16(attribute_val_str));
|
||
ESP_RETURN_ON_FALSE(cmd, ESP_ERR_NO_MEM, TAG, "Failed to alloc memory for write_command");
|
||
err = cmd->send_command();
|
||
break;
|
||
}
|
||
// uint32 value type
|
||
case Thermostat::Attributes::ACErrorCode::Id: {
|
||
write_command<uint32_t> *cmd = New<write_command<uint32_t>>(node_id, endpoint_id, Thermostat::Id, attribute_id,
|
||
string_to_uint32(attribute_val_str));
|
||
ESP_RETURN_ON_FALSE(cmd, ESP_ERR_NO_MEM, TAG, "Failed to alloc memory for write_command");
|
||
err = cmd->send_command();
|
||
break;
|
||
}
|
||
default:
|
||
err = ESP_ERR_NOT_SUPPORTED;
|
||
break;
|
||
}
|
||
return err;
|
||
}
|
||
|
||
} // namespace thermostat
|
||
|
||
namespace door_lock {
|
||
|
||
constexpr size_t k_max_language_str_len = 3;
|
||
|
||
static void language_str_free(void *ctx)
|
||
{
|
||
chip::Platform::MemoryFree(ctx);
|
||
}
|
||
|
||
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) {
|
||
// uint32 value type
|
||
case DoorLock::Attributes::DoorOpenEvents::Id:
|
||
case DoorLock::Attributes::DoorClosedEvents::Id:
|
||
case DoorLock::Attributes::AutoRelockTime::Id: {
|
||
write_command<uint32_t> *cmd = New<write_command<uint32_t>>(node_id, endpoint_id, DoorLock::Id, attribute_id,
|
||
string_to_uint32(attribute_val_str));
|
||
ESP_RETURN_ON_FALSE(cmd, ESP_ERR_NO_MEM, TAG, "Failed to alloc memory for write_command");
|
||
err = cmd->send_command();
|
||
break;
|
||
}
|
||
// uint16 value type
|
||
case DoorLock::Attributes::OpenPeriod::Id:
|
||
case DoorLock::Attributes::ExpiringUserTimeout::Id: {
|
||
write_command<uint16_t> *cmd = New<write_command<uint16_t>>(node_id, endpoint_id, DoorLock::Id, attribute_id,
|
||
string_to_uint16(attribute_val_str));
|
||
ESP_RETURN_ON_FALSE(cmd, ESP_ERR_NO_MEM, TAG, "Failed to alloc memory for write_command");
|
||
err = cmd->send_command();
|
||
break;
|
||
}
|
||
// string value type
|
||
case DoorLock::Attributes::Language::Id: {
|
||
char *language_buf = static_cast<char *>(chip::Platform::MemoryAlloc(k_max_language_str_len));
|
||
if (!language_buf) {
|
||
ESP_LOGE(TAG, "Failed to alloc memory for language_buf");
|
||
return ESP_ERR_NO_MEM;
|
||
}
|
||
strncpy(language_buf, attribute_val_str, strnlen(attribute_val_str, k_max_language_str_len - 1));
|
||
language_buf[k_max_language_str_len - 1] = 0;
|
||
write_command<chip::CharSpan> *cmd = New<write_command<chip::CharSpan>>(
|
||
node_id, endpoint_id, DoorLock::Id, attribute_id, chip::CharSpan(language_buf, strlen(language_buf)));
|
||
ESP_RETURN_ON_FALSE(cmd, ESP_ERR_NO_MEM, TAG, "Failed to alloc memory for write_command");
|
||
cmd->set_attribute_free_handler(language_str_free, language_buf);
|
||
err = cmd->send_command();
|
||
break;
|
||
}
|
||
// uint8 value type
|
||
case DoorLock::Attributes::LEDSettings::Id:
|
||
case DoorLock::Attributes::SoundVolume::Id:
|
||
case DoorLock::Attributes::OperatingMode::Id:
|
||
case DoorLock::Attributes::LocalProgrammingFeatures::Id:
|
||
case DoorLock::Attributes::WrongCodeEntryLimit::Id:
|
||
case DoorLock::Attributes::UserCodeTemporaryDisableTime::Id: {
|
||
write_command<uint8_t> *cmd = New<write_command<uint8_t>>(node_id, endpoint_id, DoorLock::Id, attribute_id,
|
||
string_to_uint8(attribute_val_str));
|
||
ESP_RETURN_ON_FALSE(cmd, ESP_ERR_NO_MEM, TAG, "Failed to alloc memory for write_command");
|
||
err = cmd->send_command();
|
||
break;
|
||
}
|
||
// boolean value type
|
||
case DoorLock::Attributes::EnableLocalProgramming::Id:
|
||
case DoorLock::Attributes::EnableOneTouchLocking::Id:
|
||
case DoorLock::Attributes::EnableInsideStatusLED::Id:
|
||
case DoorLock::Attributes::EnablePrivacyModeButton::Id:
|
||
case DoorLock::Attributes::SendPINOverTheAir::Id:
|
||
case DoorLock::Attributes::RequirePINforRemoteOperation::Id: {
|
||
write_command<bool> *cmd = New<write_command<bool>>(node_id, endpoint_id, DoorLock::Id, attribute_id,
|
||
string_to_bool(attribute_val_str));
|
||
ESP_RETURN_ON_FALSE(cmd, ESP_ERR_NO_MEM, TAG, "Failed to alloc memory for write_command");
|
||
err = cmd->send_command();
|
||
break;
|
||
}
|
||
default:
|
||
err = ESP_ERR_NOT_SUPPORTED;
|
||
break;
|
||
}
|
||
return err;
|
||
}
|
||
} // namespace door_lock
|
||
|
||
namespace occupancy_sensing {
|
||
|
||
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 OccupancySensing::Attributes::PIROccupiedToUnoccupiedDelay::Id:
|
||
case OccupancySensing::Attributes::PIRUnoccupiedToOccupiedDelay::Id:
|
||
case OccupancySensing::Attributes::UltrasonicOccupiedToUnoccupiedDelay::Id:
|
||
case OccupancySensing::Attributes::UltrasonicUnoccupiedToOccupiedDelay::Id:
|
||
case OccupancySensing::Attributes::PhysicalContactOccupiedToUnoccupiedDelay::Id:
|
||
case OccupancySensing::Attributes::PhysicalContactUnoccupiedToOccupiedDelay::Id: {
|
||
write_command<uint16_t> *cmd = New<write_command<uint16_t>>(node_id, endpoint_id, OccupancySensing::Id,
|
||
attribute_id, string_to_uint16(attribute_val_str));
|
||
ESP_RETURN_ON_FALSE(cmd, ESP_ERR_NO_MEM, TAG, "Failed to alloc memory for write_command");
|
||
return cmd->send_command();
|
||
break;
|
||
}
|
||
case OccupancySensing::Attributes::PIRUnoccupiedToOccupiedThreshold::Id:
|
||
case OccupancySensing::Attributes::UltrasonicUnoccupiedToOccupiedThreshold::Id:
|
||
case OccupancySensing::Attributes::PhysicalContactUnoccupiedToOccupiedThreshold::Id: {
|
||
write_command<uint8_t> *cmd = New<write_command<uint8_t>>(node_id, endpoint_id, OccupancySensing::Id,
|
||
attribute_id, string_to_uint8(attribute_val_str));
|
||
ESP_RETURN_ON_FALSE(cmd, ESP_ERR_NO_MEM, TAG, "Failed to alloc memory for write_command");
|
||
return cmd->send_command();
|
||
break;
|
||
}
|
||
default:
|
||
err = ESP_ERR_NOT_SUPPORTED;
|
||
}
|
||
return err;
|
||
}
|
||
|
||
} // namespace occupancy_sensing
|
||
|
||
namespace window_covering {
|
||
|
||
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 WindowCovering::Attributes::Mode::Id: {
|
||
write_command<uint8_t> *cmd = New<write_command<uint8_t>>(node_id, endpoint_id, WindowCovering::Id,
|
||
attribute_id, string_to_uint8(attribute_val_str));
|
||
ESP_RETURN_ON_FALSE(cmd, ESP_ERR_NO_MEM, TAG, "Failed to alloc memory for write_command");
|
||
return cmd->send_command();
|
||
break;
|
||
}
|
||
default:
|
||
err = ESP_ERR_NOT_SUPPORTED;
|
||
break;
|
||
}
|
||
return err;
|
||
}
|
||
|
||
} // namespace window_covering
|
||
|
||
namespace thermostat_userinterface_configuration {
|
||
|
||
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 ThermostatUserInterfaceConfiguration::Attributes::TemperatureDisplayMode::Id:
|
||
case ThermostatUserInterfaceConfiguration::Attributes::KeypadLockout::Id:
|
||
case ThermostatUserInterfaceConfiguration::Attributes::ScheduleProgrammingVisibility::Id: {
|
||
write_command<uint8_t> *cmd =
|
||
New<write_command<uint8_t>>(node_id, endpoint_id, ThermostatUserInterfaceConfiguration::Id, attribute_id,
|
||
string_to_uint8(attribute_val_str));
|
||
ESP_RETURN_ON_FALSE(cmd, ESP_ERR_NO_MEM, TAG, "Failed to alloc memory for write_command");
|
||
return cmd->send_command();
|
||
break;
|
||
}
|
||
default:
|
||
err = ESP_ERR_NOT_SUPPORTED;
|
||
}
|
||
return err;
|
||
}
|
||
|
||
} // namespace thermostat_userinterface_configuration
|
||
|
||
} // namespace clusters
|
||
|
||
static attribute_write_handler_t s_unsupported_attribute_write_handler = NULL;
|
||
|
||
void set_unsupported_attribute_write_handler(attribute_write_handler_t handler)
|
||
{
|
||
s_unsupported_attribute_write_handler = handler;
|
||
}
|
||
|
||
esp_err_t send_write_attr_command(uint64_t node_id, uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id,
|
||
char *attribute_val_str)
|
||
{
|
||
esp_err_t err = ESP_OK;
|
||
switch (cluster_id) {
|
||
case OnOff::Id:
|
||
err = clusters::on_off::write_attribute(node_id, endpoint_id, attribute_id, attribute_val_str);
|
||
break;
|
||
case LevelControl::Id:
|
||
err = clusters::level_control::write_attribute(node_id, endpoint_id, attribute_id, attribute_val_str);
|
||
break;
|
||
case ColorControl::Id:
|
||
err = clusters::color_control::write_attribute(node_id, endpoint_id, attribute_id, attribute_val_str);
|
||
break;
|
||
case AccessControl::Id:
|
||
err = clusters::access_control::write_attribute(node_id, endpoint_id, attribute_id, attribute_val_str);
|
||
break;
|
||
case Binding::Id:
|
||
err = clusters::binding::write_attribute(node_id, endpoint_id, attribute_id, attribute_val_str);
|
||
break;
|
||
case GroupKeyManagement::Id:
|
||
err = clusters::group_key_management::write_attribute(node_id, endpoint_id, attribute_id, attribute_val_str);
|
||
break;
|
||
case OccupancySensing::Id:
|
||
err = clusters::occupancy_sensing::write_attribute(node_id, endpoint_id, attribute_id, attribute_val_str);
|
||
break;
|
||
case WindowCovering::Id:
|
||
err = clusters::window_covering::write_attribute(node_id, endpoint_id, attribute_id, attribute_val_str);
|
||
break;
|
||
case ThermostatUserInterfaceConfiguration::Id:
|
||
err = clusters::thermostat_userinterface_configuration::write_attribute(node_id, endpoint_id, attribute_id,
|
||
attribute_val_str);
|
||
break;
|
||
case Identify::Id:
|
||
err = clusters::identify::write_attribute(node_id, endpoint_id, attribute_id, attribute_val_str);
|
||
break;
|
||
case Thermostat::Id:
|
||
err = clusters::thermostat::write_attribute(node_id, endpoint_id, attribute_id, attribute_val_str);
|
||
break;
|
||
case DoorLock::Id:
|
||
err = clusters::door_lock::write_attribute(node_id, endpoint_id, attribute_id, attribute_val_str);
|
||
break;
|
||
default:
|
||
err = ESP_ERR_NOT_SUPPORTED;
|
||
}
|
||
|
||
if (err == ESP_ERR_NOT_SUPPORTED && s_unsupported_attribute_write_handler) {
|
||
err = s_unsupported_attribute_write_handler(node_id, endpoint_id, cluster_id, attribute_id, attribute_val_str);
|
||
}
|
||
|
||
return err;
|
||
}
|
||
|
||
} // namespace controller
|
||
} // namespace esp_matter
|