Add new wizard + allow installing firmware over webserial (#1887)

This commit is contained in:
Paulus Schoutsen
2021-06-11 15:49:05 -07:00
committed by GitHub
parent 5a2cfa2798
commit 9ad9d64ac7
24 changed files with 98 additions and 2560 deletions
+59 -9
View File
@@ -9,6 +9,7 @@ import json
import logging
import multiprocessing
import os
import secrets
import shutil
import subprocess
import threading
@@ -44,6 +45,8 @@ from esphome.zeroconf import DashboardStatus, Zeroconf
_LOGGER = logging.getLogger(__name__)
ENV_DEV = "ESPHOME_DASHBOARD_DEV"
class DashboardSettings:
def __init__(self):
@@ -111,6 +114,7 @@ def template_args():
docs_link = "https://next.esphome.io/"
else:
docs_link = "https://www.esphome.io/"
return {
"version": version,
"docs_link": docs_link,
@@ -349,6 +353,7 @@ class SerialPortRequestHandler(BaseHandler):
data.append({"port": port.path, "desc": desc})
data.append({"port": "OTA", "desc": "Over-The-Air"})
data.sort(key=lambda x: x["port"], reverse=True)
self.set_header("content-type", "application/json")
self.write(json.dumps(data))
@@ -358,11 +363,15 @@ class WizardRequestHandler(BaseHandler):
from esphome import wizard
kwargs = {
k: "".join(x.decode() for x in v) for k, v in self.request.arguments.items()
k: "".join(x.decode() for x in v)
for k, v in self.request.arguments.items()
if k in ("name", "platform", "board", "ssid", "psk", "password")
}
kwargs["ota_password"] = secrets.token_hex(16)
destination = settings.rel_path(kwargs["name"] + ".yaml")
wizard.wizard_write(path=destination, **kwargs)
self.redirect("./?begin=True")
self.set_status(200)
self.finish()
class DownloadBinaryRequestHandler(BaseHandler):
@@ -473,7 +482,7 @@ class MainRequestHandler(BaseHandler):
entries = _list_dashboard_entries()
self.render(
"templates/index.html",
get_template_path("index"),
entries=entries,
begin=begin,
**template_args(),
@@ -560,6 +569,7 @@ class PingRequestHandler(BaseHandler):
@authenticated
def get(self):
PING_REQUEST.set()
self.set_header("content-type", "application/json")
self.write(json.dumps(PING_RESULT))
@@ -567,6 +577,21 @@ def is_allowed(configuration):
return os.path.sep not in configuration
class InfoRequestHandler(BaseHandler):
@authenticated
@bind_config
def get(self, configuration=None):
yaml_path = settings.rel_path(configuration)
all_yaml_files = settings.list_yaml_files()
if yaml_path not in all_yaml_files:
self.set_status(404)
return
self.set_header("content-type", "application/json")
self.write(DashboardEntry(yaml_path).storage.to_json())
class EditRequestHandler(BaseHandler):
@authenticated
@bind_config
@@ -633,7 +658,7 @@ class LoginHandler(BaseHandler):
def render_login_page(self, error=None):
self.render(
"templates/login.html",
get_template_path("login"),
error=error,
hassio=settings.using_hassio_auth,
has_username=bool(settings.username),
@@ -694,19 +719,44 @@ class LogoutHandler(BaseHandler):
_STATIC_FILE_HASHES = {}
def get_base_frontend_path():
if ENV_DEV not in os.environ:
import esphome_dashboard
return esphome_dashboard.where()
static_path = os.environ[ENV_DEV]
if not static_path.endswith("/"):
static_path += "/"
# This path can be relative, so resolve against the root or else templates don't work
return os.path.abspath(os.path.join(os.getcwd(), static_path, "esphome_dashboard"))
def get_template_path(template_name):
return os.path.join(get_base_frontend_path(), f"{template_name}.template.html")
def get_static_path(*args):
return os.path.join(get_base_frontend_path(), "static", *args)
def get_static_file_url(name):
static_path = os.path.join(os.path.dirname(__file__), "static")
# Module imports can't deduplicate if stuff added to url
if name == "js/esphome/index.js":
return f"./static/{name}"
if name in _STATIC_FILE_HASHES:
hash_ = _STATIC_FILE_HASHES[name]
else:
path = os.path.join(static_path, name)
path = get_static_path(name)
with open(path, "rb") as f_handle:
hash_ = hashlib.md5(f_handle.read()).hexdigest()[:8]
_STATIC_FILE_HASHES[name] = hash_
return f"./static/{name}?hash={hash_}"
def make_app(debug=False):
def make_app(debug=get_bool_env(ENV_DEV)):
def log_function(handler):
if handler.get_status() < 400:
log_method = access_log.info
@@ -736,7 +786,6 @@ def make_app(debug=False):
"Cache-Control", "no-store, no-cache, must-revalidate, max-age=0"
)
static_path = os.path.join(os.path.dirname(__file__), "static")
app_settings = {
"debug": debug,
"cookie_secret": settings.cookie_secret,
@@ -758,6 +807,7 @@ def make_app(debug=False):
(rel + "vscode", EsphomeVscodeHandler),
(rel + "ace", EsphomeAceEditorHandler),
(rel + "update-all", EsphomeUpdateAllHandler),
(rel + "info", InfoRequestHandler),
(rel + "edit", EditRequestHandler),
(rel + "download.bin", DownloadBinaryRequestHandler),
(rel + "serial-ports", SerialPortRequestHandler),
@@ -765,7 +815,7 @@ def make_app(debug=False):
(rel + "delete", DeleteRequestHandler),
(rel + "undo-delete", UndoDeleteRequestHandler),
(rel + "wizard.html", WizardRequestHandler),
(rel + r"static/(.*)", StaticFileHandler, {"path": static_path}),
(rel + r"static/(.*)", StaticFileHandler, {"path": get_static_path()}),
],
**app_settings,
)