Show Logs button, color logs

This commit is contained in:
Otto Winter 2019-06-09 15:25:52 +02:00
parent b4321880a3
commit b834cf532a
No known key found for this signature in database
GPG Key ID: DB66C0BE6013F97E
4 changed files with 185 additions and 47 deletions

View File

@ -2,7 +2,6 @@ from __future__ import print_function
import argparse import argparse
from datetime import datetime from datetime import datetime
import re
import sys import sys
import esptool import esptool
@ -35,6 +34,7 @@ def parse_args(argv):
parser.add_argument('--no-erase', parser.add_argument('--no-erase',
help="Do not erase flash before flashing", help="Do not erase flash before flashing",
action='store_true') action='store_true')
parser.add_argument('--show-logs', help="Only show logs", action='store_true')
parser.add_argument('binary', help="The binary image to flash.") parser.add_argument('binary', help="The binary image to flash.")
return parser.parse_args(argv[1:]) return parser.parse_args(argv[1:])
@ -57,16 +57,38 @@ def select_port(args):
return ports[0][0] return ports[0][0]
ANSI_REGEX = re.compile(r"\033\[[0-9;]*m") def show_logs(serial_port):
print("Showing logs:")
with serial_port:
while True:
try:
raw = serial_port.readline()
except serial.SerialException:
print("Serial port closed!")
return
text = raw.decode(errors='ignore')
line = text.replace('\r', '').replace('\n', '')
time = datetime.now().time().strftime('[%H:%M:%S]')
message = time + line
try:
print(message)
except UnicodeEncodeError:
print(message.encode('ascii', 'backslashreplace'))
def run_esphomeflasher(argv): def run_esphomeflasher(argv):
args = parse_args(argv) args = parse_args(argv)
port = select_port(args)
if args.show_logs:
serial_port = serial.Serial(port, baudrate=115200)
show_logs(serial_port)
return
try: try:
firmware = open(args.binary, 'rb') firmware = open(args.binary, 'rb')
except IOError as err: except IOError as err:
raise EsphomeflasherError("Error opening binary: {}".format(err)) raise EsphomeflasherError("Error opening binary: {}".format(err))
port = select_port(args)
chip = detect_chip(port, args.esp8266, args.esp32) chip = detect_chip(port, args.esp8266, args.esp32)
info = read_chip_info(chip) info = read_chip_info(chip)
@ -120,23 +142,7 @@ def run_esphomeflasher(argv):
print("Done! Flashing is complete!") print("Done! Flashing is complete!")
print() print()
print("Showing logs:") show_logs(stub_chip._port)
with stub_chip._port as ser:
while True:
try:
raw = ser.readline()
except serial.SerialException:
print("Serial port closed!")
return
text = raw.decode(errors='ignore')
text = ANSI_REGEX.sub('', text)
line = text.replace('\r', '').replace('\n', '')
time = datetime.now().time().strftime('[%H:%M:%S]')
message = time + line
try:
print(message)
except UnicodeEncodeError:
print(message.encode('ascii', 'backslashreplace'))
def main(): def main():

View File

@ -21,6 +21,7 @@ class MockEsptoolArgs(object):
self.flash_freq = flash_freq self.flash_freq = flash_freq
self.no_stub = False self.no_stub = False
self.verify = False self.verify = False
self.erase_all = False
class ChipInfo(object): class ChipInfo(object):

View File

