Files
nvcurve/nvcurve/nvapi/types.py
T
ARIA 6cb33187d3 feat: full fan control — all fans or individual fans
The fan curve previously only controlled fan index 0; secondary fans
stayed on driver control. The curve can now target all fans (new
default) or any individual fan(s).

Backend:
- hal/fans.py: get_num_fans() via nvmlDeviceGetNumFans; get_fan_info()
  returns per-fan speeds; set_fan_speed() accepts a fan index list
  (None = all fans; all-fans mode is lenient toward driver-locked
  fans, explicit lists are strict); reset_fan() restores all fans.
- server.py: per-GPU fan_targets state; the poller applies the curve to
  all target fans and logs write failures (once per distinct error);
  activation validates targets against the hardware (stale indices fall
  back to all fans); POST /api/fans accepts fans, POST /api/fans/speed
  accepts a fan index, GET /api/fans returns num_fans/fans/fan_targets.
- Persistence format is now {"curve": ..., "fans": ...}; legacy
  bare-curve entries migrate to "all fans" at startup.
- Profiles save/apply fan_targets alongside fan_curve.
- MonitoringSample carries per-fan speeds for live gauges.

Frontend:
- Fans tab: All / Fan 1 / Fan 2 / ... selector with live per-fan %;
  the selection is applied together with the curve.
- Live monitor: per-fan gauges with sparklines for multi-fan GPUs.
2026-09-10 15:48:28 +02:00

82 lines
2.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Clean Python dataclasses for V/F curve data.
Raw struct manipulation stays in the call layer (bootstrap.py + hal/).
Everything above HAL works with these types.
"""
from dataclasses import dataclass
@dataclass
class VFPoint:
index: int
freq_khz: int # Base frequency from VFP curve
volt_uv: int # Voltage from VFP curve
delta_khz: int # Offset from ClockBoostTable (signed)
domain: str = "gpu" # "gpu" or "memory"
@property
def effective_freq_khz(self) -> int:
return self.freq_khz + self.delta_khz
@property
def freq_mhz(self) -> float:
return self.freq_khz / 1000.0
@property
def effective_freq_mhz(self) -> float:
return self.effective_freq_khz / 1000.0
@property
def volt_mv(self) -> float:
return self.volt_uv / 1000.0
@property
def delta_mhz(self) -> float:
return self.delta_khz / 1000.0
@dataclass
class CurveState:
points: list[VFPoint]
timestamp: float
gpu_name: str
@dataclass
class MonitoringSample:
timestamp: float
voltage_uv: int | None
clock_mhz: float | None
temp_c: float | None
power_w: float | None
fan_pct: float | None
pstate: int | None # Performance state: 0 (P0, max) – 15 (P15, min)
mem_used_bytes: int | None # VRAM used (bytes)
mem_total_bytes: int | None # VRAM total (bytes)
gpu_util_pct: float | None # GPU core utilization (0–100)
mem_util_pct: float | None # Memory bus utilization (0–100)
mem_clock_mhz: float | None = None # Current memory clock (NVML_CLOCK_MEM)
throttle_reasons: int | None = None # Active clock-throttle bitmask (NVML)
pcie_link_width: int | None = None # Current PCIe link width (x1..x16)
pcie_link_generation: int | None = None # Current PCIe link generation (1..5)
mem_temp_c: float | None = None # VRAM temperature (if the GPU exposes it)
fans: list[float | None] | None = None # Per-fan speed % (index-aligned)
@dataclass
class GpuInfo:
name: str
index: int
uuid: str | None = None
pci_bus_id: int | None = None
@dataclass
class SnapshotInfo:
filepath: str
timestamp: str
gpu: str
nonzero_offsets: int
size: int