From 1c341dae0b19c14bcd05b3ec792f5f92da8e575f Mon Sep 17 00:00:00 2001 From: rudolfkoenig <> Date: Mon, 12 Jan 2009 10:26:50 +0000 Subject: [PATCH] Version 1.4 from Martin. git-svn-id: https://svn.fhem.de/fhem/trunk@329 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/contrib/fhem2speech/ChangeLog | 45 + fhem/contrib/fhem2speech/JSON-XS-2.231.tar.gz | Bin 0 -> 66373 bytes fhem/contrib/fhem2speech/README.fhem-speech | 345 +++++ fhem/contrib/fhem2speech/README.fhem2speech | 63 - fhem/contrib/fhem2speech/extensions.conf | 163 +++ fhem/contrib/fhem2speech/fhem-speech | 1169 +++++++++++++++++ fhem/contrib/fhem2speech/fhem-speech.agi | 55 + fhem/contrib/fhem2speech/fhem2speech.sh | 12 - 8 files changed, 1777 insertions(+), 75 deletions(-) create mode 100644 fhem/contrib/fhem2speech/ChangeLog create mode 100644 fhem/contrib/fhem2speech/JSON-XS-2.231.tar.gz create mode 100644 fhem/contrib/fhem2speech/README.fhem-speech delete mode 100644 fhem/contrib/fhem2speech/README.fhem2speech create mode 100644 fhem/contrib/fhem2speech/extensions.conf create mode 100755 fhem/contrib/fhem2speech/fhem-speech create mode 100755 fhem/contrib/fhem2speech/fhem-speech.agi delete mode 100755 fhem/contrib/fhem2speech/fhem2speech.sh diff --git a/fhem/contrib/fhem2speech/ChangeLog b/fhem/contrib/fhem2speech/ChangeLog new file mode 100644 index 000000000..b2d586fcf --- /dev/null +++ b/fhem/contrib/fhem2speech/ChangeLog @@ -0,0 +1,45 @@ +2009-01-11 (1.4) Martin Fischer + + * test for arguments added + * new option --set to set device status added + * documentation updated + +2009-01-07 (1.3) Martin Fischer + + * Perl modul JSON::XS for communication with FHEM included. This requires + jsonlist (fhem/contrib) for FHEM. + * several changes for parsing the JSON result + * buffering disabled + * output as asterisk AGI command added + * new subroutine for removing value units + +2009-01-01 (1.2) Martin Fischer + + * Perl modul Term::ANSIColor removed + * external command 'recode' for converting UTF8 to latin1. mbrola need + this to speek german umlauts + * new output format gsm + * options for outputformat now configurable + * translation for special chars added + * new option --asterisk, -a to support asterisk parsing added + * new option --prefix to flag outfiles with a user prefix added + * new option --force to override existing files added + * support for reading files added + * several changes in subroutine text2speech (code cleanup) + +2008-12-31 (1.1) Martin Fischer + + * command-line options now works with Getopt::Long + * check for external files (mbrola, txt2pho, sox, etc.) included + * include a debug routine. use it with --debug on command-line + * documentation added + * added support for quiet mode. use it with -q, --quiet + * FHEM error trap added + * new option -t "TEXT". read the given text + * new option to support female or male voice. use it with -S, --Sex + on command-line + * replace unwanted chars with text, e.g. - = Minus + * support for generating (cached) wave files + * update the documentation + * added cached files for all modes + diff --git a/fhem/contrib/fhem2speech/JSON-XS-2.231.tar.gz b/fhem/contrib/fhem2speech/JSON-XS-2.231.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..18e9a144544e050d9ad32bdf1645a2b2edd8d469 GIT binary patch literal 66373 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 R|84)j-obwFR)z!23;=s>-jx6V literal 0 HcmV?d00001 diff --git a/fhem/contrib/fhem2speech/README.fhem-speech b/fhem/contrib/fhem2speech/README.fhem-speech new file mode 100644 index 000000000..55fb5f754 --- /dev/null +++ b/fhem/contrib/fhem2speech/README.fhem-speech @@ -0,0 +1,345 @@ +NAME + fhem-speech - Synthesized voice (based on MBROLA) extension for FHEM + +SYNOPSIS + fhem-speech -d device [-achopqS] + + fhem-speech -d device --set state [-hp] + + fhem-speech -f file [-acoqS] + + fhem-speech -t "text" [-acoqS] + + fhem-speech [-HmV?] + + Try `fhem-speech --man` for full manual! + +DESCRIPTION + fhem-speech + fhem-speech read the status of a FHEM device and talk using the MBROLA + speech synthesizer. Furthermore it can read the content of a given file + or text. + + FHEM + FHEM is used to automate some common tasks in the household like + switching lamps/shutters/heating/etc. and to log events like + temperature/humidity/power consumption. Visit the FHEM's homepage + for more information. + + The MBROLA project + Central to the MBROLA project is MBROLA, a speech synthesizer based on + the concatenation of diphones. It takes a list of phonemes as input, + together with prosodic information, and produces speech samples on 16 + bits (linear), at the sampling frequency of the diphone database used. + This synthesizer is provided for free, for non commercial, non military + applications only. Visit the MBROLA's homepage + for more information. + + Asterisk + Optionally fhem-speech supports AGI commands to communicate with + Asterisk. Visit the Asterisk(R) homepage for + more information. + +OPTIONS + Mandatory arguments to long options are mandatory for short options too. + Ordering Options: + + -d, --device device + Run in FHEM mode. Specifies the FHEM device to be queried. The given + device must be defined. + + -f, --file file-name + Run in file mode. fhem-speech will read the given file. + + -t, --text "TEXT" + Run in Speaker's mode. fhem-speech will read the given "TEXT". + + Other options: + + -a, --asterisk + Run in Asterisk mode. fhem-speech print out AGI-commands for direct + usage in Asterisk. + + -c, --cache directory + Specifies the location of where the files should be saved if + fhem-speech started with the -o or --out argument. + + Default location: current directory. + + --force + Overwrites existing files. + + -h, --host host + Specifies the hostaddress for FHEM. + + Default address: "localhost". + + -o, --out [gsm|wav] + fhem-speech saves the output to a file with the specified output + format. + + Default address: "localhost". + + -p, --port port + Communicate with FHEM on defined port. + + Default port: "7072". + + --prefix prefix + Set the given prefix in front of filename. + + -q, --quiet + Run in quiet mode. + + --set state + Send to device. + + -S, --sex [f|m] + Specifies the sex for the voice. It depends on which voices for + MBROLA have been installed. + + Default: "de3" for the German female voice and "de2" for the German + male voice. + + -m, --man + Show the manual page and exits. + + -H, --help + Show a brief help message and exits. + + -V, --version + Show fhem-speech's version number and exit. + +EXAMPLES + Get status information for device in quiet mode: + + `fhem-speech -d EG.wz.HZ -q` + + Same as above with a male voice. FHEM runs on IP 192.168.1.100: + + `fhem-speech -d EG.wz.HZ -S m -h 192.168.1.100` + + Get status information for device in Asterisk mode: + + `fhem-speech -d EG.wz.HZ -a -q -o gsm -c /var/lib/asterisk/sounds/fhem/` + + Read the file : + + `fhem-speech -f foobar` + + Read the given text "Geht nicht gibt's nicht.": + + `fhem-speech -t "Geht nicht gibt's nicht."` + + Set the state for device : + + `fhem-speech -d EG.wz.SD.01 --set on` + +INSTALLATION + Requirements + MBROLA + You need MBROLA synthesizer, a synthesis voice, txt2pho and sox. For + more information visit: + + o MBROLA project, + + o hadifix, + + FHEM + For FHEM mode you need FHEM 4.5+ and the command extension "jsonlist". + For more information take a look at: + + /contrib/JsonList/README.JsonList + + or visit the FHEM's homepage: + + + + JSON::XS + The required command extension "jsonlist" send the result as a JSON + encoded string. fhem-speech need the Perl module JSON::XS to decode the + information. + + There are several ways to install the module: + + You can download the last version at: + + + + Or you can use the package from the contrib-folder which was delivered + with fhem-speech. + + You can use the cpan command on bash-prompt. + + Installation + This describes the installation on ubuntu: + + Make a temporarily directory for the needed files and change to the new + directory, e.g.: + + `mkdir /usr/local/src/mbrola; cd !$` + + Download the required files: + + `wget http://www.ikp.uni-bonn.de/dt/forsch/phonetik/hadifix/txt2pho.zip` + `wget http://tcts.fpms.ac.be/synthesis/mbrola/bin/pclinux/mbrola3.0.1h_i386.deb` + + Download at least one synthesis voice (e.g. German female voice): + + `wget http://tcts.fpms.ac.be/synthesis/mbrola/dba/de3/de3.zip` + + txt2pho + Install txt2pho: + + `unzip txt2pho.zip -d /usr/share/` + `chmod 755 /usr/share/txt2pho/txt2pho` + + Edit txt2phorc: + + `vi /usr/share/txt2pho/txt2phorc` + + and change the path for DATAPATH and INVPATH: + + DATAPATH=/usr/share/txt2pho/data/ + INVPATH=/usr/share/txt2pho/data/ + + Copy txt2phorc to /etc/txt2pho: + + `cp /usr/share/txt2pho/txt2phorc /etc/txt2pho` + + Synthesis Voice + Install the synthesis voice (e.g. German female voice): + + `unzip de7.zip -d /usr/share/mbrola/de7` + + fhem-speech use "de2" and "de3" as default voices. You can change this + if you like. + + MBROLA + Install MBROLA: + + `dpkg -i mbrola3.0.1h_i386.deb` + + sox + Install sox: + + `apt-get install sox libsox-fmt-all` + + Test + Test your installation: + + `echo "Test" | /usr/share/txt2pho/txt2pho |\ + mbrola /usr/share/mbrola/de7/de7 - -.au | play -q -t au -` + + fhem-speech + Copy the script fhem-speech to a directory of your choice, e.g.: + + `cp fhem-speech /usr/local/bin` + + and make it executable: + + `chmod 775 /usr/local/bin/fhem-speech` + + Perl + If you use the delivered module contrib/JSON-XS-2.231.tar.gz: + + `tar xzf JSON-XS-2.231.tar.gz` + `cd JSON-XS-2.231` + `perl Makefile.pl` + `make` + `make test` + + and as root: + + `make install` + +CONFIGURATION + Open fhem-speech with your prefered editor. + + FHEM host settings + Change the default host, if you like: + + ########################### + # FHEM + $sys{fhem}{host} = "localhost"; + $sys{fhem}{port} = "7072"; + + External commands + Change the paths depending on the installed distribution: + + ########################### + # Mandatory external Files + $sys{file}{mbrola} = "/usr/local/bin/mbrola"; + $sys{file}{pipefilt} = "/usr/local/bin/pipefilt"; + $sys{file}{play} = "/usr/bin/play"; + $sys{file}{preproc} = "/usr/local/bin/preproc"; + [...] + + Change the default settings for synthesis voice: + + ########################### + # mbrola / txt2pho options + $sys{speech}{sex} = "f"; + $sys{speech}{male} = "-f0.8 -t0.9 -l 15000"; + $sys{speech}{female} = "-f1.2 -t1.0 -l 22050"; + + Translation + fhem-speech need the $lang{} settings to decide what messages from FHEM + to be spoken. For example take a look at the FHT part: + + ########################### + # FHEM Translation + + [...] + + ########################### + # FHT + # keys: + $lang{'actuator'} = "Ventilstellung: %s Prozent"; + $lang{'day-temp'} = "Temperatur Tag: %s Grad"; + $lang{'desired-temp'} = "Angeforderte Temperatur: %s Grad"; + $lang{'measured-temp'} = "Gemessene Temperatur: %s Grad"; + $lang{'mode'} = "Modus: %s"; + $lang{'night-temp'} = "Temperatur Nacht: %s Grad"; + $lang{'windowopen-temp'} = "Temperatur Fenster offen: %s Grad"; + [...] + + On every FHEM response all of the defined $lang{} status information + will be spoken. If you don't like status information for e.g. + 'windowopen-temp' then comment this out: + + # $lang{'windowopen-temp'} = "Temperatur Fenster offen: %s Grad"; + + If you like to know the status for e.g. 'lowtemp-offset' add a line like + this: + + $lang{'lowtemp-offset'} = "Versatz Temperatur %s Grad"; + + The '%s' stands as a placeholder for the value. + +OPTIONAL + Asterisk + fhem-speech support AGI commands for direct output in Asterisk. + + Wrapper + If you like fhem-speech for use in Asterisk, you have to install a + wrapper around fhem-speech. You can use the example from + contrib/fhem-speech.agi. + + Copy the wrapper to your asterisk-environment, e.g: + + `cp contrib/fhem-speech.agi /var/lib/asterisk/agi-bin/` + + extension.conf + Take a look at the example from contrib/extension.conf. + +LEGALESE + License GPLv3+: GNU GPL version 3 or later + . + + This is free software: you are free to change and redistribute it. There + is NO WARRANTY, to the extent permitted by law. + +AUTHOR + Copyright (C) 2008 Martin Fischer + diff --git a/fhem/contrib/fhem2speech/README.fhem2speech b/fhem/contrib/fhem2speech/README.fhem2speech deleted file mode 100644 index ffd072576..000000000 --- a/fhem/contrib/fhem2speech/README.fhem2speech +++ /dev/null @@ -1,63 +0,0 @@ -Copyright (c)2008 Martin Fischer - -Description: - The script fhem2speech.sh let FHEM talk. - -Requirements: - You need MBROLA synthesizer, a synthesis voice, txt2pho and bplay. - For more information visit: - o MBROLA Project, http://tcts.fpms.ac.be/synthesis/ - o hadifix, http://www.ikp.uni-bonn.de/dt/forsch/phonetik/hadifix/ - -Installation: - This describes the installation on ubuntu: - - Make a temporarily directory for the needed files and change to - the new directory, e.g.: - 'mkdir /usr/local/src/mbrola; cd !$' - - Download the required files: - 'wget http://www.ikp.uni-bonn.de/dt/forsch/phonetik/hadifix/txt2pho.zip' - 'wget http://tcts.fpms.ac.be/synthesis/mbrola/bin/pclinux/mbrola3.0.1h_i386.deb' - - Download at least one synthesis voice (e.g. german female voice): - 'wget http://tcts.fpms.ac.be/synthesis/mbrola/dba/de7/de7.zip' - - Install txt2pho: - 'unzip txt2pho.zip -d /usr/share/' - 'chmod 755 /usr/share/txt2pho/txt2pho' - - Edit txt2phorc: - 'vi /usr/share/txt2pho/txt2phorc' - and change the path for DATAPATH and INVPATH: - DATAPATH=/usr/share/txt2pho/data/ - INVPATH=/usr/share/txt2pho/data/ - - Copy txt2phorc to /etc/txt2pho: - 'cp /usr/share/txt2pho/txt2phorc /etc/txt2pho' - - Install the synthesis voice (e.g. german female voice): - 'unzip de7.zip -d /usr/share/mbrola/de7' - - Install mbrola: - 'dpkg -i mbrola3.0.1h_i386.deb' - - Install bplay: - 'apt-get install bplay' - - Test your installation: - 'echo "Test" | /usr/share/txt2pho/txt2pho | mbrola /usr/share/mbrola/de7/de7 - - | bplay -s22050 - -b 16' - - Copy the script fhem2speech.sh to a directory of your choice, - e.g.: - 'cp fhem2speech.sh /usr/local/bin' - and make it executable: - 'chmod 775 /usr/local/bin/fhem2speech.sh' - -Usage: - Edit your FHEM configuration file and add a line like this: - define speechBattery notify .*warnings.*Battery.*low.* "/usr/local/bin/fhem2speech.sh -s "Achtung, Batterie schwach: @"" - - At last restart FHEM or do a rereadcfg and listen to your speaker. - diff --git a/fhem/contrib/fhem2speech/extensions.conf b/fhem/contrib/fhem2speech/extensions.conf new file mode 100644 index 000000000..ea6bd07ce --- /dev/null +++ b/fhem/contrib/fhem2speech/extensions.conf @@ -0,0 +1,163 @@ +; extensions.conf - the Asterisk dial plan +; + +[myHCE] +; houseautomation +exten => 5000,1(myhce),Answer() +exten => 5000,n,Set(TIMEOUT(digit)=5) +exten => 5000,n,Set(TIMEOUT(response)=10) +; skip authentication for known numbers +exten => 5000,n,GotoIf($["${CALLERID(num)}" = "01601234567"]?5000,main) +exten => 5000,n,GotoIf($["${CALLERID(num)}" = "01701234567"]?5000,main) +; authentication +exten => 5000,n,Authenticate(1137) +exten => 5000,n,Wait(1) +; main menu +exten => 5000,n(main),NoOp(Main Menu) +exten => 5000,n,Set(GLOBAL(myHCE_ext)=${EXTEN}) +exten => 5000,n,Set(GLOBAL(myHCE_pExt)=5000) +exten => 5000,n,Set(GLOBAL(myHCE_pCon)=myHCE) +include => myHCE-default +exten => 5000,n(menu),AGI(fhem-speech.agi,t,"Hauptmenü") +exten => 5000,n(choice),AGI(fhem-speech.agi,t,"Bitte wählen Sie") +exten => 5000,n,AGI(fhem-speech.agi,t,"1 für Statusabfrage") +exten => 5000,n,AGI(fhem-speech.agi,t,"2 für Steuerung") +exten => 5000,n,AGI(fhem-speech.agi,t,"5 für Hilfe") +exten => 5000,n,Background(silence/3) +exten => 5000,n,Goto(choice) +; help +exten => 5000,n(help),AGI(fhem-speech.agi,t,"Menüsteuerung für alle Menüs") +exten => 5000,n,AGI(fhem-speech.agi,t,"8 zurück zum letzten Menü") +exten => 5000,n,AGI(fhem-speech.agi,t,"9 zurück zum Hauptmenü") +exten => 5000,n,AGI(fhem-speech.agi,t,"0 zum Beenden") +exten => 5000,n,Background(silence/3) +exten => 5000,n,Goto(menu) +; selection +exten => 1,1,Goto(myHCE-status,5100,status) +exten => 2,1,Goto(myHCE-control,5200,control) +exten => 5,1,Goto(5000,help) + +[myHCE-default] +; global menu navigation +exten => 8,1,Goto(${myHCE_pCon},${myHCE_pExt},menu) +exten => 9,1,Goto(myHCE,5000,main) +exten => 0,1,Goto(myHCE-exit,5099,exit) +; wrong input +exten => i,1,AGI(fhem-speech.agi,t,"Falsche Eingabe.") +exten => i,2,Goto(${myHCE_ext},menu) + +[myHCE-exit] +; exit +exten => 5099,n(exit),AGI(fhem-speech.agi,t,"Verbindung wird getrennt. Vielen Dank!") +exten => 5099,n,Hangup() + +[myHCE-status] +exten => 5100,1(status),NoOp(Status Menu) +exten => 5100,n,Set(GLOBAL(myHCE_ext)=${EXTEN}) +exten => 5100,n,Set(GLOBAL(myHCE_pExt)=5000) +exten => 5100,n,Set(GLOBAL(myHCE_pCon)=myHCE) +include => myHCE-default +; submenu device status +exten => 5100,n(menu),AGI(fhem-speech.agi,t,"Menü Statusabfrage") +exten => 5100,n(choice),AGI(fhem-speech.agi,t,"Bitte wählen Sie") +exten => 5100,n,AGI(fhem-speech.agi,t,"1 für Wetterstation") +exten => 5100,n,AGI(fhem-speech.agi,t,"2 für Rauchmelder") +exten => 5100,n,AGI(fhem-speech.agi,t,"5 für Raumthermostate") +exten => 5100,n,Background(silence/3) +exten => 5100,n,Goto(choice) +; selection +exten => 1,1,Playback(beep) +exten => 1,n,AGI(fhem-speech.agi,d,GH.ga.WE.01) +exten => 1,n,Playback(beep) +exten => 1,n,Goto(5100,status) +exten => 2,1,Playback(beep) +exten => 2,n,AGI(fhem-speech.agi,d,NN.xx.RM.01) +exten => 2,n,Playback(beep) +exten => 2,n,Goto(5100,status) +exten => 5,1,Goto(myHCE-status_fht,5110,menu) + +[myHCE-status_fht] +exten => 5110,1(status),NoOp(Status Menu) +exten => 5110,n,Set(GLOBAL(myHCE_ext)=${EXTEN}) +exten => 5110,n,Set(GLOBAL(myHCE_pExt)=5100) +exten => 5110,n,Set(GLOBAL(myHCE_pCon)=myHCE-status) +include => myHCE-default +; submenu fht devices +exten => 5110,n(menu),AGI(fhem-speech.agi,t,"Menü Raumthermostate") +exten => 5110,n(choice),AGI(fhem-speech.agi,t,"Bitte wählen Sie") +exten => 5110,n,AGI(fhem-speech.agi,t,"1 für Wohnzimmer") +exten => 5110,n,AGI(fhem-speech.agi,t,"2 für Schlafzimmer") +exten => 5110,n,AGI(fhem-speech.agi,t,"3 für Büro") +exten => 5110,n,AGI(fhem-speech.agi,t,"4 für Badezimmer") +exten => 5110,n,Background(silence/3) +exten => 5110,n,Goto(choice) +; selection +exten => 1,1,Playback(beep) +exten => 1,n,AGI(fhem-speech.agi,d,EG.wz.HZ) +exten => 1,n,Playback(beep) +exten => 1,n,Goto(5110,status) +exten => 2,1,Playback(beep) +exten => 2,n,AGI(fhem-speech.agi,d,EG.sz.HZ) +exten => 2,n,Playback(beep) +exten => 2,n,Goto(5110,status) +exten => 3,1,Playback(beep) +exten => 3,n,AGI(fhem-speech.agi,d,EG.bu.HZ) +exten => 3,n,Playback(beep) +exten => 3,n,Goto(5110,status) +exten => 4,1,Playback(beep) +exten => 4,n,AGI(fhem-speech.agi,d,EG.bz.HZ) +exten => 4,n,Playback(beep) +exten => 4,n,Goto(5110,status) + +[myHCE-control] +include => myHCE-default +exten => 5200,1(control),AGI(fhem-speech.agi,t,"Menü Steuerung") +exten => 5200,n(menu),AGI(fhem-speech.agi,t,"Bitte wählen Sie") +exten => 5200,n,AGI(fhem-speech.agi,t,"1 für Wohnzimmer") +exten => 5200,n,AGI(fhem-speech.agi,t,"2 für Schlafzimmer") +exten => 5200,n,AGI(fhem-speech.agi,t,"3 für Büro") +exten => 5200,n,AGI(fhem-speech.agi,t,"4 für Badezimmer") +exten => 5200,n,Background(silence/3) +exten => 5200,n,Goto(menu) + +exten => 1,1,Goto(myHCE-control_wohnen,5210,menu) + +exten => i,1,AGI(fhem-speech.agi,t,"Falsche Eingabe.") +exten => i,2,Goto(5200,menu) + +[myHCE-control_wohnen] +include => myHCE-default +exten => 5210,1(control),AGI(fhem-speech.agi,t,"Menü Steuerung") +exten => 5210,n,AGI(fhem-speech.agi,t,"Wohnzimmer") +exten => 5210,n(menu),AGI(fhem-speech.agi,t,"Bitte wählen Sie") +exten => 5210,n,AGI(fhem-speech.agi,t,"1 für Lampen") +exten => 5210,n,Background(silence/3) +exten => 5210,n,Goto(menu) + +exten => 1,1,Goto(myHCE-control_wohnen-lampen,5211,set) + +exten => 8,1,Goto(myHCE-control,5200,menu) + +exten => i,1,AGI(fhem-speech.agi,t,"Falsche Eingabe.") +exten => i,2,Goto(5200,menu) + +[myHCE-control_wohnen-lampen] +include => myHCE-default +exten => 5211,1(set),AGI(fhem-speech.agi,t,"Steuerung Lampen") +exten => 5211,n,AGI(fhem-speech.agi,d,EG.wz.SD.Licht.grp) +exten => 5211,n(menu),AGI(fhem-speech.agi,t,"1 für an") +exten => 5211,n,AGI(fhem-speech.agi,t,"2 für aus") +exten => 5211,n,Background(silence/3) +exten => 5211,n,Goto(menu) + +exten => 1,1,AGI(fhem-speech.agi,s,EG.wz.SD.Licht.grp,on) +exten => 1,n,Goto(5211,set) + +exten => 2,1,AGI(fhem-speech.agi,s,EG.wz.SD.Licht.grp,off) +exten => 2,n,Goto(5211,set) + +exten => 8,1,Goto(myHCE-control_wohnen,5210,menu) + +exten => i,1,AGI(fhem-speech.agi,t,"Falsche Eingabe.") +exten => i,2,Goto(5211,menu) + diff --git a/fhem/contrib/fhem2speech/fhem-speech b/fhem/contrib/fhem2speech/fhem-speech new file mode 100755 index 000000000..13f1a73f3 --- /dev/null +++ b/fhem/contrib/fhem2speech/fhem-speech @@ -0,0 +1,1169 @@ +#!/usr/bin/perl +################################################################ +# +# $Id: fhem-speech,v 1.1 2009-01-12 10:26:50 rudolfkoenig Exp $ +# +# Copyright notice +# +# (c) 2008 Copyright: Martin Fischer (m_fischer at gmx dot de) +# All rights reserved +# +# This script free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# The GNU General Public License can be found at +# http://www.gnu.org/copyleft/gpl.html. +# A copy is found in the textfile GPL.txt and important notices to the license +# from the author is found in LICENSE.txt distributed with these scripts. +# +# This script is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +################################################################ + +use strict; +use warnings; + +use Getopt::Long qw(:config no_ignore_case);; +use Cwd; +use IO::Socket::INET; +use IO::File; +use Pod::Usage; +use JSON::XS; + +use vars qw{$call}; +use vars qw{%dev}; +use vars qw(%lang); +use vars qw{%sys}; +use vars qw($VERSION); +use vars qw($VERSION); + +################################################## +# Variables + +########################### +# FHEM +$sys{fhem}{host} = "192.168.1.100"; +$sys{fhem}{port} = "7072"; + +########################### +# Mandatory external Files +$sys{file}{mbrola} = "/usr/local/bin/mbrola"; +$sys{file}{pipefilt} = "/usr/local/bin/pipefilt"; +$sys{file}{play} = "/usr/bin/play"; +$sys{file}{preproc} = "/usr/local/bin/preproc"; +$sys{file}{preprocRules} = "/usr/share/mbrola/Rules.lst"; +$sys{file}{preprocShort} = "/usr/share/mbrola/Hadifix.abk"; +$sys{file}{sox} = "/usr/bin/sox"; +$sys{file}{recode} = "/usr/bin/recode"; +$sys{file}{txt2pho} = "/usr/local/bin/txt2pho"; +$sys{file}{voiceFemale} = "/usr/share/mbrola/de3/de3"; +$sys{file}{voiceMale} = "/usr/share/mbrola/de2/de2"; + +########################### +# mbrola / txt2pho options +$sys{speech}{sex} = "f"; +$sys{speech}{male} = "-f0.8 -t0.9 -l 15000"; +$sys{speech}{female} = "-f1.2 -t1.0 -l 22050"; +$sys{speech}{wav} = "-t au - -r 8000 -c1"; +$sys{speech}{gsm} = "-t au - -r 8000 -c1"; + +########################### +# FHEM Translation + +########################### +# misc / default +$lang{'comment'} = "Status %s"; +$lang{'room'} = "Raum %s"; +$lang{'battery'} = "Batterie %s"; +$lang{'state'} = "%s"; +$lang{'on'} = "an"; +$lang{'off'} = "aus"; +$lang{'yes'} = "ja"; +$lang{'no'} = "nein"; +$lang{'comma'} = "Komma"; +$lang{'error'} = "Status unbekannt"; +$lang{'zirkumflex'} = "Zirkumflex"; +$lang{'underline'} = "Unterstrich"; +$lang{'apostrophe'} = "Hochkomma"; +$lang{'degree'} = "Grad"; +$lang{'minus'} = "Minus"; +$lang{'plus'} = "Plus"; +$lang{'squareopen'} = "eckige Klammer auf"; +$lang{'squareclose'} = "eckige Klammer zu"; +$lang{'backtick'} = "Rueckwaerts geneigtes Hochkomma"; +$lang{'singlequote'} = "einfaches Anfuehrungszeichen"; +$lang{'quote'} = "Anfuehrungszeichen oben"; +$lang{'backslash'} = "umgekehrter Schraegstrich"; +$lang{'squaremm'} = "Quadratmilimeter"; +$lang{'squarecm'} = "Quadratzentimeter"; +$lang{'squarem'} = "Quadratmeter"; +$lang{'cubicmm'} = "Kubikmilimeter"; +$lang{'cubiccm'} = "Kubikzentimeter"; +$lang{'cubicm'} = "Kubikmeter"; + +########################### +# FHT +# keys: +$lang{'actuator'} = "Ventilstellung %s Prozent"; +$lang{'day-temp'} = "Temperatur Tag %s Grad"; +$lang{'desired-temp'} = "Angeforderte Temperatur %s Grad"; +$lang{'measured-temp'} = "Gemessene Temperatur %s Grad"; +$lang{'mode'} = "Modus %s"; +$lang{'night-temp'} = "Temperatur Nacht %s Grad"; +$lang{'windowopen-temp'} = "Temperatur Fenster offen %s Grad"; +# values: +$lang{'auto'} = "Automatik"; +$lang{'holiday'} = "Urlaub"; +$lang{'holiday_short'} = "Kurzurlaub"; +$lang{'manual'} = "Manuell"; +########################### +# KS300/KS555 +# keys: +$lang{'humidity'} = "Luftfeuchtigkeit %s Prozent"; +$lang{'israining'} = "Niederschlag %s"; +$lang{'temperature'} = "Aussentemperatur %s Grad"; +$lang{'wind'} = "Windgeschwindigkeit %s km/h"; +########################### +# HMS +# keys: +$lang{'smoke_detect'} = "Alarm %s"; + +# End of Variables +################################################## + +################################################## +# Forward declaration +sub sayFHEM; +sub queryFHEM($); +sub parseFHEM($$); +sub translateKey; +sub translateValue($$); +sub text2speech($); +sub isNumber; +sub isInteger; +sub isFloat; +sub usage; +sub usageShort; +sub version; + +main(); +exit; + +################################################## +# Main +sub main { + $call = _call(); + _debug($call,"called") if(grep(/debug/, @ARGV)); + my $result; + # disable buffering + $|=1; + + ########################### + # Variables + $VERSION = sprintf("%d.%02d", q$Revision: 1.1 $ =~ /(\d+)\.(\d+)/); + my $requiredOptions = "dft"; + + ########################### + # do some checks + # check for required files + foreach my $exec (sort keys %{$sys{file}}) { + _missing_file($sys{file}{$exec}) unless(-r $sys{file}{$exec}); + _debug($call,"check file: '".$sys{file}{$exec}."'") if(grep(/debug/, @ARGV)); + } + # check options + _missing_argv($requiredOptions) if(int(@ARGV) == 0); + + ########################### + # get options + my $args = { + asterisk => 0, # default false + cache => "", # default undef + debug => 0, # default false + device => "", # default undef + file => "", # default undef + force => 0, # default false + host => "", # default undef + out => "", # default undef + port => "", # default undef + prefix => "", # default undef + quiet => 0, # default false + set => "", # default undef + sex => "", # default undef + text => "", # default undef + }; + + eval { + local $SIG{__WARN__} = sub {}; + GetOptions( + "asterisk|a" => \$args->{asterisk}, + "cache|c=s" => \$args->{cache}, + "debug" => \$args->{debug}, + "device|d=s" => \$args->{device}, + "file|f=s" => \$args->{file}, + "force" => \$args->{force}, + "host|h=s" => \$args->{host}, + "out|o=s" => \$args->{out}, + "port|p=i" => \$args->{port}, + "prefix=s" => \$args->{prefix}, + "quiet|q" => \$args->{quiet}, + "set=s" => \$args->{set}, + "sex|S=s" => \$args->{sex}, + "text|t=s" => \$args->{text}, + "H|?" => sub { pod2usage( -exitval => 0, -verbose => 0); }, + "help" => sub { pod2usage( -exitval => 0, -verbose => 0); }, + "man" => sub { pod2usage( -exitval => 0, -verbose => 2); }, + "version|V" => sub { version(0) } + ); + } or pod2usage(); + + # set global options + $sys{asterisk} = $args->{asterisk} if($args->{asterisk}); + $sys{debug} = $args->{debug} if($args->{debug}); + $sys{force} = $args->{force} if($args->{force}); + $sys{prefix} = $args->{prefix} if($args->{prefix}); + $sys{quiet} = $args->{quiet} if($args->{quiet}); + $sys{speech}{sex} = $args->{sex} if($args->{sex}); + $sys{speech}{cache} = $args->{cache} if($args->{cache}); + $sys{speech}{out} = $args->{out} if($args->{out}); + # check for dependent options + _wrong_set("-d or -f or -t") if($args->{device} && ($args->{file} || $args->{text})); + _wrong_set("-d or -f or -t") if($args->{device} && ($args->{file} && $args->{text})); + _wrong_set("-f [-acoqS]") if($args->{file} && ($args->{host} || $args->{port})); + _wrong_set("-f [-acoqS]") if($args->{file} && ($args->{host} && $args->{port})); + _wrong_set("-t [-acoqS]") if($args->{text} && ($args->{host} || $args->{port})); + _wrong_set("-t [-acoqS]") if($args->{text} && ($args->{host} && $args->{port})); + _wrong_set("-d --set [-hp]") if( + $args->{set} && ($args->{astersik} || $args->{cache} || + $args->{file} || $args->{text} || $args->{force} || + $args->{out} || $args->{prefix} || $args->{quiet} || + $args->{sex}) + ); + _wrong_set("[-d|-f|-t] argument [-acopqS]") if($args->{asterisk} && ($args->{host} || $args->{port})); + # check for dependent options + _missing_required("-d") if(!$args->{device} && ($args->{host} || $args->{port})); + _missing_required("-d") if(!$args->{device} && ($args->{host} && $args->{port})); + _missing_required("-o") if(!$args->{out} && $args->{cache}); + _missing_required("-c") if(!$args->{cache} && ($args->{prefix} || $args->{force})); + _missing_required("-c") if(!$args->{cache} && ($args->{prefix} && $args->{force})); + _missing_required("-c") if(!$args->{cache} && $args->{asterisk}); + + # listen to text + if($args->{text}) { + $sys{speech}{text} = $args->{text} if($args->{text}); + _debug($call,"ARGV: \$sys{speech}{text}: '".$sys{speech}{text}."'") if($sys{debug}); + $result = sayTEXT(); + } + + # listen to file + if($args->{file}) { + $sys{speech}{file} = $args->{file} if($args->{file}); + _debug($call,"ARGV: \$sys{speech}{file}: '".$sys{speech}{file}."'") if($sys{debug} && $args->{file}); + $result = sayFILE(); + } + + # let FHEM talk :-) + if($args->{device}) { + _debug($call,"ARGV is 'fhem'") if($sys{debug}); + $sys{fhem}{device} = $args->{device}; + _debug($call,"ARGV: \$sys{fhem}{device}: '".$sys{fhem}{device}."'") if($sys{debug}); + $sys{fhem}{set} = $args->{set} if($args->{set}); + _debug($call,"ARGV: \$sys{fhem}{set}: '".$sys{fhem}{set}."'") if($sys{debug}); + $sys{fhem}{host} = $args->{host} if($args->{host}); + _debug($call,"ARGV: \$sys{fhem}{host}: '".$sys{fhem}{host}."'") if($sys{debug}); + $sys{fhem}{port} = $args->{port} if($args->{port}); + _debug($call,"ARGV: \$sys{fhem}{port}: '".$sys{fhem}{port}."'") if($sys{debug}); + + $result = sayFHEM() if(!$sys{fhem}{set}); + $result = setFHEM() if($sys{fhem}{set}); + } + return 0; +} + + +################################################## +# Text + +########################### +sub sayTEXT { + $call = _call(); + _debug($call,"called") if($sys{debug}); + my $result; + my $say; + + $say = $sys{speech}{text} if($sys{speech}{text}); + $result = text2speech($say) if($say); +} + +################################################## +# File + +########################### +sub sayFILE { + $call = _call(); + _debug($call,"called") if($sys{debug}); + STDOUT->autoflush(1); + my $content = ""; + my $file = $sys{speech}{file}; + my $result; + + _file_error($file) if(!-e $file || -B $file); + + my $fh = new IO::File($file, "r") or _file_error($file); + _debug($call,"read : '".$file."'") if($sys{debug}); + while (my $line = $fh->getline()) { + $content = $content.$line; + } + $fh->close(); + + $result=text2speech($content); + +} + +################################################## +# FHEM + +########################### +sub setFHEM { + $call = _call(); + _debug($call,"called") if($sys{debug}); + my $fhemCmd = "set ".$sys{fhem}{device}." ".$sys{fhem}{set}; + my $result; + + ########################### + # query FHEM and start working + $result = queryFHEM($fhemCmd); + chomp $result if($result); + _debug($call,"\$result: '".$result."'") if($sys{debug} && $result); + _fhem_error($result) if($result && $result =~ /No.*set/); +} + +########################### +sub sayFHEM { + $call = _call(); + _debug($call,"called") if($sys{debug}); + my $fhemCmd = "jsonlist ".$sys{fhem}{device}; + my $result; + my $fhemRaw; + my $say; + + ########################### + # query FHEM and start working + $result = queryFHEM($fhemCmd); + chomp $result; + _debug($call,"\$result: '".$result."'") if($sys{debug}); + _fhem_error($result) if(!$result || $result =~ /No.*device/); + + $fhemRaw = decode_json $result; + %dev = %{$fhemRaw->{ResultSet}->{Results}}; + + parseFHEM("room",$dev{ATTRIBUTES}{room}) if($dev{ATTRIBUTES}{room}); + parseFHEM("comment",$dev{ATTRIBUTES}{comment}) if($dev{ATTRIBUTES}{comment}); + + while (my ($key,$value) = each %dev) { + _debug($call,"\$key: '".$key."'") if($sys{debug}); + if($key eq "ATTRIBUTES" || $key eq "READINGS") { + while (my ($subKey,$subValue) = each %{$dev{$key}}) { + if($subKey ne "comment" && $subKey ne "room") { + _debug($call,"\$subKey: '".$subKey."'") if($sys{debug}); + $say = parseFHEM($subKey,$subValue) if($key eq "ATTRIBUTES"); + $say = parseFHEM($subKey,$subValue->{VAL}) if($key eq "READINGS"); + } + } + } else { + $say = parseFHEM($key,$value); + } + } +} + +########################### +sub queryFHEM($) { + $call = _call(); + _debug($call,"called") if($sys{debug}); + my $host = $sys{fhem}{host} . ":" . $sys{fhem}{port}; + my $cmd = shift; + my $result; + my $buf = ""; + + my $server = IO::Socket::INET->new(PeerAddr => $host); + _debug($call, "\$host: '".$sys{fhem}{host}.":".$sys{fhem}{port}."'") if($sys{debug}); + die "Can't connect to server " . $sys{fhem}{host} . " port " . $sys{fhem}{port} . "\n" if(!$server); + syswrite($server, "$cmd;quit\n"); + _debug($call, "\$cmd: '".$cmd."'") if($sys{debug}); + while(sysread($server, $buf, 256) > 0) { + $result .= $buf; + } + close($server); + return $result; +} + +########################### +sub parseFHEM($$) { + $call = _call(); + _debug($call,"called") if($sys{debug}); + my ($key,$value) = @_; + my $translatedKey; + my $translatedValue; + my @say; + my $result; + + $result = translateKey($key); + _debug($call,"\$key: '".$key."' \$result = ''") if($sys{debug} && !$result); + + if($result) { + $translatedKey = $result; + _debug($call,"\$key: '".$key."' \$translatedKey: '".$translatedKey."'") if($sys{debug}); + + if($result ne "\%s") { + @say = split("\%s",$translatedKey); + text2speech($say[0]); + translateValue($key,$value); + text2speech($say[1]) if($say[1] && $say[1] ne ":"); + } else { + translateValue($key,$value); + } + } +} + +########################### +sub translateKey { + $call = _call(); + _debug($call,"called") if($sys{debug}); + + return $lang{$_[0]} if(exists $lang{$_[0]}); +} + +########################### +sub translateValue($$) { + $call = _call(); + _debug($call,"called") if($sys{debug}); + my ($key,$value) = @_; + _debug($call,"\$key: '".$key."' \$value: '".$value."'") if($sys{debug}); + my $return; + + $value = removeUnits($value); + + # FS20 + if($key eq "state" || $key eq "smoke_detect") { + _debug($call,"\$key: '".$key."' is on/off") if($sys{debug}); + $value = $lang{'off'} if($value eq "off"); + $value = $lang{'on'} if($value eq "on"); + } + # KS300/KS555 + if($key eq "israining") { + _debug($call,"\$value: '".$value."'") if($sys{debug}); + $value = $lang{'no'} if($value eq "no (yes/no)"); + $value = $lang{'yes'} if($value eq "yes (yes/no)"); + _debug($call,"\$value converted to: '".$value."'") if($sys{debug}); + } + + if($value =~ m/^-/) { + text2speech($lang{'minus'}); + $value = substr($value,1); + } + if(isInteger($value)) { + text2speech(decodeChar($value)); + } elsif(isFloat($value)) { + my ($strLeft,$strRight) = split("\\.",$value); + _debug($call,"\$strLeft: '".$strLeft."' \$strRight '".$strRight."'") if($sys{debug}); + text2speech(decodeChar($strLeft)); + if($strRight !~ m/0+/) { + text2speech($lang{'comma'}); + text2speech(decodeChar($strRight)); + } + } else { + text2speech(decodeChar($value)); + } + +} + +################################################## +# mbrola + +########################### +sub text2speech($) { + $call = _call(); + _debug($call,"called") if($sys{debug}); + + my $str = shift; + my $cmd; + my $voice; + my $options; + my $out; + my $result; + + # define voice + if($sys{speech}{sex} eq "f") { + $voice = $sys{file}{voiceFemale}; + $options = $sys{speech}{female}; + } + if($sys{speech}{sex} eq "m") { + $voice = $sys{file}{voiceMale}; + $options = $sys{speech}{male}; + } + + # define extern commands + my $recode = $sys{file}{recode} . " " . "UTF-8..lat1"; + my $pipefilt = $sys{file}{pipefilt}; + my $preproc = $sys{file}{preproc} . " " . $sys{file}{preprocRules} . " " . $sys{file}{preprocShort}; + my $txt2pho = $sys{file}{txt2pho} . " -" . $sys{speech}{sex}; + my $mbrola = $sys{file}{mbrola} . " $options " . $voice . " - -.au"; + my $sox = $sys{file}{sox}; + + # set current dir for output if no directory defined + $sys{speech}{cache} = cwd() if(!$sys{speech}{cache} && $sys{speech}{out}); + + # sox options + if(defined($sys{speech}{out})) { + if($sys{speech}{out} eq "wav") { + # options for wav + $sys{speech}{soxOptions} = $sys{speech}{wav}; + } elsif($sys{speech}{out} eq "gsm") { + # options for gsm + $sys{speech}{soxOptions} = $sys{speech}{gsm}; +# } elsif($sys{speech}{out} eq "mp3") { +# # options for mp3 +# $sys{speech}{soxOptions} = $sys{speech}{mp3}; + } else { + _sox_format_error($sys{speech}{out}); + } + } + + # remove blanks + _debug($call,"remove bad chars") if($sys{debug}); + + # remove unwanted chars + $str = decodeChar($str); + + print $str . "\n" if(!$sys{quiet} && !$sys{asterisk}); + + # remove trailing slash + $sys{speech}{cache} =~ s/\/+$// if($sys{speech}{out}); + + _debug($call,"\$str: '".substr($str,0,40)."'") if($sys{debug}); + _debug($call,"\$sys{speech}{sex}: '".$sys{speech}{sex}."'") if($sys{debug}); + + # build command string + $cmd = "(echo \"$str\" | "; + # pipefilt | preproc | txt2pho | mbrola | + $cmd .= $recode . " | " . $pipefilt . " | " . $preproc . " | " . $txt2pho . " | " . $mbrola . " | "; + + # append command string for play only + if(!$sys{speech}{out}) { + $cmd .= $sys{file}{play} . " -q -t au -)"; + # let's get ready to rumble + $result = systemCmd($cmd); + } + + # append command string for destination file + if($sys{speech}{out}) { + $out = substr($str,0,32); + $out =~ s/[^a-zA-Z0-9]/_/gi; + $out = $sys{prefix}.$out if($sys{prefix}); + _debug($call,"\$sys{speech}{out}: '".$sys{speech}{out}."'") if($sys{debug}); + _debug($call,"\$sys{speech}{cache}: '".$sys{speech}{cache}."' \$out: '".$out."'") if($sys{debug}); + # sox + $cmd .= $sox . " " . $sys{speech}{soxOptions} . " " . $sys{speech}{cache} . "/" . $out . ".wav"; + # extend cmd for gsm + if($sys{speech}{out} eq "gsm") { + $cmd .= "; "; + # sox imput from wav + $cmd .= $sox . " " . $sys{speech}{cache} . "/" . $out . ".wav" . " "; + # sox output to gsm + $cmd .= $sys{speech}{cache} . "/" . $out . "." . $sys{speech}{out} . "; "; + # remove temporary wav file + $cmd .= "rm " . $sys{speech}{cache} . "/" . $out . ".wav" . "; "; + } + # close cmd + $cmd .= ")"; + # let's get ready to rumble + if($sys{force} || !-r $sys{speech}{cache} . "/" . $out . "." . $sys{speech}{out}) { + $result = systemCmd($cmd); + } + # print string for asterisk + if($sys{asterisk}) { + print "EXEC BACKGROUND ". $sys{speech}{cache} . "/" . $out . " \"\"\n"; + $result = ; + } + # play the result + if(!$sys{quiet}) { + $cmd = $sys{file}{play} . " -q -t " . $sys{speech}{out} . " "; + $cmd .= $sys{speech}{cache} . "/" . $out . "." . $sys{speech}{out}; + $result = systemCmd($cmd); + } + } +} + +################################################## +# misc + +##################################### +sub version { + print < +License GPLv3+: GNU GPL version 3 or later +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law. + +Written by Martin Fischer +EOT + exit($_[0]); +} + +################################################## +# helper + +##################################### +sub systemCmd { + $call = _call(); + _debug($call,"called") if($sys{debug}); + _debug($call,"system: '" . $_[0] . "'") if($sys{debug}); + return system($_[0]) == 0 || die "failed: $?"; +} + +##################################### +sub removeUnits { + $call = _call(); + _debug($call,"called") if($sys{debug}); + + $_[0] =~ s/\s\(Celsius\)//gi; + $_[0] =~ s/\s\(%\)//gi; + $_[0] =~ s/\s\(km\/h\)//gi; + $_[0] =~ s/%//gi; + + return $_[0]; +} + +##################################### +sub decodeChar { + $call = _call(); + _debug($call,"called") if($sys{debug}); + + $_[0] =~ s/^[\s\t]+//gi; + $_[0] =~ s/[\s\t]$//gi; + $_[0] =~ s/\^/ $lang{zirkumflex} /gi; + $_[0] =~ s/_/ $lang{underline} /gi; + $_[0] =~ s/'/ $lang{apostrophe} /gi; + $_[0] =~ s/°/ $lang{degree} /gi; + $_[0] =~ s/-/ $lang{minus} /gi; + $_[0] =~ s/\+/ $lang{plus} /gi; + $_[0] =~ s/\[/ $lang{squareopen} /gi; + $_[0] =~ s/\]/ $lang{squareclose} /gi; + $_[0] =~ s/`/ $lang{backtick} /gi; + $_[0] =~ s/\'/ $lang{singlequote} /gi; + $_[0] =~ s/"/ $lang{quote} /gi; + $_[0] =~ s/\\/ $lang{backslash} /gi; + $_[0] =~ s/mm²/$lang{squaremm} /gi; + $_[0] =~ s/cm²/$lang{squarecm} /gi; + $_[0] =~ s/m²/$lang{squarem} /gi; + $_[0] =~ s/mm³/$lang{cubicmm} /gi; + $_[0] =~ s/cm³/$lang{cubiccm} /gi; + $_[0] =~ s/m³/$lang{cubicm} /gi; + $_[0] =~ s/[\s\t]+/ /gi; + $_[0] =~ s/[[:cntrl:]]+//gi; + # convert to lowercase + $_[0] = lc($_[0]);; + + return $_[0]; +} + +##################################### +sub isNumber { + $call = _call(); + _debug($call,"called") if($sys{debug}); + $_[0] =~ /^\d+$/ +} + +##################################### +sub isInteger { + $call = _call(); + _debug($call,"called") if($sys{debug}); + $_[0] =~ /^[+-]?\d+$/ +} + +##################################### +sub isFloat { + $call = _call(); + _debug($call,"called") if($sys{debug}); + $_[0] =~ /^[+-]?\d+\.?\d*$/ +} + +########################### +sub _debug { + printf("\e[33m== debug:\e[37m \e[32m[%s]\e[37m \e[1m%s\e[0m\n",$_[0],$_[1]); +} + +########################### +sub _call { + (caller(1))[3]; +} + +##################################### +sub _missing_file { + $call = _call(); + _debug($call,"called") if($sys{debug}); + warn "fhem-speech: Mandatory file `".$_[0]."` does not exist, or is not readable!\n"; + warn "fhem-speech has been stopped. please fix this problem...\n"; + exit(1); +} + +##################################### +sub _missing_argv { + $call = _call(); + _debug($call,"called") if($sys{debug}); + warn "fhem-speech: You must specify one of the `-".$_[0]."` options.\n"; + warn "Try `fhem-speech --help` for more information.\n"; + exit(1); +} + +##################################### +sub _wrong_set { + $call = _call(); + _debug($call,"called") if($sys{debug}); + warn "fhem-speech: Wrong combination! Usage: `".$_[0]."` for options!\n"; + warn "Try `fhem-speech --help` for more information.\n"; + exit(1); +} + +##################################### +sub _missing_required { + $call = _call(); + _debug($call,"called") if($sys{debug}); + warn "fhem-speech: Missing required option `".$_[0]."`!\n"; + warn "Try `fhem-speech --help` for more information.\n"; + exit(1); +} + +##################################### +sub _fhem_error { + $call = _call(); + _debug($call,"called") if($sys{debug}); + $_[0] = $lang{'error'} if(!$_[0]); + chomp $_[0]; + warn "fhem-speech: FHEM result: `".$_[0]."`\n"; + #text2speech($_[0]); + exit(1); +} + +##################################### +sub _file_error { + $call = _call(); + _debug($call,"called") if($sys{debug}); + $_[0] = $lang{'error'} if(!$_[0]); + warn "fhem-speech: File `".$_[0]."` is binary. This filetype is not supported!\n"; + exit(1); +} + +##################################### +sub _sox_format_error { + $call = _call(); + _debug($call,"called") if($sys{debug}); + $_[0] = $lang{'error'} if(!$_[0]); + warn "fhem-speech: format `".$_[0]."` not supported!\n"; + exit(1); +} + + +################################################## +__END__ + +=head1 NAME + +fhem-speech - Synthesized voice (based on MBROLA) extension for FHEM + +=head1 SYNOPSIS + +B B<-d> device [B<-achopqS>] + +B B<-d> device B<--set> state [B<-hp>] + +B B<-f> file [B<-acoqS>] + +B B<-t> "text" [B<-acoqS>] + +B [B<-HmV?>] + +Try `B B<--man>` for full manual! + +=head1 DESCRIPTION + +=head2 fhem-speech + +fhem-speech read the status of a FHEM device and talk using the MBROLA +speech synthesizer. Furthermore it can read the content of a given file +or text. + +=head2 FHEM + +FHEM is used to automate some common tasks in the household like switching +lamps/shutters/heating/etc. and to log events like temperature/humidity/power +consumption. Visit the FHEM's homepage L +for more information. + +=head2 The MBROLA project + +Central to the MBROLA project is MBROLA, a speech synthesizer based on the +concatenation of diphones. It takes a list of phonemes as input, together +with prosodic information, and produces speech samples on 16 bits (linear), +at the sampling frequency of the diphone database used. This synthesizer +is provided for free, for non commercial, non military applications +only. Visit the MBROLA's homepage L for +more information. + +=head2 Asterisk + +Optionally fhem-speech supports AGI commands to communicate with Asterisk. +Visit the Asterisk(R) homepage L for more information. + +=head1 OPTIONS + +Mandatory arguments to long options are mandatory for short options too. +Ordering Options: + +=over + +=item B<-d>, B<--device> F + +Run in FHEM mode. Specifies the FHEM device to be queried. The given device +must be defined. + +=item B<-f>, B<--file> F + +Run in file mode. fhem-speech will read the given file. + +=item B<-t>, B<--text> F<"TEXT"> + +Run in Speaker's mode. fhem-speech will read the given "TEXT". + +=back + +Other options: + +=over + +=item B<-a>, B<--asterisk> + +Run in Asterisk mode. fhem-speech print out AGI-commands for direct usage in Asterisk. + +=item B<-c>, B<--cache> F + +Specifies the location of where the files should be saved if fhem-speech +started with the -o or --out argument. + +Default location: current directory. + +=item B<--force> + +Overwrites existing files. + +=item B<-h>, B<--host> F + +Specifies the hostaddress for FHEM. + +Default address: "localhost". + +=item B<-o>, B<--out> [F|F] + +fhem-speech saves the output to a file with the specified output format. + +Default address: "localhost". + +=item B<-p>, B<--port> F + +Communicate with FHEM on defined port. + +Default port: "7072". + +=item B<--prefix> F + +Set the given prefix in front of filename. + +=item B<-q>, B<--quiet> + +Run in quiet mode. + +=item B<--set> F + +Send to device. + +=item B<-S>, B<--sex> [F|F] + +Specifies the sex for the voice. It depends on which voices for MBROLA +have been installed. + +Default: "de3" for the German female voice and "de2" for the German +male voice. + +=item B<-m>, B<--man> + +Show the manual page and exits. + +=item B<-H>, B<--help> + +Show a brief help message and exits. + +=item B<-V>, B<--version> + +Show fhem-speech's version number and exit. + +=back + +=head1 EXAMPLES + +Get status information for device in quiet mode: + + `fhem-speech -d EG.wz.HZ -q` + +Same as above with a male voice. FHEM runs on IP 192.168.1.100: + + `fhem-speech -d EG.wz.HZ -S m -h 192.168.1.100` + +Get status information for device in Asterisk mode: + + `fhem-speech -d EG.wz.HZ -a -q -o gsm -c /var/lib/asterisk/sounds/fhem/` + +Read the file : + + `fhem-speech -f foobar` + +Read the given text "Geht nicht gibt's nicht.": + + `fhem-speech -t "Geht nicht gibt's nicht."` + +Set the state for device : + + `fhem-speech -d EG.wz.SD.01 --set on` + +=head1 INSTALLATION + +=head2 Requirements + +=head3 MBROLA + +You need MBROLA synthesizer, a synthesis voice, txt2pho and sox. For more +information visit: + +o MBROLA project, L + +o hadifix, L + +=head3 FHEM + +For FHEM mode you need FHEM 4.5+ and the command extension "jsonlist". For +more information take a look at: + +/F + +or visit the FHEM's homepage: + +L + +=head3 JSON::XS + +The required command extension "jsonlist" send the result as a JSON encoded +string. fhem-speech need the Perl module JSON::XS to decode the information. + +There are several ways to install the module: + +You can download the last version at: + +L + +Or you can use the package from the L-folder which was delivered +with fhem-speech. + +You can use the L command on bash-prompt. + +=head2 Installation + +This describes the installation on ubuntu: + +Make a temporarily directory for the needed files and change to the new +directory, e.g.: + + `mkdir /usr/local/src/mbrola; cd !$` + +Download the required files: + + `wget http://www.ikp.uni-bonn.de/dt/forsch/phonetik/hadifix/txt2pho.zip` + `wget http://tcts.fpms.ac.be/synthesis/mbrola/bin/pclinux/mbrola3.0.1h_i386.deb` + +Download at least one synthesis voice (e.g. German female voice): + + `wget http://tcts.fpms.ac.be/synthesis/mbrola/dba/de3/de3.zip` + +=head2 txt2pho + +Install txt2pho: + + `unzip txt2pho.zip -d /usr/share/` + `chmod 755 /usr/share/txt2pho/txt2pho` + +Edit txt2phorc: + + `vi /usr/share/txt2pho/txt2phorc` + +and change the path for DATAPATH and INVPATH: + + DATAPATH=/usr/share/txt2pho/data/ + INVPATH=/usr/share/txt2pho/data/ + +Copy txt2phorc to /etc/txt2pho: + + `cp /usr/share/txt2pho/txt2phorc /etc/txt2pho` + +=head2 Synthesis Voice + +Install the synthesis voice (e.g. German female voice): + + `unzip de7.zip -d /usr/share/mbrola/de7` + +fhem-speech use "de2" and "de3" as default voices. You can change this +if you like. + +=head2 MBROLA + +Install MBROLA: + + `dpkg -i mbrola3.0.1h_i386.deb` + +=head2 sox + +Install sox: + + `apt-get install sox libsox-fmt-all` + +=head2 Test + +Test your installation: + + `echo "Test" | /usr/share/txt2pho/txt2pho |\ + mbrola /usr/share/mbrola/de7/de7 - -.au | play -q -t au -` + +=head2 fhem-speech + +Copy the script fhem-speech to a directory of your choice, e.g.: + + `cp fhem-speech /usr/local/bin` + +and make it executable: + + `chmod 775 /usr/local/bin/fhem-speech` + +=head2 Perl + +If you use the delivered module F: + + `tar xzf JSON-XS-2.231.tar.gz` + `cd JSON-XS-2.231` + `perl Makefile.pl` + `make` + `make test` + +and as root: + + `make install` + +=head1 CONFIGURATION + +Open fhem-speech with your prefered editor. + +=head2 FHEM host settings + +Change the default host, if you like: + + ########################### + # FHEM + $sys{fhem}{host} = "localhost"; + $sys{fhem}{port} = "7072"; + +=head2 External commands + +Change the paths depending on the installed distribution: + + ########################### + # Mandatory external Files + $sys{file}{mbrola} = "/usr/local/bin/mbrola"; + $sys{file}{pipefilt} = "/usr/local/bin/pipefilt"; + $sys{file}{play} = "/usr/bin/play"; + $sys{file}{preproc} = "/usr/local/bin/preproc"; + [...] + +Change the default settings for synthesis voice: + + ########################### + # mbrola / txt2pho options + $sys{speech}{sex} = "f"; + $sys{speech}{male} = "-f0.8 -t0.9 -l 15000"; + $sys{speech}{female} = "-f1.2 -t1.0 -l 22050"; + +=head2 Translation + +fhem-speech need the $lang{} settings to decide what messages from FHEM +to be spoken. For example take a look at the FHT part: + + ########################### + # FHEM Translation + + [...] + + ########################### + # FHT + # keys: + $lang{'actuator'} = "Ventilstellung: %s Prozent"; + $lang{'day-temp'} = "Temperatur Tag: %s Grad"; + $lang{'desired-temp'} = "Angeforderte Temperatur: %s Grad"; + $lang{'measured-temp'} = "Gemessene Temperatur: %s Grad"; + $lang{'mode'} = "Modus: %s"; + $lang{'night-temp'} = "Temperatur Nacht: %s Grad"; + $lang{'windowopen-temp'} = "Temperatur Fenster offen: %s Grad"; + [...] + +On every FHEM response all of the defined $lang{} status information will +be spoken. If you don't like status information for e.g. 'windowopen-temp' then comment this out: + + # $lang{'windowopen-temp'} = "Temperatur Fenster offen: %s Grad"; + +If you like to know the status for e.g. 'lowtemp-offset' add a line like this: + + $lang{'lowtemp-offset'} = "Versatz Temperatur %s Grad"; + +The '%s' stands as a placeholder for the value. + +=head1 OPTIONAL + +=head2 Asterisk + +fhem-speech support AGI commands for direct output in Asterisk. + +=head3 Wrapper + +If you like fhem-speech for use in Asterisk, you have to install a wrapper around +fhem-speech. You can use the example from F. + +Copy the wrapper to your asterisk-environment, e.g: + + `cp contrib/fhem-speech.agi /var/lib/asterisk/agi-bin/` + +=head3 extension.conf + +Take a look at the example from F. + +=head1 LEGALESE + +License GPLv3+: GNU GPL version 3 or later . + +This is free software: you are free to change and redistribute it. There is +NO WARRANTY, to the extent permitted by law. + +=head1 AUTHOR + +Copyright (C) 2008 Martin Fischer + +=cut + diff --git a/fhem/contrib/fhem2speech/fhem-speech.agi b/fhem/contrib/fhem2speech/fhem-speech.agi new file mode 100755 index 000000000..9b0ad4169 --- /dev/null +++ b/fhem/contrib/fhem2speech/fhem-speech.agi @@ -0,0 +1,55 @@ +#!/usr/bin/perl +################################################################ +# +# $Id: fhem-speech.agi,v 1.1 2009-01-12 10:26:50 rudolfkoenig Exp $ +# + +use strict; + +$|=1; + +# Setup some variables +my $sounds = "/var/lib/asterisk/sounds/fhem/"; + +my %AGI; +my $tests = 0; +my $fail = 0; +my $pass = 0; + +while() { + chomp; + last unless length($_); + if (/^agi_(\w+)\:\s+(.*)$/) { + $AGI{$1} = $2; + } +} + +print STDERR "AGI Environment Dump:\n"; +foreach my $i (sort keys %AGI) { + print STDERR " -- $i = $AGI{$i}\n"; +} + +sub checkresult { + my ($res) = @_; + my $retval; + $tests++; + chomp $res; + if ($res =~ /^200/) { + $res =~ /result=(-?\d+)/; + if (!length($1)) { + print STDERR "FAIL ($res)\n"; + $fail++; + } else { + print STDERR "PASS ($1)\n"; + $pass++; + } + } else { + print STDERR "FAIL (unexpected result '$res')\n"; + $fail++; + } +} + +system("fhem-speech -d $ARGV[1] -a -q -o gsm -c $sounds") if ($ARGV[0] eq "d"); +system("fhem-speech -t $ARGV[1] -a -q -o gsm -c $sounds") if ($ARGV[0] eq "t"); +system("fhem-speech -d $ARGV[1] --set $ARGV[2]") if ($ARGV[0] eq "s"); + diff --git a/fhem/contrib/fhem2speech/fhem2speech.sh b/fhem/contrib/fhem2speech/fhem2speech.sh deleted file mode 100755 index 1a20c5e0b..000000000 --- a/fhem/contrib/fhem2speech/fhem2speech.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -if [ $# = 0 ]; then - echo "Usage: `basename $0` -f [filename]" - echo " `basename $0` -s \"Text\"" - exit 1; -elif [ $1 = -f ]; then - /usr/share/txt2pho/txt2pho -i $2 -f | /usr/bin/mbrola /usr/share/mbrola/de7/de7 - - \ - | /usr/bin/bplay -s 22050 -b 16 -q -elif [ $1 = -s ]; then - echo $2 | /usr/share/txt2pho/txt2pho -f | /usr/bin/mbrola /usr/share/mbrola/de7/de7 - - \ - | /usr/bin/bplay -s 22050 -b 16 -q -fi