ESPHome 2025.10.0-dev
Loading...
Searching...
No Matches
preferences.cpp
Go to the documentation of this file.
1#ifdef USE_LIBRETINY
2
4#include "esphome/core/log.h"
6#include <flashdb.h>
7#include <cstring>
8#include <memory>
9#include <string>
10
11namespace esphome {
12namespace libretiny {
13
14static const char *const TAG = "lt.preferences";
15
16struct NVSData {
17 std::string key;
18 std::unique_ptr<uint8_t[]> data;
19 size_t len;
20
21 void set_data(const uint8_t *src, size_t size) {
22 data = std::make_unique<uint8_t[]>(size);
23 memcpy(data.get(), src, size);
24 len = size;
25 }
26};
27
28static std::vector<NVSData> s_pending_save; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
29
30class LibreTinyPreferenceBackend : public ESPPreferenceBackend {
31 public:
32 std::string key;
33 fdb_kvdb_t db;
34 fdb_blob_t blob;
35
36 bool save(const uint8_t *data, size_t len) override {
37 // try find in pending saves and update that
38 for (auto &obj : s_pending_save) {
39 if (obj.key == key) {
40 obj.set_data(data, len);
41 return true;
42 }
43 }
44 NVSData save{};
45 save.key = key;
46 save.set_data(data, len);
47 s_pending_save.emplace_back(std::move(save));
48 ESP_LOGVV(TAG, "s_pending_save: key: %s, len: %zu", key.c_str(), len);
49 return true;
50 }
51
52 bool load(uint8_t *data, size_t len) override {
53 // try find in pending saves and load from that
54 for (auto &obj : s_pending_save) {
55 if (obj.key == key) {
56 if (obj.len != len) {
57 // size mismatch
58 return false;
59 }
60 memcpy(data, obj.data.get(), len);
61 return true;
62 }
63 }
64
65 fdb_blob_make(blob, data, len);
66 size_t actual_len = fdb_kv_get_blob(db, key.c_str(), blob);
67 if (actual_len != len) {
68 ESP_LOGVV(TAG, "NVS length does not match (%zu!=%zu)", actual_len, len);
69 return false;
70 } else {
71 ESP_LOGVV(TAG, "fdb_kv_get_blob: key: %s, len: %zu", key.c_str(), len);
72 }
73 return true;
74 }
75};
76
77class LibreTinyPreferences : public ESPPreferences {
78 public:
79 struct fdb_kvdb db;
80 struct fdb_blob blob;
81
82 void open() {
83 //
84 fdb_err_t err = fdb_kvdb_init(&db, "esphome", "kvs", NULL, NULL);
85 if (err != FDB_NO_ERR) {
86 LT_E("fdb_kvdb_init(...) failed: %d", err);
87 } else {
88 LT_I("Preferences initialized");
89 }
90 }
91
92 ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash) override {
93 return make_preference(length, type);
94 }
95
96 ESPPreferenceObject make_preference(size_t length, uint32_t type) override {
97 auto *pref = new LibreTinyPreferenceBackend(); // NOLINT(cppcoreguidelines-owning-memory)
98 pref->db = &db;
99 pref->blob = &blob;
100
101 uint32_t keyval = type;
102 pref->key = str_sprintf("%u", keyval);
103
104 return ESPPreferenceObject(pref);
105 }
106
107 bool sync() override {
108 if (s_pending_save.empty())
109 return true;
110
111 ESP_LOGV(TAG, "Saving %zu items...", s_pending_save.size());
112 // goal try write all pending saves even if one fails
113 int cached = 0, written = 0, failed = 0;
114 fdb_err_t last_err = FDB_NO_ERR;
115 std::string last_key{};
116
117 // go through vector from back to front (makes erase easier/more efficient)
118 for (ssize_t i = s_pending_save.size() - 1; i >= 0; i--) {
119 const auto &save = s_pending_save[i];
120 ESP_LOGVV(TAG, "Checking if FDB data %s has changed", save.key.c_str());
121 if (is_changed(&db, save)) {
122 ESP_LOGV(TAG, "sync: key: %s, len: %zu", save.key.c_str(), save.len);
123 fdb_blob_make(&blob, save.data.get(), save.len);
124 fdb_err_t err = fdb_kv_set_blob(&db, save.key.c_str(), &blob);
125 if (err != FDB_NO_ERR) {
126 ESP_LOGV(TAG, "fdb_kv_set_blob('%s', len=%zu) failed: %d", save.key.c_str(), save.len, err);
127 failed++;
128 last_err = err;
129 last_key = save.key;
130 continue;
131 }
132 written++;
133 } else {
134 ESP_LOGD(TAG, "FDB data not changed; skipping %s len=%zu", save.key.c_str(), save.len);
135 cached++;
136 }
137 s_pending_save.erase(s_pending_save.begin() + i);
138 }
139 ESP_LOGD(TAG, "Writing %d items: %d cached, %d written, %d failed", cached + written + failed, cached, written,
140 failed);
141 if (failed > 0) {
142 ESP_LOGE(TAG, "Writing %d items failed. Last error=%d for key=%s", failed, last_err, last_key.c_str());
143 }
144
145 return failed == 0;
146 }
147
148 bool is_changed(const fdb_kvdb_t db, const NVSData &to_save) {
149 struct fdb_kv kv;
150 fdb_kv_t kvp = fdb_kv_get_obj(db, to_save.key.c_str(), &kv);
151 if (kvp == nullptr) {
152 ESP_LOGV(TAG, "fdb_kv_get_obj('%s'): nullptr - the key might not be set yet", to_save.key.c_str());
153 return true;
154 }
155
156 // Check size first - if different, data has changed
157 if (kv.value_len != to_save.len) {
158 return true;
159 }
160
161 // Allocate buffer on heap to avoid stack allocation for large data
162 auto stored_data = std::make_unique<uint8_t[]>(kv.value_len);
163 fdb_blob_make(&blob, stored_data.get(), kv.value_len);
164 size_t actual_len = fdb_kv_get_blob(db, to_save.key.c_str(), &blob);
165 if (actual_len != kv.value_len) {
166 ESP_LOGV(TAG, "fdb_kv_get_blob('%s') len mismatch: %u != %u", to_save.key.c_str(), actual_len, kv.value_len);
167 return true;
168 }
169
170 // Compare the actual data
171 return memcmp(to_save.data.get(), stored_data.get(), kv.value_len) != 0;
172 }
173
174 bool reset() override {
175 ESP_LOGD(TAG, "Erasing storage");
176 s_pending_save.clear();
177
178 fdb_kv_set_default(&db);
179 fdb_kvdb_deinit(&db);
180 return true;
181 }
182};
183
185 auto *prefs = new LibreTinyPreferences(); // NOLINT(cppcoreguidelines-owning-memory)
186 prefs->open();
187 global_preferences = prefs;
188}
189
190} // namespace libretiny
191
192ESPPreferences *global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
193
194} // namespace esphome
195
196#endif // USE_LIBRETINY
uint16_t type
__int64 ssize_t
Definition httplib.h:178
uint16_t reset
Definition ina226.h:5
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::string size_t len
Definition helpers.h:291
ESPPreferences * global_preferences
std::string str_sprintf(const char *fmt,...)
Definition helpers.cpp:221
uint16_t sync
Definition sun_gtil2.cpp:0
uint32_t len
uint16_t length
Definition tt21100.cpp:0