diff --git a/HOWTO.md b/HOWTO.md index d1505a6..3939261 100644 --- a/HOWTO.md +++ b/HOWTO.md @@ -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 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 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 diff --git a/config.yaml b/config.yaml index 13b0b00..c6b895e 100644 --- a/config.yaml +++ b/config.yaml @@ -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 diff --git a/read_temperature.py b/read_temperature.py index 5cdad5d..609f1b0 100644 --- a/read_temperature.py +++ b/read_temperature.py @@ -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']}",