├── README.md └── fan.py /README.md: -------------------------------------------------------------------------------- 1 | # Thinkpad Fan Control GUI 2 | 3 | ![Screenshot](https://i.imgur.com/UXII1Mg.png) 4 | 5 | This is an application for controlling fan speed on IBM/Lenovo ThinkPads. 6 | 7 | It can also monitor CPU temp and fan RPM. 8 | 9 | It is written for Linux only. For windows, see http://www.almico.com/speedfan.php 10 | 11 | ## How it Works? 12 | + Parses `sensors` command to show CPU temp and fan RPM 13 | + Modifies `/proc/acpi/ibm/fan` to change fan speed 14 | 15 | ## Dependencies 16 | `sudo apt install lm-sensors python3 python3-tk` 17 | 18 | ## Setup 19 | + Open this file, using command -- `sudo nano /etc/modprobe.d/thinkpad_acpi.conf` 20 | + Add line `options thinkpad_acpi fan_control=1` 21 | + Reboot. 22 | + `python3 fan.py` 23 | 24 | ( Add `sudo` to modify speed ) 25 | 26 | --- 27 | 28 | Note: You are required to have the Linux kernel with `thinkpad-acpi` patch. (Ubuntu, Solus and a few others already seem to have this) 29 | -------------------------------------------------------------------------------- /fan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import tkinter as tk 3 | import subprocess 4 | from time import sleep 5 | from threading import Thread 6 | 7 | 8 | def get_info(): 9 | info_lines = subprocess.check_output("sensors").decode("utf-8").split("\n") 10 | result = [] 11 | count = 0 12 | for i in info_lines: 13 | if "Core" in i: 14 | result.append("Core %d: " % count + i.split(":")[-1].split("(")[0].strip()) 15 | count += 1 16 | 17 | if "fan" in i: 18 | result.append("Fan : " + i.split(":")[-1].strip()) 19 | 20 | return result 21 | 22 | 23 | def set_speed(speed=None): 24 | """ 25 | Set speed of fan by changing level at /proc/acpi/ibm/fan 26 | speed: 0-7, auto, disengaged, full-speed 27 | """ 28 | print("set level to %r" % speed) 29 | return subprocess.check_output( 30 | 'echo level {0} | sudo tee "/proc/acpi/ibm/fan"'.format(speed), 31 | shell=True 32 | ).decode() 33 | 34 | 35 | class MainApplication(tk.Frame): 36 | def __init__(self, parent, *args, **kwargs): 37 | tk.Frame.__init__(self, parent, *args, **kwargs) 38 | self.parent = parent 39 | self.parent.minsize(width=100, height=100) 40 | 41 | main_label = tk.Label(parent, text="") 42 | main_label.grid(row=0, column=0) 43 | 44 | row1 = tk.Frame() 45 | row1.grid() 46 | for i in range(8): 47 | tk.Button(row1, text=str(i), command=lambda x=i: set_speed(x)).grid( 48 | row=0, column=i + 1 49 | ) 50 | 51 | row2 = tk.Frame() 52 | row2.grid() 53 | 54 | tk.Button(row2, text="Auto", command=lambda: set_speed("auto")).grid( 55 | row=0, column=0 56 | ) 57 | tk.Button(row2, text="Full", command=lambda: set_speed("full-speed")).grid( 58 | row=0, column=1 59 | ) 60 | 61 | def display_loop(): 62 | while True: 63 | sleep(0.5) 64 | main_label["text"] = "\n".join(get_info()) 65 | 66 | Thread(target=display_loop).start() 67 | 68 | 69 | if __name__ == "__main__": 70 | root = tk.Tk() 71 | root.title("Thinkfan Control") 72 | MainApplication(root).grid() 73 | root.mainloop() 74 | --------------------------------------------------------------------------------