(WIP) FHEMWEB interaktiv (speziell mit RHASSPY)

Begonnen von Beta-User, 03 April 2026, 11:24:46

Vorheriges Thema - Nächstes Thema

Beta-User

Hallo zusammen,

mal wieder ein "work in Progress"-Thread...

Was bisher geschah:
Rudi hat f18 etwas aufgebohrt, so dass man per Klick gesprochene Sprache an FHEM übermitteln kann, die Vorarbeiten (nochmals ein FETTES DANKE an @schwatter) sind in [Voicecontrol] Button für Fhemweb zu finden.

Mein Ausgangspunkt war eigentlich, das FULLY-Modul so zu erweitern, das es mit RHASSPY in Punkto Sprachein- und -ausgabe so zusammenarbeitet, wie das vorher mit AMAD möglich gewesen war, insbesondere also interaktive Dialoge möglich sind. Wie es im Moment aussieht, braucht es dafür aber auch einiges an javascript, so dass es letztlich (fast) völlig egal ist, welchen Browser man verwendet...

Wer hier also mit testen und entwickeln will: Am unkompliziertesten scheint Chrome (unter Android) zu sein, den man zwischenzeitlich auch mit FHEM als "WebApp" fullscreen starten kann (super Anleitung hier https://forum.fhem.de/index.php?msg=1360621).

Mein erster Stolpersteil war die Frage, wie man das Mikro wieder aktivieren kann. Dazu hier erst mal ein snipplet (Danke an schwatter für die Vorlage!), wie das mit dem in f18 eingebauten stt-Code geht:

        my $js = "if((document.querySelector('input[name=\"fw_id\"]')||{}).value==='$hash->{FW_ID}'){f18_stt()}";
        FW_directNotify("#FHEMWEB:$_", $js, "")
            for devspec2array("TYPE=FHEMWEB");

Was aus mir bisher nicht bekannten Gründen nicht funktioniert:
     
        FW_directNotify("#FHEMWEB:$_", "f18_stt()", "")
            for devspec2array("TYPE=FHEMWEB:FILTER=FW_ID=$hash->{FW_ID}");


Wo soll das hinführen?

1. Spracheingabe wird aktiviert
2. Man sagt was (z.B. "wie spät ist es")
3. Es gibt ein Event am FHEMWEB-Device, so dass identifizierbar ist, wo genau der Text eingesprochen wurde. Stand jetzt wäre es auch an der FULLY-Instanz sichtbar, und FULLY leitet das auch schon zur Beantwortung an RHASSPY weiter.
4. Das Event wird verarbeitet in einer NotifyFn() (bzw. in FULLY direkt per spezieller set-Anweisung), also ein Gerät wird geschaltet, oder hier einfach: Die Antwort auf die Frage wird ermittelt.
5. (optional): Der Antworttext wird in Audio umgewandelt
6. An der Stelle, von der die Frage (bzw. Anweisung) kam, erfolgt eine Ausgabe, entweder als TTS-Anweisung, oder als "play audio"-Befehl (mit FULLY+RHASSPY bereits als TTS-Anweisung funktional)
=> der Einsprechende erhält seine Rückmeldung.

7. (optional) Er kann eine weitere Anweisung einsprechen, oder ggf. eine Rückfrage beantworten. Hier kommt dann das obige snipplet zum Tragen, und das Spiel kann von vorne (bei 2.) beginnen. Das (nächste zu lösende) Problem ist demnach: Wann ist der richtige Zeitpunkt dafür? Tendenziell muss unser javascript noch ein paar Events erzeugen, aus dem man ablesen kann, was der Status der Audio-Geräte (Mikro/Sounausgabe) gerade ist.

Wie man sieht: Das hat im Kern wenig mit RHASSPY zu tun, das funktioniert auch z.B. mit Babble vermutlich kaum anders...

Das soll es für den Moment erst mal gewesen sein :) .

