Created
January 31, 2026 17:00
-
-
Save svalordev/cac63601cf6bd9a9fbf958f5a45462d9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import customtkinter as ctk | |
| import os | |
| import threading | |
| import time | |
| # Configuration | |
| ctk.set_appearance_mode("System") | |
| ctk.set_default_color_theme("blue") | |
| class ScreenPadControl(ctk.CTk): | |
| def __init__(self): | |
| super().__init__() | |
| # Window Setup | |
| self.title("Asus ScreenPad Controller") | |
| self.geometry("400x320") | |
| self.resizable(False, False) | |
| # Hardware Path (Plippo/asus-wmi-screenpad standard) | |
| self.brightness_path = '/sys/class/leds/asus::screenpad/brightness' | |
| # Debounce State | |
| self._pending_value = None | |
| self._timer = None | |
| # Layout | |
| self.grid_columnconfigure(0, weight=1) | |
| self.grid_rowconfigure((0, 1, 2, 3), weight=1) | |
| # UI Elements | |
| self.title_label = ctk.CTkLabel(self, text="ScreenPad Brightness", font=("Roboto Medium", 20)) | |
| self.title_label.grid(row=0, column=0, pady=(20, 10)) | |
| self.value_label = ctk.CTkLabel(self, text="Reading...", font=("Roboto", 16)) | |
| self.value_label.grid(row=1, column=0) | |
| # Slider: Note the command is now 'schedule_update' | |
| self.slider = ctk.CTkSlider(self, from_=0, to=255, number_of_steps=255, command=self.schedule_update) | |
| self.slider.grid(row=2, column=0, padx=20, sticky="ew") | |
| # Buttons | |
| self.button_frame = ctk.CTkFrame(self, fg_color="transparent") | |
| self.button_frame.grid(row=3, column=0, pady=20) | |
| self.btn_on = ctk.CTkButton(self.button_frame, text="ON (190)", command=lambda: self.force_update(190)) | |
| self.btn_on.pack(side="left", padx=10) | |
| self.btn_off = ctk.CTkButton(self.button_frame, text="OFF", fg_color="#C0392B", hover_color="#922B21", command=lambda: self.force_update(0)) | |
| self.btn_off.pack(side="left", padx=10) | |
| self.init_hardware_state() | |
| def init_hardware_state(self): | |
| """Reads initial state gracefully.""" | |
| if os.access(self.brightness_path, os.R_OK): | |
| try: | |
| with open(self.brightness_path, 'r') as f: | |
| val = int(f.read().strip()) | |
| self.slider.set(val) | |
| self.value_label.configure(text=str(val)) | |
| except Exception as e: | |
| self.value_label.configure(text="Error reading state") | |
| else: | |
| self.value_label.configure(text="Permission Denied") | |
| def schedule_update(self, value): | |
| """Debounce logic: Updates UI immediately, defers hardware write.""" | |
| int_val = int(value) | |
| self.value_label.configure(text=str(int_val)) | |
| self._pending_value = int_val | |
| # Cancel existing timer if active | |
| if self._timer is not None: | |
| self._timer.cancel() | |
| # Schedule write after 100ms of inactivity | |
| self._timer = threading.Timer(0.1, self._execute_write) | |
| self._timer.start() | |
| def force_update(self, value): | |
| """Bypasses debounce for button clicks.""" | |
| self.slider.set(value) | |
| self.value_label.configure(text=str(value)) | |
| self._pending_value = value | |
| if self._timer: self._timer.cancel() | |
| self._execute_write() | |
| def _execute_write(self): | |
| """Performs the actual file I/O.""" | |
| if self._pending_value is None: return | |
| try: | |
| with open(self.brightness_path, 'w') as f: | |
| f.write(str(self._pending_value)) | |
| except PermissionError: | |
| # Update UI on main thread to warn user | |
| self.after(0, lambda: self.value_label.configure(text="Sudo Required!")) | |
| except FileNotFoundError: | |
| self.after(0, lambda: self.value_label.configure(text="Driver Not Loaded")) | |
| if __name__ == "__main__": | |
| app = ScreenPadControl() | |
| app.mainloop() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment