Sorry for the long delay; busy week. I've been playing around with pause/wait/delay. I've discovered that the eventing model is not at all what I expected.
Inspired by the thread @bertabcd1234 referred to above, I tried to come up with a concurrent approach for hysteresis (that is, "run once and then hold off for a while.") In this case, the intent is to run once in a particular time period. I'm using a pretty short period (couple minutes or less) for testing.
Here's the baseline rule, using time variables. This works, but it's ugly. It's also going to be vulnerable to the midnight boundary (across which a time a few minutes in the future becomes 24 hours in the past) and run twice, which isn't ideal. [ed: this is why most programming languages provide timestamps, which aren't vulnerable to those oddities.]
I've tested it and sure enough, it basically works. But it's icky, so I decided to try to use the RM pause/wait/delay primitives directly.
This experiment was pretty informative, showing that things don't work at all as I expected.
Here I'm setting a boolean with a delay. My expectation was that everything would stop until that delay completed (since the rule is sitting on its hands, waiting for the timer to fire). But that's not what happened at all. The logs show a very interesting interaction (remember, read from bottom to top):
07:46:55.447 pm info Delay Over: Set ready to true --> delayed: 0:00:45
[ten seconds later!]
07:46:45.859 pm info Action: END-IF
07:46:45.856 pm info Action: [stuff skipped here]
07:46:45.841 pm info Action: IF (Variable ready(false) = true(F) [FALSE]) THEN (skipping)
07:46:45.734 pm info ActOnce Triggered
07:46:45.522 pm info Office Motion Sensor is active
07:46:34.506 pm info Office Motion Sensor is inactive
07:46:11.063 pm info Deskside lights is on [digital]
07:46:10.274 pm info Action: END-IF
07:46:10.224 pm info Action: Set ready to true --> delayed: 0:00:45
07:46:10.184 pm info Action: On: Deskside lights
07:46:10.153 pm info Action: Set ready to false
07:46:10.147 pm info Action: IF (Variable ready(true) = true(T) [TRUE]) THEN
07:46:09.950 pm info ActOnce Triggered
07:46:09.702 pm info Office Motion Sensor is active
So that's pretty fascinating. Each event gets its own distinct instance of the rule. So all of the wait/delay/pause primitives will operate on a unique chunk of running code - there's no underlying thread with which we can interact! So this does works, but not at all in the way I expected. I thought I could just wait; instead, I have to set the guard, and then remove it after the delay.
Still, a useful approach, so I thought I'd share.