Wer mitexperimentieren will, kann sich einfach hier melden.
Server: HP-elitedesk@Debian 13, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: ZigBee2mqtt, MiLight@ESP-GW, BT@OpenMQTTGw | ZWave | SIGNALduino | MapleCUN | RHASSPY
svn: u.a Weekday-&RandomTimer, Twilight,  div. attrTemplate-files, MySensors

Beta-User

Als Basis erst mal ein JavaScript auf Basis der Vorarbeit von schwatter. Liegt bei mir in www/pgm2 und ist via Javascripts-Attribut in FHEMWEB aktiviert.

Da ist im Moment noch nicht viel anders wie vorher, außer dass es unter Chromium@Linux nicht läuft, unter Chrome@Android aber schon...+

Anders ist:
- Der eingesprochene Text wird kurz angezeigt ("eigentlich" sollte das "on-the-fly" auch mit Teilen erfolgen...) und dann automatisch an FHEM übermittelt
- Die Aktivierung erfolgt im Moment nur durch Klicken, es sollte auch gehen, das von FHEM aus zu aktivieren.
- Da man dazu die FW_ID benötigt, erzeugt diese Version die STT-Events/Readings (auch*) an der temporären FHEMWEB-Instanz, die zur betreffenden Verbindung gehört.
*Zusätzlich kommt ein Event an der FULLY-Instanz, falls man fully als Browser benutzt und die deviceid paßt.

Braucht alles noch Feinschliff, geht erst mal v.a. drum, auch die Zwischenschritte zu dokumentieren...
Server: HP-elitedesk@Debian 13, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: ZigBee2mqtt, MiLight@ESP-GW, BT@OpenMQTTGw | ZWave | SIGNALduino | MapleCUN | RHASSPY
svn: u.a Weekday-&RandomTimer, Twilight,  div. attrTemplate-files, MySensors

schwatter

#2
Nabend,

zu dem Codesnippet oben. Wo probiert du das gerade aus? In Fully, FHEMWEB oder mit f18. Muss fragen,
da ich den Überblick verloren habe.
Vielleicht noch ein paar Details zu deinen Devices, um sich ein Bild zu machen.

Edit:
Ich sehe du hast schon geantwortet. Ich gehe mal dem Problem mit Chromium nach.
Ich muss gestehen, das ich hier und da geschrieben habe Chrome/Chromium funktioniert,
ich habe es aber nie selber ausprobiert. Bin einfach davon ausgegangen, datt geht.
Melde mich wegen Chromium.

Gruß schwatter

Beta-User

#3
Der JavaScript-Code wird via FHEMWEB-Instanz geladen, vermutlich werde ich dazu künftig zu weiteren Tests eine eigene Instanz aufmachen.

Das Mikro wird dann (außer bei Chromium@Linux) in FF@Linux/Android, Chrome@Android und fully unten angezeigt.

Getestet habe ich dann unter Android sowohl mit fully wie mit Chrome.

fully spricht den set-Command an der betreffenden FULLY-Instanz an, der dann (gesteuert über ein Attribut) den Text an RHASSPY weiterleitet. Das wertet (analog zu deinem notify, aber sehr viel differenzierter...) den Text aus, schaltet ggf. entsprechend der Anweisungen ("Schalte das Radio im Esszimmer und Wohnzimmer an und das Licht am Esstisch aus"...) und gibt Rückmeldung (als Text) an FULLY, der das dann an fully aussprechen läßt (derzeit...).

Chrome erzeugt im Moment nur Events. Der Plan ist, RHASSPY per NotifyFn() darauf anzusetzen und dann direkt in FHEMWEB (via FW_directNotify()) die Antwort aussprechen zu lassen. (Der Code ist im Prinzip derselbe wir für AMAD, also zu 90% schon da...)

Letzteres ist dann mein nächster Step, wobei dazu optimalerweise ein "speak" mit nachgelagertem "schalte das Mikro wieder scharf, wenn du fertig mit sprechen bist" in den Code kommt...

