[script] drive temps

j_r0dd

Contributor
Joined
Jan 26, 2015
Messages
134
It has been awhile since I posted anything, but thought I would share this script I wrote this weekend. Before anybody asks, yes I do have SMART system alerts set to send me an email if drives exceed a certain temperature. I was just replacing the fans in my case and wanted any easy solution to periodically check drive temperatures, so I know where there might be obstructions from wiring, etc. I wanted the output to include the dev location, serial number (to make investigation easier) & the temps. You can set the max temp you want to be warned about in the script. I have 7200rpm drives and 40 degrees really is the max I'd personally like to see. Since some SSDs/DOMs don't have temp sensors, the script will display those with a temp of NA. This will detect da/ada/nvme devices. You can also set the max temp of SSD's. If you have any issues with it please let me know by including the output from the script on your system and I will do my best to address.

Update 2021-06-23:
* added version compatible with SCALE

Update 2021-03-25:
* unified script to be able to run in the shell or run as a cronjob (just run the script the script like ./drivetemp.zsh shell or ./drivetemp.zsh email)
* you can now set the max temp of your SSD's in the script since they tend to run hotter

Screen Shot 2021-03-02 at 1.05.33 AM.png


CORE
Code:
#!/usr/bin/env zsh

# Change the max temp threshold to whatever you like
HDDMAX=40
SSDMAX=55
TMPFILE=$(mktemp)
EMAIL=$(awk '{ if ($1 == "root\:") {print $2 } }' /etc/aliases)
HOSTNAME=$(hostname)
BOUNDARY=$(date +%s | md5 | cut -b1-16)
HDD=( $(smartctl --scan | grep -E 'da|nvme' | cut -d ' ' -f1) )


