mirror of
https://github.com/esphome/esphome.git
synced 2025-02-13 00:18:21 +00:00
Better ANSI color escaping
This commit is contained in:
parent
8587e7ad74
commit
41d5dcded1
@ -63,7 +63,7 @@ def choose_serial_port(config):
|
||||
return result[opt][0]
|
||||
|
||||
|
||||
def run_miniterm(config, port, escape=False):
|
||||
def run_miniterm(config, port):
|
||||
import serial
|
||||
if CONF_LOGGER not in config:
|
||||
_LOGGER.info("Logger is not enabled. Not starting UART logs.")
|
||||
@ -84,7 +84,7 @@ def run_miniterm(config, port, escape=False):
|
||||
line = raw.replace('\r', '').replace('\n', '')
|
||||
time = datetime.now().time().strftime('[%H:%M:%S]')
|
||||
message = time + line
|
||||
if escape:
|
||||
if core.FROM_DASHBOARD:
|
||||
message = message.replace('\033', '\\033')
|
||||
safe_print(message)
|
||||
|
||||
@ -152,7 +152,7 @@ def upload_program(config, args, port):
|
||||
# if upload is to a serial port use platformio, otherwise assume ota
|
||||
serial_port = port.startswith('/') or port.startswith('COM')
|
||||
if port != 'OTA' and serial_port:
|
||||
if CORE.is_esp8266 and args.use_esptoolpy:
|
||||
if CORE.is_esp8266 and args.dashboard:
|
||||
return upload_using_esptool(config, port)
|
||||
return platformio_api.run_upload(config, args.verbose, port)
|
||||
|
||||
@ -187,13 +187,12 @@ def upload_program(config, args, port):
|
||||
CORE.firmware_bin)
|
||||
|
||||
|
||||
def show_logs(config, args, port, escape=False):
|
||||
def show_logs(config, args, port):
|
||||
serial_port = port.startswith('/') or port.startswith('COM')
|
||||
if port != 'OTA' and serial_port:
|
||||
run_miniterm(config, port, escape=escape)
|
||||
run_miniterm(config, port)
|
||||
return 0
|
||||
return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id,
|
||||
escape=escape)
|
||||
return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id)
|
||||
|
||||
|
||||
def clean_mqtt(config, args):
|
||||
@ -282,7 +281,7 @@ def command_upload(args, config):
|
||||
|
||||
def command_logs(args, config):
|
||||
port = args.serial_port or choose_serial_port(config)
|
||||
return show_logs(config, args, port, escape=args.escape)
|
||||
return show_logs(config, args, port)
|
||||
|
||||
|
||||
def command_run(args, config):
|
||||
@ -300,7 +299,7 @@ def command_run(args, config):
|
||||
_LOGGER.info(u"Successfully uploaded program.")
|
||||
if args.no_logs:
|
||||
return 0
|
||||
return show_logs(config, args, port, escape=args.escape)
|
||||
return show_logs(config, args, port)
|
||||
|
||||
|
||||
def command_clean_mqtt(args, config):
|
||||
@ -381,21 +380,24 @@ def parse_args(argv):
|
||||
|
||||
subparsers = parser.add_subparsers(help='Commands', dest='command')
|
||||
subparsers.required = True
|
||||
subparsers.add_parser('config', help='Validate the configuration and spit it out.')
|
||||
config = subparsers.add_parser('config', help='Validate the configuration and spit it out.')
|
||||
config.add_argument('--dashboard', help="Internal flag used by the dashboard",
|
||||
action='store_true')
|
||||
|
||||
parser_compile = subparsers.add_parser('compile',
|
||||
help='Read the configuration and compile a program.')
|
||||
parser_compile.add_argument('--only-generate',
|
||||
help="Only generate source code, do not compile.",
|
||||
action='store_true')
|
||||
parser_compile.add_argument('--dashboard', help="Internal flag used by the dashboard",
|
||||
action='store_true')
|
||||
|
||||
parser_upload = subparsers.add_parser('upload', help='Validate the configuration '
|
||||
'and upload the latest binary.')
|
||||
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 the uploading (only for ESP8266)",
|
||||
parser_upload.add_argument('--dashboard', help="Internal flag used by the dashboard",
|
||||
action='store_true')
|
||||
|
||||
parser_logs = subparsers.add_parser('logs', help='Validate the configuration '
|
||||
@ -406,7 +408,7 @@ def parse_args(argv):
|
||||
parser_logs.add_argument('--client-id', help='Manually set the client id.')
|
||||
parser_logs.add_argument('--serial-port', help="Manually specify a serial port to use"
|
||||
"For example /dev/cu.SLAB_USBtoUART.")
|
||||
parser_logs.add_argument('--escape', help="Escape ANSI color codes for running in dashboard",
|
||||
parser_logs.add_argument('--dashboard', help="Internal flag used by the dashboard",
|
||||
action='store_true')
|
||||
|
||||
parser_run = subparsers.add_parser('run', help='Validate the configuration, create a binary, '
|
||||
@ -420,10 +422,7 @@ def parse_args(argv):
|
||||
parser_run.add_argument('--username', help='Manually set the MQTT username for logs.')
|
||||
parser_run.add_argument('--password', help='Manually set the MQTT password 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 running in dashboard",
|
||||
action='store_true')
|
||||
parser_run.add_argument('--use-esptoolpy',
|
||||
help="Use esptool.py for the uploading (only for ESP8266)",
|
||||
parser_run.add_argument('--dashboard', help="Internal flag used by the dashboard",
|
||||
action='store_true')
|
||||
|
||||
parser_clean = subparsers.add_parser('clean-mqtt', help="Helper to clear an MQTT topic from "
|
||||
@ -432,6 +431,8 @@ def parse_args(argv):
|
||||
parser_clean.add_argument('--username', help='Manually set the username.')
|
||||
parser_clean.add_argument('--password', help='Manually set the password.')
|
||||
parser_clean.add_argument('--client-id', help='Manually set the client id.')
|
||||
parser_clean.add_argument('--dashboard', help="Internal flag used by the dashboard",
|
||||
action='store_true')
|
||||
|
||||
subparsers.add_parser('wizard', help="A helpful setup wizard that will guide "
|
||||
"you through setting up esphomeyaml.")
|
||||
@ -440,7 +441,9 @@ def parse_args(argv):
|
||||
|
||||
subparsers.add_parser('version', help="Print the esphomeyaml version and exit.")
|
||||
|
||||
subparsers.add_parser('clean', help="Delete all temporary build files.")
|
||||
clean = subparsers.add_parser('clean', help="Delete all temporary build files.")
|
||||
clean.add_argument('--dashboard', help="Internal flag used by the dashboard",
|
||||
action='store_true')
|
||||
|
||||
dashboard = subparsers.add_parser('dashboard',
|
||||
help="Create a simple web server for a dashboard.")
|
||||
@ -455,14 +458,20 @@ def parse_args(argv):
|
||||
"add-on.",
|
||||
action="store_true")
|
||||
|
||||
subparsers.add_parser('hass-config', help="Dump the configuration entries that should be added"
|
||||
hass_config = subparsers.add_parser('hass-config',
|
||||
help="Dump the configuration entries that should be added "
|
||||
"to Home Assistant when not using MQTT discovery.")
|
||||
hass_config.add_argument('--dashboard', help="Internal flag used by the dashboard",
|
||||
action='store_true')
|
||||
|
||||
return parser.parse_args(argv[1:])
|
||||
|
||||
|
||||
def run_esphomeyaml(argv):
|
||||
args = parse_args(argv)
|
||||
if hasattr(args, 'dashboard'):
|
||||
core.FROM_DASHBOARD = args.dashboard
|
||||
|
||||
setup_log(args.verbose)
|
||||
if args.command in PRE_CONFIG_ACTIONS:
|
||||
try:
|
||||
|
@ -59,7 +59,7 @@ WIFI_NETWORK_STA = WIFI_NETWORK_BASE.extend({
|
||||
def validate(config):
|
||||
if CONF_PASSWORD in config and CONF_SSID not in config:
|
||||
raise vol.Invalid("Cannot have WiFi password without SSID!")
|
||||
if CONF_SSID not in config and CONF_AP not in config:
|
||||
if (CONF_SSID not in config) and (CONF_AP not in config):
|
||||
raise vol.Invalid("Please specify at least an SSID or an Access Point "
|
||||
"to create.")
|
||||
return config
|
||||
|
@ -2,10 +2,10 @@ from __future__ import print_function
|
||||
|
||||
from collections import OrderedDict
|
||||
import importlib
|
||||
import json
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
from voluptuous.humanize import humanize_error
|
||||
|
||||
from esphomeyaml import core, core_config, yaml_util
|
||||
from esphomeyaml.const import CONF_ESPHOMEYAML, CONF_PLATFORM, CONF_WIFI, ESP_PLATFORMS
|
||||
@ -268,22 +268,53 @@ def validate_config(config):
|
||||
REQUIRED = ['esphomeyaml', 'wifi']
|
||||
|
||||
|
||||
def _format_config_error(ex, domain, config):
|
||||
message = u"Invalid config for [{}]: ".format(domain)
|
||||
def _nested_getitem(data, path):
|
||||
for item_index in path:
|
||||
try:
|
||||
data = data[item_index]
|
||||
except (KeyError, IndexError, TypeError):
|
||||
return None
|
||||
return data
|
||||
|
||||
|
||||
def _format_path(path):
|
||||
return u'->'.join(unicode(m) for m in path)
|
||||
|
||||
|
||||
def humanize_error(config, validation_error):
|
||||
offending_item_summary = _nested_getitem(config, validation_error.path)
|
||||
if isinstance(offending_item_summary, dict):
|
||||
offending_item_summary = json.dumps(offending_item_summary)
|
||||
return u'{}. Got {}'.format(validation_error, offending_item_summary)
|
||||
|
||||
|
||||
def _format_config_error(ex, domain, config, recursion=False):
|
||||
message = u"" if recursion else u"Invalid config for [{}]: ".format(domain)
|
||||
if isinstance(ex, vol.MultipleInvalid):
|
||||
return color('red', message + u'\n'.join(sorted(
|
||||
_format_config_error(sub_error, domain, config, recursion=True)
|
||||
for sub_error in ex.errors
|
||||
)))
|
||||
|
||||
if u'extra keys not allowed' in ex.error_message:
|
||||
message += u'[{}] is an invalid option for [{}]. Check: {}->{}.' \
|
||||
.format(ex.path[-1], domain, domain,
|
||||
u'->'.join(str(m) for m in ex.path))
|
||||
message += u'[{}] is an invalid option for [{}].' \
|
||||
.format(ex.path[-1], domain)
|
||||
elif u'required key not provided' in ex.error_message:
|
||||
message += u"'{}' is a required option for [{}]." \
|
||||
u"".format(ex.path[-1], domain)
|
||||
else:
|
||||
message += u'{}.'.format(humanize_error(config, ex))
|
||||
|
||||
message += u' Check {}->{}.'.format(domain, _format_path(ex.path))
|
||||
message = color('red', message)
|
||||
|
||||
if isinstance(config, list):
|
||||
return message
|
||||
|
||||
domain_config = config.get(domain, config)
|
||||
message += u" (See {}, line {}). ".format(
|
||||
message += color('cyan', u" (See {}, line {}). ".format(
|
||||
getattr(domain_config, '__config_file__', '?'),
|
||||
getattr(domain_config, '__line__', '?'))
|
||||
getattr(domain_config, '__line__', '?')))
|
||||
|
||||
return message
|
||||
|
||||
@ -316,7 +347,7 @@ def line_info(obj, **kwargs):
|
||||
return '?'
|
||||
|
||||
|
||||
def dump_dict(layer, indent_count=3, listi=False, **kwargs):
|
||||
def dump_dict(layer, indent_count=0, listi=False, **kwargs):
|
||||
def sort_dict_key(val):
|
||||
"""Return the dict key for sorting."""
|
||||
key = str.lower(val[0])
|
||||
@ -358,9 +389,7 @@ def read_config():
|
||||
if excepts:
|
||||
safe_print(color('bold_white', u"Failed config"))
|
||||
for domain, config in excepts.iteritems():
|
||||
safe_print(u' {} {}'.format(color('bold_red', domain + u':'),
|
||||
color('red', '', reset='red')))
|
||||
dump_dict(config, reset='red')
|
||||
safe_print(color('reset'))
|
||||
safe_print(color('bold_red', domain + u':'))
|
||||
dump_dict(config)
|
||||
return None
|
||||
return OrderedDict(res)
|
||||
|
@ -398,3 +398,4 @@ CORE = EsphomeyamlCore()
|
||||
|
||||
ConfigType = Dict[str, Any]
|
||||
CoreType = EsphomeyamlCore
|
||||
FROM_DASHBOARD = False
|
||||
|
@ -105,7 +105,7 @@ class EsphomeyamlLogsHandler(EsphomeyamlCommandWebSocket):
|
||||
def build_command(self, message):
|
||||
js = json.loads(message)
|
||||
config_file = CONFIG_DIR + '/' + js['configuration']
|
||||
return ["esphomeyaml", config_file, "logs", '--serial-port', js["port"], '--escape']
|
||||
return ["esphomeyaml", config_file, "logs", '--serial-port', js["port"], '--dashboard']
|
||||
|
||||
|
||||
class EsphomeyamlRunHandler(EsphomeyamlCommandWebSocket):
|
||||
@ -113,42 +113,42 @@ class EsphomeyamlRunHandler(EsphomeyamlCommandWebSocket):
|
||||
js = json.loads(message)
|
||||
config_file = os.path.join(CONFIG_DIR, js['configuration'])
|
||||
return ["esphomeyaml", config_file, "run", '--upload-port', js["port"],
|
||||
'--escape', '--use-esptoolpy']
|
||||
'--dashboard']
|
||||
|
||||
|
||||
class EsphomeyamlCompileHandler(EsphomeyamlCommandWebSocket):
|
||||
def build_command(self, message):
|
||||
js = json.loads(message)
|
||||
config_file = os.path.join(CONFIG_DIR, js['configuration'])
|
||||
return ["esphomeyaml", config_file, "compile"]
|
||||
return ["esphomeyaml", config_file, "compile", '--dashboard']
|
||||
|
||||
|
||||
class EsphomeyamlValidateHandler(EsphomeyamlCommandWebSocket):
|
||||
def build_command(self, message):
|
||||
js = json.loads(message)
|
||||
config_file = os.path.join(CONFIG_DIR, js['configuration'])
|
||||
return ["esphomeyaml", config_file, "config"]
|
||||
return ["esphomeyaml", config_file, "config", '--dashboard']
|
||||
|
||||
|
||||
class EsphomeyamlCleanMqttHandler(EsphomeyamlCommandWebSocket):
|
||||
def build_command(self, message):
|
||||
js = json.loads(message)
|
||||
config_file = os.path.join(CONFIG_DIR, js['configuration'])
|
||||
return ["esphomeyaml", config_file, "clean-mqtt"]
|
||||
return ["esphomeyaml", config_file, "clean-mqtt", '--dashboard']
|
||||
|
||||
|
||||
class EsphomeyamlCleanHandler(EsphomeyamlCommandWebSocket):
|
||||
def build_command(self, message):
|
||||
js = json.loads(message)
|
||||
config_file = os.path.join(CONFIG_DIR, js['configuration'])
|
||||
return ["esphomeyaml", config_file, "clean"]
|
||||
return ["esphomeyaml", config_file, "clean", '--dashboard']
|
||||
|
||||
|
||||
class EsphomeyamlHassConfigHandler(EsphomeyamlCommandWebSocket):
|
||||
def build_command(self, message):
|
||||
js = json.loads(message)
|
||||
config_file = os.path.join(CONFIG_DIR, js['configuration'])
|
||||
return ["esphomeyaml", config_file, "hass-config"]
|
||||
return ["esphomeyaml", config_file, "hass-config", '--dashboard']
|
||||
|
||||
|
||||
class SerialPortRequestHandler(BaseHandler):
|
||||
|
@ -5,19 +5,19 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const colorReplace = (input) => {
|
||||
input = input.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
||||
input = input.replace(/\\033\[(?:0;)?31m/g, '<span class="e">');
|
||||
input = input.replace(/\\033\[(?:1;)?31m/g, '<span class="e bold">');
|
||||
input = input.replace(/\\033\[(?:0?1;)?31m/g, '<span class="e bold">');
|
||||
input = input.replace(/\\033\[(?:0;)?32m/g, '<span class="i">');
|
||||
input = input.replace(/\\033\[(?:1;)?32m/g, '<span class="i bold">');
|
||||
input = input.replace(/\\033\[(?:0?1;)?32m/g, '<span class="i bold">');
|
||||
input = input.replace(/\\033\[(?:0;)?33m/g, '<span class="w">');
|
||||
input = input.replace(/\\033\[(?:1;)?33m/g, '<span class="w bold">');
|
||||
input = input.replace(/\\033\[(?:0?1;)?33m/g, '<span class="w bold">');
|
||||
input = input.replace(/\\033\[(?:0;)?35m/g, '<span class="c">');
|
||||
input = input.replace(/\\033\[(?:1;)?35m/g, '<span class="c bold">');
|
||||
input = input.replace(/\\033\[(?:0?1;)?35m/g, '<span class="c bold">');
|
||||
input = input.replace(/\\033\[(?:0;)?36m/g, '<span class="d">');
|
||||
input = input.replace(/\\033\[(?:1;)?36m/g, '<span class="d bold">');
|
||||
input = input.replace(/\\033\[(?:0?1;)?36m/g, '<span class="d bold">');
|
||||
input = input.replace(/\\033\[(?:0;)?37m/g, '<span class="v">');
|
||||
input = input.replace(/\\033\[(?:1;)?37m/g, '<span class="v bold">');
|
||||
input = input.replace(/\\033\[(?:0?1;)?37m/g, '<span class="v bold">');
|
||||
input = input.replace(/\\033\[(?:0;)?38m/g, '<span class="vv">');
|
||||
input = input.replace(/\\033\[(?:1;)?38m/g, '<span class="vv bold">');
|
||||
input = input.replace(/\\033\[(?:0?1;)?38m/g, '<span class="vv bold">');
|
||||
input = input.replace(/\\033\[0m/g, '</span>');
|
||||
|
||||
return input;
|
||||
@ -330,6 +330,7 @@ upload.addEventListener('click', (e) => {
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
downloadButton.addEventListener('click', () => {
|
||||
const link = document.createElement("a");
|
||||
link.download = name;
|
||||
@ -540,4 +541,5 @@ $('.stepper').activateStepper({
|
||||
parallel: false
|
||||
});
|
||||
};
|
||||
|
||||
setupWizardStart.addEventListener('click', startWizard);
|
@ -49,12 +49,18 @@ def cpp_string_escape(string, encoding='utf-8'):
|
||||
return '"' + result + '"'
|
||||
|
||||
|
||||
def color(the_color, message='', reset=None):
|
||||
"""Color helper."""
|
||||
def color(the_color, message=''):
|
||||
from esphomeyaml import core
|
||||
from colorlog.escape_codes import escape_codes, parse_colors
|
||||
|
||||
if not message:
|
||||
return parse_colors(the_color)
|
||||
return parse_colors(the_color) + message + escape_codes[reset or 'reset']
|
||||
res = parse_colors(the_color)
|
||||
else:
|
||||
res = parse_colors(the_color) + message + escape_codes['reset']
|
||||
|
||||
if core.FROM_DASHBOARD:
|
||||
res = res.replace('\033', '\\033')
|
||||
return res
|
||||
|
||||
|
||||
def run_system_command(*args):
|
||||
|
@ -9,6 +9,7 @@ import sys
|
||||
|
||||
import paho.mqtt.client as mqtt
|
||||
|
||||
from esphomeyaml import core
|
||||
from esphomeyaml.const import CONF_BROKER, CONF_DISCOVERY_PREFIX, CONF_ESPHOMEYAML, \
|
||||
CONF_LOG_TOPIC, CONF_MQTT, CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_SSL_FINGERPRINTS, \
|
||||
CONF_TOPIC, CONF_TOPIC_PREFIX, CONF_USERNAME
|
||||
@ -54,7 +55,7 @@ def initialize(config, subscriptions, on_message, username, password, client_id)
|
||||
return 0
|
||||
|
||||
|
||||
def show_logs(config, topic=None, username=None, password=None, client_id=None, escape=False):
|
||||
def show_logs(config, topic=None, username=None, password=None, client_id=None):
|
||||
if topic is not None:
|
||||
pass # already have topic
|
||||
elif CONF_MQTT in config:
|
||||
@ -73,7 +74,7 @@ def show_logs(config, topic=None, username=None, password=None, client_id=None,
|
||||
def on_message(client, userdata, msg):
|
||||
time = datetime.now().time().strftime(u'[%H:%M:%S]')
|
||||
message = time + msg.payload
|
||||
if escape:
|
||||
if core.FROM_DASHBOARD:
|
||||
message = message.replace('\033', '\\033')
|
||||
safe_print(message)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user