Updates to piston not taking effect: need to clone piston every time?


#1

1) Give a description of the problem
I make any edit to this piston, then click Save. The changes, although they do show up in the code, seem to be ignored when I execute it.

2) What is the expected behavior?
Any new data passed into the piston should reflect the newly-saved code

3) What is happening/not happening?
Only the logic of the very first version of the piston is followed. I found a workaround of cloning the piston, then making changes and saving. But this can’t be the best way to do this…

4) Post a Green Snapshot of the piston

5) Attach logs after turning logging level to Full
n/a


#2

I assume this is HE?

Two suggestions

Ensure you have the latest webcore (I assume you do, but just to be sure that the webcore.groovy and webcore-piston.groovy file are the latest).

I suggest doing a clean reboot of your HE

HE console -> settings -> reboot

And let it settle for a few mins after the reboot.

The piston code is stored in the piston.


#3

One other observation to add… (you may want to sit down for this one, LOL)

If any code changes from line 28-32…
This, alone, will NOT update the “Time happens daily at” sections.

For those to be updated, we have to follow the logic flow.

  1. First, IFTTT must execute ‘alarmTime’
  2. That execution will populate the variables up top

… at this point, it can be tricky… (webCoRE vs Hubitat)
#3 & 4 will be referring to webCoRE, which may, or may not, apply to HE…
(any HE pros please correct me on this next one)

  1. With webCoRE, IF a variable is used as a timer trigger, and IF it changes while the code is running, then it will use the previous data one more time. IE: at the end of that piston run, the wakeup timer will remain what it was previously.

  2. When that next wakeup happens, (at the old time), the piston runs top to bottom (as always), but this time, it sees and acknowledges the new changed variable times, and schedules the wakeup accordingly. (unless it has changed again during this execution)

It’s a bit strange flow, but it’s better to understand it, than code blindly.
(I have always suspected that the time is cached when the piston first loads, so any local changes are not seen until the next execution… but I have not dug thru the code to verify this hunch)

There have been many occasions where I have to program additional “fake” triggers prior to the real event… This “dummy” trigger is just so the piston is aware of the latest change, and can set the real timer at the appropriate time.


To see an example of a “fake” trigger, check out my “Rocket Launch” piston… (first post)

  • Lines 130-134 is dummy code, with the sole purpose of running prior to the real event
  • The real important Time trigger is on lines 135 onwards.

TL;DR:

With webCoRE, each time your variables on line 19-21 changes, you can expect one more incorrect trigger before it gets corrected.


#4

Sorry, yes: it is.

Hubitat Package manager says all up-to-date. So, good as gold?

Donezo

(EDITED) Gotcha. The pistons (HE console > Apps > webCoRE > [piston_name]) show the updates too…


#5

@WCmore

Wow, craziness. Is there a good way to avoid this? Like could I clear these values at the end of running the piston each time?


#6

Sarcastic answer: Don’t change the variables, :stuck_out_tongue_winking_eye:

Serious answer: Use a “Fake” trigger prior to the real event, as mentioned in my last post


#7

Ah! Now I’m pickin up what you’re puttin down. Does this seem good (see last few lines)?


#8

Perhaps a step in the right direction… but all the tips above still come into play.

IE: IF {alarmDigits} changes, then line 46 will not update until the piston runs once more using the previous data.

Long story short… The fake trigger cannot be tied AT ALL to any variable that changes.
This is literally some of the trickiest code to create, and really not fair to expect a new user to grasp it.

Heck, most pros don’t even realize this happens, and even less have workarounds for it.


#9

Amen! Because I’m a new user :raising_hand_man:, aaaaaand I feel like I have zero idea of what’s happening (I could barely follow your longer post).

So maybe let me ask you this way: what specific code should be there?


#10

To be entirely frank with you, for spontaneous calls like this… (meaning the timers could come in at any time) there is no efficient way to “guess” ahead of time when a trigger may come in. Which leaves the only option is to create some kind of repeat… all day… every day… but even that is not a guarantee, if your time comes in without much warning.

Basically, the loop would have to be tighter than the shortest warning you would ever give it.


I don’t know if you took at look at my Rocket example I linked to earlier, but let’s use that as an example. It automatically updates every 12 hours… BUT, when there is a rocket launch approaching, I have extra code in place so it starts checking more frequently… It continues that frequent polling, until the rocket launches… Then it returns back to twice a day.

Notice in this method, the Rocket launch is NOT spontaneous. We know about it hours in advance. So my extra refresh does not even begin until the final 4 hours.

