Very basic help needed. Learning to code

@bertabcd1234, although this is not my question, I find the information provided very helpful. I am trying to learn as well and wanted to say thanks. I know sometimes we ask questions that may be answered in a Google search but your experience and exceptional explanations are extremely thorough! Thank you kind sir!

2 Likes

No problem! I'm apparently trying my best to make up for the fact that I was either too afraid to ask some of this when I first started writing my own SmartApps on ST (and made a lot of mistakes...) or my perception that perhaps no one in that Community at the time was willing to answer them. :laughing:

1 Like

I got another one for you, if you're up to it.

Is there a way to pull in preferences I've defined under a driver?

Within that same driver, and what do you mean with "pull"? In most cases you'd know what preferences you have since you've given them a defined name, but if they are dynamically generated or you're otherwise just curious what they are, they get saved to a settings map in a way similar to app inputs, so settings.each and other closures could iterate over all or find specific ones of interest. If you're looking for a specific setting, similar to an input in an app, the setting name is available for direct use (Groovy magic Hubitat implemented that we can't see), but like an app, settings['mySettingName'] or settings.mySettingName would also work.

Yeah, I'm still learning all the terminology... Sorry if I'm using the wrong terminology.

In the driver, I've set input names. I use these in device handlers within the driver.

The idea is, the user defines the input names, and the event handlers execute calling them like $variable.

I want to pass that $variable information into an app, so I don't have to have the user type in the info all over again.

Does that make sense?

So you're dynamically creating a preference in the driver with a user-defined name? Something like input name: "$myUsersRandomName", type: "string", title: "My user's random setting"?

If so, this would match the case above where you couldn't predict the input setting name head of time, so this might be the easiest way:

settings.each { key, val ->
  log.debug "This setting's name is: $key"
  log.debug" "and it's set to: $val"
}

However, the settings map is "private" to the driver, so you'd need to find a way to get this to the app. If it's a child device of a parent app, the parent app can access all methods of the child device, even ones that are not "public" commands, so making a method in the driver that the parent could call to get this information (e.g., returning a list of the setting names) would be one way I can think of. If it's an arbitrary app (not one you created these devices with), then I'm not sure. :thinking: Someone else might have better ideas...

Just a custom app, custom driver. Is there settings to link those together?

If the app creates the devices (using addChildDevice()), then that device is a "child" device of the "parent" app and you can do the above. If there's a good reason for the app and devices to be "linked," then that is a good idea (e.g., Hubitat's Hue Bridge Integration app probably does this for Hue Bridge bulbs and groups; I do in my custom integration as well, giving the "parent" app access to things on the Bridge child device that make a lot of things easier to do than they would otherwise be). If there's no reason for the app to create the device, this might make less sense or even make things harder for the user, but if the user is just you, I guess you can do whatever works. :slight_smile:

This isn't something you can see in the UI (they just show as "regular" devices, though there are also parent/child apps and parent/child devices where this structure is apparent), and it's not something you can change after the fact (it can only be a child device by virtue of having been created by the parent app).

Got another one.

I'm trying to do the following. It doesn't like me using the variable, but if I type in the text it will work. If I change $var to what it actually is, it will work.

Any clues?

httpGet([uri:"http://192.168.100/site"], { response ->
def blah = response.data.devices.$var.status
log.debug "The variable blah is $blah"

    })
1 Like

Try either:

response.data.devices."$var".status
or
response.data.devices[var].status

There is some useful discussion along these lines here: groovy - How to use a variable for the key part of a map - Stack Overflow

Here's a demo:

def traceRandomCode()
{
    def subMap = [blue:"b", red:"r"]
    def superMap = [subMap1: subMap, name: "superMap"]
    
    logDebug("superMap = ${superMap}")
    
    def var = "subMap1"
    
    logDebug("superMap[var].blue = ${superMap[var].blue}")
    logDebug("superMap.\"\$var\".blue = ${superMap."$var".blue}")
}




[dev:3423](http://10.0.0.4/logs#dev3423)2020-06-01 01:06:05.299 pm [debug](http://10.0.0.4/device/edit/3423)superMap."$var".blue = b

[dev:3423](http://10.0.0.4/logs#dev3423)2020-06-01 01:06:05.298 pm [debug](http://10.0.0.4/device/edit/3423)superMap[var].blue = b

[dev:3423](http://10.0.0.4/logs#dev3423)2020-06-01 01:06:05.296 pm [debug](http://10.0.0.4/device/edit/3423)superMap = [subMap1:[blue:b, red:r], name:superMap]
2 Likes

This worked! Thanks man!

1 Like

Stumbled across this thread, as I am trying to do exactly what you described - I have a method defined in the driver for a child device, and trying to call that method from the parent app. Do you have an example of the syntax for such a call?

Now I'm wondering if I have it backwards. I'm trying to find an example I thought I did but can't. (By "backwards," I mean that child devices can call arbitrary methods on the parent app--not that there's any other options since apps don't have "commands.") It may not actually be possible. Someone can correct me if I'm wrong here.

Yes, I can successfully call parent.appMethod() from the child driver, but calling child.driverMethod() throws an error - wondered if I was simply calling it incorrectly.

It sounds like I would have to expose it as a command for the device, in order to call it from the app? In reality, I'm trying to get/set a state variable on the child device... Any alternative tips?

I think exposing it as a command might be the only way. To set the state variable, you can make a command with a parameter and pass it that way. Getting it from the child if you're calling from the parent is a bit harder since to my knowledge there is no documentation for command methods returning values, but if this value is of possible use to the user, you could use an attribute (generate an event) instead, or you could create a method on the parent device to "set" some state value and call that from the child to pass it up that way. The best solution to this likely depends on what these values really mean and whether a user would likely want to use them.

You need a reference to the child device.

getChildDevice(DNI) is the interesting method. I frequently follow a pattern of getChildDevices() followed by iterating over the resulting list to find the specific child that I wanted to operate on. You can call a driver method directly that way, including all (or most?) of the methods on the device object: https://docs.hubitat.com/index.php?title=Device_Object

I'm not sure exactly how to get and set state directly, but I'd make get/set accessor methods personally (similar to what @bertabcd1234) described.

Hmm - I have a reference to the device - I think as a generic DeviceWrapper object, perhaps I need to make this more specifically a ChildDeviceWrapper via getChildDevice(device)? Will give this a try...

Here's how I do it:

In the app:
getChildDevice(getGroupName()).unbindGroupFromApp()

getGroupName() is a method in the app that creates the DNI format (a string) that the app uses when creating child devices

In the driver:

def unbindGroupFromApp()
{
    logDebug("unbindGroupFromApp()")
    
    unbindGroup()
}

unbindGroupFromApp() is not a command on the driver
unbindGroup() is another method in the driver that happens to be a command implementation, but this shouldn't be necessary.

I did it this way in case I wanted to do anything additionally from the app when executing that command.

1 Like

I literally figured it out, at least for me.

def updateChildDevice() {

child = getChildDevice("deviceNetworkId")
log.debug "$child"

if(child) {

child.updateSetting("inputNameVar", "$var_content")

}
1 Like

Yes, it seems that a DeviceWrapper is not sufficient - even if that device is a child of the app - getChildDevice(device.getNetworkId()).childMethod works great! Thanks for the help arriving at this conclusion!