Exec binding for Viessmann Vitrotronic heating information

  • Platform information:
    • Hardware: Raspberry Pi 3b+ Processor: BCM2835; Linux 4.19.50-v7+ armv7l
    • OS: Raspbian GNU/Linux 10 (buster); Firmware #896
    • Java Runtime Environment: (build 11.0.8+10-post-Raspbian-1deb10u1); Version 11.0.8" 2020-07-14
    • openHAB version: 2.5.9 Release Build

Dear community,

Happy New Year! Diving deeper into openHab, I would like to gather the information of my Viessmann Vitrotronic 200 (Modell V200KO1B) using a Raspberry Pi 3B+ and openv. I have been using openv with Volkszaehler for 1,5 years to collect my heating information in order to push it into Grafana which always worked very reliable. Getting more and more into openHab I want to get rid of my hybrid solution with both, running Volkszaehler and openHAB.

Right now I managed to collect my heating information with the Exec binding using the following command:

vclient -h 127.0.01 -p 3002 -m -c getTempA,getTempWWIst,getTempKesselIst,getTempVLSoll,getLaufzeitBrenner,getStartsBrenner,getTempRaumNorSoll,getTempRaumRedSoll,getKennlinieNeigung,getKennlinieNiveau

This command gives me ten information every 120 seconds about my heating status with the following output, for e.g.:

2021-01-05 20:25:25.852 [vent.ItemStateChangedEvent] - Brenner_RCkgabewert changed from
getTempA.value 2.700000
getTempWWIst.value 55.500000
getTempKesselIst.value 39.900002
getTempVLSoll.value 41.299999
getLaufzeitBrenner.value 11884.116211
getStartsBrenner.value 127085.000000
getTempRaumNorSoll.value 20.000000
getTempRaumRedSoll.value 16.000000
getKennlinieNeigung.value 0.700000
getKennlinieNiveau.value 7.000000 
getTempA.value 2.600000
getTempWWIst.value 55.500000
getTempKesselIst.value 44.299999
getTempVLSoll.value 41.299999
getLaufzeitBrenner.value 11884.146484
getStartsBrenner.value 127085.000000
getTempRaumNorSoll.value 20.000000
getTempRaumRedSoll.value 16.000000
getKennlinieNeigung.value 0.700000
getKennlinieNiveau.value 7.000000

which is correct and fine in general. Now comes my issue and request:
I want to have each of the ten values (heance each line) extracted into one separate item.

Unfortunately I cannot simply use ten Exec things for each value this because the Viessmann adapter and openv need some kind of “cooldown” after each request. Sometimes the adapter / heater is not ready to proceed with another request a few sends ago, therefore I have to fire my complete request at once and separate the complete output afterwards. I used the identical command in Volkszaehler, there you have for each item an “Identifier” (for .e.g getTempA.value --> Value) to assign a value to the correct topic.

How can this be done in openHAB? Maybe some kind of Regex or Python script?

Everything in these regards is totally new for me, therefore I hope someone can help me or giving me a hint for a simple solution.

PS: I know there is a Vitotronic Binding, but I was not able to get it working. The Exec binding is doing all I need, I just want to have a single item for each value.

Thanks for your support and stay healthy,

For completion, here is my whitelist record and exec binding record:

vclient -h 127.0.01 -p 3002 -m -c getTempA,getTempWWIst,getTempKesselIst,getTempVLSoll,getLaufzeitBrenner,getStartsBrenner,getTempRaumNorSoll,getTempRaumRedSoll,getKennlinieNeigung,getKennlinieNiveau

Write e.g. a python script, that calls the vclient.
Then run a loop to parse the returned lines and split /parse the rows.
After that call the REST API to set items to the values you received.
All that can be done from within a single executable ( e.g. python, perl, C, … )

1 Like

If you want to write a python script you can use HABApp for it.
That way the interaction with openhab is very easy.


Dear colleagues,

Due to time issues I was not able yet to solve my request by my own with a script for the exec binding. For now, my solution looks like the following, maybe this will help other beginners, too.

