State is empty after first loop()


(Nic Jansma) #1

Hi everyone,

I have an app that I’m working on, to turn on/off my house’s lights randomly when I’m away.

Here’s the current code:

During initialize(), my app creates a state.groups = [:] array. Each item in the list is an object with properties such as .lights, .enabled, .on, etc.

initialize() calls loop() for the first time, and everything looks good – debug logging shows my state.groups array is working:

 [0:[lights:[Living Room Lights], enabled:true, start:1462882380000, end:1462896120000, on:true], 1:[lights:[group2Lights], enabled:false], 2:[lights:[group3Lights], enabled:false], 3:[lights:[group4Lights], enabled:false]]

I then runEvery5Minutes(loop).

However, when loop() starts 5 minutes later, the state.groups array is now null:

log.debug state.groups
// null

What gives?

Note in the dashboard, I see STATE: INCOMPLETE, which I don’t know what that means:


(Nic Jansma) #2

Looking at my logs, state.groups sometimes exists in loop(), sometimes not.

I’ll get 4-5 loop()s in a row where state.groups is null, then a loop() where it has the correct entries, then several loop()s where it’s null again.


(Larry) #3

every hour you run checkfornewschedule which blows away your groups

ie

142 state.groups = [:]
143


(Nic Jansma) #4

@Larry thanks for taking a look. Unfortunately, I don’t think that’s the cause of what I’m seeing.

In my case, loop() will run 12 times before the next hour when checkForNewSchedule() is run. During those loop()s I see null (or rarely, a full object) for state.groups. checkForNewSchedule() only clears state.groups = [:] once the day rolls over too.

Also, I would expect to see not null but [:] in the log.debug state.groups if that was causing issues.


(Tim Slagle) #5

You could try building a new map before hand and only building that with non-null entries. Something like this:

myMap = myMap.each { it.value }.findAll { it.value.size() > 0 }

I didn’t see anything wrong with the code with a quick look through, but it bet the length of the map is longer than 3 and for some reason there are some null entries in there so you aren’t ever getting to the meaty entries.


(Nic Jansma) #6

@slagle thanks! But the map doesn’t have any null entries right now - there are entries for integer values 0,1,2,3, each being an object.

The problem is that the map itself (state.groups) is null, not indicies within the map.


(Larry) #7

0 can be interpreted as null in some.languages that may be your issue.


(Nic Jansma) #8

If there are any ST developers reading this, are there any limitations to what can be added to state?

Can you add a map of objects?

Here’s essentially what I’m putting into state.groups during initialization():

state.groups = [
  0:[
    lights:[Living Room Lights],
    enabled:true,
    start:1462882500000,
    end:1462896960000,
    on:false],
  1:[lights:[group2Lights], enabled:false],
  2:[lights:[group3Lights], enabled:false],
  3:[lights:[group4Lights], enabled:false]
]

This object looks good for any other functions run from initialization().

However, as soon as the next loop() runs 5 minutes later, state.groups is null.

It’s as if the state doesn’t save properly beyond initialization.


(Tim Slagle) #9

Maps are maps. And we support maps.

What happens if you use the groovy native each statement?

You may also want to use state to build a map for the method, that way your state remains untouched.


(Nic Jansma) #10

Unfortunately nothing, as state.groups is null.

@slagle Can you try the code in the above repo? You should see what I describe happens right away:

  • During install (initialize()), state.groups is set to a map of objects
  • When you run loop() next, state.groups is null

(Tim Slagle) #11

Something is nulling the state. That’s why I recommended not iterating off the state but building a map at the moment.

This is how state is meant to be used.

myMap = state.groups


(Nic Jansma) #12

I’m not really iterating off of state, or state.groups for that matter.

In the code, during loop(), I iterate over numbers 0-3:

for (def i = 0; i <= 3; i++) { ... }

I then try to pluck out state.groups[i] and modify that object’s properties:

def group = state.groups[i]

However, state.groups is null already in loop() so this throws an exception.

What would be some causes of nulling state?


(Tim Slagle) #13

Could be your running into some concurrent read/write issues. atomicState may resolve that for you.


(Nic Jansma) #14

Unfortunately, replacing every access I had of state to atomicState does not resolve the issue.

Install the app, log.debug shows state.groups is golden, click on loop() in the UI to force it to run, state.groups is already null.


(Tim Slagle) #15

So looking at your example I actually don’t see state.groups defined anywhere. Where is it defined?


(Nic Jansma) #16

In checkForNewSchedule()

state.groups = [:]


(Tim Slagle) #17

Just saw the name of this app. Have you checked out “Vacation Lighting Director” in the marketplace?


(Nic Jansma) #18

I have :slight_smile: But wanted something slightly different.


(Tim Slagle) #19

Fair enough, still looking anyways


(Nic Jansma) #20

OK, I think I’ve figured it out. Sorry – I was incorrect, state.groups is not null, but the object at the index I was trying to use was. As you alluded to, iterating over state.groups might’ve pointed me to this earlier.

Here’s the problem. Even though I’m saving an object into the map with an integer index:

state.groups[1] = [:]

The key gets serialized to as string.

So when I later try to access it via:

state.groups[1]

That’s null.

But this works:

state.groups["1"]

So as long as I treat all keys as strings, I should be OK.