"""System-Plugin: CPU-Last, RAM, Uptime. Responsive.""" import os, sys sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) from plugins.base import Widget from palette import FG, INFO, OK, WARN, ALERT, measure, fit_font, is_small, is_wide, hbar, parse_thresholds, DEFAULT_THRESHOLDS def _read_loadavg(): try: with open("/proc/loadavg") as f: parts = f.read().split() return float(parts[0]) except Exception: return 0.0 def _read_mem(): try: with open("/proc/meminfo") as f: lines = f.readlines() total = int([l for l in lines if l.startswith("MemTotal")][0].split()[1]) avail = int([l for l in lines if l.startswith("MemAvailable")][0].split()[1]) return total, total - avail, avail except Exception: return 1, 0, 1 def _read_uptime(): try: with open("/proc/uptime") as f: return float(f.read().split()[0]) except Exception: return 0.0 def _fmt_uptime(s): days, rem = divmod(int(s), 86400) h, rem = divmod(rem, 3600) m = rem // 60 if days: return f"{days}d {h}h" if h: return f"{h}h {m}m" return f"{m}m" def _system_stats(): ncpu = os.cpu_count() or 1 la = _read_loadavg() total, used, avail = _read_mem() return { "cpu_pct": min(int((la / ncpu) * 100), 100), "ram_used_mb": used // 1024, "ram_total_mb": total // 1024, "ram_pct": int(used * 100 / total) if total else 0, "uptime_s": _read_uptime(), } class Widget(Widget): name = "system" label = "System (CPU/RAM)" description = "Live CPU-Last, RAM-Auslastung, Uptime. Responsive Layout fuer 1x1 bis 4x4." category = "system" config_schema = [ {"key": "show_uptime", "label": "Uptime anzeigen", "type": "bool", "default": True}, {"key": "show_load", "label": "Load Average anzeigen", "type": "bool", "default": True}, {"key": "compact", "label": "Kompaktmodus (nur Prozent)", "type": "bool", "default": False}, {"key": "bar_thresholds", "label": "CPU/RAM-Schwellen (Format: ok,warn,alert oder ok@50,warn@80,alert@95)", "type": "string", "default": "ok@50,warn@80,alert@95", "help": "Legt fest, ab wann eine Bar grün/gelb/rot wird. Mit '@P' setzt du die Schwelle in Prozent."}, {"key": "bar_gradient", "label": "Verlaufsmodus (Balken zeigt alle Stufen gleichzeitig)", "type": "bool", "default": True, "help": "Wenn aus, ist die Bar einfarbig in der Farbe der aktuellen Schwelle."}, ] default_config = {"show_uptime": True, "show_load": True, "compact": False, "bar_thresholds": "ok@50,warn@80,alert@95", "bar_gradient": True} def _bar_args(self): return (parse_thresholds(self.cfg("bar_thresholds")), bool(self.cfg("bar_gradient", True))) def fetch(self): return _system_stats() def render(self, draw, fonts, x, y, w, h): from palette import fill_for, measure pad = 8 s = self.fetch() cpu, ram = s["cpu_pct"], s["ram_pct"] thresholds, gradient = self._bar_args() # Im compact/small mode bleiben die Fallback-Farben (Text-Label reicht) if self.cfg("compact") or is_small(w, h): # mini/compact: nur CPU + RAM als 2 kleine bars label_font = fit_font(draw, "CPU", fonts, w - 2 * pad, 20) draw.text((x + pad, y + pad), "CPU", font=label_font, fill=FG) bar_y = y + pad + 22 bar_w = w - 2 * pad bar_h = max(8, (h - 30) // 4) hbar(draw, x + pad, bar_y, bar_w, bar_h, cpu, thresholds=thresholds, gradient=gradient) ram_y = bar_y + bar_h + 6 draw.text((x + pad, ram_y), "RAM", font=label_font, fill=FG) hbar(draw, x + pad, ram_y + 22, bar_w, bar_h, ram, thresholds=thresholds, gradient=gradient) return if is_wide(w, h): # Wide strip: CPU% | RAM% | Uptime (each third) col_w = w // 3 for i, (label, pct) in enumerate([ ("CPU", cpu), ("RAM", ram), ]): cx = x + i * col_w # Color = passende Schwelle col_color = WARN if thresholds: for max_p, c in thresholds: if pct <= max_p: col_color = c; break label_font = fit_font(draw, label, fonts, col_w - 2 * pad, 22) draw.text((cx + pad, y + pad), f"{label} {pct}%", font=label_font, fill=col_color) if self.cfg("show_uptime"): up_str = "up " + _fmt_uptime(s["uptime_s"]) font_up = fit_font(draw, up_str, fonts, col_w - 2 * pad, 22) draw.text((x + 2 * col_w + pad, y + pad), up_str, font=font_up, fill=INFO) return # Standard: Header, CPU-Bar, RAM-Bar, Uptime header_font = fonts.get("28") or fonts.get("24") draw.text((x + pad, y + pad), "SYSTEM", font=header_font, fill=INFO) # Big numbers + bars cur_y = y + 40 # CPU row text = f"CPU: {cpu}%" draw.text((x + pad, cur_y), text, font=fonts.get("32", fonts.get("24")), fill=FG) bar_y = cur_y + 36 bar_h = 28 hbar(draw, x + pad, bar_y, w - 2 * pad, bar_h, cpu, thresholds=thresholds, gradient=gradient) # RAM row cur_y = bar_y + bar_h + 12 text = f"RAM: {s['ram_used_mb']}/{s['ram_total_mb']} MB ({ram}%)" font_text = fit_font(draw, text, fonts, w - 2 * pad, 28) draw.text((x + pad, cur_y), text, font=font_text, fill=FG) bar_y2 = cur_y + 30 hbar(draw, x + pad, bar_y2, w - 2 * pad, bar_h, ram, thresholds=thresholds, gradient=gradient) # Bottom: load + uptime bottom_y = bar_y2 + bar_h + 8 if self.cfg("show_load") or self.cfg("show_uptime"): parts = [] if self.cfg("show_load"): parts.append(f"load {s['cpu_pct']/100.0 * (os.cpu_count() or 1):.2f}") if self.cfg("show_uptime"): parts.append("up " + _fmt_uptime(s["uptime_s"])) line = " · ".join(parts) font_bot = fit_font(draw, line, fonts, w - 2 * pad, 22) draw.text((x + pad, bottom_y), line, font=font_bot, fill=FG)