From adc8671b2e89caa3ea740b2072aec89e3d75e3a0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 5 Sep 2026 17:40:20 +0200 Subject: [PATCH] Warn for prometheus too, stub the runtime key methods under a yaml key, build the offer on esp32 idf --- esphome/components/api/api_server.h | 11 +++++++++++ esphome/components/esphome/ota/__init__.py | 6 +++--- esphome/espota2.py | 1 + tests/component_tests/ota/test_esphome_ota.py | 9 +++++---- .../components/ota/test-api_key_offer.esp32-idf.yaml | 2 ++ 5 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 tests/components/ota/test-api_key_offer.esp32-idf.yaml diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index 50084ee502..c8dd10d15d 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -79,6 +79,17 @@ class APIServer final : public Component, #ifndef USE_API_NOISE_PSK_FROM_YAML bool save_noise_psk(noise::psk_t psk, bool make_active = true); bool clear_noise_psk(bool make_active = true); +#else + // Stubs so a lambda calling these with a yaml key gets a clear error; only + // fire if instantiated + template bool save_noise_psk(noise::psk_t, bool = true) { + static_assert(B, "save_noise_psk() needs a runtime provisioned key; remove the 'key' from 'api: encryption:'"); + return false; + } + template bool clear_noise_psk(bool = true) { + static_assert(B, "clear_noise_psk() needs a runtime provisioned key; remove the 'key' from 'api: encryption:'"); + return false; + } #endif /// psk points at 32 bytes that live in flash for the life of the program void set_noise_psk(const uint8_t *psk) { this->noise_ctx_.set_psk(psk); } diff --git a/esphome/components/esphome/ota/__init__.py b/esphome/components/esphome/ota/__init__.py index 35d65423f6..f5eb878260 100644 --- a/esphome/components/esphome/ota/__init__.py +++ b/esphome/components/esphome/ota/__init__.py @@ -165,10 +165,10 @@ def ota_esphome_final_validate(config: ConfigType) -> None: CONF_OTA, CONF_PASSWORD, ) - # Only the web_server component keeps the listener up; the captive - # portal's copy is the recovery path + # web_server and prometheus keep the shared listener up; the captive + # portal's copy only exists on the fallback AP and is the recovery path if ( - CONF_WEB_SERVER in full_conf + (CONF_WEB_SERVER in full_conf or "prometheus" in full_conf) and any(conf.get(CONF_PLATFORM) == CONF_WEB_SERVER for conf in full_ota_conf) and any( CONF_ENCRYPTION in conf diff --git a/esphome/espota2.py b/esphome/espota2.py index 68e5d50cab..ce403c398d 100644 --- a/esphome/espota2.py +++ b/esphome/espota2.py @@ -240,6 +240,7 @@ class _EncryptionAttempt: self.plaintext_fallback = False +# Remove before 2027.3.0: only the fallback decision needs this distinction class OTAHandshakeNetworkError(OTANetworkError): """A transport failure inside the noise handshake; retrying encrypted may succeed.""" diff --git a/tests/component_tests/ota/test_esphome_ota.py b/tests/component_tests/ota/test_esphome_ota.py index ab4c639a54..d3092294dc 100644 --- a/tests/component_tests/ota/test_esphome_ota.py +++ b/tests/component_tests/ota/test_esphome_ota.py @@ -266,13 +266,14 @@ def test_encryption_explicit_key_with_runtime_provisioned_api_accepted() -> None fv.full_config.reset(token) +@pytest.mark.parametrize("component", ["web_server", "prometheus"]) def test_encryption_with_web_server_ota_warns( - caplog: pytest.LogCaptureFixture, + caplog: pytest.LogCaptureFixture, component: str ) -> None: - """With the web_server component the plaintext /update endpoint is always - on; the combination validates with a warning.""" + """web_server and prometheus keep the shared listener up, so the + plaintext /update endpoint is always on and the combination warns.""" full_conf = { - "web_server": {}, + component: {}, CONF_OTA: [ _make_ota_config(port=3232, **{CONF_ENCRYPTION: {CONF_KEY: OTHER_KEY}}), {CONF_PLATFORM: "web_server", CONF_ID: ID("ota_ws", is_manual=False)}, diff --git a/tests/components/ota/test-api_key_offer.esp32-idf.yaml b/tests/components/ota/test-api_key_offer.esp32-idf.yaml new file mode 100644 index 0000000000..ecda625521 --- /dev/null +++ b/tests/components/ota/test-api_key_offer.esp32-idf.yaml @@ -0,0 +1,2 @@ +packages: + ota: !include api_key_offer.yaml