flua — user guide for QA developers
flua runs Fibaro QuickApps offline on your Mac or PC. You write the same
Lua you'd write on the HC3 — QuickApp, fibaro, api, net, mqtt — and
flua provides the rest of the world: a simulated HC3, real network access, a
virtual clock, a proper debugger with breakpoints, and a one-command path back
to the HC3 as a .fqa file.
The HC3 editor is tiny and debugging there means print() statements. flua
exists so you can develop in your real editor, with real tooling, and upload
when it works.
- Install
- Your first QuickApp
- Running QAs
- VS Code setup
- QA directives (
--%%) - Multi-file QAs
- The offline HC3
- Loading QAs at runtime
- Network clients
- Deploying to the HC3
- Bringing an HC3 QA home
- Static checks
- flua extensions (
_FLUA)
Install
Python 3.11+. lupa (which bundles Lua) is the only runtime dependency.
pip install fibaro-flua # from PyPI — installs the flua command
From a checkout (development):
cd flua
python3 -m venv .venv
.venv/bin/pip install -e ".[dev]"
flua --version
Your first QuickApp
A QuickApp is an ordinary Lua file. QuickApp:onInit runs exactly like on
the HC3, and self:debug prints immediately (unlike the HC3, where you'd
only see it in the debug window):
--%%name:hello
-- --------------- EOH ---------------
function QuickApp:onInit()
self:debug("I started")
setTimeout(function() self:debug("one second later") end, 1000)
end
Save it as hello.lua and run it:
.venv/bin/flua hello.lua
Every .lua file you pass is a QuickApp — it gets QuickApp, fibaro,
api, net, mqtt, timers, and runs onInit exactly like on the HC3.
Anything that has nothing left to do (no timers, no network connections)
makes flua exit; QAs with timers or open connections keep running.
Running QAs
.venv/bin/flua script.lua # one QA
.venv/bin/flua qa1.lua qa2.lua # several QAs, isolated, can talk to each other
.venv/bin/flua -e 'setTimeout(function() print("hi") end, 100)'
.venv/bin/flua --run-for 5 script.lua # at least 5 s, then exit when idle
.venv/bin/flua --run-for -3 script.lua # exactly 3 s
Virtual time — great for testing long-running logic:
.venv/bin/flua --speed 60 script.lua # 60x faster
.venv/bin/flua --instant script.lua # timers fire now, os.time() jumps ahead
VS Code setup
The repo ships .vscode/launch.json with three configurations:
- Flua: Run Current File — runs the file in the panel.
- Flua: Run Current File (Terminal) — runs it in the integrated terminal
(pick this one for
self:debugoutput you can scroll). - Flua: Debug Current File (mobdebug) — real debugging: breakpoints,
stepping, variable inspection. Install the VS Code extension
Lua MobDebug (
alexeymelnichuk.lua-mobdebug), open your QA, press F5 with the mobdebug configuration selected. flua waits for the debugger before running your code.
While you step through code, print/self:debug output appears immediately
— logs never queue behind a paused program.
The fastest loop for tinkering:
.venv/bin/flua --watch script.lua
flua restarts the QA whenever you save the file (or any --%%file file).
Ctrl-C stops it. Works on top of the debugger too.
QA directives (--%%)
Directives are ordinary Lua comments at the top of the file. flua parses them before running; the HC3 ignores them — the same file runs in both places. Parsing stops at the end-of-header marker:
--%%name:my-qa
-- --------------- EOH ---------------
-- nothing below this line is parsed as a directive
| Directive | Meaning |
|---|---|
--%%name:x |
the device/QA name (_FLUA.config.name) |
--%%type:com.fibaro.binarySwitch |
device type (defaults to binarySwitch; unknown types are an error) |
--%%properties:value=false |
default device properties (plural form) |
--%%property:value=true |
raw property, scalar values; repeatable, merges |
--%%var:name=value |
initializes a QuickApp variable (string values; self:getVariable(name)) |
--%%uid:... |
sets quickAppUuid |
--%%description:... |
sets userDescription |
--%%model:... |
sets model |
--%%build:7 |
sets buildNumber (a number) |
--%%manufacturer:... |
sets manufacturer |
--%%file:lib.lua,lib |
extra QA file, loads before main (see below) |
--%%speed:60 |
virtual time speed (global) |
--%%instant:true |
instant mode (global) |
--%%maxhours:48 |
stop after 48 virtual hours (global) |
--%%time:speed=2,instant=true,hours=48,start=2027/10/6 12:00:20 |
combined runtime settings (global) |
--%%time:2027/10/6 12:00:20 |
bare form: set the virtual start time |
Names follow plua: --%%name:value for scalars,
--%%name:sub1=val1,sub2=val2 for subparameters. A typo'd directive is
silently ignored at runtime — run flua --check to catch those.
The virtual clock starts now unless you set a start time — handy for
testing dates: leap years, DST switches, New Year logic. Combine with
--instant to fast-forward through the interesting moments:
.venv/bin/flua --start "2027/12/31 23:59:50" script.lua
# or in the file: --%%time:start=2027/12/31 23:59:50,instant=true
Multi-file QAs
On the HC3 a QA is a set of named Lua files — one is main. flua keeps your
files on disk and declares the extras in the main file:
--%%file:lib.lua,lib
--%%file:util.lua,util
-- --------------- EOH ---------------
print(helper())
Files load in declaration order, main loads last. Paths resolve relative
to the main file's directory. See examples/multifile.lua. The HC3's file
API works offline too (api.get('/quickApp/' .. _FLUA.qaId .. '/files') etc.).
The offline HC3
--seed loads a simulated house (see examples/house.json):
.venv/bin/flua --seed examples/house.json script.lua
Your QAs become devices (ids from 5000) in the same simulated HC3, so everything works between them:
api.get/post/put/delete— the real HC3 REST surface (devices, plugins, variables, globals, scenes, alarms, profiles, refreshStates, …)fibaro.call(id, 'turnOn')— calls another QA's QuickApp methodfibaro.emitCustomEvent(name)→QuickApp:onCustomEvent(name)handlersself:setVariable/getVariable— persistent, survives restartsGET /refreshStates?last=N— the HC3's polling change feed
QAs find each other by name with fibaro.getIds({type = "quickApp"}) — see
examples/qa3.lua and examples/qa4.lua calling each other.
Loading QAs at runtime
From VS Code you often run one QA — let it bring up the QAs it needs:
local id = _FLUA.loadQAfromFile("examples/qa3.lua") -- annotations parsed
local id2 = _FLUA.loadQAfromString([[ ...inline QA... ]])
fibaro.call(id, "turnOn") -- immediately reachable
Both return the new QA id (or nil, error). examples/dynamic.lua demos it.
Network clients
Real network, HC3-style APIs, all asynchronous through the pump (callbacks run in your QA, timers keep running while requests are in flight):
net.HTTPClient()→request(url, {options, success, error})—examples/http.luanet.TCPSocket({timeout=ms})→connect/send/read/readUntil/close—examples/tcp.luanet.UDPSocket({broadcast, timeout})→sendTo/receive—examples/udp.luanet.WebSocketClient()/WebSocketClientTls()→addEventListener,connect,send—examples/websocket.luamqtt.Client.connect(uri, options)→subscribe/publish/unsubscribe/disconnect,mqtt.QoS—examples/mqtt.lua
Deploying to the HC3
.venv/bin/flua export script.lua -o myqa.fqa
Upload myqa.fqa through the HC3 web UI (Create QuickApp → import). The
package follows the HC3's schema — only the properties the HC3 accepts
travel; dynamic properties are left out.
Bringing an HC3 QA home
Export the QA from the HC3 UI (.fqa), then:
.venv/bin/flua unpack myqa.fqa -d myqa-project/
You get a runnable flua project: main.lua with generated --%% directives
and one file per QA file, ready for editing and debugging.
Static checks
.venv/bin/flua --check script.lua
Checks syntax, unknown --%% directives (typos), and deprecated API calls.
Warnings don't fail the run; syntax errors exit 1. Good in CI.
flua extensions (_FLUA)
Everything HC3-compatible is a plain global (QuickApp, fibaro, api,
net, mqtt, json, …). flua-specific helpers live on _FLUA so your code
stays portable:
_FLUA.qaId— this QA's id;_FLUA.config— this QA's config table_FLUA.arg— the file path /-emarker_FLUA.exit(code)— stop the engine (vsexit(code)which stops only this QA)_FLUA.qa(id)— another QA's QuickApp instance (lupa proxy)_FLUA.loadQAfromFile(path)/_FLUA.loadQAfromString(code)— dynamic QA loading_FLUA.async.run(fn)/_FLUA.async.await(worker)/_FLUA.async.wait(ms)— coroutine awaits_FLUA.setTimeout(fn, ms, qaId)— timer with explicit QA attributionif _FLUA then— detect flua at runtime
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fibaro_flua-0.1.1.tar.gz.
File metadata
- Download URL: fibaro_flua-0.1.1.tar.gz
- Upload date:
- Size: 170.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b8fbb4358b0ede2567436b9df10e9df4336a2c99f8e6f54336d934b18ecbc796
|
|
| MD5 |
43636ea4a66c1bd1d282009cba952e88
|
|
| BLAKE2b-256 |
cde1b6c2781fbac21152bb45f427ecf2d88e6293cd51a3e2415dd6fe6b313e74
|
File details
Details for the file fibaro_flua-0.1.1-py3-none-any.whl.
File metadata
- Download URL: fibaro_flua-0.1.1-py3-none-any.whl
- Upload date:
- Size: 141.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9b1cd405d7ef19dde26ba7801b7b00c71dd7a6dab0ca310b9a9c597bcb9a4b3f
|
|
| MD5 |
927997a842a85754e1f994a82e9d2a45
|
|
| BLAKE2b-256 |
1983272233b7ae32d53206367ab917e9c5b96897bebf463a7f165a2ee4af0885
|