mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-04 00:51:49 +00:00 
			
		
		
		
	Compare commits
	
		
			4 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					628e47f670 | ||
| 
						 | 
					7666581c54 | ||
| 
						 | 
					03c36920ff | ||
| 
						 | 
					abdd6b232f | 
@@ -12,6 +12,8 @@
 | 
			
		||||
#include "esp_crt_bundle.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "esp_task_wdt.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace http_request {
 | 
			
		||||
 | 
			
		||||
@@ -117,11 +119,11 @@ std::shared_ptr<HttpContainer> HttpRequestIDF::start(std::string url, std::strin
 | 
			
		||||
    return nullptr;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  App.feed_wdt();
 | 
			
		||||
  container->feed_wdt();
 | 
			
		||||
  container->content_length = esp_http_client_fetch_headers(client);
 | 
			
		||||
  App.feed_wdt();
 | 
			
		||||
  container->feed_wdt();
 | 
			
		||||
  container->status_code = esp_http_client_get_status_code(client);
 | 
			
		||||
  App.feed_wdt();
 | 
			
		||||
  container->feed_wdt();
 | 
			
		||||
  if (is_success(container->status_code)) {
 | 
			
		||||
    container->duration_ms = millis() - start;
 | 
			
		||||
    return container;
 | 
			
		||||
@@ -151,11 +153,11 @@ std::shared_ptr<HttpContainer> HttpRequestIDF::start(std::string url, std::strin
 | 
			
		||||
        return nullptr;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      App.feed_wdt();
 | 
			
		||||
      container->feed_wdt();
 | 
			
		||||
      container->content_length = esp_http_client_fetch_headers(client);
 | 
			
		||||
      App.feed_wdt();
 | 
			
		||||
      container->feed_wdt();
 | 
			
		||||
      container->status_code = esp_http_client_get_status_code(client);
 | 
			
		||||
      App.feed_wdt();
 | 
			
		||||
      container->feed_wdt();
 | 
			
		||||
      if (is_success(container->status_code)) {
 | 
			
		||||
        container->duration_ms = millis() - start;
 | 
			
		||||
        return container;
 | 
			
		||||
@@ -185,8 +187,9 @@ int HttpContainerIDF::read(uint8_t *buf, size_t max_len) {
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  App.feed_wdt();
 | 
			
		||||
  this->feed_wdt();
 | 
			
		||||
  int read_len = esp_http_client_read(this->client_, (char *) buf, bufsize);
 | 
			
		||||
  this->feed_wdt();
 | 
			
		||||
  this->bytes_read_ += read_len;
 | 
			
		||||
 | 
			
		||||
  this->duration_ms += (millis() - start);
 | 
			
		||||
@@ -201,6 +204,13 @@ void HttpContainerIDF::end() {
 | 
			
		||||
  esp_http_client_cleanup(this->client_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HttpContainerIDF::feed_wdt() {
 | 
			
		||||
  // Tests to see if the executing task has a watchdog timer attached
 | 
			
		||||
  if (esp_task_wdt_status(nullptr) == ESP_OK) {
 | 
			
		||||
    App.feed_wdt();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace http_request
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,9 @@ class HttpContainerIDF : public HttpContainer {
 | 
			
		||||
  int read(uint8_t *buf, size_t max_len) override;
 | 
			
		||||
  void end() override;
 | 
			
		||||
 | 
			
		||||
  /// @brief Feeds the watchdog timer if the executing task has one attached
 | 
			
		||||
  void feed_wdt();
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  esp_http_client_handle_t client_;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,13 @@
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace http_request {
 | 
			
		||||
 | 
			
		||||
// The update function runs in a task only on ESP32s.
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
#define UPDATE_RETURN vTaskDelete(nullptr)  // Delete the current update task
 | 
			
		||||
#else
 | 
			
		||||
#define UPDATE_RETURN return
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "http_request.update";
 | 
			
		||||
 | 
			
		||||
static const size_t MAX_READ_SIZE = 256;
 | 
			
		||||
@@ -29,113 +36,131 @@ void HttpRequestUpdate::setup() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HttpRequestUpdate::update() {
 | 
			
		||||
  auto container = this->request_parent_->get(this->source_url_);
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
  xTaskCreate(HttpRequestUpdate::update_task, "update_task", 8192, (void *) this, 1, &this->update_task_handle_);
 | 
			
		||||
#else
 | 
			
		||||
  this->update_task(this);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HttpRequestUpdate::update_task(void *params) {
 | 
			
		||||
  HttpRequestUpdate *this_update = (HttpRequestUpdate *) params;
 | 
			
		||||
 | 
			
		||||
  auto container = this_update->request_parent_->get(this_update->source_url_);
 | 
			
		||||
 | 
			
		||||
  if (container == nullptr || container->status_code != HTTP_STATUS_OK) {
 | 
			
		||||
    std::string msg = str_sprintf("Failed to fetch manifest from %s", this->source_url_.c_str());
 | 
			
		||||
    this->status_set_error(msg.c_str());
 | 
			
		||||
    return;
 | 
			
		||||
    std::string msg = str_sprintf("Failed to fetch manifest from %s", this_update->source_url_.c_str());
 | 
			
		||||
    this_update->status_set_error(msg.c_str());
 | 
			
		||||
    UPDATE_RETURN;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
 | 
			
		||||
  uint8_t *data = allocator.allocate(container->content_length);
 | 
			
		||||
  if (data == nullptr) {
 | 
			
		||||
    std::string msg = str_sprintf("Failed to allocate %d bytes for manifest", container->content_length);
 | 
			
		||||
    this->status_set_error(msg.c_str());
 | 
			
		||||
    this_update->status_set_error(msg.c_str());
 | 
			
		||||
    container->end();
 | 
			
		||||
    return;
 | 
			
		||||
    UPDATE_RETURN;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  size_t read_index = 0;
 | 
			
		||||
  while (container->get_bytes_read() < container->content_length) {
 | 
			
		||||
    int read_bytes = container->read(data + read_index, MAX_READ_SIZE);
 | 
			
		||||
 | 
			
		||||
    App.feed_wdt();
 | 
			
		||||
    yield();
 | 
			
		||||
 | 
			
		||||
    read_index += read_bytes;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::string response((char *) data, read_index);
 | 
			
		||||
  allocator.deallocate(data, container->content_length);
 | 
			
		||||
  bool valid = false;
 | 
			
		||||
  {  // Ensures the response string falls out of scope and deallocates before the task ends
 | 
			
		||||
    std::string response((char *) data, read_index);
 | 
			
		||||
    allocator.deallocate(data, container->content_length);
 | 
			
		||||
 | 
			
		||||
  container->end();
 | 
			
		||||
    container->end();
 | 
			
		||||
    container.reset();  // Release ownership of the container's shared_ptr
 | 
			
		||||
 | 
			
		||||
  bool valid = json::parse_json(response, [this](JsonObject root) -> bool {
 | 
			
		||||
    if (!root.containsKey("name") || !root.containsKey("version") || !root.containsKey("builds")) {
 | 
			
		||||
      ESP_LOGE(TAG, "Manifest does not contain required fields");
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    this->update_info_.title = root["name"].as<std::string>();
 | 
			
		||||
    this->update_info_.latest_version = root["version"].as<std::string>();
 | 
			
		||||
 | 
			
		||||
    for (auto build : root["builds"].as<JsonArray>()) {
 | 
			
		||||
      if (!build.containsKey("chipFamily")) {
 | 
			
		||||
    valid = json::parse_json(response, [this_update](JsonObject root) -> bool {
 | 
			
		||||
      if (!root.containsKey("name") || !root.containsKey("version") || !root.containsKey("builds")) {
 | 
			
		||||
        ESP_LOGE(TAG, "Manifest does not contain required fields");
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
      if (build["chipFamily"] == ESPHOME_VARIANT) {
 | 
			
		||||
        if (!build.containsKey("ota")) {
 | 
			
		||||
      this_update->update_info_.title = root["name"].as<std::string>();
 | 
			
		||||
      this_update->update_info_.latest_version = root["version"].as<std::string>();
 | 
			
		||||
 | 
			
		||||
      for (auto build : root["builds"].as<JsonArray>()) {
 | 
			
		||||
        if (!build.containsKey("chipFamily")) {
 | 
			
		||||
          ESP_LOGE(TAG, "Manifest does not contain required fields");
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
        auto ota = build["ota"];
 | 
			
		||||
        if (!ota.containsKey("path") || !ota.containsKey("md5")) {
 | 
			
		||||
          ESP_LOGE(TAG, "Manifest does not contain required fields");
 | 
			
		||||
          return false;
 | 
			
		||||
        if (build["chipFamily"] == ESPHOME_VARIANT) {
 | 
			
		||||
          if (!build.containsKey("ota")) {
 | 
			
		||||
            ESP_LOGE(TAG, "Manifest does not contain required fields");
 | 
			
		||||
            return false;
 | 
			
		||||
          }
 | 
			
		||||
          auto ota = build["ota"];
 | 
			
		||||
          if (!ota.containsKey("path") || !ota.containsKey("md5")) {
 | 
			
		||||
            ESP_LOGE(TAG, "Manifest does not contain required fields");
 | 
			
		||||
            return false;
 | 
			
		||||
          }
 | 
			
		||||
          this_update->update_info_.firmware_url = ota["path"].as<std::string>();
 | 
			
		||||
          this_update->update_info_.md5 = ota["md5"].as<std::string>();
 | 
			
		||||
 | 
			
		||||
          if (ota.containsKey("summary"))
 | 
			
		||||
            this_update->update_info_.summary = ota["summary"].as<std::string>();
 | 
			
		||||
          if (ota.containsKey("release_url"))
 | 
			
		||||
            this_update->update_info_.release_url = ota["release_url"].as<std::string>();
 | 
			
		||||
 | 
			
		||||
          return true;
 | 
			
		||||
        }
 | 
			
		||||
        this->update_info_.firmware_url = ota["path"].as<std::string>();
 | 
			
		||||
        this->update_info_.md5 = ota["md5"].as<std::string>();
 | 
			
		||||
 | 
			
		||||
        if (ota.containsKey("summary"))
 | 
			
		||||
          this->update_info_.summary = ota["summary"].as<std::string>();
 | 
			
		||||
        if (ota.containsKey("release_url"))
 | 
			
		||||
          this->update_info_.release_url = ota["release_url"].as<std::string>();
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
  });
 | 
			
		||||
      return false;
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!valid) {
 | 
			
		||||
    std::string msg = str_sprintf("Failed to parse JSON from %s", this->source_url_.c_str());
 | 
			
		||||
    this->status_set_error(msg.c_str());
 | 
			
		||||
    return;
 | 
			
		||||
    std::string msg = str_sprintf("Failed to parse JSON from %s", this_update->source_url_.c_str());
 | 
			
		||||
    this_update->status_set_error(msg.c_str());
 | 
			
		||||
    UPDATE_RETURN;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Merge source_url_ and this->update_info_.firmware_url
 | 
			
		||||
  if (this->update_info_.firmware_url.find("http") == std::string::npos) {
 | 
			
		||||
    std::string path = this->update_info_.firmware_url;
 | 
			
		||||
  // Merge source_url_ and this_update->update_info_.firmware_url
 | 
			
		||||
  if (this_update->update_info_.firmware_url.find("http") == std::string::npos) {
 | 
			
		||||
    std::string path = this_update->update_info_.firmware_url;
 | 
			
		||||
    if (path[0] == '/') {
 | 
			
		||||
      std::string domain = this->source_url_.substr(0, this->source_url_.find('/', 8));
 | 
			
		||||
      this->update_info_.firmware_url = domain + path;
 | 
			
		||||
      std::string domain = this_update->source_url_.substr(0, this_update->source_url_.find('/', 8));
 | 
			
		||||
      this_update->update_info_.firmware_url = domain + path;
 | 
			
		||||
    } else {
 | 
			
		||||
      std::string domain = this->source_url_.substr(0, this->source_url_.rfind('/') + 1);
 | 
			
		||||
      this->update_info_.firmware_url = domain + path;
 | 
			
		||||
      std::string domain = this_update->source_url_.substr(0, this_update->source_url_.rfind('/') + 1);
 | 
			
		||||
      this_update->update_info_.firmware_url = domain + path;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::string current_version;
 | 
			
		||||
  {  // Ensures the current version string falls out of scope and deallocates before the task ends
 | 
			
		||||
    std::string current_version;
 | 
			
		||||
#ifdef ESPHOME_PROJECT_VERSION
 | 
			
		||||
  current_version = ESPHOME_PROJECT_VERSION;
 | 
			
		||||
    current_version = ESPHOME_PROJECT_VERSION;
 | 
			
		||||
#else
 | 
			
		||||
  current_version = ESPHOME_VERSION;
 | 
			
		||||
    current_version = ESPHOME_VERSION;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  this->update_info_.current_version = current_version;
 | 
			
		||||
 | 
			
		||||
  if (this->update_info_.latest_version.empty() || this->update_info_.latest_version == update_info_.current_version) {
 | 
			
		||||
    this->state_ = update::UPDATE_STATE_NO_UPDATE;
 | 
			
		||||
  } else {
 | 
			
		||||
    this->state_ = update::UPDATE_STATE_AVAILABLE;
 | 
			
		||||
    this_update->update_info_.current_version = current_version;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->update_info_.has_progress = false;
 | 
			
		||||
  this->update_info_.progress = 0.0f;
 | 
			
		||||
  if (this_update->update_info_.latest_version.empty() ||
 | 
			
		||||
      this_update->update_info_.latest_version == this_update->update_info_.current_version) {
 | 
			
		||||
    this_update->state_ = update::UPDATE_STATE_NO_UPDATE;
 | 
			
		||||
  } else {
 | 
			
		||||
    this_update->state_ = update::UPDATE_STATE_AVAILABLE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->status_clear_error();
 | 
			
		||||
  this->publish_state();
 | 
			
		||||
  this_update->update_info_.has_progress = false;
 | 
			
		||||
  this_update->update_info_.progress = 0.0f;
 | 
			
		||||
 | 
			
		||||
  this_update->status_clear_error();
 | 
			
		||||
  this_update->publish_state();
 | 
			
		||||
 | 
			
		||||
  UPDATE_RETURN;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HttpRequestUpdate::perform(bool force) {
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,10 @@
 | 
			
		||||
#include "esphome/components/http_request/ota/ota_http_request.h"
 | 
			
		||||
#include "esphome/components/update/update_entity.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
#include <freertos/FreeRTOS.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace http_request {
 | 
			
		||||
 | 
			
		||||
@@ -29,6 +33,11 @@ class HttpRequestUpdate : public update::UpdateEntity, public PollingComponent {
 | 
			
		||||
  HttpRequestComponent *request_parent_;
 | 
			
		||||
  OtaHttpRequestComponent *ota_parent_;
 | 
			
		||||
  std::string source_url_;
 | 
			
		||||
 | 
			
		||||
  static void update_task(void *params);
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
  TaskHandle_t update_task_handle_{nullptr};
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace http_request
 | 
			
		||||
 
 | 
			
		||||
@@ -147,7 +147,7 @@ class LibreTinyPreferences : public ESPPreferences {
 | 
			
		||||
      ESP_LOGV(TAG, "fdb_kv_get_obj('%s'): nullptr - the key might not be set yet", to_save.key.c_str());
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    stored_data.data.reserve(kv.value_len);
 | 
			
		||||
    stored_data.data.resize(kv.value_len);
 | 
			
		||||
    fdb_blob_make(&blob, stored_data.data.data(), kv.value_len);
 | 
			
		||||
    size_t actual_len = fdb_kv_get_blob(db, to_save.key.c_str(), &blob);
 | 
			
		||||
    if (actual_len != kv.value_len) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
"""Constants used by esphome."""
 | 
			
		||||
 | 
			
		||||
__version__ = "2024.12.2"
 | 
			
		||||
__version__ = "2024.12.3"
 | 
			
		||||
 | 
			
		||||
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
 | 
			
		||||
VALID_SUBSTITUTIONS_CHARACTERS = (
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user