├── temppull.sh ├── README.md ├── fancontrol.sh └── guide └── unraid.md /temppull.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #the IP address of your target iDrac 3 | IPMIHOST=192.168.0.42 4 | 5 | #iDrac user 6 | IPMIUSER=root 7 | 8 | #iDrac password (calvin is the default password) 9 | IPMIPW=calvin 10 | 11 | #YOUR IPMI ENCRYPTION KEY 12 | IPMIEK=0000000000000000000000000000000000000000 13 | 14 | #Side note: you shouldn't ever store credentials in a script. Period. Here it's an example. 15 | #I suggest you give a look at tools like https://github.com/plyint/encpass.sh 16 | 17 | ipmitool -I lanplus -H $IPMIHOST -U $IPMIUSER -P $IPMIPW -y $IPMIEK sdr type temperature 18 | 19 | # Should return something like that: 20 | #Inlet Temp | 04h | ok | 7.1 | 19 degrees C 21 | #Exhaust Temp | 01h | ok | 7.1 | 36 degrees C 22 | #Temp | 0Eh | ok | 3.1 | 41 degrees C 23 | #Temp | 0Fh | ok | 3.2 | 40 degrees C 24 | 25 | #It lets you know the "id" to grep for in the fancontrol.sh script. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PowerEdge-shutup 2 |
3 | 4 | [ Changelog ] 5 | 6 |

7 | 8 | - 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. 9 | - 2022 07 11 | >minor update: Swapping CPU IDs fixing single CPU detection failure. 10 | - 2022 05 13 | >minor update: Adding custom failsafe value. Work In Progress branch for upcoming R5. 11 | - 2022 03 04 | >R4 patch1 : Adding new CPU Data source option, minor log corrections. 12 | - 2022 03 03 | >R4 Deltacheck CPU mode, DeltaA/E with Ambient check, failsafes, infinite CPU count. 13 | - 2022 02 27 | >minor update: adding IPMI-fail fail-safe. 14 | - 2022 02 27 | >R3 Auto CPUn/Ambient mode switching, logging, auto hexadecimal conversion, and more. 15 | - 2022 02 01 | >minor update: beginner friendly guide for beginner friendly Unraid and minor edits. 16 | - 2021 11 10 | >/!\ r2f : fixing a small, but quite critical fluke. 17 | - 2021 08 19 | >R2 update: now takes in account intake and exhaust temperatures! 18 | - 2021 04-08 | > various shit edits 19 | - 2021 04 14 | >R1 initial dump from my running environment & comments 20 |