Raw-Definitionen von den Devices dürften im Moment nicht viel weiterhelfen. Doku zu RHASSPY gibt es ausführlich im Wiki (https://wiki.fhem.de/wiki/RHASSPY).
Auszugsweise, was man (außer, das in RHASSPY per devspec zu erfassen) konfigurieren muss, um das vernünftig (an/aus, Helligkeit, colortemp, Farbe...) aus RHASSPY heraus schalten zu können:
defmod Licht_Essen MQTT2_DEVICE zigbee_Esstisch
attr Licht_Essen genericDeviceType light
attr Licht_Essen rhasspyName licht am esstisch

Oder der Receiver:
defmod Yamaha_Main YAMAHA_AVR <IP-Adresse>
attr Yamaha_Main genericDeviceType media
attr Yamaha_Main rhasspyMapping GetNumeric:currentVal=volume,type=volume\
SetNumeric:currentVal=volume,cmd=volume,minVal=0,maxVal=99,step=2,type=volume\
SetOnOff:cmdOn=on,cmdOff=off\
GetOnOff:currentVal=state,valueOff=off\
GetState:response=Verstärker ist [Yamaha_Main:state] die Lautstärke ist [Yamaha_Main:volume]
attr Yamaha_Main rhasspyName verstärker,receiver,radio
attr Yamaha_Main rhasspyRoom wohnzimmer
attr Yamaha_Main rhasspySpecials scenes:scene1="Musik hören" scene2="Film ansehen" scene3=none scene4="vorderen Eingang auswählen"\
priority: inRoom=volume outsideRoom=volume,scene\
confirm: SetOnOff="wirklich $target $Value schalten?" SetScene

Ist das jetzt etwas klarer?
Server: HP-elitedesk@Debian 13, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: ZigBee2mqtt, MiLight@ESP-GW, BT@OpenMQTTGw | ZWave | SIGNALduino | MapleCUN | RHASSPY
svn: u.a Weekday-&RandomTimer, Twilight,  div. attrTemplate-files, MySensors

schwatter

#4
Ok,
danke. Dein JS habe ich kurz reingeschaut. Du hast einfach den Freischaltprocess ausgeschaltet um direkt Google SpeechToText zu starten.
Ja Chromium, da werde ich nochmal in Ruhe schauen warum der Button nicht auftaucht.
5 Minuten Suche im Internet hat mir aber offenbart, genau so schlecht wie bei Firefox.
Quasi wie ein AOSP Rom für ein Androidhandy. Der is blank. Keys kann man selber einbauen aber....ist mist. Fully z.B greift da auf Android Systemdienste zu.

Gruß schwatter

schwatter

#5
Moin,

hier mal ein Codeschnipsel mit dem du TextToSpeach machen kannst. 2ter Informchannel auf ein reading deiner
Wahl. Mein notify macht jetzt am Ende ein setreading voiceSpeak in global.

    function startExternalTunnel() {
        if (!isJamesActive) return;

        if (jamesSocket) jamesSocket.close();
        if (globalSocket) globalSocket.close();

        const protocol = location.protocol === "https:" ? "wss:" : "ws:";
        const combinedFilter = `(${DEVICE}|global)`;
        jamesSocket = new WebSocket(`${protocol}//${FHEM_IP}/fhem?XHR=1&inform=type=status;filter=${combinedFilter}`);

        jamesSocket.onmessage = (e) => {
            if (!isJamesActive) return;
            if (!isWaitingForCommand && !isSpeaking && e.data.includes(DEVICE) && e.data.includes(TRIGGER)) {
                startJamesSTT();
            }
            if (e.data.includes("global-voiceSpeak")) {
                let raw = e.data.split('global-voiceSpeak')[1];
                let txt = raw.replace(/^[^\w\däöüÄÖÜß]+/, '').split(/[",\]<]/)[0];
               
                txt = txt.replace(/_/g, ' ').trim();
                if (txt && txt !== "no" && txt !== "definition") {
                    showBubble("🤖 " + txt);
                    speak(txt);
                }
            }
        };
       
        jamesSocket.onopen = () => console.log("DEBUG: James-Kombi-Socket verbunden");
    }

edit:
Nochmal angepasst auf eine Websocketverbindung mit Filter.
 

Gruß schwatter

Beta-User

Zitat von: schwatter am 03 April 2026, 21:46:11Ja Chromium, da werde ich nochmal in Ruhe schauen warum der Button nicht auftaucht.
Bei meinem Code dachte ich, es läge nach der JS-Konsole an der Syntax. Jetzt ist das Mikro aber auch an Chromium@Linux da. Es tut nur nix in Richtung STT-Reading :o .

Zitat von: schwatter am 04 April 2026, 08:27:512ter Informchannel auf ein reading deiner Wahl
:)
Der Ansatz, dafür einen socket von der JS-Seite her aufzumachen, will zumindest im Moment nicht zu meinen Design-Vorstellungen passen:
Der js-Code soll am Ende ganz ohne irgendwelche Vorab-Parametrierungen (const DEVICE = "atom_echos3r_9888e00f4280"; und so) auskommen, der diesbezügliche Block am Anfang ist im Moment nur noch deswegen drin, weil der Code erst mal nicht zu sehr verändert werden sollte, um die Lauffähigkeit zu erhalten ;D .

Die Idee wäre, aus dem Endgerät (und eventuell dem verwendeten wakeword oä.) abzulesen, wer da gerade sprechen will, um dann die Parametrierung des aktuell im Browser laufenden Codes und/oder von FHEM so anzupassen, dass z.B. "er" auf seinem Endgerät deutsche Ein- und Ausgaben erhält, und "sie" auf ihrem spanische...
(Oder die Kinder bestimmte Dinge nicht dürfen?)

Vielleicht wäre eine Funktion hilfreich, die einen JSON-Blob entgegennimmt, aus dem sich alle jeweils aktuell erforderlichen Infos ableiten lassen. Falls du da eine Idee hast: Gerne!!!
Server: HP-elitedesk@Debian 13, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: ZigBee2mqtt, MiLight@ESP-GW, BT@OpenMQTTGw | ZWave | SIGNALduino | MapleCUN | RHASSPY
svn: u.a Weekday-&RandomTimer, Twilight,  div. attrTemplate-files, MySensors

schwatter

Ich konnte die Websocketverbindung auf eine reduzieren. Siehe oben. Aber so wie bei
dir, das passt auch nicht zu meinen Designvorstellungen. Problem, fhemweb.js
Lebt ihm hier und jetzt, bzw abonniert nur Devices bei Raum- oder Deviceübersicht.
Direkt ans Backendfiltern klappt nicht? Wenn doch, vielleicht hat wer ein Beispiel?
Oder ich bin gerade zu doof  :-[

Gruß schwatter

Beta-User

Zitat von: schwatter am 04 April 2026, 10:00:35Lebt ihm hier und jetzt, bzw abonniert nur Devices bei Raum- oder Deviceübersicht.
Das ist in der Tat ein Problem.

Zitat von: schwatter am 04 April 2026, 10:00:35Wenn doch, vielleicht hat wer ein Beispiel?
Hmm, ich _glaube_, du hattest ein passendes Beispiel geliefert!!!

Meine Mikro-Aktivierung
Zitat von: Beta-User am 03 April 2026, 11:24:46        my $js = "if((document.querySelector('input[name=\"fw_id\"]')||{}).value==='$hash->{FW_ID}'){f18_stt()}";
        FW_directNotify("#FHEMWEB:$_", $js, "")
            for devspec2array("TYPE=FHEMWEB");
basiert auf deinem notify-Code, jetzt zu finden unter https://wiki.fhem.de/wiki/FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword#Beispiel:_notify, dort der Abschnitt #Hilfe.
Damit machst du was genau? Du sendest an eine mehr oder weniger unbekannte Stelle formatierten Text hin, um den in genau einem FHEMWEB-Client anzuzeigen... Das müßte doch eigentlich genauso für TTS-Infos gehen ;) , oder stehe ich auf dem Schlauch?

Server: HP-elitedesk@Debian 13, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: ZigBee2mqtt, MiLight@ESP-GW, BT@OpenMQTTGw | ZWave | SIGNALduino | MapleCUN | RHASSPY
svn: u.a Weekday-&RandomTimer, Twilight,  div. attrTemplate-files, MySensors

Beta-User

Thx to Rudi gibt es jetzt auch eine speak-Funktion in f18 :) . (=> https://forum.fhem.de/index.php?msg=1361205).

Damit kann man RHASSPY jetzt aus Chrome@Android wie folgt befragen, zum Testen habe ich eine separate FHEMWEB-Instanz aufgemacht, und die mit zwei allowed-Devices abgesichert, und beim einen einen ROOMMATE als username und damit erlaubtem Partner abgesichert:

defmod WEBVOICE FHEMWEB 8084 global
attr WEBVOICE HTTPS 1
attr WEBVOICE styleData {\
 "f18": {\
  "Pinned.menu": false,\
[...]
  "showMicro": true\
 }\
}
attr WEBVOICE stylesheetPrefix f18
attr WEBVOICE title { if($FW_room) { "FHEM: $FW_room" } elsif($FW_detail) { "FHEM: $FW_detail" } else { "Home, Sweet Home" } }

defmod allowed_rr_Frau allowed WEBVOICE
attr allowed_rr_Frau basicAuth <user sollte dem ROOMATE-Namen entsprechen>
attr allowed_rr_Frau validFor WEBVOICE

defmod allowed_rr_Mann allowed WEBVOICE
attr allowed_rr_Mann basicAuth <user muss dem ROOMATE-Namen entsprechen>
attr allowed_rr_Mann validFor WEBVOICE

Dann den ROOMMATE (rr_Mann) neben dem (nicht notwendigen, aber noch dort stehenden AMADDevice) eingetragen.
attr rhasspy rhasspySpeechDialog allowed=TabletWohnzimmer,rr_Mann

Fertig*...

(update ab morgen, oder aus dem svn holen)

*noch lange nicht, aber es funktioniert prinzipiell, dass man eine Antwort genau da erhält, wo man die Frage bzw. Anweisung eingesprochen hatte 8)
Server: HP-elitedesk@Debian 13, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: ZigBee2mqtt, MiLight@ESP-GW, BT@OpenMQTTGw | ZWave | SIGNALduino | MapleCUN | RHASSPY
svn: u.a Weekday-&RandomTimer, Twilight,  div. attrTemplate-files, MySensors

schwatter

Zitat von: Beta-User am 04 April 2026, 11:48:18
Zitat von: schwatter am 04 April 2026, 10:00:35Lebt ihm hier und jetzt, bzw abonniert nur Devices bei Raum- oder Deviceübersicht.
Das ist in der Tat ein Problem.

Zitat von: schwatter am 04 April 2026, 10:00:35Wenn doch, vielleicht hat wer ein Beispiel?
Hmm, ich _glaube_, du hattest ein passendes Beispiel geliefert!!!

Meine Mikro-Aktivierung
Zitat von: Beta-User am 03 April 2026, 11:24:46        my $js = "if((document.querySelector('input[name=\"fw_id\"]')||{}).value==='$hash->{FW_ID}'){f18_stt()}";
        FW_directNotify("#FHEMWEB:$_", $js, "")
            for devspec2array("TYPE=FHEMWEB");
basiert auf deinem notify-Code, jetzt zu finden unter https://wiki.fhem.de/wiki/FHEMWEB/VoiceControl:_Web-STT_%26_Hardware-Wakeword#Beispiel:_notify, dort der Abschnitt #Hilfe.
Damit machst du was genau? Du sendest an eine mehr oder weniger unbekannte Stelle formatierten Text hin, um den in genau einem FHEMWEB-Client anzuzeigen... Das müßte doch eigentlich genauso für TTS-Infos gehen ;) , oder stehe ich auf dem Schlauch?



