🎨 format
This commit is contained in:
@@ -1,252 +1,266 @@
|
||||
<script lang="ts">
|
||||
import { onDestroy, onMount } from 'svelte'
|
||||
import { modals } from 'svelte-modals'
|
||||
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte'
|
||||
import SettingsCard from '$lib/components/SettingsCard.svelte'
|
||||
import Spinner from '$lib/components/Spinner.svelte'
|
||||
import { slide } from 'svelte/transition'
|
||||
import { cubicOut } from 'svelte/easing'
|
||||
import { type SystemInformation, type Analytics, MessageTopic } from '$lib/types/models'
|
||||
import { socket } from '$lib/stores/socket'
|
||||
import { api } from '$lib/api'
|
||||
import { convertSeconds } from '$lib/utilities'
|
||||
import { useFeatureFlags } from '$lib/stores/featureFlags'
|
||||
import {
|
||||
Cancel,
|
||||
Power,
|
||||
FactoryReset,
|
||||
Sleep,
|
||||
Health,
|
||||
CPU,
|
||||
SDK,
|
||||
CPP,
|
||||
Speed,
|
||||
Heap,
|
||||
Pyramid,
|
||||
Sketch,
|
||||
Flash,
|
||||
Folder,
|
||||
Temperature,
|
||||
Stopwatch
|
||||
} from '$lib/components/icons'
|
||||
import StatusItem from '$lib/components/StatusItem.svelte'
|
||||
import ActionButton from './ActionButton.svelte'
|
||||
import { onDestroy, onMount } from 'svelte'
|
||||
import { modals } from 'svelte-modals'
|
||||
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte'
|
||||
import SettingsCard from '$lib/components/SettingsCard.svelte'
|
||||
import Spinner from '$lib/components/Spinner.svelte'
|
||||
import { slide } from 'svelte/transition'
|
||||
import { cubicOut } from 'svelte/easing'
|
||||
import { type SystemInformation, type Analytics, MessageTopic } from '$lib/types/models'
|
||||
import { socket } from '$lib/stores/socket'
|
||||
import { api } from '$lib/api'
|
||||
import { convertSeconds } from '$lib/utilities'
|
||||
import { useFeatureFlags } from '$lib/stores/featureFlags'
|
||||
import {
|
||||
Cancel,
|
||||
Power,
|
||||
FactoryReset,
|
||||
Sleep,
|
||||
Health,
|
||||
CPU,
|
||||
SDK,
|
||||
CPP,
|
||||
Speed,
|
||||
Heap,
|
||||
Pyramid,
|
||||
Sketch,
|
||||
Flash,
|
||||
Folder,
|
||||
Temperature,
|
||||
Stopwatch
|
||||
} from '$lib/components/icons'
|
||||
import StatusItem from '$lib/components/StatusItem.svelte'
|
||||
import ActionButton from './ActionButton.svelte'
|
||||
|
||||
const features = useFeatureFlags()
|
||||
const features = useFeatureFlags()
|
||||
|
||||
let systemInformation: SystemInformation | null = $state(null)
|
||||
let systemInformation: SystemInformation | null = $state(null)
|
||||
|
||||
async function getSystemStatus() {
|
||||
const result = await api.get<SystemInformation>('/api/system/status')
|
||||
if (result.isErr()) {
|
||||
console.error('Error:', result.inner)
|
||||
return
|
||||
async function getSystemStatus() {
|
||||
const result = await api.get<SystemInformation>('/api/system/status')
|
||||
if (result.isErr()) {
|
||||
console.error('Error:', result.inner)
|
||||
return
|
||||
}
|
||||
systemInformation = result.inner
|
||||
return systemInformation
|
||||
}
|
||||
systemInformation = result.inner
|
||||
return systemInformation
|
||||
}
|
||||
|
||||
const postFactoryReset = async () => await api.post('/api/system/reset')
|
||||
const postFactoryReset = async () => await api.post('/api/system/reset')
|
||||
|
||||
const postSleep = async () => await api.post('api/sleep')
|
||||
const postSleep = async () => await api.post('api/sleep')
|
||||
|
||||
onMount(() => socket.on(MessageTopic.analytics, handleSystemData))
|
||||
onMount(() => socket.on(MessageTopic.analytics, handleSystemData))
|
||||
|
||||
onDestroy(() => socket.off(MessageTopic.analytics, handleSystemData))
|
||||
const handleSystemData = (data: Analytics) => {
|
||||
if (systemInformation) {
|
||||
systemInformation = {
|
||||
...systemInformation,
|
||||
...(data as unknown as SystemInformation)
|
||||
}
|
||||
onDestroy(() => socket.off(MessageTopic.analytics, handleSystemData))
|
||||
const handleSystemData = (data: Analytics) => {
|
||||
if (systemInformation) {
|
||||
systemInformation = {
|
||||
...systemInformation,
|
||||
...(data as unknown as SystemInformation)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const postRestart = async () => await api.post('/api/system/restart')
|
||||
const postRestart = async () => await api.post('/api/system/restart')
|
||||
|
||||
function confirmRestart() {
|
||||
modals.open(ConfirmDialog, {
|
||||
title: 'Confirm Restart',
|
||||
message: 'Are you sure you want to restart the device?',
|
||||
labels: {
|
||||
cancel: { label: 'Abort', icon: Cancel },
|
||||
confirm: { label: 'Restart', icon: Power }
|
||||
},
|
||||
onConfirm: () => {
|
||||
modals.close()
|
||||
postRestart()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function confirmReset() {
|
||||
modals.open(ConfirmDialog, {
|
||||
title: 'Confirm Factory Reset',
|
||||
message: 'Are you sure you want to reset the device to its factory defaults?',
|
||||
labels: {
|
||||
cancel: { label: 'Abort', icon: Cancel },
|
||||
confirm: { label: 'Factory Reset', icon: FactoryReset }
|
||||
},
|
||||
onConfirm: () => {
|
||||
modals.close()
|
||||
postFactoryReset()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function confirmSleep() {
|
||||
modals.open(ConfirmDialog, {
|
||||
title: 'Confirm Going to Sleep',
|
||||
message: 'Are you sure you want to put the device into sleep?',
|
||||
labels: {
|
||||
cancel: { label: 'Abort', icon: Cancel },
|
||||
confirm: { label: 'Sleep', icon: Sleep }
|
||||
},
|
||||
onConfirm: () => {
|
||||
modals.close()
|
||||
postSleep()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
interface ActionButtonDef {
|
||||
icon: any
|
||||
label: string
|
||||
onClick: () => void
|
||||
type?: string
|
||||
condition?: () => boolean
|
||||
}
|
||||
|
||||
const actionButtons: ActionButtonDef[] = [
|
||||
{
|
||||
icon: Sleep,
|
||||
label: 'Sleep',
|
||||
onClick: confirmSleep,
|
||||
condition: () => Boolean($features.sleep)
|
||||
},
|
||||
{
|
||||
icon: Power,
|
||||
label: 'Restart',
|
||||
onClick: confirmRestart
|
||||
},
|
||||
{
|
||||
icon: FactoryReset,
|
||||
label: 'Factory Reset',
|
||||
onClick: confirmReset,
|
||||
type: 'secondary'
|
||||
function confirmRestart() {
|
||||
modals.open(ConfirmDialog, {
|
||||
title: 'Confirm Restart',
|
||||
message: 'Are you sure you want to restart the device?',
|
||||
labels: {
|
||||
cancel: { label: 'Abort', icon: Cancel },
|
||||
confirm: { label: 'Restart', icon: Power }
|
||||
},
|
||||
onConfirm: () => {
|
||||
modals.close()
|
||||
postRestart()
|
||||
}
|
||||
})
|
||||
}
|
||||
]
|
||||
|
||||
function confirmReset() {
|
||||
modals.open(ConfirmDialog, {
|
||||
title: 'Confirm Factory Reset',
|
||||
message: 'Are you sure you want to reset the device to its factory defaults?',
|
||||
labels: {
|
||||
cancel: { label: 'Abort', icon: Cancel },
|
||||
confirm: { label: 'Factory Reset', icon: FactoryReset }
|
||||
},
|
||||
onConfirm: () => {
|
||||
modals.close()
|
||||
postFactoryReset()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function confirmSleep() {
|
||||
modals.open(ConfirmDialog, {
|
||||
title: 'Confirm Going to Sleep',
|
||||
message: 'Are you sure you want to put the device into sleep?',
|
||||
labels: {
|
||||
cancel: { label: 'Abort', icon: Cancel },
|
||||
confirm: { label: 'Sleep', icon: Sleep }
|
||||
},
|
||||
onConfirm: () => {
|
||||
modals.close()
|
||||
postSleep()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
interface ActionButtonDef {
|
||||
icon: any
|
||||
label: string
|
||||
onClick: () => void
|
||||
type?: string
|
||||
condition?: () => boolean
|
||||
}
|
||||
|
||||
const actionButtons: ActionButtonDef[] = [
|
||||
{
|
||||
icon: Sleep,
|
||||
label: 'Sleep',
|
||||
onClick: confirmSleep,
|
||||
condition: () => Boolean($features.sleep)
|
||||
},
|
||||
{
|
||||
icon: Power,
|
||||
label: 'Restart',
|
||||
onClick: confirmRestart
|
||||
},
|
||||
{
|
||||
icon: FactoryReset,
|
||||
label: 'Factory Reset',
|
||||
onClick: confirmReset,
|
||||
type: 'secondary'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<SettingsCard collapsible={false}>
|
||||
{#snippet icon()}
|
||||
<Health class="lex-shrink-0 mr-2 h-6 w-6 self-end" />
|
||||
{/snippet}
|
||||
{#snippet title()}
|
||||
<span>System Status</span>
|
||||
{/snippet}
|
||||
{#snippet icon()}
|
||||
<Health class="lex-shrink-0 mr-2 h-6 w-6 self-end" />
|
||||
{/snippet}
|
||||
{#snippet title()}
|
||||
<span>System Status</span>
|
||||
{/snippet}
|
||||
|
||||
<div class="w-full overflow-x-auto">
|
||||
{#await getSystemStatus()}
|
||||
<Spinner />
|
||||
{:then}
|
||||
{#if systemInformation}
|
||||
<div
|
||||
class="flex w-full flex-col space-y-1"
|
||||
transition:slide|local={{ duration: 300, easing: cubicOut }}>
|
||||
<StatusItem
|
||||
icon={CPU}
|
||||
title="Chip"
|
||||
description={`${systemInformation.cpu_type} Rev ${systemInformation.cpu_rev}`} />
|
||||
<div class="w-full overflow-x-auto">
|
||||
{#await getSystemStatus()}
|
||||
<Spinner />
|
||||
{:then}
|
||||
{#if systemInformation}
|
||||
<div
|
||||
class="flex w-full flex-col space-y-1"
|
||||
transition:slide|local={{ duration: 300, easing: cubicOut }}
|
||||
>
|
||||
<StatusItem
|
||||
icon={CPU}
|
||||
title="Chip"
|
||||
description={`${systemInformation.cpu_type} Rev ${systemInformation.cpu_rev}`}
|
||||
/>
|
||||
|
||||
<StatusItem
|
||||
icon={SDK}
|
||||
title="SDK Version"
|
||||
description={`ESP-IDF ${systemInformation.sdk_version} / Arduino ${systemInformation.arduino_version}`} />
|
||||
<StatusItem
|
||||
icon={SDK}
|
||||
title="SDK Version"
|
||||
description={`ESP-IDF ${systemInformation.sdk_version} / Arduino ${systemInformation.arduino_version}`}
|
||||
/>
|
||||
|
||||
<StatusItem
|
||||
icon={CPP}
|
||||
title="Firmware Version"
|
||||
description={systemInformation.firmware_version} />
|
||||
<StatusItem
|
||||
icon={CPP}
|
||||
title="Firmware Version"
|
||||
description={systemInformation.firmware_version}
|
||||
/>
|
||||
|
||||
<StatusItem
|
||||
icon={Speed}
|
||||
title="CPU Frequency"
|
||||
description={`${systemInformation.cpu_freq_mhz} MHz ${
|
||||
systemInformation.cpu_cores == 2 ? 'Dual Core' : 'Single Core'
|
||||
}`} />
|
||||
<StatusItem
|
||||
icon={Speed}
|
||||
title="CPU Frequency"
|
||||
description={`${systemInformation.cpu_freq_mhz} MHz ${
|
||||
systemInformation.cpu_cores == 2 ? 'Dual Core' : 'Single Core'
|
||||
}`}
|
||||
/>
|
||||
|
||||
<StatusItem
|
||||
icon={Heap}
|
||||
title="Heap (Free / Max Alloc)"
|
||||
description={`${systemInformation.free_heap} / ${systemInformation.max_alloc_heap} bytes`} />
|
||||
<StatusItem
|
||||
icon={Heap}
|
||||
title="Heap (Free / Max Alloc)"
|
||||
description={`${systemInformation.free_heap} / ${systemInformation.max_alloc_heap} bytes`}
|
||||
/>
|
||||
|
||||
<StatusItem
|
||||
icon={Pyramid}
|
||||
title="PSRAM (Size / Free)"
|
||||
description={`${systemInformation.psram_size} / ${systemInformation.psram_size} bytes`} />
|
||||
<StatusItem
|
||||
icon={Pyramid}
|
||||
title="PSRAM (Size / Free)"
|
||||
description={`${systemInformation.psram_size} / ${systemInformation.psram_size} bytes`}
|
||||
/>
|
||||
|
||||
<StatusItem
|
||||
icon={Sketch}
|
||||
title="Sketch (Used / Free)"
|
||||
description={`${(
|
||||
(systemInformation.sketch_size / systemInformation.free_sketch_space) *
|
||||
100
|
||||
).toFixed(1)} % of
|
||||
<StatusItem
|
||||
icon={Sketch}
|
||||
title="Sketch (Used / Free)"
|
||||
description={`${(
|
||||
(systemInformation.sketch_size / systemInformation.free_sketch_space) *
|
||||
100
|
||||
).toFixed(1)} % of
|
||||
${systemInformation.free_sketch_space / 1000000} MB used (${
|
||||
(systemInformation.free_sketch_space - systemInformation.sketch_size) / 1000000
|
||||
} MB free)`} />
|
||||
(systemInformation.free_sketch_space - systemInformation.sketch_size) / 1000000
|
||||
} MB free)`}
|
||||
/>
|
||||
|
||||
<StatusItem
|
||||
icon={Flash}
|
||||
title="Flash Chip (Size / Speed)"
|
||||
description={`${systemInformation.flash_chip_size / 1000000} MB / ${
|
||||
systemInformation.flash_chip_speed / 1000000
|
||||
} MHz`} />
|
||||
<StatusItem
|
||||
icon={Flash}
|
||||
title="Flash Chip (Size / Speed)"
|
||||
description={`${systemInformation.flash_chip_size / 1000000} MB / ${
|
||||
systemInformation.flash_chip_speed / 1000000
|
||||
} MHz`}
|
||||
/>
|
||||
|
||||
<StatusItem
|
||||
icon={Folder}
|
||||
title="File System (Used / Total)"
|
||||
description={`${(
|
||||
(systemInformation.fs_used / systemInformation.fs_total) *
|
||||
100
|
||||
).toFixed(1)} % of ${systemInformation.fs_total / 1000000} MB used (${
|
||||
(systemInformation.fs_total - systemInformation.fs_used) / 1000000
|
||||
}
|
||||
MB free)`} />
|
||||
<StatusItem
|
||||
icon={Folder}
|
||||
title="File System (Used / Total)"
|
||||
description={`${(
|
||||
(systemInformation.fs_used / systemInformation.fs_total) *
|
||||
100
|
||||
).toFixed(1)} % of ${systemInformation.fs_total / 1000000} MB used (${
|
||||
(systemInformation.fs_total - systemInformation.fs_used) / 1000000
|
||||
}
|
||||
MB free)`}
|
||||
/>
|
||||
|
||||
<StatusItem
|
||||
icon={Temperature}
|
||||
title="Core Temperature"
|
||||
description={`${
|
||||
systemInformation.core_temp == 53.33 ?
|
||||
'NaN'
|
||||
: systemInformation.core_temp.toFixed(2) + ' °C'
|
||||
}`} />
|
||||
<StatusItem
|
||||
icon={Temperature}
|
||||
title="Core Temperature"
|
||||
description={`${
|
||||
systemInformation.core_temp == 53.33 ?
|
||||
'NaN'
|
||||
: systemInformation.core_temp.toFixed(2) + ' °C'
|
||||
}`}
|
||||
/>
|
||||
|
||||
<StatusItem
|
||||
icon={Stopwatch}
|
||||
title="Uptime"
|
||||
description={convertSeconds(systemInformation.uptime)} />
|
||||
<StatusItem
|
||||
icon={Stopwatch}
|
||||
title="Uptime"
|
||||
description={convertSeconds(systemInformation.uptime)}
|
||||
/>
|
||||
|
||||
<StatusItem
|
||||
icon={Power}
|
||||
title="Reset Reason"
|
||||
description={systemInformation.cpu_reset_reason} />
|
||||
</div>
|
||||
{/if}
|
||||
{/await}
|
||||
</div>
|
||||
<StatusItem
|
||||
icon={Power}
|
||||
title="Reset Reason"
|
||||
description={systemInformation.cpu_reset_reason}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/await}
|
||||
</div>
|
||||
|
||||
<div class="mt-4 flex flex-wrap justify-end gap-2">
|
||||
{#each actionButtons as button}
|
||||
{#if button.condition === undefined || button.condition()}
|
||||
<ActionButton
|
||||
onclick={button.onClick}
|
||||
icon={button.icon}
|
||||
label={button.label}
|
||||
type={button.type || 'primary'} />
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<div class="mt-4 flex flex-wrap justify-end gap-2">
|
||||
{#each actionButtons as button}
|
||||
{#if button.condition === undefined || button.condition()}
|
||||
<ActionButton
|
||||
onclick={button.onClick}
|
||||
icon={button.icon}
|
||||
label={button.label}
|
||||
type={button.type || 'primary'}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</SettingsCard>
|
||||
|
||||
Reference in New Issue
Block a user