Switch items to be on hold for a 12seconds

So I have installed a bar in my flat that comes down from the ceiling, I made a video from it.
Here is a rough setup of the bar, it has two sides one for wine and one for spirits/liquior.(two seperate blind motors)
It has an arduino with 4 buttons hooked up to 4 relays, 1 relay shut power( on/off) and the second one select direction(up/down),

However as seen in the video it takes 12seconds to lower and rise the bar, so my question is how can I deactive that the user are allowed to change the state of the switch during this time? Or should I change the switch item to a scene(number) instead?

Lastly does someone have some nice bar icon?

Below my item file:

Switch bar_wine "Wine" (Group_LivingRoom,Group_LightLogging) //Mappings("UP","DOWN)
Switch bar_spirit "Spirit" (Group_LivingRoom,Group_LightLogging) //Mappings("UP","DOWN)
String Arduino "Arduino" { serial="/dev/tty99@9600" }

Below is my rule file:

rule "Wine"

when
    Item bar_wine changed 
then
	
	
    if(bar_wine.state == OFF){
        Arduino.sendCommand("3")
		//postUpdate(bar_wine,ON)
    }
    else{
        Arduino.sendCommand("4")
		//postUpdate(bar_wine,ON)
    }
end

rule "Spirit"

when
    Item bar_spirit changed 
then
	
	
    if(bar_spirit.state == OFF){
        Arduino.sendCommand("1")
    }
    else{
        Arduino.sendCommand("2")
    }
end

You can add a Switch Item that gets set to OFF when the bar is initially triggered with a Timer to turn it back to ON after 12 seconds. Then put a check in your rule to only do anything when that Switch is ON.

I donā€™t think the UI is reliably responsive enough to try to do something like hiding the switch for 12 seconds using the visibility flag or the like so your best bet will be to just ignore the presses during that time.

Brilliantā€¦ will try to implement it on monday or soā€¦ got the concept at
least.

So, I finally got around to do some openhab work yesterday.

So here is my GUI:

So I tried to do what Rich suggested, but I am a bit confused how the delays works:

import org.openhab.core.persistence.*
import org.openhab.model.script.actions.*
import org.openhab.core.library.types.*

var Timer timer=null

rule "Bar_Delay"

when
    Item bar_wine changed or bar_spirit changed 
then
	if bar_delay.state = OFF {
		BarTimer = createTimer(now.plusSeconds(12))[|
			sendCommand(bar_delay, ON)
		]
	}
	else{
		sendCommand(bar_delay, OFF)
	}
	
end

rule "Wine"

when
    Item bar_wine changed 
then
		
    if(bar_wine.state == OFF and bar_delay.state = OFF ){
        Arduino.sendCommand("3")
    }
    else if bar_wine.state == ON and bar_delay.state = OFF {
        Arduino.sendCommand("4")
    }
	else{
		bar_wine.state= !bar_wine.state
	}
end

The arduino code can be found here:

Care to elaborate?

Upon review of the code above I would like to strongly recommend using Designer. There are a ton of syntax errors in those few lines of code. You should be seeing errors in your logs.

Your Bar_Delay rule trigger should be:

Item bar_wine changed or
Item bar_spirit changed

You do not declare BarTimer anywhere. Did you mean to use ā€œtimerā€ which is declared?

