Get data from volkszaehler into OpenHab2

Hello OpenHab Community,
I’m new on this form and I hope I put my request into the right category.
I have a volkszaehler server running on a Raspberry Pi which is logging power datas. Few days ago I set up a brand new OpenHab Server and was testing some given Bindings. I do like this idea with OpenHab and I want to use this for more projects. I was wondering if it’s possible to combine OpenHab with Volkszaehler and if there is any binding for this yet.
It would be nice if someone could help me on this.
Thanks

1 Like

Hi there,

never heared of this project before, but since it is open source and the documentation is well, it would be possible.
Unfortunately I’m not a java-guy, so I can’t give you any information about a binding. However, if you just want to show the graphs in your WebUI, there is a way.
Take a look at the API

You have to send this via HTTP Get (just to test everything out, a webbrowser will do the job)

http://<server:port><einstiegspfad>/middleware.php/<kontext>[/<uuid>].<format>[?<parameter>]

As format you have to use png, gif or jpg. I found this information here. Please note, that jpGraph has to be installed on your volkszaehler.
If you will get a chart as image, you can just define a picture in your openhab UI and put the working URL in it.

Of course you could use txt or json as a output, and build a openhab rule to put those values in openhab items, but if you just want the chart, go with my provided solution.

I hope I gave you some point of starting!

1 Like

I also use volkszaehler to monitor my power consumption and report it to openhab. However, my setup is a bit more low-level :wink:

  • on my Pi, cron triggers a script every hour to read the current value of power consumption via the IR reader
  • the current value is inserted into a mysql database

In the database, I have several views to read the power consumption for the last 24h per hour, for the last 30 days per day, and for the last 12 months per month.

  • then another script takes the values of these 3 views and pushes them to OH via its REST API

If you are interested in some of the script snippets, I could post them here.

Thank you! I will try this very soon.

Just in case someone is interested, here is my script (works for OH1 but it should also work for OH2):

#!/bin/bash

# set serial device
INPUT_DEV="/dev/ttyUSB0"

# set $INPUT_DEV to 9600 8N1
stty -F $INPUT_DEV 1:0:8bd:0:3:1c:7f:15:4:5:1:0:11:13:1a:0:12:f:17:16:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0

# the meter sends packages peridically.
# for my meter, a package has length 384 and the longest expression to match is 24.
# so to be sure to get each substring of interest, with some tolerance, 420 should be enough.
# -p: hex format, -u: upper case, -l: length, -c: block size (avoid spaces)
DUMP=`cat $INPUT_DEV 2>/dev/null | xxd -p -u -l 420 -c 420`

# default output format is json
FORMAT="json"
if [ "$1" == "sql" ]
then
	# usage example: ./read2.sh sql | ssh paphko@nas mysql -upi -ppassword haus
	# useful e.g. for adding to crontab: 0 * * * * /opt/volkszaehler/read2.sh sql | sudo -u pi ssh paphko@nas mysql -upi -ppassword haus
	FORMAT="sql"
fi

# match some expression and extract numeric value with unit and scaler
function extractNumericFromDump {
	# first group: unit
	# second group: scaler
	# last (large) group: actual value
	REGEX=$1
	LENGTH=$2
	KIND=$3
	MATCH=`echo $DUMP | sed -nr $REGEX`
	UNIT=${MATCH:0:2}
	SCALER=${MATCH:2:2}
	SCALER=$((((0x$SCALER + 128) % 256) - 128)) # convert scaler from HEX to sint8 (-128..127)
	VALUE=${MATCH:4:$LENGTH}
	VALUE=$((0x$VALUE)) # convert value from hex to decimal

	# format value
	if [ $SCALER -gt 0 ]
	then
		for i in `seq 1 $SCALER`
		do
			VALUE=$(($VALUE * 10))
		done
	elif [ $SCALER -lt 0 ]
	then
		for i in `seq 1 $(($SCALER * -1))`
		do
			VALUE=$(($VALUE / 10))
		done
	fi

	if [ $FORMAT == 'sql' ]
	then
		echo $VALUE
	else
		echo '"'$KIND'":{"value":'$VALUE',"unit":"'$UNIT'"}'
	fi
}

