Zum Hauptinhalt springen Skip to page footer

Dashboard auf Basis eines ESP32

Man kann für viel Geld Dashboards die kompatibel zu iobroker und Homematic sind kaufen. Man kann aber auch selbst Hand anlegen und sich eine maßgeschneiderte Lösung mit viel Spaß und Lernerfolg zusammenbauen.

Schon länger meldet ein im Wohnzimmer installierter Lautsprecher beim öffnen der Haustür, das noch Fenster offen sind. Aber eben im Wohnzimmer und nicht im Flur. Beim Verlassen der Wohnung will man doch mit einem Blick erfassen, wie denn der Status zu Licht, Fenster und weiteren Smart Home Daten ist. Lösungen in Form von Dashboards die iobroker kompatibel sind wirft die Suchmaschine des Vertrauens auch einige aus. Aber so richtig den Vorstellungen und den Anpassungswünschen entsprechen sie dann leider nicht.

Einen ESP32 zu programmieren ist zum Glück kein Hexenwerk mehr. Also schnell die Wunschliste zusammengeschrieben und die Zutaten bestellt:

- Neopixel mit 8 LEDs in Stickform um visuell per RGB-Leuchtdioden verschiedene Status anzuzeigen
- ein 1,77" TFT Display für Statusmeldungen (mit ST7735 Bildcontroller)
- ein PIR Melder, der LEDs und Displays abschaltet, wenn sie nicht benötigt werden (HC-SR501 PIR Modul)
- ein MP3 Modul mit Lautsprecher für Statusdurchsagen (Mini MP3 Player DFPlayer Master Module und ein kleiner 8Ohm, 0.25Watt) und microSD-Karte
- ein RF-ID Modul zur An- und Abmeldung per NFC Karte (RFC522 Modul)
- ein ESP32 WROOM Modul, da es doch einiges an Ein-/Ausgängen braucht und der Prozessor sich per WLAN ins smarte Heim einbinden soll

Beim Aufbau hat mir noch ein Breadboard mit Jumperkabeln gute Dienste geleistet.

Es sind dann doch einige Kabel geworden, und nach ein paar Versuchsrunden wurden diese auch funktionsfähig an die verschiedenen Ports des ESP32 angeschlossen.

Was soll die Platine nun in Summe tun? Primär mal als Frontend arbeiten, ohne viel eigene Steuerungslogik. Das soll die iobroker Instanz im Hintergrund per Skript übernehmen und die entsprechenden Kommandos per MQTT übermitteln. Der ESP32 wertet diese aus und steuert die entsprechenden Anzeigen, sofern er eine Bewegung über den PIR Melder entdeckt hat. Letzteres macht er allerdings dann doch autark per geflashtem Code. Hintergrund ist schlicht und ergreifend der Ansatz, das Backlight des Displays zu schonen und natürlich auch Strom zu sparen. Das TFT Display bekommt ebenfalls per MQTT über 6 Zeilen verteilt den anzuzeigenden Text, jeweils als Überschrift/Status-Pärchen, übermittelt. So kann z.B. unter dem Titel "Fenster" die Anzahl derselbigen noch offen stehenden angezeigt werden. Auch kann per Trigger einer der auf der microSD-Karte abgelegten MP3-Dateien abgespielt werden. Damit ist ein akustischer Hinweis, dass noch Fenster offen stehen, nun auch im Flur möglich. Und schlussendlich steht ein NFC Reader zur Verfügung, über den man sich an und abmelden kann.

Wie man die Arduino IDE installiert, die notwendigen Bibliotheken einbindet und den Code auf den ESP32 flasht ist an vielen Stellen im Internet in gut nachvollziehbaren Tutorials beschrieben. Persönlich habe ich mich per Buch eingelesen und fand das Makerbuch zum Arduino ganz hilfreich, wenn es auch nicht im Kern auf den ESP abzielt, sondern wie der Titel schon verrät auf den Arduino.

Der Aufbau der Schaltung ist in diversen Etappen zu schaffen:

- Installation und Inbetriebnahme Arduino IDE
- Aufbau der Schaltung rund um den ESP32 mit allen Peripherieelementen
- Flashen des Codes auf den ESP32
- ggfs. Einbau in ein Gehäuse (ich habe vor hier einen Bilderrahmen zu verwenden)
- Einbinden und Anpassen der Scripte in iobroker
- Test und finale Inbetriebnahme

ESP32 Code

