[SOLVED] Pass a variable to timerfunction in Blocky

  • Platform information:
    • Hardware: Raspberry PI 4
    • openHAB version: 4.1.1

Is it possible to develop this code in blocky.
I use this to turn off different Lights.
I found how to get the calling event item, but I didn’t find a solution to pass the itemname to the Funciton in Blocky

var name = event.itemName;
var zeit = (name =='Licht_1_Betrieb')? 10 : 15;
var timername = "Timer"+name;
var fuctionvar = function (itemname) {
  return function() {
    items.getItem(itemname).sendCommand('OFF');
  };
}

if (cache.private.exists(timername) === false || cache.private.get(timername).hasTerminated()) {
  cache.private.put(timername, actions.ScriptExecution.createTimer(timername, time.ZonedDateTime.now().plusMinutes(zeit), fuctionvar(name)));
} else {  
  cache.private.get(timername).reschedule(time.ZonedDateTime.now().plusMinutes(zeit));
};

You cannot create a function that returns a function in Blockly like this. But you don’t really need to.

But first I’ll answer the XY Problem. To create a function that takes an argument click the cog icon on the function block and drag one “input name” for each argument to pass to the function.

image

Then when you add the call function block you’ll have a slot for each input name.

image

But all this function stuff is unnecessary. It was unnecessary in the original code too but it’s definitely not needed for blockly.

Were I to rewrite the original code it would look something like:

var timerName = "Timer"+event.itemName;
var timer = cache.private.get(timerName);
var zeit = (name == 'Licht_1_Betrieb') ? time.toZDT('PT10M') : time.toZDT('PT15M');
if(timer === null || timer.hasTerminated()) {
  cache.private.put(timerName, 
                    actions.ScriptExecution.createTimer(timername, zeit,
                                                        () => items[event.itemName].sendCommand('OFF'))); 
}
else {
  timer.reschedule(zeit);
}

Which helps inform the approach to use in Blockly.

Close to a one-to-one block to line of code translation into Blockly is as follows:

image

The Java Script these blocks generate become:

var name2, zeit;


name2 = event.itemName;
if (name2 == 'Licht_1_Betrieb') {
  zeit = 10;
} else {
  zeit = 15;
}
if (cache.private.exists(('Timer' + String(name2))) === false || cache.private.get(('Timer' + String(name2))).hasTerminated()) {
  cache.private.put(('Timer' + String(name2)), actions.ScriptExecution.createTimer(('Timer' + String(name2)), time.ZonedDateTime.now().plusMinutes(zeit), function () {
    name2.sendCommand('OFF');
    }));
} else {
  cache.private.get(('Timer' + String(name2))).reschedule(time.ZonedDateTime.now().plusMinutes(zeit));
};

which is pretty close to my rewriting.

But the ultimate XY Problem here is you don’t need a rule for this in the first place. Set expire metadata on the Items and the Items will turn OFF after the given amount of time, no rule required.

1 Like

That`s what I tried first, you created in Blocky for me.
It works if only one switch ist turned on, because the name variable has the origin value if the timer ends and the send command is called.
If during the first Timer running ta second light is turned on the name variable gets a new value and after the time of the first timer expires, the second light is turned of because the name variable has now the value of the second light and the first is still burning.
So this was the only way I got it working to pass the item name into the timer function.

If I set expire would it be rescheduled if the light is turned of an on again?

Yes.

In that case the following should work. Move the creation of the Timer to a function. The variables should become fixed inside the context of the function so they don’t get overwritten on subsequent calls to the rule.

Thank you for helping.
It`s great how fast I got a solution for the problem.
Blocky is a very powerful toolset.
I have tried the solution with expire and it also works great.

1 Like

You know what, I completely forgot about my own library. :nerd_face:

I have a library for which I’ve created a block library which has a Timer Manager built to handle a case like this.

Obviously expire is the correct end solution but in the more general case where you have one timer per Item Timer Manager can be very powerful.

I am not sure if the function is required with Timer Manager to fix the variables. I’d need to look at the code to double check so I’ve left it in a function.

It doesn’t really add much in this one case but if you need to detect flapping it can handle that for you.