From edbe795caa07fbcc3a5f4f085a14b7d7af7ba3d9 Mon Sep 17 00:00:00 2001 From: nasseeder1 Date: Wed, 10 Jan 2024 18:46:13 +0000 Subject: [PATCH] 93_DbRep: new setter multiCmd, change autoForward git-svn-id: https://svn.fhem.de/fhem/trunk@28370 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 2 + fhem/FHEM/93_DbRep.pm | 1995 ++++++++++++++++++++++++----------------- 2 files changed, 1179 insertions(+), 818 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index e9c7a6db4..54d7240e9 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,7 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Do not insert empty lines here, update check depends on it. + - feature: 93_DbRep: new setter multiCmd, change autoForward + bugfix: reducelog problem Forum:#136581 - bugfix: 76_SolarForecast: fix get Automatic State - change: 76_SolarForecast: optimize battery management once more - change: 76_SolarForecast: optimize batterymanagement diff --git a/fhem/FHEM/93_DbRep.pm b/fhem/FHEM/93_DbRep.pm index 977b00baf..dcc0d7df2 100644 --- a/fhem/FHEM/93_DbRep.pm +++ b/fhem/FHEM/93_DbRep.pm @@ -3,7 +3,7 @@ ########################################################################################################## # 93_DbRep.pm # -# (c) 2016-2023 by Heiko Maaz +# (c) 2016-2024 by Heiko Maaz # e-mail: Heiko dot Maaz at t-online dot de # # This Module can be used to select and report content of databases written by 93_DbLog module @@ -59,6 +59,7 @@ no if $] >= 5.017011, warnings => 'experimental::smartmatch'; # Version History intern my %DbRep_vNotesIntern = ( + "8.53.0" => "10.01.2024 new setter multiCmd, change DbRep_autoForward, fix reducelog problem Forum:#136581 ", "8.52.15" => "08.12.2023 fix use fhem default variables in attr executeBeforeProc/executeAfterProc ". "forum: https://forum.fhem.de/index.php?msg=1296146 ", "8.52.14" => "08.11.2023 fix period calculation when using attr timeYearPeriod ", @@ -114,63 +115,6 @@ my %DbRep_vNotesIntern = ( "8.46.11" => "03.01.2022 more code refacturing, minor fixes ", "8.46.10" => "02.01.2022 more code refacturing, minor fixes ", "8.46.9" => "01.01.2022 some more code refacturing, minor fixes ", - "8.46.8" => "30.12.2021 some code refacturing and minor bug fixing ", - "8.46.7" => "27.12.2021 some code improvements, insert accept a multiline string ", - "8.46.6" => "26.12.2021 sub DbRep_sqlCmd uses credentials dependend of attr useAdminCredentials ". - "fix warnings in sub export/import file, fix error of DbRep_expfile filedefinition ". - "change retrieve minTimestamp forum:#124987", - "8.46.5" => "19.12.2021 some code improvements ", - "8.46.4" => "14.12.2021 fix tableCurrentPurge ", - "8.46.3" => "12.12.2021 edit/fix minor faults in exportToFile/importFromFile, new getter initData ", - "8.46.2" => "08.12.2021 Pragma query possible in sqlCmd. So far only 'set', get encoding info from database ", - "8.46.1" => "07.12.2021 some code improvements ", - "8.46.0" => "06.12.2021 reduceLog options INCLUDE / EXCLUCE parse by parseParams ", - "8.45.0" => "05.12.2021 revised userExitFn, fix average=day problem in reduceLog (Forum: https://forum.fhem.de/index.php/topic,53584.msg1177799.html#msg1177799) ", - "8.44.1" => "27.11.2021 change diffValue: recognize if diff is 0 or no value available ", - "8.44.0" => "21.11.2021 new attr numDecimalPlaces ", - "8.43.1" => "02.11.2021 fix SQL statement if devspec can't be resolved, Forum:https://forum.fhem.de/index.php/topic,53584.msg1184155.html#msg1184155 ", - "8.43.0" => "22.09.2021 consider attr device, reading in sqlCmd, remove length limit of attr device, reading ", - "8.42.9" => "05.09.2021 minor fixes, change SQL for SQLite in DbRep_deldoublets ", - "8.42.8" => "17.07.2021 more log data verbose 5, delete whitespaces in sub getInitData ", - "8.42.7" => "27.02.2021 fix attribute sqlCmdVars is not working in sqlCmdBlocking Forum: /topic,53584.msg1135528.html#msg1135528", - "8.42.6" => "25.02.2021 fix commandref ", - "8.42.5" => "02.02.2021 correct possible values for attr seqDoubletsVariance ", - "8.42.4" => "30.01.2021 fix commandref ", - "8.42.3" => "03.01.2021 set fastStart as default for TYPE Client ", - "8.42.2" => "03.01.2021 sumValue - create 0 instaed of '-' if value of DS is 0, Forum:#index.php/topic,53584.msg1116910.html#msg1116910 ", - "8.42.1" => "22.11.2020 fix delete \$hash->{HELPER}{REDUCELOG} Forum:#116057 ", - "8.42.0" => "14.11.2020 new vals next_day_begin, next_day_end for time attr Forum: https://forum.fhem.de/index.php/topic,53584.msg1100040.html#msg1100040 ", - "8.41.0" => "08.11.2020 new attrbute avgDailyMeanGWSwithGTS for Grassland temperature sum, minor bugfixes in create time array ", - "8.40.8" => "17.09.2020 sqlCmd supports PREPARE statament Forum: #114293, commandRef revised ", - "8.40.7" => "03.09.2020 rename getter dbValue to sqlCmdBlocking, consider attr timeout in function DbRep_sqlCmdBlocking (blocking function), commandRef revised ", - "8.40.6" => "27.08.2020 commandRef revised ", - "8.40.5" => "29.07.2020 fix crash if delEntries startet without any time limits, Forum:#113202 ", - "8.40.4" => "23.07.2020 new aggregation value 'minute', some fixes ", - "8.40.3" => "22.07.2020 delete prototypes ", - "8.40.2" => "27.06.2020 improve versionNotes 2 ", - "8.40.1" => "18.05.2020 improve 'restore' setlist, revised comRef, fix compare timesettings for delEntries,reduceLog ", - "8.40.0" => "30.03.2020 new attribute 'autoForward' ", - "8.39.0" => "28.03.2020 option 'writeToDBInTime' for function 'sumValue' and 'averageValue' ", - "8.38.0" => "21.03.2020 sqlSpecial readingsDifferenceByTimeDelta, fix FHEM crash if no time-attribute is set and time ". - "option of delEntries / reduceLog is used, minor fix for sqlCmdHistory ", - "8.37.0" => "20.03.2020 add column header for free selects (sqlCmd) ", - "8.36.0" => "19.03.2020 sqlSpecial recentReadingsOfDevice ", - "8.35.0" => "19.03.2020 option older than days and newer than days for delEntries function ", - "8.34.0" => "18.03.2020 option writeToDBSingle for functions averageValue, sumValue ", - "8.33.0" => "18.03.2020 option older than days and newer than days for reduceLog function ", - "8.32.4" => "15.03.2020 fix rights check for index operation ", - "8.32.3" => "10.03.2020 better logfile messages in some cases of index operation ", - "8.32.2" => "01.03.2020 fix PERL WARNING: Argument \"\" isn't numeric in sprintf at ./FHEM/93_DbRep.pm line 10708 again ", - "8.32.1" => "08.02.2020 fix PERL WARNING: Argument \"\" isn't numeric in sprintf at ./FHEM/93_DbRep.pm line 10708 ", - "8.32.0" => "29.01.2020 new option \"deleteOther\" for minValue ", - "8.31.0" => "26.01.2020 new option \"deleteOther\" for maxValue ", - "8.30.8" => "25.01.2020 adjust SQL-Statements in OutputWriteToDB to avoid Duplicate entry errors and other fixes ", - "8.30.7" => "24.01.2020 corrected count of inserted rows into standby database (DbRep_WriteToDB) ", - "8.30.6" => "23.01.2020 delDoublets now are working also for PostgreSQL, calculation of number_fetched_rows corrected ", - "8.30.5" => "23.01.2020 remove adminCredentials from set of device type \"Agent\" ", - "8.30.4" => "22.01.2020 adjust behavior of OutputWriteToDB (averageValue,sumValue) - write value at every begin and also at every end of period ". - "Forum: https://forum.fhem.de/index.php/topic,105787.msg1013920.html#msg1013920 ". - "fix Warning when Agent has detected a renamed device", "1.0.0" => "19.05.2016 Initial" ); @@ -396,35 +340,36 @@ my %DbRep_vHintsExt_de = ( # dobp = executeBeforeProc auswerten ################################################################### my %dbrep_hmainf = ( - sumValue => { fn => "DbRep_sumval", fndone => "DbRep_sumvalDone", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 1, dobp => 1, table => "history" }, - countEntries => { fn => "DbRep_count", fndone => "DbRep_countDone", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 1, dobp => 1 }, - sqlCmd => { fn => "DbRep_sqlCmd", fndone => "DbRep_sqlCmdDone", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 1, dobp => 1 }, - sqlCmdHistory => { fn => "DbRep_sqlCmd", fndone => "DbRep_sqlCmdDone", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 1, dobp => 1 }, - sqlSpecial => { fn => "DbRep_sqlCmd", fndone => "DbRep_sqlCmdDone", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 1, dobp => 1 }, - averageValue => { fn => "DbRep_averval", fndone => "DbRep_avervalDone", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 1, dobp => 1, table => "history" }, - fetchrows => { fn => "DbRep_fetchrows", fndone => "DbRep_fetchrowsDone", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 1, dobp => 1 }, - maxValue => { fn => "DbRep_maxval", fndone => "DbRep_maxvalDone", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 1, dobp => 1, table => "history" }, - minValue => { fn => "DbRep_minval", fndone => "DbRep_minvalDone", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 1, dobp => 1, table => "history" }, - exportToFile => { fn => "DbRep_expfile", fndone => "DbRep_expfile_Done", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 1, dobp => 1, table => "history" }, - importFromFile => { fn => "DbRep_impfile", fndone => "DbRep_impfile_Done", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 1, dobp => 1, table => "history" }, - tableCurrentFillup => { fn => "DbRep_currentfillup", fndone => "DbRep_currentfillupDone", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 1, dobp => 1, table => "current" }, - diffValue => { fn => "DbRep_diffval", fndone => "DbRep_diffvalDone", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 1, dobp => 1, table => "history" }, - delEntries => { fn => "DbRep_del", fndone => "DbRep_del_Done", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 1, dobp => 1, table => "history" }, - syncStandby => { fn => "DbRep_syncStandby", fndone => "DbRep_syncStandbyDone", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 1, dobp => 1, table => "history" }, - delSeqDoublets => { fn => "DbRep_delseqdoubl", fndone => "DbRep_deldoubl_Done", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 1, dobp => 1, table => "history" }, - delDoublets => { fn => "DbRep_deldoublets", fndone => "DbRep_deldoubl_Done", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 1, dobp => 1, table => "history" }, - reduceLog => { fn => "DbRep_reduceLog", fndone => "DbRep_reduceLogDone", fnabort => "DbRep_reduceLogAborted", pk => "RUNNING_REDUCELOG", timeset => 1, dobp => 1, table => "history" }, - tableCurrentPurge => { fn => "DbRep_del", fndone => "DbRep_del_Done", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 0, dobp => 1, table => "current" }, - dbvars => { fn => "DbRep_dbmeta", fndone => "DbRep_dbmeta_Done", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 0, dobp => 0 }, - dbstatus => { fn => "DbRep_dbmeta", fndone => "DbRep_dbmeta_Done", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 0, dobp => 0 }, - tableinfo => { fn => "DbRep_dbmeta", fndone => "DbRep_dbmeta_Done", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 0, dobp => 0 }, - procinfo => { fn => "DbRep_dbmeta", fndone => "DbRep_dbmeta_Done", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 0, dobp => 0 }, - svrinfo => { fn => "DbRep_dbmeta", fndone => "DbRep_dbmeta_Done", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 0, dobp => 0 }, - insert => { fn => "DbRep_insert", fndone => "DbRep_insertDone", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 0, dobp => 1, table => "history" }, - deviceRename => { fn => "DbRep_changeDevRead", fndone => "DbRep_changeDone", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 0, dobp => 1, table => "history", renmode => "devren" }, - readingRename => { fn => "DbRep_changeDevRead", fndone => "DbRep_changeDone", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 0, dobp => 1, table => "history", renmode => "readren" }, - changeValue => { fn => "DbRep_changeVal", fndone => "DbRep_changeDone", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 1, dobp => 1, table => "history", renmode => "changeval" }, - migrateCollation => { fn => "DbRep_migCollation", fndone => "DbRep_migCollation_Done", fnabort => "DbRep_ParseAborted", pk => "RUNNING_PID", timeset => 0, dobp => 1 }, + eraseReadings => { fn => 'DbRep_delread', fndone => '', fnabort => '', pk => '', timeset => 0, dobp => 0, table => '' }, + sumValue => { fn => 'DbRep_sumval', fndone => 'DbRep_sumvalDone', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 1, dobp => 1, table => 'history' }, + countEntries => { fn => 'DbRep_count', fndone => 'DbRep_countDone', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 1, dobp => 1 }, + sqlCmd => { fn => 'DbRep_sqlCmd', fndone => 'DbRep_sqlCmdDone', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 1, dobp => 1 }, + sqlCmdHistory => { fn => 'DbRep_sqlCmd', fndone => 'DbRep_sqlCmdDone', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 1, dobp => 1 }, + sqlSpecial => { fn => 'DbRep_sqlCmd', fndone => 'DbRep_sqlCmdDone', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 1, dobp => 1 }, + averageValue => { fn => 'DbRep_averval', fndone => 'DbRep_avervalDone', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 1, dobp => 1, table => 'history' }, + fetchrows => { fn => 'DbRep_fetchrows', fndone => 'DbRep_fetchrowsDone', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 1, dobp => 1 }, + maxValue => { fn => 'DbRep_maxval', fndone => 'DbRep_maxvalDone', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 1, dobp => 1, table => 'history' }, + minValue => { fn => 'DbRep_minval', fndone => 'DbRep_minvalDone', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 1, dobp => 1, table => 'history' }, + exportToFile => { fn => 'DbRep_expfile', fndone => 'DbRep_expfile_Done', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 1, dobp => 1, table => 'history' }, + importFromFile => { fn => 'DbRep_impfile', fndone => 'DbRep_impfile_Done', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 1, dobp => 1, table => 'history' }, + tableCurrentFillup => { fn => 'DbRep_currentfillup', fndone => 'DbRep_currentfillupDone', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 1, dobp => 1, table => 'current' }, + diffValue => { fn => 'DbRep_diffval', fndone => 'DbRep_diffvalDone', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 1, dobp => 1, table => 'history' }, + delEntries => { fn => 'DbRep_del', fndone => 'DbRep_del_Done', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 1, dobp => 1, table => 'history' }, + syncStandby => { fn => 'DbRep_syncStandby', fndone => 'DbRep_syncStandbyDone', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 1, dobp => 1, table => 'history' }, + delSeqDoublets => { fn => 'DbRep_delseqdoubl', fndone => 'DbRep_deldoubl_Done', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 1, dobp => 1, table => 'history' }, + delDoublets => { fn => 'DbRep_deldoublets', fndone => 'DbRep_deldoubl_Done', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 1, dobp => 1, table => 'history' }, + reduceLog => { fn => 'DbRep_reduceLog', fndone => 'DbRep_reduceLogDone', fnabort => 'DbRep_reduceLogAborted', pk => 'RUNNING_REDUCELOG', timeset => 1, dobp => 1, table => 'history' }, + tableCurrentPurge => { fn => 'DbRep_del', fndone => 'DbRep_del_Done', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 0, dobp => 1, table => 'current' }, + dbvars => { fn => 'DbRep_dbmeta', fndone => 'DbRep_dbmeta_Done', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 0, dobp => 0 }, + dbstatus => { fn => 'DbRep_dbmeta', fndone => 'DbRep_dbmeta_Done', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 0, dobp => 0 }, + tableinfo => { fn => 'DbRep_dbmeta', fndone => 'DbRep_dbmeta_Done', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 0, dobp => 0 }, + procinfo => { fn => 'DbRep_dbmeta', fndone => 'DbRep_dbmeta_Done', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 0, dobp => 0 }, + svrinfo => { fn => 'DbRep_dbmeta', fndone => 'DbRep_dbmeta_Done', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 0, dobp => 0 }, + insert => { fn => 'DbRep_insert', fndone => 'DbRep_insertDone', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 0, dobp => 1, table => 'history' }, + deviceRename => { fn => 'DbRep_changeDevRead', fndone => 'DbRep_changeDone', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 0, dobp => 1, table => 'history', renmode => 'devren' }, + readingRename => { fn => 'DbRep_changeDevRead', fndone => 'DbRep_changeDone', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 0, dobp => 1, table => 'history', renmode => 'readren' }, + changeValue => { fn => 'DbRep_changeVal', fndone => 'DbRep_changeDone', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 1, dobp => 1, table => 'history', renmode => 'changeval' }, + migrateCollation => { fn => 'DbRep_migCollation', fndone => 'DbRep_migCollation_Done', fnabort => 'DbRep_ParseAborted', pk => 'RUNNING_PID', timeset => 0, dobp => 1 }, ); my %dbrep_havgfn = ( # Schemafunktionen von averageValue @@ -590,18 +535,19 @@ return; ################################################################################### sub DbRep_Set { my ($hash, @a) = @_; - return "\"set X\" needs at least an argument" if ( @a < 2 ); + return qq{"set " needs at least an argument} if(@a < 2); + my $name = $a[0]; my $opt = $a[1]; my $prop = $a[2]; my $prop1 = $a[3]; - my $dbh = $hash->{DBH}; + my $dblogdevice = $hash->{HELPER}{DBLOGDEVICE}; my $dbloghash = $defs{$hash->{HELPER}{DBLOGDEVICE}}; my $dbmodel = $dbloghash->{MODEL}; my $dbname = $hash->{DATABASE}; - my $sd =""; + my $sd = ""; my (@bkps,$dir); $dir = AttrVal ($name, "dumpDirLocal", $dbrep_dump_path_def); # 'dumpDirRemote' (Backup-Verz. auf dem MySQL-Server) muß gemountet sein und in 'dumpDirLocal' eingetragen sein @@ -629,6 +575,7 @@ sub DbRep_Set { my $cj = @bkps ? join(",",reverse(sort @bkps)) : " "; my (undef, $hl) = DbRep_listSQLcmdCache ($name); # Drop-Down Liste bisherige Befehle in "sqlCmd" erstellen + if (AttrVal($name, "sqlCmdHistoryLength", 0)) { $hl .= "___purge_sqlhistory___"; $hl .= ",___list_sqlhistory___"; @@ -662,44 +609,49 @@ sub DbRep_Set { "eraseReadings:noArg ". "deviceRename ". "index:".$indl." ". - (($hash->{ROLE} ne "Agent") ? "delDoublets:adviceDelete,delete " : ""). - (($hash->{ROLE} ne "Agent") ? "delEntries " : ""). - (($hash->{ROLE} ne "Agent") ? "changeValue " : ""). - (($hash->{ROLE} ne "Agent") ? "readingRename " : ""). - (($hash->{ROLE} ne "Agent") ? "exportToFile " : ""). - (($hash->{ROLE} ne "Agent") ? "importFromFile " : ""). - (($hash->{ROLE} ne "Agent") ? "maxValue:display,writeToDB,deleteOther " : ""). - (($hash->{ROLE} ne "Agent") ? "minValue:display,writeToDB,deleteOther " : ""). - (($hash->{ROLE} ne "Agent") ? "fetchrows:history,current " : ""). - (($hash->{ROLE} ne "Agent") ? "diffValue:display,writeToDB " : ""). - (($hash->{ROLE} ne "Agent" && $dbmodel =~ /MYSQL/) ? "adminCredentials " : ""). - (($hash->{ROLE} ne "Agent") ? "insert " : ""). - (($hash->{ROLE} ne "Agent") ? "reduceLog " : ""). - (($hash->{ROLE} ne "Agent") ? "sqlCmd:textField-long " : ""). - (($hash->{ROLE} ne "Agent" && $hl) ? "sqlCmdHistory:".$hl." " : ""). - (($hash->{ROLE} ne "Agent") ? "sqlSpecial:".$specials." " : ""). - (($hash->{ROLE} ne "Agent") ? "syncStandby " : ""). - (($hash->{ROLE} ne "Agent") ? "tableCurrentFillup:noArg " : ""). - (($hash->{ROLE} ne "Agent") ? "tableCurrentPurge:noArg " : ""). - (($hash->{ROLE} ne "Agent") ? "countEntries:history,current " : ""). - (($hash->{ROLE} ne "Agent") ? "sumValue:display,writeToDB,writeToDBSingle,writeToDBInTime " : ""). - (($hash->{ROLE} ne "Agent") ? "averageValue:display,writeToDB,writeToDBSingle,writeToDBSingleStart,writeToDBInTime " : ""). - (($hash->{ROLE} ne "Agent") ? "delSeqDoublets:adviceRemain,adviceDelete,delete " : ""). - (($hash->{ROLE} ne "Agent" && $dbmodel =~ /MYSQL/) ? "dumpMySQL:clientSide,serverSide " : ""). - (($hash->{ROLE} ne "Agent" && $dbmodel =~ /MYSQL/) ? "migrateCollation:".$collation." " : ""). - (($hash->{ROLE} ne "Agent" && $dbmodel =~ /SQLITE/) ? "dumpSQLite:noArg " : ""). - (($hash->{ROLE} ne "Agent" && $dbmodel =~ /SQLITE/) ? "repairSQLite " : ""). - (($hash->{ROLE} ne "Agent" && $dbmodel =~ /MYSQL/) ? "optimizeTables:showInfo,execute " : ""). - (($hash->{ROLE} ne "Agent" && $dbmodel =~ /SQLITE|POSTGRESQL/) ? "vacuum:noArg " : ""). - (($hash->{ROLE} ne "Agent" && $dbmodel =~ /MYSQL/) ? "restoreMySQL:".$cj." " : ""). - (($hash->{ROLE} ne "Agent" && $dbmodel =~ /SQLITE/) ? "restoreSQLite:".$cj." " : "") + ($hash->{ROLE} ne "Agent" ? "delDoublets:adviceDelete,delete " : ""). + ($hash->{ROLE} ne "Agent" ? "delEntries " : ""). + ($hash->{ROLE} ne "Agent" ? "changeValue " : ""). + ($hash->{ROLE} ne "Agent" ? "readingRename " : ""). + ($hash->{ROLE} ne "Agent" ? "exportToFile " : ""). + ($hash->{ROLE} ne "Agent" ? "importFromFile " : ""). + ($hash->{ROLE} ne "Agent" ? "maxValue:display,writeToDB,deleteOther " : ""). + ($hash->{ROLE} ne "Agent" ? "minValue:display,writeToDB,deleteOther " : ""). + ($hash->{ROLE} ne "Agent" ? "multiCmd:textField-long " : ""). + ($hash->{ROLE} ne "Agent" ? "fetchrows:history,current " : ""). + ($hash->{ROLE} ne "Agent" ? "diffValue:display,writeToDB " : ""). + ($hash->{ROLE} ne "Agent" ? "insert " : ""). + ($hash->{ROLE} ne "Agent" ? "reduceLog " : ""). + ($hash->{ROLE} ne "Agent" ? "sqlCmd:textField-long " : ""). + ($hash->{ROLE} ne "Agent" ? "sqlSpecial:".$specials." " : ""). + ($hash->{ROLE} ne "Agent" ? "syncStandby " : ""). + ($hash->{ROLE} ne "Agent" ? "tableCurrentFillup:noArg " : ""). + ($hash->{ROLE} ne "Agent" ? "tableCurrentPurge:noArg " : ""). + ($hash->{ROLE} ne "Agent" ? "countEntries:history,current " : ""). + ($hash->{ROLE} ne "Agent" ? "sumValue:display,writeToDB,writeToDBSingle,writeToDBInTime " : ""). + ($hash->{ROLE} ne "Agent" ? "averageValue:display,writeToDB,writeToDBSingle,writeToDBSingleStart,writeToDBInTime " : ""). + ($hash->{ROLE} ne "Agent" ? "delSeqDoublets:adviceRemain,adviceDelete,delete " : ""). + ($hash->{ROLE} ne "Agent" && $hl ? "sqlCmdHistory:".$hl." " : ""). + ($hash->{ROLE} ne "Agent" && $dbmodel =~ /MYSQL/ ? "adminCredentials " : ""). + ($hash->{ROLE} ne "Agent" && $dbmodel =~ /MYSQL/ ? "dumpMySQL:clientSide,serverSide " : ""). + ($hash->{ROLE} ne "Agent" && $dbmodel =~ /MYSQL/ ? "migrateCollation:".$collation." " : ""). + ($hash->{ROLE} ne "Agent" && $dbmodel =~ /SQLITE/ ? "dumpSQLite:noArg " : ""). + ($hash->{ROLE} ne "Agent" && $dbmodel =~ /SQLITE/ ? "repairSQLite " : ""). + ($hash->{ROLE} ne "Agent" && $dbmodel =~ /MYSQL/ ? "optimizeTables:showInfo,execute " : ""). + ($hash->{ROLE} ne "Agent" && $dbmodel =~ /SQLITE|POSTGRESQL/ ? "vacuum:noArg " : ""). + ($hash->{ROLE} ne "Agent" && $dbmodel =~ /MYSQL/ ? "restoreMySQL:".$cj." " : ""). + ($hash->{ROLE} ne "Agent" && $dbmodel =~ /SQLITE/ ? "restoreSQLite:".$cj." " : "") ; return if(IsDisabled($name)); - if ($opt =~ /eraseReadings/) { + if ($opt eq 'eraseReadings') { DbRep_setLastCmd (@a); - DbRep_delread ($hash); # Readings löschen die nicht in der Ausnahmeliste (Attr readingPreventFromDel) stehen + + no strict 'refs'; + $dbrep_hmainf{$opt}{fn} ($hash); # Readings löschen die nicht in der Ausnahmeliste (Attr readingPreventFromDel) stehen + use strict; + return; } @@ -808,24 +760,23 @@ sub DbRep_Set { return; } - if ($opt =~ m/reduceLog/ && $hash->{ROLE} ne "Agent") { + if ($opt =~ m/reduceLog/ && $hash->{ROLE} ne "Agent") { if ($hash->{HELPER}{$dbrep_hmainf{reduceLog}{pk}} && $hash->{HELPER}{$dbrep_hmainf{reduceLog}{pk}}{pid} !~ m/DEAD/) { return "reduceLog already in progress. Please wait for the current process to finish."; } - else { - delete $hash->{HELPER}{$dbrep_hmainf{reduceLog}{pk}}; - DbRep_setLastCmd (@a); - $hash->{HELPER}{REDUCELOG} = \@a; + delete $hash->{HELPER}{$dbrep_hmainf{reduceLog}{pk}}; + DbRep_setLastCmd (@a); - Log3 ($name, 3, "DbRep $name - ################################################################"); - Log3 ($name, 3, "DbRep $name - ### new reduceLog run ###"); - Log3 ($name, 3, "DbRep $name - ################################################################"); + $hash->{HELPER}{REDUCELOG} = \@a; - DbRep_Main ($hash, $opt, $prop); + Log3 ($name, 3, "DbRep $name - ################################################################"); + Log3 ($name, 3, "DbRep $name - ### new reduceLog run ###"); + Log3 ($name, 3, "DbRep $name - ################################################################"); - return; - } + DbRep_Main ($hash, $opt, $prop); + + return; } if ($hash->{HELPER}{RUNNING_BACKUP_CLIENT}) { @@ -885,7 +836,7 @@ sub DbRep_Set { return; } - if ($opt eq "index") { + if ($opt eq 'index') { DbRep_setLastCmd (@a); Log3 ($name, 3, "DbRep $name - ################################################################"); Log3 ($name, 3, "DbRep $name - ### New Index operation ###"); @@ -897,7 +848,7 @@ sub DbRep_Set { return; } - if ($opt eq "adminCredentials" && $hash->{ROLE} ne "Agent") { + if ($opt eq 'adminCredentials' && $hash->{ROLE} ne "Agent") { return "Credentials are incomplete, use username password" if (!$prop || !$prop1); my $success = DbRep_setcredentials($hash, "adminCredentials", $prop, $prop1); @@ -909,7 +860,7 @@ sub DbRep_Set { } } - if ($opt =~ /countEntries/ && $hash->{ROLE} ne "Agent") { + if ($opt eq 'countEntries' && $hash->{ROLE} ne "Agent") { my $table = $prop // "history"; DbRep_setLastCmd ($name, $opt, $table); @@ -917,7 +868,8 @@ sub DbRep_Set { return; } - elsif ($opt =~ /fetchrows/ && $hash->{ROLE} ne "Agent") { + + if ($opt eq 'fetchrows' && $hash->{ROLE} ne "Agent") { my $table = $prop // "history"; DbRep_setLastCmd ($name, $opt, $table); @@ -931,11 +883,12 @@ sub DbRep_Set { ####################################################################################################### if ($dbloghash->{HELPER}{REOPEN_RUNS} && $opt !~ /\?/) { my $ro = $dbloghash->{HELPER}{REOPEN_RUNS_UNTIL}; + Log3 ($name, 3, "DbRep $name - connection $dblogdevice to db $dbname is closed until $ro - $opt postponed"); + ReadingsSingleUpdateValue ($hash, "state", "connection $dblogdevice to $dbname is closed until $ro - $opt postponed", 1); return; } - ####################################################################################################### if ($opt =~ m/(max|min|sum|average|diff)Value/ && $hash->{ROLE} ne "Agent") { if (!AttrVal($hash->{NAME}, "reading", "")) { @@ -962,14 +915,13 @@ sub DbRep_Set { return " Set attribute 'allowDeletion' if you want to allow deletion of any database entries. Use it with care !"; } - delete $hash->{HELPER}{DELENTRIES}; DbRep_setLastCmd (@a); shift @a; shift @a; $hash->{HELPER}{DELENTRIES} = \@a if(@a); - DbRep_Main ($hash,$opt); + DbRep_Main ($hash,$opt); } elsif ($opt eq "deviceRename") { shift @a; @@ -1047,7 +999,7 @@ sub DbRep_Set { DbRep_Main ($hash, $opt, "$i_timestamp,$i_device,$i_reading,$i_value,$i_unit"); } elsif ($opt eq "exportToFile" && $hash->{ROLE} ne "Agent") { - my ($ar, $hr) = parseParams(join ' ', @a); + my ($ar, $hr) = parseParams (join ' ', @a); my $f = $ar->[2] // AttrVal($name, "expimpfile", ""); if (!$f) { @@ -1066,7 +1018,7 @@ sub DbRep_Set { DbRep_Main ($hash, $opt, $prop); } elsif ($opt eq "importFromFile" && $hash->{ROLE} ne "Agent") { - my ($ar, $hr) = parseParams(join ' ', @a); + my ($ar, $hr) = parseParams (join ' ', @a); my $f = $ar->[2] // AttrVal($name, "expimpfile", ""); if (!$f) { @@ -1184,7 +1136,7 @@ sub DbRep_Set { } } - if ($sqlcmd =~ m/^\s*delete/is && !AttrVal($hash->{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 !"; } @@ -1235,6 +1187,33 @@ sub DbRep_Set { DbRep_setLastCmd (@a); DbRep_Main ($hash, $opt, $prop); } + elsif ($opt eq 'multiCmd' && $hash->{ROLE} ne "Agent") { + my @cmd = @a; + shift @cmd; + shift @cmd; + + my $ok = 0; + my $arg = join " ", @cmd; + my $err = perlSyntaxCheck ($arg); + return $err if($err); + + if ($arg =~ m/^\{.*\}$/xs && $arg =~ m/=>/xs) { # ist als Hash geschrieben + my $av = eval $arg; + + if (ref $av eq "HASH") { + $arg = $av; + $ok = 1; + } + } + + return "The syntax of 'multiCmd' is wrong. See command reference." if(!$ok); + + delete $data{DbRep}{$name}{multicmd}; # evtl. alten multiCmd löschen + $data{DbRep}{$name}{multicmd} = $arg; + + DbRep_setLastCmd ($name, $opt); + DbRep_nextMultiCmd ($name); # Multikommandokette starten + } else { return "$setlist"; } @@ -1600,13 +1579,9 @@ sub DbRep_Attr { } } - if ($aName eq "readingPreventFromDel") { - if($cmd eq "set") { - if($aVal =~ / /) {return "Usage of $aName is wrong. Use a comma separated list of readings which are should prevent from deletion when a new selection starts.";} - $hash->{HELPER}{RDPFDEL} = $aVal; - } - else { - delete $hash->{HELPER}{RDPFDEL} if($hash->{HELPER}{RDPFDEL}); + if ($aName eq 'readingPreventFromDel' && $cmd eq 'set') { + if ($aVal =~ / /) { + return "Usage of $aName is wrong. Use a comma separated list of readings which are should prevent from deletion when a new selection starts."; } } @@ -1648,18 +1623,18 @@ sub DbRep_Attr { } } } - + if ($aName =~ /executeAfterProc|executeBeforeProc/xs) { if($cmd eq "set") { - if ($aVal =~ m/^\s*(\{.*\}|{.*|.*})\s*$/xs && $aVal !~ /{".*"}/xs) { + if ($aVal =~ m/^\s*(\{.*\}|{.*|.*})\s*$/xs && $aVal !~ /{".*"}/xs) { $aVal = $1; - + my $fdv = __DbRep_fhemDefVars (); - my ($today, $hms, $we) = ($fdv->{today}, $fdv->{hms}, $fdv->{we}); + my ($today, $hms, $we) = ($fdv->{today}, $fdv->{hms}, $fdv->{we}); my ($sec, $min, $hour) = ($fdv->{sec}, $fdv->{min}, $fdv->{hour}); my ($mday, $month, $year) = ($fdv->{mday}, $fdv->{month}, $fdv->{year}); my ($wday, $yday, $isdst) = ($fdv->{wday}, $fdv->{yday}, $fdv->{isdst}); - + eval $aVal; return $@ if ($@); } @@ -1747,36 +1722,36 @@ sub DbRep_Attr { my ($mm1, $dd1, $mm2, $dd2) = ($aVal =~ /^(\d{2})-(\d{2}) (\d{2})-(\d{2})$/); my (undef,undef,undef,$mday,$mon,$year,undef,undef,undef) = localtime (time); # Istzeit Ableitung my ($ybp, $yep); - + $year += 1900; $mon++; - + my $bdval = $mm1 * 30 + int $dd1; my $adval = $mon * 30 + int $mday; - + if ($adval >= $bdval) { $ybp = $year; $yep = $year++; } else { $ybp = $year--; - $yep = $year; + $yep = $year; } eval { my $t1 = timelocal(00, 00, 00, $dd1, $mm1-1, $ybp-1900); - my $t2 = timelocal(00, 00, 00, $dd2, $mm2-1, $yep-1900); + my $t2 = timelocal(00, 00, 00, $dd2, $mm2-1, $yep-1900); } or do { return " The Value of $aName is out of range"; - }; - + }; + delete($attr{$name}{timestamp_begin}) if ($attr{$name}{timestamp_begin}); delete($attr{$name}{timestamp_end}) if ($attr{$name}{timestamp_end}); delete($attr{$name}{timeDiffToNow}) if ($attr{$name}{timeDiffToNow}); delete($attr{$name}{timeOlderThan}) if ($attr{$name}{timeOlderThan}); return; } - + if ($aName eq "timestamp_begin" || $aName eq "timestamp_end") { my @dtas = qw(current_year_begin current_year_end @@ -1829,27 +1804,27 @@ sub DbRep_Attr { delete($attr{$name}{timeOlderThan}); delete($attr{$name}{timeYearPeriod}); } - + if ($aName =~ /ftpTimeout|timeout/) { unless ($aVal =~ /^[0-9]+$/) { return " The Value of $aName is not valid. Use only figures 0-9 without decimal places !"; } } - + if ($aName =~ /diffAccept/) { my ($sign, $daval) = DbRep_ExplodeDiffAcc ($aVal); - + if (!$daval) { return " The Value of $aName is not valid. Use only figures 0-9 without decimal places !"; } } - + if ($aName eq "readingNameMap") { unless ($aVal =~ m/^[A-Za-z\d_\.-]+$/) { return " Unsupported character in $aName found. Use only A-Z a-z _ . -"; } } - + if ($aName eq "timeDiffToNow") { unless ($aVal =~ /^[0-9]+$/ || $aVal =~ /^\s*[ydhms]:([\d]+)\s*/ && $aVal !~ /.*,.*/ ) { return "The Value of \"$aName\" isn't valid. Set simple seconds like \"86400\" or use form like \"y:1 d:10 h:6 m:12 s:20\". Refer to commandref !"; @@ -1858,7 +1833,7 @@ sub DbRep_Attr { delete($attr{$name}{timestamp_end}); delete($attr{$name}{timeYearPeriod}); } - + if ($aName eq "timeOlderThan") { unless ($aVal =~ /^[0-9]+$/ || $aVal =~ /^\s*[ydhms]:([\d]+)\s*/ && $aVal !~ /.*,.*/ ) { return "The Value of \"$aName\" isn't valid. Set simple seconds like \"86400\" or use form like \"y:1 d:10 h:6 m:12 s:20\". Refer to commandref !"; @@ -1867,7 +1842,7 @@ sub DbRep_Attr { delete($attr{$name}{timestamp_end}); delete($attr{$name}{timeYearPeriod}); } - + if ($aName eq "dumpMemlimit" || $aName eq "dumpSpeed") { unless ($aVal =~ /^[0-9]+$/) { return "The Value of $aName is not valid. Use only figures 0-9 without decimal places."; @@ -1886,11 +1861,11 @@ sub DbRep_Attr { } } } - + if ($aName eq "ftpUse") { delete($attr{$name}{ftpUseSSL}); } - + if ($aName eq "ftpUseSSL") { delete($attr{$name}{ftpUse}); } @@ -1956,7 +1931,7 @@ sub DbRep_Notify { $own_hash->{HELPER}{OLDDEV} = $evl[1]; $own_hash->{HELPER}{NEWDEV} = $evl[2]; $own_hash->{HELPER}{RENMODE} = "devren"; - + DbRep_Main($own_hash, "deviceRename"); for my $repname (devspec2array("TYPE=DbRep")) { # die Attribute "device" in allen DbRep-Devices mit der Datenbank = DB des Agenten von alten Device in neues Device ändern @@ -2409,13 +2384,16 @@ sub DbRep_Main { # Entkommentieren für Testroutine im Vordergrund # DbRep_testexit($hash); - return if (($hash->{HELPER}{RUNNING_BACKUP_CLIENT} || - $hash->{HELPER}{RUNNING_BCKPREST_SERVER} || - $hash->{HELPER}{RUNNING_RESTORE} || - $hash->{HELPER}{RUNNING_REPAIR} || - $hash->{HELPER}{RUNNING_REDUCELOG} || - $hash->{HELPER}{RUNNING_OPTIMIZE}) && - $opt !~ /dumpMySQL|restoreMySQL|dumpSQLite|restoreSQLite|optimizeTables|vacuum|repairSQLite/ ); + if (($hash->{HELPER}{RUNNING_BACKUP_CLIENT} || + $hash->{HELPER}{RUNNING_BCKPREST_SERVER} || + $hash->{HELPER}{RUNNING_RESTORE} || + $hash->{HELPER}{RUNNING_REPAIR} || + $hash->{HELPER}{RUNNING_REDUCELOG} || + $hash->{HELPER}{RUNNING_OPTIMIZE}) && + $opt !~ /dumpMySQL|restoreMySQL|dumpSQLite|restoreSQLite|optimizeTables|vacuum|repairSQLite/ ) { + DbRep_delHashtags ($hash); + return; + } DbRep_delread($hash); # Readings löschen die nicht in der Ausnahmeliste (Attr readingPreventFromDel) stehen @@ -2558,15 +2536,17 @@ sub DbRep_Main { ## eventuell bereits laufenden BlockingCall beenden ##################################################### if (exists($hash->{HELPER}{$dbrep_hmainf{$opt}{pk}}) && $hash->{ROLE} ne "Agent") { + Log3 ($name, 3, "DbRep $name - WARNING - running process $hash->{HELPER}{$dbrep_hmainf{$opt}{pk}}{pid} will be killed now to start a new operation"); + BlockingKill($hash->{HELPER}{$dbrep_hmainf{$opt}{pk}}); } # initiale Datenermittlung wie minimal Timestamp, Datenbankstrukturen, ... ############################################################################ if (!$hash->{HELPER}{MINTS} or !$hash->{HELPER}{DBREPCOL}{COLSET}) { - my $dbname = $hash->{DATABASE}; - $hash->{HELPER}{IDRETRIES} = 3 if($hash->{HELPER}{IDRETRIES} < 0); + my $dbname = $hash->{DATABASE}; + $hash->{HELPER}{IDRETRIES} = 3 if($hash->{HELPER}{IDRETRIES} < 0); Log3 ($name, 3, "DbRep $name - get initial structure information of database \"$dbname\", remaining attempts: ".$hash->{HELPER}{IDRETRIES}); @@ -2588,7 +2568,7 @@ sub DbRep_Main { my ($epoch_seconds_begin,$epoch_seconds_end,$runtime_string_first,$runtime_string_next); - if ($dbrep_hmainf{$opt} && exists &{$dbrep_hmainf{$opt}{fn}}) { + if (defined $dbrep_hmainf{$opt} && defined &{$dbrep_hmainf{$opt}{fn}}) { $params = { hash => $hash, name => $name, @@ -2623,7 +2603,13 @@ sub DbRep_Main { } if ($opt eq "delEntries" || $opt =~ /reduceLog/xi) { # Forum:#113202 - DbRep_checkValidTimeSequence ($hash, $runtime_string_first, $runtime_string_next) or return; + my ($valid, $cause) = DbRep_checkValidTimeSequence ($hash, $runtime_string_first, $runtime_string_next); + + if (!$valid) { + Log3 ($name, 2, "DbRep $name - ERROR - $cause"); + DbRep_delHashtags ($hash); + return; + } if ($opt =~ /reduceLog/xi) { ReadingsSingleUpdateValue ($hash, 'state', 'reduceLog database is running - be patient and see Logfile!', 1); @@ -2642,7 +2628,7 @@ sub DbRep_Main { $hash ); - delete $hash->{HELPER}{DELENTRIES}; + DbRep_delHashtags ($hash); if (exists $hash->{HELPER}{$dbrep_hmainf{$opt}{pk}}) { $hash->{HELPER}{$dbrep_hmainf{$opt}{pk}}{loglevel} = 5; # Forum https://forum.fhem.de/index.php/topic,77057.msg689918.html#msg689918 @@ -2685,16 +2671,16 @@ sub DbRep_createTimeArray { if (my $tap = AttrVal ($name, 'timeYearPeriod', undef)) { my ($ybp, $yep); my (undef,undef,undef,$mday,$mon,$year,undef,undef,undef) = localtime (time); # Istzeit Ableitung - + $tap =~ qr/^(\d{2})-(\d{2}) (\d{2})-(\d{2})$/p; my $mbp = $1; my $dbp = $2; my $mep = $3; my $dep = $4; - + $year += 1900; $mon++; - + my $bdval = $mbp * 30 + int $dbp; my $adval = $mon * 30 + int $mday; @@ -2704,7 +2690,7 @@ sub DbRep_createTimeArray { } else { $ybp = $year - 1; - $yep = $year; + $yep = $year; } $tsbegin = "$ybp-$mbp-$dbp 00:00:00"; @@ -2774,7 +2760,7 @@ sub DbRep_createTimeArray { $tsub = 345600 if($wday == 5); # wenn Start am "Fr" dann Korrektur -4 Tage $tsub = 432000 if($wday == 6); # wenn Start am "Sa" dann Korrektur -5 Tage $tsub = 518400 if($wday == 0); # wenn Start am "So" dann Korrektur -6 Tage - + ($rsec,$rmin,$rhour,$rmday,$rmon,$ryear) = localtime(time-$tsub); $tsbegin = strftime "%Y-%m-%d %T", localtime(timelocal(0,0,0,$rmday,$rmon,$ryear)) if(AttrVal($name, "timestamp_begin", "") eq "current_week_begin"); $tsend = strftime "%Y-%m-%d %T", localtime(timelocal(0,0,0,$rmday,$rmon,$ryear)) if(AttrVal($name, "timestamp_end", "") eq "current_week_begin"); @@ -2789,7 +2775,7 @@ sub DbRep_createTimeArray { $tadd = 172800 if($wday == 5); # wenn Start am "Fr" dann Korrektur +2 Tage $tadd = 86400 if($wday == 6); # wenn Start am "Sa" dann Korrektur +1 Tage $tadd = 0 if($wday == 0); # wenn Start am "So" keine Korrektur - + ($rsec,$rmin,$rhour,$rmday,$rmon,$ryear) = localtime(time+$tadd); $tsbegin = strftime "%Y-%m-%d %T", localtime(timelocal(59,59,23,$rmday,$rmon,$ryear)) if(AttrVal($name, "timestamp_begin", "") eq "current_week_end"); $tsend = strftime "%Y-%m-%d %T", localtime(timelocal(59,59,23,$rmday,$rmon,$ryear)) if(AttrVal($name, "timestamp_end", "") eq "current_week_end"); @@ -2804,7 +2790,7 @@ sub DbRep_createTimeArray { $tsub = 950400 if($wday == 5); # wenn Start am "Fr" dann Korrektur -11 Tage $tsub = 1036800 if($wday == 6); # wenn Start am "Sa" dann Korrektur -12 Tage $tsub = 1123200 if($wday == 0); # wenn Start am "So" dann Korrektur -13 Tage - + ($rsec,$rmin,$rhour,$rmday,$rmon,$ryear) = localtime(time-$tsub); $tsbegin = strftime "%Y-%m-%d %T", localtime(timelocal(0,0,0,$rmday,$rmon,$ryear)) if(AttrVal($name, "timestamp_begin", "") eq "previous_week_begin"); $tsend = strftime "%Y-%m-%d %T", localtime(timelocal(0,0,0,$rmday,$rmon,$ryear)) if(AttrVal($name, "timestamp_end", "") eq "previous_week_begin"); @@ -2819,7 +2805,7 @@ sub DbRep_createTimeArray { $tsub = 432000 if($wday == 5); # wenn Start am "Fr" dann Korrektur -5 Tage $tsub = 518400 if($wday == 6); # wenn Start am "Sa" dann Korrektur -6 Tage $tsub = 604800 if($wday == 0); # wenn Start am "So" dann Korrektur -7 Tage - + ($rsec,$rmin,$rhour,$rmday,$rmon,$ryear) = localtime(time-$tsub); $tsbegin = strftime "%Y-%m-%d %T", localtime(timelocal(59,59,23,$rmday,$rmon,$ryear)) if(AttrVal($name, "timestamp_begin", "") eq "previous_week_end"); $tsend = strftime "%Y-%m-%d %T", localtime(timelocal(59,59,23,$rmday,$rmon,$ryear)) if(AttrVal($name, "timestamp_end", "") eq "previous_week_end"); @@ -3589,7 +3575,7 @@ sub _DbRep_avgDailyMeanGWS { my @wsn = split " ", $runtime_string_next; my $wsft = $wsf[1] ? '_'.$wsf[1] : q{}; my $wsnt = $wsn[1] ? '_'.$wsn[1] : q{}; - + $wrstr .= $runtime_string."#".$sum."#".$wsf[0].$wsft."#".$wsn[0].$wsnt."|"; # Kombi zum Rückschreiben in die DB ### Grünlandtemperatursumme lt. https://de.wikipedia.org/wiki/Gr%C3%BCnlandtemperatursumme ### @@ -3818,6 +3804,7 @@ sub DbRep_avervalDone { ReadingsSingleUpdateValue ($hash, "state", "error", 1); DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen incl. state + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -3891,6 +3878,7 @@ sub DbRep_avervalDone { readingsEndUpdate ($hash, 1); DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen incl. state + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -4021,6 +4009,7 @@ sub DbRep_countDone { ReadingsSingleUpdateValue ($hash, "state", "error", 1); DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -4032,26 +4021,26 @@ sub DbRep_countDone { no warnings 'uninitialized'; - readingsBeginUpdate($hash); + readingsBeginUpdate ($hash); my @arr = split("\\|", $arrstr); for my $row (@arr) { - my @a = split("#", $row); - my $runtime_string = $a[0]; - my $reading = $a[1]; - $reading =~ s/[^A-Za-z\/\d_\.-]/\//g; - my $c = $a[2]; - my $rsf = $a[3]."__"; + my @a = split("#", $row); + my $runtime_string = $a[0]; + my $reading = $a[1]; + $reading =~ s/[^A-Za-z\/\d_\.-]/\//g; + my $c = $a[2]; + my $rsf = $a[3]."__"; - if (AttrVal($hash->{NAME}, "readingNameMap", "")) { - $reading_runtime_string = $rsf.AttrVal($hash->{NAME}, "readingNameMap", "")."__".$runtime_string; + if (AttrVal($name, 'readingNameMap', '')) { + $reading_runtime_string = $rsf.AttrVal($name, 'readingNameMap', '')."__".$runtime_string; } else { my ($ds,$rds) = ("",""); $ds = $device."__" if ($device); $rds = $reading."__" if ($reading); - if(AttrVal($name,"countEntriesDetail",0)) { + if (AttrVal($name, 'countEntriesDetail', 0)) { $reading_runtime_string = $rsf.$rds."COUNT_".$table."__".$runtime_string; } else { @@ -4066,6 +4055,7 @@ sub DbRep_countDone { readingsEndUpdate ($hash, 1); DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen incl. state + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -4258,6 +4248,7 @@ sub DbRep_maxvalDone { ReadingsSingleUpdateValue ($hash, "state", "error", 1); DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen incl. state + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -4300,7 +4291,8 @@ sub DbRep_maxvalDone { ReadingsBulkUpdateTime ($hash, $brt, $rt); readingsEndUpdate ($hash, 1); - DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen incl. state + DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen incl. state + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -4487,6 +4479,7 @@ sub DbRep_minvalDone { ReadingsSingleUpdateValue ($hash, "state", "error", 1); DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -4531,7 +4524,8 @@ sub DbRep_minvalDone { ReadingsBulkUpdateTime ($hash, $brt, $rt); readingsEndUpdate ($hash, 1); - DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen incl. state + DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen incl. state + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -4574,7 +4568,7 @@ sub DbRep_diffval { my @row_array; my @array; - + my $difflimit = AttrVal ($name, 'diffAccept', 20); # legt fest, bis zu welchem Wert Differenzen akzeptiert werden (Ausreißer eliminieren) my ($sign, $dlim) = DbRep_ExplodeDiffAcc ($difflimit); # $sign -> Vorzeichen (+-) @@ -4625,18 +4619,18 @@ sub DbRep_diffval { Log3 ($name, 3, "DbRep $name - WARNING - dataset has no numeric value >$vnew< and is ignored\ntimestamp >$timestamp<, device >$device<, reading >$reading<"); next; } - + if (!defined $vold) { $vold = $vnew; } - + if ($sign =~ /\+-/xs) { # sowohl positive als auch negative Abweichung auswerten $dse = $vnew - $vold; } else { $dse = ($vnew - $vold) > 0 ? ($vnew - $vold) : 0; # nur positive Abweichung auswerten } - + @sp = $runtime_string." ".$timestamp." ".$vnew." ".$dse."\n"; $vold = $vnew; @@ -4697,12 +4691,12 @@ sub DbRep_diffval { my @a = split /\s+/x, $row, 6; my $runtime_string = decode_base64($a[0]); $lastruntimestring = $runtime_string if ($i == 1); - + if(!$a[2]) { $rh{$runtime_string} = $runtime_string."|-|".$runtime_string; next; } - + my $timestamp = $a[1]."_".$a[2]; my $value = $a[3] ? $a[3] : 0; my $diff = $a[4] ? $a[4] : 0; @@ -4731,13 +4725,13 @@ sub DbRep_diffval { if(abs $diff <= $dlim) { $diff_total = $diff; } - + $rh{$runtime_string} = $runtime_string."|".$diff_total."|".$timestamp; - $ch{$runtime_string} = 1 if(defined $a[3]); + $ch{$runtime_string} = 1 if(defined $a[3]); $lval = $value; $rslval = $runtime_string; } - + if ($diff) { if(abs $diff <= $dlim) { $diff_total = $diff_total + $diff; @@ -4745,7 +4739,7 @@ sub DbRep_diffval { $rh{$runtime_string} = $runtime_string."|".$diff_total."|".$timestamp; } - + $lval = $value; $rslval = $runtime_string; $ch{$runtime_string}++ if(defined $a[3] && $i > 1); @@ -4760,11 +4754,11 @@ sub DbRep_diffval { Log3 ($name, 5, "DbRep $name - balance difference of $uediff between $rslval and $runtime_string"); $rslval = $runtime_string; - + if(abs $diff <= $dlim) { $diff_total = $diff; } - + $rh{$runtime_string} = $runtime_string."|".$diff_total."|".$timestamp; $ch{$runtime_string} = 1 if(defined $a[3]); $uediff = 0; @@ -4839,7 +4833,7 @@ sub DbRep_diffvalDone { my $ndp = AttrVal ($name, "numDecimalPlaces", $dbrep_defdecplaces); my $difflimit = AttrVal ($name, 'diffAccept', 20); # legt fest, bis zu welchem Wert Differenzen akzeptiert werden (Ausreißer eliminieren) my ($sign, $dlim) = DbRep_ExplodeDiffAcc ($difflimit); - + my $reading_runtime_string; Log3 ($name, 5, qq{DbRep $name - BlockingCall PID "$hash->{HELPER}{RUNNING_PID}{pid}" finished}); @@ -4850,7 +4844,8 @@ sub DbRep_diffvalDone { ReadingsSingleUpdateValue ($hash, "errortext", $err, 1); ReadingsSingleUpdateValue ($hash, "state", "error", 1); - DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen + DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -4908,7 +4903,8 @@ sub DbRep_diffvalDone { ReadingsBulkUpdateTime ($hash, $brt, $rt); readingsEndUpdate ($hash, 1); - DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen incl. state + DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen incl. state + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -5040,7 +5036,8 @@ sub DbRep_sumvalDone { ReadingsSingleUpdateValue ($hash, "errortext", $err, 1); ReadingsSingleUpdateValue ($hash, "state", "error", 1); - DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen + DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -5057,6 +5054,7 @@ sub DbRep_sumvalDone { readingsBeginUpdate ($hash); my @arr = split("\\|", $arrstr); + for my $row (@arr) { my @a = split("#", $row); my $runtime_string = $a[0]; @@ -5080,7 +5078,8 @@ sub DbRep_sumvalDone { ReadingsBulkUpdateTime ($hash, $brt, $rt); readingsEndUpdate ($hash, 1); - DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen incl. state + DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen incl. state + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -5162,7 +5161,8 @@ sub DbRep_del_Done { ReadingsSingleUpdateValue ($hash, "errortext", $err, 1); ReadingsSingleUpdateValue ($hash, "state", "error", 1); - DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen + DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -5194,7 +5194,8 @@ sub DbRep_del_Done { ReadingsBulkUpdateTime ($hash, $brt, $rt); readingsEndUpdate ($hash, 1); - DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen incl. state + DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen incl. state + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -5307,7 +5308,8 @@ sub DbRep_insertDone { ReadingsSingleUpdateValue ($hash, "errortext", $err, 1); ReadingsSingleUpdateValue ($hash, "state", "error", 1); - DbRep_afterproc ($hash, "insert"); # Befehl nach Procedure ausführen + DbRep_afterproc ($hash, "insert"); # Befehl nach Procedure ausführen + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -5324,7 +5326,8 @@ sub DbRep_insertDone { ReadingsBulkUpdateTime ($hash, $brt, $rt); readingsEndUpdate ($hash, 1); - DbRep_afterproc ($hash, "insert"); # Befehl nach Procedure ausführen incl. state + DbRep_afterproc ($hash, "insert"); # Befehl nach Procedure ausführen incl. state + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -5444,7 +5447,8 @@ sub DbRep_currentfillupDone { ReadingsSingleUpdateValue ($hash, "errortext", $err, 1); ReadingsSingleUpdateValue ($hash, "state", "error", 1); - DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen + DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -5467,10 +5471,11 @@ sub DbRep_currentfillupDone { ReadingsBulkUpdateTime ($hash, $brt, $rt); readingsEndUpdate ($hash, 1); - DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen incl. state - Log3 ($name, 3, "DbRep $name - Table '$hash->{DATABASE}'.'current' filled up with rows: $rowstr"); + DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen incl. state + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt + return; } @@ -5762,7 +5767,8 @@ sub DbRep_changeDone { ReadingsSingleUpdateValue ($hash, "errortext", $err, 1); ReadingsSingleUpdateValue ($hash, "state", "error", 1); - DbRep_afterproc ($hash, $renmode); # Befehl nach Procedure ausführen + DbRep_afterproc ($hash, $renmode); # Befehl nach Procedure ausführen + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -5795,8 +5801,6 @@ sub DbRep_changeDone { ReadingsBulkUpdateTime ($hash, $brt, $rt); readingsEndUpdate ($hash, 1); - DbRep_afterproc ($hash, $renmode); # Befehl nach Procedure ausführen incl. state - if ($urow != 0) { Log3 ($name, 3, "DbRep ".($hash->{ROLE} eq "Agent" ? "Agent " : "")."$name - DEVICE renamed in \"$hash->{DATABASE}\" - old: \"$old\", new: \"$new\", number: $urow ") if($renmode eq "devren"); Log3 ($name, 3, "DbRep ".($hash->{ROLE} eq "Agent" ? "Agent " : "")."$name - READING renamed in \"$hash->{DATABASE}\" - old: \"$old\", new: \"$new\", number: $urow ") if($renmode eq "readren"); @@ -5808,6 +5812,9 @@ sub DbRep_changeDone { Log3 ($name, 3, "DbRep ".($hash->{ROLE} eq "Agent" ? "Agent " : "")."$name - WARNING - old value \"$old\" not found in database \"$hash->{DATABASE}\" ") if($renmode eq "changeval"); } + DbRep_afterproc ($hash, $renmode); # Befehl nach Procedure ausführen incl. state + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt + return; } @@ -5905,7 +5912,8 @@ sub DbRep_fetchrowsDone { ReadingsSingleUpdateValue ($hash, "errortext", $err, 1); ReadingsSingleUpdateValue ($hash, "state", "error", 1); - DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen + DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -6014,7 +6022,8 @@ sub DbRep_fetchrowsDone { "done - Warning: present rows exceed specified limit, adjust attribute limit") if($nrows-$limit>0); readingsEndUpdate($hash, 1); - DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen incl. state + DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen incl. state + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -6439,7 +6448,8 @@ sub DbRep_deldoubl_Done { ReadingsSingleUpdateValue ($hash, "errortext", $err, 1); ReadingsSingleUpdateValue ($hash, "state", "error", 1); - DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen + DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -6491,7 +6501,8 @@ sub DbRep_deldoubl_Done { "done - Warning: not all items are shown, adjust attribute limit if you want see more") if($l >= $limit); readingsEndUpdate ($hash, 1); - DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen incl. state + DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen incl. state + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -6657,7 +6668,8 @@ sub DbRep_expfile_Done { ReadingsSingleUpdateValue ($hash, "errortext", $err, 1); ReadingsSingleUpdateValue ($hash, "state", "error", 1); - DbRep_afterproc ($hash, "export"); # Befehl nach Procedure ausführen + DbRep_afterproc ($hash, "export"); # Befehl nach Procedure ausführen + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -6678,7 +6690,8 @@ sub DbRep_expfile_Done { ReadingsBulkUpdateTime ($hash, $brt, $rt); readingsEndUpdate ($hash, 1); - DbRep_afterproc ($hash, "export"); # Befehl nach Procedure ausführen incl. state + DbRep_afterproc ($hash, "export"); # Befehl nach Procedure ausführen incl. state + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -6872,7 +6885,8 @@ sub DbRep_impfile_Done { ReadingsSingleUpdateValue ($hash, "errortext", $err, 1); ReadingsSingleUpdateValue ($hash, "state", "error", 1); - DbRep_afterproc ($hash, "import"); # Befehl nach Procedure ausführen + DbRep_afterproc ($hash, "import"); # Befehl nach Procedure ausführen + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -6888,10 +6902,11 @@ sub DbRep_impfile_Done { ReadingsBulkUpdateTime ($hash, $brt, $rt); readingsEndUpdate ($hash, 1); - DbRep_afterproc ($hash, "import"); # Befehl nach Procedure ausführen incl. state - Log3 ($name, 3, "DbRep $name - Number of imported datasets to $hash->{DATABASE} from file $infile: $irowdone"); + DbRep_afterproc ($hash, "import"); # Befehl nach Procedure ausführen incl. state + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt + return; } @@ -7371,7 +7386,7 @@ sub DbRep_sqlCmdDone { delete($hash->{HELPER}{RUNNING_PID}); - my $tmpsql = $data{DbRep}{$name}{sqlcache}{temp}; # SQL incl. Formatierung aus Zwischenspeicher holen + my $tmpsql = $data{DbRep}{$name}{sqlcache}{temp}; # SQL incl. Formatierung aus Zwischenspeicher holen if ($err) { readingsBeginUpdate ($hash); @@ -7381,6 +7396,7 @@ sub DbRep_sqlCmdDone { readingsEndUpdate ($hash, 1); DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -7468,6 +7484,7 @@ sub DbRep_sqlCmdDone { readingsEndUpdate ($hash, 1); DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen incl. state + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -7942,6 +7959,7 @@ sub DbRep_IndexDone { ReadingsSingleUpdateValue ($hash, "state", "error", 1); DbRep_afterproc ($hash, "index"); # Befehl nach Procedure ausführen incl. state + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -7954,6 +7972,7 @@ sub DbRep_IndexDone { readingsEndUpdate ($hash, 1); DbRep_afterproc ($hash, "index"); # Befehl nach Procedure ausführen incl. state + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -7974,8 +7993,7 @@ sub DbRep_IndexAborted { my $erread = DbRep_afterproc ($hash, "index"); # Befehl nach Procedure ausführen $erread = ", ".(split("but", $erread))[1] if($erread); - - my $state = $cause.$erread; + my $state = $cause.$erread; $dbh->disconnect() if(defined($dbh)); @@ -7985,6 +8003,8 @@ sub DbRep_IndexAborted { delete($hash->{HELPER}{RUNNING_INDEX}); + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt + return; } @@ -8246,6 +8266,7 @@ sub DbRep_OptimizeDone { ReadingsSingleUpdateValue ($hash, "state", "error", 1); DbRep_afterproc ($hash, "optimize"); # Befehl nach Procedure ausführen + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -8260,10 +8281,11 @@ sub DbRep_OptimizeDone { ReadingsBulkUpdateTime ($hash, $brt, undef); readingsEndUpdate ($hash, 1); - DbRep_afterproc ($hash, "optimize"); # Befehl nach Procedure ausführen incl. state - Log3 ($name, 3, "DbRep $name - Optimize tables finished successfully. "); + DbRep_afterproc ($hash, "optimize"); # Befehl nach Procedure ausführen incl. state + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt + return; } @@ -8328,33 +8350,33 @@ sub DbRep_mysql_DumpClientSide { my $collation = ''; my $dbcharset = ''; - if ($v[0] >= 5 || ($v[0] >= 4 && $v[1] >= 1) ) { # mysql Version >= 4.1 + if ($v[0] >= 5 || ($v[0] >= 4 && $v[1] >= 1) ) { # mysql Version >= 4.1 ($err, $sth) = DbRep_prepareExecuteQuery ($name, $dbh, qq(SHOW VARIABLES LIKE 'collation_database')); - return "$name|$err" if($err); + return "$name|$err" if($err); @ar = $sth->fetchrow; - + if ($ar[1]) { $collation = $ar[1]; - $dbcharset = (split '_', $collation, 2)[0]; - - ($err, $sth) = DbRep_prepareExecuteQuery ($name, $dbh, qq(SET NAMES "$dbcharset" COLLATE "$collation")); - return "$name|$err" if($err); - } + $dbcharset = (split '_', $collation, 2)[0]; + + ($err, $sth) = DbRep_prepareExecuteQuery ($name, $dbh, qq(SET NAMES "$dbcharset" COLLATE "$collation")); + return "$name|$err" if($err); + } } else { # mysql Version < 4.1 -> no SET NAMES available ($err, $sth) = DbRep_prepareExecuteQuery ($name, $dbh, "SHOW VARIABLES LIKE 'dbcharset'"); # get standard encoding of MySQl-Server return "$name|$err" if($err); @ar = $sth->fetchrow; - + if ($ar[1]) { $dbcharset = $ar[1]; } } Log3 ($name, 3, "DbRep $name - Characterset of collection set to $dbcharset. "); - + my $t = 0; my $r = 0; my $value = 0; @@ -8364,7 +8386,7 @@ sub DbRep_mysql_DumpClientSide { if ($dbpraefix ne "") { $query .= " LIKE '$dbpraefix%'"; - + Log3 ($name, 3, "DbRep $name - Searching for tables inside database $dbname with prefix $dbpraefix...."); } else { @@ -8461,7 +8483,7 @@ sub DbRep_mysql_DumpClientSide { } } - if ($dump_table == 1) { + if ($dump_table == 1) { ($err, $sth) = DbRep_prepareExecuteQuery ($name, $dbh, qq(SELECT count(*) FROM `$tablename`)); return "$name|$err" if($err); @@ -8472,7 +8494,7 @@ sub DbRep_mysql_DumpClientSide { $r += $db_tables{$tablename}{Rows}; push @tables, $db_tables{$tablename}{Name}; # add tablename to backuped tables $t++; - + if (!defined $db_tables{$tablename}{Update_time}) { $db_tables{$tablename}{Update_time} = 0; } @@ -8493,7 +8515,7 @@ sub DbRep_mysql_DumpClientSide { } } } - + $part .= $mysql_commentstring."EOF TABLE-INFO"; Log3 ($name, 3, "DbRep $name - Found ".(@tables)." tables with $r records."); @@ -8520,14 +8542,14 @@ sub DbRep_mysql_DumpClientSide { ## neues SQL Ausgabefile mit Header anlegen ############################################# my $sql_file = ''; - - ($err, $sql_file, $backupfile) = DbRep_NewDumpFilename ( { sql_text => $sql_text, - dump_path => $dump_path, - dbname => $dbname, + + ($err, $sql_file, $backupfile) = DbRep_NewDumpFilename ( { sql_text => $sql_text, + dump_path => $dump_path, + dbname => $dbname, time_stamp => $time_stamp - } + } ); - + if ($err) { Log3 ($name, 2, "DbRep $name - $err"); $err = encode_base64 ($err, ""); @@ -8536,27 +8558,27 @@ sub DbRep_mysql_DumpClientSide { else { Log3 ($name, 3, "DbRep $name - New dump file $sql_file was created"); } - + my $first_insert = 0; - - ## DB Einstellungen + + ## DB Einstellungen ##################### - + $sql_text = "/*!40101 SET NAMES '".$dbcharset."' */;"; $sql_text .= "\n"; $sql_text .= "SET FOREIGN_KEY_CHECKS=0;"; $sql_text .= "\n\n"; - + DbRep_WriteToDumpFile ($sql_text, $sql_file); - ## DB Create Statement einfügen - ################################# + ## DB Create Statement einfügen + ################################# ($err, $sth) = DbRep_prepareExecuteQuery ($name, $dbh, qq(SHOW CREATE DATABASE IF NOT EXISTS $dbname)); return "$name|$err" if($err); my $db_create = $sth->fetchrow; $sth->finish; - + $sql_text = $mysql_commentstring; $sql_text .= "\n"; $sql_text .= $mysql_commentstring; @@ -8564,15 +8586,15 @@ sub DbRep_mysql_DumpClientSide { $sql_text .= "\n"; $sql_text .= $mysql_commentstring; $sql_text .= "\n"; - + $sql_text .= $db_create.';'; $sql_text .= "\n"; $sql_text .= "USE `$dbname`;"; $sql_text .= "\n\n"; - + DbRep_WriteToDumpFile ($sql_text, $sql_file); - - ## jede einzelne Tabelle dumpen + + ## jede einzelne Tabelle dumpen ################################# $totalrecords = 0; $sql_text = ""; @@ -8591,11 +8613,11 @@ sub DbRep_mysql_DumpClientSide { if ($db_tables{$tablename}{Engine} ne 'VIEW' ) { $part .= "DROP TABLE IF EXISTS `$tablename`;"; - } + } else { $part .= "DROP VIEW IF EXISTS `$tablename`;"; } - + $sql_text .= $part; $sql_text .= "\n"; @@ -8604,21 +8626,21 @@ sub DbRep_mysql_DumpClientSide { @ctab = $sth->fetchrow; $sth->finish; - + $part = $ctab[1].";"; $part .= "\n"; if (length($part) < 10) { $err = "Fatal error! Couldn't read CREATE-Statement for table `$tablename`! This backup might be incomplete! Check your database for errors. MySQL-Error: ".$DBI::errstr; - + Log3 ($name, 2, "DbRep $name - $err"); - + return "$name|$err"; } else { $sql_text .= $part; } - + Log3 ($name, 5, "DbRep $name - Create-SQL found:\n$part"); if ($db_tables{$tablename}{skip_data} == 0) { @@ -8627,17 +8649,17 @@ sub DbRep_mysql_DumpClientSide { $sql_text .= "$mysql_commentstring"; $sql_text .= "Dumping data of table `$tablename`\n"; $sql_text .= "$mysql_commentstring\n"; - + $sql_text .= "/*!40000 ALTER TABLE `$tablename` DISABLE KEYS */;"; DbRep_WriteToDumpFile ($sql_text, $sql_file); - - $sql_text = ""; - ($err, $sth) = DbRep_prepareExecuteQuery ($name, $dbh, qq(SHOW FIELDS FROM `$tablename`)); + $sql_text = ""; + + ($err, $sth) = DbRep_prepareExecuteQuery ($name, $dbh, qq(SHOW FIELDS FROM `$tablename`)); return "$name|$err" if($err); - - $fieldlist = "("; + + $fieldlist = "("; while (@ar = $sth->fetchrow) { # build fieldlist $fieldlist .= "`".$ar[0]."`,"; @@ -8683,15 +8705,15 @@ sub DbRep_mysql_DumpClientSide { $sql_text = ""; } } - + $sth->finish; } - + $sql_text .= "\n/*!40000 ALTER TABLE `$tablename` ENABLE KEYS */;\n"; } ($err, $filesize) = DbRep_WriteToDumpFile ($sql_text, $sql_file); # write sql commands to file - + $sql_text = ""; if ($db_tables{$tablename}{skip_data} == 0) { @@ -8705,7 +8727,7 @@ sub DbRep_mysql_DumpClientSide { } # end - + DbRep_WriteToDumpFile("\nSET FOREIGN_KEY_CHECKS=1;\n", $sql_file); ($err, $filesize) = DbRep_WriteToDumpFile ($mysql_commentstring."EOB\n", $sql_file); @@ -8715,7 +8737,7 @@ sub DbRep_mysql_DumpClientSide { my $rt = tv_interval($st); # SQL-Laufzeit ermitteln my $compress = AttrVal ($name, "dumpCompress", 0); # Dumpfile komprimieren wenn dumpCompress=1 - + if ($compress) { ($err, $backupfile, $filesize) = DbRep_dumpCompress ($hash, $backupfile); } @@ -9017,7 +9039,8 @@ sub DbRep_DumpDone { ReadingsSingleUpdateValue ($hash, "errortext", $err, 1); ReadingsSingleUpdateValue ($hash, "state", "error", 1); - DbRep_afterproc ($hash, "dump"); # Befehl nach Procedure ausführen + DbRep_afterproc ($hash, "dump"); # Befehl nach Procedure ausführen + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -9037,10 +9060,11 @@ sub DbRep_DumpDone { ReadingsBulkUpdateValue ($hash, "background_processing_time", sprintf("%.4f",$brt)); readingsEndUpdate ($hash, 1); - DbRep_afterproc ($hash, "dump"); # Befehl nach Procedure ausführen incl. state - Log3 ($name, 3, "DbRep $name - Database dump finished successfully. "); + DbRep_afterproc ($hash, "dump"); # Befehl nach Procedure ausführen incl. state + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt + return; } @@ -9143,6 +9167,7 @@ sub DbRep_RepairDone { ReadingsSingleUpdateValue ($hash, "state", "error", 1); DbRep_afterproc ($hash, "repair"); # Befehl nach Procedure ausführen + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -9153,10 +9178,11 @@ sub DbRep_RepairDone { ReadingsBulkUpdateValue ($hash, "background_processing_time", sprintf("%.4f",$brt)); readingsEndUpdate ($hash, 1); - DbRep_afterproc ($hash, "repair"); # Befehl nach Procedure ausführen incl. state - Log3 ($name, 3, "DbRep $name - Database repair $hash->{DATABASE} finished - total time used (hh:mm:ss): ".DbRep_sec2hms($brt)); + DbRep_afterproc ($hash, "repair"); # Befehl nach Procedure ausführen incl. state + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt + return; } @@ -9311,7 +9337,7 @@ sub DbRep_mysql_RestoreClientSide { if ($bfile =~ m/.*.gzip$/) { # Dumpfile dekomprimieren wenn gzip ($err,$bfile) = DbRep_dumpUnCompress($hash,$bfile); - + if ($err) { $err = encode_base64($err,""); $dbh->disconnect; @@ -9351,7 +9377,7 @@ sub DbRep_mysql_RestoreClientSide { if (substr($line,-1) eq ";") { if ($line !~ /^INSERT INTO.*$/) { Log3 ($name, 4, "DbRep $name - do query: $line"); - + eval { $dbh->do($line); } or do { @@ -9359,10 +9385,10 @@ sub DbRep_mysql_RestoreClientSide { $err = encode_base64($e,""); close(FH); $dbh->disconnect; - + Log3 ($name, 1, "DbRep $name - last query: $line"); Log3 ($name, 1, "DbRep $name - $e"); - + return "$name|$err"; }; @@ -9405,10 +9431,10 @@ sub DbRep_mysql_RestoreClientSide { $err = encode_base64($e,""); close(FH); $dbh->disconnect; - + Log3 ($name, 1, "DbRep $name - last query: $query"); Log3 ($name, 1, "DbRep $name - $e"); - + return "$name|$err"; }; @@ -9427,7 +9453,7 @@ sub DbRep_mysql_RestoreClientSide { if (length($query) >= $i_max) { $query = $query.";"; - + eval { $dbh->do($query); } or do { @@ -9435,10 +9461,10 @@ sub DbRep_mysql_RestoreClientSide { $err = encode_base64($e,""); close(FH); $dbh->disconnect; - + Log3 ($name, 1, "DbRep $name - last query: $query"); Log3 ($name, 1, "DbRep $name - $e"); - + return "$name|$err"; }; @@ -9458,10 +9484,10 @@ sub DbRep_mysql_RestoreClientSide { $err = encode_base64($e,""); close(FH); $dbh->disconnect; - + Log3 ($name, 1, "DbRep $name - last query: $query"); Log3 ($name, 1, "DbRep $name - $e"); - + return "$name|$err"; }; @@ -9500,6 +9526,7 @@ sub DbRep_restoreDone { ReadingsSingleUpdateValue ($hash, "state", "error", 1); DbRep_afterproc ($hash, "restore", $bfile); # Befehl nach Procedure ausführen + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -9512,10 +9539,11 @@ sub DbRep_restoreDone { ReadingsBulkUpdateTime ($hash, $brt, undef); readingsEndUpdate ($hash, 1); - DbRep_afterproc ($hash, "restore", $bfile); # Befehl nach Procedure ausführen incl. state - Log3 ($name, 3, "DbRep $name - Database restore finished successfully. "); + DbRep_afterproc ($hash, "restore", $bfile); # Befehl nach Procedure ausführen incl. state + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt + return; } @@ -9650,6 +9678,7 @@ sub DbRep_syncStandbyDone { ReadingsSingleUpdateValue ($hash, "state", "error", 1); DbRep_afterproc ($hash, "syncStandby"); # Befehl nach Procedure ausführen + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -9662,6 +9691,7 @@ sub DbRep_syncStandbyDone { readingsEndUpdate ($hash, 1); DbRep_afterproc ($hash, "syncStandby"); # Befehl nach Procedure ausführen incl. state + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -10602,13 +10632,13 @@ sub DbRep_reduceLogDone { my $dbloghash = $defs{$hash->{HELPER}{DBLOGDEVICE}}; delete $hash->{HELPER}{RUNNING_REDUCELOG}; - delete $hash->{HELPER}{REDUCELOG}; if ($err) { ReadingsSingleUpdateValue ($hash, "errortext", $err, 1); ReadingsSingleUpdateValue ($hash, "state", "error", 1); DbRep_afterproc ($hash, "reduceLog"); # Befehl nach Procedure ausführen + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -10619,6 +10649,7 @@ sub DbRep_reduceLogDone { readingsEndUpdate ($hash, 1); DbRep_afterproc ($hash, "reduceLog"); # Befehl nach Procedure ausführen + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -10752,6 +10783,7 @@ sub DbRep_migCollation_Done { ReadingsSingleUpdateValue ($hash, "state", "error", 1); DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -10768,6 +10800,7 @@ sub DbRep_migCollation_Done { readingsEndUpdate ($hash, 1); DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen incl. state + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -10789,7 +10822,7 @@ sub DbRep_ParseAborted { ReadingsSingleUpdateValue ($hash, 'state', 'Abort', 0); - my $erread = DbRep_afterproc ($hash, "command"); # Befehl nach Procedure ausführen + my $erread = DbRep_afterproc ($hash, "command"); # Befehl nach Procedure ausführen $erread = ", ".(split("but", $erread))[1] if($erread); my $state = $cause.$erread; @@ -10799,6 +10832,8 @@ sub DbRep_ParseAborted { Log3 ($name, 2, "DbRep $name - Database command aborted: \"$cause\" "); + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt + return; } @@ -10816,7 +10851,7 @@ sub DbRep_reduceLogAborted { ReadingsSingleUpdateValue ($hash, 'state', 'Abort', 0); - my $erread = DbRep_afterproc ($hash, "reduceLog"); # Befehl nach Procedure ausführen + my $erread = DbRep_afterproc ($hash, "reduceLog"); # Befehl nach Procedure ausführen $erread = ", ".(split("but", $erread))[1] if($erread); my $state = $cause.$erread; @@ -10827,7 +10862,8 @@ sub DbRep_reduceLogAborted { Log3 ($name, 2, "DbRep $name - Database reduceLog aborted: \"$cause\" "); delete($hash->{HELPER}{RUNNING_REDUCELOG}); - delete $hash->{HELPER}{REDUCELOG}; + + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt return; } @@ -10858,6 +10894,8 @@ sub DbRep_restoreAborted { delete($hash->{HELPER}{RUNNING_RESTORE}); + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt + return; } @@ -10876,7 +10914,7 @@ sub DbRep_DumpAborted { ReadingsSingleUpdateValue ($hash, 'state', 'Abort', 0); - my $erread = DbRep_afterproc ($hash, "dump"); # Befehl nach Procedure ausführen + my $erread = DbRep_afterproc ($hash, "dump"); # Befehl nach Procedure ausführen $erread = ", ".(split("but", $erread))[1] if($erread); my $state = $cause.$erread; @@ -10889,6 +10927,8 @@ sub DbRep_DumpAborted { delete($hash->{HELPER}{RUNNING_BACKUP_CLIENT}); delete($hash->{HELPER}{RUNNING_BCKPREST_SERVER}); + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt + return; } @@ -10906,7 +10946,7 @@ sub DbRep_OptimizeAborted { ReadingsSingleUpdateValue ($hash, 'state', 'Abort', 0); - my $erread = DbRep_afterproc ($hash, "optimize"); # Befehl nach Procedure ausführen + my $erread = DbRep_afterproc ($hash, "optimize"); # Befehl nach Procedure ausführen $erread = ", ".(split("but", $erread))[1] if($erread); my $state = $cause.$erread; @@ -10918,6 +10958,8 @@ sub DbRep_OptimizeAborted { delete($hash->{HELPER}{RUNNING_OPTIMIZE}); + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt + return; } @@ -10940,7 +10982,7 @@ sub DbRep_RepairAborted { ReadingsSingleUpdateValue ($hash, 'state', 'Abort', 0); - my $erread = DbRep_afterproc ($hash, "repair"); # Befehl nach Procedure ausführen + my $erread = DbRep_afterproc ($hash, "repair"); # Befehl nach Procedure ausführen $erread = ", ".(split("but", $erread))[1] if($erread); my $state = $cause.$erread; @@ -10951,6 +10993,9 @@ sub DbRep_RepairAborted { Log3 ($name, 2, "DbRep $name - Database repair aborted: \"$cause\" "); delete($hash->{HELPER}{RUNNING_REPAIR}); + + DbRep_nextMultiCmd ($name); # nächstes multiCmd ausführen falls gesetzt + return; } @@ -11125,15 +11170,15 @@ sub DbRep_createSelectSql { $vf = "VALUE REGEXP '$valfilter' AND "; } } - + if ($dbmodel eq 'POSTGRESQL') { # eingefügt mit V 8.52.10 if ($rsn =~ /\d{4}-\d{2}-\d{2}\s\d{2}$/xs) { $rsn .= ':00:00'; } - + if ($rsf =~ /\d{4}-\d{2}-\d{2}\s\d{2}$/xs) { $rsf .= ':00:00'; - } + } } $sql = "SELECT $selspec FROM $table where "; @@ -11254,7 +11299,7 @@ sub DbRep_createDeleteSql { my $dbmodel = $dbloghash->{MODEL}; my $valfilter = AttrVal($name, "valueFilter", undef); # Wertefilter my $tnfull = 0; - + my ($sql,$vf,@dwc,@rwc); if($table eq "current") { @@ -11276,15 +11321,15 @@ sub DbRep_createDeleteSql { $vf = "VALUE REGEXP '$valfilter' AND "; } } - + if ($dbmodel eq 'POSTGRESQL') { # eingefügt mit V 8.52.10 if ($rsn =~ /\d{4}-\d{2}-\d{2}\s\d{2}$/xs) { $rsn .= ':00:00'; } - + if ($rsf =~ /\d{4}-\d{2}-\d{2}\s\d{2}$/xs) { $rsf .= ':00:00'; - } + } } $sql = "delete FROM $table where "; @@ -11339,14 +11384,14 @@ sub DbRep_createDeleteSql { for (@rwc) { if ($i<$len) { $sql .= "READING LIKE '$_' OR "; - } + } else { $sql .= "READING LIKE '$_' "; } - + $i++; } - + if($iranz) { $sql .= "OR "; } @@ -11379,11 +11424,11 @@ sub DbRep_createDeleteSql { #$sql .= "TIMESTAMP >= ".($rsf eq '?' ? $rsf : qq{'}.$rsf.qq{'})." AND TIMESTAMP ".($tnfull ? "<=" : "<")." ".($rsn eq '?' ? $rsn : qq{'}.$rsn.qq{'})." $addon;"; $sql .= _DbRep_timeSelspec ($rsf, $rsn, $tnfull); $sql .= "$addon;"; - } + } else { if ($dbmodel eq "POSTGRESQL") { $sql .= "true;"; - } + } else { $sql .= "1;"; } @@ -11400,9 +11445,9 @@ sub _DbRep_timeSelspec { my $rsf = shift; my $rsn = shift; my $tnfull = shift; - + my $tlspec = "TIMESTAMP >= ".($rsf eq '?' ? $rsf : qq{'}.$rsf.qq{'})." AND TIMESTAMP ".($tnfull ? "<=" : "<")." ".($rsn eq '?' ? $rsn : qq{'}.$rsn.qq{'})." "; - + return $tlspec; } @@ -12058,26 +12103,26 @@ sub DbRep_checktimeaggr { if ($aggregation ne "no") { $IsAggrSet = 1; } - + if($hash->{LASTCMD} =~ /delSeqDoublets|delDoublets/) { $aggregation = ($aggregation eq "no") ? "day" : $aggregation; # wenn Aggregation "no", für delSeqDoublets immer "day" setzen $IsAggrSet = 1; } - + if($hash->{LASTCMD} =~ /averageValue/ && AttrVal($name, "averageCalcForm", "avgArithmeticMean") =~ /avgDailyMeanGWS/x) { $aggregation = "day"; # für Tagesmittelwertberechnung des deutschen Wetterdienstes immer "day" $IsAggrSet = 1; } - + if($hash->{LASTCMD} =~ /^sql|delEntries|fetchrows|deviceRename|readingRename|tableCurrentFillup|reduceLog|\breadingsDifferenceByTimeDelta\b/) { $IsAggrSet = 0; $aggregation = "no"; } - + if($hash->{LASTCMD} =~ /deviceRename|readingRename/) { $IsTimeSet = 0; } - + if($hash->{LASTCMD} =~ /changeValue/) { if($hash->{HELPER}{COMPLEX}) { $IsAggrSet = 1; @@ -12088,7 +12133,7 @@ sub DbRep_checktimeaggr { $aggregation = "no"; } } - + if($hash->{LASTCMD} =~ /syncStandby/ ) { if($aggregation !~ /minute|hour|day|week/) { $aggregation = "day"; @@ -12240,17 +12285,19 @@ return; # autoForward Attribut: # # { -# " => "" [=> ]", -# " => "" [=> ]", +# "" => "" [=> ]", +# "" => "" [=> ]", # .... # } #################################################################################################### sub DbRep_autoForward { - my ($name,$reading,$value) = @_; + my $name = shift; + my $reading = shift; + my $value = shift; + my $hash = $defs{$name}; - my $av = AttrVal($name, "autoForward", ""); - my ($sr,$af); - + my $av = AttrVal ($name, 'autoForward', ''); + return if(!$av); $av =~ m/^\{(.*)\}/s; @@ -12259,16 +12306,19 @@ sub DbRep_autoForward { my @a = split ",", $av; $av = "{ "; + my $i = 0; - - for (@a) { - $av .= "\"$i\" => \"$_\","; + for my $elm (@a) { + $av .= qq("$i" => "$elm",); $i++; } $av .= " }"; + my ($sr,$af); + $af = eval $av; + if($@ || ref($af) ne "HASH") { Log3($name, 2, "$name - Values specified in attribute \"autoForward\" are not defined as HASH ... exiting !") if(ref($af) ne "HASH"); Log3($name, 2, "$name - Error while evaluate: ".$@) if($@); @@ -12276,11 +12326,12 @@ sub DbRep_autoForward { } for my $key (keys %{$af}) { - my ($srr,$ddev,$dr) = split("=>", $af->{$key}); - $srr = DbRep_trim($srr) if($srr); - $ddev = DbRep_trim($ddev) if($ddev); - $dr = DbRep_trim($dr) if($dr); + my ($srr, $ddev, $dr) = split("=>", $af->{$key}); + $ddev = DbRep_trim ($ddev) if($ddev); next if(!$ddev); + + $srr = DbRep_trim ($srr) if($srr); + $dr = DbRep_trim ($dr) if($dr); if(!$defs{$ddev}) { # Vorhandensein Destination Device prüfen Log3($name, 2, "$name - WARNING - Forward reading \"$reading\" not possible, device \"$ddev\" doesn't exist"); @@ -12288,13 +12339,14 @@ sub DbRep_autoForward { } if(!$srr || $reading !~ /^$srr$/) { - # Log3($name, 4, "$name - Reading \"$reading\" doesn't match autoForward-Regex: ".($srr?$srr:"")." - no forward to \"$ddev\" "); + # Log3 ($name, 4, "$name - Reading \"$reading\" doesn't match autoForward-Regex: ".($srr?$srr:"")." - no forward to \"$ddev\" "); next; } eval { $sr = $srr }; - $dr = $dr ? $dr : ($sr ne ".*") ? $sr : $reading; # Destination Reading = Source Reading wenn Destination Reading nicht angegeben - $dr = makeReadingName($dr); # Destination Readingname validieren / entfernt aus dem übergebenen Readingname alle ungültigen Zeichen und ersetzt diese durch einen Unterstrich "_" + $dr = $dr ? $dr : ($sr !~ /\.\*/xs) ? $sr : $reading; # Destination Reading = Source Reading wenn Destination Reading nicht angegeben + $dr = makeReadingName ($dr); # Destination Readingname validieren / entfernt aus dem übergebenen Readingname alle ungültigen Zeichen und ersetzt diese durch einen Unterstrich "_" + Log3($name, 4, "$name - Forward reading \"$reading\" to \"$ddev:$dr\" "); CommandSetReading(undef, "$ddev $dr $value"); @@ -12585,7 +12637,7 @@ sub DbRep_beforeproc { if($fn) { Log3 ($name, 3, "DbRep $name - execute command before $cmd: '$fn' "); - + my $err = _DbRep_procCode ($hash, $fn); if ($err) { @@ -12617,12 +12669,12 @@ sub DbRep_afterproc { if($fn) { Log3 ($name, 3, "DbRep $name - execute command after $cmd: '$fn' "); - + $err = _DbRep_procCode ($hash, $fn); - + if ($err) { Log3 ($name, 2, qq{DbRep $name - command message after $cmd: >$err<}); - + $erread = $sval eq 'error' ? $sval : qq(WARNING - $cmd finished, but message after command appeared); ReadingsSingleUpdateValue ($hash, 'after_'.$cmd.'_message', $err, 1); @@ -12647,67 +12699,67 @@ return ''; } ################################################################################### -# Befehl oder Code prozessieren +# Befehl oder Code prozessieren ################################################################################### sub _DbRep_procCode { my $hash = shift; my $fn = shift; - + my $err = q{}; my $name = $hash->{NAME}; $fn =~ s/\s*#.*//g; # Kommentare entfernen $fn = join ' ', split /\s+/sx, $fn; # Funktion serialisieren - - if ($fn =~ m/^\s*(\{.*\})\s*$/xs) { # unnamed Funktion direkt mit {...} + + if ($fn =~ m/^\s*(\{.*\})\s*$/xs) { # unnamed Funktion direkt mit {...} $fn = $1; - + my $fdv = __DbRep_fhemDefVars (); - my ($today, $hms, $we) = ($fdv->{today}, $fdv->{hms}, $fdv->{we}); + my ($today, $hms, $we) = ($fdv->{today}, $fdv->{hms}, $fdv->{we}); my ($sec, $min, $hour) = ($fdv->{sec}, $fdv->{min}, $fdv->{hour}); my ($mday, $month, $year) = ($fdv->{mday}, $fdv->{month}, $fdv->{year}); my ($wday, $yday, $isdst) = ($fdv->{wday}, $fdv->{yday}, $fdv->{isdst}); - + eval $fn; $err = $@ if($@); } else { $err = AnalyzeCommandChain (undef, $fn); } - + return $err; } ################################################################################### # FHEM Standardvariablen bereitstellen identisch fhem.pl sub AnalyzePerlCommand -# AnalyzePerlCommand kann nicht verwendet werden ohne die bisherige Syntax +# AnalyzePerlCommand kann nicht verwendet werden ohne die bisherige Syntax # (Übergabe von $name, $hash) zu brechen. ################################################################################### -sub __DbRep_fhemDefVars { +sub __DbRep_fhemDefVars { my ($sec, $min, $hour, $mday, $month, $year, $wday, $yday, $isdst) = localtime (gettimeofday()); - $month++; + $month++; $year += 1900; my $today = sprintf '%04d-%02d-%02d', $year, $month, $mday; my $hms = sprintf '%02d:%02d:%02d', $hour, $min, $sec; my $we = IsWe (undef, $wday); - + my $retvals = { sec => $sec, min => $min, hour => $hour, mday => $mday, - + month => $month, year => $year, wday => $wday, yday => $yday, isdst => $isdst, - + today => $today, hms => $hms, we => $we }; - + return $retvals; } @@ -12782,15 +12834,17 @@ return; # Readings löschen die nicht in der Ausnahmeliste (Attr readingPreventFromDel) stehen #################################################################################################### sub DbRep_delread { - my ($hash,$shutdown) = @_; + my $hash = shift; + my $shutdown = shift; + my $name = $hash->{NAME}; my @allrds = keys%{$defs{$name}{READINGS}}; - my $featurelevel = AttrVal("global","featurelevel",99.99); + my $featurelevel = AttrVal ('global', 'featurelevel', 99.99); if($shutdown) { my $do = 0; - for my $key(@allrds) { # Highlighted Readings löschen und save statefile wegen Inkompatibilitär beim Restart + for my $key(@allrds) { # Highlighted Readings löschen und save statefile wegen Inkompatibilitär beim Restart if($key =~ /{HELPER}{RDPFDEL}) if($hash->{HELPER}{RDPFDEL}); + my @rdpfdel = split ",", AttrVal ($name, 'readingPreventFromDel', ''); if(@rdpfdel) { for my $key(@allrds) { my $dodel = 1; for my $rdpfdel(@rdpfdel) { - if($key =~ /$rdpfdel/ || $key =~ /\bstate\b|\bassociatedWith\b/) { + if($key =~ /^$rdpfdel$/xs || $key =~ /\bstate\b|\bassociatedWith\b/) { $dodel = 0; } } @@ -12843,7 +12896,7 @@ sub DbRep_NewDumpFilename { my $dump_path = $paref->{dump_path}; my $dbname = $paref->{dbname}; my $time_stamp = $paref->{time_stamp}; - + my $part = ""; my $sql_file = $dump_path.$dbname."_".$time_stamp.$part.".sql"; my $backupfile = $dbname."_".$time_stamp.$part.".sql"; @@ -12925,6 +12978,75 @@ sub DbRep_setcredentials { return ($success); } +###################################################################################### +# das erste bzw. nächste Kommando des übergebenen multiCmd ausführen +###################################################################################### +sub DbRep_nextMultiCmd { + my $name = shift; + + return if(!defined $data{DbRep}{$name}{multicmd} || !scalar keys %{$data{DbRep}{$name}{multicmd}}); + + my @mattr = qw(aggregation + autoForward + averageCalcForm + timestamp_begin + timestamp_end + timeDiffToNow + timeOlderThan + timeYearPeriod + device + reading + readingNameMap + optimizeTablesBeforeDump + ); + + for my $da (@mattr) { + CommandDeleteAttr (undef, "-silent $name $da") if(defined AttrVal($name, $da, undef)); + } + + pop (@mattr); # optimizeTablesBeforeDump aus Liste entfernen -> Attr darf nicht gesetzt werden! + + my $ok = 0; + my $verb = 4; + my $cmd = ''; + my $la = ''; + + for my $k (sort{$a<=>$b} keys %{$data{DbRep}{$name}{multicmd}}) { + my $mcmd = delete $data{DbRep}{$name}{multicmd}{$k}; + + for my $sa (@mattr) { + next if(!defined $mcmd->{$sa}); + + CommandAttr (undef, "-silent $name $sa $mcmd->{$sa}"); + } + + $cmd = (split " ", $mcmd->{cmd})[0]; + + if (defined $dbrep_hmainf{$cmd}) { + $cmd = $mcmd->{cmd}; + $la = 'start'; + $ok = 1; + } + else { + $verb = 1; + $la = "don't contain a valid command -> skip '$cmd'"; + } + + Log3 ($name, $verb, "DbRep $name - multiCmd index >$k< $la"); + + last; # immer nur den ersten verbliebenen Eintrag abarbeiten + } + + if ($ok) { + CommandSet (undef, "$name $cmd"); + } + else { + DbRep_nextMultiCmd ($name); # nächsten Eintrag abarbeiten falls Kommando ungültig + } + +return; +} + ###################################################################################### # Username / Paßwort abrufen # $cre = "adminCredentials" -> Credentials für Datenbank root-Zugriff @@ -12969,18 +13091,15 @@ return ($success, $username, $passwd); # anlegen Keyvalue-File für DbRep wenn nicht vorhanden #################################################################################################### sub DbRep_createCmdFile { - my $hash = shift; + my $hash = shift; - my $param = { - FileName => $dbrep_fName, - #ForceType => "file", - }; + my $param = {FileName => $dbrep_fName}; my @new; - push(@new, "# This file is auto generated from 93_DbRep.pm", - "# Please do not modify, move or delete it.", - ""); + push (@new, "# This file is auto generated from 93_DbRep.pm", + "# Please do not modify, move or delete it.", + ""); -return FileWrite($param, @new); +return FileWrite ($param, @new); } #################################################################################################### @@ -12989,14 +13108,10 @@ return FileWrite($param, @new); sub DbRep_setCmdFile { my ($key,$value,$hash) = @_; - my $param = { - FileName => $dbrep_fName, - #ForceType => "file", - }; + my $param = {FileName => $dbrep_fName}; + my ($err, @old) = FileRead ($param); - my ($err, @old) = FileRead($param); - - DbRep_createCmdFile($hash) if($err); + DbRep_createCmdFile ($hash) if($err); my @new; my $fnd; @@ -13013,7 +13128,7 @@ sub DbRep_setCmdFile { push @new, "$key:$value" if(!$fnd && defined($value)); - $err = FileWrite($param, @new); + $err = FileWrite ($param, @new); return $err; } @@ -13022,14 +13137,10 @@ return $err; # Leseroutine aus DbRep Keyvalue-File #################################################################################################### sub DbRep_getCmdFile { - my $key = shift; + my $key = shift; - my $param = { - FileName => $dbrep_fName, - #ForceType => "file", - }; - - my ($err, @l) = FileRead($param); + my $param = {FileName => $dbrep_fName}; + my ($err, @l) = FileRead ($param); return ($err, '') if($err); for my $line (@l) { @@ -14135,6 +14246,18 @@ sub DbRep_WriteToDB { return ($err,$irowdone,$wrt); } +################################################################ +# gesetzte Hashtags (Steuerbits) löschen +################################################################ +sub DbRep_delHashtags { + my $hash = shift; + + my $rdltag = delete $hash->{HELPER}{REDUCELOG}; + my $deetag = delete $hash->{HELPER}{DELENTRIES}; + +return ($rdltag, $deetag); +} + ################################################################ # check ob primary key genutzt wird ################################################################ @@ -14172,29 +14295,32 @@ sub DbRep_checkValidTimeSequence { my $runtime_string_next = shift; my $valid = 1; + my $cause = ''; return $valid if(!$runtime_string_first || !$runtime_string_next); - + my $mint = $hash->{HELPER}{MINTS} // '1970-01-01 01:00:00'; # Time des 1. Datensatzes in der DB my ($yyyy1, $mm1, $dd1, $hh1, $min1, $sec1) = $runtime_string_first =~ /(\d+)-(\d+)-(\d+)\s(\d+):(\d+):(\d+)/x; my ($yyyy2, $mm2, $dd2, $hh2, $min2, $sec2) = $runtime_string_next =~ /(\d+)-(\d+)-(\d+)\s(\d+):(\d+):(\d+)/x; my ($yyyy3, $mm3, $dd3, $hh3, $min3, $sec3) = $mint =~ /(\d+)-(\d+)-(\d+)\s(\d+):(\d+):(\d+)/x; - + my $nthants = fhemTimeLocal($sec1, $min1, $hh1, $dd1, $mm1-1, $yyyy1-1900); my $othants = fhemTimeLocal($sec2, $min2, $hh2, $dd2, $mm2-1, $yyyy2-1900); my $mints = fhemTimeLocal($sec3, $min3, $hh3, $dd3, $mm3-1, $yyyy3-1900); if ($mints > $othants) { - ReadingsSingleUpdateValue ($hash, 'state', 'No data found in specified time range', 1); + ReadingsSingleUpdateValue ($hash, 'state', 'The Timestamp of the oldest dataset is newer than the specified time range', 1); $valid = 0; + $cause = "The Timestamp of the oldest dataset ($mints) is newer than specified end time ($othants)"; } elsif ($nthants > $othants) { ReadingsSingleUpdateValue ($hash, 'state', "ERROR - Wrong time limits. The 'nn' (days newer than) option must be greater than the 'no' (older than) one!", 1); $valid = 0; + $cause = "Wrong time limits. The time stamps for start and end are logically wrong for each other."; } -return $valid; +return ($valid, $cause); } ################################################################ @@ -14215,7 +14341,7 @@ return $val; ################################################################ sub DbRep_ExplodeDiffAcc { my $val = shift // q{empty}; - + my $sign = q{}; my $daval = q{}; @@ -14261,9 +14387,9 @@ return $val; sub DbRep_setLastCmd { my (@vars) = @_; - my $name = shift(@vars); + my $name = shift @vars; my $hash = $defs{$name}; - $hash->{LASTCMD} = join(" ",@vars); + $hash->{LASTCMD} = join " ", @vars; return; } @@ -14730,21 +14856,22 @@ return; Currently following set-commands are included. They are used to trigger the evaluations and define the evaluation option option itself. The criteria of searching database content and determine aggregation is carried out by setting several attributes.

