2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-03 16:56:54 +00:00

93_DbRep: contrib 8.50.3

git-svn-id: https://svn.fhem.de/fhem/trunk@26426 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
nasseeder1 2022-09-19 14:48:38 +00:00
parent 941cabca36
commit 6ce7fcf1de

View File

@ -1,5 +1,5 @@
##########################################################################################################
# $Id: 93_DbRep.pm 26283 2022-08-03 19:58:49Z DS_Starter $
# $Id: 93_DbRep.pm 26413 2022-09-17 18:08:05Z DS_Starter $
##########################################################################################################
# 93_DbRep.pm
#
@ -57,6 +57,9 @@ no if $] >= 5.017011, warnings => 'experimental::smartmatch';
# Version History intern
my %DbRep_vNotesIntern = (
"8.50.3" => "19.09.2022 reduce memory allocation of function DbRep_reduceLog ",
"8.50.2" => "17.09.2022 release setter 'index' for device model 'Agent' ",
"8.50.1" => "05.09.2022 DbRep_setLastCmd, change changeValue syntax, minor fixes ",
"8.50.0" => "20.08.2022 rework of DbRep_reduceLog - add max, max=day, min, min=day, sum, sum=day ",
"8.49.1" => "03.08.2022 fix DbRep_deleteOtherFromDB, Forum: https://forum.fhem.de/index.php/topic,128605.0.html ".
"some code changes and bug fixes ",
@ -602,6 +605,7 @@ sub DbRep_Set {
my $setlist = "Unknown argument $opt, choose one of ".
"eraseReadings:noArg ".
"deviceRename ".
"index:".$indl." ".
(($hash->{ROLE} ne "Agent") ? "delDoublets:adviceDelete,delete " : "").
(($hash->{ROLE} ne "Agent") ? "delEntries " : "").
(($hash->{ROLE} ne "Agent") ? "changeValue " : "").
@ -622,7 +626,6 @@ sub DbRep_Set {
(($hash->{ROLE} ne "Agent") ? "tableCurrentFillup:noArg " : "").
(($hash->{ROLE} ne "Agent") ? "tableCurrentPurge:noArg " : "").
(($hash->{ROLE} ne "Agent") ? "countEntries:history,current " : "").
(($hash->{ROLE} ne "Agent") ? "index:".$indl." " : "").
(($hash->{ROLE} ne "Agent") ? "sumValue:display,writeToDB,writeToDBSingle,writeToDBInTime " : "").
(($hash->{ROLE} ne "Agent") ? "averageValue:display,writeToDB,writeToDBSingle,writeToDBInTime " : "").
(($hash->{ROLE} ne "Agent") ? "delSeqDoublets:adviceRemain,adviceDelete,delete " : "").
@ -638,13 +641,14 @@ sub DbRep_Set {
return if(IsDisabled($name));
if ($opt =~ /eraseReadings/) {
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt";
DbRep_delread($hash); # Readings löschen die nicht in der Ausnahmeliste (Attr readingPreventFromDel) stehen
DbRep_setLastCmd (@a);
DbRep_delread ($hash); # Readings löschen die nicht in der Ausnahmeliste (Attr readingPreventFromDel) stehen
return;
}
if ($opt eq "dumpMySQL" && $hash->{ROLE} ne "Agent") {
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt";
DbRep_setLastCmd (@a);
if ($prop eq "serverSide") {
Log3 ($name, 3, "DbRep $name - ################################################################");
Log3 ($name, 3, "DbRep $name - ### New database serverSide dump ###");
@ -656,31 +660,34 @@ sub DbRep_Set {
Log3 ($name, 3, "DbRep $name - ################################################################");
}
DbRep_beforeproc($hash, "dump");
DbRep_Main($hash, $opt, $prop);
DbRep_beforeproc ($hash, "dump");
DbRep_Main ($hash, $opt, $prop);
return;
}
if ($opt eq "dumpSQLite" && $hash->{ROLE} ne "Agent") {
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt";
DbRep_setLastCmd (@a);
Log3 ($name, 3, "DbRep $name - ################################################################");
Log3 ($name, 3, "DbRep $name - ### New SQLite dump ###");
Log3 ($name, 3, "DbRep $name - ################################################################");
DbRep_beforeproc($hash, "dump");
DbRep_Main($hash, $opt, $prop);
DbRep_beforeproc ($hash, "dump");
DbRep_Main ($hash, $opt, $prop);
return;
}
if ($opt eq "repairSQLite" && $hash->{ROLE} ne "Agent") {
$prop = $prop ? $prop : 36000;
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 !";
}
$hash->{LASTCMD} = $prop?"$opt $prop":"$opt";
DbRep_setLastCmd ($name, $opt, $prop);
Log3 ($name, 3, "DbRep $name - ################################################################");
Log3 ($name, 3, "DbRep $name - ### New SQLite repair attempt ###");
Log3 ($name, 3, "DbRep $name - ################################################################");
@ -700,7 +707,7 @@ sub DbRep_Set {
return qq{The command "$opt" needs an argument.};
}
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt";
DbRep_setLastCmd (@a);
Log3 ($name, 3, "DbRep $name - ################################################################");
Log3 ($name, 3, "DbRep $name - ### New database Restore/Recovery ###");
@ -713,7 +720,7 @@ sub DbRep_Set {
}
if ($opt =~ /optimizeTables|vacuum/ && $hash->{ROLE} ne "Agent") {
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt";
DbRep_setLastCmd (@a);
Log3 ($name, 3, "DbRep $name - ################################################################");
Log3 ($name, 3, "DbRep $name - ### New optimize table / vacuum execution ###");
@ -733,7 +740,7 @@ sub DbRep_Set {
$prop //= "adviceDelete";
}
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt";
DbRep_setLastCmd ($name, $opt, $prop);
if ($prop =~ /delete/ && !AttrVal($hash->{NAME}, "allowDeletion", 0)) {
return " Set attribute 'allowDeletion' if you want to allow deletion of any database entries. Use it with care !";
@ -745,14 +752,13 @@ sub DbRep_Set {
}
if ($opt =~ m/reduceLog/ && $hash->{ROLE} ne "Agent") {
if ($hash->{HELPER}{RUNNING_REDUCELOG} && $hash->{HELPER}{RUNNING_REDUCELOG}{pid} !~ m/DEAD/) {
if ($hash->{HELPER}{$dbrep_hmainf{reduceLog}{pk}} && $hash->{HELPER}{$dbrep_hmainf{reduceLog}{pk}}{pid} !~ m/DEAD/) {
return "reduceLog already in progress. Please wait for the current process to finish.";
}
else {
delete $hash->{HELPER}{RUNNING_REDUCELOG};
my @b = @a;
shift(@b);
$hash->{LASTCMD} = join(" ",@b);
delete $hash->{HELPER}{$dbrep_hmainf{reduceLog}{pk}};
DbRep_setLastCmd (@a);
$hash->{HELPER}{REDUCELOG} = \@a;
Log3 ($name, 3, "DbRep $name - ################################################################");
@ -781,39 +787,43 @@ sub DbRep_Set {
}
if ($opt eq "cancelDump" && $hash->{ROLE} ne "Agent") {
$hash->{LASTCMD} = $prop?"$opt $prop":"$opt";
DbRep_setLastCmd (@a);
BlockingKill($hash->{HELPER}{RUNNING_BACKUP_CLIENT});
Log3 ($name, 3, "DbRep $name -> running Dump has been canceled");
ReadingsSingleUpdateValue ($hash, "state", "Dump canceled", 1);
return;
}
if ($opt eq "cancelRepair" && $hash->{ROLE} ne "Agent") {
$hash->{LASTCMD} = $prop?"$opt $prop":"$opt";
DbRep_setLastCmd (@a);
BlockingKill($hash->{HELPER}{RUNNING_REPAIR});
Log3 ($name, 3, "DbRep $name -> running Repair has been canceled");
ReadingsSingleUpdateValue ($hash, "state", "Repair canceled", 1);
return;
}
if ($opt eq "cancelRestore" && $hash->{ROLE} ne "Agent") {
$hash->{LASTCMD} = $prop?"$opt $prop":"$opt";
DbRep_setLastCmd (@a);
BlockingKill($hash->{HELPER}{RUNNING_RESTORE});
Log3 ($name, 3, "DbRep $name -> running Restore has been canceled");
ReadingsSingleUpdateValue ($hash, "state", "Restore canceled", 1);
return;
}
if ($opt =~ m/tableCurrentFillup/ && $hash->{ROLE} ne "Agent") {
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt";
DbRep_Main($hash, $opt);
DbRep_setLastCmd (@a);
DbRep_Main ($hash, $opt);
return;
}
if ($opt eq "index" && $hash->{ROLE} ne "Agent") {
$hash->{LASTCMD} = $prop ? "$opt $prop " : "$opt";
if ($opt eq "index") {
DbRep_setLastCmd (@a);
Log3 ($name, 3, "DbRep $name - ################################################################");
Log3 ($name, 3, "DbRep $name - ### New Index operation ###");
Log3 ($name, 3, "DbRep $name - ################################################################");
@ -837,17 +847,17 @@ sub DbRep_Set {
}
if ($opt =~ /countEntries/ && $hash->{ROLE} ne "Agent") {
$hash->{LASTCMD} = $prop ? "$opt $prop " : "$opt";
my $table = $prop // "history";
DbRep_setLastCmd ($name, $opt, $table);
DbRep_Main ($hash, $opt, $table);
return;
}
elsif ($opt =~ /fetchrows/ && $hash->{ROLE} ne "Agent") {
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt";
my $table = $prop // "history";
DbRep_setLastCmd ($name, $opt, $table);
DbRep_Main ($hash, $opt, $table);
return;
@ -865,13 +875,14 @@ sub DbRep_Set {
#######################################################################################################
if ($opt =~ m/(max|min|sum|average|diff)Value/ && $hash->{ROLE} ne "Agent") {
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt";
if (!AttrVal($hash->{NAME}, "reading", "")) {
return " The attribute reading to analyze is not set !";
}
if ($prop && $prop =~ /deleteOther/ && !AttrVal($hash->{NAME}, "allowDeletion", 0)) {
return " Set attribute 'allowDeletion' if you want to allow deletion of any database entries. Use it with care !";
}
if ($prop && $prop =~ /writeToDB/) {
if (!AttrVal($hash->{NAME}, "device", "") || AttrVal($hash->{NAME}, "device", "") =~ /[%*:=,]/ || AttrVal($hash->{NAME}, "reading", "") =~ /[,\s]/) {
return "<html>If you want write results back to database, attributes \"device\" and \"reading\" must be set.<br>
@ -880,6 +891,7 @@ sub DbRep_Set {
}
}
DbRep_setLastCmd (@a);
DbRep_Main ($hash,$opt,$prop);
}
elsif ($opt =~ m/delEntries|tableCurrentPurge/ && $hash->{ROLE} ne "Agent") {
@ -887,8 +899,8 @@ sub DbRep_Set {
return " Set attribute 'allowDeletion' if you want to allow deletion of any database entries. Use it with care !";
}
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt";
delete $hash->{HELPER}{DELENTRIES};
DbRep_setLastCmd (@a);
shift @a;
shift @a;
@ -901,27 +913,31 @@ sub DbRep_Set {
shift @a;
$prop = join " ", @a; # Device Name kann Leerzeichen enthalten
my ($olddev, $newdev) = split ",", $prop;
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt";
if (!$olddev || !$newdev) {return qq{Both entries "old device name" and "new device name" are needed. Use "set $name deviceRename olddevname,newdevname"};}
if (!$olddev || !$newdev) {
return qq{Both entries "old device name" and "new device name" are needed. Use "set $name deviceRename olddevname,newdevname"};
}
$hash->{HELPER}{OLDDEV} = $olddev;
$hash->{HELPER}{NEWDEV} = $newdev;
DbRep_setLastCmd ($name, $opt, "$olddev,$newdev");
DbRep_Main ($hash, $opt, $prop);
}
elsif ($opt eq "readingRename") {
shift @a;
shift @a;
$prop = join " ", @a; # Readingname kann Leerzeichen enthalten
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt";
my ($oldread, $newread) = split ",", $prop;
if (!$oldread || !$newread) {return qq{Both entries "old reading name" and "new reading name" are needed. Use "set $name readingRename oldreadingname,newreadingname"};}
if (!$oldread || !$newread) {
return qq{Both entries "old reading name" and "new reading name" are needed. Use "set $name readingRename oldreadingname,newreadingname"};
}
$hash->{HELPER}{OLDREAD} = $oldread;
$hash->{HELPER}{NEWREAD} = $newread;
DbRep_setLastCmd ($name, $opt, "$oldread,$newread");
DbRep_Main ($hash, $opt, $prop);
}
elsif ($opt eq "insert" && $hash->{ROLE} ne "Agent") {
@ -930,7 +946,7 @@ sub DbRep_Set {
$prop = join " ", @a;
if (!$prop) {
return qq{Data to insert to table 'history' are needed like this pattern: 'Date,Time,Value,[Unit]'. "Unit" is optional. Spaces are not allowed !};
return qq{Data to insert to table 'history' are needed like this pattern: 'Date,Time,Value,[Unit],[<Device>],[<Reading>]'. Parameters included in "[...]" are optional. Spaces are not allowed !};
}
my ($i_date, $i_time, $i_value, $i_unit, $i_device, $i_reading) = split ",", $prop;
@ -939,7 +955,7 @@ sub DbRep_Set {
$i_reading //= AttrVal($name, "reading", ""); # Reading aus Attr lesen wenn nicht im insert angegeben
if (!$i_date || !$i_time || !defined $i_value) {
return qq{At least data for "Date", "Time" and "Value" is needed to insert. "Unit" is optional. Inputformat is "YYYY-MM-DD,HH:MM:SS,<Value>,<Unit>"};
return qq{At least data for "Date", "Time" and "Value" is needed to insert. Inputformat is "YYYY-MM-DD,HH:MM:SS,<Value>,<Unit>"};
}
if ($i_date !~ /^(\d{4})-(\d{2})-(\d{2})$/x || $i_time !~ /^(\d{2}):(\d{2}):(\d{2})$/x) {
@ -959,20 +975,17 @@ sub DbRep_Set {
my ($yyyy, $mm, $dd, $hh, $min, $sec) = ($i_timestamp =~ /(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)/);
eval { my $ts = timelocal($sec, $min, $hh, $dd, $mm-1, $yyyy-1900); };
if ($@) {
my @l = split (/at/, $@);
return " Timestamp is out of range - $l[0]";
}
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt";
DbRep_setLastCmd ($name, $opt, $prop);
DbRep_Main ($hash, $opt, "$i_timestamp,$i_device,$i_reading,$i_value,$i_unit");
}
elsif ($opt eq "exportToFile" && $hash->{ROLE} ne "Agent") {
$prop // push @a, AttrVal($name, "expimpfile", "");
my ($ar, $hr) = parseParams(join ' ', @a);
my $f = $ar->[2] // "";
my $f = $ar->[2] // AttrVal($name, "expimpfile", "");
if (!$f) {
return qq{"$opt" needs a file as argument or the attribute "expimpfile" (path and filename) to be set !};
@ -985,22 +998,19 @@ sub DbRep_Set {
}
$prop = $e ? $f." ".$e : $f;
$hash->{LASTCMD} = $opt.' '.$prop;
DbRep_setLastCmd ($name, $opt, $prop);
DbRep_Main ($hash, $opt, $prop);
}
elsif ($opt eq "importFromFile" && $hash->{ROLE} ne "Agent") {
$prop // push @a, AttrVal($name, "expimpfile", "");
my ($ar, $hr) = parseParams(join ' ', @a);
my $f = $ar->[2] // "";
my $f = $ar->[2] // AttrVal($name, "expimpfile", "");
if (!$f) {
return qq{"$opt" needs a file as an argument or the attribute "expimpfile" (path and filename) to be set !};
}
$hash->{LASTCMD} = $opt.' '.$f;
DbRep_setLastCmd ($name, $opt, $f);
DbRep_Main ($hash, $opt, $f);
}
elsif ($opt =~ /sqlCmd|sqlSpecial|sqlCmdHistory/) {
@ -1119,45 +1129,37 @@ sub DbRep_Set {
}
}
$hash->{LASTCMD} = $sqlcmd ? "$opt $sqlcmd" : "$opt";
#$hash->{LASTCMD} = $sqlcmd ? "$opt $sqlcmd" : "$opt";
if ($sqlcmd =~ m/^\s*delete/is && !AttrVal($hash->{NAME}, "allowDeletion", undef)) {
return "Attribute 'allowDeletion = 1' is needed for command '$sqlcmd'. Use it with care !";
}
DbRep_setLastCmd ($name, $opt, $sqlcmd);
DbRep_Main ($hash, $opt, $sqlcmd);
}
elsif ($opt =~ /changeValue/) {
shift @a;
shift @a;
$prop = join(" ", @a);
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt";
my ($ac, $hc) = parseParams(join ' ', @a);
unless($prop =~ m/^\s*(".*",".*")\s*$/) {return "Both entries \"old string\", \"new string\" are needed. Use \"set $name changeValue \"old string\",\"new string\" (use quotes)";}
my $oldval = $hc->{old};
my $newval = $hc->{new};
if (!$oldval || !$newval) {
return qq{Both entries old="old string" new="new string" are needed.};
}
my $complex = 0;
my ($oldval,$newval) = ($prop =~ /^\s*"(.*?)","(.*?)"\s*$/);
if($newval =~ m/[{}]/) {
if($newval =~ m/^\s*(\{.*\})\s*$/s) {
if($newval =~ m/^\s*\{"(.*)"\}\s*$/s) {
$newval = $1;
$complex = 1;
}
my %specials = (
"%VALUE" => $name,
"%UNIT" => $name,
);
$newval = EvalSpecials($newval, %specials);
}
else {
return "The expression of \"new string\" has to be included in \"{ }\" ";
}
}
$hash->{HELPER}{COMPLEX} = $complex;
$hash->{HELPER}{OLDVAL} = $oldval;
$hash->{HELPER}{NEWVAL} = $newval;
DbRep_setLastCmd ($name, $opt, "old=$oldval new=$newval");
DbRep_Main ($hash, $opt, $prop);
}
elsif ($opt =~ m/syncStandby/ && $hash->{ROLE} ne "Agent") {
@ -1166,10 +1168,10 @@ sub DbRep_Set {
}
if(!exists($defs{$prop}) || $defs{$prop}->{TYPE} ne "DbLog") {
return "The device \"$prop\" doesn't exist or is not a DbLog-device. ";
return qq{The device "$prop" doesn't exist or is not a DbLog-device.};
}
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt";
DbRep_setLastCmd (@a);
DbRep_Main ($hash, $opt, $prop);
}
else {
@ -1225,21 +1227,19 @@ sub DbRep_Get {
return "Dump is running - try again later !" if($hash->{HELPER}{RUNNING_BACKUP_CLIENT});
return "The operation \"$opt\" isn't available with database type $dbmodel" if($dbmodel ne 'MYSQL');
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt";
DbRep_setLastCmd (@a);
DbRep_Main ($hash, $opt, $prop);
}
elsif ($opt eq "svrinfo") {
return "Dump is running - try again later !" if($hash->{HELPER}{RUNNING_BACKUP_CLIENT});
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt";
DbRep_setLastCmd (@a);
DbRep_Main ($hash, $opt, $prop);
}
elsif ($opt eq "blockinginfo") {
return "Dump is running - try again later !" if($hash->{HELPER}{RUNNING_BACKUP_CLIENT});
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt";
DbRep_setLastCmd (@a);
DbRep_delread ($hash);
ReadingsSingleUpdateValue ($hash, "state", "running", 1);
DbRep_getblockinginfo ($hash);
@ -1247,9 +1247,9 @@ sub DbRep_Get {
elsif ($opt eq "minTimestamp" || $opt eq "initData") {
return "Dump is running - try again later !" if($hash->{HELPER}{RUNNING_BACKUP_CLIENT});
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt";
$hash->{HELPER}{IDRETRIES} = 3; # Anzahl wie oft versucht wird initiale Daten zu holen
DbRep_setLastCmd (@a);
DbRep_delread ($hash);
ReadingsSingleUpdateValue ($hash, "state", "running", 1);
@ -1268,7 +1268,8 @@ sub DbRep_Get {
my $sqlcmd = join " ", @cmd;
$sqlcmd =~ tr/ A-Za-z0-9!"#$§%&'()*+,-.\/:;<=>?@[\\]^_`{|}~äöüÄÖÜ߀/ /cs;
$hash->{LASTCMD} = $sqlcmd ? "$opt $sqlcmd" : "$opt";
DbRep_setLastCmd ($name, $opt, $sqlcmd);
if ($sqlcmd =~ m/^\s*delete/is && !AttrVal($name, "allowDeletion", undef)) {
return "Attribute 'allowDeletion = 1' is needed for command '$sqlcmd'. Use it with care !";
@ -1899,7 +1900,8 @@ sub DbRep_firstconnect {
my $fadef = $hash->{MODEL} eq "Client" ? 1 : 0; # fastStart default immer 1 für Clients (0 für Agenten)
if (AttrVal($name, "fastStart", $fadef) && $prop eq "onBoot" ) {
$hash->{LASTCMD} = "initial database connect stopped due to attribute 'fastStart'";
DbRep_setLastCmd ($name, "initial database connect stopped due to attribute 'fastStart'");
return;
}
@ -2382,9 +2384,11 @@ sub DbRep_Main {
return;
}
if (exists($hash->{HELPER}{RUNNING_PID}) && $hash->{ROLE} ne "Agent") {
Log3 ($name, 3, "DbRep $name - WARNING - running process $hash->{HELPER}{RUNNING_PID}{pid} will be killed now to start a new operation");
BlockingKill($hash->{HELPER}{RUNNING_PID});
## eventuell bereits laufenden BlockingCall beenden
#####################################################
if (exists($hash->{HELPER}{$dbrep_hmainf{$opt}{pk}}) && $hash->{ROLE} ne "Agent") {
Log3 ($name, 3, "DbRep $name - WARNING - running process $hash->{HELPER}{$dbrep_hmainf{$opt}{pk}}{pid} will be killed now to start a new operation");
BlockingKill($hash->{HELPER}{$dbrep_hmainf{$opt}{pk}});
}
# initiale Datenermittlung wie minimal Timestamp, Datenbankstrukturen, ...
@ -4539,7 +4543,7 @@ sub DbRep_diffvalDone {
ReadingsBulkUpdateValue ($hash, "db_lines_processed", $irowdone) if($hash->{LASTCMD} =~ /writeToDB/);
ReadingsBulkUpdateValue ($hash, "diff_overrun_limit_".$difflimit, $rowsrej) if($rowsrej);
ReadingsBulkUpdateValue ($hash, "less_data_in_period", $ncpstr) if($ncpstr);
ReadingsBulkUpdateTimeState ($hash,$brt,$rt,($ncpstr||$rowsrej)?"Warning":$state);
ReadingsBulkUpdateTimeState ($hash,$brt,$rt,($ncpstr||$rowsrej) ? "Warning" : $state);
readingsEndUpdate ($hash, 1);
@ -5206,7 +5210,7 @@ sub DbRep_changeVal {
my $db = $hash->{DATABASE};
my $complex = $hash->{HELPER}{COMPLEX}; # einfache oder komplexe Werteersetzung
my ($sql,$urow,$sth,$old,$new);
my ($sql,$urow,$sth);
my $bst = [gettimeofday]; # Background-Startzeit
@ -5222,10 +5226,10 @@ sub DbRep_changeVal {
$err = DbRep_beginDatabaseTransaction ($name, $dbh);
return "$name|$err" if ($err);
if (!$complex) {
$old = delete $hash->{HELPER}{OLDVAL};
$new = delete $hash->{HELPER}{NEWVAL};
my $old = delete $hash->{HELPER}{OLDVAL};
my $new = delete $hash->{HELPER}{NEWVAL};
if (!$complex) {
Log3 ($name, 5, qq{DbRep $name -> Change old value "$old" to new value "$new" in database $db});
$old =~ s/'/''/g; # escape ' with ''
@ -5271,8 +5275,6 @@ sub DbRep_changeVal {
$urow = $sth->rows;
}
else {
$old = delete $hash->{HELPER}{OLDVAL};
$new = delete $hash->{HELPER}{NEWVAL};
$old =~ s/'/''/g; # escape ' with ''
my @tsa = split("\\|", $ts); # Timestampstring to Array
@ -5312,6 +5314,7 @@ sub DbRep_changeVal {
my $oval = $value; # Selektkriterium für Update alter Valuewert
my $VALUE = $value;
my $UNIT = $unit;
eval $new;
if ($@) {
$err = encode_base64($@,"");
@ -8942,7 +8945,6 @@ sub DbRep_reduceLog {
my $ots = $paref->{rsn} // "";
my @a = @{$hash->{HELPER}{REDUCELOG}};
my $rlpar = join " ", @a;
my $err = q{};
@ -9115,7 +9117,7 @@ sub DbRep_reduceLog {
$err = _DbRep_rl_deleteDayRows ($params);
return "$name|$err" if ($err);
@dayRows = ();
undef @dayRows;
}
if ($mode =~ /average|max|min|sum/i) {
@ -9138,7 +9140,7 @@ sub DbRep_reduceLog {
$err = _DbRep_rl_updateHour ($params);
return "$name|$err" if ($err);
@updateHour = ();
undef @updateHour;
}
if ($mode =~ /=day/i && scalar @updateDay) {
@ -9163,9 +9165,9 @@ sub DbRep_reduceLog {
}
%hourlyKnown = ();
@updateHour = ();
@updateDay = ();
undef %hourlyKnown;
undef @updateHour;
undef @updateDay;
$currentHour = 99;
}
@ -9180,7 +9182,7 @@ sub DbRep_reduceLog {
push(@updateHour, {%hourlyKnown});
}
%hourlyKnown = ();
undef %hourlyKnown;
$currentHour = $hour;
}
@ -12971,6 +12973,19 @@ sub DbRep_removeLeadingZero {
return $val;
}
################################################################
# setzt Internal LASTCMD
################################################################
sub DbRep_setLastCmd {
my (@vars) = @_;
my $name = shift(@vars);
my $hash = $defs{$name};
$hash->{LASTCMD} = join(" ",@vars);
return;
}
################################################################
# Werte aus BlockingCall heraus setzen
# Erwartete Liste:
@ -13062,12 +13077,12 @@ sub DbRep_setVersionInfo {
if($modules{$type}{META}{x_prereqs_src} && !$hash->{HELPER}{MODMETAABSENT}) {
# META-Daten sind vorhanden
$modules{$type}{META}{version} = "v".$v; # Version aus META.json überschreiben, Anzeige mit {Dumper $modules{SMAPortal}{META}}
if($modules{$type}{META}{x_version}) { # {x_version} ( nur gesetzt wenn $Id: 93_DbRep.pm 26283 2022-08-03 19:58:49Z DS_Starter $ im Kopf komplett! vorhanden )
if($modules{$type}{META}{x_version}) { # {x_version} ( nur gesetzt wenn $Id: 93_DbRep.pm 26413 2022-09-17 18:08:05Z DS_Starter $ im Kopf komplett! vorhanden )
$modules{$type}{META}{x_version} =~ s/1.1.1/$v/g;
} else {
$modules{$type}{META}{x_version} = $v;
}
return $@ unless (FHEM::Meta::SetInternals($hash)); # FVERSION wird gesetzt ( nur gesetzt wenn $Id: 93_DbRep.pm 26283 2022-08-03 19:58:49Z DS_Starter $ im Kopf komplett! vorhanden )
return $@ unless (FHEM::Meta::SetInternals($hash)); # FVERSION wird gesetzt ( nur gesetzt wenn $Id: 93_DbRep.pm 26413 2022-09-17 18:08:05Z DS_Starter $ im Kopf komplett! vorhanden )
if(__PACKAGE__ eq "FHEM::$type" || __PACKAGE__ eq $type) {
# es wird mit Packages gearbeitet -> Perl übliche Modulversion setzen
# mit {<Modul>->VERSION()} im FHEMWEB kann Modulversion abgefragt werden
@ -13656,32 +13671,30 @@ return;
<li><b> cancelDump </b> - stops a running database dump. </li> <br>
<li><b> changeValue </b> - changes the saved value of readings.
If the selection is limited to particular device/reading-combinations by
<a href="#DbRepattr">attribute</a> "device" respectively "reading", it is considered as well
as possibly defined time limits by time attributes (time.*). <br>
If no limits are set, the whole database is scanned and the specified value will be
changed. <br><br>
<li><b> changeValue </b> - changes the stored value of a reading.
If the selection is limited to certain device/reading combinations by the attributes
<a href="#DbRepattr">attribute</a> "device" or "reading", they are taken into account
in the same way as set time limits (attributes time.*). <br>
If these constraints are missing, the entire database is searched and the specified value is
is changed. <br><br>
<ul>
<b>Syntax: </b> <br>
set &lt;name&gt; changeValue "&lt;old string&gt;","&lt;new string&gt;" <br><br>
set &lt;name&gt; changeValue old="&lt;old string&gt;" new="&lt;new string&gt;" <br><br>
The strings have to be quoted and separated by comma.
A "string" can be: <br>
The "string" can be: <br>
<table>
<colgroup> <col width=15%> <col width=85%> </colgroup>
<tr><td><b>&lt;old string&gt; :</b> </td><td><li>a simple string with/without spaces, e.g. "OL 12" </li>
<li>a string with usage of SQL-wildcard, e.g. "%OL%" </li> </td></tr>
<colgroup> <col width=20%> <col width=80%> </colgroup>
<tr><td><b>&lt;old String&gt; :</b> </td><td><li>a simple string with/without spaces, e.g. "OL 12" </li> </td></tr>
<tr><td> </td><td><li>a string with use of SQL wildcard, e.g. "%OL%" </li> </td></tr>
<tr><td> </td><td> </td></tr>
<tr><td> </td><td> </td></tr>
<tr><td><b>&lt;new string&gt; :</b> </td><td><li>a simple string with/without spaces, e.g. "12 kWh" </li>
<li>Perl code embedded in "{}" with quotes, e.g. "{($VALUE,$UNIT) = split(" ",$VALUE)}".
The perl expression the variables $VALUE and $UNIT are committed to.
The variables are changable within the perl code. The returned value
of VALUE and UNIT are saved into the database field VALUE respectively
UNIT of the dataset. </li></td></tr>
<tr><td><b>&lt;new String&gt; :</b> </td><td><li>a simple string with/without spaces, e.g. "12 kWh" </li> </td></tr>
<tr><td> </td><td><li>Perl code enclosed in {"..."} including quotes, e.g. {"($VALUE,$UNIT) = split(" ",$VALUE)"} </td></tr>
<tr><td> </td><td>The variables $VALUE and $UNIT are passed to the Perl expression. They can be changed </td></tr>
<tr><td> </td><td>within the Perl code. The returned value of $VALUE and $UNIT is stored </td></tr>
<tr><td> </td><td>in the VALUE or UNIT field of the record. </li> </td></tr>
</table>
<br>
@ -13692,10 +13705,10 @@ return;
set &lt;name&gt; changeValue "%OL%","12 OL" <br>
# contains the field VALUE the substring "OL", it is changed to "12 OL". <br><br>
set &lt;name&gt; changeValue "12 kWh","{($VALUE,$UNIT) = split(" ",$VALUE)}" <br>
set &lt;name&gt; changeValue "12 kWh",{"($VALUE,$UNIT) = split(" ",$VALUE)"} <br>
# the old field value "12 kWh" is splitted to VALUE=12 and UNIT=kWh and saved into the database fields <br><br>
set &lt;name&gt; changeValue "24%","{$VALUE = (split(" ",$VALUE))[0]}" <br>
set &lt;name&gt; changeValue "24%",{"$VALUE = (split(" ",$VALUE))[0]"} <br>
# if the old field value begins with "24", it is splitted and VALUE=24 is saved (e.g. "24 kWh")
<br><br>
@ -14610,23 +14623,37 @@ return;
</li>
<li><b> reduceLog [&lt;no&gt;[:&lt;nn&gt;]] [average[=day]] [EXCLUDE=device1:reading1,device2:reading2,...] [INCLUDE=device:reading] </b> <br>
<li><b> reduceLog [&lt;no&gt;[:&lt;nn&gt;]] [mode] [EXCLUDE=device1:reading1,device2:reading2,...] [INCLUDE=device:reading] </b> <br>
Reduces historical data sets. <br><br>
<b>Method without option specification</b> <br><br>
<b>Operation without specifying command line operators</b> <br><br>
The data within the time limits defined by the <b>time.*</b> attributes will be
reduced to one entry (the first) per hour per device & reading.
The data is cleaned within the time limits defined by the <b>time.*</b>-attributes.
At least one of the <b>time.*</b> attributes must be set (see table below).
The FullDay option (full days are always selected) is used implicitly.
The respective missing time delimitation is calculated by the module in this case.
The respective missing time accrual is determined by the module in this case. <br>
The working mode is determined by the optional specification of <b>mode</b>:
<br><br>
By optionally specifying <b>average</b>, not only the database will be cleaned up, but
all numeric values of an hour are reduced to a single average value.
With the option <b>average=day</b>, all numeric values of a day are reduced to a single
average value (implies 'average').
<br><br>
<ul>
<table>
<colgroup> <col width=23%> <col width=77%> </colgroup>
<tr><td> <b>without specification of mode</b> </td><td>:&nbsp;the data is reduced to the first entry per hour per device & reading </td></tr>
<tr><td> <b>average</b> </td><td>:&nbsp;numerical values are reduced to an average value per hour per device & reading, otherwise as without mode </td></tr>
<tr><td> <b>average=day</b> </td><td>:&nbsp;numeric values are reduced to one mean value per day per device & reading, otherwise as without mode </td></tr>
<tr><td> </td><td>&nbsp;&nbsp;The FullDay option (full days are always selected) is used implicitly. </td></tr>
<tr><td> <b>max</b> </td><td>:&nbsp;numeric values are reduced to the maximum value per hour per device & reading, otherwise as without mode </td></tr>
<tr><td> <b>max=day</b> </td><td>:&nbsp;numeric values are reduced to the maximum value per day per device & reading, otherwise as without mode </td></tr>
<tr><td> </td><td>&nbsp;&nbsp;The FullDay option (full days are always selected) is used implicitly. </td></tr>
<tr><td> <b>min</b> </td><td>:&nbsp;numeric values are reduced to the minimum value per hour per device & reading, otherwise as without mode </td></tr>
<tr><td> <b>min=day</b> </td><td>:&nbsp;numeric values are reduced to the minimum value per day per device & reading, otherwise as without mode </td></tr>
<tr><td> </td><td>&nbsp;&nbsp;The FullDay option (full days are always selected) is used implicitly. </td></tr>
<tr><td> <b>sum</b> </td><td>:&nbsp;numeric values are reduced to the sum per hour per Device & Reading, otherwise as without mode </td></tr>
<tr><td> <b>sum=day</b> </td><td>:&nbsp;numeric values are reduced to the sum per day per Device & Reading, otherwise as without mode </td></tr>
<tr><td> </td><td>&nbsp;&nbsp;The FullDay option (full days are always selected) is used implicitly. </td></tr>
</table>
</ul>
<br>
With the attributes <b>device</b> and <b>reading</b> the data records to be considered can be included
or be excluded. Both restrictions reduce the selected data and reduce the
@ -14683,14 +14710,14 @@ return;
</ul>
<br>
<b>Method with option specification</b> <br><br>
<b>Operation with specification of command line operators</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>
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.
Records are considered that are older than <b>&lt;no&gt;</b> days and (optionally) newer than
<b>&lt;nn&gt;</b> days.
The working mode is determined by the optional specification of <b>mode</b> as described above.
<br><br>
The additions "EXCLUDE" or "INCLUDE" can be added to exclude or include device/reading combinations in reduceLog
and override the "device" and "reading" attributes, which are ignored in this case. <br>
@ -14707,10 +14734,10 @@ return;
<br>
<b>Note:</b> <br>
Although the function itself is designed non-blocking, the assigned DbLog device should
operated in asynchronous mode to avoid blocking of FHEMWEB (table lock). <br>
It is also strongly recommended to use the standard INDEX 'Search_Idx' in the table 'history'.
to put on ! <br>
Although the function itself is designed non-blocking, the assigned DbLog device should be operated in
asynchronous mode to avoid blocking FHEMWEB (table lock). <br>
Furthermore it is strongly recommended to create the standard INDEX 'Search_Idx' in the table
'history' ! <br>
The processing of this command may take an extremely long time (without INDEX). <br><br>
</li> <br>
@ -16461,21 +16488,21 @@ return;
<ul>
<b>Syntax: </b> <br>
set &lt;name&gt; changeValue "&lt;alter String&gt;","&lt;neuer String&gt;" <br><br>
set &lt;name&gt; changeValue old="&lt;alter String&gt;" new="&lt;neuer String&gt;" <br><br>
"String" kann sein: <br>
<table>
<colgroup> <col width=15%> <col width=85%> </colgroup>
<tr><td><b>&lt;alter String&gt; :</b> </td><td><li>ein einfacher String mit/ohne Leerzeichen, z.B. "OL 12" </li>
<li>ein String mit Verwendung von SQL-Wildcard, z.B. "%OL%" </li> </td></tr>
<colgroup> <col width=20%> <col width=80%> </colgroup>
<tr><td><b>&lt;alter String&gt; :</b> </td><td><li>ein einfacher String mit/ohne Leerzeichen, z.B. "OL 12" </li> </td></tr>
<tr><td> </td><td><li>ein String mit Verwendung von SQL-Wildcard, z.B. "%OL%" </li> </td></tr>
<tr><td> </td><td> </td></tr>
<tr><td> </td><td> </td></tr>
<tr><td><b>&lt;neuer String&gt; :</b> </td><td><li>ein einfacher String mit/ohne Leerzeichen, z.B. "12 kWh" </li>
<li>Perl Code eingeschlossen in "{}" inkl. Quotes, z.B. "{($VALUE,$UNIT) = split(" ",$VALUE)}".
Dem Perl-Ausdruck werden die Variablen $VALUE und $UNIT übergeben. Sie können innerhalb
des Perl-Code geändert werden. Der zurückgebene Wert von $VALUE und $UNIT wird in dem Feld
VALUE bzw. UNIT des Datensatzes gespeichert. </li></td></tr>
<tr><td><b>&lt;neuer String&gt; :</b> </td><td><li>ein einfacher String mit/ohne Leerzeichen, z.B. "12 kWh" </li> </td></tr>
<tr><td> </td><td><li>Perl Code eingeschlossen in {"..."} inkl. Quotes, z.B. {"($VALUE,$UNIT) = split(" ",$VALUE)"} </td></tr>
<tr><td> </td><td>Dem Perl-Ausdruck werden die Variablen $VALUE und $UNIT übergeben. Sie können innerhalb </td></tr>
<tr><td> </td><td>des Perl-Code geändert werden. Der zurückgebene Wert von $VALUE und $UNIT wird in dem Feld </td></tr>
<tr><td> </td><td>VALUE bzw. UNIT des Datensatzes gespeichert. </li> </td></tr>
</table>
</ul>
<br>
@ -16488,10 +16515,10 @@ return;
set &lt;name&gt; changeValue "%OL%","12 OL" <br>
# enthält das Feld VALUE den Teilstring "OL", wird es in "12 OL" geändert. <br><br>
set &lt;name&gt; changeValue "12 kWh","{($VALUE,$UNIT) = split(" ",$VALUE)}" <br>
set &lt;name&gt; changeValue "12 kWh",{"($VALUE,$UNIT) = split(" ",$VALUE)"} <br>
# der alte Feldwert "12 kWh" wird in VALUE=12 und UNIT=kWh gesplittet und in den Datenbankfeldern gespeichert <br><br>
set &lt;name&gt; changeValue "24%","{$VALUE = (split(" ",$VALUE))[0]}" <br>
set &lt;name&gt; changeValue "24%",{"$VALUE = (split(" ",$VALUE))[0]"} <br>
# beginnt der alte Feldwert mit "24", wird er gesplittet und VALUE=24 gespeichert (z.B. "24 kWh")
<br><br>