Coole Uhr für Symcon - Ulanzi / La Metric Clone

Ich nutze sowohl „custom apps“ (sind eigentlich bloß wiederkehrende Meldungen) für Zustände und Notifications für Ereignisse. Finde es auf jeden Fall nützlich, dass die „apps“ ein Verfallsdatum bekommen, damit sie bei verlorener Verbindung nicht ewig angezeigt werden, bspw mitten in der Nacht die Außentemperatur vom Nachmittag.

Für diejenigen die die Uhr bestellen möchten. Mit dem Code „SHARE02“ gibt es 10% Rabatt im Shop :slight_smile:

Danke,
hatte schon überlegt, mir noch eine zweite zu bestellen.

VG,
Doc

Es gibt offenbar ein Problem in der FW 0.69. Das Teil rebootet ungefragt und geht teilweise in den AP Modus. Soweit ich das aus den Meldungen herauslesen konnte, wenn man Farben für Indikatoren und Textfragmente verwendet.
Ich hab einen Fix dafür gefunden.
firmware.zip (811,2 KB)
Muss über die Weboberfläche der Uhr eingespielt werden. Das dürfte das Problem lösen, zumindest bei mir.

1 „Gefällt mir“

Hallo, meine Uhr ist auch angekommen, leider bekomme ich keine MQTT Daten an die Uhr gesendet.
Die Verbindung per MQTT steht, ich Empfange über das Topic „awtrix/stats“ die aktuellen Werte.

Folgendes habe ich gemacht:

MQTT Server Device mit dem Topic „awtrix_EG/notify“ erstellt.
image

Unterhalb des MQTT Server Device eine String Variable erstellt.

Folgendes Testscript erstellt:

<?php


$Meldung='{"text": "IP-Symcon","rainbow": true,"duration": 10}';

RequestAction(12162,$Meldung);

Führe ich das Script aus, bekomme ich folgendes Fehlermeldung:

Warning: No valid action available in /mnt/data/symcon/scripts/59266.ips.php on line 6

Was mache ich falsch?

Bei deiner Variable „Value“ fehlt das Aktionsskript. Vergleiche die Farbe bei „CurrentApp“. Außerdem gehört bei requestaction die ID der Variablen rein, nicht der Instanz. Versuche noch mal ein MQTT-Servier-Device zu erstellen. Die Variable Value muss schaltbar sein, braucht also das Standard-Aktionsskript. Oder hast du es eventuell in den Eigenschaften der Variable deaktiviert?

Zum verzweifeln. Neues Server Device angelegt.
image

String Variable manuell, unterhalb des Server Device angelegt, inkl. Aktionsscript.

Wenn ich jetzt folgendes Script ausführe, dann schreibt er mir auch die Meldung in die String Variable, allerdings sendet er mir nichts an die Uhr und die Uhr zeigt auch nichts an.
Fehlermeldung erscheint auch nicht beim Ausführen des Scripts.

<?php


$Meldung='{"text": "IP-Symcon","rainbow": true,"duration": 10}';

RequestAction(14980,$Meldung);

Mit diesem Skript wird das nicht funktionieren. Das müsste von IPS automatisch bei der Variable "Value"hinterlegt werden.

Das muss so aussehen

Hast du beim Gateway das richtige gewählt?

1 „Gefällt mir“

Das bedeutet, wenn ich manuell ein MQTT Server Device anlege, müßte die Variable mit angelegt werden? Mmmm, das passiert bei mir nicht. :roll_eyes:

Genau das meinte ich. Die Variable und die Zuordnung des Aktionsskripts passiert automatisch.

Ahhhhhhhhhh, jetzt! Es muss Typ „String“ sein und nicht „JSON“. Dann wird die Variable auch automatisch angelegt.

Kannst du das Script für diese Anzeige einmal Online stellen :smiley:

Sicher
Das ist das Skript für Pool- und Wetterdaten

