Scope of variables... Please guide

I have programmed a lot in the past primarily C++ , but no Java and no groovy. I know this is a basic question. Lets say I have an app and I need to know if the Alarm has been activated. I want to have access to "alarmactive" value in the different functions of my app. How do I make alarmactive global to my app? ( I have tried @Field I get an unable to resolve class error). For my real life example I actually want to also save the last_evt object values, to use if the alarm was armed and triggered. How do I make that object global, is there a simple answer, what I found in the threads went to more higher level discussions of performance and divers methods of declarations such as @Field, none which worked in this simple case.

def AlarmActivated()
{
alarmactive = TRUE
notifyalarmandtriggerevent( last_evt)
......
}

def DeviceEventHandler(evt) {
if (alarmactive = FALSE)
{ last_evt = evt
/* Need this when the alarm actually triggers, this will have been its trigger /
/
Don't send unless armed and triggered. /
.....
}
else
{
/
send these follow on events to monitor station until no longer active*/
}
}

< Last question is there a link to where scope of variables and the connections between Drivers, Apps, Initialization code and Dashboards is defined, as well as the persistence of their values... Thank you>

1 Like

That should work... you will want a Boolean I think

import groovy.transform.Field
@Field static String alarmactive='none'

otherwise you could pass alarmactive as a parameter

Thank you. Is settings a global to the app and is there also a settings for the driver... and if so can you add a variable to it.... I tried and failed....
I will use field... the reason I can't pass the value is because I can create it at the driver or app... but when a motion event (device) occurs the device cannot pass it, and I need it in the event code.... when the motion goes off... I need to know if the system alarm has been already triggered.
[ The devices sending the triggers are only in my list from the configuration, and any one of them can be triggered. (I do have a device for getting triggered by HSM which has a parent app that does the reception of triggers, and communicates to the alarm company...)].
I am actually updating someone elses code in order for the alarm company to know the device, and if it needs to send out a fire truck...

1 Like

Settings is just a map. The issue with settings is that it is not updated immediately sometimes in the same way that you can have state and atomicState. Plus you can't use atomicState in drivers.

@Field should solve your issue and you can always add calls from a driver into the main app code to handle things there.

Note that a static field variable is shared among all instances of the app (or driver). If yours is single-instance (and there's no guarantee unless you specify this in your metadata), this isn't a problem, but otherwise you'll need to use some solution like storing them in a Map indexed by app ID instead of storing them "directly" in a variable. Or you could use state, as mentioned above, with either that or atomicState (only available in apps) being more traditional solutions to this issue. However, a field will work if you don't need persistence across reboots, code saves, and whatnot (all of which will reinitialize the value).

On a related note, if concurrency is a concern and you're looking at atomicState, then you should look at ConcurrentHashMap if you take the Map approach. You can also add the singleThreaded: true attribute to your app/driver, new in 2.2.9 and which should allow only one method to execute--and then exit, saving everything--at a time.

And finally, it looks like you might be trying to read a device state, and in that case you can generally do so directly without needing to store the value in the app. (There may be minor concurrency issues here in that a device generating an event could take a few milliseconds to get committed to the database and, in turn, read out as the "new" value when the app asks, but your exact concern isn't clear to me, so these are just general statements about the Hubitat environment...)

1 Like

Annoying but true, this means that you can’t edit your code and continue running like before, instead you will have to restart the app to ensure the field variables get set up again correctly.

1 Like

THANK YOU! is there a link to any documentation on state and atomicState. Also is there any documentation that explains how hubitat apps address single instance, multi instance, multi thread and single thread. It would be so helpful to have a "quick start" developer guide that would give this overview, and an overview of drivers, apps and dashboards explaining what is and isn't persistent, and how they interact in single and multi instances.
I don't need it now, but you me mentioned storing values in a map indexed app with id, so you get back the value of the variable for your instance,.. do you have a link to such code (ie in GitHub). Thank you again

I don't know of any documentation (the classic ST docs were decent and similar enough, but I don't think they're around anymore). However, state is basically a Map available to any app or driver to store most common data. There are some types of objects you can't store, but in general, you'll be good with most common data types like String, Integer, Long (I think...), BigDecimal, etc. You can't store things like device references (but can use device ID and look up the object later, for example). So you can do something like state.myValue = "something" and then retrieve it later (e.g., String x = state.myValue).

Apps also have atomicState, with the difference being that atomicState is committed immediately, whereas state is not committed until that execution of the app/driver exits (so if another instance starts during that time, it might still see the "old" value). Most of the time, this isn't a problem, but I don't know your specific use.

There is only singleThreaded as I mentioned above, and it's new in 2.2.9. I don't know of any docs on it besides the posts you can find from staff mentioning it when it was new, but it sounds like it's supposed to wait for an existing execution to finish before starting a new one and be more efficient than atomicState (plus available to both apps and drivers) if that is your use case.

Regarding the Map, since static fields are shared among all instances of an app or driver, you'd need to do something like this (unless you're dealing with a single-instance app and don't want to bother):

@Field static Map<Long,String> theMap = [:]

// ...

def someMethod() {
   theMap[device.idAsLong] = "my value"     // or app.id
}

Otherwise, if you just use a @Field static String, the value will be shared among all instances of your code (e.g., two different child apps, or different devices that use the same driver code). And really you'd probably want to use a java.util.concurrent.ConcurrentHashMap here instead of just a plain Map (which defaults to a HashMap in Groovy), but the idea is the same. :smiley:

In many cases, this might need to be even more complicated, like a map of maps, depending on what you're storing; Hubitat's Z-Wave driver development guide for Z-Wave Supervision (just a forum post at the moment) has an example of doing this like this.

If you describe the issue you're trying to address, someone can probably give you more specific guidance; again, this is just general information about how the Hubitat environment works, and I didn't even mention other possibilities (like reading device attributes, which for devices you're automating--and that's probably why you have them on Hubitat--could be all you need).

1 Like

Just have to know where to look :sunglasses::

https://web.archive.org/web/20200509231906/https://docs.smartthings.com/en/latest/smartapp-developers-guide/index.html

3 Likes