From d611cd043b7e3e970a195bf800311862ac13cb78 Mon Sep 17 00:00:00 2001 From: Rune Harlyk Date: Sat, 3 Jan 2026 20:48:15 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Make=20ip=20be=20uint32=20instead?= =?UTF-8?q?=20of=20strings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/lib/types/models.ts | 28 +++---- app/src/lib/utilities/index.ts | 1 + app/src/lib/utilities/ip-utilities.ts | 23 ++++++ app/src/routes/wifi/ap/Accesspoint.svelte | 44 ++++++----- app/src/routes/wifi/sta/Wifi.svelte | 95 ++++++++++++++--------- esp32/include/settings/ap_settings.h | 19 +++-- esp32/include/settings/mdns_settings.h | 1 - esp32/include/settings/wifi_settings.h | 21 +++-- esp32/include/utils/json_utils.h | 29 ------- esp32/src/ap_service.cpp | 2 +- esp32/src/system_service.cpp | 12 +-- esp32/src/wifi_service.cpp | 10 +-- platform_shared/message.proto | 2 +- 13 files changed, 156 insertions(+), 131 deletions(-) create mode 100644 app/src/lib/utilities/ip-utilities.ts delete mode 100644 esp32/include/utils/json_utils.h diff --git a/app/src/lib/types/models.ts b/app/src/lib/types/models.ts index 2827afe..e3cf7f5 100644 --- a/app/src/lib/types/models.ts +++ b/app/src/lib/types/models.ts @@ -19,27 +19,27 @@ export enum MessageTopic { export type WifiStatus = { status: number - local_ip: string + local_ip: number mac_address: string rssi: number ssid: string bssid: string channel: number - subnet_mask: string - gateway_ip: string - dns_ip_1: string - dns_ip_2?: string + subnet_mask: number + gateway_ip: number + dns_ip_1: number + dns_ip_2?: number } export type KnownNetworkItem = { ssid: string password: string static_ip_config: boolean - local_ip?: string - subnet_mask?: string - gateway_ip?: string - dns_ip_1?: string - dns_ip_2?: string + local_ip?: number + subnet_mask?: number + gateway_ip?: number + dns_ip_1?: number + dns_ip_2?: number } export type WifiSettings = { @@ -73,7 +73,7 @@ export type NetworkItem = { export type ApStatus = { status: number - ip_address: string + ip_address: number mac_address: string station_num: number } @@ -85,9 +85,9 @@ export type ApSettings = { channel: number ssid_hidden: boolean max_clients: number - local_ip: string - gateway_ip: string - subnet_mask: string + local_ip: number + gateway_ip: number + subnet_mask: number } export type Rssi = { diff --git a/app/src/lib/utilities/index.ts b/app/src/lib/utilities/index.ts index 87d93b2..9b6b7d0 100644 --- a/app/src/lib/utilities/index.ts +++ b/app/src/lib/utilities/index.ts @@ -6,3 +6,4 @@ export * from './buffer-utilities' export * from './model-utilities' export * from './string-utilities' export * from './color-utilities' +export * from './ip-utilities' diff --git a/app/src/lib/utilities/ip-utilities.ts b/app/src/lib/utilities/ip-utilities.ts new file mode 100644 index 0000000..11372dd --- /dev/null +++ b/app/src/lib/utilities/ip-utilities.ts @@ -0,0 +1,23 @@ +export function ipToUint32(ip: string): number { + const parts = ip.split('.') + if (parts.length !== 4) return 0 + return ( + (parseInt(parts[0], 10) | + (parseInt(parts[1], 10) << 8) | + (parseInt(parts[2], 10) << 16) | + (parseInt(parts[3], 10) << 24)) >>> + 0 + ) +} + +export function uint32ToIp(ip: number): string { + return [ip & 0xff, (ip >>> 8) & 0xff, (ip >>> 16) & 0xff, (ip >>> 24) & 0xff].join('.') +} + +export function isValidIpString(ip: string | undefined): boolean { + if (!ip) return false + 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/ + return regexExp.test(ip) +} + diff --git a/app/src/routes/wifi/ap/Accesspoint.svelte b/app/src/routes/wifi/ap/Accesspoint.svelte index fb60aec..a2741dd 100644 --- a/app/src/routes/wifi/ap/Accesspoint.svelte +++ b/app/src/routes/wifi/ap/Accesspoint.svelte @@ -8,12 +8,19 @@ import Spinner from '$lib/components/Spinner.svelte' import type { ApSettings, ApStatus } from '$lib/types/models' import { api } from '$lib/api' + import { ipToUint32, uint32ToIp, isValidIpString } from '$lib/utilities' import { AP, Devices, Home, MAC } from '$lib/components/icons' import StatusItem from '$lib/components/StatusItem.svelte' let apSettings: ApSettings | null = $state(null) let apStatus: ApStatus | null = $state(null) + let ipDisplay = $state({ + local_ip: '', + gateway_ip: '', + subnet_mask: '' + }) + let formField: Record = $state({}) async function getAPStatus() { @@ -33,6 +40,11 @@ return } apSettings = result.inner + ipDisplay = { + local_ip: uint32ToIp(apSettings.local_ip), + gateway_ip: uint32ToIp(apSettings.gateway_ip), + subnet_mask: uint32ToIp(apSettings.subnet_mask) + } return apSettings } @@ -90,7 +102,6 @@ if (!apSettings) return let valid = true - // Validate SSID if (apSettings.ssid.length < 3 || apSettings.ssid.length > 32) { valid = false formErrors.ssid = true @@ -98,7 +109,6 @@ formErrors.ssid = false } - // Validate Channel let channel = Number(apSettings.channel) if (1 > channel || channel > 13) { valid = false @@ -107,7 +117,6 @@ formErrors.channel = false } - // Validate max_clients let maxClients = Number(apSettings.max_clients) if (1 > maxClients || maxClients > 8) { valid = false @@ -116,36 +125,31 @@ formErrors.max_clients = false } - // RegEx for IPv4 - 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/ - - // Validate gateway IP - if (!regexExp.test(apSettings.gateway_ip)) { + if (!isValidIpString(ipDisplay.gateway_ip)) { valid = false formErrors.gateway_ip = true } else { formErrors.gateway_ip = false } - // Validate Subnet Mask - if (!regexExp.test(apSettings.subnet_mask)) { + if (!isValidIpString(ipDisplay.subnet_mask)) { valid = false formErrors.subnet_mask = true } else { formErrors.subnet_mask = false } - // Validate local IP - if (!regexExp.test(apSettings.local_ip)) { + if (!isValidIpString(ipDisplay.local_ip)) { valid = false formErrors.local_ip = true } else { formErrors.local_ip = false } - // Submit JSON to REST API if (valid) { + apSettings.local_ip = ipToUint32(ipDisplay.local_ip) + apSettings.gateway_ip = ipToUint32(ipDisplay.gateway_ip) + apSettings.subnet_mask = ipToUint32(ipDisplay.subnet_mask) postAPSettings(apSettings) } } @@ -174,7 +178,11 @@ description={apStatusDescription[apStatus.status]} /> - + @@ -319,7 +327,7 @@ minlength="7" maxlength="15" size="15" - bind:value={apSettings.local_ip} + bind:value={ipDisplay.local_ip} id="localIP" required /> @@ -344,7 +352,7 @@ minlength="7" maxlength="15" size="15" - bind:value={apSettings.gateway_ip} + bind:value={ipDisplay.gateway_ip} id="gateway" required /> @@ -368,7 +376,7 @@ minlength="7" maxlength="15" size="15" - bind:value={apSettings.subnet_mask} + bind:value={ipDisplay.subnet_mask} id="subnet" required /> diff --git a/app/src/routes/wifi/sta/Wifi.svelte b/app/src/routes/wifi/sta/Wifi.svelte index 31338aa..f749cb7 100644 --- a/app/src/routes/wifi/sta/Wifi.svelte +++ b/app/src/routes/wifi/sta/Wifi.svelte @@ -12,6 +12,7 @@ import InfoDialog from '$lib/components/InfoDialog.svelte' import { type KnownNetworkItem, type WifiSettings, type WifiStatus } from '$lib/types/models' import { api } from '$lib/api' + import { ipToUint32, uint32ToIp, isValidIpString } from '$lib/utilities' import { Cancel, Delete, @@ -37,11 +38,19 @@ ssid: '', password: '', static_ip_config: false, - local_ip: undefined, - subnet_mask: undefined, - gateway_ip: undefined, - dns_ip_1: undefined, - dns_ip_2: undefined + local_ip: 0, + subnet_mask: 0, + gateway_ip: 0, + dns_ip_1: 0, + dns_ip_2: 0 + }) + + let ipDisplay = $state({ + local_ip: '', + subnet_mask: '', + gateway_ip: '', + dns_ip_1: '', + dns_ip_2: '' }) let static_ip_config = $state(false) @@ -119,7 +128,6 @@ event.preventDefault() let valid = true - // Validate SSID if (networkEditable.ssid.length < 3 || networkEditable.ssid.length > 32) { valid = false formErrors.ssid = true @@ -130,49 +138,46 @@ networkEditable.static_ip_config = static_ip_config if (networkEditable.static_ip_config) { - // RegEx for IPv4 - 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/ - - // Validate gateway IP - if (!regexExp.test(networkEditable.gateway_ip!)) { + if (!isValidIpString(ipDisplay.gateway_ip)) { valid = false formErrors.gateway_ip = true } else { formErrors.gateway_ip = false } - // Validate Subnet Mask - if (!regexExp.test(networkEditable.subnet_mask!)) { + if (!isValidIpString(ipDisplay.subnet_mask)) { valid = false formErrors.subnet_mask = true } else { formErrors.subnet_mask = false } - // Validate local IP - if (!regexExp.test(networkEditable.local_ip!)) { + if (!isValidIpString(ipDisplay.local_ip)) { valid = false formErrors.local_ip = true } else { formErrors.local_ip = false } - // Validate DNS 1 - if (!regexExp.test(networkEditable.dns_ip_1!)) { + if (!isValidIpString(ipDisplay.dns_ip_1)) { valid = false formErrors.dns_1 = true } else { formErrors.dns_1 = false } - // Validate DNS 2 - if (!regexExp.test(networkEditable.dns_ip_2!)) { + if (!isValidIpString(ipDisplay.dns_ip_2)) { valid = false formErrors.dns_2 = true } else { formErrors.dns_2 = false } + + networkEditable.local_ip = ipToUint32(ipDisplay.local_ip) + networkEditable.subnet_mask = ipToUint32(ipDisplay.subnet_mask) + networkEditable.gateway_ip = ipToUint32(ipDisplay.gateway_ip) + networkEditable.dns_ip_1 = ipToUint32(ipDisplay.dns_ip_1) + networkEditable.dns_ip_2 = ipToUint32(ipDisplay.dns_ip_2) } else { formErrors.local_ip = false formErrors.subnet_mask = false @@ -180,7 +185,7 @@ formErrors.dns_1 = false formErrors.dns_2 = false } - // Submit JSON to REST API + if (valid) { if (newNetwork) { dndNetworkList.push(networkEditable) @@ -188,7 +193,7 @@ dndNetworkList.splice(dndNetworkList.indexOf(networkEditable), 1, networkEditable) } addNetwork() - dndNetworkList = [...dndNetworkList] //Trigger reactivity + dndNetworkList = [...dndNetworkList] showNetworkEditor = false } } @@ -210,11 +215,18 @@ ssid: '', password: '', static_ip_config: false, - local_ip: undefined, - subnet_mask: undefined, - gateway_ip: undefined, - dns_ip_1: undefined, - dns_ip_2: undefined + local_ip: 0, + subnet_mask: 0, + gateway_ip: 0, + dns_ip_1: 0, + dns_ip_2: 0 + } + ipDisplay = { + local_ip: '', + subnet_mask: '', + gateway_ip: '', + dns_ip_1: '', + dns_ip_2: '' } } @@ -222,6 +234,13 @@ newNetwork = false showNetworkEditor = true networkEditable = dndNetworkList[index] + ipDisplay = { + local_ip: networkEditable.local_ip ? uint32ToIp(networkEditable.local_ip) : '', + subnet_mask: networkEditable.subnet_mask ? uint32ToIp(networkEditable.subnet_mask) : '', + gateway_ip: networkEditable.gateway_ip ? uint32ToIp(networkEditable.gateway_ip) : '', + dns_ip_1: networkEditable.dns_ip_1 ? uint32ToIp(networkEditable.dns_ip_1) : '', + dns_ip_2: networkEditable.dns_ip_2 ? uint32ToIp(networkEditable.dns_ip_2) : '' + } } function confirmDelete(index: number) { @@ -300,7 +319,7 @@ @@ -343,16 +362,20 @@ - + {/if} {/if} @@ -543,7 +566,7 @@ minlength="7" maxlength="15" size="15" - bind:value={networkEditable.local_ip} + bind:value={ipDisplay.local_ip} id="localIP" required /> @@ -572,7 +595,7 @@ minlength="7" maxlength="15" size="15" - bind:value={networkEditable.gateway_ip} + bind:value={ipDisplay.gateway_ip} required />