@ -1,5 +1,5 @@
# This GUI is a fork of the brilliant https://github.com/marcelstoer/nodemcu-pyflasher # This GUI is a fork of the brilliant https://github.com/marcelstoer/nodemcu-pyflasher
import re
import sys import sys
import threading import threading
@ -12,39 +12,154 @@ import wx.lib.mixins.inspection
from esphomeflasher.helpers import list_serial_ports from esphomeflasher.helpers import list_serial_ports
COLOR_RE = re.compile(r'(?:\033)(?:\[(.*?)[@-~]|\].*?(?:\007|\033\\))')
COLORS = {
'black': wx.BLACK,
'red': wx.RED,
'green': wx.GREEN,
'yellow': wx.YELLOW,
'blue': wx.BLUE,
'magenta': wx.Colour(255, 0, 255),
'cyan': wx.CYAN,
'white': wx.WHITE,
}
FORE_COLORS = {**COLORS, None: wx.WHITE}
BACK_COLORS = {**COLORS, None: wx.BLACK}
# See discussion at http://stackoverflow.com/q/41101897/131929 # See discussion at http://stackoverflow.com/q/41101897/131929
class RedirectText: class RedirectText:
def __init__(self, text_ctrl): def __init__(self, text_ctrl):
self._out = text_ctrl self._out = text_ctrl
self._i = 0
self._line = ''
self._bold = False
self._italic = False
self._underline = False
self._foreground = None
self._background = None
self._secret = False
def _add_content(self, value):
attr = wx.TextAttr()
if self._bold:
attr.SetFontWeight(wx.FONTWEIGHT_BOLD)
attr.SetTextColour(FORE_COLORS[self._foreground])
attr.SetBackgroundColour(BACK_COLORS[self._background])
wx.CallAfter(self._out.SetDefaultStyle, attr)
wx.CallAfter(self._out.AppendText, value)
def _write_line(self):
pos = 0
while True:
match = COLOR_RE.search(self._line, pos)
if match is None:
break
j = match.start()
self._add_content(self._line[pos:j])
pos = match.end()
for code in match.group(1).split(';'):
code = int(code)
if code == 0:
self._bold = False
self._italic = False
self._underline = False
self._foreground = None
self._background = None
self._secret = False
elif code == 1:
self._bold = True
elif code == 3:
self._italic = True
elif code == 4:
self._underline = True
elif code == 5:
self._secret = True
elif code == 6:
self._secret = False
elif code == 22:
self._bold = False
elif code == 23:
self._italic = False
elif code == 24:
self._underline = False
elif code == 30:
self._foreground = 'black'
elif code == 31:
self._foreground = 'red'
elif code == 32:
self._foreground = 'green'
elif code == 33:
self._foreground = 'yellow'
elif code == 34:
self._foreground = 'blue'
elif code == 35:
self._foreground = 'magenta'
elif code == 36:
self._foreground = 'cyan'
elif code == 37:
self._foreground = 'white'
elif code == 39:
self._foreground = None
elif code == 40:
self._background = 'black'
elif code == 41:
self._background = 'red'
elif code == 42:
self._background = 'green'
elif code == 43:
self._background = 'yellow'
elif code == 44:
self._background = 'blue'
elif code == 45:
self._background = 'magenta'
elif code == 46:
self._background = 'cyan'
elif code == 47:
self._background = 'white'
elif code == 49:
self._background = None
self._add_content(self._line[pos:])
def write(self, string): def write(self, string):
if string.startswith("\r"): for s in string:
# carriage return -> remove last line i.e. reset position to start of last line if s == '\r':
current_value = self._out.GetValue() current_value = self._out.GetValue()
last_newline = current_value.rfind("\n") last_newline = current_value.rfind("\n")
new_value = current_value[:last_newline + 1] # preserve \n wx.CallAfter(self._out.Remove, last_newline + 1, len(current_value))
new_value += string[1:] # chop off leading \r # self._line += '\n'
wx.CallAfter(self._out.SetValue, new_value) self._write_line()
else: self._line = ''
wx.CallAfter(self._out.AppendText, string) continue
self._line += s
if s == '\n':
self._write_line()
self._line = ''
continue
def flush(self): def flush(self):
pass pass
class FlashingThread(threading.Thread): class FlashingThread(threading.Thread):
def __init__(self, parent, firmware, port): def __init__(self, parent, firmware, port, show_logs=False):
threading.Thread.__init__(self) threading.Thread.__init__(self)
self.daemon = True self.daemon = True
self._parent = parent self._parent = parent
self._firmware = firmware self._firmware = firmware
self._port = port self._port = port
self._show_logs = show_logs
def run(self): def run(self):
try: try:
from esphomeflasher.__main__ import run_esphomeflasher from esphomeflasher.__main__ import run_esphomeflasher
argv = ['esphomeflasher', '--port', self._port, self._firmware] argv = ['esphomeflasher', '--port', self._port, self._firmware]
if self._show_logs:
argv.append('--show-logs')
run_esphomeflasher(argv) run_esphomeflasher(argv)
except Exception as e: except Exception as e:
print("Unexpected error: {}".format(e)) print("Unexpected error: {}".format(e))
@ -53,7 +168,7 @@ class FlashingThread(threading.Thread):
class MainFrame(wx.Frame): class MainFrame(wx.Frame):
def __init__(self, parent, title): def __init__(self, parent, title):
wx.Frame.__init__(self, parent, -1, title, size=(700, 650), wx.Frame.__init__(self, parent, -1, title, size=(725, 650),
style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE) style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
self._firmware = None self._firmware = None
@ -76,6 +191,11 @@ class MainFrame(wx.Frame):
worker = FlashingThread(self, self._firmware, self._port) worker = FlashingThread(self, self._firmware, self._port)
worker.start() worker.start()
def on_logs_clicked(event):
self.console_ctrl.SetValue("")
worker = FlashingThread(self, 'dummy', self._port, show_logs=True)
worker.start()
def on_select_port(event): def on_select_port(event):
choice = event.GetEventObject() choice = event.GetEventObject()
self._port = choice.GetString(choice.GetSelection()) self._port = choice.GetString(choice.GetSelection())
@ -108,12 +228,15 @@ class MainFrame(wx.Frame):
button = wx.Button(panel, -1, "Flash ESP") button = wx.Button(panel, -1, "Flash ESP")
button.Bind(wx.EVT_BUTTON, on_clicked) button.Bind(wx.EVT_BUTTON, on_clicked)
logs_button = wx.Button(panel, -1, "View Logs")
logs_button.Bind(wx.EVT_BUTTON, on_logs_clicked)
self.console_ctrl = wx.TextCtrl(panel, style=wx.TE_MULTILINE | wx.TE_READONLY | wx.HSCROLL) self.console_ctrl = wx.TextCtrl(panel, style=wx.TE_MULTILINE | wx.TE_READONLY | wx.HSCROLL)
self.console_ctrl.SetFont( self.console_ctrl.SetFont(wx.Font((0, 13), wx.FONTFAMILY_TELETYPE, wx.FONTSTYLE_NORMAL,
wx.Font(13, wx.FONTFAMILY_TELETYPE, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)) wx.FONTWEIGHT_NORMAL))
self.console_ctrl.SetBackgroundColour(wx.BLACK) self.console_ctrl.SetBackgroundColour(wx.BLACK)
self.console_ctrl.SetForegroundColour(wx.RED) self.console_ctrl.SetForegroundColour(wx.WHITE)
self.console_ctrl.SetDefaultStyle(wx.TextAttr(wx.RED)) self.console_ctrl.SetDefaultStyle(wx.TextAttr(wx.WHITE))
port_label = wx.StaticText(panel, label="Serial port") port_label = wx.StaticText(panel, label="Serial port")
file_label = wx.StaticText(panel, label="Firmware") file_label = wx.StaticText(panel, label="Firmware")
@ -121,11 +244,18 @@ class MainFrame(wx.Frame):
console_label = wx.StaticText(panel, label="Console") console_label = wx.StaticText(panel, label="Console")
fgs.AddMany([ fgs.AddMany([
# Port selection row
port_label, (serial_boxsizer, 1, wx.EXPAND), port_label, (serial_boxsizer, 1, wx.EXPAND),
# Firmware selection row (growable)
file_label, (file_picker, 1, wx.EXPAND), file_label, (file_picker, 1, wx.EXPAND),
# Flash ESP button
(wx.StaticText(panel, label="")), (button, 1, wx.EXPAND), (wx.StaticText(panel, label="")), (button, 1, wx.EXPAND),
(console_label, 1, wx.EXPAND), (self.console_ctrl, 1, wx.EXPAND)]) # View Logs button
fgs.AddGrowableRow(3, 1) (wx.StaticText(panel, label="")), (logs_button, 1, wx.EXPAND),
# Console View (growable)
(console_label, 1, wx.EXPAND), (self.console_ctrl, 1, wx.EXPAND),
])
fgs.AddGrowableRow(4, 1)
fgs.AddGrowableCol(1, 1) fgs.AddGrowableCol(1, 1)
hbox.Add(fgs, proportion=2, flag=wx.ALL | wx.EXPAND, border=15) hbox.Add(fgs, proportion=2, flag=wx.ALL | wx.EXPAND, border=15)
panel.SetSizer(hbox) panel.SetSizer(hbox)
@ -151,9 +281,9 @@ class MainFrame(wx.Frame):
class App(wx.App, wx.lib.mixins.inspection.InspectionMixin): class App(wx.App, wx.lib.mixins.inspection.InspectionMixin):
def OnInit(self): def OnInit(self):
wx.SystemOptions.SetOption("mac.window-plain-transition", 1) wx.SystemOptions.SetOption("mac.window-plain-transition", 1)
self.SetAppName("esphomeflasher") self.SetAppName("esphome-flasher (Based on NodeMCU PyFlasher)")
frame = MainFrame(None, "esphomeflasher") frame = MainFrame(None, "esphome-flasher (Based on NodeMCU PyFlasher)")
frame.Show() frame.Show()
return True return True

View File

@ -7,12 +7,12 @@ from esphomeflasher import const
PROJECT_NAME = 'esphomeflasher' PROJECT_NAME = 'esphomeflasher'
PROJECT_PACKAGE_NAME = 'esphomeflasher' PROJECT_PACKAGE_NAME = 'esphomeflasher'
PROJECT_LICENSE = 'MIT' PROJECT_LICENSE = 'MIT'
PROJECT_AUTHOR = 'Otto Winter' PROJECT_AUTHOR = 'ESPHome'
PROJECT_COPYRIGHT = '2018, Otto Winter' PROJECT_COPYRIGHT = '2019, ESPHome'
PROJECT_URL = 'https://esphomelib.com/esphomeyaml/guides/esphomeflasher.html' PROJECT_URL = 'https://esphome.io/guides/faq.html'
PROJECT_EMAIL = 'contact@otto-winter.com' PROJECT_EMAIL = 'contact@esphome.io'
PROJECT_GITHUB_USERNAME = 'OttoWinter' PROJECT_GITHUB_USERNAME = 'esphome'
PROJECT_GITHUB_REPOSITORY = 'esphomeflasher' PROJECT_GITHUB_REPOSITORY = 'esphomeflasher'
PYPI_URL = 'https://pypi.python.org/pypi/{}'.format(PROJECT_PACKAGE_NAME) PYPI_URL = 'https://pypi.python.org/pypi/{}'.format(PROJECT_PACKAGE_NAME)
@ -22,8 +22,9 @@ GITHUB_URL = 'https://github.com/{}'.format(GITHUB_PATH)
DOWNLOAD_URL = '{}/archive/{}.zip'.format(GITHUB_URL, const.__version__) DOWNLOAD_URL = '{}/archive/{}.zip'.format(GITHUB_URL, const.__version__)
REQUIRES = [ REQUIRES = [
'esptool==2.5.1', 'wxpython>=4.0,<5.0',
'requests', 'esptool==2.6',
'requests>=2.0,<3',
] ]
setup( setup(