<?php
$zaehler=getvalue(35750);
RequestAction(53097,'{"color": [0,0,0]}');
switch ($zaehler)
{
    case 0: 
			switch (getvalue(25070))
			{
				case 0: RequestAction(53097,'');break;
				case 1: RequestAction(53097,'{"color": [255,0,0]}');break;
				case -1: RequestAction(53097,'{"color": [0,0,255]}');break;

			}
            setvalue(35750,($zaehler+1));            
			RequestAction(33923,'{
            "text": "'.number_format(getvalue(36180),1,".",".").'°", 
            "hold": false,
            "icon": "47989",
			"stack":false
            }');
            break;     
    case 1:
			switch (getvalue(34162))
			{
				case 0: RequestAction(53097,'');break;
				case 1: RequestAction(53097,'{"color": [255,0,0]}');break;
				case -1: RequestAction(53097,'{"color": [0,0,255]}');break;

			}
            RequestAction(33923,'{
            "text":"'.number_format(getvalue(29469),1,".",".").'",
            "hold": false,
            "icon": "10767",
			"stack":false
            }');
            setvalue(35750,($zaehler+1));
            break;
    case 2:
            RequestAction(33923,'{
            "text":"'.getvalue(16543).'",
            "hold": false,
            "icon": "10769",
			"stack":false
            }');
            setvalue(35750,($zaehler+1));
            break;
    case 3:
				switch (getvalue(42587))
			{
				case 0: RequestAction(53097,'');break;
				case 1: RequestAction(53097,'{"color": [255,0,0]}');break;
				case -1: RequestAction(53097,'{"color": [0,0,255]}');break;

			}
            setvalue(35750,($zaehler+1));
            RequestAction(33923,'{
            "text": "'.number_format(getvalue(47158),1,".",".").'°",
            "hold": false,
            "icon": "38231",
			"stack":false
            }');
            setvalue(35750,($zaehler+1));
            break;            
    case 4:
            RequestAction(33923,'{
            "text": "'.number_format(getvalue(48644),1,".",".").'",
            "hold": false,
            "icon": "3363",
			"stack":false
            }');
            setvalue(35750,0);
            break;                   
}  

Das für die Uhr im Wohnzimmer für PV und Temperaturen

<?php
$zaehler=getvalue(14679);

function rgb2hex($r, $g=-1, $b=-1) 
{
	if (is_array($r) && sizeof($r) == 3) list($r, $g, $b) = $r;
 	$r = intval($r);
 	$g = intval($g);
 	$b = intval($b);
 	$r = dechex($r<0?0:($r>255?255:$r));
 	$g = dechex($g<0?0:($g>255?255:$g));
 	$b = dechex($b<0?0:($b>255?255:$b));
 	$color = (strlen($r) < 2?'0':'').$r;
 	$color .= (strlen($g) < 2?'0':'').$g;
 	$color .= (strlen($b) < 2?'0':'').$b;
 	return $color;
}

