Appearance
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.
| Function | Returns |
|---|---|
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:
| Field | Meaning | Default |
|---|---|---|
method | HTTP method | "GET" |
path | Path appended to the device's configured base address | "/" |
query | Table of query parameters | none |
headers | Table of header names to string values | none |
body | Request body. Only a string is sent; encode tables with ctx.json.encode | none |
auth | Per-call credentials: { basic = { username = …, password = … } } or { bearer = "…" }. An explicit headers.Authorization takes precedence | none |
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 likectx.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. Usectx.device.sqlto 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.