From d02da3c8dede540dba7415b2d08ea90df9b9e849 Mon Sep 17 00:00:00 2001 From: Brandon Harvey <8107750+bharvey88@users.noreply.github.com> Date: Sat, 17 Oct 2026 14:54:55 -0500 Subject: [PATCH] [wifi] Support hidden option in single-network shorthand (#18157) Co-authored-by: J. Nick Koston --- esphome/components/wifi/__init__.py | 11 +++- tests/component_tests/wifi/test_wifi.py | 51 +++++++++++++++++++ .../validate-hidden-shorthand.esp32-idf.yaml | 5 ++ 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 tests/component_tests/wifi/test_wifi.py create mode 100644 tests/components/wifi/validate-hidden-shorthand.esp32-idf.yaml diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index 5fceb891573..5660b1189b8 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -515,10 +515,16 @@ FINAL_VALIDATE_SCHEMA = cv.All( ) -def _validate(config): +def _validate(config: ConfigType) -> ConfigType: if CONF_PASSWORD in config and CONF_SSID not in config: raise cv.Invalid("Cannot have WiFi password without SSID!") + if CONF_HIDDEN in config and CONF_SSID not in config: + raise cv.Invalid( + "Cannot have 'hidden' without 'ssid'. If you are using the 'networks' " + "list, put 'hidden' inside the network entry instead." + ) + if CONF_SSID in config: # Automatically move single network to 'networks' section config = config.copy() @@ -527,6 +533,8 @@ def _validate(config): network[CONF_PASSWORD] = config.pop(CONF_PASSWORD) if CONF_EAP in config: network[CONF_EAP] = config.pop(CONF_EAP) + if CONF_HIDDEN in config: + network[CONF_HIDDEN] = config.pop(CONF_HIDDEN) if CONF_NETWORKS in config: # In testing mode, merged component tests may have both ssid and networks # Just use the networks list and ignore the single ssid @@ -643,6 +651,7 @@ CONFIG_SCHEMA = cv.All( ), cv.Optional(CONF_SSID): cv.sensitive(cv.ssid), cv.Optional(CONF_PASSWORD): cv.sensitive(validate_password), + cv.Optional(CONF_HIDDEN): cv.boolean, cv.Optional( CONF_MANUAL_IP, visibility=cv.Visibility.ADVANCED ): STA_MANUAL_IP_SCHEMA, diff --git a/tests/component_tests/wifi/test_wifi.py b/tests/component_tests/wifi/test_wifi.py new file mode 100644 index 00000000000..3513ee88a69 --- /dev/null +++ b/tests/component_tests/wifi/test_wifi.py @@ -0,0 +1,51 @@ +"""Config-validation tests for the wifi component.""" + +import pytest +from voluptuous import Invalid + +from esphome.components.wifi import _validate +from esphome.const import ( + CONF_HIDDEN, + CONF_NETWORKS, + CONF_PASSWORD, + CONF_SSID, + CONF_USE_ADDRESS, +) + + +def test_hidden_folded_into_networks() -> None: + """The shorthand 'hidden' option is moved into the generated networks entry.""" + config = _validate( + { + CONF_SSID: "MyHomeNetwork", + CONF_PASSWORD: "password1", + CONF_HIDDEN: True, + CONF_USE_ADDRESS: "test.local", + } + ) + assert CONF_SSID not in config + assert CONF_PASSWORD not in config + assert CONF_HIDDEN not in config + networks = config[CONF_NETWORKS] + assert len(networks) == 1 + assert networks[0][CONF_SSID] == "MyHomeNetwork" + assert networks[0][CONF_PASSWORD] == "password1" + assert networks[0][CONF_HIDDEN] is True + + +def test_shorthand_without_hidden_has_no_hidden_key() -> None: + """No 'hidden' key is set on the networks entry unless explicitly configured.""" + config = _validate( + { + CONF_SSID: "MyHomeNetwork", + CONF_PASSWORD: "password1", + CONF_USE_ADDRESS: "test.local", + } + ) + assert CONF_HIDDEN not in config[CONF_NETWORKS][0] + + +def test_hidden_without_ssid_rejected() -> None: + """A 'hidden' option without an SSID has nothing to apply to and is rejected.""" + with pytest.raises(Invalid, match="hidden"): + _validate({CONF_HIDDEN: True, CONF_USE_ADDRESS: "test.local"}) diff --git a/tests/components/wifi/validate-hidden-shorthand.esp32-idf.yaml b/tests/components/wifi/validate-hidden-shorthand.esp32-idf.yaml new file mode 100644 index 00000000000..8482c4e2022 --- /dev/null +++ b/tests/components/wifi/validate-hidden-shorthand.esp32-idf.yaml @@ -0,0 +1,5 @@ +# Exercises the hidden option in the single-network ssid/password shorthand. +wifi: + ssid: MyHiddenNetwork + password: password1 + hidden: true