Dashboard Update all button (#615)

* Add update all button

* Use bold
This commit is contained in:
Otto Winter 2019-06-07 14:26:28 +02:00 committed by GitHub
parent 4fe0c95ccb
commit 7a895adec9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 128 additions and 27 deletions

View File

@ -15,7 +15,7 @@ from esphome.const import CONF_BAUD_RATE, CONF_BROKER, CONF_LOGGER, CONF_OTA, \
from esphome.core import CORE, EsphomeError, coroutine, coroutine_with_priority
from esphome.helpers import color, indent
from esphome.py_compat import IS_PY2, safe_input
from esphome.util import run_external_command, run_external_process, safe_print
from esphome.util import run_external_command, run_external_process, safe_print, list_yaml_files
_LOGGER = logging.getLogger(__name__)
@ -187,8 +187,7 @@ def upload_program(config, args, host):
ota_conf = config[CONF_OTA]
remote_port = ota_conf[CONF_PORT]
password = ota_conf[CONF_PASSWORD]
res = espota2.run_ota(host, remote_port, password, CORE.firmware_bin)
return res
return espota2.run_ota(host, remote_port, password, CORE.firmware_bin)
def show_logs(config, args, port):
@ -350,11 +349,52 @@ def command_dashboard(args):
return dashboard.start_web_server(args)
def command_update_all(args):
import click
success = {}
files = list_yaml_files(args.configuration)
twidth = 60
def print_bar(middle_text):
middle_text = " {} ".format(middle_text)
width = len(click.unstyle(middle_text))
half_line = "=" * ((twidth - width) / 2)
click.echo("%s%s%s" % (half_line, middle_text, half_line))
for f in files:
print("Updating {}".format(color('cyan', f)))
print('-' * twidth)
print()
rc = run_external_process('esphome', f, 'run', '--no-logs')
if rc == 0:
print_bar("[{}] {}".format(color('bold_green', 'SUCCESS'), f))
success[f] = True
else:
print_bar("[{}] {}".format(color('bold_red', 'ERROR'), f))
success[f] = False
print()
print()
print()
print_bar('[{}]'.format(color('bold_white', 'SUMMARY')))
failed = 0
for f in files:
if success[f]:
print(" - {}: {}".format(f, color('green', 'SUCCESS')))
else:
print(" - {}: {}".format(f, color('bold_red', 'FAILED')))
failed += 1
return failed
PRE_CONFIG_ACTIONS = {
'wizard': command_wizard,
'version': command_version,
'dashboard': command_dashboard,
'vscode': command_vscode,
'update-all': command_update_all,
}
POST_CONFIG_ACTIONS = {
@ -446,6 +486,8 @@ def parse_args(argv):
vscode = subparsers.add_parser('vscode', help=argparse.SUPPRESS)
vscode.add_argument('--ace', action='store_true')
subparsers.add_parser('update-all', help=argparse.SUPPRESS)
return parser.parse_args(argv[1:])

View File

@ -26,7 +26,7 @@ import tornado.process
import tornado.web
import tornado.websocket
from esphome import const
from esphome import const, util
from esphome.__main__ import get_serial_ports
from esphome.helpers import mkdir_p, get_bool_env, run_system_command
from esphome.py_compat import IS_PY2, decode_text
@ -93,17 +93,7 @@ class DashboardSettings(object):
return os.path.join(self.config_dir, *args)
def list_yaml_files(self):
files = []
for file in os.listdir(self.config_dir):
if not file.endswith('.yaml'):
continue
if file.startswith('.'):
continue
if file == 'secrets.yaml':
continue
files.append(file)
files.sort()
return files
return util.list_yaml_files(self.config_dir)
settings = DashboardSettings()
@ -122,6 +112,7 @@ def template_args():
'get_static_file_url': get_static_file_url,
'relative_url': settings.relative_url,
'streamer_mode': get_bool_env('ESPHOME_STREAMER_MODE'),
'config_dir': settings.config_dir,
}
@ -315,6 +306,11 @@ class EsphomeAceEditorHandler(EsphomeCommandWebSocket):
return ["esphome", "--dashboard", "-q", settings.config_dir, "vscode", "--ace"]
class EsphomeUpdateAllHandler(EsphomeCommandWebSocket):
def build_command(self, json_message):
return ["esphome", "--dashboard", settings.config_dir, "update-all"]
class SerialPortRequestHandler(BaseHandler):
@authenticated
def get(self):
@ -690,6 +686,7 @@ def make_app(debug=False):
(rel + "clean", EsphomeCleanHandler),
(rel + "vscode", EsphomeVscodeHandler),
(rel + "ace", EsphomeAceEditorHandler),
(rel + "update-all", EsphomeUpdateAllHandler),
(rel + "edit", EditRequestHandler),
(rel + "download.bin", DownloadBinaryRequestHandler),
(rel + "serial-ports", SerialPortRequestHandler),

View File

@ -131,10 +131,14 @@ ul.stepper:not(.horizontal) .step.active::before, ul.stepper:not(.horizontal) .s
.select-port-container {
margin-top: 8px;
margin-right: 24px;
margin-right: 10px;
width: 350px;
}
#dropdown-nav-trigger {
margin-right: 24px;
}
.select-port-container .select-dropdown {
color: #fff;
}

View File

