kaenix-plugins

kaenix Plugins

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.


Dateistruktur

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.


Minimales Plugin

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

Felder im Detail

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 / description

label 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 / outputs

Arrays 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.

config

Felder, 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.

globalSettings

Einstellungen, 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)

Rückgabewert

// Einzelwert → geht an outputs.out
return 42;

// Mehrere Ausgänge explizit setzen
return { out: 42, error: null };

// Nichts ausgeben
return {};

context – Helper-Objekt

Logging

context.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)

Asynchrone Ausgabe

Für Ausgaben nach einem Timer oder HTTP-Call (der Rückgabewert von execute ist dann {}):

context.emitOutput('status', 1);   // handle-Name, Wert

Globale Einstellungen

const token = context.globalSetting('apiToken');
context.setGlobalSetting('letzterWert', 42);

Node-ID

const nodeId = context.nodeId;   // Eindeutige ID der Node-Instanz (string)

Async / Timer / HTTP

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 {};
},

Pro-Node-State (mehrere Instanzen)

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 {};
  },
};

Vollständiges Beispiel

/**
 * @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;
  },
};

Namenskonvention

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