Hey, ne. Da reden wir aneinander vorbei. Mir ging es darum, im Webinterface direkt per inform Updates zu Devices
zu bekommen, welche nicht in einem Raum/DeviceOverview sind. Per FW_directNotify wird ja nur ein JS-Befehl abgeschickt.

Gruß schwatter

Beta-User

Zitat von: schwatter am 05 April 2026, 20:49:19Hey, ne. Da reden wir aneinander vorbei. Mir ging es darum, im Webinterface direkt per inform Updates zu Devices zu bekommen, welche nicht in einem Raum/DeviceOverview sind.
Hmm, ok.
Vermutlich fehlen mir da noch ein paar weitere Verständnis-Brocken. Mein Ansatz scheint etwas anders zu sein, nämlich, dass das js "nichts" zu "wissen" braucht, also z.B. die Verbindung zwischen einem wakeword-Event an einem ESP (das wird dauern...) und einem bestimmten Endgerät dann eher über ein notify hergestellt werden soll. Dazu braucht es verlässliche Info auf der FHEM-Seite, was im Moment nur über fully/FULLY 1:1 geht, oder wohl auch z.B. über die IP des Endgeräts, falls man die fest einstellt.


Für den nächsten Schritt kämpfe ich mit piper-tts...
Der Server an sich läuft, was auch die empfohlene Variante für schnelle Reaktion wäre, aber die Anfrage-Syntax ist anders als bei z.B. maryTTS, https://github.com/OHF-Voice/piper1-gpl/blob/main/docs/API_HTTP.md:
curl -X POST -H 'Content-Type: application/json' -d '{ "text": "This is a test." }' -o test.wav localhost:5000
Das Text2Speak-Modul "kann" zwar maryTTS, aber für diesen Call müßte man das mal wieder aufbohren, und außerdem gefällt mir das "Zwischenlagern" von files nicht...

