Compare commits

..

12 Commits

Author SHA1 Message Date
J. Nick Koston
99a54369bf Merge remote-tracking branch 'upstream/dev' into loop_runtime_stats 2025-06-11 22:01:22 -05:00
J. Nick Koston
98a2f23024 Merge remote-tracking branch 'upstream/dev' into loop_runtime_stats 2025-05-29 11:04:14 -05:00
J. Nick Koston
c955897d1b Merge remote-tracking branch 'upstream/dev' into loop_runtime_stats 2025-05-27 11:39:45 -05:00
J. Nick Koston
cfdb0925ce Merge branch 'dev' into loop_runtime_stats 2025-05-13 23:42:19 -05:00
J. Nick Koston
83db3eddd9 revert ota 2025-05-13 01:07:43 -05:00
J. Nick Koston
cc2c5a544e revert ota 2025-05-13 01:07:38 -05:00
J. Nick Koston
8fba8c2800 revert ota 2025-05-13 01:05:37 -05:00
J. Nick Koston
51d1da8460 revert ota 2025-05-13 01:04:09 -05:00
J. Nick Koston
2f1257056d revert 2025-05-13 01:02:00 -05:00
J. Nick Koston
2f8f6967bf fix ota 2025-05-13 00:55:19 -05:00
J. Nick Koston
246527e618 runtime stats 2025-05-13 00:54:05 -05:00
J. Nick Koston
3857cc9c83 runtime stats 2025-05-13 00:51:14 -05:00
29 changed files with 298 additions and 218 deletions

View File

@@ -22,7 +22,6 @@ from esphome.cpp_generator import ( # noqa: F401
TemplateArguments,
add,
add_build_flag,
add_build_unflag,
add_define,
add_global,
add_library,
@@ -35,7 +34,6 @@ from esphome.cpp_generator import ( # noqa: F401
process_lambda,
progmem_array,
safe_exp,
set_cpp_standard,
statement,
static_const_array,
templatable,

View File

@@ -260,7 +260,7 @@ uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint16_t mes
return 0; // Doesn't fit
}
// Allocate buffer space - pass payload size, allocation functions add header/footer space
// Allocate exact buffer space needed (just the payload, not the overhead)
ProtoWriteBuffer buffer =
is_single ? conn->allocate_single_message_buffer(size) : conn->allocate_batch_message_buffer(size);

View File

@@ -94,13 +94,6 @@ COMPILER_OPTIMIZATIONS = {
"SIZE": "CONFIG_COMPILER_OPTIMIZATION_SIZE",
}
ARDUINO_ALLOWED_VARIANTS = [
VARIANT_ESP32,
VARIANT_ESP32C3,
VARIANT_ESP32S2,
VARIANT_ESP32S3,
]
def get_cpu_frequencies(*frequencies):
return [str(x) + "MHZ" for x in frequencies]
@@ -150,17 +143,12 @@ def set_core_data(config):
CORE.data[KEY_ESP32][KEY_COMPONENTS] = {}
elif conf[CONF_TYPE] == FRAMEWORK_ARDUINO:
CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "arduino"
if variant not in ARDUINO_ALLOWED_VARIANTS:
raise cv.Invalid(
f"ESPHome does not support using the Arduino framework for the {variant}. Please use the ESP-IDF framework instead.",
path=[CONF_FRAMEWORK, CONF_TYPE],
)
CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version.parse(
config[CONF_FRAMEWORK][CONF_VERSION]
)
CORE.data[KEY_ESP32][KEY_BOARD] = config[CONF_BOARD]
CORE.data[KEY_ESP32][KEY_VARIANT] = variant
CORE.data[KEY_ESP32][KEY_VARIANT] = config[CONF_VARIANT]
CORE.data[KEY_ESP32][KEY_EXTRA_BUILD_FILES] = {}
return config
@@ -630,21 +618,6 @@ ESP_IDF_FRAMEWORK_SCHEMA = cv.All(
)
def _set_default_framework(config):
if CONF_FRAMEWORK not in config:
config = config.copy()
variant = config[CONF_VARIANT]
if variant in ARDUINO_ALLOWED_VARIANTS:
config[CONF_FRAMEWORK] = ARDUINO_FRAMEWORK_SCHEMA({})
config[CONF_FRAMEWORK][CONF_TYPE] = FRAMEWORK_ARDUINO
else:
config[CONF_FRAMEWORK] = ESP_IDF_FRAMEWORK_SCHEMA({})
config[CONF_FRAMEWORK][CONF_TYPE] = FRAMEWORK_ESP_IDF
return config
FRAMEWORK_ESP_IDF = "esp-idf"
FRAMEWORK_ARDUINO = "arduino"
FRAMEWORK_SCHEMA = cv.typed_schema(
@@ -654,6 +627,7 @@ FRAMEWORK_SCHEMA = cv.typed_schema(
},
lower=True,
space="-",
default_type=FRAMEWORK_ARDUINO,
)
@@ -680,11 +654,10 @@ CONFIG_SCHEMA = cv.All(
),
cv.Optional(CONF_PARTITIONS): cv.file_,
cv.Optional(CONF_VARIANT): cv.one_of(*VARIANTS, upper=True),
cv.Optional(CONF_FRAMEWORK): FRAMEWORK_SCHEMA,
cv.Optional(CONF_FRAMEWORK, default={}): FRAMEWORK_SCHEMA,
}
),
_detect_variant,
_set_default_framework,
set_core_data,
)
@@ -695,7 +668,6 @@ FINAL_VALIDATE_SCHEMA = cv.Schema(final_validate)
async def to_code(config):
cg.add_platformio_option("board", config[CONF_BOARD])
cg.add_platformio_option("board_upload.flash_size", config[CONF_FLASH_SIZE])
cg.set_cpp_standard("gnu++17")
cg.add_build_flag("-DUSE_ESP32")
cg.add_define("ESPHOME_BOARD", config[CONF_BOARD])
cg.add_build_flag(f"-DUSE_ESP32_VARIANT_{config[CONF_VARIANT]}")

