Skip to content
Get the appGet app

Node-RED and Scripts

Anything that can send an HTTPS request can drive Aivi. On Home Assistant, use the Live Activities guide and the blueprints; on the iPhone itself, use Shortcuts; on openHAB, see openHAB. This page covers the rest: Node-RED, a Homematic CCU with RedMatic, and plain scripts.

Every update is one PATCH request with your API token, sent to the slug of a widget or activity you created in the app. See the API reference for the request body of each.

This keeps a Gauge widget with the slug nas-disk in sync with a disk’s usage on a Linux machine. Create the widget in the app’s Widgets tab first, then run the script on a schedule:

#!/bin/sh
USED=$(df --output=pcent /srv | tail -1 | tr -dc '0-9')
curl -sS --fail-with-body \
-X PATCH https://api.getaivi.app/widget/nas-disk \
-H 'Authorization: Token [token]' \
-H 'Content-Type: application/json' \
--data-binary @- <<EOF
{
"content": {
"template": "gauge",
"value": $USED,
"min_value": 0,
"max_value": 100,
"unit": "%",
"label": "NAS disk",
"icon": "externaldrive"
}
}
EOF

A crontab line such as */15 * * * * /usr/local/bin/aivi-disk.sh updates it every 15 minutes. The same request works from Python, a CI job, or any other language; only the URL, the two headers, and the JSON matter.

The built-in http request node sends the update, and a function node builds it from whatever comes in: an MQTT message, a device node, or a timer.

  1. In the Aivi app’s Widgets tab, create a widget with the Gauge template and the slug living-room.

  2. Give Node-RED your API token as an environment variable named AIVI_TOKEN: double-click the flow’s tab and add it under Environment Variables, so the token stays out of the flow itself.

  3. Copy the flow below, then choose Import from the Node-RED menu and paste it.

  4. Replace the Every 5 minutes inject node with your real source, wired into Build Aivi widget update. The function expects the reading in msg.payload.

  5. Deploy. The Aivi response debug node shows 200 for every successful update.

The function node sets the URL, the headers, and the body:

// msg.payload is the reading to show, e.g. a temperature.
const slug = "living-room";
msg.url = `https://api.getaivi.app/widget/${slug}`;
msg.headers = {
"Authorization": `Token ${env.get("AIVI_TOKEN")}`,
"Content-Type": "application/json",
};
msg.payload = {
content: {
template: "gauge",
value: Number(msg.payload),
min_value: 10,
max_value: 35,
unit: "°C",
label: "Living room",
icon: "thermometer.medium",
},
};
return msg;
The flow to import
[
{
"id": "aivi_inject",
"type": "inject",
"name": "Every 5 minutes",
"repeat": "300",
"once": true,
"onceDelay": "1",
"topic": "",
"payload": "21.5",
"payloadType": "num",
"props": [{ "p": "payload" }],
"x": 150,
"y": 80,
"wires": [["aivi_function"]]
},
{
"id": "aivi_function",
"type": "function",
"name": "Build Aivi widget update",
"func": "// msg.payload is the reading to show, e.g. a temperature.\nconst slug = \"living-room\";\n\nmsg.url = `https://api.getaivi.app/widget/${slug}`;\nmsg.headers = {\n \"Authorization\": `Token ${env.get(\"AIVI_TOKEN\")}`,\n \"Content-Type\": \"application/json\",\n};\nmsg.payload = {\n content: {\n template: \"gauge\",\n value: Number(msg.payload),\n min_value: 10,\n max_value: 35,\n unit: \"°C\",\n label: \"Living room\",\n icon: \"thermometer.medium\",\n },\n};\nreturn msg;",
"outputs": 1,
"x": 390,
"y": 80,
"wires": [["aivi_request"]]
},
{
"id": "aivi_request",
"type": "http request",
"name": "PATCH to Aivi",
"method": "PATCH",
"ret": "obj",
"paytoqs": "ignore",
"url": "",
"persist": false,
"senderr": false,
"headers": [],
"x": 620,
"y": 80,
"wires": [["aivi_debug"]]
},
{
"id": "aivi_debug",
"type": "debug",
"name": "Aivi response",
"active": true,
"complete": "statusCode",
"targetType": "msg",
"x": 820,
"y": 80,
"wires": []
}
]

To drive a Live Activity instead, send to /activity/{slug} with a state and a Live Activity template in the body; see Update an activity.

RedMatic runs Node-RED on a CCU3 or OpenCCU, so the same flow works there. Use a ccu value node for your device’s datapoint as the source, for example a thermostat’s ACTUAL_TEMPERATURE, and wire it into Build Aivi widget update.

A 200 means the update was accepted and is on its way to every device that shows the widget or activity. Errors return a JSON detail: 401 for a wrong token, 402 without an active subscription, 404 for an unknown slug, and 422 when the body does not match the template. See Errors for the full list.