2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-04-21 14:04:15 +00:00

93_DbRep: older than days / newer than days for reduceLog and delEntries as option, option writeToDBSingle for averageValue and sumValue, new sqlSpecial recentReadingsOfDevice, Forum: #topic,53584.msg1032788.html#msg1032788

git-svn-id: https://svn.fhem.de/fhem/trunk@21463 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
nasseeder1 2020-03-20 14:11:45 +00:00
parent 3f7d8a2261
commit 9ba1f55e8a
3 changed files with 320 additions and 211 deletions

View File

@ -1,5 +1,10 @@
# 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.
- feature: 93_DbRep: older than days / newer than days for reduceLog
and delEntries as option, option writeToDBSingle
for averageValue and sumValue,
new sqlSpecial recentReadingsOfDevice,
Forum: #topic,53584.msg1032788.html#msg1032788
- feature: 72_TA_CMI_JSON: DL-Bus data now also available to RSM (thx mkwi) - feature: 72_TA_CMI_JSON: DL-Bus data now also available to RSM (thx mkwi)
- new: 89_ESPEInk: New Module for EInk Modules connected to ESP boards - new: 89_ESPEInk: New Module for EInk Modules connected to ESP boards
- feature: 73_AutoShuttersControl: add additional condition - feature: 73_AutoShuttersControl: add additional condition

View File

@ -58,6 +58,10 @@ no if $] >= 5.017011, warnings => 'experimental::smartmatch';
# Version History intern # Version History intern
our %DbRep_vNotesIntern = ( our %DbRep_vNotesIntern = (
"8.36.0" => "19.03.2020 sqlSpecial recentReadingsOfDevice ",
"8.35.0" => "19.03.2020 option older than days and newer than days for delEntries function ",
"8.34.0" => "18.03.2020 option writeToDBSingle for functions averageValue, sumValue ",
"8.33.0" => "18.03.2020 option older than days and newer than days for reduceLog function ",
"8.32.4" => "15.03.2020 fix rights check for index operation ", "8.32.4" => "15.03.2020 fix rights check for index operation ",
"8.32.3" => "10.03.2020 better logfile messages in some cases of index operation ", "8.32.3" => "10.03.2020 better logfile messages in some cases of index operation ",
"8.32.2" => "01.03.2020 fix PERL WARNING: Argument \"\" isn't numeric in sprintf at ./FHEM/93_DbRep.pm line 10708 again ", "8.32.2" => "01.03.2020 fix PERL WARNING: Argument \"\" isn't numeric in sprintf at ./FHEM/93_DbRep.pm line 10708 again ",
@ -546,11 +550,11 @@ sub DbRep_Set($@) {
my $setlist = "Unknown argument $opt, choose one of ". my $setlist = "Unknown argument $opt, choose one of ".
"eraseReadings:noArg ". "eraseReadings:noArg ".
(($hash->{ROLE} ne "Agent")?"sumValue:display,writeToDB ":""). (($hash->{ROLE} ne "Agent")?"sumValue:display,writeToDB,writeToDBSingle ":"").
(($hash->{ROLE} ne "Agent")?"averageValue:display,writeToDB ":""). (($hash->{ROLE} ne "Agent")?"averageValue:display,writeToDB,writeToDBSingle ":"").
(($hash->{ROLE} ne "Agent")?"changeValue ":""). (($hash->{ROLE} ne "Agent")?"changeValue ":"").
(($hash->{ROLE} ne "Agent")?"delDoublets:adviceDelete,delete ":""). (($hash->{ROLE} ne "Agent")?"delDoublets:adviceDelete,delete ":"").
(($hash->{ROLE} ne "Agent")?"delEntries:noArg ":""). (($hash->{ROLE} ne "Agent")?"delEntries ":"").
(($hash->{ROLE} ne "Agent")?"delSeqDoublets:adviceRemain,adviceDelete,delete ":""). (($hash->{ROLE} ne "Agent")?"delSeqDoublets:adviceRemain,adviceDelete,delete ":"").
"deviceRename ". "deviceRename ".
(($hash->{ROLE} ne "Agent")?"readingRename ":""). (($hash->{ROLE} ne "Agent")?"readingRename ":"").
@ -566,7 +570,7 @@ sub DbRep_Set($@) {
(($hash->{ROLE} ne "Agent")?"reduceLog ":""). (($hash->{ROLE} ne "Agent")?"reduceLog ":"").
(($hash->{ROLE} ne "Agent")?"sqlCmd:textField-long ":""). (($hash->{ROLE} ne "Agent")?"sqlCmd:textField-long ":"").
(($hash->{ROLE} ne "Agent" && $hl)?"sqlCmdHistory:".$hl." ":""). (($hash->{ROLE} ne "Agent" && $hl)?"sqlCmdHistory:".$hl." ":"").
(($hash->{ROLE} ne "Agent")?"sqlSpecial:50mostFreqLogsLast2days,allDevCount,allDevReadCount ":""). (($hash->{ROLE} ne "Agent")?"sqlSpecial:50mostFreqLogsLast2days,allDevCount,allDevReadCount,recentReadingsOfDevice ":"").
(($hash->{ROLE} ne "Agent")?"syncStandby ":""). (($hash->{ROLE} ne "Agent")?"syncStandby ":"").
(($hash->{ROLE} ne "Agent")?"tableCurrentFillup:noArg ":""). (($hash->{ROLE} ne "Agent")?"tableCurrentFillup:noArg ":"").
(($hash->{ROLE} ne "Agent")?"tableCurrentPurge:noArg ":""). (($hash->{ROLE} ne "Agent")?"tableCurrentPurge:noArg ":"").
@ -583,8 +587,7 @@ sub DbRep_Set($@) {
if ($opt =~ /eraseReadings/) { if ($opt =~ /eraseReadings/) {
$hash->{LASTCMD} = $prop?"$opt $prop":"$opt"; $hash->{LASTCMD} = $prop?"$opt $prop":"$opt";
# Readings löschen die nicht in der Ausnahmeliste (Attr readingPreventFromDel) stehen DbRep_delread($hash); # Readings löschen die nicht in der Ausnahmeliste (Attr readingPreventFromDel) stehen
DbRep_delread($hash);
return undef; return undef;
} }
@ -599,7 +602,6 @@ sub DbRep_Set($@) {
Log3 ($name, 3, "DbRep $name - ### New database clientSide dump ###"); Log3 ($name, 3, "DbRep $name - ### New database clientSide dump ###");
Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ################################################################");
} }
# Befehl vor Procedure ausführen
DbRep_beforeproc($hash, "dump"); DbRep_beforeproc($hash, "dump");
DbRep_Main($hash,$opt,$prop); DbRep_Main($hash,$opt,$prop);
return undef; return undef;
@ -610,7 +612,6 @@ sub DbRep_Set($@) {
Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ################################################################");
Log3 ($name, 3, "DbRep $name - ### New SQLite dump ###"); Log3 ($name, 3, "DbRep $name - ### New SQLite dump ###");
Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ################################################################");
# Befehl vor Procedure ausführen
DbRep_beforeproc($hash, "dump"); DbRep_beforeproc($hash, "dump");
DbRep_Main($hash,$opt,$prop); DbRep_Main($hash,$opt,$prop);
return undef; return undef;
@ -620,7 +621,6 @@ sub DbRep_Set($@) {
$prop = $prop?$prop:36000; $prop = $prop?$prop:36000;
if($prop) { if($prop) {
unless($prop =~ /^(\d+)$/) { return " The Value of $opt is not valid. Use only figures 0-9 without decimal places !";}; unless($prop =~ /^(\d+)$/) { return " The Value of $opt is not valid. Use only figures 0-9 without decimal places !";};
# unless ($aVal =~ /^[0-9]+$/) { return " The Value of $aName is not valid. Use only figures 0-9 without decimal places !";}
} }
$hash->{LASTCMD} = $prop?"$opt $prop":"$opt"; $hash->{LASTCMD} = $prop?"$opt $prop":"$opt";
Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ################################################################");
@ -631,7 +631,6 @@ sub DbRep_Set($@) {
my $dbl = $dbloghash->{NAME}; my $dbl = $dbloghash->{NAME};
CommandSet(undef,"$dbl reopen $prop"); CommandSet(undef,"$dbl reopen $prop");
# Befehl vor Procedure ausführen
DbRep_beforeproc($hash, "repair"); DbRep_beforeproc($hash, "repair");
DbRep_Main($hash,$opt); DbRep_Main($hash,$opt);
return undef; return undef;
@ -642,7 +641,6 @@ sub DbRep_Set($@) {
Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ################################################################");
Log3 ($name, 3, "DbRep $name - ### New database Restore/Recovery ###"); Log3 ($name, 3, "DbRep $name - ### New database Restore/Recovery ###");
Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ################################################################");
# Befehl vor Procedure ausführen
DbRep_beforeproc($hash, "restore"); DbRep_beforeproc($hash, "restore");
DbRep_Main($hash,$opt,$prop); DbRep_Main($hash,$opt,$prop);
return undef; return undef;
@ -653,7 +651,6 @@ sub DbRep_Set($@) {
Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ################################################################");
Log3 ($name, 3, "DbRep $name - ### New optimize table / vacuum execution ###"); Log3 ($name, 3, "DbRep $name - ### New optimize table / vacuum execution ###");
Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ################################################################");
# Befehl vor Procedure ausführen
DbRep_beforeproc($hash, "optimize"); DbRep_beforeproc($hash, "optimize");
DbRep_Main($hash,$opt); DbRep_Main($hash,$opt);
return undef; return undef;
@ -681,7 +678,6 @@ sub DbRep_Set($@) {
Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ################################################################");
Log3 ($name, 3, "DbRep $name - ### new reduceLog run ###"); Log3 ($name, 3, "DbRep $name - ### new reduceLog run ###");
Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ################################################################");
# Befehl vor Procedure ausführen
DbRep_beforeproc($hash, "reduceLog"); DbRep_beforeproc($hash, "reduceLog");
DbRep_Main($hash,$opt); DbRep_Main($hash,$opt);
return undef; return undef;
@ -799,13 +795,15 @@ sub DbRep_Set($@) {
if (!AttrVal($hash->{NAME}, "allowDeletion", undef)) { if (!AttrVal($hash->{NAME}, "allowDeletion", undef)) {
return " Set attribute 'allowDeletion' if you want to allow deletion of any database entries. Use it with care !"; return " Set attribute 'allowDeletion' if you want to allow deletion of any database entries. Use it with care !";
} }
delete $hash->{HELPER}{DELENTRIES};
$hash->{HELPER}{DELENTRIES} = \@a;
DbRep_beforeproc($hash, "delEntries"); DbRep_beforeproc($hash, "delEntries");
DbRep_Main($hash,$opt); DbRep_Main($hash,$opt);
} elsif ($opt eq "deviceRename") { } elsif ($opt eq "deviceRename") {
shift @a; shift @a;
shift @a; shift @a;
$prop = join(" ",@a); # Device Name kann Leerzeichen enthalten $prop = join(" ",@a); # Device Name kann Leerzeichen enthalten
my ($olddev, $newdev) = split(",",$prop); my ($olddev, $newdev) = split(",",$prop);
$hash->{LASTCMD} = $prop?"$opt $prop":"$opt"; $hash->{LASTCMD} = $prop?"$opt $prop":"$opt";
if (!$olddev || !$newdev) {return "Both entries \"old device name\", \"new device name\" are needed. Use \"set $name deviceRename olddevname,newdevname\" ";} if (!$olddev || !$newdev) {return "Both entries \"old device name\", \"new device name\" are needed. Use \"set $name deviceRename olddevname,newdevname\" ";}
@ -817,7 +815,7 @@ sub DbRep_Set($@) {
} elsif ($opt eq "readingRename") { } elsif ($opt eq "readingRename") {
shift @a; shift @a;
shift @a; shift @a;
$prop = join(" ",@a); # Readingname kann Leerzeichen enthalten $prop = join(" ",@a); # Readingname kann Leerzeichen enthalten
$hash->{LASTCMD} = $prop?"$opt $prop":"$opt"; $hash->{LASTCMD} = $prop?"$opt $prop":"$opt";
my ($oldread, $newread) = split(",",$prop); my ($oldread, $newread) = split(",",$prop);
if (!$oldread || !$newread) {return "Both entries \"old reading name\", \"new reading name\" are needed. Use \"set $name readingRename oldreadingname,newreadingname\" ";} if (!$oldread || !$newread) {return "Both entries \"old reading name\", \"new reading name\" are needed. Use \"set $name readingRename oldreadingname,newreadingname\" ";}
@ -896,7 +894,6 @@ sub DbRep_Set($@) {
} elsif ($opt =~ /sqlCmd|sqlSpecial|sqlCmdHistory/) { } elsif ($opt =~ /sqlCmd|sqlSpecial|sqlCmdHistory/) {
return "\"set $opt\" needs at least an argument" if ( @a < 3 ); return "\"set $opt\" needs at least an argument" if ( @a < 3 );
# remove arg 0, 1 to get SQL command
my $sqlcmd; my $sqlcmd;
if($opt eq "sqlSpecial") { if($opt eq "sqlSpecial") {
$sqlcmd = $prop; $sqlcmd = $prop;
@ -905,7 +902,9 @@ sub DbRep_Set($@) {
my @cmd = @a; my @cmd = @a;
shift @cmd; shift @cmd; shift @cmd; shift @cmd;
$sqlcmd = join(" ", @cmd); $sqlcmd = join(" ", @cmd);
$sqlcmd =~ tr/ A-Za-z0-9!"#$§%&'()*+,-.\/:;<=>?@[\\]^_`{|}~äöüÄÖÜ߀/ /cs; @cmd = split(/\s/, $sqlcmd);
$sqlcmd = join(" ", @cmd);
# $sqlcmd =~ tr/ A-Za-z0-9!"#$§%&'()*+,-.\/:;<=>?@[\\]^_`{|}~äöüÄÖÜ߀/ /cs; # V8.36.0 20.03.2020
} }
if($opt eq "sqlCmdHistory") { if($opt eq "sqlCmdHistory") {
$prop =~ tr/ A-Za-z0-9!"#$%&'()*+,-.\/:;<=>?@[\\]^_`{|}~äöüÄÖÜ߀/ /cs; $prop =~ tr/ A-Za-z0-9!"#$%&'()*+,-.\/:;<=>?@[\\]^_`{|}~äöüÄÖÜ߀/ /cs;
@ -1041,7 +1040,6 @@ sub DbRep_Get($@) {
} elsif ($opt =~ /dbValue/) { } elsif ($opt =~ /dbValue/) {
return "get \"$opt\" needs at least an argument" if ( @a < 3 ); return "get \"$opt\" needs at least an argument" if ( @a < 3 );
# remove arg 0, 1 to get SQL command
my @cmd = @a; my @cmd = @a;
shift @cmd; shift @cmd; shift @cmd; shift @cmd;
my $sqlcmd = join(" ",@cmd); my $sqlcmd = join(" ",@cmd);
@ -2008,6 +2006,14 @@ sub DbRep_Main($$;$) {
$hash->{HELPER}{RUNNING_PID} = BlockingCall("minval_DoParse", "$name§$device§$reading§$prop§$ts", "minval_ParseDone", $to, "DbRep_ParseAborted", $hash); $hash->{HELPER}{RUNNING_PID} = BlockingCall("minval_DoParse", "$name§$device§$reading§$prop§$ts", "minval_ParseDone", $to, "DbRep_ParseAborted", $hash);
} elsif ($opt eq "delEntries") { } elsif ($opt eq "delEntries") {
my ($yyyy1, $mm1, $dd1, $hh1, $min1, $sec1) = ($runtime_string_first =~ /(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)/);
my ($yyyy2, $mm2, $dd2, $hh2, $min2, $sec2) = ($runtime_string_next =~ /(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)/);
my $nthants = timelocal($sec1, $min1, $hh1, $dd1, $mm1-1, $yyyy1-1900);
my $othants = timelocal($sec2, $min2, $hh2, $dd2, $mm2-1, $yyyy2-1900);
if($nthants >= $othants) {
ReadingsSingleUpdateValue ($hash, "state", "Error - Wrong time limits. The <nn> (days newer than) option must be greater than the <no> (older than) one !", 1);
return;
}
$hash->{HELPER}{RUNNING_PID} = BlockingCall("del_DoParse", "$name|history|$device|$reading|$runtime_string_first|$runtime_string_next", "del_ParseDone", $to, "DbRep_ParseAborted", $hash); $hash->{HELPER}{RUNNING_PID} = BlockingCall("del_DoParse", "$name|history|$device|$reading|$runtime_string_first|$runtime_string_next", "del_ParseDone", $to, "DbRep_ParseAborted", $hash);
} elsif ($opt eq "tableCurrentPurge") { } elsif ($opt eq "tableCurrentPurge") {
@ -2042,12 +2048,28 @@ sub DbRep_Main($$;$) {
if ($opt =~ /sqlSpecial/) { if ($opt =~ /sqlSpecial/) {
if($prop eq "50mostFreqLogsLast2days") { if($prop eq "50mostFreqLogsLast2days") {
$prop = "select Device, reading, count(0) AS `countA` from history where ( TIMESTAMP > (now() - interval 2 day)) group by DEVICE, READING order by countA desc, DEVICE limit 50;" if($dbmodel =~ /MYSQL/); $prop = "select Device, reading, count(0) AS `countA` from history where ( TIMESTAMP > (now() - interval 2 day)) group by DEVICE, READING order by countA desc, DEVICE limit 50;" if($dbmodel =~ /MYSQL/);
$prop = "select Device, reading, count(0) AS `countA` from history where ( TIMESTAMP > ('now' - '2 days')) group by DEVICE, READING order by countA desc, DEVICE limit 50;" if($dbmodel =~ /SQLITE/); $prop = "select Device, reading, count(0) AS `countA` from history where ( TIMESTAMP > ('now' - '2 days')) group by DEVICE, READING order by countA desc, DEVICE limit 50;" if($dbmodel =~ /SQLITE/);
$prop = "select Device, reading, count(0) AS countA from history where ( TIMESTAMP > (NOW() - INTERVAL '2' DAY)) group by DEVICE, READING order by countA desc, DEVICE limit 50;" if($dbmodel =~ /POSTGRESQL/); $prop = "select Device, reading, count(0) AS countA from history where ( TIMESTAMP > (NOW() - INTERVAL '2' DAY)) group by DEVICE, READING order by countA desc, DEVICE limit 50;" if($dbmodel =~ /POSTGRESQL/);
} elsif ($prop eq "allDevReadCount") { } elsif ($prop eq "allDevReadCount") {
$prop = "select device, reading, count(*) from history group by DEVICE, READING;"; $prop = "select device, reading, count(*) from history group by DEVICE, READING;";
} elsif ($prop eq "allDevCount") { } elsif ($prop eq "allDevCount") {
$prop = "select device, count(*) from history group by DEVICE;"; $prop = "select device, count(*) from history group by DEVICE;";
} elsif ($prop eq "recentReadingsOfDevice") {
my ($tq,$gcl);
if($dbmodel =~ /MYSQL/) {$tq = "NOW() - INTERVAL 1 DAY"; $gcl = "READING"};
if($dbmodel =~ /SQLITE/) {$tq = "datetime('now','-1 day')"; $gcl = "READING"};
if($dbmodel =~ /POSTGRESQL/) {$tq = "CURRENT_TIMESTAMP - INTERVAL '1 day'"; $gcl = "READING,DEVICE"};
my @cmd = split(/\s/, "SELECT t1.TIMESTAMP,t1.DEVICE,t1.READING,t1.VALUE
FROM history t1
INNER JOIN
(select max(TIMESTAMP) AS TIMESTAMP,DEVICE,READING
from history where DEVICE = '".$device."' and TIMESTAMP > ".$tq." group by ".$gcl.") x
ON x.TIMESTAMP = t1.TIMESTAMP AND
x.DEVICE = t1.DEVICE AND
x.READING = t1.READING;");
$prop = join(" ", @cmd);
} }
} }
$hash->{HELPER}{RUNNING_PID} = BlockingCall("sqlCmd_DoParse", "$name|$opt|$runtime_string_first|$runtime_string_next|$prop", "sqlCmd_ParseDone", $to, "DbRep_ParseAborted", $hash); $hash->{HELPER}{RUNNING_PID} = BlockingCall("sqlCmd_DoParse", "$name|$opt|$runtime_string_first|$runtime_string_next|$prop", "sqlCmd_ParseDone", $to, "DbRep_ParseAborted", $hash);
@ -2059,6 +2081,14 @@ sub DbRep_Main($$;$) {
} }
if ($opt =~ /reduceLog/) { if ($opt =~ /reduceLog/) {
my ($yyyy1, $mm1, $dd1, $hh1, $min1, $sec1) = ($runtime_string_first =~ /(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)/);
my ($yyyy2, $mm2, $dd2, $hh2, $min2, $sec2) = ($runtime_string_next =~ /(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)/);
my $nthants = timelocal($sec1, $min1, $hh1, $dd1, $mm1-1, $yyyy1-1900);
my $othants = timelocal($sec2, $min2, $hh2, $dd2, $mm2-1, $yyyy2-1900);
if($nthants >= $othants) {
ReadingsSingleUpdateValue ($hash, "state", "Error - Wrong time limits. The <nn> (days newer than) option must be greater than the <no> (older than) one !", 1);
return;
}
$hash->{HELPER}{RUNNING_REDUCELOG} = BlockingCall("DbRep_reduceLog", "$name|$device|$reading|$runtime_string_first|$runtime_string_next", "DbRep_reduceLogDone", $to, "DbRep_reduceLogAborted", $hash); $hash->{HELPER}{RUNNING_REDUCELOG} = BlockingCall("DbRep_reduceLog", "$name|$device|$reading|$runtime_string_first|$runtime_string_next", "DbRep_reduceLogDone", $to, "DbRep_reduceLogAborted", $hash);
ReadingsSingleUpdateValue ($hash, "state", "reduceLog database is running - be patient and see Logfile !", 1); ReadingsSingleUpdateValue ($hash, "state", "reduceLog database is running - be patient and see Logfile !", 1);
$hash->{HELPER}{RUNNING_REDUCELOG}{loglevel} = 5 if($hash->{HELPER}{RUNNING_REDUCELOG}); # Forum #77057 $hash->{HELPER}{RUNNING_REDUCELOG}{loglevel} = 5 if($hash->{HELPER}{RUNNING_REDUCELOG}); # Forum #77057
@ -2118,7 +2148,7 @@ sub DbRep_createTimeArray($$$) {
} }
if (AttrVal($name,"timestamp_begin","") eq "current_year_begin" || if (AttrVal($name,"timestamp_begin","") eq "current_year_begin" ||
AttrVal($name,"timestamp_end","") eq "current_year_begin") { AttrVal($name,"timestamp_end","") eq "current_year_begin") {
$tsbegin = strftime "%Y-%m-%d %T",localtime(timelocal(0,0,0,1,0,$year)) if(AttrVal($name,"timestamp_begin","") eq "current_year_begin"); $tsbegin = strftime "%Y-%m-%d %T",localtime(timelocal(0,0,0,1,0,$year)) if(AttrVal($name,"timestamp_begin","") eq "current_year_begin");
$tsend = strftime "%Y-%m-%d %T",localtime(timelocal(0,0,0,1,0,$year)) if(AttrVal($name,"timestamp_end","") eq "current_year_begin"); $tsend = strftime "%Y-%m-%d %T",localtime(timelocal(0,0,0,1,0,$year)) if(AttrVal($name,"timestamp_end","") eq "current_year_begin");
} }
@ -8547,13 +8577,24 @@ sub DbRep_reduceLog($) {
my $dbmodel = $dbloghash->{MODEL}; my $dbmodel = $dbloghash->{MODEL};
my $dbpassword = $attr{"sec$dblogname"}{secret}; my $dbpassword = $attr{"sec$dblogname"}{secret};
my @a = @{$hash->{HELPER}{REDUCELOG}}; my @a = @{$hash->{HELPER}{REDUCELOG}};
my $rlpar = join(" ", @a);
my $utf8 = defined($hash->{UTF8})?$hash->{UTF8}:0; my $utf8 = defined($hash->{UTF8})?$hash->{UTF8}:0;
delete $hash->{HELPER}{REDUCELOG};
my ($ret,$row,$filter,$exclude,$c,$day,$hour,$lastHour,$updDate,$updHour,$average,$processingDay,$lastUpdH); my ($ret,$row,$filter,$exclude,$c,$day,$hour,$lastHour,$updDate,$updHour,$average,$processingDay,$lastUpdH);
my (%hourlyKnown,%averageHash,@excludeRegex,@dayRows,@averageUpd,@averageUpdD); my (%hourlyKnown,%averageHash,@excludeRegex,@dayRows,@averageUpd,@averageUpdD);
my ($startTime,$currentHour,$currentDay,$deletedCount,$updateCount,$sum,$rowCount,$excludeCount) = (time(),99,0,0,0,0,0,0); my ($startTime,$currentHour,$currentDay,$deletedCount,$updateCount,$sum,$rowCount,$excludeCount) = (time(),99,0,0,0,0,0,0);
my ($dbh,$err,$brt); my ($dbh,$err,$brt);
BlockingInformParent("DbRep_delHashValFromBlocking", [$name, "HELPER","REDUCELOG"], 1);
# ausfiltern von optionalen Zeitangaben, z.B. 700:750 -> Rest zur Einhaltung des ursprünglichen Formats nach @a schreiben
my @b;
foreach (@a) {
next if($_ =~ /\b(\d+(:\d+)?)\b/);
push @b, $_;
}
@a = @b;
Log3 ($name, 5, "DbRep $name -> Start DbLog_reduceLog"); Log3 ($name, 5, "DbRep $name -> Start DbLog_reduceLog");
eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoInactiveDestroy => 1, mysql_enable_utf8 => $utf8 });}; eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoInactiveDestroy => 1, mysql_enable_utf8 => $utf8 });};
@ -9785,7 +9826,18 @@ sub DbRep_normRelTime($) {
my $tdtn = AttrVal($name, "timeDiffToNow", undef); my $tdtn = AttrVal($name, "timeDiffToNow", undef);
my $toth = AttrVal($name, "timeOlderThan", undef); my $toth = AttrVal($name, "timeOlderThan", undef);
my $fdopt = 0; # FullDay Option my $fdopt = 0; # FullDay Option
my ($y,$d,$h,$m,$s,$aval); my ($y,$d,$h,$m,$s,$aval,@a);
# evtl. Relativzeiten bei "reduceLog" oder "deleteEntries" berücksichtigen
@a = @{$hash->{HELPER}{REDUCELOG}} if($hash->{HELPER}{REDUCELOG});
@a = @{$hash->{HELPER}{DELENTRIES}} if($hash->{HELPER}{DELENTRIES});
foreach (@a) {
if($_ =~ /\b(\d+(:\d+)?)\b/) {
my ($od,$nd) = split(":",$1); # $od - Tage älter als , $nd - Tage neuer als
$toth = "d:$od" if($od);
$tdtn = "d:$nd" if($nd);
}
}
if($tdtn && $tdtn =~ /^\s*[ydhms]:(([\d]+.[\d]+)|[\d]+)\s*/) { if($tdtn && $tdtn =~ /^\s*[ydhms]:(([\d]+.[\d]+)|[\d]+)\s*/) {
$aval = $tdtn; $aval = $tdtn;
@ -10743,7 +10795,7 @@ sub DbRep_OutputWriteToDB($$$$$) {
# Daten auf maximale Länge beschneiden (DbLog-Funktion !) # Daten auf maximale Länge beschneiden (DbLog-Funktion !)
($device,$type,$event,$reading,$value,$unit) = DbLog_cutCol($dbloghash,$device,$type,$event,$reading,$value,$unit); ($device,$type,$event,$reading,$value,$unit) = DbLog_cutCol($dbloghash,$device,$type,$event,$reading,$value,$unit);
if($i == 0) { if($i == 0) {
push(@row_array, "$date $time|$device|$type|$event|$reading|$value|$unit"); push(@row_array, "$date $time|$device|$type|$event|$reading|$value|$unit") if($hash->{LASTCMD} !~ /\bwriteToDBSingle\b/);
push(@row_array, "$ndate $ntime|$device|$type|$event|$reading|$value|$unit"); push(@row_array, "$ndate $ntime|$device|$type|$event|$reading|$value|$unit");
} else { } else {
if ($aggr =~ /no|day|week|month|year/) { if ($aggr =~ /no|day|week|month|year/) {
@ -10755,7 +10807,7 @@ sub DbRep_OutputWriteToDB($$$$$) {
$t1 = fhemTimeLocal(01, 00, $hour, $mday, $mon-1, $year-1900); $t1 = fhemTimeLocal(01, 00, $hour, $mday, $mon-1, $year-1900);
($date,$time) = split(" ",FmtDateTime($t1)); ($date,$time) = split(" ",FmtDateTime($t1));
} }
push(@row_array, "$date $time|$device|$type|$event|$reading|$value|$unit"); push(@row_array, "$date $time|$device|$type|$event|$reading|$value|$unit") if($hash->{LASTCMD} !~ /\bwriteToDBSingle\b/);
push(@row_array, "$ndate $ntime|$device|$type|$event|$reading|$value|$unit"); push(@row_array, "$ndate $ntime|$device|$type|$event|$reading|$value|$unit");
} }
} }
@ -11191,6 +11243,22 @@ sub DbRep_numval ($){
return $val; return $val;
} }
####################################################################################################
# löscht einen Wert vom $hash des Hauptprozesses aus einem BlockingCall heraus
####################################################################################################
sub DbRep_delHashValFromBlocking($$;$) {
my ($name,$v1,$v2) = @_;
my $hash = $defs{$name};
if($v2) {
delete $hash->{$v1}{$v2};
} elsif ($v1) {
delete $hash->{$v1};
}
return 1;
}
################################################################ ################################################################
# sortiert eine Liste von Versionsnummern x.x.x # sortiert eine Liste von Versionsnummern x.x.x
# Schwartzian Transform and the GRT transform # Schwartzian Transform and the GRT transform
@ -11740,21 +11808,27 @@ return;
</li> <br> </li> <br>
<li><b> averageValue [display | writeToDB]</b> <li><b> averageValue [display | writeToDB | writeToDBSingle]</b>
- calculates the average value of database column "VALUE" between period given by - calculates an average value of the database field "VALUE" in the time limits
timestamp-<a href="#DbRepattr">attributes</a> which are set. of the possible time.*-attributes. <br><br>
The reading to evaluate must be specified by attribute "reading". <br>
By attribute "averageCalcForm" the calculation variant for average determination will be configured.
Is no or the option "display" specified, the results are only displayed. Using The reading to be evaluated must be specified in the attribute <a href="#reading">reading</a>
option "writeToDB" the calculated results are stored in the database with a new reading must be specified.
name. <br> With the attribute <a href="#averageCalcForm">averageCalcForm</a> the calculation variant
The new readingname is built of a prefix and the original reading name, is used for Averaging defined. <br><br>
in which the original reading name can be replaced by the value of attribute "readingNameMap".
The prefix is made up of the creation function and the aggregation. <br> If none or the option <b>display</b> is specified, the results are only displayed. With
The timestamp of the new stored readings is deviated from aggregation period, the options <b>writeToDB</b> or <b>writeToDBSingle</b> the calculation results are written
unless no unique point of time of the result can be determined. with a new reading name into the database. <br>
The field "EVENT" will be filled with "calculated".<br><br> <b>writeToDB</b> writes one value each at the beginning and end of an evaluation period.
<b>writeToDBSingle</b> writes only one value at the end of an evaluation period. <br><br>
The new reading name is formed from a prefix and the original reading name,
where the original reading name can be replaced by the attribute "readingNameMap".
The prefix consists of the educational function and the aggregation. <br>
The timestamp of the new readings in the database is determined by the set aggregation period
if no clear time of the result can be determined.
The field "EVENT" is filled with "calculated". <br><br>
<ul> <ul>
<b>Example of building a new reading name from the original reading "totalpac":</b> <br> <b>Example of building a new reading name from the original reading "totalpac":</b> <br>
@ -11937,19 +12011,27 @@ return;
</li> </li>
<li><b> delEntries </b> - deletes all database entries or only the database entries specified by <a href="#DbRepattr">attributes</a> Device and/or <li><b> delEntries [&lt;no&gt;[:&lt;nn&gt;]] </b> - deletes all database entries or only the database entries specified by
Reading and the entered time period between "timestamp_begin", "timestamp_end" (if set) or "timeDiffToNow/timeOlderThan". <br><br> <a href="#DbRepattr">attributes</a> device and/or reading. <br><br>
The time limits are considered according to the available time.*-attributes: <br><br>
<ul> <ul>
"timestamp_begin" is set <b>-&gt;</b> deletes db entries <b>from</b> this timestamp until current date/time <br> "timestamp_begin" is set <b>-&gt;</b> deletes db entries <b>from</b> this timestamp until current date/time <br>
"timestamp_end" is set <b>-&gt;</b> deletes db entries <b>until</b> this timestamp <br> "timestamp_end" is set <b>-&gt;</b> deletes db entries <b>until</b> this timestamp <br>
both Timestamps are set <b>-&gt;</b> deletes db entries <b>between</b> these timestamps <br> both Timestamps are set <b>-&gt;</b> deletes db entries <b>between</b> these timestamps <br>
"timeOlderThan" is set <b>-&gt;</b> delete entries <b>older</b> than current time minus "timeOlderThan" <br> "timeOlderThan" is set <b>-&gt;</b> delete entries <b>older</b> than current time minus "timeOlderThan" <br>
"timeDiffToNow" is set <b>-&gt;</b> delete db entries <b>from</b> current time minus "timeDiffToNow" until now <br> "timeDiffToNow" is set <b>-&gt;</b> delete db entries <b>from</b> current time minus "timeDiffToNow" until now <br>
</ul>
<br> <br>
Due to security reasons the attribute <a href="#DbRepattr">attribute</a> "allowDeletion" needs to be set to unlock the Due to security reasons the attribute <a href="#allowDeletion">allowDeletion</a> needs to be set to unlock the
delete-function. <br> delete-function. <br>
Time limits (days) can be specified as an option. In this case, any time.*-attributes set are
overmodulated.
Records older than <b>&lt;no&gt;</b> days and (optionally) newer than
<b>&lt;nn&gt;</b> days are considered.
<br><br>
The relevant attributes to control function changeValue delEntries are: <br><br> The relevant attributes to control function changeValue delEntries are: <br><br>
@ -11969,7 +12051,6 @@ return;
</li> </li>
<br> <br>
</ul>
<li><b> delSeqDoublets [adviceRemain | adviceDelete | delete]</b> - show respectively delete identical sequentially datasets. <li><b> delSeqDoublets [adviceRemain | adviceDelete | delete]</b> - show respectively delete identical sequentially datasets.
Therefore Device,Reading and Value of the sequentially datasets are compared. Therefore Device,Reading and Value of the sequentially datasets are compared.
@ -12664,88 +12745,90 @@ return;
is operating in asynchronous mode to avoid FHEMWEB from blocking. <br><br> is operating in asynchronous mode to avoid FHEMWEB from blocking. <br><br>
</li> <br> </li> <br>
<li><b> reduceLog [average[=day]] </b> <br> <li><b> reduceLog [&lt;no&gt;[:&lt;nn&gt;]] [average[=day]] [EXCLUDE=device1:reading1,device2:reading2,...] [INCLUDE=device:reading] </b> <br>
Reduces historical records within the limits given by the "time.*"-attributes to one record Reduces historical data sets. <br><br>
(the 1st) each hour per device & reading.
At least one of the "time.*"-attributes must be set (pls. see table below). <b>Method without option specification</b> <br><br>
Each of missing time limit will be calculated by the module itself in this case.
If no options are specified, the data will be stored within the time specified by the <b>time.*</b> attributes.
Time limits reduced to one entry (the first) per hour per Device & Reading.
At least one of the <b>time.*</b> attributes must be set (see table below).
In this case, the missing time limit is calculated by the module.
<br><br> <br><br>
With the optional argument 'average' not only the records will be reduced, but all numerical With the attributes <b>device</b> and <b>reading</b> the data records to be considered can be included
values of an hour will be reduced to a single average. or be excluded. Both restrictions reduce the selected data and reduce the
With option 'average=day' all numerical values of a day are reduced to a single average resource requirements.
(implies 'average'). <br><br> The read "reduceLogState" contains the execution result of the last reduceLog command. <br><br>
By setting attributes "device" and/or "reading" the records to be considered could be included Taking the above into account, the following attributes are relevant for this function: <br><br>
respectively excluded. Both containments delimit the selected data from database and may
reduce the system RAM load.
The reading "reduceLogState" contains the result of the last executed reducelog run. <br><br>
The relevant attributes for this function are: <br><br> <ul>
<table>
<colgroup> <col width=5%> <col width=95%> </colgroup>
<tr><td> <b>executeBeforeProc</b> </td><td>: execution of FHEM command (or Perl-routine) before reducelog </td></tr>
<tr><td> <b>executeAfterProc</b> </td><td>: execution of FHEM command (or Perl-routine) after reducelog </td></tr>
<tr><td> <b>device</b> </td><td>: include or exclude &lt;device&gt; for selection </td></tr>
<tr><td> <b>reading</b> </td><td>: include or exclude &lt;reading&gt; for selection </td></tr>
<tr><td> <b>timeOlderThan</b> </td><td>: records <b>older</b> than this attribute will be reduced </td></tr>
<tr><td> <b>timestamp_end</b> </td><td>: records <b>older</b> than this attribute will be reduced </td></tr>
<tr><td> <b>timeDiffToNow</b> </td><td>: records <b>newer</b> than this attribute will be reduced </td></tr>
<tr><td> <b>timestamp_begin</b> </td><td>: records <b>newer</b> than this attribute will be reduced </td></tr>
<tr><td> <b>valueFilter</b> </td><td>: an additional REGEXP to control the record selection. The REGEXP is applied to the database field 'VALUE'. </td></tr>
</table>
</ul>
<br>
<ul>
<table>
<colgroup> <col width=5%> <col width=95%> </colgroup>
<tr><td> <b>executeBeforeProc</b> </td><td>: execution of FHEM command (or Perl-routine) before reducelog </td></tr>
<tr><td> <b>executeAfterProc</b> </td><td>: execution of FHEM command (or Perl-routine) after reducelog </td></tr>
<tr><td> <b>device</b> </td><td>: include or exclude &lt;device&gt; for selection </td></tr>
<tr><td> <b>reading</b> </td><td>: include or exclude &lt;reading&gt; for selection </td></tr>
<tr><td> <b>timeOlderThan</b> </td><td>: records <b>older</b> than this attribute will be reduced </td></tr>
<tr><td> <b>timestamp_end</b> </td><td>: records <b>older</b> than this attribute will be reduced </td></tr>
<tr><td> <b>timeDiffToNow</b> </td><td>: records <b>newer</b> than this attribute will be reduced </td></tr>
<tr><td> <b>timestamp_begin</b> </td><td>: records <b>newer</b> than this attribute will be reduced </td></tr>
<tr><td> <b>valueFilter</b> </td><td>: an additional REGEXP to control the record selection. The REGEXP is applied to the database field 'VALUE'. </td></tr>
</table>
</ul>
<br>
For compatibility reason the reducelog command can optionally be completed with supplements "EXCLUDE"
respectively "INCLUDE" to exclude and/or include device/reading combinations from reducelog: <br><br>
<ul>
"reduceLog [average[=day]] [EXCLUDE=device1:reading1,device2:reading2,...] [INCLUDE=device:reading]" <br><br>
</ul>
This declaration is evaluated as Regex and overwrites the settings made by attributes "device" and "reading",
which won't be considered in this case. <br><br>
<ul>
<b>Examples: </b><br><br> <b>Examples: </b><br><br>
<ul>
attr &lt;name&gt; timeOlderThan = d:200 <br> attr &lt;name&gt; timeOlderThan = d:200 <br>
set &lt;name&gt; reduceLog <br> set &lt;name&gt; reduceLog <br>
# Records older than 200 days are reduced to the first entry per hour of each device and reading. # Records older than 200 days are written to the first entry per hour per Device & Reading. <br>
<br>
<br> <br>
attr &lt;name&gt; timeDiffToNow = d:200 <br> attr &lt;name&gt; timeDiffToNow = d:200 <br>
set &lt;name&gt; reduceLog average=day <br> set &lt;name&gt; reduceLog average=day <br>
# Records newer than 200 days are reduced to a single average a day of each device and reading. # Records newer than 200 days are limited to one entry per day per Device & Reading. <br>
<br>
<br> <br>
attr &lt;name&gt; timeDiffToNow = d:30 <br> attr &lt;name&gt; timeDiffToNow = d:30 <br>
attr &lt;name&gt; device = TYPE=SONOSPLAYER EXCLUDE=Sonos_Kueche <br> attr &lt;name&gt; device = TYPE=SONOSPLAYER EXCLUDE=Sonos_Kueche <br>
attr &lt;name&gt; reading = room% EXCLUDE=roomNameAlias <br> attr &lt;name&gt; reading = room% EXCLUDE=roomNameAlias <br>
set &lt;name&gt; reduceLog <br> set &lt;name&gt; reduceLog <br>
# Records newer than 30 days which are contain devices from type SONOSPLAYER # Records newer than 30 days that are devices of type SONOSPLAYER
(except device "Sonos_Kueche"), the readings beginning with "room" (except "roomNameAlias"), (except Device "Sonos_Kitchen") and the readings start with "room" (except "roomNameAlias")
are reduced to the first entry per hour of each device and reading. <br> are reduced to the first entry per hour per Device & Reading. <br>
<br> <br>
attr &lt;name&gt; timeDiffToNow = d:10 <br> attr &lt;name&gt; timeDiffToNow = d:10 <br>
attr &lt;name&gt; timeOlderThan = d:5 <br> attr &lt;name&gt; timeOlderThan = d:5 <br>
attr &lt;name&gt; device = Luftdaten_remote <br> attr &lt;name&gt; device = Luftdaten_remote <br>
set &lt;name&gt; reduceLog average <br> set &lt;name&gt; reduceLog average <br>
# Records older than 5 and newer than 10 days and contain DEVICE "Luftdaten_remote" are # Records older than 5 and newer than 10 days and containing DEVICE "Luftdaten_remote
revised. Numerical values are reduced to the first entry per hour of each device and reading are adjusted. Numerical values of an hour are reduced to an average value <br>
<br>
<br> <br>
</ul> </ul>
<br>
<b>Method with option specification</b> <br><br>
Records older than <b>&lt;no&gt;</b> days and (optionally) newer than
<b>&lt;nn&gt;</b> days are considered.
Specifying <b>average</b> not only cleans up the database, but also
all numerical values of an hour are reduced to a single mean value.
With the option <b>average=day</b> all numerical values of a day are reduced to a single
Average value reduced (implies 'average'). <br><br>
The additions "EXCLUDE" and "INCLUDE" can be added to exclude device/reading combinations of reduceLog
or to include them. This specification is evaluated as regex and overwrites the setting of the attributes "device".
and "reading", which are not considered in this case. <br><br>
<b>Note:</b> <br> <b>Note:</b> <br>
Even though the function itself is non-blocking, you have to set the attached DbLog device into Although the function itself is designed non-blocking, the assigned DbLog device should
the asynchronous mode (attr asyncMode = 1) to avoid a blocking situation of FHEM !. <br> operated in asynchronous mode to avoid blocking of FHEMWEB (table lock). <br>
It is strongly recommended to check if the default INDEX 'Search_Idx' exists on the table It is also strongly recommended to use the standard INDEX 'Search_Idx' in the table 'history'.
'history'! <br><br> to put on ! <br>
The processing of this command may take an extremely long time (without INDEX). <br><br>
</li> <br> </li> <br>
<li><b> repairSQLite </b> - repairs a corrupted SQLite database. <br> <li><b> repairSQLite </b> - repairs a corrupted SQLite database. <br>
@ -12919,10 +13002,24 @@ return;
<li><b> sqlSpecial </b> - This function provides a drop-down list with a selection of prepared reportings. <br> <li><b> sqlSpecial </b> - This function provides a drop-down list with a selection of prepared reportings. <br>
The statements result is depicted in reading "SqlResult". The statements result is depicted in reading "SqlResult".
The result can be formatted by <a href="#DbRepattr">attribute</a> "sqlResultFormat", The result can be formatted by attribute <a href="#sqlResultFormat">sqlResultFormat</a>
a well as the used field separator by <a href="#DbRepattr">attribute</a> "sqlResultFieldSep". a well as the used field separator by attribute <a href="#sqlResultFieldSep">sqlResultFieldSep</a>.
<br><br> <br><br>
The following predefined reportings are selectable: <br><br>
<ul>
<table>
<colgroup> <col width=5%> <col width=95%> </colgroup>
<tr><td> <b>50mostFreqLogsLast2days </b> </td><td>: reports the 50 most occuring log entries of the last 2 days </td></tr>
<tr><td> <b>allDevCount </b> </td><td>: all devices occuring in database and their quantity </td></tr>
<tr><td> <b>allDevReadCount </b> </td><td>: all device/reading combinations occuring in database and their quantity </td></tr>
<tr><td> <b>recentReadingsOfDevice </b> </td><td>: determines the newest records of a device available in the database. The
device must be defined in attribute <a href="#reading">reading</a>.
Only <b>one</b> device to be evaluated can be specified.. </td></tr>
</table>
</ul>
<br>
The relevant attributes for this function are: <br><br> The relevant attributes for this function are: <br><br>
<ul> <ul>
<table> <table>
@ -12933,36 +13030,29 @@ return;
<tr><td> <b>sqlResultFieldSep</b> </td><td>: determines the used field separator in statement result </td></tr> <tr><td> <b>sqlResultFieldSep</b> </td><td>: determines the used field separator in statement result </td></tr>
</table> </table>
</ul> </ul>
<br>
The following predefined reportings are selectable: <br><br>
<ul>
<table>
<colgroup> <col width=5%> <col width=95%> </colgroup>
<tr><td> <b>50mostFreqLogsLast2days </b> </td><td>: reports the 50 most occuring log entries of the last 2 days </td></tr>
<tr><td> <b>allDevCount </b> </td><td>: all devices occuring in database and their quantity </td></tr>
<tr><td> <b>allDevReadCount </b> </td><td>: all device/reading combinations occuring in database and their quantity </td></tr>
</table>
</ul>
</li><br><br> </li><br><br>
<li><b> sumValue [display | writeToDB]</b> <li><b> sumValue [display | writeToDB | writeToDBSingle] </b>
- calculates the summary of database column "VALUE" between period given by - Calculates the total values of the database field "VALUE" in the time limits
<a href="#DbRepattr">attributes</a> "timestamp_begin", "timestamp_end" or of the possible time.*-attributes. <br><br>
"timeDiffToNow / timeOlderThan". The reading to evaluate must be defined using attribute
"reading". Using this function is mostly reasonable if value-differences of readings
are written to the database. <br>
Is no or the option "display" specified, the results are only displayed. Using The reading to be evaluated must be specified in the attribute <a href="#reading">reading</a>.
option "writeToDB" the calculation results are stored in the database with a new reading This function is useful if continuous value differences of a reading are written
name. <br> into the database. <br><br>
The new readingname is built of a prefix and the original reading name,
in which the original reading name can be replaced by the value of attribute "readingNameMap". If none or the option <b>display</b> is specified, the results are only displayed. With
The prefix is made up of the creation function and the aggregation. <br> the options <b>writeToDB</b> or <b>writeToDBSingle</b> the calculation results are written
The timestamp of the new stored readings is deviated from aggregation period, with a new reading name into the database. <br>
unless no unique point of time of the result can be determined. <b>writeToDB</b> writes one value each at the beginning and end of an evaluation period.
The field "EVENT" will be filled with "calculated".<br><br> <b>writeToDBSingle</b> writes only one value at the end of an evaluation period. <br><br>
The new reading name is formed from a prefix and the original reading name,
where the original reading name can be replaced by the attribute "readingNameMap".
The prefix consists of the educational function and the aggregation. <br>
The timestamp of the new reading in the database is determined by the set aggregation period
if no clear time of the result can be determined.
The field "EVENT" is filled with "calculated". <br><br>
<ul> <ul>
<b>Example of building a new reading name from the original reading "totalpac":</b> <br> <b>Example of building a new reading name from the original reading "totalpac":</b> <br>
@ -14234,15 +14324,21 @@ sub bdump {
</li> <br> </li> <br>
<li><b> averageValue [display | writeToDB]</b> <li><b> averageValue [display | writeToDB | writeToDBSingle]</b>
- berechnet einen Durchschnittswert des Datenbankfelds "VALUE" in den - berechnet einen Durchschnittswert des Datenbankfelds "VALUE" in den Zeitgrenzen
gegebenen Zeitgrenzen ( siehe <a href="#DbRepattr">Attribute</a>). der möglichen time.*-Attribute. <br><br>
Es muss das auszuwertende Reading über das <a href="#DbRepattr">Attribut</a> "reading"
angegeben sein. <br> Es muss das auszuwertende Reading im Attribut <a href="#reading">reading</a>
Mit dem Attribut "averageCalcForm" wird die Berechnungsvariante zur Mittelwertermittlung definiert. angegeben sein.
Ist keine oder die Option "display" angegeben, werden die Ergebnisse nur angezeigt. Mit Mit dem Attribut <a href="#averageCalcForm">averageCalcForm</a> wird die Berechnungsvariante zur
der Option "writeToDB" werden die Berechnungsergebnisse mit einem neuen Readingnamen Mittelwertermittlung definiert. <br><br>
in der Datenbank gespeichert. <br>
Ist keine oder die Option <b>display</b> angegeben, werden die Ergebnisse nur angezeigt. Mit
den Optionen <b>writeToDB</b> bzw. <b>writeToDBSingle</b> werden die Berechnungsergebnisse
mit einem neuen Readingnamen in der Datenbank gespeichert. <br>
<b>writeToDB</b> schreibt jeweils einen Wert am Anfang und am Ende einer Auswertungsperiode.
<b>writeToDBSingle</b> schreibt nur einen Wert am Ende einer Auswertungsperiode. <br><br>
Der neue Readingname wird aus einem Präfix und dem originalen Readingnamen gebildet, Der neue Readingname wird aus einem Präfix und dem originalen Readingnamen gebildet,
wobei der originale Readingname durch das Attribut "readingNameMap" ersetzt werden kann. wobei der originale Readingname durch das Attribut "readingNameMap" ersetzt werden kann.
Der Präfix setzt sich aus der Bildungsfunktion und der Aggregation zusammen. <br> Der Präfix setzt sich aus der Bildungsfunktion und der Aggregation zusammen. <br>
@ -14435,7 +14531,7 @@ sub bdump {
</li> </li>
<li><b> delEntries </b> - löscht alle oder die durch die <a href="#DbRepattr">Attribute</a> device und/oder <li><b> delEntries [&lt;no&gt;[:&lt;nn&gt;]] </b> - löscht alle oder die durch die <a href="#DbRepattr">Attribute</a> device und/oder
reading definierten Datenbankeinträge. Die Eingrenzung über Timestamps erfolgt reading definierten Datenbankeinträge. Die Eingrenzung über Timestamps erfolgt
folgendermaßen: <br><br> folgendermaßen: <br><br>
@ -14448,8 +14544,13 @@ sub bdump {
</ul> </ul>
<br> <br>
Aus Sicherheitsgründen muss das <a href="#DbRepattr">Attribut</a> "allowDeletion" Aus Sicherheitsgründen muss das Attribut <a href="#allowDeletion">allowDeletion</a>
gesetzt sein um die Löschfunktion freizuschalten. <br><br> gesetzt sein um die Löschfunktion freizuschalten. <br>
Zeitgrenzen (Tage) können als Option angegeben werden. In diesem Fall werden eventuell gesetzte Zeitattribute
übersteuert.
Es werden Datensätze berücksichtigt die älter sind als <b>&lt;no&gt;</b> Tage und (optional) neuer sind als
<b>&lt;nn&gt;</b> Tage.
<br><br>
Die zur Steuerung von delEntries relevanten Attribute: <br><br> Die zur Steuerung von delEntries relevanten Attribute: <br><br>
@ -15181,24 +15282,23 @@ sub bdump {
im asynchronen Modus betrieben werden um ein Blockieren von FHEMWEB zu vermeiden (Tabellen-Lock). <br><br> im asynchronen Modus betrieben werden um ein Blockieren von FHEMWEB zu vermeiden (Tabellen-Lock). <br><br>
</li> <br> </li> <br>
<li><b> reduceLog [average[=day]] </b> <br> <li><b> reduceLog [&lt;no&gt;[:&lt;nn&gt;]] [average[=day]] [EXCLUDE=device1:reading1,device2:reading2,...] [INCLUDE=device:reading] </b> <br>
Reduziert historische Datensätze innerhalb der durch die "time.*"-Attribute bestimmten Reduziert historische Datensätze. <br><br>
Zeitgrenzen auf einen Eintrag (den ersten) pro Stunde je Device & Reading.
Es muss mindestens eines der "time.*"-Attribute gesetzt sein (siehe Tabelle unten). <b>Arbeitsweise ohne Optionsangabe</b> <br><br>
Sind keine Optionen angegeben, werden die Daten innerhalb der durch die <b>time.*</b>-Attribute bestimmten
Zeitgrenzen auf einen Eintrag (den ersten) pro Stunde je Device & Reading reduziert.
Es muss mindestens eines der <b>time.*</b>-Attribute gesetzt sein (siehe Tabelle unten).
Die jeweils fehlende Zeitabgrenzung wird in diesem Fall durch das Modul errechnet. Die jeweils fehlende Zeitabgrenzung wird in diesem Fall durch das Modul errechnet.
<br><br> <br><br>
Durch die optionale Angabe von 'average' wird nicht nur die Datenbank bereinigt, sondern Mit den Attributen <b>device</b> und <b>reading</b> können die zu berücksichtigenden Datensätze eingeschlossen
alle numerischen Werte einer Stunde werden auf einen einzigen Mittelwert reduziert.
Mit der Option 'average=day' werden alle numerischen Werte eines Tages auf einen einzigen
Mittelwert reduziert (impliziert 'average'). <br><br>
Mit den Attributen "device" und "reading" können die zu berücksichtigenden Datensätze eingeschlossen
bzw. ausgeschlossen werden. Beide Eingrenzungen reduzieren die selektierten Daten und verringern den bzw. ausgeschlossen werden. Beide Eingrenzungen reduzieren die selektierten Daten und verringern den
Ressourcenbedarf. Ressourcenbedarf.
Das Reading "reduceLogState" enthält das Ausführungsergebnis des letzten reduceLog-Befehls. <br><br> Das Reading "reduceLogState" enthält das Ausführungsergebnis des letzten reduceLog-Befehls. <br><br>
Die für diese Funktion relevanten Attribute sind: <br><br> Unter Berücksichtigung des oben genannten sind für diese Funktion folgende Attribute relevant: <br><br>
<ul> <ul>
<table> <table>
@ -15216,17 +15316,8 @@ sub bdump {
</ul> </ul>
<br> <br>
Aus Kompatibilitätsgründen kann der Set-Befehl optional durch die Zusätze "EXCLUDE" bzw. "INCLUDE"
direkt ergänzt werden um device/reading Kombinationen von reduceLog auszuschließen bzw. einzuschließen: <br><br>
<ul>
"reduceLog [average[=day]] [EXCLUDE=device1:reading1,device2:reading2,...] [INCLUDE=device:reading]" <br><br>
</ul>
Diese Angabe wird als Regex ausgewertet und überschreibt die Einstellung der Attribute "device" und "reading",
die in diesem Fall nicht beachtet werden. <br><br>
<ul>
<b>Beispiele: </b><br><br> <b>Beispiele: </b><br><br>
<ul>
attr &lt;name&gt; timeOlderThan = d:200 <br> attr &lt;name&gt; timeOlderThan = d:200 <br>
set &lt;name&gt; reduceLog <br> set &lt;name&gt; reduceLog <br>
# Datensätze die älter als 200 Tage sind, werden auf den ersten Eintrag pro Stunde je Device & Reading # Datensätze die älter als 200 Tage sind, werden auf den ersten Eintrag pro Stunde je Device & Reading
@ -15256,6 +15347,20 @@ sub bdump {
werden bereinigt. Numerische Werte einer Stunde werden auf einen Mittelwert reduziert <br> werden bereinigt. Numerische Werte einer Stunde werden auf einen Mittelwert reduziert <br>
<br> <br>
</ul> </ul>
<br>
<b>Arbeitsweise mit Optionsangabe</b> <br><br>
Es werden Datensätze berücksichtigt die älter sind als <b>&lt;no&gt;</b> Tage und (optional) neuer sind als
<b>&lt;nn&gt;</b> Tage.
Durch die Angabe von <b>average</b> wird nicht nur die Datenbank bereinigt, sondern
alle numerischen Werte einer Stunde werden auf einen einzigen Mittelwert reduziert.
Mit der Option <b>average=day</b> werden alle numerischen Werte eines Tages auf einen einzigen
Mittelwert reduziert (impliziert 'average'). <br><br>
Die Zusätze "EXCLUDE" bzw. "INCLUDE" können ergänzt werden um device/reading Kombinationen von reduceLog auszuschließen
bzw. einzuschließen. Diese Angabe wird als Regex ausgewertet und überschreibt die Einstellung der Attribute "device"
und "reading", die in diesem Fall nicht beachtet werden. <br><br>
<b>Hinweis:</b> <br> <b>Hinweis:</b> <br>
Obwohl die Funktion selbst non-blocking ausgelegt ist, sollte das zugeordnete DbLog-Device Obwohl die Funktion selbst non-blocking ausgelegt ist, sollte das zugeordnete DbLog-Device
@ -15443,9 +15548,23 @@ sub bdump {
<li><b> sqlSpecial </b> - Die Funktion bietet eine Drop-Downliste mit einer Auswahl vorbereiter Auswertungen <li><b> sqlSpecial </b> - Die Funktion bietet eine Drop-Downliste mit einer Auswahl vorbereiter Auswertungen
an. <br> an. <br>
Das Ergebnis des Statements wird im Reading "SqlResult" dargestellt. Das Ergebnis des Statements wird im Reading "SqlResult" dargestellt.
Die Ergebnis-Formatierung kann durch das <a href="#DbRepattr">Attribut</a> "sqlResultFormat" Die Ergebnis-Formatierung kann durch das Attribut <a href="#sqlResultFormat">sqlResultFormat</a>
ausgewählt, sowie der verwendete Feldtrenner durch das <a href="#DbRepattr">Attribut</a> ausgewählt, sowie der verwendete Feldtrenner durch das Attribut <a href="#sqlResultFieldSep">sqlResultFieldSep</a>
"sqlResultFieldSep" festgelegt werden. <br><br> festgelegt werden. <br><br>
Es sind die folgenden vordefinierte Auswertungen auswählbar: <br><br>
<ul>
<table>
<colgroup> <col width=5%> <col width=95%> </colgroup>
<tr><td> <b>50mostFreqLogsLast2days </b> </td><td>: ermittelt die 50 am häufigsten vorkommenden Loggingeinträge der letzten 2 Tage </td></tr>
<tr><td> <b>allDevCount </b> </td><td>: alle in der Datenbank vorkommenden Devices und deren Anzahl </td></tr>
<tr><td> <b>allDevReadCount </b> </td><td>: alle in der Datenbank vorkommenden Device/Reading-Kombinationen und deren Anzahl</td></tr>
<tr><td> <b>recentReadingsOfDevice </b> </td><td>: ermittelt die neuesten in der Datenbank vorhandenen Datensätze eines Devices. Das auszuwertende
Device muß im Attribut <a href="#reading">reading</a> definiert sein.
Es kann nur <b>ein</b> auszuwertendes Device angegeben werden. </td></tr>
</table>
</ul>
<br>
Die für diese Funktion relevanten Attribute sind: <br><br> Die für diese Funktion relevanten Attribute sind: <br><br>
<ul> <ul>
@ -15457,30 +15576,23 @@ sub bdump {
<tr><td> <b>sqlResultFieldSep</b> </td><td>: Auswahl des Trennzeichens zwischen Ergebnisfeldern </td></tr> <tr><td> <b>sqlResultFieldSep</b> </td><td>: Auswahl des Trennzeichens zwischen Ergebnisfeldern </td></tr>
</table> </table>
</ul> </ul>
<br>
Es sind die folgenden vordefinierte Auswertungen auswählbar: <br><br>
<ul>
<table>
<colgroup> <col width=5%> <col width=95%> </colgroup>
<tr><td> <b>50mostFreqLogsLast2days </b> </td><td>: ermittelt die 50 am häufigsten vorkommenden Loggingeinträge der letzten 2 Tage </td></tr>
<tr><td> <b>allDevCount </b> </td><td>: alle in der Datenbank vorkommenden Devices und deren Anzahl </td></tr>
<tr><td> <b>allDevReadCount </b> </td><td>: alle in der Datenbank vorkommenden Device/Reading-Kombinationen und deren Anzahl</td></tr>
</table>
</ul>
</li><br><br> </li><br><br>
<li><b> sumValue [display | writeToDB]</b> <li><b> sumValue [display | writeToDB | writeToDBSingle]</b>
- Berechnet die Summenwerte des Datenbankfelds "VALUE" in den Zeitgrenzen - Berechnet die Summenwerte des Datenbankfelds "VALUE" in den Zeitgrenzen
(Attribute) "timestamp_begin", "timestamp_end" bzw. "timeDiffToNow / timeOlderThan". der möglichen time.*-Attribute. <br><br>
Es muss das auszuwertende Reading im <a href="#DbRepattr">Attribut</a> "reading"
angegeben sein. Diese Funktion ist sinnvoll wenn fortlaufend Wertedifferenzen eines Es muss das auszuwertende Reading im Attribut <a href="#reading">reading</a>
Readings in die Datenbank geschrieben werden. <br> angegeben sein. Diese Funktion ist sinnvoll wenn fortlaufend Wertedifferenzen eines
Readings in die Datenbank geschrieben werden. <br><br>
Ist keine oder die Option <b>display</b> angegeben, werden die Ergebnisse nur angezeigt. Mit
den Optionen <b>writeToDB</b> bzw. <b>writeToDBSingle</b> werden die Berechnungsergebnisse
mit einem neuen Readingnamen in der Datenbank gespeichert. <br>
<b>writeToDB</b> schreibt jeweils einen Wert am Anfang und am Ende einer Auswertungsperiode.
<b>writeToDBSingle</b> schreibt nur einen Wert am Ende einer Auswertungsperiode. <br><br>
Ist keine oder die Option "display" angegeben, werden die Ergebnisse nur angezeigt. Mit
der Option "writeToDB" werden die Berechnungsergebnisse mit einem neuen Readingnamen
in der Datenbank gespeichert. <br>
Der neue Readingname wird aus einem Präfix und dem originalen Readingnamen gebildet, Der neue Readingname wird aus einem Präfix und dem originalen Readingnamen gebildet,
wobei der originale Readingname durch das Attribut "readingNameMap" ersetzt werden kann. wobei der originale Readingname durch das Attribut "readingNameMap" ersetzt werden kann.
Der Präfix setzt sich aus der Bildungsfunktion und der Aggregation zusammen. <br> Der Präfix setzt sich aus der Bildungsfunktion und der Aggregation zusammen. <br>

View File

@ -587,8 +587,7 @@ sub DbRep_Set($@) {
if ($opt =~ /eraseReadings/) { if ($opt =~ /eraseReadings/) {
$hash->{LASTCMD} = $prop?"$opt $prop":"$opt"; $hash->{LASTCMD} = $prop?"$opt $prop":"$opt";
# Readings löschen die nicht in der Ausnahmeliste (Attr readingPreventFromDel) stehen DbRep_delread($hash); # Readings löschen die nicht in der Ausnahmeliste (Attr readingPreventFromDel) stehen
DbRep_delread($hash);
return undef; return undef;
} }
@ -603,7 +602,6 @@ sub DbRep_Set($@) {
Log3 ($name, 3, "DbRep $name - ### New database clientSide dump ###"); Log3 ($name, 3, "DbRep $name - ### New database clientSide dump ###");
Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ################################################################");
} }
# Befehl vor Procedure ausführen
DbRep_beforeproc($hash, "dump"); DbRep_beforeproc($hash, "dump");
DbRep_Main($hash,$opt,$prop); DbRep_Main($hash,$opt,$prop);
return undef; return undef;
@ -614,7 +612,6 @@ sub DbRep_Set($@) {
Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ################################################################");
Log3 ($name, 3, "DbRep $name - ### New SQLite dump ###"); Log3 ($name, 3, "DbRep $name - ### New SQLite dump ###");
Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ################################################################");
# Befehl vor Procedure ausführen
DbRep_beforeproc($hash, "dump"); DbRep_beforeproc($hash, "dump");
DbRep_Main($hash,$opt,$prop); DbRep_Main($hash,$opt,$prop);
return undef; return undef;
@ -624,7 +621,6 @@ sub DbRep_Set($@) {
$prop = $prop?$prop:36000; $prop = $prop?$prop:36000;
if($prop) { if($prop) {
unless($prop =~ /^(\d+)$/) { return " The Value of $opt is not valid. Use only figures 0-9 without decimal places !";}; unless($prop =~ /^(\d+)$/) { return " The Value of $opt is not valid. Use only figures 0-9 without decimal places !";};
# unless ($aVal =~ /^[0-9]+$/) { return " The Value of $aName is not valid. Use only figures 0-9 without decimal places !";}
} }
$hash->{LASTCMD} = $prop?"$opt $prop":"$opt"; $hash->{LASTCMD} = $prop?"$opt $prop":"$opt";
Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ################################################################");
@ -635,7 +631,6 @@ sub DbRep_Set($@) {
my $dbl = $dbloghash->{NAME}; my $dbl = $dbloghash->{NAME};
CommandSet(undef,"$dbl reopen $prop"); CommandSet(undef,"$dbl reopen $prop");
# Befehl vor Procedure ausführen
DbRep_beforeproc($hash, "repair"); DbRep_beforeproc($hash, "repair");
DbRep_Main($hash,$opt); DbRep_Main($hash,$opt);
return undef; return undef;
@ -646,7 +641,6 @@ sub DbRep_Set($@) {
Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ################################################################");
Log3 ($name, 3, "DbRep $name - ### New database Restore/Recovery ###"); Log3 ($name, 3, "DbRep $name - ### New database Restore/Recovery ###");
Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ################################################################");
# Befehl vor Procedure ausführen
DbRep_beforeproc($hash, "restore"); DbRep_beforeproc($hash, "restore");
DbRep_Main($hash,$opt,$prop); DbRep_Main($hash,$opt,$prop);
return undef; return undef;
@ -657,7 +651,6 @@ sub DbRep_Set($@) {
Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ################################################################");
Log3 ($name, 3, "DbRep $name - ### New optimize table / vacuum execution ###"); Log3 ($name, 3, "DbRep $name - ### New optimize table / vacuum execution ###");
Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ################################################################");
# Befehl vor Procedure ausführen
DbRep_beforeproc($hash, "optimize"); DbRep_beforeproc($hash, "optimize");
DbRep_Main($hash,$opt); DbRep_Main($hash,$opt);
return undef; return undef;
@ -685,7 +678,6 @@ sub DbRep_Set($@) {
Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ################################################################");
Log3 ($name, 3, "DbRep $name - ### new reduceLog run ###"); Log3 ($name, 3, "DbRep $name - ### new reduceLog run ###");
Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ################################################################");
# Befehl vor Procedure ausführen
DbRep_beforeproc($hash, "reduceLog"); DbRep_beforeproc($hash, "reduceLog");
DbRep_Main($hash,$opt); DbRep_Main($hash,$opt);
return undef; return undef;
@ -811,7 +803,7 @@ sub DbRep_Set($@) {
} elsif ($opt eq "deviceRename") { } elsif ($opt eq "deviceRename") {
shift @a; shift @a;
shift @a; shift @a;
$prop = join(" ",@a); # Device Name kann Leerzeichen enthalten $prop = join(" ",@a); # Device Name kann Leerzeichen enthalten
my ($olddev, $newdev) = split(",",$prop); my ($olddev, $newdev) = split(",",$prop);
$hash->{LASTCMD} = $prop?"$opt $prop":"$opt"; $hash->{LASTCMD} = $prop?"$opt $prop":"$opt";
if (!$olddev || !$newdev) {return "Both entries \"old device name\", \"new device name\" are needed. Use \"set $name deviceRename olddevname,newdevname\" ";} if (!$olddev || !$newdev) {return "Both entries \"old device name\", \"new device name\" are needed. Use \"set $name deviceRename olddevname,newdevname\" ";}
@ -823,7 +815,7 @@ sub DbRep_Set($@) {
} elsif ($opt eq "readingRename") { } elsif ($opt eq "readingRename") {
shift @a; shift @a;
shift @a; shift @a;
$prop = join(" ",@a); # Readingname kann Leerzeichen enthalten $prop = join(" ",@a); # Readingname kann Leerzeichen enthalten
$hash->{LASTCMD} = $prop?"$opt $prop":"$opt"; $hash->{LASTCMD} = $prop?"$opt $prop":"$opt";
my ($oldread, $newread) = split(",",$prop); my ($oldread, $newread) = split(",",$prop);
if (!$oldread || !$newread) {return "Both entries \"old reading name\", \"new reading name\" are needed. Use \"set $name readingRename oldreadingname,newreadingname\" ";} if (!$oldread || !$newread) {return "Both entries \"old reading name\", \"new reading name\" are needed. Use \"set $name readingRename oldreadingname,newreadingname\" ";}
@ -902,7 +894,6 @@ sub DbRep_Set($@) {
} elsif ($opt =~ /sqlCmd|sqlSpecial|sqlCmdHistory/) { } elsif ($opt =~ /sqlCmd|sqlSpecial|sqlCmdHistory/) {
return "\"set $opt\" needs at least an argument" if ( @a < 3 ); return "\"set $opt\" needs at least an argument" if ( @a < 3 );
# remove arg 0, 1 to get SQL command
my $sqlcmd; my $sqlcmd;
if($opt eq "sqlSpecial") { if($opt eq "sqlSpecial") {
$sqlcmd = $prop; $sqlcmd = $prop;
@ -911,7 +902,9 @@ sub DbRep_Set($@) {
my @cmd = @a; my @cmd = @a;
shift @cmd; shift @cmd; shift @cmd; shift @cmd;
$sqlcmd = join(" ", @cmd); $sqlcmd = join(" ", @cmd);
$sqlcmd =~ tr/ A-Za-z0-9!"#$§%&'()*+,-.\/:;<=>?@[\\]^_`{|}~äöüÄÖÜ߀/ /cs; @cmd = split(/\s/, $sqlcmd);
$sqlcmd = join(" ", @cmd);
# $sqlcmd =~ tr/ A-Za-z0-9!"#$§%&'()*+,-.\/:;<=>?@[\\]^_`{|}~äöüÄÖÜ߀/ /cs; # V8.36.0 20.03.2020
} }
if($opt eq "sqlCmdHistory") { if($opt eq "sqlCmdHistory") {
$prop =~ tr/ A-Za-z0-9!"#$%&'()*+,-.\/:;<=>?@[\\]^_`{|}~äöüÄÖÜ߀/ /cs; $prop =~ tr/ A-Za-z0-9!"#$%&'()*+,-.\/:;<=>?@[\\]^_`{|}~äöüÄÖÜ߀/ /cs;
@ -1047,7 +1040,6 @@ sub DbRep_Get($@) {
} elsif ($opt =~ /dbValue/) { } elsif ($opt =~ /dbValue/) {
return "get \"$opt\" needs at least an argument" if ( @a < 3 ); return "get \"$opt\" needs at least an argument" if ( @a < 3 );
# remove arg 0, 1 to get SQL command
my @cmd = @a; my @cmd = @a;
shift @cmd; shift @cmd; shift @cmd; shift @cmd;
my $sqlcmd = join(" ",@cmd); my $sqlcmd = join(" ",@cmd);