How to properly handle duplicate events?


#2

Are you seeing in the ST IDE or in the button’s recent tab in the ST app that the button is actually registering two presses? You also don’t post your piston. It might be in the way you’re looking for the button press.


#3

Hi - No I am going by the Debug log console in the piston itself. It shows it is called twice for just a single button press.


#4

So, you’re assuming that the ST is only recognizing the one press as actually one press. It could be a problem with the button. You also haven’t posted a copy of your piston.


#5

I agree, resolving the buttons double commands sounds like the first step…

That being said, the following piston will make sure that webCoRE ignores the second press.

Switch 1 should point to your Button 7 “is pressed”
Dimmer 1 should point to your LIFX


You can add to this code, but just make sure that the very first command remains "Set variable {running} = true"… and make sure the very last two steps remain "Wait 1 second" and "Set variable {running} = false"


#6

Thanks! I’ll post the full piston when I have time to simplify it to just the reveant part (the piston handles more than just the button press). BTW, I believe in the Enerwave Scene Controller thread on ST Community when I asked about the double button press there a year or so ago, I was told it was a known issue and I don’t think it was ever fixed).

I understand what your code is doing - thanks. A few questions please if you don’t mind:

  1. Why the need for the the Wait 1 Seconds?

  2. I assume this piston should (or is ok to) run in parallel mode instead of serial? Otherwise it you press the button and then change your mind and purposely price it twice, then a semaphore makes the piston wait 10 seconds before it’ll response to the second button press.

  3. Isn’t it possible (perhaps likely at some point) that a race condition could occur and this trick not work in that case? Any way to set a serialization lock of some sort around the setting and checking of the variable?


#7

You might have success without the wait, but I wanted to give the commands a moment to finish up before resetting the variable. (I would definitely keep it at least 300ms though, based on your current issue, or it may run twice)


I am not for sure on this one. (It would be easy to test though if you import & test the piston above before adding it into your complex piston.) I usually have parallelism disabled, which means a second press would be ignored until the 1 second Wait has expired.


I have no idea what you are asking here. Can you re-phrase and/or give examples?


#8

Thanks. Technically speaking, if the approach was to work, then the Wait shouldn’t be necessary, because if it got called a 2nd time “running” would still be true had it not finished yet. So I do not see the need for it, but will test it both ways.

Sure. Its possible that the execution of two threads simultaneously in the same piston could result in both of them getting to the “and If Not Running” before the other set Running to True. I think its very unlikely though, and in that case I’m not worse off than I am already where it already runs twice. :slight_smile:

I’m not sure if this is default webCore behavior when the piston runs in the default Serial mode, however I found that when the piston was already running, a second button even would call the piston the 2nd time but then it would wait on a semaphore there for 10000 ms before running. At that point my logic would think that the user legitimately pressed the button a second time and then toggle the light again. That’s why I change it to parallel. That way it doesn’t wait and both events trigger within 20 ms of each other, and it gives my logic the ability to say “and it hasn’t been 1500 ms since the last toggle, then toggle”. I’m pretty sure if I set it back to Serial your logic would not work either because it would have the same issue - Running would be false the second time always because it would be 10000 ms after the last one was run. So the Running logic would not work with Serial.


#9

Paralellism should be disabled. Because if it’s not then you get two copies of the piston running at the same time. So, if the piston does actions 1-10 and gets reexecuted while on step 5, you’ll the have steps 6-10 running at the same time as 1-10 the second time. But this only impacts pistons with waits or TONS of actions. If your actions all happen immediately, you’re not going to be able to time it right to get in the middle of the execution. Because by the time the dashboard is updated the piston is done. So, you only ever have one copy running at a time anyway. So, even with a 1 second wait, Paralellism enabled or disabled probably won’t matter.


#10

While technically, this may be true, in actuality, webCoRE sending the command will take a moment to reach ST, and depending on your bulbs, another moment for the bulbs to trigger. (in other words, just because the code finished running, does not mean the bulbs are in the new state at that precise millisecond)

Feel free to do whatever you like, but I would not expect 100% reliability with a wait less than 200ms.


Yes, if the double press registers in the split second before the first command writes to the variable, you may get two, but only one should get past that and actually toggle the lights. It might be worth keeping this double button in it’s own piston, instead of merging it within a larger piston.


For the record, the sample piston I have posted above is run meant to be run standard, with parallelism disabled.


And again, as a friendly reminder, this is a workaround… Ideally, resolving the buttons double commands sounds like the first step…


#11

And if the code is done, a second press would still be considered separate. Without the wait the piston is executed in a matter of milliseconds (in an ideal world). But I would still like to understand why the press is being counted twice. All this workaround stuff might not be necessary.


#12

