14static const char *
const TAG =
"lt.preferences";
18 std::unique_ptr<uint8_t[]> data;
21 void set_data(
const uint8_t *src,
size_t size) {
22 data = std::make_unique<uint8_t[]>(size);
23 memcpy(data.get(), src, size);
28static std::vector<NVSData> s_pending_save;
30class LibreTinyPreferenceBackend :
public ESPPreferenceBackend {
36 bool save(
const uint8_t *data,
size_t len)
override {
38 for (
auto &obj : s_pending_save) {
40 obj.set_data(data,
len);
46 save.set_data(data,
len);
47 s_pending_save.emplace_back(std::move(save));
48 ESP_LOGVV(TAG,
"s_pending_save: key: %s, len: %zu", key.c_str(),
len);
52 bool load(uint8_t *data,
size_t len)
override {
54 for (
auto &obj : s_pending_save) {
60 memcpy(data, obj.data.get(),
len);
65 fdb_blob_make(blob, data,
len);
66 size_t actual_len = fdb_kv_get_blob(db, key.c_str(), blob);
67 if (actual_len !=
len) {
68 ESP_LOGVV(TAG,
"NVS length does not match (%zu!=%zu)", actual_len,
len);
71 ESP_LOGVV(TAG,
"fdb_kv_get_blob: key: %s, len: %zu", key.c_str(),
len);
77class LibreTinyPreferences :
public ESPPreferences {
84 fdb_err_t err = fdb_kvdb_init(&db,
"esphome",
"kvs", NULL, NULL);
85 if (err != FDB_NO_ERR) {
86 LT_E(
"fdb_kvdb_init(...) failed: %d", err);
88 LT_I(
"Preferences initialized");
92 ESPPreferenceObject make_preference(
size_t length, uint32_t
type,
bool in_flash)
override {
96 ESPPreferenceObject make_preference(
size_t length, uint32_t
type)
override {
97 auto *pref =
new LibreTinyPreferenceBackend();
104 return ESPPreferenceObject(pref);
107 bool sync()
override {
108 if (s_pending_save.empty())
111 ESP_LOGV(TAG,
"Saving %zu items...", s_pending_save.size());
113 int cached = 0, written = 0, failed = 0;
114 fdb_err_t last_err = FDB_NO_ERR;
115 std::string last_key{};
118 for (
ssize_t i = s_pending_save.size() - 1; i >= 0; i--) {
119 const auto &save = s_pending_save[i];
120 ESP_LOGVV(TAG,
"Checking if FDB data %s has changed", save.key.c_str());
121 if (is_changed(&db, save)) {
122 ESP_LOGV(TAG,
"sync: key: %s, len: %zu", save.key.c_str(), save.len);
123 fdb_blob_make(&blob, save.data.get(), save.len);
124 fdb_err_t err = fdb_kv_set_blob(&db, save.key.c_str(), &blob);
125 if (err != FDB_NO_ERR) {
126 ESP_LOGV(TAG,
"fdb_kv_set_blob('%s', len=%zu) failed: %d", save.key.c_str(), save.len, err);
134 ESP_LOGD(TAG,
"FDB data not changed; skipping %s len=%zu", save.key.c_str(), save.len);
137 s_pending_save.erase(s_pending_save.begin() + i);
139 ESP_LOGD(TAG,
"Writing %d items: %d cached, %d written, %d failed", cached + written + failed, cached, written,
142 ESP_LOGE(TAG,
"Writing %d items failed. Last error=%d for key=%s", failed, last_err, last_key.c_str());
148 bool is_changed(
const fdb_kvdb_t db,
const NVSData &to_save) {
150 fdb_kv_t kvp = fdb_kv_get_obj(db, to_save.key.c_str(), &kv);
151 if (kvp ==
nullptr) {
152 ESP_LOGV(TAG,
"fdb_kv_get_obj('%s'): nullptr - the key might not be set yet", to_save.key.c_str());
157 if (kv.value_len != to_save.len) {
162 auto stored_data = std::make_unique<uint8_t[]>(kv.value_len);
163 fdb_blob_make(&blob, stored_data.get(), kv.value_len);
164 size_t actual_len = fdb_kv_get_blob(db, to_save.key.c_str(), &blob);
165 if (actual_len != kv.value_len) {
166 ESP_LOGV(TAG,
"fdb_kv_get_blob('%s') len mismatch: %u != %u", to_save.key.c_str(), actual_len, kv.value_len);
171 return memcmp(to_save.data.get(), stored_data.get(), kv.value_len) != 0;
174 bool reset()
override {
175 ESP_LOGD(TAG,
"Erasing storage");
176 s_pending_save.clear();
178 fdb_kv_set_default(&db);
179 fdb_kvdb_deinit(&db);
185 auto *prefs =
new LibreTinyPreferences();
Providing packet encoding functions for exchanging data with a remote host.
ESPPreferences * global_preferences
std::string str_sprintf(const char *fmt,...)