mirror of
https://github.com/esphome/esphome.git
synced 2024-12-22 13:34:54 +01:00
Add relative_url, streamer_mode, status_use_ping dashboard options (#461)
* Add relative_url, streamer_mode, status_use_ping dashboard options Additionally Hass.io now stores all build files in /data, so that snapshots no longer get huge. * Lint * Lint * Replace tabs with spaces
This commit is contained in:
parent
5a102c2ab7
commit
067ec30c56
11 changed files with 178 additions and 71 deletions
|
@ -6,9 +6,23 @@
|
|||
# shellcheck disable=SC1091
|
||||
source /usr/lib/hassio-addons/base.sh
|
||||
|
||||
export ESPHOME_IS_HASSIO=true
|
||||
|
||||
if hass.config.true 'leave_front_door_open'; then
|
||||
export DISABLE_HA_AUTHENTICATION=true
|
||||
fi
|
||||
|
||||
if hass.config.true 'streamer_mode'; then
|
||||
export ESPHOME_STREAMER_MODE=true
|
||||
fi
|
||||
|
||||
if hass.config.true 'status_use_ping'; then
|
||||
export ESPHOME_DASHBOARD_USE_PING=true
|
||||
fi
|
||||
|
||||
if hass.config.has_value 'relative_url'; then
|
||||
export ESPHOME_DASHBOARD_RELATIVE_URL=$(hass.config.get 'relative_url')
|
||||
fi
|
||||
|
||||
hass.log.info "Starting ESPHome dashboard..."
|
||||
exec esphome /config/esphome dashboard --socket /var/run/esphome.sock --hassio
|
||||
|
|
|
@ -171,7 +171,7 @@ def compile_program(args, config):
|
|||
|
||||
|
||||
def upload_using_esptool(config, port):
|
||||
path = os.path.join(CORE.build_path, '.pioenvs', CORE.name, 'firmware.bin')
|
||||
path = CORE.firmware_bin
|
||||
cmd = ['esptool.py', '--before', 'default_reset', '--after', 'hard_reset',
|
||||
'--chip', 'esp8266', '--port', port, 'write_flash', '0x0', path]
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ from typing import Any, Dict, List # noqa
|
|||
from esphome.const import CONF_ARDUINO_VERSION, CONF_ESPHOME, CONF_ESPHOME_CORE_VERSION, \
|
||||
CONF_LOCAL, CONF_USE_ADDRESS, CONF_WIFI, ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266, \
|
||||
CONF_REPOSITORY, CONF_BRANCH
|
||||
from esphome.helpers import ensure_unique_string
|
||||
from esphome.helpers import ensure_unique_string, is_hassio
|
||||
from esphome.py_compat import IS_PY2, integer_types
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -358,9 +358,19 @@ class EsphomeCore(object):
|
|||
path_ = os.path.expanduser(os.path.join(*path))
|
||||
return os.path.join(self.build_path, path_)
|
||||
|
||||
def relative_pioenvs_path(self, *path):
|
||||
if is_hassio():
|
||||
return os.path.join('/data', self.name, '.pioenvs', *path)
|
||||
return self.relative_build_path('.pioenvs', *path)
|
||||
|
||||
def relative_piolibdeps_path(self, *path):
|
||||
if is_hassio():
|
||||
return os.path.join('/data', self.name, '.piolibdeps', *path)
|
||||
return self.relative_build_path('.piolibdeps', *path)
|
||||
|
||||
@property
|
||||
def firmware_bin(self):
|
||||
return self.relative_build_path('.pioenvs', self.name, 'firmware.bin')
|
||||
return self.relative_pioenvs_path(self.name, 'firmware.bin')
|
||||
|
||||
@property
|
||||
def is_esp8266(self):
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
from __future__ import print_function
|
||||
|
||||
import codecs
|
||||
import collections
|
||||
import hmac
|
||||
import json
|
||||
import logging
|
||||
import multiprocessing
|
||||
import os
|
||||
import subprocess
|
||||
import threading
|
||||
|
@ -23,7 +25,7 @@ import tornado.websocket
|
|||
|
||||
from esphome import const
|
||||
from esphome.__main__ import get_serial_ports
|
||||
from esphome.helpers import mkdir_p
|
||||
from esphome.helpers import mkdir_p, get_bool_env, run_system_command
|
||||
from esphome.py_compat import IS_PY2
|
||||
from esphome.storage_json import EsphomeStorageJSON, StorageJSON, \
|
||||
esphome_storage_path, ext_storage_path
|
||||
|
@ -42,6 +44,8 @@ USING_PASSWORD = False
|
|||
ON_HASSIO = False
|
||||
USING_HASSIO_AUTH = True
|
||||
HASSIO_MQTT_CONFIG = None
|
||||
RELATIVE_URL = os.getenv('ESPHOME_DASHBOARD_RELATIVE_URL', '/')
|
||||
STATUS_USE_PING = get_bool_env('ESPHOME_DASHBOARD_USE_PING')
|
||||
|
||||
if IS_PY2:
|
||||
cookie_authenticated_yes = 'yes'
|
||||
|
@ -49,6 +53,17 @@ else:
|
|||
cookie_authenticated_yes = b'yes'
|
||||
|
||||
|
||||
def template_args():
|
||||
version = const.__version__
|
||||
return {
|
||||
'version': version,
|
||||
'docs_link': 'https://beta.esphome.io/' if 'b' in version else 'https://esphome.io/',
|
||||
'get_static_file_url': get_static_file_url,
|
||||
'relative_url': RELATIVE_URL,
|
||||
'streamer_mode': get_bool_env('ESPHOME_STREAMER_MODE'),
|
||||
}
|
||||
|
||||
|
||||
# pylint: disable=abstract-method
|
||||
class BaseHandler(tornado.web.RequestHandler):
|
||||
def is_authenticated(self):
|
||||
|
@ -165,7 +180,7 @@ class EsphomeHassConfigHandler(EsphomeCommandWebSocket):
|
|||
class SerialPortRequestHandler(BaseHandler):
|
||||
def get(self):
|
||||
if not self.is_authenticated():
|
||||
self.redirect('/login')
|
||||
self.redirect(RELATIVE_URL + 'login')
|
||||
return
|
||||
ports = get_serial_ports()
|
||||
data = []
|
||||
|
@ -187,7 +202,7 @@ class WizardRequestHandler(BaseHandler):
|
|||
from esphome import wizard
|
||||
|
||||
if not self.is_authenticated():
|
||||
self.redirect('/login')
|
||||
self.redirect(RELATIVE_URL + 'login')
|
||||
return
|
||||
kwargs = {k: ''.join(v) for k, v in self.request.arguments.items()}
|
||||
destination = os.path.join(CONFIG_DIR, kwargs['name'] + '.yaml')
|
||||
|
@ -198,7 +213,7 @@ class WizardRequestHandler(BaseHandler):
|
|||
class DownloadBinaryRequestHandler(BaseHandler):
|
||||
def get(self):
|
||||
if not self.is_authenticated():
|
||||
self.redirect('/login')
|
||||
self.redirect(RELATIVE_URL + 'login')
|
||||
return
|
||||
|
||||
# pylint: disable=no-value-for-parameter
|
||||
|
@ -214,8 +229,8 @@ class DownloadBinaryRequestHandler(BaseHandler):
|
|||
filename = '{}.bin'.format(storage_json.name)
|
||||
self.set_header("Content-Disposition", 'attachment; filename="{}"'.format(filename))
|
||||
with open(path, 'rb') as f:
|
||||
while 1:
|
||||
data = f.read(16384) # or some other nice-sized chunk
|
||||
while True:
|
||||
data = f.read(16384)
|
||||
if not data:
|
||||
break
|
||||
self.write(data)
|
||||
|
@ -302,21 +317,26 @@ class DashboardEntry(object):
|
|||
class MainRequestHandler(BaseHandler):
|
||||
def get(self):
|
||||
if not self.is_authenticated():
|
||||
self.redirect('/login')
|
||||
self.redirect(RELATIVE_URL + 'login')
|
||||
return
|
||||
|
||||
begin = bool(self.get_argument('begin', False))
|
||||
entries = _list_dashboard_entries()
|
||||
version = const.__version__
|
||||
docs_link = 'https://beta.esphome.io/' if 'b' in version else \
|
||||
'https://esphome.io/'
|
||||
|
||||
self.render("templates/index.html", entries=entries,
|
||||
version=version, begin=begin, docs_link=docs_link,
|
||||
get_static_file_url=get_static_file_url)
|
||||
self.render("templates/index.html", entries=entries, begin=begin,
|
||||
**template_args())
|
||||
|
||||
|
||||
class PingThread(threading.Thread):
|
||||
def _ping_func(filename, address):
|
||||
if os.name == 'nt':
|
||||
command = ['ping', '-n', '1', address]
|
||||
else:
|
||||
command = ['ping', '-c', '1', address]
|
||||
rc, _, _ = run_system_command(*command)
|
||||
return filename, rc == 0
|
||||
|
||||
|
||||
class MDNSStatusThread(threading.Thread):
|
||||
def run(self):
|
||||
zc = Zeroconf()
|
||||
|
||||
|
@ -337,10 +357,52 @@ class PingThread(threading.Thread):
|
|||
zc.close()
|
||||
|
||||
|
||||
class PingStatusThread(threading.Thread):
|
||||
def run(self):
|
||||
pool = multiprocessing.Pool(processes=8)
|
||||
while not STOP_EVENT.is_set():
|
||||
# Only do pings if somebody has the dashboard open
|
||||
|
||||
def callback(ret):
|
||||
PING_RESULT[ret[0]] = ret[1]
|
||||
|
||||
entries = _list_dashboard_entries()
|
||||
queue = collections.deque()
|
||||
for entry in entries:
|
||||
if entry.address is None:
|
||||
PING_RESULT[entry.filename] = None
|
||||
continue
|
||||
|
||||
result = pool.apply_async(_ping_func, (entry.filename, entry.address),
|
||||
callback=callback)
|
||||
queue.append(result)
|
||||
|
||||
while queue:
|
||||
item = queue[0]
|
||||
if item.ready():
|
||||
queue.popleft()
|
||||
continue
|
||||
|
||||
try:
|
||||
item.get(0.1)
|
||||
except OSError:
|
||||
# ping not installed
|
||||
pass
|
||||
except multiprocessing.TimeoutError:
|
||||
pass
|
||||
|
||||
if STOP_EVENT.is_set():
|
||||
pool.terminate()
|
||||
return
|
||||
|
||||
PING_REQUEST.wait()
|
||||
PING_REQUEST.clear()
|
||||
|
||||
|
||||
class PingRequestHandler(BaseHandler):
|
||||
def get(self):
|
||||
if not self.is_authenticated():
|
||||
self.redirect('/login')
|
||||
self.redirect(RELATIVE_URL + 'login')
|
||||
return
|
||||
|
||||
PING_REQUEST.set()
|
||||
|
@ -354,7 +416,7 @@ def is_allowed(configuration):
|
|||
class EditRequestHandler(BaseHandler):
|
||||
def get(self):
|
||||
if not self.is_authenticated():
|
||||
self.redirect('/login')
|
||||
self.redirect(RELATIVE_URL + 'login')
|
||||
return
|
||||
# pylint: disable=no-value-for-parameter
|
||||
configuration = self.get_argument('configuration')
|
||||
|
@ -368,7 +430,7 @@ class EditRequestHandler(BaseHandler):
|
|||
|
||||
def post(self):
|
||||
if not self.is_authenticated():
|
||||
self.redirect('/login')
|
||||
self.redirect(RELATIVE_URL + 'login')
|
||||
return
|
||||
# pylint: disable=no-value-for-parameter
|
||||
configuration = self.get_argument('configuration')
|
||||
|
@ -392,18 +454,13 @@ class LoginHandler(BaseHandler):
|
|||
if USING_HASSIO_AUTH:
|
||||
self.render_hassio_login()
|
||||
return
|
||||
self.write('<html><body><form action="/login" method="post">'
|
||||
self.write('<html><body><form action="' + RELATIVE_URL + 'login" method="post">'
|
||||
'Password: <input type="password" name="password">'
|
||||
'<input type="submit" value="Sign in">'
|
||||
'</form></body></html>')
|
||||
|
||||
def render_hassio_login(self, error=None):
|
||||
version = const.__version__
|
||||
docs_link = 'https://beta.esphome.io/' if 'b' in version else \
|
||||
'https://esphome.io/'
|
||||
|
||||
self.render("templates/login.html", version=version, docs_link=docs_link, error=error,
|
||||
get_static_file_url=get_static_file_url)
|
||||
self.render("templates/login.html", error=error, **template_args())
|
||||
|
||||
def post_hassio_login(self):
|
||||
import requests
|
||||
|
@ -454,9 +511,9 @@ def get_static_file_url(name):
|
|||
else:
|
||||
path = os.path.join(static_path, name)
|
||||
with open(path, 'rb') as f_handle:
|
||||
hash_ = hash(f_handle.read())
|
||||
hash_ = hash(f_handle.read()) & (2**32-1)
|
||||
_STATIC_FILE_HASHES[name] = hash_
|
||||
return u'/static/{}?hash={}'.format(name, hash_)
|
||||
return RELATIVE_URL + u'static/{}?hash={:08X}'.format(name, hash_)
|
||||
|
||||
|
||||
def make_app(debug=False):
|
||||
|
@ -491,21 +548,21 @@ def make_app(debug=False):
|
|||
'websocket_ping_interval': 30.0,
|
||||
}
|
||||
app = tornado.web.Application([
|
||||
(r"/", MainRequestHandler),
|
||||
(r"/login", LoginHandler),
|
||||
(r"/logs", EsphomeLogsHandler),
|
||||
(r"/run", EsphomeRunHandler),
|
||||
(r"/compile", EsphomeCompileHandler),
|
||||
(r"/validate", EsphomeValidateHandler),
|
||||
(r"/clean-mqtt", EsphomeCleanMqttHandler),
|
||||
(r"/clean", EsphomeCleanHandler),
|
||||
(r"/hass-config", EsphomeHassConfigHandler),
|
||||
(r"/edit", EditRequestHandler),
|
||||
(r"/download.bin", DownloadBinaryRequestHandler),
|
||||
(r"/serial-ports", SerialPortRequestHandler),
|
||||
(r"/ping", PingRequestHandler),
|
||||
(r"/wizard.html", WizardRequestHandler),
|
||||
(r'/static/(.*)', StaticFileHandler, {'path': static_path}),
|
||||
(RELATIVE_URL + "", MainRequestHandler),
|
||||
(RELATIVE_URL + "login", LoginHandler),
|
||||
(RELATIVE_URL + "logs", EsphomeLogsHandler),
|
||||
(RELATIVE_URL + "run", EsphomeRunHandler),
|
||||
(RELATIVE_URL + "compile", EsphomeCompileHandler),
|
||||
(RELATIVE_URL + "validate", EsphomeValidateHandler),
|
||||
(RELATIVE_URL + "clean-mqtt", EsphomeCleanMqttHandler),
|
||||
(RELATIVE_URL + "clean", EsphomeCleanHandler),
|
||||
(RELATIVE_URL + "hass-config", EsphomeHassConfigHandler),
|
||||
(RELATIVE_URL + "edit", EditRequestHandler),
|
||||
(RELATIVE_URL + "download.bin", DownloadBinaryRequestHandler),
|
||||
(RELATIVE_URL + "serial-ports", SerialPortRequestHandler),
|
||||
(RELATIVE_URL + "ping", PingRequestHandler),
|
||||
(RELATIVE_URL + "wizard.html", WizardRequestHandler),
|
||||
(RELATIVE_URL + r"static/(.*)", StaticFileHandler, {'path': static_path}),
|
||||
], **settings)
|
||||
|
||||
if debug:
|
||||
|
@ -528,7 +585,7 @@ def start_web_server(args):
|
|||
|
||||
ON_HASSIO = args.hassio
|
||||
if ON_HASSIO:
|
||||
USING_HASSIO_AUTH = not bool(os.getenv('DISABLE_HA_AUTHENTICATION'))
|
||||
USING_HASSIO_AUTH = not get_bool_env('DISABLE_HA_AUTHENTICATION')
|
||||
USING_PASSWORD = False
|
||||
else:
|
||||
USING_HASSIO_AUTH = False
|
||||
|
@ -565,14 +622,17 @@ def start_web_server(args):
|
|||
|
||||
webbrowser.open('localhost:{}'.format(args.port))
|
||||
|
||||
ping_thread = PingThread()
|
||||
ping_thread.start()
|
||||
if STATUS_USE_PING:
|
||||
status_thread = PingStatusThread()
|
||||
else:
|
||||
status_thread = MDNSStatusThread()
|
||||
status_thread.start()
|
||||
try:
|
||||
tornado.ioloop.IOLoop.current().start()
|
||||
except KeyboardInterrupt:
|
||||
_LOGGER.info("Shutting down...")
|
||||
STOP_EVENT.set()
|
||||
PING_REQUEST.set()
|
||||
ping_thread.join()
|
||||
status_thread.join()
|
||||
if args.socket is not None:
|
||||
os.remove(args.socket)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// Disclaimer: This file was written in a hurry and by someone
|
||||
// who does not know JS at all. This file desperately needs cleanup.
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
M.AutoInit(document.body);
|
||||
});
|
||||
|
@ -183,7 +185,7 @@ let wsProtocol = "ws:";
|
|||
if (window.location.protocol === "https:") {
|
||||
wsProtocol = 'wss:';
|
||||
}
|
||||
const wsUrl = wsProtocol + '//' + window.location.hostname + ':' + window.location.port;
|
||||
const wsUrl = `${wsProtocol}//${window.location.hostname}:${window.location.port}${relative_url}`;
|
||||
|
||||
let isFetchingPing = false;
|
||||
const fetchPing = () => {
|
||||
|
@ -191,7 +193,7 @@ const fetchPing = () => {
|
|||
return;
|
||||
isFetchingPing = true;
|
||||
|
||||
fetch('/ping', {credentials: "same-origin"}).then(res => res.json())
|
||||
fetch(`${relative_url}ping`, {credentials: "same-origin"}).then(res => res.json())
|
||||
.then(response => {
|
||||
for (let filename in response) {
|
||||
let node = document.querySelector(`.status-indicator[data-node="${filename}"]`);
|
||||
|
@ -233,7 +235,7 @@ const portSelect = document.querySelector('.nav-wrapper select');
|
|||
let ports = [];
|
||||
|
||||
const fetchSerialPorts = (begin=false) => {
|
||||
fetch('/serial-ports', {credentials: "same-origin"}).then(res => res.json())
|
||||
fetch(`${relative_url}serial-ports`, {credentials: "same-origin"}).then(res => res.json())
|
||||
.then(response => {
|
||||
if (ports.length === response.length) {
|
||||
let allEqual = true;
|
||||
|
@ -301,7 +303,7 @@ document.querySelectorAll(".action-show-logs").forEach((showLogs) => {
|
|||
const filenameField = logsModalElem.querySelector('.filename');
|
||||
filenameField.innerHTML = configuration;
|
||||
|
||||
const logSocket = new WebSocket(wsUrl + "/logs");
|
||||
const logSocket = new WebSocket(wsUrl + "logs");
|
||||
logSocket.addEventListener('message', (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.event === "line") {
|
||||
|
@ -350,7 +352,7 @@ document.querySelectorAll(".action-upload").forEach((upload) => {
|
|||
const filenameField = uploadModalElem.querySelector('.filename');
|
||||
filenameField.innerHTML = configuration;
|
||||
|
||||
const logSocket = new WebSocket(wsUrl + "/run");
|
||||
const logSocket = new WebSocket(wsUrl + "run");
|
||||
logSocket.addEventListener('message', (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.event === "line") {
|
||||
|
@ -399,7 +401,7 @@ document.querySelectorAll(".action-validate").forEach((upload) => {
|
|||
const filenameField = validateModalElem.querySelector('.filename');
|
||||
filenameField.innerHTML = configuration;
|
||||
|
||||
const logSocket = new WebSocket(wsUrl + "/validate");
|
||||
const logSocket = new WebSocket(wsUrl + "validate");
|
||||
logSocket.addEventListener('message', (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.event === "line") {
|
||||
|
@ -457,7 +459,7 @@ document.querySelectorAll(".action-compile").forEach((upload) => {
|
|||
const filenameField = compileModalElem.querySelector('.filename');
|
||||
filenameField.innerHTML = configuration;
|
||||
|
||||
const logSocket = new WebSocket(wsUrl + "/compile");
|
||||
const logSocket = new WebSocket(wsUrl + "compile");
|
||||
logSocket.addEventListener('message', (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.event === "line") {
|
||||
|
@ -492,7 +494,7 @@ document.querySelectorAll(".action-compile").forEach((upload) => {
|
|||
downloadButton.addEventListener('click', () => {
|
||||
const link = document.createElement("a");
|
||||
link.download = name;
|
||||
link.href = '/download.bin?configuration=' + encodeURIComponent(configuration);
|
||||
link.href = `${relative_url}download.bin?configuration=${encodeURIComponent(configuration)}`;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
|
@ -515,7 +517,7 @@ document.querySelectorAll(".action-clean-mqtt").forEach((btn) => {
|
|||
const filenameField = cleanMqttModalElem.querySelector('.filename');
|
||||
filenameField.innerHTML = configuration;
|
||||
|
||||
const logSocket = new WebSocket(wsUrl + "/clean-mqtt");
|
||||
const logSocket = new WebSocket(wsUrl + "clean-mqtt");
|
||||
logSocket.addEventListener('message', (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.event === "line") {
|
||||
|
@ -557,7 +559,7 @@ document.querySelectorAll(".action-clean").forEach((btn) => {
|
|||
const filenameField = cleanModalElem.querySelector('.filename');
|
||||
filenameField.innerHTML = configuration;
|
||||
|
||||
const logSocket = new WebSocket(wsUrl + "/clean");
|
||||
const logSocket = new WebSocket(wsUrl + "clean");
|
||||
logSocket.addEventListener('message', (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.event === "line") {
|
||||
|
@ -605,7 +607,7 @@ document.querySelectorAll(".action-hass-config").forEach((btn) => {
|
|||
const filenameField = hassConfigModalElem.querySelector('.filename');
|
||||
filenameField.innerHTML = configuration;
|
||||
|
||||
const logSocket = new WebSocket(wsUrl + "/hass-config");
|
||||
const logSocket = new WebSocket(wsUrl + "hass-config");
|
||||
logSocket.addEventListener('message', (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.event === "line") {
|
||||
|
@ -646,7 +648,7 @@ editor.session.setOption('tabSize', 2);
|
|||
|
||||
const saveButton = editModalElem.querySelector(".save-button");
|
||||
const saveEditor = () => {
|
||||
fetch(`/edit?configuration=${configuration}`, {
|
||||
fetch(`${relative_url}edit?configuration=${configuration}`, {
|
||||
credentials: "same-origin",
|
||||
method: "POST",
|
||||
body: editor.getValue()
|
||||
|
@ -673,7 +675,7 @@ document.querySelectorAll(".action-edit").forEach((btn) => {
|
|||
const filenameField = editModalElem.querySelector('.filename');
|
||||
filenameField.innerHTML = configuration;
|
||||
|
||||
fetch(`/edit?configuration=${configuration}`, {credentials: "same-origin"})
|
||||
fetch(`${relative_url}edit?configuration=${configuration}`, {credentials: "same-origin"})
|
||||
.then(res => res.text()).then(response => {
|
||||
editor.setValue(response, -1);
|
||||
});
|
||||
|
|
|
@ -16,6 +16,15 @@
|
|||
|
||||
<script src="{{ get_static_file_url('materialize-stepper.min.js') }}"></script>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<script>const relative_url = "{{ relative_url }}";</script>
|
||||
|
||||
{% if streamer_mode %}
|
||||
<style>
|
||||
.log-secret {
|
||||
visibility: hidden !important;
|
||||
}
|
||||
</style>
|
||||
{% end %}
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col card s10 offset-s1 m10 offset-m1 l8 offset-l2">
|
||||
<form action="/login" method="post">
|
||||
<form action="{{ relative_url }}login" method="post">
|
||||
<div class="card-content">
|
||||
<span class="card-title">Enter credentials</span>
|
||||
<p>
|
||||
|
|
|
@ -134,6 +134,14 @@ def resolve_ip_address(host):
|
|||
return ip
|
||||
|
||||
|
||||
def get_bool_env(var, default=False):
|
||||
return bool(os.getenv(var, default))
|
||||
|
||||
|
||||
def is_hassio():
|
||||
return get_bool_env('ESPHOME_IS_HASSIO')
|
||||
|
||||
|
||||
def symlink(src, dst):
|
||||
if hasattr(os, 'symlink'):
|
||||
os.symlink(src, dst)
|
||||
|
|
|
@ -14,6 +14,8 @@ _LOGGER = logging.getLogger(__name__)
|
|||
|
||||
def run_platformio_cli(*args, **kwargs):
|
||||
os.environ["PLATFORMIO_FORCE_COLOR"] = "true"
|
||||
os.environ["PLATFORMIO_BUILD_DIR"] = os.path.abspath(CORE.relative_pioenvs_path())
|
||||
os.environ["PLATFORMIO_LIBDEPS_DIR"] = os.path.abspath(CORE.relative_piolibdeps_path())
|
||||
cmd = ['platformio'] + list(args)
|
||||
|
||||
if os.environ.get('ESPHOME_USE_SUBPROCESS') is None:
|
||||
|
|
|
@ -8,7 +8,7 @@ import voluptuous as vol
|
|||
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import ESP_PLATFORMS, ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
|
||||
from esphome.helpers import color
|
||||
from esphome.helpers import color, get_bool_env
|
||||
# pylint: disable=anomalous-backslash-in-string
|
||||
from esphome.pins import ESP32_BOARD_PINS, ESP8266_BOARD_PINS
|
||||
from esphome.py_compat import safe_input, text_type
|
||||
|
@ -86,7 +86,7 @@ def wizard_write(path, **kwargs):
|
|||
storage.save(storage_path)
|
||||
|
||||
|
||||
if os.getenv('ESPHOME_QUICKWIZARD', ''):
|
||||
if get_bool_env('ESPHOME_QUICKWIZARD'):
|
||||
def sleep(time):
|
||||
pass
|
||||
else:
|
||||
|
|
|
@ -105,7 +105,7 @@ def update_esphome_core_repo():
|
|||
# Git commit hash or tag cannot be updated
|
||||
return
|
||||
|
||||
esphome_core_path = CORE.relative_build_path('.piolibdeps', 'esphome-core')
|
||||
esphome_core_path = CORE.relative_piolibdeps_path('esphome-core')
|
||||
|
||||
rc, _, _ = run_system_command('git', '-C', esphome_core_path, '--help')
|
||||
if rc != 0:
|
||||
|
@ -503,12 +503,14 @@ def write_cpp(code_s):
|
|||
|
||||
|
||||
def clean_build():
|
||||
for directory in ('.piolibdeps', '.pioenvs'):
|
||||
dir_path = CORE.relative_build_path(directory)
|
||||
if not os.path.isdir(dir_path):
|
||||
continue
|
||||
_LOGGER.info("Deleting %s", dir_path)
|
||||
shutil.rmtree(dir_path)
|
||||
pioenvs = CORE.relative_pioenvs_path()
|
||||
if os.path.isdir(pioenvs):
|
||||
_LOGGER.info("Deleting %s", pioenvs)
|
||||
shutil.rmtree(pioenvs)
|
||||
piolibdeps = CORE.relative_piolibdeps_path()
|
||||
if os.path.isdir(piolibdeps):
|
||||
_LOGGER.info("Deleting %s", piolibdeps)
|
||||
shutil.rmtree(piolibdeps)
|
||||
|
||||
|
||||
GITIGNORE_CONTENT = """# Gitignore settings for ESPHome
|
||||
|
|
Loading…
Reference in a new issue