mirror of
https://github.com/Threnklyn/esphome-dev.git
synced 2026-05-25 15:18:29 +02:00
Add Captive Portal (#624)
* WIP: Captive Portal * Updates * Updates * Lint * Fixes
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import web_server_base
|
||||
from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID
|
||||
from esphome.const import CONF_CSS_URL, CONF_ID, CONF_JS_URL, CONF_PORT
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
from esphome.core import coroutine_with_priority
|
||||
|
||||
DEPENDENCIES = ['network']
|
||||
AUTO_LOAD = ['json']
|
||||
AUTO_LOAD = ['json', 'web_server_base']
|
||||
|
||||
web_server_ns = cg.esphome_ns.namespace('web_server')
|
||||
WebServer = web_server_ns.class_('WebServer', cg.Component, cg.Controller)
|
||||
@@ -14,18 +15,18 @@ CONFIG_SCHEMA = cv.Schema({
|
||||
cv.Optional(CONF_PORT, default=80): cv.port,
|
||||
cv.Optional(CONF_CSS_URL, default="https://esphome.io/_static/webserver-v1.min.css"): cv.string,
|
||||
cv.Optional(CONF_JS_URL, default="https://esphome.io/_static/webserver-v1.min.js"): cv.string,
|
||||
|
||||
cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id(web_server_base.WebServerBase),
|
||||
}).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
@coroutine_with_priority(40.0)
|
||||
def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
paren = yield cg.get_variable(config[CONF_WEB_SERVER_BASE_ID])
|
||||
|
||||
var = cg.new_Pvariable(config[CONF_ID], paren)
|
||||
yield cg.register_component(var, config)
|
||||
|
||||
cg.add(var.set_port(config[CONF_PORT]))
|
||||
cg.add(paren.set_port(config[CONF_PORT]))
|
||||
cg.add(var.set_css_url(config[CONF_CSS_URL]))
|
||||
cg.add(var.set_js_url(config[CONF_JS_URL]))
|
||||
|
||||
if CORE.is_esp32:
|
||||
cg.add_library('FS', None)
|
||||
cg.add_library('ESP Async WebServer', '1.1.1')
|
||||
|
||||
@@ -6,13 +6,6 @@
|
||||
|
||||
#include "StreamString.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include <Update.h>
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
#include <Updater.h>
|
||||
#endif
|
||||
|
||||
#include <cstdlib>
|
||||
#include <esphome/components/logger/logger.h>
|
||||
|
||||
@@ -66,7 +59,7 @@ void WebServer::set_js_url(const char *js_url) { this->js_url_ = js_url; }
|
||||
|
||||
void WebServer::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up web server...");
|
||||
this->server_ = new AsyncWebServer(this->port_);
|
||||
this->base_->init();
|
||||
|
||||
this->events_.onConnect([this](AsyncEventSourceClient *client) {
|
||||
// Configure reconnect timeout
|
||||
@@ -114,91 +107,18 @@ void WebServer::setup() {
|
||||
logger::global_logger->add_on_log_callback(
|
||||
[this](int level, const char *tag, const char *message) { this->events_.send(message, "log", millis()); });
|
||||
#endif
|
||||
this->server_->addHandler(this);
|
||||
this->server_->addHandler(&this->events_);
|
||||
|
||||
this->server_->begin();
|
||||
this->base_->add_handler(&this->events_);
|
||||
this->base_->add_handler(this);
|
||||
this->base_->add_ota_handler();
|
||||
|
||||
this->set_interval(10000, [this]() { this->events_.send("", "ping", millis(), 30000); });
|
||||
}
|
||||
void WebServer::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Web Server:");
|
||||
ESP_LOGCONFIG(TAG, " Address: %s:%u", network_get_address().c_str(), this->port_);
|
||||
ESP_LOGCONFIG(TAG, " Address: %s:%u", network_get_address().c_str(), this->base_->get_port());
|
||||
}
|
||||
float WebServer::get_setup_priority() const { return setup_priority::WIFI - 1.0f; }
|
||||
|
||||
void WebServer::handle_update_request(AsyncWebServerRequest *request) {
|
||||
AsyncWebServerResponse *response;
|
||||
if (!Update.hasError()) {
|
||||
response = request->beginResponse(200, "text/plain", "Update Successful!");
|
||||
} else {
|
||||
StreamString ss;
|
||||
ss.print("Update Failed: ");
|
||||
Update.printError(ss);
|
||||
response = request->beginResponse(200, "text/plain", ss);
|
||||
}
|
||||
response->addHeader("Connection", "close");
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
void report_ota_error() {
|
||||
StreamString ss;
|
||||
Update.printError(ss);
|
||||
ESP_LOGW(TAG, "OTA Update failed! Error: %s", ss.c_str());
|
||||
}
|
||||
|
||||
void WebServer::handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data,
|
||||
size_t len, bool final) {
|
||||
bool success;
|
||||
if (index == 0) {
|
||||
ESP_LOGI(TAG, "OTA Update Start: %s", filename.c_str());
|
||||
this->ota_read_length_ = 0;
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
Update.runAsync(true);
|
||||
success = Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000);
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (Update.isRunning())
|
||||
Update.abort();
|
||||
success = Update.begin(UPDATE_SIZE_UNKNOWN, U_FLASH);
|
||||
#endif
|
||||
if (!success) {
|
||||
report_ota_error();
|
||||
return;
|
||||
}
|
||||
} else if (Update.hasError()) {
|
||||
// don't spam logs with errors if something failed at start
|
||||
return;
|
||||
}
|
||||
|
||||
success = Update.write(data, len) == len;
|
||||
if (!success) {
|
||||
report_ota_error();
|
||||
return;
|
||||
}
|
||||
this->ota_read_length_ += len;
|
||||
|
||||
const uint32_t now = millis();
|
||||
if (now - this->last_ota_progress_ > 1000) {
|
||||
if (request->contentLength() != 0) {
|
||||
float percentage = (this->ota_read_length_ * 100.0f) / request->contentLength();
|
||||
ESP_LOGD(TAG, "OTA in progress: %0.1f%%", percentage);
|
||||
} else {
|
||||
ESP_LOGD(TAG, "OTA in progress: %u bytes read", this->ota_read_length_);
|
||||
}
|
||||
this->last_ota_progress_ = now;
|
||||
}
|
||||
|
||||
if (final) {
|
||||
if (Update.end(true)) {
|
||||
ESP_LOGI(TAG, "OTA update successful!");
|
||||
this->set_timeout(100, []() { App.safe_reboot(); });
|
||||
} else {
|
||||
report_ota_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebServer::handle_index_request(AsyncWebServerRequest *request) {
|
||||
AsyncResponseStream *stream = request->beginResponseStream("text/html");
|
||||
std::string title = App.get_name() + " Web Server";
|
||||
@@ -248,7 +168,7 @@ 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>"
|
||||
"<h2>OTA Update</h2><form method='POST' action=\"/update\" enctype=\"multipart/form-data\"><input "
|
||||
"<h2>OTA Update</h2><form method=\"POST\" action=\"/update\" enctype=\"multipart/form-data\"><input "
|
||||
"type=\"file\" name=\"update\"><input type=\"submit\" value=\"Update\"></form>"
|
||||
"<h2>Debug Log</h2><pre id=\"log\"></pre>"
|
||||
"<script src=\""));
|
||||
@@ -531,9 +451,6 @@ bool WebServer::canHandle(AsyncWebServerRequest *request) {
|
||||
if (request->url() == "/")
|
||||
return true;
|
||||
|
||||
if (request->url() == "/update" && request->method() == HTTP_POST)
|
||||
return true;
|
||||
|
||||
UrlMatch match = match_url(request->url().c_str(), true);
|
||||
if (!match.valid)
|
||||
return false;
|
||||
@@ -575,11 +492,6 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (request->url() == "/update") {
|
||||
this->handle_update_request(request);
|
||||
return;
|
||||
}
|
||||
|
||||
UrlMatch match = match_url(request->url().c_str());
|
||||
#ifdef USE_SENSOR
|
||||
if (match.domain == "sensor") {
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/controller.h"
|
||||
#include "esphome/components/web_server_base/web_server_base.h"
|
||||
|
||||
#include <vector>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace web_server {
|
||||
@@ -28,8 +28,7 @@ struct UrlMatch {
|
||||
*/
|
||||
class WebServer : public Controller, public Component, public AsyncWebHandler {
|
||||
public:
|
||||
void set_port(uint16_t port) { port_ = port; }
|
||||
|
||||
WebServer(web_server_base::WebServerBase *base) : base_(base) {}
|
||||
/** Set the URL to the CSS <link> that's sent to each client. Defaults to
|
||||
* https://esphome.io/_static/webserver-v1.min.css
|
||||
*
|
||||
@@ -57,8 +56,6 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
||||
/// Handle an index request under '/'.
|
||||
void handle_index_request(AsyncWebServerRequest *request);
|
||||
|
||||
void handle_update_request(AsyncWebServerRequest *request);
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
void on_sensor_update(sensor::Sensor *obj, float state) override;
|
||||
/// Handle a sensor request under '/sensor/<id>'.
|
||||
@@ -122,19 +119,14 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
||||
bool canHandle(AsyncWebServerRequest *request) override;
|
||||
/// Override the web handler's handleRequest method.
|
||||
void handleRequest(AsyncWebServerRequest *request) override;
|
||||
void handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len,
|
||||
bool final) override;
|
||||
/// This web handle is not trivial.
|
||||
bool isRequestHandlerTrivial() override;
|
||||
|
||||
protected:
|
||||
uint16_t port_;
|
||||
AsyncWebServer *server_;
|
||||
web_server_base::WebServerBase *base_;
|
||||
AsyncEventSource events_{"/events"};
|
||||
const char *css_url_{nullptr};
|
||||
const char *js_url_{nullptr};
|
||||
uint32_t last_ota_progress_{0};
|
||||
uint32_t ota_read_length_{0};
|
||||
};
|
||||
|
||||
} // namespace web_server
|
||||
|
||||
Reference in New Issue
Block a user