mirror of
https://github.com/esphome/esphome.git
synced 2025-07-28 14:16:40 +00:00
Allow disabling OTA for web_server while keeping it enabled for captive_portal (#9583)
This commit is contained in:
parent
02999195cd
commit
b1655b3fd4
@ -74,13 +74,14 @@ def validate_local(config: ConfigType) -> ConfigType:
|
||||
return config
|
||||
|
||||
|
||||
def validate_ota_removed(config: ConfigType) -> ConfigType:
|
||||
# Only raise error if OTA is explicitly enabled (True)
|
||||
# If it's False or not specified, we can safely ignore it
|
||||
if config.get(CONF_OTA):
|
||||
def validate_ota(config: ConfigType) -> ConfigType:
|
||||
# The OTA option only accepts False to explicitly disable OTA for web_server
|
||||
# IMPORTANT: Setting ota: false ONLY affects the web_server component
|
||||
# The captive_portal component will still be able to perform OTA updates
|
||||
if CONF_OTA in config and config[CONF_OTA] is not False:
|
||||
raise cv.Invalid(
|
||||
f"The '{CONF_OTA}' option has been removed from 'web_server'. "
|
||||
f"Please use the new OTA platform structure instead:\n\n"
|
||||
f"The '{CONF_OTA}' option in 'web_server' only accepts 'false' to disable OTA. "
|
||||
f"To enable OTA, please use the new OTA platform structure instead:\n\n"
|
||||
f"ota:\n"
|
||||
f" - platform: web_server\n\n"
|
||||
f"See https://esphome.io/components/ota for more information."
|
||||
@ -185,7 +186,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
web_server_base.WebServerBase
|
||||
),
|
||||
cv.Optional(CONF_INCLUDE_INTERNAL, default=False): cv.boolean,
|
||||
cv.Optional(CONF_OTA, default=False): cv.boolean,
|
||||
cv.Optional(CONF_OTA): cv.boolean,
|
||||
cv.Optional(CONF_LOG, default=True): cv.boolean,
|
||||
cv.Optional(CONF_LOCAL): cv.boolean,
|
||||
cv.Optional(CONF_SORTING_GROUPS): cv.ensure_list(sorting_group),
|
||||
@ -203,7 +204,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
default_url,
|
||||
validate_local,
|
||||
validate_sorting_groups,
|
||||
validate_ota_removed,
|
||||
validate_ota,
|
||||
)
|
||||
|
||||
|
||||
@ -288,7 +289,11 @@ async def to_code(config):
|
||||
cg.add(var.set_css_url(config[CONF_CSS_URL]))
|
||||
cg.add(var.set_js_url(config[CONF_JS_URL]))
|
||||
# OTA is now handled by the web_server OTA platform
|
||||
# The CONF_OTA option is kept only for backwards compatibility validation
|
||||
# The CONF_OTA option is kept to allow explicitly disabling OTA for web_server
|
||||
# IMPORTANT: This ONLY affects the web_server component, NOT captive_portal
|
||||
# Captive portal will still be able to perform OTA updates even when this is set
|
||||
if config.get(CONF_OTA) is False:
|
||||
cg.add_define("USE_WEBSERVER_OTA_DISABLED")
|
||||
cg.add(var.set_expose_log(config[CONF_LOG]))
|
||||
if config[CONF_ENABLE_PRIVATE_NETWORK_ACCESS]:
|
||||
cg.add_define("USE_WEBSERVER_PRIVATE_NETWORK_ACCESS")
|
||||
|
@ -5,6 +5,10 @@
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef USE_CAPTIVE_PORTAL
|
||||
#include "esphome/components/captive_portal/captive_portal.h"
|
||||
#endif
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
#ifdef USE_ESP8266
|
||||
#include <Updater.h>
|
||||
@ -25,7 +29,22 @@ class OTARequestHandler : public AsyncWebHandler {
|
||||
void handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len,
|
||||
bool final) override;
|
||||
bool canHandle(AsyncWebServerRequest *request) const override {
|
||||
return request->url() == "/update" && request->method() == HTTP_POST;
|
||||
// Check if this is an OTA update request
|
||||
bool is_ota_request = request->url() == "/update" && request->method() == HTTP_POST;
|
||||
|
||||
#if defined(USE_WEBSERVER_OTA_DISABLED) && defined(USE_CAPTIVE_PORTAL)
|
||||
// IMPORTANT: USE_WEBSERVER_OTA_DISABLED only disables OTA for the web_server component
|
||||
// Captive portal can still perform OTA updates - check if request is from active captive portal
|
||||
// Note: global_captive_portal is the standard way components communicate in ESPHome
|
||||
return is_ota_request && captive_portal::global_captive_portal != nullptr &&
|
||||
captive_portal::global_captive_portal->is_active();
|
||||
#elif defined(USE_WEBSERVER_OTA_DISABLED)
|
||||
// OTA disabled for web_server and no captive portal compiled in
|
||||
return false;
|
||||
#else
|
||||
// OTA enabled for web_server
|
||||
return is_ota_request;
|
||||
#endif
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||
|
@ -268,10 +268,10 @@ std::string WebServer::get_config_json() {
|
||||
return json::build_json([this](JsonObject root) {
|
||||
root["title"] = App.get_friendly_name().empty() ? App.get_name() : App.get_friendly_name();
|
||||
root["comment"] = App.get_comment();
|
||||
#ifdef USE_WEBSERVER_OTA
|
||||
root["ota"] = true; // web_server OTA platform is configured
|
||||
#if defined(USE_WEBSERVER_OTA_DISABLED) || !defined(USE_WEBSERVER_OTA)
|
||||
root["ota"] = false; // Note: USE_WEBSERVER_OTA_DISABLED only affects web_server, not captive_portal
|
||||
#else
|
||||
root["ota"] = false;
|
||||
root["ota"] = true;
|
||||
#endif
|
||||
root["log"] = this->expose_log_;
|
||||
root["lang"] = "en";
|
||||
|
@ -192,7 +192,9 @@ void WebServer::handle_index_request(AsyncWebServerRequest *request) {
|
||||
|
||||
stream->print(F("</tbody></table><p>See <a href=\"https://esphome.io/web-api/index.html\">ESPHome Web API</a> for "
|
||||
"REST API documentation.</p>"));
|
||||
#ifdef USE_WEBSERVER_OTA
|
||||
#if defined(USE_WEBSERVER_OTA) && !defined(USE_WEBSERVER_OTA_DISABLED)
|
||||
// Show OTA form only if web_server OTA is not explicitly disabled
|
||||
// Note: USE_WEBSERVER_OTA_DISABLED only affects web_server, not captive_portal
|
||||
stream->print(F("<h2>OTA Update</h2><form method=\"POST\" action=\"/update\" enctype=\"multipart/form-data\"><input "
|
||||
"type=\"file\" name=\"update\"><input type=\"submit\" value=\"Update\"></form>"));
|
||||
#endif
|
||||
|
@ -8,31 +8,31 @@ from esphome.types import ConfigType
|
||||
|
||||
def test_web_server_ota_true_fails_validation() -> None:
|
||||
"""Test that web_server with ota: true fails validation with helpful message."""
|
||||
from esphome.components.web_server import validate_ota_removed
|
||||
from esphome.components.web_server import validate_ota
|
||||
|
||||
# Config with ota: true should fail
|
||||
config: ConfigType = {"ota": True}
|
||||
|
||||
with pytest.raises(cv.Invalid) as exc_info:
|
||||
validate_ota_removed(config)
|
||||
validate_ota(config)
|
||||
|
||||
# Check error message contains migration instructions
|
||||
error_msg = str(exc_info.value)
|
||||
assert "has been removed from 'web_server'" in error_msg
|
||||
assert "only accepts 'false' to disable OTA" in error_msg
|
||||
assert "platform: web_server" in error_msg
|
||||
assert "ota:" in error_msg
|
||||
|
||||
|
||||
def test_web_server_ota_false_passes_validation() -> None:
|
||||
"""Test that web_server with ota: false passes validation."""
|
||||
from esphome.components.web_server import validate_ota_removed
|
||||
from esphome.components.web_server import validate_ota
|
||||
|
||||
# Config with ota: false should pass
|
||||
config: ConfigType = {"ota": False}
|
||||
result = validate_ota_removed(config)
|
||||
result = validate_ota(config)
|
||||
assert result == config
|
||||
|
||||
# Config without ota should also pass
|
||||
config: ConfigType = {}
|
||||
result = validate_ota_removed(config)
|
||||
result = validate_ota(config)
|
||||
assert result == config
|
||||
|
Loading…
x
Reference in New Issue
Block a user