21 |
22 | 23 | ## Requirements 24 | - iDrac Entreprise (afaik it won't work with express) 25 | - [IPMItool](https://github.com/ipmitool/ipmitool) 26 | - G11*, G12, G13/G14** Dell Poweredge server 27 | 28 | *See also [me/PowerEdge-IPMItools](https://github.com/White-Raven/PowerEdge-IPMItools) for more applications and resources.* 29 | 30 | ## What about it 31 | Does what it says, depending on your environmental constraints, it might let you make your servers whisper-quiet, which 32 | - for an office or small business, might let you have a functionnal and capable server room without ACTUALLY having to have a proper server room with space and soundproofing, 33 | - and for a small home setup or homelab might be a life and sleep saver 34 | 35 | 36 | It's the "raw script" that can be almost used as is in a cron job, you're obviously free to use it and modify it. 37 | 38 | I'ld just appreciate that if you itterate on it or send it somewhere, you could else reference the source or commit it here under a new file name*, or fork it. 39 | (*to keep the original as-is, as an example of lazyness) 40 | 41 | As of what you can do with these great little commands... well.. 42 | 43 | You can run them as a cron job, or create a loop, but point is, lets you set your fan speed to bare minimum RPM depending of how warm or cool is your room and how hard you hit your servers. 44 | 45 | 46 | 47 | ## What if Linux hangs, and my server stops adjusting its fan speed? 48 | 49 | For the sake of simplicity in this repo, I won't dive into the whole mess of scripts in own setup, BUT 50 | but the script actually doesn't run on the server itself but on a Pi2 of which the sole purpose is to manage IPMI enabled machines' cooling, be a server to distribute UPS data/status, and answer pings. 51 | 52 | - If the Pi hangs, when the server pings it, it won't get an answer, and the server will switch itself on auto fan mode, which is BIOS/Firmware managed. 53 | The server can also change its curve when running some tasks/loads, in fact it goes through NetCat to tell the pie "now I need that" and the pie switches to an other set of fan curve for that server. 54 | 55 | - If the server hangs... well it hanged, no biggie, the Pi keeps its cooling managed, since the IPMI would still pull accurate temps readings and would still answer the phone when the Pi tells it to do some stuff. 56 | So yeah, bit more convoluted but it allows the servers to not be needing to be stable indefinitely to not risk to catch fire. 57 | 58 | I simply haven't included all that because it's a lot more cumbersome and needs to be kinda adapted to each setup and set of needs, and I'm clearly not going to do that. 59 | 60 | 61 | ---------------- 62 | *_G11 seem to lack CPU temps in the data you can pull and rely on. Beware of the comments about it in the script, look for Delta A/E mode and Ambient mode._ 63 | 64 | **_I was told it is also working on iDrac8 (G13) and iDrac9 (G14), but that beyond iDrac update 3.30.30.30 for G13 and 3.34.34.34 for G14, Dell has modified/removed the ability to control the fans via IMPI._ 65 | -------------------------------------------------------------------------------- /fancontrol.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #WIP - sum 4 | #>Present data sources: 5 | #-IPMI CPU 6 | #-IPMI Ambient/INLET 7 | #-IPMI EXHAUST 8 | #-lm-sensors CPU (doable through SSH with a passwordless secure auth) 9 | #Todo 10 | #-Add select drive temps (using smartctl or other) 11 | 12 | #>Present modes: 13 | #-'default' - CPU temps (average/highest governor) + AE modifier + CPU temp delta safeguard for average temp governor 14 | #-'ambient-default' - DELTA AE, drives fan speed from calculation using the delta of temperature between inlet and exhaust and uses it instead of CPU temps, 15 | # then + AE modifiers as in default mode. Is the default fall back method for missing CPU temp data. 16 | #-'ambient-legacy' - ignores CPU temps and curves, drives fan speed from AE temps only following ambient specific curve, crude fall back method. 17 | #Todo 18 | #- create additional parameters for drive temps, seperated in two famillies (either for front and back, or for HDD and SSD... or maybe 3 famillies/groups), 19 | # with each a 3 step curve of modifiers. 20 | #- turn the bottom mess into a single dynamic function that each mode can call to do the job to facilitate adding new modes/profiles 21 | #- ?rewrite to use arrays instead of variables to store fan curves, and possibly from CPU temp data too? 22 | #- add alternative failsafe - 100% instead of auto 23 | 24 | 25 | #the IP address of iDrac 26 | IPMIHOST=192.168.0.42 27 | 28 | #iDrac user 29 | IPMIUSER=root 30 | 31 | #iDrac password (calvin is the default password) 32 | IPMIPW=calvin 33 | 34 | #YOUR IPMI ENCRYPTION KEY - a big string of zeros is the default, and by default isn't mandatory to be specified. 35 | #You can modify it, for example in idrac7's webinterface under iDRAC Settings>Network , in the IPMI Settings section. 36 | IPMIEK=0000000000000000000000000000000000000000 37 | 38 | #Side note: you shouldn't ever store credentials in a script. Period. Here it's an example. 39 | #I suggest you give a look at tools like https://github.com/plyint/encpass.sh 40 | 41 | #Failsafe mode 42 | #(Possible values being a number between 80 and 100, or "auto") 43 | E_value="auto" 44 | 45 | #IPMI IDs 46 | #/!\ IMPORTANT - the "0Eh"(CPU0),"0Fh"(CPU1), "04h"(inlet) and "01h"(exhaust) values are the proper ones for MY R720, maybe not for your server. 47 | #To check your values, use the "temppull.sh" script. 48 | CPUID0=0Eh 49 | CPUID1=0Fh 50 | CPUID2="0#h" 51 | CPUID3="0#h" 52 | #Yes, there are 4 CPU servers in the poweredge line. I don't have one, so I left 0#h values for these. As said above, modify accordingly. 53 | AMBIENT_ID=04h 54 | EXHAUST_ID=01h 55 | #------------------------------------------------- 56 | #For G11 servers and some other unlucky ones: 57 | #I was made aware that people on iDrac6, notably the R610, reported only having access to ambient temperature, and not CPU temps neither exhaust temps. 58 | #Keep in mind though that this method is way less indicative of CPU temps. 59 | #If your load isn't consistent enough to properly profile your server, it might lead to overheating. 60 | #In that case, you will have to do with only Ambient temp to define your fan speed, or rely on other sources for CPU temps. 61 | #------------------------------------------------- 62 | 63 | #Non-IPMI data source for CPU: 64 | NICPU_toggle=false 65 | #Command, or you way to pull data per device (here, using coretemp driver's coretemp-isa-#### ) 66 | NICPUdatadump_command=(sensors -A) 67 | #Top level Device scan 68 | NICPUdatadump_device="coretemp-isa-" 69 | #Top level device count of numbers. For example coretemp-isa-0000 and coretemp-isa-0001 on a R720, coretemp-isa-#### would be 4. 70 | NICPUdatadump_device_num=4 71 | #"Core #" label for grep 72 | NICPUdatadump_core=Core 73 | #Where to cut in the line 74 | NICPUdatadump_cut="-c16-18" 75 | #Temperature offset : Some drivers report higher or lower temps than real world. Your offset must be an integer (ex: 0, -5, 12) 76 | NICPUdatadump_offset=0 77 | #IPMI data can be still used for Ambient and Exhaust data, but if you want to ignore pulling IPMI data all together, you can toggle it to false. 78 | IPMIDATA_toggle=true 79 | 80 | #Logtype: 81 | #0 = Only Alerts 82 | #1 = Fan speed output + alerts 83 | #2 = Simple text + fanspeed output + alerts 84 | #3 = Table + fanspeed output + alerts 85 | Logtype=2 86 | 87 | #There you basically define your fan curve. For each fan step temperature (in °C) you define which fan speed it uses when it's equal or under this temp. 88 | #For example: until it reaches step0 at 30°C, it runs at 2% fan speed, if it's above 30°C and under 35°C, it will run at 6% fan speed, ect 89 | #Fan speed values are to be set as for each step in the FST# value, in % between 0 and 100. 90 | TEMP_STEP0=30 91 | FST0=2 92 | TEMP_STEP1=35 93 | FST1=6 94 | TEMP_STEP2=40 95 | FST2=8 96 | TEMP_STEP3=50 97 | FST3=10 98 | TEMP_STEP4=60 99 | FST4=12 100 | TEMP_STEP5=75 101 | FST5=20 102 | #CPU fan governor type - keep in mind, with IPMI it's CPUs, not cores. 103 | #0 = uses average CPU temperature accross CPUs 104 | #1 = uses highest CPU temperature 105 | TEMPgov=0 106 | #Maximum allowed delta in TEMPgov0. If exceeded, switches profile to highest value. 107 | CPUdelta=15 108 | 109 | #These values are used as steps for the intake temps. 110 | #If Ambient temp is within range of $AMBTEMP_STEP#, it inflates the CPUs' temp average by AMBTEMP_STEP#_MOD when checked against TEMP_STEP#s. 111 | #If Ambient temp is above $AMBTEMP_MAX, which is step 4, a temp modifier of 69 should be well enough to make the script select auto-fan mode. 112 | #AMBTEMP_STEPX_noCPU_Fanspeed : Some servers don't report their CPU temps. In that case Fan speed can only be adjusted using Ambient temperature. 113 | #In case of lack of CPU temps in IPMI, Fan speed values are to be defined here as for each step in the AMBTEMP_noCPU_FS_STEP# value, in % between 0 and 100. 114 | 115 | AMBTEMP_STEP0=20 116 | AMBTEMP_MOD_STEP0=0 117 | AMBTEMP_noCPU_FS_STEP0=8 118 | 119 | AMBTEMP_STEP1=21 120 | AMBTEMP_MOD_STEP1=10 121 | AMBTEMP_noCPU_FS_STEP1=15 122 | 123 | AMBTEMP_STEP2=24 124 | AMBTEMP_MOD_STEP2=15 125 | AMBTEMP_noCPU_FS_STEP2=20 126 | 127 | AMBTEMP_STEP3=26 128 | AMBTEMP_MOD_STEP3=20 129 | AMBTEMP_noCPU_FS_STEP3=30 130 | 131 | MAX_MOD=69 132 | 133 | #If your exhaust temp is reaching 65°C, you've been cooking your server. It needs the woosh. 134 | EXHTEMP_MAX=65 135 | 136 | #Ambient fan mode - Delta mode 137 | # => Fall back method when no CPU readings are available. 138 | #Delta mode uses the temperature difference (delta) between intake (ambient) and exhaust to control fan-speed. 139 | #To set the Deltatemp and fan speeds for each, use the parameters for the CPU fan mode profile. 140 | #By default, for safety, the temperature is divided by 3, so for the default first step, 30°C of CPU temp, the delta value is 10°C. 141 | #To modify the ratio, modify the value DeltaR. Default is 3, no ratio is 1. 142 | AMBDeltaMode=true 143 | DeltaR=3 144 | #If in delta mode, ambient temp would warrant an higher fan speed according to the ambient profile, it will use ambient profile's 145 | #recommendation as to protect your hardware from running with a low delta but with an overall too high temperature. 146 | #If you lack Exhaust temps, there is a fallback method to solely ambient. 147 | #If you lack Intake temps, the fall back method swaps Exhaust to intake temps, you need to modify Ambient temp stepping, 148 | #because the values are too low, even if they are safe, since fans would run higher than you might want in that case. 149 | #If you lack both readings, it falls back to auto-fan mode. 150 | 151 | #Log loop debug - true or false, logging of loops for debugging script 152 | Logloop=false 153 | 154 | #Looplog prefix 155 | l="Loop -" 156 | 157 | #IPMI Commands to set fan speeds. 158 | # "ipmitool -I lanplus -H $IPMIHOST -U $IPMIUSER -P $IPMIPW -y $IPMIEK raw 0x30 0x30 0x01 0x01" gives back to the server the right to automate fan speed 159 | # "ipmitool -I lanplus -H $IPMIHOST -U $IPMIUSER -P $IPMIPW -y $IPMIEK raw 0x30 0x30 0x01 0x00" stops the server from adjusting fanspeed by itself, no matter the temp 160 | # "ipmitool -I lanplus -H $IPMIHOST -U $IPMIUSER -P $IPMIPW -y $IPMIEK raw 0x30 0x30 0x02 0xff 0x"hex value 00-64" lets you define fan speed 161 | 162 | #Extra Curves and data sources 163 | 164 | #Hexadecimal conversion and IPMI command into a function 165 | ipmifanctl=(ipmitool -I lanplus -H "$IPMIHOST" -U "$IPMIUSER" -P "$IPMIPW" -y "$IPMIEK" raw 0x30 0x30) 166 | function setfanspeed () { 167 | TEMP_Check=$1 168 | TEMP_STEP=$2 169 | FS=$3 170 | if [[ $FS == "auto" ]]; then 171 | if [ "$Logtype" != 0 ] && [ "$4" -eq 0 ]; then 172 | echo "> $TEMP_Check °C is higher or equal to $TEMP_STEP °C. Switching to automatic fan control" 173 | fi 174 | [ "$4" -eq 1 ] && echo "> ERROR : Keeping fans on auto as safety measure" 175 | "${ipmifanctl[@]}" 0x01 0x01 176 | exit $4 177 | else 178 | if [[ $FS -gt "100" ]]; then 179 | FS=100 180 | fi 181 | HEX_value=$(printf '%#04x' "$FS") 182 | if [ "$4" -eq 1 ]; then 183 | echo "> ERROR : Keeping fans on high profile ($3 %) as safety measure" 184 | elif [ "$Logtype" != 0 ]; then 185 | echo "> $TEMP_Check °C is lower or equal to $TEMP_STEP °C. Switching to manual $FS % control" 186 | fi 187 | "${ipmifanctl[@]}" 0x01 0x00 188 | "${ipmifanctl[@]}" 0x02 0xff "$HEX_value" 189 | exit $4 190 | fi 191 | } 192 | #Failsafe = Parameter check 193 | re='^[0-9]+$' 194 | ren='^[+-]?[0-9]+?$' 195 | if [ "$Logloop" != false ] && [ "$Logloop" != true ]; then 196 | echo "Logloop parameter invalid, must be true or false!" 197 | setfanspeed XX XX "$E_value" 1 198 | fi 199 | if [ "$AMBDeltaMode" != false ] && [ "$AMBDeltaMode" != true ]; then 200 | echo "AMBDeltaMode parameter invalid, must be true or false!" 201 | setfanspeed XX XX "$E_value" 1 202 | fi 203 | if [[ "$DeltaR" =~ $ren ]]; then 204 | if [ "$DeltaR" -le "0" ]; then 205 | echo "DeltaR parameter invalid, must be greater than 0!" 206 | setfanspeed XX XX "$E_value" 1 207 | fi 208 | else 209 | echo "DeltaR parameter invalid, not a number!" 210 | setfanspeed XX XX "$E_value" 1 211 | fi 212 | if [[ "$CPUdelta" =~ $ren ]]; then 213 | if [ "$CPUdelta" -le "0" ]; then 214 | echo "CPUdelta parameter invalid, must be greater than 0!" 215 | setfanspeed XX XX "$E_value" 1 216 | fi 217 | else 218 | echo "CPUdelta parameter invalid, not a number!" 219 | setfanspeed XX XX "$E_value" 1 220 | fi 221 | if [ "$TEMPgov" != 1 ] && [ "$TEMPgov" != 0 ]; then 222 | echo "TEMPgov parameter invalid, can only be 0 or 1!" 223 | setfanspeed XX XX "$E_value" 1 224 | fi 225 | if [[ "$Logtype" =~ $ren ]]; then 226 | if [ "$Logtype" -lt 0 ] || [ "$Logtype" -gt 3 ]; then 227 | echo "Logtype parameter invalid, must be in 0-3 range!" 228 | setfanspeed XX XX "$E_value" 1 229 | fi 230 | else 231 | echo "Logtype parameter invalid, not a number!" 232 | setfanspeed XX XX "$E_value" 1 233 | fi 234 | if [[ "$EXHTEMP_MAX" =~ $ren ]]; then 235 | if [ "$EXHTEMP_MAX" -lt 0 ]; then 236 | echo "EXHTEMP_MAX parameter invalid, can't be negative!" 237 | setfanspeed XX XX "$E_value" 1 238 | fi 239 | else 240 | echo "EXHTEMP_MAX parameter invalid, not a number!" 241 | setfanspeed XX XX "$E_value" 1 242 | fi 243 | if [[ $MAX_MOD =~ $ren ]]; then 244 | if [ "$MAX_MOD" -lt 0 ]; then 245 | echo "MAX_MOD parameter invalid, can't be negative!" 246 | setfanspeed XX XX "$E_value" 1 247 | fi 248 | else 249 | echo "MAX_MOD parameter invalid, not a number!" 250 | setfanspeed XX XX "$E_value" 1 251 | fi 252 | if [[ "$E_value" =~ $ren ]]; then 253 | if [ "$E_value" -lt 80 ]; then 254 | echo "E_value parameter invalid, can't be negative or lower than 80" 255 | E_value="auto" 256 | fi 257 | if [ "$E_value" -gt 100 ]; then 258 | echo "E_value parameter invalid, can't be greater than 100" 259 | E_value="auto" 260 | fi 261 | elif [ "$E_value" != "auto" ]; then 262 | echo "E_value parameter invalid, not a number!" 263 | E_value="auto" 264 | fi 265 | #Counting CPU Fan speed steps and setting max value 266 | if $Logloop ; then 267 | echo "$l New loop => Counting CPU Fan speed steps and setting max value" 268 | fi 269 | for ((i=0; i>=0 ; i++)) 270 | do 271 | inloopstep="TEMP_STEP$i" 272 | inloopspeed="FST$i" 273 | if [[ ! -z "${!inloopspeed}" ]] && [[ ! -z "${!inloopstep}" ]]; then 274 | if $Logloop ; then 275 | echo "$l CPU Temperature step n°$i = ${!inloopstep}°C" 276 | echo "$l Fan speed step n°$i = ${!inloopspeed}%" 277 | fi 278 | if ! [[ "${!inloopstep}" =~ $ren ]]; then 279 | echo "Butterfinger failsafe: CPU Temperature step n°$i isn't a number!" 280 | setfanspeed XX XX "$E_value" 1 281 | fi 282 | if [[ "${!inloopspeed}" =~ $ren ]]; then 283 | if [[ "${!inloopspeed}" -lt 0 ]]; then 284 | echo "Butterfinger failsafe: Fan speed step n°$i is negative!" 285 | setfanspeed XX XX "$E_value" 1 286 | fi 287 | 288 | else 289 | echo "Butterfinger failsafe: Fan speed step n°$i isn't a number!" 290 | setfanspeed XX XX "$E_value" 1 291 | fi 292 | else 293 | inloopmaxstep="TEMP_STEP$((i-1))" 294 | if [ $((i-1)) -le 0 ]; then 295 | echo "Butterfinger failsafe: no CPU stepping found!!" 296 | setfanspeed XX XX "$E_value" 1 297 | fi 298 | MAXTEMP="${!inloopmaxstep}" 299 | TEMP_STEP_COUNT=$i 300 | if $Logloop ; then 301 | echo "$l CPU temperature step count = $i" 302 | echo "$l CPU max temperature to auto mode = $MAXTEMP°C" 303 | echo "$l CPU Temp Steps counting = stop" 304 | fi 305 | break 306 | fi 307 | done 308 | #Counting Ambiant Fan speed and MOD steps and setting max value 309 | if $Logloop ; then 310 | echo "$l New loop => Counting Ambiant Fan speed and MOD steps and setting max value" 311 | fi 312 | for ((i=0; i>=0 ; i++)) 313 | do 314 | inloopstep="AMBTEMP_STEP$i" 315 | inloopspeed="AMBTEMP_noCPU_FS_STEP$i" 316 | inloopmod="AMBTEMP_MOD_STEP$i" 317 | if [[ ! -z "${!inloopspeed}" ]] && [[ ! -z "${!inloopmod}" ]] && [[ ! -z "${!inloopstep}" ]]; then 318 | if $Logloop ; then 319 | echo "$l Ambient temperature step n°$i = ${!inloopstep}°C" 320 | echo "$l Ambient modifier for CPU temp step n°$i = ${!inloopmod}°C" 321 | echo "$l Ambient NO CPU fan speed step n°$i = ${!inloopspeed}%" 322 | fi 323 | if ! [[ "${!inloopstep}" =~ $ren ]]; then 324 | echo "Butterfinger failsafe: Ambient temperature step n°$i isn't a number!" 325 | setfanspeed XX XX "$E_value" 1 326 | fi 327 | if [[ "${!inloopmod}" =~ $ren ]]; then 328 | if [[ "${!inloopmod}" -lt 0 ]]; then 329 | echo "Beware: Ambient modifier for CPU temp step n°$i is negative!" 330 | echo "Proceeding..." 331 | fi 332 | 333 | else 334 | echo "Butterfinger failsafe: Ambient modifier for CPU temp step n°$i isn't a number!" 335 | setfanspeed XX XX "$E_value" 1 336 | fi 337 | if [[ "${!inloopspeed}" =~ $ren ]]; then 338 | if [[ "${!inloopspeed}" -lt 0 ]]; then 339 | echo "Butterfinger failsafe: Ambient NO CPU fan speed step n°$i is negative!" 340 | setfanspeed XX XX "$E_value" 1 341 | fi 342 | 343 | else 344 | echo "Butterfinger failsafe: Ambient NO CPU fan speed step n°$i isn't a number!" 345 | setfanspeed XX XX "$E_value" 1 346 | fi 347 | else 348 | inloopmaxstep="AMBTEMP_STEP$((i-1))" 349 | if [ $((i-1)) -le 0 ]; then 350 | echo "Butterfinger failsafe: no Ambient stepping found!!" 351 | setfanspeed XX XX "$E_value" 1 352 | fi 353 | AMBTEMP_MAX="${!inloopmaxstep}" 354 | AMB_STEP_COUNT=$i 355 | if $Logloop ; then 356 | echo "$l Ambient temperature step count = $i" 357 | echo "$l Ambient max temperature to max mod = $AMBTEMP_MAX°C" 358 | echo "$l CPU Ambiant Steps counting = stop" 359 | fi 360 | break 361 | fi 362 | done 363 | #Pulling temperature data from IPMI 364 | if $IPMIDATA_toggle ; then 365 | IPMIPULLDATA=$(ipmitool -I lanplus -H $IPMIHOST -U $IPMIUSER -P $IPMIPW -y $IPMIEK sdr type temperature) 366 | DATADUMP=$(echo "$IPMIPULLDATA") 367 | if [ -z "$DATADUMP" ]; then 368 | echo "No data was pulled from IPMI" 369 | setfanspeed XX XX "$E_value" 1 370 | else 371 | AUTOEM=false 372 | fi 373 | else 374 | if $NICPU_toggle ; then 375 | AUTOEM=false 376 | else 377 | echo "Both IPMI data and Non-IPMI-CPU data are toggled off" 378 | setfanspeed XX XX "$E_value" 1 379 | fi 380 | fi 381 | #Parsing CPU Temp data into values to be later checked in count, continuity and value validity. 382 | if $NICPU_toggle ; then 383 | echo "Non-IPMI data source. An error can be thrown without incidence." 384 | if $Logloop ; then 385 | echo "$l New loop => Pulling data dynamically from Non-IPMI source" 386 | fi 387 | for ((j=0; j>=0 ; j++)) 388 | do 389 | [ -z "$socketcount" ] && socketcount=0 390 | datadump=$("$NICPUdatadump_command" "$NICPUdatadump_device$(printf "%0"$NICPUdatadump_device_num"d" "$socketcount")") 391 | if [[ ! -z $datadump ]]; then 392 | if $Logloop ; then 393 | echo "$l Detected CPU socket $socketcount !!" 394 | echo "$l New loop => Parsing CPU Core data" 395 | fi 396 | socketcount=$((socketcount+1)) 397 | for ((i=0; i>=0 ; i++)) 398 | do 399 | [ -z "$corecount" ] && corecount=0 400 | Corecountloop_data=$( echo "$datadump" | grep -A 0 "$NICPUdatadump_core $i"| cut "$NICPUdatadump_cut") 401 | if [[ ! -z $Corecountloop_data ]]; then 402 | declare CPUTEMP$corecount="$((Corecountloop_data+NICPUdatadump_offset))" 403 | if $Logloop ; then 404 | echo "$l Defining CPUTEMP$corecount with value : $((CPUTEMP$corecount))" 405 | fi 406 | corecount=$((corecount+1)) 407 | else 408 | if $Logloop ; then 409 | echo "$l CPU Core data parsing on CPU Socket $((socketcount-1)) = stop" 410 | fi 411 | break 412 | fi 413 | done 414 | else 415 | echo "Non-IPMI detection : done." 416 | if $Logloop ; then 417 | echo "$l Result : $corecount Total CPU temperature sources added." 418 | echo "$l CPU Data parsing from Non-IPMI source = stop" 419 | fi 420 | break 421 | fi 422 | done 423 | else 424 | CPUTEMP0=$(echo "$DATADUMP" |grep "$CPUID0" |grep degrees |grep -Po '\d{2}' | tail -1) 425 | CPUTEMP1=$(echo "$DATADUMP" |grep "$CPUID1" |grep degrees |grep -Po '\d{2}' | tail -1) 426 | CPUTEMP2=$(echo "$DATADUMP" |grep "$CPUID2" |grep degrees |grep -Po '\d{2}' | tail -1) 427 | CPUTEMP3=$(echo "$DATADUMP" |grep "$CPUID3" |grep degrees |grep -Po '\d{2}' | tail -1) 428 | fi 429 | #CPU counting 430 | if [ -z "$CPUTEMP0" ]; then 431 | CPUcount=0 432 | else 433 | if [[ ! -z "$CPUTEMP0" ]]; then #Infinite CPU number adding, if you pull individual CPU cores from lm-sensors or something 434 | for ((i=0; i>=0 ; i++)) 435 | do 436 | CPUcountloop="CPUTEMP$i" 437 | if [[ ! -z "${!CPUcountloop}" ]]; then 438 | if $Logloop ; then 439 | echo "$l CPU detection = CPU$i detected / Value = ${!CPUcountloop}" 440 | fi 441 | if ! [[ "${!CPUcountloop}" =~ $re ]] ; then 442 | echo "!!error: Reading is not a number or negative!!" 443 | echo "Falling back to ambient mode..." 444 | CPUcount=0 445 | break 446 | fi 447 | currcputemp="${!CPUcountloop}" 448 | CPUcount=$((i+1)) 449 | TEMPadd=$((TEMPadd+currcputemp)) 450 | else 451 | if [[ $((CPUcount % 2)) -eq 0 ]] || [[ $CPUcount -eq 1 ]]; then 452 | if $Logloop ; then 453 | if [ "$CPUcount" -eq "1" ]; then 454 | echo "$l CPU count : $CPUcount CPU detected!" 455 | else 456 | echo "$l CPU count is even : $CPUcount CPU detected!" 457 | fi 458 | echo "$l CPU counting = stop" 459 | fi 460 | CPUn=$((TEMPadd/CPUcount)) 461 | break 462 | else 463 | CPUcount=0 464 | echo "CPU count is odd, please check your configuration"; 465 | echo "Falling back to ambient mode..." 466 | break 467 | fi 468 | fi 469 | done 470 | 471 | fi 472 | fi 473 | #CPU Find lowest and highest CPU temps 474 | if [ "$CPUcount" -gt 1 ]; then 475 | if $Logloop ; then 476 | echo "$l New loop => Finding highest and lowest CPU temps" 477 | fi 478 | for ((i=0; i Ambient temperature modifier" 524 | fi 525 | if [ "$AMBTEMP" -ge $AMBTEMP_MAX ]; then 526 | echo "Intake temp is very high!! : $AMBTEMP °C!" 527 | TEMPMOD=$MAX_MOD 528 | else 529 | for ((i=0; i MISSING EXHAUST READING" 562 | echo "FALL BACK TO DEFAULT AMBIENT MODE" 563 | AMBDeltaMode=false 564 | EMAMBmode=false 565 | elif [[ ! -z "$EXHTEMP" ]] && [[ -z "$AMBTEMP" ]]; then 566 | echo "DELTA MODE ERROR => MISSING AMBIENT READING" 567 | echo "FALL BACK TO EMERGENCY AMBIENT MODE" 568 | echo "!!EMERGENCY MODE => USING AMBIANT PROFILE WITH EXHAUST TEMP!!" 569 | AMBDeltaMode=false 570 | EMAMBmode=true 571 | elif [[ -z "$EXHTEMP" ]] && [[ -z "$AMBTEMP" ]]; then 572 | echo "DELTA MODE ERROR => MISSING AMBIENT READING" 573 | echo "DELTA MODE ERROR => MISSING EXHAUST READING" 574 | echo "!!EMERGENCY MODE => FALL BACK TO AUTO FAN PROFILE!!" 575 | AMBDeltaMode=false 576 | AUTOEM=true 577 | elif [[ -z "$DeltaR" ]] || [[ "$DeltaR" -le 0 ]]; then 578 | echo "DELTA MODE ERROR => DELTA RATIO INVALID" 579 | echo "!!EMERGENCY MODE => FALL BACK TO AUTO FAN PROFILE!!" 580 | AMBDeltaMode=false 581 | AUTOEM=true 582 | fi 583 | else 584 | if [[ ! -z "$EXHTEMP" ]] && [[ -z "$AMBTEMP" ]]; then 585 | echo "!!MISSING AMBIENT READING!!" 586 | echo "FALL BACK TO EMERGENCY AMBIENT MODE" 587 | echo "!!EMERGENCY MODE => USING AMBIANT PROFILE WITH EXHAUST TEMP!!" 588 | EMAMBmode=true 589 | elif [[ -z "$EXHTEMP" ]] && [[ -z "$AMBTEMP" ]]; then 590 | echo "NO TEMPERATURE READINGS" 591 | echo "!!EMERGENCY MODE => FALL BACK TO AUTO FAN PROFILE!!" 592 | AUTOEM=true 593 | else 594 | EMAMBmode=false 595 | if [[ ! -z "$EXHTEMP" ]]; then 596 | if [ "$EXHTEMP" -ge $EXHTEMP_MAX ]; then 597 | echo "Exhaust temp is critical!! : $EXHTEMP °C!" 598 | TEMPMOD=$MAX_MOD 599 | fi 600 | fi 601 | fi 602 | fi 603 | fi 604 | #vTemp 605 | if [ -z "$TEMPMOD" ]; then 606 | TEMPMOD=0 607 | fi 608 | if [ $CPUcount != 0 ]; then 609 | vTEMP=$((CPUn+TEMPMOD)) 610 | else 611 | if [[ ! -z "$EXHTEMP" ]] && [[ ! -z "$AMBTEMP" ]]; then 612 | if $AMBDeltaMode ; then 613 | if [ "$AMBTEMP" -ge "$EXHTEMP" ]; then 614 | echo "!! Intake = $AMBTEMP°C / Exhaust = $EXHTEMP°C !!" 615 | echo "?Insufficient or reverse airflow?" 616 | echo "!!EMERGENCY MODE => FALL BACK TO AUTO FAN PROFILE!!" 617 | AUTOEM=true 618 | else 619 | vTEMP=$((EXHTEMP-AMBTEMP)) 620 | fi 621 | else 622 | if $EMAMBmode ; then 623 | vTEMP=$EXHTEMP 624 | else 625 | vTEMP=$((AMBTEMP+TEMPMOD)) 626 | fi 627 | fi 628 | else 629 | if $EMAMBmode ; then 630 | vTEMP=$EXHTEMP 631 | else 632 | vTEMP=$((AMBTEMP+TEMPMOD)) 633 | fi 634 | fi 635 | fi 636 | #Emergency mode trigger 637 | if $AUTOEM ; then 638 | setfanspeed XX XX "$E_value" 1 639 | fi 640 | #Logtype logic 641 | if [ $Logtype -eq 2 ]; then 642 | for ((i=0; i Defining fan speeds according to Delta A/E to CPU temp steps : $DeltaR" 712 | fi 713 | for ((i=0; i Checking fan speeds according to values provided by Ambient temp steps" 739 | fi 740 | for ((i=0; i Defining fan speeds according to values provided by Ambiant temp steps" 783 | fi 784 | for ((i=0; i Defining fan speeds according to values provided by CPU temp steps" 810 | fi 811 | for ((i=0; i 46 | 47 | Also available here. Check link before for commented version with detailed info! 48 | 49 |

