mirror of
https://github.com/end-4/dots-hyprland.git
synced 2026-06-05 14:59:27 -05:00
✨ feat: sound alerts for battery and pomodoro
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user