/*
 * Dashboard
 *
 * Displaying information provided by MQTT from an iobroker/Homematic instance
 * Dashboard acts as a client, whilst business logic is located in iobroker
 * This is due to more flexibility as well as security considerations
 *
 * Copyright (c) 2023 Martin Arend
 * This software is published under GNU General Public License version 3 or later
 * www.gnu.org/licenses/gpl-3.0.txt 
 * 
 */

// Open Tasks:
// ToDo: clean all input via MQTT (Hex and Strings)

// including libraries
#include <Adafruit_NeoPixel.h>          // NeoPixel library
#include <WiFi.h>                       // WiFi library
#include <PubSubClient.h>               // MQTT library
#include <Adafruit_GFX.h>               // Core graphics library
#include <Adafruit_ST7735.h>            // Hardware-specific library
#include <SPI.h>                        // SPI library
#include <Fonts/FreeSans9pt7b.h>        // include font (weight: normal) for TFT
#include <Fonts/FreeSansBold9pt7b.h>    // include font (weight: bold) for TFT
#include <SoftwareSerial.h>             // include Serial library for MP3 Player
#include <DFRobotDFPlayerMini.h>        // include MP3player lib
#include <MFRC522.h>                    // include RF-ID lib

// define Clients
WiFiClient espClient;                   // WiFi Client
PubSubClient client(espClient);         // MQTT Client

// define arrays to store MQTT payloads for LEDs, TFT, RF-UID and String Programmstate
String payloadLED[8];
String Headline[3];
String TftText[3];
String PSTATE;
char message_buff[100];

// define setup for LED bar (Neopixel)
#define PIN            12
#define NUMPIXELS      8
#define BRIGHTNESS     20
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

// define setup for PIR
#define PIRpin         14

// define setup for WiFi connection
const char* ssid = "SSID DEINES WLANS";
const char* password = "DEIN WLAN PASSWORT";

// define MQTT Broker Credentials
const char* MQTT_BROKER = "IP.DEINES.MQTT.BROKERS";  // Broker IP Adresse
const char* MQTT_USER = "USER";                      // Broker User
const char* MQTT_PASSWORD = "MQTT USER PASSWORT";    // Broker Passwort

// define setup for TFT display
#define TFT_CS         5
#define TFT_RST        4
#define TFT_DC         2
#define TFT_MOSI       26  // Data out - 23
#define TFT_SCLK       25  // Clock out - 18
#define TFT_BACKLIGHT  27  // TFT Backlight
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);

// define color set for TFT display
const uint16_t  Black        = 0x0000;
const uint16_t  Blue         = 0x001F;
const uint16_t  Red          = 0xF800;
const uint16_t  Green        = 0x07E0;
const uint16_t  White        = 0xFFFF;
uint16_t  Text_Color         = White;
uint16_t  Background_Color   = Black;

// define display timings
long now = millis();
long timeDisplayOff = now;
const int HYSTERESIS = 100000; // time in ms after display switches off again 

// define MP3player
int volume = 15;                          // volume 0 - 30
#define MP3_TX         22
#define MP3_RX         21
SoftwareSerial MP3Player(MP3_RX, MP3_TX); // RX, TX
DFRobotDFPlayerMini dfPlayer;

// define RF-ID
#define SS_PIN         16
#define RST_PIN        17
MFRC522 rfid(SS_PIN, RST_PIN);

// ***************************************************************************
// Interrupt routine for PIR 
void IRAM_ATTR detectsMovement() {
  Serial.print(millis());
  Serial.println("ms - Motion detected");
  timeDisplayOff = millis() + HYSTERESIS;
  Serial.println("Switch off at " + String(timeDisplayOff) + "ms");   
}

