Preventing double/concurrent activation

I had to do my own integration to my HVAC vendor's API, so I use a virtual thermostat on my C7 and have a rule that watches for changes to the virtual device. When the virtual thermostat settings are changed, the rule sends a http request to the API.

In the case that's causing an issue, I have a morning routine that is triggered by another a virtual switch turning on. That triggers several 'wake up' actions for lights and also changes both the heat set point and the cool set point of the virtual thermostat.

The rule I'm trying to troubleshoot has 4 triggers for virtual thermostat states: heat set point changes, cool set point changes, fan mode changes, or operation mode changes.

So each morning both the cool set point and heat set point change at the same time (or as close to the same time as can occur).

app:10162022-11-01 08:07:52.814infoTriggered: Cool setpoint of Thermostat(75.0) *changed*
app:10162022-11-01 08:07:52.490infoTriggered: Heat setpoint of Thermostat(66.0) *changed*

I tried to prevent double activations by using a required condition of 'Private Boolean = true' and having the first step of the rule set the private boolean to 'false', but that is happening AFTER the double activation:

app:10162022-11-01 08:07:53.099infoAction: Set Private Boolean(false) False
app:10162022-11-01 08:07:52.877infoAction: Set Private Boolean(true) False
app:10162022-11-01 08:07:52.814infoTriggered: Cool setpoint of Thermostat(75.0) *changed*
app:10162022-11-01 08:07:52.490infoTriggered: Heat setpoint of Thermostat(66.0) *changed*

The second trigger occurs before the first trigger can set the private boolean to false.

I'm also delaying the http call to the API by 5 seconds (cancelable) but the same thing happens there... the actions are following so close behind one another that the 'cancel delayed' actions for the second instance is called before the delayed action is set by the first instance.

I think I have a way to fix this, but just want to make sure there isn't a better way. What I was thinking of doing was having a rule with the four triggers that turns on a virtual switch. That virtual switch would be set for 'auto-off' after 1 second. Then I would use that virtual switch as a trigger to send the http request to the API. That way, no matter how many times the virtual switch rule gets triggered within that 1 second, the API call can't be re-triggered since the virtual switch will be saying (for those who get the Super Troopers reference) "I'm already turned on man! I can't turn on any further!"

That seems a little clunky, so I just wanted to check here first to see if there is a cleaner way to do this within the rule first.

If the timing is that tight, then you have no choice but to 'debounce' the triggers by some means or another. Your idea of a switch with auto-off is a fine way to do it.

Great idea, but I would set the auto-off to 2 seconds (or even 5 seconds), just to be sure you catch all the triggers.

@jwjr I would do that, or maybe even better, turn the switch off as the last action in the rule if I really wanted to prevent double activations, however in this case I actually just want to prevent activations so close together that they mess up the 'cancel delayed actions' logic.

I ended up taking out the private boolean required expression and doing something a little different.

I have to set a bunch of local variables before sending the http request, so the rule now looks like:

When virtual switch turns on:

  • Cancel delayed actions
  • Set a bunch of variables (8 total), with a 5 second cancelable delay on each set command
  • Delay 6 seconds (cancelable)
  • Conditional action: use variables to compare new values to old values. If different, send http request (this way if I bump the temp up one degree and then back down one degree, nothing gets sent)

As long as the rule gets to the 6-second delay point before the switch turns off, I'm good. Any reactivation after that will cancel the previous activation. Looking at the logs, it takes a little less than 1 second for the hub to get to that point.

So if I adjust the virtual thermostat on my dashboard, raising the heat set point from 63:
Click -> +1 degree (64), Click -> +1 degree (65), Click -> +1 degree (66)

The first click activates the virtual switch which triggers the rule, but the variables won't be set for 5 seconds. By then the heat set point on the virtual device will be 66. One second after that, the API request is sent as long as the rule hasn't re-triggered.

However, if I'm fiddling with the Thermostat for more than one second, I want to cancel everything and start over. So add to the above scenario that I then change the fan mode from On to Auto, but I do that 2 seconds after the initial temp change:

  • First temp change triggers the rule
  • After 1 second the virtual switch turns off
  • One second after that I change the fan mode
  • That turns the virtual switch on again, re-triggering the rule.
  • That cancels the delayed events, and now I start a new 5 second delay before settings the variables and making the http call

I just started diving in the deep end of Rule Machine a week or two ago, but I am really impressed by what it can do once you figure out the right path to get there.

2 Likes

Very cool, Alan, Congrats! :+1:

@bravenel - Tagging you for advice again.

The virtual switch method isn't preventing some double activations:

Here is the setup:

  • 1st rule has 4 criteria that will trigger it

  • The rule action is to turn on a virtual switch

  • It also sets it's private boolean to false. That gets reset once daily by another rule, so I should only get one trigger per day. But my issue isn't more than once per day, it is more than once per few milliseconds.

  • The virtual switch has a 5 second auto-off, so it should only be capable of going from off to on once per 5 seconds.

  • 2nd rule is triggered by the virtual switch turning on

  • It sends me a notification by making an http request

The first rule is watching for 4 custom attributes of the same device. They tend to all update at once when the device refreshes. The values are tire pressures on a car... when it gets cold, several can fall below the trigger threshold value at the same time.

So the first rule is being triggered 4 times within a few ms. The problem is that the second rule is also triggering more than once.

Here are the logs

dev:909  2022-11-20 18:59:55.335 info VS Tire Pressure was turned off
app:1023 2022-11-20 18:59:54.529 info Action: Set Private Boolean(false) False
app:1023 2022-11-20 18:59:54.426 info Action: Set Private Boolean(false) False
app:1023 2022-11-20 18:59:54.305 info Action: Set Private Boolean(false) False
dev:909  2022-11-20 18:59:54.305 info VS Tire Pressure is on
app:1023 2022-11-20 18:59:54.295 info Action: On: VS Tire Pressure
app:1023 2022-11-20 18:59:54.271 info Action: Set Private Boolean(true) False
dev:909  2022-11-20 18:59:54.231 info VS Tire Pressure is on
app:1023 2022-11-20 18:59:54.223 info Action: On: VS Tire Pressure
dev:909  2022-11-20 18:59:54.090 info VS Tire Pressure was turned on
app:1023 2022-11-20 18:59:54.079 info Action: On: VS Tire Pressure
dev:909  2022-11-20 18:59:54.035 info VS Tire Pressure was turned on
app:1023 2022-11-20 18:59:54.021 info Action: On: VS Tire Pressure

You can see app 1023 triggered 4 times, the switch is logged 'on' 4 times, and the rule sets its own private boolean to false 4 times.

I didn't have logging on for the second rule that sends the notification, but I received the notification twice, so that rule was triggered at least twice by the switch turning on.

The only solution I can think of is instead of directly calling the notification URL, calling a http listener on Node Red and having it send the notification. Then I can use a throttle node to prevent more than one message getting through even if 2 or more http requests are sent to Node Red. I would just really prefer to keep this all self-contained in Hubitat if there is a way to stop the multiple activations.

Any other ideas? I was thinking of using random delays, which would work some of the time, but I'd prefer a 100% solution.

This means that something has to be debounced. There is an example app in the public repo that does this for a contact sensor -- easy to change it for something else. It's a tricky problem, but there are some ways to do it.

The general idea is to create a virtual device to stand-in for the real device in the apps, but the app absorbs the multiple quick events reducing to a single event over a defined period. This would need to change from contact sensor and its events to the tire and its events, at some level where that makes sense.

1 Like

Download the Hubitat app