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

This commit is contained in:
danny
2025-12-17 11:37:48 +01:00
parent 97eb133094
commit 05964dc7fb
4 changed files with 164 additions and 20 deletions
+19 -2
View File
@@ -463,7 +463,8 @@
"info": "Info",
"device-address": "Device address",
"paired": "Paired",
"trusted": "Trusted"
"trusted": "Trusted",
"discoverable": "Discoverable"
}
},
"calendar": {
@@ -2553,7 +2554,23 @@
"connected": "Connected to '{ssid}'",
"disabled": "Disabled",
"disconnected": "Disconnected from '{ssid}'",
"enabled": "Enabled"
"enabled": "Enabled",
"incorrect-password": "Incorrect password",
"network-not-found": "Network not found",
"connection-timeout": "Connection timeout",
"connection-failed": "Connection failed"
},
"bluetooth": {
"enabled": "Enabled",
"disabled": "Disabled",
"pair-failed": "Failed to pair device",
"connect-failed": "Failed to connect to device",
"disconnect-failed": "Failed to disconnect from device",
"forget-failed": "Failed to forget device",
"state-change-failed": "Failed to change Bluetooth state",
"discoverable-enabled": "Discoverable enabled",
"discoverable-disabled": "Discoverable disabled",
"discoverable-change-failed": "Failed to change discoverable state"
}
},
"tooltips": {
@@ -62,6 +62,17 @@ SmartPanel {
baseSize: Style.baseWidgetSize * 0.65
}
// Discoverability toggle (advertising)
NIconButton {
enabled: BluetoothService.enabled
icon: BluetoothService.discoverable ? "broadcast" : "broadcast-off"
tooltipText: I18n.tr("bluetooth.panel.discoverable")
baseSize: Style.baseWidgetSize * 0.8
onClicked: {
BluetoothService.setDiscoverable(!BluetoothService.discoverable);
}
}
NIconButton {
enabled: BluetoothService.enabled
icon: BluetoothService.adapter && BluetoothService.adapter.discovering ? "stop" : "refresh"
+70 -14
View File
@@ -10,6 +10,14 @@ import qs.Services.UI
Singleton {
id: root
// Translation fallback helper (prevents ##key## leaking to UI)
function trOr(key, fallback) {
var v = I18n.tr(key);
if (!v) return fallback;
if (v.indexOf("##") === 0 && v.lastIndexOf("##") === v.length - 2) return fallback;
return v;
}
property bool airplaneModeToggled: false
property bool lastBluetoothBlocked: false
readonly property BluetoothAdapter adapter: Bluetooth.defaultAdapter
@@ -18,6 +26,8 @@ Singleton {
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
// Adapter discoverability (advertising) flag
readonly property bool discoverable: (adapter && adapter.discoverable !== undefined) ? adapter.discoverable : false
readonly property var devices: adapter ? adapter.devices : null
readonly property var pairedDevices: {
if (!adapter || !adapter.devices) {
@@ -71,10 +81,10 @@ Singleton {
if (bluetoothBlockedToggled) {
checkWifiBlocked.running = true;
} else if (adapter.state === BluetoothAdapterState.Enabled) {
ToastService.showNotice(I18n.tr("bluetooth.panel.title"), I18n.tr("toast.bluetooth.enabled"), "bluetooth");
ToastService.showNotice(I18n.tr("bluetooth.panel.title"), trOr("toast.bluetooth.enabled", "Enabled"), "bluetooth");
discoveryTimer.running = true;
} else if (adapter.state === BluetoothAdapterState.Disabled) {
ToastService.showNotice(I18n.tr("bluetooth.panel.title"), I18n.tr("toast.bluetooth.disabled"), "bluetooth-off");
ToastService.showNotice(I18n.tr("bluetooth.panel.title"), trOr("toast.bluetooth.disabled", "Disabled"), "bluetooth-off");
}
}
}
@@ -280,12 +290,14 @@ Singleton {
}
} catch (e) {
Logger.w("Bluetooth", "pairDevice failed", e);
ToastService.showWarning(I18n.tr("bluetooth.panel.title"), I18n.tr("toast.bluetooth.pair-failed"));
// Fallback to connect if pair not supported
try {
device.trusted = true;
device.connect();
} catch (e2) {
Logger.w("Bluetooth", "pairDevice connect fallback failed", e2);
ToastService.showWarning(I18n.tr("bluetooth.panel.title"), I18n.tr("toast.bluetooth.connect-failed"));
}
}
}
@@ -299,26 +311,38 @@ Singleton {
if (!device) {
return;
}
device.trusted = true;
device.connect();
try {
device.trusted = true;
device.connect();
} catch (e) {
Logger.w("Bluetooth", "connectDeviceWithTrust failed", e);
ToastService.showWarning(I18n.tr("bluetooth.panel.title"), I18n.tr("toast.bluetooth.connect-failed"));
}
}
function disconnectDevice(device) {
if (!device) {
return;
}
device.disconnect();
try {
device.disconnect();
} catch (e) {
Logger.w("Bluetooth", "disconnectDevice failed", e);
ToastService.showWarning(I18n.tr("bluetooth.panel.title"), I18n.tr("toast.bluetooth.disconnect-failed"));
}
}
function forgetDevice(device) {
if (!device) {
return;
}
device.trusted = false;
device.forget();
try {
device.trusted = false;
device.forget();
} catch (e) {
Logger.w("Bluetooth", "forgetDevice failed", e);
ToastService.showWarning(I18n.tr("bluetooth.panel.title"), I18n.tr("toast.bluetooth.forget-failed"));
}
}
function setBluetoothEnabled(state) {
@@ -328,7 +352,32 @@ Singleton {
}
Logger.i("Bluetooth", "SetBluetoothEnabled", state);
adapter.enabled = state;
try {
adapter.enabled = state;
} catch (e) {
Logger.w("Bluetooth", "Enable/Disable failed", e);
ToastService.showWarning(I18n.tr("bluetooth.panel.title"), I18n.tr("toast.bluetooth.state-change-failed"));
}
}
// Toggle adapter discoverability (advertising visibility)
function setDiscoverable(state) {
if (!adapter) {
Logger.w("Bluetooth", "setDiscoverable: No adapter available");
return;
}
try {
adapter.discoverable = state;
if (state) {
ToastService.showNotice(I18n.tr("bluetooth.panel.title"), trOr("toast.bluetooth.discoverable-enabled", "Discoverable enabled"), "broadcast");
} else {
ToastService.showNotice(I18n.tr("bluetooth.panel.title"), trOr("toast.bluetooth.discoverable-disabled", "Discoverable disabled"), "broadcast-off");
}
Logger.i("Bluetooth", "Discoverable state set to:", state);
} catch (e) {
Logger.w("Bluetooth", "Failed to change discoverable state", e);
ToastService.showWarning(I18n.tr("bluetooth.panel.title"), trOr("toast.bluetooth.discoverable-change-failed", "Failed to change discoverable state"));
}
}
Process {
@@ -339,7 +388,7 @@ Singleton {
stdout: StdioCollector {
onStreamFinished: {
var wifiBlocked = text && text.trim().indexOf("Soft blocked: yes") !== -1;
Logger.d("Network", "Wi-Fi adapter was detected as blocked:", blocked);
Logger.d("Network", "Wi-Fi adapter was detected as blocked:", wifiBlocked);
// Check if airplane mode has been toggled
if (wifiBlocked && wifiBlocked === root.blocked) {
@@ -351,13 +400,20 @@ Singleton {
NetworkService.setWifiEnabled(true);
ToastService.showNotice(I18n.tr("toast.airplane-mode.title"), I18n.tr("toast.airplane-mode.disabled"), "plane-off");
} else if (adapter.enabled) {
ToastService.showNotice(I18n.tr("bluetooth.panel.title"), I18n.tr("toast.bluetooth.enabled"), "bluetooth");
ToastService.showNotice(I18n.tr("bluetooth.panel.title"), trOr("toast.bluetooth.enabled", "Enabled"), "bluetooth");
discoveryTimer.running = true;
} else {
ToastService.showNotice(I18n.tr("bluetooth.panel.title"), I18n.tr("toast.bluetooth.disabled"), "bluetooth-off");
ToastService.showNotice(I18n.tr("bluetooth.panel.title"), trOr("toast.bluetooth.disabled", "Disabled"), "bluetooth-off");
}
root.airplaneModeToggled = false;
}
}
stderr: StdioCollector {
onStreamFinished: {
if (text && text.trim()) {
Logger.w("Bluetooth", "rfkill (wifi) stderr:", text.trim());
}
}
}
}
}
+64 -4
View File
@@ -286,6 +286,13 @@ Singleton {
}
}
}
stderr: StdioCollector {
onStreamFinished: {
if (text && text.trim()) {
Logger.w("Network", "ethernetState nmcli stderr:", text.trim());
}
}
}
}
// Discover connected WiFi interface
@@ -321,6 +328,18 @@ Singleton {
}
}
}
stderr: StdioCollector {
onStreamFinished: {
if (text && text.trim()) {
Logger.w("Network", "nmcli device list stderr:", text.trim());
}
// Fail-safe to avoid spinner
if (!root.activeWifiIf) {
root.activeWifiDetailsTimestamp = Date.now();
root.detailsLoading = false;
}
}
}
}
// Fetch IPv4 and gateway for the interface
@@ -365,6 +384,15 @@ Singleton {
wifiIwLinkProcess.running = true;
}
}
stderr: StdioCollector {
onStreamFinished: {
if (text && text.trim()) {
Logger.w("Network", "nmcli device show stderr:", text.trim());
}
// Still proceed to finalize details to avoid UI waiting forever
root.activeWifiDetailsTimestamp = Date.now();
}
}
}
// Optional: query WiFi bitrate via iw if available
@@ -426,6 +454,15 @@ Singleton {
root.detailsLoading = false;
}
}
stderr: StdioCollector {
onStreamFinished: {
if (text && text.trim()) {
Logger.w("Network", "iw link stderr:", text.trim());
}
root.activeWifiDetailsTimestamp = Date.now();
root.detailsLoading = false;
}
}
}
// Only check the state of the actual interface
@@ -444,6 +481,13 @@ Singleton {
}
}
}
stderr: StdioCollector {
onStreamFinished: {
if (text && text.trim()) {
Logger.w("Network", "Wi-Fi state query stderr:", text.trim());
}
}
}
}
// Process to enable/disable the Wi-Fi interface
@@ -571,6 +615,19 @@ Singleton {
scanProcess.running = true;
}
}
stderr: StdioCollector {
onStreamFinished: {
if (text && text.trim()) {
Logger.w("Network", "Profile check stderr:", text.trim());
}
// Fail safe
if (root.scanning) {
root.scanning = false;
delayedScanTimer.interval = 5000;
delayedScanTimer.restart();
}
}
}
}
Process {
@@ -797,17 +854,20 @@ Singleton {
if (text.trim()) {
// Parse common errors
if (text.indexOf("Secrets were required") !== -1 || text.indexOf("no secrets provided") !== -1) {
root.lastError = "Incorrect password";
root.lastError = I18n.tr("toast.wifi.incorrect-password");
forget(connectProcess.ssid);
} else if (text.indexOf("No network with SSID") !== -1) {
root.lastError = "Network not found";
root.lastError = I18n.tr("toast.wifi.network-not-found");
} else if (text.indexOf("Timeout") !== -1) {
root.lastError = "Connection timeout";
root.lastError = I18n.tr("toast.wifi.connection-timeout");
} else {
root.lastError = text.split("\n")[0].trim();
// Generic fallback
root.lastError = I18n.tr("toast.wifi.connection-failed");
}
Logger.w("Network", "Connect error: " + text);
// Notify user about the failure
ToastService.showWarning(I18n.tr("wifi.panel.title"), root.lastError || I18n.tr("toast.wifi.connection-failed"));
}
}
}