For each value, I created a single thing for the Exec binding which I initially wanted to avoid:

//Exec things
Thing exec:command:getTempA                 "getTempA"                  [command="vclient -h 127.0.01 -p 3002 -m -c getTempA | cut -d\" \" -f2", interval=0, timeout=600, autorun=false]
Thing exec:command:getTempWWIst             "getTempWWIst"              [command="vclient -h 127.0.01 -p 3002 -m -c getTempWWIst | cut -d\" \" -f2", interval=0, timeout=600, autorun=false]
Thing exec:command:getTempKesselIst         "getTempKesselIst"          [command="vclient -h 127.0.01 -p 3002 -m -c getTempKesselIst | cut -d\" \" -f2", interval=0, timeout=600, autorun=false]
Thing exec:command:getTempVLSoll            "getTempVLSoll"             [command="vclient -h 127.0.01 -p 3002 -m -c getTempVLSoll | cut -d\" \" -f2", interval=0, timeout=600, autorun=false]
Thing exec:command:getLaufzeitBrenner       "getLaufzeitBrenner"        [command="vclient -h 127.0.01 -p 3002 -m -c getLaufzeitBrenner | cut -d\" \" -f2", interval=0, timeout=600, autorun=false]
Thing exec:command:getStartsBrenner         "getStartsBrenner"          [command="vclient -h 127.0.01 -p 3002 -m -c getStartsBrenner | cut -d\" \" -f2", interval=0, timeout=600, autorun=false]
Thing exec:command:getTempRaumNorSoll       "getTempRaumNorSoll"        [command="vclient -h 127.0.01 -p 3002 -m -c getTempRaumNorSoll | cut -d\" \" -f2", interval=0, timeout=600, autorun=false]
Thing exec:command:getTempRaumRedSoll       "getTempRaumRedSoll"        [command="vclient -h 127.0.01 -p 3002 -m -c getTempRaumRedSoll | cut -d\" \" -f2", interval=0, timeout=600, autorun=false]
Thing exec:command:getKennlinieNeigung      "getKennlinieNeigung"       [command="vclient -h 127.0.01 -p 3002 -m -c getKennlinieNeigung | cut -d\" \" -f2", interval=0, timeout=600, autorun=false]
Thing exec:command:getKennlinieNiveau       "getKennlinieNiveau"        [command="vclient -h 127.0.01 -p 3002 -m -c getKennlinieNiveau | cut -d\" \" -f2", interval=0, timeout=6e00, autorun=false]

My whitelist of course contains all of the mentioned shell commands:

vclient -h 127.0.01 -p 3002 -m -c getTempA | cut -d" " -f2
vclient -h 127.0.01 -p 3002 -m -c getTempWWIst | cut -d" " -f2
vclient -h 127.0.01 -p 3002 -m -c getTempKesselIst | cut -d" " -f2
vclient -h 127.0.01 -p 3002 -m -c getTempVLSoll | cut -d" " -f2
vclient -h 127.0.01 -p 3002 -m -c getLaufzeitBrenner | cut -d" " -f2
vclient -h 127.0.01 -p 3002 -m -c getStartsBrenner | cut -d" " -f2
vclient -h 127.0.01 -p 3002 -m -c getTempRaumNorSoll | cut -d" " -f2
vclient -h 127.0.01 -p 3002 -m -c getTempRaumRedSoll | cut -d" " -f2
vclient -h 127.0.01 -p 3002 -m -c getKennlinieNeigung | cut -d" " -f2
vclient -h 127.0.01 -p 3002 -m -c getKennlinieNiveau | cut -d" " -f2

Now I added for each value an item for the run-channel, output-channel and lastexecution channel as well as a proxy Number item in order to convert the string ouput to a number for influxdb:

