mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
feat(bluetooth): auto-connect paired & trusted devices with toggle control
Adds automatic reconnection of paired and trusted Bluetooth devices when Bluetooth is enabled or when the shell starts. The feature is fully toggleable ON/OFF from three places: - Settings > Connections > Bluetooth (persistent NToggle) - Bluetooth Panel quick toggle (NIconButton in header) - IPC commands: toggleAutoConnect, enableAutoConnect, disableAutoConnect Changes: - New setting: bluetoothAutoConnect (default: true) - Auto-trust devices upon pairing via Instantiator/Connections watcher - 2s delay after BT enable to allow adapter initialization - Respects airplane mode - Toast notification when auto-connect fires
This commit is contained in:
@@ -911,6 +911,8 @@
|
||||
},
|
||||
"connections": {
|
||||
"authentication-required": "Authentication required",
|
||||
"bluetooth-auto-connect-description": "Automatically connect to trusted paired devices when Bluetooth is enabled.",
|
||||
"bluetooth-auto-connect-label": "Auto-connect paired devices",
|
||||
"bluetooth-devices-unnamed": "Unnamed devices are not shown.",
|
||||
"bluetooth-discoverable": "This device is discoverable as <b>{hostName}</b> while this settings tab is open.",
|
||||
"bluetooth-rssi-polling-description": "Periodically sample RSSI for connected devices via bluetoothctl. May not be available for all devices; uses minimal resources when enabled.",
|
||||
@@ -1777,6 +1779,9 @@
|
||||
},
|
||||
"bluetooth": {
|
||||
"address-copied": "Address copied to clipboard",
|
||||
"auto-connect-disabled": "Auto-connect disabled",
|
||||
"auto-connect-enabled": "Auto-connect enabled",
|
||||
"auto-connecting": "Connecting to {count} device(s)...",
|
||||
"confirm-code": "Confirm code {value} on the other device.",
|
||||
"connect-failed": "Failed to connect to device",
|
||||
"disconnect-failed": "Failed to disconnect from device",
|
||||
@@ -1849,6 +1854,8 @@
|
||||
},
|
||||
"tooltips": {
|
||||
"add-widget": "Add widget",
|
||||
"bluetooth-auto-connect-off": "Auto-connect is off — click to enable",
|
||||
"bluetooth-auto-connect-on": "Auto-connect is on — click to disable",
|
||||
"bluetooth-devices": "Bluetooth devices",
|
||||
"brightness-at": "Brightness: {brightness}%",
|
||||
"click-to-start-recording": "Screen recorder (start recording)",
|
||||
|
||||
@@ -559,6 +559,7 @@ Singleton {
|
||||
property string bluetoothDetailsViewMode: "grid" // "grid" or "list"
|
||||
property bool bluetoothHideUnnamedDevices: false
|
||||
property bool disableDiscoverability: false
|
||||
property bool bluetoothAutoConnect: true
|
||||
}
|
||||
|
||||
// session menu
|
||||
|
||||
@@ -59,6 +59,14 @@ SmartPanel {
|
||||
baseSize: Style.baseWidgetSize * 0.65
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: Settings.data.network.bluetoothAutoConnect ? "bluetooth-connected" : "bluetooth"
|
||||
tooltipText: Settings.data.network.bluetoothAutoConnect ? I18n.tr("tooltips.bluetooth-auto-connect-on") : I18n.tr("tooltips.bluetooth-auto-connect-off")
|
||||
colorFg: Settings.data.network.bluetoothAutoConnect ? Color.mPrimary : Color.mOnSurfaceVariant
|
||||
baseSize: Style.baseWidgetSize * 0.8
|
||||
onClicked: Settings.data.network.bluetoothAutoConnect = !Settings.data.network.bluetoothAutoConnect
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "settings"
|
||||
tooltipText: I18n.tr("tooltips.open-settings")
|
||||
|
||||
@@ -313,6 +313,13 @@ Item {
|
||||
anchors.margins: Style.marginXL
|
||||
spacing: Style.marginM
|
||||
|
||||
NToggle {
|
||||
label: I18n.tr("panels.connections.bluetooth-auto-connect-label")
|
||||
description: I18n.tr("panels.connections.bluetooth-auto-connect-description")
|
||||
checked: Settings.data.network.bluetoothAutoConnect
|
||||
onToggled: checked => Settings.data.network.bluetoothAutoConnect = checked
|
||||
}
|
||||
|
||||
NToggle {
|
||||
label: I18n.tr("panels.connections.hide-unnamed-devices-label")
|
||||
description: I18n.tr("panels.connections.hide-unnamed-devices-description")
|
||||
|
||||
@@ -606,6 +606,15 @@ Singleton {
|
||||
bluetoothPanel?.toggle(null, "Bluetooth");
|
||||
});
|
||||
}
|
||||
function toggleAutoConnect() {
|
||||
Settings.data.network.bluetoothAutoConnect = !Settings.data.network.bluetoothAutoConnect;
|
||||
}
|
||||
function enableAutoConnect() {
|
||||
Settings.data.network.bluetoothAutoConnect = true;
|
||||
}
|
||||
function disableAutoConnect() {
|
||||
Settings.data.network.bluetoothAutoConnect = false;
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
|
||||
@@ -67,6 +67,17 @@ Singleton {
|
||||
// Internal: temporarily pause discovery during pair/connect to reduce HCI churn
|
||||
property bool _discoveryWasRunning: false
|
||||
property bool _ctlInit: false
|
||||
property bool _autoConnectInProgress: false
|
||||
|
||||
// Mirror the setting so we can react to changes via onAutoConnectEnabledChanged
|
||||
property bool autoConnectEnabled: Settings?.data?.network?.bluetoothAutoConnect ?? true
|
||||
onAutoConnectEnabledChanged: {
|
||||
if (autoConnectEnabled && adapter && adapter.enabled) {
|
||||
autoConnectTimer.restart();
|
||||
} else {
|
||||
autoConnectTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: initDelayTimer
|
||||
@@ -75,6 +86,29 @@ Singleton {
|
||||
repeat: false
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: autoConnectTimer
|
||||
interval: 2000
|
||||
repeat: false
|
||||
onTriggered: root.attemptAutoConnect()
|
||||
}
|
||||
|
||||
Instantiator {
|
||||
id: deviceWatcher
|
||||
active: root.autoConnectEnabled && root.adapter !== null
|
||||
model: root.adapter ? root.adapter.devices : null
|
||||
delegate: Connections {
|
||||
required property var modelData
|
||||
target: modelData
|
||||
function onPairedChanged() {
|
||||
if (modelData.paired && !modelData.trusted) {
|
||||
Logger.i("Bluetooth", "Auto-trusting newly paired device:", modelData.name || modelData.deviceName);
|
||||
modelData.trusted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
Logger.i("Bluetooth", "Service started");
|
||||
}
|
||||
@@ -86,6 +120,10 @@ Singleton {
|
||||
Quickshell.execDetached(["rfkill", "block", "wifi"]);
|
||||
Quickshell.execDetached(["rfkill", "block", "bluetooth"]);
|
||||
}
|
||||
// Auto-connect on startup if BT is already enabled
|
||||
if (root.autoConnectEnabled && adapter && adapter.enabled) {
|
||||
autoConnectTimer.restart();
|
||||
}
|
||||
}
|
||||
|
||||
// Handle system wakeup to force-poll and ensure state is up-to-date
|
||||
@@ -106,6 +144,11 @@ Singleton {
|
||||
}
|
||||
checkAirplaneMode.running = true;
|
||||
}
|
||||
function onEnabledChanged() {
|
||||
if (adapter && adapter.enabled && root.autoConnectEnabled) {
|
||||
autoConnectTimer.restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onAdapterChanged: {
|
||||
@@ -597,6 +640,33 @@ Singleton {
|
||||
forgetDevice(device);
|
||||
}
|
||||
|
||||
function attemptAutoConnect() {
|
||||
if (_autoConnectInProgress) return;
|
||||
if (airplaneModeEnabled) return;
|
||||
if (!adapter || !adapter.enabled) return;
|
||||
if (!autoConnectEnabled) return;
|
||||
|
||||
_autoConnectInProgress = true;
|
||||
|
||||
var devList = adapter.devices.values.filter(function(dev) {
|
||||
return dev && dev.paired && !dev.connected && !dev.blocked;
|
||||
});
|
||||
|
||||
var count = devList.length;
|
||||
for (var i = 0; i < devList.length; i++) {
|
||||
Logger.i("Bluetooth", "Auto-connecting to:", devList[i].name || devList[i].deviceName);
|
||||
connectDeviceWithTrust(devList[i]);
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
ToastService.showNotice(I18n.tr("common.bluetooth"), I18n.tr("toast.bluetooth.auto-connecting", {
|
||||
count: count
|
||||
}), "bluetooth");
|
||||
}
|
||||
|
||||
_autoConnectInProgress = false;
|
||||
}
|
||||
|
||||
function connectDeviceWithTrust(device) {
|
||||
if (!device) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user