140 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			140 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Python
		
	
	
	
| # linux_hardware_info.py
 | |
| import subprocess
 | |
| import re
 | |
| 
 | |
| def _run_command(command: list[str]) -> str:
 | |
|     """Helper to run a command and return its stdout."""
 | |
|     try:
 | |
|         process = subprocess.run(command, capture_output=True, text=True, check=True)
 | |
|         return process.stdout
 | |
|     except FileNotFoundError:
 | |
|         print(f"Error: Command '{command[0]}' not found. Is 'pciutils' (for lspci) installed?")
 | |
|         return ""
 | |
|     except subprocess.CalledProcessError as e:
 | |
|         print(f"Error executing {' '.join(command)}: {e.stderr}")
 | |
|         return ""
 | |
|     except Exception as e:
 | |
|         print(f"An unexpected error occurred with command {' '.join(command)}: {e}")
 | |
|         return ""
 | |
| 
 | |
| def get_pci_devices_info() -> list[dict]:
 | |
|     """
 | |
|     Gets a list of dictionaries, each containing info about a PCI device,
 | |
|     focusing on VGA, Audio, and Ethernet controllers.
 | |
|     Output format for relevant devices:
 | |
|     {'type': 'VGA', 'vendor_id': '10de', 'device_id': '13c2', 'description': 'NVIDIA GTX 970'}
 | |
|     {'type': 'Audio', 'vendor_id': '8086', 'device_id': 'a170', 'description': 'Intel Sunrise Point-H HD Audio'}
 | |
|     {'type': 'Ethernet', 'vendor_id': '8086', 'device_id': '15b8', 'description': 'Intel Ethernet Connection I219-V'}
 | |
|     """
 | |
|     output = _run_command(["lspci", "-nnk"])
 | |
|     if not output:
 | |
|         return []
 | |
| 
 | |
|     devices = []
 | |
|     # Regex to capture device type (from description), description, and [vendor:device]
 | |
|     # Example line: 01:00.0 VGA compatible controller [0300]: NVIDIA Corporation GM204 [GeForce GTX 970] [10de:13c2] (rev a1)
 | |
|     # Example line: 00:1f.3 Audio device [0403]: Intel Corporation Sunrise Point-H HD Audio [8086:a170] (rev 31)
 | |
|     # Example line: 00:1f.6 Ethernet controller [0200]: Intel Corporation Ethernet Connection (2) I219-V [8086:15b8] (rev 31)
 | |
| 
 | |
|     # More robust regex:
 | |
|     # It captures the class description (like "VGA compatible controller", "Audio device")
 | |
|     # and the main device description (like "NVIDIA Corporation GM204 [GeForce GTX 970]")
 | |
|     # and the vendor/device IDs like "[10de:13c2]"
 | |
|     regex = re.compile(
 | |
|         r"^[0-9a-fA-F]{2}:[0-9a-fA-F]{2}\.\d\s+"  # PCI Address (e.g., 01:00.0 )
 | |
|         r"(.+?)\s+"                              # Class Description (e.g., "VGA compatible controller")
 | |
|         r"\[[0-9a-fA-F]{4}\]:\s+"                 # PCI Class Code (e.g., [0300]: )
 | |
|         r"(.+?)\s+"                              # Full Device Description (e.g., "NVIDIA Corporation GM204 [GeForce GTX 970]")
 | |
|         r"\[([0-9a-fA-F]{4}):([0-9a-fA-F]{4})\]"  # Vendor and Device ID (e.g., [10de:13c2])
 | |
|     )
 | |
| 
 | |
|     for line in output.splitlines():
 | |
|         match = regex.search(line)
 | |
|         if match:
 | |
|             class_desc = match.group(1).strip()
 | |
|             full_desc = match.group(2).strip()
 | |
|             vendor_id = match.group(3).lower()
 | |
|             device_id = match.group(4).lower()
 | |
| 
 | |
|             device_type = None
 | |
|             if "VGA compatible controller" in class_desc or "3D controller" in class_desc:
 | |
|                 device_type = "VGA"
 | |
|             elif "Audio device" in class_desc:
 | |
|                 device_type = "Audio"
 | |
