From da15ec1ca4425679c73bc4c6c2a3b6b0ccf2ac8e Mon Sep 17 00:00:00 2001 From: Ignace Date: Mon, 10 Aug 2026 20:49:20 +0200 Subject: [PATCH] a second pae with a week history --- README.md | 18 +++- ups2rrd.py | 235 +++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 218 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 266b4a5..62993b5 100644 --- a/README.md +++ b/README.md @@ -774,17 +774,23 @@ The script creates: ```text /var/www/html/ups/index.html +/var/www/html/ups/week.html /var/www/html/ups/status-3m.png +/var/www/html/ups/status-1w.png +/var/www/html/ups/battery-1w.png /var/www/html/ups/battery-1y.png /var/www/html/ups/battery-5y.png +/var/www/html/ups/runtime-1w.png /var/www/html/ups/runtime-1y.png /var/www/html/ups/runtime-5y.png +/var/www/html/ups/load-1w.png /var/www/html/ups/load-3m.png +/var/www/html/ups/power-1w.png /var/www/html/ups/power-1y.png /var/www/html/ups/power-5y.png ``` @@ -793,19 +799,27 @@ If `/var/www/html` is served by the web server, these can therefore be viewed as ```text /ups/ +/ups/week.html /ups/status-3m.png +/ups/status-1w.png +/ups/battery-1w.png /ups/battery-1y.png /ups/battery-5y.png +/ups/runtime-1w.png /ups/runtime-1y.png /ups/runtime-5y.png +/ups/load-1w.png /ups/load-3m.png +/ups/power-1w.png /ups/power-1y.png /ups/power-5y.png ``` -The responsive dashboard groups all graphs on one page and refreshes every -60 seconds. Clicking a graph opens its full-size PNG. +The main responsive dashboard groups the long-term graphs on one page. The +weekly dashboard at `/ups/week.html` shows one-week history for every collected +value. The pages link to each other, refresh every 60 seconds, and open a +full-size PNG when a graph is clicked. --- diff --git a/ups2rrd.py b/ups2rrd.py index d79144e..fd60387 100644 --- a/ups2rrd.py +++ b/ups2rrd.py @@ -52,6 +52,13 @@ RRD_FILE = os.path.join(RRD_DIR, "bx950mi.rrd") GRAPH_DIR = "/var/www/html/ups" HTML_FILE = os.path.join(GRAPH_DIR, "index.html") +WEEKLY_HTML_FILE = os.path.join(GRAPH_DIR, "week.html") + +GRAPH_STATUS_1W = os.path.join(GRAPH_DIR, "status-1w.png") +GRAPH_BATTERY_1W = os.path.join(GRAPH_DIR, "battery-1w.png") +GRAPH_RUNTIME_1W = os.path.join(GRAPH_DIR, "runtime-1w.png") +GRAPH_LOAD_1W = os.path.join(GRAPH_DIR, "load-1w.png") +GRAPH_POWER_1W = os.path.join(GRAPH_DIR, "power-1w.png") GRAPH_STATUS = os.path.join(GRAPH_DIR, "status-3m.png") @@ -817,6 +824,132 @@ def graph_power_5y(): ) +# --------------------------------------------------------------------------- +# Graphs: one-week history +# --------------------------------------------------------------------------- + +def graph_weekly_status(): + """UPS status for the last week.""" + + now = int(time.time()) + run_rrdtool( + [ + "graph", GRAPH_STATUS_1W, + "--start", str(now - 7 * 86400), + "--end", str(now), + "--width", "1000", + "--height", "350", + "--title", "APC BX950MI - UPS Status (1 Week)", + "--vertical-label", "Status", + "--lower-limit", "0", + "--upper-limit", "4", + "--rigid", + f"DEF:status={RRD_FILE}:status:LAST", + "LINE2:status#0000FF:UPS status", + "COMMENT:\\n", + "COMMENT:0 = On Line (OL)\\n", + "COMMENT:1 = On Battery (OB)\\n", + "COMMENT:2 = Low Battery (LB)\\n", + "COMMENT:3 = Replace Battery (RB)\\n", + "COMMENT:4 = Unknown\\n", + ] + ) + + +def graph_weekly_metric( + output_file, + title, + vertical_label, + definition, + line, + value_name, + unit, + limits=None, + calculation=None, +): + """Create a one-week graph for a numeric RRD data source.""" + + now = int(time.time()) + command = [ + "graph", output_file, + "--start", str(now - 7 * 86400), + "--end", str(now), + "--width", "1000", + "--height", "350", + "--title", f"APC BX950MI - {title} (1 Week)", + "--vertical-label", vertical_label, + ] + + if limits: + command.extend( + [ + "--lower-limit", str(limits[0]), + "--upper-limit", str(limits[1]), + ] + ) + + command.append(definition) + + if calculation: + command.append(calculation) + + command.extend( + [ + line, + f"GPRINT:{value_name}:MIN:Minimum %.1lf{unit}", + f"GPRINT:{value_name}:AVERAGE:Average %.1lf{unit}", + f"GPRINT:{value_name}:MAX:Maximum %.1lf{unit}", + ] + ) + + run_rrdtool(command) + + +def create_weekly_graphs(): + """Generate one-week graphs for every collected value.""" + + graph_weekly_status() + graph_weekly_metric( + GRAPH_BATTERY_1W, + "Battery Charge", + "Charge (%)", + f"DEF:charge={RRD_FILE}:battery_charge:AVERAGE", + "LINE2:charge#008000:Battery charge", + "charge", + "%%", + limits=(0, 100), + ) + graph_weekly_metric( + GRAPH_RUNTIME_1W, + "Battery Runtime", + "Runtime (minutes)", + f"DEF:runtime={RRD_FILE}:battery_runtime:AVERAGE", + "LINE2:minutes#800080:Battery runtime", + "minutes", + " min", + calculation="CDEF:minutes=runtime,60,/", + ) + graph_weekly_metric( + GRAPH_LOAD_1W, + "UPS Load", + "Load (%)", + f"DEF:load={RRD_FILE}:ups_load:AVERAGE", + "LINE2:load#FF8000:UPS load", + "load", + "%%", + limits=(0, 100), + ) + graph_weekly_metric( + GRAPH_POWER_1W, + "Nominal Power", + "Power (W)", + f"DEF:power={RRD_FILE}:realpower_nominal:AVERAGE", + "LINE2:power#CC0000:Nominal power", + "power", + " W", + ) + + # --------------------------------------------------------------------------- # Main # --------------------------------------------------------------------------- @@ -840,37 +973,17 @@ def create_graphs(): graph_power_5y() -def create_dashboard(): - """Create a responsive HTML dashboard for the generated graphs.""" +def write_dashboard( + html_file, + page_title, + graph_groups, + other_page, + other_page_label, +): + """Write a responsive HTML dashboard for a set of graphs.""" generated_at = time.strftime("%Y-%m-%d %H:%M:%S %Z") - graph_groups = [ - ("UPS status", [("Last 3 months", GRAPH_STATUS)]), - ( - "Battery charge", - [ - ("Last year", GRAPH_BATTERY_1Y), - ("Last 5 years", GRAPH_BATTERY_5Y), - ], - ), - ( - "Battery runtime", - [ - ("Last year", GRAPH_RUNTIME_1Y), - ("Last 5 years", GRAPH_RUNTIME_5Y), - ], - ), - ("UPS load", [("Last 3 months", GRAPH_LOAD)]), - ( - "Nominal power", - [ - ("Last year", GRAPH_POWER_1Y), - ("Last 5 years", GRAPH_POWER_5Y), - ], - ), - ] - sections = [] for title, graphs in graph_groups: @@ -898,7 +1011,7 @@ def create_dashboard(): - APC BX950MI UPS Dashboard + {page_title}