Files
epaper-dashboard/plugins/system.py
T
ki 28124c5617 Initial commit: epaper-dashboard for 7.3" ACeP 7-Color display
- 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
2026-08-26 14:12:43 +04:00

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)