mirror of
https://github.com/esphome/esphome.git
synced 2026-02-04 03:19:41 -07:00
Compare commits
3 Commits
dev
...
scanf_bloa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f42f222e82 | ||
|
|
e68b302bba | ||
|
|
3e11a9d8a5 |
@@ -203,11 +203,10 @@ class ESP32Preferences : public ESPPreferences {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static ESP32Preferences s_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
|
||||||
|
|
||||||
void setup_preferences() {
|
void setup_preferences() {
|
||||||
s_preferences.open();
|
auto *prefs = new ESP32Preferences(); // NOLINT(cppcoreguidelines-owning-memory)
|
||||||
global_preferences = &s_preferences;
|
prefs->open();
|
||||||
|
global_preferences = prefs;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace esp32
|
} // namespace esp32
|
||||||
|
|||||||
@@ -211,14 +211,11 @@ bool Esp32HostedUpdate::fetch_manifest_() {
|
|||||||
int read_or_error = container->read(buf, sizeof(buf));
|
int read_or_error = container->read(buf, sizeof(buf));
|
||||||
App.feed_wdt();
|
App.feed_wdt();
|
||||||
yield();
|
yield();
|
||||||
auto result =
|
auto result = http_request::http_read_loop_result(read_or_error, last_data_time, read_timeout);
|
||||||
http_request::http_read_loop_result(read_or_error, last_data_time, read_timeout, container->is_read_complete());
|
|
||||||
if (result == http_request::HttpReadLoopResult::RETRY)
|
if (result == http_request::HttpReadLoopResult::RETRY)
|
||||||
continue;
|
continue;
|
||||||
// Note: COMPLETE is currently unreachable since the loop condition checks bytes_read < content_length,
|
|
||||||
// but this is defensive code in case chunked transfer encoding support is added in the future.
|
|
||||||
if (result != http_request::HttpReadLoopResult::DATA)
|
if (result != http_request::HttpReadLoopResult::DATA)
|
||||||
break; // COMPLETE, ERROR, or TIMEOUT
|
break; // ERROR or TIMEOUT
|
||||||
json_str.append(reinterpret_cast<char *>(buf), read_or_error);
|
json_str.append(reinterpret_cast<char *>(buf), read_or_error);
|
||||||
}
|
}
|
||||||
container->end();
|
container->end();
|
||||||
@@ -339,14 +336,9 @@ bool Esp32HostedUpdate::stream_firmware_to_coprocessor_() {
|
|||||||
App.feed_wdt();
|
App.feed_wdt();
|
||||||
yield();
|
yield();
|
||||||
|
|
||||||
auto result =
|
auto result = http_request::http_read_loop_result(read_or_error, last_data_time, read_timeout);
|
||||||
http_request::http_read_loop_result(read_or_error, last_data_time, read_timeout, container->is_read_complete());
|
|
||||||
if (result == http_request::HttpReadLoopResult::RETRY)
|
if (result == http_request::HttpReadLoopResult::RETRY)
|
||||||
continue;
|
continue;
|
||||||
// Note: COMPLETE is currently unreachable since the loop condition checks bytes_read < content_length,
|
|
||||||
// but this is defensive code in case chunked transfer encoding support is added in the future.
|
|
||||||
if (result == http_request::HttpReadLoopResult::COMPLETE)
|
|
||||||
break;
|
|
||||||
if (result != http_request::HttpReadLoopResult::DATA) {
|
if (result != http_request::HttpReadLoopResult::DATA) {
|
||||||
if (result == http_request::HttpReadLoopResult::TIMEOUT) {
|
if (result == http_request::HttpReadLoopResult::TIMEOUT) {
|
||||||
ESP_LOGE(TAG, "Timeout reading firmware data");
|
ESP_LOGE(TAG, "Timeout reading firmware data");
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ namespace esphome::esp8266 {
|
|||||||
|
|
||||||
static const char *const TAG = "esp8266.preferences";
|
static const char *const TAG = "esp8266.preferences";
|
||||||
|
|
||||||
|
static uint32_t *s_flash_storage = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
static bool s_prevent_write = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
static bool s_flash_dirty = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
|
||||||
static constexpr uint32_t ESP_RTC_USER_MEM_START = 0x60001200;
|
static constexpr uint32_t ESP_RTC_USER_MEM_START = 0x60001200;
|
||||||
static constexpr uint32_t ESP_RTC_USER_MEM_SIZE_WORDS = 128;
|
static constexpr uint32_t ESP_RTC_USER_MEM_SIZE_WORDS = 128;
|
||||||
static constexpr uint32_t ESP_RTC_USER_MEM_SIZE_BYTES = ESP_RTC_USER_MEM_SIZE_WORDS * 4;
|
static constexpr uint32_t ESP_RTC_USER_MEM_SIZE_BYTES = ESP_RTC_USER_MEM_SIZE_WORDS * 4;
|
||||||
@@ -39,11 +43,6 @@ static constexpr uint32_t ESP8266_FLASH_STORAGE_SIZE = 128;
|
|||||||
static constexpr uint32_t ESP8266_FLASH_STORAGE_SIZE = 64;
|
static constexpr uint32_t ESP8266_FLASH_STORAGE_SIZE = 64;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static uint32_t
|
|
||||||
s_flash_storage[ESP8266_FLASH_STORAGE_SIZE]; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
|
||||||
static bool s_prevent_write = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
|
||||||
static bool s_flash_dirty = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
|
||||||
|
|
||||||
static inline bool esp_rtc_user_mem_read(uint32_t index, uint32_t *dest) {
|
static inline bool esp_rtc_user_mem_read(uint32_t index, uint32_t *dest) {
|
||||||
if (index >= ESP_RTC_USER_MEM_SIZE_WORDS) {
|
if (index >= ESP_RTC_USER_MEM_SIZE_WORDS) {
|
||||||
return false;
|
return false;
|
||||||
@@ -181,6 +180,7 @@ class ESP8266Preferences : public ESPPreferences {
|
|||||||
uint32_t current_flash_offset = 0; // in words
|
uint32_t current_flash_offset = 0; // in words
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
|
s_flash_storage = new uint32_t[ESP8266_FLASH_STORAGE_SIZE]; // NOLINT
|
||||||
ESP_LOGVV(TAG, "Loading preferences from flash");
|
ESP_LOGVV(TAG, "Loading preferences from flash");
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -283,11 +283,10 @@ class ESP8266Preferences : public ESPPreferences {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static ESP8266Preferences s_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
|
||||||
|
|
||||||
void setup_preferences() {
|
void setup_preferences() {
|
||||||
s_preferences.setup();
|
auto *pref = new ESP8266Preferences(); // NOLINT(cppcoreguidelines-owning-memory)
|
||||||
global_preferences = &s_preferences;
|
pref->setup();
|
||||||
|
global_preferences = pref;
|
||||||
}
|
}
|
||||||
void preferences_prevent_write(bool prevent) { s_prevent_write = prevent; }
|
void preferences_prevent_write(bool prevent) { s_prevent_write = prevent; }
|
||||||
|
|
||||||
|
|||||||
@@ -66,11 +66,10 @@ ESPPreferenceObject HostPreferences::make_preference(size_t length, uint32_t typ
|
|||||||
return ESPPreferenceObject(backend);
|
return ESPPreferenceObject(backend);
|
||||||
};
|
};
|
||||||
|
|
||||||
static HostPreferences s_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
|
||||||
|
|
||||||
void setup_preferences() {
|
void setup_preferences() {
|
||||||
host_preferences = &s_preferences;
|
auto *pref = new HostPreferences(); // NOLINT(cppcoreguidelines-owning-memory)
|
||||||
global_preferences = &s_preferences;
|
host_preferences = pref;
|
||||||
|
global_preferences = pref;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HostPreferenceBackend::save(const uint8_t *data, size_t len) {
|
bool HostPreferenceBackend::save(const uint8_t *data, size_t len) {
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ struct Header {
|
|||||||
enum HttpStatus {
|
enum HttpStatus {
|
||||||
HTTP_STATUS_OK = 200,
|
HTTP_STATUS_OK = 200,
|
||||||
HTTP_STATUS_NO_CONTENT = 204,
|
HTTP_STATUS_NO_CONTENT = 204,
|
||||||
HTTP_STATUS_RESET_CONTENT = 205,
|
|
||||||
HTTP_STATUS_PARTIAL_CONTENT = 206,
|
HTTP_STATUS_PARTIAL_CONTENT = 206,
|
||||||
|
|
||||||
/* 3xx - Redirection */
|
/* 3xx - Redirection */
|
||||||
@@ -127,21 +126,19 @@ struct HttpReadResult {
|
|||||||
|
|
||||||
/// Result of processing a non-blocking read with timeout (for manual loops)
|
/// Result of processing a non-blocking read with timeout (for manual loops)
|
||||||
enum class HttpReadLoopResult : uint8_t {
|
enum class HttpReadLoopResult : uint8_t {
|
||||||
DATA, ///< Data was read, process it
|
DATA, ///< Data was read, process it
|
||||||
COMPLETE, ///< All content has been read, caller should exit loop
|
RETRY, ///< No data yet, already delayed, caller should continue loop
|
||||||
RETRY, ///< No data yet, already delayed, caller should continue loop
|
ERROR, ///< Read error, caller should exit loop
|
||||||
ERROR, ///< Read error, caller should exit loop
|
TIMEOUT, ///< Timeout waiting for data, caller should exit loop
|
||||||
TIMEOUT, ///< Timeout waiting for data, caller should exit loop
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Process a read result with timeout tracking and delay handling
|
/// Process a read result with timeout tracking and delay handling
|
||||||
/// @param bytes_read_or_error Return value from read() - positive for bytes read, negative for error
|
/// @param bytes_read_or_error Return value from read() - positive for bytes read, negative for error
|
||||||
/// @param last_data_time Time of last successful read, updated when data received
|
/// @param last_data_time Time of last successful read, updated when data received
|
||||||
/// @param timeout_ms Maximum time to wait for data
|
/// @param timeout_ms Maximum time to wait for data
|
||||||
/// @param is_read_complete Whether all expected content has been read (from HttpContainer::is_read_complete())
|
/// @return DATA if data received, RETRY if should continue loop, ERROR/TIMEOUT if should exit
|
||||||
/// @return How the caller should proceed - see HttpReadLoopResult enum
|
inline HttpReadLoopResult http_read_loop_result(int bytes_read_or_error, uint32_t &last_data_time,
|
||||||
inline HttpReadLoopResult http_read_loop_result(int bytes_read_or_error, uint32_t &last_data_time, uint32_t timeout_ms,
|
uint32_t timeout_ms) {
|
||||||
bool is_read_complete) {
|
|
||||||
if (bytes_read_or_error > 0) {
|
if (bytes_read_or_error > 0) {
|
||||||
last_data_time = millis();
|
last_data_time = millis();
|
||||||
return HttpReadLoopResult::DATA;
|
return HttpReadLoopResult::DATA;
|
||||||
@@ -149,10 +146,7 @@ inline HttpReadLoopResult http_read_loop_result(int bytes_read_or_error, uint32_
|
|||||||
if (bytes_read_or_error < 0) {
|
if (bytes_read_or_error < 0) {
|
||||||
return HttpReadLoopResult::ERROR;
|
return HttpReadLoopResult::ERROR;
|
||||||
}
|
}
|
||||||
// bytes_read_or_error == 0: either "no data yet" or "all content read"
|
// bytes_read_or_error == 0: no data available yet
|
||||||
if (is_read_complete) {
|
|
||||||
return HttpReadLoopResult::COMPLETE;
|
|
||||||
}
|
|
||||||
if (millis() - last_data_time >= timeout_ms) {
|
if (millis() - last_data_time >= timeout_ms) {
|
||||||
return HttpReadLoopResult::TIMEOUT;
|
return HttpReadLoopResult::TIMEOUT;
|
||||||
}
|
}
|
||||||
@@ -165,9 +159,9 @@ class HttpRequestComponent;
|
|||||||
class HttpContainer : public Parented<HttpRequestComponent> {
|
class HttpContainer : public Parented<HttpRequestComponent> {
|
||||||
public:
|
public:
|
||||||
virtual ~HttpContainer() = default;
|
virtual ~HttpContainer() = default;
|
||||||
size_t content_length{0};
|
size_t content_length;
|
||||||
int status_code{-1}; ///< -1 indicates no response received yet
|
int status_code;
|
||||||
uint32_t duration_ms{0};
|
uint32_t duration_ms;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read data from the HTTP response body.
|
* @brief Read data from the HTTP response body.
|
||||||
@@ -200,24 +194,9 @@ class HttpContainer : public Parented<HttpRequestComponent> {
|
|||||||
virtual void end() = 0;
|
virtual void end() = 0;
|
||||||
|
|
||||||
void set_secure(bool secure) { this->secure_ = secure; }
|
void set_secure(bool secure) { this->secure_ = secure; }
|
||||||
void set_chunked(bool chunked) { this->is_chunked_ = chunked; }
|
|
||||||
|
|
||||||
size_t get_bytes_read() const { return this->bytes_read_; }
|
size_t get_bytes_read() const { return this->bytes_read_; }
|
||||||
|
|
||||||
/// Check if all expected content has been read
|
|
||||||
/// For chunked responses, returns false (completion detected via read() returning error/EOF)
|
|
||||||
bool is_read_complete() const {
|
|
||||||
// Per RFC 9112, these responses have no body:
|
|
||||||
// - 1xx (Informational), 204 No Content, 205 Reset Content, 304 Not Modified
|
|
||||||
if ((this->status_code >= 100 && this->status_code < 200) || this->status_code == HTTP_STATUS_NO_CONTENT ||
|
|
||||||
this->status_code == HTTP_STATUS_RESET_CONTENT || this->status_code == HTTP_STATUS_NOT_MODIFIED) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// For non-chunked responses, complete when bytes_read >= content_length
|
|
||||||
// This handles both Content-Length: 0 and Content-Length: N cases
|
|
||||||
return !this->is_chunked_ && this->bytes_read_ >= this->content_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get response headers.
|
* @brief Get response headers.
|
||||||
*
|
*
|
||||||
@@ -230,7 +209,6 @@ class HttpContainer : public Parented<HttpRequestComponent> {
|
|||||||
protected:
|
protected:
|
||||||
size_t bytes_read_{0};
|
size_t bytes_read_{0};
|
||||||
bool secure_{false};
|
bool secure_{false};
|
||||||
bool is_chunked_{false}; ///< True if response uses chunked transfer encoding
|
|
||||||
std::map<std::string, std::list<std::string>> response_headers_{};
|
std::map<std::string, std::list<std::string>> response_headers_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -241,7 +219,7 @@ class HttpContainer : public Parented<HttpRequestComponent> {
|
|||||||
/// @param total_size Total bytes to read
|
/// @param total_size Total bytes to read
|
||||||
/// @param chunk_size Maximum bytes per read call
|
/// @param chunk_size Maximum bytes per read call
|
||||||
/// @param timeout_ms Read timeout in milliseconds
|
/// @param timeout_ms Read timeout in milliseconds
|
||||||
/// @return HttpReadResult with status and error_code on failure; use container->get_bytes_read() for total bytes read
|
/// @return HttpReadResult with status and error_code on failure
|
||||||
inline HttpReadResult http_read_fully(HttpContainer *container, uint8_t *buffer, size_t total_size, size_t chunk_size,
|
inline HttpReadResult http_read_fully(HttpContainer *container, uint8_t *buffer, size_t total_size, size_t chunk_size,
|
||||||
uint32_t timeout_ms) {
|
uint32_t timeout_ms) {
|
||||||
size_t read_index = 0;
|
size_t read_index = 0;
|
||||||
@@ -253,11 +231,9 @@ inline HttpReadResult http_read_fully(HttpContainer *container, uint8_t *buffer,
|
|||||||
App.feed_wdt();
|
App.feed_wdt();
|
||||||
yield();
|
yield();
|
||||||
|
|
||||||
auto result = http_read_loop_result(read_bytes_or_error, last_data_time, timeout_ms, container->is_read_complete());
|
auto result = http_read_loop_result(read_bytes_or_error, last_data_time, timeout_ms);
|
||||||
if (result == HttpReadLoopResult::RETRY)
|
if (result == HttpReadLoopResult::RETRY)
|
||||||
continue;
|
continue;
|
||||||
if (result == HttpReadLoopResult::COMPLETE)
|
|
||||||
break; // Server sent less data than requested, but transfer is complete
|
|
||||||
if (result == HttpReadLoopResult::ERROR)
|
if (result == HttpReadLoopResult::ERROR)
|
||||||
return {HttpReadStatus::ERROR, read_bytes_or_error};
|
return {HttpReadStatus::ERROR, read_bytes_or_error};
|
||||||
if (result == HttpReadLoopResult::TIMEOUT)
|
if (result == HttpReadLoopResult::TIMEOUT)
|
||||||
@@ -417,12 +393,11 @@ template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> {
|
|||||||
int read_or_error = container->read(buf + read_index, std::min<size_t>(max_length - read_index, 512));
|
int read_or_error = container->read(buf + read_index, std::min<size_t>(max_length - read_index, 512));
|
||||||
App.feed_wdt();
|
App.feed_wdt();
|
||||||
yield();
|
yield();
|
||||||
auto result =
|
auto result = http_read_loop_result(read_or_error, last_data_time, read_timeout);
|
||||||
http_read_loop_result(read_or_error, last_data_time, read_timeout, container->is_read_complete());
|
|
||||||
if (result == HttpReadLoopResult::RETRY)
|
if (result == HttpReadLoopResult::RETRY)
|
||||||
continue;
|
continue;
|
||||||
if (result != HttpReadLoopResult::DATA)
|
if (result != HttpReadLoopResult::DATA)
|
||||||
break; // COMPLETE, ERROR, or TIMEOUT
|
break; // ERROR or TIMEOUT
|
||||||
read_index += read_or_error;
|
read_index += read_or_error;
|
||||||
}
|
}
|
||||||
response_body.reserve(read_index);
|
response_body.reserve(read_index);
|
||||||
|
|||||||
@@ -135,23 +135,9 @@ std::shared_ptr<HttpContainer> HttpRequestArduino::perform(const std::string &ur
|
|||||||
// When cast to size_t, -1 becomes SIZE_MAX (4294967295 on 32-bit).
|
// When cast to size_t, -1 becomes SIZE_MAX (4294967295 on 32-bit).
|
||||||
// The read() method handles this: bytes_read_ can never reach SIZE_MAX, so the
|
// The read() method handles this: bytes_read_ can never reach SIZE_MAX, so the
|
||||||
// early return check (bytes_read_ >= content_length) will never trigger.
|
// early return check (bytes_read_ >= content_length) will never trigger.
|
||||||
//
|
|
||||||
// TODO: Chunked transfer encoding is NOT properly supported on Arduino.
|
|
||||||
// The implementation in #7884 was incomplete - it only works correctly on ESP-IDF where
|
|
||||||
// esp_http_client_read() decodes chunks internally. On Arduino, using getStreamPtr()
|
|
||||||
// returns raw TCP data with chunk framing (e.g., "12a\r\n{json}\r\n0\r\n\r\n") instead
|
|
||||||
// of decoded content. This wasn't noticed because requests would complete and payloads
|
|
||||||
// were only examined on IDF. The long transfer times were also masked by the misleading
|
|
||||||
// "HTTP on Arduino version >= 3.1 is **very** slow" warning above. This causes two issues:
|
|
||||||
// 1. Response body is corrupted - contains chunk size headers mixed with data
|
|
||||||
// 2. Cannot detect end of transfer - connection stays open (keep-alive), causing timeout
|
|
||||||
// The proper fix would be to use getString() for chunked responses, which decodes chunks
|
|
||||||
// internally, but this buffers the entire response in memory.
|
|
||||||
int content_length = container->client_.getSize();
|
int content_length = container->client_.getSize();
|
||||||
ESP_LOGD(TAG, "Content-Length: %d", content_length);
|
ESP_LOGD(TAG, "Content-Length: %d", content_length);
|
||||||
container->content_length = (size_t) content_length;
|
container->content_length = (size_t) content_length;
|
||||||
// -1 (SIZE_MAX when cast to size_t) means chunked transfer encoding
|
|
||||||
container->set_chunked(content_length == -1);
|
|
||||||
container->duration_ms = millis() - start;
|
container->duration_ms = millis() - start;
|
||||||
|
|
||||||
return container;
|
return container;
|
||||||
@@ -192,9 +178,9 @@ int HttpContainerArduino::read(uint8_t *buf, size_t max_len) {
|
|||||||
|
|
||||||
if (bufsize == 0) {
|
if (bufsize == 0) {
|
||||||
this->duration_ms += (millis() - start);
|
this->duration_ms += (millis() - start);
|
||||||
// Check if we've read all expected content (non-chunked only)
|
// Check if we've read all expected content (only valid when content_length is known and not SIZE_MAX)
|
||||||
// For chunked encoding (content_length == SIZE_MAX), is_read_complete() returns false
|
// For chunked encoding (content_length == SIZE_MAX), we can't use this check
|
||||||
if (this->is_read_complete()) {
|
if (this->content_length > 0 && this->bytes_read_ >= this->content_length) {
|
||||||
return 0; // All content read successfully
|
return 0; // All content read successfully
|
||||||
}
|
}
|
||||||
// No data available - check if connection is still open
|
// No data available - check if connection is still open
|
||||||
|
|||||||
@@ -160,7 +160,6 @@ std::shared_ptr<HttpContainer> HttpRequestIDF::perform(const std::string &url, c
|
|||||||
// esp_http_client_fetch_headers() returns 0 for chunked transfer encoding (no Content-Length header).
|
// esp_http_client_fetch_headers() returns 0 for chunked transfer encoding (no Content-Length header).
|
||||||
// The read() method handles content_length == 0 specially to support chunked responses.
|
// The read() method handles content_length == 0 specially to support chunked responses.
|
||||||
container->content_length = esp_http_client_fetch_headers(client);
|
container->content_length = esp_http_client_fetch_headers(client);
|
||||||
container->set_chunked(esp_http_client_is_chunked_response(client));
|
|
||||||
container->feed_wdt();
|
container->feed_wdt();
|
||||||
container->status_code = esp_http_client_get_status_code(client);
|
container->status_code = esp_http_client_get_status_code(client);
|
||||||
container->feed_wdt();
|
container->feed_wdt();
|
||||||
@@ -196,7 +195,6 @@ std::shared_ptr<HttpContainer> HttpRequestIDF::perform(const std::string &url, c
|
|||||||
|
|
||||||
container->feed_wdt();
|
container->feed_wdt();
|
||||||
container->content_length = esp_http_client_fetch_headers(client);
|
container->content_length = esp_http_client_fetch_headers(client);
|
||||||
container->set_chunked(esp_http_client_is_chunked_response(client));
|
|
||||||
container->feed_wdt();
|
container->feed_wdt();
|
||||||
container->status_code = esp_http_client_get_status_code(client);
|
container->status_code = esp_http_client_get_status_code(client);
|
||||||
container->feed_wdt();
|
container->feed_wdt();
|
||||||
@@ -241,9 +239,10 @@ int HttpContainerIDF::read(uint8_t *buf, size_t max_len) {
|
|||||||
const uint32_t start = millis();
|
const uint32_t start = millis();
|
||||||
watchdog::WatchdogManager wdm(this->parent_->get_watchdog_timeout());
|
watchdog::WatchdogManager wdm(this->parent_->get_watchdog_timeout());
|
||||||
|
|
||||||
// Check if we've already read all expected content (non-chunked only)
|
// Check if we've already read all expected content
|
||||||
// For chunked responses (content_length == 0), esp_http_client_read() handles EOF
|
// Skip this check when content_length is 0 (chunked transfer encoding or unknown length)
|
||||||
if (this->is_read_complete()) {
|
// For chunked responses, esp_http_client_read() will return 0 when all data is received
|
||||||
|
if (this->content_length > 0 && this->bytes_read_ >= this->content_length) {
|
||||||
return 0; // All content read successfully
|
return 0; // All content read successfully
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -130,13 +130,9 @@ uint8_t OtaHttpRequestComponent::do_ota_() {
|
|||||||
App.feed_wdt();
|
App.feed_wdt();
|
||||||
yield();
|
yield();
|
||||||
|
|
||||||
auto result = http_read_loop_result(bufsize_or_error, last_data_time, read_timeout, container->is_read_complete());
|
auto result = http_read_loop_result(bufsize_or_error, last_data_time, read_timeout);
|
||||||
if (result == HttpReadLoopResult::RETRY)
|
if (result == HttpReadLoopResult::RETRY)
|
||||||
continue;
|
continue;
|
||||||
// Note: COMPLETE is currently unreachable since the loop condition checks bytes_read < content_length,
|
|
||||||
// but this is defensive code in case chunked transfer encoding support is added for OTA in the future.
|
|
||||||
if (result == HttpReadLoopResult::COMPLETE)
|
|
||||||
break;
|
|
||||||
if (result != HttpReadLoopResult::DATA) {
|
if (result != HttpReadLoopResult::DATA) {
|
||||||
if (result == HttpReadLoopResult::TIMEOUT) {
|
if (result == HttpReadLoopResult::TIMEOUT) {
|
||||||
ESP_LOGE(TAG, "Timeout reading data");
|
ESP_LOGE(TAG, "Timeout reading data");
|
||||||
|
|||||||
@@ -189,11 +189,10 @@ class LibreTinyPreferences : public ESPPreferences {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static LibreTinyPreferences s_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
|
||||||
|
|
||||||
void setup_preferences() {
|
void setup_preferences() {
|
||||||
s_preferences.open();
|
auto *prefs = new LibreTinyPreferences(); // NOLINT(cppcoreguidelines-owning-memory)
|
||||||
global_preferences = &s_preferences;
|
prefs->open();
|
||||||
|
global_preferences = prefs;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace libretiny
|
} // namespace libretiny
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
#include "esphome/core/defines.h"
|
#include "esphome/core/defines.h"
|
||||||
#include "esphome/core/controller_registry.h"
|
#include "esphome/core/controller_registry.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include "esphome/core/progmem.h"
|
|
||||||
|
|
||||||
namespace esphome::lock {
|
namespace esphome::lock {
|
||||||
|
|
||||||
@@ -85,21 +84,21 @@ LockCall &LockCall::set_state(optional<LockState> state) {
|
|||||||
this->state_ = state;
|
this->state_ = state;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
LockCall &LockCall::set_state(const char *state) {
|
LockCall &LockCall::set_state(const std::string &state) {
|
||||||
if (ESPHOME_strcasecmp_P(state, ESPHOME_PSTR("LOCKED")) == 0) {
|
if (str_equals_case_insensitive(state, "LOCKED")) {
|
||||||
this->set_state(LOCK_STATE_LOCKED);
|
this->set_state(LOCK_STATE_LOCKED);
|
||||||
} else if (ESPHOME_strcasecmp_P(state, ESPHOME_PSTR("UNLOCKED")) == 0) {
|
} else if (str_equals_case_insensitive(state, "UNLOCKED")) {
|
||||||
this->set_state(LOCK_STATE_UNLOCKED);
|
this->set_state(LOCK_STATE_UNLOCKED);
|
||||||
} else if (ESPHOME_strcasecmp_P(state, ESPHOME_PSTR("JAMMED")) == 0) {
|
} else if (str_equals_case_insensitive(state, "JAMMED")) {
|
||||||
this->set_state(LOCK_STATE_JAMMED);
|
this->set_state(LOCK_STATE_JAMMED);
|
||||||
} else if (ESPHOME_strcasecmp_P(state, ESPHOME_PSTR("LOCKING")) == 0) {
|
} else if (str_equals_case_insensitive(state, "LOCKING")) {
|
||||||
this->set_state(LOCK_STATE_LOCKING);
|
this->set_state(LOCK_STATE_LOCKING);
|
||||||
} else if (ESPHOME_strcasecmp_P(state, ESPHOME_PSTR("UNLOCKING")) == 0) {
|
} else if (str_equals_case_insensitive(state, "UNLOCKING")) {
|
||||||
this->set_state(LOCK_STATE_UNLOCKING);
|
this->set_state(LOCK_STATE_UNLOCKING);
|
||||||
} else if (ESPHOME_strcasecmp_P(state, ESPHOME_PSTR("NONE")) == 0) {
|
} else if (str_equals_case_insensitive(state, "NONE")) {
|
||||||
this->set_state(LOCK_STATE_NONE);
|
this->set_state(LOCK_STATE_NONE);
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGW(TAG, "'%s' - Unrecognized state %s", this->parent_->get_name().c_str(), state);
|
ESP_LOGW(TAG, "'%s' - Unrecognized state %s", this->parent_->get_name().c_str(), state.c_str());
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,8 +83,7 @@ class LockCall {
|
|||||||
/// Set the state of the lock device.
|
/// Set the state of the lock device.
|
||||||
LockCall &set_state(optional<LockState> state);
|
LockCall &set_state(optional<LockState> state);
|
||||||
/// Set the state of the lock device based on a string.
|
/// Set the state of the lock device based on a string.
|
||||||
LockCall &set_state(const char *state);
|
LockCall &set_state(const std::string &state);
|
||||||
LockCall &set_state(const std::string &state) { return this->set_state(state.c_str()); }
|
|
||||||
|
|
||||||
void perform();
|
void perform();
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
#include "esphome/core/defines.h"
|
#include "esphome/core/defines.h"
|
||||||
#include "esphome/core/controller_registry.h"
|
#include "esphome/core/controller_registry.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include "esphome/core/progmem.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace media_player {
|
namespace media_player {
|
||||||
@@ -108,25 +107,25 @@ MediaPlayerCall &MediaPlayerCall::set_command(optional<MediaPlayerCommand> comma
|
|||||||
this->command_ = command;
|
this->command_ = command;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
MediaPlayerCall &MediaPlayerCall::set_command(const char *command) {
|
MediaPlayerCall &MediaPlayerCall::set_command(const std::string &command) {
|
||||||
if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("PLAY")) == 0) {
|
if (str_equals_case_insensitive(command, "PLAY")) {
|
||||||
this->set_command(MEDIA_PLAYER_COMMAND_PLAY);
|
this->set_command(MEDIA_PLAYER_COMMAND_PLAY);
|
||||||
} else if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("PAUSE")) == 0) {
|
} else if (str_equals_case_insensitive(command, "PAUSE")) {
|
||||||
this->set_command(MEDIA_PLAYER_COMMAND_PAUSE);
|
this->set_command(MEDIA_PLAYER_COMMAND_PAUSE);
|
||||||
} else if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("STOP")) == 0) {
|
} else if (str_equals_case_insensitive(command, "STOP")) {
|
||||||
this->set_command(MEDIA_PLAYER_COMMAND_STOP);
|
this->set_command(MEDIA_PLAYER_COMMAND_STOP);
|
||||||
} else if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("MUTE")) == 0) {
|
} else if (str_equals_case_insensitive(command, "MUTE")) {
|
||||||
this->set_command(MEDIA_PLAYER_COMMAND_MUTE);
|
this->set_command(MEDIA_PLAYER_COMMAND_MUTE);
|
||||||
} else if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("UNMUTE")) == 0) {
|
} else if (str_equals_case_insensitive(command, "UNMUTE")) {
|
||||||
this->set_command(MEDIA_PLAYER_COMMAND_UNMUTE);
|
this->set_command(MEDIA_PLAYER_COMMAND_UNMUTE);
|
||||||
} else if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("TOGGLE")) == 0) {
|
} else if (str_equals_case_insensitive(command, "TOGGLE")) {
|
||||||
this->set_command(MEDIA_PLAYER_COMMAND_TOGGLE);
|
this->set_command(MEDIA_PLAYER_COMMAND_TOGGLE);
|
||||||
} else if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("TURN_ON")) == 0) {
|
} else if (str_equals_case_insensitive(command, "TURN_ON")) {
|
||||||
this->set_command(MEDIA_PLAYER_COMMAND_TURN_ON);
|
this->set_command(MEDIA_PLAYER_COMMAND_TURN_ON);
|
||||||
} else if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("TURN_OFF")) == 0) {
|
} else if (str_equals_case_insensitive(command, "TURN_OFF")) {
|
||||||
this->set_command(MEDIA_PLAYER_COMMAND_TURN_OFF);
|
this->set_command(MEDIA_PLAYER_COMMAND_TURN_OFF);
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGW(TAG, "'%s' - Unrecognized command %s", this->parent_->get_name().c_str(), command);
|
ESP_LOGW(TAG, "'%s' - Unrecognized command %s", this->parent_->get_name().c_str(), command.c_str());
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,8 +114,7 @@ class MediaPlayerCall {
|
|||||||
|
|
||||||
MediaPlayerCall &set_command(MediaPlayerCommand command);
|
MediaPlayerCall &set_command(MediaPlayerCommand command);
|
||||||
MediaPlayerCall &set_command(optional<MediaPlayerCommand> command);
|
MediaPlayerCall &set_command(optional<MediaPlayerCommand> command);
|
||||||
MediaPlayerCall &set_command(const char *command);
|
MediaPlayerCall &set_command(const std::string &command);
|
||||||
MediaPlayerCall &set_command(const std::string &command) { return this->set_command(command.c_str()); }
|
|
||||||
|
|
||||||
MediaPlayerCall &set_media_url(const std::string &url);
|
MediaPlayerCall &set_media_url(const std::string &url);
|
||||||
|
|
||||||
|
|||||||
@@ -94,29 +94,3 @@ DriverChip(
|
|||||||
(0x29, 0x00),
|
(0x29, 0x00),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
DriverChip(
|
|
||||||
"WAVESHARE-ESP32-P4-WIFI6-TOUCH-LCD-7B",
|
|
||||||
height=600,
|
|
||||||
width=1024,
|
|
||||||
hsync_back_porch=160,
|
|
||||||
hsync_pulse_width=10,
|
|
||||||
hsync_front_porch=160,
|
|
||||||
vsync_back_porch=23,
|
|
||||||
vsync_pulse_width=1,
|
|
||||||
vsync_front_porch=12,
|
|
||||||
pclk_frequency="52MHz",
|
|
||||||
lane_bit_rate="900Mbps",
|
|
||||||
no_transform=True,
|
|
||||||
color_order="RGB",
|
|
||||||
initsequence=[
|
|
||||||
(0x80, 0x8B),
|
|
||||||
(0x81, 0x78),
|
|
||||||
(0x82, 0x84),
|
|
||||||
(0x83, 0x88),
|
|
||||||
(0x84, 0xA8),
|
|
||||||
(0x85, 0xE3),
|
|
||||||
(0x86, 0x88),
|
|
||||||
(0xB2, 0x10),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -4,10 +4,8 @@ from typing import Any
|
|||||||
from esphome import automation, pins
|
from esphome import automation, pins
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import sensor
|
from esphome.components import sensor
|
||||||
from esphome.components.esp32 import include_builtin_idf_component
|
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID, CONF_TRIGGER_ID, PLATFORM_ESP32, PLATFORM_ESP8266
|
from esphome.const import CONF_ID, CONF_TRIGGER_ID, PLATFORM_ESP32, PLATFORM_ESP8266
|
||||||
from esphome.core import CORE
|
|
||||||
|
|
||||||
from . import const, generate, schema, validate
|
from . import const, generate, schema, validate
|
||||||
|
|
||||||
@@ -85,12 +83,6 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
|
|
||||||
|
|
||||||
async def to_code(config: dict[str, Any]) -> None:
|
async def to_code(config: dict[str, Any]) -> None:
|
||||||
if CORE.is_esp32:
|
|
||||||
# Re-enable ESP-IDF's legacy driver component (excluded by default to save compile time)
|
|
||||||
# Provides driver/timer.h header for hardware timer API
|
|
||||||
# TODO: Remove this once opentherm migrates to GPTimer API (driver/gptimer.h)
|
|
||||||
include_builtin_idf_component("driver")
|
|
||||||
|
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,6 @@
|
|||||||
#include "opentherm.h"
|
#include "opentherm.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
// TODO: Migrate from legacy timer API (driver/timer.h) to GPTimer API (driver/gptimer.h)
|
|
||||||
// The legacy timer API is deprecated in ESP-IDF 5.x. See opentherm.h for details.
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
#include "driver/timer.h"
|
#include "driver/timer.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
|
|||||||
@@ -12,10 +12,6 @@
|
|||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
// TODO: Migrate from legacy timer API (driver/timer.h) to GPTimer API (driver/gptimer.h)
|
|
||||||
// The legacy timer API is deprecated in ESP-IDF 5.x. Migration would allow removing the
|
|
||||||
// "driver" IDF component dependency. See:
|
|
||||||
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/migration-guides/release-5.x/5.0/peripherals.html#id4
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
#include "driver/timer.h"
|
#include "driver/timer.h"
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -18,12 +18,11 @@ namespace rp2040 {
|
|||||||
|
|
||||||
static const char *const TAG = "rp2040.preferences";
|
static const char *const TAG = "rp2040.preferences";
|
||||||
|
|
||||||
static constexpr uint32_t RP2040_FLASH_STORAGE_SIZE = 512;
|
static bool s_prevent_write = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
static uint8_t *s_flash_storage = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
static bool s_flash_dirty = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
|
||||||
static bool s_prevent_write = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
static const uint32_t RP2040_FLASH_STORAGE_SIZE = 512;
|
||||||
static uint8_t
|
|
||||||
s_flash_storage[RP2040_FLASH_STORAGE_SIZE]; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
|
||||||
static bool s_flash_dirty = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
|
||||||
|
|
||||||
// Stack buffer size for preferences - covers virtually all real-world preferences without heap allocation
|
// Stack buffer size for preferences - covers virtually all real-world preferences without heap allocation
|
||||||
static constexpr size_t PREF_BUFFER_SIZE = 64;
|
static constexpr size_t PREF_BUFFER_SIZE = 64;
|
||||||
@@ -92,6 +91,7 @@ class RP2040Preferences : public ESPPreferences {
|
|||||||
|
|
||||||
RP2040Preferences() : eeprom_sector_(&_EEPROM_start) {}
|
RP2040Preferences() : eeprom_sector_(&_EEPROM_start) {}
|
||||||
void setup() {
|
void setup() {
|
||||||
|
s_flash_storage = new uint8_t[RP2040_FLASH_STORAGE_SIZE]; // NOLINT
|
||||||
ESP_LOGVV(TAG, "Loading preferences from flash");
|
ESP_LOGVV(TAG, "Loading preferences from flash");
|
||||||
memcpy(s_flash_storage, this->eeprom_sector_, RP2040_FLASH_STORAGE_SIZE);
|
memcpy(s_flash_storage, this->eeprom_sector_, RP2040_FLASH_STORAGE_SIZE);
|
||||||
}
|
}
|
||||||
@@ -149,11 +149,10 @@ class RP2040Preferences : public ESPPreferences {
|
|||||||
uint8_t *eeprom_sector_;
|
uint8_t *eeprom_sector_;
|
||||||
};
|
};
|
||||||
|
|
||||||
static RP2040Preferences s_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
|
||||||
|
|
||||||
void setup_preferences() {
|
void setup_preferences() {
|
||||||
s_preferences.setup();
|
auto *prefs = new RP2040Preferences(); // NOLINT(cppcoreguidelines-owning-memory)
|
||||||
global_preferences = &s_preferences;
|
prefs->setup();
|
||||||
|
global_preferences = prefs;
|
||||||
}
|
}
|
||||||
void preferences_prevent_write(bool prevent) { s_prevent_write = prevent; }
|
void preferences_prevent_write(bool prevent) { s_prevent_write = prevent; }
|
||||||
|
|
||||||
|
|||||||
@@ -157,14 +157,8 @@ def _read_audio_file_and_type(file_config):
|
|||||||
|
|
||||||
import puremagic
|
import puremagic
|
||||||
|
|
||||||
try:
|
file_type: str = puremagic.from_string(data)
|
||||||
file_type: str = puremagic.from_string(data)
|
file_type = file_type.removeprefix(".")
|
||||||
file_type = file_type.removeprefix(".")
|
|
||||||
except puremagic.PureError as e:
|
|
||||||
raise cv.Invalid(
|
|
||||||
f"Unable to determine audio file type of '{path}'. "
|
|
||||||
f"Try re-encoding the file into a supported format. Details: {e}"
|
|
||||||
)
|
|
||||||
|
|
||||||
media_file_type = audio.AUDIO_FILE_TYPE_ENUM["NONE"]
|
media_file_type = audio.AUDIO_FILE_TYPE_ENUM["NONE"]
|
||||||
if file_type in ("wav"):
|
if file_type in ("wav"):
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include "esphome/core/application.h"
|
#include "esphome/core/application.h"
|
||||||
#include "esphome/core/controller_registry.h"
|
#include "esphome/core/controller_registry.h"
|
||||||
#include "esphome/core/progmem.h"
|
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
@@ -23,23 +22,23 @@ WaterHeaterCall &WaterHeaterCall::set_mode(WaterHeaterMode mode) {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
WaterHeaterCall &WaterHeaterCall::set_mode(const char *mode) {
|
WaterHeaterCall &WaterHeaterCall::set_mode(const std::string &mode) {
|
||||||
if (ESPHOME_strcasecmp_P(mode, ESPHOME_PSTR("OFF")) == 0) {
|
if (str_equals_case_insensitive(mode, "OFF")) {
|
||||||
this->set_mode(WATER_HEATER_MODE_OFF);
|
this->set_mode(WATER_HEATER_MODE_OFF);
|
||||||
} else if (ESPHOME_strcasecmp_P(mode, ESPHOME_PSTR("ECO")) == 0) {
|
} else if (str_equals_case_insensitive(mode, "ECO")) {
|
||||||
this->set_mode(WATER_HEATER_MODE_ECO);
|
this->set_mode(WATER_HEATER_MODE_ECO);
|
||||||
} else if (ESPHOME_strcasecmp_P(mode, ESPHOME_PSTR("ELECTRIC")) == 0) {
|
} else if (str_equals_case_insensitive(mode, "ELECTRIC")) {
|
||||||
this->set_mode(WATER_HEATER_MODE_ELECTRIC);
|
this->set_mode(WATER_HEATER_MODE_ELECTRIC);
|
||||||
} else if (ESPHOME_strcasecmp_P(mode, ESPHOME_PSTR("PERFORMANCE")) == 0) {
|
} else if (str_equals_case_insensitive(mode, "PERFORMANCE")) {
|
||||||
this->set_mode(WATER_HEATER_MODE_PERFORMANCE);
|
this->set_mode(WATER_HEATER_MODE_PERFORMANCE);
|
||||||
} else if (ESPHOME_strcasecmp_P(mode, ESPHOME_PSTR("HIGH_DEMAND")) == 0) {
|
} else if (str_equals_case_insensitive(mode, "HIGH_DEMAND")) {
|
||||||
this->set_mode(WATER_HEATER_MODE_HIGH_DEMAND);
|
this->set_mode(WATER_HEATER_MODE_HIGH_DEMAND);
|
||||||
} else if (ESPHOME_strcasecmp_P(mode, ESPHOME_PSTR("HEAT_PUMP")) == 0) {
|
} else if (str_equals_case_insensitive(mode, "HEAT_PUMP")) {
|
||||||
this->set_mode(WATER_HEATER_MODE_HEAT_PUMP);
|
this->set_mode(WATER_HEATER_MODE_HEAT_PUMP);
|
||||||
} else if (ESPHOME_strcasecmp_P(mode, ESPHOME_PSTR("GAS")) == 0) {
|
} else if (str_equals_case_insensitive(mode, "GAS")) {
|
||||||
this->set_mode(WATER_HEATER_MODE_GAS);
|
this->set_mode(WATER_HEATER_MODE_GAS);
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGW(TAG, "'%s' - Unrecognized mode %s", this->parent_->get_name().c_str(), mode);
|
ESP_LOGW(TAG, "'%s' - Unrecognized mode %s", this->parent_->get_name().c_str(), mode.c_str());
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,8 +75,7 @@ class WaterHeaterCall {
|
|||||||
WaterHeaterCall(WaterHeater *parent);
|
WaterHeaterCall(WaterHeater *parent);
|
||||||
|
|
||||||
WaterHeaterCall &set_mode(WaterHeaterMode mode);
|
WaterHeaterCall &set_mode(WaterHeaterMode mode);
|
||||||
WaterHeaterCall &set_mode(const char *mode);
|
WaterHeaterCall &set_mode(const std::string &mode);
|
||||||
WaterHeaterCall &set_mode(const std::string &mode) { return this->set_mode(mode.c_str()); }
|
|
||||||
WaterHeaterCall &set_target_temperature(float temperature);
|
WaterHeaterCall &set_target_temperature(float temperature);
|
||||||
WaterHeaterCall &set_target_temperature_low(float temperature);
|
WaterHeaterCall &set_target_temperature_low(float temperature);
|
||||||
WaterHeaterCall &set_target_temperature_high(float temperature);
|
WaterHeaterCall &set_target_temperature_high(float temperature);
|
||||||
|
|||||||
@@ -585,13 +585,11 @@ async def to_code(config):
|
|||||||
await cg.past_safe_mode()
|
await cg.past_safe_mode()
|
||||||
|
|
||||||
if on_connect_config := config.get(CONF_ON_CONNECT):
|
if on_connect_config := config.get(CONF_ON_CONNECT):
|
||||||
cg.add_define("USE_WIFI_CONNECT_TRIGGER")
|
|
||||||
await automation.build_automation(
|
await automation.build_automation(
|
||||||
var.get_connect_trigger(), [], on_connect_config
|
var.get_connect_trigger(), [], on_connect_config
|
||||||
)
|
)
|
||||||
|
|
||||||
if on_disconnect_config := config.get(CONF_ON_DISCONNECT):
|
if on_disconnect_config := config.get(CONF_ON_DISCONNECT):
|
||||||
cg.add_define("USE_WIFI_DISCONNECT_TRIGGER")
|
|
||||||
await automation.build_automation(
|
await automation.build_automation(
|
||||||
var.get_disconnect_trigger(), [], on_disconnect_config
|
var.get_disconnect_trigger(), [], on_disconnect_config
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ template<typename... Ts> class WiFiConfigureAction : public Action<Ts...>, publi
|
|||||||
char ssid_buf[SSID_BUFFER_SIZE];
|
char ssid_buf[SSID_BUFFER_SIZE];
|
||||||
if (strcmp(global_wifi_component->wifi_ssid_to(ssid_buf), ssid.c_str()) == 0) {
|
if (strcmp(global_wifi_component->wifi_ssid_to(ssid_buf), ssid.c_str()) == 0) {
|
||||||
// Callback to notify the user that the connection was successful
|
// Callback to notify the user that the connection was successful
|
||||||
this->connect_trigger_.trigger();
|
this->connect_trigger_->trigger();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Create a new WiFiAP object with the new SSID and password
|
// Create a new WiFiAP object with the new SSID and password
|
||||||
@@ -79,13 +79,13 @@ template<typename... Ts> class WiFiConfigureAction : public Action<Ts...>, publi
|
|||||||
// Start a timeout for the fallback if the connection to the old AP fails
|
// Start a timeout for the fallback if the connection to the old AP fails
|
||||||
this->set_timeout("wifi-fallback-timeout", this->connection_timeout_.value(x...), [this]() {
|
this->set_timeout("wifi-fallback-timeout", this->connection_timeout_.value(x...), [this]() {
|
||||||
this->connecting_ = false;
|
this->connecting_ = false;
|
||||||
this->error_trigger_.trigger();
|
this->error_trigger_->trigger();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Trigger<> *get_connect_trigger() { return &this->connect_trigger_; }
|
Trigger<> *get_connect_trigger() const { return this->connect_trigger_; }
|
||||||
Trigger<> *get_error_trigger() { return &this->error_trigger_; }
|
Trigger<> *get_error_trigger() const { return this->error_trigger_; }
|
||||||
|
|
||||||
void loop() override {
|
void loop() override {
|
||||||
if (!this->connecting_)
|
if (!this->connecting_)
|
||||||
@@ -98,10 +98,10 @@ template<typename... Ts> class WiFiConfigureAction : public Action<Ts...>, publi
|
|||||||
char ssid_buf[SSID_BUFFER_SIZE];
|
char ssid_buf[SSID_BUFFER_SIZE];
|
||||||
if (strcmp(global_wifi_component->wifi_ssid_to(ssid_buf), this->new_sta_.get_ssid().c_str()) == 0) {
|
if (strcmp(global_wifi_component->wifi_ssid_to(ssid_buf), this->new_sta_.get_ssid().c_str()) == 0) {
|
||||||
// Callback to notify the user that the connection was successful
|
// Callback to notify the user that the connection was successful
|
||||||
this->connect_trigger_.trigger();
|
this->connect_trigger_->trigger();
|
||||||
} else {
|
} else {
|
||||||
// Callback to notify the user that the connection failed
|
// Callback to notify the user that the connection failed
|
||||||
this->error_trigger_.trigger();
|
this->error_trigger_->trigger();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,8 +110,8 @@ template<typename... Ts> class WiFiConfigureAction : public Action<Ts...>, publi
|
|||||||
bool connecting_{false};
|
bool connecting_{false};
|
||||||
WiFiAP new_sta_;
|
WiFiAP new_sta_;
|
||||||
WiFiAP old_sta_;
|
WiFiAP old_sta_;
|
||||||
Trigger<> connect_trigger_;
|
Trigger<> *connect_trigger_{new Trigger<>()};
|
||||||
Trigger<> error_trigger_;
|
Trigger<> *error_trigger_{new Trigger<>()};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace esphome::wifi
|
} // namespace esphome::wifi
|
||||||
|
|||||||
@@ -651,21 +651,14 @@ void WiFiComponent::loop() {
|
|||||||
const uint32_t now = App.get_loop_component_start_time();
|
const uint32_t now = App.get_loop_component_start_time();
|
||||||
|
|
||||||
if (this->has_sta()) {
|
if (this->has_sta()) {
|
||||||
#if defined(USE_WIFI_CONNECT_TRIGGER) || defined(USE_WIFI_DISCONNECT_TRIGGER)
|
|
||||||
if (this->is_connected() != this->handled_connected_state_) {
|
if (this->is_connected() != this->handled_connected_state_) {
|
||||||
#ifdef USE_WIFI_DISCONNECT_TRIGGER
|
|
||||||
if (this->handled_connected_state_) {
|
if (this->handled_connected_state_) {
|
||||||
this->disconnect_trigger_.trigger();
|
this->disconnect_trigger_->trigger();
|
||||||
|
} else {
|
||||||
|
this->connect_trigger_->trigger();
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
#ifdef USE_WIFI_CONNECT_TRIGGER
|
|
||||||
if (!this->handled_connected_state_) {
|
|
||||||
this->connect_trigger_.trigger();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
this->handled_connected_state_ = this->is_connected();
|
this->handled_connected_state_ = this->is_connected();
|
||||||
}
|
}
|
||||||
#endif // USE_WIFI_CONNECT_TRIGGER || USE_WIFI_DISCONNECT_TRIGGER
|
|
||||||
|
|
||||||
switch (this->state_) {
|
switch (this->state_) {
|
||||||
case WIFI_COMPONENT_STATE_COOLDOWN: {
|
case WIFI_COMPONENT_STATE_COOLDOWN: {
|
||||||
|
|||||||
@@ -454,12 +454,8 @@ class WiFiComponent : public Component {
|
|||||||
void set_keep_scan_results(bool keep_scan_results) { this->keep_scan_results_ = keep_scan_results; }
|
void set_keep_scan_results(bool keep_scan_results) { this->keep_scan_results_ = keep_scan_results; }
|
||||||
void set_post_connect_roaming(bool enabled) { this->post_connect_roaming_ = enabled; }
|
void set_post_connect_roaming(bool enabled) { this->post_connect_roaming_ = enabled; }
|
||||||
|
|
||||||
#ifdef USE_WIFI_CONNECT_TRIGGER
|
Trigger<> *get_connect_trigger() const { return this->connect_trigger_; };
|
||||||
Trigger<> *get_connect_trigger() { return &this->connect_trigger_; }
|
Trigger<> *get_disconnect_trigger() const { return this->disconnect_trigger_; };
|
||||||
#endif
|
|
||||||
#ifdef USE_WIFI_DISCONNECT_TRIGGER
|
|
||||||
Trigger<> *get_disconnect_trigger() { return &this->disconnect_trigger_; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int32_t get_wifi_channel();
|
int32_t get_wifi_channel();
|
||||||
|
|
||||||
@@ -710,9 +706,7 @@ class WiFiComponent : public Component {
|
|||||||
|
|
||||||
// Group all boolean values together
|
// Group all boolean values together
|
||||||
bool has_ap_{false};
|
bool has_ap_{false};
|
||||||
#if defined(USE_WIFI_CONNECT_TRIGGER) || defined(USE_WIFI_DISCONNECT_TRIGGER)
|
|
||||||
bool handled_connected_state_{false};
|
bool handled_connected_state_{false};
|
||||||
#endif
|
|
||||||
bool error_from_callback_{false};
|
bool error_from_callback_{false};
|
||||||
bool scan_done_{false};
|
bool scan_done_{false};
|
||||||
bool ap_setup_{false};
|
bool ap_setup_{false};
|
||||||
@@ -739,12 +733,9 @@ class WiFiComponent : public Component {
|
|||||||
SemaphoreHandle_t high_performance_semaphore_{nullptr};
|
SemaphoreHandle_t high_performance_semaphore_{nullptr};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_WIFI_CONNECT_TRIGGER
|
// Pointers at the end (naturally aligned)
|
||||||
Trigger<> connect_trigger_;
|
Trigger<> *connect_trigger_{new Trigger<>()};
|
||||||
#endif
|
Trigger<> *disconnect_trigger_{new Trigger<>()};
|
||||||
#ifdef USE_WIFI_DISCONNECT_TRIGGER
|
|
||||||
Trigger<> disconnect_trigger_;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Stores a pointer to a string literal (static storage duration).
|
// Stores a pointer to a string literal (static storage duration).
|
||||||
|
|||||||
@@ -152,11 +152,10 @@ class ZephyrPreferences : public ESPPreferences {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static ZephyrPreferences s_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
|
||||||
|
|
||||||
void setup_preferences() {
|
void setup_preferences() {
|
||||||
global_preferences = &s_preferences;
|
auto *prefs = new ZephyrPreferences(); // NOLINT(cppcoreguidelines-owning-memory)
|
||||||
s_preferences.open();
|
global_preferences = prefs;
|
||||||
|
prefs->open();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace zephyr
|
} // namespace zephyr
|
||||||
|
|||||||
@@ -227,8 +227,6 @@
|
|||||||
#define USE_WIFI_SCAN_RESULTS_LISTENERS
|
#define USE_WIFI_SCAN_RESULTS_LISTENERS
|
||||||
#define USE_WIFI_CONNECT_STATE_LISTENERS
|
#define USE_WIFI_CONNECT_STATE_LISTENERS
|
||||||
#define USE_WIFI_POWER_SAVE_LISTENERS
|
#define USE_WIFI_POWER_SAVE_LISTENERS
|
||||||
#define USE_WIFI_CONNECT_TRIGGER
|
|
||||||
#define USE_WIFI_DISCONNECT_TRIGGER
|
|
||||||
#define ESPHOME_WIFI_IP_STATE_LISTENERS 2
|
#define ESPHOME_WIFI_IP_STATE_LISTENERS 2
|
||||||
#define ESPHOME_WIFI_SCAN_RESULTS_LISTENERS 2
|
#define ESPHOME_WIFI_SCAN_RESULTS_LISTENERS 2
|
||||||
#define ESPHOME_WIFI_CONNECT_STATE_LISTENERS 2
|
#define ESPHOME_WIFI_CONNECT_STATE_LISTENERS 2
|
||||||
|
|||||||
@@ -756,6 +756,28 @@ def lint_no_sprintf(fname, match):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@lint_re_check(
|
||||||
|
# Match scanf family functions: scanf, sscanf, fscanf, vscanf, vsscanf, vfscanf
|
||||||
|
# Also match std:: prefixed versions
|
||||||
|
# [^\w] ensures we match function calls, not substrings
|
||||||
|
r"[^\w]((?:std::)?v?[fs]?scanf)\s*\(" + CPP_RE_EOL,
|
||||||
|
include=cpp_include,
|
||||||
|
)
|
||||||
|
def lint_no_scanf(fname, match):
|
||||||
|
func = match.group(1)
|
||||||
|
return (
|
||||||
|
f"{highlight(func + '()')} is not allowed in new ESPHome code. The scanf family "
|
||||||
|
f"pulls in ~7KB flash on ESP8266 and ~9KB on ESP32, and ESPHome doesn't otherwise "
|
||||||
|
f"need this code.\n"
|
||||||
|
f"Please use alternatives:\n"
|
||||||
|
f" - {highlight('parse_number<T>(str)')} for parsing integers/floats from strings\n"
|
||||||
|
f" - {highlight('strtol()/strtof()')} for C-style number parsing with error checking\n"
|
||||||
|
f" - {highlight('parse_hex()')} for hex string parsing\n"
|
||||||
|
f" - Manual parsing for simple fixed formats\n"
|
||||||
|
f"(If strictly necessary, add `// NOLINT` to the end of the line)"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@lint_content_find_check(
|
@lint_content_find_check(
|
||||||
"ESP_LOG",
|
"ESP_LOG",
|
||||||
include=["*.h", "*.tcc"],
|
include=["*.h", "*.tcc"],
|
||||||
|
|||||||
@@ -26,7 +26,3 @@ wifi:
|
|||||||
- ssid: MySSID3
|
- ssid: MySSID3
|
||||||
password: password3
|
password: password3
|
||||||
priority: 0
|
priority: 0
|
||||||
on_connect:
|
|
||||||
- logger.log: "WiFi connected!"
|
|
||||||
on_disconnect:
|
|
||||||
- logger.log: "WiFi disconnected!"
|
|
||||||
|
|||||||
Reference in New Issue
Block a user