50 | 51 | ```bash 52 | #!/bin/bash 53 | #the IP address of iDrac 54 | IPMIHOST=192.168.0.42 55 | 56 | #iDrac user 57 | IPMIUSER=root 58 | 59 | #iDrac password (calvin is the default password) 60 | IPMIPW=calvin 61 | 62 | #YOUR IPMI ENCRYPTION KEY 63 | IPMIEK=0000000000000000000000000000000000000000 64 | 65 | #Side note: you shouldn't ever store credentials in a script. Period. Here it's an example. 66 | #I suggest you give a look at tools like https://github.com/plyint/encpass.sh 67 | 68 | #Failsafe mode 69 | #(Possible values being a number between 80 and 100, or "auto") 70 | E_value="auto" 71 | 72 | #IPMI IDs 73 | CPUID0=0Eh 74 | CPUID1=0Fh 75 | CPUID2="0#h" 76 | CPUID3="0#h" 77 | AMBIENT_ID=04h 78 | EXHAUST_ID=01h 79 | 80 | #Non-IPMI data source for CPU: 81 | NICPU_toggle=false 82 | NICPUdatadump_command=(sensors -A) 83 | NICPUdatadump_device="coretemp-isa-" 84 | NICPUdatadump_device_num=4 85 | NICPUdatadump_core=Core 86 | NICPUdatadump_cut="-c16-18" 87 | NICPUdatadump_offset=0 88 | IPMIDATA_toggle=true 89 | 90 | #Logtype: 91 | #0 = Only Alerts 92 | #1 = Fan speed output + alerts 93 | #2 = Simple text + fanspeed output + alerts 94 | #3 = Table + fanspeed output + alerts 95 | Logtype=2 96 | 97 | #There you basically define your fan curve. 98 | TEMP_STEP0=30 99 | FST0=2 100 | TEMP_STEP1=35 101 | FST1=6 102 | TEMP_STEP2=40 103 | FST2=8 104 | TEMP_STEP3=50 105 | FST3=10 106 | TEMP_STEP4=60 107 | FST4=12 108 | TEMP_STEP5=75 109 | FST5=20 110 | #CPU fan governor type 111 | TEMPgov=0 112 | CPUdelta=15 113 | 114 | #These values are used as steps for the intake temps. 115 | 116 | AMBTEMP_STEP0=20 117 | AMBTEMP_MOD_STEP0=0 118 | AMBTEMP_noCPU_FS_STEP0=8 119 | 120 | AMBTEMP_STEP1=21 121 | AMBTEMP_MOD_STEP1=10 122 | AMBTEMP_noCPU_FS_STEP1=15 123 | 124 | AMBTEMP_STEP2=24 125 | AMBTEMP_MOD_STEP2=15 126 | AMBTEMP_noCPU_FS_STEP2=20 127 | 128 | AMBTEMP_STEP3=26 129 | AMBTEMP_MOD_STEP3=20 130 | AMBTEMP_noCPU_FS_STEP3=30 131 | 132 | MAX_MOD=69 133 | 134 | EXHTEMP_MAX=65 135 | 136 | #Ambient fan mode - Delta mode 137 | AMBDeltaMode=true 138 | DeltaR=3 139 | 140 | #Log loop debug - true or false, logging of loops for debugging script 141 | Logloop=false 142 | 143 | #Looplog prefix 144 | l="Loop -" 145 | 146 | #Hexadecimal conversion and IPMI command into a function 147 | ipmifanctl=(ipmitool -I lanplus -H "$IPMIHOST" -U "$IPMIUSER" -P "$IPMIPW" -y "$IPMIEK" raw 0x30 0x30) 148 | function setfanspeed () { 149 | TEMP_Check=$1 150 | TEMP_STEP=$2 151 | FS=$3 152 | if [[ $FS == "auto" ]]; then 153 | if [ "$Logtype" != 0 ] && [ "$4" -eq 0 ]; then 154 | echo "> $TEMP_Check °C is higher or equal to $TEMP_STEP °C. Switching to automatic fan control" 155 | fi 156 | [ "$4" -eq 1 ] && echo "> ERROR : Keeping fans on auto as safety measure" 157 | "${ipmifanctl[@]}" 0x01 0x01 158 | exit $4 159 | else 160 | if [[ $FS -gt "100" ]]; then 161 | FS=100 162 | fi 163 | HEX_value=$(printf '%#04x' "$FS") 164 | if [ "$4" -eq 1 ]; then 165 | echo "> ERROR : Keeping fans on high profile ($3 %) as safety measure" 166 | elif [ "$Logtype" != 0 ]; then 167 | echo "> $TEMP_Check °C is lower or equal to $TEMP_STEP °C. Switching to manual $FS % control" 168 | fi 169 | "${ipmifanctl[@]}" 0x01 0x00 170 | "${ipmifanctl[@]}" 0x02 0xff "$HEX_value" 171 | exit $4 172 | fi 173 | } 174 | #Failsafe = Parameter check 175 | re='^[0-9]+$' 176 | ren='^[+-]?[0-9]+?$' 177 | if [ "$Logloop" != false ] && [ "$Logloop" != true ]; then 178 | echo "Logloop parameter invalid, must be true or false!" 179 | setfanspeed XX XX "$E_value" 1 180 | fi 181 | if [ "$AMBDeltaMode" != false ] && [ "$AMBDeltaMode" != true ]; then 182 | echo "AMBDeltaMode parameter invalid, must be true or false!" 183 | setfanspeed XX XX "$E_value" 1 184 | fi 185 | if [[ "$DeltaR" =~ $ren ]]; then 186 | if [ "$DeltaR" -le "0" ]; then 187 | echo "DeltaR parameter invalid, must be greater than 0!" 188 | setfanspeed XX XX "$E_value" 1 189 | fi 190 | else 191 | echo "DeltaR parameter invalid, not a number!" 192 | setfanspeed XX XX "$E_value" 1 193 | fi 194 | if [[ "$CPUdelta" =~ $ren ]]; then 195 | if [ "$CPUdelta" -le "0" ]; then 196 | echo "CPUdelta parameter invalid, must be greater than 0!" 197 | setfanspeed XX XX "$E_value" 1 198 | fi 199 | else 200 | echo "CPUdelta parameter invalid, not a number!" 201 | setfanspeed XX XX "$E_value" 1 202 | fi 203 | if [ "$TEMPgov" != 1 ] && [ "$TEMPgov" != 0 ]; then 204 | echo "TEMPgov parameter invalid, can only be 0 or 1!" 205 | setfanspeed XX XX "$E_value" 1 206 | fi 207 | if [[ "$Logtype" =~ $ren ]]; then 208 | if [ "$Logtype" -lt 0 ] || [ "$Logtype" -gt 3 ]; then 209 | echo "Logtype parameter invalid, must be in 0-3 range!" 210 | setfanspeed XX XX "$E_value" 1 211 | fi 212 | else 213 | echo "Logtype parameter invalid, not a number!" 214 | setfanspeed XX XX "$E_value" 1 215 | fi 216 | if [[ "$EXHTEMP_MAX" =~ $ren ]]; then 217 | if [ "$EXHTEMP_MAX" -lt 0 ]; then 218 | echo "EXHTEMP_MAX parameter invalid, can't be negative!" 219 | setfanspeed XX XX "$E_value" 1 220 | fi 221 | else 222 | echo "EXHTEMP_MAX parameter invalid, not a number!" 223 | setfanspeed XX XX "$E_value" 1 224 | fi 225 | if [[ $MAX_MOD =~ $ren ]]; then 226 | if [ "$MAX_MOD" -lt 0 ]; then 227 | echo "MAX_MOD parameter invalid, can't be negative!" 228 | setfanspeed XX XX "$E_value" 1 229 | fi 230 | else 231 | echo "MAX_MOD parameter invalid, not a number!" 232 | setfanspeed XX XX "$E_value" 1 233 | fi 234 | if [[ "$E_value" =~ $ren ]]; then 235 | if [ "$E_value" -lt 80 ]; then 236 | echo "E_value parameter invalid, can't be negative or lower than 80" 237 | E_value="auto" 238 | fi 239 | if [ "$E_value" -gt 100 ]; then 240 | echo "E_value parameter invalid, can't be greater than 100" 241 | E_value="auto" 242 | fi 243 | elif [ "$E_value" != "auto" ]; then 244 | echo "E_value parameter invalid, not a number!" 245 | E_value="auto" 246 | fi 247 | #Counting CPU Fan speed steps and setting max value 248 | if $Logloop ; then 249 | echo "$l New loop => Counting CPU Fan speed steps and setting max value" 250 | fi 251 | for ((i=0; i>=0 ; i++)) 252 | do 253 | inloopstep="TEMP_STEP$i" 254 | inloopspeed="FST$i" 255 | if [[ ! -z "${!inloopspeed}" ]] && [[ ! -z "${!inloopstep}" ]]; then 256 | if $Logloop ; then 257 | echo "$l CPU Temperature step n°$i = ${!inloopstep}°C" 258 | echo "$l Fan speed step n°$i = ${!inloopspeed}%" 259 | fi 260 | if ! [[ "${!inloopstep}" =~ $ren ]]; then 261 | echo "Butterfinger failsafe: CPU Temperature step n°$i isn't a number!" 262 | setfanspeed XX XX "$E_value" 1 263 | fi 264 | if [[ "${!inloopspeed}" =~ $ren ]]; then 265 | if [[ "${!inloopspeed}" -lt 0 ]]; then 266 | echo "Butterfinger failsafe: Fan speed step n°$i is negative!" 267 | setfanspeed XX XX "$E_value" 1 268 | fi 269 | 270 | else 271 | echo "Butterfinger failsafe: Fan speed step n°$i isn't a number!" 272 | setfanspeed XX XX "$E_value" 1 273 | fi 274 | else 275 | inloopmaxstep="TEMP_STEP$((i-1))" 276 | if [ $((i-1)) -le 0 ]; then 277 | echo "Butterfinger failsafe: no CPU stepping found!!" 278 | setfanspeed XX XX "$E_value" 1 279 | fi 280 | MAXTEMP="${!inloopmaxstep}" 281 | TEMP_STEP_COUNT=$i 282 | if $Logloop ; then 283 | echo "$l CPU temperature step count = $i" 284 | echo "$l CPU max temperature to auto mode = $MAXTEMP°C" 285 | echo "$l CPU Temp Steps counting = stop" 286 | fi 287 | break 288 | fi 289 | done 290 | #Counting Ambiant Fan speed and MOD steps and setting max value 291 | if $Logloop ; then 292 | echo "$l New loop => Counting Ambiant Fan speed and MOD steps and setting max value" 293 | fi 294 | for ((i=0; i>=0 ; i++)) 295 | do 296 | inloopstep="AMBTEMP_STEP$i" 297 | inloopspeed="AMBTEMP_noCPU_FS_STEP$i" 298 | inloopmod="AMBTEMP_MOD_STEP$i" 299 | if [[ ! -z "${!inloopspeed}" ]] && [[ ! -z "${!inloopmod}" ]] && [[ ! -z "${!inloopstep}" ]]; then 300 | if $Logloop ; then 301 | echo "$l Ambient temperature step n°$i = ${!inloopstep}°C" 302 | echo "$l Ambient modifier for CPU temp step n°$i = ${!inloopmod}°C" 303 | echo "$l Ambient NO CPU fan speed step n°$i = ${!inloopspeed}%" 304 | fi 305 | if ! [[ "${!inloopstep}" =~ $ren ]]; then 306 | echo "Butterfinger failsafe: Ambient temperature step n°$i isn't a number!" 307 | setfanspeed XX XX "$E_value" 1 308 | fi 309 | if [[ "${!inloopmod}" =~ $ren ]]; then 310 | if [[ "${!inloopmod}" -lt 0 ]]; then 311 | echo "Beware: Ambient modifier for CPU temp step n°$i is negative!" 312 | echo "Proceeding..." 313 | fi 314 | 315 | else 316 | echo "Butterfinger failsafe: Ambient modifier for CPU temp step n°$i isn't a number!" 317 | setfanspeed XX XX "$E_value" 1 318 | fi 319 | if [[ "${!inloopspeed}" =~ $ren ]]; then 320 | if [[ "${!inloopspeed}" -lt 0 ]]; then 321 | echo "Butterfinger failsafe: Ambient NO CPU fan speed step n°$i is negative!" 322 | setfanspeed XX XX "$E_value" 1 323 | fi 324 | 325 | else 326 | echo "Butterfinger failsafe: Ambient NO CPU fan speed step n°$i isn't a number!" 327 | setfanspeed XX XX "$E_value" 1 328 | fi 329 | else 330 | inloopmaxstep="AMBTEMP_STEP$((i-1))" 331 | if [ $((i-1)) -le 0 ]; then 332 | echo "Butterfinger failsafe: no Ambient stepping found!!" 333 | setfanspeed XX XX "$E_value" 1 334 | fi 335 | AMBTEMP_MAX="${!inloopmaxstep}" 336 | AMB_STEP_COUNT=$i 337 | if $Logloop ; then 338 | echo "$l Ambient temperature step count = $i" 339 | echo "$l Ambient max temperature to max mod = $AMBTEMP_MAX°C" 340 | echo "$l CPU Ambiant Steps counting = stop" 341 | fi 342 | break 343 | fi 344 | done 345 | #Pulling temperature data from IPMI 346 | if $IPMIDATA_toggle ; then 347 | IPMIPULLDATA=$(ipmitool -I lanplus -H $IPMIHOST -U $IPMIUSER -P $IPMIPW -y $IPMIEK sdr type temperature) 348 | DATADUMP=$(echo "$IPMIPULLDATA") 349 | if [ -z "$DATADUMP" ]; then 350 | echo "No data was pulled from IPMI" 351 | setfanspeed XX XX "$E_value" 1 352 | else 353 | AUTOEM=false 354 | fi 355 | else 356 | if $NICPU_toggle ; then 357 | AUTOEM=false 358 | else 359 | echo "Both IPMI data and Non-IPMI-CPU data are toggled off" 360 | setfanspeed XX XX "$E_value" 1 361 | fi 362 | fi 363 | #Parsing CPU Temp data into values to be later checked in count, continuity and value validity. 364 | if $NICPU_toggle ; then 365 | echo "Non-IPMI data source. An error can be thrown without incidence." 366 | if $Logloop ; then 367 | echo "$l New loop => Pulling data dynamically from Non-IPMI source" 368 | fi 369 | for ((j=0; j>=0 ; j++)) 370 | do 371 | [ -z "$socketcount" ] && socketcount=0 372 | datadump=$("$NICPUdatadump_command" "$NICPUdatadump_device$(printf "%0"$NICPUdatadump_device_num"d" "$socketcount")") 373 | if [[ ! -z $datadump ]]; then 374 | if $Logloop ; then 375 | echo "$l Detected CPU socket $socketcount !!" 376 | echo "$l New loop => Parsing CPU Core data" 377 | fi 378 | socketcount=$((socketcount+1)) 379 | for ((i=0; i>=0 ; i++)) 380 | do 381 | [ -z "$corecount" ] && corecount=0 382 | Corecountloop_data=$( echo "$datadump" | grep -A 0 "$NICPUdatadump_core $i"| cut "$NICPUdatadump_cut") 383 | if [[ ! -z $Corecountloop_data ]]; then 384 | declare CPUTEMP$corecount="$((Corecountloop_data+NICPUdatadump_offset))" 385 | if $Logloop ; then 386 | echo "$l Defining CPUTEMP$corecount with value : $((CPUTEMP$corecount))" 387 | fi 388 | corecount=$((corecount+1)) 389 | else 390 | if $Logloop ; then 391 | echo "$l CPU Core data parsing on CPU Socket $((socketcount-1)) = stop" 392 | fi 393 | break 394 | fi 395 | done 396 | else 397 | echo "Non-IPMI detection : done." 398 | if $Logloop ; then 399 | echo "$l Result : $corecount Total CPU temperature sources added." 400 | echo "$l CPU Data parsing from Non-IPMI source = stop" 401 | fi 402 | break 403 | fi 404 | done 405 | else 406 | CPUTEMP0=$(echo "$DATADUMP" |grep "$CPUID0" |grep degrees |grep -Po '\d{2}' | tail -1) 407 | CPUTEMP1=$(echo "$DATADUMP" |grep "$CPUID1" |grep degrees |grep -Po '\d{2}' | tail -1) 408 | CPUTEMP2=$(echo "$DATADUMP" |grep "$CPUID2" |grep degrees |grep -Po '\d{2}' | tail -1) 409 | CPUTEMP3=$(echo "$DATADUMP" |grep "$CPUID3" |grep degrees |grep -Po '\d{2}' | tail -1) 410 | fi 411 | #CPU counting 412 | if [ -z "$CPUTEMP0" ]; then 413 | CPUcount=0 414 | else 415 | if [[ ! -z "$CPUTEMP0" ]]; then #Infinite CPU number adding, if you pull individual CPU cores from lm-sensors or something 416 | for ((i=0; i>=0 ; i++)) 417 | do 418 | CPUcountloop="CPUTEMP$i" 419 | if [[ ! -z "${!CPUcountloop}" ]]; then 420 | if $Logloop ; then 421 | echo "$l CPU detection = CPU$i detected / Value = ${!CPUcountloop}" 422 | fi 423 | if ! [[ "${!CPUcountloop}" =~ $re ]] ; then 424 | echo "!!error: Reading is not a number or negative!!" 425 | echo "Falling back to ambient mode..." 426 | CPUcount=0 427 | break 428 | fi 429 | currcputemp="${!CPUcountloop}" 430 | CPUcount=$((i+1)) 431 | TEMPadd=$((TEMPadd+currcputemp)) 432 | else 433 | if [[ $((CPUcount % 2)) -eq 0 ]] || [[ $CPUcount -eq 1 ]]; then 434 | if $Logloop ; then 435 | if [ "$CPUcount" -eq "1" ]; then 436 | echo "$l CPU count : $CPUcount CPU detected!" 437 | else 438 | echo "$l CPU count is even : $CPUcount CPU detected!" 439 | fi 440 | echo "$l CPU counting = stop" 441 | fi 442 | CPUn=$((TEMPadd/CPUcount)) 443 | break 444 | else 445 | CPUcount=0 446 | echo "CPU count is odd, please check your configuration"; 447 | echo "Falling back to ambient mode..." 448 | break 449 | fi 450 | fi 451 | done 452 | 453 | fi 454 | fi 455 | #CPU Find lowest and highest CPU temps 456 | if [ "$CPUcount" -gt 1 ]; then 457 | if $Logloop ; then 458 | echo "$l New loop => Finding highest and lowest CPU temps" 459 | fi 460 | for ((i=0; i Ambient temperature modifier" 506 | fi 507 | if [ "$AMBTEMP" -ge $AMBTEMP_MAX ]; then 508 | echo "Intake temp is very high!! : $AMBTEMP °C!" 509 | TEMPMOD=$MAX_MOD 510 | else 511 | for ((i=0; i MISSING EXHAUST READING" 544 | echo "FALL BACK TO DEFAULT AMBIENT MODE" 545 | AMBDeltaMode=false 546 | EMAMBmode=false 547 | elif [[ ! -z "$EXHTEMP" ]] && [[ -z "$AMBTEMP" ]]; then 548 | echo "DELTA MODE ERROR => MISSING AMBIENT READING" 549 | echo "FALL BACK TO EMERGENCY AMBIENT MODE" 550 | echo "!!EMERGENCY MODE => USING AMBIANT PROFILE WITH EXHAUST TEMP!!" 551 | AMBDeltaMode=false 552 | EMAMBmode=true 553 | elif [[ -z "$EXHTEMP" ]] && [[ -z "$AMBTEMP" ]]; then 554 | echo "DELTA MODE ERROR => MISSING AMBIENT READING" 555 | echo "DELTA MODE ERROR => MISSING EXHAUST READING" 556 | echo "!!EMERGENCY MODE => FALL BACK TO AUTO FAN PROFILE!!" 557 | AMBDeltaMode=false 558 | AUTOEM=true 559 | elif [[ -z "$DeltaR" ]] || [[ "$DeltaR" -le 0 ]]; then 560 | echo "DELTA MODE ERROR => DELTA RATIO INVALID" 561 | echo "!!EMERGENCY MODE => FALL BACK TO AUTO FAN PROFILE!!" 562 | AMBDeltaMode=false 563 | AUTOEM=true 564 | fi 565 | else 566 | if [[ ! -z "$EXHTEMP" ]] && [[ -z "$AMBTEMP" ]]; then 567 | echo "!!MISSING AMBIENT READING!!" 568 | echo "FALL BACK TO EMERGENCY AMBIENT MODE" 569 | echo "!!EMERGENCY MODE => USING AMBIANT PROFILE WITH EXHAUST TEMP!!" 570 | EMAMBmode=true 571 | elif [[ -z "$EXHTEMP" ]] && [[ -z "$AMBTEMP" ]]; then 572 | echo "NO TEMPERATURE READINGS" 573 | echo "!!EMERGENCY MODE => FALL BACK TO AUTO FAN PROFILE!!" 574 | AUTOEM=true 575 | else 576 | EMAMBmode=false 577 | if [[ ! -z "$EXHTEMP" ]]; then 578 | if [ "$EXHTEMP" -ge $EXHTEMP_MAX ]; then 579 | echo "Exhaust temp is critical!! : $EXHTEMP °C!" 580 | TEMPMOD=$MAX_MOD 581 | fi 582 | fi 583 | fi 584 | fi 585 | fi 586 | #vTemp 587 | if [ -z "$TEMPMOD" ]; then 588 | TEMPMOD=0 589 | fi 590 | if [ $CPUcount != 0 ]; then 591 | vTEMP=$((CPUn+TEMPMOD)) 592 | else 593 | if [[ ! -z "$EXHTEMP" ]] && [[ ! -z "$AMBTEMP" ]]; then 594 | if $AMBDeltaMode ; then 595 | if [ "$AMBTEMP" -ge "$EXHTEMP" ]; then 596 | echo "!! Intake = $AMBTEMP°C / Exhaust = $EXHTEMP°C !!" 597 | echo "?Insufficient or reverse airflow?" 598 | echo "!!EMERGENCY MODE => FALL BACK TO AUTO FAN PROFILE!!" 599 | AUTOEM=true 600 | else 601 | vTEMP=$((EXHTEMP-AMBTEMP)) 602 | fi 603 | else 604 | if $EMAMBmode ; then 605 | vTEMP=$EXHTEMP 606 | else 607 | vTEMP=$((AMBTEMP+TEMPMOD)) 608 | fi 609 | fi 610 | else 611 | if $EMAMBmode ; then 612 | vTEMP=$EXHTEMP 613 | else 614 | vTEMP=$((AMBTEMP+TEMPMOD)) 615 | fi 616 | fi 617 | fi 618 | #Emergency mode trigger 619 | if $AUTOEM ; then 620 | setfanspeed XX XX "$E_value" 1 621 | fi 622 | #Logtype logic 623 | if [ $Logtype -eq 2 ]; then 624 | for ((i=0; i Defining fan speeds according to Delta A/E to CPU temp steps : $DeltaR" 694 | fi 695 | for ((i=0; i Checking fan speeds according to values provided by Ambient temp steps" 721 | fi 722 | for ((i=0; i Defining fan speeds according to values provided by Ambiant temp steps" 765 | fi 766 | for ((i=0; i Defining fan speeds according to values provided by CPU temp steps" 792 | fi 793 | for ((i=0; i 815 | 816 | 817 | ## Cron job yet? 818 | 819 | Good, you added your script. Still, at this moment, it doesn't run by itself. 820 | 821 | It's normal, for now it's just a script on a file, with no wit to it, it's not YET a cron job. You now have to give it a schedule (if you so desire). 822 | 823 | Unraid makes it easy again. 824 | Click on the droplist that should be showing "Schedule Disabled" as its selected option. 825 | You should find a pre-made array of ready to go schedules as follow: 826 | 827 | Schedule Disabled| 828 | ------------ | 829 | Scheduled Hourly | 830 | Scheduled Weekly | 831 | Scheduled Monthly | 832 | At Startup of Array | 833 | At Stopping of Array | 834 | At First Array Start Only | 835 | **Custom** | 836 | 837 | The ones linked to array events execute on the array's "active time", aka after the array started, and before it shuts down, keep that in mind. 838 | 839 | **But** that's not what we want for a script managing cooling. Polling data and adjusting fan speed must be on a schedule way tighter than hourly, so we will use the **Custom** option. 840 | 841 | Selection the "Custom" option will pop a little text box to the right of the droplist, of which the formatting is important and follow standard cron job format. 842 | 843 | Cron expressions speak in minutes, hours, day of the month, month, day of the week and year. 844 | 845 | Before going more in depth, let's cut to the chase, if you want your script to run every minute, punch in: ```* * * * *``` 846 | 847 | **Keep in mind** that as stated before user scripts only run on Array uptime, meaning that setting up a script that puts back fan control on auto when you shut down the array is more than advised. 848 | 849 | To do so, create a separate script, scheduled on "At stopping of Array", punching in your own infos and credentials, that goes as follow: 850 | ```bash 851 | #!/bin/bash 852 | IPMIHOST=192.168.0.42 853 | #iDrac user 854 | IPMIUSER=root 855 | #iDrac password (calvin is the default password) 856 | IPMIPW=calvin 857 | #YOUR IPMI ENCRYPTION KEY - a big string of zeros is the default, and by default isn't mandatory to be specified. 858 | #You can modify it, for example in idrac7's webinterface under iDRAC Settings>Network , in the IPMI Settings section. 859 | IPMIEK=0000000000000000000000000000000000000000 860 | 861 | #The command will just put fans back to auto. 862 | ipmitool -I lanplus -H $IPMIHOST -U $IPMIUSER -P $IPMIPW -y $IPMIEK raw 0x30 0x30 0x01 0x01 863 | ``` 864 | With that, you're more or less set for the fan control. 865 | 866 | 867 | ## More cron 868 | Well, you can do a lot with cron expressions, that's the gist of it. 869 | 870 |