- + Note:
If you are in detail view it could be necessary to refresh the browser to see the result of operation as soon in DeviceOverview section "state = done" will be shown.

      + +
    • adminCredentials <User> <Passwort>

      -
    • adminCredentials <User> <Passwort> - - Save a user / password for the privileged respectively administrative database access. - The user is required for database operations which has to be executed by a privileged user. - Please see also attribute useAdminCredentials.
      - (only valid if database type is MYSQL and DbRep-type "Client") - -

    • + Save a user / password for the privileged respectively administrative database access. + The user is required for database operations which has to be executed by a privileged user. + Please see also attribute useAdminCredentials. + +
      +
    • averageValue [display | writeToDB | writeToDBSingle | writeToDBSingleStart | writeToDBInTime]

      Calculates an average value of the database field "VALUE" in the time limits @@ -14801,10 +14928,14 @@ return;


    - -
  • cancelDump - stops a running database dump.

  • + +
  • cancelDump

    + + Stops a running database dump. +
  • +
  • changeValue old="<old String>" new="<new String>"

    @@ -14862,34 +14993,36 @@ return;
  • -
  • countEntries [history|current] - provides the number of table entries (default: history) between time period set - by time.* -attributes if set. - If time.* attributes not set, all entries of the table will be count. - The device and reading can be used to - limit the evaluation.
    - By default the summary of all counted datasets, labeled by "ALLREADINGS", will be created. - If the attribute "countEntriesDetail" is set, the number of every reading - is reported additionally.

    + +
  • countEntries [history|current]

    - The relevant attributes for this function are:

    + Provides the number of table entries (default: history) between time period set by + time.* -attributes if set. + If time.* attributes not set, all entries of the table will be count. + The device and reading can be used to limit the + evaluation.
    + By default the summary of all counted datasets, labeled by "ALLREADINGS", will be created. If the attribute + countEntriesDetail is set, the number of every reading is reported + additionally.

    -
      - - - - - - - - - - -
      aggregation : aggregatiion/grouping of time intervals
      countEntriesDetail : detailed report the count of datasets (per reading)
      device : include or exclude <device> from selection
      reading : include or exclude <reading> from selection
      executeBeforeProc : execution of FHEM command (or Perl-routine) before operation
      executeAfterProc : execution of FHEM command (or Perl-routine) after operation
      time.* : a number of attributes to limit selection by time
      valueFilter : an additional REGEXP to control the record selection. The REGEXP is applied to the database field 'VALUE'.
      -
    -
    -
    + The relevant attributes for this function are:

    -
  • +
      + + + + + + + + + + +
      aggregation : aggregatiion/grouping of time intervals
      countEntriesDetail : detailed report the count of datasets (per reading)
      device : include or exclude <device> from selection
      reading : include or exclude <reading> from selection
      executeBeforeProc : execution of FHEM command (or Perl-routine) before operation
      executeAfterProc : execution of FHEM command (or Perl-routine) after operation
      time.* : a number of attributes to limit selection by time
      valueFilter : an additional REGEXP to control the record selection. The REGEXP is applied to the database field 'VALUE'.
      +
    + +
    +
  • delDoublets [adviceDelete | delete] - show respectively delete duplicate/multiple datasets. Therefore the fields TIMESTAMP, DEVICE, READING and VALUE of records are compared.
    @@ -14953,46 +15086,48 @@ return;
  • -
  • delEntries [<no>[:<nn>]] - deletes all database entries or only the database entries specified by attributes - device and/or reading.

    + +
  • delEntries [<no>[:<nn>]]

    - The time limits are considered according to the available time.*-attributes:

    + Deletes all database entries or only the database entries specified by attributes + device and/or reading.

    -
      - "timestamp_begin" is set -> deletes db entries from this timestamp until current date/time
      - "timestamp_end" is set -> deletes db entries until this timestamp
      - both Timestamps are set -> deletes db entries between these timestamps
      - "timeOlderThan" is set -> delete entries older than current time minus "timeOlderThan"
      - "timeDiffToNow" is set -> delete db entries from current time minus "timeDiffToNow" until now
      -
    + The time limits are considered according to the available time.*-attributes:

    -
    - Due to security reasons the attribute allowDeletion needs to be set to unlock the - delete-function.
    - Time limits (days) can be specified as an option. In this case, any time.*-attributes set are - overmodulated. - Records older than <no> days and (optionally) newer than - <nn> days are considered. -

    +
      + "timestamp_begin" is set -> deletes db entries from this timestamp until current date/time
      + "timestamp_end" is set -> deletes db entries until this timestamp
      + both Timestamps are set -> deletes db entries between these timestamps
      + "timeOlderThan" is set -> delete entries older than current time minus "timeOlderThan"
      + "timeDiffToNow" is set -> delete db entries from current time minus "timeDiffToNow" until now
      +
    - The relevant attributes to control function changeValue delEntries are:

    +
    + Due to security reasons the attribute allowDeletion needs to be set to unlock the + delete-function.
    + Time limits (days) can be specified as an option. In this case, any time.*-attributes set are + overmodulated. + Records older than <no> days and (optionally) newer than + <nn> days are considered. +

    -
      - - - - - - - - -
      allowDeletion : unlock the delete function
      device : include or exclude <device> from selection
      reading : include or exclude <reading> from selection
      time.* : a number of attributes to limit selection by time
      executeBeforeProc : execute a FHEM command (or Perl-routine) before start of delEntries
      executeAfterProc : execute a FHEM command (or Perl-routine) after delEntries is finished
      -
    -
    -
    + The relevant attributes to control function changeValue delEntries are:

    -
  • -
    +
      + + + + + + + + +
      allowDeletion : unlock the delete function
      device : include or exclude <device> from selection
      reading : include or exclude <reading> from selection
      time.* : a number of attributes to limit selection by time
      executeBeforeProc : execute a FHEM command (or Perl-routine) before start of delEntries
      executeAfterProc : execute a FHEM command (or Perl-routine) after delEntries is finished
      +
    +
    +
    + +
  • delSeqDoublets [adviceRemain | adviceDelete | delete] - show respectively delete identical sequentially datasets. Therefore Device,Reading and Value of the sequentially datasets are compared. @@ -15084,38 +15219,39 @@ return;

    -
  • deviceRename <old_name>,<new_name> - - renames the device name of a device inside the connected database (Internal DATABASE). - The devicename will allways be changed in the entire database. Possibly set time limits or restrictions by - device and/or reading will not be considered.

    + +
  • deviceRename <old_name>,<new_name>

    -
      - Example:
      - set <name> deviceRename ST_5000,ST5100
      - # The amount of renamed device names (datasets) will be displayed in reading "device_renamed".
      - # If the device name to be renamed was not found in the database, a WARNUNG will appear in reading "device_not_renamed".
      - # Appropriate entries will be written to Logfile if verbose >= 3 is set. -
    -

    + Renames the device name of a device inside the connected database (Internal DATABASE). + The devicename will allways be changed in the entire database. Possibly set time limits or restrictions by + device and/or reading will not be considered.

    - Note:
    - Even though the function itself is designed non-blocking, make sure the assigned DbLog-device - is operating in asynchronous mode to avoid FHEMWEB from blocking.
    -
    +
      + Example:
      + set <name> deviceRename ST_5000,ST5100
      + # The amount of renamed device names (datasets) will be displayed in reading "device_renamed".
      + # If the device name to be renamed was not found in the database, a WARNUNG will appear in reading "device_not_renamed".
      + # Appropriate entries will be written to Logfile if verbose >= 3 is set. +
    +

    - The relevant attributes to control this function are:

    + Note:
    + Even though the function itself is designed non-blocking, make sure the assigned DbLog-device + is operating in asynchronous mode to avoid FHEMWEB from blocking.
    +
    -
      - - - - -
      executeBeforeProc : execution of FHEM command (or Perl-routine) before operation
      executeAfterProc : execution of FHEM command (or Perl-routine) after operation
      -
    -
    -
    + The relevant attributes to control this function are:

    -
  • +
      + + + + +
      executeBeforeProc : execution of FHEM command (or Perl-routine) before operation
      executeAfterProc : execution of FHEM command (or Perl-routine) after operation
      +
    +
    +
    +
  • diffValue [display | writeToDB] - calculates the difference of database column "VALUE" in the given time period. (see also the several time*-attributes).
    @@ -15390,9 +15526,13 @@ return; transfer in topic "dumpMySQL".


  • -
  • eraseReadings - deletes all created readings in the device, except reading "state" and readings, which are - contained in exception list defined by attribute "readingPreventFromDel". -

  • + +
  • eraseReadings

    + + Deletes all created readings in the device, except reading "state" and readings, which are + contained in exception list defined by attribute readingPreventFromDel. +
  • +
  • exportToFile [</path/file>] [MAXLINES=<lines>] - exports DB-entries to a file in CSV-format of time period specified by time attributes.

    @@ -15719,6 +15859,72 @@ return;

  • + +
  • multiCmd {<Befehl-Hash>}

    + + Executes several set commands sequentially in a definable order.
    + The commands and certain modifiable attributes that are relevant for the commands are transferred in a + hash.
    + The commands to be executed (key cmd) and the attributes to be set for them are defined via keys in the + transferred hash. The order in which the commands are processed is determined via the command index in the + hash. +

    + + Attribute keys that can be defined in the hash are:
    + + +
    + + Example of the definition of a command hash:
    + +
      +
      +        {
      +          1  => { timestamp_begin => '2023-12-17 00:00:00', 
      +                  timestamp_end   => '2023-12-17 01:00:00', 
      +                  device          => 'SMA_Energymeter', 
      +                  reading         => 'Einspeisung_Wirkleistung_Zaehler', 
      +                  cmd             => 'countEntries history'
      +                },
      +          2  => { timestamp_begin => '2023-12-15 11:00:00', 
      +                  timestamp_end   => 'previous_day_end', 
      +                  device          => 'SMA_Energymeter', 
      +                  reading         => 'Einspeisung_Wirkleistung_Zaehler', 
      +                  cmd             => 'countEntries' 
      +                },
      +          3  => { timeDiffToNow   => 'd:2',
      +                  readingNameMap  => 'COUNT',
      +                  autoForward     => '{ ".*COUNT.*" => "Dum.Rep.All" }',
      +                  device          => 'SMA_%,MySTP.*',
      +                  reading         => 'etotal,etoday,Ein% EXCLUDE=%Wirkleistung', 
      +                  cmd             => 'countEntries history' 
      +                },
      +          4  => { timeDiffToNow   => 'd:2',
      +                  readingNameMap  => 'SUM',
      +                  autoForward     => '{ ".*SUM.*" => "Dum.Rep.All" }',
      +                  device          => 'SMA_%,MySTP.*',
      +                  reading         => 'etotal,etoday,Ein% EXCLUDE=%Wirkleistung', 
      +                  cmd             => 'sumValue' 
      +                },
      +          5  => { cmd             => 'sqlCmd select count(*) from current'
      +                },
      +        }
      +      
      +
    +
  • +
    +
  • optimizeTables [showInfo | execute]

    @@ -15778,7 +15984,7 @@ return;
  • reduceLog [<no>[:<nn>]] [mode] [EXCLUDE=device1:reading1,device2:reading2,...] [INCLUDE=device:reading]

    - + Reduces historical data sets.

    Operation without specifying command line operators

    @@ -15892,24 +16098,24 @@ return; Furthermore it is strongly recommended to create the standard INDEX 'Search_Idx' in the table 'history' !
    The processing of this command may take an extremely long time (without INDEX).

    - -
  • + +
    -
  • repairSQLite [sec]

    - +
  • repairSQLite [sec]

    + Repairs a corrupted SQLite database.

    - + A corruption is usally existent when the error message "database disk image is malformed" appears in reading "state" of the connected DbLog-device. If the command was started, the connected DbLog-device will firstly disconnected from the database for 10 hours (36000 seconds) automatically (breakup time). After the repair is finished, the DbLog-device will be connected to the (repaired) database immediately.
    As an argument the command can be completed by a differing breakup time (in seconds).
    - The corrupted database is saved as <database>.corrupt in same directory. + The corrupted database is saved as <database>.corrupt in same directory.

    - + Relevant attributes are:
      @@ -15931,15 +16137,15 @@ return; Note:
      It can't be guaranteed, that the repair attempt proceed successfully and no data loss will result. Depending from corruption severity data loss may occur or the repair will fail even though - no error appears during the repair process. Please make sure a valid backup took place ! + no error appears during the repair process. Please make sure a valid backup took place !

      - - + +
      - +
    • restoreMySQL <File> - restore a database from serverSide- or clientSide-Dump.

      - + The function provides a drop-down-list of files which can be used for restore.

      Usage of serverSide-Dumps
      @@ -15966,7 +16172,7 @@ return; The database user needs rights for database management, e.g.:
      CREATE, ALTER, INDEX, DROP, SHOW VIEW, CREATE VIEW

      - + Relevant attributes are:

      - +

    • restoreSQLite <File>.sqlitebkp[.gzip]

      - + Restores a backup of SQLite database.
      The function provides a drop-down-list of files which can be used for restore. The data stored in the current database are deleted respectively overwritten. All files with extension "sqlitebkp[.gzip]" and if the filename is beginning with the name of the connected database - will are listed. + will are listed.

      - + Relevant attributes are:
      -
      - +
      +

    • @@ -16109,7 +16315,7 @@ return;
    • sqlCmdHistory

      - + If activated with the attribute sqlCmdHistoryLength, a stored SQL statement can be selected from a list and executed.
      The SQL cache is automatically saved when FHEM is closed and restored when the system is started.
      @@ -16148,7 +16354,7 @@ return;
    • sqlSpecial

      - + This function provides a drop-down list with a selection of prepared reportings.
      The statements result is depicted in reading "SqlResult". The result can be formatted by attribute sqlResultFormat @@ -16186,13 +16392,13 @@ return;


    - +

  • sumValue [display | writeToDB | writeToDBSingle | writeToDBInTime]

    - + Calculates the total values of the database field "VALUE" in the time limits of the possible time.*-attributes.

    @@ -16244,11 +16450,11 @@ return;

    -
  • +
  • syncStandby <DbLog-Device Standby>

    - + Datasets of the connected database (source) are transmitted into another database (Standby-database).
    Here the "<DbLog-Device Standby>" is the DbLog-Device what is connected to the @@ -16277,12 +16483,12 @@ return;

    -
  • +
  • tableCurrentFillup

    - + The current-table will be filled u with an extract of the history-table.
    The attributes for limiting time and device, reading are considered.
    Thereby the content of the extract can be affected.
    @@ -16308,7 +16514,7 @@ return;
  • tableCurrentPurge

    - + Deletes the content of current-table.
    There are no limits, e.g. by attributes timestamp_begin, timestamp_end, device or reading considered. @@ -16574,42 +16780,61 @@ sub dbval {
        -
      • aggregation - Aggregation of Device/Reading-selections. Possible values are hour, day, week, month, year and "no". - Delivers e.g. the count of database entries for a day (countEntries), Summation of - difference values of a reading (sumValue) and so on. Using aggregation "no" (default) an - aggregation don't happens but the output contains all values of Device/Reading in the selected time period.

      • - +
      • aggregation

        + + Creation of the function results in time slices within the selection period. +

        + +
          + + + + + + + + + +
          no - No aggregation (default)
          minute - the function results are summarized per minute
          hour - the function results are summarized per hour
          day - the function results are summarized per calendar day
          week - the function results are summarized per calendar week
          month - the function results are summarized per calendar month
          year - the functional results are summarized per calendar year
          +
        +
      • +
        + -
      • allowDeletion - unlocks the delete-function

      • +
      • allowDeletion

        + + Enables the delete function of the module. +
      • +
        -
      • autoForward - if activated, the result threads of a function are transferred to one or more devices.
        - The definition takes the form:
        + +
      • autoForward

        + If activated, the result threads of a function are transferred to one or more devices.
        + The definition takes the form:
        -
        -{
        -  "<source-reading> => "<dest.device> [=> <dest.-reading>]",
        -  "<source-reading> => "<dest.device> [=> <dest.-reading>]",
        -  ...
        -}
        -                              
        +
        +  {
        +   "<source-reading>" => "<dest.device> [=> <dest.-reading>]",
        +   "<source-reading>" => "<dest.device> [=> <dest.-reading>]",
        +   ...
        +  }
        +  
        - Wildcards (.*) are permitted in the specification <source-reading>.

        + Wildcards (.*) are permitted in the specification <source-reading>.

        -
          - Example: -
          -{
          -  ".*"        => "Dum.Rep.All",
          -  ".*AVGAM.*" => "Dum.Rep     => average",
          -  ".*SUM.*"   => "Dum.Rep.Sum => summary",
          -}
          -# all readings are transferred to device "Dum.Rep.All", reading name remains in the target
          -# readings with "AVGAM" in the name are transferred to the "Dum.Rep" device in the reading "average"
          -# readings with "SUM" in the name are transferred to the device "Dum.Rep.Sum" in the reading "summary"
          -                              
          - -
        -

      • + Example: +
        +  {
        +    ".*"        => "Dum.Rep.All",
        +    ".*AVGAM.*" => "Dum.Rep     => average",
        +    ".*SUM.*"   => "Dum.Rep.Sum => summary",
        +  }
        +  # All readings are transferred to device "Dum.Rep.All", reading name remains in the target
        +  # readings with "AVGAM" in the name are transferred to the "Dum.Rep" device in the reading "average"
        +  # readings with "SUM" in the name are transferred to the device "Dum.Rep.Sum" in the reading "summary"
        +  
        + +
      • averageCalcForm

        @@ -16641,9 +16866,12 @@ sub dbval {
        -
      • countEntriesDetail - If set, the function countEntries creates a detailed report of counted datasets of - every reading. By default only the summary of counted datasets is reported. -

      • +
      • countEntriesDetail

        + + If set, the function countEntries creates a detailed report of counted datasets of + every reading. By default only the summary of counted datasets is reported. +
      • +
      • device - Selection of particular or several devices.
        @@ -16701,16 +16929,16 @@ sub dbval {
      • dumpComment
        - User-specific comment which is entered in the header of the file generated by "dumpMyQL clientSide". -
      • + User-specific comment which is entered in the header of the file generated by "dumpMyQL clientSide". +
      • dumpCompress
        - If set, the file created by "dumpMySQL" or "dumpSQLite" is then compressed and the uncompressed source file is deleted. -
      • + If set, the file created by "dumpMySQL" or "dumpSQLite" is then compressed and the uncompressed source file is deleted. +
        - +
      • dumpDirLocal

          @@ -16763,9 +16991,9 @@ sub dbval {
        • executeAfterProc

          - + You can specify a FHEM command or Perl code that should be executed after the command is processed.
          - Perl code is to be enclosed in {...}. The variables $hash (hash of the DbRep device) and $name + Perl code is to be enclosed in {...}. The variables $hash (hash of the DbRep device) and $name (name of the DbRep device) are available.

            @@ -16790,9 +17018,9 @@ sub adump {
          • executeBeforeProc

            - + A FHEM command or Perl code can be specified which is to be executed before the command is processed.
            - Perl code is to be enclosed in {...}. The variables $hash (hash of the DbRep device) and $name + Perl code is to be enclosed in {...}. The variables $hash (hash of the DbRep device) and $name (name of the DbRep device) are available.

              @@ -16984,18 +17212,35 @@ sub bdump { -
            • readingNameMap - A part of the created reading name will be replaced by the specified string

            • +
            • readingNameMap

              + + The part between the first and last double underscore ('__') of the created reading name is replaced with the + specified string. +
            • +
              -
            • role - the role of the DbRep-device. Standard role is "Client".
              - - The role "Agent" is described in section DbRep-Agent. - -

            • +
            • role

              + + The role of the DbRep-device. Standard role is "Client".
              + The role "Agent" is described in section DbRep-Agent. +
            • +
              -
            • readingPreventFromDel - comma separated list of readings which are should prevent from deletion when a - new operation starts

            • +
            • readingPreventFromDel

              + + Comma separated list of readings which are should prevent from deletion when a new operation starts.
              + The readings can be specified as a regular expression.
              + (default: state) +

              + +
                + Example:
                + attr <name> readingPreventFromDel .*Count.*,.*Summary1.*,.*Summary2.* +
              +
            • +
            • seqDoubletsVariance <positive variance [negative variance] [EDGE=negative|positive]>

              @@ -17176,9 +17421,9 @@ sub bdump {
            • timeYearPeriod <Month>-<Day> <Month>-<Day>
              An annual period is determined for the database selection. - The annual period is calculated dynamically at execution time. + The annual period is calculated dynamically at execution time. It is not possible to provide information during the year.
              - This attribute is primarily intended to create evaluations synchronized with a billing period, e.g. that of an energy or + This attribute is primarily intended to create evaluations synchronized with a billing period, e.g. that of an energy or gas supplier.

              @@ -17717,23 +17962,24 @@ return; Zur Zeit gibt es folgende Set-Kommandos. Über sie werden die Auswertungen angestoßen und definieren selbst die Auswertungsvariante. Nach welchen Kriterien die Datenbankinhalte durchsucht werden und die Aggregation erfolgt, wird durch Attribute gesteuert.

              - + Hinweis:
              - In der Detailansicht kann ein Browserrefresh nötig sein um die Operationsergebnisse zu sehen sobald im DeviceOverview "state = done" angezeigt wird. + In der Detailansicht kann ein Browserrefresh nötig sein um die Operationsergebnisse zu sehen sobald im DeviceOverview + "state = done" angezeigt wird.

                  + +
                • adminCredentials <User> <Passwort>

                  + Speichert einen User / Passwort für den privilegierten bzw. administrativen + Datenbankzugriff. Er wird bei Datenbankoperationen benötigt, die mit einem privilegierten User + ausgeführt werden müssen. Siehe auch Attribut useAdminCredentials. +
                • +
                  -
                • adminCredentials <User> <Passwort> - - Speichert einen User / Passwort für den privilegierten bzw. administrativen - Datenbankzugriff. Er wird bei Datenbankoperationen benötigt, die mit einem privilegierten User - ausgeführt werden müssen. Siehe auch Attribut useAdminCredentials.
                  - (nur gültig bei Datenbanktyp MYSQL und DbRep-Typ "Client") -

                • - - +
                • averageValue [display | writeToDB | writeToDBSingle | writeToDBSingleStart | writeToDBInTime]

                  Berechnet einen Durchschnittswert des Datenbankfelds "VALUE" in den Zeitgrenzen @@ -17794,7 +18040,12 @@ return;

                • -
                • cancelDump - bricht einen laufenden Datenbankdump ab.

                • + +
                • cancelDump

                  + + Bricht einen laufenden Datenbankdump ab. +
                • +
                • changeValue old="<alter String>" new="<neuer String>"

                  @@ -17852,38 +18103,37 @@ return;
                • -
                • countEntries [history | current] - - liefert die Anzahl der Tabelleneinträge (default: history) in den gegebenen - Zeitgrenzen (siehe time*-Attribute). - Sind die Timestamps nicht gesetzt, werden alle Einträge der Tabelle gezählt. - Beschränkungen durch die Attribute device bzw. reading - gehen in die Selektion mit ein.
                  -
                • + +
                • countEntries [history | current]

                  - Standardmäßig wird die Summe aller Datensätze, gekennzeichnet mit "ALLREADINGS", erstellt. - Ist das Attribut "countEntriesDetail" gesetzt, wird die Anzahl jedes einzelnen Readings - zusätzlich ausgegeben.

                  + Liefert die Anzahl der Tabelleneinträge (default: history) in den gegebenen + Zeitgrenzen (siehe time*-Attribute). + Sind die Timestamps nicht gesetzt, werden alle Einträge der Tabelle gezählt. + Beschränkungen durch die Attribute device bzw. reading + gehen in die Selektion mit ein.
                  + Standardmäßig wird die Summe aller Datensätze, gekennzeichnet mit "ALLREADINGS", erstellt. + Ist das Attribut countEntriesDetail gesetzt, wird die Anzahl jedes + einzelnen Readings zusätzlich ausgegeben.

                  - Die für diese Funktion relevanten Attribute sind:

                  - -
                    - - - - - - - - - - - -
                    aggregation : Zusammenfassung/Gruppierung von Zeitintervallen
                    countEntriesDetail : detaillierte Ausgabe der Datensatzanzahl
                    device : einschließen oder ausschließen von Datensätzen die <device> enthalten
                    reading : einschließen oder ausschließen von Datensätzen die <reading> enthalten
                    time.* : eine Reihe von Attributen zur Zeitabgrenzung
                    executeBeforeProc : ausführen FHEM Kommando (oder Perl-Routine) vor Ausführung
                    executeAfterProc : ausführen FHEM Kommando (oder Perl-Routine) nach Ausführung
                    readingNameMap : die entstehenden Ergebnisreadings werden partiell umbenannt
                    valueFilter : ein zusätzliches REGEXP um die Datenselektion zu steuern. Der REGEXP wird auf das Datenbankfeld 'VALUE' angewendet.
                    -
                  -
                  - -
                  + Die für diese Funktion relevanten Attribute sind:

                  +
                    + + + + + + + + + + + +
                    aggregation : Zusammenfassung/Gruppierung von Zeitintervallen
                    countEntriesDetail : detaillierte Ausgabe der Datensatzanzahl
                    device : einschließen oder ausschließen von Datensätzen die <device> enthalten
                    reading : einschließen oder ausschließen von Datensätzen die <reading> enthalten
                    time.* : eine Reihe von Attributen zur Zeitabgrenzung
                    executeBeforeProc : ausführen FHEM Kommando (oder Perl-Routine) vor Ausführung
                    executeAfterProc : ausführen FHEM Kommando (oder Perl-Routine) nach Ausführung
                    readingNameMap : die entstehenden Ergebnisreadings werden partiell umbenannt
                    valueFilter : ein zusätzliches REGEXP um die Datenselektion zu steuern. Der REGEXP wird auf das Datenbankfeld 'VALUE' angewendet.
                    +
                  +
                • +
                  +
                • delDoublets [adviceDelete | delete] - zeigt bzw. löscht doppelte / mehrfach vorkommende Datensätze. Dazu wird Timestamp, Device,Reading und Value ausgewertet.
                  @@ -17948,46 +18198,47 @@ return;
                  -
                • delEntries [<no>[:<nn>]] - löscht alle oder die durch die Attribute device und/oder - reading definierten Datenbankeinträge. Die Eingrenzung über Timestamps erfolgt - folgendermaßen:

                  +
                • delEntries [<no>[:<nn>]]

                  -
                    - "timestamp_begin" gesetzt -> gelöscht werden DB-Einträge ab diesem Zeitpunkt bis zum aktuellen Datum/Zeit
                    - "timestamp_end" gesetzt -> gelöscht werden DB-Einträge bis bis zu diesem Zeitpunkt
                    - beide Timestamps gesetzt -> gelöscht werden DB-Einträge zwischen diesen Zeitpunkten
                    - "timeOlderThan" gesetzt -> gelöscht werden DB-Einträge älter als aktuelle Zeit minus "timeOlderThan"
                    - "timeDiffToNow" gesetzt -> gelöscht werden DB-Einträge ab aktueller Zeit minus "timeDiffToNow" bis jetzt
                    -
                  -
                  + Löscht alle oder die durch die Attribute device und/oder + reading definierten Datenbankeinträge. Die Eingrenzung über Timestamps erfolgt + folgendermaßen:

                  - Aus Sicherheitsgründen muss das Attribut allowDeletion - gesetzt sein um die Löschfunktion freizuschalten.
                  - Zeitgrenzen (Tage) können als Option angegeben werden. In diesem Fall werden eventuell gesetzte Zeitattribute - übersteuert. - Es werden Datensätze berücksichtigt die älter sind als <no> Tage und (optional) neuer sind als - <nn> Tage. -

                  +
                    + "timestamp_begin" gesetzt -> gelöscht werden DB-Einträge ab diesem Zeitpunkt bis zum aktuellen Datum/Zeit
                    + "timestamp_end" gesetzt -> gelöscht werden DB-Einträge bis bis zu diesem Zeitpunkt
                    + beide Timestamps gesetzt -> gelöscht werden DB-Einträge zwischen diesen Zeitpunkten
                    + "timeOlderThan" gesetzt -> gelöscht werden DB-Einträge älter als aktuelle Zeit minus "timeOlderThan"
                    + "timeDiffToNow" gesetzt -> gelöscht werden DB-Einträge ab aktueller Zeit minus "timeDiffToNow" bis jetzt
                    +
                  +
                  - Die zur Steuerung von delEntries relevanten Attribute:

                  + Aus Sicherheitsgründen muss das Attribut allowDeletion + gesetzt sein um die Löschfunktion freizuschalten.
                  + Zeitgrenzen (Tage) können als Option angegeben werden. In diesem Fall werden eventuell gesetzte Zeitattribute + übersteuert. + Es werden Datensätze berücksichtigt die älter sind als <no> Tage und (optional) neuer sind als + <nn> Tage. +

                  -
                    - - - - - - - - - - -
                    allowDeletion : Freischaltung der Löschfunktion
                    device : einschließen oder ausschließen von Datensätzen die <device> enthalten
                    reading : einschließen oder ausschließen von Datensätzen die <reading> enthalten
                    readingNameMap : die entstehenden Ergebnisreadings werden partiell umbenannt
                    time.* : eine Reihe von Attributen zur Zeitabgrenzung
                    executeBeforeProc : ausführen FHEM Kommando (oder Perl-Routine) vor Start delEntries
                    executeAfterProc : ausführen FHEM Kommando (oder Perl-Routine) nach Ende delEntries
                    valueFilter : ein zusätzliches REGEXP um die Datenselektion zu steuern. Der REGEXP wird auf das Datenbankfeld 'VALUE' angewendet.
                    -
                  -
                  + Die zur Steuerung von delEntries relevanten Attribute:

                  -
                • -
                  +
                    + + + + + + + + + + +
                    allowDeletion : Freischaltung der Löschfunktion
                    device : einschließen oder ausschließen von Datensätzen die <device> enthalten
                    reading : einschließen oder ausschließen von Datensätzen die <reading> enthalten
                    readingNameMap : die entstehenden Ergebnisreadings werden partiell umbenannt
                    time.* : eine Reihe von Attributen zur Zeitabgrenzung
                    executeBeforeProc : ausführen FHEM Kommando (oder Perl-Routine) vor Start delEntries
                    executeAfterProc : ausführen FHEM Kommando (oder Perl-Routine) nach Ende delEntries
                    valueFilter : ein zusätzliches REGEXP um die Datenselektion zu steuern. Der REGEXP wird auf das Datenbankfeld 'VALUE' angewendet.
                    +
                  +
                  + +
                • delSeqDoublets [adviceRemain | adviceDelete | delete] - zeigt bzw. löscht aufeinander folgende identische Datensätze. Dazu wird Device,Reading und Value ausgewertet. Nicht gelöscht werden der erste und der letzte Datensatz @@ -18080,76 +18331,76 @@ return;
                • + +
                • deviceRename <old_name>,<new_name>

                  -
                • deviceRename <old_name>,<new_name> - - benennt den Namen eines Device innerhalb der angeschlossenen Datenbank (Internal DATABASE) um. - Der Gerätename wird immer in der gesamten Datenbank umgesetzt. Eventuell gesetzte - Zeitgrenzen oder Beschränkungen durch die Attribute device bzw. - reading werden nicht berücksichtigt.

                  + Benennt den Namen eines Device innerhalb der angeschlossenen Datenbank (Internal DATABASE) um. + Der Gerätename wird immer in der gesamten Datenbank umgesetzt. Eventuell gesetzte + Zeitgrenzen oder Beschränkungen durch die Attribute device bzw. + reading werden nicht berücksichtigt.

                  -
                    - Beispiel:
                    - set <name> deviceRename ST_5000,ST5100
                    - # Die Anzahl der umbenannten Device-Datensätze wird im Reading "device_renamed" ausgegeben.
                    - # Wird der umzubenennende Gerätename in der Datenbank nicht gefunden, wird eine WARNUNG im Reading "device_not_renamed" ausgegeben.
                    - # Entsprechende Einträge erfolgen auch im Logfile mit verbose=3 -

                    -
                  +
                    + Beispiel:
                    + set <name> deviceRename ST_5000,ST5100
                    + # Die Anzahl der umbenannten Device-Datensätze wird im Reading "device_renamed" ausgegeben.
                    + # Wird der umzubenennende Gerätename in der Datenbank nicht gefunden, wird eine WARNUNG im Reading "device_not_renamed" ausgegeben.
                    + # Entsprechende Einträge erfolgen auch im Logfile mit verbose=3 +
                  +

                  - Hinweis:
                  - Obwohl die Funktion selbst non-blocking ausgelegt ist, sollte das zugeordnete DbLog-Device - im asynchronen Modus betrieben werden um ein Blockieren von FHEMWEB zu vermeiden (Tabellen-Lock).
                  -
                  + Hinweis:
                  + Obwohl die Funktion selbst non-blocking ausgelegt ist, sollte das zugeordnete DbLog-Device + im asynchronen Modus betrieben werden um ein Blockieren von FHEMWEB zu vermeiden (Tabellen-Lock).
                  +
                  - Zusammengefasst sind die zur Steuerung dieser Funktion relevanten Attribute:

                  + Zusammengefasst sind die zur Steuerung dieser Funktion relevanten Attribute:

                  -
                    - - - - -
                    executeBeforeProc : ausführen FHEM Kommando (oder Perl-Routine) vor Start des Befehls
                    executeAfterProc : ausführen FHEM Kommando (oder Perl-Routine) nach Ende des Befehls
                    -
                  -
                  -
                  - -
                • +
                    + + + + +
                    executeBeforeProc : ausführen FHEM Kommando (oder Perl-Routine) vor Start des Befehls
                    executeAfterProc : ausführen FHEM Kommando (oder Perl-Routine) nach Ende des Befehls
                    +
                  +
                  +
                  +
                • diffValue [display | writeToDB]

                  - - Berechnet den Differenzwert des Datenbankfelds "VALUE" in den angegebenen Zeitgrenzen + + Berechnet den Differenzwert des Datenbankfelds "VALUE" in den angegebenen Zeitgrenzen (siehe verschiedenen time*-Attribute).

                  - + Es wird die Differenz aus den VALUE-Werten der im Aggregationszeitraum (z.B. day) vorhandenen Datensätze gebildet und aufsummiert. - Ein Übertragswert aus der Vorperiode (aggregation) zur darauf folgenden - Aggregationsperiode wird berücksichtigt, sofern diese Periode einen Value-Wert enhtält.

                  - + Ein Übertragswert aus der Vorperiode (aggregation) zur darauf folgenden + Aggregationsperiode wird berücksichtigt, sofern diese Periode einen Value-Wert enhtält.

                  + In der Standardeinstellung wertet die Funktion nur positive Differenzen aus wie sie z.B. bei einem stetig ansteigenden - Zählerwert auftreten. - Mit dem Attribut diffAccept) kann sowohl die akzeptierte Differenzschwelle als + Zählerwert auftreten. + Mit dem Attribut diffAccept) kann sowohl die akzeptierte Differenzschwelle als auch die Möglichkeit negative Differenzen auszuwerten eingestellt werden.

                  - +
                    Hinweis:
                    - Im Auswertungs- bzw. Aggregationszeitraum (Tag, Woche, Monat, etc.) sollten dem Modul pro Periode mindestens ein - Datensatz zu Beginn und ein Datensatz gegen Ende des Aggregationszeitraumes zur Verfügung stehen um eine möglichst + Im Auswertungs- bzw. Aggregationszeitraum (Tag, Woche, Monat, etc.) sollten dem Modul pro Periode mindestens ein + Datensatz zu Beginn und ein Datensatz gegen Ende des Aggregationszeitraumes zur Verfügung stehen um eine möglichst genaue Auswertung der Differenzwerte vornehmen zu können.
                    - - Wird in einer auszuwertenden Zeit- bzw. Aggregationsperiode nur ein Datensatz gefunden, kann die Differenz in - Verbindung mit dem Differenzübertrag der Vorperiode berechnet werden. in diesem Fall kann es zu einer logischen - Ungenauigkeit in der Zuordnung der Differenz zu der Aggregationsperiode kommen. In diesem Fall wird eine Warnung + + Wird in einer auszuwertenden Zeit- bzw. Aggregationsperiode nur ein Datensatz gefunden, kann die Differenz in + Verbindung mit dem Differenzübertrag der Vorperiode berechnet werden. in diesem Fall kann es zu einer logischen + Ungenauigkeit in der Zuordnung der Differenz zu der Aggregationsperiode kommen. In diesem Fall wird eine Warnung im state ausgegeben und das Reading less_data_in_period mit einer Liste der betroffenen Perioden erzeugt.

                  - + Ist keine oder die Option display angegeben, werden die Ergebnisse nur angezeigt.

                  - + Mit der Option writeToDB werden die Berechnungsergebnisse mit einem neuen Readingnamen in der Datenbank gespeichert.
                  Der neue Readingname wird aus einem Präfix und dem originalen Readingnamen gebildet, - wobei der originale Readingname durch das Attribut readingNameMap ersetzt + wobei der originale Readingname durch das Attribut readingNameMap ersetzt werden kann.
                  Der Präfix setzt sich aus der Bildungsfunktion und der Aggregation zusammen.
                  Der Timestamp der neuen Readings in der Datenbank wird von der eingestellten Aggregationsperiode @@ -18181,7 +18432,7 @@ return;

                - +
              • dumpMySQL [clientSide | serverSide]

                @@ -18397,9 +18648,14 @@ return; unter "dumpMySQL".


              • -
              • eraseReadings - Löscht alle angelegten Readings im Device, außer dem Reading "state" und Readings, die in der - Ausnahmeliste definiert mit Attribut "readingPreventFromDel" enthalten sind. -

              • + +
              • eraseReadings

                + + Löscht alle angelegten Readings im Device, außer dem Reading "state" und Readings, die in der + Ausnahmeliste definiert mit Attribut readingPreventFromDel + enthalten sind. +
              • +
              • exportToFile [</Pfad/File>] [MAXLINES=<lines>] - exportiert DB-Einträge im CSV-Format in den gegebenen Zeitgrenzen.

                @@ -18734,6 +18990,73 @@ return;

              • + +
              • multiCmd {<Befehl-Hash>}

                + + Führt mehrere Set-Befehle sequentiell in einer definierbaren Reihenfolge aus.
                + Die Befehle und bestimmte veränderbare Attribute, die für die Befehle relevant sind, werden in einem + Hash übergeben. In einem Script, z.B. einem at-Device, kann auch eine Variable übergeben werden die einen definierten + Hash enthält.
                + Die auszuführenden Befehle (Schlüssel cmd) und die dafür zu setzenden Attribute werden über Schlüssel im + übergebenen Hash definiert. Die Festlegung der Abarbeitungsreihenfolge der Befehle erfolgt über den Befehl-Index im + Hash. +

                + + Im Hash definierbare Attributschlüssel sind:
                + + +
                + + Beispiel für die Definition eines Befehl-Hashes:
                + +
                  +
                  +        {
                  +          1  => { timestamp_begin => '2023-12-17 00:00:00', 
                  +                  timestamp_end   => '2023-12-17 01:00:00', 
                  +                  device          => 'SMA_Energymeter', 
                  +                  reading         => 'Einspeisung_Wirkleistung_Zaehler', 
                  +                  cmd             => 'countEntries history'
                  +                },
                  +          2  => { timestamp_begin => '2023-12-15 11:00:00', 
                  +                  timestamp_end   => 'previous_day_end', 
                  +                  device          => 'SMA_Energymeter', 
                  +                  reading         => 'Einspeisung_Wirkleistung_Zaehler', 
                  +                  cmd             => 'countEntries' 
                  +                },
                  +          3  => { timeDiffToNow   => 'd:2',
                  +                  readingNameMap  => 'COUNT',
                  +                  autoForward     => '{ ".*COUNT.*" => "Dum.Rep.All" }',
                  +                  device          => 'SMA_%,MySTP.*',
                  +                  reading         => 'etotal,etoday,Ein% EXCLUDE=%Wirkleistung', 
                  +                  cmd             => 'countEntries history' 
                  +                },
                  +          4  => { timeDiffToNow   => 'd:2',
                  +                  readingNameMap  => 'SUM',
                  +                  autoForward     => '{ ".*SUM.*" => "Dum.Rep.All" }',
                  +                  device          => 'SMA_%,MySTP.*',
                  +                  reading         => 'etotal,etoday,Ein% EXCLUDE=%Wirkleistung', 
                  +                  cmd             => 'sumValue' 
                  +                },
                  +          5  => { cmd             => 'sqlCmd select count(*) from current'
                  +                },
                  +        }
                  +      
                  +
                +
              • +
                +
              • optimizeTables [showInfo | execute]

                @@ -18756,7 +19079,6 @@ return; userExitFn

              -

            • @@ -18799,7 +19121,7 @@ return;
            • reduceLog [<no>[:<nn>]] [mode] [EXCLUDE=device1:reading1,device2:reading2,...] [INCLUDE=device:reading]

              - + Reduziert historische Datensätze.

              Arbeitsweise ohne Angabe von Befehlszeilenoperatoren

              @@ -18916,23 +19238,23 @@ return; anzulegen !
              Die Abarbeitung dieses Befehls dauert unter Umständen (ohne INDEX) extrem lange.

              -
            • +
            • repairSQLite [sec]

              - + Repariert eine korrupte SQLite-Datenbank.

              - + Eine Korruption liegt im Allgemeinen vor, wenn die Fehlermitteilung "database disk image is malformed" im state des DbLog-Devices erscheint. Wird dieses Kommando gestartet, wird das angeschlossene DbLog-Device zunächst automatisch für 10 Stunden (36000 Sekunden) von der Datenbank getrennt (Trennungszeit). Nach Abschluss der Reparatur erfolgt wieder eine sofortige Neuverbindung zur reparierten Datenbank.
              Dem Befehl kann eine abweichende Trennungszeit (in Sekunden) als Argument angegeben werden.
              - Die korrupte Datenbank wird als <database>.corrupt im gleichen Verzeichnis gespeichert. + Die korrupte Datenbank wird als <database>.corrupt im gleichen Verzeichnis gespeichert.

              - + Relevante Attribute sind:
                @@ -18955,15 +19277,15 @@ return; Es ist nicht garantiert, dass die Reparatur erfolgreich verläuft und keine Daten verloren gehen. Je nach Schwere der Korruption kann Datenverlust auftreten oder die Reparatur scheitern, auch wenn kein Fehler im Ablauf signalisiert wird. Ein Backup der Datenbank sollte unbedingt vorhanden - sein! + sein!

                - - + +
                - +
              • restoreMySQL <File>

                - + Stellt die Datenbank aus einem serverSide- oder clientSide-Dump wieder her.
                Die Funktion stellt über eine Drop-Down Liste eine Dateiauswahl für den Restore zur Verfügung.

                @@ -18989,7 +19311,7 @@ return; Der Datenbankuser benötigt Rechte zum Tabellenmanagement, z.B.:
                CREATE, ALTER, INDEX, DROP, SHOW VIEW, CREATE VIEW

                - + Relevante Attribute sind:

                - +

              • -
              • restoreSQLite <File>.sqlitebkp[.gzip]

                - +
              • restoreSQLite <File>.sqlitebkp[.gzip]

                + Stellt das Backup einer SQLite-Datenbank wieder her.
                Die Funktion stellt über eine Drop-Down Liste die für den Restore zur Verfügung stehenden Dateien zur Verfügung. Die aktuell in der Zieldatenbank enthaltenen Daten werden gelöscht bzw. überschrieben. Es werden alle Files mit der Endung "sqlitebkp[.gzip]" und deren Name mit dem Namen der - verbundenen Datenbank beginnt, aufgelistet. + verbundenen Datenbank beginnt, aufgelistet.

                - + Relevante Attribute sind:
                -
                - +
                +

              • @@ -19127,15 +19449,15 @@ return; zu große Ergebnismenge (Anzahl Zeilen bzw. Readings) die Browsersesssion bzw. FHEMWEB blockieren.
                Wenn man sich unsicher ist, sollte man vorsorglich dem Statement ein Limit - hinzufügen. + hinzufügen.

                - +
              • sqlCmdHistory

                - + Wenn mit dem Attribut sqlCmdHistoryLength aktiviert, kann ein gespeichertes SQL-Statement aus einer Liste ausgewählt und ausgeführt werden.
                Der SQL Cache wird beim Beenden von FHEM automatisch gesichert und beim Start des Systems wiederhergestellt.
                @@ -19174,7 +19496,7 @@ return;
              • sqlSpecial

                - + Die Funktion bietet eine Drop-Downliste mit einer Auswahl vorbereiter Auswertungen an.
                Das Ergebnis des Statements wird im Reading "SqlResult" dargestellt. @@ -19213,12 +19535,12 @@ return;


              - +

            • sumValue [display | writeToDB | writeToDBSingle | writeToDBInTime]

              - + Berechnet die Summenwerte des Datenbankfelds "VALUE" in den Zeitgrenzen der möglichen time.*-Attribute.

              @@ -19245,7 +19567,7 @@ return; wobei der originale Readingname durch das Attribut "readingNameMap" ersetzt werden kann.
              Der Präfix setzt sich aus der Bildungsfunktion und der Aggregation zusammen.
              Der Timestamp der neuen Readings in der Datenbank wird von der eingestellten Aggregationsperiode abgeleitet,
              - sofern kein eindeutiger Zeitpunkt des Ergebnisses bestimmt werden kann. + sofern kein eindeutiger Zeitpunkt des Ergebnisses bestimmt werden kann. Das Feld "EVENT" wird mit "calculated" gefüllt.

                @@ -19271,11 +19593,11 @@ return;

                - +
              • syncStandby <DbLog-Device Standby>

                - + Es werden die Datensätze aus der angeschlossenen Datenbank (Quelle) direkt in eine weitere Datenbank (Standby-Datenbank) übertragen. Dabei ist "<DbLog-Device Standby>" das DbLog-Device, welches mit der Standby-Datenbank @@ -19304,12 +19626,12 @@ return;

                -
              • +
              • tableCurrentFillup

                - + Die current-Tabelle wird mit einem Extrakt der history-Tabelle aufgefüllt.
                Die Attribute zur Zeiteinschränkung bzw. device, reading werden ausgewertet.
                Dadurch kann der Inhalt des Extrakts beeinflusst werden.
                @@ -19335,7 +19657,7 @@ return;
              • tableCurrentPurge

                - + Löscht den Inhalt der current-Tabelle.
                Es werden keine Limitierungen, z.B. durch die Attribute timestamp_begin, timestamp_end, device oder reading ausgewertet. @@ -19603,46 +19925,62 @@ sub dbval {
                    -
                  • aggregation - Zusammenfassung der Device/Reading-Selektionen in Stunden, Tagen, Kalenderwochen, Kalendermonaten, Kalenderjahren - oder "no".
                    - Liefert z.B. die Anzahl der DB-Einträge am Tag (countEntries) oder die Summierung von - Differenzwerten eines Readings (sumValue) usw.
                    - Mit Aggregation "no" (default) wird das Ergebnis aus allen Werten einer Device/Reading-Kombination im - selektierten Zeitraum ermittelt.

                  • +
                  • aggregation

                    + + Erstellung der Funktionsergebnisse in Zeitscheiben innerhalb des Selektionszeitraumes. +

                    + +
                      + + + + + + + + + +
                      no - keine Aggregation (default)
                      minute - die Funktionsergebnisse werden pro Minute zusammengefasst
                      hour - die Funktionsergebnisse werden pro Stunde zusammengefasst
                      day - die Funktionsergebnisse werden pro Kalendertag zusammengefasst
                      week - die Funktionsergebnisse werden pro Kalenderwoche zusammengefasst
                      month - die Funktionsergebnisse werden pro Kalendermonat zusammengefasst
                      year - die Funktionsergebnisse werden pro Kalenderjahr zusammengefasst
                      +
                    +
                  • +
                    -
                  • allowDeletion - schaltet die Löschfunktion des Moduls frei

                  • +
                  • allowDeletion

                    + + Schaltet die Löschfunktion des Moduls frei. +
                  • +
                    -
                  • autoForward - wenn aktiviert, werden die Ergebnisreadings einer Funktion in ein oder mehrere Devices - übertragen.
                    - Die Definition erfolgt in der Form:
                    +
                  • autoForward

                    + Wenn aktiviert, werden die Ergebnisreadings einer Funktion in ein oder mehrere Devices + übertragen.
                    + Die Definition erfolgt in der Form:
                    -
                    -{
                    -  "<source-reading> => "<dest.device> [=> <dest.-reading>]",
                    -  "<source-reading> => "<dest.device> [=> <dest.-reading>]",
                    -  ...
                    -}
                    -                              
                    +
                    +   {
                    +    "<source-reading>" => "<dest.device> [=> <dest.-reading>]",
                    +    "<source-reading>" => "<dest.device> [=> <dest.-reading>]",
                    +    ...
                    +   }
                    +  
                    - In der Angabe <source-reading> sind Wildcards (.*) erlaubt.

                    + In der Angabe <source-reading> sind Wildcards (.*) erlaubt.

                    -
                      - Beispiel: -
                      -{
                      -  ".*"        => "Dum.Rep.All",
                      -  ".*AVGAM.*" => "Dum.Rep     => average",
                      -  ".*SUM.*"   => "Dum.Rep.Sum => summary",
                      -}
                      -# alle Readings werden zum Device "Dum.Rep.All" übertragen, Readingname bleibt im Ziel erhalten
                      -# Readings mit "AVGAM" im Namen werden zum Device "Dum.Rep" in das Reading "average" übertragen
                      -# Readings mit "SUM" im Namen werden zum Device "Dum.Rep.Sum" in das Reading "summary" übertragen
                      -                              
                      - -
                    -

                  • + Beispiel: +
                    +   {
                    +    ".*"        => "Dum.Rep.All",
                    +    ".*AVGAM.*" => "Dum.Rep     => average",
                    +    ".*SUM.*"   => "Dum.Rep.Sum => summary",
                    +   }
                    +   # alle Readings werden zum Device "Dum.Rep.All" übertragen, Readingname bleibt im Ziel erhalten
                    +   # Readings mit "AVGAM" im Namen werden zum Device "Dum.Rep" in das Reading "average" übertragen
                    +   # Readings mit "SUM" im Namen werden zum Device "Dum.Rep.Sum" in das Reading "summary" übertragen
                    +  
                    + +
                  • averageCalcForm

                    @@ -19675,10 +20013,13 @@ sub dbval {
                    -
                  • countEntriesDetail - Wenn gesetzt, erstellt die Funktion "countEntries" eine detallierte Ausgabe der Datensatzzahl - pro Reading und Zeitintervall. - Standardmäßig wird nur die Summe aller selektierten Datensätze ausgegeben. -

                  • +
                  • countEntriesDetail

                    + + Wenn gesetzt, erstellt die Funktion "countEntries" eine detallierte Ausgabe der Datensatzzahl + pro Reading und Zeitintervall. + Standardmäßig wird nur die Summe aller selektierten Datensätze ausgegeben. +
                  • +
                  • device - Abgrenzung der DB-Selektionen auf ein bestimmtes oder mehrere Devices.
                    @@ -19712,22 +20053,22 @@ sub dbval {
                  • diffAccept [+-]<Schwellenwert>

                    - - diffAccept legt für die Funktion diffValue fest, bis zu welchem <Schwellenwert> eine + + diffAccept legt für die Funktion diffValue fest, bis zu welchem <Schwellenwert> eine Werte-Differenz zwischen zwei unmittelbar aufeinander folgenden Datensätzen akzeptiert werden.
                    - Wird dem Schwellenwert +- (optional) vorangestellt, werden sowohl positive als auch negative Differenzen - ausgewertet. + Wird dem Schwellenwert +- (optional) vorangestellt, werden sowohl positive als auch negative Differenzen + ausgewertet.

                    - + (default: 20, nur positive Differenzen zwischen Vorgänger und Nachfolger)

                    - +
                      Beispiel:
                      attr diffAccept +-10000

                    - + Bei Schwellenwertüberschreitungen wird das Reading diff_overrun_limit_<Schwellenwert> erstellt.
                    Es enthält eine Liste der relevanten Wertepaare. Mit verbose 3 werden diese Datensätze ebenfalls im Logfile protokolliert. @@ -19743,22 +20084,22 @@ sub dbval { und führt zu einem zu hohen Differenzwert.
                    # Es ist zu entscheiden ob der Datensatz gelöscht, ignoriert, oder das Attribut diffAccept angepasst werden sollte.
                  - +
                • dumpComment
                  Benutzer spezifischer Kommentar, welcher im Kopf der durch "dumpMyQL clientSide" erzeugten Datei - eingetragen wird. -
                • + eingetragen wird. +
                • dumpCompress
                  - Wenn gesetzt, wird die durch "dumpMySQL" bzw. "dumpSQLite" erzeugte Datei anschließend komprimiert und die - unkomprimierte Quellendatei gelöscht. -
                • + Wenn gesetzt, wird die durch "dumpMySQL" bzw. "dumpSQLite" erzeugte Datei anschließend komprimiert und die + unkomprimierte Quellendatei gelöscht. +
                  @@ -19819,10 +20160,10 @@ sub dbval {
                • executeAfterProc

                  - - Es kann ein FHEM-Kommando oder Perl Code angegeben werden der nach der Befehlsabarbeitung ausgeführt + + Es kann ein FHEM-Kommando oder Perl Code angegeben werden der nach der Befehlsabarbeitung ausgeführt werden soll.
                  - Perl Code ist in {...} einzuschließen. Es stehen die Variablen $hash (Hash des DbRep Devices) und $name + Perl Code ist in {...} einzuschließen. Es stehen die Variablen $hash (Hash des DbRep Devices) und $name (Name des DbRep-Devices) zur Verfügung.

                    @@ -19847,10 +20188,10 @@ sub adump {
                  • executeBeforeProc

                    - - Es kann ein FHEM-Kommando oder Perl Code angegeben werden der vor der Befehlsabarbeitung ausgeführt + + Es kann ein FHEM-Kommando oder Perl Code angegeben werden der vor der Befehlsabarbeitung ausgeführt werden soll.
                    - Perl Code ist in {...} einzuschließen. Es stehen die Variablen $hash (Hash des DbRep Devices) und $name + Perl Code ist in {...} einzuschließen. Es stehen die Variablen $hash (Hash des DbRep Devices) und $name (Name des DbRep-Devices) zur Verfügung.

                      @@ -20042,17 +20383,35 @@ sub bdump { -
                    • readingNameMap - ein Teil des erstellten Readingnamens wird mit dem angegebenen String ersetzt.

                    • +
                    • readingNameMap

                      + + Der Teil zwischen dem ersten und letzten doppelten Unterstrich ('__') des erstellten Readingnamens wird mit dem + angegebenen String ersetzt. +
                    • +
                      -
                    • readingPreventFromDel - Komma separierte Liste von Readings die vor einer neuen Operation nicht gelöscht - werden sollen

                    • +
                    • readingPreventFromDel

                      + + Komma separierte Liste von Readings die vor einer neuen Operation nicht gelöscht werden sollen.
                      + Die Readings können als regulärer Ausdruck angegeben werden.
                      + (default: state) +

                      + +
                        + Beispiel:
                        + attr <name> readingPreventFromDel .*Count.*,.*Summary1.*,.*Summary2.* +
                      +
                    • +
                      -
                    • role - die Rolle des DbRep-Device. Standard ist "Client". Die Rolle "Agent" ist im Abschnitt - "DbRep-Agent" beschrieben.
                      - Siehe auch Abschnitt DbRep-Agent. -

                    • +
                    • role

                      + + Die Rolle des DbRep-Device. Standard ist "Client". + Die Rolle "Agent" ist im Abschnitt DbRep-Agent beschrieben.
                      +
                    • +
                    • seqDoubletsVariance <positive Abweichung [negative Abweichung] [EDGE=negative|positive]>

                      @@ -20234,7 +20593,7 @@ sub bdump {
                    • timeYearPeriod <Monat>-<Tag> <Monat>-<Tag>
                      Es wird eine jährliche Periode für die Datenbankselektion bestimmt. - Die Jahresperiode wird dynamisch zur Ausführungszeit berechnet. + Die Jahresperiode wird dynamisch zur Ausführungszeit berechnet. Eine unterjährige Angabe ist nicht möglich.
                      Dieses Attribut ist vor allem dazu gedacht Auswertungen synchron zu einer Abrechnungsperiode, z.B. der eines Energie- oder Gaslieferanten, anzufertigen.