1
0
mirror of https://github.com/esphome/esphome.git synced 2025-03-14 06:38:17 +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
import argparse
from datetime import datetime
import logging
import os
import random
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.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.helpers import AssignmentExpression, RawStatement, _EXPRESSIONS, add, add_task, \
color, get_variable, indent, quote, statement, Expression
from esphomeyaml.helpers import AssignmentExpression, Expression, RawStatement, _EXPRESSIONS, add, \
add_task, color, get_variable, indent, quote, statement
_LOGGER = logging.getLogger(__name__)
@ -66,7 +67,7 @@ def choose_serial_port(config):
return result[opt][0]
def run_platformio(*cmd):
def run_platformio(*cmd, **kwargs):
def mock_exit(return_code):
raise SystemExit(return_code)
@ -75,10 +76,13 @@ def run_platformio(*cmd):
full_cmd = u' '.join(quote(x) for x in cmd)
_LOGGER.info(u"Running: %s", full_cmd)
try:
import platformio.__main__
main = kwargs.get('main')
if main is None:
import platformio.__main__
main = platformio.__main__.main
sys.argv = list(cmd)
sys.exit = mock_exit
return platformio.__main__.main()
return main() or 0
except KeyboardInterrupt:
return 1
except SystemExit as err:
@ -91,13 +95,19 @@ def run_platformio(*cmd):
sys.exit = orig_exit
def run_miniterm(config, port):
from serial.tools import miniterm
def run_miniterm(config, port, escape=False):
import serial
baud_rate = config.get(CONF_LOGGER, {}).get(CONF_BAUD_RATE, 115200)
sys.argv = ['miniterm', '--raw', '--exit-char', '3']
miniterm.main(
default_port=port,
default_baudrate=baud_rate)
_LOGGER.info("Starting log output from %s with baud rate %s", port, baud_rate)
with serial.Serial(port, baudrate=baud_rate) as ser:
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):
@ -162,9 +172,21 @@ def get_upload_host(config):
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):
_LOGGER.info("Uploading binary...")
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),
'-t', 'upload', '--upload-port', port)
@ -190,7 +212,7 @@ def upload_program(config, args, port):
def show_logs(config, args, port, escape=False):
if port != 'OTA':
run_miniterm(config, port)
run_miniterm(config, port, escape=escape)
return 0
return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id,
escape=escape)
@ -329,6 +351,9 @@ def parse_args(argv):
parser_upload.add_argument('--upload-port', help="Manually specify the upload port to use. "
"For example /dev/cu.SLAB_USBtoUART.")
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 '
'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('--escape', help="Escape ANSI color codes for HassIO",
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 "
"retain messages.")
@ -408,6 +435,8 @@ def main():
except ESPHomeYAMLError as e:
_LOGGER.error(e)
return 1
except KeyboardInterrupt:
return 1
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_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 = [
# C++ keywords http://en.cppreference.com/w/cpp/keyword

View File

@ -1,9 +1,9 @@
from __future__ import print_function
import codecs
import os
import json
import logging
import os
import random
import subprocess
@ -19,8 +19,8 @@ try:
except ImportError as err:
pass
from esphomeyaml import const
from esphomeyaml.__main__ import get_serial_ports
from esphomeyaml import const, core, __main__
from esphomeyaml.__main__ import get_serial_ports, get_base_path, get_name
from esphomeyaml.helpers import quote
_LOGGER = logging.getLogger(__name__)
@ -76,8 +76,9 @@ class EsphomeyamlLogsHandler(EsphomeyamlCommandWebSocket):
super(EsphomeyamlLogsHandler, self).__init__(application, request, **kwargs)
def build_command(self, message):
config_file = CONFIG_DIR + '/' + message
return ["esphomeyaml", config_file, "logs", '--escape', '--serial-port', 'OTA']
js = json.loads(message)
config_file = CONFIG_DIR + '/' + js['configuration']
return ["esphomeyaml", config_file, "logs", '--serial-port', js["port"], '--escape']
class EsphomeyamlRunHandler(EsphomeyamlCommandWebSocket):
@ -86,8 +87,19 @@ class EsphomeyamlRunHandler(EsphomeyamlCommandWebSocket):
def build_command(self, message):
js = json.loads(message)
config_file = CONFIG_DIR + '/' + js['configuration']
return ["esphomeyaml", config_file, "run", '--upload-port', js["port"], '--escape']
config_file = os.path.join(CONFIG_DIR, js['configuration'])
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):
@ -107,7 +119,7 @@ class WizardRequestHandler(tornado.web.RequestHandler):
def post(self):
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)
destination = os.path.join(CONFIG_DIR, kwargs['name'] + '.yaml')
with codecs.open(destination, 'w') as f_handle:
@ -116,10 +128,31 @@ class WizardRequestHandler(tornado.web.RequestHandler):
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):
def get(self):
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():
@ -128,15 +161,19 @@ def make_app():
(r"/", MainRequestHandler),
(r"/logs", EsphomeyamlLogsHandler),
(r"/run", EsphomeyamlRunHandler),
(r"/compile", EsphomeyamlCompileHandler),
(r"/download.bin", DownloadBinaryRequestHandler),
(r"/serial-ports", SerialPortRequestHandler),
(r"/wizard.html", WizardRequestHandler),
(r'/static/(.*)', tornado.web.StaticFileHandler, {'path': static_path}),
])
], debug=True)
def start_web_server(args):
global CONFIG_DIR
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...",
args.port, CONFIG_DIR)

View File

