feat: bar bluetooth devices

This commit is contained in:
2 * r + 2 * t 2025-04-29 18:47:45 +10:00
parent a24e6f5b93
commit 169450aa2e
9 changed files with 127 additions and 11 deletions

View file

@ -35,9 +35,9 @@ Item {
vertical: BarConfig.vertical vertical: BarConfig.vertical
anchors.left: root.get(osIcon.right, undefined) anchors.left: root.get(osIcon.right, undefined)
anchors.leftMargin: root.get(Appearance.padding.normal, 0) anchors.leftMargin: root.get(Appearance.padding.large, 0)
anchors.top: root.get(undefined, osIcon.bottom) anchors.top: root.get(undefined, osIcon.bottom)
anchors.topMargin: root.get(0, Appearance.padding.normal) anchors.topMargin: root.get(0, Appearance.padding.large)
anchors.horizontalCenter: root.get(undefined, parent.horizontalCenter) anchors.horizontalCenter: root.get(undefined, parent.horizontalCenter)
anchors.verticalCenter: root.get(parent.verticalCenter, undefined) anchors.verticalCenter: root.get(parent.verticalCenter, undefined)
@ -70,9 +70,9 @@ Item {
StatusIcons { StatusIcons {
anchors.left: root.get(clock.right, undefined) anchors.left: root.get(clock.right, undefined)
anchors.leftMargin: root.get(Appearance.padding.normal, 0) anchors.leftMargin: root.get(Appearance.padding.large, 0)
anchors.top: root.get(undefined, clock.bottom) anchors.top: root.get(undefined, clock.bottom)
anchors.topMargin: root.get(0, Appearance.padding.normal) anchors.topMargin: root.get(0, Appearance.padding.large)
anchors.horizontalCenter: root.get(undefined, parent.horizontalCenter) anchors.horizontalCenter: root.get(undefined, parent.horizontalCenter)
anchors.verticalCenter: root.get(parent.verticalCenter, undefined) anchors.verticalCenter: root.get(parent.verticalCenter, undefined)

View file

@ -19,7 +19,7 @@ StyledRect {
prevAnchor: icon prevAnchor: icon
horizontalAlignment: StyledText.AlignHCenter horizontalAlignment: StyledText.AlignHCenter
text: root.vertical ? Time.format("hh\nmm") : Time.format("dd/MM/yy hh:mm") text: root.vertical ? Time.format("hh\nmm") : Time.format("hh:mm • dddd, dd MMMM")
font.pointSize: Appearance.font.size.smaller font.pointSize: Appearance.font.size.smaller
font.family: Appearance.font.family.mono font.family: Appearance.font.family.mono
color: root.colour color: root.colour

View file

@ -3,6 +3,7 @@ import "root:/services"
import "root:/utils" import "root:/utils"
import "root:/config" import "root:/config"
import QtQuick import QtQuick
import QtQuick.Controls
StyledRect { StyledRect {
id: root id: root
@ -10,9 +11,44 @@ StyledRect {
readonly property color colour: Appearance.colours.rosewater readonly property color colour: Appearance.colours.rosewater
MaterialIcon { MaterialIcon {
id: icon id: network
animate: true
text: Icons.getNetworkIcon(Network.active.strength) text: Icons.getNetworkIcon(Network.active.strength)
color: root.colour color: root.colour
} }
AnchorText {
id: bluetooth
prevAnchor: network
animate: true
text: Bluetooth.powered ? "bluetooth" : "bluetooth_disabled"
color: root.colour
font.family: Appearance.font.family.material
font.pointSize: Appearance.font.size.larger
}
BoxLayout {
anchors.left: vertical ? undefined : bluetooth.right
anchors.leftMargin: vertical ? 0 : Appearance.padding.smaller
anchors.top: vertical ? bluetooth.bottom : undefined
anchors.topMargin: vertical ? Appearance.padding.smaller : 0
anchors.horizontalCenter: vertical ? bluetooth.horizontalCenter : undefined
anchors.verticalCenter: vertical ? undefined : bluetooth.verticalCenter
Repeater {
model: Bluetooth.connected
MaterialIcon {
required property Bluetooth.Device modelData
animate: true
text: Icons.getBluetoothIcon(modelData.icon)
color: root.colour
}
}
}
} }

70
services/Bluetooth.qml Normal file
View file

@ -0,0 +1,70 @@
pragma Singleton
import Quickshell
import Quickshell.Io
import QtQuick
Singleton {
id: root
property bool powered
property bool discovering
readonly property list<Device> devices: []
readonly property list<Device> connected: devices.filter(d => d.connected)
Process {
running: true
command: ["bluetoothctl"]
stdout: SplitParser {
onRead: getInfo.running = true
}
}
Process {
id: getInfo
running: true
command: ["sh", "-c", "bluetoothctl show | paste -s"]
stdout: SplitParser {
onRead: data => {
root.powered = data.includes("Powered: yes");
root.discovering = data.includes("Discovering: yes");
}
}
}
Process {
id: getDevices
running: true
command: ["fish", "-c", `for a in (bluetoothctl devices | cut -d ' ' -f 2); bluetoothctl info $a | jq -R 'reduce (inputs / ":") as [$key, $value] ({}; .[$key | ltrimstr("\t")] = ($value | ltrimstr(" ")))' | jq -c --arg addr $a '.Address = $addr'; end`]
stdout: SplitParser {
onRead: data => {
const d = JSON.parse(data);
root.devices.push(deviceComp.createObject(root, {
name: d.Name,
alias: d.Alias,
address: d.Address,
icon: d.Icon,
connected: d.Connected === "yes",
paired: d.Paired === "yes",
trusted: d.Trusted === "yes"
}));
}
}
}
component Device: QtObject {
property string name
property string alias
property string address
property string icon
property bool connected
property bool paired
property bool trusted
}
Component {
id: deviceComp
Device {}
}
}

View file

@ -39,7 +39,7 @@ Singleton {
Process { Process {
id: getClients id: getClients
command: ["bash", "-c", "hyprctl -j clients | jq -c"] command: ["sh", "-c", "hyprctl -j clients | jq -c"]
stdout: SplitParser { stdout: SplitParser {
onRead: data => { onRead: data => {
const clients = JSON.parse(data); const clients = JSON.parse(data);
@ -58,7 +58,7 @@ Singleton {
Process { Process {
id: getActiveClient id: getActiveClient
command: ["bash", "-c", "hyprctl -j activewindow | jq -c"] command: ["sh", "-c", "hyprctl -j activewindow | jq -c"]
stdout: SplitParser { stdout: SplitParser {
onRead: data => { onRead: data => {
const client = JSON.parse(data); const client = JSON.parse(data);

View file

@ -177,6 +177,16 @@ Singleton {
return "signal_wifi_0_bar"; return "signal_wifi_0_bar";
} }
function getBluetoothIcon(icon: string): string {
if (icon.includes("headset") || icon.includes("headphones"))
return "headphones";
if (icon.includes("audio"))
return "speaker";
if (icon.includes("phone"))
return "smartphone";
return "bluetooth";
}
FileView { FileView {
path: "/etc/os-release" path: "/etc/os-release"
onLoaded: { onLoaded: {

View file

@ -5,7 +5,7 @@ StyledText {
id: root id: root
required property Item prevAnchor required property Item prevAnchor
property bool vertical property bool vertical: parent.vertical ?? false
anchors.left: vertical ? undefined : prevAnchor.right anchors.left: vertical ? undefined : prevAnchor.right
anchors.leftMargin: vertical ? 0 : Appearance.padding.smaller anchors.leftMargin: vertical ? 0 : Appearance.padding.smaller

View file

@ -2,7 +2,7 @@ import "root:/config"
import QtQuick.Layouts import QtQuick.Layouts
GridLayout { GridLayout {
property bool vertical: false property bool vertical: parent.vertical ?? false // Propagate from parent
property bool homogenous: false property bool homogenous: false
property int spacing: Appearance.spacing.small property int spacing: Appearance.spacing.small

View file

@ -5,7 +5,7 @@ Rectangle {
id: root id: root
property bool animate: false property bool animate: false
property bool vertical: false // Convenience property for propagation to children property bool vertical: parent.vertical ?? false // Convenience property for propagation to children
color: "transparent" color: "transparent"
implicitWidth: childrenRect.width implicitWidth: childrenRect.width