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 
to
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,
Markus

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.

2 Likes

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"
when
//  System started or
    Time cron "0 */2 * ? * *"
then
    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) ])
end

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"
when
    Item getTempA_Raw changed
then
    getTempA_Num.postUpdate(Float::parseFloat(String::format("%s",getTempA_Raw.state).replace(' ','')))
end

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

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

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

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

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

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

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

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

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

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,
Markus