Openhab2 RPI System Temperature and DS18B20 OneWire Chart with persistence

This tutorial is based on this, this, this and a lot of threads and tutorials i had to read till i got it working.

I try to give a step by step Tutorial how to get a temperature chart with OpenHAB2 and the ExecBinding 2 using the RRD4J database for persistency.

I will not explain everything in detail, please read this post to get an basic understanding of how OpenHAB works. And use the tutorials and the manual to get deeper insight of how the different parts work.

Now to read out the CPU temperatur we can use following command. The temperature will be return as milli degree celsius so we will need to devide it later by thausend to have it as degrees.

cat /sys/class/thermal/thermal_zone0/temp
48312

Testing if the user openhab can execute this command is a good idea for all commands we try to execute later from OpenHAB. As this works we can continue configuring our setup.

sudo -u openhab cat /sys/class/thermal/thermal_zone0/temp
47774

We need to install the ExecBinding and the Persistence database RRD4J Addon. As i never know how they are called and which are available, i use the Karaf console to retrieve the full name. feature:list will return all available features.
Also the REGEX and EXEC transformation have to be installed.

ssh -p 8101 openhab@localhost
habopen
feature:list |grep rrd4j
feature:install openhab-persistence-rrd4j
feature:list |grep exec
feature:install openhab-binding-exec
feature:list |grep regex
feature:install openhab-transformation-regex
feature:list |grep exec
feature:install openhab-transformation-exec
logout

Persistence means to store the states of items over time. For that we use the simple RRD4J database. Which is explained here or here.

We need to set up a rule which defines when which data schould be stored. RRD4J will take care to minimize the database but only provides summerized data of the past. We create the config file as user openhab so we do not have to hassl with file access rights later on.

sudo -u openhab nano /etc/openhab2/persistence/rrd4j.persist

The first part configures when to store the data and the second part which items to store. We use a group item which will be stored, this will make it easier to add more values later on.

Strategies {
    // for rrd charts, we need a cron strategy, every Minute is a must have.
    everyMinute : "0 * * * * ?"
    // get the data reduced for older values to keep database small
    everyHour : "0 0 * * * ?"
    everyDay : "0 0 0 * * ?"
    
    default = everyChange
}

Items {
    // additionally persist Items
    System_Temperature_Chart* : strategy = everyUpdate, everyMinute
}

Now we have to set up Things, Items, a Sitemap and a Rule.

sudo nano /etc/openhab2/things/exec.things
Thing exec:command:cpuTemp [
        // Command to execute
        command="cat /sys/class/thermal/thermal_zone0/temp",
        // interval time in seconds
        interval=10,
        // should it run when input channel is changed
        autorun=false,
        // REGEX transformation to make 44007/1000 => 44.007
        // explanation for the regex
        // s/           - substitute
        // (.{2})(.{3}) - a a number with 5 characters, split in two groups
        // /$1.$2/g     - returned value= first group, a dot, then second group, used to devide by 1000
        transform="REGEX(s/(.{2})(.{3})/$1.$2/g)"
        // alternative EXEC transformation with python 
        //transform="EXEC(python3 -c \"print(%s/1000)\")"
]

sudo nano /etc/openhab2/items/SysTemp.items
// System temperatures
Group  System_Temperature_Chart (System, Charts)
Number System_Temperature_Chart_Period "Periode" (System)
Number System_Temperature_CPU "Temperature CPU [%.1f °C]" <temperature> (System_Temperature_Chart) 

// Output of command line execution 
String System_Temperature_CPU_out { channel="exec:command:cpuTemp:output" }

sudo nano /etc/openhab2/sitemaps/SysTemp.sitemap
// Name of file and name of sitemap has to be the same
sitemap SysTemp label="System Temperature RPI"
{
        Frame {
            Text item=System_Temperature_CPU
        }
        Frame {
            Switch item=System_Temperature_Chart_Period mappings=[0="1h", 1="4h", 2="8h", 3="12h", 4="24h"]
            Chart  item=System_Temperature_Chart period=h   refresh=60000 visibility=[System_Temperature_Chart_Period==0, System_Temperature_Chart_Period=="Uninitialized"]
            Chart  item=System_Temperature_Chart period=4h  refresh=60000 visibility=[System_Temperature_Chart_Period==1]
            Chart  item=System_Temperature_Chart period=8h  refresh=60000 visibility=[System_Temperature_Chart_Period==2]
            Chart  item=System_Temperature_Chart period=12h refresh=60000 visibility=[System_Temperature_Chart_Period==3]
            Chart  item=System_Temperature_Chart period=D   refresh=60000 visibility=[System_Temperature_Chart_Period==4]
        }
}

