mirror of
https://github.com/espressif/esp-matter.git
synced 2026-04-27 19:13:13 +00:00
app_rainmaker: Add support for custom rainmaker cluster
This also adds the custom attribute and custom command for required user node association. sdkconfig.defauls: Setting self claim by default. This does not get set for esp32, since the config option is not valid for esp32.
This commit is contained in:
@@ -35,7 +35,7 @@ export ESP_RMAKER_PATH=/path/to/esp-rainmaker
|
||||
|
||||
### RainMaker Claiming
|
||||
|
||||
This need to be done before flashing the firmware. Note the mac address of the device.
|
||||
If self-claiming is not enabled/supported, this need to be done before flashing the firmware.
|
||||
|
||||
RainMaker CLI:
|
||||
```
|
||||
@@ -45,19 +45,34 @@ $ rainmaker.py claim --addr 0x3E0000 $ESPPORT
|
||||
|
||||
### RainMaker User-Node Association
|
||||
|
||||
This need to be done after commissioning.
|
||||
This needs to be done after commissioning.
|
||||
|
||||
Check if the device already has user node association done, using the custom RainMaker cluster (cluster_id: 0xc00):
|
||||
```
|
||||
$ ./out/debug/chip-tool any read-by-id 0xc00 0x0 0x1 0x0
|
||||
```
|
||||
* If the above custom status attribute (attribute_id: 0x0) returns true, the association has already been done.
|
||||
* If the attribute returns false, the association has not been done. And the below custom configuration command
|
||||
(command_id: 0x0) can be used to do the association.
|
||||
|
||||
|
||||
RainMaker CLI:
|
||||
|
||||
Get the details: This will print the user_id and secret_key (do not close this):
|
||||
```
|
||||
$ rainmaker.py test --addnode <node-id>
|
||||
|
||||
>> add-user <user-id> <secret-key>
|
||||
```
|
||||
|
||||
This will print the console command to be run on the device:
|
||||
Prepare the command payload: Use the above details.
|
||||
```
|
||||
add-user <user-id> <secret-key>
|
||||
payload: <user_id>::<secret_key>
|
||||
```
|
||||
|
||||
Use these details in the below command on the device console.
|
||||
Now use the payload to run the RainMaker configuration command from chip-tool:
|
||||
```
|
||||
matter esp rainmaker add-user <user-id> <secret-key>
|
||||
$ ./out/debug/chip-tool any command-by-id 0xc00 0x0 '"<user_id>::<secret_key>"' 0x1 0x0
|
||||
```
|
||||
|
||||
The device/node should now be associated with the user.
|
||||
|
||||
@@ -55,6 +55,14 @@ static esp_err_t app_attribute_update_cb(esp_matter_callback_type_t type, int en
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t app_command_callback(int endpoint_id, int cluster_id, int command_id, TLVReader &tlv_data, void *priv_data)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
/* Pass all the commands to all the ecosystems, if their command callbacks exist */
|
||||
err = app_rainmaker_command_callback(endpoint_id, cluster_id, command_id, tlv_data, priv_data);
|
||||
return err;
|
||||
}
|
||||
|
||||
extern "C" void app_main()
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
@@ -81,6 +89,10 @@ extern "C" void app_main()
|
||||
/* Initialize driver */
|
||||
app_driver_init();
|
||||
|
||||
/* Initialize rainmaker */
|
||||
esp_matter_command_set_custom_callback(app_command_callback, NULL);
|
||||
app_rainmaker_init();
|
||||
|
||||
/* Matter start */
|
||||
err = esp_matter_start(app_event_cb);
|
||||
if (err != ESP_OK) {
|
||||
@@ -88,8 +100,8 @@ extern "C" void app_main()
|
||||
}
|
||||
app_qrcode_print();
|
||||
|
||||
/* Initialize rainmaker */
|
||||
app_rainmaker_init();
|
||||
/* Start rainmaker */
|
||||
app_rainmaker_start();
|
||||
|
||||
#if CONFIG_ENABLE_CHIP_SHELL
|
||||
esp_matter_console_diagnostics_register_commands();
|
||||
|
||||
@@ -334,7 +334,124 @@ 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 "<user_id>::<secret_key>" */
|
||||
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 \"<user_id>::<secret_key>\"",
|
||||
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();
|
||||
}
|
||||
|
||||
esp_err_t app_rainmaker_start()
|
||||
{
|
||||
/* Initialize the ESP RainMaker Agent.
|
||||
* Note that this should be called after app_wifi_init() but before app_wifi_start()
|
||||
@@ -368,6 +485,16 @@ esp_err_t app_rainmaker_init()
|
||||
/* 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_rmaker_start();
|
||||
|
||||
|
||||
@@ -17,16 +17,28 @@ extern "C" {
|
||||
|
||||
/** Initialize ESP RainMaker
|
||||
*
|
||||
* This initializes the devices and params for RainMaker, corresponding to the endpoint and attributes. It also adds
|
||||
* RainMaker features like OTA, Scheduling, etc.
|
||||
* This adds the custom RainMaker cluster.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t app_rainmaker_init(void);
|
||||
|
||||
/** Start ESP RainMaker
|
||||
*
|
||||
* This initializes the devices and params for RainMaker, corresponding to the endpoint and attributes. It also adds
|
||||
* RainMaker features like OTA, Scheduling, etc.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t app_rainmaker_start(void);
|
||||
|
||||
esp_err_t app_rainmaker_attribute_update(int endpoint_id, int cluster_id, int attribute_id, esp_matter_attr_val_t *val);
|
||||
|
||||
esp_err_t app_rainmaker_command_callback(int endpoint_id, int cluster_id, int command_id, TLVReader &tlv_data,
|
||||
void *priv_data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -33,3 +33,7 @@ CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=3120
|
||||
#enable lwIP route hooks
|
||||
CONFIG_LWIP_HOOK_IP6_ROUTE_DEFAULT=y
|
||||
CONFIG_LWIP_HOOK_ND6_GET_GW_DEFAULT=y
|
||||
|
||||
# ESP RainMaker
|
||||
CONFIG_ESP_RMAKER_USER_ID_CHECK=y
|
||||
CONFIG_ESP_RMAKER_SELF_CLAIM=y
|
||||
|
||||
@@ -30,5 +30,8 @@ CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=3120
|
||||
CONFIG_LWIP_HOOK_IP6_ROUTE_DEFAULT=y
|
||||
CONFIG_LWIP_HOOK_ND6_GET_GW_DEFAULT=y
|
||||
|
||||
# ESP RainMaker
|
||||
CONFIG_ESP_RMAKER_USER_ID_CHECK=y
|
||||
|
||||
# Disable BLE
|
||||
CONFIG_ENABLE_CHIPOBLE=n
|
||||
|
||||
Reference in New Issue
Block a user