Features: - CPU governor control (performance/powersave) - Fan level control (L1-L7) with thinkfan integration - Battery charge threshold management (75-80%, 80-90%, 0-100%) - Quick charge to 100% option - Automated battery management (time-based and network-based) - System tray application with auto-start - Privilege elevation via pkexec helper script
152 lines
6.1 KiB
Python
Executable File
152 lines
6.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import tkinter as tk
|
|
from tkinter import Menu
|
|
import subprocess
|
|
import threading
|
|
import sys
|
|
|
|
# Try to import pystray for system tray
|
|
try:
|
|
from PIL import Image, ImageDraw
|
|
import pystray
|
|
HAS_TRAY = True
|
|
except ImportError:
|
|
print("Installing required packages for system tray...")
|
|
subprocess.run([sys.executable, "-m", "pip", "install", "pystray", "pillow"], check=True)
|
|
from PIL import Image, ImageDraw
|
|
import pystray
|
|
HAS_TRAY = True
|
|
|
|
class SystemTrayApp:
|
|
def __init__(self):
|
|
self.icon = None
|
|
self.current_status = {}
|
|
self.update_status()
|
|
|
|
def run_command(self, cmd):
|
|
try:
|
|
result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=5)
|
|
return result.stdout.strip()
|
|
except Exception as e:
|
|
return ""
|
|
|
|
def update_status(self):
|
|
"""Get current system status"""
|
|
self.current_status = {
|
|
'temp': self.run_command("cat /sys/class/hwmon/hwmon5/temp1_input | awk '{print int($1/1000)}'"),
|
|
'governor': self.run_command("cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"),
|
|
'battery': self.run_command("cat /sys/class/power_supply/BAT0/capacity"),
|
|
'charging': self.run_command("cat /sys/class/power_supply/BAT0/status"),
|
|
'fan': self.run_command("grep '^level' /proc/acpi/ibm/fan | awk '{print $NF}'")
|
|
}
|
|
|
|
def create_image(self):
|
|
"""Create system tray icon"""
|
|
# Create a simple icon
|
|
image = Image.new('RGB', (64, 64), color='black')
|
|
draw = ImageDraw.Draw(image)
|
|
draw.ellipse([16, 16, 48, 48], fill='#0066cc', outline='white')
|
|
return image
|
|
|
|
def open_full_gui(self, icon=None, item=None):
|
|
"""Open the main GUI"""
|
|
subprocess.Popen(['python3', '/home/rwiegand/Nextcloud/entwicklung/Werkzeuge/battery_management/system-settings-gui.py'])
|
|
|
|
def set_governor(self, gov):
|
|
subprocess.run(f"pkexec /usr/local/bin/system-settings-helper.sh cpu-governor {gov}", shell=True)
|
|
self.update_status()
|
|
if self.icon:
|
|
self.icon.title = self.get_tooltip()
|
|
|
|
def set_battery(self, start, end):
|
|
subprocess.run(f"pkexec /usr/local/bin/system-settings-helper.sh battery-start {start}", shell=True)
|
|
subprocess.run(f"pkexec /usr/local/bin/system-settings-helper.sh battery-end {end}", shell=True)
|
|
self.update_status()
|
|
if self.icon:
|
|
self.icon.title = self.get_tooltip()
|
|
|
|
def charge_now(self):
|
|
subprocess.run("pkexec /usr/local/bin/system-settings-helper.sh battery-start 0", shell=True)
|
|
subprocess.run("pkexec /usr/local/bin/system-settings-helper.sh battery-end 100", shell=True)
|
|
self.update_status()
|
|
if self.icon:
|
|
self.icon.title = self.get_tooltip()
|
|
|
|
def set_fan(self, level):
|
|
if level == 2:
|
|
# Level 2 = automatic control
|
|
subprocess.run("pkexec /usr/local/bin/system-settings-helper.sh fan-start-thinkfan", shell=True)
|
|
else:
|
|
# Other levels = manual control
|
|
subprocess.run("pkexec /usr/local/bin/system-settings-helper.sh fan-stop-thinkfan", shell=True)
|
|
subprocess.run(f"pkexec /usr/local/bin/system-settings-helper.sh fan-level {level}", shell=True)
|
|
self.update_status()
|
|
if self.icon:
|
|
self.icon.title = self.get_tooltip()
|
|
|
|
def get_tooltip(self):
|
|
"""Generate tooltip text"""
|
|
return f"T14: {self.current_status['temp']}°C | {self.current_status['governor']} | {self.current_status['battery']}% {self.current_status['charging']}"
|
|
|
|
def create_menu(self):
|
|
"""Create system tray menu"""
|
|
return pystray.Menu(
|
|
pystray.MenuItem("T14 System Settings", pystray.Menu.SEPARATOR),
|
|
pystray.MenuItem("Open Full GUI", self.open_full_gui, default=True),
|
|
pystray.MenuItem("CPU Governor", pystray.Menu(
|
|
pystray.MenuItem("Performance", lambda: self.set_governor("performance")),
|
|
pystray.MenuItem("Powersave", lambda: self.set_governor("powersave"))
|
|
)),
|
|
pystray.MenuItem("Fan Level", pystray.Menu(
|
|
pystray.MenuItem("Level 1", lambda: self.set_fan(1)),
|
|
pystray.MenuItem("Level 2", lambda: self.set_fan(2)),
|
|
pystray.MenuItem("Level 3", lambda: self.set_fan(3)),
|
|
pystray.MenuItem("Level 4", lambda: self.set_fan(4)),
|
|
pystray.MenuItem("Level 5", lambda: self.set_fan(5)),
|
|
pystray.MenuItem("Level 6", lambda: self.set_fan(6)),
|
|
pystray.MenuItem("Level 7", lambda: self.set_fan(7))
|
|
)),
|
|
pystray.MenuItem("Battery", pystray.Menu(
|
|
pystray.MenuItem("⚡ Charge to 100% NOW", self.charge_now),
|
|
pystray.MenuItem("Conservative (75-80%)", lambda: self.set_battery(75, 80)),
|
|
pystray.MenuItem("Moderate (80-90%)", lambda: self.set_battery(80, 90)),
|
|
pystray.MenuItem("Full (0-100%)", lambda: self.set_battery(0, 100))
|
|
)),
|
|
pystray.MenuItem("Refresh Status", lambda: self.refresh_status()),
|
|
pystray.MenuItem("Quit", self.quit_app)
|
|
)
|
|
|
|
def refresh_status(self):
|
|
self.update_status()
|
|
if self.icon:
|
|
self.icon.title = self.get_tooltip()
|
|
|
|
def quit_app(self, icon=None, item=None):
|
|
if self.icon:
|
|
self.icon.stop()
|
|
|
|
def run(self):
|
|
"""Run the system tray application"""
|
|
self.icon = pystray.Icon(
|
|
"t14_settings",
|
|
self.create_image(),
|
|
self.get_tooltip(),
|
|
self.create_menu()
|
|
)
|
|
|
|
# Update status every 30 seconds
|
|
def update_loop():
|
|
import time
|
|
while True:
|
|
time.sleep(30)
|
|
self.refresh_status()
|
|
|
|
thread = threading.Thread(target=update_loop, daemon=True)
|
|
thread.start()
|
|
|
|
self.icon.run()
|
|
|
|
if __name__ == "__main__":
|
|
app = SystemTrayApp()
|
|
app.run()
|