function tempShell () {
rows="%-22b %-34b %-2b\n"
sep=$(printf "%.s=" {1..54})
printf "%s\n" "$sep"
printf "$rows" "\e[37mDevice" "\e[37mSerial" "\e[37mTemp\e[m"
printf "%s\n" "$sep"

for i in "${HDD[@]}"; do
    TEMP=$(smartctl --json=g -A "$i" | grep 'json.temperature.current' | cut -d ' ' -f3 | tr -d \;)
    SN=$(smartctl --json=g -i "$i" | grep 'json.serial_number' | cut -d ' ' -f3 | tr -d \"\;)
    TYPE=$(smartctl --json=g -a "$i" | grep 'json.rotation_rate' | cut -d ' ' -f3 | tr -d \;)
    if [[ $TEMP -ne 0 ]]; then
        if [[ $TEMP -ge $SSDMAX && $TYPE -eq 0 || $TEMP -ge $HDDMAX && $TYPE -gt 0 ]]; then
          printf "$rows" "\e[37m$i" "\e[90m$SN" "\e[91;5m$TEMP\e[m"
        else
          printf "$rows" "\e[37m$i" "\e[90m$SN" "\e[96m$TEMP\e[m"
        fi
      else
        printf "$rows" "\e[37m$i" "\e[90m$SN" "\e[93mNA\e[m"
    fi
done
}


function tempEmail () {
if [[ "$EMAIL" == "None" ]]; then
    printf "%s\n" "Email is not configured for root user" "Please configure and run again" "exiting..."
    exit 1
fi

{
printf "To: %s\n" "$EMAIL"
printf "Subject: TrueNAS %s: Drive Temp report on %s\n" "$HOSTNAME" "$HOSTNAME"
printf "%s\n" "MIME-Version: 1.0"
printf "Content-Type: multipart/alternative; boundary=%s\n" "$BOUNDARY"
printf "%s\n" --"$BOUNDARY" "Content-Type: text/html; charset=\"utf-8\"" "Content-Transfer-Encoding: 8bit"
printf "%s\n" "<html xmlns=\"http://www.w3.org/1999/xhtml\">" "<body style=\"color:#121212; background-color:#FFFFFF\">" "<table style=\"text-align:left;\">"
printf "%s\n" "<tr>" "<th style=\"width:22ch;\">Device</th>" "<th style=\"width:34ch;\">Serial</th>" "<th style=\"width:4ch;\">Temp</th>" "</tr>"
} >> "$TMPFILE"

for i in "${HDD[@]}"; do
    TEMP=$(smartctl --json=g -A "$i" | grep 'json.temperature.current' | cut -d ' ' -f3 | tr -d \;)
    SN=$(smartctl --json=g -i "$i" | grep 'json.serial_number' | cut -d ' ' -f3 | tr -d \"\;)
    TYPE=$(smartctl --json=g -a "$i" | grep 'json.rotation_rate' | cut -d ' ' -f3 | tr -d \;)
    if [[ $TEMP -ne 0 ]]; then
        if [[ $TEMP -ge $SSDMAX && $TYPE -eq 0 || $TEMP -ge $HDDMAX && $TYPE -gt 0 ]]; then
          printf "%s\n" "<tr>" "<td>$i</td>" "<td style=\"color:#696969;\">$SN</td>" "<td style=\"color:#FF1744;\">$TEMP</td>" "</tr>" >> "$TMPFILE"
        else
          printf "%s\n" "<tr>" "<td>$i</td>" "<td style=\"color:#696969;\">$SN</td>" "<td style=\"color:#00E5FF;\">$TEMP</td>" "</tr>" >> "$TMPFILE"
        fi
      else
        printf "%s\n" "<tr>" "<td>$i</td>" "<td style=\"color:#696969;\">$SN</td>" "<td style=\"color:#FFEE58;\">NA</td>" "</tr>" >> "$TMPFILE"
    fi
done

printf "%s\n" "</table>" "</body>" "</html>" >> "$TMPFILE"
sendmail -t < "$TMPFILE"
rm "$TMPFILE"
}


if [[ $1 == "shell" ]]; then
    tempShell
  elif [[ $1 == "email" ]]; then
    tempEmail
  else
    printf "%s\n" "Incorrect option of $1" "Please run again with [email/shell]" "exiting..."
fi


SCALE
Code:
#!/usr/bin/env zsh

# Change the max temp threshold to whatever you like
HDDMAX=40
SSDMAX=60
TMPFILE=$(mktemp)
EMAIL=$(awk '{ if ($1 == "root:") {print $2 } }' /etc/aliases)
HOSTNAME=$(hostname)
BOUNDARY=$(date +%s | md5sum | cut -b1-16)
HDD=( $(smartctl --scan | cut -d ' ' -f1) )


function tempShell () {
rows="%-22b %-34b %-2b\n"
sep=$(printf "%.s=" {1..54})
printf "%s\n" "$sep"
printf "$rows" "\e[37mDevice" "\e[37mSerial" "\e[37mTemp\e[m"
printf "%s\n" "$sep"

for i in "${HDD[@]}"; do
    TEMP=$(smartctl --json=g -A "$i" | grep 'json.temperature.current' | cut -d ' ' -f3 | tr -d \;)
    SN=$(smartctl --json=g -i "$i" | grep 'json.serial_number' | cut -d ' ' -f3 | tr -d \"\;)
    TYPE=$(smartctl --json=g -a "$i" | grep 'json.rotation_rate' | cut -d ' ' -f3 | tr -d \;)
    if [[ $TEMP -ne 0 ]]; then
        if [[ $TEMP -ge $SSDMAX && $TYPE -eq 0 || $TEMP -ge $HDDMAX && $TYPE -gt 0 ]]; then
          printf "$rows" "\e[37m$i" "\e[90m$SN" "\e[91;5m$TEMP\e[m"
        else
          printf "$rows" "\e[37m$i" "\e[90m$SN" "\e[96m$TEMP\e[m"
        fi
      else
        printf "$rows" "\e[37m$i" "\e[90m$SN" "\e[93mNA\e[m"
    fi
done
}


function tempEmail () {
if [[ "$EMAIL" == "None" ]]; then
    printf "%s\n" "Email is not configured for root user" "Please configure and run again" "exiting..."
    exit 1
fi

{
printf "To: %s\n" "$EMAIL"
printf "Subject: TrueNAS %s: Drive Temp report on %s\n" "$HOSTNAME" "$HOSTNAME"
printf "%s\n" "MIME-Version: 1.0"
printf "Content-Type: multipart/alternative; boundary=%s\n" "$BOUNDARY"
printf "%s\n" --"$BOUNDARY" "Content-Type: text/html; charset=\"utf-8\"" "Content-Transfer-Encoding: 8bit"
printf "%s\n" "<html xmlns=\"http://www.w3.org/1999/xhtml\">" "<body style=\"color:#121212; background-color:#FFFFFF\">" "<table style=\"text-align:left;\">"
printf "%s\n" "<tr>" "<th style=\"width:22ch;\">Device</th>" "<th style=\"width:34ch;\">Serial</th>" "<th style=\"width:4ch;\">Temp</th>" "</tr>"
} >> "$TMPFILE"
                                                                                                                                                                                                                           
for i in "${HDD[@]}"; do
    TEMP=$(smartctl --json=g -A "$i" | grep 'json.temperature.current' | cut -d ' ' -f3 | tr -d \;)
    SN=$(smartctl --json=g -i "$i" | grep 'json.serial_number' | cut -d ' ' -f3 | tr -d \"\;)
    TYPE=$(smartctl --json=g -a "$i" | grep 'json.rotation_rate' | cut -d ' ' -f3 | tr -d \;)
    if [[ $TEMP -ne 0 ]]; then
        if [[ $TEMP -ge $SSDMAX && $TYPE -eq 0 || $TEMP -ge $HDDMAX && $TYPE -gt 0 ]]; then
          printf "%s\n" "<tr>" "<td>$i</td>" "<td style=\"color:#696969;\">$SN</td>" "<td style=\"color:#FF1744;\">$TEMP</td>" "</tr>" >> "$TMPFILE"
        else
          printf "%s\n" "<tr>" "<td>$i</td>" "<td style=\"color:#696969;\">$SN</td>" "<td style=\"color:#00E5FF;\">$TEMP</td>" "</tr>" >> "$TMPFILE"
        fi
      else
        printf "%s\n" "<tr>" "<td>$i</td>" "<td style=\"color:#696969;\">$SN</td>" "<td style=\"color:#FFEE58;\">NA</td>" "</tr>" >> "$TMPFILE"
    fi
done

printf "%s\n" "</table>" "</body>" "</html>" >> "$TMPFILE"
sendmail -t < "$TMPFILE"
rm "$TMPFILE"
}


if [[ $1 == "shell" ]]; then
    tempShell
  elif [[ $1 == "email" ]]; then
    tempEmail
  else
    printf "%s\n" "Incorrect option of $1" "Please run again with or [shell]" "exiting..."
fi
 
Last edited:

j_r0dd

Contributor
Joined
Jan 26, 2015
Messages
134
updated some of the code to be posix compliant
 

j_r0dd

Contributor
Joined
Jan 26, 2015
Messages
134
Since the default shell in TrueNAS is now ZSH, I made this compatible with ZSH and BASH. ZSH handles arrays a little different than BASH. I also updated the code to give this a nice performance boost. This should work on Linux as well.
 

hervon

Patron
Joined
Apr 23, 2012
Messages
353
TrueNAS-12.0-U2.1 I copied the code. Made sure the EOL was linux compatible. I get this :
./drivetemp.sh: Code:: not found
./drivetemp.sh: 21: Syntax error: word unexpected (expecting ")")

Maybe the issue is on my side. Would not be the first time. ;-) Thanks.
 

Etorix

Wizard
Joined
Dec 30, 2020
Messages
2,112
./drivetemp.sh: Code:: not found
The actual code begins at #! /usr/bin/env zsh. Copy the script anew from this line, without the "Code:".
 

j_r0dd

Contributor
Joined
Jan 26, 2015
Messages
134
Can you post the output of either 'bash -x drivetemp.sh' or 'zsh -x drivetemp.sh'. This is not compatible with sh. I only made compatible with zsh or bash.
 

hervon

Patron
Joined
Apr 23, 2012
Messages
353
The actual code begins at #! /usr/bin/env zsh. Copy the script anew from this line, without the "Code:".
Fail on my part indeed.

Can you post the output of either 'bash -x drivetemp.sh' or 'zsh -x drivetemp.sh'. This is not compatible with sh. I only made compatible with zsh or bash.

Maybe i'm doing something wrong again.

Code:
root@FreeNAS:~ # zsh -x drivetemp.sh
+drivetemp.sh:16> MAX=40
+drivetemp.sh:19> HDD=+drivetemp.sh:19> smartctl --scan
+drivetemp.sh:19> HDD=+drivetemp.sh:19> grep -E 'da|nvme'
+drivetemp.sh:19> HDD=+drivetemp.sh:19> cut -d ' ' -f1
+drivetemp.sh:19> HDD=( /dev/da0 /dev/da1 /dev/ada0 /dev/ada1 /dev/ada2 /dev/ada3 /dev/ada4 /dev/ada5 )
+drivetemp.sh:22> sep=+drivetemp.sh:22> printf '%.s=' 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
+drivetemp.sh:22> sep='============================================'
+drivetemp.sh:23> printf '%s\n' '============================================'
============================================
+drivetemp.sh:24> printf 'Device\t\tSerial\t\t\tTemp\n'
Device        Serial            Temp
+drivetemp.sh:25> printf '%s\n' '============================================'
============================================
+drivetemp.sh:27> i=/dev/da0
+drivetemp.sh:28> TEMP=+drivetemp.sh:28> smartctl -A /dev/da0
+drivetemp.sh:28> TEMP=+drivetemp.sh:28> grep Temperature_Celsius
+drivetemp.sh:28> TEMP=+drivetemp.sh:28> cut -d ' ' -f37
+drivetemp.sh:28> TEMP=29
+drivetemp.sh:29> SN=+drivetemp.sh:29> smartctl -i /dev/da0
+drivetemp.sh:29> SN=+drivetemp.sh:29> grep Serial
+drivetemp.sh:29> SN=+drivetemp.sh:29> cut -d ' ' -f6
+drivetemp.sh:29> SN=HKR02YFT
+drivetemp.sh:30> [[ 29 -ne 0 ]]
+drivetemp.sh:31> [[ 29 -ge 40 ]]
+drivetemp.sh:34> printf '\33[37m%s/dev/da0\t\33[90m%sHKR02YFT\t\t\033[36m%s29\n'
/dev/da0    HKR02YFT        29
+drivetemp.sh:27> i=/dev/da1
+drivetemp.sh:28> TEMP=+drivetemp.sh:28> smartctl -A /dev/da1
+drivetemp.sh:28> TEMP=+drivetemp.sh:28> grep Temperature_Celsius
+drivetemp.sh:28> TEMP=+drivetemp.sh:28> cut -d ' ' -f37
+drivetemp.sh:28> TEMP=29
+drivetemp.sh:29> SN=+drivetemp.sh:29> smartctl -i /dev/da1
+drivetemp.sh:29> SN=+drivetemp.sh:29> grep Serial
+drivetemp.sh:29> SN=+drivetemp.sh:29> cut -d ' ' -f6
+drivetemp.sh:29> SN=HKR0217N
+drivetemp.sh:30> [[ 29 -ne 0 ]]
+drivetemp.sh:31> [[ 29 -ge 40 ]]
+drivetemp.sh:34> printf '\33[37m%s/dev/da1\t\33[90m%sHKR0217N\t\t\033[36m%s29\n'
/dev/da1    HKR0217N        29
+drivetemp.sh:27> i=/dev/ada0
+drivetemp.sh:28> TEMP=+drivetemp.sh:28> smartctl -A /dev/ada0
+drivetemp.sh:28> TEMP=+drivetemp.sh:28> grep Temperature_Celsius
+drivetemp.sh:28> TEMP=+drivetemp.sh:28> cut -d ' ' -f37
+drivetemp.sh:28> TEMP='(Min/Max'
+drivetemp.sh:29> SN=+drivetemp.sh:29> smartctl -i /dev/ada0
+drivetemp.sh:29> SN=+drivetemp.sh:29> grep Serial
+drivetemp.sh:29> SN=+drivetemp.sh:29> cut -d ' ' -f6
+drivetemp.sh:29> SN=979030411582
+drivetemp.sh:30> [[ '(Min/Max' -ne 0drivetemp.sh:30: division by zero
 ]]
 

j_r0dd

Contributor
Joined
Jan 26, 2015
Messages
134
Fail on my part indeed.



Maybe i'm doing something wrong again.

Code:
root@FreeNAS:~ # zsh -x drivetemp.sh
+drivetemp.sh:16> MAX=40
+drivetemp.sh:19> HDD=+drivetemp.sh:19> smartctl --scan
+drivetemp.sh:19> HDD=+drivetemp.sh:19> grep -E 'da|nvme'
+drivetemp.sh:19> HDD=+drivetemp.sh:19> cut -d ' ' -f1
+drivetemp.sh:19> HDD=( /dev/da0 /dev/da1 /dev/ada0 /dev/ada1 /dev/ada2 /dev/ada3 /dev/ada4 /dev/ada5 )
+drivetemp.sh:22> sep=+drivetemp.sh:22> printf '%.s=' 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
+drivetemp.sh:22> sep='============================================'
+drivetemp.sh:23> printf '%s\n' '============================================'
============================================
+drivetemp.sh:24> printf 'Device\t\tSerial\t\t\tTemp\n'
Device        Serial            Temp
+drivetemp.sh:25> printf '%s\n' '============================================'
============================================
+drivetemp.sh:27> i=/dev/da0
+drivetemp.sh:28> TEMP=+drivetemp.sh:28> smartctl -A /dev/da0
+drivetemp.sh:28> TEMP=+drivetemp.sh:28> grep Temperature_Celsius
+drivetemp.sh:28> TEMP=+drivetemp.sh:28> cut -d ' ' -f37
+drivetemp.sh:28> TEMP=29
+drivetemp.sh:29> SN=+drivetemp.sh:29> smartctl -i /dev/da0
+drivetemp.sh:29> SN=+drivetemp.sh:29> grep Serial
+drivetemp.sh:29> SN=+drivetemp.sh:29> cut -d ' ' -f6
+drivetemp.sh:29> SN=HKR02YFT
+drivetemp.sh:30> [[ 29 -ne 0 ]]
+drivetemp.sh:31> [[ 29 -ge 40 ]]
+drivetemp.sh:34> printf '\33[37m%s/dev/da0\t\33[90m%sHKR02YFT\t\t\033[36m%s29\n'
/dev/da0    HKR02YFT        29
+drivetemp.sh:27> i=/dev/da1
+drivetemp.sh:28> TEMP=+drivetemp.sh:28> smartctl -A /dev/da1
+drivetemp.sh:28> TEMP=+drivetemp.sh:28> grep Temperature_Celsius
+drivetemp.sh:28> TEMP=+drivetemp.sh:28> cut -d ' ' -f37
+drivetemp.sh:28> TEMP=29
+drivetemp.sh:29> SN=+drivetemp.sh:29> smartctl -i /dev/da1
+drivetemp.sh:29> SN=+drivetemp.sh:29> grep Serial
+drivetemp.sh:29> SN=+drivetemp.sh:29> cut -d ' ' -f6
+drivetemp.sh:29> SN=HKR0217N
+drivetemp.sh:30> [[ 29 -ne 0 ]]
+drivetemp.sh:31> [[ 29 -ge 40 ]]
+drivetemp.sh:34> printf '\33[37m%s/dev/da1\t\33[90m%sHKR0217N\t\t\033[36m%s29\n'
/dev/da1    HKR0217N        29
+drivetemp.sh:27> i=/dev/ada0
+drivetemp.sh:28> TEMP=+drivetemp.sh:28> smartctl -A /dev/ada0
+drivetemp.sh:28> TEMP=+drivetemp.sh:28> grep Temperature_Celsius
+drivetemp.sh:28> TEMP=+drivetemp.sh:28> cut -d ' ' -f37
+drivetemp.sh:28> TEMP='(Min/Max'
+drivetemp.sh:29> SN=+drivetemp.sh:29> smartctl -i /dev/ada0
+drivetemp.sh:29> SN=+drivetemp.sh:29> grep Serial
+drivetemp.sh:29> SN=+drivetemp.sh:29> cut -d ' ' -f6
+drivetemp.sh:29> SN=979030411582
+drivetemp.sh:30> [[ '(Min/Max' -ne 0drivetemp.sh:30: division by zero
]]

I'm going to dm you to debug what the script is doing here. For whatever reason the column on /dev/ada0 is returning "(Min/Max" not the number.
 

alexr

Explorer
Joined
Apr 14, 2016
Messages
59
FWIW, this script doesn't get the temp off SAS drives, which don't report the temp as a SMART attribute.
 

j_r0dd

Contributor
Joined
Jan 26, 2015
Messages
134
FWIW, this script doesn't get the temp off SAS drives, which don't report the temp as a SMART attribute.
That is correct. If it's not reporting a temp to SMART it will just return NA. Are there any tools that are used that you know of to monitor those drives other than SMART?
 

alexr

Explorer
Joined
Apr 14, 2016
Messages
59
That is correct. If it's not reporting a temp to SMART it will just return NA. Are there any tools that are used that you know of to monitor those drives other than SMART?

It does report it, but differently:

Code:
freenas% sudo smartctl -A /dev/da0
Password:
smartctl 7.0 2018-12-30 r4883 [FreeBSD 11.3-RELEASE-p14 amd64] (local build)
Copyright (C) 2002-18, Bruce Allen, Christian Franke, www.smartmontools.org

=== START OF READ SMART DATA SECTION ===
Current Drive Temperature:     35 C
Drive Trip Temperature:        85 C

Manufactured in week 38 of year 2017
Specified cycle count over device lifetime:  50000
Accumulated start-stop cycles:  111
Specified load-unload count over device lifetime:  600000
Accumulated load-unload cycles:  1338
Elements in grown defect list: 0
 

j_r0dd

Contributor
Joined
Jan 26, 2015
Messages
134
It does report it, but differently:

Code:
freenas% sudo smartctl -A /dev/da0
Password:
smartctl 7.0 2018-12-30 r4883 [FreeBSD 11.3-RELEASE-p14 amd64] (local build)
Copyright (C) 2002-18, Bruce Allen, Christian Franke, www.smartmontools.org

=== START OF READ SMART DATA SECTION ===
Current Drive Temperature:     35 C
Drive Trip Temperature:        85 C

Manufactured in week 38 of year 2017
Specified cycle count over device lifetime:  50000
Accumulated start-stop cycles:  111
Specified load-unload count over device lifetime:  600000
Accumulated load-unload cycles:  1338
Elements in grown defect list: 0
Ahh ok. I will dm you so I get the right command to pull the correct info for those drives.
 

j_r0dd

Contributor
Joined
Jan 26, 2015
Messages
134
Script has been updated and hopefully is the final version. Thanks to @alexr for suggesting to use the new --json flag in the smartctl commands. This unifies temperature across various devices/vendors. I did increase spacing in the output format to hopefully have better formatting with devices that have longer serials.
 
Last edited:

j_r0dd

Contributor
Joined
Jan 26, 2015
Messages
134
Cleaned up the print code and tweaked formatting. This should cover every scenario now. Enjoy!
 

j_r0dd

Contributor
Joined
Jan 26, 2015
Messages
134
Because crypto and the stock market was bloody last week, I had time to play around with a second version of this script. This one you can use as a cronjob or run from the shell and it will email the results. Unfortunately, email can't interpret ansi escape codes so I had to make this output to a email formatted html temp file, so email clients can display this properly formatted. This will display properly on light and dark modes of email clients.

296D5934-A8D0-468F-8926-BDEFB50F2C7F_1_201_a.jpeg
B6C115E4-26F1-4CF4-B74C-320A59BCDEB1_1_201_a.jpeg
 
Last edited:

j_r0dd

Contributor
Joined
Jan 26, 2015
Messages
134
I unified the 2 versions of the script and added ability to chose a different temperature threshold for ssd's.
 

j_r0dd

Contributor
Joined
Jan 26, 2015
Messages
134
Just added a separate version that works on SCALE
 

Rudi Pittman

Contributor
Joined
Dec 22, 2015
Messages
161
The scale version gives errors with cut/paste. Can you not upload to somewhere like pastebin and provide a link? They have a raw mode that makes cut/paste much easier.
 

j_r0dd

Contributor
Joined
Jan 26, 2015
Messages
134

Rudi Pittman

Contributor
Joined
Dec 22, 2015
Messages
161
Thank you! The colors are a bit hard to read though?
 

Attachments

  • 2021-08-26_10-27.png
    2021-08-26_10-27.png
    64.1 KB · Views: 207
Last edited:
Top