Files
noctalia-shell/Services/BatteryService.qml
T
2025-10-16 15:07:11 +02:00

248 lines
8.1 KiB
QML

pragma Singleton
import Quickshell
import Quickshell.Io
import Quickshell.Services.UPower
import qs.Commons
import qs.Services
Singleton {
id: root
enum ChargingMode {
Disabled = 0,
Full,
Balanced,
Lifespan
}
property int chargingMode: Settings.data.battery.chargingMode
readonly property string batterySetterScript: Quickshell.shellDir + '/Bin/battery-manager/set-battery-treshold.sh'
readonly property string batteryInstallerScript: Quickshell.shellDir + '/Bin/battery-manager/install-battery-manager.sh'
readonly property string batteryUninstallerScript: Quickshell.shellDir + '/Bin/battery-manager/uninstall-battery-manager.sh'
// This is used to omit toast message and writing mode to settings on startup
property bool initialSetter: true
// Choose icon based on charge and charging state
function getIcon(percent, charging, isReady) {
if (!isReady) {
return "battery-exclamation"
}
if (charging) {
return "battery-charging"
} else {
if (percent >= 90)
return "battery-4"
if (percent >= 50)
return "battery-3"
if (percent >= 25)
return "battery-2"
if (percent >= 0)
return "battery-1"
return "battery"
}
}
function getThresholdValue(chargingMode) {
switch (chargingMode) {
case BatteryService.ChargingMode.Full:
return "100"
case BatteryService.ChargingMode.Balanced:
return "80"
case BatteryService.ChargingMode.Lifespan:
return "60"
}
}
function toggleEnabled(enabled) {
if (enabled) {
setChargingMode(BatteryService.ChargingMode.Full)
} else {
BatteryService.initialSetter = true
ToastService.showNotice(I18n.tr("toast.battery-manager.title"), I18n.tr("toast.battery-manager.uninstall-setup"))
PanelService.getPanel("batteryPanel")?.toggle(this)
uninstallerProcess.running = true
}
}
function setChargingMode(newMode) {
if (newMode !== BatteryService.ChargingMode.Full && newMode !== BatteryService.ChargingMode.Balanced && newMode !== BatteryService.ChargingMode.Lifespan) {
Logger.w("BatteryService", `Invalid charging mode set ${newMode}`)
return
}
BatteryService.chargingMode = newMode
BatteryService.applyChargingMode()
}
function cycleModes() {
// Cycles charging modes from full to lifespan while skipping disabled
const nextMode = (chargingMode % 3) + 1
setChargingMode(nextMode)
}
function applyChargingMode() {
let command = [batterySetterScript]
// Currently the script sends notifications by default but quickshell
// uses toast messages so the flag is passed to supress notifs
command.push("-q")
command.push(BatteryService.getThresholdValue(BatteryService.chargingMode))
setterProcess.command = command
setterProcess.running = true
}
function runInstaller() {
installerProcess.command = ["pkexec", batteryInstallerScript]
installerProcess.running = true
}
function init() {
if (BatteryService.chargingMode !== BatteryService.ChargingMode.Disabled && BatteryService.chargingMode !== BatteryService.ChargingMode.Full) {
BatteryService.applyChargingMode()
}
}
Process {
id: setterProcess
workingDirectory: Quickshell.shellDir
running: false
onExited: (exitCode, exitStatus) => {
if (exitCode === 0) {
Logger.i("BatteryService", "Battery threshold set successfully")
if (BatteryService.initialSetter) {
BatteryService.initialSetter = false
return
}
ToastService.showNotice(I18n.tr("toast.battery-manager.title"), I18n.tr("toast.battery-manager.set-success-desc", {
"percent": BatteryService.getThresholdValue(BatteryService.chargingMode)
}))
Settings.data.battery.chargingMode = BatteryService.chargingMode
} else if (exitCode === 2) {
ToastService.showWarning(I18n.tr("toast.battery-manager.title"), I18n.tr("toast.battery-manager.initial-setup"))
PanelService.getPanel("batteryPanel")?.toggle(this)
BatteryService.runInstaller()
} else {
ToastService.showError(I18n.tr("toast.battery-manager.title"), I18n.tr("toast.battery-manager.set-failed"))
Logger.e("BatteryService", `Setter process failed with exit code: ${exitCode}`)
}
}
stderr: StdioCollector {
onStreamFinished: {
if (this.text) {
Logger.w("BatteryService", "SetterProcess stderr:", this.text)
}
}
}
stdout: StdioCollector {
onStreamFinished: {
if (this.text) {
Logger.i("BatteryService", "SetterProcess stdout:", this.text)
}
}
}
}
// Installer process - installs battery manager components
Process {
id: installerProcess
workingDirectory: Quickshell.shellDir
running: false
onExited: (exitCode, exitStatus) => {
if (exitCode === 0) {
ToastService.showNotice(I18n.tr("toast.battery-manager.title"), I18n.tr("toast.battery-manager.install-success"))
BatteryService.applyChargingMode()
} else if (exitCode === 2) {
ToastService.showError(I18n.tr("toast.battery-manager.title"), I18n.tr("toast.battery-manager.install-missing"))
} else if (exitCode === 3) {
ToastService.showError(I18n.tr("toast.battery-manager.title"), I18n.tr("toast.battery-manager.install-unsupported"))
} else {
ToastService.showError(I18n.tr("toast.battery-manager.title"), I18n.tr("toast.battery-manager.install-failed"))
}
if (exitCode !== 0) {
BatteryService.chargingMode = BatteryService.ChargingMode.Disabled
}
}
stderr: StdioCollector {
onStreamFinished: {
if (this.text) {
Logger.w("BatteryService", "InstallerProcess stderr:", this.text)
}
}
}
stdout: StdioCollector {
onStreamFinished: {
if (this.text) {
Logger.i("BatteryService", "InstallerProcess stdout:", this.text)
}
}
}
}
Process {
id: uninstallerProcess
workingDirectory: Quickshell.shellDir
command: ["pkexec", batteryUninstallerScript]
running: false
onExited: (exitCode, exitStatus) => {
if (exitCode === 0) {
Logger.i("BatteryService", "Battery Manager uninstalled successfully")
ToastService.showNotice(I18n.tr("toast.battery-manager.title"), I18n.tr("toast.battery-manager.uninstall-success"))
Settings.data.battery.chargingMode = BatteryService.chargingMode
BatteryService.chargingMode = BatteryService.ChargingMode.Disabled
cleanupProcess.running = true
} else {
ToastService.showError(I18n.tr("toast.battery-manager.title"), I18n.tr("toast.battery-manager.uninstall-failed"))
Logger.e("BatteryService", `Uninstaller process failed with exit code: ${exitCode}`)
}
}
stderr: StdioCollector {
onStreamFinished: {
if (this.text) {
Logger.w("BatteryService", "UninstallerProcess stderr:", this.text)
}
}
}
stdout: StdioCollector {
onStreamFinished: {
if (this.text) {
Logger.i("BatteryService", "UninstallerProcess stdout:", this.text)
}
}
}
}
// Cleanup process - deletes uninstaller after it sucessfull ;
Process {
id: cleanupProcess
workingDirectory: Quickshell.shellDir
command: ["rm", "-rf", batteryUninstallerScript]
running: false
onExited: (exitCode, exitStatus) => {
if (exitCode === 0) {
Logger.i("BatteryService", "Battery Manager uninstalled successfully")
} else {
Logger.e("BatteryService", `Cleanup process failed with exit code: ${exitCode}`)
}
}
stderr: StdioCollector {
onStreamFinished: {
if (this.text) {
Logger.w("BatteryService", "CleanupProcess stderr:", this.text)
}
}
}
stdout: StdioCollector {
onStreamFinished: {
if (this.text) {
Logger.i("BatteryService", "CleanupProcess stdout:", this.text)
}
}
}
}
}