mirror of
https://github.com/esphome/esphome.git
synced 2026-02-02 17:57:36 -07:00
Compare commits
10 Commits
integratio
...
esp8266-lo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4267e5dda2 | ||
|
|
1119003eb5 | ||
|
|
c089d9aeac | ||
|
|
4f0894e970 | ||
|
|
848c237159 | ||
|
|
6892805094 | ||
|
|
bd3b7aa50a | ||
|
|
bce4a9c9ab | ||
|
|
aa8ccfc32b | ||
|
|
18991686ab |
@@ -4,6 +4,8 @@ from __future__ import annotations
|
||||
|
||||
from collections import defaultdict
|
||||
from collections.abc import Callable
|
||||
import heapq
|
||||
from operator import itemgetter
|
||||
import sys
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
@@ -29,6 +31,10 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
|
||||
)
|
||||
# Lower threshold for RAM symbols (RAM is more constrained)
|
||||
RAM_SYMBOL_SIZE_THRESHOLD: int = 24
|
||||
# Number of top symbols to show in the largest symbols report
|
||||
TOP_SYMBOLS_LIMIT: int = 30
|
||||
# Width for symbol name display in top symbols report
|
||||
COL_TOP_SYMBOL_NAME: int = 55
|
||||
|
||||
# Column width constants
|
||||
COL_COMPONENT: int = 29
|
||||
@@ -147,6 +153,37 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
|
||||
section_label = f" [{section[1:]}]" # .data -> [data], .bss -> [bss]
|
||||
return f"{demangled} ({size:,} B){section_label}"
|
||||
|
||||
def _add_top_symbols(self, lines: list[str]) -> None:
|
||||
"""Add a section showing the top largest symbols in the binary."""
|
||||
# Collect all symbols from all components: (symbol, demangled, size, section, component)
|
||||
all_symbols = [
|
||||
(symbol, demangled, size, section, component)
|
||||
for component, symbols in self._component_symbols.items()
|
||||
for symbol, demangled, size, section in symbols
|
||||
]
|
||||
|
||||
# Get top N symbols by size using heapq for efficiency
|
||||
top_symbols = heapq.nlargest(
|
||||
self.TOP_SYMBOLS_LIMIT, all_symbols, key=itemgetter(2)
|
||||
)
|
||||
|
||||
lines.append("")
|
||||
lines.append(f"Top {self.TOP_SYMBOLS_LIMIT} Largest Symbols:")
|
||||
# Calculate truncation limit from column width (leaving room for "...")
|
||||
truncate_limit = self.COL_TOP_SYMBOL_NAME - 3
|
||||
for i, (_, demangled, size, section, component) in enumerate(top_symbols):
|
||||
# Format section label
|
||||
section_label = f"[{section[1:]}]" if section else ""
|
||||
# Truncate demangled name if too long
|
||||
demangled_display = (
|
||||
f"{demangled[:truncate_limit]}..."
|
||||
if len(demangled) > self.COL_TOP_SYMBOL_NAME
|
||||
else demangled
|
||||
)
|
||||
lines.append(
|
||||
f"{i + 1:>2}. {size:>7,} B {section_label:<8} {demangled_display:<{self.COL_TOP_SYMBOL_NAME}} {component}"
|
||||
)
|
||||
|
||||
def generate_report(self, detailed: bool = False) -> str:
|
||||
"""Generate a formatted memory report."""
|
||||
components = sorted(
|
||||
@@ -248,6 +285,9 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
|
||||
"RAM",
|
||||
)
|
||||
|
||||
# Top largest symbols in the binary
|
||||
self._add_top_symbols(lines)
|
||||
|
||||
# Add ESPHome core detailed analysis if there are core symbols
|
||||
if self._esphome_core_symbols:
|
||||
self._add_section_header(lines, f"{_COMPONENT_CORE} Detailed Analysis")
|
||||
|
||||
@@ -45,6 +45,7 @@ service APIConnection {
|
||||
rpc time_command (TimeCommandRequest) returns (void) {}
|
||||
rpc update_command (UpdateCommandRequest) returns (void) {}
|
||||
rpc valve_command (ValveCommandRequest) returns (void) {}
|
||||
rpc water_heater_command (WaterHeaterCommandRequest) returns (void) {}
|
||||
|
||||
rpc subscribe_bluetooth_le_advertisements(SubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
|
||||
rpc bluetooth_device_request(BluetoothDeviceRequest) returns (void) {}
|
||||
|
||||
@@ -1385,7 +1385,7 @@ uint16_t APIConnection::try_send_water_heater_info(EntityBase *entity, APIConnec
|
||||
is_single);
|
||||
}
|
||||
|
||||
void APIConnection::on_water_heater_command_request(const WaterHeaterCommandRequest &msg) {
|
||||
void APIConnection::water_heater_command(const WaterHeaterCommandRequest &msg) {
|
||||
ENTITY_COMMAND_MAKE_CALL(water_heater::WaterHeater, water_heater, water_heater)
|
||||
if (msg.has_fields & enums::WATER_HEATER_COMMAND_HAS_MODE)
|
||||
call.set_mode(static_cast<water_heater::WaterHeaterMode>(msg.mode));
|
||||
|
||||
@@ -170,7 +170,7 @@ class APIConnection final : public APIServerConnection {
|
||||
|
||||
#ifdef USE_WATER_HEATER
|
||||
bool send_water_heater_state(water_heater::WaterHeater *water_heater);
|
||||
void on_water_heater_command_request(const WaterHeaterCommandRequest &msg) override;
|
||||
void water_heater_command(const WaterHeaterCommandRequest &msg) override;
|
||||
#endif
|
||||
|
||||
#ifdef USE_IR_RF
|
||||
|
||||
@@ -746,6 +746,11 @@ void APIServerConnection::on_update_command_request(const UpdateCommandRequest &
|
||||
#ifdef USE_VALVE
|
||||
void APIServerConnection::on_valve_command_request(const ValveCommandRequest &msg) { this->valve_command(msg); }
|
||||
#endif
|
||||
#ifdef USE_WATER_HEATER
|
||||
void APIServerConnection::on_water_heater_command_request(const WaterHeaterCommandRequest &msg) {
|
||||
this->water_heater_command(msg);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
|
||||
const SubscribeBluetoothLEAdvertisementsRequest &msg) {
|
||||
|
||||
@@ -303,6 +303,9 @@ class APIServerConnection : public APIServerConnectionBase {
|
||||
#ifdef USE_VALVE
|
||||
virtual void valve_command(const ValveCommandRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_WATER_HEATER
|
||||
virtual void water_heater_command(const WaterHeaterCommandRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
|
||||
#endif
|
||||
@@ -432,6 +435,9 @@ class APIServerConnection : public APIServerConnectionBase {
|
||||
#ifdef USE_VALVE
|
||||
void on_valve_command_request(const ValveCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_WATER_HEATER
|
||||
void on_water_heater_command_request(const WaterHeaterCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
||||
#endif
|
||||
|
||||
@@ -34,14 +34,29 @@ static const char *const ESP_HOSTED_VERSION_STR = STRINGIFY(ESP_HOSTED_VERSION_M
|
||||
ESP_HOSTED_VERSION_MINOR_1) "." STRINGIFY(ESP_HOSTED_VERSION_PATCH_1);
|
||||
|
||||
#ifdef USE_ESP32_HOSTED_HTTP_UPDATE
|
||||
// Parse an integer from str, advancing ptr past the number
|
||||
// Returns false if no digits were parsed
|
||||
static bool parse_int(const char *&ptr, int &value) {
|
||||
char *end;
|
||||
value = static_cast<int>(strtol(ptr, &end, 10));
|
||||
if (end == ptr)
|
||||
return false;
|
||||
ptr = end;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parse version string "major.minor.patch" into components
|
||||
// Returns true if parsing succeeded
|
||||
// Returns true if at least major.minor was parsed
|
||||
static bool parse_version(const std::string &version_str, int &major, int &minor, int &patch) {
|
||||
major = minor = patch = 0;
|
||||
if (sscanf(version_str.c_str(), "%d.%d.%d", &major, &minor, &patch) >= 2) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
const char *ptr = version_str.c_str();
|
||||
|
||||
if (!parse_int(ptr, major) || *ptr++ != '.' || !parse_int(ptr, minor))
|
||||
return false;
|
||||
if (*ptr == '.')
|
||||
parse_int(++ptr, patch);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Compare two versions, returns:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import logging
|
||||
|
||||
from esphome import pins
|
||||
from esphome import automation, pins
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.esp32 import (
|
||||
VARIANT_ESP32,
|
||||
@@ -35,6 +35,8 @@ from esphome.const import (
|
||||
CONF_MODE,
|
||||
CONF_MOSI_PIN,
|
||||
CONF_NUMBER,
|
||||
CONF_ON_CONNECT,
|
||||
CONF_ON_DISCONNECT,
|
||||
CONF_PAGE_ID,
|
||||
CONF_PIN,
|
||||
CONF_POLLING_INTERVAL,
|
||||
@@ -237,6 +239,8 @@ BASE_SCHEMA = cv.Schema(
|
||||
cv.Optional(CONF_DOMAIN, default=".local"): cv.domain_name,
|
||||
cv.Optional(CONF_USE_ADDRESS): cv.string_strict,
|
||||
cv.Optional(CONF_MAC_ADDRESS): cv.mac_address,
|
||||
cv.Optional(CONF_ON_CONNECT): automation.validate_automation(single=True),
|
||||
cv.Optional(CONF_ON_DISCONNECT): automation.validate_automation(single=True),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
@@ -430,6 +434,18 @@ async def to_code(config):
|
||||
if CORE.using_arduino:
|
||||
cg.add_library("WiFi", None)
|
||||
|
||||
if on_connect_config := config.get(CONF_ON_CONNECT):
|
||||
cg.add_define("USE_ETHERNET_CONNECT_TRIGGER")
|
||||
await automation.build_automation(
|
||||
var.get_connect_trigger(), [], on_connect_config
|
||||
)
|
||||
|
||||
if on_disconnect_config := config.get(CONF_ON_DISCONNECT):
|
||||
cg.add_define("USE_ETHERNET_DISCONNECT_TRIGGER")
|
||||
await automation.build_automation(
|
||||
var.get_disconnect_trigger(), [], on_disconnect_config
|
||||
)
|
||||
|
||||
CORE.add_job(final_step)
|
||||
|
||||
|
||||
|
||||
@@ -309,6 +309,9 @@ void EthernetComponent::loop() {
|
||||
|
||||
this->dump_connect_params_();
|
||||
this->status_clear_warning();
|
||||
#ifdef USE_ETHERNET_CONNECT_TRIGGER
|
||||
this->connect_trigger_.trigger();
|
||||
#endif
|
||||
} else if (now - this->connect_begin_ > 15000) {
|
||||
ESP_LOGW(TAG, "Connecting failed; reconnecting");
|
||||
this->start_connect_();
|
||||
@@ -318,10 +321,16 @@ void EthernetComponent::loop() {
|
||||
if (!this->started_) {
|
||||
ESP_LOGI(TAG, "Stopped connection");
|
||||
this->state_ = EthernetComponentState::STOPPED;
|
||||
#ifdef USE_ETHERNET_DISCONNECT_TRIGGER
|
||||
this->disconnect_trigger_.trigger();
|
||||
#endif
|
||||
} else if (!this->connected_) {
|
||||
ESP_LOGW(TAG, "Connection lost; reconnecting");
|
||||
this->state_ = EthernetComponentState::CONNECTING;
|
||||
this->start_connect_();
|
||||
#ifdef USE_ETHERNET_DISCONNECT_TRIGGER
|
||||
this->disconnect_trigger_.trigger();
|
||||
#endif
|
||||
} else {
|
||||
this->finish_connect_();
|
||||
// When connected and stable, disable the loop to save CPU cycles
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/network/ip_address.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
@@ -119,6 +120,12 @@ class EthernetComponent : public Component {
|
||||
void add_ip_state_listener(EthernetIPStateListener *listener) { this->ip_state_listeners_.push_back(listener); }
|
||||
#endif
|
||||
|
||||
#ifdef USE_ETHERNET_CONNECT_TRIGGER
|
||||
Trigger<> *get_connect_trigger() { return &this->connect_trigger_; }
|
||||
#endif
|
||||
#ifdef USE_ETHERNET_DISCONNECT_TRIGGER
|
||||
Trigger<> *get_disconnect_trigger() { return &this->disconnect_trigger_; }
|
||||
#endif
|
||||
protected:
|
||||
static void eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
|
||||
static void got_ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
|
||||
@@ -190,6 +197,12 @@ class EthernetComponent : public Component {
|
||||
StaticVector<EthernetIPStateListener *, ESPHOME_ETHERNET_IP_STATE_LISTENERS> ip_state_listeners_;
|
||||
#endif
|
||||
|
||||
#ifdef USE_ETHERNET_CONNECT_TRIGGER
|
||||
Trigger<> connect_trigger_;
|
||||
#endif
|
||||
#ifdef USE_ETHERNET_DISCONNECT_TRIGGER
|
||||
Trigger<> disconnect_trigger_;
|
||||
#endif
|
||||
private:
|
||||
// Stores a pointer to a string literal (static storage duration).
|
||||
// ONLY set from Python-generated code with string literals - never dynamic strings.
|
||||
|
||||
@@ -601,7 +601,8 @@ class Logger : public Component {
|
||||
// Updates buffer_at with the formatted length, handling truncation:
|
||||
// - When vsnprintf truncates (ret >= remaining), it writes (remaining - 1) chars + null terminator
|
||||
// - When it doesn't truncate (ret < remaining), it writes ret chars + null terminator
|
||||
static inline void HOT process_vsnprintf_result_(char *buffer, uint16_t *buffer_at, uint16_t remaining, int ret) {
|
||||
__attribute__((always_inline)) static inline void process_vsnprintf_result(const char *buffer, uint16_t *buffer_at,
|
||||
uint16_t remaining, int ret) {
|
||||
if (ret < 0)
|
||||
return; // Encoding error, do not increment buffer_at
|
||||
*buffer_at += (ret >= remaining) ? (remaining - 1) : static_cast<uint16_t>(ret);
|
||||
@@ -616,7 +617,7 @@ class Logger : public Component {
|
||||
if (*buffer_at >= buffer_size)
|
||||
return;
|
||||
const uint16_t remaining = buffer_size - *buffer_at;
|
||||
process_vsnprintf_result_(buffer, buffer_at, remaining, vsnprintf(buffer + *buffer_at, remaining, format, args));
|
||||
process_vsnprintf_result(buffer, buffer_at, remaining, vsnprintf(buffer + *buffer_at, remaining, format, args));
|
||||
}
|
||||
|
||||
#ifdef USE_STORE_LOG_STR_IN_FLASH
|
||||
@@ -626,7 +627,7 @@ class Logger : public Component {
|
||||
if (*buffer_at >= buffer_size)
|
||||
return;
|
||||
const uint16_t remaining = buffer_size - *buffer_at;
|
||||
process_vsnprintf_result_(buffer, buffer_at, remaining, vsnprintf_P(buffer + *buffer_at, remaining, format, args));
|
||||
process_vsnprintf_result(buffer, buffer_at, remaining, vsnprintf_P(buffer + *buffer_at, remaining, format, args));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -139,7 +139,8 @@ class MQTTBackendESP32 final : public MQTTBackend {
|
||||
this->lwt_retain_ = retain;
|
||||
}
|
||||
void set_server(network::IPAddress ip, uint16_t port) final {
|
||||
this->host_ = ip.str();
|
||||
char ip_buf[network::IP_ADDRESS_BUFFER_SIZE];
|
||||
this->host_ = ip.str_to(ip_buf);
|
||||
this->port_ = port;
|
||||
}
|
||||
void set_server(const char *host, uint16_t port) final {
|
||||
|
||||
@@ -62,7 +62,7 @@ class RealTimeClock : public PollingComponent {
|
||||
void apply_timezone_();
|
||||
#endif
|
||||
|
||||
CallbackManager<void()> time_sync_callback_;
|
||||
LazyCallbackManager<void()> time_sync_callback_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class TimeHasTimeCondition : public Condition<Ts...> {
|
||||
|
||||
@@ -359,6 +359,10 @@ void Component::defer(const std::string &name, std::function<void()> &&f) { //
|
||||
void Component::defer(const char *name, std::function<void()> &&f) { // NOLINT
|
||||
App.scheduler.set_timeout(this, name, 0, std::move(f));
|
||||
}
|
||||
void Component::defer(uint32_t id, std::function<void()> &&f) { // NOLINT
|
||||
App.scheduler.set_timeout(this, id, 0, std::move(f));
|
||||
}
|
||||
bool Component::cancel_defer(uint32_t id) { return App.scheduler.cancel_timeout(this, id); }
|
||||
void Component::set_timeout(uint32_t timeout, std::function<void()> &&f) { // NOLINT
|
||||
App.scheduler.set_timeout(this, static_cast<const char *>(nullptr), timeout, std::move(f));
|
||||
}
|
||||
|
||||
@@ -494,11 +494,15 @@ class Component {
|
||||
/// Defer a callback to the next loop() call.
|
||||
void defer(std::function<void()> &&f); // NOLINT
|
||||
|
||||
/// Defer a callback with a numeric ID (zero heap allocation)
|
||||
void defer(uint32_t id, std::function<void()> &&f); // NOLINT
|
||||
|
||||
/// Cancel a defer callback using the specified name, name must not be empty.
|
||||
// Remove before 2026.7.0
|
||||
ESPDEPRECATED("Use const char* overload instead. Removed in 2026.7.0", "2026.1.0")
|
||||
bool cancel_defer(const std::string &name); // NOLINT
|
||||
bool cancel_defer(const char *name); // NOLINT
|
||||
bool cancel_defer(uint32_t id); // NOLINT
|
||||
|
||||
// Ordered for optimal packing on 32-bit systems
|
||||
const LogString *component_source_{nullptr};
|
||||
|
||||
@@ -240,6 +240,8 @@
|
||||
#define USE_ETHERNET_KSZ8081
|
||||
#define USE_ETHERNET_MANUAL_IP
|
||||
#define USE_ETHERNET_IP_STATE_LISTENERS
|
||||
#define USE_ETHERNET_CONNECT_TRIGGER
|
||||
#define USE_ETHERNET_DISCONNECT_TRIGGER
|
||||
#define ESPHOME_ETHERNET_IP_STATE_LISTENERS 2
|
||||
#endif
|
||||
|
||||
|
||||
@@ -13,3 +13,7 @@ ethernet:
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
||||
mac_address: "02:AA:BB:CC:DD:01"
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
|
||||
@@ -13,3 +13,7 @@ ethernet:
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
||||
mac_address: "02:AA:BB:CC:DD:01"
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
|
||||
@@ -13,3 +13,7 @@ ethernet:
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
||||
mac_address: "02:AA:BB:CC:DD:01"
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
|
||||
@@ -13,3 +13,7 @@ ethernet:
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
||||
mac_address: "02:AA:BB:CC:DD:01"
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
|
||||
@@ -13,3 +13,7 @@ ethernet:
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
||||
mac_address: "02:AA:BB:CC:DD:01"
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
|
||||
@@ -13,3 +13,7 @@ ethernet:
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
||||
mac_address: "02:AA:BB:CC:DD:01"
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
|
||||
@@ -12,3 +12,7 @@ ethernet:
|
||||
gateway: 192.168.178.1
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
|
||||
@@ -13,3 +13,7 @@ ethernet:
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
||||
mac_address: "02:AA:BB:CC:DD:01"
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
ethernet:
|
||||
type: OPENETH
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
|
||||
@@ -13,3 +13,7 @@ ethernet:
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
||||
mac_address: "02:AA:BB:CC:DD:01"
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
|
||||
@@ -13,3 +13,7 @@ ethernet:
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
||||
mac_address: "02:AA:BB:CC:DD:01"
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
|
||||
@@ -20,6 +20,9 @@ globals:
|
||||
- id: retry_counter
|
||||
type: int
|
||||
initial_value: '0'
|
||||
- id: defer_counter
|
||||
type: int
|
||||
initial_value: '0'
|
||||
- id: tests_done
|
||||
type: bool
|
||||
initial_value: 'false'
|
||||
@@ -136,11 +139,49 @@ script:
|
||||
App.scheduler.cancel_retry(component1, 6002U);
|
||||
ESP_LOGI("test", "Cancelled numeric retry 6002");
|
||||
|
||||
// Test 12: defer with numeric ID (Component method)
|
||||
class TestDeferComponent : public Component {
|
||||
public:
|
||||
void test_defer_methods() {
|
||||
// Test defer with uint32_t ID - should execute on next loop
|
||||
this->defer(7001U, []() {
|
||||
ESP_LOGI("test", "Component numeric defer 7001 fired");
|
||||
id(defer_counter) += 1;
|
||||
});
|
||||
|
||||
// Test another defer with numeric ID
|
||||
this->defer(7002U, []() {
|
||||
ESP_LOGI("test", "Component numeric defer 7002 fired");
|
||||
id(defer_counter) += 1;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
static TestDeferComponent test_defer_component;
|
||||
test_defer_component.test_defer_methods();
|
||||
|
||||
// Test 13: cancel_defer with numeric ID (Component method)
|
||||
class TestCancelDeferComponent : public Component {
|
||||
public:
|
||||
void test_cancel_defer() {
|
||||
// Set a defer that should be cancelled
|
||||
this->defer(8001U, []() {
|
||||
ESP_LOGE("test", "ERROR: Numeric defer 8001 should have been cancelled");
|
||||
});
|
||||
// Cancel it immediately
|
||||
bool cancelled = this->cancel_defer(8001U);
|
||||
ESP_LOGI("test", "Cancelled numeric defer 8001: %s", cancelled ? "true" : "false");
|
||||
}
|
||||
};
|
||||
|
||||
static TestCancelDeferComponent test_cancel_defer_component;
|
||||
test_cancel_defer_component.test_cancel_defer();
|
||||
|
||||
- id: report_results
|
||||
then:
|
||||
- lambda: |-
|
||||
ESP_LOGI("test", "Final results - Timeouts: %d, Intervals: %d, Retries: %d",
|
||||
id(timeout_counter), id(interval_counter), id(retry_counter));
|
||||
ESP_LOGI("test", "Final results - Timeouts: %d, Intervals: %d, Retries: %d, Defers: %d",
|
||||
id(timeout_counter), id(interval_counter), id(retry_counter), id(defer_counter));
|
||||
|
||||
sensor:
|
||||
- platform: template
|
||||
|
||||
@@ -19,6 +19,7 @@ async def test_scheduler_numeric_id_test(
|
||||
timeout_count = 0
|
||||
interval_count = 0
|
||||
retry_count = 0
|
||||
defer_count = 0
|
||||
|
||||
# Events for each test completion
|
||||
numeric_timeout_1001_fired = asyncio.Event()
|
||||
@@ -33,6 +34,9 @@ async def test_scheduler_numeric_id_test(
|
||||
max_id_timeout_fired = asyncio.Event()
|
||||
numeric_retry_done = asyncio.Event()
|
||||
numeric_retry_cancelled = asyncio.Event()
|
||||
numeric_defer_7001_fired = asyncio.Event()
|
||||
numeric_defer_7002_fired = asyncio.Event()
|
||||
numeric_defer_cancelled = asyncio.Event()
|
||||
final_results_logged = asyncio.Event()
|
||||
|
||||
# Track interval counts
|
||||
@@ -40,7 +44,7 @@ async def test_scheduler_numeric_id_test(
|
||||
numeric_retry_count = 0
|
||||
|
||||
def on_log_line(line: str) -> None:
|
||||
nonlocal timeout_count, interval_count, retry_count
|
||||
nonlocal timeout_count, interval_count, retry_count, defer_count
|
||||
nonlocal numeric_interval_count, numeric_retry_count
|
||||
|
||||
# Strip ANSI color codes
|
||||
@@ -105,15 +109,27 @@ async def test_scheduler_numeric_id_test(
|
||||
elif "Cancelled numeric retry 6002" in clean_line:
|
||||
numeric_retry_cancelled.set()
|
||||
|
||||
# Check for numeric defer tests
|
||||
elif "Component numeric defer 7001 fired" in clean_line:
|
||||
numeric_defer_7001_fired.set()
|
||||
|
||||
elif "Component numeric defer 7002 fired" in clean_line:
|
||||
numeric_defer_7002_fired.set()
|
||||
|
||||
elif "Cancelled numeric defer 8001: true" in clean_line:
|
||||
numeric_defer_cancelled.set()
|
||||
|
||||
# Check for final results
|
||||
elif "Final results" in clean_line:
|
||||
match = re.search(
|
||||
r"Timeouts: (\d+), Intervals: (\d+), Retries: (\d+)", clean_line
|
||||
r"Timeouts: (\d+), Intervals: (\d+), Retries: (\d+), Defers: (\d+)",
|
||||
clean_line,
|
||||
)
|
||||
if match:
|
||||
timeout_count = int(match.group(1))
|
||||
interval_count = int(match.group(2))
|
||||
retry_count = int(match.group(3))
|
||||
defer_count = int(match.group(4))
|
||||
final_results_logged.set()
|
||||
|
||||
async with (
|
||||
@@ -201,6 +217,23 @@ async def test_scheduler_numeric_id_test(
|
||||
"Numeric retry 6002 should have been cancelled"
|
||||
)
|
||||
|
||||
# Wait for numeric defer tests
|
||||
try:
|
||||
await asyncio.wait_for(numeric_defer_7001_fired.wait(), timeout=0.5)
|
||||
except TimeoutError:
|
||||
pytest.fail("Numeric defer 7001 did not fire within 0.5 seconds")
|
||||
|
||||
try:
|
||||
await asyncio.wait_for(numeric_defer_7002_fired.wait(), timeout=0.5)
|
||||
except TimeoutError:
|
||||
pytest.fail("Numeric defer 7002 did not fire within 0.5 seconds")
|
||||
|
||||
# Verify numeric defer was cancelled
|
||||
try:
|
||||
await asyncio.wait_for(numeric_defer_cancelled.wait(), timeout=0.5)
|
||||
except TimeoutError:
|
||||
pytest.fail("Numeric defer 8001 cancel confirmation not received")
|
||||
|
||||
# Wait for final results
|
||||
try:
|
||||
await asyncio.wait_for(final_results_logged.wait(), timeout=3.0)
|
||||
@@ -215,3 +248,4 @@ async def test_scheduler_numeric_id_test(
|
||||
assert retry_count >= 2, (
|
||||
f"Expected at least 2 retry attempts, got {retry_count}"
|
||||
)
|
||||
assert defer_count >= 2, f"Expected at least 2 defer fires, got {defer_count}"
|
||||
|
||||
Reference in New Issue
Block a user