Bugfixes and GitHub API cache

This commit is contained in:
Djuri Baars 2024-06-09 23:06:53 +02:00
parent dfcf973176
commit 775ee5e904
6 changed files with 86 additions and 39 deletions

1
.gitignore vendored
View file

@ -267,3 +267,4 @@ $RECYCLE.BIN/
# End of https://www.toptal.com/developers/gitignore/api/python,visualstudiocode,macos,windows,linux
firmware/*.bin
firmware/*.json

View file

@ -1,5 +1,8 @@
# BTClock OTA Flasher interface
![Screenshot Windows](screenshot-win.webp)
![Screenshot Mac](screenshot-mac.webp)
## Instructions
- Make sure you have Python (tested with Python 3.12)
- Run `pip3 install -r requirements.txt`
@ -17,7 +20,7 @@ pyinstaller --hidden-import zeroconf._utils.ipaddress --hidden-import zeroconf._
### Windows
````
pyinstaller.exe --hidden-import zeroconf._utils.ipaddress --hidden-import zeroconf._handlers.answers --hidden-import pyserial -n BTClockOTA --windowed --onefile app.py
pyinstaller.exe BTClockOTA.spec
````
### Linux

View file

@ -17,6 +17,7 @@ from app.zeroconf_listener import ZeroconfListener
from app.espota import FLASH, SPIFFS
class SerialPortsComboBox(wx.ComboBox):
def __init__(self, parent, fw_update):
self.fw_update = fw_update
@ -95,12 +96,19 @@ class BTClockOTAUpdater(wx.Frame):
info.parsed_addresses()[0])
version = info.properties.get(b"rev").decode()
fsHash = "Too old"
hwRev = "REV_A_EPD_2_13"
if 'gitTag' in deviceSettings:
version = deviceSettings["gitTag"]
if 'fsRev' in deviceSettings:
fsHash = deviceSettings['fsRev'][:7]
if (info.properties.get(b"hw_rev") is not None):
hwRev = info.properties.get(b"hw_rev").decode()
fwHash = info.properties.get(b"rev").decode()[:7]
fsHash = deviceSettings['fsRev'][:7]
address = info.parsed_addresses()[0]
if index == wx.NOT_FOUND:
@ -109,23 +117,19 @@ class BTClockOTAUpdater(wx.Frame):
self.device_list.SetItem(index, 0, name)
self.device_list.SetItem(index, 1, version)
self.device_list.SetItem(index, 2, fwHash)
if (info.properties.get(b"hw_rev") is not None):
self.device_list.SetItem(
index, 3, info.properties.get(b"hw_rev").decode())
self.device_list.SetItem(index, 3, hwRev)
self.device_list.SetItem(index, 4, address)
else:
self.device_list.SetItem(index, 0, name)
self.device_list.SetItem(index, 1, version)
self.device_list.SetItem(index, 2, fwHash)
if (info.properties.get(b"hw_rev").decode()):
self.device_list.SetItem(
index, 3, info.properties.get(b"hw_rev").decode())
self.device_list.SetItem(index, 3, hwRev)
self.device_list.SetItem(index, 4, address)
self.device_list.SetItem(index, 5, fsHash)
self.device_list.SetItemData(index, index)
self.device_list.itemDataMap[index] = [
name, version, fwHash, info.properties.get(b"hw_rev").decode(), address, fsHash]
name, version, fwHash, hwRev, address, fsHash]
for col in range(0, len(self.device_list.column_headings)):
self.device_list.SetColumnWidth(
col, wx.LIST_AUTOSIZE_USEHEADER)

View file

@ -1,10 +1,15 @@
import json
import os
import requests
import wx
from typing import Callable
from datetime import datetime, timedelta
from app.utils import keep_latest_versions
CACHE_FILE = 'firmware/cache.json'
CACHE_DURATION = timedelta(minutes=30)
class ReleaseChecker:
'''Release Checker for firmware updates'''
@ -14,53 +19,87 @@ class ReleaseChecker:
def __init__(self):
self.progress_callback: Callable[[int], None] = None
def load_cache(self):
'''Load cached data from file'''
if os.path.exists(CACHE_FILE):
with open(CACHE_FILE, 'r') as f:
return json.load(f)
return {}
def save_cache(self, cache_data):
'''Save cache data to file'''
with open(CACHE_FILE, 'w') as f:
json.dump(cache_data, f)
def fetch_latest_release(self):
'''Fetch latest firmware release from GitHub'''
repo = "btclock/btclock_v3"
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']
else:
url = f"https://api.github.com/repos/{repo}/releases/latest"
try:
response = requests.get(url)
response.raise_for_status()
latest_release = response.json()
cache['latest_release'] = {
'data': latest_release,
'timestamp': now.isoformat()
}
self.save_cache(cache)
except requests.RequestException as e:
raise ReleaseCheckerException(
f"Error fetching release: {e}") from e
release_name = latest_release['tag_name']
self.release_name = release_name
filenames_to_download = ["lolin_s3_mini_213epd_firmware.bin",
"btclock_rev_b_213epd_firmware.bin", "littlefs.bin"]
url = f"https://api.github.com/repos/{repo}/releases/latest"
try:
response = requests.get(url)
response.raise_for_status()
latest_release = response.json()
release_name = latest_release['tag_name']
self.release_name = release_name
asset_url = None
asset_urls = []
for asset in latest_release['assets']:
if asset['name'] in filenames_to_download:
asset_urls.append(asset['browser_download_url'])
if asset_urls:
for asset_url in asset_urls:
self.download_file(asset_url, release_name)
ref_url = f"https://api.github.com/repos/{
repo}/git/ref/tags/{release_name}"
asset_urls = [asset['browser_download_url']
for asset in latest_release['assets'] if asset['name'] in filenames_to_download]
if asset_urls:
for asset_url in asset_urls:
self.download_file(asset_url, release_name)
ref_url = f"https://api.github.com/repos/{
repo}/git/ref/tags/{release_name}"
if ref_url in cache and (now - datetime.fromisoformat(cache[ref_url]['timestamp'])) < CACHE_DURATION:
commit_hash = cache[ref_url]['data']
else:
response = requests.get(ref_url)
response.raise_for_status()
ref_info = response.json()
if (ref_info["object"]["type"] == "commit"):
self.commit_hash = ref_info["object"]["sha"]
if ref_info["object"]["type"] == "commit":
commit_hash = ref_info["object"]["sha"]
else:
tag_url = f"https://api.github.com/repos/{
repo}/git/tags/{ref_info["object"]["sha"]}"
repo}/git/tags/{ref_info['object']['sha']}"
response = requests.get(tag_url)
response.raise_for_status()
tag_info = response.json()
self.commit_hash = tag_info["object"]["sha"]
commit_hash = tag_info["object"]["sha"]
cache[ref_url] = {
'data': commit_hash,
'timestamp': now.isoformat()
}
self.save_cache(cache)
return self.release_name
self.commit_hash = commit_hash
else:
raise ReleaseCheckerException(
f"File {filenames_to_download} not found in latest release")
except requests.RequestException as e:
return self.release_name
else:
raise ReleaseCheckerException(
f"Error fetching release: {e}") from e
f"File {filenames_to_download} not found in latest release")
def download_file(self, url, release_name):
'''Downloads Fimware Files'''

BIN
screenshot-mac.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
screenshot-win.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB