mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	Better ANSI color escaping
This commit is contained in:
		| @@ -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; | ||||
| @@ -26,17 +26,17 @@ return input; | ||||
| let configuration = ""; | ||||
| let wsProtocol = "ws:"; | ||||
| if (window.location.protocol === "https:") { | ||||
| wsProtocol = 'wss:'; | ||||
|   wsProtocol = 'wss:'; | ||||
| } | ||||
| const wsUrl = wsProtocol + '//' + window.location.hostname + ':' + window.location.port; | ||||
|  | ||||
| let isFetchingPing = false; | ||||
| const fetchPing = () => { | ||||
| if (isFetchingPing) | ||||
|   if (isFetchingPing) | ||||
|       return; | ||||
| isFetchingPing = true; | ||||
|   isFetchingPing = true; | ||||
|  | ||||
| fetch('/ping', {credentials: "same-origin"}).then(res => res.json()) | ||||
|   fetch('/ping', {credentials: "same-origin"}).then(res => res.json()) | ||||
|     .then(response => { | ||||
|       for (let filename in response) { | ||||
|         let node = document.querySelector(`.status-indicator[data-node="${filename}"]`); | ||||
| @@ -78,7 +78,7 @@ const portSelect = document.querySelector('.nav-wrapper select'); | ||||
| let ports = []; | ||||
|  | ||||
| const fetchSerialPorts = (begin=false) => { | ||||
| fetch('/serial-ports', {credentials: "same-origin"}).then(res => res.json()) | ||||
|   fetch('/serial-ports', {credentials: "same-origin"}).then(res => res.json()) | ||||
|     .then(response => { | ||||
|       if (ports.length === response.length) { | ||||
|         let allEqual = true; | ||||
| @@ -118,13 +118,13 @@ fetch('/serial-ports', {credentials: "same-origin"}).then(res => res.json()) | ||||
| }; | ||||
|  | ||||
| const getUploadPort = () => { | ||||
| const inst = M.FormSelect.getInstance(portSelect); | ||||
| if (inst === undefined) { | ||||
|   const inst = M.FormSelect.getInstance(portSelect); | ||||
|   if (inst === undefined) { | ||||
|     return "OTA"; | ||||
| } | ||||
|   } | ||||
|  | ||||
| inst._setSelectedStates(); | ||||
| return inst.getSelectedValues()[0]; | ||||
|   inst._setSelectedStates(); | ||||
|   return inst.getSelectedValues()[0]; | ||||
| }; | ||||
| setInterval(fetchSerialPorts, 5000); | ||||
| fetchSerialPorts(true); | ||||
| @@ -132,7 +132,7 @@ fetchSerialPorts(true); | ||||
| const logsModalElem = document.getElementById("modal-logs"); | ||||
|  | ||||
| document.querySelectorAll(".action-show-logs").forEach((showLogs) => { | ||||
| showLogs.addEventListener('click', (e) => { | ||||
|   showLogs.addEventListener('click', (e) => { | ||||
|     configuration = e.target.getAttribute('data-node'); | ||||
|     const modalInstance = M.Modal.getInstance(logsModalElem); | ||||
|     const log = logsModalElem.querySelector(".log"); | ||||
| @@ -174,13 +174,13 @@ showLogs.addEventListener('click', (e) => { | ||||
|     modalInstance.options.onCloseStart = () => { | ||||
|       logSocket.close(); | ||||
|     }; | ||||
| }); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| const uploadModalElem = document.getElementById("modal-upload"); | ||||
|  | ||||
| document.querySelectorAll(".action-upload").forEach((upload) => { | ||||
| upload.addEventListener('click', (e) => { | ||||
|   upload.addEventListener('click', (e) => { | ||||
|     configuration = e.target.getAttribute('data-node'); | ||||
|     const modalInstance = M.Modal.getInstance(uploadModalElem); | ||||
|     const log = uploadModalElem.querySelector(".log"); | ||||
| @@ -222,13 +222,13 @@ upload.addEventListener('click', (e) => { | ||||
|     modalInstance.options.onCloseStart = () => { | ||||
|       logSocket.close(); | ||||
|     }; | ||||
| }); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| const validateModalElem = document.getElementById("modal-validate"); | ||||
|  | ||||
| document.querySelectorAll(".action-validate").forEach((upload) => { | ||||
| upload.addEventListener('click', (e) => { | ||||
|   upload.addEventListener('click', (e) => { | ||||
|     configuration = e.target.getAttribute('data-node'); | ||||
|     const modalInstance = M.Modal.getInstance(validateModalElem); | ||||
|     const log = validateModalElem.querySelector(".log"); | ||||
| @@ -276,14 +276,14 @@ upload.addEventListener('click', (e) => { | ||||
|     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) => { | ||||
|   upload.addEventListener('click', (e) => { | ||||
|     configuration = e.target.getAttribute('data-node'); | ||||
|     const modalInstance = M.Modal.getInstance(compileModalElem); | ||||
|     const log = compileModalElem.querySelector(".log"); | ||||
| @@ -328,19 +328,20 @@ upload.addEventListener('click', (e) => { | ||||
|     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 link = document.createElement("a"); | ||||
|   link.download = name; | ||||
|   link.href = '/download.bin?configuration=' + encodeURIComponent(configuration); | ||||
|   link.click(); | ||||
| }); | ||||
|  | ||||
| const cleanMqttModalElem = document.getElementById("modal-clean-mqtt"); | ||||
|  | ||||
| document.querySelectorAll(".action-clean-mqtt").forEach((btn) => { | ||||
| btn.addEventListener('click', (e) => { | ||||
|   btn.addEventListener('click', (e) => { | ||||
|     configuration = e.target.getAttribute('data-node'); | ||||
|     const modalInstance = M.Modal.getInstance(cleanMqttModalElem); | ||||
|     const log = cleanMqttModalElem.querySelector(".log"); | ||||
| @@ -376,13 +377,13 @@ btn.addEventListener('click', (e) => { | ||||
|     modalInstance.options.onCloseStart = () => { | ||||
|       logSocket.close(); | ||||
|     }; | ||||
| }); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| const cleanModalElem = document.getElementById("modal-clean"); | ||||
|  | ||||
| document.querySelectorAll(".action-clean").forEach((btn) => { | ||||
| btn.addEventListener('click', (e) => { | ||||
|   btn.addEventListener('click', (e) => { | ||||
|     configuration = e.target.getAttribute('data-node'); | ||||
|     const modalInstance = M.Modal.getInstance(cleanModalElem); | ||||
|     const log = cleanModalElem.querySelector(".log"); | ||||
| @@ -424,13 +425,13 @@ btn.addEventListener('click', (e) => { | ||||
|     modalInstance.options.onCloseStart = () => { | ||||
|       logSocket.close(); | ||||
|     }; | ||||
| }); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| const hassConfigModalElem = document.getElementById("modal-hass-config"); | ||||
|  | ||||
| document.querySelectorAll(".action-hass-config").forEach((btn) => { | ||||
| btn.addEventListener('click', (e) => { | ||||
|   btn.addEventListener('click', (e) => { | ||||
|     configuration = e.target.getAttribute('data-node'); | ||||
|     const modalInstance = M.Modal.getInstance(hassConfigModalElem); | ||||
|     const log = hassConfigModalElem.querySelector(".log"); | ||||
| @@ -472,7 +473,7 @@ btn.addEventListener('click', (e) => { | ||||
|     modalInstance.options.onCloseStart = () => { | ||||
|       logSocket.close(); | ||||
|     }; | ||||
| }); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| const editModalElem = document.getElementById("modal-editor"); | ||||
| @@ -486,7 +487,7 @@ editor.session.setOption('tabSize', 2); | ||||
|  | ||||
| const saveButton = editModalElem.querySelector(".save-button"); | ||||
| const saveEditor = () => { | ||||
| fetch(`/edit?configuration=${configuration}`, { | ||||
|   fetch(`/edit?configuration=${configuration}`, { | ||||
|       credentials: "same-origin", | ||||
|       method: "POST", | ||||
|       body: editor.getValue() | ||||
| @@ -498,16 +499,16 @@ fetch(`/edit?configuration=${configuration}`, { | ||||
| }; | ||||
|  | ||||
| editor.commands.addCommand({ | ||||
| name: 'saveCommand', | ||||
| bindKey: {win: 'Ctrl-S',  mac: 'Command-S'}, | ||||
| exec: saveEditor, | ||||
| readOnly: false | ||||
|   name: 'saveCommand', | ||||
|   bindKey: {win: 'Ctrl-S',  mac: 'Command-S'}, | ||||
|   exec: saveEditor, | ||||
|   readOnly: false | ||||
| }); | ||||
|  | ||||
| saveButton.addEventListener('click', saveEditor); | ||||
|  | ||||
| document.querySelectorAll(".action-edit").forEach((btn) => { | ||||
| btn.addEventListener('click', (e) => { | ||||
|   btn.addEventListener('click', (e) => { | ||||
|     configuration = e.target.getAttribute('data-node'); | ||||
|     const modalInstance = M.Modal.getInstance(editModalElem); | ||||
|     const filenameField = editModalElem.querySelector('.filename'); | ||||
| @@ -519,25 +520,26 @@ btn.addEventListener('click', (e) => { | ||||
|     }); | ||||
|  | ||||
|     modalInstance.open(); | ||||
| }); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| const modalSetupElem = document.getElementById("modal-wizard"); | ||||
| const setupWizardStart = document.getElementById('setup-wizard-start'); | ||||
| const startWizard = () => { | ||||
| const modalInstance = M.Modal.getInstance(modalSetupElem); | ||||
| modalInstance.open(); | ||||
|   const modalInstance = M.Modal.getInstance(modalSetupElem); | ||||
|   modalInstance.open(); | ||||
|  | ||||
| modalInstance.options.onCloseStart = () => { | ||||
|   modalInstance.options.onCloseStart = () => { | ||||
|  | ||||
| }; | ||||
|   }; | ||||
|  | ||||
| $('.stepper').activateStepper({ | ||||
|   $('.stepper').activateStepper({ | ||||
|     linearStepsNavigation: false, | ||||
|     autoFocusInput: true, | ||||
|     autoFormCreation: true, | ||||
|     showFeedbackLoader: true, | ||||
|     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) | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user