diff --git a/fhem/CHANGED b/fhem/CHANGED index 7ed8c09e7..e7f0bf9bb 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,7 @@ # Add changes at the top of the list. Keep it in ASCII - SVN + - feature: new module 33_readingsGroup to display a collection of readings from + on or more devices. this will replace weblink readings. (by justme1968) - feature: setreading command added - change: DbLog: by using DbLog a new Attribute DbLogExclude will be propagated to all Devices. DbLogExclue will work as regexp to exclude @@ -43,7 +45,7 @@ (betateilchen) - feature: use xhr for colorupdates by colorpicker and rgb presets - feature: new module 34_SWAP for generic SWAP protocoll support using - a panStick and 34_panStamp as IODevice(by justme1968) + a panStick and 34_panStamp as IODevice (by justme1968) - feature: new module 34_panStamp to use a panStick as a RF modem for panStamps using the SWAP protocoll (by justme1968) - feature: EGPM2LAN module added for Gembird (R) Energenie LAN Support diff --git a/fhem/FHEM/33_readingsGroup.pm b/fhem/FHEM/33_readingsGroup.pm new file mode 100644 index 000000000..8e9ad4b7c --- /dev/null +++ b/fhem/FHEM/33_readingsGroup.pm @@ -0,0 +1,437 @@ + +# $Id: 31_readingsGroup.pm 3641 2013-08-09 12:51:30Z justme1968 $ + +package main; + +use strict; +use warnings; + +use vars qw(%FW_webArgs); # all arguments specified in the GET + +sub readingsGroup_Initialize($) +{ + my ($hash) = @_; + + $hash->{DefFn} = "readingsGroup_Define"; + $hash->{NotifyFn} = "readingsGroup_Notify"; + $hash->{UndefFn} = "readingsGroup_Undefine"; + #$hash->{SetFn} = "readingsGroup_Set"; + $hash->{GetFn} = "readingsGroup_Get"; + $hash->{AttrList} = "mapping separator style nameStyle valueStyle timestampStyle noheading:1 notime:1 nostate:1"; + + $hash->{FW_detailFn} = "readingsGroup_detailFn"; + $hash->{FW_summaryFn} = "readingsGroup_detailFn"; + + $hash->{FW_atPageEnd} = 1; +} + +sub +readingsGroup_updateDevices($) +{ + my ($hash) = @_; + + my %list; + my @devices; + + my @params = split(" ", $hash->{DEF}); + while (@params) { + my $param = shift(@params); + + # for backwards compatibility with weblink readings + if( $param eq '*noheading' ) { + $attr{$hash->{NAME}}{noheading} = 1; + $hash->{DEF} =~ s/(\s*)$param((:\S+)?\s*)/ /g; + $hash->{DEF} =~ s/^ //; + $hash->{DEF} =~ s/ $//; + } elsif( $param eq '*notime' ) { + $attr{$hash->{NAME}}{notime} = 1; + $hash->{DEF} =~ s/(\s*)$param((:\S+)?\s*)/ /g; + $hash->{DEF} =~ s/^ //; + $hash->{DEF} =~ s/ $//; + } elsif( $param eq '*nostate' ) { + $attr{$hash->{NAME}}{nostate} = 1; + $hash->{DEF} =~ s/(\s*)$param((:\S+)?\s*)/ /g; + $hash->{DEF} =~ s/^ //; + $hash->{DEF} =~ s/ $//; + } elsif( $param =~ m/^{/) { + $attr{$hash->{NAME}}{mapping} = $param ." ". join( " ", @params ); + $hash->{DEF} =~ s/\s*[{].*$//g; + last; + } else { + my @device = split(":", $param); + + if( defined($defs{$device[0]}) ) { + $list{$device[0]} = 1; + push @devices, [@device]; + } else { + foreach my $d (sort keys %defs) { + next if( IsIgnored($d) ); + next if( $d !~ m/^$device[0]$/); + $list{$d} = 1; + push @devices, [$d,$device[1]]; + } + } + } + } + + $hash->{CONTENT} = \%list; + $hash->{DEVICES} = \@devices; +} + +sub readingsGroup_Define($$) +{ + my ($hash, $def) = @_; + + my @args = split("[ \t]+", $def); + + return "Usage: define readingsGroup +" if(@args < 3); + + my $name = shift(@args); + my $type = shift(@args); + + readingsGroup_updateDevices($hash); + + $hash->{STATE} = 'Initialized'; + + return undef; +} + +sub readingsGroup_Undefine($$) +{ + my ($hash,$arg) = @_; + + return undef; +} + +sub +readingsGroup_2html($) +{ + my($hash) = @_; + $hash = $defs{$hash} if( ref($hash) ne 'HASH' ); + + return undef if( !$hash ); + + my $d = $hash->{NAME}; + + my $show_heading = !AttrVal( $d, "noheading", "0" ); + my $show_state = !AttrVal( $d, "nostate", "0" ); + my $show_time = !AttrVal( $d, "notime", "0" ); + + my $separator = AttrVal( $d, "separator", ":" ); + my $style = AttrVal( $d, "style", "" ); + my $name_style = AttrVal( $d, "nameStyle", "" ); + my $value_style = AttrVal( $d, "valueStyle", "" ); + my $timestamp_style = AttrVal( $d, "timestampStyle", "" ); + + my $mapping = AttrVal( $d, "mapping", undef); + $mapping = eval $mapping if( $mapping ); + $mapping = undef if( ref($mapping) ne 'HASH' ); + + my $devices = $hash->{DEVICES}; + + my $ret; + + my $row = 1; + $ret .= ""; + $ret .= "" if( $show_heading ); + $ret .= ""; + $ret .= "
"; + foreach my $device (@{$devices}) { + my $h = $defs{@{$device}[0]}; + my $regex = @{$device}[1]; + my $name = $h->{NAME}; + next if( !$h ); + + if( $regex =~ m/\+(.*)/ ) { + $regex = $1; + + my $now = gettimeofday(); + my $fmtDateTime = FmtDateTime($now); + + foreach my $n (sort keys %{$h}) { + next if( $n =~ m/^\./); + next if( defined($regex) && $n !~ m/^$regex$/); + my $val = $h->{$n}; + + my $r = ref($val); + next if($r && ($r ne "HASH" || !defined($hash->{$n}{VAL}))); + + my $v = FW_htmlEscape($val); + + $ret .= sprintf("", ($row&1)?"odd":"even"); + $row++; + + my $name_style = $name_style; + if(defined($name_style) && $name_style =~ m/^{.*}$/) { + my $DEVICE = $name; + my $READING = $n; + my $VALUE = $v; + $name_style = eval $name_style; + $name_style = "" if( !$name_style ); + } + my $value_style = $value_style; + if(defined($value_style) && $value_style =~ m/^{.*}$/) { + my $DEVICE = $name; + my $READING = $n; + my $VALUE = $v; + $value_style = eval $value_style; + $value_style = "" if( !$value_style ); + } + + my $m = "$name$separator$n"; + $m = $mapping->{$n} if( defined($mapping) && defined($mapping->{$n}) ); + $m = $mapping->{$name.".".$n} if( defined($mapping) && defined($mapping->{$name.".".$n}) ); + $m =~ s/\%DEVICE/$name/g; + $m =~ s/\%READING/$n/g; + $ret .= ""; + + $ret .= ""; + $ret .= "" if( $show_time ); + } + } else { + foreach my $n (sort keys %{$h->{READINGS}}) { + next if( $n =~ m/^\./); + next if( $n eq "state" && !$show_state ); + next if( defined($regex) && $n !~ m/^$regex$/); + my $val = $h->{READINGS}->{$n}; + + if(ref($val)) { + my ($v, $t) = ($val->{VAL}, $val->{TIME}); + $v = FW_htmlEscape($v); + $t = "" if(!$t); + + $ret .= sprintf("", ($row&1)?"odd":"even"); + $row++; + + my $name_style = $name_style; + if(defined($name_style) && $name_style =~ m/^{.*}$/) { + my $DEVICE = $name; + my $READING = $n; + my $VALUE = $v; + $name_style = eval $name_style; + $name_style = "" if( !$name_style ); + } + my $value_style = $value_style; + if(defined($value_style) && $value_style =~ m/^{.*}$/) { + my $DEVICE = $name; + my $READING = $n; + my $VALUE = $v; + $value_style = eval $value_style; + $value_style = "" if( !$value_style ); + } + + my $m = "$name$separator$n"; + $m = $mapping->{$n} if( defined($mapping) && defined($mapping->{$n}) ); + $m = $mapping->{$name.".".$n} if( defined($mapping) && defined($mapping->{$name.".".$n}) ); + $m =~ s/\%DEVICE/$name/g; + $m =~ s/\%READING/$n/g; + $ret .= ""; + + $ret .= ""; + $ret .= "" if( $show_time ); + } + } + } + } + $ret .= "
$v
$fmtDateTime
$v
$t
"; + $ret .= "
"; + + return $ret; +} +sub +readingsGroup_detailFn() +{ + my ($FW_wname, $d, $room, $pageHash) = @_; # pageHash is set for summaryFn. + + return readingsGroup_2html($d); +} + +sub +readingsGroup_Notify($$) +{ + my ($hash,$dev) = @_; + my $name = $hash->{NAME}; + + 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; + } + readingsGroup_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/ $//; + } + readingsGroup_updateDevices($hash); + } elsif( $dev->{NAME} eq "global" && $s =~ m/^DEFINED ([^ ]*)$/) { + readingsGroup_updateDevices($hash); + } else { + next if(AttrVal($name,"disable", undef)); + + next if (!$hash->{CONTENT}->{$dev->{NAME}}); + + my @parts = split(/: /,$s); + my $reading = shift @parts; + my $value = join(": ", @parts); + + $reading = "" if( !defined($reading) ); + next if( $reading =~ m/^\./); + $value = "" if( !defined($value) ); + if( $value eq "" ) { + next if( AttrVal( $name, "nostate", "0" ) ); + + $reading = "state"; + $value = $s; + } + + foreach my $device (@{$devices}) { + my $h = $defs{@{$device}[0]}; + next if( !$h ); + next if( $dev->{NAME} ne $h->{NAME} ); + my $regex = @{$device}[1]; + next if( defined($regex) && $reading !~ m/^$regex$/); + CommandTrigger( "", "$name $dev->{NAME}.$reading: $value" ); + } + } + } + + return undef; +} + +sub +readingsGroup_Set($@) +{ + my ($hash, $name, $cmd, $param, @a) = @_; + my $ret = ""; + + return undef; +} + +sub +readingsGroup_Get($@) +{ + my ($hash, @a) = @_; + + 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 readingsGroup_2html($hash); + } + + return undef; + return "Unknown argument $cmd, choose one of html:noArg"; +} + +1; + +=pod +=begin html + + +

readingsGroup

+ + +=end html +=cut diff --git a/fhem/MAINTAINER.txt b/fhem/MAINTAINER.txt index a7c413de6..4661775db 100644 --- a/fhem/MAINTAINER.txt +++ b/fhem/MAINTAINER.txt @@ -84,6 +84,7 @@ FHEM/30_HUEBridge.pm justme1968 http://forum.fhem.de Sonstige FHEM/31_HUEDevice.pm justme1968 http://forum.fhem.de Sonstige Systeme FHEM/31_LightScene.pm justme1968 http://forum.fhem.de Automatisierung FHEM/32_SYSSTAT.pm justme1968 http://forum.fhem.de Unterstützende Dienste +FHEM/33_readingsGroup.pm justme1968 http://forum.fhem.de Automatisierung FHEM/32_speedtest.pm justme1968 http://forum.fhem.de Sonstiges FHEM/34_panStamp.pm justme1968 http://forum.fhem.de Sonstiges Systeme FHEM/34_SWAP.pm justme1968 http://forum.fhem.de Sonstiges Systeme