Merge branch 'main' into integration-eremem

This commit is contained in:
rmm 2024-05-25 14:53:42 +02:00
commit b6a9a7f45d
2 changed files with 160 additions and 40 deletions

View File

@ -61,12 +61,12 @@ function install_packages {
# If the 'sensors' command is not available, prompt the user to install lm-sensors # If the 'sensors' command is not available, prompt the user to install lm-sensors
read -p "lm-sensors is not installed. Would you like to install it? (y/n) " choice read -p "lm-sensors is not installed. Would you like to install it? (y/n) " choice
case "$choice" in case "$choice" in
y | Y) [yY])
# If the user chooses to install lm-sensors, update the package list and install the package # If the user chooses to install lm-sensors, update the package list and install the package
apt-get update apt-get update
apt-get install lm-sensors apt-get install lm-sensors
;; ;;
n | N) [nN])
# If the user chooses not to install lm-sensors, exit the script with a zero status code # If the user chooses not to install lm-sensors, exit the script with a zero status code
msg "Decided to not install lm-sensors. The mod cannot run without it. Exiting..." msg "Decided to not install lm-sensors. The mod cannot run without it. Exiting..."
exit 0 exit 0
@ -80,6 +80,7 @@ function install_packages {
} }
function configure { function configure {
sensorsDetected=false
local sensorsOutput=$(sensors -j) local sensorsOutput=$(sensors -j)
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err "Sensor output error.\n\nCommand output:\n${sensorsOutput}\n\nExiting...\n" err "Sensor output error.\n\nCommand output:\n${sensorsOutput}\n\nExiting...\n"
@ -111,10 +112,8 @@ function configure {
if [ -n "$CPU_ADDRESS_PREFIX" ]; then if [ -n "$CPU_ADDRESS_PREFIX" ]; then
msg "Detected sensors:\n$(echo "$sensorsOutput" | grep -o "\"${CPU_ADDRESS_PREFIX}[^\"]*\"" | sed 's/"//g')" msg "Detected sensors:\n$(echo "$sensorsOutput" | grep -o "\"${CPU_ADDRESS_PREFIX}[^\"]*\"" | sed 's/"//g')"
fi else
# If cpu is not known, ask the user for input # If cpu is not known, ask the user for input
if [ -z "$CPU_ADDRESS_PREFIX" ]; then
warn "Could not automatically detect the CPU temperature sensor. Please configure it manually." warn "Could not automatically detect the CPU temperature sensor. Please configure it manually."
# Ask user for CPU information # Ask user for CPU information
# Inform the user and prompt them to press any key to continue # Inform the user and prompt them to press any key to continue
@ -131,6 +130,8 @@ function configure {
if [[ -z "$CPU_ADDRESS_PREFIX" || -z "$CPU_ITEM_PREFIX" ]]; then if [[ -z "$CPU_ADDRESS_PREFIX" || -z "$CPU_ITEM_PREFIX" ]]; then
warn "The CPU configuration is not complete. Temperatures will not be available." warn "The CPU configuration is not complete. Temperatures will not be available."
else
sensorsDetected=true
fi fi
# Check if HDD/SSD data is installed # Check if HDD/SSD data is installed
@ -140,6 +141,7 @@ function configure {
if (echo "$sensorsOutput" | grep -q "drivetemp-scsi-"); then if (echo "$sensorsOutput" | grep -q "drivetemp-scsi-"); then
msg "Detected sensors:\n$(echo "$sensorsOutput" | grep -o '"drivetemp-scsi[^"]*"' | sed 's/"//g')" msg "Detected sensors:\n$(echo "$sensorsOutput" | grep -o '"drivetemp-scsi[^"]*"' | sed 's/"//g')"
enableHddTemp=true enableHddTemp=true
sensorsDetected=true
else else
warn "Kernel module \"drivetemp\" is not installed. HDD/SDD temperatures will not be available." warn "Kernel module \"drivetemp\" is not installed. HDD/SDD temperatures will not be available."
enableHddTemp=false enableHddTemp=false
@ -154,6 +156,7 @@ function configure {
if (echo "$sensorsOutput" | grep -q "nvme-"); then if (echo "$sensorsOutput" | grep -q "nvme-"); then
msg "Detected sensors:\n$(echo "$sensorsOutput" | grep -o '"nvme[^"]*"' | sed 's/"//g')" msg "Detected sensors:\n$(echo "$sensorsOutput" | grep -o '"nvme[^"]*"' | sed 's/"//g')"
enableNvmeTemp=true enableNvmeTemp=true
sensorsDetected=true
else else
warn "No NVMe temperature sensors found." warn "No NVMe temperature sensors found."
enableNvmeTemp=false enableNvmeTemp=false
@ -164,11 +167,29 @@ function configure {
if (echo "$sensorsOutput" | grep -q "fan[0-9]*_input"); then if (echo "$sensorsOutput" | grep -q "fan[0-9]*_input"); then
msg "Detected fan speed sensors:\n$(echo "$sensorsOutput" | grep -o 'fan[0-9]*_input[^"]*')" msg "Detected fan speed sensors:\n$(echo "$sensorsOutput" | grep -o 'fan[0-9]*_input[^"]*')"
enableFanSpeed=true enableFanSpeed=true
sensorsDetected=true
else else
warn "No fan speed sensors found." warn "No fan speed sensors found."
enableFanSpeed=false enableFanSpeed=false
fi fi
if [ $sensorsDetected = true ]; then
msg "\nSelect a unit for temperature readings..."
read -p "Type C for Celsius or F for Fahrenheit and press Enter: " TEMP_UNIT
case "$TEMP_UNIT" in
[cC])
TEMP_UNIT="C"
;;
[fF])
TEMP_UNIT="F"
;;
*)
warn "Invalid selection. Temperatures will be presented in degrees Celsius."
TEMP_UNIT="C"
;;
esac
fi
echo # add a new line echo # add a new line
} }
@ -190,7 +211,10 @@ function install_mod {
cp "$nodespm" "$BACKUP_DIR/Nodes.pm.$timestamp" cp "$nodespm" "$BACKUP_DIR/Nodes.pm.$timestamp"
msg "Backup of \"$nodespm\" saved to \"$BACKUP_DIR/Nodes.pm.$timestamp\"." msg "Backup of \"$nodespm\" saved to \"$BACKUP_DIR/Nodes.pm.$timestamp\"."
sed -i '/my $dinfo = df('\''\/'\'', 1);/i\'$'\t''$res->{sensorsOutput} = `sensors -j`;\n\t# sanitize JSON output\n\t$res->{sensorsOutput} =~ s/ERROR:.+\\s(\\w+):\\s(.+)/\\"$1\\": 0.000,/g;\n\t$res->{sensorsOutput} =~ s/ERROR:.+\\s(\\w+)!/\\"$1\\": 0.000,/g;\n\t$res->{sensorsOutput} =~ s/,(.*[.\\n]*.+})/$1/g;\n' "$nodespm" # WTF: sensors -f used for Fahrenheit breaks the fan speeds :|
#local sensorsCmd=$([[ "$TEMP_UNIT" = "F" ]] && echo "sensors -j -f" || echo "sensors -j")
local sensorsCmd="sensors -j"
sed -i '/my \$dinfo = df('\''\/'\'', 1);/i\'$'\t''$res->{sensorsOutput} = `'"$sensorsCmd"'`;\n\t# sanitize JSON output\n\t$res->{sensorsOutput} =~ s/ERROR:.+\\s(\\w+):\\s(.+)/\\"$1\\": 0.000,/g;\n\t$res->{sensorsOutput} =~ s/ERROR:.+\\s(\\w+)!/\\"$1\\": 0.000,/g;\n\t$res->{sensorsOutput} =~ s/,(.*[.\\n]*.+})/$1/g;\n' "$nodespm"
msg "Sensors' output added to \"$nodespm\"." msg "Sensors' output added to \"$nodespm\"."
else else
warn "Sensors' output already integrated in in \"$nodespm\"." warn "Sensors' output already integrated in in \"$nodespm\"."
@ -202,6 +226,7 @@ function install_mod {
cp "$pvemanagerlibjs" "$BACKUP_DIR/pvemanagerlib.js.$timestamp" cp "$pvemanagerlibjs" "$BACKUP_DIR/pvemanagerlib.js.$timestamp"
msg "Backup of \"$pvemanagerlibjs\" saved to \"$BACKUP_DIR/pvemanagerlib.js.$timestamp\"." msg "Backup of \"$pvemanagerlibjs\" saved to \"$BACKUP_DIR/pvemanagerlib.js.$timestamp\"."
local tempHelperCtorParams=$([[ "$TEMP_UNIT" = "F" ]] && echo '{srcUnit: PVE.mod.TempHelper.CELSIUS, dstUnit: PVE.mod.TempHelper.FAHRENHEIT}' || echo '{srcUnit: PVE.mod.TempHelper.CELSIUS, dstUnit: PVE.mod.TempHelper.CELSIUS}')
# Expand space in StatusView # Expand space in StatusView
sed -i "/Ext.define('PVE\.node\.StatusView'/,/\},/ { sed -i "/Ext.define('PVE\.node\.StatusView'/,/\},/ {
s/\(bodyPadding:\) '[^']*'/\1 '20 15 20 15'/ s/\(bodyPadding:\) '[^']*'/\1 '20 15 20 15'/
@ -210,6 +235,94 @@ function install_mod {
}" "$pvemanagerlibjs" }" "$pvemanagerlibjs"
msg "Expanded space in \"$pvemanagerlibjs\"." msg "Expanded space in \"$pvemanagerlibjs\"."
sed -i "/^Ext.define('PVE.node.StatusView'/i\
Ext.define('PVE.mod.TempHelper', {\n\
//singleton: true,\n\
\n\
requires: ['Ext.util.Format'],\n\
\n\
statics: {\n\
CELSIUS: 0,\n\
FAHRENHEIT: 1\n\
},\n\
\n\
srcUnit: null,\n\
dstUnit: null,\n\
\n\
isValidUnit: function (unit) {\n\
return (\n\
Ext.isNumber(unit) && (unit === this.self.CELSIUS || unit === this.self.FAHRENHEIT)\n\
);\n\
},\n\
\n\
constructor: function (config) {\n\
this.srcUnit = config && this.isValidUnit(config.srcUnit) ? config.srcUnit : this.self.CELSIUS;\n\
this.dstUnit = config && this.isValidUnit(config.dstUnit) ? config.dstUnit : this.self.CELSIUS;\n\
},\n\
\n\
toFahrenheit: function (tempCelsius) {\n\
return Ext.isNumber(tempCelsius)\n\
? tempCelsius * 9 / 5 + 32\n\
: NaN;\n\
},\n\
\n\
toCelsius: function (tempFahrenheit) {\n\
return Ext.isNumber(tempFahrenheit)\n\
? (tempFahrenheit - 32) * 5 / 9\n\
: NaN;\n\
},\n\
\n\
getTemp: function (value) {\n\
if (this.srcUnit !== this.dstUnit) {\n\
switch (this.srcUnit) {\n\
case this.self.CELSIUS:\n\
switch (this.dstUnit) {\n\
case this.self.FAHRENHEIT:\n\
return this.toFahrenheit(value);\n\
\n\
default:\n\
Ext.raise({\n\
msg:\n\
'Unsupported destination temperature unit: ' + this.dstUnit,\n\
});\n\
}\n\
case this.self.FAHRENHEIT:\n\
switch (this.dstUnit) {\n\
case this.self.CELSIUS:\n\
return this.toCelsius(value);\n\
\n\
default:\n\
Ext.raise({\n\
msg:\n\
'Unsupported destination temperature unit: ' + this.dstUnit,\n\
});\n\
}\n\
default:\n\
Ext.raise({\n\
msg: 'Unsupported source temperature unit: ' + this.srcUnit,\n\
});\n\
}\n\
} else {\n\
return value;\n\
}\n\
},\n\
\n\
getUnit: function(plainText) {\n\
switch (this.dstUnit) {\n\
case this.self.CELSIUS:\n\
return plainText !== true ? '\°C' : '\\\'C';\n\
\n\
case this.self.FAHRENHEIT:\n\\n\
return plainText !== true ? '\°F' : '\\\'F';\n\
\n\
default:\n\
Ext.raise({\n\
msg: 'Unsupported destination temperature unit: ' + this.srcUnit,\n\
});\n\
}\n\
},\n\
});\n" "$pvemanagerlibjs"
sed -i "/^Ext.define('PVE.node.StatusView',/ { sed -i "/^Ext.define('PVE.node.StatusView',/ {
:a; :a;
/items:/!{N;ba;} /items:/!{N;ba;}
@ -229,6 +342,7 @@ function install_mod {
const addressPrefix = \"$CPU_ADDRESS_PREFIX\";\n\ const addressPrefix = \"$CPU_ADDRESS_PREFIX\";\n\
const cpuItemPrefix = \"$CPU_ITEM_PREFIX\";\n\ const cpuItemPrefix = \"$CPU_ITEM_PREFIX\";\n\
const cpuTempCaption = \"$CPU_TEMP_CAPTION\";\n\ const cpuTempCaption = \"$CPU_TEMP_CAPTION\";\n\
const cpuTempHelper = Ext.create('PVE.mod.TempHelper', $tempHelperCtorParams);\n\
// display configuration\n\ // display configuration\n\
const itemsPerRow = $CPU_ITEMS_PER_ROW;\n\ const itemsPerRow = $CPU_ITEMS_PER_ROW;\n\
// ---\n\ // ---\n\
@ -250,11 +364,11 @@ function install_mod {
let tempVal = NaN, tempMax = NaN, tempCrit = NaN;\n\ let tempVal = NaN, tempMax = NaN, tempCrit = NaN;\n\
Object.keys(items[coreKey]).forEach((secondLevelKey) => {\n\ Object.keys(items[coreKey]).forEach((secondLevelKey) => {\n\
if (secondLevelKey.endsWith('_input')) {\n\ if (secondLevelKey.endsWith('_input')) {\n\
tempVal = parseFloat(items[coreKey][secondLevelKey]);\n\ tempVal = cpuTempHelper.getTemp(parseFloat(items[coreKey][secondLevelKey]));\n\
} else if (secondLevelKey.endsWith('_max')) {\n\ } else if (secondLevelKey.endsWith('_max')) {\n\
tempMax = parseFloat(items[coreKey][secondLevelKey]);\n\ tempMax = cpuTempHelper.getTemp(parseFloat(items[coreKey][secondLevelKey]));\n\
} else if (secondLevelKey.endsWith('_crit')) {\n\ } else if (secondLevelKey.endsWith('_crit')) {\n\
tempCrit = parseFloat(items[coreKey][secondLevelKey]);\n\ tempCrit = cpuTempHelper.getTemp(parseFloat(items[coreKey][secondLevelKey]));\n\
}\n\ }\n\
});\n\ });\n\
if (!isNaN(tempVal)) {\n\ if (!isNaN(tempVal)) {\n\
@ -269,9 +383,9 @@ function install_mod {
let tempIndex = coreKey.match(/\\\S+\\\s*(\\\d+)/);\n\ let tempIndex = coreKey.match(/\\\S+\\\s*(\\\d+)/);\n\
if (tempIndex !== null && tempIndex.length > 1) {\n\ if (tempIndex !== null && tempIndex.length > 1) {\n\
tempIndex = tempIndex[1];\n\ tempIndex = tempIndex[1];\n\
tempStr = \`\${cpuTempCaption}&nbsp;\${tempIndex}:&nbsp;<span style=\"\${tempStyle}\">\${tempVal}&deg;C</span>\`;\n\ tempStr = \`\${cpuTempCaption}&nbsp;\${tempIndex}:&nbsp;<span style=\"\${tempStyle}\">\${Ext.util.Format.number(tempVal, '0.#')}\${cpuTempHelper.getUnit()}</span>\`;\n\
} else {\n\ } else {\n\
tempStr = \`\${cpuTempCaption}:&nbsp;\${tempVal}&deg;C\`;\n\ tempStr = \`\${cpuTempCaption}:&nbsp;\${Ext.util.Format.number(tempVal, '0.#')}\${cpuTempHelper.getUnit()}\`;\n\
}\n\ }\n\
cpuTemps.push(tempStr);\n\ cpuTemps.push(tempStr);\n\
}\n\ }\n\
@ -315,6 +429,7 @@ function install_mod {
// sensors configuration\n\ // sensors configuration\n\
const addressPrefix = \"drivetemp-scsi-\";\n\ const addressPrefix = \"drivetemp-scsi-\";\n\
const sensorName = \"temp1\";\n\ const sensorName = \"temp1\";\n\
const tempHelper = Ext.create('PVE.mod.TempHelper', $tempHelperCtorParams);\n\
// display configuration\n\ // display configuration\n\
const itemsPerRow = ${HDD_ITEMS_PER_ROW};\n\ const itemsPerRow = ${HDD_ITEMS_PER_ROW};\n\
// ---\n\ // ---\n\
@ -331,11 +446,11 @@ function install_mod {
let tempVal = NaN, tempMax = NaN, tempCrit = NaN;\n\ let tempVal = NaN, tempMax = NaN, tempCrit = NaN;\n\
Object.keys(objValue[drvKey][sensorName]).forEach((secondLevelKey) => {\n\ Object.keys(objValue[drvKey][sensorName]).forEach((secondLevelKey) => {\n\
if (secondLevelKey.endsWith('_input')) {\n\ if (secondLevelKey.endsWith('_input')) {\n\
tempVal = parseFloat(objValue[drvKey][sensorName][secondLevelKey]);\n\ tempVal = tempHelper.getTemp(parseFloat(objValue[drvKey][sensorName][secondLevelKey]));\n\
} else if (secondLevelKey.endsWith('_max')) {\n\ } else if (secondLevelKey.endsWith('_max')) {\n\
tempMax = parseFloat(objValue[drvKey][sensorName][secondLevelKey]);\n\ tempMax = tempHelper.getTemp(parseFloat(objValue[drvKey][sensorName][secondLevelKey]));\n\
} else if (secondLevelKey.endsWith('_crit')) {\n\ } else if (secondLevelKey.endsWith('_crit')) {\n\
tempCrit = parseFloat(objValue[drvKey][sensorName][secondLevelKey]);\n\ tempCrit = tempHelper.getTemp(parseFloat(objValue[drvKey][sensorName][secondLevelKey]));\n\
}\n\ }\n\
});\n\ });\n\
if (!isNaN(tempVal)) {\n\ if (!isNaN(tempVal)) {\n\
@ -346,7 +461,7 @@ function install_mod {
if (!isNaN(tempCrit) && tempVal >= tempCrit) {\n\ if (!isNaN(tempCrit) && tempVal >= tempCrit) {\n\
tempStyle = 'color: red; font-weight: bold;';\n\ tempStyle = 'color: red; font-weight: bold;';\n\
}\n\ }\n\
const tempStr = \`Drive&nbsp;\${index + 1}:&nbsp;<span style=\"\${tempStyle}\">\${tempVal}&deg;C</span>\`;\n\ const tempStr = \`Drive&nbsp;\${index + 1}:&nbsp;<span style=\"\${tempStyle}\">\${Ext.util.Format.number(tempVal, '0.#')}\${tempHelper.getUnit()}</span>\`;\n\
temps.push(tempStr);\n\ temps.push(tempStr);\n\
}\n\ }\n\
} catch(e) { /*_*/ }\n\ } catch(e) { /*_*/ }\n\
@ -377,6 +492,7 @@ function install_mod {
// sensors configuration\n\ // sensors configuration\n\
const addressPrefix = \"nvme-pci-\";\n\ const addressPrefix = \"nvme-pci-\";\n\
const sensorName = \"Composite\";\n\ const sensorName = \"Composite\";\n\
const tempHelper = Ext.create('PVE.mod.TempHelper', $tempHelperCtorParams);\n\
// display configuration\n\ // display configuration\n\
const itemsPerRow = ${NVME_ITEMS_PER_ROW};\n\ const itemsPerRow = ${NVME_ITEMS_PER_ROW};\n\
// ---\n\ // ---\n\
@ -393,11 +509,11 @@ function install_mod {
let tempVal = NaN, tempMax = NaN, tempCrit = NaN;\n\ let tempVal = NaN, tempMax = NaN, tempCrit = NaN;\n\
Object.keys(objValue[nvmeKey][sensorName]).forEach((secondLevelKey) => {\n\ Object.keys(objValue[nvmeKey][sensorName]).forEach((secondLevelKey) => {\n\
if (secondLevelKey.endsWith('_input')) {\n\ if (secondLevelKey.endsWith('_input')) {\n\
tempVal = parseFloat(objValue[nvmeKey][sensorName][secondLevelKey]);\n\ tempVal = tempHelper.getTemp(parseFloat(objValue[nvmeKey][sensorName][secondLevelKey]));\n\
} else if (secondLevelKey.endsWith('_max')) {\n\ } else if (secondLevelKey.endsWith('_max')) {\n\
tempMax = parseFloat(objValue[nvmeKey][sensorName][secondLevelKey]);\n\ tempMax = tempHelper.getTemp(parseFloat(objValue[nvmeKey][sensorName][secondLevelKey]));\n\
} else if (secondLevelKey.endsWith('_crit')) {\n\ } else if (secondLevelKey.endsWith('_crit')) {\n\
tempCrit = parseFloat(objValue[nvmeKey][sensorName][secondLevelKey]);\n\ tempCrit = tempHelper.getTemp(parseFloat(objValue[nvmeKey][sensorName][secondLevelKey]));\n\
}\n\ }\n\
});\n\ });\n\
if (!isNaN(tempVal)) {\n\ if (!isNaN(tempVal)) {\n\
@ -408,7 +524,7 @@ function install_mod {
if (!isNaN(tempCrit) && tempVal >= tempCrit) {\n\ if (!isNaN(tempCrit) && tempVal >= tempCrit) {\n\
tempStyle = 'color: red; font-weight: bold;';\n\ tempStyle = 'color: red; font-weight: bold;';\n\
}\n\ }\n\
const tempStr = \`Drive&nbsp;\${index + 1}:&nbsp;<span style=\"\${tempStyle}\">\${tempVal}&deg;C</span>\`;\n\ const tempStr = \`Drive&nbsp;\${index + 1}:&nbsp;<span style=\"\${tempStyle}\">\${Ext.util.Format.number(tempVal, '0.#')}\${tempHelper.getUnit()}</span>\`;\n\
temps.push(tempStr);\n\ temps.push(tempStr);\n\
}\n\ }\n\
} catch(e) { /*_*/ }\n\ } catch(e) { /*_*/ }\n\
@ -553,6 +669,8 @@ function install_mod {
restart_proxy restart_proxy
msg "Installation completed" msg "Installation completed"
msg "Clear the browser cache to ensure all changes are visualized."
else else
warn "Sensor display items already added to the summary panel in \"$pvemanagerlibjs\"." warn "Sensor display items already added to the summary panel in \"$pvemanagerlibjs\"."
fi fi

View File

@ -6,7 +6,7 @@ If you find this helpful, a small donation is appreciated, [![Donate](https://ww
## Node sensor readings view ## Node sensor readings view
(Tested compatibility: 7.x - 8.2) (Tested compatibility: 7.x - 8.2)
This bash script installs a modification to the Proxmox Virtual Environment (PVE) web user interface (UI) to display sensor readings in a flexible and readable manner. Supported are CPU, NVMe/HDD/SSD temperatures and fan speeds. This bash script installs a modification to the Proxmox Virtual Environment (PVE) web user interface (UI) to display sensor readings in a flexible and readable manner. Supported are CPU, NVMe/HDD/SSD temperatures (Celsius/Fahrenheit) and fan speeds.
The modification includes three main steps: The modification includes three main steps:
@ -22,8 +22,10 @@ For HDDs/SSDs readings to work, the kernel module *drivetemp* must be installed.
``` ```
apt-get install lm-sensors apt-get install lm-sensors
wget https://raw.githubusercontent.com/Meliox/PVE-mods/main/pve-mod-gui-sensors.sh wget https://raw.githubusercontent.com/Meliox/PVE-mods/main/pve-mod-gui-sensors.sh
bash pve-mod-gui-sensors.sh
``` ```
Or use git clone. Or use git clone.
Then clear the browser cache to ensure all changes are visualized.
![Promxox temp mod](https://github.com/Meliox/PVE-mods/blob/main/pve-mod-sensors.png?raw=true) ![Promxox temp mod](https://github.com/Meliox/PVE-mods/blob/main/pve-mod-sensors.png?raw=true)