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);
});