a second pae with a week history

This commit is contained in:
2026-08-10 20:49:20 +02:00
parent 2f95758c75
commit da15ec1ca4
2 changed files with 218 additions and 35 deletions
+202 -33
View File
@@ -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():
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="refresh" content="60">
<title>APC BX950MI UPS Dashboard</title>
<title>{page_title}</title>
<style>
:root {{ color-scheme: light dark; font-family: system-ui, sans-serif; }}
body {{ margin: 0; background: #10141c; color: #edf2f7; }}
@@ -907,6 +1020,9 @@ def create_dashboard():
h1, h2, h3, p {{ margin-top: 0; }}
h1 {{ margin-bottom: .35rem; }}
header p {{ color: #aab5c5; }}
nav {{ margin-top: 1rem; }}
nav a {{ display: inline-block; padding: .6rem .9rem; border-radius: 8px; background: #263449; color: #edf2f7; text-decoration: none; }}
nav a:hover {{ background: #344763; }}
section {{ margin: 1.5rem 0 2.5rem; }}
h2 {{ border-bottom: 1px solid #344054; padding-bottom: .5rem; }}
.grid {{ display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; }}
@@ -922,8 +1038,9 @@ def create_dashboard():
</head>
<body>
<header>
<h1>APC BX950MI UPS Dashboard</h1>
<h1>{page_title}</h1>
<p>Updated {generated_at} · This page refreshes every 60 seconds.</p>
<nav><a href="{other_page}">{other_page_label}</a></nav>
</header>
<main>
{''.join(sections)}
@@ -933,10 +1050,60 @@ def create_dashboard():
</html>
"""
with open(HTML_FILE, "w", encoding="utf-8") as dashboard:
with open(html_file, "w", encoding="utf-8") as dashboard:
dashboard.write(document)
print(f"Created dashboard: {HTML_FILE}")
print(f"Created dashboard: {html_file}")
def create_dashboards():
"""Create the long-term and one-week HTML dashboards."""
write_dashboard(
HTML_FILE,
"APC BX950MI UPS Dashboard",
[
("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),
],
),
],
"week.html",
"View one-week history",
)
write_dashboard(
WEEKLY_HTML_FILE,
"APC BX950MI — One-Week History",
[
("UPS status", [("Last week", GRAPH_STATUS_1W)]),
("Battery charge", [("Last week", GRAPH_BATTERY_1W)]),
("Battery runtime", [("Last week", GRAPH_RUNTIME_1W)]),
("UPS load", [("Last week", GRAPH_LOAD_1W)]),
("Nominal power", [("Last week", GRAPH_POWER_1W)]),
],
"index.html",
"View long-term history",
)
def main():
@@ -946,7 +1113,9 @@ def main():
create_graphs()
create_dashboard()
create_weekly_graphs()
create_dashboards()
if __name__ == "__main__":