// ***************************************************************************
// Setup routine
// ***************************************************************************
void setup() 
{
  // setup Serial  
  Serial.begin(115200);
  SPI.begin();                        //init SPI bus
  Serial.println("Booting...");

  // setup Statemachine
  PSTATE = "BOOT";

  // setup NeoPixel
  pixels.begin();                     // This initializes the NeoPixel library.
  pixels.setBrightness(BRIGHTNESS);   // sets brightness for the NeoPixel
  pixels.show();                      // initialize pixel to all off

  // setup TFT Display
  pinMode(TFT_BACKLIGHT, OUTPUT);     // define Pin as output to control TFT backlight
  digitalWrite(TFT_BACKLIGHT, HIGH);  // switch TFT backlight on
  tft.initR(INITR_BLACKTAB);          // init TFT driver
  tft.setFont(&FreeSans9pt7b);        // load font for display
  tft.fillScreen(Background_Color);
  delay(500);
  tft.setCursor(25, 42);
  tft.setTextSize(1);
  tft.setTextColor(Blue);
  tft.println("miniSOFT");
  tft.setTextColor(Text_Color);
  tft.setCursor(15, 62);
  tft.println("SMARTdash");  
  tft.setFont();
  tft.setCursor(30, 100);
  tft.print("Booting   ");

  // wait for PIR, play animation
  Serial.println("Wait for Motion Sensor.");
  // animation
  for (int i = 0; i < 8; i++) { 
    for (int j = 0; j < 25; j++) { 
      pixels.setPixelColor(i, pixels.Color(j*10,j*10,j*10));
      pixels.show();
      delay(14);
    }
  }
  // end animation
  Serial.println("Motion Sensor setup finished.");
  tft.fillRect(3, 90, 120, 140, Background_Color);
  tft.setCursor(30, 100);  
  tft.print("PIR ready  ");

  // setup PIR
  pinMode(PIRpin, INPUT_PULLUP);
  // Set motionSensor pin as interrupt, assign interrupt function and set RISING mode
  attachInterrupt(digitalPinToInterrupt(PIRpin), detectsMovement, RISING);

  // setup MP3 Player
  Serial.println("Setup MP3 Player.");
  tft.fillRect(3, 90, 120, 140, Background_Color);
  tft.setCursor(30, 100);  
  tft.print("Init MP3  ");
  
  MP3Player.begin(9600);

  if (!dfPlayer.begin(MP3Player)) {                           //use softwareSerial to communicate with mp3
    Serial.println(F("Unable to begin:"));
    Serial.println(F("1. Please recheck the connection!"));
    Serial.println(F("2. Please insert the SD card!"));
  } else {
    Serial.println(F("MP3 Player online."));
  }
  
  dfPlayer.volume(volume);                                    //sets volume for MP3 Player
  dfPlayer.play(1);                                           //play the first mp3 as startup sound

  // setup RF-ID
  Serial.println("Setup RF-ID.");
  tft.fillRect(3, 90, 120, 140, Background_Color);
  tft.setCursor(30, 100);  
  tft.print("Init RF-ID  ");
  rfid.PCD_Init();                                            //init MFRC522

  // switch all LEDs blue to indicate searching for WiFi
  for (int i = 0; i < 8; i++) { 
    pixels.setPixelColor(i, pixels.Color(0,0,255));
    pixels.show();    
  }
  
  // setup WiFi and MQTT
  initWiFi();
  client.setServer(MQTT_BROKER, 1883);

  // initialisation all done - switch all LEDs off
  for (int i = 0; i < 8; i++) { 
    pixels.setPixelColor(i, pixels.Color(0,0,0));
    pixels.show();    
  }
  tft.fillRect(3, 90, 120, 140, Background_Color);
  tft.setCursor(30, 100);
  tft.print("WiFi ready");

  // setup MQTT callback
  client.setCallback(callback);
  tft.fillRect(3, 90, 120, 140, Background_Color);
  tft.setCursor(30, 100);
  tft.print("Ready");
  delay(1000);

  // clear TFT and load font
  tft.fillScreen(Background_Color);
  tft.setFont(&FreeSans9pt7b);

  // setup Statemachine to listen to MQTT messages
  PSTATE = "MQTT_READ";
  digitalWrite(TFT_BACKLIGHT, LOW); // switch TFT backlight off
  
}

