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 # 93_DbRep.pm
# #
@ -57,6 +57,9 @@ no if $] >= 5.017011, warnings => 'experimental::smartmatch';
# Version History intern # Version History intern
my %DbRep_vNotesIntern = ( 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.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 ". "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 ", "some code changes and bug fixes ",
@ -602,6 +605,7 @@ sub DbRep_Set {
my $setlist = "Unknown argument $opt, choose one of ". my $setlist = "Unknown argument $opt, choose one of ".
"eraseReadings:noArg ". "eraseReadings:noArg ".
"deviceRename ". "deviceRename ".
"index:".$indl." ".
(($hash->{ROLE} ne "Agent") ? "delDoublets:adviceDelete,delete " : ""). (($hash->{ROLE} ne "Agent") ? "delDoublets:adviceDelete,delete " : "").
(($hash->{ROLE} ne "Agent") ? "delEntries " : ""). (($hash->{ROLE} ne "Agent") ? "delEntries " : "").
(($hash->{ROLE} ne "Agent") ? "changeValue " : ""). (($hash->{ROLE} ne "Agent") ? "changeValue " : "").
@ -622,7 +626,6 @@ sub DbRep_Set {
(($hash->{ROLE} ne "Agent") ? "tableCurrentFillup:noArg " : ""). (($hash->{ROLE} ne "Agent") ? "tableCurrentFillup:noArg " : "").
(($hash->{ROLE} ne "Agent") ? "tableCurrentPurge:noArg " : ""). (($hash->{ROLE} ne "Agent") ? "tableCurrentPurge:noArg " : "").
(($hash->{ROLE} ne "Agent") ? "countEntries:history,current " : ""). (($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") ? "sumValue:display,writeToDB,writeToDBSingle,writeToDBInTime " : "").
(($hash->{ROLE} ne "Agent") ? "averageValue:display,writeToDB,writeToDBSingle,writeToDBInTime " : ""). (($hash->{ROLE} ne "Agent") ? "averageValue:display,writeToDB,writeToDBSingle,writeToDBInTime " : "").
(($hash->{ROLE} ne "Agent") ? "delSeqDoublets:adviceRemain,adviceDelete,delete " : ""). (($hash->{ROLE} ne "Agent") ? "delSeqDoublets:adviceRemain,adviceDelete,delete " : "").
@ -638,13 +641,14 @@ sub DbRep_Set {
return if(IsDisabled($name)); return if(IsDisabled($name));
if ($opt =~ /eraseReadings/) { if ($opt =~ /eraseReadings/) {
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt"; DbRep_setLastCmd (@a);
DbRep_delread($hash); # 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
return; return;
} }
if ($opt eq "dumpMySQL" && $hash->{ROLE} ne "Agent") { if ($opt eq "dumpMySQL" && $hash->{ROLE} ne "Agent") {
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt"; DbRep_setLastCmd (@a);
if ($prop eq "serverSide") { if ($prop eq "serverSide") {
Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ################################################################");
Log3 ($name, 3, "DbRep $name - ### New database serverSide dump ###"); Log3 ($name, 3, "DbRep $name - ### New database serverSide dump ###");
@ -656,31 +660,34 @@ sub DbRep_Set {
Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ################################################################");
} }
DbRep_beforeproc($hash, "dump"); DbRep_beforeproc ($hash, "dump");
DbRep_Main($hash, $opt, $prop); DbRep_Main ($hash, $opt, $prop);
return; return;
} }
if ($opt eq "dumpSQLite" && $hash->{ROLE} ne "Agent") { 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 - ################################################################");
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 - ################################################################");
DbRep_beforeproc($hash, "dump"); DbRep_beforeproc ($hash, "dump");
DbRep_Main($hash, $opt, $prop); DbRep_Main ($hash, $opt, $prop);
return; return;
} }
if ($opt eq "repairSQLite" && $hash->{ROLE} ne "Agent") { if ($opt eq "repairSQLite" && $hash->{ROLE} ne "Agent") {
$prop = $prop ? $prop : 36000; $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 - ################################################################");
Log3 ($name, 3, "DbRep $name - ### New SQLite repair attempt ###"); Log3 ($name, 3, "DbRep $name - ### New SQLite repair attempt ###");
Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ################################################################");
@ -700,7 +707,7 @@ sub DbRep_Set {
return qq{The command "$opt" needs an argument.}; 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 - ################################################################");
Log3 ($name, 3, "DbRep $name - ### New database Restore/Recovery ###"); Log3 ($name, 3, "DbRep $name - ### New database Restore/Recovery ###");
@ -713,7 +720,7 @@ sub DbRep_Set {
} }
if ($opt =~ /optimizeTables|vacuum/ && $hash->{ROLE} ne "Agent") { 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 - ################################################################");
Log3 ($name, 3, "DbRep $name - ### New optimize table / vacuum execution ###"); Log3 ($name, 3, "DbRep $name - ### New optimize table / vacuum execution ###");
@ -726,14 +733,14 @@ sub DbRep_Set {
} }
if ($opt =~ m/delSeqDoublets|delDoublets/ && $hash->{ROLE} ne "Agent") { if ($opt =~ m/delSeqDoublets|delDoublets/ && $hash->{ROLE} ne "Agent") {
if ($opt eq "delSeqDoublets") { if ($opt eq "delSeqDoublets") {
$prop //= "adviceRemain"; $prop //= "adviceRemain";
} }
elsif ($opt eq "delDoublets") { elsif ($opt eq "delDoublets") {
$prop //= "adviceDelete"; $prop //= "adviceDelete";
} }
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt"; DbRep_setLastCmd ($name, $opt, $prop);
if ($prop =~ /delete/ && !AttrVal($hash->{NAME}, "allowDeletion", 0)) { 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 !"; 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 ($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."; return "reduceLog already in progress. Please wait for the current process to finish.";
} }
else { else {
delete $hash->{HELPER}{RUNNING_REDUCELOG}; delete $hash->{HELPER}{$dbrep_hmainf{reduceLog}{pk}};
my @b = @a; DbRep_setLastCmd (@a);
shift(@b);
$hash->{LASTCMD} = join(" ",@b);
$hash->{HELPER}{REDUCELOG} = \@a; $hash->{HELPER}{REDUCELOG} = \@a;
Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ################################################################");
@ -781,39 +787,43 @@ sub DbRep_Set {
} }
if ($opt eq "cancelDump" && $hash->{ROLE} ne "Agent") { if ($opt eq "cancelDump" && $hash->{ROLE} ne "Agent") {
$hash->{LASTCMD} = $prop?"$opt $prop":"$opt"; DbRep_setLastCmd (@a);
BlockingKill($hash->{HELPER}{RUNNING_BACKUP_CLIENT}); BlockingKill($hash->{HELPER}{RUNNING_BACKUP_CLIENT});
Log3 ($name, 3, "DbRep $name -> running Dump has been canceled"); Log3 ($name, 3, "DbRep $name -> running Dump has been canceled");
ReadingsSingleUpdateValue ($hash, "state", "Dump canceled", 1); ReadingsSingleUpdateValue ($hash, "state", "Dump canceled", 1);
return; return;
} }
if ($opt eq "cancelRepair" && $hash->{ROLE} ne "Agent") { if ($opt eq "cancelRepair" && $hash->{ROLE} ne "Agent") {
$hash->{LASTCMD} = $prop?"$opt $prop":"$opt"; DbRep_setLastCmd (@a);
BlockingKill($hash->{HELPER}{RUNNING_REPAIR}); BlockingKill($hash->{HELPER}{RUNNING_REPAIR});
Log3 ($name, 3, "DbRep $name -> running Repair has been canceled"); Log3 ($name, 3, "DbRep $name -> running Repair has been canceled");
ReadingsSingleUpdateValue ($hash, "state", "Repair canceled", 1); ReadingsSingleUpdateValue ($hash, "state", "Repair canceled", 1);
return; return;
} }
if ($opt eq "cancelRestore" && $hash->{ROLE} ne "Agent") { if ($opt eq "cancelRestore" && $hash->{ROLE} ne "Agent") {
$hash->{LASTCMD} = $prop?"$opt $prop":"$opt"; DbRep_setLastCmd (@a);
BlockingKill($hash->{HELPER}{RUNNING_RESTORE}); BlockingKill($hash->{HELPER}{RUNNING_RESTORE});
Log3 ($name, 3, "DbRep $name -> running Restore has been canceled"); Log3 ($name, 3, "DbRep $name -> running Restore has been canceled");
ReadingsSingleUpdateValue ($hash, "state", "Restore canceled", 1); ReadingsSingleUpdateValue ($hash, "state", "Restore canceled", 1);
return; return;
} }
if ($opt =~ m/tableCurrentFillup/ && $hash->{ROLE} ne "Agent") { if ($opt =~ m/tableCurrentFillup/ && $hash->{ROLE} ne "Agent") {
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt"; DbRep_setLastCmd (@a);
DbRep_Main ($hash, $opt);
DbRep_Main($hash, $opt);
return; return;
} }
if ($opt eq "index" && $hash->{ROLE} ne "Agent") { if ($opt eq "index") {
$hash->{LASTCMD} = $prop ? "$opt $prop " : "$opt"; DbRep_setLastCmd (@a);
Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ################################################################");
Log3 ($name, 3, "DbRep $name - ### New Index operation ###"); Log3 ($name, 3, "DbRep $name - ### New Index operation ###");
Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ################################################################");
@ -837,18 +847,18 @@ sub DbRep_Set {
} }
if ($opt =~ /countEntries/ && $hash->{ROLE} ne "Agent") { if ($opt =~ /countEntries/ && $hash->{ROLE} ne "Agent") {
$hash->{LASTCMD} = $prop ? "$opt $prop " : "$opt"; my $table = $prop // "history";
my $table = $prop // "history";
DbRep_Main ($hash, $opt, $table); DbRep_setLastCmd ($name, $opt, $table);
DbRep_Main ($hash, $opt, $table);
return; return;
} }
elsif ($opt =~ /fetchrows/ && $hash->{ROLE} ne "Agent") { elsif ($opt =~ /fetchrows/ && $hash->{ROLE} ne "Agent") {
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt"; my $table = $prop // "history";
my $table = $prop // "history";
DbRep_Main ($hash, $opt, $table); DbRep_setLastCmd ($name, $opt, $table);
DbRep_Main ($hash, $opt, $table);
return; return;
} }
@ -864,14 +874,15 @@ sub DbRep_Set {
} }
####################################################################################################### #######################################################################################################
if ($opt =~ m/(max|min|sum|average|diff)Value/ && $hash->{ROLE} ne "Agent") { if ($opt =~ m/(max|min|sum|average|diff)Value/ && $hash->{ROLE} ne "Agent") {
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt";
if (!AttrVal($hash->{NAME}, "reading", "")) { if (!AttrVal($hash->{NAME}, "reading", "")) {
return " The attribute reading to analyze is not set !"; return " The attribute reading to analyze is not set !";
} }
if ($prop && $prop =~ /deleteOther/ && !AttrVal($hash->{NAME}, "allowDeletion", 0)) { 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 !"; return " Set attribute 'allowDeletion' if you want to allow deletion of any database entries. Use it with care !";
} }
if ($prop && $prop =~ /writeToDB/) { if ($prop && $prop =~ /writeToDB/) {
if (!AttrVal($hash->{NAME}, "device", "") || AttrVal($hash->{NAME}, "device", "") =~ /[%*:=,]/ || AttrVal($hash->{NAME}, "reading", "") =~ /[,\s]/) { 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> return "<html>If you want write results back to database, attributes \"device\" and \"reading\" must be set.<br>
@ -880,49 +891,54 @@ sub DbRep_Set {
} }
} }
DbRep_Main ($hash,$opt,$prop); DbRep_setLastCmd (@a);
DbRep_Main ($hash,$opt,$prop);
} }
elsif ($opt =~ m/delEntries|tableCurrentPurge/ && $hash->{ROLE} ne "Agent") { elsif ($opt =~ m/delEntries|tableCurrentPurge/ && $hash->{ROLE} ne "Agent") {
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 !";
} }
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt"; delete $hash->{HELPER}{DELENTRIES};
delete $hash->{HELPER}{DELENTRIES}; DbRep_setLastCmd (@a);
shift @a; shift @a;
shift @a; shift @a;
$hash->{HELPER}{DELENTRIES} = \@a if(@a); $hash->{HELPER}{DELENTRIES} = \@a if(@a);
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";
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}{OLDDEV} = $olddev;
$hash->{HELPER}{NEWDEV} = $newdev; $hash->{HELPER}{NEWDEV} = $newdev;
DbRep_Main ($hash, $opt, $prop); DbRep_setLastCmd ($name, $opt, "$olddev,$newdev");
DbRep_Main ($hash, $opt, $prop);
} }
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";
my ($oldread, $newread) = split ",", $prop; 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}{OLDREAD} = $oldread;
$hash->{HELPER}{NEWREAD} = $newread; $hash->{HELPER}{NEWREAD} = $newread;
DbRep_Main ($hash, $opt, $prop); DbRep_setLastCmd ($name, $opt, "$oldread,$newread");
DbRep_Main ($hash, $opt, $prop);
} }
elsif ($opt eq "insert" && $hash->{ROLE} ne "Agent") { elsif ($opt eq "insert" && $hash->{ROLE} ne "Agent") {
shift @a; shift @a;
@ -930,7 +946,7 @@ sub DbRep_Set {
$prop = join " ", @a; $prop = join " ", @a;
if (!$prop) { 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; 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 $i_reading //= AttrVal($name, "reading", ""); # Reading aus Attr lesen wenn nicht im insert angegeben
if (!$i_date || !$i_time || !defined $i_value) { 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) { 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+)/); 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); }; eval { my $ts = timelocal($sec, $min, $hh, $dd, $mm-1, $yyyy-1900); };
if ($@) { if ($@) {
my @l = split (/at/, $@); my @l = split (/at/, $@);
return " Timestamp is out of range - $l[0]"; 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");
DbRep_Main ($hash, $opt, "$i_timestamp,$i_device,$i_reading,$i_value,$i_unit");
} }
elsif ($opt eq "exportToFile" && $hash->{ROLE} ne "Agent") { elsif ($opt eq "exportToFile" && $hash->{ROLE} ne "Agent") {
$prop // push @a, AttrVal($name, "expimpfile", "");
my ($ar, $hr) = parseParams(join ' ', @a); my ($ar, $hr) = parseParams(join ' ', @a);
my $f = $ar->[2] // ""; my $f = $ar->[2] // AttrVal($name, "expimpfile", "");
if (!$f) { if (!$f) {
return qq{"$opt" needs a file as argument or the attribute "expimpfile" (path and filename) to be set !}; return qq{"$opt" needs a file as argument or the attribute "expimpfile" (path and filename) to be set !};
@ -984,24 +997,21 @@ sub DbRep_Set {
return qq{The "MAXLINES" parameter must be a integer value !} if($e !~ /^MAXLINES=\d+$/); return qq{The "MAXLINES" parameter must be a integer value !} if($e !~ /^MAXLINES=\d+$/);
} }
$prop = $e ? $f." ".$e : $f; $prop = $e ? $f." ".$e : $f;
$hash->{LASTCMD} = $opt.' '.$prop;
DbRep_Main ($hash, $opt, $prop); DbRep_setLastCmd ($name, $opt, $prop);
DbRep_Main ($hash, $opt, $prop);
} }
elsif ($opt eq "importFromFile" && $hash->{ROLE} ne "Agent") { elsif ($opt eq "importFromFile" && $hash->{ROLE} ne "Agent") {
$prop // push @a, AttrVal($name, "expimpfile", "");
my ($ar, $hr) = parseParams(join ' ', @a); my ($ar, $hr) = parseParams(join ' ', @a);
my $f = $ar->[2] // ""; my $f = $ar->[2] // AttrVal($name, "expimpfile", "");
if (!$f) { if (!$f) {
return qq{"$opt" needs a file as an argument or the attribute "expimpfile" (path and filename) to be set !}; 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);
DbRep_Main ($hash, $opt, $f);
} }
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 );
@ -1119,46 +1129,38 @@ 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)) { if ($sqlcmd =~ m/^\s*delete/is && !AttrVal($hash->{NAME}, "allowDeletion", undef)) {
return "Attribute 'allowDeletion = 1' is needed for command '$sqlcmd'. Use it with care !"; return "Attribute 'allowDeletion = 1' is needed for command '$sqlcmd'. Use it with care !";
} }
DbRep_Main ($hash, $opt, $sqlcmd); DbRep_setLastCmd ($name, $opt, $sqlcmd);
DbRep_Main ($hash, $opt, $sqlcmd);
} }
elsif ($opt =~ /changeValue/) { elsif ($opt =~ /changeValue/) {
shift @a; my ($ac, $hc) = parseParams(join ' ', @a);
shift @a;
$prop = join(" ", @a);
$hash->{LASTCMD} = $prop ? "$opt $prop" : "$opt";
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};
my $complex = 0; if (!$oldval || !$newval) {
my ($oldval,$newval) = ($prop =~ /^\s*"(.*?)","(.*?)"\s*$/); return qq{Both entries old="old string" new="new string" are needed.};
if($newval =~ m/[{}]/) {
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 \"{ }\" ";
}
} }
my $complex = 0;
if($newval =~ m/^\s*\{"(.*)"\}\s*$/s) {
$newval = $1;
$complex = 1;
}
$hash->{HELPER}{COMPLEX} = $complex; $hash->{HELPER}{COMPLEX} = $complex;
$hash->{HELPER}{OLDVAL} = $oldval; $hash->{HELPER}{OLDVAL} = $oldval;
$hash->{HELPER}{NEWVAL} = $newval; $hash->{HELPER}{NEWVAL} = $newval;
DbRep_Main ($hash, $opt, $prop); DbRep_setLastCmd ($name, $opt, "old=$oldval new=$newval");
DbRep_Main ($hash, $opt, $prop);
} }
elsif ($opt =~ m/syncStandby/ && $hash->{ROLE} ne "Agent") { elsif ($opt =~ m/syncStandby/ && $hash->{ROLE} ne "Agent") {
unless($prop) { unless($prop) {
@ -1166,10 +1168,10 @@ sub DbRep_Set {
} }
if(!exists($defs{$prop}) || $defs{$prop}->{TYPE} ne "DbLog") { 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); DbRep_Main ($hash, $opt, $prop);
} }
else { else {
@ -1225,21 +1227,19 @@ sub DbRep_Get {
return "Dump is running - try again later !" if($hash->{HELPER}{RUNNING_BACKUP_CLIENT}); 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'); 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);
DbRep_Main ($hash, $opt, $prop);
} }
elsif ($opt eq "svrinfo") { elsif ($opt eq "svrinfo") {
return "Dump is running - try again later !" if($hash->{HELPER}{RUNNING_BACKUP_CLIENT}); 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);
DbRep_Main ($hash, $opt, $prop);
} }
elsif ($opt eq "blockinginfo") { elsif ($opt eq "blockinginfo") {
return "Dump is running - try again later !" if($hash->{HELPER}{RUNNING_BACKUP_CLIENT}); 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); DbRep_delread ($hash);
ReadingsSingleUpdateValue ($hash, "state", "running", 1); ReadingsSingleUpdateValue ($hash, "state", "running", 1);
DbRep_getblockinginfo ($hash); DbRep_getblockinginfo ($hash);
@ -1247,9 +1247,9 @@ sub DbRep_Get {
elsif ($opt eq "minTimestamp" || $opt eq "initData") { elsif ($opt eq "minTimestamp" || $opt eq "initData") {
return "Dump is running - try again later !" if($hash->{HELPER}{RUNNING_BACKUP_CLIENT}); 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 $hash->{HELPER}{IDRETRIES} = 3; # Anzahl wie oft versucht wird initiale Daten zu holen
DbRep_setLastCmd (@a);
DbRep_delread ($hash); DbRep_delread ($hash);
ReadingsSingleUpdateValue ($hash, "state", "running", 1); ReadingsSingleUpdateValue ($hash, "state", "running", 1);
@ -1268,7 +1268,8 @@ sub DbRep_Get {
my $sqlcmd = join " ", @cmd; my $sqlcmd = join " ", @cmd;
$sqlcmd =~ tr/ A-Za-z0-9!"#$§%&'()*+,-.\/:;<=>?@[\\]^_`{|}~äöüÄÖÜ߀/ /cs; $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)) { if ($sqlcmd =~ m/^\s*delete/is && !AttrVal($name, "allowDeletion", undef)) {
return "Attribute 'allowDeletion = 1' is needed for command '$sqlcmd'. Use it with care !"; 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) 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" ) { 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; return;
} }
@ -2382,9 +2384,11 @@ sub DbRep_Main {
return; return;
} }
if (exists($hash->{HELPER}{RUNNING_PID}) && $hash->{ROLE} ne "Agent") { ## eventuell bereits laufenden BlockingCall beenden
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}); 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, ... # initiale Datenermittlung wie minimal Timestamp, Datenbankstrukturen, ...
@ -4536,10 +4540,10 @@ sub DbRep_diffvalDone {
ReadingsBulkUpdateValue ($hash, $reading_runtime_string, (!$valid ? "-" : defined $rv ? sprintf "%.${ndp}f", $rv : "-")); ReadingsBulkUpdateValue ($hash, $reading_runtime_string, (!$valid ? "-" : defined $rv ? sprintf "%.${ndp}f", $rv : "-"));
} }
ReadingsBulkUpdateValue ($hash, "db_lines_processed", $irowdone) if($hash->{LASTCMD} =~ /writeToDB/); ReadingsBulkUpdateValue ($hash, "db_lines_processed", $irowdone) if($hash->{LASTCMD} =~ /writeToDB/);
ReadingsBulkUpdateValue ($hash, "diff_overrun_limit_".$difflimit, $rowsrej) if($rowsrej); ReadingsBulkUpdateValue ($hash, "diff_overrun_limit_".$difflimit, $rowsrej) if($rowsrej);
ReadingsBulkUpdateValue ($hash, "less_data_in_period", $ncpstr) if($ncpstr); 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); readingsEndUpdate ($hash, 1);
@ -5206,7 +5210,7 @@ sub DbRep_changeVal {
my $db = $hash->{DATABASE}; my $db = $hash->{DATABASE};
my $complex = $hash->{HELPER}{COMPLEX}; # einfache oder komplexe Werteersetzung my $complex = $hash->{HELPER}{COMPLEX}; # einfache oder komplexe Werteersetzung
my ($sql,$urow,$sth,$old,$new); my ($sql,$urow,$sth);
my $bst = [gettimeofday]; # Background-Startzeit my $bst = [gettimeofday]; # Background-Startzeit
@ -5221,11 +5225,11 @@ sub DbRep_changeVal {
$err = DbRep_beginDatabaseTransaction ($name, $dbh); $err = DbRep_beginDatabaseTransaction ($name, $dbh);
return "$name|$err" if ($err); return "$name|$err" if ($err);
my $old = delete $hash->{HELPER}{OLDVAL};
my $new = delete $hash->{HELPER}{NEWVAL};
if (!$complex) { if (!$complex) {
$old = delete $hash->{HELPER}{OLDVAL};
$new = delete $hash->{HELPER}{NEWVAL};
Log3 ($name, 5, qq{DbRep $name -> Change old value "$old" to new value "$new" in database $db}); Log3 ($name, 5, qq{DbRep $name -> Change old value "$old" to new value "$new" in database $db});
$old =~ s/'/''/g; # escape ' with '' $old =~ s/'/''/g; # escape ' with ''
@ -5271,8 +5275,6 @@ sub DbRep_changeVal {
$urow = $sth->rows; $urow = $sth->rows;
} }
else { else {
$old = delete $hash->{HELPER}{OLDVAL};
$new = delete $hash->{HELPER}{NEWVAL};
$old =~ s/'/''/g; # escape ' with '' $old =~ s/'/''/g; # escape ' with ''
my @tsa = split("\\|", $ts); # Timestampstring to Array my @tsa = split("\\|", $ts); # Timestampstring to Array
@ -5284,7 +5286,7 @@ sub DbRep_changeVal {
my $addon = $old =~ /%/ ? "AND VALUE LIKE '$old'" : "AND VALUE='$old'"; my $addon = $old =~ /%/ ? "AND VALUE LIKE '$old'" : "AND VALUE='$old'";
for my $row (@tsa) { # DB-Abfrage zeilenweise für jeden Array-Eintrag for my $row (@tsa) { # DB-Abfrage zeilenweise für jeden Array-Eintrag
my @ra = split("#", $row); my @ra = split("#", $row);
my $runtime_string = $ra[0]; my $runtime_string = $ra[0];
my $runtime_string_first = $ra[1]; my $runtime_string_first = $ra[1];
my $runtime_string_next = $ra[2]; my $runtime_string_next = $ra[2];
@ -5312,6 +5314,7 @@ sub DbRep_changeVal {
my $oval = $value; # Selektkriterium für Update alter Valuewert my $oval = $value; # Selektkriterium für Update alter Valuewert
my $VALUE = $value; my $VALUE = $value;
my $UNIT = $unit; my $UNIT = $unit;
eval $new; eval $new;
if ($@) { if ($@) {
$err = encode_base64($@,""); $err = encode_base64($@,"");
@ -8942,7 +8945,6 @@ sub DbRep_reduceLog {
my $ots = $paref->{rsn} // ""; my $ots = $paref->{rsn} // "";
my @a = @{$hash->{HELPER}{REDUCELOG}}; my @a = @{$hash->{HELPER}{REDUCELOG}};
my $rlpar = join " ", @a;
my $err = q{}; my $err = q{};
@ -9115,7 +9117,7 @@ sub DbRep_reduceLog {
$err = _DbRep_rl_deleteDayRows ($params); $err = _DbRep_rl_deleteDayRows ($params);
return "$name|$err" if ($err); return "$name|$err" if ($err);
@dayRows = (); undef @dayRows;
} }
if ($mode =~ /average|max|min|sum/i) { if ($mode =~ /average|max|min|sum/i) {
@ -9138,7 +9140,7 @@ sub DbRep_reduceLog {
$err = _DbRep_rl_updateHour ($params); $err = _DbRep_rl_updateHour ($params);
return "$name|$err" if ($err); return "$name|$err" if ($err);
@updateHour = (); undef @updateHour;
} }
if ($mode =~ /=day/i && scalar @updateDay) { if ($mode =~ /=day/i && scalar @updateDay) {
@ -9163,9 +9165,9 @@ sub DbRep_reduceLog {
} }
%hourlyKnown = (); undef %hourlyKnown;
@updateHour = (); undef @updateHour;
@updateDay = (); undef @updateDay;
$currentHour = 99; $currentHour = 99;
} }
@ -9180,7 +9182,7 @@ sub DbRep_reduceLog {
push(@updateHour, {%hourlyKnown}); push(@updateHour, {%hourlyKnown});
} }
%hourlyKnown = (); undef %hourlyKnown;
$currentHour = $hour; $currentHour = $hour;
} }
@ -12971,6 +12973,19 @@ sub DbRep_removeLeadingZero {
return $val; 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 # Werte aus BlockingCall heraus setzen
# Erwartete Liste: # Erwartete Liste:
@ -13062,12 +13077,12 @@ sub DbRep_setVersionInfo {
if($modules{$type}{META}{x_prereqs_src} && !$hash->{HELPER}{MODMETAABSENT}) { if($modules{$type}{META}{x_prereqs_src} && !$hash->{HELPER}{MODMETAABSENT}) {
# META-Daten sind vorhanden # META-Daten sind vorhanden
$modules{$type}{META}{version} = "v".$v; # Version aus META.json überschreiben, Anzeige mit {Dumper $modules{SMAPortal}{META}} $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; $modules{$type}{META}{x_version} =~ s/1.1.1/$v/g;
} else { } else {
$modules{$type}{META}{x_version} = $v; $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) { if(__PACKAGE__ eq "FHEM::$type" || __PACKAGE__ eq $type) {
# es wird mit Packages gearbeitet -> Perl übliche Modulversion setzen # es wird mit Packages gearbeitet -> Perl übliche Modulversion setzen
# mit {<Modul>->VERSION()} im FHEMWEB kann Modulversion abgefragt werden # mit {<Modul>->VERSION()} im FHEMWEB kann Modulversion abgefragt werden
@ -13656,33 +13671,31 @@ return;
<li><b> cancelDump </b> - stops a running database dump. </li> <br> <li><b> cancelDump </b> - stops a running database dump. </li> <br>
<li><b> changeValue </b> - changes the saved value of readings. <li><b> changeValue </b> - changes the stored value of a reading.
If the selection is limited to particular device/reading-combinations by If the selection is limited to certain device/reading combinations by the attributes
<a href="#DbRepattr">attribute</a> "device" respectively "reading", it is considered as well <a href="#DbRepattr">attribute</a> "device" or "reading", they are taken into account
as possibly defined time limits by time attributes (time.*). <br> in the same way as set time limits (attributes time.*). <br>
If no limits are set, the whole database is scanned and the specified value will be If these constraints are missing, the entire database is searched and the specified value is
changed. <br><br> is changed. <br><br>
<ul> <ul>
<b>Syntax: </b> <br> <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. The "string" can be: <br>
A "string" can be: <br>
<table> <table>
<colgroup> <col width=15%> <col width=85%> </colgroup> <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> <tr><td><b>&lt;old String&gt; :</b> </td><td><li>a simple string with/without spaces, e.g. "OL 12" </li> </td></tr>
<li>a string with usage of SQL-wildcard, e.g. "%OL%" </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> </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> <tr><td><b>&lt;new String&gt; :</b> </td><td><li>a simple string with/without spaces, e.g. "12 kWh" </li> </td></tr>
<li>Perl code embedded in "{}" with quotes, e.g. "{($VALUE,$UNIT) = split(" ",$VALUE)}". <tr><td> </td><td><li>Perl code enclosed in {"..."} including quotes, e.g. {"($VALUE,$UNIT) = split(" ",$VALUE)"} </td></tr>
The perl expression the variables $VALUE and $UNIT are committed to. <tr><td> </td><td>The variables $VALUE and $UNIT are passed to the Perl expression. They can be changed </td></tr>
The variables are changable within the perl code. The returned value <tr><td> </td><td>within the Perl code. The returned value of $VALUE and $UNIT is stored </td></tr>
of VALUE and UNIT are saved into the database field VALUE respectively <tr><td> </td><td>in the VALUE or UNIT field of the record. </li> </td></tr>
UNIT of the dataset. </li></td></tr> </table>
</table>
<br> <br>
<b>Examples: </b> <br> <b>Examples: </b> <br>
@ -13692,10 +13705,10 @@ return;
set &lt;name&gt; changeValue "%OL%","12 OL" <br> set &lt;name&gt; changeValue "%OL%","12 OL" <br>
# contains the field VALUE the substring "OL", it is changed to "12 OL". <br><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> # 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") # if the old field value begins with "24", it is splitted and VALUE=24 is saved (e.g. "24 kWh")
<br><br> <br><br>
@ -14610,23 +14623,37 @@ return;
</li> </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> 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 The data is cleaned within the time limits defined by the <b>time.*</b>-attributes.
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). 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 accrual is determined by the module in this case. <br>
The respective missing time delimitation is calculated by the module in this case. The working mode is determined by the optional specification of <b>mode</b>:
<br><br> <br><br>
By optionally specifying <b>average</b>, not only the database will be cleaned up, but <ul>
all numeric values of an hour are reduced to a single average value. <table>
With the option <b>average=day</b>, all numeric values of a day are reduced to a single <colgroup> <col width=23%> <col width=77%> </colgroup>
average value (implies 'average'). <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>
<br><br> <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 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 or be excluded. Both restrictions reduce the selected data and reduce the
@ -14683,14 +14710,14 @@ return;
</ul> </ul>
<br> <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 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> days are considered. <b>&lt;nn&gt;</b> Tage.
Specifying <b>average</b> not only cleans up the database, but also Records are considered that are older than <b>&lt;no&gt;</b> days and (optionally) newer than
all numerical values of an hour are reduced to a single mean value. <b>&lt;nn&gt;</b> days.
With the option <b>average=day</b> all numerical values of a day are reduced to a single The working mode is determined by the optional specification of <b>mode</b> as described above.
Average value reduced (implies 'average'). <br><br> <br><br>
The additions "EXCLUDE" or "INCLUDE" can be added to exclude or include device/reading combinations in reduceLog 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> and override the "device" and "reading" attributes, which are ignored in this case. <br>
@ -14707,10 +14734,10 @@ return;
<br> <br>
<b>Note:</b> <br> <b>Note:</b> <br>
Although the function itself is designed non-blocking, the assigned DbLog device should Although the function itself is designed non-blocking, the assigned DbLog device should be operated in
operated in asynchronous mode to avoid blocking of FHEMWEB (table lock). <br> asynchronous mode to avoid blocking FHEMWEB (table lock). <br>
It is also strongly recommended to use the standard INDEX 'Search_Idx' in the table 'history'. Furthermore it is strongly recommended to create the standard INDEX 'Search_Idx' in the table
to put on ! <br> 'history' ! <br>
The processing of this command may take an extremely long time (without INDEX). <br><br> The processing of this command may take an extremely long time (without INDEX). <br><br>
</li> <br> </li> <br>
@ -16461,21 +16488,21 @@ return;
<ul> <ul>
<b>Syntax: </b> <br> <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> "String" kann sein: <br>
<table> <table>
<colgroup> <col width=15%> <col width=85%> </colgroup> <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> <tr><td><b>&lt;alter String&gt; :</b> </td><td><li>ein einfacher String mit/ohne Leerzeichen, z.B. "OL 12" </li> </td></tr>
<li>ein String mit Verwendung von SQL-Wildcard, z.B. "%OL%" </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> </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> <tr><td><b>&lt;neuer String&gt; :</b> </td><td><li>ein einfacher String mit/ohne Leerzeichen, z.B. "12 kWh" </li> </td></tr>
<li>Perl Code eingeschlossen in "{}" inkl. Quotes, z.B. "{($VALUE,$UNIT) = split(" ",$VALUE)}". <tr><td> </td><td><li>Perl Code eingeschlossen in {"..."} inkl. Quotes, z.B. {"($VALUE,$UNIT) = split(" ",$VALUE)"} </td></tr>
Dem Perl-Ausdruck werden die Variablen $VALUE und $UNIT übergeben. Sie können innerhalb <tr><td> </td><td>Dem Perl-Ausdruck werden die Variablen $VALUE und $UNIT übergeben. Sie können innerhalb </td></tr>
des Perl-Code geändert werden. Der zurückgebene Wert von $VALUE und $UNIT wird in dem Feld <tr><td> </td><td>des Perl-Code geändert werden. Der zurückgebene Wert von $VALUE und $UNIT wird in dem Feld </td></tr>
VALUE bzw. UNIT des Datensatzes gespeichert. </li></td></tr> <tr><td> </td><td>VALUE bzw. UNIT des Datensatzes gespeichert. </li> </td></tr>
</table> </table>
</ul> </ul>
<br> <br>
@ -16488,10 +16515,10 @@ return;
set &lt;name&gt; changeValue "%OL%","12 OL" <br> 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> # 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> # 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") # beginnt der alte Feldwert mit "24", wird er gesplittet und VALUE=24 gespeichert (z.B. "24 kWh")
<br><br> <br><br>