mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	add preferences
This commit is contained in:
		| @@ -17,6 +17,8 @@ from esphome.const import ( | |||||||
|     CONF_BOARD, |     CONF_BOARD, | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | AUTO_LOAD = ["preferences"] | ||||||
| KEY_BOARD = "board" | KEY_BOARD = "board" | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -80,6 +82,9 @@ def zephyr_to_code(conf): | |||||||
|     zephyr_add_prj_conf("NEWLIB_LIBC_FLOAT_PRINTF", True) |     zephyr_add_prj_conf("NEWLIB_LIBC_FLOAT_PRINTF", True) | ||||||
|     zephyr_add_prj_conf("CPLUSPLUS", True) |     zephyr_add_prj_conf("CPLUSPLUS", True) | ||||||
|     zephyr_add_prj_conf("LIB_CPLUSPLUS", True) |     zephyr_add_prj_conf("LIB_CPLUSPLUS", True) | ||||||
|  |     # preferences | ||||||
|  |     zephyr_add_prj_conf("SETTINGS", True) | ||||||
|  |     zephyr_add_prj_conf("NVS", True) | ||||||
|     # watchdog |     # watchdog | ||||||
|     zephyr_add_prj_conf("WATCHDOG", True) |     zephyr_add_prj_conf("WATCHDOG", True) | ||||||
|     zephyr_add_prj_conf("WDT_DISABLE_AT_BOOT", False) |     zephyr_add_prj_conf("WDT_DISABLE_AT_BOOT", False) | ||||||
| @@ -93,8 +98,10 @@ def zephyr_to_code(conf): | |||||||
|     zephyr_add_prj_conf("USE_SEGGER_RTT", True) |     zephyr_add_prj_conf("USE_SEGGER_RTT", True) | ||||||
|     zephyr_add_prj_conf("RTT_CONSOLE", True) |     zephyr_add_prj_conf("RTT_CONSOLE", True) | ||||||
|     zephyr_add_prj_conf("LOG", True) |     zephyr_add_prj_conf("LOG", True) | ||||||
|  |     zephyr_add_prj_conf("LOG_BLOCK_IN_THREAD", True) | ||||||
|  |     zephyr_add_prj_conf("LOG_BUFFER_SIZE", 4096) | ||||||
|  |     zephyr_add_prj_conf("SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL", True) | ||||||
|  |  | ||||||
|     # zephyr_add_prj_conf("USB_DEVICE_LOG_LEVEL_ERR", True) |  | ||||||
|     zephyr_add_prj_conf("USB_CDC_ACM_LOG_LEVEL_WRN", True) |     zephyr_add_prj_conf("USB_CDC_ACM_LOG_LEVEL_WRN", True) | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,105 +2,148 @@ | |||||||
|  |  | ||||||
| #include "esphome/core/preferences.h" | #include "esphome/core/preferences.h" | ||||||
| #include "esphome/core/log.h" | #include "esphome/core/log.h" | ||||||
|  | #include <zephyr/settings/settings.h> | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace zephyr { | namespace zephyr { | ||||||
|  |  | ||||||
| static const char *const TAG = "esp32.preferences"; | static const char *const TAG = "zephyr.preferences"; | ||||||
|  |  | ||||||
| struct NVSData { | #define ESPHOME_SETTINGS_KEY "esphome" | ||||||
|   std::string key; |  | ||||||
|   std::vector<uint8_t> data; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static std::vector<NVSData> s_pending_save;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) |  | ||||||
|  |  | ||||||
| class ZephyrPreferenceBackend : public ESPPreferenceBackend { | class ZephyrPreferenceBackend : public ESPPreferenceBackend { | ||||||
|  public: |  public: | ||||||
|   std::string key; |   ZephyrPreferenceBackend(uint32_t type) { this->type_ = type; } | ||||||
|   // uint32_t nvs_handle; |   ZephyrPreferenceBackend(uint32_t type, std::vector<uint8_t> &&data) : data(std::move(data)) { this->type_ = type; } | ||||||
|  |  | ||||||
|   bool save(const uint8_t *data, size_t len) override { |   bool save(const uint8_t *data, size_t len) override { | ||||||
|     // try find in pending saves and update that |     this->data.resize(len); | ||||||
|     for (auto &obj : s_pending_save) { |     std::memcpy(this->data.data(), data, len); | ||||||
|       if (obj.key == key) { |     ESP_LOGVV(TAG, "save key: %u, len: %d", type_, len); | ||||||
|         obj.data.assign(data, data + len); |  | ||||||
|         return true; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     NVSData save{}; |  | ||||||
|     save.key = key; |  | ||||||
|     save.data.assign(data, data + len); |  | ||||||
|     s_pending_save.emplace_back(save); |  | ||||||
|     ESP_LOGVV(TAG, "s_pending_save: key: %s, len: %d", key.c_str(), len); |  | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool load(uint8_t *data, size_t len) override { |   bool load(uint8_t *data, size_t len) override { | ||||||
|     // try find in pending saves and load from that |     if (len != this->data.size()) { | ||||||
|     for (auto &obj : s_pending_save) { |       ESP_LOGE(TAG, "size of setting key %s changed, from: %u, to: %u", get_key().c_str(), this->data.size(), len); | ||||||
|       if (obj.key == key) { |       return false; | ||||||
|         if (obj.data.size() != len) { |  | ||||||
|           // size mismatch |  | ||||||
|           return false; |  | ||||||
|         } |  | ||||||
|         memcpy(data, obj.data.data(), len); |  | ||||||
|         return true; |  | ||||||
|       } |  | ||||||
|     } |     } | ||||||
|  |     std::memcpy(data, this->data.data(), len); | ||||||
|   // TODO |     ESP_LOGVV(TAG, "load key: %u, len: %d", type_, len); | ||||||
|  |  | ||||||
|   //   size_t actual_len; |  | ||||||
|   //   esp_err_t err = nvs_get_blob(nvs_handle, key.c_str(), nullptr, &actual_len); |  | ||||||
|   //   if (err != 0) { |  | ||||||
|   //     ESP_LOGV(TAG, "nvs_get_blob('%s'): %s - the key might not be set yet", key.c_str(), esp_err_to_name(err)); |  | ||||||
|   //     return false; |  | ||||||
|   //   } |  | ||||||
|   //   if (actual_len != len) { |  | ||||||
|   //     ESP_LOGVV(TAG, "NVS length does not match (%u!=%u)", actual_len, len); |  | ||||||
|   //     return false; |  | ||||||
|   //   } |  | ||||||
|   //   err = nvs_get_blob(nvs_handle, key.c_str(), data, &len); |  | ||||||
|   //   if (err != 0) { |  | ||||||
|   //     ESP_LOGV(TAG, "nvs_get_blob('%s') failed: %s", key.c_str(), esp_err_to_name(err)); |  | ||||||
|   //     return false; |  | ||||||
|   //   } else { |  | ||||||
|   //     ESP_LOGVV(TAG, "nvs_get_blob: key: %s, len: %d", key.c_str(), len); |  | ||||||
|   //   } |  | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   const uint32_t get_type() const { return type_; } | ||||||
|  |   const std::string get_key() const { return str_sprintf(ESPHOME_SETTINGS_KEY "/%" PRIx32, type_); } | ||||||
|  |  | ||||||
|  |   std::vector<uint8_t> data; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   uint32_t type_ = 0; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class ZephyrPreferences : public ESPPreferences { | class ZephyrPreferences : public ESPPreferences { | ||||||
|  public: |  public: | ||||||
|  |   void open() { | ||||||
|  |     int err = settings_subsys_init(); | ||||||
|  |     if (err) { | ||||||
|  |       ESP_LOGE(TAG, "Failed to initialize settings subsystem, err: %d", err); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static struct settings_handler settings_cb = { | ||||||
|  |         .name = ESPHOME_SETTINGS_KEY, | ||||||
|  |         .h_set = load_setting_, | ||||||
|  |         .h_export = export_settings_, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     err = settings_register(&settings_cb); | ||||||
|  |     if (err) { | ||||||
|  |       ESP_LOGE(TAG, "setting_register failed, err, %d", err); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     err = settings_load_subtree(ESPHOME_SETTINGS_KEY); | ||||||
|  |     if (err) { | ||||||
|  |       ESP_LOGE(TAG, "Cannot load settings, err: %d", err); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     ESP_LOGD(TAG, "Loaded %u settings.", backends_.size()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash) override { |   ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash) override { | ||||||
|     return make_preference(length, type); |     return make_preference(length, type); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   ESPPreferenceObject make_preference(size_t length, uint32_t type) override { |   ESPPreferenceObject make_preference(size_t length, uint32_t type) override { | ||||||
|     auto *pref = new ZephyrPreferenceBackend();  // NOLINT(cppcoreguidelines-owning-memory) |     for (auto backend : backends_) { | ||||||
|     // pref->nvs_handle = nvs_handle; |       if (backend->get_type() == type) { | ||||||
|  |         return ESPPreferenceObject(backend); | ||||||
|     uint32_t keyval = type; |       } | ||||||
|     pref->key = str_sprintf("%" PRIu32, keyval); |     } | ||||||
|  |     printf("type %u size %u\n", type, backends_.size()); | ||||||
|  |     auto *pref = new ZephyrPreferenceBackend(type); | ||||||
|  |     ESP_LOGD(TAG, "Add new setting %s.", pref->get_key().c_str()); | ||||||
|  |     backends_.push_back(pref); | ||||||
|     return ESPPreferenceObject(pref); |     return ESPPreferenceObject(pref); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool sync() override { |   bool sync() override { | ||||||
|     // TODO |     ESP_LOGD(TAG, "Save settings"); | ||||||
|  |     int err = settings_save(); | ||||||
|  |     if (err) { | ||||||
|  |       ESP_LOGE(TAG, "Cannot save settings, err: %d", err); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool reset() override { |   bool reset() override { | ||||||
|     // TODO |     ESP_LOGD(TAG, "Reset settings"); | ||||||
|  |     for (auto backend : backends_) { | ||||||
|  |       // save empty delete data | ||||||
|  |       backend->data.clear(); | ||||||
|  |     } | ||||||
|  |     sync(); | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   std::vector<ZephyrPreferenceBackend *> backends_; | ||||||
|  |  | ||||||
|  |   static int load_setting_(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg) { | ||||||
|  |     auto type = parse_hex<uint32_t>(name); | ||||||
|  |     if (!type.has_value()) { | ||||||
|  |       std::string full_name(ESPHOME_SETTINGS_KEY); | ||||||
|  |       full_name += "/"; | ||||||
|  |       full_name += name; | ||||||
|  |       // Delete unusable keys. Otherwise it will stay in flash forever. | ||||||
|  |       settings_delete(full_name.c_str()); | ||||||
|  |       return 1; | ||||||
|  |     } | ||||||
|  |     std::vector<uint8_t> data(len); | ||||||
|  |     int err = read_cb(cb_arg, data.data(), len); | ||||||
|  |  | ||||||
|  |     ESP_LOGD(TAG, "load setting, name: %s(%u), len %u, err %u", name, *type, len, err); | ||||||
|  |     auto *pref = new ZephyrPreferenceBackend(*type, std::move(data)); | ||||||
|  |     static_cast<ZephyrPreferences *>(global_preferences)->backends_.push_back(pref); | ||||||
|  |     return 0; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   static int export_settings_(int (*cb)(const char *name, const void *value, size_t val_len)) { | ||||||
|  |     for (auto backend : static_cast<ZephyrPreferences *>(global_preferences)->backends_) { | ||||||
|  |       auto name = backend->get_key(); | ||||||
|  |       int err = cb(name.c_str(), backend->data.data(), backend->data.size()); | ||||||
|  |       ESP_LOGD(TAG, "save in flash, name %s, len %u, err %d", name.c_str(), backend->data.size(), err); | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  |   } | ||||||
| }; | }; | ||||||
|  |  | ||||||
| void setup_preferences() { | void setup_preferences() { | ||||||
|   auto *prefs = new ZephyrPreferences();  // NOLINT(cppcoreguidelines-owning-memory) |   auto *prefs = new ZephyrPreferences(); | ||||||
|   global_preferences = prefs; |   global_preferences = prefs; | ||||||
|  |   prefs->open(); | ||||||
| } | } | ||||||
|  |  | ||||||
| }  // namespace zephyr | }  // namespace zephyr | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ from smpclient import SMPClient | |||||||
| from smpclient.mcuboot import IMAGE_TLV, ImageInfo, TLVNotFound | from smpclient.mcuboot import IMAGE_TLV, ImageInfo, TLVNotFound | ||||||
| from smpclient.requests.image_management import ImageStatesRead, ImageStatesWrite | from smpclient.requests.image_management import ImageStatesRead, ImageStatesWrite | ||||||
| from smpclient.requests.os_management import ResetWrite | from smpclient.requests.os_management import ResetWrite | ||||||
|  | from smp.exceptions import SMPBadStartDelimiter | ||||||
|  |  | ||||||
| from smpclient.generics import error, success | from smpclient.generics import error, success | ||||||
| from esphome.espota2 import ProgressBar | from esphome.espota2 import ProgressBar | ||||||
| @@ -102,9 +103,13 @@ async def smpmgr_upload(config, host, firmware): | |||||||
|  |  | ||||||
|     _LOGGER.info(f"Connected {host}...") |     _LOGGER.info(f"Connected {host}...") | ||||||
|  |  | ||||||
|     image_state = await asyncio.wait_for( |     try: | ||||||
|         smp_client.request(ImageStatesRead()), timeout=SMPClient.MEDIUM_TIMEOUT |         image_state = await asyncio.wait_for( | ||||||
|     ) |             smp_client.request(ImageStatesRead()), timeout=SMPClient.MEDIUM_TIMEOUT | ||||||
|  |         ) | ||||||
|  |     except SMPBadStartDelimiter as e: | ||||||
|  |         _LOGGER.error(f"mcumgr is not supported by device ({e})") | ||||||
|  |         return 1 | ||||||
|  |  | ||||||
|     already_uploaded = False |     already_uploaded = False | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user