[core] Add new options suspend_loop and loop_interval (#18136)

Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
This commit is contained in:
luar123
2026-10-02 09:09:14 -05:00
committed by GitHub
co-authored by Jonathan Swoboda pre-commit-ci-lite[bot] J. Nick Koston
parent 4c9a44301d
commit 1440998f3c
15 changed files with 255 additions and 9 deletions
+2
View File
@@ -1,5 +1,7 @@
esphome:
debug_scheduler: true
loop_interval: 50ms
suspend_loop: false
platformio_options:
board_build.flash_mode: dio
build_flags:
+5 -1
View File
@@ -1 +1,5 @@
<<: !include common.yaml
packages:
base: !include common.yaml
esphome:
suspend_loop: true
@@ -1 +1,5 @@
<<: !include common.yaml
packages:
base: !include common.yaml
esphome:
suspend_loop: true
@@ -1 +1,5 @@
<<: !include common.yaml
packages:
base: !include common.yaml
esphome:
suspend_loop: true
+92
View File
@@ -26,6 +26,7 @@ from esphome.const import (
)
from esphome.core import CORE, KEY_CONTROLLER_REGISTRY_CONTROLLERS, config
from esphome.core.config import (
CONF_SUSPEND_LOOP,
Area,
make_app_name_cpp,
preload_core_config,
@@ -248,6 +249,97 @@ def test_area_id_collision(
assert "ID duplicate_id redefined! Check esphome->area->id." in captured.out
@pytest.mark.parametrize(
("fixture", "expected_platform"),
[
("suspend_loop_host.yaml", "host"),
("suspend_loop_rp2.yaml", "rp2"),
],
)
def test_suspend_loop_fail(
yaml_file: Callable[[str], str],
capsys: pytest.CaptureFixture[str],
fixture: str,
expected_platform: str,
) -> None:
"""Test that suspend_loop fails."""
result = load_config_from_fixture(yaml_file, fixture, FIXTURES_DIR)
assert result is None
# Check for the specific error message in stdout
captured = capsys.readouterr()
assert (
f"Suspend loop is not available on {expected_platform} platform" in captured.out
)
def test_loop_interval_warn_esp32(
yaml_file: Callable[[str], str],
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test that too high loop_interval prints warning."""
result = load_config_from_fixture(
yaml_file, "loop_interval_esp32.yaml", FIXTURES_DIR
)
assert result is not None
assert (
"loop_interval of 7s exceeds the 2400ms maximum sleep on this platform; the loop will still "
"wake every 2400ms. Raise esp32.watchdog_timeout to sleep longer."
in caplog.text
)
@pytest.mark.parametrize(
("fixture", "interval", "max_loop"),
[
("loop_interval_bk72xx.yaml", "5000ms", "4000"),
("loop_interval_nrf52.yaml", "700ms", "600"),
],
)
def test_loop_interval_warn(
yaml_file: Callable[[str], str],
caplog: pytest.LogCaptureFixture,
fixture: str,
interval: str,
max_loop: str,
) -> None:
"""Test that too high loop_interval prints warning."""
result = load_config_from_fixture(yaml_file, fixture, FIXTURES_DIR)
assert result is not None
assert (
f"loop_interval of {interval} exceeds the {max_loop}ms maximum sleep on this platform; the loop will still "
f"wake every {max_loop}ms." in caplog.text
)
@pytest.mark.asyncio
@pytest.mark.filterwarnings("ignore::RuntimeWarning")
async def test_suspend_loop_and_loop_interval(
yaml_file: Callable[[str], Path],
) -> None:
"""Test suspend_loop and loop_interval on esp32"""
result = load_config_from_fixture(
yaml_file, "suspend_loop_esp32.yaml", FIXTURES_DIR
)
assert result is not None
esphome_config = result["esphome"]
assert esphome_config.get(CONF_SUSPEND_LOOP)
with patch("esphome.core.config.cg") as mock_cg:
mock_cg.RawStatement.side_effect = lambda *args, **kwargs: MagicMock()
mock_cg.RawExpression.side_effect = lambda *args, **kwargs: MagicMock()
await config.to_code(result[CONF_ESPHOME])
mock_cg.add_define.assert_any_call("ESPHOME_SUSPEND_LOOP")
mock_cg.add_define.assert_any_call("ESPHOME_DEBUG_SCHEDULER")
mock_cg.App.set_loop_interval.assert_called_once_with(
cv.TimePeriodMilliseconds(milliseconds=50)
)
def test_device_without_area(yaml_file: Callable[[str], str]) -> None:
"""Test that devices without area_id work correctly."""
result = load_config_from_fixture(
@@ -0,0 +1,7 @@
esphome:
name: test
suspend_loop: true
loop_interval: 5000ms
bk72xx:
board: generic-bk7252
@@ -0,0 +1,7 @@
esphome:
name: test
loop_interval: 7s
esp32:
board: esp32dev
watchdog_timeout: 6s
@@ -0,0 +1,6 @@
esphome:
name: test
loop_interval: 700ms
nrf52:
board: adafruit_feather_nrf52840
@@ -0,0 +1,9 @@
esphome:
name: test
debug_scheduler: true
suspend_loop: true
loop_interval: 50ms
esp32:
board: esp32dev
watchdog_timeout: 6s
@@ -0,0 +1,5 @@
esphome:
name: test
suspend_loop: true
host:
@@ -0,0 +1,6 @@
esphome:
name: test
suspend_loop: true
rp2:
board: rpipicow