Via https://curlconverter.com/javascript/ bin ich dann auf diesen Testcode gekommen:
function
f18_speak2(txt)
{
//curl -X POST -H 'Content-Type: application/json' -d '{ "text": "This is a test." }' -o test.wav
  var url = fetch('http://<server-ip>:5000', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Access-Control-Allow-Origin': 'http://192.168.2.70'
    },
  // body: '{ "text": "This is a test." }',
    body: JSON.stringify({
      'text': 'Hier spricht Thorsten!'
    })
  });

  //  console.log('PLAY:', url);

  var a = new Audio(url);
  a.addEventListener("ended", function(){
     a.currentTime = 0;
     var fw_id = $("body").attr("fw_id");
     FW_cmd(`${FW_root}?cmd=setreading `+
            `TYPE=FHEMWEB:FILTER=FW_ID=${fw_id}:FILTER=inform=.%2B `+
            `TTS_state audio finished&XHR=1`);
  });

  a.play().catch(function(e){
    console.log('audio blocked:', e);
  });
}
Nun ja, über die js-console gibt es jetzt entweder die Rückmeldung, dass die Quelle HTTPS sein sollte, und irgendwas mit CORS. Mal wieder Neuland...
Server: HP-elitedesk@Debian 13, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: ZigBee2mqtt, MiLight@ESP-GW, BT@OpenMQTTGw | ZWave | SIGNALduino | MapleCUN | RHASSPY
svn: u.a Weekday-&RandomTimer, Twilight,  div. attrTemplate-files, MySensors

