mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
Replace inline Bluetooth pairing script with external reusable bash script for improved maintainability and compatibility. Refactor Bluetooth panel/device logic to use adapter-based APIs.
This commit is contained in:
Executable
+59
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# BluetoothConnectionScript.sh
|
||||
# Pairs, trusts, and attempts to connect to a Bluetooth device using bluetoothctl.
|
||||
# Usage: BluetoothConnectionScript.sh <addr> <pairWaitSeconds> <attempts> <intervalSec>
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [[ ${#} -lt 4 ]]; then
|
||||
echo "Usage: $0 <addr> <pairWaitSeconds> <attempts> <intervalSec>" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
addr=$1
|
||||
pair_wait_seconds=$2
|
||||
attempts=$3
|
||||
interval_sec=$4
|
||||
|
||||
if [[ -z "${addr}" || ${#addr} -lt 7 ]]; then
|
||||
echo "Invalid Bluetooth address: '${addr}'" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Launch bluetoothctl session to pair, trust, and try to connect repeatedly.
|
||||
{
|
||||
echo 'agent KeyboardDisplay'
|
||||
echo 'default-agent'
|
||||
echo 'power on'
|
||||
echo "pair ${addr}"
|
||||
# Give time for potential confirmation prompt; send 'yes' optimistically (no-op if not needed)
|
||||
sleep 1
|
||||
echo 'yes'
|
||||
# Mark device trusted
|
||||
echo "trust ${addr}"
|
||||
# Attempt multiple connects within the session
|
||||
for i in $(seq 1 "${attempts}"); do
|
||||
echo "connect ${addr}"
|
||||
sleep "${interval_sec}"
|
||||
done
|
||||
echo 'quit'
|
||||
} | bluetoothctl &
|
||||
|
||||
# Wait up to pair_wait_seconds for pairing to complete
|
||||
for i in $(seq 1 "${pair_wait_seconds}"); do
|
||||
if bluetoothctl info "${addr}" | grep -q 'Paired: yes'; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Check connection state for ~attempts*interval_sec seconds total
|
||||
for i in $(seq 1 "${attempts}"); do
|
||||
if bluetoothctl info "${addr}" | grep -q 'Connected: yes'; then
|
||||
exit 0
|
||||
fi
|
||||
sleep "${interval_sec}"
|
||||
done
|
||||
|
||||
exit 1
|
||||
@@ -1,42 +0,0 @@
|
||||
.pragma library
|
||||
|
||||
var pairAndConnectScript = (addr, pairWaitSeconds, attempts, intervalSec) => {
|
||||
// Produces a shell script that pairs, trusts and attempts to connect repeatedly.
|
||||
return `
|
||||
addr='${addr}'
|
||||
{
|
||||
echo 'agent KeyboardDisplay'
|
||||
echo 'default-agent'
|
||||
echo 'power on'
|
||||
echo "pair $addr"
|
||||
# Give time for potential confirmation prompt; send 'yes' optimistically (no-op if not needed)
|
||||
sleep 1
|
||||
echo 'yes'
|
||||
# Mark device trusted
|
||||
echo "trust $addr"
|
||||
# Attempt multiple connects within the session
|
||||
for i in $(seq 1 ${attempts}); do
|
||||
echo "connect $addr"
|
||||
sleep ${intervalSec}
|
||||
done
|
||||
echo 'quit'
|
||||
} | bluetoothctl &
|
||||
|
||||
# Wait up to ${pairWaitSeconds}s for pairing to complete
|
||||
for i in $(seq 1 ${pairWaitSeconds}); do
|
||||
if bluetoothctl info "$addr" | grep -q 'Paired: yes'; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Check connection state for ~${attempts * intervalSec}s total
|
||||
for i in $(seq 1 ${attempts}); do
|
||||
if bluetoothctl info "$addr" | grep -q 'Connected: yes'; then
|
||||
exit 0
|
||||
fi
|
||||
sleep ${intervalSec}
|
||||
done
|
||||
exit 1
|
||||
`;
|
||||
};
|
||||
@@ -156,9 +156,9 @@ SmartPanel {
|
||||
label: I18n.tr("bluetooth.panel.connected-devices")
|
||||
headerMode: "layout"
|
||||
property var items: {
|
||||
if (!BluetoothService.adapter || !Bluetooth.devices)
|
||||
if (!BluetoothService.adapter || !BluetoothService.adapter.devices)
|
||||
return [];
|
||||
var filtered = Bluetooth.devices.values.filter(dev => dev && !dev.blocked && dev.connected);
|
||||
var filtered = BluetoothService.adapter.devices.values.filter(dev => dev && !dev.blocked && dev.connected);
|
||||
filtered = BluetoothService.dedupeDevices(filtered);
|
||||
return BluetoothService.sortDevices(filtered);
|
||||
}
|
||||
@@ -173,9 +173,9 @@ SmartPanel {
|
||||
tooltipText: I18n.tr("tooltips.connect-disconnect-devices")
|
||||
headerMode: "layout"
|
||||
property var items: {
|
||||
if (!BluetoothService.adapter || !Bluetooth.devices)
|
||||
if (!BluetoothService.adapter || !BluetoothService.adapter.devices)
|
||||
return [];
|
||||
var filtered = Bluetooth.devices.values.filter(dev => dev && !dev.blocked && !dev.connected && (dev.paired || dev.trusted));
|
||||
var filtered = BluetoothService.adapter.devices.values.filter(dev => dev && !dev.blocked && !dev.connected && (dev.paired || dev.trusted));
|
||||
filtered = BluetoothService.dedupeDevices(filtered);
|
||||
return BluetoothService.sortDevices(filtered);
|
||||
}
|
||||
@@ -189,9 +189,9 @@ SmartPanel {
|
||||
label: I18n.tr("bluetooth.panel.available-devices")
|
||||
headerMode: "filter"
|
||||
property var items: {
|
||||
if (!BluetoothService.adapter || !Bluetooth.devices)
|
||||
if (!BluetoothService.adapter || !BluetoothService.adapter.devices)
|
||||
return [];
|
||||
var filtered = Bluetooth.devices.values.filter(dev => dev && !dev.blocked && !dev.paired && !dev.trusted);
|
||||
var filtered = BluetoothService.adapter.devices.values.filter(dev => dev && !dev.blocked && !dev.paired && !dev.trusted);
|
||||
// Optionally hide devices without a meaningful name when the filter is enabled
|
||||
if (Settings.data && Settings.data.ui && Settings.data.ui.bluetoothHideUnnamedDevices) {
|
||||
filtered = filtered.filter(function (dev) {
|
||||
@@ -268,12 +268,12 @@ SmartPanel {
|
||||
// Empty state when no devices
|
||||
NBox {
|
||||
visible: {
|
||||
if (!Bluetooth.devices || BluetoothService.scanningActive)
|
||||
if (!(BluetoothService.adapter && BluetoothService.adapter.devices) || BluetoothService.scanningActive)
|
||||
return false;
|
||||
|
||||
var availableCount = Bluetooth.devices.values.filter(dev => {
|
||||
return dev && !dev.blocked && (dev.signalStrength === undefined || dev.signalStrength > 0);
|
||||
}).length;
|
||||
var availableCount = BluetoothService.adapter.devices.values.filter(dev => {
|
||||
return dev && !dev.blocked && (dev.signalStrength === undefined || dev.signalStrength > 0);
|
||||
}).length;
|
||||
return (availableCount === 0);
|
||||
}
|
||||
Layout.fillWidth: true
|
||||
@@ -321,13 +321,13 @@ SmartPanel {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: columnScanning.implicitHeight + Style.marginM * 2
|
||||
visible: {
|
||||
if (!Bluetooth.devices || !BluetoothService.scanningActive) {
|
||||
if (!(BluetoothService.adapter && BluetoothService.adapter.devices) || !BluetoothService.scanningActive) {
|
||||
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 = BluetoothService.adapter.devices.values.filter(dev => {
|
||||
return dev && !dev.paired && !dev.pairing && !dev.blocked && (dev.signalStrength === undefined || dev.signalStrength > 0);
|
||||
}).length;
|
||||
return (availableCount === 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import qs.Commons
|
||||
import qs.Services.UI
|
||||
import "."
|
||||
import "../../Helpers/BluetoothUtils.js" as BluetoothUtils
|
||||
import "../../Helpers/BluetoothScripts.js" as BluetoothScripts
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
@@ -105,7 +104,21 @@ QtObject {
|
||||
restoreDiscoveryTimer.stop();
|
||||
root._discoveryWasRunning = false;
|
||||
} catch (_) {}
|
||||
|
||||
// Prefer Quickshell API if available, fall back to bluetoothctl
|
||||
try {
|
||||
if (adapter) {
|
||||
if (active && adapter.startDiscovery !== undefined) {
|
||||
adapter.startDiscovery();
|
||||
} else if (!active && adapter.stopDiscovery !== undefined) {
|
||||
adapter.stopDiscovery();
|
||||
}
|
||||
}
|
||||
} catch (e1) {}
|
||||
|
||||
// Always issue bluetoothctl as a compatibility fallback
|
||||
btExec(["bluetoothctl", "scan", active ? "on" : "off"]);
|
||||
|
||||
if (active && durationMs && durationMs > 0) {
|
||||
manualScanTimer.interval = durationMs;
|
||||
manualScanTimer.restart();
|
||||
@@ -119,22 +132,22 @@ QtObject {
|
||||
function toggleDiscovery() {
|
||||
if (!adapter)
|
||||
return;
|
||||
setScanActive(!root.ctlDiscovering, scanAutoStopMs);
|
||||
setScanActive(!root.scanningActive, scanAutoStopMs);
|
||||
}
|
||||
|
||||
// Auto-stop manual discovery after a short window
|
||||
property Timer manualScanTimer: Timer {
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
// Stop scan via bluetoothctl if currently active
|
||||
if (root.ctlDiscovering) {
|
||||
// Stop scan if currently active
|
||||
if (root.scanningActive) {
|
||||
root.setScanActive(false, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exposed scanning flag for UI button state, driven by bluetoothctl state
|
||||
readonly property bool scanningActive: (root.ctlDiscovering === true) || manualScanTimer.running
|
||||
// Exposed scanning flag for UI button state; reflects adapter discovery when available
|
||||
readonly property bool scanningActive: ((adapter && adapter.discovering) ? true : (root.ctlDiscovering === true)) || manualScanTimer.running
|
||||
|
||||
function init() {
|
||||
Logger.i("Bluetooth", "Service started");
|
||||
@@ -414,9 +427,10 @@ QtObject {
|
||||
const totalPauseMs = (pairWait * 1000) + (attempts * intervalSec * 1000) + 2000;
|
||||
_pauseDiscoveryFor(totalPauseMs);
|
||||
|
||||
// Auto-confirm pairing with bluetoothctl ("yes"). Build script via helper.
|
||||
const script = BluetoothScripts.pairAndConnectScript(addr, pairWait, attempts, intervalSec);
|
||||
btExec(["sh", "-c", script]);
|
||||
// Prefer external dev script for pairing/connecting; executed detached
|
||||
const scriptPath = Quickshell.shellDir + "/Bin/dev/BluetoothConnectionScript.sh";
|
||||
// Use bash explicitly to avoid relying on executable bit in all environments
|
||||
btExec(["bash", scriptPath, String(addr), String(pairWait), String(attempts), String(intervalSec)]);
|
||||
}
|
||||
|
||||
// --- Helper to run bluetoothctl and scripts with consistent error logging ---
|
||||
|
||||
Reference in New Issue
Block a user