better graphing

This commit is contained in:
2026-08-22 09:21:17 +02:00
parent 88fc09d2bd
commit 4d04d961a3
3 changed files with 40 additions and 7 deletions
+4
View File
@@ -402,6 +402,10 @@ Each graph contains a filled temperature area, a solid humidity line, and a
dotted battery line. Initially, most of each graph is blank because no historic
samples existed before the RRD was created.
Temperature uses the left axis with a default range of 030°C. Humidity and
battery use the right axis with a default range of 0100%. These ranges and
labels can be changed in `config.yaml` under `graph.axes`.
The collector also generates `/var/www/html/sensors/index.html`. It displays
six graphs at a time and has controls for switching between the daily, monthly,
and two-year periods. It remembers the selected period and refreshes the PNGs
+9 -2
View File
@@ -72,9 +72,16 @@ graph:
width: 1000
height: 360
end: now
vertical_label: degrees C / percent
slope_mode: true
alt_autoscale: true
axes:
temperature:
minimum: 0
maximum: 30
label: degrees C
percentage:
minimum: 0
maximum: 100
label: percent
periods:
day:
start: end-1d
+27 -5
View File
@@ -194,11 +194,26 @@ def render_graphs(sensor: Sensor, rrd_path: Path, config: Config) -> None:
temperature = graph["temperature"]
humidity = graph["humidity"]
battery = graph["battery"]
temperature_axis = graph["axes"]["temperature"]
percentage_axis = graph["axes"]["percentage"]
temperature_minimum = float(temperature_axis["minimum"])
temperature_maximum = float(temperature_axis["maximum"])
percentage_minimum = float(percentage_axis["minimum"])
percentage_maximum = float(percentage_axis["maximum"])
temperature_span = temperature_maximum - temperature_minimum
percentage_span = percentage_maximum - percentage_minimum
if temperature_span <= 0 or percentage_span <= 0:
raise SystemExit("Graph axis maximums must be greater than their minimums")
percentage_to_temperature = temperature_span / percentage_span
right_axis_scale = percentage_span / temperature_span
right_axis_shift = percentage_minimum - temperature_minimum * right_axis_scale
percentage_transform = (
f"{percentage_minimum},-,{percentage_to_temperature},*,{temperature_minimum},+"
)
optional_flags = []
if graph.get("slope_mode", False):
optional_flags.append("--slope-mode")
if graph.get("alt_autoscale", False):
optional_flags.append("--alt-autoscale")
for suffix, period in graph["periods"].items():
output = config.graph_dir / f"{sensor.slug}_{suffix}.png"
@@ -206,15 +221,22 @@ def render_graphs(sensor: Sensor, rrd_path: Path, config: Config) -> None:
"graph", str(output), "--start", str(period["start"]),
"--end", str(graph["end"]),
"--title", f"{sensor.name} - {period['label']}",
"--vertical-label", str(graph["vertical_label"]),
"--vertical-label", str(temperature_axis["label"]),
"--right-axis", f"{right_axis_scale}:{right_axis_shift}",
"--right-axis-label", str(percentage_axis["label"]),
"--lower-limit", str(temperature_minimum),
"--upper-limit", str(temperature_maximum),
"--rigid",
"--width", str(graph["width"]), "--height", str(graph["height"]),
*optional_flags,
f"DEF:temp={rrd_path}:temperature:AVERAGE",
f"DEF:humidity={rrd_path}:humidity:AVERAGE",
f"DEF:battery={rrd_path}:battery:AVERAGE",
f"CDEF:humidity_scaled=humidity,{percentage_transform}",
f"CDEF:battery_scaled=battery,{percentage_transform}",
f"AREA:temp#{temperature['color']}:{temperature['legend']}",
f"LINE{humidity['line_width']}:humidity#{humidity['color']}:{humidity['legend']}",
f"LINE{battery['line_width']}:battery#{battery['color']}:{battery['legend']}:dashes={battery['dashes']}",
f"LINE{humidity['line_width']}:humidity_scaled#{humidity['color']}:{humidity['legend']}",
f"LINE{battery['line_width']}:battery_scaled#{battery['color']}:{battery['legend']}:dashes={battery['dashes']}",
f"GPRINT:temp:LAST:{temperature['last_value_format']}",
f"GPRINT:humidity:LAST:{humidity['last_value_format']}",
f"GPRINT:battery:LAST:{battery['last_value_format']}",