import glob import psutil from app.sample import Sample from app.utils import sysfs _temp_path: str | None = None _temp_checked = False def _find_temp_path() -> str | None: """Find the sysfs file reporting CPU temperature, in millidegrees. Prefers hwmon sensors named k10temp (AMD), coretemp (Intel), or cpu_thermal (ARM), taking the first temp*_input of the first matching hwmon; falls back to the acpitz thermal zone. The result is cached by temp() for the process lifetime. Returns: The sysfs file to read, or None if no suitable sensor exists. """ for hwmon in sorted(glob.glob("/sys/class/hwmon/hwmon*")): name = (sysfs.read_str(f"{hwmon}/name") or "").lower() if name in ("k10temp", "coretemp", "cpu_thermal"): for t in sorted(glob.glob(f"{hwmon}/temp*_input")): return t return None for zone in sorted(glob.glob("/sys/class/thermal/thermal_zone*")): if (sysfs.read_str(f"{zone}/type") or "").lower() == "acpitz": return f"{zone}/temp" return None def temp() -> float | None: """Read the CPU temperature in degrees Celsius. The sensor path is resolved once via _find_temp_path. Sysfs reports millidegrees; the value is converted and rounded to 0.1 °C. Returns: Temperature in °C, or None if no sensor or unreadable value. """ global _temp_path, _temp_checked if not _temp_checked: _temp_checked = True _temp_path = _find_temp_path() if _temp_path is None: return None v = sysfs.read_float(_temp_path) if v is None: return None return round(v / 1000.0, 1) def prime() -> None: """Prime psutil's CPU percent counter so the next call has a real delta. psutil.cpu_percent(None) returns 0.0 on its first call; sampler_loop invokes this before the first sample for that reason. """ _ = psutil.cpu_percent(None) def core_count() -> int: """Number of logical CPU cores (at least 1).""" return psutil.cpu_count(logical=True) or 1 def fill(s: Sample) -> None: """Fill the cpu, cpu_temp, and load-average fields of a Sample. Args: s: sample to fill. """ s.cpu = psutil.cpu_percent(None) s.cpu_temp = temp() l1, l5, l15 = psutil.getloadavg() s.load1 = l1 s.load5 = l5 s.load15 = l15