Lint and format all files

This commit is contained in:
Djuri Baars 2023-11-19 20:27:22 +01:00
parent 3eaf897dbb
commit d25284e3a4
22 changed files with 1357 additions and 1280 deletions

View File

@ -18,6 +18,9 @@ module.exports = {
es2017: true,
node: true
},
rules: {
'no-empty': ['error', { allowEmptyCatch: true }]
},
overrides: [
{
files: ['*.svelte'],

View File

@ -3,7 +3,7 @@ name: BTClock WebUI CI
on: [push]
env:
PUBLIC_BASE_URL: ""
PUBLIC_BASE_URL: ''
jobs:
check-changes:
@ -12,13 +12,13 @@ jobs:
all_changed_and_modified_files_count: ${{ steps.changed-files.outputs.all_changed_and_modified_files_count }}
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Get changed files count
id: changed-files
uses: tj-actions/changed-files@v40.1.1
with:
files_ignore: "doc/**,README.md,Dockerfile,.*"
files_ignore: 'doc/**,README.md,Dockerfile,.*'
files_ignore_separator: ','
- name: Print changed files count
run: >
@ -33,7 +33,7 @@ jobs:
contents: write
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: actions/setup-node@v3
@ -71,7 +71,7 @@ jobs:
- name: Create tarball
run: tar czf webui.tgz --strip-components=1 dist
- name: Build LittleFS
run: /tmp/mklittlefs/mklittlefs -c build_gz -s 409600 littlefs.bin
run: /tmp/mklittlefs/mklittlefs -c build_gz -s 409600 littlefs.bin
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
@ -84,7 +84,7 @@ jobs:
tag: ${{ steps.getBlockHeight.outputs.blockHeight }}
commit: main
name: release-${{ steps.getBlockHeight.outputs.blockHeight }}
artifacts: "littlefs.bin,webui.tgz"
artifacts: 'littlefs.bin,webui.tgz'
allowUpdates: true
removeArtifacts: true
makeLatest: true
makeLatest: true

View File

@ -8,7 +8,7 @@ node_modules
!.env.example
dist/
build_gz
dist/**
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml

View File

@ -35,7 +35,7 @@ To upload the firmware to the BTClock, you need to GZIP all the files. You can u
Then you can make a `LittleFS.bin` with mklittlefs:
```bash
mklittlefs -c build_gz -s 409600 littlefs.bin
mklittlefs -c build_gz -s 409600 littlefs.bin
```
You can preview the production build with `yarn preview`.
You can preview the production build with `yarn preview`.

View File

@ -1,10 +1,10 @@
import type { Handle } from '@sveltejs/kit'
import { locale } from 'svelte-i18n'
import type { Handle } from '@sveltejs/kit';
import { locale } from 'svelte-i18n';
export const handle: Handle = async ({ event, resolve }) => {
const lang = event.request.headers.get('accept-language')?.split(',')[0]
const lang = event.request.headers.get('accept-language')?.split(',')[0];
if (lang) {
locale.set(lang)
locale.set(lang);
}
return resolve(event)
}
return resolve(event);
};

View File

@ -1,13 +1,17 @@
import { browser } from '$app/environment'
import { init, register } from 'svelte-i18n'
import { browser } from '$app/environment';
import { init, register } from 'svelte-i18n';
const defaultLocale = 'en'
const defaultLocale = 'en';
register('en', () => import('../locales/en.json'))
register('nl', () => import('../locales/nl.json'))
register('es', () => import('../locales/es.json'))
register('en', () => import('../locales/en.json'));
register('nl', () => import('../locales/nl.json'));
register('es', () => import('../locales/es.json'));
init({
fallbackLocale: defaultLocale,
initialLocale: browser ? browser && localStorage.getItem('locale') ? localStorage.getItem('locale') : window.navigator.language : defaultLocale,
})
initialLocale: browser
? browser && localStorage.getItem('locale')
? localStorage.getItem('locale')
: window.navigator.language
: defaultLocale
});

View File

@ -1,71 +1,71 @@
{
"section": {
"settings": {
"title": "Settings",
"textColor": "Text color",
"backgroundColor": "Background color",
"ledPowerOnTest": "LED power-on test",
"ledFlashOnBlock": "LED flash on new block",
"timePerScreen": "Time per screen",
"ledBrightness": "LED brightness",
"timezoneOffset": "Timezone offset",
"timeBetweenPriceUpdates": "Time between price updates",
"fullRefreshEvery": "Full refresh every",
"mempoolnstance": "Mempool Instance",
"hostnamePrefix": "Hostname prefix",
"StealFocusOnNewBlock": "Steal focus on new block",
"useBigCharsMcap": "Use big characters for market cap",
"otaUpdates": "OTA updates",
"enableMdns": "mDNS",
"fetchEuroPrice": "Fetch € price",
"shortAmountsWarning": "Short amounts might shorten lifespan.",
"tzOffsetHelpText": "A restart is required to apply TZ offset.",
"screens": "Screens"
},
"control": {
"systemInfo": "System info",
"version": "Version",
"buildTime": "Build time",
"ledColor": "LED color",
"turnOff": "Turn off",
"setColor": "Set color",
"showText": "Show text",
"text": "Text",
"title": "Control",
"hostname": "Hostname"
},
"status": {
"title": "Status",
"screenCycle": "Screen cycle",
"memoryFree": "Memory free",
"wsPriceConnection": "WS Price connection",
"wsMempoolConnection": "WS Mempool.space connection",
"fetchEuroNote": "If you use \"Fetch € price\" the WS Price connection will show ❌ since it uses another data source.",
"uptime": "Uptime"
}
},
"colors": {
"black": "Black",
"white": "White"
},
"time": {
"minutes": "minutes",
"seconds": "seconds"
},
"restartRequired": "restart required",
"button": {
"save": "Save",
"reset": "Reset",
"restart": "Restart",
"forceFullRefresh": "Force full refresh"
},
"timer": {
"running": "running",
"stopped": "stopped"
},
"sections": {
"control": {
"keepSameColor": "Keep same color"
}
}
"section": {
"settings": {
"title": "Settings",
"textColor": "Text color",
"backgroundColor": "Background color",
"ledPowerOnTest": "LED power-on test",
"ledFlashOnBlock": "LED flash on new block",
"timePerScreen": "Time per screen",
"ledBrightness": "LED brightness",
"timezoneOffset": "Timezone offset",
"timeBetweenPriceUpdates": "Time between price updates",
"fullRefreshEvery": "Full refresh every",
"mempoolnstance": "Mempool Instance",
"hostnamePrefix": "Hostname prefix",
"StealFocusOnNewBlock": "Steal focus on new block",
"useBigCharsMcap": "Use big characters for market cap",
"otaUpdates": "OTA updates",
"enableMdns": "mDNS",
"fetchEuroPrice": "Fetch € price",
"shortAmountsWarning": "Short amounts might shorten lifespan.",
"tzOffsetHelpText": "A restart is required to apply TZ offset.",
"screens": "Screens"
},
"control": {
"systemInfo": "System info",
"version": "Version",
"buildTime": "Build time",
"ledColor": "LED color",
"turnOff": "Turn off",
"setColor": "Set color",
"showText": "Show text",
"text": "Text",
"title": "Control",
"hostname": "Hostname"
},
"status": {
"title": "Status",
"screenCycle": "Screen cycle",
"memoryFree": "Memory free",
"wsPriceConnection": "WS Price connection",
"wsMempoolConnection": "WS Mempool.space connection",
"fetchEuroNote": "If you use \"Fetch € price\" the WS Price connection will show ❌ since it uses another data source.",
"uptime": "Uptime"
}
},
"colors": {
"black": "Black",
"white": "White"
},
"time": {
"minutes": "minutes",
"seconds": "seconds"
},
"restartRequired": "restart required",
"button": {
"save": "Save",
"reset": "Reset",
"restart": "Restart",
"forceFullRefresh": "Force full refresh"
},
"timer": {
"running": "running",
"stopped": "stopped"
},
"sections": {
"control": {
"keepSameColor": "Keep same color"
}
}
}

View File

@ -1,71 +1,71 @@
{
"section": {
"settings": {
"title": "Configuración",
"textColor": "Color de texto",
"backgroundColor": "Color de fondo",
"ledBrightness": "Brillo LED",
"screens": "Pantallas",
"shortAmountsWarning": "Cantidades pequeñas pueden acortar la vida útil.",
"fullRefreshEvery": "Actualización completa cada",
"timePerScreen": "Tiempo por pantalla",
"tzOffsetHelpText": "Es necesario reiniciar para aplicar la compensación.",
"timezoneOffset": "Compensación de zona horaria",
"StealFocusOnNewBlock": "Presta atención al nuevo bloque",
"ledFlashOnBlock": "El LED parpadea con un bloque nuevo",
"useBigCharsMcap": "Utilice caracteres grandes para la market cap",
"fetchEuroPrice": "Obtener precio en €",
"timeBetweenPriceUpdates": "Tiempo entre actualizaciones de precios",
"ledPowerOnTest": "Prueba de encendido del LED",
"enableMdns": "mDNS",
"hostnamePrefix": "Prefijo de nombre de host",
"mempoolnstance": "Instancia de Mempool",
"otaUpdates": "Actualización por aire"
},
"control": {
"turnOff": "Apagar",
"setColor": "Establecer el color",
"version": "Versión",
"ledColor": "color del LED",
"systemInfo": "Info del sistema",
"showText": "Mostrar texto",
"text": "Texto",
"title": "Control",
"buildTime": "Tiempo de compilación",
"hostname": "Nombre del host"
},
"status": {
"memoryFree": "Memoria RAM libre",
"wsPriceConnection": "Conexión WebSocket Precio",
"wsMempoolConnection": "Conexión WebSocket Mempool.space",
"screenCycle": "Ciclo de pantalla",
"uptime": "Tiempo de funcionamiento",
"fetchEuroNote": "Si utiliza \"Obtener precio en €\", la conexión de Precio WS mostrará ❌ ya que utiliza otra fuente de datos.",
"title": "Estado"
}
},
"button": {
"save": "Guardar",
"reset": "Restaurar",
"restart": "Reiniciar",
"forceFullRefresh": "Forzar refresco"
},
"colors": {
"black": "Negro",
"white": "Blanco"
},
"restartRequired": "reinicio requerido",
"time": {
"minutes": "minutos",
"seconds": "segundos"
},
"timer": {
"running": "funcionando",
"stopped": "detenido"
},
"sections": {
"control": {
"keepSameColor": "Mantén el mismo color"
}
}
"section": {
"settings": {
"title": "Configuración",
"textColor": "Color de texto",
"backgroundColor": "Color de fondo",
"ledBrightness": "Brillo LED",
"screens": "Pantallas",
"shortAmountsWarning": "Cantidades pequeñas pueden acortar la vida útil.",
"fullRefreshEvery": "Actualización completa cada",
"timePerScreen": "Tiempo por pantalla",
"tzOffsetHelpText": "Es necesario reiniciar para aplicar la compensación.",
"timezoneOffset": "Compensación de zona horaria",
"StealFocusOnNewBlock": "Presta atención al nuevo bloque",
"ledFlashOnBlock": "El LED parpadea con un bloque nuevo",
"useBigCharsMcap": "Utilice caracteres grandes para la market cap",
"fetchEuroPrice": "Obtener precio en €",
"timeBetweenPriceUpdates": "Tiempo entre actualizaciones de precios",
"ledPowerOnTest": "Prueba de encendido del LED",
"enableMdns": "mDNS",
"hostnamePrefix": "Prefijo de nombre de host",
"mempoolnstance": "Instancia de Mempool",
"otaUpdates": "Actualización por aire"
},
"control": {
"turnOff": "Apagar",
"setColor": "Establecer el color",
"version": "Versión",
"ledColor": "color del LED",
"systemInfo": "Info del sistema",
"showText": "Mostrar texto",
"text": "Texto",
"title": "Control",
"buildTime": "Tiempo de compilación",
"hostname": "Nombre del host"
},
"status": {
"memoryFree": "Memoria RAM libre",
"wsPriceConnection": "Conexión WebSocket Precio",
"wsMempoolConnection": "Conexión WebSocket Mempool.space",
"screenCycle": "Ciclo de pantalla",
"uptime": "Tiempo de funcionamiento",
"fetchEuroNote": "Si utiliza \"Obtener precio en €\", la conexión de Precio WS mostrará ❌ ya que utiliza otra fuente de datos.",
"title": "Estado"
}
},
"button": {
"save": "Guardar",
"reset": "Restaurar",
"restart": "Reiniciar",
"forceFullRefresh": "Forzar refresco"
},
"colors": {
"black": "Negro",
"white": "Blanco"
},
"restartRequired": "reinicio requerido",
"time": {
"minutes": "minutos",
"seconds": "segundos"
},
"timer": {
"running": "funcionando",
"stopped": "detenido"
},
"sections": {
"control": {
"keepSameColor": "Mantén el mismo color"
}
}
}

View File

@ -1,70 +1,70 @@
{
"section": {
"settings": {
"title": "Instellingen",
"textColor": "Tekstkleur",
"backgroundColor": "Achtergrondkleur",
"timeBetweenPriceUpdates": "Tijd tussen prijs updates",
"timezoneOffset": "Tijdzone afwijking",
"ledBrightness": "LED helderheid",
"timePerScreen": "Tijd per scherm",
"fullRefreshEvery": "Volledig verversen elke",
"shortAmountsWarning": "Lage waardes verkorten levensduur",
"tzOffsetHelpText": "Herstart nodig voor toepassen afwijking.",
"enableMdns": "mDNS",
"ledPowerOnTest": "LED test bij aanzetten",
"StealFocusOnNewBlock": "Pak aandacht bij nieuw blok",
"ledFlashOnBlock": "Knipper led bij nieuw blok",
"useBigCharsMcap": "Gebruik grote tekens bij market cap",
"fetchEuroPrice": "Toon € prijs",
"screens": "Schermen",
"hostnamePrefix": "Hostnaam voorvoegsel",
"mempoolnstance": "Mempool instantie",
"otaUpdates": "OTA updates"
},
"control": {
"systemInfo": "Systeeminformatie",
"version": "Versie",
"buildTime": "Bouwtijd",
"setColor": "Kleur instellen",
"turnOff": "Uitzetten",
"ledColor": "LED kleur",
"showText": "Toon tekst",
"text": "Tekst",
"title": "Besturing"
},
"status": {
"title": "Status",
"memoryFree": "Geheugen vrij",
"screenCycle": "Scherm cyclus",
"wsPriceConnection": "WS Prijs verbinding",
"wsMempoolConnection": "WS Mempool.space verbinding",
"fetchEuroNote": "Wanneer je \"Toon € prijs\" aanzet, zal de prijsverbinding als ❌ verbroken getoond worden vanwege het gebruik van een andere bron.",
"uptime": "Uptime"
}
},
"colors": {
"black": "Zwart",
"white": "Wit"
},
"time": {
"minutes": "minuten",
"seconds": "seconden"
},
"restartRequired": "herstart nodig",
"button": {
"save": "Opslaan",
"reset": "Herstel",
"restart": "Herstart",
"forceFullRefresh": "Forceer scherm verversen"
},
"timer": {
"running": "actief",
"stopped": "gestopt"
},
"sections": {
"control": {
"keepSameColor": "Behoud zelfde kleur"
}
}
"section": {
"settings": {
"title": "Instellingen",
"textColor": "Tekstkleur",
"backgroundColor": "Achtergrondkleur",
"timeBetweenPriceUpdates": "Tijd tussen prijs updates",
"timezoneOffset": "Tijdzone afwijking",
"ledBrightness": "LED helderheid",
"timePerScreen": "Tijd per scherm",
"fullRefreshEvery": "Volledig verversen elke",
"shortAmountsWarning": "Lage waardes verkorten levensduur",
"tzOffsetHelpText": "Herstart nodig voor toepassen afwijking.",
"enableMdns": "mDNS",
"ledPowerOnTest": "LED test bij aanzetten",
"StealFocusOnNewBlock": "Pak aandacht bij nieuw blok",
"ledFlashOnBlock": "Knipper led bij nieuw blok",
"useBigCharsMcap": "Gebruik grote tekens bij market cap",
"fetchEuroPrice": "Toon € prijs",
"screens": "Schermen",
"hostnamePrefix": "Hostnaam voorvoegsel",
"mempoolnstance": "Mempool instantie",
"otaUpdates": "OTA updates"
},
"control": {
"systemInfo": "Systeeminformatie",
"version": "Versie",
"buildTime": "Bouwtijd",
"setColor": "Kleur instellen",
"turnOff": "Uitzetten",
"ledColor": "LED kleur",
"showText": "Toon tekst",
"text": "Tekst",
"title": "Besturing"
},
"status": {
"title": "Status",
"memoryFree": "Geheugen vrij",
"screenCycle": "Scherm cyclus",
"wsPriceConnection": "WS Prijs verbinding",
"wsMempoolConnection": "WS Mempool.space verbinding",
"fetchEuroNote": "Wanneer je \"Toon € prijs\" aanzet, zal de prijsverbinding als ❌ verbroken getoond worden vanwege het gebruik van een andere bron.",
"uptime": "Uptime"
}
},
"colors": {
"black": "Zwart",
"white": "Wit"
},
"time": {
"minutes": "minuten",
"seconds": "seconden"
},
"restartRequired": "herstart nodig",
"button": {
"save": "Opslaan",
"reset": "Herstel",
"restart": "Herstart",
"forceFullRefresh": "Forceer scherm verversen"
},
"timer": {
"running": "actief",
"stopped": "gestopt"
},
"sections": {
"control": {
"keepSameColor": "Behoud zelfde kleur"
}
}
}

View File

@ -1,193 +1,197 @@
@import "../node_modules/bootstrap/scss/functions";
@import "../node_modules/bootstrap/scss/variables";
@import "../node_modules/bootstrap/scss/variables-dark";
@import '../node_modules/bootstrap/scss/functions';
@import '../node_modules/bootstrap/scss/variables';
@import '../node_modules/bootstrap/scss/variables-dark';
//@import "@fontsource/antonio/latin-400.css";
@import "@fontsource/ubuntu/latin-400.css";
@import "@fontsource/oswald/latin-400.css";
@import '@fontsource/ubuntu/latin-400.css';
@import '@fontsource/oswald/latin-400.css';
$form-range-track-bg: #fff;
$color-mode-type: media-query;
$font-family-base: "Ubuntu";
$font-family-base: 'Ubuntu';
$font-size-base: 0.9rem;
//$font-size-sm: $font-size-base * .875 !default;
//$form-label-font-size: $font-size-base * .575 !default;
//$input-btn-font-size-sm: 0.4rem;
//$form-label-font-size: 0.4rem;
$input-font-size-sm: $font-size-base * .875;
$input-font-size-sm: $font-size-base * 0.875;
// $border-radius: .675rem;
@import "../node_modules/bootstrap/scss/mixins";
@import "../node_modules/bootstrap/scss/maps";
@import "../node_modules/bootstrap/scss/utilities";
@import '../node_modules/bootstrap/scss/mixins';
@import '../node_modules/bootstrap/scss/maps';
@import '../node_modules/bootstrap/scss/utilities';
@import "../node_modules/bootstrap/scss/root";
@import "../node_modules/bootstrap/scss/reboot";
@import "../node_modules/bootstrap/scss/type";
@import "../node_modules/bootstrap/scss/containers";
@import "../node_modules/bootstrap/scss/grid";
@import "../node_modules/bootstrap/scss/forms";
@import "../node_modules/bootstrap/scss/buttons";
@import "../node_modules/bootstrap/scss/button-group";
@import "../node_modules/bootstrap/scss/pagination";
@import '../node_modules/bootstrap/scss/root';
@import '../node_modules/bootstrap/scss/reboot';
@import '../node_modules/bootstrap/scss/type';
@import '../node_modules/bootstrap/scss/containers';
@import '../node_modules/bootstrap/scss/grid';
@import '../node_modules/bootstrap/scss/forms';
@import '../node_modules/bootstrap/scss/buttons';
@import '../node_modules/bootstrap/scss/button-group';
@import '../node_modules/bootstrap/scss/pagination';
@import "../node_modules/bootstrap/scss/dropdown";
@import '../node_modules/bootstrap/scss/dropdown';
@import "../node_modules/bootstrap/scss/navbar";
@import "../node_modules/bootstrap/scss/nav";
@import "../node_modules/bootstrap/scss/card";
@import "../node_modules/bootstrap/scss/progress";
@import '../node_modules/bootstrap/scss/navbar';
@import '../node_modules/bootstrap/scss/nav';
@import '../node_modules/bootstrap/scss/card';
@import '../node_modules/bootstrap/scss/progress';
@import "../node_modules/bootstrap/scss/helpers";
@import "../node_modules/bootstrap/scss/utilities/api";
@import '../node_modules/bootstrap/scss/helpers';
@import '../node_modules/bootstrap/scss/utilities/api';
@include media-breakpoint-down(xl) {
html {
font-size: 85%;
}
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%;
}
html {
font-size: 75%;
}
}
nav {
margin-bottom: 15px;
margin-bottom: 15px;
}
.splitText div:first-child::after {
display: block;
content: '';
margin-top: 0px;
border-bottom: 2px solid;
margin-bottom: 3px;
display: block;
content: '';
margin-top: 0px;
border-bottom: 2px solid;
margin-bottom: 3px;
}
#btcclock-wrapper {
margin: 0 auto;
margin: 0 auto;
}
.btclock {
border: 1px solid darkgray;
background: #000;
border-radius: 5px;
padding: 10px;
max-width: 700px;
margin: 0 auto;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: space-between;
align-items: center;
align-content: stretch;
font-family: 'Oswald', sans-serif;
border: 1px solid darkgray;
background: #000;
border-radius: 5px;
padding: 10px;
max-width: 700px;
margin: 0 auto;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: space-between;
align-items: center;
align-content: stretch;
font-family: 'Oswald', sans-serif;
>div {
padding: 5px;
}
> div {
padding: 5px;
}
.digit,
.splitText,
.mediumText {
border: 2px solid gold;
border-radius: 8px;
.digit,
.splitText,
.mediumText {
border: 2px solid gold;
border-radius: 8px;
@include media-breakpoint-up(sm) {
min-width: 10px;
}
@include media-breakpoint-up(sm) {
min-width: 10px;
}
@include media-breakpoint-up(xxl) {
min-width: 70px;
}
@include media-breakpoint-up(xxl) {
min-width: 70px;
}
text-align: center;
color: #fff;
}
text-align: center;
color: #fff;
}
}
.darkMode .btclock>div {
color: #fff;
border-color: #fff;
.darkMode .btclock > div {
color: #fff;
border-color: #fff;
}
.lightMode .btclock>div {
background: #fff;
.lightMode .btclock > div {
background: #fff;
}
.lightMode .btclock>div {
color: #000;
.lightMode .btclock > div {
color: #000;
}
.darkMode .btclock>div {
background: #000;
.darkMode .btclock > div {
background: #000;
}
.splitText {
@include media-breakpoint-up(sm) {
font-size: 1.0rem;
padding-top: 8px !important;
padding-bottom: 9px !important;
}
@include media-breakpoint-up(xxl) {
font-size: 1.8rem;
padding-top: 19px !important;
padding-bottom: 20px !important;
}
@include media-breakpoint-up(sm) {
font-size: 1rem;
padding-top: 8px !important;
padding-bottom: 9px !important;
}
@include media-breakpoint-up(xxl) {
font-size: 1.8rem;
padding-top: 19px !important;
padding-bottom: 20px !important;
}
text-align: center;
text-align: center;
}
.mediumText {
font-size: 3rem;
padding-left: 5px;
padding-right: 5px;
padding-top: 20px !important;
padding-bottom: 20px !important;
font-size: 3rem;
padding-left: 5px;
padding-right: 5px;
padding-top: 20px !important;
padding-bottom: 20px !important;
}
.digit {
font-size: 5rem;
@include media-breakpoint-up(sm) {
font-size: 2.5rem;
}
@include media-breakpoint-up(xxl) {
font-size: 5rem;
}
padding-left: 10px;
padding-right: 10px;
font-size: 5rem;
@include media-breakpoint-up(sm) {
font-size: 2.5rem;
}
@include media-breakpoint-up(xxl) {
font-size: 5rem;
}
padding-left: 10px;
padding-right: 10px;
}
.digit-blank {
content: "abc";
content: 'abc';
}
#customText {
text-transform: uppercase;
text-transform: uppercase;
}
.system_info {
padding: 0;
padding: 0;
li {
list-style: none;
}
li {
list-style: none;
}
}
.card-title {
margin-bottom: 0;
margin-bottom: 0;
}
.navbar-brand {
font-style: italic;
font-weight: 600;
}
font-style: italic;
font-weight: 600;
}

View File

@ -1,21 +1,19 @@
<script lang="ts">
import {
Navbar,
NavbarBrand,
Collapse,
Dropdown,
DropdownItem,
DropdownMenu,
DropdownToggle,
Nav,
NavItem,
NavLink,
Collapse,
Dropdown,
DropdownMenu,
DropdownItem,
DropdownToggle
Navbar,
NavbarBrand
} from 'sveltestrap';
import { locale, locales, waitLocale } from 'svelte-i18n';
import type { LayoutLoad } from './$types';
import { browser } from '$app/environment';
import { page } from '$app/stores';
import { locale, locales } from 'svelte-i18n';
export const setLocale = (lang: string) => () => {
locale.set(lang);
@ -33,7 +31,7 @@
const lowercaseCode = languageCode.toLowerCase();
// Check if the language code is in the flagMap
if (flagMap.hasOwnProperty(lowercaseCode)) {
if (Object.prototype.hasOwnProperty.call(flagMap, lowercaseCode)) {
return flagMap[lowercaseCode];
} else {
// Return null for unsupported language codes
@ -50,7 +48,7 @@
const lowercaseCode = languageCode.toLowerCase();
return languageNames.hasOwnProperty(lowercaseCode)
return Object.prototype.hasOwnProperty.call(languageNames, lowercaseCode)
? languageNames[lowercaseCode][lowercaseCode]
: null;
};
@ -71,7 +69,9 @@
<DropdownToggle nav caret>{getFlagEmoji($locale)} {getLanguageName($locale)}</DropdownToggle>
<DropdownMenu end>
{#each $locales as locale}
<DropdownItem on:click={setLocale(locale)}>{getFlagEmoji(locale)} {getLanguageName(locale)}</DropdownItem>
<DropdownItem on:click={setLocale(locale)}
>{getFlagEmoji(locale)} {getLanguageName(locale)}</DropdownItem
>
{/each}
</DropdownMenu>
</Dropdown>

View File

@ -1,20 +1,19 @@
import "$lib/style/app.scss";
import '$lib/style/app.scss';
import { browser } from '$app/environment'
import '$lib/i18n' // Import to initialize. Important :)
import { locale, waitLocale } from 'svelte-i18n'
import type { LayoutLoad } from './$types'
import { browser } from '$app/environment';
import '$lib/i18n'; // Import to initialize. Important :)
import { locale, waitLocale } from 'svelte-i18n';
import type { LayoutLoad } from './$types';
export const load: LayoutLoad = async () => {
if (browser && localStorage.getItem('locale')) {
locale.set(localStorage.getItem('locale'));
} else if (browser) {
locale.set(window.navigator.language)
if (browser && localStorage.getItem('locale')) {
locale.set(localStorage.getItem('locale'));
} else if (browser) {
locale.set(window.navigator.language);
}
await waitLocale();
}
await waitLocale();
};
export const prerender = true;
export const ssr = false;
export const csr = true;
export const csr = true;

View File

@ -1,45 +1,44 @@
<script lang="ts">
import { PUBLIC_BASE_URL } from '$env/static/public';
import { PUBLIC_BASE_URL } from '$env/static/public';
import { _ } from 'svelte-i18n';
import { Col, Container, Row } from 'sveltestrap';
import { Container, Row } from 'sveltestrap';
import Control from './Control.svelte';
import Status from './Status.svelte';
import Settings from './Settings.svelte';
import { writable } from 'svelte/store';
import { onMount } from 'svelte';
import { writable } from 'svelte/store';
import Control from './Control.svelte';
import Settings from './Settings.svelte';
import Status from './Status.svelte';
let settings = writable({
fgColor: "0"
});
fgColor: '0'
});
let status = writable({
data: ["L", "O", "A", "D", "I", "N", "G"],
espFreeHeap: 0,
espHeapSize: 0,
connectionStatus: {
"price": false,
"blocks": false
},
data: ['L', 'O', 'A', 'D', 'I', 'N', 'G'],
espFreeHeap: 0,
espHeapSize: 0,
connectionStatus: {
price: false,
blocks: false
},
leds: []
});
});
onMount(() => {
fetch( PUBLIC_BASE_URL + `/api/settings`)
fetch(PUBLIC_BASE_URL + `/api/settings`)
.then((res) => res.json())
.then((data) => {
data.fgColor = String(data.fgColor);
data.bgColor = String(data.bgColor);
data.timePerScreen = data.timerSeconds / 60;
data.timePerScreen = data.timerSeconds / 60;
if (data.fgColor> 65535) {
data.fgColor = "65535";
}
if (data.fgColor > 65535) {
data.fgColor = '65535';
}
if (data.bgColor> 65535) {
data.bgColor = "65535";
}
if (data.bgColor > 65535) {
data.bgColor = '65535';
}
settings.set(data);
});
@ -49,12 +48,12 @@
status.set(data);
});
const evtSource = new EventSource(`${PUBLIC_BASE_URL}/events`);
const evtSource = new EventSource(`${PUBLIC_BASE_URL}/events`);
evtSource.addEventListener('status', (e) => {
let dataObj = (JSON.parse(e.data));
status.set(dataObj);
});
evtSource.addEventListener('status', (e) => {
let dataObj = JSON.parse(e.data);
status.set(dataObj);
});
});
</script>
@ -65,7 +64,7 @@
<Container fluid>
<Row>
<Control bind:settings bind:status></Control>
<Status bind:settings bind:status></Status>
<Status bind:settings bind:status></Status>
<Settings bind:settings></Settings>
</Row>
</Container>

View File

@ -1,82 +1,78 @@
<script lang="ts">
import { PUBLIC_BASE_URL } from '$env/static/public';
import { PUBLIC_BASE_URL } from '$env/static/public';
import { onDestroy } from 'svelte';
import { _ } from 'svelte-i18n';
import type { Subscriber, Unsubscriber } from 'svelte/motion';
import { _ } from 'svelte-i18n';
import type { Writable } from 'svelte/store';
import {
Button,
ButtonGroup,
Card,
CardTitle,
CardBody,
CardHeader,
Col,
Container,
Form,
Input,
Label,
Row
} from 'sveltestrap';
import {
Button,
Card,
CardBody,
CardHeader,
CardTitle,
Col,
Form,
Input,
Label,
Row
} from 'sveltestrap';
export let settings = {};
export let customText:String;
export let ledColor:String = "#FFCC00";
export let status:Writable<{leds:[]}>;
let ledStatus = [];
let keepLedsSameColor = false;
export let settings = {};
export let customText: string;
export let ledColor: string = '#FFCC00';
export let status: Writable<{ leds: [] }>;
let ledStatus = [];
let keepLedsSameColor = false;
const setCustomText = () => {
fetch(`${PUBLIC_BASE_URL}/api/show/text/${customText}`).catch(err => { });
};
const setCustomText = () => {
fetch(`${PUBLIC_BASE_URL}/api/show/text/${customText}`).catch(() => {});
};
const checkSyncLeds = (e:Event) => {
console.log('checksyncleds', keepLedsSameColor);
if (keepLedsSameColor) {
console.log(e.target.value);
const checkSyncLeds = (e: Event) => {
console.log('checksyncleds', keepLedsSameColor);
if (keepLedsSameColor) {
console.log(e.target.value);
ledStatus.forEach((element, i) => {
if (ledStatus[i].hex != e.target_value) {
ledStatus[i].hex = e.target.value;
}
});
}
}
ledStatus.forEach((element, i) => {
if (ledStatus[i].hex != e.target_value) {
ledStatus[i].hex = e.target.value;
}
});
}
};
const setLEDcolor = () => {
console.log(`${PUBLIC_BASE_URL}/api/lights/${ledColor}`);
fetch(`${PUBLIC_BASE_URL}/api/lights`, {
headers: {
'Content-Type': 'application/json'
},
method: 'PATCH',
body: JSON.stringify(ledStatus)
}
).catch(err => { });
};
const setLEDcolor = () => {
console.log(`${PUBLIC_BASE_URL}/api/lights/${ledColor}`);
fetch(`${PUBLIC_BASE_URL}/api/lights`, {
headers: {
'Content-Type': 'application/json'
},
method: 'PATCH',
body: JSON.stringify(ledStatus)
}).catch(() => {});
};
const turnOffLeds = () => {
fetch(`${PUBLIC_BASE_URL}/api/lights/off`).catch(err => { });
};
const turnOffLeds = () => {
fetch(`${PUBLIC_BASE_URL}/api/lights/off`).catch(() => {});
};
const restartClock = () => {
fetch(`${PUBLIC_BASE_URL}/api/restart`).catch(err => { });
}
const restartClock = () => {
fetch(`${PUBLIC_BASE_URL}/api/restart`).catch(() => {});
};
const forceFullRefresh = () => {
fetch(`${PUBLIC_BASE_URL}/api/full_refresh`).catch(err => { });
}
const forceFullRefresh = () => {
fetch(`${PUBLIC_BASE_URL}/api/full_refresh`).catch(() => {});
};
let firstLedDataSubscription = () => {};
let firstLedDataSubscription = () => {};
firstLedDataSubscription = status.subscribe(async(val) => {
if (val && val.leds) {
ledStatus = val.leds.map((obj) => ({ ["hex"]: obj["hex"] }));
firstLedDataSubscription();
}
})
firstLedDataSubscription = status.subscribe(async (val) => {
if (val && val.leds) {
ledStatus = val.leds.map((obj) => ({ ['hex']: obj['hex'] }));
firstLedDataSubscription();
}
});
onDestroy(firstLedDataSubscription);
onDestroy(firstLedDataSubscription);
</script>
<Col>
@ -87,48 +83,72 @@ onDestroy(firstLedDataSubscription);
<CardBody>
<Form>
<Row>
<Label md={6} for="customText">{ $_('section.control.text') }</Label>
<Label md={6} for="customText">{$_('section.control.text')}</Label>
<Col md="6">
<Input type="text" id="customText" bind:value={customText} bsSize="sm" maxLength="{$settings.numScreens}" />
<Input
type="text"
id="customText"
bind:value={customText}
bsSize="sm"
maxLength={$settings.numScreens}
/>
</Col>
</Row>
<Button color="primary" on:click={setCustomText}>{ $_('section.control.showText') }</Button>
<Button color="primary" on:click={setCustomText}>{$_('section.control.showText')}</Button>
</Form>
<hr />
<h3>LEDs</h3>
<Form>
<Form>
<Row>
<Label md={6} for="ledColorPicker" size="sm">{ $_('section.control.ledColor') }</Label>
<Label md={6} for="ledColorPicker" size="sm">{$_('section.control.ledColor')}</Label>
<Col md="6">
<Row class="justify-content-between">
{#if ledStatus}
{#each ledStatus as led, i }
<Col>
<Input type="color" id="ledColorPicker[{i}]" bind:value="{led.hex}" class="mx-auto" on:change="{checkSyncLeds}" />
</Col>
{/each}
{#each ledStatus as led, i}
<Col>
<Input
type="color"
id="ledColorPicker[{i}]"
bind:value={led.hex}
class="mx-auto"
on:change={checkSyncLeds}
/>
</Col>
{/each}
{/if}
<Col>
<Input bind:checked={keepLedsSameColor} type="switch" class="mx-auto" label="{ $_('sections.control.keepSameColor') }" />
<Input
bind:checked={keepLedsSameColor}
type="switch"
class="mx-auto"
label={$_('sections.control.keepSameColor')}
/>
</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>
<Button color="secondary" id="turnOffLedsBtn" on:click={turnOffLeds}
>{$_('section.control.turnOff')}</Button
>
<Button color="primary" on:click={setLEDcolor}>{$_('section.control.setColor')}</Button>
</Form>
<hr />
<h3>{ $_('section.control.systemInfo') }</h3>
<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>{$_('section.control.version')}: {$settings.gitRev}</li>
<li>
{$_('section.control.buildTime')}: {new Date(
$settings.lastBuildTime * 1000
).toLocaleString()}
</li>
<li>IP: {$settings.ip}</li>
<li>{ $_('section.control.hostname') }: {$settings.hostname}</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>
<Button color="danger" id="restartBtn" on:click={restartClock}>{$_('button.restart')}</Button>
<Button color="warning" id="forceFullRefresh" on:click={forceFullRefresh}
>{$_('button.forceFullRefresh')}</Button
>
</CardBody>
</Card>
</Col>

View File

@ -1,25 +1,25 @@
<script lang="ts">
export let status = {};
export let status = {};
const isSplitText = (str:String) => {
return str.includes("/");
}
const isSplitText = (str: string) => {
return str.includes('/');
};
</script>
<div class="btcclock-wrapper" id="btcclock-wrapper">
<div class="btclock">
{#each status.data as char}
{#if isSplitText(char)}
<div class="splitText">
{#each char.split("/") as part}
<div class="flex-items">{part}</div>
{/each}
</div>
{:else if char.length === 0 || char === " "}
<div class="digit">&nbsp;&nbsp;</div>
{:else}
<div class="digit">{char}</div>
{/if}
{/each}
</div>
</div>
<div class="btclock">
{#each status.data as char}
{#if isSplitText(char)}
<div class="splitText">
{#each char.split('/') as part}
<div class="flex-items">{part}</div>
{/each}
</div>
{:else if char.length === 0 || char === ' '}
<div class="digit">&nbsp;&nbsp;</div>
{:else}
<div class="digit">{char}</div>
{/if}
{/each}
</div>
</div>

View File

@ -1,46 +1,41 @@
<script lang="ts">
import { PUBLIC_BASE_URL } from '$env/static/public';
import { onMount } from 'svelte';
import { readonly, writable } from 'svelte/store';
import { PUBLIC_BASE_URL } from '$env/static/public';
import { _ } from 'svelte-i18n';
import {
Col,
Container,
Row,
Button,
Card,
CardTitle,
CardHeader,
CardBody,
CardHeader,
CardTitle,
Col,
Form,
FormGroup,
FormText,
Label,
Input,
InputGroup,
InputGroupText,
Button
Label,
Row
} from 'sveltestrap';
export let settings;
const onSave = async(e:Event) => {
e.preventDefault();
let formSettings = $settings;
const onSave = async (e: Event) => {
e.preventDefault();
let formSettings = $settings;
delete formSettings["gitRev"];
delete formSettings["ip"];
delete formSettings["lastBuildTime"];
delete formSettings['gitRev'];
delete formSettings['ip'];
delete formSettings['lastBuildTime'];
const res = await fetch(`${PUBLIC_BASE_URL}/api/json/settings`, {
await fetch(`${PUBLIC_BASE_URL}/api/json/settings`, {
method: 'PATCH',
headers: {
"Content-Type": "application/json",
},
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formSettings)
})
}
});
};
</script>
<Col>
@ -51,7 +46,9 @@
<CardBody>
<Form on:submit={onSave}>
<Row>
<Label md={6} for="fgColor" size="sm">{$_('section.settings.textColor', { default: 'Text color' })}</Label>
<Label md={6} for="fgColor" size="sm"
>{$_('section.settings.textColor', { default: 'Text color' })}</Label
>
<Col md="6">
<Input
type="select"
@ -61,13 +58,13 @@
bsSize="sm"
class="form-select-sm"
>
<option value="0">{ $_('colors.black') }</option>
<option value="65535">{ $_('colors.white') }</option>
<option value="0">{$_('colors.black')}</option>
<option value="65535">{$_('colors.white')}</option>
</Input>
</Col>
</Row>
<Row>
<Label md={6} for="bgColor" size="sm">{ $_('section.settings.backgroundColor') }</Label>
<Label md={6} for="bgColor" size="sm">{$_('section.settings.backgroundColor')}</Label>
<Col md="6">
<Input
type="select"
@ -77,41 +74,45 @@
bsSize="sm"
class="form-select-sm"
>
<option value="0">{ $_('colors.black') }</option>
<option value="65535">{ $_('colors.white') }</option>
<option value="0">{$_('colors.black')}</option>
<option value="65535">{$_('colors.white')}</option>
</Input>
</Col>
</Row>
<Row>
<Label md={6} for="timePerScreen" size="sm">{ $_('section.settings.timePerScreen') }</Label>
<Label md={6} for="timePerScreen" size="sm">{$_('section.settings.timePerScreen')}</Label>
<Col md="6">
<InputGroup size="sm">
<Input type="number" min={1} step="1" bind:value={$settings.timePerScreen} />
<InputGroupText>{ $_('time.minutes') }</InputGroupText>
<InputGroupText>{$_('time.minutes')}</InputGroupText>
</InputGroup>
</Col>
</Row>
<Row>
<Label md={6} for="fullRefreshMin" size="sm">{ $_('section.settings.fullRefreshEvery') }</Label>
<Label md={6} for="fullRefreshMin" size="sm"
>{$_('section.settings.fullRefreshEvery')}</Label
>
<Col md="6">
<InputGroup size="sm">
<Input type="number" min={1} step="1" bind:value={$settings.fullRefreshMin} />
<InputGroupText>{ $_('time.minutes') }</InputGroupText>
<InputGroupText>{$_('time.minutes')}</InputGroupText>
</InputGroup>
</Col>
</Row>
<Row>
<Label md={6} for="minSecPriceUpd" size="sm">{ $_('section.settings.timeBetweenPriceUpdates') }</Label>
<Label md={6} for="minSecPriceUpd" size="sm"
>{$_('section.settings.timeBetweenPriceUpdates')}</Label
>
<Col md="6">
<InputGroup size="sm">
<Input type="number" min={1} step="1" bind:value={$settings.minSecPriceUpd} />
<InputGroupText>{ $_('time.seconds') }</InputGroupText>
<InputGroupText>{$_('time.seconds')}</InputGroupText>
</InputGroup>
<FormText>{ $_('section.settings.shortAmountsWarning') }</FormText>
<FormText>{$_('section.settings.shortAmountsWarning')}</FormText>
</Col>
</Row>
<Row>
<Label md={6} for="tzOffset" size="sm">{ $_('section.settings.timezoneOffset') }</Label>
<Label md={6} for="tzOffset" size="sm">{$_('section.settings.timezoneOffset')}</Label>
<Col md="6">
<InputGroup size="sm">
<Input
@ -122,19 +123,19 @@
id="tzOffset"
bind:value={$settings.tzOffset}
/>
<InputGroupText>{ $_('time.minutes') }</InputGroupText>
<InputGroupText>{$_('time.minutes')}</InputGroupText>
</InputGroup>
<FormText>{ $_('section.settings.tzOffsetHelpText') }</FormText>
<FormText>{$_('section.settings.tzOffsetHelpText')}</FormText>
</Col>
</Row>
<Row>
<Label md={6} for="ledBrightness" size="sm">{ $_('section.settings.ledBrightness') }</Label>
<Label md={6} for="ledBrightness" size="sm">{$_('section.settings.ledBrightness')}</Label>
<Col md="6">
<Input
type="range"
name="ledBrightness"
id="ledBrightness"
bind:value={$settings.ledBrightness}
bind:value={$settings.ledBrightness}
min={0}
max={255}
step={1}
@ -142,67 +143,117 @@
</Col>
</Row>
<Row>
<Label md={6} for="mempoolInstance" size="sm">{ $_('section.settings.mempoolnstance') }</Label>
<Col md="6">
<Label md={6} for="mempoolInstance" size="sm"
>{$_('section.settings.mempoolnstance')}</Label
>
<Col md="6">
<Input
type="text"
bind:value={$settings.mempoolInstance}
name="mempoolInstance"
id="mempoolInstance"
bsSize="sm"
>
</Input>
></Input>
</Col>
</Row>
<Row>
<Label md={6} for="hostnamePrefix" size="sm">{ $_('section.settings.hostnamePrefix') }</Label>
<Col md="6">
<Label md={6} for="hostnamePrefix" size="sm"
>{$_('section.settings.hostnamePrefix')}</Label
>
<Col md="6">
<Input
type="text"
bind:value={$settings.hostnamePrefix}
name="hostnamePrefix"
id="hostnamePrefix"
bsSize="sm"
>
</Input>
></Input>
</Col>
</Row>
<Row>
<Col md="6">
<Input
id="ledTestOnPower"
bind:checked={$settings.ledTestOnPower}
type="switch"
bsSize="sm"
label={$_('section.settings.ledPowerOnTest')}
/>
</Col>
<Col md="6">
<Input
id="ledFlashOnUpd"
bind:checked={$settings.ledFlashOnUpd}
type="switch"
bsSize="sm"
label={$_('section.settings.ledFlashOnBlock')}
/>
</Col>
<Col md="6">
<Input
id="stealFocus"
bind:checked={$settings.stealFocus}
type="switch"
bsSize="sm"
label={$_('section.settings.StealFocusOnNewBlock')}
/>
</Col>
<Col md="6">
<Input
id="mcapBigChar"
bind:checked={$settings.mcapBigChar}
type="switch"
bsSize="sm"
label={$_('section.settings.useBigCharsMcap')}
/>
</Col>
<Col md="6">
<Input
id="otaEnabled"
bind:checked={$settings.otaEnabled}
type="switch"
bsSize="sm"
label="{$_('section.settings.otaUpdates')} ({$_('restartRequired')})"
/>
</Col>
<Col md="6">
<Input
id="mdnsEnabled"
bind:checked={$settings.mdnsEnabled}
type="switch"
bsSize="sm"
label="{$_('section.settings.enableMdns')} ({$_('restartRequired')})"
/>
</Col>
<Col md="6">
<Input
id="fetchEurPrice"
bind:checked={$settings.fetchEurPrice}
type="switch"
bsSize="sm"
label="{$_('section.settings.fetchEuroPrice')} ({$_('restartRequired')})"
/>
</Col>
</Row>
<Row>
<Col md="6">
<Input id="ledTestOnPower" bind:checked={$settings.ledTestOnPower} type="switch" bsSize="sm" label="{ $_('section.settings.ledPowerOnTest') }" />
</Col>
<Col md="6">
<Input id="ledFlashOnUpd" bind:checked={$settings.ledFlashOnUpd} type="switch" bsSize="sm" label="{ $_('section.settings.ledFlashOnBlock') }" />
</Col>
<Col md="6">
<Input id="stealFocus" bind:checked={$settings.stealFocus} type="switch" bsSize="sm" label="{ $_('section.settings.StealFocusOnNewBlock') }" />
</Col>
<Col md="6">
<Input id="mcapBigChar" bind:checked={$settings.mcapBigChar} type="switch" bsSize="sm" label="{ $_('section.settings.useBigCharsMcap') }" />
</Col>
<Col md="6">
<Input id="otaEnabled" bind:checked={$settings.otaEnabled} type="switch" bsSize="sm" label="{ $_('section.settings.otaUpdates') } ({ $_('restartRequired') })" />
</Col>
<Col md="6">
<Input id="mdnsEnabled" bind:checked={$settings.mdnsEnabled} type="switch" bsSize="sm" label="{ $_('section.settings.enableMdns') } ({ $_('restartRequired') })" />
</Col>
<Col md="6">
<Input id="fetchEurPrice" bind:checked={$settings.fetchEurPrice} type="switch" bsSize="sm" label="{ $_('section.settings.fetchEuroPrice') } ({ $_('restartRequired') })" />
</Col>
</Row>
<Row>
<h3>{ $_('section.settings.screens') }</h3>
{#if $settings.screens}
{#each $settings.screens as s}
<Col md="6">
<Input id="screens_{s.id}" bind:checked={s.enabled} type="switch" bsSize="sm" label="{s.name}" />
</Col>
{/each}
{/if}
</Row>
<Button type="reset" color="secondary">{ $_('button.reset') }</Button>
<Button color="primary">{ $_('button.save') }</Button>
<Row>
<h3>{$_('section.settings.screens')}</h3>
{#if $settings.screens}
{#each $settings.screens as s}
<Col md="6">
<Input
id="screens_{s.id}"
bind:checked={s.enabled}
type="switch"
bsSize="sm"
label={s.name}
/>
</Col>
{/each}
{/if}
</Row>
<Button type="reset" color="secondary">{$_('button.reset')}</Button>
<Button color="primary">{$_('button.save')}</Button>
</Form>
</CardBody>
</Card>

View File

@ -1,65 +1,71 @@
<script lang="ts">
import { PUBLIC_BASE_URL } from '$env/static/public';
import { PUBLIC_BASE_URL } from '$env/static/public';
import { onMount } from 'svelte';
import { _ } from 'svelte-i18n';
import { writable, type Writable } from 'svelte/store';
import { Row, Input, Button, ButtonGroup, Card, CardBody, CardHeader, Col, Progress,CardTitle } from 'sveltestrap';
import { writable } from 'svelte/store';
import {
Button,
ButtonGroup,
Card,
CardBody,
CardHeader,
CardTitle,
Col,
Input,
Progress,
Row
} from 'sveltestrap';
import Rendered from './Rendered.svelte';
export let settings;
export let status:Writable<{}>;
const toTime = (secs:Number) => {
var hours = Math.floor(secs / (60 * 60));
export let settings;
export let status: writable<object>;
var divisor_for_minutes = secs % (60 * 60);
var minutes = Math.floor(divisor_for_minutes / 60);
const toTime = (secs: number) => {
var hours = Math.floor(secs / (60 * 60));
var divisor_for_seconds = divisor_for_minutes % 60;
var seconds = Math.ceil(divisor_for_seconds);
var divisor_for_minutes = secs % (60 * 60);
var minutes = Math.floor(divisor_for_minutes / 60);
var obj = {
"h": hours,
"m": minutes,
"s": seconds
};
return obj;
}
var divisor_for_seconds = divisor_for_minutes % 60;
var seconds = Math.ceil(divisor_for_seconds);
const toUptimeString = (secs:Number):String => {
let time = toTime(secs);
var obj = {
h: hours,
m: minutes,
s: seconds
};
return obj;
};
return `${time.h}h ${time.m}m ${time.s}s`;
}
const toUptimestring = (secs: number): string => {
let time = toTime(secs);
let memoryFreePercent:number = 50;
let lightMode:boolean = false;
return `${time.h}h ${time.m}m ${time.s}s`;
};
status.subscribe((value: {}) => {
memoryFreePercent = Math.round(value.espFreeHeap / value.espHeapSize * 100);
});
let memoryFreePercent: number = 50;
let lightMode: boolean = false;
settings.subscribe((value: {}) => {
lightMode = value.bgColor > value.fgColor;
});
status.subscribe((value: object) => {
memoryFreePercent = Math.round((value.espFreeHeap / value.espHeapSize) * 100);
});
const setScreen = (id:number) => () => {
fetch(`${PUBLIC_BASE_URL}/api/show/screen/${id}`).catch(err => { });
}
settings.subscribe((value: object) => {
lightMode = value.bgColor > value.fgColor;
});
const toggleTimer = (currentStatus:boolean) => () => {
if (currentStatus) {
fetch(`${PUBLIC_BASE_URL}/api/action/pause`);
} else {
fetch(`${PUBLIC_BASE_URL}/api/action/timer_restart`);
}
}
const isLightMode = () => {
return $settings.bgColor > $settings.fgColor;
}
const setScreen = (id: number) => () => {
fetch(`${PUBLIC_BASE_URL}/api/show/screen/${id}`).catch(() => {});
};
const toggleTimer = (currentStatus: boolean) => (e: Event) => {
e.preventDefault();
if (currentStatus) {
fetch(`${PUBLIC_BASE_URL}/api/action/pause`);
} else {
fetch(`${PUBLIC_BASE_URL}/api/action/timer_restart`);
}
};
</script>
<Col>
@ -68,64 +74,86 @@
<CardTitle>{$_('section.status.title', { default: 'Status' })}</CardTitle>
</CardHeader>
<CardBody>
{#if $settings.screens}
<div class="d-flex justify-content-center">
<ButtonGroup size="sm">
{#each $settings.screens as s}
<Button color="outline-primary" active={$status.currentScreen == s.id} on:click={setScreen(s.id)}>{s.name}</Button>
{/each}
</ButtonGroup>
</div>
<hr>
{#if $status.data}
<section class={lightMode ? 'lightMode': ''}>
<Rendered status="{$status}"></Rendered>
</section>
{ $_('section.status.screenCycle') }: <a style="cursor: pointer" on:click="{toggleTimer($status.timerRunning)}">{#if $status.timerRunning}&#9205; { $_('timer.running') }{:else}&#9208; { $_('timer.stopped') }{/if}</a>
{/if}
{/if}
<hr>
<Row class="justify-content-evenly">
{#if $status.leds}
{#each $status.leds as led}
<Col>
<Input type="color" id="ledColorPicker" bind:value="{led.hex}" class="mx-auto" disabled />
</Col>
{/each}
{/if}
</Row>
<hr>
<Progress striped value={memoryFreePercent}>{ memoryFreePercent }%</Progress>
<div class="d-flex justify-content-between">
<div>{ $_('section.status.memoryFree') } </div>
<div>{ Math.round($status.espFreeHeap / 1024) } / { Math.round($status.espHeapSize / 1024) } KiB</div>
</div>
<hr>
{ $_('section.status.uptime') }: {toUptimeString($status.espUptime)}
<br>
<p>
{ $_('section.status.wsPriceConnection') }:
<span>
{#if $status.connectionStatus && $status.connectionStatus.price}
&#9989;
{:else}
&#10060;
{/if}
</span>
-
{ $_('section.status.wsMempoolConnection') }:
<span>
{#if $status.connectionStatus && $status.connectionStatus.blocks}
&#9989;
{:else}
&#10060;
{/if}
</span><br>
{#if $settings.fetchEurPrice}
<small>{ $_('section.status.fetchEuroNote') }</small>
{/if}
</p>
</CardBody>
</Card>
{#if $settings.screens}
<div class="d-flex justify-content-center">
<ButtonGroup size="sm">
{#each $settings.screens as s}
<Button
color="outline-primary"
active={$status.currentScreen == s.id}
on:click={setScreen(s.id)}>{s.name}</Button
>
{/each}
</ButtonGroup>
</div>
<hr />
{#if $status.data}
<section class={lightMode ? 'lightMode' : ''}>
<Rendered status={$status}></Rendered>
</section>
{$_('section.status.screenCycle')}:
<a
href={'#'}
style="cursor: pointer"
tabindex="0"
role="button"
aria-pressed="false"
on:click={toggleTimer($status.timerRunning)}
>{#if $status.timerRunning}&#9205; {$_('timer.running')}{:else}&#9208; {$_(
'timer.stopped'
)}{/if}</a
>
{/if}
{/if}
<hr />
<Row class="justify-content-evenly">
{#if $status.leds}
{#each $status.leds as led}
<Col>
<Input
type="color"
id="ledColorPicker"
bind:value={led.hex}
class="mx-auto"
disabled
/>
</Col>
{/each}
{/if}
</Row>
<hr />
<Progress striped value={memoryFreePercent}>{memoryFreePercent}%</Progress>
<div class="d-flex justify-content-between">
<div>{$_('section.status.memoryFree')}</div>
<div>
{Math.round($status.espFreeHeap / 1024)} / {Math.round($status.espHeapSize / 1024)} KiB
</div>
</div>
<hr />
{$_('section.status.uptime')}: {toUptimestring($status.espUptime)}
<br />
<p>
{$_('section.status.wsPriceConnection')}:
<span>
{#if $status.connectionStatus && $status.connectionStatus.price}
&#9989;
{:else}
&#10060;
{/if}
</span>
-
{$_('section.status.wsMempoolConnection')}:
<span>
{#if $status.connectionStatus && $status.connectionStatus.blocks}
&#9989;
{:else}
&#10060;
{/if}
</span><br />
{#if $settings.fetchEurPrice}
<small>{$_('section.status.fetchEuroNote')}</small>
{/if}
</p>
</CardBody>
</Card>
</Col>

View File

@ -1,36 +1,31 @@
<script lang="ts">
import { _ } from 'svelte-i18n';
import { Col, Container, Row, Button } from 'sveltestrap';
import { Button, Container } from 'sveltestrap';
import { onMount } from 'svelte';
//import * as swaggerJson from '../../../static/swagger.json';
// import SwaggerUI from 'swagger-ui';
import 'swagger-ui/dist/swagger-ui.css';
let swaggerLoaded:boolean = false;
let swaggerLoaded: boolean = false;
onMount(async () => {
// @ts-ignore
loadSwagger();
});
const loadSwagger = () => {
if (!SwaggerUIBundle)
return;
// @ts-expect-error: SwaggerUIBundle is loaded from external script from CDN
if (!SwaggerUIBundle) return; // eslint-disable-line
swaggerLoaded = true;
// @ts-expect-error: SwaggerUIBundle is loaded from external script from CDN
// eslint-disable-next-line
window.ui = SwaggerUIBundle({
url: '/swagger.json',
dom_id: '#swagger-ui-container',
presets: [
// @ts-ignore
SwaggerUIBundle.presets.apis,
// @ts-ignore
SwaggerUIStandalonePreset
],
// layout: "StandaloneLayout",
});
}
url: '/swagger.json',
dom_id: '#swagger-ui-container',
presets: [
// @ts-expect-error: SwaggerUIBundle is loaded from external script from CDN
SwaggerUIBundle.presets.apis, // eslint-disable-line
// @ts-expect-error: SwaggerUIStandalonePreset is loaded from external script from CDN
SwaggerUIStandalonePreset // eslint-disable-line
]
// layout: "StandaloneLayout",
});
};
</script>
<svelte:head>
@ -54,11 +49,9 @@
crossorigin="anonymous"
referrerpolicy="no-referrer"
/>
</svelte:head>
<Container fluid class="bg-light">
<section class:invisible={swaggerLoaded}><Button on:click="{loadSwagger}">Load</Button></section>
<section class:invisible={swaggerLoaded}><Button on:click={loadSwagger}>Load</Button></section>
<div id="swagger-ui-container" />
</Container>

View File

@ -1,480 +1,448 @@
{
"openapi": "3.0.3",
"info": {
"title": "BTClock API",
"version": "3.0",
"description": "BTClock V3 API"
},
"servers": [
{
"url": "/api/"
}
],
"paths": {
"/status": {
"get": {
"tags": [
"system"
],
"summary": "Get current status",
"responses": {
"200": {
"description": "successful operation"
}
}
}
},
"/system_status": {
"get": {
"tags": [
"system"
],
"summary": "Get system status",
"responses": {
"200": {
"description": "successful operation"
}
}
}
},
"/settings": {
"get": {
"tags": [
"system"
],
"summary": "Get current settings",
"responses": {
"200": {
"description": "successful operation",
"content": {
"application/json": {
"schema": {
"$ref": null
}
}
}
}
}
},
"post": {
"tags": [
"system"
],
"summary": "Save current settings",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Settings"
}
}
}
},
"responses": {
"200": {
"description": "successful operation"
}
}
},
"patch": {
"tags": [
"system"
],
"summary": "Save current settings",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Settings"
}
}
}
},
"responses": {
"200": {
"description": "successful operation"
}
}
}
},
"/action/pause": {
"get": {
"tags": [
"timer"
],
"summary": "Pause screen rotation",
"responses": {
"200": {
"description": "successful operation"
}
}
}
},
"/action/timer_restart": {
"get": {
"tags": [
"timer"
],
"summary": "Restart screen rotation",
"responses": {
"200": {
"description": "successful operation"
}
}
}
},
"/show/screen/{id}": {
"get": {
"tags": [
"screens"
],
"summary": "Set screen to show",
"parameters": [
{
"in": "path",
"name": "id",
"schema": {
"type": "integer",
"default": 1
},
"required": true,
"description": "ID of screen to show"
}
],
"responses": {
"200": {
"description": "successful operation"
}
}
}
},
"/show/text/{text}": {
"get": {
"tags": [
"screens"
],
"summary": "Set text to show",
"parameters": [
{
"in": "path",
"name": "text",
"schema": {
"type": "string",
"default": "text"
},
"required": true,
"description": "Text to show"
}
],
"responses": {
"200": {
"description": "successful operation"
}
}
}
},
"/show/custom": {
"post": {
"tags": [
"screens"
],
"summary": "Set text to show (advanced)",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CustomText"
}
}
}
},
"responses": {
"200": {
"description": "successful operation"
}
}
}
},
"/full_refresh": {
"get": {
"tags": [
"system"
],
"summary": "Force full refresh of all displays",
"responses": {
"200": {
"description": "successful operation"
}
}
}
},
"/lights": {
"get": {
"tags": [
"lights"
],
"summary": "Get LEDs status",
"responses": {
"200": {
"description": "successful operation",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ArrayOfLeds"
}
}
}
}
}
},
"patch": {
"tags": [
"lights"
],
"summary": "Set individual LEDs",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ArrayOfLedsInput"
}
}
}
},
"responses": {
"200": {
"description": "succesful operation"
},
"400": {
"description": "invalid colors or wrong amount of LEDs"
}
}
}
},
"/lights/{color}": {
"get": {
"tags": [
"lights"
],
"summary": "Turn on LEDs with specific color",
"parameters": [
{
"in": "path",
"name": "color",
"schema": {
"type": "string",
"default": "FFCC00"
},
"required": true,
"description": "Color in RGB hex"
}
],
"responses": {
"200": {
"description": "successful operation"
}
}
}
},
"/lights/off": {
"get": {
"tags": [
"lights"
],
"summary": "Turn LEDs off",
"responses": {
"200": {
"description": "successful operation"
}
}
}
},
"/restart": {
"get": {
"tags": [
"system"
],
"summary": "Restart BTClock",
"responses": {
"200": {
"description": "successful operation"
}
}
}
}
},
"components": {
"schemas": {
"RgbColorValues": {
"type": "object",
"properties": {
"red": {
"type": "integer",
"minimum": 0,
"maximum": 255,
"example": 255
},
"green": {
"type": "integer",
"minimum": 0,
"maximum": 255,
"example": 204
},
"blue": {
"type": "integer",
"minimum": 0,
"maximum": 255,
"example": 0
}
}
},
"RgbColorHex": {
"type": "object",
"properties": {
"hex": {
"type": "string",
"pattern": "^#(?:[0-9a-fA-F]{3}){1,2}$",
"example": "#FFCC00"
}
}
},
"RgbColorValueAndHex": {
"allOf": [
{
"$ref": "#/components/schemas/RgbColorValues"
},
{
"$ref": "#/components/schemas/RgbColorHex"
}
]
},
"RgbColorValueOrHex": {
"oneOf": [
{
"$ref": "#/components/schemas/RgbColorValues"
},
{
"$ref": "#/components/schemas/RgbColorHex"
}
]
},
"ArrayOfLeds": {
"type": "array",
"items": {
"$ref": "#/components/schemas/RgbColorValueAndHex"
}
},
"ArrayOfLedsInput": {
"type": "array",
"items": {
"$ref": "#/components/schemas/RgbColorValueOrHex"
}
},
"Settings": {
"type": "object",
"properties": {
"fetchEurPrice": {
"type": "boolean",
"description": "Fetch EUR price instead of USD"
},
"fgColor": {
"type": "string",
"default": 16777215,
"description": "ePaper foreground (text) color"
},
"bgColor": {
"type": "string",
"default": 0,
"description": "ePaper background color"
},
"ledTestOnPower": {
"type": "boolean",
"default": true,
"description": "Do LED test on power-on"
},
"ledFlashOnUpd": {
"type": "boolean",
"default": false,
"description": "Flash LEDs on new block"
},
"mdnsEnabled": {
"type": "boolean",
"default": true,
"description": "Enable mDNS"
},
"otaEnabled": {
"type": "boolean",
"default": true,
"description": "Enable over-the-air updates"
},
"stealFocus": {
"type": "boolean",
"default": false,
"description": "Steal focus on new block"
},
"mcapBigChar": {
"type": "boolean",
"default": false,
"description": "Use big characters for market cap screen"
},
"mempoolInstance": {
"type": "string",
"default": "mempool.space",
"description": "Mempool.space instance to connect to"
},
"ledBrightness": {
"type": "integer",
"default": 128,
"description": "Brightness of LEDs"
},
"fullRefreshMin": {
"type": "integer",
"default": 60,
"description": "Full refresh time of ePaper displays in minutes"
},
"screen[0]": {
"type": "boolean"
},
"screen[1]": {
"type": "boolean"
},
"screen[2]": {
"type": "boolean"
},
"screen[3]": {
"type": "boolean"
},
"screen[4]": {
"type": "boolean"
},
"screen[5]": {
"type": "boolean"
},
"tzOffset": {
"type": "integer",
"default": 60,
"description": "Timezone offset in minutes"
},
"minSecPriceUpd": {
"type": "integer",
"default": 30,
"description": "Minimum time between price updates in seconds"
},
"timePerScreen": {
"type": "integer",
"default": 30,
"description": "Time between screens when rotating in minutes"
}
}
},
"CustomText": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 7,
"maxItems": 7
}
}
}
}
"openapi": "3.0.3",
"info": {
"title": "BTClock API",
"version": "3.0",
"description": "BTClock V3 API"
},
"servers": [
{
"url": "/api/"
}
],
"paths": {
"/status": {
"get": {
"tags": ["system"],
"summary": "Get current status",
"responses": {
"200": {
"description": "successful operation"
}
}
}
},
"/system_status": {
"get": {
"tags": ["system"],
"summary": "Get system status",
"responses": {
"200": {
"description": "successful operation"
}
}
}
},
"/settings": {
"get": {
"tags": ["system"],
"summary": "Get current settings",
"responses": {
"200": {
"description": "successful operation",
"content": {
"application/json": {
"schema": {
"$ref": null
}
}
}
}
}
},
"post": {
"tags": ["system"],
"summary": "Save current settings",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Settings"
}
}
}
},
"responses": {
"200": {
"description": "successful operation"
}
}
},
"patch": {
"tags": ["system"],
"summary": "Save current settings",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Settings"
}
}
}
},
"responses": {
"200": {
"description": "successful operation"
}
}
}
},
"/action/pause": {
"get": {
"tags": ["timer"],
"summary": "Pause screen rotation",
"responses": {
"200": {
"description": "successful operation"
}
}
}
},
"/action/timer_restart": {
"get": {
"tags": ["timer"],
"summary": "Restart screen rotation",
"responses": {
"200": {
"description": "successful operation"
}
}
}
},
"/show/screen/{id}": {
"get": {
"tags": ["screens"],
"summary": "Set screen to show",
"parameters": [
{
"in": "path",
"name": "id",
"schema": {
"type": "integer",
"default": 1
},
"required": true,
"description": "ID of screen to show"
}
],
"responses": {
"200": {
"description": "successful operation"
}
}
}
},
"/show/text/{text}": {
"get": {
"tags": ["screens"],
"summary": "Set text to show",
"parameters": [
{
"in": "path",
"name": "text",
"schema": {
"type": "string",
"default": "text"
},
"required": true,
"description": "Text to show"
}
],
"responses": {
"200": {
"description": "successful operation"
}
}
}
},
"/show/custom": {
"post": {
"tags": ["screens"],
"summary": "Set text to show (advanced)",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CustomText"
}
}
}
},
"responses": {
"200": {
"description": "successful operation"
}
}
}
},
"/full_refresh": {
"get": {
"tags": ["system"],
"summary": "Force full refresh of all displays",
"responses": {
"200": {
"description": "successful operation"
}
}
}
},
"/lights": {
"get": {
"tags": ["lights"],
"summary": "Get LEDs status",
"responses": {
"200": {
"description": "successful operation",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ArrayOfLeds"
}
}
}
}
}
},
"patch": {
"tags": ["lights"],
"summary": "Set individual LEDs",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ArrayOfLedsInput"
}
}
}
},
"responses": {
"200": {
"description": "succesful operation"
},
"400": {
"description": "invalid colors or wrong amount of LEDs"
}
}
}
},
"/lights/{color}": {
"get": {
"tags": ["lights"],
"summary": "Turn on LEDs with specific color",
"parameters": [
{
"in": "path",
"name": "color",
"schema": {
"type": "string",
"default": "FFCC00"
},
"required": true,
"description": "Color in RGB hex"
}
],
"responses": {
"200": {
"description": "successful operation"
}
}
}
},
"/lights/off": {
"get": {
"tags": ["lights"],
"summary": "Turn LEDs off",
"responses": {
"200": {
"description": "successful operation"
}
}
}
},
"/restart": {
"get": {
"tags": ["system"],
"summary": "Restart BTClock",
"responses": {
"200": {
"description": "successful operation"
}
}
}
}
},
"components": {
"schemas": {
"RgbColorValues": {
"type": "object",
"properties": {
"red": {
"type": "integer",
"minimum": 0,
"maximum": 255,
"example": 255
},
"green": {
"type": "integer",
"minimum": 0,
"maximum": 255,
"example": 204
},
"blue": {
"type": "integer",
"minimum": 0,
"maximum": 255,
"example": 0
}
}
},
"RgbColorHex": {
"type": "object",
"properties": {
"hex": {
"type": "string",
"pattern": "^#(?:[0-9a-fA-F]{3}){1,2}$",
"example": "#FFCC00"
}
}
},
"RgbColorValueAndHex": {
"allOf": [
{
"$ref": "#/components/schemas/RgbColorValues"
},
{
"$ref": "#/components/schemas/RgbColorHex"
}
]
},
"RgbColorValueOrHex": {
"oneOf": [
{
"$ref": "#/components/schemas/RgbColorValues"
},
{
"$ref": "#/components/schemas/RgbColorHex"
}
]
},
"ArrayOfLeds": {
"type": "array",
"items": {
"$ref": "#/components/schemas/RgbColorValueAndHex"
}
},
"ArrayOfLedsInput": {
"type": "array",
"items": {
"$ref": "#/components/schemas/RgbColorValueOrHex"
}
},
"Settings": {
"type": "object",
"properties": {
"fetchEurPrice": {
"type": "boolean",
"description": "Fetch EUR price instead of USD"
},
"fgColor": {
"type": "string",
"default": 16777215,
"description": "ePaper foreground (text) color"
},
"bgColor": {
"type": "string",
"default": 0,
"description": "ePaper background color"
},
"ledTestOnPower": {
"type": "boolean",
"default": true,
"description": "Do LED test on power-on"
},
"ledFlashOnUpd": {
"type": "boolean",
"default": false,
"description": "Flash LEDs on new block"
},
"mdnsEnabled": {
"type": "boolean",
"default": true,
"description": "Enable mDNS"
},
"otaEnabled": {
"type": "boolean",
"default": true,
"description": "Enable over-the-air updates"
},
"stealFocus": {
"type": "boolean",
"default": false,
"description": "Steal focus on new block"
},
"mcapBigChar": {
"type": "boolean",
"default": false,
"description": "Use big characters for market cap screen"
},
"mempoolInstance": {
"type": "string",
"default": "mempool.space",
"description": "Mempool.space instance to connect to"
},
"ledBrightness": {
"type": "integer",
"default": 128,
"description": "Brightness of LEDs"
},
"fullRefreshMin": {
"type": "integer",
"default": 60,
"description": "Full refresh time of ePaper displays in minutes"
},
"screen[0]": {
"type": "boolean"
},
"screen[1]": {
"type": "boolean"
},
"screen[2]": {
"type": "boolean"
},
"screen[3]": {
"type": "boolean"
},
"screen[4]": {
"type": "boolean"
},
"screen[5]": {
"type": "boolean"
},
"tzOffset": {
"type": "integer",
"default": 60,
"description": "Timezone offset in minutes"
},
"minSecPriceUpd": {
"type": "integer",
"default": 30,
"description": "Minimum time between price updates in seconds"
},
"timePerScreen": {
"type": "integer",
"default": 30,
"description": "Time between screens when rotating in minutes"
}
}
},
"CustomText": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 7,
"maxItems": 7
}
}
}
}

View File

@ -31,7 +31,7 @@ paths:
'200':
description: successful operation
content:
application/json:
application/json:
schema:
$ref: #/components/schemas/ArrayOfLeds
post:
@ -140,7 +140,7 @@ paths:
schema:
$ref: '#/components/schemas/ArrayOfLeds'
patch:
tags:
tags:
- lights
summary: Set individual LEDs
requestBody:
@ -149,9 +149,9 @@ paths:
schema:
$ref: '#/components/schemas/ArrayOfLedsInput'
responses:
"200":
'200':
description: succesful operation
"400":
'400':
description: invalid colors or wrong amount of LEDs
/lights/{color}:
get:
@ -188,38 +188,38 @@ paths:
components:
schemas:
RgbColorValues:
type: object
properties:
red:
type: integer
minimum: 0
maximum: 255
example: 255
green:
type: integer
minimum: 0
maximum: 255
example: 204
blue:
type: integer
minimum: 0
maximum: 255
example: 0
type: object
properties:
red:
type: integer
minimum: 0
maximum: 255
example: 255
green:
type: integer
minimum: 0
maximum: 255
example: 204
blue:
type: integer
minimum: 0
maximum: 255
example: 0
RgbColorHex:
type: object
properties:
hex:
type: string
pattern: ^#(?:[0-9a-fA-F]{3}){1,2}$
example: "#FFCC00"
type: object
properties:
hex:
type: string
pattern: ^#(?:[0-9a-fA-F]{3}){1,2}$
example: '#FFCC00'
RgbColorValueAndHex:
allOf:
- $ref: '#/components/schemas/RgbColorValues'
- $ref: '#/components/schemas/RgbColorHex'
allOf:
- $ref: '#/components/schemas/RgbColorValues'
- $ref: '#/components/schemas/RgbColorHex'
RgbColorValueOrHex:
oneOf:
- $ref: '#/components/schemas/RgbColorValues'
- $ref: '#/components/schemas/RgbColorHex'
oneOf:
- $ref: '#/components/schemas/RgbColorValues'
- $ref: '#/components/schemas/RgbColorHex'
ArrayOfLeds:
type: array
items:

View File

@ -5,16 +5,14 @@ import { vitePreprocess } from '@sveltejs/kit/vite';
const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: vitePreprocess({
}),
build: {
rollupOptions: {
output: {
assetFilenames: '[hash]'
preprocess: vitePreprocess({}),
build: {
rollupOptions: {
output: {
assetFilenames: '[hash]'
}
}
}
},
},
kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
@ -24,11 +22,11 @@ build: {
// these options are set automatically — see below
pages: 'dist',
assets: 'dist',
fallback: "bundle.html",
fallback: 'bundle.html',
precompress: false,
strict: true
}),
appDir: "build",
appDir: 'build'
// inlineStyleThreshold: 9999999999
}
};

View File

@ -1,61 +1,71 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
import * as fs from 'fs'
import * as path from 'path'
import * as fs from 'fs';
import * as path from 'path';
const doRewrap = ({ cssClass }) => {
try {
if (fs.existsSync(path.resolve(__dirname, 'dist/bundle.js'))) {
return
return;
}
} catch(e) {}
console.log("\nStart re-wrapping...")
fs.readFile(path.resolve(__dirname, 'dist/bundle.html'), 'utf8', function(err, data){
} catch (e) {}
console.log('\nStart re-wrapping...');
fs.readFile(path.resolve(__dirname, 'dist/bundle.html'), 'utf8', function (err, data) {
if (!data) {
console.log(`[Error]: No bundle.html generated, check svelte.config.js -> config.kit.adapter -> fallback: "bundle.html"`)
return
console.log(
`[Error]: No bundle.html generated, check svelte.config.js -> config.kit.adapter -> fallback: "bundle.html"`
);
return;
}
let matchData = data.match(/(?<=<script\b[^>]*>)([\s\S]*?)(?=<\/script>)/gm)
const matchData = data.match(/(?<=<script\b[^>]*>)([\s\S]*?)(?=<\/script>)/gm);
if (matchData) {
let cleanData = matchData[0].trim()
.replace(`document.querySelector('[data-sveltekit-hydrate="45h"]').parentNode`, `document.querySelector(".${cssClass}")`)
const cleanData = matchData[0]
.trim()
.replace(
`document.querySelector('[data-sveltekit-hydrate="45h"]').parentNode`,
`document.querySelector(".${cssClass}")`
);
fs.writeFile(path.resolve(__dirname, 'dist/bundle.js'), cleanData, (err) => {
if (err)
console.log(err)
if (err) console.log(err);
else {
try {
fs.rename(path.resolve(__dirname,'dist/index.page'), path.resolve(__dirname, 'dist/index.html'), (err) => { })
} catch (e) { }
fs.rename(
path.resolve(__dirname, 'dist/index.page'),
path.resolve(__dirname, 'dist/index.html'),
() => {}
);
} catch (e) {}
try {
fs.unlinkSync(path.resolve(__dirname, 'dist/bundle.html'))
} catch (e) { }
console.log("Finished: bundle.js + index.html have been regenerated.\n")
fs.unlinkSync(path.resolve(__dirname, 'dist/bundle.html'));
} catch (e) {}
console.log('Finished: bundle.js + index.html have been regenerated.\n');
}
})
} else
console.log(`[Error]: No proper <script> tag found in bundle.html`)
})
}
});
} else console.log(`[Error]: No proper <script> tag found in bundle.html`);
});
};
export default defineConfig({
plugins: [sveltekit(), {
name: 'postbuild-command',
plugins: [
sveltekit(),
{
name: 'postbuild-command',
closeBundle: {
order: 'post',
handler() {
setTimeout(() => doRewrap({ cssClass: "overlay" }), Math.random()*500+500)
setTimeout(() => doRewrap({ cssClass: 'overlay' }), Math.random() * 500 + 500);
}
}
}],
}
],
build: {
minify: true,
cssCodeSplit: false,
rollupOptions: {
output: {
manualChunks: () => 'app',
assetFileNames: "[name][extname]"
assetFileNames: '[name][extname]'
}
}
}