Bug in Parent Child Devices

I’m developing a Service Manager SmartApp.

The Device calls a function in the Service Manager via parent.XXX() call. The Service Manager then executes some functions. However it now needs to defer the final action so it saves the state in state.queuedActions

    state.queuedActions << [[function: this.&updateCameraImage, parameter: "", child: child]]
    runIn(10, executeQueuedActions) // If was successful then give it time to complete

When executeQueuedActions runs later:

log.trace "Called execute queued actions: $state.queuedActions"

I see:

Called execute queued actions:

It isnt’ saving the context of the call. I think the original call is in the context of the Device Handler where as the runIn end up running the context of the Service Manager SmartApp and doesn’t save the state variable

@Jim @jody.albritton @slagle any comments? Is this expected behavior or is it a bug?

I’ve had problems like this in my app (Ecobee) recently as well. It was from a child device instead of a child SmartApp but it was the same exact effect.

To fix it the only thing I found that worked was to change all state variables to be atomicState instead. At that point I was able to store values successfully.

Noone seemed to be able to give me an explanation as to why. I tried for probably 4-5 hours to troubleshoot and finally gave up and went with atomicState and decide to live with any performance penalties.

Hope this helps you at least move forward with your app until someone can explain the behavior.

2 Likes

I can’t use atomic state with map data. It just hangs! I posted another thread on that bug. Looks like we’re in a deadlock here state won’t save the context, atomicstate hangs

1 Like

I had the same problem on that front as well.

My workaround for that was to use a constructed variable like this:

   atomicState."previousFanMinOnTime${deviceId}" = getFanMinOnTime(child)
    atomicState."previousHVACMode${deviceId}" = getHVACMode(child)        

It’s an ugly hack to do it that way, but it worked for my situation as a way to not wait for a fix.

2 Likes

Unfortunately it doesn’t work for lists or maps

atomicState.“queuedCommands”.add([function:“1”, child:“2”])

This doesnt’ work :anguished:

And this causes it to hang:

atomicState.“queuedCommands” = atomicState.“queuedCommands” << [function:“1”, child:“2”]

@slagle @jody.albritton any thoughts?

I’m completely frustrated here, when calling the parent from a child app the state just won’t remember anything, it’s like a temporary variable who’s scope disappears after the function exits!

log.trace "Pending Queued commands: ${state.queuedCommands}" // DEBUG
state.queuedCommands.push([function:"1", child:"2"]) // Add it to the queue

Everytime the function is called the state variable is empty!!

So here’s the summary for a Parent Child app when the Child (Device Handler) calls the parent (Smartapp)

  1. state variable in the parent app does NOT work, it’s a temporary variable who’s value is cleared as soon as the function exits!
  2. atomicState ONLY works for simple values, if you try to assign a map or a list to it it hangs!

@Ben @slagle @jody.albritton - guys need you to jump in here, this is a very sorry state, if the State variable doesn’t work then the platform is moot! Without the state variable can’t do much to track child apps from the parent and one can’t develop any meaningful apps! HELP!!

2 Likes

I’m not where I can test it at the moment, but a little experiment that I might try later this weekend.

What if, instead of calling the state.XYZ directly from the child App, does it behave differently if you define getXYZ and setXYZ functions in the parent and use those? (Would you need to also then define those as custom commands too???)

@StrykerSKS the simple issue here is that I need to control the execution order and timing of the functions. The server I’m connecting to has concurrency limitation of 1. So if 5 devices are connecting to the server (via the parent app) I need to control it. Hence I need to queue the commands. To queue the commands I need to save them and then process them one by one. The only way I know how to queue a command is ST is to use the state variable to track them (or atomicState). However as above the state variable doesn’t work with parent apps and atomicState doesn’t accept lists.
Any suggestions?

Nope, I’m at a loss here too. And I’d love to see the atomicState issue be solved. I’d be able to simplify my workaround if that were resolved.

I think I need to experiment some more this weekend.

I do have some lists in my atomicState but they all seem to be created like this:

def tempList = [some list data]
atomicState.savedList = tempList

What about this:
def tempList = atomicState.queuedCommands
tempList << [new commands] // (or other list operators)
atomicState.queuedCommands = tempList

If that actually works, then you could write a helper function to reduce it to one command in your code.

Pretty sure atomicState can contain anything, just not having items added to it dynamically
Have you tried this?
def myList = atomicState.list
//add stuff to myList
atomicState.list = myList

1 Like

I’ll check it out, but what’s with the “State” variable not working?? How basic is that.

@Mike_Maxwell this issue is specific to the Parent App being called from the child device. State and AtomicState work fine when using directly through the context of a SmartApp (e.g. a method called through an event or timer) however when the Parent is called from the child State just doesn’t work, it always loses the value after the function exits

Also marking @Mike_Maxwell

Nope this hangs at:

atomicState.queuedCommands = tempList

Again this is specific to when the method in the parent is called from the child device

I think the issue is that there are separate instances of the app running. Each child can have multiple instances running due to events. When an instance of a child calls the parent, in effect the called method becomes part of that child’s instance. That’s why, for example, a log.debug in the parent called method shows up in the child’s log stream. With all of these multiple instances floating around, obviously state is going to be toast if one of the instances changes it. When does the change get written to storage? When which instance is done running?

The issues with atomic state are more confusing. But, at the base of the issue is the possibility of two instances both attempting to write different values to it at the same time: i.e., a non-determinism exists with atomic state.

1 Like

If the instance determines the state of the “State” variable it should retain it when called again from the same child. I’ve tested this and each time the child calls the parent the “State” variable wipes clean! I would understand if the retained the context of the child and the value for that context, i.e. for each child calling the parent method the State variable would have a different value, but here with the same child it wipes clean each time. Hence I still maintain it’s a bug.

BTW, when the parent app calls log.debug when the method is invoked by the child nothing is logged so Iv’e had to write a custom call back to the child to print the debug message.

1 Like

Then I’m out of ideas. Seems to me that there is a platform issue where the context to access the “state” (atomic or otherwise) seems to be broken when dealing with a new thread (call from a child app).

Did you guys see this?

http://docs.smartthings.com/en/latest/smartapp-developers-guide/parent-child-smartapps.html#tips-best-practices

Tips & Best Practices

  • Think carefully about creating more than one level of parent-to-child relationships, as it may negatively impact usability and create unneeded complications.
  • Sharing state or atomicState between parent and child SmartApps is not currently supported.

Without seeing some sample code I can’t replicate what you guys are seeing. I do this currently with a device I wrote but put it in a method and return it.

1 Like

Yes, have seen this.

I interpreted the “Sharing state or atomicState between parent and child SmartApps” part as meaning that they don’t use the same context, but not that changes to the state variables made by the parent when called by a child wouldn’t work.

Btw, I had this same set of problems within the same app, no parent/child apps involved for me. (For me it was a child device calling a parent app). And all of my state variable handling was directly in the parent app (no direct calls to the state variables from the child device). I ended up having to switch from state to atomicState to fix part of it, but then I had problems with creating lists on atomicState.

Something isn’t right here.

Rule Machine makes extensive use of state variables in the parent. It used to use more, but I changed something. It used to be that every Rule reported its truth state to the parent, who kept it in a state variable. That worked with 100% reliability. Now, I simply have the parent call the child in question to get the truth state, and report it back to which ever child is asking for it. I still keep “subscriptions” in the parent, in a state variable, and that has been 100% reliable also.

And, whenever I put log.debug statements in the parent in methods called by child apps, they show up just fine, in the child app log stream.

So, @RBoy, something is messed up with your parent-child setup! More than the bug you think you’re seeing. In fact, it could be that whatever is messed up is the reason for the problem. Just setting a state variable in the parent, and then seeing it later, work fine for me, and I’ve banged the heck out of it. So have a lot of users. Except for the bugs that I’ve introduced along the way, that all works as expected.

1 Like