259 lines
7.4 KiB
Svelte
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>
|