View File

@@ -183,7 +183,6 @@ async def to_code(config):
cg.add_platformio_option("board", config[CONF_BOARD])
cg.add_build_flag("-DUSE_ESP8266")
cg.set_cpp_standard("gnu++17")
cg.add_define("ESPHOME_BOARD", config[CONF_BOARD])
cg.add_define("ESPHOME_VARIANT", "ESP8266")

View File

@@ -129,13 +129,6 @@ void INA219Component::setup() {
}
}
void INA219Component::on_powerdown() {
// Mode = 0 -> power down
if (!this->write_byte_16(INA219_REGISTER_CONFIG, 0)) {
ESP_LOGE(TAG, "powerdown error");
}
}
void INA219Component::dump_config() {
ESP_LOGCONFIG(TAG, "INA219:");
LOG_I2C_DEVICE(this);

View File

@@ -15,7 +15,6 @@ class INA219Component : public PollingComponent, public i2c::I2CDevice {
void dump_config() override;
float get_setup_priority() const override;
void update() override;
void on_powerdown() override;
void set_shunt_resistance_ohm(float shunt_resistance_ohm) { shunt_resistance_ohm_ = shunt_resistance_ohm; }
void set_max_current_a(float max_current_a) { max_current_a_ = max_current_a; }

View File

@@ -264,7 +264,6 @@ async def component_to_code(config):
# force using arduino framework
cg.add_platformio_option("framework", "arduino")
cg.add_build_flag("-DUSE_ARDUINO")
cg.set_cpp_standard("gnu++17")
# disable library compatibility checks
cg.add_platformio_option("lib_ldf_mode", "off")

View File

@@ -337,26 +337,23 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
bool Nextion::upload_end_(bool successful) {
ESP_LOGD(TAG, "TFT upload done: %s", YESNO(successful));
this->is_updating_ = false;
this->ignore_is_setup_ = false;
uint32_t baud_rate = this->parent_->get_baud_rate();
if (baud_rate != this->original_baud_rate_) {
ESP_LOGD(TAG, "Baud back: %" PRIu32 "->%" PRIu32, baud_rate, this->original_baud_rate_);
this->parent_->set_baud_rate(this->original_baud_rate_);
this->parent_->load_settings();
}
if (successful) {
ESP_LOGD(TAG, "Restart");
delay(1500); // NOLINT
App.safe_reboot();
delay(1500); // NOLINT
} else {
ESP_LOGE(TAG, "TFT upload failed");
this->is_updating_ = false;
this->ignore_is_setup_ = false;
uint32_t baud_rate = this->parent_->get_baud_rate();
if (baud_rate != this->original_baud_rate_) {
ESP_LOGD(TAG, "Baud back: %" PRIu32 "->%" PRIu32, baud_rate, this->original_baud_rate_);
this->parent_->set_baud_rate(this->original_baud_rate_);
this->parent_->load_settings();
}
}
return successful;
}

View File

@@ -337,6 +337,15 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
bool Nextion::upload_end_(bool successful) {
ESP_LOGD(TAG, "TFT upload done: %s", YESNO(successful));
this->is_updating_ = false;
this->ignore_is_setup_ = false;
uint32_t baud_rate = this->parent_->get_baud_rate();
if (baud_rate != this->original_baud_rate_) {
ESP_LOGD(TAG, "Baud back: %" PRIu32 "->%" PRIu32, baud_rate, this->original_baud_rate_);
this->parent_->set_baud_rate(this->original_baud_rate_);
this->parent_->load_settings();
}
if (successful) {
ESP_LOGD(TAG, "Restart");
@@ -344,18 +353,7 @@ bool Nextion::upload_end_(bool successful) {
App.safe_reboot();
} else {
ESP_LOGE(TAG, "TFT upload failed");
this->is_updating_ = false;
this->ignore_is_setup_ = false;
uint32_t baud_rate = this->parent_->get_baud_rate();
if (baud_rate != this->original_baud_rate_) {
ESP_LOGD(TAG, "Baud back: %" PRIu32 "->%" PRIu32, baud_rate, this->original_baud_rate_);
this->parent_->set_baud_rate(this->original_baud_rate_);
this->parent_->load_settings();
}
}
return successful;
}

View File

@@ -31,6 +31,7 @@ CONFIG_SCHEMA = cv.Schema(
}
),
},
cv.only_with_arduino,
).extend(cv.COMPONENT_SCHEMA)

View File

@@ -167,7 +167,6 @@ async def to_code(config):
cg.add_platformio_option("lib_ldf_mode", "chain+")
cg.add_platformio_option("board", config[CONF_BOARD])
cg.add_build_flag("-DUSE_RP2040")
cg.set_cpp_standard("gnu++17")
cg.add_define("ESPHOME_BOARD", config[CONF_BOARD])
cg.add_define("ESPHOME_VARIANT", "RP2040")