// ***************************************************************************
// Main routine
// ***************************************************************************
void loop() 
{
  // MQTT
  if (!client.connected()) {
    while (!client.connected()) {
      client.connect("ESPDashboard", MQTT_USER, MQTT_PASSWORD);
      client.subscribe("dashboard/LED1");
      client.subscribe("dashboard/LED2");
      client.subscribe("dashboard/LED3");
      client.subscribe("dashboard/LED4");
      client.subscribe("dashboard/LED5");
      client.subscribe("dashboard/LED6");
      client.subscribe("dashboard/LED7");
      client.subscribe("dashboard/LED8");
      client.subscribe("dashboard/Headline1");
      client.subscribe("dashboard/Text1");
      client.subscribe("dashboard/Headline2");
      client.subscribe("dashboard/Text2");
      client.subscribe("dashboard/Headline3");
      client.subscribe("dashboard/Text3");
      client.subscribe("dashboard/MP3trigger");
      client.subscribe("dashboard/RFID");
      delay(1000);
    }
  }
  client.loop();

  if (PSTATE = "MQTT_READ") {
    // activate display only on detected motion
    if(millis() < timeDisplayOff) { // display + LED on
      for (int i = 0; i < 8; i++) { 
        hexrgb(payloadLED[i], i); 
      }
      digitalWrite(TFT_BACKLIGHT, HIGH);  // switch TFT backlight on
      dfPlayer.volume(volume);
    } else { // no motion, no trigger and/or hysteresis expired  
      for (int i = 0; i < 8; i++) { 
        pixels.setPixelColor(i, pixels.Color(0,0,0));
        pixels.show();  
      }
      digitalWrite(TFT_BACKLIGHT, LOW);  // switch TFT backlight off
      //dfPlayer.volume(0);                // switches off the speaker if display is off
    }
  }  

  if (rfid.PICC_IsNewCardPresent()) {                             // new tag is available
    if (rfid.PICC_ReadCardSerial()) {                             // NUID has been read
      // publish that a new card is detected via MQTT
      client.publish("dashboard/RFuid", "new card");
      
      // read out new RF-UID
      MFRC522::PICC_Type piccType = rfid.PICC_GetType(rfid.uid.sak);
      Serial.print("RFID/NFC Tag Type: ");
      Serial.println(rfid.PICC_GetTypeName(piccType));

      // read UID in the hex format
      Serial.print("UID:");
      String rfidUid = "";
        for (byte i = 0; i < rfid.uid.size; i++) {
          rfidUid += String(rfid.uid.uidByte[i] < 0x10 ? "0" : "");
          rfidUid += String(rfid.uid.uidByte[i], HEX);
        }

      Serial.println(rfidUid);

      // publish UID via MQTT
      rfidUid.toCharArray(message_buff, rfidUid.length() + 1);
      client.publish("dashboard/RFuid", message_buff);
      
      rfid.PICC_HaltA();                                          // halt PICC
      rfid.PCD_StopCrypto1();                                     // stop encryption on PCD
    }
  }  
}

// ***************************************************************************
void hexrgb(String msg, int LEDnum) {
  // conversion HEX to RGB
    long rgb = strtol(&msg[1], NULL, 16);
    byte r = rgb>>16;
    byte g = rgb>>8;
    byte b = rgb;    
    pixels.setPixelColor(LEDnum, pixels.Color(r,g,b));
    pixels.show(); 
}