Thanks guys. Here is a post I made in June 2017 asking about the double events that come in on the button press: https://community.smartthings.com/t/zwn-sc7-enerwave-7-button-scene-controller/2969/720?u=hdguy (see item A in that post).

The Community Master Eric responded and said it was a known issue. I had no idea how to go about solving it and no one else seemed to know or maintain this DTH, so the easiest thing to do was to add code to detect the duplicate press event and ignore it. As it turns out, the workaround wasn’t as straight-forward as I thought it would be. :slight_smile:

Does that info help at all? If it’s indeed a known issue with that DTH, it doesn’t seem likely that I’d be able to find the root cause and fix it properly. Hence the questions about the workaround. Thanks!


#13

Yeah, a DH 2.5 years old, with no updates sounds abandoned. Have you reached out to the author of the DH? (you may find his personal website/contact info written in the top section of his DH code)

Even if it is abandoned, I would like to think that someone took the time to update and improve the code. Surely you are not the only person with that device or issue…

One idea is trying to search Google using only four keywords…
The model number plus the words ‘device handler double’
(without any quotes)

Also, it is possible that there is an alternative DH (by a different author) that does not have this issue.


Dog Door Tracking - Restricting Triggers
#14

Yes I have been down that road - went no where. At a certain point I figured a workaround was far easier.

Here’s a minimalistic version of the piston:

Recall that if I make it run with the default Serial mode, then the issue is that with the duplicate even the piston waits at the top for 10000ms before running. Therefore the check to see if its been 1700ms becomes worthless because it will have been about 10s, due to the semaphore blocking. By making it parallel the 1750ms check should work but it doesn’t, because the set variable on it from the first piston hasn’t been set yet, so both times the piston runs it thinks its the same event. So it runs twice basically at the same time. However it is working this way. I think because the back to back LIFX toggles are too fast for LIFX so it essentially ignores the second call from the 2nd piston.

What’s also odd is that since making it parallel, the piston sometimes runs only once (no duplicate event). I’d say it gets the duplicate maybe one out of 5 times. Whereas with serial it gets it 5/5 times.

So essentially the way I have it now, before even implementing the Running flag, is working. But only because the immediately back to back LIFX toggle commands can’t be processed so essentially it winds up ignoring the 2nd one. I’d still like to clean it up, but it probably isn’t worth more of anyone’s time at this point.

BTW I don’t think the Running flag trick will work either - for the same reason that the set Variable from the 1st piston is not seen by the 2nd piston (probably because it is running two quick) so the Running flag would likely have the same issue.


#15

Ugg, all this time I have spent trying to help, and you haven’t even tested my first suggestion…

Sorry, I do not have that device, so any testing has to be done on your end.
It takes 30 seconds to import “z6mo” and pause your current piston to test.


#16

Thanks. Yes I’m going to test it shortly. Haven’t had much time to experiment these past few days. Will let you know soon! But as mentioned, I’m predicting that Running will have the exact same issue that the flag I’m using has (the 2nd running piston won’t see it as set to True in time). Anyway let’s see what happens.


#17

Thanks. Just to narrow it down a bit, please only turn on one light when testing. We can always ‘complexify’ it at a later time…


#18

OK I did some testing with your piston. It is as you created, except the only difference is that I kept it calling the LIFX toggle command instead of switching a single light. I paused my existing piston and then turned debugging on for your test one.

I pressed button 7 once. The light turned off, then 10 seconds later turned on. This is exactly the original behavior I had, and as predicted yesterday, Running flag does not work because of the semaphore.

Here’s the debug log after pressing button 7 just one:

10/8/2018, 12:51:47 PM +76ms
+1ms ╔Received event [Home].time = 1539017505134 with a delay of 1942ms
+101ms ║RunTime Analysis CS > 21ms > PS > 61ms > PE > 18ms > CE
+104ms ║Runtime (40225 bytes) successfully initialized in 61ms (v0.3.108.20180906) (101ms)
+105ms ║╔Execution stage started
+136ms ║║Executed virtual command setVariable (4ms)
+140ms ║╚Execution stage complete. (36ms)
+143ms ╚Event processed successfully (142ms)
10/8/2018, 12:51:31 PM +295ms
+1ms ╔Received event [ZWN-SC7 Enerwave 7 Button Scene Controller].button = pushed with a delay of 727ms
+10100ms ║RunTime Analysis CS > 18ms > PS > 10072ms > PE > 11ms > CE
+10101ms ║Piston waited at a semaphore for 10038ms
+10104ms ║Runtime (40338 bytes) successfully initialized in 10072ms (v0.3.108.20180906) (10101ms)
+10105ms ║╔Execution stage started
+10118ms ║║Comparison (enum) pushed gets (string) pushed = true (2ms)
+10120ms ║║Condition #2 evaluated true (8ms)
+10125ms ║║Comparison (boolean) false is_not (boolean) true = true (2ms)
+10127ms ║║Condition #3 evaluated true (6ms)
+10128ms ║║Condition group #1 evaluated true (state did not change) (18ms)
+10131ms ║║Cancelling statement #9’s schedules…
+10138ms ║║Executed virtual command setVariable (3ms)
+12828ms ║║Executed virtual command lifxToggle (2687ms)
+12832ms ║║Cancelling statement #10’s schedules…
+12837ms ║║Executed virtual command wait (0ms)
+12838ms ║║Requesting a wake up for Mon, Oct 8 2018 @ 12:51:45 PM EDT (in 1.0s)
+12843ms ║╚Execution stage complete. (2739ms)
+12845ms ║Setting up scheduled job for Mon, Oct 8 2018 @ 12:51:45 PM EDT (in 1s)
+12872ms ╚Event processed successfully (12872ms)
10/8/2018, 12:51:31 PM +175ms
+1ms ╔Received event [ZWN-SC7 Enerwave 7 Button Scene Controller].button = pushed with a delay of 609ms
+10178ms ║RunTime Analysis CS > 17ms > PS > 10148ms > PE > 14ms > CE
+10179ms ║Piston waited at a semaphore for 10097ms
+10182ms ║Runtime (40338 bytes) successfully initialized in 10148ms (v0.3.108.20180906) (10179ms)
+10183ms ║╔Execution stage started
+10196ms ║║Comparison (enum) pushed gets (string) pushed = true (4ms)
+10198ms ║║Condition #2 evaluated true (8ms)
+10204ms ║║Comparison (boolean) false is_not (boolean) true = true (2ms)
+10206ms ║║Condition #3 evaluated true (6ms)
+10207ms ║║Condition group #1 evaluated true (state did not change) (19ms)
+10210ms ║║Cancelling statement #9’s schedules…
+10221ms ║║Executed virtual command setVariable (5ms)
+12141ms ║║Executed virtual command lifxToggle (1915ms)
+12145ms ║║Cancelling statement #10’s schedules…
+12152ms ║║Executed virtual command wait (1ms)
+12154ms ║║Requesting a wake up for Mon, Oct 8 2018 @ 12:51:44 PM EDT (in 1.0s)
+12159ms ║╚Execution stage complete. (1977ms)
+12161ms ║Setting up scheduled job for Mon, Oct 8 2018 @ 12:51:44 PM EDT (in 1s)
+12170ms ╚Event processed successfully (12170ms)
10/8/2018, 12:51:31 PM +144ms
+1ms ╔Received event [ZWN-SC7 Enerwave 7 Button Scene Controller].button = pushed with a delay of 565ms
+233ms ║RunTime Analysis CS > 17ms > PS > 205ms > PE > 12ms > CE
+236ms ║Runtime (40265 bytes) successfully initialized in 205ms (v0.3.108.20180906) (233ms)
+237ms ║╔Execution stage started
+250ms ║║Comparison (enum) pushed gets (string) pushed = true (3ms)
+252ms ║║Cancelling condition #2’s schedules…
+253ms ║║Condition #2 evaluated true (9ms)
+258ms ║║Comparison (boolean) false is_not (boolean) true = true (2ms)
+260ms ║║Condition #3 evaluated true (6ms)
+261ms ║║Cancelling condition #1’s schedules…
+262ms ║║Condition group #1 evaluated true (state changed) (20ms)
+265ms ║║Cancelling statement #9’s schedules…
+273ms ║║Executed virtual command setVariable (4ms)
+1909ms ║║Executed virtual command lifxToggle (1632ms)
+1913ms ║║Cancelling statement #10’s schedules…
+1917ms ║║Executed virtual command wait (1ms)
+1918ms ║║Waiting for 1000ms
+2926ms ║║Executed virtual command setVariable (4ms)
+2929ms ║╚Execution stage complete. (2693ms)
+2931ms ╚Event processed successfully (2931ms)

I then changed it to run Parallel instead of Serial. This improves things, but also as predicted last night, the Running logic does not help (just like my 1750 ms logic doesn’t either), for the same reason - because one thread sets Running to true but the 2nd thread doesn’t see it yet. I cleared the log and then press the button. The light turned off and stayed off. But as you’ll see, it attempted to toggle the light in the 2nd piston, but it doesn’t work (fortunately) because apparently LIFX can’t process both toggles fast enough which is a good thing in this case.

So the bottom line is that your approach and mine are similar in design, and the issue that keeps mine from working is identical to the issue that keeps yours from working. But in the end, the fact that LIFX can’t process the duplicate Toggle command fast enough so the 2nd one is a no-op actually benefits us and provides a working system for me. I’m not crazy about it though, because technically it shouldn’t work, which means in the future if something changes it may cause this to break again. Here’s the log from parallel mode:

10/8/2018, 12:55:43 PM +635ms
+2ms ╔Received event [ZWN-SC7 Enerwave 7 Button Scene Controller].button = pushed with a delay of 58ms
+65ms ║RunTime Analysis CS > 13ms > PS > 37ms > PE > 15ms > CE
+68ms ║Runtime (40244 bytes) successfully initialized in 37ms (v0.3.108.20180906) (65ms)
+69ms ║╔Execution stage started
+82ms ║║Comparison (enum) pushed gets (string) pushed = true (4ms)
+84ms ║║Cancelling condition #2’s schedules…
+85ms ║║Condition #2 evaluated true (10ms)
+91ms ║║Comparison (boolean) false is_not (boolean) true = true (2ms)
+93ms ║║Condition #3 evaluated true (7ms)
+94ms ║║Cancelling condition #1’s schedules…
+95ms ║║Condition group #1 evaluated true (state changed) (20ms)
+98ms ║║Cancelling statement #9’s schedules…
+104ms ║║Executed virtual command setVariable (3ms)
+2702ms ║║Executed virtual command lifxToggle (2594ms)
+2706ms ║║Cancelling statement #10’s schedules…
+2710ms ║║Executed virtual command wait (1ms)
+2711ms ║║Waiting for 1000ms
+3718ms ║║Executed virtual command setVariable (3ms)
+3731ms ║╚Execution stage complete. (3662ms)
+3736ms ╚Event processed successfully (3736ms)
10/8/2018, 12:55:43 PM +654ms
+2ms ╔Received event [ZWN-SC7 Enerwave 7 Button Scene Controller].button = pushed with a delay of 90ms
+75ms ║RunTime Analysis CS > 16ms > PS > 41ms > PE > 17ms > CE
+77ms ║Runtime (40244 bytes) successfully initialized in 41ms (v0.3.108.20180906) (75ms)
+79ms ║╔Execution stage started
+90ms ║║Comparison (enum) pushed gets (string) pushed = true (2ms)
+92ms ║║Cancelling condition #2’s schedules…
+93ms ║║Condition #2 evaluated true (8ms)
+99ms ║║Comparison (boolean) false is_not (boolean) true = true (2ms)
+101ms ║║Condition #3 evaluated true (6ms)
+102ms ║║Cancelling condition #1’s schedules…
+103ms ║║Condition group #1 evaluated true (state changed) (18ms)
+106ms ║║Cancelling statement #9’s schedules…
+114ms ║║Executed virtual command setVariable (4ms)
+2170ms ║║Executed virtual command lifxToggle (2052ms)
+2174ms ║║Cancelling statement #10’s schedules…
+2179ms ║║Executed virtual command wait (1ms)
+2180ms ║║Waiting for 1000ms
+3188ms ║║Executed virtual command setVariable (4ms)
+3202ms ║╚Execution stage complete. (3124ms)
+3208ms ╚Event processed successfully (3207ms)

Finally, here’s the test piston generating those debug logs:


#19

Thanks for testing and showing the logs. I think our logic is sound, but the speed of the double trigger is happening faster than can be accounted for. Although, I am a little confused why 30ms wasn’t enough time to set the variable though. Most of my variables get set in less than 10ms.

If an alternative DH (from a different author) is unavailable for your device, I would likely remove the ‘toggle’ and make one button always turn it on, and another button always turn it off. This way, even if there is spam, the end results will always be what you want. (albeit, requiring two buttons) The question to ask yourself is, how reliable do you want this?


I am thinking outside the box here, but I wonder if you modified the latest piston slightly to toggle a Simulated Switch instead of the LIFX lights. (line 32 above) Then make a one line piston that whenever the SimSwitch toggles, to toggle your LIFX lights.

My logic is, if webCoRE is still processing the first trigger, then the SimSwitch will likely only toggle once, which means, your LIFX would only toggle once. Kind of like a buffer-zone.


#20

Right. This is what I was saying in earlier posts. One thread sets the value but it seems to take too many ms for it to actually get set, so the next thread doesn’t see that updated value. This is the reason my logic with the 1750 ms check, and your Running logic, do not work (same reason in both cases).

Perhaps @ady624 can provide some insight as to how this could be happening, as it may have to do with webCore itself.

As for further workaround, thanks, however it’s really not worth any more time (yours or mine) since the current workaround works (for no other reason than LIFX is still handling one toggle when it gets the other toggle command, so the 2nd one has no effect, fortunately, so the second toggle effectively gets ignored). That said, it would be nice to understand what’s going on an why these variables cannot be read properly by the other thread. But only Ady I think is in a position to answer that.


#21

Try never cancel. It might be canceling your wait when the second button press happens.