summaryrefslogtreecommitdiff
path: root/services
diff options
context:
space:
mode:
authorATMDA <atdma2600@gmail.com>2025-11-14 09:00:05 -0500
committerATMDA <atdma2600@gmail.com>2025-11-14 09:00:05 -0500
commitba5dfbd4c48352856865687fa90aba1b1cdd6fb7 (patch)
treeb3510d0273c1974da78a2ac9123f7813138c4061 /services
parentcontrolcenter: correcting forget network button sizing issues (diff)
downloadcaelestia-shell-ba5dfbd4c48352856865687fa90aba1b1cdd6fb7.tar.gz
caelestia-shell-ba5dfbd4c48352856865687fa90aba1b1cdd6fb7.tar.bz2
caelestia-shell-ba5dfbd4c48352856865687fa90aba1b1cdd6fb7.zip
network: removed all execs from Network.qml, now relies only on Nmcli.qml
Diffstat (limited to 'services')
-rw-r--r--services/Network.qml1078
1 files changed, 182 insertions, 896 deletions
diff --git a/services/Network.qml b/services/Network.qml
index 2b79f12..fc16915 100644
--- a/services/Network.qml
+++ b/services/Network.qml
@@ -3,6 +3,7 @@ pragma Singleton
import Quickshell
import Quickshell.Io
import QtQuick
+import qs.services
Singleton {
id: root
@@ -13,13 +14,24 @@ Singleton {
getEthernetDevices();
});
// Load saved connections on startup
- listConnectionsProc.running = true;
+ Nmcli.loadSavedConnections(() => {
+ root.savedConnections = Nmcli.savedConnections;
+ root.savedConnectionSsids = Nmcli.savedConnectionSsids;
+ });
+ // Get initial WiFi status
+ Nmcli.getWifiStatus((enabled) => {
+ root.wifiEnabled = enabled;
+ });
+ // Sync networks from Nmcli on startup
+ Qt.callLater(() => {
+ syncNetworksFromNmcli();
+ }, 100);
}
readonly property list<AccessPoint> networks: []
readonly property AccessPoint active: networks.find(n => n.active) ?? null
property bool wifiEnabled: true
- readonly property bool scanning: rescanProc.running
+ readonly property bool scanning: Nmcli.scanning
property list<var> ethernetDevices: []
readonly property var activeEthernet: ethernetDevices.find(d => d.connected) ?? null
@@ -29,334 +41,245 @@ Singleton {
property var wirelessDeviceDetails: null
function enableWifi(enabled: bool): void {
- const cmd = enabled ? "on" : "off";
- enableWifiProc.exec(["nmcli", "radio", "wifi", cmd]);
+ Nmcli.enableWifi(enabled, (result) => {
+ if (result.success) {
+ root.getWifiStatus();
+ Nmcli.getNetworks(() => {
+ syncNetworksFromNmcli();
+ });
+ }
+ });
}
function toggleWifi(): void {
- const cmd = wifiEnabled ? "off" : "on";
- enableWifiProc.exec(["nmcli", "radio", "wifi", cmd]);
+ Nmcli.toggleWifi((result) => {
+ if (result.success) {
+ root.getWifiStatus();
+ Nmcli.getNetworks(() => {
+ syncNetworksFromNmcli();
+ });
+ }
+ });
}
function rescanWifi(): void {
- rescanProc.running = true;
+ Nmcli.rescanWifi();
}
property var pendingConnection: null
signal connectionFailed(string ssid)
function connectToNetwork(ssid: string, password: string, bssid: string, callback: var): void {
- // When password is provided, use BSSID for more reliable connection
- // When no password, use SSID (will use saved password if available)
- const hasBssid = bssid !== undefined && bssid !== null && bssid.length > 0;
- let cmd = [];
-
// Set up pending connection tracking if callback provided
if (callback) {
+ const hasBssid = bssid !== undefined && bssid !== null && bssid.length > 0;
root.pendingConnection = { ssid: ssid, bssid: hasBssid ? bssid : "", callback: callback };
}
-
- if (password && password.length > 0) {
- // When password is provided, try BSSID first if available, otherwise use SSID
- if (hasBssid) {
- // Use BSSID when password is provided - ensure BSSID is uppercase
- const bssidUpper = bssid.toUpperCase();
-
- // Check if a connection with this SSID already exists
- const existingConnection = root.savedConnections.find(conn =>
- conn && conn.toLowerCase().trim() === ssid.toLowerCase().trim()
- );
-
- if (existingConnection) {
- // Connection already exists - delete it first, then create new one with updated password
- deleteConnectionProc.exec(["nmcli", "connection", "delete", existingConnection]);
- // Wait a moment for deletion to complete, then create new connection
- Qt.callLater(() => {
- createConnectionWithPassword(ssid, bssidUpper, password);
- }, 300);
- return;
- } else {
- // No existing connection, create new one
- createConnectionWithPassword(ssid, bssidUpper, password);
- return;
- }
+
+ Nmcli.connectToNetwork(ssid, password, bssid, (result) => {
+ if (result && result.success) {
+ // Connection successful
+ if (callback) callback(result);
+ root.pendingConnection = null;
+ } else if (result && result.needsPassword) {
+ // Password needed - callback will handle showing dialog
+ if (callback) callback(result);
} else {
- // Fallback to SSID if BSSID not available - use device wifi connect
- cmd = ["nmcli", "device", "wifi", "connect", ssid, "password", password];
+ // Connection failed
+ if (result && result.error) {
+ root.connectionFailed(ssid);
+ }
+ if (callback) callback(result);
+ root.pendingConnection = null;
}
- } else {
- // Try to connect to existing connection first (will use saved password if available)
- cmd = ["nmcli", "device", "wifi", "connect", ssid];
- }
-
- // Set command and start process
- connectProc.command = cmd;
-
- // If we're creating a connection profile, we need to activate it after creation
- const isConnectionAdd = cmd.length > 0 && cmd[0] === "nmcli" && cmd[1] === "connection" && cmd[2] === "add";
-
- // Wait a moment before starting to ensure command is set
- Qt.callLater(() => {
- connectProc.running = true;
- });
-
- // Start connection check timer if we have a callback
- if (callback) {
- connectionCheckTimer.start();
- }
- }
-
- function createConnectionWithPassword(ssid: string, bssidUpper: string, password: string): void {
- // Create connection profile with all required properties for BSSID + password
- const cmd = ["nmcli", "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];
-
- // Set command and start process
- connectProc.command = cmd;
-
- Qt.callLater(() => {
- connectProc.running = true;
});
}
function connectToNetworkWithPasswordCheck(ssid: string, isSecure: bool, callback: var, bssid: string): void {
- // For secure networks, try connecting without password first
- // If connection succeeds (saved password exists), we're done
- // If it fails with password error, callback will be called to show password dialog
- if (isSecure) {
- const hasBssid = bssid !== undefined && bssid !== null && bssid.length > 0;
- root.pendingConnection = { ssid: ssid, bssid: hasBssid ? bssid : "", callback: callback };
- // Try connecting without password - will use saved password if available
- connectProc.exec(["nmcli", "device", "wifi", "connect", ssid]);
- // Start timer to check if connection succeeded
- connectionCheckTimer.start();
- } else {
- connectToNetwork(ssid, "", bssid, null);
- }
+ // Set up pending connection tracking
+ const hasBssid = bssid !== undefined && bssid !== null && bssid.length > 0;
+ root.pendingConnection = { ssid: ssid, bssid: hasBssid ? bssid : "", callback: callback };
+
+ Nmcli.connectToNetworkWithPasswordCheck(ssid, isSecure, (result) => {
+ if (result && result.success) {
+ // Connection successful
+ if (callback) callback(result);
+ root.pendingConnection = null;
+ } else if (result && result.needsPassword) {
+ // Password needed - callback will handle showing dialog
+ if (callback) callback(result);
+ } else {
+ // Connection failed
+ if (result && result.error) {
+ root.connectionFailed(ssid);
+ }
+ if (callback) callback(result);
+ root.pendingConnection = null;
+ }
+ }, bssid);
}
function disconnectFromNetwork(): void {
// Try to disconnect - use connection name if available, otherwise use device
- if (active && active.ssid) {
- // First try to disconnect by connection name (more reliable)
- disconnectByConnectionProc.exec(["nmcli", "connection", "down", active.ssid]);
- } else {
- // Fallback: disconnect by device
- disconnectProc.exec(["nmcli", "device", "disconnect", "wifi"]);
- }
+ Nmcli.disconnectFromNetwork();
+ // Refresh network list after disconnection
+ Qt.callLater(() => {
+ Nmcli.getNetworks(() => {
+ syncNetworksFromNmcli();
+ });
+ }, 500);
}
function forgetNetwork(ssid: string): void {
// Delete the connection profile for this network
// This will remove the saved password and connection settings
- if (ssid && ssid.length > 0) {
- deleteConnectionProc.exec(["nmcli", "connection", "delete", ssid]);
- // Also refresh network list after deletion
- Qt.callLater(() => {
- getNetworks.running = true;
- }, 500);
- }
+ Nmcli.forgetNetwork(ssid, (result) => {
+ if (result.success) {
+ // Refresh network list after deletion
+ Qt.callLater(() => {
+ Nmcli.getNetworks(() => {
+ syncNetworksFromNmcli();
+ });
+ }, 500);
+ }
+ });
}
- function hasConnectionProfile(ssid: string): bool {
- // Check if a connection profile exists for this SSID
- // This is synchronous check - returns true if connection exists
- if (!ssid || ssid.length === 0) {
- return false;
- }
- // Use nmcli to check if connection exists
- // We'll use a Process to check, but for now return false
- // The actual check will be done asynchronously
- return false;
- }
property list<string> savedConnections: []
property list<string> savedConnectionSsids: []
- property var wifiConnectionQueue: []
- property int currentSsidQueryIndex: 0
- Process {
- id: listConnectionsProc
- command: ["nmcli", "-t", "-f", "NAME,TYPE", "connection", "show"]
- environment: ({
- LANG: "C.UTF-8",
- LC_ALL: "C.UTF-8"
- })
- onExited: {
- if (exitCode === 0) {
- parseConnectionList(stdout.text);
- }
+ // Sync saved connections from Nmcli when they're updated
+ Connections {
+ target: Nmcli
+ function onSavedConnectionsChanged() {
+ root.savedConnections = Nmcli.savedConnections;
}
- stdout: StdioCollector {
- onStreamFinished: {
- parseConnectionList(text);
- }
+ function onSavedConnectionSsidsChanged() {
+ root.savedConnectionSsids = Nmcli.savedConnectionSsids;
}
}
- function parseConnectionList(output: string): void {
- const lines = output.trim().split("\n").filter(line => line.length > 0);
- const wifiConnections = [];
- const connections = [];
-
- // First pass: identify WiFi 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);
- }
- }
+ function syncNetworksFromNmcli(): void {
+ const rNetworks = root.networks;
+ const nNetworks = Nmcli.networks;
+
+ // Build a map of existing networks by key
+ const existingMap = new Map();
+ for (const rn of rNetworks) {
+ const key = `${rn.frequency}:${rn.ssid}:${rn.bssid}`;
+ existingMap.set(key, rn);
}
-
- root.savedConnections = connections;
-
- // Second pass: get SSIDs for WiFi connections
- if (wifiConnections.length > 0) {
- root.wifiConnectionQueue = wifiConnections;
- root.currentSsidQueryIndex = 0;
- root.savedConnectionSsids = [];
- // Start querying SSIDs one by one
- queryNextSsid();
- } else {
- root.savedConnectionSsids = [];
- root.wifiConnectionQueue = [];
+
+ // Build a map of new networks by key
+ const newMap = new Map();
+ for (const nn of nNetworks) {
+ const key = `${nn.frequency}:${nn.ssid}:${nn.bssid}`;
+ newMap.set(key, nn);
}
- }
-
- Process {
- id: getSsidProc
-
- environment: ({
- LANG: "C.UTF-8",
- LC_ALL: "C.UTF-8"
- })
- onExited: {
- if (exitCode === 0) {
- processSsidOutput(stdout.text);
- } else {
- // Move to next connection even if this one failed
- queryNextSsid();
+
+ // Remove networks that no longer exist
+ for (const [key, network] of existingMap) {
+ if (!newMap.has(key)) {
+ const index = rNetworks.indexOf(network);
+ if (index >= 0) {
+ rNetworks.splice(index, 1);
+ network.destroy();
+ }
}
}
- stdout: StdioCollector {
- onStreamFinished: {
- processSsidOutput(text);
+
+ // Add or update networks from Nmcli
+ for (const [key, nNetwork] of newMap) {
+ const existing = existingMap.get(key);
+ if (existing) {
+ // Update existing network's lastIpcObject
+ existing.lastIpcObject = nNetwork.lastIpcObject;
+ } else {
+ // Create new AccessPoint from Nmcli's data
+ rNetworks.push(apComp.createObject(root, {
+ lastIpcObject: nNetwork.lastIpcObject
+ }));
}
}
}
- function processSsidOutput(output: string): void {
- // Parse "802-11-wireless.ssid:SSID_NAME" format
- 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) {
- // Add to list if not already present (case-insensitive)
- const ssidLower = ssid.toLowerCase();
- if (!root.savedConnectionSsids.some(s => s && s.toLowerCase() === ssidLower)) {
- // Create new array to trigger QML property change notification
- const newList = root.savedConnectionSsids.slice();
- newList.push(ssid);
- root.savedConnectionSsids = newList;
- }
- }
- }
- }
-
- // Query next connection
- queryNextSsid();
+ 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
}
- function queryNextSsid(): void {
- if (root.currentSsidQueryIndex < root.wifiConnectionQueue.length) {
- const connectionName = root.wifiConnectionQueue[root.currentSsidQueryIndex];
- root.currentSsidQueryIndex++;
- getSsidProc.command = ["nmcli", "-t", "-f", "802-11-wireless.ssid", "connection", "show", connectionName];
- getSsidProc.running = true;
- } else {
- // All SSIDs retrieved
- root.wifiConnectionQueue = [];
- root.currentSsidQueryIndex = 0;
- }
+ Component {
+ id: apComp
+ AccessPoint {}
}
function hasSavedProfile(ssid: string): bool {
- if (!ssid || ssid.length === 0) {
- return false;
- }
- const ssidLower = ssid.toLowerCase().trim();
-
- // If currently connected to this network, it definitely has a saved profile
- if (root.active && root.active.ssid) {
- const activeSsidLower = root.active.ssid.toLowerCase().trim();
- if (activeSsidLower === ssidLower) {
- return true;
- }
- }
-
- // Check if SSID is in saved connections (case-insensitive comparison)
- const hasSsid = root.savedConnectionSsids.some(savedSsid =>
- savedSsid && savedSsid.toLowerCase().trim() === ssidLower
- );
-
- if (hasSsid) {
- return true;
- }
-
- // Fallback: also check if connection name matches SSID (some connections use SSID as name)
- const hasConnectionName = root.savedConnections.some(connName =>
- connName && connName.toLowerCase().trim() === ssidLower
- );
-
- return hasConnectionName;
+ // Use Nmcli's hasSavedProfile which has the same logic
+ return Nmcli.hasSavedProfile(ssid);
}
function getWifiStatus(): void {
- wifiStatusProc.running = true;
+ Nmcli.getWifiStatus((enabled) => {
+ root.wifiEnabled = enabled;
+ });
}
function getEthernetDevices(): void {
- getEthernetDevicesProc.running = true;
+ root.ethernetProcessRunning = true;
+ Nmcli.getEthernetInterfaces((interfaces) => {
+ root.ethernetDevices = Nmcli.ethernetDevices;
+ root.ethernetDeviceCount = Nmcli.ethernetDevices.length;
+ root.ethernetProcessRunning = false;
+ });
}
function connectEthernet(connectionName: string, interfaceName: string): void {
- if (connectionName && connectionName.length > 0) {
- // Use connection name if available
- connectEthernetProc.exec(["nmcli", "connection", "up", connectionName]);
- } else if (interfaceName && interfaceName.length > 0) {
- // Fallback to device interface if no connection name
- connectEthernetProc.exec(["nmcli", "device", "connect", interfaceName]);
- }
+ Nmcli.connectEthernet(connectionName, interfaceName, (result) => {
+ if (result.success) {
+ getEthernetDevices();
+ // Refresh device details after connection
+ Qt.callLater(() => {
+ const activeDevice = root.ethernetDevices.find(function(d) { return d.connected; });
+ if (activeDevice && activeDevice.interface) {
+ updateEthernetDeviceDetails(activeDevice.interface);
+ }
+ }, 1000);
+ }
+ });
}
function disconnectEthernet(connectionName: string): void {
- disconnectEthernetProc.exec(["nmcli", "connection", "down", connectionName]);
+ Nmcli.disconnectEthernet(connectionName, (result) => {
+ if (result.success) {
+ getEthernetDevices();
+ // Clear device details after disconnection
+ Qt.callLater(() => {
+ root.ethernetDeviceDetails = null;
+ });
+ }
+ });
}
function updateEthernetDeviceDetails(interfaceName: string): void {
- if (interfaceName && interfaceName.length > 0) {
- getEthernetDetailsProc.exec(["nmcli", "device", "show", interfaceName]);
- } else {
- ethernetDeviceDetails = null;
- }
+ Nmcli.getEthernetDeviceDetails(interfaceName, (details) => {
+ root.ethernetDeviceDetails = details;
+ });
}
function updateWirelessDeviceDetails(): void {
// Find the wireless interface by looking for wifi devices
- findWirelessInterfaceProc.exec(["nmcli", "device", "status"]);
+ // Pass empty string to let Nmcli find the active interface automatically
+ Nmcli.getWirelessDeviceDetails("", (details) => {
+ root.wirelessDeviceDetails = details;
+ });
}
function cidrToSubnetMask(cidr: string): string {
@@ -382,649 +305,12 @@ Singleton {
command: ["nmcli", "m"]
stdout: SplitParser {
onRead: {
- getNetworks.running = true;
+ Nmcli.getNetworks(() => {
+ syncNetworksFromNmcli();
+ });
getEthernetDevices();
}
}
}
- Process {
- id: wifiStatusProc
-
- running: true
- command: ["nmcli", "radio", "wifi"]
- environment: ({
- LANG: "C.UTF-8",
- LC_ALL: "C.UTF-8"
- })
- stdout: StdioCollector {
- onStreamFinished: {
- root.wifiEnabled = text.trim() === "enabled";
- }
- }
- }
-
- Process {
- id: enableWifiProc
-
- onExited: {
- root.getWifiStatus();
- getNetworks.running = true;
- }
- }
-
- Process {
- id: rescanProc
-
- command: ["nmcli", "dev", "wifi", "list", "--rescan", "yes"]
- onExited: {
- getNetworks.running = true;
- }
- }
-
- Timer {
- id: connectionCheckTimer
- interval: 4000
- onTriggered: {
- if (root.pendingConnection) {
- const connected = root.active && root.active.ssid === root.pendingConnection.ssid;
-
- if (!connected && root.pendingConnection.callback) {
- // Connection didn't succeed after multiple checks, show password dialog
- const pending = root.pendingConnection;
- root.pendingConnection = null;
- immediateCheckTimer.stop();
- immediateCheckTimer.checkCount = 0;
- pending.callback();
- } else if (connected) {
- // Connection succeeded, clear pending
- 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) {
- // Connection succeeded, stop timers and clear pending
- connectionCheckTimer.stop();
- immediateCheckTimer.stop();
- immediateCheckTimer.checkCount = 0;
- root.pendingConnection = null;
- } else if (checkCount >= 6) {
- // Checked 6 times (3 seconds total), connection likely failed
- // Stop immediate check, let the main timer handle it
- immediateCheckTimer.stop();
- immediateCheckTimer.checkCount = 0;
- }
- } else {
- immediateCheckTimer.stop();
- immediateCheckTimer.checkCount = 0;
- }
- }
- }
-
- Process {
- id: connectProc
-
- onExited: {
-
- // Check if this was a "connection add" command - if so, we need to activate it
- const wasConnectionAdd = connectProc.command && connectProc.command.length > 0
- && connectProc.command[0] === "nmcli"
- && connectProc.command[1] === "connection"
- && connectProc.command[2] === "add";
-
- if (wasConnectionAdd && root.pendingConnection) {
- const ssid = root.pendingConnection.ssid;
-
- // Check for duplicate connection warning in stderr text
- const stderrText = connectProc.stderr ? connectProc.stderr.text : "";
- const hasDuplicateWarning = stderrText && (
- stderrText.includes("another connection with the name") ||
- stderrText.includes("Reference the connection by its uuid")
- );
-
- // Even with duplicate warning (or if connection already exists), we should try to activate it
- // Also try if exit code is non-zero but small (might be a warning, not a real error)
- if (exitCode === 0 || hasDuplicateWarning || (exitCode > 0 && exitCode < 10)) {
-
- // Update saved connections list
- listConnectionsProc.running = true;
-
- // Try to activate the connection by SSID (connection name)
- connectProc.command = ["nmcli", "connection", "up", ssid];
- Qt.callLater(() => {
- connectProc.running = true;
- });
- // Don't start timers yet - wait for activation to complete
- return;
- } else {
- // Connection add failed - try using device wifi connect as fallback
- // Extract password from the command if available
- let password = "";
- if (connectProc.command) {
- const pskIndex = connectProc.command.findIndex(arg => arg === "802-11-wireless-security.psk");
- if (pskIndex >= 0 && pskIndex + 1 < connectProc.command.length) {
- password = connectProc.command[pskIndex + 1];
- }
- }
-
- if (password && password.length > 0) {
- connectProc.command = ["nmcli", "device", "wifi", "connect", ssid, "password", password];
- Qt.callLater(() => {
- connectProc.running = true;
- });
- return;
- }
- }
- }
-
- // Refresh network list after connection attempt
- getNetworks.running = true;
-
- // Check if connection succeeded after a short delay (network list needs to update)
- if (root.pendingConnection) {
- immediateCheckTimer.start();
- }
- }
- stdout: SplitParser {
- onRead: {
- getNetworks.running = true;
- }
- }
- stderr: StdioCollector {
- onStreamFinished: {
- const error = text.trim();
- if (error && error.length > 0) {
- // Check for specific errors that indicate password is needed
- // Be careful not to match success messages
- const needsPassword = (error.includes("Secrets were required") ||
- error.includes("No secrets provided") ||
- error.includes("802-11-wireless-security.psk") ||
- (error.includes("password") && !error.includes("Connection activated")) ||
- (error.includes("Secrets") && !error.includes("Connection activated")) ||
- (error.includes("802.11") && !error.includes("Connection activated"))) &&
- !error.includes("Connection activated") &&
- !error.includes("successfully");
-
- if (needsPassword && root.pendingConnection && root.pendingConnection.callback) {
- // Connection failed because password is needed - show dialog immediately
- connectionCheckTimer.stop();
- immediateCheckTimer.stop();
- const pending = root.pendingConnection;
- root.pendingConnection = null;
- pending.callback();
- } else if (error && error.length > 0 && !error.includes("Connection activated") && !error.includes("successfully")) {
- // Emit signal for UI to handle
- root.connectionFailed(root.pendingConnection ? root.pendingConnection.ssid : "");
- }
- }
- }
- }
- }
-
- Process {
- id: disconnectProc
-
- onExited: {
- // Refresh network list after disconnection
- getNetworks.running = true;
- }
- stdout: SplitParser {
- onRead: getNetworks.running = true
- }
- stderr: StdioCollector {
- onStreamFinished: {
- }
- }
- }
-
- Process {
- id: disconnectByConnectionProc
-
- onExited: {
- // Refresh network list after disconnection
- getNetworks.running = true;
- }
- stdout: SplitParser {
- onRead: getNetworks.running = true
- }
- stderr: StdioCollector {
- onStreamFinished: {
- const error = text.trim();
- if (error && error.length > 0 && !error.includes("successfully") && !error.includes("disconnected")) {
- // If connection down failed, try device disconnect as fallback
- disconnectProc.exec(["nmcli", "device", "disconnect", "wifi"]);
- }
- }
- }
- }
-
- Process {
- id: deleteConnectionProc
-
- // Delete connection profile - refresh network list and saved connections after deletion
- onExited: {
- // Refresh network list and saved connections after deletion
- getNetworks.running = true;
- listConnectionsProc.running = true;
- }
- stderr: StdioCollector {
- onStreamFinished: {
- }
- }
- }
-
- Process {
- id: getNetworks
-
- running: true
- command: ["nmcli", "-g", "ACTIVE,SIGNAL,FREQ,SSID,BSSID,SECURITY", "d", "w"]
- environment: ({
- LANG: "C.UTF-8",
- LC_ALL: "C.UTF-8"
- })
- stdout: StdioCollector {
- onStreamFinished: {
- const PLACEHOLDER = "STRINGWHICHHOPEFULLYWONTBEUSED";
- const rep = new RegExp("\\\\:", "g");
- const rep2 = new RegExp(PLACEHOLDER, "g");
-
- const allNetworks = text.trim().split("\n").map(n => {
- const net = n.replace(rep, PLACEHOLDER).split(":");
- return {
- active: net[0] === "yes",
- strength: parseInt(net[1]),
- frequency: parseInt(net[2]),
- ssid: net[3]?.replace(rep2, ":") ?? "",
- bssid: net[4]?.replace(rep2, ":") ?? "",
- security: net[5] ?? ""
- };
- }).filter(n => n.ssid && n.ssid.length > 0);
-
- // Group networks by SSID and prioritize connected ones
- const networkMap = new Map();
- for (const network of allNetworks) {
- const existing = networkMap.get(network.ssid);
- if (!existing) {
- networkMap.set(network.ssid, network);
- } else {
- // Prioritize active/connected networks
- if (network.active && !existing.active) {
- networkMap.set(network.ssid, network);
- } else if (!network.active && !existing.active) {
- // If both are inactive, keep the one with better signal
- if (network.strength > existing.strength) {
- networkMap.set(network.ssid, network);
- }
- }
- // If existing is active and new is not, keep existing
- }
- }
-
- 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)
- rNetworks.splice(rNetworks.indexOf(network), 1).forEach(n => n.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
- }));
- }
- }
-
- // Check if pending connection succeeded after network list is fully updated
- if (root.pendingConnection) {
- Qt.callLater(() => {
- const connected = root.active && root.active.ssid === root.pendingConnection.ssid;
- if (connected) {
- // Connection succeeded, stop timers and clear pending
- connectionCheckTimer.stop();
- immediateCheckTimer.stop();
- immediateCheckTimer.checkCount = 0;
- root.pendingConnection = null;
- }
- });
- }
- }
- }
- }
-
- Process {
- id: getEthernetDevicesProc
-
- running: false
- command: ["nmcli", "-g", "DEVICE,TYPE,STATE,CONNECTION", "device", "status"]
- environment: ({
- LANG: "C.UTF-8",
- LC_ALL: "C.UTF-8"
- })
- onRunningChanged: {
- root.ethernetProcessRunning = running;
- }
- onExited: {
- Qt.callLater(() => {
- const outputLength = ethernetStdout.text ? ethernetStdout.text.length : 0;
- if (outputLength > 0) {
- // Output was captured, process it
- const output = ethernetStdout.text.trim();
- root.processEthernetOutput(output);
- }
- });
- }
- stdout: StdioCollector {
- id: ethernetStdout
- onStreamFinished: {
- const output = text.trim();
-
- if (!output || output.length === 0) {
- return;
- }
-
- root.processEthernetOutput(output);
- }
- }
- }
-
- function processEthernetOutput(output: string): void {
- const PLACEHOLDER = "STRINGWHICHHOPEFULLYWONTBEUSED";
- const rep = new RegExp("\\\\:", "g");
- const rep2 = new RegExp(PLACEHOLDER, "g");
-
- const lines = output.split("\n");
-
- const allDevices = lines.map(d => {
- const dev = d.replace(rep, PLACEHOLDER).split(":");
- return {
- interface: dev[0]?.replace(rep2, ":") ?? "",
- type: dev[1]?.replace(rep2, ":") ?? "",
- state: dev[2]?.replace(rep2, ":") ?? "",
- connection: dev[3]?.replace(rep2, ":") ?? ""
- };
- });
-
- const ethernetOnly = allDevices.filter(d => d.type === "ethernet");
-
- const ethernetDevices = ethernetOnly.map(d => {
- const state = d.state || "";
- const connected = state === "100 (connected)" || state === "connected" || state.startsWith("connected");
- return {
- interface: d.interface,
- type: d.type,
- state: state,
- connection: d.connection,
- connected: connected,
- ipAddress: "",
- gateway: "",
- dns: [],
- subnet: "",
- macAddress: "",
- speed: ""
- };
- });
-
- // Update the list - replace the entire array to ensure QML detects the change
- // Create a new array and assign it to the property
- const newDevices = [];
- for (let i = 0; i < ethernetDevices.length; i++) {
- newDevices.push(ethernetDevices[i]);
- }
-
- // Replace the entire list
- root.ethernetDevices = newDevices;
-
- // Force QML to detect the change by updating a property
- root.ethernetDeviceCount = ethernetDevices.length;
- }
-
-
- Process {
- id: connectEthernetProc
-
- onExited: {
- getEthernetDevices();
- // Refresh device details after connection
- Qt.callLater(() => {
- const activeDevice = root.ethernetDevices.find(function(d) { return d.connected; });
- if (activeDevice && activeDevice.interface) {
- updateEthernetDeviceDetails(activeDevice.interface);
- }
- });
- }
- stdout: SplitParser {
- onRead: getEthernetDevices()
- }
- stderr: StdioCollector {
- onStreamFinished: {
- }
- }
- }
-
- Process {
- id: disconnectEthernetProc
-
- onExited: {
- getEthernetDevices();
- // Clear device details after disconnection
- Qt.callLater(() => {
- root.ethernetDeviceDetails = null;
- });
- }
- stdout: SplitParser {
- onRead: getEthernetDevices()
- }
- stderr: StdioCollector {
- onStreamFinished: {
- }
- }
- }
-
- Process {
- id: getEthernetDetailsProc
-
- environment: ({
- LANG: "C.UTF-8",
- LC_ALL: "C.UTF-8"
- })
- stdout: StdioCollector {
- onStreamFinished: {
- const output = text.trim();
- if (!output || output.length === 0) {
- root.ethernetDeviceDetails = null;
- return;
- }
-
- const lines = output.split("\n");
- const details = {
- ipAddress: "",
- gateway: "",
- dns: [],
- subnet: "",
- macAddress: "",
- speed: ""
- };
-
- 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")) {
- // Extract IP and subnet from format like "10.13.1.45/24"
- const ipParts = value.split("/");
- details.ipAddress = ipParts[0] || "";
- if (ipParts[1]) {
- // Convert CIDR notation to subnet mask
- details.subnet = root.cidrToSubnetMask(ipParts[1]);
- } else {
- details.subnet = "";
- }
- } else if (key === "IP4.GATEWAY") {
- details.gateway = value;
- } else if (key.startsWith("IP4.DNS")) {
- details.dns.push(value);
- } else if (key === "WIRED-PROPERTIES.MAC") {
- details.macAddress = value;
- } else if (key === "WIRED-PROPERTIES.SPEED") {
- details.speed = value;
- }
- }
- }
-
- root.ethernetDeviceDetails = details;
- }
- }
- onExited: {
- if (exitCode !== 0) {
- root.ethernetDeviceDetails = null;
- }
- }
- }
-
- Process {
- id: findWirelessInterfaceProc
-
- environment: ({
- LANG: "C.UTF-8",
- LC_ALL: "C.UTF-8"
- })
- stdout: StdioCollector {
- onStreamFinished: {
- const output = text.trim();
- if (!output || output.length === 0) {
- root.wirelessDeviceDetails = null;
- return;
- }
-
- // Find the connected wifi interface from device status
- const lines = output.split("\n");
- let wifiInterface = "";
-
- for (let i = 0; i < lines.length; i++) {
- const line = lines[i];
- const parts = line.split(/\s+/);
- // Format: DEVICE TYPE STATE CONNECTION
- // Look for wifi devices that are connected
- if (parts.length >= 3 && parts[1] === "wifi" && parts[2] === "connected") {
- wifiInterface = parts[0];
- break;
- }
- }
-
- if (wifiInterface && wifiInterface.length > 0) {
- getWirelessDetailsProc.exec(["nmcli", "device", "show", wifiInterface]);
- } else {
- root.wirelessDeviceDetails = null;
- }
- }
- }
- onExited: {
- if (exitCode !== 0) {
- root.wirelessDeviceDetails = null;
- }
- }
- }
-
- Process {
- id: getWirelessDetailsProc
-
- environment: ({
- LANG: "C.UTF-8",
- LC_ALL: "C.UTF-8"
- })
- stdout: StdioCollector {
- onStreamFinished: {
- const output = text.trim();
- if (!output || output.length === 0) {
- root.wirelessDeviceDetails = null;
- return;
- }
-
- const lines = output.split("\n");
- const details = {
- ipAddress: "",
- gateway: "",
- dns: [],
- subnet: "",
- macAddress: "",
- speed: ""
- };
-
- 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")) {
- // Extract IP and subnet from format like "10.13.1.45/24"
- const ipParts = value.split("/");
- details.ipAddress = ipParts[0] || "";
- if (ipParts[1]) {
- // Convert CIDR notation to subnet mask
- details.subnet = root.cidrToSubnetMask(ipParts[1]);
- } else {
- details.subnet = "";
- }
- } else if (key === "IP4.GATEWAY") {
- details.gateway = value;
- } else if (key.startsWith("IP4.DNS")) {
- details.dns.push(value);
- } else if (key === "GENERAL.HWADDR") {
- details.macAddress = value;
- }
- }
- }
-
- root.wirelessDeviceDetails = details;
- }
- }
- onExited: {
- if (exitCode !== 0) {
- root.wirelessDeviceDetails = null;
- }
- }
- }
-
- 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 {}
- }
}