forked from btclock/webui
Add Web Updater functionality, improved layout for small displays
This commit is contained in:
parent
4f15eee72b
commit
e53b487236
11 changed files with 401 additions and 92 deletions
4
.github/workflows/workflow.yml
vendored
4
.github/workflows/workflow.yml
vendored
|
@ -41,7 +41,7 @@ jobs:
|
|||
node-version: lts/*
|
||||
cache: yarn
|
||||
cache-dependency-path: '**/yarn.lock'
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/pip
|
||||
|
@ -80,6 +80,8 @@ jobs:
|
|||
run: mkdir -p output && echo "$BLOCK_HEIGHT" > output/version.txt
|
||||
- name: gzip build for LittleFS
|
||||
run: find dist -type f ! -name ".*" -exec sh -c 'mkdir -p "build_gz/$(dirname "${1#dist/}")" && gzip -k "$1" -c > "build_gz/${1#dist/}".gz' _ {} \;
|
||||
- name: Write git rev to file
|
||||
run: echo "$GITHUB_SHA" > build_gz/fs_hash.txt
|
||||
- name: Check GZipped directory size
|
||||
run: |
|
||||
# Set the threshold size in bytes
|
||||
|
|
|
@ -60,6 +60,12 @@
|
|||
"uptime": "Betriebszeit",
|
||||
"wifiSignalStrength": "WiFi-Signalstärke",
|
||||
"wsDataConnection": "BTClock-Datenquelle verbindung"
|
||||
},
|
||||
"firmwareUpdater": {
|
||||
"fileUploadSuccess": "Datei erfolgreich hochgeladen, Gerät neu gestartet. WebUI in {countdown} Sekunden neu geladen",
|
||||
"fileUploadFailed": "Das Hochladen der Datei ist fehlgeschlagen. \nStellen Sie sicher, dass Sie die richtige Datei ausgewählt haben, und versuchen Sie es erneut.",
|
||||
"uploading": "Hochladen",
|
||||
"firmwareUpdateText": "Wenn Sie die Firmware-Upload-Funktion verwenden, stellen Sie sicher, dass Sie die richtigen Dateien verwenden. \nDas Hochladen der falschen Dateien kann dazu führen, dass das Gerät nicht mehr funktioniert. \nWenn es schief geht, können Sie die Firmware wiederherstellen, indem Sie das vollständige Image hochladen, nachdem Sie das Gerät in den BOOT-Modus versetzt haben."
|
||||
}
|
||||
},
|
||||
"colors": {
|
||||
|
@ -88,5 +94,6 @@
|
|||
},
|
||||
"rssiBar": {
|
||||
"tooltip": "Werte > -67 dBm gelten als gut. > -30 dBm ist erstaunlich"
|
||||
}
|
||||
},
|
||||
"warning": "Achtung"
|
||||
}
|
||||
|
|
|
@ -48,7 +48,9 @@
|
|||
"hostname": "Hostname",
|
||||
"frontlight": "Frontlight",
|
||||
"turnOn": "Turn on",
|
||||
"flashFrontlight": "Flash"
|
||||
"flashFrontlight": "Flash",
|
||||
"firmwareUpdate": "Firmware update",
|
||||
"fwCommit": "Firmware commit"
|
||||
},
|
||||
"status": {
|
||||
"title": "Status",
|
||||
|
@ -60,6 +62,12 @@
|
|||
"uptime": "Uptime",
|
||||
"wifiSignalStrength": "WiFi Signal strength",
|
||||
"wsDataConnection": "BTClock data-source connection"
|
||||
},
|
||||
"firmwareUpdater": {
|
||||
"fileUploadFailed": "File upload failed. Make sure you have selected the correct file and try again.",
|
||||
"fileUploadSuccess": "File uploaded successfully, restarting device and reloading WebUI in {countdown} seconds",
|
||||
"uploading": "Uploading",
|
||||
"firmwareUpdateText": "When you use the firmware upload functionality, make sure you use the correct files. Uploading the wrong files can result in a non-working device. If it goes wrong, you can restore firmware by uploading the full image after setting the device in BOOT-mode."
|
||||
}
|
||||
},
|
||||
"colors": {
|
||||
|
@ -88,5 +96,6 @@
|
|||
},
|
||||
"rssiBar": {
|
||||
"tooltip": "Values > -67 dBm are considered good. > -30 dBm is amazing"
|
||||
}
|
||||
},
|
||||
"warning": "Warning"
|
||||
}
|
||||
|
|
|
@ -59,6 +59,12 @@
|
|||
"title": "Estado",
|
||||
"wifiSignalStrength": "Fuerza de la señal WiFi",
|
||||
"wsDataConnection": "Conexión de fuente de datos BTClock"
|
||||
},
|
||||
"firmwareUpdater": {
|
||||
"fileUploadSuccess": "Archivo cargado exitosamente, reiniciando el dispositivo. Recargando WebUI en {countdown} segundos",
|
||||
"fileUploadFailed": "Error al cargar el archivo. \nAsegúrese de haber seleccionado el archivo correcto e inténtelo nuevamente.",
|
||||
"uploading": "Subiendo",
|
||||
"firmwareUpdateText": "Cuando utilice la función de carga de firmware, asegúrese de utilizar los archivos correctos. \nCargar archivos incorrectos puede provocar que el dispositivo no funcione. \nSi sale mal, puede restaurar el firmware cargando la imagen completa después de configurar el dispositivo en modo BOOT."
|
||||
}
|
||||
},
|
||||
"button": {
|
||||
|
@ -87,5 +93,6 @@
|
|||
},
|
||||
"rssiBar": {
|
||||
"tooltip": "Se consideran buenos valores > -67 dBm. > -30 dBm es increíble"
|
||||
}
|
||||
},
|
||||
"warning": "Aviso"
|
||||
}
|
||||
|
|
|
@ -59,6 +59,12 @@
|
|||
"uptime": "Uptime",
|
||||
"wifiSignalStrength": "WiFi signaalsterkte",
|
||||
"wsDataConnection": "BTClock-gegevensbron verbinding"
|
||||
},
|
||||
"firmwareUpdater": {
|
||||
"fileUploadSuccess": "Bestand geüpload, apparaat herstart. WebUI opnieuw geladen over {countdown} seconden",
|
||||
"fileUploadFailed": "Bestandsupload mislukt. \nZorg ervoor dat het juiste bestand is geselecteerd en probeer het opnieuw.",
|
||||
"uploading": "Uploaden",
|
||||
"firmwareUpdateText": "Zorg bij het gebruiken van de firmware upload dat de juiste bestanden gebruikt worden. \nHet uploaden van de verkeerde bestanden kan resulteren in een niet-werkend apparaat. \nAls het misgaat, kunt u de firmware herstellen door de volledige afbeelding te uploaden nadat u het apparaat in de BOOT-modus hebt gezet."
|
||||
}
|
||||
},
|
||||
"colors": {
|
||||
|
@ -87,5 +93,6 @@
|
|||
},
|
||||
"rssiBar": {
|
||||
"tooltip": "Waarden > -67 dBm zijn goed. > -30 dBm is verbazingwekkend"
|
||||
}
|
||||
},
|
||||
"warning": "Waarschuwing"
|
||||
}
|
||||
|
|
9
src/lib/screen.ts
Normal file
9
src/lib/screen.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { writable } from 'svelte/store';
|
||||
|
||||
// Create a writable store to track screen size
|
||||
export const screenSize = writable<number>(window.innerWidth);
|
||||
|
||||
// Function to update the screen size
|
||||
export const updateScreenSize = () => {
|
||||
screenSize.set(window.innerWidth);
|
||||
};
|
|
@ -36,33 +36,34 @@ $input-font-size-sm: $font-size-base * 0.875;
|
|||
@import '../node_modules/bootstrap/scss/progress';
|
||||
@import '../node_modules/bootstrap/scss/tooltip';
|
||||
@import '../node_modules/bootstrap/scss/toasts';
|
||||
@import '../node_modules/bootstrap/scss/alert';
|
||||
|
||||
@import '../node_modules/bootstrap/scss/helpers';
|
||||
@import '../node_modules/bootstrap/scss/utilities/api';
|
||||
|
||||
@include media-breakpoint-down(xl) {
|
||||
html {
|
||||
font-size: 85%;
|
||||
}
|
||||
// @include media-breakpoint-down(xl) {
|
||||
// html {
|
||||
// // font-size: 85%;
|
||||
// }
|
||||
|
||||
button.btn,
|
||||
input[type='button'].btn,
|
||||
input[type='submit'].btn,
|
||||
input[type='reset'].btn {
|
||||
@include button-size(
|
||||
$btn-padding-y-sm,
|
||||
$btn-padding-x-sm,
|
||||
$font-size-sm,
|
||||
$btn-border-radius-sm
|
||||
);
|
||||
}
|
||||
}
|
||||
// button.btn,
|
||||
// input[type='button'].btn,
|
||||
// input[type='submit'].btn,
|
||||
// input[type='reset'].btn {
|
||||
// @include button-size(
|
||||
// $btn-padding-y-sm,
|
||||
// $btn-padding-x-sm,
|
||||
// $font-size-sm,
|
||||
// $btn-border-radius-sm
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
@include media-breakpoint-down(lg) {
|
||||
html {
|
||||
font-size: 75%;
|
||||
}
|
||||
}
|
||||
// @include media-breakpoint-down(lg) {
|
||||
// html {
|
||||
// font-size: 75%;
|
||||
// }
|
||||
// }
|
||||
|
||||
nav {
|
||||
margin-bottom: 15px;
|
||||
|
@ -208,3 +209,8 @@ nav {
|
|||
font-style: italic;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.firmwareUploadStatusAlert,
|
||||
#firmwareUploadProgress {
|
||||
@extend .my-2;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { PUBLIC_BASE_URL } from '$lib/config';
|
||||
import { screenSize, updateScreenSize } from '$lib/screen';
|
||||
|
||||
import { Container, Row, Toast, ToastBody } from 'sveltestrap';
|
||||
|
||||
|
@ -13,6 +14,12 @@
|
|||
fgColor: '0'
|
||||
});
|
||||
|
||||
let uiSettings = writable({
|
||||
inputSize: 'sm',
|
||||
selectClass: '',
|
||||
btnSize: 'lg'
|
||||
});
|
||||
|
||||
let status = writable({
|
||||
data: ['L', 'O', 'A', 'D', 'I', 'N', 'G'],
|
||||
espFreeHeap: 0,
|
||||
|
@ -61,8 +68,43 @@
|
|||
let dataObj = JSON.parse(e.data);
|
||||
status.set(dataObj);
|
||||
});
|
||||
|
||||
function handleResize() {
|
||||
updateScreenSize();
|
||||
}
|
||||
|
||||
// Add an event listener to update the screen size when the window is resized
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
// Call the function initially to set the initial screen size
|
||||
updateScreenSize();
|
||||
|
||||
// Cleanup function to remove the event listener when the component is destroyed
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
};
|
||||
});
|
||||
|
||||
$: {
|
||||
const lgBreakpoint = parseInt(
|
||||
getComputedStyle(document.documentElement).getPropertyValue('--bs-breakpoint-lg')
|
||||
);
|
||||
|
||||
if ($screenSize >= lgBreakpoint) {
|
||||
uiSettings.set({
|
||||
inputSize: 'sm',
|
||||
selectClass: 'form-select-sm',
|
||||
btnSize: 'sm'
|
||||
});
|
||||
} else {
|
||||
uiSettings.set({
|
||||
inputSize: 'lg',
|
||||
selectClass: 'form-select-lg',
|
||||
btnSize: 'xl'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let toastIsOpen = false;
|
||||
let toastColor = 'success';
|
||||
let toastBody = '';
|
||||
|
@ -79,10 +121,15 @@
|
|||
</svelte:head>
|
||||
|
||||
<Container fluid>
|
||||
<Row>
|
||||
<Control bind:settings bind:status></Control>
|
||||
<Row cols={{ lg: 3, sm: 1 }}>
|
||||
<Control bind:settings bind:uiSettings bind:status></Control>
|
||||
<Status bind:settings bind:status></Status>
|
||||
<Settings bind:settings on:showToast={showToast} on:formReset={fetchSettingsData}></Settings>
|
||||
<Settings
|
||||
bind:settings
|
||||
bind:uiSettings
|
||||
on:showToast={showToast}
|
||||
on:formReset={fetchSettingsData}
|
||||
></Settings>
|
||||
</Row>
|
||||
</Container>
|
||||
<div class="position-fixed bottom-0 end-0 p-2">
|
||||
|
|
|
@ -15,8 +15,11 @@
|
|||
Label,
|
||||
Row
|
||||
} from 'sveltestrap';
|
||||
import FirmwareUpdater from './FirmwareUpdater.svelte';
|
||||
|
||||
export let settings = {};
|
||||
export let uiSettings;
|
||||
|
||||
export let customText: string;
|
||||
export let status: Writable<{ leds: [] }>;
|
||||
let ledStatus = [];
|
||||
|
@ -102,25 +105,35 @@
|
|||
<CardBody>
|
||||
<Form>
|
||||
<Row>
|
||||
<Label md={4} for="customText">{$_('section.control.text')}</Label>
|
||||
<Label md={4} for="customText" size={$uiSettings.inputSize}
|
||||
>{$_('section.control.text')}</Label
|
||||
>
|
||||
<Col md="8">
|
||||
<Input
|
||||
type="text"
|
||||
id="customText"
|
||||
bind:value={customText}
|
||||
bsSize="sm"
|
||||
bsSize="$uiSettings.inputSize"
|
||||
maxLength={$settings.numScreens}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Button color="primary" on:click={setCustomText}>{$_('section.control.showText')}</Button>
|
||||
<Row>
|
||||
<Col class="d-flex justify-content-end">
|
||||
<Button color="primary" on:click={setCustomText} bsSize={$uiSettings.btnSize}
|
||||
>{$_('section.control.showText')}</Button
|
||||
>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
<hr />
|
||||
{#if !$settings.disableLeds}
|
||||
<h3>LEDs</h3>
|
||||
<Form>
|
||||
<Row>
|
||||
<Label md={4} for="ledColorPicker" size="sm">{$_('section.control.ledColor')}</Label>
|
||||
<Label md={4} for="ledColorPicker" size={$uiSettings.inputSize}
|
||||
>{$_('section.control.ledColor')}</Label
|
||||
>
|
||||
<Col md="8">
|
||||
<Row class="justify-content-between">
|
||||
{#if ledStatus}
|
||||
|
@ -137,51 +150,84 @@
|
|||
{/each}
|
||||
{/if}
|
||||
</Row>
|
||||
<Row class="justify-content-between">
|
||||
<Col>
|
||||
<Row>
|
||||
<Col class="d-flex justify-content-end">
|
||||
<Input
|
||||
bind:checked={keepLedsSameColor}
|
||||
type="switch"
|
||||
class="mx-auto"
|
||||
label={$_('sections.control.keepSameColor')}
|
||||
bsSize={$uiSettings.inputSize}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
<Button color="secondary" id="turnOffLedsBtn" on:click={turnOffLeds}
|
||||
>{$_('section.control.turnOff')}</Button
|
||||
>
|
||||
<Button color="primary" on:click={setLEDcolor}>{$_('section.control.setColor')}</Button>
|
||||
<Row>
|
||||
<Col class="d-flex justify-content-end">
|
||||
<Button
|
||||
color="secondary"
|
||||
id="turnOffLedsBtn"
|
||||
on:click={turnOffLeds}
|
||||
bsSize={$uiSettings.inputSize}>{$_('section.control.turnOff')}</Button
|
||||
>
|
||||
<div class="mx-2"></div>
|
||||
<Button color="primary" on:click={setLEDcolor} bsSize={$uiSettings.inputSize}
|
||||
>{$_('section.control.setColor')}</Button
|
||||
>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
<hr />
|
||||
{/if}
|
||||
{#if $settings.hasFrontlight}
|
||||
<h3>{$_('section.control.frontlight')}</h3>
|
||||
<Button color="secondary" id="turnOffFrontlightBtn" on:click={turnOffFrontlight}
|
||||
>{$_('section.control.turnOff')}</Button
|
||||
>
|
||||
<Button color="primary" on:click={turnOnFrontlight}>{$_('section.control.turnOn')}</Button>
|
||||
<Button color="success" id="flashFrontlight" on:click={flashFrontlight}
|
||||
>{$_('section.control.flashFrontlight')}</Button
|
||||
>
|
||||
<Row class="d-flex justify-content-between justify-content-md-end">
|
||||
<Col md="auto" class="">
|
||||
<Button color="secondary" id="turnOffFrontlightBtn" on:click={turnOffFrontlight}
|
||||
>{$_('section.control.turnOff')}</Button
|
||||
>
|
||||
</Col><Col md="auto" class="">
|
||||
<Button color="primary" on:click={turnOnFrontlight}
|
||||
>{$_('section.control.turnOn')}</Button
|
||||
>
|
||||
</Col><Col md="auto" class="">
|
||||
<Button color="success" id="flashFrontlight" on:click={flashFrontlight}
|
||||
>{$_('section.control.flashFrontlight')}</Button
|
||||
>
|
||||
</Col>
|
||||
</Row>
|
||||
<hr />
|
||||
{/if}
|
||||
<h3>{$_('section.control.systemInfo')}</h3>
|
||||
<ul class="small system_info">
|
||||
<li>{$_('section.control.version')}: {$settings.gitRev}</li>
|
||||
<li>
|
||||
{$_('section.control.buildTime')}: {new Date(
|
||||
$settings.lastBuildTime * 1000
|
||||
).toLocaleString()}
|
||||
</li>
|
||||
<li>IP: {$settings.ip}</li>
|
||||
<li>HW revision: {$settings.hwRev}</li>
|
||||
<li>{$_('section.control.fwCommit')}: {$settings.gitRev}</li>
|
||||
<li>WebUI commit: {$settings.fsRev}</li>
|
||||
<li>{$_('section.control.hostname')}: {$settings.hostname}</li>
|
||||
</ul>
|
||||
<Button color="danger" id="restartBtn" on:click={restartClock}>{$_('button.restart')}</Button>
|
||||
<Button color="warning" id="forceFullRefresh" on:click={forceFullRefresh}
|
||||
>{$_('button.forceFullRefresh')}</Button
|
||||
>
|
||||
<Row>
|
||||
<Col class="d-flex justify-content-end">
|
||||
<Button color="danger" id="restartBtn" on:click={restartClock}
|
||||
>{$_('button.restart')}</Button
|
||||
>
|
||||
<div class="mx-2"></div>
|
||||
|
||||
<Button color="warning" id="forceFullRefresh" on:click={forceFullRefresh}
|
||||
>{$_('button.forceFullRefresh')}</Button
|
||||
>
|
||||
</Col>
|
||||
</Row>
|
||||
{#if $settings.otaEnabled}
|
||||
<hr />
|
||||
<h3>{$_('section.control.firmwareUpdate')}</h3>
|
||||
<FirmwareUpdater bind:settings />
|
||||
{/if}
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Col>
|
||||
|
|
155
src/routes/FirmwareUpdater.svelte
Normal file
155
src/routes/FirmwareUpdater.svelte
Normal file
|
@ -0,0 +1,155 @@
|
|||
<script lang="ts">
|
||||
import { PUBLIC_BASE_URL } from '$lib/config';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { writable } from 'svelte/store';
|
||||
import { Progress, Alert, Button } from 'sveltestrap';
|
||||
|
||||
export let settings = { hwRev: '' };
|
||||
const countdown = writable(10);
|
||||
let firmwareUploadFile: File | null = null;
|
||||
let firmwareWebUiFile: File | null = null;
|
||||
|
||||
let firmwareUploadProgress = 0;
|
||||
let firmwareUploadSuccess = false;
|
||||
let firmwareUploadError = false;
|
||||
|
||||
const handleFileChange = (event: Event, setFile: (file: File) => void) => {
|
||||
const target = event.target as HTMLInputElement;
|
||||
if (target.files && target.files.length > 0) {
|
||||
setFile(target.files[0]);
|
||||
}
|
||||
};
|
||||
|
||||
function startCountdownToReload(duration: number) {
|
||||
let timeRemaining = duration;
|
||||
|
||||
const interval = setInterval(() => {
|
||||
timeRemaining -= 1;
|
||||
countdown.set(timeRemaining);
|
||||
|
||||
if (timeRemaining <= 0) {
|
||||
clearInterval(interval);
|
||||
location.reload();
|
||||
}
|
||||
}, 1000); // Update every second
|
||||
}
|
||||
|
||||
const uploadFile = async (file: File | null, endpoint: string) => {
|
||||
if (!file) return;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
firmwareUploadSuccess = false;
|
||||
firmwareUploadError = false;
|
||||
try {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', endpoint);
|
||||
|
||||
xhr.upload.onprogress = (event: ProgressEvent) => {
|
||||
if (event.lengthComputable) {
|
||||
firmwareUploadProgress = Math.round((event.loaded * 100) / event.total);
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onload = () => {
|
||||
if (xhr.status === 200 && xhr.responseText != 'FAIL') {
|
||||
firmwareUploadSuccess = true;
|
||||
startCountdownToReload(10);
|
||||
} else {
|
||||
firmwareUploadError = true;
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onerror = () => {
|
||||
firmwareUploadError = true;
|
||||
};
|
||||
|
||||
xhr.send(formData);
|
||||
} catch (error) {
|
||||
firmwareUploadError = true;
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const uploadFirmwareFile = () => {
|
||||
uploadFile(firmwareUploadFile, `${PUBLIC_BASE_URL}/upload/firmware`);
|
||||
};
|
||||
|
||||
const uploadWebUiFile = () => {
|
||||
uploadFile(firmwareWebUiFile, `${PUBLIC_BASE_URL}/upload/webui`);
|
||||
};
|
||||
|
||||
const getFirmwareBinaryName = () => {
|
||||
let binaryFilename = '';
|
||||
switch ($settings.hwRev) {
|
||||
case 'REV_B_EPD_2_13':
|
||||
binaryFilename = 'btclock_rev_b_213epd_firmware.bin';
|
||||
break;
|
||||
case 'REV_A_EPD_2_13':
|
||||
binaryFilename = 'lolin_s3_mini_213epd_firmware.bin';
|
||||
break;
|
||||
case 'REV_A_EPD_2_9':
|
||||
binaryFilename = 'lolin_s3_mini_29epd_firmware.bin';
|
||||
break;
|
||||
default:
|
||||
binaryFilename = 'Unsupported hardware, unable to determine firmware binary filename';
|
||||
}
|
||||
|
||||
return binaryFilename;
|
||||
};
|
||||
</script>
|
||||
|
||||
<section class="row row-cols-lg-auto align-items-end">
|
||||
<div class="col-12">
|
||||
<label for="firmwareFile" class="form-label">Firmware file ({getFirmwareBinaryName()})</label>
|
||||
<input
|
||||
type="file"
|
||||
id="firmwareFile"
|
||||
on:change={(e) => handleFileChange(e, (file) => (firmwareUploadFile = file))}
|
||||
name="update"
|
||||
class="form-control"
|
||||
accept=".bin"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-fill">
|
||||
<Button block on:click={uploadFirmwareFile} color="primary" disabled={!firmwareUploadFile}
|
||||
>Update firmware</Button
|
||||
>
|
||||
</div>
|
||||
<div class="col mt-2">
|
||||
<label for="webuiFile" class="form-label">WebUI file (littlefs.bin)</label>
|
||||
<input
|
||||
type="file"
|
||||
id="webuiFile"
|
||||
name="update"
|
||||
class="form-control"
|
||||
placeholder="littlefs.bin"
|
||||
on:change={(e) => handleFileChange(e, (file) => (firmwareWebUiFile = file))}
|
||||
accept=".bin"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-fill">
|
||||
<Button block on:click={uploadWebUiFile} color="secondary" disabled={!firmwareWebUiFile}
|
||||
>Update WebUI</Button
|
||||
>
|
||||
</div>
|
||||
</section>
|
||||
{#if firmwareUploadProgress > 0}
|
||||
<Progress striped value={firmwareUploadProgress} class="progress" id="firmwareUploadProgress"
|
||||
>{$_('section.firmwareUpdater.uploading')}... {firmwareUploadProgress}%</Progress
|
||||
>
|
||||
{/if}
|
||||
{#if firmwareUploadSuccess}
|
||||
<Alert color="success" class="firmwareUploadStatusAlert"
|
||||
>{$_('section.firmwareUpdater.fileUploadSuccess', { values: { countdown: $countdown } })}
|
||||
</Alert>
|
||||
{/if}
|
||||
|
||||
{#if firmwareUploadError}
|
||||
<Alert color="danger" class="firmwareUploadStatusAlert"
|
||||
>{$_('section.firmwareUpdater.fileUploadFailed')}</Alert
|
||||
>
|
||||
{/if}
|
||||
<small
|
||||
>⚠️ <strong>{$_('warning')}</strong>: {$_('section.firmwareUpdater.firmwareUpdateText')}</small
|
||||
>
|
|
@ -20,6 +20,7 @@
|
|||
} from 'sveltestrap';
|
||||
|
||||
export let settings;
|
||||
export let uiSettings;
|
||||
|
||||
const wifiTxPowerMap = new Map<string, number>([
|
||||
['Default', 80],
|
||||
|
@ -89,7 +90,7 @@
|
|||
<CardBody>
|
||||
<Form on:submit={onSave}>
|
||||
<Row>
|
||||
<Label md={6} for="fgColor" size="sm"
|
||||
<Label md={6} for="fgColor" size={$uiSettings.inputSize}
|
||||
>{$_('section.settings.textColor', { default: 'Text color' })}</Label
|
||||
>
|
||||
<Col md="6">
|
||||
|
@ -98,8 +99,8 @@
|
|||
bind:value={$settings.fgColor}
|
||||
name="select"
|
||||
id="fgColor"
|
||||
bsSize="sm"
|
||||
class="form-select-sm"
|
||||
bsSize={$uiSettings.inputSize}
|
||||
class={$uiSettings.selectClass}
|
||||
>
|
||||
<option value="0">{$_('colors.black')}</option>
|
||||
<option value="65535">{$_('colors.white')}</option>
|
||||
|
@ -107,15 +108,17 @@
|
|||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Label md={6} for="bgColor" size="sm">{$_('section.settings.backgroundColor')}</Label>
|
||||
<Label md={6} for="bgColor" size={$uiSettings.inputSize}
|
||||
>{$_('section.settings.backgroundColor')}</Label
|
||||
>
|
||||
<Col md="6">
|
||||
<Input
|
||||
type="select"
|
||||
bind:value={$settings.bgColor}
|
||||
name="select"
|
||||
id="bgColor"
|
||||
bsSize="sm"
|
||||
class="form-select-sm"
|
||||
bsSize={$uiSettings.inputSize}
|
||||
class={$uiSettings.selectClass}
|
||||
>
|
||||
<option value="0">{$_('colors.black')}</option>
|
||||
<option value="65535">{$_('colors.white')}</option>
|
||||
|
@ -123,9 +126,11 @@
|
|||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Label md={6} for="timePerScreen" size="sm">{$_('section.settings.timePerScreen')}</Label>
|
||||
<Label md={6} for="timePerScreen" size={$uiSettings.inputSize}
|
||||
>{$_('section.settings.timePerScreen')}</Label
|
||||
>
|
||||
<Col md="6">
|
||||
<InputGroup size="sm">
|
||||
<InputGroup size={$uiSettings.inputSize}>
|
||||
<Input
|
||||
type="number"
|
||||
id="timePerScreen"
|
||||
|
@ -138,11 +143,11 @@
|
|||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Label md={6} for="fullRefreshMin" size="sm"
|
||||
<Label md={6} for="fullRefreshMin" size={$uiSettings.inputSize}
|
||||
>{$_('section.settings.fullRefreshEvery')}</Label
|
||||
>
|
||||
<Col md="6">
|
||||
<InputGroup size="sm">
|
||||
<InputGroup size={$uiSettings.inputSize}>
|
||||
<Input
|
||||
type="number"
|
||||
id="fullRefreshMin"
|
||||
|
@ -155,11 +160,11 @@
|
|||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Label md={6} for="minSecPriceUpd" size="sm"
|
||||
<Label md={6} for="minSecPriceUpd" size={$uiSettings.inputSize}
|
||||
>{$_('section.settings.timeBetweenPriceUpdates')}</Label
|
||||
>
|
||||
<Col md="6">
|
||||
<InputGroup size="sm">
|
||||
<InputGroup size={$uiSettings.inputSize}>
|
||||
<Input
|
||||
type="number"
|
||||
id="minSecPriceUpd"
|
||||
|
@ -173,9 +178,11 @@
|
|||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Label md={6} for="tzOffset" size="sm">{$_('section.settings.timezoneOffset')}</Label>
|
||||
<Label md={6} for="tzOffset" size={$uiSettings.inputSize}
|
||||
>{$_('section.settings.timezoneOffset')}</Label
|
||||
>
|
||||
<Col md="6">
|
||||
<InputGroup size="sm">
|
||||
<InputGroup size={$uiSettings.inputSize}>
|
||||
<Input
|
||||
type="number"
|
||||
step="1"
|
||||
|
@ -189,7 +196,9 @@
|
|||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Label md={6} for="ledBrightness" size="sm">{$_('section.settings.ledBrightness')}</Label>
|
||||
<Label md={6} for="ledBrightness" size={$uiSettings.inputSize}
|
||||
>{$_('section.settings.ledBrightness')}</Label
|
||||
>
|
||||
<Col md="6">
|
||||
<Input
|
||||
type="range"
|
||||
|
@ -204,7 +213,7 @@
|
|||
</Row>
|
||||
{#if $settings.hasFrontlight}
|
||||
<Row>
|
||||
<Label md={6} for="flMaxBrightness" size="sm"
|
||||
<Label md={6} for="flMaxBrightness" size={$uiSettings.inputSize}
|
||||
>{$_('section.settings.flMaxBrightness')}</Label
|
||||
>
|
||||
<Col md="6">
|
||||
|
@ -221,7 +230,7 @@
|
|||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Label md={6} for="flEffectDelay" size="sm"
|
||||
<Label md={6} for="flEffectDelay" size={$uiSettings.inputSize}
|
||||
>{$_('section.settings.flEffectDelay')}</Label
|
||||
>
|
||||
<Col md="6">
|
||||
|
@ -238,7 +247,7 @@
|
|||
</Row>
|
||||
{/if}
|
||||
<Row>
|
||||
<Label md={6} for="hostnamePrefix" size="sm"
|
||||
<Label md={6} for="hostnamePrefix" size={$uiSettings.inputSize}
|
||||
>{$_('section.settings.hostnamePrefix')}</Label
|
||||
>
|
||||
<Col md="6">
|
||||
|
@ -247,12 +256,12 @@
|
|||
bind:value={$settings.hostnamePrefix}
|
||||
name="hostnamePrefix"
|
||||
id="hostnamePrefix"
|
||||
bsSize="sm"
|
||||
bsSize={$uiSettings.inputSize}
|
||||
></Input>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Label md={6} for="wifiTxPower" size="sm"
|
||||
<Label md={6} for="wifiTxPower" size={$uiSettings.inputSize}
|
||||
>{$_('section.settings.wifiTxPower', { default: 'WiFi Tx Power' })}</Label
|
||||
>
|
||||
<Col md="6">
|
||||
|
@ -261,8 +270,8 @@
|
|||
bind:value={$settings.txPower}
|
||||
name="select"
|
||||
id="fgColor"
|
||||
bsSize="sm"
|
||||
class="form-select-sm"
|
||||
bsSize={$uiSettings.inputSize}
|
||||
class={$uiSettings.selectClass}
|
||||
>
|
||||
{#each wifiTxPowerMap as [key, value]}
|
||||
<option {value}>{key}</option>
|
||||
|
@ -277,7 +286,7 @@
|
|||
id="ledTestOnPower"
|
||||
bind:checked={$settings.ledTestOnPower}
|
||||
type="switch"
|
||||
bsSize="sm"
|
||||
bsSize={$uiSettings.inputSize}
|
||||
label={$_('section.settings.ledPowerOnTest')}
|
||||
/>
|
||||
</Col>
|
||||
|
@ -286,7 +295,7 @@
|
|||
id="ledFlashOnUpd"
|
||||
bind:checked={$settings.ledFlashOnUpd}
|
||||
type="switch"
|
||||
bsSize="sm"
|
||||
bsSize={$uiSettings.inputSize}
|
||||
label={$_('section.settings.ledFlashOnBlock')}
|
||||
/>
|
||||
</Col>
|
||||
|
@ -295,7 +304,7 @@
|
|||
id="stealFocus"
|
||||
bind:checked={$settings.stealFocus}
|
||||
type="switch"
|
||||
bsSize="sm"
|
||||
bsSize={$uiSettings.inputSize}
|
||||
label={$_('section.settings.StealFocusOnNewBlock')}
|
||||
/>
|
||||
</Col>
|
||||
|
@ -304,7 +313,7 @@
|
|||
id="mcapBigChar"
|
||||
bind:checked={$settings.mcapBigChar}
|
||||
type="switch"
|
||||
bsSize="sm"
|
||||
bsSize={$uiSettings.inputSize}
|
||||
label={$_('section.settings.useBigCharsMcap')}
|
||||
/>
|
||||
</Col>
|
||||
|
@ -313,7 +322,7 @@
|
|||
id="otaEnabled"
|
||||
bind:checked={$settings.otaEnabled}
|
||||
type="switch"
|
||||
bsSize="sm"
|
||||
bsSize={$uiSettings.inputSize}
|
||||
label="{$_('section.settings.otaUpdates')} ({$_('restartRequired')})"
|
||||
/>
|
||||
</Col>
|
||||
|
@ -322,7 +331,7 @@
|
|||
id="mdnsEnabled"
|
||||
bind:checked={$settings.mdnsEnabled}
|
||||
type="switch"
|
||||
bsSize="sm"
|
||||
bsSize={$uiSettings.inputSize}
|
||||
label="{$_('section.settings.enableMdns')} ({$_('restartRequired')})"
|
||||
/>
|
||||
</Col>
|
||||
|
@ -331,7 +340,7 @@
|
|||
id="fetchEurPrice"
|
||||
bind:checked={$settings.fetchEurPrice}
|
||||
type="switch"
|
||||
bsSize="sm"
|
||||
bsSize={$uiSettings.inputSize}
|
||||
label="{$_('section.settings.fetchEuroPrice')} ({$_('restartRequired')})"
|
||||
/>
|
||||
</Col>
|
||||
|
@ -340,7 +349,7 @@
|
|||
id="useBlkCountdown"
|
||||
bind:checked={$settings.useBlkCountdown}
|
||||
type="switch"
|
||||
bsSize="sm"
|
||||
bsSize={$uiSettings.inputSize}
|
||||
label={$_('section.settings.useBlkCountdown')}
|
||||
/>
|
||||
</Col>
|
||||
|
@ -349,7 +358,7 @@
|
|||
id="useSatsSymbol"
|
||||
bind:checked={$settings.useSatsSymbol}
|
||||
type="switch"
|
||||
bsSize="sm"
|
||||
bsSize={$uiSettings.inputSize}
|
||||
label={$_('section.settings.useSatsSymbol')}
|
||||
/>
|
||||
</Col>
|
||||
|
@ -358,7 +367,7 @@
|
|||
id="suffixPrice"
|
||||
bind:checked={$settings.suffixPrice}
|
||||
type="switch"
|
||||
bsSize="sm"
|
||||
bsSize={$uiSettings.inputSize}
|
||||
label={$_('section.settings.suffixPrice')}
|
||||
/>
|
||||
</Col>
|
||||
|
@ -367,7 +376,7 @@
|
|||
id="disableLeds"
|
||||
bind:checked={$settings.disableLeds}
|
||||
type="switch"
|
||||
bsSize="sm"
|
||||
bsSize={$uiSettings.inputSize}
|
||||
label={$_('section.settings.disableLeds')}
|
||||
/>
|
||||
</Col>
|
||||
|
@ -376,7 +385,7 @@
|
|||
id="ownDataSource"
|
||||
bind:checked={$settings.ownDataSource}
|
||||
type="switch"
|
||||
bsSize="sm"
|
||||
bsSize={$uiSettings.inputSize}
|
||||
label="{$_('section.settings.ownDataSource')} ({$_('restartRequired')})"
|
||||
/>
|
||||
</Col>
|
||||
|
@ -386,7 +395,7 @@
|
|||
id="flAlwaysOn"
|
||||
bind:checked={$settings.flAlwaysOn}
|
||||
type="switch"
|
||||
bsSize="sm"
|
||||
bsSize={$uiSettings.inputSize}
|
||||
label={$_('section.settings.flAlwaysOn')}
|
||||
/>
|
||||
</Col>
|
||||
|
@ -395,7 +404,7 @@
|
|||
id="flFlashOnUpd"
|
||||
bind:checked={$settings.flFlashOnUpd}
|
||||
type="switch"
|
||||
bsSize="sm"
|
||||
bsSize={$uiSettings.inputSize}
|
||||
label={$_('section.settings.flFlashOnUpd')}
|
||||
/>
|
||||
</Col>
|
||||
|
@ -411,15 +420,20 @@
|
|||
id="screens_{s.id}"
|
||||
bind:checked={s.enabled}
|
||||
type="switch"
|
||||
bsSize="sm"
|
||||
bsSize={$uiSettings.inputSize}
|
||||
label={s.name}
|
||||
/>
|
||||
</Col>
|
||||
{/each}
|
||||
{/if}
|
||||
</Row>
|
||||
<Button on:click={handleReset} color="secondary">{$_('button.reset')}</Button>
|
||||
<Button color="primary">{$_('button.save')}</Button>
|
||||
<Row>
|
||||
<Col class="d-flex justify-content-end">
|
||||
<Button on:click={handleReset} color="secondary">{$_('button.reset')}</Button>
|
||||
<div class="mx-2"></div>
|
||||
<Button color="primary">{$_('button.save')}</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
|
Loading…
Reference in a new issue