Created
February 28, 2025 17:31
-
-
Save Allesanddro/417063843939695719e4f46f19d2c5b4 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
#!/usr/bin/env python3 | |
import subprocess | |
import re | |
import time | |
import logging | |
FAN_CURVE = { | |
25: 0, | |
28: 3, | |
30: 5, | |
32: 7, | |
34: 9, | |
36: 11, | |
38: 13, | |
40: 15, | |
42: 20, | |
44: 25, | |
46: 30, | |
48: 35, | |
50: 40, | |
52: 45, | |
54: 50, | |
56: 55, | |
58: 60, | |
60: 65, | |
62: 70, | |
64: 75, | |
66: 78, | |
68: 80, | |
70: 80, | |
75: 80, | |
} | |
FAN_SPEED_FAILSAFE = 50 | |
IPMI_USER = "root" | |
HOST_IP = "192.168.178.220" | |
IPMI_PASSWORD = "YourPassWord" | |
IPMITOOL_PATH = "/usr/bin/ipmitool" | |
SLEEP_INTERVAL = 10 | |
LOG_FILE = "/var/log/fanspeed.log" | |
logging.basicConfig(filename=LOG_FILE, level=logging.INFO, | |
format='%(asctime)s - %(levelname)s - %(message)s') | |
def get_ipmitool_base_command_list(): | |
return [IPMITOOL_PATH, "-I", "lanplus", "-H", HOST_IP, "-U", IPMI_USER, "-P", IPMI_PASSWORD] | |
def set_fan_speed_python(ipmitool_base_command, fan_percentage): | |
hex_value = "{:02X}".format(int(fan_percentage * 255 / 100)) | |
ipmitool_corrected_fan_set_command_list = ipmitool_base_command + ["raw", "0x30", "0x30", "0x02", "0xff", f"0x{hex_value}"] | |
try: | |
subprocess.run(ipmitool_corrected_fan_set_command_list, check=True, capture_output=True) | |
output_message = f"Fan speed set to {fan_percentage}% (Python) - Remote Host {HOST_IP} - Corrected Command - HEX: 0x{hex_value}" | |
echo_output = subprocess.run(['echo', output_message], capture_output=True, text=True) | |
print(echo_output.stdout.strip()) | |
logging.info(f"Fan speed set to {fan_percentage}% - HEX: 0x{hex_value}") | |
return True | |
except subprocess.CalledProcessError as e: | |
error_message = f"ERROR (Python): Failed to set fan speed to {fan_percentage}% (Python) - Remote Host {HOST_IP} - Corrected Command. Error: {e}" | |
echo_output = subprocess.run(['echo', error_message], capture_output=True, text=True) | |
print(echo_output.stdout.strip()) | |
logging.error(error_message) | |
return False | |
except FileNotFoundError: | |
error_message = "ERROR (Python): ipmitool executable not found." | |
echo_output = subprocess.run(['echo', error_message], capture_output=True, text=True) | |
print(echo_output.stdout.strip()) | |
logging.error(error_message) | |
return False | |
def get_cpu_temp_python(ipmitool_base_command): | |
try: | |
command_list = ipmitool_base_command + ["sdr", "get", "Temp"] | |
process = subprocess.run(command_list, capture_output=True, text=True, check=True) | |
raw_temp_detail = process.stdout | |
except subprocess.CalledProcessError as e: | |
error_message = f"ERROR: Could not get CPU Temperature from ipmitool (sdr get Temp) - Remote Host {HOST_IP}" | |
echo_output = subprocess.run(['echo', error_message], capture_output=True, text=True) | |
print(echo_output.stdout.strip()) | |
logging.error(error_message) | |
return None | |
except FileNotFoundError: | |
error_message = "ERROR (Python): ipmitool executable not found." | |
echo_output = subprocess.run(['echo', error_message], capture_output=True, text=True) | |
print(echo_output.stdout.strip()) | |
logging.error(error_message) | |
return None | |
temp_reading_line = "" | |
for line in raw_temp_detail.splitlines(): | |
if "Sensor Reading" in line: | |
temp_reading_line = line.strip() | |
break | |
if not temp_reading_line: | |
error_message = f"ERROR: Failed to parse temperature from ipmitool output (sdr get Temp) - 'Sensor Reading' line missing - Remote Host {HOST_IP}" | |
echo_output = subprocess.run(['echo', error_message], capture_output=True, text=True) | |
print(echo_output.stdout.strip()) | |
logging.error(error_message) | |
return None | |
match = re.search(r":\s*(\d+)", temp_reading_line) | |
if match: | |
temp_value_str = match.group(1) | |
return temp_value_str | |
else: | |
error_message = f"ERROR: Failed to parse numeric temperature value from ipmitool output (sdr get Temp) - Regex failed - Remote Host {HOST_IP}" | |
echo_output = subprocess.run(['echo', error_message], capture_output=True, text=True) | |
print(echo_output.stdout.strip()) | |
logging.error(error_message) | |
return None | |
def calculate_fan_speed_curve(temp_celsius, fan_curve): | |
temp_points = sorted(fan_curve.keys()) | |
if temp_celsius <= temp_points[0]: | |
return fan_curve[temp_points[0]] | |
if temp_celsius >= temp_points[-1]: | |
return fan_curve[temp_points[-1]] | |
for i in range(len(temp_points) - 1): | |
temp_lower = temp_points[i] | |
temp_upper = temp_points[i+1] | |
if temp_lower <= temp_celsius <= temp_upper: | |
speed_lower = fan_curve[temp_lower] | |
speed_upper = fan_curve[temp_upper] | |
fan_speed = speed_lower + (speed_upper - speed_lower) * (temp_celsius - temp_lower) / (temp_upper - temp_lower) | |
return max(min(int(fan_speed), 100), 0) | |
def set_failsafe_fan_python(ipmitool_base_command, failsafe_fan_speed): | |
failsafe_message = f"Setting failsafe fan speed: {failsafe_fan_speed}% (Python)" | |
echo_output = subprocess.run(['echo', failsafe_message], capture_output=True, text=True) | |
print(echo_output.stdout.strip()) | |
logging.warning(failsafe_message) | |
return set_fan_speed_python(ipmitool_base_command, failsafe_fan_speed) | |
if __name__ == "__main__": | |
ipmitool_base_command_list = get_ipmitool_base_command_list() | |
while True: | |
cpu_temp_str = get_cpu_temp_python(ipmitool_base_command_list) | |
if cpu_temp_str is None: | |
set_failsafe_fan_python(ipmitool_base_command_list, FAN_SPEED_FAILSAFE) | |
continue | |
try: | |
cpu_temp = int(cpu_temp_str) | |
except ValueError: | |
error_message = f"ERROR (Python): Could not convert temperature value '{cpu_temp_str}' to integer." | |
echo_output = subprocess.run(['echo', error_message], capture_output=True, text=True) | |
print(echo_output.stdout.strip()) | |
logging.error(error_message) | |
set_failsafe_fan_python(ipmitool_base_command_list, FAN_SPEED_FAILSAFE) | |
continue | |
target_fan_speed = calculate_fan_speed_curve(cpu_temp, FAN_CURVE) | |
output_message = f"Current CPU Temp (Python): {cpu_temp}°C, Target Fan Speed: {target_fan_speed}%" | |
echo_output = subprocess.run(['echo', output_message], capture_output=True, text=True) | |
print(echo_output.stdout.strip()) | |
logging.info(f"CPU Temp: {cpu_temp}°C, Target Fan Speed: {target_fan_speed}%") | |
if not set_fan_speed_python(ipmitool_base_command_list, target_fan_speed): | |
set_failsafe_fan_python(ipmitool_base_command_list, FAN_SPEED_FAILSAFE) | |
time.sleep(SLEEP_INTERVAL) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment