Possible Bug: Duplicate event called using state or atomicState

I just stumbled onto a weird bug developing a button controller parent/child SmartApp. Trying to keep track of the toggle state of switches I am resorting to using state to store the status of a switch before controlling it. The problem is that when I call state or atomicState at any point in the event handler the event fires the SmartApp a second time with the same Event ID. If I remove or comment the code assigning values to the state variable, the function is not called a second time.

I added a filter, assigning an atomicState varible to track the last event ID and prevent execution if the event has previously called the function. This fixed the problem, but its a very odd glitch. It’s affecting all child apps, even those newly created.

Anyone else encounter this before?

state is a variable (a collection), not a method or object (I think…) so it isn’t “called”, just accessed.

So I find it odd that using it would cause a new invocation of the SmartApp. But… Well… There’s a lot that has happens under the covers, so I may be overlooking an obvious place a bug could occur…

atomicState performs an immediate read/write operation, so might have different behavior.

Have you been able to reproduce this with a trivial test SmartApp just as proof of concept that anyone could use to see if they replicate the problem?

The child SmartApp, is very trivial…

Commenting or removing these 2 lines prevents the duplicate call. I’m aware of how state functions, but accessing it is causing a the SmartApp to be called a second time.

state.pressToggleOn = pressTogglesOn
state.pressToggleOff = pressTogglesOff

Here’s a snippet of code…

def initialize()
{
	log.info "Smart Button Controller Initialized"

    state.pressToggleOn = [:]
    state.pressToggleOff = [:]
    atomicState.lastEventId = null

	subscribe(smartButton, "button", handleButtonEvent)
}

def handleButtonEvent(evt)
{
    log.trace "== [${app.name}]: Beginning execution of event id [${evt.id}] =="

	// Sanity check, when state is used in a Event Handler the event is called more than once.
    if ("${atomicState.lastEventId}" == "${evt.id}")
	{
		log.error "== The SmartThings cloud failed a sanity check, this event has already been called to this SmartApp, aborting. =="
		return false
	}
	atomicState.lastEventId = evt.id
    
 	log.trace "=== Button [${evt.value}] event ==="
 
	// Button pressed
	if (evt?.value == "pushed")
	{
		// Schedule the verification first
		if (verifyCommands) runIn(20, verifyPushed)

		// Toggles
		def pressTogglesOn = pressToggleSwitches.findAll { it.currentValue("switch") == "on" }
		def pressTogglesOff = pressToggleSwitches.findAll { it.currentValue("switch") == "off" }
        
        state.pressToggleOn = pressTogglesOn
        state.pressToggleOff = pressTogglesOff

		// Turn ON/OFF
        onPressSwitches?.on()
		offPressSwitches?.off()

		log.trace "==== Turning ${pressTogglesOn} toggle switches off..."
		pressTogglesOn.collect
        {
			it.off()
			if (delayCommands && commandDelay > 0) pause(commandDelay)
        }
        
		log.trace "==== Turning ${pressTogglesOff} toggle switches on..."
        pressTogglesOff.collect
        {
			it.on()
			if (delayCommands && commandDelay > 0) pause(commandDelay)
        }
        
        // Run routine
        location.helloHome?.execute(settings.hhPressAction)
	}

}