Skip to content

Bindings & Berechnungen

Ein Binding ist ein Platzhalter im HTML, den aura live durch einen Datenpunktwert ersetzt — und auf Wunsch vorher damit rechnet, vergleicht oder formatiert.

Gültig in:

WidgetFeld
HTMLhtmlContent (statisches HTML) und der Inhalt aus htmlDatapoint
Wert-AnzeigehtmlTemplate
Marker (Widget, Bereich, Tab)Text beim Stil Label
Abschnittstitelsubtitle (Untertitel)

Bindings lesen nur. Um aus dem HTML heraus einen Datenpunkt zu setzen, gibt es aura.setState — siehe HTML → Datenpunkte schreiben.

Die Syntax folgt ioBroker.vis: Bindings aus einer bestehenden vis-Ansicht lassen sich übernehmen. Abweichungen stehen unter Unterschiede zu ioBroker.vis.

Die vier Schreibweisen

SchreibweiseBeispielwofür
Wert{0_userdata.0.Netz}ein Datenpunkt, formatiert wie im Widget eingestellt
Operations-Kette{0_userdata.0.Netz;round(0)}ein Datenpunkt durch eine Reihe von Operationen
Benannte Variablen{a:dp1;b:dp2;a + b}mehrere Datenpunkte, freier Ausdruck (vis-Schreibweise)
Kurzform{{ dp1 + dp2 }}dasselbe, Datenpunkt-IDs direkt im Ausdruck (aura)

Alle drei rechnenden Formen benutzen dieselben Operationen und denselben Ausdruck. Was man nimmt, ist Geschmackssache:

  • Kette, wenn ein Wert nur durch ein paar Schritte soll — kürzeste Schreibweise.
  • Benannte Variablen, wenn Bindings aus vis kopiert werden oder IDs lang sind und mehrfach vorkommen.
  • Kurzform, wenn der Ausdruck im Vordergrund steht. Sie stört Inline-CSS nie, weil {{ in CSS nicht vorkommt.

Schnellstart

html
<!-- 1. roher Wert -->
<b>{0_userdata.0.Temperatur}</b> °C

21,4 °C

html
<!-- 2. gerundet -->
{0_userdata.0.Netz;round(0)} W

-1235 W

html
<!-- 3. Farbe nach Vorzeichen -->
<span style="color: {{ 0_userdata.0.Netz < 0 ? '#00ff00' : '#ff2c0a' }}">
  {0_userdata.0.Netz;formatValue(1)} W
</span>

→ grüner Text, -1.234,6 W

html
<!-- 4. Balkenhöhe in einem SVG -->
<svg viewBox="0 0 200 200">
  <rect x="0" width="200" height="10"
        y="{{ 180 - 180 * Math.min(0_userdata.0.Fuellstand / 255, 1) }}" />
</svg>

→ bei 100 wird daraus y="109.411764706"

Datenpunkte ansprechen

Eine Zeichenkette gilt als Datenpunkt-ID, wenn sie mit einem Buchstaben, einer Ziffer oder _ beginnt und mindestens einen Punkt enthält — 0_userdata.0.Temperatur, senec.0.ENERGY.GUI_GRID_POW. Alles ohne Punkt ist eine Variable.

Jeder referenzierte Datenpunkt wird automatisch abonniert: ändert er sich, rendert das Widget neu.

JSON-Pfade

Steht im Datenpunkt ein Objekt oder ein JSON-String, adressiert ein Pfad den Teilwert.

FormBeispielgilt in
?pfad{0_userdata.0.Akku?soc}Wert, Kette, Deklaration
#pfad{0_userdata.0.Akku#soc}Wert
}#pfad{0_userdata.0.Akku}#socWert
['pfad']{{ 0_userdata.0.Akku['soc'] }}Ausdruck

Alle vier zeigen auf dasselbe und lösen dieselbe eine Subscription aus. Array-Indizes: {0_userdata.0.Akku?cells.1} bzw. {{ 0_userdata.0.Akku['cells'][1] }}.

In Ausdrücken kein ?

Innerhalb von {{ … }} und hinter den Deklarationen ist ? der Bedingungs-Operator. Dort muss die Klammer-Schreibweise benutzt werden.

Zeitstempel

Ein Suffix an der ID liefert statt des Werts seinen Zeitstempel (Millisekunden seit 1970):

Suffixbedeutet
.tsletzte Aktualisierung
.lcletzte Änderung (last change)
html
Zuletzt geändert: {0_userdata.0.Netz.lc;date(hh:mm)}

Zuletzt geändert: 14:32

Das kostet keine zusätzliche Subscription — die Zeitstempel kommen mit dem Wert mit.

IDs mit Sonderzeichen

# und - gehören zur ID, solange direkt ein Wortzeichen folgt. Shelly-IDs bleiben damit heil:

html
{shelly.0.SHSW-25#4C7525#1.Relay0.Switch}

Operatoren mit Leerzeichen schreiben

Genau deshalb ist dp1-dp2 eine ID und keine Subtraktion. In Ausdrücken immer dp1 - dp2 schreiben. Betroffen ist nur -; + * / % sind eindeutig.

Umlaute, ß und andere Nicht-ASCII-Buchstaben (z. B. kyrillische) sind gültige ioBroker-IDs und funktionieren in allen vier Schreibweisen:

html
{0_userdata.0.Haus.EG_Küche.Sensor.temperature}

Trotzdem besser vermeiden

Für neue Datenpunkte sind ASCII-Namen die ruhigere Wahl — Skripte, Exporte und Fremdadapter gehen nicht alle so gelassen damit um.

Fehlende Werte

Schreibweisefehlender Datenpunkt ergibt
{id}
{id;op}, {a:id;…}, {{ … }}nichts (leerer Text)

Der Unterschied ist Absicht: ein Gedankenstrich in y="…" würde ein SVG zerlegen. Wer eine sichtbare Lücke will, hängt default() an:

html
{0_userdata.0.Fehlt;default(–)}
{{ 0_userdata.0.Fehlt | default('–') }}

Variablen

Ohne Punkt im Namen ist ein Bezeichner eine Variable. Aura kennt diese:

VariableInhalt
dpWert des Widget-Datenpunkts (HTML-Widget: valueDatapoint, ersatzweise der Haupt-Datenpunkt)
coloraktuelle Schwellwert-Farbe (nur Wert-Anzeige)
unitkonfigurierte Einheit (nur Wert-Anzeige)
languageSprache der Oberfläche, de oder en
viewName des gerade angezeigten Tabs
widID des Widgets
wnameTitel des Widgets

Dazu alles, was das Binding selbst mit name:datenpunkt deklariert:

html
{leistung:senec.0.ENERGY.GUI_INVERTER_POWER;akku:0_userdata.0.Akku?soc;leistung * akku / 100}

Deklarationen gewinnen gegen Datenpunkt-IDs: heißt eine Variable dp, ist dp die Variable.

JSON-Pfade in Variablen gehen mit Punkt statt Klammer, weil eine Variable nie eine ID ist: {dp}#battery.soc bzw. {{ dp.battery.soc }}.

Aus vis nicht übernommen

username, login und instance gibt es nicht — das aura-Frontend hat keine eigene Benutzersitzung. Die sitzungslokalen local_*-Variablen von vis fehlen ebenfalls.

Operationen

Operationen hängt man mit ; an (Kette) oder mit | an (Ausdruck). Es ist dieselbe Liste:

html
{0_userdata.0.Netz;round(0)}          <!-- Kette -->
{{ 0_userdata.0.Netz | round(0) }}    <!-- Ausdruck -->

Rechnen

OperationWirkungBeispiel → Ergebnis
*(n)multiplizieren{dp;*(4)} bei 416
+(n)addieren{dp;+(2)} bei 46
-(n)subtrahieren{dp;-(-674.5)} bei 0674.5
/(n)dividieren{dp;/(100)} bei 2502.5
%(n)Rest{dp;%(3)} bei 101
sqrtQuadratwurzel{dp;sqrt} bei 164
powquadrieren{dp;pow} bei 39
pow(n)n-te Potenz{dp;pow(10)} bei 21024

Runden & Grenzen

OperationWirkungBeispiel → Ergebnis
roundauf ganze Zahl{dp;round} bei 2.63
round(n)auf n Nachkommastellen{dp;round(1)} bei -1234.56-1234.6
floorabrunden{dp;floor} bei 2.72
ceilaufrunden{dp;ceil} bei 2.13
min(n)Untergrenze{dp;min(0)} bei -50, bei 77
max(n)Obergrenze{dp;max(100)} bei 150100, bei 4040

min und max sind vertauscht

Das kommt aus vis und bleibt aus Kompatibilitätsgründen so: min(0) hebt kleine Werte an, max(100) deckelt große. Ein Wert wird also so auf 0…100 geklemmt:

html
{0_userdata.0.Roh;min(0);max(100)}

Die Funktionen Math.min() / Math.max() in Ausdrücken verhalten sich dagegen wie in JavaScript — Math.min(a, b) ist der kleinere der beiden.

Farben

OperationWirkungBeispiel → Ergebnis
hexHex, kleinbei 255ff
hex2Hex, klein, zweistelligbei 120c
HEXHex, großbei 255FF
HEX2Hex, groß, zweistelligbei 120C
html
<div style="background:#{0_userdata.0.Rot;HEX2}{0_userdata.0.Gruen;HEX2}{0_userdata.0.Blau;HEX2}">

→ bei 100 / 200 / 12: background:#64C80C

Werte in Prozent vorher skalieren: {0_userdata.0.Rot;/(100);*(255);HEX2}

Formatieren

OperationWirkungBeispiel → Ergebnis
formatValue(n)Anzeige-Format des Widgets (Tausenderpunkt, Dezimalkomma)bei 1234.561.234,6
fixed(n)feste Nachkommastellen, technischbei 1234.561234.6
default(x)Ersatz für leere Wertebei nullx
upper · lower · trimTextbei abAB · ab · ab
boolWahrheitswertbei "true"true
jsonals JSON-Textbei {a:1}{"a":1}

formatValue ohne Argument nimmt die Nachkommastellen aus den Widget-Einstellungen.

default, fixed, upper, lower, trim, bool und json sind aura-Erweiterungen und in vis nicht vorhanden.

Argumente

FormKette (;)Ausdruck (|)
Zahlround(1)round(1)
Text ohne Anführungszeichendate(hh:mm)
Text mit Anführungszeichendate('hh:mm')date('hh:mm')

In der Kette ist ein Argument roher Text — genau wie in vis. Im Ausdruck ist es ein Ausdruck und Text braucht Anführungszeichen.

Ausdrücke

Ein Ausdruck steht zwischen {{ und }} oder hinter den Deklarationen eines {a:id;…}-Bindings.

Operatoren

Von schwach nach stark bindend:

StufeOperatoren
1|Operation anhängen
2? :Bedingung
3??erster nicht-null-Wert
4||erster wahrer Wert
5&&letzter Wert, wenn alle wahr
6== != === !==Vergleich
7< <= > >=Größenvergleich
8+ -plus/minus (+ verkettet Text)
9* / %mal/geteilt/Rest
10-x +x !xVorzeichen, Negation
11[…] .name (…)Pfad, Aufruf

( ) klammern wie üblich. == vergleicht als Text (1 == '1' ist wahr), === streng typisiert (1 === '1' ist falsch) — für ioBroker-Werte, die oft als String ankommen, ist == meist das Richtige.

Funktionen

Nur diese sind aufrufbar; alles andere macht das Binding ungültig und es bleibt sichtbar stehen.

GruppeFunktionen
MathematikMath.abs Math.min Math.max Math.round Math.floor Math.ceil Math.trunc Math.sign Math.pow Math.sqrt Math.log Math.exp Math.hypot
KonstantenMath.PI Math.E
Kurzform ohne Math.abs min max round floor ceil sqrt pow
UmwandlungNumber String Boolean parseFloat parseInt isNaN

Typen

ioBroker liefert Werte oft als Text. Rechenoperatoren wandeln selbst um, + aber nicht — dort entscheidet der Typ über Addition oder Verkettung:

html
{{ 0_userdata.0.Text * 2 }}                    <!-- "17.5" → 35 -->
{{ parseFloat(0_userdata.0.Text) + 1 }}        <!-- "17.5" → 18.5 -->
{{ 0_userdata.0.Text + 1 }}                    <!-- "17.5" → "17.51" -->

Unlesbare Werte ergeben nichts statt NaN:

html
[{{ 'abc' | round(1) }}]

[]

Zahlen in der Ausgabe

Ergebnisse werden technisch ausgegeben: Dezimalpunkt, kein Tausendertrenner.

BindingAusgabe
{0_userdata.0.Netz}-1.234,56 (Widget-Format)
{{ 0_userdata.0.Netz }}-1234.56
{0_userdata.0.Netz;formatValue(1)}-1.234,6

Grund: berechnete Zahlen landen meistens in SVG-Koordinaten, CSS-Längen oder Farbwerten — ein Dezimalkomma würde y="177,3" ungültig machen. Für die Anzeige also immer formatValue(n) anhängen.

Rundungsrauschen wird gekappt: {{ 0.1 + 0.2 }} ergibt 0.3, nicht 0.30000000000000004.

Datum & Zeit

date(muster) und momentDate(muster) formatieren einen Zeitwert. Erkannt werden Unix-Sekunden, Millisekunden, ISO-Zeitangaben und HH:mm — also auch .ts / .lc.

TokenergibtBeispiel (22.08.2026, 14:32:07.045)
yyyy · YYYYJahr2026
yy · YYJahr, zweistellig26
MMMMMonatsnameAugust
MMMonat08
dd · DDTag22
EEEE · ddddWochentagSamstag
EE · dddWochentag, kurzSa
HHStunde (24 h)14
hhStunde — siehe unten14 bzw. 02
mmMinute32
ssSekunde07
SSSMillisekunde045
wwKalenderwoche34

hh bedeutet zweierlei

In date() sind beide Stunden-Tokens 24-stündig — so macht es vis, dessen eigenes Beispiel date(hh:mm) eine Uhrzeit meint. In momentDate() gilt die moment.js-Bedeutung: hh ist 12-stündig, HH 24-stündig.

Bindingum 14:32
{dp;date(hh:mm)}14:32
{dp;momentDate(hh:mm)}02:32
{dp;momentDate(HH:mm)}14:32

momentDate(muster, true) ersetzt den Wochentag durch „Heute" bzw. „Gestern", wenn er darauf fällt:

html
{0_userdata.0.Termin;momentDate(dddd HH:mm, true)}

Heute 08:05 · Gestern 08:05 · Dienstag 08:05

Ohne Muster gilt dd.MM.yyyy HH:mm. Ein unlesbarer Wert ergibt nichts. Jedes Token wird an jeder Stelle ersetzt, date(mm:mm) ergibt also 32:32.

Rezepte

Balkenhöhe in einem SVG — je größer der Wert, desto weiter oben beginnt der Balken:

html
<svg viewBox="0 0 200 200">
  <rect x="0" width="200" height="10" fill="#4ade80"
        y="{{ 180 - 180 * Math.min(0_userdata.0.Fuellstand / 255, 1) }}" />
</svg>

→ bei 100: y="109.411764706"

Farbe nach Vorzeichen — Einspeisung grün, Bezug rot:

html
<span style="color:{{ senec.0.ENERGY.GUI_GRID_POW < 0 ? '#00ff00' : '#ff2c0a' }}">
  {senec.0.ENERGY.GUI_GRID_POW;round(0)} W
</span>

Farbe aus drei Datenpunkten — das vis-Standardrezept:

html
<div style="background:#{0_userdata.0.Rot;HEX2}{0_userdata.0.Gruen;HEX2}{0_userdata.0.Blau;HEX2};
            width:40px;height:40px"></div>

→ bei 100 / 200 / 12: #64C80C

Anteil zweier Datenpunkte in Prozent:

html
{{ round(0_userdata.0.Rot / (0_userdata.0.Rot + 0_userdata.0.Gruen) * 100) }} %

→ bei 100 und 200: 33 %

Zuletzt geändert um:

html
Stand {senec.0.ENERGY.GUI_GRID_POW.lc;date(HH:mm)}

Stand 14:32

Ampel über zwei Schwellen:

html
{{ 0_userdata.0.Temperatur > 25 ? 'rot' : 0_userdata.0.Temperatur > 20 ? 'gelb' : 'gruen' }}

→ bei 21.4: gelb

Fortschrittsbalken mit geklemmtem Wert — Rohwerte außerhalb von 0…100 laufen sonst aus dem Rahmen:

html
<div style="width:{0_userdata.0.Roh;min(0);max(100)}%;height:8px;background:#38bdf8"></div>

→ bei 120: width:100%

Text aus mehreren Datenpunkten:

html
{{ 'Akku ' + (0_userdata.0.Akku['soc'] | formatValue(0)) + ' %' }}

Akku 87 %

Deckkraft aus einem Prozentwert:

html
<img src="…" style="opacity:{{ Math.min(0_userdata.0.Akku['soc'] / 100, 1) }}">

→ bei 87: opacity:0.87

Was nicht ersetzt wird

Bindings dürfen kein normales HTML/CSS anfassen. Diese Eingaben bleiben Zeichen für Zeichen erhalten:

EingabeGrund
{ color: red }Leerzeichen in einem einteiligen {…} → kein Token
{color:red;background:blue}red und blue sind keine Datenpunkt-IDs → keine Deklarationen
{margin:0;padding:0}dito
{ font: 12px/1.4 sans-serif }dito
@media(min-width:1px){.a{color:red}}}} ohne vorheriges {{
{foo}ein Wort ohne Punkt und keine bekannte Variable
{{ 1 + }}kein gültiger Ausdruck
{0_userdata.0.X;frobnicate}unbekannte Operation
href="{dp}#TOP"Großbuchstaben nach # gelten nie als JSON-Pfad

Grundregel: was nicht sicher als Binding erkannt wird, bleibt stehen. Ein sichtbar gebliebenes Binding ist also der Hinweis auf einen Tippfehler — nicht auf einen fehlenden Wert.

Die drei Platzhalter-Ebenen von aura

Sie sehen ähnlich aus, machen aber Verschiedenes:

EbeneSyntaxWoWann
Widget-Titel[[0_userdata.0.Temp]]Titelfeld jedes Widgetslive, bei jeder Wertänderung
Popup-Views{{parent}} {{parent2}} {{dp}} {{name}}alle Felder eines Popup-Widgetseinmal beim Öffnen, ersetzt IDs, keine Werte
Bindings{…} · {{ … }}HTML-Widget, HTML-Template, Marker-Label, Untertitel des Abschnittstitelslive, bei jeder Wertänderung

Die Popup-Ebene benutzt ebenfalls doppelte Klammern, kollidiert aber nicht: {{parent}} ist ein einzelnes Wort ohne Leerzeichen und bleibt der Popup-Ebene vorbehalten. Höhere Ebenen werden deshalb durchnummeriert ({{parent2}}, {{parent3}}, …) und nicht als {{parent.parent}} geschrieben. Alles mit Leerzeichen, Punkt oder Operator ist ein Ausdruck. Wer in einem Popup den Wert des Haupt-Datenpunkts will, schreibt {dp} mit einfachen Klammern.

Unterschiede zu ioBroker.vis

visaura
{id}, {id;op;op}, {a:id;b:id;expr}identisch
*() +() -() /() %() round min max sqrt pow floor ceil hex hex2 HEX HEX2 formatValue date momentDateidentisch, inklusive der vertauschten min/max-Bedeutung
.ts / .lcidentisch
Ausdruck ist beliebiges JavaScript (new Function)fester Sprachumfang mit Funktions-Whitelist → siehe Grenzen
Bedingung braucht :: statt :nicht nötig — der Ausdruck wird nie an : zerlegt. :: wird trotzdem akzeptiert
json(pfad)stattdessen ?pfad / ['pfad'], funktioniert auch in der Kette
array(a,b), random(n)nicht vorhanden
username, login, instance, local_*, widget, widgetOidnicht vorhanden
{{style: value}} als Escape für CSSnicht nötig — CSS wird nie als Binding gelesen
zusätzlich {{ ausdruck }} mit IDs direkt im Ausdruck
zusätzlich default fixed upper lower trim bool json
moment.js für momentDateeigener Formatter, gleiche Tokens, keine zusätzliche Bibliothek

Fehlersuche

SymptomUrsacheLösung
Binding bleibt als Text sichtbarSyntaxfehler, unbekannte Operation oder unbekannte FunktionKlammern und Namen prüfen; erlaubt ist nur, was in Operationen und Funktionen steht
Ergebnis ist leerDatenpunkt existiert nicht oder Wert ist nicht lesbarID prüfen; default() anhängen, um das sichtbar zu machen
statt einer Zahleinfaches {id}-Token ohne WertID prüfen
Zahl ergibt UnsinnWert kommt als Text und wurde mit + verkettetparseFloat(…) benutzen oder mit */- rechnen
SVG/CSS kaputt, Komma in der ZahlformatValue in einer Koordinatedort kein formatValue verwenden — Ausdrücke geben von sich aus einen Punkt aus
Ganzes CSS verschwundeneine CSS-Zeile sah wie ein Binding aus ({background:img.0.png;…})Deklaration in eine <style>-Regel mit Leerzeichen schreiben oder den Wert anders formulieren
Wert aktualisiert sich nichtID im Binding falsch geschrieben — dann wird auch nichts abonniertID über den Datenpunkt-Wähler kopieren
{{ dp1-dp2 }} liefert nichtsohne Leerzeichen ist das eine ID{{ dp1 - dp2 }} schreiben
Zeit zeigt 02:32 statt 14:32momentDate(hh:mm) ist 12-stündigHH benutzen oder date()

Grenzen & Sicherheit

Der Ausdruck ist kein JavaScript, sondern eine eigene, geschlossene Sprache:

  • aufrufbar ist nur, was in der Funktionsliste steht — kein Zugriff auf window, fetch, eval oder Objekt-Methoden
  • keine Schleifen, keine Zuweisungen, keine Funktionsdefinitionen
  • höchstens 4000 Zeichen, 500 Bestandteile und 50 Klammer-Ebenen je Ausdruck
  • ein Text-Literal darf kein }} enthalten — es würde die Kurzform vorzeitig beenden. In so einem Fall die Deklarations-Schreibweise nehmen: {v:id;v + '}}'}

Das ist bewusst so: HTML darf aus einem Datenpunkt kommen, und Bindings werden ausgewertet, bevor der Inhalt in den Sandbox-iFrame geht. Mit echtem eval könnte ein Skript, das diesen Datenpunkt schreibt, beliebigen Code im aura-Frontend ausführen.

Schreiben kann ein Ausdruck ebenfalls nicht — dafür gibt es die eigene API aura.setState im HTML-Widget, die in der Sandbox läuft und pro Widget abschaltbar ist.

Zwei Dinge bleiben in der Verantwortung des Autors:

  • Das Ergebnis eines Bindings wird unmaskiert ins HTML geschrieben. Ein Datenpunkt, der <script> enthält, landet also als Markup im Dokument — im HTML-Widget im Sandbox-iFrame, in der Wert-Anzeige direkt.
  • Jeder referenzierte Datenpunkt ist eine Subscription. Ein Widget mit 50 Bindings hält 50 davon offen.