better graphing
This commit is contained in:
@@ -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
|
dotted battery line. Initially, most of each graph is blank because no historic
|
||||||
samples existed before the RRD was created.
|
samples existed before the RRD was created.
|
||||||
|
|
||||||
|
Temperature uses the left axis with a default range of 0–30°C. Humidity and
|
||||||
|
battery use the right axis with a default range of 0–100%. 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
|
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,
|
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
|
and two-year periods. It remembers the selected period and refreshes the PNGs
|
||||||
|
|||||||
+9
-2
@@ -72,9 +72,16 @@ graph:
|
|||||||
width: 1000
|
width: 1000
|
||||||
height: 360
|
height: 360
|
||||||
end: now
|
end: now
|
||||||
vertical_label: degrees C / percent
|
|
||||||
slope_mode: true
|
slope_mode: true
|
||||||
alt_autoscale: true
|
axes:
|
||||||
|
temperature:
|
||||||
|
minimum: 0
|
||||||
|
maximum: 30
|
||||||
|
label: degrees C
|
||||||
|
percentage:
|
||||||
|
minimum: 0
|
||||||
|
maximum: 100
|
||||||
|
label: percent
|
||||||
periods:
|
periods:
|
||||||
day:
|
day:
|
||||||
start: end-1d
|
start: end-1d
|
||||||
|
|||||||
+27
-5
@@ -194,11 +194,26 @@ def render_graphs(sensor: Sensor, rrd_path: Path, config: Config) -> None:
|
|||||||
temperature = graph["temperature"]
|
temperature = graph["temperature"]
|
||||||
humidity = graph["humidity"]
|
humidity = graph["humidity"]
|
||||||
battery = graph["battery"]
|
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 = []
|
optional_flags = []
|
||||||
if graph.get("slope_mode", False):
|
if graph.get("slope_mode", False):
|
||||||
optional_flags.append("--slope-mode")
|
optional_flags.append("--slope-mode")
|
||||||
if graph.get("alt_autoscale", False):
|
|
||||||
optional_flags.append("--alt-autoscale")
|
|
||||||
|
|
||||||
for suffix, period in graph["periods"].items():
|
for suffix, period in graph["periods"].items():
|
||||||
output = config.graph_dir / f"{sensor.slug}_{suffix}.png"
|
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"]),
|
"graph", str(output), "--start", str(period["start"]),
|
||||||
"--end", str(graph["end"]),
|
"--end", str(graph["end"]),
|
||||||
"--title", f"{sensor.name} - {period['label']}",
|
"--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"]),
|
"--width", str(graph["width"]), "--height", str(graph["height"]),
|
||||||
*optional_flags,
|
*optional_flags,
|
||||||
f"DEF:temp={rrd_path}:temperature:AVERAGE",
|
f"DEF:temp={rrd_path}:temperature:AVERAGE",
|
||||||
f"DEF:humidity={rrd_path}:humidity:AVERAGE",
|
f"DEF:humidity={rrd_path}:humidity:AVERAGE",
|
||||||
f"DEF:battery={rrd_path}:battery: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"AREA:temp#{temperature['color']}:{temperature['legend']}",
|
||||||
f"LINE{humidity['line_width']}:humidity#{humidity['color']}:{humidity['legend']}",
|
f"LINE{humidity['line_width']}:humidity_scaled#{humidity['color']}:{humidity['legend']}",
|
||||||
f"LINE{battery['line_width']}:battery#{battery['color']}:{battery['legend']}:dashes={battery['dashes']}",
|
f"LINE{battery['line_width']}:battery_scaled#{battery['color']}:{battery['legend']}:dashes={battery['dashes']}",
|
||||||
f"GPRINT:temp:LAST:{temperature['last_value_format']}",
|
f"GPRINT:temp:LAST:{temperature['last_value_format']}",
|
||||||
f"GPRINT:humidity:LAST:{humidity['last_value_format']}",
|
f"GPRINT:humidity:LAST:{humidity['last_value_format']}",
|
||||||
f"GPRINT:battery:LAST:{battery['last_value_format']}",
|
f"GPRINT:battery:LAST:{battery['last_value_format']}",
|
||||||
|
|||||||
Reference in New Issue
Block a user