871 | 872 | Everything you need should be here 873 | 874 |

875 | 876 | Instead of boring you with text, here's the alphabet of them: 877 | 878 | Field Name | Mandatory | Allowed Values | Allowed Special Characters | 879 | ------ | ------- | ------- | ------- | 880 | Minutes | YES | 0 - 59 | , - \* / | 881 | Hours | YES | 0 - 23 | , - \* / | 882 | Day of month | YES | 1 - 31 | , - \* ? / L W | 883 | Month | YES | 1 - 12 (representing Jan - Dec), JAN - DEC (case-insensitive), JANUARY - DECEMBER (case-insensitive) | , - \* / | 884 | Day of week | YES | 0 - 6, 7 (representing Sun - Sat and Sun again), SUN - SAT (case-insensitive), SUNDAY - SATURDAY (case-insensitive) | , - \* ? / L # | 885 | Year | NO | empty or 1970-2099 | , - \* / | 886 | 887 | And here a cheatsheet, you'll probably find what you're looking for in it, or be able to make it from it. 888 | 889 | Cron Expression examples | Meaning | 890 | --------- | --------- | 891 | \* \* \* \* \* 2022 | Execute a cron job every minute during the year 2022 | 892 | \* \* \* \* \* | Execute a cron job every minute | 893 | \*/5 \* \* \* \* | Execute a cron job every 5 minutes | 894 | 0 \* \* \* \* | Execute a cron job every hour | 895 | 0 12 \* \* \* | Fire at 12:00 PM (noon) every day | 896 | 15 10 \* \* \* | Fire at 10:15 AM every day | 897 | 15 10 \* \* ? | Fire at 10:15 AM every day | 898 | 15 10 \* \* \* 2022-2024 | Fire at 10:15 AM every day during the years 2022, 2023 and 2024 | 899 | \* 14 \* \* \* | Fire every minute starting at 2:00 PM and ending at 2:59 PM, every day | 900 | 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 | 901 | 0-5 14 \* \* \* | Fire every minute starting at 2:00 PM and ending at 2:05 PM, every day | 902 | 10,44 14 \* 3 3 | Fire at 2:10 PM and at 2:44 PM every Wednesday in the month of March. | 903 | 15 10 \* \* 1-5 | Fire at 10:15 AM every Monday, Tuesday, Wednesday, Thursday and Friday | 904 | 15 10 15 \* \* | Fire at 10:15 AM on the 15th day of every month | 905 | 15 10 L \* \* | Fire at 10:15 AM on the last day of every month | 906 | 15 10 \* \* 5L | Fire at 10:15 AM on the last Friday of every month | 907 | 15 10 \* \* 5#3 | Fire at 10:15 AM on the third Friday of every month | 908 | 0 12 1/5 \* \* | Fire at 12:00 PM (noon) every 5 days every month, starting on the first day of the month. | 909 | 11 11 11 11 \* | Fire every November 11th at 11:11 AM. | 910 | 11 11 11 11 \* 2022 | Fire at 11:11 AM on November 11th in the year 2022. | 911 | 0 0 \* \* 3 | Fire at midnight of each Wednesday. | 912 | 0 0 1,2 \* \* | Fire at midnight of 1st, 2nd day of each month | 913 | 0 0 1,2 \* 3 | Fire at midnight of 1st, 2nd day of each month, and each Wednesday. | 914 | 915 |

916 |
917 | --------------------------------------------------------------------------------