ESPHome 2026.4.0
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 <cinttypes>
7#include <cstring>
8#include <vector>
9
10namespace esphome::libretiny {
11
12static const char *const TAG = "preferences";
13
14// Buffer size for converting uint32_t to string: max "4294967295" (10 chars) + null terminator + 1 padding
15static constexpr size_t KEY_BUFFER_SIZE = 12;
16
17struct NVSData {
18 uint32_t key;
19 SmallInlineBuffer<8> data; // Most prefs fit in 8 bytes (covers fan, cover, select, etc.)
20};
21
22static std::vector<NVSData> s_pending_save; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
23
24bool LibreTinyPreferenceBackend::save(const uint8_t *data, size_t len) {
25 // try find in pending saves and update that
26 for (auto &obj : s_pending_save) {
27 if (obj.key == this->key) {
28 obj.data.set(data, len);
29 return true;
30 }
31 }
32 NVSData save{};
33 save.key = this->key;
34 save.data.set(data, len);
35 s_pending_save.push_back(std::move(save));
36 ESP_LOGVV(TAG, "s_pending_save: key: %" PRIu32 ", len: %zu", this->key, len);
37 return true;
38}
39
40bool LibreTinyPreferenceBackend::load(uint8_t *data, size_t len) {
41 // try find in pending saves and load from that
42 for (auto &obj : s_pending_save) {
43 if (obj.key == this->key) {
44 if (obj.data.size() != len) {
45 // size mismatch
46 return false;
47 }
48 memcpy(data, obj.data.data(), len);
49 return true;
50 }
51 }
52
53 char key_str[KEY_BUFFER_SIZE];
54 snprintf(key_str, sizeof(key_str), "%" PRIu32, this->key);
55 fdb_blob_make(this->blob, data, len);
56 size_t actual_len = fdb_kv_get_blob(this->db, key_str, this->blob);
57 if (actual_len != len) {
58 ESP_LOGVV(TAG, "NVS length does not match (%zu!=%zu)", actual_len, len);
59 return false;
60 } else {
61 ESP_LOGVV(TAG, "fdb_kv_get_blob: key: %s, len: %zu", key_str, len);
62 }
63 return true;
64}
65
67 //
68 fdb_err_t err = fdb_kvdb_init(&this->db, "esphome", "kvs", NULL, NULL);
69 if (err != FDB_NO_ERR) {
70 LT_E("fdb_kvdb_init(...) failed: %d", err);
71 } else {
72 LT_I("Preferences initialized");
73 }
74}
75
77 auto *pref = new LibreTinyPreferenceBackend(); // NOLINT(cppcoreguidelines-owning-memory)
78 pref->db = &this->db;
79 pref->blob = &this->blob;
80 pref->key = type;
81
82 return ESPPreferenceObject(pref);
83}
84
86 if (s_pending_save.empty())
87 return true;
88
89 ESP_LOGV(TAG, "Saving %zu items...", s_pending_save.size());
90 int cached = 0, written = 0, failed = 0;
91 fdb_err_t last_err = FDB_NO_ERR;
92 uint32_t last_key = 0;
93
94 for (const auto &save : s_pending_save) {
95 char key_str[KEY_BUFFER_SIZE];
96 snprintf(key_str, sizeof(key_str), "%" PRIu32, save.key);
97 ESP_LOGVV(TAG, "Checking if FDB data %s has changed", key_str);
98 if (this->is_changed_(&this->db, save, key_str)) {
99 ESP_LOGV(TAG, "sync: key: %s, len: %zu", key_str, save.data.size());
100 fdb_blob_make(&this->blob, save.data.data(), save.data.size());
101 fdb_err_t err = fdb_kv_set_blob(&this->db, key_str, &this->blob);
102 if (err != FDB_NO_ERR) {
103 ESP_LOGV(TAG, "fdb_kv_set_blob('%s', len=%zu) failed: %d", key_str, save.data.size(), err);
104 failed++;
105 last_err = err;
106 last_key = save.key;
107 continue;
108 }
109 written++;
110 } else {
111 ESP_LOGV(TAG, "FDB data not changed; skipping %" PRIu32 " len=%zu", save.key, save.data.size());
112 cached++;
113 }
114 }
115 s_pending_save.clear();
116
117 if (failed > 0) {
118 ESP_LOGE(TAG, "Writing %d items: %d cached, %d written, %d failed. Last error=%d for key=%" PRIu32,
119 cached + written + failed, cached, written, failed, last_err, last_key);
120 } else if (written > 0) {
121 ESP_LOGD(TAG, "Writing %d items: %d cached, %d written, %d failed", cached + written + failed, cached, written,
122 failed);
123 } else {
124 ESP_LOGV(TAG, "Writing %d items: %d cached, %d written, %d failed", cached + written + failed, cached, written,
125 failed);
126 }
127
128 return failed == 0;
129}
130
131bool LibreTinyPreferences::is_changed_(fdb_kvdb_t db, const NVSData &to_save, const char *key_str) {
132 struct fdb_kv kv;
133 fdb_kv_t kvp = fdb_kv_get_obj(db, key_str, &kv);
134 if (kvp == nullptr) {
135 ESP_LOGV(TAG, "fdb_kv_get_obj('%s'): nullptr - the key might not be set yet", key_str);
136 return true;
137 }
138
139 // Check size first - if different, data has changed
140 if (kv.value_len != to_save.data.size()) {
141 return true;
142 }
143
144 // Most preferences are small, use stack buffer with heap fallback for large ones
145 SmallBufferWithHeapFallback<256> stored_data(kv.value_len);
146 fdb_blob_make(&this->blob, stored_data.get(), kv.value_len);
147 size_t actual_len = fdb_kv_get_blob(db, key_str, &this->blob);
148 if (actual_len != kv.value_len) {
149 ESP_LOGV(TAG, "fdb_kv_get_blob('%s') len mismatch: %zu != %zu", key_str, actual_len, (size_t) kv.value_len);
150 return true;
151 }
152
153 // Compare the actual data
154 return memcmp(to_save.data.data(), stored_data.get(), kv.value_len) != 0;
155}
156
158 ESP_LOGD(TAG, "Erasing storage");
159 s_pending_save.clear();
160
161 fdb_kv_set_default(&this->db);
162 fdb_kvdb_deinit(&this->db);
163 return true;
164}
165
166static LibreTinyPreferences s_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
167
168LibreTinyPreferences *get_preferences() { return &s_preferences; }
169
171 s_preferences.open();
172 global_preferences = &s_preferences;
173}
174
175} // namespace esphome::libretiny
176
177namespace esphome {
178ESPPreferences *global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
179} // namespace esphome
180
181#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
ESPPreferences * global_preferences
int written
Definition helpers.h:1089
static void uint32_t
uint16_t length
Definition tt21100.cpp:0