This rule is used to update the Number itmes out of the returned String itmes from the command line. Only Works since openhab 2.2.
This is necessary as RRD4j only accepts numbers and the Exec Binding returns strings.

sudo nano /etc/openhab2/rules/SysTemp.rules
 rule "Convert String to Item Type"
  when
    Item System_Temperature_CPU_out changed 
  then
   
    // Get name of Number Item by removing "_out" from the name of the String Item
    val toUpdate = triggeringItem.name.toString.split("_out").get(0)
    
    // post the new value to the Number Item
    postUpdate( toUpdate.toString , triggeringItem.state.toString )

    logInfo("Convert", "Item "+ toUpdate +" state "+ triggeringItem.state) 
 end

If we keep the Karaf console open while we copy paste the files we can see if everything went as it should. We can also see the logInfo massages we added in the rule.

ssh -p 8101 openhab@localhost
habopen
openhab> log:tail
[INFO ] [ipse.smarthome.model.script.CPU Temp] - 48312
[INFO ] [ipse.smarthome.model.script.CPU Temp] - 48.31200000
[INFO ] [marthome.event.ItemStateChangedEvent] - System_Temperature_CPU changed from 49.38800000 to 48.31200000

On our sitemap we can now see the CPU temperature and a chart which shows the progress. We have to wait a while till there is something to plot.

Also adding the GPU Temperature can easily achieved, the command to get the value is as follows and returns an string which we well have to parse by a regex.

pi@raspberrypi:~ $ /opt/vc/bin/vcgencmd measure_temp
temp=48.3'C

Testing if user openhab can also access this value.

sudo -u openhab /opt/vc/bin/vcgencmd measure_temp
VCHI initialization failed

So something does not work, mostly it is the user rights to execute this command. As we saw user pi was able to execute it. A google search points us to which user group we have to add openhab to. We need to add openhab to to the video group, the new rights will be available after a reboot.

sudo usermod -a -G video openhab
sudo reboot

When we test it again we should be able to run it as openhab user and get the same result as without sudo -u openhab.

Now we need to add a Things, a Item, an entry to the Sitemap and one to the Rule to the existing files.

sudo nano /etc/openhab2/things/exec.things
Thing exec:command:gpuTemp [
        // Command to execute
        command="/opt/vc/bin/vcgencmd measure_temp",
        // interval time in seconds
        interval=10,
        // should it run when input channel is changed
        autorun=false,
        // REGEX transformation to make "temp=44.0'C" => 44.0
        transform="REGEX(temp=(.*?)'C)" ]

As the chart and the rrd4j database where defined to use the group System_Temperature_Chart we only need to add a new item for the GPU Temperature to this group. And also an item to store the return value of the command line execution.

sudo nano /etc/openhab2/items/SysTemp.items
// System temperatures
Number System_Temperature_GPU "Temperature GPU [%.1f °C]" <temperature> (System_Temperature_Chart) 

// Output of command line execution 
String System_Temperature_CPU_out { channel="exec:command:cpuTemp:output" }
String System_Temperature_GPU_out { channel="exec:command:gpuTemp:output" }

sudo nano /etc/openhab2/sitemaps/SysTemp.sitemap
 // Name of file and name of sitemap has to be the same
sitemap SysTemp label="System Temperature RPI"
{
        Frame {
            Text item=System_Temperature_CPU
            // This line is new
            Text item=System_Temperature_GPU
        }
        Frame {
            Switch item=System_Temperature_Chart_Period label="Periode" mappings=[0="1h", 1="4h", 2="8h", 3="12h", 4="24h"]
            Chart  item=System_Temperature_Chart period=h   refresh=60000 visibility=[System_Temperature_Chart_Period==0, System_Temperature_Chart_Period=="Uninitialized"]
            Chart  item=System_Temperature_Chart period=4h  refresh=60000 visibility=[System_Temperature_Chart_Period==1]
            Chart  item=System_Temperature_Chart period=8h  refresh=60000 visibility=[System_Temperature_Chart_Period==2]
            Chart  item=System_Temperature_Chart period=12h refresh=60000 visibility=[System_Temperature_Chart_Period==3]
            Chart  item=System_Temperature_Chart period=D   refresh=60000 visibility=[System_Temperature_Chart_Period==4]
        }
}