View File

@@ -0,0 +1,26 @@
"""
Runtime statistics component for ESPHome.
"""
import esphome.codegen as cg
import esphome.config_validation as cv
DEPENDENCIES = []
CONF_ENABLED = "enabled"
CONF_LOG_INTERVAL = "log_interval"
CONFIG_SCHEMA = cv.Schema(
{
cv.Optional(CONF_ENABLED, default=True): cv.boolean,
cv.Optional(
CONF_LOG_INTERVAL, default=60000
): cv.positive_time_period_milliseconds,
}
)
async def to_code(config):
"""Generate code for the runtime statistics component."""
cg.add(cg.App.set_runtime_stats_enabled(config[CONF_ENABLED]))
cg.add(cg.App.set_runtime_stats_log_interval(config[CONF_LOG_INTERVAL]))

View File

@@ -110,7 +110,15 @@ void TemplateAlarmControlPanel::loop() {
delay = this->arming_night_time_;
}
if ((millis() - this->last_update_) > delay) {
this->bypass_before_arming();
#ifdef USE_BINARY_SENSOR
for (auto sensor_info : this->sensor_map_) {
// Check for sensors left on and set to bypass automatically and remove them from monitoring
if ((sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_AUTO) && (sensor_info.first->state)) {
ESP_LOGW(TAG, "%s is left on and will be automatically bypassed", sensor_info.first->get_name().c_str());
this->bypassed_sensor_indicies_.push_back(sensor_info.second.store_index);
}
}
#endif
this->publish_state(this->desired_state_);
}
return;
@@ -251,23 +259,10 @@ void TemplateAlarmControlPanel::arm_(optional<std::string> code, alarm_control_p
if (delay > 0) {
this->publish_state(ACP_STATE_ARMING);
} else {
this->bypass_before_arming();
this->publish_state(state);
}
}
void TemplateAlarmControlPanel::bypass_before_arming() {
#ifdef USE_BINARY_SENSOR
for (auto sensor_info : this->sensor_map_) {
// Check for sensors left on and set to bypass automatically and remove them from monitoring
if ((sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_AUTO) && (sensor_info.first->state)) {
ESP_LOGW(TAG, "'%s' is left on and will be automatically bypassed", sensor_info.first->get_name().c_str());
this->bypassed_sensor_indicies_.push_back(sensor_info.second.store_index);
}
}
#endif
}
void TemplateAlarmControlPanel::control(const AlarmControlPanelCall &call) {
if (call.get_state()) {
if (call.get_state() == ACP_STATE_ARMED_AWAY) {

View File

@@ -60,7 +60,6 @@ class TemplateAlarmControlPanel : public alarm_control_panel::AlarmControlPanel,
bool get_requires_code_to_arm() const override { return this->requires_code_to_arm_; }
bool get_all_sensors_ready() { return this->sensors_ready_; };
void set_restore_mode(TemplateAlarmControlPanelRestoreMode restore_mode) { this->restore_mode_ = restore_mode; }
void bypass_before_arming();
#ifdef USE_BINARY_SENSOR
/** Add a binary_sensor to the alarm_panel.

View File

@@ -8,6 +8,8 @@ CONFIG_SCHEMA = cv.All(
cv.only_with_esp_idf,
)
AUTO_LOAD = ["web_server"]
async def to_code(config):
# Increase the maximum supported size of headers section in HTTP request packet to be processed by the server

View File

@@ -9,12 +9,10 @@
#include "utils.h"
#include "web_server_idf.h"
#ifdef USE_WEBSERVER
#include "esphome/components/web_server/web_server.h"
#include "esphome/components/web_server/list_entities.h"
#endif // USE_WEBSERVER
#include "web_server_idf.h"
namespace esphome {
namespace web_server_idf {
@@ -275,7 +273,6 @@ void AsyncResponseStream::printf(const char *fmt, ...) {
this->print(str);
}
#ifdef USE_WEBSERVER
AsyncEventSource::~AsyncEventSource() {
for (auto *ses : this->sessions_) {
delete ses; // NOLINT(cppcoreguidelines-owning-memory)
@@ -514,7 +511,6 @@ void AsyncEventSourceResponse::deferrable_send_state(void *source, const char *e
}
}
}
#endif
} // namespace web_server_idf
} // namespace esphome

View File

@@ -1,7 +1,6 @@
#pragma once
#ifdef USE_ESP_IDF
#include "esphome/core/defines.h"
#include <esp_http_server.h>
#include <functional>
@@ -13,12 +12,10 @@
#include <vector>
namespace esphome {
#ifdef USE_WEBSERVER
namespace web_server {
class WebServer;
class ListEntitiesIterator;
}; // namespace web_server
#endif
namespace web_server_idf {
#define F(string_literal) (string_literal)
@@ -223,7 +220,6 @@ class AsyncWebHandler {
virtual bool isRequestHandlerTrivial() { return true; }
};
#ifdef USE_WEBSERVER
class AsyncEventSource;
class AsyncEventSourceResponse;
@@ -311,13 +307,10 @@ class AsyncEventSource : public AsyncWebHandler {
connect_handler_t on_connect_{};
esphome::web_server::WebServer *web_server_;
};
#endif // USE_WEBSERVER
class DefaultHeaders {
friend class AsyncWebServerRequest;
#ifdef USE_WEBSERVER
friend class AsyncEventSourceResponse;
#endif
public:
// NOLINTNEXTLINE(readability-identifier-naming)

View File

@@ -507,8 +507,6 @@ class EsphomeCore:
self.libraries: list[Library] = []
# A set of build flags to set in the platformio project
self.build_flags: set[str] = set()
# A set of build unflags to set in the platformio project
self.build_unflags: set[str] = set()
# A set of defines to set for the compile process in esphome/core/defines.h
self.defines: set[Define] = set()
# A map of all platformio options to apply
@@ -547,7 +545,6 @@ class EsphomeCore:
self.global_statements = []
self.libraries = []
self.build_flags = set()
self.build_unflags = set()
self.defines = set()
self.platformio_options = {}
self.loaded_integrations = set()
@@ -769,15 +766,11 @@ class EsphomeCore:
self.libraries.append(library)
return library
def add_build_flag(self, build_flag: str) -> str:
def add_build_flag(self, build_flag):
self.build_flags.add(build_flag)
_LOGGER.debug("Adding build flag: %s", build_flag)
return build_flag
def add_build_unflag(self, build_unflag: str) -> None:
self.build_unflags.add(build_unflag)
_LOGGER.debug("Adding build unflag: %s", build_unflag)
def add_define(self, define):
if isinstance(define, str):
define = Define(define)

View File

@@ -117,9 +117,7 @@ void Application::loop() {
// Use the last component's end time instead of calling millis() again
auto elapsed = last_op_end_time - this->last_loop_;
if (elapsed >= this->loop_interval_ || HighFrequencyLoopRequester::is_high_frequency()) {
// Even if we overran the loop interval, we still need to select()
// to know if any sockets have data ready
this->yield_with_select_(0);
yield();
} else {
uint32_t delay_time = this->loop_interval_ - elapsed;
uint32_t next_schedule = this->scheduler.next_schedule_in().value_or(delay_time);
@@ -128,7 +126,7 @@ void Application::loop() {
next_schedule = std::max(next_schedule, delay_time / 2);
delay_time = std::min(next_schedule, delay_time);
this->yield_with_select_(delay_time);
this->delay_with_select_(delay_time);
}
this->last_loop_ = last_op_end_time;
@@ -217,7 +215,7 @@ void Application::teardown_components(uint32_t timeout_ms) {
// Give some time for I/O operations if components are still pending
if (!pending_components.empty()) {
this->yield_with_select_(1);
this->delay_with_select_(1);
}
// Update time for next iteration
@@ -295,6 +293,8 @@ bool Application::is_socket_ready(int fd) const {
// This function is thread-safe for reading the result of select()
// However, it should only be called after select() has been executed in the main loop
// The read_fds_ is only modified by select() in the main loop
if (HighFrequencyLoopRequester::is_high_frequency())
return true; // fd sets via select are not updated in high frequency looping - so force true fallback behavior
if (fd < 0 || fd >= FD_SETSIZE)
return false;
@@ -302,9 +302,7 @@ bool Application::is_socket_ready(int fd) const {
}
#endif
void Application::yield_with_select_(uint32_t delay_ms) {
// Delay while monitoring sockets. When delay_ms is 0, always yield() to ensure other tasks run
// since select() with 0 timeout only polls without yielding.
void Application::delay_with_select_(uint32_t delay_ms) {
#ifdef USE_SOCKET_SELECT_SUPPORT
if (!this->socket_fds_.empty()) {
// Update fd_set if socket list has changed
@@ -342,10 +340,6 @@ void Application::yield_with_select_(uint32_t delay_ms) {
ESP_LOGW(TAG, "select() failed with errno %d", errno);
delay(delay_ms);
}
// When delay_ms is 0, we need to yield since select(0) doesn't yield
if (delay_ms == 0) {
yield();
}
} else {
// No sockets registered, use regular delay
delay(delay_ms);

View File

@@ -7,6 +7,7 @@
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/preferences.h"
#include "esphome/core/runtime_stats.h"
#include "esphome/core/scheduler.h"
#ifdef USE_SOCKET_SELECT_SUPPORT
@@ -314,6 +315,18 @@ class Application {
uint32_t get_loop_interval() const { return this->loop_interval_; }
/** Enable or disable runtime statistics collection.
*
* @param enable Whether to enable runtime statistics collection.
*/
void set_runtime_stats_enabled(bool enable) { runtime_stats.set_enabled(enable); }
/** Set the interval at which runtime statistics are logged.
*
* @param interval The interval in milliseconds between logging of runtime statistics.
*/
void set_runtime_stats_log_interval(uint32_t interval) { runtime_stats.set_log_interval(interval); }
void schedule_dump_config() { this->dump_config_at_ = 0; }
void feed_wdt(uint32_t time = 0);
@@ -575,7 +588,7 @@ class Application {
void feed_wdt_arch_();
/// Perform a delay while also monitoring socket file descriptors for readiness
void yield_with_select_(uint32_t delay_ms);
void delay_with_select_(uint32_t delay_ms);
std::vector<Component *> components_{};
std::vector<Component *> looping_components_{};
@@ -649,27 +662,21 @@ class Application {
std::string area_;
const char *comment_{nullptr};
const char *compilation_time_{nullptr};
Component *current_component_{nullptr};
bool name_add_mac_suffix_;
uint32_t last_loop_{0};
uint32_t loop_interval_{16};
<<<<<<< Updated upstream
size_t dump_config_at_{SIZE_MAX};
uint32_t app_state_{0};
Component *current_component_{nullptr};
=======
>>>>>>> Stashed changes
uint32_t loop_component_start_time_{0};
size_t dump_config_at_{SIZE_MAX};
bool name_add_mac_suffix_;
uint8_t app_state_{0};
#ifdef USE_SOCKET_SELECT_SUPPORT
// Socket select management
std::vector<int> socket_fds_; // Vector of all monitored socket file descriptors
bool socket_fds_changed_{false}; // Flag to rebuild base_read_fds_ when socket_fds_ changes
int max_fd_{-1}; // Highest file descriptor number for select()
fd_set base_read_fds_{}; // Cached fd_set rebuilt only when socket_fds_ changes
fd_set read_fds_{}; // Working fd_set for select(), copied from base_read_fds_
int max_fd_{-1}; // Highest file descriptor number for select()
bool socket_fds_changed_{false}; // Flag to rebuild base_read_fds_ when socket_fds_ changes
#endif
};

View File

@@ -246,6 +246,9 @@ uint32_t WarnIfComponentBlockingGuard::finish() {
uint32_t curr_time = millis();
uint32_t blocking_time = curr_time - this->started_;
// Record component runtime stats
runtime_stats.record_component_time(this->component_, blocking_time, curr_time);
bool should_warn;
if (this->component_ != nullptr) {
should_warn = this->component_->should_warn_of_blocking(blocking_time);

View File

@@ -6,6 +6,7 @@
#include <string>
#include "esphome/core/optional.h"
#include "esphome/core/runtime_stats.h"
namespace esphome {
@@ -63,7 +64,7 @@ extern const uint32_t STATUS_LED_OK;
extern const uint32_t STATUS_LED_WARNING;
extern const uint32_t STATUS_LED_ERROR;
enum class RetryResult : uint8_t { DONE, RETRY };
enum class RetryResult { DONE, RETRY };
extern const uint32_t WARN_IF_BLOCKING_OVER_MS;

View File

@@ -0,0 +1,28 @@
#include "esphome/core/runtime_stats.h"
#include "esphome/core/component.h"
namespace esphome {
RuntimeStatsCollector runtime_stats;
void RuntimeStatsCollector::record_component_time(Component *component, uint32_t duration_ms, uint32_t current_time) {
if (!this->enabled_ || component == nullptr)
return;
const char *component_source = component->get_component_source();
this->component_stats_[component_source].record_time(duration_ms);
// If next_log_time_ is 0, initialize it
if (this->next_log_time_ == 0) {
this->next_log_time_ = current_time + this->log_interval_;
return;
}
if (current_time >= this->next_log_time_) {
this->log_stats_();
this->reset_stats_();
this->next_log_time_ = current_time + this->log_interval_;
}
}
} // namespace esphome

View File

@@ -0,0 +1,161 @@
#pragma once
#include <map>
#include <string>
#include <vector>
#include <cstdint>
#include <algorithm>
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
static const char *const RUNTIME_TAG = "runtime";
class Component; // Forward declaration
class ComponentRuntimeStats {
public:
ComponentRuntimeStats()
: period_count_(0),
total_count_(0),
period_time_ms_(0),
total_time_ms_(0),
period_max_time_ms_(0),
total_max_time_ms_(0) {}
void record_time(uint32_t duration_ms) {
// Update period counters
this->period_count_++;
this->period_time_ms_ += duration_ms;
if (duration_ms > this->period_max_time_ms_)
this->period_max_time_ms_ = duration_ms;
// Update total counters
this->total_count_++;
this->total_time_ms_ += duration_ms;
if (duration_ms > this->total_max_time_ms_)
this->total_max_time_ms_ = duration_ms;
}
void reset_period_stats() {
this->period_count_ = 0;
this->period_time_ms_ = 0;
this->period_max_time_ms_ = 0;
}
// Period stats (reset each logging interval)
uint32_t get_period_count() const { return this->period_count_; }
uint32_t get_period_time_ms() const { return this->period_time_ms_; }
uint32_t get_period_max_time_ms() const { return this->period_max_time_ms_; }
float get_period_avg_time_ms() const {
return this->period_count_ > 0 ? this->period_time_ms_ / static_cast<float>(this->period_count_) : 0.0f;
}
// Total stats (persistent until reboot)
uint32_t get_total_count() const { return this->total_count_; }
uint32_t get_total_time_ms() const { return this->total_time_ms_; }
uint32_t get_total_max_time_ms() const { return this->total_max_time_ms_; }
float get_total_avg_time_ms() const {
return this->total_count_ > 0 ? this->total_time_ms_ / static_cast<float>(this->total_count_) : 0.0f;
}
protected:
// Period stats (reset each logging interval)
uint32_t period_count_;
uint32_t period_time_ms_;
uint32_t period_max_time_ms_;
// Total stats (persistent until reboot)
uint32_t total_count_;
uint32_t total_time_ms_;
uint32_t total_max_time_ms_;
};
// For sorting components by run time
struct ComponentStatPair {
std::string name;
const ComponentRuntimeStats *stats;
bool operator>(const ComponentStatPair &other) const {
// Sort by period time as that's what we're displaying in the logs
return stats->get_period_time_ms() > other.stats->get_period_time_ms();
}
};
class RuntimeStatsCollector {
public:
RuntimeStatsCollector() : log_interval_(60000), next_log_time_(0), enabled_(true) {}
void set_log_interval(uint32_t log_interval) { this->log_interval_ = log_interval; }
uint32_t get_log_interval() const { return this->log_interval_; }
void set_enabled(bool enabled) { this->enabled_ = enabled; }
bool is_enabled() const { return this->enabled_; }
void record_component_time(Component *component, uint32_t duration_ms, uint32_t current_time);
protected:
void log_stats_() {
ESP_LOGI(RUNTIME_TAG, "Component Runtime Statistics");
ESP_LOGI(RUNTIME_TAG, "Period stats (last %" PRIu32 "ms):", this->log_interval_);
// First collect stats we want to display
std::vector<ComponentStatPair> stats_to_display;
for (const auto &it : this->component_stats_) {
const ComponentRuntimeStats &stats = it.second;
if (stats.get_period_count() > 0) {
ComponentStatPair pair = {it.first, &stats};
stats_to_display.push_back(pair);
}
}
// Sort by period runtime (descending)
std::sort(stats_to_display.begin(), stats_to_display.end(), std::greater<ComponentStatPair>());
// Log top components by period runtime
for (const auto &it : stats_to_display) {
const std::string &source = it.name;
const ComponentRuntimeStats *stats = it.stats;
ESP_LOGI(RUNTIME_TAG, " %s: count=%" PRIu32 ", avg=%.2fms, max=%" PRIu32 "ms, total=%" PRIu32 "ms",
source.c_str(), stats->get_period_count(), stats->get_period_avg_time_ms(),
stats->get_period_max_time_ms(), stats->get_period_time_ms());
}
// Log total stats since boot
ESP_LOGI(RUNTIME_TAG, "Total stats (since boot):");
// Re-sort by total runtime for all-time stats
std::sort(stats_to_display.begin(), stats_to_display.end(),
[](const ComponentStatPair &a, const ComponentStatPair &b) {
return a.stats->get_total_time_ms() > b.stats->get_total_time_ms();
});
for (const auto &it : stats_to_display) {
const std::string &source = it.name;
const ComponentRuntimeStats *stats = it.stats;
ESP_LOGI(RUNTIME_TAG, " %s: count=%" PRIu32 ", avg=%.2fms, max=%" PRIu32 "ms, total=%" PRIu32 "ms",
source.c_str(), stats->get_total_count(), stats->get_total_avg_time_ms(), stats->get_total_max_time_ms(),
stats->get_total_time_ms());
}
}
void reset_stats_() {
for (auto &it : this->component_stats_) {
it.second.reset_period_stats();
}
}
std::map<std::string, ComponentRuntimeStats> component_stats_;
uint32_t log_interval_;
uint32_t next_log_time_;
bool enabled_;
};
// Global instance for runtime stats collection
extern RuntimeStatsCollector runtime_stats;
} // namespace esphome

View File

@@ -608,17 +608,6 @@ def add_build_flag(build_flag: str):
CORE.add_build_flag(build_flag)
def add_build_unflag(build_unflag: str) -> None:
"""Add a global build unflag to the compiler flags."""
CORE.add_build_unflag(build_unflag)
def set_cpp_standard(standard: str) -> None:
"""Set C++ standard with compiler flag `-std={standard}`."""
CORE.add_build_unflag("-std=gnu++11")
CORE.add_build_flag(f"-std={standard}")
def add_define(name: str, value: SafeExpType = None):
"""Add a global define to the auto-generated defines.h file.

View File

@@ -67,6 +67,20 @@ esp8266:
"""
ESP32_CONFIG = """
esp32:
board: {board}
framework:
type: arduino
"""
ESP32S2_CONFIG = """
esp32:
board: {board}
framework:
type: esp-idf
"""
ESP32C3_CONFIG = """
esp32:
board: {board}
framework:
@@ -91,6 +105,8 @@ rtl87xx:
HARDWARE_BASE_CONFIGS = {
"ESP8266": ESP8266_CONFIG,
"ESP32": ESP32_CONFIG,
"ESP32S2": ESP32S2_CONFIG,
"ESP32C3": ESP32C3_CONFIG,
"RP2040": RP2040_CONFIG,
"BK72XX": BK72XX_CONFIG,
"RTL87XX": RTL87XX_CONFIG,

View File

@@ -153,9 +153,6 @@ def get_ini_content():
# Sort to avoid changing build flags order
CORE.add_platformio_option("build_flags", sorted(CORE.build_flags))
# Sort to avoid changing build unflags order
CORE.add_platformio_option("build_unflags", sorted(CORE.build_unflags))
content = "[platformio]\n"
content += f"description = ESPHome {__version__}\n"

View File

@@ -48,9 +48,6 @@ lib_deps =
lvgl/lvgl@8.4.0 ; lvgl
build_flags =
-DESPHOME_LOG_LEVEL=ESPHOME_LOG_LEVEL_VERY_VERBOSE
-std=gnu++17
build_unflags =
-std=gnu++11
src_filter =
+<./>
+<../tests/dummy_main.cpp>
@@ -76,8 +73,6 @@ lib_deps =
build_flags =
${common.build_flags}
-DUSE_ARDUINO
build_unflags =
${common.build_unflags}
; This are common settings for all IDF-framework based environments.
[common:idf]
@@ -85,8 +80,6 @@ extends = common
build_flags =
${common.build_flags}
-DUSE_ESP_IDF
build_unflags =
${common.build_unflags}
; This are common settings for the ESP8266 using Arduino.
[common:esp8266-arduino]
@@ -111,8 +104,6 @@ build_flags =
-Wno-nonnull-compare
-DUSE_ESP8266
-DUSE_ESP8266_FRAMEWORK_ARDUINO
build_unflags =
${common.build_unflags}
extra_scripts = post:esphome/components/esp8266/post_build.py.script
; This are common settings for the ESP32 (all variants) using Arduino.
@@ -144,8 +135,6 @@ build_flags =
-DUSE_ESP32
-DUSE_ESP32_FRAMEWORK_ARDUINO
-DAUDIO_NO_SD_FS ; i2s_audio
build_unflags =
${common.build_unflags}
extra_scripts = post:esphome/components/esp32/post_build.py.script
; This are common settings for the ESP32 (all variants) using IDF.
@@ -166,8 +155,6 @@ build_flags =
-Wno-nonnull-compare
-DUSE_ESP32
-DUSE_ESP32_FRAMEWORK_ESP_IDF
build_unflags =
${common.build_unflags}
extra_scripts = post:esphome/components/esp32/post_build.py.script
; This are common settings for the ESP32 using the latest ESP-IDF version.
@@ -194,8 +181,6 @@ build_flags =
${common:arduino.build_flags}
-DUSE_RP2040
-DUSE_RP2040_FRAMEWORK_ARDUINO
build_unflags =
${common.build_unflags}
; This are common settings for the LibreTiny (all variants) using Arduino.
[common:libretiny-arduino]
@@ -207,8 +192,6 @@ lib_deps =
build_flags =
${common:arduino.build_flags}
-DUSE_LIBRETINY
build_unflags =
${common.build_unflags}
build_src_flags = -include Arduino.h
; This is the common settings for the nRF52 using Zephyr.
@@ -241,8 +224,6 @@ board = nodemcuv2
build_flags =
${common:esp8266-arduino.build_flags}
${flags:runtime.build_flags}
build_unflags =
${common.build_unflags}
[env:esp8266-arduino-tidy]
extends = common:esp8266-arduino
@@ -250,8 +231,6 @@ board = nodemcuv2
build_flags =
${common:esp8266-arduino.build_flags}
${flags:clangtidy.build_flags}
build_unflags =
${common.build_unflags}
;;;;;;;; ESP32 ;;;;;;;;
@@ -263,8 +242,6 @@ build_flags =
${common:esp32-arduino.build_flags}
${flags:runtime.build_flags}
-DUSE_ESP32_VARIANT_ESP32
build_unflags =
${common.build_unflags}
[env:esp32-arduino-tidy]
extends = common:esp32-arduino
@@ -273,8 +250,6 @@ build_flags =
${common:esp32-arduino.build_flags}
${flags:clangtidy.build_flags}
-DUSE_ESP32_VARIANT_ESP32
build_unflags =
${common.build_unflags}
[env:esp32-idf]
extends = common:esp32-idf
@@ -284,8 +259,6 @@ build_flags =
${common:esp32-idf.build_flags}
${flags:runtime.build_flags}
-DUSE_ESP32_VARIANT_ESP32
build_unflags =
${common.build_unflags}
[env:esp32-idf-5_3]
extends = common:esp32-idf-5_3
@@ -295,8 +268,6 @@ build_flags =
${common:esp32-idf.build_flags}
${flags:runtime.build_flags}
-DUSE_ESP32_VARIANT_ESP32
build_unflags =
${common.build_unflags}
[env:esp32-idf-tidy]
extends = common:esp32-idf
@@ -306,8 +277,6 @@ build_flags =
${common:esp32-idf.build_flags}
${flags:clangtidy.build_flags}
-DUSE_ESP32_VARIANT_ESP32
build_unflags =
${common.build_unflags}
;;;;;;;; ESP32-C3 ;;;;;;;;
@@ -318,8 +287,6 @@ build_flags =
${common:esp32-arduino.build_flags}
${flags:runtime.build_flags}
-DUSE_ESP32_VARIANT_ESP32C3
build_unflags =
${common.build_unflags}
[env:esp32c3-arduino-tidy]
extends = common:esp32-arduino
@@ -328,8 +295,6 @@ build_flags =
${common:esp32-arduino.build_flags}
${flags:clangtidy.build_flags}
-DUSE_ESP32_VARIANT_ESP32C3
build_unflags =
${common.build_unflags}
[env:esp32c3-idf]
extends = common:esp32-idf
@@ -339,8 +304,6 @@ build_flags =
${common:esp32-idf.build_flags}
${flags:runtime.build_flags}
-DUSE_ESP32_VARIANT_ESP32C3
build_unflags =
${common.build_unflags}
[env:esp32c3-idf-5_3]
extends = common:esp32-idf-5_3
@@ -350,8 +313,6 @@ build_flags =
${common:esp32-idf.build_flags}
${flags:runtime.build_flags}
-DUSE_ESP32_VARIANT_ESP32C3
build_unflags =
${common.build_unflags}
[env:esp32c3-idf-tidy]
extends = common:esp32-idf
@@ -361,8 +322,6 @@ build_flags =
${common:esp32-idf.build_flags}
${flags:clangtidy.build_flags}
-DUSE_ESP32_VARIANT_ESP32C3
build_unflags =
${common.build_unflags}
;;;;;;;; ESP32-C6 ;;;;;;;;
@@ -384,8 +343,6 @@ build_flags =
${common:esp32-arduino.build_flags}
${flags:runtime.build_flags}
-DUSE_ESP32_VARIANT_ESP32S2
build_unflags =
${common.build_unflags}
[env:esp32s2-arduino-tidy]
extends = common:esp32-arduino
@@ -394,8 +351,6 @@ build_flags =
${common:esp32-arduino.build_flags}
${flags:clangtidy.build_flags}
-DUSE_ESP32_VARIANT_ESP32S2
build_unflags =
${common.build_unflags}
[env:esp32s2-idf]
extends = common:esp32-idf
@@ -405,8 +360,6 @@ build_flags =
${common:esp32-idf.build_flags}
${flags:runtime.build_flags}
-DUSE_ESP32_VARIANT_ESP32S2
build_unflags =
${common.build_unflags}
[env:esp32s2-idf-5_3]
extends = common:esp32-idf-5_3
@@ -416,8 +369,6 @@ build_flags =
${common:esp32-idf.build_flags}
${flags:runtime.build_flags}
-DUSE_ESP32_VARIANT_ESP32S2
build_unflags =
${common.build_unflags}
[env:esp32s2-idf-tidy]
extends = common:esp32-idf
@@ -427,8 +378,6 @@ build_flags =
${common:esp32-idf.build_flags}
${flags:clangtidy.build_flags}
-DUSE_ESP32_VARIANT_ESP32S2
build_unflags =
${common.build_unflags}
;;;;;;;; ESP32-S3 ;;;;;;;;
@@ -439,8 +388,6 @@ build_flags =
${common:esp32-arduino.build_flags}
${flags:runtime.build_flags}
-DUSE_ESP32_VARIANT_ESP32S3
build_unflags =
${common.build_unflags}
[env:esp32s3-arduino-tidy]
extends = common:esp32-arduino
@@ -449,8 +396,6 @@ build_flags =
${common:esp32-arduino.build_flags}
${flags:clangtidy.build_flags}
-DUSE_ESP32_VARIANT_ESP32S3
build_unflags =
${common.build_unflags}
[env:esp32s3-idf]
extends = common:esp32-idf
@@ -460,8 +405,6 @@ build_flags =
${common:esp32-idf.build_flags}
${flags:runtime.build_flags}
-DUSE_ESP32_VARIANT_ESP32S3
build_unflags =
${common.build_unflags}
[env:esp32s3-idf-5_3]
extends = common:esp32-idf-5_3
@@ -471,8 +414,6 @@ build_flags =
${common:esp32-idf.build_flags}
${flags:runtime.build_flags}
-DUSE_ESP32_VARIANT_ESP32S3
build_unflags =
${common.build_unflags}
[env:esp32s3-idf-tidy]
extends = common:esp32-idf
@@ -482,8 +423,6 @@ build_flags =
${common:esp32-idf.build_flags}
${flags:clangtidy.build_flags}
-DUSE_ESP32_VARIANT_ESP32S3
build_unflags =
${common.build_unflags}
;;;;;;;; ESP32-P4 ;;;;;;;;
@@ -505,8 +444,6 @@ board = rpipico
build_flags =
${common:rp2040-arduino.build_flags}
${flags:runtime.build_flags}
build_unflags =
${common.build_unflags}
;;;;;;;; LibreTiny ;;;;;;;;
@@ -518,8 +455,6 @@ build_flags =
${flags:runtime.build_flags}
-DUSE_BK72XX
-DUSE_LIBRETINY_VARIANT_BK7231N
build_unflags =
${common.build_unflags}
[env:rtl87xxb-arduino]
extends = common:libretiny-arduino
@@ -529,8 +464,6 @@ build_flags =
${flags:runtime.build_flags}
-DUSE_RTL87XX
-DUSE_LIBRETINY_VARIANT_RTL8710B
build_unflags =
${common.build_unflags}
[env:rtl87xxc-arduino]
extends = common:libretiny-arduino
@@ -540,8 +473,6 @@ build_flags =
${flags:runtime.build_flags}
-DUSE_RTL87XX
-DUSE_LIBRETINY_VARIANT_RTL8720C
build_unflags =
${common.build_unflags}
[env:host]
extends = common
@@ -552,8 +483,6 @@ build_flags =
${common.build_flags}
-DUSE_HOST
-std=c++17
build_unflags =
${common.build_unflags}
;;;;;;;; nRF52 ;;;;;;;;
@@ -563,8 +492,6 @@ board = adafruit_feather_nrf52840
build_flags =
${common:nrf52-zephyr.build_flags}
${flags:runtime.build_flags}
build_unflags =
${common.build_unflags}
[env:nrf52-tidy]
extends = common:nrf52-zephyr
@@ -572,5 +499,3 @@ board = adafruit_feather_nrf52840
build_flags =
${common:nrf52-zephyr.build_flags}
${flags:clangtidy.build_flags}
build_unflags =
${common.build_unflags}

View File

@@ -6,9 +6,9 @@ pre-commit
# Unit tests
pytest==8.4.0
pytest-cov==6.2.1
pytest-cov==6.1.1
pytest-mock==3.14.1
pytest-asyncio==1.0.0
pytest-asyncio==0.26.0
pytest-xdist==3.7.0
asyncmock==0.4.2
hypothesis==6.92.1