Show Logs button, color logs
This commit is contained in:
parent
b4321880a3
commit
b834cf532a
|
@ -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():
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
15
setup.py
15
setup.py
|
@ -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(
|
||||||
|
|
Loading…
Reference in New Issue