$prozent=getvalue(20790);
$rot=255*(1-$prozent/100);
$gruen=255*$prozent/100;
$blau=0;
$hex=rgb2hex($rot,$gruen,$blau);
RequestAction(15241,'{"color": [0,0,0]}');
switch ($zaehler)
{
    case 0:
            RequestAction(27530,'{
            "text": "'.getvalue(20790).'%",
            "hold": false,
            "background": "#'.$hex.'",
            "color": "#000000",
            "icon": "15124",
			"stack":false
            }');
            setvalue(14679,($zaehler+1));
            break;
    case 1:
            RequestAction(27530,'{
            "text": "'.getvalue(19434).'W",
            "hold": false,
            "background": "#000000",
            "color": "#FFFFFF",
            "icon": "1338",
			"stack":false
            }');
            setvalue(14679,($zaehler+1));
            break;
    case 2:
			if (getvalue(30776)<=0) $icon="53194"; else $icon="53177";
            RequestAction(27530,'{
            "text": "'.getvalue(30776).'W",
            "hold": false,
            "background": "#000000",
            "color": "#FFFFFF",
            "icon": "'.$icon.'",
			"stack":false
            }');		
            setvalue(14679,($zaehler+1));
            break;
	case 3:
            RequestAction(27530,'{
            "text": "'.number_format(getvalue(13902),1,".",".").'°",
            "hold": false,
            "color": "#FFFFFF",
            "background": "#000000",
            "icon": "38230",
			"stack":false
            }');
			switch (getvalue(25097))
			{
				case 0: RequestAction(15241,'');break;
				case 1: RequestAction(15241,'{"color": [255,0,0]}');break;
				case -1: RequestAction(15241,'{"color": [0,0,255]}');break;

			}
            setvalue(14679,($zaehler+1));
			break;
	case 4:
            RequestAction(27530,'{
            "text": "'.number_format(getvalue(47158),1,".",".").'°",
            "hold": false,
            "color": "#FFFFFF",
            "background": "#000000",
            "icon": "38231",
			"stack":false
            }');
			switch (getvalue(42587))
			{
				case 0: RequestAction(15241,'');break;
				case 1: RequestAction(15241,'{"color": [255,0,0]}');break;
				case -1: RequestAction(15241,'{"color": [0,0,255]}');break;

			}				
            setvalue(14679,($zaehler+1));
			break;
	case 5:
			switch (date("N",time()))
			{
				case 1: $wochentag="Montag";break;
				case 2: $wochentag="Dienstag";break;
				case 3: $wochentag="Mittwoch";break;
				case 4: $wochentag="Donnerstag";break;
				case 5: $wochentag="Freitag";break;
				case 6: $wochentag="Samstag";break;
				case 7: $wochentag="Sonntag";break;
			}
			switch (date("n",time()))
			{
				case 1:$monat="Jänner";break;
				case 2:$monat="Februar";break;
				case 3:$monat="März";break;
				case 4:$monat="April";break;
				case 5:$monat="Mai";break;
				case 6:$monat="Juni";break;
				case 7:$monat="Juli";break;
				case 8:$monat="Augugust";break;
				case 9:$monat="September";break;
				case 10:$monat="Oktober";break;
				case 11:$monat="November";break;
				case 12:$monat="Dezember";break;
			}
            RequestAction(27530,'{
            "text": "'.$wochentag.','.date('j.',time()).' '.$monat.' '.date('y',time()).'-'.date('G:i',time()).'",
            "hold": false,
            "color": "#FFFFFF",
            "background": "#000000",
			"rainbow": false,
			"stack":false
            }');
			if (getvalue(19434)>0) setvalue(14679,0); else setvalue(14679,2);
			break;
} 

Für beide Skripte habe ich noch jeweils eine Zählervariable definiert. Die wird um eins für die nächste „Seite“ erhöht oder am Ende auf 0 gesetzt.

image

2 „Gefällt mir“

für den ioBroker hat jemand ein Modul geschrieben.

Das zeigt ganz gut, was mit dem Ding möglich ist.
Ich habe mir auch mal eine bestellt. Das ist so schön retro.

Gruß
Christian

Da ich bis vor kurzem ziemlich viele Verbindungsprobleme hatte, dies aber seit einigen Veränderungen an der Einstellung meines Unifi-Systems deutlich besser geworden ist, sei auf dies verwiesen.

Ich hatte bisher noch nichts an Code geteilt, weil ich leider meine Integration von Anfang an sehr tief mit den Eigenheiten meines Systems verwoben hatte. Aber hier einmal etwas Code zum Anzeigen von momentanen Meldungen oder wiederkehrenden („Apps“).

Das ganze ist so gedacht, dass es als Include-Datei innerhalb anderer Skripte verwendet werden kann. Zum Einrichten ein Skript erstellen, Code hinein kopieren und einmal ausführen. Skript ist dann fortan als AWTL.ips.php inkludierbar.

// Enter all sounds you defined in the MELODIES folder
$awtl_sounds = ["blip", "notification", (...)];

// Give string names to numeric icon indexes. It's possible
// to put indexes in quotes so the IPS console doesn't confuse
// them with object IDs.
$awtl_icons = array(
    "door" => "20969",
    "key" => 106,
    (...)
);

// Set the ID of the MQTT Server isntance
$awtl_server_id = (...);

// can be handy to define arrays for when you want to
// address several clocks at the same time
define('ALL_CLOCKS', ["uhr1", "uhr2", (...)]);
define('KITCHEN_CLOCK', ["uhr1", "uhr4"]);

//////////////////////////////////////////////////////////////////////////////////////
// No changes required beyond this point