schwatter

Zitat von: Beta-User am 14 April 2026, 07:42:32
Zitat von: schwatter am 05 April 2026, 20:49:19Hey, ne. Da reden wir aneinander vorbei. Mir ging es darum, im Webinterface direkt per inform Updates zu Devices zu bekommen, welche nicht in einem Raum/DeviceOverview sind.
Hmm, ok.
Vermutlich fehlen mir da noch ein paar weitere Verständnis-Brocken. Mein Ansatz scheint etwas anders zu sein, nämlich, dass das js "nichts" zu "wissen" braucht, also z.B. die Verbindung zwischen einem wakeword-Event an einem ESP (das wird dauern...) und einem bestimmten Endgerät dann eher über ein notify hergestellt werden soll. Dazu braucht es verlässliche Info auf der FHEM-Seite, was im Moment nur über fully/FULLY 1:1 geht, oder wohl auch z.B. über die IP des Endgeräts, falls man die fest einstellt.

Bei dem JS im contrib verfolge ich mehr oder weniger auch den Weg, das es fast nichts kennen muss. Außer
Wakeword und wo das Readings STT ist. Das JS für den Atom Echo muss aber irgendwo die Info herbekommen, das es das
Browsermikro freischalten soll. Das passiert durch das neue Inform. Da wird einfach "wakeword_detected" weitergereicht.
Jetzt auch, ohne das das MQTT-Device im JS bekannt ist. Glaub mir, das ist super, ohne das man auf die Perlebene muss, oder
einen Websocket, Jsonlist2, whatever im Hintergrund zur Abfrage braucht. Wie ein NOTIFYDEV.

