diff --git a/app.py b/app.py index 7e92065..e82a0c9 100644 --- a/app.py +++ b/app.py @@ -1,7 +1,8 @@ +from app.main import BTClockOTAUpdater import wx -from app.main import BTClockOTAUpdater - -app = wx.App(False) -frame = BTClockOTAUpdater(None, 'BTClock OTA updater') -app.MainLoop() \ No newline at end of file +if __name__ == "__main__": + app = wx.App(False) + frame = BTClockOTAUpdater(None, 'BTClock OTA updater') + + app.MainLoop() diff --git a/app/fw_updater.py b/app/fw_updater.py index ec19d88..e4e5d8e 100644 --- a/app/fw_updater.py +++ b/app/fw_updater.py @@ -7,6 +7,8 @@ import esptool import serial import wx +from app.utils import get_app_data_folder + class FwUpdater: update_progress = None @@ -72,7 +74,7 @@ class FwUpdater: if (hw_rev == "REV_B_EPD_2_13"): model_name = "btclock_rev_b_213epd" - local_filename = f"firmware/{ + local_filename = f"{get_app_data_folder()}/{ release_name}_{model_name}_firmware.bin" self.updatingName = address @@ -88,7 +90,7 @@ class FwUpdater: def start_fs_update(self, release_name, address): # Path to the firmware file - local_filename = f"firmware/{release_name}_littlefs.bin" + local_filename = f"{get_app_data_folder()}/{release_name}_littlefs.bin" self.updatingName = address self.currentlyUpdating = True diff --git a/app/gui/action_button_panel.py b/app/gui/action_button_panel.py index 5c28669..bfbf3a2 100644 --- a/app/gui/action_button_panel.py +++ b/app/gui/action_button_panel.py @@ -27,7 +27,7 @@ class ActionButtonPanel(wx.Panel): self.update_button = wx.Button(self, label="Update Firmware") self.update_button.Bind(wx.EVT_BUTTON, self.on_click_update_firmware) - self.update_fs_button = wx.Button(self, label="Update Filesystem") + self.update_fs_button = wx.Button(self, label="Update WebUI") self.update_fs_button.Bind(wx.EVT_BUTTON, self.on_click_update_fs) self.identify_button = wx.Button(self, label="Identify") diff --git a/app/main.py b/app/main.py index 2b79b05..719dcde 100644 --- a/app/main.py +++ b/app/main.py @@ -1,5 +1,6 @@ import concurrent.futures import logging +import traceback import serial from app.gui.action_button_panel import ActionButtonPanel @@ -15,11 +16,14 @@ from app import espota from app.api import ApiHandler from app.fw_updater import FwUpdater from app.gui.devices_panel import DevicesPanel +from app.utils import get_app_data_folder from app.zeroconf_listener import ZeroconfListener from app.espota import FLASH, SPIFFS - +class BTClockOTAApp(wx.App): + def OnInit(self): + return True class RichTextCtrlHandler(logging.Handler): def __init__(self, ctrl): super().__init__() @@ -27,7 +31,7 @@ class RichTextCtrlHandler(logging.Handler): def emit(self, record): msg = self.format(record) - wx.CallAfter(self.append_text, msg + '\n') + wx.CallAfter(self.append_text, "\n" + msg) def append_text(self, text): self.ctrl.AppendText(text) @@ -46,6 +50,7 @@ class BTClockOTAUpdater(wx.Frame): def __init__(self, parent, title): wx.Frame.__init__(self, parent, title=title, size=(800, 500)) + self.SetMinSize((800, 500)) self.releaseChecker = ReleaseChecker() self.zeroconf = Zeroconf() @@ -62,7 +67,7 @@ class BTClockOTAUpdater(wx.Frame): self.log_ctrl.SetFont(monospace_font) handler = RichTextCtrlHandler(self.log_ctrl) - handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')) + handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s', '%H:%M:%S')) logging.getLogger().addHandler(handler) logging.getLogger().setLevel(logging.DEBUG) @@ -93,7 +98,7 @@ class BTClockOTAUpdater(wx.Frame): self.setup_ui() wx.CallAfter(self.fetch_latest_release_async) - + wx.YieldIfNeeded() def setup_ui(self): self.setup_menubar() self.status_bar = self.CreateStatusBar(2) @@ -102,6 +107,8 @@ class BTClockOTAUpdater(wx.Frame): def setup_menubar(self): filemenu = wx.Menu() + menuOpenDownloadDir = filemenu.Append( + wx.ID_OPEN, "&Open Download Dir", " Open the directory with firmware files and cache") menuAbout = filemenu.Append( wx.ID_ABOUT, "&About", " Information about this program") menuExit = filemenu.Append( @@ -109,8 +116,9 @@ class BTClockOTAUpdater(wx.Frame): menuBar = wx.MenuBar() menuBar.Append(filemenu, "&File") - self.SetMenuBar(menuBar) + self.SetMenuBar(menuBar) + self.Bind(wx.EVT_MENU, self.OnOpenDownloadFolder, menuOpenDownloadDir) self.Bind(wx.EVT_MENU, self.OnAbout, menuAbout) self.Bind(wx.EVT_MENU, self.OnExit, menuExit) @@ -175,6 +183,9 @@ class BTClockOTAUpdater(wx.Frame): def fetch_latest_release_async(self): # Start a new thread to execute fetch_latest_release + app_folder = get_app_data_folder() + if not os.path.exists(app_folder): + os.makedirs(app_folder) executor = concurrent.futures.ThreadPoolExecutor() future = executor.submit(self.releaseChecker.fetch_latest_release) future.add_done_callback(self.handle_latest_release) @@ -186,7 +197,11 @@ class BTClockOTAUpdater(wx.Frame): latest_release}\nCommit: {self.releaseChecker.commit_hash}") except Exception as e: self.fw_label.SetLabel(f"Error occurred: {str(e)}") - + traceback.print_tb(e.__traceback__) + + def OnOpenDownloadFolder(self, e): + wx.LaunchDefaultBrowser(get_app_data_folder()) + def OnAbout(self, e): dlg = wx.MessageDialog( self, "An updater for BTClocks", "About BTClock OTA Updater", wx.OK) diff --git a/app/release_checker.py b/app/release_checker.py index c10e04b..15272dc 100644 --- a/app/release_checker.py +++ b/app/release_checker.py @@ -6,9 +6,9 @@ import wx from typing import Callable from datetime import datetime, timedelta -from app.utils import keep_latest_versions +from app.utils import get_app_data_folder, keep_latest_versions -CACHE_FILE = 'firmware/cache.json' +CACHE_FILE = get_app_data_folder() + '/cache.json' CACHE_DURATION = timedelta(minutes=30) @@ -38,8 +38,6 @@ class ReleaseChecker: cache = self.load_cache() now = datetime.now() - if not os.path.exists("firmware"): - os.makedirs("firmware") if 'latest_release' in cache and (now - datetime.fromisoformat(cache['latest_release']['timestamp'])) < CACHE_DURATION: latest_release = cache['latest_release']['data'] @@ -106,14 +104,12 @@ class ReleaseChecker: '''Downloads Fimware Files''' local_filename = f"{release_name}_{url.split('/')[-1]}" - if not os.path.exists("firmware"): - os.makedirs("firmware") - if os.path.exists(f"firmware/{local_filename}"): + if os.path.exists(f"{get_app_data_folder()}/{local_filename}"): return response = requests.get(url, stream=True) total_length = response.headers.get('content-length') - keep_latest_versions('firmware', 2) + keep_latest_versions(get_app_data_folder(), 2) if total_length is None: raise ReleaseCheckerException("No content length header") @@ -121,7 +117,7 @@ class ReleaseChecker: total_length = int(total_length) chunk_size = 1024 num_chunks = total_length // chunk_size - with open(f"firmware/{local_filename}", 'wb') as f: + with open(f"{get_app_data_folder()}/{local_filename}", 'wb') as f: for i, chunk in enumerate(response.iter_content(chunk_size=chunk_size)): if chunk: f.write(chunk) diff --git a/app/utils.py b/app/utils.py index 8b79405..82559c7 100644 --- a/app/utils.py +++ b/app/utils.py @@ -1,6 +1,6 @@ import os import re -import shutil +import wx def count_versions(folder_path): @@ -30,3 +30,16 @@ def keep_latest_versions(folder_path, num_versions_to_keep=2): for version in versions_to_remove: for file_name in version_files[version]: os.remove(os.path.join(folder_path, file_name)) + +def get_app_data_folder(): + app = wx.GetApp() + if app is None: + app = wx.App(False) + standard_paths = wx.StandardPaths.Get() + app_data_dir = standard_paths.GetAppDocumentsDir() + "/BTClockOTA" + app.Destroy() + return app_data_dir + else: + standard_paths = wx.StandardPaths.Get() + app_data_dir = standard_paths.GetAppDocumentsDir() + "/BTClockOTA" + return app_data_dir \ No newline at end of file