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