diff --git a/esphome/components/api/__init__.py b/esphome/components/api/__init__.py index 485a55e493..e9cf0ad459 100644 --- a/esphome/components/api/__init__.py +++ b/esphome/components/api/__init__.py @@ -318,7 +318,11 @@ _OUTGOING_CONNECTION_SCHEMA = cv.Schema( { cv.Optional(CONF_HOST): cv.ipaddress, cv.Optional(CONF_PORT, default=6054): cv.port, - cv.Optional(CONF_DELAY, default="60s"): cv.positive_time_period_milliseconds, + # Bounded so the value cannot wrap the device's uint32 milliseconds + cv.Optional(CONF_DELAY, default="60s"): cv.All( + cv.positive_time_period_milliseconds, + cv.Range(max=cv.TimePeriod(milliseconds=4294967295)), + ), } ) diff --git a/esphome/components/api/api_outgoing_connection.cpp b/esphome/components/api/api_outgoing_connection.cpp index 3fb28ad43b..0970f3a205 100644 --- a/esphome/components/api/api_outgoing_connection.cpp +++ b/esphome/components/api/api_outgoing_connection.cpp @@ -262,10 +262,13 @@ void OutgoingConnectionManager::dump_config() const { if (host == nullptr) { host = "none remembered yet"; } + // The boot delay differs from delay: on deep sleep builds, so print the + // value that actually applies ESP_LOGCONFIG(TAG, " Outgoing connection port: %u\n" - " Outgoing connection host: %s", - API_OUTGOING_CONNECTION_PORT, host); + " Outgoing connection host: %s\n" + " Outgoing connection boot delay: %ums", + API_OUTGOING_CONNECTION_PORT, host, BOOT_WAIT_MS); } } // namespace esphome::api diff --git a/tests/component_tests/api/test_outgoing_connection.py b/tests/component_tests/api/test_outgoing_connection.py index c8040a2120..38e8199c63 100644 --- a/tests/component_tests/api/test_outgoing_connection.py +++ b/tests/component_tests/api/test_outgoing_connection.py @@ -63,6 +63,15 @@ def test_outgoing_connection_bare_block( assert outgoing["port"] == 6054 +def test_outgoing_connection_delay_bounded( + set_core_config: SetCoreConfigCallable, +) -> None: + """A delay past the device's uint32 milliseconds is rejected, not wrapped.""" + set_core_config(PlatformFramework.ESP32_IDF, platform_data=ESP32_PLATFORM_DATA) + with pytest.raises(cv.Invalid, match="value must be at most"): + CONFIG_SCHEMA(_api_config({"delay": "60d"})) + + def test_outgoing_connection_requires_encryption( set_core_config: SetCoreConfigCallable, ) -> None: