What is smart things crontab format?


(Ron) #1

The documentation only provides on example and doesn’t explain the fields.

http://docs.smartthings.com/en/latest/smartapp-developers-guide/scheduling.html
// execute handlerMethod every hour on the half hour.
schedule(“0 30 * * * ?”, handlerMethod)

Normal crontab (unix) is as follows

min hour dayOfMonth month dayOfWeek commandToRun

So every half hour should be
"30 * * * *" in crontab

So what are the other level that smart things uses ?
What is the “?” for ?

It would have been really helpful to document this in … the documentation :smile:


(Ron) #2

Never mind I think I figured it out. Seem to match quartz format
http://quartz-scheduler.org/documentation/quartz-1.x/tutorials/crontrigger

Would still be good if ST documentation was improved.


(Jim Anderson) #3

I updated the docs to try and clarify where to get more information on the cron syntax and parameters (from the tutorial, as you found).


(Ron) #4

I have a question, can I schedule two cron expressions in one smart app ?

I have a smart app which has two schedules allowed. One for M-F and one for S-S.

I want to schedule both upon config and not need to change but the documentation says “Each SmartApp or device-type handler can only have one handler method scheduled at any time.”

So I think this means when my event fires I need to see if the schedule is changing from weekday to weekend and vis-versa. Then I have to “unschedule” and schedule the new time.

Later in the documentation it says unschedule is expensive
"unschedule can take several seconds to execute
As discussed above, unschedule is currently a potentially expensive operation." which implies it should be avoided but you really can’t avoid if only one schedule is allowed at any time.


(Chuck Pearce) #5

You may just want to execute it each day at a given time, then have a check at the beginning of the function to check what day it is and see if that is a valid day and apply weekend/weekday logic at that point, rather than using the cron.


(Ron) #6

This is how I currently have it coded, but there is a problem with the way the schedule method is implemented. Schedule only looks at the time of the date/time passed. This leads to a bug in my processing which I need more detail to explain.

My current code takes it’s event action, then schedules tomorrow’s event based on what day it is currently. Therefore On Sunday-Thursday it set’s the M-F time. On Friday and Saturday it sets the Sat-Sunday time. Since it is always scheduling for the next day.

This works fine but it fires twice on Friday because I have M-F scheduled for 7am and S-S scheduled for 9am. Therefore on Friday it tries to set Saturday’s 9am schedule. But the schedule method ignores the date and assumes I want the next available 9am which will happen in two hours, on the same day (friday) and not tomorrow’s 9am which is what I wanted to schedule.

I could add code to check if the 9am event matches the day and ignore it if it doesn’t. Which I might do. But this logic could be unnecessary if I could simply schedule two cron events as one might expect they can do.

Actually using this approach I have to unschedule the previous schedule anyway. I currently just unschedule every day and set the new schedule even if it isn’t changing. I could just change on Friday and Sunday but again the bug exists.

I think I might as well change my code to the cron style to fix the issue and code it to switch only on Friday and Sunday. If two schedules were allowed I could just set them and forget them until a configuration change was made. That would have been nice.


(sidjohn1) #7

W/o seeing your code I can’t be for sure, but it sounds like unscheulding your current cron before scheduling a new one might fix your issue. This a sample of code I’m using to poll a device every minuet when it’s on and every 30 min when it’s not.

if (evt.value == "on") {
	unschedule("poll")
    schedule("0 0/1 * * * ?", "poll")
} else {
unschedule("poll")
    schedule("0 0/30 * * * ?", "poll") }

(Ron) #8

Why does this cron fail ?
java.lang.RuntimeException: CronExpression ‘0 0 9 * * 1,7’ is invalid,. @ line 73

From the documentation
http://www.quartz-scheduler.org/documentation/quartz-1.x/tutorials/TutorialLesson06

// Quartz format "Sec Min Hour DayOfMonth Month DayOfWeek"
isn’t this
0 seconds
0 Min
9 Hour 9am

  • any day of Month
  • any month
    1,7 Sunday and Saturday
    "0 0 9 * * 1,7"

(Ron) #9

Figured it out needed to change to

min=0
hour=9
schedDays=“SAT,SUN”
“0 $min $hour ? * $schedDays”


(Thompson) #10

I’ve been playing around with crontrigger for a few days now and noticed that the “DayOfWeek” field is no longer valid once app is installed. You should be able to input either a number or a day string (1-7 or SUN-SAT). SmartThings seems to only allow the no value input of “?”. Is anyone else having this issue?


(Ron) #11

@snoopyjoe What is your full schedule string ?
Some rules are not allowed such as “Day of the Week” combined with “Day of the Month”

This is documented on the quartz schedule docs which I think ST uses
http://www.quartz-scheduler.org/documentation/quartz-1.x/tutorials/crontrigger
"Support for specifying both a day-of-week and a day-of-month value is not complete (you must currently use the ‘?’ character in one of these fields)."

I use SAT-SUN and MON-FRI and have not had a problem. I think I had issue trying to use number. When do you see the issue ? Is it throwing errors when you schedule ?

Here is my code which is working.

