ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
ota_backend.h
Go to the documentation of this file.
1#pragma once
2
6
7#include <concepts>
8#include <cstddef>
9#include <cstdint>
10#include <type_traits>
11
12#ifdef USE_OTA_STATE_LISTENER
13#include <vector>
14#endif
15
16namespace esphome::ota {
17
22
32
56};
57
70bool version_is_older(const char *candidate, const char *reference);
71
72// 64 KiB flash block; the erase granularity the ESP-IDF backend erases ahead with.
73static constexpr size_t OTA_BLOCK_ERASE_SIZE = 64 * 1024;
74
80constexpr size_t next_erase_end(size_t write_end, size_t partition_size) {
81 const size_t rounded = (write_end + OTA_BLOCK_ERASE_SIZE - 1) & ~(OTA_BLOCK_ERASE_SIZE - 1);
82 return rounded < partition_size ? rounded : partition_size;
83}
84
92
98
102 public:
103 UpdateLock() = default;
104 ~UpdateLock() { this->release(); }
105 UpdateLock(const UpdateLock &) = delete;
106 UpdateLock &operator=(const UpdateLock &) = delete;
107
109 bool try_acquire();
110 void release();
111 bool is_held() const { return this->held_; }
113 void hold_until_reboot() { this->held_ = false; }
114
115 protected:
117 static void force_release();
118
119 bool held_{false};
120};
121
122// The OTA backend method surface. Exactly one backend exists per build,
123// selected in ota_backend_factory.h where this concept is asserted on
124// make_ota_backend()'s return type. Semantics beyond the signatures:
125// - begin: prepare for an image of the given size; 0 means the size is unknown
126// (web_server upload) and the backend allows the largest image it can take.
127// ota_type defaults to an app update, so both call forms must be accepted.
128// - set_update_md5: expected digest of the incoming image, hex string.
129// - write: consume the next chunk; end: finalize and mark bootable.
130// - abort: safe to call in any state, including after end().
131// - supports_compression: constexpr, whether a gzip image is stored as is and
132// inflated at reboot.
133template<typename T>
134concept OTABackendContract = requires(T backend, size_t image_size, uint8_t *data, size_t len, const char *md5) {
135 { backend.begin(image_size, OTA_TYPE_UPDATE_APP) } -> std::same_as<OTAResponseTypes>;
136 { backend.begin(image_size) } -> std::same_as<OTAResponseTypes>;
137 backend.set_update_md5(md5);
138 { backend.write(data, len) } -> std::same_as<OTAResponseTypes>;
139 { backend.end() } -> std::same_as<OTAResponseTypes>;
140 backend.abort();
141 { T::supports_compression() } -> std::same_as<bool>;
142 // The value must be a constant expression
143 typename std::bool_constant<T::supports_compression()>;
144};
145
152 public:
153 virtual ~OTAStateListener() = default;
154 virtual void on_ota_state(OTAState state, float progress, uint8_t error) = 0;
155};
156
157class OTAComponent : public Component {
158#ifdef USE_OTA_STATE_LISTENER
159 public:
160 void add_state_listener(OTAStateListener *listener) { this->state_listeners_.push_back(listener); }
161
162 protected:
163 void notify_state_(OTAState state, float progress, uint8_t error);
164
170 void notify_state_deferred_(OTAState state, float progress, uint8_t error);
171
172 std::vector<OTAStateListener *> state_listeners_;
173#endif
174};
175
176#ifdef USE_OTA_STATE_LISTENER
177
183 public:
184 virtual ~OTAGlobalStateListener() = default;
185 virtual void on_ota_global_state(OTAState state, float progress, uint8_t error, OTAComponent *component) = 0;
186};
187
194 public:
195 void add_global_state_listener(OTAGlobalStateListener *listener) { this->global_listeners_.push_back(listener); }
196
197 void notify_ota_state(OTAState state, float progress, uint8_t error, OTAComponent *component) {
198 for (auto *listener : this->global_listeners_) {
199 listener->on_ota_global_state(state, progress, error, component);
200 }
201 }
202
203 protected:
204 std::vector<OTAGlobalStateListener *> global_listeners_;
205};
206
208
209// OTA implementations should use:
210// - notify_state_() when already in main loop (e.g., esphome OTA)
211// - notify_state_deferred_() when in separate task (e.g., web_server OTA)
212// This ensures proper listener execution in all contexts.
213#endif
214} // namespace esphome::ota
void notify_state_deferred_(OTAState state, float progress, uint8_t error)
Notify state with deferral to main loop (for thread safety).
void add_state_listener(OTAStateListener *listener)
std::vector< OTAStateListener * > state_listeners_
void notify_state_(OTAState state, float progress, uint8_t error)
Global callback that aggregates OTA state from all OTA components.
std::vector< OTAGlobalStateListener * > global_listeners_
void notify_ota_state(OTAState state, float progress, uint8_t error, OTAComponent *component)
void add_global_state_listener(OTAGlobalStateListener *listener)
Listener interface for global OTA state changes (includes OTA component pointer).
virtual void on_ota_global_state(OTAState state, float progress, uint8_t error, OTAComponent *component)=0
virtual ~OTAGlobalStateListener()=default
Listener interface for OTA state changes.
virtual ~OTAStateListener()=default
virtual void on_ota_state(OTAState state, float progress, uint8_t error)=0
Serializes flash updates.
void hold_until_reboot()
Keep the lock set until reboot; is_held() becomes false.
UpdateLock(const UpdateLock &)=delete
static void force_release()
Tests only.
UpdateLock & operator=(const UpdateLock &)=delete
bool try_acquire()
Returns false if held elsewhere.
const Component * component
Definition component.cpp:34
bool state
Definition fan.h:2
@ OTA_TYPE_UPDATE_BOOTLOADER
Definition ota_backend.h:96
@ OTA_TYPE_UPDATE_PARTITION_TABLE
Definition ota_backend.h:95
bool version_is_older(const char *candidate, const char *reference)
Compare two dotted-numeric version strings (such as "1.2.3").
OTAGlobalCallback * get_global_ota_callback()
constexpr size_t next_erase_end(size_t write_end, size_t partition_size)
Target erased watermark for lazy block erase-ahead.
Definition ota_backend.h:80
@ OTA_RESPONSE_ERROR_MD5_MISMATCH
Definition ota_backend.h:44
@ OTA_RESPONSE_ERROR_VERSION_DOWNGRADE
Definition ota_backend.h:52
@ OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE
Definition ota_backend.h:42
@ OTA_RESPONSE_UPDATE_PREPARE_OK
Definition ota_backend.h:25
@ OTA_RESPONSE_ERROR_ENCRYPTION_REQUIRED
Definition ota_backend.h:53
@ OTA_RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG
Definition ota_backend.h:40
@ OTA_RESPONSE_SUPPORTS_COMPRESSION
Definition ota_backend.h:29
@ OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG
Definition ota_backend.h:39
@ OTA_RESPONSE_BIN_MD5_OK
Definition ota_backend.h:26
@ OTA_RESPONSE_UPDATE_END_OK
Definition ota_backend.h:28
@ OTA_RESPONSE_RECEIVE_OK
Definition ota_backend.h:27
@ OTA_RESPONSE_ERROR_BOOTLOADER_UPDATE
Definition ota_backend.h:51
@ OTA_RESPONSE_CHUNK_OK
Definition ota_backend.h:30
@ OTA_RESPONSE_ERROR_WRITING_FLASH
Definition ota_backend.h:36
@ OTA_RESPONSE_FEATURE_FLAGS
Definition ota_backend.h:31
@ OTA_RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE
Definition ota_backend.h:41
@ OTA_RESPONSE_ERROR_PARTITION_TABLE_UPDATE
Definition ota_backend.h:49
@ OTA_RESPONSE_ERROR_UNSUPPORTED_OTA_TYPE
Definition ota_backend.h:47
@ OTA_RESPONSE_ERROR_UPDATE_END
Definition ota_backend.h:37
@ OTA_RESPONSE_ERROR_SIGNATURE_INVALID
Definition ota_backend.h:46
@ OTA_RESPONSE_ERROR_UPDATE_BUSY
Definition ota_backend.h:54
@ OTA_RESPONSE_ERROR_AUTH_INVALID
Definition ota_backend.h:35
@ OTA_RESPONSE_ERROR_RP2040_NOT_ENOUGH_SPACE
Definition ota_backend.h:45
@ OTA_RESPONSE_ERROR_UNKNOWN
Definition ota_backend.h:55
@ OTA_RESPONSE_REQUEST_SHA256_AUTH
Definition ota_backend.h:21
@ OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION
Definition ota_backend.h:43
@ OTA_RESPONSE_ERROR_MAGIC
Definition ota_backend.h:33
@ OTA_RESPONSE_ERROR_UPDATE_PREPARE
Definition ota_backend.h:34
@ OTA_RESPONSE_HEADER_OK
Definition ota_backend.h:23
@ OTA_RESPONSE_REQUEST_AUTH
Definition ota_backend.h:20
@ OTA_RESPONSE_ERROR_INVALID_BOOTSTRAPPING
Definition ota_backend.h:38
@ OTA_RESPONSE_ERROR_PARTITION_TABLE_VERIFY
Definition ota_backend.h:48
@ OTA_RESPONSE_ERROR_BOOTLOADER_VERIFY
Definition ota_backend.h:50
const void size_t len
Definition hal.h:68