6c8b6d81fe
System: services/system.py (psutil CPU/RAM/Disk, sysfs GPU/Temp guarded), routers/system.py GET status + restart (Whitelist) + self-update (systemd- USER, sudo-frei). Connect: services/connect.py (Cline/OpenCode/Zed/Continue/ Claude Code/Memory-MCP → Gateway model:auto, LAN-IP-Override), routers/ connect.py. Frontend: SystemView (Metrik-Bars) + ConnectView (Tool-Tabs, Copy, IP-Override). Lokal verifiziert: Backend-Smoke + Frontend-Build + Browser (System-Bars, Connect-Snippets). Docs aktualisiert (README/STATUS/Plan). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
73 lines
2.2 KiB
Python
73 lines
2.2 KiB
Python
"""
|
|
System/OS-Metriken für die Box (Bosgame / Strix Halo).
|
|
|
|
CPU/RAM/Disk via psutil (plattformübergreifend). GPU-Auslastung/VRAM/Temperatur
|
|
via sysfs (amdgpu) — nur Linux; auf anderen Plattformen None (amd-smi fehlt auf
|
|
der Box, daher sysfs). Verschachtelte Struktur wie v1 (cpu.percent, ram.used Bytes).
|
|
"""
|
|
|
|
import glob
|
|
import os
|
|
|
|
import psutil
|
|
|
|
from config import MODELS_DIR
|
|
|
|
|
|
def _read_int(path: str) -> int | None:
|
|
try:
|
|
with open(path) as f:
|
|
return int(f.read().strip())
|
|
except Exception:
|
|
return None
|
|
|
|
|
|
def _gpu_sysfs() -> dict | None:
|
|
"""AMD-GPU-Auslastung + VRAM via sysfs (Linux). None, wenn nicht vorhanden."""
|
|
base = "/sys/class/drm/card0/device"
|
|
if not os.path.isdir(base):
|
|
return None
|
|
busy = _read_int(f"{base}/gpu_busy_percent")
|
|
vram_used = _read_int(f"{base}/mem_info_vram_used")
|
|
vram_total = _read_int(f"{base}/mem_info_vram_total")
|
|
if busy is None and vram_used is None:
|
|
return None
|
|
return {"busy_percent": busy, "vram_used": vram_used, "vram_total": vram_total}
|
|
|
|
|
|
def _temps() -> dict | None:
|
|
"""CPU/GPU-Temperatur via hwmon (Linux). None bei Fehlen."""
|
|
out: dict = {}
|
|
for hw in glob.glob("/sys/class/hwmon/hwmon*"):
|
|
name = ""
|
|
try:
|
|
with open(f"{hw}/name") as f:
|
|
name = f.read().strip()
|
|
except Exception:
|
|
continue
|
|
t = _read_int(f"{hw}/temp1_input")
|
|
if t is None:
|
|
continue
|
|
c = round(t / 1000.0, 1)
|
|
if name in ("k10temp", "zenpower", "coretemp"):
|
|
out["cpu"] = c
|
|
elif name in ("amdgpu", "edge"):
|
|
out["gpu"] = c
|
|
return out or None
|
|
|
|
|
|
def system_status() -> dict:
|
|
vm = psutil.virtual_memory()
|
|
try:
|
|
du = psutil.disk_usage(str(MODELS_DIR) if MODELS_DIR.exists() else os.getcwd())
|
|
disk = {"total": du.total, "used": du.used, "percent": du.percent}
|
|
except Exception:
|
|
disk = None
|
|
return {
|
|
"cpu": {"percent": psutil.cpu_percent(interval=0.1), "cores": psutil.cpu_count()},
|
|
"ram": {"total": vm.total, "used": vm.used, "percent": vm.percent},
|
|
"gpu": _gpu_sysfs(),
|
|
"temp": _temps(),
|
|
"disk": disk,
|
|
}
|