diff --git a/esphome/components/web_server/__init__.py b/esphome/components/web_server/__init__.py index bfa3cac5a1..f0aaac9b5e 100644 --- a/esphome/components/web_server/__init__.py +++ b/esphome/components/web_server/__init__.py @@ -362,7 +362,16 @@ async def add_entity_config(entity, config): def build_index_html(config: ConfigType, offline_hint: bool = True) -> str: + js_url = config[CONF_JS_URL] + # The interface is downloaded from the internet. Without internet access, which is + # common for browsers on the WiFi AP, that download fails or simply stalls and the + # page stays blank. Show a hint after a few seconds unless www.js has registered the + # esp-app element; CSS hides the hint again if the interface does arrive later. + # Kept short: it lives in flash on every build without captive_portal. + offline_hint = offline_hint and bool(js_url) html = "
" + if offline_hint: + html += "" css_include = config.get(CONF_CSS_INCLUDE) js_include = config.get(CONF_JS_INCLUDE) if css_include: @@ -373,18 +382,15 @@ def build_index_html(config: ConfigType, offline_hint: bool = True) -> str: if js_include: html += "" html += "The web interface is not loading. This browser needs internet " + "access to download it, or set local: true under web_server: in the YAML.
" + "" + ) + if js_url: + html += f'' html += "" return html diff --git a/tests/unit_tests/components/test_web_server.py b/tests/unit_tests/components/test_web_server.py index dc0fb03d21..55c26b0dc7 100644 --- a/tests/unit_tests/components/test_web_server.py +++ b/tests/unit_tests/components/test_web_server.py @@ -23,24 +23,29 @@ JS_URL = "https://oi.esphome.io/v2/www.js" def test_build_index_html_has_offline_hint() -> None: - """The hosted script tag shows a hint when the browser cannot download it.""" + """A hidden hint is shown after a timeout unless www.js registered esp-app.""" html = build_index_html({CONF_JS_URL: JS_URL, CONF_CSS_URL: ""}) - assert f'' in html - assert "onerror" not in html + assert f'" not in html def test_build_index_html_without_js_url() -> None: """No hosted script and no hint when js_url is empty.""" html = build_index_html({CONF_JS_URL: "", CONF_CSS_URL: ""}) assert "