Real integrations: HTTP and MQTT from your QuickApp

So far the series lived in the sim: timers, UI, the house, mock services. Today the QuickApp reaches out of the laptop — flua's net.HTTPClient and mqtt clients make real network calls, with the same API the QA uses on the HC3. Write it once, test it offline against the real service, and the same file runs on the controller.

HTTP: net.HTTPClient

Requests are asynchronous with a success/error callback pair — no fibaro.sleep, no busy waits. Timeouts, headers, bodies, the lot:

--%%name:http-demo
-- --------------- EOH ---------------
setTimeout(function()
  local client = net.HTTPClient()

  -- GET with a success/error pair
  client:request("https://httpbin.org/get?hello=world", {
    options = { timeout = 5 },
    success = function(resp)
      print("get:", resp.status, resp.headers["Content-Type"])
      print("get data:", resp.data:sub(1, 80))
    end,
    error = function(err) print("get error:", err) end,
  })

  -- POST with headers and a body
  client:request("https://httpbin.org/post", {
    options = {
      method = "POST",
      headers = { ["Content-Type"] = "application/json" },
      data = '{"value": 42}',
      timeout = 5,
    },
    success = function(resp) print("post:", resp.status) end,
    error = function(err) print("post error:", err) end,
  })
end, 100)

Run it — httpbin echoes whatever you send, so you see your own query string and body come back:

[http-demo5000]: get: 200 application/json
[http-demo5000]: get data: {
  "args": {
    "hello": "world"
  }, ...
[http-demo5000]: post: 200

Errors never crash the QA: a refused connection, a DNS failure, a timeout — all land in the error callback (the full examples/http.lua has a request to a dead port to prove it). Credentials for real APIs belong in a --%%var reading the config file, as in the previous post.

MQTT: mqtt.Client

The HC3's MQTT client (firmware 5.030+ API) is fully emulated. examples/mqtt.lua connects to the public broker at broker.hivemq.com, subscribes, publishes, and prints its own echo:

local broker = os.getenv("MQTT_URL") or "mqtt://broker.hivemq.com:1883"
setTimeout(function()
  local client = mqtt.Client.connect(broker, {
    clientId = "flua-demo-" .. tostring(os.time()),
    cleanSession = true,
    callback = function(errorCode)
      print("connect result:", errorCode == 0 and "ok" or "failed " .. tostring(errorCode))
    end,
  })

  client:addEventListener("connected", function()
    print("connected:", client:isConnected())
    client:subscribe("flua/demo", { qos = mqtt.QoS.AT_LEAST_ONCE })
  end)

  client:addEventListener("subscribed", function(event)
    client:publish("flua/demo", "hello from flua", {
      qos = mqtt.QoS.AT_LEAST_ONCE,
      retain = false,
    })
  end)

  client:addEventListener("message", function(event)
    print("mqtt echo:", event.topic, event.payload, "qos=" .. tostring(event.qos))
    client:disconnect()
  end)

  client:addEventListener("closed", function() print("closed") end)
  client:addEventListener("error", function(event) print("mqtt error:", event.code) end)
end, 100)
[mqtt-demo5000]: connected: true
[mqtt-demo5000]: connect result: ok
[mqtt-demo5000]: mqtt echo: flua/demo hello from flua qos=1

The echo takes a while — it is a real broker across the internet, and public brokers throttle rapid reconnects; if it is down or unhappy, the error path prints cleanly instead of crashing. Point the broker at your own (or a local mock) with MQTT_URL=mqtt://127.0.0.1:1883 — and that is exactly where post 14's .env comes in for real broker credentials.

Secure brokers work too: mqtts:// or options.tls enables TLS, with SNI on by default like the HC3 (since 5.191) — tls = { useSNI = false } switches it off when a broker trips over it. Also available: allowUnauthorized, certificateAuthority, clientCertificate.

Test against a mock, then go live

The loop from post 8 fits here perfectly: while you build the integration logic, run against a mock server in Lua (net.HTTPServer / net.TCPServer), then flip the URL to the real service and run the same QA. The service contract is tested offline; the only thing that changes is the endpoint.

Next time: proxy mode — flua mirrors a QA onto your real HC3, so it runs offline but behaves as a device on the controller. The deepest offline-online blend yet.

net + mqtt: USAGE.md ("Network") · examples/http.lua, examples/mqtt.lua · flua: https://github.com/jangabrielsson/flua