So, let’s say that a launch update comes in with only a 15 minute warning…
My piston will MISS it every time.

The only way to catch an event 15 minutes in the future, is to spam the network every 14 minutes…
(which I do not want to do)

Does this make sense?


TL;DR:

If all of the timers will be in the far future,
then your coding (and stress to your SmartHome) will be reduced… :+1:

If any timer may ever be in the near future,
then your coding (and stress to your SmartHome) will be much more… :-1:


#11

One more example…

  • If you were creating an automated morning alarm…
  • and your alarm always went off sometime between 4 and 6am…
  • then you could program your “fake” timer at 3:55
    (which would refresh the wakeup time to the new time)

Notice how in this case, we have solid boundaries already set, so it makes the refresh time easy to choose. (IE: it would not trigger if the alarm was ever set for 3:30am)

You may find another pattern in your household that you can adopt into your code.


#12

Ooookay. I actually found a much simpler solution, with two different pistons (“Master” and “Slave”):

  1. Slave is by default paused
  2. When Master gets an update from IFTTT, it resumes Slave (and also sets up #5)
  3. Slave wakes up, schedules the scene, in the form of calling a URL endpoint (which is for some reason more stable than triggering a Rule directly)
  4. When the alarm time is reached, the scene triggers
  5. 30 seconds after alarm time is reached, Master pauses Slave again

Master:

Slave:


#13

Unfortunately, your “Solution” was a bit hasty, because you are still in the same boat…
The “master” will always get {alarmDigits} time wrong ONCE after it changes.


#14

I hear you but I’m not actually hitting that issue! I’ve tested many times and it seems to be getting it right every single time.

If you think I’m missing the failure mode here, can you give me a scenario to test?


#15

Oh no, by all means… If it ain’t broke… don’t fix it, LOL :grin:

The discrepancy could also have to do with the differences between webCoRE and HE, as well.


You really have me curious though… I am always trying to learn something new.

The part I am having a hard time wrapping my head around, is this scenario in the “master” piston:

For example:

At 1:00:00, an external device sends the time 1:05, and the variable {alarmDigits} changes
At 1:05:30, the last block executes

It doesn’t have to be those times, of course, but I would love to see this in a full log, if you are willing. (ideally, set your Log Level to Full and then Clear before beginning the test… and then do not touch anything until it has fully stopped)


Edit: Also, please turn on Trace and post the master green snapshot if you post that log.
(Trace adds more info to the JPEG)


#16

Sure thing! At 21:26:41, I set an alarm for 21:30:00.

Master

10/15/2020, 9:30:30 PM +80ms
+8ms ╔Received event [REDACTED_LOCATION].time = 1602822630000 with a delay of 80ms, canQueue: true, calledMyself: false
+25ms ║RunTime initialize > 24 LockT > 2ms > rtDT > 2ms > pistonT > 1ms (first state access 20 10 14)
+29ms ║Runtime (6702 bytes) successfully initialized in 2ms (v0.3.110.20201015_HE)
+32ms ║╔Execution stage started
+37ms ║║Comparison (string) null executes (string) alarmTime = false (2ms)
+39ms ║║Cancelling condition #2's schedules...
+42ms ║║Condition #2 evaluated false (7ms)
+43ms ║║Cancelling condition #1's schedules...
+44ms ║║Condition group #1 evaluated false (state changed) (9ms)
+52ms ║║Comparison (time) 77430000 happens_daily_at (time) 77400000 = true (0ms)
+53ms ║║Time restriction check passed
+54ms ║║Cancelling condition #10's schedules...
+55ms ║║Condition #10 evaluated true (8ms)
+56ms ║║Cancelling statement #10's schedules...
+62ms ║║Requesting time schedule wake up at Fri, Oct 16 2020 @ 9:30:30 PM PDT
+64ms ║║Cancelling condition #9's schedules...
+65ms ║║Condition group #9 evaluated true (state changed) (17ms)
+66ms ║║Cancelling statement #11's schedules...
+224ms ║║Executed virtual command pausePiston (155ms)
+230ms ║╚Execution stage complete. (197ms)
+264ms ║Setting up scheduled job for Fri, Oct 16 2020 @ 9:30:30 PM PDT (in 86400s)
+266ms ╚Event processed successfully (265ms)

10/15/2020, 9:26:41 PM +466ms
+6ms ╔Received event [REDACTED_LOCATION].ifttt.alarmTime = alarmTime with a delay of 67ms, canQueue: true, calledMyself: false
+48ms ║RunTime initialize > 47 LockT > 2ms > rtDT > 32ms > pistonT > 31ms (first state access 13 8 39)
+53ms ║Runtime (6651 bytes) successfully initialized in 32ms (v0.3.110.20201015_HE)
+55ms ║╔Execution stage started
+65ms ║║Comparison (string) alarmTime executes (string) alarmTime = true (2ms)
+68ms ║║Cancelling condition #2's schedules...
+71ms ║║Condition #2 evaluated true (11ms)
+73ms ║║Cancelling condition #1's schedules...
+75ms ║║Condition group #1 evaluated true (state changed) (15ms)
+77ms ║║Cancelling statement #3's schedules...
+89ms ║║Executed virtual command setVariable (3ms)
+99ms ║║Executed virtual command setVariable (2ms)
+105ms ║║Executed virtual command setVariable (2ms)
+289ms ║║Executed virtual command resumePiston (182ms)
+508ms ║║Executed virtual command executePiston (214ms)
+518ms ║║Comparison (time) 77201000 happens_daily_at (time) 77400000 = false (1ms)
+520ms ║║Condition #10 evaluated false (6ms)
+521ms ║║Cancelling statement #10's schedules...
+528ms ║║Requesting time schedule wake up at Thu, Oct 15 2020 @ 9:30:30 PM PDT
+530ms ║║Condition group #9 evaluated false (state did not change) (16ms)
+536ms ║╚Execution stage complete. (482ms)
+564ms ║Setting up scheduled job for Thu, Oct 15 2020 @ 9:30:30 PM PDT (in 228s)
+566ms ╚Event processed successfully (564ms)


Slave

10/15/2020, 9:30:30 PM +209ms
+46ms ╔Stopping piston...
+66ms ╚Piston successfully stopped (20ms)

10/15/2020, 9:30:02 PM +311ms
+17ms ╔Received event [REDACTED_LOCATION].wc_async_reply = httpRequest with a delay of 1ms, canQueue: true, calledMyself: false
+75ms ║RunTime initialize > 74 LockT > 2ms > rtDT > 2ms > pistonT > 1ms (first state access 70 19 55)
+95ms ║Runtime (5769 bytes) successfully initialized in 2ms (v0.3.110.20201015_HE)
+177ms ║╔Execution stage started
+268ms ║╚Execution stage complete. (72ms)
+1097ms ║Setting up scheduled job for Fri, Oct 16 2020 @ 9:30:00 PM PDT (in 86397s)
+1099ms ╚Event processed successfully (1097ms)

10/15/2020, 9:29:59 PM +941ms
+7ms ╔Received event [REDACTED_LOCATION].time = 1602822600000 with a delay of -59ms, canQueue: true, calledMyself: false
+21ms ║RunTime initialize > 20 LockT > 1ms > rtDT > 2ms > pistonT > 1ms (first state access 17 8 12)
+25ms ║Runtime (5896 bytes) successfully initialized in 2ms (v0.3.110.20201015_HE)
+29ms ║Synchronizing scheduled event, waiting for 31ms
+62ms ║╔Execution stage started
+64ms ║║Cancelling statement #56's schedules...
+73ms ║║Executed virtual command setVariable (2ms)
+83ms ║║Comparison (time) 77400000 happens_daily_at (time) 77400000 = true (1ms)
+84ms ║║Time restriction check passed
+86ms ║║Cancelling condition #13's schedules...
+88ms ║║Condition #13 evaluated true (10ms)
+89ms ║║Cancelling statement #13's schedules...
+97ms ║║Requesting time schedule wake up at Fri, Oct 16 2020 @ 9:30:00 PM PDT
+99ms ║║Cancelling condition #12's schedules...
+101ms ║║Condition group #12 evaluated true (state changed) (24ms)
+103ms ║║Cancelling statement #47's schedules...
+113ms ║║Sending asynchttpGet web request to: cloud.hubitat.com/api/[REDACTED_API]/apps/114/trigger?access_token=[REDACTED_TOKEN]
+117ms ║║Executed virtual command httpRequest (6ms)
+120ms ║║Requesting a wake up for Thu, Oct 15 2020 @ 9:30:24 PM PDT (in 24s)
+128ms ║╚Execution stage complete. (66ms)
+175ms ║Setting up scheduled job for Thu, Oct 15 2020 @ 9:30:24 PM PDT (in 24s), with 1 more job pending
+178ms ╚Event processed successfully (176ms)

10/15/2020, 9:26:41 PM +813ms
+5ms ╔Received event [REDACTED_LOCATION].execute = :[REDACTED_PISTON]: with a delay of 3ms, canQueue: false, calledMyself: false
+30ms ║RunTime initialize > 29 LockT > 1ms > rtDT > 16ms > pistonT > 15ms (first state access 12 6 23)
+33ms ║Runtime (5731 bytes) successfully initialized in 16ms (v0.3.110.20201015_HE)
+34ms ║╔Execution stage started
+37ms ║║Cancelling statement #56's schedules...
+43ms ║║Executed virtual command setVariable (3ms)
+49ms ║║Comparison (time) 77201000 happens_daily_at (time) 77400000 = false (0ms)
+51ms ║║Condition #13 evaluated false (4ms)
+52ms ║║Cancelling statement #13's schedules...
+59ms ║║Requesting time schedule wake up at Thu, Oct 15 2020 @ 9:30:00 PM PDT
+61ms ║║Condition group #12 evaluated false (state did not change) (15ms)
+70ms ║╚Execution stage complete. (35ms)
+97ms ║Setting up scheduled job for Thu, Oct 15 2020 @ 9:30:00 PM PDT (in 198s)
+99ms ╚Event processed successfully (97ms)

10/15/2020, 9:26:41 PM +617ms
+31ms ╔Subscribing to devices...
+48ms ╚Finished subscribing (20ms)
+62ms ║Comparison (time) 77201000 happens_daily_at (time) 65040000 = false (0ms)
+67ms ║Cancelling statement #13's schedules...
+82ms ║Requesting time schedule wake up at Fri, Oct 16 2020 @ 6:04:00 PM PDT
+110ms ║Setting up scheduled job for Fri, Oct 16 2020 @ 6:04:00 PM PDT (in 74238s)
+114ms ╔Starting piston... (v0.3.110.20201015_HE)
+115ms ╚Piston successfully started (94ms)

For whatever reason, there is no trace for Slave. Although I see you didn’t request it.


#17

Thanks!

While I examine, can you say with 100% confidence that the previous {alarmDigits} was something other than 9:30?


#18

For sure. It was some time a few hours earlier, maybe 6 something PM, while I was testing.


#19

Hey thanks for your patience… I have learned something new today. :grin:
I was able to successfully replicate your test… and three variations of.

The one that failed, is one of my favorite code blocks: :frowning_face:

Every X 
    do data gathering stuff & set {timer}
End every

IF Time happens daily at {timer}
    Then do cool stuff
END IF

This layout is awesome as long as I force in a “fake” trigger prior to the real {timer}.

So while I really like the compartmentalization of the EVERY block, it does not look outside it’s own bounds, (for better or for worse)… Nor will it refresh the IF’s new wakeup time. (as mentioned in all my earlier ramblings, LOL)

Switching over to “IF Time happens daily at” (as you have done), we get the benefit of a faster update… but we get a penalty of only one run per day (without external influence). I tend to favor the “Every 6 hours” etc, because I can tweak the frequency better…


Which reminds me of a recent discovery…
I think credit goes to @guxdude for this one…

pic

The yellow digits can be between 00-59, and the blue number, we tested 1-4 I think.
(Hey @guxdude, how far did we push that overlap?)

Anyways, that example will trigger every hour… or can be changed to +4 to have it run every 4 hrs.

This way you can get multiple runs per day in an IF block.
The sacrifice though is that the piston will run top to bottom
(EVERY blocks will only execute that one block… I will miss this feature)


So with all this info combined, I think I will add a couple of words to my earlier statement (in green):

So many variations and things to remember… LOL


Oh… and I guess another benefit of this method is you can go back to a single piston, and drop the whole pause/resume fiasco, :sunglasses:


#20

I believe I once tested up to 6 hours when I crated a variation of the launch gauge that varied run time depending on how far away a launch was. It worked fine but I ended up going back to every hour since we saw pop-up launches that it would miss.

One further note regarding compartmentalization. if you create multiple timers, it will keep them all separate:

if time happens daily at {($hour24+1) ":00"} then
   do stuff and set {timer}
end if
if time happens daily at {timer}
   do more stuff
end if

When running the first ‘if’, the second ‘if’ time will be updated immediately but the code won’t run (unless maybe if timer is $now). Not exactly the same but I use this compartmentalization effectively in my pool pump control (I want it to run at night if there is a chance of freezing and during the day otherwise but also modify the start time based on how hot it will be for other reasons).