<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>http://wiki.fhem.de/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Schwatter</id>
	<title>FHEMWiki - Benutzerbeiträge [de]</title>
	<link rel="self" type="application/atom+xml" href="http://wiki.fhem.de/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Schwatter"/>
	<link rel="alternate" type="text/html" href="http://wiki.fhem.de/wiki/Spezial:Beitr%C3%A4ge/Schwatter"/>
	<updated>2026-04-11T06:51:22Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.43.6</generator>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40949</id>
		<title>FHEMWEB/VoiceControl: Web-STT &amp; Hardware-Wakeword</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40949"/>
		<updated>2026-04-07T19:53:42Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Voicecontrol.png|mini]]&lt;br /&gt;
&lt;br /&gt;
== VoiceControl – Sprachsteuerung via Browser und Atom Echo s3r ==&lt;br /&gt;
&lt;br /&gt;
Diese Lösung ermöglicht eine flexible Sprachsteuerung für [[FHEM]]. Sprache wird in Text (Speech-to-Text) umgewandelt und als Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; bereitgestellt. Dieses Reading kann anschließend zentral (z. B. über &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DOIF&amp;lt;/code&amp;gt;) ausgewertet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Unterstützt werden nur Chrome-basierte Browser (Chrome, Edge, Fully Browser). Firefox und Chromium haben&lt;br /&gt;
leider kein Backend.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei unterschiedliche Wege zur Spracherfassung:&lt;br /&gt;
&lt;br /&gt;
* Weg 1️⃣ (Software): Browser-basierte Komplettlösung&lt;br /&gt;
* Weg 2️⃣ (Hybrid): Hardware-Wakeword + Browser-Spracherkennung&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Grundprinzip ====&lt;br /&gt;
* Sprache → Speech-to-Text&lt;br /&gt;
* Ergebnis → Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zentrale Logik verarbeitet Befehle&lt;br /&gt;
&lt;br /&gt;
==== Betriebsarten ====&lt;br /&gt;
* Push-to-Talk (nur Browser)&lt;br /&gt;
* Always-On mit Wakeword&lt;br /&gt;
* Hardware-Wakeword (Hybrid)&lt;br /&gt;
&lt;br /&gt;
==== Rückmeldungen ====&lt;br /&gt;
* Sprachausgabe (TTS)&lt;br /&gt;
* Visuelle Bubble im Browser&lt;br /&gt;
* Optional gezielte Rückmeldung per Client-ID&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 1️⃣: Browser-Lösung (voicecontrol.js) ==&lt;br /&gt;
&lt;br /&gt;
Das Script nutzt nur die Google Web Speech API.&lt;br /&gt;
&lt;br /&gt;
=== Bedienung ===&lt;br /&gt;
&lt;br /&gt;
==== Push-to-Talk ====&lt;br /&gt;
* Button gedrückt halten&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung ca.6sek Zeit für Befehl&lt;br /&gt;
&lt;br /&gt;
==== Always-On ====&lt;br /&gt;
* Kurzer Klick aktiviert Dauerbetrieb&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung ca.6sek Zeit für Befehl&lt;br /&gt;
* JS wird neu gestartet udn Schleife fängt von vorne an&lt;br /&gt;
&lt;br /&gt;
==== Wakeword ====&lt;br /&gt;
* Standard: &amp;lt;code&amp;gt;james&amp;lt;/code&amp;gt;&lt;br /&gt;
* Anpassbar im Script:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const wakewords = [&amp;quot;james&amp;quot;];&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{ Svn_GetFile(&#039;contrib/voicecontrol.js&#039;, &#039;www/pgm2/voicecontrol.js&#039;) }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBphone JavaScripts www/pgm2/voicecontrol.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Hinweis (HTTP ohne HTTPS) ====&lt;br /&gt;
Chrome benötigt Freigabe für Mikrofon:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;chrome://flags/#unsafely-treat-insecure-origin-as-secure&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eigene URL hinzufügen&lt;br /&gt;
* Auf &amp;lt;code&amp;gt;Enabled&amp;lt;/code&amp;gt; setzen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 2️⃣: Hybrid-Lösung mit Fully Kiosk Browser + Atom Echo (voicecontrol_echo.js) ==&lt;br /&gt;
&lt;br /&gt;
Diese Variante kombiniert Hardware und Software. Daher ist sie besonders geeignet für&lt;br /&gt;
ein Tablet mit Fully Kiosk Browser oder ähnlich, welches das Webinterface dauerhaft anzeigt.&lt;br /&gt;
&lt;br /&gt;
* Wakeword-Erkennung erfolgt auf dem ESP (Atom Echo s3r)&lt;br /&gt;
* Die eigentliche Sprachverarbeitung (Speech-to-Text) erfolgt im Browser&lt;br /&gt;
&lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
&lt;br /&gt;
# Wakeword wird auf dem ESP erkannt&lt;br /&gt;
# FHEM erzeugt Event (z. B. &amp;lt;code&amp;gt;james_detected&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Browser empfängt Event via WebSocket&lt;br /&gt;
# Speech-to-Text startet im Browser&lt;br /&gt;
# Ergebnis wird an FHEM übertragen&lt;br /&gt;
&lt;br /&gt;
=== Aktivierung ===&lt;br /&gt;
&lt;br /&gt;
* Kurzer Klick auf Button aktiviert/deaktiviert System&lt;br /&gt;
* Baut WebSocket-Verbindung zu FHEM auf&lt;br /&gt;
* Kein Push-to-Talk-Modus&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&lt;br /&gt;
Die Datei voicecontrol_echo.js ist hier zu finden:&lt;br /&gt;
{{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
und muss nach www/pgm2/voicecontrol_echo.js kopiert werden.&lt;br /&gt;
&lt;br /&gt;
Die echo_s3r.yaml ist für den Atom Echo s3r. Die Konfuguration ist weiter unten beschrieben.&lt;br /&gt;
&lt;br /&gt;
==== Einbindung in Fhem ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBtablet JavaScripts www/pgm2/voicecontrol_echo.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Das Skript voicecontrol_echo.js muss in dem FHEMWEB-Device angelegt werden, das für den Fully Kiosk Browser zuständig ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBtablet additionalInform atom_echos3r_9888e00f4280,global&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* atom_echos3r_9********** = da kommt das Wakeword an, bzw. der Devicename vom Echo&lt;br /&gt;
* global = das ist zum auswerten der Sprachausgabe wichtig&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration im Javascript ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const DEVICE  = &amp;quot;atom_echos3r_9888e00f4280&amp;quot;;&lt;br /&gt;
const TRIGGER = &amp;quot;james_detected&amp;quot;;&lt;br /&gt;
const FHEM_IP = &amp;quot;192.168.1.76:8085&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; → FHEM-Device des ESP&lt;br /&gt;
* &amp;lt;code&amp;gt;TRIGGER&amp;lt;/code&amp;gt; → Event bei Wakeword&lt;br /&gt;
* &amp;lt;code&amp;gt;FHEM_IP&amp;lt;/code&amp;gt; → FHEM-Server&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration in der Yaml ===&lt;br /&gt;
&lt;br /&gt;
Damit sich der ESP mit eurem MQTT-Server und WLAN verbindet, müssen folgende Stellen angepasst werden.&lt;br /&gt;
&lt;br /&gt;
   wifi:&lt;br /&gt;
     ssid: &amp;quot;YOUR_SSID&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     fast_connect: true&lt;br /&gt;
&lt;br /&gt;
   mqtt:&lt;br /&gt;
     broker: 192.168.1.76&lt;br /&gt;
     port: 1884&lt;br /&gt;
     username: &amp;quot;YOUR_USERNAME&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     topic_prefix: atom_echo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wakeword (ESP / ESPHome) ===&lt;br /&gt;
&lt;br /&gt;
Das Wakeword wird direkt auf dem ESP definiert:&lt;br /&gt;
&lt;br /&gt;
* Umsetzung über ESPHome&lt;br /&gt;
* Eine große Auswahl an Wakewords sind hier zu finden:&lt;br /&gt;
   https://github.com/TaterTotterson/microWakeWords&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel, wie das Wakeword definiert wird.&lt;br /&gt;
&lt;br /&gt;
   micro_wake_word:&lt;br /&gt;
     id: mww&lt;br /&gt;
     microphone: echo_mic&lt;br /&gt;
     models:&lt;br /&gt;
       - model: &amp;quot;https://github.com/TaterTotterson/microWakeWords/raw/main/microWakeWordsV2/james.json&amp;quot;&lt;br /&gt;
         id: james_model&lt;br /&gt;
         probability_cutoff: 0.45   &lt;br /&gt;
&lt;br /&gt;
=== Kommunikation ===&lt;br /&gt;
&lt;br /&gt;
* WebSocket-Verbindung zu FHEM&lt;br /&gt;
* Lauscht auf Device-Events&lt;br /&gt;
* Automatischer Reconnect bei Abbruch&lt;br /&gt;
&lt;br /&gt;
=== Ablauf nach Wakeword ===&lt;br /&gt;
&lt;br /&gt;
# System sagt „Ja?“&lt;br /&gt;
# Browser startet SpeechRecognition&lt;br /&gt;
# Nutzer spricht Befehl&lt;br /&gt;
# Befehl wird verarbeitet&lt;br /&gt;
# Rückmeldung „Erledigt“&lt;br /&gt;
&lt;br /&gt;
== Zentrale Auswertung (Logik) ==&lt;br /&gt;
&lt;br /&gt;
Beide Wege schreiben in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;global:STT&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung erfolgt zentral über ein &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt;. Ein Device wird in der Mappingtabelle eingetragen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;quot;esszimmer:licht|lampe|deckenlampe&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;, label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aufschlüsselung:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;quot;hauptkeyword:Filter1|Filter3|Filter3&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Devicename&amp;quot;, label =&amp;gt; &amp;quot;Übersichtname&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: notify ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod n_VoiceControl notify global:STT:.* {\&lt;br /&gt;
    # --- VORBEREITUNG ---\&lt;br /&gt;
    my ($cleanEvent, $clientId) = $EVENT =~ /^(.*)\s\[(.*)\]$/;;\&lt;br /&gt;
    $cleanEvent //= $EVENT;;\&lt;br /&gt;
    $clientId   //= &amp;quot;unknown&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # --- KONFIGURATION: Zentrales Mapping ---\&lt;br /&gt;
    my %smartHomeDevices = (\&lt;br /&gt;
        &amp;quot;esszimmer:licht|lampe|deckenlampe&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;, label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;esszimmer:aquarium&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Aquarium_Aktor&amp;quot;, label =&amp;gt; &amp;quot;Aquarium&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;küche&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Deckenlampe_Kue&amp;quot;, label =&amp;gt; &amp;quot;Licht Küche&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe06_Dek&amp;quot;, label =&amp;gt; &amp;quot;Licht Wohnzimmer&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;fernseher|tv&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;VuPlus&amp;quot;, label =&amp;gt; &amp;quot;Fernseher&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;rechner|pc&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;PC_Aktor&amp;quot;, label =&amp;gt; &amp;quot;PC&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;garage|tor&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Garagentor_Aktor&amp;quot;, label =&amp;gt; &amp;quot;Garagentor öffnen/schließen&amp;quot;, cmdOn =&amp;gt; &amp;quot;open&amp;quot;, cmdOff =&amp;gt; &amp;quot;close&amp;quot; },\&lt;br /&gt;
        &amp;quot;kaffee&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Kaffeemaschine&amp;quot;, label =&amp;gt; &amp;quot;Kaffeemaschine&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;roberto&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;MQTT2_valetudo_FlusteredUnequaledFish&amp;quot;, label =&amp;gt; &amp;quot;Lade Roberto&amp;quot;, cmdOn =&amp;gt; &amp;quot;charge&amp;quot; },\&lt;br /&gt;
        &amp;quot;ambiente&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;LampeSzeneAlle&amp;quot;, label =&amp;gt; &amp;quot;Zentrales Licht&amp;quot;, type =&amp;gt; &amp;quot;dimmer&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;sauge|reinige|putze|staubsauger|roboter&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;MQTT2_valetudo_FlusteredUnequaledFish&amp;quot;, label =&amp;gt; &amp;quot;Staubsauger&amp;quot;, type =&amp;gt; &amp;quot;vacuum&amp;quot; },\&lt;br /&gt;
         &amp;quot;ambilight&amp;quot; =&amp;gt; { label =&amp;gt; &amp;quot;Ambilight&amp;quot;, type =&amp;gt; &amp;quot;system&amp;quot;, cmd =&amp;gt; &amp;quot;sshpass -p &#039;GEHEIMESPASSWORD&#039; ssh -o StrictHostKeyChecking=no root\@192.168.1.46 &#039;/usr/share/hyperhdr/scripts/hyperhdr_toggle.sh&#039;&amp;quot; }\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    my %vacRooms = (&amp;quot;arbeitszimmer&amp;quot; =&amp;gt; &amp;quot;Arbeitszimmer&amp;quot;, &amp;quot;badezimmer&amp;quot; =&amp;gt; &amp;quot;Badezimmer&amp;quot;, &amp;quot;esszimmer&amp;quot; =&amp;gt; &amp;quot;Esszimmer&amp;quot;, &amp;quot;flur&amp;quot; =&amp;gt; &amp;quot;Flur&amp;quot;, &amp;quot;küche&amp;quot; =&amp;gt; &amp;quot;Küche&amp;quot;, &amp;quot;wohnzimmer&amp;quot; =&amp;gt; &amp;quot;Wohnzimmer&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $onRegEx  = &#039;\b(an|ein|einschalten|starte|aktivier|aktiviere|öffne|öffnen|auf|hoch|lade)\b&#039;;;\&lt;br /&gt;
    my $offRegEx = &#039;\b(aus|ausschalten|stop|stoppe|beende|deaktivier|deaktiviere|schließe|schließen|zu|runter)\b&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
    my @commands = split(/\s*(?:und|dann|,)\s*/, lc($cleanEvent));;\&lt;br /&gt;
\&lt;br /&gt;
    # --- DER BEFEHLS-LOOP ---\&lt;br /&gt;
    foreach my $cmd_part (@commands) {\&lt;br /&gt;
        $cmd_part =~ s/^\s+|\s+$//g;;\&lt;br /&gt;
        my $is_on  = ($cmd_part =~ /$onRegEx/) ? 1 : 0;;\&lt;br /&gt;
        my $is_off = ($cmd_part =~ /$offRegEx/) ? 1 : 0;;\&lt;br /&gt;
        $cmd_part =~ s/\b(ich|brauche|mach|bitte|kannst du|würdest du|mal|doch|den|das|die|im|in der)\b//g;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- GENERISCHE STEUERUNG ---\&lt;br /&gt;
        my $match = 0;;\&lt;br /&gt;
        # Sortierung stellt sicher, dass &#039;dimmer/vacuum&#039; Vorrang haben\&lt;br /&gt;
        foreach my $key (sort { ($smartHomeDevices{$b}{type}//&amp;quot;&amp;quot;) cmp ($smartHomeDevices{$a}{type}//&amp;quot;&amp;quot;) } keys %smartHomeDevices) {\&lt;br /&gt;
            \&lt;br /&gt;
            my ($main, $must) = split(/:/, $key);;\&lt;br /&gt;
            \&lt;br /&gt;
            # Prüfe Hauptwort\&lt;br /&gt;
            if ($cmd_part =~ /\b($main)\b/i) {\&lt;br /&gt;
                # Prüfe Pflichtwort falls vorhanden\&lt;br /&gt;
                next if ($must &amp;amp;&amp;amp; $cmd_part !~ /\b($must)\b/i);;\&lt;br /&gt;
                \&lt;br /&gt;
                my $d = $smartHomeDevices{$key};;\&lt;br /&gt;
\&lt;br /&gt;
                # A) System\&lt;br /&gt;
                if (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;system&amp;quot;) {\&lt;br /&gt;
                    system($d-&amp;gt;{cmd});;\&lt;br /&gt;
                }\&lt;br /&gt;
                \&lt;br /&gt;
                # B) Staubsauger\&lt;br /&gt;
                if (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;vacuum&amp;quot;) {\&lt;br /&gt;
                    my @found = grep { $cmd_part =~ /\b$_\b/ } keys %vacRooms;;\&lt;br /&gt;
                    fhem(@found ? &amp;quot;set $d-&amp;gt;{dev} clean_segment &amp;quot;.join(&amp;quot;,&amp;quot;, map { $vacRooms{$_} } @found) : &amp;quot;set $d-&amp;gt;{dev} start&amp;quot;);;\&lt;br /&gt;
                }\&lt;br /&gt;
                # C) Dimmer (Kompakt &amp;amp; korrigiert)\&lt;br /&gt;
                elsif (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;dimmer&amp;quot;) {\&lt;br /&gt;
                    $cmd_part =~ /(\d+)/ \&lt;br /&gt;
                        ? fhem(&amp;quot;set $d-&amp;gt;{dev} brightness &amp;quot; . int(($1 &amp;gt; 100 ? 100 : $1) * 2.55)) \&lt;br /&gt;
                        : ($is_on || $is_off) &amp;amp;&amp;amp; fhem(&amp;quot;set $d-&amp;gt;{dev} &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;));;\&lt;br /&gt;
                }\&lt;br /&gt;
                # D) Standard An/Aus\&lt;br /&gt;
                else {\&lt;br /&gt;
                    my $fhem_cmd = $is_off ? ($d-&amp;gt;{cmdOff} // &amp;quot;off&amp;quot;) : ($d-&amp;gt;{cmdOn} // &amp;quot;on&amp;quot;);;\&lt;br /&gt;
                    fhem(&amp;quot;set $d-&amp;gt;{dev} $fhem_cmd&amp;quot;) if ($is_on || $is_off);;\&lt;br /&gt;
                }\&lt;br /&gt;
                \&lt;br /&gt;
                $match = 1;; last;;\&lt;br /&gt;
            }\&lt;br /&gt;
        }\&lt;br /&gt;
        next if $match;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- HILFE ---\&lt;br /&gt;
        if ($cmd_part =~ /(hilfe|kommandos|übersicht)/) {\&lt;br /&gt;
            my $h = &#039;&amp;lt;div style=&amp;quot;text-align:left;;min-width:250px;;font-family:sans-serif;;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Befehlsübersicht:&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            my %seen;;\&lt;br /&gt;
            for my $k (sort keys %smartHomeDevices) {\&lt;br /&gt;
                my $d = $smartHomeDevices{$k};;\&lt;br /&gt;
                next if $seen{$d-&amp;gt;{label}}++;; \&lt;br /&gt;
                my $suffix = ($d-&amp;gt;{cmdOn} &amp;amp;&amp;amp; $d-&amp;gt;{cmdOff} &amp;amp;&amp;amp; $d-&amp;gt;{label} !~ /(an\/aus|öffnen\/schließen)/i) ? &amp;quot; (an/aus)&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
                $suffix = &amp;quot; [0-100%]&amp;quot; if ($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;dimmer&amp;quot;;;\&lt;br /&gt;
                $h .= &amp;quot;• $d-&amp;gt;{label}$suffix&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            }\&lt;br /&gt;
            $h .= &#039;&amp;lt;br&amp;gt;&amp;lt;u&amp;gt;Staubsauger Räume&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;• &#039;.join(&amp;quot;, &amp;quot;, map { ucfirst($_) } sort keys %vacRooms).&#039;&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt;&#039;;;\&lt;br /&gt;
            $h =~ s/&#039;/\\&amp;quot;/g;;\&lt;br /&gt;
            my $js = &amp;quot;if((document.querySelector(&#039;input[name=\&amp;quot;fw_id\&amp;quot;]&#039;)||{}).value===&#039;$clientId&#039;){FW_okDialog(&#039;$h&#039;)}&amp;quot;;;\&lt;br /&gt;
            FW_directNotify(&amp;quot;#FHEMWEB:$_&amp;quot;, $js, &amp;quot;&amp;quot;) for devspec2array(&amp;quot;TYPE=FHEMWEB&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Unterschiede der Wege ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Feature&lt;br /&gt;
! Browser&lt;br /&gt;
! Hybrid (Atom Echo S3)&lt;br /&gt;
|-&lt;br /&gt;
| Wakeword&lt;br /&gt;
| Browser&lt;br /&gt;
| ESP (Hardware)&lt;br /&gt;
|-&lt;br /&gt;
| Push-to-Talk&lt;br /&gt;
| Ja&lt;br /&gt;
| Nein&lt;br /&gt;
|-&lt;br /&gt;
| Always-On&lt;br /&gt;
| Ja (bedingt)&lt;br /&gt;
| Ja&lt;br /&gt;
|-&lt;br /&gt;
| Architektur&lt;br /&gt;
| Software&lt;br /&gt;
| Software + Hardware&lt;br /&gt;
|-&lt;br /&gt;
| Stabilität&lt;br /&gt;
| Sehr stabil&lt;br /&gt;
| Sehr stabil (Wakeword extern)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== FireOS mit Fully Browser (Plus) ==&lt;br /&gt;
&lt;br /&gt;
Damit die Spracherkennung funktioniert:&lt;br /&gt;
&lt;br /&gt;
* Apps installieren:&lt;br /&gt;
   Google&lt;br /&gt;
   Speech Recognition &amp;amp; Synthesis&lt;br /&gt;
* FireOS:&lt;br /&gt;
   „Tastatur und Sprache“ → „Text-to-Speech“&lt;br /&gt;
* Fully Browser:&lt;br /&gt;
   &amp;lt;code&amp;gt;Enable JavaScriptInterface (PLUS)&amp;lt;/code&amp;gt; aktivieren&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;br /&gt;
[[Kategorie:Sprachsteuerung]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40948</id>
		<title>FHEMWEB/VoiceControl: Web-STT &amp; Hardware-Wakeword</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40948"/>
		<updated>2026-04-07T19:41:10Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Voicecontrol.png|mini]]&lt;br /&gt;
&lt;br /&gt;
== VoiceControl – Sprachsteuerung via Browser und Atom Echo s3r ==&lt;br /&gt;
&lt;br /&gt;
Diese Lösung ermöglicht eine flexible Sprachsteuerung für [[FHEM]]. Sprache wird in Text (Speech-to-Text) umgewandelt und als Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; bereitgestellt. Dieses Reading kann anschließend zentral (z. B. über &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DOIF&amp;lt;/code&amp;gt;) ausgewertet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Unterstützt werden nur Chrome-basierte Browser (Chrome, Edge, Fully Browser). Firefox und Chromium haben&lt;br /&gt;
leider kein Backend.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei unterschiedliche Wege zur Spracherfassung:&lt;br /&gt;
&lt;br /&gt;
* Weg 1️⃣ (Software): Browser-basierte Komplettlösung&lt;br /&gt;
* Weg 2️⃣ (Hybrid): Hardware-Wakeword + Browser-Spracherkennung&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Grundprinzip ====&lt;br /&gt;
* Sprache → Speech-to-Text&lt;br /&gt;
* Ergebnis → Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zentrale Logik verarbeitet Befehle&lt;br /&gt;
&lt;br /&gt;
==== Betriebsarten ====&lt;br /&gt;
* Push-to-Talk (nur Browser)&lt;br /&gt;
* Always-On mit Wakeword&lt;br /&gt;
* Hardware-Wakeword (Hybrid)&lt;br /&gt;
&lt;br /&gt;
==== Rückmeldungen ====&lt;br /&gt;
* Sprachausgabe (TTS)&lt;br /&gt;
* Visuelle Bubble im Browser&lt;br /&gt;
* Optional gezielte Rückmeldung per Client-ID&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 1️⃣: Browser-Lösung (voicecontrol.js) ==&lt;br /&gt;
&lt;br /&gt;
Das Script nutzt nur die Google Web Speech API.&lt;br /&gt;
&lt;br /&gt;
=== Bedienung ===&lt;br /&gt;
&lt;br /&gt;
==== Push-to-Talk ====&lt;br /&gt;
* Button gedrückt halten&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung ca.6sek Zeit für Befehl&lt;br /&gt;
&lt;br /&gt;
==== Always-On ====&lt;br /&gt;
* Kurzer Klick aktiviert Dauerbetrieb&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung ca.6sek Zeit für Befehl&lt;br /&gt;
* JS wird neu gestartet udn Schleife fängt von vorne an&lt;br /&gt;
&lt;br /&gt;
==== Wakeword ====&lt;br /&gt;
* Standard: &amp;lt;code&amp;gt;james&amp;lt;/code&amp;gt;&lt;br /&gt;
* Anpassbar im Script:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const wakewords = [&amp;quot;james&amp;quot;];&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{ Svn_GetFile(&#039;contrib/voicecontrol.js&#039;, &#039;www/pgm2/voicecontrol.js&#039;) }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBphone JavaScripts www/pgm2/voicecontrol.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Hinweis (HTTP ohne HTTPS) ====&lt;br /&gt;
Chrome benötigt Freigabe für Mikrofon:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;chrome://flags/#unsafely-treat-insecure-origin-as-secure&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eigene URL hinzufügen&lt;br /&gt;
* Auf &amp;lt;code&amp;gt;Enabled&amp;lt;/code&amp;gt; setzen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 2️⃣: Hybrid-Lösung mit Fully Kiosk Browser + Atom Echo (voicecontrol_echo.js) ==&lt;br /&gt;
&lt;br /&gt;
Diese Variante kombiniert Hardware und Software. Daher ist sie besonders geeignet für&lt;br /&gt;
ein Tablet mit Fully Kiosk Browser oder ähnlich, welches das Webinterface dauerhaft anzeigt.&lt;br /&gt;
&lt;br /&gt;
* Wakeword-Erkennung erfolgt auf dem ESP (Atom Echo s3r)&lt;br /&gt;
* Die eigentliche Sprachverarbeitung (Speech-to-Text) erfolgt im Browser&lt;br /&gt;
&lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
&lt;br /&gt;
# Wakeword wird auf dem ESP erkannt&lt;br /&gt;
# FHEM erzeugt Event (z. B. &amp;lt;code&amp;gt;james_detected&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Browser empfängt Event via WebSocket&lt;br /&gt;
# Speech-to-Text startet im Browser&lt;br /&gt;
# Ergebnis wird an FHEM übertragen&lt;br /&gt;
&lt;br /&gt;
=== Aktivierung ===&lt;br /&gt;
&lt;br /&gt;
* Kurzer Klick auf Button aktiviert/deaktiviert System&lt;br /&gt;
* Baut WebSocket-Verbindung zu FHEM auf&lt;br /&gt;
* Kein Push-to-Talk-Modus&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&lt;br /&gt;
Die Datei voicecontrol_echo.js ist hier zu finden:&lt;br /&gt;
{{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
und muss nach www/pgm2/voicecontrol_echo.js kopiert werden.&lt;br /&gt;
&lt;br /&gt;
Die echo_s3r.yaml ist für den Atom Echo s3r. Die Konfuguration ist weiter unten beschrieben.&lt;br /&gt;
&lt;br /&gt;
==== Einbindung in Fhem ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBtablet JavaScripts www/pgm2/voicecontrol_echo.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Skript voicecontrol_echo.js muss im FHEMWEB-Device angelegt werden, das für den Fully Kiosk Browser zuständig ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBtablet additionalInform atom_echos3r_9888e00f4280,global&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
atom_echos3r_9********** = da kommt das Wakeword an, bzw. der Devicename vom Echo&lt;br /&gt;
global = das ist zum auswerten der Sprachausgabe wichtig&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration im Javascript ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const DEVICE  = &amp;quot;atom_echos3r_9888e00f4280&amp;quot;;&lt;br /&gt;
const TRIGGER = &amp;quot;james_detected&amp;quot;;&lt;br /&gt;
const FHEM_IP = &amp;quot;192.168.1.76:8085&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; → FHEM-Device des ESP&lt;br /&gt;
* &amp;lt;code&amp;gt;TRIGGER&amp;lt;/code&amp;gt; → Event bei Wakeword&lt;br /&gt;
* &amp;lt;code&amp;gt;FHEM_IP&amp;lt;/code&amp;gt; → FHEM-Server&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration in der Yaml ===&lt;br /&gt;
&lt;br /&gt;
Damit sich der ESP mit eurem MQTT-Server und WLAN verbindet, müssen folgende Stellen angepasst werden.&lt;br /&gt;
&lt;br /&gt;
   wifi:&lt;br /&gt;
     ssid: &amp;quot;YOUR_SSID&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     fast_connect: true&lt;br /&gt;
&lt;br /&gt;
   mqtt:&lt;br /&gt;
     broker: 192.168.1.76&lt;br /&gt;
     port: 1884&lt;br /&gt;
     username: &amp;quot;YOUR_USERNAME&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     topic_prefix: atom_echo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wakeword (ESP / ESPHome) ===&lt;br /&gt;
&lt;br /&gt;
Das Wakeword wird direkt auf dem ESP definiert:&lt;br /&gt;
&lt;br /&gt;
* Umsetzung über ESPHome&lt;br /&gt;
* Eine große Auswahl an Wakewords sind hier zu finden:&lt;br /&gt;
   https://github.com/TaterTotterson/microWakeWords&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel, wie das Wakeword definiert wird.&lt;br /&gt;
&lt;br /&gt;
   micro_wake_word:&lt;br /&gt;
     id: mww&lt;br /&gt;
     microphone: echo_mic&lt;br /&gt;
     models:&lt;br /&gt;
       - model: &amp;quot;https://github.com/TaterTotterson/microWakeWords/raw/main/microWakeWordsV2/james.json&amp;quot;&lt;br /&gt;
         id: james_model&lt;br /&gt;
         probability_cutoff: 0.45   &lt;br /&gt;
&lt;br /&gt;
=== Kommunikation ===&lt;br /&gt;
&lt;br /&gt;
* WebSocket-Verbindung zu FHEM&lt;br /&gt;
* Lauscht auf Device-Events&lt;br /&gt;
* Automatischer Reconnect bei Abbruch&lt;br /&gt;
&lt;br /&gt;
=== Ablauf nach Wakeword ===&lt;br /&gt;
&lt;br /&gt;
# System sagt „Ja?“&lt;br /&gt;
# Browser startet SpeechRecognition&lt;br /&gt;
# Nutzer spricht Befehl&lt;br /&gt;
# Befehl wird verarbeitet&lt;br /&gt;
# Rückmeldung „Erledigt“&lt;br /&gt;
&lt;br /&gt;
== Zentrale Auswertung (Logik) ==&lt;br /&gt;
&lt;br /&gt;
Beide Wege schreiben in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;global:STT&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung erfolgt zentral über ein &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt;. Ein Device wird in der Mappingtabelle eingetragen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;quot;esszimmer:licht|lampe|deckenlampe&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;, label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aufschlüsselung:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;quot;hauptkeyword:Filter1|Filter3|Filter3&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Devicename&amp;quot;, label =&amp;gt; &amp;quot;Übersichtname&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: notify ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod n_VoiceControl notify global:STT:.* {\&lt;br /&gt;
    # --- VORBEREITUNG ---\&lt;br /&gt;
    my ($cleanEvent, $clientId) = $EVENT =~ /^(.*)\s\[(.*)\]$/;;\&lt;br /&gt;
    $cleanEvent //= $EVENT;;\&lt;br /&gt;
    $clientId   //= &amp;quot;unknown&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # --- KONFIGURATION: Zentrales Mapping ---\&lt;br /&gt;
    my %smartHomeDevices = (\&lt;br /&gt;
        &amp;quot;esszimmer:licht|lampe|deckenlampe&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;, label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;esszimmer:aquarium&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Aquarium_Aktor&amp;quot;, label =&amp;gt; &amp;quot;Aquarium&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;küche&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Deckenlampe_Kue&amp;quot;, label =&amp;gt; &amp;quot;Licht Küche&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe06_Dek&amp;quot;, label =&amp;gt; &amp;quot;Licht Wohnzimmer&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;fernseher|tv&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;VuPlus&amp;quot;, label =&amp;gt; &amp;quot;Fernseher&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;rechner|pc&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;PC_Aktor&amp;quot;, label =&amp;gt; &amp;quot;PC&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;garage|tor&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Garagentor_Aktor&amp;quot;, label =&amp;gt; &amp;quot;Garagentor öffnen/schließen&amp;quot;, cmdOn =&amp;gt; &amp;quot;open&amp;quot;, cmdOff =&amp;gt; &amp;quot;close&amp;quot; },\&lt;br /&gt;
        &amp;quot;kaffee&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Kaffeemaschine&amp;quot;, label =&amp;gt; &amp;quot;Kaffeemaschine&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;roberto&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;MQTT2_valetudo_FlusteredUnequaledFish&amp;quot;, label =&amp;gt; &amp;quot;Lade Roberto&amp;quot;, cmdOn =&amp;gt; &amp;quot;charge&amp;quot; },\&lt;br /&gt;
        &amp;quot;ambiente&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;LampeSzeneAlle&amp;quot;, label =&amp;gt; &amp;quot;Zentrales Licht&amp;quot;, type =&amp;gt; &amp;quot;dimmer&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;sauge|reinige|putze|staubsauger|roboter&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;MQTT2_valetudo_FlusteredUnequaledFish&amp;quot;, label =&amp;gt; &amp;quot;Staubsauger&amp;quot;, type =&amp;gt; &amp;quot;vacuum&amp;quot; },\&lt;br /&gt;
         &amp;quot;ambilight&amp;quot; =&amp;gt; { label =&amp;gt; &amp;quot;Ambilight&amp;quot;, type =&amp;gt; &amp;quot;system&amp;quot;, cmd =&amp;gt; &amp;quot;sshpass -p &#039;GEHEIMESPASSWORD&#039; ssh -o StrictHostKeyChecking=no root\@192.168.1.46 &#039;/usr/share/hyperhdr/scripts/hyperhdr_toggle.sh&#039;&amp;quot; }\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    my %vacRooms = (&amp;quot;arbeitszimmer&amp;quot; =&amp;gt; &amp;quot;Arbeitszimmer&amp;quot;, &amp;quot;badezimmer&amp;quot; =&amp;gt; &amp;quot;Badezimmer&amp;quot;, &amp;quot;esszimmer&amp;quot; =&amp;gt; &amp;quot;Esszimmer&amp;quot;, &amp;quot;flur&amp;quot; =&amp;gt; &amp;quot;Flur&amp;quot;, &amp;quot;küche&amp;quot; =&amp;gt; &amp;quot;Küche&amp;quot;, &amp;quot;wohnzimmer&amp;quot; =&amp;gt; &amp;quot;Wohnzimmer&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $onRegEx  = &#039;\b(an|ein|einschalten|starte|aktivier|aktiviere|öffne|öffnen|auf|hoch|lade)\b&#039;;;\&lt;br /&gt;
    my $offRegEx = &#039;\b(aus|ausschalten|stop|stoppe|beende|deaktivier|deaktiviere|schließe|schließen|zu|runter)\b&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
    my @commands = split(/\s*(?:und|dann|,)\s*/, lc($cleanEvent));;\&lt;br /&gt;
\&lt;br /&gt;
    # --- DER BEFEHLS-LOOP ---\&lt;br /&gt;
    foreach my $cmd_part (@commands) {\&lt;br /&gt;
        $cmd_part =~ s/^\s+|\s+$//g;;\&lt;br /&gt;
        my $is_on  = ($cmd_part =~ /$onRegEx/) ? 1 : 0;;\&lt;br /&gt;
        my $is_off = ($cmd_part =~ /$offRegEx/) ? 1 : 0;;\&lt;br /&gt;
        $cmd_part =~ s/\b(ich|brauche|mach|bitte|kannst du|würdest du|mal|doch|den|das|die|im|in der)\b//g;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- GENERISCHE STEUERUNG ---\&lt;br /&gt;
        my $match = 0;;\&lt;br /&gt;
        # Sortierung stellt sicher, dass &#039;dimmer/vacuum&#039; Vorrang haben\&lt;br /&gt;
        foreach my $key (sort { ($smartHomeDevices{$b}{type}//&amp;quot;&amp;quot;) cmp ($smartHomeDevices{$a}{type}//&amp;quot;&amp;quot;) } keys %smartHomeDevices) {\&lt;br /&gt;
            \&lt;br /&gt;
            my ($main, $must) = split(/:/, $key);;\&lt;br /&gt;
            \&lt;br /&gt;
            # Prüfe Hauptwort\&lt;br /&gt;
            if ($cmd_part =~ /\b($main)\b/i) {\&lt;br /&gt;
                # Prüfe Pflichtwort falls vorhanden\&lt;br /&gt;
                next if ($must &amp;amp;&amp;amp; $cmd_part !~ /\b($must)\b/i);;\&lt;br /&gt;
                \&lt;br /&gt;
                my $d = $smartHomeDevices{$key};;\&lt;br /&gt;
\&lt;br /&gt;
                # A) System\&lt;br /&gt;
                if (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;system&amp;quot;) {\&lt;br /&gt;
                    system($d-&amp;gt;{cmd});;\&lt;br /&gt;
                }\&lt;br /&gt;
                \&lt;br /&gt;
                # B) Staubsauger\&lt;br /&gt;
                if (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;vacuum&amp;quot;) {\&lt;br /&gt;
                    my @found = grep { $cmd_part =~ /\b$_\b/ } keys %vacRooms;;\&lt;br /&gt;
                    fhem(@found ? &amp;quot;set $d-&amp;gt;{dev} clean_segment &amp;quot;.join(&amp;quot;,&amp;quot;, map { $vacRooms{$_} } @found) : &amp;quot;set $d-&amp;gt;{dev} start&amp;quot;);;\&lt;br /&gt;
                }\&lt;br /&gt;
                # C) Dimmer (Kompakt &amp;amp; korrigiert)\&lt;br /&gt;
                elsif (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;dimmer&amp;quot;) {\&lt;br /&gt;
                    $cmd_part =~ /(\d+)/ \&lt;br /&gt;
                        ? fhem(&amp;quot;set $d-&amp;gt;{dev} brightness &amp;quot; . int(($1 &amp;gt; 100 ? 100 : $1) * 2.55)) \&lt;br /&gt;
                        : ($is_on || $is_off) &amp;amp;&amp;amp; fhem(&amp;quot;set $d-&amp;gt;{dev} &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;));;\&lt;br /&gt;
                }\&lt;br /&gt;
                # D) Standard An/Aus\&lt;br /&gt;
                else {\&lt;br /&gt;
                    my $fhem_cmd = $is_off ? ($d-&amp;gt;{cmdOff} // &amp;quot;off&amp;quot;) : ($d-&amp;gt;{cmdOn} // &amp;quot;on&amp;quot;);;\&lt;br /&gt;
                    fhem(&amp;quot;set $d-&amp;gt;{dev} $fhem_cmd&amp;quot;) if ($is_on || $is_off);;\&lt;br /&gt;
                }\&lt;br /&gt;
                \&lt;br /&gt;
                $match = 1;; last;;\&lt;br /&gt;
            }\&lt;br /&gt;
        }\&lt;br /&gt;
        next if $match;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- HILFE ---\&lt;br /&gt;
        if ($cmd_part =~ /(hilfe|kommandos|übersicht)/) {\&lt;br /&gt;
            my $h = &#039;&amp;lt;div style=&amp;quot;text-align:left;;min-width:250px;;font-family:sans-serif;;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Befehlsübersicht:&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            my %seen;;\&lt;br /&gt;
            for my $k (sort keys %smartHomeDevices) {\&lt;br /&gt;
                my $d = $smartHomeDevices{$k};;\&lt;br /&gt;
                next if $seen{$d-&amp;gt;{label}}++;; \&lt;br /&gt;
                my $suffix = ($d-&amp;gt;{cmdOn} &amp;amp;&amp;amp; $d-&amp;gt;{cmdOff} &amp;amp;&amp;amp; $d-&amp;gt;{label} !~ /(an\/aus|öffnen\/schließen)/i) ? &amp;quot; (an/aus)&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
                $suffix = &amp;quot; [0-100%]&amp;quot; if ($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;dimmer&amp;quot;;;\&lt;br /&gt;
                $h .= &amp;quot;• $d-&amp;gt;{label}$suffix&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            }\&lt;br /&gt;
            $h .= &#039;&amp;lt;br&amp;gt;&amp;lt;u&amp;gt;Staubsauger Räume&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;• &#039;.join(&amp;quot;, &amp;quot;, map { ucfirst($_) } sort keys %vacRooms).&#039;&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt;&#039;;;\&lt;br /&gt;
            $h =~ s/&#039;/\\&amp;quot;/g;;\&lt;br /&gt;
            my $js = &amp;quot;if((document.querySelector(&#039;input[name=\&amp;quot;fw_id\&amp;quot;]&#039;)||{}).value===&#039;$clientId&#039;){FW_okDialog(&#039;$h&#039;)}&amp;quot;;;\&lt;br /&gt;
            FW_directNotify(&amp;quot;#FHEMWEB:$_&amp;quot;, $js, &amp;quot;&amp;quot;) for devspec2array(&amp;quot;TYPE=FHEMWEB&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Unterschiede der Wege ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Feature&lt;br /&gt;
! Browser&lt;br /&gt;
! Hybrid (Atom Echo S3)&lt;br /&gt;
|-&lt;br /&gt;
| Wakeword&lt;br /&gt;
| Browser&lt;br /&gt;
| ESP (Hardware)&lt;br /&gt;
|-&lt;br /&gt;
| Push-to-Talk&lt;br /&gt;
| Ja&lt;br /&gt;
| Nein&lt;br /&gt;
|-&lt;br /&gt;
| Always-On&lt;br /&gt;
| Ja (bedingt)&lt;br /&gt;
| Ja&lt;br /&gt;
|-&lt;br /&gt;
| Architektur&lt;br /&gt;
| Software&lt;br /&gt;
| Software + Hardware&lt;br /&gt;
|-&lt;br /&gt;
| Stabilität&lt;br /&gt;
| Sehr stabil&lt;br /&gt;
| Sehr stabil (Wakeword extern)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== FireOS mit Fully Browser (Plus) ==&lt;br /&gt;
&lt;br /&gt;
Damit die Spracherkennung funktioniert:&lt;br /&gt;
&lt;br /&gt;
* Apps installieren:&lt;br /&gt;
   Google&lt;br /&gt;
   Speech Recognition &amp;amp; Synthesis&lt;br /&gt;
* FireOS:&lt;br /&gt;
   „Tastatur und Sprache“ → „Text-to-Speech“&lt;br /&gt;
* Fully Browser:&lt;br /&gt;
   &amp;lt;code&amp;gt;Enable JavaScriptInterface (PLUS)&amp;lt;/code&amp;gt; aktivieren&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;br /&gt;
[[Kategorie:Sprachsteuerung]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40947</id>
		<title>FHEMWEB/VoiceControl: Web-STT &amp; Hardware-Wakeword</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40947"/>
		<updated>2026-04-07T19:20:31Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Voicecontrol.png|mini]]&lt;br /&gt;
&lt;br /&gt;
== VoiceControl – Sprachsteuerung via Browser und Atom Echo s3r ==&lt;br /&gt;
&lt;br /&gt;
Diese Lösung ermöglicht eine flexible Sprachsteuerung für [[FHEM]]. Sprache wird in Text (Speech-to-Text) umgewandelt und als Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; bereitgestellt. Dieses Reading kann anschließend zentral (z. B. über &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DOIF&amp;lt;/code&amp;gt;) ausgewertet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Unterstützt werden nur Chrome-basierte Browser (Chrome, Edge, Fully Browser). Firefox und Chromium haben&lt;br /&gt;
leider kein Backend.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei unterschiedliche Wege zur Spracherfassung:&lt;br /&gt;
&lt;br /&gt;
* Weg 1️⃣ (Software): Browser-basierte Komplettlösung&lt;br /&gt;
* Weg 2️⃣ (Hybrid): Hardware-Wakeword + Browser-Spracherkennung&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Grundprinzip ====&lt;br /&gt;
* Sprache → Speech-to-Text&lt;br /&gt;
* Ergebnis → Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zentrale Logik verarbeitet Befehle&lt;br /&gt;
&lt;br /&gt;
==== Betriebsarten ====&lt;br /&gt;
* Push-to-Talk (nur Browser)&lt;br /&gt;
* Always-On mit Wakeword&lt;br /&gt;
* Hardware-Wakeword (Hybrid)&lt;br /&gt;
&lt;br /&gt;
==== Rückmeldungen ====&lt;br /&gt;
* Sprachausgabe (TTS)&lt;br /&gt;
* Visuelle Bubble im Browser&lt;br /&gt;
* Optional gezielte Rückmeldung per Client-ID&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 1️⃣: Browser-Lösung (voicecontrol.js) ==&lt;br /&gt;
&lt;br /&gt;
Das Script nutzt nur die Google Web Speech API.&lt;br /&gt;
&lt;br /&gt;
=== Bedienung ===&lt;br /&gt;
&lt;br /&gt;
==== Push-to-Talk ====&lt;br /&gt;
* Button gedrückt halten&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung ca.6sek Zeit für Befehl&lt;br /&gt;
&lt;br /&gt;
==== Always-On ====&lt;br /&gt;
* Kurzer Klick aktiviert Dauerbetrieb&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung ca.6sek Zeit für Befehl&lt;br /&gt;
* JS wird neu gestartet udn Schleife fängt von vorne an&lt;br /&gt;
&lt;br /&gt;
==== Wakeword ====&lt;br /&gt;
* Standard: &amp;lt;code&amp;gt;james&amp;lt;/code&amp;gt;&lt;br /&gt;
* Anpassbar im Script:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const wakewords = [&amp;quot;james&amp;quot;];&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{ Svn_GetFile(&#039;contrib/voicecontrol.js&#039;, &#039;www/pgm2/voicecontrol.js&#039;) }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBphone JavaScripts www/pgm2/voicecontrol.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Hinweis (HTTP ohne HTTPS) ====&lt;br /&gt;
Chrome benötigt Freigabe für Mikrofon:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;chrome://flags/#unsafely-treat-insecure-origin-as-secure&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eigene URL hinzufügen&lt;br /&gt;
* Auf &amp;lt;code&amp;gt;Enabled&amp;lt;/code&amp;gt; setzen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 2️⃣: Hybrid-Lösung mit Browser + Atom Echo (voicecontrol_echo.js) ==&lt;br /&gt;
&lt;br /&gt;
Diese Variante kombiniert Hardware und Software:&lt;br /&gt;
&lt;br /&gt;
* Wakeword-Erkennung erfolgt auf dem ESP (Atom Echo s3r)&lt;br /&gt;
* Die eigentliche Sprachverarbeitung (Speech-to-Text) erfolgt im Browser&lt;br /&gt;
&lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
&lt;br /&gt;
# Wakeword wird auf dem ESP erkannt&lt;br /&gt;
# FHEM erzeugt Event (z. B. &amp;lt;code&amp;gt;james_detected&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Browser empfängt Event via WebSocket&lt;br /&gt;
# Speech-to-Text startet im Browser&lt;br /&gt;
# Ergebnis wird an FHEM übertragen&lt;br /&gt;
&lt;br /&gt;
=== Aktivierung ===&lt;br /&gt;
&lt;br /&gt;
* Kurzer Klick auf Button aktiviert/deaktiviert System&lt;br /&gt;
* Baut WebSocket-Verbindung zu FHEM auf&lt;br /&gt;
* Kein Push-to-Talk-Modus&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&lt;br /&gt;
Die Datei voicecontrol_echo.js ist hier zu finden:&lt;br /&gt;
{{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
und muss nach www/pgm2/voicecontrol_echo.js kopiert werden.&lt;br /&gt;
&lt;br /&gt;
Die echo_s3r.yaml ist für den Atom Echo s3r. Die Konfuguration ist weiter unten beschrieben.&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBtablet JavaScripts www/pgm2/voicecontrol_echo.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration im Javascript ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const DEVICE  = &amp;quot;atom_echos3r_9888e00f4280&amp;quot;;&lt;br /&gt;
const TRIGGER = &amp;quot;james_detected&amp;quot;;&lt;br /&gt;
const FHEM_IP = &amp;quot;192.168.1.76:8085&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; → FHEM-Device des ESP&lt;br /&gt;
* &amp;lt;code&amp;gt;TRIGGER&amp;lt;/code&amp;gt; → Event bei Wakeword&lt;br /&gt;
* &amp;lt;code&amp;gt;FHEM_IP&amp;lt;/code&amp;gt; → FHEM-Server&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration in der Yaml ===&lt;br /&gt;
&lt;br /&gt;
Damit sich der ESP mit eurem MQTT-Server und WLAN verbindet, müssen folgende Stellen angepasst werden.&lt;br /&gt;
&lt;br /&gt;
   wifi:&lt;br /&gt;
     ssid: &amp;quot;YOUR_SSID&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     fast_connect: true&lt;br /&gt;
&lt;br /&gt;
   mqtt:&lt;br /&gt;
     broker: 192.168.1.76&lt;br /&gt;
     port: 1884&lt;br /&gt;
     username: &amp;quot;YOUR_USERNAME&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     topic_prefix: atom_echo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wakeword (ESP / ESPHome) ===&lt;br /&gt;
&lt;br /&gt;
Das Wakeword wird direkt auf dem ESP definiert:&lt;br /&gt;
&lt;br /&gt;
* Umsetzung über ESPHome&lt;br /&gt;
* Eine große Auswahl an Wakewords sind hier zu finden:&lt;br /&gt;
   https://github.com/TaterTotterson/microWakeWords&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel, wie das Wakeword definiert wird.&lt;br /&gt;
&lt;br /&gt;
   micro_wake_word:&lt;br /&gt;
     id: mww&lt;br /&gt;
     microphone: echo_mic&lt;br /&gt;
     models:&lt;br /&gt;
       - model: &amp;quot;https://github.com/TaterTotterson/microWakeWords/raw/main/microWakeWordsV2/james.json&amp;quot;&lt;br /&gt;
         id: james_model&lt;br /&gt;
         probability_cutoff: 0.6   &lt;br /&gt;
&lt;br /&gt;
=== Kommunikation ===&lt;br /&gt;
&lt;br /&gt;
* WebSocket-Verbindung zu FHEM&lt;br /&gt;
* Lauscht auf Device-Events&lt;br /&gt;
* Automatischer Reconnect bei Abbruch&lt;br /&gt;
&lt;br /&gt;
=== Ablauf nach Wakeword ===&lt;br /&gt;
&lt;br /&gt;
# System sagt „Ja?“&lt;br /&gt;
# Browser startet SpeechRecognition&lt;br /&gt;
# Nutzer spricht Befehl&lt;br /&gt;
# Befehl wird verarbeitet&lt;br /&gt;
# Rückmeldung „Erledigt“&lt;br /&gt;
&lt;br /&gt;
== Zentrale Auswertung (Logik) ==&lt;br /&gt;
&lt;br /&gt;
Beide Wege schreiben in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;global:STT&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung erfolgt zentral über ein &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt;. Ein Device wird in der Mappingtabelle eingetragen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;quot;esszimmer:licht|lampe|deckenlampe&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;, label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aufschlüsselung:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;quot;hauptkeyword:Filter1|Filter3|Filter3&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Devicename&amp;quot;, label =&amp;gt; &amp;quot;Übersichtname&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: notify ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod n_VoiceControl notify global:STT:.* {\&lt;br /&gt;
    # --- VORBEREITUNG ---\&lt;br /&gt;
    my ($cleanEvent, $clientId) = $EVENT =~ /^(.*)\s\[(.*)\]$/;;\&lt;br /&gt;
    $cleanEvent //= $EVENT;;\&lt;br /&gt;
    $clientId   //= &amp;quot;unknown&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # --- KONFIGURATION: Zentrales Mapping ---\&lt;br /&gt;
    my %smartHomeDevices = (\&lt;br /&gt;
        &amp;quot;esszimmer:licht|lampe|deckenlampe&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;, label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;esszimmer:aquarium&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Aquarium_Aktor&amp;quot;, label =&amp;gt; &amp;quot;Aquarium&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;küche&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Deckenlampe_Kue&amp;quot;, label =&amp;gt; &amp;quot;Licht Küche&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe06_Dek&amp;quot;, label =&amp;gt; &amp;quot;Licht Wohnzimmer&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;fernseher|tv&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;VuPlus&amp;quot;, label =&amp;gt; &amp;quot;Fernseher&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;rechner|pc&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;PC_Aktor&amp;quot;, label =&amp;gt; &amp;quot;PC&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;garage|tor&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Garagentor_Aktor&amp;quot;, label =&amp;gt; &amp;quot;Garagentor öffnen/schließen&amp;quot;, cmdOn =&amp;gt; &amp;quot;open&amp;quot;, cmdOff =&amp;gt; &amp;quot;close&amp;quot; },\&lt;br /&gt;
        &amp;quot;kaffee&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Kaffeemaschine&amp;quot;, label =&amp;gt; &amp;quot;Kaffeemaschine&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;roberto&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;MQTT2_valetudo_FlusteredUnequaledFish&amp;quot;, label =&amp;gt; &amp;quot;Lade Roberto&amp;quot;, cmdOn =&amp;gt; &amp;quot;charge&amp;quot; },\&lt;br /&gt;
        &amp;quot;ambiente&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;LampeSzeneAlle&amp;quot;, label =&amp;gt; &amp;quot;Zentrales Licht&amp;quot;, type =&amp;gt; &amp;quot;dimmer&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;sauge|reinige|putze|staubsauger|roboter&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;MQTT2_valetudo_FlusteredUnequaledFish&amp;quot;, label =&amp;gt; &amp;quot;Staubsauger&amp;quot;, type =&amp;gt; &amp;quot;vacuum&amp;quot; },\&lt;br /&gt;
         &amp;quot;ambilight&amp;quot; =&amp;gt; { label =&amp;gt; &amp;quot;Ambilight&amp;quot;, type =&amp;gt; &amp;quot;system&amp;quot;, cmd =&amp;gt; &amp;quot;sshpass -p &#039;GEHEIMESPASSWORD&#039; ssh -o StrictHostKeyChecking=no root\@192.168.1.46 &#039;/usr/share/hyperhdr/scripts/hyperhdr_toggle.sh&#039;&amp;quot; }\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    my %vacRooms = (&amp;quot;arbeitszimmer&amp;quot; =&amp;gt; &amp;quot;Arbeitszimmer&amp;quot;, &amp;quot;badezimmer&amp;quot; =&amp;gt; &amp;quot;Badezimmer&amp;quot;, &amp;quot;esszimmer&amp;quot; =&amp;gt; &amp;quot;Esszimmer&amp;quot;, &amp;quot;flur&amp;quot; =&amp;gt; &amp;quot;Flur&amp;quot;, &amp;quot;küche&amp;quot; =&amp;gt; &amp;quot;Küche&amp;quot;, &amp;quot;wohnzimmer&amp;quot; =&amp;gt; &amp;quot;Wohnzimmer&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $onRegEx  = &#039;\b(an|ein|einschalten|starte|aktivier|aktiviere|öffne|öffnen|auf|hoch|lade)\b&#039;;;\&lt;br /&gt;
    my $offRegEx = &#039;\b(aus|ausschalten|stop|stoppe|beende|deaktivier|deaktiviere|schließe|schließen|zu|runter)\b&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
    my @commands = split(/\s*(?:und|dann|,)\s*/, lc($cleanEvent));;\&lt;br /&gt;
\&lt;br /&gt;
    # --- DER BEFEHLS-LOOP ---\&lt;br /&gt;
    foreach my $cmd_part (@commands) {\&lt;br /&gt;
        $cmd_part =~ s/^\s+|\s+$//g;;\&lt;br /&gt;
        my $is_on  = ($cmd_part =~ /$onRegEx/) ? 1 : 0;;\&lt;br /&gt;
        my $is_off = ($cmd_part =~ /$offRegEx/) ? 1 : 0;;\&lt;br /&gt;
        $cmd_part =~ s/\b(ich|brauche|mach|bitte|kannst du|würdest du|mal|doch|den|das|die|im|in der)\b//g;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- GENERISCHE STEUERUNG ---\&lt;br /&gt;
        my $match = 0;;\&lt;br /&gt;
        # Sortierung stellt sicher, dass &#039;dimmer/vacuum&#039; Vorrang haben\&lt;br /&gt;
        foreach my $key (sort { ($smartHomeDevices{$b}{type}//&amp;quot;&amp;quot;) cmp ($smartHomeDevices{$a}{type}//&amp;quot;&amp;quot;) } keys %smartHomeDevices) {\&lt;br /&gt;
            \&lt;br /&gt;
            my ($main, $must) = split(/:/, $key);;\&lt;br /&gt;
            \&lt;br /&gt;
            # Prüfe Hauptwort\&lt;br /&gt;
            if ($cmd_part =~ /\b($main)\b/i) {\&lt;br /&gt;
                # Prüfe Pflichtwort falls vorhanden\&lt;br /&gt;
                next if ($must &amp;amp;&amp;amp; $cmd_part !~ /\b($must)\b/i);;\&lt;br /&gt;
                \&lt;br /&gt;
                my $d = $smartHomeDevices{$key};;\&lt;br /&gt;
\&lt;br /&gt;
                # A) System\&lt;br /&gt;
                if (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;system&amp;quot;) {\&lt;br /&gt;
                    system($d-&amp;gt;{cmd});;\&lt;br /&gt;
                }\&lt;br /&gt;
                \&lt;br /&gt;
                # B) Staubsauger\&lt;br /&gt;
                if (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;vacuum&amp;quot;) {\&lt;br /&gt;
                    my @found = grep { $cmd_part =~ /\b$_\b/ } keys %vacRooms;;\&lt;br /&gt;
                    fhem(@found ? &amp;quot;set $d-&amp;gt;{dev} clean_segment &amp;quot;.join(&amp;quot;,&amp;quot;, map { $vacRooms{$_} } @found) : &amp;quot;set $d-&amp;gt;{dev} start&amp;quot;);;\&lt;br /&gt;
                }\&lt;br /&gt;
                # C) Dimmer (Kompakt &amp;amp; korrigiert)\&lt;br /&gt;
                elsif (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;dimmer&amp;quot;) {\&lt;br /&gt;
                    $cmd_part =~ /(\d+)/ \&lt;br /&gt;
                        ? fhem(&amp;quot;set $d-&amp;gt;{dev} brightness &amp;quot; . int(($1 &amp;gt; 100 ? 100 : $1) * 2.55)) \&lt;br /&gt;
                        : ($is_on || $is_off) &amp;amp;&amp;amp; fhem(&amp;quot;set $d-&amp;gt;{dev} &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;));;\&lt;br /&gt;
                }\&lt;br /&gt;
                # D) Standard An/Aus\&lt;br /&gt;
                else {\&lt;br /&gt;
                    my $fhem_cmd = $is_off ? ($d-&amp;gt;{cmdOff} // &amp;quot;off&amp;quot;) : ($d-&amp;gt;{cmdOn} // &amp;quot;on&amp;quot;);;\&lt;br /&gt;
                    fhem(&amp;quot;set $d-&amp;gt;{dev} $fhem_cmd&amp;quot;) if ($is_on || $is_off);;\&lt;br /&gt;
                }\&lt;br /&gt;
                \&lt;br /&gt;
                $match = 1;; last;;\&lt;br /&gt;
            }\&lt;br /&gt;
        }\&lt;br /&gt;
        next if $match;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- HILFE ---\&lt;br /&gt;
        if ($cmd_part =~ /(hilfe|kommandos|übersicht)/) {\&lt;br /&gt;
            my $h = &#039;&amp;lt;div style=&amp;quot;text-align:left;;min-width:250px;;font-family:sans-serif;;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Befehlsübersicht:&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            my %seen;;\&lt;br /&gt;
            for my $k (sort keys %smartHomeDevices) {\&lt;br /&gt;
                my $d = $smartHomeDevices{$k};;\&lt;br /&gt;
                next if $seen{$d-&amp;gt;{label}}++;; \&lt;br /&gt;
                my $suffix = ($d-&amp;gt;{cmdOn} &amp;amp;&amp;amp; $d-&amp;gt;{cmdOff} &amp;amp;&amp;amp; $d-&amp;gt;{label} !~ /(an\/aus|öffnen\/schließen)/i) ? &amp;quot; (an/aus)&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
                $suffix = &amp;quot; [0-100%]&amp;quot; if ($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;dimmer&amp;quot;;;\&lt;br /&gt;
                $h .= &amp;quot;• $d-&amp;gt;{label}$suffix&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            }\&lt;br /&gt;
            $h .= &#039;&amp;lt;br&amp;gt;&amp;lt;u&amp;gt;Staubsauger Räume&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;• &#039;.join(&amp;quot;, &amp;quot;, map { ucfirst($_) } sort keys %vacRooms).&#039;&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt;&#039;;;\&lt;br /&gt;
            $h =~ s/&#039;/\\&amp;quot;/g;;\&lt;br /&gt;
            my $js = &amp;quot;if((document.querySelector(&#039;input[name=\&amp;quot;fw_id\&amp;quot;]&#039;)||{}).value===&#039;$clientId&#039;){FW_okDialog(&#039;$h&#039;)}&amp;quot;;;\&lt;br /&gt;
            FW_directNotify(&amp;quot;#FHEMWEB:$_&amp;quot;, $js, &amp;quot;&amp;quot;) for devspec2array(&amp;quot;TYPE=FHEMWEB&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Unterschiede der Wege ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Feature&lt;br /&gt;
! Browser&lt;br /&gt;
! Hybrid (Atom Echo S3)&lt;br /&gt;
|-&lt;br /&gt;
| Wakeword&lt;br /&gt;
| Browser&lt;br /&gt;
| ESP (Hardware)&lt;br /&gt;
|-&lt;br /&gt;
| Push-to-Talk&lt;br /&gt;
| Ja&lt;br /&gt;
| Nein&lt;br /&gt;
|-&lt;br /&gt;
| Always-On&lt;br /&gt;
| Ja (bedingt)&lt;br /&gt;
| Ja&lt;br /&gt;
|-&lt;br /&gt;
| Architektur&lt;br /&gt;
| Software&lt;br /&gt;
| Software + Hardware&lt;br /&gt;
|-&lt;br /&gt;
| Stabilität&lt;br /&gt;
| Sehr stabil&lt;br /&gt;
| Sehr stabil (Wakeword extern)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== FireOS mit Fully Browser (Plus) ==&lt;br /&gt;
&lt;br /&gt;
Damit die Spracherkennung funktioniert:&lt;br /&gt;
&lt;br /&gt;
* Apps installieren:&lt;br /&gt;
   Google&lt;br /&gt;
   Speech Recognition &amp;amp; Synthesis&lt;br /&gt;
* FireOS:&lt;br /&gt;
   „Tastatur und Sprache“ → „Text-to-Speech“&lt;br /&gt;
* Fully Browser:&lt;br /&gt;
   &amp;lt;code&amp;gt;Enable JavaScriptInterface (PLUS)&amp;lt;/code&amp;gt; aktivieren&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;br /&gt;
[[Kategorie:Sprachsteuerung]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40918</id>
		<title>FHEMWEB/VoiceControl: Web-STT &amp; Hardware-Wakeword</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40918"/>
		<updated>2026-04-03T17:00:31Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Voicecontrol.png|mini]]&lt;br /&gt;
&lt;br /&gt;
== VoiceControl – Sprachsteuerung via Browser und Atom Echo s3r ==&lt;br /&gt;
&lt;br /&gt;
Diese Lösung ermöglicht eine flexible Sprachsteuerung für [[FHEM]]. Sprache wird in Text (Speech-to-Text) umgewandelt und als Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; bereitgestellt. Dieses Reading kann anschließend zentral (z. B. über &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DOIF&amp;lt;/code&amp;gt;) ausgewertet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Unterstützt werden nur Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox hat leider kein Backend.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei unterschiedliche Wege zur Spracherfassung:&lt;br /&gt;
&lt;br /&gt;
* Weg 1️⃣ (Software): Browser-basierte Komplettlösung&lt;br /&gt;
* Weg 2️⃣ (Hybrid): Hardware-Wakeword + Browser-Spracherkennung&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Grundprinzip ====&lt;br /&gt;
* Sprache → Speech-to-Text&lt;br /&gt;
* Ergebnis → Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zentrale Logik verarbeitet Befehle&lt;br /&gt;
&lt;br /&gt;
==== Betriebsarten ====&lt;br /&gt;
* Push-to-Talk (nur Browser)&lt;br /&gt;
* Always-On mit Wakeword&lt;br /&gt;
* Hardware-Wakeword (Hybrid)&lt;br /&gt;
&lt;br /&gt;
==== Rückmeldungen ====&lt;br /&gt;
* Sprachausgabe (TTS)&lt;br /&gt;
* Visuelle Bubble im Browser&lt;br /&gt;
* Optional gezielte Rückmeldung per Client-ID&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 1️⃣: Browser-Lösung (voicecontrol.js) ==&lt;br /&gt;
&lt;br /&gt;
Das Script nutzt die Google Web Speech API. Unterstützt werden Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox wird aktuell nicht unterstützt.&lt;br /&gt;
&lt;br /&gt;
=== Bedienung ===&lt;br /&gt;
&lt;br /&gt;
==== Push-to-Talk ====&lt;br /&gt;
* Button gedrückt halten&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung ca.6sek Zeit für Befehl&lt;br /&gt;
&lt;br /&gt;
==== Always-On ====&lt;br /&gt;
* Kurzer Klick aktiviert Dauerbetrieb&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung ca.6sek Zeit für Befehl&lt;br /&gt;
* JS wird neu gestartet udn Schleife fängt von vorne an&lt;br /&gt;
&lt;br /&gt;
==== Wakeword ====&lt;br /&gt;
* Standard: &amp;lt;code&amp;gt;james&amp;lt;/code&amp;gt;&lt;br /&gt;
* Anpassbar im Script:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const wakewords = [&amp;quot;james&amp;quot;];&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{ Svn_GetFile(&#039;contrib/voicecontrol.js&#039;, &#039;www/pgm2/voicecontrol.js&#039;) }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBphone JavaScripts www/pgm2/voicecontrol.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Hinweis (HTTP ohne HTTPS) ====&lt;br /&gt;
Chrome benötigt Freigabe für Mikrofon:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;chrome://flags/#unsafely-treat-insecure-origin-as-secure&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eigene URL hinzufügen&lt;br /&gt;
* Auf &amp;lt;code&amp;gt;Enabled&amp;lt;/code&amp;gt; setzen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 2️⃣: Hybrid-Lösung mit Browser + Atom Echo (voicecontrol_echo.js) ==&lt;br /&gt;
&lt;br /&gt;
Diese Variante kombiniert Hardware und Software:&lt;br /&gt;
&lt;br /&gt;
* Wakeword-Erkennung erfolgt auf dem ESP (Atom Echo s3r)&lt;br /&gt;
* Die eigentliche Sprachverarbeitung (Speech-to-Text) erfolgt im Browser&lt;br /&gt;
&lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
&lt;br /&gt;
# Wakeword wird auf dem ESP erkannt&lt;br /&gt;
# FHEM erzeugt Event (z. B. &amp;lt;code&amp;gt;james_detected&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Browser empfängt Event via WebSocket&lt;br /&gt;
# Speech-to-Text startet im Browser&lt;br /&gt;
# Ergebnis wird an FHEM übertragen&lt;br /&gt;
&lt;br /&gt;
=== Aktivierung ===&lt;br /&gt;
&lt;br /&gt;
* Kurzer Klick auf Button aktiviert/deaktiviert System&lt;br /&gt;
* Baut WebSocket-Verbindung zu FHEM auf&lt;br /&gt;
* Kein Push-to-Talk-Modus&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&lt;br /&gt;
Die Datei voicecontrol_echo.js ist hier zu finden:&lt;br /&gt;
{{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
und muss nach www/pgm2/voicecontrol_echo.js kopiert werden.&lt;br /&gt;
&lt;br /&gt;
Die echo_s3r.yaml ist für den Atom Echo s3r. Die Konfuguration ist weiter unten beschrieben.&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBtablet JavaScripts www/pgm2/voicecontrol_echo.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration im Javascript ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const DEVICE  = &amp;quot;atom_echos3r_9888e00f4280&amp;quot;;&lt;br /&gt;
const TRIGGER = &amp;quot;james_detected&amp;quot;;&lt;br /&gt;
const FHEM_IP = &amp;quot;192.168.1.76:8085&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; → FHEM-Device des ESP&lt;br /&gt;
* &amp;lt;code&amp;gt;TRIGGER&amp;lt;/code&amp;gt; → Event bei Wakeword&lt;br /&gt;
* &amp;lt;code&amp;gt;FHEM_IP&amp;lt;/code&amp;gt; → FHEM-Server&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration in der Yaml ===&lt;br /&gt;
&lt;br /&gt;
Damit sich der ESP mit eurem MQTT-Server und WLAN verbindet, müssen folgende Stellen angepasst werden.&lt;br /&gt;
&lt;br /&gt;
   wifi:&lt;br /&gt;
     ssid: &amp;quot;YOUR_SSID&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     fast_connect: true&lt;br /&gt;
&lt;br /&gt;
   mqtt:&lt;br /&gt;
     broker: 192.168.1.76&lt;br /&gt;
     port: 1884&lt;br /&gt;
     username: &amp;quot;YOUR_USERNAME&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     topic_prefix: atom_echo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wakeword (ESP / ESPHome) ===&lt;br /&gt;
&lt;br /&gt;
Das Wakeword wird direkt auf dem ESP definiert:&lt;br /&gt;
&lt;br /&gt;
* Umsetzung über ESPHome&lt;br /&gt;
* Eine große Auswahl an Wakewords sind hier zu finden:&lt;br /&gt;
   https://github.com/TaterTotterson/microWakeWords&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel, wie das Wakeword definiert wird.&lt;br /&gt;
&lt;br /&gt;
   micro_wake_word:&lt;br /&gt;
     id: mww&lt;br /&gt;
     microphone: echo_mic&lt;br /&gt;
     models:&lt;br /&gt;
       - model: &amp;quot;https://github.com/TaterTotterson/microWakeWords/raw/main/microWakeWordsV2/james.json&amp;quot;&lt;br /&gt;
         id: james_model&lt;br /&gt;
         probability_cutoff: 0.6   &lt;br /&gt;
&lt;br /&gt;
=== Kommunikation ===&lt;br /&gt;
&lt;br /&gt;
* WebSocket-Verbindung zu FHEM&lt;br /&gt;
* Lauscht auf Device-Events&lt;br /&gt;
* Automatischer Reconnect bei Abbruch&lt;br /&gt;
&lt;br /&gt;
=== Ablauf nach Wakeword ===&lt;br /&gt;
&lt;br /&gt;
# System sagt „Ja?“&lt;br /&gt;
# Browser startet SpeechRecognition&lt;br /&gt;
# Nutzer spricht Befehl&lt;br /&gt;
# Befehl wird verarbeitet&lt;br /&gt;
# Rückmeldung „Erledigt“&lt;br /&gt;
&lt;br /&gt;
== Zentrale Auswertung (Logik) ==&lt;br /&gt;
&lt;br /&gt;
Beide Wege schreiben in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;global:STT&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung erfolgt zentral über ein &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt;. Ein Device wird in der Mappingtabelle eingetragen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;quot;esszimmer:licht|lampe|deckenlampe&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;, label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aufschlüsselung:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;quot;hauptkeyword:Filter1|Filter3|Filter3&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Devicename&amp;quot;, label =&amp;gt; &amp;quot;Übersichtname&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: notify ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod n_VoiceControl notify global:STT:.* {\&lt;br /&gt;
    # --- VORBEREITUNG ---\&lt;br /&gt;
    my ($cleanEvent, $clientId) = $EVENT =~ /^(.*)\s\[(.*)\]$/;;\&lt;br /&gt;
    $cleanEvent //= $EVENT;;\&lt;br /&gt;
    $clientId   //= &amp;quot;unknown&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # --- KONFIGURATION: Zentrales Mapping ---\&lt;br /&gt;
    my %smartHomeDevices = (\&lt;br /&gt;
        &amp;quot;esszimmer:licht|lampe|deckenlampe&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;, label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;esszimmer:aquarium&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Aquarium_Aktor&amp;quot;, label =&amp;gt; &amp;quot;Aquarium&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;küche&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Deckenlampe_Kue&amp;quot;, label =&amp;gt; &amp;quot;Licht Küche&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe06_Dek&amp;quot;, label =&amp;gt; &amp;quot;Licht Wohnzimmer&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;fernseher|tv&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;VuPlus&amp;quot;, label =&amp;gt; &amp;quot;Fernseher&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;rechner|pc&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;PC_Aktor&amp;quot;, label =&amp;gt; &amp;quot;PC&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;garage|tor&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Garagentor_Aktor&amp;quot;, label =&amp;gt; &amp;quot;Garagentor öffnen/schließen&amp;quot;, cmdOn =&amp;gt; &amp;quot;open&amp;quot;, cmdOff =&amp;gt; &amp;quot;close&amp;quot; },\&lt;br /&gt;
        &amp;quot;kaffee&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Kaffeemaschine&amp;quot;, label =&amp;gt; &amp;quot;Kaffeemaschine&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;roberto&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;MQTT2_valetudo_FlusteredUnequaledFish&amp;quot;, label =&amp;gt; &amp;quot;Lade Roberto&amp;quot;, cmdOn =&amp;gt; &amp;quot;charge&amp;quot; },\&lt;br /&gt;
        &amp;quot;ambiente&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;LampeSzeneAlle&amp;quot;, label =&amp;gt; &amp;quot;Zentrales Licht&amp;quot;, type =&amp;gt; &amp;quot;dimmer&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;sauge|reinige|putze|staubsauger|roboter&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;MQTT2_valetudo_FlusteredUnequaledFish&amp;quot;, label =&amp;gt; &amp;quot;Staubsauger&amp;quot;, type =&amp;gt; &amp;quot;vacuum&amp;quot; },\&lt;br /&gt;
         &amp;quot;ambilight&amp;quot; =&amp;gt; { label =&amp;gt; &amp;quot;Ambilight&amp;quot;, type =&amp;gt; &amp;quot;system&amp;quot;, cmd =&amp;gt; &amp;quot;sshpass -p &#039;GEHEIMESPASSWORD&#039; ssh -o StrictHostKeyChecking=no root\@192.168.1.46 &#039;/usr/share/hyperhdr/scripts/hyperhdr_toggle.sh&#039;&amp;quot; }\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    my %vacRooms = (&amp;quot;arbeitszimmer&amp;quot; =&amp;gt; &amp;quot;Arbeitszimmer&amp;quot;, &amp;quot;badezimmer&amp;quot; =&amp;gt; &amp;quot;Badezimmer&amp;quot;, &amp;quot;esszimmer&amp;quot; =&amp;gt; &amp;quot;Esszimmer&amp;quot;, &amp;quot;flur&amp;quot; =&amp;gt; &amp;quot;Flur&amp;quot;, &amp;quot;küche&amp;quot; =&amp;gt; &amp;quot;Küche&amp;quot;, &amp;quot;wohnzimmer&amp;quot; =&amp;gt; &amp;quot;Wohnzimmer&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $onRegEx  = &#039;\b(an|ein|einschalten|starte|aktivier|aktiviere|öffne|öffnen|auf|hoch|lade)\b&#039;;;\&lt;br /&gt;
    my $offRegEx = &#039;\b(aus|ausschalten|stop|stoppe|beende|deaktivier|deaktiviere|schließe|schließen|zu|runter)\b&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
    my @commands = split(/\s*(?:und|dann|,)\s*/, lc($cleanEvent));;\&lt;br /&gt;
\&lt;br /&gt;
    # --- DER BEFEHLS-LOOP ---\&lt;br /&gt;
    foreach my $cmd_part (@commands) {\&lt;br /&gt;
        $cmd_part =~ s/^\s+|\s+$//g;;\&lt;br /&gt;
        my $is_on  = ($cmd_part =~ /$onRegEx/) ? 1 : 0;;\&lt;br /&gt;
        my $is_off = ($cmd_part =~ /$offRegEx/) ? 1 : 0;;\&lt;br /&gt;
        $cmd_part =~ s/\b(ich|brauche|mach|bitte|kannst du|würdest du|mal|doch|den|das|die|im|in der)\b//g;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- GENERISCHE STEUERUNG ---\&lt;br /&gt;
        my $match = 0;;\&lt;br /&gt;
        # Sortierung stellt sicher, dass &#039;dimmer/vacuum&#039; Vorrang haben\&lt;br /&gt;
        foreach my $key (sort { ($smartHomeDevices{$b}{type}//&amp;quot;&amp;quot;) cmp ($smartHomeDevices{$a}{type}//&amp;quot;&amp;quot;) } keys %smartHomeDevices) {\&lt;br /&gt;
            \&lt;br /&gt;
            my ($main, $must) = split(/:/, $key);;\&lt;br /&gt;
            \&lt;br /&gt;
            # Prüfe Hauptwort\&lt;br /&gt;
            if ($cmd_part =~ /\b($main)\b/i) {\&lt;br /&gt;
                # Prüfe Pflichtwort falls vorhanden\&lt;br /&gt;
                next if ($must &amp;amp;&amp;amp; $cmd_part !~ /\b($must)\b/i);;\&lt;br /&gt;
                \&lt;br /&gt;
                my $d = $smartHomeDevices{$key};;\&lt;br /&gt;
\&lt;br /&gt;
                # A) System\&lt;br /&gt;
                if (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;system&amp;quot;) {\&lt;br /&gt;
                    system($d-&amp;gt;{cmd});;\&lt;br /&gt;
                }\&lt;br /&gt;
                \&lt;br /&gt;
                # B) Staubsauger\&lt;br /&gt;
                if (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;vacuum&amp;quot;) {\&lt;br /&gt;
                    my @found = grep { $cmd_part =~ /\b$_\b/ } keys %vacRooms;;\&lt;br /&gt;
                    fhem(@found ? &amp;quot;set $d-&amp;gt;{dev} clean_segment &amp;quot;.join(&amp;quot;,&amp;quot;, map { $vacRooms{$_} } @found) : &amp;quot;set $d-&amp;gt;{dev} start&amp;quot;);;\&lt;br /&gt;
                }\&lt;br /&gt;
                # C) Dimmer (Kompakt &amp;amp; korrigiert)\&lt;br /&gt;
                elsif (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;dimmer&amp;quot;) {\&lt;br /&gt;
                    $cmd_part =~ /(\d+)/ \&lt;br /&gt;
                        ? fhem(&amp;quot;set $d-&amp;gt;{dev} brightness &amp;quot; . int(($1 &amp;gt; 100 ? 100 : $1) * 2.55)) \&lt;br /&gt;
                        : ($is_on || $is_off) &amp;amp;&amp;amp; fhem(&amp;quot;set $d-&amp;gt;{dev} &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;));;\&lt;br /&gt;
                }\&lt;br /&gt;
                # D) Standard An/Aus\&lt;br /&gt;
                else {\&lt;br /&gt;
                    my $fhem_cmd = $is_off ? ($d-&amp;gt;{cmdOff} // &amp;quot;off&amp;quot;) : ($d-&amp;gt;{cmdOn} // &amp;quot;on&amp;quot;);;\&lt;br /&gt;
                    fhem(&amp;quot;set $d-&amp;gt;{dev} $fhem_cmd&amp;quot;) if ($is_on || $is_off);;\&lt;br /&gt;
                }\&lt;br /&gt;
                \&lt;br /&gt;
                $match = 1;; last;;\&lt;br /&gt;
            }\&lt;br /&gt;
        }\&lt;br /&gt;
        next if $match;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- HILFE ---\&lt;br /&gt;
        if ($cmd_part =~ /(hilfe|kommandos|übersicht)/) {\&lt;br /&gt;
            my $h = &#039;&amp;lt;div style=&amp;quot;text-align:left;;min-width:250px;;font-family:sans-serif;;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Befehlsübersicht:&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            my %seen;;\&lt;br /&gt;
            for my $k (sort keys %smartHomeDevices) {\&lt;br /&gt;
                my $d = $smartHomeDevices{$k};;\&lt;br /&gt;
                next if $seen{$d-&amp;gt;{label}}++;; \&lt;br /&gt;
                my $suffix = ($d-&amp;gt;{cmdOn} &amp;amp;&amp;amp; $d-&amp;gt;{cmdOff} &amp;amp;&amp;amp; $d-&amp;gt;{label} !~ /(an\/aus|öffnen\/schließen)/i) ? &amp;quot; (an/aus)&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
                $suffix = &amp;quot; [0-100%]&amp;quot; if ($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;dimmer&amp;quot;;;\&lt;br /&gt;
                $h .= &amp;quot;• $d-&amp;gt;{label}$suffix&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            }\&lt;br /&gt;
            $h .= &#039;&amp;lt;br&amp;gt;&amp;lt;u&amp;gt;Staubsauger Räume&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;• &#039;.join(&amp;quot;, &amp;quot;, map { ucfirst($_) } sort keys %vacRooms).&#039;&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt;&#039;;;\&lt;br /&gt;
            $h =~ s/&#039;/\\&amp;quot;/g;;\&lt;br /&gt;
            my $js = &amp;quot;if((document.querySelector(&#039;input[name=\&amp;quot;fw_id\&amp;quot;]&#039;)||{}).value===&#039;$clientId&#039;){FW_okDialog(&#039;$h&#039;)}&amp;quot;;;\&lt;br /&gt;
            FW_directNotify(&amp;quot;#FHEMWEB:$_&amp;quot;, $js, &amp;quot;&amp;quot;) for devspec2array(&amp;quot;TYPE=FHEMWEB&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Unterschiede der Wege ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Feature&lt;br /&gt;
! Browser&lt;br /&gt;
! Hybrid (Atom Echo S3)&lt;br /&gt;
|-&lt;br /&gt;
| Wakeword&lt;br /&gt;
| Browser&lt;br /&gt;
| ESP (Hardware)&lt;br /&gt;
|-&lt;br /&gt;
| Push-to-Talk&lt;br /&gt;
| Ja&lt;br /&gt;
| Nein&lt;br /&gt;
|-&lt;br /&gt;
| Always-On&lt;br /&gt;
| Ja (bedingt)&lt;br /&gt;
| Ja&lt;br /&gt;
|-&lt;br /&gt;
| Architektur&lt;br /&gt;
| Software&lt;br /&gt;
| Software + Hardware&lt;br /&gt;
|-&lt;br /&gt;
| Stabilität&lt;br /&gt;
| Sehr stabil&lt;br /&gt;
| Sehr stabil (Wakeword extern)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== FireOS mit Fully Browser (Plus) ==&lt;br /&gt;
&lt;br /&gt;
Damit die Spracherkennung funktioniert:&lt;br /&gt;
&lt;br /&gt;
* Apps installieren:&lt;br /&gt;
   Google&lt;br /&gt;
   Speech Recognition &amp;amp; Synthesis&lt;br /&gt;
* FireOS:&lt;br /&gt;
   „Tastatur und Sprache“ → „Text-to-Speech“&lt;br /&gt;
* Fully Browser:&lt;br /&gt;
   &amp;lt;code&amp;gt;Enable JavaScriptInterface (PLUS)&amp;lt;/code&amp;gt; aktivieren&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40917</id>
		<title>FHEMWEB/VoiceControl: Web-STT &amp; Hardware-Wakeword</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40917"/>
		<updated>2026-04-03T16:59:09Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Voicecontrol.png|mini]]&lt;br /&gt;
&lt;br /&gt;
== VoiceControl – Sprachsteuerung via Browser und Atom Echo s3r ==&lt;br /&gt;
&lt;br /&gt;
Diese Lösung ermöglicht eine flexible Sprachsteuerung für [[FHEM]]. Sprache wird in Text (Speech-to-Text) umgewandelt und als Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; bereitgestellt. Dieses Reading kann anschließend zentral (z. B. über &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DOIF&amp;lt;/code&amp;gt;) ausgewertet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Unterstützt werden nur Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox hat leider kein Backend.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei unterschiedliche Wege zur Spracherfassung:&lt;br /&gt;
&lt;br /&gt;
* Weg 1️⃣ (Software): Browser-basierte Komplettlösung&lt;br /&gt;
* Weg 2️⃣ (Hybrid): Hardware-Wakeword + Browser-Spracherkennung&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Grundprinzip ====&lt;br /&gt;
* Sprache → Speech-to-Text&lt;br /&gt;
* Ergebnis → Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zentrale Logik verarbeitet Befehle&lt;br /&gt;
&lt;br /&gt;
==== Betriebsarten ====&lt;br /&gt;
* Push-to-Talk (nur Browser)&lt;br /&gt;
* Always-On mit Wakeword&lt;br /&gt;
* Hardware-Wakeword (Hybrid)&lt;br /&gt;
&lt;br /&gt;
==== Rückmeldungen ====&lt;br /&gt;
* Sprachausgabe (TTS)&lt;br /&gt;
* Visuelle Bubble im Browser&lt;br /&gt;
* Optional gezielte Rückmeldung per Client-ID&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 1️⃣: Browser-Lösung (voicecontrol.js) ==&lt;br /&gt;
&lt;br /&gt;
Das Script nutzt die Google Web Speech API. Unterstützt werden Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox wird aktuell nicht unterstützt.&lt;br /&gt;
&lt;br /&gt;
=== Bedienung ===&lt;br /&gt;
&lt;br /&gt;
==== Push-to-Talk ====&lt;br /&gt;
* Button gedrückt halten&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung ca.6sek Zeit für Befehl&lt;br /&gt;
&lt;br /&gt;
==== Always-On ====&lt;br /&gt;
* Kurzer Klick aktiviert Dauerbetrieb&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung ca.6sek Zeit für Befehl&lt;br /&gt;
* JS wird neu gestartet udn Schleife fängt von vorne an&lt;br /&gt;
&lt;br /&gt;
==== Wakeword ====&lt;br /&gt;
* Standard: &amp;lt;code&amp;gt;james&amp;lt;/code&amp;gt;&lt;br /&gt;
* Anpassbar im Script:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const wakewords = [&amp;quot;james&amp;quot;];&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{ Svn_GetFile(&#039;contrib/voicecontrol.js&#039;, &#039;www/pgm2/voicecontrol.js&#039;) }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBphone JavaScripts www/pgm2/voicecontrol.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Hinweis (HTTP ohne HTTPS) ====&lt;br /&gt;
Chrome benötigt Freigabe für Mikrofon:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;chrome://flags/#unsafely-treat-insecure-origin-as-secure&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eigene URL hinzufügen&lt;br /&gt;
* Auf &amp;lt;code&amp;gt;Enabled&amp;lt;/code&amp;gt; setzen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 2️⃣: Hybrid-Lösung mit Browser + Atom Echo (voicecontrol_echo.js) ==&lt;br /&gt;
&lt;br /&gt;
Diese Variante kombiniert Hardware und Software:&lt;br /&gt;
&lt;br /&gt;
* Wakeword-Erkennung erfolgt auf dem ESP (Atom Echo s3r)&lt;br /&gt;
* Die eigentliche Sprachverarbeitung (Speech-to-Text) erfolgt im Browser&lt;br /&gt;
&lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
&lt;br /&gt;
# Wakeword wird auf dem ESP erkannt&lt;br /&gt;
# FHEM erzeugt Event (z. B. &amp;lt;code&amp;gt;james_detected&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Browser empfängt Event via WebSocket&lt;br /&gt;
# Speech-to-Text startet im Browser&lt;br /&gt;
# Ergebnis wird an FHEM übertragen&lt;br /&gt;
&lt;br /&gt;
=== Aktivierung ===&lt;br /&gt;
&lt;br /&gt;
* Kurzer Klick auf Button aktiviert/deaktiviert System&lt;br /&gt;
* Baut WebSocket-Verbindung zu FHEM auf&lt;br /&gt;
* Kein Push-to-Talk-Modus&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&lt;br /&gt;
Die Datei voicecontrol_echo.js ist hier zu finden:&lt;br /&gt;
{{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
und muss nach www/pgm2/voicecontrol_echo.js kopiert werden.&lt;br /&gt;
&lt;br /&gt;
Die echo_s3r.yaml ist für den Atom Echo s3r. Die Konfuguration ist weiter unten beschrieben.&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBtablet JavaScripts www/pgm2/voicecontrol_echo.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration im Javascript ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const DEVICE  = &amp;quot;atom_echos3r_9888e00f4280&amp;quot;;&lt;br /&gt;
const TRIGGER = &amp;quot;james_detected&amp;quot;;&lt;br /&gt;
const FHEM_IP = &amp;quot;192.168.1.76:8085&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; → FHEM-Device des ESP&lt;br /&gt;
* &amp;lt;code&amp;gt;TRIGGER&amp;lt;/code&amp;gt; → Event bei Wakeword&lt;br /&gt;
* &amp;lt;code&amp;gt;FHEM_IP&amp;lt;/code&amp;gt; → FHEM-Server&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration in der Yaml ===&lt;br /&gt;
&lt;br /&gt;
Damit sich der ESP mit eurem MQTT-Server und WLAN verbindet, müssen folgende Stellen angepasst werden.&lt;br /&gt;
&lt;br /&gt;
   wifi:&lt;br /&gt;
     ssid: &amp;quot;YOUR_SSID&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     fast_connect: true&lt;br /&gt;
&lt;br /&gt;
   mqtt:&lt;br /&gt;
     broker: 192.168.1.76&lt;br /&gt;
     port: 1884&lt;br /&gt;
     username: &amp;quot;YOUR_USERNAME&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     topic_prefix: atom_echo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wakeword (ESP / ESPHome) ===&lt;br /&gt;
&lt;br /&gt;
Das Wakeword wird direkt auf dem ESP definiert:&lt;br /&gt;
&lt;br /&gt;
* Umsetzung über ESPHome&lt;br /&gt;
* Eine große Auswahl an Wakewords sind hier zu finden:&lt;br /&gt;
   https://github.com/TaterTotterson/microWakeWords&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel, wie das Wakeword definiert wird.&lt;br /&gt;
&lt;br /&gt;
   micro_wake_word:&lt;br /&gt;
     id: mww&lt;br /&gt;
     microphone: echo_mic&lt;br /&gt;
     models:&lt;br /&gt;
       - model: &amp;quot;https://github.com/TaterTotterson/microWakeWords/raw/main/microWakeWordsV2/james.json&amp;quot;&lt;br /&gt;
         id: james_model&lt;br /&gt;
         probability_cutoff: 0.6   &lt;br /&gt;
&lt;br /&gt;
=== Kommunikation ===&lt;br /&gt;
&lt;br /&gt;
* WebSocket-Verbindung zu FHEM&lt;br /&gt;
* Lauscht auf Device-Events&lt;br /&gt;
* Automatischer Reconnect bei Abbruch&lt;br /&gt;
&lt;br /&gt;
=== Ablauf nach Wakeword ===&lt;br /&gt;
&lt;br /&gt;
# System sagt „Ja?“&lt;br /&gt;
# Browser startet SpeechRecognition&lt;br /&gt;
# Nutzer spricht Befehl&lt;br /&gt;
# Befehl wird verarbeitet&lt;br /&gt;
# Rückmeldung „Erledigt“&lt;br /&gt;
&lt;br /&gt;
== Zentrale Auswertung (Logik) ==&lt;br /&gt;
&lt;br /&gt;
Beide Wege schreiben in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;global:STT&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung erfolgt zentral über ein &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt;. Ein Device wird in der Mappingtabelle eingetragen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;quot;esszimmer:licht|lampe|deckenlampe&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;, label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aufschlüsselung:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;quot;hauptkeyword:Filter1|Filter3|Filter3&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Devicename&amp;quot;, label =&amp;gt; &amp;quot;Übersichtname&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: notify ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod n_VoiceControl notify global:STT:.* {\&lt;br /&gt;
    # --- VORBEREITUNG ---\&lt;br /&gt;
    my ($cleanEvent, $clientId) = $EVENT =~ /^(.*)\s\[(.*)\]$/;;\&lt;br /&gt;
    $cleanEvent //= $EVENT;;\&lt;br /&gt;
    $clientId   //= &amp;quot;unknown&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # --- KONFIGURATION: Zentrales Mapping ---\&lt;br /&gt;
    my %smartHomeDevices = (\&lt;br /&gt;
        &amp;quot;esszimmer:licht|lampe|deckenlampe&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;, label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;esszimmer:aquarium&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Aquarium_Aktor&amp;quot;, label =&amp;gt; &amp;quot;Aquarium&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;küche&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Deckenlampe_Kue&amp;quot;, label =&amp;gt; &amp;quot;Licht Küche&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe06_Dek&amp;quot;, label =&amp;gt; &amp;quot;Licht Wohnzimmer&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;fernseher|tv&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;VuPlus&amp;quot;, label =&amp;gt; &amp;quot;Fernseher&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;rechner|pc&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;PC_Aktor&amp;quot;, label =&amp;gt; &amp;quot;PC&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;garage|tor&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Garagentor_Aktor&amp;quot;, label =&amp;gt; &amp;quot;Garagentor öffnen/schließen&amp;quot;, cmdOn =&amp;gt; &amp;quot;open&amp;quot;, cmdOff =&amp;gt; &amp;quot;close&amp;quot; },\&lt;br /&gt;
        &amp;quot;kaffee&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Kaffeemaschine&amp;quot;, label =&amp;gt; &amp;quot;Kaffeemaschine&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;roberto&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;MQTT2_valetudo_FlusteredUnequaledFish&amp;quot;, label =&amp;gt; &amp;quot;Lade Roberto&amp;quot;, cmdOn =&amp;gt; &amp;quot;charge&amp;quot; },\&lt;br /&gt;
        &amp;quot;ambiente&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;LampeSzeneAlle&amp;quot;, label =&amp;gt; &amp;quot;Zentrales Licht&amp;quot;, type =&amp;gt; &amp;quot;dimmer&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;sauge|reinige|putze|staubsauger|roboter&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;MQTT2_valetudo_FlusteredUnequaledFish&amp;quot;, label =&amp;gt; &amp;quot;Staubsauger&amp;quot;, type =&amp;gt; &amp;quot;vacuum&amp;quot; },\&lt;br /&gt;
         &amp;quot;ambilight&amp;quot; =&amp;gt; { label =&amp;gt; &amp;quot;Ambilight&amp;quot;, type =&amp;gt; &amp;quot;system&amp;quot;, cmd =&amp;gt; &amp;quot;sshpass -p &#039;GEHEIMESPASSWORD&#039; ssh -o StrictHostKeyChecking=no root\@192.168.1.46 &#039;/usr/share/hyperhdr/scripts/hyperhdr_toggle.sh&#039;&amp;quot; }\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    my %vacRooms = (&amp;quot;arbeitszimmer&amp;quot; =&amp;gt; &amp;quot;Arbeitszimmer&amp;quot;, &amp;quot;badezimmer&amp;quot; =&amp;gt; &amp;quot;Badezimmer&amp;quot;, &amp;quot;esszimmer&amp;quot; =&amp;gt; &amp;quot;Esszimmer&amp;quot;, &amp;quot;flur&amp;quot; =&amp;gt; &amp;quot;Flur&amp;quot;, &amp;quot;küche&amp;quot; =&amp;gt; &amp;quot;Küche&amp;quot;, &amp;quot;wohnzimmer&amp;quot; =&amp;gt; &amp;quot;Wohnzimmer&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $onRegEx  = &#039;\b(an|ein|einschalten|starte|aktivier|aktiviere|öffne|öffnen|auf|hoch|lade)\b&#039;;;\&lt;br /&gt;
    my $offRegEx = &#039;\b(aus|ausschalten|stop|stoppe|beende|deaktivier|deaktiviere|schließe|schließen|zu|runter)\b&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
    my @commands = split(/\s*(?:und|dann|,)\s*/, lc($cleanEvent));;\&lt;br /&gt;
\&lt;br /&gt;
    # --- DER BEFEHLS-LOOP ---\&lt;br /&gt;
    foreach my $cmd_part (@commands) {\&lt;br /&gt;
        $cmd_part =~ s/^\s+|\s+$//g;;\&lt;br /&gt;
        my $is_on  = ($cmd_part =~ /$onRegEx/) ? 1 : 0;;\&lt;br /&gt;
        my $is_off = ($cmd_part =~ /$offRegEx/) ? 1 : 0;;\&lt;br /&gt;
        $cmd_part =~ s/\b(ich|brauche|mach|bitte|kannst du|würdest du|mal|doch|den|das|die|im|in der)\b//g;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- GENERISCHE STEUERUNG ---\&lt;br /&gt;
        my $match = 0;;\&lt;br /&gt;
        # Sortierung stellt sicher, dass &#039;dimmer/vacuum&#039; Vorrang haben\&lt;br /&gt;
        foreach my $key (sort { ($smartHomeDevices{$b}{type}//&amp;quot;&amp;quot;) cmp ($smartHomeDevices{$a}{type}//&amp;quot;&amp;quot;) } keys %smartHomeDevices) {\&lt;br /&gt;
            \&lt;br /&gt;
            my ($main, $must) = split(/:/, $key);;\&lt;br /&gt;
            \&lt;br /&gt;
            # Prüfe Hauptwort\&lt;br /&gt;
            if ($cmd_part =~ /\b($main)\b/i) {\&lt;br /&gt;
                # Prüfe Pflichtwort falls vorhanden\&lt;br /&gt;
                next if ($must &amp;amp;&amp;amp; $cmd_part !~ /\b($must)\b/i);;\&lt;br /&gt;
                \&lt;br /&gt;
                my $d = $smartHomeDevices{$key};;\&lt;br /&gt;
\&lt;br /&gt;
                # A) System\&lt;br /&gt;
                if (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;system&amp;quot;) {\&lt;br /&gt;
                    system($d-&amp;gt;{cmd});;\&lt;br /&gt;
                }\&lt;br /&gt;
                \&lt;br /&gt;
                # B) Staubsauger\&lt;br /&gt;
                if (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;vacuum&amp;quot;) {\&lt;br /&gt;
                    my @found = grep { $cmd_part =~ /\b$_\b/ } keys %vacRooms;;\&lt;br /&gt;
                    fhem(@found ? &amp;quot;set $d-&amp;gt;{dev} clean_segment &amp;quot;.join(&amp;quot;,&amp;quot;, map { $vacRooms{$_} } @found) : &amp;quot;set $d-&amp;gt;{dev} start&amp;quot;);;\&lt;br /&gt;
                }\&lt;br /&gt;
                # C) Dimmer (Kompakt &amp;amp; korrigiert)\&lt;br /&gt;
                elsif (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;dimmer&amp;quot;) {\&lt;br /&gt;
                    $cmd_part =~ /(\d+)/ \&lt;br /&gt;
                        ? fhem(&amp;quot;set $d-&amp;gt;{dev} brightness &amp;quot; . int(($1 &amp;gt; 100 ? 100 : $1) * 2.55)) \&lt;br /&gt;
                        : ($is_on || $is_off) &amp;amp;&amp;amp; fhem(&amp;quot;set $d-&amp;gt;{dev} &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;));;\&lt;br /&gt;
                }\&lt;br /&gt;
                # D) Standard An/Aus\&lt;br /&gt;
                else {\&lt;br /&gt;
                    my $fhem_cmd = $is_off ? ($d-&amp;gt;{cmdOff} // &amp;quot;off&amp;quot;) : ($d-&amp;gt;{cmdOn} // &amp;quot;on&amp;quot;);;\&lt;br /&gt;
                    fhem(&amp;quot;set $d-&amp;gt;{dev} $fhem_cmd&amp;quot;) if ($is_on || $is_off);;\&lt;br /&gt;
                }\&lt;br /&gt;
                \&lt;br /&gt;
                $match = 1;; last;;\&lt;br /&gt;
            }\&lt;br /&gt;
        }\&lt;br /&gt;
        next if $match;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- HILFE ---\&lt;br /&gt;
        if ($cmd_part =~ /(hilfe|kommandos|übersicht)/) {\&lt;br /&gt;
            my $h = &#039;&amp;lt;div style=&amp;quot;text-align:left;;min-width:250px;;font-family:sans-serif;;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Befehlsübersicht:&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            my %seen;;\&lt;br /&gt;
            for my $k (sort keys %smartHomeDevices) {\&lt;br /&gt;
                my $d = $smartHomeDevices{$k};;\&lt;br /&gt;
                next if $seen{$d-&amp;gt;{label}}++;; \&lt;br /&gt;
                my $suffix = ($d-&amp;gt;{cmdOn} &amp;amp;&amp;amp; $d-&amp;gt;{cmdOff} &amp;amp;&amp;amp; $d-&amp;gt;{label} !~ /(an\/aus|öffnen\/schließen)/i) ? &amp;quot; (an/aus)&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
                $suffix = &amp;quot; [0-100%]&amp;quot; if ($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;dimmer&amp;quot;;;\&lt;br /&gt;
                $h .= &amp;quot;• $d-&amp;gt;{label}$suffix&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            }\&lt;br /&gt;
            $h .= &#039;&amp;lt;br&amp;gt;&amp;lt;u&amp;gt;Staubsauger Räume&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;• &#039;.join(&amp;quot;, &amp;quot;, map { ucfirst($_) } sort keys %vacRooms).&#039;&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt;&#039;;;\&lt;br /&gt;
            $h =~ s/&#039;/\\&amp;quot;/g;;\&lt;br /&gt;
            my $js = &amp;quot;if((document.querySelector(&#039;input[name=\&amp;quot;fw_id\&amp;quot;]&#039;)||{}).value===&#039;$clientId&#039;){FW_okDialog(&#039;$h&#039;)}&amp;quot;;;\&lt;br /&gt;
            FW_directNotify(&amp;quot;#FHEMWEB:$_&amp;quot;, $js, &amp;quot;&amp;quot;) for devspec2array(&amp;quot;TYPE=FHEMWEB&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Unterschiede der Wege ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Feature&lt;br /&gt;
! Browser&lt;br /&gt;
! Hybrid (Atom Echo S3)&lt;br /&gt;
|-&lt;br /&gt;
| Wakeword&lt;br /&gt;
| Browser&lt;br /&gt;
| ESP (Hardware)&lt;br /&gt;
|-&lt;br /&gt;
| Push-to-Talk&lt;br /&gt;
| Ja&lt;br /&gt;
| Nein&lt;br /&gt;
|-&lt;br /&gt;
| Always-On&lt;br /&gt;
| Ja (bedingt)&lt;br /&gt;
| Ja&lt;br /&gt;
|-&lt;br /&gt;
| Architektur&lt;br /&gt;
| Software&lt;br /&gt;
| Software + Hardware&lt;br /&gt;
|-&lt;br /&gt;
| Stabilität&lt;br /&gt;
| Sehr stabil&lt;br /&gt;
| Sehr stabil (Wakeword extern)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== FireOS mit Fully Browser (Plus) ==&lt;br /&gt;
&lt;br /&gt;
Damit die Spracherkennung funktioniert:&lt;br /&gt;
&lt;br /&gt;
* Apps installieren:&lt;br /&gt;
   Google&lt;br /&gt;
   Speech Recognition &amp;amp; Synthesis&lt;br /&gt;
* FireOS:&lt;br /&gt;
   „Tastatur und Sprache“ → „Text-to-Speech“&lt;br /&gt;
* Fully Browser:&lt;br /&gt;
   &amp;lt;code&amp;gt;Enable JavaScriptInterface (PLUS)&amp;lt;/code&amp;gt; aktivieren&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40916</id>
		<title>FHEMWEB/VoiceControl: Web-STT &amp; Hardware-Wakeword</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40916"/>
		<updated>2026-04-03T16:58:24Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Voicecontrol.png|mini]]&lt;br /&gt;
&lt;br /&gt;
== VoiceControl – Sprachsteuerung via Browser und Atom Echo s3r ==&lt;br /&gt;
&lt;br /&gt;
Diese Lösung ermöglicht eine flexible Sprachsteuerung für [[FHEM]]. Sprache wird in Text (Speech-to-Text) umgewandelt und als Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; bereitgestellt. Dieses Reading kann anschließend zentral (z. B. über &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DOIF&amp;lt;/code&amp;gt;) ausgewertet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Unterstützt werden nur Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox hat leider kein Backend.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei unterschiedliche Wege zur Spracherfassung:&lt;br /&gt;
&lt;br /&gt;
* Weg 1️⃣ (Software): Browser-basierte Komplettlösung&lt;br /&gt;
* Weg 2️⃣ (Hybrid): Hardware-Wakeword + Browser-Spracherkennung&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
Dort befinden sich in Post #1 auch alle benötigten Dateien.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Grundprinzip ====&lt;br /&gt;
* Sprache → Speech-to-Text&lt;br /&gt;
* Ergebnis → Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zentrale Logik verarbeitet Befehle&lt;br /&gt;
&lt;br /&gt;
==== Betriebsarten ====&lt;br /&gt;
* Push-to-Talk (nur Browser)&lt;br /&gt;
* Always-On mit Wakeword&lt;br /&gt;
* Hardware-Wakeword (Hybrid)&lt;br /&gt;
&lt;br /&gt;
==== Rückmeldungen ====&lt;br /&gt;
* Sprachausgabe (TTS)&lt;br /&gt;
* Visuelle Bubble im Browser&lt;br /&gt;
* Optional gezielte Rückmeldung per Client-ID&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 1️⃣: Browser-Lösung (voicecontrol.js) ==&lt;br /&gt;
&lt;br /&gt;
Das Script nutzt die Google Web Speech API. Unterstützt werden Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox wird aktuell nicht unterstützt.&lt;br /&gt;
&lt;br /&gt;
=== Bedienung ===&lt;br /&gt;
&lt;br /&gt;
==== Push-to-Talk ====&lt;br /&gt;
* Button gedrückt halten&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung ca.6sek Zeit für Befehl&lt;br /&gt;
&lt;br /&gt;
==== Always-On ====&lt;br /&gt;
* Kurzer Klick aktiviert Dauerbetrieb&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung ca.6sek Zeit für Befehl&lt;br /&gt;
* JS wird neu gestartet udn Schleife fängt von vorne an&lt;br /&gt;
&lt;br /&gt;
==== Wakeword ====&lt;br /&gt;
* Standard: &amp;lt;code&amp;gt;james&amp;lt;/code&amp;gt;&lt;br /&gt;
* Anpassbar im Script:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const wakewords = [&amp;quot;james&amp;quot;];&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{ Svn_GetFile(&#039;contrib/voicecontrol.js&#039;, &#039;www/pgm2/voicecontrol.js&#039;) }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBphone JavaScripts www/pgm2/voicecontrol.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Hinweis (HTTP ohne HTTPS) ====&lt;br /&gt;
Chrome benötigt Freigabe für Mikrofon:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;chrome://flags/#unsafely-treat-insecure-origin-as-secure&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eigene URL hinzufügen&lt;br /&gt;
* Auf &amp;lt;code&amp;gt;Enabled&amp;lt;/code&amp;gt; setzen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 2️⃣: Hybrid-Lösung mit Browser + Atom Echo (voicecontrol_echo.js) ==&lt;br /&gt;
&lt;br /&gt;
Diese Variante kombiniert Hardware und Software:&lt;br /&gt;
&lt;br /&gt;
* Wakeword-Erkennung erfolgt auf dem ESP (Atom Echo s3r)&lt;br /&gt;
* Die eigentliche Sprachverarbeitung (Speech-to-Text) erfolgt im Browser&lt;br /&gt;
&lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
&lt;br /&gt;
# Wakeword wird auf dem ESP erkannt&lt;br /&gt;
# FHEM erzeugt Event (z. B. &amp;lt;code&amp;gt;james_detected&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Browser empfängt Event via WebSocket&lt;br /&gt;
# Speech-to-Text startet im Browser&lt;br /&gt;
# Ergebnis wird an FHEM übertragen&lt;br /&gt;
&lt;br /&gt;
=== Aktivierung ===&lt;br /&gt;
&lt;br /&gt;
* Kurzer Klick auf Button aktiviert/deaktiviert System&lt;br /&gt;
* Baut WebSocket-Verbindung zu FHEM auf&lt;br /&gt;
* Kein Push-to-Talk-Modus&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&lt;br /&gt;
Die Datei voicecontrol_echo.js ist hier zu finden:&lt;br /&gt;
{{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
und muss nach www/pgm2/voicecontrol_echo.js kopiert werden.&lt;br /&gt;
&lt;br /&gt;
Die echo_s3r.yaml ist für den Atom Echo s3r. Die Konfuguration ist weiter unten beschrieben.&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBtablet JavaScripts www/pgm2/voicecontrol_echo.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration im Javascript ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const DEVICE  = &amp;quot;atom_echos3r_9888e00f4280&amp;quot;;&lt;br /&gt;
const TRIGGER = &amp;quot;james_detected&amp;quot;;&lt;br /&gt;
const FHEM_IP = &amp;quot;192.168.1.76:8085&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; → FHEM-Device des ESP&lt;br /&gt;
* &amp;lt;code&amp;gt;TRIGGER&amp;lt;/code&amp;gt; → Event bei Wakeword&lt;br /&gt;
* &amp;lt;code&amp;gt;FHEM_IP&amp;lt;/code&amp;gt; → FHEM-Server&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration in der Yaml ===&lt;br /&gt;
&lt;br /&gt;
Damit sich der ESP mit eurem MQTT-Server und WLAN verbindet, müssen folgende Stellen angepasst werden.&lt;br /&gt;
&lt;br /&gt;
   wifi:&lt;br /&gt;
     ssid: &amp;quot;YOUR_SSID&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     fast_connect: true&lt;br /&gt;
&lt;br /&gt;
   mqtt:&lt;br /&gt;
     broker: 192.168.1.76&lt;br /&gt;
     port: 1884&lt;br /&gt;
     username: &amp;quot;YOUR_USERNAME&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     topic_prefix: atom_echo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wakeword (ESP / ESPHome) ===&lt;br /&gt;
&lt;br /&gt;
Das Wakeword wird direkt auf dem ESP definiert:&lt;br /&gt;
&lt;br /&gt;
* Umsetzung über ESPHome&lt;br /&gt;
* Eine große Auswahl an Wakewords sind hier zu finden:&lt;br /&gt;
   https://github.com/TaterTotterson/microWakeWords&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel, wie das Wakeword definiert wird.&lt;br /&gt;
&lt;br /&gt;
   micro_wake_word:&lt;br /&gt;
     id: mww&lt;br /&gt;
     microphone: echo_mic&lt;br /&gt;
     models:&lt;br /&gt;
       - model: &amp;quot;https://github.com/TaterTotterson/microWakeWords/raw/main/microWakeWordsV2/james.json&amp;quot;&lt;br /&gt;
         id: james_model&lt;br /&gt;
         probability_cutoff: 0.6   &lt;br /&gt;
&lt;br /&gt;
=== Kommunikation ===&lt;br /&gt;
&lt;br /&gt;
* WebSocket-Verbindung zu FHEM&lt;br /&gt;
* Lauscht auf Device-Events&lt;br /&gt;
* Automatischer Reconnect bei Abbruch&lt;br /&gt;
&lt;br /&gt;
=== Ablauf nach Wakeword ===&lt;br /&gt;
&lt;br /&gt;
# System sagt „Ja?“&lt;br /&gt;
# Browser startet SpeechRecognition&lt;br /&gt;
# Nutzer spricht Befehl&lt;br /&gt;
# Befehl wird verarbeitet&lt;br /&gt;
# Rückmeldung „Erledigt“&lt;br /&gt;
&lt;br /&gt;
== Zentrale Auswertung (Logik) ==&lt;br /&gt;
&lt;br /&gt;
Beide Wege schreiben in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;global:STT&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung erfolgt zentral über ein &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt;. Ein Device wird in der Mappingtabelle eingetragen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;quot;esszimmer:licht|lampe|deckenlampe&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;, label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aufschlüsselung:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;quot;hauptkeyword:Filter1|Filter3|Filter3&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Devicename&amp;quot;, label =&amp;gt; &amp;quot;Übersichtname&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: notify ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod n_VoiceControl notify global:STT:.* {\&lt;br /&gt;
    # --- VORBEREITUNG ---\&lt;br /&gt;
    my ($cleanEvent, $clientId) = $EVENT =~ /^(.*)\s\[(.*)\]$/;;\&lt;br /&gt;
    $cleanEvent //= $EVENT;;\&lt;br /&gt;
    $clientId   //= &amp;quot;unknown&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # --- KONFIGURATION: Zentrales Mapping ---\&lt;br /&gt;
    my %smartHomeDevices = (\&lt;br /&gt;
        &amp;quot;esszimmer:licht|lampe|deckenlampe&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;, label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;esszimmer:aquarium&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Aquarium_Aktor&amp;quot;, label =&amp;gt; &amp;quot;Aquarium&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;küche&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Deckenlampe_Kue&amp;quot;, label =&amp;gt; &amp;quot;Licht Küche&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe06_Dek&amp;quot;, label =&amp;gt; &amp;quot;Licht Wohnzimmer&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;fernseher|tv&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;VuPlus&amp;quot;, label =&amp;gt; &amp;quot;Fernseher&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;rechner|pc&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;PC_Aktor&amp;quot;, label =&amp;gt; &amp;quot;PC&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;garage|tor&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Garagentor_Aktor&amp;quot;, label =&amp;gt; &amp;quot;Garagentor öffnen/schließen&amp;quot;, cmdOn =&amp;gt; &amp;quot;open&amp;quot;, cmdOff =&amp;gt; &amp;quot;close&amp;quot; },\&lt;br /&gt;
        &amp;quot;kaffee&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Kaffeemaschine&amp;quot;, label =&amp;gt; &amp;quot;Kaffeemaschine&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;roberto&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;MQTT2_valetudo_FlusteredUnequaledFish&amp;quot;, label =&amp;gt; &amp;quot;Lade Roberto&amp;quot;, cmdOn =&amp;gt; &amp;quot;charge&amp;quot; },\&lt;br /&gt;
        &amp;quot;ambiente&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;LampeSzeneAlle&amp;quot;, label =&amp;gt; &amp;quot;Zentrales Licht&amp;quot;, type =&amp;gt; &amp;quot;dimmer&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;sauge|reinige|putze|staubsauger|roboter&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;MQTT2_valetudo_FlusteredUnequaledFish&amp;quot;, label =&amp;gt; &amp;quot;Staubsauger&amp;quot;, type =&amp;gt; &amp;quot;vacuum&amp;quot; },\&lt;br /&gt;
         &amp;quot;ambilight&amp;quot; =&amp;gt; { label =&amp;gt; &amp;quot;Ambilight&amp;quot;, type =&amp;gt; &amp;quot;system&amp;quot;, cmd =&amp;gt; &amp;quot;sshpass -p &#039;GEHEIMESPASSWORD&#039; ssh -o StrictHostKeyChecking=no root\@192.168.1.46 &#039;/usr/share/hyperhdr/scripts/hyperhdr_toggle.sh&#039;&amp;quot; }\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    my %vacRooms = (&amp;quot;arbeitszimmer&amp;quot; =&amp;gt; &amp;quot;Arbeitszimmer&amp;quot;, &amp;quot;badezimmer&amp;quot; =&amp;gt; &amp;quot;Badezimmer&amp;quot;, &amp;quot;esszimmer&amp;quot; =&amp;gt; &amp;quot;Esszimmer&amp;quot;, &amp;quot;flur&amp;quot; =&amp;gt; &amp;quot;Flur&amp;quot;, &amp;quot;küche&amp;quot; =&amp;gt; &amp;quot;Küche&amp;quot;, &amp;quot;wohnzimmer&amp;quot; =&amp;gt; &amp;quot;Wohnzimmer&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $onRegEx  = &#039;\b(an|ein|einschalten|starte|aktivier|aktiviere|öffne|öffnen|auf|hoch|lade)\b&#039;;;\&lt;br /&gt;
    my $offRegEx = &#039;\b(aus|ausschalten|stop|stoppe|beende|deaktivier|deaktiviere|schließe|schließen|zu|runter)\b&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
    my @commands = split(/\s*(?:und|dann|,)\s*/, lc($cleanEvent));;\&lt;br /&gt;
\&lt;br /&gt;
    # --- DER BEFEHLS-LOOP ---\&lt;br /&gt;
    foreach my $cmd_part (@commands) {\&lt;br /&gt;
        $cmd_part =~ s/^\s+|\s+$//g;;\&lt;br /&gt;
        my $is_on  = ($cmd_part =~ /$onRegEx/) ? 1 : 0;;\&lt;br /&gt;
        my $is_off = ($cmd_part =~ /$offRegEx/) ? 1 : 0;;\&lt;br /&gt;
        $cmd_part =~ s/\b(ich|brauche|mach|bitte|kannst du|würdest du|mal|doch|den|das|die|im|in der)\b//g;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- GENERISCHE STEUERUNG ---\&lt;br /&gt;
        my $match = 0;;\&lt;br /&gt;
        # Sortierung stellt sicher, dass &#039;dimmer/vacuum&#039; Vorrang haben\&lt;br /&gt;
        foreach my $key (sort { ($smartHomeDevices{$b}{type}//&amp;quot;&amp;quot;) cmp ($smartHomeDevices{$a}{type}//&amp;quot;&amp;quot;) } keys %smartHomeDevices) {\&lt;br /&gt;
            \&lt;br /&gt;
            my ($main, $must) = split(/:/, $key);;\&lt;br /&gt;
            \&lt;br /&gt;
            # Prüfe Hauptwort\&lt;br /&gt;
            if ($cmd_part =~ /\b($main)\b/i) {\&lt;br /&gt;
                # Prüfe Pflichtwort falls vorhanden\&lt;br /&gt;
                next if ($must &amp;amp;&amp;amp; $cmd_part !~ /\b($must)\b/i);;\&lt;br /&gt;
                \&lt;br /&gt;
                my $d = $smartHomeDevices{$key};;\&lt;br /&gt;
\&lt;br /&gt;
                # A) System\&lt;br /&gt;
                if (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;system&amp;quot;) {\&lt;br /&gt;
                    system($d-&amp;gt;{cmd});;\&lt;br /&gt;
                }\&lt;br /&gt;
                \&lt;br /&gt;
                # B) Staubsauger\&lt;br /&gt;
                if (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;vacuum&amp;quot;) {\&lt;br /&gt;
                    my @found = grep { $cmd_part =~ /\b$_\b/ } keys %vacRooms;;\&lt;br /&gt;
                    fhem(@found ? &amp;quot;set $d-&amp;gt;{dev} clean_segment &amp;quot;.join(&amp;quot;,&amp;quot;, map { $vacRooms{$_} } @found) : &amp;quot;set $d-&amp;gt;{dev} start&amp;quot;);;\&lt;br /&gt;
                }\&lt;br /&gt;
                # C) Dimmer (Kompakt &amp;amp; korrigiert)\&lt;br /&gt;
                elsif (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;dimmer&amp;quot;) {\&lt;br /&gt;
                    $cmd_part =~ /(\d+)/ \&lt;br /&gt;
                        ? fhem(&amp;quot;set $d-&amp;gt;{dev} brightness &amp;quot; . int(($1 &amp;gt; 100 ? 100 : $1) * 2.55)) \&lt;br /&gt;
                        : ($is_on || $is_off) &amp;amp;&amp;amp; fhem(&amp;quot;set $d-&amp;gt;{dev} &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;));;\&lt;br /&gt;
                }\&lt;br /&gt;
                # D) Standard An/Aus\&lt;br /&gt;
                else {\&lt;br /&gt;
                    my $fhem_cmd = $is_off ? ($d-&amp;gt;{cmdOff} // &amp;quot;off&amp;quot;) : ($d-&amp;gt;{cmdOn} // &amp;quot;on&amp;quot;);;\&lt;br /&gt;
                    fhem(&amp;quot;set $d-&amp;gt;{dev} $fhem_cmd&amp;quot;) if ($is_on || $is_off);;\&lt;br /&gt;
                }\&lt;br /&gt;
                \&lt;br /&gt;
                $match = 1;; last;;\&lt;br /&gt;
            }\&lt;br /&gt;
        }\&lt;br /&gt;
        next if $match;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- HILFE ---\&lt;br /&gt;
        if ($cmd_part =~ /(hilfe|kommandos|übersicht)/) {\&lt;br /&gt;
            my $h = &#039;&amp;lt;div style=&amp;quot;text-align:left;;min-width:250px;;font-family:sans-serif;;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Befehlsübersicht:&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            my %seen;;\&lt;br /&gt;
            for my $k (sort keys %smartHomeDevices) {\&lt;br /&gt;
                my $d = $smartHomeDevices{$k};;\&lt;br /&gt;
                next if $seen{$d-&amp;gt;{label}}++;; \&lt;br /&gt;
                my $suffix = ($d-&amp;gt;{cmdOn} &amp;amp;&amp;amp; $d-&amp;gt;{cmdOff} &amp;amp;&amp;amp; $d-&amp;gt;{label} !~ /(an\/aus|öffnen\/schließen)/i) ? &amp;quot; (an/aus)&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
                $suffix = &amp;quot; [0-100%]&amp;quot; if ($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;dimmer&amp;quot;;;\&lt;br /&gt;
                $h .= &amp;quot;• $d-&amp;gt;{label}$suffix&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            }\&lt;br /&gt;
            $h .= &#039;&amp;lt;br&amp;gt;&amp;lt;u&amp;gt;Staubsauger Räume&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;• &#039;.join(&amp;quot;, &amp;quot;, map { ucfirst($_) } sort keys %vacRooms).&#039;&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt;&#039;;;\&lt;br /&gt;
            $h =~ s/&#039;/\\&amp;quot;/g;;\&lt;br /&gt;
            my $js = &amp;quot;if((document.querySelector(&#039;input[name=\&amp;quot;fw_id\&amp;quot;]&#039;)||{}).value===&#039;$clientId&#039;){FW_okDialog(&#039;$h&#039;)}&amp;quot;;;\&lt;br /&gt;
            FW_directNotify(&amp;quot;#FHEMWEB:$_&amp;quot;, $js, &amp;quot;&amp;quot;) for devspec2array(&amp;quot;TYPE=FHEMWEB&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Unterschiede der Wege ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Feature&lt;br /&gt;
! Browser&lt;br /&gt;
! Hybrid (Atom Echo S3)&lt;br /&gt;
|-&lt;br /&gt;
| Wakeword&lt;br /&gt;
| Browser&lt;br /&gt;
| ESP (Hardware)&lt;br /&gt;
|-&lt;br /&gt;
| Push-to-Talk&lt;br /&gt;
| Ja&lt;br /&gt;
| Nein&lt;br /&gt;
|-&lt;br /&gt;
| Always-On&lt;br /&gt;
| Ja (bedingt)&lt;br /&gt;
| Ja&lt;br /&gt;
|-&lt;br /&gt;
| Architektur&lt;br /&gt;
| Software&lt;br /&gt;
| Software + Hardware&lt;br /&gt;
|-&lt;br /&gt;
| Stabilität&lt;br /&gt;
| Sehr stabil&lt;br /&gt;
| Sehr stabil (Wakeword extern)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== FireOS mit Fully Browser (Plus) ==&lt;br /&gt;
&lt;br /&gt;
Damit die Spracherkennung funktioniert:&lt;br /&gt;
&lt;br /&gt;
* Apps installieren:&lt;br /&gt;
   Google&lt;br /&gt;
   Speech Recognition &amp;amp; Synthesis&lt;br /&gt;
* FireOS:&lt;br /&gt;
   „Tastatur und Sprache“ → „Text-to-Speech“&lt;br /&gt;
* Fully Browser:&lt;br /&gt;
   &amp;lt;code&amp;gt;Enable JavaScriptInterface (PLUS)&amp;lt;/code&amp;gt; aktivieren&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40915</id>
		<title>FHEMWEB/VoiceControl: Web-STT &amp; Hardware-Wakeword</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40915"/>
		<updated>2026-04-03T16:55:59Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Voicecontrol.png|mini]]&lt;br /&gt;
&lt;br /&gt;
== VoiceControl – Sprachsteuerung via Browser und Atom Echo s3r ==&lt;br /&gt;
&lt;br /&gt;
Diese Lösung ermöglicht eine flexible Sprachsteuerung für [[FHEM]]. Sprache wird in Text (Speech-to-Text) umgewandelt und als Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; bereitgestellt. Dieses Reading kann anschließend zentral (z. B. über &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DOIF&amp;lt;/code&amp;gt;) ausgewertet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Unterstützt werden nur Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox hat leider kein Backend.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei unterschiedliche Wege zur Spracherfassung:&lt;br /&gt;
&lt;br /&gt;
* Weg 1️⃣ (Software): Browser-basierte Komplettlösung&lt;br /&gt;
* Weg 2️⃣ (Hybrid): Hardware-Wakeword + Browser-Spracherkennung&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
Dort befinden sich in Post #1 auch alle benötigten Dateien.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Grundprinzip ====&lt;br /&gt;
* Sprache → Speech-to-Text&lt;br /&gt;
* Ergebnis → Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zentrale Logik verarbeitet Befehle&lt;br /&gt;
&lt;br /&gt;
==== Betriebsarten ====&lt;br /&gt;
* Push-to-Talk (nur Browser)&lt;br /&gt;
* Always-On mit Wakeword&lt;br /&gt;
* Hardware-Wakeword (Hybrid)&lt;br /&gt;
&lt;br /&gt;
==== Rückmeldungen ====&lt;br /&gt;
* Sprachausgabe (TTS)&lt;br /&gt;
* Visuelle Bubble im Browser&lt;br /&gt;
* Optional gezielte Rückmeldung per Client-ID&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 1️⃣: Browser-Lösung (voicecontrol.js) ==&lt;br /&gt;
&lt;br /&gt;
Das Script nutzt die Google Web Speech API. Unterstützt werden Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox wird aktuell nicht unterstützt.&lt;br /&gt;
&lt;br /&gt;
=== Bedienung ===&lt;br /&gt;
&lt;br /&gt;
==== Push-to-Talk ====&lt;br /&gt;
* Button gedrückt halten&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung ca.6sek Zeit für Befehl&lt;br /&gt;
&lt;br /&gt;
==== Always-On ====&lt;br /&gt;
* Kurzer Klick aktiviert Dauerbetrieb&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung ca.6sek Zeit für Befehl&lt;br /&gt;
* JS wird neu gestartet udn Schleife fängt von vorne an&lt;br /&gt;
&lt;br /&gt;
==== Wakeword ====&lt;br /&gt;
* Standard: &amp;lt;code&amp;gt;james&amp;lt;/code&amp;gt;&lt;br /&gt;
* Anpassbar im Script:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const wakewords = [&amp;quot;james&amp;quot;];&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ablauf:&lt;br /&gt;
# „James“ sagen&lt;br /&gt;
# System antwortet „Ja?“&lt;br /&gt;
# Zeitfenster (~6 Sekunden) für Befehl&lt;br /&gt;
# Danach automatische Verarbeitung oder Abbruch&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{ Svn_GetFile(&#039;contrib/voicecontrol.js&#039;, &#039;www/pgm2/voicecontrol.js&#039;) }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBphone JavaScripts www/pgm2/voicecontrol.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Hinweis (HTTP ohne HTTPS) ====&lt;br /&gt;
Chrome benötigt Freigabe für Mikrofon:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;chrome://flags/#unsafely-treat-insecure-origin-as-secure&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eigene URL hinzufügen&lt;br /&gt;
* Auf &amp;lt;code&amp;gt;Enabled&amp;lt;/code&amp;gt; setzen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 2️⃣: Hybrid-Lösung mit Browser + Atom Echo (voicecontrol_echo.js) ==&lt;br /&gt;
&lt;br /&gt;
Diese Variante kombiniert Hardware und Software:&lt;br /&gt;
&lt;br /&gt;
* Wakeword-Erkennung erfolgt auf dem ESP (Atom Echo s3r)&lt;br /&gt;
* Die eigentliche Sprachverarbeitung (Speech-to-Text) erfolgt im Browser&lt;br /&gt;
&lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
&lt;br /&gt;
# Wakeword wird auf dem ESP erkannt&lt;br /&gt;
# FHEM erzeugt Event (z. B. &amp;lt;code&amp;gt;james_detected&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Browser empfängt Event via WebSocket&lt;br /&gt;
# Speech-to-Text startet im Browser&lt;br /&gt;
# Ergebnis wird an FHEM übertragen&lt;br /&gt;
&lt;br /&gt;
=== Aktivierung ===&lt;br /&gt;
&lt;br /&gt;
* Kurzer Klick auf Button aktiviert/deaktiviert System&lt;br /&gt;
* Baut WebSocket-Verbindung zu FHEM auf&lt;br /&gt;
* Kein Push-to-Talk-Modus&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&lt;br /&gt;
Die Datei voicecontrol_echo.js ist hier zu finden:&lt;br /&gt;
{{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
und muss nach www/pgm2/voicecontrol_echo.js kopiert werden.&lt;br /&gt;
&lt;br /&gt;
Die echo_s3r.yaml ist für den Atom Echo s3r. Die Konfuguration ist weiter unten beschrieben.&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBtablet JavaScripts www/pgm2/voicecontrol_echo.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration im Javascript ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const DEVICE  = &amp;quot;atom_echos3r_9888e00f4280&amp;quot;;&lt;br /&gt;
const TRIGGER = &amp;quot;james_detected&amp;quot;;&lt;br /&gt;
const FHEM_IP = &amp;quot;192.168.1.76:8085&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; → FHEM-Device des ESP&lt;br /&gt;
* &amp;lt;code&amp;gt;TRIGGER&amp;lt;/code&amp;gt; → Event bei Wakeword&lt;br /&gt;
* &amp;lt;code&amp;gt;FHEM_IP&amp;lt;/code&amp;gt; → FHEM-Server&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration in der Yaml ===&lt;br /&gt;
&lt;br /&gt;
Damit sich der ESP mit eurem MQTT-Server und WLAN verbindet, müssen folgende Stellen angepasst werden.&lt;br /&gt;
&lt;br /&gt;
   wifi:&lt;br /&gt;
     ssid: &amp;quot;YOUR_SSID&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     fast_connect: true&lt;br /&gt;
&lt;br /&gt;
   mqtt:&lt;br /&gt;
     broker: 192.168.1.76&lt;br /&gt;
     port: 1884&lt;br /&gt;
     username: &amp;quot;YOUR_USERNAME&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     topic_prefix: atom_echo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wakeword (ESP / ESPHome) ===&lt;br /&gt;
&lt;br /&gt;
Das Wakeword wird direkt auf dem ESP definiert:&lt;br /&gt;
&lt;br /&gt;
* Umsetzung über ESPHome&lt;br /&gt;
* Eine große Auswahl an Wakewords sind hier zu finden:&lt;br /&gt;
   https://github.com/TaterTotterson/microWakeWords&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel, wie das Wakeword definiert wird.&lt;br /&gt;
&lt;br /&gt;
   micro_wake_word:&lt;br /&gt;
     id: mww&lt;br /&gt;
     microphone: echo_mic&lt;br /&gt;
     models:&lt;br /&gt;
       - model: &amp;quot;https://github.com/TaterTotterson/microWakeWords/raw/main/microWakeWordsV2/james.json&amp;quot;&lt;br /&gt;
         id: james_model&lt;br /&gt;
         probability_cutoff: 0.6   &lt;br /&gt;
&lt;br /&gt;
=== Kommunikation ===&lt;br /&gt;
&lt;br /&gt;
* WebSocket-Verbindung zu FHEM&lt;br /&gt;
* Lauscht auf Device-Events&lt;br /&gt;
* Automatischer Reconnect bei Abbruch&lt;br /&gt;
&lt;br /&gt;
=== Ablauf nach Wakeword ===&lt;br /&gt;
&lt;br /&gt;
# System sagt „Ja?“&lt;br /&gt;
# Browser startet SpeechRecognition&lt;br /&gt;
# Nutzer spricht Befehl&lt;br /&gt;
# Befehl wird verarbeitet&lt;br /&gt;
# Rückmeldung „Erledigt“&lt;br /&gt;
&lt;br /&gt;
== Zentrale Auswertung (Logik) ==&lt;br /&gt;
&lt;br /&gt;
Beide Wege schreiben in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;global:STT&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung erfolgt zentral über ein &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt;. Ein Device wird in der Mappingtabelle eingetragen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;quot;esszimmer:licht|lampe|deckenlampe&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;, label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aufschlüsselung:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;quot;hauptkeyword:Filter1|Filter3|Filter3&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Devicename&amp;quot;, label =&amp;gt; &amp;quot;Übersichtname&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: notify ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod n_VoiceControl notify global:STT:.* {\&lt;br /&gt;
    # --- VORBEREITUNG ---\&lt;br /&gt;
    my ($cleanEvent, $clientId) = $EVENT =~ /^(.*)\s\[(.*)\]$/;;\&lt;br /&gt;
    $cleanEvent //= $EVENT;;\&lt;br /&gt;
    $clientId   //= &amp;quot;unknown&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # --- KONFIGURATION: Zentrales Mapping ---\&lt;br /&gt;
    my %smartHomeDevices = (\&lt;br /&gt;
        &amp;quot;esszimmer:licht|lampe|deckenlampe&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;, label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;esszimmer:aquarium&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Aquarium_Aktor&amp;quot;, label =&amp;gt; &amp;quot;Aquarium&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;küche&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Deckenlampe_Kue&amp;quot;, label =&amp;gt; &amp;quot;Licht Küche&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe06_Dek&amp;quot;, label =&amp;gt; &amp;quot;Licht Wohnzimmer&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;fernseher|tv&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;VuPlus&amp;quot;, label =&amp;gt; &amp;quot;Fernseher&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;rechner|pc&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;PC_Aktor&amp;quot;, label =&amp;gt; &amp;quot;PC&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;garage|tor&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Garagentor_Aktor&amp;quot;, label =&amp;gt; &amp;quot;Garagentor öffnen/schließen&amp;quot;, cmdOn =&amp;gt; &amp;quot;open&amp;quot;, cmdOff =&amp;gt; &amp;quot;close&amp;quot; },\&lt;br /&gt;
        &amp;quot;kaffee&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Kaffeemaschine&amp;quot;, label =&amp;gt; &amp;quot;Kaffeemaschine&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;roberto&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;MQTT2_valetudo_FlusteredUnequaledFish&amp;quot;, label =&amp;gt; &amp;quot;Lade Roberto&amp;quot;, cmdOn =&amp;gt; &amp;quot;charge&amp;quot; },\&lt;br /&gt;
        &amp;quot;ambiente&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;LampeSzeneAlle&amp;quot;, label =&amp;gt; &amp;quot;Zentrales Licht&amp;quot;, type =&amp;gt; &amp;quot;dimmer&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;sauge|reinige|putze|staubsauger|roboter&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;MQTT2_valetudo_FlusteredUnequaledFish&amp;quot;, label =&amp;gt; &amp;quot;Staubsauger&amp;quot;, type =&amp;gt; &amp;quot;vacuum&amp;quot; },\&lt;br /&gt;
         &amp;quot;ambilight&amp;quot; =&amp;gt; { label =&amp;gt; &amp;quot;Ambilight&amp;quot;, type =&amp;gt; &amp;quot;system&amp;quot;, cmd =&amp;gt; &amp;quot;sshpass -p &#039;GEHEIMESPASSWORD&#039; ssh -o StrictHostKeyChecking=no root\@192.168.1.46 &#039;/usr/share/hyperhdr/scripts/hyperhdr_toggle.sh&#039;&amp;quot; }\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    my %vacRooms = (&amp;quot;arbeitszimmer&amp;quot; =&amp;gt; &amp;quot;Arbeitszimmer&amp;quot;, &amp;quot;badezimmer&amp;quot; =&amp;gt; &amp;quot;Badezimmer&amp;quot;, &amp;quot;esszimmer&amp;quot; =&amp;gt; &amp;quot;Esszimmer&amp;quot;, &amp;quot;flur&amp;quot; =&amp;gt; &amp;quot;Flur&amp;quot;, &amp;quot;küche&amp;quot; =&amp;gt; &amp;quot;Küche&amp;quot;, &amp;quot;wohnzimmer&amp;quot; =&amp;gt; &amp;quot;Wohnzimmer&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $onRegEx  = &#039;\b(an|ein|einschalten|starte|aktivier|aktiviere|öffne|öffnen|auf|hoch|lade)\b&#039;;;\&lt;br /&gt;
    my $offRegEx = &#039;\b(aus|ausschalten|stop|stoppe|beende|deaktivier|deaktiviere|schließe|schließen|zu|runter)\b&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
    my @commands = split(/\s*(?:und|dann|,)\s*/, lc($cleanEvent));;\&lt;br /&gt;
\&lt;br /&gt;
    # --- DER BEFEHLS-LOOP ---\&lt;br /&gt;
    foreach my $cmd_part (@commands) {\&lt;br /&gt;
        $cmd_part =~ s/^\s+|\s+$//g;;\&lt;br /&gt;
        my $is_on  = ($cmd_part =~ /$onRegEx/) ? 1 : 0;;\&lt;br /&gt;
        my $is_off = ($cmd_part =~ /$offRegEx/) ? 1 : 0;;\&lt;br /&gt;
        $cmd_part =~ s/\b(ich|brauche|mach|bitte|kannst du|würdest du|mal|doch|den|das|die|im|in der)\b//g;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- GENERISCHE STEUERUNG ---\&lt;br /&gt;
        my $match = 0;;\&lt;br /&gt;
        # Sortierung stellt sicher, dass &#039;dimmer/vacuum&#039; Vorrang haben\&lt;br /&gt;
        foreach my $key (sort { ($smartHomeDevices{$b}{type}//&amp;quot;&amp;quot;) cmp ($smartHomeDevices{$a}{type}//&amp;quot;&amp;quot;) } keys %smartHomeDevices) {\&lt;br /&gt;
            \&lt;br /&gt;
            my ($main, $must) = split(/:/, $key);;\&lt;br /&gt;
            \&lt;br /&gt;
            # Prüfe Hauptwort\&lt;br /&gt;
            if ($cmd_part =~ /\b($main)\b/i) {\&lt;br /&gt;
                # Prüfe Pflichtwort falls vorhanden\&lt;br /&gt;
                next if ($must &amp;amp;&amp;amp; $cmd_part !~ /\b($must)\b/i);;\&lt;br /&gt;
                \&lt;br /&gt;
                my $d = $smartHomeDevices{$key};;\&lt;br /&gt;
\&lt;br /&gt;
                # A) System\&lt;br /&gt;
                if (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;system&amp;quot;) {\&lt;br /&gt;
                    system($d-&amp;gt;{cmd});;\&lt;br /&gt;
                }\&lt;br /&gt;
                \&lt;br /&gt;
                # B) Staubsauger\&lt;br /&gt;
                if (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;vacuum&amp;quot;) {\&lt;br /&gt;
                    my @found = grep { $cmd_part =~ /\b$_\b/ } keys %vacRooms;;\&lt;br /&gt;
                    fhem(@found ? &amp;quot;set $d-&amp;gt;{dev} clean_segment &amp;quot;.join(&amp;quot;,&amp;quot;, map { $vacRooms{$_} } @found) : &amp;quot;set $d-&amp;gt;{dev} start&amp;quot;);;\&lt;br /&gt;
                }\&lt;br /&gt;
                # C) Dimmer (Kompakt &amp;amp; korrigiert)\&lt;br /&gt;
                elsif (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;dimmer&amp;quot;) {\&lt;br /&gt;
                    $cmd_part =~ /(\d+)/ \&lt;br /&gt;
                        ? fhem(&amp;quot;set $d-&amp;gt;{dev} brightness &amp;quot; . int(($1 &amp;gt; 100 ? 100 : $1) * 2.55)) \&lt;br /&gt;
                        : ($is_on || $is_off) &amp;amp;&amp;amp; fhem(&amp;quot;set $d-&amp;gt;{dev} &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;));;\&lt;br /&gt;
                }\&lt;br /&gt;
                # D) Standard An/Aus\&lt;br /&gt;
                else {\&lt;br /&gt;
                    my $fhem_cmd = $is_off ? ($d-&amp;gt;{cmdOff} // &amp;quot;off&amp;quot;) : ($d-&amp;gt;{cmdOn} // &amp;quot;on&amp;quot;);;\&lt;br /&gt;
                    fhem(&amp;quot;set $d-&amp;gt;{dev} $fhem_cmd&amp;quot;) if ($is_on || $is_off);;\&lt;br /&gt;
                }\&lt;br /&gt;
                \&lt;br /&gt;
                $match = 1;; last;;\&lt;br /&gt;
            }\&lt;br /&gt;
        }\&lt;br /&gt;
        next if $match;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- HILFE ---\&lt;br /&gt;
        if ($cmd_part =~ /(hilfe|kommandos|übersicht)/) {\&lt;br /&gt;
            my $h = &#039;&amp;lt;div style=&amp;quot;text-align:left;;min-width:250px;;font-family:sans-serif;;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Befehlsübersicht:&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            my %seen;;\&lt;br /&gt;
            for my $k (sort keys %smartHomeDevices) {\&lt;br /&gt;
                my $d = $smartHomeDevices{$k};;\&lt;br /&gt;
                next if $seen{$d-&amp;gt;{label}}++;; \&lt;br /&gt;
                my $suffix = ($d-&amp;gt;{cmdOn} &amp;amp;&amp;amp; $d-&amp;gt;{cmdOff} &amp;amp;&amp;amp; $d-&amp;gt;{label} !~ /(an\/aus|öffnen\/schließen)/i) ? &amp;quot; (an/aus)&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
                $suffix = &amp;quot; [0-100%]&amp;quot; if ($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;dimmer&amp;quot;;;\&lt;br /&gt;
                $h .= &amp;quot;• $d-&amp;gt;{label}$suffix&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            }\&lt;br /&gt;
            $h .= &#039;&amp;lt;br&amp;gt;&amp;lt;u&amp;gt;Staubsauger Räume&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;• &#039;.join(&amp;quot;, &amp;quot;, map { ucfirst($_) } sort keys %vacRooms).&#039;&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt;&#039;;;\&lt;br /&gt;
            $h =~ s/&#039;/\\&amp;quot;/g;;\&lt;br /&gt;
            my $js = &amp;quot;if((document.querySelector(&#039;input[name=\&amp;quot;fw_id\&amp;quot;]&#039;)||{}).value===&#039;$clientId&#039;){FW_okDialog(&#039;$h&#039;)}&amp;quot;;;\&lt;br /&gt;
            FW_directNotify(&amp;quot;#FHEMWEB:$_&amp;quot;, $js, &amp;quot;&amp;quot;) for devspec2array(&amp;quot;TYPE=FHEMWEB&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Unterschiede der Wege ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Feature&lt;br /&gt;
! Browser&lt;br /&gt;
! Hybrid (Atom Echo S3)&lt;br /&gt;
|-&lt;br /&gt;
| Wakeword&lt;br /&gt;
| Browser&lt;br /&gt;
| ESP (Hardware)&lt;br /&gt;
|-&lt;br /&gt;
| Push-to-Talk&lt;br /&gt;
| Ja&lt;br /&gt;
| Nein&lt;br /&gt;
|-&lt;br /&gt;
| Always-On&lt;br /&gt;
| Ja (bedingt)&lt;br /&gt;
| Ja&lt;br /&gt;
|-&lt;br /&gt;
| Architektur&lt;br /&gt;
| Software&lt;br /&gt;
| Software + Hardware&lt;br /&gt;
|-&lt;br /&gt;
| Stabilität&lt;br /&gt;
| Sehr stabil&lt;br /&gt;
| Sehr stabil (Wakeword extern)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== FireOS mit Fully Browser (Plus) ==&lt;br /&gt;
&lt;br /&gt;
Damit die Spracherkennung funktioniert:&lt;br /&gt;
&lt;br /&gt;
* Apps installieren:&lt;br /&gt;
   Google&lt;br /&gt;
   Speech Recognition &amp;amp; Synthesis&lt;br /&gt;
* FireOS:&lt;br /&gt;
   „Tastatur und Sprache“ → „Text-to-Speech“&lt;br /&gt;
* Fully Browser:&lt;br /&gt;
   &amp;lt;code&amp;gt;Enable JavaScriptInterface (PLUS)&amp;lt;/code&amp;gt; aktivieren&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40914</id>
		<title>FHEMWEB/VoiceControl: Web-STT &amp; Hardware-Wakeword</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40914"/>
		<updated>2026-04-03T16:28:02Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Voicecontrol.png|mini]]&lt;br /&gt;
&lt;br /&gt;
== VoiceControl – Sprachsteuerung via Browser und Atom Echo s3r ==&lt;br /&gt;
&lt;br /&gt;
Diese Lösung ermöglicht eine flexible Sprachsteuerung für [[FHEM]]. Sprache wird in Text (Speech-to-Text) umgewandelt und als Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; bereitgestellt. Dieses Reading kann anschließend zentral (z. B. über &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DOIF&amp;lt;/code&amp;gt;) ausgewertet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Unterstützt werden nur Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox hat leider kein Backend.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei unterschiedliche Wege zur Spracherfassung:&lt;br /&gt;
&lt;br /&gt;
* Weg 1️⃣ (Software): Browser-basierte Komplettlösung&lt;br /&gt;
* Weg 2️⃣ (Hybrid): Hardware-Wakeword + Browser-Spracherkennung&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
Dort befinden sich in Post #1 auch alle benötigten Dateien.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Grundprinzip ====&lt;br /&gt;
* Sprache → Speech-to-Text&lt;br /&gt;
* Ergebnis → Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zentrale Logik verarbeitet Befehle&lt;br /&gt;
&lt;br /&gt;
==== Betriebsarten ====&lt;br /&gt;
* Push-to-Talk (nur Browser)&lt;br /&gt;
* Always-On mit Wakeword&lt;br /&gt;
* Hardware-Wakeword (Hybrid)&lt;br /&gt;
&lt;br /&gt;
==== Rückmeldungen ====&lt;br /&gt;
* Sprachausgabe (TTS)&lt;br /&gt;
* Visuelle Bubble im Browser&lt;br /&gt;
* Optional gezielte Rückmeldung per Client-ID&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 1️⃣: Browser-Lösung (voicecontrol.js) ==&lt;br /&gt;
&lt;br /&gt;
Das Script nutzt die Google Web Speech API. Unterstützt werden Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox wird aktuell nicht unterstützt.&lt;br /&gt;
&lt;br /&gt;
=== Bedienung ===&lt;br /&gt;
&lt;br /&gt;
==== Push-to-Talk ====&lt;br /&gt;
* Button gedrückt halten (~450 ms)&lt;br /&gt;
* Direkt sprechen (kein Wakeword nötig)&lt;br /&gt;
* Befehl wird sofort verarbeitet&lt;br /&gt;
&lt;br /&gt;
==== Always-On ====&lt;br /&gt;
* Kurzer Klick aktiviert Dauerbetrieb&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung permanentes Mithören&lt;br /&gt;
&lt;br /&gt;
==== Wakeword ====&lt;br /&gt;
* Standard: &amp;lt;code&amp;gt;james&amp;lt;/code&amp;gt;&lt;br /&gt;
* Anpassbar im Script:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const wakewords = [&amp;quot;james&amp;quot;];&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ablauf:&lt;br /&gt;
# „James“ sagen&lt;br /&gt;
# System antwortet „Ja?“&lt;br /&gt;
# Zeitfenster (~6 Sekunden) für Befehl&lt;br /&gt;
# Danach automatische Verarbeitung oder Abbruch&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{ Svn_GetFile(&#039;contrib/voicecontrol.js&#039;, &#039;www/pgm2/voicecontrol.js&#039;) }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBphone JavaScripts www/pgm2/voicecontrol.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Hinweis (HTTP ohne HTTPS) ====&lt;br /&gt;
Chrome benötigt Freigabe für Mikrofon:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;chrome://flags/#unsafely-treat-insecure-origin-as-secure&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eigene URL hinzufügen&lt;br /&gt;
* Auf &amp;lt;code&amp;gt;Enabled&amp;lt;/code&amp;gt; setzen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 2️⃣: Hybrid-Lösung mit Browser + Atom Echo (voicecontrol_echo.js) ==&lt;br /&gt;
&lt;br /&gt;
Diese Variante kombiniert Hardware und Software:&lt;br /&gt;
&lt;br /&gt;
* Wakeword-Erkennung erfolgt auf dem ESP (Atom Echo s3r)&lt;br /&gt;
* Die eigentliche Sprachverarbeitung (Speech-to-Text) erfolgt im Browser&lt;br /&gt;
&lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
&lt;br /&gt;
# Wakeword wird auf dem ESP erkannt&lt;br /&gt;
# FHEM erzeugt Event (z. B. &amp;lt;code&amp;gt;james_detected&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Browser empfängt Event via WebSocket&lt;br /&gt;
# Speech-to-Text startet im Browser&lt;br /&gt;
# Ergebnis wird an FHEM übertragen&lt;br /&gt;
&lt;br /&gt;
=== Aktivierung ===&lt;br /&gt;
&lt;br /&gt;
* Kurzer Klick auf Button aktiviert/deaktiviert System&lt;br /&gt;
* Baut WebSocket-Verbindung zu FHEM auf&lt;br /&gt;
* Kein Push-to-Talk-Modus&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&lt;br /&gt;
Die Datei voicecontrol_echo.js ist hier zu finden:&lt;br /&gt;
{{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
und muss nach www/pgm2/voicecontrol_echo.js kopiert werden.&lt;br /&gt;
&lt;br /&gt;
Die echo_s3r.yaml ist für den Atom Echo s3r. Die Konfuguration ist weiter unten beschrieben.&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBtablet JavaScripts www/pgm2/voicecontrol_echo.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration im Javascript ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const DEVICE  = &amp;quot;atom_echos3r_9888e00f4280&amp;quot;;&lt;br /&gt;
const TRIGGER = &amp;quot;james_detected&amp;quot;;&lt;br /&gt;
const FHEM_IP = &amp;quot;192.168.1.76:8085&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; → FHEM-Device des ESP&lt;br /&gt;
* &amp;lt;code&amp;gt;TRIGGER&amp;lt;/code&amp;gt; → Event bei Wakeword&lt;br /&gt;
* &amp;lt;code&amp;gt;FHEM_IP&amp;lt;/code&amp;gt; → FHEM-Server&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration in der Yaml ===&lt;br /&gt;
&lt;br /&gt;
Damit sich der ESP mit eurem MQTT-Server und WLAN verbindet, müssen folgende Stellen angepasst werden.&lt;br /&gt;
&lt;br /&gt;
   wifi:&lt;br /&gt;
     ssid: &amp;quot;YOUR_SSID&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     fast_connect: true&lt;br /&gt;
&lt;br /&gt;
   mqtt:&lt;br /&gt;
     broker: 192.168.1.76&lt;br /&gt;
     port: 1884&lt;br /&gt;
     username: &amp;quot;YOUR_USERNAME&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     topic_prefix: atom_echo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wakeword (ESP / ESPHome) ===&lt;br /&gt;
&lt;br /&gt;
Das Wakeword wird direkt auf dem ESP definiert:&lt;br /&gt;
&lt;br /&gt;
* Umsetzung über ESPHome&lt;br /&gt;
* Eine große Auswahl an Wakewords sind hier zu finden:&lt;br /&gt;
   https://github.com/TaterTotterson/microWakeWords&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel, wie das Wakeword definiert wird.&lt;br /&gt;
&lt;br /&gt;
   micro_wake_word:&lt;br /&gt;
     id: mww&lt;br /&gt;
     microphone: echo_mic&lt;br /&gt;
     models:&lt;br /&gt;
       - model: &amp;quot;https://github.com/TaterTotterson/microWakeWords/raw/main/microWakeWordsV2/james.json&amp;quot;&lt;br /&gt;
         id: james_model&lt;br /&gt;
         probability_cutoff: 0.6   &lt;br /&gt;
&lt;br /&gt;
=== Kommunikation ===&lt;br /&gt;
&lt;br /&gt;
* WebSocket-Verbindung zu FHEM&lt;br /&gt;
* Lauscht auf Device-Events&lt;br /&gt;
* Automatischer Reconnect bei Abbruch&lt;br /&gt;
&lt;br /&gt;
=== Ablauf nach Wakeword ===&lt;br /&gt;
&lt;br /&gt;
# System sagt „Ja?“&lt;br /&gt;
# Browser startet SpeechRecognition&lt;br /&gt;
# Nutzer spricht Befehl&lt;br /&gt;
# Befehl wird verarbeitet&lt;br /&gt;
# Rückmeldung „Erledigt“&lt;br /&gt;
&lt;br /&gt;
== Zentrale Auswertung (Logik) ==&lt;br /&gt;
&lt;br /&gt;
Beide Wege schreiben in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;global:STT&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung erfolgt zentral über ein &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt;. Ein Device wird in der Mappingtabelle eingetragen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;quot;esszimmer:licht|lampe|deckenlampe&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;, label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aufschlüsselung:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;quot;hauptkeyword:Filter1|Filter3|Filter3&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Devicename&amp;quot;, label =&amp;gt; &amp;quot;Übersichtname&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: notify ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod n_VoiceControl notify global:STT:.* {\&lt;br /&gt;
    # --- VORBEREITUNG ---\&lt;br /&gt;
    my ($cleanEvent, $clientId) = $EVENT =~ /^(.*)\s\[(.*)\]$/;;\&lt;br /&gt;
    $cleanEvent //= $EVENT;;\&lt;br /&gt;
    $clientId   //= &amp;quot;unknown&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # --- KONFIGURATION: Zentrales Mapping ---\&lt;br /&gt;
    my %smartHomeDevices = (\&lt;br /&gt;
        &amp;quot;esszimmer:licht|lampe|deckenlampe&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;, label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;esszimmer:aquarium&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Aquarium_Aktor&amp;quot;, label =&amp;gt; &amp;quot;Aquarium&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;küche&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Deckenlampe_Kue&amp;quot;, label =&amp;gt; &amp;quot;Licht Küche&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe06_Dek&amp;quot;, label =&amp;gt; &amp;quot;Licht Wohnzimmer&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;fernseher|tv&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;VuPlus&amp;quot;, label =&amp;gt; &amp;quot;Fernseher&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;rechner|pc&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;PC_Aktor&amp;quot;, label =&amp;gt; &amp;quot;PC&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;garage|tor&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Garagentor_Aktor&amp;quot;, label =&amp;gt; &amp;quot;Garagentor öffnen/schließen&amp;quot;, cmdOn =&amp;gt; &amp;quot;open&amp;quot;, cmdOff =&amp;gt; &amp;quot;close&amp;quot; },\&lt;br /&gt;
        &amp;quot;kaffee&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Kaffeemaschine&amp;quot;, label =&amp;gt; &amp;quot;Kaffeemaschine&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;roberto&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;MQTT2_valetudo_FlusteredUnequaledFish&amp;quot;, label =&amp;gt; &amp;quot;Lade Roberto&amp;quot;, cmdOn =&amp;gt; &amp;quot;charge&amp;quot; },\&lt;br /&gt;
        &amp;quot;ambiente&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;LampeSzeneAlle&amp;quot;, label =&amp;gt; &amp;quot;Zentrales Licht&amp;quot;, type =&amp;gt; &amp;quot;dimmer&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;sauge|reinige|putze|staubsauger|roboter&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;MQTT2_valetudo_FlusteredUnequaledFish&amp;quot;, label =&amp;gt; &amp;quot;Staubsauger&amp;quot;, type =&amp;gt; &amp;quot;vacuum&amp;quot; },\&lt;br /&gt;
         &amp;quot;ambilight&amp;quot; =&amp;gt; { label =&amp;gt; &amp;quot;Ambilight&amp;quot;, type =&amp;gt; &amp;quot;system&amp;quot;, cmd =&amp;gt; &amp;quot;sshpass -p &#039;GEHEIMESPASSWORD&#039; ssh -o StrictHostKeyChecking=no root\@192.168.1.46 &#039;/usr/share/hyperhdr/scripts/hyperhdr_toggle.sh&#039;&amp;quot; }\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    my %vacRooms = (&amp;quot;arbeitszimmer&amp;quot; =&amp;gt; &amp;quot;Arbeitszimmer&amp;quot;, &amp;quot;badezimmer&amp;quot; =&amp;gt; &amp;quot;Badezimmer&amp;quot;, &amp;quot;esszimmer&amp;quot; =&amp;gt; &amp;quot;Esszimmer&amp;quot;, &amp;quot;flur&amp;quot; =&amp;gt; &amp;quot;Flur&amp;quot;, &amp;quot;küche&amp;quot; =&amp;gt; &amp;quot;Küche&amp;quot;, &amp;quot;wohnzimmer&amp;quot; =&amp;gt; &amp;quot;Wohnzimmer&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $onRegEx  = &#039;\b(an|ein|einschalten|starte|aktivier|aktiviere|öffne|öffnen|auf|hoch|lade)\b&#039;;;\&lt;br /&gt;
    my $offRegEx = &#039;\b(aus|ausschalten|stop|stoppe|beende|deaktivier|deaktiviere|schließe|schließen|zu|runter)\b&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
    my @commands = split(/\s*(?:und|dann|,)\s*/, lc($cleanEvent));;\&lt;br /&gt;
\&lt;br /&gt;
    # --- DER BEFEHLS-LOOP ---\&lt;br /&gt;
    foreach my $cmd_part (@commands) {\&lt;br /&gt;
        $cmd_part =~ s/^\s+|\s+$//g;;\&lt;br /&gt;
        my $is_on  = ($cmd_part =~ /$onRegEx/) ? 1 : 0;;\&lt;br /&gt;
        my $is_off = ($cmd_part =~ /$offRegEx/) ? 1 : 0;;\&lt;br /&gt;
        $cmd_part =~ s/\b(ich|brauche|mach|bitte|kannst du|würdest du|mal|doch|den|das|die|im|in der)\b//g;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- GENERISCHE STEUERUNG ---\&lt;br /&gt;
        my $match = 0;;\&lt;br /&gt;
        # Sortierung stellt sicher, dass &#039;dimmer/vacuum&#039; Vorrang haben\&lt;br /&gt;
        foreach my $key (sort { ($smartHomeDevices{$b}{type}//&amp;quot;&amp;quot;) cmp ($smartHomeDevices{$a}{type}//&amp;quot;&amp;quot;) } keys %smartHomeDevices) {\&lt;br /&gt;
            \&lt;br /&gt;
            my ($main, $must) = split(/:/, $key);;\&lt;br /&gt;
            \&lt;br /&gt;
            # Prüfe Hauptwort\&lt;br /&gt;
            if ($cmd_part =~ /\b($main)\b/i) {\&lt;br /&gt;
                # Prüfe Pflichtwort falls vorhanden\&lt;br /&gt;
                next if ($must &amp;amp;&amp;amp; $cmd_part !~ /\b($must)\b/i);;\&lt;br /&gt;
                \&lt;br /&gt;
                my $d = $smartHomeDevices{$key};;\&lt;br /&gt;
\&lt;br /&gt;
                # A) System\&lt;br /&gt;
                if (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;system&amp;quot;) {\&lt;br /&gt;
                    system($d-&amp;gt;{cmd});;\&lt;br /&gt;
                }\&lt;br /&gt;
                \&lt;br /&gt;
                # B) Staubsauger\&lt;br /&gt;
                if (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;vacuum&amp;quot;) {\&lt;br /&gt;
                    my @found = grep { $cmd_part =~ /\b$_\b/ } keys %vacRooms;;\&lt;br /&gt;
                    fhem(@found ? &amp;quot;set $d-&amp;gt;{dev} clean_segment &amp;quot;.join(&amp;quot;,&amp;quot;, map { $vacRooms{$_} } @found) : &amp;quot;set $d-&amp;gt;{dev} start&amp;quot;);;\&lt;br /&gt;
                }\&lt;br /&gt;
                # C) Dimmer (Kompakt &amp;amp; korrigiert)\&lt;br /&gt;
                elsif (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;dimmer&amp;quot;) {\&lt;br /&gt;
                    $cmd_part =~ /(\d+)/ \&lt;br /&gt;
                        ? fhem(&amp;quot;set $d-&amp;gt;{dev} brightness &amp;quot; . int(($1 &amp;gt; 100 ? 100 : $1) * 2.55)) \&lt;br /&gt;
                        : ($is_on || $is_off) &amp;amp;&amp;amp; fhem(&amp;quot;set $d-&amp;gt;{dev} &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;));;\&lt;br /&gt;
                }\&lt;br /&gt;
                # D) Standard An/Aus\&lt;br /&gt;
                else {\&lt;br /&gt;
                    my $fhem_cmd = $is_off ? ($d-&amp;gt;{cmdOff} // &amp;quot;off&amp;quot;) : ($d-&amp;gt;{cmdOn} // &amp;quot;on&amp;quot;);;\&lt;br /&gt;
                    fhem(&amp;quot;set $d-&amp;gt;{dev} $fhem_cmd&amp;quot;) if ($is_on || $is_off);;\&lt;br /&gt;
                }\&lt;br /&gt;
                \&lt;br /&gt;
                $match = 1;; last;;\&lt;br /&gt;
            }\&lt;br /&gt;
        }\&lt;br /&gt;
        next if $match;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- HILFE ---\&lt;br /&gt;
        if ($cmd_part =~ /(hilfe|kommandos|übersicht)/) {\&lt;br /&gt;
            my $h = &#039;&amp;lt;div style=&amp;quot;text-align:left;;min-width:250px;;font-family:sans-serif;;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Befehlsübersicht:&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            my %seen;;\&lt;br /&gt;
            for my $k (sort keys %smartHomeDevices) {\&lt;br /&gt;
                my $d = $smartHomeDevices{$k};;\&lt;br /&gt;
                next if $seen{$d-&amp;gt;{label}}++;; \&lt;br /&gt;
                my $suffix = ($d-&amp;gt;{cmdOn} &amp;amp;&amp;amp; $d-&amp;gt;{cmdOff} &amp;amp;&amp;amp; $d-&amp;gt;{label} !~ /(an\/aus|öffnen\/schließen)/i) ? &amp;quot; (an/aus)&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
                $suffix = &amp;quot; [0-100%]&amp;quot; if ($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;dimmer&amp;quot;;;\&lt;br /&gt;
                $h .= &amp;quot;• $d-&amp;gt;{label}$suffix&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            }\&lt;br /&gt;
            $h .= &#039;&amp;lt;br&amp;gt;&amp;lt;u&amp;gt;Staubsauger Räume&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;• &#039;.join(&amp;quot;, &amp;quot;, map { ucfirst($_) } sort keys %vacRooms).&#039;&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt;&#039;;;\&lt;br /&gt;
            $h =~ s/&#039;/\\&amp;quot;/g;;\&lt;br /&gt;
            my $js = &amp;quot;if((document.querySelector(&#039;input[name=\&amp;quot;fw_id\&amp;quot;]&#039;)||{}).value===&#039;$clientId&#039;){FW_okDialog(&#039;$h&#039;)}&amp;quot;;;\&lt;br /&gt;
            FW_directNotify(&amp;quot;#FHEMWEB:$_&amp;quot;, $js, &amp;quot;&amp;quot;) for devspec2array(&amp;quot;TYPE=FHEMWEB&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Unterschiede der Wege ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Feature&lt;br /&gt;
! Browser&lt;br /&gt;
! Hybrid (Atom Echo S3)&lt;br /&gt;
|-&lt;br /&gt;
| Wakeword&lt;br /&gt;
| Browser&lt;br /&gt;
| ESP (Hardware)&lt;br /&gt;
|-&lt;br /&gt;
| Push-to-Talk&lt;br /&gt;
| Ja&lt;br /&gt;
| Nein&lt;br /&gt;
|-&lt;br /&gt;
| Always-On&lt;br /&gt;
| Ja (bedingt)&lt;br /&gt;
| Ja&lt;br /&gt;
|-&lt;br /&gt;
| Architektur&lt;br /&gt;
| Software&lt;br /&gt;
| Software + Hardware&lt;br /&gt;
|-&lt;br /&gt;
| Stabilität&lt;br /&gt;
| Sehr stabil&lt;br /&gt;
| Sehr stabil (Wakeword extern)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== FireOS mit Fully Browser (Plus) ==&lt;br /&gt;
&lt;br /&gt;
Damit die Spracherkennung funktioniert:&lt;br /&gt;
&lt;br /&gt;
* Apps installieren:&lt;br /&gt;
   Google&lt;br /&gt;
   Speech Recognition &amp;amp; Synthesis&lt;br /&gt;
* FireOS:&lt;br /&gt;
   „Tastatur und Sprache“ → „Text-to-Speech“&lt;br /&gt;
* Fully Browser:&lt;br /&gt;
   &amp;lt;code&amp;gt;Enable JavaScriptInterface (PLUS)&amp;lt;/code&amp;gt; aktivieren&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40913</id>
		<title>FHEMWEB/VoiceControl: Web-STT &amp; Hardware-Wakeword</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40913"/>
		<updated>2026-04-03T16:17:23Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Voicecontrol.png|mini]]&lt;br /&gt;
&lt;br /&gt;
== VoiceControl – Sprachsteuerung via Browser und Atom Echo s3r ==&lt;br /&gt;
&lt;br /&gt;
Diese Lösung ermöglicht eine flexible Sprachsteuerung für [[FHEM]]. Sprache wird in Text (Speech-to-Text) umgewandelt und als Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; bereitgestellt. Dieses Reading kann anschließend zentral (z. B. über &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DOIF&amp;lt;/code&amp;gt;) ausgewertet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Unterstützt werden nur Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox hat leider kein Backend.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei unterschiedliche Wege zur Spracherfassung:&lt;br /&gt;
&lt;br /&gt;
* Weg 1️⃣ (Software): Browser-basierte Komplettlösung&lt;br /&gt;
* Weg 2️⃣ (Hybrid): Hardware-Wakeword + Browser-Spracherkennung&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
Dort befinden sich in Post #1 auch alle benötigten Dateien.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Grundprinzip ====&lt;br /&gt;
* Sprache → Speech-to-Text&lt;br /&gt;
* Ergebnis → Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zentrale Logik verarbeitet Befehle&lt;br /&gt;
&lt;br /&gt;
==== Betriebsarten ====&lt;br /&gt;
* Push-to-Talk (nur Browser)&lt;br /&gt;
* Always-On mit Wakeword&lt;br /&gt;
* Hardware-Wakeword (Hybrid)&lt;br /&gt;
&lt;br /&gt;
==== Rückmeldungen ====&lt;br /&gt;
* Sprachausgabe (TTS)&lt;br /&gt;
* Visuelle Bubble im Browser&lt;br /&gt;
* Optional gezielte Rückmeldung per Client-ID&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 1️⃣: Browser-Lösung (voicecontrol.js) ==&lt;br /&gt;
&lt;br /&gt;
Das Script nutzt die Google Web Speech API. Unterstützt werden Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox wird aktuell nicht unterstützt.&lt;br /&gt;
&lt;br /&gt;
=== Bedienung ===&lt;br /&gt;
&lt;br /&gt;
==== Push-to-Talk ====&lt;br /&gt;
* Button gedrückt halten (~450 ms)&lt;br /&gt;
* Direkt sprechen (kein Wakeword nötig)&lt;br /&gt;
* Befehl wird sofort verarbeitet&lt;br /&gt;
&lt;br /&gt;
==== Always-On ====&lt;br /&gt;
* Kurzer Klick aktiviert Dauerbetrieb&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung permanentes Mithören&lt;br /&gt;
&lt;br /&gt;
==== Wakeword ====&lt;br /&gt;
* Standard: &amp;lt;code&amp;gt;james&amp;lt;/code&amp;gt;&lt;br /&gt;
* Anpassbar im Script:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const wakewords = [&amp;quot;james&amp;quot;];&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ablauf:&lt;br /&gt;
# „James“ sagen&lt;br /&gt;
# System antwortet „Ja?“&lt;br /&gt;
# Zeitfenster (~6 Sekunden) für Befehl&lt;br /&gt;
# Danach automatische Verarbeitung oder Abbruch&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{ Svn_GetFile(&#039;contrib/voicecontrol.js&#039;, &#039;www/pgm2/voicecontrol.js&#039;) }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBphone JavaScripts www/pgm2/voicecontrol.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Hinweis (HTTP ohne HTTPS) ====&lt;br /&gt;
Chrome benötigt Freigabe für Mikrofon:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;chrome://flags/#unsafely-treat-insecure-origin-as-secure&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eigene URL hinzufügen&lt;br /&gt;
* Auf &amp;lt;code&amp;gt;Enabled&amp;lt;/code&amp;gt; setzen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 2️⃣: Hybrid-Lösung mit Browser + Atom Echo (voicecontrol_echo.js) ==&lt;br /&gt;
&lt;br /&gt;
Diese Variante kombiniert Hardware und Software:&lt;br /&gt;
&lt;br /&gt;
* Wakeword-Erkennung erfolgt auf dem ESP (Atom Echo s3r)&lt;br /&gt;
* Die eigentliche Sprachverarbeitung (Speech-to-Text) erfolgt im Browser&lt;br /&gt;
&lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
&lt;br /&gt;
# Wakeword wird auf dem ESP erkannt&lt;br /&gt;
# FHEM erzeugt Event (z. B. &amp;lt;code&amp;gt;james_detected&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Browser empfängt Event via WebSocket&lt;br /&gt;
# Speech-to-Text startet im Browser&lt;br /&gt;
# Ergebnis wird an FHEM übertragen&lt;br /&gt;
&lt;br /&gt;
=== Aktivierung ===&lt;br /&gt;
&lt;br /&gt;
* Kurzer Klick auf Button aktiviert/deaktiviert System&lt;br /&gt;
* Baut WebSocket-Verbindung zu FHEM auf&lt;br /&gt;
* Kein Push-to-Talk-Modus&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&lt;br /&gt;
Die Datei voicecontrol_echo.js ist hier zu finden:&lt;br /&gt;
{{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
und muss nach www/pgm2/voicecontrol_echo.js kopiert werden.&lt;br /&gt;
&lt;br /&gt;
Die echo_s3r.yaml ist für den Atom Echo s3r. Die Konfuguration ist weiter unten beschrieben.&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBtablet JavaScripts www/pgm2/voicecontrol_echo.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration im Javascript ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const DEVICE  = &amp;quot;atom_echos3r_9888e00f4280&amp;quot;;&lt;br /&gt;
const TRIGGER = &amp;quot;james_detected&amp;quot;;&lt;br /&gt;
const FHEM_IP = &amp;quot;192.168.1.76:8085&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; → FHEM-Device des ESP&lt;br /&gt;
* &amp;lt;code&amp;gt;TRIGGER&amp;lt;/code&amp;gt; → Event bei Wakeword&lt;br /&gt;
* &amp;lt;code&amp;gt;FHEM_IP&amp;lt;/code&amp;gt; → FHEM-Server&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration in der Yaml ===&lt;br /&gt;
&lt;br /&gt;
Damit sich der ESP mit eurem MQTT-Server und WLAN verbindet, müssen folgende Stellen angepasst werden.&lt;br /&gt;
&lt;br /&gt;
   wifi:&lt;br /&gt;
     ssid: &amp;quot;YOUR_SSID&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     fast_connect: true&lt;br /&gt;
&lt;br /&gt;
   mqtt:&lt;br /&gt;
     broker: 192.168.1.76&lt;br /&gt;
     port: 1884&lt;br /&gt;
     username: &amp;quot;YOUR_USERNAME&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     topic_prefix: atom_echo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wakeword (ESP / ESPHome) ===&lt;br /&gt;
&lt;br /&gt;
Das Wakeword wird direkt auf dem ESP definiert:&lt;br /&gt;
&lt;br /&gt;
* Umsetzung über ESPHome&lt;br /&gt;
* Eine große Auswahl an Wakewords sind hier zu finden:&lt;br /&gt;
   https://github.com/TaterTotterson/microWakeWords&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel, wie das Wakeword definiert wird.&lt;br /&gt;
&lt;br /&gt;
   micro_wake_word:&lt;br /&gt;
     id: mww&lt;br /&gt;
     microphone: echo_mic&lt;br /&gt;
     models:&lt;br /&gt;
       - model: &amp;quot;https://github.com/TaterTotterson/microWakeWords/raw/main/microWakeWordsV2/james.json&amp;quot;&lt;br /&gt;
         id: james_model&lt;br /&gt;
         probability_cutoff: 0.6   &lt;br /&gt;
&lt;br /&gt;
=== Kommunikation ===&lt;br /&gt;
&lt;br /&gt;
* WebSocket-Verbindung zu FHEM&lt;br /&gt;
* Lauscht auf Device-Events&lt;br /&gt;
* Automatischer Reconnect bei Abbruch&lt;br /&gt;
&lt;br /&gt;
=== Ablauf nach Wakeword ===&lt;br /&gt;
&lt;br /&gt;
# System sagt „Ja?“&lt;br /&gt;
# Browser startet SpeechRecognition&lt;br /&gt;
# Nutzer spricht Befehl&lt;br /&gt;
# Befehl wird verarbeitet&lt;br /&gt;
# Rückmeldung „Erledigt“&lt;br /&gt;
&lt;br /&gt;
== Zentrale Auswertung (Logik) ==&lt;br /&gt;
&lt;br /&gt;
Beide Wege schreiben in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;global:STT&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung erfolgt zentral über ein &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt;. Ein Device wird in der Mappingtabelle eingetragen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;quot;esszimmer:licht|lampe|deckenlampe&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;, label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aufschlüsselung:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;quot;hauptkeyword:Filter1|Filter3|Filter3&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Devicename&amp;quot;, label =&amp;gt; &amp;quot;Übersichtname&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: notify ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod n_VoiceControl notify global:STT:.* {\&lt;br /&gt;
    # --- VORBEREITUNG ---\&lt;br /&gt;
    my ($cleanEvent, $clientId) = $EVENT =~ /^(.*)\s\[(.*)\]$/;;\&lt;br /&gt;
    $cleanEvent //= $EVENT;;\&lt;br /&gt;
    $clientId   //= &amp;quot;unknown&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # --- KONFIGURATION: Zentrales Mapping ---\&lt;br /&gt;
    my %smartHomeDevices = (\&lt;br /&gt;
        &amp;quot;esszimmer:licht|lampe|deckenlampe&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;, label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;esszimmer:aquarium&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Aquarium_Aktor&amp;quot;, label =&amp;gt; &amp;quot;Aquarium&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;küche&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Deckenlampe_Kue&amp;quot;, label =&amp;gt; &amp;quot;Licht Küche&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe06_Dek&amp;quot;, label =&amp;gt; &amp;quot;Licht Wohnzimmer&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;fernseher|tv&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;VuPlus&amp;quot;, label =&amp;gt; &amp;quot;Fernseher&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;rechner|pc&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;PC_Aktor&amp;quot;, label =&amp;gt; &amp;quot;PC&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;garage|tor&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Garagentor_Aktor&amp;quot;, label =&amp;gt; &amp;quot;Garagentor öffnen/schließen&amp;quot;, cmdOn =&amp;gt; &amp;quot;open&amp;quot;, cmdOff =&amp;gt; &amp;quot;close&amp;quot; },\&lt;br /&gt;
        &amp;quot;kaffee&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Kaffeemaschine&amp;quot;, label =&amp;gt; &amp;quot;Kaffeemaschine&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;roberto&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;MQTT2_valetudo_FlusteredUnequaledFish&amp;quot;, label =&amp;gt; &amp;quot;Lade Roberto&amp;quot;, cmdOn =&amp;gt; &amp;quot;charge&amp;quot; },\&lt;br /&gt;
        &amp;quot;ambiente&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;LampeSzeneAlle&amp;quot;, label =&amp;gt; &amp;quot;Zentrales Licht&amp;quot;, type =&amp;gt; &amp;quot;dimmer&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;sauge|reinige|putze|staubsauger|roboter&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;MQTT2_valetudo_FlusteredUnequaledFish&amp;quot;, label =&amp;gt; &amp;quot;Staubsauger&amp;quot;, type =&amp;gt; &amp;quot;vacuum&amp;quot; }\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    my %vacRooms = (&amp;quot;arbeitszimmer&amp;quot; =&amp;gt; &amp;quot;Arbeitszimmer&amp;quot;, &amp;quot;badezimmer&amp;quot; =&amp;gt; &amp;quot;Badezimmer&amp;quot;, &amp;quot;esszimmer&amp;quot; =&amp;gt; &amp;quot;Esszimmer&amp;quot;, &amp;quot;flur&amp;quot; =&amp;gt; &amp;quot;Flur&amp;quot;, &amp;quot;küche&amp;quot; =&amp;gt; &amp;quot;Küche&amp;quot;, &amp;quot;wohnzimmer&amp;quot; =&amp;gt; &amp;quot;Wohnzimmer&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $onRegEx  = &#039;\b(an|ein|einschalten|starte|aktivier|aktiviere|öffne|öffnen|auf|hoch|lade)\b&#039;;;\&lt;br /&gt;
    my $offRegEx = &#039;\b(aus|ausschalten|stop|stoppe|beende|deaktivier|deaktiviere|schließe|schließen|zu|runter)\b&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
    my @commands = split(/\s*(?:und|dann|,)\s*/, lc($cleanEvent));;\&lt;br /&gt;
\&lt;br /&gt;
    # --- DER BEFEHLS-LOOP ---\&lt;br /&gt;
    foreach my $cmd_part (@commands) {\&lt;br /&gt;
        $cmd_part =~ s/^\s+|\s+$//g;;\&lt;br /&gt;
        my $is_on  = ($cmd_part =~ /$onRegEx/) ? 1 : 0;;\&lt;br /&gt;
        my $is_off = ($cmd_part =~ /$offRegEx/) ? 1 : 0;;\&lt;br /&gt;
        $cmd_part =~ s/\b(ich|brauche|mach|bitte|kannst du|würdest du|mal|doch|den|das|die|im|in der)\b//g;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- AMBILIGHT ---\&lt;br /&gt;
        if ($cmd_part =~ /ambilight/) {\&lt;br /&gt;
            system(&amp;quot;sshpass -p &#039;1431Fhem1982&#039; ssh -o StrictHostKeyChecking=no root\@192.168.1.46 &#039;/usr/share/hyperhdr/scripts/hyperhdr_toggle.sh&#039;&amp;quot;);;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- GENERISCHE STEUERUNG ---\&lt;br /&gt;
        my $match = 0;;\&lt;br /&gt;
        # Sortierung stellt sicher, dass &#039;dimmer/vacuum&#039; Vorrang haben\&lt;br /&gt;
        foreach my $key (sort { ($smartHomeDevices{$b}{type}//&amp;quot;&amp;quot;) cmp ($smartHomeDevices{$a}{type}//&amp;quot;&amp;quot;) } keys %smartHomeDevices) {\&lt;br /&gt;
            \&lt;br /&gt;
            my ($main, $must) = split(/:/, $key);;\&lt;br /&gt;
            \&lt;br /&gt;
            # Prüfe Hauptwort\&lt;br /&gt;
            if ($cmd_part =~ /\b($main)\b/i) {\&lt;br /&gt;
                # Prüfe Pflichtwort falls vorhanden\&lt;br /&gt;
                next if ($must &amp;amp;&amp;amp; $cmd_part !~ /\b($must)\b/i);;\&lt;br /&gt;
                \&lt;br /&gt;
                my $d = $smartHomeDevices{$key};;\&lt;br /&gt;
                \&lt;br /&gt;
                # A) Staubsauger\&lt;br /&gt;
                if (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;vacuum&amp;quot;) {\&lt;br /&gt;
                    my @found = grep { $cmd_part =~ /\b$_\b/ } keys %vacRooms;;\&lt;br /&gt;
                    fhem(@found ? &amp;quot;set $d-&amp;gt;{dev} clean_segment &amp;quot;.join(&amp;quot;,&amp;quot;, map { $vacRooms{$_} } @found) : &amp;quot;set $d-&amp;gt;{dev} start&amp;quot;);;\&lt;br /&gt;
                }\&lt;br /&gt;
                # B) Dimmer (Kompakt &amp;amp; korrigiert)\&lt;br /&gt;
                elsif (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;dimmer&amp;quot;) {\&lt;br /&gt;
                    $cmd_part =~ /(\d+)/ \&lt;br /&gt;
                        ? fhem(&amp;quot;set $d-&amp;gt;{dev} brightness &amp;quot; . int(($1 &amp;gt; 100 ? 100 : $1) * 2.55)) \&lt;br /&gt;
                        : ($is_on || $is_off) &amp;amp;&amp;amp; fhem(&amp;quot;set $d-&amp;gt;{dev} &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;));;\&lt;br /&gt;
                }\&lt;br /&gt;
                # C) Standard An/Aus\&lt;br /&gt;
                else {\&lt;br /&gt;
                    my $fhem_cmd = $is_off ? ($d-&amp;gt;{cmdOff} // &amp;quot;off&amp;quot;) : ($d-&amp;gt;{cmdOn} // &amp;quot;on&amp;quot;);;\&lt;br /&gt;
                    fhem(&amp;quot;set $d-&amp;gt;{dev} $fhem_cmd&amp;quot;) if ($is_on || $is_off);;\&lt;br /&gt;
                }\&lt;br /&gt;
                \&lt;br /&gt;
                $match = 1;; last;;\&lt;br /&gt;
            }\&lt;br /&gt;
        }\&lt;br /&gt;
        next if $match;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- HILFE ---\&lt;br /&gt;
        if ($cmd_part =~ /(hilfe|kommandos|übersicht)/) {\&lt;br /&gt;
            my $h = &#039;&amp;lt;div style=&amp;quot;text-align:left;;min-width:250px;;font-family:sans-serif;;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Befehlsübersicht:&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            my %seen;;\&lt;br /&gt;
            for my $k (sort keys %smartHomeDevices) {\&lt;br /&gt;
                my $d = $smartHomeDevices{$k};;\&lt;br /&gt;
                next if $seen{$d-&amp;gt;{label}}++;; \&lt;br /&gt;
                my $suffix = ($d-&amp;gt;{cmdOn} &amp;amp;&amp;amp; $d-&amp;gt;{cmdOff} &amp;amp;&amp;amp; $d-&amp;gt;{label} !~ /(an\/aus|öffnen\/schließen)/i) ? &amp;quot; (an/aus)&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
                $suffix = &amp;quot; [0-100%]&amp;quot; if ($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;dimmer&amp;quot;;;\&lt;br /&gt;
                $h .= &amp;quot;• $d-&amp;gt;{label}$suffix&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            }\&lt;br /&gt;
            $h .= &#039;&amp;lt;br&amp;gt;&amp;lt;u&amp;gt;Staubsauger Räume&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;• &#039;.join(&amp;quot;, &amp;quot;, map { ucfirst($_) } sort keys %vacRooms).&#039;&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt;&#039;;;\&lt;br /&gt;
            $h =~ s/&#039;/\\&amp;quot;/g;;\&lt;br /&gt;
            my $js = &amp;quot;if((document.querySelector(&#039;input[name=\&amp;quot;fw_id\&amp;quot;]&#039;)||{}).value===&#039;$clientId&#039;){FW_okDialog(&#039;$h&#039;)}&amp;quot;;;\&lt;br /&gt;
            FW_directNotify(&amp;quot;#FHEMWEB:$_&amp;quot;, $js, &amp;quot;&amp;quot;) for devspec2array(&amp;quot;TYPE=FHEMWEB&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Unterschiede der Wege ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Feature&lt;br /&gt;
! Browser&lt;br /&gt;
! Hybrid (Atom Echo S3)&lt;br /&gt;
|-&lt;br /&gt;
| Wakeword&lt;br /&gt;
| Browser&lt;br /&gt;
| ESP (Hardware)&lt;br /&gt;
|-&lt;br /&gt;
| Push-to-Talk&lt;br /&gt;
| Ja&lt;br /&gt;
| Nein&lt;br /&gt;
|-&lt;br /&gt;
| Always-On&lt;br /&gt;
| Ja (bedingt)&lt;br /&gt;
| Ja&lt;br /&gt;
|-&lt;br /&gt;
| Architektur&lt;br /&gt;
| Software&lt;br /&gt;
| Software + Hardware&lt;br /&gt;
|-&lt;br /&gt;
| Stabilität&lt;br /&gt;
| Sehr stabil&lt;br /&gt;
| Sehr stabil (Wakeword extern)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== FireOS mit Fully Browser (Plus) ==&lt;br /&gt;
&lt;br /&gt;
Damit die Spracherkennung funktioniert:&lt;br /&gt;
&lt;br /&gt;
* Apps installieren:&lt;br /&gt;
   Google&lt;br /&gt;
   Speech Recognition &amp;amp; Synthesis&lt;br /&gt;
* FireOS:&lt;br /&gt;
   „Tastatur und Sprache“ → „Text-to-Speech“&lt;br /&gt;
* Fully Browser:&lt;br /&gt;
   &amp;lt;code&amp;gt;Enable JavaScriptInterface (PLUS)&amp;lt;/code&amp;gt; aktivieren&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40912</id>
		<title>FHEMWEB/VoiceControl: Web-STT &amp; Hardware-Wakeword</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40912"/>
		<updated>2026-04-03T16:15:29Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Voicecontrol.png|mini]]&lt;br /&gt;
&lt;br /&gt;
== VoiceControl – Sprachsteuerung via Browser und Atom Echo s3r ==&lt;br /&gt;
&lt;br /&gt;
Diese Lösung ermöglicht eine flexible Sprachsteuerung für [[FHEM]]. Sprache wird in Text (Speech-to-Text) umgewandelt und als Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; bereitgestellt. Dieses Reading kann anschließend zentral (z. B. über &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DOIF&amp;lt;/code&amp;gt;) ausgewertet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Unterstützt werden nur Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox hat leider kein Backend.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei unterschiedliche Wege zur Spracherfassung:&lt;br /&gt;
&lt;br /&gt;
* Weg 1️⃣ (Software): Browser-basierte Komplettlösung&lt;br /&gt;
* Weg 2️⃣ (Hybrid): Hardware-Wakeword + Browser-Spracherkennung&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
Dort befinden sich in Post #1 auch alle benötigten Dateien.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Grundprinzip ====&lt;br /&gt;
* Sprache → Speech-to-Text&lt;br /&gt;
* Ergebnis → Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zentrale Logik verarbeitet Befehle&lt;br /&gt;
&lt;br /&gt;
==== Betriebsarten ====&lt;br /&gt;
* Push-to-Talk (nur Browser)&lt;br /&gt;
* Always-On mit Wakeword&lt;br /&gt;
* Hardware-Wakeword (Hybrid)&lt;br /&gt;
&lt;br /&gt;
==== Rückmeldungen ====&lt;br /&gt;
* Sprachausgabe (TTS)&lt;br /&gt;
* Visuelle Bubble im Browser&lt;br /&gt;
* Optional gezielte Rückmeldung per Client-ID&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 1️⃣: Browser-Lösung (voicecontrol.js) ==&lt;br /&gt;
&lt;br /&gt;
Das Script nutzt die Google Web Speech API. Unterstützt werden Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox wird aktuell nicht unterstützt.&lt;br /&gt;
&lt;br /&gt;
=== Bedienung ===&lt;br /&gt;
&lt;br /&gt;
==== Push-to-Talk ====&lt;br /&gt;
* Button gedrückt halten (~450 ms)&lt;br /&gt;
* Direkt sprechen (kein Wakeword nötig)&lt;br /&gt;
* Befehl wird sofort verarbeitet&lt;br /&gt;
&lt;br /&gt;
==== Always-On ====&lt;br /&gt;
* Kurzer Klick aktiviert Dauerbetrieb&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung permanentes Mithören&lt;br /&gt;
&lt;br /&gt;
==== Wakeword ====&lt;br /&gt;
* Standard: &amp;lt;code&amp;gt;james&amp;lt;/code&amp;gt;&lt;br /&gt;
* Anpassbar im Script:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const wakewords = [&amp;quot;james&amp;quot;];&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ablauf:&lt;br /&gt;
# „James“ sagen&lt;br /&gt;
# System antwortet „Ja?“&lt;br /&gt;
# Zeitfenster (~6 Sekunden) für Befehl&lt;br /&gt;
# Danach automatische Verarbeitung oder Abbruch&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{ Svn_GetFile(&#039;contrib/voicecontrol.js&#039;, &#039;www/pgm2/voicecontrol.js&#039;) }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBphone JavaScripts www/pgm2/voicecontrol.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Hinweis (HTTP ohne HTTPS) ====&lt;br /&gt;
Chrome benötigt Freigabe für Mikrofon:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;chrome://flags/#unsafely-treat-insecure-origin-as-secure&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eigene URL hinzufügen&lt;br /&gt;
* Auf &amp;lt;code&amp;gt;Enabled&amp;lt;/code&amp;gt; setzen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 2️⃣: Hybrid-Lösung mit Browser + Atom Echo (voicecontrol_echo.js) ==&lt;br /&gt;
&lt;br /&gt;
Diese Variante kombiniert Hardware und Software:&lt;br /&gt;
&lt;br /&gt;
* Wakeword-Erkennung erfolgt auf dem ESP (Atom Echo s3r)&lt;br /&gt;
* Die eigentliche Sprachverarbeitung (Speech-to-Text) erfolgt im Browser&lt;br /&gt;
&lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
&lt;br /&gt;
# Wakeword wird auf dem ESP erkannt&lt;br /&gt;
# FHEM erzeugt Event (z. B. &amp;lt;code&amp;gt;james_detected&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Browser empfängt Event via WebSocket&lt;br /&gt;
# Speech-to-Text startet im Browser&lt;br /&gt;
# Ergebnis wird an FHEM übertragen&lt;br /&gt;
&lt;br /&gt;
=== Aktivierung ===&lt;br /&gt;
&lt;br /&gt;
* Kurzer Klick auf Button aktiviert/deaktiviert System&lt;br /&gt;
* Baut WebSocket-Verbindung zu FHEM auf&lt;br /&gt;
* Kein Push-to-Talk-Modus&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&lt;br /&gt;
Die Datei voicecontrol_echo.js ist hier zu finden:&lt;br /&gt;
{{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
und muss nach www/pgm2/voicecontrol_echo.js kopiert werden.&lt;br /&gt;
&lt;br /&gt;
Die echo_s3r.yaml ist für den Atom Echo s3r. Die Konfuguration ist weiter unten beschrieben.&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBtablet JavaScripts www/pgm2/voicecontrol_echo.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration im Javascript ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const DEVICE  = &amp;quot;atom_echos3r_9888e00f4280&amp;quot;;&lt;br /&gt;
const TRIGGER = &amp;quot;james_detected&amp;quot;;&lt;br /&gt;
const FHEM_IP = &amp;quot;192.168.1.76:8085&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; → FHEM-Device des ESP&lt;br /&gt;
* &amp;lt;code&amp;gt;TRIGGER&amp;lt;/code&amp;gt; → Event bei Wakeword&lt;br /&gt;
* &amp;lt;code&amp;gt;FHEM_IP&amp;lt;/code&amp;gt; → FHEM-Server&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration in der Yaml ===&lt;br /&gt;
&lt;br /&gt;
Damit sich der ESP mit eurem MQTT-Server und WLAN verbindet, müssen folgende Stellen angepasst werden.&lt;br /&gt;
&lt;br /&gt;
   wifi:&lt;br /&gt;
     ssid: &amp;quot;YOUR_SSID&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     fast_connect: true&lt;br /&gt;
&lt;br /&gt;
   mqtt:&lt;br /&gt;
     broker: 192.168.1.76&lt;br /&gt;
     port: 1884&lt;br /&gt;
     username: &amp;quot;YOUR_USERNAME&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     topic_prefix: atom_echo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wakeword (ESP / ESPHome) ===&lt;br /&gt;
&lt;br /&gt;
Das Wakeword wird direkt auf dem ESP definiert:&lt;br /&gt;
&lt;br /&gt;
* Umsetzung über ESPHome&lt;br /&gt;
* Eine große Auswahl an Wakewords sind hier zu finden:&lt;br /&gt;
   https://github.com/TaterTotterson/microWakeWords&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel, wie das Wakeword definiert wird.&lt;br /&gt;
&lt;br /&gt;
   micro_wake_word:&lt;br /&gt;
     id: mww&lt;br /&gt;
     microphone: echo_mic&lt;br /&gt;
     models:&lt;br /&gt;
       - model: &amp;quot;https://github.com/TaterTotterson/microWakeWords/raw/main/microWakeWordsV2/james.json&amp;quot;&lt;br /&gt;
         id: james_model&lt;br /&gt;
         probability_cutoff: 0.6   &lt;br /&gt;
&lt;br /&gt;
=== Kommunikation ===&lt;br /&gt;
&lt;br /&gt;
* WebSocket-Verbindung zu FHEM&lt;br /&gt;
* Lauscht auf Device-Events&lt;br /&gt;
* Automatischer Reconnect bei Abbruch&lt;br /&gt;
&lt;br /&gt;
=== Ablauf nach Wakeword ===&lt;br /&gt;
&lt;br /&gt;
# System sagt „Ja?“&lt;br /&gt;
# Browser startet SpeechRecognition&lt;br /&gt;
# Nutzer spricht Befehl&lt;br /&gt;
# Befehl wird verarbeitet&lt;br /&gt;
# Rückmeldung „Erledigt“&lt;br /&gt;
&lt;br /&gt;
== Zentrale Auswertung (Logik) ==&lt;br /&gt;
&lt;br /&gt;
Beide Wege schreiben in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;global:STT&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung erfolgt zentral über ein &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt;. In der Mappingtabelle wird das Device eingetragen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;quot;esszimmer:licht|lampe|deckenlampe&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;, label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aufschlüsselung:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;quot;hauptkeyword:Filter1|Filter3|Filter3&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Devicename&amp;quot;, label =&amp;gt; &amp;quot;Übersichtname&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: notify ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod n_VoiceControl notify global:STT:.* {\&lt;br /&gt;
    # --- VORBEREITUNG ---\&lt;br /&gt;
    my ($cleanEvent, $clientId) = $EVENT =~ /^(.*)\s\[(.*)\]$/;;\&lt;br /&gt;
    $cleanEvent //= $EVENT;;\&lt;br /&gt;
    $clientId   //= &amp;quot;unknown&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # --- KONFIGURATION: Zentrales Mapping ---\&lt;br /&gt;
    my %smartHomeDevices = (\&lt;br /&gt;
        &amp;quot;esszimmer:licht|lampe|deckenlampe&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;, label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;esszimmer:aquarium&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Aquarium_Aktor&amp;quot;, label =&amp;gt; &amp;quot;Aquarium&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;küche&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Deckenlampe_Kue&amp;quot;, label =&amp;gt; &amp;quot;Licht Küche&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe06_Dek&amp;quot;, label =&amp;gt; &amp;quot;Licht Wohnzimmer&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;fernseher|tv&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;VuPlus&amp;quot;, label =&amp;gt; &amp;quot;Fernseher&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;rechner|pc&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;PC_Aktor&amp;quot;, label =&amp;gt; &amp;quot;PC&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;garage|tor&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Garagentor_Aktor&amp;quot;, label =&amp;gt; &amp;quot;Garagentor öffnen/schließen&amp;quot;, cmdOn =&amp;gt; &amp;quot;open&amp;quot;, cmdOff =&amp;gt; &amp;quot;close&amp;quot; },\&lt;br /&gt;
        &amp;quot;kaffee&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Kaffeemaschine&amp;quot;, label =&amp;gt; &amp;quot;Kaffeemaschine&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;roberto&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;MQTT2_valetudo_FlusteredUnequaledFish&amp;quot;, label =&amp;gt; &amp;quot;Lade Roberto&amp;quot;, cmdOn =&amp;gt; &amp;quot;charge&amp;quot; },\&lt;br /&gt;
        &amp;quot;ambiente&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;LampeSzeneAlle&amp;quot;, label =&amp;gt; &amp;quot;Zentrales Licht&amp;quot;, type =&amp;gt; &amp;quot;dimmer&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;sauge|reinige|putze|staubsauger|roboter&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;MQTT2_valetudo_FlusteredUnequaledFish&amp;quot;, label =&amp;gt; &amp;quot;Staubsauger&amp;quot;, type =&amp;gt; &amp;quot;vacuum&amp;quot; }\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    my %vacRooms = (&amp;quot;arbeitszimmer&amp;quot; =&amp;gt; &amp;quot;Arbeitszimmer&amp;quot;, &amp;quot;badezimmer&amp;quot; =&amp;gt; &amp;quot;Badezimmer&amp;quot;, &amp;quot;esszimmer&amp;quot; =&amp;gt; &amp;quot;Esszimmer&amp;quot;, &amp;quot;flur&amp;quot; =&amp;gt; &amp;quot;Flur&amp;quot;, &amp;quot;küche&amp;quot; =&amp;gt; &amp;quot;Küche&amp;quot;, &amp;quot;wohnzimmer&amp;quot; =&amp;gt; &amp;quot;Wohnzimmer&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $onRegEx  = &#039;\b(an|ein|einschalten|starte|aktivier|aktiviere|öffne|öffnen|auf|hoch|lade)\b&#039;;;\&lt;br /&gt;
    my $offRegEx = &#039;\b(aus|ausschalten|stop|stoppe|beende|deaktivier|deaktiviere|schließe|schließen|zu|runter)\b&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
    my @commands = split(/\s*(?:und|dann|,)\s*/, lc($cleanEvent));;\&lt;br /&gt;
\&lt;br /&gt;
    # --- DER BEFEHLS-LOOP ---\&lt;br /&gt;
    foreach my $cmd_part (@commands) {\&lt;br /&gt;
        $cmd_part =~ s/^\s+|\s+$//g;;\&lt;br /&gt;
        my $is_on  = ($cmd_part =~ /$onRegEx/) ? 1 : 0;;\&lt;br /&gt;
        my $is_off = ($cmd_part =~ /$offRegEx/) ? 1 : 0;;\&lt;br /&gt;
        $cmd_part =~ s/\b(ich|brauche|mach|bitte|kannst du|würdest du|mal|doch|den|das|die|im|in der)\b//g;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- AMBILIGHT ---\&lt;br /&gt;
        if ($cmd_part =~ /ambilight/) {\&lt;br /&gt;
            system(&amp;quot;sshpass -p &#039;1431Fhem1982&#039; ssh -o StrictHostKeyChecking=no root\@192.168.1.46 &#039;/usr/share/hyperhdr/scripts/hyperhdr_toggle.sh&#039;&amp;quot;);;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- GENERISCHE STEUERUNG ---\&lt;br /&gt;
        my $match = 0;;\&lt;br /&gt;
        # Sortierung stellt sicher, dass &#039;dimmer/vacuum&#039; Vorrang haben\&lt;br /&gt;
        foreach my $key (sort { ($smartHomeDevices{$b}{type}//&amp;quot;&amp;quot;) cmp ($smartHomeDevices{$a}{type}//&amp;quot;&amp;quot;) } keys %smartHomeDevices) {\&lt;br /&gt;
            \&lt;br /&gt;
            my ($main, $must) = split(/:/, $key);;\&lt;br /&gt;
            \&lt;br /&gt;
            # Prüfe Hauptwort\&lt;br /&gt;
            if ($cmd_part =~ /\b($main)\b/i) {\&lt;br /&gt;
                # Prüfe Pflichtwort falls vorhanden\&lt;br /&gt;
                next if ($must &amp;amp;&amp;amp; $cmd_part !~ /\b($must)\b/i);;\&lt;br /&gt;
                \&lt;br /&gt;
                my $d = $smartHomeDevices{$key};;\&lt;br /&gt;
                \&lt;br /&gt;
                # A) Staubsauger\&lt;br /&gt;
                if (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;vacuum&amp;quot;) {\&lt;br /&gt;
                    my @found = grep { $cmd_part =~ /\b$_\b/ } keys %vacRooms;;\&lt;br /&gt;
                    fhem(@found ? &amp;quot;set $d-&amp;gt;{dev} clean_segment &amp;quot;.join(&amp;quot;,&amp;quot;, map { $vacRooms{$_} } @found) : &amp;quot;set $d-&amp;gt;{dev} start&amp;quot;);;\&lt;br /&gt;
                }\&lt;br /&gt;
                # B) Dimmer (Kompakt &amp;amp; korrigiert)\&lt;br /&gt;
                elsif (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;dimmer&amp;quot;) {\&lt;br /&gt;
                    $cmd_part =~ /(\d+)/ \&lt;br /&gt;
                        ? fhem(&amp;quot;set $d-&amp;gt;{dev} brightness &amp;quot; . int(($1 &amp;gt; 100 ? 100 : $1) * 2.55)) \&lt;br /&gt;
                        : ($is_on || $is_off) &amp;amp;&amp;amp; fhem(&amp;quot;set $d-&amp;gt;{dev} &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;));;\&lt;br /&gt;
                }\&lt;br /&gt;
                # C) Standard An/Aus\&lt;br /&gt;
                else {\&lt;br /&gt;
                    my $fhem_cmd = $is_off ? ($d-&amp;gt;{cmdOff} // &amp;quot;off&amp;quot;) : ($d-&amp;gt;{cmdOn} // &amp;quot;on&amp;quot;);;\&lt;br /&gt;
                    fhem(&amp;quot;set $d-&amp;gt;{dev} $fhem_cmd&amp;quot;) if ($is_on || $is_off);;\&lt;br /&gt;
                }\&lt;br /&gt;
                \&lt;br /&gt;
                $match = 1;; last;;\&lt;br /&gt;
            }\&lt;br /&gt;
        }\&lt;br /&gt;
        next if $match;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- HILFE ---\&lt;br /&gt;
        if ($cmd_part =~ /(hilfe|kommandos|übersicht)/) {\&lt;br /&gt;
            my $h = &#039;&amp;lt;div style=&amp;quot;text-align:left;;min-width:250px;;font-family:sans-serif;;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Befehlsübersicht:&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            my %seen;;\&lt;br /&gt;
            for my $k (sort keys %smartHomeDevices) {\&lt;br /&gt;
                my $d = $smartHomeDevices{$k};;\&lt;br /&gt;
                next if $seen{$d-&amp;gt;{label}}++;; \&lt;br /&gt;
                my $suffix = ($d-&amp;gt;{cmdOn} &amp;amp;&amp;amp; $d-&amp;gt;{cmdOff} &amp;amp;&amp;amp; $d-&amp;gt;{label} !~ /(an\/aus|öffnen\/schließen)/i) ? &amp;quot; (an/aus)&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
                $suffix = &amp;quot; [0-100%]&amp;quot; if ($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;dimmer&amp;quot;;;\&lt;br /&gt;
                $h .= &amp;quot;• $d-&amp;gt;{label}$suffix&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            }\&lt;br /&gt;
            $h .= &#039;&amp;lt;br&amp;gt;&amp;lt;u&amp;gt;Staubsauger Räume&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;• &#039;.join(&amp;quot;, &amp;quot;, map { ucfirst($_) } sort keys %vacRooms).&#039;&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt;&#039;;;\&lt;br /&gt;
            $h =~ s/&#039;/\\&amp;quot;/g;;\&lt;br /&gt;
            my $js = &amp;quot;if((document.querySelector(&#039;input[name=\&amp;quot;fw_id\&amp;quot;]&#039;)||{}).value===&#039;$clientId&#039;){FW_okDialog(&#039;$h&#039;)}&amp;quot;;;\&lt;br /&gt;
            FW_directNotify(&amp;quot;#FHEMWEB:$_&amp;quot;, $js, &amp;quot;&amp;quot;) for devspec2array(&amp;quot;TYPE=FHEMWEB&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Unterschiede der Wege ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Feature&lt;br /&gt;
! Browser&lt;br /&gt;
! Hybrid (Atom Echo S3)&lt;br /&gt;
|-&lt;br /&gt;
| Wakeword&lt;br /&gt;
| Browser&lt;br /&gt;
| ESP (Hardware)&lt;br /&gt;
|-&lt;br /&gt;
| Push-to-Talk&lt;br /&gt;
| Ja&lt;br /&gt;
| Nein&lt;br /&gt;
|-&lt;br /&gt;
| Always-On&lt;br /&gt;
| Ja (bedingt)&lt;br /&gt;
| Ja&lt;br /&gt;
|-&lt;br /&gt;
| Architektur&lt;br /&gt;
| Software&lt;br /&gt;
| Software + Hardware&lt;br /&gt;
|-&lt;br /&gt;
| Stabilität&lt;br /&gt;
| Sehr stabil&lt;br /&gt;
| Sehr stabil (Wakeword extern)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== FireOS mit Fully Browser (Plus) ==&lt;br /&gt;
&lt;br /&gt;
Damit die Spracherkennung funktioniert:&lt;br /&gt;
&lt;br /&gt;
* Apps installieren:&lt;br /&gt;
   Google&lt;br /&gt;
   Speech Recognition &amp;amp; Synthesis&lt;br /&gt;
* FireOS:&lt;br /&gt;
   „Tastatur und Sprache“ → „Text-to-Speech“&lt;br /&gt;
* Fully Browser:&lt;br /&gt;
   &amp;lt;code&amp;gt;Enable JavaScriptInterface (PLUS)&amp;lt;/code&amp;gt; aktivieren&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40911</id>
		<title>FHEMWEB/VoiceControl: Web-STT &amp; Hardware-Wakeword</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40911"/>
		<updated>2026-04-03T16:07:19Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Voicecontrol.png|mini]]&lt;br /&gt;
&lt;br /&gt;
== VoiceControl – Sprachsteuerung via Browser und Atom Echo s3r ==&lt;br /&gt;
&lt;br /&gt;
Diese Lösung ermöglicht eine flexible Sprachsteuerung für [[FHEM]]. Sprache wird in Text (Speech-to-Text) umgewandelt und als Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; bereitgestellt. Dieses Reading kann anschließend zentral (z. B. über &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DOIF&amp;lt;/code&amp;gt;) ausgewertet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Unterstützt werden nur Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox hat leider kein Backend.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei unterschiedliche Wege zur Spracherfassung:&lt;br /&gt;
&lt;br /&gt;
* Weg 1️⃣ (Software): Browser-basierte Komplettlösung&lt;br /&gt;
* Weg 2️⃣ (Hybrid): Hardware-Wakeword + Browser-Spracherkennung&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
Dort befinden sich in Post #1 auch alle benötigten Dateien.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Grundprinzip ====&lt;br /&gt;
* Sprache → Speech-to-Text&lt;br /&gt;
* Ergebnis → Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zentrale Logik verarbeitet Befehle&lt;br /&gt;
&lt;br /&gt;
==== Betriebsarten ====&lt;br /&gt;
* Push-to-Talk (nur Browser)&lt;br /&gt;
* Always-On mit Wakeword&lt;br /&gt;
* Hardware-Wakeword (Hybrid)&lt;br /&gt;
&lt;br /&gt;
==== Rückmeldungen ====&lt;br /&gt;
* Sprachausgabe (TTS)&lt;br /&gt;
* Visuelle Bubble im Browser&lt;br /&gt;
* Optional gezielte Rückmeldung per Client-ID&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 1️⃣: Browser-Lösung (voicecontrol.js) ==&lt;br /&gt;
&lt;br /&gt;
Das Script nutzt die Google Web Speech API. Unterstützt werden Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox wird aktuell nicht unterstützt.&lt;br /&gt;
&lt;br /&gt;
=== Bedienung ===&lt;br /&gt;
&lt;br /&gt;
==== Push-to-Talk ====&lt;br /&gt;
* Button gedrückt halten (~450 ms)&lt;br /&gt;
* Direkt sprechen (kein Wakeword nötig)&lt;br /&gt;
* Befehl wird sofort verarbeitet&lt;br /&gt;
&lt;br /&gt;
==== Always-On ====&lt;br /&gt;
* Kurzer Klick aktiviert Dauerbetrieb&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung permanentes Mithören&lt;br /&gt;
&lt;br /&gt;
==== Wakeword ====&lt;br /&gt;
* Standard: &amp;lt;code&amp;gt;james&amp;lt;/code&amp;gt;&lt;br /&gt;
* Anpassbar im Script:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const wakewords = [&amp;quot;james&amp;quot;];&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ablauf:&lt;br /&gt;
# „James“ sagen&lt;br /&gt;
# System antwortet „Ja?“&lt;br /&gt;
# Zeitfenster (~6 Sekunden) für Befehl&lt;br /&gt;
# Danach automatische Verarbeitung oder Abbruch&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{ Svn_GetFile(&#039;contrib/voicecontrol.js&#039;, &#039;www/pgm2/voicecontrol.js&#039;) }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBphone JavaScripts www/pgm2/voicecontrol.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Hinweis (HTTP ohne HTTPS) ====&lt;br /&gt;
Chrome benötigt Freigabe für Mikrofon:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;chrome://flags/#unsafely-treat-insecure-origin-as-secure&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eigene URL hinzufügen&lt;br /&gt;
* Auf &amp;lt;code&amp;gt;Enabled&amp;lt;/code&amp;gt; setzen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 2️⃣: Hybrid-Lösung mit Browser + Atom Echo (voicecontrol_echo.js) ==&lt;br /&gt;
&lt;br /&gt;
Diese Variante kombiniert Hardware und Software:&lt;br /&gt;
&lt;br /&gt;
* Wakeword-Erkennung erfolgt auf dem ESP (Atom Echo s3r)&lt;br /&gt;
* Die eigentliche Sprachverarbeitung (Speech-to-Text) erfolgt im Browser&lt;br /&gt;
&lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
&lt;br /&gt;
# Wakeword wird auf dem ESP erkannt&lt;br /&gt;
# FHEM erzeugt Event (z. B. &amp;lt;code&amp;gt;james_detected&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Browser empfängt Event via WebSocket&lt;br /&gt;
# Speech-to-Text startet im Browser&lt;br /&gt;
# Ergebnis wird an FHEM übertragen&lt;br /&gt;
&lt;br /&gt;
=== Aktivierung ===&lt;br /&gt;
&lt;br /&gt;
* Kurzer Klick auf Button aktiviert/deaktiviert System&lt;br /&gt;
* Baut WebSocket-Verbindung zu FHEM auf&lt;br /&gt;
* Kein Push-to-Talk-Modus&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&lt;br /&gt;
Die Datei voicecontrol_echo.js ist hier zu finden:&lt;br /&gt;
{{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
und muss nach www/pgm2/voicecontrol_echo.js kopiert werden.&lt;br /&gt;
&lt;br /&gt;
Die echo_s3r.yaml ist für den Atom Echo s3r. Die Konfuguration ist weiter unten beschrieben.&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBtablet JavaScripts www/pgm2/voicecontrol_echo.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration im Javascript ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const DEVICE  = &amp;quot;atom_echos3r_9888e00f4280&amp;quot;;&lt;br /&gt;
const TRIGGER = &amp;quot;james_detected&amp;quot;;&lt;br /&gt;
const FHEM_IP = &amp;quot;192.168.1.76:8085&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; → FHEM-Device des ESP&lt;br /&gt;
* &amp;lt;code&amp;gt;TRIGGER&amp;lt;/code&amp;gt; → Event bei Wakeword&lt;br /&gt;
* &amp;lt;code&amp;gt;FHEM_IP&amp;lt;/code&amp;gt; → FHEM-Server&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration in der Yaml ===&lt;br /&gt;
&lt;br /&gt;
Damit sich der ESP mit eurem MQTT-Server und WLAN verbindet, müssen folgende Stellen angepasst werden.&lt;br /&gt;
&lt;br /&gt;
   wifi:&lt;br /&gt;
     ssid: &amp;quot;YOUR_SSID&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     fast_connect: true&lt;br /&gt;
&lt;br /&gt;
   mqtt:&lt;br /&gt;
     broker: 192.168.1.76&lt;br /&gt;
     port: 1884&lt;br /&gt;
     username: &amp;quot;YOUR_USERNAME&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     topic_prefix: atom_echo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wakeword (ESP / ESPHome) ===&lt;br /&gt;
&lt;br /&gt;
Das Wakeword wird direkt auf dem ESP definiert:&lt;br /&gt;
&lt;br /&gt;
* Umsetzung über ESPHome&lt;br /&gt;
* Eine große Auswahl an Wakewords sind hier zu finden:&lt;br /&gt;
   https://github.com/TaterTotterson/microWakeWords&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel, wie das Wakeword definiert wird.&lt;br /&gt;
&lt;br /&gt;
   micro_wake_word:&lt;br /&gt;
     id: mww&lt;br /&gt;
     microphone: echo_mic&lt;br /&gt;
     models:&lt;br /&gt;
       - model: &amp;quot;https://github.com/TaterTotterson/microWakeWords/raw/main/microWakeWordsV2/james.json&amp;quot;&lt;br /&gt;
         id: james_model&lt;br /&gt;
         probability_cutoff: 0.6   &lt;br /&gt;
&lt;br /&gt;
=== Kommunikation ===&lt;br /&gt;
&lt;br /&gt;
* WebSocket-Verbindung zu FHEM&lt;br /&gt;
* Lauscht auf Device-Events&lt;br /&gt;
* Automatischer Reconnect bei Abbruch&lt;br /&gt;
&lt;br /&gt;
=== Ablauf nach Wakeword ===&lt;br /&gt;
&lt;br /&gt;
# System sagt „Ja?“&lt;br /&gt;
# Browser startet SpeechRecognition&lt;br /&gt;
# Nutzer spricht Befehl&lt;br /&gt;
# Befehl wird verarbeitet&lt;br /&gt;
# Rückmeldung „Erledigt“&lt;br /&gt;
&lt;br /&gt;
== Zentrale Auswertung (Logik) ==&lt;br /&gt;
&lt;br /&gt;
Beide Wege schreiben in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;global:STT&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung erfolgt zentral über ein &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: notify ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod n_VoiceControl notify global:STT:.* {\&lt;br /&gt;
    # --- VORBEREITUNG ---\&lt;br /&gt;
    my ($cleanEvent, $clientId) = $EVENT =~ /^(.*)\s\[(.*)\]$/;;\&lt;br /&gt;
    $cleanEvent //= $EVENT;;\&lt;br /&gt;
    $clientId   //= &amp;quot;unknown&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # --- KONFIGURATION: Zentrales Mapping ---\&lt;br /&gt;
    my %smartHomeDevices = (\&lt;br /&gt;
        &amp;quot;esszimmer:licht|lampe|deckenlampe&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;, label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot;,  cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;esszimmer:aquarium&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Aquarium_Aktor&amp;quot;, label =&amp;gt; &amp;quot;Aquarium&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;küche&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Deckenlampe_Kue&amp;quot;, label =&amp;gt; &amp;quot;Licht Küche&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe06_Dek&amp;quot;, label =&amp;gt; &amp;quot;Licht Wohnzimmer&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;fernseher|tv&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;VuPlus&amp;quot;, label =&amp;gt; &amp;quot;Fernseher&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;rechner|pc&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;PC_Aktor&amp;quot;, label =&amp;gt; &amp;quot;PC&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;garage|tor&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Garagentor_Aktor&amp;quot;, label =&amp;gt; &amp;quot;Garagentor öffnen/schließen&amp;quot;, cmdOn =&amp;gt; &amp;quot;open&amp;quot;, cmdOff =&amp;gt; &amp;quot;close&amp;quot; },\&lt;br /&gt;
        &amp;quot;kaffee&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Kaffeemaschine&amp;quot;, label =&amp;gt; &amp;quot;Kaffeemaschine&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;roberto&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;MQTT2_valetudo_FlusteredUnequaledFish&amp;quot;, label =&amp;gt; &amp;quot;Lade Roberto&amp;quot;, cmdOn =&amp;gt; &amp;quot;charge&amp;quot; },\&lt;br /&gt;
        &amp;quot;ambiente&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;LampeSzeneAlle&amp;quot;, label =&amp;gt; &amp;quot;Zentrales Licht&amp;quot;, type =&amp;gt; &amp;quot;dimmer&amp;quot;, cmdOn =&amp;gt; &amp;quot;on&amp;quot;, cmdOff =&amp;gt; &amp;quot;off&amp;quot; },\&lt;br /&gt;
        &amp;quot;sauge|reinige|putze|staubsauger|roboter&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;MQTT2_valetudo_FlusteredUnequaledFish&amp;quot;, label =&amp;gt; &amp;quot;Staubsauger&amp;quot;, type =&amp;gt; &amp;quot;vacuum&amp;quot; }\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    my %vacRooms = (&amp;quot;arbeitszimmer&amp;quot; =&amp;gt; &amp;quot;Arbeitszimmer&amp;quot;, &amp;quot;badezimmer&amp;quot; =&amp;gt; &amp;quot;Badezimmer&amp;quot;, &amp;quot;esszimmer&amp;quot; =&amp;gt; &amp;quot;Esszimmer&amp;quot;, &amp;quot;flur&amp;quot; =&amp;gt; &amp;quot;Flur&amp;quot;, &amp;quot;küche&amp;quot; =&amp;gt; &amp;quot;Küche&amp;quot;, &amp;quot;wohnzimmer&amp;quot; =&amp;gt; &amp;quot;Wohnzimmer&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $onRegEx  = &#039;\b(an|ein|einschalten|starte|aktivier|aktiviere|öffne|öffnen|auf|hoch|lade)\b&#039;;;\&lt;br /&gt;
    my $offRegEx = &#039;\b(aus|ausschalten|stop|stoppe|beende|deaktivier|deaktiviere|schließe|schließen|zu|runter)\b&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
    my @commands = split(/\s*(?:und|dann|,)\s*/, lc($cleanEvent));;\&lt;br /&gt;
\&lt;br /&gt;
    # --- DER BEFEHLS-LOOP ---\&lt;br /&gt;
    foreach my $cmd_part (@commands) {\&lt;br /&gt;
        $cmd_part =~ s/^\s+|\s+$//g;;\&lt;br /&gt;
        my $is_on  = ($cmd_part =~ /$onRegEx/) ? 1 : 0;;\&lt;br /&gt;
        my $is_off = ($cmd_part =~ /$offRegEx/) ? 1 : 0;;\&lt;br /&gt;
        $cmd_part =~ s/\b(ich|brauche|mach|bitte|kannst du|würdest du|mal|doch|den|das|die|im|in der)\b//g;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- AMBILIGHT ---\&lt;br /&gt;
        if ($cmd_part =~ /ambilight/) {\&lt;br /&gt;
            system(&amp;quot;sshpass -p &#039;1431Fhem1982&#039; ssh -o StrictHostKeyChecking=no root\@192.168.1.46 &#039;/usr/share/hyperhdr/scripts/hyperhdr_toggle.sh&#039;&amp;quot;);;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- GENERISCHE STEUERUNG ---\&lt;br /&gt;
        my $match = 0;;\&lt;br /&gt;
        # Sortierung stellt sicher, dass &#039;dimmer/vacuum&#039; Vorrang haben\&lt;br /&gt;
        foreach my $key (sort { ($smartHomeDevices{$b}{type}//&amp;quot;&amp;quot;) cmp ($smartHomeDevices{$a}{type}//&amp;quot;&amp;quot;) } keys %smartHomeDevices) {\&lt;br /&gt;
            \&lt;br /&gt;
            my ($main, $must) = split(/:/, $key);;\&lt;br /&gt;
            \&lt;br /&gt;
            # Prüfe Hauptwort\&lt;br /&gt;
            if ($cmd_part =~ /\b($main)\b/i) {\&lt;br /&gt;
                # Prüfe Pflichtwort falls vorhanden\&lt;br /&gt;
                next if ($must &amp;amp;&amp;amp; $cmd_part !~ /\b($must)\b/i);;\&lt;br /&gt;
                \&lt;br /&gt;
                my $d = $smartHomeDevices{$key};;\&lt;br /&gt;
                \&lt;br /&gt;
                # A) Staubsauger\&lt;br /&gt;
                if (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;vacuum&amp;quot;) {\&lt;br /&gt;
                    my @found = grep { $cmd_part =~ /\b$_\b/ } keys %vacRooms;;\&lt;br /&gt;
                    fhem(@found ? &amp;quot;set $d-&amp;gt;{dev} clean_segment &amp;quot;.join(&amp;quot;,&amp;quot;, map { $vacRooms{$_} } @found) : &amp;quot;set $d-&amp;gt;{dev} start&amp;quot;);;\&lt;br /&gt;
                }\&lt;br /&gt;
                # B) Dimmer (Kompakt &amp;amp; korrigiert)\&lt;br /&gt;
                elsif (($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;dimmer&amp;quot;) {\&lt;br /&gt;
                    $cmd_part =~ /(\d+)/ \&lt;br /&gt;
                        ? fhem(&amp;quot;set $d-&amp;gt;{dev} brightness &amp;quot; . int(($1 &amp;gt; 100 ? 100 : $1) * 2.55)) \&lt;br /&gt;
                        : ($is_on || $is_off) &amp;amp;&amp;amp; fhem(&amp;quot;set $d-&amp;gt;{dev} &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;));;\&lt;br /&gt;
                }\&lt;br /&gt;
                # C) Standard An/Aus\&lt;br /&gt;
                else {\&lt;br /&gt;
                    my $fhem_cmd = $is_off ? ($d-&amp;gt;{cmdOff} // &amp;quot;off&amp;quot;) : ($d-&amp;gt;{cmdOn} // &amp;quot;on&amp;quot;);;\&lt;br /&gt;
                    fhem(&amp;quot;set $d-&amp;gt;{dev} $fhem_cmd&amp;quot;) if ($is_on || $is_off);;\&lt;br /&gt;
                }\&lt;br /&gt;
                \&lt;br /&gt;
                $match = 1;; last;;\&lt;br /&gt;
            }\&lt;br /&gt;
        }\&lt;br /&gt;
        next if $match;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- HILFE ---\&lt;br /&gt;
        if ($cmd_part =~ /(hilfe|kommandos|übersicht)/) {\&lt;br /&gt;
            my $h = &#039;&amp;lt;div style=&amp;quot;text-align:left;;min-width:250px;;font-family:sans-serif;;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Befehlsübersicht:&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            my %seen;;\&lt;br /&gt;
            for my $k (sort keys %smartHomeDevices) {\&lt;br /&gt;
                my $d = $smartHomeDevices{$k};;\&lt;br /&gt;
                next if $seen{$d-&amp;gt;{label}}++;; \&lt;br /&gt;
                my $suffix = ($d-&amp;gt;{cmdOn} &amp;amp;&amp;amp; $d-&amp;gt;{cmdOff} &amp;amp;&amp;amp; $d-&amp;gt;{label} !~ /(an\/aus|öffnen\/schließen)/i) ? &amp;quot; (an/aus)&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
                $suffix = &amp;quot; [0-100%]&amp;quot; if ($d-&amp;gt;{type} // &amp;quot;&amp;quot;) eq &amp;quot;dimmer&amp;quot;;;\&lt;br /&gt;
                $h .= &amp;quot;• $d-&amp;gt;{label}$suffix&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            }\&lt;br /&gt;
            $h .= &#039;&amp;lt;br&amp;gt;&amp;lt;u&amp;gt;Staubsauger Räume&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;• &#039;.join(&amp;quot;, &amp;quot;, map { ucfirst($_) } sort keys %vacRooms).&#039;&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt;&#039;;;\&lt;br /&gt;
            $h =~ s/&#039;/\\&amp;quot;/g;;\&lt;br /&gt;
            my $js = &amp;quot;if((document.querySelector(&#039;input[name=\&amp;quot;fw_id\&amp;quot;]&#039;)||{}).value===&#039;$clientId&#039;){FW_okDialog(&#039;$h&#039;)}&amp;quot;;;\&lt;br /&gt;
            FW_directNotify(&amp;quot;#FHEMWEB:$_&amp;quot;, $js, &amp;quot;&amp;quot;) for devspec2array(&amp;quot;TYPE=FHEMWEB&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Unterschiede der Wege ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Feature&lt;br /&gt;
! Browser&lt;br /&gt;
! Hybrid (Atom Echo S3)&lt;br /&gt;
|-&lt;br /&gt;
| Wakeword&lt;br /&gt;
| Browser&lt;br /&gt;
| ESP (Hardware)&lt;br /&gt;
|-&lt;br /&gt;
| Push-to-Talk&lt;br /&gt;
| Ja&lt;br /&gt;
| Nein&lt;br /&gt;
|-&lt;br /&gt;
| Always-On&lt;br /&gt;
| Ja (bedingt)&lt;br /&gt;
| Ja&lt;br /&gt;
|-&lt;br /&gt;
| Architektur&lt;br /&gt;
| Software&lt;br /&gt;
| Software + Hardware&lt;br /&gt;
|-&lt;br /&gt;
| Stabilität&lt;br /&gt;
| Sehr stabil&lt;br /&gt;
| Sehr stabil (Wakeword extern)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== FireOS mit Fully Browser (Plus) ==&lt;br /&gt;
&lt;br /&gt;
Damit die Spracherkennung funktioniert:&lt;br /&gt;
&lt;br /&gt;
* Apps installieren:&lt;br /&gt;
   Google&lt;br /&gt;
   Speech Recognition &amp;amp; Synthesis&lt;br /&gt;
* FireOS:&lt;br /&gt;
   „Tastatur und Sprache“ → „Text-to-Speech“&lt;br /&gt;
* Fully Browser:&lt;br /&gt;
   &amp;lt;code&amp;gt;Enable JavaScriptInterface (PLUS)&amp;lt;/code&amp;gt; aktivieren&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40908</id>
		<title>FHEMWEB/VoiceControl: Web-STT &amp; Hardware-Wakeword</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40908"/>
		<updated>2026-04-03T09:00:47Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Voicecontrol.png|mini]]&lt;br /&gt;
&lt;br /&gt;
== VoiceControl – Sprachsteuerung via Browser und Atom Echo s3r ==&lt;br /&gt;
&lt;br /&gt;
Diese Lösung ermöglicht eine flexible Sprachsteuerung für [[FHEM]]. Sprache wird in Text (Speech-to-Text) umgewandelt und als Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; bereitgestellt. Dieses Reading kann anschließend zentral (z. B. über &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DOIF&amp;lt;/code&amp;gt;) ausgewertet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Unterstützt werden nur Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox hat leider kein Backend.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei unterschiedliche Wege zur Spracherfassung:&lt;br /&gt;
&lt;br /&gt;
* Weg 1️⃣ (Software): Browser-basierte Komplettlösung&lt;br /&gt;
* Weg 2️⃣ (Hybrid): Hardware-Wakeword + Browser-Spracherkennung&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
Dort befinden sich in Post #1 auch alle benötigten Dateien.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Grundprinzip ====&lt;br /&gt;
* Sprache → Speech-to-Text&lt;br /&gt;
* Ergebnis → Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zentrale Logik verarbeitet Befehle&lt;br /&gt;
&lt;br /&gt;
==== Betriebsarten ====&lt;br /&gt;
* Push-to-Talk (nur Browser)&lt;br /&gt;
* Always-On mit Wakeword&lt;br /&gt;
* Hardware-Wakeword (Hybrid)&lt;br /&gt;
&lt;br /&gt;
==== Rückmeldungen ====&lt;br /&gt;
* Sprachausgabe (TTS)&lt;br /&gt;
* Visuelle Bubble im Browser&lt;br /&gt;
* Optional gezielte Rückmeldung per Client-ID&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 1️⃣: Browser-Lösung (voicecontrol.js) ==&lt;br /&gt;
&lt;br /&gt;
Das Script nutzt die Google Web Speech API. Unterstützt werden Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox wird aktuell nicht unterstützt.&lt;br /&gt;
&lt;br /&gt;
=== Bedienung ===&lt;br /&gt;
&lt;br /&gt;
==== Push-to-Talk ====&lt;br /&gt;
* Button gedrückt halten (~450 ms)&lt;br /&gt;
* Direkt sprechen (kein Wakeword nötig)&lt;br /&gt;
* Befehl wird sofort verarbeitet&lt;br /&gt;
&lt;br /&gt;
==== Always-On ====&lt;br /&gt;
* Kurzer Klick aktiviert Dauerbetrieb&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung permanentes Mithören&lt;br /&gt;
&lt;br /&gt;
==== Wakeword ====&lt;br /&gt;
* Standard: &amp;lt;code&amp;gt;james&amp;lt;/code&amp;gt;&lt;br /&gt;
* Anpassbar im Script:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const wakewords = [&amp;quot;james&amp;quot;];&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ablauf:&lt;br /&gt;
# „James“ sagen&lt;br /&gt;
# System antwortet „Ja?“&lt;br /&gt;
# Zeitfenster (~6 Sekunden) für Befehl&lt;br /&gt;
# Danach automatische Verarbeitung oder Abbruch&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{ Svn_GetFile(&#039;contrib/voicecontrol.js&#039;, &#039;www/pgm2/voicecontrol.js&#039;) }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBphone JavaScripts www/pgm2/voicecontrol.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Hinweis (HTTP ohne HTTPS) ====&lt;br /&gt;
Chrome benötigt Freigabe für Mikrofon:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;chrome://flags/#unsafely-treat-insecure-origin-as-secure&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eigene URL hinzufügen&lt;br /&gt;
* Auf &amp;lt;code&amp;gt;Enabled&amp;lt;/code&amp;gt; setzen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 2️⃣: Hybrid-Lösung mit Browser + Atom Echo (voicecontrol_echo.js) ==&lt;br /&gt;
&lt;br /&gt;
Diese Variante kombiniert Hardware und Software:&lt;br /&gt;
&lt;br /&gt;
* Wakeword-Erkennung erfolgt auf dem ESP (Atom Echo s3r)&lt;br /&gt;
* Die eigentliche Sprachverarbeitung (Speech-to-Text) erfolgt im Browser&lt;br /&gt;
&lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
&lt;br /&gt;
# Wakeword wird auf dem ESP erkannt&lt;br /&gt;
# FHEM erzeugt Event (z. B. &amp;lt;code&amp;gt;james_detected&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Browser empfängt Event via WebSocket&lt;br /&gt;
# Speech-to-Text startet im Browser&lt;br /&gt;
# Ergebnis wird an FHEM übertragen&lt;br /&gt;
&lt;br /&gt;
=== Aktivierung ===&lt;br /&gt;
&lt;br /&gt;
* Kurzer Klick auf Button aktiviert/deaktiviert System&lt;br /&gt;
* Baut WebSocket-Verbindung zu FHEM auf&lt;br /&gt;
* Kein Push-to-Talk-Modus&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&lt;br /&gt;
Die Datei voicecontrol_echo.js ist hier zu finden:&lt;br /&gt;
{{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
und muss nach www/pgm2/voicecontrol_echo.js kopiert werden.&lt;br /&gt;
&lt;br /&gt;
Die echo_s3r.yaml ist für den Atom Echo s3r. Die Konfuguration ist weiter unten beschrieben.&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBtablet JavaScripts www/pgm2/voicecontrol_echo.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration im Javascript ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const DEVICE  = &amp;quot;atom_echos3r_9888e00f4280&amp;quot;;&lt;br /&gt;
const TRIGGER = &amp;quot;james_detected&amp;quot;;&lt;br /&gt;
const FHEM_IP = &amp;quot;192.168.1.76:8085&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; → FHEM-Device des ESP&lt;br /&gt;
* &amp;lt;code&amp;gt;TRIGGER&amp;lt;/code&amp;gt; → Event bei Wakeword&lt;br /&gt;
* &amp;lt;code&amp;gt;FHEM_IP&amp;lt;/code&amp;gt; → FHEM-Server&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration in der Yaml ===&lt;br /&gt;
&lt;br /&gt;
Damit sich der ESP mit eurem MQTT-Server und WLAN verbindet, müssen folgende Stellen angepasst werden.&lt;br /&gt;
&lt;br /&gt;
   wifi:&lt;br /&gt;
     ssid: &amp;quot;YOUR_SSID&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     fast_connect: true&lt;br /&gt;
&lt;br /&gt;
   mqtt:&lt;br /&gt;
     broker: 192.168.1.76&lt;br /&gt;
     port: 1884&lt;br /&gt;
     username: &amp;quot;YOUR_USERNAME&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     topic_prefix: atom_echo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wakeword (ESP / ESPHome) ===&lt;br /&gt;
&lt;br /&gt;
Das Wakeword wird direkt auf dem ESP definiert:&lt;br /&gt;
&lt;br /&gt;
* Umsetzung über ESPHome&lt;br /&gt;
* Eine große Auswahl an Wakewords sind hier zu finden:&lt;br /&gt;
   https://github.com/TaterTotterson/microWakeWords&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel, wie das Wakeword definiert wird.&lt;br /&gt;
&lt;br /&gt;
   micro_wake_word:&lt;br /&gt;
     id: mww&lt;br /&gt;
     microphone: echo_mic&lt;br /&gt;
     models:&lt;br /&gt;
       - model: &amp;quot;https://github.com/TaterTotterson/microWakeWords/raw/main/microWakeWordsV2/james.json&amp;quot;&lt;br /&gt;
         id: james_model&lt;br /&gt;
         probability_cutoff: 0.6   &lt;br /&gt;
&lt;br /&gt;
=== Kommunikation ===&lt;br /&gt;
&lt;br /&gt;
* WebSocket-Verbindung zu FHEM&lt;br /&gt;
* Lauscht auf Device-Events&lt;br /&gt;
* Automatischer Reconnect bei Abbruch&lt;br /&gt;
&lt;br /&gt;
=== Ablauf nach Wakeword ===&lt;br /&gt;
&lt;br /&gt;
# System sagt „Ja?“&lt;br /&gt;
# Browser startet SpeechRecognition&lt;br /&gt;
# Nutzer spricht Befehl&lt;br /&gt;
# Befehl wird verarbeitet&lt;br /&gt;
# Rückmeldung „Erledigt“&lt;br /&gt;
&lt;br /&gt;
== Zentrale Auswertung (Logik) ==&lt;br /&gt;
&lt;br /&gt;
Beide Wege schreiben in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;global:STT&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung erfolgt zentral über ein &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: notify ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod n_VoiceControl notify global:STT:.* {\&lt;br /&gt;
    # 1. VORBEREITUNG\&lt;br /&gt;
    my ($cleanEvent, $clientId) = $EVENT =~ /^(.*)\s\[(.*)\]$/;;\&lt;br /&gt;
    $cleanEvent //= $EVENT;;\&lt;br /&gt;
    $clientId   //= &amp;quot;unknown&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    my %lightRooms = (\&lt;br /&gt;
        &amp;quot;esszimmer&amp;quot;  =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;,       label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot; },\&lt;br /&gt;
        &amp;quot;küche&amp;quot;      =&amp;gt; { dev =&amp;gt; &amp;quot;Deckenlampe_Kue&amp;quot;,  label =&amp;gt; &amp;quot;Licht Küche&amp;quot; },\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe06_Dek&amp;quot;,         label =&amp;gt; &amp;quot;Licht Wohnzimmer&amp;quot; }\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    my %vacRooms = (\&lt;br /&gt;
        &amp;quot;arbeitszimmer&amp;quot; =&amp;gt; &amp;quot;Arbeitszimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;badezimmer&amp;quot; =&amp;gt; &amp;quot;Badezimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;esszimmer&amp;quot; =&amp;gt; &amp;quot;Esszimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;flur&amp;quot; =&amp;gt; &amp;quot;Flur&amp;quot;,\&lt;br /&gt;
        &amp;quot;küche&amp;quot; =&amp;gt; &amp;quot;Küche&amp;quot;,\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot; =&amp;gt; &amp;quot;Wohnzimmer&amp;quot;\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    my $onRegEx  = &#039;\b(an|ein|einschalten|starte|aktivier|aktiviere)\b&#039;;;\&lt;br /&gt;
    my $offRegEx = &#039;\b(aus|ausschalten|stop|stoppe|beende|deaktivier|deaktiviere)\b&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
    my @commands = split(/\s*(?:und|dann|,)\s*/, lc($cleanEvent));;\&lt;br /&gt;
\&lt;br /&gt;
    # 2. DER BEFEHLS-LOOP\&lt;br /&gt;
    foreach my $cmd_part (@commands) {\&lt;br /&gt;
        \&lt;br /&gt;
        $cmd_part =~ s/^\s+|\s+$//g;;\&lt;br /&gt;
        \&lt;br /&gt;
        my $is_on  = ($cmd_part =~ /$onRegEx/) ? 1 : 0;;\&lt;br /&gt;
        my $is_off = ($cmd_part =~ /$offRegEx/) ? 1 : 0;;\&lt;br /&gt;
\&lt;br /&gt;
        $cmd_part =~ s/\b(ich|brauche|mach|bitte|kannst du|würdest du|mal|doch|den|das|die|im|in der)\b//g;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: STAUBSAUGER ---\&lt;br /&gt;
        if ($cmd_part =~ /(reinige|sauge|putze|staubsauger|roboter)/) {\&lt;br /&gt;
            my @found = grep { $cmd_part =~ /\b$_\b/ } keys %vacRooms;;\&lt;br /&gt;
            if (@found) {\&lt;br /&gt;
                fhem(&amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish clean_segment &amp;quot; . join(&amp;quot;,&amp;quot;, map { $vacRooms{$_} } @found));;\&lt;br /&gt;
            } else {\&lt;br /&gt;
                fhem(&amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish start&amp;quot;);;\&lt;br /&gt;
            }\&lt;br /&gt;
            next;;\&lt;br /&gt;
        } elsif ($cmd_part =~ /(lade|aufladen|dock|station|home)/) {\&lt;br /&gt;
            fhem(&amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish charge&amp;quot;);;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: FERNSEHER ---\&lt;br /&gt;
        if ($cmd_part =~ /(fernseher|tv|vuplus)/) {\&lt;br /&gt;
            fhem(&amp;quot;set VuPlus &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;));;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: AMBIENTE ---\&lt;br /&gt;
        if ($cmd_part =~ /ambiente/) {\&lt;br /&gt;
            if ($cmd_part =~ /(\d+)/) {\&lt;br /&gt;
                my $b = ($1 &amp;gt; 255 ? 255 : ($1 &amp;lt; 1 ? 1 : $1));;\&lt;br /&gt;
                fhem(&amp;quot;set LampeSzeneAlle brightness $b&amp;quot;);;\&lt;br /&gt;
            } else {\&lt;br /&gt;
                fhem(&amp;quot;set LampeSzeneAlle &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;));;\&lt;br /&gt;
            }\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: AMBILIGHT ---\&lt;br /&gt;
        if ($cmd_part =~ /ambilight/) {\&lt;br /&gt;
            system(&amp;quot;sshpass -p &#039;GEHEIMESPASSWORT&#039; ssh -o StrictHostKeyChecking=no root\@192.168.1.46 &#039;/usr/share/hyperhdr/scripts/hyperhdr_toggle.sh&#039;&amp;quot;);;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: LICHT ---\&lt;br /&gt;
        my ($lightRoom) = grep { $cmd_part =~ /\b$_\b/ } keys %lightRooms;;\&lt;br /&gt;
        if ($lightRoom || $cmd_part =~ /(licht|lampe)/) {\&lt;br /&gt;
            my $dev = $lightRooms{$lightRoom}{dev} // &amp;quot;LampeSzeneAlle&amp;quot;;;\&lt;br /&gt;
            fhem(&amp;quot;set $dev &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;)) if ($is_on || $is_off);;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- HILFE (DYNAMISCH) ---\&lt;br /&gt;
        if ($cmd_part =~ /(hilfe|kommandos|übersicht)/) {\&lt;br /&gt;
\&lt;br /&gt;
            my $h = &#039;&amp;lt;div style=&amp;quot;text-align:left;;min-width:250px;;font-family:sans-serif;;&amp;quot;&amp;gt;&#039;;;\&lt;br /&gt;
            $h .= &#039;&amp;lt;b&amp;gt;Befehlsübersicht:&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
            # Licht\&lt;br /&gt;
            $h .= &#039;&amp;lt;u&amp;gt;Licht&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            for my $k (sort keys %lightRooms) {\&lt;br /&gt;
                $h .= &amp;quot;• &amp;quot;.$lightRooms{$k}{label}.&amp;quot; an/aus&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            }\&lt;br /&gt;
\&lt;br /&gt;
            # Staubsauger\&lt;br /&gt;
            $h .= &#039;&amp;lt;br&amp;gt;&amp;lt;u&amp;gt;Staubsauger&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            $h .= &amp;quot;• Sauge [Raum]&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;  Räume: &amp;quot;.join(&amp;quot;, &amp;quot;, map { ucfirst($_) } sort keys %vacRooms).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;• Lade Roberto&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
            # Sonstiges\&lt;br /&gt;
            $h .= &#039;&amp;lt;br&amp;gt;&amp;lt;u&amp;gt;Sonstiges&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            $h .= &amp;quot;• Fernseher an/aus&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;• Ambiente [an|aus|1-255]&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;• Ambilight&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
            $h .= &#039;&amp;lt;/div&amp;gt;&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
            $h =~ s/&#039;/\\&amp;quot;/g;;\&lt;br /&gt;
\&lt;br /&gt;
            my $js = &amp;quot;if((document.querySelector(&#039;input[name=\&amp;quot;fw_id\&amp;quot;]&#039;)||{}).value===&#039;$clientId&#039;){FW_okDialog(&#039;$h&#039;)}&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
            FW_directNotify(&amp;quot;#FHEMWEB:$_&amp;quot;, $js, &amp;quot;&amp;quot;)\&lt;br /&gt;
                for devspec2array(&amp;quot;TYPE=FHEMWEB&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Unterschiede der Wege ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Feature&lt;br /&gt;
! Browser&lt;br /&gt;
! Hybrid (Atom Echo S3)&lt;br /&gt;
|-&lt;br /&gt;
| Wakeword&lt;br /&gt;
| Browser&lt;br /&gt;
| ESP (Hardware)&lt;br /&gt;
|-&lt;br /&gt;
| Push-to-Talk&lt;br /&gt;
| Ja&lt;br /&gt;
| Nein&lt;br /&gt;
|-&lt;br /&gt;
| Always-On&lt;br /&gt;
| Ja (bedingt)&lt;br /&gt;
| Ja&lt;br /&gt;
|-&lt;br /&gt;
| Architektur&lt;br /&gt;
| Software&lt;br /&gt;
| Software + Hardware&lt;br /&gt;
|-&lt;br /&gt;
| Stabilität&lt;br /&gt;
| Sehr stabil&lt;br /&gt;
| Sehr stabil (Wakeword extern)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== FireOS mit Fully Browser (Plus) ==&lt;br /&gt;
&lt;br /&gt;
Damit die Spracherkennung funktioniert:&lt;br /&gt;
&lt;br /&gt;
* Apps installieren:&lt;br /&gt;
   Google&lt;br /&gt;
   Speech Recognition &amp;amp; Synthesis&lt;br /&gt;
* FireOS:&lt;br /&gt;
   „Tastatur und Sprache“ → „Text-to-Speech“&lt;br /&gt;
* Fully Browser:&lt;br /&gt;
   &amp;lt;code&amp;gt;Enable JavaScriptInterface (PLUS)&amp;lt;/code&amp;gt; aktivieren&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40907</id>
		<title>FHEMWEB/VoiceControl: Web-STT &amp; Hardware-Wakeword</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40907"/>
		<updated>2026-04-03T08:49:43Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Voicecontrol.png|mini]]&lt;br /&gt;
&lt;br /&gt;
== VoiceControl – Sprachsteuerung via Browser und Atom Echo s3r ==&lt;br /&gt;
&lt;br /&gt;
Diese Lösung ermöglicht eine flexible Sprachsteuerung für [[FHEM]]. Sprache wird in Text (Speech-to-Text) umgewandelt und als Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; bereitgestellt. Dieses Reading kann anschließend zentral (z. B. über &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DOIF&amp;lt;/code&amp;gt;) ausgewertet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Unterstützt werden nur Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox hat leider kein Backend.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei unterschiedliche Wege zur Spracherfassung:&lt;br /&gt;
&lt;br /&gt;
* Weg 1️⃣ (Software): Browser-basierte Komplettlösung&lt;br /&gt;
* Weg 2️⃣ (Hybrid): Hardware-Wakeword + Browser-Spracherkennung&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
Dort befinden sich in Post #1 auch alle benötigten Dateien.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Grundprinzip ====&lt;br /&gt;
* Sprache → Speech-to-Text&lt;br /&gt;
* Ergebnis → Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zentrale Logik verarbeitet Befehle&lt;br /&gt;
&lt;br /&gt;
==== Betriebsarten ====&lt;br /&gt;
* Push-to-Talk (nur Browser)&lt;br /&gt;
* Always-On mit Wakeword&lt;br /&gt;
* Hardware-Wakeword (Hybrid)&lt;br /&gt;
&lt;br /&gt;
==== Rückmeldungen ====&lt;br /&gt;
* Sprachausgabe (TTS)&lt;br /&gt;
* Visuelle Bubble im Browser&lt;br /&gt;
* Optional gezielte Rückmeldung per Client-ID&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 1️⃣: Browser-Lösung (voicecontrol.js) ==&lt;br /&gt;
&lt;br /&gt;
Das Script nutzt die Google Web Speech API. Unterstützt werden Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox wird aktuell nicht unterstützt.&lt;br /&gt;
&lt;br /&gt;
=== Bedienung ===&lt;br /&gt;
&lt;br /&gt;
==== Push-to-Talk ====&lt;br /&gt;
* Button gedrückt halten (~450 ms)&lt;br /&gt;
* Direkt sprechen (kein Wakeword nötig)&lt;br /&gt;
* Befehl wird sofort verarbeitet&lt;br /&gt;
&lt;br /&gt;
==== Always-On ====&lt;br /&gt;
* Kurzer Klick aktiviert Dauerbetrieb&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung permanentes Mithören&lt;br /&gt;
&lt;br /&gt;
==== Wakeword ====&lt;br /&gt;
* Standard: &amp;lt;code&amp;gt;james&amp;lt;/code&amp;gt;&lt;br /&gt;
* Anpassbar im Script:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const wakewords = [&amp;quot;james&amp;quot;];&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ablauf:&lt;br /&gt;
# „James“ sagen&lt;br /&gt;
# System antwortet „Ja?“&lt;br /&gt;
# Zeitfenster (~6 Sekunden) für Befehl&lt;br /&gt;
# Danach automatische Verarbeitung oder Abbruch&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{ Svn_GetFile(&#039;contrib/voicecontrol.js&#039;, &#039;www/pgm2/voicecontrol.js&#039;) }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBphone JavaScripts pgm2/voicecontrol.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Hinweis (HTTP ohne HTTPS) ====&lt;br /&gt;
Chrome benötigt Freigabe für Mikrofon:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;chrome://flags/#unsafely-treat-insecure-origin-as-secure&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eigene URL hinzufügen&lt;br /&gt;
* Auf &amp;lt;code&amp;gt;Enabled&amp;lt;/code&amp;gt; setzen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 2️⃣: Hybrid-Lösung mit Browser + Atom Echo (voicecontrol_echo.js) ==&lt;br /&gt;
&lt;br /&gt;
Diese Variante kombiniert Hardware und Software:&lt;br /&gt;
&lt;br /&gt;
* Wakeword-Erkennung erfolgt auf dem ESP (Atom Echo s3r)&lt;br /&gt;
* Die eigentliche Sprachverarbeitung (Speech-to-Text) erfolgt im Browser&lt;br /&gt;
&lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
&lt;br /&gt;
# Wakeword wird auf dem ESP erkannt&lt;br /&gt;
# FHEM erzeugt Event (z. B. &amp;lt;code&amp;gt;james_detected&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Browser empfängt Event via WebSocket&lt;br /&gt;
# Speech-to-Text startet im Browser&lt;br /&gt;
# Ergebnis wird an FHEM übertragen&lt;br /&gt;
&lt;br /&gt;
=== Aktivierung ===&lt;br /&gt;
&lt;br /&gt;
* Kurzer Klick auf Button aktiviert/deaktiviert System&lt;br /&gt;
* Baut WebSocket-Verbindung zu FHEM auf&lt;br /&gt;
* Kein Push-to-Talk-Modus&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&lt;br /&gt;
Die Datei voicecontrol_echo.js ist hier zu finden:&lt;br /&gt;
{{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
und muss nach www/pgm2/voicecontrol_echo.js kopiert werden.&lt;br /&gt;
&lt;br /&gt;
Die echo_s3r.yaml ist für den Atom Echo s3r. Die Konfuguration ist weiter unten beschrieben.&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBtablet JavaScripts pgm2/voicecontrol_echo.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration im Javascript ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const DEVICE  = &amp;quot;atom_echos3r_9888e00f4280&amp;quot;;&lt;br /&gt;
const TRIGGER = &amp;quot;james_detected&amp;quot;;&lt;br /&gt;
const FHEM_IP = &amp;quot;192.168.1.76:8085&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; → FHEM-Device des ESP&lt;br /&gt;
* &amp;lt;code&amp;gt;TRIGGER&amp;lt;/code&amp;gt; → Event bei Wakeword&lt;br /&gt;
* &amp;lt;code&amp;gt;FHEM_IP&amp;lt;/code&amp;gt; → FHEM-Server&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration in der Yaml ===&lt;br /&gt;
&lt;br /&gt;
Damit sich der ESP mit eurem MQTT-Server und WLAN verbindet, müssen folgende Stellen angepasst werden.&lt;br /&gt;
&lt;br /&gt;
   wifi:&lt;br /&gt;
     ssid: &amp;quot;YOUR_SSID&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     fast_connect: true&lt;br /&gt;
&lt;br /&gt;
   mqtt:&lt;br /&gt;
     broker: 192.168.1.76&lt;br /&gt;
     port: 1884&lt;br /&gt;
     username: &amp;quot;YOUR_USERNAME&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     topic_prefix: atom_echo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wakeword (ESP / ESPHome) ===&lt;br /&gt;
&lt;br /&gt;
Das Wakeword wird direkt auf dem ESP definiert:&lt;br /&gt;
&lt;br /&gt;
* Umsetzung über ESPHome&lt;br /&gt;
* Eine große Auswahl an Wakewords sind hier zu finden:&lt;br /&gt;
   https://github.com/TaterTotterson/microWakeWords&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel, wie das Wakeword definiert wird.&lt;br /&gt;
&lt;br /&gt;
   micro_wake_word:&lt;br /&gt;
     id: mww&lt;br /&gt;
     microphone: echo_mic&lt;br /&gt;
     models:&lt;br /&gt;
       - model: &amp;quot;https://github.com/TaterTotterson/microWakeWords/raw/main/microWakeWordsV2/james.json&amp;quot;&lt;br /&gt;
         id: james_model&lt;br /&gt;
         probability_cutoff: 0.6   &lt;br /&gt;
&lt;br /&gt;
=== Kommunikation ===&lt;br /&gt;
&lt;br /&gt;
* WebSocket-Verbindung zu FHEM&lt;br /&gt;
* Lauscht auf Device-Events&lt;br /&gt;
* Automatischer Reconnect bei Abbruch&lt;br /&gt;
&lt;br /&gt;
=== Ablauf nach Wakeword ===&lt;br /&gt;
&lt;br /&gt;
# System sagt „Ja?“&lt;br /&gt;
# Browser startet SpeechRecognition&lt;br /&gt;
# Nutzer spricht Befehl&lt;br /&gt;
# Befehl wird verarbeitet&lt;br /&gt;
# Rückmeldung „Erledigt“&lt;br /&gt;
&lt;br /&gt;
== Zentrale Auswertung (Logik) ==&lt;br /&gt;
&lt;br /&gt;
Beide Wege schreiben in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;global:STT&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung erfolgt zentral über ein &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: notify ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod n_VoiceControl notify global:STT:.* {\&lt;br /&gt;
    # 1. VORBEREITUNG\&lt;br /&gt;
    my ($cleanEvent, $clientId) = $EVENT =~ /^(.*)\s\[(.*)\]$/;;\&lt;br /&gt;
    $cleanEvent //= $EVENT;;\&lt;br /&gt;
    $clientId   //= &amp;quot;unknown&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    my %lightRooms = (\&lt;br /&gt;
        &amp;quot;esszimmer&amp;quot;  =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;,       label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot; },\&lt;br /&gt;
        &amp;quot;küche&amp;quot;      =&amp;gt; { dev =&amp;gt; &amp;quot;Deckenlampe_Kue&amp;quot;,  label =&amp;gt; &amp;quot;Licht Küche&amp;quot; },\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe06_Dek&amp;quot;,         label =&amp;gt; &amp;quot;Licht Wohnzimmer&amp;quot; }\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    my %vacRooms = (\&lt;br /&gt;
        &amp;quot;arbeitszimmer&amp;quot; =&amp;gt; &amp;quot;Arbeitszimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;badezimmer&amp;quot; =&amp;gt; &amp;quot;Badezimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;esszimmer&amp;quot; =&amp;gt; &amp;quot;Esszimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;flur&amp;quot; =&amp;gt; &amp;quot;Flur&amp;quot;,\&lt;br /&gt;
        &amp;quot;küche&amp;quot; =&amp;gt; &amp;quot;Küche&amp;quot;,\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot; =&amp;gt; &amp;quot;Wohnzimmer&amp;quot;\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    my $onRegEx  = &#039;\b(an|ein|einschalten|starte|aktivier|aktiviere)\b&#039;;;\&lt;br /&gt;
    my $offRegEx = &#039;\b(aus|ausschalten|stop|stoppe|beende|deaktivier|deaktiviere)\b&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
    my @commands = split(/\s*(?:und|dann|,)\s*/, lc($cleanEvent));;\&lt;br /&gt;
\&lt;br /&gt;
    # 2. DER BEFEHLS-LOOP\&lt;br /&gt;
    foreach my $cmd_part (@commands) {\&lt;br /&gt;
        \&lt;br /&gt;
        $cmd_part =~ s/^\s+|\s+$//g;;\&lt;br /&gt;
        \&lt;br /&gt;
        my $is_on  = ($cmd_part =~ /$onRegEx/) ? 1 : 0;;\&lt;br /&gt;
        my $is_off = ($cmd_part =~ /$offRegEx/) ? 1 : 0;;\&lt;br /&gt;
\&lt;br /&gt;
        $cmd_part =~ s/\b(ich|brauche|mach|bitte|kannst du|würdest du|mal|doch|den|das|die|im|in der)\b//g;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: STAUBSAUGER ---\&lt;br /&gt;
        if ($cmd_part =~ /(reinige|sauge|putze|staubsauger|roboter)/) {\&lt;br /&gt;
            my @found = grep { $cmd_part =~ /\b$_\b/ } keys %vacRooms;;\&lt;br /&gt;
            if (@found) {\&lt;br /&gt;
                fhem(&amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish clean_segment &amp;quot; . join(&amp;quot;,&amp;quot;, map { $vacRooms{$_} } @found));;\&lt;br /&gt;
            } else {\&lt;br /&gt;
                fhem(&amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish start&amp;quot;);;\&lt;br /&gt;
            }\&lt;br /&gt;
            next;;\&lt;br /&gt;
        } elsif ($cmd_part =~ /(lade|aufladen|dock|station|home)/) {\&lt;br /&gt;
            fhem(&amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish charge&amp;quot;);;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: FERNSEHER ---\&lt;br /&gt;
        if ($cmd_part =~ /(fernseher|tv|vuplus)/) {\&lt;br /&gt;
            fhem(&amp;quot;set VuPlus &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;));;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: AMBIENTE ---\&lt;br /&gt;
        if ($cmd_part =~ /ambiente/) {\&lt;br /&gt;
            if ($cmd_part =~ /(\d+)/) {\&lt;br /&gt;
                my $b = ($1 &amp;gt; 255 ? 255 : ($1 &amp;lt; 1 ? 1 : $1));;\&lt;br /&gt;
                fhem(&amp;quot;set LampeSzeneAlle brightness $b&amp;quot;);;\&lt;br /&gt;
            } else {\&lt;br /&gt;
                fhem(&amp;quot;set LampeSzeneAlle &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;));;\&lt;br /&gt;
            }\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: AMBILIGHT ---\&lt;br /&gt;
        if ($cmd_part =~ /ambilight/) {\&lt;br /&gt;
            system(&amp;quot;sshpass -p &#039;GEHEIMESPASSWORT&#039; ssh -o StrictHostKeyChecking=no root\@192.168.1.46 &#039;/usr/share/hyperhdr/scripts/hyperhdr_toggle.sh&#039;&amp;quot;);;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: LICHT ---\&lt;br /&gt;
        my ($lightRoom) = grep { $cmd_part =~ /\b$_\b/ } keys %lightRooms;;\&lt;br /&gt;
        if ($lightRoom || $cmd_part =~ /(licht|lampe)/) {\&lt;br /&gt;
            my $dev = $lightRooms{$lightRoom}{dev} // &amp;quot;LampeSzeneAlle&amp;quot;;;\&lt;br /&gt;
            fhem(&amp;quot;set $dev &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;)) if ($is_on || $is_off);;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- HILFE (DYNAMISCH) ---\&lt;br /&gt;
        if ($cmd_part =~ /(hilfe|kommandos|übersicht)/) {\&lt;br /&gt;
\&lt;br /&gt;
            my $h = &#039;&amp;lt;div style=&amp;quot;text-align:left;;min-width:250px;;font-family:sans-serif;;&amp;quot;&amp;gt;&#039;;;\&lt;br /&gt;
            $h .= &#039;&amp;lt;b&amp;gt;Befehlsübersicht:&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
            # Licht\&lt;br /&gt;
            $h .= &#039;&amp;lt;u&amp;gt;Licht&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            for my $k (sort keys %lightRooms) {\&lt;br /&gt;
                $h .= &amp;quot;• &amp;quot;.$lightRooms{$k}{label}.&amp;quot; an/aus&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            }\&lt;br /&gt;
\&lt;br /&gt;
            # Staubsauger\&lt;br /&gt;
            $h .= &#039;&amp;lt;br&amp;gt;&amp;lt;u&amp;gt;Staubsauger&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            $h .= &amp;quot;• Sauge [Raum]&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;  Räume: &amp;quot;.join(&amp;quot;, &amp;quot;, map { ucfirst($_) } sort keys %vacRooms).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;• Lade Roberto&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
            # Sonstiges\&lt;br /&gt;
            $h .= &#039;&amp;lt;br&amp;gt;&amp;lt;u&amp;gt;Sonstiges&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            $h .= &amp;quot;• Fernseher an/aus&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;• Ambiente [an|aus|1-255]&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;• Ambilight&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
            $h .= &#039;&amp;lt;/div&amp;gt;&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
            $h =~ s/&#039;/\\&amp;quot;/g;;\&lt;br /&gt;
\&lt;br /&gt;
            my $js = &amp;quot;if((document.querySelector(&#039;input[name=\&amp;quot;fw_id\&amp;quot;]&#039;)||{}).value===&#039;$clientId&#039;){FW_okDialog(&#039;$h&#039;)}&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
            FW_directNotify(&amp;quot;#FHEMWEB:$_&amp;quot;, $js, &amp;quot;&amp;quot;)\&lt;br /&gt;
                for devspec2array(&amp;quot;TYPE=FHEMWEB&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Unterschiede der Wege ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Feature&lt;br /&gt;
! Browser&lt;br /&gt;
! Hybrid (Atom Echo S3)&lt;br /&gt;
|-&lt;br /&gt;
| Wakeword&lt;br /&gt;
| Browser&lt;br /&gt;
| ESP (Hardware)&lt;br /&gt;
|-&lt;br /&gt;
| Push-to-Talk&lt;br /&gt;
| Ja&lt;br /&gt;
| Nein&lt;br /&gt;
|-&lt;br /&gt;
| Always-On&lt;br /&gt;
| Ja (bedingt)&lt;br /&gt;
| Ja&lt;br /&gt;
|-&lt;br /&gt;
| Architektur&lt;br /&gt;
| Software&lt;br /&gt;
| Software + Hardware&lt;br /&gt;
|-&lt;br /&gt;
| Stabilität&lt;br /&gt;
| Sehr stabil&lt;br /&gt;
| Sehr stabil (Wakeword extern)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== FireOS mit Fully Browser (Plus) ==&lt;br /&gt;
&lt;br /&gt;
Damit die Spracherkennung funktioniert:&lt;br /&gt;
&lt;br /&gt;
* Apps installieren:&lt;br /&gt;
   Google&lt;br /&gt;
   Speech Recognition &amp;amp; Synthesis&lt;br /&gt;
* FireOS:&lt;br /&gt;
   „Tastatur und Sprache“ → „Text-to-Speech“&lt;br /&gt;
* Fully Browser:&lt;br /&gt;
   &amp;lt;code&amp;gt;Enable JavaScriptInterface (PLUS)&amp;lt;/code&amp;gt; aktivieren&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40906</id>
		<title>FHEMWEB/VoiceControl: Web-STT &amp; Hardware-Wakeword</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40906"/>
		<updated>2026-04-03T08:38:08Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Voicecontrol.png|mini]]&lt;br /&gt;
&lt;br /&gt;
== VoiceControl – Sprachsteuerung via Browser und Atom Echo s3r ==&lt;br /&gt;
&lt;br /&gt;
Diese Lösung ermöglicht eine flexible Sprachsteuerung für [[FHEM]]. Sprache wird in Text (Speech-to-Text) umgewandelt und als Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; bereitgestellt. Dieses Reading kann anschließend zentral (z. B. über &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DOIF&amp;lt;/code&amp;gt;) ausgewertet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Unterstützt werden nur Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox hat leider kein Backend.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei unterschiedliche Wege zur Spracherfassung:&lt;br /&gt;
&lt;br /&gt;
* Weg 1️⃣ (Software): Browser-basierte Komplettlösung&lt;br /&gt;
* Weg 2️⃣ (Hybrid): Hardware-Wakeword + Browser-Spracherkennung&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
Dort befinden sich in Post #1 auch alle benötigten Dateien.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Grundprinzip ====&lt;br /&gt;
* Sprache → Speech-to-Text&lt;br /&gt;
* Ergebnis → Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zentrale Logik verarbeitet Befehle&lt;br /&gt;
&lt;br /&gt;
==== Betriebsarten ====&lt;br /&gt;
* Push-to-Talk (nur Browser)&lt;br /&gt;
* Always-On mit Wakeword&lt;br /&gt;
* Hardware-Wakeword (Hybrid)&lt;br /&gt;
&lt;br /&gt;
==== Rückmeldungen ====&lt;br /&gt;
* Sprachausgabe (TTS)&lt;br /&gt;
* Visuelle Bubble im Browser&lt;br /&gt;
* Optional gezielte Rückmeldung per Client-ID&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 1️⃣: Browser-Lösung (voicecontrol.js) ==&lt;br /&gt;
&lt;br /&gt;
Das Script nutzt die Google Web Speech API. Unterstützt werden Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox wird aktuell nicht unterstützt.&lt;br /&gt;
&lt;br /&gt;
=== Bedienung ===&lt;br /&gt;
&lt;br /&gt;
==== Push-to-Talk ====&lt;br /&gt;
* Button gedrückt halten (~450 ms)&lt;br /&gt;
* Direkt sprechen (kein Wakeword nötig)&lt;br /&gt;
* Befehl wird sofort verarbeitet&lt;br /&gt;
&lt;br /&gt;
==== Always-On ====&lt;br /&gt;
* Kurzer Klick aktiviert Dauerbetrieb&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung permanentes Mithören&lt;br /&gt;
&lt;br /&gt;
==== Wakeword ====&lt;br /&gt;
* Standard: &amp;lt;code&amp;gt;james&amp;lt;/code&amp;gt;&lt;br /&gt;
* Anpassbar im Script:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const wakewords = [&amp;quot;james&amp;quot;];&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ablauf:&lt;br /&gt;
# „James“ sagen&lt;br /&gt;
# System antwortet „Ja?“&lt;br /&gt;
# Zeitfenster (~6 Sekunden) für Befehl&lt;br /&gt;
# Danach automatische Verarbeitung oder Abbruch&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{ Svn_GetFile(&#039;contrib/voicecontrol.js&#039;, &#039;www/pgm2/voicecontrol.js&#039;) }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBphone JavaScripts pgm2/voicecontrol.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Hinweis (HTTP ohne HTTPS) ====&lt;br /&gt;
Chrome benötigt Freigabe für Mikrofon:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;chrome://flags/#unsafely-treat-insecure-origin-as-secure&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eigene URL hinzufügen&lt;br /&gt;
* Auf &amp;lt;code&amp;gt;Enabled&amp;lt;/code&amp;gt; setzen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 2️⃣: Hybrid-Lösung mit Browser + Atom Echo (voicecontrol_echo.js) ==&lt;br /&gt;
&lt;br /&gt;
Diese Variante kombiniert Hardware und Software:&lt;br /&gt;
&lt;br /&gt;
* Wakeword-Erkennung erfolgt auf dem ESP (Atom Echo s3r)&lt;br /&gt;
* Die eigentliche Sprachverarbeitung (Speech-to-Text) erfolgt im Browser&lt;br /&gt;
&lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
&lt;br /&gt;
# Wakeword wird auf dem ESP erkannt&lt;br /&gt;
# FHEM erzeugt Event (z. B. &amp;lt;code&amp;gt;james_detected&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Browser empfängt Event via WebSocket&lt;br /&gt;
# Speech-to-Text startet im Browser&lt;br /&gt;
# Ergebnis wird an FHEM übertragen&lt;br /&gt;
&lt;br /&gt;
=== Aktivierung ===&lt;br /&gt;
&lt;br /&gt;
* Kurzer Klick auf Button aktiviert/deaktiviert System&lt;br /&gt;
* Baut WebSocket-Verbindung zu FHEM auf&lt;br /&gt;
* Kein Push-to-Talk-Modus&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration im Javascript ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const DEVICE  = &amp;quot;atom_echos3r_9888e00f4280&amp;quot;;&lt;br /&gt;
const TRIGGER = &amp;quot;james_detected&amp;quot;;&lt;br /&gt;
const FHEM_IP = &amp;quot;192.168.1.76:8085&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; → FHEM-Device des ESP&lt;br /&gt;
* &amp;lt;code&amp;gt;TRIGGER&amp;lt;/code&amp;gt; → Event bei Wakeword&lt;br /&gt;
* &amp;lt;code&amp;gt;FHEM_IP&amp;lt;/code&amp;gt; → FHEM-Server&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration in der Yaml ===&lt;br /&gt;
&lt;br /&gt;
Damit sich der ESP mit eurem MQTT-Server und WLAN verbindet, müssen folgende Stellen angepasst werden.&lt;br /&gt;
&lt;br /&gt;
   wifi:&lt;br /&gt;
     ssid: &amp;quot;YOUR_SSID&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     fast_connect: true&lt;br /&gt;
&lt;br /&gt;
   mqtt:&lt;br /&gt;
     broker: 192.168.1.76&lt;br /&gt;
     port: 1884&lt;br /&gt;
     username: &amp;quot;YOUR_USERNAME&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     topic_prefix: atom_echo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wakeword (ESP / ESPHome) ===&lt;br /&gt;
&lt;br /&gt;
Das Wakeword wird direkt auf dem ESP definiert:&lt;br /&gt;
&lt;br /&gt;
* Umsetzung über ESPHome&lt;br /&gt;
* Eine große Auswahl an Wakewords sind hier zu finden:&lt;br /&gt;
   https://github.com/TaterTotterson/microWakeWords&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel, wie das Wakeword definiert wird.&lt;br /&gt;
&lt;br /&gt;
   micro_wake_word:&lt;br /&gt;
     id: mww&lt;br /&gt;
     microphone: echo_mic&lt;br /&gt;
     models:&lt;br /&gt;
       - model: &amp;quot;https://github.com/TaterTotterson/microWakeWords/raw/main/microWakeWordsV2/james.json&amp;quot;&lt;br /&gt;
         id: james_model&lt;br /&gt;
         probability_cutoff: 0.6   &lt;br /&gt;
&lt;br /&gt;
=== Kommunikation ===&lt;br /&gt;
&lt;br /&gt;
* WebSocket-Verbindung zu FHEM&lt;br /&gt;
* Lauscht auf Device-Events&lt;br /&gt;
* Automatischer Reconnect bei Abbruch&lt;br /&gt;
&lt;br /&gt;
=== Ablauf nach Wakeword ===&lt;br /&gt;
&lt;br /&gt;
# System sagt „Ja?“&lt;br /&gt;
# Browser startet SpeechRecognition&lt;br /&gt;
# Nutzer spricht Befehl&lt;br /&gt;
# Befehl wird verarbeitet&lt;br /&gt;
# Rückmeldung „Erledigt“&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
siehe oben wie unter Punkt 1.&lt;br /&gt;
&lt;br /&gt;
== Zentrale Auswertung (Logik) ==&lt;br /&gt;
&lt;br /&gt;
Beide Wege schreiben in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;global:STT&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung erfolgt zentral über ein &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: notify ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod n_VoiceControl notify global:STT:.* {\&lt;br /&gt;
    # 1. VORBEREITUNG\&lt;br /&gt;
    my ($cleanEvent, $clientId) = $EVENT =~ /^(.*)\s\[(.*)\]$/;;\&lt;br /&gt;
    $cleanEvent //= $EVENT;;\&lt;br /&gt;
    $clientId   //= &amp;quot;unknown&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    my %lightRooms = (\&lt;br /&gt;
        &amp;quot;esszimmer&amp;quot;  =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;,       label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot; },\&lt;br /&gt;
        &amp;quot;küche&amp;quot;      =&amp;gt; { dev =&amp;gt; &amp;quot;Deckenlampe_Kue&amp;quot;,  label =&amp;gt; &amp;quot;Licht Küche&amp;quot; },\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe06_Dek&amp;quot;,         label =&amp;gt; &amp;quot;Licht Wohnzimmer&amp;quot; }\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    my %vacRooms = (\&lt;br /&gt;
        &amp;quot;arbeitszimmer&amp;quot; =&amp;gt; &amp;quot;Arbeitszimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;badezimmer&amp;quot; =&amp;gt; &amp;quot;Badezimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;esszimmer&amp;quot; =&amp;gt; &amp;quot;Esszimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;flur&amp;quot; =&amp;gt; &amp;quot;Flur&amp;quot;,\&lt;br /&gt;
        &amp;quot;küche&amp;quot; =&amp;gt; &amp;quot;Küche&amp;quot;,\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot; =&amp;gt; &amp;quot;Wohnzimmer&amp;quot;\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    my $onRegEx  = &#039;\b(an|ein|einschalten|starte|aktivier|aktiviere)\b&#039;;;\&lt;br /&gt;
    my $offRegEx = &#039;\b(aus|ausschalten|stop|stoppe|beende|deaktivier|deaktiviere)\b&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
    my @commands = split(/\s*(?:und|dann|,)\s*/, lc($cleanEvent));;\&lt;br /&gt;
\&lt;br /&gt;
    # 2. DER BEFEHLS-LOOP\&lt;br /&gt;
    foreach my $cmd_part (@commands) {\&lt;br /&gt;
        \&lt;br /&gt;
        $cmd_part =~ s/^\s+|\s+$//g;;\&lt;br /&gt;
        \&lt;br /&gt;
        my $is_on  = ($cmd_part =~ /$onRegEx/) ? 1 : 0;;\&lt;br /&gt;
        my $is_off = ($cmd_part =~ /$offRegEx/) ? 1 : 0;;\&lt;br /&gt;
\&lt;br /&gt;
        $cmd_part =~ s/\b(ich|brauche|mach|bitte|kannst du|würdest du|mal|doch|den|das|die|im|in der)\b//g;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: STAUBSAUGER ---\&lt;br /&gt;
        if ($cmd_part =~ /(reinige|sauge|putze|staubsauger|roboter)/) {\&lt;br /&gt;
            my @found = grep { $cmd_part =~ /\b$_\b/ } keys %vacRooms;;\&lt;br /&gt;
            if (@found) {\&lt;br /&gt;
                fhem(&amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish clean_segment &amp;quot; . join(&amp;quot;,&amp;quot;, map { $vacRooms{$_} } @found));;\&lt;br /&gt;
            } else {\&lt;br /&gt;
                fhem(&amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish start&amp;quot;);;\&lt;br /&gt;
            }\&lt;br /&gt;
            next;;\&lt;br /&gt;
        } elsif ($cmd_part =~ /(lade|aufladen|dock|station|home)/) {\&lt;br /&gt;
            fhem(&amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish charge&amp;quot;);;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: FERNSEHER ---\&lt;br /&gt;
        if ($cmd_part =~ /(fernseher|tv|vuplus)/) {\&lt;br /&gt;
            fhem(&amp;quot;set VuPlus &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;));;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: AMBIENTE ---\&lt;br /&gt;
        if ($cmd_part =~ /ambiente/) {\&lt;br /&gt;
            if ($cmd_part =~ /(\d+)/) {\&lt;br /&gt;
                my $b = ($1 &amp;gt; 255 ? 255 : ($1 &amp;lt; 1 ? 1 : $1));;\&lt;br /&gt;
                fhem(&amp;quot;set LampeSzeneAlle brightness $b&amp;quot;);;\&lt;br /&gt;
            } else {\&lt;br /&gt;
                fhem(&amp;quot;set LampeSzeneAlle &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;));;\&lt;br /&gt;
            }\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: AMBILIGHT ---\&lt;br /&gt;
        if ($cmd_part =~ /ambilight/) {\&lt;br /&gt;
            system(&amp;quot;sshpass -p &#039;GEHEIMESPASSWORT&#039; ssh -o StrictHostKeyChecking=no root\@192.168.1.46 &#039;/usr/share/hyperhdr/scripts/hyperhdr_toggle.sh&#039;&amp;quot;);;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: LICHT ---\&lt;br /&gt;
        my ($lightRoom) = grep { $cmd_part =~ /\b$_\b/ } keys %lightRooms;;\&lt;br /&gt;
        if ($lightRoom || $cmd_part =~ /(licht|lampe)/) {\&lt;br /&gt;
            my $dev = $lightRooms{$lightRoom}{dev} // &amp;quot;LampeSzeneAlle&amp;quot;;;\&lt;br /&gt;
            fhem(&amp;quot;set $dev &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;)) if ($is_on || $is_off);;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- HILFE (DYNAMISCH) ---\&lt;br /&gt;
        if ($cmd_part =~ /(hilfe|kommandos|übersicht)/) {\&lt;br /&gt;
\&lt;br /&gt;
            my $h = &#039;&amp;lt;div style=&amp;quot;text-align:left;;min-width:250px;;font-family:sans-serif;;&amp;quot;&amp;gt;&#039;;;\&lt;br /&gt;
            $h .= &#039;&amp;lt;b&amp;gt;Befehlsübersicht:&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
            # Licht\&lt;br /&gt;
            $h .= &#039;&amp;lt;u&amp;gt;Licht&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            for my $k (sort keys %lightRooms) {\&lt;br /&gt;
                $h .= &amp;quot;• &amp;quot;.$lightRooms{$k}{label}.&amp;quot; an/aus&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            }\&lt;br /&gt;
\&lt;br /&gt;
            # Staubsauger\&lt;br /&gt;
            $h .= &#039;&amp;lt;br&amp;gt;&amp;lt;u&amp;gt;Staubsauger&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            $h .= &amp;quot;• Sauge [Raum]&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;  Räume: &amp;quot;.join(&amp;quot;, &amp;quot;, map { ucfirst($_) } sort keys %vacRooms).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;• Lade Roberto&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
            # Sonstiges\&lt;br /&gt;
            $h .= &#039;&amp;lt;br&amp;gt;&amp;lt;u&amp;gt;Sonstiges&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            $h .= &amp;quot;• Fernseher an/aus&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;• Ambiente [an|aus|1-255]&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;• Ambilight&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
            $h .= &#039;&amp;lt;/div&amp;gt;&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
            $h =~ s/&#039;/\\&amp;quot;/g;;\&lt;br /&gt;
\&lt;br /&gt;
            my $js = &amp;quot;if((document.querySelector(&#039;input[name=\&amp;quot;fw_id\&amp;quot;]&#039;)||{}).value===&#039;$clientId&#039;){FW_okDialog(&#039;$h&#039;)}&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
            FW_directNotify(&amp;quot;#FHEMWEB:$_&amp;quot;, $js, &amp;quot;&amp;quot;)\&lt;br /&gt;
                for devspec2array(&amp;quot;TYPE=FHEMWEB&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Unterschiede der Wege ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Feature&lt;br /&gt;
! Browser&lt;br /&gt;
! Hybrid (Atom Echo S3)&lt;br /&gt;
|-&lt;br /&gt;
| Wakeword&lt;br /&gt;
| Browser&lt;br /&gt;
| ESP (Hardware)&lt;br /&gt;
|-&lt;br /&gt;
| Push-to-Talk&lt;br /&gt;
| Ja&lt;br /&gt;
| Nein&lt;br /&gt;
|-&lt;br /&gt;
| Always-On&lt;br /&gt;
| Ja (bedingt)&lt;br /&gt;
| Ja&lt;br /&gt;
|-&lt;br /&gt;
| Architektur&lt;br /&gt;
| Software&lt;br /&gt;
| Software + Hardware&lt;br /&gt;
|-&lt;br /&gt;
| Stabilität&lt;br /&gt;
| Sehr stabil&lt;br /&gt;
| Sehr stabil (Wakeword extern)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== FireOS mit Fully Browser (Plus) ==&lt;br /&gt;
&lt;br /&gt;
Damit die Spracherkennung funktioniert:&lt;br /&gt;
&lt;br /&gt;
* Apps installieren:&lt;br /&gt;
   Google&lt;br /&gt;
   Speech Recognition &amp;amp; Synthesis&lt;br /&gt;
* FireOS:&lt;br /&gt;
   „Tastatur und Sprache“ → „Text-to-Speech“&lt;br /&gt;
* Fully Browser:&lt;br /&gt;
   &amp;lt;code&amp;gt;Enable JavaScriptInterface (PLUS)&amp;lt;/code&amp;gt; aktivieren&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40905</id>
		<title>FHEMWEB/VoiceControl: Web-STT &amp; Hardware-Wakeword</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40905"/>
		<updated>2026-04-03T08:34:50Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:VoiceControl_Logo.png|Mini|rechts]]&lt;br /&gt;
&lt;br /&gt;
[[Datei:Voicecontrol.png|mini]]&lt;br /&gt;
&lt;br /&gt;
== VoiceControl – Sprachsteuerung via Browser und Atom Echo s3r ==&lt;br /&gt;
&lt;br /&gt;
Diese Lösung ermöglicht eine flexible Sprachsteuerung für [[FHEM]]. Sprache wird in Text (Speech-to-Text) umgewandelt und als Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; bereitgestellt. Dieses Reading kann anschließend zentral (z. B. über &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DOIF&amp;lt;/code&amp;gt;) ausgewertet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Unterstützt werden nur Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox hat leider kein Backend.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei unterschiedliche Wege zur Spracherfassung:&lt;br /&gt;
&lt;br /&gt;
* Weg 1️⃣ (Software): Browser-basierte Komplettlösung&lt;br /&gt;
* Weg 2️⃣ (Hybrid): Hardware-Wakeword + Browser-Spracherkennung&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
Dort befinden sich in Post #1 auch alle benötigten Dateien.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Grundprinzip ====&lt;br /&gt;
* Sprache → Speech-to-Text&lt;br /&gt;
* Ergebnis → Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zentrale Logik verarbeitet Befehle&lt;br /&gt;
&lt;br /&gt;
==== Betriebsarten ====&lt;br /&gt;
* Push-to-Talk (nur Browser)&lt;br /&gt;
* Always-On mit Wakeword&lt;br /&gt;
* Hardware-Wakeword (Hybrid)&lt;br /&gt;
&lt;br /&gt;
==== Rückmeldungen ====&lt;br /&gt;
* Sprachausgabe (TTS)&lt;br /&gt;
* Visuelle Bubble im Browser&lt;br /&gt;
* Optional gezielte Rückmeldung per Client-ID&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 1️⃣: Browser-Lösung (voicecontrol.js) ==&lt;br /&gt;
&lt;br /&gt;
Das Script nutzt die Google Web Speech API. Unterstützt werden Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox wird aktuell nicht unterstützt.&lt;br /&gt;
&lt;br /&gt;
=== Bedienung ===&lt;br /&gt;
&lt;br /&gt;
==== Push-to-Talk ====&lt;br /&gt;
* Button gedrückt halten (~450 ms)&lt;br /&gt;
* Direkt sprechen (kein Wakeword nötig)&lt;br /&gt;
* Befehl wird sofort verarbeitet&lt;br /&gt;
&lt;br /&gt;
==== Always-On ====&lt;br /&gt;
* Kurzer Klick aktiviert Dauerbetrieb&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung permanentes Mithören&lt;br /&gt;
&lt;br /&gt;
==== Wakeword ====&lt;br /&gt;
* Standard: &amp;lt;code&amp;gt;james&amp;lt;/code&amp;gt;&lt;br /&gt;
* Anpassbar im Script:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const wakewords = [&amp;quot;james&amp;quot;];&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ablauf:&lt;br /&gt;
# „James“ sagen&lt;br /&gt;
# System antwortet „Ja?“&lt;br /&gt;
# Zeitfenster (~6 Sekunden) für Befehl&lt;br /&gt;
# Danach automatische Verarbeitung oder Abbruch&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{ Svn_GetFile(&#039;contrib/voicecontrol.js&#039;, &#039;www/pgm2/voicecontrol.js&#039;) }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBphone JavaScripts pgm2/voicecontrol.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Hinweis (HTTP ohne HTTPS) ====&lt;br /&gt;
Chrome benötigt Freigabe für Mikrofon:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;chrome://flags/#unsafely-treat-insecure-origin-as-secure&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eigene URL hinzufügen&lt;br /&gt;
* Auf &amp;lt;code&amp;gt;Enabled&amp;lt;/code&amp;gt; setzen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 2️⃣: Hybrid-Lösung mit Browser + Atom Echo (voicecontrol_echo.js) ==&lt;br /&gt;
&lt;br /&gt;
Diese Variante kombiniert Hardware und Software:&lt;br /&gt;
&lt;br /&gt;
* Wakeword-Erkennung erfolgt auf dem ESP (Atom Echo s3r)&lt;br /&gt;
* Die eigentliche Sprachverarbeitung (Speech-to-Text) erfolgt im Browser&lt;br /&gt;
&lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
&lt;br /&gt;
# Wakeword wird auf dem ESP erkannt&lt;br /&gt;
# FHEM erzeugt Event (z. B. &amp;lt;code&amp;gt;james_detected&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Browser empfängt Event via WebSocket&lt;br /&gt;
# Speech-to-Text startet im Browser&lt;br /&gt;
# Ergebnis wird an FHEM übertragen&lt;br /&gt;
&lt;br /&gt;
=== Aktivierung ===&lt;br /&gt;
&lt;br /&gt;
* Kurzer Klick auf Button aktiviert/deaktiviert System&lt;br /&gt;
* Baut WebSocket-Verbindung zu FHEM auf&lt;br /&gt;
* Kein Push-to-Talk-Modus&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration im Javascript ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const DEVICE  = &amp;quot;atom_echos3r_9888e00f4280&amp;quot;;&lt;br /&gt;
const TRIGGER = &amp;quot;james_detected&amp;quot;;&lt;br /&gt;
const FHEM_IP = &amp;quot;192.168.1.76:8085&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; → FHEM-Device des ESP&lt;br /&gt;
* &amp;lt;code&amp;gt;TRIGGER&amp;lt;/code&amp;gt; → Event bei Wakeword&lt;br /&gt;
* &amp;lt;code&amp;gt;FHEM_IP&amp;lt;/code&amp;gt; → FHEM-Server&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration in der Yaml ===&lt;br /&gt;
&lt;br /&gt;
Damit sich der ESP mit eurem MQTT-Server und WLAN verbindet, müssen folgende Stellen angepasst werden.&lt;br /&gt;
&lt;br /&gt;
   wifi:&lt;br /&gt;
     ssid: &amp;quot;YOUR_SSID&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     fast_connect: true&lt;br /&gt;
&lt;br /&gt;
   mqtt:&lt;br /&gt;
     broker: 192.168.1.76&lt;br /&gt;
     port: 1884&lt;br /&gt;
     username: &amp;quot;YOUR_USERNAME&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     topic_prefix: atom_echo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wakeword (ESP / ESPHome) ===&lt;br /&gt;
&lt;br /&gt;
Das Wakeword wird direkt auf dem ESP definiert:&lt;br /&gt;
&lt;br /&gt;
* Umsetzung über ESPHome&lt;br /&gt;
* Eine große Auswahl an Wakewords sind hier zu finden:&lt;br /&gt;
   https://github.com/TaterTotterson/microWakeWords&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel, wie das Wakeword definiert wird.&lt;br /&gt;
&lt;br /&gt;
   micro_wake_word:&lt;br /&gt;
     id: mww&lt;br /&gt;
     microphone: echo_mic&lt;br /&gt;
     models:&lt;br /&gt;
       - model: &amp;quot;https://github.com/TaterTotterson/microWakeWords/raw/main/microWakeWordsV2/james.json&amp;quot;&lt;br /&gt;
         id: james_model&lt;br /&gt;
         probability_cutoff: 0.6   &lt;br /&gt;
&lt;br /&gt;
=== Kommunikation ===&lt;br /&gt;
&lt;br /&gt;
* WebSocket-Verbindung zu FHEM&lt;br /&gt;
* Lauscht auf Device-Events&lt;br /&gt;
* Automatischer Reconnect bei Abbruch&lt;br /&gt;
&lt;br /&gt;
=== Ablauf nach Wakeword ===&lt;br /&gt;
&lt;br /&gt;
# System sagt „Ja?“&lt;br /&gt;
# Browser startet SpeechRecognition&lt;br /&gt;
# Nutzer spricht Befehl&lt;br /&gt;
# Befehl wird verarbeitet&lt;br /&gt;
# Rückmeldung „Erledigt“&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
siehe oben wie unter Punkt 1.&lt;br /&gt;
&lt;br /&gt;
== Zentrale Auswertung (Logik) ==&lt;br /&gt;
&lt;br /&gt;
Beide Wege schreiben in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;global:STT&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung erfolgt zentral über ein &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: notify ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod n_VoiceControl notify global:STT:.* {\&lt;br /&gt;
    # 1. VORBEREITUNG\&lt;br /&gt;
    my ($cleanEvent, $clientId) = $EVENT =~ /^(.*)\s\[(.*)\]$/;;\&lt;br /&gt;
    $cleanEvent //= $EVENT;;\&lt;br /&gt;
    $clientId   //= &amp;quot;unknown&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    my %lightRooms = (\&lt;br /&gt;
        &amp;quot;esszimmer&amp;quot;  =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;,       label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot; },\&lt;br /&gt;
        &amp;quot;küche&amp;quot;      =&amp;gt; { dev =&amp;gt; &amp;quot;Deckenlampe_Kue&amp;quot;,  label =&amp;gt; &amp;quot;Licht Küche&amp;quot; },\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe06_Dek&amp;quot;,         label =&amp;gt; &amp;quot;Licht Wohnzimmer&amp;quot; }\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    my %vacRooms = (\&lt;br /&gt;
        &amp;quot;arbeitszimmer&amp;quot; =&amp;gt; &amp;quot;Arbeitszimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;badezimmer&amp;quot; =&amp;gt; &amp;quot;Badezimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;esszimmer&amp;quot; =&amp;gt; &amp;quot;Esszimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;flur&amp;quot; =&amp;gt; &amp;quot;Flur&amp;quot;,\&lt;br /&gt;
        &amp;quot;küche&amp;quot; =&amp;gt; &amp;quot;Küche&amp;quot;,\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot; =&amp;gt; &amp;quot;Wohnzimmer&amp;quot;\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    my $onRegEx  = &#039;\b(an|ein|einschalten|starte|aktivier|aktiviere)\b&#039;;;\&lt;br /&gt;
    my $offRegEx = &#039;\b(aus|ausschalten|stop|stoppe|beende|deaktivier|deaktiviere)\b&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
    my @commands = split(/\s*(?:und|dann|,)\s*/, lc($cleanEvent));;\&lt;br /&gt;
\&lt;br /&gt;
    # 2. DER BEFEHLS-LOOP\&lt;br /&gt;
    foreach my $cmd_part (@commands) {\&lt;br /&gt;
        \&lt;br /&gt;
        $cmd_part =~ s/^\s+|\s+$//g;;\&lt;br /&gt;
        \&lt;br /&gt;
        my $is_on  = ($cmd_part =~ /$onRegEx/) ? 1 : 0;;\&lt;br /&gt;
        my $is_off = ($cmd_part =~ /$offRegEx/) ? 1 : 0;;\&lt;br /&gt;
\&lt;br /&gt;
        $cmd_part =~ s/\b(ich|brauche|mach|bitte|kannst du|würdest du|mal|doch|den|das|die|im|in der)\b//g;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: STAUBSAUGER ---\&lt;br /&gt;
        if ($cmd_part =~ /(reinige|sauge|putze|staubsauger|roboter)/) {\&lt;br /&gt;
            my @found = grep { $cmd_part =~ /\b$_\b/ } keys %vacRooms;;\&lt;br /&gt;
            if (@found) {\&lt;br /&gt;
                fhem(&amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish clean_segment &amp;quot; . join(&amp;quot;,&amp;quot;, map { $vacRooms{$_} } @found));;\&lt;br /&gt;
            } else {\&lt;br /&gt;
                fhem(&amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish start&amp;quot;);;\&lt;br /&gt;
            }\&lt;br /&gt;
            next;;\&lt;br /&gt;
        } elsif ($cmd_part =~ /(lade|aufladen|dock|station|home)/) {\&lt;br /&gt;
            fhem(&amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish charge&amp;quot;);;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: FERNSEHER ---\&lt;br /&gt;
        if ($cmd_part =~ /(fernseher|tv|vuplus)/) {\&lt;br /&gt;
            fhem(&amp;quot;set VuPlus &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;));;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: AMBIENTE ---\&lt;br /&gt;
        if ($cmd_part =~ /ambiente/) {\&lt;br /&gt;
            if ($cmd_part =~ /(\d+)/) {\&lt;br /&gt;
                my $b = ($1 &amp;gt; 255 ? 255 : ($1 &amp;lt; 1 ? 1 : $1));;\&lt;br /&gt;
                fhem(&amp;quot;set LampeSzeneAlle brightness $b&amp;quot;);;\&lt;br /&gt;
            } else {\&lt;br /&gt;
                fhem(&amp;quot;set LampeSzeneAlle &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;));;\&lt;br /&gt;
            }\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: AMBILIGHT ---\&lt;br /&gt;
        if ($cmd_part =~ /ambilight/) {\&lt;br /&gt;
            system(&amp;quot;sshpass -p &#039;GEHEIMESPASSWORT&#039; ssh -o StrictHostKeyChecking=no root\@192.168.1.46 &#039;/usr/share/hyperhdr/scripts/hyperhdr_toggle.sh&#039;&amp;quot;);;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: LICHT ---\&lt;br /&gt;
        my ($lightRoom) = grep { $cmd_part =~ /\b$_\b/ } keys %lightRooms;;\&lt;br /&gt;
        if ($lightRoom || $cmd_part =~ /(licht|lampe)/) {\&lt;br /&gt;
            my $dev = $lightRooms{$lightRoom}{dev} // &amp;quot;LampeSzeneAlle&amp;quot;;;\&lt;br /&gt;
            fhem(&amp;quot;set $dev &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;)) if ($is_on || $is_off);;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- HILFE (DYNAMISCH) ---\&lt;br /&gt;
        if ($cmd_part =~ /(hilfe|kommandos|übersicht)/) {\&lt;br /&gt;
\&lt;br /&gt;
            my $h = &#039;&amp;lt;div style=&amp;quot;text-align:left;;min-width:250px;;font-family:sans-serif;;&amp;quot;&amp;gt;&#039;;;\&lt;br /&gt;
            $h .= &#039;&amp;lt;b&amp;gt;Befehlsübersicht:&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
            # Licht\&lt;br /&gt;
            $h .= &#039;&amp;lt;u&amp;gt;Licht&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            for my $k (sort keys %lightRooms) {\&lt;br /&gt;
                $h .= &amp;quot;• &amp;quot;.$lightRooms{$k}{label}.&amp;quot; an/aus&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            }\&lt;br /&gt;
\&lt;br /&gt;
            # Staubsauger\&lt;br /&gt;
            $h .= &#039;&amp;lt;br&amp;gt;&amp;lt;u&amp;gt;Staubsauger&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            $h .= &amp;quot;• Sauge [Raum]&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;  Räume: &amp;quot;.join(&amp;quot;, &amp;quot;, map { ucfirst($_) } sort keys %vacRooms).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;• Lade Roberto&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
            # Sonstiges\&lt;br /&gt;
            $h .= &#039;&amp;lt;br&amp;gt;&amp;lt;u&amp;gt;Sonstiges&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            $h .= &amp;quot;• Fernseher an/aus&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;• Ambiente [an|aus|1-255]&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;• Ambilight&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
            $h .= &#039;&amp;lt;/div&amp;gt;&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
            $h =~ s/&#039;/\\&amp;quot;/g;;\&lt;br /&gt;
\&lt;br /&gt;
            my $js = &amp;quot;if((document.querySelector(&#039;input[name=\&amp;quot;fw_id\&amp;quot;]&#039;)||{}).value===&#039;$clientId&#039;){FW_okDialog(&#039;$h&#039;)}&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
            FW_directNotify(&amp;quot;#FHEMWEB:$_&amp;quot;, $js, &amp;quot;&amp;quot;)\&lt;br /&gt;
                for devspec2array(&amp;quot;TYPE=FHEMWEB&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Unterschiede der Wege ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Feature&lt;br /&gt;
! Browser&lt;br /&gt;
! Hybrid (Atom Echo S3)&lt;br /&gt;
|-&lt;br /&gt;
| Wakeword&lt;br /&gt;
| Browser&lt;br /&gt;
| ESP (Hardware)&lt;br /&gt;
|-&lt;br /&gt;
| Push-to-Talk&lt;br /&gt;
| Ja&lt;br /&gt;
| Nein&lt;br /&gt;
|-&lt;br /&gt;
| Always-On&lt;br /&gt;
| Ja (bedingt)&lt;br /&gt;
| Ja&lt;br /&gt;
|-&lt;br /&gt;
| Architektur&lt;br /&gt;
| Software&lt;br /&gt;
| Software + Hardware&lt;br /&gt;
|-&lt;br /&gt;
| Stabilität&lt;br /&gt;
| Sehr stabil&lt;br /&gt;
| Sehr stabil (Wakeword extern)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== FireOS mit Fully Browser (Plus) ==&lt;br /&gt;
&lt;br /&gt;
Damit die Spracherkennung funktioniert:&lt;br /&gt;
&lt;br /&gt;
* Apps installieren:&lt;br /&gt;
   Google&lt;br /&gt;
   Speech Recognition &amp;amp; Synthesis&lt;br /&gt;
* FireOS:&lt;br /&gt;
   „Tastatur und Sprache“ → „Text-to-Speech“&lt;br /&gt;
* Fully Browser:&lt;br /&gt;
   &amp;lt;code&amp;gt;Enable JavaScriptInterface (PLUS)&amp;lt;/code&amp;gt; aktivieren&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Datei:Voicecontrol.png&amp;diff=40904</id>
		<title>Datei:Voicecontrol.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Datei:Voicecontrol.png&amp;diff=40904"/>
		<updated>2026-04-03T08:34:07Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Voicecontrol&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40903</id>
		<title>FHEMWEB/VoiceControl: Web-STT &amp; Hardware-Wakeword</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40903"/>
		<updated>2026-04-03T08:25:51Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:VoiceControl_Logo.png|Mini|rechts]]&lt;br /&gt;
&lt;br /&gt;
== VoiceControl – Sprachsteuerung via Browser und Atom Echo s3r ==&lt;br /&gt;
&lt;br /&gt;
Diese Lösung ermöglicht eine flexible Sprachsteuerung für [[FHEM]]. Sprache wird in Text (Speech-to-Text) umgewandelt und als Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; bereitgestellt. Dieses Reading kann anschließend zentral (z. B. über &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DOIF&amp;lt;/code&amp;gt;) ausgewertet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Unterstützt werden nur Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox hat leider kein Backend.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei unterschiedliche Wege zur Spracherfassung:&lt;br /&gt;
&lt;br /&gt;
* Weg 1️⃣ (Software): Browser-basierte Komplettlösung&lt;br /&gt;
* Weg 2️⃣ (Hybrid): Hardware-Wakeword + Browser-Spracherkennung&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
Dort befinden sich in Post #1 auch alle benötigten Dateien.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Grundprinzip ====&lt;br /&gt;
* Sprache → Speech-to-Text&lt;br /&gt;
* Ergebnis → Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zentrale Logik verarbeitet Befehle&lt;br /&gt;
&lt;br /&gt;
==== Betriebsarten ====&lt;br /&gt;
* Push-to-Talk (nur Browser)&lt;br /&gt;
* Always-On mit Wakeword&lt;br /&gt;
* Hardware-Wakeword (Hybrid)&lt;br /&gt;
&lt;br /&gt;
==== Rückmeldungen ====&lt;br /&gt;
* Sprachausgabe (TTS)&lt;br /&gt;
* Visuelle Bubble im Browser&lt;br /&gt;
* Optional gezielte Rückmeldung per Client-ID&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 1️⃣: Browser-Lösung (voicecontrol.js) ==&lt;br /&gt;
&lt;br /&gt;
Das Script nutzt die Google Web Speech API. Unterstützt werden Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox wird aktuell nicht unterstützt.&lt;br /&gt;
&lt;br /&gt;
=== Bedienung ===&lt;br /&gt;
&lt;br /&gt;
==== Push-to-Talk ====&lt;br /&gt;
* Button gedrückt halten (~450 ms)&lt;br /&gt;
* Direkt sprechen (kein Wakeword nötig)&lt;br /&gt;
* Befehl wird sofort verarbeitet&lt;br /&gt;
&lt;br /&gt;
==== Always-On ====&lt;br /&gt;
* Kurzer Klick aktiviert Dauerbetrieb&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung permanentes Mithören&lt;br /&gt;
&lt;br /&gt;
==== Wakeword ====&lt;br /&gt;
* Standard: &amp;lt;code&amp;gt;james&amp;lt;/code&amp;gt;&lt;br /&gt;
* Anpassbar im Script:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const wakewords = [&amp;quot;james&amp;quot;];&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ablauf:&lt;br /&gt;
# „James“ sagen&lt;br /&gt;
# System antwortet „Ja?“&lt;br /&gt;
# Zeitfenster (~6 Sekunden) für Befehl&lt;br /&gt;
# Danach automatische Verarbeitung oder Abbruch&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{ Svn_GetFile(&#039;contrib/voicecontrol.js&#039;, &#039;www/pgm2/voicecontrol.js&#039;) }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBphone JavaScripts pgm2/voicecontrol.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Hinweis (HTTP ohne HTTPS) ====&lt;br /&gt;
Chrome benötigt Freigabe für Mikrofon:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;chrome://flags/#unsafely-treat-insecure-origin-as-secure&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eigene URL hinzufügen&lt;br /&gt;
* Auf &amp;lt;code&amp;gt;Enabled&amp;lt;/code&amp;gt; setzen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 2️⃣: Hybrid-Lösung mit Browser + Atom Echo (voicecontrol_echo.js) ==&lt;br /&gt;
&lt;br /&gt;
Diese Variante kombiniert Hardware und Software:&lt;br /&gt;
&lt;br /&gt;
* Wakeword-Erkennung erfolgt auf dem ESP (Atom Echo s3r)&lt;br /&gt;
* Die eigentliche Sprachverarbeitung (Speech-to-Text) erfolgt im Browser&lt;br /&gt;
&lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
&lt;br /&gt;
# Wakeword wird auf dem ESP erkannt&lt;br /&gt;
# FHEM erzeugt Event (z. B. &amp;lt;code&amp;gt;james_detected&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Browser empfängt Event via WebSocket&lt;br /&gt;
# Speech-to-Text startet im Browser&lt;br /&gt;
# Ergebnis wird an FHEM übertragen&lt;br /&gt;
&lt;br /&gt;
=== Aktivierung ===&lt;br /&gt;
&lt;br /&gt;
* Kurzer Klick auf Button aktiviert/deaktiviert System&lt;br /&gt;
* Baut WebSocket-Verbindung zu FHEM auf&lt;br /&gt;
* Kein Push-to-Talk-Modus&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration im Javascript ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const DEVICE  = &amp;quot;atom_echos3r_9888e00f4280&amp;quot;;&lt;br /&gt;
const TRIGGER = &amp;quot;james_detected&amp;quot;;&lt;br /&gt;
const FHEM_IP = &amp;quot;192.168.1.76:8085&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; → FHEM-Device des ESP&lt;br /&gt;
* &amp;lt;code&amp;gt;TRIGGER&amp;lt;/code&amp;gt; → Event bei Wakeword&lt;br /&gt;
* &amp;lt;code&amp;gt;FHEM_IP&amp;lt;/code&amp;gt; → FHEM-Server&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration in der Yaml ===&lt;br /&gt;
&lt;br /&gt;
Damit sich der ESP mit eurem MQTT-Server und WLAN verbindet, müssen folgende Stellen angepasst werden.&lt;br /&gt;
&lt;br /&gt;
   wifi:&lt;br /&gt;
     ssid: &amp;quot;YOUR_SSID&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     fast_connect: true&lt;br /&gt;
&lt;br /&gt;
   mqtt:&lt;br /&gt;
     broker: 192.168.1.76&lt;br /&gt;
     port: 1884&lt;br /&gt;
     username: &amp;quot;YOUR_USERNAME&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     topic_prefix: atom_echo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wakeword (ESP / ESPHome) ===&lt;br /&gt;
&lt;br /&gt;
Das Wakeword wird direkt auf dem ESP definiert:&lt;br /&gt;
&lt;br /&gt;
* Umsetzung über ESPHome&lt;br /&gt;
* Eine große Auswahl an Wakewords sind hier zu finden:&lt;br /&gt;
   https://github.com/TaterTotterson/microWakeWords&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel, wie das Wakeword definiert wird.&lt;br /&gt;
&lt;br /&gt;
   micro_wake_word:&lt;br /&gt;
     id: mww&lt;br /&gt;
     microphone: echo_mic&lt;br /&gt;
     models:&lt;br /&gt;
       - model: &amp;quot;https://github.com/TaterTotterson/microWakeWords/raw/main/microWakeWordsV2/james.json&amp;quot;&lt;br /&gt;
         id: james_model&lt;br /&gt;
         probability_cutoff: 0.6   &lt;br /&gt;
&lt;br /&gt;
=== Kommunikation ===&lt;br /&gt;
&lt;br /&gt;
* WebSocket-Verbindung zu FHEM&lt;br /&gt;
* Lauscht auf Device-Events&lt;br /&gt;
* Automatischer Reconnect bei Abbruch&lt;br /&gt;
&lt;br /&gt;
=== Ablauf nach Wakeword ===&lt;br /&gt;
&lt;br /&gt;
# System sagt „Ja?“&lt;br /&gt;
# Browser startet SpeechRecognition&lt;br /&gt;
# Nutzer spricht Befehl&lt;br /&gt;
# Befehl wird verarbeitet&lt;br /&gt;
# Rückmeldung „Erledigt“&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
siehe oben wie unter Punkt 1.&lt;br /&gt;
&lt;br /&gt;
== Zentrale Auswertung (Logik) ==&lt;br /&gt;
&lt;br /&gt;
Beide Wege schreiben in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;global:STT&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung erfolgt zentral über ein &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: notify ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod n_VoiceControl notify global:STT:.* {\&lt;br /&gt;
    # 1. VORBEREITUNG\&lt;br /&gt;
    my ($cleanEvent, $clientId) = $EVENT =~ /^(.*)\s\[(.*)\]$/;;\&lt;br /&gt;
    $cleanEvent //= $EVENT;;\&lt;br /&gt;
    $clientId   //= &amp;quot;unknown&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    my %lightRooms = (\&lt;br /&gt;
        &amp;quot;esszimmer&amp;quot;  =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;,       label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot; },\&lt;br /&gt;
        &amp;quot;küche&amp;quot;      =&amp;gt; { dev =&amp;gt; &amp;quot;Deckenlampe_Kue&amp;quot;,  label =&amp;gt; &amp;quot;Licht Küche&amp;quot; },\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe06_Dek&amp;quot;,         label =&amp;gt; &amp;quot;Licht Wohnzimmer&amp;quot; }\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    my %vacRooms = (\&lt;br /&gt;
        &amp;quot;arbeitszimmer&amp;quot; =&amp;gt; &amp;quot;Arbeitszimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;badezimmer&amp;quot; =&amp;gt; &amp;quot;Badezimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;esszimmer&amp;quot; =&amp;gt; &amp;quot;Esszimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;flur&amp;quot; =&amp;gt; &amp;quot;Flur&amp;quot;,\&lt;br /&gt;
        &amp;quot;küche&amp;quot; =&amp;gt; &amp;quot;Küche&amp;quot;,\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot; =&amp;gt; &amp;quot;Wohnzimmer&amp;quot;\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    my $onRegEx  = &#039;\b(an|ein|einschalten|starte|aktivier|aktiviere)\b&#039;;;\&lt;br /&gt;
    my $offRegEx = &#039;\b(aus|ausschalten|stop|stoppe|beende|deaktivier|deaktiviere)\b&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
    my @commands = split(/\s*(?:und|dann|,)\s*/, lc($cleanEvent));;\&lt;br /&gt;
\&lt;br /&gt;
    # 2. DER BEFEHLS-LOOP\&lt;br /&gt;
    foreach my $cmd_part (@commands) {\&lt;br /&gt;
        \&lt;br /&gt;
        $cmd_part =~ s/^\s+|\s+$//g;;\&lt;br /&gt;
        \&lt;br /&gt;
        my $is_on  = ($cmd_part =~ /$onRegEx/) ? 1 : 0;;\&lt;br /&gt;
        my $is_off = ($cmd_part =~ /$offRegEx/) ? 1 : 0;;\&lt;br /&gt;
\&lt;br /&gt;
        $cmd_part =~ s/\b(ich|brauche|mach|bitte|kannst du|würdest du|mal|doch|den|das|die|im|in der)\b//g;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: STAUBSAUGER ---\&lt;br /&gt;
        if ($cmd_part =~ /(reinige|sauge|putze|staubsauger|roboter)/) {\&lt;br /&gt;
            my @found = grep { $cmd_part =~ /\b$_\b/ } keys %vacRooms;;\&lt;br /&gt;
            if (@found) {\&lt;br /&gt;
                fhem(&amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish clean_segment &amp;quot; . join(&amp;quot;,&amp;quot;, map { $vacRooms{$_} } @found));;\&lt;br /&gt;
            } else {\&lt;br /&gt;
                fhem(&amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish start&amp;quot;);;\&lt;br /&gt;
            }\&lt;br /&gt;
            next;;\&lt;br /&gt;
        } elsif ($cmd_part =~ /(lade|aufladen|dock|station|home)/) {\&lt;br /&gt;
            fhem(&amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish charge&amp;quot;);;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: FERNSEHER ---\&lt;br /&gt;
        if ($cmd_part =~ /(fernseher|tv|vuplus)/) {\&lt;br /&gt;
            fhem(&amp;quot;set VuPlus &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;));;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: AMBIENTE ---\&lt;br /&gt;
        if ($cmd_part =~ /ambiente/) {\&lt;br /&gt;
            if ($cmd_part =~ /(\d+)/) {\&lt;br /&gt;
                my $b = ($1 &amp;gt; 255 ? 255 : ($1 &amp;lt; 1 ? 1 : $1));;\&lt;br /&gt;
                fhem(&amp;quot;set LampeSzeneAlle brightness $b&amp;quot;);;\&lt;br /&gt;
            } else {\&lt;br /&gt;
                fhem(&amp;quot;set LampeSzeneAlle &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;));;\&lt;br /&gt;
            }\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: AMBILIGHT ---\&lt;br /&gt;
        if ($cmd_part =~ /ambilight/) {\&lt;br /&gt;
            system(&amp;quot;sshpass -p &#039;GEHEIMESPASSWORT&#039; ssh -o StrictHostKeyChecking=no root\@192.168.1.46 &#039;/usr/share/hyperhdr/scripts/hyperhdr_toggle.sh&#039;&amp;quot;);;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: LICHT ---\&lt;br /&gt;
        my ($lightRoom) = grep { $cmd_part =~ /\b$_\b/ } keys %lightRooms;;\&lt;br /&gt;
        if ($lightRoom || $cmd_part =~ /(licht|lampe)/) {\&lt;br /&gt;
            my $dev = $lightRooms{$lightRoom}{dev} // &amp;quot;LampeSzeneAlle&amp;quot;;;\&lt;br /&gt;
            fhem(&amp;quot;set $dev &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;)) if ($is_on || $is_off);;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- HILFE (DYNAMISCH) ---\&lt;br /&gt;
        if ($cmd_part =~ /(hilfe|kommandos|übersicht)/) {\&lt;br /&gt;
\&lt;br /&gt;
            my $h = &#039;&amp;lt;div style=&amp;quot;text-align:left;;min-width:250px;;font-family:sans-serif;;&amp;quot;&amp;gt;&#039;;;\&lt;br /&gt;
            $h .= &#039;&amp;lt;b&amp;gt;Befehlsübersicht:&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
            # Licht\&lt;br /&gt;
            $h .= &#039;&amp;lt;u&amp;gt;Licht&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            for my $k (sort keys %lightRooms) {\&lt;br /&gt;
                $h .= &amp;quot;• &amp;quot;.$lightRooms{$k}{label}.&amp;quot; an/aus&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            }\&lt;br /&gt;
\&lt;br /&gt;
            # Staubsauger\&lt;br /&gt;
            $h .= &#039;&amp;lt;br&amp;gt;&amp;lt;u&amp;gt;Staubsauger&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            $h .= &amp;quot;• Sauge [Raum]&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;  Räume: &amp;quot;.join(&amp;quot;, &amp;quot;, map { ucfirst($_) } sort keys %vacRooms).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;• Lade Roberto&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
            # Sonstiges\&lt;br /&gt;
            $h .= &#039;&amp;lt;br&amp;gt;&amp;lt;u&amp;gt;Sonstiges&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            $h .= &amp;quot;• Fernseher an/aus&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;• Ambiente [an|aus|1-255]&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;• Ambilight&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
            $h .= &#039;&amp;lt;/div&amp;gt;&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
            $h =~ s/&#039;/\\&amp;quot;/g;;\&lt;br /&gt;
\&lt;br /&gt;
            my $js = &amp;quot;if((document.querySelector(&#039;input[name=\&amp;quot;fw_id\&amp;quot;]&#039;)||{}).value===&#039;$clientId&#039;){FW_okDialog(&#039;$h&#039;)}&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
            FW_directNotify(&amp;quot;#FHEMWEB:$_&amp;quot;, $js, &amp;quot;&amp;quot;)\&lt;br /&gt;
                for devspec2array(&amp;quot;TYPE=FHEMWEB&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Unterschiede der Wege ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Feature&lt;br /&gt;
! Browser&lt;br /&gt;
! Hybrid (Atom Echo S3)&lt;br /&gt;
|-&lt;br /&gt;
| Wakeword&lt;br /&gt;
| Browser&lt;br /&gt;
| ESP (Hardware)&lt;br /&gt;
|-&lt;br /&gt;
| Push-to-Talk&lt;br /&gt;
| Ja&lt;br /&gt;
| Nein&lt;br /&gt;
|-&lt;br /&gt;
| Always-On&lt;br /&gt;
| Ja (bedingt)&lt;br /&gt;
| Ja&lt;br /&gt;
|-&lt;br /&gt;
| Architektur&lt;br /&gt;
| Software&lt;br /&gt;
| Software + Hardware&lt;br /&gt;
|-&lt;br /&gt;
| Stabilität&lt;br /&gt;
| Sehr stabil&lt;br /&gt;
| Sehr stabil (Wakeword extern)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== FireOS mit Fully Browser (Plus) ==&lt;br /&gt;
&lt;br /&gt;
Damit die Spracherkennung funktioniert:&lt;br /&gt;
&lt;br /&gt;
* Apps installieren:&lt;br /&gt;
   Google&lt;br /&gt;
   Speech Recognition &amp;amp; Synthesis&lt;br /&gt;
* FireOS:&lt;br /&gt;
   „Tastatur und Sprache“ → „Text-to-Speech“&lt;br /&gt;
* Fully Browser:&lt;br /&gt;
   &amp;lt;code&amp;gt;Enable JavaScriptInterface (PLUS)&amp;lt;/code&amp;gt; aktivieren&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40902</id>
		<title>FHEMWEB/VoiceControl: Web-STT &amp; Hardware-Wakeword</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40902"/>
		<updated>2026-04-03T08:21:34Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:VoiceControl_Logo.png|Mini|rechts]]&lt;br /&gt;
&lt;br /&gt;
== VoiceControl – Sprachsteuerung via Browser und Atom Echo s3r ==&lt;br /&gt;
&lt;br /&gt;
Diese Lösung ermöglicht eine flexible Sprachsteuerung für [[FHEM]]. Sprache wird in Text (Speech-to-Text) umgewandelt und als Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; bereitgestellt. Dieses Reading kann anschließend zentral (z. B. über &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DOIF&amp;lt;/code&amp;gt;) ausgewertet werden.&lt;br /&gt;
&lt;br /&gt;
Das System unterstützt zwei unterschiedliche Wege zur Spracherfassung:&lt;br /&gt;
&lt;br /&gt;
* Weg 1️⃣ (Software): Browser-basierte Komplettlösung&lt;br /&gt;
* Weg 2️⃣ (Hybrid): Hardware-Wakeword + Browser-Spracherkennung&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
Dort befinden sich in Post #1 auch alle benötigten Dateien.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Grundprinzip ====&lt;br /&gt;
* Sprache → Speech-to-Text&lt;br /&gt;
* Ergebnis → Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zentrale Logik verarbeitet Befehle&lt;br /&gt;
&lt;br /&gt;
==== Betriebsarten ====&lt;br /&gt;
* Push-to-Talk (nur Browser)&lt;br /&gt;
* Always-On mit Wakeword&lt;br /&gt;
* Hardware-Wakeword (Hybrid)&lt;br /&gt;
&lt;br /&gt;
==== Rückmeldungen ====&lt;br /&gt;
* Sprachausgabe (TTS)&lt;br /&gt;
* Visuelle Bubble im Browser&lt;br /&gt;
* Optional gezielte Rückmeldung per Client-ID&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 1️⃣: Browser-Lösung (voicecontrol.js) ==&lt;br /&gt;
&lt;br /&gt;
Das Script nutzt die Google Web Speech API. Unterstützt werden Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox wird aktuell nicht unterstützt.&lt;br /&gt;
&lt;br /&gt;
=== Bedienung ===&lt;br /&gt;
&lt;br /&gt;
==== Push-to-Talk ====&lt;br /&gt;
* Button gedrückt halten (~450 ms)&lt;br /&gt;
* Direkt sprechen (kein Wakeword nötig)&lt;br /&gt;
* Befehl wird sofort verarbeitet&lt;br /&gt;
&lt;br /&gt;
==== Always-On ====&lt;br /&gt;
* Kurzer Klick aktiviert Dauerbetrieb&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung permanentes Mithören&lt;br /&gt;
&lt;br /&gt;
==== Wakeword ====&lt;br /&gt;
* Standard: &amp;lt;code&amp;gt;james&amp;lt;/code&amp;gt;&lt;br /&gt;
* Anpassbar im Script:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const wakewords = [&amp;quot;james&amp;quot;];&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ablauf:&lt;br /&gt;
# „James“ sagen&lt;br /&gt;
# System antwortet „Ja?“&lt;br /&gt;
# Zeitfenster (~6 Sekunden) für Befehl&lt;br /&gt;
# Danach automatische Verarbeitung oder Abbruch&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{ Svn_GetFile(&#039;contrib/voicecontrol.js&#039;, &#039;www/pgm2/voicecontrol.js&#039;) }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBphone JavaScripts pgm2/voicecontrol.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Hinweis (HTTP ohne HTTPS) ====&lt;br /&gt;
Chrome benötigt Freigabe für Mikrofon:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;chrome://flags/#unsafely-treat-insecure-origin-as-secure&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eigene URL hinzufügen&lt;br /&gt;
* Auf &amp;lt;code&amp;gt;Enabled&amp;lt;/code&amp;gt; setzen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 2️⃣: Hybrid-Lösung mit Browser + Atom Echo (voicecontrol_echo.js) ==&lt;br /&gt;
&lt;br /&gt;
Diese Variante kombiniert Hardware und Software:&lt;br /&gt;
&lt;br /&gt;
* Wakeword-Erkennung erfolgt auf dem ESP (Atom Echo s3r)&lt;br /&gt;
* Die eigentliche Sprachverarbeitung (Speech-to-Text) erfolgt im Browser&lt;br /&gt;
&lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
&lt;br /&gt;
# Wakeword wird auf dem ESP erkannt&lt;br /&gt;
# FHEM erzeugt Event (z. B. &amp;lt;code&amp;gt;james_detected&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Browser empfängt Event via WebSocket&lt;br /&gt;
# Speech-to-Text startet im Browser&lt;br /&gt;
# Ergebnis wird an FHEM übertragen&lt;br /&gt;
&lt;br /&gt;
=== Aktivierung ===&lt;br /&gt;
&lt;br /&gt;
* Kurzer Klick auf Button aktiviert/deaktiviert System&lt;br /&gt;
* Baut WebSocket-Verbindung zu FHEM auf&lt;br /&gt;
* Kein Push-to-Talk-Modus&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration im Javascript ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const DEVICE  = &amp;quot;atom_echos3r_9888e00f4280&amp;quot;;&lt;br /&gt;
const TRIGGER = &amp;quot;james_detected&amp;quot;;&lt;br /&gt;
const FHEM_IP = &amp;quot;192.168.1.76:8085&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; → FHEM-Device des ESP&lt;br /&gt;
* &amp;lt;code&amp;gt;TRIGGER&amp;lt;/code&amp;gt; → Event bei Wakeword&lt;br /&gt;
* &amp;lt;code&amp;gt;FHEM_IP&amp;lt;/code&amp;gt; → FHEM-Server&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration in der Yaml ===&lt;br /&gt;
&lt;br /&gt;
Damit sich der ESP mit eurem MQTT-Server und WLAN verbindet, müssen folgende Stellen angepasst werden.&lt;br /&gt;
&lt;br /&gt;
   wifi:&lt;br /&gt;
     ssid: &amp;quot;YOUR_SSID&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     fast_connect: true&lt;br /&gt;
&lt;br /&gt;
   mqtt:&lt;br /&gt;
     broker: 192.168.1.76&lt;br /&gt;
     port: 1884&lt;br /&gt;
     username: &amp;quot;YOUR_USERNAME&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     topic_prefix: atom_echo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wakeword (ESP / ESPHome) ===&lt;br /&gt;
&lt;br /&gt;
Das Wakeword wird direkt auf dem ESP definiert:&lt;br /&gt;
&lt;br /&gt;
* Umsetzung über ESPHome&lt;br /&gt;
* Eine große Auswahl an Wakewords sind hier zu finden:&lt;br /&gt;
   https://github.com/TaterTotterson/microWakeWords&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel, wie das Wakeword definiert wird.&lt;br /&gt;
&lt;br /&gt;
   micro_wake_word:&lt;br /&gt;
     id: mww&lt;br /&gt;
     microphone: echo_mic&lt;br /&gt;
     models:&lt;br /&gt;
       - model: &amp;quot;https://github.com/TaterTotterson/microWakeWords/raw/main/microWakeWordsV2/james.json&amp;quot;&lt;br /&gt;
         id: james_model&lt;br /&gt;
         probability_cutoff: 0.6   &lt;br /&gt;
&lt;br /&gt;
=== Kommunikation ===&lt;br /&gt;
&lt;br /&gt;
* WebSocket-Verbindung zu FHEM&lt;br /&gt;
* Lauscht auf Device-Events&lt;br /&gt;
* Automatischer Reconnect bei Abbruch&lt;br /&gt;
&lt;br /&gt;
=== Ablauf nach Wakeword ===&lt;br /&gt;
&lt;br /&gt;
# System sagt „Ja?“&lt;br /&gt;
# Browser startet SpeechRecognition&lt;br /&gt;
# Nutzer spricht Befehl&lt;br /&gt;
# Befehl wird verarbeitet&lt;br /&gt;
# Rückmeldung „Erledigt“&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
siehe oben wie unter Punkt 1.&lt;br /&gt;
&lt;br /&gt;
== Zentrale Auswertung (Logik) ==&lt;br /&gt;
&lt;br /&gt;
Beide Wege schreiben in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;global:STT&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung erfolgt zentral über ein &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: notify ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod n_VoiceControl notify global:STT:.* {\&lt;br /&gt;
    # 1. VORBEREITUNG\&lt;br /&gt;
    my ($cleanEvent, $clientId) = $EVENT =~ /^(.*)\s\[(.*)\]$/;;\&lt;br /&gt;
    $cleanEvent //= $EVENT;;\&lt;br /&gt;
    $clientId   //= &amp;quot;unknown&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    my %lightRooms = (\&lt;br /&gt;
        &amp;quot;esszimmer&amp;quot;  =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;,       label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot; },\&lt;br /&gt;
        &amp;quot;küche&amp;quot;      =&amp;gt; { dev =&amp;gt; &amp;quot;Deckenlampe_Kue&amp;quot;,  label =&amp;gt; &amp;quot;Licht Küche&amp;quot; },\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe06_Dek&amp;quot;,         label =&amp;gt; &amp;quot;Licht Wohnzimmer&amp;quot; }\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    my %vacRooms = (\&lt;br /&gt;
        &amp;quot;arbeitszimmer&amp;quot; =&amp;gt; &amp;quot;Arbeitszimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;badezimmer&amp;quot; =&amp;gt; &amp;quot;Badezimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;esszimmer&amp;quot; =&amp;gt; &amp;quot;Esszimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;flur&amp;quot; =&amp;gt; &amp;quot;Flur&amp;quot;,\&lt;br /&gt;
        &amp;quot;küche&amp;quot; =&amp;gt; &amp;quot;Küche&amp;quot;,\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot; =&amp;gt; &amp;quot;Wohnzimmer&amp;quot;\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    my $onRegEx  = &#039;\b(an|ein|einschalten|starte|aktivier|aktiviere)\b&#039;;;\&lt;br /&gt;
    my $offRegEx = &#039;\b(aus|ausschalten|stop|stoppe|beende|deaktivier|deaktiviere)\b&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
    my @commands = split(/\s*(?:und|dann|,)\s*/, lc($cleanEvent));;\&lt;br /&gt;
\&lt;br /&gt;
    # 2. DER BEFEHLS-LOOP\&lt;br /&gt;
    foreach my $cmd_part (@commands) {\&lt;br /&gt;
        \&lt;br /&gt;
        $cmd_part =~ s/^\s+|\s+$//g;;\&lt;br /&gt;
        \&lt;br /&gt;
        my $is_on  = ($cmd_part =~ /$onRegEx/) ? 1 : 0;;\&lt;br /&gt;
        my $is_off = ($cmd_part =~ /$offRegEx/) ? 1 : 0;;\&lt;br /&gt;
\&lt;br /&gt;
        $cmd_part =~ s/\b(ich|brauche|mach|bitte|kannst du|würdest du|mal|doch|den|das|die|im|in der)\b//g;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: STAUBSAUGER ---\&lt;br /&gt;
        if ($cmd_part =~ /(reinige|sauge|putze|staubsauger|roboter)/) {\&lt;br /&gt;
            my @found = grep { $cmd_part =~ /\b$_\b/ } keys %vacRooms;;\&lt;br /&gt;
            if (@found) {\&lt;br /&gt;
                fhem(&amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish clean_segment &amp;quot; . join(&amp;quot;,&amp;quot;, map { $vacRooms{$_} } @found));;\&lt;br /&gt;
            } else {\&lt;br /&gt;
                fhem(&amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish start&amp;quot;);;\&lt;br /&gt;
            }\&lt;br /&gt;
            next;;\&lt;br /&gt;
        } elsif ($cmd_part =~ /(lade|aufladen|dock|station|home)/) {\&lt;br /&gt;
            fhem(&amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish charge&amp;quot;);;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: FERNSEHER ---\&lt;br /&gt;
        if ($cmd_part =~ /(fernseher|tv|vuplus)/) {\&lt;br /&gt;
            fhem(&amp;quot;set VuPlus &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;));;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: AMBIENTE ---\&lt;br /&gt;
        if ($cmd_part =~ /ambiente/) {\&lt;br /&gt;
            if ($cmd_part =~ /(\d+)/) {\&lt;br /&gt;
                my $b = ($1 &amp;gt; 255 ? 255 : ($1 &amp;lt; 1 ? 1 : $1));;\&lt;br /&gt;
                fhem(&amp;quot;set LampeSzeneAlle brightness $b&amp;quot;);;\&lt;br /&gt;
            } else {\&lt;br /&gt;
                fhem(&amp;quot;set LampeSzeneAlle &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;));;\&lt;br /&gt;
            }\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: AMBILIGHT ---\&lt;br /&gt;
        if ($cmd_part =~ /ambilight/) {\&lt;br /&gt;
            system(&amp;quot;sshpass -p &#039;GEHEIMESPASSWORT&#039; ssh -o StrictHostKeyChecking=no root\@192.168.1.46 &#039;/usr/share/hyperhdr/scripts/hyperhdr_toggle.sh&#039;&amp;quot;);;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: LICHT ---\&lt;br /&gt;
        my ($lightRoom) = grep { $cmd_part =~ /\b$_\b/ } keys %lightRooms;;\&lt;br /&gt;
        if ($lightRoom || $cmd_part =~ /(licht|lampe)/) {\&lt;br /&gt;
            my $dev = $lightRooms{$lightRoom}{dev} // &amp;quot;LampeSzeneAlle&amp;quot;;;\&lt;br /&gt;
            fhem(&amp;quot;set $dev &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;)) if ($is_on || $is_off);;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- HILFE (DYNAMISCH) ---\&lt;br /&gt;
        if ($cmd_part =~ /(hilfe|kommandos|übersicht)/) {\&lt;br /&gt;
\&lt;br /&gt;
            my $h = &#039;&amp;lt;div style=&amp;quot;text-align:left;;min-width:250px;;font-family:sans-serif;;&amp;quot;&amp;gt;&#039;;;\&lt;br /&gt;
            $h .= &#039;&amp;lt;b&amp;gt;Befehlsübersicht:&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
            # Licht\&lt;br /&gt;
            $h .= &#039;&amp;lt;u&amp;gt;Licht&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            for my $k (sort keys %lightRooms) {\&lt;br /&gt;
                $h .= &amp;quot;• &amp;quot;.$lightRooms{$k}{label}.&amp;quot; an/aus&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            }\&lt;br /&gt;
\&lt;br /&gt;
            # Staubsauger\&lt;br /&gt;
            $h .= &#039;&amp;lt;br&amp;gt;&amp;lt;u&amp;gt;Staubsauger&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            $h .= &amp;quot;• Sauge [Raum]&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;  Räume: &amp;quot;.join(&amp;quot;, &amp;quot;, map { ucfirst($_) } sort keys %vacRooms).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;• Lade Roberto&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
            # Sonstiges\&lt;br /&gt;
            $h .= &#039;&amp;lt;br&amp;gt;&amp;lt;u&amp;gt;Sonstiges&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            $h .= &amp;quot;• Fernseher an/aus&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;• Ambiente [an|aus|1-255]&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;• Ambilight&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
            $h .= &#039;&amp;lt;/div&amp;gt;&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
            $h =~ s/&#039;/\\&amp;quot;/g;;\&lt;br /&gt;
\&lt;br /&gt;
            my $js = &amp;quot;if((document.querySelector(&#039;input[name=\&amp;quot;fw_id\&amp;quot;]&#039;)||{}).value===&#039;$clientId&#039;){FW_okDialog(&#039;$h&#039;)}&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
            FW_directNotify(&amp;quot;#FHEMWEB:$_&amp;quot;, $js, &amp;quot;&amp;quot;)\&lt;br /&gt;
                for devspec2array(&amp;quot;TYPE=FHEMWEB&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Unterschiede der Wege ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Feature&lt;br /&gt;
! Browser&lt;br /&gt;
! Hybrid (Atom Echo S3)&lt;br /&gt;
|-&lt;br /&gt;
| Wakeword&lt;br /&gt;
| Browser&lt;br /&gt;
| ESP (Hardware)&lt;br /&gt;
|-&lt;br /&gt;
| Push-to-Talk&lt;br /&gt;
| Ja&lt;br /&gt;
| Nein&lt;br /&gt;
|-&lt;br /&gt;
| Always-On&lt;br /&gt;
| Ja (bedingt)&lt;br /&gt;
| Ja&lt;br /&gt;
|-&lt;br /&gt;
| Architektur&lt;br /&gt;
| Software&lt;br /&gt;
| Software + Hardware&lt;br /&gt;
|-&lt;br /&gt;
| Stabilität&lt;br /&gt;
| Sehr stabil&lt;br /&gt;
| Sehr stabil (Wakeword extern)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== FireOS mit Fully Browser (Plus) ==&lt;br /&gt;
&lt;br /&gt;
Damit die Spracherkennung funktioniert:&lt;br /&gt;
&lt;br /&gt;
* Apps installieren:&lt;br /&gt;
   Google&lt;br /&gt;
   Speech Recognition &amp;amp; Synthesis&lt;br /&gt;
* FireOS:&lt;br /&gt;
   „Tastatur und Sprache“ → „Text-to-Speech“&lt;br /&gt;
* Fully Browser:&lt;br /&gt;
   &amp;lt;code&amp;gt;Enable JavaScriptInterface (PLUS)&amp;lt;/code&amp;gt; aktivieren&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40901</id>
		<title>FHEMWEB/VoiceControl: Web-STT &amp; Hardware-Wakeword</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40901"/>
		<updated>2026-04-03T07:50:19Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:VoiceControl_Logo.png|Mini|rechts]]&lt;br /&gt;
&lt;br /&gt;
== VoiceControl – Sprachsteuerung via Browser und Atom Echo s3r ==&lt;br /&gt;
&lt;br /&gt;
Diese Lösung ermöglicht eine flexible Sprachsteuerung für [[FHEM]]. Sprache wird in Text (Speech-to-Text) umgewandelt und als Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; bereitgestellt. Dieses Reading kann anschließend zentral (z. B. über &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DOIF&amp;lt;/code&amp;gt;) ausgewertet werden.&lt;br /&gt;
&lt;br /&gt;
Das System unterstützt zwei unterschiedliche Wege zur Spracherfassung:&lt;br /&gt;
&lt;br /&gt;
* Weg 1️⃣ (Software): Browser-basierte Komplettlösung&lt;br /&gt;
* Weg 2️⃣ (Hybrid): Hardware-Wakeword + Browser-Spracherkennung&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
Dort befinden sich in Post #1 auch alle benötigten Dateien.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Grundprinzip ====&lt;br /&gt;
* Sprache → Speech-to-Text&lt;br /&gt;
* Ergebnis → Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zentrale Logik verarbeitet Befehle&lt;br /&gt;
&lt;br /&gt;
==== Betriebsarten ====&lt;br /&gt;
* Push-to-Talk (nur Browser)&lt;br /&gt;
* Always-On mit Wakeword&lt;br /&gt;
* Hardware-Wakeword (Hybrid)&lt;br /&gt;
&lt;br /&gt;
==== Rückmeldungen ====&lt;br /&gt;
* Sprachausgabe (TTS)&lt;br /&gt;
* Visuelle Bubble im Browser&lt;br /&gt;
* Optional gezielte Rückmeldung per Client-ID&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 1️⃣: Browser-Lösung (voicecontrol.js) ==&lt;br /&gt;
&lt;br /&gt;
Das Script nutzt die Google Web Speech API. Unterstützt werden Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox wird aktuell nicht unterstützt.&lt;br /&gt;
&lt;br /&gt;
=== Bedienung ===&lt;br /&gt;
&lt;br /&gt;
==== Push-to-Talk ====&lt;br /&gt;
* Button gedrückt halten (~450 ms)&lt;br /&gt;
* Direkt sprechen (kein Wakeword nötig)&lt;br /&gt;
* Befehl wird sofort verarbeitet&lt;br /&gt;
&lt;br /&gt;
==== Always-On ====&lt;br /&gt;
* Kurzer Klick aktiviert Dauerbetrieb&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung permanentes Mithören&lt;br /&gt;
&lt;br /&gt;
==== Wakeword ====&lt;br /&gt;
* Standard: &amp;lt;code&amp;gt;james&amp;lt;/code&amp;gt;&lt;br /&gt;
* Anpassbar im Script:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const wakewords = [&amp;quot;james&amp;quot;];&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ablauf:&lt;br /&gt;
# „James“ sagen&lt;br /&gt;
# System antwortet „Ja?“&lt;br /&gt;
# Zeitfenster (~6 Sekunden) für Befehl&lt;br /&gt;
# Danach automatische Verarbeitung oder Abbruch&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&amp;lt;code&amp;gt;voicecontrol.js&amp;lt;/code&amp;gt; nach:&lt;br /&gt;
&amp;lt;code&amp;gt;/opt/fhem/www/voicecontrol/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBphone JavaScripts voicecontrol/voicecontrol.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Hinweis (HTTP ohne HTTPS) ====&lt;br /&gt;
Chrome benötigt Freigabe für Mikrofon:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;chrome://flags/#unsafely-treat-insecure-origin-as-secure&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eigene URL hinzufügen&lt;br /&gt;
* Auf &amp;lt;code&amp;gt;Enabled&amp;lt;/code&amp;gt; setzen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 2️⃣: Hybrid-Lösung mit Browser + Atom Echo (voicecontrol_echo.js) ==&lt;br /&gt;
&lt;br /&gt;
Diese Variante kombiniert Hardware und Software:&lt;br /&gt;
&lt;br /&gt;
* Wakeword-Erkennung erfolgt auf dem ESP (Atom Echo s3r)&lt;br /&gt;
* Die eigentliche Sprachverarbeitung (Speech-to-Text) erfolgt im Browser&lt;br /&gt;
&lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
&lt;br /&gt;
# Wakeword wird auf dem ESP erkannt&lt;br /&gt;
# FHEM erzeugt Event (z. B. &amp;lt;code&amp;gt;james_detected&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Browser empfängt Event via WebSocket&lt;br /&gt;
# Speech-to-Text startet im Browser&lt;br /&gt;
# Ergebnis wird an FHEM übertragen&lt;br /&gt;
&lt;br /&gt;
=== Aktivierung ===&lt;br /&gt;
&lt;br /&gt;
* Kurzer Klick auf Button aktiviert/deaktiviert System&lt;br /&gt;
* Baut WebSocket-Verbindung zu FHEM auf&lt;br /&gt;
* Kein Push-to-Talk-Modus&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration im Javascript ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const DEVICE  = &amp;quot;atom_echos3r_9888e00f4280&amp;quot;;&lt;br /&gt;
const TRIGGER = &amp;quot;james_detected&amp;quot;;&lt;br /&gt;
const FHEM_IP = &amp;quot;192.168.1.76:8085&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; → FHEM-Device des ESP&lt;br /&gt;
* &amp;lt;code&amp;gt;TRIGGER&amp;lt;/code&amp;gt; → Event bei Wakeword&lt;br /&gt;
* &amp;lt;code&amp;gt;FHEM_IP&amp;lt;/code&amp;gt; → FHEM-Server&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration in der Yaml ===&lt;br /&gt;
&lt;br /&gt;
Damit sich der ESP mit eurem MQTT-Server und WLAN verbindet, müssen folgende Stellen angepasst werden.&lt;br /&gt;
&lt;br /&gt;
   wifi:&lt;br /&gt;
     ssid: &amp;quot;YOUR_SSID&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     fast_connect: true&lt;br /&gt;
&lt;br /&gt;
   mqtt:&lt;br /&gt;
     broker: 192.168.1.76&lt;br /&gt;
     port: 1884&lt;br /&gt;
     username: &amp;quot;YOUR_USERNAME&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     topic_prefix: atom_echo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wakeword (ESP / ESPHome) ===&lt;br /&gt;
&lt;br /&gt;
Das Wakeword wird direkt auf dem ESP definiert:&lt;br /&gt;
&lt;br /&gt;
* Umsetzung über ESPHome&lt;br /&gt;
* Eine große Auswahl an Wakewords sind hier zu finden:&lt;br /&gt;
   https://github.com/TaterTotterson/microWakeWords&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel, wie das Wakeword definiert wird.&lt;br /&gt;
&lt;br /&gt;
   micro_wake_word:&lt;br /&gt;
     id: mww&lt;br /&gt;
     microphone: echo_mic&lt;br /&gt;
     models:&lt;br /&gt;
       - model: &amp;quot;https://github.com/TaterTotterson/microWakeWords/raw/main/microWakeWordsV2/james.json&amp;quot;&lt;br /&gt;
         id: james_model&lt;br /&gt;
         probability_cutoff: 0.6   &lt;br /&gt;
&lt;br /&gt;
=== Kommunikation ===&lt;br /&gt;
&lt;br /&gt;
* WebSocket-Verbindung zu FHEM&lt;br /&gt;
* Lauscht auf Device-Events&lt;br /&gt;
* Automatischer Reconnect bei Abbruch&lt;br /&gt;
&lt;br /&gt;
=== Ablauf nach Wakeword ===&lt;br /&gt;
&lt;br /&gt;
# System sagt „Ja?“&lt;br /&gt;
# Browser startet SpeechRecognition&lt;br /&gt;
# Nutzer spricht Befehl&lt;br /&gt;
# Befehl wird verarbeitet&lt;br /&gt;
# Rückmeldung „Erledigt“&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
siehe oben wie unter Punkt 1.&lt;br /&gt;
&lt;br /&gt;
== Zentrale Auswertung (Logik) ==&lt;br /&gt;
&lt;br /&gt;
Beide Wege schreiben in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;global:STT&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung erfolgt zentral über ein &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: notify ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod n_VoiceControl notify global:STT:.* {\&lt;br /&gt;
    # 1. VORBEREITUNG\&lt;br /&gt;
    my ($cleanEvent, $clientId) = $EVENT =~ /^(.*)\s\[(.*)\]$/;;\&lt;br /&gt;
    $cleanEvent //= $EVENT;;\&lt;br /&gt;
    $clientId   //= &amp;quot;unknown&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    my %lightRooms = (\&lt;br /&gt;
        &amp;quot;esszimmer&amp;quot;  =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe01_Ez&amp;quot;,       label =&amp;gt; &amp;quot;Licht Esszimmer&amp;quot; },\&lt;br /&gt;
        &amp;quot;küche&amp;quot;      =&amp;gt; { dev =&amp;gt; &amp;quot;Deckenlampe_Kue&amp;quot;,  label =&amp;gt; &amp;quot;Licht Küche&amp;quot; },\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot; =&amp;gt; { dev =&amp;gt; &amp;quot;Lampe06_Dek&amp;quot;,         label =&amp;gt; &amp;quot;Licht Wohnzimmer&amp;quot; }\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    my %vacRooms = (\&lt;br /&gt;
        &amp;quot;arbeitszimmer&amp;quot; =&amp;gt; &amp;quot;Arbeitszimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;badezimmer&amp;quot; =&amp;gt; &amp;quot;Badezimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;esszimmer&amp;quot; =&amp;gt; &amp;quot;Esszimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;flur&amp;quot; =&amp;gt; &amp;quot;Flur&amp;quot;,\&lt;br /&gt;
        &amp;quot;küche&amp;quot; =&amp;gt; &amp;quot;Küche&amp;quot;,\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot; =&amp;gt; &amp;quot;Wohnzimmer&amp;quot;\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    my $onRegEx  = &#039;\b(an|ein|einschalten|starte|aktivier|aktiviere)\b&#039;;;\&lt;br /&gt;
    my $offRegEx = &#039;\b(aus|ausschalten|stop|stoppe|beende|deaktivier|deaktiviere)\b&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
    my @commands = split(/\s*(?:und|dann|,)\s*/, lc($cleanEvent));;\&lt;br /&gt;
\&lt;br /&gt;
    # 2. DER BEFEHLS-LOOP\&lt;br /&gt;
    foreach my $cmd_part (@commands) {\&lt;br /&gt;
        \&lt;br /&gt;
        $cmd_part =~ s/^\s+|\s+$//g;;\&lt;br /&gt;
        \&lt;br /&gt;
        my $is_on  = ($cmd_part =~ /$onRegEx/) ? 1 : 0;;\&lt;br /&gt;
        my $is_off = ($cmd_part =~ /$offRegEx/) ? 1 : 0;;\&lt;br /&gt;
\&lt;br /&gt;
        $cmd_part =~ s/\b(ich|brauche|mach|bitte|kannst du|würdest du|mal|doch|den|das|die|im|in der)\b//g;;\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: STAUBSAUGER ---\&lt;br /&gt;
        if ($cmd_part =~ /(reinige|sauge|putze|staubsauger|roboter)/) {\&lt;br /&gt;
            my @found = grep { $cmd_part =~ /\b$_\b/ } keys %vacRooms;;\&lt;br /&gt;
            if (@found) {\&lt;br /&gt;
                fhem(&amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish clean_segment &amp;quot; . join(&amp;quot;,&amp;quot;, map { $vacRooms{$_} } @found));;\&lt;br /&gt;
            } else {\&lt;br /&gt;
                fhem(&amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish start&amp;quot;);;\&lt;br /&gt;
            }\&lt;br /&gt;
            next;;\&lt;br /&gt;
        } elsif ($cmd_part =~ /(lade|aufladen|dock|station|home)/) {\&lt;br /&gt;
            fhem(&amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish charge&amp;quot;);;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: FERNSEHER ---\&lt;br /&gt;
        if ($cmd_part =~ /(fernseher|tv|vuplus)/) {\&lt;br /&gt;
            fhem(&amp;quot;set VuPlus &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;));;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: AMBIENTE ---\&lt;br /&gt;
        if ($cmd_part =~ /ambiente/) {\&lt;br /&gt;
            if ($cmd_part =~ /(\d+)/) {\&lt;br /&gt;
                my $b = ($1 &amp;gt; 255 ? 255 : ($1 &amp;lt; 1 ? 1 : $1));;\&lt;br /&gt;
                fhem(&amp;quot;set LampeSzeneAlle brightness $b&amp;quot;);;\&lt;br /&gt;
            } else {\&lt;br /&gt;
                fhem(&amp;quot;set LampeSzeneAlle &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;));;\&lt;br /&gt;
            }\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: AMBILIGHT ---\&lt;br /&gt;
        if ($cmd_part =~ /ambilight/) {\&lt;br /&gt;
            system(&amp;quot;sshpass -p &#039;GEHEIMESPASSWORT&#039; ssh -o StrictHostKeyChecking=no root\@192.168.1.46 &#039;/usr/share/hyperhdr/scripts/hyperhdr_toggle.sh&#039;&amp;quot;);;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- INTENT: LICHT ---\&lt;br /&gt;
        my ($lightRoom) = grep { $cmd_part =~ /\b$_\b/ } keys %lightRooms;;\&lt;br /&gt;
        if ($lightRoom || $cmd_part =~ /(licht|lampe)/) {\&lt;br /&gt;
            my $dev = $lightRooms{$lightRoom}{dev} // &amp;quot;LampeSzeneAlle&amp;quot;;;\&lt;br /&gt;
            fhem(&amp;quot;set $dev &amp;quot; . ($is_off ? &amp;quot;off&amp;quot; : &amp;quot;on&amp;quot;)) if ($is_on || $is_off);;\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
\&lt;br /&gt;
        # --- HILFE (DYNAMISCH) ---\&lt;br /&gt;
        if ($cmd_part =~ /(hilfe|kommandos|übersicht)/) {\&lt;br /&gt;
\&lt;br /&gt;
            my $h = &#039;&amp;lt;div style=&amp;quot;text-align:left;;min-width:250px;;font-family:sans-serif;;&amp;quot;&amp;gt;&#039;;;\&lt;br /&gt;
            $h .= &#039;&amp;lt;b&amp;gt;Befehlsübersicht:&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
            # Licht\&lt;br /&gt;
            $h .= &#039;&amp;lt;u&amp;gt;Licht&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            for my $k (sort keys %lightRooms) {\&lt;br /&gt;
                $h .= &amp;quot;• &amp;quot;.$lightRooms{$k}{label}.&amp;quot; an/aus&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            }\&lt;br /&gt;
\&lt;br /&gt;
            # Staubsauger\&lt;br /&gt;
            $h .= &#039;&amp;lt;br&amp;gt;&amp;lt;u&amp;gt;Staubsauger&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            $h .= &amp;quot;• Sauge [Raum]&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;  Räume: &amp;quot;.join(&amp;quot;, &amp;quot;, map { ucfirst($_) } sort keys %vacRooms).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;• Lade Roberto&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
            # Sonstiges\&lt;br /&gt;
            $h .= &#039;&amp;lt;br&amp;gt;&amp;lt;u&amp;gt;Sonstiges&amp;lt;/u&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
            $h .= &amp;quot;• Fernseher an/aus&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;• Ambiente [an|aus|1-255]&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
            $h .= &amp;quot;• Ambilight&amp;lt;br&amp;gt;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
            $h .= &#039;&amp;lt;/div&amp;gt;&#039;;;\&lt;br /&gt;
\&lt;br /&gt;
            $h =~ s/&#039;/\\&amp;quot;/g;;\&lt;br /&gt;
\&lt;br /&gt;
            my $js = &amp;quot;if((document.querySelector(&#039;input[name=\&amp;quot;fw_id\&amp;quot;]&#039;)||{}).value===&#039;$clientId&#039;){FW_okDialog(&#039;$h&#039;)}&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
            FW_directNotify(&amp;quot;#FHEMWEB:$_&amp;quot;, $js, &amp;quot;&amp;quot;)\&lt;br /&gt;
                for devspec2array(&amp;quot;TYPE=FHEMWEB&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
            next;;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Unterschiede der Wege ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Feature&lt;br /&gt;
! Browser&lt;br /&gt;
! Hybrid (Atom Echo S3)&lt;br /&gt;
|-&lt;br /&gt;
| Wakeword&lt;br /&gt;
| Browser&lt;br /&gt;
| ESP (Hardware)&lt;br /&gt;
|-&lt;br /&gt;
| Push-to-Talk&lt;br /&gt;
| Ja&lt;br /&gt;
| Nein&lt;br /&gt;
|-&lt;br /&gt;
| Always-On&lt;br /&gt;
| Ja (bedingt)&lt;br /&gt;
| Ja&lt;br /&gt;
|-&lt;br /&gt;
| Architektur&lt;br /&gt;
| Software&lt;br /&gt;
| Software + Hardware&lt;br /&gt;
|-&lt;br /&gt;
| Stabilität&lt;br /&gt;
| Sehr stabil&lt;br /&gt;
| Sehr stabil (Wakeword extern)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== FireOS mit Fully Browser (Plus) ==&lt;br /&gt;
&lt;br /&gt;
Damit die Spracherkennung funktioniert:&lt;br /&gt;
&lt;br /&gt;
* Apps installieren:&lt;br /&gt;
   Google&lt;br /&gt;
   Speech Recognition &amp;amp; Synthesis&lt;br /&gt;
* FireOS:&lt;br /&gt;
   „Tastatur und Sprache“ → „Text-to-Speech“&lt;br /&gt;
* Fully Browser:&lt;br /&gt;
   &amp;lt;code&amp;gt;Enable JavaScriptInterface (PLUS)&amp;lt;/code&amp;gt; aktivieren&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:Schnittstellen]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40890</id>
		<title>FHEMWEB/VoiceControl: Web-STT &amp; Hardware-Wakeword</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40890"/>
		<updated>2026-03-30T18:14:13Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:VoiceControl_Logo.png|Mini|rechts]]&lt;br /&gt;
&lt;br /&gt;
== VoiceControl – Sprachsteuerung via Browser und Atom Echo s3r ==&lt;br /&gt;
&lt;br /&gt;
Diese Lösung ermöglicht eine flexible Sprachsteuerung für [[FHEM]]. Sprache wird in Text (Speech-to-Text) umgewandelt und als Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; bereitgestellt. Dieses Reading kann anschließend zentral (z. B. über &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DOIF&amp;lt;/code&amp;gt;) ausgewertet werden.&lt;br /&gt;
&lt;br /&gt;
Das System unterstützt zwei unterschiedliche Wege zur Spracherfassung:&lt;br /&gt;
&lt;br /&gt;
* Weg 1️⃣ (Software): Browser-basierte Komplettlösung&lt;br /&gt;
* Weg 2️⃣ (Hybrid): Hardware-Wakeword + Browser-Spracherkennung&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
Dort befinden sich in Post #1 auch alle benötigten Dateien.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Grundprinzip ====&lt;br /&gt;
* Sprache → Speech-to-Text&lt;br /&gt;
* Ergebnis → Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zentrale Logik verarbeitet Befehle&lt;br /&gt;
&lt;br /&gt;
==== Betriebsarten ====&lt;br /&gt;
* Push-to-Talk (nur Browser)&lt;br /&gt;
* Always-On mit Wakeword&lt;br /&gt;
* Hardware-Wakeword (Hybrid)&lt;br /&gt;
&lt;br /&gt;
==== Rückmeldungen ====&lt;br /&gt;
* Sprachausgabe (TTS)&lt;br /&gt;
* Visuelle Bubble im Browser&lt;br /&gt;
* Optional gezielte Rückmeldung per Client-ID&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 1️⃣: Browser-Lösung (voicecontrol.js) ==&lt;br /&gt;
&lt;br /&gt;
Das Script nutzt die Google Web Speech API. Unterstützt werden Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox wird aktuell nicht unterstützt.&lt;br /&gt;
&lt;br /&gt;
=== Bedienung ===&lt;br /&gt;
&lt;br /&gt;
==== Push-to-Talk ====&lt;br /&gt;
* Button gedrückt halten (~450 ms)&lt;br /&gt;
* Direkt sprechen (kein Wakeword nötig)&lt;br /&gt;
* Befehl wird sofort verarbeitet&lt;br /&gt;
&lt;br /&gt;
==== Always-On ====&lt;br /&gt;
* Kurzer Klick aktiviert Dauerbetrieb&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung permanentes Mithören&lt;br /&gt;
&lt;br /&gt;
==== Wakeword ====&lt;br /&gt;
* Standard: &amp;lt;code&amp;gt;james&amp;lt;/code&amp;gt;&lt;br /&gt;
* Anpassbar im Script:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const wakewords = [&amp;quot;james&amp;quot;];&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ablauf:&lt;br /&gt;
# „James“ sagen&lt;br /&gt;
# System antwortet „Ja?“&lt;br /&gt;
# Zeitfenster (~6 Sekunden) für Befehl&lt;br /&gt;
# Danach automatische Verarbeitung oder Abbruch&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&amp;lt;code&amp;gt;voicecontrol.js&amp;lt;/code&amp;gt; nach:&lt;br /&gt;
&amp;lt;code&amp;gt;/opt/fhem/www/voicecontrol/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBphone JavaScripts voicecontrol/voicecontrol.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Hinweis (HTTP ohne HTTPS) ====&lt;br /&gt;
Chrome benötigt Freigabe für Mikrofon:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;chrome://flags/#unsafely-treat-insecure-origin-as-secure&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eigene URL hinzufügen&lt;br /&gt;
* Auf &amp;lt;code&amp;gt;Enabled&amp;lt;/code&amp;gt; setzen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 2️⃣: Hybrid-Lösung mit Browser + Atom Echo (voicecontrol_echo.js) ==&lt;br /&gt;
&lt;br /&gt;
Diese Variante kombiniert Hardware und Software:&lt;br /&gt;
&lt;br /&gt;
* Wakeword-Erkennung erfolgt auf dem ESP (Atom Echo s3r)&lt;br /&gt;
* Die eigentliche Sprachverarbeitung (Speech-to-Text) erfolgt im Browser&lt;br /&gt;
&lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
&lt;br /&gt;
# Wakeword wird auf dem ESP erkannt&lt;br /&gt;
# FHEM erzeugt Event (z. B. &amp;lt;code&amp;gt;james_detected&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Browser empfängt Event via WebSocket&lt;br /&gt;
# Speech-to-Text startet im Browser&lt;br /&gt;
# Ergebnis wird an FHEM übertragen&lt;br /&gt;
&lt;br /&gt;
=== Aktivierung ===&lt;br /&gt;
&lt;br /&gt;
* Kurzer Klick auf Button aktiviert/deaktiviert System&lt;br /&gt;
* Baut WebSocket-Verbindung zu FHEM auf&lt;br /&gt;
* Kein Push-to-Talk-Modus&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration im Javascript ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const DEVICE  = &amp;quot;atom_echos3r_9888e00f4280&amp;quot;;&lt;br /&gt;
const TRIGGER = &amp;quot;james_detected&amp;quot;;&lt;br /&gt;
const FHEM_IP = &amp;quot;192.168.1.76:8085&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; → FHEM-Device des ESP&lt;br /&gt;
* &amp;lt;code&amp;gt;TRIGGER&amp;lt;/code&amp;gt; → Event bei Wakeword&lt;br /&gt;
* &amp;lt;code&amp;gt;FHEM_IP&amp;lt;/code&amp;gt; → FHEM-Server&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration in der Yaml ===&lt;br /&gt;
&lt;br /&gt;
Damit sich der ESP mit eurem MQTT-Server und WLAN verbindet, müssen folgende Stellen angepasst werden.&lt;br /&gt;
&lt;br /&gt;
   wifi:&lt;br /&gt;
     ssid: &amp;quot;YOUR_SSID&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     fast_connect: true&lt;br /&gt;
&lt;br /&gt;
   mqtt:&lt;br /&gt;
     broker: 192.168.1.76&lt;br /&gt;
     port: 1884&lt;br /&gt;
     username: &amp;quot;YOUR_USERNAME&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     topic_prefix: atom_echo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wakeword (ESP / ESPHome) ===&lt;br /&gt;
&lt;br /&gt;
Das Wakeword wird direkt auf dem ESP definiert:&lt;br /&gt;
&lt;br /&gt;
* Umsetzung über ESPHome&lt;br /&gt;
* Eine große Auswahl an Wakewords sind hier zu finden:&lt;br /&gt;
   https://github.com/TaterTotterson/microWakeWords&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel, wie das Wakeword definiert wird.&lt;br /&gt;
&lt;br /&gt;
   micro_wake_word:&lt;br /&gt;
     id: mww&lt;br /&gt;
     microphone: echo_mic&lt;br /&gt;
     models:&lt;br /&gt;
       - model: &amp;quot;https://github.com/TaterTotterson/microWakeWords/raw/main/microWakeWordsV2/james.json&amp;quot;&lt;br /&gt;
         id: james_model&lt;br /&gt;
         probability_cutoff: 0.6   &lt;br /&gt;
&lt;br /&gt;
=== Kommunikation ===&lt;br /&gt;
&lt;br /&gt;
* WebSocket-Verbindung zu FHEM&lt;br /&gt;
* Lauscht auf Device-Events&lt;br /&gt;
* Automatischer Reconnect bei Abbruch&lt;br /&gt;
&lt;br /&gt;
=== Ablauf nach Wakeword ===&lt;br /&gt;
&lt;br /&gt;
# System sagt „Ja?“&lt;br /&gt;
# Browser startet SpeechRecognition&lt;br /&gt;
# Nutzer spricht Befehl&lt;br /&gt;
# Befehl wird verarbeitet&lt;br /&gt;
# Rückmeldung „Erledigt“&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
siehe oben wie unter Punkt 1.&lt;br /&gt;
&lt;br /&gt;
== Zentrale Auswertung (Logik) ==&lt;br /&gt;
&lt;br /&gt;
Beide Wege schreiben in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;global:STT&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung erfolgt zentral über ein &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: notify ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod n_VoiceControl notify global:STT:.* {\&lt;br /&gt;
    # Parsing: Befehl und ID trennen\&lt;br /&gt;
    my ($cleanEvent, $clientId) = $EVENT =~ /^(.*)\s\[(.*)\]$/;;\&lt;br /&gt;
    $cleanEvent //= $EVENT;;\&lt;br /&gt;
    $clientId   //= &amp;quot;unknown&amp;quot;;;\&lt;br /&gt;
    my $event_lc = lc($cleanEvent);;\&lt;br /&gt;
\&lt;br /&gt;
    # Befehlmapping\&lt;br /&gt;
    my %simpleCmds = (\&lt;br /&gt;
        &amp;quot;stt: esszimmer licht an&amp;quot;  =&amp;gt; &amp;quot;set Lampe01_Ez on&amp;quot;,\&lt;br /&gt;
        &amp;quot;stt: esszimmer licht aus&amp;quot; =&amp;gt; &amp;quot;set Lampe01_Ez off&amp;quot;,\&lt;br /&gt;
        &amp;quot;stt: küche licht an&amp;quot;      =&amp;gt; &amp;quot;set Deckenlampe_Kue on&amp;quot;,\&lt;br /&gt;
        &amp;quot;stt: küche licht aus&amp;quot;     =&amp;gt; &amp;quot;set Deckenlampe_Kue off&amp;quot;,\&lt;br /&gt;
        &amp;quot;stt: fernseher an&amp;quot;        =&amp;gt; &amp;quot;set VuPlus on&amp;quot;,\&lt;br /&gt;
        &amp;quot;stt: fernseher aus&amp;quot;       =&amp;gt; &amp;quot;set VuPlus off&amp;quot;,\&lt;br /&gt;
        &amp;quot;stt: lade roberto&amp;quot;        =&amp;gt; &amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish charge&amp;quot;,\&lt;br /&gt;
        &amp;quot;stt: ambilight&amp;quot;    =&amp;gt; &#039;{ system(&amp;quot;sshpass -p \&#039;1431Fhem1982\&#039; ssh -o StrictHostKeyChecking=no root\@192.168.1.46 \&amp;quot;/usr/share/hyperhdr/scripts/hyperhdr_toggle.sh\&amp;quot;&amp;quot;) }&#039;,\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    if (exists $simpleCmds{$event_lc}) {\&lt;br /&gt;
        fhem($simpleCmds{$event_lc});;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    # Valetudo\&lt;br /&gt;
    elsif ($event_lc =~ /reinige/) {\&lt;br /&gt;
        my %rooms = (\&lt;br /&gt;
        &amp;quot;arbeitszimmer&amp;quot; =&amp;gt;&amp;quot;Arbeitszimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;badezimmer&amp;quot;    =&amp;gt;&amp;quot;Badezimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;esszimmer&amp;quot;     =&amp;gt;&amp;quot;Esszimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;flur&amp;quot;          =&amp;gt;&amp;quot;Flur&amp;quot;,\&lt;br /&gt;
        &amp;quot;küche&amp;quot;         =&amp;gt;&amp;quot;Küche&amp;quot;,\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot;    =&amp;gt;&amp;quot;Wohnzimmer&amp;quot;);;\&lt;br /&gt;
        my @found = grep { index($event_lc, $_) != -1 } keys %rooms;;\&lt;br /&gt;
        fhem(&amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish clean_segment &amp;quot;.join(&amp;quot;,&amp;quot;, map { $rooms{$_} } @found)) if @found;;\&lt;br /&gt;
    }\&lt;br /&gt;
    # Lampenscene\&lt;br /&gt;
    elsif ($event_lc =~ /ambiente/) {\&lt;br /&gt;
        if    ($event_lc =~ /(\d+)/) { my $b = ($1 &amp;gt; 255 ? 255 : ($1 &amp;lt; 1 ? 1 : $1));; fhem(&amp;quot;set LampeSzeneAlle brightness $b&amp;quot;) }\&lt;br /&gt;
        elsif ($event_lc =~ /an/)     { fhem(&amp;quot;set LampeSzeneAlle on&amp;quot;) }\&lt;br /&gt;
        elsif ($event_lc =~ /aus/)    { fhem(&amp;quot;set LampeSzeneAlle off&amp;quot;) }\&lt;br /&gt;
    }\&lt;br /&gt;
    # Kommandoübersicht\&lt;br /&gt;
    elsif ($event_lc =~ /kommandos/) {\&lt;br /&gt;
        my $h = &#039;&amp;lt;div style=&amp;quot;text-align:left;;min-width:200px;;font-family:sans-serif;;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Befehlsübersicht:&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
        $h .= &amp;quot;• &amp;quot;.($_ =~ s/^stt: //r).&amp;quot;&amp;lt;br&amp;gt;&amp;quot; for sort keys %simpleCmds;;\&lt;br /&gt;
        $h .= &amp;quot;• reinige [küche, bad, ...]&amp;lt;br&amp;gt;• ambiente [an|aus|0-255]&amp;lt;/div&amp;gt;&amp;quot;;;\&lt;br /&gt;
        $h =~ s/&#039;/\\&amp;quot;/g;;\&lt;br /&gt;
\&lt;br /&gt;
        my $js = &amp;quot;if((document.querySelector(&#039;input[name=\&amp;quot;fw_id\&amp;quot;]&#039;)||{}).value===&#039;$clientId&#039;){FW_okDialog(&#039;$h&#039;)}&amp;quot;;;\&lt;br /&gt;
        FW_directNotify(&amp;quot;#FHEMWEB:$_&amp;quot;, $js, &amp;quot;&amp;quot;)\&lt;br /&gt;
            for devspec2array(&amp;quot;TYPE=FHEMWEB&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Unterschiede der Wege ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Feature&lt;br /&gt;
! Browser&lt;br /&gt;
! Hybrid (Atom Echo S3)&lt;br /&gt;
|-&lt;br /&gt;
| Wakeword&lt;br /&gt;
| Browser&lt;br /&gt;
| ESP (Hardware)&lt;br /&gt;
|-&lt;br /&gt;
| Push-to-Talk&lt;br /&gt;
| Ja&lt;br /&gt;
| Nein&lt;br /&gt;
|-&lt;br /&gt;
| Always-On&lt;br /&gt;
| Ja (bedingt)&lt;br /&gt;
| Ja&lt;br /&gt;
|-&lt;br /&gt;
| Audioaufnahme&lt;br /&gt;
| Möglich&lt;br /&gt;
| Nur nach Trigger&lt;br /&gt;
|-&lt;br /&gt;
| Architektur&lt;br /&gt;
| Software&lt;br /&gt;
| Software + Hardware&lt;br /&gt;
|-&lt;br /&gt;
| Stabilität&lt;br /&gt;
| Stabil&lt;br /&gt;
| Sehr stabil (Wakeword extern)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== FireOS mit Fully Browser (Plus) ==&lt;br /&gt;
&lt;br /&gt;
Damit die Spracherkennung funktioniert:&lt;br /&gt;
&lt;br /&gt;
* Apps installieren:&lt;br /&gt;
   Google&lt;br /&gt;
   Speech Recognition &amp;amp; Synthesis&lt;br /&gt;
* FireOS:&lt;br /&gt;
   „Tastatur und Sprache“ → „Text-to-Speech“&lt;br /&gt;
* Fully Browser:&lt;br /&gt;
   &amp;lt;code&amp;gt;Enable JavaScriptInterface (PLUS)&amp;lt;/code&amp;gt; aktivieren&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:Schnittstellen]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40889</id>
		<title>FHEMWEB/VoiceControl: Web-STT &amp; Hardware-Wakeword</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40889"/>
		<updated>2026-03-30T18:07:40Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:VoiceControl_Logo.png|Mini|rechts]]&lt;br /&gt;
&lt;br /&gt;
== VoiceControl – Sprachsteuerung via Browser und Atom Echo S3 ==&lt;br /&gt;
&lt;br /&gt;
Diese Lösung ermöglicht eine flexible Sprachsteuerung für [[FHEM]]. Sprache wird in Text (Speech-to-Text) umgewandelt und als Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; bereitgestellt. Dieses Reading kann anschließend zentral (z. B. über &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DOIF&amp;lt;/code&amp;gt;) ausgewertet werden.&lt;br /&gt;
&lt;br /&gt;
Das System unterstützt zwei unterschiedliche Wege zur Spracherfassung:&lt;br /&gt;
&lt;br /&gt;
* Weg 1️⃣ (Software): Browser-basierte Komplettlösung&lt;br /&gt;
* Weg 2️⃣ (Hybrid): Hardware-Wakeword + Browser-Spracherkennung&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
Dort befinden sich in Post #1 auch alle benötigten Dateien.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Grundprinzip ====&lt;br /&gt;
* Sprache → Speech-to-Text&lt;br /&gt;
* Ergebnis → Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zentrale Logik verarbeitet Befehle&lt;br /&gt;
&lt;br /&gt;
==== Betriebsarten ====&lt;br /&gt;
* Push-to-Talk (nur Browser)&lt;br /&gt;
* Always-On mit Wakeword&lt;br /&gt;
* Hardware-Wakeword (Hybrid)&lt;br /&gt;
&lt;br /&gt;
==== Rückmeldungen ====&lt;br /&gt;
* Sprachausgabe (TTS)&lt;br /&gt;
* Visuelle Bubble im Browser&lt;br /&gt;
* Optional gezielte Rückmeldung per Client-ID&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 1️⃣: Browser-Lösung (voicecontrol.js) ==&lt;br /&gt;
&lt;br /&gt;
Das Script nutzt die Google Web Speech API. Unterstützt werden Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox wird aktuell nicht unterstützt.&lt;br /&gt;
&lt;br /&gt;
=== Bedienung ===&lt;br /&gt;
&lt;br /&gt;
==== Push-to-Talk ====&lt;br /&gt;
* Button gedrückt halten (~450 ms)&lt;br /&gt;
* Direkt sprechen (kein Wakeword nötig)&lt;br /&gt;
* Befehl wird sofort verarbeitet&lt;br /&gt;
&lt;br /&gt;
==== Always-On ====&lt;br /&gt;
* Kurzer Klick aktiviert Dauerbetrieb&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung permanentes Mithören&lt;br /&gt;
&lt;br /&gt;
==== Wakeword ====&lt;br /&gt;
* Standard: &amp;lt;code&amp;gt;james&amp;lt;/code&amp;gt;&lt;br /&gt;
* Anpassbar im Script:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const wakewords = [&amp;quot;james&amp;quot;];&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ablauf:&lt;br /&gt;
# „James“ sagen&lt;br /&gt;
# System antwortet „Ja?“&lt;br /&gt;
# Zeitfenster (~6 Sekunden) für Befehl&lt;br /&gt;
# Danach automatische Verarbeitung oder Abbruch&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&amp;lt;code&amp;gt;voicecontrol.js&amp;lt;/code&amp;gt; nach:&lt;br /&gt;
&amp;lt;code&amp;gt;/opt/fhem/www/voicecontrol/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBphone JavaScripts voicecontrol/voicecontrol.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Hinweis (HTTP ohne HTTPS) ====&lt;br /&gt;
Chrome benötigt Freigabe für Mikrofon:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;chrome://flags/#unsafely-treat-insecure-origin-as-secure&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eigene URL hinzufügen&lt;br /&gt;
* Auf &amp;lt;code&amp;gt;Enabled&amp;lt;/code&amp;gt; setzen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 2️⃣: Hybrid-Lösung mit Browser + Atom Echo (voicecontrol_echo.js) ==&lt;br /&gt;
&lt;br /&gt;
Diese Variante kombiniert Hardware und Software:&lt;br /&gt;
&lt;br /&gt;
* Wakeword-Erkennung erfolgt auf dem ESP (Atom Echo S3)&lt;br /&gt;
* Die eigentliche Sprachverarbeitung (Speech-to-Text) erfolgt im Browser&lt;br /&gt;
&lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
&lt;br /&gt;
# Wakeword wird auf dem ESP erkannt&lt;br /&gt;
# FHEM erzeugt Event (z. B. &amp;lt;code&amp;gt;james_detected&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Browser empfängt Event via WebSocket&lt;br /&gt;
# Speech-to-Text startet im Browser&lt;br /&gt;
# Ergebnis wird an FHEM übertragen&lt;br /&gt;
&lt;br /&gt;
=== Aktivierung ===&lt;br /&gt;
&lt;br /&gt;
* Kurzer Klick auf Button aktiviert/deaktiviert System&lt;br /&gt;
* Baut WebSocket-Verbindung zu FHEM auf&lt;br /&gt;
* Kein Push-to-Talk-Modus&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration im Javascript ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const DEVICE  = &amp;quot;atom_echos3r_9888e00f4280&amp;quot;;&lt;br /&gt;
const TRIGGER = &amp;quot;james_detected&amp;quot;;&lt;br /&gt;
const FHEM_IP = &amp;quot;192.168.1.76:8085&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; → FHEM-Device des ESP&lt;br /&gt;
* &amp;lt;code&amp;gt;TRIGGER&amp;lt;/code&amp;gt; → Event bei Wakeword&lt;br /&gt;
* &amp;lt;code&amp;gt;FHEM_IP&amp;lt;/code&amp;gt; → FHEM-Server&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration in der Yaml ===&lt;br /&gt;
&lt;br /&gt;
Damit sich der ESP mit eurem MQTT-Server und WLAN verbindet, müssen folgende Stellen angepasst werden.&lt;br /&gt;
&lt;br /&gt;
   wifi:&lt;br /&gt;
     ssid: &amp;quot;YOUR_SSID&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     fast_connect: true&lt;br /&gt;
&lt;br /&gt;
   mqtt:&lt;br /&gt;
     broker: 192.168.1.76&lt;br /&gt;
     port: 1884&lt;br /&gt;
     username: &amp;quot;YOUR_USERNAME&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     topic_prefix: atom_echo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wakeword (ESP / ESPHome) ===&lt;br /&gt;
&lt;br /&gt;
Das Wakeword wird direkt auf dem ESP definiert:&lt;br /&gt;
&lt;br /&gt;
* Umsetzung über ESPHome&lt;br /&gt;
* Eine große Auswahl an Wakewords sind hier zu finden:&lt;br /&gt;
   https://github.com/TaterTotterson/microWakeWords&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel, wie das Wakeword definiert wird.&lt;br /&gt;
&lt;br /&gt;
   micro_wake_word:&lt;br /&gt;
     id: mww&lt;br /&gt;
     microphone: echo_mic&lt;br /&gt;
     models:&lt;br /&gt;
       - model: &amp;quot;https://github.com/TaterTotterson/microWakeWords/raw/main/microWakeWordsV2/james.json&amp;quot;&lt;br /&gt;
         id: james_model&lt;br /&gt;
         probability_cutoff: 0.6   &lt;br /&gt;
&lt;br /&gt;
=== Kommunikation ===&lt;br /&gt;
&lt;br /&gt;
* WebSocket-Verbindung zu FHEM&lt;br /&gt;
* Lauscht auf Device-Events&lt;br /&gt;
* Automatischer Reconnect bei Abbruch&lt;br /&gt;
&lt;br /&gt;
=== Ablauf nach Wakeword ===&lt;br /&gt;
&lt;br /&gt;
# System sagt „Ja?“&lt;br /&gt;
# Browser startet SpeechRecognition&lt;br /&gt;
# Nutzer spricht Befehl&lt;br /&gt;
# Befehl wird verarbeitet&lt;br /&gt;
# Rückmeldung „Erledigt“&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
siehe oben wie unter Punkt 1.&lt;br /&gt;
&lt;br /&gt;
== Zentrale Auswertung (Logik) ==&lt;br /&gt;
&lt;br /&gt;
Beide Wege schreiben in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;global:STT&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung erfolgt zentral über ein &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: notify ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod n_VoiceControl notify global:STT:.* {\&lt;br /&gt;
    # Parsing: Befehl und ID trennen\&lt;br /&gt;
    my ($cleanEvent, $clientId) = $EVENT =~ /^(.*)\s\[(.*)\]$/;;\&lt;br /&gt;
    $cleanEvent //= $EVENT;;\&lt;br /&gt;
    $clientId   //= &amp;quot;unknown&amp;quot;;;\&lt;br /&gt;
    my $event_lc = lc($cleanEvent);;\&lt;br /&gt;
\&lt;br /&gt;
    # Befehlmapping\&lt;br /&gt;
    my %simpleCmds = (\&lt;br /&gt;
        &amp;quot;stt: esszimmer licht an&amp;quot;  =&amp;gt; &amp;quot;set Lampe01_Ez on&amp;quot;,\&lt;br /&gt;
        &amp;quot;stt: esszimmer licht aus&amp;quot; =&amp;gt; &amp;quot;set Lampe01_Ez off&amp;quot;,\&lt;br /&gt;
        &amp;quot;stt: küche licht an&amp;quot;      =&amp;gt; &amp;quot;set Deckenlampe_Kue on&amp;quot;,\&lt;br /&gt;
        &amp;quot;stt: küche licht aus&amp;quot;     =&amp;gt; &amp;quot;set Deckenlampe_Kue off&amp;quot;,\&lt;br /&gt;
        &amp;quot;stt: fernseher an&amp;quot;        =&amp;gt; &amp;quot;set VuPlus on&amp;quot;,\&lt;br /&gt;
        &amp;quot;stt: fernseher aus&amp;quot;       =&amp;gt; &amp;quot;set VuPlus off&amp;quot;,\&lt;br /&gt;
        &amp;quot;stt: lade roberto&amp;quot;        =&amp;gt; &amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish charge&amp;quot;,\&lt;br /&gt;
        &amp;quot;stt: ambilight&amp;quot;    =&amp;gt; &#039;{ system(&amp;quot;sshpass -p \&#039;1431Fhem1982\&#039; ssh -o StrictHostKeyChecking=no root\@192.168.1.46 \&amp;quot;/usr/share/hyperhdr/scripts/hyperhdr_toggle.sh\&amp;quot;&amp;quot;) }&#039;,\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    if (exists $simpleCmds{$event_lc}) {\&lt;br /&gt;
        fhem($simpleCmds{$event_lc});;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    # Valetudo\&lt;br /&gt;
    elsif ($event_lc =~ /reinige/) {\&lt;br /&gt;
        my %rooms = (\&lt;br /&gt;
        &amp;quot;arbeitszimmer&amp;quot; =&amp;gt;&amp;quot;Arbeitszimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;badezimmer&amp;quot;    =&amp;gt;&amp;quot;Badezimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;esszimmer&amp;quot;     =&amp;gt;&amp;quot;Esszimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;flur&amp;quot;          =&amp;gt;&amp;quot;Flur&amp;quot;,\&lt;br /&gt;
        &amp;quot;küche&amp;quot;         =&amp;gt;&amp;quot;Küche&amp;quot;,\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot;    =&amp;gt;&amp;quot;Wohnzimmer&amp;quot;);;\&lt;br /&gt;
        my @found = grep { index($event_lc, $_) != -1 } keys %rooms;;\&lt;br /&gt;
        fhem(&amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish clean_segment &amp;quot;.join(&amp;quot;,&amp;quot;, map { $rooms{$_} } @found)) if @found;;\&lt;br /&gt;
    }\&lt;br /&gt;
    # Lampenscene\&lt;br /&gt;
    elsif ($event_lc =~ /ambiente/) {\&lt;br /&gt;
        if    ($event_lc =~ /(\d+)/) { my $b = ($1 &amp;gt; 255 ? 255 : ($1 &amp;lt; 1 ? 1 : $1));; fhem(&amp;quot;set LampeSzeneAlle brightness $b&amp;quot;) }\&lt;br /&gt;
        elsif ($event_lc =~ /an/)     { fhem(&amp;quot;set LampeSzeneAlle on&amp;quot;) }\&lt;br /&gt;
        elsif ($event_lc =~ /aus/)    { fhem(&amp;quot;set LampeSzeneAlle off&amp;quot;) }\&lt;br /&gt;
    }\&lt;br /&gt;
    # Kommandoübersicht\&lt;br /&gt;
    elsif ($event_lc =~ /kommandos/) {\&lt;br /&gt;
        my $h = &#039;&amp;lt;div style=&amp;quot;text-align:left;;min-width:200px;;font-family:sans-serif;;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Befehlsübersicht:&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
        $h .= &amp;quot;• &amp;quot;.($_ =~ s/^stt: //r).&amp;quot;&amp;lt;br&amp;gt;&amp;quot; for sort keys %simpleCmds;;\&lt;br /&gt;
        $h .= &amp;quot;• reinige [küche, bad, ...]&amp;lt;br&amp;gt;• ambiente [an|aus|0-255]&amp;lt;/div&amp;gt;&amp;quot;;;\&lt;br /&gt;
        $h =~ s/&#039;/\\&amp;quot;/g;;\&lt;br /&gt;
\&lt;br /&gt;
        my $js = &amp;quot;if((document.querySelector(&#039;input[name=\&amp;quot;fw_id\&amp;quot;]&#039;)||{}).value===&#039;$clientId&#039;){FW_okDialog(&#039;$h&#039;)}&amp;quot;;;\&lt;br /&gt;
        FW_directNotify(&amp;quot;#FHEMWEB:$_&amp;quot;, $js, &amp;quot;&amp;quot;)\&lt;br /&gt;
            for devspec2array(&amp;quot;TYPE=FHEMWEB&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Unterschiede der Wege ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Feature&lt;br /&gt;
! Browser&lt;br /&gt;
! Hybrid (Atom Echo S3)&lt;br /&gt;
|-&lt;br /&gt;
| Wakeword&lt;br /&gt;
| Browser&lt;br /&gt;
| ESP (Hardware)&lt;br /&gt;
|-&lt;br /&gt;
| Push-to-Talk&lt;br /&gt;
| Ja&lt;br /&gt;
| Nein&lt;br /&gt;
|-&lt;br /&gt;
| Always-On&lt;br /&gt;
| Ja (bedingt)&lt;br /&gt;
| Ja&lt;br /&gt;
|-&lt;br /&gt;
| Audioaufnahme&lt;br /&gt;
| Möglich&lt;br /&gt;
| Nur nach Trigger&lt;br /&gt;
|-&lt;br /&gt;
| Architektur&lt;br /&gt;
| Software&lt;br /&gt;
| Software + Hardware&lt;br /&gt;
|-&lt;br /&gt;
| Stabilität&lt;br /&gt;
| Stabil&lt;br /&gt;
| Sehr stabil (Wakeword extern)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== FireOS mit Fully Browser (Plus) ==&lt;br /&gt;
&lt;br /&gt;
Damit die Spracherkennung funktioniert:&lt;br /&gt;
&lt;br /&gt;
* Apps installieren:&lt;br /&gt;
   Google&lt;br /&gt;
   Speech Recognition &amp;amp; Synthesis&lt;br /&gt;
* FireOS:&lt;br /&gt;
   „Tastatur und Sprache“ → „Text-to-Speech“&lt;br /&gt;
* Fully Browser:&lt;br /&gt;
   &amp;lt;code&amp;gt;Enable JavaScriptInterface (PLUS)&amp;lt;/code&amp;gt; aktivieren&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:Schnittstellen]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40888</id>
		<title>FHEMWEB/VoiceControl: Web-STT &amp; Hardware-Wakeword</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword&amp;diff=40888"/>
		<updated>2026-03-30T17:51:08Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: Die Seite wurde neu angelegt: „rechts  == VoiceControl – Sprachsteuerung via Browser und Atom Echo S3 ==  Diese Lösung ermöglicht eine flexible Sprachsteuerung für FHEM. Sprache wird in Text (Speech-to-Text) umgewandelt und als Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; bereitgestellt. Dieses Reading kann anschließend zentral (z. B. über &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DOIF&amp;lt;/code&amp;gt;) ausgewertet werden.  Das System unterstützt z…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:VoiceControl_Logo.png|Mini|rechts]]&lt;br /&gt;
&lt;br /&gt;
== VoiceControl – Sprachsteuerung via Browser und Atom Echo S3 ==&lt;br /&gt;
&lt;br /&gt;
Diese Lösung ermöglicht eine flexible Sprachsteuerung für [[FHEM]]. Sprache wird in Text (Speech-to-Text) umgewandelt und als Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; bereitgestellt. Dieses Reading kann anschließend zentral (z. B. über &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DOIF&amp;lt;/code&amp;gt;) ausgewertet werden.&lt;br /&gt;
&lt;br /&gt;
Das System unterstützt zwei unterschiedliche Wege zur Spracherfassung:&lt;br /&gt;
&lt;br /&gt;
* Weg 1️⃣ (Software): Browser-basierte Komplettlösung&lt;br /&gt;
* Weg 2️⃣ (Hybrid): Hardware-Wakeword + Browser-Spracherkennung&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}&lt;br /&gt;
Dort befinden sich in Post #1 auch alle benötigten Dateien.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Grundprinzip ====&lt;br /&gt;
* Sprache → Speech-to-Text&lt;br /&gt;
* Ergebnis → Reading &amp;lt;code&amp;gt;STT&amp;lt;/code&amp;gt; im Device &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zentrale Logik verarbeitet Befehle&lt;br /&gt;
&lt;br /&gt;
==== Betriebsarten ====&lt;br /&gt;
* Push-to-Talk (nur Browser)&lt;br /&gt;
* Always-On mit Wakeword&lt;br /&gt;
* Hardware-Wakeword (Hybrid)&lt;br /&gt;
&lt;br /&gt;
==== Rückmeldungen ====&lt;br /&gt;
* Sprachausgabe (TTS)&lt;br /&gt;
* Visuelle Bubble im Browser&lt;br /&gt;
* Optional gezielte Rückmeldung per Client-ID&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 1️⃣: Browser-Lösung (voicecontrol.js) ==&lt;br /&gt;
&lt;br /&gt;
Das Script nutzt die Google Web Speech API. Unterstützt werden Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox wird aktuell nicht unterstützt.&lt;br /&gt;
&lt;br /&gt;
=== Bedienung ===&lt;br /&gt;
&lt;br /&gt;
==== Push-to-Talk ====&lt;br /&gt;
* Button gedrückt halten (~450 ms)&lt;br /&gt;
* Direkt sprechen (kein Wakeword nötig)&lt;br /&gt;
* Befehl wird sofort verarbeitet&lt;br /&gt;
&lt;br /&gt;
==== Always-On ====&lt;br /&gt;
* Kurzer Klick aktiviert Dauerbetrieb&lt;br /&gt;
* Wakeword erforderlich (Standard: „James“)&lt;br /&gt;
* Nach Aktivierung permanentes Mithören&lt;br /&gt;
&lt;br /&gt;
==== Wakeword ====&lt;br /&gt;
* Standard: &amp;lt;code&amp;gt;james&amp;lt;/code&amp;gt;&lt;br /&gt;
* Anpassbar im Script:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const wakewords = [&amp;quot;james&amp;quot;];&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ablauf:&lt;br /&gt;
# „James“ sagen&lt;br /&gt;
# System antwortet „Ja?“&lt;br /&gt;
# Zeitfenster (~6 Sekunden) für Befehl&lt;br /&gt;
# Danach automatische Verarbeitung oder Abbruch&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Datei kopieren ====&lt;br /&gt;
&amp;lt;code&amp;gt;voicecontrol.js&amp;lt;/code&amp;gt; nach:&lt;br /&gt;
&amp;lt;code&amp;gt;/opt/fhem/www/voicecontrol/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einbindung ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEBphone JavaScripts voicecontrol/voicecontrol.js&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Hinweis (HTTP ohne HTTPS) ====&lt;br /&gt;
Chrome benötigt Freigabe für Mikrofon:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;chrome://flags/#unsafely-treat-insecure-origin-as-secure&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eigene URL hinzufügen&lt;br /&gt;
* Auf &amp;lt;code&amp;gt;Enabled&amp;lt;/code&amp;gt; setzen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weg 2️⃣: Hybrid-Lösung mit Browser + Atom Echo (voicecontrol_echo.js) ==&lt;br /&gt;
&lt;br /&gt;
Diese Variante kombiniert Hardware und Software:&lt;br /&gt;
&lt;br /&gt;
* Wakeword-Erkennung erfolgt auf dem ESP (Atom Echo S3)&lt;br /&gt;
* Die eigentliche Sprachverarbeitung (Speech-to-Text) erfolgt im Browser&lt;br /&gt;
&lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
&lt;br /&gt;
# Wakeword wird auf dem ESP erkannt&lt;br /&gt;
# FHEM erzeugt Event (z. B. &amp;lt;code&amp;gt;james_detected&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Browser empfängt Event via WebSocket&lt;br /&gt;
# Speech-to-Text startet im Browser&lt;br /&gt;
# Ergebnis wird an FHEM übertragen&lt;br /&gt;
&lt;br /&gt;
=== Aktivierung ===&lt;br /&gt;
&lt;br /&gt;
* Kurzer Klick auf Button aktiviert/deaktiviert System&lt;br /&gt;
* Baut WebSocket-Verbindung zu FHEM auf&lt;br /&gt;
* Kein Push-to-Talk-Modus&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration im Javascript ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const DEVICE  = &amp;quot;atom_echos3r_9888e00f4280&amp;quot;;&lt;br /&gt;
const TRIGGER = &amp;quot;james_detected&amp;quot;;&lt;br /&gt;
const FHEM_IP = &amp;quot;192.168.1.76:8085&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; → FHEM-Device des ESP&lt;br /&gt;
* &amp;lt;code&amp;gt;TRIGGER&amp;lt;/code&amp;gt; → Event bei Wakeword&lt;br /&gt;
* &amp;lt;code&amp;gt;FHEM_IP&amp;lt;/code&amp;gt; → FHEM-Server&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration in der Yaml ===&lt;br /&gt;
&lt;br /&gt;
Damit sich der ESP mit eurem MQTT-Server und WLAN verbindet, müssen folgende Stellen angepasst werden.&lt;br /&gt;
&lt;br /&gt;
   wifi:&lt;br /&gt;
     ssid: &amp;quot;YOUR_SSID&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     fast_connect: true&lt;br /&gt;
&lt;br /&gt;
   mqtt:&lt;br /&gt;
     broker: 192.168.1.76&lt;br /&gt;
     port: 1884&lt;br /&gt;
     username: &amp;quot;YOUR_USERNAME&amp;quot;&lt;br /&gt;
     password: &amp;quot;YOUR_PW&amp;quot;&lt;br /&gt;
     topic_prefix: atom_echo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wakeword (ESP / ESPHome) ===&lt;br /&gt;
&lt;br /&gt;
Das Wakeword wird direkt auf dem ESP definiert:&lt;br /&gt;
&lt;br /&gt;
* Umsetzung über ESPHome&lt;br /&gt;
* Eine große Auswahl an Wakewords sind hier zu finden:&lt;br /&gt;
   https://github.com/TaterTotterson/microWakeWords&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel, wie das Wakeword definiert wird.&lt;br /&gt;
&lt;br /&gt;
   micro_wake_word:&lt;br /&gt;
     id: mww&lt;br /&gt;
     microphone: echo_mic&lt;br /&gt;
     models:&lt;br /&gt;
       - model: &amp;quot;https://github.com/TaterTotterson/microWakeWords/raw/main/microWakeWordsV2/james.json&amp;quot;&lt;br /&gt;
         id: james_model&lt;br /&gt;
         probability_cutoff: 0.6   &lt;br /&gt;
&lt;br /&gt;
=== Kommunikation ===&lt;br /&gt;
&lt;br /&gt;
* WebSocket-Verbindung zu FHEM&lt;br /&gt;
* Lauscht auf Device-Events&lt;br /&gt;
* Automatischer Reconnect bei Abbruch&lt;br /&gt;
&lt;br /&gt;
=== Ablauf nach Wakeword ===&lt;br /&gt;
&lt;br /&gt;
# System sagt „Ja?“&lt;br /&gt;
# Browser startet SpeechRecognition&lt;br /&gt;
# Nutzer spricht Befehl&lt;br /&gt;
# Befehl wird verarbeitet&lt;br /&gt;
# Rückmeldung „Erledigt“&lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
siehe oben wie unter Punkt 1.&lt;br /&gt;
&lt;br /&gt;
== Zentrale Auswertung (Logik) ==&lt;br /&gt;
&lt;br /&gt;
Beide Wege schreiben in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;global:STT&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung erfolgt zentral über ein &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: notify ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod n_VoiceControl notify global:STT:.* {\&lt;br /&gt;
    # Parsing: Befehl und ID trennen\&lt;br /&gt;
    my ($cleanEvent, $clientId) = $EVENT =~ /^(.*)\s\[(.*)\]$/;;\&lt;br /&gt;
    $cleanEvent //= $EVENT;;\&lt;br /&gt;
    $clientId   //= &amp;quot;unknown&amp;quot;;;\&lt;br /&gt;
    my $event_lc = lc($cleanEvent);;\&lt;br /&gt;
\&lt;br /&gt;
    # Befehlmapping\&lt;br /&gt;
    my %simpleCmds = (\&lt;br /&gt;
        &amp;quot;stt: esszimmer licht an&amp;quot;  =&amp;gt; &amp;quot;set Lampe01_Ez on&amp;quot;,\&lt;br /&gt;
        &amp;quot;stt: esszimmer licht aus&amp;quot; =&amp;gt; &amp;quot;set Lampe01_Ez off&amp;quot;,\&lt;br /&gt;
        &amp;quot;stt: küche licht an&amp;quot;      =&amp;gt; &amp;quot;set Deckenlampe_Kue on&amp;quot;,\&lt;br /&gt;
        &amp;quot;stt: küche licht aus&amp;quot;     =&amp;gt; &amp;quot;set Deckenlampe_Kue off&amp;quot;,\&lt;br /&gt;
        &amp;quot;stt: fernseher an&amp;quot;        =&amp;gt; &amp;quot;set VuPlus on&amp;quot;,\&lt;br /&gt;
        &amp;quot;stt: fernseher aus&amp;quot;       =&amp;gt; &amp;quot;set VuPlus off&amp;quot;,\&lt;br /&gt;
        &amp;quot;stt: lade roberto&amp;quot;        =&amp;gt; &amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish charge&amp;quot;,\&lt;br /&gt;
        &amp;quot;stt: ambilight&amp;quot;    =&amp;gt; &#039;{ system(&amp;quot;sshpass -p \&#039;1431Fhem1982\&#039; ssh -o StrictHostKeyChecking=no root\@192.168.1.46 \&amp;quot;/usr/share/hyperhdr/scripts/hyperhdr_toggle.sh\&amp;quot;&amp;quot;) }&#039;,\&lt;br /&gt;
    );;\&lt;br /&gt;
\&lt;br /&gt;
    if (exists $simpleCmds{$event_lc}) {\&lt;br /&gt;
        fhem($simpleCmds{$event_lc});;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    # Valetudo\&lt;br /&gt;
    elsif ($event_lc =~ /reinige/) {\&lt;br /&gt;
        my %rooms = (\&lt;br /&gt;
        &amp;quot;arbeitszimmer&amp;quot; =&amp;gt;&amp;quot;Arbeitszimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;badezimmer&amp;quot;    =&amp;gt;&amp;quot;Badezimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;esszimmer&amp;quot;     =&amp;gt;&amp;quot;Esszimmer&amp;quot;,\&lt;br /&gt;
        &amp;quot;flur&amp;quot;          =&amp;gt;&amp;quot;Flur&amp;quot;,\&lt;br /&gt;
        &amp;quot;küche&amp;quot;         =&amp;gt;&amp;quot;Küche&amp;quot;,\&lt;br /&gt;
        &amp;quot;wohnzimmer&amp;quot;    =&amp;gt;&amp;quot;Wohnzimmer&amp;quot;);;\&lt;br /&gt;
        my @found = grep { index($event_lc, $_) != -1 } keys %rooms;;\&lt;br /&gt;
        fhem(&amp;quot;set MQTT2_valetudo_FlusteredUnequaledFish clean_segment &amp;quot;.join(&amp;quot;,&amp;quot;, map { $rooms{$_} } @found)) if @found;;\&lt;br /&gt;
    }\&lt;br /&gt;
    # Lampenscene\&lt;br /&gt;
    elsif ($event_lc =~ /ambiente/) {\&lt;br /&gt;
        if    ($event_lc =~ /(\d+)/) { my $b = ($1 &amp;gt; 255 ? 255 : ($1 &amp;lt; 1 ? 1 : $1));; fhem(&amp;quot;set LampeSzeneAlle brightness $b&amp;quot;) }\&lt;br /&gt;
        elsif ($event_lc =~ /an/)     { fhem(&amp;quot;set LampeSzeneAlle on&amp;quot;) }\&lt;br /&gt;
        elsif ($event_lc =~ /aus/)    { fhem(&amp;quot;set LampeSzeneAlle off&amp;quot;) }\&lt;br /&gt;
    }\&lt;br /&gt;
    # Kommandoübersicht\&lt;br /&gt;
    elsif ($event_lc =~ /kommandos/) {\&lt;br /&gt;
        my $h = &#039;&amp;lt;div style=&amp;quot;text-align:left;;min-width:200px;;font-family:sans-serif;;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Befehlsübersicht:&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;;;\&lt;br /&gt;
        $h .= &amp;quot;• &amp;quot;.($_ =~ s/^stt: //r).&amp;quot;&amp;lt;br&amp;gt;&amp;quot; for sort keys %simpleCmds;;\&lt;br /&gt;
        $h .= &amp;quot;• reinige [küche, bad, ...]&amp;lt;br&amp;gt;• ambiente [an|aus|0-255]&amp;lt;/div&amp;gt;&amp;quot;;;\&lt;br /&gt;
        $h =~ s/&#039;/\\&amp;quot;/g;;\&lt;br /&gt;
\&lt;br /&gt;
        my $js = &amp;quot;if((document.querySelector(&#039;input[name=\&amp;quot;fw_id\&amp;quot;]&#039;)||{}).value===&#039;$clientId&#039;){FW_okDialog(&#039;$h&#039;)}&amp;quot;;;\&lt;br /&gt;
        FW_directNotify(&amp;quot;#FHEMWEB:$_&amp;quot;, $js, &amp;quot;&amp;quot;)\&lt;br /&gt;
            for devspec2array(&amp;quot;TYPE=FHEMWEB&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Unterschiede der Wege ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Feature&lt;br /&gt;
! Browser&lt;br /&gt;
! Hybrid (Atom Echo S3)&lt;br /&gt;
|-&lt;br /&gt;
| Wakeword&lt;br /&gt;
| Browser&lt;br /&gt;
| ESP (Hardware)&lt;br /&gt;
|-&lt;br /&gt;
| Push-to-Talk&lt;br /&gt;
| Ja&lt;br /&gt;
| Nein&lt;br /&gt;
|-&lt;br /&gt;
| Always-On&lt;br /&gt;
| Ja&lt;br /&gt;
| Ja&lt;br /&gt;
|-&lt;br /&gt;
| Audioaufnahme&lt;br /&gt;
| Permanent möglich&lt;br /&gt;
| Nur nach Trigger&lt;br /&gt;
|-&lt;br /&gt;
| Architektur&lt;br /&gt;
| Software&lt;br /&gt;
| Software + Hardware&lt;br /&gt;
|-&lt;br /&gt;
| Stabilität&lt;br /&gt;
| Stabil&lt;br /&gt;
| Sehr stabil (Wakeword extern)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== FireOS mit Fully Browser (Plus) ==&lt;br /&gt;
&lt;br /&gt;
Damit die Spracherkennung funktioniert:&lt;br /&gt;
&lt;br /&gt;
* Apps installieren:&lt;br /&gt;
   Google&lt;br /&gt;
   Speech Recognition &amp;amp; Synthesis&lt;br /&gt;
* FireOS:&lt;br /&gt;
   „Tastatur und Sprache“ → „Text-to-Speech“&lt;br /&gt;
* Fully Browser:&lt;br /&gt;
   &amp;lt;code&amp;gt;Enable JavaScriptInterface (PLUS)&amp;lt;/code&amp;gt; aktivieren&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:Schnittstellen]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/Widgets&amp;diff=40642</id>
		<title>FHEMWEB/Widgets</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/Widgets&amp;diff=40642"/>
		<updated>2026-01-03T09:38:44Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Widgets sind Frontendelemente, die der Dateneingabe und -anzeige dienen. Die hier gezeigten Frontendelemente werden durch [[FHEMWEB]] ([[PGM2]]) bereitgestellt.&lt;br /&gt;
&lt;br /&gt;
== Syntax ==&lt;br /&gt;
Die Syntax der Widgets ist unter dem Attribut [https://fhem.de/commandref_DE.html#widgetOverride widgetOverride] in der Befehlreferenz beschrieben.&lt;br /&gt;
&lt;br /&gt;
== Verwendung ==&lt;br /&gt;
Viele Module (u.a. [[DOIF]], [[dummy]], [[MQTT_DEVICE]], [[MQTT2_DEVICE]], [[MYSENSORS_DEVICE]], [[readingsGroup]]) bieten die Möglichkeit eine Benutzerschnittstelle zu erstellen.&lt;br /&gt;
&lt;br /&gt;
Widgets werden über &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;:&amp;lt;modifier&amp;gt;&amp;lt;/code&amp;gt;-Paare an [[Readings]] oder [[Attribute]] gebunden, dabei ist:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;&amp;lt;/code&amp;gt; ein Readingsname, ein Attribut oder ein Mapping-Argument.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;modifier&amp;gt;&amp;lt;/code&amp;gt; ein Widget einschliesslich Parameter.&lt;br /&gt;
&lt;br /&gt;
Bei der Erstellung spielen verschiedene Attribute zusammen:&lt;br /&gt;
&lt;br /&gt;
* Mit readingList (bzw. bei Modulen, die dies nicht kennen: widgetOverride) ist es möglich den Set-Befehl auf [[Readings]] zu erweitern, siehe [https://fhem.de/commandref_DE.html#readingList readingList].&lt;br /&gt;
* Mit [[setList]] (bzw. widgetOverride) werden die Widgets einem Reading zugeordnet und parametrisiert, siehe [https://fhem.de/commandref_DE.html#setList setList].&lt;br /&gt;
* Mit [[webCmd]] werden die Widgets angezeigt, siehe [https://fhem.de/commandref_DE.html#setList webCmd].&lt;br /&gt;
* Mit webCmdLabel werden die Widgets beschriftet und tabellarisch angeordnet, siehe [https://fhem.de/commandref_DE.html#webCmdLabel webCmdLabel].&lt;br /&gt;
* Mit widgetOverride ist es möglich, die im Modul festgelegten Widgets zu überschreiben, siehe [https://fhem.de/commandref_DE.html#widgetOverride widgetOverride].&lt;br /&gt;
* Mit [[eventMap]] ist es möglich Widgets an Mapping-Argumente zu binden, siehe [https://fhem.de/commandref_DE.html#eventMap eventMap] und {{Link2Forum|Topic=76755|LinkText=eventMap und Widget-Anbindung}}.&lt;br /&gt;
Das Zusammenspiel dieser Attribute ist u.A. im Artikel [[DeviceOverview anpassen#webCmd .26 Co|DeviceOverview anpassen]] näher erläutert.&lt;br /&gt;
&lt;br /&gt;
== Zusammenstellung der Widgets ==&lt;br /&gt;
[[Datei:fhemweb_widgets_1.png|mini|rechts]]&lt;br /&gt;
Die Bildschirmkopie zeigt das Benutzerinterface des Dummys mit den Widgets&lt;br /&gt;
&lt;br /&gt;
=== weitere Konfiguration ===&lt;br /&gt;
* &#039;&#039;&#039;knob&#039;&#039;&#039;, siehe https://github.com/aterrien/jQuery-Knob&lt;br /&gt;
* &#039;&#039;&#039;colorpicker&#039;&#039;&#039;, siehe [[Color]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
== Das Beispiel für [[DOIF/Import_von_Code_Snippets|Raw definition]] zum Ausprobieren ==&lt;br /&gt;
In der vorstehenden Abbildung fehlt das Look and Feel der Widgets. Mit dem folgenden Code-Snippet zum Ausprobieren, wird dies ergänzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod widgets dummy&lt;br /&gt;
attr widgets readingList 00select 01select 02selectnumbers 03selectnumbers 04textField 05textFieldNL 06textField-long 07textFieldNL-long 08slider 09multiple 10multiple-strict 11knob 12sortable 13sortable-strict 14sortable-given 15uzsuToggle 16uzsuSelect 17uzsuSelectRadio 18uzsuDropDown 19colorpicker_RGB 19colorpicker_HSV 19colorpicker_CT 19colorpicker_HUE 19colorpicker_BRI 20time 21iconRadio 21iconRadio_use4icon 22iconSwitch 23iconLabel 24iconButtons 24iconButtons_use4icon 25bitfield 26widgetList&lt;br /&gt;
attr widgets room 0_Test&lt;br /&gt;
attr widgets setList 00select:1,2,3,4,5,0,a,b,c,def,hik \&lt;br /&gt;
01select:select,1,2,3,4,5,0,a,b,c,def,hik \&lt;br /&gt;
02selectnumbers:selectnumbers,-30,3,33,1,lin \&lt;br /&gt;
03selectnumbers:selectnumbers,1,0.0625,140000,0,log10 \&lt;br /&gt;
04textField:textField \&lt;br /&gt;
05textFieldNL:textFieldNL\&lt;br /&gt;
06textField-long:textField-long,87 \&lt;br /&gt;
07textFieldNL-long:textFieldNL-long,87 \&lt;br /&gt;
08slider:slider,7,0.5,30,1 \&lt;br /&gt;
09multiple:multiple,shirt,shoes,skirt,trowsers \&lt;br /&gt;
10multiple-strict:multiple-strict,red,green,blue,yellow \&lt;br /&gt;
11knob:knob,min:0,max:360,width:50,height:50,step:1,lineCap:round,angleOffset:180,cursor:3,thickness:.3 \&lt;br /&gt;
12sortable:sortable,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
13sortable-strict:sortable-strict,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
14sortable-given:sortable-given,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
15uzsuToggle:uzsuToggle,ON,OFF\&lt;br /&gt;
16uzsuSelect:uzsuSelect,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
17uzsuSelectRadio:uzsuSelectRadio,MW,KW,UKW\&lt;br /&gt;
18uzsuDropDown:uzsuDropDown,red,green,blue,yellow \&lt;br /&gt;
19colorpicker_RGB:colorpicker,RGB \&lt;br /&gt;
19colorpicker_HSV:colorpicker,HSV \&lt;br /&gt;
19colorpicker_CT:colorpicker,CT,2000,10,6500 \&lt;br /&gt;
19colorpicker_HUE:colorpicker,HUE,0,1,359 \&lt;br /&gt;
19colorpicker_BRI:colorpicker,BRI,0,1,100 \&lt;br /&gt;
20time:time \&lt;br /&gt;
21iconRadio:iconRadio,#808080,10,fts_shutter_10@FFA500,20,fts_shutter_20@orange,30,fts_shutter_30@orange,40,fts_shutter_40@orange,50,fts_shutter_50@orange,60,fts_shutter_60@orange,70,fts_shutter_70@orange,80,fts_shutter_80@orange,90,fts_shutter_90@orange,100,fts_shutter_100@orange \&lt;br /&gt;
21iconRadio_use4icon:iconRadio,use4icon@808080,10,fts_shutter_10@FFA500,20,fts_shutter_20@orange,30,fts_shutter_30@orange,40,fts_shutter_40@orange,50,fts_shutter_50@orange,60,fts_shutter_60@orange,70,fts_shutter_70@orange,80,fts_shutter_80@orange,90,fts_shutter_90@orange,100,fts_shutter_100@orange \&lt;br /&gt;
22iconSwitch:iconSwitch,wide#open,@red,half#open,fts_shutter_50@%23cd2906,totally#closed,fts_shutter_100@magenta,half#closed, \&lt;br /&gt;
23iconLabel:iconLabel,wide#open,@red,half#open,fts_shutter_50@%23cd2906,totally#closed,fts_shutter_100@magenta,half#closed,\&lt;br /&gt;
24iconButtons:iconButtons,orange,Bad,sani_heating@green,Wohnzimmer_1,sani_heating@green,Wohnzimmer_2,sani_heating@green,Esszimmer,sani_heating@green,Diele,sani_heating@green,Gäste_WC,sani_heating@green,Büro,sani_heating@green,Wintergarten,sani_heating@green,Werkstatt,sani_heating@green,Schlafzimmer,sani_heating@green \&lt;br /&gt;
24iconButtons_use4icon:iconButtons,use4icon@red,Bad,sani_heating@0000FF,Wohnzimmer_1,sani_heating@blue,Wohnzimmer_2,sani_heating@blue,Esszimmer,sani_heating@blue,Diele,sani_heating@blue,Gäste_WC,sani_heating@blue,Büro,sani_heating@blue,Wintergarten,sani_heating@blue,Werkstatt,sani_heating@blue,Schlafzimmer,sani_heating@0000FF\&lt;br /&gt;
25bitfield:bitfield\&lt;br /&gt;
26widgetList:widgetList,4,select,Bad,Küche,Wohnzimmer,6,selectnumbers,7,1,30,0,lin&lt;br /&gt;
attr widgets userReadings 23iconLabel:22iconSwitch.* {ReadingsVal($name,&amp;quot;22iconSwitch&amp;quot;,&amp;quot;wideopen&amp;quot;)}&lt;br /&gt;
attr widgets webCmd 00select:01select:02selectnumbers:03selectnumbers:04textField:05textFieldNL:06textField-long:07textFieldNL-long:08slider:20time:09multiple:10multiple-strict:11knob:12sortable:13sortable-strict:14sortable-given:15uzsuToggle:16uzsuSelect:17uzsuSelectRadio:18uzsuDropDown:19colorpicker_RGB:19colorpicker_HSV:19colorpicker_CT:19colorpicker_HUE:19colorpicker_BRI:21iconRadio:21iconRadio_use4icon:24iconButtons:24iconButtons_use4icon:22iconSwitch:23iconLabel:25bitfield:26widgetList&lt;br /&gt;
attr widgets webCmdLabel select (default)\&lt;br /&gt;
:select (explicit)\&lt;br /&gt;
:selectnumbers (linear)\&lt;br /&gt;
:selectnumbers (logarithmic)\&lt;br /&gt;
:textField\&lt;br /&gt;
:textFieldNL (no label)\&lt;br /&gt;
:textField-long (multi-line)\&lt;br /&gt;
:textFieldNL-long (multi-line,no label)\&lt;br /&gt;
:slider\&lt;br /&gt;
:time\&lt;br /&gt;
:multiple\&lt;br /&gt;
:multiple-strict\&lt;br /&gt;
:knob\&lt;br /&gt;
:sortable\&lt;br /&gt;
:sortable-strict\&lt;br /&gt;
:sortable-given\&lt;br /&gt;
:uzsuToggle\&lt;br /&gt;
:uzsuSelect\&lt;br /&gt;
:uzsuSelectRadio\&lt;br /&gt;
:uzsuDropDown\&lt;br /&gt;
:colorpicker RGB\&lt;br /&gt;
:colorpicker HSV\&lt;br /&gt;
:colorpicker Farbtemperatur\&lt;br /&gt;
:colorpicker Farbton\&lt;br /&gt;
:colorpicker Helligkeit\&lt;br /&gt;
:iconRadio\&lt;br /&gt;
:iconRadio prefix use4icon\&lt;br /&gt;
:iconButtons\&lt;br /&gt;
:iconButtons prefix use4icon\&lt;br /&gt;
:iconSwitch\&lt;br /&gt;
:iconLabel\&lt;br /&gt;
:bitfield\&lt;br /&gt;
:widgetList (Raum Temperatur)&lt;br /&gt;
attr widgets widgetOverride readingList|setList|webCmd|webCmdLabel|widgetOverride:textField-long,86&lt;br /&gt;
&lt;br /&gt;
setstate widgets iconSwitch wide#open&lt;br /&gt;
setstate widgets 2024-03-19 20:18:38 00select 1&lt;br /&gt;
setstate widgets 2021-09-27 18:52:48 01select 0&lt;br /&gt;
setstate widgets 2021-09-27 18:52:33 02selectnumbers 0.0&lt;br /&gt;
setstate widgets 2021-09-27 12:13:35 03selectnumbers 64938&lt;br /&gt;
setstate widgets 2023-01-03 14:32:14 04textField Hello&lt;br /&gt;
setstate widgets 2017-08-24 17:39:22 05textField Hallo&lt;br /&gt;
setstate widgets 2023-01-03 14:47:53 05textFieldNL Hello&lt;br /&gt;
setstate widgets 2023-01-08 19:25:46 06textField-long Hello&lt;br /&gt;
setstate widgets 2022-11-28 18:23:30 07textFieldNL-long Hello&lt;br /&gt;
setstate widgets 2017-08-24 18:22:07 08slider 17.0&lt;br /&gt;
setstate widgets 2017-08-24 18:22:40 09multiple shirt,shoes&lt;br /&gt;
setstate widgets 2017-08-24 18:22:57 10multiple-strict red,green&lt;br /&gt;
setstate widgets 2021-09-27 03:19:35 11knob 207&lt;br /&gt;
setstate widgets 2017-08-24 18:21:09 12sortable blue,shirt,brown,skirt,white,shoes,black,jeans&lt;br /&gt;
setstate widgets 2017-08-24 18:48:17 13sortable-strict blue,white,shirt,shoes,brown,skirt,black,jeans&lt;br /&gt;
setstate widgets 2017-08-24 18:49:34 14sortable-given blue,shirt,white,shoes,brown,skirt,black,jeans&lt;br /&gt;
setstate widgets 2024-03-19 20:11:06 15uzsuToggle OFF&lt;br /&gt;
setstate widgets 2024-03-19 20:10:53 16uzsuSelect brown&lt;br /&gt;
setstate widgets 2024-03-19 20:11:16 17uzsuSelectRadio UKW&lt;br /&gt;
setstate widgets 2024-03-19 20:11:09 18uzsuDropDown blue&lt;br /&gt;
setstate widgets 2017-10-07 07:56:43 19colorpicker_BRI 54&lt;br /&gt;
setstate widgets 2017-10-07 07:56:31 19colorpicker_CT 5100&lt;br /&gt;
setstate widgets 2017-10-07 07:59:26 19colorpicker_HSV 208c20&lt;br /&gt;
setstate widgets 2017-10-07 07:56:29 19colorpicker_HUE 94&lt;br /&gt;
setstate widgets 2017-10-07 07:53:01 19colorpicker_RGB c95918&lt;br /&gt;
setstate widgets 2017-08-24 19:03:20 20time 12:00&lt;br /&gt;
setstate widgets 2021-09-27 18:54:56 21iconRadio 90&lt;br /&gt;
setstate widgets 2021-09-27 03:19:18 21iconRadio_use4icon 40&lt;br /&gt;
setstate widgets 2024-03-20 18:55:18 22iconSwitch half closed&lt;br /&gt;
setstate widgets 2024-03-20 18:55:18 23iconLabel half closed&lt;br /&gt;
setstate widgets 2021-09-27 18:57:53 24iconButtons Büro,Schlafzimmer&lt;br /&gt;
setstate widgets 2017-10-25 08:20:05 24iconButtons_use4icon Bad,Wohnzimmer_2,Diele,Büro,Werkstatt&lt;br /&gt;
setstate widgets 2024-03-19 20:07:03 25bitfield 25&lt;br /&gt;
setstate widgets 2024-04-11 19:25:52 26widgetList Küche,23&lt;br /&gt;
setstate widgets 2021-09-27 19:02:25 state iconSwitch wide#open&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beigesteuerte Widgets ==&lt;br /&gt;
[[Datei:datetimepicker.png|mini|rechts]]&lt;br /&gt;
* Forenthread zu {{Link2Forum|Topic=35736|LinkText= DateTimePicker Widget}}&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;[[Datei:Controlminidash.png|mini|rechts]]&lt;br /&gt;
* Wiki Seite zu [[FHEMWEB/ControlMiniDash| ControlMiniDash Widget]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;[[Datei:Minichart flex mobile.png|mini|rechts]]&lt;br /&gt;
* Wiki Seite zu [[FHEMWEB/MiniChart| MiniChart Widget]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;[[Datei:AnimatedSVG.gif|mini|rechts]]&lt;br /&gt;
* Wiki Seite zu [[FHEMWEB/iconAnimated| iconAnimated Widget]] (Bild drücken für Gif)&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;br /&gt;
[[Kategorie:Glossary]]&lt;br /&gt;
[[Kategorie:HOWTOS]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/Widgets&amp;diff=40641</id>
		<title>FHEMWEB/Widgets</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/Widgets&amp;diff=40641"/>
		<updated>2026-01-03T09:15:39Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Widgets sind Frontendelemente, die der Dateneingabe und -anzeige dienen. Die hier gezeigten Frontendelemente werden durch [[FHEMWEB]] ([[PGM2]]) bereitgestellt.&lt;br /&gt;
&lt;br /&gt;
== Syntax ==&lt;br /&gt;
Die Syntax der Widgets ist unter dem Attribut [https://fhem.de/commandref_DE.html#widgetOverride widgetOverride] in der Befehlreferenz beschrieben.&lt;br /&gt;
&lt;br /&gt;
== Verwendung ==&lt;br /&gt;
Viele Module (u.a. [[DOIF]], [[dummy]], [[MQTT_DEVICE]], [[MQTT2_DEVICE]], [[MYSENSORS_DEVICE]], [[readingsGroup]]) bieten die Möglichkeit eine Benutzerschnittstelle zu erstellen.&lt;br /&gt;
&lt;br /&gt;
Widgets werden über &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;:&amp;lt;modifier&amp;gt;&amp;lt;/code&amp;gt;-Paare an [[Readings]] oder [[Attribute]] gebunden, dabei ist:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;&amp;lt;/code&amp;gt; ein Readingsname, ein Attribut oder ein Mapping-Argument.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;modifier&amp;gt;&amp;lt;/code&amp;gt; ein Widget einschliesslich Parameter.&lt;br /&gt;
&lt;br /&gt;
Bei der Erstellung spielen verschiedene Attribute zusammen:&lt;br /&gt;
&lt;br /&gt;
* Mit readingList (bzw. bei Modulen, die dies nicht kennen: widgetOverride) ist es möglich den Set-Befehl auf [[Readings]] zu erweitern, siehe [https://fhem.de/commandref_DE.html#readingList readingList].&lt;br /&gt;
* Mit [[setList]] (bzw. widgetOverride) werden die Widgets einem Reading zugeordnet und parametrisiert, siehe [https://fhem.de/commandref_DE.html#setList setList].&lt;br /&gt;
* Mit [[webCmd]] werden die Widgets angezeigt, siehe [https://fhem.de/commandref_DE.html#setList webCmd].&lt;br /&gt;
* Mit webCmdLabel werden die Widgets beschriftet und tabellarisch angeordnet, siehe [https://fhem.de/commandref_DE.html#webCmdLabel webCmdLabel].&lt;br /&gt;
* Mit widgetOverride ist es möglich, die im Modul festgelegten Widgets zu überschreiben, siehe [https://fhem.de/commandref_DE.html#widgetOverride widgetOverride].&lt;br /&gt;
* Mit [[eventMap]] ist es möglich Widgets an Mapping-Argumente zu binden, siehe [https://fhem.de/commandref_DE.html#eventMap eventMap] und {{Link2Forum|Topic=76755|LinkText=eventMap und Widget-Anbindung}}.&lt;br /&gt;
Das Zusammenspiel dieser Attribute ist u.A. im Artikel [[DeviceOverview anpassen#webCmd .26 Co|DeviceOverview anpassen]] näher erläutert.&lt;br /&gt;
&lt;br /&gt;
== Zusammenstellung der Widgets ==&lt;br /&gt;
[[Datei:fhemweb_widgets_1.png|mini|rechts]]&lt;br /&gt;
Die Bildschirmkopie zeigt das Benutzerinterface des Dummys mit den Widgets&lt;br /&gt;
&lt;br /&gt;
=== weitere Konfiguration ===&lt;br /&gt;
* &#039;&#039;&#039;knob&#039;&#039;&#039;, siehe http://anthonyterrien.com/knob/&lt;br /&gt;
* &#039;&#039;&#039;colorpicker&#039;&#039;&#039;, siehe [[Color]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
== Das Beispiel für [[DOIF/Import_von_Code_Snippets|Raw definition]] zum Ausprobieren ==&lt;br /&gt;
In der vorstehenden Abbildung fehlt das Look and Feel der Widgets. Mit dem folgenden Code-Snippet zum Ausprobieren, wird dies ergänzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod widgets dummy&lt;br /&gt;
attr widgets readingList 00select 01select 02selectnumbers 03selectnumbers 04textField 05textFieldNL 06textField-long 07textFieldNL-long 08slider 09multiple 10multiple-strict 11knob 12sortable 13sortable-strict 14sortable-given 15uzsuToggle 16uzsuSelect 17uzsuSelectRadio 18uzsuDropDown 19colorpicker_RGB 19colorpicker_HSV 19colorpicker_CT 19colorpicker_HUE 19colorpicker_BRI 20time 21iconRadio 21iconRadio_use4icon 22iconSwitch 23iconLabel 24iconButtons 24iconButtons_use4icon 25bitfield 26widgetList&lt;br /&gt;
attr widgets room 0_Test&lt;br /&gt;
attr widgets setList 00select:1,2,3,4,5,0,a,b,c,def,hik \&lt;br /&gt;
01select:select,1,2,3,4,5,0,a,b,c,def,hik \&lt;br /&gt;
02selectnumbers:selectnumbers,-30,3,33,1,lin \&lt;br /&gt;
03selectnumbers:selectnumbers,1,0.0625,140000,0,log10 \&lt;br /&gt;
04textField:textField \&lt;br /&gt;
05textFieldNL:textFieldNL\&lt;br /&gt;
06textField-long:textField-long,87 \&lt;br /&gt;
07textFieldNL-long:textFieldNL-long,87 \&lt;br /&gt;
08slider:slider,7,0.5,30,1 \&lt;br /&gt;
09multiple:multiple,shirt,shoes,skirt,trowsers \&lt;br /&gt;
10multiple-strict:multiple-strict,red,green,blue,yellow \&lt;br /&gt;
11knob:knob,min:0,max:360,width:50,height:50,step:1,lineCap:round,angleOffset:180,cursor:3,thickness:.3 \&lt;br /&gt;
12sortable:sortable,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
13sortable-strict:sortable-strict,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
14sortable-given:sortable-given,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
15uzsuToggle:uzsuToggle,ON,OFF\&lt;br /&gt;
16uzsuSelect:uzsuSelect,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
17uzsuSelectRadio:uzsuSelectRadio,MW,KW,UKW\&lt;br /&gt;
18uzsuDropDown:uzsuDropDown,red,green,blue,yellow \&lt;br /&gt;
19colorpicker_RGB:colorpicker,RGB \&lt;br /&gt;
19colorpicker_HSV:colorpicker,HSV \&lt;br /&gt;
19colorpicker_CT:colorpicker,CT,2000,10,6500 \&lt;br /&gt;
19colorpicker_HUE:colorpicker,HUE,0,1,359 \&lt;br /&gt;
19colorpicker_BRI:colorpicker,BRI,0,1,100 \&lt;br /&gt;
20time:time \&lt;br /&gt;
21iconRadio:iconRadio,#808080,10,fts_shutter_10@FFA500,20,fts_shutter_20@orange,30,fts_shutter_30@orange,40,fts_shutter_40@orange,50,fts_shutter_50@orange,60,fts_shutter_60@orange,70,fts_shutter_70@orange,80,fts_shutter_80@orange,90,fts_shutter_90@orange,100,fts_shutter_100@orange \&lt;br /&gt;
21iconRadio_use4icon:iconRadio,use4icon@808080,10,fts_shutter_10@FFA500,20,fts_shutter_20@orange,30,fts_shutter_30@orange,40,fts_shutter_40@orange,50,fts_shutter_50@orange,60,fts_shutter_60@orange,70,fts_shutter_70@orange,80,fts_shutter_80@orange,90,fts_shutter_90@orange,100,fts_shutter_100@orange \&lt;br /&gt;
22iconSwitch:iconSwitch,wide#open,@red,half#open,fts_shutter_50@%23cd2906,totally#closed,fts_shutter_100@magenta,half#closed, \&lt;br /&gt;
23iconLabel:iconLabel,wide#open,@red,half#open,fts_shutter_50@%23cd2906,totally#closed,fts_shutter_100@magenta,half#closed,\&lt;br /&gt;
24iconButtons:iconButtons,orange,Bad,sani_heating@green,Wohnzimmer_1,sani_heating@green,Wohnzimmer_2,sani_heating@green,Esszimmer,sani_heating@green,Diele,sani_heating@green,Gäste_WC,sani_heating@green,Büro,sani_heating@green,Wintergarten,sani_heating@green,Werkstatt,sani_heating@green,Schlafzimmer,sani_heating@green \&lt;br /&gt;
24iconButtons_use4icon:iconButtons,use4icon@red,Bad,sani_heating@0000FF,Wohnzimmer_1,sani_heating@blue,Wohnzimmer_2,sani_heating@blue,Esszimmer,sani_heating@blue,Diele,sani_heating@blue,Gäste_WC,sani_heating@blue,Büro,sani_heating@blue,Wintergarten,sani_heating@blue,Werkstatt,sani_heating@blue,Schlafzimmer,sani_heating@0000FF\&lt;br /&gt;
25bitfield:bitfield\&lt;br /&gt;
26widgetList:widgetList,4,select,Bad,Küche,Wohnzimmer,6,selectnumbers,7,1,30,0,lin&lt;br /&gt;
attr widgets userReadings 23iconLabel:22iconSwitch.* {ReadingsVal($name,&amp;quot;22iconSwitch&amp;quot;,&amp;quot;wideopen&amp;quot;)}&lt;br /&gt;
attr widgets webCmd 00select:01select:02selectnumbers:03selectnumbers:04textField:05textFieldNL:06textField-long:07textFieldNL-long:08slider:20time:09multiple:10multiple-strict:11knob:12sortable:13sortable-strict:14sortable-given:15uzsuToggle:16uzsuSelect:17uzsuSelectRadio:18uzsuDropDown:19colorpicker_RGB:19colorpicker_HSV:19colorpicker_CT:19colorpicker_HUE:19colorpicker_BRI:21iconRadio:21iconRadio_use4icon:24iconButtons:24iconButtons_use4icon:22iconSwitch:23iconLabel:25bitfield:26widgetList&lt;br /&gt;
attr widgets webCmdLabel select (default)\&lt;br /&gt;
:select (explicit)\&lt;br /&gt;
:selectnumbers (linear)\&lt;br /&gt;
:selectnumbers (logarithmic)\&lt;br /&gt;
:textField\&lt;br /&gt;
:textFieldNL (no label)\&lt;br /&gt;
:textField-long (multi-line)\&lt;br /&gt;
:textFieldNL-long (multi-line,no label)\&lt;br /&gt;
:slider\&lt;br /&gt;
:time\&lt;br /&gt;
:multiple\&lt;br /&gt;
:multiple-strict\&lt;br /&gt;
:knob\&lt;br /&gt;
:sortable\&lt;br /&gt;
:sortable-strict\&lt;br /&gt;
:sortable-given\&lt;br /&gt;
:uzsuToggle\&lt;br /&gt;
:uzsuSelect\&lt;br /&gt;
:uzsuSelectRadio\&lt;br /&gt;
:uzsuDropDown\&lt;br /&gt;
:colorpicker RGB\&lt;br /&gt;
:colorpicker HSV\&lt;br /&gt;
:colorpicker Farbtemperatur\&lt;br /&gt;
:colorpicker Farbton\&lt;br /&gt;
:colorpicker Helligkeit\&lt;br /&gt;
:iconRadio\&lt;br /&gt;
:iconRadio prefix use4icon\&lt;br /&gt;
:iconButtons\&lt;br /&gt;
:iconButtons prefix use4icon\&lt;br /&gt;
:iconSwitch\&lt;br /&gt;
:iconLabel\&lt;br /&gt;
:bitfield\&lt;br /&gt;
:widgetList (Raum Temperatur)&lt;br /&gt;
attr widgets widgetOverride readingList|setList|webCmd|webCmdLabel|widgetOverride:textField-long,86&lt;br /&gt;
&lt;br /&gt;
setstate widgets iconSwitch wide#open&lt;br /&gt;
setstate widgets 2024-03-19 20:18:38 00select 1&lt;br /&gt;
setstate widgets 2021-09-27 18:52:48 01select 0&lt;br /&gt;
setstate widgets 2021-09-27 18:52:33 02selectnumbers 0.0&lt;br /&gt;
setstate widgets 2021-09-27 12:13:35 03selectnumbers 64938&lt;br /&gt;
setstate widgets 2023-01-03 14:32:14 04textField Hello&lt;br /&gt;
setstate widgets 2017-08-24 17:39:22 05textField Hallo&lt;br /&gt;
setstate widgets 2023-01-03 14:47:53 05textFieldNL Hello&lt;br /&gt;
setstate widgets 2023-01-08 19:25:46 06textField-long Hello&lt;br /&gt;
setstate widgets 2022-11-28 18:23:30 07textFieldNL-long Hello&lt;br /&gt;
setstate widgets 2017-08-24 18:22:07 08slider 17.0&lt;br /&gt;
setstate widgets 2017-08-24 18:22:40 09multiple shirt,shoes&lt;br /&gt;
setstate widgets 2017-08-24 18:22:57 10multiple-strict red,green&lt;br /&gt;
setstate widgets 2021-09-27 03:19:35 11knob 207&lt;br /&gt;
setstate widgets 2017-08-24 18:21:09 12sortable blue,shirt,brown,skirt,white,shoes,black,jeans&lt;br /&gt;
setstate widgets 2017-08-24 18:48:17 13sortable-strict blue,white,shirt,shoes,brown,skirt,black,jeans&lt;br /&gt;
setstate widgets 2017-08-24 18:49:34 14sortable-given blue,shirt,white,shoes,brown,skirt,black,jeans&lt;br /&gt;
setstate widgets 2024-03-19 20:11:06 15uzsuToggle OFF&lt;br /&gt;
setstate widgets 2024-03-19 20:10:53 16uzsuSelect brown&lt;br /&gt;
setstate widgets 2024-03-19 20:11:16 17uzsuSelectRadio UKW&lt;br /&gt;
setstate widgets 2024-03-19 20:11:09 18uzsuDropDown blue&lt;br /&gt;
setstate widgets 2017-10-07 07:56:43 19colorpicker_BRI 54&lt;br /&gt;
setstate widgets 2017-10-07 07:56:31 19colorpicker_CT 5100&lt;br /&gt;
setstate widgets 2017-10-07 07:59:26 19colorpicker_HSV 208c20&lt;br /&gt;
setstate widgets 2017-10-07 07:56:29 19colorpicker_HUE 94&lt;br /&gt;
setstate widgets 2017-10-07 07:53:01 19colorpicker_RGB c95918&lt;br /&gt;
setstate widgets 2017-08-24 19:03:20 20time 12:00&lt;br /&gt;
setstate widgets 2021-09-27 18:54:56 21iconRadio 90&lt;br /&gt;
setstate widgets 2021-09-27 03:19:18 21iconRadio_use4icon 40&lt;br /&gt;
setstate widgets 2024-03-20 18:55:18 22iconSwitch half closed&lt;br /&gt;
setstate widgets 2024-03-20 18:55:18 23iconLabel half closed&lt;br /&gt;
setstate widgets 2021-09-27 18:57:53 24iconButtons Büro,Schlafzimmer&lt;br /&gt;
setstate widgets 2017-10-25 08:20:05 24iconButtons_use4icon Bad,Wohnzimmer_2,Diele,Büro,Werkstatt&lt;br /&gt;
setstate widgets 2024-03-19 20:07:03 25bitfield 25&lt;br /&gt;
setstate widgets 2024-04-11 19:25:52 26widgetList Küche,23&lt;br /&gt;
setstate widgets 2021-09-27 19:02:25 state iconSwitch wide#open&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beigesteuerte Widgets ==&lt;br /&gt;
[[Datei:datetimepicker.png|mini|rechts]]&lt;br /&gt;
* Forenthread zu {{Link2Forum|Topic=35736|LinkText= DateTimePicker Widget}}&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;[[Datei:Controlminidash.png|mini|rechts]]&lt;br /&gt;
* Wiki Seite zu [[FHEMWEB/ControlMiniDash| ControlMiniDash Widget]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;[[Datei:Minichart flex mobile.png|mini|rechts]]&lt;br /&gt;
* Wiki Seite zu [[FHEMWEB/MiniChart| MiniChart Widget]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;[[Datei:AnimatedSVG.gif|mini|rechts]]&lt;br /&gt;
* Wiki Seite zu [[FHEMWEB/iconAnimated| iconAnimated Widget]] (Bild drücken für Gif)&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;br /&gt;
[[Kategorie:Glossary]]&lt;br /&gt;
[[Kategorie:HOWTOS]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/ControlMiniDash&amp;diff=40640</id>
		<title>FHEMWEB/ControlMiniDash</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/ControlMiniDash&amp;diff=40640"/>
		<updated>2026-01-03T09:09:02Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Controlminidash.png|Mini|rechts]]&lt;br /&gt;
== ControlMiniDash – kompaktes Dashboard-Widget ==&lt;br /&gt;
&lt;br /&gt;
Dieses Widget stellt ein kompaktes Dashboard bereit, das direkt in einem Device im [[FHEMWEB]] ([[PGM2]]) Frontend &lt;br /&gt;
definiert werden kann. Es wurde ursprünglich für ein Klimaanlagen-Setup entwickelt und dient als flexibles, &lt;br /&gt;
visuelles Bedienelement für Geräte wie z. B. den HmIP-WTH-2.&lt;br /&gt;
&lt;br /&gt;
Das Widget besteht aus zentralen Informationstexten, einem farbigen Außenring, einem Sliderbutton für die&lt;br /&gt;
Solltemperatur sowie bis zu sechs frei definierbaren Buttons.&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=142831|LinkText=ControlMiniDash Widget}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Zentrale Textelemente ====&lt;br /&gt;
Vier Textelemente werden in der Mitte angezeigt:&lt;br /&gt;
&lt;br /&gt;
* Gemessene Temperatur&lt;br /&gt;
* Luftfeuchtigkeit&lt;br /&gt;
* Solltemperatur&lt;br /&gt;
* Freitextfeld / Statusfeld&lt;br /&gt;
&lt;br /&gt;
==== Interaktion ====&lt;br /&gt;
* Der runde Button auf dem Außenring ermöglicht das direkte Erhöhen oder Verringern der Solltemperatur.&lt;br /&gt;
* Der Außenring ändert seine Farbe abhängig von der aktuellen Temperatur (7 °C bis 30 °C).&lt;br /&gt;
* Links (1–3) und rechts (4–6) können jeweils drei frei belegbare Buttons definiert werden.&lt;br /&gt;
* Für die Buttons können sowohl Data-URL-SVG als auch FHEM-SVG-Icons verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Einrichtung im Device ===&lt;br /&gt;
&lt;br /&gt;
Das Widget wird über &amp;lt;code&amp;gt;webCmd&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;widgetOverride&amp;lt;/code&amp;gt; aktiviert.&lt;br /&gt;
&lt;br /&gt;
==== Beispiele ====&lt;br /&gt;
&lt;br /&gt;
===== Fhemicon + Fhemcmd only =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr tcmdWidget webCmd climacontrol&lt;br /&gt;
attr tcmdWidget widgetOverride climacontrol:controlminidash,measured-temp,desired-temp,measured-temp@°C,humidity@%,desired-temp@°C,state,#,rc_MINUS@boost.off,#,#,rc_PLUS@boost.on,#&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Fhemicon + Fhemcmd bzw. Perlcmd aus userattr =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr tcmdWidget webCmd climacontrol&lt;br /&gt;
attr tcmdWidget widgetOverride climacontrol:controlminidash,measured-temp,desired-temp,measured-temp@°C,humidity@%,desired-temp@°C,state,#,rc_MINUS@#,#,#,rc_PLUS@#,#&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== SVG Data URL aus userattr + Fhemcmd bzw. Perlcmd aus userattr =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr tcmdWidget webCmd climacontrol&lt;br /&gt;
attr tcmdWidget widgetOverride climacontrol:controlminidash,measured-temp,desired-temp,measured-temp@°C,humidity@%,desired-temp@°C,state,#,#,#,#,#,#&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Dummy-Device für die Beispiele ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod tcmdWidget dummy&lt;br /&gt;
attr tcmdWidget userattr btn2Cmd btn2Color btn2Icon btn5Cmd btn5Color btn5Icon&lt;br /&gt;
attr tcmdWidget btn2Cmd { fhem(&amp;quot;set tcmdWidget desired-temp &amp;quot;.(ReadingsVal(&amp;quot;tcmdWidget&amp;quot;,&amp;quot;desired-temp&amp;quot;,0) - 0.5)) }&lt;br /&gt;
attr tcmdWidget btn2Color blue&lt;br /&gt;
attr tcmdWidget btn2Icon data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M12%2C20C7.59%2C20%204%2C16.41%204%2C12C4%2C7.59%207.59%2C4%2012%2C4C16.41%2C4%2020%2C7.59%2020%2C12C20%2C16.41%2016.41%2C20%2012%2C20M12%2C2A10%2C10%200%200%2C0%202%2C12A10%2C10%200%200%2C0%2012%2C22A10%2C10%200%200%2C0%2022%2C12A10%2C10%200%200%2C0%2012%2C2M7%2C13H17V11H7%22%20%2F%3E%3C%2Fsvg%3E&lt;br /&gt;
attr tcmdWidget btn5Cmd { fhem(&amp;quot;set tcmdWidget desired-temp &amp;quot;.(ReadingsVal(&amp;quot;tcmdWidget&amp;quot;,&amp;quot;desired-temp&amp;quot;,0) + 0.5)) }&lt;br /&gt;
attr tcmdWidget btn5Color red&lt;br /&gt;
attr tcmdWidget btn5Icon data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M12%2C20C7.59%2C20%204%2C16.41%204%2C12C4%2C7.59%207.59%2C4%2012%2C4C16.41%2C4%2020%2C7.59%2020%2C12C20%2C16.41%2016.41%2C20%2012%2C20M12%2C2A10%2C10%200%200%2C0%202%2C12A10%2C10%200%200%2C0%2012%2C22A10%2C10%200%200%2C0%2022%2C12A10%2C10%200%200%2C0%2012%2C2M13%2C7H11V11H7V13H11V17H13V13H17V11H13V7Z%22%20%2F%3E%3C%2Fsvg%3E&lt;br /&gt;
attr tcmdWidget readingList measured-temp desired-temp humidity state&lt;br /&gt;
attr tcmdWidget room Test&lt;br /&gt;
attr tcmdWidget setList measured-temp desired-temp humidity state&lt;br /&gt;
attr tcmdWidget webCmd climacontrol&lt;br /&gt;
attr tcmdWidget widgetOverride climacontrol:controlminidash,measured-temp,desired-temp,measured-temp@°C,humidity@%,desired-temp@°C,state,#,rc_MINUS@boost.off,#,#,rc_PLUS@boost.on,#&lt;br /&gt;
&lt;br /&gt;
setstate tcmdWidget boost on&lt;br /&gt;
setstate tcmdWidget 2025-12-28 16:40:00 desired-temp 18.5&lt;br /&gt;
setstate tcmdWidget 2025-12-28 14:11:59 humidity 55&lt;br /&gt;
setstate tcmdWidget 2025-12-28 15:48:53 measured-temp 22&lt;br /&gt;
setstate tcmdWidget 2025-12-28 16:45:38 state boost on&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Parameterübersicht ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Abschnitt&lt;br /&gt;
! Bedeutung&lt;br /&gt;
! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;lt;beliebigerName&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
| webCmd-Zuweisung&lt;br /&gt;
| Gibt an, welches &amp;lt;code&amp;gt;webCmd&amp;lt;/code&amp;gt; das Widget repräsentiert.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;controlminidash&amp;lt;/code&amp;gt;&lt;br /&gt;
| Widget-Typ&lt;br /&gt;
| Aktiviert das Dashboard-Widget.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ring1Val&amp;lt;/code&amp;gt;&lt;br /&gt;
|Istwert&lt;br /&gt;
|Zeigt nach dem Setzen des Sollwerts den aktuellen Istwert an.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ring2Val&amp;lt;/code&amp;gt;&lt;br /&gt;
|Sollwert&lt;br /&gt;
|Zu setzender Wert.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;info1@unit&amp;lt;/code&amp;gt;&lt;br /&gt;
| Status-/Textfeld&lt;br /&gt;
| Freiwählbares Reading.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;info2@unit&amp;lt;/code&amp;gt;&lt;br /&gt;
| Status-/Textfeld&lt;br /&gt;
| Freiwählbares Reading.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;info3@unit&amp;lt;/code&amp;gt;&lt;br /&gt;
| Status-/Textfeld&lt;br /&gt;
| Freiwählbares Reading.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;info4@unit&amp;lt;/code&amp;gt;&lt;br /&gt;
| Status-/Textfeld&lt;br /&gt;
| Freiwählbares Reading. &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;btnXIcon@btnXCmd &amp;lt;/code&amp;gt;&lt;br /&gt;
| Button-Attribute&lt;br /&gt;
| Individuelle Definition von Icon und Befehl für die sechs möglichen Buttons.&lt;br /&gt;
 Beispiel:sani_heating_automatic@controlMode.automatic&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;&lt;br /&gt;
| Button ausblenden&lt;br /&gt;
| Beispiel:  &lt;br /&gt;
  &amp;lt;code&amp;gt;climacontrol:controlminidash,measured-temp,humidity,desired-temp,state,#,#,#,#,#,#&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Individualisierung per userattr ===&lt;br /&gt;
  &lt;br /&gt;
Damit können Fhemcmd&#039;s, Farbe und Icon für jeden der sechs Buttons separat konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel-Definition:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
 defmod widgetControlUserAttr dummy&lt;br /&gt;
 attr widgetControlUserAttr userattr btn1Cmd btn1Color btn1Icon btn2Cmd btn2Color btn2Icon btn3Cmd btn3Color btn3Icon btn4Cmd btn4Color btn4Icon btn5Cmd btn5Color btn5Icon btn6Cmd btn6Color btn6Icon&lt;br /&gt;
 attr widgetControlUserAttr btn1Cmd   set widgetControlUserAttr state cooling&lt;br /&gt;
 attr widgetControlUserAttr btn1Color blue&lt;br /&gt;
 attr widgetControlUserAttr btn1Icon  data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns...&lt;br /&gt;
 attr widgetControlUserAttr btn2Cmd   { fhem(&amp;quot;set widgetControlUserAttr desired-temp &amp;quot;.(ReadingsVal(&amp;quot;widgetControlUserAttr&amp;quot;,&amp;quot;desired-temp&amp;quot;,0)-0.5)) }&lt;br /&gt;
 attr widgetControlUserAttr btn2Color blue&lt;br /&gt;
 attr widgetControlUserAttr btn2Icon  data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns...&lt;br /&gt;
 attr widgetControlUserAttr btn3Cmd   set widgetControlUserAttr state off&lt;br /&gt;
 attr widgetControlUserAttr btn3Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr btn4Cmd   set widgetControlUserAttr state heating&lt;br /&gt;
 attr widgetControlUserAttr btn4Color red&lt;br /&gt;
 attr widgetControlUserAttr btn4Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr btn5Cmd   { fhem(&amp;quot;set widgetControlUserAttr desired-temp &amp;quot;.(ReadingsVal(&amp;quot;widgetControlUserAttr&amp;quot;,&amp;quot;desired-temp&amp;quot;,0)+0.5)) }&lt;br /&gt;
 attr widgetControlUserAttr btn5Color red&lt;br /&gt;
 attr widgetControlUserAttr btn5Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr btn6Cmd   set widgetControlUserAttr state on&lt;br /&gt;
 attr widgetControlUserAttr btn6Color green&lt;br /&gt;
 attr widgetControlUserAttr btn6Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr readingList measured-temp humidity desired-temp state&lt;br /&gt;
 attr widgetControlUserAttr room Test&lt;br /&gt;
 attr widgetControlUserAttr setList measured-temp humidity desired-temp state&lt;br /&gt;
 attr widgetControlUserAttr subType Thermostat&lt;br /&gt;
 attr widgetControlUserAttr webCmd climacontrol&lt;br /&gt;
 attr widgetControlUserAttr widgetOverride climacontrol:controlminidash,measured-temp,desired-temp,measured-temp,humidity,desired-temp,state,#,#,#,#,#,#&lt;br /&gt;
 setstate widgetControlUserAttr heating&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:51:37 desired-temp 20.0&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:42:42 humidity 56&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:42:48 measured-temp 19&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:51:21 state heating&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Mit DOIF diverse Widgets in einem Table zusammenfassen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
 defmod di_widget DOIF ##&lt;br /&gt;
 attr di_widget room Test&lt;br /&gt;
 attr di_widget uiTable {package ui_Table;;\&lt;br /&gt;
 $_param=&#039;controlminidash,measured-temp,desired-temp,measured-temp,humidity,desired-temp,state,sani_heating_manual@controlMode.manual,rc_MINUS@#,sani_heating_boost@boost,sani_heating_automatic@controlMode.automatic,rc_PLUS@#,sani_heating_mode@night_day&#039;;;\&lt;br /&gt;
 $_set=&amp;quot;set desired-temp&amp;quot;;;\&lt;br /&gt;
 }\&lt;br /&gt;
 widget([TH_DG_HM:desired-temp],$_param,$_set)|\&lt;br /&gt;
 widget([TH_Kz_w_HM:desired-temp],$_param,$_set)\&lt;br /&gt;
 widget([TH_Kz_o_HM:desired-temp],$_param,$_set)|\&lt;br /&gt;
 widget([TH_Kueche_HM:desired-temp],$_param,$_set)\&lt;br /&gt;
 widget([TH_Keller_HM:desired-temp],$_param,$_set)|\&lt;br /&gt;
 widget([TH_Bad_HM:desired-temp],$_param,$_set)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Einrichtungen für KNX RaumThermostat_/_HeizungsAktor ===&lt;br /&gt;
&lt;br /&gt;
siehe: [[KNX_Device_Definition_-_Beispiele#RaumThermostat_/_HeizungsAktor_mit_widget_controlminidash| RaumThermostat_/_HeizungsAktor_mit_widget_controlminidash]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/ControlMiniDash&amp;diff=40639</id>
		<title>FHEMWEB/ControlMiniDash</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/ControlMiniDash&amp;diff=40639"/>
		<updated>2026-01-03T09:07:10Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Controlminidash.png|Mini|rechts]]&lt;br /&gt;
== ControlMiniDash – kompaktes Dashboard-Widget ==&lt;br /&gt;
&lt;br /&gt;
Dieses Widget stellt ein kompaktes Dashboard bereit, das direkt in einem Device im [[FHEMWEB]] ([[PGM2]]) Frontend &lt;br /&gt;
definiert werden kann. Es wurde ursprünglich für ein Klimaanlagen-Setup entwickelt und dient als flexibles, &lt;br /&gt;
visuelles Bedienelement für Geräte wie z. B. den HmIP-WTH-2.&lt;br /&gt;
&lt;br /&gt;
Das Widget besteht aus zentralen Informationstexten, einem farbigen Außenring, einem Sliderbutton für die&lt;br /&gt;
Solltemperatur sowie bis zu sechs frei definierbaren Buttons.&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=142831|LinkText=ControlMiniDash Widget}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Zentrale Textelemente ====&lt;br /&gt;
Vier Textelemente werden in der Mitte angezeigt:&lt;br /&gt;
&lt;br /&gt;
* Gemessene Temperatur&lt;br /&gt;
* Luftfeuchtigkeit&lt;br /&gt;
* Solltemperatur&lt;br /&gt;
* Freitextfeld / Statusfeld&lt;br /&gt;
&lt;br /&gt;
==== Interaktion ====&lt;br /&gt;
* Der runde Button auf dem Außenring ermöglicht das direkte Erhöhen oder Verringern der Solltemperatur.&lt;br /&gt;
* Der Außenring ändert seine Farbe abhängig von der aktuellen Temperatur (7 °C bis 30 °C).&lt;br /&gt;
* Links (1–3) und rechts (4–6) können jeweils drei frei belegbare Buttons definiert werden.&lt;br /&gt;
* Für die Buttons können sowohl Data-URL-SVG als auch FHEM-SVG-Icons verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Einrichtung im Device ===&lt;br /&gt;
&lt;br /&gt;
Das Widget wird über &amp;lt;code&amp;gt;webCmd&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;widgetOverride&amp;lt;/code&amp;gt; aktiviert.&lt;br /&gt;
&lt;br /&gt;
==== Beispiele ====&lt;br /&gt;
&lt;br /&gt;
===== Fhemicon + Fhemcmd only =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr tcmdWidget webCmd climacontrol&lt;br /&gt;
attr tcmdWidget widgetOverride climacontrol:controlminidash,measured-temp,desired-temp,measured-temp@°C,humidity@%,desired-temp@°C,state,#,rc_MINUS@boost.off,#,#,rc_PLUS@boost.on,#&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Fhemicon + Fhemcmd bzw. Perlcmd aus userattr =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr tcmdWidget webCmd climacontrol&lt;br /&gt;
attr tcmdWidget widgetOverride climacontrol:controlminidash,measured-temp,desired-temp,measured-temp@°C,humidity@%,desired-temp@°C,state,#,rc_MINUS@#,#,#,rc_PLUS@#,#&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== SVG Data URL aus userattr + Fhemcmd bzw. Perlcmd aus userattr =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr tcmdWidget webCmd climacontrol&lt;br /&gt;
attr tcmdWidget widgetOverride climacontrol:controlminidash,measured-temp,desired-temp,measured-temp@°C,humidity@%,desired-temp@°C,state,#,#,#,#,#,#&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Dummy-Device für die Beispiele ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod tcmdWidget dummy&lt;br /&gt;
attr tcmdWidget userattr btn2Cmd btn2Color btn2Icon btn5Cmd btn5Color btn5Icon&lt;br /&gt;
attr tcmdWidget btn2Cmd { fhem(&amp;quot;set tcmdWidget desired-temp &amp;quot;.(ReadingsVal(&amp;quot;tcmdWidget&amp;quot;,&amp;quot;desired-temp&amp;quot;,0) - 0.5)) }&lt;br /&gt;
attr tcmdWidget btn2Color blue&lt;br /&gt;
attr tcmdWidget btn2Icon data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M12%2C20C7.59%2C20%204%2C16.41%204%2C12C4%2C7.59%207.59%2C4%2012%2C4C16.41%2C4%2020%2C7.59%2020%2C12C20%2C16.41%2016.41%2C20%2012%2C20M12%2C2A10%2C10%200%200%2C0%202%2C12A10%2C10%200%200%2C0%2012%2C22A10%2C10%200%200%2C0%2022%2C12A10%2C10%200%200%2C0%2012%2C2M7%2C13H17V11H7%22%20%2F%3E%3C%2Fsvg%3E&lt;br /&gt;
attr tcmdWidget btn5Cmd { fhem(&amp;quot;set tcmdWidget desired-temp &amp;quot;.(ReadingsVal(&amp;quot;tcmdWidget&amp;quot;,&amp;quot;desired-temp&amp;quot;,0) + 0.5)) }&lt;br /&gt;
attr tcmdWidget btn5Color red&lt;br /&gt;
attr tcmdWidget btn5Icon data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M12%2C20C7.59%2C20%204%2C16.41%204%2C12C4%2C7.59%207.59%2C4%2012%2C4C16.41%2C4%2020%2C7.59%2020%2C12C20%2C16.41%2016.41%2C20%2012%2C20M12%2C2A10%2C10%200%200%2C0%202%2C12A10%2C10%200%200%2C0%2012%2C22A10%2C10%200%200%2C0%2022%2C12A10%2C10%200%200%2C0%2012%2C2M13%2C7H11V11H7V13H11V17H13V13H17V11H13V7Z%22%20%2F%3E%3C%2Fsvg%3E&lt;br /&gt;
attr tcmdWidget readingList measured-temp desired-temp humidity state&lt;br /&gt;
attr tcmdWidget room Test&lt;br /&gt;
attr tcmdWidget setList measured-temp desired-temp humidity state&lt;br /&gt;
attr tcmdWidget webCmd climacontrol&lt;br /&gt;
attr tcmdWidget widgetOverride climacontrol:controlminidash,measured-temp,desired-temp,measured-temp@°C,humidity@%,desired-temp@°C,state,#,rc_MINUS@boost.off,#,#,rc_PLUS@boost.on,#&lt;br /&gt;
&lt;br /&gt;
setstate tcmdWidget boost on&lt;br /&gt;
setstate tcmdWidget 2025-12-28 16:40:00 desired-temp 18.5&lt;br /&gt;
setstate tcmdWidget 2025-12-28 14:11:59 humidity 55&lt;br /&gt;
setstate tcmdWidget 2025-12-28 15:48:53 measured-temp 22&lt;br /&gt;
setstate tcmdWidget 2025-12-28 16:45:38 state boost on&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Parameterübersicht ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Abschnitt&lt;br /&gt;
! Bedeutung&lt;br /&gt;
! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;lt;beliebigerName&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
| webCmd-Zuweisung&lt;br /&gt;
| Gibt an, welches &amp;lt;code&amp;gt;webCmd&amp;lt;/code&amp;gt; das Widget repräsentiert.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;controlminidash&amp;lt;/code&amp;gt;&lt;br /&gt;
| Widget-Typ&lt;br /&gt;
| Aktiviert das Dashboard-Widget.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ring1Val&amp;lt;/code&amp;gt;&lt;br /&gt;
|Istwert&lt;br /&gt;
|Zeigt nach dem Setzen des Sollwerts den aktuellen Istwert an.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ring2Val&amp;lt;/code&amp;gt;&lt;br /&gt;
|Sollwert&lt;br /&gt;
|Zu setzender Wert.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;info1@unit&amp;lt;/code&amp;gt;&lt;br /&gt;
| Status-/Textfeld&lt;br /&gt;
| Freiwählbares Reading.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;info2@unit&amp;lt;/code&amp;gt;&lt;br /&gt;
| Status-/Textfeld&lt;br /&gt;
| Freiwählbares Reading.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;info3@unit&amp;lt;/code&amp;gt;&lt;br /&gt;
| Status-/Textfeld&lt;br /&gt;
| Freiwählbares Reading.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;info4@unit&amp;lt;/code&amp;gt;&lt;br /&gt;
| Status-/Textfeld&lt;br /&gt;
| Freiwählbares Reading. &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;btnXIcon@btnXCmd &amp;lt;/code&amp;gt;&lt;br /&gt;
| Button-Attribute&lt;br /&gt;
| Individuelle Definition von Icon und Befehl für die sechs möglichen Buttons.&lt;br /&gt;
 Beispiel:sani_heating_automatic@controlMode.automatic&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;&lt;br /&gt;
| Button ausblenden&lt;br /&gt;
| Beispiel:  &lt;br /&gt;
  &amp;lt;code&amp;gt;climacontrol:controlminidash,measured-temp,humidity,desired-temp,state,#,#,#,#,#,#&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Individualisierung per userattr ===&lt;br /&gt;
  &lt;br /&gt;
Damit können Fhemcmd&#039;s, Farbe und Icon für jeden der sechs Buttons separat konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel-Definition:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
 defmod widgetControlUserAttr dummy&lt;br /&gt;
 attr widgetControlUserAttr userattr btn1Cmd btn1Color btn1Icon btn2Cmd btn2Color btn2Icon btn3Cmd btn3Color btn3Icon btn4Cmd btn4Color btn4Icon btn5Cmd btn5Color btn5Icon btn6Cmd btn6Color btn6Icon&lt;br /&gt;
 attr widgetControlUserAttr btn1Cmd   set widgetControlUserAttr state cooling&lt;br /&gt;
 attr widgetControlUserAttr btn1Color blue&lt;br /&gt;
 attr widgetControlUserAttr btn1Icon  data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns...&lt;br /&gt;
 attr widgetControlUserAttr btn2Cmd   { fhem(&amp;quot;set widgetControlUserAttr desired-temp &amp;quot;.(ReadingsVal(&amp;quot;widgetControlUserAttr&amp;quot;,&amp;quot;desired-temp&amp;quot;,0)-0.5)) }&lt;br /&gt;
 attr widgetControlUserAttr btn2Color blue&lt;br /&gt;
 attr widgetControlUserAttr btn2Icon  data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns...&lt;br /&gt;
 attr widgetControlUserAttr btn3Cmd   set widgetControlUserAttr state off&lt;br /&gt;
 attr widgetControlUserAttr btn3Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr btn4Cmd   set widgetControlUserAttr state heating&lt;br /&gt;
 attr widgetControlUserAttr btn4Color red&lt;br /&gt;
 attr widgetControlUserAttr btn4Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr btn5Cmd   { fhem(&amp;quot;set widgetControlUserAttr desired-temp &amp;quot;.(ReadingsVal(&amp;quot;widgetControlUserAttr&amp;quot;,&amp;quot;desired-temp&amp;quot;,0)+0.5)) }&lt;br /&gt;
 attr widgetControlUserAttr btn5Color red&lt;br /&gt;
 attr widgetControlUserAttr btn5Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr btn6Cmd   set widgetControlUserAttr state on&lt;br /&gt;
 attr widgetControlUserAttr btn6Color green&lt;br /&gt;
 attr widgetControlUserAttr btn6Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr readingList measured-temp humidity desired-temp state&lt;br /&gt;
 attr widgetControlUserAttr room Test&lt;br /&gt;
 attr widgetControlUserAttr setList measured-temp humidity desired-temp state&lt;br /&gt;
 attr widgetControlUserAttr subType Thermostat&lt;br /&gt;
 attr widgetControlUserAttr webCmd climacontrol&lt;br /&gt;
 attr widgetControlUserAttr widgetOverride climacontrol:controlminidash,measured-temp,desired-temp,measured-temp,humidity,desired-temp,state,#,#,#,#,#,#&lt;br /&gt;
 setstate widgetControlUserAttr heating&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:51:37 desired-temp 20.0&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:42:42 humidity 56&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:42:48 measured-temp 19&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:51:21 state heating&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Mit DOIF diverse Widgets in einem Table zusammenfassen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
 defmod di_widget DOIF ##&lt;br /&gt;
 attr di_widget room Test&lt;br /&gt;
 attr di_widget uiTable {package ui_Table;;\&lt;br /&gt;
 $_param=&#039;controlminidash,measured-temp,desired-temp,measured-temp,humidity,desired-temp,state,sani_heating_manual@controlMode.manual,rc_MINUS@#,sani_heating_boost@boost,sani_heating_automatic@controlMode.automatic,rc_PLUS@#,sani_heating_mode@night_day&#039;;;\&lt;br /&gt;
 $_set=&amp;quot;set desired-temp&amp;quot;;;\&lt;br /&gt;
 }\&lt;br /&gt;
 widget([TH_DG_HM:desired-temp],$_param,$_set)|\&lt;br /&gt;
 widget([TH_Kz_w_HM:desired-temp],$_param,$_set)\&lt;br /&gt;
 widget([TH_Kz_o_HM:desired-temp],$_param,$_set)|\&lt;br /&gt;
 widget([TH_Kueche_HM:desired-temp],$_param,$_set)\&lt;br /&gt;
 widget([TH_Keller_HM:desired-temp],$_param,$_set)|\&lt;br /&gt;
 widget([TH_Bad_HM:desired-temp],$_param,$_set)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Einrichtungen für KNX RaumThermostat_/_HeizungsAktor ===&lt;br /&gt;
&lt;br /&gt;
siehe: [[KNX_Device_Definition_-_Beispiele#RaumThermostat_/_HeizungsAktor_mit_widget_controlminidash| RaumThermostat_/_HeizungsAktor_mit_widget_controlminidash]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/ControlMiniDash&amp;diff=40638</id>
		<title>FHEMWEB/ControlMiniDash</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/ControlMiniDash&amp;diff=40638"/>
		<updated>2026-01-03T08:58:20Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Controlminidash.png|Mini|rechts]]&lt;br /&gt;
== ControlMiniDash – kompaktes Dashboard-Widget ==&lt;br /&gt;
&lt;br /&gt;
Dieses Widget stellt ein kompaktes Dashboard bereit, das direkt in einem Device im [[FHEMWEB]] ([[PGM2]]) Frontend &lt;br /&gt;
definiert werden kann. Es wurde ursprünglich für ein Klimaanlagen-Setup entwickelt und dient als flexibles, &lt;br /&gt;
visuelles Bedienelement für Geräte wie z. B. den HmIP-WTH-2.&lt;br /&gt;
&lt;br /&gt;
Das Widget besteht aus zentralen Informationstexten, einem farbigen Außenring, einem Sliderbutton für die&lt;br /&gt;
Solltemperatur sowie bis zu sechs frei definierbaren Buttons.&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=142831|LinkText=ControlMiniDash Widget}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Zentrale Textelemente ====&lt;br /&gt;
Vier Textelemente werden in der Mitte angezeigt:&lt;br /&gt;
&lt;br /&gt;
* Gemessene Temperatur&lt;br /&gt;
* Luftfeuchtigkeit&lt;br /&gt;
* Solltemperatur&lt;br /&gt;
* Freitextfeld / Statusfeld&lt;br /&gt;
&lt;br /&gt;
==== Interaktion ====&lt;br /&gt;
* Der runde Button auf dem Außenring ermöglicht das direkte Erhöhen oder Verringern der Solltemperatur.&lt;br /&gt;
* Der Außenring ändert seine Farbe abhängig von der aktuellen Temperatur (7 °C bis 30 °C).&lt;br /&gt;
* Links (1–3) und rechts (4–6) können jeweils drei frei belegbare Buttons definiert werden.&lt;br /&gt;
* Für die Buttons können sowohl Data-URL-SVG als auch FHEM-SVG-Icons verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Einrichtung im Device ===&lt;br /&gt;
&lt;br /&gt;
Das Widget wird über &amp;lt;code&amp;gt;webCmd&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;widgetOverride&amp;lt;/code&amp;gt; aktiviert.&lt;br /&gt;
&lt;br /&gt;
==== Beispiele ====&lt;br /&gt;
&lt;br /&gt;
===== Fhemicon + Fhemcmd only =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr tcmdWidget webCmd climacontrol&lt;br /&gt;
attr tcmdWidget widgetOverride climacontrol:controlminidash,measured-temp,desired-temp,measured-temp@°C,humidity@%,desired-temp@°C,state,#,rc_MINUS@boost.off,#,#,rc_PLUS@boost.on,#&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Fhemicon + Fhemcmd bzw. Perlcmd aus userattr =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr tcmdWidget webCmd climacontrol&lt;br /&gt;
attr tcmdWidget widgetOverride climacontrol:controlminidash,measured-temp,desired-temp,measured-temp@°C,humidity@%,desired-temp@°C,state,#,rc_MINUS@#,#,#,rc_PLUS@#,#&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== SVG Data URL aus userattr + Fhemcmd bzw. Perlcmd aus userattr =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr tcmdWidget webCmd climacontrol&lt;br /&gt;
attr tcmdWidget widgetOverride climacontrol:controlminidash,measured-temp,desired-temp,measured-temp@°C,humidity@%,desired-temp@°C,state,#,#,#,#,#,#&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Dummy-Device für die Beispiele ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod tcmdWidget dummy&lt;br /&gt;
attr tcmdWidget userattr btn2Cmd btn2Color btn2Icon btn5Cmd btn5Color btn5Icon&lt;br /&gt;
attr tcmdWidget btn2Cmd { fhem(&amp;quot;set tcmdWidget desired-temp &amp;quot;.(ReadingsVal(&amp;quot;tcmdWidget&amp;quot;,&amp;quot;desired-temp&amp;quot;,0) - 0.5)) }&lt;br /&gt;
attr tcmdWidget btn2Color blue&lt;br /&gt;
attr tcmdWidget btn2Icon data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M12%2C20C7.59%2C20%204%2C16.41%204%2C12C4%2C7.59%207.59%2C4%2012%2C4C16.41%2C4%2020%2C7.59%2020%2C12C20%2C16.41%2016.41%2C20%2012%2C20M12%2C2A10%2C10%200%200%2C0%202%2C12A10%2C10%200%200%2C0%2012%2C22A10%2C10%200%200%2C0%2022%2C12A10%2C10%200%200%2C0%2012%2C2M7%2C13H17V11H7%22%20%2F%3E%3C%2Fsvg%3E&lt;br /&gt;
attr tcmdWidget btn5Cmd { fhem(&amp;quot;set tcmdWidget desired-temp &amp;quot;.(ReadingsVal(&amp;quot;tcmdWidget&amp;quot;,&amp;quot;desired-temp&amp;quot;,0) + 0.5)) }&lt;br /&gt;
attr tcmdWidget btn5Color red&lt;br /&gt;
attr tcmdWidget btn5Icon data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M12%2C20C7.59%2C20%204%2C16.41%204%2C12C4%2C7.59%207.59%2C4%2012%2C4C16.41%2C4%2020%2C7.59%2020%2C12C20%2C16.41%2016.41%2C20%2012%2C20M12%2C2A10%2C10%200%200%2C0%202%2C12A10%2C10%200%200%2C0%2012%2C22A10%2C10%200%200%2C0%2022%2C12A10%2C10%200%200%2C0%2012%2C2M13%2C7H11V11H7V13H11V17H13V13H17V11H13V7Z%22%20%2F%3E%3C%2Fsvg%3E&lt;br /&gt;
attr tcmdWidget readingList measured-temp desired-temp humidity state&lt;br /&gt;
attr tcmdWidget room Test&lt;br /&gt;
attr tcmdWidget setList measured-temp desired-temp humidity state&lt;br /&gt;
attr tcmdWidget webCmd climacontrol&lt;br /&gt;
attr tcmdWidget widgetOverride climacontrol:controlminidash,measured-temp,desired-temp,measured-temp@°C,humidity@%,desired-temp@°C,state,#,rc_MINUS@boost.off,#,#,rc_PLUS@boost.on,#&lt;br /&gt;
&lt;br /&gt;
setstate tcmdWidget boost on&lt;br /&gt;
setstate tcmdWidget 2025-12-28 16:40:00 desired-temp 18.5&lt;br /&gt;
setstate tcmdWidget 2025-12-28 14:11:59 humidity 55&lt;br /&gt;
setstate tcmdWidget 2025-12-28 15:48:53 measured-temp 22&lt;br /&gt;
setstate tcmdWidget 2025-12-28 16:45:38 state boost on&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Parameterübersicht ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Abschnitt&lt;br /&gt;
! Bedeutung&lt;br /&gt;
! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;lt;beliebigerName&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
| webCmd-Zuweisung&lt;br /&gt;
| Gibt an, welches &amp;lt;code&amp;gt;webCmd&amp;lt;/code&amp;gt; das Widget repräsentiert.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;controlminidash&amp;lt;/code&amp;gt;&lt;br /&gt;
| Widget-Typ&lt;br /&gt;
| Aktiviert das Dashboard-Widget.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ring1Val&amp;lt;/code&amp;gt;&lt;br /&gt;
|Istwert&lt;br /&gt;
|Zeigt nach dem Setzen des Sollwerts den aktuellen Istwert an.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ring2Val&amp;lt;/code&amp;gt;&lt;br /&gt;
|Sollwert&lt;br /&gt;
|Zu setzender Wert.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;info1@unit&amp;lt;/code&amp;gt;&lt;br /&gt;
| Status-/Textfeld&lt;br /&gt;
| Freiwählbares Reading.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;info2@unit&amp;lt;/code&amp;gt;&lt;br /&gt;
| Status-/Textfeld&lt;br /&gt;
| Freiwählbares Reading.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;info3@unit&amp;lt;/code&amp;gt;&lt;br /&gt;
| Status-/Textfeld&lt;br /&gt;
| Freiwählbares Reading.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;info4@unit&amp;lt;/code&amp;gt;&lt;br /&gt;
| Status-/Textfeld&lt;br /&gt;
| Freiwählbares Reading. &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;btnXIcon@btnXCmd &amp;lt;/code&amp;gt;&lt;br /&gt;
| Button-Attribute&lt;br /&gt;
| Individuelle Definition von Icon und Befehl für die sechs möglichen Buttons.&lt;br /&gt;
 Beispiel:sani_heating_automatic@controlMode.automatic&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;&lt;br /&gt;
| Button ausblenden&lt;br /&gt;
| Beispiel:  &lt;br /&gt;
  &amp;lt;code&amp;gt;climacontrol:controlminidash,measured-temp,humidity,desired-temp,state,#,#,#,#,#,#&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Individualisierung per userattr ===&lt;br /&gt;
  &lt;br /&gt;
Damit können Fhemcmd&#039;s, Farbe und Icon für jeden der sechs Buttons separat konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel-Definition:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
 defmod widgetControlUserAttr dummy&lt;br /&gt;
 attr widgetControlUserAttr userattr btn1Cmd btn1Color btn1Icon btn2Cmd btn2Color btn2Icon btn3Cmd btn3Color btn3Icon btn4Cmd btn4Color btn4Icon btn5Cmd btn5Color btn5Icon btn6Cmd btn6Color btn6Icon&lt;br /&gt;
 attr widgetControlUserAttr btn1Cmd   set widgetControlUserAttr state cooling&lt;br /&gt;
 attr widgetControlUserAttr btn1Color blue&lt;br /&gt;
 attr widgetControlUserAttr btn1Icon  data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns...&lt;br /&gt;
 attr widgetControlUserAttr btn2Cmd   { fhem(&amp;quot;set widgetControlUserAttr desired-temp &amp;quot;.(ReadingsVal(&amp;quot;widgetControlUserAttr&amp;quot;,&amp;quot;desired-temp&amp;quot;,0)-0.5)) }&lt;br /&gt;
 attr widgetControlUserAttr btn2Color blue&lt;br /&gt;
 attr widgetControlUserAttr btn2Icon  data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns...&lt;br /&gt;
 attr widgetControlUserAttr btn3Cmd   set widgetControlUserAttr state off&lt;br /&gt;
 attr widgetControlUserAttr btn3Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr btn4Cmd   set widgetControlUserAttr state heating&lt;br /&gt;
 attr widgetControlUserAttr btn4Color red&lt;br /&gt;
 attr widgetControlUserAttr btn4Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr btn5Cmd   { fhem(&amp;quot;set widgetControlUserAttr desired-temp &amp;quot;.(ReadingsVal(&amp;quot;widgetControlUserAttr&amp;quot;,&amp;quot;desired-temp&amp;quot;,0)+0.5)) }&lt;br /&gt;
 attr widgetControlUserAttr btn5Color red&lt;br /&gt;
 attr widgetControlUserAttr btn5Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr btn6Cmd   set widgetControlUserAttr state on&lt;br /&gt;
 attr widgetControlUserAttr btn6Color green&lt;br /&gt;
 attr widgetControlUserAttr btn6Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr readingList measured-temp humidity desired-temp state&lt;br /&gt;
 attr widgetControlUserAttr room Test&lt;br /&gt;
 attr widgetControlUserAttr setList measured-temp humidity desired-temp state&lt;br /&gt;
 attr widgetControlUserAttr subType Thermostat&lt;br /&gt;
 attr widgetControlUserAttr webCmd climacontrol&lt;br /&gt;
 attr widgetControlUserAttr widgetOverride climacontrol:controlminidash,measured-temp,desired-temp,measured-temp,humidity,desired-temp,state,#,#,#,#,#,#&lt;br /&gt;
 setstate widgetControlUserAttr heating&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:51:37 desired-temp 20.0&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:42:42 humidity 56&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:42:48 measured-temp 19&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:51:21 state heating&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Mit DOIF diverse Widgets in einem Table zusammenfassen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
 defmod di_widget DOIF ##&lt;br /&gt;
 attr di_widget room Test&lt;br /&gt;
 attr di_widget uiTable {package ui_Table;;\&lt;br /&gt;
 $_param=&#039;controlminidash,measured-temp,desired-temp,measured-temp,humidity,desired-temp,state,sani_heating_manual@controlMode.manual,rc_MINUS@#,sani_heating_boost@boost,sani_heating_automatic@controlMode.automatic,rc_PLUS@#,sani_heating_mode@night_day&#039;;;\&lt;br /&gt;
 $_set=&amp;quot;set desired-temp&amp;quot;;;\&lt;br /&gt;
 }\&lt;br /&gt;
 widget([TH_DG_HM:desired-temp],$_param,$_set)|\&lt;br /&gt;
 widget([TH_Kz_w_HM:desired-temp],$_param,$_set)\&lt;br /&gt;
 widget([TH_Kz_o_HM:desired-temp],$_param,$_set)|\&lt;br /&gt;
 widget([TH_Kueche_HM:desired-temp],$_param,$_set)\&lt;br /&gt;
 widget([TH_Keller_HM:desired-temp],$_param,$_set)|\&lt;br /&gt;
 widget([TH_Bad_HM:desired-temp],$_param,$_set)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/ControlMiniDash&amp;diff=40637</id>
		<title>FHEMWEB/ControlMiniDash</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/ControlMiniDash&amp;diff=40637"/>
		<updated>2026-01-03T00:05:18Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: /* Parameterübersicht */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Controlminidash.png|Mini|rechts]]&lt;br /&gt;
== ControlMiniDash – kompaktes Dashboard-Widget ==&lt;br /&gt;
&lt;br /&gt;
Dieses Widget stellt ein kompaktes Dashboard bereit, das direkt in einem Device im [[FHEMWEB]] ([[PGM2]]) Frontend &lt;br /&gt;
definiert werden kann. Es wurde ursprünglich für ein Klimaanlagen-Setup entwickelt und dient als flexibles, &lt;br /&gt;
visuelles Bedienelement für Geräte wie z. B. den HmIP-WTH-2.&lt;br /&gt;
&lt;br /&gt;
Das Widget besteht aus zentralen Informationstexten, einem farbigen Außenring, einem Sliderbutton für die&lt;br /&gt;
Solltemperatur sowie bis zu sechs frei definierbaren Buttons.&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=142831|LinkText=ControlMiniDash Widget}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Zentrale Textelemente ====&lt;br /&gt;
Vier Textelemente werden in der Mitte angezeigt:&lt;br /&gt;
&lt;br /&gt;
* Gemessene Temperatur&lt;br /&gt;
* Luftfeuchtigkeit&lt;br /&gt;
* Solltemperatur&lt;br /&gt;
* Freitextfeld / Statusfeld&lt;br /&gt;
&lt;br /&gt;
==== Interaktion ====&lt;br /&gt;
* Der runde Button auf dem Außenring ermöglicht das direkte Erhöhen oder Verringern der Solltemperatur.&lt;br /&gt;
* Der Außenring ändert seine Farbe abhängig von der aktuellen Temperatur (7 °C bis 30 °C).&lt;br /&gt;
* Links (1–3) und rechts (4–6) können jeweils drei frei belegbare Buttons definiert werden.&lt;br /&gt;
* Für die Buttons können sowohl Data-URL-SVG als auch FHEM-SVG-Icons verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Einrichtung im Device ===&lt;br /&gt;
&lt;br /&gt;
Das Widget wird über &amp;lt;code&amp;gt;webCmd&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;widgetOverride&amp;lt;/code&amp;gt; aktiviert.&lt;br /&gt;
&lt;br /&gt;
Beispiel ohne Buttons:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
 attr &amp;lt;device&amp;gt; webCmd climacontrol&lt;br /&gt;
 attr &amp;lt;device&amp;gt; widgetOverride climacontrol:controlminidash,measured-temp,desired-temp,measured-temp,humidity,desired-temp,state,#,#,#,#,#,#&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel mit FhemSVG-Buttons:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
 attr &amp;lt;device&amp;gt; webCmd climacontrol&lt;br /&gt;
 attr &amp;lt;device&amp;gt; widgetOverride climacontrol:controlminidash,measured-temp,desired-temp,measured-temp,humidity,desired-temp,state,sani_heating_manual@controlMode.manual,rc_MINUS@temp-5,sani_heating_boost@boost,sani_heating_automatic@controlMode.automatic,rc_PLUS@temp+5,sani_heating_mode@night_day&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn im Device keine Luftfeuchte/Humidity vorhanden ist, kann diese mit # an der dritten Position ausgeblendet werden.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
 attr HmIP_KLHR_STHD webCmd controlMD auto:manu:boost:on:off&lt;br /&gt;
 attr HmIP_KLHR_STHD widgetOverride controlMD:controlminidash,measured-temp,desired-temp,measured-temp,#,desired-temp,HEATING_COOLING,#,#,#,#,#,#&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Parameterübersicht ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Abschnitt&lt;br /&gt;
! Bedeutung&lt;br /&gt;
! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;lt;beliebigerName&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
| webCmd-Zuweisung&lt;br /&gt;
| Gibt an, welches &amp;lt;code&amp;gt;webCmd&amp;lt;/code&amp;gt; das Widget repräsentiert.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;controlminidash&amp;lt;/code&amp;gt;&lt;br /&gt;
| Widget-Typ&lt;br /&gt;
| Aktiviert das Dashboard-Widget.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ring1Val&amp;lt;/code&amp;gt;&lt;br /&gt;
|Istwert&lt;br /&gt;
|Zeigt nach dem Setzen des Sollwerts den aktuellen Istwert an.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ring2Val&amp;lt;/code&amp;gt;&lt;br /&gt;
|Sollwert&lt;br /&gt;
|Zu setzender Wert.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;info1@unit&amp;lt;/code&amp;gt;&lt;br /&gt;
| Status-/Textfeld&lt;br /&gt;
| Freiwählbares Reading.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;info2@unit&amp;lt;/code&amp;gt;&lt;br /&gt;
| Status-/Textfeld&lt;br /&gt;
| Freiwählbares Reading.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;info3@unit&amp;lt;/code&amp;gt;&lt;br /&gt;
| Status-/Textfeld&lt;br /&gt;
| Freiwählbares Reading.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;info4@unit&amp;lt;/code&amp;gt;&lt;br /&gt;
| Status-/Textfeld&lt;br /&gt;
| Freiwählbares Reading. &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;btnXIcon@btnXCmd &amp;lt;/code&amp;gt;&lt;br /&gt;
| Button-Attribute&lt;br /&gt;
| Individuelle Definition von Icon und Befehl für die sechs möglichen Buttons.&lt;br /&gt;
 Beispiel:sani_heating_automatic@controlMode.automatic&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;&lt;br /&gt;
| Button ausblenden&lt;br /&gt;
| Beispiel:  &lt;br /&gt;
  &amp;lt;code&amp;gt;climacontrol:controlminidash,measured-temp,humidity,desired-temp,state,#,#,#,#,#,#&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Individualisierung per userattr ===&lt;br /&gt;
  &lt;br /&gt;
Damit können Fhemcmd&#039;s, Farbe und Icon für jeden der sechs Buttons separat konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel-Definition:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
 defmod widgetControlUserAttr dummy&lt;br /&gt;
 attr widgetControlUserAttr userattr btn1Cmd btn1Color btn1Icon btn2Cmd btn2Color btn2Icon btn3Cmd btn3Color btn3Icon btn4Cmd btn4Color btn4Icon btn5Cmd btn5Color btn5Icon btn6Cmd btn6Color btn6Icon&lt;br /&gt;
 attr widgetControlUserAttr btn1Cmd   set widgetControlUserAttr state cooling&lt;br /&gt;
 attr widgetControlUserAttr btn1Color blue&lt;br /&gt;
 attr widgetControlUserAttr btn1Icon  data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns...&lt;br /&gt;
 attr widgetControlUserAttr btn2Cmd   { fhem(&amp;quot;set widgetControlUserAttr desired-temp &amp;quot;.(ReadingsVal(&amp;quot;widgetControlUserAttr&amp;quot;,&amp;quot;desired-temp&amp;quot;,0)-0.5)) }&lt;br /&gt;
 attr widgetControlUserAttr btn2Color blue&lt;br /&gt;
 attr widgetControlUserAttr btn2Icon  data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns...&lt;br /&gt;
 attr widgetControlUserAttr btn3Cmd   set widgetControlUserAttr state off&lt;br /&gt;
 attr widgetControlUserAttr btn3Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr btn4Cmd   set widgetControlUserAttr state heating&lt;br /&gt;
 attr widgetControlUserAttr btn4Color red&lt;br /&gt;
 attr widgetControlUserAttr btn4Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr btn5Cmd   { fhem(&amp;quot;set widgetControlUserAttr desired-temp &amp;quot;.(ReadingsVal(&amp;quot;widgetControlUserAttr&amp;quot;,&amp;quot;desired-temp&amp;quot;,0)+0.5)) }&lt;br /&gt;
 attr widgetControlUserAttr btn5Color red&lt;br /&gt;
 attr widgetControlUserAttr btn5Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr btn6Cmd   set widgetControlUserAttr state on&lt;br /&gt;
 attr widgetControlUserAttr btn6Color green&lt;br /&gt;
 attr widgetControlUserAttr btn6Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr readingList measured-temp humidity desired-temp state&lt;br /&gt;
 attr widgetControlUserAttr room Test&lt;br /&gt;
 attr widgetControlUserAttr setList measured-temp humidity desired-temp state&lt;br /&gt;
 attr widgetControlUserAttr subType Thermostat&lt;br /&gt;
 attr widgetControlUserAttr webCmd climacontrol&lt;br /&gt;
 attr widgetControlUserAttr widgetOverride climacontrol:controlminidash,measured-temp,desired-temp,measured-temp,humidity,desired-temp,state,#,#,#,#,#,#&lt;br /&gt;
 setstate widgetControlUserAttr heating&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:51:37 desired-temp 20.0&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:42:42 humidity 56&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:42:48 measured-temp 19&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:51:21 state heating&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Mit DOIF diverse Widgets in einem Table zusammenfassen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
 defmod di_widget DOIF ##&lt;br /&gt;
 attr di_widget room Test&lt;br /&gt;
 attr di_widget uiTable {package ui_Table;;\&lt;br /&gt;
 $_param=&#039;controlminidash,measured-temp,desired-temp,measured-temp,humidity,desired-temp,state,sani_heating_manual@controlMode.manual,rc_MINUS@temp-5,sani_heating_boost@boost,sani_heating_automatic@controlMode.automatic,rc_PLUS@temp+5,sani_heating_mode@night_day&#039;;;\&lt;br /&gt;
 $_set=&amp;quot;set desired-temp&amp;quot;;;\&lt;br /&gt;
 }\&lt;br /&gt;
 widget([TH_DG_HM:desired-temp],$_param,$_set)|\&lt;br /&gt;
 widget([TH_Kz_w_HM:desired-temp],$_param,$_set)\&lt;br /&gt;
 widget([TH_Kz_o_HM:desired-temp],$_param,$_set)|\&lt;br /&gt;
 widget([TH_Kueche_HM:desired-temp],$_param,$_set)\&lt;br /&gt;
 widget([TH_Keller_HM:desired-temp],$_param,$_set)|\&lt;br /&gt;
 widget([TH_Bad_HM:desired-temp],$_param,$_set)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/MiniChart&amp;diff=40633</id>
		<title>FHEMWEB/MiniChart</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/MiniChart&amp;diff=40633"/>
		<updated>2026-01-02T16:01:56Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Minichart flex mobile.png|Mini|rechts]]&lt;br /&gt;
== MiniChart – kompaktes Chart-Widget (Line &amp;amp; Bar) ==&lt;br /&gt;
Das MiniChart-Widget visualisiert Messwertverläufe als kompaktes Liniendiagramm oder Balkendiagramm für die Darstellung im [[FHEMWEB]] ([[PGM2]]) Frontend.&lt;br /&gt;
Zusätzlich zeigt es drei aktuelle Werte, die aus Label, Reading und Einheit bestehen. Zum Einrichten sind drei Schritte notwendig.&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=141857|LinkText=MiniChart Widget}}&lt;br /&gt;
&lt;br /&gt;
== 1️⃣ 99_myUtils.pm – Hilfsfunktion zum Speichern von historischen Werten ==&lt;br /&gt;
Zuerst wird eine Subroutine in [[99_myUtils_anlegen|99_myUtils.pm]] angelegt, die neue Werte an ein Fixed-Array anhängt und alte Werte entfernt, falls die maximale Größe überschritten wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
 sub AppendToFixedArray {&lt;br /&gt;
     my ($dev, $reading, $newVal, $maxSize) = @_;&lt;br /&gt;
     $maxSize //= 250;  # Standard: 250 Werte&lt;br /&gt;
     my $current = ReadingsVal($dev, $reading, &#039;&#039;);&lt;br /&gt;
     my @values = split(&#039;,&#039;, $current);&lt;br /&gt;
     if (@values &amp;gt;= $maxSize) {&lt;br /&gt;
         @values = @values[-($maxSize - 1) .. -1];  # Älteste Werte entfernen&lt;br /&gt;
     }&lt;br /&gt;
     push @values, $newVal;&lt;br /&gt;
     return join(&#039;,&#039;, @values);&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2️⃣ at-Timer zum regelmäßigen Aktualisieren ==&lt;br /&gt;
Mit einem `[[at]]`-Timer werden die Werte der Messgeräte regelmäßig in die `chartArray`-Readings geschrieben.  &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod chartUpdate at +*00:05 {\&lt;br /&gt;
  my @devices = (\&lt;br /&gt;
    # [&amp;quot;Devicename&amp;quot;, &amp;quot;Readingname&amp;quot;]\&lt;br /&gt;
    [&amp;quot;Smartmeter_2E1F50&amp;quot;, &amp;quot;APOX_Power&amp;quot;],\&lt;br /&gt;
    [&amp;quot;OpenDTU&amp;quot;,           &amp;quot;power&amp;quot;],\&lt;br /&gt;
    [&amp;quot;Kuehlschrank&amp;quot;,      &amp;quot;power&amp;quot;],\&lt;br /&gt;
    [&amp;quot;Luftentfeuchter&amp;quot;,   &amp;quot;ENERGY_Power&amp;quot;],\&lt;br /&gt;
    [&amp;quot;Geschirrspueler&amp;quot;,   &amp;quot;power&amp;quot;],\&lt;br /&gt;
    [&amp;quot;Trockner&amp;quot;,          &amp;quot;power&amp;quot;],\&lt;br /&gt;
    [&amp;quot;Waschmaschine&amp;quot;,     &amp;quot;power&amp;quot;],\&lt;br /&gt;
    [&amp;quot;Pumpe_FBH_Pwr&amp;quot;,     &amp;quot;power&amp;quot;],\&lt;br /&gt;
    [&amp;quot;Heizstab_BW_Pwr&amp;quot;,   &amp;quot;power&amp;quot;],\&lt;br /&gt;
  );;\&lt;br /&gt;
\&lt;br /&gt;
  for my $d (@devices) {\&lt;br /&gt;
    my ($dev, $reading) = @$d;;\&lt;br /&gt;
\&lt;br /&gt;
    my $val = ReadingsVal($dev, $reading, 0);;\&lt;br /&gt;
    my $new = AppendToFixedArray($dev, &amp;quot;chartArray&amp;quot;, $val);;\&lt;br /&gt;
    fhem(&amp;quot;setreading $dev chartArray $new&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 3️⃣ Minichart ==&lt;br /&gt;
=== 1. Widget-Datei aus contrib laden ===&lt;br /&gt;
Bevor das MiniChart-Widget genutzt werden kann, muss die JS-Datei ins FHEM-Webverzeichnis kopiert werden.&lt;br /&gt;
Dies kann mit der Perlfunktion &amp;lt;code&amp;gt;{ Svn_GetFile(&#039;from SVN Path&#039;, &#039;to local Path&#039;) }&amp;lt;/code&amp;gt; direkt in der FHEM Kommandozeile erfolgen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{ Svn_GetFile(&#039;contrib/fhemweb_minichart.js&#039;, &#039;www/pgm2/fhemweb_minichart.js&#039;) } &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 2. Widget im Device mit widgetOverride einrichten ===&lt;br /&gt;
=== Werteanzeige ===&lt;br /&gt;
Drei Textzeilen können angezeigt werden, jeweils im Format:&lt;br /&gt;
:&amp;lt;code&amp;gt;Label@Reading@Einheit&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
:&amp;lt;code&amp;gt;Jetzt@power@W,Heute@yieldday@kWh,Gesamt@yieldtotal@kWh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Ausblenden einzelner Zeilen ====&lt;br /&gt;
Jede der drei Zeilen kann vollständig oder teilweise ausgeblendet werden, indem das jeweilige Element leer gelassen wird. Das Widget setzt intern automatisch ein geschütztes Leerzeichen (&amp;amp;nbsp;).&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* Nur Label ausblenden:&lt;br /&gt;
:&amp;lt;code&amp;gt;@reading@W&amp;lt;/code&amp;gt;&lt;br /&gt;
* Nur Reading ausblenden:&lt;br /&gt;
:&amp;lt;code&amp;gt;Label@@W&amp;lt;/code&amp;gt;&lt;br /&gt;
* Nur Einheit ausblenden:&lt;br /&gt;
:&amp;lt;code&amp;gt;Label@reading@&amp;lt;/code&amp;gt;&lt;br /&gt;
* Ganze Zeile ausblenden:&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Diagrammtypen ===&lt;br /&gt;
MiniChart unterstützt zwei Diagrammtypen:&lt;br /&gt;
* line – geglättetes Liniendiagramm mit Farbverlauf  &lt;br /&gt;
* bar  – Balkendiagramm mit positiver/negativer Farbgebung&lt;br /&gt;
&lt;br /&gt;
Das Chart-Reading benötigt eine kommagetrennte Zahlenliste, z. B.:&lt;br /&gt;
:&amp;lt;code&amp;gt;10,20,15,18,25&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Chartoptionen:&lt;br /&gt;
:&amp;lt;code&amp;gt;chartType@colorPositive@colorNegative&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
:&amp;lt;code&amp;gt;line@#3b82f6@#ff0000&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;bar@#00cc88@#cc0044&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fallback (nur ChartReading angegeben):&lt;br /&gt;
:&amp;lt;code&amp;gt;dann wird automatisch line@#3b82f6@#3b82f6 gesetzt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Einrichtung im Device ===&lt;br /&gt;
Syntax:&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;lt;webCmd&amp;gt;:minichart,Label1@Reading1@Einheit,Label2@Reading2@Einheit,Label3@Reading3@Einheit,ChartReading,chartType@colorPos@colorNeg&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel Liniendiagramm:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot; line&amp;gt;&lt;br /&gt;
attr &amp;lt;device&amp;gt; webCmd minichart&lt;br /&gt;
attr &amp;lt;device&amp;gt; widgetOverride minichart:minichart,Jetzt@power@W,Heute@yieldday@kWh,Gesamt@yieldtotal@kWh,chartArray,line@#3b82f6@#ff0000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel Balkendiagramm:&lt;br /&gt;
:&amp;lt;code&amp;gt;...,chartArray,bar@#00cc88@#cc0044&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fallback (Standard line + Standardfarbe):&lt;br /&gt;
:&amp;lt;code&amp;gt;...,chartArray&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Parameterübersicht ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Abschnitt&lt;br /&gt;
! Bedeutung&lt;br /&gt;
! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;lt;beliebigerName&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
| webCmd-Zuweisung&lt;br /&gt;
| Definiert die Instanz des Widgets.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;minichart&amp;lt;/code&amp;gt;&lt;br /&gt;
| Widget-Typ&lt;br /&gt;
| Aktiviert das MiniChart-Widget.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Label1@Reading1@Einheit1&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeigezeilen&lt;br /&gt;
| Drei frei definierbare Werte-/Statusfelder; mit &amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt; komplett ausblendbar.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Label2@Reading2@Einheit2&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeigezeilen&lt;br /&gt;
| Drei frei definierbare Werte-/Statusfelder; mit &amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt; komplett ausblendbar.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Label3@Reading3@Einheit3&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeigezeilen&lt;br /&gt;
| Drei frei definierbare Werte-/Statusfelder; mit &amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt; komplett ausblendbar.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ChartReading&amp;lt;/code&amp;gt;&lt;br /&gt;
| Datenquelle &lt;br /&gt;
| Liefert Array-Werte für das Diagramm .&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;chartType@colorPos@colorNeg&amp;lt;/code&amp;gt;&lt;br /&gt;
| Chart-Optionen&lt;br /&gt;
| line oder bar; Farben optional.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/Widgets&amp;diff=40632</id>
		<title>FHEMWEB/Widgets</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/Widgets&amp;diff=40632"/>
		<updated>2026-01-02T15:59:49Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Widgets sind Frontendelemente, die der Dateneingabe und -anzeige dienen. Die hier gezeigten Frontendelemente werden durch [[FHEMWEB]] ([[PGM2]]) bereitgestellt.&lt;br /&gt;
&lt;br /&gt;
== Syntax ==&lt;br /&gt;
Die Syntax der Widgets ist unter dem Attribut [https://fhem.de/commandref_DE.html#widgetOverride widgetOverride] in der Befehlreferenz beschrieben.&lt;br /&gt;
&lt;br /&gt;
== Verwendung ==&lt;br /&gt;
Viele Module (u.a. [[DOIF]], [[dummy]], [[MQTT_DEVICE]], [[MQTT2_DEVICE]], [[MYSENSORS_DEVICE]], [[readingsGroup]]) bieten die Möglichkeit eine Benutzerschnittstelle zu erstellen.&lt;br /&gt;
&lt;br /&gt;
Widgets werden über &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;:&amp;lt;modifier&amp;gt;&amp;lt;/code&amp;gt;-Paare an [[Readings]] oder [[Attribute]] gebunden, dabei ist:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;&amp;lt;/code&amp;gt; ein Readingsname, ein Attribut oder ein Mapping-Argument.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;modifier&amp;gt;&amp;lt;/code&amp;gt; ein Widget einschliesslich Parameter.&lt;br /&gt;
&lt;br /&gt;
Bei der Erstellung spielen verschiedene Attribute zusammen:&lt;br /&gt;
&lt;br /&gt;
* Mit readingList (bzw. bei Modulen, die dies nicht kennen: widgetOverride) ist es möglich den Set-Befehl auf [[Readings]] zu erweitern, siehe [https://fhem.de/commandref_DE.html#readingList readingList].&lt;br /&gt;
* Mit [[setList]] (bzw. widgetOverride) werden die Widgets einem Reading zugeordnet und parametrisiert, siehe [https://fhem.de/commandref_DE.html#setList setList].&lt;br /&gt;
* Mit [[webCmd]] werden die Widgets angezeigt, siehe [https://fhem.de/commandref_DE.html#setList webCmd].&lt;br /&gt;
* Mit webCmdLabel werden die Widgets beschriftet und tabellarisch angeordnet, siehe [https://fhem.de/commandref_DE.html#webCmdLabel webCmdLabel].&lt;br /&gt;
* Mit widgetOverride ist es möglich, die im Modul festgelegten Widgets zu überschreiben, siehe [https://fhem.de/commandref_DE.html#widgetOverride widgetOverride].&lt;br /&gt;
* Mit [[eventMap]] ist es möglich Widgets an Mapping-Argumente zu binden, siehe [https://fhem.de/commandref_DE.html#eventMap eventMap] und {{Link2Forum|Topic=76755|LinkText=eventMap und Widget-Anbindung}}.&lt;br /&gt;
Das Zusammenspiel dieser Attribute ist u.A. im Artikel [[DeviceOverview anpassen#webCmd .26 Co|DeviceOverview anpassen]] näher erläutert.&lt;br /&gt;
&lt;br /&gt;
== Zusammenstellung der Widgets ==&lt;br /&gt;
[[Datei:fhemweb_widgets_1.png|mini|rechts]]&lt;br /&gt;
Die Bildschirmkopie zeigt das Benutzerinterface des Dummys mit den Widgets&lt;br /&gt;
&lt;br /&gt;
=== weitere Konfiguration ===&lt;br /&gt;
* &#039;&#039;&#039;knob&#039;&#039;&#039;, siehe http://anthonyterrien.com/knob/&lt;br /&gt;
* &#039;&#039;&#039;colorpicker&#039;&#039;&#039;, siehe [[Color]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
== Das Beispiel für [[DOIF/Import_von_Code_Snippets|Raw definition]] zum Ausprobieren ==&lt;br /&gt;
In der vorstehenden Abbildung fehlt das Look and Feel der Widgets. Mit dem folgenden Code-Snippet zum Ausprobieren, wird dies ergänzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod widgets dummy&lt;br /&gt;
attr widgets readingList 00select 01select 02selectnumbers 03selectnumbers 04textField 05textFieldNL 06textField-long 07textFieldNL-long 08slider 09multiple 10multiple-strict 11knob 12sortable 13sortable-strict 14sortable-given 15uzsuToggle 16uzsuSelect 17uzsuSelectRadio 18uzsuDropDown 19colorpicker_RGB 19colorpicker_HSV 19colorpicker_CT 19colorpicker_HUE 19colorpicker_BRI 20time 21iconRadio 21iconRadio_use4icon 22iconSwitch 23iconLabel 24iconButtons 24iconButtons_use4icon 25bitfield 26widgetList&lt;br /&gt;
attr widgets room 0_Test&lt;br /&gt;
attr widgets setList 00select:1,2,3,4,5,0,a,b,c,def,hik \&lt;br /&gt;
01select:select,1,2,3,4,5,0,a,b,c,def,hik \&lt;br /&gt;
02selectnumbers:selectnumbers,-30,3,33,1,lin \&lt;br /&gt;
03selectnumbers:selectnumbers,1,0.0625,140000,0,log10 \&lt;br /&gt;
04textField:textField \&lt;br /&gt;
05textFieldNL:textFieldNL\&lt;br /&gt;
06textField-long:textField-long,87 \&lt;br /&gt;
07textFieldNL-long:textFieldNL-long,87 \&lt;br /&gt;
08slider:slider,7,0.5,30,1 \&lt;br /&gt;
09multiple:multiple,shirt,shoes,skirt,trowsers \&lt;br /&gt;
10multiple-strict:multiple-strict,red,green,blue,yellow \&lt;br /&gt;
11knob:knob,min:0,max:360,width:50,height:50,step:1,lineCap:round,angleOffset:180,cursor:3,thickness:.3 \&lt;br /&gt;
12sortable:sortable,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
13sortable-strict:sortable-strict,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
14sortable-given:sortable-given,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
15uzsuToggle:uzsuToggle,ON,OFF\&lt;br /&gt;
16uzsuSelect:uzsuSelect,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
17uzsuSelectRadio:uzsuSelectRadio,MW,KW,UKW\&lt;br /&gt;
18uzsuDropDown:uzsuDropDown,red,green,blue,yellow \&lt;br /&gt;
19colorpicker_RGB:colorpicker,RGB \&lt;br /&gt;
19colorpicker_HSV:colorpicker,HSV \&lt;br /&gt;
19colorpicker_CT:colorpicker,CT,2000,10,6500 \&lt;br /&gt;
19colorpicker_HUE:colorpicker,HUE,0,1,359 \&lt;br /&gt;
19colorpicker_BRI:colorpicker,BRI,0,1,100 \&lt;br /&gt;
20time:time \&lt;br /&gt;
21iconRadio:iconRadio,#808080,10,fts_shutter_10@FFA500,20,fts_shutter_20@orange,30,fts_shutter_30@orange,40,fts_shutter_40@orange,50,fts_shutter_50@orange,60,fts_shutter_60@orange,70,fts_shutter_70@orange,80,fts_shutter_80@orange,90,fts_shutter_90@orange,100,fts_shutter_100@orange \&lt;br /&gt;
21iconRadio_use4icon:iconRadio,use4icon@808080,10,fts_shutter_10@FFA500,20,fts_shutter_20@orange,30,fts_shutter_30@orange,40,fts_shutter_40@orange,50,fts_shutter_50@orange,60,fts_shutter_60@orange,70,fts_shutter_70@orange,80,fts_shutter_80@orange,90,fts_shutter_90@orange,100,fts_shutter_100@orange \&lt;br /&gt;
22iconSwitch:iconSwitch,wide#open,@red,half#open,fts_shutter_50@%23cd2906,totally#closed,fts_shutter_100@magenta,half#closed, \&lt;br /&gt;
23iconLabel:iconLabel,wide#open,@red,half#open,fts_shutter_50@%23cd2906,totally#closed,fts_shutter_100@magenta,half#closed,\&lt;br /&gt;
24iconButtons:iconButtons,orange,Bad,sani_heating@green,Wohnzimmer_1,sani_heating@green,Wohnzimmer_2,sani_heating@green,Esszimmer,sani_heating@green,Diele,sani_heating@green,Gäste_WC,sani_heating@green,Büro,sani_heating@green,Wintergarten,sani_heating@green,Werkstatt,sani_heating@green,Schlafzimmer,sani_heating@green \&lt;br /&gt;
24iconButtons_use4icon:iconButtons,use4icon@red,Bad,sani_heating@0000FF,Wohnzimmer_1,sani_heating@blue,Wohnzimmer_2,sani_heating@blue,Esszimmer,sani_heating@blue,Diele,sani_heating@blue,Gäste_WC,sani_heating@blue,Büro,sani_heating@blue,Wintergarten,sani_heating@blue,Werkstatt,sani_heating@blue,Schlafzimmer,sani_heating@0000FF\&lt;br /&gt;
25bitfield:bitfield\&lt;br /&gt;
26widgetList:widgetList,4,select,Bad,Küche,Wohnzimmer,6,selectnumbers,7,1,30,0,lin&lt;br /&gt;
attr widgets userReadings 23iconLabel:22iconSwitch.* {ReadingsVal($name,&amp;quot;22iconSwitch&amp;quot;,&amp;quot;wideopen&amp;quot;)}&lt;br /&gt;
attr widgets webCmd 00select:01select:02selectnumbers:03selectnumbers:04textField:05textFieldNL:06textField-long:07textFieldNL-long:08slider:20time:09multiple:10multiple-strict:11knob:12sortable:13sortable-strict:14sortable-given:15uzsuToggle:16uzsuSelect:17uzsuSelectRadio:18uzsuDropDown:19colorpicker_RGB:19colorpicker_HSV:19colorpicker_CT:19colorpicker_HUE:19colorpicker_BRI:21iconRadio:21iconRadio_use4icon:24iconButtons:24iconButtons_use4icon:22iconSwitch:23iconLabel:25bitfield:26widgetList&lt;br /&gt;
attr widgets webCmdLabel select (default)\&lt;br /&gt;
:select (explicit)\&lt;br /&gt;
:selectnumbers (linear)\&lt;br /&gt;
:selectnumbers (logarithmic)\&lt;br /&gt;
:textField\&lt;br /&gt;
:textFieldNL (no label)\&lt;br /&gt;
:textField-long (multi-line)\&lt;br /&gt;
:textFieldNL-long (multi-line,no label)\&lt;br /&gt;
:slider\&lt;br /&gt;
:time\&lt;br /&gt;
:multiple\&lt;br /&gt;
:multiple-strict\&lt;br /&gt;
:knob\&lt;br /&gt;
:sortable\&lt;br /&gt;
:sortable-strict\&lt;br /&gt;
:sortable-given\&lt;br /&gt;
:uzsuToggle\&lt;br /&gt;
:uzsuSelect\&lt;br /&gt;
:uzsuSelectRadio\&lt;br /&gt;
:uzsuDropDown\&lt;br /&gt;
:colorpicker RGB\&lt;br /&gt;
:colorpicker HSV\&lt;br /&gt;
:colorpicker Farbtemperatur\&lt;br /&gt;
:colorpicker Farbton\&lt;br /&gt;
:colorpicker Helligkeit\&lt;br /&gt;
:iconRadio\&lt;br /&gt;
:iconRadio prefix use4icon\&lt;br /&gt;
:iconButtons\&lt;br /&gt;
:iconButtons prefix use4icon\&lt;br /&gt;
:iconSwitch\&lt;br /&gt;
:iconLabel\&lt;br /&gt;
:bitfield\&lt;br /&gt;
:widgetList (Raum Temperatur)&lt;br /&gt;
attr widgets widgetOverride readingList|setList|webCmd|webCmdLabel|widgetOverride:textField-long,86&lt;br /&gt;
&lt;br /&gt;
setstate widgets iconSwitch wide#open&lt;br /&gt;
setstate widgets 2024-03-19 20:18:38 00select 1&lt;br /&gt;
setstate widgets 2021-09-27 18:52:48 01select 0&lt;br /&gt;
setstate widgets 2021-09-27 18:52:33 02selectnumbers 0.0&lt;br /&gt;
setstate widgets 2021-09-27 12:13:35 03selectnumbers 64938&lt;br /&gt;
setstate widgets 2023-01-03 14:32:14 04textField Hello&lt;br /&gt;
setstate widgets 2017-08-24 17:39:22 05textField Hallo&lt;br /&gt;
setstate widgets 2023-01-03 14:47:53 05textFieldNL Hello&lt;br /&gt;
setstate widgets 2023-01-08 19:25:46 06textField-long Hello&lt;br /&gt;
setstate widgets 2022-11-28 18:23:30 07textFieldNL-long Hello&lt;br /&gt;
setstate widgets 2017-08-24 18:22:07 08slider 17.0&lt;br /&gt;
setstate widgets 2017-08-24 18:22:40 09multiple shirt,shoes&lt;br /&gt;
setstate widgets 2017-08-24 18:22:57 10multiple-strict red,green&lt;br /&gt;
setstate widgets 2021-09-27 03:19:35 11knob 207&lt;br /&gt;
setstate widgets 2017-08-24 18:21:09 12sortable blue,shirt,brown,skirt,white,shoes,black,jeans&lt;br /&gt;
setstate widgets 2017-08-24 18:48:17 13sortable-strict blue,white,shirt,shoes,brown,skirt,black,jeans&lt;br /&gt;
setstate widgets 2017-08-24 18:49:34 14sortable-given blue,shirt,white,shoes,brown,skirt,black,jeans&lt;br /&gt;
setstate widgets 2024-03-19 20:11:06 15uzsuToggle OFF&lt;br /&gt;
setstate widgets 2024-03-19 20:10:53 16uzsuSelect brown&lt;br /&gt;
setstate widgets 2024-03-19 20:11:16 17uzsuSelectRadio UKW&lt;br /&gt;
setstate widgets 2024-03-19 20:11:09 18uzsuDropDown blue&lt;br /&gt;
setstate widgets 2017-10-07 07:56:43 19colorpicker_BRI 54&lt;br /&gt;
setstate widgets 2017-10-07 07:56:31 19colorpicker_CT 5100&lt;br /&gt;
setstate widgets 2017-10-07 07:59:26 19colorpicker_HSV 208c20&lt;br /&gt;
setstate widgets 2017-10-07 07:56:29 19colorpicker_HUE 94&lt;br /&gt;
setstate widgets 2017-10-07 07:53:01 19colorpicker_RGB c95918&lt;br /&gt;
setstate widgets 2017-08-24 19:03:20 20time 12:00&lt;br /&gt;
setstate widgets 2021-09-27 18:54:56 21iconRadio 90&lt;br /&gt;
setstate widgets 2021-09-27 03:19:18 21iconRadio_use4icon 40&lt;br /&gt;
setstate widgets 2024-03-20 18:55:18 22iconSwitch half closed&lt;br /&gt;
setstate widgets 2024-03-20 18:55:18 23iconLabel half closed&lt;br /&gt;
setstate widgets 2021-09-27 18:57:53 24iconButtons Büro,Schlafzimmer&lt;br /&gt;
setstate widgets 2017-10-25 08:20:05 24iconButtons_use4icon Bad,Wohnzimmer_2,Diele,Büro,Werkstatt&lt;br /&gt;
setstate widgets 2024-03-19 20:07:03 25bitfield 25&lt;br /&gt;
setstate widgets 2024-04-11 19:25:52 26widgetList Küche,23&lt;br /&gt;
setstate widgets 2021-09-27 19:02:25 state iconSwitch wide#open&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beigesteuerte Widgets ==&lt;br /&gt;
[[Datei:datetimepicker.png|mini|rechts]]&lt;br /&gt;
* Forenthread zu {{Link2Forum|Topic=35736|LinkText= DateTimePicker Widget}}&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;[[Datei:Controlminidash.png|mini|rechts]]&lt;br /&gt;
* Wiki Seite zu [[FHEMWEB/ControlMiniDash| ControlMiniDash Widget]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;[[Datei:Minichart flex mobile.png|mini|rechts]]&lt;br /&gt;
* Wiki Seite zu [[FHEMWEB/MiniChart| MiniChart Widget]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;[[Datei:AnimatedSVG.gif|mini|rechts]]&lt;br /&gt;
* Wiki Seite zu [[FHEMWEB/iconAnimated| iconAnimated Widget]] (Bild drücken für Gif)&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:Glossary]]&lt;br /&gt;
[[Kategorie:HOWTOS]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/iconAnimated&amp;diff=40550</id>
		<title>FHEMWEB/iconAnimated</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/iconAnimated&amp;diff=40550"/>
		<updated>2025-12-17T20:18:15Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:AnimatedSVG.gif|mini|rechts]]&lt;br /&gt;
== iconAnimated – animiertes SVG-Icon-Widget ==&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;iconAnimated&#039;&#039;&#039;-Widget visualisiert Gerätezustände als animiertes SVG-Icon im [[FHEMWEB]] ([[PGM2]]) Frontend.  &lt;br /&gt;
Farbe und Animation des Icons passen sich dynamisch an ein konfiguriertes Reading an.&lt;br /&gt;
&lt;br /&gt;
Neben inline eingebetteten SVGs werden auch &#039;&#039;&#039;FHEM-Icons direkt unterstützt&#039;&#039;&#039; (Attribut &amp;lt;code&amp;gt;icon&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=143313|LinkText=iconAnimated Widget}}&lt;br /&gt;
&lt;br /&gt;
== 1️⃣ Widget-Datei aus contrib laden ==&lt;br /&gt;
Die JavaScript-Datei muss ins FHEM-Webverzeichnis kopiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{ Svn_GetFile(&#039;contrib/fhemweb_iconAnimated.js&#039;, &#039;www/pgm2/fhemweb_iconAnimated.js&#039;) }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2️⃣ Widget im Device einrichten ==&lt;br /&gt;
=== Syntax ===&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;lt;webCmd&amp;gt;:iconAnimated,&amp;lt;Reading&amp;gt;,&amp;lt;StateOn@ColorOn&amp;gt;,&amp;lt;StateOff@ColorOff&amp;gt;,&amp;lt;AnimationType&amp;gt;,[size@Px]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Parameter ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter&lt;br /&gt;
! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Reading&amp;lt;/code&amp;gt;&lt;br /&gt;
| Name des Readings, dessen Wert das Icon steuert (Standard: &amp;lt;code&amp;gt;state&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;StateOn@ColorOn&amp;lt;/code&amp;gt;&lt;br /&gt;
| Zustand für „ein“ (z. B. &amp;lt;code&amp;gt;on@#00ff00&amp;lt;/code&amp;gt;).  &lt;br /&gt;
Numerischer Vergleich möglich, z. B. &amp;lt;code&amp;gt;&amp;gt;=1&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;StateOff@ColorOff&amp;lt;/code&amp;gt;&lt;br /&gt;
| Zustand für „aus“ (z. B. &amp;lt;code&amp;gt;off@#666&amp;lt;/code&amp;gt;).  &lt;br /&gt;
Numerischer Vergleich möglich, z. B. &amp;lt;code&amp;gt;&amp;lt;1&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;AnimationType&amp;lt;/code&amp;gt;&lt;br /&gt;
| Art der Animation (siehe unten)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;size@Px&amp;lt;/code&amp;gt;&lt;br /&gt;
| Optional: Icongröße in Pixel (Standard: 40px)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Beispiele ===&lt;br /&gt;
* Zeichenfolge:&lt;br /&gt;
:&amp;lt;code&amp;gt;attr MyDevice widgetOverride webCmd:iconAnimated,state,on@#00ff00,off@#666,pulse,size@32&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Numerischer Vergleich:&lt;br /&gt;
:&amp;lt;code&amp;gt;attr MyDevice widgetOverride webCmd:iconAnimated,power,&amp;gt;=1@#ff0000,&amp;lt;1@#666,bounce,size@32&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier wird das Icon animiert „on“, wenn &amp;lt;code&amp;gt;power&amp;lt;/code&amp;gt; ≥ 1 ist, und „off“, wenn &amp;lt; 1.&lt;br /&gt;
&lt;br /&gt;
== Animationen ==&lt;br /&gt;
* &#039;&#039;&#039;alarm&#039;&#039;&#039; – Alarmartige Skalierung + Rotation&lt;br /&gt;
* &#039;&#039;&#039;blink&#039;&#039;&#039; – Blinkende Anzeige&lt;br /&gt;
* &#039;&#039;&#039;flow&#039;&#039;&#039; – sanfte vertikale Bewegung&lt;br /&gt;
* &#039;&#039;&#039;heat&#039;&#039;&#039; – Hitze-/Flammenanimation&lt;br /&gt;
* &#039;&#039;&#039;heatDevice&#039;&#039;&#039; – Geräte die Hitze ausstrahlen&lt;br /&gt;
* &#039;&#039;&#039;pulse&#039;&#039;&#039; – pulsierende Skalierung&lt;br /&gt;
* &#039;&#039;&#039;ring&#039;&#039;&#039; – klingelnde Bewegung&lt;br /&gt;
* &#039;&#039;&#039;robot&#039;&#039;&#039; – Roboterartige Rotation&lt;br /&gt;
* &#039;&#039;&#039;rotateLeft&#039;&#039;&#039; – Rotation nach links&lt;br /&gt;
* &#039;&#039;&#039;rotateRight&#039;&#039;&#039; – Rotation nach rechts&lt;br /&gt;
* &#039;&#039;&#039;rotate2d&#039;&#039;&#039; – 3D-Drehung um Y-Achse&lt;br /&gt;
* &#039;&#039;&#039;bounce&#039;&#039;&#039; – leichtes Hüpfen&lt;br /&gt;
* &#039;&#039;&#039;shake&#039;&#039;&#039; – seitliches Wackeln&lt;br /&gt;
* &#039;&#039;&#039;swing&#039;&#039;&#039; – pendelnde Bewegung&lt;br /&gt;
&lt;br /&gt;
== Icon laden ==&lt;br /&gt;
=== FHEM-Icons ===&lt;br /&gt;
SVG-Icons aus der FHEM-Icon-Bibliothek werden &#039;&#039;&#039;direkt unterstützt&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr &amp;lt;Device&amp;gt; icon &amp;lt;fhem-svg-iconname&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Widget lädt das SVG automatisch aus dem attr icon und animiert es entsprechend.&lt;br /&gt;
&lt;br /&gt;
=== Inline-SVG ===&lt;br /&gt;
Alternativ kann ein SVG direkt als Data-URL hinterlegt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr &amp;lt;Device&amp;gt; userattr iconAnimated&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr &amp;lt;Device&amp;gt; iconAnimated data:image/svg+xml;base64,...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In diesem Fall hat &amp;lt;code&amp;gt;iconAnimated&amp;lt;/code&amp;gt; Vorrang vor dem &amp;lt;code&amp;gt;icon&amp;lt;/code&amp;gt;-Attribut.&lt;br /&gt;
Eine gute Icon-Quelle dafür ist [https://pictogrammers.com/libraries/ pictogrammers.com].&lt;br /&gt;
&lt;br /&gt;
== Hinweise ==&lt;br /&gt;
* Das Widget funktioniert nur mit &#039;&#039;&#039;SVG-Icons&#039;&#039;&#039;.&lt;br /&gt;
* PNG/JPG werden ignoriert.&lt;br /&gt;
* Fhemicons sind etwas zickig. Beispiel rotateLeft und das Icon &amp;quot;aurora&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/Widgets&amp;diff=40549</id>
		<title>FHEMWEB/Widgets</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/Widgets&amp;diff=40549"/>
		<updated>2025-12-17T19:42:11Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Widgets sind Frontendelemente, die der Dateneingabe und -anzeige dienen. Die hier gezeigten Frontendelemente werden durch [[FHEMWEB]] ([[PGM2]]) bereitgestellt.&lt;br /&gt;
&lt;br /&gt;
== Syntax ==&lt;br /&gt;
Die Syntax der Widgets ist unter dem Attribut [https://fhem.de/commandref_DE.html#widgetOverride widgetOverride] in der Befehlreferenz beschrieben.&lt;br /&gt;
&lt;br /&gt;
== Verwendung ==&lt;br /&gt;
Viele Module (u.a. [[DOIF]], [[dummy]], [[MQTT_DEVICE]], [[MQTT2_DEVICE]], [[MYSENSORS_DEVICE]], [[readingsGroup]]) bieten die Möglichkeit eine Benutzerschnittstelle zu erstellen.&lt;br /&gt;
&lt;br /&gt;
Widgets werden über &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;:&amp;lt;modifier&amp;gt;&amp;lt;/code&amp;gt;-Paare an [[Readings]] oder [[Attribute]] gebunden, dabei ist:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;&amp;lt;/code&amp;gt; ein Readingsname, ein Attribut oder ein Mapping-Argument.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;modifier&amp;gt;&amp;lt;/code&amp;gt; ein Widget einschliesslich Parameter.&lt;br /&gt;
&lt;br /&gt;
Bei der Erstellung spielen verschiedene Attribute zusammen:&lt;br /&gt;
&lt;br /&gt;
* Mit readingList (bzw. bei Modulen, die dies nicht kennen: widgetOverride) ist es möglich den Set-Befehl auf [[Readings]] zu erweitern, siehe [https://fhem.de/commandref_DE.html#readingList readingList].&lt;br /&gt;
* Mit [[setList]] (bzw. widgetOverride) werden die Widgets einem Reading zugeordnet und parametrisiert, siehe [https://fhem.de/commandref_DE.html#setList setList].&lt;br /&gt;
* Mit [[webCmd]] werden die Widgets angezeigt, siehe [https://fhem.de/commandref_DE.html#setList webCmd].&lt;br /&gt;
* Mit webCmdLabel werden die Widgets beschriftet und tabellarisch angeordnet, siehe [https://fhem.de/commandref_DE.html#webCmdLabel webCmdLabel].&lt;br /&gt;
* Mit widgetOverride ist es möglich, die im Modul festgelegten Widgets zu überschreiben, siehe [https://fhem.de/commandref_DE.html#widgetOverride widgetOverride].&lt;br /&gt;
* Mit [[eventMap]] ist es möglich Widgets an Mapping-Argumente zu binden, siehe [https://fhem.de/commandref_DE.html#eventMap eventMap] und {{Link2Forum|Topic=76755|LinkText=eventMap und Widget-Anbindung}}.&lt;br /&gt;
Das Zusammenspiel dieser Attribute ist u.A. im Artikel [[DeviceOverview anpassen#webCmd .26 Co|DeviceOverview anpassen]] näher erläutert.&lt;br /&gt;
&lt;br /&gt;
== Zusammenstellung der Widgets ==&lt;br /&gt;
[[Datei:fhemweb_widgets_1.png|mini|rechts]]&lt;br /&gt;
Die Bildschirmkopie zeigt das Benutzerinterface des Dummys mit den Widgets&lt;br /&gt;
&lt;br /&gt;
=== weitere Konfiguration ===&lt;br /&gt;
* &#039;&#039;&#039;knob&#039;&#039;&#039;, siehe http://anthonyterrien.com/knob/&lt;br /&gt;
* &#039;&#039;&#039;colorpicker&#039;&#039;&#039;, siehe [[Color]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
== Das Beispiel für [[DOIF/Import_von_Code_Snippets|Raw definition]] zum Ausprobieren ==&lt;br /&gt;
In der vorstehenden Abbildung fehlt das Look and Feel der Widgets. Mit dem folgenden Code-Snippet zum Ausprobieren, wird dies ergänzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod widgets dummy&lt;br /&gt;
attr widgets readingList 00select 01select 02selectnumbers 03selectnumbers 04textField 05textFieldNL 06textField-long 07textFieldNL-long 08slider 09multiple 10multiple-strict 11knob 12sortable 13sortable-strict 14sortable-given 15uzsuToggle 16uzsuSelect 17uzsuSelectRadio 18uzsuDropDown 19colorpicker_RGB 19colorpicker_HSV 19colorpicker_CT 19colorpicker_HUE 19colorpicker_BRI 20time 21iconRadio 21iconRadio_use4icon 22iconSwitch 23iconLabel 24iconButtons 24iconButtons_use4icon 25bitfield 26widgetList&lt;br /&gt;
attr widgets room 0_Test&lt;br /&gt;
attr widgets setList 00select:1,2,3,4,5,0,a,b,c,def,hik \&lt;br /&gt;
01select:select,1,2,3,4,5,0,a,b,c,def,hik \&lt;br /&gt;
02selectnumbers:selectnumbers,-30,3,33,1,lin \&lt;br /&gt;
03selectnumbers:selectnumbers,1,0.0625,140000,0,log10 \&lt;br /&gt;
04textField:textField \&lt;br /&gt;
05textFieldNL:textFieldNL\&lt;br /&gt;
06textField-long:textField-long,87 \&lt;br /&gt;
07textFieldNL-long:textFieldNL-long,87 \&lt;br /&gt;
08slider:slider,7,0.5,30,1 \&lt;br /&gt;
09multiple:multiple,shirt,shoes,skirt,trowsers \&lt;br /&gt;
10multiple-strict:multiple-strict,red,green,blue,yellow \&lt;br /&gt;
11knob:knob,min:0,max:360,width:50,height:50,step:1,lineCap:round,angleOffset:180,cursor:3,thickness:.3 \&lt;br /&gt;
12sortable:sortable,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
13sortable-strict:sortable-strict,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
14sortable-given:sortable-given,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
15uzsuToggle:uzsuToggle,ON,OFF\&lt;br /&gt;
16uzsuSelect:uzsuSelect,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
17uzsuSelectRadio:uzsuSelectRadio,MW,KW,UKW\&lt;br /&gt;
18uzsuDropDown:uzsuDropDown,red,green,blue,yellow \&lt;br /&gt;
19colorpicker_RGB:colorpicker,RGB \&lt;br /&gt;
19colorpicker_HSV:colorpicker,HSV \&lt;br /&gt;
19colorpicker_CT:colorpicker,CT,2000,10,6500 \&lt;br /&gt;
19colorpicker_HUE:colorpicker,HUE,0,1,359 \&lt;br /&gt;
19colorpicker_BRI:colorpicker,BRI,0,1,100 \&lt;br /&gt;
20time:time \&lt;br /&gt;
21iconRadio:iconRadio,#808080,10,fts_shutter_10@FFA500,20,fts_shutter_20@orange,30,fts_shutter_30@orange,40,fts_shutter_40@orange,50,fts_shutter_50@orange,60,fts_shutter_60@orange,70,fts_shutter_70@orange,80,fts_shutter_80@orange,90,fts_shutter_90@orange,100,fts_shutter_100@orange \&lt;br /&gt;
21iconRadio_use4icon:iconRadio,use4icon@808080,10,fts_shutter_10@FFA500,20,fts_shutter_20@orange,30,fts_shutter_30@orange,40,fts_shutter_40@orange,50,fts_shutter_50@orange,60,fts_shutter_60@orange,70,fts_shutter_70@orange,80,fts_shutter_80@orange,90,fts_shutter_90@orange,100,fts_shutter_100@orange \&lt;br /&gt;
22iconSwitch:iconSwitch,wide#open,@red,half#open,fts_shutter_50@%23cd2906,totally#closed,fts_shutter_100@magenta,half#closed, \&lt;br /&gt;
23iconLabel:iconLabel,wide#open,@red,half#open,fts_shutter_50@%23cd2906,totally#closed,fts_shutter_100@magenta,half#closed,\&lt;br /&gt;
24iconButtons:iconButtons,orange,Bad,sani_heating@green,Wohnzimmer_1,sani_heating@green,Wohnzimmer_2,sani_heating@green,Esszimmer,sani_heating@green,Diele,sani_heating@green,Gäste_WC,sani_heating@green,Büro,sani_heating@green,Wintergarten,sani_heating@green,Werkstatt,sani_heating@green,Schlafzimmer,sani_heating@green \&lt;br /&gt;
24iconButtons_use4icon:iconButtons,use4icon@red,Bad,sani_heating@0000FF,Wohnzimmer_1,sani_heating@blue,Wohnzimmer_2,sani_heating@blue,Esszimmer,sani_heating@blue,Diele,sani_heating@blue,Gäste_WC,sani_heating@blue,Büro,sani_heating@blue,Wintergarten,sani_heating@blue,Werkstatt,sani_heating@blue,Schlafzimmer,sani_heating@0000FF\&lt;br /&gt;
25bitfield:bitfield\&lt;br /&gt;
26widgetList:widgetList,4,select,Bad,Küche,Wohnzimmer,6,selectnumbers,7,1,30,0,lin&lt;br /&gt;
attr widgets userReadings 23iconLabel:22iconSwitch.* {ReadingsVal($name,&amp;quot;22iconSwitch&amp;quot;,&amp;quot;wideopen&amp;quot;)}&lt;br /&gt;
attr widgets webCmd 00select:01select:02selectnumbers:03selectnumbers:04textField:05textFieldNL:06textField-long:07textFieldNL-long:08slider:20time:09multiple:10multiple-strict:11knob:12sortable:13sortable-strict:14sortable-given:15uzsuToggle:16uzsuSelect:17uzsuSelectRadio:18uzsuDropDown:19colorpicker_RGB:19colorpicker_HSV:19colorpicker_CT:19colorpicker_HUE:19colorpicker_BRI:21iconRadio:21iconRadio_use4icon:24iconButtons:24iconButtons_use4icon:22iconSwitch:23iconLabel:25bitfield:26widgetList&lt;br /&gt;
attr widgets webCmdLabel select (default)\&lt;br /&gt;
:select (explicit)\&lt;br /&gt;
:selectnumbers (linear)\&lt;br /&gt;
:selectnumbers (logarithmic)\&lt;br /&gt;
:textField\&lt;br /&gt;
:textFieldNL (no label)\&lt;br /&gt;
:textField-long (multi-line)\&lt;br /&gt;
:textFieldNL-long (multi-line,no label)\&lt;br /&gt;
:slider\&lt;br /&gt;
:time\&lt;br /&gt;
:multiple\&lt;br /&gt;
:multiple-strict\&lt;br /&gt;
:knob\&lt;br /&gt;
:sortable\&lt;br /&gt;
:sortable-strict\&lt;br /&gt;
:sortable-given\&lt;br /&gt;
:uzsuToggle\&lt;br /&gt;
:uzsuSelect\&lt;br /&gt;
:uzsuSelectRadio\&lt;br /&gt;
:uzsuDropDown\&lt;br /&gt;
:colorpicker RGB\&lt;br /&gt;
:colorpicker HSV\&lt;br /&gt;
:colorpicker Farbtemperatur\&lt;br /&gt;
:colorpicker Farbton\&lt;br /&gt;
:colorpicker Helligkeit\&lt;br /&gt;
:iconRadio\&lt;br /&gt;
:iconRadio prefix use4icon\&lt;br /&gt;
:iconButtons\&lt;br /&gt;
:iconButtons prefix use4icon\&lt;br /&gt;
:iconSwitch\&lt;br /&gt;
:iconLabel\&lt;br /&gt;
:bitfield\&lt;br /&gt;
:widgetList (Raum Temperatur)&lt;br /&gt;
attr widgets widgetOverride readingList|setList|webCmd|webCmdLabel|widgetOverride:textField-long,86&lt;br /&gt;
&lt;br /&gt;
setstate widgets iconSwitch wide#open&lt;br /&gt;
setstate widgets 2024-03-19 20:18:38 00select 1&lt;br /&gt;
setstate widgets 2021-09-27 18:52:48 01select 0&lt;br /&gt;
setstate widgets 2021-09-27 18:52:33 02selectnumbers 0.0&lt;br /&gt;
setstate widgets 2021-09-27 12:13:35 03selectnumbers 64938&lt;br /&gt;
setstate widgets 2023-01-03 14:32:14 04textField Hello&lt;br /&gt;
setstate widgets 2017-08-24 17:39:22 05textField Hallo&lt;br /&gt;
setstate widgets 2023-01-03 14:47:53 05textFieldNL Hello&lt;br /&gt;
setstate widgets 2023-01-08 19:25:46 06textField-long Hello&lt;br /&gt;
setstate widgets 2022-11-28 18:23:30 07textFieldNL-long Hello&lt;br /&gt;
setstate widgets 2017-08-24 18:22:07 08slider 17.0&lt;br /&gt;
setstate widgets 2017-08-24 18:22:40 09multiple shirt,shoes&lt;br /&gt;
setstate widgets 2017-08-24 18:22:57 10multiple-strict red,green&lt;br /&gt;
setstate widgets 2021-09-27 03:19:35 11knob 207&lt;br /&gt;
setstate widgets 2017-08-24 18:21:09 12sortable blue,shirt,brown,skirt,white,shoes,black,jeans&lt;br /&gt;
setstate widgets 2017-08-24 18:48:17 13sortable-strict blue,white,shirt,shoes,brown,skirt,black,jeans&lt;br /&gt;
setstate widgets 2017-08-24 18:49:34 14sortable-given blue,shirt,white,shoes,brown,skirt,black,jeans&lt;br /&gt;
setstate widgets 2024-03-19 20:11:06 15uzsuToggle OFF&lt;br /&gt;
setstate widgets 2024-03-19 20:10:53 16uzsuSelect brown&lt;br /&gt;
setstate widgets 2024-03-19 20:11:16 17uzsuSelectRadio UKW&lt;br /&gt;
setstate widgets 2024-03-19 20:11:09 18uzsuDropDown blue&lt;br /&gt;
setstate widgets 2017-10-07 07:56:43 19colorpicker_BRI 54&lt;br /&gt;
setstate widgets 2017-10-07 07:56:31 19colorpicker_CT 5100&lt;br /&gt;
setstate widgets 2017-10-07 07:59:26 19colorpicker_HSV 208c20&lt;br /&gt;
setstate widgets 2017-10-07 07:56:29 19colorpicker_HUE 94&lt;br /&gt;
setstate widgets 2017-10-07 07:53:01 19colorpicker_RGB c95918&lt;br /&gt;
setstate widgets 2017-08-24 19:03:20 20time 12:00&lt;br /&gt;
setstate widgets 2021-09-27 18:54:56 21iconRadio 90&lt;br /&gt;
setstate widgets 2021-09-27 03:19:18 21iconRadio_use4icon 40&lt;br /&gt;
setstate widgets 2024-03-20 18:55:18 22iconSwitch half closed&lt;br /&gt;
setstate widgets 2024-03-20 18:55:18 23iconLabel half closed&lt;br /&gt;
setstate widgets 2021-09-27 18:57:53 24iconButtons Büro,Schlafzimmer&lt;br /&gt;
setstate widgets 2017-10-25 08:20:05 24iconButtons_use4icon Bad,Wohnzimmer_2,Diele,Büro,Werkstatt&lt;br /&gt;
setstate widgets 2024-03-19 20:07:03 25bitfield 25&lt;br /&gt;
setstate widgets 2024-04-11 19:25:52 26widgetList Küche,23&lt;br /&gt;
setstate widgets 2021-09-27 19:02:25 state iconSwitch wide#open&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beigesteuerte Widgets ==&lt;br /&gt;
[[Datei:datetimepicker.png|mini|rechts]]&lt;br /&gt;
* Forenthread zu {{Link2Forum|Topic=35736|LinkText= DateTimePicker Widget}}&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;[[Datei:Controlminidash.png|mini|rechts]]&lt;br /&gt;
* Wiki Seite zu [[FHEMWEB/ControlMiniDash| ControlMiniDash Widget]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;[[Datei:Minichart flex mobile.png|mini|rechts]]&lt;br /&gt;
* Wiki Seite zu [[FHEMWEB/MiniChart| MiniChart Widget]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;[[Datei:AnimatedSVG.gif|mini|rechts]]&lt;br /&gt;
* Wiki Seite zu [[FHEMWEB/iconAnimated| iconanimated Widget]] (Bild drücken für Gif)&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:Glossary]]&lt;br /&gt;
[[Kategorie:HOWTOS]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/iconAnimated&amp;diff=40548</id>
		<title>FHEMWEB/iconAnimated</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/iconAnimated&amp;diff=40548"/>
		<updated>2025-12-17T19:39:25Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: Die Seite wurde neu angelegt: „rechts == iconAnimated – animiertes SVG-Icon-Widget ==  Das &amp;#039;&amp;#039;&amp;#039;iconAnimated&amp;#039;&amp;#039;&amp;#039;-Widget visualisiert Gerätezustände als animiertes SVG-Icon im FHEMWEB (PGM2) Frontend.   Farbe und Animation des Icons passen sich dynamisch an ein konfiguriertes Reading an.  Neben inline eingebetteten SVGs werden auch &amp;#039;&amp;#039;&amp;#039;FHEM-Icons direkt unterstützt&amp;#039;&amp;#039;&amp;#039; (Attribut &amp;lt;code&amp;gt;icon&amp;lt;/code&amp;gt;).  === Hilfe === * Forenthread zum {{Link2Forum|Topi…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:AnimatedSVG.gif|mini|rechts]]&lt;br /&gt;
== iconAnimated – animiertes SVG-Icon-Widget ==&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;iconAnimated&#039;&#039;&#039;-Widget visualisiert Gerätezustände als animiertes SVG-Icon im [[FHEMWEB]] ([[PGM2]]) Frontend.  &lt;br /&gt;
Farbe und Animation des Icons passen sich dynamisch an ein konfiguriertes Reading an.&lt;br /&gt;
&lt;br /&gt;
Neben inline eingebetteten SVGs werden auch &#039;&#039;&#039;FHEM-Icons direkt unterstützt&#039;&#039;&#039; (Attribut &amp;lt;code&amp;gt;icon&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=143313|LinkText=iconAnimated Widget}}&lt;br /&gt;
&lt;br /&gt;
== 1️⃣ Widget-Datei aus contrib laden ==&lt;br /&gt;
Die JavaScript-Datei muss ins FHEM-Webverzeichnis kopiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{ Svn_GetFile(&#039;contrib/fhemweb_iconAnimated.js&#039;, &#039;www/pgm2/fhemweb_iconAnimated.js&#039;) }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2️⃣ Widget im Device einrichten ==&lt;br /&gt;
=== Syntax ===&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;lt;webCmd&amp;gt;:iconAnimated,&amp;lt;Reading&amp;gt;,&amp;lt;StateOn@ColorOn&amp;gt;,&amp;lt;StateOff@ColorOff&amp;gt;,&amp;lt;AnimationType&amp;gt;,[size@Px]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Parameter ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter&lt;br /&gt;
! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Reading&amp;lt;/code&amp;gt;&lt;br /&gt;
| Name des Readings, dessen Wert das Icon steuert (Standard: &amp;lt;code&amp;gt;state&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;StateOn@ColorOn&amp;lt;/code&amp;gt;&lt;br /&gt;
| Zustand für „ein“ (z. B. &amp;lt;code&amp;gt;on@#00ff00&amp;lt;/code&amp;gt;).  &lt;br /&gt;
Numerischer Vergleich möglich, z. B. &amp;lt;code&amp;gt;&amp;gt;=1&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;StateOff@ColorOff&amp;lt;/code&amp;gt;&lt;br /&gt;
| Zustand für „aus“ (z. B. &amp;lt;code&amp;gt;off@#666&amp;lt;/code&amp;gt;).  &lt;br /&gt;
Numerischer Vergleich möglich, z. B. &amp;lt;code&amp;gt;&amp;lt;1&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;AnimationType&amp;lt;/code&amp;gt;&lt;br /&gt;
| Art der Animation (siehe unten)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;size@Px&amp;lt;/code&amp;gt;&lt;br /&gt;
| Optional: Icongröße in Pixel (Standard: 40px)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Beispiele ===&lt;br /&gt;
* Zeichenfolge:&lt;br /&gt;
:&amp;lt;code&amp;gt;attr MyDevice widgetOverride webCmd:iconAnimated,state,on@#00ff00,off@#666,pulse,size@32&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Numerischer Vergleich:&lt;br /&gt;
:&amp;lt;code&amp;gt;attr MyDevice widgetOverride webCmd:iconAnimated,power,&amp;gt;=1@#ff0000,&amp;lt;1@#666,bounce,size@32&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier wird das Icon animiert „on“, wenn &amp;lt;code&amp;gt;power&amp;lt;/code&amp;gt; ≥ 1 ist, und „off“, wenn &amp;lt; 1.&lt;br /&gt;
&lt;br /&gt;
== Animationen ==&lt;br /&gt;
* &#039;&#039;&#039;alarm&#039;&#039;&#039; – Alarmartige Skalierung + Rotation&lt;br /&gt;
* &#039;&#039;&#039;blink&#039;&#039;&#039; – Blinkende Anzeige&lt;br /&gt;
* &#039;&#039;&#039;flow&#039;&#039;&#039; – sanfte vertikale Bewegung&lt;br /&gt;
* &#039;&#039;&#039;heat&#039;&#039;&#039; – Hitze-/Flammenanimation&lt;br /&gt;
* &#039;&#039;&#039;heatDevice&#039;&#039;&#039; – Geräte die Hitze ausstrahlen&lt;br /&gt;
* &#039;&#039;&#039;pulse&#039;&#039;&#039; – pulsierende Skalierung&lt;br /&gt;
* &#039;&#039;&#039;ring&#039;&#039;&#039; – klingelnde Bewegung&lt;br /&gt;
* &#039;&#039;&#039;robot&#039;&#039;&#039; – Roboterartige Rotation&lt;br /&gt;
* &#039;&#039;&#039;rotateLeft&#039;&#039;&#039; – Rotation nach links&lt;br /&gt;
* &#039;&#039;&#039;rotateRight&#039;&#039;&#039; – Rotation nach rechts&lt;br /&gt;
* &#039;&#039;&#039;rotate2d&#039;&#039;&#039; – 3D-Drehung um Y-Achse&lt;br /&gt;
* &#039;&#039;&#039;bounce&#039;&#039;&#039; – leichtes Hüpfen&lt;br /&gt;
* &#039;&#039;&#039;shake&#039;&#039;&#039; – seitliches Wackeln&lt;br /&gt;
* &#039;&#039;&#039;swing&#039;&#039;&#039; – pendelnde Bewegung&lt;br /&gt;
&lt;br /&gt;
== Icon laden ==&lt;br /&gt;
=== FHEM-Icons (empfohlen) ===&lt;br /&gt;
SVG-Icons aus der FHEM-Icon-Bibliothek werden &#039;&#039;&#039;direkt unterstützt&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr &amp;lt;Device&amp;gt; icon &amp;lt;fhem-svg-iconname&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Widget lädt das SVG automatisch aus dem attr icon und animiert es entsprechend.&lt;br /&gt;
&lt;br /&gt;
=== Inline-SVG (optional) ===&lt;br /&gt;
Alternativ kann ein SVG direkt als Data-URL hinterlegt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr &amp;lt;Device&amp;gt; userattr iconAnimated&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr &amp;lt;Device&amp;gt; iconAnimated data:image/svg+xml;base64,...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In diesem Fall hat &amp;lt;code&amp;gt;iconAnimated&amp;lt;/code&amp;gt; Vorrang vor dem &amp;lt;code&amp;gt;icon&amp;lt;/code&amp;gt;-Attribut.&lt;br /&gt;
Eine gute Icon-Quelle dafür ist [https://pictogrammers.com/libraries/ pictogrammers.com].&lt;br /&gt;
&lt;br /&gt;
== Hinweise ==&lt;br /&gt;
* Das Widget funktioniert nur mit &#039;&#039;&#039;SVG-Icons&#039;&#039;&#039;.&lt;br /&gt;
* PNG/JPG werden ignoriert.&lt;br /&gt;
* Fhemicons sind etwas zickig. Beispiel rotateLeft und das Icon &amp;quot;aurora&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/Widgets&amp;diff=40544</id>
		<title>FHEMWEB/Widgets</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/Widgets&amp;diff=40544"/>
		<updated>2025-12-14T12:57:46Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Widgets sind Frontendelemente, die der Dateneingabe und -anzeige dienen. Die hier gezeigten Frontendelemente werden durch [[FHEMWEB]] ([[PGM2]]) bereitgestellt.&lt;br /&gt;
&lt;br /&gt;
== Syntax ==&lt;br /&gt;
Die Syntax der Widgets ist unter dem Attribut [https://fhem.de/commandref_DE.html#widgetOverride widgetOverride] in der Befehlreferenz beschrieben.&lt;br /&gt;
&lt;br /&gt;
== Verwendung ==&lt;br /&gt;
Viele Module (u.a. [[DOIF]], [[dummy]], [[MQTT_DEVICE]], [[MQTT2_DEVICE]], [[MYSENSORS_DEVICE]], [[readingsGroup]]) bieten die Möglichkeit eine Benutzerschnittstelle zu erstellen.&lt;br /&gt;
&lt;br /&gt;
Widgets werden über &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;:&amp;lt;modifier&amp;gt;&amp;lt;/code&amp;gt;-Paare an [[Readings]] oder [[Attribute]] gebunden, dabei ist:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;&amp;lt;/code&amp;gt; ein Readingsname, ein Attribut oder ein Mapping-Argument.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;modifier&amp;gt;&amp;lt;/code&amp;gt; ein Widget einschliesslich Parameter.&lt;br /&gt;
&lt;br /&gt;
Bei der Erstellung spielen verschiedene Attribute zusammen:&lt;br /&gt;
&lt;br /&gt;
* Mit readingList (bzw. bei Modulen, die dies nicht kennen: widgetOverride) ist es möglich den Set-Befehl auf [[Readings]] zu erweitern, siehe [https://fhem.de/commandref_DE.html#readingList readingList].&lt;br /&gt;
* Mit [[setList]] (bzw. widgetOverride) werden die Widgets einem Reading zugeordnet und parametrisiert, siehe [https://fhem.de/commandref_DE.html#setList setList].&lt;br /&gt;
* Mit [[webCmd]] werden die Widgets angezeigt, siehe [https://fhem.de/commandref_DE.html#setList webCmd].&lt;br /&gt;
* Mit webCmdLabel werden die Widgets beschriftet und tabellarisch angeordnet, siehe [https://fhem.de/commandref_DE.html#webCmdLabel webCmdLabel].&lt;br /&gt;
* Mit widgetOverride ist es möglich, die im Modul festgelegten Widgets zu überschreiben, siehe [https://fhem.de/commandref_DE.html#widgetOverride widgetOverride].&lt;br /&gt;
* Mit [[eventMap]] ist es möglich Widgets an Mapping-Argumente zu binden, siehe [https://fhem.de/commandref_DE.html#eventMap eventMap] und {{Link2Forum|Topic=76755|LinkText=eventMap und Widget-Anbindung}}.&lt;br /&gt;
Das Zusammenspiel dieser Attribute ist u.A. im Artikel [[DeviceOverview anpassen#webCmd .26 Co|DeviceOverview anpassen]] näher erläutert.&lt;br /&gt;
&lt;br /&gt;
== Zusammenstellung der Widgets ==&lt;br /&gt;
[[Datei:fhemweb_widgets_1.png|mini|rechts]]&lt;br /&gt;
Die Bildschirmkopie zeigt das Benutzerinterface des Dummys mit den Widgets&lt;br /&gt;
&lt;br /&gt;
=== weitere Konfiguration ===&lt;br /&gt;
* &#039;&#039;&#039;knob&#039;&#039;&#039;, siehe http://anthonyterrien.com/knob/&lt;br /&gt;
* &#039;&#039;&#039;colorpicker&#039;&#039;&#039;, siehe [[Color]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
== Das Beispiel für [[DOIF/Import_von_Code_Snippets|Raw definition]] zum Ausprobieren ==&lt;br /&gt;
In der vorstehenden Abbildung fehlt das Look and Feel der Widgets. Mit dem folgenden Code-Snippet zum Ausprobieren, wird dies ergänzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod widgets dummy&lt;br /&gt;
attr widgets readingList 00select 01select 02selectnumbers 03selectnumbers 04textField 05textFieldNL 06textField-long 07textFieldNL-long 08slider 09multiple 10multiple-strict 11knob 12sortable 13sortable-strict 14sortable-given 15uzsuToggle 16uzsuSelect 17uzsuSelectRadio 18uzsuDropDown 19colorpicker_RGB 19colorpicker_HSV 19colorpicker_CT 19colorpicker_HUE 19colorpicker_BRI 20time 21iconRadio 21iconRadio_use4icon 22iconSwitch 23iconLabel 24iconButtons 24iconButtons_use4icon 25bitfield 26widgetList&lt;br /&gt;
attr widgets room 0_Test&lt;br /&gt;
attr widgets setList 00select:1,2,3,4,5,0,a,b,c,def,hik \&lt;br /&gt;
01select:select,1,2,3,4,5,0,a,b,c,def,hik \&lt;br /&gt;
02selectnumbers:selectnumbers,-30,3,33,1,lin \&lt;br /&gt;
03selectnumbers:selectnumbers,1,0.0625,140000,0,log10 \&lt;br /&gt;
04textField:textField \&lt;br /&gt;
05textFieldNL:textFieldNL\&lt;br /&gt;
06textField-long:textField-long,87 \&lt;br /&gt;
07textFieldNL-long:textFieldNL-long,87 \&lt;br /&gt;
08slider:slider,7,0.5,30,1 \&lt;br /&gt;
09multiple:multiple,shirt,shoes,skirt,trowsers \&lt;br /&gt;
10multiple-strict:multiple-strict,red,green,blue,yellow \&lt;br /&gt;
11knob:knob,min:0,max:360,width:50,height:50,step:1,lineCap:round,angleOffset:180,cursor:3,thickness:.3 \&lt;br /&gt;
12sortable:sortable,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
13sortable-strict:sortable-strict,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
14sortable-given:sortable-given,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
15uzsuToggle:uzsuToggle,ON,OFF\&lt;br /&gt;
16uzsuSelect:uzsuSelect,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
17uzsuSelectRadio:uzsuSelectRadio,MW,KW,UKW\&lt;br /&gt;
18uzsuDropDown:uzsuDropDown,red,green,blue,yellow \&lt;br /&gt;
19colorpicker_RGB:colorpicker,RGB \&lt;br /&gt;
19colorpicker_HSV:colorpicker,HSV \&lt;br /&gt;
19colorpicker_CT:colorpicker,CT,2000,10,6500 \&lt;br /&gt;
19colorpicker_HUE:colorpicker,HUE,0,1,359 \&lt;br /&gt;
19colorpicker_BRI:colorpicker,BRI,0,1,100 \&lt;br /&gt;
20time:time \&lt;br /&gt;
21iconRadio:iconRadio,#808080,10,fts_shutter_10@FFA500,20,fts_shutter_20@orange,30,fts_shutter_30@orange,40,fts_shutter_40@orange,50,fts_shutter_50@orange,60,fts_shutter_60@orange,70,fts_shutter_70@orange,80,fts_shutter_80@orange,90,fts_shutter_90@orange,100,fts_shutter_100@orange \&lt;br /&gt;
21iconRadio_use4icon:iconRadio,use4icon@808080,10,fts_shutter_10@FFA500,20,fts_shutter_20@orange,30,fts_shutter_30@orange,40,fts_shutter_40@orange,50,fts_shutter_50@orange,60,fts_shutter_60@orange,70,fts_shutter_70@orange,80,fts_shutter_80@orange,90,fts_shutter_90@orange,100,fts_shutter_100@orange \&lt;br /&gt;
22iconSwitch:iconSwitch,wide#open,@red,half#open,fts_shutter_50@%23cd2906,totally#closed,fts_shutter_100@magenta,half#closed, \&lt;br /&gt;
23iconLabel:iconLabel,wide#open,@red,half#open,fts_shutter_50@%23cd2906,totally#closed,fts_shutter_100@magenta,half#closed,\&lt;br /&gt;
24iconButtons:iconButtons,orange,Bad,sani_heating@green,Wohnzimmer_1,sani_heating@green,Wohnzimmer_2,sani_heating@green,Esszimmer,sani_heating@green,Diele,sani_heating@green,Gäste_WC,sani_heating@green,Büro,sani_heating@green,Wintergarten,sani_heating@green,Werkstatt,sani_heating@green,Schlafzimmer,sani_heating@green \&lt;br /&gt;
24iconButtons_use4icon:iconButtons,use4icon@red,Bad,sani_heating@0000FF,Wohnzimmer_1,sani_heating@blue,Wohnzimmer_2,sani_heating@blue,Esszimmer,sani_heating@blue,Diele,sani_heating@blue,Gäste_WC,sani_heating@blue,Büro,sani_heating@blue,Wintergarten,sani_heating@blue,Werkstatt,sani_heating@blue,Schlafzimmer,sani_heating@0000FF\&lt;br /&gt;
25bitfield:bitfield\&lt;br /&gt;
26widgetList:widgetList,4,select,Bad,Küche,Wohnzimmer,6,selectnumbers,7,1,30,0,lin&lt;br /&gt;
attr widgets userReadings 23iconLabel:22iconSwitch.* {ReadingsVal($name,&amp;quot;22iconSwitch&amp;quot;,&amp;quot;wideopen&amp;quot;)}&lt;br /&gt;
attr widgets webCmd 00select:01select:02selectnumbers:03selectnumbers:04textField:05textFieldNL:06textField-long:07textFieldNL-long:08slider:20time:09multiple:10multiple-strict:11knob:12sortable:13sortable-strict:14sortable-given:15uzsuToggle:16uzsuSelect:17uzsuSelectRadio:18uzsuDropDown:19colorpicker_RGB:19colorpicker_HSV:19colorpicker_CT:19colorpicker_HUE:19colorpicker_BRI:21iconRadio:21iconRadio_use4icon:24iconButtons:24iconButtons_use4icon:22iconSwitch:23iconLabel:25bitfield:26widgetList&lt;br /&gt;
attr widgets webCmdLabel select (default)\&lt;br /&gt;
:select (explicit)\&lt;br /&gt;
:selectnumbers (linear)\&lt;br /&gt;
:selectnumbers (logarithmic)\&lt;br /&gt;
:textField\&lt;br /&gt;
:textFieldNL (no label)\&lt;br /&gt;
:textField-long (multi-line)\&lt;br /&gt;
:textFieldNL-long (multi-line,no label)\&lt;br /&gt;
:slider\&lt;br /&gt;
:time\&lt;br /&gt;
:multiple\&lt;br /&gt;
:multiple-strict\&lt;br /&gt;
:knob\&lt;br /&gt;
:sortable\&lt;br /&gt;
:sortable-strict\&lt;br /&gt;
:sortable-given\&lt;br /&gt;
:uzsuToggle\&lt;br /&gt;
:uzsuSelect\&lt;br /&gt;
:uzsuSelectRadio\&lt;br /&gt;
:uzsuDropDown\&lt;br /&gt;
:colorpicker RGB\&lt;br /&gt;
:colorpicker HSV\&lt;br /&gt;
:colorpicker Farbtemperatur\&lt;br /&gt;
:colorpicker Farbton\&lt;br /&gt;
:colorpicker Helligkeit\&lt;br /&gt;
:iconRadio\&lt;br /&gt;
:iconRadio prefix use4icon\&lt;br /&gt;
:iconButtons\&lt;br /&gt;
:iconButtons prefix use4icon\&lt;br /&gt;
:iconSwitch\&lt;br /&gt;
:iconLabel\&lt;br /&gt;
:bitfield\&lt;br /&gt;
:widgetList (Raum Temperatur)&lt;br /&gt;
attr widgets widgetOverride readingList|setList|webCmd|webCmdLabel|widgetOverride:textField-long,86&lt;br /&gt;
&lt;br /&gt;
setstate widgets iconSwitch wide#open&lt;br /&gt;
setstate widgets 2024-03-19 20:18:38 00select 1&lt;br /&gt;
setstate widgets 2021-09-27 18:52:48 01select 0&lt;br /&gt;
setstate widgets 2021-09-27 18:52:33 02selectnumbers 0.0&lt;br /&gt;
setstate widgets 2021-09-27 12:13:35 03selectnumbers 64938&lt;br /&gt;
setstate widgets 2023-01-03 14:32:14 04textField Hello&lt;br /&gt;
setstate widgets 2017-08-24 17:39:22 05textField Hallo&lt;br /&gt;
setstate widgets 2023-01-03 14:47:53 05textFieldNL Hello&lt;br /&gt;
setstate widgets 2023-01-08 19:25:46 06textField-long Hello&lt;br /&gt;
setstate widgets 2022-11-28 18:23:30 07textFieldNL-long Hello&lt;br /&gt;
setstate widgets 2017-08-24 18:22:07 08slider 17.0&lt;br /&gt;
setstate widgets 2017-08-24 18:22:40 09multiple shirt,shoes&lt;br /&gt;
setstate widgets 2017-08-24 18:22:57 10multiple-strict red,green&lt;br /&gt;
setstate widgets 2021-09-27 03:19:35 11knob 207&lt;br /&gt;
setstate widgets 2017-08-24 18:21:09 12sortable blue,shirt,brown,skirt,white,shoes,black,jeans&lt;br /&gt;
setstate widgets 2017-08-24 18:48:17 13sortable-strict blue,white,shirt,shoes,brown,skirt,black,jeans&lt;br /&gt;
setstate widgets 2017-08-24 18:49:34 14sortable-given blue,shirt,white,shoes,brown,skirt,black,jeans&lt;br /&gt;
setstate widgets 2024-03-19 20:11:06 15uzsuToggle OFF&lt;br /&gt;
setstate widgets 2024-03-19 20:10:53 16uzsuSelect brown&lt;br /&gt;
setstate widgets 2024-03-19 20:11:16 17uzsuSelectRadio UKW&lt;br /&gt;
setstate widgets 2024-03-19 20:11:09 18uzsuDropDown blue&lt;br /&gt;
setstate widgets 2017-10-07 07:56:43 19colorpicker_BRI 54&lt;br /&gt;
setstate widgets 2017-10-07 07:56:31 19colorpicker_CT 5100&lt;br /&gt;
setstate widgets 2017-10-07 07:59:26 19colorpicker_HSV 208c20&lt;br /&gt;
setstate widgets 2017-10-07 07:56:29 19colorpicker_HUE 94&lt;br /&gt;
setstate widgets 2017-10-07 07:53:01 19colorpicker_RGB c95918&lt;br /&gt;
setstate widgets 2017-08-24 19:03:20 20time 12:00&lt;br /&gt;
setstate widgets 2021-09-27 18:54:56 21iconRadio 90&lt;br /&gt;
setstate widgets 2021-09-27 03:19:18 21iconRadio_use4icon 40&lt;br /&gt;
setstate widgets 2024-03-20 18:55:18 22iconSwitch half closed&lt;br /&gt;
setstate widgets 2024-03-20 18:55:18 23iconLabel half closed&lt;br /&gt;
setstate widgets 2021-09-27 18:57:53 24iconButtons Büro,Schlafzimmer&lt;br /&gt;
setstate widgets 2017-10-25 08:20:05 24iconButtons_use4icon Bad,Wohnzimmer_2,Diele,Büro,Werkstatt&lt;br /&gt;
setstate widgets 2024-03-19 20:07:03 25bitfield 25&lt;br /&gt;
setstate widgets 2024-04-11 19:25:52 26widgetList Küche,23&lt;br /&gt;
setstate widgets 2021-09-27 19:02:25 state iconSwitch wide#open&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beigesteuerte Widgets ==&lt;br /&gt;
[[Datei:datetimepicker.png|mini|rechts]]&lt;br /&gt;
* Forenthread zu {{Link2Forum|Topic=35736|LinkText= DateTimePicker Widget}}&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;[[Datei:Controlminidash.png|mini|rechts]]&lt;br /&gt;
* Wiki Seite zu [[FHEMWEB/ControlMiniDash| ControlMiniDash Widget]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;[[Datei:Minichart flex mobile.png|mini|rechts]]&lt;br /&gt;
* Wiki Seite zu [[FHEMWEB/MiniChart| MiniChart Widget]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;[[Datei:AnimatedSVG.gif|mini|rechts]]&lt;br /&gt;
* Wiki Seite zu [[FHEMWEB/AnimatedSVG| AnimatedSVG Widget]] (Bild drücken für Gif)&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:Glossary]]&lt;br /&gt;
[[Kategorie:HOWTOS]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/Widgets&amp;diff=40543</id>
		<title>FHEMWEB/Widgets</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/Widgets&amp;diff=40543"/>
		<updated>2025-12-14T12:55:49Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Widgets sind Frontendelemente, die der Dateneingabe und -anzeige dienen. Die hier gezeigten Frontendelemente werden durch [[FHEMWEB]] ([[PGM2]]) bereitgestellt.&lt;br /&gt;
&lt;br /&gt;
== Syntax ==&lt;br /&gt;
Die Syntax der Widgets ist unter dem Attribut [https://fhem.de/commandref_DE.html#widgetOverride widgetOverride] in der Befehlreferenz beschrieben.&lt;br /&gt;
&lt;br /&gt;
== Verwendung ==&lt;br /&gt;
Viele Module (u.a. [[DOIF]], [[dummy]], [[MQTT_DEVICE]], [[MQTT2_DEVICE]], [[MYSENSORS_DEVICE]], [[readingsGroup]]) bieten die Möglichkeit eine Benutzerschnittstelle zu erstellen.&lt;br /&gt;
&lt;br /&gt;
Widgets werden über &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;:&amp;lt;modifier&amp;gt;&amp;lt;/code&amp;gt;-Paare an [[Readings]] oder [[Attribute]] gebunden, dabei ist:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;&amp;lt;/code&amp;gt; ein Readingsname, ein Attribut oder ein Mapping-Argument.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;modifier&amp;gt;&amp;lt;/code&amp;gt; ein Widget einschliesslich Parameter.&lt;br /&gt;
&lt;br /&gt;
Bei der Erstellung spielen verschiedene Attribute zusammen:&lt;br /&gt;
&lt;br /&gt;
* Mit readingList (bzw. bei Modulen, die dies nicht kennen: widgetOverride) ist es möglich den Set-Befehl auf [[Readings]] zu erweitern, siehe [https://fhem.de/commandref_DE.html#readingList readingList].&lt;br /&gt;
* Mit [[setList]] (bzw. widgetOverride) werden die Widgets einem Reading zugeordnet und parametrisiert, siehe [https://fhem.de/commandref_DE.html#setList setList].&lt;br /&gt;
* Mit [[webCmd]] werden die Widgets angezeigt, siehe [https://fhem.de/commandref_DE.html#setList webCmd].&lt;br /&gt;
* Mit webCmdLabel werden die Widgets beschriftet und tabellarisch angeordnet, siehe [https://fhem.de/commandref_DE.html#webCmdLabel webCmdLabel].&lt;br /&gt;
* Mit widgetOverride ist es möglich, die im Modul festgelegten Widgets zu überschreiben, siehe [https://fhem.de/commandref_DE.html#widgetOverride widgetOverride].&lt;br /&gt;
* Mit [[eventMap]] ist es möglich Widgets an Mapping-Argumente zu binden, siehe [https://fhem.de/commandref_DE.html#eventMap eventMap] und {{Link2Forum|Topic=76755|LinkText=eventMap und Widget-Anbindung}}.&lt;br /&gt;
Das Zusammenspiel dieser Attribute ist u.A. im Artikel [[DeviceOverview anpassen#webCmd .26 Co|DeviceOverview anpassen]] näher erläutert.&lt;br /&gt;
&lt;br /&gt;
== Zusammenstellung der Widgets ==&lt;br /&gt;
[[Datei:fhemweb_widgets_1.png|mini|rechts]]&lt;br /&gt;
Die Bildschirmkopie zeigt das Benutzerinterface des Dummys mit den Widgets&lt;br /&gt;
&lt;br /&gt;
=== weitere Konfiguration ===&lt;br /&gt;
* &#039;&#039;&#039;knob&#039;&#039;&#039;, siehe http://anthonyterrien.com/knob/&lt;br /&gt;
* &#039;&#039;&#039;colorpicker&#039;&#039;&#039;, siehe [[Color]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
== Das Beispiel für [[DOIF/Import_von_Code_Snippets|Raw definition]] zum Ausprobieren ==&lt;br /&gt;
In der vorstehenden Abbildung fehlt das Look and Feel der Widgets. Mit dem folgenden Code-Snippet zum Ausprobieren, wird dies ergänzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod widgets dummy&lt;br /&gt;
attr widgets readingList 00select 01select 02selectnumbers 03selectnumbers 04textField 05textFieldNL 06textField-long 07textFieldNL-long 08slider 09multiple 10multiple-strict 11knob 12sortable 13sortable-strict 14sortable-given 15uzsuToggle 16uzsuSelect 17uzsuSelectRadio 18uzsuDropDown 19colorpicker_RGB 19colorpicker_HSV 19colorpicker_CT 19colorpicker_HUE 19colorpicker_BRI 20time 21iconRadio 21iconRadio_use4icon 22iconSwitch 23iconLabel 24iconButtons 24iconButtons_use4icon 25bitfield 26widgetList&lt;br /&gt;
attr widgets room 0_Test&lt;br /&gt;
attr widgets setList 00select:1,2,3,4,5,0,a,b,c,def,hik \&lt;br /&gt;
01select:select,1,2,3,4,5,0,a,b,c,def,hik \&lt;br /&gt;
02selectnumbers:selectnumbers,-30,3,33,1,lin \&lt;br /&gt;
03selectnumbers:selectnumbers,1,0.0625,140000,0,log10 \&lt;br /&gt;
04textField:textField \&lt;br /&gt;
05textFieldNL:textFieldNL\&lt;br /&gt;
06textField-long:textField-long,87 \&lt;br /&gt;
07textFieldNL-long:textFieldNL-long,87 \&lt;br /&gt;
08slider:slider,7,0.5,30,1 \&lt;br /&gt;
09multiple:multiple,shirt,shoes,skirt,trowsers \&lt;br /&gt;
10multiple-strict:multiple-strict,red,green,blue,yellow \&lt;br /&gt;
11knob:knob,min:0,max:360,width:50,height:50,step:1,lineCap:round,angleOffset:180,cursor:3,thickness:.3 \&lt;br /&gt;
12sortable:sortable,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
13sortable-strict:sortable-strict,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
14sortable-given:sortable-given,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
15uzsuToggle:uzsuToggle,ON,OFF\&lt;br /&gt;
16uzsuSelect:uzsuSelect,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
17uzsuSelectRadio:uzsuSelectRadio,MW,KW,UKW\&lt;br /&gt;
18uzsuDropDown:uzsuDropDown,red,green,blue,yellow \&lt;br /&gt;
19colorpicker_RGB:colorpicker,RGB \&lt;br /&gt;
19colorpicker_HSV:colorpicker,HSV \&lt;br /&gt;
19colorpicker_CT:colorpicker,CT,2000,10,6500 \&lt;br /&gt;
19colorpicker_HUE:colorpicker,HUE,0,1,359 \&lt;br /&gt;
19colorpicker_BRI:colorpicker,BRI,0,1,100 \&lt;br /&gt;
20time:time \&lt;br /&gt;
21iconRadio:iconRadio,#808080,10,fts_shutter_10@FFA500,20,fts_shutter_20@orange,30,fts_shutter_30@orange,40,fts_shutter_40@orange,50,fts_shutter_50@orange,60,fts_shutter_60@orange,70,fts_shutter_70@orange,80,fts_shutter_80@orange,90,fts_shutter_90@orange,100,fts_shutter_100@orange \&lt;br /&gt;
21iconRadio_use4icon:iconRadio,use4icon@808080,10,fts_shutter_10@FFA500,20,fts_shutter_20@orange,30,fts_shutter_30@orange,40,fts_shutter_40@orange,50,fts_shutter_50@orange,60,fts_shutter_60@orange,70,fts_shutter_70@orange,80,fts_shutter_80@orange,90,fts_shutter_90@orange,100,fts_shutter_100@orange \&lt;br /&gt;
22iconSwitch:iconSwitch,wide#open,@red,half#open,fts_shutter_50@%23cd2906,totally#closed,fts_shutter_100@magenta,half#closed, \&lt;br /&gt;
23iconLabel:iconLabel,wide#open,@red,half#open,fts_shutter_50@%23cd2906,totally#closed,fts_shutter_100@magenta,half#closed,\&lt;br /&gt;
24iconButtons:iconButtons,orange,Bad,sani_heating@green,Wohnzimmer_1,sani_heating@green,Wohnzimmer_2,sani_heating@green,Esszimmer,sani_heating@green,Diele,sani_heating@green,Gäste_WC,sani_heating@green,Büro,sani_heating@green,Wintergarten,sani_heating@green,Werkstatt,sani_heating@green,Schlafzimmer,sani_heating@green \&lt;br /&gt;
24iconButtons_use4icon:iconButtons,use4icon@red,Bad,sani_heating@0000FF,Wohnzimmer_1,sani_heating@blue,Wohnzimmer_2,sani_heating@blue,Esszimmer,sani_heating@blue,Diele,sani_heating@blue,Gäste_WC,sani_heating@blue,Büro,sani_heating@blue,Wintergarten,sani_heating@blue,Werkstatt,sani_heating@blue,Schlafzimmer,sani_heating@0000FF\&lt;br /&gt;
25bitfield:bitfield\&lt;br /&gt;
26widgetList:widgetList,4,select,Bad,Küche,Wohnzimmer,6,selectnumbers,7,1,30,0,lin&lt;br /&gt;
attr widgets userReadings 23iconLabel:22iconSwitch.* {ReadingsVal($name,&amp;quot;22iconSwitch&amp;quot;,&amp;quot;wideopen&amp;quot;)}&lt;br /&gt;
attr widgets webCmd 00select:01select:02selectnumbers:03selectnumbers:04textField:05textFieldNL:06textField-long:07textFieldNL-long:08slider:20time:09multiple:10multiple-strict:11knob:12sortable:13sortable-strict:14sortable-given:15uzsuToggle:16uzsuSelect:17uzsuSelectRadio:18uzsuDropDown:19colorpicker_RGB:19colorpicker_HSV:19colorpicker_CT:19colorpicker_HUE:19colorpicker_BRI:21iconRadio:21iconRadio_use4icon:24iconButtons:24iconButtons_use4icon:22iconSwitch:23iconLabel:25bitfield:26widgetList&lt;br /&gt;
attr widgets webCmdLabel select (default)\&lt;br /&gt;
:select (explicit)\&lt;br /&gt;
:selectnumbers (linear)\&lt;br /&gt;
:selectnumbers (logarithmic)\&lt;br /&gt;
:textField\&lt;br /&gt;
:textFieldNL (no label)\&lt;br /&gt;
:textField-long (multi-line)\&lt;br /&gt;
:textFieldNL-long (multi-line,no label)\&lt;br /&gt;
:slider\&lt;br /&gt;
:time\&lt;br /&gt;
:multiple\&lt;br /&gt;
:multiple-strict\&lt;br /&gt;
:knob\&lt;br /&gt;
:sortable\&lt;br /&gt;
:sortable-strict\&lt;br /&gt;
:sortable-given\&lt;br /&gt;
:uzsuToggle\&lt;br /&gt;
:uzsuSelect\&lt;br /&gt;
:uzsuSelectRadio\&lt;br /&gt;
:uzsuDropDown\&lt;br /&gt;
:colorpicker RGB\&lt;br /&gt;
:colorpicker HSV\&lt;br /&gt;
:colorpicker Farbtemperatur\&lt;br /&gt;
:colorpicker Farbton\&lt;br /&gt;
:colorpicker Helligkeit\&lt;br /&gt;
:iconRadio\&lt;br /&gt;
:iconRadio prefix use4icon\&lt;br /&gt;
:iconButtons\&lt;br /&gt;
:iconButtons prefix use4icon\&lt;br /&gt;
:iconSwitch\&lt;br /&gt;
:iconLabel\&lt;br /&gt;
:bitfield\&lt;br /&gt;
:widgetList (Raum Temperatur)&lt;br /&gt;
attr widgets widgetOverride readingList|setList|webCmd|webCmdLabel|widgetOverride:textField-long,86&lt;br /&gt;
&lt;br /&gt;
setstate widgets iconSwitch wide#open&lt;br /&gt;
setstate widgets 2024-03-19 20:18:38 00select 1&lt;br /&gt;
setstate widgets 2021-09-27 18:52:48 01select 0&lt;br /&gt;
setstate widgets 2021-09-27 18:52:33 02selectnumbers 0.0&lt;br /&gt;
setstate widgets 2021-09-27 12:13:35 03selectnumbers 64938&lt;br /&gt;
setstate widgets 2023-01-03 14:32:14 04textField Hello&lt;br /&gt;
setstate widgets 2017-08-24 17:39:22 05textField Hallo&lt;br /&gt;
setstate widgets 2023-01-03 14:47:53 05textFieldNL Hello&lt;br /&gt;
setstate widgets 2023-01-08 19:25:46 06textField-long Hello&lt;br /&gt;
setstate widgets 2022-11-28 18:23:30 07textFieldNL-long Hello&lt;br /&gt;
setstate widgets 2017-08-24 18:22:07 08slider 17.0&lt;br /&gt;
setstate widgets 2017-08-24 18:22:40 09multiple shirt,shoes&lt;br /&gt;
setstate widgets 2017-08-24 18:22:57 10multiple-strict red,green&lt;br /&gt;
setstate widgets 2021-09-27 03:19:35 11knob 207&lt;br /&gt;
setstate widgets 2017-08-24 18:21:09 12sortable blue,shirt,brown,skirt,white,shoes,black,jeans&lt;br /&gt;
setstate widgets 2017-08-24 18:48:17 13sortable-strict blue,white,shirt,shoes,brown,skirt,black,jeans&lt;br /&gt;
setstate widgets 2017-08-24 18:49:34 14sortable-given blue,shirt,white,shoes,brown,skirt,black,jeans&lt;br /&gt;
setstate widgets 2024-03-19 20:11:06 15uzsuToggle OFF&lt;br /&gt;
setstate widgets 2024-03-19 20:10:53 16uzsuSelect brown&lt;br /&gt;
setstate widgets 2024-03-19 20:11:16 17uzsuSelectRadio UKW&lt;br /&gt;
setstate widgets 2024-03-19 20:11:09 18uzsuDropDown blue&lt;br /&gt;
setstate widgets 2017-10-07 07:56:43 19colorpicker_BRI 54&lt;br /&gt;
setstate widgets 2017-10-07 07:56:31 19colorpicker_CT 5100&lt;br /&gt;
setstate widgets 2017-10-07 07:59:26 19colorpicker_HSV 208c20&lt;br /&gt;
setstate widgets 2017-10-07 07:56:29 19colorpicker_HUE 94&lt;br /&gt;
setstate widgets 2017-10-07 07:53:01 19colorpicker_RGB c95918&lt;br /&gt;
setstate widgets 2017-08-24 19:03:20 20time 12:00&lt;br /&gt;
setstate widgets 2021-09-27 18:54:56 21iconRadio 90&lt;br /&gt;
setstate widgets 2021-09-27 03:19:18 21iconRadio_use4icon 40&lt;br /&gt;
setstate widgets 2024-03-20 18:55:18 22iconSwitch half closed&lt;br /&gt;
setstate widgets 2024-03-20 18:55:18 23iconLabel half closed&lt;br /&gt;
setstate widgets 2021-09-27 18:57:53 24iconButtons Büro,Schlafzimmer&lt;br /&gt;
setstate widgets 2017-10-25 08:20:05 24iconButtons_use4icon Bad,Wohnzimmer_2,Diele,Büro,Werkstatt&lt;br /&gt;
setstate widgets 2024-03-19 20:07:03 25bitfield 25&lt;br /&gt;
setstate widgets 2024-04-11 19:25:52 26widgetList Küche,23&lt;br /&gt;
setstate widgets 2021-09-27 19:02:25 state iconSwitch wide#open&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beigesteuerte Widgets ==&lt;br /&gt;
[[Datei:datetimepicker.png|mini|rechts]]&lt;br /&gt;
* Forenthread zu {{Link2Forum|Topic=35736|LinkText= DateTimePicker Widget}}&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;[[Datei:Controlminidash.png|mini|rechts]]&lt;br /&gt;
* Wiki Seite zu [[FHEMWEB/ControlMiniDash| ControlMiniDash Widget]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;[[Datei:Minichart flex mobile.png|mini|rechts]]&lt;br /&gt;
* Wiki Seite zu [[FHEMWEB/MiniChart| MiniChart Widget]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;[[Datei:AnimatedSVG.gif|mini|rechts]]&lt;br /&gt;
* Wiki Seite zu [[FHEMWEB/AnimatedSVG| AnimatedSVG Widget]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:Glossary]]&lt;br /&gt;
[[Kategorie:HOWTOS]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Datei:AnimatedSVG.gif&amp;diff=40542</id>
		<title>Datei:AnimatedSVG.gif</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Datei:AnimatedSVG.gif&amp;diff=40542"/>
		<updated>2025-12-14T12:43:04Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;AnimatedSVG&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/Widgets&amp;diff=40540</id>
		<title>FHEMWEB/Widgets</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/Widgets&amp;diff=40540"/>
		<updated>2025-12-14T10:12:47Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Widgets sind Frontendelemente, die der Dateneingabe und -anzeige dienen. Die hier gezeigten Frontendelemente werden durch [[FHEMWEB]] ([[PGM2]]) bereitgestellt.&lt;br /&gt;
&lt;br /&gt;
== Syntax ==&lt;br /&gt;
Die Syntax der Widgets ist unter dem Attribut [https://fhem.de/commandref_DE.html#widgetOverride widgetOverride] in der Befehlreferenz beschrieben.&lt;br /&gt;
&lt;br /&gt;
== Verwendung ==&lt;br /&gt;
Viele Module (u.a. [[DOIF]], [[dummy]], [[MQTT_DEVICE]], [[MQTT2_DEVICE]], [[MYSENSORS_DEVICE]], [[readingsGroup]]) bieten die Möglichkeit eine Benutzerschnittstelle zu erstellen.&lt;br /&gt;
&lt;br /&gt;
Widgets werden über &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;:&amp;lt;modifier&amp;gt;&amp;lt;/code&amp;gt;-Paare an [[Readings]] oder [[Attribute]] gebunden, dabei ist:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;&amp;lt;/code&amp;gt; ein Readingsname, ein Attribut oder ein Mapping-Argument.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;modifier&amp;gt;&amp;lt;/code&amp;gt; ein Widget einschliesslich Parameter.&lt;br /&gt;
&lt;br /&gt;
Bei der Erstellung spielen verschiedene Attribute zusammen:&lt;br /&gt;
&lt;br /&gt;
* Mit readingList (bzw. bei Modulen, die dies nicht kennen: widgetOverride) ist es möglich den Set-Befehl auf [[Readings]] zu erweitern, siehe [https://fhem.de/commandref_DE.html#readingList readingList].&lt;br /&gt;
* Mit [[setList]] (bzw. widgetOverride) werden die Widgets einem Reading zugeordnet und parametrisiert, siehe [https://fhem.de/commandref_DE.html#setList setList].&lt;br /&gt;
* Mit [[webCmd]] werden die Widgets angezeigt, siehe [https://fhem.de/commandref_DE.html#setList webCmd].&lt;br /&gt;
* Mit webCmdLabel werden die Widgets beschriftet und tabellarisch angeordnet, siehe [https://fhem.de/commandref_DE.html#webCmdLabel webCmdLabel].&lt;br /&gt;
* Mit widgetOverride ist es möglich, die im Modul festgelegten Widgets zu überschreiben, siehe [https://fhem.de/commandref_DE.html#widgetOverride widgetOverride].&lt;br /&gt;
* Mit [[eventMap]] ist es möglich Widgets an Mapping-Argumente zu binden, siehe [https://fhem.de/commandref_DE.html#eventMap eventMap] und {{Link2Forum|Topic=76755|LinkText=eventMap und Widget-Anbindung}}.&lt;br /&gt;
Das Zusammenspiel dieser Attribute ist u.A. im Artikel [[DeviceOverview anpassen#webCmd .26 Co|DeviceOverview anpassen]] näher erläutert.&lt;br /&gt;
&lt;br /&gt;
== Zusammenstellung der Widgets ==&lt;br /&gt;
[[Datei:fhemweb_widgets_1.png|mini|rechts]]&lt;br /&gt;
Die Bildschirmkopie zeigt das Benutzerinterface des Dummys mit den Widgets&lt;br /&gt;
&lt;br /&gt;
=== weitere Konfiguration ===&lt;br /&gt;
* &#039;&#039;&#039;knob&#039;&#039;&#039;, siehe http://anthonyterrien.com/knob/&lt;br /&gt;
* &#039;&#039;&#039;colorpicker&#039;&#039;&#039;, siehe [[Color]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
== Das Beispiel für [[DOIF/Import_von_Code_Snippets|Raw definition]] zum Ausprobieren ==&lt;br /&gt;
In der vorstehenden Abbildung fehlt das Look and Feel der Widgets. Mit dem folgenden Code-Snippet zum Ausprobieren, wird dies ergänzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod widgets dummy&lt;br /&gt;
attr widgets readingList 00select 01select 02selectnumbers 03selectnumbers 04textField 05textFieldNL 06textField-long 07textFieldNL-long 08slider 09multiple 10multiple-strict 11knob 12sortable 13sortable-strict 14sortable-given 15uzsuToggle 16uzsuSelect 17uzsuSelectRadio 18uzsuDropDown 19colorpicker_RGB 19colorpicker_HSV 19colorpicker_CT 19colorpicker_HUE 19colorpicker_BRI 20time 21iconRadio 21iconRadio_use4icon 22iconSwitch 23iconLabel 24iconButtons 24iconButtons_use4icon 25bitfield 26widgetList&lt;br /&gt;
attr widgets room 0_Test&lt;br /&gt;
attr widgets setList 00select:1,2,3,4,5,0,a,b,c,def,hik \&lt;br /&gt;
01select:select,1,2,3,4,5,0,a,b,c,def,hik \&lt;br /&gt;
02selectnumbers:selectnumbers,-30,3,33,1,lin \&lt;br /&gt;
03selectnumbers:selectnumbers,1,0.0625,140000,0,log10 \&lt;br /&gt;
04textField:textField \&lt;br /&gt;
05textFieldNL:textFieldNL\&lt;br /&gt;
06textField-long:textField-long,87 \&lt;br /&gt;
07textFieldNL-long:textFieldNL-long,87 \&lt;br /&gt;
08slider:slider,7,0.5,30,1 \&lt;br /&gt;
09multiple:multiple,shirt,shoes,skirt,trowsers \&lt;br /&gt;
10multiple-strict:multiple-strict,red,green,blue,yellow \&lt;br /&gt;
11knob:knob,min:0,max:360,width:50,height:50,step:1,lineCap:round,angleOffset:180,cursor:3,thickness:.3 \&lt;br /&gt;
12sortable:sortable,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
13sortable-strict:sortable-strict,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
14sortable-given:sortable-given,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
15uzsuToggle:uzsuToggle,ON,OFF\&lt;br /&gt;
16uzsuSelect:uzsuSelect,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
17uzsuSelectRadio:uzsuSelectRadio,MW,KW,UKW\&lt;br /&gt;
18uzsuDropDown:uzsuDropDown,red,green,blue,yellow \&lt;br /&gt;
19colorpicker_RGB:colorpicker,RGB \&lt;br /&gt;
19colorpicker_HSV:colorpicker,HSV \&lt;br /&gt;
19colorpicker_CT:colorpicker,CT,2000,10,6500 \&lt;br /&gt;
19colorpicker_HUE:colorpicker,HUE,0,1,359 \&lt;br /&gt;
19colorpicker_BRI:colorpicker,BRI,0,1,100 \&lt;br /&gt;
20time:time \&lt;br /&gt;
21iconRadio:iconRadio,#808080,10,fts_shutter_10@FFA500,20,fts_shutter_20@orange,30,fts_shutter_30@orange,40,fts_shutter_40@orange,50,fts_shutter_50@orange,60,fts_shutter_60@orange,70,fts_shutter_70@orange,80,fts_shutter_80@orange,90,fts_shutter_90@orange,100,fts_shutter_100@orange \&lt;br /&gt;
21iconRadio_use4icon:iconRadio,use4icon@808080,10,fts_shutter_10@FFA500,20,fts_shutter_20@orange,30,fts_shutter_30@orange,40,fts_shutter_40@orange,50,fts_shutter_50@orange,60,fts_shutter_60@orange,70,fts_shutter_70@orange,80,fts_shutter_80@orange,90,fts_shutter_90@orange,100,fts_shutter_100@orange \&lt;br /&gt;
22iconSwitch:iconSwitch,wide#open,@red,half#open,fts_shutter_50@%23cd2906,totally#closed,fts_shutter_100@magenta,half#closed, \&lt;br /&gt;
23iconLabel:iconLabel,wide#open,@red,half#open,fts_shutter_50@%23cd2906,totally#closed,fts_shutter_100@magenta,half#closed,\&lt;br /&gt;
24iconButtons:iconButtons,orange,Bad,sani_heating@green,Wohnzimmer_1,sani_heating@green,Wohnzimmer_2,sani_heating@green,Esszimmer,sani_heating@green,Diele,sani_heating@green,Gäste_WC,sani_heating@green,Büro,sani_heating@green,Wintergarten,sani_heating@green,Werkstatt,sani_heating@green,Schlafzimmer,sani_heating@green \&lt;br /&gt;
24iconButtons_use4icon:iconButtons,use4icon@red,Bad,sani_heating@0000FF,Wohnzimmer_1,sani_heating@blue,Wohnzimmer_2,sani_heating@blue,Esszimmer,sani_heating@blue,Diele,sani_heating@blue,Gäste_WC,sani_heating@blue,Büro,sani_heating@blue,Wintergarten,sani_heating@blue,Werkstatt,sani_heating@blue,Schlafzimmer,sani_heating@0000FF\&lt;br /&gt;
25bitfield:bitfield\&lt;br /&gt;
26widgetList:widgetList,4,select,Bad,Küche,Wohnzimmer,6,selectnumbers,7,1,30,0,lin&lt;br /&gt;
attr widgets userReadings 23iconLabel:22iconSwitch.* {ReadingsVal($name,&amp;quot;22iconSwitch&amp;quot;,&amp;quot;wideopen&amp;quot;)}&lt;br /&gt;
attr widgets webCmd 00select:01select:02selectnumbers:03selectnumbers:04textField:05textFieldNL:06textField-long:07textFieldNL-long:08slider:20time:09multiple:10multiple-strict:11knob:12sortable:13sortable-strict:14sortable-given:15uzsuToggle:16uzsuSelect:17uzsuSelectRadio:18uzsuDropDown:19colorpicker_RGB:19colorpicker_HSV:19colorpicker_CT:19colorpicker_HUE:19colorpicker_BRI:21iconRadio:21iconRadio_use4icon:24iconButtons:24iconButtons_use4icon:22iconSwitch:23iconLabel:25bitfield:26widgetList&lt;br /&gt;
attr widgets webCmdLabel select (default)\&lt;br /&gt;
:select (explicit)\&lt;br /&gt;
:selectnumbers (linear)\&lt;br /&gt;
:selectnumbers (logarithmic)\&lt;br /&gt;
:textField\&lt;br /&gt;
:textFieldNL (no label)\&lt;br /&gt;
:textField-long (multi-line)\&lt;br /&gt;
:textFieldNL-long (multi-line,no label)\&lt;br /&gt;
:slider\&lt;br /&gt;
:time\&lt;br /&gt;
:multiple\&lt;br /&gt;
:multiple-strict\&lt;br /&gt;
:knob\&lt;br /&gt;
:sortable\&lt;br /&gt;
:sortable-strict\&lt;br /&gt;
:sortable-given\&lt;br /&gt;
:uzsuToggle\&lt;br /&gt;
:uzsuSelect\&lt;br /&gt;
:uzsuSelectRadio\&lt;br /&gt;
:uzsuDropDown\&lt;br /&gt;
:colorpicker RGB\&lt;br /&gt;
:colorpicker HSV\&lt;br /&gt;
:colorpicker Farbtemperatur\&lt;br /&gt;
:colorpicker Farbton\&lt;br /&gt;
:colorpicker Helligkeit\&lt;br /&gt;
:iconRadio\&lt;br /&gt;
:iconRadio prefix use4icon\&lt;br /&gt;
:iconButtons\&lt;br /&gt;
:iconButtons prefix use4icon\&lt;br /&gt;
:iconSwitch\&lt;br /&gt;
:iconLabel\&lt;br /&gt;
:bitfield\&lt;br /&gt;
:widgetList (Raum Temperatur)&lt;br /&gt;
attr widgets widgetOverride readingList|setList|webCmd|webCmdLabel|widgetOverride:textField-long,86&lt;br /&gt;
&lt;br /&gt;
setstate widgets iconSwitch wide#open&lt;br /&gt;
setstate widgets 2024-03-19 20:18:38 00select 1&lt;br /&gt;
setstate widgets 2021-09-27 18:52:48 01select 0&lt;br /&gt;
setstate widgets 2021-09-27 18:52:33 02selectnumbers 0.0&lt;br /&gt;
setstate widgets 2021-09-27 12:13:35 03selectnumbers 64938&lt;br /&gt;
setstate widgets 2023-01-03 14:32:14 04textField Hello&lt;br /&gt;
setstate widgets 2017-08-24 17:39:22 05textField Hallo&lt;br /&gt;
setstate widgets 2023-01-03 14:47:53 05textFieldNL Hello&lt;br /&gt;
setstate widgets 2023-01-08 19:25:46 06textField-long Hello&lt;br /&gt;
setstate widgets 2022-11-28 18:23:30 07textFieldNL-long Hello&lt;br /&gt;
setstate widgets 2017-08-24 18:22:07 08slider 17.0&lt;br /&gt;
setstate widgets 2017-08-24 18:22:40 09multiple shirt,shoes&lt;br /&gt;
setstate widgets 2017-08-24 18:22:57 10multiple-strict red,green&lt;br /&gt;
setstate widgets 2021-09-27 03:19:35 11knob 207&lt;br /&gt;
setstate widgets 2017-08-24 18:21:09 12sortable blue,shirt,brown,skirt,white,shoes,black,jeans&lt;br /&gt;
setstate widgets 2017-08-24 18:48:17 13sortable-strict blue,white,shirt,shoes,brown,skirt,black,jeans&lt;br /&gt;
setstate widgets 2017-08-24 18:49:34 14sortable-given blue,shirt,white,shoes,brown,skirt,black,jeans&lt;br /&gt;
setstate widgets 2024-03-19 20:11:06 15uzsuToggle OFF&lt;br /&gt;
setstate widgets 2024-03-19 20:10:53 16uzsuSelect brown&lt;br /&gt;
setstate widgets 2024-03-19 20:11:16 17uzsuSelectRadio UKW&lt;br /&gt;
setstate widgets 2024-03-19 20:11:09 18uzsuDropDown blue&lt;br /&gt;
setstate widgets 2017-10-07 07:56:43 19colorpicker_BRI 54&lt;br /&gt;
setstate widgets 2017-10-07 07:56:31 19colorpicker_CT 5100&lt;br /&gt;
setstate widgets 2017-10-07 07:59:26 19colorpicker_HSV 208c20&lt;br /&gt;
setstate widgets 2017-10-07 07:56:29 19colorpicker_HUE 94&lt;br /&gt;
setstate widgets 2017-10-07 07:53:01 19colorpicker_RGB c95918&lt;br /&gt;
setstate widgets 2017-08-24 19:03:20 20time 12:00&lt;br /&gt;
setstate widgets 2021-09-27 18:54:56 21iconRadio 90&lt;br /&gt;
setstate widgets 2021-09-27 03:19:18 21iconRadio_use4icon 40&lt;br /&gt;
setstate widgets 2024-03-20 18:55:18 22iconSwitch half closed&lt;br /&gt;
setstate widgets 2024-03-20 18:55:18 23iconLabel half closed&lt;br /&gt;
setstate widgets 2021-09-27 18:57:53 24iconButtons Büro,Schlafzimmer&lt;br /&gt;
setstate widgets 2017-10-25 08:20:05 24iconButtons_use4icon Bad,Wohnzimmer_2,Diele,Büro,Werkstatt&lt;br /&gt;
setstate widgets 2024-03-19 20:07:03 25bitfield 25&lt;br /&gt;
setstate widgets 2024-04-11 19:25:52 26widgetList Küche,23&lt;br /&gt;
setstate widgets 2021-09-27 19:02:25 state iconSwitch wide#open&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beigesteuerte Widgets ==&lt;br /&gt;
[[Datei:datetimepicker.png|mini|rechts]]&lt;br /&gt;
* Forenthread zu {{Link2Forum|Topic=35736|LinkText= DateTimePicker Widget}}&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;[[Datei:Controlminidash.png|mini|rechts]]&lt;br /&gt;
* Wiki Seite zu [[FHEMWEB/ControlMiniDash| ControlMiniDash Widget]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;[[Datei:Minichart flex mobile.png|mini|rechts]]&lt;br /&gt;
* Wiki Seite zu [[FHEMWEB/MiniChart| MiniChart Widget]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:Glossary]]&lt;br /&gt;
[[Kategorie:HOWTOS]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/ControlMiniDash&amp;diff=40539</id>
		<title>FHEMWEB/ControlMiniDash</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/ControlMiniDash&amp;diff=40539"/>
		<updated>2025-12-09T20:10:07Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Controlminidash.png|Mini|rechts]]&lt;br /&gt;
== ControlMiniDash – kompaktes Dashboard-Widget ==&lt;br /&gt;
&lt;br /&gt;
Dieses Widget stellt ein kompaktes Dashboard bereit, das direkt in einem Device im [[FHEMWEB]] ([[PGM2]]) Frontend &lt;br /&gt;
definiert werden kann. Es wurde ursprünglich für ein Klimaanlagen-Setup entwickelt und dient als flexibles, &lt;br /&gt;
visuelles Bedienelement für Geräte wie z. B. den HmIP-WTH-2.&lt;br /&gt;
&lt;br /&gt;
Das Widget besteht aus zentralen Informationstexten, einem farbigen Außenring, einem Sliderbutton für die&lt;br /&gt;
Solltemperatur sowie bis zu sechs frei definierbaren Buttons.&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=142831|LinkText=ControlMiniDash Widget}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Zentrale Textelemente ====&lt;br /&gt;
Vier Textelemente werden in der Mitte angezeigt:&lt;br /&gt;
&lt;br /&gt;
* Gemessene Temperatur&lt;br /&gt;
* Luftfeuchtigkeit&lt;br /&gt;
* Solltemperatur&lt;br /&gt;
* Freitextfeld / Statusfeld&lt;br /&gt;
&lt;br /&gt;
==== Interaktion ====&lt;br /&gt;
* Der runde Button auf dem Außenring ermöglicht das direkte Erhöhen oder Verringern der Solltemperatur.&lt;br /&gt;
* Der Außenring ändert seine Farbe abhängig von der aktuellen Temperatur (7 °C bis 30 °C).&lt;br /&gt;
* Links (1–3) und rechts (4–6) können jeweils drei frei belegbare Buttons definiert werden.&lt;br /&gt;
* Für die Buttons können sowohl Data-URL-SVG als auch FHEM-SVG-Icons verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Einrichtung im Device ===&lt;br /&gt;
&lt;br /&gt;
Das Widget wird über &amp;lt;code&amp;gt;webCmd&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;widgetOverride&amp;lt;/code&amp;gt; aktiviert.&lt;br /&gt;
&lt;br /&gt;
Beispiel ohne Buttons:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
 attr &amp;lt;device&amp;gt; webCmd climacontrol&lt;br /&gt;
 attr &amp;lt;device&amp;gt; widgetOverride climacontrol:controlminidash,measured-temp,humidity,desired-temp,state,#,#,#,#,#,#&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel mit FhemSVG-Buttons:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
 attr &amp;lt;device&amp;gt; webCmd climacontrol&lt;br /&gt;
 attr &amp;lt;device&amp;gt; widgetOverride climacontrol:controlminidash,measured-temp,humidity,desired-temp,state,sani_heating_manual@controlMode.manual,rc_MINUS@temp-5,sani_heating_boost@boost,sani_heating_automatic@controlMode.automatic,rc_PLUS@temp+5,sani_heating_mode@night_day&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn im Device keine Luftfeuchte/Humidity vorhanden ist, kann diese mit # an der dritten Position ausgeblendet werden.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
 attr HmIP_KLHR_STHD webCmd controlMD auto:manu:boost:on:off&lt;br /&gt;
 attr HmIP_KLHR_STHD widgetOverride controlMD:controlminidash,measured-temp,#,desired-temp,HEATING_COOLING,#,#,#,#,#,#&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Parameterübersicht ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Abschnitt&lt;br /&gt;
! Bedeutung&lt;br /&gt;
! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;lt;beliebigerName&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
| webCmd-Zuweisung&lt;br /&gt;
| Gibt an, welches &amp;lt;code&amp;gt;webCmd&amp;lt;/code&amp;gt; das Widget repräsentiert.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;controlminidash&amp;lt;/code&amp;gt;&lt;br /&gt;
| Widget-Typ&lt;br /&gt;
| Aktiviert das Dashboard-Widget.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;measured-temp&amp;lt;/code&amp;gt;&lt;br /&gt;
| Reading&lt;br /&gt;
| Anzeige der gemessenen Temperatur.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;humidity&amp;lt;/code&amp;gt;&lt;br /&gt;
| Reading&lt;br /&gt;
| Anzeige der Luftfeuchtigkeit. Nicht vorhanden → mit &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt; deaktivieren.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;desired-temp&amp;lt;/code&amp;gt;&lt;br /&gt;
| Reading / Setter&lt;br /&gt;
| Anzeige und Steuerung der Solltemperatur.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;state&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeige eines Status- oder Textfelds.&lt;br /&gt;
| Das Reading ist frei wählbar. &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;btnXIcon@btnXCmd &amp;lt;/code&amp;gt;&lt;br /&gt;
| Button-Attribute&lt;br /&gt;
| Individuelle Definition von Icon und Befehl für die sechs möglichen Buttons.&lt;br /&gt;
 Beispiel:sani_heating_automatic@controlMode.automatic&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;&lt;br /&gt;
| Button ausblenden&lt;br /&gt;
| Beispiel:  &lt;br /&gt;
  &amp;lt;code&amp;gt;climacontrol:controlminidash,measured-temp,humidity,desired-temp,state,#,#,#,#,#,#&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Individualisierung per userattr ===&lt;br /&gt;
  &lt;br /&gt;
Damit können Fhemcmd&#039;s, Farbe und Icon für jeden der sechs Buttons separat konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel-Definition:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
 defmod widgetControlUserAttr dummy&lt;br /&gt;
 attr widgetControlUserAttr userattr btn1Cmd btn1Color btn1Icon btn2Cmd btn2Color btn2Icon btn3Cmd btn3Color btn3Icon btn4Cmd btn4Color btn4Icon btn5Cmd btn5Color btn5Icon btn6Cmd btn6Color btn6Icon&lt;br /&gt;
 attr widgetControlUserAttr btn1Cmd   set widgetControlUserAttr state cooling&lt;br /&gt;
 attr widgetControlUserAttr btn1Color blue&lt;br /&gt;
 attr widgetControlUserAttr btn1Icon  data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns...&lt;br /&gt;
 attr widgetControlUserAttr btn2Cmd   { fhem(&amp;quot;set widgetControlUserAttr desired-temp &amp;quot;.(ReadingsVal(&amp;quot;widgetControlUserAttr&amp;quot;,&amp;quot;desired-temp&amp;quot;,0)-0.5)) }&lt;br /&gt;
 attr widgetControlUserAttr btn2Color blue&lt;br /&gt;
 attr widgetControlUserAttr btn2Icon  data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns...&lt;br /&gt;
 attr widgetControlUserAttr btn3Cmd   set widgetControlUserAttr state off&lt;br /&gt;
 attr widgetControlUserAttr btn3Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr btn4Cmd   set widgetControlUserAttr state heating&lt;br /&gt;
 attr widgetControlUserAttr btn4Color red&lt;br /&gt;
 attr widgetControlUserAttr btn4Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr btn5Cmd   { fhem(&amp;quot;set widgetControlUserAttr desired-temp &amp;quot;.(ReadingsVal(&amp;quot;widgetControlUserAttr&amp;quot;,&amp;quot;desired-temp&amp;quot;,0)+0.5)) }&lt;br /&gt;
 attr widgetControlUserAttr btn5Color red&lt;br /&gt;
 attr widgetControlUserAttr btn5Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr btn6Cmd   set widgetControlUserAttr state on&lt;br /&gt;
 attr widgetControlUserAttr btn6Color green&lt;br /&gt;
 attr widgetControlUserAttr btn6Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr readingList measured-temp humidity desired-temp state&lt;br /&gt;
 attr widgetControlUserAttr room Test&lt;br /&gt;
 attr widgetControlUserAttr setList measured-temp humidity desired-temp state&lt;br /&gt;
 attr widgetControlUserAttr subType Thermostat&lt;br /&gt;
 attr widgetControlUserAttr webCmd climacontrol&lt;br /&gt;
 attr widgetControlUserAttr widgetOverride climacontrol:controlminidash,measured-temp,humidity,desired-temp,state,#,#,#,#,#,#&lt;br /&gt;
 setstate widgetControlUserAttr heating&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:51:37 desired-temp 20.0&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:42:42 humidity 56&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:42:48 measured-temp 19&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:51:21 state heating&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Mit DOIF diverse Widgets in einem Table zusammenfassen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
 defmod di_widget DOIF ##&lt;br /&gt;
 attr di_widget room Test&lt;br /&gt;
 attr di_widget uiTable {package ui_Table;;\&lt;br /&gt;
 $_param=&#039;controlminidash,measured-temp,humidity,desired-temp,state,sani_heating_manual@controlMode.manual,rc_MINUS@temp-5,sani_heating_boost@boost,sani_heating_automatic@controlMode.automatic,rc_PLUS@temp+5,sani_heating_mode@night_day&#039;;;\&lt;br /&gt;
 $_set=&amp;quot;set desired-temp&amp;quot;;;\&lt;br /&gt;
 }\&lt;br /&gt;
 widget([TH_DG_HM:desired-temp],$_param,$_set)|\&lt;br /&gt;
 widget([TH_Kz_w_HM:desired-temp],$_param,$_set)\&lt;br /&gt;
 widget([TH_Kz_o_HM:desired-temp],$_param,$_set)|\&lt;br /&gt;
 widget([TH_Kueche_HM:desired-temp],$_param,$_set)\&lt;br /&gt;
 widget([TH_Keller_HM:desired-temp],$_param,$_set)|\&lt;br /&gt;
 widget([TH_Bad_HM:desired-temp],$_param,$_set)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/ControlMiniDash&amp;diff=40538</id>
		<title>FHEMWEB/ControlMiniDash</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/ControlMiniDash&amp;diff=40538"/>
		<updated>2025-12-09T20:05:18Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Controlminidash.png|Mini|rechts]]&lt;br /&gt;
== ControlMiniDash – kompaktes Dashboard-Widget ==&lt;br /&gt;
&lt;br /&gt;
Dieses Widget stellt ein kompaktes Dashboard bereit, das direkt in einem Device im [[FHEMWEB]] ([[PGM2]]) Frontend &lt;br /&gt;
definiert werden kann. Es wurde ursprünglich für ein Klimaanlagen-Setup entwickelt und dient als flexibles, &lt;br /&gt;
visuelles Bedienelement für Geräte wie z. B. den HmIP-WTH-2.&lt;br /&gt;
&lt;br /&gt;
Das Widget besteht aus zentralen Informationstexten, einem farbigen Außenring, einem Sliderbutton für die&lt;br /&gt;
Solltemperatur sowie bis zu sechs frei definierbaren Buttons.&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum [https://forum.fhem.de/index.php?topic=142831.0 ControlMiniDash Widget]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Zentrale Textelemente ====&lt;br /&gt;
Vier Textelemente werden in der Mitte angezeigt:&lt;br /&gt;
&lt;br /&gt;
* Gemessene Temperatur&lt;br /&gt;
* Luftfeuchtigkeit&lt;br /&gt;
* Solltemperatur&lt;br /&gt;
* Freitextfeld / Statusfeld&lt;br /&gt;
&lt;br /&gt;
==== Interaktion ====&lt;br /&gt;
* Der runde Button auf dem Außenring ermöglicht das direkte Erhöhen oder Verringern der Solltemperatur.&lt;br /&gt;
* Der Außenring ändert seine Farbe abhängig von der aktuellen Temperatur (7 °C bis 30 °C).&lt;br /&gt;
* Links (1–3) und rechts (4–6) können jeweils drei frei belegbare Buttons definiert werden.&lt;br /&gt;
* Für die Buttons können sowohl Data-URL-SVG als auch FHEM-SVG-Icons verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Einrichtung im Device ===&lt;br /&gt;
&lt;br /&gt;
Das Widget wird über &amp;lt;code&amp;gt;webCmd&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;widgetOverride&amp;lt;/code&amp;gt; aktiviert.&lt;br /&gt;
&lt;br /&gt;
Beispiel ohne Buttons:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
 attr &amp;lt;device&amp;gt; webCmd climacontrol&lt;br /&gt;
 attr &amp;lt;device&amp;gt; widgetOverride climacontrol:controlminidash,measured-temp,humidity,desired-temp,state,#,#,#,#,#,#&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel mit FhemSVG-Buttons:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
 attr &amp;lt;device&amp;gt; webCmd climacontrol&lt;br /&gt;
 attr &amp;lt;device&amp;gt; widgetOverride climacontrol:controlminidash,measured-temp,humidity,desired-temp,state,sani_heating_manual@controlMode.manual,rc_MINUS@temp-5,sani_heating_boost@boost,sani_heating_automatic@controlMode.automatic,rc_PLUS@temp+5,sani_heating_mode@night_day&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn im Device keine Luftfeuchte/Humidity vorhanden ist, kann diese mit # an der dritten Position ausgeblendet werden.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
 attr HmIP_KLHR_STHD webCmd controlMD auto:manu:boost:on:off&lt;br /&gt;
 attr HmIP_KLHR_STHD widgetOverride controlMD:controlminidash,measured-temp,#,desired-temp,HEATING_COOLING,#,#,#,#,#,#&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Parameterübersicht ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Abschnitt&lt;br /&gt;
! Bedeutung&lt;br /&gt;
! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;lt;beliebigerName&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
| webCmd-Zuweisung&lt;br /&gt;
| Gibt an, welches &amp;lt;code&amp;gt;webCmd&amp;lt;/code&amp;gt; das Widget repräsentiert.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;controlminidash&amp;lt;/code&amp;gt;&lt;br /&gt;
| Widget-Typ&lt;br /&gt;
| Aktiviert das Dashboard-Widget.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;measured-temp&amp;lt;/code&amp;gt;&lt;br /&gt;
| Reading&lt;br /&gt;
| Anzeige der gemessenen Temperatur.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;humidity&amp;lt;/code&amp;gt;&lt;br /&gt;
| Reading&lt;br /&gt;
| Anzeige der Luftfeuchtigkeit. Nicht vorhanden → mit &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt; deaktivieren.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;desired-temp&amp;lt;/code&amp;gt;&lt;br /&gt;
| Reading / Setter&lt;br /&gt;
| Anzeige und Steuerung der Solltemperatur.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;state&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeige eines Status- oder Textfelds.&lt;br /&gt;
| Das Reading ist frei wählbar. &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;btnXIcon@btnXCmd &amp;lt;/code&amp;gt;&lt;br /&gt;
| Button-Attribute&lt;br /&gt;
| Individuelle Definition von Icon und Befehl für die sechs möglichen Buttons.&lt;br /&gt;
 Beispiel:sani_heating_automatic@controlMode.automatic&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;&lt;br /&gt;
| Button ausblenden&lt;br /&gt;
| Beispiel:  &lt;br /&gt;
  &amp;lt;code&amp;gt;climacontrol:controlminidash,measured-temp,humidity,desired-temp,state,#,#,#,#,#,#&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Individualisierung per userattr ===&lt;br /&gt;
  &lt;br /&gt;
Damit können Fhemcmd&#039;s, Farbe und Icon für jeden der sechs Buttons separat konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel-Definition:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
 defmod widgetControlUserAttr dummy&lt;br /&gt;
 attr widgetControlUserAttr userattr btn1Cmd btn1Color btn1Icon btn2Cmd btn2Color btn2Icon btn3Cmd btn3Color btn3Icon btn4Cmd btn4Color btn4Icon btn5Cmd btn5Color btn5Icon btn6Cmd btn6Color btn6Icon&lt;br /&gt;
 attr widgetControlUserAttr btn1Cmd   set widgetControlUserAttr state cooling&lt;br /&gt;
 attr widgetControlUserAttr btn1Color blue&lt;br /&gt;
 attr widgetControlUserAttr btn1Icon  data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns...&lt;br /&gt;
 attr widgetControlUserAttr btn2Cmd   { fhem(&amp;quot;set widgetControlUserAttr desired-temp &amp;quot;.(ReadingsVal(&amp;quot;widgetControlUserAttr&amp;quot;,&amp;quot;desired-temp&amp;quot;,0)-0.5)) }&lt;br /&gt;
 attr widgetControlUserAttr btn2Color blue&lt;br /&gt;
 attr widgetControlUserAttr btn2Icon  data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns...&lt;br /&gt;
 attr widgetControlUserAttr btn3Cmd   set widgetControlUserAttr state off&lt;br /&gt;
 attr widgetControlUserAttr btn3Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr btn4Cmd   set widgetControlUserAttr state heating&lt;br /&gt;
 attr widgetControlUserAttr btn4Color red&lt;br /&gt;
 attr widgetControlUserAttr btn4Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr btn5Cmd   { fhem(&amp;quot;set widgetControlUserAttr desired-temp &amp;quot;.(ReadingsVal(&amp;quot;widgetControlUserAttr&amp;quot;,&amp;quot;desired-temp&amp;quot;,0)+0.5)) }&lt;br /&gt;
 attr widgetControlUserAttr btn5Color red&lt;br /&gt;
 attr widgetControlUserAttr btn5Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr btn6Cmd   set widgetControlUserAttr state on&lt;br /&gt;
 attr widgetControlUserAttr btn6Color green&lt;br /&gt;
 attr widgetControlUserAttr btn6Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr readingList measured-temp humidity desired-temp state&lt;br /&gt;
 attr widgetControlUserAttr room Test&lt;br /&gt;
 attr widgetControlUserAttr setList measured-temp humidity desired-temp state&lt;br /&gt;
 attr widgetControlUserAttr subType Thermostat&lt;br /&gt;
 attr widgetControlUserAttr webCmd climacontrol&lt;br /&gt;
 attr widgetControlUserAttr widgetOverride climacontrol:controlminidash,measured-temp,humidity,desired-temp,state,#,#,#,#,#,#&lt;br /&gt;
 setstate widgetControlUserAttr heating&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:51:37 desired-temp 20.0&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:42:42 humidity 56&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:42:48 measured-temp 19&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:51:21 state heating&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Mit DOIF diverse Widgets in einem Table zusammenfassen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
 defmod di_widget DOIF ##&lt;br /&gt;
 attr di_widget room Test&lt;br /&gt;
 attr di_widget uiTable {package ui_Table;;\&lt;br /&gt;
 $_param=&#039;controlminidash,measured-temp,humidity,desired-temp,state,sani_heating_manual@controlMode.manual,rc_MINUS@temp-5,sani_heating_boost@boost,sani_heating_automatic@controlMode.automatic,rc_PLUS@temp+5,sani_heating_mode@night_day&#039;;;\&lt;br /&gt;
 $_set=&amp;quot;set desired-temp&amp;quot;;;\&lt;br /&gt;
 }\&lt;br /&gt;
 widget([TH_DG_HM:desired-temp],$_param,$_set)|\&lt;br /&gt;
 widget([TH_Kz_w_HM:desired-temp],$_param,$_set)\&lt;br /&gt;
 widget([TH_Kz_o_HM:desired-temp],$_param,$_set)|\&lt;br /&gt;
 widget([TH_Kueche_HM:desired-temp],$_param,$_set)\&lt;br /&gt;
 widget([TH_Keller_HM:desired-temp],$_param,$_set)|\&lt;br /&gt;
 widget([TH_Bad_HM:desired-temp],$_param,$_set)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/ControlMiniDash&amp;diff=40537</id>
		<title>FHEMWEB/ControlMiniDash</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/ControlMiniDash&amp;diff=40537"/>
		<updated>2025-12-09T20:03:46Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Controlminidash.png|Mini|rechts]]&lt;br /&gt;
== ControlMiniDash – kompaktes Dashboard-Widget ==&lt;br /&gt;
&lt;br /&gt;
Dieses Widget stellt ein kompaktes Dashboard bereit, das direkt in einem Device im [[FHEMWEB]] ([[PGM2]]) Frontend &lt;br /&gt;
definiert werden kann. Es wurde ursprünglich für ein Klimaanlagen-Setup entwickelt und dient als flexibles, &lt;br /&gt;
visuelles Bedienelement für Geräte wie z. B. den HmIP-WTH-2.&lt;br /&gt;
&lt;br /&gt;
Das Widget besteht aus zentralen Informationstexten, einem farbigen Außenring, einem Sliderbutton für die&lt;br /&gt;
Solltemperatur sowie bis zu sechs frei definierbaren Buttons.&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum [https://forum.fhem.de/index.php?topic=142831.0 ControlMiniDash Widget]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
==== Zentrale Textelemente ====&lt;br /&gt;
Vier Textelemente werden in der Mitte angezeigt:&lt;br /&gt;
&lt;br /&gt;
* Gemessene Temperatur&lt;br /&gt;
* Luftfeuchtigkeit&lt;br /&gt;
* Solltemperatur&lt;br /&gt;
* Freitextfeld / Statusfeld&lt;br /&gt;
&lt;br /&gt;
==== Interaktion ====&lt;br /&gt;
* Der runde Button auf dem Außenring ermöglicht das direkte Erhöhen oder Verringern der Solltemperatur.&lt;br /&gt;
* Der Außenring ändert seine Farbe abhängig von der aktuellen Temperatur (7 °C bis 30 °C).&lt;br /&gt;
* Links (1–3) und rechts (4–6) können jeweils drei frei belegbare Buttons definiert werden.&lt;br /&gt;
* Für die Buttons können sowohl Data-URL-SVG als auch FHEM-SVG-Icons verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Einrichtung im Device ===&lt;br /&gt;
&lt;br /&gt;
Das Widget wird über &amp;lt;code&amp;gt;webCmd&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;widgetOverride&amp;lt;/code&amp;gt; aktiviert.&lt;br /&gt;
&lt;br /&gt;
Beispiel ohne Buttons:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
 attr &amp;lt;device&amp;gt; webCmd climacontrol&lt;br /&gt;
 attr &amp;lt;device&amp;gt; widgetOverride climacontrol:controlminidash,measured-temp,humidity,desired-temp,state,#,#,#,#,#,#&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel mit FhemSVG-Buttons:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
 attr &amp;lt;device&amp;gt; webCmd climacontrol&lt;br /&gt;
 attr &amp;lt;device&amp;gt; widgetOverride climacontrol:controlminidash,measured-temp,humidity,desired-temp,state,sani_heating_manual@controlMode.manual,rc_MINUS@temp-5,sani_heating_boost@boost,sani_heating_automatic@controlMode.automatic,rc_PLUS@temp+5,sani_heating_mode@night_day&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn im Device keine Luftfeuchte/Humidity vorhanden ist, kann diese mit # an der dritten Position ausgeblendet werden.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
 attr HmIP_KLHR_STHD webCmd controlMD auto:manu:boost:on:off&lt;br /&gt;
 attr HmIP_KLHR_STHD widgetOverride controlMD:controlminidash,measured-temp,#,desired-temp,HEATING_COOLING,#,#,#,#,#,#&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Parameterübersicht ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Abschnitt&lt;br /&gt;
! Bedeutung&lt;br /&gt;
! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;lt;beliebigerName&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
| webCmd-Zuweisung&lt;br /&gt;
| Gibt an, welches &amp;lt;code&amp;gt;webCmd&amp;lt;/code&amp;gt; das Widget repräsentiert.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;controlminidash&amp;lt;/code&amp;gt;&lt;br /&gt;
| Widget-Typ&lt;br /&gt;
| Aktiviert das Dashboard-Widget.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;measured-temp&amp;lt;/code&amp;gt;&lt;br /&gt;
| Reading&lt;br /&gt;
| Anzeige der gemessenen Temperatur.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;humidity&amp;lt;/code&amp;gt;&lt;br /&gt;
| Reading&lt;br /&gt;
| Anzeige der Luftfeuchtigkeit. Nicht vorhanden → mit &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt; deaktivieren.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;desired-temp&amp;lt;/code&amp;gt;&lt;br /&gt;
| Reading / Setter&lt;br /&gt;
| Anzeige und Steuerung der Solltemperatur.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;state&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeige eines Status- oder Textfelds.&lt;br /&gt;
| Das Reading ist frei wählbar. &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;btnXIcon@btnXCmd &amp;lt;/code&amp;gt;&lt;br /&gt;
| Button-Attribute&lt;br /&gt;
| Individuelle Definition von Icon und Befehl für die sechs möglichen Buttons.&lt;br /&gt;
 Beispiel:sani_heating_automatic@controlMode.automatic&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;&lt;br /&gt;
| Button ausblenden&lt;br /&gt;
| Beispiel:  &lt;br /&gt;
  &amp;lt;code&amp;gt;climacontrol:controlminidash,measured-temp,humidity,desired-temp,state,#,#,#,#,#,#&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Individualisierung per userattr ===&lt;br /&gt;
  &lt;br /&gt;
Damit können Fhemcmd&#039;s, Farbe und Icon für jeden der sechs Buttons separat konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel-Definition:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
 defmod widgetControlUserAttr dummy&lt;br /&gt;
 attr widgetControlUserAttr userattr btn1Cmd btn1Color btn1Icon btn2Cmd btn2Color btn2Icon btn3Cmd btn3Color btn3Icon btn4Cmd btn4Color btn4Icon btn5Cmd btn5Color btn5Icon btn6Cmd btn6Color btn6Icon&lt;br /&gt;
 attr widgetControlUserAttr btn1Cmd   set widgetControlUserAttr state cooling&lt;br /&gt;
 attr widgetControlUserAttr btn1Color blue&lt;br /&gt;
 attr widgetControlUserAttr btn1Icon  data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns...&lt;br /&gt;
 attr widgetControlUserAttr btn2Cmd   { fhem(&amp;quot;set widgetControlUserAttr desired-temp &amp;quot;.(ReadingsVal(&amp;quot;widgetControlUserAttr&amp;quot;,&amp;quot;desired-temp&amp;quot;,0)-0.5)) }&lt;br /&gt;
 attr widgetControlUserAttr btn2Color blue&lt;br /&gt;
 attr widgetControlUserAttr btn2Icon  data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns...&lt;br /&gt;
 attr widgetControlUserAttr btn3Cmd   set widgetControlUserAttr state off&lt;br /&gt;
 attr widgetControlUserAttr btn3Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr btn4Cmd   set widgetControlUserAttr state heating&lt;br /&gt;
 attr widgetControlUserAttr btn4Color red&lt;br /&gt;
 attr widgetControlUserAttr btn4Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr btn5Cmd   { fhem(&amp;quot;set widgetControlUserAttr desired-temp &amp;quot;.(ReadingsVal(&amp;quot;widgetControlUserAttr&amp;quot;,&amp;quot;desired-temp&amp;quot;,0)+0.5)) }&lt;br /&gt;
 attr widgetControlUserAttr btn5Color red&lt;br /&gt;
 attr widgetControlUserAttr btn5Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr btn6Cmd   set widgetControlUserAttr state on&lt;br /&gt;
 attr widgetControlUserAttr btn6Color green&lt;br /&gt;
 attr widgetControlUserAttr btn6Icon  data:image/svg+xml...&lt;br /&gt;
 attr widgetControlUserAttr readingList measured-temp humidity desired-temp state&lt;br /&gt;
 attr widgetControlUserAttr room Test&lt;br /&gt;
 attr widgetControlUserAttr setList measured-temp humidity desired-temp state&lt;br /&gt;
 attr widgetControlUserAttr subType Thermostat&lt;br /&gt;
 attr widgetControlUserAttr webCmd climacontrol&lt;br /&gt;
 attr widgetControlUserAttr widgetOverride climacontrol:controlminidash,measured-temp,humidity,desired-temp,state,#,#,#,#,#,#&lt;br /&gt;
 setstate widgetControlUserAttr heating&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:51:37 desired-temp 20.0&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:42:42 humidity 56&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:42:48 measured-temp 19&lt;br /&gt;
 setstate widgetControlUserAttr 2025-11-30 11:51:21 state heating&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Mit DOIF diverse Widgets in einem Table zusammenfassen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
 defmod di_widget DOIF ##&lt;br /&gt;
 attr di_widget room Test&lt;br /&gt;
 attr di_widget uiTable {package ui_Table;;\&lt;br /&gt;
 $_param=&#039;controlminidash,measured-temp,humidity,desired-temp,state,sani_heating_manual@controlMode.manual,rc_MINUS@temp-5,sani_heating_boost@boost,sani_heating_automatic@controlMode.automatic,rc_PLUS@temp+5,sani_heating_mode@night_day&#039;;;\&lt;br /&gt;
 $_set=&amp;quot;set desired-temp&amp;quot;;;\&lt;br /&gt;
 }\&lt;br /&gt;
 widget([TH_DG_HM:desired-temp],$_param,$_set)|\&lt;br /&gt;
 widget([TH_Kz_w_HM:desired-temp],$_param,$_set)\&lt;br /&gt;
 widget([TH_Kz_o_HM:desired-temp],$_param,$_set)|\&lt;br /&gt;
 widget([TH_Kueche_HM:desired-temp],$_param,$_set)\&lt;br /&gt;
 widget([TH_Keller_HM:desired-temp],$_param,$_set)|\&lt;br /&gt;
 widget([TH_Bad_HM:desired-temp],$_param,$_set)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/MiniChart&amp;diff=40532</id>
		<title>FHEMWEB/MiniChart</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/MiniChart&amp;diff=40532"/>
		<updated>2025-12-07T18:13:14Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Minichart flex mobile.png]]&lt;br /&gt;
&lt;br /&gt;
== MiniChart – kompaktes Chart-Widget (Line &amp;amp; Bar) ==&lt;br /&gt;
&lt;br /&gt;
Das MiniChart-Widget visualisiert Messwertverläufe als kompaktes Liniendiagramm oder Balkendiagramm.&lt;br /&gt;
Zusätzlich zeigt es drei aktuelle Werte, die aus Label, Reading und Einheit bestehen. Zum Einrichten sind 3 Schritte notwendig.&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
Forenthread zum [https://forum.fhem.de/index.php?topic=141857.0 MiniChart Widget]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 1️⃣ 99_myUtils.pm – Hilfsfunktion zum Speichern von historischen Werten ==&lt;br /&gt;
&lt;br /&gt;
Zuerst wird eine Subroutine angelegt, die neue Werte an ein Fixed-Array anhängt und alte Werte entfernt, falls die maximale Größe überschritten wird.&lt;br /&gt;
&lt;br /&gt;
 sub AppendToFixedArray {&lt;br /&gt;
     my ($dev, $reading, $newVal, $maxSize) = @_;&lt;br /&gt;
     $maxSize //= 250;  # Standard: 250 Werte&lt;br /&gt;
     my $current = ReadingsVal($dev, $reading, &#039;&#039;);&lt;br /&gt;
     my @values = split(&#039;,&#039;, $current);&lt;br /&gt;
     if (@values &amp;gt;= $maxSize) {&lt;br /&gt;
         @values = @values[-($maxSize - 1) .. -1];  # Älteste Werte entfernen&lt;br /&gt;
     }&lt;br /&gt;
     push @values, $newVal;&lt;br /&gt;
     return join(&#039;,&#039;, @values);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 2️⃣ at-Timer zum regelmäßigen Aktualisieren ==&lt;br /&gt;
&lt;br /&gt;
Mit einem `at`-Timer werden die Werte der Messgeräte regelmäßig in die `chartArray`-Readings geschrieben.  &lt;br /&gt;
&lt;br /&gt;
 defmod chartUpdate at +*00:05 {\&lt;br /&gt;
   my $val = ReadingsVal(&amp;quot;Smartmeter_2E1F50&amp;quot;, &amp;quot;APOX_Power&amp;quot;, 0);;\&lt;br /&gt;
   my $new = AppendToFixedArray(&amp;quot;Smartmeter_2E1F50&amp;quot;, &amp;quot;chartArray&amp;quot;, $val);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Smartmeter_2E1F50 chartArray $new&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val1 = ReadingsVal(&amp;quot;OpenDTU&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new1 = AppendToFixedArray(&amp;quot;OpenDTU&amp;quot;, &amp;quot;chartArray&amp;quot;, $val1);;\&lt;br /&gt;
   fhem(&amp;quot;setreading OpenDTU chartArray $new1&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val2 = ReadingsVal(&amp;quot;Kuehlschrank&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new2 = AppendToFixedArray(&amp;quot;Kuehlschrank&amp;quot;, &amp;quot;chartArray&amp;quot;, $val2);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Kuehlschrank chartArray $new2&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val3 = ReadingsVal(&amp;quot;Luftentfeuchter&amp;quot;, &amp;quot;ENERGY_Power&amp;quot;, 0);;\&lt;br /&gt;
   my $new3 = AppendToFixedArray(&amp;quot;Luftentfeuchter&amp;quot;, &amp;quot;chartArray&amp;quot;, $val3);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Luftentfeuchter chartArray $new3&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val4 = ReadingsVal(&amp;quot;Geschirrspueler&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new4 = AppendToFixedArray(&amp;quot;Geschirrspueler&amp;quot;, &amp;quot;chartArray&amp;quot;, $val4);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Geschirrspueler chartArray $new4&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val5 = ReadingsVal(&amp;quot;Trockner&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new5 = AppendToFixedArray(&amp;quot;Trockner&amp;quot;, &amp;quot;chartArray&amp;quot;, $val5);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Trockner chartArray $new5&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val6 = ReadingsVal(&amp;quot;Waschmaschine&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new6 = AppendToFixedArray(&amp;quot;Waschmaschine&amp;quot;, &amp;quot;chartArray&amp;quot;, $val6);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Waschmaschine chartArray $new6&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val7 = ReadingsVal(&amp;quot;Pumpe_FBH_Pwr&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new7 = AppendToFixedArray(&amp;quot;Pumpe_FBH_Pwr&amp;quot;, &amp;quot;chartArray&amp;quot;, $val7);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Pumpe_FBH_Pwr chartArray $new7&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val8 = ReadingsVal(&amp;quot;Heizstab_BW_Pwr&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new8 = AppendToFixedArray(&amp;quot;Heizstab_BW_Pwr&amp;quot;, &amp;quot;chartArray&amp;quot;, $val8);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Heizstab_BW_Pwr chartArray $new8&amp;quot;);;\&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3️⃣ Minichart ==&lt;br /&gt;
&lt;br /&gt;
==== 1. Widget-Datei aus contrib laden ====&lt;br /&gt;
&lt;br /&gt;
Bevor das MiniChart-Widget genutzt werden kann, muss die JS-Datei ins FHEM-Webverzeichnis kopiert werden.&lt;br /&gt;
Dies kann mit der Perlfunktion { Svn_GetFile(&#039;from SVN Path&#039;, &#039;to local Path&#039;) } direkt in der FHEM Kommandozeile erfolgen.&lt;br /&gt;
&lt;br /&gt;
 { Svn_GetFile(&#039;contrib/fhemweb_minichart.js&#039;, &#039;www/pgm2/fhemweb_minichart.js&#039;) } &lt;br /&gt;
&lt;br /&gt;
==== 2. Widget im Device mit widgetOverride einrichten ====&lt;br /&gt;
&lt;br /&gt;
==== Werteanzeige ====&lt;br /&gt;
Drei Textzeilen können angezeigt werden, jeweils im Format:&lt;br /&gt;
  Label@Reading@Einheit&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
  Jetzt@power@W,Heute@yieldday@kWh,Gesamt@yieldtotal@kWh&lt;br /&gt;
&lt;br /&gt;
===== Ausblenden einzelner Zeilen =====&lt;br /&gt;
Jede der drei Zeilen kann vollständig oder teilweise ausgeblendet werden, indem das jeweilige Element&lt;br /&gt;
leer gelassen wird. Das Widget setzt intern automatisch ein geschütztes Leerzeichen (&amp;amp;nbsp;).&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* Nur Label ausblenden:&lt;br /&gt;
  @reading@W&lt;br /&gt;
* Nur Reading ausblenden:&lt;br /&gt;
  Label@@W&lt;br /&gt;
* Nur Einheit ausblenden:&lt;br /&gt;
  Label@reading@&lt;br /&gt;
* Ganze Zeile ausblenden:&lt;br /&gt;
  &amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
==== Diagrammtypen ====&lt;br /&gt;
MiniChart unterstützt zwei Diagrammtypen:&lt;br /&gt;
&lt;br /&gt;
* line – geglättetes Liniendiagramm mit Farbverlauf  &lt;br /&gt;
* bar  – Balkendiagramm mit positiver/negativer Farbgebung&lt;br /&gt;
&lt;br /&gt;
Das Chart-Reading benötigt eine kommagetrennte Zahlenliste, z. B.:&lt;br /&gt;
  10,20,15,18,25&lt;br /&gt;
&lt;br /&gt;
Chartoptionen:&lt;br /&gt;
  chartType@colorPositive@colorNegative&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
  line@#3b82f6@#ff0000&lt;br /&gt;
  bar@#00cc88@#cc0044&lt;br /&gt;
&lt;br /&gt;
Fallback (nur ChartReading angegeben):&lt;br /&gt;
  dann wird automatisch line@#3b82f6@#3b82f6 gesetzt&lt;br /&gt;
&lt;br /&gt;
=== Einrichtung im Device ===&lt;br /&gt;
&lt;br /&gt;
Syntax:&lt;br /&gt;
  &amp;lt;webCmd&amp;gt;:minichart,Label1@Reading1@Einheit,Label2@Reading2@Einheit,Label3@Reading3@Einheit,ChartReading,chartType@colorPos@colorNeg&lt;br /&gt;
&lt;br /&gt;
Beispiel Liniendiagramm:&lt;br /&gt;
  attr &amp;lt;device&amp;gt; webCmd minichart&lt;br /&gt;
  attr &amp;lt;device&amp;gt; widgetOverride minichart:minichart,Jetzt@power@W,Heute@yieldday@kWh,Gesamt@yieldtotal@kWh,chartArray,line@#3b82f6@#ff0000&lt;br /&gt;
&lt;br /&gt;
Beispiel Balkendiagramm:&lt;br /&gt;
  ...,chartArray,bar@#00cc88@#cc0044&lt;br /&gt;
&lt;br /&gt;
Fallback (Standard line + Standardfarbe):&lt;br /&gt;
  ...,chartArray&lt;br /&gt;
&lt;br /&gt;
=== Parameterübersicht ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Abschnitt&lt;br /&gt;
! Bedeutung&lt;br /&gt;
! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;lt;beliebigerName&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
| webCmd-Zuweisung&lt;br /&gt;
| Definiert die Instanz des Widgets.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;minichart&amp;lt;/code&amp;gt;&lt;br /&gt;
| Widget-Typ&lt;br /&gt;
| Aktiviert das MiniChart-Widget.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Label1@Reading1@Einheit1&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeigezeilen&lt;br /&gt;
| Drei frei definierbare Werte-/Statusfelder; mit &amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt; komplett ausblendbar.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Label2@Reading2@Einheit2&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeigezeilen&lt;br /&gt;
| Drei frei definierbare Werte-/Statusfelder; mit &amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt; komplett ausblendbar.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Label3@Reading3@Einheit3&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeigezeilen&lt;br /&gt;
| Drei frei definierbare Werte-/Statusfelder; mit &amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt; komplett ausblendbar.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ChartReading&amp;lt;/code&amp;gt;&lt;br /&gt;
| Datenquelle &lt;br /&gt;
| Liefert Array-Werte für das Diagramm .&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;chartType@colorPos@colorNeg&amp;lt;/code&amp;gt;&lt;br /&gt;
| Chart-Optionen&lt;br /&gt;
| line oder bar; Farben optional.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/MiniChart&amp;diff=40531</id>
		<title>FHEMWEB/MiniChart</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/MiniChart&amp;diff=40531"/>
		<updated>2025-12-07T17:50:31Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Minichart flex mobile.png]]&lt;br /&gt;
&lt;br /&gt;
== MiniChart – kompaktes Chart-Widget (Line &amp;amp; Bar) ==&lt;br /&gt;
&lt;br /&gt;
Das MiniChart-Widget visualisiert Messwertverläufe als kompaktes Liniendiagramm oder Balkendiagramm.&lt;br /&gt;
Zusätzlich zeigt es drei aktuelle Werte, die aus Label, Reading und Einheit bestehen. Zum Einrichten sind 3 Schritte notwendig.&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
Forenthread zum [https://forum.fhem.de/index.php?topic=141857.0 MiniChart Widget]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 1️⃣ 99_myUtils.pm – Hilfsfunktion zum Speichern von historischen Werten ==&lt;br /&gt;
&lt;br /&gt;
Zuerst wird eine Subroutine angelegt, die neue Werte an ein Fixed-Array anhängt und alte Werte entfernt, falls die maximale Größe überschritten wird.&lt;br /&gt;
&lt;br /&gt;
 sub AppendToFixedArray {&lt;br /&gt;
     my ($dev, $reading, $newVal, $maxSize) = @_;&lt;br /&gt;
     $maxSize //= 250;  # Standard: 250 Werte&lt;br /&gt;
     my $current = ReadingsVal($dev, $reading, &#039;&#039;);&lt;br /&gt;
     my @values = split(&#039;,&#039;, $current);&lt;br /&gt;
     if (@values &amp;gt;= $maxSize) {&lt;br /&gt;
         @values = @values[-($maxSize - 1) .. -1];  # Älteste Werte entfernen&lt;br /&gt;
     }&lt;br /&gt;
     push @values, $newVal;&lt;br /&gt;
     return join(&#039;,&#039;, @values);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 2️⃣ at-Timer zum regelmäßigen Aktualisieren ==&lt;br /&gt;
&lt;br /&gt;
Mit einem `at`-Timer werden die Werte der Messgeräte regelmäßig in die `chartArray`-Readings geschrieben.  &lt;br /&gt;
&lt;br /&gt;
 defmod chartUpdate at +*00:05 {\&lt;br /&gt;
   my $val = ReadingsVal(&amp;quot;Smartmeter_2E1F50&amp;quot;, &amp;quot;APOX_Power&amp;quot;, 0);;\&lt;br /&gt;
   my $new = AppendToFixedArray(&amp;quot;Smartmeter_2E1F50&amp;quot;, &amp;quot;chartArray&amp;quot;, $val);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Smartmeter_2E1F50 chartArray $new&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val1 = ReadingsVal(&amp;quot;OpenDTU&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new1 = AppendToFixedArray(&amp;quot;OpenDTU&amp;quot;, &amp;quot;chartArray&amp;quot;, $val1);;\&lt;br /&gt;
   fhem(&amp;quot;setreading OpenDTU chartArray $new1&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val2 = ReadingsVal(&amp;quot;Kuehlschrank&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new2 = AppendToFixedArray(&amp;quot;Kuehlschrank&amp;quot;, &amp;quot;chartArray&amp;quot;, $val2);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Kuehlschrank chartArray $new2&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val3 = ReadingsVal(&amp;quot;Luftentfeuchter&amp;quot;, &amp;quot;ENERGY_Power&amp;quot;, 0);;\&lt;br /&gt;
   my $new3 = AppendToFixedArray(&amp;quot;Luftentfeuchter&amp;quot;, &amp;quot;chartArray&amp;quot;, $val3);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Luftentfeuchter chartArray $new3&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val4 = ReadingsVal(&amp;quot;Geschirrspueler&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new4 = AppendToFixedArray(&amp;quot;Geschirrspueler&amp;quot;, &amp;quot;chartArray&amp;quot;, $val4);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Geschirrspueler chartArray $new4&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val5 = ReadingsVal(&amp;quot;Trockner&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new5 = AppendToFixedArray(&amp;quot;Trockner&amp;quot;, &amp;quot;chartArray&amp;quot;, $val5);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Trockner chartArray $new5&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val6 = ReadingsVal(&amp;quot;Waschmaschine&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new6 = AppendToFixedArray(&amp;quot;Waschmaschine&amp;quot;, &amp;quot;chartArray&amp;quot;, $val6);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Waschmaschine chartArray $new6&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val7 = ReadingsVal(&amp;quot;Pumpe_FBH_Pwr&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new7 = AppendToFixedArray(&amp;quot;Pumpe_FBH_Pwr&amp;quot;, &amp;quot;chartArray&amp;quot;, $val7);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Pumpe_FBH_Pwr chartArray $new7&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val8 = ReadingsVal(&amp;quot;Heizstab_BW_Pwr&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new8 = AppendToFixedArray(&amp;quot;Heizstab_BW_Pwr&amp;quot;, &amp;quot;chartArray&amp;quot;, $val8);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Heizstab_BW_Pwr chartArray $new8&amp;quot;);;\&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3️⃣ Minichart ==&lt;br /&gt;
&lt;br /&gt;
==== 1. Widget-Datei aus contrib laden ====&lt;br /&gt;
&lt;br /&gt;
Bevor das MiniChart-Widget genutzt werden kann, muss die JS-Datei ins FHEM-Webverzeichnis kopiert werden.&lt;br /&gt;
Dies kann mit der Perlfunktion { Svn_GetFile(&#039;from SVN Path&#039;, &#039;to local Path&#039;) } direkt in der FHEM Kommandozeile erfolgen.&lt;br /&gt;
&lt;br /&gt;
 { Svn_GetFile(&#039;contrib/fhemweb_minichart.js&#039;, &#039;www/pgm2/fhemweb_minichart.js&#039;) } &lt;br /&gt;
&lt;br /&gt;
==== 2. Widget im Device mit widgetOverride einrichten ====&lt;br /&gt;
&lt;br /&gt;
==== Werteanzeige ====&lt;br /&gt;
Drei Textzeilen können angezeigt werden, jeweils im Format:&lt;br /&gt;
  Label@Reading@Einheit&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
  Jetzt@power@W,Heute@yieldday@kWh,Gesamt@yieldtotal@kWh&lt;br /&gt;
&lt;br /&gt;
===== Ausblenden einzelner Zeilen =====&lt;br /&gt;
Jede der drei Zeilen kann vollständig oder teilweise ausgeblendet werden, indem das jeweilige Element&lt;br /&gt;
leer gelassen wird. Das Widget setzt intern automatisch ein geschütztes Leerzeichen (&amp;amp;nbsp;).&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* Nur Label ausblenden:&lt;br /&gt;
  @reading@W&lt;br /&gt;
* Nur Reading ausblenden:&lt;br /&gt;
  Label@@W&lt;br /&gt;
* Nur Einheit ausblenden:&lt;br /&gt;
  Label@reading@&lt;br /&gt;
* Ganze Zeile ausblenden:&lt;br /&gt;
  &amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
==== Diagrammtypen ====&lt;br /&gt;
MiniChart unterstützt zwei Diagrammtypen:&lt;br /&gt;
&lt;br /&gt;
* line – geglättetes Liniendiagramm mit Farbverlauf  &lt;br /&gt;
* bar  – Balkendiagramm mit positiver/negativer Farbgebung&lt;br /&gt;
&lt;br /&gt;
Das Chart-Reading liefert eine kommagetrennte Zahlenliste, z. B.:&lt;br /&gt;
  10,20,15,18,25&lt;br /&gt;
&lt;br /&gt;
Chartoptionen:&lt;br /&gt;
  chartType@colorPositive@colorNegative&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
  line@#3b82f6@#ff0000&lt;br /&gt;
  bar@#00cc88@#cc0044&lt;br /&gt;
&lt;br /&gt;
Fallback (nur ChartReading angegeben):&lt;br /&gt;
  line@#3b82f6@#3b82f6&lt;br /&gt;
&lt;br /&gt;
=== Einrichtung im Device ===&lt;br /&gt;
&lt;br /&gt;
Syntax:&lt;br /&gt;
  &amp;lt;webCmd&amp;gt;:minichart,Label1@Reading1@Einheit,Label2@Reading2@Einheit,Label3@Reading3@Einheit,ChartReading,chartType@colorPos@colorNeg&lt;br /&gt;
&lt;br /&gt;
Beispiel Liniendiagramm:&lt;br /&gt;
  attr &amp;lt;device&amp;gt; webCmd minichart&lt;br /&gt;
  attr &amp;lt;device&amp;gt; widgetOverride minichart:minichart,Jetzt@power@W,Heute@yieldday@kWh,Gesamt@yieldtotal@kWh,chartArray,line@#3b82f6@#ff0000&lt;br /&gt;
&lt;br /&gt;
Beispiel Balkendiagramm:&lt;br /&gt;
  ..., chartArray, bar@#00cc88@#cc0044&lt;br /&gt;
&lt;br /&gt;
Fallback (Standard line + Standardfarbe):&lt;br /&gt;
  ..., chartArray&lt;br /&gt;
&lt;br /&gt;
=== Parameterübersicht ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Abschnitt&lt;br /&gt;
! Bedeutung&lt;br /&gt;
! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;lt;beliebigerName&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
| webCmd-Zuweisung&lt;br /&gt;
| Definiert die Instanz des Widgets.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;minichart&amp;lt;/code&amp;gt;&lt;br /&gt;
| Widget-Typ&lt;br /&gt;
| Aktiviert das MiniChart-Widget.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Label1@Reading1@Einheit1&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeigezeilen&lt;br /&gt;
| Drei frei definierbare Werte-/Statusfelder; mit &amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt; komplett ausblendbar.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Label2@Reading2@Einheit2&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeigezeilen&lt;br /&gt;
| Drei frei definierbare Werte-/Statusfelder; mit &amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt; komplett ausblendbar.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Label3@Reading3@Einheit3&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeigezeilen&lt;br /&gt;
| Drei frei definierbare Werte-/Statusfelder; mit &amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt; komplett ausblendbar.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ChartReading&amp;lt;/code&amp;gt;&lt;br /&gt;
| Datenquelle &lt;br /&gt;
| Liefert Array-Werte für das Diagramm .&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;chartType@colorPos@colorNeg&amp;lt;/code&amp;gt;&lt;br /&gt;
| Chart-Optionen&lt;br /&gt;
| line oder bar; Farben optional.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/Widgets&amp;diff=40530</id>
		<title>FHEMWEB/Widgets</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/Widgets&amp;diff=40530"/>
		<updated>2025-12-07T17:48:43Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Widgets sind Frontendelemente, die der Dateneingabe und -anzeige dienen. Die hier gezeigten Frontendelemente werden durch [[FHEMWEB]] ([[PGM2]]) bereitgestellt.&lt;br /&gt;
&lt;br /&gt;
== Syntax ==&lt;br /&gt;
Die Syntax der Widgets ist unter dem Attribut [https://fhem.de/commandref_DE.html#widgetOverride widgetOverride] in der Befehlreferenz beschrieben.&lt;br /&gt;
&lt;br /&gt;
== Verwendung ==&lt;br /&gt;
Viele Module (u.a. [[DOIF]], [[dummy]], [[MQTT_DEVICE]], [[MQTT2_DEVICE]], [[MYSENSORS_DEVICE]], [[readingsGroup]]) bieten die Möglichkeit eine Benutzerschnittstelle zu erstellen.&lt;br /&gt;
&lt;br /&gt;
Widgets werden über &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;:&amp;lt;modifier&amp;gt;&amp;lt;/code&amp;gt;-Paare an [[Readings]] oder [[Attribute]] gebunden, dabei ist:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;&amp;lt;/code&amp;gt; ein Readingsname, ein Attribut oder ein Mapping-Argument.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;modifier&amp;gt;&amp;lt;/code&amp;gt; ein Widget einschliesslich Parameter.&lt;br /&gt;
&lt;br /&gt;
Bei der Erstellung spielen verschiedene Attribute zusammen:&lt;br /&gt;
&lt;br /&gt;
* Mit readingList (bzw. bei Modulen, die dies nicht kennen: widgetOverride) ist es möglich den Set-Befehl auf [[Readings]] zu erweitern, siehe [https://fhem.de/commandref_DE.html#readingList readingList].&lt;br /&gt;
* Mit [[setList]] (bzw. widgetOverride) werden die Widgets einem Reading zugeordnet und parametrisiert, siehe [https://fhem.de/commandref_DE.html#setList setList].&lt;br /&gt;
* Mit [[webCmd]] werden die Widgets angezeigt, siehe [https://fhem.de/commandref_DE.html#setList webCmd].&lt;br /&gt;
* Mit webCmdLabel werden die Widgets beschriftet und tabellarisch angeordnet, siehe [https://fhem.de/commandref_DE.html#webCmdLabel webCmdLabel].&lt;br /&gt;
* Mit widgetOverride ist es möglich, die im Modul festgelegten Widgets zu überschreiben, siehe [https://fhem.de/commandref_DE.html#widgetOverride widgetOverride].&lt;br /&gt;
* Mit [[eventMap]] ist es möglich Widgets an Mapping-Argumente zu binden, siehe [https://fhem.de/commandref_DE.html#eventMap eventMap] und {{Link2Forum|Topic=76755|LinkText=eventMap und Widget-Anbindung}}.&lt;br /&gt;
Das Zusammenspiel dieser Attribute ist u.A. im Artikel [[DeviceOverview anpassen#webCmd .26 Co|DeviceOverview anpassen]] näher erläutert.&lt;br /&gt;
&lt;br /&gt;
== Zusammenstellung der Widgets ==&lt;br /&gt;
[[Datei:fhemweb_widgets_1.png|mini|rechts]]&lt;br /&gt;
Die Bildschirmkopie zeigt das Benutzerinterface des Dummys mit den Widgets&lt;br /&gt;
&lt;br /&gt;
=== weitere Konfiguration ===&lt;br /&gt;
* &#039;&#039;&#039;knob&#039;&#039;&#039;, siehe http://anthonyterrien.com/knob/&lt;br /&gt;
* &#039;&#039;&#039;colorpicker&#039;&#039;&#039;, siehe [[Color]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
== Das Beispiel für [[DOIF/Import_von_Code_Snippets|Raw definition]] zum Ausprobieren ==&lt;br /&gt;
In der vorstehenden Abbildung fehlt das Look and Feel der Widgets. Mit dem folgenden Code-Snippet zum Ausprobieren, wird dies ergänzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod widgets dummy&lt;br /&gt;
attr widgets readingList 00select 01select 02selectnumbers 03selectnumbers 04textField 05textFieldNL 06textField-long 07textFieldNL-long 08slider 09multiple 10multiple-strict 11knob 12sortable 13sortable-strict 14sortable-given 15uzsuToggle 16uzsuSelect 17uzsuSelectRadio 18uzsuDropDown 19colorpicker_RGB 19colorpicker_HSV 19colorpicker_CT 19colorpicker_HUE 19colorpicker_BRI 20time 21iconRadio 21iconRadio_use4icon 22iconSwitch 23iconLabel 24iconButtons 24iconButtons_use4icon 25bitfield 26widgetList&lt;br /&gt;
attr widgets room 0_Test&lt;br /&gt;
attr widgets setList 00select:1,2,3,4,5,0,a,b,c,def,hik \&lt;br /&gt;
01select:select,1,2,3,4,5,0,a,b,c,def,hik \&lt;br /&gt;
02selectnumbers:selectnumbers,-30,3,33,1,lin \&lt;br /&gt;
03selectnumbers:selectnumbers,1,0.0625,140000,0,log10 \&lt;br /&gt;
04textField:textField \&lt;br /&gt;
05textFieldNL:textFieldNL\&lt;br /&gt;
06textField-long:textField-long,87 \&lt;br /&gt;
07textFieldNL-long:textFieldNL-long,87 \&lt;br /&gt;
08slider:slider,7,0.5,30,1 \&lt;br /&gt;
09multiple:multiple,shirt,shoes,skirt,trowsers \&lt;br /&gt;
10multiple-strict:multiple-strict,red,green,blue,yellow \&lt;br /&gt;
11knob:knob,min:0,max:360,width:50,height:50,step:1,lineCap:round,angleOffset:180,cursor:3,thickness:.3 \&lt;br /&gt;
12sortable:sortable,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
13sortable-strict:sortable-strict,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
14sortable-given:sortable-given,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
15uzsuToggle:uzsuToggle,ON,OFF\&lt;br /&gt;
16uzsuSelect:uzsuSelect,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
17uzsuSelectRadio:uzsuSelectRadio,MW,KW,UKW\&lt;br /&gt;
18uzsuDropDown:uzsuDropDown,red,green,blue,yellow \&lt;br /&gt;
19colorpicker_RGB:colorpicker,RGB \&lt;br /&gt;
19colorpicker_HSV:colorpicker,HSV \&lt;br /&gt;
19colorpicker_CT:colorpicker,CT,2000,10,6500 \&lt;br /&gt;
19colorpicker_HUE:colorpicker,HUE,0,1,359 \&lt;br /&gt;
19colorpicker_BRI:colorpicker,BRI,0,1,100 \&lt;br /&gt;
20time:time \&lt;br /&gt;
21iconRadio:iconRadio,#808080,10,fts_shutter_10@FFA500,20,fts_shutter_20@orange,30,fts_shutter_30@orange,40,fts_shutter_40@orange,50,fts_shutter_50@orange,60,fts_shutter_60@orange,70,fts_shutter_70@orange,80,fts_shutter_80@orange,90,fts_shutter_90@orange,100,fts_shutter_100@orange \&lt;br /&gt;
21iconRadio_use4icon:iconRadio,use4icon@808080,10,fts_shutter_10@FFA500,20,fts_shutter_20@orange,30,fts_shutter_30@orange,40,fts_shutter_40@orange,50,fts_shutter_50@orange,60,fts_shutter_60@orange,70,fts_shutter_70@orange,80,fts_shutter_80@orange,90,fts_shutter_90@orange,100,fts_shutter_100@orange \&lt;br /&gt;
22iconSwitch:iconSwitch,wide#open,@red,half#open,fts_shutter_50@%23cd2906,totally#closed,fts_shutter_100@magenta,half#closed, \&lt;br /&gt;
23iconLabel:iconLabel,wide#open,@red,half#open,fts_shutter_50@%23cd2906,totally#closed,fts_shutter_100@magenta,half#closed,\&lt;br /&gt;
24iconButtons:iconButtons,orange,Bad,sani_heating@green,Wohnzimmer_1,sani_heating@green,Wohnzimmer_2,sani_heating@green,Esszimmer,sani_heating@green,Diele,sani_heating@green,Gäste_WC,sani_heating@green,Büro,sani_heating@green,Wintergarten,sani_heating@green,Werkstatt,sani_heating@green,Schlafzimmer,sani_heating@green \&lt;br /&gt;
24iconButtons_use4icon:iconButtons,use4icon@red,Bad,sani_heating@0000FF,Wohnzimmer_1,sani_heating@blue,Wohnzimmer_2,sani_heating@blue,Esszimmer,sani_heating@blue,Diele,sani_heating@blue,Gäste_WC,sani_heating@blue,Büro,sani_heating@blue,Wintergarten,sani_heating@blue,Werkstatt,sani_heating@blue,Schlafzimmer,sani_heating@0000FF\&lt;br /&gt;
25bitfield:bitfield\&lt;br /&gt;
26widgetList:widgetList,4,select,Bad,Küche,Wohnzimmer,6,selectnumbers,7,1,30,0,lin&lt;br /&gt;
attr widgets userReadings 23iconLabel:22iconSwitch.* {ReadingsVal($name,&amp;quot;22iconSwitch&amp;quot;,&amp;quot;wideopen&amp;quot;)}&lt;br /&gt;
attr widgets webCmd 00select:01select:02selectnumbers:03selectnumbers:04textField:05textFieldNL:06textField-long:07textFieldNL-long:08slider:20time:09multiple:10multiple-strict:11knob:12sortable:13sortable-strict:14sortable-given:15uzsuToggle:16uzsuSelect:17uzsuSelectRadio:18uzsuDropDown:19colorpicker_RGB:19colorpicker_HSV:19colorpicker_CT:19colorpicker_HUE:19colorpicker_BRI:21iconRadio:21iconRadio_use4icon:24iconButtons:24iconButtons_use4icon:22iconSwitch:23iconLabel:25bitfield:26widgetList&lt;br /&gt;
attr widgets webCmdLabel select (default)\&lt;br /&gt;
:select (explicit)\&lt;br /&gt;
:selectnumbers (linear)\&lt;br /&gt;
:selectnumbers (logarithmic)\&lt;br /&gt;
:textField\&lt;br /&gt;
:textFieldNL (no label)\&lt;br /&gt;
:textField-long (multi-line)\&lt;br /&gt;
:textFieldNL-long (multi-line,no label)\&lt;br /&gt;
:slider\&lt;br /&gt;
:time\&lt;br /&gt;
:multiple\&lt;br /&gt;
:multiple-strict\&lt;br /&gt;
:knob\&lt;br /&gt;
:sortable\&lt;br /&gt;
:sortable-strict\&lt;br /&gt;
:sortable-given\&lt;br /&gt;
:uzsuToggle\&lt;br /&gt;
:uzsuSelect\&lt;br /&gt;
:uzsuSelectRadio\&lt;br /&gt;
:uzsuDropDown\&lt;br /&gt;
:colorpicker RGB\&lt;br /&gt;
:colorpicker HSV\&lt;br /&gt;
:colorpicker Farbtemperatur\&lt;br /&gt;
:colorpicker Farbton\&lt;br /&gt;
:colorpicker Helligkeit\&lt;br /&gt;
:iconRadio\&lt;br /&gt;
:iconRadio prefix use4icon\&lt;br /&gt;
:iconButtons\&lt;br /&gt;
:iconButtons prefix use4icon\&lt;br /&gt;
:iconSwitch\&lt;br /&gt;
:iconLabel\&lt;br /&gt;
:bitfield\&lt;br /&gt;
:widgetList (Raum Temperatur)&lt;br /&gt;
attr widgets widgetOverride readingList|setList|webCmd|webCmdLabel|widgetOverride:textField-long,86&lt;br /&gt;
&lt;br /&gt;
setstate widgets iconSwitch wide#open&lt;br /&gt;
setstate widgets 2024-03-19 20:18:38 00select 1&lt;br /&gt;
setstate widgets 2021-09-27 18:52:48 01select 0&lt;br /&gt;
setstate widgets 2021-09-27 18:52:33 02selectnumbers 0.0&lt;br /&gt;
setstate widgets 2021-09-27 12:13:35 03selectnumbers 64938&lt;br /&gt;
setstate widgets 2023-01-03 14:32:14 04textField Hello&lt;br /&gt;
setstate widgets 2017-08-24 17:39:22 05textField Hallo&lt;br /&gt;
setstate widgets 2023-01-03 14:47:53 05textFieldNL Hello&lt;br /&gt;
setstate widgets 2023-01-08 19:25:46 06textField-long Hello&lt;br /&gt;
setstate widgets 2022-11-28 18:23:30 07textFieldNL-long Hello&lt;br /&gt;
setstate widgets 2017-08-24 18:22:07 08slider 17.0&lt;br /&gt;
setstate widgets 2017-08-24 18:22:40 09multiple shirt,shoes&lt;br /&gt;
setstate widgets 2017-08-24 18:22:57 10multiple-strict red,green&lt;br /&gt;
setstate widgets 2021-09-27 03:19:35 11knob 207&lt;br /&gt;
setstate widgets 2017-08-24 18:21:09 12sortable blue,shirt,brown,skirt,white,shoes,black,jeans&lt;br /&gt;
setstate widgets 2017-08-24 18:48:17 13sortable-strict blue,white,shirt,shoes,brown,skirt,black,jeans&lt;br /&gt;
setstate widgets 2017-08-24 18:49:34 14sortable-given blue,shirt,white,shoes,brown,skirt,black,jeans&lt;br /&gt;
setstate widgets 2024-03-19 20:11:06 15uzsuToggle OFF&lt;br /&gt;
setstate widgets 2024-03-19 20:10:53 16uzsuSelect brown&lt;br /&gt;
setstate widgets 2024-03-19 20:11:16 17uzsuSelectRadio UKW&lt;br /&gt;
setstate widgets 2024-03-19 20:11:09 18uzsuDropDown blue&lt;br /&gt;
setstate widgets 2017-10-07 07:56:43 19colorpicker_BRI 54&lt;br /&gt;
setstate widgets 2017-10-07 07:56:31 19colorpicker_CT 5100&lt;br /&gt;
setstate widgets 2017-10-07 07:59:26 19colorpicker_HSV 208c20&lt;br /&gt;
setstate widgets 2017-10-07 07:56:29 19colorpicker_HUE 94&lt;br /&gt;
setstate widgets 2017-10-07 07:53:01 19colorpicker_RGB c95918&lt;br /&gt;
setstate widgets 2017-08-24 19:03:20 20time 12:00&lt;br /&gt;
setstate widgets 2021-09-27 18:54:56 21iconRadio 90&lt;br /&gt;
setstate widgets 2021-09-27 03:19:18 21iconRadio_use4icon 40&lt;br /&gt;
setstate widgets 2024-03-20 18:55:18 22iconSwitch half closed&lt;br /&gt;
setstate widgets 2024-03-20 18:55:18 23iconLabel half closed&lt;br /&gt;
setstate widgets 2021-09-27 18:57:53 24iconButtons Büro,Schlafzimmer&lt;br /&gt;
setstate widgets 2017-10-25 08:20:05 24iconButtons_use4icon Bad,Wohnzimmer_2,Diele,Büro,Werkstatt&lt;br /&gt;
setstate widgets 2024-03-19 20:07:03 25bitfield 25&lt;br /&gt;
setstate widgets 2024-04-11 19:25:52 26widgetList Küche,23&lt;br /&gt;
setstate widgets 2021-09-27 19:02:25 state iconSwitch wide#open&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beigesteuerte Widgets ==&lt;br /&gt;
[[Datei:datetimepicker.png|mini|rechts]]&lt;br /&gt;
* Forenthread {{Link2Forum|Topic=35736|LinkText=neues DateTimePicker Widget}}&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;[[Datei:Controlminidash.png|mini|rechts]]&lt;br /&gt;
* Wiki Seite [[FHEMWEB/ControlMiniDash|neues ControlMiniDash Widget]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;[[Datei:Minichart flex mobile.png|mini|rechts]]&lt;br /&gt;
* Wiki Seite [[FHEMWEB/MiniChart|neues MiniChart Widget]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:Glossary]]&lt;br /&gt;
[[Kategorie:HOWTOS]]&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/MiniChart&amp;diff=40529</id>
		<title>FHEMWEB/MiniChart</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/MiniChart&amp;diff=40529"/>
		<updated>2025-12-07T17:34:04Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Minichart flex mobile.png]]&lt;br /&gt;
&lt;br /&gt;
== MiniChart – kompaktes Chart-Widget (Line &amp;amp; Bar) ==&lt;br /&gt;
&lt;br /&gt;
Das MiniChart-Widget visualisiert Messwertverläufe als kompaktes Liniendiagramm oder Balkendiagramm.&lt;br /&gt;
Zusätzlich zeigt es drei aktuelle Werte, die aus Label, Reading und Einheit bestehen. Zum Einrichten sind 3 Schritte notwendig.&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
Forenthread zum [https://forum.fhem.de/index.php?topic=142831.0 ControlMiniDash Widget]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 1️⃣ 99_myUtils.pm – Hilfsfunktion zum Speichern von historischen Werten ==&lt;br /&gt;
&lt;br /&gt;
Zuerst wird eine Subroutine angelegt, die neue Werte an ein Fixed-Array anhängt und alte Werte entfernt, falls die maximale Größe überschritten wird.&lt;br /&gt;
&lt;br /&gt;
 sub AppendToFixedArray {&lt;br /&gt;
     my ($dev, $reading, $newVal, $maxSize) = @_;&lt;br /&gt;
     $maxSize //= 250;  # Standard: 250 Werte&lt;br /&gt;
     my $current = ReadingsVal($dev, $reading, &#039;&#039;);&lt;br /&gt;
     my @values = split(&#039;,&#039;, $current);&lt;br /&gt;
     if (@values &amp;gt;= $maxSize) {&lt;br /&gt;
         @values = @values[-($maxSize - 1) .. -1];  # Älteste Werte entfernen&lt;br /&gt;
     }&lt;br /&gt;
     push @values, $newVal;&lt;br /&gt;
     return join(&#039;,&#039;, @values);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 2️⃣ at-Timer zum regelmäßigen Aktualisieren ==&lt;br /&gt;
&lt;br /&gt;
Mit einem `at`-Timer werden die Werte der Messgeräte regelmäßig in die `chartArray`-Readings geschrieben.  &lt;br /&gt;
&lt;br /&gt;
 defmod chartUpdate at +*00:05 {\&lt;br /&gt;
   my $val = ReadingsVal(&amp;quot;Smartmeter_2E1F50&amp;quot;, &amp;quot;APOX_Power&amp;quot;, 0);;\&lt;br /&gt;
   my $new = AppendToFixedArray(&amp;quot;Smartmeter_2E1F50&amp;quot;, &amp;quot;chartArray&amp;quot;, $val);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Smartmeter_2E1F50 chartArray $new&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val1 = ReadingsVal(&amp;quot;OpenDTU&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new1 = AppendToFixedArray(&amp;quot;OpenDTU&amp;quot;, &amp;quot;chartArray&amp;quot;, $val1);;\&lt;br /&gt;
   fhem(&amp;quot;setreading OpenDTU chartArray $new1&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val2 = ReadingsVal(&amp;quot;Kuehlschrank&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new2 = AppendToFixedArray(&amp;quot;Kuehlschrank&amp;quot;, &amp;quot;chartArray&amp;quot;, $val2);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Kuehlschrank chartArray $new2&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val3 = ReadingsVal(&amp;quot;Luftentfeuchter&amp;quot;, &amp;quot;ENERGY_Power&amp;quot;, 0);;\&lt;br /&gt;
   my $new3 = AppendToFixedArray(&amp;quot;Luftentfeuchter&amp;quot;, &amp;quot;chartArray&amp;quot;, $val3);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Luftentfeuchter chartArray $new3&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val4 = ReadingsVal(&amp;quot;Geschirrspueler&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new4 = AppendToFixedArray(&amp;quot;Geschirrspueler&amp;quot;, &amp;quot;chartArray&amp;quot;, $val4);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Geschirrspueler chartArray $new4&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val5 = ReadingsVal(&amp;quot;Trockner&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new5 = AppendToFixedArray(&amp;quot;Trockner&amp;quot;, &amp;quot;chartArray&amp;quot;, $val5);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Trockner chartArray $new5&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val6 = ReadingsVal(&amp;quot;Waschmaschine&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new6 = AppendToFixedArray(&amp;quot;Waschmaschine&amp;quot;, &amp;quot;chartArray&amp;quot;, $val6);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Waschmaschine chartArray $new6&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val7 = ReadingsVal(&amp;quot;Pumpe_FBH_Pwr&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new7 = AppendToFixedArray(&amp;quot;Pumpe_FBH_Pwr&amp;quot;, &amp;quot;chartArray&amp;quot;, $val7);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Pumpe_FBH_Pwr chartArray $new7&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val8 = ReadingsVal(&amp;quot;Heizstab_BW_Pwr&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new8 = AppendToFixedArray(&amp;quot;Heizstab_BW_Pwr&amp;quot;, &amp;quot;chartArray&amp;quot;, $val8);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Heizstab_BW_Pwr chartArray $new8&amp;quot;);;\&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3️⃣ Minichart ==&lt;br /&gt;
&lt;br /&gt;
==== 1. Widget-Datei aus contrib laden ====&lt;br /&gt;
&lt;br /&gt;
Bevor das MiniChart-Widget genutzt werden kann, muss die JS-Datei ins FHEM-Webverzeichnis kopiert werden.&lt;br /&gt;
Dies kann mit der Perlfunktion { Svn_GetFile(&#039;from SVN Path&#039;, &#039;to local Path&#039;) } direkt in der FHEM Kommandozeile erfolgen.&lt;br /&gt;
&lt;br /&gt;
 { Svn_GetFile(&#039;contrib/fhemweb_minichart.js&#039;, &#039;www/pgm2/fhemweb_minichart.js&#039;) } &lt;br /&gt;
&lt;br /&gt;
==== 2. Widget im Device mit widgetOverride einrichten ====&lt;br /&gt;
&lt;br /&gt;
==== Werteanzeige ====&lt;br /&gt;
Drei Textzeilen können angezeigt werden, jeweils im Format:&lt;br /&gt;
  Label@Reading@Einheit&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
  Jetzt@power@W,Heute@yieldday@kWh,Gesamt@yieldtotal@kWh&lt;br /&gt;
&lt;br /&gt;
===== Ausblenden einzelner Zeilen =====&lt;br /&gt;
Jede der drei Zeilen kann vollständig oder teilweise ausgeblendet werden, indem das jeweilige Element&lt;br /&gt;
leer gelassen wird. Das Widget setzt intern automatisch ein geschütztes Leerzeichen (&amp;amp;nbsp;).&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* Nur Label ausblenden:&lt;br /&gt;
  @reading@W&lt;br /&gt;
* Nur Reading ausblenden:&lt;br /&gt;
  Label@@W&lt;br /&gt;
* Nur Einheit ausblenden:&lt;br /&gt;
  Label@reading@&lt;br /&gt;
* Ganze Zeile ausblenden:&lt;br /&gt;
  &amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
==== Diagrammtypen ====&lt;br /&gt;
MiniChart unterstützt zwei Diagrammtypen:&lt;br /&gt;
&lt;br /&gt;
* line – geglättetes Liniendiagramm mit Farbverlauf  &lt;br /&gt;
* bar  – Balkendiagramm mit positiver/negativer Farbgebung&lt;br /&gt;
&lt;br /&gt;
Das Chart-Reading liefert eine kommagetrennte Zahlenliste, z. B.:&lt;br /&gt;
  10,20,15,18,25&lt;br /&gt;
&lt;br /&gt;
Chartoptionen:&lt;br /&gt;
  chartType@colorPositive@colorNegative&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
  line@#3b82f6@#ff0000&lt;br /&gt;
  bar@#00cc88@#cc0044&lt;br /&gt;
&lt;br /&gt;
Fallback (nur ChartReading angegeben):&lt;br /&gt;
  line@#3b82f6@#3b82f6&lt;br /&gt;
&lt;br /&gt;
=== Einrichtung im Device ===&lt;br /&gt;
&lt;br /&gt;
Syntax:&lt;br /&gt;
  &amp;lt;webCmd&amp;gt;:minichart,Label1@Reading1@Einheit,Label2@Reading2@Einheit,Label3@Reading3@Einheit,ChartReading,chartType@colorPos@colorNeg&lt;br /&gt;
&lt;br /&gt;
Beispiel Liniendiagramm:&lt;br /&gt;
  attr &amp;lt;device&amp;gt; webCmd minichart&lt;br /&gt;
  attr &amp;lt;device&amp;gt; widgetOverride minichart:minichart,Jetzt@power@W,Heute@yieldday@kWh,Gesamt@yieldtotal@kWh,chartArray,line@#3b82f6@#ff0000&lt;br /&gt;
&lt;br /&gt;
Beispiel Balkendiagramm:&lt;br /&gt;
  ..., chartArray, bar@#00cc88@#cc0044&lt;br /&gt;
&lt;br /&gt;
Fallback (Standard line + Standardfarbe):&lt;br /&gt;
  ..., chartArray&lt;br /&gt;
&lt;br /&gt;
=== Parameterübersicht ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Abschnitt&lt;br /&gt;
! Bedeutung&lt;br /&gt;
! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;lt;beliebigerName&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
| webCmd-Zuweisung&lt;br /&gt;
| Definiert die Instanz des Widgets.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;minichart&amp;lt;/code&amp;gt;&lt;br /&gt;
| Widget-Typ&lt;br /&gt;
| Aktiviert das MiniChart-Widget.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Label1@Reading1@Einheit1&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeigezeilen&lt;br /&gt;
| Drei frei definierbare Werte-/Statusfelder; mit &amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt; komplett ausblendbar.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Label2@Reading2@Einheit2&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeigezeilen&lt;br /&gt;
| Drei frei definierbare Werte-/Statusfelder; mit &amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt; komplett ausblendbar.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Label3@Reading3@Einheit3&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeigezeilen&lt;br /&gt;
| Drei frei definierbare Werte-/Statusfelder; mit &amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt; komplett ausblendbar.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ChartReading&amp;lt;/code&amp;gt;&lt;br /&gt;
| Datenquelle &lt;br /&gt;
| Liefert Array-Werte für das Diagramm .&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;chartType@colorPos@colorNeg&amp;lt;/code&amp;gt;&lt;br /&gt;
| Chart-Optionen&lt;br /&gt;
| line oder bar; Farben optional.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/MiniChart&amp;diff=40528</id>
		<title>FHEMWEB/MiniChart</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/MiniChart&amp;diff=40528"/>
		<updated>2025-12-07T16:22:13Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Minichart flex mobile.png]]&lt;br /&gt;
&lt;br /&gt;
== MiniChart – kompaktes Chart-Widget (Line &amp;amp; Bar) ==&lt;br /&gt;
&lt;br /&gt;
Das MiniChart-Widget visualisiert Messwertverläufe als kompaktes Liniendiagramm oder Balkendiagramm.&lt;br /&gt;
Zusätzlich zeigt es drei aktuelle Werte, die aus Label, Reading und Einheit bestehen. Zum Einrichten sind 3 Schritte notwendig.&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
Forenthread zum [https://forum.fhem.de/index.php?topic=142831.0 ControlMiniDash Widget]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 1️⃣ 99_myUtils.pm – Hilfsfunktion zum Speichern von historischen Werten ==&lt;br /&gt;
&lt;br /&gt;
Zuerst wird eine Subroutine angelegt, die neue Werte an ein Fixed-Array anhängt und alte Werte entfernt, falls die maximale Größe überschritten wird.&lt;br /&gt;
&lt;br /&gt;
 sub AppendToFixedArray {&lt;br /&gt;
     my ($dev, $reading, $newVal, $maxSize) = @_;&lt;br /&gt;
     $maxSize //= 250;  # Standard: 250 Werte&lt;br /&gt;
     my $current = ReadingsVal($dev, $reading, &#039;&#039;);&lt;br /&gt;
     my @values = split(&#039;,&#039;, $current);&lt;br /&gt;
     if (@values &amp;gt;= $maxSize) {&lt;br /&gt;
         @values = @values[-($maxSize - 1) .. -1];  # Älteste Werte entfernen&lt;br /&gt;
     }&lt;br /&gt;
     push @values, $newVal;&lt;br /&gt;
     return join(&#039;,&#039;, @values);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 2️⃣ at-Timer zum regelmäßigen Aktualisieren ==&lt;br /&gt;
&lt;br /&gt;
Mit einem `at`-Timer werden die Werte der Messgeräte regelmäßig in die `chartArray`-Readings geschrieben.  &lt;br /&gt;
&lt;br /&gt;
 defmod chartUpdate at +*00:05 {\&lt;br /&gt;
   my $val = ReadingsVal(&amp;quot;Smartmeter_2E1F50&amp;quot;, &amp;quot;APOX_Power&amp;quot;, 0);;\&lt;br /&gt;
   my $new = AppendToFixedArray(&amp;quot;Smartmeter_2E1F50&amp;quot;, &amp;quot;chartArray&amp;quot;, $val);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Smartmeter_2E1F50 chartArray $new&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val1 = ReadingsVal(&amp;quot;OpenDTU&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new1 = AppendToFixedArray(&amp;quot;OpenDTU&amp;quot;, &amp;quot;chartArray&amp;quot;, $val1);;\&lt;br /&gt;
   fhem(&amp;quot;setreading OpenDTU chartArray $new1&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val2 = ReadingsVal(&amp;quot;Kuehlschrank&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new2 = AppendToFixedArray(&amp;quot;Kuehlschrank&amp;quot;, &amp;quot;chartArray&amp;quot;, $val2);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Kuehlschrank chartArray $new2&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val3 = ReadingsVal(&amp;quot;Luftentfeuchter&amp;quot;, &amp;quot;ENERGY_Power&amp;quot;, 0);;\&lt;br /&gt;
   my $new3 = AppendToFixedArray(&amp;quot;Luftentfeuchter&amp;quot;, &amp;quot;chartArray&amp;quot;, $val3);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Luftentfeuchter chartArray $new3&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val4 = ReadingsVal(&amp;quot;Geschirrspueler&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new4 = AppendToFixedArray(&amp;quot;Geschirrspueler&amp;quot;, &amp;quot;chartArray&amp;quot;, $val4);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Geschirrspueler chartArray $new4&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val5 = ReadingsVal(&amp;quot;Trockner&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new5 = AppendToFixedArray(&amp;quot;Trockner&amp;quot;, &amp;quot;chartArray&amp;quot;, $val5);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Trockner chartArray $new5&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val6 = ReadingsVal(&amp;quot;Waschmaschine&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new6 = AppendToFixedArray(&amp;quot;Waschmaschine&amp;quot;, &amp;quot;chartArray&amp;quot;, $val6);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Waschmaschine chartArray $new6&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val7 = ReadingsVal(&amp;quot;Pumpe_FBH_Pwr&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new7 = AppendToFixedArray(&amp;quot;Pumpe_FBH_Pwr&amp;quot;, &amp;quot;chartArray&amp;quot;, $val7);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Pumpe_FBH_Pwr chartArray $new7&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val8 = ReadingsVal(&amp;quot;Heizstab_BW_Pwr&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new8 = AppendToFixedArray(&amp;quot;Heizstab_BW_Pwr&amp;quot;, &amp;quot;chartArray&amp;quot;, $val8);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Heizstab_BW_Pwr chartArray $new8&amp;quot;);;\&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3️⃣ Minichart ==&lt;br /&gt;
&lt;br /&gt;
==== 1. Widget-Datei aus contrib laden ====&lt;br /&gt;
&lt;br /&gt;
Bevor das MiniChart-Widget genutzt werden kann, muss die JS-Datei ins FHEM-Webverzeichnis kopiert werden.&lt;br /&gt;
Dies kann mit der Perlfunktion { Svn_GetFile(&#039;from SVN Path&#039;, &#039;to local Path&#039;) } direkt in der FHEM Kommandozeile erfolgen.&lt;br /&gt;
&lt;br /&gt;
 { Svn_GetFile(&#039;contrib/fhemweb_minichart.js&#039;, &#039;/fhem/www/pgm2/fhemweb_minichart.js&#039;) }&lt;br /&gt;
&lt;br /&gt;
==== 2. Widget im Device mit widgetOverride einrichten ====&lt;br /&gt;
&lt;br /&gt;
==== Werteanzeige ====&lt;br /&gt;
Drei Textzeilen können angezeigt werden, jeweils im Format:&lt;br /&gt;
  Label@Reading@Einheit&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
  Jetzt@power@W,Heute@yieldday@kWh,Gesamt@yieldtotal@kWh&lt;br /&gt;
&lt;br /&gt;
===== Ausblenden einzelner Zeilen =====&lt;br /&gt;
Jede der drei Zeilen kann vollständig oder teilweise ausgeblendet werden, indem das jeweilige Element&lt;br /&gt;
leer gelassen wird. Das Widget setzt intern automatisch ein geschütztes Leerzeichen (&amp;amp;nbsp;).&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* Nur Label ausblenden:&lt;br /&gt;
  @reading@W&lt;br /&gt;
* Nur Reading ausblenden:&lt;br /&gt;
  Label@@W&lt;br /&gt;
* Nur Einheit ausblenden:&lt;br /&gt;
  Label@reading@&lt;br /&gt;
* Ganze Zeile ausblenden:&lt;br /&gt;
  &amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
==== Diagrammtypen ====&lt;br /&gt;
MiniChart unterstützt zwei Diagrammtypen:&lt;br /&gt;
&lt;br /&gt;
* line – geglättetes Liniendiagramm mit Farbverlauf  &lt;br /&gt;
* bar  – Balkendiagramm mit positiver/negativer Farbgebung&lt;br /&gt;
&lt;br /&gt;
Das Chart-Reading liefert eine kommagetrennte Zahlenliste, z. B.:&lt;br /&gt;
  10,20,15,18,25&lt;br /&gt;
&lt;br /&gt;
Chartoptionen:&lt;br /&gt;
  chartType@colorPositive@colorNegative&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
  line@#3b82f6@#ff0000&lt;br /&gt;
  bar@#00cc88@#cc0044&lt;br /&gt;
&lt;br /&gt;
Fallback (nur ChartReading angegeben):&lt;br /&gt;
  line@#3b82f6@#3b82f6&lt;br /&gt;
&lt;br /&gt;
=== Einrichtung im Device ===&lt;br /&gt;
&lt;br /&gt;
Syntax:&lt;br /&gt;
  &amp;lt;webCmd&amp;gt;:minichart,Label1@Reading1@Einheit,Label2@Reading2@Einheit,Label3@Reading3@Einheit,ChartReading,chartType@colorPos@colorNeg&lt;br /&gt;
&lt;br /&gt;
Beispiel Liniendiagramm:&lt;br /&gt;
  attr &amp;lt;device&amp;gt; webCmd minichart&lt;br /&gt;
  attr &amp;lt;device&amp;gt; widgetOverride minichart:minichart,Jetzt@power@W,Heute@yieldday@kWh,Gesamt@yieldtotal@kWh,chartArray,line@#3b82f6@#ff0000&lt;br /&gt;
&lt;br /&gt;
Beispiel Balkendiagramm:&lt;br /&gt;
  ..., chartArray, bar@#00cc88@#cc0044&lt;br /&gt;
&lt;br /&gt;
Fallback (Standard line + Standardfarbe):&lt;br /&gt;
  ..., chartArray&lt;br /&gt;
&lt;br /&gt;
=== Parameterübersicht ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Abschnitt&lt;br /&gt;
! Bedeutung&lt;br /&gt;
! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;lt;beliebigerName&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
| webCmd-Zuweisung&lt;br /&gt;
| Definiert die Instanz des Widgets.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;minichart&amp;lt;/code&amp;gt;&lt;br /&gt;
| Widget-Typ&lt;br /&gt;
| Aktiviert das MiniChart-Widget.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Label1@Reading1@Einheit1&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeigezeilen&lt;br /&gt;
| Drei frei definierbare Werte-/Statusfelder; mit &amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt; komplett ausblendbar.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Label2@Reading2@Einheit2&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeigezeilen&lt;br /&gt;
| Drei frei definierbare Werte-/Statusfelder; mit &amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt; komplett ausblendbar.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Label3@Reading3@Einheit3&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeigezeilen&lt;br /&gt;
| Drei frei definierbare Werte-/Statusfelder; mit &amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt; komplett ausblendbar.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ChartReading&amp;lt;/code&amp;gt;&lt;br /&gt;
| Datenquelle &lt;br /&gt;
| Liefert Array-Werte für das Diagramm .&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;chartType@colorPos@colorNeg&amp;lt;/code&amp;gt;&lt;br /&gt;
| Chart-Optionen&lt;br /&gt;
| line oder bar; Farben optional.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/MiniChart&amp;diff=40520</id>
		<title>FHEMWEB/MiniChart</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/MiniChart&amp;diff=40520"/>
		<updated>2025-12-07T10:57:39Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Minichart flex mobile.png]]&lt;br /&gt;
&lt;br /&gt;
== MiniChart – kompaktes Chart-Widget (Line &amp;amp; Bar) ==&lt;br /&gt;
&lt;br /&gt;
Das MiniChart-Widget visualisiert Messwertverläufe als kompaktes Liniendiagramm oder Balkendiagramm.&lt;br /&gt;
Zusätzlich zeigt es drei aktuelle Werte, die aus Label, Reading und Einheit bestehen. Zum Einrichten sind 3 Schritte notwendig.&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
Forenthread zum [https://forum.fhem.de/index.php?topic=142831.0 ControlMiniDash Widget]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 1️⃣ 99_myUtils.pm – Hilfsfunktion zum Speichern von historischen Werten ==&lt;br /&gt;
&lt;br /&gt;
Zuerst wird eine Subroutine angelegt, die neue Werte an ein Fixed-Array anhängt und alte Werte entfernt, falls die maximale Größe überschritten wird.&lt;br /&gt;
&lt;br /&gt;
 sub AppendToFixedArray {&lt;br /&gt;
     my ($dev, $reading, $newVal, $maxSize) = @_;&lt;br /&gt;
     $maxSize //= 250;  # Standard: 250 Werte&lt;br /&gt;
     my $current = ReadingsVal($dev, $reading, &#039;&#039;);&lt;br /&gt;
     my @values = split(&#039;,&#039;, $current);&lt;br /&gt;
     if (@values &amp;gt;= $maxSize) {&lt;br /&gt;
         @values = @values[-($maxSize - 1) .. -1];  # Älteste Werte entfernen&lt;br /&gt;
     }&lt;br /&gt;
     push @values, $newVal;&lt;br /&gt;
     return join(&#039;,&#039;, @values);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 2️⃣ at-Timer zum regelmäßigen Aktualisieren ==&lt;br /&gt;
&lt;br /&gt;
Mit einem `at`-Timer werden die Werte der Messgeräte regelmäßig in die `chartArray`-Readings geschrieben.  &lt;br /&gt;
&lt;br /&gt;
 defmod chartUpdate at +*00:05 {\&lt;br /&gt;
   my $val = ReadingsVal(&amp;quot;Smartmeter_2E1F50&amp;quot;, &amp;quot;APOX_Power&amp;quot;, 0);;\&lt;br /&gt;
   my $new = AppendToFixedArray(&amp;quot;Smartmeter_2E1F50&amp;quot;, &amp;quot;chartArray&amp;quot;, $val);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Smartmeter_2E1F50 chartArray $new&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val1 = ReadingsVal(&amp;quot;OpenDTU&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new1 = AppendToFixedArray(&amp;quot;OpenDTU&amp;quot;, &amp;quot;chartArray&amp;quot;, $val1);;\&lt;br /&gt;
   fhem(&amp;quot;setreading OpenDTU chartArray $new1&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val2 = ReadingsVal(&amp;quot;Kuehlschrank&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new2 = AppendToFixedArray(&amp;quot;Kuehlschrank&amp;quot;, &amp;quot;chartArray&amp;quot;, $val2);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Kuehlschrank chartArray $new2&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val3 = ReadingsVal(&amp;quot;Luftentfeuchter&amp;quot;, &amp;quot;ENERGY_Power&amp;quot;, 0);;\&lt;br /&gt;
   my $new3 = AppendToFixedArray(&amp;quot;Luftentfeuchter&amp;quot;, &amp;quot;chartArray&amp;quot;, $val3);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Luftentfeuchter chartArray $new3&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val4 = ReadingsVal(&amp;quot;Geschirrspueler&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new4 = AppendToFixedArray(&amp;quot;Geschirrspueler&amp;quot;, &amp;quot;chartArray&amp;quot;, $val4);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Geschirrspueler chartArray $new4&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val5 = ReadingsVal(&amp;quot;Trockner&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new5 = AppendToFixedArray(&amp;quot;Trockner&amp;quot;, &amp;quot;chartArray&amp;quot;, $val5);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Trockner chartArray $new5&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val6 = ReadingsVal(&amp;quot;Waschmaschine&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new6 = AppendToFixedArray(&amp;quot;Waschmaschine&amp;quot;, &amp;quot;chartArray&amp;quot;, $val6);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Waschmaschine chartArray $new6&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val7 = ReadingsVal(&amp;quot;Pumpe_FBH_Pwr&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new7 = AppendToFixedArray(&amp;quot;Pumpe_FBH_Pwr&amp;quot;, &amp;quot;chartArray&amp;quot;, $val7);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Pumpe_FBH_Pwr chartArray $new7&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val8 = ReadingsVal(&amp;quot;Heizstab_BW_Pwr&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new8 = AppendToFixedArray(&amp;quot;Heizstab_BW_Pwr&amp;quot;, &amp;quot;chartArray&amp;quot;, $val8);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Heizstab_BW_Pwr chartArray $new8&amp;quot;);;\&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3️⃣ Minichart ==&lt;br /&gt;
&lt;br /&gt;
==== 1. Widget-Datei aus contrib laden ====&lt;br /&gt;
&lt;br /&gt;
Bevor das MiniChart-Widget genutzt werden kann, muss die JS-Datei ins FHEM-Webverzeichnis kopiert werden.&lt;br /&gt;
Dies kann mit der Perlfunktion { Svn_GetFile(&#039;from SVN Path&#039;, &#039;to local Path&#039;) } direkt in der FHEM Kommandozeile erfolgen.&lt;br /&gt;
&lt;br /&gt;
 { Svn_GetFile(&#039;contrib/schwatter/fhemweb_minichart.js&#039;, &#039;/fhem/www/pgm2/fhemweb_minichart.js&#039;) }&lt;br /&gt;
&lt;br /&gt;
==== 2. Widget im Device mit widgetOverride einrichten ====&lt;br /&gt;
&lt;br /&gt;
==== Werteanzeige ====&lt;br /&gt;
Drei Textzeilen können angezeigt werden, jeweils im Format:&lt;br /&gt;
  Label@Reading@Einheit&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
  Jetzt@power@W,Heute@yieldday@kWh,Gesamt@yieldtotal@kWh&lt;br /&gt;
&lt;br /&gt;
===== Ausblenden einzelner Zeilen =====&lt;br /&gt;
Jede der drei Zeilen kann vollständig oder teilweise ausgeblendet werden, indem das jeweilige Element&lt;br /&gt;
leer gelassen wird. Das Widget setzt intern automatisch ein geschütztes Leerzeichen (&amp;amp;nbsp;).&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* Nur Label ausblenden:&lt;br /&gt;
  @reading@W&lt;br /&gt;
* Nur Reading ausblenden:&lt;br /&gt;
  Label@@W&lt;br /&gt;
* Nur Einheit ausblenden:&lt;br /&gt;
  Label@reading@&lt;br /&gt;
* Ganze Zeile ausblenden:&lt;br /&gt;
  &amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
==== Diagrammtypen ====&lt;br /&gt;
MiniChart unterstützt zwei Diagrammtypen:&lt;br /&gt;
&lt;br /&gt;
* line – geglättetes Liniendiagramm mit Farbverlauf  &lt;br /&gt;
* bar  – Balkendiagramm mit positiver/negativer Farbgebung&lt;br /&gt;
&lt;br /&gt;
Das Chart-Reading liefert eine kommagetrennte Zahlenliste, z. B.:&lt;br /&gt;
  10,20,15,18,25&lt;br /&gt;
&lt;br /&gt;
Chartoptionen:&lt;br /&gt;
  chartType@colorPositive@colorNegative&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
  line@#3b82f6@#ff0000&lt;br /&gt;
  bar@#00cc88@#cc0044&lt;br /&gt;
&lt;br /&gt;
Fallback (nur ChartReading angegeben):&lt;br /&gt;
  line@#3b82f6@#3b82f6&lt;br /&gt;
&lt;br /&gt;
=== Einrichtung im Device ===&lt;br /&gt;
&lt;br /&gt;
Syntax:&lt;br /&gt;
  &amp;lt;webCmd&amp;gt;:minichart,Label1@Reading1@Einheit,Label2@Reading2@Einheit,Label3@Reading3@Einheit,ChartReading,chartType@colorPos@colorNeg&lt;br /&gt;
&lt;br /&gt;
Beispiel Liniendiagramm:&lt;br /&gt;
  attr &amp;lt;device&amp;gt; webCmd minichart&lt;br /&gt;
  attr &amp;lt;device&amp;gt; widgetOverride minichart:minichart,Jetzt@power@W,Heute@yieldday@kWh,Gesamt@yieldtotal@kWh,chartArray,line@#3b82f6@#ff0000&lt;br /&gt;
&lt;br /&gt;
Beispiel Balkendiagramm:&lt;br /&gt;
  ..., chartArray, bar@#00cc88@#cc0044&lt;br /&gt;
&lt;br /&gt;
Fallback (Standard line + Standardfarbe):&lt;br /&gt;
  ..., chartArray&lt;br /&gt;
&lt;br /&gt;
=== Parameterübersicht ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Abschnitt&lt;br /&gt;
! Bedeutung&lt;br /&gt;
! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;lt;beliebigerName&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
| webCmd-Zuweisung&lt;br /&gt;
| Definiert die Instanz des Widgets.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;minichart&amp;lt;/code&amp;gt;&lt;br /&gt;
| Widget-Typ&lt;br /&gt;
| Aktiviert das MiniChart-Widget.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Label1@Reading1@Einheit1&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeigezeilen&lt;br /&gt;
| Drei frei definierbare Werte-/Statusfelder; mit &amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt; komplett ausblendbar.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Label2@Reading2@Einheit2&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeigezeilen&lt;br /&gt;
| Drei frei definierbare Werte-/Statusfelder; mit &amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt; komplett ausblendbar.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Label3@Reading3@Einheit3&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeigezeilen&lt;br /&gt;
| Drei frei definierbare Werte-/Statusfelder; mit &amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt; komplett ausblendbar.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ChartReading&amp;lt;/code&amp;gt;&lt;br /&gt;
| Datenquelle &lt;br /&gt;
| Liefert Array-Werte für das Diagramm .&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;chartType@colorPos@colorNeg&amp;lt;/code&amp;gt;&lt;br /&gt;
| Chart-Optionen&lt;br /&gt;
| line oder bar; Farben optional.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/MiniChart&amp;diff=40519</id>
		<title>FHEMWEB/MiniChart</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/MiniChart&amp;diff=40519"/>
		<updated>2025-12-07T10:51:49Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Minichart flex mobile.png]]&lt;br /&gt;
&lt;br /&gt;
== MiniChart – kompaktes Chart-Widget (Line &amp;amp; Bar) ==&lt;br /&gt;
&lt;br /&gt;
Das MiniChart-Widget visualisiert Messwertverläufe als kompaktes Liniendiagramm oder Balkendiagramm.&lt;br /&gt;
Zusätzlich zeigt es drei aktuelle Werte, die aus Label, Reading und Einheit bestehen. Zum Einrichten sind 3 Schritte notwendig.&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
Forenthread zum [https://forum.fhem.de/index.php?topic=142831.0 ControlMiniDash Widget]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 1️⃣ 99_myUtils.pm – Hilfsfunktion zum Speichern von historischen Werten ==&lt;br /&gt;
&lt;br /&gt;
Zuerst wird eine Subroutine angelegt, die neue Werte an ein Fixed-Array anhängt und alte Werte entfernt, falls die maximale Größe überschritten wird.&lt;br /&gt;
&lt;br /&gt;
 sub AppendToFixedArray {&lt;br /&gt;
     my ($dev, $reading, $newVal, $maxSize) = @_;&lt;br /&gt;
     $maxSize //= 250;  # Standard: 250 Werte&lt;br /&gt;
     my $current = ReadingsVal($dev, $reading, &#039;&#039;);&lt;br /&gt;
     my @values = split(&#039;,&#039;, $current);&lt;br /&gt;
     if (@values &amp;gt;= $maxSize) {&lt;br /&gt;
         @values = @values[-($maxSize - 1) .. -1];  # Älteste Werte entfernen&lt;br /&gt;
     }&lt;br /&gt;
     push @values, $newVal;&lt;br /&gt;
     return join(&#039;,&#039;, @values);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 2️⃣ at-Timer zum regelmäßigen Aktualisieren ==&lt;br /&gt;
&lt;br /&gt;
Mit einem `at`-Timer werden die Werte der Messgeräte regelmäßig in die `chartArray`-Readings geschrieben.  &lt;br /&gt;
&lt;br /&gt;
 defmod chartUpdate at +*00:05 {\&lt;br /&gt;
   my $val = ReadingsVal(&amp;quot;Smartmeter_2E1F50&amp;quot;, &amp;quot;APOX_Power&amp;quot;, 0);;\&lt;br /&gt;
   my $new = AppendToFixedArray(&amp;quot;Smartmeter_2E1F50&amp;quot;, &amp;quot;chartArray&amp;quot;, $val);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Smartmeter_2E1F50 chartArray $new&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val1 = ReadingsVal(&amp;quot;OpenDTU&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new1 = AppendToFixedArray(&amp;quot;OpenDTU&amp;quot;, &amp;quot;chartArray&amp;quot;, $val1);;\&lt;br /&gt;
   fhem(&amp;quot;setreading OpenDTU chartArray $new1&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val2 = ReadingsVal(&amp;quot;Kuehlschrank&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new2 = AppendToFixedArray(&amp;quot;Kuehlschrank&amp;quot;, &amp;quot;chartArray&amp;quot;, $val2);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Kuehlschrank chartArray $new2&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val3 = ReadingsVal(&amp;quot;Luftentfeuchter&amp;quot;, &amp;quot;ENERGY_Power&amp;quot;, 0);;\&lt;br /&gt;
   my $new3 = AppendToFixedArray(&amp;quot;Luftentfeuchter&amp;quot;, &amp;quot;chartArray&amp;quot;, $val3);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Luftentfeuchter chartArray $new3&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val4 = ReadingsVal(&amp;quot;Geschirrspueler&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new4 = AppendToFixedArray(&amp;quot;Geschirrspueler&amp;quot;, &amp;quot;chartArray&amp;quot;, $val4);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Geschirrspueler chartArray $new4&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val5 = ReadingsVal(&amp;quot;Trockner&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new5 = AppendToFixedArray(&amp;quot;Trockner&amp;quot;, &amp;quot;chartArray&amp;quot;, $val5);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Trockner chartArray $new5&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val6 = ReadingsVal(&amp;quot;Waschmaschine&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new6 = AppendToFixedArray(&amp;quot;Waschmaschine&amp;quot;, &amp;quot;chartArray&amp;quot;, $val6);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Waschmaschine chartArray $new6&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val7 = ReadingsVal(&amp;quot;Pumpe_FBH_Pwr&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new7 = AppendToFixedArray(&amp;quot;Pumpe_FBH_Pwr&amp;quot;, &amp;quot;chartArray&amp;quot;, $val7);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Pumpe_FBH_Pwr chartArray $new7&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val8 = ReadingsVal(&amp;quot;Heizstab_BW_Pwr&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new8 = AppendToFixedArray(&amp;quot;Heizstab_BW_Pwr&amp;quot;, &amp;quot;chartArray&amp;quot;, $val8);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Heizstab_BW_Pwr chartArray $new8&amp;quot;);;\&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3️⃣ Minichart ==&lt;br /&gt;
&lt;br /&gt;
==== 1. Widget-Datei aus contrib laden ====&lt;br /&gt;
&lt;br /&gt;
Bevor das MiniChart-Widget genutzt werden kann, muss die JS-Datei ins FHEM-Webverzeichnis kopiert werden.&lt;br /&gt;
Dies kann mit der Perlfunktion { Svn_GetFile(&#039;from SVN Path&#039;, &#039;to local Path&#039;) } direkt in der FHEM Kommandozeile erfolgen.&lt;br /&gt;
&lt;br /&gt;
 { Svn_GetFile(&#039;contrib/schwatter/fhemweb_minichart.js&#039;, &#039;/fhem/www/pgm2/fhemweb_minichart.js&#039;) }&lt;br /&gt;
&lt;br /&gt;
==== 2. Widget im Device mit widgetOverride einrichten ====&lt;br /&gt;
&lt;br /&gt;
==== Werteanzeige ====&lt;br /&gt;
Drei Textzeilen können angezeigt werden, jeweils im Format:&lt;br /&gt;
  Label@Reading@Einheit&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
  Jetzt@power@W,Heute@yieldday@kWh,Gesamt@yieldtotal@kWh&lt;br /&gt;
&lt;br /&gt;
===== Ausblenden einzelner Zeilen =====&lt;br /&gt;
Jede der drei Zeilen kann vollständig oder teilweise ausgeblendet werden, indem das jeweilige Element&lt;br /&gt;
leer gelassen wird. Das Widget setzt intern automatisch ein geschütztes Leerzeichen (&amp;amp;nbsp;).&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* Nur Label ausblenden:&lt;br /&gt;
  @reading@W&lt;br /&gt;
* Nur Reading ausblenden:&lt;br /&gt;
  Label@@W&lt;br /&gt;
* Nur Einheit ausblenden:&lt;br /&gt;
  Label@reading@&lt;br /&gt;
* Ganze Zeile ausblenden:&lt;br /&gt;
  &amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
==== Diagrammtypen ====&lt;br /&gt;
MiniChart unterstützt zwei Diagrammtypen:&lt;br /&gt;
&lt;br /&gt;
* line – geglättetes Liniendiagramm mit Farbverlauf  &lt;br /&gt;
* bar  – Balkendiagramm mit positiver/negativer Farbgebung&lt;br /&gt;
&lt;br /&gt;
Das Chart-Reading liefert eine kommagetrennte Zahlenliste, z. B.:&lt;br /&gt;
  10,20,15,18,25&lt;br /&gt;
&lt;br /&gt;
Chartoptionen:&lt;br /&gt;
  chartType@colorPositive@colorNegative&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
  line@#3b82f6@#ff0000&lt;br /&gt;
  bar@#00cc88@#cc0044&lt;br /&gt;
&lt;br /&gt;
Fallback (nur ChartReading angegeben):&lt;br /&gt;
  line@#3b82f6@#3b82f6&lt;br /&gt;
&lt;br /&gt;
=== Einrichtung im Device ===&lt;br /&gt;
&lt;br /&gt;
Syntax:&lt;br /&gt;
  &amp;lt;webCmd&amp;gt;:minichart,Label1@Reading1@Einheit,Label2@Reading2@Einheit,Label3@Reading3@Einheit,ChartReading,chartType@colorPos@colorNeg&lt;br /&gt;
&lt;br /&gt;
Beispiel Liniendiagramm:&lt;br /&gt;
  attr &amp;lt;device&amp;gt; webCmd minichart&lt;br /&gt;
  attr &amp;lt;device&amp;gt; widgetOverride minichart:minichart,Jetzt@power@W,Heute@yieldday@kWh,Gesamt@yieldtotal@kWh,chartArray,line@#3b82f6@#ff0000&lt;br /&gt;
&lt;br /&gt;
Beispiel Balkendiagramm:&lt;br /&gt;
  ..., chartArray, bar@#00cc88@#cc0044&lt;br /&gt;
&lt;br /&gt;
Fallback (Standard line + Standardfarbe):&lt;br /&gt;
  ..., chartArray&lt;br /&gt;
&lt;br /&gt;
=== Parameterübersicht ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Abschnitt&lt;br /&gt;
! Bedeutung&lt;br /&gt;
! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;lt;beliebigerName&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
| webCmd-Zuweisung&lt;br /&gt;
| Definiert die Instanz des Widgets.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;minichart&amp;lt;/code&amp;gt;&lt;br /&gt;
| Widget-Typ&lt;br /&gt;
| Aktiviert das MiniChart-Widget.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Label1@Reading1@Einheit1&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeigezeilen&lt;br /&gt;
| Drei frei definierbare Werte-/Statusfelder; mit &amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt; komplett ausblendbar.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Label2@Reading2@Einheit2&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeigezeilen&lt;br /&gt;
| Drei frei definierbare Werte-/Statusfelder; mit &amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt; komplett ausblendbar.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Label3@Reading3@Einheit3&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeigezeilen&lt;br /&gt;
| Drei frei definierbare Werte-/Statusfelder; mit &amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt; komplett ausblendbar.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ChartReading&amp;lt;/code&amp;gt;&lt;br /&gt;
| Datenquelle&lt;br /&gt;
| Kommagetrennte Zahlenliste für das Chart.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;chartType@colorPos@colorNeg&amp;lt;/code&amp;gt;&lt;br /&gt;
| Chart-Optionen&lt;br /&gt;
| line oder bar; Farben optional.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/MiniChart&amp;diff=40518</id>
		<title>FHEMWEB/MiniChart</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/MiniChart&amp;diff=40518"/>
		<updated>2025-12-07T10:49:31Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Minichart flex mobile.png]]&lt;br /&gt;
&lt;br /&gt;
== MiniChart – kompaktes Chart-Widget (Line &amp;amp; Bar) ==&lt;br /&gt;
&lt;br /&gt;
Das MiniChart-Widget visualisiert Messwertverläufe als kompaktes Liniendiagramm oder Balkendiagramm.&lt;br /&gt;
Zusätzlich zeigt es drei aktuelle Werte, die aus Label, Reading und Einheit bestehen. Zum Einrichten sind 3 Schritte notwendig.&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
Forenthread zum [https://forum.fhem.de/index.php?topic=142831.0 ControlMiniDash Widget]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 1️⃣ 99_myUtils.pm – Hilfsfunktion zum Speichern von historischen Werten ==&lt;br /&gt;
&lt;br /&gt;
Zuerst wird eine Subroutine angelegt, die neue Werte an ein Fixed-Array anhängt und alte Werte entfernt, falls die maximale Größe überschritten wird.&lt;br /&gt;
&lt;br /&gt;
 sub AppendToFixedArray {&lt;br /&gt;
     my ($dev, $reading, $newVal, $maxSize) = @_;&lt;br /&gt;
     $maxSize //= 250;  # Standard: 250 Werte&lt;br /&gt;
     my $current = ReadingsVal($dev, $reading, &#039;&#039;);&lt;br /&gt;
     my @values = split(&#039;,&#039;, $current);&lt;br /&gt;
     if (@values &amp;gt;= $maxSize) {&lt;br /&gt;
         @values = @values[-($maxSize - 1) .. -1];  # Älteste Werte entfernen&lt;br /&gt;
     }&lt;br /&gt;
     push @values, $newVal;&lt;br /&gt;
     return join(&#039;,&#039;, @values);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 2️⃣ at-Timer zum regelmäßigen Aktualisieren ==&lt;br /&gt;
&lt;br /&gt;
Mit einem `at`-Timer werden die Werte der Messgeräte regelmäßig in die `chartArray`-Readings geschrieben.  &lt;br /&gt;
&lt;br /&gt;
 defmod chartUpdate at +*00:05 {\&lt;br /&gt;
   my $val = ReadingsVal(&amp;quot;Smartmeter_2E1F50&amp;quot;, &amp;quot;APOX_Power&amp;quot;, 0);;\&lt;br /&gt;
   my $new = AppendToFixedArray(&amp;quot;Smartmeter_2E1F50&amp;quot;, &amp;quot;chartArray&amp;quot;, $val);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Smartmeter_2E1F50 chartArray $new&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val1 = ReadingsVal(&amp;quot;OpenDTU&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new1 = AppendToFixedArray(&amp;quot;OpenDTU&amp;quot;, &amp;quot;chartArray&amp;quot;, $val1);;\&lt;br /&gt;
   fhem(&amp;quot;setreading OpenDTU chartArray $new1&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val2 = ReadingsVal(&amp;quot;Kuehlschrank&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new2 = AppendToFixedArray(&amp;quot;Kuehlschrank&amp;quot;, &amp;quot;chartArray&amp;quot;, $val2);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Kuehlschrank chartArray $new2&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val3 = ReadingsVal(&amp;quot;Luftentfeuchter&amp;quot;, &amp;quot;ENERGY_Power&amp;quot;, 0);;\&lt;br /&gt;
   my $new3 = AppendToFixedArray(&amp;quot;Luftentfeuchter&amp;quot;, &amp;quot;chartArray&amp;quot;, $val3);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Luftentfeuchter chartArray $new3&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val4 = ReadingsVal(&amp;quot;Geschirrspueler&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new4 = AppendToFixedArray(&amp;quot;Geschirrspueler&amp;quot;, &amp;quot;chartArray&amp;quot;, $val4);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Geschirrspueler chartArray $new4&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val5 = ReadingsVal(&amp;quot;Trockner&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new5 = AppendToFixedArray(&amp;quot;Trockner&amp;quot;, &amp;quot;chartArray&amp;quot;, $val5);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Trockner chartArray $new5&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val6 = ReadingsVal(&amp;quot;Waschmaschine&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new6 = AppendToFixedArray(&amp;quot;Waschmaschine&amp;quot;, &amp;quot;chartArray&amp;quot;, $val6);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Waschmaschine chartArray $new6&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val7 = ReadingsVal(&amp;quot;Pumpe_FBH_Pwr&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new7 = AppendToFixedArray(&amp;quot;Pumpe_FBH_Pwr&amp;quot;, &amp;quot;chartArray&amp;quot;, $val7);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Pumpe_FBH_Pwr chartArray $new7&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val8 = ReadingsVal(&amp;quot;Heizstab_BW_Pwr&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new8 = AppendToFixedArray(&amp;quot;Heizstab_BW_Pwr&amp;quot;, &amp;quot;chartArray&amp;quot;, $val8);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Heizstab_BW_Pwr chartArray $new8&amp;quot;);;\&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3️⃣ Minichart ==&lt;br /&gt;
&lt;br /&gt;
==== 1. Widget-Datei aus contrib laden ====&lt;br /&gt;
&lt;br /&gt;
Bevor das MiniChart-Widget genutzt werden kann, muss die JS-Datei ins FHEM-Webverzeichnis kopiert werden.&lt;br /&gt;
Dies kann mit der Perlfunktion { Svn_GetFile(&#039;from SVN Path&#039;, &#039;to local Path&#039;) } direkt in der FHEM Kommandozeile erfolgen.&lt;br /&gt;
&lt;br /&gt;
 { Svn_GetFile(&#039;contrib/schwatter/fhemweb_minichart.js&#039;, &#039;/fhem/www/pgm2/fhemweb_minichart.js&#039;) }&lt;br /&gt;
&lt;br /&gt;
==== 2. Widget im Device mit widgetOverride einrichten ====&lt;br /&gt;
&lt;br /&gt;
==== Werteanzeige ====&lt;br /&gt;
Drei Textzeilen können angezeigt werden, jeweils im Format:&lt;br /&gt;
  Label@Reading@Einheit&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
  Jetzt@power@W,Heute@yieldday@kWh,Gesamt@yieldtotal@kWh&lt;br /&gt;
&lt;br /&gt;
===== Ausblenden einzelner Zeilen =====&lt;br /&gt;
Jede der drei Zeilen kann vollständig oder teilweise ausgeblendet werden, indem das jeweilige Element&lt;br /&gt;
leer gelassen wird. Das Widget setzt intern automatisch ein geschütztes Leerzeichen (&amp;amp;nbsp;).&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* Nur Label ausblenden:&lt;br /&gt;
  @reading@W&lt;br /&gt;
* Nur Reading ausblenden:&lt;br /&gt;
  Label@@W&lt;br /&gt;
* Nur Einheit ausblenden:&lt;br /&gt;
  Label@reading@&lt;br /&gt;
* Ganze Zeile ausblenden:&lt;br /&gt;
  &amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
==== Diagrammtypen ====&lt;br /&gt;
MiniChart unterstützt zwei Diagrammtypen:&lt;br /&gt;
&lt;br /&gt;
* line – geglättetes Liniendiagramm mit Farbverlauf  &lt;br /&gt;
* bar  – Balkendiagramm mit positiver/negativer Farbgebung&lt;br /&gt;
&lt;br /&gt;
Das Chart-Reading liefert eine kommagetrennte Zahlenliste, z. B.:&lt;br /&gt;
  10,20,15,18,25&lt;br /&gt;
&lt;br /&gt;
Chartoptionen:&lt;br /&gt;
  chartType@colorPositive@colorNegative&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
  line@#3b82f6@#ff0000&lt;br /&gt;
  bar@#00cc88@#cc0044&lt;br /&gt;
&lt;br /&gt;
Fallback (nur ChartReading angegeben):&lt;br /&gt;
  line@#3b82f6@#3b82f6&lt;br /&gt;
&lt;br /&gt;
=== Einrichtung im Device ===&lt;br /&gt;
&lt;br /&gt;
Syntax:&lt;br /&gt;
  &amp;lt;webCmd&amp;gt;:minichart,Label1@Reading1@Einheit,Label2@Reading2@Einheit,Label3@Reading3@Einheit,ChartReading,chartType@colorPos@colorNeg&lt;br /&gt;
&lt;br /&gt;
Beispiel Liniendiagramm:&lt;br /&gt;
  attr &amp;lt;device&amp;gt; webCmd minichart&lt;br /&gt;
  attr &amp;lt;device&amp;gt; widgetOverride minichart:minichart,Jetzt@power@W,Heute@yieldday@kWh,Gesamt@yieldtotal@kWh,chartArray,line@#3b82f6@#ff0000&lt;br /&gt;
&lt;br /&gt;
Beispiel Balkendiagramm:&lt;br /&gt;
  ..., chartArray, bar@#00cc88@#cc0044&lt;br /&gt;
&lt;br /&gt;
Fallback (Standard line + Standardfarbe):&lt;br /&gt;
  ..., chartArray&lt;br /&gt;
&lt;br /&gt;
=== Parameterübersicht ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Abschnitt&lt;br /&gt;
! Bedeutung&lt;br /&gt;
! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;lt;beliebigerName&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
| webCmd-Zuweisung&lt;br /&gt;
| Definiert die Instanz des Widgets.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;minichart&amp;lt;/code&amp;gt;&lt;br /&gt;
| Widget-Typ&lt;br /&gt;
| Aktiviert das MiniChart-Widget.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Label@Reading@Einheit&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeigezeilen&lt;br /&gt;
| Drei frei definierbare Werte-/Statusfelder; mit &amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt; komplett ausblendbar.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ChartReading&amp;lt;/code&amp;gt;&lt;br /&gt;
| Datenquelle&lt;br /&gt;
| Kommagetrennte Zahlenliste für das Chart.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;chartType@colorPos@colorNeg&amp;lt;/code&amp;gt;&lt;br /&gt;
| Chart-Optionen&lt;br /&gt;
| line oder bar; Farben optional.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Datei:Minichart_flex_mobile.png&amp;diff=40517</id>
		<title>Datei:Minichart flex mobile.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Datei:Minichart_flex_mobile.png&amp;diff=40517"/>
		<updated>2025-12-07T10:46:04Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Minichart flex mobile&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Datei:Minichart_flex_desktop.png&amp;diff=40516</id>
		<title>Datei:Minichart flex desktop.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Datei:Minichart_flex_desktop.png&amp;diff=40516"/>
		<updated>2025-12-07T10:45:31Z</updated>

		<summary type="html">&lt;p&gt;Schwatter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Minichart flex desktop&lt;/div&gt;</summary>
		<author><name>Schwatter</name></author>
	</entry>
</feed>