]",
+# ....
+# }
+####################################################################################################
+sub DbRep_autoForward ($$$) {
+ my ($name,$reading,$value) = @_;
+ my $hash = $defs{$name};
+ my $av = AttrVal($name, "autoForward", "");
+ my ($sr,$af);
+
+ return if(!$av);
+
+ $av =~ m/^\{(.*)\}/s;
+ $av = $1;
+ $av =~ s/["\n]//g;
+
+ my @a = split(",",$av);
+
+ $av = "{ ";
+ my $i = 0;
+ foreach (@a) {
+ $av .= "\"$i\" => \"$_\",";
+ $i++;
+ }
+ $av .= " }";
+
+ $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($@);
+ return;
+ }
+
+ foreach 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);
+ next if(!$ddev);
+ if(!$defs{$ddev}) { # Vorhandensein Destination Device prüfen
+ Log3($name, 2, "$name - WARNING - Forward reading \"$reading\" not possible, device \"$ddev\" doesn't exist");
+ next;
+ }
+
+ if(!$srr || $reading !~ /^$srr$/) {
+ # 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 "_"
+ Log3($name, 4, "$name - Forward reading \"$reading\" to \"$ddev:$dr\" ");
+ CommandSetReading(undef, "$ddev $dr $value");
+ }
+
+return;
+}
+
####################################################################################################
# Anzeige von laufenden Blocking Prozessen
####################################################################################################
-sub DbRep_getblockinginfo($@) {
+sub DbRep_getblockinginfo ($) {
my ($hash) = @_;
my $name = $hash->{NAME};
@@ -9863,7 +9958,7 @@ sub DbRep_getblockinginfo($@) {
ReadingsBulkUpdateValue ($hash,"Blocking_Count",$#rows+1);
ReadingsBulkUpdateTimeState($hash,undef,undef,"done");
- readingsEndUpdate($hash, 1);
+ readingsEndUpdate ($hash, 1);
return;
}
@@ -10160,6 +10255,7 @@ sub DbRep_userexit ($$$) {
$cmd = "{".$cmd."}";
my $r = AnalyzeCommandChain(undef, $cmd);
}
+
return;
}
@@ -10817,33 +10913,33 @@ sub DbRep_OutputWriteToDB($$$$$) {
foreach my $row (@arr) {
my @a = split("#", $row);
my $runtime_string = $a[0]; # Aggregations-Alias (nicht benötigt)
- $value = defined($a[1])?(looks_like_number($a[1])?sprintf("%.4f",$a[1]):$a[1]):undef; # Version 8.32.2
+ $value = defined($a[1])?(looks_like_number($a[1])?sprintf("%.4f",$a[1]):undef):undef; # in Version 8.40.0 geändert
$rsf = $a[2]; # Runtime String first - Datum / Zeit für DB-Speicherung
($date,$time) = split("_",$rsf);
$time =~ s/-/:/g if($time);
$rsn = $a[3]; # Runtime String next - Datum / Zeit für DB-Speicherung
($ndate,$ntime) = split("_",$rsn);
$ntime =~ s/-/:/g if($ntime);
+
+ if($aggr =~ /no|day|week|month|year/) {
+ $time = "00:00:01" if($time !~ /^(\d{2}):(\d{2}):(\d{2})$/ || $hash->{LASTCMD} =~ /\bwriteToDB(Single)*?\b/); # https://forum.fhem.de/index.php/topic,105787.msg1013920.html#msg1013920
+ $ntime = "23:59:59" if($ntime !~ /^(\d{2}):(\d{2}):(\d{2})$/ || $hash->{LASTCMD} =~ /\bwriteToDB(Single)*?\b/);
+ ($year,$mon,$mday) = split("-", $ndate);
+ $corr = ($i != $ele) ? 86400 : 0;
+ $t1 = fhemTimeLocal(59, 59, 23, $mday, $mon-1, $year-1900)-$corr;
+ ($ndate,undef) = split(" ",FmtDateTime($t1));
- if($time !~ /^(\d{2}):(\d{2}):(\d{2})$/) {
- if($aggr =~ /no|day|week|month|year/) {
- $time = "00:00:01"; # https://forum.fhem.de/index.php/topic,105787.msg1013920.html#msg1013920
- $ntime = "23:59:59";
+ } elsif ($aggr =~ /hour/) {
+ $hour = (split(":", $time))[0];
+ $time = "$hour:00:01" if($time !~ /^(\d{2}):(\d{2}):(\d{2})$/ || $hash->{LASTCMD} =~ /\bwriteToDB(Single)*?\b/); # https://forum.fhem.de/index.php/topic,105787.msg1013920.html#msg1013920
+ $ntime = "$hour:59:59" if($ntime !~ /^(\d{2}):(\d{2}):(\d{2})$/ || $hash->{LASTCMD} =~ /\bwriteToDB(Single)*?\b/);
+ if ($ntime eq "23:59:59") {
($year,$mon,$mday) = split("-", $ndate);
- $corr = ($i != $ele) ? 86400 : 0;
- $t1 = fhemTimeLocal(59, 59, 23, $mday, $mon-1, $year-1900)-$corr;
- ($ndate,undef) = split(" ",FmtDateTime($t1));
- } elsif ($aggr =~ /hour/) {
- $hour = $time;
- $time = "$hour:00:01"; # https://forum.fhem.de/index.php/topic,105787.msg1013920.html#msg1013920
- $ntime = "$hour:59:59";
- if ($ntime eq "23:59:59") {
- ($year,$mon,$mday) = split("-", $ndate);
- $t1 = fhemTimeLocal(59, 59, 23, $mday, $mon-1, $year-1900)-86400;
- ($ndate,undef) = split(" ",FmtDateTime($t1));
- }
+ $t1 = fhemTimeLocal(59, 59, 23, $mday, $mon-1, $year-1900)-86400;
+ ($ndate,undef) = split(" ",FmtDateTime($t1));
}
}
+
if (defined $value) {
# Daten auf maximale Länge beschneiden (DbLog-Funktion !)
($device,$type,$event,$reading,$value,$unit) = DbLog_cutCol($dbloghash,$device,$type,$event,$reading,$value,$unit);
@@ -11138,15 +11234,17 @@ sub DbRep_WriteToDB($$$@) {
if (lc($DbLogType) =~ m(history)) {
# insert history mit/ohne primary key
- if ($usepkh && $dbloghash->{MODEL} eq 'MYSQL') {
- eval { $sth_ih = $dbh->prepare_cached("INSERT IGNORE INTO history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); };
- } elsif ($usepkh && $dbloghash->{MODEL} eq 'SQLITE') {
- eval { $sth_ih = $dbh->prepare_cached("INSERT OR IGNORE INTO history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); };
- } elsif ($usepkh && $dbloghash->{MODEL} eq 'POSTGRESQL') {
- eval { $sth_ih = $dbh->prepare_cached("INSERT INTO history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?) ON CONFLICT DO NOTHING"); };
- } else {
- eval { $sth_ih = $dbh->prepare_cached("INSERT INTO history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); };
- }
+ eval {
+ if ($usepkh && $dbloghash->{MODEL} eq 'MYSQL') {
+ $sth_ih = $dbh->prepare_cached("INSERT IGNORE INTO history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)");
+ } elsif ($usepkh && $dbloghash->{MODEL} eq 'SQLITE') {
+ $sth_ih = $dbh->prepare_cached("INSERT OR IGNORE INTO history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)");
+ } elsif ($usepkh && $dbloghash->{MODEL} eq 'POSTGRESQL') {
+ $sth_ih = $dbh->prepare_cached("INSERT INTO history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?) ON CONFLICT DO NOTHING");
+ } else {
+ $sth_ih = $dbh->prepare_cached("INSERT INTO history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)");
+ }
+ };
if ($@) {
$err = $@;
Log3 ($name, 2, "DbRep $name - $@");
@@ -11599,14 +11697,14 @@ sub DbRep_modAssociatedWith ($$$) {
foreach my $e (@edvs) {
$e =~ s/%/\.*/g if($e !~ /^%$/); # SQL Wildcard % auflösen
@edvspcs = devspec2array($e);
- @edvspcs = map {s/\.\*/%/g; $_; } @edvspcs;
+ @edvspcs = map { my $e = $_; $e =~ s/\.\*/%/xg; } @edvspcs;
if((map {$_ =~ /%/;} @edvspcs) && $edevice !~ /^%$/) { # Devices mit Wildcard (%) aussortieren, die nicht aufgelöst werden konnten
$edevswc .= "|" if($edevswc);
$edevswc .= join(" ",@edvspcs);
} else {
$edevs .= "|" if($edevs);
- $edevs .= join("|",@edvspcs);
- }
+ $edevs .= join("|",@edvspcs);
+ }
}
}
@@ -11861,7 +11959,7 @@ return;
- averageValue [display | writeToDB | writeToDBSingle]
+ averageValue [display | writeToDB | writeToDBSingle | writeToDBInTime]
- calculates an average value of the database field "VALUE" in the time limits
of the possible time.*-attributes.
@@ -11871,10 +11969,18 @@ return;
is used for Averaging defined.
If none or the option display is specified, the results are only displayed. With
- the options writeToDB or writeToDBSingle the calculation results are written
- with a new reading name into the database.
- writeToDB writes one value each at the beginning and end of an evaluation period.
- writeToDBSingle writes only one value at the end of an evaluation period.
+ the options writeToDB, writeToDBSingle or writeToDBInTime the calculation results are written
+ with a new reading name into the database.
+
+
+
+
+ writeToDB | : writes one value each with the time stamps 00:00:01 and 23:59:59 within the respective evaluation period |
+ writeToDBSingle | : writes only one value with the time stamp 23:59:59 at the end of an evaluation period |
+ writeToDBInTime | : writes a value at the beginning and end of the time limits of an evaluation period |
+
+
+
The new reading name is formed from a prefix and the original reading name,
where the original reading name can be replaced by the attribute "readingNameMap".
@@ -13090,7 +13196,7 @@ return;
- sumValue [display | writeToDB | writeToDBSingle]
+ sumValue [display | writeToDB | writeToDBSingle | writeToDBInTime]
- Calculates the total values of the database field "VALUE" in the time limits
of the possible time.*-attributes.
@@ -13099,10 +13205,18 @@ return;
into the database.
If none or the option display is specified, the results are only displayed. With
- the options writeToDB or writeToDBSingle the calculation results are written
- with a new reading name into the database.
- writeToDB writes one value each at the beginning and end of an evaluation period.
- writeToDBSingle writes only one value at the end of an evaluation period.
+ the options writeToDB, writeToDBSingle or writeToDBInTime the calculation results are written
+ with a new reading name into the database.
+
+
+
+
+ writeToDB | : writes one value each with the time stamps 00:00:01 and 23:59:59 within the respective evaluation period |
+ writeToDBSingle | : writes only one value with the time stamp 23:59:59 at the end of an evaluation period |
+ writeToDBInTime | : writes a value at the beginning and end of the time limits of an evaluation period |
+
+
+
The new reading name is formed from a prefix and the original reading name,
where the original reading name can be replaced by the attribute "readingNameMap".
@@ -13388,6 +13502,35 @@ return $ret;
allowDeletion - unlocks the delete-function
+
+ 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>]",
+ ...
+}
+
+
+ Wildcards (.*) are permitted in the specification <source-reading>.
+
+
+{
+ ".*" => "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 - specifies the calculation variant of average peak by "averageValue".
@@ -14381,7 +14524,7 @@ sub bdump {
- averageValue [display | writeToDB | writeToDBSingle]
+ averageValue [display | writeToDB | writeToDBSingle | writeToDBInTime]
- berechnet einen Durchschnittswert des Datenbankfelds "VALUE" in den Zeitgrenzen
der möglichen time.*-Attribute.
@@ -14391,10 +14534,18 @@ sub bdump {
Mittelwertermittlung definiert.
Ist keine oder die Option display angegeben, werden die Ergebnisse nur angezeigt. Mit
- den Optionen writeToDB bzw. writeToDBSingle werden die Berechnungsergebnisse
- mit einem neuen Readingnamen in der Datenbank gespeichert.
- writeToDB schreibt jeweils einen Wert am Anfang und am Ende einer Auswertungsperiode.
- writeToDBSingle schreibt nur einen Wert am Ende einer Auswertungsperiode.
+ den Optionen writeToDB, writeToDBSingle bzw. writeToDBInTime werden die Berechnungsergebnisse
+ mit einem neuen Readingnamen in der Datenbank gespeichert.
+
+
+
+
+ writeToDB | : schreibt jeweils einen Wert mit den Zeitstempeln 00:00:01 und 23:59:59 innerhalb der jeweiligen Auswertungsperiode |
+ writeToDBSingle | : schreibt nur einen Wert mit dem Zeitstempel 23:59:59 am Ende einer Auswertungsperiode |
+ writeToDBInTime | : schreibt jeweils einen Wert am Anfang und am Ende der Zeitgrenzen einer Auswertungsperiode |
+
+
+
Der neue Readingname wird aus einem Präfix und dem originalen Readingnamen gebildet,
wobei der originale Readingname durch das Attribut "readingNameMap" ersetzt werden kann.
@@ -15640,7 +15791,7 @@ sub bdump {
- sumValue [display | writeToDB | writeToDBSingle]
+ sumValue [display | writeToDB | writeToDBSingle | writeToDBInTime]
- Berechnet die Summenwerte des Datenbankfelds "VALUE" in den Zeitgrenzen
der möglichen time.*-Attribute.
@@ -15649,10 +15800,18 @@ sub bdump {
Readings in die Datenbank geschrieben werden.
Ist keine oder die Option display angegeben, werden die Ergebnisse nur angezeigt. Mit
- den Optionen writeToDB bzw. writeToDBSingle werden die Berechnungsergebnisse
- mit einem neuen Readingnamen in der Datenbank gespeichert.
- writeToDB schreibt jeweils einen Wert am Anfang und am Ende einer Auswertungsperiode.
- writeToDBSingle schreibt nur einen Wert am Ende einer Auswertungsperiode.
+ den Optionen writeToDB, writeToDBSingle bzw. writeToDBInTime werden die Berechnungsergebnisse
+ mit einem neuen Readingnamen in der Datenbank gespeichert.
+
+
+
+
+ writeToDB | : schreibt jeweils einen Wert mit den Zeitstempeln 00:00:01 und 23:59:59 innerhalb der jeweiligen Auswertungsperiode |
+ writeToDBSingle | : schreibt nur einen Wert mit dem Zeitstempel 23:59:59 am Ende einer Auswertungsperiode |
+ writeToDBInTime | : schreibt jeweils einen Wert am Anfang und am Ende der Zeitgrenzen einer Auswertungsperiode |
+
+
+
Der neue Readingname wird aus einem Präfix und dem originalen Readingnamen gebildet,
wobei der originale Readingname durch das Attribut "readingNameMap" ersetzt werden kann.
@@ -15948,6 +16107,37 @@ return $ret;
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:
+
+
+{
+ "<source-reading> => "<dest.device> [=> <dest.-reading>]",
+ "<source-reading> => "<dest.device> [=> <dest.-reading>]",
+ ...
+}
+
+
+ In der Angabe <source-reading> sind Wildcards (.*) erlaubt.
+
+
+{
+ ".*" => "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 - legt die Berechnungsvariante für die Ermittlung des Durchschnittswertes mit "averageValue"
fest.