Calculating absolute humidity with Munin

30th June 2024 from jc's blog

In my previous two posts I talked about rrdtool and about Munin. Today I want to showcase how the two can work together using one of Munin’s more advanced features, which are virtual plugins using aggregate graphs.

Using aggregate graphs, you can instruct Munin to build a graph from multiple data sources. “Virtual” plugins are really just plugins that don’t actually exist somewhere, you can write their instructions in your Munin master configuration file to create aggregated host overviews across multiple systems. For instance, if you have multiple database servers each containing a partition of users, you can utilize this to build a sum of how many users you have in your databases, with 0 changes required to your plugins.

Motivation

The reason for choosing absolute humidity monitoring to demonstrate this functionality is that the relative humidity is not too useful when figuring out if the outside air is more humid than yours (giving an indication as to whether you can ventilate out humidity or whether you will ventilate more in). Warmer air can “hold” more water than colder air, so the same absolute humidity at higher temperatures will be a lower relative humidity.

Existing setup

My existing “home climate” plugin reads temperature and humidity from MQTT, where my sensors publish. It outputs values for two graphs (a multigraph plugin), which looks like this:

$ /etc/munin/plugins/home_climate values
multigraph home_temperature
kitchen.value 26.800001
bedroom.value 23.100000
outside.value 20.73
multigraph home_humidity
kitchen.value 64.099998
bedroom.value 69.800003
outside.value 70

Graph aggregation

Normally, we could now update the munin master configuration file to perform the aggregation in there. Since I wrote the plugin myself, I will simply add the instructions to make another graph to my existing plugin. This will only require changes to the config output, not values, since all the aggregation will be done in the Munin master.

We start off by defining a new graph with the following instructions:

multigraph home_humidity_absolute
graph_title Home humidity (absolute)
graph_vlabel Absolute humidity in g/m³
graph_category smarthome
graph_order absolute_humidity_bedroom absolute_humidity_kitchen absolute_humidity_outside relative_humidity_bedroom=home_humidity.bedroom relative_humidity_kitchen=home_humidity.kitchen relative_humidity_outside=home_humidity.outside temperature_bedroom=home_temperature.bedroom temperature_kitchen=home_temperature.kitchen temperature_outside=home_temperature.outside

We then need to inform Munin of the labels of each field. If you want, you could also draw these on the graph, I have set .graph no on all of them as I do not want to see these values on my graph:

relative_humidity_bedroom.graph no
relative_humidity_bedroom.label Bedroom relative humidity
relative_humidity_kitchen.graph no
relative_humidity_kitchen.label Kitchen relative humidity
relative_humidity_outside.graph no
relative_humidity_outside.label Outside relative humidity
temperature_bedroom.graph no
temperature_bedroom.label Bedroom temperature
temperature_kitchen.graph no
temperature_kitchen.label Kitchen temperature
temperature_outside.graph no
temperature_outside.label Outside temperature
absolute_humidity_bedroom.label Bedroom absolute humidity
absolute_humidity_bedroom.cdef # ???
absolute_humidity_kitchen.label Kitchen absolute humidity
absolute_humidity_kitchen.cdef # ???
absolute_humidity_outside.label Outside absolute humidity
absolute_humidity_outside.cdef # ???

The cdef field is left empty. We will take care of this in a moment.

CDEFs and Reverse Polish Notation

The cdef attribute on fields allows you to perform more involved data calculations. The contents of this field will be forwarded to RRDTool1.

RRDTool expects this in Reverse Polish Notation. This might look intimidating at first since infix notation is common practice, but it is not too hard to wrap your head around. For instance, if you had a data source that reported sent bytes per second on some network interface and you wanted the bytes instead, you could use the cdef instruction sent,8,* which means sent * 8 in infix notation. You may also want to read the RRDTool rpntutorial and cdeftutorial to understand how it works. The cdeftutorial in particular contains plenty of examples such as converting from Celsius to Fahrenheit.

The main limit to what you can calculate with the CDEF statements is your imagination. RRDTool has built-in functions for averaging, median, standard deviation, calculating trends, future value predictions, and more. You may want to see the rrdgraph_rpn man page for the full listing.

Absolute humidity formula

We will use Peter Mander’s Relative to absolute humidity conversion formula, translated to a RPN that RRDTool can work with. This looks as follows:

6.112,2.7182812,17.67,t,*,t,243.5,+,/,POW,*,h,*,18.02,*,273.15,t,+,100,*,0.08314,*,/

where t is the temperature in degrees celsius and h is the relative humidity in %.

Putting it together

We now need to plug that formula into cdef statements for Munin. Remember the question marks above? We end up with the following:

absolute_humidity_bedroom.label Bedroom absolute humidity
absolute_humidity_bedroom.cdef 6.112,2.7182812,17.67,temperature_bedroom,*,temperature_bedroom,243.5,+,/,POW,*,relative_humidity_bedroom,*,18.02,*,273.15,temperature_bedroom,+,100,*,0.08314,*,/
absolute_humidity_kitchen.label Kitchen absolute humidity
absolute_humidity_kitchen.cdef 6.112,2.7182812,17.67,temperature_kitchen,*,temperature_kitchen,243.5,+,/,POW,*,relative_humidity_kitchen,*,18.02,*,273.15,temperature_kitchen,+,100,*,0.08314,*,/
absolute_humidity_outside.label Outside absolute humidity
absolute_humidity_outside.cdef 6.112,2.7182812,17.67,temperature_outside,*,temperature_outside,243.5,+,/,POW,*,relative_humidity_outside,*,18.02,*,273.15,temperature_outside,+,100,*,0.08314,*,/

It certainly won’t win an award for readability. But I’m not sure other monitoring systems such as Prometheus make it much more readable either, except for the use of infix notation.


  1. Munin will perform a pass over the CDEF instructions before passing them on to RRDTool to translate any variables referencing Munin values to their internal name. ↩︎

reply via email