Die verschiedenen Möglichkeiten, Audio-Dateien (insbesondere Sprachausgabe-Dateien) via FHEM abzuspielen, sind ganz gut dokumentiert. Nun wollte ich allerdings auch eine Sprachnachricht bekommen, wenn ich nur mit einem Browser in der FHEM-Oberfläche angemeldet bin.
Das habe ich jetzt nach längerem Experimentieren auch geschafft.
Das läuft wie folgt ab:
1.In einer Webseite (z.B. per Attribut von FHEMWEB) wird eine Javascript-Datei ssepah-loader.js eingebunden. Dieser Code überprüft, ob es sich beim Browser um ein modernes System (Chrome oder Firefox) oder um einen Safari-Browser handelt. Im ersten Fall wird eine Datei ssepah-main.js nachgeladen, die einen so genannten Shared Worker startet. Im zweiten Fall wird eine Datei ssepah-fallback geladen, die mit Hilfe eines Workarounds ebenfalls Audio abspielen kann.
2. Der Shared Worker versucht alle 2 Sekunden, sich mit einem Server (auf dem z.B. FHEM läuft) als Quelle von Server Side Events (SSE) zu verbinden. Ob dies gelingt, wird durch ein 5x5 Pixel großes Quadrat im Seitenheader angezeigt, das rot, grün, oder gelb ist. Natürlich will man nicht, dass diese Verbindung von allen Clients aufgemacht wird - darum kann man in einer Blacklist alle Clients ausschließen, die man damit nicht belästigen will. Bei mir sind das z.B. einige wandhängende Tablets. Auch die lokale IP-Adresse des Clients ist wichtig, um zu entscheiden, ob hier überhaupt ein Verbindungsversuch unternommen wird.
3. Wenn der Shared Worker Kontakt zum SSE-Server erhält, kann er von diesem eine Audio-Datei empfangen und abspielen.
4. Der SSE-Server ist ein kleines separates Perl-Programm tts_push_server.pl (könnte man auch in einer anderen Sprache schreiben), das nur so lange lebt, bis die übergebene Audiodatei an den Client (also den Browser) übertragen worden ist. Und sich danach selbst beendet.
5. Der SSE-Server sowie ein weiteres Perl-Programm tts_ready_copy.pl, das die fertige MP3-Datei von "irgendwoher" an die richtige Stelle kopiert, werden von einem Shellscript tts_run.sh gesteuert. Das wiederum wird in einem FHEM-Modul aufgerufen, z.B. mit
}
...elsif( $dadtype eq "Browser" ){
Log 1,"===========> [speakDAD] for Browser name=$name daddev=$daddev dadtype=$dadtype text=$text\n";
#--start TTS server
my $cmd = "/opt/fhem/tts_run.sh ".$ttsDir.$ttsTarget.".mp3 ".$ttsDur." 1 &";
Log 1,"===========> cmd= $cmd";
system($cmd);
(da sind noch allerhand Debug-Ausgaben drin).
Ergebnis: Ich kann meine Sprachmeldungen ohne lokale Server-Installation und ohne MP3-Player oder so etwas im Browser hören.
Die Dateien tts.* liegen bei mir momentan noch unter /opt/fhem, die Dateien ssepah.* unter /opt/fhem/www/pgm2. Die Änderung in der Styledatei, damit das bei dem betreffenden Rechner auch auf der FHEM-Oberfläche angezeigt wird, lautet
div#hdr {
position: relative;
}
div#hdr:before {
float: left;
color: #6d77e2;
left: 20px;
width: 140px;
margin: 10px 0px 0px 30px;
font-size: 24px;
font-weight: bold;
text-shadow: 2px 2px #c5c5c5;
content: "FHEM 94";
display: inline-block;
}
#sse-status {
display: inline-block;
width: 5px;
height: 5px;
margin-left: 8px;
border-radius: 1px;
vertical-align: middle;
background-color: gray;
cursor: default;
user-select: none;
}
#sse-status.ok {
background-color: #4CAF50; /* grün */
}
#sse-status.error {
background-color: #F44336; /* rot */
}
#sse-status.waiting {
background-color: #FFEB3B; /* gelb */
border: 1px solid #BCA800;
}
Noch ein Hinweis: Die modernen Browser blockieren die Audio-Ausgabe, wenn man nicht einmal auf der Seite irgendwo geklickt hat. Daran knobele ich noch herum.
Und selbstverständlich kann man den ganzen Kram auch dafür benutzen, andere Push-Daten an beliebige Browser zu senden.
Im Anhang erstmal die ganzen Dateien zum testen.
LG
pah
Noch ist Einiges zu tun, so möchte ich eigentlich nicht, dass jede Sprachausgabedatei ins Filesystem geschrieben wird, sondern will das eher auf eine Ramdisk legen (/tmp etwa). Im Prinzip funktioniert das aber sehr gut
So, ich habe noch neue Versionen meiner Dateien erstellt.
1. Wenn der Browser einen Shared Worker unterstützt, und mehrere Tabs für die Webseite (z.B. FHEM) offen sind, wird nur EIN Worker gestartet - das verhindert, dass die MP3-Datei mehrfach parallel abgespielt wird.
2. Die Shell-Skripte habe ich noch mit einem ordentlichen Logging für Fehler ausgestattet, und ein paar Timeouts eingebaut.
Der Aufruf aus FHEM heraus mit }elsif( $dadtype eq "Browser" ){
#Log 1,"===========> [speakDAD] for Browser name=$name daddev=$daddev dadtype=$dadtype text=$text\n";
#-- start TTS server, 2 seconds delay
my $cmd = "/opt/fhem/tts_run.sh ".$ttsDir.$ttsTarget.".mp3 ".$ttsDur." 2 &";
Log 1,"===========> cmd= $cmd";
system($cmd);
funktioniert jetzt astrein, getestet unter Firefox und Chrome.
LG
pah