Intergas Incomfort Lan2RF rules

Hi,
this is my first time to show a ‘more than a few lines’ rule at the formum with special thanks to @Dim, @job, and @rlkoshak . It is all working, no errors, but if anyone could comment on smart improvements I would really appriciate that! I hope other Lan2RF users may bennefit. (I think it’s mostly Dutch residents). Because of me being Dutch: WK is Woonkamer = Livingroom. I didn’t translate my items.

My situation: I have a Intergas boiler with Opentherm, Honeywell Round (wired) and a Intergas Lan2RF gateway which is connected to the boiler through RadioFrequency and to my network through UTP, ence the name Lan2RF.
I can set the setpoint with a http-Get request, and receive current data about i.e. setpoint (not updated immediately) and currend temperature. These data are stored in a json-file. The temperatures are calculated from two numbers, the lsb (least significant bit) and the msb (most significant bit). To get from a string (the jason file) to a number, I used:

  • JSONPATH
  • store the value in an item
  • calculte new value from item lsb and item msb

Because it takes up to a minute to set a new setpoint from openhab in the thermostat, I have to postpone accepting new setpoints from the thermostat untill setting one from openhab has finnished, and vise versa. I have come up with a blocking switch for that.

I used a mapping-file to get human readable status-info from the boiler.
To-Do:

  • Also get error messages and only show them when there is an error
  • Get info on water set and return temperatures and show them on a ‘admin’'page
  • Somehow get the power used (percentage) since that is not in the API-documentation but it should be possible to get this info since the android app does this too

Here are my files:

services/http.cfg

format=false  # very important!
CV_data.url=http://192.168.0.6/data.json?heater=%3C0611f022270%3E
CV_data.updateInterval=60000

transform/BoilerStatus.map

85=sensortest
170=service
204=tapwater
51=tapwater_int
240=boiler_int
15=boiler_ext
153=postrun_boiler
0=opentherm
102=zone-heating #this one is not in the API-manual
255=buffer
24=frost
231=postrun_ch
126=standby
37=central_heating_rf

items/default.items

Group LivingRoomHeating "LivingRoomHeating"
Group Heating "Heating"
Number T_WK_is "Temp Livingroom [%.1f °C]" <temperature> (Heating)
Number T_WK_sp "Setpoint Temp Livingroom [%.1f °C]" <temperature> (Heating)
Number Room_temp_set_1_lsb
Number Room_temp_set_1_msb
Switch T_WK_SP_block1 { expire="5m,command=OFF" }//block setting new setpoint from Thermostat-data, expire incase of wrong initialisation
Switch T_WK_SP_block2 { expire="5m,command=OFF" }//block setting new setpoint from openHAB, expire incase of wrong initialisation
Number WK_room_temp_1_lsb "Thermostat Tis lsb" { http="<[CV_data:10000:JSONPATH($.room_temp_1_lsb)]" }
Number WK_room_temp_1_msb "Thermostat Tis msb" { http="<[CV_data:10000:JSONPATH($.room_temp_1_msb)]" }
Number WK_room_temp_set_1_lsb "Thermostat setpoint lsb" { http="<[CV_data:10000:JSONPATH($.room_temp_set_1_lsb)]" }
Number WK_room_temp_set_1_msb "Thermostat setpoint msb" { http="<[CV_data:10000:JSONPATH($.room_temp_set_1_msb)]" }
Number WK_displ_code "CV-status [MAP(BoilerStatus.map):%s]" { http="<[CV_data:10000:JSONPATH($.displ_code)]" }

sitemap/default.sitemap

 Frame item=LivingRoomHeating {
			Text item=T_WK_is
                        Setpoint item=T_WK_sp minValue=10 maxValue=24 step=0.5
                        //Switch item=T_WK_SP_block1 for debug
                        //Switch item=T_WK_SP_block2 for debug
                        Text item=WK_displ_code
		}

rules/default.rules

val String debug = "ON" //set ON for debug messages
val String Lan2RF_ip = "192.168.0.6" //check the right IP in browser!
rule "Update Thermostat SP from openhab"
when
    Item T_WK_sp changed
