14static const char *
const TAG =
"lt.preferences";
17static constexpr size_t KEY_BUFFER_SIZE = 12;
21 std::unique_ptr<uint8_t[]> data;
24 void set_data(
const uint8_t *src,
size_t size) {
25 if (!this->data || this->len != size) {
26 this->data = std::make_unique<uint8_t[]>(size);
29 memcpy(this->data.get(), src, size);
33static std::vector<NVSData> s_pending_save;
35class LibreTinyPreferenceBackend :
public ESPPreferenceBackend {
41 bool save(
const uint8_t *data,
size_t len)
override {
43 for (
auto &obj : s_pending_save) {
44 if (obj.key == this->key) {
45 obj.set_data(data,
len);
51 save.set_data(data,
len);
52 s_pending_save.emplace_back(std::move(save));
53 ESP_LOGVV(TAG,
"s_pending_save: key: %" PRIu32
", len: %zu", this->key,
len);
57 bool load(uint8_t *data,
size_t len)
override {
59 for (
auto &obj : s_pending_save) {
60 if (obj.key == this->key) {
65 memcpy(data, obj.data.get(),
len);
70 char key_str[KEY_BUFFER_SIZE];
71 snprintf(key_str,
sizeof(key_str),
"%" PRIu32, this->key);
72 fdb_blob_make(this->blob, data,
len);
73 size_t actual_len = fdb_kv_get_blob(this->db, key_str, this->blob);
74 if (actual_len !=
len) {
75 ESP_LOGVV(TAG,
"NVS length does not match (%zu!=%zu)", actual_len,
len);
78 ESP_LOGVV(TAG,
"fdb_kv_get_blob: key: %s, len: %zu", key_str,
len);
84class LibreTinyPreferences :
public ESPPreferences {
91 fdb_err_t err = fdb_kvdb_init(&db,
"esphome",
"kvs", NULL, NULL);
92 if (err != FDB_NO_ERR) {
93 LT_E(
"fdb_kvdb_init(...) failed: %d", err);
95 LT_I(
"Preferences initialized");
99 ESPPreferenceObject make_preference(
size_t length, uint32_t
type,
bool in_flash)
override {
103 ESPPreferenceObject make_preference(
size_t length, uint32_t
type)
override {
104 auto *pref =
new LibreTinyPreferenceBackend();
105 pref->db = &this->db;
106 pref->blob = &this->blob;
109 return ESPPreferenceObject(pref);
112 bool sync()
override {
113 if (s_pending_save.empty())
116 ESP_LOGV(TAG,
"Saving %zu items...", s_pending_save.size());
118 int cached = 0,
written = 0, failed = 0;
119 fdb_err_t last_err = FDB_NO_ERR;
123 for (
ssize_t i = s_pending_save.size() - 1; i >= 0; i--) {
124 const auto &save = s_pending_save[i];
125 char key_str[KEY_BUFFER_SIZE];
126 snprintf(key_str,
sizeof(key_str),
"%" PRIu32, save.key);
127 ESP_LOGVV(TAG,
"Checking if FDB data %s has changed", key_str);
128 if (this->is_changed_(&this->db, save, key_str)) {
129 ESP_LOGV(TAG,
"sync: key: %s, len: %zu", key_str, save.len);
130 fdb_blob_make(&this->blob, save.data.get(), save.len);
131 fdb_err_t err = fdb_kv_set_blob(&this->db, key_str, &this->blob);
132 if (err != FDB_NO_ERR) {
133 ESP_LOGV(TAG,
"fdb_kv_set_blob('%s', len=%zu) failed: %d", key_str, save.len, err);
141 ESP_LOGD(TAG,
"FDB data not changed; skipping %" PRIu32
" len=%zu", save.key, save.len);
144 s_pending_save.erase(s_pending_save.begin() + i);
146 ESP_LOGD(TAG,
"Writing %d items: %d cached, %d written, %d failed", cached + written + failed, cached, written,
149 ESP_LOGE(TAG,
"Writing %d items failed. Last error=%d for key=%" PRIu32, failed, last_err, last_key);
156 bool is_changed_(fdb_kvdb_t db,
const NVSData &to_save,
const char *key_str) {
158 fdb_kv_t kvp = fdb_kv_get_obj(db, key_str, &kv);
159 if (kvp ==
nullptr) {
160 ESP_LOGV(TAG,
"fdb_kv_get_obj('%s'): nullptr - the key might not be set yet", key_str);
165 if (kv.value_len != to_save.len) {
170 auto stored_data = std::make_unique<uint8_t[]>(kv.value_len);
171 fdb_blob_make(&this->blob, stored_data.get(), kv.value_len);
172 size_t actual_len = fdb_kv_get_blob(db, key_str, &this->blob);
173 if (actual_len != kv.value_len) {
174 ESP_LOGV(TAG,
"fdb_kv_get_blob('%s') len mismatch: %u != %u", key_str, actual_len, kv.value_len);
179 return memcmp(to_save.data.get(), stored_data.get(), kv.value_len) != 0;
182 bool reset()
override {
183 ESP_LOGD(TAG,
"Erasing storage");
184 s_pending_save.clear();
186 fdb_kv_set_default(&db);
187 fdb_kvdb_deinit(&db);
193 auto *prefs =
new LibreTinyPreferences();
Providing packet encoding functions for exchanging data with a remote host.
ESPPreferences * global_preferences