Mixing atomicState and state

So myList can be included as the option variable in my preferences section which is outside of the method where I instantiate it ?

Gavin - both of those use state rather than atomicState but I will try as that might just be a typo.

no. We're talking past each other. I didn't realize that the problem you had originally was because you didn't have the input in a section. That's what caused the error in your first post. So forget all about the local variable thing. You can use atomicState for the options as you first wanted to.

It has always been in a section and works for a state variable but not an atomicState one.

Sorry typo. But state and atomicState both fail the same way when its directly in the prefs and in a section. Once I put it in my dynamic page it works fine. I do it this way as it gets rid of that mode and name input that is automatically added ot apps.

1 Like

Strange - state works for me directly in the prefs but atomicState doesnt. I'll try in a dynamic page.

Sorry I have to dash out now for the evening but will check back later.

Another simple option is to create a function that returns the enum list to the input statement. I have used this technique in the past. A function is callable anywhere within you app/driver, and can access state or atomicState and return a properly formatted result to the input() statement.

2 Likes

Definitely looks as though atomicState isn't valid at that point then. Whether it would be valid there later I don't know, so I'd try atomicState?.HASwitchDevices ?: []

So I have one createChildDevice() method and within that I have these four lines

		atomicState.count++
		log.debug "atomic Count is " + atomicState.count
		childrenCount = getChildDevices().size()
		log.debug "This installed app has $childrenCount child devices"

This starts out in synch from 1 but over time drifts and by the end of my device creation this is the output.

This installed app has 129 child devices
atomic Count is 76

What am I doing wrong ?

Could this be because I have child devices in my app using drivers that are using state and not atomicState ? Do I have to create these as child devices of the device driver rather than the app ? They are created quite fast - those 130 within about 30 secs

Is that the only place that atomicState.count is modified?

Yes - except for an initialisation to 0 in def updated()

I'm starting to lose confidence in atomicState - I have a similar issue with an atomicState string that I'm appending to...

here's the end of that string in 3 consecutiive log.debug outputs (chronologically top to bottom)

... kwhr_down, bathroom_sonos, doorbell, kwhr_up, standard_lamp_2

... kwhr_down, bathroom_sonos, doorbell, kwhr_up, comfortalarm_mode_off

... kwhr_down, bathroom_sonos, doorbell, kwhr_up, standard_lamp_2, generic_device

You can clearly see that the entry for comfortalarm_mode_off gets lost i.e. the atomicState.devices string variable was not saved and restored before the later append which was applied to the earlier version.

Note 'comfort_alarm_mode_off' was I assume appended to an earlier string that didn't even have 'standard_lamp_2' present but then 'standard_lamp_2' reappears in the following log.debug output ! These are correctly in order as it's only output in one method. It's behaving like a state variable.

I don't know if this is because I have a child device driver in this app using state but if so that is a serious issue for HE as any published drivers could implement state variables. Maybe it's a bug and not evident in ST due to the speed degradation of cloud and limited LAN device implementations.

Can you try a little experiment? Change atomicState.count++ to atomicState.count = atomicState.count + 1 and see whether that makes a difference to the reported number.

states are unique to Apps and Devices, they do not mix and have no effect on one another. It does not matter what a child App or child Device is using for state, they each have their own value. You have not really posted any of your code, so at this point it is only a guess at what is going wrong.

As it's using my MQTT broker it won't be replicable easily in it's current form for you so I'm going to try and produce a minimal version of the app that illustrates my issue.

Hi Rob - I have already tried this and I also tried copying to a local var and then incrementing and writing back. I did with the same with my atomicState string and also the list as I read that collections are handled differently in atomicStata - but the results are the same.

Let's see if I can create a simple app/device that illustrates this issue. Also going to reinstall app in case it's previous usage of state is messing it up somehow. I'm new to Groovy so I am likely doing something very stupid.

OK - I'm sure I'm just about to expose myself to ridicule as a Groovy newbie but here is a device driver and an app, create a virtual device, load the app, select the device driver and run it.

