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


#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).


#21

@WCmore I get lost in some of your more loquacious posts… :sweat_smile:

What (in pseudocode) is your suggestion in the end? Is it this?:

EVERY day at 4am 
    compute {alarmTime}
End EVERY

IF Time happens daily at {alarmTime}
    set {morningScene}
End IF

#22

Sorry… my words never seem able to do my brain justice. LOL
(Believe it or not, my posts here are greatly simplified)

Usually, I avoid answering complex question to new users, because there is so much basic webCoRE understanding that should come first… but somehow, I ignored that policy with you. :grin:


If you use EVERY blocks to set a time variable, then there needs to be a dummy block prior to the real event. If we reduce my Rocket Launch piston to the bare structure, we get this:

Every 12 hours
    Compute and Set variable {nextLaunch} to something
END EVERY

IF Time happens daily at ($hour24+1)":00"
    Then write to log
END IF

IF Date & Time happens daily at {nextLaunch}
    Then do cool stuff
END IF

The first block runs twice a day, gathering data and setting the {nextLaunch}.
(it ignores anything outside the EVERY block)

The second block runs hourly, and basically is the “Fake” timer, which updates the schedule.
(it runs top to bottom, ignoring the EVERY block)

The third block, is the real event.
(it also runs top to bottom, ignoring the EVERY block)


So the EVERY block is a great way to insert a “mini-piston” inside of another piston… as long as we keep in mind that each event only looks at part of the piston.


#23

Sorry, I should have elaborated…

One of the tricks in my repertoire is to place commands outside of any trigger blocks.
(meaning certain commands run at each execution… except for EVERY triggers)

This also lets me do some fancy juggling, since WAITS no longer need to check any preexisting conditions afterwards…

I can no longer do this when I drop the EVERY block.


As always with webCoRE, there are ways around this, but coding with or without an EVERY block is quite different.


#24

You could always add an ‘exit’ to make a particular timer act like an EVERY block but, of course, order of blocks would be important.

Agreed!