From 0eabbb3fd24af2b13925b6e58d6f1652a8852e29 Mon Sep 17 00:00:00 2001 From: SavageCore Date: Fri, 16 Jun 2023 15:07:24 +0100 Subject: [PATCH] Offline support --- .gitignore | 1 + README.md | 7 +++++++ github.py | 15 ++++++++------- main.py | 40 +++++++++++++++++++++++++++++++++++----- 4 files changed, 51 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 8805282..3a9be4c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ state.json __pycache__/ +firmware/ diff --git a/README.md b/README.md index d84a0b7..7530fc8 100644 --- a/README.md +++ b/README.md @@ -30,3 +30,10 @@ This guide assumes you're running a Raspberry Pi (< 4) with PiTFT and have follo Also included is a script to create a systemd service to start the flasher on boot. To use it, run `./create-systemd-service.sh` and it will start on next boot. To stop it from starting on boot, run `sudo systemctl disable gp2040-flasher`. If you want to start it manually, run `sudo systemctl start gp2040-flasher`. You can double press the bottom button to exit the program. Note if you installed the systemd service, it will restart the program. + +## Offline Usage + +1. Download the latest releases from the [releases page](https://github.com/OpenStickCommunity/GP2040-CE/releases) and place them in the `firmware` directory. +2. Start the app with `sudo python3 main.py --offline`. + +The app will also fail gracefully if you're API rate limited or any other connection issue to GitHub and fallback to offline mode. diff --git a/github.py b/github.py index e76abba..5eabda5 100644 --- a/github.py +++ b/github.py @@ -1,6 +1,5 @@ import requests import urllib.request -import tempfile import os @@ -12,8 +11,11 @@ def __init__(self): def get_latest_release_info(self): url = f"https://api.github.com/repos/{self.owner}/{self.repo}/releases" - response = requests.get(url) - if response.status_code == 200: + + try: + response = requests.get(url) + response.raise_for_status() # Raise exception for non-2xx status codes + releases = response.json() for release in releases: if not release.get("prerelease"): @@ -25,11 +27,10 @@ def get_latest_release_info(self): asset for asset in assets if asset["name"] != "flash_nuke.uf2"] return version, release_date, assets - else: - # Handle API request error - print("Error:", response.status_code) + except requests.exceptions.RequestException as e: + print("Failed to fetch release info:", e) - return None, None, None + return None, None, None def download_file(self, url): # Extract the filename from the URL diff --git a/main.py b/main.py index 6092a46..a26a433 100644 --- a/main.py +++ b/main.py @@ -1,12 +1,14 @@ import pygame import pyudev import os +import sys from splash_screen import SplashScreen from button import Button from picotool import Picotool from github import Github from state_manager import StateManager +offline_mode = False # Hide pygame message os.putenv('PYGAME_HIDE_SUPPORT_PROMPT', '1') # Output to PiTFT screen @@ -16,8 +18,16 @@ state_manager = StateManager() selected_firmware = state_manager.get_value("selected_firmware") or 0 -github = Github() -version, release_date, firmware_files = github.get_latest_release_info() +# If argument --offline is passed, force offline mode +if len(sys.argv) > 1 and sys.argv[1] == "--offline": + offline_mode = True + print("Forcing offline mode") + +if offline_mode: + firmware_files = None +else: + github = Github() + version, release_date, firmware_files = github.get_latest_release_info() # Initialise the pygame library pygame.init() @@ -29,7 +39,23 @@ # If no firmware files are found, exit if firmware_files is None: # None is returned if the API request fails print("Error looking up GitHub releases") - exit() + print("Assuming no internet connection") + offline_mode = True + # We're offline, recreate the firmware_files dictionary from files in the firmware directory, skipping flash_nuke.uf2 + + selected_firmware = 0 + firmware_files = {} + for file in os.listdir("firmware"): + if file.endswith(".uf2") and not file == "flash_nuke.uf2": + firmware_files[len(firmware_files)] = { + "name": os.path.join('firmware', file) + } + + # If no firmware files are found, exit + if len(firmware_files) == 0: + print("No firmware files found") + exit() + def clear_screen(screen): @@ -62,14 +88,18 @@ def render_text(screen, text, font, color, padding=5): def flash_drive_handler(action, device): global selected_firmware global splash + global offline_mode device_name = device.sys_name.split('/')[-1] if action == 'add' and device_name == "sda" and device.get('ID_VENDOR') == 'RPI' and device.get('ID_MODEL') == 'RP2': picotool = Picotool() splash.set_text("Pico detected") - download_url = firmware_files[selected_firmware]["browser_download_url"] - firmware_file = github.download_file(download_url) + if offline_mode: + firmware_file = firmware_files[selected_firmware]["name"] + else: + download_url = firmware_files[selected_firmware]["browser_download_url"] + firmware_file = github.download_file(download_url) # If the firmware file is downloaded successfully, flash it to the Pico if firmware_file is not None: