Hi,
hier eine Anleitung zur Realisierung einer Sprachsteuerung für IP-Symcon mit Android, Raspberry, Asterisk
Hier ein kleines Demo Video:
[video=youtube_share;QfiRIS-OvX4]http://youtu.be/QfiRIS-OvX4[/video]
Vorweg ein paar Infos:
[ul]
[li]Spracherkennung über GoogleTTS (Internetverbindung notwendig!)[/li][li]Sprachmusterauswertung für Befehlssteuerung über php script in IP Symcon[/li][li]Asterisk auf Raspberry als VOIP Server[/li][li]SIP Client auf Android Device[/li][li]Server Socket als Perl Script auf Raspberry[/li][li]Server und Client-Socket eingerichtet in IP Symcon[/li][li]Ausbaufähig noch mit Handshakes, Asterisk konfigurieren um außerhalb WLAN zu erreichen, phpAGI Nutzung, offline TTS für Asterisk verwenden, usw…[/li][li]Spracherkennung nicht 100% zufriedenstellend[/li][/ul]
Server für IP-Symcon in meiner Beschreibung ist 10.0.0.88 und Raspberry ist 10.0.0.92
Asterisk auf Raspberry PI installieren
sudo apt-get install asterisk
Während Installation folgendes eingeben:
ITU-T-Telefon-Code: 43 (für Österreich) bzw. 49 (für Deutschland) usw…
Asterisk auf Raspberry PI starten
sudo /etc/init.d/asterisk start
Comannd Line Tool auf Raspberry PI testen
sudo asterisk -vvvr
Aussteigen mit Exit, Hilfe mit Help
Konfigurieren der Geräte in der sip.conf Datei auf Raspberry PI
Diese können sich anschließend am Asterik-Server anmelden.
sudo vi /etc/asterisk/sip.conf
Ich schreibe nur jene Einträge die ich modifiziert habe durch zurücknehmen des „;“ und teilweise ändern von den Werten oder komplett hinzufügen von Zeilen. Ich habe die Defaultports gelassen:
port 5060 for UDP and TCP, 5061 for TLS
[general]
allowguest=no
disable=all
allow=alaw
allow=ulaw
videosupport=yes
localnet=10.0.0.0/255.255.255.0
directmedia=no
nat=yes
und ganz zum Schluss beim File noch folgendes ergänzt:
[1000]
permit=10.0.0.0/255.255.255.0
type=friend
context=phones
host=dynamic
secret=PASSWORT
call-limit=3
contactdeny=0.0.0.0/0.0.0.0
contactpermit=10.0.0.0/255.255.0.0
[2000]
permit=10.0.0.0/255.255.255.0
type=friend
context=phones
host=dynamic
secret=PASSWORT
call-limit=3
contactdeny=0.0.0.0/0.0.0.0
contactpermit=10.0.0.0/255.255.0.0
Im Bereich „[general]“ befinden sich allgemeingültige Einstellungen für SIP mit unserer Asterisk-Installation. Dort wird z.B. der Port des Asterisk SIP-Servers angegeben, aus Sicherheitsgründen das lokale Netz und dass es sich hinter einem NAT-Router befindet. Würde man die Werte in „[general]“ raus nehmen, würde es hier trotzdem funktionieren.
Die Bereiche „[1000]“ markieren die Rufnummern und SIP-Anschlüsse. Wäre z.B. die gewünschte Rufnummer des Anschlusses nicht 1000, 2000 oder 3000 sondern 4711, dann kann man den Bereich auch mit [4711] beginnen. Die Einstellung „permit“ legt fest aus welchem Netzwerkbereich sich dieser Anschluss anmelden darf. Meine Anschlüsse dürfen sich also nur aus dem lokalen Netzwerk anmelden. Die Einstellung „type“ legt fest, was der Anschluss darf. „friend“ darf angerufen werden und selbst anrufen. „user“ darf nur anrufen, kann aber nicht angerufen werden. „peer“ kann angerufen werden, darf selbst aber keine Anrufe initiieren. Context legt eine Art Gruppe fest und wird in der extensions.conf wiederverwendet - dazu komme ich dann in einer der nächsten Abschnitte. „host=dynamic“ legt fest, dass der Anschluss sich von unterschiedlichen IP Adresse aus anmelden darf. Zu guter Letzt ist „secret“ noch das Passwort, welches der Anschluss bei der Anmeldung verwenden soll.
Erstellen eines Anrufbeantworters
sudo vi /etc/asterisk/voicemail.conf
Folgenden Inhalt abändern:
[general]
format = wav
[default]
1000 => 1001,Hans Mustermann,hansi@company.de
2000 => 2001,Ute Beispiel,ute.beispiel@company.de
;1001 und 2001 ist das Passwort wenn die Mailbox mit Anwahl Rufnummer 2999 vom Android Device abgefragt wird
Erstellen des Wählplans (Dialplan) in der extensions.conf
Damit die Geräte untereinander kommunizieren können.
sudo vi /etc/asterisk/extensions.conf
Im Default Bereich einen „;“ davor setzen um keine demo auf 1000 zu haben und auf internal zu verweisen:
[default]
;include => demo
include => internal
Ganz unten folgendes ergänzen:
[internal]
exten => _X.,1,Answer()
exten => _X.,2,Verbose(D E F A U L T ==> ${CALLERID(num)} kam um ${STRFTIME(${EPOCH},,%Y%m%d-%H%M%S)} in DEFAULT an als er versuchte die Nummer ${EXTEN} anzurufen.)
exten => _X.,3,Playback(hello-world)
exten => _X.,4,Hangup()
exten => 1001,1,Answer()
exten => 1001,2,Playback(hello-world)
exten => 1001,3,Hangup()
;;Speech recognition demo:
exten => 1236,1,Answer()
exten => 1236,n,agi(googletts.agi,"Sag nach dem Pieps Ton etwas auf Deutsch und drücke anschließend die # Taste",de)
exten => 1236,n(record),agi(speech-recog.agi,de-DE)
exten => 1236,n,Verbose(1,Script returned: ${status} , ${id} , ${confidence} , ${utterance})
;Check the probability of a successful recognition:
exten => 1236,n(success),GotoIf($["${confidence}" > "0.6"]?playback:retry)
;Playback the text:
exten => 1236,n(playback),agi(googletts.agi,"Der Text den ich erkannt habe war...",de)
exten => 1236,n,agi(googletts.agi,"${utterance}",de)
exten => 1236,n,System(echo '${utterance}' | nc -w 1 10.0.0.88 4099)
exten => 1236,n,goto(end)
;Retry in case speech recognition wasn't successful:
exten => 1236,n(retry),agi(googletts.agi,"Bitte deutlicher sprechen",de)
exten => 1236,n,goto(record)
exten => 1236,n(fail),agi(googletts.agi,"Ich konnte den Text nicht verstehen",de)
exten => 1236,n(end),Hangup()
exten => 1000,1,Dial(SIP/1000,20)
exten => 1000,2,VoiceMail(1000,u)
exten => 2000,1,Dial(SIP/2000,20)
exten => 2000,2,VoiceMail(2000,u)
exten => 2999,1,VoiceMailMain(${CALLERID(num)},s)
exten => 9999,1,Answer()
exten => 9999,n,agi(googletts.agi,"Hier spricht dein IP-Symcon. Bitte gib mir einen Befehl nach dem Pieps Ton. Bei starkem Hintergrundgeräusch drücke anschließend das #",de)
exten => 9999,n(record),agi(speech-recog.agi,de-DE)
exten => 9999,n,System(yes | rm /home/pi/asterisk.txt)
exten => 9999,n,Verbose(1,Script returned: ${status} , ${id} , ${confidence} , ${utterance})
;Check the probability of a successful recognition (quality > 0.55)
exten => 9999,n(success),GotoIf($["${confidence}" > "0.53"]?playback:retry)
;Playback the text, send to IP Sycmon, get response from IP Sycmon, check response, check ENDE
exten => 9999,n(playback),GotoIf($["${utterance}" = "auflegen" | "${utterance}" = "Auflegen"]?ende:continue)
;exten => 9999,n(continue),agi(googletts.agi,"Folgenden Befehl werde ich umsetzen",de)
;exten => 9999,n,agi(googletts.agi,"${utterance}",de)
exten => 9999,n(playback),System(echo '${utterance}' | sed -e 'ß' 'ss' -e s/ä/ae/g -e s/ö/oe/g -e s/ü/ue/g | nc -w 1 10.0.0.88 4099)
exten => 9999,n,Set(zaehler=0)
;exten => 9999,n,Wait(1)
exten => 9999,n,Set(filestatus=${SHELL(find '/home/pi/asterisk.txt' 2>/dev/null | grep -q /home/pi/asterisk.txt && echo 1 || echo 0)})
exten => 9999,n,While($[${filestatus} < 1])
exten => 9999,n,Wait(1)
exten => 9999,n,Set(zaehler=$[${zaehler} + 1])
exten => 9999,n,Set(filestatus=${SHELL(find '/home/pi/asterisk.txt' 2>/dev/null | grep -q /home/pi/asterisk.txt && echo 1 || echo 0)})
exten => 9999,n,ExecIf($["${zaehler}" > 60 ]?ExitWhile:ContinueWhile())
exten => 9999,n,EndWhile()
exten => 9999,n,Set(utterance=${SHELL(cat /home/pi/asterisk.txt | tr -d '
\r')})
exten => 9999,n,GotoIf($["${utterance}" = "" | "${utterance}" = "0"]?no_file:neu)
exten => 9999,n,goto(neu)
;Retry in case speech recognition wasn't successful:
exten => 9999,n(retry),agi(googletts.agi,"Bitte deutlicher sprechen",de)
exten => 9999,n,goto(record)
exten => 9999,n(neu),agi(googletts.agi,"${utterance}",de)
exten => 9999,n,agi(googletts.agi,"Bitte gib mir einen neuen Befehl. Zum Beenden sage einfach auflegen",de)
exten => 9999,n,goto(record)
exten => 9999,n(no_file),agi(googletts.agi,"Ich habe keine Antwort von mir selbst in /home/pi/asterisk.txt erhalten. Wiederhole deinen Befehl bitte",de)
exten => 9999,n,goto(record)
exten => 9999,n(fail),agi(googletts.agi,"Ich konnte den Text nicht verstehen",de)
exten => 9999,n,goto(ende)
exten => 9999,n(ende),agi(googletts.agi,"Vielen Dank für das nette Gespräch. Bis zum nächsten Mal. Tschüss",de)
exten => 9999,n,Wait(1)
exten => 9999,n,Hangup()
[AnrufundSprachausgabe]
exten => s,1,Answer()
exten => s,n,Wait(1)
exten => s,n,Set(utterance=${SHELL(cat /home/pi/asterisk.txt | tr -d '
\r')})
exten => s,n,GotoIf($["${utterance}" = "" | "${utterance}" = "0"]?no_file,1)
exten => s,n,agi(googletts.agi,"${utterance}",de)
exten => s,n,System(yes | rm /home/pi/asterisk.txt)
exten => s,n,Wait(1)
exten => s,n,Hangup()
exten => no_file,1,agi(googletts.agi,"Datei /home/pi/asterisk.txt existiert nicht oder der Inhalt ist leer",de)
exten => no_file,n,Wait(1)
exten => no_file,n,Hangup()
[phones]
include => internal
Darin zu sehen sind jetzt wieder die bekannten Abschnitte wie „[globals]“ für globale Variablen und „[general]“ für generelle Einstellungen. Interessant wird es bei „[internal]“, welches der Kontext für die internen Telefonate ist. Der Abschitt ganz unten ist der Kontext unserer Telefone, der ja „[phones]“ heißt. Mit „include => internal“ schließt der „phones“-Kontext schließt den Kontext „internal“ mit ein. In „[internal]“ befindet sich der eigentliche Wählplan, der hier nur ein Einzeiler ist und besagt, dass alle vierstelligen Nummern, die von unseren Anschlüssen angewählt werden dazu führen sollen, dass diese Rufnummer über SIP intern angerufen wird.
Wählpläne in Asterisk sind sehr umfangreich und bieten eine enorme Vielzahl an Möglichkeiten. Es bedarf jedoch auch eine sehr große Zeit, sich in diese einzuarbeiten.
Erstellen der Remote Telnet Möglichkeit (Optional)
ACHTUNG: Viele Rechte und Unsicheres Protokoll!!!
sudo vi /etc/asterisk/manager.conf
Einfügen von:
[general]
enabled = yes
port = 5038
; IP-Adresse Asterisk Server
bindaddr = 10.0.0.92
; No access is allowed by default.
; To set a password, create a file in /etc/asterisk/manager.d
; use creative permission games to allow other serivces to create their own
; files
include "manager.d/*.conf"
sudo vi /etc/asterisk/manager.d/REMOTE.conf
Folgender Inhalt
[admin]
secret = TESTPASSWORT
deny = 0.0.0.0/0.0.0.0
permit = 10.0.0.0/255.255.255.0
read = all,system,call,log,verbose,command,agent,user,config
write = all,system,call,log,verbose,command,agent,user,config
Reload der Configs
sudo asterisk -vvvr
Und danach folgende Befehle nacheinander:
sip reload
dialplan reload
manager reload
Danach mit Exit aussteigen
Änderung /etc/group und Gruppenschreibrecht auf /home/pi Benutzer
sudo chmod g+w /home/pi
sudo vi /etc/group
Alt:
pi:x:1000
Neu:
pi:x:1000:asterisk
Konfigurieren der Android Geräte für Asterisk
[ul]
[li]Download von CSipSimple vom Playstore und Grundeinstellung setzen bzgl. Erreichbarkeit (zB: nur WLAN)[/li][li]- Neues Konto hinzufügen und dann Auswahl von Basic und Eingabe von Kontoname (beliebig wählbar), Benutzername 1000 (Telefonnummer für erstes Device), SIP Server (IP Adresse des Raspberry), Passwort (Passwort aus sip.conf Datei) danach sollte der Eintrag auf Grün gehen und kurz mit SIP registriert auftauchen[/li][/ul]
Testen Android Geräte für Asterisk
zB: von Android Handy (1000) auf Android Tablet (2000) einfach die 2000 wählen und schon kann man Mal untereinander telefonieren. Also erster Schritt mit der Haus Telefonanlage funktioniert. Beim Anwählen von zB: 1001 sollte ein Hello World erfolgen.
Installieren verschiedener Spracherkennungsmodule auf Raspberry
wget https://github.com/downloads/zaf/asterisk-speech-recog/asterisk-speech-recog-0.5.tar.gz
wget https://github.com/downloads/zaf/asterisk-googletranslate/asterisk-googletranslate-0.2.tar.gz
wget https://github.com/downloads/zaf/asterisk-googletts/asterisk-googletts-0.6.tar.gz
gunzip asterisk-speech-recog-0.5.tar.gz
gunzip asterisk-googletranslate-0.2.tar.gz
gunzip asterisk-googletts-0.6.tar.gz
tar -xvf asterisk-speech-recog-0.5.tar
tar -xvf asterisk-googletranslate-0.2.tar
tar -xvf asterisk-googletts-0.6.tar
sudo cp asterisk-speech-recog-0.5/speech-recog.agi /usr/share/asterisk/agi-bin/.
sudo cp asterisk-googletranslate-0.2/googletranslate.agi /usr/share/asterisk/agi-bin/.
sudo cp asterisk-googletts-0.6/googletts.agi /usr/share/asterisk/agi-bin/.
sudo apt-get install perl libwww-perl flac sox mpg123 libjson-perl libio-socket-ssl-perl build-essential tofrodos
Testen Android Geräte für Asterisk Teil2
Wenn man jetzt vom Handy aus 1236 wählt, dann sollte der erste Test möglich sein mit Google Spracherkennung und Feedback des erkannten gesprochenen über die Google API
Einbinden in IP Symcon per ServerSocket
a.) Neue Instanz als Server Socket hinzufügen
WICHTIG: „Alle Module anzeigen“ auswählen und dann Server Socket wählen
Name „AsteriskSprachsteuerungInput“ und Port 4099. Sollte danach so aussehen
b.) Neue String Variable unter dieser Instanz anlegen mit dem Namen „receiveSprachsteuerung“
c.) Neues Script unter dieser Instanz anlegen mit folgendem Namen „receiveNCdata“ und folgendem Inhalt (die ID der Variable am Anfang mit Eurer ersetzen)
<?
$ID_ReceiveText = 50029 /*[AsteriskSprachsteuerungInput\receiveSpracherkennung]*/;
// wenn das Skript von einer RegisterVariable-Instanz aus aufgerufen worden ist
if ($_IPS['SENDER'] == "RegisterVariable")
{
// bereits im Puffer der Instanz vorhandene Daten in $data kopieren
$data = RegVar_GetBuffer($_IPS['INSTANCE']);
// neu empfangene Daten an $data anhängen
$data .= $_IPS['VALUE'];
// Inhalt von $data in eine Variable schreiben
SetValueString($ID_ReceiveText,$data);
// Lerren Inhalt im Puffer der RegisterVariable-Instanz speichern
RegVar_SetBuffer($_IPS['INSTANCE'], '');
}
?>
d.) Neue Instanz als Registered Variable hinzufügen mit Namen „receiveNC“
Danach diese Variable receiveNC noch per Doppelklick eine übergeordnete Instanz zuweisen
Port 4099 gegeben falls in Windows Firewall freischalten notwendig für eingehende Verbindungen!!!
Erster Test ob das gesprochene auch in IP Symcon ankommt
Auf dem Raspberry folgendes Kommando aufrufen um das Log zu sehen:
sudo asterisk -vvvr
Am Android Handy die 1236 wählen und als Test Befehl „Das ist ein Test sprechen“ und nachsehen ob es so ankommt wie unten ersichtlich.
Jetzt geht es richtig ins eingemachte mit einem Callback in Form eines Client Sockets für den Raspberry
Auf Raspberry:
sudo vi autobahn.pl
und folgenden Inhalt einfügen:
#!/usr/bin/perl
#autobahn.pl
use IO::Socket::INET;
use Encode qw(encode decode);
use utf8;
# flush after every write
$| = 1;
my ($socket,$client_socket);
my ($peeraddress,$peerport);
# creating object interface of IO::Socket::INET modules which internally does
# socket creation, binding and listening at the specified port address.
$socket = new IO::Socket::INET (
LocalHost => '10.0.0.92',
LocalPort => '9001',
Proto => 'tcp',
Listen => 5,
Reuse => 1
) or die "ERROR in Socket Creation : $!
";
#print "SERVER Waiting for client connection on port 9001";
while(1)
{
# waiting for new client connection.
$client_socket = $socket->accept();
# get the host and port number of newly connected client.
$peer_address = $client_socket->peerhost();
$peer_port = $client_socket->peerport();
#print "Accepted New Client Connection From : $peeraddress, $peerport
";
# write operation on the newly accepted client.
#$data = "DATA from Server";
#print $client_socket "$data
";
# we can also send the data through IO::Socket::INET module,
# $client_socket->send($data);
# read operation on the newly accepted client
#$data = <$client_socket>;
# we can also read from socket through recv() in IO::Socket::INET
$client_socket->recv($data,1024);
utf8::decode($data);
$data =~ s/ä/ä/g;
$data =~ s/ö/ö/g;
$data =~ s/ü/ü/g;
$data =~ s/Ä/Ä/g;
$data =~ s/Ö/Ö/g;
$data =~ s/Ü/Ü/g;
$data =~ s/ß/ß/g;
utf8::encode($data);
#print "Received from Client : $data
";
#if $data begin with Text: write it to /home/pi/asterisk.txt
if ($data =~ /^Text:/)
{
@arraytext = split(/:/, $data);
open(ASTERISK, ">/home/pi/asterisk.txt");
print ASTERISK "$arraytext[1]
";
close(ASTERISK);
$cnt = chmod 0777, "/home/pi/asterisk.txt";
$cnt = chown pi, pi, "/home/pi/asterisk.txt";
}
#if $data begin with Receiver: write it to /var/spool/asterisk/outgoing/call.call
#split the data by ; and write it with following format example
#Channel: SIP/2000
#MaxRetries: 0
#WaitTime: 30
#Context: AnrufundSprachausgabe
#Priority: 1
#Extension: 2000
if ($data =~ /^Receiver:/)
{
@arrayreceiver = split(/:/, $data);
@arraydata = split(/;/, $arrayreceiver[1]);
open(CALL, ">/var/spool/asterisk/outgoing/call.call");
print CALL "Channel: $arraydata[0]
";
print CALL "MaxRetries: $arraydata[1]
";
print CALL "WaitTime: $arraydata[2]
";
print CALL "Context: $arraydata[3]
";
print CALL "Priority: $arraydata[4]
";
print CALL "Extension: $arraydata[5]
";
close(CALL);
}
#close the client socket
$client_socket->close();
}
#close the server socket (with endless while this will never happen)
$socket->close();
Starten mit folgendem Kommando im Hintergrund für erste Testzwecke.
sudo perl autobahn.pl &
Eventuell ist es wieder notwendig entsprechend die Firewall auf dem IP Symcon Server bzw. die iptables auf dem Raspberry anzupassen wenn man damit arbeitet!
Client Socket in IP Symcon einrichten
a.) Neue Instanz als Client Socket hinzufügen (zu finden im gleichen Baum wie der Server Socket)
Name „AsteriskSprachsteuerungOutput“ und IP des Raspberry und Port 9001. Sollte danach so aussehen
Ich habe nicht meine richtige IP für die Screenshots verwendet. Nicht wundern weil er bei mir nicht grün ist
b.) Neue String Variable anlegen mit dem Namen „BufferSendAutobahn“
Neue Integer Variable anlegen mit dem Namen „errorCountAutobahn“
c.) Neues Script unter dieser Instanz anlegen mit dem Namen „sendAutobahndata“ und folgendem Inhalt
<?
// Change Configuration parameters if needed
//$maxRetry = 5; // retries until buffer is cleared
$socketReadyDelay = 2; //delay between retries if socket not ready
$msgDelay = 2; // delay in sec until timeout and command re-send command
//-------------------------------------------------------------------------------------------
// Nothing to configure below this line
$Id_ClientSocket = IPS_GetParent($_IPS['SELF']);
$IDRegVar = @IPS_GetObjectIDByName("sendAutobahn", $Id_ClientSocket);
$IDBuffer = IPS_GetVariableIDByName("BufferSendAutobahn", $Id_ClientSocket);
$IDerrorCount = IPS_GetVariableIDByName("errorCountAutobahn", $Id_ClientSocket);
print_r($_IPS['SENDER']);
Switch ($_IPS['SENDER'])
{
Case "RunScript":
if (IPS_GetInstance($Id_ClientSocket)['InstanceStatus'] != 102) {// Check if Socket is Active
CSCK_SetOpen($Id_ClientSocket,true);
IPS_ApplyChanges($Id_ClientSocket);
//if (IPS_GetInstance($Id_ClientSocket)['InstanceStatus'] != 102) {
// IPS_SetScriptTimer($IPS_SELF, $socketReadyDelay); //arm Timer for retry
// echo "Keine Verbindung zu ".IPS_GetName($Id_ClientSocket);
// Return; // Stop in case Socket Open fail
// }
}
$buffer=GetValueString($IDBuffer); //re-read in case modified meanwhile
RegVar_SendText($IDRegVar, $buffer);
//IPS_SetScriptTimer($IPS_SELF, $msgDelay); //arm Timer for retry if Fail
break;
// Case "TimerEvent":
// IPS_SetScriptTimer($IPS_SELF, 0); //stop Timer
// $errorCount=GetValue($IDerrorCount);
// $errorCount=$errorCount+1;
// if ($errorCount < $maxRetry) {
// SetValue($IDerrorCount,$errorCount);
// IPS_RunScript($_IPS['SELF']); // Run next try
// }
// else {
// SetValue($IDerrorCount,0);
// SetValueString($IDBuffer,"nothing to send");
// // todo: add additional error processing there
// }
// break;
Default: // send Hash ti initiate handshake
print_r($_IPS['SENDER']);
//$data .= ":";
//$hash = md5($data);
//$hash .= "
";
//RegVar_SendText($IDRegVar, $hash);
//Break;
}
?>
d.) Registered Variable anlegen mit Namen „sendAutobahn“ und mit der Instanz „AsteriskSprachsteuerungOutput“ und dem Script „sendAutobahndata“ verknüpfen
Das ganze sollte jetzt in etwa so aussehen mit IP 10.0.0.92
Include Script in IP Symcon anlegen
Neues Script erstellen mit Namen „SendToAutobahn_include“ irgendwo in Eurem Bereich und folgenden Inhalt einfügen.
<?
function SendtoAutobahn($Id_ClientSocket,$new) {
$IDBuffer = @IPS_GetObjectIDByName("BufferSendAutobahn", $Id_ClientSocket);
$IDSendScript = @IPS_GetObjectIDByName("sendAutobahndata", $Id_ClientSocket);
SetValue($IDBuffer,$new);
IPS_RunScript($IDSendScript);
}
function tsay($Id_ClientSocket,$msg) {
$tsay = "Text: " . $msg;
SendtoAutobahn($Id_ClientSocket,$tsay);
}
function tcall($Id_ClientSocket,$itelnr) {
$trecv = "Receiver: SIP/" . $itelnr . ";0;30;AnrufundSprachausgabe;1;" . $itelnr;
SendtoAutobahn($Id_ClientSocket,$trecv);
}
?>
Anschließend auf Script doppelklicken und im Menü Umbennen Button klicken um von zB: 21190.ips.php den physischen Scriptnamen umzubennen auf SendToAutobahn_include.ips.php
Einrichten damit Perl Socket Server in Zukunft über Autostart am Raspberry lauft
sudo vi /etc/init.d/autobahn
Mit folgendem Inhalt
#! /bin/sh
# /etc/init.d/autobahn
perl /home/pi/autobahn.pl &
Danach:
sudo chmod 755 /etc/init.d/autobahn
sudo update-rc.d autobahn defaults
IP Symcon Beispiel Script für Anruf und Sprachausgabe einer Meldung
Manuell testen auf Raspberry indem 2 Dateien angelegt werden
sudo vi /home/pi/asterisk.txt
Inhalt:
Das ist ein manueller Test Alarm
Danach folgende Kommandos:
sudo chmod 777 /home/pi/asterisk.txt
sudo chown pi:pi /home/pi/asterisk.txt
sudo vi /var/spool/asterisk/outgoing/call.call
Mit folgendem Inhalt:
Channel: SIP/2000
MaxRetries: 0
WaitTime: 30
Context: AnrufundSprachausgabe
Priority: 1
Extension: 2000
Die 2te Datei wird nach dem erfolgten Anruf automatisch wieder gelöscht von Asterisk. Hier lasse ich mich auf dem Tablet testweise anrufen.
Jetzt das IP Symcon Script der über den Client Socket geht und einen Anruf tätigt mit Namen „Beispiel_Tabletanrufen_Textausgeben„ anlegen und folgendem Inhalt
<?
include_once "SendToAutobahn_include.ips.php";
// this library provides SendtoAutobahn() function;
// here we need our client socket (for example the websocket autobahn python script on Raspberry
// where also the asterisk virtual VOIP server is running)
$Autobahn = 40934 /*[AsteriskSprachsteuerungOutput]*/;
// at first for output to fill on Raspberry server the /home/pi/asterisk.txt file
// we need first the text
tsay($Autobahn,"Hier spricht dein IP-Symcon. Soeben wurde Hochwasseralarm ausgelöst!");
sleep(1);
// and then we need our Asterisk Receiver in following format
// Channel; Max Retries; Waittime; Context; Priority; Callerid
// example for internal telephone number 2000:
// SIP/2000;0;30;AnrufundSprachausgabe;1;2000
// with this information a file /var/spool/asterisk/outgoing/call.call will be created on your client socket
// (for example Raspberry with installed Asterisk virtual VOIP server)
// a simple example to only specify the internal telephone number (waittime, max retries and so on automatically filled
tcall($Autobahn,"2000");
// another way to specify all parameter
//SendtoAutobahn($Autobahn,"Receiver: SIP/2000;0;30;AnrufundSprachausgabe;1;2000");
?>
Danach das Script ausführen und Ihr solltet einen Anruf erhalten mit der Textansage
IP Symcon Shutter Control Script einrichten und Instanzen erstellen und einmessen
http://www.ip-symcon.de/service/dokumentation/modulreferenz/shutter-control
(ich habe zB: Möller Eaton XComfort)
IP Symcon Beispiel Script für Haussteuerung Rollladen
Jetzt ein Script anlegen mit folgendem Namen: „Beispiel_Rollladen_Temperatur“
<?
include_once "SendToAutobahn_include.ips.php";
// this library provides SendtoAutobahn() function;
// here we need our client socket (for example the websocket autobahn python script on Raspberry
// where also the asterisk virtual VOIP server is running)
$Autobahn = 40934 /*[AsteriskSprachsteuerungOutput]*/;
$input = GetValueString(50029 /*[AsteriskSprachsteuerungInput\receiveSpracherkennung]*/);
//hier in einem Array jetzt alle möglichen Variablen oder Geräteinstanzen etc... reinpacken
//(abgekupfert die Logik und ein klein wenig erweitert)
global $tg_info_devices;
$tg_info_devices = array(
"Büro1" => array("Typ" => "Licht", "Variable" => 12345, "Aussprache" => "Im Büro ist das Licht" ),
"Büro2" => array("Typ" => "Rollladen", "Variable" => 41402, "Aussprache" => "Im Büro ist der Rollladen" ),
"Schlafzimmer1" => array("Typ" => "Licht", "Variable" => 12346, "Aussprache" => "Im Schlafzimmer ist das Licht" ),
"Schlafzimmer2" => array("Typ" => "Temperatur", "Variable" => 36426, "Aussprache" => "Im Schlafzimmer beträgt die Temperatur" ),
"Schlafzimmer3" => array("Typ" => "Luftfeuchtigkeit", "Variable" => 39357, "Aussprache" => "Im Schlafzimmer beträgt die Luftfeuchtigkeit" ),
"Schlafzimmer4" => array("Typ" => "Rollladen", "Variable" => 52336, "Aussprache" => "Im Schlafzimmer ist der Rollladen" ),
"aussen1" => array("Typ" => "Windaktuell", "Variable" => 56046, "Aussprache" => "Draussen gibt es einen Wind von" ),
"aussen2" => array("Typ" => "Windböen", "Variable" => 40250, "Aussprache" => "Draussen existieren Windböen mit" ),
"aussen3" => array("Typ" => "Temperatur", "Variable" => 32134, "Aussprache" => "Draussen hat die Temperatur" ),
"aussen4" => array("Typ" => "Luftfeuchtigkeit", "Variable" => 40108, "Aussprache" => "Draussen hat es eine Luftfeuchtigkeit von" ),
"aussen5" => array("Typ" => "Regen", "Variable" => 59347, "Aussprache" => "Draussen regnet es" ),
"Wohnzimmer1" => array("Typ" => "Rollladen", "Variable" => 41403, "Art" => "Tür", "Aussprache" => "Im Wohnzimmer ist das Raffstore der Tür" ),
"Wohnzimmer2" => array("Typ" => "Rollladen", "Variable" => 41404, "Art" => "Fenster", "Aussprache" => "Im Wohnzimmer ist das Raffstore des Fensters" ),
"Wohnzimmer3" => array("Typ" => "Temperatur", "Variable" => 15013, "Aussprache" => "Im Wohnzimmer beträgt die Temperatur" ),
"Wohnzimmer4" => array("Typ" => "Luftfeuchtigkeit", "Variable" => 43371, "Aussprache" => "Im Wohnzimmer beträgt die Luftfeuchtigkeit" ),
"Bad1" => array("Typ" => "Temperatur", "Variable" => 42086, "Aussprache" => "Im Bad beträgt die Temperatur" ),
"Bad2" => array("Typ" => "Luftfeuchtigkeit", "Variable" => 10945, "Aussprache" => "Im Bad beträgt die Luftfeuchtigkeit" )
);
// a.) Suche nach Zahl, Aktion, Raum im Kommando (ansonsten wenn Aktion nicht gefunden mit Meldung aussteigen)
$zahl = 0;
$detail = "";
$tsay = "";
switch (true) {
// Suche nach erster Zahl im Kommando
case (preg_match("([1-9]+)", $input, $matches)) :
$zahl = $matches[0];
case (preg_match("/Rol|Store/i", $input) ? true : false) :
$typ = "Rollladen";
break;
case (preg_match("/Licht|Beleuchtung|Lampe|Led/i", $input) ? true : false) :
$typ = "Licht";
break;
case (preg_match("/grad|temperatur/i", $input) ? true : false ) :
$typ = "Temperatur";
break;
case (preg_match("/luft|feucht/i", $input) ? true : false ) :
$typ = "Luftfeuchtigkeit";
break;
case (preg_match("/boeen|spitze/i", $input) ? true : false ) :
$typ = "Windböen";
break;
case (preg_match("/kmh|wind|geschwindigkeit/i", $input) ? true : false ) :
$typ = "Windaktuell";
break;
case (preg_match("/regen|niederschlag/i", $input) ? true : false ) :
$typ = "Regen";
break;
}
switch (true) {
case (preg_match("/schliesse|zu|runter|unten/i", $input) ? true : false) :
$aktion = "runter";
break;
case (preg_match("/oeffne|auf|hoch|oben/i", $input) ? true : false ) :
$aktion = "rauf";
break;
case (preg_match("/an|ein|on/i", $input) ? true : false ) :
$aktion = "ein";
break;
case (preg_match("/aus|off/i", $input) ? true : false ) :
$aktion = "aus";
break;
case (preg_match("/wer|wie|was|wo|warum|weshalb|wann|welche|wieviel|sage|antworte/i", $input) ? true : false ) :
$aktion = "frage";
break;
default :
$aktion = "frage";
break;
}
switch (true) {
case (preg_match("/sekunde/i", $input) ? true : false ) :
$detail = "Sekunden";
break;
}
switch (true) {
case (preg_match("/tuer/i", $input) ? true : false ) :
$art = "Tür";
break;
case (preg_match("/fenster/i", $input) ? true : false ) :
$art = "Fenster";
break;
case (preg_match("/nord/i", $input) ? true : false ) :
$art = "Norden";
break;
case (preg_match("/sued/i", $input) ? true : false ) :
$art = "Süden";
break;
case (preg_match("/west/i", $input) ? true : false ) :
$art = "Westen";
break;
case (preg_match("/ost/i", $input) ? true : false ) :
$art = "Osten";
break;
case (preg_match("/decke/i", $input) ? true : false ) :
$art = "Decke";
break;
case (preg_match("/wand/i", $input) ? true : false ) :
$art = "Wand";
break;
}
switch (true) {
case (preg_match("/buero/i", $input) ? true : false ) :
$raum = "Büro";
break;
case (preg_match("/wohn/i", $input) ? true : false ) :
$raum = "Wohnzimmer";
break;
case (preg_match("/schlaf/i", $input)) :
$raum = "Schlafzimmer";
break;
case (preg_match("/kueche/i", $input) ? true : false ) :
$raum = "Küche";
break;
case (preg_match("/bad/i", $input) ? true : false ) :
$raum = "Bad";
break;
case (preg_match("/ess/i", $input) ? true : false ) :
$raum = "Essplatz";
break;
case (preg_match("/klo|wc/i", $input) ? true : false ) :
$raum = "WC";
break;
case (preg_match("/gaeste|kinder/i", $input) ? true : false ) :
$raum = "Gästezimmer";
break;
case (preg_match("/party|feier/i", $input) ? true : false ) :
$raum = "Partyraum";
break;
case (preg_match("/garage/i", $input) ? true : false ) :
$raum = "Garage";
break;
case (preg_match("/alle/i", $input) ? true : false ) :
$raum = "alle";
break;
case (preg_match("/aussen/i", $input) ? true : false ) :
$raum = "Aussen";
break;
}
if ($zahl == 0) {
$zahl = 1 ;
}
//print_r($typ);
//print_r($raum);
//print_r($aktion);
//print_r($zahl);
// b.) Auswerten ob $raum gesetzt
if (ISSET($raum) == FALSE ) {
tsay($Autobahn,"Du hast vergessen eine Örtlichkeit bzw. einen Raum mit zu geben! Mit diesem Befehl kann ich so nichts anfangen!");
exit(0);
}
// c.) Auswerten ob $aktion gesetzt
if (ISSET($aktion) == FALSE ) {
tsay($Autobahn,"Du hast vergessen eine Aktion mit zu geben! Mit diesem Befehl kann ich so nichts anfangen!");
exit(0);
}
// d.) Auswerten ob $typ gesetzt
if (ISSET($typ) == FALSE ) {
tsay($Autobahn,"Du hast vergessen einen Typ mit zu geben! Mit diesem Befehl kann ich so nichts anfangen!");
exit(0);
}
// e.) Steuern der Aktionen
// lese zuerst alle Typen die übereinstimmen aus dem Array
foreach( $tg_info_devices as $rauma => $value ) {
foreach( $value as $wert => $inhalt ) {
if ($wert == "Typ" & preg_match("/$typ/i", $inhalt) == true ) {
//jetzt brauchen wir auch die nächsten beiden
$leseweiter = TRUE;
}
if ($wert == "Typ" & preg_match("/$typ/i", $inhalt) == false) {
$leseweiter = FALSE;
}
if ($leseweiter == TRUE & $wert == "Variable" ) {
$variablea = $inhalt;
}
if ($leseweiter == TRUE & $wert == "Art" ) {
$arta = $inhalt;
}
if ($leseweiter == TRUE & $wert == "Aussprache" ) {
$aussprache = $inhalt;
//hier jetzt die Aktion durchführen wenn auch Raum übereinstimmt!
$leseweiter = FALSE;
if ( preg_match("/$raum/i", $rauma) == true | $raum == "alle" ) {
//zuerst noch Art abfragen wenn man zB: 2 Fenster, Lichter etc. in einem Raum hat
//ob Match gegeben ist
$continue = 0;
if (ISSET($art) == TRUE ) {
if ($art == $arta) {
$continue = 1;
}
}
else {
$continue = 1;
}
if ($continue == 1) {
switch ($typ) {
case "Rollladen" :
if ($aktion == "runter") {
if ($detail == "Sekunden") {
$zahlms = $zahl * 1000;
SC_MoveDown($variablea,$zahlms);
$tsay .= " $aussprache $zahl Sekunden nach unten bewegt worden!";
} else {
SC_MoveDown($variablea,0);
$tsay .= " $aussprache jetzt unten";
}
}
if ($aktion == "rauf") {
if ($detail == "Sekunden") {
$zahlms = $zahl * 1000;
SC_MoveUp($variablea,$zahlms);
$tsay .= " $aussprache $zahl Sekunden nach oben bewegt worden!";
} else {
SC_MoveUp($variablea,0);
$tsay .= " $aussprache jetzt oben";
}
}
break;
case "Licht" :
if ($aktion == "ein") {
//offen wie zu implementieren
}
if ($aktion == "aus") {
//offen wie zu implementieren
}
break;
case "Temperatur" :
$temperatur = GetValueFloat($variablea);
$tsay .= " $aussprache $temperatur Grad";
break;
case (preg_match("/Wind/i", $typ) ? true : false ) :
$wind = GetValueFloat($variablea);
$tsay .= " $aussprache $wind Kilometer pro Stunde";
break;
case "Luftfeuchtigkeit" :
$luftfeuchtigkeit = GetValueInteger($variablea);
$tsay .= " $aussprache $luftfeuchtigkeit Prozent";
break;
case "Regen" :
$regen = GetValueFloat($variablea);
$tsay .= " $aussprache $regen Millimeter";
break;
}
}
}
}
}
}
if ($tsay == "") {
$tsay = "Der Befehl konnte von mir nicht ausgewertet werden. Bitte sei präziser";
}
//Ausgabe aller gesammelten Nachrichten an Asterisk
tsay($Autobahn,"$tsay");
//f.) Offene Themen
//f1.) Prozent Angaben für öffnen /schließen usw.. aus IPSShadowing Logik?
//f2.) Wie bekommt IPSShadowing den veränderten Status mit bei Rollladen schließen über Shutter Modul???
//f3.) Licht RGB und dimmen wie implementieren in dieser Logik???
//g.) Ansätze für Lösungen
//???
//h.) Text zurücksenden an Asterisk
//tsay($Autobahn,"Der Rollladen im $RAUM wurde auf $PROZENT Prozent bewegt!");
//tsay($Autobahn,"Der Rolladen im $RAUM wurde auf $PROZENT Prozent bewegt!");
//tsay($Autobahn,"Der Rolladen im $RAUM wurde komplett geöffnet!");
//tsay($Autobahn,"Der Rolladen im $RAUM wurde komplett geschlossen!");
//tsay($Autobahn,"Der Rolladen im $RAUM wurde $PROZENT Prozent geöffnet");
//tsay($Autobahn,"Der Rolladen im $RAUM wurde $PROZENT Prozent geschlossen");
//tsay($Autobahn,"Die Temperatur $RAUM beträgt $GRAD Grad Celsius.");
//tsay($Autobahn,"Die Luftfeuchtigkeit $RAUM beträgt $PROZENT Prozent.");
//tsay($Autobahnn,"Das Licht $RAUM ist jetzt $STATUS");
//tsay($Autobahn,"Der Regen $ZEITRAUM betägt $LITER Liter pro Quadratmeter");
//tsay($Autobahn,"Die Windspitze $ZEITRAUM war $KMH Kilometer pro Stunde");
?>
Danach bei diesem Script ein Ereignis ergänzen. Und zwar ein auslösendes mit Variable AsteriskSprachsteuerungInput\receiveSpracherkennung und Status „bei Variablenaktualisierung“
Jetzt die Telefonnummer 9999 anrufen und genießen!!!
Weiterführende Links:
http://www.asterisk.org
http://www.kammerath.net/asterisk-virtuelle-telefonanlage.html
http://zaf.github.io/asterisk-googletts/
http://das-asterisk-buch.de/1.6/
http://phpagi.sourceforge.net/
http://das-asterisk-buch.de/1.6/applications.html