Zum Inhalt springen

FHEMWEB/VoiceControl: Web-STT & Hardware-Wakeword: Unterschied zwischen den Versionen

Aus FHEMWiki
Schwatter (Diskussion | Beiträge)
Keine Bearbeitungszusammenfassung
Schwatter (Diskussion | Beiträge)
Keine Bearbeitungszusammenfassung
 
(2 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 11: Zeile 11:
* Weg 1️⃣ (Software): Browser-basierte Komplettlösung
* Weg 1️⃣ (Software): Browser-basierte Komplettlösung
* Weg 2️⃣ (Hybrid): Hardware-Wakeword + Browser-Spracherkennung
* Weg 2️⃣ (Hybrid): Hardware-Wakeword + Browser-Spracherkennung


=== Hilfe ===
=== Hilfe ===
* Forenthread zum {{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}
* Forenthread zum {{Link2Forum|Topic=144147|LinkText=VoiceControl Sprachsteuerung}}
Dort befinden sich in Post #1 auch alle benötigten Dateien.




Zeile 57: Zeile 57:
<syntaxhighlight lang="javascript">
<syntaxhighlight lang="javascript">
const wakewords = ["james"];</syntaxhighlight>
const wakewords = ["james"];</syntaxhighlight>
Ablauf:
# „James“ sagen
# System antwortet „Ja?“
# Zeitfenster (~6 Sekunden) für Befehl
# Danach automatische Verarbeitung oder Abbruch


=== Installation ===
=== Installation ===

Aktuelle Version vom 3. April 2026, 19:00 Uhr

VoiceControl – Sprachsteuerung via Browser und Atom Echo s3r

Diese Lösung ermöglicht eine flexible Sprachsteuerung für FHEM. Sprache wird in Text (Speech-to-Text) umgewandelt und als Reading STT im Device global bereitgestellt. Dieses Reading kann anschließend zentral (z. B. über notify oder DOIF) ausgewertet werden.

Unterstützt werden nur Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox hat leider kein Backend.

Es gibt zwei unterschiedliche Wege zur Spracherfassung:

  • Weg 1️⃣ (Software): Browser-basierte Komplettlösung
  • Weg 2️⃣ (Hybrid): Hardware-Wakeword + Browser-Spracherkennung


Hilfe


Funktionen

Grundprinzip

  • Sprache → Speech-to-Text
  • Ergebnis → Reading STT im Device global
  • Zentrale Logik verarbeitet Befehle

Betriebsarten

  • Push-to-Talk (nur Browser)
  • Always-On mit Wakeword
  • Hardware-Wakeword (Hybrid)

Rückmeldungen

  • Sprachausgabe (TTS)
  • Visuelle Bubble im Browser
  • Optional gezielte Rückmeldung per Client-ID


Weg 1️⃣: Browser-Lösung (voicecontrol.js)

Das Script nutzt die Google Web Speech API. Unterstützt werden Chromium-basierte Browser (Chrome, Edge, Fully Browser). Firefox wird aktuell nicht unterstützt.

Bedienung

Push-to-Talk

  • Button gedrückt halten
  • Wakeword erforderlich (Standard: „James“)
  • Nach Aktivierung ca.6sek Zeit für Befehl

Always-On

  • Kurzer Klick aktiviert Dauerbetrieb
  • Wakeword erforderlich (Standard: „James“)
  • Nach Aktivierung ca.6sek Zeit für Befehl
  • JS wird neu gestartet udn Schleife fängt von vorne an

Wakeword

  • Standard: james
  • Anpassbar im Script:
const wakewords = ["james"];

Installation

Datei kopieren

{ Svn_GetFile('contrib/voicecontrol.js', 'www/pgm2/voicecontrol.js') }

Einbindung

attr WEBphone JavaScripts www/pgm2/voicecontrol.js

Hinweis (HTTP ohne HTTPS)

Chrome benötigt Freigabe für Mikrofon:

  • chrome://flags/#unsafely-treat-insecure-origin-as-secure
  • Eigene URL hinzufügen
  • Auf Enabled setzen


Weg 2️⃣: Hybrid-Lösung mit Browser + Atom Echo (voicecontrol_echo.js)

Diese Variante kombiniert Hardware und Software:

  • Wakeword-Erkennung erfolgt auf dem ESP (Atom Echo s3r)
  • Die eigentliche Sprachverarbeitung (Speech-to-Text) erfolgt im Browser

Funktionsweise

  1. Wakeword wird auf dem ESP erkannt
  2. FHEM erzeugt Event (z. B. james_detected)
  3. Browser empfängt Event via WebSocket
  4. Speech-to-Text startet im Browser
  5. Ergebnis wird an FHEM übertragen

Aktivierung

  • Kurzer Klick auf Button aktiviert/deaktiviert System
  • Baut WebSocket-Verbindung zu FHEM auf
  • Kein Push-to-Talk-Modus

Installation

Datei kopieren

Die Datei voicecontrol_echo.js ist hier zu finden: VoiceControl Sprachsteuerung und muss nach www/pgm2/voicecontrol_echo.js kopiert werden.

Die echo_s3r.yaml ist für den Atom Echo s3r. Die Konfuguration ist weiter unten beschrieben.

Einbindung

attr WEBtablet JavaScripts www/pgm2/voicecontrol_echo.js

Konfiguration im Javascript

const DEVICE  = "atom_echos3r_9888e00f4280";
const TRIGGER = "james_detected";
const FHEM_IP = "192.168.1.76:8085";
  • DEVICE → FHEM-Device des ESP
  • TRIGGER → Event bei Wakeword
  • FHEM_IP → FHEM-Server

Konfiguration in der Yaml

Damit sich der ESP mit eurem MQTT-Server und WLAN verbindet, müssen folgende Stellen angepasst werden.

  wifi:
    ssid: "YOUR_SSID"
    password: "YOUR_PW"
    fast_connect: true
  mqtt:
    broker: 192.168.1.76
    port: 1884
    username: "YOUR_USERNAME"
    password: "YOUR_PW"
    topic_prefix: atom_echo


Wakeword (ESP / ESPHome)

Das Wakeword wird direkt auf dem ESP definiert:

  • Umsetzung über ESPHome
  • Eine große Auswahl an Wakewords sind hier zu finden:
  https://github.com/TaterTotterson/microWakeWords

Hier ein Beispiel, wie das Wakeword definiert wird.

  micro_wake_word:
    id: mww
    microphone: echo_mic
    models:
      - model: "https://github.com/TaterTotterson/microWakeWords/raw/main/microWakeWordsV2/james.json"
        id: james_model
        probability_cutoff: 0.6   

Kommunikation

  • WebSocket-Verbindung zu FHEM
  • Lauscht auf Device-Events
  • Automatischer Reconnect bei Abbruch

Ablauf nach Wakeword

  1. System sagt „Ja?“
  2. Browser startet SpeechRecognition
  3. Nutzer spricht Befehl
  4. Befehl wird verarbeitet
  5. Rückmeldung „Erledigt“

Zentrale Auswertung (Logik)

Beide Wege schreiben in:

global:STT

Die Verarbeitung erfolgt zentral über ein notify. Ein Device wird in der Mappingtabelle eingetragen.

Beispiel: "esszimmer:licht|lampe|deckenlampe" => { dev => "Lampe01_Ez", label => "Licht Esszimmer", cmdOn => "on", cmdOff => "off" }

Aufschlüsselung: "hauptkeyword:Filter1|Filter3|Filter3" => { dev => "Devicename", label => "Übersichtname", cmdOn => "on", cmdOff => "off" }

Beispiel: notify

defmod n_VoiceControl notify global:STT:.* {\
    # --- VORBEREITUNG ---\
    my ($cleanEvent, $clientId) = $EVENT =~ /^(.*)\s\[(.*)\]$/;;\
    $cleanEvent //= $EVENT;;\
    $clientId   //= "unknown";;\
\
    # --- KONFIGURATION: Zentrales Mapping ---\
    my %smartHomeDevices = (\
        "esszimmer:licht|lampe|deckenlampe" => { dev => "Lampe01_Ez", label => "Licht Esszimmer",  cmdOn => "on", cmdOff => "off" },\
        "esszimmer:aquarium" => { dev => "Aquarium_Aktor", label => "Aquarium", cmdOn => "on", cmdOff => "off" },\
        "küche" => { dev => "Deckenlampe_Kue", label => "Licht Küche", cmdOn => "on", cmdOff => "off" },\
        "wohnzimmer" => { dev => "Lampe06_Dek", label => "Licht Wohnzimmer", cmdOn => "on", cmdOff => "off" },\
        "fernseher|tv" => { dev => "VuPlus", label => "Fernseher", cmdOn => "on", cmdOff => "off" },\
        "rechner|pc" => { dev => "PC_Aktor", label => "PC", cmdOn => "on", cmdOff => "off" },\
        "garage|tor" => { dev => "Garagentor_Aktor", label => "Garagentor öffnen/schließen", cmdOn => "open", cmdOff => "close" },\
        "kaffee" => { dev => "Kaffeemaschine", label => "Kaffeemaschine", cmdOn => "on", cmdOff => "off" },\
        "roberto" => { dev => "MQTT2_valetudo_FlusteredUnequaledFish", label => "Lade Roberto", cmdOn => "charge" },\
        "ambiente" => { dev => "LampeSzeneAlle", label => "Zentrales Licht", type => "dimmer", cmdOn => "on", cmdOff => "off" },\
        "sauge|reinige|putze|staubsauger|roboter" => { dev => "MQTT2_valetudo_FlusteredUnequaledFish", label => "Staubsauger", type => "vacuum" },\
         "ambilight" => { label => "Ambilight", type => "system", cmd => "sshpass -p 'GEHEIMESPASSWORD' ssh -o StrictHostKeyChecking=no root\@192.168.1.46 '/usr/share/hyperhdr/scripts/hyperhdr_toggle.sh'" }\
    );;\
\
    my %vacRooms = ("arbeitszimmer" => "Arbeitszimmer", "badezimmer" => "Badezimmer", "esszimmer" => "Esszimmer", "flur" => "Flur", "küche" => "Küche", "wohnzimmer" => "Wohnzimmer");;\
\
    my $onRegEx  = '\b(an|ein|einschalten|starte|aktivier|aktiviere|öffne|öffnen|auf|hoch|lade)\b';;\
    my $offRegEx = '\b(aus|ausschalten|stop|stoppe|beende|deaktivier|deaktiviere|schließe|schließen|zu|runter)\b';;\
\
    my @commands = split(/\s*(?:und|dann|,)\s*/, lc($cleanEvent));;\
\
    # --- DER BEFEHLS-LOOP ---\
    foreach my $cmd_part (@commands) {\
        $cmd_part =~ s/^\s+|\s+$//g;;\
        my $is_on  = ($cmd_part =~ /$onRegEx/) ? 1 : 0;;\
        my $is_off = ($cmd_part =~ /$offRegEx/) ? 1 : 0;;\
        $cmd_part =~ s/\b(ich|brauche|mach|bitte|kannst du|würdest du|mal|doch|den|das|die|im|in der)\b//g;;\
\
        # --- GENERISCHE STEUERUNG ---\
        my $match = 0;;\
        # Sortierung stellt sicher, dass 'dimmer/vacuum' Vorrang haben\
        foreach my $key (sort { ($smartHomeDevices{$b}{type}//"") cmp ($smartHomeDevices{$a}{type}//"") } keys %smartHomeDevices) {\
            \
            my ($main, $must) = split(/:/, $key);;\
            \
            # Prüfe Hauptwort\
            if ($cmd_part =~ /\b($main)\b/i) {\
                # Prüfe Pflichtwort falls vorhanden\
                next if ($must && $cmd_part !~ /\b($must)\b/i);;\
                \
                my $d = $smartHomeDevices{$key};;\
\
                # A) System\
                if (($d->{type} // "") eq "system") {\
                    system($d->{cmd});;\
                }\
                \
                # B) Staubsauger\
                if (($d->{type} // "") eq "vacuum") {\
                    my @found = grep { $cmd_part =~ /\b$_\b/ } keys %vacRooms;;\
                    fhem(@found ? "set $d->{dev} clean_segment ".join(",", map { $vacRooms{$_} } @found) : "set $d->{dev} start");;\
                }\
                # C) Dimmer (Kompakt & korrigiert)\
                elsif (($d->{type} // "") eq "dimmer") {\
                    $cmd_part =~ /(\d+)/ \
                        ? fhem("set $d->{dev} brightness " . int(($1 > 100 ? 100 : $1) * 2.55)) \
                        : ($is_on || $is_off) && fhem("set $d->{dev} " . ($is_off ? "off" : "on"));;\
                }\
                # D) Standard An/Aus\
                else {\
                    my $fhem_cmd = $is_off ? ($d->{cmdOff} // "off") : ($d->{cmdOn} // "on");;\
                    fhem("set $d->{dev} $fhem_cmd") if ($is_on || $is_off);;\
                }\
                \
                $match = 1;; last;;\
            }\
        }\
        next if $match;;\
\
        # --- HILFE ---\
        if ($cmd_part =~ /(hilfe|kommandos|übersicht)/) {\
            my $h = '<div style="text-align:left;;min-width:250px;;font-family:sans-serif;;"><b>Befehlsübersicht:</b><br><br>';;\
            my %seen;;\
            for my $k (sort keys %smartHomeDevices) {\
                my $d = $smartHomeDevices{$k};;\
                next if $seen{$d->{label}}++;; \
                my $suffix = ($d->{cmdOn} && $d->{cmdOff} && $d->{label} !~ /(an\/aus|öffnen\/schließen)/i) ? " (an/aus)" : "";;\
                $suffix = " [0-100%]" if ($d->{type} // "") eq "dimmer";;\
                $h .= "• $d->{label}$suffix<br>";;\
            }\
            $h .= '<br><u>Staubsauger Räume</u><br>• '.join(", ", map { ucfirst($_) } sort keys %vacRooms).'<br></div>';;\
            $h =~ s/'/\\"/g;;\
            my $js = "if((document.querySelector('input[name=\"fw_id\"]')||{}).value==='$clientId'){FW_okDialog('$h')}";;\
            FW_directNotify("#FHEMWEB:$_", $js, "") for devspec2array("TYPE=FHEMWEB");;\
        }\
    }\
}


Unterschiede der Wege

Feature Browser Hybrid (Atom Echo S3)
Wakeword Browser ESP (Hardware)
Push-to-Talk Ja Nein
Always-On Ja (bedingt) Ja
Architektur Software Software + Hardware
Stabilität Sehr stabil Sehr stabil (Wakeword extern)


FireOS mit Fully Browser (Plus)

Damit die Spracherkennung funktioniert:

  • Apps installieren:
  Google
  Speech Recognition & Synthesis
  • FireOS:
  „Tastatur und Sprache“ → „Text-to-Speech“
  • Fully Browser:
  Enable JavaScriptInterface (PLUS) aktivieren