mirror of
https://github.com/esphome/esphome.git
synced 2024-12-22 05:24:53 +01:00
Webserver - include css, js in index (#932)
* add new config options * init variables in code * load css and js in python * update print inside webserver * fix indentation * fix indentation * indentation fix * fix condition in init * use cv.file_ instead of cv.string * do not import EsphomeError * support embedding js and css at the same time as defined in url * handle css as separate page * handle js as separate page * fix copy and paste error
This commit is contained in:
parent
4402a6eb4c
commit
67cbaabd99
4 changed files with 107 additions and 8 deletions
|
@ -3,7 +3,7 @@ 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,
|
||||
CONF_CSS_INCLUDE, CONF_CSS_URL, CONF_ID, CONF_JS_INCLUDE, CONF_JS_URL, CONF_PORT,
|
||||
CONF_AUTH, CONF_USERNAME, CONF_PASSWORD)
|
||||
from esphome.core import coroutine_with_priority
|
||||
|
||||
|
@ -16,7 +16,9 @@ CONFIG_SCHEMA = cv.Schema({
|
|||
cv.GenerateID(): cv.declare_id(WebServer),
|
||||
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_CSS_INCLUDE): cv.file_,
|
||||
cv.Optional(CONF_JS_URL, default="https://esphome.io/_static/webserver-v1.min.js"): cv.string,
|
||||
cv.Optional(CONF_JS_INCLUDE): cv.file_,
|
||||
cv.Optional(CONF_AUTH): cv.Schema({
|
||||
cv.Required(CONF_USERNAME): cv.string_strict,
|
||||
cv.Required(CONF_PASSWORD): cv.string_strict,
|
||||
|
@ -39,3 +41,11 @@ def to_code(config):
|
|||
if CONF_AUTH in config:
|
||||
cg.add(var.set_username(config[CONF_AUTH][CONF_USERNAME]))
|
||||
cg.add(var.set_password(config[CONF_AUTH][CONF_PASSWORD]))
|
||||
if CONF_CSS_INCLUDE in config:
|
||||
cg.add_define('WEBSERVER_CSS_INCLUDE')
|
||||
with open(config[CONF_CSS_INCLUDE], "r") as myfile:
|
||||
cg.add(var.set_css_include(myfile.read()))
|
||||
if CONF_JS_INCLUDE in config:
|
||||
cg.add_define('WEBSERVER_JS_INCLUDE')
|
||||
with open(config[CONF_JS_INCLUDE], "r") as myfile:
|
||||
cg.add(var.set_js_include(myfile.read()))
|
||||
|
|
|
@ -60,7 +60,9 @@ UrlMatch match_url(const std::string &url, bool only_domain = false) {
|
|||
}
|
||||
|
||||
void WebServer::set_css_url(const char *css_url) { this->css_url_ = css_url; }
|
||||
void WebServer::set_css_include(const char *css_include) { this->css_include_ = css_include; }
|
||||
void WebServer::set_js_url(const char *js_url) { this->js_url_ = js_url; }
|
||||
void WebServer::set_js_include(const char *js_include) { this->js_include_ = js_include; }
|
||||
|
||||
void WebServer::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up web server...");
|
||||
|
@ -133,9 +135,16 @@ void WebServer::handle_index_request(AsyncWebServerRequest *request) {
|
|||
std::string title = App.get_name() + " Web Server";
|
||||
stream->print(F("<!DOCTYPE html><html><head><meta charset=UTF-8><title>"));
|
||||
stream->print(title.c_str());
|
||||
stream->print(F("</title><link rel=\"stylesheet\" href=\""));
|
||||
stream->print(this->css_url_);
|
||||
stream->print(F("\"></head><body><article class=\"markdown-body\"><h1>"));
|
||||
stream->print(F("</title>"));
|
||||
#ifdef WEBSERVER_CSS_INCLUDE
|
||||
stream->print(F("<link rel=\"stylesheet\" href=\"/0.css\">"));
|
||||
#endif
|
||||
if (strlen(this->css_url_) > 0) {
|
||||
stream->print(F("<link rel=\"stylesheet\" href=\""));
|
||||
stream->print(this->css_url_);
|
||||
stream->print(F("\">"));
|
||||
}
|
||||
stream->print(F("</head><body><article class=\"markdown-body\"><h1>"));
|
||||
stream->print(title.c_str());
|
||||
stream->print(F("</h1><h2>States</h2><table id=\"states\"><thead><tr><th>Name<th>State<th>Actions<tbody>"));
|
||||
// All content is controlled and created by user - so allowing all origins is fine here.
|
||||
|
@ -175,14 +184,44 @@ void WebServer::handle_index_request(AsyncWebServerRequest *request) {
|
|||
"REST API documentation.</p>"
|
||||
"<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=\""));
|
||||
stream->print(this->js_url_);
|
||||
stream->print(F("\"></script></article></body></html>"));
|
||||
"<h2>Debug Log</h2><pre id=\"log\"></pre>"));
|
||||
#ifdef WEBSERVER_JS_INCLUDE
|
||||
if (this->js_include_ != nullptr) {
|
||||
stream->print(F("<script src=\"/0.js\"></script>"));
|
||||
}
|
||||
#endif
|
||||
if (strlen(this->js_url_) > 0) {
|
||||
stream->print(F("<script src=\""));
|
||||
stream->print(this->js_url_);
|
||||
stream->print(F("\"></script>"));
|
||||
}
|
||||
stream->print(F("</article></body></html>"));
|
||||
|
||||
request->send(stream);
|
||||
}
|
||||
|
||||
#ifdef WEBSERVER_CSS_INCLUDE
|
||||
void WebServer::handle_css_request(AsyncWebServerRequest *request) {
|
||||
AsyncResponseStream *stream = request->beginResponseStream("text/css");
|
||||
if (this->css_include_ != nullptr) {
|
||||
stream->print(this->css_include_);
|
||||
}
|
||||
|
||||
request->send(stream);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WEBSERVER_JS_INCLUDE
|
||||
void WebServer::handle_js_request(AsyncWebServerRequest *request) {
|
||||
AsyncResponseStream *stream = request->beginResponseStream("text/javascript");
|
||||
if (this->js_include_ != nullptr) {
|
||||
stream->print(this->js_include_);
|
||||
}
|
||||
|
||||
request->send(stream);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
void WebServer::on_sensor_update(sensor::Sensor *obj, float state) {
|
||||
this->events_.send(this->sensor_json(obj, state).c_str(), "state");
|
||||
|
@ -460,6 +499,16 @@ bool WebServer::canHandle(AsyncWebServerRequest *request) {
|
|||
if (request->url() == "/")
|
||||
return true;
|
||||
|
||||
#ifdef WEBSERVER_CSS_INCLUDE
|
||||
if (request->url() == "/0.css")
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#ifdef WEBSERVER_JS_INCLUDE
|
||||
if (request->url() == "/0.js")
|
||||
return true;
|
||||
#endif
|
||||
|
||||
UrlMatch match = match_url(request->url().c_str(), true);
|
||||
if (!match.valid)
|
||||
return false;
|
||||
|
@ -505,6 +554,20 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) {
|
|||
return;
|
||||
}
|
||||
|
||||
#ifdef WEBSERVER_CSS_INCLUDE
|
||||
if (request->url() == "/0.css") {
|
||||
this->handle_css_request(request);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WEBSERVER_JS_INCLUDE
|
||||
if (request->url() == "/0.js") {
|
||||
this->handle_js_request(request);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
UrlMatch match = match_url(request->url().c_str());
|
||||
#ifdef USE_SENSOR
|
||||
if (match.domain == "sensor") {
|
||||
|
|
|
@ -41,6 +41,12 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|||
*/
|
||||
void set_css_url(const char *css_url);
|
||||
|
||||
/** Set local path to the script that's embedded in the index page. Defaults to
|
||||
*
|
||||
* @param css_include Local path to web server script.
|
||||
*/
|
||||
void set_css_include(const char *css_include);
|
||||
|
||||
/** Set the URL to the script that's embedded in the index page. Defaults to
|
||||
* https://esphome.io/_static/webserver-v1.min.js
|
||||
*
|
||||
|
@ -48,6 +54,12 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|||
*/
|
||||
void set_js_url(const char *js_url);
|
||||
|
||||
/** Set local path to the script that's embedded in the index page. Defaults to
|
||||
*
|
||||
* @param js_include Local path to web server script.
|
||||
*/
|
||||
void set_js_include(const char *js_include);
|
||||
|
||||
// ========== INTERNAL METHODS ==========
|
||||
// (In most use cases you won't need these)
|
||||
/// Setup the internal web server and register handlers.
|
||||
|
@ -61,6 +73,16 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|||
/// Handle an index request under '/'.
|
||||
void handle_index_request(AsyncWebServerRequest *request);
|
||||
|
||||
#ifdef WEBSERVER_CSS_INCLUDE
|
||||
/// Handle included css request under '/0.css'.
|
||||
void handle_css_request(AsyncWebServerRequest *request);
|
||||
#endif
|
||||
|
||||
#ifdef WEBSERVER_JS_INCLUDE
|
||||
/// Handle included js request under '/0.js'.
|
||||
void handle_js_request(AsyncWebServerRequest *request);
|
||||
#endif
|
||||
|
||||
bool using_auth() { return username_ != nullptr && password_ != nullptr; }
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
|
@ -135,7 +157,9 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|||
const char *username_{nullptr};
|
||||
const char *password_{nullptr};
|
||||
const char *css_url_{nullptr};
|
||||
const char *css_include_{nullptr};
|
||||
const char *js_url_{nullptr};
|
||||
const char *js_include_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace web_server
|
||||
|
|
|
@ -103,6 +103,7 @@ CONF_COOL_ACTION = 'cool_action'
|
|||
CONF_COUNT_MODE = 'count_mode'
|
||||
CONF_CRON = 'cron'
|
||||
CONF_CS_PIN = 'cs_pin'
|
||||
CONF_CSS_INCLUDE = 'css_include'
|
||||
CONF_CSS_URL = 'css_url'
|
||||
CONF_CURRENT = 'current'
|
||||
CONF_CURRENT_OPERATION = 'current_operation'
|
||||
|
@ -210,6 +211,7 @@ CONF_INVALID_COOLDOWN = 'invalid_cooldown'
|
|||
CONF_INVERT = 'invert'
|
||||
CONF_INVERTED = 'inverted'
|
||||
CONF_IP_ADDRESS = 'ip_address'
|
||||
CONF_JS_INCLUDE = 'js_include'
|
||||
CONF_JS_URL = 'js_url'
|
||||
CONF_JVC = 'jvc'
|
||||
CONF_KEEP_ON_TIME = 'keep_on_time'
|
||||
|
|
Loading…
Reference in a new issue