Bugfixes and GitHub API cache
This commit is contained in:
parent
dfcf973176
commit
775ee5e904
6 changed files with 86 additions and 39 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -267,3 +267,4 @@ $RECYCLE.BIN/
|
|||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/python,visualstudiocode,macos,windows,linux
|
||||
firmware/*.bin
|
||||
firmware/*.json
|
|
@ -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
|
||||
|
|
20
app/main.py
20
app/main.py
|
@ -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)
|
||||
|
|
|
@ -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
BIN
screenshot-mac.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
BIN
screenshot-win.webp
Normal file
BIN
screenshot-win.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
Loading…
Reference in a new issue