void callback(char* topic, byte* payload, unsigned int length) {
  // decode MQTT payload
  String msg = "";
  for (byte i = 0; i < length; i++) {
    char tmp = char(payload[i]);
    msg += tmp;
  }
  
  // store MQTTpayload per LED
  if(strcmp(topic, "dashboard/LED1") == 0) {
    payloadLED[0] = msg;
    Serial.println("LED1: " + payloadLED[0]);      
  } else if(strcmp(topic, "dashboard/LED2") == 0) {
    payloadLED[1] = msg;
    Serial.println("LED2: " + payloadLED[1]);      
  } else if(strcmp(topic, "dashboard/LED3") == 0) {
    payloadLED[2] = msg;
    Serial.println("LED3: " + payloadLED[2]);          
  } else if(strcmp(topic, "dashboard/LED4") == 0) {
    payloadLED[3] = msg;
    Serial.println("LED4: " + payloadLED[3]);      
  } else if(strcmp(topic, "dashboard/LED5") == 0) {
    payloadLED[4] = msg;
    Serial.println("LED5: " + payloadLED[4]);      
  } else if(strcmp(topic, "dashboard/LED6") == 0) {
    payloadLED[5] = msg;
    Serial.println("LED6: " + payloadLED[5]);      
  } else if(strcmp(topic, "dashboard/LED7") == 0) {
    payloadLED[6] = msg;
    Serial.println("LED7: " + payloadLED[6]);      
  } else if(strcmp(topic, "dashboard/LED8") == 0) {
    payloadLED[7] = msg;
    Serial.println("LED8: " + payloadLED[7]);      
  } else if(strcmp(topic, "dashboard/Headline1") == 0) {
    if (msg != Headline[0]) {
      tft.setFont(&FreeSansBold9pt7b);
      tft.setTextColor(Background_Color);
      tft.setCursor(10, 25);
      tft.print(Headline[0]);
      tft.setCursor(10, 25);
      tft.setTextColor(Blue);
      tft.print(msg);
      Headline[0] = msg;
      Serial.println("Headline1: " + Headline[0]);  
    }    
  } else if(strcmp(topic, "dashboard/Text1") == 0) {
    if (msg != TftText[0]) {
      tft.setFont(&FreeSans9pt7b);
      tft.setTextColor(Background_Color);
      tft.setCursor(10, 45);
      tft.print(TftText[0]);
      tft.setCursor(10, 45);
      tft.setTextColor(Text_Color);
      tft.print(msg);
      TftText[0] = msg;
      Serial.println("Text1: " + TftText[0]); 
    }     
  } else if(strcmp(topic, "dashboard/Headline2") == 0) {
    if (msg != Headline[1]) {
      tft.setFont(&FreeSansBold9pt7b);
      tft.setTextColor(Background_Color);
      tft.setCursor(10, 75);
      tft.print(Headline[1]);
      tft.setCursor(10, 75);
      tft.setTextColor(Blue);
      tft.print(msg);
      Headline[1] = msg;
      Serial.println("Headline2: " + Headline[1]);      
    }
  } else if(strcmp(topic, "dashboard/Text2") == 0) {
    if (msg != TftText[1]) {
      tft.setFont(&FreeSans9pt7b);
      tft.setTextColor(Background_Color);
      tft.setCursor(10, 95);
      tft.print(TftText[1]);
      tft.setCursor(10, 95);
      tft.setTextColor(Text_Color);
      tft.print(msg);
      TftText[1] = msg;
      Serial.println("Text2: " + TftText[1]);
    }      
  } else if(strcmp(topic, "dashboard/Headline3") == 0) {
    if (msg != Headline[2]) {
      tft.setFont(&FreeSansBold9pt7b);
      tft.setTextColor(Background_Color);
      tft.setCursor(10, 125);
      tft.print(Headline[2]);
      tft.setCursor(10, 125);
      tft.setTextColor(Blue);
      tft.print(msg);
      Headline[2] = msg;
      Serial.println("Headline3: " + Headline[2]);      
    }
  } else if(strcmp(topic, "dashboard/Text3") == 0) {
    if (msg != TftText[2]) {
      tft.setFont(&FreeSans9pt7b);
      tft.setTextColor(Background_Color);
      tft.setCursor(10, 145);
      tft.print(TftText[2]);
      tft.setCursor(10, 145);
      tft.setTextColor(Text_Color);
      tft.print(msg);
      TftText[2] = msg;
      Serial.println("Text3: " + TftText[2]);      
    }
  } else if(strcmp(topic, "dashboard/MP3trigger") == 0) {
      if (msg != "off") {
        Serial.println("MP3: " + msg);  
        dfPlayer.volume(volume);                                    //sets volume for MP3 Player
        dfPlayer.play(msg.toInt());
      }
  }
}

void initWiFi() {
  // init WIFI connection with provided credentials
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi ..");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(1000);
  }
  Serial.println(WiFi.localIP());
}

Wie immer sei an dieser Stelle der Hinweis erlaubt, dass ich Autodidakt bin. Der Code hat noch einiges Optimierungspotential und kann sicher an der ein oder anderen Stelle eleganter als jetzt ausfallen. Für den Moment tut er aber was er soll.

Die Statemachine habe ich für eine zukünftige Erweiterung angelegt. Sie wird aber noch nicht wirklich genutzt und kann daher ignoriert werden.

Wichtig ist, die eigenen Credentials für das WLAN und den MQTT Broker, der bei mir Tei der iobroker Instanz ist, anzugegeben. Sonst wird es nicht funktionieren.

Nach dem Start initialisiert der ESP32 alle Elemente der Peripherie und zeigt während dem Bootvorgang auf dem Display auch die einzelnen Schritte die er durchläuft an. Der Neopixel-Stick wird ebenfalls in die Anzeige der Startup-Sequenz eingebunden und animiert z.B. die Wartezeiten bei der Initialisierung und schaltet die LEDs auf blau, während auf die Netzwerkverbindung gewartet wird. Auch erfolgt eine erste Tonausgabe, ein Startup-Sound, indem die erste auf der microSD Karte liegende MP3 Datei abgespielt wird.

Ist alles initialisiert, hört der ESP32 per MQTT auf eingehende Nachrichten und wertet diese aus:

- Unter dashboard/LED1 bis /LED8 werden Hexwerte für die Farbsteuerung der LEDs erwartet. #000000 schaltet die LED aus, #FFFFFF auf weiß und z.B. #FF0000 auf rot.
- dashboard/Headline1 bis /Headline3 und die zugehörigen dashboard/Text1 bis /Text3 nehmen Texte im Stringformat zur Anzeige auf.
- dashboard/MP3trigger erwartet eine Integer Zahl um die dazugehörige auf der microSD Karte liegende MP3 Datei abzuspielen
- und schließlich sendet der ESP32 an den MQTT-Broker die UID der aufgelegten NFC Karte unter dashboard/RFID. 

