Add a "Check now" button to the Firmware update card
Build and push server image / build-and-push (push) Successful in 36s
Build and push server image / build-and-push (push) Successful in 36s
GET /api/firmware/check's 15-minute throttle meant a genuinely new Gitea release could sit invisible in the UI for up to that long even though POST /api/firmware/apply-latest (unthrottled) would've picked it up immediately. New ?force=true bypasses the throttle for an explicit check; the button wires it up and surfaces errors instead of failing silently like the passive poll.
This commit is contained in:
+2
-1
@@ -178,7 +178,8 @@ algorithm itself -- it just streams the response straight to the panel.
|
||||
- `GET /api/firmware/check` -- throttled (`gitea_releases.UPDATE_CHECK_INTERVAL_S`,
|
||||
15 min) check of the configured Gitea repo's latest release for the
|
||||
frame's board variant (learned from the device, see `device_board_variant`
|
||||
below -- not user-configured). `{"enabled": false}` if no repo URL is
|
||||
below -- not user-configured). `?force=true` (the "Check now" button)
|
||||
bypasses the throttle. `{"enabled": false}` if no repo URL is
|
||||
configured; otherwise `{"enabled": true, "board": "xiao" | null,
|
||||
"latest_version": "1.2.3" | null, "staged_version": "1.2.2" | null,
|
||||
"update_available": bool}`. `update_available` stays false until the
|
||||
|
||||
+8
-3
@@ -529,7 +529,7 @@ def _apply_gitea_update(cfg: config.FrameConfig) -> str:
|
||||
|
||||
|
||||
@app.get("/api/firmware/check", dependencies=[Depends(require_access_token)])
|
||||
def api_firmware_check():
|
||||
def api_firmware_check(force: bool = False):
|
||||
"""Throttled check of the configured Gitea repo's latest release
|
||||
(gitea_releases.UPDATE_CHECK_INTERVAL_S) -- cheap, since it only reads
|
||||
the release's tag name, not its binaries. If firmware_auto_update is
|
||||
@@ -538,13 +538,18 @@ def api_firmware_check():
|
||||
Applying (auto or manual) needs to know the frame's board, which is
|
||||
learned from the device's own X-Frame-Board header rather than
|
||||
picked by the user -- update_available stays false until a device
|
||||
has checked in at least once, regardless of what Gitea has."""
|
||||
has checked in at least once, regardless of what Gitea has.
|
||||
|
||||
force=true (the "Check now" button) bypasses the throttle and always
|
||||
hits Gitea -- otherwise a genuinely new release can sit invisible in
|
||||
the UI for up to the full throttle interval even though it's already
|
||||
live, since the passive poll won't look again until then."""
|
||||
cfg = config.load()
|
||||
if not cfg.firmware_update_repo_url:
|
||||
return {"enabled": False}
|
||||
|
||||
now = time.time()
|
||||
if now - cfg.firmware_update_checked_at >= gitea_releases.UPDATE_CHECK_INTERVAL_S:
|
||||
if force or now - cfg.firmware_update_checked_at >= gitea_releases.UPDATE_CHECK_INTERVAL_S:
|
||||
# Deliberately not updated on failure (see below) -- checked_at only
|
||||
# advances on a successful reach, so a Gitea outage gets retried
|
||||
# every poll instead of waiting out the full throttle interval.
|
||||
|
||||
@@ -131,6 +131,7 @@
|
||||
</div>
|
||||
<button type="button" class="secondary" id="firmware-settings-save">Save</button>
|
||||
<p class="sub" id="firmware-gitea-status" style="display: none; margin-top: 10px;"></p>
|
||||
<button type="button" class="secondary" id="firmware-check-now">Check now</button>
|
||||
<button type="button" id="firmware-update-btn" style="display: none;">Update frame</button>
|
||||
</section>
|
||||
</div>
|
||||
@@ -541,13 +542,16 @@
|
||||
}
|
||||
});
|
||||
|
||||
async function loadFirmwareCheck() {
|
||||
async function loadFirmwareCheck(force) {
|
||||
const statusEl = document.getElementById('firmware-gitea-status');
|
||||
const btn = document.getElementById('firmware-update-btn');
|
||||
const boardEl = document.getElementById('firmware-board');
|
||||
try {
|
||||
const resp = await fetch('/api/firmware/check');
|
||||
const resp = await fetch('/api/firmware/check' + (force ? '?force=true' : ''));
|
||||
if (!resp.ok) {
|
||||
if (force) {
|
||||
showStatus(false, await resp.text());
|
||||
}
|
||||
return;
|
||||
}
|
||||
const data = await resp.json();
|
||||
@@ -557,6 +561,9 @@
|
||||
if (!data.enabled) {
|
||||
statusEl.style.display = 'none';
|
||||
btn.style.display = 'none';
|
||||
if (force) {
|
||||
showStatus(false, 'No Gitea repo URL configured.');
|
||||
}
|
||||
return;
|
||||
}
|
||||
statusEl.style.display = 'block';
|
||||
@@ -573,12 +580,21 @@
|
||||
statusEl.textContent = 'No releases found yet.';
|
||||
btn.style.display = 'none';
|
||||
}
|
||||
if (force) {
|
||||
showStatus(true, 'Checked.');
|
||||
}
|
||||
} catch (e) {
|
||||
// A failed check is silent -- the manual upload path still works
|
||||
// regardless, and this just retries on the next poll.
|
||||
// A failed passive poll is silent -- the manual upload path still
|
||||
// works regardless, and this just retries on the next poll. An
|
||||
// explicit "Check now" click still surfaces the error.
|
||||
if (force) {
|
||||
showStatus(false, e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('firmware-check-now').addEventListener('click', () => loadFirmwareCheck(true));
|
||||
|
||||
document.getElementById('firmware-update-btn').addEventListener('click', async () => {
|
||||
const btn = document.getElementById('firmware-update-btn');
|
||||
btn.disabled = true;
|
||||
|
||||
Reference in New Issue
Block a user