So, zu Piper. Ich habe es auch mal installiert. Mit pip und pipx wollte es nicht. Daher mein Manual-Way.

1. System-Installation (Standalone Engine)
Diese Befehle installieren die Engine unabhängig von Python direkt im System.

Bash
# In temporäres Verzeichnis wechseln
cd /tmp

# Piper Standalone für x86_64 laden
wget https://github.com/rhasspy/piper/releases/download/v1.2.0/piper_amd64.tar.gz

# Entpacken und nach /opt verschieben
tar -xvf piper_amd64.tar.gz
sudo mv piper /opt/piper

# Rechte setzen (Besitz root, ausführbar für alle)
sudo chown -R root:root /opt/piper
sudo chmod -R 755 /opt/piper

# Symlink erstellen, damit 'piper' als Befehl überall funktioniert
sudo ln -sf /opt/piper/piper /usr/local/bin/piper

# Bibliotheken im System registrieren (wichtig!)
echo "/opt/piper" | sudo tee /etc/ld.so.conf.d/piper.conf
sudo ldconfig
2. Stimmen-Modell (Thorsten Medium)
Hier laden wir die Sprachdateien herunter und setzen die korrekten FHEM-Berechtigungen.

Bash
# Verzeichnis für Models erstellen
sudo mkdir -p /opt/fhem/piper_models
cd /opt/fhem/piper_models

# Thorsten Medium Modell und Konfiguration laden
sudo wget -O de_DE-thorsten-medium.onnx https://huggingface.co/Thorsten-Voice/Piper/resolve/main/de_DE-thorsten-medium.onnx
sudo wget -O de_DE-thorsten-medium.onnx.json https://huggingface.co/Thorsten-Voice/Piper/resolve/main/de_DE-thorsten-medium.onnx.json

# Rechte für den User fhem anpassen
sudo chown -R fhem:dialout /opt/fhem/piper_models
sudo chmod 644 /opt/fhem/piper_models/de_DE-thorsten-medium.onnx*
3. FHEM Audio-Verzeichnis vorbereiten
Sicherstellen, dass Piper die Sprachdatei in das Web-Verzeichnis schreiben darf.

Bash
sudo mkdir -p /opt/fhem/www/audio
sudo chown fhem:dialout /opt/fhem/www/audio
sudo chmod 775 /opt/fhem/www/audio
4. Test-Befehl (Funktionsprüfung)
Diesen Befehl nutzen, um zu prüfen, ob der User fhem fehlerfrei sprechen kann.

Bash
sudo -u fhem piper --model /opt/fhem/piper_models/de_DE-thorsten-medium.onnx --output_file /opt/fhem/www/audio/voice.wav <<< "Test erfolgreich."

Immer wenn ich Zeit habe, schau ich mal weiter bei Piper mit rein.

Gruß schwatter