App code: Compare settings to events

I'm just getting started with custom apps;

I'm trying to recognize which of multiple-selected devices was the one to trigger a handler. In preferences I have an input called 'switch B' which allows multiple switches to be selected. A subscription to switch B activates an event handler. In that event handler I can log two things:

  1. "settings.switch B[0]" -- which is the name of the first selected device from preferences.
  2. "evt.device" -- which is the name of the device that triggered the event handler.

When activating the first-selected switch of switch B, both of the above appear identical in the log. But when compared in a truth statement, they are false. That is keeping me from using it in an IF statement. Why are these two things not the same?

A snippet of code with the pertinent syntax from the switch B handler:

And the log output:

Any thoughts would be appreciated.

You should be able to just use the device name, instead of the entire device wrapper (what you've done above). So you could do this:

evt.displayName == "${settings.switchB[0]}"

or the device id, like this:

evt.deviceId == settings.switchB[0].id

1 Like

Thanks for the quick response! I see, I think, that I was using the whole wrapper. I'm still getting my head around that concept.
Your first solution worked. That's just comparing strings, I guess.
I thought the second solution of using the id would be even cleaner, but that one didn't work for me. Are both of those integers?

image

image

You know what, evt.deviceId is probably a string, so slap .toLong() on it.

I'm pretty sure that evt.device.id is an integer, and so is settings.switch[0].id. In both cases, pulling id out of a device wrapper.

I never remember this for sure, but a quick test shows me that id in both cases is a String:

In an event handler for a setting named input1:

log.trace "evt.device.id: ${getObjectClassName(evt.device.id)}" // String
log.trace "settings.input1.id: ${getObjectClassName(settings.input1.id)}" // String

However, the idAsLong propery is availalble:

log.trace "evt.device.idAsLong: ${getObjectClassName(evt.device.idAsLong)}" // Long
log.trace "settings.input1.idAsLong: ${getObjectClassName(settings.input1.idAsLong)}" // Long

You probably know why there are both; I don't. :smiley: (Maybe something ST did, too?) I do know (and it's why I remember the difference) that this once this took me quite long to figure out my problem when trying to retrieve values from a map based on a key that I thought was a number but wasn't...

2 Likes

Yup. That’s definitely my issue. And a good means to test for it, too. Thanks for clearing that up, guys!

I found this snippet of code in RM, doing exactly the same sort of test you want to do:

myRelDev.id == evt.device?.id

myRelDev is a device setting, just like yours. Turns out that both myRelDev.id and evt.device.id are strings.

1 Like

A surprising number of ways to skin this cat. Here's a summary of what I see:

The app code for a number of approaches on the two objects:

The log output (in reverse order, of course):

I feel like the last option from @bravenel might be best (...device?.id). I take it the '?' converts it to a string. The earlier option (...toLong) will work, too, but only with numeric values, I suspect. I guess I'm going to have to settle on approach and try to stay consistent!

Thanks again for the help!

https://groovy-lang.org/operators.html#_safe_navigation_operator

No, that prevents throwing a NPE if evt.device is null. In this particular context of RM, that was a possibility, so the ? protects against that.

It's an unusual case that you'd need to check for this at all, that is, to verify event device against a setting. Usually what you'd do instead is just use a different handler, so that by context you knew a priori what setting to use, or what to do with the event device without needing this test. So think about whether you are overloading a particular subscription/handler. Your app should know at the time it is initialized exactly what to expect from events without need to test like this.

Oh, I see. It's the '.' 'evt.deviceid' is Long, 'evt.device.id' is String. One is a class and the other is an attribute of a different class, I guess.

Good point about the questioning the overall approach, Bruce. It does seem a bit klunky. I'd like to develop a variable:value map where some of the variables are device-specific and updated upon event. But I don't know how many of a particular device I will have until after the preferences are set. I was thinking the handler could check against the settings to know which position on the list was triggering the event - then that would be the variable it updates. The handler activates via subscription to the group. I'm not sure how I would separate those subscriptions to activate different handlers if I don't know how many there will be up front. I'll have to think on it - maybe an each loop of some sort.
In the spirit of your point, though, I think I will consider creating custom drivers that track the variables I am interested in and maintain them with the device. I may be stuck with the same problem as I'll still build the master map (of numerous disparate devices and variables) in the app. But worth a try.

Thanks for the link, @tomw! I keep seeing people comment on how generic groovy is different from Hubitat app code groovy. But I think I'm going to have to at least learn the format & syntax.

1 Like

Groovy is Groovy. There are a few caveats about working in the Hubitat environment, for example certain libraries aren't allowed for import and the Groovy version itself is old so some newer language features aren't available.

But you can generally use online references for Groovy and even many/most Java-specific ones as a reference for Hubitat development.

2 Likes