13static const char *
const TAG =
"lt.preferences";
16static constexpr size_t KEY_BUFFER_SIZE = 12;
20 SmallInlineBuffer<8> data;
23static std::vector<NVSData> s_pending_save;
25class LibreTinyPreferenceBackend :
public ESPPreferenceBackend {
31 bool save(
const uint8_t *data,
size_t len)
override {
33 for (
auto &obj : s_pending_save) {
34 if (obj.key == this->key) {
35 obj.data.set(data,
len);
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);
47 bool load(uint8_t *data,
size_t len)
override {
49 for (
auto &obj : s_pending_save) {
50 if (obj.key == this->key) {
51 if (obj.data.size() !=
len) {
55 memcpy(data, obj.data.data(),
len);
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);
68 ESP_LOGVV(TAG,
"fdb_kv_get_blob: key: %s, len: %zu", key_str,
len);
74class LibreTinyPreferences :
public ESPPreferences {
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);
85 LT_I(
"Preferences initialized");
89 ESPPreferenceObject make_preference(
size_t length, uint32_t
type,
bool in_flash)
override {
93 ESPPreferenceObject make_preference(
size_t length, uint32_t
type)
override {
94 auto *pref =
new LibreTinyPreferenceBackend();
96 pref->blob = &this->blob;
99 return ESPPreferenceObject(pref);
102 bool sync()
override {
103 if (s_pending_save.empty())
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;
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);
128 ESP_LOGD(TAG,
"FDB data not changed; skipping %" PRIu32
" len=%zu", save.key, save.data.size());
132 s_pending_save.clear();
134 ESP_LOGD(TAG,
"Writing %d items: %d cached, %d written, %d failed", cached +
written + failed, cached,
written,
137 ESP_LOGE(TAG,
"Writing %d items failed. Last error=%d for key=%" PRIu32, failed, last_err, last_key);
144 bool is_changed_(fdb_kvdb_t db,
const NVSData &to_save,
const char *key_str) {
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);
153 if (kv.value_len != to_save.data.size()) {
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);
167 return memcmp(to_save.data.data(), stored_data.get(), kv.value_len) != 0;
170 bool reset()
override {
171 ESP_LOGD(TAG,
"Erasing storage");
172 s_pending_save.clear();
174 fdb_kv_set_default(&db);
175 fdb_kvdb_deinit(&db);
180static LibreTinyPreferences s_preferences;
183 s_preferences.open();
Providing packet encoding functions for exchanging data with a remote host.
ESPPreferences * global_preferences