webui-ng/src/lib/components/sections/FirmwareUpdateSection.svelte
Djuri Baars 98ad7d1432
Some checks failed
/ build (push) Failing after 1m26s
/ check-changes (push) Successful in 8s
feat: Most settings implemented
2025-05-04 02:12:17 +02:00

259 lines
7.4 KiB
Svelte

<script lang="ts">
import { m } from '$lib/paraglide/messages';
import { CardContainer, Alert } from '$lib/components';
import { getFirmwareBinaryName, getWebUiBinaryName } from '$lib/utils';
import { status, settings, firmwareRelease } from '$lib/stores';
import { onMount } from 'svelte';
import { uploadFirmwareFile, uploadWebUiFile, autoUpdate } from '$lib/clockControl';
// Format date to a more readable format
function formatDate(dateString: string): string {
if (!dateString) return '';
const date = new Date(dateString);
return date.toLocaleString();
}
// Upload status
let firmwareUploadProgress = 0;
let webuiUploadProgress = 0;
let isUploading = false;
let uploadError: string | null = null;
let uploadSuccess = false;
let countdownValue = 0;
let countdownInterval: number | null = null;
// File selection status
let firmwareFileSelected = false;
let webuiFileSelected = false;
// Handle file selection change for firmware file
function handleFirmwareFileChange(event: Event) {
const fileInput = event.target as HTMLInputElement;
firmwareFileSelected = !!(fileInput.files && fileInput.files.length > 0);
}
// Handle file selection change for WebUI file
function handleWebUIFileChange(event: Event) {
const fileInput = event.target as HTMLInputElement;
webuiFileSelected = !!(fileInput.files && fileInput.files.length > 0);
}
// Start countdown to reload page after successful upload
function startCountdownToReload(seconds: number) {
countdownValue = seconds;
if (countdownInterval) clearInterval(countdownInterval);
countdownInterval = setInterval(() => {
countdownValue--;
if (countdownValue <= 0) {
if (countdownInterval) clearInterval(countdownInterval);
window.location.reload();
}
}, 1000) as unknown as number;
}
// Handle firmware file upload
function handleFirmwareUpload() {
const fileInput = document.getElementById('firmwareFile') as HTMLInputElement;
if (!fileInput || !fileInput.files || fileInput.files.length === 0) {
uploadError = 'Please select a firmware file';
return;
}
isUploading = true;
uploadError = null;
uploadSuccess = false;
firmwareUploadProgress = 0;
uploadFirmwareFile(fileInput.files[0], {
onProgress: (progress) => {
firmwareUploadProgress = progress;
},
onSuccess: () => {
isUploading = false;
uploadSuccess = true;
startCountdownToReload(10);
},
onError: (error) => {
isUploading = false;
uploadError =
typeof error === 'string' && error !== 'FAIL' ? error : 'Failed to upload firmware';
}
});
}
// Handle webUI file upload
function handleWebUIUpload() {
const fileInput = document.getElementById('webuiFile') as HTMLInputElement;
if (!fileInput || !fileInput.files || fileInput.files.length === 0) {
uploadError = 'Please select a WebUI file';
return;
}
isUploading = true;
uploadError = null;
uploadSuccess = false;
webuiUploadProgress = 0;
uploadWebUiFile(fileInput.files[0], {
onProgress: (progress) => {
webuiUploadProgress = progress;
},
onSuccess: () => {
isUploading = false;
uploadSuccess = true;
startCountdownToReload(10);
},
onError: (error) => {
isUploading = false;
uploadError = typeof error === 'string' ? error : 'Failed to upload WebUI';
}
});
}
const onAutoUpdate = (e: Event) => {
e.preventDefault();
autoUpdate({
onSuccess: () => {
// toast.push({
// type: 'success',
// message
// });
startCountdownToReload(10);
},
onError: (error) => {
// toast.push({
// type: 'error',
// message: error as string
// });
uploadError = typeof error === 'string' ? error : 'Failed to auto-update';
}
});
};
// Fetch firmware data when component is mounted
onMount(() => {
firmwareRelease.fetchLatest();
// Cleanup on component destroy
return () => {
if (countdownInterval) clearInterval(countdownInterval);
};
});
</script>
<CardContainer title={m['section.control.firmwareUpdate']()} className="">
<div>
{#if $firmwareRelease.isLoading}
<div class="my-4 flex justify-center">
<span class="loading loading-spinner"></span>
</div>
{:else if $firmwareRelease.error}
<Alert type="error" className="mb-4" message={$firmwareRelease.error} />
{:else}
<p class="mb-2 text-sm">
{m['section.firmwareUpdater.latestVersion']()}
{$firmwareRelease.tag_name} - {m['section.firmwareUpdater.releaseDate']()}
{formatDate($firmwareRelease.published_at)} -
<a
href={$firmwareRelease.html_url}
target="_blank"
rel="noopener noreferrer"
class="link link-primary"
>
{m['section.firmwareUpdater.viewRelease']()}
</a>
</p>
{#if !$status.isOTAUpdating && $settings.gitTag !== $firmwareRelease.tag_name}
<Alert type="success" className="mb-4">
{m['section.firmwareUpdater.swUpdateAvailable']()}
{$settings.gitTag}{$firmwareRelease.tag_name} -
<a href="/" class="link link-primary" on:click={onAutoUpdate}
>{m['section.firmwareUpdater.autoUpdate']()}</a
>.
</Alert>
{/if}
{/if}
{#if !$status.isOTAUpdating}
<div class="form-control mb-4">
<label class="label" for="firmwareFile">
<span class="label-text">Firmware File ({getFirmwareBinaryName($settings.hwRev)})</span>
</label>
<div class="flex gap-2">
<input
type="file"
class="file-input file-input-bordered w-full"
id="firmwareFile"
accept=".bin"
disabled={isUploading}
on:change={handleFirmwareFileChange}
/>
<button
class="btn btn-primary w-1/4"
on:click={handleFirmwareUpload}
disabled={isUploading || !firmwareFileSelected}
>
{#if isUploading && firmwareUploadProgress > 0}
{firmwareUploadProgress}%
{:else}
{m['section.control.firmwareUpdate']()}
{/if}
</button>
</div>
{#if isUploading && firmwareUploadProgress > 0}
<progress class="progress progress-primary mt-2" value={firmwareUploadProgress} max="100"
></progress>
{/if}
</div>
<div class="form-control">
<label class="label" for="webuiFile">
<span class="label-text">WebUI File ({getWebUiBinaryName($settings.hwRev)})</span>
</label>
<div class="flex gap-2">
<input
type="file"
class="file-input file-input-bordered w-full"
id="webuiFile"
accept=".bin"
disabled={isUploading}
on:change={handleWebUIFileChange}
/>
<button
class="btn btn-primary w-1/4"
on:click={handleWebUIUpload}
disabled={isUploading || !webuiFileSelected}
>
{#if isUploading && webuiUploadProgress > 0}
{webuiUploadProgress}%
{:else}
Update WebUI
{/if}
</button>
</div>
{#if isUploading && webuiUploadProgress > 0}
<progress class="progress progress-primary mt-2" value={webuiUploadProgress} max="100"
></progress>
{/if}
</div>
{:else}
<Alert type="info" className="my-4">
<span class="loading loading-spinner text-neutral"></span>
{m['section.firmwareUpdater.autoUpdateInProgress']()}
</Alert>
{/if}
{#if uploadSuccess}
<Alert type="success" className="my-4">
{m['section.firmwareUpdater.fileUploadSuccess']({ countdown: countdownValue })}
</Alert>
{:else if uploadError}
<Alert type="error" className="my-4">
{uploadError}
</Alert>
{/if}
<Alert type="warning" className="mt-4">
{m['section.firmwareUpdater.firmwareUpdateText']()}
</Alert>
</div>
</CardContainer>