mirror of
https://github.com/esphome/esphome.git
synced 2026-08-24 15:16:20 +00:00
[wifi] Warn when an AP only config serves the hosted web_server page
This commit is contained in:
@@ -41,6 +41,7 @@ from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_IDENTITY,
|
||||
CONF_KEY,
|
||||
CONF_LOCAL,
|
||||
CONF_MANUAL_IP,
|
||||
CONF_NETWORKS,
|
||||
CONF_ON_CONNECT,
|
||||
@@ -343,24 +344,38 @@ def _apply_min_auth_mode_default(config):
|
||||
return config
|
||||
|
||||
|
||||
def final_validate(config):
|
||||
has_sta = bool(config.get(CONF_NETWORKS, True))
|
||||
def final_validate(config: ConfigType) -> None:
|
||||
has_sta = bool(config.get(CONF_NETWORKS))
|
||||
has_ap = CONF_AP in config
|
||||
full_config = fv.full_config.get()
|
||||
has_improv = "esp32_improv" in full_config
|
||||
has_improv_serial = "improv_serial" in full_config
|
||||
has_captive_portal = "captive_portal" in full_config
|
||||
has_web_server = "web_server" in full_config
|
||||
web_server = full_config.get("web_server")
|
||||
if not (has_sta or has_ap or has_improv or has_improv_serial):
|
||||
raise cv.Invalid(
|
||||
"Please specify at least an SSID or an Access Point to create."
|
||||
)
|
||||
if has_ap and not has_captive_portal and not has_web_server:
|
||||
if has_ap and not has_captive_portal and web_server is None:
|
||||
_LOGGER.warning(
|
||||
"WiFi AP is configured but neither captive_portal nor web_server is enabled. "
|
||||
"The AP will not be usable for configuration or monitoring. "
|
||||
"Add 'captive_portal:' or 'web_server:' to your configuration."
|
||||
)
|
||||
elif (
|
||||
has_ap
|
||||
and not has_sta
|
||||
and not has_captive_portal
|
||||
and web_server is not None
|
||||
and not web_server.get(CONF_LOCAL)
|
||||
):
|
||||
# AP only: the device is reachable only through its own AP, where browsers
|
||||
# usually have no internet to download the hosted web_server interface.
|
||||
_LOGGER.warning(
|
||||
"WiFi is AP only and web_server loads its interface from the internet, which "
|
||||
"clients of the AP usually cannot reach, so the page stays blank. "
|
||||
"Set 'local: true' under 'web_server:' or add 'captive_portal:'."
|
||||
)
|
||||
|
||||
|
||||
def _consume_wifi_sockets(config: ConfigType) -> ConfigType:
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
"""Tests for WiFi component public helpers."""
|
||||
|
||||
import logging
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome.components.esp32 import const
|
||||
from esphome.components.wifi import (
|
||||
check_placeholder_credentials,
|
||||
final_validate,
|
||||
has_native_wifi,
|
||||
variant_has_wifi,
|
||||
)
|
||||
@@ -17,6 +20,7 @@ from esphome.const import (
|
||||
Platform,
|
||||
)
|
||||
from esphome.core import EsphomeError, Lambda
|
||||
import esphome.final_validate as fv
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -197,3 +201,32 @@ def test_check_placeholder_credentials_skips_template_ssid() -> None:
|
||||
"""A templated (Lambda) SSID is not a string and is skipped."""
|
||||
config = _wifi_config(networks=[{CONF_SSID: Lambda('return "x";')}])
|
||||
assert check_placeholder_credentials(config) is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("wifi_config", "full_config", "expect_warning"),
|
||||
[
|
||||
# AP only with a hosted web_server page: warn.
|
||||
({CONF_AP: {}}, {"web_server": {}}, True),
|
||||
# local: true serves the page from the device.
|
||||
({CONF_AP: {}}, {"web_server": {"local": True}}, False),
|
||||
# captive_portal serves its own local page.
|
||||
({CONF_AP: {}}, {"web_server": {}, "captive_portal": {}}, False),
|
||||
# STA with AP fallback: the AP is rarely used, stay quiet.
|
||||
({CONF_AP: {}, CONF_NETWORKS: [{CONF_SSID: "x"}]}, {"web_server": {}}, False),
|
||||
],
|
||||
)
|
||||
def test_final_validate_ap_only_hosted_web_server_warning(
|
||||
wifi_config: dict,
|
||||
full_config: dict,
|
||||
expect_warning: bool,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""AP only configs with a hosted web_server page get a hint to use local: true."""
|
||||
token = fv.full_config.set({"wifi": wifi_config, **full_config})
|
||||
try:
|
||||
with caplog.at_level(logging.WARNING):
|
||||
final_validate(wifi_config)
|
||||
finally:
|
||||
fv.full_config.reset(token)
|
||||
assert ("AP only" in caplog.text) is expect_warning
|
||||
|
||||
Reference in New Issue
Block a user