#!/bin/bash

##########################################################
# Copyright (c) 2001-2026 Alexey Kuryakin daqgroup@mail.ru
##########################################################

##########################################################
# Utility to view hardware monitor temperature sensors.
##########################################################
# Temperature sensors information located in:
# /sys/class/hwmon/hwmon0/name        - mon0 source name
# /sys/class/hwmon/hwmon0/temp1_label - mon0 sensor1 label
# /sys/class/hwmon/hwmon0/temp1_input - mon0 sensor1 value
# Temperature values given in milli-Celcius (factor 1000).
##########################################################

#############################################################
# Bash basename and dirname scripts (faster compare commands)
# https://ufoot.org/software/howto/bashbasename/en/
# Two additional functions:
# 1) namebase prints the basename without extension
# 2) fileext prints extension of a file, including "."
#############################################################
function fastbasename() { local name="${1##*/}"; echo "${name%$2}"; };
function fastdirname() { local dir="${1%${1##*/}}"; if [[ "${dir:=./}" != "/" ]]; then dir="${dir%?}"; fi; echo "$dir"; };
function fastnamebase() { local name="${1##*/}"; local base="${name%.*}"; echo "${base:-$name}"; };
function fastfileext() { local name="${1##*/}"; local base="${name%.*}"; local ext="${base:+${name#$base}}"; echo "${ext:-.}"; };

################################
# Current script identification.
################################
readonly startupdir="$(pwd -LP)";
readonly scriptfile="${BASH_SOURCE[0]}";
readonly scriptname="$(fastbasename $scriptfile)";
readonly scriptbase="$(fastbasename $scriptfile .sh)";
readonly scripthome="$(fastdirname  $scriptfile)";
readonly scriptFILE="$(realpath $scriptfile)";
readonly scriptHOME="$(fastdirname  $scriptFILE)";

readonly sudoit="unix sudoit";
readonly hddtemp_lt="$scriptHOME/hddtemp-lt";

function fatal(){
 1>&2 echo -ne "\n$2\n\n";
 exit $1;
};

function do_version(){
 echo "$scriptname version 1.1";
};

function do_print_help(){
 do_version;
cat<<EOF
$scriptname Copyright (c) 2026 Alexey Kuryakin daqgroup@mail.ru
Utility to view hardware monitor temperature sensors.
Usage:
 $scriptname [-options] [s1 s2 …]
Options:
 --version    - print version
 -h,--help    - print help screen
 -l,--list    - print list of hardware monitor sensors
 -n,--numeric - print numeric (no names, only values)
 -s,--short   - print short   (no names, only values)
 -r,--raw     - print raw values (not scaled)
 -0,--0       - format values as XX       ( integer )
 -1,--1       - format values as XX.X     ( default )
 -2,--2       - format values as XX.XX    ( 12.34   )
 -3,--3       - format values as XX.XXX   ( 12.345  )
 -b,--bias b  - set bias (substract b from value)
 -m,--mult m  - set multiplier factor val=(val-bias)*mult
 -sct,--sct   - use SMART control to get HDD temperatures
Parameters:
 s1 s2 …      - name(s) of sensor(s) or HDD device(s)
Examples:
 $scriptname --help            # print help screen
 $scriptname --version         # print version
 $scriptname                   # print sensor names & values
 $scriptname --list            # print sensor names  only
 $scriptname --short           # print sensor values only
 $scriptname -n -r             # print sensor raw values only
 $scriptname amdgpu/edge       # print sensor by name 'amdgpu/edge'
 $scriptname -sct /dev/sda     # print SMART HDD temperature of /dev/sda
 $scriptname -b 30 amdgpu/edge # print sensor value with bias 30 subtracted
EOF
};

############################
# Program internal variables
############################
declare -i scale=1;        # precision for bc
declare -i optraw=0;       # option --raw
declare -i optsct=0;       # use SMART control
declare optbias="0";       # bias to subrtract
declare optmult="1";       # multiplier factor
declare -i optlist=0;      # option --list
declare -i optnumeric=0;   # option --numeric
declare -i hwsensors=0;    # sensor counter
declare -a hwlabfile=();   # sensor label file
declare -a hwlabname=();   # sensor name/label array

#########################################
# List all hwmon temperature label files.
#########################################
function list_hwmon_label_files(){
 find /sys/class/hwmon/hwmon*/temp*_label | xargs;
};

#####################################
# Inialize hwmon temperature sensors:
# array name/label and label file(s).
#####################################
function init_hwmon(){
 if [[ $hwsensors -eq 0 ]]; then
  for fn in $(list_hwmon_label_files); do
   hwlabname+=("$(cat $(dirname $fn)/name)/$(cat $fn)");
   hwlabfile+=("$fn");
   let hwsensors++;
  done;
 fi;
};

