写脚本的原因

之前每次开机都要进BMC把风扇转速调低,不然吵得要死。但是又不能调得太低,因为如果调的太低怕一不留神温度就飙上去了,导致硬件出问题。
正好前几天看到了一个别人用Python写的简易风扇一键调节脚本,但是只能设置固定转速,没办法根据温度动态调节。于是我重构了这个脚本,做到了根据温度动态调节风扇转速。

环境要求

  • 系统要求:Linux(Windows不可用,因为没有sensors软件包)
  • 软件环境:Python3环境,sensors软件包(用于获取CPU温度)

Python源代码

import subprocess
import re
import http.client
import time

# 全局变量用于存储会话连接和令牌
conn = None
session_cookie = None

def login(IP, User, Password):
    global conn, session_cookie
    if conn is not None:
        conn.close()  # 关闭旧连接
    conn = http.client.HTTPConnection(IP)
    payload = "WEBVAR_USERNAME=" + User + "&WEBVAR_PASSWORD=" + Password

    headers = {
        'Content-Type': "application/x-www-form-urlencoded",
        'Origin': "http://{}".format(IP),
        'Referer': "http://{}/index.html".format(IP),
        'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0",
    }

    conn.request("POST", "/rpc/WEBSES/create.asp", payload, headers)
    response = conn.getresponse().read().decode("utf-8")
    match = re.search(r"'SESSION_COOKIE' : '([^']*)'", response)
    if match:
        session_cookie = match.group(1)
    else:
        raise ValueError("SESSION_COOKIE not found in the string")

def set_fan_speed(IP, fan_speeds):
    global conn, session_cookie

    # 设置风扇为手动模式
    payload1 = "MODE=1"
    headers1 = {
        'Content-Type': "application/x-www-form-urlencoded",
        'Cookie': "BMC_IP_ADDR={}; SessionCookie=".format(IP) + session_cookie,
    }
    conn.request("POST", "/rpc/setfanmode.asp", payload1, headers1)
    conn.getresponse()

    # 调整风扇转速
    headers2 = {
        'Content-Type': "application/x-www-form-urlencoded",
        'Cookie': "SessionCookie=" + session_cookie + "; BMC_IP_ADDR={}".format(IP),
    }

    for fan_ID, Speed_x in fan_speeds.items():
        payload2 = "ID=" + fan_ID + "&PERCENT=" + Speed_x
        conn.request("POST", "/rpc/setfanspeed.asp", payload2, headers2)
        conn.getresponse()
        print("调整风扇{}转速{}%".format(fan_ID, Speed_x))

def get_package_temperatures():
    output = subprocess.check_output(['sensors']).decode('utf-8')
    pattern = re.compile(
        r'^Package id (\d+):\s+\+([\d.]+)\.?\s*°C',
        re.MULTILINE
    )
    matches = pattern.findall(output)
    result = {f"Package id {package_id}": int(float(temp_str)) for package_id, temp_str in matches}
    return result

def compute_fan_speed(temp):
    if temp <= 45:
        return 10
    elif temp >= 70:
        return 100
    else:
        return int(10 + (temp - 45) * (90 / (70 - 45)))

last_temp = None

if __name__ == "__main__":
    IP = "192.168.0.6" #服务器BMC内网IP地址
    User = "admin" #服务器BMC管理员账号
    Password = "admin" #服务器BMC管理员密码

    # 首次登录
    login(IP, User, Password)

    while True:
        try:
            temperatures = get_package_temperatures()
            max_temp = max(temperatures.values())

            if last_temp is None or abs(max_temp - last_temp) >= 2:
                fan_speed = str(compute_fan_speed(max_temp))
                set_fan_speed(IP, {"0": fan_speed, "2": str(compute_fan_speed(max_temp)+3), "4": fan_speed, "6": fan_speed})
                last_temp = max_temp
                print(temperatures, max_temp)
            else:
                continue
                #print(f"温度变化不足2度 ({last_temp}℃ -> {max_temp}℃),跳过风扇转速调整.")

        except Exception as e:
            print(f"遇到错误: {e}")
            # 发生错误时重新登录
            login(IP, User, Password)
        time.sleep(2)

代码说明

  1. 风扇转速的策略是,45度以下风扇转速10%,超过45度开始线性增加,70度时风扇转速100%。可以根据需要修改compute_fan_speed函数的逻辑。
  2. 02号风扇默认比其他风扇转速高3%,因为这个风扇对应的是CPU1,为了安装显卡换了散热器,并且阻挡较多温度较高,所以风扇转速设置偏高一点平衡两边温度。如果不需要安装显卡可以修改set_fan_speed(IP, {"0": fan_speed, "2": str(compute_fan_speed(max_temp)+3), "4": fan_speed, "6": fan_speed})这部分,风扇速度传入的是一个字典,键值对是风扇ID:转速百分比
  3. 如果你的服务器是其他有八个风扇的型号(例如SA5112M4),那只需要吧传入的字典部分改成八个对应的键值对即可。

希望这个风扇调节脚本可以帮到你。