timer = createTimer(now.plusSeconds(12), [| ...

Your if statement is incorrect correct syntax. It should be:

if(bar_delay.state == OFF) {

In your Wine rule, as with the previous, you need to use ā€œ==ā€ instead of ā€œ=ā€ to do a comparison.

You cannot change the state of an Item using an assignment (ignoring the fact that I donā€™t think you can use the Not operator on a State object like that either).

bar_wine.sendCommand(if(bar_wine.state == OFF) ON else OFF)

Finally, this is just a nit pick, but it seems to work a lot more reliably if you use the sendCommand method on the Item rather than using the sendCommand action.:

bar_delay.sendCommand(ON)

It isnā€™t that much of a problem for Switches and Contacts but when you start using Numbers, Dimmers, and other Items that accept multiple formats for input the action tends to be a problem.

Sorry, but today the designer tool did not work. Can not read my items file for some reason. (Work computer reading from dropboxā€¦)

So what I have problem understanding is the timer thing. Does it spins off in a seperate thread or does it work like arduino delay, stops the thread for X seconds?

So if its like arduino then:

do something
timer(12s) pauses the rule for 12s
do something

What happens then if the rule is called again, does the rule run again in a seprate thread, or does not trigger because the rule is already doing somestuff?

So what I am trying to achieve is the switch item bar_wine is not allowed to change value no matter how many times the rules wine is executed for 12s from the first time the rule were excuted.

So in my code above how do I know which rule is excuted first:

rule "Bar_Delay"

when
    Item bar_wine changed or Item bar_spirit changed 
then

or

rule "Wine"

when
    Item bar_wine changed 
then

So my guess is that I need to move the barDelay rule into the wine rule.

Sidenote: Another issue I saw yesterday aswell was that the USB port went down and only came back again after reboot, is this a common issue? (Easier to reboot then unplugā€¦)

Updated code:

var Timer BarTimer=null

rule "Wine"

when
    Item bar_wine changed 
then
		
    if(bar_wine.state == OFF and bar_delay.state = OFF ){
        Arduino.sendCommand("3")
        BarTimer = createTimer(now.plusSeconds(12))[|
			bar_delay.sendCommand(ON)
		]
    }
    else if bar_wine.state == ON and bar_delay.state = OFF {
        Arduino.sendCommand("4")
        BarTimer = createTimer(now.plusSeconds(12))[|
			bar_delay.sendCommand(ON)
		]
    }
	else{
		bar_wine.sendCommand(if(bar_wine.state == OFF) ON else OFF)
	}
end

It gets added to a Timer thread which executes the code in a separate thread at the scheduled time. If you want to stop execution for a period of time use Thread::sleep(msec).

Each triggered rule runs in a separate thread. So if you have a rule that is sleeping (Thread::sleep) and the rule is triggered again two instances of the Rule will be running at the same time in parallel. See this post I just make discussing the difference between Timer, Thread::sleep, and the updatedSince/changedSince.

You donā€™t know which will be executed first. They will be both executing in parallel.

In this case I think that does make sense as you want to make sure the Bar_Delay executes first.

Iā€™ve not seen that issue before nor has it been reported on these forums that I can recall.

Updated code needs to be to be syntactically correct (edits have a comment indicating what I changed):

import org.openhab.model.script.actions.* // need to import Timer and createTimer

var Timer BarTimer=null

rule "Wine"

when
    Item bar_wine changed 
then
		
    if(bar_wine.state == OFF && bar_delay.state == OFF ){ // && instead of and, == instead of =
        Arduino.sendCommand("3")
        BarTimer = createTimer(now.plusSeconds(12))[|
			bar_delay.sendCommand(ON)
		]
    }
    else if (bar_wine.state == ON && bar_delay.state == OFF) { // put if clause in ( ), && instead of and,  == instead of =
        Arduino.sendCommand("4")
        BarTimer = createTimer(now.plusSeconds(12))[|
			bar_delay.sendCommand(ON)
		]
    }
	else{
		bar_wine.sendCommand(if(bar_wine.state == OFF) ON else OFF)
	}
end

Do you have bar_wine and bar_delay declared in an .item file? Your screenshot of Designer indicates that neither are defined. You must declare all your Items in a .items file in order to reference them from inside a rule.

While the code above is now syntactically correct, it doesnā€™t make much sense to me logically.

I would write your rule something like to following (note, the most common coding style you will find in examples on this forum and the OH wiki is to use first cap camel case for Item names and first letter lower-case for variables which I use here):

#Items

Switch BarWine // whatever your binding is
Switch BarSpirits // whatever your binding is
Switch BarDelay
import org.openhab.model.script.actions.*
import java.util.concurrent.locks.ReentrantLock

var Timer barTimer = null
val ReentrantLock lock = new ReentrantLock // used so we can use the same Timer for both spirits and wine

rule "Wine"
when
    Item BarWine changed
then
    lock.lock // prevents the rule from being triggered more than once at a time or BarSpirits as well
    try {
        // Only do something it BarDelay is off
        if(BarDelay.state == OFF){

            // Send the command to the Arduino
            if(BarWine.state == OFF) Arduino.sendCommand("3")
            else Arduino.sendCommand("4")

            // Turn on BarDelay so this and spirit rule won't execite
            BarDelay.sendCommand(ON)

            // Probably not needed here but it is good to get in the habit of cleaning up Timers before creating new ones
            if(barTimer != null){
                barTimer.cancel
                barTimer = null
            }
            
            // Turn off BarDelay in 12 seconds
            barTimer = createTimer(now.plusSeconds(12), [|
                BarDelay.sendCommand(OFF)
                barTimer = null
            ]
        }
    } 
    // Catch any error
    catch(Throwable t) { }
    // Make sure the lock gets unlocked even in the case there is an error
    finally {
        lock.unlock
    }
end

rule "Spirits"
// Pretty much a copy of the above. It is probably possible to merge the two but get it working as two separate rules first

In words what is happening above is a lock is set which will make it so only one instance of the code between the lock and unlock can execute at the same time. This is me being overly cautious as I doubt it will ever be a case that you can press the button fast enough that multiple copies of the rule would be executing in parallel, but this will ensure that is the case. By putting the lock in both rules it also prevents changes the Spirits and Wine rules from executing in parallel as well.

Next I simply check for the state of BarDelay. The main part of the rule will only execute if it is OFF. The rule exits without doing anything but unlocking the lock if BarDelay is ON.

If BarDelay is OFF, if BarWine is OFF we send ā€œ3ā€ to Arduino. If BarWine is ON we send ā€œ4ā€ to Arduino.

Now we set BarDelay to ON which activates the delay. Because we put the locks around the rule, this will block the execution of the logic in any instances of the rule that have queued up while this instance was running (unlikely as that may be) and all subsequent instances until it is turned OFF.

Next we do some bookkeeping clean up and cancel the timer if it is already running and set it to null. This is really not needed here because of the lock and the fact that we only get to this point if BarDelay is OFF we should never be in a situation where an existing barTimer is running. I include the code here for informational purposes as in most cases it is something you will want/need to do if you donā€™t have these guards around the creation of the timer.

Now we create a Timer set to execute in 12 seconds from now with a body that sets BarDelay to OFF, reenabling the rule. Setting barTimer to null as the last line of the Timer body is again just some good Timer bookkeeping and not really needed here. NOTE: when you create a Timer, the lambda you pass to the Timer (i.e. the stuff between the [ ]) inherits the context that exists where it was created so it has access to all the same variables that were available to the rule when it was created.

Finally we catch any error that may have been thrown and unlock the lock. The use of try/catch/finally guarantees that when the rule exits the lock has been unlocked, even in the event of an error.

1 Like

Thanks Rich, you are the man. I know understands how OH handle rules and threads.

So I basically should add this to every single rule I have created right?

Yes, the file must have got corrupted or a bug in the designer, made it not loadā€¦

So how do you keep your files?

So basically what would be the best is :

You have two raspberry Pi(or any other device) that are samba to two different location on dropbox,google drive etc I would call them OpenHabInUse OpenHabDevelopement.

Then I would like OpenHabInUse to be a copy of the master branch on github and the OpenHabDevelopement to be a copy of developmentBranch on github.

So then if I now test this new rule on my development Pi and everything works fine, I can merge it to masterbranch on github and it should then autmatically be set in production.

Is this possible to achieve? The benefit of using github is that I can then add comments in the wiki, and everyone can get a copy of the code if they want to. I just need to figure out how to make the configuration.cfg hidden since this contains the password for emails, myopenhab etc. (Or make sure this is not synced to github somehow).

Locking is almost never needed. It is only needed in this case because there are some edge cases where if two instances of the rule run at the same time it could cause some unexpected behavior. In my thousand lines of rules code I use a lock like this exactly once. I would expect the same to be the case for you.

I have a somewhat involved process. I basically believe that the OH configuration is basically code so I keep it like code.

I created an openhab folder in my user account which contains all the directories that can be changed in OH: /etc/configurations, /usr/share/openhab/webapps. I symbolically link this folder to the appropriate location where OH reads them. This folder is configuration controlled using git and for backup I have a git server which I push changes to as well.

I have a VNC server running on this machine and I use Designer over VNC. When Iā€™m really remote (e.g. at work or otherwise not home) I either VPN into my home network or I tunnel VNC over an SSH connection.

One of the great advantages of using git (or any other source control) is if you mess something up you can very easily go back to the previous version. You can also do branching like you describe above.

I can see a couple of things that could make this approach challenging if at all possible. First Iā€™ll address your lines individually:

Why throw in the complications of DropBox and Google Drive? It seems to be adding complication with little real world benefit. It seems you should be able to just set up a local folder just keep the two in separate folders. If you are concerned about off site backup set a cron job to tar and drop your files into the DropBox or Google Drive folder. Or not even worry about it as you are already talking about checking your stuff into GitHub.

There is one major challenge which is for a lot of technologies (e.g. zwave) you canā€™t have two separate OH instances interacting with the same devices which limits how thoroughly you can test your new rules.

I think it is important at this point to step back and determine what risk you are trying to address with the Dev/Production split. While I agree it is a good approach and Iā€™ve seen it used all over the place in industry and used it myself, the problems that it addresses tend not to be problems for a single developer project with a relatively low impact caused by failure. You donā€™t have a bunch of developers working on separate parts of the system that need to be merged and tested. Itā€™s just you developing. If your openHAB doesnā€™t work quite right you will not be losing thousands or millions of dollars/euros/currency of choice an hour nor will lives be at risk. At worse you might have an annoyed spouse. So this coupled with challenges in being able to thoroughly test your dev instance of OH makes me really question whether the benefit justifies the added complexity.

I would modify your approach to instead have the one OH instance and utilize two branches in git. While you are actively working/testing you switch the one OH instance to use your dev branch. When it is time to stop for the day you switch it back to the Prod branch. You can make the switch easily by writing a simple script that swaps the symbolic links to your config folders between the two baselines and restarting OH (assuming OH doesnā€™t pick up all the changes automatically). You are already talking about using GitHub so your offsite backup is handled so I would avoid DropBox/Google Drive entirely. If your intent is to use DropBox/Google Drive to make it easier to edit your files with Designer on another machine, why not just clone your GitHub on that machine instead? Keeping everything in sync is a simple as a git pull and git push.

See gitignore for how to keep files from being checked in. However I caution you that there are usernames and passwords and secret keys all over the place (openhab.cfg, user.cfg, potentially in rules, potentially in items) so be very deliberate in what you check in. In particular not being able to check in openhab.cfg (or the OH 2 equivalent) is a pretty significant problem as there is a lot of work that goes into getting it right. Also consider whether excluding all of those files will result in enough of a working repo to make using something like GitHub worthwhile. This is the major reason I decided against GitHub and went with setting up my own git server instead.

Sorry, this is a late reply.

If you have arduinos, why not do all the logics in them ? and only react to the UP/DOWN from openhab ?

or iā€™m missing somthing ? :wink:

Well, [quote=ā€œRigor_M, post:10, topic:8216ā€]
If you have arduinos, why not do all the logics in them ? and only react to the UP/DOWN from openhab ?
[/quote]

The problem is if you press a button in openhab(Up/Down) the arduino controls a slow motor(12s) so lets say you press down. the motor starts going down. then for some reason you after 11s press up because you changed ur mind this will be ignored by arduino because it still has 1s to go and the display on ur android(OH) will then say up but it is actually down. Here is the arduino code.

However I could get my arduino to send feedback to OH about it status, and then use this to change state to the correct one. However I am a bit unsure how to do this, below is my try when the arduino sends feedback like:
ā€œBAR,WINE DOWNā€. I am not sure how to split strings and how to implement select case in rule files.

rule arduinoRule
when arduino changed
	then
	
	select split(ucase(arduino),",")(0)
	
	case "BAR"
		select split(ucase(arduino),",")(1)
		
		case "WINE DOWN"
			bar_wine.state = OFF
		case "WINE UP"
			bar_wine.state = ON
		else
		
		end select
	case "POWER"
	else
	
	end select
	
end

humā€¦ this is me but, I would put a multi-turn pot on the side of the motor so the arduino would always know where the motor is at. that way, no confusion if you change your mind mid way down or up :slight_smile:

Yes, feedback to a ā€˜stateā€™ value (dont know if youā€™re using MQTT to talk to the arduinos) would be you best bet IMO.

Using serial directly to the RPI, can I do hardwire with MQTT?

Unfortunately I dont have space for additional cables to have feedback. It is blind motors with endstop, so I use relays to run them. I run them for 1s after they reach the endstop, before i kill the power to them to avoid having the endswitch standing with power. Just a safety thing i like.

To split a String:

val String str = "The quick brown fox jumped over the lazy dog."
val strSplit = str.split(" ") // returns ArrayList of the string using " " as the split point

A select case is actually called a switch statement in OH Ruleā€™s DSL.

switch str {
    case "BAR": // bar code
    case "WINE DOWN": // wine down code
    case "WINE UP": // wine up code
    default: // default code, usually print error message
}

Unlike languages like C or Java, case statements do not flow down (i.e. if you hit the BAR case and do not have a break statement it will also execute WINE DOWNā€™s case statement).

Iā€™m also not certain you can have multi-line case statements (seems like you should be able to but all the examples Iā€™ve see are single line only. Iā€™d try it using { }.

Also, you do not assign a new state to Items, you have to postUpdate or sendCommand.

I would implement it something like the following:

val cmdSplit = arduino.state.split(",")
switch cmdSplit.get(0) {
    case "BAR": {
        if(cmdSplit.get(1) == "WINE DOWN") bar_wine.sendCommand(OFF)
        else if(cmdSplit.get(1) == "WINE UP") bar_wine.sendCommand(ON)
    }
    case "POWER":
    default:
}