State variable not storing object

Here was the thread I reported the issue in Jim.

This was when was I was developing the Blink camera DH that I ran into the issue.

I whipped up a simple Parent Child (DH) app to replicate the issue and found that state is now working as it should be, the context is global, whether called from a child or within a parent etc, appears to retain the values. Maybe it was a bug in the way state was being handled when I was writing the DH, it seems to be working fine now. atomicState and State are both able to handle nested maps and both retain context now. (good news)

There is however another issue (?) I found while testing this. After installed() is called by the platform, it immediately calls updated(), is that expected behavior?

Here are the Parent and Child DH apps to replicate:

PARENT SMARTAPP

definition(
    name: "Test Device Parent",
    namespace: "rboy",
    author: "RBoy",
    description: "Test Parent Child app",
    category: "Safety & Security",
    iconUrl: "https://s3.amazonaws.com/smartapp-icons/Developers/whole-house-fan.png",
    iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Developers/whole-house-fan@2x.png",
    singleInstance: false)

preferences {
    page(name: "mainPage")
}

def mainPage() {
    dynamicPage(name: "mainPage", title: "Test Parent Child Device App", install: true, uninstall: true) {
        // Let the user know the current status
        section("Status") {
            def devices = getChildDevices().each { device ->
                log.trace "Found child ${device.displayName}"
                def stuff = device.readStuff()
                paragraph "${device.displayName}: $stuff"
            }
        }

        def physicalHubs = location.hubs.findAll { it.type == physicalgraph.device.HubType.PHYSICAL } // Ignore Virtual hubs
        if (physicalHubs.size() > 1) { // If there is more than one hub then select the hub otherwise we'll the default hub
            section("Hub Selection") {
                paragraph title: "", "Multiple SmartThings Hubs have been detected at this location. Please select the Hub."
                input name: "installHub", type: "hub", title: "Select the Hub", required: true
            }
        }
    }
}

def installed()
{
    log.debug "Installed: $settings"
    initialize()
}

def updated()
{
    log.debug "Updated: $settings"

    unsubscribe()
    unschedule()
    initialize()
}

def uninstalled() {
    log.trace "Uninstalled called"
    getChildDevices().each {device ->
        log.info "Deleting Device $device.displayName"
        deleteChildDevice(device.deviceNetworkId)
    }
}

def initialize() {
    def physicalHubs = location.hubs.findAll { it.type == physicalgraph.device.HubType.PHYSICAL } // Ignore Virtual hubs
    log.trace "Selected Hub ID ${installHub?.id}, All Hubs Types: ${location.hubs*.type}, Names: ${location.hubs*.name}, IDs: ${location.hubs*.id}, IPs: ${location.hubs*.localIP}, Total Hubs Found: ${location.hubs.size()}, Physical Hubs Found: ${physicalHubs.size()}"

    try {
        def existingDevices = getChildDevices()
        log.trace "Found devices $existingDevices"
        if(!existingDevices) {
            if ((physicalHubs.size() > 1) && !installHub) {
                log.error "Found more than one physical hub and user has NOT selected a hub in the SmartApp settings"
                throw new RuntimeException("Select Hub in SmartApp settings") // Lets not continue with out this settings
            }
            if (physicalHubs.size() < 1) {
                log.error "NO Physical hubs found at this location, please contact SmartThings support!"
                throw new RuntimeException("No physical hubs found") // Lets not continue with out this settings
            }
            
            (1..1).each {
                def id = 3000 + it
                log.info "Creating Device ID $id on Hub Id ${physicalHubs.size() > 1 ? installHub.id : physicalHubs[0].id}"
                def childDevice = addChildDevice("rboy", "Test Device", id.toString(), (physicalHubs.size() > 1 ? installHub.id : physicalHubs[0].id), [name: "Test Device $id", label: "Test Device $id", completedSetup: true])
            }

            existingDevices = getChildDevices()
        }

        log.trace "Working with devices $existingDevices"
        
        /*existingDevices.each { device ->
            def stuff = "DeviceID" + device.deviceNetworkId.toString()
            log.trace "Saving stuff to ${device.displayName}: $stuff"
            device.saveStuff(stuff)
            stuff = device.readStuff()
            log.debug "Read stuff from ${device.displayName}: $stuff"
        }*/
    } catch (e) {
        log.error "Error creating device: ${e}"
        throw e // Don't lose the exception here
    }
    
    atomicState.testVar = [name: "inInitialize", value: [loop: "testInitialize", loop1: "testInitialize"]]
    log.warn "From initialize: $atomicState.testVar"
    
    runIn(10, someFunc)
}

def someFunc() {
    log.warn "From someFunc: $atomicState.testVar"
    atomicState.testVar = [name: "inSomeFunc", value: [loop: "testSomeFunc", loop1: "testSomeFunc"]]
    log.warn "After setting someFunc: $atomicState.testVar"
}

def childFunc(child) {
    log.warn "From childFunc: $atomicState.testVar"
    child.log "From childFunc: $atomicState.testVar", "warn"
    atomicState.testVar = [name: "inChildFunc", value: [loop: "testChild", loop1: "testChild"]]
    log.warn "After setting childFunc: $atomicState.testVar"
    child.log "After setting childFunc: $atomicState.testVar", "warn"

    runIn(10, randomFunc)
}

def randomFunc() {
    log.warn "From randomFunc: $state.testVar"
    atomicState.testVar = [name: "inRandomFunc", value: [loop: "testRandomFunc", loop1: "testRandomFunc"]]
    log.warn "After setting randomFunc: $atomicState.testVar"
    
    runIn(10, someFunc)
}

CHILD DH:

metadata {
    definition (name: "Test Device", namespace: "rboy", author: "RBoy") {
        capability "Polling"
        capability "Refresh"

        // Calls from Parent to Child
        command "saveStuff", ["string"]
        command "readStuff"
    }

    tiles(scale: 2) {
        standardTile("refresh", "device.status", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
            state "refresh", action:"refresh.refresh", icon:"st.secondary.refresh"
        }
        
        main "refresh"
        details(["refresh"])
    }
}

def initialize() {
    log.trace "Initialize called settings: $settings"
    try {
        if (!state.init) {
            state.init = true
        }
        response(refresh()) // Get the updates
    } catch (e) {
        log.warn "updated() threw $e"
    }
}

def updated() {
    log.trace "Update called settings: $settings"
    try {
        if (!state.init) {
            state.init = true
        }
        response(refresh()) // Get the updates
    } catch (e) {
        log.warn "updated() threw $e"
    }
}

def refresh() {
	log.trace "Refresh called"
    parent.childFunc(this)
}

// PARENT CHILD INTERFACES
def void saveStuff(def stuff) {
    log.debug "SaveStuff called with: $stuff"
    state.stuff = stuff as String
}

def String readStuff() {
    log.debug "ReadStuff called, returning: ${state.stuff}"
    return state.stuff as String
}


// Print log message from parent
def void log(message, level = "trace") {
    switch (level) {
        case "trace":
        log.trace "LOG FROM PARENT>" + message
        break;

        case "debug":
        log.debug "LOG FROM PARENT>" + message
        break

        case "info":
        log.info "LOG FROM PARENT>" + message
        break

        case "warn":
        log.warn "LOG FROM PARENT>" + message
        break

        case "error":
        log.error "LOG FROM PARENT>" + message
        break

        default:
            log.error "LOG FROM PARENT>" + message
        break;
    }            
}