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:
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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};
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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}"
|
||||||
|
|||||||
Reference in New Issue
Block a user