def setNextSchedule()
{
unschedule()
def now=new Date()
def tz = location.timeZone
def dayString = now.format(“EEE”,tz)
def schedTime
def schedDays
if (dayString.equals(‘Fri’) || dayString.equals(‘Sat’)) {
// Next event will be Sat or Sunday
log.debug "Using Sat/Sun Schedule $time2"
schedTime = timeToday(time2,tz)
schedDays = “SAT,SUN”
} else {
log.debug "Using M-F Schedule $time1"
schedTime = timeToday(time1,tz)
schedDays = “MON-FRI”
}
def hour = schedTime.format(“H”,tz)
def min = schedTime.format(“m”,tz)
log.debug “$dayString: Scheduling $schedDays $hour:$min”
// Quartz crontab format Sec Min Hour DayOfMonth Month DayOfWeek
// example 0 30 9 ? * SAT,SUN for 9:30am Saturday and Sunday
schedule(“0 $min $hour ? * $schedDays”, “scheduleCheck”)
}

Full code here


(Thompson) #12

Sorry, I did not see the note at the bottom of the doc stating that “Day of month” and “Day of Week” cannot be used together. Long coding days has its draw backs. So in order to give a user both a weekly schedule or a monthly schedule option, you would need to provide two separate schedules with two separate crontab strings correct? Here’s my schedule as of now. I’ve removed an option for weekly schedules. I’ll work on a second weekly option later.

subscribe(meter, "energy", meterHandler)
def RepeatTime = "0 0 ${repeatHour} ${repeatDate} * ?"
schedule(RepeatTime, meterHandler)

(Ron) #13

Again I would need to see more of your code to know the correct answer to your question but you can only have one “active” schedule at a time

http://docs.smartthings.com/en/latest/smartapp-developers-guide/scheduling.html
"Each SmartApp or device-type handler can only have one handler method scheduled at any time. This means that, unlike runIn or runOnce, a job created with schedule must either execute or be canceled with the unschedule method before you can schedule another job with the same method. The schedule method does not accept the overwrite option like runOnce and runIn."

So you would need to write some code which would determine that the next schedule should be and then set that schedule.

I would probably make mutually exclusive options to the user. You can have this schedule or that schedule not both…but I don’t know you app so maybe you are writing something more general that needs more options in which case you need to do the extra work of figuring out what is next as I have mentioned.

My app allows two schedules M-F and S-S. I then check if it is Friday then I switch to the S-S preferences, if it is Sunday then I switch to the M-F preferences. That can be seen in the code I provided above.

Good Luck :four_leaf_clover:


(Thompson) #14

What I have are options for Daily, Weekly(Temp removed), and Monthly schedules. Input for daily and monthly options are passed to the same schedule. But if the weekly option is chosen then that input would be passed to a second and separate schedule. Only one schedule would run and to avoid any runtime errors unschedule() during install should only allow one schedule to run correct? Here’s my code minus the message section

preferences
{
section(“Select which meter to collect from…”)
{
input(name: “meter”, type: “capability.powerMeter”, title: “Collect from this meter…”, required: true, multiple: false, description: null)
}

section("When to send report")
{      
    input(name: "repeatHour", type: "number", title: "Everyday at...(0-23). Default=12p", required: true, defaultValue:12)
    input(name: "repeatDate", type: "text", title: "Monthly on which date...(1-31). No input = everyday", required: false, defaultValue: "*")
}

section("When to reset data(Optional)")
{
    input(name: "optional", type: "bool", title: "Run Reset schedule??", required: true)
    input(name: "resetHour", type: "number", title: "Everyday at...(0-23). Default=12p", required: false, defaultValue:12)
    input(name: "resetDate", type: "text", title: "Monthly on which date...(1-31). No input = everyday", required: false, defaultValue: "*")
}

section 
{
    input("recipients", "contact", title: "Send notifications to") 
    {
    input(name: "sms", type: "phone", title: "Send A Text To", description: null, required: false)
    input(name: "pushNotification", type: "bool", title: "Send a push notification", description: null, defaultValue: true)
    }
}

}

def installed()
{
log.debug "Installed with settings: ${settings}"
initialize()
}

def updated()
{
log.debug "Updated with settings: ${settings}"
unsubscribe()
initialize()
}

def initialize()
{
//subscribe(meter, “energy”, meterHandler)
def RepeatTime = "0 0 ${repeatHour} ${repeatDate} * ?"
schedule(RepeatTime, meterHandler)

if(optional == true)
{
    def ResetTime = "0 0 ${resetHour} ${resetDate} * ?"
    schedule(ResetTime, meterReset)
}

}

def meterHandler()
{
def msg = "${meter} used ${meter.latestValue(“energy”)} kWh."
sendMessage(msg)
}

def meterReset()
{
meter.reset()
}

def sendMessage(msg)


(Thompson) #15

Ok so I keep getting a throwable exception error on this section. No other schedule is running. I’ve tried running the crontab as “0 0 12 * * MON” as well and still get the same error. Is there something I’m forgetting?

def RepeatTime = "0 0 ${repeatHour} * * MON"
schedule(RepeatTime, meterHandler)

(Ron) #16

Another note from the quarts documentation page I provided
Pay attention to the effects of ‘?’ and ‘*’ in the day-of-week and day-of-month fields!

  • is a value meaning EVERY
    ? means no setting

So you ARE using both day-of-week and a day-of-month.

Change to the following and it should work.
“0 0 ${repeatHour} ${repeatDate} ? ?”

You need ? in either Day-Of-Week or Day-Of-Month if one or the other is set. If you want to change to
"0 0 ${repeatHour} ${repeatDate} ${repeatDays} ?" Then you just have to make sure your code sets either repeatDate or repeatDays to ? when the other has a value.

This drove me CRAZY for a while before I “gleamed” it from examples. It’s not really clear in the documentation how this behaves.