The device driver will create 26 very rapid device events for 'A' to 'Z' and the app correctly receives them all , although in an ad hoc order (!), it then counts them in two ways. It logs the output at the end including a concatenated received event value string.

Everything gives different and inconsistent results and fall way short of the 26 events actually sent (and received).

atomicState.devlist is ACDBENKM

atomicState.counter is 7

atomicState.count is 6

So let's all have a laugh, and I'll do the walk of shame ..what am I doing wrong ?

App

definition(
	name: "atomicState minimal",
	namespace: "ukusa",
	author: "Kevin Hawkins",
	description: "Testing atomicState",
	category: "My Apps",
	iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/App-BigButtonsAndSwitches.png",
	iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/App-BigButtonsAndSwitches@2x.png",
	iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/App-BigButtonsAndSwitches@2x.png"
)


preferences {
    section {
		input "mqtt", "capability.telnet", required: false, title: "Device Minimal", submitOnChange: true 
    }

}
		
def installed()
{
	log.info "${app.name} Installed"
	initialize()
}

def updated()
{
	log.info "${app.name} Updated"


	initialize()
}

def initialize()
{
	unsubscribe()
	log.info "${app.name} Initialized"
	subscribe(mqtt,"addDev", createDevice)
	atomicState.count=0
	atomicState.counter=0
	atomicState.devlist=""
	mqtt.initialize()
	pause(1000)
	mqtt.createDevs()
	runIn (1, "devSummary")
}

def createDevice(evt) {  //TODO merge with above
	atomicState.count++
	local=atomicState.counter
	local=local+1
	atomicState.counter = local
	atomicState.devlist += evt.value
	log.debug "Event from device ${evt.value}"
	}



def devSummary()
{
	log.debug "atomicState.count is ${atomicState.count}"
	log.debug "atomicState.counter is ${atomicState.counter}"
	log.debug "atomicState.devlist is ${atomicState.devlist}"
}

driver

metadata {
		// v0.1
    	definition (name: "MQTT minimal", namespace: "ukusa", author: "Kevin Hawkins") {
        capability "Initialize"
		capability "Telnet"  // temporary kludge as no mqtt capability yet
			
		command "createDevs"
    }

    preferences {
        // put configuration here
    }

}

def installed() {
    log.info "Installed"
}

def initialize() {
	log.info "Initialised"
	state.devices = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]
}

def createDevs () {
	for (String item : state.devices) {
		fakeParse (item)
	}
	
	//fakeParse (name)	
}
def updated() {
    log.info "updated..."
    initialize()
}

def uninstalled() {
    log.info "uninstalled"
}

def fakeParse(String name) {
	log.info "Parsing " + name
	sendEvent(name: "addDev", value: name)
}

@chuck.schwer - sorry I didn't tag you re my reply (above) to your post asking for code

Also - if I put a very small delay of 50ms in the device driver before the return of each event so they are paced better it works with 26 counted and everything is obviously in A-Z order .. but I suspect state would then work too. Less than 50ms and it starts to introduce more and more problems. This is probably dependent on how long I take in my methods to handle the event , avoiding concurreny of execution

I believe atomicState is still not totally robust to concurrency... with the italics being important in the ST documentation

Modifications (additional, removal, updating) to Atomic State within an execution are persisted to external storage more or less immediately .

and this is a bit misleading..

If using atomicState , which reads and writes to the external data store when the object is updated or accessed,

as it falls foul of the same issue state does but at a higher threhold

Yes I was going to reply with the same thing. atomicState reduces the possibility of a race condition but does not eliminate it. Unfortunately things are happening so fast with this that you are seeing this problem. I will give some thought about a possible solution to this problem, it might require a synchronization option for methods, or maybe for atomicState.

Ironic, It’s surfaced in HE because your Hub is local for network devices and so damn fast at it !

I have my bigger app working OK now and populating complete lists for input selections... as I had originally intended.

One last Q.. can I create but hide and/or disable devices programmatically ?

My device driver delay routine / loop is horrible too, can’t we have a better option like sleep()