ESPHome 2026.8.0b4
Loading...
Searching...
No Matches
preferences.cpp
Go to the documentation of this file.
1#ifdef USE_LIBRETINY
2
3#include "preferences.h"
5#include "esphome/core/log.h"
6#include <cstring>
7#include <vector>
8
9namespace esphome::libretiny {
10
11static const char *const TAG = "preferences";
12
13struct NVSData {
14 uint32_t key;
15 SmallInlineBuffer<8> data; // Most prefs fit in 8 bytes (covers fan, cover, select, etc.)
16};
17
18static std::vector<NVSData> s_pending_save; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
19
20bool LibreTinyPreferenceBackend::save(const uint8_t *data, size_t len) {
21 // try find in pending saves and update that
22 for (auto &obj : s_pending_save) {
23 if (obj.key == this->key) {
24 obj.data.set(data, len);
25 return true;
26 }
27 }
28 NVSData save{};
29 save.key = this->key;
30 save.data.set(data, len);
31 s_pending_save.push_back(std::move(save));
32 ESP_LOGVV(TAG, "s_pending_save: key: %" PRIu32 ", len: %zu", this->key, len);
33 return true;
34}
35
36bool LibreTinyPreferenceBackend::load(uint8_t *data, size_t len) {
37 // try find in pending saves and load from that
38 for (auto &obj : s_pending_save) {
39 if (obj.key == this->key) {
40 if (obj.data.size() != len) {
41 // size mismatch
42 return false;
43 }
44 memcpy(data, obj.data.data(), len);
45 return true;
46 }
47 }
48
49 char key_str[UINT32_MAX_STR_SIZE];
50 uint32_to_str(key_str, this->key);
51 fdb_blob_make(this->blob, data, len);
52 size_t actual_len = fdb_kv_get_blob(this->db, key_str, this->blob);
53 if (actual_len != len) {
54 ESP_LOGVV(TAG, "NVS length does not match (%zu!=%zu)", actual_len, len);
55 return false;
56 } else {
57 ESP_LOGVV(TAG, "fdb_kv_get_blob: key: %s, len: %zu", key_str, len);
58 }
59 return true;
60}
61
63 //
64 fdb_err_t err = fdb_kvdb_init(&this->db, "esphome", "kvs", NULL, NULL);
65 if (err != FDB_NO_ERR) {
66 LT_E("fdb_kvdb_init(...) failed: %d", err);
67 } else {
68 LT_I("Preferences initialized");
69 }
70}
71
73 // NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
75}
76
79 backend.key = type;
80 backend.db = &this->db;
81 backend.blob = &this->blob;
82 return backend;
83}
84
86 LibreTinyPreferenceBackend backend = this->make_backend_(type);
87 return backend.load(data, len);
88}
89
91 if (s_pending_save.empty())
92 return true;
93
94 ESP_LOGV(TAG, "Saving %zu items...", s_pending_save.size());
95 int cached = 0, written = 0, failed = 0;
96 fdb_err_t last_err = FDB_NO_ERR;
97 uint32_t last_key = 0;
98
99 for (const auto &save : s_pending_save) {
100 char key_str[UINT32_MAX_STR_SIZE];
101 uint32_to_str(key_str, save.key);
102 ESP_LOGVV(TAG, "Checking if FDB data %s has changed", key_str);
103 if (this->is_changed_(&this->db, save, key_str)) {
104 ESP_LOGV(TAG, "sync: key: %s, len: %zu", key_str, save.data.size());
105 fdb_blob_make(&this->blob, save.data.data(), save.data.size());
106 fdb_err_t err = fdb_kv_set_blob(&this->db, key_str, &this->blob);
107 if (err != FDB_NO_ERR) {
108 ESP_LOGV(TAG, "fdb_kv_set_blob('%s', len=%zu) failed: %d", key_str, save.data.size(), err);
109 failed++;
110 last_err = err;
111 last_key = save.key;
112 continue;
113 }
114 written++;
115 } else {
116 ESP_LOGV(TAG, "FDB data not changed; skipping %" PRIu32 " len=%zu", save.key, save.data.size());
117 cached++;
118 }
119 }
120 s_pending_save.clear();
121
122 if (failed > 0) {
123 ESP_LOGE(TAG, "Writing %d items: %d cached, %d written, %d failed. Last error=%d for key=%" PRIu32,
124 cached + written + failed, cached, written, failed, last_err, last_key);
125 } else if (written > 0) {
126 ESP_LOGD(TAG, "Writing %d items: %d cached, %d written, %d failed", cached + written + failed, cached, written,
127 failed);
128 } else {
129 ESP_LOGV(TAG, "Writing %d items: %d cached, %d written, %d failed", cached + written + failed, cached, written,
130 failed);
131 }
132
133 return failed == 0;
134}
135
136bool LibreTinyPreferences::is_changed_(fdb_kvdb_t db, const NVSData &to_save, const char *key_str) {
137 struct fdb_kv kv;
138 fdb_kv_t kvp = fdb_kv_get_obj(db, key_str, &kv);
139 if (kvp == nullptr) {
140 ESP_LOGV(TAG, "fdb_kv_get_obj('%s'): nullptr - the key might not be set yet", key_str);
141 return true;
142 }
143
144 // Check size first - if different, data has changed
145 if (kv.value_len != to_save.data.size()) {
146 return true;
147 }
148
149 // Most preferences are small, use stack buffer with heap fallback for large ones
150 SmallBufferWithHeapFallback<256> stored_data(kv.value_len);
151 fdb_blob_make(&this->blob, stored_data.get(), kv.value_len);
152 size_t actual_len = fdb_kv_get_blob(db, key_str, &this->blob);
153 if (actual_len != kv.value_len) {
154 ESP_LOGV(TAG, "fdb_kv_get_blob('%s') len mismatch: %zu != %zu", key_str, actual_len, (size_t) kv.value_len);
155 return true;
156 }
157
158 // Compare the actual data
159 return memcmp(to_save.data.data(), stored_data.get(), kv.value_len) != 0;
160}
161
163 ESP_LOGD(TAG, "Erasing storage");
164 s_pending_save.clear();
165
166 fdb_kv_set_default(&this->db);
167 fdb_kvdb_deinit(&this->db);
168 return true;
169}
170
171static LibreTinyPreferences s_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
172
173LibreTinyPreferences *get_preferences() { return &s_preferences; }
174
176 s_preferences.open();
177 global_preferences = &s_preferences;
178}
179
180} // namespace esphome::libretiny
181
182namespace esphome {
183ESPPreferences *global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
184} // namespace esphome
185
186#endif // USE_LIBRETINY
Helper class for efficient buffer allocation - uses stack for small sizes, heap for large This is use...
Definition helpers.h:737
bool load(uint8_t *data, size_t len)
bool save(const uint8_t *data, size_t len)
ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)
Definition preferences.h:15
bool load_from_key(uint32_t type, uint8_t *data, size_t len)
One-shot read of a stored preference by key, without allocating a backend.
bool is_changed_(fdb_kvdb_t db, const NVSData &to_save, const char *key_str)
LibreTinyPreferenceBackend make_backend_(uint32_t type)
uint16_t type
LibreTinyPreferences * get_preferences()
Preferences ESPPreferences
Definition preferences.h:43
const void size_t len
Definition hal.h:64
size_t uint32_to_str(std::span< char, UINT32_MAX_STR_SIZE > buf, uint32_t val)
Write unsigned 32-bit integer to buffer with compile-time size check.
Definition helpers.h:1353
ESPPreferences * global_preferences
int written
Definition helpers.h:1069
static void uint32_t
uint16_t length
Definition tt21100.cpp:0