diff --git a/applications/matter_bridge/CMakeLists.txt b/applications/matter_bridge/CMakeLists.txt index 1451de14ea9d..1b92824d75a9 100644 --- a/applications/matter_bridge/CMakeLists.txt +++ b/applications/matter_bridge/CMakeLists.txt @@ -45,9 +45,11 @@ target_sources(app PRIVATE src/bridge_shell.cpp ${COMMON_ROOT}/src/bridge/bridge_manager.cpp ${COMMON_ROOT}/src/bridge/bridged_device.cpp + ${COMMON_ROOT}/src/bridge/bridge_storage_manager.cpp src/zap-generated/IMClusterCommandHandler.cpp src/zap-generated/callback-stub.cpp ${COMMON_ROOT}/src/led_widget.cpp + ${COMMON_ROOT}/src/persistent_storage_util.cpp ) if(CONFIG_BRIDGED_DEVICE_BT) diff --git a/applications/matter_bridge/src/bridged_device_types/humidity_sensor.h b/applications/matter_bridge/src/bridged_device_types/humidity_sensor.h index bab5b6b98f90..80b67f371597 100644 --- a/applications/matter_bridge/src/bridged_device_types/humidity_sensor.h +++ b/applications/matter_bridge/src/bridged_device_types/humidity_sensor.h @@ -21,6 +21,7 @@ class HumiditySensorDevice : public BridgedDevice { uint16_t GetRelativeHumidityMeasurementClusterRevision() { return kRelativeHumidityMeasurementClusterRevision; } uint32_t GetRelativeHumidityMeasurementFeatureMap() { return kRelativeHumidityMeasurementFeatureMap; } + BridgedDevice::DeviceType GetDeviceType() const override { return BridgedDevice::DeviceType::HumiditySensor; } CHIP_ERROR HandleRead(chip::ClusterId clusterId, chip::AttributeId attributeId, uint8_t *buffer, uint16_t maxReadLength) override; CHIP_ERROR HandleReadRelativeHumidityMeasurement(chip::AttributeId attributeId, uint8_t *buffer, diff --git a/applications/matter_bridge/src/bridged_device_types/onoff_light.h b/applications/matter_bridge/src/bridged_device_types/onoff_light.h index d5bb95424168..afab15061941 100644 --- a/applications/matter_bridge/src/bridged_device_types/onoff_light.h +++ b/applications/matter_bridge/src/bridged_device_types/onoff_light.h @@ -20,6 +20,7 @@ class OnOffLightDevice : public BridgedDevice { uint16_t GetOnOffClusterRevision() { return kOnOffClusterRevision; } uint32_t GetOnOffFeatureMap() { return kOnOffFeatureMap; } + BridgedDevice::DeviceType GetDeviceType() const override { return BridgedDevice::DeviceType::OnOffLight; } CHIP_ERROR HandleRead(chip::ClusterId clusterId, chip::AttributeId attributeId, uint8_t *buffer, uint16_t maxReadLength) override; CHIP_ERROR HandleReadOnOff(chip::AttributeId attributeId, uint8_t *buffer, uint16_t maxReadLength); diff --git a/applications/matter_bridge/src/bridged_device_types/temperature_sensor.h b/applications/matter_bridge/src/bridged_device_types/temperature_sensor.h index c7a05982e092..74befce4a77d 100644 --- a/applications/matter_bridge/src/bridged_device_types/temperature_sensor.h +++ b/applications/matter_bridge/src/bridged_device_types/temperature_sensor.h @@ -21,6 +21,10 @@ class TemperatureSensorDevice : public BridgedDevice { uint16_t GetTemperatureMeasurementClusterRevision() { return kTemperatureMeasurementClusterRevision; } uint32_t GetTemperatureMeasurementFeatureMap() { return kTemperatureMeasurementFeatureMap; } + BridgedDevice::DeviceType GetDeviceType() const override + { + return BridgedDevice::DeviceType::TemperatureSensor; + } CHIP_ERROR HandleRead(chip::ClusterId clusterId, chip::AttributeId attributeId, uint8_t *buffer, uint16_t maxReadLength) override; CHIP_ERROR HandleReadTemperatureMeasurement(chip::AttributeId attributeId, uint8_t *buffer, diff --git a/samples/matter/common/src/bridge/bridge_storage_manager.cpp b/samples/matter/common/src/bridge/bridge_storage_manager.cpp new file mode 100644 index 000000000000..1d32b6b87c65 --- /dev/null +++ b/samples/matter/common/src/bridge/bridge_storage_manager.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include "bridge_storage_manager.h" + +namespace +{ +template bool LoadDataToObject(PersistentStorageNode *node, T &data) +{ + size_t readSize = 0; + + bool result = PersistentStorage::Instance().Load(node, &data, sizeof(T), readSize); + + return result; +} + +PersistentStorageNode CreateIndexNode(uint8_t bridgedDeviceIndex, PersistentStorageNode *parent) +{ + char index[BridgeStorageManager::kMaxIndexLength + 1] = { 0 }; + + snprintf(index, sizeof(index), "%d", bridgedDeviceIndex); + + return PersistentStorageNode(index, strlen(index), parent); +} + +} /* namespace */ + +bool BridgeStorageManager::StoreBridgedDevicesCount(uint8_t count) +{ + return PersistentStorage::Instance().Store(&mBridgedDevicesCount, &count, sizeof(count)); +} + +bool BridgeStorageManager::LoadBridgedDevicesCount(uint8_t &count) +{ + return LoadDataToObject(&mBridgedDevicesCount, count); +} + +bool BridgeStorageManager::StoreBridgedDevicesIndexes(uint8_t *indexes, uint8_t count) +{ + if (!indexes) { + return false; + } + + return PersistentStorage::Instance().Store(&mBridgedDevicesIndexes, indexes, count); +} + +bool BridgeStorageManager::LoadBridgedDevicesIndexes(uint8_t *indexes, uint8_t maxCount, size_t &count) +{ + if (!indexes) { + return false; + } + + return PersistentStorage::Instance().Load(&mBridgedDevicesIndexes, indexes, maxCount, count); +} + +bool BridgeStorageManager::StoreBridgedDeviceEndpointId(uint16_t endpointId, uint8_t bridgedDeviceIndex) +{ + PersistentStorageNode id = CreateIndexNode(bridgedDeviceIndex, &mBridgedDeviceEndpointId); + + return PersistentStorage::Instance().Store(&id, &endpointId, sizeof(endpointId)); +} + +bool BridgeStorageManager::LoadBridgedDeviceEndpointId(uint16_t &endpointId, uint8_t bridgedDeviceIndex) +{ + PersistentStorageNode id = CreateIndexNode(bridgedDeviceIndex, &mBridgedDeviceEndpointId); + + return LoadDataToObject(&id, endpointId); +} + +bool BridgeStorageManager::RemoveBridgedDeviceEndpointId(uint8_t bridgedDeviceIndex) +{ + PersistentStorageNode id = CreateIndexNode(bridgedDeviceIndex, &mBridgedDeviceEndpointId); + + return PersistentStorage::Instance().Remove(&id); +} + +bool BridgeStorageManager::StoreBridgedDeviceNodeLabel(const char *label, size_t labelLength, + uint8_t bridgedDeviceIndex) +{ + PersistentStorageNode id = CreateIndexNode(bridgedDeviceIndex, &mBridgedDeviceNodeLabel); + + return PersistentStorage::Instance().Store(&id, label, labelLength); +} + +bool BridgeStorageManager::LoadBridgedDeviceNodeLabel(char *label, size_t labelMaxLength, size_t &labelLength, + uint8_t bridgedDeviceIndex) +{ + PersistentStorageNode id = CreateIndexNode(bridgedDeviceIndex, &mBridgedDeviceNodeLabel); + + return PersistentStorage::Instance().Load(&id, label, labelMaxLength, labelLength); +} + +bool BridgeStorageManager::RemoveBridgedDeviceNodeLabel(uint8_t bridgedDeviceIndex) +{ + PersistentStorageNode id = CreateIndexNode(bridgedDeviceIndex, &mBridgedDeviceNodeLabel); + + return PersistentStorage::Instance().Remove(&id); +} + +bool BridgeStorageManager::StoreBridgedDeviceType(uint16_t deviceType, uint8_t bridgedDeviceIndex) +{ + PersistentStorageNode id = CreateIndexNode(bridgedDeviceIndex, &mBridgedDeviceType); + + return PersistentStorage::Instance().Store(&id, &deviceType, sizeof(deviceType)); +} + +bool BridgeStorageManager::LoadBridgedDeviceType(uint16_t &deviceType, uint8_t bridgedDeviceIndex) +{ + PersistentStorageNode id = CreateIndexNode(bridgedDeviceIndex, &mBridgedDeviceType); + + return LoadDataToObject(&id, deviceType); +} + +bool BridgeStorageManager::RemoveBridgedDeviceType(uint8_t bridgedDeviceIndex) +{ + PersistentStorageNode id = CreateIndexNode(bridgedDeviceIndex, &mBridgedDeviceType); + + return PersistentStorage::Instance().Remove(&id); +} + +bool BridgeStorageManager::StoreBridgedDevice(const BridgedDevice *device, uint8_t index) +{ + if (!device) { + return false; + } + + if (!StoreBridgedDeviceEndpointId(device->GetEndpointId(), index)) { + return false; + } + + if (!StoreBridgedDeviceNodeLabel(device->GetNodeLabel(), strlen(device->GetNodeLabel()), index)) { + return false; + } + + return StoreBridgedDeviceType(device->GetDeviceType(), index); +} + +#ifdef CONFIG_BRIDGED_DEVICE_BT +bool BridgeStorageManager::StoreBtAddress(bt_addr_le_t addr, uint8_t bridgedDeviceIndex) +{ + PersistentStorageNode id = CreateIndexNode(bridgedDeviceIndex, &mBtAddress); + + return PersistentStorage::Instance().Store(&id, &addr, sizeof(addr)); +} + +bool BridgeStorageManager::LoadBtAddress(bt_addr_le_t &addr, uint8_t bridgedDeviceIndex) +{ + PersistentStorageNode id = CreateIndexNode(bridgedDeviceIndex, &mBtAddress); + + return LoadDataToObject(&id, addr); +} + +bool BridgeStorageManager::RemoveBtAddress(uint8_t bridgedDeviceIndex) +{ + PersistentStorageNode id = CreateIndexNode(bridgedDeviceIndex, &mBtAddress); + + return PersistentStorage::Instance().Remove(&id); +} +#endif diff --git a/samples/matter/common/src/bridge/bridge_storage_manager.h b/samples/matter/common/src/bridge/bridge_storage_manager.h new file mode 100644 index 000000000000..ad867bc1b85f --- /dev/null +++ b/samples/matter/common/src/bridge/bridge_storage_manager.h @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#pragma once + +#include "bridged_device.h" +#include "persistent_storage_util.h" + +#ifdef CONFIG_BRIDGED_DEVICE_BT +#include +#endif + +/* + * The class implements the following key-values storage structure: + * + * /br/ + * /brd_cnt/ // + * /brd_ids/ // + * /brd/ + * /eid/ + * /0/ // + * /1/ // + * . + * . + * /n/ // + * /label/ + * /0/ // + * /1/ // + * . + * . + * /n/ // + * /type/ + * /0/ // + * /1/ // + * . + * . + * /n/ // + * /bt/ + * /addr/ + * /0/ // + * /1/ // + * . + * . + * /n/ // + */ +class BridgeStorageManager { +public: + static constexpr auto kMaxIndexLength = 3; + + BridgeStorageManager() + : mBridge("br", strlen("br")), mBridgedDevicesCount("brd_cnt", strlen("brd_cnt"), &mBridge), + mBridgedDevicesIndexes("brd_ids", strlen("brd_ids"), &mBridge), + mBridgedDevice("brd", strlen("brd"), &mBridge), + mBridgedDeviceEndpointId("eid", strlen("eid"), &mBridgedDevice), + mBridgedDeviceNodeLabel("label", strlen("label"), &mBridgedDevice), + mBridgedDeviceType("type", strlen("type"), &mBridgedDevice) +#ifdef CONFIG_BRIDGED_DEVICE_BT + , + mBt("bt", strlen("bt"), &mBridgedDevice), mBtAddress("addr", strlen("addr"), &mBt) +#endif + { + } + + static BridgeStorageManager &Instance() + { + static BridgeStorageManager sInstance; + return sInstance; + } + + /** + * @brief Initialize BridgeStorageManager module. + * + * @return true if module has been initialized successfully + * @return false an error occurred + */ + bool Init() { return PersistentStorage::Instance().Init(); } + + /** + * @brief Store bridged devices count into settings + * + * @param count count value to be stored + * @return true if key has been written successfully + * @return false an error occurred + */ + bool StoreBridgedDevicesCount(uint8_t count); + + /** + * @brief Load bridged devices count from settings + * + * @param count reference to the count object to be filled with loaded data + * @return true if key has been loaded successfully + * @return false an error occurred + */ + bool LoadBridgedDevicesCount(uint8_t &count); + + /** + * @brief Store bridged devices indexes into settings + * + * @param indexes address of array containing indexes to be stored + * @param count size of indexes array to be stored + * @return true if key has been written successfully + * @return false an error occurred + */ + bool StoreBridgedDevicesIndexes(uint8_t *indexes, uint8_t count); + + /** + * @brief Load bridged devices indexes from settings + * + * @param indexes address of indexes array to be filled with loaded data + * @param maxCount maximum size that can be used for indexes array + * @param count reference to the count object to be filled with size of actually loaded data + * @return true if key has been loaded successfully + * @return false an error occurred + */ + bool LoadBridgedDevicesIndexes(uint8_t *indexes, uint8_t maxCount, size_t &count); + + /** + * @brief Store bridged device endpoint id into settings + * + * @param endpointId endpoint id value to be stored + * @param bridgedDeviceIndex index describing specific bridged device using given endpoint id + * @return true if key has been written successfully + * @return false an error occurred + */ + bool StoreBridgedDeviceEndpointId(uint16_t endpointId, uint8_t bridgedDeviceIndex); + + /** + * @brief Load bridged device endpoint id from settings + * + * @param endpointId reference to the endpoint id object to be filled with loaded data + * @param bridgedDeviceIndex index describing specific bridged device using given endpoint id + * @return true if key has been loaded successfully + * @return false an error occurred + */ + bool LoadBridgedDeviceEndpointId(uint16_t &endpointId, uint8_t bridgedDeviceIndex); + + /** + * @brief Remove bridged device's endpoint id entry from settings + * + * @param bridgedDeviceIndex index describing specific bridged device's endpoint id to be removed + * @return true if key entry has been removed successfully + * @return false an error occurred + */ + bool RemoveBridgedDeviceEndpointId(uint8_t bridgedDeviceIndex); + + /** + * @brief Store bridged device node label into settings + * + * @param label address of node label to be stored + * @param labelLength length of node label + * @param bridgedDeviceIndex index describing specific bridged device using given node label + * @return true if key has been written successfully + * @return false an error occurred + */ + bool StoreBridgedDeviceNodeLabel(const char *label, size_t labelLength, uint8_t bridgedDeviceIndex); + + /** + * @brief Load bridged device node label from settings + * + * @param label address of char array to be filled with loaded data + * @param labelMaxLength maximum size that can be used for label array + * @param labelLength reference to the labelLength object to be filled with size of actually loaded data + * @param bridgedDeviceIndex index describing specific bridged device using given node label + * @return true if key has been loaded successfully + * @return false an error occurred + */ + bool LoadBridgedDeviceNodeLabel(char *label, size_t labelMaxLength, size_t &labelLength, + uint8_t bridgedDeviceIndex); + + /** + * @brief Remove bridged device's node label entry from settings + * + * @param bridgedDeviceIndex index describing specific bridged device's node label to be removed + * @return true if key entry has been removed successfully + * @return false an error occurred + */ + bool RemoveBridgedDeviceNodeLabel(uint8_t bridgedDeviceIndex); + + /** + * @brief Store bridged device Matter device type into settings + * + * @param deviceType Matter device type to be stored + * @param bridgedDeviceIndex index describing specific bridged device using given device type + * @return true if key has been written successfully + * @return false an error occurred + */ + bool StoreBridgedDeviceType(uint16_t deviceType, uint8_t bridgedDeviceIndex); + + /** + * @brief Load bridged device Matter device type from settings + * + * @param deviceType reference to the device type object to be filled with loaded data + * @param bridgedDeviceIndex index describing specific bridged device using given device type + * @return true if key has been loaded successfully + * @return false an error occurred + */ + bool LoadBridgedDeviceType(uint16_t &deviceType, uint8_t bridgedDeviceIndex); + + /** + * @brief Remove bridged device's Matter device type entry from settings + * + * @param bridgedDeviceIndex index describing specific bridged device's device type to be removed + * @return true if key entry has been removed successfully + * @return false an error occurred + */ + bool RemoveBridgedDeviceType(uint8_t bridgedDeviceIndex); + + /** + * @brief Store bridged device into settings. Helper method allowing to store endpoint id, node label and device + * type of specific bridged device using a single call. + * + * @param device address of bridged device object to be stored + * @param index index describing specific bridged device + * @return true if key has been written successfully + * @return false an error occurred + */ + bool StoreBridgedDevice(const BridgedDevice *device, uint8_t index); + +#ifdef CONFIG_BRIDGED_DEVICE_BT + + /** + * @brief Store bridged device Bluetooth LE address into settings + * + * @param addr Bluetooth LE address to be stored + * @param bridgedDeviceIndex index describing specific bridged device using given address + * @return true if key has been written successfully + * @return false an error occurred + */ + bool StoreBtAddress(bt_addr_le_t addr, uint8_t bridgedDeviceIndex); + + /** + * @brief Load bridged device Bluetooth LE address from settings + * + * @param addr reference to the address object to be filled with loaded data + * @param bridgedDeviceIndex index describing specific bridged device using given address + * @return true if key has been loaded successfully + * @return false an error occurred + */ + bool LoadBtAddress(bt_addr_le_t &addr, uint8_t bridgedDeviceIndex); + + /** + * @brief Remove bridged device's Bluetooth LE address entry from settings + * + * @param bridgedDeviceIndex index describing specific bridged device's bluetooth address to be removed + * @return true if key entry has been removed successfully + * @return false an error occurred + */ + bool RemoveBtAddress(uint8_t bridgedDeviceIndex); +#endif + +private: + PersistentStorageNode mBridge; + PersistentStorageNode mBridgedDevicesCount; + PersistentStorageNode mBridgedDevicesIndexes; + PersistentStorageNode mBridgedDevice; + PersistentStorageNode mBridgedDeviceEndpointId; + PersistentStorageNode mBridgedDeviceNodeLabel; + PersistentStorageNode mBridgedDeviceType; +#ifdef CONFIG_BRIDGED_DEVICE_BT + PersistentStorageNode mBt; + PersistentStorageNode mBtAddress; +#endif +}; diff --git a/samples/matter/common/src/bridge/bridged_device.h b/samples/matter/common/src/bridge/bridged_device.h index de58c0d83d96..4cafd4813734 100644 --- a/samples/matter/common/src/bridge/bridged_device.h +++ b/samples/matter/common/src/bridge/bridged_device.h @@ -58,8 +58,9 @@ class BridgedDevice { virtual ~BridgedDevice() { chip::Platform::MemoryFree(mDataVersion); } void Init(chip::EndpointId endpoint) { mEndpointId = endpoint; } - chip::EndpointId GetEndpointId() { return mEndpointId; } + chip::EndpointId GetEndpointId() const { return mEndpointId; } + virtual DeviceType GetDeviceType() const = 0; virtual CHIP_ERROR HandleRead(chip::ClusterId clusterId, chip::AttributeId attributeId, uint8_t *buffer, uint16_t maxReadLength) = 0; virtual CHIP_ERROR HandleWrite(chip::ClusterId clusterId, chip::AttributeId attributeId, uint8_t *buffer) = 0; @@ -70,8 +71,8 @@ class BridgedDevice { uint16_t maxReadLength); CHIP_ERROR HandleReadDescriptor(chip::AttributeId attributeId, uint8_t *buffer, uint16_t maxReadLength); - bool GetIsReachable() { return mIsReachable; } - const char *GetNodeLabel() { return mNodeLabel; } + bool GetIsReachable() const { return mIsReachable; } + const char *GetNodeLabel() const { return mNodeLabel; } uint16_t GetBridgedDeviceBasicInformationClusterRevision() { return kBridgedDeviceBasicInformationClusterRevision; diff --git a/samples/matter/common/src/persistent_storage_util.cpp b/samples/matter/common/src/persistent_storage_util.cpp new file mode 100644 index 000000000000..f27bcfdf67bb --- /dev/null +++ b/samples/matter/common/src/persistent_storage_util.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include "persistent_storage_util.h" + +#include + +LOG_MODULE_DECLARE(app, CONFIG_CHIP_APP_LOG_LEVEL); + +namespace +{ +struct ReadEntry { + void *destination; + size_t destinationBufferSize; + size_t readSize; + bool result; +}; + +/* Random magic bytes to represent an empty value. + It is needed because Zephyr settings subsystem does not distinguish an empty value from no value. +*/ +constexpr uint8_t kEmptyValue[] = { 0x22, 0xa6, 0x54, 0xd1, 0x39 }; +constexpr size_t kEmptyValueSize = sizeof(kEmptyValue); + +int LoadEntryCallback(const char *name, size_t entrySize, settings_read_cb readCb, void *cbArg, void *param) +{ + ReadEntry &entry = *static_cast(param); + + /* name != nullptr -> If requested key X is empty, process the next one + name != '\0' -> process just node X and ignore all its descendants: X */ + if (name != nullptr && *name != '\0') { + return 0; + } + + if (!entry.destination || 0 == entry.destinationBufferSize) { + /* Just found the key, do not try to read value */ + entry.result = true; + return 1; + } + + uint8_t emptyValue[kEmptyValueSize]; + + if (entrySize == kEmptyValueSize && readCb(cbArg, emptyValue, kEmptyValueSize) == kEmptyValueSize && + memcmp(emptyValue, kEmptyValue, kEmptyValueSize) == 0) { + /* There are wrong bytes stored - the same as the magic ones defined above */ + entry.result = false; + return 1; + } + + const ssize_t bytesRead = readCb(cbArg, entry.destination, entry.destinationBufferSize); + entry.readSize = bytesRead > 0 ? bytesRead : 0; + + if (entrySize > entry.destinationBufferSize) { + entry.result = false; + } else { + entry.result = bytesRead > 0; + } + + return 1; +} +} /* namespace */ + +bool PersistentStorage::Init() +{ + return settings_load() ? false : true; +} + +bool PersistentStorage::Store(PersistentStorageNode *node, const void *data, size_t dataSize) +{ + if (!data || !node) { + return false; + } + + char key[PersistentStorageNode::kMaxKeyNameLength]; + + if (!node->GetKey(key)) { + return false; + } + + return (0 == settings_save_one(key, data, dataSize)); +} + +bool PersistentStorage::Load(PersistentStorageNode *node, void *data, size_t dataMaxSize, size_t &outSize) +{ + if (!data || !node) { + return false; + } + + char key[PersistentStorageNode::kMaxKeyNameLength]; + + if (!node->GetKey(key)) { + return false; + } + + size_t resultSize; + + bool result = LoadEntry(key, data, dataMaxSize, &resultSize); + + if (result) { + outSize = resultSize; + } + + return result; +} + +bool PersistentStorage::HasEntry(PersistentStorageNode *node) +{ + if (!node) { + return false; + } + + char key[PersistentStorageNode::kMaxKeyNameLength]; + + if (!node->GetKey(key)) { + return false; + } + + return LoadEntry(key); +} + +bool PersistentStorage::Remove(PersistentStorageNode *node) +{ + if (!node) { + return false; + } + + char key[PersistentStorageNode::kMaxKeyNameLength]; + + if (!node->GetKey(key)) { + return false; + } + + if (!LoadEntry(key)) { + return false; + } + + settings_delete(key); + + return true; +} + +bool PersistentStorage::LoadEntry(const char *key, void *data, size_t dataMaxSize, size_t *outSize) +{ + ReadEntry entry{ data, dataMaxSize, 0, false }; + settings_load_subtree_direct(key, LoadEntryCallback, &entry); + + if (!entry.result) { + return false; + } + + if (outSize != nullptr) { + *outSize = entry.readSize; + } + + return true; +} + +bool PersistentStorageNode::GetKey(char *key) +{ + if (!key || mKeyName[0] == '\0') { + return false; + } + + /* Recursively call GetKey method for the parents until the full key name including all hierarchy levels will be + * created. */ + if (mParent != nullptr) { + char parentKey[kMaxKeyNameLength]; + + if (!mParent->GetKey(parentKey)) { + return false; + } + + int result = snprintf(key, kMaxKeyNameLength, "%s/%s", parentKey, mKeyName); + + if (result < 0 || result >= kMaxKeyNameLength) { + return false; + } + + } else { + /* In case of not having a parent, return only own key name. */ + strncpy(key, mKeyName, kMaxKeyNameLength); + } + + return true; +} diff --git a/samples/matter/common/src/persistent_storage_util.h b/samples/matter/common/src/persistent_storage_util.h new file mode 100644 index 000000000000..872051877d51 --- /dev/null +++ b/samples/matter/common/src/persistent_storage_util.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#pragma once + +#include +#include + +/** + * @brief Class representing single settings tree node and containing information about its key. + */ +class PersistentStorageNode { +public: + static constexpr auto kMaxKeyNameLength = (SETTINGS_MAX_NAME_LEN + 1); + + /** + * @brief Constructor assigns name of a key for this settings node and sets parent node address in the tree + * hierarchy. The node does not need to have a parent (parent = nullptr), if it is a top level element. + */ + PersistentStorageNode(const char *keyName, size_t keyNameLength, PersistentStorageNode *parent = nullptr) + { + if (keyName && keyNameLength < kMaxKeyNameLength) { + memcpy(mKeyName, keyName, keyNameLength); + mKeyName[keyNameLength] = '\0'; + } + mParent = parent; + } + + /** + * @brief Gets complete settings key name for this node including its position in the tree hierarchy. The key + * name is a key name specific for this node concatenated with the names of all parents up to the top of the + * tree. + * + * @return true if the key has been created successfully + * @return false otherwise + */ + bool GetKey(char *key); + +private: + PersistentStorageNode *mParent = nullptr; + char mKeyName[kMaxKeyNameLength] = { 0 }; +}; + +/** + * @brief Class to manage peristent storage using generic PersistentStorageNode data unit that wraps settings key + * information. + */ +class PersistentStorage { +public: + /** + * @brief Load all settings from persistent storage + * + * @return true if settings have been loaded + * @return false otherwise + */ + bool Init(); + + /** + * @brief Store data into settings + * + * @param node address of settings tree node containing information about the key + * @param data data to store into a specific settings key + * @param dataSize a size of input buffer + * @return true if key has been written successfully + * @return false an error occurred + */ + bool Store(PersistentStorageNode *node, const void *data, size_t dataSize); + + /** + * @brief Load data from settings + * + * @param node address of settings tree node containing information about the key + * @param data data buffer to load from a specific settings key + * @param dataMaxSize a size of data buffer to load + * @param outSize an actual size of read data + * @return true if key has been loaded successfully + * @return false an error occurred + */ + bool Load(PersistentStorageNode *node, void *data, size_t dataMaxSize, size_t &outSize); + + /** + * @brief Check if given key entry exists in settings + * + * @param node address of settings tree node containing information about the key + * @return true if key entry exists + * @return false if key entry does not exist + */ + bool HasEntry(PersistentStorageNode *node); + + /** + * @brief Remove given key entry from settings + * + * @param node address of settings tree node containing information about the key + * @return true if key entry has been removed successfully + * @return false an error occurred + */ + bool Remove(PersistentStorageNode *node); + + static PersistentStorage &Instance() + { + static PersistentStorage sInstance; + return sInstance; + } + +private: + bool LoadEntry(const char *key, void *data = nullptr, size_t dataMaxSize = 0, size_t *outSize = nullptr); +};