sudo nano /etc/openhab2/rules/SysTemp.rules
 rule "Convert String to Item Type"
  when
    Item System_Temperature_CPU_out changed or //here new or
    // and this line is new
    Item System_Temperature_GPU_out changed 
  then
   
    // Get name of Number Item by removing "_out" from the name of the String Item
    val toUpdate = triggeringItem.name.toString.split("_out").get(0)
    
    // post the new value to the Number Item
    postUpdate( toUpdate.toString , triggeringItem.state.toString )

    logInfo("Convert", "Item "+ toUpdate +" state "+ triggeringItem.state) 
 end

As always this is just how i got it work. I would apriciate every amendment or improvement by the pro openHAB user. Is there anything that is done bad, or decreases performance? Is there an other better way todo this, besides using nicer charts like explained here.

Update:
The Regex output channel is a string, added the simplest way to get all returned strings to the Number Item with only one rule.
Using InfluxDB persistence and graphing with Grafana a 2 element solution with one Item and one Thing can be achieved. Explained here.

Last but not least i prefer the console and explaining how to install a binding/addon is 4 lines instead of a book with pictures to explain where to find what. :joy:
It is also much faster and can be used as copy paste instructions.

Thank your your comments @rlkoshak.

Update:
Got some DS18B20 OneWire Temperature sensors and did a quick installation. following this guide, sorry its german. But in the official rpi docs it is also described.

I added a new thing and also used the regex to filter the value and divde it by 1000.

Thing exec:command:ds18b20_1 [
        // Command to execute
        command="cat  /sys/bus/w1/devices/28-0516b0780eff/w1_slave",
        // interval time in seconds
        interval=5,
        // should it run when input channel is changed
        autorun=false,
        // explanation for the regex
        // s/           - substitute
        // .+\\s.+t=    - all char, a linebreack (with "\" to escape), all char, t=
        // (.{2})(.{3}) - a number with 5 characters, split in two groups
        // /$1.$2/g     - returned value= first group, a dot, then second group, used to devide by 1000
        transform="REGEX(s/.+\\s.+t=(.{2})(.{3})/$1.$2/g)" ]

the rest is is done es explained above.

3 Likes

Nice tutorial. I don’t really have too much to add. Just a couple of comments/questions:

  • If you apply the REGEX transform to your GPU temperature thing you can eliminate the Rule entirely.
  • You might be able to move the divide by 1000 calculation to a JS transformation, though I’m not certain that would make the code any clearer or run any better.
  • I suspect the majority of users install and manage add-ons using PaperUI or addons.cfg. I like the example of showing how to do it with the Karaf console but suspect that will be a much less frequently use approach.

Thanks for posting!

Its 4 lines unless you also have to explain how to log into the console, what ssh is, what do mean ssh is called putty on Windows, why can’t I access the console from another machine, 
 By the time you deal with the “what’s an ssh” type users you’ve saved nothing.

For sure that is true. I added a link to the Karaf console explanation.

In log I have:

Couldn’t transform response because transformationService of type ‘EXEC’ is unavailable

Install “RegEx Transformation” in PapperUI don’t help me
I get System_Temperature_CPU about 59072

Try to install the EXEC transformation not the only REGEX

@Bagunda Did you reboot?

Yes. I rebooted.
Now I install “Exec Transformation” in PapperUI.
Problem solved!
Thanx @Josar!

Hi I am getting this error, can u suggest. Why?

2018-02-26 17:19:29.627 [ERROR] [hab.binding.exec.handler.ExecHandler] - An exception occurred while formatting the command line with the current time and input values : ‘Format specifier ‘%2$s’’

2018-02-26 17:19:29.845 [ERROR] [hab.binding.exec.handler.ExecHandler] - An exception occurred while executing ‘output’ : ‘Cannot run program “output”: error=2, No such file or directory’

Hope this helps.

1 Like

Good work! Thanks, just the last ] is on a // out-commented line

Hello - i setup my DS18B20 sensor and cpu temp as suggested in this tutorial and both sensors deliver correct values. But only the CPU Temp is written into the rrd4j database. There is not .rrd file generated for the DS18B20 sensor and the log (debug level) does not show any erros. Detailed description her
Any help would be very appreciated. Thanks

It’s always best to start a new thread rather than reopening a test great old thread.

Rrd4j does not create a new file per item. What makes you think both items are not persisted?

Thanks for the reply - i started a new thread but I thought it could help to ask here, because i setup everything based on this tutorial - which works fine for the CPU Temp (incl. rrd4dj) but does not work with the DS18B20. I am able to get the DS18B20 Temp value but not able to get it into rr4dj. Let see if my other thread helps me to get this understood.