The FluaHelper: the HC3's local-only API from your QA

Not everything on the HC3 is reachable over the REST API. The controller serves part of its surface only to local callers — QA code running on the box. The most useful casualty: the single-key QA variable endpoints. The list works fine, but GET /plugins/<id>/variables/<key> and the variable writes answer 500/501 to anything outside, which means no tool and no other QA can read or change a QA's internalStorage from the outside.

flua's fix: a small invisible helper QA deployed to the controller. It runs your call in the local context and reports back over a WebSocket to your laptop. Install it once:

flua --tool installHelper
# FluaHelper already installed (v1.3.0) — HC3 device id 4378

Re-running it is safe and idempotent — it finds the helper by its quickAppUuid (a renamed QA stays recognized), keeps the newest copy, and removes any older ones. The helper never dials anywhere on its own: it waits idle until flua needs it, then connects back on demand. One helper serves many flua machines; every online-mode run gets a free OS-assigned port, so parallel instances just work.

api.helper — any REST call, locally

From a QA, calls route through the helper with the same (data, status) contract as api.get:

--%%name:helper-demo
--%%mode:online
-- --------------- EOH ---------------
function QuickApp:onInit()
  -- single-key variable read: the HC3 refuses this to external callers,
  -- so it goes through the helper. Point 4366/x at one of your QAs.
  local v, status = api.helper("GET", "/plugins/4366/variables/x", nil)
  print("helper read:", v and v.value, status)

  -- fibaro.* executed on the real controller
  print("fibaro.getName:", api.helperFibaro("getName", {1}))
end

Run it online (credentials from the environment chain):

[helper-demo5000]: helper read: Hello world 200
[helper-demo5000]: fibaro.getName: zwave

The single-key read answers the variable's dto ({name, value, isHidden}) — read the value field. api.helperFibaro(name, args) runs fibaro.<name>(...) inside the helper and returns its values — a window into the real controller's fibaro table from your laptop. Keep the calls quick: a fibaro.sleep would block the helper and drop the connection.

If the helper is missing or unreachable, the call answers (nil, 0) and the run log says what to do — the QA never crashes:

[helper-demo5000]: api.helper GET /plugins/4366/variables/x -> helper not connected (run flua --tool installHelper)
[helper-demo5000]: helper read: nil 0

Transparent routing — and proxy state that survives

You rarely need api.helper explicitly for variables: in online and proxy mode, api.* calls to variable endpoints on an HC3-namespace id (below 5000 — the controller's own QAs) route through the helper automatically, exactly like the other REST endpoints forward to the HC3. Emulated ids (5000 and up) stay in the sim.

In proxy mode that becomes a persistence story: a proxy QA's variables — and its children's — live on the real controller. Reads answer from the HC3, writes land there first with the sim shadow mirroring after. Kill flua, start it again: the state is still there, no db needed. Libraries that keep their state in child variables (QwikChild and friends) just work across restarts.

Next time: the promised proxy-mode deep dive — flua mirrors a QA onto your real HC3, and the two stay in sync while you develop on the laptop.