ESPHome 2026.5.0-dev
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 auto *pref = new LibreTinyPreferenceBackend(); // NOLINT(cppcoreguidelines-owning-memory)
74 pref->db = &this->db;
75 pref->blob = &this->blob;
76 pref->key = type;
77
78 return ESPPreferenceObject(pref);
79}
80
82 if (s_pending_save.empty())
83 return true;
84
85 ESP_LOGV(TAG, "Saving %zu items...", s_pending_save.size());
86 int cached = 0, written = 0, failed = 0;
87 fdb_err_t last_err = FDB_NO_ERR;
88 uint32_t last_key = 0;
89
90 for (const auto &save : s_pending_save) {
91 char key_str[UINT32_MAX_STR_SIZE];
92 uint32_to_str(key_str, save.key);
93 ESP_LOGVV(TAG, "Checking if FDB data %s has changed", key_str);
94 if (this->is_changed_(&this->db, save, key_str)) {
95 ESP_LOGV(TAG, "sync: key: %s, len: %zu", key_str, save.data.size());
96 fdb_blob_make(&this->blob, save.data.data(), save.data.size());
97 fdb_err_t err = fdb_kv_set_blob(&this->db, key_str, &this->blob);
98 if (err != FDB_NO_ERR) {
99 ESP_LOGV(TAG, "fdb_kv_set_blob('%s', len=%zu) failed: %d", key_str, save.data.size(), err);
100 failed++;
101 last_err = err;
102 last_key = save.key;
103 continue;
104 }
105 written++;
106 } else {
107 ESP_LOGV(TAG, "FDB data not changed; skipping %" PRIu32 " len=%zu", save.key, save.data.size());
108 cached++;
109 }
110 }
111 s_pending_save.clear();
112
113 if (failed > 0) {
114 ESP_LOGE(TAG, "Writing %d items: %d cached, %d written, %d failed. Last error=%d for key=%" PRIu32,
115 cached + written + failed, cached, written, failed, last_err, last_key);
116 } else if (written > 0) {
117 ESP_LOGD(TAG, "Writing %d items: %d cached, %d written, %d failed", cached + written + failed, cached, written,
118 failed);
119 } else {
120 ESP_LOGV(TAG, "Writing %d items: %d cached, %d written, %d failed", cached + written + failed, cached, written,
121 failed);
122 }
123
124 return failed == 0;
125}
126
127bool LibreTinyPreferences::is_changed_(fdb_kvdb_t db, const NVSData &to_save, const char *key_str) {
128 struct fdb_kv kv;
129 fdb_kv_t kvp = fdb_kv_get_obj(db, key_str, &kv);
130 if (kvp == nullptr) {
131 ESP_LOGV(TAG, "fdb_kv_get_obj('%s'): nullptr - the key might not be set yet", key_str);
132 return true;
133 }
134
135 // Check size first - if different, data has changed
136 if (kv.value_len != to_save.data.size()) {
137 return true;
138 }
139
140 // Most preferences are small, use stack buffer with heap fallback for large ones
141 SmallBufferWithHeapFallback<256> stored_data(kv.value_len);
142 fdb_blob_make(&this->blob, stored_data.get(), kv.value_len);
143 size_t actual_len = fdb_kv_get_blob(db, key_str, &this->blob);
144 if (actual_len != kv.value_len) {
145 ESP_LOGV(TAG, "fdb_kv_get_blob('%s') len mismatch: %zu != %zu", key_str, actual_len, (size_t) kv.value_len);
146 return true;
147 }
148
149 // Compare the actual data
150 return memcmp(to_save.data.data(), stored_data.get(), kv.value_len) != 0;
151}
152
154 ESP_LOGD(TAG, "Erasing storage");
155 s_pending_save.clear();
156
157 fdb_kv_set_default(&this->db);
158 fdb_kvdb_deinit(&this->db);
159 return true;
160}
161
162static LibreTinyPreferences s_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
163
164LibreTinyPreferences *get_preferences() { return &s_preferences; }
165
167 s_preferences.open();
168 global_preferences = &s_preferences;
169}
170
171} // namespace esphome::libretiny
172
173namespace esphome {
174ESPPreferences *global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
175} // namespace esphome
176
177#endif // USE_LIBRETINY
Helper class for efficient buffer allocation - uses stack for small sizes, heap for large This is use...
Definition helpers.h:706
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 is_changed_(fdb_kvdb_t db, const NVSData &to_save, const char *key_str)
uint16_t type
LibreTinyPreferences * get_preferences()
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
Preferences ESPPreferences
Definition preferences.h:42
std::string size_t len
Definition helpers.h:1045
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:1307
ESPPreferences * global_preferences
int written
Definition helpers.h:1089
static void uint32_t
uint16_t length
Definition tt21100.cpp:0