// Setup routine. Execute this script in the console.
$script_dir = IPS_GetKernelDir() . '/scripts/';
$include_file_script_id = @IPS_GetScriptIDByFile(substr(__FILE__, strlen($script_dir) - 1));
if($include_file_script_id === false || $include_file_script_id == 0) {
    die("Script ID of include file could not be determined!");
}
if($include_file_script_id == $_IPS['SELF']) {
    /* Upon launching this script, ensure it's got a memorable name for
    easy including
    */
    $script_pretty_name = "AWTL.ips.php";
    $script_file_name = IPS_GetScript($_IPS['SELF'])['ScriptFile'];
    if($script_file_name != $script_pretty_name) {
        echo "File name changed to " . $script_pretty_name . " for easy including.\n";
        rename(
            $script_dir . $script_file_name,
            $script_dir . $script_pretty_name
        );
        IPS_SetScriptFile($_IPS['SELF'], $script_pretty_name);
    } else {
        echo "File name is " . $script_pretty_name . " for easy including.\n";
    }
}

// Power to the matrix (or not)
function AWTL_SetPower($clock_prefix, $status) {
    global $awtl_server_id;

    // allow specifying an array of prefixes in order to address several clocks at the same time
    if(is_array($clock_prefix)) {
        $result = true;
        foreach($clock_prefix as $this_clock_prefix) {
            if(!AWTL_SetPower(
                $this_clock_prefix, $status
            )){
                $result = false;
            }
        }
        return $result; // return true if all were successful
    }

    if(!is_bool($status)) return false;

    // prepare topic name
    $topic = $clock_prefix . "/power";

    $payload = array(
        "power" => $status
    );

    return MQTT_Publish(
        $awtl_server_id,
        $topic,
        $payload
    );
} // AWTL_SetPower

function AWTL_Reboot($clock_prefix) {
    global $awtl_server_id;

    // allow specifying an array of prefixes in order to address several clocks at the same time
    if(is_array($clock_prefix)) {
        $result = true;
        foreach($clock_prefix as $this_clock_prefix) {
            if(!AWTL_Reboot(
                $this_clock_prefix
            )){
                $result = false;
            }
        }
        return $result; // return true if all were successful
    }

    // prepare topic name
    $topic = $clock_prefix . "/reboot";

    $payload = "";

    return MQTT_Publish(
        $awtl_server_id,
        $topic,
        $payload
    );
} // AWTL_Reboot

function AWTL_SetIndicator($clock_prefix, $color, $bottom = false, $blink = false) {
    global $awtl_server_id;

    // allow specifying an array of prefixes in order to address several clocks at the same time
    if(is_array($clock_prefix)) {
        $result = true;
        foreach($clock_prefix as $this_clock_prefix) {
            if(!AWTL_SetIndicator(
                $this_clock_prefix, $color, $bottom, $blink
            )){
                $result = false;
            }
        }
        return $result; // return true if all were successful
    }

    if(!is_bool($bottom)) return false;
    if(!is_bool($blink)) return false;
    if(!is_string($color)) return false;

    // prepare topic name
    if($bottom) {
        $topic = $clock_prefix . "/indicator2";
    } else {
        $topic = $clock_prefix . "/indicator1";
    }

    $payload = array(
        "color" => $color,
        "blink" => $blink
    );

    return MQTT_Publish(
        $awtl_server_id,
        $topic,
        $payload
    );
} // AWTL_SetIndicator

