conrolcenter: debug/rewrite of wireless panel
This commit is contained in:
parent
84e839cf55
commit
7e6f327091
5 changed files with 637 additions and 24 deletions
|
|
@ -118,7 +118,7 @@ ColumnLayout {
|
||||||
Network.disconnectFromNetwork();
|
Network.disconnectFromNetwork();
|
||||||
} else {
|
} else {
|
||||||
root.connectingToSsid = networkItem.modelData.ssid;
|
root.connectingToSsid = networkItem.modelData.ssid;
|
||||||
Network.connectToNetwork(networkItem.modelData.ssid, "");
|
Network.connectToNetwork(networkItem.modelData.ssid, "", networkItem.modelData.bssid, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -95,10 +95,31 @@ Item {
|
||||||
// Callback: connection failed, show password dialog
|
// Callback: connection failed, show password dialog
|
||||||
root.session.network.showPasswordDialog = true;
|
root.session.network.showPasswordDialog = true;
|
||||||
root.session.network.pendingNetwork = root.network;
|
root.session.network.pendingNetwork = root.network;
|
||||||
}
|
},
|
||||||
|
root.network.bssid
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
Network.connectToNetwork(root.network.ssid, "");
|
Network.connectToNetwork(root.network.ssid, "", root.network.bssid, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: Appearance.spacing.normal
|
||||||
|
visible: root.network && root.network.ssid && Network.savedConnections.includes(root.network.ssid)
|
||||||
|
color: Colours.palette.m3errorContainer
|
||||||
|
onColor: Colours.palette.m3onErrorContainer
|
||||||
|
text: qsTr("Forget Network")
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if (root.network && root.network.ssid) {
|
||||||
|
// Disconnect first if connected
|
||||||
|
if (root.network.active) {
|
||||||
|
Network.disconnectFromNetwork();
|
||||||
|
}
|
||||||
|
// Delete the connection profile
|
||||||
|
Network.forgetNetwork(root.network.ssid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -152,9 +173,39 @@ Item {
|
||||||
deviceDetails: Network.wirelessDeviceDetails
|
deviceDetails: Network.wirelessDeviceDetails
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
component Button: StyledRect {
|
||||||
|
property color onColor: Colours.palette.m3onSurface
|
||||||
|
property alias disabled: stateLayer.disabled
|
||||||
|
property alias text: label.text
|
||||||
|
property alias enabled: stateLayer.enabled
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: label.implicitHeight + Appearance.padding.normal * 2
|
||||||
|
radius: Appearance.rounding.normal
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
id: stateLayer
|
||||||
|
color: parent.onColor
|
||||||
|
function onClicked(): void {
|
||||||
|
if (parent.enabled !== false) {
|
||||||
|
parent.clicked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: label
|
||||||
|
anchors.centerIn: parent
|
||||||
|
color: parent.onColor
|
||||||
|
}
|
||||||
|
|
||||||
|
signal clicked
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -203,10 +203,11 @@ ColumnLayout {
|
||||||
// Callback: connection failed, show password dialog
|
// Callback: connection failed, show password dialog
|
||||||
root.session.network.showPasswordDialog = true;
|
root.session.network.showPasswordDialog = true;
|
||||||
root.session.network.pendingNetwork = modelData;
|
root.session.network.pendingNetwork = modelData;
|
||||||
}
|
},
|
||||||
|
modelData.bssid
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
Network.connectToNetwork(modelData.ssid, "");
|
Network.connectToNetwork(modelData.ssid, "", modelData.bssid, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,37 @@ Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
required property Session session
|
required property Session session
|
||||||
readonly property var network: session.network.pendingNetwork
|
readonly property var network: {
|
||||||
|
// Try pendingNetwork first, then fall back to active network selection
|
||||||
|
if (session.network.pendingNetwork) {
|
||||||
|
return session.network.pendingNetwork;
|
||||||
|
}
|
||||||
|
// Fallback to active network if available
|
||||||
|
if (session.network.active) {
|
||||||
|
return session.network.active;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
visible: session.network.showPasswordDialog
|
visible: session.network.showPasswordDialog
|
||||||
enabled: visible
|
enabled: visible
|
||||||
focus: visible
|
focus: visible
|
||||||
|
|
||||||
|
// Ensure network is set when dialog opens
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (visible && !session.network.pendingNetwork && session.network.active) {
|
||||||
|
session.network.pendingNetwork = session.network.active;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: root
|
||||||
|
function onVisibleChanged(): void {
|
||||||
|
if (visible && !session.network.pendingNetwork && session.network.active) {
|
||||||
|
session.network.pendingNetwork = session.network.active;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Keys.onEscapePressed: {
|
Keys.onEscapePressed: {
|
||||||
root.session.network.showPasswordDialog = false;
|
root.session.network.showPasswordDialog = false;
|
||||||
|
|
@ -105,6 +131,35 @@ Item {
|
||||||
font.pointSize: Appearance.font.size.small
|
font.pointSize: Appearance.font.size.small
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: statusText
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.topMargin: Appearance.spacing.small
|
||||||
|
visible: Network.connectionStatus.length > 0 || connectButton.connecting
|
||||||
|
text: {
|
||||||
|
if (Network.connectionStatus.length > 0) {
|
||||||
|
return Network.connectionStatus;
|
||||||
|
} else if (connectButton.connecting) {
|
||||||
|
return qsTr("Connecting...");
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
color: {
|
||||||
|
const status = Network.connectionStatus;
|
||||||
|
if (status.includes("Error") || status.includes("error") || status.includes("failed")) {
|
||||||
|
return Colours.palette.m3error;
|
||||||
|
} else if (status.includes("successful") || status.includes("Connected") || status.includes("success")) {
|
||||||
|
return Colours.palette.m3primary;
|
||||||
|
}
|
||||||
|
return Colours.palette.m3onSurfaceVariant;
|
||||||
|
}
|
||||||
|
font.pointSize: Appearance.font.size.small
|
||||||
|
font.weight: (Network.connectionStatus.includes("Error") || Network.connectionStatus.includes("error")) ? 500 : 400
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
Layout.maximumWidth: parent.width - Appearance.padding.large * 2
|
||||||
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.topMargin: Appearance.spacing.large
|
Layout.topMargin: Appearance.spacing.large
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
@ -145,9 +200,17 @@ Item {
|
||||||
if (root.visible) {
|
if (root.visible) {
|
||||||
passwordField.forceActiveFocus();
|
passwordField.forceActiveFocus();
|
||||||
passwordField.text = "";
|
passwordField.text = "";
|
||||||
|
Network.connectionStatus = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: Network
|
||||||
|
function onConnectionStatusChanged(): void {
|
||||||
|
// Status updated, ensure it's visible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Keys.onReturnPressed: {
|
Keys.onReturnPressed: {
|
||||||
if (connectButton.enabled) {
|
if (connectButton.enabled) {
|
||||||
|
|
@ -190,12 +253,222 @@ Item {
|
||||||
text: qsTr("Connect")
|
text: qsTr("Connect")
|
||||||
enabled: passwordField.text.length > 0
|
enabled: passwordField.text.length > 0
|
||||||
|
|
||||||
|
property bool connecting: false
|
||||||
|
|
||||||
function onClicked(): void {
|
function onClicked(): void {
|
||||||
if (root.network && passwordField.text.length > 0) {
|
Network.connectionStatus = "";
|
||||||
Network.connectToNetwork(root.network.ssid, passwordField.text);
|
|
||||||
root.session.network.showPasswordDialog = false;
|
// Get password first
|
||||||
passwordField.text = "";
|
const password = passwordField.text;
|
||||||
|
|
||||||
|
// Try multiple ways to get the network
|
||||||
|
let networkToUse = null;
|
||||||
|
|
||||||
|
// Try 1: root.network (computed property)
|
||||||
|
if (root.network) {
|
||||||
|
networkToUse = root.network;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try 2: pendingNetwork
|
||||||
|
if (!networkToUse && root.session.network.pendingNetwork) {
|
||||||
|
networkToUse = root.session.network.pendingNetwork;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try 3: active network
|
||||||
|
if (!networkToUse && root.session.network.active) {
|
||||||
|
networkToUse = root.session.network.active;
|
||||||
|
root.session.network.pendingNetwork = networkToUse;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check all conditions
|
||||||
|
const hasNetwork = !!networkToUse;
|
||||||
|
const hasPassword = password && password.length > 0;
|
||||||
|
const notConnecting = !connecting;
|
||||||
|
|
||||||
|
if (hasNetwork && hasPassword && notConnecting) {
|
||||||
|
// Set status immediately
|
||||||
|
Network.connectionStatus = qsTr("Preparing to connect...");
|
||||||
|
|
||||||
|
// Keep dialog open and track connection
|
||||||
|
connecting = true;
|
||||||
|
connectButton.enabled = false;
|
||||||
|
connectButton.text = qsTr("Connecting...");
|
||||||
|
|
||||||
|
// Force immediate UI update
|
||||||
|
statusText.visible = true;
|
||||||
|
|
||||||
|
// Store target SSID for later comparison
|
||||||
|
const ssidToConnect = networkToUse.ssid || "";
|
||||||
|
const bssidToConnect = networkToUse.bssid || "";
|
||||||
|
|
||||||
|
// Store the SSID we're connecting to so we can compare later
|
||||||
|
// even if root.network changes
|
||||||
|
content.connectingToSsid = ssidToConnect;
|
||||||
|
|
||||||
|
// Execute connection immediately
|
||||||
|
Network.connectToNetwork(
|
||||||
|
ssidToConnect,
|
||||||
|
password,
|
||||||
|
bssidToConnect,
|
||||||
|
() => {
|
||||||
|
// Callback if connection fails - keep dialog open
|
||||||
|
connecting = false;
|
||||||
|
connectButton.enabled = true;
|
||||||
|
connectButton.text = qsTr("Connect");
|
||||||
|
content.connectingToSsid = ""; // Clear on failure
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Start connection check timer immediately
|
||||||
|
connectionCheckTimer.checkCount = 0;
|
||||||
|
connectionCheckTimer.start();
|
||||||
|
|
||||||
|
// Also check immediately after a short delay to catch quick connections
|
||||||
|
Qt.callLater(() => {
|
||||||
|
if (root.visible) {
|
||||||
|
closeDialogIfConnected();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Show error in status
|
||||||
|
Network.connectionStatus = qsTr("Error: Cannot connect - missing network or password");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the SSID we're connecting to when connection starts
|
||||||
|
property string connectingToSsid: ""
|
||||||
|
|
||||||
|
property string targetSsid: {
|
||||||
|
// Track the SSID we're trying to connect to
|
||||||
|
// Prefer explicitly stored connectingToSsid, then computed values
|
||||||
|
if (connectingToSsid && connectingToSsid.length > 0) {
|
||||||
|
return connectingToSsid;
|
||||||
|
}
|
||||||
|
if (root.network && root.network.ssid) {
|
||||||
|
return root.network.ssid;
|
||||||
|
}
|
||||||
|
if (root.session.network.pendingNetwork && root.session.network.pendingNetwork.ssid) {
|
||||||
|
return root.session.network.pendingNetwork.ssid;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeDialogIfConnected(): bool {
|
||||||
|
// Check if we're connected to the network we're trying to connect to
|
||||||
|
const ssid = targetSsid;
|
||||||
|
|
||||||
|
if (!ssid || ssid.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Network.active) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeSsid = Network.active.ssid || "";
|
||||||
|
|
||||||
|
if (activeSsid === ssid) {
|
||||||
|
// Connection succeeded - close dialog
|
||||||
|
connectionCheckTimer.stop();
|
||||||
|
aggressiveCheckTimer.stop();
|
||||||
|
connectionCheckTimer.checkCount = 0;
|
||||||
|
connectButton.connecting = false;
|
||||||
|
Network.connectionStatus = "";
|
||||||
|
root.session.network.showPasswordDialog = false;
|
||||||
|
passwordField.text = "";
|
||||||
|
content.connectingToSsid = ""; // Clear stored SSID
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: connectionCheckTimer
|
||||||
|
interval: 1000 // Check every 1 second for faster response
|
||||||
|
repeat: true
|
||||||
|
triggeredOnStart: false
|
||||||
|
property int checkCount: 0
|
||||||
|
|
||||||
|
onTriggered: {
|
||||||
|
checkCount++;
|
||||||
|
|
||||||
|
// Try to close dialog if connected
|
||||||
|
const closed = content.closeDialogIfConnected();
|
||||||
|
if (closed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connectButton.connecting) {
|
||||||
|
// Still connecting, check again
|
||||||
|
// Limit to 20 checks (20 seconds total)
|
||||||
|
if (checkCount >= 20) {
|
||||||
|
connectionCheckTimer.stop();
|
||||||
|
connectionCheckTimer.checkCount = 0;
|
||||||
|
connectButton.connecting = false;
|
||||||
|
connectButton.enabled = true;
|
||||||
|
connectButton.text = qsTr("Connect");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Not connecting anymore, stop timer
|
||||||
|
connectionCheckTimer.stop();
|
||||||
|
connectionCheckTimer.checkCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: Network
|
||||||
|
function onActiveChanged(): void {
|
||||||
|
// Check immediately when active network changes
|
||||||
|
if (root.visible) {
|
||||||
|
// Check immediately - if connected, close right away
|
||||||
|
if (content.closeDialogIfConnected()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also check after a delay in case the active network isn't fully updated yet
|
||||||
|
Qt.callLater(() => {
|
||||||
|
if (root.visible) {
|
||||||
|
content.closeDialogIfConnected();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also check when dialog becomes visible
|
||||||
|
Connections {
|
||||||
|
target: root
|
||||||
|
function onVisibleChanged(): void {
|
||||||
|
if (root.visible) {
|
||||||
|
// Check immediately when dialog opens
|
||||||
|
Qt.callLater(() => {
|
||||||
|
if (root.visible) {
|
||||||
|
closeDialogIfConnected();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aggressive polling timer - checks every 500ms when dialog is visible and connecting
|
||||||
|
// This ensures we catch the connection even if signals are missed
|
||||||
|
Timer {
|
||||||
|
id: aggressiveCheckTimer
|
||||||
|
interval: 500
|
||||||
|
repeat: true
|
||||||
|
running: root.visible && connectButton.connecting
|
||||||
|
triggeredOnStart: true
|
||||||
|
|
||||||
|
onTriggered: {
|
||||||
|
if (root.visible && connectButton.connecting) {
|
||||||
|
if (content.closeDialogIfConnected()) {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ Singleton {
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
getEthernetDevices();
|
getEthernetDevices();
|
||||||
});
|
});
|
||||||
|
// Load saved connections on startup
|
||||||
|
listConnectionsProc.running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property list<AccessPoint> networks: []
|
readonly property list<AccessPoint> networks: []
|
||||||
|
|
@ -26,6 +28,34 @@ Singleton {
|
||||||
property bool ethernetProcessRunning: false
|
property bool ethernetProcessRunning: false
|
||||||
property var ethernetDeviceDetails: null
|
property var ethernetDeviceDetails: null
|
||||||
property var wirelessDeviceDetails: null
|
property var wirelessDeviceDetails: null
|
||||||
|
property string connectionStatus: ""
|
||||||
|
property string connectionDebug: ""
|
||||||
|
|
||||||
|
function clearConnectionStatus(): void {
|
||||||
|
connectionStatus = "";
|
||||||
|
// Don't clear debug - keep it for reference
|
||||||
|
// connectionDebug = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function setConnectionStatus(status: string): void {
|
||||||
|
connectionStatus = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addDebugInfo(info: string): void {
|
||||||
|
const timestamp = new Date().toLocaleTimeString();
|
||||||
|
const newInfo = "[" + timestamp + "] " + info;
|
||||||
|
// CRITICAL: Always append - NEVER replace
|
||||||
|
// Get current value - NEVER allow it to be empty/cleared
|
||||||
|
let current = connectionDebug;
|
||||||
|
if (!current || current === undefined || current === null) {
|
||||||
|
current = "";
|
||||||
|
}
|
||||||
|
// ALWAYS append - never replace
|
||||||
|
// If current is empty, just use newInfo, otherwise append with newline
|
||||||
|
const updated = (current.length > 0) ? (current + "\n" + newInfo) : newInfo;
|
||||||
|
// CRITICAL: Only assign if we're appending, never replace
|
||||||
|
connectionDebug = updated;
|
||||||
|
}
|
||||||
|
|
||||||
function enableWifi(enabled: bool): void {
|
function enableWifi(enabled: bool): void {
|
||||||
const cmd = enabled ? "on" : "off";
|
const cmd = enabled ? "on" : "off";
|
||||||
|
|
@ -44,30 +74,118 @@ Singleton {
|
||||||
property var pendingConnection: null
|
property var pendingConnection: null
|
||||||
signal connectionFailed(string ssid)
|
signal connectionFailed(string ssid)
|
||||||
|
|
||||||
function connectToNetwork(ssid: string, password: string): void {
|
function connectToNetwork(ssid: string, password: string, bssid: string, callback: var): void {
|
||||||
// First try to connect to an existing connection
|
// When password is provided, use BSSID for more reliable connection
|
||||||
// If that fails, create a new 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) {
|
||||||
|
root.pendingConnection = { ssid: ssid, bssid: hasBssid ? bssid : "", callback: callback };
|
||||||
|
}
|
||||||
|
|
||||||
if (password && password.length > 0) {
|
if (password && password.length > 0) {
|
||||||
connectProc.exec(["nmcli", "device", "wifi", "connect", ssid, "password", password]);
|
// 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();
|
||||||
|
// Create connection profile with all required properties for BSSID + password
|
||||||
|
// First remove any existing connection with this name
|
||||||
|
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];
|
||||||
|
root.setConnectionStatus(qsTr("Connecting to %1 (BSSID: %2)...").arg(ssid).arg(bssidUpper));
|
||||||
|
root.addDebugInfo(qsTr("Using BSSID: %1 for SSID: %2").arg(bssidUpper).arg(ssid));
|
||||||
|
root.addDebugInfo(qsTr("Creating connection profile with password and key-mgmt"));
|
||||||
|
} else {
|
||||||
|
// Fallback to SSID if BSSID not available - use device wifi connect
|
||||||
|
cmd = ["nmcli", "device", "wifi", "connect", ssid, "password", password];
|
||||||
|
root.setConnectionStatus(qsTr("Connecting to %1...").arg(ssid));
|
||||||
|
root.addDebugInfo(qsTr("Using SSID only (no BSSID): %1").arg(ssid));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Try to connect to existing connection first (will use saved password if available)
|
// Try to connect to existing connection first (will use saved password if available)
|
||||||
connectProc.exec(["nmcli", "device", "wifi", "connect", ssid]);
|
cmd = ["nmcli", "device", "wifi", "connect", ssid];
|
||||||
|
root.setConnectionStatus(qsTr("Connecting to %1 (using saved password)...").arg(ssid));
|
||||||
|
root.addDebugInfo(qsTr("Using saved password for: %1").arg(ssid));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the exact command being executed
|
||||||
|
const cmdStr = cmd.join(" ");
|
||||||
|
root.addDebugInfo(qsTr("=== COMMAND TO EXECUTE ==="));
|
||||||
|
root.addDebugInfo(qsTr("Command: %1").arg(cmdStr));
|
||||||
|
root.addDebugInfo(qsTr("Command array: [%1]").arg(cmd.map((arg, i) => `"${arg}"`).join(", ")));
|
||||||
|
root.addDebugInfo(qsTr("Command array length: %1").arg(cmd.length));
|
||||||
|
root.addDebugInfo(qsTr("==========================="));
|
||||||
|
|
||||||
|
// Set command and start process
|
||||||
|
root.addDebugInfo(qsTr("Setting command property..."));
|
||||||
|
connectProc.command = cmd;
|
||||||
|
const setCmdStr = connectProc.command ? connectProc.command.join(" ") : "null";
|
||||||
|
root.addDebugInfo(qsTr("Command property set, value: %1").arg(setCmdStr));
|
||||||
|
root.addDebugInfo(qsTr("Command property verified: %1").arg(setCmdStr === cmdStr ? "Match" : "MISMATCH"));
|
||||||
|
|
||||||
|
// 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(() => {
|
||||||
|
root.addDebugInfo(qsTr("=== STARTING PROCESS ==="));
|
||||||
|
root.addDebugInfo(qsTr("Current running state: %1").arg(connectProc.running));
|
||||||
|
root.addDebugInfo(qsTr("Command to run: %1").arg(connectProc.command ? connectProc.command.join(" ") : "NOT SET"));
|
||||||
|
root.addDebugInfo(qsTr("Is connection add command: %1").arg(isConnectionAdd));
|
||||||
|
connectProc.running = true;
|
||||||
|
root.addDebugInfo(qsTr("Process running set to: %1").arg(connectProc.running));
|
||||||
|
root.addDebugInfo(qsTr("========================"));
|
||||||
|
|
||||||
|
// Check if process actually started after a short delay
|
||||||
|
Qt.callLater(() => {
|
||||||
|
root.addDebugInfo(qsTr("Process status check (100ms later):"));
|
||||||
|
root.addDebugInfo(qsTr(" Running: %1").arg(connectProc.running));
|
||||||
|
root.addDebugInfo(qsTr(" Command: %1").arg(connectProc.command ? connectProc.command.join(" ") : "null"));
|
||||||
|
if (!connectProc.running) {
|
||||||
|
root.addDebugInfo(qsTr("WARNING: Process did not start!"));
|
||||||
|
root.setConnectionStatus(qsTr("Error: Process failed to start"));
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start connection check timer if we have a callback
|
||||||
|
if (callback) {
|
||||||
|
root.addDebugInfo(qsTr("Starting connection check timer (4 second interval)"));
|
||||||
|
connectionCheckTimer.start();
|
||||||
|
} else {
|
||||||
|
root.addDebugInfo(qsTr("No callback provided - not starting connection check timer"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function connectToNetworkWithPasswordCheck(ssid: string, isSecure: bool, callback: var): void {
|
function connectToNetworkWithPasswordCheck(ssid: string, isSecure: bool, callback: var, bssid: string): void {
|
||||||
|
root.addDebugInfo(qsTr("=== connectToNetworkWithPasswordCheck ==="));
|
||||||
|
root.addDebugInfo(qsTr("SSID: %1, isSecure: %2").arg(ssid).arg(isSecure));
|
||||||
|
|
||||||
// For secure networks, try connecting without password first
|
// For secure networks, try connecting without password first
|
||||||
// If connection succeeds (saved password exists), we're done
|
// If connection succeeds (saved password exists), we're done
|
||||||
// If it fails with password error, callback will be called to show password dialog
|
// If it fails with password error, callback will be called to show password dialog
|
||||||
if (isSecure) {
|
if (isSecure) {
|
||||||
root.pendingConnection = { ssid: ssid, callback: callback };
|
const hasBssid = bssid !== undefined && bssid !== null && bssid.length > 0;
|
||||||
|
root.pendingConnection = { ssid: ssid, bssid: hasBssid ? bssid : "", callback: callback };
|
||||||
|
root.addDebugInfo(qsTr("Trying to connect without password (will use saved if available)"));
|
||||||
// Try connecting without password - will use saved password if available
|
// Try connecting without password - will use saved password if available
|
||||||
connectProc.exec(["nmcli", "device", "wifi", "connect", ssid]);
|
connectProc.exec(["nmcli", "device", "wifi", "connect", ssid]);
|
||||||
// Start timer to check if connection succeeded
|
// Start timer to check if connection succeeded
|
||||||
|
root.addDebugInfo(qsTr("Starting connection check timer"));
|
||||||
connectionCheckTimer.start();
|
connectionCheckTimer.start();
|
||||||
} else {
|
} else {
|
||||||
connectToNetwork(ssid, "");
|
root.addDebugInfo(qsTr("Network is not secure, connecting directly"));
|
||||||
|
connectToNetwork(ssid, "", bssid, null);
|
||||||
}
|
}
|
||||||
|
root.addDebugInfo(qsTr("========================================="));
|
||||||
}
|
}
|
||||||
|
|
||||||
function disconnectFromNetwork(): void {
|
function disconnectFromNetwork(): void {
|
||||||
|
|
@ -80,6 +198,50 @@ Singleton {
|
||||||
disconnectProc.exec(["nmcli", "device", "disconnect", "wifi"]);
|
disconnectProc.exec(["nmcli", "device", "disconnect", "wifi"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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: []
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: listConnectionsProc
|
||||||
|
command: ["nmcli", "-t", "-f", "NAME", "connection", "show"]
|
||||||
|
onExited: {
|
||||||
|
if (exitCode === 0) {
|
||||||
|
// Parse connection names from output
|
||||||
|
const connections = stdout.text.trim().split("\n").filter(name => name.length > 0);
|
||||||
|
root.savedConnections = connections;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stdout: StdioCollector {
|
||||||
|
onStreamFinished: {
|
||||||
|
const connections = text.trim().split("\n").filter(name => name.length > 0);
|
||||||
|
root.savedConnections = connections;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getWifiStatus(): void {
|
function getWifiStatus(): void {
|
||||||
wifiStatusProc.running = true;
|
wifiStatusProc.running = true;
|
||||||
|
|
@ -184,11 +346,17 @@ Singleton {
|
||||||
id: connectionCheckTimer
|
id: connectionCheckTimer
|
||||||
interval: 4000
|
interval: 4000
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
|
root.addDebugInfo(qsTr("=== CONNECTION CHECK TIMER (4s) ==="));
|
||||||
if (root.pendingConnection) {
|
if (root.pendingConnection) {
|
||||||
// Final check - if connection still hasn't succeeded, show password dialog
|
|
||||||
const connected = root.active && root.active.ssid === root.pendingConnection.ssid;
|
const connected = root.active && root.active.ssid === root.pendingConnection.ssid;
|
||||||
|
root.addDebugInfo(qsTr("Checking connection status..."));
|
||||||
|
root.addDebugInfo(qsTr(" Pending SSID: %1").arg(root.pendingConnection.ssid));
|
||||||
|
root.addDebugInfo(qsTr(" Active SSID: %1").arg(root.active ? root.active.ssid : "None"));
|
||||||
|
root.addDebugInfo(qsTr(" Connected: %1").arg(connected));
|
||||||
|
|
||||||
if (!connected && root.pendingConnection.callback) {
|
if (!connected && root.pendingConnection.callback) {
|
||||||
// Connection didn't succeed after multiple checks, show password dialog
|
// Connection didn't succeed after multiple checks, show password dialog
|
||||||
|
root.addDebugInfo(qsTr("Connection failed - calling password dialog callback"));
|
||||||
const pending = root.pendingConnection;
|
const pending = root.pendingConnection;
|
||||||
root.pendingConnection = null;
|
root.pendingConnection = null;
|
||||||
immediateCheckTimer.stop();
|
immediateCheckTimer.stop();
|
||||||
|
|
@ -196,11 +364,19 @@ Singleton {
|
||||||
pending.callback();
|
pending.callback();
|
||||||
} else if (connected) {
|
} else if (connected) {
|
||||||
// Connection succeeded, clear pending
|
// Connection succeeded, clear pending
|
||||||
|
root.addDebugInfo(qsTr("Connection succeeded!"));
|
||||||
|
root.setConnectionStatus(qsTr("Connected successfully!"));
|
||||||
root.pendingConnection = null;
|
root.pendingConnection = null;
|
||||||
immediateCheckTimer.stop();
|
immediateCheckTimer.stop();
|
||||||
immediateCheckTimer.checkCount = 0;
|
immediateCheckTimer.checkCount = 0;
|
||||||
|
} else {
|
||||||
|
root.addDebugInfo(qsTr("Still connecting..."));
|
||||||
|
root.setConnectionStatus(qsTr("Still connecting..."));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
root.addDebugInfo(qsTr("No pending connection"));
|
||||||
}
|
}
|
||||||
|
root.addDebugInfo(qsTr("================================"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -210,23 +386,37 @@ Singleton {
|
||||||
repeat: true
|
repeat: true
|
||||||
triggeredOnStart: false
|
triggeredOnStart: false
|
||||||
property int checkCount: 0
|
property int checkCount: 0
|
||||||
|
|
||||||
|
onRunningChanged: {
|
||||||
|
if (running) {
|
||||||
|
root.addDebugInfo(qsTr("Immediate check timer started (checks every 500ms)"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
if (root.pendingConnection) {
|
if (root.pendingConnection) {
|
||||||
checkCount++;
|
checkCount++;
|
||||||
const connected = root.active && root.active.ssid === root.pendingConnection.ssid;
|
const connected = root.active && root.active.ssid === root.pendingConnection.ssid;
|
||||||
|
root.addDebugInfo(qsTr("Immediate check #%1: Connected=%2").arg(checkCount).arg(connected));
|
||||||
|
|
||||||
if (connected) {
|
if (connected) {
|
||||||
// Connection succeeded, stop timers and clear pending
|
// Connection succeeded, stop timers and clear pending
|
||||||
|
root.addDebugInfo(qsTr("Connection succeeded on check #%1!").arg(checkCount));
|
||||||
|
root.setConnectionStatus(qsTr("Connected successfully!"));
|
||||||
connectionCheckTimer.stop();
|
connectionCheckTimer.stop();
|
||||||
immediateCheckTimer.stop();
|
immediateCheckTimer.stop();
|
||||||
immediateCheckTimer.checkCount = 0;
|
immediateCheckTimer.checkCount = 0;
|
||||||
root.pendingConnection = null;
|
root.pendingConnection = null;
|
||||||
} else if (checkCount >= 6) {
|
} else if (checkCount >= 6) {
|
||||||
|
root.addDebugInfo(qsTr("Checked %1 times (3 seconds) - connection taking longer").arg(checkCount));
|
||||||
|
root.setConnectionStatus(qsTr("Connection taking longer than expected..."));
|
||||||
// Checked 6 times (3 seconds total), connection likely failed
|
// Checked 6 times (3 seconds total), connection likely failed
|
||||||
// Stop immediate check, let the main timer handle it
|
// Stop immediate check, let the main timer handle it
|
||||||
immediateCheckTimer.stop();
|
immediateCheckTimer.stop();
|
||||||
immediateCheckTimer.checkCount = 0;
|
immediateCheckTimer.checkCount = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
root.addDebugInfo(qsTr("Immediate check: No pending connection, stopping timer"));
|
||||||
immediateCheckTimer.stop();
|
immediateCheckTimer.stop();
|
||||||
immediateCheckTimer.checkCount = 0;
|
immediateCheckTimer.checkCount = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -236,22 +426,92 @@ Singleton {
|
||||||
Process {
|
Process {
|
||||||
id: connectProc
|
id: connectProc
|
||||||
|
|
||||||
|
onRunningChanged: {
|
||||||
|
root.addDebugInfo(qsTr("Process running changed to: %1").arg(running));
|
||||||
|
}
|
||||||
|
|
||||||
|
onStarted: {
|
||||||
|
root.addDebugInfo(qsTr("Process started successfully"));
|
||||||
|
}
|
||||||
|
|
||||||
onExited: {
|
onExited: {
|
||||||
|
root.addDebugInfo(qsTr("=== PROCESS EXITED ==="));
|
||||||
|
root.addDebugInfo(qsTr("Exit code: %1").arg(exitCode));
|
||||||
|
root.addDebugInfo(qsTr("(Exit code 0 = success, non-zero = error)"));
|
||||||
|
|
||||||
|
// 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 && exitCode === 0 && root.pendingConnection) {
|
||||||
|
// Connection profile was created successfully, now activate it
|
||||||
|
const ssid = root.pendingConnection.ssid;
|
||||||
|
root.addDebugInfo(qsTr("Connection profile created successfully, now activating: %1").arg(ssid));
|
||||||
|
root.setConnectionStatus(qsTr("Activating connection..."));
|
||||||
|
|
||||||
|
// Update saved connections list since we just created one
|
||||||
|
listConnectionsProc.running = true;
|
||||||
|
|
||||||
|
// Activate the connection we just created
|
||||||
|
connectProc.command = ["nmcli", "connection", "up", ssid];
|
||||||
|
Qt.callLater(() => {
|
||||||
|
connectProc.running = true;
|
||||||
|
});
|
||||||
|
// Don't start timers yet - wait for activation to complete
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Refresh network list after connection attempt
|
// Refresh network list after connection attempt
|
||||||
getNetworks.running = true;
|
getNetworks.running = true;
|
||||||
|
|
||||||
// Check if connection succeeded after a short delay (network list needs to update)
|
// Check if connection succeeded after a short delay (network list needs to update)
|
||||||
if (root.pendingConnection) {
|
if (root.pendingConnection) {
|
||||||
immediateCheckTimer.start();
|
if (exitCode === 0) {
|
||||||
|
// Process succeeded, start checking connection status
|
||||||
|
root.setConnectionStatus(qsTr("Connection command succeeded, verifying..."));
|
||||||
|
root.addDebugInfo(qsTr("Command succeeded, checking connection status..."));
|
||||||
|
root.addDebugInfo(qsTr("Starting immediate check timer (500ms intervals)"));
|
||||||
|
immediateCheckTimer.start();
|
||||||
|
} else {
|
||||||
|
// Process failed, but wait a moment to see if connection still works
|
||||||
|
root.setConnectionStatus(qsTr("Connection command exited with code %1, checking status...").arg(exitCode));
|
||||||
|
root.addDebugInfo(qsTr("Command exited with error code %1").arg(exitCode));
|
||||||
|
root.addDebugInfo(qsTr("This usually means the command failed"));
|
||||||
|
root.addDebugInfo(qsTr("Checking connection status anyway..."));
|
||||||
|
root.addDebugInfo(qsTr("Starting immediate check timer (500ms intervals)"));
|
||||||
|
immediateCheckTimer.start();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
root.addDebugInfo(qsTr("No pending connection - not starting immediate check timer"));
|
||||||
}
|
}
|
||||||
|
root.addDebugInfo(qsTr("======================"));
|
||||||
}
|
}
|
||||||
stdout: SplitParser {
|
stdout: SplitParser {
|
||||||
onRead: getNetworks.running = true
|
onRead: {
|
||||||
|
getNetworks.running = true;
|
||||||
|
// Also log output for debugging
|
||||||
|
if (text && text.trim().length > 0) {
|
||||||
|
root.addDebugInfo(qsTr("STDOUT: %1").arg(text.trim()));
|
||||||
|
root.setConnectionStatus(qsTr("Status: %1").arg(text.trim()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
stderr: StdioCollector {
|
stderr: StdioCollector {
|
||||||
onStreamFinished: {
|
onStreamFinished: {
|
||||||
const error = text.trim();
|
const error = text.trim();
|
||||||
|
root.addDebugInfo(qsTr("=== STDERR OUTPUT ==="));
|
||||||
if (error && error.length > 0) {
|
if (error && error.length > 0) {
|
||||||
|
// Split error into lines and add each one
|
||||||
|
const errorLines = error.split("\n");
|
||||||
|
for (let i = 0; i < errorLines.length; i++) {
|
||||||
|
const line = errorLines[i].trim();
|
||||||
|
if (line.length > 0) {
|
||||||
|
root.addDebugInfo(qsTr("STDERR: %1").arg(line));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check for specific errors that indicate password is needed
|
// Check for specific errors that indicate password is needed
|
||||||
// Be careful not to match success messages
|
// Be careful not to match success messages
|
||||||
const needsPassword = (error.includes("Secrets were required") ||
|
const needsPassword = (error.includes("Secrets were required") ||
|
||||||
|
|
@ -270,11 +530,19 @@ Singleton {
|
||||||
const pending = root.pendingConnection;
|
const pending = root.pendingConnection;
|
||||||
root.pendingConnection = null;
|
root.pendingConnection = null;
|
||||||
pending.callback();
|
pending.callback();
|
||||||
} else if (error && error.length > 0 && !error.includes("Connection activated")) {
|
} else if (error && error.length > 0 && !error.includes("Connection activated") && !error.includes("successfully")) {
|
||||||
// Only log non-success messages
|
// Log all errors (except success messages)
|
||||||
console.warn("Network connection error:", error);
|
root.setConnectionStatus(qsTr("Error: %1").arg(errorLines[0] || error));
|
||||||
|
// Emit signal for UI to handle
|
||||||
|
root.connectionFailed(root.pendingConnection ? root.pendingConnection.ssid : "");
|
||||||
|
} else if (error && (error.includes("Connection activated") || error.includes("successfully"))) {
|
||||||
|
root.addDebugInfo(qsTr("Connection successful!"));
|
||||||
|
root.setConnectionStatus(qsTr("Connection successful!"));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
root.addDebugInfo(qsTr("STDERR: (empty)"));
|
||||||
}
|
}
|
||||||
|
root.addDebugInfo(qsTr("===================="));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -321,6 +589,26 @@ Singleton {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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: {
|
||||||
|
const error = text.trim();
|
||||||
|
if (error && error.length > 0) {
|
||||||
|
// Log error but don't fail - connection might not exist
|
||||||
|
console.warn("Network connection delete error:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: getNetworks
|
id: getNetworks
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue