diff --git a/README.md b/README.md index 7d194f3..c7a3152 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,16 @@ # asrock-pwm-ipmi +This is a script to control 4-pin PWM fans on ASRock Rack motherboards with IPMI. The BMC does not properly expose fan control, so they must be controlled using raw IPMI commands. This script is a user-friendly way to do that. + + +usage: asrock-pwm-ipmi [-h] [-i] [FAN:SPEED [FAN:SPEED ...]] + +Read information about and control fans on ASRock boards with IPMI. + +positional arguments: + FAN:SPEED Fan to change the speed of, and the speed, separated by ':'. Set + to 0 for auto. + +optional arguments: + -h, --help show this help message and exit + -i, --info Read fan information diff --git a/__pycache__/run_cmd.cpython-37.pyc b/__pycache__/run_cmd.cpython-37.pyc new file mode 100644 index 0000000..e83be71 Binary files /dev/null and b/__pycache__/run_cmd.cpython-37.pyc differ diff --git a/asrock-pwm-ipmi b/asrock-pwm-ipmi new file mode 100755 index 0000000..0a01e24 --- /dev/null +++ b/asrock-pwm-ipmi @@ -0,0 +1,2 @@ +#!/bin/bash +./pwm.py $@ diff --git a/pwm.py b/pwm.py new file mode 100755 index 0000000..319ad7d --- /dev/null +++ b/pwm.py @@ -0,0 +1,37 @@ +#!/usr/bin/python3 +import run_cmd +import argparse + +MAXFAN = 6 + +parser = argparse.ArgumentParser(description='Read information about and control fans on ASRock boards with IPMI.', prog='asrock-pwm-ipmi') +parser.add_argument('fanplusspeed', nargs='*', metavar='FAN:SPEED', + help='Fan to change the speed of, and the speed, separated by \':\'. Set to 0 for auto.') +#parser.add_argument('SPEED', type=int, nargs='+', +# help='Speed to set FAN to') +parser.add_argument('-i', '--info', action="store_true", default=False, + help='Read fan information') + + +args = parser.parse_args() +#print(args.info) +#print(args) +#print(args.fanplusspeed) +if args.info is False and args.fanplusspeed == []: + print("Nothing to do! See --help for usage.") + quit +if args.fanplusspeed != []: + for fanopt in args.fanplusspeed: + if str(fanopt.split(":"))[2:-2] == fanopt or int(fanopt.split(":")[0]) < 1 or int(fanopt.split(":")[0]) > MAXFAN: + print("Improper format!") + continue + fan, speed = fanopt.split(":") + run_cmd.setSpeed(int(fan), int(speed)) + if int(speed) == 0: + print("Set speed of FAN" + fan + " to Auto.") + else: + print("Set speed of FAN" + fan + " to " + speed + "%.") +if args.info is True: + print("\nRetrieving fan speeds...\n") + for line in run_cmd.getFanInfo(): + print(line) diff --git a/run_cmd.py b/run_cmd.py new file mode 100644 index 0000000..9f55939 --- /dev/null +++ b/run_cmd.py @@ -0,0 +1,48 @@ +#!/usr/bin/python3 +import subprocess as sp + +def setSpeed(fan, setspeed): + speeds, speedsRaw = getAllSpeeds() + speedcmd = ["ipmitool", "raw", "0x3a", "0x01"] + for speed in speedsRaw: + hexspeed = "0x" + hexspeed += str(speed) + speedcmd.append(hexspeed) + speedcmd.append("0x00") # append the mystery (unused) extra values + speedcmd.append("0x00") + speedcmd[fan + 3] = hex(setspeed) # index + 3 because the array already has the beginning of the command + #print(speeds) + sp.call(speedcmd) + +def getAllSpeeds(): + cmdoutput = sp.Popen(["ipmitool", "raw", "0x3a", "0x02"], stdout=sp.PIPE) + speedsRaw, err = cmdoutput.communicate() + speedsRaw = str(speedsRaw)[3:20].split() + speeds = [] + for speed in speedsRaw: + speeds.append(int(speed, 16)) + return speeds, speedsRaw + +def getSpeed(fan): + speeds, speedsRaw = getAllSpeeds() + return speeds[fan - 1] + +def getFanInfo(): + cmdoutput = sp.Popen(["ipmitool", "sdr"], stdout=sp.PIPE) + sensorinfo, err = cmdoutput.communicate() + #print(sensorinfo.splitlines()) + faninfo = [] + speeds, speedsRaw= getAllSpeeds() + for line in sensorinfo.splitlines(): + if "FAN" in str(line): + faninfo.append(str(line)) + for index, line in enumerate(faninfo): + faninfo[index] = str(line)[2:-1] + faninfo[index] += " | " + if speeds[index] == 0: + faninfo[index] += "Auto" + else: + faninfo[index] += str(speeds[index]) + faninfo[index] += "%" + #print(faninfo[index]) + return faninfo