Every HC3 QA has timers — and testing them on the real controller means waiting. A QA that acts at 22:00, polls every hour, or re-arms after ten quiet minutes is painful to develop. flua runs your QA on a virtual clock: the QA believes it is any date and time you choose, and you can fast-forward through hours in seconds.
A lamp that turns itself on at 22:00 — with the scenario configured
in the file, so plain F5 in VS Code (or a bare flua
night_lamp.lua) runs it as-is:
--%%name:NightLamp
--%%type:com.fibaro.binarySwitch
--%%time:start=2027/6/21 21:59:50,speed=60
function QuickApp:onInit()
self:debug("booted at", os.date("%Y/%m/%d %H:%M:%S"))
local now = os.date("*t")
local target = os.time{year=now.year, month=now.month, day=now.day, hour=22, min=0}
if os.time() > target then target = target + 24 * 3600 end
setTimeout(function()
self:updateProperty("value", true)
self:debug("good night — lamp on at", os.date("%H:%M:%S"))
exit(0)
end, (target - os.time()) * 1000)
end
The --%%time: directive sets the virtual start time and
speed for this QA. Run it — no flags needed:
flua night_lamp.lua
The log tells the story — virtual time, not wall time:
[21.06.2027][21:59:51] NightLamp5000: booted at 2027/06/21 21:59:51 [21.06.2027][22:00:00] NightLamp5000: good night — lamp on at 22:00:00
The QA booted at 21:59 on June 21 2027 and fired exactly at 22:00:00 — in a fraction of a real second. Change the directive to a date where 22:00 already passed and the QA arms itself for tomorrow instead; either branch is one line away, no waiting for the actual sun.
For one-off experiments, the same parameters exist as command-line flags (they override the file):
flua --start "2027/6/21 21:59:50" --speed 60 night_lamp.lua flua --speed 3600 night_lamp.lua # 1 hour of virtual time per real second flua --instant night_lamp.lua # timers fire immediately, os.time() jumps flua --max-hours 24 night_lamp.lua # stop after a virtual day
...and as individual directives too: --%%speed:3600,
--%%instant:true, --%%maxhours:24.
--instant is the one to remember: every
setTimeout/setInterval fires at once and
os.time() jumps by each delay, so a QA that loops through a whole
day completes in a blink — ideal for catching infinite loops and off-by-one
timer bugs before the controller finds them for you. The dates in the
log are the QA's time, not your wall clock.
And because the clock is virtual, it pauses while you debug — a breakpoint never races against a timer (next post).
More timer patterns: flua --examples → timers.lua,
timers2.lua, sleep.lua · flua: https://github.com/jangabrielsson/flua