Only allow switch to be pressed every 30 seconds?

is there a way to prevent a switch from being pressed (or from sending command if pressed) more than every 30 seconds? i have an alarm sensor and z-wave appliance module set up to open/close my garage door, and i’d like to prevent it from being pressed more than once every 30 seconds or so to give the door a chance to open and the GUI a chance to update (to prevent the z-wave command from being sent multiple times too quickly and end up with the door halfway open)…

i’ve been tinkering with rules and timers to make this happen, but haven’t been able to get anything to work so far.

First of all, from a safety perspective this sounds like a bad idea. There can be some scenarios where being able to “cancel” your previous button press would be important from a safety perspective.

This sounds like a job for Proxy Items:

Create a Proxy Switch which is what you send commands to and put on your sitemap. However, this Item is not bound to anything. Sending commands to this switch triggers a rule.

The rule will check to see if at least 30 seconds have passed since the last time it was sent a command and if so it will send the command to the “real” Switch which is connected to the opener.

import org.joda.time.*

val DateTime lastPressed = null

rule "garage proxy"
when
    Item GarageProxy received command
then
    // If you have persistence
    if(GarageProxy.lastUpdate.after(now.minusSeconds(30).toDate)) Garage.sendCommand(ON)

    // If not you need the import and val above
    if(lastPressed.isAfter(now.minusSeconds(30)){
        lastPressed = now
        Garage.sendCommand(ON)
    }
end

how exactly do i create a proxy switch in the sitemap? here’s what i have so far, and it works exactly as i want it to (except that if i press the button to close the door and then press it again before the sensor has time to update, i end up with the door stopping halfway closed, which is what i’m trying to avoid with the 30 second lockout)…

items:

Contact z04GarageDoorTripped “z04: Garage Door Tripped [MAP(en.map):%s]” (GDevices,GRoom2) {mios=“unit:xxx,device:34/service/SecuritySensor1/Tripped”}

Switch GarageDoorStatus “Garage Door Status” (GDevices,GRoom8) {mios=“unit:xxx,device:48/service/DoorLock1/Status”}

sitemap:

  Group label="Garage" icon="garage" {
	Switch item=GarageDoorStatus label="Garage Door" icon="garagedoor-closed" mappings=[OFF=Open] visibility=[z04GarageDoorTripped=="CLOSED"]
	Switch item=GarageDoorStatus label="Garage Door" icon="garagedoor-open" mappings=[ON=Close] visibility=[z04GarageDoorTripped=="OPEN"]
  }

actually i take that back, now my switch isn’t working at all after i tried tinkering to get a proxy to work. ugh. i’m a programmer by day and getting openhab set up and running has been one of the most frustrating things i’ve ever tried to do…

alright, i have the proxy switch set up now…but persistence doesn’t seem to be working. i have mysql persistence set up and there’s data in the table, but GarageProxy.lastUpdate is returning null every time…

alright, making progress…got persistence working. however, it’s always returning the current time (because it’s counting the switch press as the lastUpdate). i.e. i press the switch, it logs that into the db for persistence, and then returns that current time…so the if(GarageProxy.lastUpdate.after(now.minusSeconds(30).toDate)) is always returning true. i’m never able to get the last time prior to that…

Hmmmm. That is weird behavior. I’ve seen that with MapDB but figured it was caused by weirdness in MapDB. Maybe it works that way in MySQL as well. Grrrr.

At this point we can try to figure out a way around it or go with the non-persistence solution I included above.

I just saw this solution in another thread which should work for this as well.

import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantLock

var Lock lock = new ReentrantLock()

rule "garage proxy"
when
    Item Garage received command
then
    // tryLock will acquire the lock if it is available, and fail fast otherwise.
    if (lock.tryLock()) {
        Garage.sendCommand(ON)
        Thread::sleep(30000)
        lock.unlock()
    } else {
        logInfo("Garage", "Garage button currently disabled")
    }
end

This uses a Reentrant Lock to disable the rule. A Rule triggers in one thread, grabs the lock, sends the command to the “real” garage switch, sleeps for 30 seconds and gives up the lock. Any subsequent rule triggers while the first one is sleeping will simply jump to the else clause.

alright, i think i have it figured out (and seems to be working exactly how i want it to). had to change the rule to check lastUpdate on the actual garage door switch, not the proxy. also changed the delay to 15 seconds, after timing my garage door i felt that was a better safety time than 30 seconds…

rule "GarageDoor"
when
	Item GarageDoorStatus_proxy received command
then	
	logInfo("GarageDoor", receivedCommand.toString())
	val safetyDelay = 15
	
	if(GarageDoorStatus.lastUpdate.before(now.minusSeconds(safetyDelay).toDate)) {
		GarageDoorStatus.sendCommand(receivedCommand)
		logInfo("GarageDoor", "allowed garage door switch press")
	}
	else {
		logInfo("GarageDoor", "disallowed garage door switch press (wait for door to finish opening/closing)")
	}
end