Delay on item action exec binding

Hi Guys,

I hope someone can help me I’m using the exec binding and codesend to control some RF sockets however there is one issue if you switch several sockets quickly after each other than the command get’s “lost” and I was thinking if I could add a second delay or something straight into my items?

Currently it looks like:

Switch Window_light "Exec" (exec) { exec=">[ON:/bin/sh@@-c@@sudo /scripts/switch.sh 7721996] >[OFF:/bin/sh@@-c@@sudo /scripts/switch.sh 7721987]" }

I would need to have a second or two delay from openhab in case I use one of the switch items but adding a delay in the script didn’t seem to help.

Any suggestions appreciated.

Thanks

EDIT:

Seems like I’m coming closer to a solution whilst using sleep like:

Switch Window_light "Exec" (exec) { exec=">[ON:/bin/sh@@-c@@sleep 1;sudo /scripts/switch.sh 7721996] >[OFF:/bin/sh@@-c@@sleep 1;sudo /scripts/switch.sh 7721987]" }

I will evaluate this over the next week or so

1 Like

Personally I would move the exec to a rule or rules and use executeCommandLine to call your script. Then you can add logic to only add the delay if the other switch was triggered less than a second before. Most of the time your switches will work instantly and you will only see the delay in those cases where it is really needed. You can add the sleep in the rule using Thread::Sleep(1000).

Hi,

Thanks for your reply this sounds very good to me however I’m not sure how to implement this if you don’t mind could you give me an example? Also I just noticed whilst changing colour’s on my milight LED bulbs if I do “all at once” the command seems to get mixed up and I get different results than I want this is an edge case scenario however I was thinking there might be a solution to add delays in general if more than one item is being changed weather that is brightness colour or on/off

Any chance of doing this?

Thanks a lot

Here is an example of how I do the exec from a rule instead of the Item.

Item:

 Switch S_N_GarageMqttReporter "Reset mqttReporter on Garage" <network> // Note it is not bound to anything

Rule

rule "Reset Garage mqttReporter"
when
        Item S_N_GarageMqttReporter received command
then
    var String results = executeCommandLine("chmod a+x /etc/openhab/configurations/scripts/*", 5000)
    logInfo("Network", "Resetting permissions on script:\n" + results)
    results = executeCommandLine("chmod go-r ~openhab/.ssh/*", 5000)
    logInfo("Network", "Resetting permissions on .ssh:\n" + results)
    results = executeCommandLine("/etc/openhab/configurations/scripts/resetGarageMqttReporter.sh", 5000)
    logInfo("Network", "Resetting garage mwttReporter. Results:\n" + results)
end

Notice how above I added some chmod commands in which is a work around to deal with the fact that the OH start script messes up my permissions every time OH restarts. This is where you would put your Thread::sleep(1000).

There are a number of ways you could inject the sleep only if one of the switches received the command within the last second. Here is just one approach.


var Timer sleepTimer = null

rule "Activate Timer"
when
    Item Window_light received command
then
    if(sleepTimer != null) {
        Thread::sleep(1000)
    }
    sleepTimer = createTimer(now.plusSeconds(1), [| sleepTimer = null])

    val results = executeCommandLine("/bin/sh@@-c@@sleep 1;sudo /scripts/switch.sh 7721996] >[OFF:/bin/sh@@-c@@sleep 1;sudo /scripts/switch.sh 7721987", 5000)
end

// Create a rule for each switch that needs to be a second apart from each other. If you just want commands to this 
// switch to be impacted you are done

NOTE: I just typed in the above, there may be errors.

1 Like

Alright I’ll try this on the weekend so essentially all I need to do is create a rule for all switches which need a delay in my case these are 3 and remove the commend from the switch item itself and move it into my rule.

Which means per switch I would need two rules since or add an or operator on the first line where we say When XXX correct?

Thank a lot for your help really appreciate it!

Hi @rlkoshak and Others!

This is an old thread but my topic is similar so it might be valuable to others if I continue here with my question.

I use the caldav binding to schedule my 433 MHz outlets. If I were to schedule several outlets to operate at the exact same time there would be “conflicts” in the handling of transmissions or possible misses of commands handled by my transmitter. To remedy this I’ve created a separate set of switches which is used by the calendar commands and they impose an individual “delay thread” duration to separate the transmissions before calling the actual switch item. This approach works quite well and the only downside is that its quite tedious to add new outlets and right now my latest outlet imposes a delay of 10,5 s before giving the command to the switch item.

Could I utilize something along the way as you describe above instead? I guess the code is quite similar with the obvious difference that in my case I would have to define a specific delay for each switch.

A more efficient code would be to somehow use a function, one function, for the delay which executes it solely based on the criteria that the switch is in a group. This approach would be modular and I would avoid the hassle of adding new rules for each new addition of outlets.

In short, something like:

rule "delay for calendar switches"
when 
   item [in group calendarSwitches] received command // unspecified item but belonging to the group
then
   val results = executeCommandLine([if command ON do this for the specific item if command OFF do that for the specific item], 1500) //sleep 1500 ms right?
   // during this sleep the next command in line would hold at OH before executing, is this possible?
   // the commands would hopefully then be queuing up at OH before being sent to the transmitter
