diff --git a/AppImage/components/hardware.tsx b/AppImage/components/hardware.tsx index 9bc19d4..c15306f 100644 --- a/AppImage/components/hardware.tsx +++ b/AppImage/components/hardware.tsx @@ -20,7 +20,7 @@ import { Info, } from "lucide-react" import useSWR from "swr" -import { useState, useEffect } from "react" +import { useState } from "react" import { type HardwareData, type GPU, type PCIDevice, type StorageDevice, fetcher } from "../types/hardware" const formatMemory = (memoryKB: number | string): string => { @@ -116,56 +116,26 @@ export default function Hardware() { const [selectedNetwork, setSelectedNetwork] = useState(null) const [selectedUPS, setSelectedUPS] = useState(null) // Added state for UPS modal - useEffect(() => { - if (!selectedGPU) return + if (!hardwareData && !error) { + return ( +
+
+ +

Loading hardware information...

+
+
+ ) + } - const pciDevice = findPCIDeviceForGPU(selectedGPU) - const fullSlot = pciDevice?.slot || selectedGPU.slot - - if (!fullSlot) return - - const abortController = new AbortController() - - const fetchRealtimeData = async () => { - try { - const apiUrl = `http://${window.location.hostname}:8008/api/gpu/${fullSlot}/realtime` - - const response = await fetch(apiUrl, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - signal: abortController.signal, - }) - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`) - } - - const data = await response.json() - setRealtimeGPUData(data) - setDetailsLoading(false) - } catch (error) { - // Only log non-abort errors - if (error instanceof Error && error.name !== "AbortError") { - console.error("[v0] Error fetching GPU realtime data:", error) - } - setRealtimeGPUData({ has_monitoring_tool: false }) - setDetailsLoading(false) - } - } - - // Initial fetch - fetchRealtimeData() - - // Poll every 3 seconds - const interval = setInterval(fetchRealtimeData, 3000) - - return () => { - clearInterval(interval) - abortController.abort() - } - }, [selectedGPU]) + if (error) { + return ( +
+
+

Error loading hardware information

+
+
+ ) + } const handleGPUClick = async (gpu: GPU) => { setSelectedGPU(gpu) @@ -952,8 +922,10 @@ export default function Hardware() { )} - {/* UPS */} - {/* Modified UPS card to include info button and trigger modal */} + {/* Power Supplies */} + {/* This section was moved to be grouped with Power Consumption */} + + {/* UPS - Enhanced with detailed modal */} {hardwareData?.ups && Object.keys(hardwareData.ups).length > 0 && hardwareData.ups.model && (
@@ -968,7 +940,7 @@ export default function Hardware() { {hardwareData.ups.model}
)} - {/* CHANGE: Corrected input_voltage field name */} - {hardwareData.ups.input_voltage && ( + {hardwareData.ups.line_voltage && (
Input Voltage
- {hardwareData.ups.input_voltage} + {hardwareData.ups.line_voltage}
@@ -1044,229 +1015,265 @@ export default function Hardware() { {selectedUPS && ( <> - {selectedUPS.model || "UPS Details"} - Complete UPS Information from NUT + {selectedUPS.model || selectedUPS.name} + Comprehensive UPS Information
{/* Device Information */} -
-

- Device Information -

-
- {selectedUPS.manufacturer && ( -
- Manufacturer - {selectedUPS.manufacturer} -
- )} - {selectedUPS.model && ( -
- Model - {selectedUPS.model} -
- )} - {selectedUPS.serial && ( -
- Serial Number - {selectedUPS.serial} -
- )} - {selectedUPS.device_type && ( -
- Device Type - {selectedUPS.device_type} -
- )} - {selectedUPS.firmware && ( -
- Firmware - {selectedUPS.firmware} -
- )} + {(selectedUPS.model || selectedUPS.manufacturer || selectedUPS.serial || selectedUPS.device_type) && ( +
+

+ Device Information +

+
+ {selectedUPS.model && ( +
+ Model + {selectedUPS.model} +
+ )} + {selectedUPS.manufacturer && ( +
+ Manufacturer + {selectedUPS.manufacturer} +
+ )} + {selectedUPS.serial && ( +
+ Serial Number + {selectedUPS.serial} +
+ )} + {selectedUPS.device_type && ( +
+ Device Type + {selectedUPS.device_type} +
+ )} + {selectedUPS.firmware && ( +
+ Firmware + {selectedUPS.firmware} +
+ )} +
-
+ )} {/* Status */} -
-

Status

-
- {selectedUPS.status && ( -
- UPS Status - - {selectedUPS.status} - -
- )} - {selectedUPS.test_result && ( -
- Test Result - {selectedUPS.test_result} -
- )} - {selectedUPS.beeper_status && ( -
- Beeper Status - {selectedUPS.beeper_status} -
- )} -
-
- - {/* Battery Information */} -
-

Battery

-
- {selectedUPS.battery_charge && ( -
-
- Charge - {selectedUPS.battery_charge} + {(selectedUPS.status || selectedUPS.beeper_status || selectedUPS.test_result) && ( +
+

Status

+
+ {selectedUPS.status && ( +
+ UPS Status + + {selectedUPS.status} +
- -
- )} - {selectedUPS.battery_charge_low && ( -
- Low Charge Threshold - {selectedUPS.battery_charge_low} -
- )} - {selectedUPS.time_left && ( -
- Runtime - {selectedUPS.time_left} -
- )} - {selectedUPS.battery_runtime_low && ( -
- Low Runtime Threshold - {selectedUPS.battery_runtime_low} -
- )} - {selectedUPS.battery_voltage && ( -
- Voltage - {selectedUPS.battery_voltage} -
- )} - {selectedUPS.battery_voltage_nominal && ( -
- Nominal Voltage - {selectedUPS.battery_voltage_nominal} -
- )} - {selectedUPS.battery_type && ( -
- Type - {selectedUPS.battery_type} -
- )} -
-
- - {/* Power Information */} -
-

Power

-
- {selectedUPS.load_percent && ( -
-
- Load - {selectedUPS.load_percent} + )} + {selectedUPS.beeper_status && ( +
+ Beeper Status + {selectedUPS.beeper_status}
- -
- )} - {selectedUPS.real_power && ( -
- Real Power - {selectedUPS.real_power} -
- )} - {selectedUPS.real_power_nominal && ( -
- Nominal Real Power - {selectedUPS.real_power_nominal} -
- )} - {selectedUPS.apparent_power && ( -
- Apparent Power - {selectedUPS.apparent_power} -
- )} - {selectedUPS.apparent_power_nominal && ( -
- Nominal Apparent Power - {selectedUPS.apparent_power_nominal} -
- )} + )} + {selectedUPS.test_result && ( +
+ Test Result + {selectedUPS.test_result} +
+ )} +
-
+ )} + + {/* Battery */} + {(selectedUPS.battery_charge || + selectedUPS.time_left || + selectedUPS.battery_voltage || + selectedUPS.battery_type) && ( +
+

+ Battery +

+
+ {selectedUPS.battery_charge && ( +
+
+ Charge Level + {selectedUPS.battery_charge} +
+ +
+ )} + {selectedUPS.battery_charge_low && ( +
+ Low Battery Threshold + {selectedUPS.battery_charge_low} +
+ )} + {selectedUPS.time_left && ( +
+ Runtime Remaining + {selectedUPS.time_left} +
+ )} + {selectedUPS.battery_runtime_low && ( +
+ Low Runtime Threshold + {selectedUPS.battery_runtime_low} +
+ )} + {selectedUPS.battery_voltage && ( +
+ Battery Voltage + {selectedUPS.battery_voltage} +
+ )} + {selectedUPS.battery_voltage_nominal && ( +
+ Nominal Voltage + {selectedUPS.battery_voltage_nominal} +
+ )} + {selectedUPS.battery_type && ( +
+ Battery Type + {selectedUPS.battery_type} +
+ )} + {selectedUPS.battery_mfr_date && ( +
+ Battery Manufacture Date + {selectedUPS.battery_mfr_date} +
+ )} +
+
+ )} + + {/* Power */} + {(selectedUPS.load_percent || selectedUPS.real_power || selectedUPS.apparent_power) && ( +
+

Power

+
+ {selectedUPS.load_percent && ( +
+
+ Load + {selectedUPS.load_percent} +
+ +
+ )} + {selectedUPS.real_power && ( +
+ Real Power + {selectedUPS.real_power} +
+ )} + {selectedUPS.realpower_nominal && ( +
+ Nominal Real Power + {selectedUPS.realpower_nominal} +
+ )} + {selectedUPS.apparent_power && ( +
+ Apparent Power + {selectedUPS.apparent_power} +
+ )} + {selectedUPS.power_nominal && ( +
+ Nominal Apparent Power + {selectedUPS.power_nominal} +
+ )} +
+
+ )} {/* Input/Output */} -
-

- Input / Output -

-
- {selectedUPS.input_voltage && ( -
- Input Voltage - {selectedUPS.input_voltage} -
- )} - {selectedUPS.input_voltage_nominal && ( -
- Nominal Input Voltage - {selectedUPS.input_voltage_nominal} -
- )} - {selectedUPS.input_frequency && ( -
- Input Frequency - {selectedUPS.input_frequency} -
- )} - {selectedUPS.output_voltage && ( -
- Output Voltage - {selectedUPS.output_voltage} -
- )} - {selectedUPS.output_voltage_nominal && ( -
- Nominal Output Voltage - {selectedUPS.output_voltage_nominal} -
- )} - {selectedUPS.output_frequency && ( -
- Output Frequency - {selectedUPS.output_frequency} -
- )} + {(selectedUPS.input_voltage || selectedUPS.output_voltage || selectedUPS.input_frequency) && ( +
+

+ Input / Output +

+
+ {selectedUPS.input_voltage && ( +
+ Input Voltage + {selectedUPS.input_voltage} +
+ )} + {selectedUPS.input_voltage_nominal && ( +
+ Nominal Input Voltage + {selectedUPS.input_voltage_nominal} +
+ )} + {selectedUPS.input_frequency && ( +
+ Input Frequency + {selectedUPS.input_frequency} +
+ )} + {selectedUPS.input_transfer_high && ( +
+ Transfer High + {selectedUPS.input_transfer_high} +
+ )} + {selectedUPS.input_transfer_low && ( +
+ Transfer Low + {selectedUPS.input_transfer_low} +
+ )} + {selectedUPS.transfer_reason && ( +
+ Last Transfer Reason + {selectedUPS.transfer_reason} +
+ )} + {selectedUPS.output_voltage && ( +
+ Output Voltage + {selectedUPS.output_voltage} +
+ )} + {selectedUPS.output_voltage_nominal && ( +
+ Nominal Output Voltage + {selectedUPS.output_voltage_nominal} +
+ )} + {selectedUPS.output_frequency && ( +
+ Output Frequency + {selectedUPS.output_frequency} +
+ )} +
-
+ )} - {/* Driver Information */} + {/* Driver */} {(selectedUPS.driver_name || selectedUPS.driver_version) && (

Driver

@@ -1283,12 +1290,30 @@ export default function Hardware() { {selectedUPS.driver_version}
)} + {selectedUPS.driver_version_internal && ( +
+ Internal Version + {selectedUPS.driver_version_internal} +
+ )} + {selectedUPS.driver_poll_freq && ( +
+ Poll Frequency + {selectedUPS.driver_poll_freq} +
+ )} + {selectedUPS.driver_poll_interval && ( +
+ Poll Interval + {selectedUPS.driver_poll_interval} +
+ )}
)} {/* Configuration */} - {(selectedUPS.delay_shutdown || selectedUPS.delay_start) && ( + {(selectedUPS.delay_shutdown || selectedUPS.delay_start || selectedUPS.timer_shutdown) && (

Configuration @@ -1306,22 +1331,34 @@ export default function Hardware() { {selectedUPS.delay_start}

)} + {selectedUPS.timer_shutdown && ( +
+ Shutdown Timer + {selectedUPS.timer_shutdown} +
+ )} + {selectedUPS.timer_reboot && ( +
+ Reboot Timer + {selectedUPS.timer_reboot} +
+ )}
)} - {/* Raw Data Section - Collapsible */} - {selectedUPS.raw_data && Object.keys(selectedUPS.raw_data).length > 0 && ( + {/* Raw Variables - Collapsible section with all UPS variables */} + {selectedUPS.raw_variables && Object.keys(selectedUPS.raw_variables).length > 0 && (
- - All UPS Variables ({Object.keys(selectedUPS.raw_data).length}) + + All UPS Variables ({Object.keys(selectedUPS.raw_variables).length}) -
-
- {Object.entries(selectedUPS.raw_data).map(([key, value]) => ( -
- {key} - {value as string} +
+
+ {Object.entries(selectedUPS.raw_variables).map(([key, value]) => ( +
+ {key} + {value as string}
))}
diff --git a/AppImage/scripts/flask_server.py b/AppImage/scripts/flask_server.py index 57814b0..a0aab54 100644 --- a/AppImage/scripts/flask_server.py +++ b/AppImage/scripts/flask_server.py @@ -961,7 +961,6 @@ def get_smart_data(disk_name): break elif smart_data['model'] != 'Unknown' or smart_data['serial'] != 'Unknown': print(f"[v0] Extracted partial data from text output, continuing to next attempt...") - else: print(f"[v0] No usable output (return code {result_code}), trying next command...") @@ -1548,50 +1547,55 @@ def get_ipmi_power(): 'power_meter': power_meter } +# def get_ups_info(): """Get UPS information from NUT (upsc) - supports both local and remote UPS""" ups_list = [] try: - upsmon_conf = '/etc/nut/upsmon.conf' configured_ups = [] + try: + with open('/etc/nut/upsmon.conf', 'r') as f: + for line in f: + line = line.strip() + # Parse MONITOR lines: MONITOR @: + if line.startswith('MONITOR') and not line.startswith('#'): + parts = line.split() + if len(parts) >= 2: + ups_identifier = parts[1] # e.g., "apc@localhost" or "ups@192.168.1.10" + configured_ups.append(ups_identifier) + print(f"[v0] Found configured UPS in upsmon.conf: {ups_identifier}") + except FileNotFoundError: + print("[v0] /etc/nut/upsmon.conf not found, will try local detection only") + except Exception as e: + print(f"[v0] Error reading upsmon.conf: {e}") - if os.path.exists(upsmon_conf): - try: - with open(upsmon_conf, 'r') as f: - for line in f: - line = line.strip() - # Look for MONITOR lines: MONITOR ups@hostname powervalue username password ("master"|"slave") - if line.startswith('MONITOR') and not line.startswith('#'): - parts = line.split() - if len(parts) >= 2: - ups_identifier = parts[1] # Format: upsname@hostname or just upsname - configured_ups.append(ups_identifier) - print(f"[v0] Found configured UPS in upsmon.conf: {ups_identifier}") - except Exception as e: - print(f"[v0] Error reading upsmon.conf: {e}") + all_ups_names = set(configured_ups) - result = subprocess.run(['upsc', '-l'], capture_output=True, text=True, timeout=5) - local_ups = [] - if result.returncode == 0: - local_ups = [ups.strip() for ups in result.stdout.strip().split('\n') if ups.strip()] - print(f"[v0] Found local UPS devices: {local_ups}") + # Also try to list local UPS devices + try: + result = subprocess.run(['upsc', '-l'], capture_output=True, text=True, timeout=5) + if result.returncode == 0: + local_ups = result.stdout.strip().split('\n') + for ups in local_ups: + if ups: + all_ups_names.add(ups) + print(f"[v0] Found local UPS: {ups}") + except Exception as e: + print(f"[v0] Error listing local UPS: {e}") - all_ups = list(set(configured_ups + local_ups)) - - if not all_ups: - print("[v0] No UPS devices found") - return {} - - for ups_identifier in all_ups: - try: - ups_data = { - 'name': ups_identifier, - 'raw_data': {} # Store all raw data for the modal - } + for ups_name in all_ups_names: + if not ups_name: + continue - # Get detailed UPS info - result = subprocess.run(['upsc', ups_identifier], capture_output=True, text=True, timeout=5) + ups_data = { + 'name': ups_name, + 'raw_variables': {} # Store all raw variables for the modal + } + + try: + print(f"[v0] Querying UPS: {ups_name}") + result = subprocess.run(['upsc', ups_name], capture_output=True, text=True, timeout=5) if result.returncode == 0: for line in result.stdout.split('\n'): if ':' in line: @@ -1599,10 +1603,10 @@ def get_ups_info(): key = key.strip() value = value.strip() - # Store all raw data - ups_data['raw_data'][key] = value + # Store all raw variables + ups_data['raw_variables'][key] = value - # Map common UPS variables for quick access + # Device Information if key == 'device.model': ups_data['model'] = value elif key == 'device.mfr': @@ -1611,96 +1615,160 @@ def get_ups_info(): ups_data['serial'] = value elif key == 'device.type': ups_data['device_type'] = value + + # Status elif key == 'ups.status': ups_data['status'] = value + elif key == 'ups.beeper.status': + ups_data['beeper_status'] = value + elif key == 'ups.test.result': + ups_data['test_result'] = value + + # Battery elif key == 'battery.charge': ups_data['battery_charge'] = f"{value}%" - ups_data['battery_charge_value'] = float(value) + ups_data['battery_charge_raw'] = float(value) elif key == 'battery.charge.low': ups_data['battery_charge_low'] = f"{value}%" elif key == 'battery.runtime': - # Convert seconds to minutes try: runtime_sec = int(value) runtime_min = runtime_sec // 60 ups_data['time_left'] = f"{runtime_min} minutes" - ups_data['time_left_seconds'] = runtime_sec + ups_data['battery_runtime_seconds'] = runtime_sec except ValueError: ups_data['time_left'] = value elif key == 'battery.runtime.low': - try: - runtime_sec = int(value) - runtime_min = runtime_sec // 60 - ups_data['battery_runtime_low'] = f"{runtime_min} minutes" - except ValueError: - ups_data['battery_runtime_low'] = value + ups_data['battery_runtime_low'] = f"{value}s" elif key == 'battery.voltage': ups_data['battery_voltage'] = f"{value}V" elif key == 'battery.voltage.nominal': ups_data['battery_voltage_nominal'] = f"{value}V" elif key == 'battery.type': ups_data['battery_type'] = value + elif key == 'battery.mfr.date': + ups_data['battery_mfr_date'] = value + + # Power elif key == 'ups.load': ups_data['load_percent'] = f"{value}%" - ups_data['load_value'] = float(value) + ups_data['load_raw'] = float(value) elif key == 'ups.realpower': ups_data['real_power'] = f"{value}W" elif key == 'ups.realpower.nominal': - ups_data['real_power_nominal'] = f"{value}W" + ups_data['realpower_nominal'] = f"{value}W" elif key == 'ups.power': ups_data['apparent_power'] = f"{value}VA" elif key == 'ups.power.nominal': - ups_data['apparent_power_nominal'] = f"{value}VA" + ups_data['power_nominal'] = f"{value}VA" + + # Input elif key == 'input.voltage': + ups_data['line_voltage'] = f"{value}V" ups_data['input_voltage'] = f"{value}V" elif key == 'input.voltage.nominal': ups_data['input_voltage_nominal'] = f"{value}V" elif key == 'input.frequency': ups_data['input_frequency'] = f"{value}Hz" + elif key == 'input.transfer.reason': + ups_data['transfer_reason'] = value + elif key == 'input.transfer.high': + ups_data['input_transfer_high'] = f"{value}V" + elif key == 'input.transfer.low': + ups_data['input_transfer_low'] = f"{value}V" + + # Output elif key == 'output.voltage': ups_data['output_voltage'] = f"{value}V" elif key == 'output.voltage.nominal': ups_data['output_voltage_nominal'] = f"{value}V" elif key == 'output.frequency': ups_data['output_frequency'] = f"{value}Hz" - elif key == 'ups.beeper.status': - ups_data['beeper_status'] = value - elif key == 'ups.delay.shutdown': - ups_data['delay_shutdown'] = f"{value}s" - elif key == 'ups.delay.start': - ups_data['delay_start'] = f"{value}s" - elif key == 'ups.test.result': - ups_data['test_result'] = value - elif key == 'ups.firmware': - ups_data['firmware'] = value + + # Driver elif key == 'driver.name': ups_data['driver_name'] = value elif key == 'driver.version': ups_data['driver_version'] = value + elif key == 'driver.version.internal': + ups_data['driver_version_internal'] = value + elif key == 'driver.parameter.pollfreq': + ups_data['driver_poll_freq'] = value + elif key == 'driver.parameter.pollinterval': + ups_data['driver_poll_interval'] = value + + # Firmware + elif key == 'ups.firmware': + ups_data['firmware'] = value + elif key == 'ups.mfr': + ups_data['ups_manufacturer'] = value + elif key == 'ups.mfr.date': + ups_data['ups_mfr_date'] = value + elif key == 'ups.productid': + ups_data['product_id'] = value + elif key == 'ups.vendorid': + ups_data['vendor_id'] = value + + # Timers + elif key == 'ups.delay.shutdown': + ups_data['delay_shutdown'] = f"{value}s" + elif key == 'ups.delay.start': + ups_data['delay_start'] = f"{value}s" + elif key == 'ups.timer.shutdown': + ups_data['timer_shutdown'] = f"{value}s" + elif key == 'ups.timer.reboot': + ups_data['timer_reboot'] = f"{value}s" ups_list.append(ups_data) - print(f"[v0] Successfully retrieved data for UPS: {ups_identifier}") - else: - print(f"[v0] Failed to get data for UPS: {ups_identifier}") + print(f"[v0] Successfully queried UPS: {ups_name}") + except subprocess.TimeoutExpired: + print(f"[v0] Timeout querying UPS: {ups_name}") except Exception as e: - print(f"[v0] Error getting info for UPS {ups_identifier}: {e}") - - if len(ups_list) == 0: - return {} - elif len(ups_list) == 1: - return ups_list[0] - else: - return {'ups_list': ups_list, 'count': len(ups_list)} - + print(f"[v0] Error querying UPS {ups_name}: {e}") + except FileNotFoundError: - print("[v0] upsc not found - NUT client not installed") + print("[v0] upsc command not found - NUT client not installed") except Exception as e: - print(f"[v0] Error getting UPS info: {e}") - import traceback - traceback.print_exc() + print(f"[v0] Error in get_ups_info: {e}") - return {} + # Return first UPS for backward compatibility, or None if no UPS found + return ups_list[0] if ups_list else None +# + +def identify_temperature_sensor(sensor_name, adapter): + """Identify what a temperature sensor corresponds to""" + sensor_lower = sensor_name.lower() + adapter_lower = adapter.lower() if adapter else "" + + # CPU/Package temperatures + if "package" in sensor_lower or "tctl" in sensor_lower or "tccd" in sensor_lower: + return "CPU Package" + if "core" in sensor_lower: + core_num = re.search(r'(\d+)', sensor_name) + return f"CPU Core {core_num.group(1)}" if core_num else "CPU Core" + + # Motherboard/Chipset + if "temp1" in sensor_lower and ("isa" in adapter_lower or "acpi" in adapter_lower): + return "Motherboard/Chipset" + if "pch" in sensor_lower or "chipset" in sensor_lower: + return "Chipset" + + # Storage (NVMe, SATA) + if "nvme" in sensor_lower or "composite" in sensor_lower: + return "NVMe SSD" + if "sata" in sensor_lower or "ata" in sensor_lower: + return "SATA Drive" + + # GPU + if any(gpu in adapter_lower for gpu in ["nouveau", "amdgpu", "radeon", "i915"]): + return "GPU" + + # Network adapters + if "pci" in adapter_lower and "temp" in sensor_lower: + return "PCI Device" + + return sensor_name def get_temperature_info(): """Get detailed temperature information from sensors command""" @@ -4265,22 +4333,22 @@ def api_prometheus(): if ups.get('battery_charge') is not None: metrics.append(f'# HELP proxmox_ups_battery_charge_percent UPS battery charge percentage') metrics.append(f'# TYPE proxmox_ups_battery_charge_percent gauge') - metrics.append(f'proxmox_ups_battery_charge_percent{{node="{node}",ups="{ups_name}"}} {ups["battery_charge_value"]} {timestamp}') # Use numeric value + metrics.append(f'proxmox_ups_battery_charge_percent{{node="{node}",ups="{ups_name}"}} {ups["battery_charge_raw"]} {timestamp}') # Use raw value for metric - if ups.get('load_percent') is not None: # Changed from 'load' to 'load_percent' + if ups.get('load_raw') is not None: # Changed from 'load' to 'load_percent' metrics.append(f'# HELP proxmox_ups_load_percent UPS load percentage') metrics.append(f'# TYPE proxmox_ups_load_percent gauge') - metrics.append(f'proxmox_ups_load_percent{{node="{node}",ups="{ups_name}"}} {ups["load_value"]} {timestamp}') # Use numeric value + metrics.append(f'proxmox_ups_load_percent{{node="{node}",ups="{ups_name}"}} {ups["load_raw"]} {timestamp}') - if ups.get('time_left_seconds') is not None: + if ups.get('battery_runtime_seconds') is not None: # Use seconds for metric metrics.append(f'# HELP proxmox_ups_runtime_seconds UPS runtime in seconds') metrics.append(f'# TYPE proxmox_ups_runtime_seconds gauge') - metrics.append(f'proxmox_ups_runtime_seconds{{node="{node}",ups="{ups_name}"}} {ups["time_left_seconds"]} {timestamp}') + metrics.append(f'proxmox_ups_runtime_seconds{{node="{node}",ups="{ups_name}"}} {ups["battery_runtime_seconds"]} {timestamp}') if ups.get('input_voltage') is not None: metrics.append(f'# HELP proxmox_ups_input_voltage_volts UPS input voltage in volts') metrics.append(f'# TYPE proxmox_ups_input_voltage_volts gauge') - metrics.append(f'proxmox_ups_input_voltage_volts{{node="{node}",ups="{ups_name}"}} {ups["input_voltage"]} {timestamp}') # This is a string, might need conversion + metrics.append(f'proxmox_ups_input_voltage_volts{{node="{node}",ups="{ups_name}"}} {float(ups["input_voltage"].replace("V", ""))} {timestamp}') # Extract numeric value except Exception as e: print(f"[v0] Error getting hardware metrics for Prometheus: {e}") diff --git a/AppImage/types/hardware.ts b/AppImage/types/hardware.ts index 17b8596..9efe277 100644 --- a/AppImage/types/hardware.ts +++ b/AppImage/types/hardware.ts @@ -77,11 +77,53 @@ export interface PowerSupply { export interface UPS { name: string status: string - battery_charge?: number - battery_runtime?: number - load?: number - input_voltage?: number - output_voltage?: number + model?: string + manufacturer?: string + serial?: string + device_type?: string + firmware?: string + battery_charge?: string + battery_charge_raw?: number + battery_charge_low?: string + battery_runtime_seconds?: number + battery_runtime_low?: string + battery_voltage?: string + battery_voltage_nominal?: string + battery_type?: string + battery_mfr_date?: string + time_left?: string + load_percent?: string + load_raw?: number + real_power?: string + realpower_nominal?: string + apparent_power?: string + power_nominal?: string + input_voltage?: string + input_voltage_nominal?: string + input_frequency?: string + input_transfer_high?: string + input_transfer_low?: string + transfer_reason?: string + line_voltage?: string + output_voltage?: string + output_voltage_nominal?: string + output_frequency?: string + driver_name?: string + driver_version?: string + driver_version_internal?: string + driver_poll_freq?: string + driver_poll_interval?: string + ups_manufacturer?: string + ups_mfr_date?: string + product_id?: string + vendor_id?: string + beeper_status?: string + test_result?: string + delay_shutdown?: string + delay_start?: string + timer_shutdown?: string + timer_reboot?: string + raw_variables?: Record } export interface GPU {