How to use Hub Variables in apps

You will need to create an input yourself, whether it be a free text box for the user to type the name or one of type: enum populated with the result you get from getGlobalVarsByType() or a similar method (I normally think the latter is a better user experience, but your app may differ). For an even better user experience, don't forget to register the variable as in use and un-register if not. :slight_smile:

Here's an app I wrote that uses the second approach, though it's not exactly a minimal example: https://github.com/RMoRobert/Hubitat/blob/master/apps/DimmerButtonController/DimmerButtonController4.groovy

1 Like
List vars = []
getAllGlobalVars().each{vars += it.key}
input "whichVar", "enum", title: "Choose a variable", options: vars
2 Likes

Thanks Bruce and Robert. That feels a lot tidier now. :slight_smile:

Quick way to get them in alpha order using Bruce's code:

List vars = []
getAllGlobalVars().each{vars += it.key}
input "whichVar", "enum", title: "Choose a variable", options: vars.sort()
1 Like

That's easy enough in installed() and uninstalled(), but I am having a hard time in updated() where the user could choose a different variable with the app still installed. I've tried using a temp holding variable to take the input but the value it's not getting assigned to the fulltime variable when the time comes. I am not sure whether getGlobalVarsByType() is returning string values of the hub variables' names or references to the hub variables themselves? Nonetheless I feel as if I should be able to assign it to another variable!!

Both getGlobalVarbsByType() and getAllGlobalVars() return a Map, with the documented format being:

[ globalVarName:[type:xx, value:xx, deviceId,xx] ,... ]

(This is why the approaches above take the key for this map as the thing to use for the list of variables, by the way--that's the variable name, which you want in that case.)

The rest of your question isn't clear to me, so maybe try providing a minimal example snippet of code? If you're just asking how to know what variables to register and un-register as user selections change, an easy enough thing to do is just un-register everything and then (re)register the ones you know the user has selected. It sounds a bit lazy, but outside of keeping track of which ones you have registered and hoping that never gets out of sync, I can't think of a better way, and this should be a relatively rare occurrence (only when the user modifies the app--not every time it runs, for example) that IMHO probably isn't worth optimizing further.

In preferences I have
input "varchoice", "enum", title: "Choose a boolean variable", options: vars

in top level of script I have
indicator = null

Then in updated() I have
def updated() {
log.info "in updated"
log.info "varchoice " + varchoice
log.info "indicator " + indicator
if (indicator != null)
{
log.info "indicator had a value"
removeInUseGlobalVar($indicator)
}
else
{
indicator = varchoice
log.info "indicator was null"
}
log.info "indicator" + indicator
log.info "out of updated"
addInUseGlobalVar($indicator)
}

As indicator was resolutely null all the way through I had expected it to match the else block and become the same as varchoice.

NB I am not at all used to Groovy, as I am sure you can tell.

Edit: I don't want to take up too much space with this part of my problem in this thread as it's probably no longer anything to do with it being a hub variable. I guess if you tell me how to remove all hubvars from InUse without knowing their names that will solve the immediate problem

Yeah, could be best to move this to a different thread. The biggest problem I see in your code is that addInUseGlobalVar($indicator) at least won't work and possibly won't compile; you probably meant addInUseGlobalVar(indicator) or addInUseGlobalVar("$indicator") instead.

Regarding indicator itself, I don't like to define variables at the top level of a Groovy script, in part because this is rarely needed but also because their behavior isn't always clear to me. (I think it's the same as a @Field, which is shared among all instances--not just this one--and is cleared on reboot or re-save of the code? I do use fields on occasion, just specifically annotated as such such because then I know for sure what I'm working with.) In your case, the need to save this value separately isn't clear to me. If you want something that persists between executions and is specific to this installed instance of the app, I'd suggest state instead (e.g., state.myName = x). The state map is built-in to all apps and drivers for this use case--but again, I'm not sure you need anything at all.

To answer the last question, the answer you're looking for is the second of the two "remove" methods documented in the first post:

Boolean removeInUseGlobalVar(String name)
Boolean removeAllInUseGlobalVar()
    Note: removes calling app from in use list for variable, or all variables 
​
2 Likes

So just one last question that's still on-topic: if I want to setGlobalVar(varname, false), do I need to somehow extract the name string (the one chosen in preferences) from the returned array, or can I just say setGlobalVar(indicator, false) ?

setGlobalVar() takes a String as the first parameter, so passing an entire Map entry won't work--but if you're using the code example above, it looks like vars is already a list of strings, the varchoice field (the name of your input) would be set to that string, and indicator is in turn set to that string (though I'm still not sure why it's there :smiley: ) ... so you should be good!

Oh sorry I was intending to use that all along. It was varchoice that was meant to be the tempvar

Is there a way to increase the string size > 255 bytes? Can we have long strings maybe running around 4096 bytes or more? I like to pass massive data from apps to device drivers. Or better yet use a string subscription length on the variable settings.

This is well beyond the intended scope of use of Hub Variables. You will have to use some other means to pass large amounts of data from apps to drivers. But, presumably you're the one writing both the app and the driver, so this can be done without using Hub Variables.

1 Like

Ugh! I think I just found the getGlobalVar routine is not in device code. Unsupported right?

newgrouppingiplist = getGlobalVar("GroupPingIPList")
newgrouppingnamelist = getGlobalVar("GroupPingNameList")
newgrouppingswitchlist = getGlobalVar("GroupPingSwitchList")

1 Like

@bravenel can you confirm that getGlobalVar is not available in drivers code?

My use case is : I need to calculate the energy cost inside a driver code. The enrgy cost varies around the clock and it will be very convinient to change it at once from RM5 for all devices that use this driver (instead of calling SetEnergyCost() for each device one by one).

Any ideas for an alternative approach, if getGlobalVar does not work in drivers?

In my sandbox, I have some rough code that gets around the restriction using a small app to get/set the variable based on attributes being updated in a device. Not really production worthy, but may give you some ideas.

App: https://raw.githubusercontent.com/thebearmay/hubitat/main/apps/varController.groovy
Driver: https://raw.githubusercontent.com/thebearmay/hubitat/main/varCntrl.groovy

1 Like

Thank you for the idea! Probably, something similar will work in my case.
As I don't have much experience with Hub Variables use in Apps, I am not sure if it be possible to subscribe for a Hub Variable change, and then automatically invoke the "setEnergyCost" custom command for all subscrived devices?

subscribe(location,"variable:$varName", "handlerMethod")
1 Like

Thank you!
By some reason input "device.VariableControllerDevice" didn't work for me, I had to change it to "capability.actuator" to be able to select the virtual device I am testing with.. Anything else works! :slight_smile:

Yeah that would onlly pull up devices using the Variable Controller Device driver.

1 Like