@ -333,6 +333,10 @@ class LogModalElem {
this.activeSocket.close();
}
open(event) {
this._onPress(event);
}
_onPress(event) {
this.activeConfig = event.target.getAttribute('data-node');
this._setupModalInstance();
@ -745,3 +749,32 @@ jQuery.validator.addMethod("nospaces", (value, element) => {
jQuery.validator.addMethod("lowercase", (value, element) => {
return value === value.toLowerCase();
}, "Name must be lowercase.");
const updateAllModal = new LogModalElem({
name: 'update-all',
onPrepare: (modalElem, config) => {
modalElem.querySelector('.stop-logs').innerHTML = "Stop";
downloadButton.classList.add('disabled');
},
onProcessExit: (modalElem, code) => {
if (code === 0) {
M.toast({html: "Program exited successfully."});
downloadButton.classList.remove('disabled');
} else {
M.toast({html: `Program failed with code ${data.code}`});
}
modalElem.querySelector(".stop-logs").innerHTML = "Close";
},
onSocketClose: (modalElem) => {
M.toast({html: 'Terminated process.'});
},
dismissible: false,
});
updateAllModal.setup();
const updateAllButton = document.getElementById('update-all-button');
updateAllButton.addEventListener('click', (e) => {
updateAllModal.open(e);
});

View File

@ -31,10 +31,16 @@
<nav>
<div class="nav-wrapper indigo">
<a href="#" class="brand-logo left">ESPHome Dashboard</a>
<i class="material-icons dropdown-trigger right" id="dropdown-nav-trigger" data-target="dropdown-nav-actions">more_vert</i>
<div class="select-port-container right" id="select-port-target">
<select></select>
</div>
</div>
<ul id="dropdown-nav-actions" class="select-action dropdown-content card-dropdown-action">
<li><a id="update-all-button" class="modal-close waves-effect waves-green btn-flat"
data-node="{{ escape(config_dir) }}">Update All</a></li>
</ul>
</nav>
{% if begin %}
@ -445,6 +451,16 @@
</div>
</div>
<div id="modal-update-all" class="modal modal-fixed-footer">
<div class="modal-content">
<h4>Update All</h4>
<pre class="log"></pre>
</div>
<div class="modal-footer">
<a class="modal-close waves-effect waves-green btn-flat stop-logs">Stop</a>
</div>
</div>
<a class="btn-floating btn-large ribbon-fab waves-effect waves-light pink accent-2" id="setup-wizard-start">
<i class="material-icons">add</i>
</a>

View File

@ -299,3 +299,4 @@ def run_ota(remote_host, remote_port, password, filename):
return run_ota_impl_(remote_host, remote_port, password, filename)
except OTAError as err:
_LOGGER.error(err)
return 1

View File

@ -3,6 +3,7 @@ from __future__ import print_function
import collections
import io
import logging
import os
import re
import subprocess
import sys
@ -207,3 +208,16 @@ class OrderedDict(collections.OrderedDict):
root[1] = first[0] = link
else:
super(OrderedDict, self).move_to_end(key, last=last) # pylint: disable=no-member
def list_yaml_files(folder):
files = filter_yaml_files([os.path.join(folder, p) for p in os.listdir(folder)])
files.sort()
return files
def filter_yaml_files(files):
files = [f for f in files if os.path.splitext(f)[1] == '.yaml']
files = [f for f in files if os.path.basename(f) != 'secrets.yaml']
files = [f for f in files if not os.path.basename(f).startswith('.')]
return files

View File

@ -14,7 +14,7 @@ from esphome import core
from esphome.config_helpers import read_config_file
from esphome.core import EsphomeError, IPAddress, Lambda, MACAddress, TimePeriod, DocumentRange
from esphome.py_compat import text_type, IS_PY2
from esphome.util import OrderedDict
from esphome.util import OrderedDict, filter_yaml_files
_LOGGER = logging.getLogger(__name__)
@ -260,12 +260,12 @@ class ESPHomeLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors
@_add_data_ref
def construct_include_dir_list(self, node):
files = _filter_yaml_files(_find_files(self._rel_path(node.value), '*.yaml'))
files = filter_yaml_files(_find_files(self._rel_path(node.value), '*.yaml'))
return [_load_yaml_internal(f) for f in files]
@_add_data_ref
def construct_include_dir_merge_list(self, node):
files = _filter_yaml_files(_find_files(self._rel_path(node.value), '*.yaml'))
files = filter_yaml_files(_find_files(self._rel_path(node.value), '*.yaml'))
merged_list = []
for fname in files:
loaded_yaml = _load_yaml_internal(fname)
@ -275,7 +275,7 @@ class ESPHomeLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors
@_add_data_ref
def construct_include_dir_named(self, node):
files = _filter_yaml_files(_find_files(self._rel_path(node.value), '*.yaml'))
files = filter_yaml_files(_find_files(self._rel_path(node.value), '*.yaml'))
mapping = OrderedDict()
for fname in files:
filename = os.path.splitext(os.path.basename(fname))[0]
@ -284,7 +284,7 @@ class ESPHomeLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors
@_add_data_ref
def construct_include_dir_merge_named(self, node):
files = _filter_yaml_files(_find_files(self._rel_path(node.value), '*.yaml'))
files = filter_yaml_files(_find_files(self._rel_path(node.value), '*.yaml'))
mapping = OrderedDict()
for fname in files:
loaded_yaml = _load_yaml_internal(fname)
@ -297,12 +297,6 @@ class ESPHomeLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors
return Lambda(text_type(node.value))
def _filter_yaml_files(files):
files = [f for f in files if os.path.basename(f) != SECRET_YAML]
files = [f for f in files if not os.path.basename(f).startswith('.')]
return files
ESPHomeLoader.add_constructor(u'tag:yaml.org,2002:int', ESPHomeLoader.construct_yaml_int)
ESPHomeLoader.add_constructor(u'tag:yaml.org,2002:float', ESPHomeLoader.construct_yaml_float)
ESPHomeLoader.add_constructor(u'tag:yaml.org,2002:binary', ESPHomeLoader.construct_yaml_binary)