mirror of
https://github.com/esphome/esphome-flasher.git
synced 2024-11-25 12:25:47 +01:00
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
|
||||
from datetime import datetime
|
||||
import re
|
||||
import sys
|
||||
|
||||
import esptool
|
||||
@ -35,6 +34,7 @@ def parse_args(argv):
|
||||
parser.add_argument('--no-erase',
|
||||
help="Do not erase flash before flashing",
|
||||
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.")
|
||||
|
||||
return parser.parse_args(argv[1:])
|
||||
@ -57,16 +57,38 @@ def select_port(args):
|
||||
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):
|
||||
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:
|
||||
firmware = open(args.binary, 'rb')
|
||||
except IOError as err:
|
||||
raise EsphomeflasherError("Error opening binary: {}".format(err))
|
||||
port = select_port(args)
|
||||
chip = detect_chip(port, args.esp8266, args.esp32)
|
||||
info = read_chip_info(chip)
|
||||
|
||||
@ -120,23 +142,7 @@ def run_esphomeflasher(argv):
|
||||
print("Done! Flashing is complete!")
|
||||
print()
|
||||
|
||||
print("Showing logs:")
|
||||
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'))
|
||||
show_logs(stub_chip._port)
|
||||
|
||||
|
||||
def main():
|
||||
|
@ -21,6 +21,7 @@ class MockEsptoolArgs(object):
|
||||
self.flash_freq = flash_freq
|
||||
self.no_stub = False
|
||||
self.verify = False
|
||||
self.erase_all = False
|
||||
|
||||
|
||||
class ChipInfo(object):
|
||||
|
@ -1,5 +1,5 @@
|
||||
# This GUI is a fork of the brilliant https://github.com/marcelstoer/nodemcu-pyflasher
|
||||
|
||||
import re
|
||||
import sys
|
||||
import threading
|
||||
|
||||
@ -12,39 +12,154 @@ import wx.lib.mixins.inspection
|
||||
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
|
||||
class RedirectText:
|
||||
def __init__(self, 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):
|
||||
if string.startswith("\r"):
|
||||
# carriage return -> remove last line i.e. reset position to start of last line
|
||||
current_value = self._out.GetValue()
|
||||
last_newline = current_value.rfind("\n")
|
||||
new_value = current_value[:last_newline + 1] # preserve \n
|
||||
new_value += string[1:] # chop off leading \r
|
||||
wx.CallAfter(self._out.SetValue, new_value)
|
||||
else:
|
||||
wx.CallAfter(self._out.AppendText, string)
|
||||
for s in string:
|
||||
if s == '\r':
|
||||
current_value = self._out.GetValue()
|
||||
last_newline = current_value.rfind("\n")
|
||||
wx.CallAfter(self._out.Remove, last_newline + 1, len(current_value))
|
||||
# self._line += '\n'
|
||||
self._write_line()
|
||||
self._line = ''
|
||||
continue
|
||||
self._line += s
|
||||
if s == '\n':
|
||||
self._write_line()
|
||||
self._line = ''
|
||||
continue
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
|
||||
class FlashingThread(threading.Thread):
|
||||
def __init__(self, parent, firmware, port):
|
||||
def __init__(self, parent, firmware, port, show_logs=False):
|
||||
threading.Thread.__init__(self)
|
||||
self.daemon = True
|
||||
self._parent = parent
|
||||
self._firmware = firmware
|
||||
self._port = port
|
||||
self._show_logs = show_logs
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
from esphomeflasher.__main__ import run_esphomeflasher
|
||||
|
||||
argv = ['esphomeflasher', '--port', self._port, self._firmware]
|
||||
if self._show_logs:
|
||||
argv.append('--show-logs')
|
||||
run_esphomeflasher(argv)
|
||||
except Exception as e:
|
||||
print("Unexpected error: {}".format(e))
|
||||
@ -53,7 +168,7 @@ class FlashingThread(threading.Thread):
|
||||
|
||||
class MainFrame(wx.Frame):
|
||||
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)
|
||||
|
||||
self._firmware = None
|
||||
@ -76,6 +191,11 @@ class MainFrame(wx.Frame):
|
||||
worker = FlashingThread(self, self._firmware, self._port)
|
||||
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):
|
||||
choice = event.GetEventObject()
|
||||
self._port = choice.GetString(choice.GetSelection())
|
||||
@ -108,12 +228,15 @@ class MainFrame(wx.Frame):
|
||||
button = wx.Button(panel, -1, "Flash ESP")
|
||||
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.SetFont(
|
||||
wx.Font(13, wx.FONTFAMILY_TELETYPE, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL))
|
||||
self.console_ctrl.SetFont(wx.Font((0, 13), wx.FONTFAMILY_TELETYPE, wx.FONTSTYLE_NORMAL,
|
||||
wx.FONTWEIGHT_NORMAL))
|
||||
self.console_ctrl.SetBackgroundColour(wx.BLACK)
|
||||
self.console_ctrl.SetForegroundColour(wx.RED)
|
||||
self.console_ctrl.SetDefaultStyle(wx.TextAttr(wx.RED))
|
||||
self.console_ctrl.SetForegroundColour(wx.WHITE)
|
||||
self.console_ctrl.SetDefaultStyle(wx.TextAttr(wx.WHITE))
|
||||
|
||||
port_label = wx.StaticText(panel, label="Serial port")
|
||||
file_label = wx.StaticText(panel, label="Firmware")
|
||||
@ -121,11 +244,18 @@ class MainFrame(wx.Frame):
|
||||
console_label = wx.StaticText(panel, label="Console")
|
||||
|
||||
fgs.AddMany([
|
||||
# Port selection row
|
||||
port_label, (serial_boxsizer, 1, wx.EXPAND),
|
||||
# Firmware selection row (growable)
|
||||
file_label, (file_picker, 1, wx.EXPAND),
|
||||
# Flash ESP button
|
||||
(wx.StaticText(panel, label="")), (button, 1, wx.EXPAND),
|
||||
(console_label, 1, wx.EXPAND), (self.console_ctrl, 1, wx.EXPAND)])
|
||||
fgs.AddGrowableRow(3, 1)
|
||||
# View Logs button
|
||||
(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)
|
||||
hbox.Add(fgs, proportion=2, flag=wx.ALL | wx.EXPAND, border=15)
|
||||
panel.SetSizer(hbox)
|
||||
@ -151,9 +281,9 @@ class MainFrame(wx.Frame):
|
||||
class App(wx.App, wx.lib.mixins.inspection.InspectionMixin):
|
||||
def OnInit(self):
|
||||
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()
|
||||
|
||||
return True
|
||||
|
15
setup.py
15
setup.py
@ -7,12 +7,12 @@ from esphomeflasher import const
|
||||
PROJECT_NAME = 'esphomeflasher'
|
||||
PROJECT_PACKAGE_NAME = 'esphomeflasher'
|
||||
PROJECT_LICENSE = 'MIT'
|
||||
PROJECT_AUTHOR = 'Otto Winter'
|
||||
PROJECT_COPYRIGHT = '2018, Otto Winter'
|
||||
PROJECT_URL = 'https://esphomelib.com/esphomeyaml/guides/esphomeflasher.html'
|
||||
PROJECT_EMAIL = 'contact@otto-winter.com'
|
||||
PROJECT_AUTHOR = 'ESPHome'
|
||||
PROJECT_COPYRIGHT = '2019, ESPHome'
|
||||
PROJECT_URL = 'https://esphome.io/guides/faq.html'
|
||||
PROJECT_EMAIL = 'contact@esphome.io'
|
||||
|
||||
PROJECT_GITHUB_USERNAME = 'OttoWinter'
|
||||
PROJECT_GITHUB_USERNAME = 'esphome'
|
||||
PROJECT_GITHUB_REPOSITORY = 'esphomeflasher'
|
||||
|
||||
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__)
|
||||
|
||||
REQUIRES = [
|
||||
'esptool==2.5.1',
|
||||
'requests',
|
||||
'wxpython>=4.0,<5.0',
|
||||
'esptool==2.6',
|
||||
'requests>=2.0,<3',
|
||||
]
|
||||
|
||||
setup(
|
||||
|
Loading…
Reference in New Issue
Block a user