#!/usr/bin/env python3 import tkinter as tk from tkinter import ttk import subprocess import threading class SystemSettingsGUI: def __init__(self, root): self.root = root self.root.title("T14 System Settings") self.root.geometry("650x900") self.root.configure(bg='#f0f0f0') # Title title = tk.Label(root, text="T14 System Settings", font=("Arial", 16, "bold"), bg='#f0f0f0') title.pack(pady=10) # CPU Governor Section self.create_section(root, "CPU Governor") self.gov_var = tk.StringVar() gov_frame = tk.Frame(root, bg='#f0f0f0') gov_frame.pack(pady=5) tk.Button(gov_frame, text="Performance", width=15, command=lambda: self.set_governor("performance")).pack(side=tk.LEFT, padx=5) tk.Button(gov_frame, text="Powersave", width=15, command=lambda: self.set_governor("powersave")).pack(side=tk.LEFT, padx=5) self.gov_label = tk.Label(root, text="", font=("Arial", 10), bg='#f0f0f0', fg='#0066cc') self.gov_label.pack() # Fan Level Section self.create_section(root, "Fan Level") fan_frame = tk.Frame(root, bg='#f0f0f0') fan_frame.pack(pady=5) for level in range(1, 8): tk.Button(fan_frame, text=f"L{level}", width=5, command=lambda l=level: self.set_fan_level(l)).pack(side=tk.LEFT, padx=2) self.fan_label = tk.Label(root, text="", font=("Arial", 10), bg='#f0f0f0', fg='#0066cc') self.fan_label.pack() # Battery Thresholds Section self.create_section(root, "Battery Charging - Manual") battery_frame = tk.Frame(root, bg='#f0f0f0') battery_frame.pack(pady=5) tk.Button(battery_frame, text="Conservative (75-80%)", width=20, command=lambda: self.set_battery(75, 80)).pack(pady=5) tk.Button(battery_frame, text="Moderate (80-90%)", width=20, command=lambda: self.set_battery(80, 90)).pack(pady=5) tk.Button(battery_frame, text="Full Charge (0-100%)", width=20, command=lambda: self.set_battery(0, 100)).pack(pady=5) tk.Button(battery_frame, text="⚡ Charge to 100% NOW", width=20, bg='#ffaa00', command=self.charge_full_now).pack(pady=5) self.battery_label = tk.Label(root, text="", font=("Arial", 10), bg='#f0f0f0', fg='#0066cc') self.battery_label.pack() # Automated Battery Management Section self.create_section(root, "Automated Battery Management") auto_frame = tk.Frame(root, bg='#f0f0f0') auto_frame.pack(pady=5) tk.Button(auto_frame, text="Setup Time-Based (3:45 PM)", width=25, command=self.setup_time_based).pack(pady=3) tk.Button(auto_frame, text="Setup Network-Based", width=25, command=self.setup_network_based).pack(pady=3) self.auto_label = tk.Label(root, text="Not configured", font=("Arial", 9), bg='#f0f0f0', fg='#666666') self.auto_label.pack() # Status Section self.create_section(root, "Current Status") self.status_label = tk.Label(root, text="Loading...", font=("Arial", 9), bg='#f0f0f0', justify=tk.LEFT) self.status_label.pack(pady=10) # Refresh button tk.Button(root, text="Refresh Status", command=self.refresh_all).pack(pady=10) # Start refresh thread self.refresh_all() def create_section(self, parent, title): sep = ttk.Separator(parent, orient='horizontal') sep.pack(fill='x', pady=10) label = tk.Label(parent, text=title, font=("Arial", 12, "bold"), bg='#f0f0f0') label.pack() 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 f"Error: {e}" def set_governor(self, gov): self.run_command(f"pkexec /usr/local/bin/system-settings-helper.sh cpu-governor {gov}") self.update_governor_label() def update_governor_label(self): gov = self.run_command("cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor") self.gov_label.config(text=f"Current: {gov.upper()}") def set_fan_level(self, level): if level == 2: # Level 2 = re-enable automatic control via thinkfan self.run_command("pkexec /usr/local/bin/system-settings-helper.sh fan-start-thinkfan") self.fan_label.config(text="Fan: Automatic (thinkfan managing)") else: # Other levels = manual control, stop thinkfan self.run_command("pkexec /usr/local/bin/system-settings-helper.sh fan-stop-thinkfan") self.run_command(f"pkexec /usr/local/bin/system-settings-helper.sh fan-level {level}") self.update_fan_label() def update_fan_label(self): fan_info = self.run_command("grep -E 'speed|level' /proc/acpi/ibm/fan | head -2") self.fan_label.config(text=fan_info) def set_battery(self, start, end): # Get current capacity and status capacity = int(self.run_command("cat /sys/class/power_supply/BAT0/capacity")) status = self.run_command("cat /sys/class/power_supply/BAT0/status") # If currently charging and new end threshold is below current capacity, stop charging first if status == "Charging" and end < capacity: # Set end threshold below current to stop charging self.run_command(f"pkexec /usr/local/bin/system-settings-helper.sh battery-end {capacity - 1}") import time time.sleep(0.5) # Now set the desired thresholds self.run_command(f"pkexec /usr/local/bin/system-settings-helper.sh battery-start {start}") self.run_command(f"pkexec /usr/local/bin/system-settings-helper.sh battery-end {end}") self.update_battery_label() def update_battery_label(self): start = self.run_command("cat /sys/class/power_supply/BAT0/charge_control_start_threshold") end = self.run_command("cat /sys/class/power_supply/BAT0/charge_control_end_threshold") capacity = self.run_command("cat /sys/class/power_supply/BAT0/capacity") self.battery_label.config(text=f"Thresholds: {start}-{end}% | Current: {capacity}%") def charge_full_now(self): # Set to full range to force charging self.run_command("pkexec /usr/local/bin/system-settings-helper.sh battery-start 0") self.run_command("pkexec /usr/local/bin/system-settings-helper.sh battery-end 100") # Wait a moment and check import time time.sleep(0.5) capacity = self.run_command("cat /sys/class/power_supply/BAT0/capacity") status = self.run_command("cat /sys/class/power_supply/BAT0/status") # Visual feedback self.battery_label.config(text=f"⚡ Charging to 100% | Status: {status} | Current: {capacity}%", fg='#ff6600') # Refresh after a second to show updated status self.root.after(2000, self.update_battery_label) def refresh_all(self): thread = threading.Thread(target=self._refresh_status) thread.daemon = True thread.start() def _refresh_status(self): temp = self.run_command("cat /sys/class/hwmon/hwmon5/temp1_input | awk '{print int($1/1000)}'") gov = self.run_command("cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor") fan = self.run_command("grep '^speed' /proc/acpi/ibm/fan | awk '{print $NF}'") capacity = self.run_command("cat /sys/class/power_supply/BAT0/capacity") status = self.run_command("cat /sys/class/power_supply/BAT0/status") status_text = f""" CPU Temperature: {temp}°C Governor: {gov.upper()} Fan Speed: {fan} RPM Battery: {capacity}% Status: {status} """ self.status_label.config(text=status_text) self.update_governor_label() self.update_fan_label() self.update_battery_label() def setup_time_based(self): # Create a dialog for time-based setup dialog = tk.Toplevel(self.root) dialog.title("Time-Based Battery Management") dialog.geometry("400x250") dialog.configure(bg='#f0f0f0') tk.Label(dialog, text="Time-Based Battery Management", font=("Arial", 12, "bold"), bg='#f0f0f0').pack(pady=10) tk.Label(dialog, text="At 3:45 PM: Switch to 0-100% charging\nBefore 3:45 PM: Keep 75-80% charging", bg='#f0f0f0').pack(pady=10) tk.Label(dialog, text="Time threshold (HH:MM):", bg='#f0f0f0').pack() time_entry = tk.Entry(dialog, width=10) time_entry.insert(0, "15:45") time_entry.pack(pady=5) def confirm_time_based(): threshold_time = time_entry.get() self.install_time_based_management(threshold_time) dialog.destroy() tk.Button(dialog, text="Install", command=confirm_time_based).pack(pady=10) def setup_network_based(self): # Create a dialog for network-based setup dialog = tk.Toplevel(self.root) dialog.title("Network-Based Battery Management") dialog.geometry("450x250") dialog.configure(bg='#f0f0f0') tk.Label(dialog, text="Network-Based Battery Management", font=("Arial", 12, "bold"), bg='#f0f0f0').pack(pady=10) tk.Label(dialog, text="Logic:\n• At office before 3:45 PM → 75-80% (conservative)\n• At office after 3:45 PM → 0-100% (full charge)\n• At home (not on office WiFi) → 75-80% (conservative)", justify=tk.LEFT, bg='#f0f0f0').pack(pady=10) tk.Label(dialog, text="Office WiFi SSID:", bg='#f0f0f0').pack() office_entry = tk.Entry(dialog, width=30) office_entry.pack(pady=5) tk.Label(dialog, text="Time threshold (HH:MM):", bg='#f0f0f0').pack() time_entry = tk.Entry(dialog, width=10) time_entry.insert(0, "15:45") time_entry.pack(pady=5) def confirm_network_based(): office_ssid = office_entry.get() threshold_time = time_entry.get() if office_ssid: self.install_network_based_management(office_ssid, threshold_time) dialog.destroy() tk.Button(dialog, text="Install", command=confirm_network_based).pack(pady=10) def install_time_based_management(self, threshold_time): # Create systemd timer for time-based switching script_content = f"""#!/bin/bash # Switch to full charging at {threshold_time} echo 0 > /sys/class/power_supply/BAT0/charge_control_start_threshold echo 100 > /sys/class/power_supply/BAT0/charge_control_end_threshold echo "[$(date)] Switched to 0-100% charging (time-based)" >> /var/log/battery-thresholds.log """ self.run_command(f"sudo tee /usr/local/bin/battery-charge-full.sh > /dev/null << 'EOF'\n{script_content}EOF") self.run_command("sudo chmod +x /usr/local/bin/battery-charge-full.sh") # Create systemd timer timer_unit = f"""[Unit] Description=Switch battery to full charge at {threshold_time} After=network.target [Timer] OnCalendar=*-*-* {threshold_time}:00 Persistent=true [Install] WantedBy=timers.target """ self.run_command(f"sudo tee /etc/systemd/system/battery-charge-full.timer > /dev/null << 'EOF'\n{timer_unit}EOF") service_unit = """[Unit] Description=Battery full charge service After=network.target [Service] Type=oneshot ExecStart=/usr/local/bin/battery-charge-full.sh """ self.run_command(f"sudo tee /etc/systemd/system/battery-charge-full.service > /dev/null << 'EOF'\n{service_unit}EOF") self.run_command("sudo systemctl daemon-reload") self.run_command("sudo systemctl enable battery-charge-full.timer") self.run_command("sudo systemctl start battery-charge-full.timer") self.auto_label.config(text=f"✓ Time-based enabled (switch at {threshold_time})", fg='#009900') def install_network_based_management(self, office_ssid, threshold_time): # Create network monitoring script script_content = f"""#!/bin/bash # Network-based battery management OFFICE_SSID="{office_ssid}" THRESHOLD_TIME="{threshold_time}" CURRENT_SSID=$(nmcli -t -f active,ssid dev wifi | grep '^yes' | cut -d: -f2) CURRENT_TIME=$(date +%H:%M) if [ "$CURRENT_SSID" = "$OFFICE_SSID" ]; then # At office - check time if [ "$CURRENT_TIME" \>= "$THRESHOLD_TIME" ]; then # After threshold time - charge to full before leaving echo 0 > /sys/class/power_supply/BAT0/charge_control_start_threshold echo 100 > /sys/class/power_supply/BAT0/charge_control_end_threshold echo "[$(date)] Office WiFi + after $THRESHOLD_TIME - 0-100% charging (full before leaving)" >> /var/log/battery-thresholds.log else # Before threshold time - conservative charging echo 75 > /sys/class/power_supply/BAT0/charge_control_start_threshold echo 80 > /sys/class/power_supply/BAT0/charge_control_end_threshold echo "[$(date)] Office WiFi + before $THRESHOLD_TIME - 75-80% charging (conservative)" >> /var/log/battery-thresholds.log fi else # Not on office WiFi - assume at home, conservative charging echo 75 > /sys/class/power_supply/BAT0/charge_control_start_threshold echo 80 > /sys/class/power_supply/BAT0/charge_control_end_threshold echo "[$(date)] Not on office WiFi (at home) - 75-80% charging (conservative)" >> /var/log/battery-thresholds.log fi """ self.run_command(f"sudo tee /usr/local/bin/battery-network-mgmt.sh > /dev/null << 'EOFSCRIPT'\n{script_content}EOFSCRIPT") self.run_command("sudo chmod +x /usr/local/bin/battery-network-mgmt.sh") # Create dispatcher script for NetworkManager self.run_command(f"sudo tee /etc/NetworkManager/dispatcher.d/99-battery-management > /dev/null << 'EOF'\n#!/bin/bash\n/usr/local/bin/battery-network-mgmt.sh\nEOF") self.run_command("sudo chmod +x /etc/NetworkManager/dispatcher.d/99-battery-management") # Also create systemd timer to check every 5 minutes as backup timer_content = """[Unit] Description=Battery Network Management Timer After=network.target [Timer] OnBootSec=2min OnUnitActiveSec=5min Persistent=true [Install] WantedBy=timers.target """ self.run_command(f"sudo tee /etc/systemd/system/battery-network-check.timer > /dev/null << 'EOF'\n{timer_content}EOF") service_content = """[Unit] Description=Battery Network Management Check After=network.target [Service] Type=oneshot ExecStart=/usr/local/bin/battery-network-mgmt.sh """ self.run_command(f"sudo tee /etc/systemd/system/battery-network-check.service > /dev/null << 'EOF'\n{service_content}EOF") self.run_command("sudo systemctl daemon-reload") self.run_command("sudo systemctl enable battery-network-check.timer") self.run_command("sudo systemctl start battery-network-check.timer") # Run once immediately self.run_command("sudo /usr/local/bin/battery-network-mgmt.sh") self.auto_label.config(text=f"✓ Network-based enabled (Office: {office_ssid}, at {threshold_time})", fg='#009900') if __name__ == "__main__": root = tk.Tk() app = SystemSettingsGUI(root) root.mainloop()