Plugins erweitern den Logic Editor von kaenix.io um eigene Node-Typen.
Jedes Plugin ist eine einzelne JavaScript-Datei und wird beim Start automatisch geladen.
plugins/
└── mein-plugin/
└── mein-plugin-kaenix.js ← Dateiname endet auf -kaenix.js
Der Ordnername spielt keine Rolle – erkannt werden alle Dateien, die auf -kaenix.js enden.
module.exports = {
type: 'mein-plugin', // eindeutiger Typ-Bezeichner (kebab-case)
category: 'Eigene', // Gruppe im Node-Menü
label: 'Mein Plugin', // Anzeigename
description: 'Was dieses Plugin tut',
color: '#6366f1', // Header-Farbe der Node (Hex)
inputs: [{ handle: 'in', label: 'Eingang' }],
outputs: [{ handle: 'out', label: 'Ausgang' }],
config: [], // Konfigurationsfelder (siehe unten)
execute(inputs, data, context) {
// Logik hier
return inputs.in; // Einzelwert → geht an outputs.out
},
};
type (string, Pflicht)Eindeutiger Bezeichner in kebab-case. Darf nur einmal vorkommen.
category (string)Gruppenname im “Node hinzufügen”-Panel, z.B. 'Automatisierung', 'Mathematik', 'Benachrichtigung'.
label / descriptionlabel erscheint als Node-Titel im Logic-Editor.
description ist nur in der Detailansicht sichtbar (nicht auf der Node selbst).
color (CSS Hex-Farbe)Header-Farbe der Node, z.B. '#f59e0b'.
inputs / outputsArrays von Handle-Definitionen:
inputs: [
{ handle: 'trigger', label: 'Trigger' },
{ handle: 'wert', label: 'Wert' },
],
outputs: [
{ handle: 'out', label: 'Ergebnis' },
{ handle: 'error', label: 'Fehler' },
],
handle ist der interne Bezeichner (wird in inputs.* und return {} verwendet).
label ist der Anzeigetext neben dem Verbindungspunkt.
configFelder, die der Nutzer pro Node-Instanz konfigurieren kann:
config: [
{ key: 'zeitSekunden', label: 'Zeit in Sekunden', type: 'number', placeholder: '60' },
{ key: 'modus', label: 'Modus', type: 'select',
options: [
{ value: 'auto', label: 'Automatisch' },
{ value: 'manuell', label: 'Manuell' },
]
},
{ key: 'text', label: 'Nachricht', type: 'text' },
{ key: 'token', label: 'API Token', type: 'password' },
]
Verfügbare type-Werte: text, number, password, select, checkbox.
globalSettingsEinstellungen, die einmalig pro Plugin-Typ gelten (z.B. API-Keys).
Syntax identisch zu config. Zugriff via context.globalSetting(key).
globalSettings: [
{ key: 'apiToken', label: 'API Token', type: 'password' },
],
execute(inputs, data, context)Wird aufgerufen, wenn ein Eingangssignal eintrifft.
| Parameter | Inhalt |
|---|---|
inputs |
Objekt mit den aktuellen Eingangs-Werten, z.B. inputs.trigger, inputs.in |
data |
Konfigurationswerte der Node (aus config), z.B. data.zeitSekunden |
context |
Helper-Objekt (siehe unten) |
// Einzelwert → geht an outputs.out
return 42;
// Mehrere Ausgänge explizit setzen
return { out: 42, error: null };
// Nichts ausgeben
return {};
context – Helper-Objektcontext.log('Wert empfangen:', inputs.in); // Eintrag im Script-Log-Panel
context.warn('Achtung:', inputs.in); // Wie log, aber mit [warn]-Präfix
context.nodeLog('⏱ 42s'); // Kurztext direkt auf der Node (max. 3 Zeilen)
Für Ausgaben nach einem Timer oder HTTP-Call (der Rückgabewert von execute ist dann {}):
context.emitOutput('status', 1); // handle-Name, Wert
const token = context.globalSetting('apiToken');
context.setGlobalSetting('letzterWert', 42);
const nodeId = context.nodeId; // Eindeutige ID der Node-Instanz (string)
execute kann asynchrone Operationen starten. Wichtig: return {} sofort, Ausgaben über context.emitOutput.
Timer-Beispiel:
execute(inputs, data, context) {
if (!inputs.trigger) return {};
setTimeout(() => {
context.emitOutput('out', 1);
context.nodeLog('✓ gesendet');
}, 2000);
return {};
},
HTTP-Fetch-Beispiel:
execute(inputs, data, context) {
fetch('https://api.example.com/data')
.then((res) => res.json())
.then((json) => {
context.emitOutput('out', json.value);
context.log('Antwort:', json.value);
})
.catch((e) => context.warn('Fehler:', e.message));
return {};
},
Damit Timers und Zustände pro Node-Instanz getrennt laufen:
const _states = {};
function getState(nodeId) {
if (!_states[nodeId]) _states[nodeId] = { timer: null };
return _states[nodeId];
}
module.exports = {
// ...
execute(inputs, data, context) {
const nodeId = context.nodeId || 'default';
const s = getState(nodeId);
clearTimeout(s.timer);
s.timer = setTimeout(() => {
context.emitOutput('out', 1);
}, 1000);
return {};
},
};
/**
* @plugin Scaler
* @version 1.0.0
* @author Dein Name
*/
module.exports = {
type: 'scaler',
category: 'Mathematik',
label: 'Scaler',
description: 'Skaliert einen Wert: (in × Faktor) + Offset',
color: '#0d9488',
inputs: [{ handle: 'in', label: 'Wert' }],
outputs: [{ handle: 'out', label: 'Ergebnis' }],
config: [
{ key: 'factor', label: 'Faktor', type: 'number' },
{ key: 'offset', label: 'Offset', type: 'number' },
{ key: 'digits', label: 'Nachkommastellen', type: 'number' },
],
execute(inputs, data, context) {
const val = parseFloat(inputs.in ?? 0);
const factor = parseFloat(data.factor ?? 1);
const offset = parseFloat(data.offset ?? 0);
const digits = parseInt(data.digits ?? 1, 10);
const result = parseFloat((val * factor + offset).toFixed(digits));
context.log(`${val} × ${factor} + ${offset} = ${result}`);
return result;
},
};
| Was | Konvention | Beispiel |
|---|---|---|
| Dateiname | <name>-kaenix.js |
mein-plugin-kaenix.js |
type |
kebab-case, eindeutig | mein-plugin |
handle-Namen |
lowercase, keine Leerzeichen | trigger, restzeit |
config-Keys |
camelCase | zeitSekunden, apiToken |