From e1b3c7df44b9b2706f4d6f66d50144f65d67faee Mon Sep 17 00:00:00 2001
From: Wzut <>
Date: Wed, 15 Mar 2017 19:07:32 +0000
Subject: [PATCH] 96_SIP: add Text2Speech support for all audio attributes
git-svn-id: https://svn.fhem.de/fhem/trunk@13712 2b470e98-0d58-463d-a4d8-8e2adae1ed80
---
fhem/FHEM/96_SIP.pm | 289 ++++++++++++++++++++++++++++++++------------
1 file changed, 212 insertions(+), 77 deletions(-)
diff --git a/fhem/FHEM/96_SIP.pm b/fhem/FHEM/96_SIP.pm
index 7717896ee..63292a6d4 100644
--- a/fhem/FHEM/96_SIP.pm
+++ b/fhem/FHEM/96_SIP.pm
@@ -53,7 +53,7 @@ use Net::Domain qw( hostfqdn );
use Blocking; # http://www.fhemwiki.de/wiki/Blocking_Call
#use Data::Dumper;
-my $sip_version ="V1.41 / 13.03.17";
+my $sip_version ="V1.42 / 15.03.17";
my $ua; # SIP user agent
my %sets = (
@@ -109,7 +109,6 @@ sub SIP_Define($$)
$hash->{STATE} = "defined";
$hash->{VERSION} = $sip_version;
$hash->{".reset"} = 0;
-
$attr{$name}{sip_ringtime} = '3' unless (exists($attr{$name}{sip_ringtime}));
$attr{$name}{sip_user} = '620' unless (exists($attr{$name}{sip_user}));
$attr{$name}{sip_ip} = $addr unless (exists($attr{$name}{sip_ip}));
@@ -122,16 +121,24 @@ sub SIP_Define($$)
$attr{$name}{sip_from} = 'sip:'.$attr{$name}{sip_user}.'@'.$attr{$name}{sip_registrar} unless (exists($attr{$name}{sip_from}));
RemoveInternalTimer($hash);
- InternalTimer(gettimeofday()+5, "SIP_updateConfig", $hash, 0);
+ InternalTimer(gettimeofday()+5, "SIP_updateConfig", $hash);
return undef;
}
sub SIP_Notify($$)
{
- # $hash is my entry, $dev_hash is the entry of the changed device
+ # $hash is my hash, $dev_hash is the hash of the changed device
my ($hash, $dev_hash) = @_;
return undef if ($dev_hash->{NAME} ne AttrVal($hash->{NAME},"T2S_Device",""));
- SIP_wait_for_tts($hash) if (defined($hash->{callnr}) && defined($hash->{ringtime}));
+ my $val = ReadingsVal($dev_hash->{NAME},"lastFilename","");
+ return undef if (!$val);
+
+ if (defined($hash->{audio1}) ||
+ defined($hash->{audio2}) ||
+ defined($hash->{audio3}) ||
+ (defined($hash->{callnr}) && defined($hash->{ringtime})))
+ { SIP_wait_for_t2s($hash);}
+
return undef;
}
@@ -191,11 +198,12 @@ sub SIP_Attr (@)
if ($hash->{".reset"})
{
Log3 $name,5,"$name , SIP_Attr : reset";
- SIP_updateConfig($hash);
+ InternalTimer(gettimeofday()+1,"SIP_updateConfig",$hash);
}
return undef;
}
+
sub SIP_updateConfig($)
{
# this routine is called 5 sec after the last define of a restart
@@ -208,7 +216,7 @@ sub SIP_updateConfig($)
if (!$init_done)
{
RemoveInternalTimer($hash);
- InternalTimer(gettimeofday()+5,"SIP_updateConfig", $hash, 0);
+ InternalTimer(gettimeofday()+5,"SIP_updateConfig", $hash);
return;
}
## kommen wir via reset Kommando ?
@@ -226,10 +234,10 @@ sub SIP_updateConfig($)
}
if(defined($hash->{CPID}))
{
- Log3 $name,4, "$name, CALL Kill PID : ".$hash->{CPID};
+ Log3 $name,4, "$name, CALL Kill PID : ".$hash->{CPID};
BlockingKill($hash->{helper}{CALL_PID});
delete $hash->{helper}{CALL_PID};
- delete $hash->{CPID};
+ delete $hash->{CPID};
Log3 $name,4,"$name, Reset Call done";
}
}
@@ -516,41 +524,23 @@ sub SIP_Set($@)
}
elsif (substr($msg,0,1) eq "!") # Text2Speech Text ?
{
- $msg =~ s/^\!//; # das ! muss weg
- my $t2s_name = AttrVal($name,"T2S_Device",undef);
- return "attr T2S_Device not set !" if !defined($t2s_name);
- my $t2s_hash = $defs{$t2s_name};
- return "T2S_Device $t2s_name not found" if !defined($t2s_hash);
-
- return "attr audio_converter not set" if !AttrVal($name,"audio_converter","");
- return "external sox or ffmpeg programm not found, please install sox or ffmpeg first and set attr audio_converter" if !defined($hash->{AC});
-
- my $t2s_file = ReadingsVal($t2s_name,"lastFilename",undef);
- Log3 $name,3,"$name, Reading lastFilename not found at device $t2s_name, are you using a old version ?" if !defined($t2s_file);
-
- readingsSingleUpdate($t2s_hash,"lastFilename","---",0);
-
shift @a;
shift @a;
- $a[0] = $t2s_name; # ist aber egal wird eh verworfen
+ $a[0] = "t2s_name"; # ist egal wird eh verworfen
$a[1] = "tts"; # Kommando des Set Befehls
- $a[2] = $msg;
+ $a[2] =~ s/^\!//; # das ! muss weg
+
+ my $ret = SIP_create_T2S_File($hash,@a); # na dann lege schon mal los
+ return $ret if defined($ret); # Das ging leider schief
- my $ret = Text2Speech_Set($t2s_hash, @a); # na dann lege schon mal los
- if (defined($ret))
- {
- Log3 $name,3,"$name, T2S error : $ret";
- readingsSingleUpdate($hash,"last_error",$ret,0);
- return $ret; # Das ging leider schief
- }
readingsSingleUpdate($hash,"call_state","waiting T2S",0);
$hash->{callnr} = $nr;
$hash->{ringtime} = $ringtime;
RemoveInternalTimer($hash);
- # geben wir TTS mal ein paar Sekunden
- InternalTimer(gettimeofday()+int(AttrVal($name,"T2S_Timeout",5)), "SIP_wait_for_tts", $hash, 0);
+ # geben wir T2S mal ein paar Sekunden
+ InternalTimer(gettimeofday()+int(AttrVal($name,"T2S_Timeout",5)), "SIP_wait_for_t2s", $hash);
return undef;
}
elsif (-e $msg)
@@ -680,7 +670,8 @@ sub SIP_Undef($$)
$ua->cleanup if (defined($ua));
BlockingKill($hash->{helper}{LISTEN_PID}) if (defined($hash->{helper}{LISTEN_PID}));
- RemoveInternalTimer($hash);
+ #RemoveInternalTimer($hash);
+ #RemoveInternalTimer($name);
return undef;
}
@@ -717,25 +708,33 @@ sub SIP_ListenStart($)
return $name."|ListenRegister: $error" if ($error);
my $msg1 = AttrVal($name, "sip_audiofile_dtmf", "");
- my $msg2 = AttrVal($name, "sip_audiofile_ok", "");
- my $msg3 = AttrVal($name, "sip_audiofile_wfp", "");
+ my $msg2 = AttrVal($name, "sip_audiofile_ok", "");
+ my $msg3 = AttrVal($name, "sip_audiofile_wfp", "");
- $msg1 = SIP_check_file($hash,$msg1) if ($msg1);
- $msg2 = SIP_check_file($hash,$msg2) if ($msg2);
- $msg3 = SIP_check_file($hash,$msg3) if ($msg3);
+ $msg1 = $hash->{audio1} if (defined($hash->{audio1}) && $msg1);
+ $msg1 = SIP_check_file($hash,$msg1) if (!defined($hash->{audio1}) && $msg1);;
+ $msg2 = $hash->{audio2} if (defined($hash->{audio2}) && $msg2);
+ $msg2 = SIP_check_file($hash,$msg2) if (!defined($hash->{audio2}) && $msg2);;
+
+ $msg3 = $hash->{audio3} if (defined($hash->{audio3}) && $msg3);
+ $msg3 = SIP_check_file($hash,$msg3) if (!defined($hash->{audio3}) && $msg3);;
+
+ Log3 $name,4,"$name, using $msg1 for audio_dtmf" if ($msg1);
+ Log3 $name,4,"$name, using $msg2 for audio_ok" if ($msg2);
+ Log3 $name,4,"$name, using $msg3 for audio_wfp" if ($msg3);
$hash->{dtmf} = 0;
$hash->{dtmf_event} = "";
$hash->{old} ="-";
- $send_something = sub {
- return unless $packets-- > 0;
- my $buf = sprintf "%010d",$packets;
- $buf .= "1234567890" x 15;
- return $buf; # 160 bytes for PCMU/8000
- };
-
+ $send_something = sub
+ {
+ return unless $packets-- > 0;
+ my $buf = sprintf "%010d",$packets;
+ $buf .= "1234567890" x 15;
+ return $buf; # 160 bytes for PCMU/8000
+ };
$sub_dtmf = sub
{
@@ -940,7 +939,7 @@ sub SIP_ListenDone($)
delete($hash->{helper}{LISTEN_PID});
delete $hash->{LPID};
- RemoveInternalTimer($hash);
+ RemoveInternalTimer($name);
if ($ret ne "end")
{
@@ -950,7 +949,7 @@ sub SIP_ListenDone($)
readingsEndUpdate($hash, 1 );
Log3 $name, 3 , "$name, listen error -> $ret";
return if(IsDisabled($name));
- InternalTimer(gettimeofday()+AttrVal($name, "sip_watch_listen", 60), "SIP_try_listen", $hash, 0);
+ InternalTimer(gettimeofday()+AttrVal($name, "sip_watch_listen", 60), "SIP_try_listen", $hash);
}
else
{
@@ -967,6 +966,52 @@ sub SIP_try_listen($)
my ($hash) = @_;
my $name = $hash->{NAME};
my $waits = AttrVal($name, "sip_watch_listen", 60);
+ my $audio1 = AttrVal($name, "sip_audiofile_dtmf","-");
+ my $audio2 = AttrVal($name, "sip_audiofile_ok", "-");
+ my $audio3 = AttrVal($name, "sip_audiofile_wfp", "-");
+ my @a = ("tts","tts", "-");;
+
+ if (AttrVal($name,"sip_listen","none") eq "dtmf")
+ {
+ if ((substr($audio1,0,1) eq "!") && !defined($hash->{audio1})) # muss erst T2S gefragt werden ?
+ {
+ $audio1 =~ s/^\!//;
+ $hash->{audio1} = $audio1;
+ $a[2] = $audio1;
+ Log3 $name ,4,"$name, hole $audio1";
+ }
+ elsif ((substr($audio2,0,1) eq "!") && !defined($hash->{audio2})) # muss erst T2S gefragt werden ?
+ {
+ $audio2 =~ s/^\!//;
+ $hash->{audio2} = $audio2;
+ $a[2] = $audio2;
+ Log3 $name ,4,"$name, hole $audio2";
+ }
+ }
+ elsif ((substr($audio3,0,1) eq "!") && !defined($hash->{audio3}) && (AttrVal($name,"sip_listen","none") eq "wfp")) # muss erst T2S gefragt werden ?
+ {
+ $audio3 =~ s/^\!//;
+ $hash->{audio3} = $audio3;
+ $a[2] = $audio3;
+ Log3 $name ,4,"$name, hole $audio3";
+ }
+
+ if ($a[2] ne "-")
+ {
+ my $ret = SIP_create_T2S_File($hash,@a);
+ if ($ret)
+ {
+ delete $hash->{audio1} if defined($hash->{audio1});
+ delete $hash->{audio2} if defined($hash->{audio2});
+ delete $hash->{audio3} if defined($hash->{audio3});
+ return $ret;
+ }
+ #starte die Überwachung von T2S
+ RemoveInternalTimer($hash);
+ InternalTimer(gettimeofday()+int(AttrVal($name,"T2S_Timeout",5)), "SIP_watchdog_t2s", $hash);
+ return undef;
+ }
+
$hash->{helper}{LISTEN_PID} = BlockingCall("SIP_ListenStart",$name, "SIP_ListenDone") unless(exists($hash->{helper}{LISTEN_PID}));
@@ -974,17 +1019,18 @@ sub SIP_try_listen($)
{
$hash->{LPID} = $hash->{helper}{LISTEN_PID}{pid};
Log3 $name, 4 , $name.", Listen new PID : ".$hash->{LPID};
- RemoveInternalTimer($hash);
- InternalTimer(gettimeofday()+$waits, "SIP_watch_listen", $hash, 0); # starte die Überwachung
- #my $state = "listen_for_".AttrVal($name,"sip_listen",undef);
- #readingsSingleUpdate($hash, "state", $state, 1);
+ RemoveInternalTimer($name);
+ InternalTimer(gettimeofday()+$waits, "SIP_watch_listen", $name); # starte die Überwachung
+ delete $hash->{audio1};
+ delete $hash->{audio2};
+ delete $hash->{audio3};
return 0;
}
else
{
Log3 $name, 2 , $name.", Listen Start failed, waiting $waits seconds for next try";
RemoveInternalTimer($hash);
- InternalTimer(gettimeofday()+$waits, "SIP_try_listen", $hash, 0);
+ InternalTimer(gettimeofday()+$waits, "SIP_try_listen", $hash);
return "Listen Start failed";
}
}
@@ -992,10 +1038,10 @@ sub SIP_try_listen($)
sub SIP_watch_listen($)
{
# Lebt denn der Listen Prozess überhaupt noch ?
- my ($hash) = @_;
- my $name = $hash->{NAME};
+ my ($name) = @_;
+ my $hash = $defs{$name};
- RemoveInternalTimer($hash);
+ RemoveInternalTimer($name);
return if (IsDisabled($name));
return if (!defined($hash->{LPID}));
@@ -1012,23 +1058,24 @@ sub SIP_watch_listen($)
}
else { Log3 $name, 5 , $name.", listen prozess ".$hash->{LPID}." found"; }
- InternalTimer(gettimeofday()+60, "SIP_watch_listen", $hash, 0);
+ InternalTimer(gettimeofday()+60, "SIP_watch_listen", $name, 0);
return;
}
-sub SIP_wait_for_tts($)
+sub SIP_wait_for_t2s($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
RemoveInternalTimer($hash);
my $t2s_name = AttrVal($name,"T2S_Device",undef);
- my $file = ReadingsVal($t2s_name,"lastFilename","---");
+ my $file = ReadingsVal($t2s_name,"lastFilename","");
my $msg = "";
+ Log3 $name,4,"$name, wait_for_t2s file : $file";
- if ($file ne "---")
+ if ($file)
{
- Log3 $name,4,"$name, new TTS file $file";
+ Log3 $name,4,"$name, new T2S file $file";
my $out = $file;
$out =~ s/mp3/alaw/;
@@ -1067,24 +1114,43 @@ sub SIP_wait_for_tts($)
else
{
Log3 $name,3,"$name, timeout waiting for T2S";
- readingsSingleUpdate($hash,"call_state","TTS timeout",1);
+ readingsSingleUpdate($hash,"call_state","T2S timeout",1);
}
-
+
+ if ($msg && defined($hash->{audio3}))
+ {
+ $hash->{audio3} = $msg;
+ SIP_try_listen($hash);
+ return undef;
+ }
+
+ if ($msg && defined($hash->{audio2}))
+ {
+ $hash->{audio2} = $msg;
+ SIP_try_listen($hash);
+ return undef;
+ }
+
+ if ($msg && defined($hash->{audio1}))
+ {
+ $hash->{audio1} = $msg;
+ SIP_try_listen($hash);
+ return undef;
+ }
+
# nun aber calling
my @a = ($name,"call",$hash->{callnr}, $hash->{ringtime},$msg) ;
delete($hash->{callnr});
delete($hash->{ringtime});
my $ret = SIP_Set($hash , @a);
- Log3 $name,3,"$name, TTS Call : $ret" if defined($ret);
- # haben wir vllt. den Timer missbraucht ?
- SIP_watch_listen($hash) if (defined($hash->{LPID}));
+ Log3 $name,3,"$name, T2S Call : $ret" if defined($ret);
return undef;
}
sub SIP_telnetPort()
{
-foreach my $d (sort keys %defs)
+ foreach my $d (sort keys %defs)
{
my $h = $defs{$d};
next if(!$h->{TYPE} || $h->{TYPE} ne "telnet" || $h->{SNAME});
@@ -1205,7 +1271,7 @@ sub SIP_readPassword($)
if (substr($file,0,1) eq "!")
{
- Log3 $name,3,"$name, Text2Speech is not supported for listen, ignoring it";
+ Log3 $name,3,"$name, Text : $file found, ignoring it";
return "";
}
if (!-e $file)
@@ -1221,6 +1287,62 @@ sub SIP_readPassword($)
return $file;
}
+ sub SIP_create_T2S_File($@)
+ {
+ my ($hash,@a) = @_;
+ my $name = $hash->{NAME};
+
+ my $t2s_name = AttrVal($name,"T2S_Device",undef);
+ return "attr T2S_Device not set !" if !defined($t2s_name);
+ my $t2s_hash = $defs{$t2s_name};
+ return "T2S_Device $t2s_name not found" if !defined($t2s_hash);
+
+ return "attr audio_converter not set" if !AttrVal($name,"audio_converter","");
+ return "external sox or ffmpeg programm not found, please install sox or ffmpeg first and set attr audio_converter" if !defined($hash->{AC});
+
+ my $t2s_file = ReadingsVal($t2s_name,"lastFilename",undef);
+ Log3 $name,3,"$name, Reading lastFilename not found at device $t2s_name, are you using a old version ?" if !defined($t2s_file);
+
+ readingsSingleUpdate($t2s_hash,"lastFilename","",0);
+
+ my $ret = Text2Speech_Set($t2s_hash, @a); # na dann lege schon mal los
+ if (defined($ret))
+ {
+ Log3 $name,3,"$name, T2S error : $ret";
+ readingsSingleUpdate($hash,"last_error",$ret,0);
+ return $ret; # Das ging leider schief
+ }
+
+ return undef; # alles klar
+ }
+
+ sub SIP_watchdog_T2S($)
+ {
+ my ($hash) = @_;
+ my $name = $hash->{NAME};
+ Log3 $name,3,"$name, Timeout waiting for T2S";
+
+ if (defined($hash->{audio1}))
+ {
+ $hash->{audio1}="!T2S Timeout";
+ SIP_try_listen($hash);
+ return undef;
+ }
+
+ if (defined($hash->{audio2}))
+ {
+ $hash->{audio2}="!T2S Timeout";
+ SIP_try_listen($hash);
+ return undef;
+ }
+
+ if (defined($hash->{audio3}))
+ {
+ $hash->{audio3}="!T2S Timeout";
+ SIP_try_listen($hash);
+ return undef;
+ }
+ }
1;
@@ -1263,9 +1385,9 @@ sub SIP_readPassword($)
Stop any listen process and initialize device.
set <name> call <number> [<ringtime>] [<message>]
set <name> call <number> [<maxtime>] [<message>]
set <name> call <nummer> [<ringtime>] [<nachricht>]
set <name> call <nummer> [<maxtime>] [<nachricht>]