Improved network and Bluetooth panel with additional info and options to manage devices.

This commit is contained in:
danny
2025-12-17 06:10:48 +01:00
parent 62e22fbbb7
commit 97eb133094
6 changed files with 190 additions and 123 deletions
@@ -52,7 +52,8 @@ NBox {
readonly property bool isBusy: BluetoothService.isDeviceBusy(modelData)
readonly property bool isExpanded: root.expandedDeviceKey === BluetoothService.deviceKey(modelData)
function getContentColor(defaultColor = Color.mOnSurface) {
function getContentColor(defaultColor) {
if (defaultColor === undefined) defaultColor = Color.mOnSurface;
if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting)
return Color.mPrimary;
if (modelData.blocked)
+7 -7
View File
@@ -58,7 +58,7 @@ SmartPanel {
NToggle {
id: bluetoothSwitch
checked: BluetoothService.enabled
onToggled: checked => BluetoothService.setBluetoothEnabled(checked)
onToggled: function(checked) { BluetoothService.setBluetoothEnabled(checked); }
baseSize: Style.baseWidgetSize * 0.65
}
@@ -150,7 +150,7 @@ SmartPanel {
property var items: {
if (!BluetoothService.adapter || !Bluetooth.devices)
return [];
var filtered = Bluetooth.devices.values.filter(dev => dev && !dev.blocked && dev.connected);
var filtered = Bluetooth.devices.values.filter(function(dev) { return dev && !dev.blocked && dev.connected; });
filtered = BluetoothService.dedupeDevices(filtered);
return BluetoothService.sortDevices(filtered);
}
@@ -166,7 +166,7 @@ SmartPanel {
property var items: {
if (!BluetoothService.adapter || !Bluetooth.devices)
return [];
var filtered = Bluetooth.devices.values.filter(dev => dev && !dev.blocked && !dev.connected && (dev.paired || dev.trusted));
var filtered = Bluetooth.devices.values.filter(function(dev) { return dev && !dev.blocked && !dev.connected && (dev.paired || dev.trusted); });
filtered = BluetoothService.dedupeDevices(filtered);
return BluetoothService.sortDevices(filtered);
}
@@ -181,7 +181,7 @@ SmartPanel {
property var items: {
if (!BluetoothService.adapter || !Bluetooth.devices)
return [];
var filtered = Bluetooth.devices.values.filter(dev => dev && !dev.blocked && !dev.paired && !dev.trusted);
var filtered = Bluetooth.devices.values.filter(function(dev) { return dev && !dev.blocked && !dev.paired && !dev.trusted; });
filtered = BluetoothService.dedupeDevices(filtered);
return BluetoothService.sortDevices(filtered);
}
@@ -199,9 +199,9 @@ SmartPanel {
return false;
}
var availableCount = Bluetooth.devices.values.filter(dev => {
return dev && !dev.paired && !dev.pairing && !dev.blocked && (dev.signalStrength === undefined || dev.signalStrength > 0);
}).length;
var availableCount = Bluetooth.devices.values.filter(function(dev) {
return dev && !dev.paired && !dev.pairing && !dev.blocked && (dev.signalStrength === undefined || dev.signalStrength > 0);
}).length;
return (availableCount === 0);
}
+45 -11
View File
@@ -215,7 +215,7 @@ NBox {
NIconButton {
visible: modelData.connected && NetworkService.disconnectingFrom !== modelData.ssid
icon: "info-circle"
tooltipText: I18n.tr("wifi.panel.info")
tooltipText: root.trOr("wifi.panel.info", "Info")
baseSize: Style.baseWidgetSize * 0.8
onClicked: {
if (root.infoSsid === modelData.ssid) {
@@ -276,6 +276,7 @@ NBox {
border.width: Style.borderS
border.color: Color.mOutline
height: infoColumn.implicitHeight + Style.marginS * 2
clip: true
ColumnLayout {
id: infoColumn
@@ -283,12 +284,6 @@ NBox {
anchors.margins: Style.marginS
spacing: Style.marginXS
RowLayout {
spacing: Style.marginS
NIcon { icon: NetworkService.signalIcon(modelData.signal, modelData.connected); pointSize: Style.fontSizeM; color: Color.mOnSurface }
NText { text: "Signal: " + modelData.signal + "%"; pointSize: Style.fontSizeXS; color: Color.mOnSurface }
}
RowLayout {
spacing: Style.marginS
NIcon { icon: "lock"; pointSize: Style.fontSizeM; color: Color.mOnSurface }
@@ -306,17 +301,56 @@ NBox {
spacing: Style.marginS
NIcon { icon: "activity"; pointSize: Style.fontSizeM; color: Color.mOnSurface }
NText { text: root.trOr("wifi.panel.link-speed", "Link speed") + ": "; pointSize: Style.fontSizeXS; color: Color.mOnSurfaceVariant }
NText { text: (NetworkService.activeWifiDetails.rate || "-"); pointSize: Style.fontSizeXS; color: Color.mOnSurface }
NText {
text: (NetworkService.activeWifiDetails.rateShort && NetworkService.activeWifiDetails.rateShort.length > 0)
? NetworkService.activeWifiDetails.rateShort
: ((NetworkService.activeWifiDetails.rate && NetworkService.activeWifiDetails.rate.length > 0)
? NetworkService.activeWifiDetails.rate
: "-");
pointSize: Style.fontSizeXS
color: Color.mOnSurface
Layout.fillWidth: true
elide: Text.ElideRight
}
}
RowLayout {
spacing: Style.marginS
NIcon { icon: "router"; pointSize: Style.fontSizeM; color: Color.mOnSurface }
NText { text: "IPv4: "; pointSize: Style.fontSizeXS; color: Color.mOnSurfaceVariant }
NText { text: (NetworkService.activeWifiDetails.ipv4 || "-"); pointSize: Style.fontSizeXS; color: Color.mOnSurface }
NText { text: "•"; pointSize: Style.fontSizeXS; color: Color.mOnSurfaceVariant }
NText {
text: (NetworkService.activeWifiDetails.ipv4 || "-")
pointSize: Style.fontSizeXS
color: Color.mOnSurface
Layout.fillWidth: true
elide: Text.ElideRight
}
}
RowLayout {
spacing: Style.marginS
NIcon { icon: "router"; pointSize: Style.fontSizeM; color: Color.mOnSurface }
NText { text: root.trOr("wifi.panel.gateway", "Gateway") + ": "; pointSize: Style.fontSizeXS; color: Color.mOnSurfaceVariant }
NText { text: (NetworkService.activeWifiDetails.gateway4 || "-"); pointSize: Style.fontSizeXS; color: Color.mOnSurface }
NText {
text: (NetworkService.activeWifiDetails.gateway4 || "-")
pointSize: Style.fontSizeXS
color: Color.mOnSurface
Layout.fillWidth: true
elide: Text.ElideRight
}
}
RowLayout {
spacing: Style.marginS
NIcon { icon: "server"; pointSize: Style.fontSizeM; color: Color.mOnSurface }
NText { text: root.trOr("wifi.panel.dns", "DNS") + ": "; pointSize: Style.fontSizeXS; color: Color.mOnSurfaceVariant }
NText {
text: (NetworkService.activeWifiDetails.dns || "-")
pointSize: Style.fontSizeXS
color: Color.mOnSurface
Layout.fillWidth: true
elide: Text.ElideRight
}
}
}
}
+12 -10
View File
@@ -22,15 +22,15 @@ SmartPanel {
if (!Settings.data.network.wifiEnabled)
return [];
const nets = Object.values(NetworkService.networks);
const known = nets.filter(n => n.connected || n.existing || n.cached);
var nets = Object.values(NetworkService.networks);
var known = nets.filter(function(n) { return n.connected || n.existing || n.cached; });
// Sort: connected first, then by signal strength
known.sort((a, b) => {
if (a.connected !== b.connected)
return b.connected - a.connected;
return b.signal - a.signal;
});
known.sort(function(a, b) {
if (a.connected !== b.connected)
return (b.connected ? 1 : 0) - (a.connected ? 1 : 0);
return b.signal - a.signal;
});
return known;
}
@@ -39,11 +39,11 @@ SmartPanel {
if (!Settings.data.network.wifiEnabled)
return [];
const nets = Object.values(NetworkService.networks);
const available = nets.filter(n => !n.connected && !n.existing && !n.cached);
var nets = Object.values(NetworkService.networks);
var available = nets.filter(function(n) { return !n.connected && !n.existing && !n.cached; });
// Sort by signal strength
available.sort((a, b) => b.signal - a.signal);
available.sort(function(a, b) { return b.signal - a.signal; });
return available;
}
@@ -51,6 +51,8 @@ SmartPanel {
onOpened: {
hasHadNetworks = false;
NetworkService.scan();
// Preload active WiFi details so Info shows instantly
NetworkService.refreshActiveWifiDetails();
}
onKnownNetworksChanged: {
+37 -37
View File
@@ -13,34 +13,34 @@ Singleton {
property bool airplaneModeToggled: false
property bool lastBluetoothBlocked: false
readonly property BluetoothAdapter adapter: Bluetooth.defaultAdapter
readonly property int state: adapter?.state ?? 0
readonly property int state: (adapter && adapter.state !== undefined) ? adapter.state : 0
readonly property bool available: (adapter !== null)
readonly property bool enabled: adapter?.enabled ?? false
readonly property bool blocked: (adapter?.state === BluetoothAdapterState.Blocked)
readonly property bool discovering: (adapter && adapter.discovering) ?? false
readonly property bool enabled: (adapter && adapter.enabled !== undefined) ? adapter.enabled : false
readonly property bool blocked: (adapter && adapter.state === BluetoothAdapterState.Blocked)
readonly property bool discovering: (adapter && adapter.discovering) ? adapter.discovering : false
readonly property var devices: adapter ? adapter.devices : null
readonly property var pairedDevices: {
if (!adapter || !adapter.devices) {
return [];
}
return adapter.devices.values.filter(dev => {
return dev && (dev.paired || dev.trusted);
});
return adapter.devices.values.filter(function(dev) {
return dev && (dev.paired || dev.trusted);
});
}
readonly property var connectedDevices: {
if (!adapter || !adapter.devices) {
return [];
}
return adapter.devices.values.filter(dev => dev && dev.connected);
return adapter.devices.values.filter(function(dev) { return dev && dev.connected; });
}
readonly property var allDevicesWithBattery: {
if (!adapter || !adapter.devices) {
return [];
}
return adapter.devices.values.filter(dev => {
return dev && dev.batteryAvailable && dev.battery > 0;
});
return adapter.devices.values.filter(function(dev) {
return dev && dev.batteryAvailable && dev.battery > 0;
});
}
function init() {
@@ -80,22 +80,22 @@ Singleton {
}
function sortDevices(devices) {
return devices.sort((a, b) => {
var aName = a.name || a.deviceName || "";
var bName = b.name || b.deviceName || "";
return devices.sort(function(a, b) {
var aName = a.name || a.deviceName || "";
var bName = b.name || b.deviceName || "";
var aHasRealName = aName.includes(" ") && aName.length > 3;
var bHasRealName = bName.includes(" ") && bName.length > 3;
var aHasRealName = aName.indexOf(" ") !== -1 && aName.length > 3;
var bHasRealName = bName.indexOf(" ") !== -1 && bName.length > 3;
if (aHasRealName && !bHasRealName)
return -1;
if (!aHasRealName && bHasRealName)
return 1;
if (aHasRealName && !bHasRealName)
return -1;
if (!aHasRealName && bHasRealName)
return 1;
var aSignal = (a.signalStrength !== undefined && a.signalStrength > 0) ? a.signalStrength : 0;
var bSignal = (b.signalStrength !== undefined && b.signalStrength > 0) ? b.signalStrength : 0;
return bSignal - aSignal;
});
var aSignal = (a.signalStrength !== undefined && a.signalStrength > 0) ? a.signalStrength : 0;
var bSignal = (b.signalStrength !== undefined && b.signalStrength > 0) ? b.signalStrength : 0;
return bSignal - aSignal;
});
}
function getDeviceIcon(device) {
@@ -105,37 +105,37 @@ Singleton {
var name = (device.name || device.deviceName || "").toLowerCase();
var icon = (device.icon || "").toLowerCase();
if (icon.includes("controller") || icon.includes("gamepad") || name.includes("controller") || name.includes("gamepad")) {
if (icon.indexOf("controller") !== -1 || icon.indexOf("gamepad") !== -1 || name.indexOf("controller") !== -1 || name.indexOf("gamepad") !== -1) {
return "bt-device-gamepad";
}
if (icon.includes("microphone") || name.includes("microphone")) {
if (icon.indexOf("microphone") !== -1 || name.indexOf("microphone") !== -1) {
return "bt-device-microphone";
}
if (name.includes("pod") || name.includes("bud") || name.includes("minor")) {
if (name.indexOf("pod") !== -1 || name.indexOf("bud") !== -1 || name.indexOf("minor") !== -1) {
return "bt-device-earbuds";
}
if (icon.includes("headset") || name.includes("arctis") || name.includes("headset") || name.includes("major")) {
if (icon.indexOf("headset") !== -1 || name.indexOf("arctis") !== -1 || name.indexOf("headset") !== -1 || name.indexOf("major") !== -1) {
return "bt-device-headset";
}
if (icon.includes("headphone") || name.includes("headphone")) {
if (icon.indexOf("headphone") !== -1 || name.indexOf("headphone") !== -1) {
return "bt-device-headphones";
}
if (icon.includes("mouse") || name.includes("mouse")) {
if (icon.indexOf("mouse") !== -1 || name.indexOf("mouse") !== -1) {
return "bt-device-mouse";
}
if (icon.includes("keyboard") || name.includes("keyboard")) {
if (icon.indexOf("keyboard") !== -1 || name.indexOf("keyboard") !== -1) {
return "bt-device-keyboard";
}
if (icon.includes("watch") || name.includes("watch")) {
if (icon.indexOf("watch") !== -1 || name.indexOf("watch") !== -1) {
return "bt-device-watch";
}
if (icon.includes("speaker") || name.includes("speaker") || name.includes("audio") || name.includes("sound")) {
if (icon.indexOf("speaker") !== -1 || name.indexOf("speaker") !== -1 || name.indexOf("audio") !== -1 || name.indexOf("sound") !== -1) {
return "bt-device-speaker";
}
if (icon.includes("display") || name.includes("tv")) {
if (icon.indexOf("display") !== -1 || name.indexOf("tv") !== -1) {
return "bt-device-tv";
}
if (icon.includes("phone") || name.includes("phone") || name.includes("iphone") || name.includes("android") || name.includes("samsung")) {
if (icon.indexOf("phone") !== -1 || name.indexOf("phone") !== -1 || name.indexOf("iphone") !== -1 || name.indexOf("android") !== -1 || name.indexOf("samsung") !== -1) {
return "bt-device-phone";
}
return "bt-device-generic";
@@ -196,7 +196,7 @@ Singleton {
}
function getBattery(device) {
return `Battery: ${Math.round(device.battery * 100)}%`;
return "Battery: " + Math.round(device.battery * 100) + "%";
}
function getSignalIcon(device) {
@@ -338,7 +338,7 @@ Singleton {
stdout: StdioCollector {
onStreamFinished: {
const wifiBlocked = text && text.trim().includes("Soft blocked: yes");
var wifiBlocked = text && text.trim().indexOf("Soft blocked: yes") !== -1;
Logger.d("Network", "Wi-Fi adapter was detected as blocked:", blocked);
// Check if airplane mode has been toggled
+87 -57
View File
@@ -78,6 +78,8 @@ Singleton {
Logger.i("Network", "Service started");
syncWifiState();
scan();
// Preload details at startup if already connected
refreshActiveWifiDetails();
}
// Save cache with debounce
@@ -163,7 +165,8 @@ Singleton {
Logger.d("Network", "Wi-Fi scan in progress...");
}
function connect(ssid, password = "") {
function connect(ssid, password) {
if (password === undefined) password = "";
if (connecting)
return;
connecting = true;
@@ -171,7 +174,7 @@ Singleton {
lastError = "";
// Check if we have a saved connection
if (networks[ssid]?.existing || cachedNetworks[ssid]) {
if ((networks[ssid] && networks[ssid].existing) || cachedNetworks[ssid]) {
connectProcess.mode = "saved";
connectProcess.ssid = ssid;
connectProcess.password = "";
@@ -243,7 +246,8 @@ Singleton {
}
// Helper functions
function signalIcon(signal, isConnected = false) {
function signalIcon(signal, isConnected) {
if (isConnected === undefined) isConnected = false;
if (isConnected && !root.internetConnectivity)
return "world-off";
if (signal >= 80)
@@ -267,10 +271,15 @@ Singleton {
stdout: StdioCollector {
onStreamFinished: {
const connected = text.split("\n").some(line => {
const parts = line.split(":");
return parts[1] === "ethernet" && parts[2] === "connected";
});
var connected = false;
var lines = text.split("\n");
for (var i = 0; i < lines.length; i++) {
var parts = lines[i].split(":");
if (parts.length >= 3 && parts[1] === "ethernet" && parts[2] === "connected") {
connected = true;
break;
}
}
if (root.ethernetConnected !== connected) {
root.ethernetConnected = connected;
Logger.d("Network", "Ethernet connected:", root.ethernetConnected);
@@ -335,11 +344,11 @@ Singleton {
if (idx === -1) continue;
const key = line.substring(0, idx);
const val = line.substring(idx + 1);
if (key.startsWith("IP4.ADDRESS")) {
if (key.indexOf("IP4.ADDRESS") === 0) {
ipv4 = val.split("/")[0];
} else if (key === "IP4.GATEWAY") {
gw4 = val;
} else if (key.startsWith("IP4.DNS")) {
} else if (key.indexOf("IP4.DNS") === 0) {
if (val && dnsServers.indexOf(val) === -1) {
dnsServers.push(val);
}
@@ -370,10 +379,11 @@ Singleton {
const details = root.activeWifiDetails || ({});
let rate = "";
const lines = text.split("\n");
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (line.toLowerCase().startsWith("tx bitrate:")) {
rate = line.substring(11).trim();
for (var k = 0; k < lines.length; k++) {
var line2 = lines[k].trim();
var low = line2.toLowerCase();
if (low.indexOf("tx bitrate:") === 0) {
rate = line2.substring(11).trim();
break;
}
}
@@ -515,7 +525,7 @@ Singleton {
id: pingCheckProcess
command: ["sh", "-c", "ping -c1 -W2 ping.archlinux.org >/dev/null 2>&1 || " + "ping -c1 -W2 1.1.1.1 >/dev/null 2>&1 || " + "curl -fsI --max-time 5 https://cloudflare.com/cdn-cgi/trace >/dev/null 2>&1"]
onExited: (exitCode, exitStatus) => {
onExited: function(exitCode, exitStatus) {
if (exitCode === 0) {
connectivityCheckProcess.failedChecks = 0;
} else {
@@ -549,10 +559,13 @@ Singleton {
return;
}
const profiles = {};
const lines = text.split("\n").filter(l => l.trim());
for (const line of lines) {
profiles[line.trim()] = true;
var profiles = {};
var lines = text.split("\n");
for (var i = 0; i < lines.length; i++) {
var l = lines[i];
if (l && l.trim()) {
profiles[l.trim()] = true;
}
}
scanProcess.existingProfiles = profiles;
scanProcess.running = true;
@@ -657,8 +670,8 @@ Singleton {
// Logging
const oldSSIDs = Object.keys(root.networks);
const newSSIDs = Object.keys(networksMap);
const newNetworks = newSSIDs.filter(ssid => !oldSSIDs.includes(ssid));
const lostNetworks = oldSSIDs.filter(ssid => !newSSIDs.includes(ssid));
const newNetworks = newSSIDs.filter(function(ssid) { return oldSSIDs.indexOf(ssid) === -1; });
const lostNetworks = oldSSIDs.filter(function(ssid) { return newSSIDs.indexOf(ssid) === -1; });
if (newNetworks.length > 0 || lostNetworks.length > 0) {
if (newNetworks.length > 0) {
@@ -674,6 +687,22 @@ Singleton {
root.networks = networksMap;
root.scanning = false;
// Preload active WiFi details so Info panel shows instantly when opened
// This is lightweight and guarded by detailsLoading + TTL.
var hasConnected = false;
for (var ssid in networksMap) {
if (networksMap.hasOwnProperty(ssid)) {
var net = networksMap[ssid];
if (net && net.connected) {
hasConnected = true;
break;
}
}
}
if (hasConnected) {
root.refreshActiveWifiDetails();
}
// Check if we need to start a new scan
if (root.scanPending) {
root.scanPending = false;
@@ -707,7 +736,7 @@ Singleton {
if (mode === "saved") {
return ["nmcli", "connection", "up", "id", ssid];
} else {
const cmd = ["nmcli", "device", "wifi", "connect", ssid];
var cmd = ["nmcli", "device", "wifi", "connect", ssid];
if (password) {
cmd.push("password", password);
}
@@ -725,7 +754,7 @@ Singleton {
// on success. Empty output or other messages indicate failure.
const output = text.trim();
if (!output || (!output.includes("successfully activated") && !output.includes("Connection successfully"))) {
if (!output || (output.indexOf("successfully activated") === -1 && output.indexOf("Connection successfully") === -1)) {
// No success message - likely an error occurred
// Don't update anything, let stderr handler deal with it
return;
@@ -744,9 +773,12 @@ Singleton {
// Immediately update the UI before scanning
root.updateNetworkStatus(connectProcess.ssid, true);
// Preload details immediately so Info panel has data instantly
root.refreshActiveWifiDetails();
root.connecting = false;
root.connectingTo = "";
Logger.i("Network", `Connected to network: '${connectProcess.ssid}'`);
Logger.i("Network", "Connected to network: '" + connectProcess.ssid + "'");
ToastService.showNotice(I18n.tr("wifi.panel.title"), I18n.tr("toast.wifi.connected", {
"ssid": connectProcess.ssid
}), "wifi");
@@ -764,12 +796,12 @@ Singleton {
if (text.trim()) {
// Parse common errors
if (text.includes("Secrets were required") || text.includes("no secrets provided")) {
if (text.indexOf("Secrets were required") !== -1 || text.indexOf("no secrets provided") !== -1) {
root.lastError = "Incorrect password";
forget(connectProcess.ssid);
} else if (text.includes("No network with SSID")) {
} else if (text.indexOf("No network with SSID") !== -1) {
root.lastError = "Network not found";
} else if (text.includes("Timeout")) {
} else if (text.indexOf("Timeout") !== -1) {
root.lastError = "Connection timeout";
} else {
root.lastError = text.split("\n")[0].trim();
@@ -789,7 +821,7 @@ Singleton {
stdout: StdioCollector {
onStreamFinished: {
Logger.i("Network", `Disconnected from network: '${disconnectProcess.ssid}'`);
Logger.i("Network", "Disconnected from network: '" + disconnectProcess.ssid + "'");
ToastService.showNotice(I18n.tr("wifi.panel.title"), I18n.tr("toast.wifi.disconnected", {
"ssid": disconnectProcess.ssid
}), "wifi-off");
@@ -823,38 +855,36 @@ Singleton {
running: false
// Try multiple common profile name patterns
command: ["sh", "-c", `
ssid="$1"
deleted=false
# Try exact SSID match first
if nmcli connection delete id "$ssid" 2>/dev/null; then
echo "Deleted profile: $ssid"
deleted=true
fi
# Try "Auto <SSID>" pattern
if nmcli connection delete id "Auto $ssid" 2>/dev/null; then
echo "Deleted profile: Auto $ssid"
deleted=true
fi
# Try "<SSID> 1", "<SSID> 2", etc. patterns
for i in 1 2 3; do
if nmcli connection delete id "$ssid $i" 2>/dev/null; then
echo "Deleted profile: $ssid $i"
deleted=true
fi
done
if [ "$deleted" = "false" ]; then
echo "No profiles found for SSID: $ssid"
fi
`, "--", ssid]
command: {
var script = "";
script += "ssid=\"$1\"\n";
script += "deleted=false\n\n";
script += "# Try exact SSID match first\n";
script += "if nmcli connection delete id \"$ssid\" 2>/dev/null; then\n";
script += " echo \"Deleted profile: $ssid\"\n";
script += " deleted=true\n";
script += "fi\n\n";
script += "# Try \"Auto $ssid\" pattern\n";
script += "if nmcli connection delete id \"Auto $ssid\" 2>/dev/null; then\n";
script += " echo \"Deleted profile: Auto $ssid\"\n";
script += " deleted=true\n";
script += "fi\n\n";
script += "# Try \"$ssid 1\", \"$ssid 2\", etc. patterns\n";
script += "for i in 1 2 3; do\n";
script += " if nmcli connection delete id \"$ssid $i\" 2>/dev/null; then\n";
script += " echo \"Deleted profile: $ssid $i\"\n";
script += " deleted=true\n";
script += " fi\n";
script += "done\n\n";
script += "if [ \"$deleted\" = \"false\" ]; then\n";
script += " echo \"No profiles found for SSID: $ssid\"\n";
script += "fi\n";
return ["sh", "-c", script, "--", ssid];
}
stdout: StdioCollector {
onStreamFinished: {
Logger.i("Network", `Forget network: "${forgetProcess.ssid}"`);
Logger.i("Network", "Forget network: \"" + forgetProcess.ssid + "\"");
Logger.d("Network", text.trim().replace(/[\r\n]/g, " "));
// Update both cached and existing status immediately
@@ -878,7 +908,7 @@ Singleton {
stderr: StdioCollector {
onStreamFinished: {
root.forgettingNetwork = "";
if (text.trim() && !text.includes("No profiles found")) {
if (text.trim() && text.indexOf("No profiles found") === -1) {
Logger.w("Network", "Forget error: " + text);
}
// Still Trigger a scan even on error