From 87e957c3f4f05525318316015edf02a5f9326ce6 Mon Sep 17 00:00:00 2001 From: nasseeder1 <> Date: Sun, 9 Oct 2016 17:56:52 +0000 Subject: [PATCH] 93_DbRep: deviceRename added, new Internal DATABASE, Internal/Attribute ROLE added for Autorename-function delEntries not running on SQLite git-svn-id: https://svn.fhem.de/fhem/trunk@12305 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 4 +- fhem/FHEM/93_DbRep.pm | 594 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 515 insertions(+), 83 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index 7c0f10e5e..7ab9730ae 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,6 +1,8 @@ # 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: deviceRename added, new Internal DATABASE, + Internal/Attribute ROLE added for Autorename-function + - bugfix: 93_DbRep: delEntries not running on SQLite - added: 74_THINKINGCLEANER: new module to support remote control of Roomba cleaning robots - feature: 02_FTUISRV: if and loop constructs added diff --git a/fhem/FHEM/93_DbRep.pm b/fhem/FHEM/93_DbRep.pm index cd35f9ff7..104a58c61 100644 --- a/fhem/FHEM/93_DbRep.pm +++ b/fhem/FHEM/93_DbRep.pm @@ -22,7 +22,7 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with fhem. If not, see .# +# along with fhem. If not, see .# # ########################################################################################################### # @@ -36,6 +36,15 @@ # ########################################################################################################### # Versions History: +# 4.1.3 09.10.2016 bugfix delEntries running on SQLite +# 4.1.2 08.10.2016 old device in DEF of connected DbLog device will substitute by renamed device if +# it is present in DEF +# 4.1.1 06.10.2016 NotifyFn is getting events from global AND own device, set is reduced if +# ROLE=Agent, english commandref enhanced +# 4.1 05.10.2016 DbRep_Attr changed +# 4.0 04.10.2016 Internal/Attribute ROLE added, sub DbRep_firstconnect changed +# NotifyFN activated to start deviceRename if ROLE=Agent +# 3.13 03.10.2016 added deviceRename to rename devices in database, new Internal DATABASE # 3.12 02.10.2016 function minValue added # 3.11.1 30.09.2016 bugfix include first and next day in calculation if Timestamp is exactly 'YYYY-MM-DD 00:00:00' # 3.11 29.09.2016 maxValue calculation moved to background to reduce FHEM-load @@ -146,18 +155,19 @@ sub DbRep_Initialize($) { my ($hash) = @_; $hash->{DefFn} = "DbRep_Define"; $hash->{UndefFn} = "DbRep_Undef"; - # $hash->{NotifyFn} = "DbRep_Notify"; + $hash->{NotifyFn} = "DbRep_Notify"; $hash->{SetFn} = "DbRep_Set"; $hash->{AttrFn} = "DbRep_Attr"; $hash->{AttrList} = "disable:1,0 ". - "reading ". + "reading ". "allowDeletion:1,0 ". "readingNameMap ". "readingPreventFromDel ". "device ". "expimpfile ". "aggregation:hour,day,week,month,no ". + "role:Client,Agent ". "showproctime:1,0 ". "timestamp_begin ". "timestamp_end ". @@ -165,7 +175,7 @@ sub DbRep_Initialize($) { "timeOlderThan ". "timeout ". $readingFnAttributes; - + return undef; } @@ -188,12 +198,18 @@ sub DbRep_Define($@) { } $hash->{LASTCMD} = " "; + $hash->{ROLE} = AttrVal($name, "role", "Client"); $hash->{HELPER}{DBLOGDEVICE} = $a[2]; + $hash->{NOTIFYDEV} = "global,".$name; # nur Events dieser Devices an DbRep_Notify weiterleiten + + my $dbconn = $defs{$a[2]}{dbconn}; + $hash->{DATABASE} = (split(/;|=/, $dbconn))[1]; + RemoveInternalTimer($hash); InternalTimer(time+5, 'DbRep_firstconnect', $hash, 0); - Log3 ($name, 3, "DbRep $name - initialized"); + Log3 ($name, 4, "DbRep $name - initialized"); readingsSingleUpdate($hash, 'state', 'initialized', 1); return undef; @@ -214,39 +230,47 @@ sub DbRep_Set($@) { my $dbmodel = $hash->{dbloghash}{DBMODEL}; my $setlist = "Unknown argument $opt, choose one of ". - "sumValue:noArg ". - "averageValue:noArg ". - "delEntries:noArg ". - "exportToFile:noArg ". - "importFromFile:noArg ". - "maxValue:noArg ". - "minValue:noArg ". - "fetchrows:noArg ". - "diffValue:noArg ". - "insert ". - "countEntries:noArg "; + (($hash->{ROLE} ne "Agent")?"sumValue:noArg ":""). + (($hash->{ROLE} ne "Agent")?"averageValue:noArg ":""). + (($hash->{ROLE} ne "Agent")?"delEntries:noArg ":""). + "deviceRename ". + (($hash->{ROLE} ne "Agent")?"exportToFile:noArg ":""). + (($hash->{ROLE} ne "Agent")?"importFromFile:noArg ":""). + (($hash->{ROLE} ne "Agent")?"maxValue:noArg ":""). + (($hash->{ROLE} ne "Agent")?"minValue:noArg ":""). + (($hash->{ROLE} ne "Agent")?"fetchrows:noArg ":""). + (($hash->{ROLE} ne "Agent")?"diffValue:noArg ":""). + (($hash->{ROLE} ne "Agent")?"insert ":""). + (($hash->{ROLE} ne "Agent")?"countEntries:noArg ":""); return if(IsDisabled($name)); - if ($opt eq "countEntries") { + if ($opt eq "countEntries" && $hash->{ROLE} ne "Agent") { sqlexec($hash,$opt); - } elsif ($opt eq "fetchrows") { + } elsif ($opt eq "fetchrows" && $hash->{ROLE} ne "Agent") { sqlexec($hash,$opt); - } elsif ($opt eq "maxValue" || $opt eq "minValue" || $opt eq "sumValue" || $opt eq "averageValue" || $opt eq "diffValue") { + } elsif ($opt =~ m/(max|min|sum|average|diff)Value/ && $hash->{ROLE} ne "Agent") { if (!AttrVal($hash->{NAME}, "reading", "")) { return " The attribute reading to analyze is not set !"; } sqlexec($hash,$opt); - } elsif ($opt eq "delEntries") { + } elsif ($opt eq "delEntries" && $hash->{ROLE} ne "Agent") { if (!AttrVal($hash->{NAME}, "allowDeletion", undef)) { return " Set attribute 'allowDeletion' if you want to allow deletion of any database entries. Use it with care !"; } sqlexec($hash,$opt); - } elsif ($opt eq "insert") { + } elsif ($opt eq "deviceRename") { + my ($olddev, $newdev) = split(",",$prop); + if (!$olddev || !$newdev) {return "Both entries \"old device name\", \"new device name\" are needed. Use \"set ... deviceRename olddevname,newdevname\" ";} + $hash->{HELPER}{OLDDEV} = $olddev; + $hash->{HELPER}{NEWDEV} = $newdev; + sqlexec($hash,$opt); + + } elsif ($opt eq "insert" && $hash->{ROLE} ne "Agent") { if ($prop) { if (!AttrVal($hash->{NAME}, "device", "") || !AttrVal($hash->{NAME}, "reading", "") ) { return " One or both of attributes \"device\", \"reading\" is not set. It's mandatory to set both to complete dataset for manual insert !"; @@ -300,13 +324,13 @@ sub DbRep_Set($@) { sqlexec($hash,$opt); - } elsif ($opt eq "exportToFile") { + } elsif ($opt eq "exportToFile" && $hash->{ROLE} ne "Agent") { if (!AttrVal($hash->{NAME}, "expimpfile", "")) { return " The attribute \"expimpfile\" (path and filename) has to be set for export to file !"; } sqlexec($hash,$opt); - } elsif ($opt eq "importFromFile") { + } elsif ($opt eq "importFromFile" && $hash->{ROLE} ne "Agent") { if (!AttrVal($hash->{NAME}, "expimpfile", "")) { return " The attribute \"expimpfile\" (path and filename) has to be set for import from file !"; } @@ -327,8 +351,7 @@ return undef; sub DbRep_Attr($$$$) { my ($cmd,$name,$aName,$aVal) = @_; my $hash = $defs{$name}; - my $dblogdevice = $hash->{HELPER}{DBLOGDEVICE}; - $hash->{dbloghash} = $defs{$dblogdevice}; + $hash->{dbloghash} = $defs{$hash->{HELPER}{DBLOGDEVICE}}; my $dbmodel = $hash->{dbloghash}{DBMODEL}; my $do; @@ -336,6 +359,20 @@ sub DbRep_Attr($$$$) { # $name is device name # aName and aVal are Attribute name and value + # nicht erlaubte / zu löschende Attribute wenn role = Agent + my @agentnoattr = qw(aggregation + allowDeletion + reading + readingNameMap + readingPreventFromDel + device + expimpfile + timestamp_begin + timestamp_end + timeDiffToNow + timeOlderThan + ); + if ($aName eq "disable") { if($cmd eq "set") { $do = ($aVal) ? 1 : 0; @@ -355,6 +392,12 @@ sub DbRep_Attr($$$$) { } + if ($cmd eq "set" && $hash->{ROLE} eq "Agent") { + foreach (@agentnoattr) { + return ("Attribute $aName is not usable due to role of $name is \"$hash->{ROLE}\" ") if ($_ eq $aName); + } + } + 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.";} @@ -363,6 +406,32 @@ sub DbRep_Attr($$$$) { delete $hash->{HELPER}{RDPFDEL} if($hash->{HELPER}{RDPFDEL}); } } + + if ($aName eq "role") { + if($cmd eq "set") { + if ($aVal eq "Agent") { + # check ob bereits ein Agent für die angeschlossene Datenbank existiert -> DbRep-Device kann dann keine Agent-Rolle einnehmen + foreach(devspec2array("TYPE=DbRep")) { + my $devname = $_; + next if($devname eq $name); + my $devrole = $defs{$_}{ROLE}; + my $devdb = $defs{$_}{DATABASE}; + if ($devrole eq "Agent" && $devdb eq $hash->{DATABASE}) { return "There is already an Agent device: $devname defined for database $hash->{DATABASE} !"; } + } + # nicht erlaubte Attribute löschen falls gesetzt + foreach (@agentnoattr) { + delete($attr{$name}{$_}); + } + + $attr{$name}{icon} = "security"; + } + $do = $aVal; + } else { + $do = "Client"; + } + $hash->{ROLE} = $do; + delete($attr{$name}{icon}) if($do eq "Client"); + } if ($cmd eq "set") { if ($aName eq "timestamp_begin" || $aName eq "timestamp_end") { @@ -406,7 +475,7 @@ sub DbRep_Attr($$$$) { delete($attr{$name}{timeDiffToNow}) if ($attr{$name}{timeDiffToNow}); } if ($aName eq "reading" || $aName eq "device") { - if ($dbmodel ne 'SQLITE') { + if ($dbmodel && $dbmodel ne 'SQLITE') { if ($dbmodel eq 'POSTGRESQL') { return "Length of \"$aName\" is too big. Maximum lenth for database type $dbmodel is $dbrep_col_postgre{READING}" if(length($aVal) > $dbrep_col_postgre{READING}); } elsif ($dbmodel eq 'MYSQL') { @@ -414,7 +483,7 @@ sub DbRep_Attr($$$$) { } } } - } + } return undef; } @@ -424,27 +493,68 @@ return undef; ################################################################################### sub DbRep_Notify($$) { - my ($dbrep, $dev) = @_; - my $myName = $dbrep->{NAME}; # Name des eigenen Devices - my $devName = $dev->{NAME}; # Name des Devices welches Events erzeugt hat + # Es werden nur die Events von Geräten verarbeitet die im Hash $hash->{NOTIFYDEV} gelistet sind (wenn definiert). + # Dadurch kann die Menge der Events verringert werden. In sub DbRep_Define angeben. + # Beispiele: + # $hash->{NOTIFYDEV} = "global"; + # $hash->{NOTIFYDEV} = "global,Definition_A,Definition_B"; - return if(IsDisabled($myName)); # Return if the module is disabled + my ($own_hash, $dev_hash) = @_; + my $myName = $own_hash->{NAME}; # Name des eigenen Devices + my $devName = $dev_hash->{NAME}; # Device welches Events erzeugt hat + + return if(IsDisabled($myName)); # Return if the module is disabled + + my $events = deviceEvents($dev_hash,0); + return if(!$events); - my $max = int(@{$dev->{CHANGED}}); # number of events / changes - - for (my $i = 0; $i < $max; $i++) { - my $s = $dev->{CHANGED}[$i]; - next if(!defined($s)); - my ($evName, $val) = split(" ", $s, 2); # resets $1 - next if($devName !~ m/^$myName$/); + foreach my $event (@{$events}) { + $event = "" if(!defined($event)); + my @evl = split("[ \t][ \t]*", $event); - if ($evName =~ m/done/) { - # Log3 ($myName, 3, "DbRep $myName - Event received - device: $myName Event: $evName"); - # fhem ("trigger WEB JS:location.reload('false')"); - # FW_directNotify("#FHEMWEB:WEB", "location.reload('false')", ""); - # map {FW_directNotify("#FHEMWEB:$_", "location.reload('false')", "")} devspec2array("WEB.*"); +# if ($devName = $myName && $evl[0] =~ /done/) { +# InternalTimer(time+1, "browser_refresh", $own_hash, 0); +# } + + # wenn Rolle "Agent" Verbeitung von RENAMED Events + if ($own_hash->{ROLE} eq "Agent") { + next if ($event !~ /RENAMED/); + + my $strucChanged; + # altes in neues device in der DEF des angeschlossenen DbLog-device ändern (neues device loggen) + my $dblog_name = $own_hash->{dbloghash}{NAME}; # Name des an den DbRep-Agenten angeschlossenen DbLog-Dev + my $dblog_hash = $defs{$dblog_name}; + + if ( $dblog_hash->{DEF} =~ m/( |\(|\|)$evl[1]( |\)|\||:)/ ) { + $dblog_hash->{DEF} =~ s/$evl[1]/$evl[2]/; + $dblog_hash->{REGEXP} =~ s/$evl[1]/$evl[2]/; + # Definitionsänderung wurde vorgenommen + $strucChanged = 1; + Log3 ($myName, 3, "DbRep Agent $myName - $dblog_name substituted in DEF, old: \"$evl[1]\", new: \"$evl[2]\" "); + } + + # DEVICE innerhalb angeschlossener Datenbank umbenennen + Log3 ($myName, 4, "DbRep Agent $myName - Evt RENAMED rec - old device: $evl[1], new device: $evl[2] -> start deviceRename in DB: $own_hash->{DATABASE} "); + $own_hash->{HELPER}{OLDDEV} = $evl[1]; + $own_hash->{HELPER}{NEWDEV} = $evl[2]; + sqlexec($own_hash,"deviceRename"); + + # die Attribute "device" in allen DbRep-Devices mit der Datenbank = DB des Agenten von alten Device in neues Device ändern + foreach(devspec2array("TYPE=DbRep")) { + my $repname = $_; + next if($_ eq $myName); + my $repattrdevice = $attr{$_}{device}; + next if(!$repattrdevice); + my $repdb = $defs{$_}{DATABASE}; + if ($repattrdevice eq $evl[1] && $repdb eq $own_hash->{DATABASE}) { + $attr{$_}{device} = $evl[2]; + # Definitionsänderung wurde vorgenommen + $strucChanged = 1; + Log3 ($myName, 3, "DbRep Agent $myName - $_ attr device changed, old: \"$evl[1]\", new: \"$evl[2]\" "); + } + } + # if ($strucChanged) {CommandSave("","")}; } - } } @@ -475,15 +585,24 @@ sub DbRep_firstconnect($) { my $dblogdevice = $hash->{HELPER}{DBLOGDEVICE}; $hash->{dbloghash} = $defs{$dblogdevice}; my $dbconn = $hash->{dbloghash}{dbconn}; + +if ($init_done == 1) { + + $hash->{ROLE} = AttrVal($name, "role", "Client"); # Rolle der DbRep-Instanz setzen if ( !DbRep_Connect($hash) ) { - Log3 ($name, 2, "DbRep $name - DB connect failed. Credentials of $hash->{dbloghash}{NAME} are valid and database reachable ?"); + Log3 ($name, 2, "DbRep $name - DB connect failed. Credentials of database $hash->{DATABASE} are valid and database reachable ?"); readingsSingleUpdate($hash, "state", "disconnected", 1); } else { - Log3 ($name, 3, "DbRep $name - Connectiontest to db $dbconn was successful"); + Log3 ($name, 4, "DbRep $name - Connectiontest to db $dbconn successful"); my $dbh = $hash->{DBH}; $dbh->disconnect(); } +} else { + RemoveInternalTimer($hash, "DbRep_firstconnect"); + InternalTimer(time+1, "DbRep_firstconnect", $hash, 0); +} + return; } @@ -512,7 +631,7 @@ sub DbRep_Connect($) { InternalTimer(time+5, 'DbRep_Connect', $hash, 0); - Log3 ($name, 3, "DbRep $name - Waiting for database connection to test"); + Log3 ($name, 3, "DbRep $name - Waiting for database connection"); return 0; } @@ -520,6 +639,7 @@ sub DbRep_Connect($) { $hash->{DBH} = $dbh; readingsSingleUpdate($hash, "state", "connected", 1); + Log3 ($name, 3, "DbRep $name - connected"); return 1; } @@ -547,8 +667,8 @@ sub sqlexec($$) { # $dbh->disconnect; #} - if (exists($hash->{HELPER}{RUNNING_PID})) { - Log3 ($name, 3, "DbRep $name - Warning: old process $hash->{HELPER}{RUNNING_PID}{pid} will be killed now to start a new BlockingCall"); + if (exists($hash->{HELPER}{RUNNING_PID}) && $hash->{ROLE} ne "Agent") { + Log3 ($name, 3, "DbRep $name - WARNING - old process $hash->{HELPER}{RUNNING_PID}{pid} will be killed now to start a new BlockingCall"); BlockingKill($hash->{HELPER}{RUNNING_PID}); } @@ -743,12 +863,15 @@ $hash->{HELPER}{CV} = \%cv; $hash->{HELPER}{RUNNING_PID} = BlockingCall("del_DoParse", "$name|$device|$reading|$runtime_string_first|$runtime_string_next", "del_ParseDone", $to, "ParseAborted", $hash); - } elsif ($opt eq "diffValue") { + } elsif ($opt eq "diffValue") { $hash->{HELPER}{RUNNING_PID} = BlockingCall("diffval_DoParse", "$name§$device§$reading§$ts", "diffval_ParseDone", $to, "ParseAborted", $hash); - } elsif ($opt eq "insert") { + } elsif ($opt eq "insert") { $hash->{HELPER}{RUNNING_PID} = BlockingCall("insert_Push", "$name", "insert_Done", $to, "ParseAborted", $hash); + } elsif ($opt eq "deviceRename") { + $hash->{HELPER}{RUNNING_PID} = BlockingCall("devren_Push", "$name", "devren_Done", $to, "ParseAborted", $hash); + } return; @@ -1984,7 +2107,7 @@ sub del_DoParse($) { my $sql = "DELETE FROM history where "; $sql .= "DEVICE = '$device' AND " if($device); $sql .= "READING = '$reading' AND " if($reading); - $sql .= "TIMESTAMP >= ? AND TIMESTAMP < ? ORDER BY TIMESTAMP ;"; + $sql .= "TIMESTAMP >= ? AND TIMESTAMP < ? ;"; # SQL zusammenstellen für Logausgabe my $sql1 = "DELETE FROM history where "; @@ -2067,7 +2190,7 @@ sub del_ParseDone($) { $rows = $ds.$rds.$rows; - Log3 ($name, 3, "DbRep $name - Entries of database $hash->{dbloghash}{NAME} deleted: $rows"); + Log3 ($name, 3, "DbRep $name - Entries of database $hash->{DATABASE} deleted: $rows"); readingsBulkUpdate($hash, "background_processing_time", sprintf("%.4f",$brt)) if(AttrVal($name, "showproctime", undef)); readingsBulkUpdate($hash, "sql_processing_time", sprintf("%.4f",$rt)) if(AttrVal($name, "showproctime", undef)); @@ -2199,7 +2322,7 @@ sub insert_Done($) { readingsBulkUpdate($hash, "state", "done"); readingsEndUpdate($hash, 1); - Log3 ($name, 5, "DbRep $name - Inserted into database $hash->{dbloghash}{NAME} table 'history': Timestamp: $i_timestamp, Device: $i_device, Type: $i_type, Event: $i_event, Reading: $i_reading, Value: $i_value, Unit: $i_unit"); + Log3 ($name, 5, "DbRep $name - Inserted into database $hash->{DATABASE} table 'history': Timestamp: $i_timestamp, Device: $i_device, Type: $i_type, Event: $i_event, Reading: $i_reading, Value: $i_value, Unit: $i_unit"); delete($hash->{HELPER}{RUNNING_PID}); Log3 ($name, 4, "DbRep $name -> BlockingCall insert_Done finished"); @@ -2207,6 +2330,128 @@ sub insert_Done($) { return; } +#################################################################################################### +# nichtblockierendes DB deviceRename +#################################################################################################### + +sub devren_Push($) { + my ($name) = @_; + my $hash = $defs{$name}; + my $dbloghash = $hash->{dbloghash}; + my $dbconn = $dbloghash->{dbconn}; + my $dbuser = $dbloghash->{dbuser}; + my $dblogname = $dbloghash->{NAME}; + my $dbpassword = $attr{"sec$dblogname"}{secret}; + my $err; + + # Background-Startzeit + my $bst = [gettimeofday]; + + Log3 ($name, 4, "DbRep $name -> Start BlockingCall devren_Push"); + + my $dbh; + eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoCommit => 1, AutoInactiveDestroy => 1 });}; + + if ($@) { + $err = encode_base64($@,""); + Log3 ($name, 2, "DbRep $name - $@"); + Log3 ($name, 4, "DbRep $name -> BlockingCall devren_Push finished"); + return "$name|''|''|$err"; + } + + my $olddev = delete $hash->{HELPER}{OLDDEV}; + my $newdev = delete $hash->{HELPER}{NEWDEV}; + + # SQL zusammenstellen für DB-Operation + + Log3 ($name, 5, "DbRep $name -> Rename old device name \"$olddev\" to new device name \"$newdev\" in database $dblogname "); + + # SQL-Startzeit + my $st = [gettimeofday]; + + $dbh->begin_work(); + my $sth = $dbh->prepare_cached("UPDATE history SET TIMESTAMP=TIMESTAMP,DEVICE=? WHERE DEVICE=? ") ; + eval {$sth->execute($newdev, $olddev);}; + + my $urow; + if ($@) { + $err = encode_base64($@,""); + Log3 ($name, 2, "DbRep $name - Failed to rename old device name \"$olddev\" to new device name \"$newdev\": $@"); + $dbh->rollback(); + $dbh->disconnect(); + Log3 ($name, 4, "DbRep $name -> BlockingCall devren_Push finished"); + return "$name|''|''|$err"; + } else { + $dbh->commit(); + $urow = $sth->rows; + $dbh->disconnect(); + } + + # SQL-Laufzeit ermitteln + my $rt = tv_interval($st); + + Log3 ($name, 4, "DbRep $name -> BlockingCall devren_Push finished"); + + # Background-Laufzeit ermitteln + my $brt = tv_interval($bst); + + $rt = $rt.",".$brt; + + return "$name|$urow|$rt|0|$olddev|$newdev"; +} + +#################################################################################################### +# Auswertungsroutine DB deviceRename +#################################################################################################### + +sub devren_Done($) { + my ($string) = @_; + my @a = split("\\|",$string); + my $hash = $defs{$a[0]}; + my $name = $hash->{NAME}; + my $urow = $a[1]; + my $bt = $a[2]; + my ($rt,$brt) = split(",", $bt); + my $err = $a[3]?decode_base64($a[3]):undef; + my $olddev = $a[4]; + my $newdev = $a[5]; + + Log3 ($name, 4, "DbRep $name -> Start BlockingCall devren_Done"); + + + + if ($err) { + readingsSingleUpdate($hash, "errortext", $err, 1); + readingsSingleUpdate($hash, "state", "error", 1); + delete($hash->{HELPER}{RUNNING_PID}); + Log3 ($name, 4, "DbRep $name -> BlockingCall devren_Done finished"); + return; + } + + # only for this block because of warnings if details of readings are not set + no warnings 'uninitialized'; + + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, "number_lines_updated", $urow); + readingsBulkUpdate($hash, "device_renamed", "old: ".$olddev." to new: ".$newdev) if ($urow != 0); + readingsBulkUpdate($hash, "device_not_renamed", "WARNING - old: ".$olddev." not found, not renamed to new: ".$newdev) if ($urow == 0); + readingsBulkUpdate($hash, "background_processing_time", sprintf("%.4f",$brt)) if(AttrVal($name, "showproctime", undef)); + readingsBulkUpdate($hash, "sql_processing_time", sprintf("%.4f",$rt)) if(AttrVal($name, "showproctime", undef)); + readingsBulkUpdate($hash, "state", "done"); + readingsEndUpdate($hash, 1); + + if ($urow != 0) { + Log3 ($name, 3, "DbRep ".(($hash->{ROLE} eq "Agent")?"Agent ":"")."$name - DEVICE renamed in \"$hash->{DATABASE}\", old: \"$olddev\", new: \"$newdev\", amount: $urow "); + } else { + Log3 ($name, 3, "DbRep ".(($hash->{ROLE} eq "Agent")?"Agent ":"")."$name - WARNING - old device \"$olddev\" was not found in database \"$hash->{DATABASE}\" "); + } + + delete($hash->{HELPER}{RUNNING_PID}); + Log3 ($name, 4, "DbRep $name -> BlockingCall devren_Done finished"); + +return; +} + #################################################################################################### # nichtblockierende DB-Abfrage fetchrows @@ -2490,7 +2735,7 @@ sub expfile_ParseDone($) { readingsEndUpdate($hash, 1); my $rows = $ds.$rds.$nrows; - Log3 ($name, 3, "DbRep $name - Number of exported datasets from $hash->{dbloghash}{NAME} to file ".AttrVal($name, "expimpfile", undef).": $rows"); + Log3 ($name, 3, "DbRep $name - Number of exported datasets from $hash->{DATABASE} to file ".AttrVal($name, "expimpfile", undef).": $rows"); delete($hash->{HELPER}{RUNNING_PID}); @@ -2678,7 +2923,7 @@ sub impfile_PushDone($) { readingsBulkUpdate($hash, "state", "done"); readingsEndUpdate($hash, 1); - Log3 ($name, 3, "DbRep $name - Number of imported datasets to $hash->{dbloghash}{NAME} from file ".AttrVal($name, "expimpfile", undef).": $irowdone"); + Log3 ($name, 3, "DbRep $name - Number of imported datasets to $hash->{DATABASE} from file ".AttrVal($name, "expimpfile", undef).": $irowdone"); delete($hash->{HELPER}{RUNNING_PID}); Log3 ($name, 4, "DbRep $name -> BlockingCall impfile_PushDone finished"); @@ -2698,6 +2943,18 @@ my $name = $hash->{NAME}; delete($hash->{HELPER}{RUNNING_PID}); } +#################################################################################################### +# Browser Refresh nach DB-Abfrage +#################################################################################################### +sub browser_refresh($) { + return; + + my ($hash) = @_; + RemoveInternalTimer($hash, "browser_refresh"); + # FW_directNotify("#FHEMWEB:$name", "location.reload(true);","" ); + map { FW_directNotify("#FHEMWEB:$_", "location.reload(true)", "") } devspec2array("WEB.*"); +return; +} ################################################################################################################ @@ -2895,18 +3152,25 @@ return ($runtime,$runtime_string,$runtime_string_first,$runtime_string_next,$ll)
+ + To activate the function "Autorename" the attribute "role" has to be assigned to a defined DbRep-device. The standard role after DbRep definition is "Client. + Please read more in section DbRep-Agent .

