Very basic help needed. Learning to code

How can I delay this, and cancel if the the motion sensor becomes active again?

// Main Event Handlers

def motionHandler(evt) {

if (evt.value == "active" && pauseswitch.currentSwitch == "off" && switches.currentSwitch == "off") {         
 switches.on()
}
else if (evt.value == "active" && pauseswitch.currentSwitch == "off" && switches.currentSwitch == "on") {
 log.debug "$switches is already on. Skipping."   
}
else if (evt.value =="inactive" && pauseswitch.currentSwitch == "off" && switches.currentSwitch == "on") {
 switches.off()
}
else if (evt.value == "inactive" && pauseswitch.currentSwitch == "off" && switches.currentSwitch == "off") {
 log.debug "$switches is already off. Skipping."   
}
else {
 log.debug "$pauseswitch is on. Skipping."
}                   

}

The general pattern you'd want for something like this is:

if (evt.value == "active") {
  unschedule(doOff)  // this is the "cancellation"
  switches.on()
}
else {  // it's inactive
  runIn(numberOfSecondsLater, doOff) // this the "delay"; numberOfSeconds could be an input field, or a minutes field value * 60, for example, or hard-coded
}

which would require adding a handler (doOff is what I called it in the schedule() call; you can call it whatever) like this:

def doOff() {
  switches.off()
}

This does not include any of your extra logic for the "pauseswitch" and whatnot (I didn't spend much time thinking about that because there's a lot of context missing that you are probably better equipped to fill in), but this does show you the Hubitat-Groovy-app equivalent of "delay" and "cancel," at least as it pertains to this type of subscription and schedule. :slight_smile: Hopefully you're able to work this into your logic above where needed. Good luck!

2 Likes

Thanks, that did it.

I have another basic question. How can I do something like this. I'm sure the syntax is wrong.

if (evt.value = "active" && now >= timebegin && now <= ending)

Could you explain a bit more? Where do the "begin" and "ending" times come in? The now() function will return the current time as Unix epoch time (in milliseconds), if that is of any help for the comparisons.

I managed to figure it out. Did some searching and found it on the forum.

I’m not in front of the computer to post.

I’m starting to get a hang of stuff.

I’m building my own specific motion lighting app.

I have plenty to learn, but it’s satisfying when things start working!

1 Like

How do you have a child app under the parent?

As of right now it keeps creating parent apps. I want it to be like rule machine.

Rule Machine
Rule1
Rule 2
Rule 3

Yay!

For the parent/child thing, you need to make two different apps (or at least that's the recommended path--I would follow staff recommendations and not some community posts that can trick this into working with one file). The classic ST docs on parent/child SmartApps have a bit of info that applies equally to Hubitat as far as I can see.

A lot of parent apps are just very basic "containers" for the child app. For example, my Timed Switch Helper parent app is just an easy way to create and display instances of child apps:

The key line here is:

app(name: "childApps", appName: "Timed Switch Helper (Child App)", namespace: "RMoRobert", title: "Add New Timed Switch Helper", multiple: true

The app() method is just another thing you can use inside a section(), like input() or paragraph(). The name of childApps is totally arbitrary, just like it is for inputs ; what's important is the appName that has to match that of your child app as declared in its definition.

The other key thing is that in the child app, you need to specify the parent by namespace and app name, separated by a colon, in its definition, like:

parent: "RMoRobert:Timed Switch Helper"

(as an aside, that gets tricky if the parent app has funky characters in the name, so I always try to avoid those in general but especially here.)

RM would be a bit more complicated because the parent app actually does more (like store global variables; there are ways parent and child apps can "talk"), but for more or less just organizational purposes, you shouldn't need much more than something like the above.

1 Like

Thanks man. This is really helping.

Now I just need to figure out how to incorporate modes. Basically having options to select which modes to run under.

For tomorrow... :slight_smile:

Not sure if I left that in the app above, but if not: the mode selector shows up automatically on single-page apps. If you explicitly define pages, then you'll have to add the the selector (input) and the logic to use it on your own. I pretty much always do it the latter way but think the platform handles it all if it's the first method (don't quote me on that).

Good to know.

One thing I’m still stuck on, how do you deal with multiple devices.

Examples multiple: true

You select multiple motion sensors. How would you write to make sure all motion sensors are inactive?

So that will get you a list of devices instead of a single device (technically a DeviceWrapperList instead of a DeviceWrapper), which behaves in the same way as most Groovy collections (I assume it's a subclass or implements some interface that requires it to behave like List, but I have no knowledge of this). Hubitat implements some convenience on top of this and I think just about anything you could do with one device you could do with all with this collection, but things like .currentValue('motion') would return a list of these states, not one representative state. You could iterate over that or use a closure like below to do something, but I prefer to just work with the Device list itself as below.

Being a collection allows you to use adorable Groovy Closures to do things like this:

Boolean anyActive
anyActive = motionSensors.any{ it.currentValue('motion') == 'active' }
if (anyActive) {
  // do things
}

(For reasons not clear to me, you'll see a lot of code attempting to do that uses things like motionSensors.find { it.currentValue('motion') == 'active' }, which will work with Groovy truth in that it will evaluate to false if nothing is found, but it seems unnecessary if you're not actually interested in which device it is. You'll also see people use latestValue with lists sometimes, but this behavior--like many things--is not documented on Hubitat. I assume it is the same as SmartThings, where latestValue is the exact same as currentValue and currentXyz for attribute xyz...no need to specially use it here.)

1 Like

So, how can I search a variable to see if it contains one of the modes selected?

So far I have this, but it doesn't work since there is multiple modes selected.

if ($modesON == location.mode) {
 log.debug "Within Modes. The current mode is $location.mode"   
}
else {
 log.debug "Outside Modes. The current mode is $location.mode"   
}

If you have an input with multiple: true specified, then you get a list of objects regardless of how many are selected, just like you would if the input was for a specific device/capability. Groovy collection methods can come to the rescue here. Say you have:

input "onOnlyInModes", "mode", title: "Turn on only when mode is", multiple: true, required: false

Then you could do a check like this:

if onOnlyInModes?.contains(location.mode) {
  // the current mode is one of the modes selected
}

The alternative mode selector is using mode() instead of input(), and in that case you'd also get a list since there is no way to prohibit multiple selection in that case. (Oh, and in case you're curious, I looked up more in the meantime. I wasn't clear above about whether the platform handles mode restrictions automatically for single-page apps. It will because it provides a mode() input by default without you needing to add one, but the distinction is really that mode() does everything for you, whereas you need to provide your own logic where needed with input(). I've always done the latter because it lets you give the user more control over exactly what does/doesn't happen, and even if it's a simple app where that doesn't matter, it's easier for me to remember...clearly. :laughing: )

tl;dr I guess .contains() is the method you're looking for, which works on most (all?) Groovy collections. (I added the null safe operator ? so you don't get an exception thrown if the collection happens to be null; in that case, now the whole thing will just return null, which will evaluate to false and work with your if under Groovy truth.)

2 Likes

Thanks, all of this has been helpful.

I managed to build a motion lighting app around how I use hubitat. I have no gain in terms of speed when it comes to comparing the Hubitat Motion Lighting App and my own.

Now I can apply a lot of what I learned and start building other applications and drivers I've been aiming to do!

Thank you again for your help and guidance!!! I'm sure I will bother you more. :slight_smile:

2 Likes

@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...