Beispiel: Vollständige Dashboard-Konfiguration¶
Dieses Beispiel kombiniert die wichtigsten Features in einer zusammenhängenden Datei.
Vollbeispiel¶
{
"schemaVersion": 1,
"dashboard": {
"title": "Mein Smart Home",
"defaultTabId": "home",
"layout": {
"portraitColumns": 4,
"landscapeColumns": 6,
"spacingDp": 14,
"contentPaddingDp": 16,
"cornerRadiusDp": 26
},
"groups": [
{
"id": "living-room-controls",
"title": "Wohnzimmer",
"order": 14,
"widthColumns": 6,
"highlightColor": "#B45309",
"borderColor": "#F59E0B",
"widgetIds": [
"living-room-light",
"living-room-dimmer",
"living-room-scene",
"living-room-shutter"
]
}
],
"tabs": [
{
"id": "home",
"title": "Zuhause",
"order": 10,
"widgetIds": [
"outdoor-camera"
],
"groupIds": [
"living-room-controls"
]
},
{
"id": "energy",
"title": "Energie",
"order": 20,
"widgetIds": [
"power-overview",
"api-health",
"open-camera-page",
"go-home-tab"
]
}
]
},
"defaults": {
"request": {
"timeoutMs": 7000,
"headers": {
"Accept": "application/json, image/png, image/jpeg"
}
},
"sharedStatusRequests": [
{
"id": "living-room-light-state",
"request": {
"url": "http://192.168.1.20/api/lights/living-room"
}
}
]
},
"widgets": [
{
"id": "living-room-light",
"layout": {
"order": 10,
"size": "1/1"
},
"content": {
"mode": "icon_text",
"icon": {
"name": "lightbulb",
"defaultColor": "#64748B",
"colorRules": [
{
"when": {
"path": "state",
"equals": "on"
},
"color": "#F59E0B"
}
]
},
"title": {
"mode": "static",
"value": "Wohnzimmer"
},
"value": {
"mode": "template",
"value": "{{brightness}} %"
},
"colors": {
"tileColor": "#FFF4D8",
"titleColor": "#4A3412",
"valueColor": "#B45309"
}
},
"status": {
"sharedRequestId": "living-room-light-state"
},
"action": {
"request": {
"url": "http://192.168.1.20/api/lights/living-room/toggle"
}
}
},
{
"id": "living-room-dimmer",
"layout": {
"order": 15,
"size": "2/1"
},
"content": {
"mode": "slider",
"icon": {
"name": "lightbulb",
"defaultColor": "#E59A20"
},
"title": {
"mode": "static",
"value": "Dimmer"
},
"value": {
"mode": "template",
"value": "{{brightness}} %"
},
"slider": {
"min": 0,
"max": 100,
"step": 5,
"valuePath": "brightness",
"actionValueBindings": [
{
"target": "json_body",
"path": "brightness"
}
]
}
},
"status": {
"sharedRequestId": "living-room-light-state"
},
"action": {
"request": {
"url": "http://192.168.1.20/api/lights/living-room/set-brightness",
"method": "post",
"jsonBody": {
"brightness": 0
}
}
}
},
{
"id": "living-room-scene",
"layout": {
"order": 16,
"size": "2/1"
},
"content": {
"mode": "select",
"icon": {
"name": "remote",
"defaultColor": "#0F766E"
},
"title": {
"mode": "static",
"value": "Szene"
},
"value": {
"mode": "path",
"value": "scene_label"
},
"select": {
"valuePath": "scene",
"optionsPath": "available_scenes",
"optionValuePath": "id",
"optionLabelPath": "label",
"action": {
"request": {
"url": "http://192.168.1.20/api/lights/living-room/scene",
"method": "post",
"jsonBody": {
"scene": ""
}
},
"valueBindings": [
{
"target": "json_body",
"path": "scene"
}
],
"prompt": true
}
}
},
"status": {
"request": {
"url": "http://192.168.1.20/api/lights/living-room/scenes"
}
}
},
{
"id": "living-room-shutter",
"layout": {
"order": 18,
"size": "2/1"
},
"content": {
"mode": "shutter",
"icon": {
"name": "window",
"defaultColor": "#4B5563"
},
"title": {
"mode": "static",
"value": "Rollladen"
},
"value": {
"mode": "template",
"value": "{{position}} %"
},
"shutter": {
"min": 0,
"max": 100,
"step": 5,
"valuePath": "position",
"sliderAction": {
"request": {
"url": "http://192.168.1.20/api/shutters/living-room/position",
"method": "post",
"jsonBody": {
"position": 0
}
},
"valueBindings": [
{
"target": "json_body",
"path": "position"
}
]
},
"upAction": {
"request": {
"url": "http://192.168.1.20/api/shutters/living-room/up"
}
},
"stopAction": {
"request": {
"url": "http://192.168.1.20/api/shutters/living-room/stop"
}
},
"downAction": {
"request": {
"url": "http://192.168.1.20/api/shutters/living-room/down"
}
}
}
},
"status": {
"request": {
"url": "http://192.168.1.20/api/shutters/living-room/status"
}
}
},
{
"id": "outdoor-camera",
"layout": {
"order": 20,
"size": "2/1"
},
"content": {
"mode": "image",
"icon": {
"name": "camera",
"defaultColor": "#2F4C57"
},
"title": {
"mode": "static",
"value": "Eingang"
}
},
"status": {
"request": {
"url": "https://example.org/camera/frontdoor/latest.jpg"
},
"responseKind": "jpg"
}
},
{
"id": "power-overview",
"layout": {
"order": 30,
"size": "3/1"
},
"content": {
"mode": "icon_text",
"icon": {
"name": "bolt",
"namePath": "grid.icon_name",
"defaultColor": "#C96C1A"
},
"title": {
"mode": "static",
"value": "Netzbezug"
},
"value": {
"mode": "template",
"value": "{{grid.draw_watts}} W | PV {{pv.watts}} W"
},
"colors": {
"tileColor": "#1E293B",
"titleColor": "#E2E8F0",
"valueColor": "#F8FAFC"
}
},
"status": {
"request": {
"url": "https://example.org/api/energy/current",
"headers": {
"X-Api-Key": "demo"
}
}
}
},
{
"id": "api-health",
"layout": {
"order": 31,
"size": "1/1"
},
"content": {
"mode": "icon_text",
"icon": {
"name": "router",
"defaultColor": "#0F766E"
},
"title": {
"mode": "static",
"value": "API"
},
"value": {
"mode": "path",
"value": "$"
}
},
"status": {
"request": {
"url": "https://example.org/api/healthz"
},
"responseKind": "text"
}
},
{
"id": "open-camera-page",
"layout": {
"order": 40,
"size": "1/1"
},
"content": {
"mode": "icon_text",
"icon": {
"name": "camera",
"defaultColor": "#2F4C57"
},
"title": {
"mode": "static",
"value": "Kamera"
}
},
"status": {
"request": {
"url": "https://example.org/api/meta/ping"
}
},
"action": {
"type": "open_url",
"url": "https://example.org/camera/frontdoor"
}
},
{
"id": "go-home-tab",
"layout": {
"order": 41,
"size": "1/1"
},
"content": {
"mode": "icon_text",
"icon": {
"name": "home",
"defaultColor": "#0E7490"
},
"title": {
"mode": "static",
"value": "Zuhause"
}
},
"status": {
"request": {
"url": "https://example.org/api/meta/ping"
}
},
"action": {
"type": "switch_tab",
"targetTabId": "home"
}
}
]
}
Warum dieses Beispiel gut skaliert¶
- gemeinsame Lichtdaten laufen über
sharedStatusRequests - Energie- und Kameradaten bleiben getrennt
- das Wohnzimmer wird über eine Gruppe strukturiert
- zwei Tabs verhindern ein überfülltes Einseiten-Dashboard
- interaktive Widgets nutzen jeweils den passenden Dialogtyp
- das Beispiel zeigt zusätzlich
responseKind = "text",open_url,switch_tabund eine bestätigte Select-Aktion
Weitere kurze Beispiele¶
Bestätigter Button ohne Status¶
{
"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,
"refreshAfterAction": true
}
}
Vollbreiter Banner¶
{
"id": "energy-banner",
"layout": {
"order": 5,
"size": "full/1"
},
"content": {
"mode": "icon_text",
"icon": {
"name": "bolt",
"defaultColor": "#C96C1A"
},
"title": {
"mode": "static",
"value": "Energie heute"
},
"value": {
"mode": "template",
"value": "PV {{pv.today_kwh}} kWh | Bezug {{grid.today_kwh}} kWh"
}
},
"status": {
"request": {
"url": "https://example.org/api/energy/today"
}
}
}
Varianten¶
- für ein Einseiten-Dashboard die Tabs ganz weglassen
- für mehrere Profile dasselbe Dashboard in eine
index.jsoneinhängen - für grössere Bereiche
full/1-Widgets als Zeilentrenner einsetzen