pragma Singleton import Quickshell import Quickshell.Io import QtQuick Singleton { id: root property var deviceStatus: null property var wirelessInterfaces: [] property var ethernetInterfaces: [] property bool isConnected: false property string activeInterface: "" property string activeConnection: "" property bool wifiEnabled: true readonly property bool scanning: rescanProc.running readonly property list networks: [] readonly property AccessPoint active: networks.find(n => n.active) ?? null property list savedConnections: [] property list savedConnectionSsids: [] property var wifiConnectionQueue: [] property int currentSsidQueryIndex: 0 property var pendingConnection: null signal connectionFailed(string ssid) property var wirelessDeviceDetails: null property var ethernetDeviceDetails: null property list ethernetDevices: [] readonly property var activeEthernet: ethernetDevices.find(d => d.connected) ?? null property list activeProcesses: [] property var debugLogger: null function setDebugLogger(logger: var): void { root.debugLogger = logger; } function log(message: string): void { if (root.debugLogger) { root.debugLogger(message); } else { console.log("[Nmcli]", message); } } function appendLog(message: string): void { log(message); } function executeCommand(args: list, callback: var): void { const proc = commandProc.createObject(root); proc.command = ["nmcli", ...args]; proc.callback = callback; activeProcesses.push(proc); proc.processFinished.connect(() => { const index = activeProcesses.indexOf(proc); if (index >= 0) { activeProcesses.splice(index, 1); } }); Qt.callLater(() => { proc.exec(proc.command); }); } function getDeviceStatus(callback: var): void { executeCommand(["-t", "-f", "DEVICE,TYPE,STATE,CONNECTION", "device", "status"], (result) => { if (callback) callback(result.output); }); } function getWirelessInterfaces(callback: var): void { executeCommand(["-t", "-f", "DEVICE,TYPE,STATE,CONNECTION", "device", "status"], (result) => { const interfaces = []; const lines = result.output.trim().split("\n"); for (const line of lines) { const parts = line.split(":"); if (parts.length >= 2 && parts[1] === "wifi") { interfaces.push({ device: parts[0] || "", type: parts[1] || "", state: parts[2] || "", connection: parts[3] || "" }); } } root.wirelessInterfaces = interfaces; if (callback) callback(interfaces); }); } function getEthernetInterfaces(callback: var): void { executeCommand(["-t", "-f", "DEVICE,TYPE,STATE,CONNECTION", "device", "status"], (result) => { const interfaces = []; const devices = []; const lines = result.output.trim().split("\n"); for (const line of lines) { const parts = line.split(":"); if (parts.length >= 2 && parts[1] === "ethernet") { const device = parts[0] || ""; const type = parts[1] || ""; const state = parts[2] || ""; const connection = parts[3] || ""; const connected = state === "100 (connected)" || state === "connected" || state.startsWith("connected"); interfaces.push({ device: device, type: type, state: state, connection: connection }); devices.push({ interface: device, type: type, state: state, connection: connection, connected: connected, ipAddress: "", gateway: "", dns: [], subnet: "", macAddress: "", speed: "" }); } } root.ethernetInterfaces = interfaces; root.ethernetDevices = devices; if (callback) callback(interfaces); }); } function connectEthernet(connectionName: string, interfaceName: string, callback: var): void { if (connectionName && connectionName.length > 0) { executeCommand(["connection", "up", connectionName], (result) => { if (result.success) { Qt.callLater(() => { getEthernetInterfaces(() => {}); if (interfaceName && interfaceName.length > 0) { Qt.callLater(() => { getEthernetDeviceDetails(interfaceName, () => {}); }, 1000); } }, 500); } if (callback) callback(result); }); } else if (interfaceName && interfaceName.length > 0) { executeCommand(["device", "connect", interfaceName], (result) => { if (result.success) { Qt.callLater(() => { getEthernetInterfaces(() => {}); Qt.callLater(() => { getEthernetDeviceDetails(interfaceName, () => {}); }, 1000); }, 500); } if (callback) callback(result); }); } else { if (callback) callback({ success: false, output: "", error: "No connection name or interface specified", exitCode: -1 }); } } function disconnectEthernet(connectionName: string, callback: var): void { if (!connectionName || connectionName.length === 0) { if (callback) callback({ success: false, output: "", error: "No connection name specified", exitCode: -1 }); return; } executeCommand(["connection", "down", connectionName], (result) => { if (result.success) { root.ethernetDeviceDetails = null; Qt.callLater(() => { getEthernetInterfaces(() => {}); }, 500); } if (callback) callback(result); }); } function getAllInterfaces(callback: var): void { executeCommand(["-t", "-f", "DEVICE,TYPE,STATE,CONNECTION", "device", "status"], (result) => { const interfaces = []; const lines = result.output.trim().split("\n"); for (const line of lines) { const parts = line.split(":"); if (parts.length >= 2 && (parts[1] === "wifi" || parts[1] === "ethernet")) { interfaces.push({ device: parts[0] || "", type: parts[1] || "", state: parts[2] || "", connection: parts[3] || "" }); } } if (callback) callback(interfaces); }); } function isInterfaceConnected(interfaceName: string, callback: var): void { executeCommand(["device", "status"], (result) => { const lines = result.output.trim().split("\n"); for (const line of lines) { const parts = line.split(/\s+/); if (parts.length >= 3 && parts[0] === interfaceName) { const connected = parts[2] === "connected" || parts[2].startsWith("connected"); if (callback) callback(connected); return; } } if (callback) callback(false); }); } function connectToNetworkWithPasswordCheck(ssid: string, isSecure: bool, callback: var, bssid: string): void { if (isSecure) { const hasBssid = bssid !== undefined && bssid !== null && bssid.length > 0; connectWireless(ssid, "", bssid, (result) => { if (result.success) { if (callback) callback({ success: true, usedSavedPassword: true, output: result.output, error: "", exitCode: 0 }); } else if (result.needsPassword) { if (callback) callback({ success: false, needsPassword: true, output: result.output, error: result.error, exitCode: result.exitCode }); } else { if (callback) callback(result); } }); } else { connectWireless(ssid, "", bssid, callback); } } function connectToNetwork(ssid: string, password: string, bssid: string, callback: var): void { connectWireless(ssid, password, bssid, callback); } function connectWireless(ssid: string, password: string, bssid: string, callback: var, retryCount: int): void { const hasBssid = bssid !== undefined && bssid !== null && bssid.length > 0; const retries = retryCount !== undefined ? retryCount : 0; const maxRetries = 2; if (callback) { root.pendingConnection = { ssid: ssid, bssid: hasBssid ? bssid : "", callback: callback, retryCount: retries }; connectionCheckTimer.start(); immediateCheckTimer.checkCount = 0; immediateCheckTimer.start(); } if (password && password.length > 0 && hasBssid) { const bssidUpper = bssid.toUpperCase(); createConnectionWithPassword(ssid, bssidUpper, password, callback); return; } let cmd = ["device", "wifi", "connect", ssid]; if (password && password.length > 0) { cmd.push("password", password); } executeCommand(cmd, (result) => { if (result.needsPassword && callback) { if (callback) callback(result); return; } if (!result.success && root.pendingConnection && retries < maxRetries) { log("Connection failed, retrying... (attempt " + (retries + 1) + "/" + maxRetries + ")"); Qt.callLater(() => { connectWireless(ssid, password, bssid, callback, retries + 1); }, 1000); } else if (!result.success && root.pendingConnection) { } else if (result.success && callback) { } else if (!result.success && !root.pendingConnection) { if (callback) callback(result); } }); } function createConnectionWithPassword(ssid: string, bssidUpper: string, password: string, callback: var): void { checkAndDeleteConnection(ssid, () => { const cmd = ["connection", "add", "type", "wifi", "con-name", ssid, "ifname", "*", "ssid", ssid, "802-11-wireless.bssid", bssidUpper, "802-11-wireless-security.key-mgmt", "wpa-psk", "802-11-wireless-security.psk", password]; executeCommand(cmd, (result) => { if (result.success) { loadSavedConnections(() => {}); activateConnection(ssid, callback); } else { const hasDuplicateWarning = result.error && ( result.error.includes("another connection with the name") || result.error.includes("Reference the connection by its uuid") ); if (hasDuplicateWarning || (result.exitCode > 0 && result.exitCode < 10)) { loadSavedConnections(() => {}); activateConnection(ssid, callback); } else { log("Connection profile creation failed, trying fallback..."); let fallbackCmd = ["device", "wifi", "connect", ssid, "password", password]; executeCommand(fallbackCmd, (fallbackResult) => { if (callback) callback(fallbackResult); }); } } }); }); } function checkAndDeleteConnection(ssid: string, callback: var): void { executeCommand(["connection", "show", ssid], (result) => { if (result.success) { executeCommand(["connection", "delete", ssid], (deleteResult) => { Qt.callLater(() => { if (callback) callback(); }, 300); }); } else { if (callback) callback(); } }); } function activateConnection(connectionName: string, callback: var): void { executeCommand(["connection", "up", connectionName], (result) => { if (callback) callback(result); }); } function loadSavedConnections(callback: var): void { executeCommand(["-t", "-f", "NAME,TYPE", "connection", "show"], (result) => { if (!result.success) { root.savedConnections = []; root.savedConnectionSsids = []; if (callback) callback([]); return; } parseConnectionList(result.output, callback); }); } function parseConnectionList(output: string, callback: var): void { const lines = output.trim().split("\n").filter(line => line.length > 0); const wifiConnections = []; const connections = []; for (const line of lines) { const parts = line.split(":"); if (parts.length >= 2) { const name = parts[0]; const type = parts[1]; connections.push(name); if (type === "802-11-wireless") { wifiConnections.push(name); } } } root.savedConnections = connections; if (wifiConnections.length > 0) { root.wifiConnectionQueue = wifiConnections; root.currentSsidQueryIndex = 0; root.savedConnectionSsids = []; queryNextSsid(callback); } else { root.savedConnectionSsids = []; root.wifiConnectionQueue = []; if (callback) callback(root.savedConnectionSsids); } } function queryNextSsid(callback: var): void { if (root.currentSsidQueryIndex < root.wifiConnectionQueue.length) { const connectionName = root.wifiConnectionQueue[root.currentSsidQueryIndex]; root.currentSsidQueryIndex++; executeCommand(["-t", "-f", "802-11-wireless.ssid", "connection", "show", connectionName], (result) => { if (result.success) { processSsidOutput(result.output); } queryNextSsid(callback); }); } else { root.wifiConnectionQueue = []; root.currentSsidQueryIndex = 0; if (callback) callback(root.savedConnectionSsids); } } function processSsidOutput(output: string): void { const lines = output.trim().split("\n"); for (const line of lines) { if (line.startsWith("802-11-wireless.ssid:")) { const ssid = line.substring("802-11-wireless.ssid:".length).trim(); if (ssid && ssid.length > 0) { const ssidLower = ssid.toLowerCase(); const exists = root.savedConnectionSsids.some(s => s && s.toLowerCase() === ssidLower); if (!exists) { const newList = root.savedConnectionSsids.slice(); newList.push(ssid); root.savedConnectionSsids = newList; } } } } } function hasSavedProfile(ssid: string): bool { if (!ssid || ssid.length === 0) { return false; } const ssidLower = ssid.toLowerCase().trim(); if (root.active && root.active.ssid) { const activeSsidLower = root.active.ssid.toLowerCase().trim(); if (activeSsidLower === ssidLower) { return true; } } const hasSsid = root.savedConnectionSsids.some(savedSsid => savedSsid && savedSsid.toLowerCase().trim() === ssidLower ); if (hasSsid) { return true; } const hasConnectionName = root.savedConnections.some(connName => connName && connName.toLowerCase().trim() === ssidLower ); return hasConnectionName; } function forgetNetwork(ssid: string, callback: var): void { if (!ssid || ssid.length === 0) { if (callback) callback({ success: false, output: "", error: "No SSID specified", exitCode: -1 }); return; } const connectionName = root.savedConnections.find(conn => conn && conn.toLowerCase().trim() === ssid.toLowerCase().trim() ) || ssid; executeCommand(["connection", "delete", connectionName], (result) => { if (result.success) { Qt.callLater(() => { loadSavedConnections(() => {}); }, 500); } if (callback) callback(result); }); } function disconnect(interfaceName: string, callback: var): void { if (interfaceName && interfaceName.length > 0) { executeCommand(["device", "disconnect", interfaceName], (result) => { if (callback) callback(result.success ? result.output : ""); }); } else { executeCommand(["device", "disconnect", "wifi"], (result) => { if (callback) callback(result.success ? result.output : ""); }); } } function disconnectFromNetwork(): void { if (active && active.ssid) { executeCommand(["connection", "down", active.ssid], (result) => { if (result.success) { getNetworks(() => {}); } }); } else { executeCommand(["device", "disconnect", "wifi"], (result) => { if (result.success) { getNetworks(() => {}); } }); } } function getDeviceDetails(interfaceName: string, callback: var): void { executeCommand(["device", "show", interfaceName], (result) => { if (callback) callback(result.output); }); } function refreshStatus(callback: var): void { getDeviceStatus((output) => { const lines = output.trim().split("\n"); let connected = false; let activeIf = ""; let activeConn = ""; for (const line of lines) { const parts = line.split(":"); if (parts.length >= 4) { const state = parts[2] || ""; if (state === "connected" || state.startsWith("connected")) { connected = true; activeIf = parts[0] || ""; activeConn = parts[3] || ""; break; } } } root.isConnected = connected; root.activeInterface = activeIf; root.activeConnection = activeConn; if (callback) callback({ connected, interface: activeIf, connection: activeConn }); }); } function bringInterfaceUp(interfaceName: string, callback: var): void { if (interfaceName && interfaceName.length > 0) { executeCommand(["device", "connect", interfaceName], (result) => { if (callback) { callback(result); } }); } else { if (callback) callback({ success: false, output: "", error: "No interface specified", exitCode: -1 }); } } function bringInterfaceDown(interfaceName: string, callback: var): void { if (interfaceName && interfaceName.length > 0) { executeCommand(["device", "disconnect", interfaceName], (result) => { if (callback) { callback(result); } }); } else { if (callback) callback({ success: false, output: "", error: "No interface specified", exitCode: -1 }); } } function scanWirelessNetworks(interfaceName: string, callback: var): void { let cmd = ["device", "wifi", "rescan"]; if (interfaceName && interfaceName.length > 0) { cmd.push("ifname", interfaceName); } executeCommand(cmd, (result) => { if (callback) { callback(result); } }); } function rescanWifi(): void { rescanProc.running = true; } function enableWifi(enabled: bool, callback: var): void { const cmd = enabled ? "on" : "off"; executeCommand(["radio", "wifi", cmd], (result) => { if (result.success) { getWifiStatus((status) => { root.wifiEnabled = status; if (callback) callback(result); }); } else { if (callback) callback(result); } }); } function toggleWifi(callback: var): void { const newState = !root.wifiEnabled; enableWifi(newState, callback); } function getWifiStatus(callback: var): void { executeCommand(["radio", "wifi"], (result) => { if (result.success) { const enabled = result.output.trim() === "enabled"; root.wifiEnabled = enabled; if (callback) callback(enabled); } else { if (callback) callback(root.wifiEnabled); } }); } function getNetworks(callback: var): void { executeCommand(["-g", "ACTIVE,SIGNAL,FREQ,SSID,BSSID,SECURITY", "d", "w"], (result) => { if (!result.success) { if (callback) callback([]); return; } const PLACEHOLDER = "STRINGWHICHHOPEFULLYWONTBEUSED"; const rep = new RegExp("\\\\:", "g"); const rep2 = new RegExp(PLACEHOLDER, "g"); const allNetworks = result.output.trim().split("\n") .filter(line => line && line.length > 0) .map(n => { const net = n.replace(rep, PLACEHOLDER).split(":"); return { active: net[0] === "yes", strength: parseInt(net[1] || "0", 10) || 0, frequency: parseInt(net[2] || "0", 10) || 0, ssid: (net[3]?.replace(rep2, ":") ?? "").trim(), bssid: (net[4]?.replace(rep2, ":") ?? "").trim(), security: (net[5] ?? "").trim() }; }) .filter(n => n.ssid && n.ssid.length > 0); const networkMap = new Map(); for (const network of allNetworks) { const existing = networkMap.get(network.ssid); if (!existing) { networkMap.set(network.ssid, network); } else { if (network.active && !existing.active) { networkMap.set(network.ssid, network); } else if (!network.active && !existing.active) { if (network.strength > existing.strength) { networkMap.set(network.ssid, network); } } } } const networks = Array.from(networkMap.values()); const rNetworks = root.networks; const destroyed = rNetworks.filter(rn => !networks.find(n => n.frequency === rn.frequency && n.ssid === rn.ssid && n.bssid === rn.bssid )); for (const network of destroyed) { const index = rNetworks.indexOf(network); if (index >= 0) { rNetworks.splice(index, 1); network.destroy(); } } for (const network of networks) { const match = rNetworks.find(n => n.frequency === network.frequency && n.ssid === network.ssid && n.bssid === network.bssid ); if (match) { match.lastIpcObject = network; } else { rNetworks.push(apComp.createObject(root, { lastIpcObject: network })); } } if (callback) callback(root.networks); checkPendingConnection(); }); } function getWirelessSSIDs(interfaceName: string, callback: var): void { let cmd = ["-t", "-f", "SSID,SIGNAL,SECURITY", "device", "wifi", "list"]; if (interfaceName && interfaceName.length > 0) { cmd.push("ifname", interfaceName); } executeCommand(cmd, (result) => { if (!result.success) { if (callback) callback([]); return; } const ssids = []; const lines = result.output.trim().split("\n"); const seenSSIDs = new Set(); for (const line of lines) { if (!line || line.length === 0) continue; const parts = line.split(":"); if (parts.length >= 1) { const ssid = parts[0].trim(); if (ssid && ssid.length > 0 && !seenSSIDs.has(ssid)) { seenSSIDs.add(ssid); const signalStr = parts.length >= 2 ? parts[1].trim() : ""; const signal = signalStr ? parseInt(signalStr, 10) : 0; const security = parts.length >= 3 ? parts[2].trim() : ""; ssids.push({ ssid: ssid, signal: signalStr, signalValue: isNaN(signal) ? 0 : signal, security: security }); } } } ssids.sort((a, b) => { return b.signalValue - a.signalValue; }); if (callback) callback(ssids); }); } component CommandProcess: Process { id: proc property var callback: null property list command: [] property bool callbackCalled: false property int exitCode: 0 signal processFinished environment: ({ LANG: "C.UTF-8", LC_ALL: "C.UTF-8" }) stdout: StdioCollector { id: stdoutCollector onStreamFinished: { } } stderr: StdioCollector { id: stderrCollector onStreamFinished: { const error = text.trim(); if (error && error.length > 0) { const isConnectionCommand = proc.command && proc.command.length > 0 && (proc.command.includes("wifi") || proc.command.includes("connection")); if (isConnectionCommand && root.pendingConnection && root.pendingConnection.callback) { const needsPassword = (error.includes("Secrets were required") || error.includes("Secrets were required, but not provided") || error.includes("No secrets provided") || error.includes("802-11-wireless-security.psk") || error.includes("password for") || (error.includes("password") && !error.includes("Connection activated") && !error.includes("successfully")) || (error.includes("Secrets") && !error.includes("Connection activated") && !error.includes("successfully")) || (error.includes("802.11") && !error.includes("Connection activated") && !error.includes("successfully"))) && !error.includes("Connection activated") && !error.includes("successfully"); if (needsPassword && !proc.callbackCalled && root.pendingConnection) { connectionCheckTimer.stop(); immediateCheckTimer.stop(); immediateCheckTimer.checkCount = 0; const pending = root.pendingConnection; root.pendingConnection = null; proc.callbackCalled = true; const result = { success: false, output: (stdoutCollector && stdoutCollector.text) ? stdoutCollector.text : "", error: error, exitCode: -1, needsPassword: true }; if (pending.callback) { pending.callback(result); } if (proc.callback && proc.callback !== pending.callback) { proc.callback(result); } } } } } } onExited: { proc.exitCode = exitCode; Qt.callLater(() => { if (proc.callbackCalled) { proc.processFinished(); return; } if (proc.callback) { const output = (stdoutCollector && stdoutCollector.text) ? stdoutCollector.text : ""; const error = (stderrCollector && stderrCollector.text) ? stderrCollector.text : ""; const success = proc.exitCode === 0; const isConnectionCommand = proc.command && proc.command.length > 0 && (proc.command.includes("wifi") || proc.command.includes("connection")); const needsPassword = isConnectionCommand && error && error.length > 0 && (error.includes("Secrets were required") || error.includes("Secrets were required, but not provided") || error.includes("No secrets provided") || error.includes("802-11-wireless-security.psk") || error.includes("password for") || (error.includes("password") && !error.includes("Connection activated") && !error.includes("successfully")) || (error.includes("Secrets") && !error.includes("Connection activated") && !error.includes("successfully")) || (error.includes("802.11") && !error.includes("Connection activated") && !error.includes("successfully"))) && !error.includes("Connection activated") && !error.includes("successfully"); if (needsPassword && root.pendingConnection && root.pendingConnection.callback) { connectionCheckTimer.stop(); immediateCheckTimer.stop(); immediateCheckTimer.checkCount = 0; const pending = root.pendingConnection; root.pendingConnection = null; proc.callbackCalled = true; const result = { success: false, output: output, error: error, exitCode: proc.exitCode, needsPassword: true }; if (pending.callback) { pending.callback(result); } if (proc.callback && proc.callback !== pending.callback) { proc.callback(result); } proc.processFinished(); return; } else if (!success && isConnectionCommand && root.pendingConnection) { const failedSsid = root.pendingConnection.ssid; root.connectionFailed(failedSsid); } proc.callbackCalled = true; proc.callback({ success: success, output: output, error: error, exitCode: proc.exitCode, needsPassword: needsPassword || false }); proc.processFinished(); } else { proc.processFinished(); } }); } } Component { id: commandProc CommandProcess {} } component AccessPoint: QtObject { required property var lastIpcObject readonly property string ssid: lastIpcObject.ssid readonly property string bssid: lastIpcObject.bssid readonly property int strength: lastIpcObject.strength readonly property int frequency: lastIpcObject.frequency readonly property bool active: lastIpcObject.active readonly property string security: lastIpcObject.security readonly property bool isSecure: security.length > 0 } Component { id: apComp AccessPoint {} } Timer { id: connectionCheckTimer interval: 4000 onTriggered: { if (root.pendingConnection) { const connected = root.active && root.active.ssid === root.pendingConnection.ssid; if (!connected && root.pendingConnection.callback) { let foundPasswordError = false; for (let i = 0; i < root.activeProcesses.length; i++) { const proc = root.activeProcesses[i]; if (proc && proc.stderr && proc.stderr.text) { const error = proc.stderr.text.trim(); if (error && error.length > 0) { const isConnectionCommand = proc.command && proc.command.length > 0 && (proc.command.includes("wifi") || proc.command.includes("connection")); if (isConnectionCommand) { const needsPassword = (error.includes("Secrets were required") || error.includes("Secrets were required, but not provided") || error.includes("No secrets provided") || error.includes("802-11-wireless-security.psk") || error.includes("password for") || (error.includes("password") && !error.includes("Connection activated") && !error.includes("successfully")) || (error.includes("Secrets") && !error.includes("Connection activated") && !error.includes("successfully")) || (error.includes("802.11") && !error.includes("Connection activated") && !error.includes("successfully"))) && !error.includes("Connection activated") && !error.includes("successfully"); if (needsPassword && !proc.callbackCalled && root.pendingConnection) { const pending = root.pendingConnection; root.pendingConnection = null; immediateCheckTimer.stop(); immediateCheckTimer.checkCount = 0; proc.callbackCalled = true; const result = { success: false, output: (proc.stdout && proc.stdout.text) ? proc.stdout.text : "", error: error, exitCode: -1, needsPassword: true }; if (pending.callback) { pending.callback(result); } if (proc.callback && proc.callback !== pending.callback) { proc.callback(result); } foundPasswordError = true; break; } } } } } if (!foundPasswordError) { const pending = root.pendingConnection; const failedSsid = pending.ssid; root.pendingConnection = null; immediateCheckTimer.stop(); immediateCheckTimer.checkCount = 0; root.connectionFailed(failedSsid); pending.callback({ success: false, output: "", error: "Connection timeout", exitCode: -1, needsPassword: false }); } } else if (connected) { root.pendingConnection = null; immediateCheckTimer.stop(); immediateCheckTimer.checkCount = 0; } } } } Timer { id: immediateCheckTimer interval: 500 repeat: true triggeredOnStart: false property int checkCount: 0 onTriggered: { if (root.pendingConnection) { checkCount++; const connected = root.active && root.active.ssid === root.pendingConnection.ssid; if (connected) { connectionCheckTimer.stop(); immediateCheckTimer.stop(); immediateCheckTimer.checkCount = 0; if (root.pendingConnection.callback) { root.pendingConnection.callback({ success: true, output: "Connected", error: "", exitCode: 0 }); } root.pendingConnection = null; } else { for (let i = 0; i < root.activeProcesses.length; i++) { const proc = root.activeProcesses[i]; if (proc && proc.stderr && proc.stderr.text) { const error = proc.stderr.text.trim(); if (error && error.length > 0) { const isConnectionCommand = proc.command && proc.command.length > 0 && (proc.command.includes("wifi") || proc.command.includes("connection")); if (isConnectionCommand) { const needsPassword = (error.includes("Secrets were required") || error.includes("Secrets were required, but not provided") || error.includes("No secrets provided") || error.includes("802-11-wireless-security.psk") || error.includes("password for") || (error.includes("password") && !error.includes("Connection activated") && !error.includes("successfully")) || (error.includes("Secrets") && !error.includes("Connection activated") && !error.includes("successfully")) || (error.includes("802.11") && !error.includes("Connection activated") && !error.includes("successfully"))) && !error.includes("Connection activated") && !error.includes("successfully"); if (needsPassword && !proc.callbackCalled && root.pendingConnection && root.pendingConnection.callback) { connectionCheckTimer.stop(); immediateCheckTimer.stop(); immediateCheckTimer.checkCount = 0; const pending = root.pendingConnection; root.pendingConnection = null; proc.callbackCalled = true; const result = { success: false, output: (proc.stdout && proc.stdout.text) ? proc.stdout.text : "", error: error, exitCode: -1, needsPassword: true }; if (pending.callback) { pending.callback(result); } if (proc.callback && proc.callback !== pending.callback) { proc.callback(result); } return; } } } } } if (checkCount >= 6) { immediateCheckTimer.stop(); immediateCheckTimer.checkCount = 0; } } } else { immediateCheckTimer.stop(); immediateCheckTimer.checkCount = 0; } } } function checkPendingConnection(): void { if (root.pendingConnection) { Qt.callLater(() => { const connected = root.active && root.active.ssid === root.pendingConnection.ssid; if (connected) { connectionCheckTimer.stop(); immediateCheckTimer.stop(); immediateCheckTimer.checkCount = 0; if (root.pendingConnection.callback) { root.pendingConnection.callback({ success: true, output: "Connected", error: "", exitCode: 0 }); } root.pendingConnection = null; } else { if (!immediateCheckTimer.running) { immediateCheckTimer.start(); } } }); } } function cidrToSubnetMask(cidr: string): string { const cidrNum = parseInt(cidr, 10); if (isNaN(cidrNum) || cidrNum < 0 || cidrNum > 32) { return ""; } const mask = (0xffffffff << (32 - cidrNum)) >>> 0; const octet1 = (mask >>> 24) & 0xff; const octet2 = (mask >>> 16) & 0xff; const octet3 = (mask >>> 8) & 0xff; const octet4 = mask & 0xff; return `${octet1}.${octet2}.${octet3}.${octet4}`; } function getWirelessDeviceDetails(interfaceName: string, callback: var): void { if (!interfaceName || interfaceName.length === 0) { const activeInterface = root.wirelessInterfaces.find(iface => { return iface.state === "connected" || iface.state.startsWith("connected"); }); if (activeInterface && activeInterface.device) { interfaceName = activeInterface.device; } else { if (callback) callback(null); return; } } executeCommand(["device", "show", interfaceName], (result) => { if (!result.success || !result.output) { root.wirelessDeviceDetails = null; if (callback) callback(null); return; } const details = parseDeviceDetails(result.output, false); root.wirelessDeviceDetails = details; if (callback) callback(details); }); } function getEthernetDeviceDetails(interfaceName: string, callback: var): void { if (!interfaceName || interfaceName.length === 0) { const activeInterface = root.ethernetInterfaces.find(iface => { return iface.state === "connected" || iface.state.startsWith("connected"); }); if (activeInterface && activeInterface.device) { interfaceName = activeInterface.device; } else { if (callback) callback(null); return; } } executeCommand(["device", "show", interfaceName], (result) => { if (!result.success || !result.output) { root.ethernetDeviceDetails = null; if (callback) callback(null); return; } const details = parseDeviceDetails(result.output, true); root.ethernetDeviceDetails = details; if (callback) callback(details); }); } function parseDeviceDetails(output: string, isEthernet: bool): var { const details = { ipAddress: "", gateway: "", dns: [], subnet: "", macAddress: "", speed: "" }; if (!output || output.length === 0) { return details; } const lines = output.trim().split("\n"); for (let i = 0; i < lines.length; i++) { const line = lines[i]; const parts = line.split(":"); if (parts.length >= 2) { const key = parts[0].trim(); const value = parts.slice(1).join(":").trim(); if (key.startsWith("IP4.ADDRESS")) { const ipParts = value.split("/"); details.ipAddress = ipParts[0] || ""; if (ipParts[1]) { details.subnet = cidrToSubnetMask(ipParts[1]); } else { details.subnet = ""; } } else if (key === "IP4.GATEWAY") { if (value !== "--") { details.gateway = value; } } else if (key.startsWith("IP4.DNS")) { if (value !== "--" && value.length > 0) { details.dns.push(value); } } else if (isEthernet && key === "WIRED-PROPERTIES.MAC") { details.macAddress = value; } else if (isEthernet && key === "WIRED-PROPERTIES.SPEED") { details.speed = value; } else if (!isEthernet && key === "GENERAL.HWADDR") { details.macAddress = value; } } } return details; } Process { id: rescanProc command: ["nmcli", "dev", "wifi", "list", "--rescan", "yes"] onExited: { getNetworks(() => {}); } } Process { id: monitorProc running: true command: ["nmcli", "monitor"] environment: ({ LANG: "C.UTF-8", LC_ALL: "C.UTF-8" }) stdout: SplitParser { onRead: { log("Connection state change detected, refreshing..."); root.refreshOnConnectionChange(); } } onExited: { log("Monitor process exited, restarting..."); Qt.callLater(() => { monitorProc.running = true; }, 2000); } } function refreshOnConnectionChange(): void { getNetworks((networks) => { const newActive = root.active; if (newActive && newActive.active) { Qt.callLater(() => { if (root.wirelessInterfaces.length > 0) { const activeWireless = root.wirelessInterfaces.find(iface => { return iface.state === "connected" || iface.state.startsWith("connected"); }); if (activeWireless && activeWireless.device) { getWirelessDeviceDetails(activeWireless.device, () => {}); } } if (root.ethernetInterfaces.length > 0) { const activeEthernet = root.ethernetInterfaces.find(iface => { return iface.state === "connected" || iface.state.startsWith("connected"); }); if (activeEthernet && activeEthernet.device) { getEthernetDeviceDetails(activeEthernet.device, () => {}); } } }, 500); } else { root.wirelessDeviceDetails = null; root.ethernetDeviceDetails = null; } getWirelessInterfaces(() => {}); getEthernetInterfaces(() => { if (root.activeEthernet && root.activeEthernet.connected) { Qt.callLater(() => { getEthernetDeviceDetails(root.activeEthernet.interface, () => {}); }, 500); } }); }); } Component.onCompleted: { getWifiStatus(() => {}); getNetworks(() => {}); loadSavedConnections(() => {}); getEthernetInterfaces(() => {}); Qt.callLater(() => { if (root.wirelessInterfaces.length > 0) { const activeWireless = root.wirelessInterfaces.find(iface => { return iface.state === "connected" || iface.state.startsWith("connected"); }); if (activeWireless && activeWireless.device) { getWirelessDeviceDetails(activeWireless.device, () => {}); } } if (root.ethernetInterfaces.length > 0) { const activeEthernet = root.ethernetInterfaces.find(iface => { return iface.state === "connected" || iface.state.startsWith("connected"); }); if (activeEthernet && activeEthernet.device) { getEthernetDeviceDetails(activeEthernet.device, () => {}); } } }, 2000); } }