@ -72,6 +72,16 @@
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 {
font-weight: bold;
}
@ -112,20 +122,11 @@
.modal {
width: 90%;
max-height: 85%;
}
.log-container {
position: absolute;
bottom: 0;
width: calc(100% - 48px);
height: 80% !important;
}
.log {
position: absolute;
bottom: 0;
right: 0;
left: 0;
overflow-y: auto;
}
.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 {
background-color: #3f51b5 !important;
}
.select-port-container {
margin-top: 19px;
}
</style>
</head>
<body>
@ -171,7 +176,7 @@
<main>
<div class="container">
{% for file in files %}
{% for file, full_path in zip(files, full_path_files) %}
<div class="row">
<div class="col s8 offset-s2 m10 offset-m1 l12">
<div class="card horizontal">
@ -180,10 +185,14 @@
</div>
<div class="card-stacked">
<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 class="card-action">
<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>
</div>
</div>
@ -196,23 +205,14 @@
<div id="modal-logs" class="modal modal-fixed-footer">
<div class="modal-content">
<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="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">
<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>
@ -223,6 +223,44 @@
</div>
</div>
<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>
</div>
</div>
@ -259,7 +297,7 @@
<p>
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.
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>
</p>
<div class="input-field col s12">
@ -268,7 +306,7 @@
</div>
</div>
<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>
</li>
@ -278,7 +316,8 @@
<div class="row">
<p>
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>
<div class="input-field col s12">
<select id="esp_type" name="platform" required>
@ -347,8 +386,10 @@
</p>
<p>
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
you wish esphomelib to use (leave them empty if you're not using any authentication).
<code class="inlinecode">192.168.1.100</code> (Note
<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>
<div class="input-field col s12">
<input id="mqtt_broker" class="validate" type="text" name="broker" required>
@ -383,7 +424,8 @@
<li>
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>
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>
See the <a href="https://esphomelib.com/esphomeyaml/index.html" target="_blank">esphomeyaml index</a>
@ -468,68 +510,44 @@
let configuration = "";
const ws_url = 'ws://' + window.location.hostname + ':' + window.location.port;
document.querySelectorAll(".action-show-logs").forEach((showLogs) => {
console.log(showLogs);
showLogs.addEventListener('click', (e) => {
configuration = e.target.getAttribute('data-node');
const elem = document.getElementById("modal-logs");
const instance = M.Modal.getInstance(elem);
const log = elem.querySelector(".log");
log.innerHTML = "";
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;
instance.open();
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);
}
});
logSocket.addEventListener('open', (event) => {
logSocket.send(configuration);
});
logSocket.addEventListener('close', () => {
M.toast({html: 'Terminated process.'});
});
instance.options.onCloseStart = () => {
logSocket.close();
};
});
logsPortSubmit.addEventListener('click', () => {
const inst = M.FormSelect.getInstance(logsPortSelect);
logsStart(inst.getSelectedValues()[0]);
inst.destroy();
});
const modalRunElem = document.getElementById("modal-run");
const submitButton = modalRunElem.querySelector('.upload-port-submit');
let startRun = undefined;
const select = modalRunElem.querySelector('select');
const uploadPort = modalRunElem.querySelector(".upload-port");
document.querySelectorAll(".action-upload").forEach((actionUpload) => {
actionUpload.addEventListener('click', (e) => {
document.querySelectorAll(".action-show-logs").forEach((showLogs) => {
showLogs.addEventListener('click', (e) => {
configuration = e.target.getAttribute('data-node');
const modal_instance = M.Modal.getInstance(modalRunElem);
if (M.FormSelect.getInstance(select) === undefined) {
M.FormSelect.getInstance(select).destroy();
}
const log = modalRunElem.querySelector(".log");
const modalInstance = M.Modal.getInstance(logsModalElem);
const log = logsModalElem.querySelector(".log");
log.innerHTML = "";
if (uploadPort.classList.contains('hide')) {
uploadPort.classList.remove('hide');
if (M.FormSelect.getInstance(logsPortSelect) !== undefined) {
M.FormSelect.getInstance(logsPortSelect).destroy();
}
modalInstance.open();
if (logsPortDiv.classList.contains('hide')) {
logsPortDiv.classList.remove('hide');
}
modal_instance.open();
startRun = (port) => {
const logSocket = new WebSocket(ws_url + "/run");
logsStart = (port) => {
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") {
M.toast({html: `Program exited with code ${data.code}`});
}
});
logSocket.addEventListener('open', () => {
@ -539,7 +557,7 @@
logSocket.addEventListener('close', () => {
M.toast({html: 'Terminated process.'});
});
modal_instance.options.onCloseStart = () => {
modalInstance.options.onCloseStart = () => {
logSocket.close();
};
};
@ -547,26 +565,130 @@
fetch('/serial-ports').then(res => res.json())
.then(response => {
if (response.length > 1) {
select.innerHTML = "";
logsPortSelect.innerHTML = "";
for (let i = 0; i < response.length; 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 {
uploadPort.classList.add('hide');
startRun("OTA");
logsStart("OTA");
}
});
});
});
submitButton.addEventListener('click', () => {
const inst = M.FormSelect.getInstance(select);
console.log(inst.getSelectedValues());
startRun(inst.getSelectedValues()[0]);
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();
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");
@ -586,14 +708,6 @@
showFeedbackLoader: true,
parallel: false
});
document.getElementById('#step-1-continue').addEventListener('click', () => {
console.log("NEXT STEP");
$('.stepper').nextStep();
});
$('#step-1-continue').on('click', () => {
});
};
setupWizardStart.addEventListener('click', startWizard);
</script>