mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-06 05:25:41 -05:00
262 lines
10 KiB
QML
262 lines
10 KiB
QML
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Io
|
|
pragma Singleton
|
|
pragma ComponentBehavior: Bound
|
|
|
|
Singleton {
|
|
id: root
|
|
|
|
// Battery properties
|
|
property bool batteryAvailable: false
|
|
property int batteryLevel: 0
|
|
property string batteryStatus: "Unknown"
|
|
property int timeRemaining: 0
|
|
property bool isCharging: false
|
|
property bool isLowBattery: false
|
|
property int batteryHealth: 100
|
|
property string batteryTechnology: "Unknown"
|
|
property int cycleCount: 0
|
|
property int batteryCapacity: 0
|
|
property var powerProfiles: []
|
|
property string activePowerProfile: ""
|
|
|
|
// Check if battery is available
|
|
Process {
|
|
id: batteryAvailabilityChecker
|
|
command: ["bash", "-c", "ls /sys/class/power_supply/ | grep -E '^BAT' | head -1"]
|
|
running: true
|
|
|
|
stdout: SplitParser {
|
|
splitMarker: "\n"
|
|
onRead: (data) => {
|
|
if (data.trim()) {
|
|
root.batteryAvailable = true
|
|
console.log("Battery found:", data.trim())
|
|
batteryStatusChecker.running = true
|
|
} else {
|
|
root.batteryAvailable = false
|
|
console.log("No battery found - this appears to be a desktop system")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Battery status checker
|
|
Process {
|
|
id: batteryStatusChecker
|
|
command: ["bash", "-c", "if [ -d /sys/class/power_supply/BAT0 ] || [ -d /sys/class/power_supply/BAT1 ]; then upower -i $(upower -e | grep 'BAT') | grep -E 'state|percentage|time to|energy|technology|cycle-count' || acpi -b 2>/dev/null || echo 'fallback'; else echo 'no-battery'; fi"]
|
|
running: false
|
|
|
|
stdout: StdioCollector {
|
|
onStreamFinished: {
|
|
if (text.trim() === "no-battery") {
|
|
root.batteryAvailable = false
|
|
return
|
|
}
|
|
|
|
if (text.trim() && text.trim() !== "fallback") {
|
|
parseBatteryInfo(text.trim())
|
|
} else {
|
|
// Fallback to simple methods
|
|
fallbackBatteryChecker.running = true
|
|
}
|
|
}
|
|
}
|
|
|
|
onExited: (exitCode) => {
|
|
if (exitCode !== 0) {
|
|
console.warn("Battery status check failed, trying fallback methods")
|
|
fallbackBatteryChecker.running = true
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fallback battery checker using /sys files
|
|
Process {
|
|
id: fallbackBatteryChecker
|
|
command: ["bash", "-c", "if [ -f /sys/class/power_supply/BAT0/capacity ]; then BAT=BAT0; elif [ -f /sys/class/power_supply/BAT1/capacity ]; then BAT=BAT1; else echo 'no-battery'; exit 1; fi; echo \"percentage: $(cat /sys/class/power_supply/$BAT/capacity)%\"; echo \"state: $(cat /sys/class/power_supply/$BAT/status 2>/dev/null || echo Unknown)\"; if [ -f /sys/class/power_supply/$BAT/technology ]; then echo \"technology: $(cat /sys/class/power_supply/$BAT/technology)\"; fi; if [ -f /sys/class/power_supply/$BAT/cycle_count ]; then echo \"cycle-count: $(cat /sys/class/power_supply/$BAT/cycle_count)\"; fi"]
|
|
running: false
|
|
|
|
stdout: StdioCollector {
|
|
onStreamFinished: {
|
|
if (text.trim() !== "no-battery") {
|
|
parseBatteryInfo(text.trim())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Power profiles checker (for systems with power-profiles-daemon)
|
|
Process {
|
|
id: powerProfilesChecker
|
|
command: ["bash", "-c", "if command -v powerprofilesctl > /dev/null; then powerprofilesctl list 2>/dev/null; else echo 'not-available'; fi"]
|
|
running: false
|
|
|
|
stdout: StdioCollector {
|
|
onStreamFinished: {
|
|
if (text.trim() !== "not-available") {
|
|
parsePowerProfiles(text.trim())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function parseBatteryInfo(batteryText) {
|
|
let lines = batteryText.split('\n')
|
|
|
|
for (let line of lines) {
|
|
line = line.trim().toLowerCase()
|
|
|
|
if (line.includes('percentage:') || line.includes('capacity:')) {
|
|
let match = line.match(/(\d+)%?/)
|
|
if (match) {
|
|
root.batteryLevel = parseInt(match[1])
|
|
root.isLowBattery = root.batteryLevel <= 20
|
|
}
|
|
} else if (line.includes('state:') || line.includes('status:')) {
|
|
let statusPart = line.split(':')[1]?.trim().toLowerCase() || line
|
|
console.log("Raw battery status line:", line, "extracted status:", statusPart)
|
|
|
|
if (statusPart === 'charging') {
|
|
root.batteryStatus = "Charging"
|
|
root.isCharging = true
|
|
console.log("Battery is charging")
|
|
} else if (statusPart === 'discharging') {
|
|
root.batteryStatus = "Discharging"
|
|
root.isCharging = false
|
|
console.log("Battery is discharging")
|
|
} else if (statusPart === 'full') {
|
|
root.batteryStatus = "Full"
|
|
root.isCharging = false
|
|
console.log("Battery is full")
|
|
} else if (statusPart === 'not charging') {
|
|
root.batteryStatus = "Not charging"
|
|
root.isCharging = false
|
|
console.log("Battery is not charging")
|
|
} else {
|
|
root.batteryStatus = statusPart.charAt(0).toUpperCase() + statusPart.slice(1) || "Unknown"
|
|
root.isCharging = false
|
|
console.log("Battery status unknown:", statusPart)
|
|
}
|
|
} else if (line.includes('time to')) {
|
|
let match = line.match(/(\d+):(\d+)/)
|
|
if (match) {
|
|
root.timeRemaining = parseInt(match[1]) * 60 + parseInt(match[2])
|
|
}
|
|
} else if (line.includes('technology:')) {
|
|
let tech = line.split(':')[1]?.trim() || "Unknown"
|
|
root.batteryTechnology = tech.charAt(0).toUpperCase() + tech.slice(1)
|
|
} else if (line.includes('cycle-count:')) {
|
|
let match = line.match(/(\d+)/)
|
|
if (match) {
|
|
root.cycleCount = parseInt(match[1])
|
|
}
|
|
} else if (line.includes('energy-full:') || line.includes('capacity:')) {
|
|
let match = line.match(/([\d.]+)\s*wh/i)
|
|
if (match) {
|
|
root.batteryCapacity = Math.round(parseFloat(match[1]) * 1000) // Convert to mWh
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log("Battery status updated:", root.batteryLevel + "%", root.batteryStatus)
|
|
}
|
|
|
|
function parsePowerProfiles(profileText) {
|
|
let lines = profileText.split('\n')
|
|
let profiles = []
|
|
|
|
for (let line of lines) {
|
|
line = line.trim()
|
|
if (line.includes('*')) {
|
|
// Active profile
|
|
let profileName = line.replace('*', '').trim()
|
|
if (profileName.includes(':')) {
|
|
profileName = profileName.split(':')[0].trim()
|
|
}
|
|
root.activePowerProfile = profileName
|
|
profiles.push(profileName)
|
|
} else if (line && !line.includes(':') && line.length > 0) {
|
|
profiles.push(line)
|
|
}
|
|
}
|
|
|
|
root.powerProfiles = profiles
|
|
console.log("Power profiles available:", profiles, "Active:", root.activePowerProfile)
|
|
}
|
|
|
|
function setBatteryProfile(profileName) {
|
|
if (!root.powerProfiles.includes(profileName)) {
|
|
console.warn("Invalid power profile:", profileName)
|
|
return
|
|
}
|
|
|
|
console.log("Setting power profile to:", profileName)
|
|
let profileProcess = Qt.createQmlObject(`
|
|
import Quickshell.Io
|
|
Process {
|
|
command: ["powerprofilesctl", "set", "${profileName}"]
|
|
running: true
|
|
onExited: (exitCode) => {
|
|
if (exitCode === 0) {
|
|
console.log("Power profile changed to:", "${profileName}")
|
|
root.activePowerProfile = "${profileName}"
|
|
} else {
|
|
console.warn("Failed to change power profile")
|
|
}
|
|
}
|
|
}
|
|
`, root)
|
|
}
|
|
|
|
function getBatteryIcon() {
|
|
if (!root.batteryAvailable) return "power"
|
|
|
|
let level = root.batteryLevel
|
|
let charging = root.isCharging
|
|
|
|
if (charging) {
|
|
if (level >= 90) return "battery_charging_full"
|
|
if (level >= 60) return "battery_charging_90"
|
|
if (level >= 30) return "battery_charging_60"
|
|
if (level >= 20) return "battery_charging_30"
|
|
return "battery_charging_20"
|
|
} else {
|
|
if (level >= 90) return "battery_full"
|
|
if (level >= 60) return "battery_6_bar"
|
|
if (level >= 50) return "battery_5_bar"
|
|
if (level >= 40) return "battery_4_bar"
|
|
if (level >= 30) return "battery_3_bar"
|
|
if (level >= 20) return "battery_2_bar"
|
|
if (level >= 10) return "battery_1_bar"
|
|
return "battery_alert"
|
|
}
|
|
}
|
|
|
|
function formatTimeRemaining() {
|
|
if (root.timeRemaining <= 0) return "Unknown"
|
|
|
|
let hours = Math.floor(root.timeRemaining / 60)
|
|
let minutes = root.timeRemaining % 60
|
|
|
|
if (hours > 0) {
|
|
return hours + "h " + minutes + "m"
|
|
} else {
|
|
return minutes + "m"
|
|
}
|
|
}
|
|
|
|
|
|
// Update timer
|
|
Timer {
|
|
interval: 30000
|
|
running: root.batteryAvailable
|
|
repeat: true
|
|
triggeredOnStart: true
|
|
onTriggered: {
|
|
batteryStatusChecker.running = true
|
|
powerProfilesChecker.running = true
|
|
}
|
|
}
|
|
} |