diff --git a/fhem/FHEM/93_DbLog.pm b/fhem/FHEM/93_DbLog.pm index d18fc5e18..eb2bf0a7f 100644 --- a/fhem/FHEM/93_DbLog.pm +++ b/fhem/FHEM/93_DbLog.pm @@ -42,9 +42,9 @@ DbLog_Define($@) my @a = split("[ \t][ \t]*", $def); return "wrong syntax: define DbLog configuration regexp" - if(int(@a) != 4); + if(int(@a) != 4); - my $regexp = $a[3]; + my $regexp = $a[3]; eval { "Hallo" =~ m/^$regexp$/ }; return "Bad regexp: $@" if($@); @@ -100,94 +100,94 @@ DbLog_Attr(@) sub DbLog_ParseEvent($$) { - my ($type, $event)= @_; - my @result; + my ($type, $event)= @_; + my @result; - # split the event into reading and argument - # "day-temp: 22.0 (Celsius)" -> "day-temp", "22.0 (Celsius)" - my @parts = split(/: /,$event); - my $reading = shift @parts; - my $value = join(": ", @parts); - my $unit = ""; + # split the event into reading and argument + # "day-temp: 22.0 (Celsius)" -> "day-temp", "22.0 (Celsius)" + my @parts = split(/: /,$event); + my $reading = shift @parts; + my $value = join(": ", @parts); + my $unit = ""; - #default - if(!defined($reading)) { $reading = ""; } - if(!defined($value)) { $value = ""; } + #default + if(!defined($reading)) { $reading = ""; } + if(!defined($value)) { $value = ""; } - # the interpretation of the argument depends on the device type - # EMEM, M232Counter, M232Voltage return plain numbers - if(($type eq "M232Voltage") || - ($type eq "M232Counter") || - ($type eq "EMEM")) { + # the interpretation of the argument depends on the device type + # EMEM, M232Counter, M232Voltage return plain numbers + if(($type eq "M232Voltage") || + ($type eq "M232Counter") || + ($type eq "EMEM")) { } - # Onewire - elsif(($type eq "OWAD") || - ($type eq "OWSWITCH") || - ($type eq "OWMULTI")) { - $reading = "data"; - $value = $event; - } - # FS20 - elsif(($type eq "FS20") || + # Onewire + elsif(($type eq "OWAD") || + ($type eq "OWSWITCH") || + ($type eq "OWMULTI")) { + $reading = "data"; + $value = $event; + } + # FS20 + elsif(($type eq "FS20") || ($type eq "X10")) { - #@parts = split(/ /,$event); - #$reading = shift @parts; - #$value = join(" ", shift @parts); + #@parts = split(/ /,$event); + #$reading = shift @parts; + #$value = join(" ", shift @parts); - if($reading =~ m/^dim(\d+).*/o) { - $value = $1; - $reading= "dim"; - $unit= "%"; - } - if(!defined($value) || $value eq "") {$value=$reading; $reading="data";} - } + if($reading =~ m/^dim(\d+).*/o) { + $value = $1; + $reading= "dim"; + $unit= "%"; + } + if(!defined($value) || $value eq "") {$value=$reading; $reading="data";} + } # FHT elsif($type eq "FHT") { if($reading =~ m(-from[12]\ ) || $reading =~ m(-to[12]\ )) { - @parts= split(/ /,$event); - $reading= $parts[0]; - $value= $parts[1]; - $unit= ""; + @parts= split(/ /,$event); + $reading= $parts[0]; + $value= $parts[1]; + $unit= ""; } if($reading =~ m(-temp)) { $value=~ s/ \(Celsius\)//; $unit= "°C"; } if($reading =~ m(temp-offset)) { $value=~ s/ \(Celsius\)//; $unit= "°C"; } if($reading =~ m(^actuator[0-9]*)) { - if($value eq "lime-protection") { - $reading= "actuator-lime-protection"; - undef $value; - } - elsif($value =~ m(^offset:)) { - $reading= "actuator-offset"; - @parts= split(/: /,$value); - $value= $parts[1]; - if(defined $value) { - $value=~ s/%//; $value= $value*1.; $unit= "%"; - } - } - elsif($value =~ m(^unknown_)) { - @parts= split(/: /,$value); - $reading= "actuator-" . $parts[0]; - $value= $parts[1]; - if(defined $value) { - $value=~ s/%//; $value= $value*1.; $unit= "%"; - } - } - elsif($value eq "synctime") { - $reading= "actuator-synctime"; - undef $value; - } - elsif($value eq "test") { - $reading= "actuator-test"; - undef $value; - } - elsif($value eq "pair") { - $reading= "actuator-pair"; - undef $value; - } - else { - $value=~ s/%//; $value= $value*1.; $unit= "%"; - } + if($value eq "lime-protection") { + $reading= "actuator-lime-protection"; + undef $value; + } + elsif($value =~ m(^offset:)) { + $reading= "actuator-offset"; + @parts= split(/: /,$value); + $value= $parts[1]; + if(defined $value) { + $value=~ s/%//; $value= $value*1.; $unit= "%"; + } + } + elsif($value =~ m(^unknown_)) { + @parts= split(/: /,$value); + $reading= "actuator-" . $parts[0]; + $value= $parts[1]; + if(defined $value) { + $value=~ s/%//; $value= $value*1.; $unit= "%"; + } + } + elsif($value eq "synctime") { + $reading= "actuator-synctime"; + undef $value; + } + elsif($value eq "test") { + $reading= "actuator-test"; + undef $value; + } + elsif($value eq "pair") { + $reading= "actuator-pair"; + undef $value; + } + else { + $value=~ s/%//; $value= $value*1.; $unit= "%"; + } } } # KS300 @@ -201,7 +201,7 @@ DbLog_ParseEvent($$) if($reading eq "rain_raw") { $value=~ s/ \(counter\)//; $unit= ""; } if($reading eq "humidity") { $value=~ s/ \(\%\)//; $unit= "%"; } if($reading eq "israining") { - $value=~ s/ \(yes\/no\)//; + $value=~ s/ \(yes\/no\)//; $value=~ s/no/0/; $value=~ s/yes/1/; } @@ -209,10 +209,10 @@ DbLog_ParseEvent($$) # HMS elsif($type eq "HMS" || $type eq "CUL_WS" || - $type eq "OWTHERM") { + $type eq "OWTHERM") { if($event =~ m(T:.*)) { $reading= "data"; $value= $event; } if($reading eq "temperature") { $value=~ s/ \(Celsius\)//; $unit= "°C"; } - if($reading eq "temperature") { $value=~ s/([-\.\d]+).*/$1/; $unit= "°C"; } #OWTHERM + if($reading eq "temperature") { $value=~ s/([-\.\d]+).*/$1/; $unit= "°C"; } #OWTHERM if($reading eq "humidity") { $value=~ s/ \(\%\)//; $unit= "%"; } if($reading eq "battery") { $value=~ s/ok/1/; @@ -223,11 +223,11 @@ DbLog_ParseEvent($$) # BS elsif($type eq "BS") { - if($event =~ m(brightness:.*)) { - @parts= split(/ /,$event); - $reading= "lux"; - $value= $parts[4]*1.; - $unit= "lux"; + if($event =~ m(brightness:.*)) { + @parts= split(/ /,$event); + $reading= "lux"; + $value= $parts[4]*1.; + $unit= "lux"; } } @@ -250,8 +250,8 @@ DbLog_ParseEvent($$) if($reading =~ m(^pressure_trend)) { $unit= ""; } } - @result= ($reading,$value,$unit); - return @result; + @result= ($reading,$value,$unit); + return @result; } @@ -359,8 +359,8 @@ DbLog_Connect($) my $configfilename= $hash->{CONFIGURATION}; if(!open(CONFIG, $configfilename)) { - Log 1, "Cannot open database configuration file $configfilename."; - return 0; } + Log 1, "Cannot open database configuration file $configfilename."; + return 0; } my @config=; close(CONFIG); @@ -469,6 +469,9 @@ DbLog_Get($@) if($outf eq "INT") { $outf = "-"; $internal = 1; + } elsif (uc($outf) eq "WEBCHART") { + # redirect the get request to the chartQuery function + return chartQuery($hash, @_); } my @readings = (); @@ -552,7 +555,7 @@ DbLog_Get($@) DEVICE, READING, VALUE - $sqlspec{all} + $sqlspec{all} FROM history WHERE 1=1 AND $sqlspec{reading_clause} = ('".$readings[$i]->[0]."|".$readings[$i]->[1]."') @@ -570,8 +573,8 @@ DbLog_Get($@) return "Cannot execute statement $stm: $DBI::errstr"; if(uc($outf) eq "ALL") { - $retval .= "Timestamp: Device, Type, Event, Reading, Value, Unit\n"; - $retval .= "=====================================================\n"; + $retval .= "Timestamp: Device, Type, Event, Reading, Value, Unit\n"; + $retval .= "=====================================================\n"; } while( ($sql_timestamp,$sql_dev,$sql_reading,$sql_value, $type, $event, $unit)= $sth->fetchrow_array) { $writeout = 0; @@ -642,12 +645,12 @@ DbLog_Get($@) ###################### Ausgabe ########################### if($writeout) { - if(uc($outf) eq "ALL") { - $retval .= sprintf("%s: %s, %s, %s, %s, %s, %s\n", $out_tstamp, $sql_dev, $type, $event, $sql_reading, $out_value, $unit); - } else { - $out_tstamp =~ s/\ /_/g; #needed by generating plots - $retval .= "$out_tstamp $out_value\n"; - } + if(uc($outf) eq "ALL") { + $retval .= sprintf("%s: %s, %s, %s, %s, %s, %s\n", $out_tstamp, $sql_dev, $type, $event, $sql_reading, $out_value, $unit); + } else { + $out_tstamp =~ s/\ /_/g; #needed by generating plots + $retval .= "$out_tstamp $out_value\n"; + } } if(defined($sql_value) || $sql_value =~ m/^[-\.\d]+$/o){ @@ -676,11 +679,11 @@ DbLog_Get($@) $out_tstamp = DbLog_implode_datetime($lasttstamp{year}, $lasttstamp{month}, $lasttstamp{day}, $lasttstamp{hour}, "30", "00") if($readings[$i]->[3] eq "delta-h"); $out_tstamp = DbLog_implode_datetime($lasttstamp{year}, $lasttstamp{month}, $lasttstamp{day}, "00", "00", "00") if($readings[$i]->[3] eq "delta-d"); if(uc($outf) eq "ALL") { - $retval .= sprintf("%s: %s %s %s %s %s %s\n", $out_tstamp, $sql_dev, $type, $event, $sql_reading, $out_value, $unit); - } else { - $out_tstamp =~ s/\ /_/g; #needed by generating plots - $retval .= "$out_tstamp $out_value\n"; - } + $retval .= sprintf("%s: %s %s %s %s %s %s\n", $out_tstamp, $sql_dev, $type, $event, $sql_reading, $out_value, $unit); + } else { + $out_tstamp =~ s/\ /_/g; #needed by generating plots + $retval .= "$out_tstamp $out_value\n"; + } } # DatenTrenner setzen $retval .= "#$readings[$i]->[0]"; @@ -713,6 +716,126 @@ DbLog_Get($@) } return $retval; } + +################################################################ +# +# Charting Specific functions start here +# +################################################################ + +################################################################ +# +# Error handling, returns a JSON String +# +################################################################ +sub jsonError($) { + my $errormsg = $_[0]; + my $json = "{success: false, msg:'$errormsg'}\n"; + return $json; +} + + +################################################################ +# +# Prepare the SQL String +# +################################################################ +sub prepareSql(@_) { + + my $starttime = $_[5]; + $starttime =~ s/_/ /; + my $endtime = $_[6]; + $endtime =~ s/_/ /; + my $device = $_[7]; + my $userquery = $_[8]; + my $xaxis = $_[9]; + my $yaxis = $_[10]; + my $savename = $_[11]; + my ($sql, $jsonstring); + + if($userquery eq "getreadings") { + $sql = "SELECT distinct(reading) FROM current WHERE device = '".$device."'"; + } elsif($userquery eq "getdevices") { + $sql = 'SELECT distinct(device) FROM history'; + } elsif($userquery eq "timerange") { + $sql = 'SELECT '.$xaxis.', VALUE FROM history WHERE READING = "'.$yaxis.'" AND DEVICE = "'.$device.'" AND TIMESTAMP Between "'.$starttime.'" AND "'.$endtime.'";'; + } elsif($userquery eq "savechart") { + $jsonstring = '[{x:"'.$xaxis.'",y:"'.$yaxis.'",device:"'.$device.'",starttime:"'.$starttime.'",endtime:"'.$endtime.'"}]'; + $sql = "INSERT INTO history (EVENT, READING, VALUE) VALUES('".$jsonstring."', 'savedchart', '".$savename."')"; + } elsif($userquery eq "getcharts") { + $sql = 'SELECT * FROM history WHERE READING = "savedchart"'; + } else { + $sql = "error"; + } + + return $sql; +} + +################################################################ +# +# Do the query +# +################################################################ +sub chartQuery($@) { + + my $sql = prepareSql(@_); + + if ($sql eq "error") { + return jsonError("Could not setup SQL String"); + } + + my ($hash, @a) = @_; + my $dbh= $hash->{DBH}; + + # prepare the query + my $query_handle = $dbh->prepare($sql) + or return jsonError("Couldn't prepare statement: " . $dbh->errstr . ", SQL was: " .$sql); + + # execute the query + $query_handle->execute() + or return jsonError("Couldn't execute statement: " . $query_handle->errstr); + + my $columns = $query_handle->{'NAME'}; + my $columncnt; + + # When columns are empty but execution was successful, we have done a successful INSERT + if($columns) { + $columncnt = scalar @$columns; + } else { + return "{success: true, msg:'Insert ok'}\n"; + } + + my $i = 0; + my $jsonstring = "["; + + while ( my @data = $query_handle->fetchrow_array()) { + + if($i == 0) { + $jsonstring .= "{"; + } else { + $jsonstring .= ",{"; + } + + for ($i = 0; $i < $columncnt; $i++) { + $jsonstring .= "'"; + $jsonstring .= uc($query_handle->{NAME}->[$i]); + $jsonstring .= "':'"; + + if (defined $data[$i]) { + $jsonstring .= $data[$i]; + } + + if($i == ($columncnt -1)) { + $jsonstring .= "'"; + } else { + $jsonstring .= "',"; + } + } + $jsonstring .= "}"; + } + $jsonstring .= "]"; + return $jsonstring; +} ################################################################ @@ -808,8 +931,8 @@ DbLog_Get($@) A dummy parameter for FileLog compatibility. Always set to -
  • <out>
    A dummy parameter for FileLog compatibility. Set it to - - to check the output for plot-computing.
    Set it to the special keyword - all to get all columns from Database.
  • + to check the output for plot-computing.
    Set it to the special keyword + all to get all columns from Database.
  • <from> / <to>
    Used to select the data. Please use the following timeformat or an initial substring of it:
    @@ -861,10 +984,65 @@ DbLog_Get($@)
  • get myDbLog - all 2012-11-10 2012-11-20 KS300:temperature
  • get myDbLog - - 2012-11-10 2012-11-20 KS300:temperature KS300:rain::delta-h KS300:rain::delta-d
  • get myDbLog - - 2012-11-10 2012-11-20 MyFS20:data:::$val=~s/(on|off).*/$1eq"on"?1:0/eg
    - return 1 for all occurance of on* (on|on-for-timer etc) and 0 for all off*
  • -
  • get myDbLog - - 2012-11-10 2012-11-20 Bodenfeuchte:data:::$val=~s/.*B:\s([-\.\d]+).*/$1/eg
    - Example of OWAD: value like this: "A: 49.527 % B: 66.647 % C: 9.797 % D: 0.097 V"
    - and output for port B is like this: 2012-11-20_10:23:54 66.647
  • + return 1 for all occurance of on* (on|on-for-timer etc) and 0 for all off* +
  • get myDbLog - - 2012-11-10 2012-11-20 Bodenfeuchte:data:::$val=~s/.*B:\s([-\.\d]+).*/$1/eg
    + Example of OWAD: value like this: "A: 49.527 % B: 66.647 % C: 9.797 % D: 0.097 V"
    + and output for port B is like this: 2012-11-20_10:23:54 66.647
  • + +

    + + + Get when used for webcharts + @@ -964,7 +1142,7 @@ DbLog_Get($@) Ermittlung der Daten aus der Datenbank für die Plotgenerierung zu prüfen.
    Durchd ie Angabe des Schlüsselworts all werden alle Spalten der Datenbank ausgegeben. - +
  • <from> / <to>
    Wird benutzt um den Zeitraum der Daten einzugrenzen. Es ist das folgende Zeitformat oder ein Teilstring davon zu benutzen:
    @@ -1024,13 +1202,70 @@ DbLog_Get($@)
  • get myDbLog - all 2012-11-10 2012-11-20 KS300:temperature
  • get myDbLog - - 2012-11-10 2012-11-20 KS300:temperature KS300:rain::delta-h KS300:rain::delta-d
  • get myDbLog - - 2012-11-10 2012-11-20 MyFS20:data:::$val=~s/(on|off).*/$1eq"on"?1:0/eg
    - gibt 1 zurück für alle Ausprägungen von on* (on|on-for-timer etc) und 0 für alle off*
  • -
  • get myDbLog - - 2012-11-10 2012-11-20 Bodenfeuchte:data:::$val=~s/.*B:\s([-\.\d]+).*/$1/eg
    - Beispiel von OWAD: Ein Wert wie z.B.: "A: 49.527 % B: 66.647 % C: 9.797 % D: 0.097 V"
    - und die Ausgabe ist für das Reading B folgende: 2012-11-20_10:23:54 66.647
  • + gibt 1 zurück für alle Ausprägungen von on* (on|on-for-timer etc) und 0 für alle off* +
  • get myDbLog - - 2012-11-10 2012-11-20 Bodenfeuchte:data:::$val=~s/.*B:\s([-\.\d]+).*/$1/eg
    + Beispiel von OWAD: Ein Wert wie z.B.: "A: 49.527 % B: 66.647 % C: 9.797 % D: 0.097 V"
    + und die Ausgabe ist für das Reading B folgende: 2012-11-20_10:23:54 66.647


  • + + Get für die Nutzung von webcharts + + Attributes