The AI driver runs AI assistants on the device itself, without anyone in front of the web interface. One gateway is one assistant, with its own mission, model, budget and permissions. Its addresses are its conversations: writing a message to a conversation address starts a turn, and the answer comes back on an output address. Any protocol can therefore talk to the assistant through a simple route: an SMS or WhatsApp message via the IM driver or SMS driver, an MQTT topic, an alarm, a cron tick.
Typical uses:
The AI driver requires the AI Connect or FULL licence. It consumes the device's credits or a dedicated API key, see section 4.
%ai:enabled and a first conversation named after the gateway, with its four output addresses.%ai:<conversation> from the grid or a route: the answer appears on %ai:<conversation>/out.
| Label | JSON Key | Description |
|---|---|---|
| Name | name | Name of the assistant. Also used for its first conversation. |
| Description | description | Optional description. |
| Installation context | json.context | What is true for the whole installation: building, equipment, hard limits. Shared wording you can copy to every assistant on the site. |
| Mission | json.mission | This assistant's role, tone and limits. Appended to the context and applied to all its conversations. |
| Connection | json.connection | Who pays this assistant's turns, including tests from the panel. Built-in: the device's credits. API key: the key below. Independent from the personal choice of the user opening the panel. |
| API key | json.apiKey | Anthropic, OpenAI or DeepSeek key, used when Connection is API key. Without a key in that mode, nothing runs, not even on credits. The ⟳ button copies one of your saved personal keys. Never shown to the assistant. |
| Model | json.model | Model identifier, as in the assistant's model menu. The ⟳ button loads the current list. Empty: the device default. In key mode the list is limited to the key's provider. |
| Memory | json.memory | Default for the conversations: Conversation (it remembers the thread) or None (each message stands alone). Every turn is journalled either way. |
| Target filter (JS) | json.filter | Body of a JavaScript function receiving call and returning true to allow the call. Restricts what the assistant may touch, on top of permissions. Anything other than true, including an error, a missing return or a timeout, denies. See section 5.1. |
| (managed by the device) | json.aiPermissions | The assistant's permanent permissions, written when you click Always on a card or from the Roles page. Do not edit by hand. |
Like the credentials of other drivers, the gateway's API key is stored in the gateway configuration on the device. Prefer the device's credits, or a key with a spending limit at the provider.
{
"context": "Office building, 3 floors. BACnet HVAC on gateway 'bacnet-hvac', Modbus meters on 'modbus-meters'. Never change setpoints outside 18-24 °C.",
"mission": "You are the maintenance assistant. Answer in two sentences maximum, in the language of the question. Ask before any write.",
"connection": "",
"model": "haiku",
"memory": "conversation",
"filter": "return !!(call.address && call.address.name.indexOf('%bacnet:') === 0)"
}
| Address | Direction | Role |
|---|---|---|
%ai:enabled |
write | Circuit breaker of the whole gateway: 1 enabled, 0 all assistants refuse turns. Created automatically. |
%ai:<conversation> |
write | Talk to the assistant in this thread. One thread per sender, room or topic; each keeps its own context. |
%ai:<conversation>/dry |
write | Dry run: the assistant explains what it would do, without doing it. |
%ai:<conversation>/out |
read | The last answer. Route it to SMS, WhatsApp, MQTT, e-mail or a display. |
%ai:<conversation>/status |
read | idle, running, error or disabled. |
%ai:<conversation>/event |
read | JSON journal of the last turn: conversation id, tokens, awaiting_permission with the blocked function if any, no_credits, timestamp, or the error. |
Conversation names use letters, digits, _, . and -. The four output addresses are created automatically for every conversation. The grid shows each conversation as a node with its outputs underneath.
| Label | JSON Key | Description |
|---|---|---|
| Name | name | %ai:<conversation>. |
| Description | description | Optional description. |
| Memory | json.memory | Overrides the gateway's memory setting for this conversation. |
| Log | log | Logging mode of the address. |
| Property | Value |
|---|---|
| Readable | No — outputs are pushed by the driver at the end of each turn |
| Writable | %ai:enabled, %ai:<conversation> and /dry |
| Routable | Yes — write into a conversation from any route, route /out, /status and /event anywhere |
| Loggable | Yes |
The gateway pays for its turns. With Connection = Built-in, every turn, including keyboard tests in the panel, is charged to the device's credits. With API key, turns go to the gateway's key, never to the personal key of the user who opens the panel. Without an AI Connect or FULL licence the driver does not start.
The Model chosen in the assistant's thread in the panel is saved into the gateway. A model chosen in the main assistant never changes a gateway assistant.
A gateway assistant is its own principal on the device: it borrows no human account. It starts with no permission. You grant rights in two ways:
You can only grant functions that your own role holds. Permissions are re-evaluated at every turn, so a revocation takes effect immediately. The panel's rights pill shows the current level of the assistant.
Permissions say what the assistant may do; the target filter says on what. It is the body of a JavaScript function called before every API call, with a call object:
| Field | Content |
|---|---|
call.module, call.method |
The API module and method about to be called. |
call.args |
Its arguments. |
call.address, call.addresses |
The resolved target address or addresses, when the call concerns addresses. |
call.assistant, call.gateway |
This assistant and its gateway. |
Return true to allow. Anything else denies: no return value, an exception, an unresolved address, or more than 50 ms of execution. A filter that does not compile denies everything. End the filter with a denial, never with an unconditional return true.
// Only the HVAC BACnet gateway, and never the delete methods
if (/^delete/.test(call.method)) return false;
if (call.address && call.address.name.indexOf('%bacnet:') === 0) return true;
return false;
%ai:<conversation> or /dry, by a route, the API or the grid. The write is acknowledged immediately; the turn runs in the background./status becomes running. One turn runs at a time per conversation; a second message during a turn is refused with a turn is already running./out, /status returns to idle or error, and /event receives the journal of the turn. If the assistant needed a permission it does not have, /event carries awaiting_permission with the function name, and the answer says so.When %ai:enabled is 0, turns are refused and /status shows disabled.
Gateway im (IM driver) receives messages; gateway ai is the assistant.
| Route | From | To |
|---|---|---|
| Incoming message | %im:whatsapp/+41791234567/#lastMessage |
%ai:tech-41791234567 |
| Answer | %ai:tech-41791234567/out |
%im:whatsapp/+41791234567 |
The recipient must have Allow reply enabled in the IM gateway. One conversation per phone number keeps each technician's context separate. Grant the assistant Read and Write values permanently, and restrict it with a target filter to the gateways technicians may touch.
A cron address fires at 18:00 and a route writes the fixed text "Summarise the alarms of the last 24 hours and list the ones still active" to %ai:daily. A second route sends %ai:daily/out to an SMTP address. Set Memory = None on %ai:daily so each report stands alone.
While building a new automation, route the trigger to %ai:ops/dry first and read %ai:ops/out: the assistant explains what it would do without doing it. Switch the route to %ai:ops once satisfied.