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