1
0
mirror of https://github.com/esphome/esphome.git synced 2025-03-15 07:08:20 +00:00
This commit is contained in:
Otto Winter 2018-05-21 14:41:15 +02:00
parent 254e0a63c2
commit b8c55c5043
No known key found for this signature in database
GPG Key ID: DB66C0BE6013F97E
4 changed files with 305 additions and 125 deletions

View File

@ -1,18 +1,19 @@
from __future__ import print_function from __future__ import print_function
import argparse import argparse
from datetime import datetime
import logging import logging
import os import os
import random import random
import sys import sys
from esphomeyaml import core, mqtt, wizard, writer, yaml_util, const from esphomeyaml import const, core, mqtt, wizard, writer, yaml_util
from esphomeyaml.config import core_to_code, get_component, iter_components, read_config from esphomeyaml.config import core_to_code, get_component, iter_components, read_config
from esphomeyaml.const import CONF_BAUD_RATE, CONF_DOMAIN, CONF_ESPHOMEYAML, CONF_HOSTNAME, \ from esphomeyaml.const import CONF_BAUD_RATE, CONF_DOMAIN, CONF_ESPHOMEYAML, CONF_HOSTNAME, \
CONF_LOGGER, CONF_MANUAL_IP, CONF_NAME, CONF_STATIC_IP, CONF_WIFI CONF_LOGGER, CONF_MANUAL_IP, CONF_NAME, CONF_STATIC_IP, CONF_WIFI, ESP_PLATFORM_ESP8266
from esphomeyaml.core import ESPHomeYAMLError from esphomeyaml.core import ESPHomeYAMLError
from esphomeyaml.helpers import AssignmentExpression, RawStatement, _EXPRESSIONS, add, add_task, \ from esphomeyaml.helpers import AssignmentExpression, Expression, RawStatement, _EXPRESSIONS, add, \
color, get_variable, indent, quote, statement, Expression add_task, color, get_variable, indent, quote, statement
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -66,7 +67,7 @@ def choose_serial_port(config):
return result[opt][0] return result[opt][0]
def run_platformio(*cmd): def run_platformio(*cmd, **kwargs):
def mock_exit(return_code): def mock_exit(return_code):
raise SystemExit(return_code) raise SystemExit(return_code)
@ -75,10 +76,13 @@ def run_platformio(*cmd):
full_cmd = u' '.join(quote(x) for x in cmd) full_cmd = u' '.join(quote(x) for x in cmd)
_LOGGER.info(u"Running: %s", full_cmd) _LOGGER.info(u"Running: %s", full_cmd)
try: try:
import platformio.__main__ main = kwargs.get('main')
if main is None:
import platformio.__main__
main = platformio.__main__.main
sys.argv = list(cmd) sys.argv = list(cmd)
sys.exit = mock_exit sys.exit = mock_exit
return platformio.__main__.main() return main() or 0
except KeyboardInterrupt: except KeyboardInterrupt:
return 1 return 1
except SystemExit as err: except SystemExit as err:
@ -91,13 +95,19 @@ def run_platformio(*cmd):
sys.exit = orig_exit sys.exit = orig_exit
def run_miniterm(config, port): def run_miniterm(config, port, escape=False):
from serial.tools import miniterm import serial
baud_rate = config.get(CONF_LOGGER, {}).get(CONF_BAUD_RATE, 115200) baud_rate = config.get(CONF_LOGGER, {}).get(CONF_BAUD_RATE, 115200)
sys.argv = ['miniterm', '--raw', '--exit-char', '3'] _LOGGER.info("Starting log output from %s with baud rate %s", port, baud_rate)
miniterm.main(
default_port=port, with serial.Serial(port, baudrate=baud_rate) as ser:
default_baudrate=baud_rate) while True:
line = ser.readline()
time = datetime.now().time().strftime('[%H:%M:%S]')
message = time + line.decode('unicode-escape').replace('\r', '').replace('\n', '')
if escape:
message = message.replace('\033', '\\033').encode('ascii', 'replace')
print(message)
def write_cpp(config): def write_cpp(config):
@ -162,9 +172,21 @@ def get_upload_host(config):
return host return host
def upload_using_esptool(config, port):
import esptool
name = get_name(config)
path = os.path.join(get_base_path(config), '.pioenvs', name, 'firmware.bin')
return run_platformio('esptool.py', '--before', 'default_reset', '--after', 'hard_reset',
'--chip', 'esp8266', '--port', port, 'write_flash', '0x0',
path, main=esptool._main)
def upload_program(config, args, port): def upload_program(config, args, port):
_LOGGER.info("Uploading binary...") _LOGGER.info("Uploading binary...")
if port != 'OTA': if port != 'OTA':
if core.ESP_PLATFORM == ESP_PLATFORM_ESP8266 and args.use_esptoolpy:
return upload_using_esptool(config, port)
return run_platformio('platformio', 'run', '-d', get_base_path(config), return run_platformio('platformio', 'run', '-d', get_base_path(config),
'-t', 'upload', '--upload-port', port) '-t', 'upload', '--upload-port', port)
@ -190,7 +212,7 @@ def upload_program(config, args, port):
def show_logs(config, args, port, escape=False): def show_logs(config, args, port, escape=False):
if port != 'OTA': if port != 'OTA':
run_miniterm(config, port) run_miniterm(config, port, escape=escape)
return 0 return 0
return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id, return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id,
escape=escape) escape=escape)
@ -329,6 +351,9 @@ def parse_args(argv):
parser_upload.add_argument('--upload-port', help="Manually specify the upload port to use. " parser_upload.add_argument('--upload-port', help="Manually specify the upload port to use. "
"For example /dev/cu.SLAB_USBtoUART.") "For example /dev/cu.SLAB_USBtoUART.")
parser_upload.add_argument('--host-port', help="Specify the host port.", type=int) parser_upload.add_argument('--host-port', help="Specify the host port.", type=int)
parser_upload.add_argument('--use-esptoolpy',
help="Use esptool.py for HassIO (only for ESP8266)",
action='store_true')
parser_logs = subparsers.add_parser('logs', help='Validate the configuration ' parser_logs = subparsers.add_parser('logs', help='Validate the configuration '
'and show all MQTT logs.') 'and show all MQTT logs.')
@ -354,6 +379,8 @@ def parse_args(argv):
parser_run.add_argument('--client-id', help='Manually set the client id for logs.') parser_run.add_argument('--client-id', help='Manually set the client id for logs.')
parser_run.add_argument('--escape', help="Escape ANSI color codes for HassIO", parser_run.add_argument('--escape', help="Escape ANSI color codes for HassIO",
action='store_true') action='store_true')
parser_run.add_argument('--use-esptoolpy', help="Use esptool.py for HassIO (only for ESP8266)",
action='store_true')
parser_clean = subparsers.add_parser('clean-mqtt', help="Helper to clear an MQTT topic from " parser_clean = subparsers.add_parser('clean-mqtt', help="Helper to clear an MQTT topic from "
"retain messages.") "retain messages.")
@ -408,6 +435,8 @@ def main():
except ESPHomeYAMLError as e: except ESPHomeYAMLError as e:
_LOGGER.error(e) _LOGGER.error(e)
return 1 return 1
except KeyboardInterrupt:
return 1
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -26,7 +26,7 @@ zero_to_one_float = vol.All(vol.Coerce(float), vol.Range(min=0, max=1))
positive_int = vol.All(vol.Coerce(int), vol.Range(min=0)) positive_int = vol.All(vol.Coerce(int), vol.Range(min=0))
positive_not_null_int = vol.All(vol.Coerce(int), vol.Range(min=0, min_included=False)) positive_not_null_int = vol.All(vol.Coerce(int), vol.Range(min=0, min_included=False))
ALLOWED_NAME_CHARS = u'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_' ALLOWED_NAME_CHARS = u'abcdefghijklmnopqrstuvwxyz0123456789_'
RESERVED_IDS = [ RESERVED_IDS = [
# C++ keywords http://en.cppreference.com/w/cpp/keyword # C++ keywords http://en.cppreference.com/w/cpp/keyword

View File

@ -1,9 +1,9 @@
from __future__ import print_function from __future__ import print_function
import codecs import codecs
import os
import json import json
import logging import logging
import os
import random import random
import subprocess import subprocess
@ -19,8 +19,8 @@ try:
except ImportError as err: except ImportError as err:
pass pass
from esphomeyaml import const from esphomeyaml import const, core, __main__
from esphomeyaml.__main__ import get_serial_ports from esphomeyaml.__main__ import get_serial_ports, get_base_path, get_name
from esphomeyaml.helpers import quote from esphomeyaml.helpers import quote
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -76,8 +76,9 @@ class EsphomeyamlLogsHandler(EsphomeyamlCommandWebSocket):
super(EsphomeyamlLogsHandler, self).__init__(application, request, **kwargs) super(EsphomeyamlLogsHandler, self).__init__(application, request, **kwargs)
def build_command(self, message): def build_command(self, message):
config_file = CONFIG_DIR + '/' + message js = json.loads(message)
return ["esphomeyaml", config_file, "logs", '--escape', '--serial-port', 'OTA'] config_file = CONFIG_DIR + '/' + js['configuration']
return ["esphomeyaml", config_file, "logs", '--serial-port', js["port"], '--escape']
class EsphomeyamlRunHandler(EsphomeyamlCommandWebSocket): class EsphomeyamlRunHandler(EsphomeyamlCommandWebSocket):
@ -86,8 +87,19 @@ class EsphomeyamlRunHandler(EsphomeyamlCommandWebSocket):
def build_command(self, message): def build_command(self, message):
js = json.loads(message) js = json.loads(message)
config_file = CONFIG_DIR + '/' + js['configuration'] config_file = os.path.join(CONFIG_DIR, js['configuration'])
return ["esphomeyaml", config_file, "run", '--upload-port', js["port"], '--escape'] return ["esphomeyaml", config_file, "run", '--upload-port', js["port"],
'--escape', '--use-esptoolpy']
class EsphomeyamlCompileHandler(EsphomeyamlCommandWebSocket):
def __init__(self, application, request, **kwargs):
super(EsphomeyamlCompileHandler, self).__init__(application, request, **kwargs)
def build_command(self, message):
js = json.loads(message)
config_file = os.path.join(CONFIG_DIR, js['configuration'])
return ["esphomeyaml", config_file, "compile"]
class SerialPortRequestHandler(tornado.web.RequestHandler): class SerialPortRequestHandler(tornado.web.RequestHandler):
@ -107,7 +119,7 @@ class WizardRequestHandler(tornado.web.RequestHandler):
def post(self): def post(self):
from esphomeyaml import wizard from esphomeyaml import wizard
kwargs = {k:''.join(v) for k,v in self.request.arguments.iteritems()} kwargs = {k: ''.join(v) for k, v in self.request.arguments.iteritems()}
config = wizard.wizard_file(**kwargs) config = wizard.wizard_file(**kwargs)
destination = os.path.join(CONFIG_DIR, kwargs['name'] + '.yaml') destination = os.path.join(CONFIG_DIR, kwargs['name'] + '.yaml')
with codecs.open(destination, 'w') as f_handle: with codecs.open(destination, 'w') as f_handle:
@ -116,10 +128,31 @@ class WizardRequestHandler(tornado.web.RequestHandler):
self.redirect('/') self.redirect('/')
class DownloadBinaryRequestHandler(tornado.web.RequestHandler):
def get(self):
configuration = self.get_argument('configuration')
config_file = os.path.join(CONFIG_DIR, configuration)
core.CONFIG_PATH = config_file
config = __main__.read_config(core.CONFIG_PATH)
name = get_name(config)
path = os.path.join(get_base_path(config), '.pioenvs', name, 'firmware.bin')
self.set_header('Content-Type', 'application/octet-stream')
self.set_header("Content-Disposition", 'attachment; filename="{}.bin"'.format(name))
with open(path, 'rb') as f:
while 1:
data = f.read(16384) # or some other nice-sized chunk
if not data:
break
self.write(data)
self.finish()
class MainRequestHandler(tornado.web.RequestHandler): class MainRequestHandler(tornado.web.RequestHandler):
def get(self): def get(self):
files = [f for f in os.listdir(CONFIG_DIR) if f.endswith('.yaml')] files = [f for f in os.listdir(CONFIG_DIR) if f.endswith('.yaml')]
self.render("templates/index.html", files=files, version=const.__version__) 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,
version=const.__version__)
def make_app(): def make_app():
@ -128,15 +161,19 @@ def make_app():
(r"/", MainRequestHandler), (r"/", MainRequestHandler),
(r"/logs", EsphomeyamlLogsHandler), (r"/logs", EsphomeyamlLogsHandler),
(r"/run", EsphomeyamlRunHandler), (r"/run", EsphomeyamlRunHandler),
(r"/compile", EsphomeyamlCompileHandler),
(r"/download.bin", DownloadBinaryRequestHandler),
(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=True)
def start_web_server(args): def start_web_server(args):
global CONFIG_DIR global CONFIG_DIR
CONFIG_DIR = args.configuration CONFIG_DIR = args.configuration
if not os.path.exists(CONFIG_DIR):
os.makedirs(CONFIG_DIR)
_LOGGER.info("Starting HassIO add-on web server on port %s and configuration dir %s...", _LOGGER.info("Starting HassIO add-on web server on port %s and configuration dir %s...",
args.port, CONFIG_DIR) args.port, CONFIG_DIR)

View File

@ -72,6 +72,16 @@
color: #DDD; color: #DDD;
} }
.inlinecode {
box-sizing: border-box;
padding: 0.2em 0.4em;
margin: 0;
font-size: 85%;
background-color: rgba(27,31,35,0.05);
border-radius: 3px;
font-family: "SFMono-Regular",Consolas,"Liberation Mono",Menlo,Courier,monospace;
}
.log.bold { .log.bold {
font-weight: bold; font-weight: bold;
} }
@ -112,20 +122,11 @@
.modal { .modal {
width: 90%; width: 90%;
max-height: 85%; max-height: 85%;
} height: 80% !important;
.log-container {
position: absolute;
bottom: 0;
width: calc(100% - 48px);
} }
.log { .log {
position: absolute;
bottom: 0;
right: 0;
left: 0;
overflow-y: auto;
} }
.page-footer { .page-footer {
@ -155,6 +156,10 @@
ul.stepper:not(.horizontal) .step.active::before, ul.stepper:not(.horizontal) .step.done::before, ul.stepper.horizontal .step.active .step-title::before, ul.stepper.horizontal .step.done .step-title::before { ul.stepper:not(.horizontal) .step.active::before, ul.stepper:not(.horizontal) .step.done::before, ul.stepper.horizontal .step.active .step-title::before, ul.stepper.horizontal .step.done .step-title::before {
background-color: #3f51b5 !important; background-color: #3f51b5 !important;
} }
.select-port-container {
margin-top: 19px;
}
</style> </style>
</head> </head>
<body> <body>
@ -171,7 +176,7 @@
<main> <main>
<div class="container"> <div class="container">
{% for file in files %} {% for file, full_path in zip(files, full_path_files) %}
<div class="row"> <div class="row">
<div class="col s8 offset-s2 m10 offset-m1 l12"> <div class="col s8 offset-s2 m10 offset-m1 l12">
<div class="card horizontal"> <div class="card horizontal">
@ -180,10 +185,14 @@
</div> </div>
<div class="card-stacked"> <div class="card-stacked">
<div class="card-content"> <div class="card-content">
<span class="card-title">{{ file }}</span> <span class="card-title">{{ escape(file) }}</span>
<p>
Full path: <code class="inlinecode">{{ escape(full_path) }}</code>
</p>
</div> </div>
<div class="card-action"> <div class="card-action">
<a href="#" class="action-upload" data-node="{{ file }}">Upload</a> <a href="#" class="action-upload" data-node="{{ file }}">Upload</a>
<a href="#" class="action-compile" data-node="{{ file }}">Compile</a>
<a href="#" class="action-show-logs" data-node="{{ file }}">Show Logs</a> <a href="#" class="action-show-logs" data-node="{{ file }}">Show Logs</a>
</div> </div>
</div> </div>
@ -196,23 +205,14 @@
<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</h4>
<div class="log-container">
<pre class="log"></pre>
</div>
</div>
<div class="modal-footer">
<a class="modal-close waves-effect waves-green btn-flat">Close</a>
</div>
</div>
<div id="modal-run" class="modal modal-fixed-footer">
<div class="modal-content">
<h4>Compile And Upload</h4>
<div class="upload-port row"> <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"> <div class="input-field col s8">
<select></select> <select></select>
</div> </div>
<div class="col s4"> <div class="col s4 select-port-container">
<button class="btn waves-effect waves-light upload-port-submit" type="submit" name="action">Select <button class="btn waves-effect waves-light upload-port-submit" type="submit" name="action">Select
<i class="material-icons right">send</i> <i class="material-icons right">send</i>
</button> </button>
@ -223,6 +223,44 @@
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<a class="modal-close waves-effect waves-green btn-flat">Close</a>
</div>
</div>
<div id="modal-upload" class="modal modal-fixed-footer">
<div class="modal-content">
<h4>Compile And Upload</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">
<pre class="log"></pre>
</div>
</div>
<div class="modal-footer">
<a class="modal-close waves-effect waves-green btn-flat">Stop</a>
</div>
</div>
<div id="modal-compile" class="modal modal-fixed-footer">
<div class="modal-content">
<h4>Compile</h4>
<div class="log-container">
<pre class="log"></pre>
</div>
</div>
<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">Stop</a> <a class="modal-close waves-effect waves-green btn-flat">Stop</a>
</div> </div>
</div> </div>
@ -259,7 +297,7 @@
<p> <p>
First, I need to know what this node should be called. Choose this name wisely, changing this First, I need to know what this node should be called. Choose this name wisely, changing this
later makes Over-The-Air Update attempts difficult. later makes Over-The-Air Update attempts difficult.
It may only contain the characters <code class="inlinecode">a-z</code>, <code class="inlinecode">A-Z</code>, It may only contain the characters <code class="inlinecode">a-z</code>,
<code class="inlinecode">0-9</code> and <code class="inlinecode">_</code> <code class="inlinecode">0-9</code> and <code class="inlinecode">_</code>
</p> </p>
<div class="input-field col s12"> <div class="input-field col s12">
@ -268,7 +306,7 @@
</div> </div>
</div> </div>
<div class="step-actions"> <div class="step-actions">
<button class="waves-effect waves-dark btn indigo next-step" id="step-1-continue">CONTINUE</button> <button class="waves-effect waves-dark btn indigo next-step"">CONTINUE</button>
</div> </div>
</div> </div>
</li> </li>
@ -278,7 +316,8 @@
<div class="row"> <div class="row">
<p> <p>
Great! Now I need to know what type of microcontroller you're using so that I can compile firmware for them. Great! Now I need to know what type of microcontroller you're using so that I can compile firmware for them.
Please choose either ESP32 or ESP8266 (use ESP8266 for Sonoff devices). Please choose either ESP32 or ESP8266 (use ESP8266 for Sonoff devices). Note that the ESP32 is currently
unsupported if HassIO is running on a Raspberry Pi.
</p> </p>
<div class="input-field col s12"> <div class="input-field col s12">
<select id="esp_type" name="platform" required> <select id="esp_type" name="platform" required>
@ -347,8 +386,10 @@
</p> </p>
<p> <p>
When you're done with that, please enter your MQTT broker here. For example When you're done with that, please enter your MQTT broker here. For example
<code class="inlinecode">hassio.local</code>. Please also specify the MQTT username and password <code class="inlinecode">192.168.1.100</code> (Note
you wish esphomelib to use (leave them empty if you're not using any authentication). <code class="inlinecode">hassio.local</code> often doesn't work, please use a static IP).
Please also specify the MQTT username and password you wish esphomelib to use
(leave them empty if you're not using any authentication).
</p> </p>
<div class="input-field col s12"> <div class="input-field col s12">
<input id="mqtt_broker" class="validate" type="text" name="broker" required> <input id="mqtt_broker" class="validate" type="text" name="broker" required>
@ -383,7 +424,8 @@
<li> <li>
Flash the firmware. This can be done using the “UPLOAD” option in the dashboard. See Flash the firmware. This can be done using the “UPLOAD” option in the dashboard. See
<a href="https://esphomelib.com/esphomeyaml/index.html#using-with" target="_blank">this</a> <a href="https://esphomelib.com/esphomeyaml/index.html#using-with" target="_blank">this</a>
for guides on how to flash different types of devices. for guides on how to flash different types of devices. Note that you need to restart this add-on
for newly plugged in serial devices to be detected.
</li> </li>
<li> <li>
See the <a href="https://esphomelib.com/esphomeyaml/index.html" target="_blank">esphomeyaml index</a> See the <a href="https://esphomelib.com/esphomeyaml/index.html" target="_blank">esphomeyaml index</a>
@ -468,68 +510,44 @@
let configuration = ""; let configuration = "";
const ws_url = 'ws://' + window.location.hostname + ':' + window.location.port; const ws_url = 'ws://' + window.location.hostname + ':' + window.location.port;
document.querySelectorAll(".action-show-logs").forEach((showLogs) => { const logsModalElem = document.getElementById("modal-logs");
console.log(showLogs); const logsPortSelect = logsModalElem.querySelector('select');
showLogs.addEventListener('click', (e) => { const logsPortDiv = logsModalElem.querySelector(".upload-port");
configuration = e.target.getAttribute('data-node'); const logsPortSubmit = logsModalElem.querySelector('.upload-port-submit');
const elem = document.getElementById("modal-logs"); let logsStart = undefined;
const instance = M.Modal.getInstance(elem);
const log = elem.querySelector(".log");
log.innerHTML = "";
instance.open(); logsPortSubmit.addEventListener('click', () => {
const inst = M.FormSelect.getInstance(logsPortSelect);
const logSocket = new WebSocket(ws_url + "/logs"); logsStart(inst.getSelectedValues()[0]);
logSocket.addEventListener('message', (event) => { inst.destroy();
const data = JSON.parse(event.data);
if (data.event === "line") {
const msg = data.data;
log.innerHTML += colorReplace(msg);
}
});
logSocket.addEventListener('open', (event) => {
logSocket.send(configuration);
});
logSocket.addEventListener('close', () => {
M.toast({html: 'Terminated process.'});
});
instance.options.onCloseStart = () => {
logSocket.close();
};
});
}); });
const modalRunElem = document.getElementById("modal-run"); document.querySelectorAll(".action-show-logs").forEach((showLogs) => {
const submitButton = modalRunElem.querySelector('.upload-port-submit'); showLogs.addEventListener('click', (e) => {
let startRun = undefined;
const select = modalRunElem.querySelector('select');
const uploadPort = modalRunElem.querySelector(".upload-port");
document.querySelectorAll(".action-upload").forEach((actionUpload) => {
actionUpload.addEventListener('click', (e) => {
configuration = e.target.getAttribute('data-node'); configuration = e.target.getAttribute('data-node');
const modalInstance = M.Modal.getInstance(logsModalElem);
const modal_instance = M.Modal.getInstance(modalRunElem); const log = logsModalElem.querySelector(".log");
if (M.FormSelect.getInstance(select) === undefined) {
M.FormSelect.getInstance(select).destroy();
}
const log = modalRunElem.querySelector(".log");
log.innerHTML = ""; log.innerHTML = "";
if (uploadPort.classList.contains('hide')) { if (M.FormSelect.getInstance(logsPortSelect) !== undefined) {
uploadPort.classList.remove('hide'); M.FormSelect.getInstance(logsPortSelect).destroy();
}
modalInstance.open();
if (logsPortDiv.classList.contains('hide')) {
logsPortDiv.classList.remove('hide');
} }
modal_instance.open(); logsStart = (port) => {
logsPortDiv.classList.add('hide');
startRun = (port) => { const logSocket = new WebSocket(ws_url + "/logs");
const logSocket = new WebSocket(ws_url + "/run");
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") {
const msg = data.data; const msg = data.data;
log.innerHTML += colorReplace(msg); log.innerHTML += colorReplace(msg);
} else if (data.event === "exit") {
M.toast({html: `Program exited with code ${data.code}`});
} }
}); });
logSocket.addEventListener('open', () => { logSocket.addEventListener('open', () => {
@ -539,7 +557,7 @@
logSocket.addEventListener('close', () => { logSocket.addEventListener('close', () => {
M.toast({html: 'Terminated process.'}); M.toast({html: 'Terminated process.'});
}); });
modal_instance.options.onCloseStart = () => { modalInstance.options.onCloseStart = () => {
logSocket.close(); logSocket.close();
}; };
}; };
@ -547,26 +565,130 @@
fetch('/serial-ports').then(res => res.json()) fetch('/serial-ports').then(res => res.json())
.then(response => { .then(response => {
if (response.length > 1) { if (response.length > 1) {
select.innerHTML = ""; logsPortSelect.innerHTML = "";
for (let i = 0; i < response.length; i++) { for (let i = 0; i < response.length; i++) {
const val = response[i]; const val = response[i];
select.innerHTML += `<option value="${val.port}">${val.port} (${val.desc})</option>`; logsPortSelect.innerHTML += `<option value="${val.port}">${val.port} (${val.desc})</option>`;
} }
M.FormSelect.init(select, {}); M.FormSelect.init(logsPortSelect, {});
} else { } else {
uploadPort.classList.add('hide'); logsStart("OTA");
startRun("OTA");
} }
}); });
}); });
}); });
submitButton.addEventListener('click', () => { const uploadModalElem = document.getElementById("modal-upload");
const inst = M.FormSelect.getInstance(select); const uploadPortSelect = uploadModalElem.querySelector('select');
console.log(inst.getSelectedValues()); const uploadPortDiv = uploadModalElem.querySelector(".upload-port");
startRun(inst.getSelectedValues()[0]); 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(); inst.destroy();
uploadPort.classList.add('hide'); });
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") {
M.toast({html: `Program exited 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) => {
const data = JSON.parse(event.data);
if (data.event === "line") {
const msg = data.data;
log.innerHTML += colorReplace(msg);
} else if (data.event === "exit") {
M.toast({html: `Program exited with code ${data.code}`});
if (data.code === 0) {
downloadButton.classList.remove('disabled');
}
}
});
logSocket.addEventListener('open', () => {
const msg = JSON.stringify({configuration: configuration});
logSocket.send(msg);
});
logSocket.addEventListener('close', () => {
M.toast({html: 'Terminated process.'});
});
modalInstance.options.onCloseStart = () => {
logSocket.close();
};
});
});
downloadButton.addEventListener('click', () => {
const link = document.createElement("a");
link.download = name;
link.href = '/download.bin?configuration=' + encodeURIComponent(configuration);
link.click();
}); });
const modalSetupElem = document.getElementById("modal-wizard"); const modalSetupElem = document.getElementById("modal-wizard");
@ -586,14 +708,6 @@
showFeedbackLoader: true, showFeedbackLoader: true,
parallel: false parallel: false
}); });
document.getElementById('#step-1-continue').addEventListener('click', () => {
console.log("NEXT STEP");
$('.stepper').nextStep();
});
$('#step-1-continue').on('click', () => {
});
}; };
setupWizardStart.addEventListener('click', startWizard); setupWizardStart.addEventListener('click', startWizard);
</script> </script>