- dashboard.py: plugin-based renderer with 4x4 grid layout - admin.py: web UI with layout editor + plugin configs - layout.py: pack algorithm, item placement, grid system - plugins/: clock, weather, system, spotify, strava, gmail, minimax, hello - network_watchdog.py: WiFi AP/client mode management - waveshare_epd_init.py: vendor driver stub
164 lines
6.3 KiB
Python
164 lines
6.3 KiB
Python
"""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)
|