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:
parent
35662776f5
commit
49203c8074
@ -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
968
fhem/FHEM/98_logProxy.pm
Normal 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_<SPEC1>:4:<SPEC1>.power';
|
||||
push @example, 'FileLog:FileLog_<SPEC1>:4:<SPEC1>.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 <name> 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 <column_spec></b><br>
|
||||
where <column_spec> can be one or more of the following:
|
||||
<ul>
|
||||
<li>FileLog:<log device>[,<options>]:<column_spec><br></li><br>
|
||||
<li>DbLog:<log device>[,<options>]:<column_spec><br></li><br>
|
||||
<li>ConstX:<time>,<y>[,<y2>]<br>
|
||||
Will draw a vertical line (or point) at <time> between <y> to <y2>.<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:<value>[,<from>[,<to>]]<br>
|
||||
Will draw a horizontal line at <value>, 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:<perl expression><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{<special-n>}.<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=<value><br>
|
||||
extend the query range to the log device by <value> seconds (or <value> months if <value> 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=<value><br>
|
||||
shift plot by <value> seconds (or <value> months if <value> ends in m).
|
||||
allows alignment of values calculated by average or statsitics module to the correct day, week or month. </li>
|
||||
<li>predict[=<value>]<br>
|
||||
no value -> extend the last plot value to now.<br>
|
||||
value -> extend the last plot value by <value> 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:<log device>:</code> or <code>DbLog:<log device>:</code> respectively.<br>
|
||||
Examples:
|
||||
<ul>
|
||||
<code>#DbLog <myDevice>:<myReading></code></br>
|
||||
<code>#FileLog 4:<SPEC1>:power\x3a::</code><br>
|
||||
<code>#FileLog 4:<SPEC1>:consumption\x3a::</code><br><br>
|
||||
will become:<br><br>
|
||||
<code>#logProxy DbLog:<myDb>:<myDevice>:<myReading></code></br>
|
||||
<code>#logProxy FileLog:FileLog_<SPEC1>:4:<SPEC1>.power\x3a::</code><br>
|
||||
<code>#logProxy FileLog:FileLog_<SPEC1>:4:<SPEC1>.consumption\x3a::</code><br>
|
||||
</ul>
|
||||
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
=cut
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user