From 09cfb92bd40a6f5493efe2005a2a561500110c48 Mon Sep 17 00:00:00 2001 From: Chirag Atal Date: Mon, 28 Mar 2022 15:38:43 +0530 Subject: [PATCH] esp_matter_rainmaker: Moving the custom rainmaker cluster from the application to this component app_rainmaker: Minor restructuring for easier maintainability. --- .../esp_matter_rainmaker/CMakeLists.txt | 12 + .../esp_matter_rainmaker.cpp | 188 ++++++++++ .../esp_matter_rainmaker.h | 56 +++ examples/rainmaker_light/CMakeLists.txt | 2 + examples/rainmaker_light/main/CMakeLists.txt | 2 +- .../rainmaker_light/main/app_rainmaker.cpp | 348 +++++++----------- 6 files changed, 386 insertions(+), 222 deletions(-) create mode 100644 components/esp_matter_rainmaker/CMakeLists.txt create mode 100644 components/esp_matter_rainmaker/esp_matter_rainmaker.cpp create mode 100644 components/esp_matter_rainmaker/esp_matter_rainmaker.h diff --git a/components/esp_matter_rainmaker/CMakeLists.txt b/components/esp_matter_rainmaker/CMakeLists.txt new file mode 100644 index 000000000..c626d4ba7 --- /dev/null +++ b/components/esp_matter_rainmaker/CMakeLists.txt @@ -0,0 +1,12 @@ +set(SRCS_LIST ) +set(REQUIRES_LIST esp_matter) + +idf_build_get_property(rainmaker_enabled RAINMAKER_ENABLED) +if (${rainmaker_enabled}) + list(APPEND SRCS_LIST esp_matter_rainmaker.cpp) + list(APPEND REQUIRES_LIST esp_rainmaker) +endif() + +idf_component_register(SRCS ${SRCS_LIST} + INCLUDE_DIRS "." + REQUIRES ${REQUIRES_LIST}) diff --git a/components/esp_matter_rainmaker/esp_matter_rainmaker.cpp b/components/esp_matter_rainmaker/esp_matter_rainmaker.cpp new file mode 100644 index 000000000..c8ccaceb6 --- /dev/null +++ b/components/esp_matter_rainmaker/esp_matter_rainmaker.cpp @@ -0,0 +1,188 @@ +// 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 +#include +#include +#include + +static const char *TAG = "esp_matter_rainmaker"; + +static esp_err_t esp_matter_rainmaker_console_handler(int argc, char **argv) +{ + if (argc == 3 && strncmp(argv[0], "add-user", sizeof("add-user")) == 0) { + printf("%s: Starting user-node mapping\n", TAG); + if (esp_rmaker_start_user_node_mapping(argv[1], argv[2]) != ESP_OK) { + return ESP_FAIL; + } + } else { + printf("%s: Invalid Usage.\n", TAG); + return ESP_ERR_INVALID_ARG; + } + return ESP_OK; +} + +static void esp_matter_rainmaker_register_commands() +{ + esp_matter_console_command_t command = { + .name = "rainmaker", + .description = "Initiate ESP RainMaker User-Node mapping from the node. " + "Usage: matter esp rainmaker add-user ", + .handler = esp_matter_rainmaker_console_handler, + }; + esp_matter_console_add_command(&command); +} + +#define ESP_MATTER_RAINMAKER_ENDPOINT_ID 0x0 /* Same as root node endpoint. This will always be endpoint_id 0. */ +#define ESP_MATTER_RAINMAKER_CLUSTER_ID 0x131B0000 /* 0x131B == manufacturer code */ +#define ESP_MATTER_RAINMAKER_STATUS_ATTRIBUTE_ID 0x0 +#define ESP_MATTER_RAINMAKER_CONFIGURATION_COMMAND_ID 0x0 +#define ESP_MATTER_RAINMAKER_CLUSTER_REVISION 1 +#define ESP_MATTER_RAINMAKER_COMMAND_LIMIT 5 /* This command can be called 5 times per reboot */ +#define ESP_MATTER_RAINMAKER_MAX_DATA_LEN 40 + +static esp_err_t rainmaker_status_attribute_update(bool status) +{ + int endpoint_id = ESP_MATTER_RAINMAKER_ENDPOINT_ID; + int cluster_id = ESP_MATTER_RAINMAKER_CLUSTER_ID; + int attribute_id = ESP_MATTER_RAINMAKER_STATUS_ATTRIBUTE_ID; + esp_matter_attr_val_t val = esp_matter_bool(status); + return esp_matter_attribute_update(endpoint_id, cluster_id, attribute_id, &val); +} + +static void user_node_association_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, + void* event_data) +{ + /* This event handler is only for user node association status */ + if (event_base == RMAKER_EVENT) { + if (event_id == RMAKER_EVENT_USER_NODE_MAPPING_DONE) { + ESP_LOGI(TAG, "User node association complete. Updating the status attribute."); + rainmaker_status_attribute_update(true); + } else if (event_id == RMAKER_EVENT_USER_NODE_MAPPING_RESET) { + ESP_LOGI(TAG, "User node association reset. Updating the status attribute."); + rainmaker_status_attribute_update(false); + } + } +} + +esp_err_t esp_matter_rainmaker_command_callback(int endpoint_id, int cluster_id, int command_id, TLVReader &tlv_data, + void *priv_data) +{ + /* Return if this is not the rainmaker configuration command */ + if (endpoint_id != ESP_MATTER_RAINMAKER_ENDPOINT_ID || cluster_id != ESP_MATTER_RAINMAKER_CLUSTER_ID + || command_id != ESP_MATTER_RAINMAKER_CONFIGURATION_COMMAND_ID) { + return ESP_OK; + } + ESP_LOGI(TAG, "RainMaker configuration command callback"); + static int command_count = ESP_MATTER_RAINMAKER_COMMAND_LIMIT; + if (command_count <= 0) { + ESP_LOGE(TAG, "This command has reached a limit. Please reboot to try again."); + return ESP_FAIL; + } + command_count--; + + /* Parse the tlv data */ + chip::CharSpan config_value; + chip::app::DataModel::Decode(tlv_data, config_value); + const char *data = config_value.data(); + int size = config_value.size(); + if (!data || size <= 0) { + ESP_LOGE(TAG, "Command data not found or was not decoded correctly. The expected data is a string or the" + "format is \"::\""); + return ESP_FAIL; + } + + /* The expected format of the data is "::" */ + char ch = ':'; + char *check = strchr(data, (int)ch); + if (check == NULL) { + ESP_LOGE(TAG, "':' not found in the received data: %.*s. The expected format is \"::\"", + size, data); + return ESP_FAIL; + } + + /* Get sizes */ + int user_id_index = 0; + int user_id_len = (int)(strchr(data, (int)ch) - data); /* (first ':') - (start of string) */ + int secret_key_index = (int)(strrchr(data, (int)ch) - data) + 1; /* (last ':') - (start of string) + 1 */ + int secret_key_len = size - secret_key_index; + if (user_id_len <= 0 || user_id_len >= ESP_MATTER_RAINMAKER_MAX_DATA_LEN || secret_key_len <= 0 + || secret_key_len >= ESP_MATTER_RAINMAKER_MAX_DATA_LEN) { + ESP_LOGE(TAG, "User id or secret key length invalid: user_id_len: %d, secret_key_len: %d", user_id_len, + secret_key_len); + return ESP_FAIL; + } + + /* Copy the data. This done to make the strings NULL terminated. */ + char user_id[ESP_MATTER_RAINMAKER_MAX_DATA_LEN] = {0}; + char secret_key[ESP_MATTER_RAINMAKER_MAX_DATA_LEN] = {0}; + strncpy(user_id, &data[user_id_index], user_id_len); + strncpy(secret_key, &data[secret_key_index], secret_key_len); + ESP_LOGI(TAG, "user_id: %s, secret_key: %s", user_id, secret_key); + + /* Call the rainmaker API */ + if (strlen(user_id) > 0 && strlen(secret_key) > 0) { + esp_rmaker_start_user_node_mapping(user_id, secret_key); + } + return ESP_OK; +} + +static esp_err_t rainmaker_custom_cluster_create() +{ + /* Get the endpoint */ + esp_matter_node_t *node = esp_matter_node_get(); + esp_matter_endpoint_t *endpoint = esp_matter_endpoint_get(node, ESP_MATTER_RAINMAKER_ENDPOINT_ID); + + /* Create custom rainmaker cluster */ + esp_matter_cluster_t *cluster = esp_matter_cluster_create(endpoint, ESP_MATTER_RAINMAKER_CLUSTER_ID, + ESP_MATTER_CLUSTER_FLAG_SERVER); + esp_matter_attribute_create(cluster, ZCL_CLUSTER_REVISION_SERVER_ATTRIBUTE_ID, ESP_MATTER_ATTRIBUTE_FLAG_NONE, + esp_matter_uint16(ESP_MATTER_RAINMAKER_CLUSTER_REVISION)); + + /* Create custom status attribute */ + /* Update the value of the attribute after esp_rmaker_node_init() is done */ + esp_matter_attribute_create(cluster, ESP_MATTER_RAINMAKER_STATUS_ATTRIBUTE_ID, ESP_MATTER_ATTRIBUTE_FLAG_NONE, + esp_matter_bool(false)); + + /* Create custom configuration command */ + esp_matter_command_create(cluster, ESP_MATTER_RAINMAKER_CONFIGURATION_COMMAND_ID, + ESP_MATTER_COMMAND_FLAG_CLIENT_GENERATED | ESP_MATTER_COMMAND_FLAG_CUSTOM, NULL); + return ESP_OK; +} + +esp_err_t esp_matter_rainmaker_init() +{ + /* Add custom rainmaker cluster */ + esp_matter_rainmaker_register_commands(); + return rainmaker_custom_cluster_create(); +} + +esp_err_t esp_matter_rainmaker_start() +{ + /* Check user node association */ + if (esp_rmaker_user_node_mapping_get_state() == ESP_RMAKER_USER_MAPPING_DONE) { + rainmaker_status_attribute_update(true); + } + + /* Register an event handler and update the state later */ + esp_event_handler_register(RMAKER_EVENT, RMAKER_EVENT_USER_NODE_MAPPING_DONE, + &user_node_association_event_handler, NULL); + esp_event_handler_register(RMAKER_EVENT, RMAKER_EVENT_USER_NODE_MAPPING_RESET, + &user_node_association_event_handler, NULL); + return ESP_OK; +} diff --git a/components/esp_matter_rainmaker/esp_matter_rainmaker.h b/components/esp_matter_rainmaker/esp_matter_rainmaker.h new file mode 100644 index 000000000..ed5b3fd94 --- /dev/null +++ b/components/esp_matter_rainmaker/esp_matter_rainmaker.h @@ -0,0 +1,56 @@ +// 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 +#include + +/** Initialize ESP Matter RainMaker + * + * This adds the custom RainMaker cluster which is used for RainMaker User Node Association. + * The 'rainmaker' console command is also added. + * This should be called before `esp_matter_start()` + * + * @return ESP_OK on success. + * @return error in case of failure. + */ +esp_err_t esp_matter_rainmaker_init(void); + +/** Start ESP Matter RainMaker + * + * This starts the post initialization process for RainMaker User Node Association. + * This should be called after `esp_rmaker_node_init()` but before `esp_rmaker_start()`. + * + * @return ESP_OK on success. + * @return error in case of failure. + */ +esp_err_t esp_matter_rainmaker_start(void); + +/** Custom Command callback + * + * Command callback for custom commands. + * This should be called when the application receives the custom command callback. + * + * @param[in] endpoint_id Endpoint ID of the command. + * @param[in] cluster_id Cluster ID of the command. + * @param[in] command_id Command ID. + * @param[in] tlv_data Command data which can be parsed. + * @param[in] priv_data Pointer to the private data paassed while setting the command callback. + * + * @return ESP_OK on success. + * @return error in case of failure. + */ +esp_err_t esp_matter_rainmaker_command_callback(int endpoint_id, int cluster_id, int command_id, TLVReader &tlv_data, + void *priv_data); diff --git a/examples/rainmaker_light/CMakeLists.txt b/examples/rainmaker_light/CMakeLists.txt index 5c24d54dd..9e74260db 100644 --- a/examples/rainmaker_light/CMakeLists.txt +++ b/examples/rainmaker_light/CMakeLists.txt @@ -32,6 +32,8 @@ endif(NOT DEFINED ENV{ESP_RMAKER_PATH}) include($ENV{IDF_PATH}/tools/cmake/project.cmake) include($ENV{ESP_MATTER_DEVICE_PATH}/esp_matter_device.cmake) +idf_build_set_property(RAINMAKER_ENABLED 1) + set(EXTRA_COMPONENT_DIRS "../common" "${IDF_PATH}/examples/common_components" diff --git a/examples/rainmaker_light/main/CMakeLists.txt b/examples/rainmaker_light/main/CMakeLists.txt index 3553bf68a..b8ba40eca 100644 --- a/examples/rainmaker_light/main/CMakeLists.txt +++ b/examples/rainmaker_light/main/CMakeLists.txt @@ -1,4 +1,4 @@ -set(PRIV_REQUIRES_LIST device esp_matter esp_matter_console route_hook app_qrcode esp_rainmaker) +set(PRIV_REQUIRES_LIST device esp_matter esp_matter_console esp_matter_rainmaker route_hook app_qrcode esp_rainmaker) idf_component_register(SRC_DIRS "." PRIV_INCLUDE_DIRS "." diff --git a/examples/rainmaker_light/main/app_rainmaker.cpp b/examples/rainmaker_light/main/app_rainmaker.cpp index 988cbe45d..e6750f5f8 100644 --- a/examples/rainmaker_light/main/app_rainmaker.cpp +++ b/examples/rainmaker_light/main/app_rainmaker.cpp @@ -18,40 +18,49 @@ #include #include #include -#include #include #include #include +#include static const char *TAG = "app_rainmaker"; extern int light_endpoint_id; #define DEFAULT_LIGHT_NAME "Light" -static esp_err_t app_rainmaker_console_handler(int argc, char **argv) +static esp_rmaker_param_val_t app_rainmaker_get_rmaker_val(esp_matter_attr_val_t *val) { - if (argc == 3 && strncmp(argv[0], "add-user", sizeof("add-user")) == 0) { - printf("%s: Starting user-node mapping\n", TAG); - if (esp_rmaker_start_user_node_mapping(argv[1], argv[2]) != ESP_OK) { - return ESP_FAIL; - } + if (val->type == ESP_MATTER_VAL_TYPE_BOOLEAN) { + return esp_rmaker_bool(val->val.b); + } else if (val->type == ESP_MATTER_VAL_TYPE_INTEGER) { + return esp_rmaker_int(val->val.i); + } else if (val->type == ESP_MATTER_VAL_TYPE_FLOAT) { + return esp_rmaker_float(val->val.f); + } else if (val->type == ESP_MATTER_VAL_TYPE_UINT8) { + return esp_rmaker_int(val->val.u8); + } else if (val->type == ESP_MATTER_VAL_TYPE_INT16) { + return esp_rmaker_int(val->val.i16); + } else if (val->type == ESP_MATTER_VAL_TYPE_UINT16) { + return esp_rmaker_int(val->val.u16); } else { - printf("%s: Invalid Usage.\n", TAG); - return ESP_ERR_INVALID_ARG; + ESP_LOGE(TAG, "Invalid val type: %d", val->type); } - return ESP_OK; + return esp_rmaker_int(0); } -static void app_rainmaker_register_commands() +static esp_matter_attr_val_t app_rainmaker_get_matter_val(esp_rmaker_param_val_t val) { - esp_matter_console_command_t command = { - .name = "rainmaker", - .description = "Initiate ESP RainMaker User-Node mapping from the node. " - "Usage: matter esp rainmaker add-user ", - .handler = app_rainmaker_console_handler, - }; - esp_matter_console_add_command(&command); + if (val.type == RMAKER_VAL_TYPE_BOOLEAN) { + return esp_matter_bool(val.val.b); + } else if (val.type == RMAKER_VAL_TYPE_INTEGER) { + return esp_matter_int(val.val.i); + } else if (val.type == RMAKER_VAL_TYPE_FLOAT) { + return esp_matter_float(val.val.f); + } else { + ESP_LOGE(TAG, "Invalid val type: %d", val.type); + } + return esp_matter_int(0); } static const char *app_rainmaker_get_device_name_from_id(int endpoint_id) @@ -167,6 +176,44 @@ static bool app_rainmaker_get_param_bounds_from_id(int cluster_id, int attribute return false; } +static esp_err_t app_rainmaker_param_add_ui_type(esp_rmaker_param_t *param, esp_matter_cluster_t *cluster, + esp_matter_attribute_t *attribute) +{ + int cluster_id = esp_matter_cluster_get_id(cluster); + int attribute_id = esp_matter_attribute_get_id(attribute); + const char *ui_type = app_rainmaker_get_param_ui_type_from_id(cluster_id, attribute_id); + if (!ui_type) { + return ESP_OK; + } + return esp_rmaker_param_add_ui_type(param, ui_type); +} + +static esp_err_t app_rainmaker_param_add_bounds(esp_rmaker_param_t *param, esp_matter_cluster_t *cluster, + esp_matter_attribute_t *attribute) +{ + esp_matter_attr_bounds_t *bounds = esp_matter_attribute_get_bounds(attribute); + if (bounds) { + esp_rmaker_param_val_t min_val = app_rainmaker_get_rmaker_val(&bounds->min); + esp_rmaker_param_val_t max_val = app_rainmaker_get_rmaker_val(&bounds->max); + esp_rmaker_param_val_t step_val = esp_rmaker_int(1); + return esp_rmaker_param_add_bounds(param, min_val, max_val, step_val); + } + + /* If bounds are not set for the attribute, check if there are any bounds to be added based on the id */ + int cluster_id = esp_matter_cluster_get_id(cluster); + int attribute_id = esp_matter_attribute_get_id(attribute); + int min = 0, max = 0, step = 0; + bool add_bounds = app_rainmaker_get_param_bounds_from_id(cluster_id, attribute_id, &min, &max, &step); + if (add_bounds) { + esp_rmaker_param_val_t min_val = esp_rmaker_int(min); + esp_rmaker_param_val_t max_val = esp_rmaker_int(max); + esp_rmaker_param_val_t step_val = esp_rmaker_int(step); + return esp_rmaker_param_add_bounds(param, min_val, max_val, step_val); + } + + return ESP_OK; +} + static int app_rainmaker_get_cluster_id_from_name(const char *param_name) { if (strcmp(param_name, ESP_RMAKER_DEF_POWER_NAME) == 0) { @@ -195,40 +242,6 @@ static int app_rainmaker_get_attribute_id_from_name(const char *param_name) return 0; } -static esp_rmaker_param_val_t app_rainmaker_get_rmaker_val(esp_matter_attr_val_t *val) -{ - if (val->type == ESP_MATTER_VAL_TYPE_BOOLEAN) { - return esp_rmaker_bool(val->val.b); - } else if (val->type == ESP_MATTER_VAL_TYPE_INTEGER) { - return esp_rmaker_int(val->val.i); - } else if (val->type == ESP_MATTER_VAL_TYPE_FLOAT) { - return esp_rmaker_float(val->val.f); - } else if (val->type == ESP_MATTER_VAL_TYPE_UINT8) { - return esp_rmaker_int(val->val.u8); - } else if (val->type == ESP_MATTER_VAL_TYPE_INT16) { - return esp_rmaker_int(val->val.i16); - } else if (val->type == ESP_MATTER_VAL_TYPE_UINT16) { - return esp_rmaker_int(val->val.u16); - } else { - ESP_LOGE(TAG, "Invalid val type: %d", val->type); - } - return esp_rmaker_int(0); -} - -static esp_matter_attr_val_t app_rainmaker_get_matter_val(esp_rmaker_param_val_t val) -{ - if (val.type == RMAKER_VAL_TYPE_BOOLEAN) { - return esp_matter_bool(val.val.b); - } else if (val.type == RMAKER_VAL_TYPE_INTEGER) { - return esp_matter_int(val.val.i); - } else if (val.type == RMAKER_VAL_TYPE_FLOAT) { - return esp_matter_float(val.val.f); - } else { - ESP_LOGE(TAG, "Invalid val type: %d", val.type); - } - return esp_matter_int(0); -} - esp_err_t app_rainmaker_attribute_update(int endpoint_id, int cluster_id, int attribute_id, esp_matter_attr_val_t *val) { const char *device_name = app_rainmaker_get_device_name_from_id(endpoint_id); @@ -250,6 +263,12 @@ esp_err_t app_rainmaker_attribute_update(int endpoint_id, int cluster_id, int at return esp_rmaker_param_update_and_report(param, rmaker_val); } +esp_err_t app_rainmaker_command_callback(int endpoint_id, int cluster_id, int command_id, TLVReader &tlv_data, + void *priv_data) +{ + return esp_matter_rainmaker_command_callback(endpoint_id, cluster_id, command_id, tlv_data, priv_data); +} + /* Callback to handle commands received from the RainMaker cloud */ static esp_err_t write_cb(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param, const esp_rmaker_param_val_t val, void *priv_data, esp_rmaker_write_ctx_t *ctx) @@ -269,7 +288,54 @@ static esp_err_t write_cb(const esp_rmaker_device_t *device, const esp_rmaker_pa return esp_matter_attribute_update(endpoint_id, cluster_id, attribute_id, &matter_val); } -static void app_rainmaker_device_create() +static esp_rmaker_device_t *app_rainmaker_device_create(const esp_rmaker_node_t *node, esp_matter_endpoint_t *endpoint) +{ + int endpoint_id = esp_matter_endpoint_get_id(endpoint); + const char *device_name = app_rainmaker_get_device_name_from_id(endpoint_id); + if (!device_name) { + return NULL; + } + /* Add this device only if endpoint_id has been handled */ + int device_type_id = esp_matter_endpoint_get_device_type_id(endpoint_id); + const char *device_type = app_rainmaker_get_device_type_from_id(device_type_id); + esp_rmaker_device_t *device = esp_rmaker_device_create(device_name, device_type, NULL); + if (!device) { + ESP_LOGE(TAG, "Could not create rainmaker device"); + return NULL; + } + esp_rmaker_device_add_cb(device, write_cb, NULL); + esp_rmaker_node_add_device(node, device); + return device; +} + +static esp_rmaker_param_t *app_rainmaker_param_create(esp_rmaker_device_t *device, esp_matter_cluster_t *cluster, + esp_matter_attribute_t *attribute) +{ + int cluster_id = esp_matter_cluster_get_id(cluster); + int attribute_id = esp_matter_attribute_get_id(attribute); + const char *param_name = app_rainmaker_get_param_name_from_id(cluster_id, attribute_id); + if (!param_name) { + return NULL; + } + /* Add this param only if attribute_id corresponding to the cluster_id is handled */ + const char *param_type = app_rainmaker_get_param_type_from_id(cluster_id, attribute_id); + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + esp_matter_attribute_get_val(attribute, &val); + esp_rmaker_param_val_t rmaker_val = app_rainmaker_get_rmaker_val(&val); + esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, param_type, rmaker_val, + PROP_FLAG_READ | PROP_FLAG_WRITE); + if (!param) { + ESP_LOGE(TAG, "Could not create rainmaker param"); + return NULL; + } + /* Add additional param details */ + app_rainmaker_param_add_ui_type(param, cluster, attribute); + app_rainmaker_param_add_bounds(param, cluster, attribute); + esp_rmaker_device_add_param(device, param); + return param; +} + +static void app_rainmaker_data_model_create() { const esp_rmaker_node_t *node = esp_rmaker_get_node(); esp_matter_node_t *matter_node = esp_matter_node_get(); @@ -277,54 +343,16 @@ static void app_rainmaker_device_create() /* Parse all endpoints */ while (endpoint) { - int endpoint_id = esp_matter_endpoint_get_id(endpoint); - const char *device_name = app_rainmaker_get_device_name_from_id(endpoint_id); - - /* Proceed only if endpoint_id has been handled */ - if (device_name) { - int device_type_id = esp_matter_endpoint_get_device_type_id(endpoint_id); - const char *device_type = app_rainmaker_get_device_type_from_id(device_type_id); - esp_rmaker_device_t *device = esp_rmaker_device_create(device_name, device_type, NULL); - if (!device) { - ESP_LOGE(TAG, "Could not create rainmaker device"); - continue; - } - esp_rmaker_device_add_cb(device, write_cb, NULL); - esp_rmaker_node_add_device(node, device); - + esp_rmaker_device_t *device = app_rainmaker_device_create(node, endpoint); + /* Proceed only if the device has been handled */ + if (device) { esp_matter_cluster_t *cluster = esp_matter_cluster_get_first(endpoint); /* Parse all clusters */ while (cluster) { - int cluster_id = esp_matter_cluster_get_id(cluster); esp_matter_attribute_t *attribute = esp_matter_attribute_get_first(cluster); /* Parse all attributes */ while (attribute) { - int attribute_id = esp_matter_attribute_get_id(attribute); - const char *param_name = app_rainmaker_get_param_name_from_id(cluster_id, attribute_id); - /* Proceed only if attribute_id corresponding to the cluster_id is handled */ - if (param_name) { - const char *param_type = app_rainmaker_get_param_type_from_id(cluster_id, attribute_id); - const char *ui_type = app_rainmaker_get_param_ui_type_from_id(cluster_id, attribute_id); - int min = 0, max = 0, step = 0; - bool add_bounds = app_rainmaker_get_param_bounds_from_id(cluster_id, attribute_id, &min, &max, - &step); - - esp_matter_attr_val_t val = esp_matter_invalid(NULL); - esp_matter_attribute_get_val(attribute, &val); - esp_rmaker_param_val_t rmaker_val = app_rainmaker_get_rmaker_val(&val); - esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, param_type, rmaker_val, - PROP_FLAG_READ | PROP_FLAG_WRITE); - - /* Add additional param details */ - if (ui_type) { - esp_rmaker_param_add_ui_type(param, ui_type); - } - if (add_bounds) { - esp_rmaker_param_add_bounds(param, esp_rmaker_int(min), esp_rmaker_int(max), - esp_rmaker_int(step)); - } - esp_rmaker_device_add_param(device, param); - } + app_rainmaker_param_create(device, cluster, attribute); attribute = esp_matter_attribute_get_next(attribute); } cluster = esp_matter_cluster_get_next(cluster); @@ -334,121 +362,10 @@ static void app_rainmaker_device_create() } } -#define ESP_MATTER_RAINMAKER_ENDPOINT_ID 0x0 /* Same as root node endpoint. This will always be endpoint_id 0. */ -#define ESP_MATTER_RAINMAKER_CLUSTER_ID 0x131B0000 /* 0x131B == manufacturer code */ -#define ESP_MATTER_RAINMAKER_STATUS_ATTRIBUTE_ID 0x0 -#define ESP_MATTER_RAINMAKER_CONFIGURATION_COMMAND_ID 0x0 -#define ESP_MATTER_RAINMAKER_CLUSTER_REVISION 1 -#define ESP_MATTER_RAINMAKER_COMMAND_LIMIT 5 /* This command can be called 5 times per reboot */ -#define ESP_MATTER_RAINMAKER_MAX_DATA_LEN 40 - -static esp_err_t app_rainmaker_status_attribute_update(bool status) -{ - int endpoint_id = ESP_MATTER_RAINMAKER_ENDPOINT_ID; - int cluster_id = ESP_MATTER_RAINMAKER_CLUSTER_ID; - int attribute_id = ESP_MATTER_RAINMAKER_STATUS_ATTRIBUTE_ID; - esp_matter_attr_val_t val = esp_matter_bool(status); - return esp_matter_attribute_update(endpoint_id, cluster_id, attribute_id, &val); -} - -static void app_rainmaker_user_node_association_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, - void* event_data) -{ - /* This event handler is only for user node association status */ - if (event_base == RMAKER_EVENT) { - if (event_id == RMAKER_EVENT_USER_NODE_MAPPING_DONE) { - ESP_LOGI(TAG, "User node association complete. Updating the status attribute."); - app_rainmaker_status_attribute_update(true); - } else if (event_id == RMAKER_EVENT_USER_NODE_MAPPING_RESET) { - ESP_LOGI(TAG, "User node association reset. Updating the status attribute."); - app_rainmaker_status_attribute_update(false); - } - } -} - -esp_err_t app_rainmaker_command_callback(int endpoint_id, int cluster_id, int command_id, TLVReader &tlv_data, - void *priv_data) -{ - /* Return if this is not the rainmaker configuration command */ - if (endpoint_id != ESP_MATTER_RAINMAKER_ENDPOINT_ID || cluster_id != ESP_MATTER_RAINMAKER_CLUSTER_ID - || command_id != ESP_MATTER_RAINMAKER_CONFIGURATION_COMMAND_ID) { - return ESP_OK; - } - ESP_LOGI(TAG, "RainMaker configuration command callback"); - static int command_count = ESP_MATTER_RAINMAKER_COMMAND_LIMIT; - if (command_count <= 0) { - ESP_LOGE(TAG, "This command has reached a limit. Please reboot to try again."); - return ESP_FAIL; - } - command_count--; - - /* Parse the tlv data */ - chip::CharSpan config_value; - chip::app::DataModel::Decode(tlv_data, config_value); - const char *data = config_value.data(); - int size = config_value.size(); - - /* The expected format of the data is "::" */ - char ch = ':'; - char *check = strchr(data, (int)ch); - if (check == NULL) { - ESP_LOGE(TAG, "':' not found in the received data: %.*s. The expected format is \"::\"", - size, data); - return ESP_FAIL; - } - - /* Get sizes */ - int user_id_index = 0; - int user_id_len = (int)(strchr(data, (int)ch) - data); /* (first ':') - (start of string) */ - int secret_key_index = (int)(strrchr(data, (int)ch) - data) + 1; /* (last ':') - (start of string) + 1 */ - int secret_key_len = size - secret_key_index; - if (user_id_len <= 0 || user_id_len >= ESP_MATTER_RAINMAKER_MAX_DATA_LEN || secret_key_len <= 0 - || secret_key_len >= ESP_MATTER_RAINMAKER_MAX_DATA_LEN) { - ESP_LOGE(TAG, "User id or secret key length invalid: user_id_len: %d, secret_key_len: %d", user_id_len, - secret_key_len); - return ESP_FAIL; - } - - /* Copy the data. This done to make the strings NULL terminated. */ - char user_id[ESP_MATTER_RAINMAKER_MAX_DATA_LEN] = {0}; - char secret_key[ESP_MATTER_RAINMAKER_MAX_DATA_LEN] = {0}; - strncpy(user_id, &data[user_id_index], user_id_len); - strncpy(secret_key, &data[secret_key_index], secret_key_len); - ESP_LOGI(TAG, "user_id: %s, secret_key: %s", user_id, secret_key); - - /* Call the rainmaker API */ - if (strlen(user_id) > 0 && strlen(secret_key) > 0) { - esp_rmaker_start_user_node_mapping(user_id, secret_key); - } - return ESP_OK; -} - -esp_err_t app_rainmaker_custom_cluster_create() -{ - /* Get the endpoint */ - esp_matter_node_t *node = esp_matter_node_get(); - esp_matter_endpoint_t *endpoint = esp_matter_endpoint_get(node, ESP_MATTER_RAINMAKER_ENDPOINT_ID); - - /* Create custom rainmaker cluster */ - esp_matter_cluster_t *cluster = esp_matter_cluster_create(endpoint, ESP_MATTER_RAINMAKER_CLUSTER_ID, ESP_MATTER_CLUSTER_FLAG_SERVER); - esp_matter_attribute_create(cluster, ZCL_CLUSTER_REVISION_SERVER_ATTRIBUTE_ID, ESP_MATTER_ATTRIBUTE_FLAG_NONE, - esp_matter_uint16(ESP_MATTER_RAINMAKER_CLUSTER_REVISION)); - - /* Create custom status attribute */ - /* Update the value of the attribute after esp_rmaker_node_init() is done */ - esp_matter_attribute_create(cluster, ESP_MATTER_RAINMAKER_STATUS_ATTRIBUTE_ID, ESP_MATTER_ATTRIBUTE_FLAG_NONE, - esp_matter_bool(false)); - - /* Create custom configuration command */ - esp_matter_command_create(cluster, ESP_MATTER_RAINMAKER_CONFIGURATION_COMMAND_ID, - ESP_MATTER_COMMAND_FLAG_CLIENT_GENERATED | ESP_MATTER_COMMAND_FLAG_CUSTOM, NULL); - return ESP_OK; -} - esp_err_t app_rainmaker_init() { /* Add custom rainmaker cluster */ - return app_rainmaker_custom_cluster_create(); + return esp_matter_rainmaker_init(); } esp_err_t app_rainmaker_start() @@ -466,8 +383,8 @@ esp_err_t app_rainmaker_start() return ESP_FAIL; } - /* Create a device and add the relevant parameters to it */ - app_rainmaker_device_create(); + /* Create the rainmaker device and its params from matter data model */ + app_rainmaker_data_model_create(); /* Enable OTA */ esp_rmaker_ota_config_t ota_config = { @@ -485,19 +402,8 @@ esp_err_t app_rainmaker_start() /* Enable scheduling. */ esp_rmaker_schedule_enable(); - /* Check user node association */ - if (esp_rmaker_user_node_mapping_get_state() == ESP_RMAKER_USER_MAPPING_DONE) { - app_rainmaker_status_attribute_update(true); - } - /* Register an event handler and update the state later */ - esp_event_handler_register(RMAKER_EVENT, RMAKER_EVENT_USER_NODE_MAPPING_DONE, - &app_rainmaker_user_node_association_event_handler, NULL); - esp_event_handler_register(RMAKER_EVENT, RMAKER_EVENT_USER_NODE_MAPPING_RESET, - &app_rainmaker_user_node_association_event_handler, NULL); - /* Start the ESP RainMaker Agent */ + esp_matter_rainmaker_start(); esp_rmaker_start(); - - app_rainmaker_register_commands(); return ESP_OK; }