From be2e7089b783650a11fe6d08d94933681514b5c6 Mon Sep 17 00:00:00 2001 From: Meliox Date: Sat, 6 Sep 2025 13:20:04 +0200 Subject: [PATCH] fix UPS gui --- pve-mod-gui-sensors.sh | 455 +++++++++++++++++++++-------------------- 1 file changed, 235 insertions(+), 220 deletions(-) diff --git a/pve-mod-gui-sensors.sh b/pve-mod-gui-sensors.sh index ceb61eb..ad62347 100644 --- a/pve-mod-gui-sensors.sh +++ b/pve-mod-gui-sensors.sh @@ -861,226 +861,6 @@ Ext.define('PVE.mod.TempHelper', {\n\ }" "$PVE_MANAGER_LIB_JS_FILE" fi - if [ $ENABLE_UPS = true ]; then - # Add UPS status display - sed -i "/^Ext.define('PVE.node.StatusView',/ { - :a; - /items:/!{N;ba;} - :b; - /'thermal.*},/!{N;bb;} - a\ - \\ - {\n\ - xtype: 'box',\n\ - colspan: 2,\n\ - html: gettext('UPS'),\n\ - },\n\ - {\n\ - itemId: 'upsc',\n\ - colspan: 2,\n\ - printBar: false,\n\ - title: gettext('Device'),\n\ - iconCls: 'fa fa-fw fa-battery-three-quarters',\n\ - textField: 'upsc',\n\ - renderer: function(value) {\n\ - console.log(value);\n\ -\n\ - let objValue = {};\n\ - try {\n\ - // Parse the UPS data\n\ - if (typeof value === 'string') {\n\ - const lines = value.split('\n');\n\ - lines.forEach(line => {\n\ - const colonIndex = line.indexOf(':');\n\ - if (colonIndex > 0) {\n\ - const key = line.substring(0, colonIndex).trim();\n\ - const val = line.substring(colonIndex + 1).trim();\n\ - objValue[key] = val;\n\ - }\n\ - });\n\ - } else if (typeof value === 'object') {\n\ - objValue = value || {};\n\ - }\n\ - } catch(e) {\n\ - objValue = {};\n\ - }\n\ -\n\ - // If objValue is null or empty, return N/A\n\ - if (!objValue || Object.keys(objValue).length === 0) {\n\ - return '
N/A
';\n\ - }\n\ -\n\ - // Helper function to get status color\n\ - function getStatusColor(status) {\n\ - if (!status) return '#999';\n\ - const statusUpper = status.toUpperCase();\n\ - if (statusUpper.includes('OL')) return 'white'; // Green for online\n\ - if (statusUpper.includes('OB')) return '#d9534f'; // Red for on battery\n\ - if (statusUpper.includes('LB')) return '#d9534f'; // Red for low battery\n\ - return '#f0ad4e'; // Orange for other states\n\ - }\n\ -\n\ - // Helper function to get load/charge color\n\ - function getPercentageColor(value, isLoad = false) {\n\ - if (!value || isNaN(value)) return '#999';\n\ - const num = parseFloat(value);\n\ - if (isLoad) {\n\ - if (num >= 80) return '#d9534f'; // Red for high load\n\ - if (num >= 60) return '#f0ad4e'; // Orange for medium load\n\ - return '#white'; // Green for low load\n\ - } else {\n\ - // For battery charge\n\ - if (num <= 20) return '#d9534f'; // Red for low charge\n\ - if (num <= 50) return '#f0ad4e'; // Orange for medium charge\n\ - return '#white'; // Green for good charge\n\ - }\n\ - }\n\ -\n\ - // Helper function to format runtime\n\ - function formatRuntime(seconds) {\n\ - if (!seconds || isNaN(seconds)) return 'N/A';\n\ - const mins = Math.floor(seconds / 60);\n\ - const secs = seconds % 60;\n\ - return \`\${mins}m \${secs}s\`;\n\ - }\n\ -\n\ - // Extract key UPS information\n\ - const batteryCharge = objValue['battery.charge'];\n\ - const batteryRuntime = objValue['battery.runtime'];\n\ - const inputVoltage = objValue['input.voltage'];\n\ - const upsLoad = objValue['ups.load'];\n\ - const upsStatus = objValue['ups.status'];\n\ - const upsModel = objValue['ups.model'] || objValue['device.model'];\n\ - const testResult = objValue['ups.test.result'];\n\ - const batteryChargeLow = objValue['battery.charge.low'];\n\ - const batteryRuntimeLow = objValue['battery.runtime.low'];\n\ - const upsRealPowerNominal = objValue['ups.realpower.nominal'];\n\ - const batteryMfrDate = objValue['battery.mfr.date'];\n\ -\n\ - // Build the status display\n\ - let displayItems = [];\n\ -\n\ - // First line: Model info\n\ - let modelLine = '';\n\ - if (upsModel) {\n\ - modelLine = \`\${upsModel}\`;\n\ - } else {\n\ - modelLine = \`N/A\`;\n\ - }\n\ - displayItems.push(modelLine);\n\ -\n\ - // Main status line with all metrics\n\ - let statusLine = '';\n\ -\n\ - // Status\n\ - if (upsStatus) {\n\ - const statusUpper = upsStatus.toUpperCase();\n\ - let statusText = 'Unknown';\n\ - let statusColor = '#f0ad4e';\n\ -\n\ - if (statusUpper.includes('OL')) {\n\ - statusText = 'Online';\n\ - statusColor = 'white'; // White for good status\n\ - } else if (statusUpper.includes('OB')) {\n\ - statusText = 'On Battery';\n\ - statusColor = '#d9534f'; // Red for on battery\n\ - } else if (statusUpper.includes('LB')) {\n\ - statusText = 'Low Battery';\n\ - statusColor = '#d9534f'; // Red for low battery\n\ - } else {\n\ - statusText = upsStatus;\n\ - statusColor = '#f0ad4e'; // Orange for unknown status\n\ - }\n\ -\n\ - statusLine += \`Status: \${statusText}\`;\n\ - } else {\n\ - statusLine += \`Status: N/A\`;\n\ - }\n\ -\n\ - // Battery charge\n\ - if (statusLine) statusLine += ' | ';\n\ - if (batteryCharge) {\n\ - const chargeColor = getPercentageColor(batteryCharge, false);\n\ - statusLine += \`Battery: \${batteryCharge}%\`;\n\ - } else {\n\ - statusLine += \`Battery: N/A\`;\n\ - }\n\ -\n\ - // Load percentage\n\ - if (statusLine) statusLine += ' | ';\n\ - if (upsLoad) {\n\ - const loadColor = getPercentageColor(upsLoad, true);\n\ - statusLine += \`Load: \${upsLoad}%\`;\n\ - } else {\n\ - statusLine += \`Load: N/A\`;\n\ - }\n\ -\n\ - // Runtime\n\ - if (statusLine) statusLine += ' | ';\n\ - if (batteryRuntime) {\n\ - const runtime = parseInt(batteryRuntime);\n\ - const runtimeLowThreshold = batteryRuntimeLow ? parseInt(batteryRuntimeLow) : 600;\n\ - let runtimeColor = 'white';\n\ - if (runtime <= runtimeLowThreshold / 2) runtimeColor = '#d9534f'; // Red if less than half of low threshold\n\ - else if (runtime <= runtimeLowThreshold) runtimeColor = '#f0ad4e'; // Orange if at low threshold\n\ -\n\ - statusLine += \`Runtime: \${formatRuntime(runtime)}\`;\n\ - } else {\n\ - statusLine += \`Runtime: N/A\`;\n\ - }\n\ -\n\ - // Input voltage\n\ - if (statusLine) statusLine += ' | ';\n\ - if (inputVoltage) {\n\ - statusLine += \`Input: \${parseFloat(inputVoltage).toFixed(0)}V\`;\n\ - } else {\n\ - statusLine += \`Input: N/A\`;\n\ - }\n\ -\n\ - // Calculate actual watt usage\n\ - if (statusLine) statusLine += ' | ';\n\ - let actualWattage = null;\n\ - if (upsLoad && upsRealPowerNominal) {\n\ - const load = parseFloat(upsLoad);\n\ - const nominal = parseFloat(upsRealPowerNominal);\n\ - if (!isNaN(load) && !isNaN(nominal)) {\n\ - actualWattage = Math.round((load / 100) * nominal);\n\ - }\n\ - }\n\ -\n\ - // Real power (calculated watt usage)\n\ - if (actualWattage !== null) {\n\ - statusLine += \`Output: \${actualWattage}W\`;\n\ - } else {\n\ - statusLine += \`Output: N/A\`;\n\ - }\n\ -\n\ - displayItems.push(statusLine);\n\ -\n\ - // Combined battery and test line\n\ - let batteryTestLine = '';\n\ - if (batteryMfrDate) {\n\ - batteryTestLine += \`Battery MFD: \${batteryMfrDate}\`;\n\ - } else {\n\ - batteryTestLine += \`Battery MFD: N/A\`;\n\ - }\n\ -\n\ - if (testResult && !testResult.toLowerCase().includes('no test')) {\n\ - const testColor = testResult.toLowerCase().includes('passed') ? 'white' : '#d9534f';\n\ - batteryTestLine += \` | Test: \${testResult}\`;\n\ - } else {\n\ - batteryTestLine += \` | Test: N/A\`;\n\ - }\n\ -\n\ - displayItems.push(batteryTestLine);\n\ -\n\ - // Format the final output\n\ - return '
' + displayItems.join('
') + '
';\n\ - }\n\ - }, - }" "$PVE_MANAGER_LIB_JS_FILE" - fi - if [ $ENABLE_RAM_TEMP = true ]; then # Add Ram temperature display sed -i "/^Ext.define('PVE.node.StatusView',/ { @@ -1170,6 +950,21 @@ Ext.define('PVE.mod.TempHelper', {\n\ }" "$PVE_MANAGER_LIB_JS_FILE" fi + if [ $ENABLE_UPS = true ]; then + local TEMP_JS_FILE="/tmp/ups_widget.js" + generate_ups_widget $TEMP_JS_FILE + + sed -i "/^Ext.define('PVE.node.StatusView',/ { + :a + /items:/!{N;ba;} + :b + /'thermal.*},/!{N;bb;} + r /tmp/ups_widget.js + }" "$PVE_MANAGER_LIB_JS_FILE" + + rm $TEMP_JS_FILE + fi + # Add an empty line to separate modified items as a visual group # NOTE: Check for the presence of items in the reverse order of display local lastItemId="" @@ -1179,6 +974,8 @@ Ext.define('PVE.mod.TempHelper', {\n\ lastItemId="thermalNvme" elif [ $ENABLE_FAN_SPEED = true ]; then lastItemId="speedFan" + elif [ $ENABLE_UPS = true ]; then + lastItemId="UPS" else lastItemId="thermalCpu" fi @@ -1241,6 +1038,224 @@ Ext.define('PVE.mod.TempHelper', {\n\ fi } +generate_ups_widget() { + cat > "$1" <<-EOF + { + xtype: 'box', + colspan: 2, + html: gettext('UPS'), + }, + { + itemId: 'upsc', + colspan: 2, + printBar: false, + title: gettext('Device'), + iconCls: 'fa fa-fw fa-battery-three-quarters', + textField: 'upsc', + renderer: function(value) { + console.log(value); + + let objValue = {}; + try { + // Parse the UPS data + if (typeof value === 'string') { + const lines = value.split('\\n'); + lines.forEach(line => { + const colonIndex = line.indexOf(':'); + if (colonIndex > 0) { + const key = line.substring(0, colonIndex).trim(); + const val = line.substring(colonIndex + 1).trim(); + objValue[key] = val; + } + }); + } else if (typeof value === 'object') { + objValue = value || {}; + } + } catch(e) { + objValue = {}; + } + + // If objValue is null or empty, return N/A + if (!objValue || Object.keys(objValue).length === 0) { + return '
N/A
'; + } + + // Helper function to get status color + function getStatusColor(status) { + if (!status) return '#999'; + const statusUpper = status.toUpperCase(); + if (statusUpper.includes('OL')) return 'white'; // White for online + if (statusUpper.includes('OB')) return '#d9534f'; // Red for on battery + if (statusUpper.includes('LB')) return '#d9534f'; // Red for low battery + return '#f0ad4e'; // Orange for other states + } + + // Helper function to get load/charge color + function getPercentageColor(value, isLoad = false) { + if (!value || isNaN(value)) return '#999'; + const num = parseFloat(value); + if (isLoad) { + if (num >= 80) return '#d9534f'; // Red for high load + if (num >= 60) return '#f0ad4e'; // Orange for medium load + return 'white'; // White for low load + } else { + // For battery charge + if (num <= 20) return '#d9534f'; // Red for low charge + if (num <= 50) return '#f0ad4e'; // Orange for medium charge + return 'white'; // White for good charge + } + } + + // Helper function to format runtime + function formatRuntime(seconds) { + if (!seconds || isNaN(seconds)) return 'N/A'; + const mins = Math.floor(seconds / 60); + const secs = seconds % 60; + return \`\${mins}m \${secs}s\`; + } + + // Extract key UPS information + const batteryCharge = objValue['battery.charge']; + const batteryRuntime = objValue['battery.runtime']; + const inputVoltage = objValue['input.voltage']; + const upsLoad = objValue['ups.load']; + const upsStatus = objValue['ups.status']; + const upsModel = objValue['ups.model'] || objValue['device.model']; + const testResult = objValue['ups.test.result']; + const batteryChargeLow = objValue['battery.charge.low']; + const batteryRuntimeLow = objValue['battery.runtime.low']; + const upsRealPowerNominal = objValue['ups.realpower.nominal']; + const batteryMfrDate = objValue['battery.mfr.date']; + + // Build the status display + let displayItems = []; + + // First line: Model info + let modelLine = ''; + if (upsModel) { + modelLine = \`\${upsModel}\`; + } else { + modelLine = \`N/A\`; + } + displayItems.push(modelLine); + + // Main status line with all metrics + let statusLine = ''; + + // Status + if (upsStatus) { + const statusUpper = upsStatus.toUpperCase(); + let statusText = 'Unknown'; + let statusColor = '#f0ad4e'; + + if (statusUpper.includes('OL')) { + statusText = 'Online'; + statusColor = 'white'; // White for good status + } else if (statusUpper.includes('OB')) { + statusText = 'On Battery'; + statusColor = '#d9534f'; // Red for on battery + } else if (statusUpper.includes('LB')) { + statusText = 'Low Battery'; + statusColor = '#d9534f'; // Red for low battery + } else { + statusText = upsStatus; + statusColor = '#f0ad4e'; // Orange for unknown status + } + + statusLine += \`Status: \${statusText}\`; + } else { + statusLine += \`Status: N/A\`; + } + + // Battery charge + if (statusLine) statusLine += ' | '; + if (batteryCharge) { + const chargeColor = getPercentageColor(batteryCharge, false); + statusLine += \`Battery: \${batteryCharge}%\`; + } else { + statusLine += \`Battery: N/A\`; + } + + // Load percentage + if (statusLine) statusLine += ' | '; + if (upsLoad) { + const loadColor = getPercentageColor(upsLoad, true); + statusLine += \`Load: \${upsLoad}%\`; + } else { + statusLine += \`Load: N/A\`; + } + + // Runtime + if (statusLine) statusLine += ' | '; + if (batteryRuntime) { + const runtime = parseInt(batteryRuntime); + const runtimeLowThreshold = batteryRuntimeLow ? parseInt(batteryRuntimeLow) : 600; + let runtimeColor = 'white'; + if (runtime <= runtimeLowThreshold / 2) runtimeColor = '#d9534f'; // Red if less than half of low threshold + else if (runtime <= runtimeLowThreshold) runtimeColor = '#f0ad4e'; // Orange if at low threshold + + statusLine += \`Runtime: \${formatRuntime(runtime)}\`; + } else { + statusLine += \`Runtime: N/A\`; + } + + // Input voltage + if (statusLine) statusLine += ' | '; + if (inputVoltage) { + statusLine += \`Input: \${parseFloat(inputVoltage).toFixed(0)}V\`; + } else { + statusLine += \`Input: N/A\`; + } + + // Calculate actual watt usage + if (statusLine) statusLine += ' | '; + let actualWattage = null; + if (upsLoad && upsRealPowerNominal) { + const load = parseFloat(upsLoad); + const nominal = parseFloat(upsRealPowerNominal); + if (!isNaN(load) && !isNaN(nominal)) { + actualWattage = Math.round((load / 100) * nominal); + } + } + + // Real power (calculated watt usage) + if (actualWattage !== null) { + statusLine += \`Output: \${actualWattage}W\`; + } else { + statusLine += \`Output: N/A\`; + } + + displayItems.push(statusLine); + + // Combined battery and test line + let batteryTestLine = ''; + if (batteryMfrDate) { + batteryTestLine += \`Battery MFD: \${batteryMfrDate}\`; + } else { + batteryTestLine += \`Battery MFD: N/A\`; + } + + if (testResult && !testResult.toLowerCase().includes('no test')) { + const testColor = testResult.toLowerCase().includes('passed') ? 'white' : '#d9534f'; + batteryTestLine += \` | Test: \${testResult}\`; + } else { + batteryTestLine += \` | Test: N/A\`; + } + + displayItems.push(batteryTestLine); + + // Format the final output + return '
' + displayItems.join('
') + '
'; + } + }, + EOF + + if [[ $? -ne 0 ]]; then + echo "Error: Failed to generate UPS widget code" >&2 + exit 1 + fi +} + # Function to uninstall the modification function uninstall_mod { check_root_privileges