mirror of
https://github.com/esphome/esphome.git
synced 2025-11-19 08:15:49 +00:00
add-black (#1593)
* Add black Update pre commit Update pre commit add empty line * Format with black
This commit is contained in:
committed by
GitHub
parent
2b60b0f1fa
commit
69879920eb
@@ -27,8 +27,13 @@ import tornado.websocket
|
||||
|
||||
from esphome import const, util
|
||||
from esphome.helpers import mkdir_p, get_bool_env, run_system_command
|
||||
from esphome.storage_json import EsphomeStorageJSON, StorageJSON, \
|
||||
esphome_storage_path, ext_storage_path, trash_storage_path
|
||||
from esphome.storage_json import (
|
||||
EsphomeStorageJSON,
|
||||
StorageJSON,
|
||||
esphome_storage_path,
|
||||
ext_storage_path,
|
||||
trash_storage_path,
|
||||
)
|
||||
from esphome.util import shlex_quote, get_serial_ports
|
||||
from .util import password_hash
|
||||
|
||||
@@ -42,18 +47,18 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
class DashboardSettings:
|
||||
def __init__(self):
|
||||
self.config_dir = ''
|
||||
self.password_hash = ''
|
||||
self.username = ''
|
||||
self.config_dir = ""
|
||||
self.password_hash = ""
|
||||
self.username = ""
|
||||
self.using_password = False
|
||||
self.on_hassio = False
|
||||
self.cookie_secret = None
|
||||
|
||||
def parse_args(self, args):
|
||||
self.on_hassio = args.hassio
|
||||
password = args.password or os.getenv('PASSWORD', '')
|
||||
password = args.password or os.getenv("PASSWORD", "")
|
||||
if not self.on_hassio:
|
||||
self.username = args.username or os.getenv('USERNAME', '')
|
||||
self.username = args.username or os.getenv("USERNAME", "")
|
||||
self.using_password = bool(password)
|
||||
if self.using_password:
|
||||
self.password_hash = password_hash(password)
|
||||
@@ -61,17 +66,17 @@ class DashboardSettings:
|
||||
|
||||
@property
|
||||
def relative_url(self):
|
||||
return os.getenv('ESPHOME_DASHBOARD_RELATIVE_URL', '/')
|
||||
return os.getenv("ESPHOME_DASHBOARD_RELATIVE_URL", "/")
|
||||
|
||||
@property
|
||||
def status_use_ping(self):
|
||||
return get_bool_env('ESPHOME_DASHBOARD_USE_PING')
|
||||
return get_bool_env("ESPHOME_DASHBOARD_USE_PING")
|
||||
|
||||
@property
|
||||
def using_hassio_auth(self):
|
||||
if not self.on_hassio:
|
||||
return False
|
||||
return not get_bool_env('DISABLE_HA_AUTHENTICATION')
|
||||
return not get_bool_env("DISABLE_HA_AUTHENTICATION")
|
||||
|
||||
@property
|
||||
def using_auth(self):
|
||||
@@ -84,10 +89,7 @@ class DashboardSettings:
|
||||
return False
|
||||
|
||||
# Compare password in constant running time (to prevent timing attacks)
|
||||
return hmac.compare_digest(
|
||||
self.password_hash,
|
||||
password_hash(password)
|
||||
)
|
||||
return hmac.compare_digest(self.password_hash, password_hash(password))
|
||||
|
||||
def rel_path(self, *args):
|
||||
return os.path.join(self.config_dir, *args)
|
||||
@@ -98,24 +100,24 @@ class DashboardSettings:
|
||||
|
||||
settings = DashboardSettings()
|
||||
|
||||
cookie_authenticated_yes = b'yes'
|
||||
cookie_authenticated_yes = b"yes"
|
||||
|
||||
|
||||
def template_args():
|
||||
version = const.__version__
|
||||
if 'b' in version:
|
||||
docs_link = 'https://beta.esphome.io/'
|
||||
elif 'dev' in version:
|
||||
docs_link = 'https://next.esphome.io/'
|
||||
if "b" in version:
|
||||
docs_link = "https://beta.esphome.io/"
|
||||
elif "dev" in version:
|
||||
docs_link = "https://next.esphome.io/"
|
||||
else:
|
||||
docs_link = 'https://www.esphome.io/'
|
||||
docs_link = "https://www.esphome.io/"
|
||||
return {
|
||||
'version': version,
|
||||
'docs_link': docs_link,
|
||||
'get_static_file_url': get_static_file_url,
|
||||
'relative_url': settings.relative_url,
|
||||
'streamer_mode': get_bool_env('ESPHOME_STREAMER_MODE'),
|
||||
'config_dir': settings.config_dir,
|
||||
"version": version,
|
||||
"docs_link": docs_link,
|
||||
"get_static_file_url": get_static_file_url,
|
||||
"relative_url": settings.relative_url,
|
||||
"streamer_mode": get_bool_env("ESPHOME_STREAMER_MODE"),
|
||||
"config_dir": settings.config_dir,
|
||||
}
|
||||
|
||||
|
||||
@@ -123,9 +125,10 @@ def authenticated(func):
|
||||
@functools.wraps(func)
|
||||
def decorator(self, *args, **kwargs):
|
||||
if not is_authenticated(self):
|
||||
self.redirect('./login')
|
||||
self.redirect("./login")
|
||||
return None
|
||||
return func(self, *args, **kwargs)
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
@@ -133,23 +136,27 @@ def is_authenticated(request_handler):
|
||||
if settings.on_hassio:
|
||||
# Handle ingress - disable auth on ingress port
|
||||
# X-Hassio-Ingress is automatically stripped on the non-ingress server in nginx
|
||||
header = request_handler.request.headers.get('X-Hassio-Ingress', 'NO')
|
||||
if str(header) == 'YES':
|
||||
header = request_handler.request.headers.get("X-Hassio-Ingress", "NO")
|
||||
if str(header) == "YES":
|
||||
return True
|
||||
if settings.using_auth:
|
||||
return request_handler.get_secure_cookie('authenticated') == cookie_authenticated_yes
|
||||
return (
|
||||
request_handler.get_secure_cookie("authenticated")
|
||||
== cookie_authenticated_yes
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
def bind_config(func):
|
||||
def decorator(self, *args, **kwargs):
|
||||
configuration = self.get_argument('configuration')
|
||||
configuration = self.get_argument("configuration")
|
||||
if not is_allowed(configuration):
|
||||
self.set_status(500)
|
||||
return None
|
||||
kwargs = kwargs.copy()
|
||||
kwargs['configuration'] = configuration
|
||||
kwargs["configuration"] = configuration
|
||||
return func(self, *args, **kwargs)
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
@@ -160,7 +167,7 @@ class BaseHandler(tornado.web.RequestHandler):
|
||||
|
||||
def websocket_class(cls):
|
||||
# pylint: disable=protected-access
|
||||
if not hasattr(cls, '_message_handlers'):
|
||||
if not hasattr(cls, "_message_handlers"):
|
||||
cls._message_handlers = {}
|
||||
|
||||
for _, method in cls.__dict__.items():
|
||||
@@ -175,6 +182,7 @@ def websocket_method(name):
|
||||
# pylint: disable=protected-access
|
||||
fn._message_handler = name
|
||||
return fn
|
||||
|
||||
return wrap
|
||||
|
||||
|
||||
@@ -190,7 +198,7 @@ class EsphomeCommandWebSocket(tornado.websocket.WebSocketHandler):
|
||||
def on_message(self, message):
|
||||
# Messages are always JSON, 500 when not
|
||||
json_message = json.loads(message)
|
||||
type_ = json_message['type']
|
||||
type_ = json_message["type"]
|
||||
# pylint: disable=no-member
|
||||
handlers = type(self)._message_handlers
|
||||
if type_ not in handlers:
|
||||
@@ -199,17 +207,19 @@ class EsphomeCommandWebSocket(tornado.websocket.WebSocketHandler):
|
||||
|
||||
handlers[type_](self, json_message)
|
||||
|
||||
@websocket_method('spawn')
|
||||
@websocket_method("spawn")
|
||||
def handle_spawn(self, json_message):
|
||||
if self._proc is not None:
|
||||
# spawn can only be called once
|
||||
return
|
||||
command = self.build_command(json_message)
|
||||
_LOGGER.info("Running command '%s'", ' '.join(shlex_quote(x) for x in command))
|
||||
self._proc = tornado.process.Subprocess(command,
|
||||
stdout=tornado.process.Subprocess.STREAM,
|
||||
stderr=subprocess.STDOUT,
|
||||
stdin=tornado.process.Subprocess.STREAM)
|
||||
_LOGGER.info("Running command '%s'", " ".join(shlex_quote(x) for x in command))
|
||||
self._proc = tornado.process.Subprocess(
|
||||
command,
|
||||
stdout=tornado.process.Subprocess.STREAM,
|
||||
stderr=subprocess.STDOUT,
|
||||
stdin=tornado.process.Subprocess.STREAM,
|
||||
)
|
||||
self._proc.set_exit_callback(self._proc_on_exit)
|
||||
tornado.ioloop.IOLoop.current().spawn_callback(self._redirect_stdout)
|
||||
|
||||
@@ -217,34 +227,34 @@ class EsphomeCommandWebSocket(tornado.websocket.WebSocketHandler):
|
||||
def is_process_active(self):
|
||||
return self._proc is not None and self._proc.returncode is None
|
||||
|
||||
@websocket_method('stdin')
|
||||
@websocket_method("stdin")
|
||||
def handle_stdin(self, json_message):
|
||||
if not self.is_process_active:
|
||||
return
|
||||
data = json_message['data']
|
||||
data = codecs.encode(data, 'utf8', 'replace')
|
||||
data = json_message["data"]
|
||||
data = codecs.encode(data, "utf8", "replace")
|
||||
_LOGGER.debug("< stdin: %s", data)
|
||||
self._proc.stdin.write(data)
|
||||
|
||||
@tornado.gen.coroutine
|
||||
def _redirect_stdout(self):
|
||||
reg = b'[\n\r]'
|
||||
reg = b"[\n\r]"
|
||||
|
||||
while True:
|
||||
try:
|
||||
data = yield self._proc.stdout.read_until_regex(reg)
|
||||
except tornado.iostream.StreamClosedError:
|
||||
break
|
||||
data = codecs.decode(data, 'utf8', 'replace')
|
||||
data = codecs.decode(data, "utf8", "replace")
|
||||
|
||||
_LOGGER.debug("> stdout: %s", data)
|
||||
self.write_message({'event': 'line', 'data': data})
|
||||
self.write_message({"event": "line", "data": data})
|
||||
|
||||
def _proc_on_exit(self, returncode):
|
||||
if not self._is_closed:
|
||||
# Check if the proc was not forcibly closed
|
||||
_LOGGER.info("Process exited with return code %s", returncode)
|
||||
self.write_message({'event': 'exit', 'code': returncode})
|
||||
self.write_message({"event": "exit", "code": returncode})
|
||||
|
||||
def on_close(self):
|
||||
# Check if proc exists (if 'start' has been run)
|
||||
@@ -260,45 +270,57 @@ class EsphomeCommandWebSocket(tornado.websocket.WebSocketHandler):
|
||||
|
||||
class EsphomeLogsHandler(EsphomeCommandWebSocket):
|
||||
def build_command(self, json_message):
|
||||
config_file = settings.rel_path(json_message['configuration'])
|
||||
return ["esphome", "--dashboard", config_file, "logs", '--serial-port',
|
||||
json_message["port"]]
|
||||
config_file = settings.rel_path(json_message["configuration"])
|
||||
return [
|
||||
"esphome",
|
||||
"--dashboard",
|
||||
config_file,
|
||||
"logs",
|
||||
"--serial-port",
|
||||
json_message["port"],
|
||||
]
|
||||
|
||||
|
||||
class EsphomeUploadHandler(EsphomeCommandWebSocket):
|
||||
def build_command(self, json_message):
|
||||
config_file = settings.rel_path(json_message['configuration'])
|
||||
return ["esphome", "--dashboard", config_file, "run", '--upload-port',
|
||||
json_message["port"]]
|
||||
config_file = settings.rel_path(json_message["configuration"])
|
||||
return [
|
||||
"esphome",
|
||||
"--dashboard",
|
||||
config_file,
|
||||
"run",
|
||||
"--upload-port",
|
||||
json_message["port"],
|
||||
]
|
||||
|
||||
|
||||
class EsphomeCompileHandler(EsphomeCommandWebSocket):
|
||||
def build_command(self, json_message):
|
||||
config_file = settings.rel_path(json_message['configuration'])
|
||||
config_file = settings.rel_path(json_message["configuration"])
|
||||
return ["esphome", "--dashboard", config_file, "compile"]
|
||||
|
||||
|
||||
class EsphomeValidateHandler(EsphomeCommandWebSocket):
|
||||
def build_command(self, json_message):
|
||||
config_file = settings.rel_path(json_message['configuration'])
|
||||
config_file = settings.rel_path(json_message["configuration"])
|
||||
return ["esphome", "--dashboard", config_file, "config"]
|
||||
|
||||
|
||||
class EsphomeCleanMqttHandler(EsphomeCommandWebSocket):
|
||||
def build_command(self, json_message):
|
||||
config_file = settings.rel_path(json_message['configuration'])
|
||||
config_file = settings.rel_path(json_message["configuration"])
|
||||
return ["esphome", "--dashboard", config_file, "clean-mqtt"]
|
||||
|
||||
|
||||
class EsphomeCleanHandler(EsphomeCommandWebSocket):
|
||||
def build_command(self, json_message):
|
||||
config_file = settings.rel_path(json_message['configuration'])
|
||||
config_file = settings.rel_path(json_message["configuration"])
|
||||
return ["esphome", "--dashboard", config_file, "clean"]
|
||||
|
||||
|
||||
class EsphomeVscodeHandler(EsphomeCommandWebSocket):
|
||||
def build_command(self, json_message):
|
||||
return ["esphome", "--dashboard", "-q", 'dummy', "vscode"]
|
||||
return ["esphome", "--dashboard", "-q", "dummy", "vscode"]
|
||||
|
||||
|
||||
class EsphomeAceEditorHandler(EsphomeCommandWebSocket):
|
||||
@@ -318,15 +340,15 @@ class SerialPortRequestHandler(BaseHandler):
|
||||
data = []
|
||||
for port in ports:
|
||||
desc = port.description
|
||||
if port.path == '/dev/ttyAMA0':
|
||||
desc = 'UART pins on GPIO header'
|
||||
split_desc = desc.split(' - ')
|
||||
if port.path == "/dev/ttyAMA0":
|
||||
desc = "UART pins on GPIO header"
|
||||
split_desc = desc.split(" - ")
|
||||
if len(split_desc) == 2 and split_desc[0] == split_desc[1]:
|
||||
# Some serial ports repeat their values
|
||||
desc = split_desc[0]
|
||||
data.append({'port': port.path, 'desc': desc})
|
||||
data.append({'port': 'OTA', 'desc': 'Over-The-Air'})
|
||||
data.sort(key=lambda x: x['port'], reverse=True)
|
||||
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.write(json.dumps(data))
|
||||
|
||||
|
||||
@@ -336,12 +358,11 @@ 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()
|
||||
}
|
||||
destination = settings.rel_path(kwargs['name'] + '.yaml')
|
||||
destination = settings.rel_path(kwargs["name"] + ".yaml")
|
||||
wizard.wizard_write(path=destination, **kwargs)
|
||||
self.redirect('./?begin=True')
|
||||
self.redirect("./?begin=True")
|
||||
|
||||
|
||||
class DownloadBinaryRequestHandler(BaseHandler):
|
||||
@@ -356,10 +377,10 @@ class DownloadBinaryRequestHandler(BaseHandler):
|
||||
return
|
||||
|
||||
path = storage_json.firmware_bin_path
|
||||
self.set_header('Content-Type', 'application/octet-stream')
|
||||
filename = f'{storage_json.name}.bin'
|
||||
self.set_header("Content-Type", "application/octet-stream")
|
||||
filename = f"{storage_json.name}.bin"
|
||||
self.set_header("Content-Disposition", f'attachment; filename="{filename}"')
|
||||
with open(path, 'rb') as f:
|
||||
with open(path, "rb") as f:
|
||||
while True:
|
||||
data = f.read(16384)
|
||||
if not data:
|
||||
@@ -386,7 +407,9 @@ class DashboardEntry:
|
||||
@property
|
||||
def storage(self): # type: () -> Optional[StorageJSON]
|
||||
if not self._loaded_storage:
|
||||
self._storage = StorageJSON.load(ext_storage_path(settings.config_dir, self.filename))
|
||||
self._storage = StorageJSON.load(
|
||||
ext_storage_path(settings.config_dir, self.filename)
|
||||
)
|
||||
self._loaded_storage = True
|
||||
return self._storage
|
||||
|
||||
@@ -399,7 +422,7 @@ class DashboardEntry:
|
||||
@property
|
||||
def name(self):
|
||||
if self.storage is None:
|
||||
return self.filename[:-len('.yaml')]
|
||||
return self.filename[: -len(".yaml")]
|
||||
return self.storage.name
|
||||
|
||||
@property
|
||||
@@ -429,8 +452,8 @@ class DashboardEntry:
|
||||
@property
|
||||
def update_old(self):
|
||||
if self.storage is None:
|
||||
return ''
|
||||
return self.storage.esphome_version or ''
|
||||
return ""
|
||||
return self.storage.esphome_version or ""
|
||||
|
||||
@property
|
||||
def update_new(self):
|
||||
@@ -446,18 +469,23 @@ class DashboardEntry:
|
||||
class MainRequestHandler(BaseHandler):
|
||||
@authenticated
|
||||
def get(self):
|
||||
begin = bool(self.get_argument('begin', False))
|
||||
begin = bool(self.get_argument("begin", False))
|
||||
entries = _list_dashboard_entries()
|
||||
|
||||
self.render("templates/index.html", entries=entries, begin=begin,
|
||||
**template_args(), login_enabled=settings.using_auth)
|
||||
self.render(
|
||||
"templates/index.html",
|
||||
entries=entries,
|
||||
begin=begin,
|
||||
**template_args(),
|
||||
login_enabled=settings.using_auth,
|
||||
)
|
||||
|
||||
|
||||
def _ping_func(filename, address):
|
||||
if os.name == 'nt':
|
||||
command = ['ping', '-n', '1', address]
|
||||
if os.name == "nt":
|
||||
command = ["ping", "-n", "1", address]
|
||||
else:
|
||||
command = ['ping', '-c', '1', address]
|
||||
command = ["ping", "-c", "1", address]
|
||||
rc, _, _ = run_system_command(*command)
|
||||
return filename, rc == 0
|
||||
|
||||
@@ -474,7 +502,9 @@ class MDNSStatusThread(threading.Thread):
|
||||
stat.start()
|
||||
while not STOP_EVENT.is_set():
|
||||
entries = _list_dashboard_entries()
|
||||
stat.request_query({entry.filename: entry.name + '.local.' for entry in entries})
|
||||
stat.request_query(
|
||||
{entry.filename: entry.name + ".local." for entry in entries}
|
||||
)
|
||||
|
||||
PING_REQUEST.wait()
|
||||
PING_REQUEST.clear()
|
||||
@@ -499,8 +529,9 @@ class PingStatusThread(threading.Thread):
|
||||
PING_RESULT[entry.filename] = None
|
||||
continue
|
||||
|
||||
result = pool.apply_async(_ping_func, (entry.filename, entry.address),
|
||||
callback=callback)
|
||||
result = pool.apply_async(
|
||||
_ping_func, (entry.filename, entry.address), callback=callback
|
||||
)
|
||||
queue.append(result)
|
||||
|
||||
while queue:
|
||||
@@ -541,10 +572,10 @@ class EditRequestHandler(BaseHandler):
|
||||
@bind_config
|
||||
def get(self, configuration=None):
|
||||
filename = settings.rel_path(configuration)
|
||||
content = ''
|
||||
content = ""
|
||||
if os.path.isfile(filename):
|
||||
# pylint: disable=no-value-for-parameter
|
||||
with open(filename, 'r') as f:
|
||||
with open(filename, "r") as f:
|
||||
content = f.read()
|
||||
self.write(content)
|
||||
|
||||
@@ -552,7 +583,7 @@ class EditRequestHandler(BaseHandler):
|
||||
@bind_config
|
||||
def post(self, configuration=None):
|
||||
# pylint: disable=no-value-for-parameter
|
||||
with open(settings.rel_path(configuration), 'wb') as f:
|
||||
with open(settings.rel_path(configuration), "wb") as f:
|
||||
f.write(self.request.body)
|
||||
self.set_status(200)
|
||||
|
||||
@@ -596,29 +627,34 @@ PING_REQUEST = threading.Event()
|
||||
class LoginHandler(BaseHandler):
|
||||
def get(self):
|
||||
if is_authenticated(self):
|
||||
self.redirect('/')
|
||||
self.redirect("/")
|
||||
else:
|
||||
self.render_login_page()
|
||||
|
||||
def render_login_page(self, error=None):
|
||||
self.render("templates/login.html", error=error, hassio=settings.using_hassio_auth,
|
||||
has_username=bool(settings.username), **template_args())
|
||||
self.render(
|
||||
"templates/login.html",
|
||||
error=error,
|
||||
hassio=settings.using_hassio_auth,
|
||||
has_username=bool(settings.username),
|
||||
**template_args(),
|
||||
)
|
||||
|
||||
def post_hassio_login(self):
|
||||
import requests
|
||||
|
||||
headers = {
|
||||
'X-HASSIO-KEY': os.getenv('HASSIO_TOKEN'),
|
||||
"X-HASSIO-KEY": os.getenv("HASSIO_TOKEN"),
|
||||
}
|
||||
data = {
|
||||
'username': self.get_argument('username', ''),
|
||||
'password': self.get_argument('password', '')
|
||||
"username": self.get_argument("username", ""),
|
||||
"password": self.get_argument("password", ""),
|
||||
}
|
||||
try:
|
||||
req = requests.post('http://hassio/auth', headers=headers, data=data)
|
||||
req = requests.post("http://hassio/auth", headers=headers, data=data)
|
||||
if req.status_code == 200:
|
||||
self.set_secure_cookie("authenticated", cookie_authenticated_yes)
|
||||
self.redirect('/')
|
||||
self.redirect("/")
|
||||
return
|
||||
except Exception as err: # pylint: disable=broad-except
|
||||
_LOGGER.warning("Error during Hass.io auth request: %s", err)
|
||||
@@ -629,13 +665,15 @@ class LoginHandler(BaseHandler):
|
||||
self.render_login_page(error="Invalid username or password")
|
||||
|
||||
def post_native_login(self):
|
||||
username = self.get_argument("username", '')
|
||||
password = self.get_argument("password", '')
|
||||
username = self.get_argument("username", "")
|
||||
password = self.get_argument("password", "")
|
||||
if settings.check_password(username, password):
|
||||
self.set_secure_cookie("authenticated", cookie_authenticated_yes)
|
||||
self.redirect("/")
|
||||
return
|
||||
error_str = "Invalid username or password" if settings.username else "Invalid password"
|
||||
error_str = (
|
||||
"Invalid username or password" if settings.username else "Invalid password"
|
||||
)
|
||||
self.set_status(401)
|
||||
self.render_login_page(error=error_str)
|
||||
|
||||
@@ -650,22 +688,22 @@ class LogoutHandler(BaseHandler):
|
||||
@authenticated
|
||||
def get(self):
|
||||
self.clear_cookie("authenticated")
|
||||
self.redirect('./login')
|
||||
self.redirect("./login")
|
||||
|
||||
|
||||
_STATIC_FILE_HASHES = {}
|
||||
|
||||
|
||||
def get_static_file_url(name):
|
||||
static_path = os.path.join(os.path.dirname(__file__), 'static')
|
||||
static_path = os.path.join(os.path.dirname(__file__), "static")
|
||||
if name in _STATIC_FILE_HASHES:
|
||||
hash_ = _STATIC_FILE_HASHES[name]
|
||||
else:
|
||||
path = os.path.join(static_path, name)
|
||||
with open(path, 'rb') as f_handle:
|
||||
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_}'
|
||||
return f"./static/{name}?hash={hash_}"
|
||||
|
||||
|
||||
def make_app(debug=False):
|
||||
@@ -684,44 +722,53 @@ def make_app(debug=False):
|
||||
|
||||
request_time = 1000.0 * handler.request.request_time()
|
||||
# pylint: disable=protected-access
|
||||
log_method("%d %s %.2fms", handler.get_status(),
|
||||
handler._request_summary(), request_time)
|
||||
log_method(
|
||||
"%d %s %.2fms",
|
||||
handler.get_status(),
|
||||
handler._request_summary(),
|
||||
request_time,
|
||||
)
|
||||
|
||||
class StaticFileHandler(tornado.web.StaticFileHandler):
|
||||
def set_extra_headers(self, path):
|
||||
if debug:
|
||||
self.set_header('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||
self.set_header(
|
||||
"Cache-Control", "no-store, no-cache, must-revalidate, max-age=0"
|
||||
)
|
||||
|
||||
static_path = os.path.join(os.path.dirname(__file__), 'static')
|
||||
static_path = os.path.join(os.path.dirname(__file__), "static")
|
||||
app_settings = {
|
||||
'debug': debug,
|
||||
'cookie_secret': settings.cookie_secret,
|
||||
'log_function': log_function,
|
||||
'websocket_ping_interval': 30.0,
|
||||
"debug": debug,
|
||||
"cookie_secret": settings.cookie_secret,
|
||||
"log_function": log_function,
|
||||
"websocket_ping_interval": 30.0,
|
||||
}
|
||||
rel = settings.relative_url
|
||||
app = tornado.web.Application([
|
||||
(rel + "", MainRequestHandler),
|
||||
(rel + "login", LoginHandler),
|
||||
(rel + "logout", LogoutHandler),
|
||||
(rel + "logs", EsphomeLogsHandler),
|
||||
(rel + "upload", EsphomeUploadHandler),
|
||||
(rel + "compile", EsphomeCompileHandler),
|
||||
(rel + "validate", EsphomeValidateHandler),
|
||||
(rel + "clean-mqtt", EsphomeCleanMqttHandler),
|
||||
(rel + "clean", EsphomeCleanHandler),
|
||||
(rel + "vscode", EsphomeVscodeHandler),
|
||||
(rel + "ace", EsphomeAceEditorHandler),
|
||||
(rel + "update-all", EsphomeUpdateAllHandler),
|
||||
(rel + "edit", EditRequestHandler),
|
||||
(rel + "download.bin", DownloadBinaryRequestHandler),
|
||||
(rel + "serial-ports", SerialPortRequestHandler),
|
||||
(rel + "ping", PingRequestHandler),
|
||||
(rel + "delete", DeleteRequestHandler),
|
||||
(rel + "undo-delete", UndoDeleteRequestHandler),
|
||||
(rel + "wizard.html", WizardRequestHandler),
|
||||
(rel + r"static/(.*)", StaticFileHandler, {'path': static_path}),
|
||||
], **app_settings)
|
||||
app = tornado.web.Application(
|
||||
[
|
||||
(rel + "", MainRequestHandler),
|
||||
(rel + "login", LoginHandler),
|
||||
(rel + "logout", LogoutHandler),
|
||||
(rel + "logs", EsphomeLogsHandler),
|
||||
(rel + "upload", EsphomeUploadHandler),
|
||||
(rel + "compile", EsphomeCompileHandler),
|
||||
(rel + "validate", EsphomeValidateHandler),
|
||||
(rel + "clean-mqtt", EsphomeCleanMqttHandler),
|
||||
(rel + "clean", EsphomeCleanHandler),
|
||||
(rel + "vscode", EsphomeVscodeHandler),
|
||||
(rel + "ace", EsphomeAceEditorHandler),
|
||||
(rel + "update-all", EsphomeUpdateAllHandler),
|
||||
(rel + "edit", EditRequestHandler),
|
||||
(rel + "download.bin", DownloadBinaryRequestHandler),
|
||||
(rel + "serial-ports", SerialPortRequestHandler),
|
||||
(rel + "ping", PingRequestHandler),
|
||||
(rel + "delete", DeleteRequestHandler),
|
||||
(rel + "undo-delete", UndoDeleteRequestHandler),
|
||||
(rel + "wizard.html", WizardRequestHandler),
|
||||
(rel + r"static/(.*)", StaticFileHandler, {"path": static_path}),
|
||||
],
|
||||
**app_settings,
|
||||
)
|
||||
|
||||
if debug:
|
||||
_STATIC_FILE_HASHES.clear()
|
||||
@@ -743,20 +790,26 @@ def start_web_server(args):
|
||||
|
||||
app = make_app(args.verbose)
|
||||
if args.socket is not None:
|
||||
_LOGGER.info("Starting dashboard web server on unix socket %s and configuration dir %s...",
|
||||
args.socket, settings.config_dir)
|
||||
_LOGGER.info(
|
||||
"Starting dashboard web server on unix socket %s and configuration dir %s...",
|
||||
args.socket,
|
||||
settings.config_dir,
|
||||
)
|
||||
server = tornado.httpserver.HTTPServer(app)
|
||||
socket = tornado.netutil.bind_unix_socket(args.socket, mode=0o666)
|
||||
server.add_socket(socket)
|
||||
else:
|
||||
_LOGGER.info("Starting dashboard web server on port %s and configuration dir %s...",
|
||||
args.port, settings.config_dir)
|
||||
_LOGGER.info(
|
||||
"Starting dashboard web server on port %s and configuration dir %s...",
|
||||
args.port,
|
||||
settings.config_dir,
|
||||
)
|
||||
app.listen(args.port)
|
||||
|
||||
if args.open_ui:
|
||||
import webbrowser
|
||||
|
||||
webbrowser.open(f'localhost:{args.port}')
|
||||
webbrowser.open(f"localhost:{args.port}")
|
||||
|
||||
if settings.status_use_ping:
|
||||
status_thread = PingStatusThread()
|
||||
|
||||
Reference in New Issue
Block a user