// modules/BluetoothWidget.qml - ᛒ status / device alias // Uses bluetoothctl via a polled Process (no BlueZ QML bindings in QS yet). import QtQuick import Quickshell.Io import ".." Pill { id: root property string btStatus: "off" // "off" | "on" | "connected" property string devAlias: "" readonly property var bgColors: ({ "off": Qt.rgba(0.565, 0.545, 0.671, 0.3), // alpha(@color3, 0.3) "on": Theme.color2, "connected": Theme.color4, }) // Override Pill's own color binding color: bgColors[btStatus] ?? Theme.pill onClicked: (m) => { if (m.button === Qt.LeftButton) Exec.run(["blueman-manager"]) } Text { text: { var s = root.btStatus if (s === "connected") return "ᛒ " + (root.devAlias || "connected") if (s === "on") return "ᛒ on" return "ᛒ off" } font { family: Theme.fontSans; pixelSize: Theme.fontSize } color: Theme.foreground } // Poll bluetoothctl show + info every 5 s Process { id: btProc running: false command: ["bash", "-c", "bluetoothctl show | grep -E 'Powered|Name'; " + "bluetoothctl info 2>/dev/null | grep -E 'Name|Connected'"] stdout: SplitParser { onRead: (line) => { if (/Powered:\s+no/i.test(line)) { root.btStatus = "off"; root.devAlias = "" } if (/Powered:\s+yes/i.test(line)) { if (root.btStatus === "off") root.btStatus = "on" } if (/Connected:\s+yes/i.test(line)) root.btStatus = "connected" if (/Connected:\s+no/i.test(line)) { if (root.btStatus === "connected") { root.btStatus = "on"; root.devAlias = "" } } var match = /^\s+Name:\s+(.+)/.exec(line) if (match && root.btStatus === "connected") root.devAlias = match[1].trim() } } onExited: btProc.running = false } Timer { interval: 5000; running: true; repeat: true triggeredOnStart: true onTriggered: btProc.running = true } }