Real QuickApps talk to real services: weather APIs, MQTT brokers, push
notifications. All of them want credentials — and hardcoding the key in the
QA file is bad news. The file gets shared on the forum, committed to git,
uploaded to the controller. flua has a small config machinery that keeps
the secret on your machine and out of the code: a Lua config file, an
environment chain, and --%%var directives.
Drop a .flua.lua next to your QA (or in your home
directory, ~/.flua.lua — handy for per-machine settings). It
is plain Lua data:
-- .flua.lua (next to the QA, or ~/.flua.lua)
return {
weatherKey = "sk-live-3f2a9c41", -- the real key, on this machine only
endpoints = { base = "https://api.weather.example", retries = 3 },
}
flua loads it once and merges it into _FLUA.config — QA
code can read it directly as _FLUA.config.endpoints.retries.
The file is Lua, not a static format: you can compute values, pull in
another file with dofile, anything.
--%%var:name=expr sets a QuickApp variable at startup. The
expression is evaluated as Lua with config (your merged
config) and os in scope — so it can read the config file or
the environment. Any Lua value is legal, including tables:
--%%name:weather --%%type:com.fibaro.binarySwitch --%%var:key=config.weatherKey,"" --%%var:base=config.endpoints.base --%%var:retries=config.endpoints.retries --%%var:envKey=os.getenv("WEATHER_KEY") --%%var:opt=config.nothere --%%var:policy={alerts=true,units="C"} -- --------------- EOH --------------- function QuickApp:onInit() print("base:", self:getVariable("base")) print("retries:", self:getVariable("retries")) print("envKey:", self:getVariable("envKey")) print("opt unset:", self:getVariable("opt") == "") print("policy:", json.encode(self:getVariable("policy"))) print("key present:", self:getVariable("key") ~= "") end
Run it and the variables are there — read them like on the HC3 with
self:getVariable(name):
[weather5000]: base: https://api.weather.example
[weather5000]: retries: 3
[weather5000]: envKey: the-key-for-this-machine
[weather5000]: opt unset: true
[weather5000]: policy: {"units": "C", "alerts": true}
[weather5000]: key present: true
Note that --%%var:key=... never appears in the QA body —
the directive is the whole story, and the QA code just reads the variable.
The variable part is the HC3-native mechanism, so the QA logic is identical
online and offline.
And that little quoted string after the comma on the
key line? It never runs — it is the package value, and
it is the subject of the last section. Hang on to it.
os.getenv(name) in a --%%var expression reads
through flua's environment chain: a local .env in the
directory you run from, then ~/.env, then your shell
environment. Files are plain KEY=value lines and are re-read
when they change — perfect for API tokens that differ per machine:
# .env — per-machine, never committed WEATHER_KEY=the-key-for-this-machine
nil (like
config.nothere) simply leaves the variable unset — no error.--%%var line decides which variables exist. Use --%%include (see USAGE.md) for shared directive
defaults.Two destinations, two rules — and both are what you want:
--tool uploadQA as a new QA): the
real values. Your controller gets the credentials the QA needs, so
the QA works there unchanged. (updateQA never syncs
variables — the values you set on the HC3 win.)--tool pack, flua export —
the .fqa you post on the forum): the package value
where you declared one, the real value otherwise.So before you pack for the forum, give every secret variable a package
value. That is the quoted string after the comma —
--%%var:key=config.weatherKey,"<set to service weather
key>" — or the Plua spelling --%%clobber:key=<set to
service weather key> on its own line. Pack the weather QA and the
package comes out scrubbed:
# flua --tool pack weather.lua — then look inside weather.fqa
quickAppVariables:
base = "https://api.weather.example"
envKey = "the-key-for-this-machine"
key = "<set to service weather key>"
policy = {"alerts": true, "units": "C"}
retries = 3
The real key appears nowhere — not in the variables, and not in the
packaged main file either: its --%%var header lines are
rewritten to the same literals. The placeholder itself never runs:
flua weather.lua still gives you the real value on your
machine, which is the whole point. And if an expression evaluates to nil
(opt = config.nothere), that variable simply stays out of the
package.
One last thing: when you downloadQA a QA home, its
variables come along as literal --%%var header lines — move
those values into .flua.lua (as config.xxx)
before you commit or share the project.
Next time: real integrations — HTTP and MQTT clients talking to actual services, with the mock-server trick from post 8 for testing.
Config + env + variables: USAGE.md ("The Lua config file", "--%%var") · flua: https://github.com/jangabrielsson/flua