then
    // construct url for setting thermostat//
    var Number celsius
    var String data
    if(T_WK_SP_block2.state == ON){
        if(debug =="ON"){
            logDebug("Update Thermostat SP", "waiting for setpoint from Thermostat to finnish") //Wait untill new setpoint arrived
        }
    } else {
        val twish = T_WK_sp.state as Number
        val setp = (twish - 5 ) * 10
        T_WK_SP_block1.sendCommand(ON) //Make sure no new setpoints are received through http-binding in other rule
        sendHttpGetRequest("http://"+Lan2RF_ip+"/data.json?heater=0&setpoint="+setp+"&thermostat=0%C3%97") //set the new setpoint
        if(debug =="ON"){
            logDebug("Update Thermostat SP", "step 1 setpoint sent: "+setp+"°C, this means: "+twish+"°C" )
        }
        do {
            Thread::sleep(30000) //wait 30 seconds before resending setpoint and get new data
            if(debug =="ON"){
                logDebug("Update Thermostat SP", "step 2, waited 30 sec, now resend setpoint and get current setpoint from thermostat" )
            }
            data = sendHttpGetRequest("http://192.168.0.6/data.json?heater=0&setpoint="+setp+"&thermostat=0%C3%97") //resend untill succes
            WK_room_temp_set_1_lsb.sendCommand(transform("JSONPATH","$.room_temp_set_1_lsb", data)) //put in item to get string to number
            WK_room_temp_set_1_msb.sendCommand(transform("JSONPATH","$.room_temp_set_1_msb", data))
            if(debug =="ON"){
                logDebug("Update Thermostat SP", "step 3 data received, checking setpoint in thermostat." )
            }
            val lsb = WK_room_temp_set_1_lsb.state as Number
            val msb = WK_room_temp_set_1_msb.state as Number
            celsius = (lsb + msb *256) / 100
            if(debug =="ON"){
                logDebug("Update Thermostat SP", "step 4 setpoint in thermostat: "+celsius+"°C, should be: "+twish+"°C" )
            }
        } while (twish != celsius)
        T_WK_SP_block1.sendCommand(OFF)
        if(debug =="ON"){
                logDebug("Update Thermostat SP", "step 5: Finnished!")
        }
    }
end


rule "Update Livingroom Thermostat setpoint from Thermostat"
when
    Item WK_room_temp_set_1_lsb changed or
    Item WK_room_temp_set_1_msb changed
then
    if(T_WK_SP_block1.state == ON){
        
        if(debug =="ON"){
                logDebug("Update Thermostat SP from Thermostat", "waiting for setpoint from OpenHAB to finnish") //Wait untill new setpoint arrived
        }
    } else {
        T_WK_SP_block2.sendCommand(ON)
        val lsb = WK_room_temp_set_1_lsb.state as Number
        val msb = WK_room_temp_set_1_msb.state as Number
        val celsius = (lsb + msb * 256) / 100
        T_WK_sp.postUpdate(celsius)
        if(debug =="ON"){
                logDebug("Update Thermostat SP from Thermostat", "temperature setpoint recieved: "+celsius+"°C")
        }
        Thread::sleep(10000) //wait 30 seconds before allowing input from OH-setpoint
        T_WK_SP_block2.sendCommand(OFF)
    }
end
//
rule "Update Livingroom Thermostat real temperature"
when
    Item WK_room_temp_1_lsb changed or
    Item WK_room_temp_1_msb changed
then
    val lsb = WK_room_temp_1_lsb.state as Number
    val msb = WK_room_temp_1_msb.state as Number
    val celsius = (lsb + msb * 256) / 100
    T_WK_is.postUpdate(celsius)
    if(debug =="ON"){
                logDebug("Update Thermostat", "temperature recieved and calculated")
    }
end

I hope I don’t mis anything, or I will edit later. It’s hard to see in this little window.

1 Like

