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
08a5ba6ef1
commit
9d80889bc9
@ -74,13 +74,14 @@ def validate_local(config: ConfigType) -> ConfigType:
|
|||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
def validate_ota_removed(config: ConfigType) -> ConfigType:
|
def validate_ota(config: ConfigType) -> ConfigType:
|
||||||
# Only raise error if OTA is explicitly enabled (True)
|
# The OTA option only accepts False to explicitly disable OTA for web_server
|
||||||
# If it's False or not specified, we can safely ignore it
|
# IMPORTANT: Setting ota: false ONLY affects the web_server component
|
||||||
if config.get(CONF_OTA):
|
# 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(
|
raise cv.Invalid(
|
||||||
f"The '{CONF_OTA}' option has been removed from 'web_server'. "
|
f"The '{CONF_OTA}' option in 'web_server' only accepts 'false' to disable OTA. "
|
||||||
f"Please use the new OTA platform structure instead:\n\n"
|
f"To enable OTA, please use the new OTA platform structure instead:\n\n"
|
||||||
f"ota:\n"
|
f"ota:\n"
|
||||||
f" - platform: web_server\n\n"
|
f" - platform: web_server\n\n"
|
||||||
f"See https://esphome.io/components/ota for more information."
|
f"See https://esphome.io/components/ota for more information."
|
||||||
@ -185,7 +186,7 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
web_server_base.WebServerBase
|
web_server_base.WebServerBase
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_INCLUDE_INTERNAL, default=False): cv.boolean,
|
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_LOG, default=True): cv.boolean,
|
||||||
cv.Optional(CONF_LOCAL): cv.boolean,
|
cv.Optional(CONF_LOCAL): cv.boolean,
|
||||||
cv.Optional(CONF_SORTING_GROUPS): cv.ensure_list(sorting_group),
|
cv.Optional(CONF_SORTING_GROUPS): cv.ensure_list(sorting_group),
|
||||||
@ -203,7 +204,7 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
default_url,
|
default_url,
|
||||||
validate_local,
|
validate_local,
|
||||||
validate_sorting_groups,
|
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_css_url(config[CONF_CSS_URL]))
|
||||||
cg.add(var.set_js_url(config[CONF_JS_URL]))
|
cg.add(var.set_js_url(config[CONF_JS_URL]))
|
||||||
# OTA is now handled by the web_server OTA platform
|
# 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]))
|
cg.add(var.set_expose_log(config[CONF_LOG]))
|
||||||
if config[CONF_ENABLE_PRIVATE_NETWORK_ACCESS]:
|
if config[CONF_ENABLE_PRIVATE_NETWORK_ACCESS]:
|
||||||
cg.add_define("USE_WEBSERVER_PRIVATE_NETWORK_ACCESS")
|
cg.add_define("USE_WEBSERVER_PRIVATE_NETWORK_ACCESS")
|
||||||
|
@ -5,6 +5,10 @@
|
|||||||
#include "esphome/core/application.h"
|
#include "esphome/core/application.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#ifdef USE_CAPTIVE_PORTAL
|
||||||
|
#include "esphome/components/captive_portal/captive_portal.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_ARDUINO
|
#ifdef USE_ARDUINO
|
||||||
#ifdef USE_ESP8266
|
#ifdef USE_ESP8266
|
||||||
#include <Updater.h>
|
#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,
|
void handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len,
|
||||||
bool final) override;
|
bool final) override;
|
||||||
bool canHandle(AsyncWebServerRequest *request) const 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)
|
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||||
|
@ -268,10 +268,10 @@ std::string WebServer::get_config_json() {
|
|||||||
return json::build_json([this](JsonObject root) {
|
return json::build_json([this](JsonObject root) {
|
||||||
root["title"] = App.get_friendly_name().empty() ? App.get_name() : App.get_friendly_name();
|
root["title"] = App.get_friendly_name().empty() ? App.get_name() : App.get_friendly_name();
|
||||||
root["comment"] = App.get_comment();
|
root["comment"] = App.get_comment();
|
||||||
#ifdef USE_WEBSERVER_OTA
|
#if defined(USE_WEBSERVER_OTA_DISABLED) || !defined(USE_WEBSERVER_OTA)
|
||||||
root["ota"] = true; // web_server OTA platform is configured
|
root["ota"] = false; // Note: USE_WEBSERVER_OTA_DISABLED only affects web_server, not captive_portal
|
||||||
#else
|
#else
|
||||||
root["ota"] = false;
|
root["ota"] = true;
|
||||||
#endif
|
#endif
|
||||||
root["log"] = this->expose_log_;
|
root["log"] = this->expose_log_;
|
||||||
root["lang"] = "en";
|
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 "
|
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>"));
|
"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 "
|
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>"));
|
"type=\"file\" name=\"update\"><input type=\"submit\" value=\"Update\"></form>"));
|
||||||
#endif
|
#endif
|
||||||
|
@ -8,31 +8,31 @@ from esphome.types import ConfigType
|
|||||||
|
|
||||||
def test_web_server_ota_true_fails_validation() -> None:
|
def test_web_server_ota_true_fails_validation() -> None:
|
||||||
"""Test that web_server with ota: true fails validation with helpful message."""
|
"""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 with ota: true should fail
|
||||||
config: ConfigType = {"ota": True}
|
config: ConfigType = {"ota": True}
|
||||||
|
|
||||||
with pytest.raises(cv.Invalid) as exc_info:
|
with pytest.raises(cv.Invalid) as exc_info:
|
||||||
validate_ota_removed(config)
|
validate_ota(config)
|
||||||
|
|
||||||
# Check error message contains migration instructions
|
# Check error message contains migration instructions
|
||||||
error_msg = str(exc_info.value)
|
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 "platform: web_server" in error_msg
|
||||||
assert "ota:" in error_msg
|
assert "ota:" in error_msg
|
||||||
|
|
||||||
|
|
||||||
def test_web_server_ota_false_passes_validation() -> None:
|
def test_web_server_ota_false_passes_validation() -> None:
|
||||||
"""Test that web_server with ota: false passes validation."""
|
"""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 with ota: false should pass
|
||||||
config: ConfigType = {"ota": False}
|
config: ConfigType = {"ota": False}
|
||||||
result = validate_ota_removed(config)
|
result = validate_ota(config)
|
||||||
assert result == config
|
assert result == config
|
||||||
|
|
||||||
# Config without ota should also pass
|
# Config without ota should also pass
|
||||||
config: ConfigType = {}
|
config: ConfigType = {}
|
||||||
result = validate_ota_removed(config)
|
result = validate_ota(config)
|
||||||
assert result == config
|
assert result == config
|
||||||
|
Loading…
x
Reference in New Issue
Block a user