| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808 |
- # $Id: 33_readingsHistory.pm 16299 2018-03-01 08:06:55Z justme1968 $
- ##############################################################################
- #
- # 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 POSIX qw(strftime);
- use vars qw(%defs);
- use vars qw(%attr);
- sub Log($$);
- sub Log3($$$);
- use vars qw($FW_ME);
- use vars qw($FW_wname);
- use vars qw($FW_subdir);
- use vars qw(%FW_hiddenroom);
- use vars qw(%FW_visibleDeviceHash);
- use vars qw(%FW_webArgs); # all arguments specified in the GET
- my $readingsHistory_hasJSON = 0;
- my $readingsHistory_hasDataDumper = 1;
- sub readingsHistory_Initialize($)
- {
- my ($hash) = @_;
- $hash->{DefFn} = "readingsHistory_Define";
- $hash->{NotifyFn} = "readingsHistory_Notify";
- $hash->{NotifyOrderPrefix}= "51-";
- $hash->{UndefFn} = "readingsHistory_Undefine";
- $hash->{SetFn} = "readingsHistory_Set";
- $hash->{GetFn} = "readingsHistory_Get";
- $hash->{AttrFn} = "readingsHistory_Attr";
- $hash->{AttrList} = "disable:1,2,3 mapping style timestampFormat valueFormat noheading:1 nohtml:1 nolinks:1 notime:1 nostate:1 alwaysTrigger:1 rows";
- $hash->{FW_detailFn} = "readingsHistory_detailFn";
- $hash->{FW_summaryFn} = "readingsHistory_detailFn";
- $data{FWEXT}{"readingsHistory"}{SCRIPT} = "fhemweb_readingsHistory.js";
- $hash->{FW_atPageEnd} = 1;
- eval "use Data::Dumper";
- $readingsHistory_hasDataDumper = 0 if($@);
- }
- sub
- readingsHistory_updateDevices($)
- {
- my ($hash) = @_;
- my %list;
- my @devices;
- my $def = $hash->{DEF};
- $def = "" if( !$def );
- my @params = split(" ", $def);
- while (@params) {
- my $param = shift(@params);
- my @device = split(":", $param);
- if($device[0] =~ m/(.*)=(.*)/) {
- my ($lattr,$re) = ($1, $2);
- foreach my $d (sort keys %defs) {
- next if( IsIgnored($d) );
- next if( !defined($defs{$d}{$lattr}) );
- next if( $defs{$d}{$lattr} !~ m/^$re$/);
- $list{$d} = 1;
- push @devices, [$d,$device[1]];
- }
- } elsif($device[0] =~ m/(.*)&(.*)/) {
- my ($lattr,$re) = ($1, $2);
- foreach my $d (sort keys %attr) {
- next if( IsIgnored($d) );
- next if( !defined($attr{$d}{$lattr}) );
- next if( $attr{$d}{$lattr} !~ m/^$re$/);
- $list{$d} = 1;
- push @devices, [$d,$device[1]];
- }
- } elsif( defined($defs{$device[0]}) ) {
- $list{$device[0]} = 1;
- push @devices, [@device];
- } else {
- foreach my $d (sort keys %defs) {
- next if( IsIgnored($d) );
- eval { $d =~ m/^$device[0]$/ };
- if( $@ ) {
- Log3 $hash->{NAME}, 3, $hash->{NAME} .": ". $device[0] .": ". $@;
- push @devices, ["<<ERROR>>"];
- last;
- }
- next if( $d !~ m/^$device[0]$/);
- $list{$d} = 1;
- push @devices, [$d,$device[1]];
- }
- }
- }
- $hash->{CONTENT} = \%list;
- $hash->{DEVICES} = \@devices;
- delete( $hash->{NOTIFYDEV} );
- if( scalar keys %list == 0 ) {
- $hash->{NOTIFYDEV} = "global";
- }
- $hash->{fhem}->{last_update} = gettimeofday();
- }
- sub
- readingsHistory_Define($$)
- {
- my ($hash, $def) = @_;
- my @args = split("[ \t]+", $def);
- return "Usage: define <name> readingsHistory [<device>[:<readings>]]+" if(@args < 2);
- my $name = shift(@args);
- my $type = shift(@args);
- $hash->{HAS_DataDumper} = $readingsHistory_hasDataDumper;
- $hash->{fhem}{history} = [] if( !defined($hash->{fhem}{history}) );
- readingsHistory_updateDevices($hash);
- $hash->{STATE} = 'Initialized';
- return undef;
- }
- sub
- readingsHistory_lookup($$$$$$$$)
- {
- my($mapping,$name,$alias,$reading,$value,$room,$group,$default) = @_;
- if( $mapping ) {
- if( !ref($mapping) && $mapping =~ m/^{.*}$/) {
- my $DEVICE = $name;
- my $READING = $reading;
- my $VALUE = $value;
- my $m = eval $mapping;
- if( $@ ) {
- Log 2, $@ if( $@ );
- } else {
- $mapping = $m;
- }
- }
- if( ref($mapping) eq 'HASH' ) {
- $default = $mapping->{$name} if( defined($mapping->{$name}) );
- $default = $mapping->{$reading} if( defined($mapping->{$reading}) );
- $default = $mapping->{$name.".".$reading} if( defined($mapping->{$name.".".$reading}) );
- $default = $mapping->{$reading.".".$value} if( defined($mapping->{$reading.".".$value}) );
- } else {
- $default = $mapping;
- }
- if( !ref($default) && $default =~ m/^{.*}$/) {
- my $DEVICE = $name;
- my $READING = $reading;
- my $VALUE = $value;
- $default = eval $default;
- $default = "" if( $@ );
- Log 2, $@ if( $@ );
- }
- return $default if( !defined($default) );
- $default =~ s/\%ALIAS/$alias/g;
- $default =~ s/\%DEVICE/$name/g;
- $default =~ s/\%READING/$reading/g;
- $default =~ s/\%VALUE/$value/g;
- $default =~ s/\%ROOM/$room/g;
- $default =~ s/\%GROUP/$group/g;
- $default =~ s/\$ALIAS/$alias/g;
- $default =~ s/\$DEVICE/$name/g;
- $default =~ s/\$READING/$reading/g;
- $default =~ s/\$VALUE/$value/g;
- $default =~ s/\$ROOM/$room/g;
- $default =~ s/\$GROUP/$group/g;
- }
- return $default;
- }
- sub
- readingsHistory_lookup2($$$$)
- {
- my($lookup,$name,$reading,$value) = @_;
- return $lookup if( !$lookup );
- if( !ref($lookup) && $lookup =~ m/^{.*}$/) {
- my $DEVICE = $name;
- my $READING = $reading;
- my $VALUE = $value;
- my $l = eval $lookup;
- if( $@ ) {
- Log 2, $@ if( $@ );
- } else {
- $lookup = $l;
- }
- }
- if( ref($lookup) eq 'HASH' ) {
- my $vf = "";
- $vf = $lookup->{$reading} if( defined($reading) && exists($lookup->{$reading}) );
- $vf = $lookup->{$name.".".$reading} if( defined($reading) && exists($lookup->{$name.".".$reading}) );
- $vf = $lookup->{$reading.".".$value} if( defined($value) && exists($lookup->{$reading.".".$value}) );
- $lookup = $vf;
- }
- return $lookup if( !defined($lookup) );
- if( !ref($lookup) && $lookup =~ m/^{.*}$/) {
- my $DEVICE = $name;
- my $READING = $reading;
- my $VALUE = $value;
- $lookup = eval $lookup;
- $lookup = "" if( $@ );
- Log 2, $@ if( $@ );
- }
- return $lookup if( !defined($lookup) );
- $lookup =~ s/\%DEVICE/$name/g;
- $lookup =~ s/\%READING/$reading/g;
- $lookup =~ s/\%VALUE/$value/g;
- $lookup =~ s/\$DEVICE/$name/g;
- $lookup =~ s/\$READING/$reading/g;
- $lookup =~ s/\$VALUE/$value/g;
- return $lookup;
- }
- sub readingsHistory_Undefine($$)
- {
- my ($hash,$arg) = @_;
- return undef;
- }
- sub
- readingsHistory_2html($)
- {
- my($hash) = @_;
- $hash = $defs{$hash} if( ref($hash) ne 'HASH' );
- return undef if( !$hash );
- if( $hash->{DEF} && $hash->{DEF} =~ m/=/ ) {
- if( !$hash->{fhem}->{last_update}
- || gettimeofday() - $hash->{fhem}->{last_update} > 600 ) {
- readingsHistory_updateDevices($hash);
- }
- }
- my $d = $hash->{NAME};
- my $show_heading = !AttrVal( $d, "noheading", "0" );
- my $show_links = !AttrVal( $d, "nolinks", "0" );
- $show_links = 0 if($FW_hiddenroom{detail});
- my $disable = AttrVal($d,"disable", 0);
- if( AttrVal($d,"disable", 0) > 2 ) {
- return undef;
- } elsif( AttrVal($d,"disable", 0) > 1 ) {
- my $ret;
- $ret .= "<table>";
- my $txt = AttrVal($d, "alias", $d);
- $txt = "<a href=\"$FW_ME$FW_subdir?detail=$d\">$txt</a>" if( $show_links );
- $ret .= "<tr><td><div class=\"devType\">$txt</a></div></td></tr>" if( $show_heading );
- $ret .= "<tr><td><table class=\"block wide\">";
- #$ret .= "<div class=\"devType\"><a style=\"color:#ff8888\" href=\"$FW_ME$FW_subdir?detail=$d\">readingsHistory $txt is disabled.</a></div>";
- $ret .= "<td><div style=\"color:#ff8888;text-align:center\">disabled</div></td>";
- $ret .= "</table></td></tr>";
- $ret .= "</table>";
- return $ret;
- }
- my $show_time = !AttrVal( $d, "notime", "0" );
- my $style = AttrVal( $d, "style", "" );
- my $devices = $hash->{DEVICES};
- my $row = 1;
- my $ret;
- $ret .= "<table>";
- my $txt = AttrVal($d, "alias", $d);
- $txt = "<a href=\"$FW_ME$FW_subdir?detail=$d\">$txt</a>" if( $show_links );
- $ret .= "<tr><td><div class=\"devType\">$txt</a></div></td></tr>" if( $show_heading );
- $ret .= "<tr><td><table $style class=\"block wide\">";
- $ret .= "<tr><td colspan=\"99\"><div style=\"color:#ff8888;text-align:center\">updates disabled</div></tr>" if( $disable > 0 );
- my $rows = AttrVal($d,"rows", 5 );
- $rows = 1 if( $rows < 1 );
- my $lines = "";
- for (my $i = 0; $i < $rows; $i++) {
- my $line = $hash->{fhem}{history}[$i];
- if( ref($line) eq 'ARRAY' ) {
- $lines .= $line->[3] if( $line );
- } else {
- $lines .= $line if( $line );
- }
- $lines .= "<br>";
- }
- $ret .= sprintf("<tr class=\"%s\">", ($row&1)?"odd":"even");
- $row++;
- $ret .= "<td><div id=\"$d-history\" rows=\"$rows\">$lines</div></td>";
- $ret .= sprintf("<tr class=\"%s\">", ($row&1)?"odd":"even");
- $ret .= "<td colspan=\"99\"><div style=\"color:#ff8888;text-align:center\">updates disabled</div></td></tr>" if( $disable > 0 );
- $ret .= "</table></td></tr>";
- $ret .= "</table>";
- return $ret;
- }
- sub
- readingsHistory_detailFn()
- {
- my ($FW_wname, $d, $room, $pageHash) = @_; # pageHash is set for summaryFn.
- my $hash = $defs{$d};
- $hash->{mayBeVisible} = 1;
- return readingsHistory_2html($d);
- }
- sub
- readingsHistory_makeTimestamp($$) {
- my ($t, $timestampFormat) = @_;
- my @lt = localtime($t);
- return strftime( $timestampFormat, @lt ) if( $timestampFormat );
- return sprintf("%02d:%02d:%02d", $lt[2], $lt[1], $lt[0] );
- }
- sub
- readingsHistory_Notify($$)
- {
- my ($hash,$dev) = @_;
- my $name = $hash->{NAME};
- if( grep(m/^INITIALIZED$/, @{$dev->{CHANGED}}) ) {
- readingsHistory_updateDevices($hash);
- readingsHistory_Load($hash);
- return undef;
- } elsif( grep(m/^REREADCFG$/, @{$dev->{CHANGED}}) ) {
- readingsHistory_updateDevices($hash);
- readingsHistory_Load($hash);
- return undef;
- } elsif( grep(m/^SAVE$/, @{$dev->{CHANGED}}) ) {
- readingsHistory_Save();
- }
- return if( AttrVal($name,"disable", 0) > 0 );
- return if($dev->{TYPE} eq $hash->{TYPE});
- #return if($dev->{NAME} eq $name);
- my $devices = $hash->{DEVICES};
- my $max = int(@{$dev->{CHANGED}});
- for (my $i = 0; $i < $max; $i++) {
- my $s = $dev->{CHANGED}[$i];
- $s = "" if(!defined($s));
- if( $dev->{NAME} eq "global" && $s =~ m/^RENAMED ([^ ]*) ([^ ]*)$/) {
- my ($old, $new) = ($1, $2);
- if( defined($hash->{CONTENT}{$old}) ) {
- $hash->{DEF} =~ s/(^|\s+)$old((:\S+)?\s*)/$1$new$2/g;
- }
- readingsHistory_updateDevices($hash);
- } elsif( $dev->{NAME} eq "global" && $s =~ m/^DELETED ([^ ]*)$/) {
- my ($name) = ($1);
- if( defined($hash->{CONTENT}{$name}) ) {
- $hash->{DEF} =~ s/(^|\s+)$name((:\S+)?\s*)/ /g;
- $hash->{DEF} =~ s/^ //;
- $hash->{DEF} =~ s/ $//;
- }
- readingsHistory_updateDevices($hash);
- } elsif( $dev->{NAME} eq "global" && $s =~ m/^DEFINED ([^ ]*)$/) {
- readingsHistory_updateDevices($hash);
- } else {
- next if(AttrVal($name,"disable", undef));
- next if (!$hash->{CONTENT}->{$dev->{NAME}});
- if( $hash->{alwaysTrigger} ) {
- $hash->{mayBeVisible} = 1;
- } elsif( !defined($hash->{mayBeVisible}) ) {
- Log3 $name, 5, "$name: not on any display, don't trigger";
- } else {
- if( defined($FW_visibleDeviceHash{$name}) ) {
- } else {
- Log3 $name, 5, "$name: no longer visible, don't trigger";
- delete( $hash->{mayBeVisible} );
- }
- }
- my ($reading,$value) = split(": ", $s, 2);
- $reading = "" if( !defined($reading) );
- next if( $reading =~ m/^\./);
- $value = "" if( !defined($value) );
- my $show_state = 1;
- if( $value eq "" ) {
- next if AttrVal( $name, "nostate", "0" );
- $reading = "state";
- $value = $s;
- }
- my $no_html = AttrVal( $name, "nohtml", "0" );
- my $spacing = " ";
- $spacing = " " if $no_html;
- my $mapping = AttrVal( $name, "mapping", "");
- $mapping = eval $mapping if( $mapping =~ m/^{.*}$/ );
- #$mapping = undef if( ref($mapping) ne 'HASH' );
- my $timestampFormat = AttrVal( $name, "timestampFormat", undef);
- my $value_format = AttrVal( $name, "valueFormat", "" );
- if( $value_format =~ m/^{.*}$/ ) {
- my $vf = eval $value_format;
- $value_format = $vf if( $vf );
- }
- foreach my $device (@{$devices}) {
- my $item = 0;
- my $h = $defs{@{$device}[0]};
- next if( !$h );
- next if( $dev->{NAME} ne $h->{NAME} );
- my $n = $h->{NAME};
- my $regex = @{$device}[1];
- my @list = (undef);
- @list = split(",",$regex) if( $regex );
- foreach my $regex (@list) {
- next if( $reading eq "state" && !$show_state && (!defined($regex) || $regex ne "state") );
- next if( defined($regex) && $reading !~ m/^$regex$/);
- my $msg = $s;
- my $value_format = readingsHistory_lookup2($value_format,$n,$reading,$value);
- next if( !defined($value_format) );
- if( $value_format =~ m/%/ ) {
- $msg = sprintf( $value_format, $value );
- } elsif( $value_format ) {
- $msg = $value_format;
- }
- my $t = time;
- my $tm = readingsHistory_makeTimestamp( $t, $timestampFormat );
- my $show_links = !AttrVal( $name, "nolinks", "0" );
- $show_links = 0 if($FW_hiddenroom{detail});
- $show_links = 0 if(!defined($FW_ME));
- $show_links = 0 if($no_html);
- my $a = AttrVal($n, "alias", $n);
- my $m = $a;
- if( $mapping ) {
- my $room = AttrVal($n, "room", "");
- my $group = AttrVal($n, "group", "");
- $m = readingsHistory_lookup($mapping,$n,$a,$reading,$value,$room,$group,$m);
- }
- if( $show_links ) {
- $msg = "$tm$spacing<a href=\"$FW_ME$FW_subdir?detail=$n\">$m</a> $msg";
- } else {
- $msg = "$tm$spacing$m $msg";
- }
- my $entry = [$t, $n, $s,$msg];
- #my $entry = [$t, $n, "$reading: $value", $msg];
- while( @{$hash->{fhem}{history}} >= AttrVal($name,"rows", 5 ) ) {
- pop @{$hash->{fhem}{history}};
- }
- unshift( @{$hash->{fhem}{history}}, $entry );
- DoTrigger( "$name", "history: <html>$msg</html>" ) if( $hash->{mayBeVisible} );
- }
- }
- }
- }
- return undef;
- }
- sub
- readingsHistory_StatefileName()
- {
- my $statefile = $attr{global}{statefile};
- $statefile = substr $statefile,0,rindex($statefile,'/')+1;
- #return $statefile ."readingsHistorys.save" if( $readingsHistory_hasJSON );
- return $statefile ."readingsHistorys.dd.save" if( $readingsHistory_hasDataDumper );
- }
- my $readingsHistory_LastSaveTime="";
- sub
- readingsHistory_Save()
- {
- my $time_now = TimeNow();
- return if( $time_now eq $readingsHistory_LastSaveTime);
- $readingsHistory_LastSaveTime = $time_now;
- return "No statefile specified" if(!$attr{global}{statefile});
- my $statefile = readingsHistory_StatefileName();
- my $hash;
- for my $d (keys %defs) {
- next if($defs{$d}{TYPE} ne "readingsHistory");
- next if( !defined($defs{$d}{fhem}{history}) );
- $hash->{$d} = $defs{$d}{fhem}{history};
- }
- if(open(FH, ">$statefile")) {
- my $t = localtime;
- print FH "#$t\n";
- if( $readingsHistory_hasJSON ) {
- print FH encode_json($hash) if( defined($hash) );
- } elsif( $readingsHistory_hasDataDumper ) {
- my $dumper = Data::Dumper->new([]);
- $dumper->Terse(1);
- $dumper->Values([$hash]);
- print FH $dumper->Dump;
- }
- close(FH);
- } else {
- my $msg = "readingsHistory_Save: Cannot open $statefile: $!";
- Log3 undef, 1, $msg;
- }
- return undef;
- }
- sub
- readingsHistory_Load($)
- {
- my ($hash) = @_;
- return "No statefile specified" if(!$attr{global}{statefile});
- my $statefile = readingsHistory_StatefileName();
- if(open(FH, "<$statefile")) {
- my $encoded;
- while (my $line = <FH>) {
- chomp $line;
- next if($line =~ m/^#.*$/);
- $encoded .= $line;
- }
- close(FH);
- return if( !defined($encoded) );
- my $decoded;
- if( $readingsHistory_hasJSON ) {
- $decoded = decode_json( $encoded );
- } elsif( $readingsHistory_hasDataDumper ) {
- $decoded = eval $encoded;
- }
- $hash->{fhem}{history} = $decoded->{$hash->{NAME}} if( defined($decoded->{$hash->{NAME}}) );
- } else {
- my $msg = "readingsHistory_Load: Cannot open $statefile: $!";
- Log3 undef, 1, $msg;
- }
- return undef;
- }
- sub
- readingsHistory_Set($@)
- {
- my ($hash, $name, $cmd, $param, @a) = @_;
- my $list = "add clear:noArg";
- my $no_html = AttrVal( $name, "nohtml", "0" );
- my $spacing = " ";
- $spacing = " " if $no_html;
- if( $cmd eq "clear" ) {
- $hash->{fhem}{history} = [];
- my $t = time;
- my $tm = readingsHistory_makeTimestamp( time, AttrVal( $name, "timestampFormat", undef) );
- my $msg = "$tm$spacing--clear--";
- my $entry = [$t,"", $cmd, $msg];
- unshift( @{$hash->{fhem}{history}}, $entry );
- DoTrigger( "$name", "clear: $msg" ) if( $hash->{mayBeVisible} );
- return undef;
- } elsif ( $cmd eq "add" ) {
- my $t = time;
- my $tm = readingsHistory_makeTimestamp( time, AttrVal( $name, "timestampFormat", undef) );
- my $msg = "$tm$spacing$param ". join( " ", @a );
- my $entry = [$t,"", "$param ". join( " ", @a ),$msg];
- while( @{$hash->{fhem}{history}} >= AttrVal($name,"rows", 5 ) ) {
- pop @{$hash->{fhem}{history}};
- }
- unshift( @{$hash->{fhem}{history}}, $entry );
- DoTrigger( "$name", "history: <html>$msg</html>" ) if( $hash->{mayBeVisible} );
- return undef;
- }
- return "Unknown argument $cmd, choose one of $list";
- }
- sub
- readingsHistory_Get($@)
- {
- my ($hash, @a) = @_;
- my $list = "history:noArg html:noArg";
- my $name = $a[0];
- return "$name: get needs at least one parameter" if(@a < 2);
- my $cmd= $a[1];
- my $ret = "";
- if( $cmd eq "html" ) {
- return readingsHistory_2html($hash);
- } elsif( $cmd eq "history" ) {
- my $rows = AttrVal($name,"rows", 5 );
- $rows = 1 if( $rows < 1 );
- my $timestampFormat = AttrVal( $name, "timestampFormat", undef);
- for (my $i = 0; $i < $rows; $i++) {
- my $line = $hash->{fhem}{history}[$i];
- if( ref($line) eq 'ARRAY' ) {
- my $tm = readingsHistory_makeTimestamp( $line->[0], $timestampFormat );
- $ret .= "$tm\t$line->[1]\t$line->[2]\t$line->[3]" if( $line );
- } else {
- $ret .= $line if( $line );
- }
- $ret .= "\n";
- }
- return $ret;
- }
- return "Unknown argument $cmd, choose one of $list";
- }
- sub
- readingsHistory_Attr($$$)
- {
- my ($cmd, $name, $attrName, $attrVal) = @_;
- my $orig = $attrVal;
- if( $attrName eq "alwaysTrigger" ) {
- my $hash = $defs{$name};
- $attrVal = 1 if($attrVal);
- if( $cmd eq "set" ) {
- $hash->{alwaysTrigger} = $attrVal;
- delete( $hash->{helper}->{myDisplay} ) if( $hash->{alwaysTrigger} );
- } else {
- delete $hash->{alwaysTrigger};
- }
- }
- if( $cmd eq "set" ) {
- if( $orig ne $attrVal ) {
- $attr{$name}{$attrName} = $attrVal;
- return $attrName ." set to ". $attrVal;
- }
- }
- return;
- }
- 1;
- =pod
- =item helper
- =item summary display a history of readings from on or more devices
- =item summary_DE Anzeige der Historie von Readings eines oder mehrerer Geräte
- =begin html
- <a name="readingsHistory"></a>
- <h3>readingsHistory</h3>
- <ul>
- Displays a history of readings from on or more devices.
- <br><br>
- <a name="readingsHistory_Define"></a>
- <b>Define</b>
- <ul>
- <code>define <name> readingsHistory [<device>[:regex] [<device-2>[:regex-2] ... [<device-n>[:regex-n]]]]</code><br>
- <br>
- Notes:
- <ul>
- <li><device> can be of the form INTERNAL=VALUE where INTERNAL is the name of an internal value and VALUE is a regex.</li>
- <li>If regex is a comma separatet list it will be used as an enumeration of allowed readings.</li>
- <li>if no device/reading argument is given only lines with 'set <device> add ...' are displayed.</li>
- </ul><br>
- Examples:
- <ul>
- </ul>
- </ul><br>
- <a name="readingsHistory_Set"></a>
- <b>Set</b>
- <ul>
- <li>add ...<br>
- directly add text as new line to history.</li>
- <li>clear<br>
- clear the history.</li>
- </ul><br>
- <a name="readingsHistory_Get"></a>
- <b>Get</b>
- <ul>
- <li>history<br>
- list history</li>
- </ul><br>
- <a name="readingsHistory_Attr"></a>
- <b>Attributes</b>
- <ul>
- <li>alwaysTrigger<br>
- 1 -> alwaysTrigger update events. even if not visible.</li>
- <li>disable<br>
- 1 -> disable notify processing and longpoll updates. Notice: this also disables rename and delete handling.<br>
- 2 -> also disable html table creation<br>
- 3 -> also disable html creation completely</li>
- <li>noheading<br>
- If set to 1 the readings table will have no heading.</li>
- <li>nolinks<br>
- Disables the html links from the heading and the reading names.</li>
- <li>notime<br>
- If set to 1 the reading timestamp is not displayed.</li>
- <li>mapping<br>
- Can be a simple string or a perl expression enclosed in {} that returns a hash that maps device names
- to the displayed name. The keys can be either the name of the reading or <device>.<reading>.
- %DEVICE, %ALIAS, %ROOM and %GROUP are replaced by the device name, device alias, room attribute and
- group attribute respectively. You can also prefix these keywords with $ instead of %.
- </li>
- <li>style<br>
- Specify an HTML style for the readings table, e.g.:<br>
- <code>attr history style style="font-size:20px"</code></li>
- <li>timestampFormat<br>
- POSIX strftime compatible string for used as the timestamp for each line.
- </li>
- <li>valueFormat<br>
- Specify an sprintf style format string used to display the reading values. If the format string is undef
- this reading will be skipped. Can be given as a string, a perl expression returning a hash or a perl
- expression returning a string, e.g.:<br>
- <code>attr history valueFormat %.1f °C</code><br>
- <code>attr history valueFormat { temperature => "%.1f °C", humidity => "%.1f %" }</code><br>
- <code>attr history valueFormat { ($READING eq 'temperature')?"%.1f °C":undef }</code></li>
- <li>rows<br>
- Number of history rows to show.</li>
- </ul>
- </ul>
- =end html
- =cut
|