a second pae with a week history
This commit is contained in:
+202
-33
@@ -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__":
|
||||
|
||||
Reference in New Issue
Block a user