Make ip be uint32 instead of strings

This commit is contained in:
Rune Harlyk
2026-01-03 20:48:15 +01:00
committed by nikguin04
parent 6be38b2e9e
commit d611cd043b
13 changed files with 156 additions and 131 deletions
+14 -14
View File
@@ -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 = {
+1
View File
@@ -6,3 +6,4 @@ export * from './buffer-utilities'
export * from './model-utilities'
export * from './string-utilities'
export * from './color-utilities'
export * from './ip-utilities'
+23
View File
@@ -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)
}
+26 -18
View File
@@ -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<string, unknown> = $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]}
/>
<StatusItem icon={Home} title="IP Address" description={apStatus.ip_address} />
<StatusItem
icon={Home}
title="IP Address"
description={uint32ToIp(apStatus.ip_address)}
/>
<StatusItem icon={MAC} title="MAC Address" description={apStatus.mac_address} />
@@ -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
/>
+59 -36
View File
@@ -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 @@
<StatusItem
icon={Home}
title="IP Address"
description={wifiStatus.local_ip}
description={uint32ToIp(wifiStatus.local_ip)}
/>
<StatusItem icon={WiFi} title="RSSI" description={`${wifiStatus.rssi} dBm`}>
@@ -343,16 +362,20 @@
<StatusItem
icon={Gateway}
title="Gateway IP"
description={wifiStatus.gateway_ip}
description={uint32ToIp(wifiStatus.gateway_ip)}
/>
<StatusItem
icon={Subnet}
title="Subnet Mask"
description={wifiStatus.subnet_mask}
description={uint32ToIp(wifiStatus.subnet_mask)}
/>
<StatusItem icon={DNS} title="DNS" description={wifiStatus.dns_ip_1} />
<StatusItem
icon={DNS}
title="DNS"
description={uint32ToIp(wifiStatus.dns_ip_1)}
/>
</div>
{/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
/>
<label class="label" for="gateway">
@@ -599,7 +622,7 @@
minlength="7"
maxlength="15"
size="15"
bind:value={networkEditable.subnet_mask}
bind:value={ipDisplay.subnet_mask}
required
/>
<label class="label" for="subnet">
@@ -626,7 +649,7 @@
minlength="7"
maxlength="15"
size="15"
bind:value={networkEditable.dns_ip_1}
bind:value={ipDisplay.dns_ip_1}
required
/>
<label class="label" for="gateway">
@@ -651,7 +674,7 @@
minlength="7"
maxlength="15"
size="15"
bind:value={networkEditable.dns_ip_2}
bind:value={ipDisplay.dns_ip_2}
required
/>
<label class="label" for="subnet">
+12 -7
View File
@@ -2,7 +2,6 @@
#include <WiFi.h>
#include <ArduinoJson.h>
#include <utils/json_utils.h>
#include <template/state_result.h>
#include <string>
@@ -53,6 +52,12 @@
enum APNetworkStatus { ACTIVE = 0, INACTIVE, LINGERING };
inline uint32_t parseIPv4(const char *str) {
IPAddress ip;
ip.fromString(str);
return (uint32_t)ip;
}
class APSettings {
public:
uint8_t provisionMode;
@@ -79,9 +84,9 @@ class APSettings {
root["channel"] = settings.channel;
root["ssid_hidden"] = settings.ssidHidden;
root["max_clients"] = settings.maxClients;
root["local_ip"] = settings.localIP.toString();
root["gateway_ip"] = settings.gatewayIP.toString();
root["subnet_mask"] = settings.subnetMask.toString();
root["local_ip"] = (uint32_t)(settings.localIP);
root["gateway_ip"] = (uint32_t)(settings.gatewayIP);
root["subnet_mask"] = (uint32_t)(settings.subnetMask);
}
static StateUpdateResult update(JsonVariant &root, APSettings &settings) {
@@ -99,9 +104,9 @@ class APSettings {
newSettings.ssidHidden = root["ssid_hidden"] | FACTORY_AP_SSID_HIDDEN;
newSettings.maxClients = root["max_clients"] | FACTORY_AP_MAX_CLIENTS;
JsonUtils::readIP(root, "local_ip", newSettings.localIP, FACTORY_AP_LOCAL_IP);
JsonUtils::readIP(root, "gateway_ip", newSettings.gatewayIP, FACTORY_AP_GATEWAY_IP);
JsonUtils::readIP(root, "subnet_mask", newSettings.subnetMask, FACTORY_AP_SUBNET_MASK);
newSettings.localIP = IPAddress(root["local_ip"] | parseIPv4(FACTORY_AP_LOCAL_IP));
newSettings.gatewayIP = IPAddress(root["gateway_ip"] | parseIPv4(FACTORY_AP_GATEWAY_IP));
newSettings.subnetMask = IPAddress(root["subnet_mask"] | parseIPv4(FACTORY_AP_SUBNET_MASK));
if (newSettings == settings) {
return StateUpdateResult::UNCHANGED;
-1
View File
@@ -1,7 +1,6 @@
#pragma once
#include <ArduinoJson.h>
#include <utils/json_utils.h>
#include <template/state_result.h>
#include <filesystem.h>
#include <string>
+10 -11
View File
@@ -2,7 +2,6 @@
#include <WiFi.h>
#include <ArduinoJson.h>
#include <utils/json_utils.h>
#include <template/state_result.h>
#include <string>
@@ -40,11 +39,11 @@ typedef struct {
json["password"] = password.c_str();
json["static_ip_config"] = staticIPConfig;
if (staticIPConfig) {
JsonUtils::writeIP(json, "local_ip", localIP);
JsonUtils::writeIP(json, "gateway_ip", gatewayIP);
JsonUtils::writeIP(json, "subnet_mask", subnetMask);
JsonUtils::writeIP(json, "dns_ip_1", dnsIP1);
JsonUtils::writeIP(json, "dns_ip_2", dnsIP2);
json["local_ip"] = (uint32_t)(localIP);
json["gateway_ip"] = (uint32_t)(gatewayIP);
json["subnet_mask"] = (uint32_t)(subnetMask);
json["dns_ip_1"] = (uint32_t)(dnsIP1);
json["dns_ip_2"] = (uint32_t)(dnsIP2);
}
}
@@ -59,11 +58,11 @@ typedef struct {
password = newPassword;
staticIPConfig = json["static_ip_config"] | false;
if (staticIPConfig) {
JsonUtils::readIP(json, "local_ip", localIP);
JsonUtils::readIP(json, "gateway_ip", gatewayIP);
JsonUtils::readIP(json, "subnet_mask", subnetMask);
JsonUtils::readIP(json, "dns_ip_1", dnsIP1);
JsonUtils::readIP(json, "dns_ip_2", dnsIP2);
localIP = IPAddress(json["local_ip"] | 0u);
gatewayIP = IPAddress(json["gateway_ip"] | 0u);
subnetMask = IPAddress(json["subnet_mask"] | 0u);
dnsIP1 = IPAddress(json["dns_ip_1"] | 0u);
dnsIP2 = IPAddress(json["dns_ip_2"] | 0u);
if (dnsIP1 == IPAddress(0, 0, 0, 0) && dnsIP2 != IPAddress(0, 0, 0, 0)) {
dnsIP1 = dnsIP2;
dnsIP2 = IPAddress(0, 0, 0, 0);
-29
View File
@@ -1,29 +0,0 @@
#pragma once
#include <Arduino.h>
#include <ArduinoJson.h>
#include <string>
class JsonUtils {
public:
static void readIP(const JsonVariant &root, const std::string &key, IPAddress &ip, const std::string &def) {
IPAddress defaultIp = {};
if (!defaultIp.fromString(def.c_str())) {
defaultIp = IPAddress(0, 0, 0, 0);
}
readIP(root, key, ip, defaultIp);
}
static void readIP(const JsonVariant &root, const std::string &key, IPAddress &ip,
const IPAddress &defaultIp = IPAddress(0, 0, 0, 0)) {
if (!root[key].is<std::string>() || !ip.fromString(root[key].as<std::string>().c_str())) {
ip = defaultIp;
}
}
static void writeIP(JsonVariant &root, const std::string &key, const IPAddress &ip) {
if (ip != IPAddress(0, 0, 0, 0)) {
root[key] = ip.toString();
}
}
};
+1 -1
View File
@@ -21,7 +21,7 @@ esp_err_t APService::getStatus(PsychicRequest *request) {
void APService::status(JsonObject &root) {
root["status"] = getAPNetworkStatus();
root["ip_address"] = WiFi.softAPIP().toString();
root["ip_address"] = (uint32_t)(WiFi.softAPIP());
root["mac_address"] = WiFi.softAPmacAddress();
root["station_num"] = WiFi.softAPgetStationNum();
}
+4 -8
View File
@@ -19,8 +19,6 @@ esp_err_t handleSleep(PsychicRequest *request) {
return request->reply(200);
}
void reset() {
ESP_LOGI(TAG, "Resetting device");
File root = ESP_FS.open(FS_CONFIG_DIRECTORY);
@@ -73,25 +71,23 @@ void sleep() {
ESP_LOGI(TAG, "Setting device to sleep");
}
void getStaticSystemInformation(socket_message_StaticSystemInformation &info) {
size_t fs_total = 0, fs_used = 0;
esp_littlefs_info("spiffs", &fs_total, &fs_used);
info.esp_platform = (char*) ESP_PLATFORM_NAME;
info.esp_platform = (char *)ESP_PLATFORM_NAME;
info.firmware_version = APP_VERSION;
info.cpu_freq_mhz = ESP.getCpuFreqMHz();
info.cpu_type = (char*) ESP.getChipModel();
info.cpu_type = (char *)ESP.getChipModel();
info.cpu_rev = ESP.getChipRevision();
info.cpu_cores = ESP.getChipCores();
info.sketch_size = ESP.getSketchSize();
info.free_sketch_space = ESP.getFreeSketchSpace();
info.sdk_version = (char*) ESP.getSdkVersion();
info.sdk_version = (char *)ESP.getSdkVersion();
info.arduino_version = ARDUINO_VERSION;
info.flash_chip_size = ESP.getFlashChipSize();
info.flash_chip_speed = ESP.getFlashChipSpeed();
info.cpu_reset_reason = (char*) resetReason(esp_reset_reason());
info.cpu_reset_reason = (char *)resetReason(esp_reset_reason());
}
void getAnalytics(socket_message_AnalyticsData &analytics) {
+5 -5
View File
@@ -91,21 +91,21 @@ void WiFiService::getNetworkStatus(JsonObject &root) {
wl_status_t status = WiFi.status();
root["status"] = (uint8_t)status;
if (status == WL_CONNECTED) {
root["local_ip"] = WiFi.localIP().toString();
root["local_ip"] = (uint32_t)(WiFi.localIP());
root["mac_address"] = WiFi.macAddress();
root["rssi"] = WiFi.RSSI();
root["ssid"] = WiFi.SSID();
root["bssid"] = WiFi.BSSIDstr();
root["channel"] = WiFi.channel();
root["subnet_mask"] = WiFi.subnetMask().toString();
root["gateway_ip"] = WiFi.gatewayIP().toString();
root["subnet_mask"] = (uint32_t)(WiFi.subnetMask());
root["gateway_ip"] = (uint32_t)(WiFi.gatewayIP());
IPAddress dnsIP1 = WiFi.dnsIP(0);
IPAddress dnsIP2 = WiFi.dnsIP(1);
if (dnsIP1 != IPAddress(0, 0, 0, 0)) {
root["dns_ip_1"] = dnsIP1.toString();
root["dns_ip_1"] = (uint32_t)(dnsIP1);
}
if (dnsIP2 != IPAddress(0, 0, 0, 0)) {
root["dns_ip_2"] = dnsIP2.toString();
root["dns_ip_2"] = (uint32_t)(dnsIP2);
}
}
}
+1 -1
View File
@@ -6,7 +6,7 @@ package socket_message;
message Vector { float x = 1; float y = 2; }
message I2CDevice { int32 address = 1; string part_number = 2; string name = 3; }
message PinConfig { int32 pin = 1; string mode = 2; string type = 3; string role = 4; }
message KnownNetworkItem { string ssid = 1; string password = 2; bool static_ip = 3; optional string local_ip = 4; optional string subnet_mask = 5; optional string gateway_ip = 6; optional string dns_ip_1 = 7; optional string dns_ip_2 = 8; }
message KnownNetworkItem { string ssid = 1; string password = 2; bool static_ip = 3; uint32 local_ip = 4; uint32 subnet_mask = 5; uint32 gateway_ip = 6; uint32 dns_ip_1 = 7; uint32 dns_ip_2 = 8; }
// Individual message data types
message IMUData {