Skip to content

Device (ctx.device) ​

Scripts cannot open arbitrary network connections. They reach external systems through devices configured in CoCoCo. The device configuration holds the base address and credentials, so a script never handles them.

FunctionReturns
ctx.device.http(deviceIdOrName, request)(ok, { status, headers, body })
ctx.device.sql(deviceIdOrName, { query, bindings? })(ok, { columns, rows, rowsAffected?, executionTimeMs? }), see SQL
ctx.device.get(deviceIdOrName)a device handle (synchronous)
ctx.device.forWorkcenter(workCenterId)a handle for the work center's bound device (synchronous)

A device is addressed by its ID (dev_…) or by its identifier. An unknown device raises an error rather than returning false.

HTTP requests ​

lua
local ok, resp = ctx.device.http("my-printer", {
  method = "GET",
  path = "/api/status",
  query = { verbose = 1 },
})

if not ok then error(resp) end
if resp.status >= 300 then
  error("my-printer answered " .. tostring(resp.status))
end

return { status = resp.status, body = resp.body }

Request fields, all optional:

FieldMeaningDefault
methodHTTP method"GET"
pathPath appended to the device's configured base address"/"
queryTable of query parametersnone
headersTable of header names to string valuesnone
bodyRequest body. Only a string is sent; encode tables with ctx.json.encodenone
authPer-call credentials: { basic = { username = …, password = … } } or { bearer = "…" }. An explicit headers.Authorization takes precedencenone

ok is false (with a message in resp) when the device cannot be reached or the request fails in transport. An HTTP error status is not a failure: ok is true and you check resp.status yourself. For many failures the script only gets a short message and the detailed cause is written to the run's log.

Sending JSON:

lua
local payload = ctx.json.encode({ jobId = "JOB-1001", copies = 250 })

local ok, resp = ctx.device.http("line-1-controller", {
  method = "POST",
  path = "/api/jobs",
  headers = { ["Content-Type"] = "application/json" },
  body = payload,
})
if not ok then error(resp) end
return { status = resp.status }

Device handles ​

ctx.device.get returns a handle so you name a device once and call several methods on it. The constructor is synchronous and returns one value. It does not look the device up: that happens each time a method runs, so an unknown name raises in the method call, not in ctx.device.get. The methods are asynchronous and use colon syntax:

  • handle:http(request) works like ctx.device.http;
  • handle:id() returns (ok, deviceId), the device the name resolved to;
  • handle:jmf(xml, path?) sends a JMF message (see JDF and JMF);
  • handle:sql({ query, bindings }) also exists, but its cells are { type, value } tables instead of plain values. Use ctx.device.sql to read rows.
lua
local printer = ctx.device.get("my-printer")

local found, result = pcall(function()
  local ok, resp = printer:http({ method = "GET", path = "/api/status" })
  if not ok then return { ok = false, error = resp } end
  local idOk, deviceId = printer:id()
  return { ok = true, status = resp.status, deviceId = idOk and deviceId or nil }
end)

if not found then return { available = false, reason = tostring(result) } end
return { available = true, ok = result.ok, status = result.status, deviceId = result.deviceId }

ctx.device.forWorkcenter(workCenterId) takes a work center ID (wcr_…) and resolves the device bound to that work center when a method runs. Rebinding the work center to another machine needs no script change. The handle has the same methods. A malformed ID raises in the constructor; a work center with no bound device raises when a method runs.

lua
local reached, result = pcall(function()
  local station = ctx.device.forWorkcenter("wcr_01KG7NHMJFFRMVY7TEYAESJVMQ")
  local ok, resp = station:http({ method = "GET", path = "/api/status" })
  local status = nil
  if ok and type(resp) ~= "string" then
    status = resp.status
  end
  return { reachable = ok, status = status }
end)

if not reached then return { available = false, reason = tostring(result) } end
return { available = true, reachable = result.reachable, status = result.status }

How requests are routed ​

If the device's network has a Controller (the CoCoCo bridge), the request goes through that Controller. If the Controller is offline, the request fails; it does not fall back to a direct connection. If the network has no Controller, the platform calls the device directly. On a multi-tenant CoCoCo instance, direct calls to private or internal addresses are refused, so devices on a private network need a Controller.

In integrations ​

Integration scripts can only reach devices that the integration manifest declares as resources and the instance binds. Pass the resource ID from the manifest instead of a device name. ctx.device.forWorkcenter is not available to integrations and raises.

Connect machines, automate workflows, and build custom apps for print production.