/* Display a message or create/update an "app" on the Awtrix Lite clock.
    $clock_prefix - The first part of the topic name specific to the clock you want
        to address. Can also be an array of strings if you want to address several
        clocks at the same time.
    $text - The text message to display or an empty string to close an app / dis-
        appear a "hold" message
    $color - A hex string specifying the color for the text message
    $duration - Number of seconds for message to display. Can be false to automa-
        tically set duration or negative value which stands for "times to scroll
        through message"
    $icon - Numeric icon index or string referring to a key entry in the
        $awtl_icons associative array, or false for no icon
    $app_name_or_sound - String either specifying a "melody" entry specified in
        the $awtl_sounds array, or a name for an "app" to create/update/vanish
        or false to display notification without sound
    $options - optional additional settings as an associative array
*/
function AWTL_Message($clock_prefix, $text, $color = "FFFFFF", $duration = false, $icon = false, $app_name_or_sound = false, $options = false) {
    global $awtl_sounds, $awtl_icons, $awtl_server_id;

    // allow specifying an array of prefixes in order to address several clocks at the same time
    if(is_array($clock_prefix)) {
        $result = true;
        foreach($clock_prefix as $this_clock_prefix) {
            if(!AWTL_Message(
                $this_clock_prefix,
                $text, $color, $duration, $icon, $app_name_or_sound, $options
            )){
                $result = false;
            }
        }
        return $result; // return true if all were successful
    }

    // prepare topic name
    $topic = $clock_prefix . "/";

    // if additional options array specified, use it as basis for payload array
    if(is_array($options)) {
        $payload = $options;
    } else if($options === false) { // otherwise create emtpy array
        $payload = array();
    } else {
        AWTL_Error('Wrong data type for parameter $options');
        return false;
    }
    
    $payload["text"] = $text; // text can be string or compley array for multicolored text

    // allow specifying no color (false) if not needed
    if(is_string($color)) {
        $payload["color"] = $color;
    } else if($color !== false) {
        AWTL_Error('Wrong data type for parameter $color');
        return false;
    }
    
    if(in_array($app_name_or_sound, $awtl_sounds)) { // parameter specifies a "melody"
        $payload["sound"] = $app_name_or_sound;
        $topic .= "notify";
    } else if(is_string($app_name_or_sound) && strlen($app_name_or_sound) > 0) { // parameter specifies custom "app"
        $topic .= "custom/" . $app_name_or_sound;
    } else if($app_name_or_sound === false) { // just a notification with no sound
        $topic .= "notify";
    } else {
        AWTL_Error('Wrong data type for parameter $app_name_or_sound');
        return false;
    }

    if(is_int($icon)) { // numeric index
        $payload["icon"] = $icon;
    } else if(is_string($icon) && array_key_exists($icon, $awtl_icons)) { // string key
        $payload["icon"] = intval($awtl_icons[$icon]);
    } else if($icon !== false) {
        AWTL_Error('Wrong data type for parameter $icon or icon not found');
        return false;
    }

    // automatically set duration if false is specified
    if($duration === false && is_string($text)) {
        $duration = strlen($text);
        if($duration < 3) $duration = 3; 
        if($duration > 10) $duration = 10; 
    } else if(!is_int($duration)) {
        AWTL_Error('Wrong data type for parameter $duration');
        return false;
    }

    if($duration < 0) { // negative value for duration means number of repeats
        $payload["repeat"] = -$duration;
    } else {
        $payload["duration"] = $duration;
    }

    // no text means we want to disappear something
    if($text == "") {
        if($app_name_or_sound === false) $topic .= "/dismiss";
        $payload = "";
    }

    return MQTT_Publish(
        $awtl_server_id,
        $topic,
        $payload
    );
}

// change a set of settings
function AWTL_Settings($clock_prefix, $payload) {
    global $awtl_server_id;

    // allow specifying an array of prefixes in order to address several clocks at the same time
    if(is_array($clock_prefix)) {
        $result = true;
        foreach($clock_prefix as $this_clock_prefix) {
            if(!AWTL_Settings(
                $this_clock_prefix, $payload
            )){
                $result = false;
            }
        }
        return $result; // return true if all were successful
    }

    if(!is_array($payload)) return false;

    // prepare topic name
    $topic = $clock_prefix . "/settings";

    return MQTT_Publish(
        $awtl_server_id,
        $topic,
        $payload
    );
} // AWTL_Settings

/* Switch to another app
    $app_name - Name of a builtin or user defined app, or "previous" or "next"
*/
function AWTL_SwitchApp($clock_prefix, $app_name) {
    global $awtl_server_id;

    // allow specifying an array of prefixes in order to address several clocks at the same time
    if(is_array($clock_prefix)) {
        $result = true;
        foreach($clock_prefix as $this_clock_prefix) {
            if(!AWTL_SwitchApp(
                $this_clock_prefix, $app_name
            )){
                $result = false;
            }
        }
        return $result; // return true if all were successful
    }

    if(!is_string($app_name)) return false;

    // prepare topic name
    $topic = $clock_prefix . "/";
    
    if($app_name == "next") {
        $topic .= "nextapp";
        $payload = "";
    } else if($app_name == "previous") {
        $topic .= "previousapp";
        $payload = "";
    } else {
        $topic .= "switch";
        $payload = array(
            "name" => $app_name
        );
    }

    return MQTT_Publish(
        $awtl_server_id,
        $topic,
        $payload
    );
} // AWTL_SwitchApp

/* Publish a given payload over a MQTT server under
   a given topic without having to manually create a
   MQTT server device */
