1
0
mirror of https://github.com/esphome/esphome.git synced 2026-02-08 00:31:58 +00:00

[core] Add missing uint32_t ID overloads for defer() and cancel_defer() (#13720)

This commit is contained in:
J. Nick Koston
2026-02-02 22:22:11 +01:00
committed by GitHub
parent c089d9aeac
commit 1119003eb5
4 changed files with 87 additions and 4 deletions

View File

@@ -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 void Component::defer(const char *name, std::function<void()> &&f) { // NOLINT
App.scheduler.set_timeout(this, name, 0, std::move(f)); 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 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)); App.scheduler.set_timeout(this, static_cast<const char *>(nullptr), timeout, std::move(f));
} }

View File

@@ -494,11 +494,15 @@ class Component {
/// Defer a callback to the next loop() call. /// Defer a callback to the next loop() call.
void defer(std::function<void()> &&f); // NOLINT 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. /// Cancel a defer callback using the specified name, name must not be empty.
// Remove before 2026.7.0 // Remove before 2026.7.0
ESPDEPRECATED("Use const char* overload instead. Removed in 2026.7.0", "2026.1.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 std::string &name); // NOLINT
bool cancel_defer(const char *name); // NOLINT bool cancel_defer(const char *name); // NOLINT
bool cancel_defer(uint32_t id); // NOLINT
// Ordered for optimal packing on 32-bit systems // Ordered for optimal packing on 32-bit systems
const LogString *component_source_{nullptr}; const LogString *component_source_{nullptr};

View File

@@ -20,6 +20,9 @@ globals:
- id: retry_counter - id: retry_counter
type: int type: int
initial_value: '0' initial_value: '0'
- id: defer_counter
type: int
initial_value: '0'
- id: tests_done - id: tests_done
type: bool type: bool
initial_value: 'false' initial_value: 'false'
@@ -136,11 +139,49 @@ script:
App.scheduler.cancel_retry(component1, 6002U); App.scheduler.cancel_retry(component1, 6002U);
ESP_LOGI("test", "Cancelled numeric retry 6002"); 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 - id: report_results
then: then:
- lambda: |- - lambda: |-
ESP_LOGI("test", "Final results - Timeouts: %d, Intervals: %d, Retries: %d", ESP_LOGI("test", "Final results - Timeouts: %d, Intervals: %d, Retries: %d, Defers: %d",
id(timeout_counter), id(interval_counter), id(retry_counter)); id(timeout_counter), id(interval_counter), id(retry_counter), id(defer_counter));
sensor: sensor:
- platform: template - platform: template

View File

@@ -19,6 +19,7 @@ async def test_scheduler_numeric_id_test(
timeout_count = 0 timeout_count = 0
interval_count = 0 interval_count = 0
retry_count = 0 retry_count = 0
defer_count = 0
# Events for each test completion # Events for each test completion
numeric_timeout_1001_fired = asyncio.Event() numeric_timeout_1001_fired = asyncio.Event()
@@ -33,6 +34,9 @@ async def test_scheduler_numeric_id_test(
max_id_timeout_fired = asyncio.Event() max_id_timeout_fired = asyncio.Event()
numeric_retry_done = asyncio.Event() numeric_retry_done = asyncio.Event()
numeric_retry_cancelled = 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() final_results_logged = asyncio.Event()
# Track interval counts # Track interval counts
@@ -40,7 +44,7 @@ async def test_scheduler_numeric_id_test(
numeric_retry_count = 0 numeric_retry_count = 0
def on_log_line(line: str) -> None: 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 nonlocal numeric_interval_count, numeric_retry_count
# Strip ANSI color codes # Strip ANSI color codes
@@ -105,15 +109,27 @@ async def test_scheduler_numeric_id_test(
elif "Cancelled numeric retry 6002" in clean_line: elif "Cancelled numeric retry 6002" in clean_line:
numeric_retry_cancelled.set() 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 # Check for final results
elif "Final results" in clean_line: elif "Final results" in clean_line:
match = re.search( 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: if match:
timeout_count = int(match.group(1)) timeout_count = int(match.group(1))
interval_count = int(match.group(2)) interval_count = int(match.group(2))
retry_count = int(match.group(3)) retry_count = int(match.group(3))
defer_count = int(match.group(4))
final_results_logged.set() final_results_logged.set()
async with ( async with (
@@ -201,6 +217,23 @@ async def test_scheduler_numeric_id_test(
"Numeric retry 6002 should have been cancelled" "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 # Wait for final results
try: try:
await asyncio.wait_for(final_results_logged.wait(), timeout=3.0) 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, ( assert retry_count >= 2, (
f"Expected at least 2 retry attempts, got {retry_count}" f"Expected at least 2 retry attempts, got {retry_count}"
) )
assert defer_count >= 2, f"Expected at least 2 defer fires, got {defer_count}"