133 lines
4.6 KiB
QML
133 lines
4.6 KiB
QML
pragma Singleton
|
|
|
|
// Transition-only device monitoring. Resting state is intentionally silent.
|
|
|
|
import Quickshell
|
|
import Quickshell.Bluetooth
|
|
import Quickshell.Io
|
|
import Quickshell.Services.Pipewire
|
|
import QtQuick
|
|
|
|
Singleton {
|
|
id: root
|
|
|
|
property bool outputInitialized: false
|
|
property bool bluetoothInitialized: false
|
|
property bool kdeInitialized: false
|
|
property string previousOutput: ""
|
|
property string previousBluetooth: ""
|
|
property string previousKde: ""
|
|
|
|
PwObjectTracker { objects: [Pipewire.defaultAudioSink] }
|
|
|
|
readonly property string outputName: {
|
|
const sink = Pipewire.defaultAudioSink;
|
|
return sink?.description || sink?.nickname || sink?.name || "";
|
|
}
|
|
|
|
readonly property string bluetoothNames: {
|
|
const devices = Bluetooth.devices?.values ?? [];
|
|
return devices.filter(device => device.connected)
|
|
.map(device => device.name || device.address)
|
|
.sort()
|
|
.join("\u001f");
|
|
}
|
|
|
|
onOutputNameChanged: {
|
|
if (!root.outputInitialized) {
|
|
root.previousOutput = root.outputName;
|
|
return;
|
|
}
|
|
if (root.outputInitialized && root.outputName && root.outputName !== root.previousOutput) {
|
|
StatusEvents.publish({
|
|
key: "device-output",
|
|
glyph: "\u{F07E7}",
|
|
title: root.outputName,
|
|
detail: "Audio output selected",
|
|
priority: StatusEvents.ambientPriority
|
|
});
|
|
}
|
|
root.previousOutput = root.outputName;
|
|
}
|
|
|
|
onBluetoothNamesChanged: {
|
|
if (!root.bluetoothInitialized) {
|
|
root.previousBluetooth = root.bluetoothNames;
|
|
return;
|
|
}
|
|
if (root.bluetoothInitialized && root.bluetoothNames !== root.previousBluetooth) {
|
|
const current = root.bluetoothNames ? root.bluetoothNames.split("\u001f") : [];
|
|
const previous = root.previousBluetooth ? root.previousBluetooth.split("\u001f") : [];
|
|
const connected = current.find(name => !previous.includes(name));
|
|
const disconnected = previous.find(name => !current.includes(name));
|
|
StatusEvents.publish({
|
|
key: "device-bluetooth",
|
|
glyph: "\u{F00B1}",
|
|
title: connected || disconnected || "Bluetooth device",
|
|
detail: connected ? "Connected" : "Disconnected",
|
|
tone: connected ? "accent" : "warn",
|
|
priority: StatusEvents.ambientPriority
|
|
});
|
|
}
|
|
root.previousBluetooth = root.bluetoothNames;
|
|
}
|
|
|
|
Timer {
|
|
id: discoverySettle
|
|
interval: 1800
|
|
onTriggered: {
|
|
root.previousOutput = root.outputName;
|
|
root.previousBluetooth = root.bluetoothNames;
|
|
root.outputInitialized = true;
|
|
root.bluetoothInitialized = true;
|
|
}
|
|
}
|
|
|
|
Timer {
|
|
interval: 15000
|
|
repeat: true
|
|
running: true
|
|
triggeredOnStart: true
|
|
onTriggered: {
|
|
if (!kdeProbe.running)
|
|
kdeProbe.running = true;
|
|
}
|
|
}
|
|
|
|
Process {
|
|
id: kdeProbe
|
|
command: ["kdeconnect-cli", "-a", "--name-only"]
|
|
stdout: StdioCollector {
|
|
onStreamFinished: {
|
|
const names = this.text.split("\n")
|
|
.map(line => line.trim())
|
|
.filter(line => line && !/^\d+ devices? found$/.test(line))
|
|
.sort()
|
|
.join("\u001f");
|
|
if (root.kdeInitialized && names !== root.previousKde) {
|
|
const current = names ? names.split("\u001f") : [];
|
|
const previous = root.previousKde ? root.previousKde.split("\u001f") : [];
|
|
const connected = current.find(name => !previous.includes(name));
|
|
const disconnected = previous.find(name => !current.includes(name));
|
|
StatusEvents.publish({
|
|
key: "device-kdeconnect",
|
|
glyph: "\u{F03F2}",
|
|
title: connected || disconnected || "Phone",
|
|
detail: connected ? "KDE Connect available" : "KDE Connect disconnected",
|
|
tone: connected ? "accent" : "warn",
|
|
priority: StatusEvents.ambientPriority
|
|
});
|
|
}
|
|
root.previousKde = names;
|
|
root.kdeInitialized = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
root.previousOutput = root.outputName;
|
|
root.previousBluetooth = root.bluetoothNames;
|
|
discoverySettle.start();
|
|
}
|
|
}
|