Give the simulated HC3 its own REST surface

The HC3's API has 359 endpoints. flua implements the slice your QAs actually touch; the rest forwards to a real controller in online mode. But sometimes you want a missing endpoint to exist in the offline sim — a panel the HC3 has no route for, a sprinkler service, the alarm partition logic. Now you can register it yourself, from Lua:

api.registerEndpoint("GET", "/panels/sprinklers/{id}", function(req)
  return { id = tonumber(req.params.id), name = "Sprinklers " .. req.params.id }, 200
end)

The handler returns (data, status) like any REST route, and from then on every QA in the run reaches it with plain api.get — no special client, no mocking framework. The whole pattern is two small files:

-- mock-panels.lua — the emulation module (require() it from any QA)
local M = {}

api.registerEndpoint("GET", "/panels/sprinklers/{id}", function(req)
  -- {id} params are strings; the query arrives parsed, e.g. {zone={"2"}}
  return { id = tonumber(req.params.id), name = "Sprinklers " .. req.params.id }, 200
end)

api.registerEndpoint("POST", "/panels/sprinklers/{id}/actions/run", function(req)
  _FLUA.postRefreshEvent{
    type = "DevicePropertyUpdatedEvent",
    data = { id = 900001, property = "state", newValue = "running" },
  }
  return { started = true, minutes = req.body and req.body.minutes or 5 }, 200
end)

return M

-- uses-mock-panels.lua — a consumer QA (run: flua --api local examples/uses-mock-panels.lua)
function QuickApp:onInit()
  require("mock-panels")
  setTimeout(function()
    local panel, s = api.get("/panels/sprinklers/7?zone=2")
    print("GET", s, json.encode(panel))
    local run, s2 = api.post("/panels/sprinklers/7/actions/run", { minutes = 10 })
    print("POST", s2, json.encode(run))
    local ok = s == 200 and panel.name == "Sprinklers 7" and s2 == 200 and run.started == true
    print(ok and "VERDICT: all passed" or "VERDICT: failed")
    exit(ok and 0 or 1)
  end, 20)
end
GET 200 {"id":7,"name":"Sprinklers 7"}
POST 200 {"minutes":10,"started":true}
VERDICT: all passed

The rules, in one breath

The repo ships both flavors as examples: examples/mock-panels.lua (additive endpoints) and the alarm loop — examples/alarm-logic.lua overrides the built-in alarm partition endpoints with exit-delay arming and refreshState events, an examples/alarm-panel.lua QA carries the arm/disarm UI, and examples/uses-alarm-panel.lua consumes it.

And the weather simulator from the last post is the same idea one level up: a QA whose endpoints and events make the whole /weather surface come alive, so your climate QAs get a forecast to react to.

Next up: packaging those endpoints as QuickApps — emulation you can distribute like any other QA.