//Exec items for run
Switch getTempA_Run                 "Run"       {channel="exec:command:getTempA:run", autoupdate="false"}                   
Switch getTempWWIst_Run             "Run"       {channel="exec:command:getTempWWIst:run", autoupdate="false"}
Switch getTempKesselIst_Run         "Run"       {channel="exec:command:getTempKesselIst:run", autoupdate="false"}
Switch getTempVLSoll_Run            "Run"       {channel="exec:command:getTempVLSoll:run", autoupdate="false"}
Switch getLaufzeitBrenner_Run       "Run"       {channel="exec:command:getLaufzeitBrenner:run", autoupdate="false"}
Switch getStartsBrenner_Run         "Run"       {channel="exec:command:getStartsBrenner:run", autoupdate="false"}
Switch getTempRaumNorSoll_Run       "Run"       {channel="exec:command:getTempRaumNorSoll:run", autoupdate="false"}
Switch getTempRaumRedSoll_Run       "Run"       {channel="exec:command:getTempRaumRedSoll:run", autoupdate="false"}
Switch getKennlinieNeigung_Run      "Run"       {channel="exec:command:getKennlinieNeigung:run", autoupdate="false"}
Switch getKennlinieNiveau_Run       "Run"       {channel="exec:command:getKennlinieNiveau:run", autoupdate="false"}

//Exec items for raw information
String getTempA_Raw                             {channel="exec:command:getTempA:output"}
String getTempWWIst_Raw                         {channel="exec:command:getTempWWIst:output"}
String getTempKesselIst_Raw                     {channel="exec:command:getTempKesselIst:output"}
String getTempVLSoll_Raw                        {channel="exec:command:getTempVLSoll:output"}
String getLaufzeitBrenner_Raw                   {channel="exec:command:getLaufzeitBrenner:output"}
String getStartsBrenner_Raw                     {channel="exec:command:getStartsBrenner:output"}
String getTempRaumNorSoll_Raw                   {channel="exec:command:getTempRaumNorSoll:output"}
String getTempRaumRedSoll_Raw                   {channel="exec:command:getTempRaumRedSoll:output"}
String getKennlinieNeigung_Raw                  {channel="exec:command:getKennlinieNeigung:output"}
String getKennlinieNiveau_Raw                   {channel="exec:command:getKennlinieNiveau:output"}

//Items for converted number information
Number getTempA_Num                             "Außentemperatur [%.1f °C]"
Number getTempWWIst_Num                         "Warmwasser [%.1f °C]"
Number getTempKesselIst_Num                     "Kesseltemperatur [%.1f °C]"
Number getTempVLSoll_Num                        "Vorlauftemperatur [%.1f °C]"
Number getLaufzeitBrenner_Num                   "Brenner Laufzeit"
Number getStartsBrenner_Num                     "Brenner Starts"
Number getTempRaumNorSoll_Num                   "Raumtemperatur Normal [%.1f °C]"
Number getTempRaumRedSoll_Num                   "Raumtemperatur Reduziert [%.1f °C]"
Number getKennlinieNeigung_Num                  "Heizkennlinie Neigung"
Number getKennlinieNiveau_Num                   "Heizkennlinie Niveau"

//Exec items for last ececution information
DateTime getTempA_Last                      {channel="exec:command:getTempA:lastexecution"}
DateTime getTempWWIst_Last                  {channel="exec:command:getTempWWIst:lastexecution"}
DateTime getTempKesselIst_Last              {channel="exec:command:getTempKesselIst:lastexecution"}
DateTime getTempVLSoll_Last                 {channel="exec:command:getTempVLSoll:lastexecution"}
DateTime getLaufzeitBrenner_Last            {channel="exec:command:getLaufzeitBrenner:lastexecution"}
DateTime getStartsBrenner_Last              {channel="exec:command:getStartsBrenner:lastexecution"}
DateTime getTempRaumNorSoll_Last            {channel="exec:command:getTempRaumNorSoll:lastexecution"}
DateTime getTempRaumRedSoll_Last            {channel="exec:command:getTempRaumRedSoll:lastexecution"}
DateTime getKennlinieNeigung_Last           {channel="exec:command:getKennlinieNeigung:lastexecution"}
DateTime getKennlinieNiveau_Last            {channel="exec:command:getKennlinieNiveau:lastexecution"}

With the following rule, every ten seconds a value is requested with the exec channel. The rule runs every two minutes. So the mentioned “cooldown” the adapter needs is 10 seconds, which, at least for now, seems to be enough to respons properly:

