Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions config/Config.qml
Original file line number Diff line number Diff line change
Expand Up @@ -1005,6 +1005,11 @@ Singleton {
adapter: JsonAdapter {
property list<string> disks: ["/"]
property bool updateServiceEnabled: true
property JsonObject batteryNotifications: JsonObject {
property bool enabled: true
property int lowThreshold: 20
property int criticalThreshold: 10
}
property JsonObject idle: JsonObject {
property JsonObject general: JsonObject {
property string lock_cmd: "ambxst lock"
Expand Down
159 changes: 159 additions & 0 deletions modules/services/BatteryAlertService.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
pragma Singleton

import QtQuick
import QtMultimedia
import Quickshell
import Quickshell.Io
import qs.config

Singleton {
id: root

readonly property var settings: Config.system && Config.system.batteryNotifications ? Config.system.batteryNotifications : null
readonly property bool enabled: settings && settings.enabled !== undefined ? settings.enabled : true
readonly property int lowThreshold: settings && settings.lowThreshold !== undefined ? settings.lowThreshold : 20
readonly property int criticalThreshold: settings && settings.criticalThreshold !== undefined ? settings.criticalThreshold : 10

property bool lowNotified: false
property bool criticalNotified: false

function resetNotificationState() {
lowNotified = false;
criticalNotified = false;
}

function sendNotification(summary, body, urgency) {
notificationProcess.running = false;
notificationProcess.command = [
"notify-send",

"-u", urgency,
"-i", "battery-caution",
summary,
body
];
notificationProcess.running = true;
warningSound.play();
}

function checkBatteryState() {
if (!enabled || !Battery.available || SuspendManager.isSuspending) {
return;
}

if (Battery.isPluggedIn || Battery.isCharging) {
resetNotificationState();
return;
}

const low = Math.max(lowThreshold, criticalThreshold);
const critical = Math.min(lowThreshold, criticalThreshold);
const percentage = Math.round(Battery.percentage);
const timeRemaining = Battery.timeToEmpty !== "" ? ` About ${Battery.timeToEmpty} remaining.` : "";

if (percentage > low) {
resetNotificationState();
return;
}

if (percentage <= critical) {
if (!criticalNotified) {
sendNotification(
`Battery critical (${percentage}%)`,
`Plug in your charger now.${timeRemaining}`,
"critical"
);
criticalNotified = true;
}
lowNotified = true;
return;
}

if (!lowNotified) {
sendNotification(
`Battery low (${percentage}%)`,
`Battery is getting low.${timeRemaining}`,
"normal"
);
lowNotified = true;
}
}

Process {
id: notificationProcess
running: false
command: []
}

SoundEffect {
id: warningSound
source: Quickshell.shellDir + "/assets/sound/polite-warning-tone.wav"
volume: 1.0
}

Connections {
target: Battery
function onPercentageChanged() {
root.checkBatteryState();
}
function onIsPluggedInChanged() {
root.checkBatteryState();
}
function onIsChargingChanged() {
root.checkBatteryState();
}
function onAvailableChanged() {
root.checkBatteryState();
}
}

Connections {
target: root.settings
ignoreUnknownSignals: true
function onEnabledChanged() {
if (!root.enabled) {
root.resetNotificationState();
} else {
root.checkBatteryState();
}
}
function onLowThresholdChanged() {
root.resetNotificationState();
root.checkBatteryState();
}
function onCriticalThresholdChanged() {
root.resetNotificationState();
root.checkBatteryState();
}
}

Connections {
target: SuspendManager
function onWakingUp() {
wakeCheckTimer.restart();
}
}

Timer {
id: startupCheckTimer
interval: 5000
running: true
repeat: false
onTriggered: root.checkBatteryState()
}

Timer {
id: wakeCheckTimer
interval: 3000
repeat: false
onTriggered: root.checkBatteryState()
}

Timer {
id: pollTimer
interval: 60000
running: true
repeat: true
onTriggered: root.checkBatteryState()
}
}
1 change: 1 addition & 0 deletions shell.qml
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ ShellRoot {
let _ = CaffeineService.inhibit;
_ = IdleService.lockCmd; // Force init
_ = GlobalShortcuts.appId; // Force init (IPC pipe listener)
_ = BatteryAlertService.enabled;
});
}
}
Expand Down