#!/usr/bin/env python3 """ plot_power.py Forgejo CI: visualize AMD TUF APU/GPU telemetry as a clean bar graph. Compatible with headerless CSV output from amd-monitor.sh Author: MarkMental """ import pandas as pd import matplotlib.pyplot as plt CSV_PATH = "power_log.csv" OUTPUT_FILE = "power_graph.png" RETENTION_SECONDS = 3600 print(f"📊 Reading {CSV_PATH}...") # --- Load CSV without headers --- df = pd.read_csv( CSV_PATH, names=["timestamp", "apu_w", "gpu_w", "total_w", "apu_temp", "gpu_temp"], comment="#", ) # --- Convert timestamps --- df["timestamp"] = pd.to_datetime(df["timestamp"], errors="coerce") latest_ts = df["timestamp"].dropna().max() if pd.notna(latest_ts): cutoff = latest_ts - pd.Timedelta(seconds=RETENTION_SECONDS) df = df[df["timestamp"] >= cutoff] df["time_fmt"] = df["timestamp"].dt.strftime("%Y-%m-%d %H:%M:%S") # --- Downsample if too many entries --- if len(df) > 200: df = df.iloc[::max(1, len(df)//200), :] # --- Plot --- fig, ax1 = plt.subplots(figsize=(12, 6)) x = range(len(df)) width = 0.3 ax1.bar([i - width for i in x], df["apu_w"], width, label="APU Power (W)", color="#ff9933") ax1.bar(x, df["gpu_w"], width, label="GPU Power (W)", color="#66cc66") ax1.bar([i + width for i in x], df["total_w"], width, label="Total Power (W)", color="#3399ff") ax1.set_xlabel("Timestamp (YYYY-MM-DD HH:MM:SS)") ax1.set_ylabel("Power (Watts)") ax1.set_title("AMD TUF Power & Temperature Log") ax1.set_xticks(x[::max(1, len(x)//10)]) ax1.set_xticklabels(df["time_fmt"].iloc[::max(1, len(x)//10)], rotation=45, ha="right") # --- Secondary axis for temperatures --- ax2 = ax1.twinx() ax2.plot(x, df["apu_temp"], "--", color="red", linewidth=1.2, label="APU Temp (°C)") ax2.plot(x, df["gpu_temp"], "--", color="purple", linewidth=1.2, label="GPU Temp (°C)") ax2.set_ylabel("Temperature (°C)") ax2.set_ylim(min(df[["apu_temp","gpu_temp"]].min())-5, max(df[["apu_temp","gpu_temp"]].max())+5) # --- Combined legend --- lines1, labels1 = ax1.get_legend_handles_labels() lines2, labels2 = ax2.get_legend_handles_labels() ax1.legend(lines1 + lines2, labels1 + labels2, loc="upper left") plt.grid(axis="y", alpha=0.3) plt.tight_layout() plt.savefig(OUTPUT_FILE, dpi=150) print(f"✅ Saved graph: {OUTPUT_FILE}")