Variable Timer w/ Simulated Buttons


#1

I need a piston that has a variable timer. Within smart things I have 3 simulated buttons with a different number of minutes for each one.

Essentially there is a primary switch that needs to stay on for a varied amount of time, based on button input. Time starts at zero and turns on for X number of minutes when simulated button 1 is pushed, turns off when time expires. If button one is pushed again it resets the time to the predetermined amount. If button 2 is pressed it will increase the time by its amount or ignore it completely if there is already that amount or more on the time.

For example:
Button 1: 10 Minute
Button 2: 20 Minutes
Button 3: 30 Minutes

Button 3 is pushed, device turns on sets to 30 minutes.

3 Minutes Later Button 2 is pushed, but since the timer still has more than 20 minutes on it, push is ignored.

25 Minutes Later Button 1 is pushed, since time is below 10 minutes, timer is reset to 10 minutes.

If timer reaches zero device turns off.


#2

The problem is webCoRE can do a wait for a specified time but there is no implementation of a timer that I am aware of. So no way to know how much time is left on the timer and interrupting a wait can be confusing/difficult. This seems like it would be very difficult to implement. Perhaps with some variables storing the off time and resetting wait times it could be done but not straightforward.


#3

As @guxdude suggested no straight way to do it (At least to my knowledge)
but there is always a way…

A simple draft example would be something like this… (not home so can’t test this) this is just to give you an idea…

Button 1 = 10 minutes
Button 2 = 20 minutes

IF button 1 is pressed (or changes to on etc)
And 
IF variable button2 is false
Set variable button 1 = True
wait 10 minutes
Set variable button 1 = false

something like this… You will need to create if blocks for all different variations you want…

I don’t have a good grip on expressions… so I don’t know if this can be done thru expressions…


#4

I think to implement what you described, you would need to set a variable to keep track of your current end time and set a wait for the appropriate interval (currentEndTime-$now). Each time a button is pressed, you would need to check if end time is in the future and by how many minutes. Then reset the end time to the greater of current end time or $now+X minutes where x is defined by the button pressed. You need to cancel any existing wait and set another wait to get to the new end time.

As @ike2018 pointed out, there is almost always a way but it can get increasingly complex.


#5

100% agree…Due to complexity of this I personally would never use such piston…


#6

For whatever reason, this question fascinated me, so here’s my attempt. I have one global variable that gets changed by two pistons. The first piston monitors the buttons and decides if the button is permitted to be pressed. The second counts down in 10 minute intervals (I’ve got it set for seconds at the moment for testing), then updates the global variable and restarts itself.

If you press a key, and that key’s timer value is equal to or greater than that of the global variable, the global variable is updated and the timer piston is restarted. If the key’s timer value is less, the keypress is ignored.

Some additional thoughts:


#7

Untested, but something tells me that the following:

pic

will execute the other piston first
… and then it will update the global.
(meaning azchipka might see the previous number)


#8

@WCmore It’s recursive - It should be calling itself. The only reason I did that instead of a while loop is to force the update of the global. It looked like it should be working, at least by my interpretation of @azchipka’s post. Also, the “10 seconds” would need to be changed to “10 minutes”. Also, it might be off by a few seconds, as the piston has to stop and restart every 10 minutes.


#9

I apologize for going off on a tangent, but I need to correct my earlier statement:

I just ran a few tests, and I am surprised at my findings… This specific case was the first time I have ever seen a global variable get updated BEFORE the last line of code executed. (or maybe it just takes a moment for the second piston to begin)

I created two tiny pistons with basically the same code as above… One piston added 1 to the global, and the other piston added 10 to the global. (Each piston waits 14 seconds, then sets the global, and executes the other piston… back and forth)

I let it run 6 full cycles (12 executions), and the global was correctly passed each time.


Below is the logs from both pistons… +1 on the left, and +10 on the right.

To read this sequentially, go:
Bottom Left, Bottom Right, then up a line and repeat


This observation opens up many doors for me and my future coding!!!

… and of course, me pushing the envelope furthur…
I am especially curious if it will correctly update twenty globals before executing the second piston…
(yes, I am thinking of you, twc.weather)


Circadian Schedule - Piston Skipping Lights
Ramp light levels based on twilight and mode
#10

Apologies for continuing the tangent …

My interpretation of the webCoRE piston code is that a piston takes a copy of the global variables when it starts up, works with that local copy saving changes to a cache, and then writes that cache on exit. So in effect it doesn’t write the global variable back until after the last line of code is executed, just as you said.

I’ve been doing similar tests. I am sort of methodical but rather impatient, so here is what I think I see:

  1. Add a three second wait to the end of the +1 piston.
    I think you will still see the global going up by 1 or 10 as you describe.
  2. Change the fourteen second wait in the +10 piston to three seconds.
    I think you will only see the global incrementing by 10 (that is from the viewpoint of the pistons, outside them ‘webCoRE’ will probably see 1, 10, 11, 20, 21, 30 etc).

Explanations:

  1. If the last thing the +1 piston does is execute a piston it can probably tidy things up in the time it takes the +10 piston to respond to the execute event (executing a piston uses a location event). So sticking a three second delay on the end makes the test more interesting. I believe the +10 piston will not see the +1 increment when it starts but because you are using a 14 second wait it is sleeping and waking up again. When it wakes up it is a new instance so it refreshes the global variables again and picks up the +1 value.
  2. All things being well, the +10 piston will always start within the three second delay added to the +1 piston. So it doesn’t see any change made by the +1 piston. Because it is doing a short wait it doesn’t sleep and wake up again so it never refreshes the global variables.

A less impatient person than me would thoroughly test this by logging the global variable before and after the waits and messing around with various wait lengths.

I think it would be easy to find yourself flirting with race conditions, and indeed that sort of brings the tangent back into the thread.


#11

Unfortunately, no. Adding a 3 sec WAIT after the “Execute piston” breaks the rare exception above… As seen below:

pic

This error is normal, since the global is not written until the very last line has executed. I would also expect a few normal commands at the end (instead of the extra WAIT) will also break it.

Well, I shouldn’t say “break”, since this is the expected behavior.


This one also proved to be false. Reducing the WAIT on the +10 piston to 3 seconds successfully passes the global back and forth.

pic


I think I am to blame for your conclusions though… I should have mentioned that I am writing to the log in the very first command… before the WAIT has even begun.

pic

(I want to know what global is visible at the moment of execution)


I have found that only two WAIT lengths are important for testing: Under 8 secs, and over 12 secs.
(both with very different code interactions)
The 8-12 second range can go either way, so I typically avoid using…


Conclusions:

This exception of globals being written before the last line has executed is extremely rare, and can only be used in very specific circumstances.

IE: @ian_boje’s “bh63” piston above will break if he adds anything else to the end of the THEN block.


#12

OK, in my tests I wrote out the values before and after the wait, but quoted the latter. Also to clarify, my second test had both the extra three seconds on the +1 and the reduced wait on the +10.

I am just suggesting that there isn’t actually any exception. It is just that there is latency in executing the piston that means the first piston has finished executing and updated the globals before the second one has taken a copy of them.

The implications are the same. In the quoted piston, any additional statements in the THEN block or after the ENDIF might cause it to fail. I don’t know the size of the margins being played with. It certainly isn’t a mechanism I’d ever want to rely on.

However It should also be noted that pistons waking up after a long ‘wait’ retrieve new copies of the global variables. In this case a ‘long’ wait is any of more than five seconds, and sometimes less.