From 9aed659f178d3abe96f67ae0040f40a6b7a2a9cd Mon Sep 17 00:00:00 2001 From: MacRimi Date: Wed, 26 Nov 2025 18:44:37 +0100 Subject: [PATCH] Update AppImage --- AppImage/scripts/flask_server.py | 49 +++++++++++++++- AppImage/scripts/hardware_monitor.py | 83 ++++++++++++++++++++++++++-- 2 files changed, 126 insertions(+), 6 deletions(-) diff --git a/AppImage/scripts/flask_server.py b/AppImage/scripts/flask_server.py index 5a72663..c3fa30a 100644 --- a/AppImage/scripts/flask_server.py +++ b/AppImage/scripts/flask_server.py @@ -2618,6 +2618,53 @@ def identify_temperature_sensor(sensor_name, adapter): return sensor_name + +def identify_fan(sensor_name, adapter): + """Identify what a fan sensor corresponds to, using hardware_monitor for GPU detection""" + sensor_lower = sensor_name.lower() + adapter_lower = adapter.lower() if adapter else "" + + # GPU fans - Use hardware_monitor to get real GPU names + if "pci adapter" in adapter_lower or any(gpu_driver in adapter_lower for gpu_driver in ["nouveau", "amdgpu", "radeon", "i915"]): + # Extract PCI address from adapter string + # Example: "nouveau-pci-0200" -> "02:00.0" + pci_match = re.search(r'pci-([0-9a-f]{4})', adapter_lower) + + if pci_match: + pci_code = pci_match.group(1) + pci_address = f"{pci_code[0:2]}:{pci_code[2:4]}.0" + + # Get GPU mapping from hardware_monitor + try: + gpu_map = hardware_monitor.get_pci_gpu_map() + if pci_address in gpu_map: + gpu_info = gpu_map[pci_address] + return f"GPU {gpu_info['vendor']} {gpu_info['name']}" + except Exception: + pass + + # Fallback: generic GPU label + if "nouveau" in adapter_lower: + return "GPU NVIDIA" + elif "amdgpu" in adapter_lower or "radeon" in adapter_lower: + return "GPU AMD" + elif "i915" in adapter_lower: + return "GPU Intel" + else: + return "GPU" + + # CPU/System fans - keep original name + if any(cpu_fan in sensor_lower for cpu_fan in ["cpu_fan", "cpufan", "sys_fan", "sysfan"]): + return sensor_name + + # Chassis fans - keep original name + if "chassis" in sensor_lower or "case" in sensor_lower: + return sensor_name + + # Default: return original name + return sensor_name + + def get_temperature_info(): """Get detailed temperature information from sensors command""" temperatures = [] @@ -4514,7 +4561,7 @@ def get_hardware_info(): # Placeholder for identify_fan - needs implementation # identified_name = identify_fan(sensor_name, current_adapter) - identified_name = sensor_name # Use original name for now + identified_name = identify_fan(sensor_name, current_adapter) fans.append({ 'name': identified_name, diff --git a/AppImage/scripts/hardware_monitor.py b/AppImage/scripts/hardware_monitor.py index 2c31a33..2c06fe7 100644 --- a/AppImage/scripts/hardware_monitor.py +++ b/AppImage/scripts/hardware_monitor.py @@ -1,22 +1,95 @@ #!/usr/bin/env python3 """ -Hardware Monitor - RAPL Power Monitoring +Hardware Monitor - RAPL Power Monitoring and GPU Identification -This module provides CPU power consumption monitoring using Intel RAPL -(Running Average Power Limit) interface when IPMI is not available. +This module provides: +1. CPU power consumption monitoring using Intel RAPL (Running Average Power Limit) +2. PCI GPU identification for better fan labeling -Only contains get_power_info() - all other hardware monitoring is handled -by flask_server.py to avoid code duplication. +Only contains these specialized functions - all other hardware monitoring +is handled by flask_server.py to avoid code duplication. """ import os import time +import subprocess +import re from typing import Dict, Any, Optional # Global variable to store previous energy reading for power calculation _last_energy_reading = {'energy_uj': None, 'timestamp': None} +def get_pci_gpu_map() -> Dict[str, Dict[str, str]]: + """ + Get a mapping of PCI addresses to GPU names from lspci. + + This function parses lspci output to identify GPU models by their PCI addresses, + which allows us to provide meaningful names for GPU fans in sensors output. + + Returns: + dict: Mapping of PCI addresses (e.g., '02:00.0') to GPU info + Example: { + '02:00.0': { + 'vendor': 'NVIDIA', + 'name': 'GeForce GTX 1080', + 'full_name': 'NVIDIA Corporation GP104 [GeForce GTX 1080]' + } + } + """ + gpu_map = {} + + try: + # Run lspci to get VGA/3D/Display controllers + result = subprocess.run( + ['lspci', '-nn'], + capture_output=True, + text=True, + timeout=5 + ) + + if result.returncode == 0: + for line in result.stdout.split('\n'): + if 'VGA compatible controller' in line or '3D controller' in line or 'Display controller' in line: + # Example line: "02:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP104 [GeForce GTX 1080] [10de:1b80]" + match = re.match(r'^([0-9a-f]{2}:[0-9a-f]{2}\.[0-9a-f])\s+.*:\s+(.+?)\s+\[([0-9a-f]{4}):([0-9a-f]{4})\]', line) + + if match: + pci_address = match.group(1) + device_name = match.group(2).strip() + + # Extract vendor + vendor = None + if 'NVIDIA' in device_name.upper() or 'GEFORCE' in device_name.upper() or 'QUADRO' in device_name.upper(): + vendor = 'NVIDIA' + elif 'AMD' in device_name.upper() or 'RADEON' in device_name.upper(): + vendor = 'AMD' + elif 'INTEL' in device_name.upper() or 'ARC' in device_name.upper(): + vendor = 'Intel' + + # Extract model name (text between brackets is usually the commercial name) + bracket_match = re.search(r'\[([^\]]+)\]', device_name) + if bracket_match: + model_name = bracket_match.group(1) + else: + # Fallback: use everything after the vendor name + if vendor: + model_name = device_name.split(vendor)[-1].strip() + else: + model_name = device_name + + gpu_map[pci_address] = { + 'vendor': vendor if vendor else 'Unknown', + 'name': model_name, + 'full_name': device_name + } + + except Exception: + pass + + return gpu_map + + def get_power_info() -> Optional[Dict[str, Any]]: """ Get CPU power consumption using Intel RAPL interface.