[SOLVED] Scheduler?

I didn’t know I needed a scheduler, but after returning from a trip this weekend, I think I might.

I want to control Home/Away on my Nest when:

my OwnTracks region (roughly 3 hours in car travel time) transitions to “enter”
OR
at a single instance specific date and time (i.e., not a timer, not a recurring alarm, not a day of the week, nor a certain time of day).

These examples (https://github.com/openhab/openhab1-addons/wiki/AlarmClock) don’t quite fit my scenario.

I searched but did not find whether an “integrated” scheduler is now part of OH2. Is this (https://github.com/foxy82/openhab2-addons/releases/tag/Timer_Alpha1) by @neil_renaud “stable” and supported?

My preference is to keep everything w/in my LAN as much as possible (e.g., calDAV vs. Google Calendar). I realize that the Nest binding requires an external connection, but the less dependent I am on external capability, the better.

I’ve read a bit about the calDAV binding and it seems like another big learning curve for me. I just went through weeks of MQTT, SSL, OwnTracks and rudimentary rules syntax learning and troubleshooting. I’m not sure I’m up to another large undertaking… particularly if what I’m thinking about may either be solved more simply or may have a simple implementation already built-in to recent OH2 releases.

I have OwnTracks regions set up to trigger actions on my Nest thermostat. I feel pretty good about this now being stable… but I found a gap in how I have this set up. The issue is that if I’m traveling without a location update (e.g., on a plane), the region transition doesn’t happen until I land. By this time I’m too close to home. I need a failsafe. The failsafe is to set a specific date and time for when the action must take place if it has not already happened.

Long ago I had set up an IFTTT applet linked to my Google Calendar which then triggers an action on my Nest thermostat. This uses a webhook to invoke the API take the necessary action. The problem with this is that the IFTTT webhook does not support redirection so I have to hard code that into the URL. If Nest changes the redirection then the webhook fails. Monitoring changes to that and updating the IFTTT webhook somewhat defeats the purpose of “automation” - at least the set it and forget it part. Thus, I want to schedule and event in openHAB to take this action locally and rely on the Nest binding (more reliable than hard coding the API via my own webhook) to do the work. I already use the Nest binding so this would just add another trigger to the rule.

Net-net - what is the best way to set up a scheduled event in openHAB?

Thanks.

Mike

There is no such capability that is part of the baseline as far as I know.

If it is not listed in PaperUI then the answer is no, or at least not yet. The fact that it is called Alpha implies it is under development and not yet ready for wider test. And I don’t really see what an addon will do because the hardest part of this whole thing is creating a way to enter the time and date you want to schedule the event for which an addon can’t do. That will require changes to all the sitemap based UIs (ClassicUI, BasicUI, Android app, iOS app).

CalDAV works with OwnCloud I believe.

I don’t understand why the alarm clock examples cannot be used to implement this, if you do not want to use the CalDav binding.

Why not link it to your openHAB through myopenhab.org using either the webhook channel or the openHAB channel? You can set the home/away status of Nest through an Item in openHAB and command that Item from IFTTT.

There are lots of ways but no best way. And none of the ways are particularly satisfactory.

I re-read those alarm clock examples… still don’t see it. For example, I want to set an event to trigger on July 6, 2018 at 2:30pm (preferably parameterized rather than hard coded). Then I want a rule to trigger when that event occurs.

You create Items to represent each of the fields:

Group:String Alarm
Number Year (Alarm)
String Month (Alarm)
Number Day (Alarm)
Number Hour (Alarm)
Number Min (Alarm)

Put them on your sitemap using Selections, Sliders, or whatever is appropriate.

When any one of them change and at System started trigger a Rule. The Rule uses all the parameters to create a Timer to go off at the date and time represented by the fields.

var Timer alarm = null
rule "Alarm changed"
when
    Member of Alarm changed
then
    val dtAsString = Year.state.toString + "-" + Month.state.toString + "-" + Day.state.toString + "T" + Hour.state.toString + ":" + Min.state.toString

    alarm?.cancel
    alarm = createTimer(new DateTime(dtAsString), [ | 
        // code to execute at the alarm time
    ]
end

I’ve not vetted the above, there might be typos. But this is the essence of all the alarm clock examples.

  1. Split the Date Time into separate fields
  2. Put the separate fields on your UI individually for change
  3. Use a Timer to go off at the configured time

Thanks Rich.

alarm?.cancel… is that question mark part of the syntax? And this statement, I take it, ensures that only the last datetime segment updated in the Alarm group ends up setting the final desired timer. Yes?

Yes, it is equivalent to

if(alarm !== null) alarm.cancel

Correct. You only want one of these timers running at a time. If you just reassign a new Timer to alarm without first cancelling the old one, the old one hangs around and will trigger when the time is up. The ? is just a cleaner way to avoid errors in the case where alarm is null, like it will be when OH first starts.

For the Sitemap alarm elements entry, is there a way to make the selection for day of the month to be dependent on the month selected? For example, If I choose June, the days that can be selected are 1-30, July 1-31, February (non-leap year) 1-28, February (leap year) 1-29?

Leaving the “days in month” question for later… I still don’t have the scheduled event rule executing successfully. I adapted your code template and have this statement to create the datetime string:

val currMonth = now.getMonthOfYear
if (SPT_house_VT_ReturnGeoFenceTime_Month.state >= currMonth)
{
    dtAsString = 
        now.getYear + "-" + 
        SPT_house_VT_ReturnGeoFenceTime_Month.state.toString + "-" + 
        SPT_house_VT_ReturnGeoFenceTime_Day.state.toString + "T" + 
        SPT_house_VT_ReturnGeoFenceTime_Hour.state.toString + ":" + 
        SPT_house_VT_ReturnGeoFenceTime_Min.state.toString
}
else
{
    dtAsString = 
        (now.getYear + 1) + "-" + 
        SPT_house_VT_ReturnGeoFenceTime_Month.state.toString + "-" + 
        SPT_house_VT_ReturnGeoFenceTime_Day.state.toString + "T" + 
        SPT_house_VT_ReturnGeoFenceTime_Hour.state.toString + ":" + 
        SPT_house_VT_ReturnGeoFenceTime_Min.state.toString
}

GeoFenceAlarm?.cancel
if (now.isBefore(DateTimeType.valueOf(dtAsString)).zonedDateTime.toInstant.toEpochMilli)
{
    GeoFenceAlarm = createTimer(new DateTime(dtAsString)) [ | 
        SPT_house_FF_backbr_ceiling_fan_speed_medium.sendCommand(ON)
        sendCommand(SPT_house_VT_nestStructure_Away, "HOME")
        sendCommand(SPT_house_FF_hallway_wall_sensor_thermostat_HVACMode, "HEAT_COOL")
    ]
}

But I get this error:
2018-7-21T12:0 is not in a valid format.

I have tried “hard coding” some datetime strings to see if they would process successfully but these did not work either.

  • 2018-7-21T12:00
  • 2018-07-21T12:0
  • 2018-07-21T12:00
  • 2018-JUL-21T12:0
  • 2018-JUL-21T12:00

Insights?

Thanks.

Mike

@rlkoshak

I’m not sure the value of infinity is high enough for the number of iterations of formatting that time string is possible. I think I’ve tried them all :wink:

Which tells me that I’m getting bit by DateTime and DateTimeType or something. My old data typing nemesis. Frustrating!

Help!

Mike

The format is ISO 8601 and the Wikipedia article is a pretty good place to start.

A quick glance seems to indicate that the seconds are required and the time zone offset may be required as well. Though I think I remember seeing examples with Joda DateTime that did not include the time zone.

OK, day, hour, minute had to be leading zero padded and the string had to include seconds (+ “00”).

dtAsString = 
    "-" + SPT_house_VT_ReturnGeoFenceTime_Month.state.format("%02d") +
    "-" + SPT_house_VT_ReturnGeoFenceTime_Day.state.format("%02d") +
    "T" + SPT_house_VT_ReturnGeoFenceTime_Hour.state.format("%02d") +
    ":" + SPT_house_VT_ReturnGeoFenceTime_Min.state.format("%02d") + ":00"

if (SPT_house_VT_ReturnGeoFenceTime_Month.state >= currMonth)
{
    dtAsString = (now.getYear).toString + dtAsString
}
else
{
    dtAsString = (now.getYear + 1).toString + dtAsString
}

GeoFenceAlarm?.cancel

// Make sure that the event trigger is in the future
if (now.isBefore((DateTimeType.valueOf(dtAsString)).zonedDateTime.toInstant.toEpochMilli))
{
    logInfo("SPT_house", "Return GeoFence Alarm set for " + dtAsString + ".")
    GeoFenceAlarm = createTimer(new DateTime(dtAsString)) [ | 
        logDebug("SPT_house", "Making sure Nest is set to HOME at ReturnTime if GeoFence didn't trigger.")
        if (SPT_house_VT_nestStructure_Away.state != "HOME")
        {
            logInfo("SPT_house", "Setting Nest HOME")

            // Turn ceiling fans on
            SPT_house_FF_backbr_ceiling_fan_speed_medium.sendCommand(ON)

            sendCommand(SPT_house_VT_nestStructure_Away, "HOME")
            sendCommand(SPT_house_FF_hallway_wall_sensor_thermostat_HVACMode, "HEAT_COOL")
        }
    ]
}

You’re going through a lot of effort to generate a string and then convert it to a DateTime, when you could just be going straight to a DateTime

val DateTime dt = new DateTime(
    if ((month.state as Number).intValue > now.getMonthOfYear) now.getYear else now.getYear + 1, 
    (month.state as Number).intValue, 
    (day.state as Number).intValue, 
    (hour.state as Number).intValue, 
    (minute.state as Number).intValue)

GeoFenceAlarm?.cancel
if (now.isBefore(dt)) {
    GeoFenceAlarm = createTimer(dt) [ | 
    // etc...

@5iver Scott,

Thanks for the simplified code. I am not proud and happily borrow (steal) code from anyone, anywhere and gladly accept donations :wink: Seriously, I am just learning (obviously) this language so most (all) of my coding is a Frankenstein’s monster of code snippets dug up from all over the web.

Thanks again!

Mike

1 Like

In case anyone finds this looking for the scheduler (https://github.com/foxy82/openhab2-addons/releases/tag/Timer_Alpha1)

I’ve moved away from openhab2 to home-assistant (which has better support for date times) as such I won’t be updating the Timer project above

Hey Scott,

Would this rule you wrote work for my Amazon nextAlarm rule PM’d you about?

The only piece I don’t know how to do is convert a date/time ITEM into a timer numeric value like you did based the break out of each variable.

Best, Jay

Nope… but if the last piece you need is to go from DateTimeType to Joda DateTime, try something like this…

reminderTimer = createTimer(new DateTime(triggeringItem.state.toString)) [|
    // do stuff
]

Thanks Scott, it worked perfectly! The next challenge is to add 10 seconds to the timer because it’s conflicting with the “actual reminder” speaking on the Echo from Amazon. OH is clobbering Amazon from talking which intern is blowing up the Echo binding . . .

Any suggestions?

reminder_Jay_tAlive = createTimer(new DateTime(triggeringItem.state.toString))

Best, Jay

1 Like
reminder_Jay_tAlive = createTimer((new DateTime(triggeringItem.state.toString)).plusSeconds(10))