From 37fade8f7a28a4bce750dc223a5b884be0f3edf3 Mon Sep 17 00:00:00 2001 From: MacRimi Date: Wed, 29 Oct 2025 22:16:40 +0100 Subject: [PATCH] Update AppImage --- AppImage/components/hardware.tsx | 67 +++++++++++++++++--- AppImage/scripts/flask_server.py | 103 +++++++++++++++++++++++++++++++ AppImage/types/hardware.ts | 2 +- 3 files changed, 162 insertions(+), 10 deletions(-) diff --git a/AppImage/components/hardware.tsx b/AppImage/components/hardware.tsx index 7714e0f..6fe121c 100644 --- a/AppImage/components/hardware.tsx +++ b/AppImage/components/hardware.tsx @@ -1614,11 +1614,23 @@ export default function Hardware() { (device) => device.type === "disk" && !device.name.startsWith("zd") && !device.name.startsWith("loop"), ) .map((device, index) => { - const getDiskTypeBadge = (diskName: string, rotationRate: number | undefined) => { + const getDiskTypeBadge = (diskName: string, rotationRate: number | string | undefined) => { let diskType = "HDD" + + // Check if it's NVMe if (diskName.startsWith("nvme")) { diskType = "NVMe" - } else if (!rotationRate || rotationRate === 0) { + } + // Check rotation rate for SSD vs HDD + else if (rotationRate !== undefined && rotationRate !== null) { + // Handle both number and string formats + const rateNum = typeof rotationRate === "string" ? Number.parseInt(rotationRate) : rotationRate + if (rateNum === 0 || isNaN(rateNum)) { + diskType = "SSD" + } + } + // If rotation_rate is "Solid State Device" string + else if (typeof rotationRate === "string" && rotationRate.includes("Solid State")) { diskType = "SSD" } @@ -1655,9 +1667,6 @@ export default function Hardware() { {device.model && (

{device.model}

)} - {device.driver && ( -

Driver: {device.driver}

- )} ) })} @@ -1681,10 +1690,44 @@ export default function Hardware() { {selectedDisk.name} - {selectedDisk.type && ( + {selectedDisk.name && (
Type - {selectedDisk.type} + {(() => { + const getDiskTypeBadge = (diskName: string, rotationRate: number | string | undefined) => { + let diskType = "HDD" + + if (diskName.startsWith("nvme")) { + diskType = "NVMe" + } else if (rotationRate !== undefined && rotationRate !== null) { + const rateNum = typeof rotationRate === "string" ? Number.parseInt(rotationRate) : rotationRate + if (rateNum === 0 || isNaN(rateNum)) { + diskType = "SSD" + } + } else if (typeof rotationRate === "string" && rotationRate.includes("Solid State")) { + diskType = "SSD" + } + + const badgeStyles: Record = { + NVMe: { + className: "bg-purple-500/10 text-purple-500 border-purple-500/20", + label: "NVMe SSD", + }, + SSD: { + className: "bg-cyan-500/10 text-cyan-500 border-cyan-500/20", + label: "SSD", + }, + HDD: { + className: "bg-blue-500/10 text-blue-500 border-blue-500/20", + label: "HDD", + }, + } + return badgeStyles[diskType] + } + + const diskBadge = getDiskTypeBadge(selectedDisk.name, selectedDisk.rotation_rate) + return {diskBadge.label} + })()}
)} @@ -1737,10 +1780,16 @@ export default function Hardware() { )} - {selectedDisk.rotation_rate && ( + {selectedDisk.rotation_rate !== undefined && selectedDisk.rotation_rate !== null && (
Rotation Rate - {selectedDisk.rotation_rate} + + {typeof selectedDisk.rotation_rate === "number" && selectedDisk.rotation_rate > 0 + ? `${selectedDisk.rotation_rate} rpm` + : typeof selectedDisk.rotation_rate === "string" + ? selectedDisk.rotation_rate + : "Solid State Device"} +
)} diff --git a/AppImage/scripts/flask_server.py b/AppImage/scripts/flask_server.py index 57fb399..7533f0b 100644 --- a/AppImage/scripts/flask_server.py +++ b/AppImage/scripts/flask_server.py @@ -936,6 +936,10 @@ def get_smart_data(disk_name): 'wear_leveling_count': None, # SSD: Wear Leveling Count 'total_lbas_written': None, # SSD/NVMe: Total LBAs Written 'ssd_life_left': None, # SSD: SSD Life Left percentage + 'firmware': None, # Added firmware + 'family': None, # Added model family + 'sata_version': None, # Added SATA version + 'form_factor': None # Added Form Factor } @@ -3802,6 +3806,105 @@ def get_hardware_info(): # print(f"[v0] Error getting storage info: {e}") pass + + try: + result = subprocess.run(['lsblk', '-J', '-o', 'NAME,SIZE,TYPE,MOUNTPOINT,MODEL'], + capture_output=True, text=True, timeout=5) + if result.returncode == 0: + import json + lsblk_data = json.loads(result.stdout) + storage_devices = [] + for device in lsblk_data.get('blockdevices', []): + if device.get('type') == 'disk': + disk_name = device.get('name', '') + + # Get SMART data for this disk + smart_data = get_smart_data(disk_name) + + # Determine interface type + interface_type = None + if disk_name.startswith('nvme'): + interface_type = 'PCIe/NVMe' + elif disk_name.startswith('sd'): + interface_type = 'ATA' + elif disk_name.startswith('hd'): + interface_type = 'IDE' + + # Get driver information + driver = None + try: + sys_block_path = f'/sys/block/{disk_name}' + if os.path.exists(sys_block_path): + device_path = os.path.join(sys_block_path, 'device') + if os.path.exists(device_path): + driver_path = os.path.join(device_path, 'driver') + if os.path.exists(driver_path): + driver = os.path.basename(os.readlink(driver_path)) + except: + pass + + # Parse SATA version from smartctl output + sata_version = None + try: + result_smart = subprocess.run(['smartctl', '-i', f'/dev/{disk_name}'], + capture_output=True, text=True, timeout=5) + if result_smart.returncode == 0: + for line in result_smart.stdout.split('\n'): + if 'SATA Version is:' in line: + sata_version = line.split(':', 1)[1].strip() + break + except: + pass + + # Parse form factor from smartctl output + form_factor = None + try: + result_smart = subprocess.run(['smartctl', '-i', f'/dev/{disk_name}'], + capture_output=True, text=True, timeout=5) + if result_smart.returncode == 0: + for line in result_smart.stdout.split('\n'): + if 'Form Factor:' in line: + form_factor = line.split(':', 1)[1].strip() + break + except: + pass + + # Build storage device with all available information + storage_device = { + 'name': disk_name, + 'size': device.get('size', ''), + 'model': smart_data.get('model', device.get('model', 'Unknown')), + 'type': device.get('type', 'disk'), + 'serial': smart_data.get('serial', 'Unknown'), + 'firmware': smart_data.get('firmware'), + 'interface': interface_type, + 'driver': driver, + 'rotation_rate': smart_data.get('rotation_rate', 0), + 'form_factor': form_factor, + 'sata_version': sata_version, + } + + # Add family if available (from smartctl) + try: + result_smart = subprocess.run(['smartctl', '-i', f'/dev/{disk_name}'], + capture_output=True, text=True, timeout=5) + if result_smart.returncode == 0: + for line in result_smart.stdout.split('\n'): + if 'Model Family:' in line: + storage_device['family'] = line.split(':', 1)[1].strip() + break + except: + pass + + storage_devices.append(storage_device) + + hardware_data['storage_devices'] = storage_devices + # print(f"[v0] Storage devices: {len(storage_devices)} found with full SMART data") + pass + except Exception as e: + # print(f"[v0] Error getting storage info: {e}") + pass + # Graphics Cards (from lspci - will be duplicated by new PCI device listing, but kept for now) try: # Try nvidia-smi first diff --git a/AppImage/types/hardware.ts b/AppImage/types/hardware.ts index b80d500..6907646 100644 --- a/AppImage/types/hardware.ts +++ b/AppImage/types/hardware.ts @@ -30,7 +30,7 @@ export interface StorageDevice { serial?: string family?: string firmware?: string - rotation_rate?: string + rotation_rate?: number | string form_factor?: string sata_version?: string }