10namespace http_request {
15#define UPDATE_RETURN \
17 vTaskDelete(nullptr); \
18 __builtin_unreachable(); \
21#define UPDATE_RETURN return
24static const char *
const TAG =
"http_request.update";
26static const size_t MAX_READ_SIZE = 256;
28static constexpr uint32_t INITIAL_CHECK_INTERVAL_MS = 10000;
29static constexpr uint8_t INITIAL_CHECK_MAX_ATTEMPTS = 6;
39 this->
set_interval(INITIAL_CHECK_INTERVAL_ID, INITIAL_CHECK_INTERVAL_MS, [
this]() {
66 ESP_LOGD(TAG,
"Network not connected, skipping update check");
82 if (container ==
nullptr || container->status_code !=
HTTP_STATUS_OK) {
83 ESP_LOGE(TAG,
"Failed to fetch manifest from %s", this_update->
source_url_.c_str());
85 this_update->
defer([this_update]() { this_update->
status_set_error(LOG_STR(
"Failed to fetch manifest")); });
90 uint8_t *data = allocator.
allocate(container->content_length);
91 if (data ==
nullptr) {
92 ESP_LOGE(TAG,
"Failed to allocate %zu bytes for manifest", container->content_length);
95 [this_update]() { this_update->
status_set_error(LOG_STR(
"Failed to allocate memory for manifest")); });
100 auto read_result =
http_read_fully(container.get(), data, container->content_length, MAX_READ_SIZE,
104 ESP_LOGE(TAG,
"Timeout reading manifest");
106 ESP_LOGE(TAG,
"Error reading manifest: %d", read_result.error_code);
109 this_update->
defer([this_update]() { this_update->
status_set_error(LOG_STR(
"Failed to read manifest")); });
110 allocator.
deallocate(data, container->content_length);
114 size_t read_index = container->get_bytes_read();
115 size_t content_length = container->content_length;
122 valid =
json::parse_json(data, read_index, [this_update](JsonObject root) ->
bool {
123 if (!root[ESPHOME_F(
"name")].is<const char *>() || !root[ESPHOME_F(
"version")].is<const char *>() ||
124 !root[ESPHOME_F(
"builds")].is<JsonArray>()) {
125 ESP_LOGE(TAG,
"Manifest does not contain required fields");
131 for (
auto build : root[ESPHOME_F(
"builds")].as<JsonArray>()) {
132 if (!build[ESPHOME_F(
"chipFamily")].is<const char *>()) {
133 ESP_LOGE(TAG,
"Manifest does not contain required fields");
136 if (build[ESPHOME_F(
"chipFamily")] == ESPHOME_VARIANT) {
137 if (!build[ESPHOME_F(
"ota")].is<JsonObject>()) {
138 ESP_LOGE(TAG,
"Manifest does not contain required fields");
141 JsonObject ota = build[ESPHOME_F(
"ota")].as<JsonObject>();
142 if (!ota[ESPHOME_F(
"path")].is<const char *>() || !ota[ESPHOME_F(
"md5")].is<const char *>()) {
143 ESP_LOGE(TAG,
"Manifest does not contain required fields");
147 this_update->
update_info_.
md5 = ota[ESPHOME_F(
"md5")].as<std::string>();
149 if (ota[ESPHOME_F(
"summary")].is<const char *>())
151 if (ota[ESPHOME_F(
"release_url")].is<const char *>())
163 ESP_LOGE(TAG,
"Failed to parse JSON from %s", this_update->
source_url_.c_str());
165 this_update->
defer([this_update]() { this_update->
status_set_error(LOG_STR(
"Failed to parse manifest JSON")); });
172 if (path[0] ==
'/') {
181#ifdef ESPHOME_PROJECT_VERSION
187 bool trigger_update_available =
false;
194 trigger_update_available =
true;
204 this_update->
defer([this_update, trigger_update_available]() {
211 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.
void status_clear_error()
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.
ESPDEPRECATED("Use const char* or uint32_t overload instead. Removed in 2026.7.0", "2026.1.0") bool cancel_interval(const std boo cancel_interval)(const char *name)
Cancel an interval function.
virtual uint32_t get_update_interval() const
Get the update interval in ms of this sensor.
An STL allocator that uses SPI or internal RAM.
void deallocate(T *p, size_t n)
uint32_t get_timeout() const
std::shared_ptr< HttpContainer > get(const std::string &url)
TaskHandle_t update_task_handle_
HttpRequestComponent * request_parent_
void on_ota_state(ota::OTAState state, float progress, uint8_t error) override
uint8_t initial_check_remaining_
OtaHttpRequestComponent * ota_parent_
static void update_task(void *params)
void set_url(const std::string &url)
void set_md5(const std::string &md5)
void add_state_listener(OTAStateListener *listener)
const UpdateState & state
Trigger< const UpdateInfo & > * get_update_available_trigger()
const UpdateInfo & update_info
constexpr uint32_t INITIAL_CHECK_INTERVAL_ID
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,...
@ TIMEOUT
Timeout waiting for data.
@ OK
Read completed successfully.
bool parse_json(const std::string &data, const json_parse_t &f)
Parse a JSON string and run the provided json parse function if it's valid.
bool is_connected()
Return whether the node is connected to the network (through wifi, eth, ...)
@ UPDATE_STATE_INSTALLING
Providing packet encoding functions for exchanging data with a remote host.
constexpr uint32_t SCHEDULER_DONT_RUN
std::string current_version
std::string latest_version