mirror of
https://github.com/esphome/esphome.git
synced 2024-11-21 22:48:10 +01:00
Fix dashboard upload port selection
This commit is contained in:
parent
967aa53bad
commit
f4d393a59e
3 changed files with 235 additions and 199 deletions
|
@ -102,11 +102,14 @@ class SerialPortRequestHandler(tornado.web.RequestHandler):
|
||||||
data = []
|
data = []
|
||||||
for port, desc in ports:
|
for port, desc in ports:
|
||||||
if port == '/dev/ttyAMA0':
|
if port == '/dev/ttyAMA0':
|
||||||
# ignore RPi built-in serial port
|
desc = 'UART pins on GPIO header'
|
||||||
continue
|
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, 'desc': desc})
|
data.append({'port': port, 'desc': desc})
|
||||||
data.append({'port': 'OTA', 'desc': 'Over-The-Air Upload/Logs'})
|
data.append({'port': 'OTA', 'desc': 'Over-The-Air'})
|
||||||
self.write(json.dumps(data))
|
self.write(json.dumps(sorted(data, reverse=True)))
|
||||||
|
|
||||||
|
|
||||||
class WizardRequestHandler(tornado.web.RequestHandler):
|
class WizardRequestHandler(tornado.web.RequestHandler):
|
||||||
|
@ -119,7 +122,7 @@ class WizardRequestHandler(tornado.web.RequestHandler):
|
||||||
with codecs.open(destination, 'w') as f_handle:
|
with codecs.open(destination, 'w') as f_handle:
|
||||||
f_handle.write(config)
|
f_handle.write(config)
|
||||||
|
|
||||||
self.redirect('/')
|
self.redirect('/?begin=True')
|
||||||
|
|
||||||
|
|
||||||
class DownloadBinaryRequestHandler(tornado.web.RequestHandler):
|
class DownloadBinaryRequestHandler(tornado.web.RequestHandler):
|
||||||
|
@ -143,14 +146,15 @@ class DownloadBinaryRequestHandler(tornado.web.RequestHandler):
|
||||||
|
|
||||||
class MainRequestHandler(tornado.web.RequestHandler):
|
class MainRequestHandler(tornado.web.RequestHandler):
|
||||||
def get(self):
|
def get(self):
|
||||||
|
begin = bool(self.get_argument('begin', False))
|
||||||
files = sorted([f for f in os.listdir(CONFIG_DIR) if f.endswith('.yaml') and
|
files = sorted([f for f in os.listdir(CONFIG_DIR) if f.endswith('.yaml') and
|
||||||
not f.startswith('.')])
|
not f.startswith('.')])
|
||||||
full_path_files = [os.path.join(CONFIG_DIR, f) for f in files]
|
full_path_files = [os.path.join(CONFIG_DIR, f) for f in files]
|
||||||
self.render("templates/index.html", files=files, full_path_files=full_path_files,
|
self.render("templates/index.html", files=files, full_path_files=full_path_files,
|
||||||
version=const.__version__)
|
version=const.__version__, begin=begin)
|
||||||
|
|
||||||
|
|
||||||
def make_app():
|
def make_app(debug=False):
|
||||||
static_path = os.path.join(os.path.dirname(__file__), 'static')
|
static_path = os.path.join(os.path.dirname(__file__), 'static')
|
||||||
return tornado.web.Application([
|
return tornado.web.Application([
|
||||||
(r"/", MainRequestHandler),
|
(r"/", MainRequestHandler),
|
||||||
|
@ -161,7 +165,7 @@ def make_app():
|
||||||
(r"/serial-ports", SerialPortRequestHandler),
|
(r"/serial-ports", SerialPortRequestHandler),
|
||||||
(r"/wizard.html", WizardRequestHandler),
|
(r"/wizard.html", WizardRequestHandler),
|
||||||
(r'/static/(.*)', tornado.web.StaticFileHandler, {'path': static_path}),
|
(r'/static/(.*)', tornado.web.StaticFileHandler, {'path': static_path}),
|
||||||
], debug=False)
|
], debug=debug)
|
||||||
|
|
||||||
|
|
||||||
def start_web_server(args):
|
def start_web_server(args):
|
||||||
|
@ -177,7 +181,7 @@ def start_web_server(args):
|
||||||
|
|
||||||
_LOGGER.info("Starting dashboard web server on port %s and configuration dir %s...",
|
_LOGGER.info("Starting dashboard web server on port %s and configuration dir %s...",
|
||||||
args.port, CONFIG_DIR)
|
args.port, CONFIG_DIR)
|
||||||
app = make_app()
|
app = make_app(args.verbose)
|
||||||
app.listen(args.port)
|
app.listen(args.port)
|
||||||
try:
|
try:
|
||||||
tornado.ioloop.IOLoop.current().start()
|
tornado.ioloop.IOLoop.current().start()
|
||||||
|
|
|
@ -121,9 +121,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal {
|
.modal {
|
||||||
width: 90%;
|
width: 95%;
|
||||||
max-height: 85%;
|
max-height: 90%;
|
||||||
height: 80% !important;
|
height: 85% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-footer {
|
.page-footer {
|
||||||
|
@ -155,7 +155,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.select-port-container {
|
.select-port-container {
|
||||||
margin-top: 19px;
|
margin-top: 8px;
|
||||||
|
margin-right: 24px;
|
||||||
|
width: 350px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
@ -165,9 +167,23 @@
|
||||||
<nav>
|
<nav>
|
||||||
<div class="nav-wrapper indigo">
|
<div class="nav-wrapper indigo">
|
||||||
<a href="#" class="brand-logo left">esphomeyaml Dashboard</a>
|
<a href="#" class="brand-logo left">esphomeyaml Dashboard</a>
|
||||||
|
<div class="select-port-container right" id="select-port-target">
|
||||||
|
<select></select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
<div class="tap-target pink lighten-1 select-port" data-target="select-port-target">
|
||||||
|
<div class="tap-target-content">
|
||||||
|
<h5>Select Upload Port</h5>
|
||||||
|
<p>
|
||||||
|
Here you can select where esphomeyaml will attempt to show logs and upload firmwares to.
|
||||||
|
By default, this is "OTA", or Over-The-Air. Note that you might have to restart the HassIO add-on
|
||||||
|
for new serial ports to be detected.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="ribbon"></div>
|
<div class="ribbon"></div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
@ -201,64 +217,38 @@
|
||||||
|
|
||||||
<div id="modal-logs" class="modal modal-fixed-footer">
|
<div id="modal-logs" class="modal modal-fixed-footer">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<h4>Show Logs</h4>
|
<h4>Show Logs <code class="inlinecode filename"></code></h4>
|
||||||
<div class="upload-port row">
|
|
||||||
<div class="col s12">
|
|
||||||
<h5>Found multiple serial ports, please choose one:</h5>
|
|
||||||
</div>
|
|
||||||
<div class="input-field col s8">
|
|
||||||
<select></select>
|
|
||||||
</div>
|
|
||||||
<div class="col s4 select-port-container">
|
|
||||||
<button class="btn waves-effect waves-light upload-port-submit" type="submit" name="action">Select
|
|
||||||
<i class="material-icons right">send</i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="log-container">
|
<div class="log-container">
|
||||||
<pre class="log"></pre>
|
<pre class="log"></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<a class="modal-close waves-effect waves-green btn-flat">Close</a>
|
<a class="modal-close waves-effect waves-green btn-flat stop-logs">Close</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="modal-upload" class="modal modal-fixed-footer">
|
<div id="modal-upload" class="modal modal-fixed-footer">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<h4>Compile And Upload</h4>
|
<h4>Compile And Upload <code class="inlinecode filename"></code></h4>
|
||||||
<div class="upload-port row">
|
|
||||||
<div class="col s12">
|
|
||||||
<h5>Found multiple upload options, please choose one:</h5>
|
|
||||||
</div>
|
|
||||||
<div class="input-field col s8">
|
|
||||||
<select></select>
|
|
||||||
</div>
|
|
||||||
<div class="col s4 select-port-container">
|
|
||||||
<button class="btn waves-effect waves-light upload-port-submit" type="submit" name="action">Select
|
|
||||||
<i class="material-icons right">send</i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="log-container">
|
<div class="log-container">
|
||||||
<pre class="log"></pre>
|
<pre class="log"></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<a class="modal-close waves-effect waves-green btn-flat">Stop</a>
|
<a class="modal-close waves-effect waves-green btn-flat stop-logs">Stop</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="modal-compile" class="modal modal-fixed-footer">
|
<div id="modal-compile" class="modal modal-fixed-footer">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<h4>Compile</h4>
|
<h4>Compile <code class="inlinecode filename"></code></h4>
|
||||||
<div class="log-container">
|
<div class="log-container">
|
||||||
<pre class="log"></pre>
|
<pre class="log"></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<a class="modal-close waves-effect waves-green btn-flat disabled download-binary">Download Binary</a>
|
<a class="modal-close waves-effect waves-green btn-flat disabled download-binary">Download Binary</a>
|
||||||
<a class="modal-close waves-effect waves-green btn-flat">Stop</a>
|
<a class="modal-close waves-effect waves-green btn-flat stop-logs">Stop</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -303,7 +293,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="step-actions">
|
<div class="step-actions">
|
||||||
<button class="waves-effect waves-dark btn indigo next-step"">CONTINUE</button>
|
<button class="waves-effect waves-dark btn indigo next-step">CONTINUE</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
@ -455,7 +445,7 @@
|
||||||
<i class="material-icons">add</i>
|
<i class="material-icons">add</i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="tap-target pink lighten-1" data-target="setup-wizard-start">
|
<div class="tap-target pink lighten-1 setup-wizard" data-target="setup-wizard-start">
|
||||||
<div class="tap-target-content">
|
<div class="tap-target-content">
|
||||||
<h5>Set up your first Node</h5>
|
<h5>Set up your first Node</h5>
|
||||||
<p>
|
<p>
|
||||||
|
@ -505,19 +495,67 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
let configuration = "";
|
let configuration = "";
|
||||||
const ws_url = 'ws://' + window.location.hostname + ':' + window.location.port;
|
let wsProtocol = "ws:";
|
||||||
|
if (window.location.protocol === "https:") {
|
||||||
|
wsProtocol = 'wss:';
|
||||||
|
}
|
||||||
|
const wsUrl = wsProtocol + '//' + window.location.hostname + ':' + window.location.port;
|
||||||
|
|
||||||
|
const portSelect = document.querySelector('.nav-wrapper select');
|
||||||
|
let ports = [];
|
||||||
|
|
||||||
|
const fetchSerialPorts = (begin=false) => {
|
||||||
|
fetch('/serial-ports').then(res => res.json())
|
||||||
|
.then(response => {
|
||||||
|
if (ports.length === response.length) {
|
||||||
|
let allEqual = true;
|
||||||
|
for (let i = 0; i < response.length; i++) {
|
||||||
|
if (ports[i].port !== response[i].port) {
|
||||||
|
allEqual = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (allEqual)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ports = response;
|
||||||
|
|
||||||
|
const inst = M.FormSelect.getInstance(portSelect);
|
||||||
|
if (inst !== undefined) {
|
||||||
|
inst.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
portSelect.innerHTML = "";
|
||||||
|
const prevSelected = getUploadPort();
|
||||||
|
for (let i = 0; i < response.length; i++) {
|
||||||
|
const val = response[i];
|
||||||
|
if (val.port === prevSelected) {
|
||||||
|
portSelect.innerHTML += `<option value="${val.port}" selected>${val.port} (${val.desc})</option>`;
|
||||||
|
} else {
|
||||||
|
portSelect.innerHTML += `<option value="${val.port}">${val.port} (${val.desc})</option>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
M.FormSelect.init(portSelect, {});
|
||||||
|
if (!begin)
|
||||||
|
M.toast({html: "Discovered new serial port."});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getUploadPort = () => {
|
||||||
|
const inst = M.FormSelect.getInstance(portSelect);
|
||||||
|
if (inst === undefined) {
|
||||||
|
return "OTA";
|
||||||
|
}
|
||||||
|
|
||||||
|
inst._setSelectedStates();
|
||||||
|
return inst.getSelectedValues()[0];
|
||||||
|
};
|
||||||
|
setInterval(fetchSerialPorts, 2500);
|
||||||
|
fetchSerialPorts(true);
|
||||||
|
|
||||||
const logsModalElem = document.getElementById("modal-logs");
|
const logsModalElem = document.getElementById("modal-logs");
|
||||||
const logsPortSelect = logsModalElem.querySelector('select');
|
|
||||||
const logsPortDiv = logsModalElem.querySelector(".upload-port");
|
|
||||||
const logsPortSubmit = logsModalElem.querySelector('.upload-port-submit');
|
|
||||||
let logsStart = undefined;
|
|
||||||
|
|
||||||
logsPortSubmit.addEventListener('click', () => {
|
|
||||||
const inst = M.FormSelect.getInstance(logsPortSelect);
|
|
||||||
logsStart(inst.getSelectedValues()[0]);
|
|
||||||
inst.destroy();
|
|
||||||
});
|
|
||||||
|
|
||||||
document.querySelectorAll(".action-show-logs").forEach((showLogs) => {
|
document.querySelectorAll(".action-show-logs").forEach((showLogs) => {
|
||||||
showLogs.addEventListener('click', (e) => {
|
showLogs.addEventListener('click', (e) => {
|
||||||
|
@ -525,145 +563,15 @@
|
||||||
const modalInstance = M.Modal.getInstance(logsModalElem);
|
const modalInstance = M.Modal.getInstance(logsModalElem);
|
||||||
const log = logsModalElem.querySelector(".log");
|
const log = logsModalElem.querySelector(".log");
|
||||||
log.innerHTML = "";
|
log.innerHTML = "";
|
||||||
|
const stopLogsButton = logsModalElem.querySelector(".stop-logs");
|
||||||
if (M.FormSelect.getInstance(logsPortSelect) !== undefined) {
|
let stopped = false;
|
||||||
M.FormSelect.getInstance(logsPortSelect).destroy();
|
stopLogsButton.innerHTML = "Stop";
|
||||||
}
|
|
||||||
modalInstance.open();
|
modalInstance.open();
|
||||||
|
|
||||||
if (logsPortDiv.classList.contains('hide')) {
|
const filenameField = logsModalElem.querySelector('.filename');
|
||||||
logsPortDiv.classList.remove('hide');
|
filenameField.innerHTML = configuration;
|
||||||
}
|
|
||||||
|
|
||||||
logsStart = (port) => {
|
const logSocket = new WebSocket(wsUrl + "/logs");
|
||||||
logsPortDiv.classList.add('hide');
|
|
||||||
const logSocket = new WebSocket(ws_url + "/logs");
|
|
||||||
logSocket.addEventListener('message', (event) => {
|
|
||||||
const data = JSON.parse(event.data);
|
|
||||||
if (data.event === "line") {
|
|
||||||
const msg = data.data;
|
|
||||||
log.innerHTML += colorReplace(msg);
|
|
||||||
} else if (data.event === "exit") {
|
|
||||||
if (data.code === 0) {
|
|
||||||
M.toast({html: "Program exited successfully!"});
|
|
||||||
} else {
|
|
||||||
M.toast({html: `Program failed with code ${data.code}`});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
logSocket.addEventListener('open', () => {
|
|
||||||
const msg = JSON.stringify({configuration: configuration, port: port});
|
|
||||||
logSocket.send(msg);
|
|
||||||
});
|
|
||||||
logSocket.addEventListener('close', () => {
|
|
||||||
M.toast({html: 'Terminated process.'});
|
|
||||||
});
|
|
||||||
modalInstance.options.onCloseStart = () => {
|
|
||||||
logSocket.close();
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
fetch('/serial-ports').then(res => res.json())
|
|
||||||
.then(response => {
|
|
||||||
if (response.length > 1) {
|
|
||||||
logsPortSelect.innerHTML = "";
|
|
||||||
for (let i = 0; i < response.length; i++) {
|
|
||||||
const val = response[i];
|
|
||||||
logsPortSelect.innerHTML += `<option value="${val.port}">${val.port} (${val.desc})</option>`;
|
|
||||||
}
|
|
||||||
M.FormSelect.init(logsPortSelect, {});
|
|
||||||
} else {
|
|
||||||
logsStart("OTA");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const uploadModalElem = document.getElementById("modal-upload");
|
|
||||||
const uploadPortSelect = uploadModalElem.querySelector('select');
|
|
||||||
const uploadPortDiv = uploadModalElem.querySelector(".upload-port");
|
|
||||||
const uploadPortSubmit = uploadModalElem.querySelector('.upload-port-submit');
|
|
||||||
let uploadStart = undefined;
|
|
||||||
|
|
||||||
uploadPortSubmit.addEventListener('click', () => {
|
|
||||||
const inst = M.FormSelect.getInstance(uploadPortSelect);
|
|
||||||
uploadStart(inst.getSelectedValues()[0]);
|
|
||||||
inst.destroy();
|
|
||||||
});
|
|
||||||
|
|
||||||
document.querySelectorAll(".action-upload").forEach((showLogs) => {
|
|
||||||
showLogs.addEventListener('click', (e) => {
|
|
||||||
configuration = e.target.getAttribute('data-node');
|
|
||||||
const modalInstance = M.Modal.getInstance(uploadModalElem);
|
|
||||||
const log = uploadModalElem.querySelector(".log");
|
|
||||||
log.innerHTML = "";
|
|
||||||
|
|
||||||
if (M.FormSelect.getInstance(uploadPortSelect) !== undefined) {
|
|
||||||
M.FormSelect.getInstance(uploadPortSelect).destroy();
|
|
||||||
}
|
|
||||||
modalInstance.open();
|
|
||||||
|
|
||||||
if (uploadPortDiv.classList.contains('hide')) {
|
|
||||||
uploadPortDiv.classList.remove('hide');
|
|
||||||
}
|
|
||||||
|
|
||||||
uploadStart = (port) => {
|
|
||||||
uploadPortDiv.classList.add('hide');
|
|
||||||
const logSocket = new WebSocket(ws_url + "/run");
|
|
||||||
logSocket.addEventListener('message', (event) => {
|
|
||||||
const data = JSON.parse(event.data);
|
|
||||||
if (data.event === "line") {
|
|
||||||
const msg = data.data;
|
|
||||||
log.innerHTML += colorReplace(msg);
|
|
||||||
} else if (data.event === "exit") {
|
|
||||||
if (data.code === 0) {
|
|
||||||
M.toast({html: "Program exited successfully!"});
|
|
||||||
} else {
|
|
||||||
M.toast({html: `Program failed with code ${data.code}`});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
logSocket.addEventListener('open', () => {
|
|
||||||
const msg = JSON.stringify({configuration: configuration, port: port});
|
|
||||||
logSocket.send(msg);
|
|
||||||
});
|
|
||||||
logSocket.addEventListener('close', () => {
|
|
||||||
M.toast({html: 'Terminated process.'});
|
|
||||||
});
|
|
||||||
modalInstance.options.onCloseStart = () => {
|
|
||||||
logSocket.close();
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
fetch('/serial-ports').then(res => res.json())
|
|
||||||
.then(response => {
|
|
||||||
if (response.length > 1) {
|
|
||||||
uploadPortSelect.innerHTML = "";
|
|
||||||
for (let i = 0; i < response.length; i++) {
|
|
||||||
const val = response[i];
|
|
||||||
uploadPortSelect.innerHTML += `<option value="${val.port}">${val.port} (${val.desc})</option>`;
|
|
||||||
}
|
|
||||||
M.FormSelect.init(uploadPortSelect, {});
|
|
||||||
} else {
|
|
||||||
uploadStart("OTA");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const compileModalElem = document.getElementById("modal-compile");
|
|
||||||
const downloadButton = compileModalElem.querySelector('.download-binary');
|
|
||||||
|
|
||||||
document.querySelectorAll(".action-compile").forEach((showLogs) => {
|
|
||||||
showLogs.addEventListener('click', (e) => {
|
|
||||||
configuration = e.target.getAttribute('data-node');
|
|
||||||
const modalInstance = M.Modal.getInstance(compileModalElem);
|
|
||||||
const log = compileModalElem.querySelector(".log");
|
|
||||||
log.innerHTML = "";
|
|
||||||
downloadButton.classList.add('disabled');
|
|
||||||
modalInstance.open();
|
|
||||||
|
|
||||||
const logSocket = new WebSocket(ws_url + "/compile");
|
|
||||||
logSocket.addEventListener('message', (event) => {
|
logSocket.addEventListener('message', (event) => {
|
||||||
const data = JSON.parse(event.data);
|
const data = JSON.parse(event.data);
|
||||||
if (data.event === "line") {
|
if (data.event === "line") {
|
||||||
|
@ -671,19 +579,23 @@
|
||||||
log.innerHTML += colorReplace(msg);
|
log.innerHTML += colorReplace(msg);
|
||||||
} else if (data.event === "exit") {
|
} else if (data.event === "exit") {
|
||||||
if (data.code === 0) {
|
if (data.code === 0) {
|
||||||
M.toast({html: "Program exited successfully!"});
|
M.toast({html: "Program exited successfully."});
|
||||||
downloadButton.classList.remove('disabled');
|
|
||||||
} else {
|
} else {
|
||||||
M.toast({html: `Program failed with code ${data.code}`});
|
M.toast({html: `Program failed with code ${data.code}`});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stopLogsButton.innerHTML = "Close";
|
||||||
|
stopped = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
logSocket.addEventListener('open', () => {
|
logSocket.addEventListener('open', () => {
|
||||||
const msg = JSON.stringify({configuration: configuration});
|
const msg = JSON.stringify({configuration: configuration, port: getUploadPort()});
|
||||||
logSocket.send(msg);
|
logSocket.send(msg);
|
||||||
});
|
});
|
||||||
logSocket.addEventListener('close', () => {
|
logSocket.addEventListener('close', () => {
|
||||||
M.toast({html: 'Terminated process.'});
|
if (!stopped) {
|
||||||
|
M.toast({html: 'Terminated process.'});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
modalInstance.options.onCloseStart = () => {
|
modalInstance.options.onCloseStart = () => {
|
||||||
logSocket.close();
|
logSocket.close();
|
||||||
|
@ -691,6 +603,105 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const uploadModalElem = document.getElementById("modal-upload");
|
||||||
|
|
||||||
|
document.querySelectorAll(".action-upload").forEach((upload) => {
|
||||||
|
upload.addEventListener('click', (e) => {
|
||||||
|
configuration = e.target.getAttribute('data-node');
|
||||||
|
const modalInstance = M.Modal.getInstance(uploadModalElem);
|
||||||
|
const log = uploadModalElem.querySelector(".log");
|
||||||
|
log.innerHTML = "";
|
||||||
|
const stopLogsButton = uploadModalElem.querySelector(".stop-logs");
|
||||||
|
let stopped = false;
|
||||||
|
stopLogsButton.innerHTML = "Stop";
|
||||||
|
modalInstance.open();
|
||||||
|
|
||||||
|
const filenameField = uploadModalElem.querySelector('.filename');
|
||||||
|
filenameField.innerHTML = configuration;
|
||||||
|
|
||||||
|
const logSocket = new WebSocket(wsUrl + "/run");
|
||||||
|
logSocket.addEventListener('message', (event) => {
|
||||||
|
const data = JSON.parse(event.data);
|
||||||
|
if (data.event === "line") {
|
||||||
|
const msg = data.data;
|
||||||
|
log.innerHTML += colorReplace(msg);
|
||||||
|
} else if (data.event === "exit") {
|
||||||
|
if (data.code === 0) {
|
||||||
|
M.toast({html: "Program exited successfully."});
|
||||||
|
} else {
|
||||||
|
M.toast({html: `Program failed with code ${data.code}`});
|
||||||
|
}
|
||||||
|
|
||||||
|
stopLogsButton.innerHTML = "Close";
|
||||||
|
stopped = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
logSocket.addEventListener('open', () => {
|
||||||
|
const msg = JSON.stringify({configuration: configuration, port: getUploadPort()});
|
||||||
|
logSocket.send(msg);
|
||||||
|
});
|
||||||
|
logSocket.addEventListener('close', () => {
|
||||||
|
if (!stopped) {
|
||||||
|
M.toast({html: 'Terminated process.'});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
modalInstance.options.onCloseStart = () => {
|
||||||
|
logSocket.close();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const compileModalElem = document.getElementById("modal-compile");
|
||||||
|
const downloadButton = compileModalElem.querySelector('.download-binary');
|
||||||
|
|
||||||
|
document.querySelectorAll(".action-compile").forEach((upload) => {
|
||||||
|
upload.addEventListener('click', (e) => {
|
||||||
|
configuration = e.target.getAttribute('data-node');
|
||||||
|
const modalInstance = M.Modal.getInstance(compileModalElem);
|
||||||
|
const log = compileModalElem.querySelector(".log");
|
||||||
|
log.innerHTML = "";
|
||||||
|
const stopLogsButton = compileModalElem.querySelector(".stop-logs");
|
||||||
|
let stopped = false;
|
||||||
|
stopLogsButton.innerHTML = "Stop";
|
||||||
|
downloadButton.classList.add('disabled');
|
||||||
|
|
||||||
|
modalInstance.open();
|
||||||
|
|
||||||
|
const filenameField = compileModalElem.querySelector('.filename');
|
||||||
|
filenameField.innerHTML = configuration;
|
||||||
|
|
||||||
|
const logSocket = new WebSocket(wsUrl + "/compile");
|
||||||
|
logSocket.addEventListener('message', (event) => {
|
||||||
|
const data = JSON.parse(event.data);
|
||||||
|
if (data.event === "line") {
|
||||||
|
const msg = data.data;
|
||||||
|
log.innerHTML += colorReplace(msg);
|
||||||
|
} else if (data.event === "exit") {
|
||||||
|
if (data.code === 0) {
|
||||||
|
M.toast({html: "Program exited successfully."});
|
||||||
|
downloadButton.classList.remove('disabled');
|
||||||
|
} else {
|
||||||
|
M.toast({html: `Program failed with code ${data.code}`});
|
||||||
|
}
|
||||||
|
|
||||||
|
stopLogsButton.innerHTML = "Close";
|
||||||
|
stopped = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
logSocket.addEventListener('open', () => {
|
||||||
|
const msg = JSON.stringify({configuration: configuration});
|
||||||
|
logSocket.send(msg);
|
||||||
|
});
|
||||||
|
logSocket.addEventListener('close', () => {
|
||||||
|
if (!stopped) {
|
||||||
|
M.toast({html: 'Terminated process.'});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
modalInstance.options.onCloseStart = () => {
|
||||||
|
logSocket.close();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
downloadButton.addEventListener('click', () => {
|
downloadButton.addEventListener('click', () => {
|
||||||
const link = document.createElement("a");
|
const link = document.createElement("a");
|
||||||
link.download = name;
|
link.download = name;
|
||||||
|
@ -722,7 +733,7 @@
|
||||||
{% if len(files) == 0 %}
|
{% if len(files) == 0 %}
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
const tapTargetElem = document.querySelector('.tap-target');
|
const tapTargetElem = document.querySelector('.tap-target.setup-wizard');
|
||||||
const tapTargetInstance = M.TapTarget.getInstance(tapTargetElem);
|
const tapTargetInstance = M.TapTarget.getInstance(tapTargetElem);
|
||||||
tapTargetInstance.options.onOpen = () => {
|
tapTargetInstance.options.onOpen = () => {
|
||||||
$('.tap-target-origin').on('click', () => {
|
$('.tap-target-origin').on('click', () => {
|
||||||
|
@ -734,5 +745,23 @@
|
||||||
</script>
|
</script>
|
||||||
{% end %}
|
{% end %}
|
||||||
|
|
||||||
|
{% if begin %}
|
||||||
|
<script>
|
||||||
|
window.history.replaceState({}, document.title, "/");
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const tapTargetElem = document.querySelector('.tap-target.select-port');
|
||||||
|
const tapTargetInstance = M.TapTarget.getInstance(tapTargetElem);
|
||||||
|
tapTargetInstance.open();
|
||||||
|
|
||||||
|
tapTargetInstance.contentEl.style["top"] = "300px";
|
||||||
|
tapTargetInstance.contentEl.style["padding"] = "250px";
|
||||||
|
tapTargetInstance.waveEl.style["top"] = "250px";
|
||||||
|
tapTargetInstance.waveEl.style["left"] = "250px";
|
||||||
|
tapTargetInstance.waveEl.style["width"] = "300px";
|
||||||
|
tapTargetInstance.waveEl.style["height"] = "300px";
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% end %}
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -450,10 +450,12 @@ def flush_tasks():
|
||||||
raise ESPHomeYAMLError("Circular dependency detected!")
|
raise ESPHomeYAMLError("Circular dependency detected!")
|
||||||
|
|
||||||
task, domain = _TASKS.popleft()
|
task, domain = _TASKS.popleft()
|
||||||
|
_LOGGER.debug("Executing task for domain=%s", domain)
|
||||||
try:
|
try:
|
||||||
task.next()
|
task.next()
|
||||||
_TASKS.append((task, domain))
|
_TASKS.append((task, domain))
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
|
_LOGGER.debug(" -> %s finished", domain)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@ -461,6 +463,7 @@ def add(expression, require=True):
|
||||||
if require and isinstance(expression, Expression):
|
if require and isinstance(expression, Expression):
|
||||||
expression.require()
|
expression.require()
|
||||||
_EXPRESSIONS.append(expression)
|
_EXPRESSIONS.append(expression)
|
||||||
|
_LOGGER.debug("Adding: %s", statement(expression))
|
||||||
return expression
|
return expression
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue