Diese Seite beschreibt die vollständige Widget-Struktur unabhängig vom konkreten Widget-Typ.
| Feld |
Typ |
Default |
Bedeutung |
id |
String |
kein Default |
eindeutige Widget-ID |
layout |
WidgetLayoutConfig |
order = 0, size = "1/1" |
Positionierungsinformationen |
content |
WidgetContentConfig |
leer mit Defaults |
Darstellung und typspezifische Inhalte |
status |
WidgetStatusConfig? |
null |
Statusquelle des Widgets; für reine icon_text-Buttons und bestimmte shutter-Setups optional |
action |
WidgetActionConfig? |
null |
optionale Tap-Aktion |
Bearbeitung in der App
Die App bearbeitet Widgets in einem ganzseitigen Wizard mit Breadcrumbs.
Grunddaten & Layout: ID, Modus, Größe, Zeilenverhalten; hier stehen auch Löschen und bei bestehenden Widgets Duplizieren
Platzierung: Reihenfolge sowie Zuordnung zu Gruppen und Reitern
Inhalt: Titel, Wert, Icon, Design und modusspezifische Inhalte als Akkordeons
Status: Statusquelle und Antworttyp
Aktion: Tap-Aktion, Overlay-Aktionen sowie bei Bild-Widgets die Bildaktionen
Zusätzliche Editorhilfen:
- Titel- und Wertquellen für
path, template und table besitzen einen Path-Picker
- Icon-Felder besitzen eine suchbare Material-Icon-Auswahl
- Farbfelder besitzen einen Color-Picker, unterstützen aber weiterhin manuelle HEX-Werte
- beim Duplizieren übernimmt das neue Widget auch seine Platzierung in Gruppen und Reitern
| Feld |
Typ |
Default |
Bedeutung |
order |
Int |
0 |
Sortierung innerhalb des Tabs bzw. Blocks |
size |
WidgetSize |
"1/1" |
Grösse im Raster |
Zulässige Werte:
1/1 bis 4/4
full/1 bis full/4
Semantik:
2/1 belegt zwei Spalten und eine Zeile
3/2 belegt drei Spalten und zwei Zeilen
full/1 belegt immer die gesamte aktuell verfügbare Dashboard-Breite
full/n startet auf einer neuen Zeile und wirkt wie ein Layout-Umbruch
WidgetContentConfig
| Feld |
Typ |
Default |
Bedeutung |
mode |
WidgetRenderMode |
auto |
Render-Modus |
icon |
WidgetIconConfig? |
null |
Icon- oder Emoji-Definition |
title |
TextSourceConfig? |
null |
Titelquelle |
value |
TextSourceConfig? |
null |
Wertquelle |
onWidgetActions |
List<WidgetOverlayActionConfig> |
[] |
zusätzliche Overlay-Aktionen direkt auf der Widget-Karte |
image |
WidgetImageConfig |
contentScale = crop |
Bilddarstellung |
chart |
WidgetChartConfig? |
null |
Chart-Konfiguration |
thermometer |
WidgetThermometerConfig? |
null |
Thermometerdetails |
gauge |
WidgetGaugeConfig? |
null |
Gauge-Details |
slider |
WidgetSliderConfig? |
null |
Sliderdetails |
select |
WidgetSelectConfig? |
null |
Selectdetails |
shutter |
WidgetShutterConfig? |
null |
Rollladendetails |
typography |
WidgetTypographyConfig |
leer |
optionale Schriftgrössen |
colors |
WidgetColorsConfig |
leer |
optionale Kachel- und Schriftfarben |
Wichtige Laufzeitregeln:
- wenn
title nicht gesetzt ist, rendert die App keinen automatisch erzeugten Default-Titel
- wenn
value nicht gesetzt ist, bleibt der Wertbereich leer
icon_text kann ohne status als reiner Button modelliert werden
shutter darf ebenfalls ohne status vorkommen, wenn das Widget nur Befehle auslösen soll
cover ist ein akzeptierter Alias für shutter
| Wert |
Bedeutung |
auto |
Bild, falls PNG vorliegt, sonst Icon/Text |
icon_text |
klassisches Text-Widget |
chart |
Canvas-Chart mit mehreren Linienserien |
thermometer |
vertikales Temperatur-Widget mit Röhre, Markerwert und Min-/Max-Beschriftung |
gauge |
halbkreisförmiges Bereichs-Widget mit Wertanzeige und Min-/Max-Labels |
slider |
Widget mit Statusanzeige und Slider-Dialog |
select |
Widget mit Statusanzeige und Select-Dialog |
shutter / cover |
Widget mit Rollladen-Dialog |
image |
Bild-Widget, fällt ohne Bildantwort auf Icon/Text zurück |
| Feld |
Typ |
Default |
Bedeutung |
titleSizeSp |
Int? |
null |
Überschreibt die Titelgrösse |
valueSizeSp |
Int? |
null |
Überschreibt die Wertgrösse |
Wirkung:
- wenn
null, verwendet die App die Material-Theme-Typografie
- wenn gesetzt, werden
fontSize und lineHeight passend angepasst
| Feld |
Typ |
Default |
Bedeutung |
tileColor |
String? |
null |
überschreibt die Kachelfarbe |
titleColor |
String? |
null |
überschreibt die Titelfarbe |
valueColor |
String? |
null |
überschreibt die Value-Farbe |
tileColorRules |
List<ColorRuleConfig> |
[] |
bedingte Farbregeln für den Hintergrund |
titleColorRules |
List<ColorRuleConfig> |
[] |
bedingte Farbregeln für den Titel |
valueColorRules |
List<ColorRuleConfig> |
[] |
bedingte Farbregeln für den Value-Text |
Wirkung:
- wenn nur
tileColor gesetzt ist, verwendet die App automatisch eine kontrastreiche Standard-Schriftfarbe
titleColor und valueColor bleiben optional und überschreiben nur die jeweilige Textart
tileColorRules, titleColorRules und valueColorRules funktionieren wie bei Icons: die erste passende Regel gewinnt
- wenn keine Farbregel passt, verwendet die App die statischen Werte aus
tileColor, titleColor und valueColor als Fallback
- Farbwerte sollten als
#RRGGBB oder #AARRGGBB angegeben werden
| Feld |
Typ |
Default |
Bedeutung |
name |
String? |
null |
statischer Material-Icon-Name |
namePath |
String? |
null |
dynamischer Icon-Name aus der Antwort |
emoji |
String? |
null |
optionales Emoji-Fallback |
sizeDp |
Int? |
null |
optionale Grösse für Icon oder Emoji |
defaultColor |
String |
#2F4C57 |
Standardfarbe für Icon oder Emoji |
colorRules |
List<ColorRuleConfig> |
[] |
bedingte Farbwechsel |
Wichtige Laufzeitregeln:
namePath hat Vorrang vor name
- wenn kein
icon gesetzt ist, wird kein Fallback-Icon gerendert
- wenn kein Material-Icon aufgelöst werden kann, wird ein vorhandenes
emoji gezeigt
sizeDp überschreibt die Standardgrösse des führenden Icons
- Emojis lassen sich technisch nicht wie Material-Icons tinten, deshalb sind
icon.name-basierte Widgets für dynamische Farben vorzuziehen
ColorRuleConfig
| Feld |
Typ |
Bedeutung |
when |
WidgetConditionConfig |
Bedingung für die Regel |
color |
String |
Ziel-Farbwert |
Die erste passende Regel gewinnt.
| Feld |
Typ |
Bedeutung |
path |
String |
JSON-Pfad auf den zu prüfenden Wert |
equals |
String? |
exakter Vergleich |
notEquals |
String? |
Negationsvergleich |
contains |
String? |
Teilstring, case-insensitive |
exists |
Boolean? |
prüft nur, ob ein Wert vorhanden ist |
matches |
String? |
regulärer Ausdruck |
greaterThan |
Double? |
numerisch grösser als |
lessThan |
Double? |
numerisch kleiner als |
truthy |
Boolean? |
boolesche Wahrheitsbewertung |
Alle gesetzten Bedingungen müssen gleichzeitig zutreffen.
TextSourceConfig
| Feld |
Typ |
Default |
Bedeutung |
mode |
TextSourceMode |
static |
Art der Textquelle |
value |
String |
kein Default |
statischer Text, JSON-Pfad oder Template |
fallback |
String? |
null |
Ersatzwert bei leerem Ergebnis |
TextSourceMode
| Wert |
Bedeutung |
static |
value wird direkt verwendet |
template |
value wird als {{path}}-Template ausgewertet |
table |
value wird als Template ausgewertet und danach per Zeilenumbruch in Rows und per | in Spalten aufgeteilt |
path |
value ist ein JSON-Pfad |
Besonderheit:
- wenn
title fehlt oder leer bleibt, rendert die App keinen Titeltext
- Dialoge für Slider, Select, Rollladen und Bestätigungen fallen intern auf die Widget-ID zurück, wenn kein Titel aufgelöst werden kann
- bei
table sind führende oder abschliessende | optional; Markdown-Trennzeilen wie |---|---| werden ignoriert
| Feld |
Typ |
Default |
Bedeutung |
contentScale |
WidgetImageScale |
crop |
Darstellungsmodus für Bilder |
enableDownload |
Boolean |
false |
blendet ein Downloadsymbol für das aktuelle Bild ein |
enableExpand |
Boolean |
false |
blendet ein Vergrößern-Symbol für eine unbeschnittene Vollbildansicht ein |
| Wert |
Bedeutung |
crop |
Bild füllt die Fläche, Überstand wird abgeschnitten |
fit |
gesamtes Bild sichtbar, kann Randänder hinterlassen |
fill_bounds |
Bild wird exakt in die Box gezerrt |
| Feld |
Typ |
Default |
Bedeutung |
icon |
String |
kein Default |
Material-Icon-Name für den Overlay-Button |
label |
String? |
null |
optionales Label für Accessibility und Bestätigungsdialog |
action |
WidgetActionConfig |
kein Default |
auszuführende Aktion |
Wichtige Laufzeitregeln:
icon ist Pflicht; fehlt es, gilt das Widget als fehlerhaft
action verwendet dieselbe Struktur wie widgets[].action
content.onWidgetActions funktioniert bei allen Widget-Arten
- bei Bild-Widgets erscheinen Overlay-Aktionen im selben Bereich wie Download und Vergrößern
- bestehende Konfigurationen mit
image.onWidgetActions werden aus Kompatibilitätsgründen weiterhin gelesen
Kompatibilität bei WidgetImageConfig:
image.onWidgetActions wird weiterhin unterstützt
- für neue Konfigurationen sollte stattdessen
content.onWidgetActions verwendet werden
| Feld |
Typ |
Default |
Bedeutung |
type |
WidgetChartType |
line |
Chart-Typ; V1 ist auf Liniencharts fokussiert |
xAxis |
WidgetChartXAxisConfig |
kind = time |
globale X-Achse |
yAxis |
WidgetChartYAxisConfig |
leer |
globale Y-Achse |
style |
WidgetChartStyleConfig |
showGrid = true, showLegend = true, showDots = false, paddingDp = 8 |
Darstellungsoptionen |
series |
List<WidgetChartSeriesConfig> |
[] |
Serienliste |
Grundidee:
- jede Serie bringt ihr eigenes
datasetPath mit
- das Diagramm muss also kein gemeinsames Dataset verwenden
- die App vereint alle Serien auf eine gemeinsame X-/Y-Domain
- V1 interpoliert fehlende Punkte nicht und rendert bewusst schlank per Canvas
| Feld |
Typ |
Default |
Bedeutung |
kind |
ChartAxisValueKind |
time |
interpretiert x.valuePath als Zeit oder Zahl |
| Feld |
Typ |
Default |
Bedeutung |
min |
Double? |
null |
feste Untergrenze; sonst automatisch aus den Serien |
max |
Double? |
null |
feste Obergrenze; sonst automatisch aus den Serien |
unit |
String? |
null |
optionale Einheit für die Legende |
zeroLine |
Boolean |
false |
zeichnet eine Null-Linie, wenn 0 im Domain-Bereich liegt |
| Feld |
Typ |
Default |
Bedeutung |
showGrid |
Boolean |
true |
zeichnet ein leichtes Raster |
showLegend |
Boolean |
true |
zeigt unterhalb des Diagramms eine kompakte Legende |
showDots |
Boolean |
false |
zeichnet Punktmarker; bei sehr vielen Punkten begrenzt die App die Marker automatisch |
paddingDp |
Int |
8 |
Innenabstand innerhalb der Plot-Fläche |
| Feld |
Typ |
Default |
Bedeutung |
id |
String |
kein Default |
stabile Serien-ID |
label |
String? |
null |
Label für die Legende; fällt auf id zurück |
datasetPath |
String |
kein Default |
JSON-Pfad auf das Array dieser Serie |
x |
WidgetChartValueSourceConfig |
kein Default |
X-Wert pro Array-Eintrag |
y |
WidgetChartValueSourceConfig |
kein Default |
Y-Wert pro Array-Eintrag |
color |
String |
#0E7490 |
Linienfarbe |
strokeWidthDp |
Int |
2 |
Liniendicke |
lineStyle |
WidgetChartLineStyle |
solid |
Voll- oder gestrichelte Linie |
hidden |
Boolean |
false |
blendet die Serie aus, ohne sie aus dem JSON zu entfernen |
| Feld |
Typ |
Default |
Bedeutung |
valuePath |
String |
kein Default |
JSON-Pfad innerhalb eines Dataset-Eintrags |
| Feld |
Typ |
Default |
Bedeutung |
min |
Double |
kein Default |
untere Skalenbegrenzung |
max |
Double |
kein Default |
obere Skalenbegrenzung |
valuePath |
String |
kein Default |
JSON-Pfad zum numerischen Temperaturwert |
gradientColors |
List<String> |
[] |
optionale Farbwerte von unten nach oben; leer ergibt standardmaessig Blau nach Rot |
Laufzeitverhalten:
- das Widget rendert oben
max und unten min als kleine Skalenlabels
- der Fuellstand in der Roehre wird aus
valuePath auf den Bereich min..max geclamped
- der Werttext sitzt rechts neben der Roehre auf der passenden Hoehe
- wenn
content.value fehlt, zeigt der Marker den numerisch formatierten Rohwert aus valuePath
gradientColors erwartet mindestens zwei Farben; ungueltige oder zu kurze Listen fallen auf den Standardverlauf zurueck
| Feld |
Typ |
Default |
Bedeutung |
min |
Double |
kein Default |
untere Skalenbegrenzung |
max |
Double |
kein Default |
obere Skalenbegrenzung |
valuePath |
String |
kein Default |
JSON-Pfad zum numerischen Wert |
gradientColors |
List<String> |
[] |
optionale Farbwerte von min nach max; leer ergibt standardmaessig Blau nach Rot |
Laufzeitverhalten:
- das Widget rendert eine halbkreisförmige Gauge mit Markerpunkt
- links und rechts erscheinen
min und max als kleine Labels
- der Werttext sitzt mittig in der Öffnung des Gauge-Bogens
- wenn
content.value fehlt, zeigt die Mitte den numerisch formatierten Rohwert aus valuePath
gradientColors erwartet mindestens zwei Farben; ungueltige oder zu kurze Listen fallen auf den Standardverlauf zurück
ChartAxisValueKind
| Wert |
Bedeutung |
time |
akzeptiert numerische Werte oder ISO-Zeitstrings |
number |
liest den X-Wert numerisch |
| Wert |
Bedeutung |
solid |
durchgezogene Linie |
dashed |
gestrichelte Linie |
Typabhängige Pflichtfelder
| Modus |
Pflichtfelder |
icon_text |
kein status nötig, wenn das Widget als reiner Button ohne Statusanzeige dienen soll |
chart |
content.chart mit mindestens einer Serie |
thermometer |
content.thermometer |
gauge |
content.gauge |
slider |
content.slider, action.type = endpoint, action.request |
select |
content.select |
shutter |
content.shutter; status darf fehlen, wenn das Widget nur als Befehlsstarter dient |
image |
keine zus. Pflichtfelder, aber sinnvollerweise ein PNG-Endpoint |
Hinweis zum Rollladen-Schema:
- auch bei
shutter ohne status bleibt content.shutter.valuePath im aktuellen Schema ein Pflichtfeld
{
"id": "hot-water-boost",
"content": {
"mode": "icon_text",
"icon": {
"name": "water",
"defaultColor": "#0E7490"
},
"title": {
"mode": "static",
"value": "Warmwasser jetzt"
}
},
"action": {
"request": {
"url": "http://192.168.1.20/api/hot-water/boost-now"
},
"prompt": true
}
}
Beispiel: Multi-Serien-Chart mit getrennten Datensätzen
{
"id": "energy-history",
"layout": {
"order": 28,
"size": "3/2"
},
"content": {
"mode": "chart",
"icon": {
"name": "bolt",
"defaultColor": "#C96C1A"
},
"title": {
"mode": "static",
"value": "Energiefluss heute"
},
"value": {
"mode": "static",
"value": "PV, Haus und Batterie"
},
"chart": {
"xAxis": {
"kind": "time"
},
"yAxis": {
"unit": "W",
"zeroLine": true
},
"series": [
{
"id": "pv",
"label": "PV",
"datasetPath": "pv.history",
"x": { "valuePath": "timestamp" },
"y": { "valuePath": "watts" },
"color": "#E8B400"
},
{
"id": "house",
"label": "Haus",
"datasetPath": "house.history",
"x": { "valuePath": "time" },
"y": { "valuePath": "watts" },
"color": "#2563EB"
},
{
"id": "battery",
"label": "Batterie",
"datasetPath": "battery.points",
"x": { "valuePath": "ts" },
"y": { "valuePath": "value" },
"color": "#059669",
"lineStyle": "dashed"
}
]
}
},
"status": {
"request": {
"url": "https://example.org/api/energy/history"
}
}
}
Beispiel: Thermometer mit eigenem Farbverlauf
{
"id": "outside-temperature",
"layout": {
"order": 22,
"size": "2/2"
},
"content": {
"mode": "thermometer",
"icon": {
"name": "device_thermostat",
"defaultColor": "#2563EB"
},
"title": {
"mode": "static",
"value": "Aussentemperatur"
},
"value": {
"mode": "template",
"value": "{{weather.outdoor.temperature}} °C"
},
"thermometer": {
"min": -20,
"max": 40,
"valuePath": "weather.outdoor.temperature",
"gradientColors": [
"#2563EB",
"#14B8A6",
"#F97316",
"#DC2626"
]
}
},
"status": {
"request": {
"url": "https://example.org/api/weather/current"
}
}
}
Beispiel: Gauge mit Batteriestatus
{
"id": "battery-charge",
"layout": {
"order": 29,
"size": "2/2"
},
"content": {
"mode": "gauge",
"icon": {
"name": "bolt",
"defaultColor": "#0F766E"
},
"title": {
"mode": "static",
"value": "Batterie"
},
"value": {
"mode": "template",
"value": "{{battery.soc}} %"
},
"gauge": {
"min": 0,
"max": 100,
"valuePath": "battery.soc",
"gradientColors": [
"#DC2626",
"#F59E0B",
"#16A34A"
]
}
},
"status": {
"request": {
"url": "https://example.org/api/energy/storage"
}
}
}
Beispiel
{
"id": "power-overview",
"layout": {
"order": 30,
"size": "3/1"
},
"content": {
"mode": "icon_text",
"icon": {
"name": "bolt",
"namePath": "grid.icon_name",
"defaultColor": "#C96C1A",
"colorRules": [
{
"when": {
"path": "grid.draw_watts",
"greaterThan": 0.0
},
"color": "#D9485F"
}
]
},
"title": {
"mode": "static",
"value": "Leistungsübersicht"
},
"value": {
"mode": "table",
"value": "Quelle | Leistung\nNetz | {{grid.draw_watts}} W\nPV | {{pv.watts}} W"
},
"colors": {
"tileColor": "#1E293B",
"titleColor": "#E2E8F0",
"valueColor": "#F8FAFC",
"tileColorRules": [
{
"when": {
"path": "grid.draw_watts",
"greaterThan": 0.0
},
"color": "#FFF4D8"
}
],
"titleColorRules": [
{
"when": {
"path": "grid.draw_watts",
"greaterThan": 0.0
},
"color": "#4A3412"
}
],
"valueColorRules": [
{
"when": {
"path": "grid.draw_watts",
"greaterThan": 0.0
},
"color": "#B45309"
}
]
},
"typography": {
"titleSizeSp": 18,
"valueSizeSp": 22
}
},
"status": {
"request": {
"url": "https://example.org/api/energy/current"
}
}
}
Weiterführend