# there are two total variable values, no idea what difference is... both have the same value as shown on the display
TOTAL1=$(extractNumericFromDump 's/.*77070100010800FF65.{8}0162(..)52(..)59(.{16})01.*/\1\2\3/p' 16 total1)
TOTAL2=$(extractNumericFromDump 's/.*77070100010801FF010162(..)52(..)59(.{16})01.*/\1\2\3/p' 16 total2)
# and there are multiple individual values, one could be the current power consumption (but which one?)
VALUE1=$(extractNumericFromDump 's/.*77070100100700FF010162(..)52(..)55(.{8})01.*/\1\2\3/p' 8 val1)
VALUE2=$(extractNumericFromDump 's/.*77070100240700FF010162(..)52(..)55(.{8})01.*/\1\2\3/p' 8 val2)
VALUE3=$(extractNumericFromDump 's/.*77070100380700FF010162(..)52(..)55(.{8})01.*/\1\2\3/p' 8 val3)
VALUE4=$(extractNumericFromDump 's/.*770701004C0700FF010162(..)52(..)55(.{8})01.*/\1\2\3/p' 8 val4)

if [ $FORMAT == 'sql' ]
then
	# output as sql insert-statement
	echo "INSERT INTO strom (DATE,TOTAL1,TOTAL2,VALUE1,VALUE2,VALUE3,VALUE4) VALUES (NOW(),$TOTAL1,$TOTAL2,$VALUE1,$VALUE2,$VALUE3,$VALUE4)"
else
	# output values in json format
	NOW=`date +%Y-%m-%d_%H:%M:%S`
	echo '{"datetime":"'$NOW'",'$TOTAL1','$TOTAL2','$VALUE1','$VALUE2','$VALUE3','$VALUE4'}'
fi

# additional parameter for openhab update
if [ "$2" == "openhab" ]
then
	# configuration for openhab updates
	OPENHAB_SERVER=http://openhab:8080/rest/items

	# get value of last hour
	LAST_HOUR=$(mysql -upi -ppassword haus -ss -e "select diff from strom_verbrauch limit 1")
	# update values to openhab
	echo curl --header "Content-Type: text/plain" --request PUT --data "$LAST_HOUR" $OPENHAB_SERVER/Strom_Stunde/state
fi

It is called via cron once per hour:

0 * * * * /opt/volkszaehler/read2.sh sql openhab | sudo -u pi ssh paphko@nas mysql -upi -ppassword haus
2 Likes

Hi, I also have Volkszaehler installed and transmit my data to OH as well. The Volkszaehler middleware has a build in push service which can be consumed using Node-Red. So, Node-Red connects to the push port from Volkszaeher and then pushes the value to my MQTT server. Openhab simply subscribes to the MQTT items. If you’re interested I can describe this in more detail.

Regards,
Michel

Please show more of your power reading.

Thank you for all of your answers and ideas! Based on them, i wrote a little php script, to transfer data from Volkszaehler into Openhab:

<?php
    $urlBaseVZ='http://###Volkszaehler URL###/middleware.php/data/';
    $uuIds=array('Volkszaehler UUID','xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
    $items=array('OpenHab item Name','Test123');
    $urlEnd='.json?from=30%20seconds%20ago';
    $urlBaseOH='http://###OpenHab URL###:8080/rest/items/';

function curl_file_get_contents($URL) {                            //get data from vz
    $c = curl_init();
    curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($c, CURLOPT_URL, $URL);
    $contents = curl_exec($c);
    curl_close($c);

    if ($contents) return $contents;
    else return FALSE;
    }

function getTimestamp() {                                        //get timestamp
    $seconds = microtime(true);
    return round( ($seconds * 1000) );
    }
function getdata($uuid) {                                        //convert data
    global $urlBaseVZ, $urlEnd;
    $minTimestamp = getTimestamp();
    $maxTimestamp = $minTimestamp;
    $url=$urlBaseVZ . $uuid . $urlEnd;
    $content = curl_file_get_contents($url);
    $content=json_decode($content);
    if (!empty($content->data->tuples)) {
        $lastTuple = end($content->data->tuples);
        $minTimestamp = min($minTimestamp,$lastTuple[0]);
        $maxTimestamp = max($maxTimestamp,$lastTuple[0]);
        $lastValue = $lastTuple[1];
    } 
    else {
        $lastValue=0;
    } // if
    return($lastValue);
}
function sendCommand($item, $data) {                            //send data to openhab
      global $urlBaseOH
      $url = $urlBaseOH . $item;
    
      $options = array(
        'http' => array(
            'header'  => "Content-type: text/plain\r\n",
            'method'  => 'POST',
            'content' => $data  //http_build_query($data),
        ),
      );

      $context  = stream_context_create($options);
      $result = file_get_contents($url, false, $context);

      return $result;
}

if(count($uuIds)==count($items)){
    foreach(range(0, count($uuIds)) as $number) {
        $item = $items[$number-1];
        $data = getdata($uuIds[$number-1]);
        sendCommand("$item","$data");
    }
}
else {
    echo "lists dont have the same length | list1: ".count($uuIds)." | list2: ".count($items);
}
?>

Hi Michel, I’m interested in this topic to connect Volkszaehler and OH. Can you describe this in more detail?

Regards,
Tobias

1 Like

Hello together,

I was also interested in the volkszähler project but wanted a lightweight integration without the whole volkszähler middleware stuff. The setup is based on Raspberry PI with vzlogger installed and the embedded httpd that serves the values I’m looking for (current energy usage, total usage and so forth).

The interesting part of the volkszähler config (vzlogger.conf) is: (followed this documentation and used this config editor)

{
  "retry": 0,
  "daemon": true,
  "verbosity": 10,
  "log": "/var/log/vzlogger.log",
  "push": [],
  "local": {
    "enabled": true,
    "port": 8080,
    "index": true,
    "timeout": 30,
    "buffer": 600
  },
  "meters": [
    {
      "enabled": true,
      "allowskip": false,
      "interval": 5,
      "aggtime": -1,
      "aggfixedinterval": false,
      "channels": [
        {
          "api": "null",
          "uuid": "fde8f1d0-c5d0-11e0-856e-f9e4360ced10",
          "identifier": "1-0:1.8.0"
        },
        {
          "api": "null",
          "uuid": "fde8f1d0-c5d0-11e0-856e-f9e4360ced11",
          "identifier": "1-0:16.7.0"
        },
        {
          "api": "null",
          "uuid": "fde8f1d0-c5d0-11e0-856e-f9e4360ced12",
          "identifier": "1-0:2.8.0"
        }
      ],
      "protocol": "sml",
      "device": "/dev/ttyUSB0",
      "pullseq": "",
      "baudrate": 9600,
      "parity": "8n1",
      "use_local_time": true
    }
  ]
}

After succesfully setup the volkszähler I’ve used the http bindung (services/http.cfg)

# Smartmeter / RPI
smartmeter.url=http://alex-smartmeter:8080/
smartmeter.updateInterval=30000

… and following Items:

Number Energy_TotalUsed "Total Usage[%.1f kWh]" { http="<[smartmeter:3000:JS(smartmeter_totalUsedEnergy.js)]" }
Number Energy_CurrentUsage "Current Usage [%.1f W]" { http="<[smartmeter:3000:JSONPATH($.data[?(@.uuid == 'fde8f1d0-c5d0-11e0-856e-f9e4360ced11')].tuples[-1:][1])]" }

The JSONPATH transformation takes the data block with the given uuid (this is the reference to a unit in volkszähler) and takes the last known reading.
Because I wanted to have kWh values instead of Wh values for the total usage I had to write a custom JS transformation script: (transform/smartmeter_totalUsedEnergy.js)

(function(i) {
        var value = JSON.parse(input);

        for(i=0; i<value.data.length; i++) {
                // fde8f1d0-c5d0-11e0-856e-f9e4360ced10 is total energy used
                if(value.data[i].uuid == 'fde8f1d0-c5d0-11e0-856e-f9e4360ced10') {
                        // tuples holding series of values where last=latest. We divide it by 1000 to convert Wh to kWh
                        return value.data[i].tuples[(value.data[i].tuples.length - 1)][1]/1000;
                }
        }
        return "";
})(input)