I’ve just done a quick scan of the code and have the following comments:

  • Your debug val is redundant. The best practice way to handle this is to set the logging level of the logger to Debug if you want to have these statements appear in the logs or set it to Info if you do not. The if(debug == "ON") is done for you already inside of logDebug so your doing it in the Rules yourself is redundant. And you should use a val Boolean debug = true and if(debug) instead. Not only is it shorter and clearer, it is way more efficient.

  • Thread::sleep for 30 seconds inside a loop that could potentially run without end is a really really bad idea. If you have a Thread::sleep` for more than 500 you must look for a way to use Timers or find another approach. With this approach you are very likely to run out of rule execution threads and your OH rules will stop executing. Would it make sense to move the polling in this loop to just use the HTTP binding with a caching config? This would let the HTTP binding poll the URL every 30 seconds all the time and you can populate your two WK Items as necessary and have a separate Rule that triggers on changes to those two Items to calculate celsius and compare the result to twish and issue the appropriate commands.

  • You shold be using ReentrantLocks, not an Item to put a lock around parts of your code. Even a global variable would work better in this case because there is a little bit of lag between an Item receiving a command and the Item reaching that state in the Item registry which can result in situations where the Item is locked but the Rule doesn’t know it yet and your lock will fail and you will have two or more instances running at the same time.

Those are the big things I can see with a quick scan.

Thanx for the feed baqck Rich.
The redundant debug is easy enough corrected.
Now i am trying with timers, but I get stuck in the syntax. I haven’t learned to work with lambda’s yet and most examples are with a lot of interaction with other variables.
When I do this:

var Timer timer = null
timer = createTimer(now.plusMinutes(5)) [|
                logDebug("Update Thermostat SP", "step 2, waited 30 sec, now resend setpoint and get current setpoint from thermostat" )
                data = sendHttpGetRequest("http://192.168.0.6/data.json?heater=0&setpoint="+setp+"&thermostat=0%C3%97") //resend untill succes
                WK_room_temp_set_1_lsb.sendCommand(transform("JSONPATH","$.room_temp_set_1_lsb", data)) //put in item to get string to number
                WK_room_temp_set_1_msb.sendCommand(transform("JSONPATH","$.room_temp_set_1_msb", data))
                logDebug("Update Thermostat SP", "step 3 data received, checking setpoint in thermostat." )
                val lsb = WK_room_temp_set_1_lsb.state as Number
                val msb = WK_room_temp_set_1_msb.state as Number
                celsius = (lsb + msb *256) / 100
                logDebug("Update Thermostat SP", "step 4 setpoint in thermostat: "+celsius+"°C, should be: "+twish+"°C" )
            ]

I get this error:

Cannot refer to the non-final variable data inside a lambda expression

for every line inside the timer.
Can you give a hint how I should do this?
I use the expire binding on occasion, but here I would like to keep this code in one rule.
I will start studying ReentrantLocks now ;-).

I guess the variable data is the problem. Either define data in global context (i.e. outside the rules at the beginning of the rules file) or define it within the lambda expression (the part inside [| ]).

Is it posiible to set the timer there without anything inside so the while-loop has to wait one cycle of the timer before it can proceed? Almost like the sleep-thing?
I am trying this now without errors, but I think the rule 'hangs’on this.

timer = createTimer(now.plusSeconds(30)) [|
                    data = sendHttpGetRequest("http://192.168.0.6/data.json?heater=0&setpoint="+setp+"&thermostat=0%C3%97") //resend untill succes
                ]

And locking aint working for me yet either. I tryed the syntax from here:

val ReentrantLock thermostatLock = new ReentrantLock
 try {
            thermostatLock.lock()  //Make sure no new setpoints are received through http-binding in other rule
            logInfo("Update Thermostat SP", "step 1 setpoint sent: "+setp+"°C, this means: "+twish+"°C" )
            do {
                data = sendHttpGetRequest("http://192.168.0.6/data.json?heater=0&setpoint="+setp+"&thermostat=0%C3%97") //resend untill succes
                WK_room_temp_set_1_lsb.sendCommand(transform("JSONPATH","$.room_temp_set_1_lsb", data)) //put in item to get string to number
                WK_room_temp_set_1_msb.sendCommand(transform("JSONPATH","$.room_temp_set_1_msb", data))
                val lsb = WK_room_temp_set_1_lsb.state as Number
                val msb = WK_room_temp_set_1_msb.state as Number
                celsius = (lsb + msb *256) / 100
                Thread::sleep(15000) //wait 30 seconds before resending setpoint and get new data
                logInfo("Update Thermostat SP", "step x setpoint in thermostat: "+celsius+"°C, should be: "+twish+"°C" )       
            } while (twish != celsius)
            T_WK_SP_block1.sendCommand(OFF)
            logInfo("Update Thermostat SP", "step 5: Finnished!")
         }
         catch(Throwable t) {
            logError("Error", "Some bad stuff happened in my rule: " + T.toString)
         } 
        finally {
        thermostatLock.unlock()
        }

This is the error I receive:
Rule ‘Update Thermostat SP from openhab’: ‘lock’ is not a member of ‘null’; line 142, column 13, length 21
Which is the line with thermostatLock.lock()

This doesn’t makes sense. A Timer schedules the code in the body to execute at some point in the future. If you want it wait longer you scheudule it further into the future.

The big thing with using timers is you can’t use a do while loop. You have to create timers and keep rescheduling it. Something like:

timer = createTimer(now.plusMinuntes(5), [|
    // get data and parse out the values

    // reschedule the timer
    if(twish != celsius) timer.reschedule(now.plusMinutes(5))
])

The looping is implemented with that timer.reschedule.

As Udo indicated, make sure to put the var Timer timer = null in the global variables section of your .rules file (i.e. above the rules).

Note, it occurs to me that if this is a heater and you overshoot twish (i.e. twish > celsius) the loop will never end. You probably want to use if(twish < celsius).

As with the timer, you must declare the thermostatLock as a global value. Otherwise, a new lock gets created every time the rule executes.

And what you probably want to do is to ignore the new setpoints rather than queueing them up. So you probably want something closer to:

    // ignore thermostat updates if we have a lock
    if(thermostatLock.hasLock) return;

    // get a lock so only one timer can run at a time
    thermostatLock.lock
    timer = createTimer(now.plusMinutes(5), [|
        // get data from URL
        
        // reschedule the timer
        if(twish != celsius) timer.reschedule(now.plusMinutes(5))
        else thermostatLock.unlock
    ])

This is probably not the safest approach however. We can’t use try/catch/finally because we need the lock to stay locked as long as we have a Timer running and unlock it in the timer itself.

So why not use the existence of the Timer as our lock?

    // ignore thermostat updates if we are already running a timer
    if(timer != null) return;

    // start polling the URL
    timer = createTimer(now.plusMinutes(5), [|
        // get data from URL
        
        // reschedule the timer
        if(twish != celsius) timer.reschedule(now.plusMinutes(5)) // @luckymallari assures me this works :-) 
        else timer = null
    ])

Thankx for your help Rich. I still need some more guidence, but I think you will have expected :wink: . At least now I learned that OH does not necessarily walk through the code in the order top → down. I didn’t know that.

I am not comparing actual temperature with setpoint, I am compairing setpoint sent to thermostat with the setpoint read out of the thermostat. It takes a while for the thermostat to accept a new setpoint. The thermostat setpoint can be changed from either openhab or manual input on the thermostat itself.
As long as I don’t have conformation that the new setpoint is accepted and stored in the thermostat, I can not accept a new setpoint coming from the thermostat as a new value for openhab. This would cause old setpoints to overrule the change made in openhab.
If I only raise the time interval of the pulling for new setpoints in the http-binding, this leaves the chance of getting an old setpoint from the thermostat just after setting a new one was set from OH. This would set the OH-setpoint back again (from the other rule). So to prevent the other rule from running, I could check if this timer == null. And With a new timer with anouther name I could prevent this one from running while the other is?

So If I understand correctly, if the timer is reset, the code between the set and reset gets looped?
By the way, I find the notation with if(this){do this} (curly braces) easier to read, so I am sticking to it. Maybe later I will be confident enough to go with your version.
I tryed the following code:

// vars and vals on top of file:
var Timer timer = null
var Number celsius
var String data
val String Lan2RF_ip = "192.168.0.6"

rule "Update Thermostat SP from openhab"
when
    Item T_WK_sp changed
then   
    // ignore thermostat updates if we are already running a timer
    if(timer !== null) {
        logInfo("Update Thermostat SP", "waiting for setpoint from Thermostat to finnish") //Wait untill new setpoint arrived
        return;
    } else {
        val twish = T_WK_sp.state as Number
        val setp = (twish - 5 ) * 10
        sendHttpGetRequest("http://192.168.0.6/data.json?heater=0&setpoint="+setp+"&thermostat=0%C3%97") //set once before timer
        timer = createTimer(now.plusSeconds(30), [| // why is there a comma, I don't see that in the examples?
        // start polling the URL
            data = sendHttpGetRequest("http://192.168.0.6/data.json?heater=0&setpoint="+setp+"&thermostat=0%C3%97") //resend untill succes
            logInfo("Update Thermostat SP", "step 1 setpoint sent: "+setp+"°C, this means: "+twish+"°C" )
            WK_room_temp_set_1_lsb.sendCommand(transform("JSONPATH","$.room_temp_set_1_lsb", data)) //put in item to get string to number
            WK_room_temp_set_1_msb.sendCommand(transform("JSONPATH","$.room_temp_set_1_msb", data))
            val lsb = WK_room_temp_set_1_lsb.state as Number
            val msb = WK_room_temp_set_1_msb.state as Number
            celsius = (lsb + msb *256) / 100 
            if(twish != celsius){
                logInfo("Update Thermostat SP", "step x setpoint in thermostat: "+celsius+"°C, should be: "+twish+"°C" )
                timer.reschedule(now.plusSeconds(10)) // go back to http-get-request
            } else {
                timer = null
                logInfo("Update Thermostat SP", "last step: Finnished!")
            }
        ])        
    }
end

It seams to work for setting the new setpoint, but it also gives me this error in the log:

2018-02-10 10:35:09.191 [ome.event.ItemCommandEvent] - Item 'T_WK_sp' received command 20.0

2018-02-10 10:35:09.208 [vent.ItemStateChangedEvent] - T_WK_sp changed from 20.5 to 20.0

2018-02-10 10:35:10.626 [vent.ItemStateChangedEvent] - systeminfo_computer_openHABianPi_cpu
2018-02-10 10:35:39.950 [ERROR] [.smarthome.model.script.actions.HTTP] - Fatal transport error: java.util.concurrent.TimeoutException

2018-02-10 10:35:39.966 [INFO ] [me.model.script.Update Thermostat SP] - step 1 setpoint sent: 150.0°C, this means: 20.0°C

2018-02-10 10:35:39.996 [ERROR] [ore.transform.actions.Transformation] - Error executing the transformation 'JSONPATH': the given parameters 'JSonPath' and 'source' must not be null

2018-02-10 10:35:40.023 [WARN ] [rthome.model.script.actions.BusEvent] - Cannot convert 'null' to a command type which item 'WK_room_temp_set_1_lsb' accepts: [DecimalType, RefreshType].

2018-02-10 10:35:40.031 [ERROR] [ore.transform.actions.Transformation] - Error executing the transformation 'JSONPATH': the given parameters 'JSonPath' and 'source' must not be null

2018-02-10 10:35:40.037 [WARN ] [rthome.model.script.actions.BusEvent] - Cannot convert 'null' to a command type which item 'WK_room_temp_set_1_msb' accepts: [DecimalType, RefreshType].

2018-02-10 10:35:40.051 [INFO ] [me.model.script.Update Thermostat SP] - last step: Finnished!

I thought it was working, but the setpoint keeps changing between the new and the old. I will report back when I understand a bit more why that is. Thanks for your advice!

A timer schedules a block of code called a lambda to execute at a time in the future. The lambda is defined by [| code]. So when you reschedule the timer you are telling it to run all the code in the lambda again at the specified time.

You will in all of my examples. A lambda is a variable like any other variable. The createTimer method takes two arguments, a DateTime and a lambda. The arguments to a method are always separated by a comma.

The language upon which the Rules DSL is based allows one to take a short cut in the case where the last argument to a method is a lambda. Instead of defining the lambda normally, one and define it immediately after the method instead of between one ( ) with a comma.

The error indicates that the get http call is timing out without a return value.

1 Like

Thanks for the explanation Rich.
I have the following rules now with your suggestion of the timer. The first rule is working, but the second no longer fires.

i made an extra set of items to prevent some unwanted interaction between the rules that use them.
default.items:

Number T_WK_OH2Lan_set_lsb //For the OH2Lan-rule
Number T_WK_OH2Lan_set_msb //For the OH2Lan rule
Number WK_room_temp_set_1_lsb "Thermostaat setpoint lsb" { http="<[CV_data:10000:JSONPATH($.room_temp_set_1_lsb)]" } //receive setpoint lsb
Number WK_room_temp_set_1_msb "Thermostaat setpoint msb" { http="<[CV_data:10000:JSONPATH($.room_temp_set_1_msb)]" } //receive setpoint msb

default.sitemap:

Text item=T_WK_is
Setpoint item=T_WK_sp minValue=10 maxValue=24 step=0.5

default.rules:

// Here come the vars
var Timer Lan2RFtimer = null
var Number celsius
var String data
val String Lan2RF_ip = "192.168.0.6" //check the right IP in browser!
// Here come the rules
rule "Update Thermostat SP OH2Lan"
when
    Item T_WK_sp changed
then   
    // ignore thermostat updates if we are already running a timer
    if(Lan2RFtimer !== null) {
        logInfo("Update Thermostat SP", "waiting for setpoint from Thermostat to finish") //Wait untill new setpoint arrived
        return;
    } else {
        val twish = T_WK_sp.state as Number
        val setp = (twish - 5 ) * 10
        sendHttpGetRequest("http://192.168.0.6/data.json?heater=0&setpoint="+setp+"&thermostat=0%C3%97") //set once before timer
        Lan2RFtimer = createTimer(now.plusSeconds(30), [| // why is there a comma, I don't see that in the examples?
        // start polling the URL
            data = sendHttpGetRequest("http://192.168.0.6/data.json?heater=0&setpoint="+setp+"&thermostat=0%C3%97") //resend fter 30sec and check result
            T_WK_OH2Lan_set_lsb.sendCommand(transform("JSONPATH","$.room_temp_set_1_lsb", data)) //put in item to get string to number
            T_WK_OH2Lan_set_msb.sendCommand(transform("JSONPATH","$.room_temp_set_1_msb", data))
            val lsb = T_WK_OH2Lan_set_lsb.state as Number
            val msb = T_WK_OH2Lan_set_msb.state as Number
            celsius = (lsb + msb *256) / 100 
            if(twish != celsius){
                logInfo("Update Thermostat SP", "step x setpoint in thermostat: "+celsius+"°C, should be: "+twish+"°C" )
                Lan2RFtimer.reschedule(now.plusSeconds(10)) // try again in 10 seconds
            } else {
                // wait until timer finishes before allowing Lan2OH start
                logInfo("Update Thermostat SP", "Setting new setpoint: "+celsius+"°C Finished!")
                Lan2RFtimer = null //could I leave this out to always wait until the timer finishes?
            }
        ])        
    }
end


rule "Update Thermostat SP Lan2OH"
when
    Item WK_room_temp_set_1_lsb changed or
    Item WK_room_temp_set_1_msb changed
then
    if(Lan2RFtimer !== null) {
        logInfo("Update Thermostat SP from Thermostat", "Waiting voor Thermostat Lan2OH is finished")
        //Wait untill new setpoint arrived
        return;
    } else{
        val lsb = WK_room_temp_set_1_lsb.state as Number
        val msb = WK_room_temp_set_1_msb.state as Number
        val celsius = (lsb + msb * 256) / 100
        T_WK_sp.postUpdate(celsius)
        logInfo("Update Thermostat SP from Thermostat", "temperature setpoint recieved: "+celsius+"°C")
    }
end

I give up for today. Tomorrow I will look agaiin. if you spot whats wrong, that would be grait.
Culd I leave the line out where the timer is set to null, to always wait until the timer finishes?

To combine both rules into one, I would need to test which of the items changed that can trigger the rules. Is something like this possible? If (Item T_WK_sp changed){ do stuff} Or is there a simple way to test which of the items in the ‘when’ part of the rule caused the rule to trigger?

The timer already always finishes. If you remove the line that sets ur to null your rule will no longer run because timer will never equal null

if(triggeringItem.name == "itemName")
1 Like

Wow, that works perfectly! (couldn’t wait trying)
I also discovered I should not use too difficult names for the timer. Do you know whay that is?
For now I am happy with one single rule for the setpoint two-way communication, and one rulle for all the read-only communication (at this stage only the measured temperature and the displaycode, but I will add all documenten values at some point in time.

rule "Update Thermostat SP two-way communication"
when
    Item T_WK_sp changed or
    Item WK_room_temp_set_1_lsb changed or
    Item WK_room_temp_set_1_msb changed
then   
    // ignore thermostat updates if we are already running a timer
    if(timer !== null) {
        logInfo("Update Thermostat SP", "waiting for setpoint from Thermostat to finish") //Wait untill new setpoint arrived
        return;
    } else {
        if(triggeringItem.name == "T_WK_sp"){
            val twish = T_WK_sp.state as Number
            val setp = (twish - 5 ) * 10
            sendHttpGetRequest("http://192.168.0.6/data.json?heater=0&setpoint="+setp+"&thermostat=0%C3%97") //set once before timer
            timer = createTimer(now.plusSeconds(30), [|
                // start polling the URL
                data = sendHttpGetRequest("http://192.168.0.6/data.json?heater=0&setpoint="+setp+"&thermostat=0%C3%97") //resend fter 30sec and check result
                T_WK_OH2Lan_set_lsb.sendCommand(transform("JSONPATH","$.room_temp_set_1_lsb", data)) //put in item to get string to number
                T_WK_OH2Lan_set_msb.sendCommand(transform("JSONPATH","$.room_temp_set_1_msb", data))
                val lsb = T_WK_OH2Lan_set_lsb.state as Number
                val msb = T_WK_OH2Lan_set_msb.state as Number
                celsius = (lsb + msb *256) / 100 
                if(twish != celsius){
                    logInfo("Update Thermostat SP", "step x setpoint in thermostat: "+celsius+"°C, should be: "+twish+"°C" )
                    timer.reschedule(now.plusSeconds(10)) // try again in 10 seconds
                } else {
                    // wait until timer finishes before allowing Lan2OH start
                    logInfo("Update Thermostat SP", "Setting new setpoint: "+celsius+"°C Finished!")
                    timer = null //could I leave this out?
                }
            ])
        } else{
            val lsb = WK_room_temp_set_1_lsb.state as Number
        val msb = WK_room_temp_set_1_msb.state as Number
        val celsius = (lsb + msb * 256) / 100
        T_WK_sp.postUpdate(celsius)
        logInfo("Update Thermostat SP from Thermostat", "temperature setpoint recieved: "+celsius+"°C")
        }
    }
end

rule "Update Livingroom Thermostat real temperature"
when
    Item WK_room_temp_1_lsb changed or
    Item WK_room_temp_1_msb changed
then
    val lsb = WK_room_temp_1_lsb.state as Number
    val msb = WK_room_temp_1_msb.state as Number
    val celsius = (lsb + msb * 256) / 100
    T_WK_is.postUpdate(celsius)
    logInfo("Update Thermostat", "New temperature recieved")
end

I am now adding other values that need conversion from lsb and msb to a value. So I thought is was better to do it like the following code. But then I need the lsb and msb etc to be a var in stead of a val and put it on top of the rules file. But then I have the chance that different var-declarations interact wrongly. Now I prevent that with the if-else but this is very clumsy of course. Is there also a way to go through a predifined list of items-pairs (i.e. WK_ch_temp, T_flow for the first calculation of bit2value) and use each item from the lis to construct somethin like this:

rule "Update Livingroom Thermostat real temperature"
when
    Item WK_ch_temp_lsb changed or
    Item WK_ch_temp_msb changed or
    Item WK_tap_temp_lsb changed or
    Item WK_tap_temp_msb changed or
    Item WK_ch_pressure_lsb changed or
    Item WK_ch_pressure_msb changed or
    Item WK_room_temp_1_lsb changed or
    Item WK_room_temp_1_msb changed
then
    //ToDo: iterate through 5 bit2value calculations
    //val ArrayList<String> datalist = new ArrayList<String>("WK_ch_temp", "WK_tap_temp", "WK_ch_pressure_lsb", "WK_room_temp_1")
    //for each
    if(triggeringItem.name == "WK_temp_lsb" || triggeringItem.name == "WK_temp_msb"){
         lsb = WK_ch_temp_lsb.state as Number
         msb = WK_ch_temp_msb.state as Number
         bit2value = (lsb + msb * 256) / 100
        T_flow.postUpdate(bit2value)
    } else if(triggeringItem.name == "WK_tap_temp_lsb" || triggeringItem.name == "WK_tap_temp_msb"){
         lsb = WK_tap_temp_lsb.state as Number
         msb = WK_tap_temp_msb.state as Number
         bit2value = (lsb + msb * 256) / 100
        T_tap.postUpdate(bit2value)
    } else if(triggeringItem.name == "WK_ch_pressure_lsb" || triggeringItem.name == "WK_ch_pressure_msb"){
         lsb = WK_ch_pressure_lsb.state as Number
         msb = WK_ch_pressure_msb.state as Number
         bit2value = (lsb + msb * 256) / 100
        WK_pressure.postUpdate(bit2value)
    } else if(triggeringItem.name == "WK_room_temp_1_lsb" || triggeringItem.name == "WK_room_temp_1_msb"){
        lsb = WK_room_temp_1_lsb.state as Number
        msb = WK_room_temp_1_msb.state as Number
        bit2value = (lsb + msb * 256) / 100
        T_WK_is.postUpdate(bit2value)
        logInfo("Update Thermostat", "New data recieved")
    }
end

You will have to be more specific.

This is what lambdas are for. You just pass the Items you want to calculate.

Or you can use Design Pattern: Associated Items.

Hahaha, yes I can imagine. Sorry for that. I started with a name in ALLCAPS, but VS_Code gave it a very different collor than the other timer so I figured this was not good. Then I tried several names with underscore or ‘-’ but ended up with just lowercase lettres.

I really have to seperate the calculations cause I sometimes get crazy results from the bit2value. This can only happen in my imagination, when the lsb and msb of different items are combined, or if lsb is from the former update while msb is from the new. I may have to build in a sleep moment to be shure that all items that come from the JSON are refreshed.

Maybe it would also be better if I would give all these values a different name in stead of re-using just 3 variables. What is your idea on that?

I will definately also look into the Lambda’s. but untill now they look horifying to me. And I didn’t find an example with two variabels yet.

By convention people use all caps to indicate constants. Kuba probably added that assumption to the syntax highlighting. But it isn’t a problem.

I think underscores are ok but dashes are definitely not allowed. By convention, starting with lower case and then capitalizing every word in the name (sometimes called camel case) is used for variables.

var myComplicatedVariableName
val MY CONSTANT = 10

When in doubt, do what makes it easier for a human to read and understand. There is nothing wrong with reusing variables.

val Functions$Function2<Arg1Type, Arg2Type, ReturnType> name= [arg1, arg2 | 
     // Do stuff
     ReturnValue
]

Where you use the appropriate types for Arg1Type et al and ReturnType and ReturnValue is what you want the lambda to return.

Replace Function with Procedure and remove all the Return stuff if you don’t have a return value.

Hi Guido, i’m new to all this, installed openHab today on a Pi3 with a Razzberry everything is working… now i want to read en change the settings from my “Intergas” Lan2Rf module. This isn’t so easy for my as for a samsung tv or onkyo receiver where I can install a package. can you be more specific how to install this on the Openhabian :slight_smile:

Hi, I am fairly new to all this too, and in the mean time I changed a lot. Some for better, some for worse. But my lan2rf is not working correctly now, I am talking to the vendor about exchanging the module.
If they don’t, I am in the market for a OTGW.
So the posted work is not finished.

Anyway, you should be able to put the above content in the files as stated. (I am now on my phone, cannot scroll easily) . Best to use the samba share linking File Explorer on a winPC to the config directory of openhab.
For instance:
services/http.cfg
rules/default.rules
Tec.

Seems it is quite a while ago since the last post. Despite I probably in future will also move to the OTGW solution, I could post my code and setup (based on Guido’s) if it is helpful to anyone.

It is even a much longer time ago since your last post, but I definately am interested.
I only have time for openhab every once in a while and I never got to rewrite my Incomfort rules and items. Please do post your solution?