Serving data

Pushing data

How live JSON reaches a widget — via the MCP, or from any script.

A widget renders whatever JSON is currently set as its feed. Update the feed and the phone picks it up on its next poll, re-invoking the widget’s onData — the page never reloads.

From the MCP

push_data sets the feed to an arbitrary JSON object the widget understands:

// push_data
{ "id": "wgt_abc123", "data": { "tempC": 21.4, "available": true } }

The data is opaque to the hub — it’s the contract between your widget’s onData renderer and whatever is producing values. Push again to update.

From a script (no MCP)

For cron jobs, sensors, or any external producer, get a standalone endpoint with get_data_endpoint:

// get_data_endpoint
{ "id": "wgt_abc123" }
// → { "url": "https://<mac-host>/admin/data/wgt_abc123", "token": "…" }

Then POST JSON with the bearer token:

curl -X POST "$URL" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"tempC": 22.0, "available": true}'

Treat the token like a secret — it grants write access to that widget’s feed.

How fast it updates

The phone polls GET /api/data/:id every refreshMs (set per widget at publish time; default 1500 ms). So an update is visible within one poll interval. Lower refreshMs for snappier widgets, higher to be gentle — the native app does the polling, not your widget.

Shape your payload for the widget

Because data arrives as a whole object each time, design a flat, self-describing shape and have the renderer tolerate missing keys:

{ "cpu": 34, "gpu": 12, "mem": 61, "available": true }
window.PERCH.onData((d) => {
  d = d || {};
  if (d.available === false) return;
  set("cpu", d.cpu); set("gpu", d.gpu); set("mem", d.mem);
});