Jump to content

Welcome to Smart Home Forum by FIBARO

Dear Guest,

 

as you can notice parts of Smart Home Forum by FIBARO is not available for you. You have to register in order to view all content and post in our community. Don't worry! Registration is a simple free process that requires minimal information for you to sign up. Become a part of of Smart Home Forum by FIBARO by creating an account.

 

As a member you can:

  •     Start new topics and reply to others
  •     Follow topics and users to get email updates
  •     Get your own profile page and make new friends
  •     Send personal messages
  •     ... and learn a lot about our system!

 

Regards,

Smart Home Forum by FIBARO Team


Recommended Posts

Posted (edited)

scheduler.lua

Repo & Code: 

Please login or register to see this link.



A Lua port of the Python 

Please login or register to see this link.

 library
by Daniel Bader  job scheduling for humans.

Designed for 

Please login or register to see this link.

 and Fibaro HC3 QuickApps,
but works in plain Lua too.

Please login or register to see this code.


Why a port?

The Python schedule API is a fluent builder pattern that reads almost like
English. Lua's metatables (specifically __index as a function) let us
reproduce both the bare-property chaining (.minutes, .day, .monday) and
the method chaining (:at(...), :to(...), :tag(...)) without the user
having to type extra parentheses.

Two execution models are provided:

Mode Use when
Polling  schedule.run_pending() Plain Lua, your own event loop
Reactive  schedule.start() plua / HC3 / anything with setTimeout

Plain interval jobs are drift-free (anchored to their first scheduled
fire). Wall-clock anchored jobs (:at("HH:MM"), weekday jobs) are tied to
local time and self-correct.


Installation

plua / Fibaro HC3 QuickApp

There's no require in QA context, so scheduler.lua installs itself onto
fibaro.schedule when loaded via the multi-file directive:

Please login or register to see this code.

Plain Lua

Please login or register to see this code.


Differences vs. the Python original

  • No timezone support. All times use os.time() / os.date() in local
    time. The Python version's at(time, tz=...) is not implemented.
  • do is a Lua keyword, so the method is :do_(fn, ...) (with a
    trailing underscore) instead of Python's .do(fn, ...).
  • Property access uses ., not :. In Lua you cannot write obj:property
    without a call. So:
    • every().monday  (property)
    • every():monday  (Lua syntax error)
    • every().day:at("10:30")  (.day is a property, :at is a method)
  • Two execution models. The Python version only has polling. This port
    adds :start() / :stop() for native setTimeout-based scheduling.
  • until_() instead of until (Lua keyword).

Grammar / chaining cheat sheet

Please login or register to see this code.

Singular forms (.second, .minute, .hour, .day, .week) require an
interval of 1; using every(2).minute raises IntervalError.

Weekdays require interval of 1 (every().monday, never every(2).monday).

at() format depends on the unit:

Unit Format
.day / weekday HH:MM or HH:MM:SS
.hour :MM, MM:SS, or :MM:SS
.minute :SS

until_(when) accepts:

  • a number — epoch seconds (large), or seconds-from-now (small)
  • "YYYY-MM-DD HH:MM:SS" (or without seconds, or just date)
  • "HH:MM" / "HH:MM:SS" — today

Examples

Time-based intervals

Please login or register to see this code.

Wall-clock times

Please login or register to see this code.

Randomized intervals

Please login or register to see this code.

Sunrise & sunset

Sun events are computed per upcoming day (so future days use the correct
sunrise/sunset for that date, not today's value). On HC3 the location is read
automatically from api.get("/settings/location"). Elsewhere, set it once:

Please login or register to see this code.

Then:

Please login or register to see this code.

Sun-based jobs require interval 1 and either .day or a weekday property.
They return a useful next-run because _schedule_next_run scans the next 14
calendar days for the first matching event in the future.

Passing arguments

Please login or register to see this code.

Tags & bulk cancellation

Please login or register to see this code.

Self-cancelling job

Return schedule.CancelJob from the function to unschedule it:

Please login or register to see this code.

Deadline

Please login or register to see this code.

Inspection

Please login or register to see this code.

Running

Please login or register to see this code.

Multiple schedulers

The module-level schedule.every, schedule.run_pending, etc. all use a
shared default scheduler. You can also create independent ones:

Please login or register to see this code.


API reference

Module-level (default scheduler)

Function Description
schedule.every([n]) Start a new job, returns a Job
schedule.run_pending() Run any due jobs (polling mode)
schedule.run_all([delay]) Run all jobs immediately, optionally with delay between
schedule.get_jobs([tag]) List all jobs (optionally filtered by tag)
schedule.clear([tag]) Cancel all jobs (or all with matching tag)
schedule.cancel_job(job) Cancel a specific job
schedule.next_run([tag]) Epoch of next run, or nil
schedule.idle_seconds() Seconds until next run, or nil
schedule.start() Begin reactive (setTimeout) execution
schedule.stop() Stop reactive execution and clear timers
schedule.jobs() Direct access to the list of jobs
schedule.CancelJob Sentinel — return from a job to unschedule it

Job methods (all chainable, return the job)

Method Description
:to(latest) Use a random interval in [interval, latest]
:at("HH:MM[:SS]") Anchor to a wall-clock time
:at_sunrise([offset]) Anchor to sunrise (offset in seconds, may be negative)
:at_sunset([offset]) Anchor to sunset
:at_sunrise_twilight([offset]) Anchor to civil-twilight sunrise
:at_sunset_twilight([offset]) Anchor to civil-twilight sunset
:tag(...) Attach one or more tags
:until_(when) Set a deadline after which the job is cancelled
:do_(fn, ...) Register the function and start the job

Job properties (no parens)

  • Units: .second(s), .minute(s), .hour(s), .day(s), .week(s)
  • Weekdays: .monday, .tuesday, .wednesday, .thursday, .friday,
    .saturday, .sunday

Scheduler class

schedule.Scheduler.new() returns a fresh instance with the same methods
as the module-level functions (using :), e.g. s:every(2):minutes,
s:run_pending(), s:start(), etc.

Sun helpers

Function Description
schedule.set_location(lat, lon) Set the lat/lon used for sun calculations (auto-read from api.get("/settings/location") on HC3)
schedule.sunCalc([time]) Returns sunrise, sunset, sunrise_twilight, sunset_twilight as seconds-since-midnight on the local date of time (default os.time()). Returns -1 for an event if the sun never rises/sets that day.

Testing

Please login or register to see this code.

QA_scheduler.lua exercises ~10 features and uses speed.lua (also in this
repo) to compress a 24-hour simulation into seconds by hijacking setTimeout
and os.time.


Credits

  • Original library: 

    Please login or register to see this link.

     by
    Daniel Bader and contributors — MIT-licensed.
  • Inspirations cited by the original:
    • Adam Wiggins' "

      Please login or register to see this link.

      "
    • The Ruby 

      Please login or register to see this link.

       module
  • Lua port by Jan Gabrielsson, 2026.
Edited by jgab
  • jgab changed the title to Schedule lib

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...