From d96d98b8f481099a9e9625831badd42dd7cbcbd0 Mon Sep 17 00:00:00 2001 From: MacRimi Date: Mon, 6 Oct 2025 23:40:54 +0200 Subject: [PATCH] Update AppImage --- AppImage/components/hardware.tsx | 265 ++++++++++++++++--------------- AppImage/scripts/flask_server.py | 35 +++- 2 files changed, 172 insertions(+), 128 deletions(-) diff --git a/AppImage/components/hardware.tsx b/AppImage/components/hardware.tsx index aa8dd8c..9ea4d54 100644 --- a/AppImage/components/hardware.tsx +++ b/AppImage/components/hardware.tsx @@ -39,23 +39,15 @@ const getDeviceTypeColor = (type: string): string => { return "bg-gray-500/10 text-gray-500 border-gray-500/20" } -const hasRealtimeData = (gpu: GPU): boolean => { - return !!( - (gpu.temperature !== undefined && gpu.temperature > 0) || - gpu.utilization_gpu !== undefined || - gpu.memory_total || - gpu.power_draw - ) -} - const getMonitoringToolRecommendation = (vendor: string): string => { const lowerVendor = vendor.toLowerCase() if (lowerVendor.includes("intel")) { return "To get extended GPU monitoring information, please install intel-gpu-tools or igt-gpu-tools package." } if (lowerVendor.includes("nvidia")) { - return "To get extended GPU monitoring information, please install nvidia-utils or nvidia-smi package." + return "For NVIDIA GPUs, real-time monitoring requires the proprietary drivers (nvidia-driver package). Install them only if your GPU is used directly by the host." } + if (lowerVendor.includes("amd") || lowerVendor.includes("ati")) { return "To get extended GPU monitoring information, please install radeontop package." } @@ -72,6 +64,11 @@ export default function Hardware() { const [selectedDisk, setSelectedDisk] = useState(null) const [selectedNetwork, setSelectedNetwork] = useState(null) + const { data: realtimeGPUData } = useSWR(selectedGPU ? `/api/gpu/${selectedGPU.slot}/realtime` : null, fetcher, { + refreshInterval: 2000, // Update every 2 seconds + revalidateOnFocus: false, + }) + const findPCIDeviceForGPU = (gpu: GPU): PCIDevice | null => { if (!hardwareData?.pci_devices || !gpu.slot) return null @@ -92,6 +89,16 @@ export default function Hardware() { return pciDevice || null } + const hasRealtimeData = (gpu: GPU, realtimeData?: any): boolean => { + const combinedData = { ...gpu, ...realtimeData } + return !!( + (combinedData.temperature !== undefined && combinedData.temperature > 0) || + combinedData.utilization_gpu !== undefined || + combinedData.memory_total || + combinedData.power_draw + ) + } + return (
{/* System Information - CPU & Motherboard */} @@ -293,99 +300,104 @@ export default function Hardware() {
- {hardwareData.gpus.map((gpu, index) => ( -
setSelectedGPU(gpu)} - className="cursor-pointer rounded-lg border border-border/30 bg-background/50 p-4 transition-colors hover:bg-background/80" - > -
- {gpu.name} - {gpu.vendor} -
+ {hardwareData.gpus.map((gpu, index) => { + const pciDevice = findPCIDeviceForGPU(gpu) + const fullSlot = pciDevice?.slot || gpu.slot -
-
- Type - {gpu.type} + return ( +
setSelectedGPU(gpu)} + className="cursor-pointer rounded-lg border border-border/30 bg-background/50 p-4 transition-colors hover:bg-background/80" + > +
+ {gpu.name} + {gpu.vendor}
- {gpu.slot && ( +
- PCI Slot - {gpu.slot} + Type + {gpu.type}
- )} - {gpu.pci_driver && ( -
- Driver - {gpu.pci_driver} -
- )} - - {gpu.pci_kernel_module && ( -
- Kernel Module - {gpu.pci_kernel_module} -
- )} - - {gpu.driver_version && ( -
- Driver Version - {gpu.driver_version} -
- )} - - {gpu.temperature !== undefined && gpu.temperature > 0 && ( -
+ {fullSlot && (
- Temperature - {gpu.temperature}°C + PCI Slot + {fullSlot}
-
-
-
-
- )} + )} - {gpu.memory_total && ( -
- Memory - - {gpu.memory_used} / {gpu.memory_total} - -
- )} - - {gpu.utilization_gpu !== undefined && ( -
+ {gpu.pci_driver && (
- GPU Usage - {gpu.utilization_gpu}% + Driver + {gpu.pci_driver}
-
-
-
-
- )} + )} - {gpu.power_draw && gpu.power_draw !== "N/A" && ( -
- Power - {gpu.power_draw} -
- )} + {gpu.pci_kernel_module && ( +
+ Kernel Module + {gpu.pci_kernel_module} +
+ )} + + {gpu.driver_version && ( +
+ Driver Version + {gpu.driver_version} +
+ )} + + {gpu.temperature !== undefined && gpu.temperature > 0 && ( +
+
+ Temperature + {gpu.temperature}°C +
+
+
+
+
+ )} + + {gpu.memory_total && ( +
+ Memory + + {gpu.memory_used} / {gpu.memory_total} + +
+ )} + + {gpu.utilization_gpu !== undefined && ( +
+
+ GPU Usage + {gpu.utilization_gpu}% +
+
+
+
+
+ )} + + {gpu.power_draw && gpu.power_draw !== "N/A" && ( +
+ Power + {gpu.power_draw} +
+ )} +
-
- ))} + ) + })}
)} @@ -401,6 +413,7 @@ export default function Hardware() { {selectedGPU && (() => { const pciDevice = findPCIDeviceForGPU(selectedGPU) + const combinedGPU = { ...selectedGPU, ...realtimeGPUData } return (
@@ -454,52 +467,52 @@ export default function Hardware() { {selectedGPU.type}
- {selectedGPU.driver_version && ( + {combinedGPU.driver_version && (
Driver Version - {selectedGPU.driver_version} + {combinedGPU.driver_version}
)} - {selectedGPU.pcie_gen && ( + {combinedGPU.pcie_gen && (
PCIe Generation - Gen {selectedGPU.pcie_gen} + Gen {combinedGPU.pcie_gen}
)} - {selectedGPU.pcie_width && ( + {combinedGPU.pcie_width && (
PCIe Width - {selectedGPU.pcie_width} + {combinedGPU.pcie_width}
)}
{/* Memory Info - Only show if available */} - {selectedGPU.memory_total && ( + {combinedGPU.memory_total && (

Memory

Total - {selectedGPU.memory_total} + {combinedGPU.memory_total}
Used - {selectedGPU.memory_used} + {combinedGPU.memory_used}
Free - {selectedGPU.memory_free} + {combinedGPU.memory_free}
- {selectedGPU.utilization_memory !== undefined && ( + {combinedGPU.utilization_memory !== undefined && (
Memory Utilization - {selectedGPU.utilization_memory}% + {combinedGPU.utilization_memory}%
- +
)}
@@ -507,45 +520,45 @@ export default function Hardware() { )} {/* Performance - Only show if realtime data available */} - {hasRealtimeData(selectedGPU) && ( + {hasRealtimeData(selectedGPU, realtimeGPUData) && (

Performance

- {selectedGPU.temperature !== undefined && selectedGPU.temperature > 0 && ( + {combinedGPU.temperature !== undefined && combinedGPU.temperature > 0 && (
Temperature - {selectedGPU.temperature}°C + {combinedGPU.temperature}°C
- +
)} - {selectedGPU.utilization_gpu !== undefined && ( + {combinedGPU.utilization_gpu !== undefined && (
GPU Utilization - {selectedGPU.utilization_gpu}% + {combinedGPU.utilization_gpu}%
- +
)} - {selectedGPU.power_draw && selectedGPU.power_draw !== "N/A" && ( + {combinedGPU.power_draw && combinedGPU.power_draw !== "N/A" && (
Power Draw - {selectedGPU.power_draw} + {combinedGPU.power_draw}
)} - {selectedGPU.power_limit && ( + {combinedGPU.power_limit && (
Power Limit - {selectedGPU.power_limit} + {combinedGPU.power_limit}
)} - {selectedGPU.fan_speed && ( + {combinedGPU.fan_speed && (
Fan Speed - {selectedGPU.fan_speed} {selectedGPU.fan_unit} + {combinedGPU.fan_speed} {combinedGPU.fan_unit}
)} @@ -554,20 +567,20 @@ export default function Hardware() { )} {/* Clock Speeds - Only show if available */} - {(selectedGPU.clock_graphics || selectedGPU.clock_memory) && ( + {(combinedGPU.clock_graphics || combinedGPU.clock_memory) && (

Clock Speeds

- {selectedGPU.clock_graphics && ( + {combinedGPU.clock_graphics && (
Graphics Clock - {selectedGPU.clock_graphics} + {combinedGPU.clock_graphics}
)} - {selectedGPU.clock_memory && ( + {combinedGPU.clock_memory && (
Memory Clock - {selectedGPU.clock_memory} + {combinedGPU.clock_memory}
)}
@@ -575,11 +588,11 @@ export default function Hardware() { )} {/* Running Processes - Only show if available */} - {selectedGPU.processes && selectedGPU.processes.length > 0 && ( + {combinedGPU.processes && combinedGPU.processes.length > 0 && (

Running Processes

- {selectedGPU.processes.map((proc, idx) => ( + {combinedGPU.processes.map((proc: any, idx: number) => (
PID: {proc.pid} @@ -592,7 +605,7 @@ export default function Hardware() {
)} - {!hasRealtimeData(selectedGPU) && ( + {!hasRealtimeData(selectedGPU, realtimeGPUData) && (
@@ -606,9 +619,9 @@ export default function Hardware() {
)} - {selectedGPU.note && ( + {combinedGPU.note && (
-

{selectedGPU.note}

+

{combinedGPU.note}

)}
diff --git a/AppImage/scripts/flask_server.py b/AppImage/scripts/flask_server.py index 75796eb..05c4763 100644 --- a/AppImage/scripts/flask_server.py +++ b/AppImage/scripts/flask_server.py @@ -1670,7 +1670,8 @@ def get_pci_device_info(pci_slot): pci_info = {} try: # Use lspci -vmm for detailed information - result = subprocess.run(['lspci', '-vmm', '-s', pci_slot], capture_output=True, text=True, timeout=5) + result = subprocess.run(['lspci', '-vmm', '-s', pci_slot], + capture_output=True, text=True, timeout=5) if result.returncode == 0: for line in result.stdout.split('\n'): line = line.strip() @@ -2481,7 +2482,8 @@ def api_info(): '/api/vms', '/api/logs', '/api/health', - '/api/hardware' + '/api/hardware', + '/api/gpu//realtime' # Added endpoint for GPU monitoring ] }) @@ -2516,6 +2518,35 @@ def api_hardware(): return jsonify(formatted_data) +@app.route('/api/gpu//realtime', methods=['GET']) +def api_gpu_realtime(slot): + """Get real-time GPU monitoring data for a specific GPU""" + try: + # Find the GPU by slot + hardware_info = get_hardware_info() + gpus = hardware_info.get('gpus', []) + + gpu = None + for g in gpus: + if g.get('slot') == slot or g.get('slot', '').startswith(slot): + gpu = g + break + + if not gpu: + return jsonify({'error': 'GPU not found'}), 404 + + # Get real-time monitoring data + realtime_data = get_detailed_gpu_info(gpu) + + print(f"[v0] /api/gpu/{slot}/realtime returning data") + print(f"[v0] - Vendor: {gpu.get('vendor')}") + print(f"[v0] - Realtime data: {realtime_data}") + + return jsonify(realtime_data) + except Exception as e: + print(f"[v0] Error getting real-time GPU data: {e}") + return jsonify({'error': str(e)}), 500 + @app.route('/api/vms/', methods=['GET']) def api_vm_details(vmid): """Get detailed information for a specific VM/LXC"""