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:
parent
941cabca36
commit
6ce7fcf1de
@ -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 <name> changeValue "<old string>","<new string>" <br><br>
|
set <name> changeValue old="<old string>" new="<new string>" <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><old string> :</b> </td><td><li>a simple string with/without spaces, e.g. "OL 12" </li>
|
<tr><td><b><old String> :</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><new string> :</b> </td><td><li>a simple string with/without spaces, e.g. "12 kWh" </li>
|
<tr><td><b><new String> :</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 <name> changeValue "%OL%","12 OL" <br>
|
set <name> 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 <name> changeValue "12 kWh","{($VALUE,$UNIT) = split(" ",$VALUE)}" <br>
|
set <name> 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 <name> changeValue "24%","{$VALUE = (split(" ",$VALUE))[0]}" <br>
|
set <name> 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 [<no>[:<nn>]] [average[=day]] [EXCLUDE=device1:reading1,device2:reading2,...] [INCLUDE=device:reading] </b> <br>
|
<li><b> reduceLog [<no>[:<nn>]] [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>: the data is reduced to the first entry per hour per device & reading </td></tr>
|
||||||
<br><br>
|
<tr><td> <b>average</b> </td><td>: 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>: numeric values are reduced to one mean value per day per device & reading, otherwise as without mode </td></tr>
|
||||||
|
<tr><td> </td><td> The FullDay option (full days are always selected) is used implicitly. </td></tr>
|
||||||
|
<tr><td> <b>max</b> </td><td>: 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>: numeric values are reduced to the maximum value per day per device & reading, otherwise as without mode </td></tr>
|
||||||
|
<tr><td> </td><td> The FullDay option (full days are always selected) is used implicitly. </td></tr>
|
||||||
|
<tr><td> <b>min</b> </td><td>: 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>: numeric values are reduced to the minimum value per day per device & reading, otherwise as without mode </td></tr>
|
||||||
|
<tr><td> </td><td> The FullDay option (full days are always selected) is used implicitly. </td></tr>
|
||||||
|
<tr><td> <b>sum</b> </td><td>: 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>: numeric values are reduced to the sum per day per Device & Reading, otherwise as without mode </td></tr>
|
||||||
|
<tr><td> </td><td> 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><no></b> days and (optionally) newer than
|
Es werden Datensätze berücksichtigt die älter sind als <b><no></b> Tage und (optional) neuer sind als
|
||||||
<b><nn></b> days are considered.
|
<b><nn></b> Tage.
|
||||||
Specifying <b>average</b> not only cleans up the database, but also
|
Records are considered that are older than <b><no></b> days and (optionally) newer than
|
||||||
all numerical values of an hour are reduced to a single mean value.
|
<b><nn></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 <name> changeValue "<alter String>","<neuer String>" <br><br>
|
set <name> changeValue old="<alter String>" new="<neuer String>" <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><alter String> :</b> </td><td><li>ein einfacher String mit/ohne Leerzeichen, z.B. "OL 12" </li>
|
<tr><td><b><alter String> :</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><neuer String> :</b> </td><td><li>ein einfacher String mit/ohne Leerzeichen, z.B. "12 kWh" </li>
|
<tr><td><b><neuer String> :</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 <name> changeValue "%OL%","12 OL" <br>
|
set <name> 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 <name> changeValue "12 kWh","{($VALUE,$UNIT) = split(" ",$VALUE)}" <br>
|
set <name> 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 <name> changeValue "24%","{$VALUE = (split(" ",$VALUE))[0]}" <br>
|
set <name> 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>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user