feat: sound alerts for battery and pomodoro

This commit is contained in:
0blivi0nis
2025-10-16 19:03:14 -07:00
parent 89585f8121
commit 37fd19fc9a
5 changed files with 112 additions and 7 deletions
@@ -244,6 +244,7 @@ Singleton {
property JsonObject battery: JsonObject {
property int low: 20
property int critical: 5
property int full: 101
property bool automaticSuspend: true
property int suspend: 3
}
@@ -395,13 +396,18 @@ Singleton {
}
}
property JsonObject sounds: JsonObject {
property bool battery: false
property bool pomodoro: false
property string theme: "freedesktop"
}
property JsonObject time: JsonObject {
// https://doc.qt.io/qt-6/qtime.html#toString
property string format: "hh:mm"
property string shortDateFormat: "dd/MM"
property string dateFormat: "ddd, dd/MM"
property JsonObject pomodoro: JsonObject {
property string alertSound: ""
property int breakTime: 300
property int cyclesBeforeLongBreak: 4
property int focus: 1500
@@ -112,6 +112,20 @@ ContentPage {
}
}
}
ConfigRow {
uniform: true
ConfigSpinBox {
icon: "charger"
text: Translation.tr("Full warning")
value: Config.options.battery.full
from: 0
to: 101
stepSize: 5
onValueChanged: {
Config.options.battery.full = value;
}
}
}
}
ContentSection {
@@ -239,6 +253,30 @@ ContentPage {
}
}
ContentSection {
icon: "notification_sound"
title: Translation.tr("Sounds")
ConfigRow {
uniform: true
ConfigSwitch {
buttonIcon: "battery_android_full"
text: Translation.tr("Battery")
checked: Config.options.sounds.battery
onCheckedChanged: {
Config.options.sounds.battery = checked;
}
}
ConfigSwitch {
buttonIcon: "av_timer"
text: Translation.tr("Pomodoro")
checked: Config.options.sounds.pomodoro
onCheckedChanged: {
Config.options.sounds.pomodoro = checked;
}
}
}
}
ContentSection {
icon: "nest_clock_farsight_analog"
title: Translation.tr("Time")
+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);
}
}
@@ -18,10 +18,12 @@ Singleton {
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
@@ -35,17 +37,28 @@ Singleton {
"-u", "critical",
"-a", "Shell"
])
if (available && Config.options.sounds.battery) {
if (isLowAndNotCharging) {
Audio.playSystemSound("dialog-warning")
}
}
}
onIsCriticalAndNotChargingChanged: {
if (available && isCriticalAndNotCharging) 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 (available && Config.options.sounds.battery) {
if (isCriticalAndNotCharging) {
Audio.playSystemSound("suspend-error")
}
}
}
onIsSuspendingAndNotChargingChanged: {
@@ -53,4 +66,29 @@ Singleton {
Quickshell.execDetached(["bash", "-c", `systemctl suspend || loginctl suspend`]);
}
}
onIsFullAndChargingChanged: {
if (available && isFullAndCharging) Quickshell.execDetached([
"notify-send",
Translation.tr("Battery full"),
Translation.tr("Please unplug the charger"),
"-a", "Shell"
]);
if (available && Config.options.sounds.battery) {
if (isFullAndCharging) {
Audio.playSystemSound("complete")
}
}
}
onIsPluggedInChanged: {
if (available && Config.options.sounds.battery) {
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;