2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-10 09:16:53 +00:00

feature: DbLog: jokers "%" in device/reading definition are now possible

feature: Text2Speech: Definition of mp3files with a custom templatedefinition.


git-svn-id: https://svn.fhem.de/fhem/trunk@4747 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
tobiasfaust 2014-01-26 13:09:51 +00:00
parent 8fdbb1abe3
commit 23ea60a1c6
4 changed files with 465 additions and 232 deletions

View File

@ -1,6 +1,7 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it. # Do not insert empty lines here, update check depends on it.
- SVN - SVN
- feature: DbLog: jokers "%" in device/reading definition are now possible
- feature: SYSMON: new CPU Statistics and Plots - feature: SYSMON: new CPU Statistics and Plots
- feature: changed 10_OWServer.pm and 11_OWDevice.pm to use - feature: changed 10_OWServer.pm and 11_OWDevice.pm to use
NOTIFYDEV (justme1968) NOTIFYDEV (justme1968)

View File

@ -6,7 +6,7 @@
# written by Dr. Boris Neubert 2007-12-30 # written by Dr. Boris Neubert 2007-12-30
# e-mail: omega at online dot de # e-mail: omega at online dot de
# #
# modified by Tobias Faust 2012-06-26 # modified and maintained by Tobias Faust since 2012-06-26
# e-mail: tobias dot faust at online dot de # e-mail: tobias dot faust at online dot de
# #
############################################## ##############################################
@ -17,11 +17,8 @@ use warnings;
use DBI; use DBI;
use Data::Dumper; use Data::Dumper;
sub DbLog($$$);
################################################################ ################################################################
sub sub DbLog_Initialize($)
DbLog_Initialize($)
{ {
my ($hash) = @_; my ($hash) = @_;
@ -36,8 +33,7 @@ DbLog_Initialize($)
} }
############################################################### ###############################################################
sub sub DbLog_Define($@)
DbLog_Define($@)
{ {
my ($hash, $def) = @_; my ($hash, $def) = @_;
my @a = split("[ \t][ \t]*", $def); my @a = split("[ \t][ \t]*", $def);
@ -64,8 +60,7 @@ DbLog_Define($@)
} }
##################################### #####################################
sub sub DbLog_Undef($$)
DbLog_Undef($$)
{ {
my ($hash, $name) = @_; my ($hash, $name) = @_;
my $dbh= $hash->{DBH}; my $dbh= $hash->{DBH};
@ -79,8 +74,7 @@ DbLog_Undef($$)
# DbLog-Instanz aufgerufen # DbLog-Instanz aufgerufen
# #
################################################################ ################################################################
sub sub DbLog_Attr(@)
DbLog_Attr(@)
{ {
my @a = @_; my @a = @_;
my $do = 0; my $do = 0;
@ -101,8 +95,7 @@ DbLog_Attr(@)
# Parsefunktion, abhaengig vom Devicetyp # Parsefunktion, abhaengig vom Devicetyp
# #
################################################################ ################################################################
sub sub DbLog_ParseEvent($$$)
DbLog_ParseEvent($$$)
{ {
my ($device, $type, $event)= @_; my ($device, $type, $event)= @_;
my @result; my @result;
@ -159,6 +152,14 @@ DbLog_ParseEvent($$$)
} }
} }
# FBDECT
elsif (($type eq "FBDECT")) {
if ( $value=~/([\.\d]+)\s([a-z])/i ) {
$value = $1;
$unit = $2;
}
}
# MAX # MAX
elsif(($type eq "MAX")) { elsif(($type eq "MAX")) {
$unit= "°C" if(lc($reading) =~ m/temp/); $unit= "°C" if(lc($reading) =~ m/temp/);
@ -339,6 +340,60 @@ DbLog_ParseEvent($$$)
return @result; return @result;
} }
################################################################
#
# param1: hash
# param2: DbLogType -> Current oder History oder Current/History
# param4: Timestamp
# param5: Device
# param6: Type
# param7: Event
# param8: Reading
# param9: Value
# param10: Unit
#
################################################################
sub DbLog_Push(@) {
my ($hash, $DbLogType, $timestamp, $device, $type, $event, $reading, $value, $unit) = @_;
my $dbh= $hash->{DBH};
$dbh->{RaiseError} = 1;
$dbh->begin_work();
my $sth_ih = $dbh->prepare_cached("INSERT INTO history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)") if (lc($DbLogType) =~ m(history) );
my $sth_ic = $dbh->prepare_cached("INSERT INTO current (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)") if (lc($DbLogType) =~ m(current) );
my $sth_uc = $dbh->prepare_cached("UPDATE current SET TIMESTAMP=?, TYPE=?, EVENT=?, VALUE=?, UNIT=? WHERE (DEVICE=?) AND (READING=?)") if (lc($DbLogType) =~ m(current) );
# insert into history
if (lc($DbLogType) =~ m(history) ) {
my $rv_ih = $sth_ih->execute(($timestamp, $device, $type, $event, $reading, $value, $unit));
}
# update or insert current
if (lc($DbLogType) =~ m(current) ) {
my $rv_uc = $sth_uc->execute(($timestamp, $type, $event, $value, $unit, $device, $reading));
if ($rv_uc == 0) {
my $rv_ic = $sth_ic->execute(($timestamp, $device, $type, $event, $reading, $value, $unit));
}
}
$dbh->commit();
if ($@) {
Log3 $hash->{NAME}, 2, "DbLog: Failed to insert new readings into database: $@";
$dbh->{RaiseError} = 0;
$dbh->rollback();
# reconnect
$dbh->disconnect();
DbLog_Connect($hash);
}
else {
$dbh->{RaiseError} = 0;
}
return $dbh->{RaiseError};
}
################################################################ ################################################################
# #
@ -346,13 +401,11 @@ DbLog_ParseEvent($$$)
# aufgerufen # aufgerufen
# #
################################################################ ################################################################
sub sub DbLog_Log($$) {
DbLog_Log($$)
{
# Log is my entry, Dev is the entry of the changed device # Log is my entry, Dev is the entry of the changed device
my ($log, $dev) = @_; my ($hash, $dev) = @_;
return undef if($log->{STATE} eq "disabled"); return undef if($hash->{STATE} eq "disabled");
# name and type required for parsing # name and type required for parsing
my $n= $dev->{NAME}; my $n= $dev->{NAME};
@ -362,25 +415,15 @@ DbLog_Log($$)
#my ($sec,$min,$hr,$day,$mon,$yr,$wday,$yday,$isdst)= localtime(time); #my ($sec,$min,$hr,$day,$mon,$yr,$wday,$yday,$isdst)= localtime(time);
#my $ts= sprintf("%04d-%02d-%02d %02d:%02d:%02d", $yr+1900,$mon+1,$day,$hr,$min,$sec); #my $ts= sprintf("%04d-%02d-%02d %02d:%02d:%02d", $yr+1900,$mon+1,$day,$hr,$min,$sec);
my $re = $log->{REGEXP}; my $re = $hash->{REGEXP};
my $max = int(@{$dev->{CHANGED}}); my $max = int(@{$dev->{CHANGED}});
my $ts_0 = TimeNow(); my $ts_0 = TimeNow();
my $now = gettimeofday(); # get timestamp in seconds since epoch my $now = gettimeofday(); # get timestamp in seconds since epoch
my $DbLogExclude = AttrVal($dev->{NAME}, "DbLogExclude", undef); my $DbLogExclude = AttrVal($dev->{NAME}, "DbLogExclude", undef);
my $DbLogType = AttrVal($hash->{NAME}, "DbLogType", "Current/History");
my $dbh= $log->{DBH};
$dbh->{RaiseError} = 1;
my $DbLogType = AttrVal($log->{NAME}, "DbLogType", "Current/History");
#one Transaction #one Transaction
eval { #eval {
$dbh->begin_work();
my $sth_ih = $dbh->prepare_cached("INSERT INTO history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)") if ($DbLogType =~ m(History) );
my $sth_uc = $dbh->prepare_cached("UPDATE current SET TIMESTAMP=?, TYPE=?, EVENT=?, VALUE=?, UNIT=? WHERE (DEVICE=?) AND (READING=?)") if ($DbLogType =~ m(Current) );
my $sth_ic = $dbh->prepare_cached("INSERT INTO current (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)") if ($DbLogType =~ m(Current) );
for (my $i = 0; $i < $max; $i++) { for (my $i = 0; $i < $max; $i++) {
my $s = $dev->{CHANGED}[$i]; my $s = $dev->{CHANGED}[$i];
$s = "" if(!defined($s)); $s = "" if(!defined($s));
@ -409,8 +452,8 @@ DbLog_Log($$)
$DoIt = 0 if(!$v2[1] && $reading =~ m/^$v2[0]$/); #Reading matcht auf Regexp, kein MinIntervall angegeben $DoIt = 0 if(!$v2[1] && $reading =~ m/^$v2[0]$/); #Reading matcht auf Regexp, kein MinIntervall angegeben
if(($v2[1] && $reading =~ m/^$v2[0]$/) && ($v2[1] =~ m/^(\d+)$/)) { if(($v2[1] && $reading =~ m/^$v2[0]$/) && ($v2[1] =~ m/^(\d+)$/)) {
#Regexp matcht und MinIntervall ist angegeben #Regexp matcht und MinIntervall ist angegeben
my $lt = $defs{$dev->{NAME}}{Helper}{DBLOG}{$reading}{$log->{NAME}}{TIME}; my $lt = $defs{$dev->{NAME}}{Helper}{DBLOG}{$reading}{$hash->{NAME}}{TIME};
my $lv = $defs{$dev->{NAME}}{Helper}{DBLOG}{$reading}{$log->{NAME}}{VALUE}; my $lv = $defs{$dev->{NAME}}{Helper}{DBLOG}{$reading}{$hash->{NAME}}{VALUE};
$lt = 0 if(!$lt); $lt = 0 if(!$lt);
$lv = "" if(!$lv); $lv = "" if(!$lv);
@ -423,39 +466,14 @@ DbLog_Log($$)
} }
next if($DoIt == 0); next if($DoIt == 0);
$defs{$dev->{NAME}}{Helper}{DBLOG}{$reading}{$log->{NAME}}{TIME} = $now; $defs{$dev->{NAME}}{Helper}{DBLOG}{$reading}{$hash->{NAME}}{TIME} = $now;
$defs{$dev->{NAME}}{Helper}{DBLOG}{$reading}{$log->{NAME}}{VALUE} = $value; $defs{$dev->{NAME}}{Helper}{DBLOG}{$reading}{$hash->{NAME}}{VALUE} = $value;
my @is= ($ts, $n, $t, $s, $reading, $value, $unit); DbLog_Push($hash, $DbLogType, $ts, $n, $t, $s, $reading, $value, $unit)
# insert into history
if ($DbLogType =~ m(History) ) {
my $rv_ih = $sth_ih->execute(@is);
}
if ($DbLogType =~ m(Current) ) {
# update or insert current
my $rv_uc = $sth_uc->execute(($ts, $t, $s, $value, $unit, $n, $reading));
if ($rv_uc == 0) {
my $rv_ic = $sth_ic->execute(@is);
}
}
} }
} }
$dbh->commit(); #};
};
if ($@) {
Log3 $dev->{NAME}, 2, "DbLog: Failed to insert new readings into database: $@";
$dbh->{RaiseError} = 0;
$dbh->rollback();
# reconnect
$dbh->disconnect();
DbLog_Connect($log);
}
else {
$dbh->{RaiseError} = 0;
}
return ""; return "";
} }
@ -467,8 +485,7 @@ DbLog_Log($$)
# uebergebenes SQL-Format: YYYY-MM-DD HH24:MI:SS # uebergebenes SQL-Format: YYYY-MM-DD HH24:MI:SS
# #
################################################################ ################################################################
sub sub DbLog_explode_datetime($%) {
DbLog_explode_datetime($%) {
my ($t, %def) = @_; my ($t, %def) = @_;
my %retv; my %retv;
@ -489,20 +506,19 @@ DbLog_explode_datetime($%) {
return %retv return %retv
} }
sub sub DbLog_implode_datetime($$$$$$) {
DbLog_implode_datetime($$$$$$) {
my ($year, $month, $day, $hour, $minute, $second) = @_; my ($year, $month, $day, $hour, $minute, $second) = @_;
my $retv = $year."-".$month."-".$day." ".$hour.":".$minute.":".$second; my $retv = $year."-".$month."-".$day." ".$hour.":".$minute.":".$second;
return $retv; return $retv;
} }
################################################################ ################################################################
# #
# Verbindung zur DB aufbauen # Verbindung zur DB aufbauen
# #
################################################################ ################################################################
sub sub DbLog_Connect($)
DbLog_Connect($)
{ {
my ($hash)= @_; my ($hash)= @_;
@ -575,6 +591,9 @@ DbLog_Connect($)
# #
# Prozeduren zum Ausfuehren des SQLs # Prozeduren zum Ausfuehren des SQLs
# #
# param1: hash
# param2: pointer : DBFilehandle
# param3: string : SQL
################################################################ ################################################################
sub sub
DbLog_ExecSQL1($$$) DbLog_ExecSQL1($$$)
@ -618,6 +637,8 @@ DbLog_ExecSQL($$)
# #
# GET Funktion # GET Funktion
# wird zb. zur Generierung der Plots implizit aufgerufen # wird zb. zur Generierung der Plots implizit aufgerufen
# infile : [-|current|history]
# outfile: [-|ALL|INT|WEBCHART]
# #
################################################################ ################################################################
sub sub
@ -632,16 +653,22 @@ DbLog_Get($@)
" <out> is a prefix, - means stdout\n" " <out> is a prefix, - means stdout\n"
if(int(@a) < 5); if(int(@a) < 5);
shift @a; shift @a;
my $inf = shift @a; my $inf = lc(shift @a);
my $outf = shift @a; my $outf = lc(shift @a);
my $from = shift @a; my $from = shift @a;
my $to = shift @a; # Now @a contains the list of column_specs my $to = shift @a; # Now @a contains the list of column_specs
my ($internal, @fld); my ($internal, @fld);
if($outf eq "INT") { if($inf eq "-") {
$inf = "history";
}
if($outf eq "int") {
$outf = "-"; $outf = "-";
$internal = 1; $internal = 1;
} elsif (uc($outf) eq "WEBCHART") { } elsif($outf eq "array"){
} elsif(uc($outf) eq "webchart") {
# redirect the get request to the chartQuery function # redirect the get request to the chartQuery function
return chartQuery($hash, @_); return chartQuery($hash, @_);
} }
@ -659,7 +686,8 @@ DbLog_Get($@)
$to = $to_datetime{datetime}; $to = $to_datetime{datetime};
my ($retval,$sql_timestamp,$sql_value, $type, $event, $unit) = ""; my ($retval,$sql_timestamp, $sql_device, $sql_reading, $sql_value, $type, $event, $unit) = "";
my @ReturnArray;
my $writeout = 0; my $writeout = 0;
my (@min, @max, @sum, @cnt, @lastv, @lastd); my (@min, @max, @sum, @cnt, @lastv, @lastd);
my (%tstamp, %lasttstamp, $out_tstamp, $out_value, $minval, $maxval); #fuer delta-h/d Berechnung my (%tstamp, %lasttstamp, $out_tstamp, $out_value, $minval, $maxval); #fuer delta-h/d Berechnung
@ -672,6 +700,8 @@ DbLog_Get($@)
$readings[$i][2] = $fld[2]; # Default $readings[$i][2] = $fld[2]; # Default
$readings[$i][3] = $fld[3]; # function $readings[$i][3] = $fld[3]; # function
$readings[$i][4] = $fld[4]; # regexp $readings[$i][4] = $fld[4]; # regexp
$readings[$i][1] = "%" if(length($readings[$i][1])==0); #falls Reading nicht gefüllt setze Joker
} }
#create new connection for plotfork #create new connection for plotfork
@ -687,7 +717,7 @@ DbLog_Get($@)
$sqlspec{get_timestamp} = "TO_CHAR(TIMESTAMP, 'YYYY-MM-DD HH24:MI:SS')"; $sqlspec{get_timestamp} = "TO_CHAR(TIMESTAMP, 'YYYY-MM-DD HH24:MI:SS')";
$sqlspec{from_timestamp} = "TO_TIMESTAMP('$from', 'YYYY-MM-DD HH24:MI:SS')"; $sqlspec{from_timestamp} = "TO_TIMESTAMP('$from', 'YYYY-MM-DD HH24:MI:SS')";
$sqlspec{to_timestamp} = "TO_TIMESTAMP('$to', 'YYYY-MM-DD HH24:MI:SS')"; $sqlspec{to_timestamp} = "TO_TIMESTAMP('$to', 'YYYY-MM-DD HH24:MI:SS')";
$sqlspec{reading_clause} = "(DEVICE || '|' || READING)"; #$sqlspec{reading_clause} = "(DEVICE || '|' || READING)";
} elsif ($hash->{DBMODEL} eq "ORACLE") { } elsif ($hash->{DBMODEL} eq "ORACLE") {
$sqlspec{get_timestamp} = "TO_CHAR(TIMESTAMP, 'YYYY-MM-DD HH24:MI:SS')"; $sqlspec{get_timestamp} = "TO_CHAR(TIMESTAMP, 'YYYY-MM-DD HH24:MI:SS')";
$sqlspec{from_timestamp} = "TO_TIMESTAMP('$from', 'YYYY-MM-DD HH24:MI:SS')"; $sqlspec{from_timestamp} = "TO_TIMESTAMP('$from', 'YYYY-MM-DD HH24:MI:SS')";
@ -706,7 +736,7 @@ DbLog_Get($@)
$sqlspec{to_timestamp} = "'$to'"; $sqlspec{to_timestamp} = "'$to'";
} }
if(uc($outf) eq "ALL") { if($outf =~ m/(all|array)/) {
$sqlspec{all} = ",TYPE,EVENT,UNIT"; $sqlspec{all} = ",TYPE,EVENT,UNIT";
} else { } else {
$sqlspec{all} = ""; $sqlspec{all} = "";
@ -724,33 +754,46 @@ DbLog_Get($@)
$minval = 999999; $minval = 999999;
$maxval = -999999; $maxval = -999999;
my $stm= "SELECT my $stm;
$sqlspec{get_timestamp}, $stm = "SELECT
VALUE $sqlspec{get_timestamp},
$sqlspec{all} DEVICE,
FROM history READING,
WHERE VALUE
DEVICE = '".$readings[$i]->[0]."' $sqlspec{all} ";
AND READING = '".$readings[$i]->[1]."'
AND TIMESTAMP > $sqlspec{from_timestamp}
AND TIMESTAMP < $sqlspec{to_timestamp}
ORDER BY TIMESTAMP";
Log3 $hash->{NAME}, 5, "Executing $stm"; $stm .= "FROM current " if($inf eq "current");
$stm .= "FROM history " if($inf eq "history");
$stm .= "WHERE 1=1 ";
$stm .= "AND DEVICE = '".$readings[$i]->[0]."' " if ($readings[$i]->[0] !~ m(\%));
$stm .= "AND DEVICE LIKE '".$readings[$i]->[0]."' " if(($readings[$i]->[0] !~ m(^\%$)) && ($readings[$i]->[0] =~ m(\%)));
$stm .= "AND READING = '".$readings[$i]->[1]."' " if ($readings[$i]->[1] !~ m(\%));
$stm .= "AND READING LIKE '".$readings[$i]->[1]."' " if(($readings[$i]->[1] !~ m(^%$)) && ($readings[$i]->[1] =~ m(\%)));
$stm .= "AND TIMESTAMP > $sqlspec{from_timestamp}
AND TIMESTAMP < $sqlspec{to_timestamp}
ORDER BY TIMESTAMP";
Log3 $hash->{NAME}, 4, "Processing Statement: $stm";
my $sth= $dbh->prepare($stm) || my $sth= $dbh->prepare($stm) ||
return "Cannot prepare statement $stm: $DBI::errstr"; return "Cannot prepare statement $stm: $DBI::errstr";
my $rc= $sth->execute() || my $rc= $sth->execute() ||
return "Cannot execute statement $stm: $DBI::errstr"; return "Cannot execute statement $stm: $DBI::errstr";
if(uc($outf) eq "ALL") { if($outf =~ m/(all|array)/) {
$sth->bind_columns(undef, \$sql_timestamp, \$sql_value, \$type, \$event, \$unit); $sth->bind_columns(undef, \$sql_timestamp, \$sql_device, \$sql_reading, \$sql_value, \$type, \$event, \$unit);
$retval .= "Timestamp: Device, Type, Event, Reading, Value, Unit\n";
$retval .= "=====================================================\n";
} }
else { else {
$sth->bind_columns(undef, \$sql_timestamp, \$sql_value); $sth->bind_columns(undef, \$sql_timestamp, \$sql_value);
}
if ($outf =~ m/(all)/) {
$retval .= "Timestamp: Device, Type, Event, Reading, Value, Unit\n";
$retval .= "=====================================================\n";
} }
while($sth->fetch()) { while($sth->fetch()) {
@ -840,8 +883,13 @@ DbLog_Get($@)
###################### Ausgabe ########################### ###################### Ausgabe ###########################
if($writeout) { if($writeout) {
if(uc($outf) eq "ALL") { if ($outf =~ m/(all)/) {
$retval .= sprintf("%s: %s, %s, %s, %s, %s, %s\n", $out_tstamp, $readings[$i]->[0], $type, $event, $readings[$i]->[1], $out_value, $unit); # Timestamp: Device, Type, Event, Reading, Value, Unit
$retval .= sprintf("%s: %s, %s, %s, %s, %s, %s\n", $out_tstamp, $sql_device, $type, $event, $sql_reading, $out_value, $unit);
} elsif ($outf =~ m/(array)/) {
push(@ReturnArray, {"tstamp" => $out_tstamp, "device" => $sql_device, "type" => $type, "event" => $event, "reading" => $sql_reading, "value" => $out_value, "unit" => $unit});
} else { } else {
$out_tstamp =~ s/\ /_/g; #needed by generating plots $out_tstamp =~ s/\ /_/g; #needed by generating plots
$retval .= "$out_tstamp $out_value\n"; $retval .= "$out_tstamp $out_value\n";
@ -873,12 +921,17 @@ DbLog_Get($@)
$out_value = sprintf("%g", $maxval - $minval); $out_value = sprintf("%g", $maxval - $minval);
$out_tstamp = DbLog_implode_datetime($lasttstamp{year}, $lasttstamp{month}, $lasttstamp{day}, $lasttstamp{hour}, "30", "00") if($readings[$i]->[3] eq "delta-h"); $out_tstamp = DbLog_implode_datetime($lasttstamp{year}, $lasttstamp{month}, $lasttstamp{day}, $lasttstamp{hour}, "30", "00") if($readings[$i]->[3] eq "delta-h");
$out_tstamp = DbLog_implode_datetime($lasttstamp{year}, $lasttstamp{month}, $lasttstamp{day}, "00", "00", "00") if($readings[$i]->[3] eq "delta-d"); $out_tstamp = DbLog_implode_datetime($lasttstamp{year}, $lasttstamp{month}, $lasttstamp{day}, "00", "00", "00") if($readings[$i]->[3] eq "delta-d");
if(uc($outf) eq "ALL") {
$retval .= sprintf("%s: %s %s %s %s %s %s\n", $out_tstamp, $readings[$i]->[0], $type, $event, $readings[$i]->[1], $out_value, $unit); if($outf =~ m/(all)/) {
} else { $retval .= sprintf("%s: %s %s %s %s %s %s\n", $out_tstamp, $sql_device, $type, $event, $sql_reading, $out_value, $unit);
$out_tstamp =~ s/\ /_/g; #needed by generating plots
$retval .= "$out_tstamp $out_value\n"; } elsif ($outf =~ m/(array)/) {
} push(@ReturnArray, {"tstamp" => $out_tstamp, "device" => $sql_device, "type" => $type, "event" => $event, "reading" => $sql_reading, "value" => $out_value, "unit" => $unit});
} else {
$out_tstamp =~ s/\ /_/g; #needed by generating plots
$retval .= "$out_tstamp $out_value\n";
}
} }
# DatenTrenner setzen # DatenTrenner setzen
$retval .= "#$readings[$i]->[0]"; $retval .= "#$readings[$i]->[0]";
@ -911,8 +964,13 @@ DbLog_Get($@)
if($internal) { if($internal) {
$internal_data = \$retval; $internal_data = \$retval;
return undef; return undef;
} elsif($outf =~ m/(array)/) {
return @ReturnArray;
} else {
return $retval;
} }
return $retval;
} }
################################################################ ################################################################
@ -1240,18 +1298,17 @@ sub chartQuery($@) {
contains the last event for any given reading and device. contains the last event for any given reading and device.
The columns have the following meaning: The columns have the following meaning:
<ol> <ol>
<li>TIMESTAMP: timestamp of event, e.g. <code>2007-12-30 21:45:22</code></li>
<li>DEVICE: device name, e.g. <code>Wetterstation</code></li>
<li>TYPE: device type, e.g. <code>KS300</code></li>
<li>EVENT: event specification as full string,
e.g. <code>humidity: 71 (%)</code></li>
<li>READING: name of reading extracted from event,
e.g. <code>humidity</code></li>
<li>TIMESTAMP: timestamp of event, e.g. <code>2007-12-30 21:45:22</code></li> <li>VALUE: actual reading extracted from event,
<li>DEVICE: device name, e.g. <code>Wetterstation</code></li> e.g. <code>71</code></li>
<li>TYPE: device type, e.g. <code>KS300</code></li> <li>UNIT: unit extracted from event, e.g. <code>%</code></li>
<li>EVENT: event specification as full string,
e.g. <code>humidity: 71 (%)</code></li>
<li>READING: name of reading extracted from event,
e.g. <code>humidity</code></li>
<li>VALUE: actual reading extracted from event,
e.g. <code>71</code></li>
<li>UNIT: unit extracted from event, e.g. <code>%</code></li>
</ol> </ol>
The content of VALUE is optimized for automated post-processing, e.g. The content of VALUE is optimized for automated post-processing, e.g.
<code>yes</code> is translated to <code>1</code> <code>yes</code> is translated to <code>1</code>
@ -1284,11 +1341,25 @@ sub chartQuery($@) {
<ul> <ul>
<li>&lt;in&gt;<br> <li>&lt;in&gt;<br>
A dummy parameter for FileLog compatibility. Always set to <code>-</code></li> A dummy parameter for FileLog compatibility. Sessing by defaultto <code>-</code><br>
<ul>
<li>current: reading actual readings from table "current"</li>
<li>history: reading history readings from table "history"</li>
<li>-: identical to "history"</li>
</ul>
</li>
<li>&lt;out&gt;<br> <li>&lt;out&gt;<br>
A dummy parameter for FileLog compatibility. Set it to <code>-</code> A dummy parameter for FileLog compatibility. Setting by default to <code>-</code>
to check the output for plot-computing.<br>Set it to the special keyword to check the output for plot-computing.<br>
<code>all</code> to get all columns from Database.</li> Set it to the special keyword
<code>all</code> to get all columns from Database.
<ul>
<li>ALL: get all colums from table, including a header</li>
<li>Array: get the columns as array of hashes</li>
<li>INT: internally used by generating plots</li>
<li>-: default</li>
</ul>
</li>
<li>&lt;from&gt; / &lt;to&gt;<br> <li>&lt;from&gt; / &lt;to&gt;<br>
Used to select the data. Please use the following timeformat or Used to select the data. Please use the following timeformat or
an initial substring of it:<br> an initial substring of it:<br>
@ -1299,9 +1370,9 @@ sub chartQuery($@) {
Syntax: &lt;device&gt;:&lt;reading&gt;:&lt;default&gt;:&lt;fn&gt;:&lt;regexp&gt;<br> Syntax: &lt;device&gt;:&lt;reading&gt;:&lt;default&gt;:&lt;fn&gt;:&lt;regexp&gt;<br>
<ul> <ul>
<li>&lt;device&gt;<br> <li>&lt;device&gt;<br>
The name of the device. Case sensitive</li> The name of the device. Case sensitive. Using a the joker "%" is supported.</li>
<li>&lt;reading&gt;<br> <li>&lt;reading&gt;<br>
The reading of the given device to select. Case sensitive. The reading of the given device to select. Case sensitive. Using a the joker "%" is supported.
</li> </li>
<li>&lt;default&gt;<br> <li>&lt;default&gt;<br>
no implemented yet no implemented yet
@ -1344,6 +1415,9 @@ sub chartQuery($@) {
Examples: Examples:
<ul> <ul>
<li><code>get myDbLog - - 2012-11-10 2012-11-20 KS300:temperature</code></li> <li><code>get myDbLog - - 2012-11-10 2012-11-20 KS300:temperature</code></li>
<li><code>get myDbLog current ALL - - %:temperature</code></li><br>
you will get all actual readings "temperature" from all logged devices.
Be carful by using "history" as inputfile because a long execution time will be expected!
<li><code>get myDbLog - - 2012-11-10_10 2012-11-10_20 KS300:temperature::int1</code><br> <li><code>get myDbLog - - 2012-11-10_10 2012-11-10_20 KS300:temperature::int1</code><br>
like from 10am until 08pm at 10.11.2012</li> like from 10am until 08pm at 10.11.2012</li>
<li><code>get myDbLog - all 2012-11-10 2012-11-20 KS300:temperature</code></li> <li><code>get myDbLog - all 2012-11-10 2012-11-20 KS300:temperature</code></li>
@ -1472,13 +1546,13 @@ sub chartQuery($@) {
definiert in <code>&lt;configfilename&gt;</code>. (Vergleiche definiert in <code>&lt;configfilename&gt;</code>. (Vergleiche
Beipspielkonfigurationsdatei in <code>contrib/dblog/db.conf</code>).<br> Beipspielkonfigurationsdatei in <code>contrib/dblog/db.conf</code>).<br>
Die Konfiguration ist in einer sparaten Datei abgelegt um das Datenbankpasswort Die Konfiguration ist in einer sparaten Datei abgelegt um das Datenbankpasswort
nicht in Klartext in der FHEM-Haupt-Konfigurationsdatei speichern zu müssen. nicht in Klartext in der FHEM-Haupt-Konfigurationsdatei speichern zu m&ouml;ssen.
Ansonsten wäre es mittels des <a href="../docs/commandref.html#list">list</a> Ansonsten w&auml;re es mittels des <a href="../docs/commandref.html#list">list</a>
Befehls einfach auslesbar. Befehls einfach auslesbar.
<br><br> <br><br>
Die Perl-Module <code>DBI</code> and <code>DBD::&lt;dbtype&gt;</code> Die Perl-Module <code>DBI</code> and <code>DBD::&lt;dbtype&gt;</code>
müssen installiert werden (use <code>cpan -i &lt;module&gt;</code> m&ouml;ssen installiert werden (use <code>cpan -i &lt;module&gt;</code>
falls die eigene Distribution diese nicht schon mitbringt). falls die eigene Distribution diese nicht schon mitbringt).
<br><br> <br><br>
@ -1487,28 +1561,28 @@ sub chartQuery($@) {
Ein Beispielcode zum Erstellen einer MySQL/PostGreSQL Datenbak ist in Ein Beispielcode zum Erstellen einer MySQL/PostGreSQL Datenbak ist in
<code>contrib/dblog/&lt;DBType&gt;_create.sql</code> zu finden. <code>contrib/dblog/&lt;DBType&gt;_create.sql</code> zu finden.
Die Datenbank beinhaltet 2 Tabellen: <code>current</code> und Die Datenbank beinhaltet 2 Tabellen: <code>current</code> und
<code>history</code>. Die Tabelle <code>current</code> enthält den letzten Stand <code>history</code>. Die Tabelle <code>current</code> enth&auml;lt den letzten Stand
pro Device und Reading. In der Tabelle <code>history</code> sind alle pro Device und Reading. In der Tabelle <code>history</code> sind alle
Events historisch gespeichert. Events historisch gespeichert.
Die Tabellenspalten haben folgende Bedeutung: Die Tabellenspalten haben folgende Bedeutung:
<ol> <ol>
<li>TIMESTAMP: Zeitpunkt des Events, z.B. <code>2007-12-30 21:45:22</code></li> <li>TIMESTAMP: Zeitpunkt des Events, z.B. <code>2007-12-30 21:45:22</code></li>
<li>DEVICE: name des Devices, z.B. <code>Wetterstation</code></li> <li>DEVICE: name des Devices, z.B. <code>Wetterstation</code></li>
<li>TYPE: Type des Devices, z.B. <code>KS300</code></li> <li>TYPE: Type des Devices, z.B. <code>KS300</code></li>
<li>EVENT: das auftretende Event als volle Zeichenkette <li>EVENT: das auftretende Event als volle Zeichenkette
z.B. <code>humidity: 71 (%)</code></li> z.B. <code>humidity: 71 (%)</code></li>
<li>READING: Name des Readings, ermittelt aus dem Event, <li>READING: Name des Readings, ermittelt aus dem Event,
z.B. <code>humidity</code></li> z.B. <code>humidity</code></li>
<li>VALUE: aktueller Wert des Readings, ermittelt aus dem Event, <li>VALUE: aktueller Wert des Readings, ermittelt aus dem Event,
z.B. <code>71</code></li> z.B. <code>71</code></li>
<li>UNIT: Einheit, ermittelt aus dem Event, z.B. <code>%</code></li> <li>UNIT: Einheit, ermittelt aus dem Event, z.B. <code>%</code></li>
</ol> </ol>
Der Wert des Rreadings ist optimiert für eine automatisierte Nachverarbeitung Der Wert des Rreadings ist optimiert f&ouml;r eine automatisierte Nachverarbeitung
z.B. <code>yes</code> ist transformiert nach <code>1</code> z.B. <code>yes</code> ist transformiert nach <code>1</code>
<br><br> <br><br>
Die gespeicherten Werte können mittels GET Funktion angezeigt werden: Die gespeicherten Werte k&ouml;nnen mittels GET Funktion angezeigt werden:
<ul> <ul>
<code>get myDbLog - - 2012-11-10 2012-11-10 KS300:temperature</code> <code>get myDbLog - - 2012-11-10 2012-11-10 KS300:temperature</code>
</ul> </ul>
@ -1516,7 +1590,6 @@ sub chartQuery($@) {
<b>Beispiel:</b> <b>Beispiel:</b>
<ul> <ul>
<code>Speichert alles in der Datenbank</code><br> <code>Speichert alles in der Datenbank</code><br>
<code>define myDbLog DbLog /etc/fhem/db.conf .*:.*</code> <code>define myDbLog DbLog /etc/fhem/db.conf .*:.*</code>
</ul> </ul>
</ul> </ul>
@ -1531,37 +1604,49 @@ sub chartQuery($@) {
<code>get &lt;name&gt; &lt;infile&gt; &lt;outfile&gt; &lt;from&gt; <code>get &lt;name&gt; &lt;infile&gt; &lt;outfile&gt; &lt;from&gt;
&lt;to&gt; &lt;column_spec&gt; </code> &lt;to&gt; &lt;column_spec&gt; </code>
<br><br> <br><br>
Ließt Daten aus der Datenbank. Wird durch die Frontends benutzt um Plots Liesst Daten aus der Datenbank. Wird durch die Frontends benutzt um Plots
zu generieren ohne selbst auf die Datenank zugreifen zu müssen. zu generieren ohne selbst auf die Datenank zugreifen zu m&ouml;ssen.
<br> <br>
<ul> <ul>
<li>&lt;in&gt;<br> <li>&lt;in&gt;<br>
Ein Dummy Parameter um eine Kompatibilität zum Filelog herzustellen. Ein Parameter um eine Kompatibilit&auml;t zum Filelog herzustellen.
Dieser Parameter ist immer auf <code>-</code> zu setzen. Dieser Parameter ist per default immer auf <code>-</code> zu setzen.<br>
Folgende Auspr&auml;gungen sind zugelassen:<br>
<ul>
<li>current: die aktuellen Werte aus der Tabelle "current" werden gelesen.</li>
<li>history: die historischen Werte aus der Tabelle "history" werden gelesen.</li>
<li>-: identisch wie "history"</li>
</ul>
</li> </li>
<li>&lt;out&gt;<br> <li>&lt;out&gt;<br>
Ein Dummy Parameter um eine Kompatibilität zum Filelog herzustellen. Ein Parameter um eine Kompatibilit&auml;t zum Filelog herzustellen.
Dieser Parameter ist immer auf <code>-</code> zu setzen um die Dieser Parameter ist per default immer auf <code>-</code> zu setzen um die
Ermittlung der Daten aus der Datenbank für die Plotgenerierung zu prüfen.<br> Ermittlung der Daten aus der Datenbank f&ouml;r die Plotgenerierung zu pr&ouml;fen.<br>
Durchd ie Angabe des Schlüsselworts <code>all</code> werden alle Folgende Auspr&auml;gungen sind zugelassen:<br>
Spalten der Datenbank ausgegeben. <ul>
</li> <li>ALL: Es werden alle Spalten der Datenbank ausgegeben. Inclusive einer &Uuml;berschrift.</li>
<li>Array: Es werden alle Spalten der Datenbank als Hash ausgegeben. Alle Datens&auml;tze als Array zusammengefasst.</li>
<li>INT: intern zur Plotgenerierung verwendet</li>
<li>-: default</li>
</ul>
</li>
<li>&lt;from&gt; / &lt;to&gt;<br> <li>&lt;from&gt; / &lt;to&gt;<br>
Wird benutzt um den Zeitraum der Daten einzugrenzen. Es ist das folgende Wird benutzt um den Zeitraum der Daten einzugrenzen. Es ist das folgende
Zeitformat oder ein Teilstring davon zu benutzen:<br> Zeitformat oder ein Teilstring davon zu benutzen:<br>
<ul><code>YYYY-MM-DD_HH24:MI:SS</code></ul></li> <ul><code>YYYY-MM-DD_HH24:MI:SS</code></ul></li>
<li>&lt;column_spec&gt;<br> <li>&lt;column_spec&gt;<br>
Für jede column_spec Gruppe wird ein Datenset zurückgegeben welches F&ouml;r jede column_spec Gruppe wird ein Datenset zur&ouml;ckgegeben welches
durch einen Kommentar getrennt wird. Dieser Kommentar repräsentiert durch einen Kommentar getrennt wird. Dieser Kommentar repr&auml;sentiert
die column_spec.<br> die column_spec.<br>
Syntax: &lt;device&gt;:&lt;reading&gt;:&lt;default&gt;:&lt;fn&gt;:&lt;regexp&gt;<br> Syntax: &lt;device&gt;:&lt;reading&gt;:&lt;default&gt;:&lt;fn&gt;:&lt;regexp&gt;<br>
<ul> <ul>
<li>&lt;device&gt;<br> <li>&lt;device&gt;<br>
Der Name des Devices. Achtung: Groß/Kleinschreibung beachten!</li> Der Name des Devices. Achtung: Gross/Kleinschreibung beachten!<br>
Es kann ein % als Jokerzeichen angegeben werden.</li>
<li>&lt;reading&gt;<br> <li>&lt;reading&gt;<br>
Das REading des angegebenen Devices zur Datenselektion. Das Reading des angegebenen Devices zur Datenselektion.<br>
Achtung: Groß/Kleinschreibung beachten! Es kann ein % als Jokerzeichen angegeben werden.<br>
Achtung: Gross/Kleinschreibung beachten!
</li> </li>
<li>&lt;default&gt;<br> <li>&lt;default&gt;<br>
Zur Zeit noch nicht implementiert. Zur Zeit noch nicht implementiert.
@ -1571,17 +1656,17 @@ sub chartQuery($@) {
<ul> <ul>
<li>int<br> <li>int<br>
Ermittelt den Zahlenwert ab dem Anfang der Zeichenkette aus der Ermittelt den Zahlenwert ab dem Anfang der Zeichenkette aus der
Spalte "VALUE". Benutzt z.B. für Ausprägungen wie 10%. Spalte "VALUE". Benutzt z.B. f&ouml;r Auspr&auml;gungen wie 10%.
</li> </li>
<li>int&lt;digit&gt;<br> <li>int&lt;digit&gt;<br>
Ermittelt den Zahlenwert ab dem Anfang der Zeichenkette aus der Ermittelt den Zahlenwert ab dem Anfang der Zeichenkette aus der
Spalte "VALUE", inclusive negativen Vorzeichen und Dezimaltrenner. Spalte "VALUE", inclusive negativen Vorzeichen und Dezimaltrenner.
Benutzt z.B. für Ausprägungen wie -5.7&deg;C. Benutzt z.B. f&ouml;r Auspr&auml;gungen wie -5.7&deg;C.
</li> </li>
<li>delta-h / delta-d<br> <li>delta-h / delta-d<br>
Ermittelt die relative Veränderung eines Zahlenwertes pro Stunde Ermittelt die relative Ver&auml;nderung eines Zahlenwertes pro Stunde
oder pro Tag. Wird benutzt z.B. für Spalten die einen oder pro Tag. Wird benutzt z.B. f&ouml;r Spalten die einen
hochlaufenden Zähler enthalten wie im Falle für ein KS300 Regenzähler hochlaufenden Z&auml;hler enthalten wie im Falle f&ouml;r ein KS300 Regenz&auml;hler
oder dem 1-wire Modul OWCOUNT. oder dem 1-wire Modul OWCOUNT.
</li> </li>
<li>delta-ts<br> <li>delta-ts<br>
@ -1590,19 +1675,19 @@ sub chartQuery($@) {
</li> </li>
</ul></li> </ul></li>
<li>&lt;regexp&gt;<br> <li>&lt;regexp&gt;<br>
Diese Zeichenkette wird als Perl Befehl ausgewertet. Die regexp wird vor dem angegebenen &lt;fn&gt; Parameter ausgeführt. Diese Zeichenkette wird als Perl Befehl ausgewertet. Die regexp wird vor dem angegebenen &lt;fn&gt; Parameter ausgef&ouml;hrt.
<br> <br>
Bitte zur Beachtung: Diese Zeichenkette darf keine Leerzeichen Bitte zur Beachtung: Diese Zeichenkette darf keine Leerzeichen
enthalten da diese sonst als &lt;column_spec&gt; Trennung enthalten da diese sonst als &lt;column_spec&gt; Trennung
interpretiert werden und alles nach dem Leerzeichen als neue interpretiert werden und alles nach dem Leerzeichen als neue
&lt;column_spec&gt; gesehen wird.<br> &lt;column_spec&gt; gesehen wird.<br>
<b>Schlüsselwörter</b> <b>Schl&ouml;sselw&ouml;rter</b>
<li>$val ist der aktuelle Wert die die Datenbank für ein Device/Reading ausgibt.</li> <li>$val ist der aktuelle Wert die die Datenbank f&ouml;r ein Device/Reading ausgibt.</li>
<li>$ts ist der aktuelle Timestamp des Logeintrages.</li> <li>$ts ist der aktuelle Timestamp des Logeintrages.</li>
<li>Wird als $val das Schlüsselwort "hide" zurückgegeben, so wird dieser Logeintrag nicht <li>Wird als $val das Schl&ouml;sselwort "hide" zur&ouml;ckgegeben, so wird dieser Logeintrag nicht
ausgegeben, trotzdem aber für die Zeitraumberechnung verwendet.</li> ausgegeben, trotzdem aber f&ouml;r die Zeitraumberechnung verwendet.</li>
<li>Wird als $val das Schlüsselwort "ignore" zurückgegeben, so wird dieser Logeintrag <li>Wird als $val das Schl&ouml;sselwort "ignore" zur&ouml;ckgegeben, so wird dieser Logeintrag
nicht für eine Folgeberechnung verwendet.</li> nicht f&ouml;r eine Folgeberechnung verwendet.</li>
</li> </li>
</ul></li> </ul></li>
</ul> </ul>
@ -1610,39 +1695,42 @@ sub chartQuery($@) {
<b>Beispiele:</b> <b>Beispiele:</b>
<ul> <ul>
<li><code>get myDbLog - - 2012-11-10 2012-11-20 KS300:temperature</code></li> <li><code>get myDbLog - - 2012-11-10 2012-11-20 KS300:temperature</code></li>
<li><code>get myDbLog current ALL - - %:temperature</code></li><br>
Damit erhält man alle aktuellen Readings "temperature" von allen in der DB geloggten Devices.
Achtung: bei Nutzung von Jokerzeichen auf die historyTabelle kann man sein FHEM aufgrund langer Laufzeit lahmlegen!
<li><code>get myDbLog - - 2012-11-10_10 2012-11-10_20 KS300:temperature::int1</code><br> <li><code>get myDbLog - - 2012-11-10_10 2012-11-10_20 KS300:temperature::int1</code><br>
gibt Daten aus von 10Uhr bis 20Uhr am 10.11.2012</li> gibt Daten aus von 10Uhr bis 20Uhr am 10.11.2012</li>
<li><code>get myDbLog - all 2012-11-10 2012-11-20 KS300:temperature</code></li> <li><code>get myDbLog - all 2012-11-10 2012-11-20 KS300:temperature</code></li>
<li><code>get myDbLog - - 2012-11-10 2012-11-20 KS300:temperature KS300:rain::delta-h KS300:rain::delta-d</code></li> <li><code>get myDbLog - - 2012-11-10 2012-11-20 KS300:temperature KS300:rain::delta-h KS300:rain::delta-d</code></li>
<li><code>get myDbLog - - 2012-11-10 2012-11-20 MyFS20:data:::$val=~s/(on|off).*/$1eq"on"?1:0/eg</code><br> <li><code>get myDbLog - - 2012-11-10 2012-11-20 MyFS20:data:::$val=~s/(on|off).*/$1eq"on"?1:0/eg</code><br>
gibt 1 zurück für alle Ausprägungen von on* (on|on-for-timer etc) und 0 für alle off*</li> gibt 1 zur&ouml;ck f&ouml;r alle Auspr&auml;gungen von on* (on|on-for-timer etc) und 0 f&ouml;r alle off*</li>
<li><code>get myDbLog - - 2012-11-10 2012-11-20 Bodenfeuchte:data:::$val=~s/.*B:\s([-\.\d]+).*/$1/eg</code><br> <li><code>get myDbLog - - 2012-11-10 2012-11-20 Bodenfeuchte:data:::$val=~s/.*B:\s([-\.\d]+).*/$1/eg</code><br>
Beispiel von OWAD: Ein Wert wie z.B.: <code>"A: 49.527 % B: 66.647 % C: 9.797 % D: 0.097 V"</code><br> Beispiel von OWAD: Ein Wert wie z.B.: <code>"A: 49.527 % B: 66.647 % C: 9.797 % D: 0.097 V"</code><br>
und die Ausgabe ist für das Reading B folgende: <code>2012-11-20_10:23:54 66.647</code></li> und die Ausgabe ist f&ouml;r das Reading B folgende: <code>2012-11-20_10:23:54 66.647</code></li>
<li><code>get DbLog - - 2013-05-26 2013-05-28 Pumpe:data::delta-ts:$val=~s/on/hide/</code><br> <li><code>get DbLog - - 2013-05-26 2013-05-28 Pumpe:data::delta-ts:$val=~s/on/hide/</code><br>
Realisierung eines Betriebsstundenzählers.Durch delta-ts wird die Zeit in Sek zwischen den Log- Realisierung eines Betriebsstundenz&auml;hlers.Durch delta-ts wird die Zeit in Sek zwischen den Log-
einträgen ermittelt. Die Zeiten werden bei den on-Meldungen nicht ausgegeben welche einer Abschaltzeit eintr&auml;gen ermittelt. Die Zeiten werden bei den on-Meldungen nicht ausgegeben welche einer Abschaltzeit
entsprechen würden.</li> entsprechen w&ouml;rden.</li>
</ul> </ul>
<br><br> <br><br>
</ul> </ul>
<b>Get</b> für die Nutzung von webcharts <b>Get</b> f&ouml;r die Nutzung von webcharts
<ul> <ul>
<code>get &lt;name&gt; &lt;infile&gt; &lt;outfile&gt; &lt;from&gt; <code>get &lt;name&gt; &lt;infile&gt; &lt;outfile&gt; &lt;from&gt;
&lt;to&gt; &lt;device&gt; &lt;querytype&gt; &lt;xaxis&gt; &lt;yaxis&gt; &lt;savename&gt; </code> &lt;to&gt; &lt;device&gt; &lt;querytype&gt; &lt;xaxis&gt; &lt;yaxis&gt; &lt;savename&gt; </code>
<br><br> <br><br>
Liest Daten aus der Datenbank aus und gibt diese in JSON formatiert aus. Wird für das Charting Frontend genutzt Liest Daten aus der Datenbank aus und gibt diese in JSON formatiert aus. Wird f&ouml;r das Charting Frontend genutzt
<br> <br>
<ul> <ul>
<li>&lt;name&gt;<br> <li>&lt;name&gt;<br>
Der Name des definierten DbLogs, so wie er in der fhem.cfg angegeben wurde.</li> Der Name des definierten DbLogs, so wie er in der fhem.cfg angegeben wurde.</li>
<li>&lt;in&gt;<br> <li>&lt;in&gt;<br>
Ein Dummy Parameter um eine Kompatibilität zum Filelog herzustellen. Ein Dummy Parameter um eine Kompatibilit&auml;t zum Filelog herzustellen.
Dieser Parameter ist immer auf <code>-</code> zu setzen.</li> Dieser Parameter ist immer auf <code>-</code> zu setzen.</li>
<li>&lt;out&gt;<br> <li>&lt;out&gt;<br>
Ein Dummy Parameter um eine Kompatibilität zum Filelog herzustellen. Ein Dummy Parameter um eine Kompatibilit&auml;t zum Filelog herzustellen.
Dieser Parameter ist auf <code>webchart</code> zu setzen um die Charting Get Funktion zu nutzen. Dieser Parameter ist auf <code>webchart</code> zu setzen um die Charting Get Funktion zu nutzen.
</li> </li>
<li>&lt;from&gt; / &lt;to&gt;<br> <li>&lt;from&gt; / &lt;to&gt;<br>
@ -1652,32 +1740,32 @@ sub chartQuery($@) {
<li>&lt;device&gt;<br> <li>&lt;device&gt;<br>
Ein String, der das abzufragende Device darstellt.</li> Ein String, der das abzufragende Device darstellt.</li>
<li>&lt;querytype&gt;<br> <li>&lt;querytype&gt;<br>
Ein String, der die zu verwendende Abfragemethode darstellt. Zur Zeit unterstützte Werte sind: <br> Ein String, der die zu verwendende Abfragemethode darstellt. Zur Zeit unterst&ouml;tzte Werte sind: <br>
<code>getreadings</code> um für ein bestimmtes device alle Readings zu erhalten<br> <code>getreadings</code> um f&ouml;r ein bestimmtes device alle Readings zu erhalten<br>
<code>getdevices</code> um alle verfügbaren devices zu erhalten<br> <code>getdevices</code> um alle verf&ouml;gbaren devices zu erhalten<br>
<code>timerange</code> um Chart-Daten abzufragen. Es werden die Parameter 'xaxis', 'yaxis', 'device', 'to' und 'from' benötigt<br> <code>timerange</code> um Chart-Daten abzufragen. Es werden die Parameter 'xaxis', 'yaxis', 'device', 'to' und 'from' ben&ouml;tigt<br>
<code>savechart</code> um einen Chart unter Angabe eines 'savename' und seiner zugehörigen Konfiguration abzuspeichern<br> <code>savechart</code> um einen Chart unter Angabe eines 'savename' und seiner zugeh&ouml;rigen Konfiguration abzuspeichern<br>
<code>deletechart</code> um einen zuvor gespeicherten Chart unter Angabe einer id zu löschen<br> <code>deletechart</code> um einen zuvor gespeicherten Chart unter Angabe einer id zu l&ouml;schen<br>
<code>getcharts</code> um eine Liste aller gespeicherten Charts zu bekommen.<br> <code>getcharts</code> um eine Liste aller gespeicherten Charts zu bekommen.<br>
<code>getTableData</code> um Daten aus der Datenbank abzufragen und in einer Tabelle darzustellen. Benötigt paging Parameter wie start und limit.<br> <code>getTableData</code> um Daten aus der Datenbank abzufragen und in einer Tabelle darzustellen. Ben&ouml;tigt paging Parameter wie start und limit.<br>
<code>hourstats</code> um Statistiken für einen Wert (yaxis) für eine Stunde abzufragen.<br> <code>hourstats</code> um Statistiken f&ouml;r einen Wert (yaxis) f&ouml;r eine Stunde abzufragen.<br>
<code>daystats</code> um Statistiken für einen Wert (yaxis) für einen Tag abzufragen.<br> <code>daystats</code> um Statistiken f&ouml;r einen Wert (yaxis) f&ouml;r einen Tag abzufragen.<br>
<code>weekstats</code> um Statistiken für einen Wert (yaxis) für eine Woche abzufragen.<br> <code>weekstats</code> um Statistiken f&ouml;r einen Wert (yaxis) f&ouml;r eine Woche abzufragen.<br>
<code>monthstats</code> um Statistiken für einen Wert (yaxis) für einen Monat abzufragen.<br> <code>monthstats</code> um Statistiken f&ouml;r einen Wert (yaxis) f&ouml;r einen Monat abzufragen.<br>
<code>yearstats</code> um Statistiken für einen Wert (yaxis) für ein Jahr abzufragen.<br> <code>yearstats</code> um Statistiken f&ouml;r einen Wert (yaxis) f&ouml;r ein Jahr abzufragen.<br>
</li> </li>
<li>&lt;xaxis&gt;<br> <li>&lt;xaxis&gt;<br>
Ein String, der die X-Achse repräsentiert</li> Ein String, der die X-Achse repr&auml;sentiert</li>
<li>&lt;yaxis&gt;<br> <li>&lt;yaxis&gt;<br>
Ein String, der die Y-Achse repräsentiert</li> Ein String, der die Y-Achse repr&auml;sentiert</li>
<li>&lt;savename&gt;<br> <li>&lt;savename&gt;<br>
Ein String, unter dem ein Chart in der Datenbank gespeichert werden soll</li> Ein String, unter dem ein Chart in der Datenbank gespeichert werden soll</li>
<li>&lt;chartconfig&gt;<br> <li>&lt;chartconfig&gt;<br>
Ein jsonstring der den zu speichernden Chart repräsentiert</li> Ein jsonstring der den zu speichernden Chart repr&auml;sentiert</li>
<li>&lt;pagingstart&gt;<br> <li>&lt;pagingstart&gt;<br>
Ein Integer um den Startwert für die Abfrage 'getTableData' festzulegen</li> Ein Integer um den Startwert f&ouml;r die Abfrage 'getTableData' festzulegen</li>
<li>&lt;paginglimit&gt;<br> <li>&lt;paginglimit&gt;<br>
Ein Integer um den Limitwert für die Abfrage 'getTableData' festzulegen</li> Ein Integer um den Limitwert f&ouml;r die Abfrage 'getTableData' festzulegen</li>
</ul> </ul>
<br><br> <br><br>
Beispiele: Beispiele:
@ -1685,16 +1773,16 @@ sub chartQuery($@) {
<li><code>get logdb - webchart "" "" "" getcharts</code><br> <li><code>get logdb - webchart "" "" "" getcharts</code><br>
Liefert alle gespeicherten Charts aus der Datenbank</li> Liefert alle gespeicherten Charts aus der Datenbank</li>
<li><code>get logdb - webchart "" "" "" getdevices</code><br> <li><code>get logdb - webchart "" "" "" getdevices</code><br>
Liefert alle verfügbaren Devices aus der Datenbank</li> Liefert alle verf&ouml;gbaren Devices aus der Datenbank</li>
<li><code>get logdb - webchart "" "" ESA2000_LED_011e getreadings</code><br> <li><code>get logdb - webchart "" "" ESA2000_LED_011e getreadings</code><br>
Liefert alle verfügbaren Readings aus der Datenbank unter Angabe eines Gerätes</li> Liefert alle verf&ouml;gbaren Readings aus der Datenbank unter Angabe eines Ger&auml;tes</li>
<li><code>get logdb - webchart 2013-02-11_00:00:00 2013-02-12_00:00:00 ESA2000_LED_011e timerange TIMESTAMP day_kwh</code><br> <li><code>get logdb - webchart 2013-02-11_00:00:00 2013-02-12_00:00:00 ESA2000_LED_011e timerange TIMESTAMP day_kwh</code><br>
Liefert Chart-Daten, die auf folgenden Parametern basieren: 'xaxis', 'yaxis', 'device', 'to' und 'from'<br> Liefert Chart-Daten, die auf folgenden Parametern basieren: 'xaxis', 'yaxis', 'device', 'to' und 'from'<br>
Die Ausgabe erfolgt als JSON, z.B.: <code>[{'TIMESTAMP':'2013-02-11 00:10:10','VALUE':'0.22431388090756'},{'TIMESTAMP'.....}]</code></li> Die Ausgabe erfolgt als JSON, z.B.: <code>[{'TIMESTAMP':'2013-02-11 00:10:10','VALUE':'0.22431388090756'},{'TIMESTAMP'.....}]</code></li>
<li><code>get logdb - webchart 2013-02-11_00:00:00 2013-02-12_00:00:00 ESA2000_LED_011e savechart TIMESTAMP day_kwh tageskwh</code><br> <li><code>get logdb - webchart 2013-02-11_00:00:00 2013-02-12_00:00:00 ESA2000_LED_011e savechart TIMESTAMP day_kwh tageskwh</code><br>
Speichert einen Chart unter Angabe eines 'savename' und seiner zugehörigen Konfiguration</li> Speichert einen Chart unter Angabe eines 'savename' und seiner zugeh&ouml;rigen Konfiguration</li>
<li><code>get logdb - webchart "" "" "" deletechart "" "" 7</code><br> <li><code>get logdb - webchart "" "" "" deletechart "" "" 7</code><br>
Löscht einen zuvor gespeicherten Chart unter Angabe einer id</li> L&ouml;scht einen zuvor gespeicherten Chart unter Angabe einer id</li>
</ul> </ul>
<br><br> <br><br>
</ul> </ul>
@ -1709,11 +1797,11 @@ sub chartQuery($@) {
</ul> </ul>
<br> <br>
Wenn DbLog genutzt wird, wird in alle Devices das Attribut <i>DbLogExclude</i> Wenn DbLog genutzt wird, wird in alle Devices das Attribut <i>DbLogExclude</i>
propagiert. Der Wert des Attributes wird als Regexp ausgewertet und schließt die propagiert. Der Wert des Attributes wird als Regexp ausgewertet und schliesst die
damit matchenden Readings von einem Logging aus. Einzelne Regexp werden durch damit matchenden Readings von einem Logging aus. Einzelne Regexp werden durch
Kommata getrennt. Ist MinIntervall angegeben, so wird der Logeintrag nur Kommata getrennt. Ist MinIntervall angegeben, so wird der Logeintrag nur
dann nicht geloggt, wenn das Intervall noch nicht erreicht und der Wert des dann nicht geloggt, wenn das Intervall noch nicht erreicht und der Wert des
Readings sich nicht verändert hat. Readings sich nicht ver&auml;ndert hat.
<br> <br>
<b>Beispiele</b> <b>Beispiele</b>
<ul> <ul>

View File

@ -62,6 +62,8 @@ sub Text2Speech_Initialize($)
" TTS_UseMP3Wrap:0,1". " TTS_UseMP3Wrap:0,1".
" TTS_MplayerCall". " TTS_MplayerCall".
" TTS_SentenceAppendix". " TTS_SentenceAppendix".
" TTS_FileMapping".
" TTS_FileTemplateDir".
" ".$readingFnAttributes; " ".$readingFnAttributes;
} }
@ -145,6 +147,10 @@ sub Text2Speech_Attr(@) {
my $hash = $defs{$a[1]}; my $hash = $defs{$a[1]};
my $value = $a[3]; my $value = $a[3];
my $TTS_FileTemplateDir = AttrVal($hash->{NAME}, "TTS_FileTemplateDir", "templates");
my $TTS_CacheFileDir = AttrVal($hash->{NAME}, "TTS_CacheFileDir", "cache");
my $TTS_FileMapping = AttrVal($hash->{NAME}, "TTS_FileMapping", ""); # zb, silence:silence.mp3 ring:myringtone.mp3;
if($a[2] eq "TTS_Delemiter" && $a[0] ne "del") { if($a[2] eq "TTS_Delemiter" && $a[0] ne "del") {
return "wrong delemiter syntax: [+-]a[lfn]. \n". return "wrong delemiter syntax: [+-]a[lfn]. \n".
" Example 1: +an~\n". " Example 1: +an~\n".
@ -166,8 +172,23 @@ sub Text2Speech_Attr(@) {
return "This Attribute is only available in direct mode" if($hash->{MODE} ne "DIRECT"); return "This Attribute is only available in direct mode" if($hash->{MODE} ne "DIRECT");
return "Attribute TTS_UseMP3Wrap is required!" unless(AttrVal($hash->{NAME}, "TTS_UseMP3Wrap", undef)); return "Attribute TTS_UseMP3Wrap is required!" unless(AttrVal($hash->{NAME}, "TTS_UseMP3Wrap", undef));
my $file = AttrVal($hash->{NAME}, "TTS_CacheFileDir", "cache") ."/". $value; my $file = $TTS_CacheFileDir ."/". $value;
return "File <".$file."> does not exists in CacheFileDir" if(! -e $file); return "File <".$file."> does not exists in CacheFileDir" if(! -e $file);
} elsif ($a[2] eq "TTS_FileTemplateDir") {
unless(-e ($TTS_CacheFileDir ."/". $value) or mkdir ($TTS_CacheFileDir ."/". $value)) {
#Verzeichnis anlegen gescheitert
return "Could not create directory: <$value>";
}
} elsif ($a[2] eq "TTS_FileMapping") {
#ueberpruefen, ob mp3 Template existiert
my @FileTpl = split(" ", $TTS_FileMapping);
for(my $j=0; $j<(@FileTpl); $j++) {
my @FileTplPc = split(/:/, $FileTpl[$j]);
return "file does not exist: <".$TTS_CacheFileDir ."/". $TTS_FileTemplateDir ."/". $FileTplPc[1] .">"
unless (-e $TTS_CacheFileDir ."/". $TTS_FileTemplateDir ."/". $FileTplPc[1]);
}
} }
if($a[0] eq "set" && $a[2] eq "disable") { if($a[0] eq "set" && $a[2] eq "disable") {
@ -319,6 +340,8 @@ sub Text2Speech_PrepareSpeech($$) {
my $TTS_Ressource = AttrVal($hash->{NAME}, "TTS_Ressource", "Google"); my $TTS_Ressource = AttrVal($hash->{NAME}, "TTS_Ressource", "Google");
my $TTS_Delemiter = AttrVal($hash->{NAME}, "TTS_Delemiter", undef); my $TTS_Delemiter = AttrVal($hash->{NAME}, "TTS_Delemiter", undef);
my $TTS_FileTpl = AttrVal($hash->{NAME}, "TTS_FileMapping", ""); # zb, silence:silence.mp3 ring:myringtone.mp3; im Text: mein Klingelton :ring: ist laut.
my $TTS_FileTemplateDir = AttrVal($hash->{NAME}, "TTS_FileTemplateDir", "templates");
my $TTS_ForceSplit = 0; my $TTS_ForceSplit = 0;
my $TTS_AddDelemiter; my $TTS_AddDelemiter;
@ -351,12 +374,30 @@ sub Text2Speech_PrepareSpeech($$) {
@text = $hash->{helper}{Text2Speech} if($hash->{helper}{Text2Speech}[0]); @text = $hash->{helper}{Text2Speech} if($hash->{helper}{Text2Speech}[0]);
push(@text, $t); push(@text, $t);
my @FileTpl = split(" ", $TTS_FileTpl);
my @FileTplPc;
for(my $i=0; $i<(@FileTpl); $i++) {
#splitte bei jedem Template auf
@FileTplPc = split(/:/, $FileTpl[$i]);
@text = Text2Speech_SplitString(\@text, 100, ":".$FileTplPc[0].":", 1, "as"); # splitte bei bspw: :ring:
}
@text = Text2Speech_SplitString(\@text, 100, $TTS_Delemiter, $TTS_ForceSplit, $TTS_AddDelemiter); @text = Text2Speech_SplitString(\@text, 100, $TTS_Delemiter, $TTS_ForceSplit, $TTS_AddDelemiter);
@text = Text2Speech_SplitString(\@text, 100, "(?<=[\\.!?])\\s*", 0, ""); @text = Text2Speech_SplitString(\@text, 100, "(?<=[\\.!?])\\s*", 0, "");
@text = Text2Speech_SplitString(\@text, 100, ",", 0, "al"); @text = Text2Speech_SplitString(\@text, 100, ",", 0, "al");
@text = Text2Speech_SplitString(\@text, 100, ";", 0, "al"); @text = Text2Speech_SplitString(\@text, 100, ";", 0, "al");
@text = Text2Speech_SplitString(\@text, 100, "und", 0, "af"); @text = Text2Speech_SplitString(\@text, 100, "und", 0, "af");
for(my $i=0; $i<(@text); $i++) {
for(my $j=0; $j<(@FileTpl); $j++) {
# entferne führende und abschließende Leerzeichen aus jedem Textbaustein
$text[$i] =~ s/^\s+|\s+$//g;
# ersetze die FileTemplates
@FileTplPc = split(/:/, $FileTpl[$j]);
$text[$i] = $TTS_FileTemplateDir ."/". $FileTplPc[1] if($text[$i] eq ":".$FileTplPc[0].":")
}
}
@{$hash->{helper}{Text2Speech}} = @text; @{$hash->{helper}{Text2Speech}} = @text;
} else { } else {
@ -370,7 +411,7 @@ sub Text2Speech_PrepareSpeech($$) {
# param3: string: Delemiter # param3: string: Delemiter
# param4: int : 1 -> es wird am Delemiter gesplittet # param4: int : 1 -> es wird am Delemiter gesplittet
# 0 -> es wird nur gesplittet, wenn Stringlänge länger als MaxChar # 0 -> es wird nur gesplittet, wenn Stringlänge länger als MaxChar
# param5: string: Add Delemiter to String? [pre|past|<empty>] # param5: string: Add Delemiter to String? [al|af|as|<empty>] (AddLast/AddFirst/AddSingle)
# #
# Splittet die Texte aus $hash->{helper}->{Text2Speech} anhand des # Splittet die Texte aus $hash->{helper}->{Text2Speech} anhand des
# Delemiters, wenn die Stringlänge MaxChars übersteigt. # Delemiters, wenn die Stringlänge MaxChars übersteigt.
@ -395,7 +436,7 @@ sub Text2Speech_SplitString(@$$$$){
for(my $j=0; $j<(@b); $j++) { for(my $j=0; $j<(@b); $j++) {
$b[$j] = $b[$j] . $Delemiter if($AddDelemiter eq "al"); # Am Satzende wieder hinzufügen. $b[$j] = $b[$j] . $Delemiter if($AddDelemiter eq "al"); # Am Satzende wieder hinzufügen.
$b[$j+1] = $Delemiter . $b[$j+1] if(($AddDelemiter eq "af") && ($b[$j+1])); # Am Satzanfang des nächsten Satzes wieder hinzufügen. $b[$j+1] = $Delemiter . $b[$j+1] if(($AddDelemiter eq "af") && ($b[$j+1])); # Am Satzanfang des nächsten Satzes wieder hinzufügen.
push(@newText, $Delemiter) if($AddDelemiter eq "as" && $j>0); # AddSingle: füge Delemiter als EinzelSatz hinzu. Zb. bei FileTemplates
push(@newText, $b[$j]); push(@newText, $b[$j]);
} }
} }
@ -434,6 +475,33 @@ sub Text2Speech_BuildMplayerCmdString($$) {
return $cmd; return $cmd;
} }
#####################################
# param1: hash : Hash
# param2: string: Dateiname
# param2: string: Text
#
# Holt den Text aus dem Google Translator als MP3Datei
#####################################
sub Text2Speech_Download($$$) {
my ($hash, $file, $text) = @_;
my $HttpResponse;
my $fh;
Log3 $hash->{NAME}, 4, "Text2Speech: Hole URL: ". "http://" . $ttsHost . $ttsPath . uri_escape($text);
$HttpResponse = GetHttpFile($ttsHost, $ttsPath . uri_escape($text));
$fh = new IO::File ">$file";
if(!defined($fh)) {
Log3 $hash->{NAME}, 2, "Text2Speech: mp3 Datei <$file> konnte nicht angelegt werden.";
return undef;
}
$fh->print($HttpResponse);
Log3 $hash->{NAME}, 4, "Text2Speech: Schreibe mp3 in die Datei $file mit ".length($HttpResponse)." Bytes";
close($fh);
}
##################################### #####################################
sub Text2Speech_DoIt($) { sub Text2Speech_DoIt($) {
my ($hash) = @_; my ($hash) = @_;
@ -445,7 +513,6 @@ sub Text2Speech_DoIt($) {
if($TTS_Ressource eq "Google") { if($TTS_Ressource eq "Google") {
my $HttpResponse;
my $filename; my $filename;
my $file; my $file;
@ -459,30 +526,35 @@ sub Text2Speech_DoIt($) {
if(AttrVal($hash->{NAME}, "TTS_UseMP3Wrap", 0)) { if(AttrVal($hash->{NAME}, "TTS_UseMP3Wrap", 0)) {
# benutze das Tool MP3Wrap um bereits einzelne vorhandene Sprachdateien # benutze das Tool MP3Wrap um bereits einzelne vorhandene Sprachdateien
# zusammenzuführen. Ziel: sauberer Sprachfluss # zusammenzuführen. Ziel: sauberer Sprachfluss
my @Mp3WrapArray; my @Mp3WrapFiles;
my $Mp3WrapText = ""; my @Mp3WrapText;
my $TTS_SentenceAppendix = AttrVal($hash->{NAME}, "TTS_SentenceAppendix", undef); #muss eine mp3-Datei sein, ohne Pfadangabe my $TTS_SentenceAppendix = AttrVal($hash->{NAME}, "TTS_SentenceAppendix", undef); #muss eine mp3-Datei sein, ohne Pfadangabe
undef($TTS_SentenceAppendix) if($TTS_SentenceAppendix && (! -e $TTS_CacheFileDir ."/".$TTS_SentenceAppendix)); my $TTS_FileTemplateDir = AttrVal($hash->{NAME}, "TTS_FileTemplateDir", "templates");
$TTS_SentenceAppendix = $TTS_CacheFileDir ."/". $TTS_FileTemplateDir ."/". $TTS_SentenceAppendix if($TTS_SentenceAppendix);
undef($TTS_SentenceAppendix) if($TTS_SentenceAppendix && (! -e $TTS_SentenceAppendix));
#Abspielliste erstellen
foreach my $t (@{$hash->{helper}{Text2Speech}}) { foreach my $t (@{$hash->{helper}{Text2Speech}}) {
$filename = md5_hex($t) . ".mp3"; if(-e $TTS_CacheFileDir."/".$t) { $filename = $t;} else {$filename = md5_hex($t) . ".mp3";} # falls eine bestimmte mp3-Datei gespielt werden soll
$file = $TTS_CacheFileDir."/".$filename; $file = $TTS_CacheFileDir."/".$filename;
if(-e $file) { if(-e $file) {
push(@Mp3WrapArray, $file); push(@Mp3WrapFiles, $file);
$Mp3WrapText .= $t; push(@Mp3WrapText, $t);
#Text2Speech_WriteStats($hash, 0, $file, $t);
} else {last;} } else {last;}
} }
push(@Mp3WrapArray, $TTS_CacheFileDir ."/".$TTS_SentenceAppendix) if($TTS_SentenceAppendix); push(@Mp3WrapFiles, $TTS_SentenceAppendix) if($TTS_SentenceAppendix);
if(scalar(@Mp3WrapArray) >= 2) { if(scalar(@Mp3WrapFiles) >= 2) {
Log3 $hash->{NAME}, 4, "Text2Speech: Bearbeite per MP3Wrap jetzt den Text: ". $Mp3WrapText; Log3 $hash->{NAME}, 4, "Text2Speech: Bearbeite per MP3Wrap jetzt den Text: ". join(" ", @Mp3WrapText);
my $Mp3WrapPrefix = md5_hex(join("|", @Mp3WrapArray)); my $Mp3WrapPrefix = md5_hex(join("|", @Mp3WrapFiles));
my $Mp3WrapFile = $TTS_CacheFileDir ."/". $Mp3WrapPrefix . "_MP3WRAP.mp3"; my $Mp3WrapFile = $TTS_CacheFileDir ."/". $Mp3WrapPrefix . "_MP3WRAP.mp3";
if(! -e $Mp3WrapFile) { if(! -e $Mp3WrapFile) {
$cmd = "mp3wrap " .$TTS_CacheFileDir. "/" .$Mp3WrapPrefix. ".mp3 " .join(" ", @Mp3WrapArray); $cmd = "mp3wrap " .$TTS_CacheFileDir. "/" .$Mp3WrapPrefix. ".mp3 " .join(" ", @Mp3WrapFiles);
$cmd .= " >/dev/null" if($verbose < 5);; $cmd .= " >/dev/null" if($verbose < 5);;
Log3 $hash->{NAME}, 4, "Text2Speech: " .$cmd; Log3 $hash->{NAME}, 4, "Text2Speech: " .$cmd;
@ -492,36 +564,29 @@ sub Text2Speech_DoIt($) {
$cmd = Text2Speech_BuildMplayerCmdString($hash, $Mp3WrapFile); $cmd = Text2Speech_BuildMplayerCmdString($hash, $Mp3WrapFile);
Log3 $hash->{NAME}, 4, "Text2Speech:" .$cmd; Log3 $hash->{NAME}, 4, "Text2Speech:" .$cmd;
system($cmd); system($cmd);
#Text2Speech_WriteStats($hash, 1, $Mp3WrapFile, join(" ", @Mp3WrapText));
} else { } else {
Log3 $hash->{NAME}, 2, "Text2Speech: Mp3Wrap Datei konnte nicht angelegt werden."; Log3 $hash->{NAME}, 2, "Text2Speech: Mp3Wrap Datei konnte nicht angelegt werden.";
} }
return $hash->{NAME} . "|". scalar(@Mp3WrapArray); return $hash->{NAME} ."|".
($TTS_SentenceAppendix ? scalar(@Mp3WrapFiles)-1: scalar(@Mp3WrapFiles)) ."|".
$Mp3WrapFile;
} }
} }
$filename = md5_hex($hash->{helper}{Text2Speech}[0]) . ".mp3"; if(-e $TTS_CacheFileDir."/".$hash->{helper}{Text2Speech}[0]) {
# falls eine bestimmte mp3-Datei gespielt werden soll
$filename = $hash->{helper}{Text2Speech}[0];
} else {
$filename = md5_hex($hash->{helper}{Text2Speech}[0]) . ".mp3";
}
$file = $TTS_CacheFileDir."/".$filename; $file = $TTS_CacheFileDir."/".$filename;
Log3 $hash->{NAME}, 4, "Text2Speech: Bearbeite jetzt den Text: ". $hash->{helper}{Text2Speech}[0]; Log3 $hash->{NAME}, 4, "Text2Speech: Bearbeite jetzt den Text: ". $hash->{helper}{Text2Speech}[0];
if(! -e $file) { # Datei existiert noch nicht im Cache if(! -e $file) { # Datei existiert noch nicht im Cache
my $fh; Text2Speech_Download($hash, $file, $hash->{helper}{Text2Speech}[0]);
Log3 $hash->{NAME}, 4, "Text2Speech: Hole URL: ". "http://" . $ttsHost . $ttsPath . uri_escape($hash->{helper}{Text2Speech}[0]);
$HttpResponse = GetHttpFile($ttsHost, $ttsPath . uri_escape($hash->{helper}{Text2Speech}[0]));
$fh = new IO::File ">$file";
if(!defined($fh)) {
Log3 $hash->{NAME}, 2, "Text2Speech: mp3 Datei <$file> konnte nicht angelegt werden.";
return undef;
}
$fh->print($HttpResponse);
Log3 $hash->{NAME}, 4, "Text2Speech: Schreibe mp3 in die Datei $file mit ".length($HttpResponse)." Bytes";
close($fh);
} else {
Log3 $hash->{NAME}, 4, "Text2Speech: Datei <$file> bereits vorhanden, erneuter Download nicht notwendig";
} }
if(-e $file) { # Datei existiert jetzt if(-e $file) { # Datei existiert jetzt
@ -530,19 +595,26 @@ sub Text2Speech_DoIt($) {
system($cmd); system($cmd);
} }
return $hash->{NAME}. "|".
"1" ."|".
$file;
} elsif ($TTS_Ressource eq "ESpeak") { } elsif ($TTS_Ressource eq "ESpeak") {
$cmd = "sudo espeak -vde+f3 -k5 -s150 \"" . $hash->{helper}{Text2Speech}[0] . "\""; $cmd = "sudo espeak -vde+f3 -k5 -s150 \"" . $hash->{helper}{Text2Speech}[0] . "\"";
Log3 $hash, 4, "Text2Speech:" .$cmd; Log3 $hash, 4, "Text2Speech:" .$cmd;
system($cmd); system($cmd);
} }
return $hash->{NAME}. "|". "1"; return $hash->{NAME}. "|".
"1" ."|".
"";
} }
#################################################### ####################################################
# Rückgabe der Blockingfunktion # Rückgabe der Blockingfunktion
# param1: HashName # param1: HashName
# param2: Anzahl der abgearbeiteten Textbausteine # param2: Anzahl der abgearbeiteten Textbausteine
# param3: Dateiname der abgespielt wurde
#################################################### ####################################################
sub Text2Speech_Done($) { sub Text2Speech_Done($) {
my ($string) = @_; my ($string) = @_;
@ -551,8 +623,16 @@ sub Text2Speech_Done($) {
my @a = split("\\|",$string); my @a = split("\\|",$string);
my $hash = $defs{shift(@a)}; my $hash = $defs{shift(@a)};
my $tts_done = shift(@a); my $tts_done = shift(@a);
#Log 1, "SleepDone: " . $string; my $filename = shift(@a);
if($filename) {
my @text;
for(my $i=0; $i<$tts_done; $i++) {
push(@text, $hash->{helper}{Text2Speech}[$i]);
}
Text2Speech_WriteStats($hash, 1, $filename, join(" ", @text));
}
delete($hash->{helper}{RUNNING_PID}); delete($hash->{helper}{RUNNING_PID});
splice(@{$hash->{helper}{Text2Speech}}, 0, $tts_done); splice(@{$hash->{helper}{Text2Speech}}, 0, $tts_done);
@ -570,6 +650,39 @@ sub Text2Speech_AbortFn($) {
Log3 $hash->{NAME}, 2, "Text2Speech: BlockingCall for ".$hash->{NAME}." was aborted"; Log3 $hash->{NAME}, 2, "Text2Speech: BlockingCall for ".$hash->{NAME}." was aborted";
} }
#####################################
# Hiermit werden Statistken per DbLogModul gesammelt
# Wichitg zur Entscheidung welche Dateien aus dem Cache lange
# nicht benutzt und somit gelöscht werden koennen.
#
# param1: hash
# param2: int: 0=indirekt (über mp3wrap); 1=direkt abgespielt
# param3: string: Datei
# param4: string: Text der als mp3 abgespielt wird
#####################################
sub Text2Speech_WriteStats($$$$){
my($hash, $typ, $file, $text) = @_;
my $DbLogDev;
#suche ein DbLogDevice
return undef unless($modules{"DbLog"} && $modules{"DbLog"}{"LOADED"});
foreach my $key (keys(%defs)) {
if($defs{$key}{TYPE} eq "DbLog") {
$DbLogDev = $key;
last;
}
}
return undef if($defs{$DbLogDev}{STATE} ne "active"); # muss active sein!
# den letzten Value von "Usage" ermitteln um dann die Staistik um 1 zu erhoehen.
my @LastValue = DbLog_Get($defs{$DbLogDev}, "", "current", "array", "-", "-", $hash->{NAME} ."|". $file.":Usage");
my $NewValue = 1;
$NewValue = $LastValue[0]{value} + 1 if($LastValue[0]);
# DbLogHash, DbLogTable, TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT
DbLog_Push($defs{$DbLogDev}, "Current", TimeNow(), $hash->{NAME} ."|". $file, $hash->{TYPE}, $text, "Usage", $NewValue, "");
}
1; 1;
=pod =pod
@ -700,6 +813,20 @@ sub Text2Speech_AbortFn($) {
Example: <code>silence.mp3</code> Example: <code>silence.mp3</code>
</li> </li>
<li>TTS_FileMapping<br>
Definition of mp3files with a custom templatedefinition. Separated by space.
All templatedefinitions can used in audiobricks by i>tts</i>.
The definition must begin and end with e colon.
The mp3files must saved in the given directory by <i>TTS_FIleTemplateDir</i>.<br>
<code>attr myTTS TTS_FileMapping ring:ringtone.mp3 beep:MyBeep.mp3</code><br>
<code>set MyTTS tts Attention: This is my ringtone :ring: Its loud?</code>
</li>
<li>TTS_FileTemplateDir<br>
Directory to save all mp3-files are defined in <i>TTS_FileMapping</i> und <i>TTS_SentenceAppendix</i><br>
Optional, Default: <code>cache/templates</code>
</li>
<li><a href="#readingFnAttributes">readingFnAttributes</a></li><br> <li><a href="#readingFnAttributes">readingFnAttributes</a></li><br>
<li><a href="#disable">disable</a><br> <li><a href="#disable">disable</a><br>
@ -847,6 +974,21 @@ sub Text2Speech_AbortFn($) {
Beispiel: <code>silence.mp3</code> Beispiel: <code>silence.mp3</code>
</li> </li>
<li>TTS_FileMapping<br>
Angabe von m&ouml;glichen MP3-Dateien mit deren Templatedefinition. Getrennt duch Leerzeichen.
Die Templatedefinitionen können in den per <i>tts</i> &uuml;bergebenen Sprachbausteinen verwendet werden
und m&uuml;ssen mit einem beginnenden und endenden Doppelpunkt angegeben werden.
Die Dateien müssen im Verzeichnis <i>TTS_FIleTemplateDir</i> gespeichert sein.<br>
<code>attr myTTS TTS_FileMapping ring:ringtone.mp3 beep:MyBeep.mp3</code><br>
<code>set MyTTS tts Achtung: hier kommt mein Klingelton :ring: War der laut?</code>
</li>
<li>TTS_FileTemplateDir<br>
Verzeichnis, in dem die per <i>TTS_FileMapping</i> und <i>TTS_SentenceAppendix</i> definierten
MP3-Dateien gespeichert sind.<br>
Optional, Default: <code>cache/templates</code>
</li>
<li><a href="#readingFnAttributes">readingFnAttributes</a></li><br> <li><a href="#readingFnAttributes">readingFnAttributes</a></li><br>
<li><a href="#disable">disable</a><br> <li><a href="#disable">disable</a><br>

View File

@ -542,4 +542,6 @@
- Fri Jan 16 2014 (andreas-fey) - Fri Jan 16 2014 (andreas-fey)
- Added new module "pilight" - Added new module "pilight"
- Sat Jan 18 2014 (tobiasfaust)
- Added new Module "Text2Speech"