|             elif "Ethernet controller" in class_desc:
 | |
|                 device_type = "Ethernet"
 | |
|             elif "Network controller" in class_desc: # Could be Wi-Fi
 | |
|                 device_type = "Network (Wi-Fi?)"
 | |
| 
 | |
| 
 | |
|             if device_type:
 | |
|                 # Try to get a cleaner description if possible, removing vendor name if it's at the start
 | |
|                 # e.g. "Intel Corporation Ethernet Connection (2) I219-V" -> "Ethernet Connection (2) I219-V"
 | |
|                 # This is a simple attempt.
 | |
|                 cleaned_desc = full_desc
 | |
|                 if full_desc.lower().startswith("intel corporation "):
 | |
|                     cleaned_desc = full_desc[len("intel corporation "):]
 | |
|                 elif full_desc.lower().startswith("nvidia corporation "):
 | |
|                     cleaned_desc = full_desc[len("nvidia corporation "):]
 | |
|                 elif full_desc.lower().startswith("advanced micro devices, inc.") or full_desc.lower().startswith("amd"):
 | |
|                     # Handle different AMD namings
 | |
|                     if full_desc.lower().startswith("advanced micro devices, inc."):
 | |
|                         cleaned_desc = re.sub(r"Advanced Micro Devices, Inc\.\s*\[AMD/ATI\]\s*", "", full_desc, flags=re.IGNORECASE)
 | |
|                     else: # Starts with AMD
 | |
|                          cleaned_desc = re.sub(r"AMD\s*\[ATI\]\s*", "", full_desc, flags=re.IGNORECASE)
 | |
|                 elif full_desc.lower().startswith("realtek semiconductor co., ltd."):
 | |
|                      cleaned_desc = full_desc[len("realtek semiconductor co., ltd. "):]
 | |
| 
 | |
| 
 | |
|                 devices.append({
 | |
|                     "type": device_type,
 | |
|                     "vendor_id": vendor_id,
 | |
|                     "device_id": device_id,
 | |
|                     "description": cleaned_desc.strip(),
 | |
|                     "full_lspci_line": line.strip() # For debugging or more info
 | |
|                 })
 | |
|     return devices
 | |
| 
 | |
| def get_cpu_info() -> dict:
 | |
|     """
 | |
|     Gets CPU information using lscpu.
 | |
|     Returns a dictionary with 'Model name', 'Vendor ID', 'CPU family', 'Model', 'Stepping', 'Flags'.
 | |
|     """
 | |
|     output = _run_command(["lscpu"])
 | |
|     if not output:
 | |
|         return {}
 | |
| 
 | |
|     info = {}
 | |
|     # Regex to capture key-value pairs from lscpu output
 | |
|     # Handles spaces in values for "Model name"
 | |
|     regex = re.compile(r"^(CPU family|Model name|Vendor ID|Model|Stepping|Flags):\s+(.*)$")
 | |
|     for line in output.splitlines():
 | |
|         match = regex.match(line)
 | |
|         if match:
 | |
|             key = match.group(1).strip()
 | |
|             value = match.group(2).strip()
 | |
|             info[key] = value
 | |
|     return info
 | |
| 
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|     print("--- PCI Devices ---")
 | |
|     pci_devs = get_pci_devices_info()
 | |
|     if pci_devs:
 | |
|         for dev in pci_devs:
 | |
|             print(f"  Type: {dev['type']}")
 | |
|             print(f"    Vendor ID: {dev['vendor_id']}")
 | |
|             print(f"    Device ID: {dev['device_id']}")
 | |
|             print(f"    Description: {dev['description']}")
 | |
|             # print(f"    Full Line: {dev['full_lspci_line']}")
 | |
|     else:
 | |
|         print("  No relevant PCI devices found or lspci not available.")
 | |
| 
 | |
|     print("\n--- CPU Info ---")
 | |
|     cpu_info = get_cpu_info()
 | |
|     if cpu_info:
 | |
|         for key, value in cpu_info.items():
 | |
|             print(f"  {key}: {value}")
 | |
|     else:
 | |
|         print("  Could not retrieve CPU info or lscpu not available.")
 |