end

Would this be possible? Note that the code in brackets is my pseudo code, my rule-code-fu is not strong enough. Please, if you know the right code expressions for this I would be most grateful!

Also, I guess this approach would be good not only for calendar delays but for switching “simplex” (433 MHz) outlets in general.

Thanks!

I would use a lock. When the rule triggers from your caldav switch, grab the lock, send the command, sleep for a sec or however long you need to to keep the commands from interfering with eachother, then release the lock. All the other copies of the rule or other rules that depend on that lock will have to wait until it is unlocked before continuing.

Unfortunately this won’t work. You can trigger a Rule based on the Group receiving an update but unfortunately the Group receives multiple updates for each single update to one of its members.

So your rule will have to have a trigger for each of your outlets. :frowning:

But you can use the same Rule to process everything and use the lock to space out the commands to your switches. The requirement to have a custom delay for each switch raises a bookkeeping problem though.

There are three ways to deal with it.

  1. Put the delays in a separate Item with a similar name to the switch and pull them up as described here: Design Pattern: Associated Items
  2. Put the delays in a global var/val data structure which gets populated in a System started rule
  3. Create a separate rule for each switch and a lambda to implement the logic and pass the delay to the lambda

1 is nice in that I like storing state in Items where possible but it will be really awkward to add new outlets.

2 will also be a bit awkward but a little less so for the same reasons.

3 will be more code but I think easier to deal with in the long run.

I would go with 3 (after a long time of recommending and implementing 1 and 2) which would look something like this:

NOTE: this is written using OH 2, I think it will work as written in OH 1.8. But I’m also just typing this in so there will likely be some errors.

import org.eclipse.xtext.xbase.lib.Functions
import java.util.concurrent.locks.ReentrantLock

val ReentrantLock lock = new ReentrantLock

val Functions$Function4<SwitchItem, OnOffType, Float, ReentrantLock, Boolean> switchDelay = [switch, command, delay, lock |
    try {
        lock.lock // blocks until the local is unlocked
        switch.sendCommand(command)
        Thread::sleep(delay)
    }
    catch(Throwable t) {
        logError("outlets", "Error in switchDelay: " + t.toString)
        lock.unlock // shoudn't be necessary but I've seen inconsistant behavior with finally executing when there is an error
    }
    finally {
        lock.unlock
    }
    true // return value
]

rule "Switch 1"
when
    Item CalSwitch1 received command
then
    switchDelay.apply(RealSwitch1, receivedCommand, 10.5, lock)
end

rule "Switch 2"
when
    Item CalSwitch2 received command
then
    switchDelay.apply(RealSwitch2, receivedCommand, 0.5, lock)
end

...

Thank you for the input!

Actually, the use of individual delays is not a wanted feature. It was just a way for me to make certain that the transmissions would be separated in time. So the passing of different delays can be cancelled. Your approach according to the third strategy sounds great.

Why is a lock more suitable then a thread delay? Wouldn’t a thread delay be sufficient?

I’m also on the OH2 train. I’m not being successful running your code though and I’m not skilled enough to identify whats wrong with the syntax. Short and simple rules I can manage but this is beyond me. If you have the time to review it more thoroughly that would be great!

Note: I’m also using a switch “enable calendar control”. But this I can manage to include by myself with a simple if statement.

It wouldn’t work because it doesn’t stop other instances of the same rule from running at the same time. If, for example you have two events that come in 10 mseconds apart, your will have two copies of “delay for calendar switches” running at the same time. It doesn’t matter how long you do a Thread::sleep for it will only affect that one instance of the Rule. The other one will continue running.

In short, you can add all the delays you want in your rule using a Timer or Thread::sleep but that will only cause a delay from when the caldav switch triggered and your outlet switches. It does not cause a delay between two caldav switches that are triggered close together which is what you need.

There is a similar problem with a Timer as the example I typed in above.

What you need is a way to prevent other instances of that Rule and any other Rule that sends commands that might cause interference from running at the same time and force them all to wait until the previous instance of the Rule stops running. This is exactly what the lock does.

I’m not a computer where I can do anything.

I recommend loading it into Designer to check for syntax errors and posting any errors that appear in the log.

I solved it. I did some reading on the lambda syntax so now I understand the structure and passing of arguments. The lambda guide was also your doing, thank you!

The only flaws with your code suggestion was that “switch” is a reserved word so I changed it to “outlet” and also the sleep function expects an integer in ms but your suggestions passes a float. I canceled the passing of the delay anyhow as I don’t need it.

The only warning now visible is the following.

22:12:54.374 [WARN ] [ore.internal.events.OSGiEventManager] - Dispatching event to subscriber 'org.eclipse.smarthome.model.rule.runtime.internal.engine.RuleEngineImpl@8d2d56' takes more than 5000ms.

But this is only a warning so I guess it works.

Thank you for forcing me to learn some coding!

Really glad you got it working. Mistakes like that is what happens when writing code on one’s phone. :grin: Or without using Designer.

The warning is just because it is taking a rule longer than 5 seconds to run, probably because it is blocked by the lock, which is what we want. You can experiment with the delay to make it as short as possible and maybe the warning will go away.