feat: sound alerts for battery and pomodoro (#2223)

This commit is contained in:
end-4
2025-10-19 21:37:25 +02:00
committed by GitHub
5 changed files with 107 additions and 10 deletions
+23 -1
View File
@@ -15,6 +15,7 @@ Singleton {
property PwNode sink: Pipewire.defaultAudioSink
property PwNode source: Pipewire.defaultAudioSource
readonly property real hardMaxValue: 2.00 // People keep joking about setting volume to 5172% so...
property string audioTheme: Config.options.sounds.theme
signal sinkProtectionTriggered(string reason);
@@ -49,7 +50,28 @@ Singleton {
}
lastVolume = sink.audio.volume;
}
}
function playSystemSound(soundName) {
const ogaPath = `/usr/share/sounds/${root.audioTheme}/stereo/${soundName}.oga`;
const oggPath = `/usr/share/sounds/${root.audioTheme}/stereo/${soundName}.ogg`;
// Try playing .oga first
let command = [
"ffplay",
"-nodisp",
"-autoexit",
ogaPath
];
Quickshell.execDetached(command);
// Also try playing .ogg (ffplay will just fail silently if file doesn't exist)
command = [
"ffplay",
"-nodisp",
"-autoexit",
oggPath
];
Quickshell.execDetached(command);
}
}
@@ -8,49 +8,79 @@ import QtQuick
import Quickshell.Io
Singleton {
id: root
property bool available: UPower.displayDevice.isLaptopBattery
property var chargeState: UPower.displayDevice.state
property bool isCharging: chargeState == UPowerDeviceState.Charging
property bool isPluggedIn: isCharging || chargeState == UPowerDeviceState.PendingCharge
property real percentage: UPower.displayDevice?.percentage ?? 1
readonly property bool allowAutomaticSuspend: Config.options.battery.automaticSuspend
readonly property bool soundEnabled: Config.options.sounds.battery
property bool isLow: available && (percentage <= Config.options.battery.low / 100)
property bool isCritical: available && (percentage <= Config.options.battery.critical / 100)
property bool isSuspending: available && (percentage <= Config.options.battery.suspend / 100)
property bool isFull: available && (percentage >= Config.options.battery.full / 100)
property bool isLowAndNotCharging: isLow && !isCharging
property bool isCriticalAndNotCharging: isCritical && !isCharging
property bool isSuspendingAndNotCharging: allowAutomaticSuspend && isSuspending && !isCharging
property bool isFullAndCharging: isFull && isCharging
property real energyRate: UPower.displayDevice.changeRate
property real timeToEmpty: UPower.displayDevice.timeToEmpty
property real timeToFull: UPower.displayDevice.timeToFull
onIsLowAndNotChargingChanged: {
if (available && isLowAndNotCharging) Quickshell.execDetached([
if (!root.available || !isLowAndNotCharging) return;
Quickshell.execDetached([
"notify-send",
Translation.tr("Low battery"),
Translation.tr("Consider plugging in your device"),
"-u", "critical",
"-a", "Shell"
])
if (root.soundEnabled) Audio.playSystemSound("dialog-warning");
}
onIsCriticalAndNotChargingChanged: {
if (available && isCriticalAndNotCharging) Quickshell.execDetached([
if (!root.available || !isCriticalAndNotCharging) return;
Quickshell.execDetached([
"notify-send",
Translation.tr("Critically low battery"),
Translation.tr("Please charge!\nAutomatic suspend triggers at %1").arg(Config.options.battery.suspend),
Translation.tr("Please charge!\nAutomatic suspend triggers at %1%").arg(Config.options.battery.suspend),
"-u", "critical",
"-a", "Shell"
]);
if (root.soundEnabled) Audio.playSystemSound("suspend-error");
}
onIsSuspendingAndNotChargingChanged: {
if (available && isSuspendingAndNotCharging) {
if (root.available && isSuspendingAndNotCharging) {
Quickshell.execDetached(["bash", "-c", `systemctl suspend || loginctl suspend`]);
}
}
onIsFullAndChargingChanged: {
if (!root.available || !isFullAndCharging) return;
Quickshell.execDetached([
"notify-send",
Translation.tr("Battery full"),
Translation.tr("Please unplug the charger"),
"-a", "Shell"
]);
if (root.soundEnabled) Audio.playSystemSound("complete");
}
onIsPluggedInChanged: {
if (!root.available || !root.soundEnabled) return;
if (isPluggedIn) {
Audio.playSystemSound("power-plug")
} else {
Audio.playSystemSound("power-unplug")
}
}
}
@@ -1,6 +1,7 @@
pragma Singleton
pragma ComponentBehavior: Bound
import qs.services
import qs.modules.common
import Quickshell
@@ -17,7 +18,6 @@ Singleton {
property int breakTime: Config.options.time.pomodoro.breakTime
property int longBreakTime: Config.options.time.pomodoro.longBreak
property int cyclesBeforeLongBreak: Config.options.time.pomodoro.cyclesBeforeLongBreak
property string alertSound: Config.options.time.pomodoro.alertSound
property bool pomodoroRunning: Persistent.states.timer.pomodoro.running
property bool pomodoroBreak: Persistent.states.timer.pomodoro.isBreak
@@ -64,8 +64,9 @@ Singleton {
}
Quickshell.execDetached(["notify-send", "Pomodoro", notificationMessage, "-a", "Shell"]);
if (alertSound)
Quickshell.execDetached(["ffplay", "-nodisp", "-autoexit", alertSound]);
if (Config.options.sounds.pomodoro) {
Audio.playSystemSound("alarm-clock-elapsed")
}
if (!pomodoroBreak) {
Persistent.states.timer.pomodoro.cycle = (Persistent.states.timer.pomodoro.cycle + 1) % root.cyclesBeforeLongBreak;