##########################################
# Print list of known temperature sensors.
##########################################
function list_hwmon_sensors(){
 init_hwmon;
 for sen in "${hwlabname[@]}"; do
  echo "$sen";
 done;
};

#######################################
# Get raw temperature by label file $1.
#######################################
function getlabtemp(){
 if [[ -n $1 ]]; then
  local temp="$(cat ${1/label/input})";
  if [[ -n $temp ]]; then
   echo "$temp";
  fi;
 fi;
};

################################
# Print sensor name if required.
################################
function print_sen_name(){
 if [[ $optnumeric -eq 0 ]] && [[ -n $1 ]]; then
  printf "%-20s " "$1:";
 fi;
};

####################################################
# Print value with scale factor (val/fac-bias)*mult.
####################################################
function print_scaled_val(){
 local val="${1:-0}"; # value
 local fac="${2:-1}"; # factor
 if [[ $optraw -eq 0 ]]; then
  if [[ "$optmult" = "0" ]]; then
   echo "scale=$scale; (($val/$fac-$optbias)*$optmult)" | bc;
  else
   local dat="$(echo "(($val/$fac-$optbias)*$optmult)" | bc)";
   env LANG=C printf "%1.*f\n" "$scale" "$dat";
  fi;
 else
  echo "$val";
 fi;
};

###################################
# Print list of sensor name: value.
# Sensor name/label passed in "$@".
###################################
function print_hwmon_sensors(){
 init_hwmon;
 for sen in "$@"; do
  local -i ind=0; local -i suc=0;
  for sor in "${hwlabname[@]}"; do
   if [[ $sen = $sor ]]; then
    if [[ $suc -eq 0 ]]; then
     print_sen_name "$sen";
     local lab="${hwlabfile[$ind]}";
     local val="$(getlabtemp $lab)";
     print_scaled_val "$val" 1000;
    fi;
    let suc++;
   fi;
   let ind++;
  done;
  if [[ $suc -eq 0 ]]; then
   print_sen_name "$sen";
   local val="00000";
   print_scaled_val "$val" 1000;
  fi;
 done;
};

######################################
# Print Current Temperature from SMART
######################################
function getsmartcurrtemp(){
 if [[ -n $1 ]]; then
  if [[ -x $hddtemp_lt ]]; then
   $sudoit $hddtemp_lt -n "$1";
   return;
  fi;
  $sudoit smartctl -x "$1" \
  | grep ' Celsius' \
  | grep -E '(^|Current )Temperature:' \
  | xargs | sed -Ee 's/.*\s([0-9\.]*)\s.*/\1/';
 fi;
};

############################
# Print list of SCT sensors.
############################
function list_sct_sensors(){
 find /dev -type b -name 'sd[a-z]'   2>/dev/null;
 find /dev -type c -name 'nvme[0-9]' 2>/dev/null;
};

#########################################################
# Print Current Temperature from SMART for all $* devices
#########################################################
function print_sct_sensors(){
 for sen in "$@"; do
  local val="00000";
  if [[ "${sen:0:5}" = "/dev/" ]]; then
   val="$(getsmartcurrtemp "$sen")";
  fi;
  val="${val:-00000}";
  print_sen_name "$sen";
  print_scaled_val "$val";
 done;
};

function main(){
 while [[ -n $1 ]]; do
  case $1 in
   --version)             do_version; return 0; ;;
   -h|-help|--help)       do_print_help; return 0; ;;
   -l|-list|--list)       let optlist=1; ;;
   -s|-short|--short)     let optnumeric=1; ;;
   -n|-numeric|--numeric) let optnumeric=1; ;;
   -r|-raw|--raw)         let optraw=1; ;;
   -0|--0)                let scale=0; ;;
   -1|--1)                let scale=1; ;;
   -2|--2)                let scale=2; ;;
   -3|--3)                let scale=3; ;;
   -sct|--sct)            let optsct=1; ;;
   -b|-bias|--bias)       shift; optbias="$1"; ;;
   -m|-mult|--mult)       shift; optmult="$1"; ;;
   -*)                    fatal 1 "Error: bad option $1"; ;;
   *)                     break; ;;
  esac;
  shift;
 done;
 if [[ $optlist -eq 1 ]]; then
  if [[ $optsct -eq 0 ]]; then
   list_hwmon_sensors;
  else
   list_sct_sensors;
  fi;
  return;
 fi;
 if [[ $optsct -eq 0 ]]; then
  init_hwmon;
  if [[ -n $1 ]]; then
   print_hwmon_sensors "$@";
  else
   print_hwmon_sensors "${hwlabname[@]}";
  fi;
 else
  if [[ -n $1 ]]; then
   print_sct_sensors "$@";
  else
   print_sct_sensors $(list_sct_sensors);
  fi;
 fi;
};

main "$@";

##############
## END OF FILE
##############