Nochmal der Hinweis: der Code beinhaltet zum jetzigen Zeitpunkt keinerlei Fehlerkorrekturen. Auch werden eingehende MQTT Nachrichten nicht auf syntaktische Korrektheit oder Zulässigkeit geprüft. Das wird zu einem späteren Zeitpunkt noch erfolgen.

Ansonsten habe ich versucht, den Code an den entscheidenden Stellen zu kommentieren.

Aufbau des ESP32

ESP32 TFT Display
G27 GPIO27 27 8 LEDA
5V V5 2 VCC
GND GND 1 GND
SP VSPI_CLK 25 3 SCK
SN VSPI_MOSI 26 4 SDA
G4 GPIO4 4 5 RES
G2 GPIO2 2 6 RS
G5 VSPI_CS 5 7 CS
ESP32 MP3 Modul  
5V V5 1 VCC
- - 6 SPK1 Speaker
- - 8 SPK2 Speaker
GND GND 7 GND
G22 GPIO22 22 3 TX
G21 GPIO21 21 2 RX
ESP32 RF-ID  
G16 GPIO16 16 1 SDA
G18 GPIO18 18 2 SCK
G23 GPIO23 23 3 MOSI
G19 GPIO19 19 4 MISO
- - 5 IRQ
GND GND 6 GND
G17 GPIO17 17 7 RST
3V3 3V3 8 3.3V

Der Zusammenbau erfolgte bei mir mit einer Streifenlochrasterplatine. Wichtig ist zu beachten, dass ihr eine ausreichend dimensionierte Stromquelle verwendet. Ich versorge den ESP32 über ein handelsübliches USB Ladegerät mit entsprechendem Stecker. Aber bei den "nur" 500mA die eine Computer-USB Buchse liefert hing sich bei mir regelmäßig der MP3 Player auf, wenn gleichzeitig das Backlight des TFT Displays und die LEDs eingeschaltet waren. Erst eine Versorgung mit > 1A über die USB Schnittstelle führten zu einem stabilen Betrieb!

Denkt auch daran, den PIR Melder Euren Wünschen entsprechend einzustellen. An den beiden Potis lassen sich Ansprechzeit und -reichweite per Schraubendreher nach eigenem Gusto manuell einstellen.

iobroker

Auch für den guten iobroker gibt es deutlich bessere Tutorials im Netz als ich sie schreiben könnte. Inklusive der Installation eines MQTT Brokers. Zu Debug und Entwicklungszwecken habe ich parallel noch die MQTTBox im Einsatz. Mit diesem Tool kann ich im SmartHome den entsprechenden Datenverkehr mithören und schnell herausfinden ob und ggfs auch was klemmt.

Was nun noch fehlt, sind die passenden Blockly Scripte zur Steuerung des Dashboards. 

Hier ein Beispielscript, dass die offenen Fenster im Erdgeschoss zählt.

Es bedient sich der Aufzählung Fenster, sprich alle Sensoren sind entsprechend in diese Kategorie einsortiert. Falls sich nun ein Zustand einer oder mehrerer der in dieser Liste aufgenommenen Sensoren ändert, triggert das Script. Es wird der Status Sensor für Sensor über die Schleife gezählt und in der Variablen gespeichert. Danach wird per MQTT das entsprechende Kommando (LED rot oder LED grün) an das Dashboard geschickt. 

Ich habe zusätzlich noch das "steuere"-Kommando mit eingebaut und aktualisiere damit in der iobroker internen Zustandsverwaltung die jeweilige Farbe. Das hat den Hintergrund, dass bei einem Reset oder gewollten Neustart des Dashboards der zuletzt gültige Wert gleich wieder subscribed und damit sofort angezeigt werden kann.

Wie man sieht, ist die LED3 in diesem Beispielscript dem Fensterstatus im Erdgeschoss zugeordnet. Analog kann man nun weitere Status per Blockly Script aufsetzen und die weiteren LEDs ansteuern. Profis können das sicher alles in einem Script zusammenfassen. Ich habe pro LED ein eigenes angelegt. Ideen habt ihr sicher genug.

Wünsche viel Erfolg beim Nachbauen und der Gestaltung Eures persönlichen Dashboards.