mirror of
https://github.com/Threnklyn/esphome-dev.git
synced 2026-06-06 04:48:27 +02:00
Add new wizard + allow installing firmware over webserial (#1887)
This commit is contained in:
@@ -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,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user