92 auto *result =
new TaskResult();
93 auto *info = &result->info;
97 if (container ==
nullptr || container->status_code !=
HTTP_STATUS_OK) {
98 ESP_LOGE(TAG,
"Failed to fetch manifest from %s", this_update->
source_url_.c_str());
99 if (container !=
nullptr)
101 result->error_str = LOG_STR(
"Failed to fetch manifest");
107 uint8_t *data = allocator.
allocate(container->content_length);
108 if (data ==
nullptr) {
109 ESP_LOGE(TAG,
"Failed to allocate %zu bytes for manifest", container->content_length);
111 result->error_str = LOG_STR(
"Failed to allocate memory for manifest");
115 auto read_result =
http_read_fully(container.get(), data, container->content_length, MAX_READ_SIZE,
119 ESP_LOGE(TAG,
"Timeout reading manifest");
121 ESP_LOGE(TAG,
"Error reading manifest: %d", read_result.error_code);
123 allocator.
deallocate(data, container->content_length);
125 result->error_str = LOG_STR(
"Failed to read manifest");
128 size_t read_index = container->get_bytes_read();
129 size_t content_length = container->content_length;
137 if (!root[ESPHOME_F(
"name")].is<const char *>() || !root[ESPHOME_F(
"version")].is<const char *>() ||
138 !root[ESPHOME_F(
"builds")].is<JsonArray>()) {
139 ESP_LOGE(TAG,
"Manifest does not contain required fields");
142 info->title = root[ESPHOME_F(
"name")].as<std::string>();
143 info->latest_version = root[ESPHOME_F(
"version")].as<std::string>();
145 auto builds_array = root[ESPHOME_F(
"builds")].as<JsonArray>();
146 for (
auto build : builds_array) {
147 if (!build[ESPHOME_F(
"chipFamily")].is<const char *>()) {
148 ESP_LOGE(TAG,
"Manifest does not contain required fields");
151 if (build[ESPHOME_F(
"chipFamily")] == ESPHOME_VARIANT) {
152 if (!build[ESPHOME_F(
"ota")].is<JsonObject>()) {
153 ESP_LOGE(TAG,
"Manifest does not contain required fields");
156 JsonObject ota = build[ESPHOME_F(
"ota")].as<JsonObject>();
157 if (!ota[ESPHOME_F(
"path")].is<const char *>() || !ota[ESPHOME_F(
"md5")].is<const char *>()) {
158 ESP_LOGE(TAG,
"Manifest does not contain required fields");
161 info->firmware_url = ota[ESPHOME_F(
"path")].as<std::string>();
162 info->md5 = ota[ESPHOME_F(
"md5")].as<std::string>();
164 if (ota[ESPHOME_F(
"summary")].is<const char *>())
165 info->summary = ota[ESPHOME_F(
"summary")].as<std::string>();
166 if (ota[ESPHOME_F(
"release_url")].is<const char *>())
167 info->release_url = ota[ESPHOME_F(
"release_url")].as<std::string>();
178 ESP_LOGE(TAG,
"Failed to parse JSON from %s", this_update->
source_url_.c_str());
179 result->error_str = LOG_STR(
"Failed to parse manifest JSON");
184 if (!info->firmware_url.empty() && info->firmware_url.find(
"http") == std::string::npos) {
185 std::string path = info->firmware_url;
186 if (path[0] ==
'/') {
188 info->firmware_url = domain + path;
191 info->firmware_url = domain + path;
195#ifdef ESPHOME_PROJECT_VERSION
196 info->current_version = ESPHOME_PROJECT_VERSION;
198 info->current_version = ESPHOME_VERSION;
210 this_update->
defer([this_update, result]() {
214 if (result->error_str !=
nullptr) {
221 bool trigger_update_available =
false;
223 if (result->info.latest_version.empty() || result->info.latest_version == result->info.current_version) {
228 trigger_update_available =
true;
233 this_update->
state_ = new_state;
239 if (trigger_update_available) {
ESPDEPRECATED("Use const char* overload instead. Removed in 2026.7.0", "2026.1.0") void defer(const std voi defer)(const char *name, std::function< void()> &&f)
Defer a callback to the next loop() call.
ESPDEPRECATED("Use const char* or uint32_t overload instead. Removed in 2026.7.0", "2026.1.0") void set_interval(const std voi set_interval)(const char *name, uint32_t interval, std::function< void()> &&f)
Set an interval function with a unique name.
HttpReadResult http_read_fully(HttpContainer *container, uint8_t *buffer, size_t total_size, size_t chunk_size, uint32_t timeout_ms)
Read data from HTTP container into buffer with timeout handling Handles feed_wdt, yield,...