mirror of
https://github.com/espressif/esp-matter.git
synced 2026-04-27 11:03:05 +00:00
379 lines
14 KiB
C++
379 lines
14 KiB
C++
// Copyright 2023 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_err.h>
|
|
#include <esp_log.h>
|
|
#include <nvs.h>
|
|
#include <nvs_flash.h>
|
|
#include <esp_matter_attribute_utils.h>
|
|
#include <esp_matter_mem.h>
|
|
#include <esp_matter_nvs.h>
|
|
|
|
#include <lib/support/Base64.h>
|
|
|
|
#define ESP_MATTER_NVS_PART_NAME CONFIG_ESP_MATTER_NVS_PART_NAME
|
|
|
|
namespace esp_matter {
|
|
namespace attribute {
|
|
|
|
const char * TAG = "mtr_nvs";
|
|
|
|
static void get_attribute_key(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, char *attribute_key)
|
|
{
|
|
// Convert the the endpoint_id, cluster_id, attribute_id to base64 string
|
|
uint8_t encode_buf[10] = {0};
|
|
char base64_str[17] = {0};
|
|
memcpy(&encode_buf[0], &endpoint_id, sizeof(endpoint_id));
|
|
memcpy(&encode_buf[2], &cluster_id, sizeof(cluster_id));
|
|
memcpy(&encode_buf[6], &attribute_id, sizeof(attribute_id));
|
|
chip::Base64Encode(encode_buf, 10, base64_str);
|
|
// The last two character must be '='
|
|
assert(base64_str[14] == '=' && base64_str[15] == '=');
|
|
// Copy the string before '='
|
|
strncpy(attribute_key, base64_str, 14);
|
|
attribute_key[14] = 0;
|
|
}
|
|
|
|
static esp_err_t nvs_store_val(const char *nvs_namespace, const char *attribute_key, const esp_matter_attr_val_t &val);
|
|
static esp_err_t nvs_erase_val(const char *nvs_namespace, const char *attribute_key);
|
|
|
|
static esp_err_t nvs_get_val(const char *nvs_namespace, const char *attribute_key, esp_matter_attr_val_t &val)
|
|
{
|
|
nvs_handle_t handle;
|
|
esp_err_t err = nvs_open_from_partition(ESP_MATTER_NVS_PART_NAME, nvs_namespace, NVS_READONLY, &handle);
|
|
if (err != ESP_OK) {
|
|
return err;
|
|
}
|
|
|
|
if (val.type == ESP_MATTER_VAL_TYPE_CHAR_STRING ||
|
|
val.type == ESP_MATTER_VAL_TYPE_LONG_CHAR_STRING ||
|
|
val.type == ESP_MATTER_VAL_TYPE_OCTET_STRING ||
|
|
val.type == ESP_MATTER_VAL_TYPE_LONG_OCTET_STRING ||
|
|
val.type == ESP_MATTER_VAL_TYPE_ARRAY) {
|
|
size_t len = 0;
|
|
if ((err = nvs_get_blob(handle, attribute_key, NULL, &len)) == ESP_OK) {
|
|
// This function will only be called when recovering the non-volatile attributes during reboot
|
|
// Add we should not decrease the size of the attribute value
|
|
len = std::max(len, static_cast<size_t>(val.val.a.s));
|
|
bool null_reserve = (val.type == ESP_MATTER_VAL_TYPE_CHAR_STRING) || (val.type == ESP_MATTER_VAL_TYPE_LONG_CHAR_STRING);
|
|
uint8_t *buffer = (uint8_t *)esp_matter_mem_calloc(1, len + (null_reserve ? 1 : 0));
|
|
if (!buffer) {
|
|
err = ESP_ERR_NO_MEM;
|
|
} else {
|
|
val.val.a.b = buffer;
|
|
val.val.a.t = len + (val.val.a.t - val.val.a.s);
|
|
val.val.a.s = len;
|
|
err = nvs_get_blob(handle, attribute_key, buffer, &len);
|
|
}
|
|
}
|
|
|
|
nvs_close(handle);
|
|
return err;
|
|
}
|
|
|
|
// This switch case handles primitive data types
|
|
// if value is stored as primitive data type return it, else check if its stored as blob
|
|
// and convert it to primitive data type
|
|
switch (val.type) {
|
|
case ESP_MATTER_VAL_TYPE_BOOLEAN: {
|
|
uint8_t b_val;
|
|
if ((err = nvs_get_u8(handle, attribute_key, &b_val)) == ESP_OK) {
|
|
val.val.b = (b_val != 0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ESP_MATTER_VAL_TYPE_INTEGER:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_INTEGER: {
|
|
err = nvs_get_i32(handle, attribute_key, reinterpret_cast<int32_t *>(&val.val.i));
|
|
break;
|
|
}
|
|
|
|
// no nvs api to read float, since it is stored as blob, reading as blob
|
|
case ESP_MATTER_VAL_TYPE_FLOAT:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_FLOAT: {
|
|
size_t length = sizeof(val.val.f);
|
|
err = nvs_get_blob(handle, attribute_key, &val.val.f, &length);
|
|
break;
|
|
}
|
|
|
|
case ESP_MATTER_VAL_TYPE_INT8:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_INT8: {
|
|
err = nvs_get_i8(handle, attribute_key, &val.val.i8);
|
|
break;
|
|
}
|
|
|
|
case ESP_MATTER_VAL_TYPE_UINT8:
|
|
case ESP_MATTER_VAL_TYPE_ENUM8:
|
|
case ESP_MATTER_VAL_TYPE_BITMAP8:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_UINT8:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_ENUM8:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_BITMAP8: {
|
|
err = nvs_get_u8(handle, attribute_key, &val.val.u8);
|
|
break;
|
|
}
|
|
|
|
case ESP_MATTER_VAL_TYPE_INT16:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_INT16: {
|
|
err = nvs_get_i16(handle, attribute_key, &val.val.i16);
|
|
break;
|
|
}
|
|
|
|
case ESP_MATTER_VAL_TYPE_UINT16:
|
|
case ESP_MATTER_VAL_TYPE_BITMAP16:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_UINT16:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_BITMAP16: {
|
|
err = nvs_get_u16(handle, attribute_key, &val.val.u16);
|
|
break;
|
|
}
|
|
|
|
case ESP_MATTER_VAL_TYPE_INT32:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_INT32: {
|
|
err = nvs_get_i32(handle, attribute_key, &val.val.i32);
|
|
break;
|
|
}
|
|
|
|
case ESP_MATTER_VAL_TYPE_UINT32:
|
|
case ESP_MATTER_VAL_TYPE_BITMAP32:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_UINT32:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_BITMAP32: {
|
|
err = nvs_get_u32(handle, attribute_key, &val.val.u32);
|
|
break;
|
|
}
|
|
|
|
case ESP_MATTER_VAL_TYPE_INT64:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_INT64: {
|
|
err = nvs_get_i64(handle, attribute_key, &val.val.i64);
|
|
break;
|
|
}
|
|
|
|
case ESP_MATTER_VAL_TYPE_UINT64:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_UINT64: {
|
|
err = nvs_get_u64(handle, attribute_key, &val.val.u64);
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
// handle the case where the type is not recognized
|
|
nvs_close(handle);
|
|
ESP_LOGE(TAG, "Invalid attribute type: %u", val.type);
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
}
|
|
|
|
// Found the value as primitive data type
|
|
if (err == ESP_OK) {
|
|
nvs_close(handle);
|
|
return err;
|
|
}
|
|
|
|
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
|
// Read as blob, if found, write as primitive data type
|
|
size_t len = sizeof(esp_matter_attr_val_t);
|
|
err = nvs_get_blob(handle, attribute_key, &val, &len);
|
|
if (err == ESP_OK) {
|
|
// found it as a blob, close the handle
|
|
nvs_close(handle);
|
|
|
|
// nvs_store_val always stores primitive value using primitive data type APIs
|
|
err = nvs_store_val(nvs_namespace, attribute_key, val);
|
|
if (err != ESP_OK) {
|
|
ESP_LOGE(TAG, "Failed to store as primitive data type");
|
|
}
|
|
}
|
|
}
|
|
|
|
// There is no harm calling this function even on a closed handle
|
|
nvs_close(handle);
|
|
return err;
|
|
}
|
|
|
|
static esp_err_t nvs_store_val(const char *nvs_namespace, const char *attribute_key, const esp_matter_attr_val_t &val)
|
|
{
|
|
nvs_handle_t handle;
|
|
esp_err_t err = nvs_open_from_partition(ESP_MATTER_NVS_PART_NAME, nvs_namespace, NVS_READWRITE, &handle);
|
|
if (err != ESP_OK) {
|
|
return err;
|
|
}
|
|
|
|
if (val.type == ESP_MATTER_VAL_TYPE_CHAR_STRING ||
|
|
val.type == ESP_MATTER_VAL_TYPE_LONG_CHAR_STRING ||
|
|
val.type == ESP_MATTER_VAL_TYPE_OCTET_STRING ||
|
|
val.type == ESP_MATTER_VAL_TYPE_LONG_OCTET_STRING ||
|
|
val.type == ESP_MATTER_VAL_TYPE_ARRAY) {
|
|
/* Store only if value is not NULL */
|
|
if (val.val.a.b) {
|
|
err = nvs_set_blob(handle, attribute_key, val.val.a.b, val.val.a.s);
|
|
} else {
|
|
err = nvs_erase_key(handle, attribute_key);
|
|
}
|
|
nvs_commit(handle);
|
|
} else {
|
|
// This switch case handles primitive data types
|
|
// always store values as primitive data type
|
|
switch (val.type) {
|
|
case ESP_MATTER_VAL_TYPE_BOOLEAN: {
|
|
err = nvs_set_u8(handle, attribute_key, val.val.b != 0);
|
|
break;
|
|
}
|
|
|
|
case ESP_MATTER_VAL_TYPE_INTEGER:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_INTEGER: {
|
|
err = nvs_set_i32(handle, attribute_key, val.val.i);
|
|
break;
|
|
}
|
|
|
|
// no nvs api to store float, storing as blob
|
|
case ESP_MATTER_VAL_TYPE_FLOAT:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_FLOAT: {
|
|
err = nvs_set_blob(handle, attribute_key, &val.val.f, sizeof(val.val.f));
|
|
break;
|
|
}
|
|
|
|
case ESP_MATTER_VAL_TYPE_INT8:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_INT8: {
|
|
err = nvs_set_i8(handle, attribute_key, val.val.i8);
|
|
break;
|
|
}
|
|
|
|
case ESP_MATTER_VAL_TYPE_UINT8:
|
|
case ESP_MATTER_VAL_TYPE_ENUM8:
|
|
case ESP_MATTER_VAL_TYPE_BITMAP8:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_UINT8:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_ENUM8:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_BITMAP8: {
|
|
err = nvs_set_u8(handle, attribute_key, val.val.u8);
|
|
break;
|
|
}
|
|
|
|
case ESP_MATTER_VAL_TYPE_INT16:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_INT16: {
|
|
err = nvs_set_i16(handle, attribute_key, val.val.i16);
|
|
break;
|
|
}
|
|
|
|
case ESP_MATTER_VAL_TYPE_UINT16:
|
|
case ESP_MATTER_VAL_TYPE_BITMAP16:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_UINT16:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_BITMAP16: {
|
|
err = nvs_set_u16(handle, attribute_key, val.val.u16);
|
|
break;
|
|
}
|
|
|
|
case ESP_MATTER_VAL_TYPE_INT32:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_INT32: {
|
|
err = nvs_set_i32(handle, attribute_key, val.val.i32);
|
|
break;
|
|
}
|
|
|
|
case ESP_MATTER_VAL_TYPE_UINT32:
|
|
case ESP_MATTER_VAL_TYPE_BITMAP32:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_UINT32:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_BITMAP32: {
|
|
err = nvs_set_u32(handle, attribute_key, val.val.u32);
|
|
break;
|
|
}
|
|
|
|
case ESP_MATTER_VAL_TYPE_INT64:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_INT64: {
|
|
err = nvs_set_i64(handle, attribute_key, val.val.i64);
|
|
break;
|
|
}
|
|
|
|
case ESP_MATTER_VAL_TYPE_UINT64:
|
|
case ESP_MATTER_VAL_TYPE_NULLABLE_UINT64: {
|
|
err = nvs_set_u64(handle, attribute_key, val.val.u64);
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
// handle the case where the type is not recognized
|
|
err = ESP_ERR_INVALID_ARG;
|
|
ESP_LOGE(TAG, "Invalid attribute type: %u", val.type);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
nvs_commit(handle);
|
|
nvs_close(handle);
|
|
return err;
|
|
}
|
|
|
|
static esp_err_t nvs_erase_val(const char *nvs_namespace, const char *attribute_key)
|
|
{
|
|
nvs_handle_t handle;
|
|
esp_err_t err = nvs_open_from_partition(ESP_MATTER_NVS_PART_NAME, nvs_namespace, NVS_READWRITE, &handle);
|
|
if (err != ESP_OK) {
|
|
return err;
|
|
}
|
|
err = nvs_erase_key(handle, attribute_key);
|
|
nvs_commit(handle);
|
|
nvs_close(handle);
|
|
return err;
|
|
}
|
|
|
|
esp_err_t get_val_from_nvs(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t &val)
|
|
{
|
|
/* Get attribute key */
|
|
char attribute_key[16] = {0};
|
|
get_attribute_key(endpoint_id, cluster_id, attribute_id, attribute_key);
|
|
|
|
ESP_LOGD(TAG, "read attribute from nvs: endpoint_id-0x%" PRIx16 ", cluster_id-0x%" PRIx32 ","
|
|
" attribute_id-0x%" PRIx32 "", endpoint_id, cluster_id, attribute_id);
|
|
esp_err_t err = nvs_get_val(ESP_MATTER_KVS_NAMESPACE, attribute_key, val);
|
|
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
|
// If we don't find attribute key in the esp_matter_kvs namespace, we will try to get the attribute value
|
|
// with the previous key from the previous namespace.
|
|
char nvs_namespace[16] = {0};
|
|
char old_attribute_key[16] = {0};
|
|
snprintf(nvs_namespace, 16, "endpoint_%" PRIX16 "", endpoint_id); /* endpoint_id */
|
|
snprintf(old_attribute_key, 16, "%" PRIX32 ":%" PRIX32 "", cluster_id, attribute_id); /* cluster_id:attribute_id */
|
|
err = nvs_get_val(nvs_namespace, old_attribute_key, val);
|
|
if (err == ESP_OK) {
|
|
// If we get the attribute value with the previous key, we will erase it and store it in current namespace
|
|
// with the new attribute key.
|
|
if (nvs_erase_val(nvs_namespace, old_attribute_key) != ESP_OK) {
|
|
ESP_LOGE(TAG, "Failed to erase old attribute key");
|
|
}
|
|
if (nvs_store_val(ESP_MATTER_KVS_NAMESPACE, attribute_key, val) != ESP_OK) {
|
|
ESP_LOGE(TAG, "Failed to store attribute_val with new attribute key");
|
|
}
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
esp_err_t store_val_in_nvs(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, const esp_matter_attr_val_t &val)
|
|
{
|
|
/* Get attribute key */
|
|
char attribute_key[16] = {0};
|
|
get_attribute_key(endpoint_id, cluster_id, attribute_id, attribute_key);
|
|
ESP_LOGD(TAG, "Store attribute in nvs: endpoint_id-0x%" PRIx16 ", cluster_id-0x%" PRIx32 ", attribute_id-0x%" PRIx32 "",
|
|
endpoint_id, cluster_id, attribute_id);
|
|
return nvs_store_val(ESP_MATTER_KVS_NAMESPACE, attribute_key, val);
|
|
}
|
|
|
|
esp_err_t erase_val_in_nvs(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id)
|
|
{
|
|
/* Get attribute key */
|
|
char attribute_key[16] = {0};
|
|
get_attribute_key(endpoint_id, cluster_id, attribute_id, attribute_key);
|
|
ESP_LOGD(TAG, "Erase attribute in nvs: endpoint_id-0x%" PRIx16 ", cluster_id-0x%" PRIx32 ", attribute_id-0x%" PRIx32 "",
|
|
endpoint_id, cluster_id, attribute_id);
|
|
return nvs_erase_val(ESP_MATTER_KVS_NAMESPACE, attribute_key);
|
|
}
|
|
|
|
} // namespace attribute
|
|
} // namespace esp_matter
|