2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-05-02 13:05:12 +00:00

98_logProxy.pm: new module 98_logProxy.pm added

git-svn-id: https://svn.fhem.de/fhem/trunk@6999 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
justme-1968 2014-11-16 16:49:31 +00:00
parent 35662776f5
commit 49203c8074
3 changed files with 970 additions and 0 deletions

View File

@ -1,5 +1,6 @@
# 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: new module 98_logProxy.pm added (justme1968)
- change: 66_ECMD: ReadyFn added (fixes issue under Windows)
- change: 02_RSS: use a GUID in RSS; urlq source for img command
- feature: 70_PushNotifier improve usebility, configuration without cURL (xusader)

968
fhem/FHEM/98_logProxy.pm Normal file
View File

@ -0,0 +1,968 @@
# $Id$
##############################################################################
#
# This file is part of fhem.
#
# Fhem is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# Fhem is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
package main;
use strict;
use warnings;
use SetExtensions;
sub logProxy_Initialize($)
{
my ($hash) = @_;
$hash->{DefFn} = "logProxy_Define";
$hash->{UndefFn} = "logProxy_Undefine";
#$hash->{SetFn} = "logProxy_Set";
$hash->{GetFn} = "logProxy_Get";
#$hash->{AttrList} = "disable:1 ";
$hash->{SVG_sampleDataFn} = "logProxy_sampleDataFn";
}
sub logProxy_Define($$)
{
my ($hash, $def) = @_;
my @args = split("[ \t]+", $def);
my $name = $args[0];
my $usage = "Usage: define <name> logProxy";
return $usage if( int(@args) != 2 );
my $d = $modules{logProxy}{defptr};
return "logProxy device already defined as $d->{NAME}." if( defined($d) );
$modules{logProxy}{defptr} = $hash;
$hash->{STATE} = 'Initialized';
return undef;
}
sub logProxy_Undefine($$)
{
my ($hash,$arg) = @_;
my $name = $hash->{NAME};
delete $modules{logProxy}{defptr};
return undef;
}
sub
logProxy_sampleDataFn($$$$$)
{
my ($ldName, $flog, $max, $conf, $wName) = @_;
my $desc = "Type,Spec";
my $columns = "ConstX,ConstY,Func,FileLog,DbLog";
my @htmlArr;
$max = 16 if($max > 16);
for(my $r=0; $r < $max; $r++) {
my @f = split(":", ($flog->[$r] ? $flog->[$r] : ":"), 6);
my $ret = "";
$ret .= SVG_sel("par_${r}_0", $columns, $f[0]);
$ret .= SVG_txt("par_${r}_1", "", join(":", @f[1..@f-1]), 30);
push @htmlArr, $ret;
}
my @example;
push @example, 'ConstY:0';
push @example, 'ConstY:$data{avg1}';
push @example, 'ConstY:$data{avg2}';
push @example, 'DbLog:myDB:myReading';
push @example, 'FileLog:myFileLog:4:myReading';
push @example, 'FileLog:FileLog_&lt;SPEC1&gt;:4:&lt;SPEC1&gt;.power';
push @example, 'FileLog:FileLog_&lt;SPEC1&gt;:4:&lt;SPEC1&gt;.consumption';
push @example, 'Func:logProxy_WeekProfile2Plot("HCB",$from,$to)';
push @example, 'Func:logProxy_WeekProfile2Plot("myHeatingControl",$from,$to,"(\\d*)\$")';
push @example, 'ConstX:logProxy_shiftTime($from,60*60*2),$data{min1},$data{max1}';
#Log 3, Dumper $desc;
#Log 3, Dumper @htmlArr;
#Log 3, Dumper $example;
return ($desc, \@htmlArr, join("<br>", @example));
}
sub
logProxy_Set($@)
{
return undef;
}
#WeekProfile format: {$wday}{$time}{$value} with 0 = sunday
sub
logProxy_Heating_Controll2WeekProfile($)
{
my ($d) = @_;
return undef if( !defined($defs{$d}) );
return undef if( !defined($defs{$d}->{helper}{SWITCHINGTIME}) );
return $defs{$d}->{helper}{SWITCHINGTIME};
}
sub
logProxy_HM2WeekProfile($;$)
{
my ($d,$list) = @_;
return undef if( !defined($defs{$d}) );
# default to 1st list of tc-it
$list = "P1" if ( !$list );
# if tc-it
my @rl = sort( grep /^R_${list}_[0-7]_tempList...$/,keys %{$defs{$d}{READINGS}} );
# else cc-tc and rt
@rl = sort( grep /^R_[0-7]_tempList...$/,keys %{$defs{$d}{READINGS}} ) if( !@rl );
return undef if( !@rl );
my %profile = ();
for(my $i=0; $i<7; ++$i) {
# correct wday
my $reading = ReadingsVal($d,$rl[($i+1)%7],undef);
# collect 'until' switching times
my %tmp = ();
my @parts = split( ' ', $reading );
while( @parts ) {
my $time = shift @parts;
$tmp{$time} = shift @parts;
}
# shift 'until' switching times into 'from' switching times
# can not be done in one step if times are out of order
my %st = ();
my $time = "00:00";
foreach my $key (sort (keys %tmp)) {
$st{$time} = $tmp{$key};
$time = $key;
}
$profile{$i} = \%st;
}
return undef if (scalar (keys %profile) != 7);
return \%profile;
}
sub
logProxy_MAX2WeekProfile($)
{
my ($d) = @_;
return undef if( !defined($defs{$d}) );
my @rl = sort( grep /^weekprofile-.-...-(temp|time)$/,keys %{$defs{$d}{READINGS}} );
return undef if( !@rl );
my %profile = ();
for(my $i=0; $i<7; ++$i) {
# correct wday
my $temps = ReadingsVal($d,$rl[(($i+1)%7)*2],undef);
my $times = ReadingsVal($d,$rl[(($i+1)%7)*2+1],undef);
my %st = ();
my @temps = split( '/', $temps );
my @times = split( '/', $times );
while( @times ) {
my $temp = shift @temps;
$temp =~ s/\s*([\d\.]*).*/$1/;
my $time = shift @times;
$time =~ s/\s*(\d\d:\d\d).*/$1/;
$st{$time} = $temp;
}
$profile{$i} = \%st;
}
return \%profile;
}
# sample implementaion to plot the week profile of a Heating_Control or HM Thermostat device.
sub
logProxy_WeekProfile2Plot($$$;$)
{
my ($profile, $from, $to, $regex) = @_;
return undef if( !$profile );
if( $regex ) {
eval { "test" =~ m/$regex/ };
if( $@ ) {
Log3 undef, 3, "logProxy_WeekProfile2Plot: $regex: $@";
return undef;
}
}
if( defined($defs{$profile}) ) {
if( $defs{$profile}{TYPE} eq "Heating_Control" ) {
$profile = logProxy_Heating_Controll2WeekProfile($profile);
} elsif( $defs{$profile}{TYPE} eq "WeekdayTimer" ) {
$profile = logProxy_Heating_Controll2WeekProfile($profile);
} elsif( $defs{$profile}{TYPE} eq "CUL_HM" ) {
my ($p,$l) = split( ',', $profile, 2 );
$profile = logProxy_HM2WeekProfile($p, $l);
} elsif( $defs{$profile}{TYPE} eq "MAX" ) {
$profile = logProxy_MAX2WeekProfile($profile);
} else {
Log3 undef, 2, "logProxy_WeekProfile2Plot: $profile is not a Heating_Control, WeekdayTimer, CUL_HM or MAX device";
return undef;
}
}
#Log 3, Dumper $profile;
if( ref($profile) ne "HASH" ) {
Log3 undef, 2, "logProxy_WeekProfile2Plot: no profile hash given";
return undef;
}
my $fromsec = SVG_time_to_sec($from);
my $tosec = SVG_time_to_sec($to);
my (undef,undef,undef,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($fromsec);
my $min = 999999;
my $max = -999999;
# go back one day to get the start value, TODO: go back multiple days
$mday -= 1;
$wday -= 1;
$wday %= 7;
my $ret = "";
my $value;
my $prev_value;
my $sec = $fromsec;
# while not end of plot range reached
while( $sec < $tosec ) {
return undef if( !defined($profile->{$wday}) );
# for all switching times of current day
foreach my $st (sort (keys %{ $profile->{$wday} })) {
#remember previous value for start of plot range
$prev_value = $value;
my ($h, $m, $s) = split( ':', $st );
$s = 0 if( !$s );
$value = $profile->{$wday}{$st};
if( $regex ) {
if( $value =~ m/$regex/ ) {
Log3 undef, 4, "logProxy_WeekProfile2Plot: $value =~ m/$regex/ => $1";
$value = $1;
} else {
Log3 undef, 3, "logProxy_WeekProfile2Plot: $value =~ m/$regex/ => no match";
}
}
# map some specials to values and eco and comfort to temperatures
$value = 0 if( $value eq "off" );
$value = 1 if( $value eq "on" );
$value = 1 if( $value eq "up" );
$value = 0 if( $value eq "down" );
$value = 18 if( $value eq "eco" );
$value = 22 if( $value eq "comfort" );
# 'dirty' hack that exploits the feature that $mday can be < 0 and > 31.
# everything should better be based on a real second counter
my $timestamp = sprintf("%04d-%02d-%02d_%02d:%02d:%02d", 1900+$year, 1+$mon, $mday, $h, $m, $s );
$sec = SVG_time_to_sec($timestamp);
# skip all values before start of plot range
next if( SVG_time_to_sec($timestamp) < $fromsec );
# add first value at start of plot range
if( !$ret && $prev_value ) {
$min = $prev_value if( $prev_value < $min );
$max = $prev_value if( $prev_value > $max );
$ret .= "$from $prev_value\n";
}
# done if after end of plot range
last if( SVG_time_to_sec($timestamp) > $tosec );
$min = $value if( $value < $min );
$max = $value if( $value > $max );
# add actual controll point
$ret .= "$timestamp $value\n";
}
# next day
$mday += 1;
$wday += 1;
$wday %= 7;
}
# add last value at end of plot range
$ret .= "$to $prev_value\n";
return ($ret,$min,$max,$prev_value);
}
sub logProxy_hms2dec($){
my ($h,$m,$s) = split(":", shift);
$m = 0 if(!$m);
$s = 0 if(!$s);
my $t = $m * 60;
$t += $s;
$t /= 3600;
$t += $h;
return ($t)
}
sub logProxy_dec2hms($){
my ($t) = @_;
my $h = int($t);
my $r = ($t - $h)*3600;
my $m = int($r/60);
my $s = $r - $m*60;
return sprintf("%02d:%02d:%02d",$h,$m,$s);
}
sub
logProxy_Range2Zoom($)
{
my( $range ) = @_;
return "year" if( $range > 1+60*60*24*28*6);
return "month" if( $range > 1+60*60*24*28);
return "week" if( $range > 1+60*60*24);
return "day" if( $range > 1+60*60*6);
return "qday" if( $range > 1+60*60 );
return "hour";
}
my %logProxy_stepDefault = ( year => 60*60*24,
month => 60*60*24,
week => 60*60*6,
day => 60*60,
qday => 60*15,
hour => 60, );
# sample implementaion to plot an arbitrary function
sub
logProxy_Func2Plot($$$;$)
{
my ($from, $to, $func, $step) = @_;
my $fromsec = SVG_time_to_sec($from);
my $tosec = SVG_time_to_sec($to);
my $secs = $tosec - $fromsec;
$step = \%logProxy_stepDefault if( !$step );
$step = eval $step if( $step =~ m/^{.*}$/ );
$step = $step->{logProxy_Range2Zoom($secs)} if( ref($step) eq "HASH" );
$step = $logProxy_stepDefault{logProxy_Range2Zoom($secs)} if( !$step );
my $min = 999999;
my $max = -999999;
my $ret = "";
my $value;
for(my $sec=$fromsec; $sec<$tosec; $sec+=$step) {
my ($s,$m,$h,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($sec);
$value = eval $func;
if( $@ ) {
Log3 undef, 1, "logProxy_Func2Plot: $func: $@";
next;
}
my $timestamp = sprintf("%04d-%02d-%02d_%02d:%02d:%02d", 1900+$year, 1+$mon, $mday, $h, $m, $s );
$min = $value if( $value < $min );
$max = $value if( $value > $max );
# add actual controll point
$ret .= "$timestamp $value\n";
}
return ($ret,$min,$max,$value);
}
# shift time by offset seconds (or months if offset ends with m)
sub
logProxy_shiftTime($$)
{
my ($time, $offset) = @_;
$time =~ s/ /_/;
if( $offset =~ m/((-)?\d)*m/ ) {
my @t = split("[-_:]", $time);
$time = mktime($t[5],$t[4],$t[3],$t[2],$t[1]-1+$1,$t[0]-1900,0,0,-1);;
} else {
$time = SVG_time_to_sec($time);
$time += $offset;
}
my @t = localtime($time);
$time = sprintf("%04d-%02d-%02d_%02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);
return $time;
}
# shift plot data by offset
sub
logProxy_shiftData($$;$$)
{
my ($dp, $offset, $from, $to) = @_;
my ($dpl,$dpoff,$l) = (length($$dp), 0, "");
while($dpoff < $dpl) { # using split instead is memory hog
my $ndpoff = index($$dp, "\n", $dpoff);
if($ndpoff == -1) {
$l = substr($$dp, $dpoff);
} else {
$l = substr($$dp, $dpoff, $ndpoff-$dpoff);
}
if($l =~ m/^#/) {
} else {
my ($d, $v) = split(" ", $l);
$d = logProxy_shiftTime($d, $offset);
substr($$dp, $dpoff, 19, $d);
}
$dpoff = $ndpoff+1;
last if($ndpoff == -1);
}
}
sub
logProxy_linearInterpolate($$$$$) {
my ($t1, $v1, $t2, $v2, $t ) = @_;
my $dt = $t2 - $t1;
return $v1 if( !$dt );
my $dv = $v2 - $v1;
my $v = $v1 + $dv * ( ($t-$t1) / $dt );
return $v;
}
# clip plot data to [$from,$to] range
sub
logProxy_clipData($$$$;$)
{
my ($dp, $from, $to, $interpolate, $predict) = @_;
my $ret = "";
my $comment = "";
my ($dpl,$dpoff,$l) = (length($$dp), 0, "");
my $prev_value;
my $prev_timestamp;
my $next_value;
my $next_timestamp;
while($dpoff < $dpl) { # using split instead is memory hog
my $ndpoff = index($$dp, "\n", $dpoff);
if($ndpoff == -1) {
$l = substr($$dp, $dpoff);
} else {
$l = substr($$dp, $dpoff, $ndpoff-$dpoff);
}
if($l =~ m/^#/) {
$comment .= "$l\n";
} else {
my ($d, $v) = split(" ", $l);
my $sec = SVG_time_to_sec($d);
if( $sec < $from ) {
$prev_timestamp = $d;
$prev_value = $v;
} elsif( $sec > $to ) {
if( !$next_value ) {
$next_timestamp = $d;
$next_value = $v;
}
} else {
if( !$ret && $prev_value && $sec < $from ) {
my $value = $prev_value;
$value = logProxy_linearInterpolate( SVG_time_to_sec($prev_timestamp), $prev_value, SVG_time_to_sec($d), $v, $from ) if( $interpolate );
my @t = localtime($from);
my $timestamp = sprintf("%04d-%02d-%02d_%02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);
$ret .= "$timestamp $value\n";
}
$ret .= "$l\n";
$prev_timestamp = $d;
$prev_value = $v;
}
}
$dpoff = $ndpoff+1;
last if($ndpoff == -1);
}
#if predict is set -> extend bejond last value
if( defined($predict) && !$next_value ) {
$next_value = $prev_value;
#if $predict = 0 -> predict to end of plot
my $time = $to;
#else predict by $predict
$time = SVG_time_to_sec($prev_timestamp) + $predict if( $predict );
#but not later than now
my ($now) = gettimeofday();
$to = minNum( $time, $now );
my @t = localtime($to);
$next_timestamp = sprintf("%04d-%02d-%02d_%02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);
}
if( $next_value ) {
my $value = $next_value;
$value = logProxy_linearInterpolate( SVG_time_to_sec($prev_timestamp), $prev_value, SVG_time_to_sec($next_timestamp), $next_value, $to ) if( $interpolate );
my @t = localtime($to);
my $timestamp = sprintf("%04d-%02d-%02d_%02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);
$ret .= "$timestamp $value\n";
}
$ret .= $comment;
return $ret;
}
sub
logProxy_Get($@)
{
my ($hash, $name, @a) = @_;
#Log 3, "logProxy_Get";
#Log 3, Dumper @a;
my $inf = shift @a;
my $outf = shift @a;
my $from = shift @a;
my $to = shift @a; # Now @a contains the list of column_specs
my $internal;
if($outf && $outf eq "INT") {
$outf = "-";
$internal = 1;
}
my $ret = "";
my %data;
for(my $i = 0; $i < int(@a); $i++) {
my $j = $i+1;
$data{"min$j"} = undef;
$data{"max$j"} = undef;
$data{"avg$j"} = undef;
$data{"sum$j"} = 0;
$data{"cnt$j"} = undef;
$data{"currval$j"} = 0;
$data{"currdate$j"} = undef;
$data{"mindate$j"} = undef;
$data{"maxdate$j"} = undef;
my @fld = split(":", $a[$i]);
if( $a[$i] =~ m/^(FileLog|DbLog):([^:]*):(.*)/ ) {
my @options = split( ',', $fld[1] );
my $log_dev = $options[0];
my $infile = $fld[0] eq "DbLog" ? "HISTORY" : "CURRENT";
my $column_specs = $3;
my $extend;
my $extend_scale;
my $offset;
my $offset_scale;
my $interpolate;
my $clip;
my $predict;
if( !defined($defs{$log_dev}) ) {
Log3 $hash->{NAME}, 1, "$hash->{NAME}: $log_dev does not exist";
$ret .= "#$a[$i]\n";
next;
}
foreach my $option ( @options[1..@options-1] ) {
my ($name,$value) = split( '=', $option, 2 );
if( $value ) {
$value = eval $value;
if( $@ ) {
Log3 $hash->{NAME}, 1, "$hash->{NAME}: $option: $@";
$ret .= "#$a[$i]\n";
next;
}
}
if( $name eq "extend" ) {
$value =~ m/(-?\d*)(m?)/;
$extend = $1;
$extend_scale = $2;
$extend_scale = "" if( !$extend_scale );
$clip = 1;
} elsif( $name eq "offset" ) {
$value =~ m/(-?\d*)(m?)/;
$offset = $1;
$offset_scale = $2;
$offset_scale = "" if( !$offset_scale );
} elsif( $name eq "interpolate" ) {
$interpolate = 1;
} elsif( $name eq "clip" ) {
$clip = 1;
} elsif( $name eq "predict" ) {
$predict = 0;
$predict = $value if( defined($value) );
} else {
Log3 $hash->{NAME}, 2, "$hash->{NAME}: unknown option >$option<";
}
}
my $fromsec = SVG_time_to_sec($from);
my $tosec = SVG_time_to_sec($to);
my $from = $from;
my $to = $to;
# shift $from and $to
$from = logProxy_shiftTime($from,-$offset.$offset_scale) if( $offset );
$to = logProxy_shiftTime($to,-$offset.$offset_scale) if( $offset );
# extend query range
$from = logProxy_shiftTime($from,-$extend.$extend_scale) if( $extend );
$to = logProxy_shiftTime($to,$extend.$extend_scale) if( $extend );
$internal_data = "";
my $cmd = "get $log_dev $infile INT $from $to $column_specs";
Log3 $hash->{NAME}, 4, "$hash->{NAME}: calling $cmd";
FW_fC($cmd, 1);
# shift data and specials back
logProxy_shiftData($internal_data,$offset.$offset_scale) if( $offset );
$main::data{"currdate1"} = logProxy_shiftTime($main::data{"currdate1"},$offset.$offset_scale) if( $offset );
# clip extended query range to plot range
if( $clip || defined($predict) ) {
$$internal_data = logProxy_clipData($internal_data,$fromsec,$tosec,$interpolate,$predict);
}
if( $$internal_data ) {
$ret .= $$internal_data;
$data{"min$j"} = $main::data{"min1"};
$data{"max$j"} = $main::data{"max1"};
$data{"avg$j"} = $main::data{"avg1"};
$data{"sum$j"} = $main::data{"sum1"};
$data{"cnt$j"} = $main::data{"cnt1"};
$data{"currval$j"} = $main::data{"currval1"};
$data{"currdate$j"} = $main::data{"currdate1"};
$data{"mindate$j"} = $main::data{"mindate1"};
$data{"maxdate$j"} = $main::data{"maxdate1"};
} else {
$ret .= "#$column_specs\n";
}
next;
} elsif( $fld[0] eq "ConstX" && $fld[1] ) {
my ($t,$y,$y2) = eval $fld[1];
if( $@ ) {
Log3 $hash->{NAME}, 1, "$hash->{NAME}: $fld[1]: $@";
$ret .= "#$a[$i]\n";
next;
}
if( !$t || !defined($y) || $y eq "undef" ) {
$ret .= "#$a[$i]\n";
next;
}
$t =~ s/ /_/;
my $from = $t;
my $to = $t;
$y2 = $y if( !defined($y2) );
$data{"min$j"} = $y > $y2 ? $y2 : $y;
$data{"max$j"} = $y > $y2 ? $y : $y2;
$data{"avg$j"} = ($y+$y2)/2;
$data{"cnt$j"} = $y != $y2 ? 2 : 1;
$data{"curdval$j"} = $y2;
$data{"curddate$j"} = $to;
$data{"maxdate$j"} = $to;
$data{"mindate$j"} = $to;
$ret .= "$from $y\n";
$ret .= "$to $y2\n";
$ret .= "#$a[$i]\n";
next;
} elsif( $fld[0] eq "ConstY" && defined($fld[1]) ) {
my ($y,$f,$t) = eval $fld[1];
if( $@ ) {
Log3 $hash->{NAME}, 1, "$hash->{NAME}: $fld[1]: $@";
$ret .= "#$a[$i]\n";
next;
}
if( !defined($y) || $y eq "undef" ) {
$ret .= "#$a[$i]\n";
next;
}
$f =~ s/ /_/ if( $f );
$t =~ s/ /_/ if( $t );
my $from = $from;
$from = $f if( $f );
my $to = $to;
$to = $t if( $t );
$data{"min$j"} = $y;
$data{"max$j"} = $y;
$data{"avg$j"} = $y;
$data{"cnt$j"} = 2;
$data{"currval$j"} = $y;
$data{"currdate$j"} = $to;
$data{"maxdate$j"} = $to;
$data{"mindate$j"} = $to;
$ret .= "$from $y\n";
$ret .= "$to $y\n";
$ret .= "#$a[$i]\n";
next;
} elsif( $fld[0] eq "Func" && $fld[1] ) {
#my $fromsec = SVG_time_to_sec($from);
#my $tosec = SVG_time_to_sec($to);
my ($r,$min,$max,$last) = eval $fld[1];
if( $@ ) {
Log3 $hash->{NAME}, 1, "$hash->{NAME}: $fld[1]: $@";
next;
}
$data{"min$j"} = $min;
$data{"max$j"} = $max;
$data{"currval$j"} = $last;
$ret .= $r;
$ret .= "#$a[$i]\n";
next;
}
}
for(my $i = 0; $i < int(@a); $i++) {
my $j = $i+1;
$main::data{"min$j"} = $data{"min$j"};
$main::data{"max$j"} = $data{"max$j"};
$main::data{"avg$j"} = $data{"avg$j"};
$main::data{"sum$j"} = $data{"sum$j"};
$main::data{"cnt$j"} = $data{"cnt$j"};
$main::data{"currval$j"} = $data{"currval$j"};
$main::data{"currdate$j"} = $data{"currdate$j"};
$main::data{"mindate$j"} = $data{"mindate$j"};
$main::data{"maxdate$j"} = $data{"maxdate$j"};
}
$internal_data = \$ret;
#Log 3, Dumper $internal_data;
return undef;
}
1;
=pod
=begin html
<a name="logProxy"></a>
<h3>logProxy</h3>
<ul>
Allows the manipulation of data to be plotted in an SVG device:
<ul>
<li>addition of horizontal lines at fixed values</li>
<li>addition of horizontal lines at dynamic values eg: min, max or average values of another plot </li>
<li>addition of vertical lines at fixed or dynamic times between two fixed or dynamic y values</li>
<li>addition of calculated data like week profiles of HeatingControll devices or heating thermostats</li>
<li>merge plot data from different sources. eg. different FileLog devices</li>
<li>horizontaly shifting a (merged) plot to align average or statistic data to the correct day,week and month</li>
</ul>
<br>
<a name="logProxy_Define"></a>
<b>Define</b>
<ul>
<code>define &lt;name&gt; logProxy</code><br>
<br>
Only one logProxy device can be defined and is needed.<br><br>
Example:
<ul>
<code>define myProxy logProxy</code><br>
</ul>
</ul><br>
<a name="logProxy_Set"></a>
<b>Set</b>
<ul>
</ul><br>
<a name="logProxy_Get"></a>
<b>Get</b>
<ul>
see <a href="#FileLogget">FileLog</a> and <a href="#DbLogget">DbLog</a>
</ul><br>
<a name="logProxy_Attr"></a>
<b>Attributes</b>
<ul>
</ul><br>
<br>
<b>#logProxy &lt;column_spec&gt;</b><br>
where &lt;column_spec&gt; can be one or more of the following:
<ul>
<li>FileLog:&lt;log device&gt;[,&lt;options&gt;]:&lt;column_spec&gt;<br></li><br>
<li>DbLog:&lt;log device&gt;[,&lt;options&gt;]:&lt;column_spec&gt;<br></li><br>
<li>ConstX:&lt;time&gt;,&lt;y&gt;[,&lt;y2&gt;]<br>
Will draw a vertical line (or point) at &lttime&gt; between &lt;y&gt; to &lt;y2&gt;.<br>
Everything after the : is evaluated as a perl expression that hast to return one time string and one or two y values.<br>
Examples:
<ul>
<code>#logProxy ConstX:$data{currdate1},$data{currval1}</code><br>
<code>#logProxy ConstX:$data{mindate1},$data{min1},$data{avg1}</code><br>
<code>#logProxy ConstX:$data{maxdate1},$data{max1},$data{avg1}</code><br>
<code>#logProxy ConstX:logProxy_shiftTime($from,60*60*2),$data{min1},$data{max1}</code><br>
</ul></li><br>
<li>ConstY:&ltvalue&gt;[,&lt;from&gt;[,&lt;to&gt;]]<br>
Will draw a horizontal line at &ltvalue&gt;, optional only between the from and to times.<br>
Everything after the : is evaluated as a perl expression that hast to return one value and optionaly one or two time strings.<br>
Examples:
<ul>
<code>#logProxy ConstY:0</code><br>
<code>#logProxy ConstY:1234+15+myFunc(123)</code><br>
<code>#logProxy ConstY:$data{avg1}</code><br>
<code>#logProxy ConstY:$data{avg2},$from,$to</code><br>
<code>#logProxy ConstY:$data{avg2},logProxy_shiftTime($from,60*60*12),logProxy_shiftTime($from,-60*60*12)</code>
</ul></li><br>
<li>Func:&ltperl expression&gt;<br>
Specifies a perl expression that returns the data to be plotted and its min, max and last value. It can not contain
space or : characters. The data has to be
one string of newline separated entries of the form: <code>yyyy-mm-dd_hh:mm:ss value</code><br>Example:
<ul>
<code>#logProxy Func:logProxy_WeekProfile2Plot("HCB",$from,$to)</code><br>
<code>#logProxy Func:logProxy_WeekProfile2Plot("myHeatingControll",$from,$to,"(\\d)*\$")</code><br>
<code>#logProxy Func:logProxy_Func2Plot($from,$to,'{logProxy_hms2dec(sunrise_abs_dat($sec))}')</code><br>
<code>#logProxy Func:logProxy_Func2Plot($from,$to,'{logProxy_hms2dec(sunset_abs_dat($sec))}')</code><br>
</ul><br>
Notes:<ul>
<li>logProxy_WeekProfile2Plot is a sample implementation of a function that will plot the week profile
of a Heating_Control, WeekdyTimer, HomeMatic or MAX Thermostat device can be found in the 98_logProxy.pm module file.</li>
<li>logProxy_Func2Plot($from,$to,$func) is a sample implementation of a function that will evaluate the given
function (3rd parameter) for a zoom factor dependent number of times. the current time is given in $sec.
the step width can be given in an optional 4th parameter. either as a number or as an hash with the keys from
the following list: hour,qday,day,week,month,year and the values representing the step with for the zoom level.</li>
<li>The perl expressions have access to $from and $to for the begining and end of the plot range and also to the
SVG specials min, max, avg, cnt, sum, currval (last value) and currdate (last date) values of the individual curves
already plotted are available as $data{&lt;special-n&gt;}.<br>
<li>logProxy_Range2Zoom($seconds) can be used to get the approximate zoom step for a plot range of $seconds.</li>
<li>SVG_time_to_sec($timestamp) can be used to convert the timestamp strings to epoch times for calculation.</li>
</ul>
</li><br>
options is a comma separated list of zero or more of:<br>
<ul>
<li>clip<br>
clip the plot data to the plot window</li>
<li>extend=&lt;value&gt;<br>
extend the query range to the log device by &lt;value&gt; seconds (or &lt;value&gt; months if &lt;value&gt; ends in m).
also activates cliping.</li>
<li>interpolate<br>
perform a linear interpolation to the values in the extended range to get the values at the plot boundary. only usefull
if plotfunction is lines.</li>
<li>offset=&lt;value&gt;<br>
shift plot by &lt;value&gt; seconds (or &lt;value&gt; months if &lt;value&gt; ends in m).
allows alignment of values calculated by average or statsitics module to the correct day, week or month. </li>
<li>predict[=&lt;value&gt;]<br>
no value -> extend the last plot value to now.<br>
value -> extend the last plot value by &lt;value&gt; but maximal to now.<br></li>
</ul>
</li><br>
</ul>
Please see also the column_spec paragraphs of FileLog, DbLog and SVG.<br>
<br>
To use any of the logProxy features with an existing plot the associated SVG file hast to be changed to use the logProxy
device and the .gplot file has to be changed in the following way:<br>
All existing #FileLog and #Dblog lines have to be changed to #logProxy lines and<br>the column_spec of these line has to
be prepended by <code>FileLog:&lt;log device&gt;:</code> or <code>DbLog:&lt;log device&gt;:</code> respectively.<br>
Examples:
<ul>
<code>#DbLog &lt;myDevice&gt;:&lt;myReading&gt;</code></br>
<code>#FileLog 4:&lt;SPEC1&gt;:power\x3a::</code><br>
<code>#FileLog 4:&lt;SPEC1&gt;:consumption\x3a::</code><br><br>
will become:<br><br>
<code>#logProxy DbLog:&lt;myDb&gt;:&lt;myDevice&gt;:&lt;myReading&gt;</code></br>
<code>#logProxy FileLog:FileLog_&lt;SPEC1&gt;:4:<SPEC1>.power\x3a::</code><br>
<code>#logProxy FileLog:FileLog_&lt;SPEC1&gt;:4:<SPEC1>.consumption\x3a::</code><br>
</ul>
</ul>
=end html
=cut

View File

@ -255,6 +255,7 @@ FHEM/98_dewpoint.pm Joachim http://forum.fhem.de Automatis
FHEM/98_dummy.pm rudolfkoenig http://forum.fhem.de Automatisierung
FHEM/98_fheminfo.pm mfr69bs http://forum.fhem.de Sonstiges
FHEM/98_HourCounter.pm john http://forum.fhem.de MAX
FHEM/98_logProxy.pm justme1968 http://forum.fhem.de Frontends
FHEM/98_notice.pm mfr69bs http://forum.fhem.de Sonstiges
FHEM/98_pilight.pm andreas-fey http://forum.fhem.de Unterstuetzende Dienste
FHEM/98_rain.pm baumrasen http://forum.fhem.de Sonstiges