✨ Makes socket messages event typed
This commit is contained in:
@@ -1,3 +1,20 @@
|
|||||||
|
export enum Topics {
|
||||||
|
imu = 'imu',
|
||||||
|
mode = 'mode',
|
||||||
|
input = 'input',
|
||||||
|
analytics = 'analytics',
|
||||||
|
position = 'position',
|
||||||
|
angles = 'angles',
|
||||||
|
i2cScan = 'i2cScan',
|
||||||
|
peripheralSettings = 'peripheralSettings',
|
||||||
|
otastatus = 'otastatus',
|
||||||
|
servoState = 'servoState',
|
||||||
|
servoPWM = 'servoPWM',
|
||||||
|
WiFiSettings = 'WiFiSettings',
|
||||||
|
sonar = 'sonar',
|
||||||
|
rssi = 'rssi'
|
||||||
|
}
|
||||||
|
|
||||||
export type vector = { x: number; y: number }
|
export type vector = { x: number; y: number }
|
||||||
|
|
||||||
export interface ControllerInput {
|
export interface ControllerInput {
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte'
|
||||||
import { page } from '$app/state';
|
import { page } from '$app/state'
|
||||||
import { Modals, modals } from 'svelte-modals';
|
import { Modals, modals } from 'svelte-modals'
|
||||||
import Toast from '$lib/components/toasts/Toast.svelte';
|
import Toast from '$lib/components/toasts/Toast.svelte'
|
||||||
import { notifications } from '$lib/components/toasts/notifications';
|
import { notifications } from '$lib/components/toasts/notifications'
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition'
|
||||||
import '../app.css';
|
import '../app.css'
|
||||||
import Menu from '../lib/components/menu/Menu.svelte';
|
import Menu from '../lib/components/menu/Menu.svelte'
|
||||||
import Statusbar from '../lib/components/statusbar/statusbar.svelte';
|
import Statusbar from '../lib/components/statusbar/statusbar.svelte'
|
||||||
import {
|
import {
|
||||||
telemetry,
|
telemetry,
|
||||||
analytics,
|
analytics,
|
||||||
@@ -19,75 +19,75 @@
|
|||||||
servoAnglesOut,
|
servoAnglesOut,
|
||||||
socket,
|
socket,
|
||||||
location,
|
location,
|
||||||
useFeatureFlags,
|
useFeatureFlags
|
||||||
} from '$lib/stores';
|
} from '$lib/stores'
|
||||||
import type { Analytics, DownloadOTA } from '$lib/types/models';
|
import { Topics, type Analytics, type DownloadOTA } from '$lib/types/models'
|
||||||
interface Props {
|
interface Props {
|
||||||
children?: import('svelte').Snippet;
|
children?: import('svelte').Snippet
|
||||||
}
|
}
|
||||||
|
|
||||||
let { children }: Props = $props();
|
let { children }: Props = $props()
|
||||||
|
|
||||||
const features = useFeatureFlags();
|
const features = useFeatureFlags()
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
const ws = $location ? $location : window.location.host;
|
const ws = $location ? $location : window.location.host
|
||||||
socket.init(`ws://${ws}/api/ws/events`);
|
socket.init(`ws://${ws}/api/ws/events`)
|
||||||
|
|
||||||
addEventListeners();
|
addEventListeners()
|
||||||
|
|
||||||
outControllerData.subscribe(data => socket.sendEvent('input', data));
|
outControllerData.subscribe(data => socket.sendEvent(Topics.input, data))
|
||||||
mode.subscribe(data => socket.sendEvent('mode', data));
|
mode.subscribe(data => socket.sendEvent(Topics.mode, data))
|
||||||
servoAnglesOut.subscribe(data => socket.sendEvent('angles', data));
|
servoAnglesOut.subscribe(data => socket.sendEvent(Topics.angles, data))
|
||||||
kinematicData.subscribe(data => socket.sendEvent('position', data));
|
kinematicData.subscribe(data => socket.sendEvent(Topics.position, data))
|
||||||
});
|
})
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
removeEventListeners();
|
removeEventListeners()
|
||||||
});
|
})
|
||||||
|
|
||||||
const addEventListeners = () => {
|
const addEventListeners = () => {
|
||||||
socket.on('open', handleOpen);
|
socket.on('open', handleOpen)
|
||||||
socket.on('close', handleClose);
|
socket.on('close', handleClose)
|
||||||
socket.on('error', handleError);
|
socket.on('error', handleError)
|
||||||
socket.on('rssi', handleNetworkStatus);
|
socket.on(Topics.rssi, handleNetworkStatus)
|
||||||
socket.on('mode', (data: ModesEnum) => mode.set(data));
|
socket.on(Topics.mode, (data: ModesEnum) => mode.set(data))
|
||||||
socket.on('analytics', handleAnalytics);
|
socket.on(Topics.analytics, handleAnalytics)
|
||||||
socket.on('angles', (angles: number[]) => {
|
socket.on(Topics.angles, (angles: number[]) => {
|
||||||
if (angles.length) servoAngles.set(angles);
|
if (angles.length) servoAngles.set(angles)
|
||||||
});
|
})
|
||||||
features.subscribe(data => {
|
features.subscribe(data => {
|
||||||
if (data?.download_firmware) socket.on('otastatus', handleOAT);
|
if (data?.download_firmware) socket.on(Topics.otastatus, handleOAT)
|
||||||
if (data?.sonar) socket.on('sonar', data => console.log(data));
|
if (data?.sonar) socket.on(Topics.sonar, data => console.log(data))
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
const removeEventListeners = () => {
|
const removeEventListeners = () => {
|
||||||
socket.off('analytics', handleAnalytics);
|
socket.off(Topics.analytics, handleAnalytics)
|
||||||
socket.off('open', handleOpen);
|
socket.off('open', handleOpen)
|
||||||
socket.off('close', handleClose);
|
socket.off('close', handleClose)
|
||||||
socket.off('rssi', handleNetworkStatus);
|
socket.off(Topics.rssi, handleNetworkStatus)
|
||||||
socket.off('otastatus', handleOAT);
|
socket.off(Topics.otastatus, handleOAT)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleOpen = () => {
|
const handleOpen = () => {
|
||||||
notifications.success('Connection to device established', 5000);
|
notifications.success('Connection to device established', 5000)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
notifications.error('Connection to device lost', 5000);
|
notifications.error('Connection to device lost', 5000)
|
||||||
telemetry.setRSSI(0);
|
telemetry.setRSSI(0)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleError = (data: any) => console.error(data);
|
const handleError = (data: any) => console.error(data)
|
||||||
|
|
||||||
const handleAnalytics = (data: Analytics) => analytics.addData(data);
|
const handleAnalytics = (data: Analytics) => analytics.addData(data)
|
||||||
|
|
||||||
const handleNetworkStatus = (data: number) => telemetry.setRSSI(data);
|
const handleNetworkStatus = (data: number) => telemetry.setRSSI(data)
|
||||||
|
|
||||||
const handleOAT = (data: DownloadOTA) => telemetry.setDownloadOTA(data);
|
const handleOAT = (data: DownloadOTA) => telemetry.setDownloadOTA(data)
|
||||||
|
|
||||||
let menuOpen = $state(false);
|
let menuOpen = $state(false)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Controls from './Controls.svelte';
|
import Controls from './Controls.svelte'
|
||||||
import WidgetContainer from '$lib/components/layout/WidgetContainer.svelte';
|
import WidgetContainer from '$lib/components/layout/WidgetContainer.svelte'
|
||||||
import { selectedView, views } from '$lib/stores/application';
|
import { selectedView, views } from '$lib/stores/application'
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte'
|
||||||
import { mpu, socket } from '$lib/stores';
|
import { mpu, socket } from '$lib/stores'
|
||||||
import { imu } from '$lib/stores/imu';
|
import { imu } from '$lib/stores/imu'
|
||||||
import type { IMU } from '$lib/types/models';
|
import { Topics, type IMU } from '$lib/types/models'
|
||||||
|
|
||||||
let layout = $derived($views.find(v => v.name === $selectedView)!);
|
let layout = $derived($views.find(v => v.name === $selectedView)!)
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
socket.on('imu', (data: IMU) => {
|
socket.on(Topics.imu, (data: IMU) => {
|
||||||
imu.addData(data);
|
imu.addData(data)
|
||||||
if (data.heading)
|
if (data.heading)
|
||||||
mpu.update(mpuData => {
|
mpu.update(mpuData => {
|
||||||
mpuData.heading = data.heading;
|
mpuData.heading = data.heading
|
||||||
console.log(data.heading);
|
console.log(data.heading)
|
||||||
|
|
||||||
return mpuData;
|
return mpuData
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="absolute top-0 select-none w-screen h-screen">
|
<div class="absolute top-0 select-none w-screen h-screen">
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import SettingsCard from '$lib/components/SettingsCard.svelte'
|
import SettingsCard from '$lib/components/SettingsCard.svelte'
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
import { socket } from '$lib/stores'
|
import { socket } from '$lib/stores'
|
||||||
import type { I2CDevice } from '$lib/types/models'
|
import { Topics, type I2CDevice } from '$lib/types/models'
|
||||||
import { Connection } from '$lib/components/icons'
|
import { Connection } from '$lib/components/icons'
|
||||||
import I2CSetting from './i2cSetting.svelte'
|
import I2CSetting from './i2cSetting.svelte'
|
||||||
|
|
||||||
@@ -24,9 +24,9 @@
|
|||||||
let isLoading = $state(false)
|
let isLoading = $state(false)
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
socket.on('i2cScan', handleScan)
|
socket.on(Topics.i2cScan, handleScan)
|
||||||
triggerScan()
|
triggerScan()
|
||||||
return () => socket.off('i2cScan', handleScan)
|
return () => socket.off(Topics.i2cScan, handleScan)
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleScan = (data: any) => {
|
const handleScan = (data: any) => {
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
|
|
||||||
const triggerScan = () => {
|
const triggerScan = () => {
|
||||||
isLoading = true
|
isLoading = true
|
||||||
socket.sendEvent('i2cScan', '')
|
socket.sendEvent(Topics.i2cScan, '')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Cancel, Edit, EditOff, Power } from '$lib/components/icons'
|
import { Cancel, Edit, EditOff, Power } from '$lib/components/icons'
|
||||||
import { socket } from '$lib/stores'
|
import { socket } from '$lib/stores'
|
||||||
import type { PeripheralsConfiguration } from '$lib/types/models'
|
import { Topics, type PeripheralsConfiguration } from '$lib/types/models'
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
import { modals } from 'svelte-modals'
|
import { modals } from 'svelte-modals'
|
||||||
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte'
|
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte'
|
||||||
@@ -10,9 +10,9 @@
|
|||||||
let isEditing = $state(false)
|
let isEditing = $state(false)
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
socket.on('peripheralSettings', handleSettings)
|
socket.on(Topics.peripheralSettings, handleSettings)
|
||||||
socket.sendEvent('peripheralSettings', '')
|
socket.sendEvent(Topics.peripheralSettings, '')
|
||||||
return () => socket.off('peripheralSettings', handleSettings)
|
return () => socket.off(Topics.peripheralSettings, handleSettings)
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleSettings = (data: any) => {
|
const handleSettings = (data: any) => {
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
},
|
},
|
||||||
onConfirm: () => {
|
onConfirm: () => {
|
||||||
modals.close()
|
modals.close()
|
||||||
socket.sendEvent('peripheralSettings', settings)
|
socket.sendEvent(Topics.peripheralSettings, settings)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,51 +1,51 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import SettingsCard from '$lib/components/SettingsCard.svelte';
|
import SettingsCard from '$lib/components/SettingsCard.svelte'
|
||||||
import { imu } from '$lib/stores/imu';
|
import { imu } from '$lib/stores/imu'
|
||||||
import { Chart, registerables } from 'chart.js';
|
import { Chart, registerables } from 'chart.js'
|
||||||
import { cubicOut } from 'svelte/easing';
|
import { cubicOut } from 'svelte/easing'
|
||||||
import { slide } from 'svelte/transition';
|
import { slide } from 'svelte/transition'
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte'
|
||||||
import { socket } from '$lib/stores';
|
import { socket } from '$lib/stores'
|
||||||
import type { IMU } from '$lib/types/models';
|
import { Topics, type IMU } from '$lib/types/models'
|
||||||
import { useFeatureFlags } from '$lib/stores/featureFlags';
|
import { useFeatureFlags } from '$lib/stores/featureFlags'
|
||||||
import { Rotate3d } from '$lib/components/icons';
|
import { Rotate3d } from '$lib/components/icons'
|
||||||
|
|
||||||
Chart.register(...registerables);
|
Chart.register(...registerables)
|
||||||
|
|
||||||
const features = useFeatureFlags();
|
const features = useFeatureFlags()
|
||||||
let intervalId: ReturnType<typeof setInterval> | number;
|
let intervalId: ReturnType<typeof setInterval> | number
|
||||||
|
|
||||||
let angleChartElement: HTMLCanvasElement;
|
let angleChartElement: HTMLCanvasElement
|
||||||
let tempChartElement: HTMLCanvasElement;
|
let tempChartElement: HTMLCanvasElement
|
||||||
let altitudeChartElement: HTMLCanvasElement;
|
let altitudeChartElement: HTMLCanvasElement
|
||||||
|
|
||||||
let angleChart: Chart;
|
let angleChart: Chart
|
||||||
let tempChart: Chart;
|
let tempChart: Chart
|
||||||
let altitudeChart: Chart;
|
let altitudeChart: Chart
|
||||||
|
|
||||||
const getChartColors = () => {
|
const getChartColors = () => {
|
||||||
const style = getComputedStyle(document.body);
|
const style = getComputedStyle(document.body)
|
||||||
return {
|
return {
|
||||||
primary: style.getPropertyValue('--color-primary'),
|
primary: style.getPropertyValue('--color-primary'),
|
||||||
secondary: style.getPropertyValue('--color-secondary'),
|
secondary: style.getPropertyValue('--color-secondary'),
|
||||||
accent: style.getPropertyValue('--color-accent'),
|
accent: style.getPropertyValue('--color-accent'),
|
||||||
background: style.getPropertyValue('--color-background'),
|
background: style.getPropertyValue('--color-background')
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const createBaseChartConfig = (bgColor: string) => ({
|
const createBaseChartConfig = (bgColor: string) => ({
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
responsive: true,
|
responsive: true,
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: { display: true },
|
legend: { display: true },
|
||||||
tooltip: { mode: 'index' as const, intersect: false },
|
tooltip: { mode: 'index' as const, intersect: false }
|
||||||
},
|
},
|
||||||
elements: { point: { radius: 1 } },
|
elements: { point: { radius: 1 } },
|
||||||
scales: {
|
scales: {
|
||||||
x: {
|
x: {
|
||||||
grid: { color: bgColor },
|
grid: { color: bgColor },
|
||||||
ticks: { color: bgColor },
|
ticks: { color: bgColor },
|
||||||
display: false,
|
display: false
|
||||||
},
|
},
|
||||||
y: {
|
y: {
|
||||||
type: 'linear' as const,
|
type: 'linear' as const,
|
||||||
@@ -54,14 +54,14 @@
|
|||||||
max: 10,
|
max: 10,
|
||||||
grid: { color: bgColor },
|
grid: { color: bgColor },
|
||||||
ticks: { color: bgColor },
|
ticks: { color: bgColor },
|
||||||
border: { color: bgColor },
|
border: { color: bgColor }
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const initializeCharts = () => {
|
const initializeCharts = () => {
|
||||||
const colors = getChartColors();
|
const colors = getChartColors()
|
||||||
const baseConfig = createBaseChartConfig(colors.background);
|
const baseConfig = createBaseChartConfig(colors.background)
|
||||||
|
|
||||||
angleChart = new Chart(angleChartElement, {
|
angleChart = new Chart(angleChartElement, {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
backgroundColor: colors.primary,
|
backgroundColor: colors.primary,
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
data: $imu.x,
|
data: $imu.x,
|
||||||
yAxisID: 'y',
|
yAxisID: 'y'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'y',
|
label: 'y',
|
||||||
@@ -81,7 +81,7 @@
|
|||||||
backgroundColor: colors.secondary,
|
backgroundColor: colors.secondary,
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
data: $imu.y,
|
data: $imu.y,
|
||||||
yAxisID: 'y',
|
yAxisID: 'y'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'z',
|
label: 'z',
|
||||||
@@ -89,9 +89,9 @@
|
|||||||
backgroundColor: colors.accent,
|
backgroundColor: colors.accent,
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
data: $imu.z,
|
data: $imu.z,
|
||||||
yAxisID: 'y',
|
yAxisID: 'y'
|
||||||
},
|
}
|
||||||
],
|
]
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
@@ -103,12 +103,12 @@
|
|||||||
display: true,
|
display: true,
|
||||||
text: 'Angle [°]',
|
text: 'Angle [°]',
|
||||||
color: colors.background,
|
color: colors.background,
|
||||||
font: { size: 16, weight: 'bold' },
|
font: { size: 16, weight: 'bold' }
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
tempChart = new Chart(tempChartElement, {
|
tempChart = new Chart(tempChartElement, {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
@@ -120,9 +120,9 @@
|
|||||||
backgroundColor: colors.secondary,
|
backgroundColor: colors.secondary,
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
data: $imu.bmp_temp,
|
data: $imu.bmp_temp,
|
||||||
yAxisID: 'y',
|
yAxisID: 'y'
|
||||||
},
|
}
|
||||||
],
|
]
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
@@ -134,12 +134,12 @@
|
|||||||
display: true,
|
display: true,
|
||||||
text: 'Temperature [C°]',
|
text: 'Temperature [C°]',
|
||||||
color: colors.background,
|
color: colors.background,
|
||||||
font: { size: 16, weight: 'bold' },
|
font: { size: 16, weight: 'bold' }
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
altitudeChart = new Chart(altitudeChartElement, {
|
altitudeChart = new Chart(altitudeChartElement, {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
@@ -151,9 +151,9 @@
|
|||||||
backgroundColor: colors.primary,
|
backgroundColor: colors.primary,
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
data: $imu.altitude,
|
data: $imu.altitude,
|
||||||
yAxisID: 'y',
|
yAxisID: 'y'
|
||||||
},
|
}
|
||||||
],
|
]
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
@@ -165,55 +165,55 @@
|
|||||||
display: true,
|
display: true,
|
||||||
text: 'Altitude [M]',
|
text: 'Altitude [M]',
|
||||||
color: colors.background,
|
color: colors.background,
|
||||||
font: { size: 16, weight: 'bold' },
|
font: { size: 16, weight: 'bold' }
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
const updateChartData = (chart: Chart, data: number[], label: string) => {
|
const updateChartData = (chart: Chart, data: number[], label: string) => {
|
||||||
chart.data.labels = data;
|
chart.data.labels = data
|
||||||
chart.data.datasets[0].data = data;
|
chart.data.datasets[0].data = data
|
||||||
chart.options.scales!.y!.min = Math.min(...data) - 1;
|
chart.options.scales!.y!.min = Math.min(...data) - 1
|
||||||
chart.options.scales!.y!.max = Math.max(...data) + 1;
|
chart.options.scales!.y!.max = Math.max(...data) + 1
|
||||||
chart.update('none');
|
chart.update('none')
|
||||||
};
|
}
|
||||||
|
|
||||||
const updateData = () => {
|
const updateData = () => {
|
||||||
if ($features.imu) {
|
if ($features.imu) {
|
||||||
angleChart.data.labels = $imu.x;
|
angleChart.data.labels = $imu.x
|
||||||
angleChart.data.datasets[0].data = $imu.x;
|
angleChart.data.datasets[0].data = $imu.x
|
||||||
angleChart.data.datasets[1].data = $imu.y;
|
angleChart.data.datasets[1].data = $imu.y
|
||||||
angleChart.data.datasets[2].data = $imu.z;
|
angleChart.data.datasets[2].data = $imu.z
|
||||||
|
|
||||||
const allValues = [...$imu.x, ...$imu.y, ...$imu.z];
|
const allValues = [...$imu.x, ...$imu.y, ...$imu.z]
|
||||||
angleChart.options.scales!.y!.min = Math.min(...allValues) - 1;
|
angleChart.options.scales!.y!.min = Math.min(...allValues) - 1
|
||||||
angleChart.options.scales!.y!.max = Math.max(...allValues) + 1;
|
angleChart.options.scales!.y!.max = Math.max(...allValues) + 1
|
||||||
angleChart.update('none');
|
angleChart.update('none')
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($features.bmp) {
|
if ($features.bmp) {
|
||||||
updateChartData(tempChart, $imu.bmp_temp, 'Temperature');
|
updateChartData(tempChart, $imu.bmp_temp, 'Temperature')
|
||||||
updateChartData(altitudeChart, $imu.altitude, 'Altitude');
|
updateChartData(altitudeChart, $imu.altitude, 'Altitude')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
socket.on('imu', (data: IMU) => {
|
socket.on(Topics.imu, (data: IMU) => {
|
||||||
console.log(data);
|
console.log(data)
|
||||||
imu.addData(data);
|
imu.addData(data)
|
||||||
});
|
})
|
||||||
|
|
||||||
initializeCharts();
|
initializeCharts()
|
||||||
intervalId = setInterval(updateData, 200);
|
intervalId = setInterval(updateData, 200)
|
||||||
});
|
})
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
socket.off('imu');
|
socket.off(Topics.imu)
|
||||||
clearInterval(intervalId);
|
clearInterval(intervalId)
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SettingsCard collapsible={false}>
|
<SettingsCard collapsible={false}>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { socket } from '$lib/stores'
|
import { socket } from '$lib/stores'
|
||||||
|
import { Topics } from '$lib/types/models'
|
||||||
import { throttler as Throttler } from '$lib/utilities'
|
import { throttler as Throttler } from '$lib/utilities'
|
||||||
|
|
||||||
let { servoId = $bindable(0), pwm = $bindable(306) } = $props()
|
let { servoId = $bindable(0), pwm = $bindable(306) } = $props()
|
||||||
@@ -11,16 +12,16 @@
|
|||||||
const throttler = new Throttler()
|
const throttler = new Throttler()
|
||||||
|
|
||||||
const activateServo = () => {
|
const activateServo = () => {
|
||||||
socket.sendEvent('servoState', { active: 1 })
|
socket.sendEvent(Topics.servoState, { active: 1 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const deactivateServo = () => {
|
const deactivateServo = () => {
|
||||||
socket.sendEvent('servoState', { active: 0 })
|
socket.sendEvent(Topics.servoState, { active: 0 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatePWM = () => {
|
const updatePWM = () => {
|
||||||
throttler.throttle(() => {
|
throttler.throttle(() => {
|
||||||
socket.sendEvent('servoPWM', { servo_id: servoId, pwm })
|
socket.sendEvent(Topics.servoPWM, { servo_id: servoId, pwm })
|
||||||
}, 10)
|
}, 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte'
|
||||||
import { modals } from 'svelte-modals';
|
import { modals } from 'svelte-modals'
|
||||||
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte';
|
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte'
|
||||||
import SettingsCard from '$lib/components/SettingsCard.svelte';
|
import SettingsCard from '$lib/components/SettingsCard.svelte'
|
||||||
import Spinner from '$lib/components/Spinner.svelte';
|
import Spinner from '$lib/components/Spinner.svelte'
|
||||||
import { slide } from 'svelte/transition';
|
import { slide } from 'svelte/transition'
|
||||||
import { cubicOut } from 'svelte/easing';
|
import { cubicOut } from 'svelte/easing'
|
||||||
import type { SystemInformation, Analytics } from '$lib/types/models';
|
import { type SystemInformation, type Analytics, Topics } from '$lib/types/models'
|
||||||
import { socket } from '$lib/stores/socket';
|
import { socket } from '$lib/stores/socket'
|
||||||
import { api } from '$lib/api';
|
import { api } from '$lib/api'
|
||||||
import { convertSeconds } from '$lib/utilities';
|
import { convertSeconds } from '$lib/utilities'
|
||||||
import { useFeatureFlags } from '$lib/stores/featureFlags';
|
import { useFeatureFlags } from '$lib/stores/featureFlags'
|
||||||
import {
|
import {
|
||||||
Cancel,
|
Cancel,
|
||||||
Power,
|
Power,
|
||||||
@@ -27,42 +27,42 @@
|
|||||||
Flash,
|
Flash,
|
||||||
Folder,
|
Folder,
|
||||||
Temperature,
|
Temperature,
|
||||||
Stopwatch,
|
Stopwatch
|
||||||
} from '$lib/components/icons';
|
} from '$lib/components/icons'
|
||||||
import StatusItem from '$lib/components/StatusItem.svelte';
|
import StatusItem from '$lib/components/StatusItem.svelte'
|
||||||
import ActionButton from './ActionButton.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() {
|
async function getSystemStatus() {
|
||||||
const result = await api.get<SystemInformation>('/api/system/status');
|
const result = await api.get<SystemInformation>('/api/system/status')
|
||||||
if (result.isErr()) {
|
if (result.isErr()) {
|
||||||
console.error('Error:', result.inner);
|
console.error('Error:', result.inner)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
systemInformation = result.inner;
|
systemInformation = result.inner
|
||||||
return systemInformation;
|
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('analytics', handleSystemData));
|
onMount(() => socket.on(Topics.analytics, handleSystemData))
|
||||||
|
|
||||||
onDestroy(() => socket.off('analytics', handleSystemData));
|
onDestroy(() => socket.off(Topics.analytics, handleSystemData))
|
||||||
const handleSystemData = (data: Analytics) => {
|
const handleSystemData = (data: Analytics) => {
|
||||||
if (systemInformation) {
|
if (systemInformation) {
|
||||||
systemInformation = {
|
systemInformation = {
|
||||||
...systemInformation,
|
...systemInformation,
|
||||||
...(data as unknown as 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() {
|
function confirmRestart() {
|
||||||
modals.open(ConfirmDialog, {
|
modals.open(ConfirmDialog, {
|
||||||
@@ -70,13 +70,13 @@
|
|||||||
message: 'Are you sure you want to restart the device?',
|
message: 'Are you sure you want to restart the device?',
|
||||||
labels: {
|
labels: {
|
||||||
cancel: { label: 'Abort', icon: Cancel },
|
cancel: { label: 'Abort', icon: Cancel },
|
||||||
confirm: { label: 'Restart', icon: Power },
|
confirm: { label: 'Restart', icon: Power }
|
||||||
},
|
},
|
||||||
onConfirm: () => {
|
onConfirm: () => {
|
||||||
modals.close();
|
modals.close()
|
||||||
postRestart();
|
postRestart()
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmReset() {
|
function confirmReset() {
|
||||||
@@ -85,13 +85,13 @@
|
|||||||
message: 'Are you sure you want to reset the device to its factory defaults?',
|
message: 'Are you sure you want to reset the device to its factory defaults?',
|
||||||
labels: {
|
labels: {
|
||||||
cancel: { label: 'Abort', icon: Cancel },
|
cancel: { label: 'Abort', icon: Cancel },
|
||||||
confirm: { label: 'Factory Reset', icon: FactoryReset },
|
confirm: { label: 'Factory Reset', icon: FactoryReset }
|
||||||
},
|
},
|
||||||
onConfirm: () => {
|
onConfirm: () => {
|
||||||
modals.close();
|
modals.close()
|
||||||
postFactoryReset();
|
postFactoryReset()
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmSleep() {
|
function confirmSleep() {
|
||||||
@@ -100,21 +100,21 @@
|
|||||||
message: 'Are you sure you want to put the device into sleep?',
|
message: 'Are you sure you want to put the device into sleep?',
|
||||||
labels: {
|
labels: {
|
||||||
cancel: { label: 'Abort', icon: Cancel },
|
cancel: { label: 'Abort', icon: Cancel },
|
||||||
confirm: { label: 'Sleep', icon: Sleep },
|
confirm: { label: 'Sleep', icon: Sleep }
|
||||||
},
|
},
|
||||||
onConfirm: () => {
|
onConfirm: () => {
|
||||||
modals.close();
|
modals.close()
|
||||||
postSleep();
|
postSleep()
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ActionButtonDef {
|
interface ActionButtonDef {
|
||||||
icon: any;
|
icon: any
|
||||||
label: string;
|
label: string
|
||||||
onClick: () => void;
|
onClick: () => void
|
||||||
type?: string;
|
type?: string
|
||||||
condition?: () => boolean;
|
condition?: () => boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const actionButtons: ActionButtonDef[] = [
|
const actionButtons: ActionButtonDef[] = [
|
||||||
@@ -122,20 +122,20 @@
|
|||||||
icon: Sleep,
|
icon: Sleep,
|
||||||
label: 'Sleep',
|
label: 'Sleep',
|
||||||
onClick: confirmSleep,
|
onClick: confirmSleep,
|
||||||
condition: () => Boolean($features.sleep),
|
condition: () => Boolean($features.sleep)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: Power,
|
icon: Power,
|
||||||
label: 'Restart',
|
label: 'Restart',
|
||||||
onClick: confirmRestart,
|
onClick: confirmRestart
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: FactoryReset,
|
icon: FactoryReset,
|
||||||
label: 'Factory Reset',
|
label: 'Factory Reset',
|
||||||
onClick: confirmReset,
|
onClick: confirmReset,
|
||||||
type: 'secondary',
|
type: 'secondary'
|
||||||
},
|
}
|
||||||
];
|
]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SettingsCard collapsible={false}>
|
<SettingsCard collapsible={false}>
|
||||||
|
|||||||
+131
-126
@@ -1,19 +1,24 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount, onDestroy } from 'svelte';
|
import { onMount, onDestroy } from 'svelte'
|
||||||
import { modals } from 'svelte-modals';
|
import { modals } from 'svelte-modals'
|
||||||
import { slide } from 'svelte/transition';
|
import { slide } from 'svelte/transition'
|
||||||
import { cubicOut } from 'svelte/easing';
|
import { cubicOut } from 'svelte/easing'
|
||||||
import { notifications } from '$lib/components/toasts/notifications';
|
import { notifications } from '$lib/components/toasts/notifications'
|
||||||
import DragDropList, { VerticalDropZone, reorder, type DropEvent } from 'svelte-dnd-list';
|
import DragDropList, { VerticalDropZone, reorder, type DropEvent } from 'svelte-dnd-list'
|
||||||
import SettingsCard from '$lib/components/SettingsCard.svelte';
|
import SettingsCard from '$lib/components/SettingsCard.svelte'
|
||||||
import { PasswordInput } from '$lib/components/input';
|
import { PasswordInput } from '$lib/components/input'
|
||||||
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte';
|
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte'
|
||||||
import ScanNetworks from './Scan.svelte';
|
import ScanNetworks from './Scan.svelte'
|
||||||
import Spinner from '$lib/components/Spinner.svelte';
|
import Spinner from '$lib/components/Spinner.svelte'
|
||||||
import InfoDialog from '$lib/components/InfoDialog.svelte';
|
import InfoDialog from '$lib/components/InfoDialog.svelte'
|
||||||
import type { KnownNetworkItem, WifiSettings, WifiStatus } from '$lib/types/models';
|
import {
|
||||||
import { socket } from '$lib/stores';
|
Topics,
|
||||||
import { api } from '$lib/api';
|
type KnownNetworkItem,
|
||||||
|
type WifiSettings,
|
||||||
|
type WifiStatus
|
||||||
|
} from '$lib/types/models'
|
||||||
|
import { socket } from '$lib/stores'
|
||||||
|
import { api } from '$lib/api'
|
||||||
import {
|
import {
|
||||||
Cancel,
|
Cancel,
|
||||||
Delete,
|
Delete,
|
||||||
@@ -31,9 +36,9 @@
|
|||||||
DNS,
|
DNS,
|
||||||
Add,
|
Add,
|
||||||
Scan,
|
Scan,
|
||||||
Edit,
|
Edit
|
||||||
} from '$lib/components/icons';
|
} from '$lib/components/icons'
|
||||||
import StatusItem from '$lib/components/StatusItem.svelte';
|
import StatusItem from '$lib/components/StatusItem.svelte'
|
||||||
|
|
||||||
let networkEditable: KnownNetworkItem = $state({
|
let networkEditable: KnownNetworkItem = $state({
|
||||||
ssid: '',
|
ssid: '',
|
||||||
@@ -43,22 +48,22 @@
|
|||||||
subnet_mask: undefined,
|
subnet_mask: undefined,
|
||||||
gateway_ip: undefined,
|
gateway_ip: undefined,
|
||||||
dns_ip_1: undefined,
|
dns_ip_1: undefined,
|
||||||
dns_ip_2: undefined,
|
dns_ip_2: undefined
|
||||||
});
|
})
|
||||||
|
|
||||||
let static_ip_config = $state(false);
|
let static_ip_config = $state(false)
|
||||||
|
|
||||||
let newNetwork: boolean = $state(true);
|
let newNetwork: boolean = $state(true)
|
||||||
let showNetworkEditor: boolean = $state(false);
|
let showNetworkEditor: boolean = $state(false)
|
||||||
|
|
||||||
let wifiStatus: WifiStatus | null = $state(null);
|
let wifiStatus: WifiStatus | null = $state(null)
|
||||||
let wifiSettings: WifiSettings | null = $state(null);
|
let wifiSettings: WifiSettings | null = $state(null)
|
||||||
|
|
||||||
let dndNetworkList: KnownNetworkItem[] = $state([]);
|
let dndNetworkList: KnownNetworkItem[] = $state([])
|
||||||
|
|
||||||
let showWifiDetails = $state(false);
|
let showWifiDetails = $state(false)
|
||||||
|
|
||||||
let formField: any = $state();
|
let formField: any = $state()
|
||||||
|
|
||||||
let formErrors = $state({
|
let formErrors = $state({
|
||||||
ssid: false,
|
ssid: false,
|
||||||
@@ -66,157 +71,157 @@
|
|||||||
gateway_ip: false,
|
gateway_ip: false,
|
||||||
subnet_mask: false,
|
subnet_mask: false,
|
||||||
dns_1: false,
|
dns_1: false,
|
||||||
dns_2: false,
|
dns_2: false
|
||||||
});
|
})
|
||||||
|
|
||||||
let formErrorhostname = $state(false);
|
let formErrorhostname = $state(false)
|
||||||
|
|
||||||
async function getWifiStatus() {
|
async function getWifiStatus() {
|
||||||
const result = await api.get<WifiStatus>('/api/wifi/sta/status');
|
const result = await api.get<WifiStatus>('/api/wifi/sta/status')
|
||||||
if (result.isErr()) {
|
if (result.isErr()) {
|
||||||
console.error(`Error occurred while fetching: `, result.inner);
|
console.error(`Error occurred while fetching: `, result.inner)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
wifiStatus = result.inner;
|
wifiStatus = result.inner
|
||||||
return wifiStatus;
|
return wifiStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getWifiSettings() {
|
async function getWifiSettings() {
|
||||||
const result = await api.get<WifiSettings>('/api/wifi/sta/settings');
|
const result = await api.get<WifiSettings>('/api/wifi/sta/settings')
|
||||||
if (result.isErr()) {
|
if (result.isErr()) {
|
||||||
console.error(`Error occurred while fetching: `, result.inner);
|
console.error(`Error occurred while fetching: `, result.inner)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
wifiSettings = result.inner;
|
wifiSettings = result.inner
|
||||||
dndNetworkList = wifiSettings.wifi_networks;
|
dndNetworkList = wifiSettings.wifi_networks
|
||||||
return wifiSettings;
|
return wifiSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
onDestroy(() => socket.off('WiFiSettings'));
|
onDestroy(() => socket.off(Topics.WiFiSettings))
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
socket.on<WifiSettings>('WiFiSettings', data => {
|
socket.on<WifiSettings>(Topics.WiFiSettings, data => {
|
||||||
wifiSettings = data;
|
wifiSettings = data
|
||||||
dndNetworkList = wifiSettings.wifi_networks;
|
dndNetworkList = wifiSettings.wifi_networks
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
async function postWiFiSettings(data: WifiSettings) {
|
async function postWiFiSettings(data: WifiSettings) {
|
||||||
const result = await api.post<WifiSettings>('/api/wifi/sta/settings', data);
|
const result = await api.post<WifiSettings>('/api/wifi/sta/settings', data)
|
||||||
if (result.isErr()) {
|
if (result.isErr()) {
|
||||||
console.error(`Error occurred while fetching: `, result.inner);
|
console.error(`Error occurred while fetching: `, result.inner)
|
||||||
notifications.error('User not authorized.', 3000);
|
notifications.error('User not authorized.', 3000)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
wifiSettings = result.inner;
|
wifiSettings = result.inner
|
||||||
notifications.success('Wi-Fi settings updated.', 3000);
|
notifications.success('Wi-Fi settings updated.', 3000)
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateHostName() {
|
function validateHostName() {
|
||||||
if (!wifiSettings) return false;
|
if (!wifiSettings) return false
|
||||||
if (wifiSettings.hostname.length < 3 || wifiSettings.hostname.length > 32) {
|
if (wifiSettings.hostname.length < 3 || wifiSettings.hostname.length > 32) {
|
||||||
formErrorhostname = true;
|
formErrorhostname = true
|
||||||
} else {
|
} else {
|
||||||
formErrorhostname = false;
|
formErrorhostname = false
|
||||||
// Update global wifiSettings object
|
// Update global wifiSettings object
|
||||||
wifiSettings.wifi_networks = dndNetworkList;
|
wifiSettings.wifi_networks = dndNetworkList
|
||||||
// Post to REST API
|
// Post to REST API
|
||||||
postWiFiSettings(wifiSettings);
|
postWiFiSettings(wifiSettings)
|
||||||
console.log(wifiSettings);
|
console.log(wifiSettings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateWiFiForm(event: SubmitEvent) {
|
function validateWiFiForm(event: SubmitEvent) {
|
||||||
event.preventDefault();
|
event.preventDefault()
|
||||||
let valid = true;
|
let valid = true
|
||||||
|
|
||||||
// Validate SSID
|
// Validate SSID
|
||||||
if (networkEditable.ssid.length < 3 || networkEditable.ssid.length > 32) {
|
if (networkEditable.ssid.length < 3 || networkEditable.ssid.length > 32) {
|
||||||
valid = false;
|
valid = false
|
||||||
formErrors.ssid = true;
|
formErrors.ssid = true
|
||||||
} else {
|
} else {
|
||||||
formErrors.ssid = false;
|
formErrors.ssid = false
|
||||||
}
|
}
|
||||||
|
|
||||||
networkEditable.static_ip_config = static_ip_config;
|
networkEditable.static_ip_config = static_ip_config
|
||||||
|
|
||||||
if (networkEditable.static_ip_config) {
|
if (networkEditable.static_ip_config) {
|
||||||
// RegEx for IPv4
|
// RegEx for IPv4
|
||||||
const regexExp =
|
const regexExp =
|
||||||
/\b(?:(?:2(?:[0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9])\.){3}(?:(?:2([0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9]))\b/;
|
/\b(?:(?:2(?:[0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9])\.){3}(?:(?:2([0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9]))\b/
|
||||||
|
|
||||||
// Validate gateway IP
|
// Validate gateway IP
|
||||||
if (!regexExp.test(networkEditable.gateway_ip!)) {
|
if (!regexExp.test(networkEditable.gateway_ip!)) {
|
||||||
valid = false;
|
valid = false
|
||||||
formErrors.gateway_ip = true;
|
formErrors.gateway_ip = true
|
||||||
} else {
|
} else {
|
||||||
formErrors.gateway_ip = false;
|
formErrors.gateway_ip = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate Subnet Mask
|
// Validate Subnet Mask
|
||||||
if (!regexExp.test(networkEditable.subnet_mask!)) {
|
if (!regexExp.test(networkEditable.subnet_mask!)) {
|
||||||
valid = false;
|
valid = false
|
||||||
formErrors.subnet_mask = true;
|
formErrors.subnet_mask = true
|
||||||
} else {
|
} else {
|
||||||
formErrors.subnet_mask = false;
|
formErrors.subnet_mask = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate local IP
|
// Validate local IP
|
||||||
if (!regexExp.test(networkEditable.local_ip!)) {
|
if (!regexExp.test(networkEditable.local_ip!)) {
|
||||||
valid = false;
|
valid = false
|
||||||
formErrors.local_ip = true;
|
formErrors.local_ip = true
|
||||||
} else {
|
} else {
|
||||||
formErrors.local_ip = false;
|
formErrors.local_ip = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate DNS 1
|
// Validate DNS 1
|
||||||
if (!regexExp.test(networkEditable.dns_ip_1!)) {
|
if (!regexExp.test(networkEditable.dns_ip_1!)) {
|
||||||
valid = false;
|
valid = false
|
||||||
formErrors.dns_1 = true;
|
formErrors.dns_1 = true
|
||||||
} else {
|
} else {
|
||||||
formErrors.dns_1 = false;
|
formErrors.dns_1 = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate DNS 2
|
// Validate DNS 2
|
||||||
if (!regexExp.test(networkEditable.dns_ip_2!)) {
|
if (!regexExp.test(networkEditable.dns_ip_2!)) {
|
||||||
valid = false;
|
valid = false
|
||||||
formErrors.dns_2 = true;
|
formErrors.dns_2 = true
|
||||||
} else {
|
} else {
|
||||||
formErrors.dns_2 = false;
|
formErrors.dns_2 = false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
formErrors.local_ip = false;
|
formErrors.local_ip = false
|
||||||
formErrors.subnet_mask = false;
|
formErrors.subnet_mask = false
|
||||||
formErrors.gateway_ip = false;
|
formErrors.gateway_ip = false
|
||||||
formErrors.dns_1 = false;
|
formErrors.dns_1 = false
|
||||||
formErrors.dns_2 = false;
|
formErrors.dns_2 = false
|
||||||
}
|
}
|
||||||
// Submit JSON to REST API
|
// Submit JSON to REST API
|
||||||
if (valid) {
|
if (valid) {
|
||||||
if (newNetwork) {
|
if (newNetwork) {
|
||||||
dndNetworkList.push(networkEditable);
|
dndNetworkList.push(networkEditable)
|
||||||
} else {
|
} else {
|
||||||
dndNetworkList.splice(dndNetworkList.indexOf(networkEditable), 1, networkEditable);
|
dndNetworkList.splice(dndNetworkList.indexOf(networkEditable), 1, networkEditable)
|
||||||
}
|
}
|
||||||
addNetwork();
|
addNetwork()
|
||||||
dndNetworkList = [...dndNetworkList]; //Trigger reactivity
|
dndNetworkList = [...dndNetworkList] //Trigger reactivity
|
||||||
showNetworkEditor = false;
|
showNetworkEditor = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function scanForNetworks() {
|
function scanForNetworks() {
|
||||||
modals.open(ScanNetworks, {
|
modals.open(ScanNetworks, {
|
||||||
storeNetwork: (network: string) => {
|
storeNetwork: (network: string) => {
|
||||||
addNetwork();
|
addNetwork()
|
||||||
networkEditable.ssid = network;
|
networkEditable.ssid = network
|
||||||
showNetworkEditor = true;
|
showNetworkEditor = true
|
||||||
modals.close();
|
modals.close()
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function addNetwork() {
|
function addNetwork() {
|
||||||
newNetwork = true;
|
newNetwork = true
|
||||||
networkEditable = {
|
networkEditable = {
|
||||||
ssid: '',
|
ssid: '',
|
||||||
password: '',
|
password: '',
|
||||||
@@ -225,14 +230,14 @@
|
|||||||
subnet_mask: undefined,
|
subnet_mask: undefined,
|
||||||
gateway_ip: undefined,
|
gateway_ip: undefined,
|
||||||
dns_ip_1: undefined,
|
dns_ip_1: undefined,
|
||||||
dns_ip_2: undefined,
|
dns_ip_2: undefined
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleEdit(index: number) {
|
function handleEdit(index: number) {
|
||||||
newNetwork = false;
|
newNetwork = false
|
||||||
showNetworkEditor = true;
|
showNetworkEditor = true
|
||||||
networkEditable = dndNetworkList[index];
|
networkEditable = dndNetworkList[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmDelete(index: number) {
|
function confirmDelete(index: number) {
|
||||||
@@ -241,20 +246,20 @@
|
|||||||
message: 'Are you sure you want to delete this network?',
|
message: 'Are you sure you want to delete this network?',
|
||||||
labels: {
|
labels: {
|
||||||
cancel: { label: 'Cancel', icon: Cancel },
|
cancel: { label: 'Cancel', icon: Cancel },
|
||||||
confirm: { label: 'Delete', icon: Delete },
|
confirm: { label: 'Delete', icon: Delete }
|
||||||
},
|
},
|
||||||
onConfirm: () => {
|
onConfirm: () => {
|
||||||
// Check if network is currently been edited and delete as well
|
// Check if network is currently been edited and delete as well
|
||||||
if (dndNetworkList[index].ssid === networkEditable.ssid) {
|
if (dndNetworkList[index].ssid === networkEditable.ssid) {
|
||||||
addNetwork();
|
addNetwork()
|
||||||
}
|
}
|
||||||
// Remove network from array
|
// Remove network from array
|
||||||
dndNetworkList.splice(index, 1);
|
dndNetworkList.splice(index, 1)
|
||||||
dndNetworkList = [...dndNetworkList]; //Trigger reactivity
|
dndNetworkList = [...dndNetworkList] //Trigger reactivity
|
||||||
showNetworkEditor = false;
|
showNetworkEditor = false
|
||||||
modals.close();
|
modals.close()
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkNetworkList() {
|
function checkNetworkList() {
|
||||||
@@ -264,21 +269,21 @@
|
|||||||
message:
|
message:
|
||||||
'You have reached the maximum number of networks. Please delete one to add another.',
|
'You have reached the maximum number of networks. Please delete one to add another.',
|
||||||
dismiss: { label: 'OK', icon: Check },
|
dismiss: { label: 'OK', icon: Check },
|
||||||
onDismiss: () => modals.close(),
|
onDismiss: () => modals.close()
|
||||||
});
|
})
|
||||||
return false;
|
return false
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDrop({ detail: { from, to } }: CustomEvent<DropEvent>) {
|
function onDrop({ detail: { from, to } }: CustomEvent<DropEvent>) {
|
||||||
if (!to || from === to) {
|
if (!to || from === to) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
dndNetworkList = reorder(dndNetworkList, from.index, to.index);
|
dndNetworkList = reorder(dndNetworkList, from.index, to.index)
|
||||||
console.log(dndNetworkList);
|
console.log(dndNetworkList)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -312,7 +317,7 @@
|
|||||||
<button
|
<button
|
||||||
class="btn btn-circle btn-ghost btn-sm modal-button"
|
class="btn btn-circle btn-ghost btn-sm modal-button"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
showWifiDetails = !showWifiDetails;
|
showWifiDetails = !showWifiDetails
|
||||||
}}>
|
}}>
|
||||||
<Down
|
<Down
|
||||||
class="text-base-content h-auto w-6 transition-transform duration-300 ease-in-out {(
|
class="text-base-content h-auto w-6 transition-transform duration-300 ease-in-out {(
|
||||||
@@ -359,8 +364,8 @@
|
|||||||
class="btn btn-primary text-primary-content btn-md absolute -top-14 right-16"
|
class="btn btn-primary text-primary-content btn-md absolute -top-14 right-16"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
if (checkNetworkList()) {
|
if (checkNetworkList()) {
|
||||||
addNetwork();
|
addNetwork()
|
||||||
showNetworkEditor = true;
|
showNetworkEditor = true
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<Add class="h-6 w-6" /></button>
|
<Add class="h-6 w-6" /></button>
|
||||||
@@ -368,8 +373,8 @@
|
|||||||
class="btn btn-primary text-primary-content btn-md absolute -top-14 right-0"
|
class="btn btn-primary text-primary-content btn-md absolute -top-14 right-0"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
if (checkNetworkList()) {
|
if (checkNetworkList()) {
|
||||||
scanForNetworks();
|
scanForNetworks()
|
||||||
showNetworkEditor = true;
|
showNetworkEditor = true
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<Scan class="h-6 w-6" /></button>
|
<Scan class="h-6 w-6" /></button>
|
||||||
@@ -389,13 +394,13 @@
|
|||||||
<button
|
<button
|
||||||
class="btn btn-ghost btn-sm"
|
class="btn btn-ghost btn-sm"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
handleEdit(index);
|
handleEdit(index)
|
||||||
}}>
|
}}>
|
||||||
<Edit class="h-6 w-6" /></button>
|
<Edit class="h-6 w-6" /></button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-ghost btn-sm"
|
class="btn btn-ghost btn-sm"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
confirmDelete(index);
|
confirmDelete(index)
|
||||||
}}>
|
}}>
|
||||||
<Delete class="text-error h-6 w-6" />
|
<Delete class="text-error h-6 w-6" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user