- 2023 XX XX | >Since last year my R720's motherboard has been dying. Not sure if I'll get around to do finish the full 2.0 rework of this script since I won't really use it, but who knows, could gamble of me getting incredibly bored at some point. - 2022 07 11 | >minor update: Swapping CPU IDs fixing single CPU detection failure. - 2022 05 13 | >minor update: Adding custom failsafe value. Work In Progress branch for upcoming R5. - 2022 03 04 | >R4 patch1 : Adding new CPU Data source option, minor log corrections. - 2022 03 03 | >R4 Deltacheck CPU mode, DeltaA/E with Ambient check, failsafes, infinite CPU count. - 2022 02 27 | >minor update: adding IPMI-fail fail-safe. - 2022 02 27 | >R3 Auto CPUn/Ambient mode switching, logging, auto hexadecimal conversion, and more. - 2022 02 01 | >minor update: beginner friendly guide for beginner friendly Unraid and minor edits. - 2021 11 10 | >/!\ r2f : fixing a small, but quite critical fluke. - 2021 08 19 | >R2 update: now takes in account intake and exhaust temperatures! - 2021 04-08 | > various shit edits - 2021 04 14 | >R1 initial dump from my running environment & comments
```bash
#!/bin/bash
#the IP address of iDrac
IPMIHOST=192.168.0.42
#iDrac user
IPMIUSER=root
#iDrac password (calvin is the default password)
IPMIPW=calvin
#YOUR IPMI ENCRYPTION KEY
IPMIEK=0000000000000000000000000000000000000000
#Side note: you shouldn't ever store credentials in a script. Period. Here it's an example.
#I suggest you give a look at tools like https://github.com/plyint/encpass.sh
#Failsafe mode
#(Possible values being a number between 80 and 100, or "auto")
E_value="auto"
#IPMI IDs
CPUID0=0Eh
CPUID1=0Fh
CPUID2="0#h"
CPUID3="0#h"
AMBIENT_ID=04h
EXHAUST_ID=01h
#Non-IPMI data source for CPU:
NICPU_toggle=false
NICPUdatadump_command=(sensors -A)
NICPUdatadump_device="coretemp-isa-"
NICPUdatadump_device_num=4
NICPUdatadump_core=Core
NICPUdatadump_cut="-c16-18"
NICPUdatadump_offset=0
IPMIDATA_toggle=true
#Logtype:
#0 = Only Alerts
#1 = Fan speed output + alerts
#2 = Simple text + fanspeed output + alerts
#3 = Table + fanspeed output + alerts
Logtype=2
#There you basically define your fan curve.
TEMP_STEP0=30
FST0=2
TEMP_STEP1=35
FST1=6
TEMP_STEP2=40
FST2=8
TEMP_STEP3=50
FST3=10
TEMP_STEP4=60
FST4=12
TEMP_STEP5=75
FST5=20
#CPU fan governor type
TEMPgov=0
CPUdelta=15
#These values are used as steps for the intake temps.
AMBTEMP_STEP0=20
AMBTEMP_MOD_STEP0=0
AMBTEMP_noCPU_FS_STEP0=8
AMBTEMP_STEP1=21
AMBTEMP_MOD_STEP1=10
AMBTEMP_noCPU_FS_STEP1=15
AMBTEMP_STEP2=24
AMBTEMP_MOD_STEP2=15
AMBTEMP_noCPU_FS_STEP2=20
AMBTEMP_STEP3=26
AMBTEMP_MOD_STEP3=20
AMBTEMP_noCPU_FS_STEP3=30
MAX_MOD=69
EXHTEMP_MAX=65
#Ambient fan mode - Delta mode
AMBDeltaMode=true
DeltaR=3
#Log loop debug - true or false, logging of loops for debugging script
Logloop=false
#Looplog prefix
l="Loop -"
#Hexadecimal conversion and IPMI command into a function
ipmifanctl=(ipmitool -I lanplus -H "$IPMIHOST" -U "$IPMIUSER" -P "$IPMIPW" -y "$IPMIEK" raw 0x30 0x30)
function setfanspeed () {
TEMP_Check=$1
TEMP_STEP=$2
FS=$3
if [[ $FS == "auto" ]]; then
if [ "$Logtype" != 0 ] && [ "$4" -eq 0 ]; then
echo "> $TEMP_Check °C is higher or equal to $TEMP_STEP °C. Switching to automatic fan control"
fi
[ "$4" -eq 1 ] && echo "> ERROR : Keeping fans on auto as safety measure"
"${ipmifanctl[@]}" 0x01 0x01
exit $4
else
if [[ $FS -gt "100" ]]; then
FS=100
fi
HEX_value=$(printf '%#04x' "$FS")
if [ "$4" -eq 1 ]; then
echo "> ERROR : Keeping fans on high profile ($3 %) as safety measure"
elif [ "$Logtype" != 0 ]; then
echo "> $TEMP_Check °C is lower or equal to $TEMP_STEP °C. Switching to manual $FS % control"
fi
"${ipmifanctl[@]}" 0x01 0x00
"${ipmifanctl[@]}" 0x02 0xff "$HEX_value"
exit $4
fi
}
#Failsafe = Parameter check
re='^[0-9]+$'
ren='^[+-]?[0-9]+?$'
if [ "$Logloop" != false ] && [ "$Logloop" != true ]; then
echo "Logloop parameter invalid, must be true or false!"
setfanspeed XX XX "$E_value" 1
fi
if [ "$AMBDeltaMode" != false ] && [ "$AMBDeltaMode" != true ]; then
echo "AMBDeltaMode parameter invalid, must be true or false!"
setfanspeed XX XX "$E_value" 1
fi
if [[ "$DeltaR" =~ $ren ]]; then
if [ "$DeltaR" -le "0" ]; then
echo "DeltaR parameter invalid, must be greater than 0!"
setfanspeed XX XX "$E_value" 1
fi
else
echo "DeltaR parameter invalid, not a number!"
setfanspeed XX XX "$E_value" 1
fi
if [[ "$CPUdelta" =~ $ren ]]; then
if [ "$CPUdelta" -le "0" ]; then
echo "CPUdelta parameter invalid, must be greater than 0!"
setfanspeed XX XX "$E_value" 1
fi
else
echo "CPUdelta parameter invalid, not a number!"
setfanspeed XX XX "$E_value" 1
fi
if [ "$TEMPgov" != 1 ] && [ "$TEMPgov" != 0 ]; then
echo "TEMPgov parameter invalid, can only be 0 or 1!"
setfanspeed XX XX "$E_value" 1
fi
if [[ "$Logtype" =~ $ren ]]; then
if [ "$Logtype" -lt 0 ] || [ "$Logtype" -gt 3 ]; then
echo "Logtype parameter invalid, must be in 0-3 range!"
setfanspeed XX XX "$E_value" 1
fi
else
echo "Logtype parameter invalid, not a number!"
setfanspeed XX XX "$E_value" 1
fi
if [[ "$EXHTEMP_MAX" =~ $ren ]]; then
if [ "$EXHTEMP_MAX" -lt 0 ]; then
echo "EXHTEMP_MAX parameter invalid, can't be negative!"
setfanspeed XX XX "$E_value" 1
fi
else
echo "EXHTEMP_MAX parameter invalid, not a number!"
setfanspeed XX XX "$E_value" 1
fi
if [[ $MAX_MOD =~ $ren ]]; then
if [ "$MAX_MOD" -lt 0 ]; then
echo "MAX_MOD parameter invalid, can't be negative!"
setfanspeed XX XX "$E_value" 1
fi
else
echo "MAX_MOD parameter invalid, not a number!"
setfanspeed XX XX "$E_value" 1
fi
if [[ "$E_value" =~ $ren ]]; then
if [ "$E_value" -lt 80 ]; then
echo "E_value parameter invalid, can't be negative or lower than 80"
E_value="auto"
fi
if [ "$E_value" -gt 100 ]; then
echo "E_value parameter invalid, can't be greater than 100"
E_value="auto"
fi
elif [ "$E_value" != "auto" ]; then
echo "E_value parameter invalid, not a number!"
E_value="auto"
fi
#Counting CPU Fan speed steps and setting max value
if $Logloop ; then
echo "$l New loop => Counting CPU Fan speed steps and setting max value"
fi
for ((i=0; i>=0 ; i++))
do
inloopstep="TEMP_STEP$i"
inloopspeed="FST$i"
if [[ ! -z "${!inloopspeed}" ]] && [[ ! -z "${!inloopstep}" ]]; then
if $Logloop ; then
echo "$l CPU Temperature step n°$i = ${!inloopstep}°C"
echo "$l Fan speed step n°$i = ${!inloopspeed}%"
fi
if ! [[ "${!inloopstep}" =~ $ren ]]; then
echo "Butterfinger failsafe: CPU Temperature step n°$i isn't a number!"
setfanspeed XX XX "$E_value" 1
fi
if [[ "${!inloopspeed}" =~ $ren ]]; then
if [[ "${!inloopspeed}" -lt 0 ]]; then
echo "Butterfinger failsafe: Fan speed step n°$i is negative!"
setfanspeed XX XX "$E_value" 1
fi
else
echo "Butterfinger failsafe: Fan speed step n°$i isn't a number!"
setfanspeed XX XX "$E_value" 1
fi
else
inloopmaxstep="TEMP_STEP$((i-1))"
if [ $((i-1)) -le 0 ]; then
echo "Butterfinger failsafe: no CPU stepping found!!"
setfanspeed XX XX "$E_value" 1
fi
MAXTEMP="${!inloopmaxstep}"
TEMP_STEP_COUNT=$i
if $Logloop ; then
echo "$l CPU temperature step count = $i"
echo "$l CPU max temperature to auto mode = $MAXTEMP°C"
echo "$l CPU Temp Steps counting = stop"
fi
break
fi
done
#Counting Ambiant Fan speed and MOD steps and setting max value
if $Logloop ; then
echo "$l New loop => Counting Ambiant Fan speed and MOD steps and setting max value"
fi
for ((i=0; i>=0 ; i++))
do
inloopstep="AMBTEMP_STEP$i"
inloopspeed="AMBTEMP_noCPU_FS_STEP$i"
inloopmod="AMBTEMP_MOD_STEP$i"
if [[ ! -z "${!inloopspeed}" ]] && [[ ! -z "${!inloopmod}" ]] && [[ ! -z "${!inloopstep}" ]]; then
if $Logloop ; then
echo "$l Ambient temperature step n°$i = ${!inloopstep}°C"
echo "$l Ambient modifier for CPU temp step n°$i = ${!inloopmod}°C"
echo "$l Ambient NO CPU fan speed step n°$i = ${!inloopspeed}%"
fi
if ! [[ "${!inloopstep}" =~ $ren ]]; then
echo "Butterfinger failsafe: Ambient temperature step n°$i isn't a number!"
setfanspeed XX XX "$E_value" 1
fi
if [[ "${!inloopmod}" =~ $ren ]]; then
if [[ "${!inloopmod}" -lt 0 ]]; then
echo "Beware: Ambient modifier for CPU temp step n°$i is negative!"
echo "Proceeding..."
fi
else
echo "Butterfinger failsafe: Ambient modifier for CPU temp step n°$i isn't a number!"
setfanspeed XX XX "$E_value" 1
fi
if [[ "${!inloopspeed}" =~ $ren ]]; then
if [[ "${!inloopspeed}" -lt 0 ]]; then
echo "Butterfinger failsafe: Ambient NO CPU fan speed step n°$i is negative!"
setfanspeed XX XX "$E_value" 1
fi
else
echo "Butterfinger failsafe: Ambient NO CPU fan speed step n°$i isn't a number!"
setfanspeed XX XX "$E_value" 1
fi
else
inloopmaxstep="AMBTEMP_STEP$((i-1))"
if [ $((i-1)) -le 0 ]; then
echo "Butterfinger failsafe: no Ambient stepping found!!"
setfanspeed XX XX "$E_value" 1
fi
AMBTEMP_MAX="${!inloopmaxstep}"
AMB_STEP_COUNT=$i
if $Logloop ; then
echo "$l Ambient temperature step count = $i"
echo "$l Ambient max temperature to max mod = $AMBTEMP_MAX°C"
echo "$l CPU Ambiant Steps counting = stop"
fi
break
fi
done
#Pulling temperature data from IPMI
if $IPMIDATA_toggle ; then
IPMIPULLDATA=$(ipmitool -I lanplus -H $IPMIHOST -U $IPMIUSER -P $IPMIPW -y $IPMIEK sdr type temperature)
DATADUMP=$(echo "$IPMIPULLDATA")
if [ -z "$DATADUMP" ]; then
echo "No data was pulled from IPMI"
setfanspeed XX XX "$E_value" 1
else
AUTOEM=false
fi
else
if $NICPU_toggle ; then
AUTOEM=false
else
echo "Both IPMI data and Non-IPMI-CPU data are toggled off"
setfanspeed XX XX "$E_value" 1
fi
fi
#Parsing CPU Temp data into values to be later checked in count, continuity and value validity.
if $NICPU_toggle ; then
echo "Non-IPMI data source. An error can be thrown without incidence."
if $Logloop ; then
echo "$l New loop => Pulling data dynamically from Non-IPMI source"
fi
for ((j=0; j>=0 ; j++))
do
[ -z "$socketcount" ] && socketcount=0
datadump=$("$NICPUdatadump_command" "$NICPUdatadump_device$(printf "%0"$NICPUdatadump_device_num"d" "$socketcount")")
if [[ ! -z $datadump ]]; then
if $Logloop ; then
echo "$l Detected CPU socket $socketcount !!"
echo "$l New loop => Parsing CPU Core data"
fi
socketcount=$((socketcount+1))
for ((i=0; i>=0 ; i++))
do
[ -z "$corecount" ] && corecount=0
Corecountloop_data=$( echo "$datadump" | grep -A 0 "$NICPUdatadump_core $i"| cut "$NICPUdatadump_cut")
if [[ ! -z $Corecountloop_data ]]; then
declare CPUTEMP$corecount="$((Corecountloop_data+NICPUdatadump_offset))"
if $Logloop ; then
echo "$l Defining CPUTEMP$corecount with value : $((CPUTEMP$corecount))"
fi
corecount=$((corecount+1))
else
if $Logloop ; then
echo "$l CPU Core data parsing on CPU Socket $((socketcount-1)) = stop"
fi
break
fi
done
else
echo "Non-IPMI detection : done."
if $Logloop ; then
echo "$l Result : $corecount Total CPU temperature sources added."
echo "$l CPU Data parsing from Non-IPMI source = stop"
fi
break
fi
done
else
CPUTEMP0=$(echo "$DATADUMP" |grep "$CPUID0" |grep degrees |grep -Po '\d{2}' | tail -1)
CPUTEMP1=$(echo "$DATADUMP" |grep "$CPUID1" |grep degrees |grep -Po '\d{2}' | tail -1)
CPUTEMP2=$(echo "$DATADUMP" |grep "$CPUID2" |grep degrees |grep -Po '\d{2}' | tail -1)
CPUTEMP3=$(echo "$DATADUMP" |grep "$CPUID3" |grep degrees |grep -Po '\d{2}' | tail -1)
fi
#CPU counting
if [ -z "$CPUTEMP0" ]; then
CPUcount=0
else
if [[ ! -z "$CPUTEMP0" ]]; then #Infinite CPU number adding, if you pull individual CPU cores from lm-sensors or something
for ((i=0; i>=0 ; i++))
do
CPUcountloop="CPUTEMP$i"
if [[ ! -z "${!CPUcountloop}" ]]; then
if $Logloop ; then
echo "$l CPU detection = CPU$i detected / Value = ${!CPUcountloop}"
fi
if ! [[ "${!CPUcountloop}" =~ $re ]] ; then
echo "!!error: Reading is not a number or negative!!"
echo "Falling back to ambient mode..."
CPUcount=0
break
fi
currcputemp="${!CPUcountloop}"
CPUcount=$((i+1))
TEMPadd=$((TEMPadd+currcputemp))
else
if [[ $((CPUcount % 2)) -eq 0 ]] || [[ $CPUcount -eq 1 ]]; then
if $Logloop ; then
if [ "$CPUcount" -eq "1" ]; then
echo "$l CPU count : $CPUcount CPU detected!"
else
echo "$l CPU count is even : $CPUcount CPU detected!"
fi
echo "$l CPU counting = stop"
fi
CPUn=$((TEMPadd/CPUcount))
break
else
CPUcount=0
echo "CPU count is odd, please check your configuration";
echo "Falling back to ambient mode..."
break
fi
fi
done
fi
fi
#CPU Find lowest and highest CPU temps
if [ "$CPUcount" -gt 1 ]; then
if $Logloop ; then
echo "$l New loop => Finding highest and lowest CPU temps"
fi
for ((i=0; i
Instead of boring you with text, here's the alphabet of them:
Field Name | Mandatory | Allowed Values | Allowed Special Characters |
------ | ------- | ------- | ------- |
Minutes | YES | 0 - 59 | , - \* / |
Hours | YES | 0 - 23 | , - \* / |
Day of month | YES | 1 - 31 | , - \* ? / L W |
Month | YES | 1 - 12 (representing Jan - Dec), JAN - DEC (case-insensitive), JANUARY - DECEMBER (case-insensitive) | , - \* / |
Day of week | YES | 0 - 6, 7 (representing Sun - Sat and Sun again), SUN - SAT (case-insensitive), SUNDAY - SATURDAY (case-insensitive) | , - \* ? / L # |
Year | NO | empty or 1970-2099 | , - \* / |
And here a cheatsheet, you'll probably find what you're looking for in it, or be able to make it from it.
Cron Expression examples | Meaning |
--------- | --------- |
\* \* \* \* \* 2022 | Execute a cron job every minute during the year 2022 |
\* \* \* \* \* | Execute a cron job every minute |
\*/5 \* \* \* \* | Execute a cron job every 5 minutes |
0 \* \* \* \* | Execute a cron job every hour |
0 12 \* \* \* | Fire at 12:00 PM (noon) every day |
15 10 \* \* \* | Fire at 10:15 AM every day |
15 10 \* \* ? | Fire at 10:15 AM every day |
15 10 \* \* \* 2022-2024 | Fire at 10:15 AM every day during the years 2022, 2023 and 2024 |
\* 14 \* \* \* | Fire every minute starting at 2:00 PM and ending at 2:59 PM, every day |
0/5 14,18 \* \* \* | Fire every 5 minutes starting at 2:00 PM and ending at 2:55 PM, AND fire every 5 minutes starting at 6:00 PM and ending at 6:55 PM, every day |
0-5 14 \* \* \* | Fire every minute starting at 2:00 PM and ending at 2:05 PM, every day |
10,44 14 \* 3 3 | Fire at 2:10 PM and at 2:44 PM every Wednesday in the month of March. |
15 10 \* \* 1-5 | Fire at 10:15 AM every Monday, Tuesday, Wednesday, Thursday and Friday |
15 10 15 \* \* | Fire at 10:15 AM on the 15th day of every month |
15 10 L \* \* | Fire at 10:15 AM on the last day of every month |
15 10 \* \* 5L | Fire at 10:15 AM on the last Friday of every month |
15 10 \* \* 5#3 | Fire at 10:15 AM on the third Friday of every month |
0 12 1/5 \* \* | Fire at 12:00 PM (noon) every 5 days every month, starting on the first day of the month. |
11 11 11 11 \* | Fire every November 11th at 11:11 AM. |
11 11 11 11 \* 2022 | Fire at 11:11 AM on November 11th in the year 2022. |
0 0 \* \* 3 | Fire at midnight of each Wednesday. |
0 0 1,2 \* \* | Fire at midnight of 1st, 2nd day of each month |
0 0 1,2 \* 3 | Fire at midnight of 1st, 2nd day of each month, and each Wednesday. |
Everything you need should be here