rule "Viessmann openv collection"
//  System started or
    Time cron "0 */2 * ? * *"
    createTimer(now.plusSeconds(0), [ getTempA_Run.sendCommand(ON) ])
    createTimer(now.plusSeconds(10), [ getTempWWIst_Run.sendCommand(ON) ])
    createTimer(now.plusSeconds(20), [ getTempKesselIst_Run.sendCommand(ON) ])
    createTimer(now.plusSeconds(30), [ getTempVLSoll_Run.sendCommand(ON) ])
    createTimer(now.plusSeconds(40), [ getLaufzeitBrenner_Run.sendCommand(ON) ])
    createTimer(now.plusSeconds(50), [ getStartsBrenner_Run.sendCommand(ON) ])
    createTimer(now.plusSeconds(60), [ getTempRaumNorSoll_Run.sendCommand(ON) ])
    createTimer(now.plusSeconds(70), [ getTempRaumRedSoll_Run.sendCommand(ON) ])
    createTimer(now.plusSeconds(80), [ getKennlinieNeigung_Run.sendCommand(ON) ])
    createTimer(now.plusSeconds(90), [ getKennlinieNiveau_Run.sendCommand(ON) ])

The next rules are the for the string to number conversion for each value in order to be ready for the influxdb.persistence:

rule "Convert getTempA"
    Item getTempA_Raw changed
    getTempA_Num.postUpdate(Float::parseFloat(String::format("%s",getTempA_Raw.state).replace(' ','')))

rule "Convert getTempWWIst"
    Item getTempWWIst_Raw changed
    getTempWWIst_Num.postUpdate(Float::parseFloat(String::format("%s",getTempWWIst_Raw.state).replace(' ','')))

rule "Convert getTempKesselIst"
    Item getTempKesselIst_Raw changed
    getTempKesselIst_Num.postUpdate(Float::parseFloat(String::format("%s",getTempKesselIst_Raw.state).replace(' ','')))

rule "Convert getTempVLSoll"
    Item getTempVLSoll_Raw changed
    getTempVLSoll_Num.postUpdate(Float::parseFloat(String::format("%s",getTempVLSoll_Raw.state).replace(' ','')))

rule "Convert getLaufzeitBrenner"
    Item getLaufzeitBrenner_Raw changed
    getLaufzeitBrenner_Num.postUpdate(Float::parseFloat(String::format("%s",getLaufzeitBrenner_Raw.state).replace(' ','')))

rule "Convert getStartsBrenner"
    Item getStartsBrenner_Raw changed
    getStartsBrenner_Num.postUpdate(Float::parseFloat(String::format("%s",getStartsBrenner_Raw.state).replace(' ','')))

rule "Convert getTempRaumNorSoll"
    Item getTempRaumNorSoll_Raw changed
    getTempRaumNorSoll_Num.postUpdate(Float::parseFloat(String::format("%s",getTempRaumNorSoll_Raw.state).replace(' ','')))

rule "Convert getTempRaumRedSoll"
    Item getTempRaumRedSoll_Raw changed
    getTempRaumRedSoll_Num.postUpdate(Float::parseFloat(String::format("%s",getTempRaumRedSoll_Raw.state).replace(' ','')))

rule "Convert getKennlinieNeigung"
    Item getKennlinieNeigung_Raw changed
    getKennlinieNeigung_Num.postUpdate(Float::parseFloat(String::format("%s",getKennlinieNeigung_Raw.state).replace(' ','')))

rule "Convert getKennlinieNiveau"
    Item getKennlinieNiveau_Raw changed
    getKennlinieNiveau_Num.postUpdate(Float::parseFloat(String::format("%s",getKennlinieNiveau_Raw.state).replace(' ','')))

With the influx persistence, every value is sucessfully passed for a nice visulatation of my Viessmann heater:

As soon I got more time I would like to solve this task with a python script, but I think I heavily rely in your support because these are my first steps at all in this regard.

Anyway, thank you for pushing me into the right direction!

Best regards,