FHEM-Forum:
- neues Modul 93_DbRep - Auswertungen und Reporting von Datenbankinhalten (DbLog).

+ Modul 93_DbRep - Reporting and Management of database content (DbLog).

+
+ Preparations

The module requires the usage of a DbLog instance and the credentials of the database definition will be used. (currently tested with MySQL and SQLite).
@@ -2953,10 +3217,32 @@ return ($runtime,$runtime_string,$runtime_string_first,$runtime_string_next,$ll)

@@ -3040,6 +3327,7 @@ return ($runtime,$runtime_string,$runtime_string_first,$runtime_string_next,$ll)
  • expimpfile - Path/filename for data export/import

  • reading - selection of a particular reading

  • readingNameMap - the name of the analyzed reading can be overwritten for output

  • +
  • 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

  • showproctime - if set, the reading "sql_processing_time" shows the required execution time (in seconds) for the sql-requests. This is not calculated for a single sql-statement, but the summary of all sql-statements necessara for within an executed DbRep-function in background.

  • timestamp_begin - begin of data selection (*)

  • @@ -3088,6 +3376,61 @@ return ($runtime,$runtime_string,$runtime_string_first,$runtime_string_next,$ll) + +DbRep Agent - automatic change of device names in databases and DbRep-definitions after FHEM "rename" command + +
    + =end html =begin html_DE @@ -3100,7 +3443,7 @@ return ($runtime,$runtime_string,$runtime_string_first,$runtime_string_next,$ll) Aggregationen auszuwerten und als Readings darzustellen. Die Abgrenzung der zu berücksichtigenden Datenbankinhalte erfolgt durch die Angabe von Device, Reading und die Zeitgrenzen für Auswertungsbeginn bzw. Auswertungsende.

    - Alle Datenbankoperationen werden nichtblockierend ausgeführt. Die Ausführungszeit der SQL-Hintergrundoperationen kann optional ebenfalls als Reading bereitgestellt + Alle Datenbankoperationen werden nichtblockierend ausgeführt. Die Ausführungszeit der (SQL)-Hintergrundoperationen kann optional ebenfalls als Reading bereitgestellt werden (siehe Attribute).
    Alle vorhandenen Readings werden vor einer neuen Operation gelöscht. Durch das Attribut "readingPreventFromDel" kann eine Komma separierte Liste von Readings angegeben werden die nicht gelöscht werden sollen.

    @@ -3110,17 +3453,22 @@ return ($runtime,$runtime_string,$runtime_string_first,$runtime_string_next,$ll)
    + + Zur Aktivierung der Funktion "Autorename" wird dem definierten DbRep-Device mit dem Attribut "role" die Rolle "Agent" zugewiesen. Die Standardrolle nach Definition + ist "Client". Mehr ist dazu im Abschnitt DbRep-Agent beschrieben.

    FHEM-Forum:
    - neues Modul 93_DbRep - Auswertungen und Reporting von Datenbankinhalten (DbLog).

    + Modul 93_DbRep - Reporting und Management von Datenbankinhalten (DbLog).

    Voraussetzungen

    @@ -3153,7 +3501,7 @@ return ($runtime,$runtime_string,$runtime_string_first,$runtime_string_next,$ll)

    - (<Name der DbLog-instanz> - es wird der Name der auszuwertenden DBLog-Datenbankdefinition angegeben) + (<Name der DbLog-instanz> - es wird der Name der auszuwertenden DBLog-Datenbankdefinition angegeben nicht der Datenbankname selbst) @@ -3164,15 +3512,39 @@ return ($runtime,$runtime_string,$runtime_string_first,$runtime_string_next,$ll) @@ -3261,6 +3634,7 @@ return ($runtime,$runtime_string,$runtime_string_first,$runtime_string_next,$ll)
  • reading - Abgrenzung der DB-Selektionen auf ein bestimmtes Reading

  • readingNameMap - der Name des ausgewerteten Readings wird mit diesem String für die Anzeige überschrieben

  • readingPreventFromDel - Komma separierte Liste von Readings die vor einer neuen Operation nicht gelöscht werden sollen

  • +
  • role - die Rolle des DbRep-Device. Standard ist "Client". Die Rolle "Agent" ist im Abschnitt DbRep-Agent beschrieben.

  • showproctime - wenn gesetzt, zeigt das Reading "sql_processing_time" die benötigte Abarbeitungszeit (in Sekunden) für die SQL-Ausführung der durchgeführten Funktion. Dabei wird nicht ein einzelnes SQl-Statement, sondern die Summe aller notwendigen SQL-Abfragen innerhalb der jeweiligen Funktion betrachtet.

  • timestamp_begin - der zeitliche Beginn für die Datenselektion (*)

  • timestamp_end - das zeitliche Ende für die Datenselektion. Wenn nicht gesetzt wird immer die aktuelle Datum/Zeit-Kombi für das Ende der Selektion eingesetzt. (*)

  • @@ -3303,9 +3677,65 @@ return ($runtime,$runtime_string,$runtime_string_first,$runtime_string_next,$ll)
  • background_processing_time - die gesamte Prozesszeit die im Hintergrund/Blockingcall verbraucht wird

  • sql_processing_time - der Anteil der Prozesszeit die für alle SQL-Statements der ausgeführten Operation verbraucht wird

  • -

    +
    + +DbRep Agent - automatisches Ändern von Device-Namen in Datenbanken und DbRep-Definitionen nach FHEM "rename" Kommando + +
    + + =end html_DE =cut