function MQTT_Publish($server_id, $topic, $payload, $retain = false) {
    global $include_file_script_id;

    // ensure server instance exists
    if(!IPS_InstanceExists($server_id)) {
        AWTL_Error('Server instance not found');
        return false;
    }

    // convert array structure to json string
    if(is_array($payload)) $payload = json_encode($payload, JSON_UNESCAPED_UNICODE);

    // determine data type
    if(is_string($payload)) {
        $ips_var_type = 3;
    } else if(is_float($payload)) {
        $ips_var_type = 2;
    } else if(is_int($payload)) {
        $ips_var_type = 1;
    } else if(is_bool($payload)) {
        $ips_var_type = 0;
    } else { // unsupported
        AWTL_Error('Unsupported data type for parameter $payload');
        return false;
    }
    //echo $payload;
    $module_id = "{01C00ADD-D04E-452E-B66A-D253278743FE}" /* Module ID of MQTT Server Device */;
    $ident = "TempMQTTDevice";

    // enter semaphore to ensure the temporary device gets used by one thread at a time
    if(IPS_SemaphoreEnter($ident, 100)) {
        // get temporary MQTT Server Device or create if needed
        $id = @IPS_GetObjectIDByIdent($ident, $include_file_script_id);
        if($id === false) {
            $id = @IPS_CreateInstance($module_id);
            if($id === false) {
                AWTL_Error('Failed to create temporary MQTT Server Device');
                return false;
            }
            IPS_SetParent($id, $include_file_script_id);
            IPS_SetIdent($id, $ident);
        }

        // ensure the specified server instance is actually compatible
        if(!IPS_IsInstanceCompatible($id, $server_id)) {
            AWTL_Error('Server instance is not compatible');
            return false;
        }

        // ensure that the temporary device is actually connected to the correct server instance
        $inst_config = IPS_GetInstance($id);
        if($inst_config["ConnectionID"] != $server_id) {
            IPS_DisconnectInstance($id);
            if(!@IPS_ConnectInstance($id, $server_id)) {
                AWTL_Error('Failed to connect temporary MQTT Server Device');
                return false;
            }
        }

        // name object to help with debugging
        IPS_SetName($id, "Temporary MQTT Server Device for topic " . $topic);

        // configure temporary device
        $config_arr = array(
            "Retain" => $retain,
            "Topic" => $topic,
            "Type" => $ips_var_type
        );
        $config_str = json_encode($config_arr);
        IPS_SetConfiguration($id, $config_str);
        IPS_ApplyChanges($id);

        // get Value variable and use it to publish the payload
        $var_id = @IPS_GetObjectIDByIdent("Value", $id);
        RequestAction($var_id, $payload);

        IPS_SemaphoreLeave($ident);
    } else { // semaphore timeout
        AWTL_Error('Semaphore timeout');
        return false;
    }

    return true;
} // MQTT_Publish

function AWTL_Error($msg) {
    if($_IPS['SENDER'] == 'Execute') {
        echo "Fehler: " . $msg . "\n";
    }
}
1 „Gefällt mir“

Danke, ich schaue mir das an, sobald meine angekommen ist.

so, meine Uhr ist mittlerweile da.
Macht einen guten Eindruck, und funktioniert soweit.
Auf mich wirkt die Darstellung etwas unscharf. Das wird besser, je weiter die Uhr weg steht.
Ich habe natürlich gleich die alternative Firmware installiert.

@sokkederheld
Danke für Dein Script, funktioniert bei mir soweit gut.
Was ich nicht hinbekomme ist für die Darstellungsfunktion das"Options" Array.
Ich würde zB mal die Regenbogendarstellung testen, bekomme das aber nicht hin.

Kannst Du mir da mal ein Beispiel für die Syntax geben?

Gruß
Christian

["rainbow" => true]

sollte als 7. Parameter (nach Sound/Appname) funktionieren, also bspw.:

AWTL_Message("uhr1", "Happy Birthday", 0, -2, "balloons", "holiday_motd", ["rainbow" => true]);

Seitdem ich das nächtliche Neu-ermitteln der WLAN-Frequenz bei meinem Unifi-Setup ausgeschaltet habe, ist das ganze deutlich stabiler geworden. Etwas doof, weil ich das durchaus gerne an gelassen hätte, aber das scheint ein Problem mit den ESP32 zu sein und nicht speziell mit der Firmware.