| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213 |
- # $Id: 33_readingsGroup.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 vars qw(%modules);
- use vars qw(%defs);
- use vars qw(%attr);
- use vars qw($init_done);
- use vars qw($lastDefChange);
- sub Log($$);
- sub Log3($$$);
- use vars qw(%data);
- 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 @mapping_attrs = qw( commands:textField-long mapping:textField-long nameIcon:textField-long cellStyle:textField-long nameStyle:textField-long valueColumn:textField-long valueColumns:textField-long valueFormat:textField-long valuePrefix:textField-long valueSuffix:textField-long valueIcon:textField-long valueStyle:textField-long );
- 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->{AttrFn} = "readingsGroup_Attr";
- $hash->{AttrList} = "disable:1,2,3 style timestampStyle ". join( " ", @mapping_attrs ) ." separator nolinks:1 noheading:1 nonames:1 notime:1 nostate:1 firstCalcRow:1,2,3,4 alwaysTrigger:1,2 sortDevices:1 sortFn visibility:hidden,hideable,collapsed,collapsible setList setFn:textField-long headerRows sortColumn";
- $hash->{FW_detailFn} = "readingsGroup_detailFn";
- $hash->{FW_summaryFn} = "readingsGroup_detailFn";
- $data{FWEXT}{"readingsGroup"}{SCRIPT} = "fhemweb_readingsGroup.js";
- $hash->{FW_atPageEnd} = 1;
- }
- sub
- readingsGroup_updateDevices($;$)
- {
- my ($hash,$def) = @_;
- $def = $hash->{helper}{DEF} if( !defined($def) );
- $hash->{helper}{DEF} = $def;
- $def = $hash->{DEF} if( !defined($def) );
- my %list;
- my %list2;
- my @devices;
- my @devices2;
- my @params = split(" ", $def);
- while (@params) {
- my $param = shift(@params);
- while ($param && $param =~ m/^</ && $param !~ m/>$/ ) {
- my $next = shift(@params);
- last if( !defined($next) );
- $param .= " ". $next;
- }
- # 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, 2); # 2 -> to allow : in calc expressions
- if( $device[1] && $device[1] =~ m/^FILTER=/ ) {
- my @device = split(":", $param); # split all to get multiple FILTER but exclude the : before the readings
- my $devspec = shift(@device);
- while( @device && $device[0] =~ m/^FILTER=/ ) {
- $devspec .= ":";
- $devspec .= shift(@device);
- }
- my $regex = join(':', @device); # merge the rest back again
- foreach my $d (devspec2array($devspec)) {
- $list{$d} = 1;
- push @devices, [$d,$regex];
- }
- } elsif($device[0] =~ m/^<.*>$/) {
- push @devices, [$device[0]];
- } elsif($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( $lattr ne 'IODev' && $defs{$d}{$lattr} !~ m/^$re$/);
- next if( $lattr eq 'IODev' && $defs{$d}{$lattr}{NAME} && $defs{$d}{$lattr}{NAME} !~ 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]];
- }
- }
- }
- }
- foreach my $device (@devices) {
- my $regex = $device->[1];
- my @list = ('.*');
- @list = split(",",$regex) if( $regex );
- my $first = 1;
- my $multi = @list;
- for( my $i = 0; $i <= $#list; ++$i ) {
- my $regex = $list[$i];
- while ($regex
- && ( ($regex =~ m/^</ && $regex !~ m/>$/) #handle , in <...>
- || ($regex =~ m/@\{/ && $regex !~ m/\}$/) #handle , in reading@{...}
- || ($regex =~ m/^\$.*\(/ && $regex !~ m/\)/) ) #handle , in $<calc>(...)
- && defined($list[++$i]) ) {
- $regex .= ",". $list[$i];
- }
- next if( !$regex );
- if( $regex =~ m/^<.*>$/ ) {
- } elsif( $regex !~ m/^\$/ && $regex =~ m/(.*)@(.*)/ ) {
- $regex = $1;
- my $name = $2;
- next if( $regex && $regex =~ m/^\+(.*)/ );
- next if( $regex && $regex =~ m/^\?(.*)/ );
- if( $name =~ m/^{(.*)}$/s ) {
- my $DEVICE = $device->[0];
- $name = eval $name;
- }
- next if( !$name );
- next if( !defined($defs{$name}) );
- $list2{$name} = 1;
- @devices2 = @devices if( !@devices2 );
- my $found = 0;
- foreach my $device (@devices2) {
- $found = 1 if( $device->[0] eq $name && $device->[1] eq $regex );
- last if $found;
- }
- next if $found;
- push @devices2, [$name,$regex];
- }
- }
- }
- if( AttrVal( $hash->{NAME}, "sortDevices", 0 ) == 1 ) {
- @devices = sort { my $aa = @{$a}[0]; my $bb = @{$b}[0];
- $aa = "#" if( $aa =~ m/^</ );
- $bb = "#" if( $bb =~ m/^</ );
- lc(AttrVal($aa,"sortby",AttrVal($aa,"alias",$aa))) cmp
- lc(AttrVal($bb,"sortby",AttrVal($bb,"alias",$bb))) } @devices;
- }
- $hash->{CONTENT} = \%list;
- $hash->{DEVICES} = \@devices;
- $hash->{CONTENT2} = \%list2;
- delete $hash->{DEVICES2};
- $hash->{DEVICES2} = \@devices2 if( @devices2 );
- $hash->{fhem}->{last_update} = gettimeofday();
- $hash->{fhem}->{lastDefChange} = $lastDefChange;
- }
- sub readingsGroup_Define($$)
- {
- my ($hash, $def) = @_;
- my @args = split("[ \t]+", $def);
- return "Usage: define <name> readingsGroup <device>+" if(@args < 3);
- my $name = shift(@args);
- my $type = shift(@args);
- $hash->{STATE} = 'Initialized';
- if( $init_done ) {
- readingsGroup_updateDevices($hash);
- $hash->{fhem}->{lastDefChange} = $lastDefChange+1;
- readingsGroup_inithtml($hash);
- }
- return undef;
- }
- sub readingsGroup_Undefine($$)
- {
- my ($hash,$arg) = @_;
- return undef;
- }
- sub
- rgVal2Num($)
- {
- my ($val) = @_;
- return $val if( !defined($val) );
- #$val =~ s/[^-\.\d]//g if( defined($val) );
- $val = ($val =~ /(-?\d+(\.\d+)?)/ ? $1 : "");
- return $val;
- }
- sub
- lookup($$$$$$$$$)
- {
- my($mapping,$name,$alias,$reading,$value,$room,$group,$row,$default) = @_;
- if( $mapping ) {
- if( !ref($mapping) && $mapping =~ m/^{.*}$/s) {
- my $DEVICE = $name;
- my $READING = $reading;
- my $VALUE = $value;
- my $NUM = rgVal2Num($value);
- my $ROW = $row;
- 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"}) );
- $default = $mapping->{"$name.$reading.$value"} if( defined($mapping->{"$name.$reading.$value"}) );
- } else {
- $default = $mapping;
- }
- return $default if( !defined($default) );
- if( !ref($default) && $default =~ m/^{.*}$/s) {
- my $DEVICE = $name;
- my $READING = $reading;
- my $VALUE = $value;
- my $NUM = rgVal2Num($value);
- my $ROW = $row;
- $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/\%ROW/$row/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;
- $default =~ s/\$ROW/$row/g;
- }
- return $default;
- }
- sub
- lookup2($$$$;$$)
- {
- my($lookup,$name,$reading,$value,$row,$column) = @_;
- return "" if( !$lookup );
- if( !ref($lookup) && $lookup =~ m/^{.*}$/s) {
- my $DEVICE = $name;
- my $READING = $reading;
- my $VALUE = $value;
- my $NUM = rgVal2Num($value);
- my $ROW = $row;
- my $COLUMN = $column;
- my $l = eval $lookup;
- if( $@ ) {
- Log 2, $@ if( $@ );
- } else {
- $lookup = $l;
- }
- }
- if( ref($lookup) eq 'HASH' ) {
- my $vf = "";
- $vf = $lookup->{""} if( defined( $lookup->{""} ) );
- $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"}) );
- $vf = $lookup->{"$name.$reading.$value"} if( defined($value) && exists($lookup->{"$name.$reading.$value"}) );
- $vf = $lookup->{"r:$row"} if( defined($row) && exists($lookup->{"r:$row"}) );
- $vf = $lookup->{"c:$column"} if( defined($column) && exists($lookup->{"c:$column"}) );
- $vf = $lookup->{"r:$row,c:$column"} if( defined($row) && defined($column) && exists($lookup->{"r:$row,c:$column"}) );
- $lookup = $vf;
- }
- return undef if( !defined($lookup) );
- if( !ref($lookup) && $lookup =~ m/^{.*}$/s) {
- my $DEVICE = $name;
- my $READING = $reading;
- my $VALUE = $value;
- my $NUM = rgVal2Num($value);
- my $ROW = $row;
- my $COLUMN = $column;
- $lookup = eval $lookup;
- $lookup = "" if( $@ );
- Log 2, $@ if( $@ );
- }
- return undef 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
- readingsGroup_makeLink($$$)
- {
- my($v,$devStateIcon,$cmd) = @_;
- if( $cmd ) {
- my $txt = $v;
- $txt = $devStateIcon if( $devStateIcon );
- my $link = "cmd=$cmd";
- if( AttrVal($FW_wname, "longpoll", 1)) {
- $txt = "<a style=\"cursor:pointer\" onClick=\"FW_cmd('$FW_ME$FW_subdir?XHR=1&$link')\">$txt</a>";
- } else {
- my $fw_room = $FW_webArgs{room};
- $fw_room = "&detail=$FW_webArgs{detail}" if( $FW_webArgs{"detail"} );
- my $srf = $fw_room ? "&room=$fw_room" : "";
- $srf = $fw_room if( $fw_room && $fw_room =~ m/^&/ );
- $txt = "<a href=\"$FW_ME$FW_subdir?$link$srf\">$txt</a>";
- }
- if( !$devStateIcon ) {
- $v = $txt;
- } else {
- $devStateIcon = $txt;
- }
- }
- return ($v, $devStateIcon);
- }
- package readingsGroup;
- sub
- rgCount($$)
- {
- my ($val,$values) = @_;
- my $count = 0;
- if( $val =~ m/^\/(.*)\/$/ ) {
- my $regex = $1;
- foreach my $v (@{$values}) {
- ++$count if( $v =~ m/$regex/ );
- }
- } elsif( $val =~ m/^!(.*)/ ) {
- my $val = $1;
- foreach my $v (@{$values}) {
- ++$count if( $v ne $val );
- }
- } else {
- foreach my $v (@{$values}) {
- ++$count if( $v eq $val );
- }
- }
- return $count;
- }
- use List::Util qw(min max sum);
- sub
- rgCalc($$$$)
- {
- my ($hash,$calc,$cell_row,$cell_column) = @_;
- my $name = $hash->{NAME};
- return undef if( !defined($hash->{helper}{values}) );
- my $args;
- my $cells;
- # format: $<operator>[(<zellen>)][@<alias>]
- if( $calc =~ m/([^@\(]*)(\(([^\(]*)\))?(\(([^\(]*)\))?(@(.*))?/ ) {
- $calc = $1;
- $cells = $5;
- $args = $3 if( defined($cells) );
- $cells = $3 if( !defined($cells) );
- }
- my $firstCalcRow = main::AttrVal($name, "firstCalcRow", 1);
- $cells = '$firstCalcRow..$ROW-1' if( !$cells );
- my @values = ();
- foreach my $cell ( split( ';', $cells ) ) {
- my ($rows,$cols) = split( ':', $cell );
- $rows = '$firstCalcRow..$ROW-1' if( !$rows );
- $cols = $cell_column if( !defined($cols) );
- my $ROW = $cell_row;
- my $COLUMN = $cell_column;
- foreach my $col (eval "($cols)") {
- foreach my $row (eval "($rows)") {
- my $value = $hash->{helper}{values}{orig}[$col][$row];
- if( defined($value) && $value ne '-' ) {
- #$value =~ s/[^-\.\d]//g;
- push @values, $value;
- }
- if( ${hash}->{inDetailFn} ) {
- #FIXME: also add indirect cells
- $hash->{helper}{recalc}[$col][$row] .= "," if( $hash->{helper}{recalc}[$col][$row] );
- $hash->{helper}{recalc}[$col][$row] .= "$cell_row:$cell_column";
- }
- }
- }
- }
- if( $calc eq 'avg' ) {
- my $cnt = scalar @values;
- return undef if( !$cnt );
- return ( sum @values ) / $cnt;
- } elsif( $calc eq 'count' ) {
- return rgCount( $args, \@values );
- }
- return eval $calc .' @values';
- }
- package main;
- sub
- readingsGroup_value2html($$$$$$$$$)
- {
- my ($hash,$calc,$name,$name2,$n,$v,$cell_row,$cell_column,$type) = @_;
- my $d = $hash->{NAME};
- my $informid = "informId=\"$d-$name.$n\"";
- my $value_orig = $v;
- if( $calc ) {
- $v = readingsGroup::rgCalc($hash,$calc,$cell_row,$cell_column);
- $hash->{helper}{values}{calc}[$cell_column][$cell_row] = $calc;
- $informid = "informId=\"$d-calc:$cell_row:$cell_column\"";
- $value_orig = $v;
- $v = "" if( !defined($v) );
- }
- my $value_format = lookup2($hash->{helper}{valueFormat},$name,$n,$v,$cell_row,$cell_column);
- return (undef) if( !defined($value_format) );
- if( $value_format =~ m/%/ ) {
- $v = sprintf( $value_format, $v );
- } elsif( $value_format ne "" ) {
- $v = $value_format;
- }
- my $value_formated = $v;
- my $room = AttrVal($name2, "room", "");
- my $alias = AttrVal($name2, "alias", $name2);
- my $group = AttrVal($name2, "group", "");
- my $cmd;
- my $devStateIcon;
- if( my $value_icon = $hash->{helper}{valueIcon} ) {
- if( my $icon = lookup($value_icon,$name,$alias,$n,$value_formated,$room,$group,$cell_row,"") ) {
- if( $icon =~ m/^[\%\$]devStateIcon$/ ) {
- my %extPage = ();
- my ($allSets, $cmdlist, $txt) = FW_devState($name, $room, \%extPage);
- $devStateIcon = $txt;
- } else {
- $devStateIcon = FW_makeImage( $icon, $v, "icon" );
- $cmd = lookup2($hash->{helper}{commands},$name,$n,$icon);
- $cmd = lookup2($hash->{helper}{commands},$name,$n,$value_formated) if( !$cmd );
- }
- }
- }
- my $webCmdFn = 0;
- if( !$devStateIcon ) {
- $cmd = lookup2($hash->{helper}{commands},$name,$n,$value_formated);
- if( $cmd && $cmd =~ m/^([\w\/.-]*):(\S*)?(\s\S*)?$/ ) {
- my $set = $1;
- my $values = $2;
- $set .= $3 if( $3 );
- if( !$values ) {
- my %extPage = ();
- my ($allSets, undef, undef) = FW_devState($name, $room, \%extPage);
- $allSets = getAllAttr($name) if( $type && $type eq 'attr' );
- my ($set) = split( ' ', $set, 2 );
- if( $allSets && $allSets =~ m/\b$set:([^ ]*)/) {
- $values = $1;
- }
- }
- my $fw_room = $FW_webArgs{room};
- $fw_room = "&detail=$FW_webArgs{detail}" if( $FW_webArgs{"detail"} );
- my $htmlTxt;
- foreach my $fn (sort keys %{$data{webCmdFn}}) {
- no strict "refs";
- $htmlTxt = &{$data{webCmdFn}{$fn}}($FW_wname,$name,$fw_room,$set,$values);
- use strict "refs";
- last if(defined($htmlTxt));
- }
- if( $htmlTxt && $htmlTxt =~ m/^<td>(.*)<\/td>$/ ) {
- $htmlTxt = $1;
- }
- if( $htmlTxt && $htmlTxt =~ m/class='fhemWidget'/ ) {
- $htmlTxt =~ s/class='fhemWidget'/class='fhemWidget' informId='$d-$name.$n'/;
- $informid = "";
- my $txt = lookup($hash->{helper}{mapping},$name,$name,$n,"",$room,$group,$cell_row,undef);
- if( defined($txt) ) {
- $informid = "rg-fhemWidget-label=\"$txt\"";
- }
- }
- if( $htmlTxt ) {
- if( $type && $type eq 'attr' ) {
- my $current = AttrVal( $name, $n, 'unknown' );
- $htmlTxt =~ s/cmd=/type='attr' cmd=/;
- $htmlTxt =~ s/current='[^']*'/current='$current'/;
- }
- $v = $htmlTxt;
- $webCmdFn = 1;
- }
- }
- }
- ($v,$devStateIcon) = readingsGroup_makeLink($v,$devStateIcon,$cmd) if( !$webCmdFn );
- if( my $value_prefix = $hash->{helper}{valuePrefix} ) {
- if( my $value_prefix = lookup2($value_prefix,$name,$n,$value_formated) ) {
- $v = $value_prefix . $v;
- $devStateIcon = $value_prefix . $devStateIcon if( $devStateIcon );
- }
- }
- if( my $value_suffix = $hash->{helper}{valueSuffix} ) {
- if( my $value_suffix = lookup2($value_suffix,$name,$n,$value_formated) ) {
- $v .= $value_suffix;
- $devStateIcon .= $value_suffix if( $devStateIcon );
- }
- }
- my $value_prefix_suffix = $v;
- $hash->{helper}{values}{orig}[$cell_column][$cell_row] = $value_orig;
- $hash->{helper}{values}{formated}[$cell_column][$cell_row] = $value_formated;
- $hash->{helper}{values}{prefixsuffix}[$cell_column][$cell_row] = $value_prefix_suffix;
- my $value_style = lookup2($hash->{helper}{valueStyle},$name,$n,$value_orig,$cell_row,$cell_column);
- $v = "<div $value_style>$v</div>" if( $value_style && !$devStateIcon );
- return($informid,$v,$devStateIcon)
- }
- sub
- readingsGroup_inithtml($)
- {
- my($hash) = @_;
- if( $hash->{alwaysTrigger} && $hash->{alwaysTrigger} > 1 ) {
- ${hash}->{inDetailFn} = 1;
- readingsGroup_2html($hash);
- delete ${hash}->{inDetailFn};
- }
- }
- sub
- readingsGroup_2html($;$)
- {
- my($hash,$extPage) = @_;
- $hash = $defs{$hash} if( ref($hash) ne 'HASH' );
- $FW_ME = "" if( !$FW_ME );
- $FW_subdir = "" if( !$FW_subdir );
- return undef if( !$hash );
- return undef if( !$init_done );
- #if( $hash->{fhem}->{cached} && $hash->{fhem}->{lastDefChange} && $hash->{fhem}->{lastDefChange} == $lastDefChange ) {
- # return $hash->{fhem}->{cached};
- #}
- my $def = $hash->{helper}{DEF};
- $def = $hash->{DEF} if( !defined($def) );
- if( $def && $def =~ m/=/
- || $hash->{fhem}->{lastDefChange} != $lastDefChange ) {
- if( !$hash->{fhem}->{last_update}
- || $hash->{fhem}->{lastDefChange} != $lastDefChange
- || gettimeofday() - $hash->{fhem}->{last_update} > 600 ) {
- readingsGroup_updateDevices($hash);
- }
- }
- delete( $hash->{helper}{recalc} );
- delete( $hash->{helper}{values} );
- delete( $hash->{helper}{positions} );
- my $d = $hash->{NAME};
- my $show_links = !AttrVal( $d, "nolinks", "0" );
- $show_links = 0 if($FW_hiddenroom{detail});
- my $show_heading = !AttrVal( $d, "noheading", "0" );
- my $show_names = !AttrVal($d, "nonames", "0" );
- my $disable = AttrVal($d,"disable", 0);
- if( AttrVal($d,"disable", 0) > 2 ) {
- return "";
- } 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\">readingsGroup $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 $show_state = !AttrVal( $d, "nostate", "0" );
- my $separator = AttrVal( $d, "separator", ":" );
- my $style = AttrVal( $d, "style", "" );
- if( $style =~ m/^{.*}$/s ) {
- my $s = eval $style;
- $style = $s if( $s );
- }
- my $timestamp_style = AttrVal( $d, "timestampStyle", "" );
- my $header_rows = AttrVal( $d, 'headerRows', 0 );
- my $in_footer = 0;
- my $sort_column = AttrVal( $d, 'sortColumn', undef );
- my $devices = $hash->{DEVICES};
- my $group;
- $group = $extPage->{group} if( $extPage );
- $group = AttrVal( $d, "group", undef ) if( !$group );
- $group = "" if( !$group );
- $group =~ s/,/_/g;
- my $show_hide = "";
- my $visibility = AttrVal($d, "visibility", undef );
- if( !$FW_webArgs{"detail"} ) {
- if( $visibility && ( $visibility eq "hidden" || $visibility eq "hideable" ) ) {
- $style = 'style=""' if( !$style );
- $style =~ s/style=(.)/style=$1display:none;/ if( $visibility eq "hidden" );
- $show_hide .= "<a style=\"cursor:pointer\" onClick=\"FW_readingsGroupToggle('$d')\">></a>";
- }
- }
- my $row = 1;
- my $cell_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\">$show_hide $txt</div></td></tr>" if( $show_heading );
- $ret .= "<tr><td><table $style id='readingsGroup-$d'".
- (defined($sort_column)?" sortColumn=\"$sort_column\"":'').
- " groupId=\"$group\" class=\"block wide readingsGroup".
- (defined($sort_column)?' sortable':'') ."\">";
- $ret .= "<thead>" if( $header_rows );
- $ret .= "<tr><td colspan=\"99\"><div style=\"color:#ff8888;text-align:center\">updates disabled</div></tr>" if( $disable > 0 );
- foreach my $device (@{$devices}) {
- my $item = 0;
- my $h = $defs{$device->[0]};
- my $regex = $device->[1];
- if( !$h && $device->[0] =~ m/^<.*>$/ ) {
- $h = $hash if( !$h );
- $regex = $device->[0];
- }
- next if( !$h );
- my $name = $h->{NAME}; #FIXME: name/name2 confusion
- my $name2 = $h->{NAME};
- delete $hash->{groupedList};
- my @list = ('.*');
- @list = split(",",$regex) if( $regex );
- if( @list && $list[0] =~ m/^@(.*)/ ) {
- my $index = $1;
- my $regex = $list[$index];
- if( $regex && $regex =~ m/^r:(.*)/ ) {
- $regex = $1;
- }
- my @l;
- foreach my $n (keys %{$h->{READINGS}}) {
- eval { $n =~ m/^$regex$/ };
- if( $@ ) {
- Log3 $name, 3, $name .": ". $regex .": ". $@;
- last;
- }
- next if( $n !~ m/^$regex$/);
- push @l, [$n, $1, $2, $3, $4];
- }
- if( my $sortFn = AttrVal($d, "sortFn", '') ) {
- sub rgSortIP {
- return inet_aton(@{$a}[1]) cmp inet_aton(@{$b}[1]);
- };
- @l = eval "sort { $sortFn } \@l";
- if( $@ ) {
- $txt = "<ERROR>";
- Log3 $d, 3, $d .": ". $regex .": ". $@;
- next;
- }
- } else {
- sub rgSort {
- return @{$a}[1] cmp @{$b}[1];
- };
- @l = sort rgSort @l;
- }
- $hash->{groupedList} = [];
- foreach my $n (@l) {
- my $cg1 = @{$n}[1]; my $cg2 = @{$n}[2]; my $cg3 = @{$n}[3]; my $cg4 = @{$n}[4];
- my @l = @list[1..@list-1];
- $l[$index-1] = @{$n}[0];
- s/#1/$cg1/ for @l; s/#2/$cg2/ for @l; s/#3/$cg3/ for @l; s/#4/$cg4/ for @l;
- push @{$hash->{groupedList}}, '<br2>' if( $hash->{groupedList} );
- push @{$hash->{groupedList}}, @l;
- }
- @list = @{$hash->{groupedList}} if( $hash->{groupedList} );
- }
- my $first = 1;
- my $multi = @list;
- my $cell_column = 1;
- for( my $i = 0; $i <= $#list; ++$i ) {
- my $name = $name;
- my $name2 = $name2;
- my $regex = $list[$i];
- while ($regex
- && ( ($regex =~ m/^</ && $regex !~ m/>$/) #handle , in <...>
- || ($regex =~ m/@\{/ && $regex !~ m/\}$/) #handle , in reading@{...}
- || ($regex =~ m/^\$.*\(/ && $regex !~ m/\)/) ) #handle , in $<calc>(...)
- && defined($list[++$i]) ) {
- $regex .= ",". $list[$i];
- }
- $item++;
- my $h = $h;
- my $type;
- my $force_show = 0;
- my $calc;
- my $format;
- if( $regex && $regex =~ m/^<(.*)>$/ ) {
- my $txt = $1;
- my $readings;
- if( $txt =~ m/^{(.*)}(@[\w\-|.*]+)?$/ ) {
- $txt = "{$1}";
- $readings = $2;
- my $new_line = $first;
- my $DEVICE = $name;
- ($txt,$new_line) = eval $txt;
- $first = $new_line if( defined($new_line) );
- if( $@ ) {
- $txt = "<ERROR>";
- Log3 $d, 3, $d .": ". $regex .": ". $@;
- }
- next if( !defined($txt) );
- }
- my $cell_style0 = lookup2($hash->{helper}{cellStyle},$name,$1,undef,$cell_row,0);
- my $cell_style = lookup2($hash->{helper}{cellStyle},$name,$1,undef,$cell_row,$cell_column);
- my $name_style = lookup2($hash->{helper}{nameStyle},$name,$1,undef,$cell_row,$cell_column);
- my $value_columns = lookup2($hash->{helper}{valueColumns},$name,$1,undef,$cell_row,$cell_column);
- my $row_style = lookup2($hash->{helper}{rowStyle},$name,$1,undef,$cell_row,undef);
- if( !$FW_webArgs{"detail"} ) {
- if( $visibility && $visibility eq "collapsed" && $txt ne '-' && $txt ne '+' && $txt ne '+-' ) {
- $row_style = 'style=""' if( !$row_style );
- $row_style =~ s/style=(.)/style=$1display:none;/;
- }
- }
- $row++ if( $txt eq 'br2' );
- if( $txt eq 'br' || $txt eq 'br2' ) {
- $ret .= sprintf("<tr class=\"%s\">", ($row-1&1)?"odd":"even");
- $ret .= "<td $value_columns><div $cell_style $name_style class=\"dname\"></div></td>" if( $show_names );
- $first = 0;
- ++$cell_row;
- $cell_column = 1;
- next;
- } elsif( $txt eq 'hr' ) {
- $ret .= sprintf("<tr $row_style class=\"%s\">", ($row&1)?"odd":"even");
- $row++;
- $ret .= "<td style='padding:0px' colspan='99'><hr/></td>";
- next;
- } elsif( $txt eq 'tfoot' ) {
- $ret .= "</tbody>" if( $header_rows && !$in_footer );
- $ret .= "<tfoot>" if( !$in_footer );
- $in_footer = 1;
- next;
- } elsif( $txt eq '-' || $txt eq '+' || $txt eq '+-' ) {
- my $collapsed = $visibility && ( $visibility eq "collapsed" ) && !$FW_webArgs{"detail"};
- my $id = '';
- if( ($txt eq '+' && !$collapsed)
- || ($txt eq '-' && $collapsed ) ) {
- $id = '';
- $row_style = 'style=""' if( !$row_style );
- $row_style =~ s/style=(.)/style=$1display:none;/;
- } elsif( $txt eq '+-' ) {
- if( $collapsed ) {
- $txt = '+';
- } else {
- $txt = '-';
- }
- $id = "id='plusminus'";
- } elsif( $txt ne '+' && $collapsed ) {
- $row_style = 'style=""' if( !$row_style );
- $row_style =~ s/style=(.)/style=$1display:none;/;
- }
- $ret .= sprintf("<tr $row_style class=\"%s\">", ($row-1&1)?"odd":"even") if( $first );
- if( $visibility && ( $visibility eq "collapsed" || $visibility eq "collapsible" ) ) {
- $ret .= "<td $value_columns><div $id style=\"cursor:pointer\" onClick=\"FW_readingsGroupToggle2('$d')\">$txt</div></td>";
- } else {
- $ret .= "<td $value_columns><div>$txt</div></td>";
- }
- $first = 0;
- ++$cell_column;
- next;
- } elsif( $txt && $txt =~ m/^%([^%]*)(%(.*))?/ ) {
- my $icon = $1;
- my $cmd = $3;
- $txt = FW_makeImage( $icon, $icon, "icon" );
- $cmd = lookup2($hash->{helper}{commands},$name,$d,$icon) if( !defined($cmd) );
- ($txt,undef) = readingsGroup_makeLink($txt,undef,$cmd);
- if( $first || $multi == 1 ) {
- $ret .= sprintf("<tr $row_style class=\"%s\">", ($row&1)?"odd":"even");
- $row++;
- }
- } elsif( $first || $multi == 1 ) {
- $ret .= sprintf("<tr $row_style class=\"%s\">", ($row&1)?"odd":"even");
- $row++;
- if( $h != $hash ) {
- my $a = AttrVal($name2, "alias", $name2);
- my $m = "$a";
- $m = $a if( $multi != 1 );
- $m = "" if( !$show_names );
- my $room = AttrVal($name2, "room", "");
- my $group = AttrVal($name2, "group", "");
- my $txt = lookup($hash->{helper}{mapping},$name2,$a,"","",$room,$group,$cell_row,$m);
- $ret .= "<td $value_columns><div $cell_style0 $name_style class=\"dname\">$txt</div></td>" if( $show_names );
- }
- } else {
- my $webCmdFn = 0;
- my $cmd = lookup2($hash->{helper}{commands},$name,$d,$txt);
- if( $cmd && $cmd =~ m/^([\w\/.-]*):(\S*)?(\s\S*)?$/ ) {
- my $set = $1;
- my $values = $2;
- $set .= $3 if( $3 );
- if( !$values ) {
- my %extPage = ();
- my ($allSets, undef, undef) = FW_devState($name, "", \%extPage);
- my ($set) = split( ' ', $set, 2 );
- if( $allSets && $allSets =~ m/\b$set:([^ ]*)/) {
- $values = $1;
- }
- }
- my $fw_room = $FW_webArgs{room};
- $fw_room = "&detail=$FW_webArgs{detail}" if( $FW_webArgs{"detail"} );
- my $htmlTxt;
- foreach my $fn (sort keys %{$data{webCmdFn}}) {
- no strict "refs";
- $htmlTxt = &{$data{webCmdFn}{$fn}}($FW_wname,$name,$fw_room,$set,$values);
- use strict "refs";
- last if(defined($htmlTxt));
- }
- if( $htmlTxt && $htmlTxt =~ m/^<td>(.*)<\/td>$/ ) {
- $htmlTxt = $1;
- }
- if( $htmlTxt ) {
- $txt = $htmlTxt;
- $webCmdFn = 1;
- }
- }
- ($txt,undef) = readingsGroup_makeLink($txt,undef,$cmd) if( !$webCmdFn );
- }
- my $informid = "";
- $informid = "informId=\"$d-item:$cell_row:$item\"" if( $readings );
- $ret .= "<td $value_columns><div $cell_style $name_style $informid>$txt</div></td>";
- $first = 0;
- ++$cell_column;
- next;
- } else {
- if( $regex && $regex !~ m/^\$/ && $regex =~ m/(.*)@([!]?)(.*)/ ) {
- $regex = $1;
- my $force_device = $2;
- $name = $3;
- if( $name =~ m/^{(.*)}$/s ) {
- my $DEVICE = $device->[0];
- $name = eval $name;
- }
- next if( !$name );
- $h = $defs{$name};
- next if( !$h && !$force_device );
- }
- $force_show = 0;
- $type = undef;
- $calc = undef;
- $format = "";
- my $modifier = "";
- if( $regex ) {
- if( $regex =~ m/^([ira]):(.*)/ ) {
- $modifier = $1;
- $regex = $2;
- }
- if( $regex =~ m/^([+?!\$]*)(.*)/ ) {
- $modifier .= $1;
- $regex = $2;
- }
- if( $regex =~ m/^(.*):(t|sec|i|d|r|r\d)$/ ) {
- $regex = $1;
- $format = $2;
- }
- }
- if( $modifier =~ m/[i+]/ ) {
- } elsif( $modifier =~ m/[a?]/ ) {
- $type = 'attr';
- $h = $attr{$name};
- } else {
- $h = $h->{READINGS} if( $h );
- }
- if( $modifier =~ m/\$/ ) {
- $force_show = 1;
- $h = undef;
- $calc = $regex;
- $name = $d;
- # format: $<operator>[(<zellen>)][@<alias>]
- if( $calc =~ m/([^@\(]*)(\(([^\(]*)\))?(\(([^\(]*)\))?(@(.*))?/ ) {
- $regex = $7;
- $regex = $1 if( !defined($regex) );
- }
- }
- $force_show = 1 if( $modifier =~ m/\!/ );
- }
- my @keys = keys %{$h};
- push (@keys, $regex) if( $force_show && (!@keys || !defined($h->{$regex}) ) );
- foreach my $n (sort @keys) {
- #foreach my $n (sort keys %{$h}) {
- next if( $n =~ m/^\./);
- next if( $n eq "state" && !$show_state && (!defined($regex) || $regex ne "state") );
- if( defined($regex) ) {
- eval { $n =~ m/^$regex$/ };
- if( $@ ) {
- Log3 $name, 3, $name .": ". $regex .": ". $@;
- last;
- }
- next if( $n !~ m/^$regex$/);
- }
- my $val = $h->{$n};
- my ($v, $t);
- if(ref($val)) {
- next if( ref($val) ne "HASH" || !defined($val->{VAL}) );
- ($v, $t) = ($val->{VAL}, $val->{TIME});
- if( $format eq 't' || $format eq 'sec' ) {
- $v = $t;
- $v = time() - time_str2num($v) if($format eq 'sec');
- }
- $t = "" if(!$t);
- $t = "" if( $multi != 1 );
- } else {
- $v = $val;
- $v = $n if( !$val && $force_show );
- }
- if( $format =~ m/^[dir]/ ) {
- $v = rgVal2Num($v);
- $v = int($v) if( $format eq 'i' );
- $v = round($v, defined($1) ? $1 : 1) if($format =~ /^r(\d)?/);
- }
- $v = FW_htmlEscape($v);
- my($informid,$devStateIcon);
- ($informid,$v,$devStateIcon) = readingsGroup_value2html($hash,$calc,$name,$name2,$n,$v,$cell_row,$cell_column,$type);
- next if( !defined($informid) );
- #$informid = "informId=\"$d-item:$cell_row:$item\"" if( $format );
- my $cell_style0 = lookup2($hash->{helper}{cellStyle},$name,$n,$v,$cell_row,0);
- my $cell_style = lookup2($hash->{helper}{cellStyle},$name,$n,$v,$cell_row,$cell_column);
- my $name_style = lookup2($hash->{helper}{nameStyle},$name,$n,$v,$cell_row,$cell_column);
- #my $value_style = lookup2($hash->{helper}{valueStyle},$name,$n,$v,$cell_row,$cell_column);
- my $row_style = lookup2($hash->{helper}{rowStyle},$name,$n,$v,$cell_row,undef);
- if( !$FW_webArgs{"detail"} ) {
- if( $visibility && $visibility eq "collapsed" ) {
- $row_style = 'style=""' if( !$row_style );
- $row_style =~ s/style=(.)/style=$1display:none;/;
- }
- }
- my $value_columns = lookup2($hash->{helper}{valueColumns},$name,$n,$v);
- if( $first || $multi == 1 ) {
- $ret .= sprintf("<tr $row_style class=\"%s\">", ($row&1)?"odd":"even");
- $row++;
- if( $show_names ) {
- my $room = AttrVal($name2, "room", "");
- my $alias = AttrVal($name2, "alias", $name2);
- my $group = AttrVal($name2, "group", "");
- my $m = "$alias$separator$n";
- $m = $alias if( $multi != 1 );
- my $txt = lookup($hash->{helper}{mapping},$name2,$alias,($multi!=1?"":$n),$v,$room,$group,$cell_row,$m);
- if( my $name_icon = $hash->{helper}{nameIcon} ) {
- if( my $icon = lookup($name_icon ,$name,$alias,$n,$v,$room,$group,$cell_row,"") ) {
- $txt = FW_makeImage( $icon, $txt, "icon" );
- }
- }
- $txt = "<div $cell_style0>$txt</div>" if( !$show_links );
- $txt = "<a $cell_style0 href=\"$FW_ME$FW_subdir?detail=$name\">$txt</a>" if( defined($txt) && $show_links );
- $ret .= "<td $value_columns><div $name_style class=\"dname\">$txt</div></td>" if( defined($txt) );
- }
- }
- my $value_column = lookup2($hash->{helper}{valueColumn},$name,$n,undef);
- if( $value_column && $multi ) {
- while ($cell_column < $value_column ) {
- $ret .= "<td></td>";
- ++$cell_column;
- }
- }
- $ret .= "<td $value_columns $informid>$devStateIcon</td>" if( $devStateIcon );
- $ret .= "<td $value_columns><div $cell_style $informid>$v</div></td>" if( !$devStateIcon );
- $ret .= "<td><div $timestamp_style informId=\"$d-$name.$n-ts\">$t</div></td>" if( $show_time && $t );
- if( !$calc ) {
- $hash->{helper}{positions}{"$name.$n"} .= "," if( $hash->{helper}{positions}{"$name.$n"} );
- $hash->{helper}{positions}{"$name.$n"} .= "$cell_row:$cell_column";
- }
- $first = 0;
- ++$cell_column;
- }
- }
- if( $cell_row == $header_rows ) {
- $ret .= "</thead>";
- $ret .= "<tbody>";
- }
- ++$cell_row;
- }
- $ret .= "</tbody>" if( $header_rows && !$in_footer );
- if( $disable > 0 ) {
- $ret .= "<tfoot>" if( !$in_footer );
- $in_footer = 1;
- $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>";
- }
- $ret .= "</tfoot>" if( $in_footer );
- $ret .= "</table></td></tr>";
- $ret .= "</table>";
- #$hash->{fhem}->{cached} = $ret;
- return $ret;
- }
- sub
- readingsGroup_detailFn()
- {
- my ($FW_wname, $d, $room, $extPage) = @_; # extPage is set for summaryFn.
- my $hash = $defs{$d};
- return undef if( ${hash}->{inDetailFn} );
- $hash->{mayBeVisible} = 1;
- ${hash}->{inDetailFn} = 1;
- my $html = readingsGroup_2html($d,$extPage);
- delete ${hash}->{inDetailFn};
- return $html;
- }
- sub
- readingsGroup_Notify($$)
- {
- my ($hash,$dev) = @_;
- my $name = $hash->{NAME};
- my $events = deviceEvents($dev,1);
- return if( !$events );
- if( grep(m/^INITIALIZED$/, @{$events}) ) {
- readingsGroup_updateDevices($hash);
- readingsGroup_inithtml($hash);
- return undef;
- } elsif( grep(m/^REREADCFG$/, @{$events}) ) {
- readingsGroup_updateDevices($hash);
- readingsGroup_inithtml($hash);
- return undef;
- }
- return if( !$init_done );
- return if( AttrVal($name,"disable", 0) > 0 );
- return if($dev->{TYPE} eq $hash->{TYPE});
- #return if($dev->{NAME} eq $name);
- my $devices = $hash->{DEVICES};
- $devices = $hash->{DEVICES2} if( $hash->{DEVICES2} );
- my %triggers = ();
- my $max = int(@{$events});
- for (my $i = 0; $i < $max; $i++) {
- my $s = $events->[$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}} && !$hash->{CONTENT2}->{$dev->{NAME}});
- if( $hash->{alwaysTrigger} ) {
- } elsif( !defined($hash->{mayBeVisible}) ) {
- Log3 $name, 5, "$name: not on any display, ignoring notify";
- return undef;
- } else {
- if( defined($FW_visibleDeviceHash{$name}) ) {
- } else {
- Log3 $name, 5, "$name: no longer visible, ignoring notify";
- delete( $hash->{mayBeVisible} );
- delete( $hash->{helper}{recalc} );
- delete( $hash->{helper}{values} );
- delete( $hash->{helper}{positions} );
- return undef;
- }
- }
- my ($reading,$value) = split(": ",$events->[$i], 2);
- next if( !defined($value) );
- next if( $reading =~ m/^\./);
- $reading = "" if( !defined($reading) );
- $value = "" if( !defined($value) );
- my $show_state = !AttrVal( $name, "nostate", "0" );
- my $cell_row = 0;
- foreach my $device (@{$devices}) {
- my $item = 0;
- ++$cell_row;
- 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 = ('.*');
- @list = split(",",$regex) if( $regex );
- if( $hash->{groupedList} ) {
- @list = @{$hash->{groupedList}};
- }
- for( my $i = 0; $i <= $#list; ++$i ) {
- my $regex = $list[$i];
- while ($regex
- && ( ($regex =~ m/^</ && $regex !~ m/>$/) #handle , in <...>
- || ($regex =~ m/@\{/ && $regex !~ m/\}$/) #handle , in reading@{...}
- || ($regex =~ m/^\$.*\(/ && $regex !~ m/\)/) ) #handle , in $<calc>(...)
- && defined($list[++$i]) ) {
- $regex .= ",". $list[$i];
- }
- ++$item;
- next if( $reading eq "state" && !$show_state && (!defined($regex) || $regex ne "state") );
- my $modifier = "";
- my $format = "";
- if( $regex ) {
- if( $regex =~ m/^([ira]):(.*)/ ) {
- $modifier = $1;
- $regex = $2;
- }
- if( $regex && $regex =~ m/^([+?!\$]*)(.*)/ ) {
- $modifier = $1;
- $regex = $2;
- }
- next if( $modifier =~ m/[i+]/ );
- next if( $modifier =~ m/[a?]/ );
- if( $regex =~ m/^(.*):(t|sec|i|d|r|r\d)$/ ) {
- $regex = $1;
- $format = $2;
- }
- }
- my $calc = undef;
- if( $modifier =~ m/\$/ ) {
- $calc = $regex;
- }
- if( $regex && $regex =~ m/^<(.*)>$/ ) {
- my $txt = $1;
- my $readings;
- if( $txt =~ m/^{(.*)}(@([\w\-|.*]+))?$/ ) {
- $txt = "{$1}";
- $readings = $3;
- next if( !$readings );
- next if( $reading !~ m/^$readings$/);
- my $new_line;
- my $DEVICE = $n;
- ($txt,$new_line) = eval $txt;
- if( $@ ) {
- $txt = "<ERROR>";
- Log3 $name, 3, $name .": ". $regex .": ". $@;
- }
- $txt = "" if( !defined($txt) );
- if( $txt && $txt =~ m/^%([^%]*)(%(.*))?/ ) {
- my $icon = $1;
- my $cmd = $3;
- $cmd = lookup2($hash->{helper}{commands},$name,$n,$icon) if( !defined($cmd) );
- $txt = FW_makeImage( $icon, $icon, "icon" );
- ($txt,undef) = readingsGroup_makeLink($txt,undef,$cmd);
- }
- DoTrigger( $name, "item:$cell_row:$item: <html>$txt</html>" );
- }
- next;
- }
- next if( defined($regex) && $reading !~ m/^$regex$/);
- my $value = $value;
- if( $format eq 't' || $format eq 'sec' ) {
- $value = TimeNow();
- $value = time() - time_str2num($value) if($format eq 'sec');
- } elsif( $format =~ m/^[dir]/ ) {
- $value = rgVal2Num($value);
- $value = int($value) if( $format eq 'i' );
- $value = round($value, defined($1) ? $1 : 1) if($format =~ /^r(\d)?/);
- }
- my $value_style = lookup2($hash->{helper}{valueStyle},$n,$reading,$value);
- my $value_orig = $value;
- if( my $value_format = $hash->{helper}{valueFormat} ) {
- my $value_format = lookup2($hash->{helper}{valueFormat},$n,$reading,$value);
- if( !defined($value_format) ) {
- $value = "";
- } elsif( $value_format =~ m/%/ ) {
- $value = sprintf( $value_format, $value );
- } elsif( $value_format ne "" ) {
- $value = $value_format;
- }
- }
- my $value_formated = $value;
- my $cmd;
- my $devStateIcon;
- if( my $value_icon = $hash->{helper}{valueIcon} ) {
- my $alias = AttrVal($n, "alias", $n);
- my $room = AttrVal($n, "room", "");
- my $group = AttrVal($n, "group", "");
- if( my $icon = lookup($value_icon,$n,$alias,$reading,$value,$room,$group,1,"") ) {
- if( $icon eq "%devStateIcon" ) {
- my %extPage = ();
- my ($allSets, $cmdlist, $txt) = FW_devState($n, $room, \%extPage);
- $devStateIcon = $txt;
- } else {
- $devStateIcon = FW_makeImage( $icon, $value, "icon" );
- $cmd = lookup2($hash->{helper}{commands},$n,$reading,$icon);
- $cmd = lookup2($hash->{helper}{commands},$n,$reading,$value) if( !$cmd );
- }
- }
- if( $devStateIcon ) {
- (undef,$devStateIcon) = readingsGroup_makeLink(undef,$devStateIcon,$cmd);
- if( $hash->{helper}{valuePrefix} ) {
- if( my $value_prefix = lookup2($hash->{helper}{valuePrefix},$n,$reading,$value) ) {
- $devStateIcon = $value_prefix . $devStateIcon if( $devStateIcon );
- }
- }
- if( $hash->{helper}{valueSuffix} ) {
- if( my $value_suffix = lookup2($hash->{helper}{valueSuffix},$n,$reading,$value) ) {
- $devStateIcon .= $value_suffix if( $devStateIcon );
- }
- }
- DoTrigger( $name, "$n.$reading: <html>$devStateIcon</html>" );
- next;
- }
- }
- $cmd = lookup2($hash->{helper}{commands},$n,$reading,$value);
- if( $cmd && $cmd =~ m/^(\w.*):(\S.*)?$/ ) {
- if( $reading eq "state" ) {
- DoTrigger( $name, "$n: $value" );
- #DoTrigger( $name, "$n: <html>$value</html>" );
- } else {
- DoTrigger( $name, "$n.$reading: $value" );
- #DoTrigger( $name, "$n.$reading: <html>$value</html>" );
- }
- next;
- }
- ($value,undef) = readingsGroup_makeLink($value,undef,$cmd);
- if( $hash->{helper}{valuePrefix} ) {
- if( my $value_prefix = lookup2($hash->{helper}{valuePrefix},$n,$reading,$value) ) {
- $value = $value_prefix . $value;
- $devStateIcon = $value_prefix . $devStateIcon if( $devStateIcon );
- }
- }
- my $value_prefix_suffix = $value;
- if( $hash->{helper}{valueSuffix} ) {
- if( my $value_suffix = lookup2($hash->{helper}{valueSuffix},$n,$reading,$value) ) {
- $value .= $value_suffix;
- $devStateIcon .= $value_suffix if( $devStateIcon );
- }
- }
- $value = "<div $value_style>$value</div>" if( $value_style );
- #FIXME: create {'$n.$reading'} = $value hash to avaid multiple events and calculations if same reading is included multiple times
- $triggers{"$n.$reading"} = $value;
- if( my $cells = $hash->{helper}{positions}{"$n.$reading"} ) {
- foreach my $cell ( split( ',', $cells ) ) {
- my ($cell_row,$cell_column) = split( ':', $cell );
- $hash->{helper}{values}{orig}[$cell_column][$cell_row] = $value_orig;
- $hash->{helper}{values}{formated}[$cell_column][$cell_row] = $value_formated;
- $hash->{helper}{values}{prefixsuffix}[$cell_column][$cell_row] = $value_prefix_suffix;
- }
- }
- }
- }
- }
- }
- readingsBeginUpdate($hash) if( $hash->{alwaysTrigger} && $hash->{alwaysTrigger} > 1 );
- foreach my $trigger (keys %triggers) {
- DoTrigger( $name, "$trigger: <html>$triggers{$trigger}</html>" );
- our $count = 0;
- sub updateRefs($$);
- sub
- updateRefs($$)
- {
- my( $hash, $refs ) = @_;
- my $name = $hash->{NAME};
- if( ++$count > 20 ) {
- Log3 $name, 2, "$name: recursionDetected: $refs";
- return;
- }
- foreach my $ref ( split( ',', $refs ) ) {
- my ($row,$col) = split( ':', $ref );
- my $calc = $hash->{helper}{values}{calc}[$col][$row];
- my $func = $calc;
- # format: $<operator>[(<zellen>)][@<alias>]
- if( $calc =~ m/([^@\(]*)(\(([^\(]*)\))?(\(([^\(]*)\))?(@(.*))?/ ) {
- $func = $7;
- $func = $1 if( !defined($func) );
- }
- my($informid,$v,$devStateIcon) = readingsGroup_value2html($hash,$calc,$name,$name,$func,$func,$row,$col,undef);
- $v = "" if( !defined($v) );
- #FIXME: use FW_directNotify
- DoTrigger( $name, "calc:$row:$col: <html>$v</html>" ) if( $hash->{mayBeVisible} );
- if( $hash->{alwaysTrigger} && $hash->{alwaysTrigger} > 1 ) {
- #DoTrigger( $name, "$func: $hash->{helper}{values}{formated}[$col][$row]" );
- readingsBulkUpdate($hash, $func, $hash->{helper}{values}{formated}[$col][$row]);
- }
- if( my $refs = $hash->{helper}{recalc}[$col][$row] ) {
- updateRefs( $hash, $refs );
- }
- }
- --$count;
- }
- if( my $cells = $hash->{helper}{positions}{$trigger} ) {
- foreach my $cell ( split( ',', $cells ) ) {
- my ($cell_row,$cell_column) = split( ':', $cell );
- if( my $refs = $hash->{helper}{recalc}[$cell_column][$cell_row] ) {
- updateRefs( $hash, $refs );
- }
- }
- }
- }
- readingsEndUpdate($hash,1) if( $hash->{alwaysTrigger} && $hash->{alwaysTrigger} > 1 );
- if( %triggers ) {
- my $sort_column = AttrVal( $hash->{NAME}, 'sortColumn', undef );
- DoTrigger( $hash->{NAME}, "sort: $sort_column" ) if( defined($sort_column) )
- }
- return undef;
- }
- sub
- readingsGroup_Set($@)
- {
- my ($hash, $name, $cmd, $param, @a) = @_;
- my $list = "visibility:toggle,toggle2,show,hide";
- if( $cmd eq "refresh" ) {
- readingsGroup_updateDevices($hash);
- return undef;
- } elsif( $cmd eq "visibility" ) {
- readingsGroup_updateDevices($hash);
- DoTrigger( $hash->{NAME}, "visibility: $param" );
- return undef;
- }
- if( my $setList = AttrVal($name, "setList", undef) ) {
- $list .= " ". $setList;
- return "Unknown argument $cmd, choose one of $list" if( $cmd eq '?' );
- foreach my $set (split(" ", $setList)) {
- if( "$set " =~ m/^${cmd}[ :]/ ) {
- my $v = join(" ", @a);
- my $set_fn = AttrVal( $hash->{NAME}, "setFn", "" );
- if( $set_fn =~ m/^{.*}$/s ) {
- my $CMD = $cmd;
- my $ARGS = $param ." ". join(" ", @a);
- my $set_fn = eval $set_fn;
- Log3 $name, 3, $name .": setFn: ". $@ if($@);
- return $set_fn;
- }
- }
- }
- }
- return "Unknown argument $cmd, choose one of $list";
- }
- 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";
- }
- sub
- readingsGroup_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;
- } else {
- delete $hash->{alwaysTrigger};
- }
- readingsGroup_inithtml($hash);
- } elsif( grep { $_ =~ m/$attrName(:.*)?/ } @mapping_attrs ) {
- my $hash = $defs{$name};
- if( $cmd eq "set" ) {
- my $attrVal = $attrVal;
- my %specials= (
- "%DEVICE" => $name,
- "%READING" => $name,
- "%VALUE" => "1",
- "%NUM" => "1",
- "%ROW" => "1",
- "%COLUMN" => "1",
- );
- my $err = perlSyntaxCheck($attrVal, %specials);
- return $err if($err);
- if( $attrVal =~ m/^{.*}$/s && $attrVal =~ m/=>/ && $attrVal !~ m/\$/ ) {
- my $av = eval $attrVal;
- if( $@ ) {
- Log3 $hash->{NAME}, 3, $hash->{NAME} .": ". $@;
- } else {
- $attrVal = $av if( ref($av) eq "HASH" );
- }
- }
- $hash->{helper}{$attrName} = $attrVal;
- } else {
- delete $hash->{helper}{$attrName};
- }
- } elsif( $attrName eq "sortDevices" ) {
- if( $cmd eq "set" ) {
- $attrVal = 1 if($attrVal);
- $attr{$name}{$attrName} = $attrVal;
- } else {
- delete $attr{$name}{$attrName};
- }
- my $hash = $defs{$name};
- readingsGroup_updateDevices($hash);
- }
- 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 formated collection of readings from devices
- =item summary_DE Stellt eine formatierte Darstellung aus Readings von Geräte bereit
- =begin html
- <a name="readingsGroup"></a>
- <h3>readingsGroup</h3>
- <ul>
- Displays a collection of readings from on or more devices.
- <br><br>
- <a name="readingsGroup_Define"></a>
- <b>Define</b>
- <ul>
- <code>define <name> readingsGroup <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><device> can be of the form ATTRIBUTE&VALUE where ATTRIBUTE is the name of an attribute and VALUE is a regex.</li>
- <li><device> can be of the form <STRING> or <{perl}> where STRING or the string returned by perl is
- inserted as a line in the readings list. skipped if STRING is undef.</li>
- <li><device> can be a devspec (see <a href="#devspec">devspec</a>) with at least one FILTER expression.</li>
- <li>If regex is a comma separatet list the reading values will be shown on a single line.</li>
- <li>If regex starts with a '+' it will be matched against the internal values of the device instead of the readings.</li>
- <li>If regex starts with a '?' it will be matched against the attributes of the device instead of the readings.</li>
- <li>If regex starts with a '!' the display of the value will be forced even if no reading with this name is available.</li>
- <li>If regex starts with a '$' the calculation with value columns and rows is possible.</li>
- <li>The following <a href="#set">"set magic"</a> prefixes and suffixes can be used with regex:
- <ul>
- <li>You can use an i:, r: or a: prefix instead of + and ? analogue to the devspec filtering.</li>
- <li>The suffix :d retrieves the first number.</li>
- <li>The suffix :i retrieves the integer part of the first number.</li>
- <li>The suffix :r<n> retrieves the first number and rounds it to <n> decimal places. If <n> is missing, then rounds it to one decimal place.</li>
- <li>The suffix :t returns the timestamp (works only for readings).</li>
- <li>The suffix :sec returns the number of seconds since the reading was set. probably not realy usefull with readingsGroups.</li>
- </ul></li>
- <li>regex can be of the form <regex>@device to use readings from a different device.<br>
- if the device name part starts with a '!' the display will be foreced.
- use in conjunction with ! in front of the reading name.</li>
- <li>regex can be of the form <regex>@{perl} to use readings from a different device.</li>
- <li>regex can be of the form <STRING> or <{perl}[@readings]> where STRING or the string returned by perl is
- inserted as a reading or:
- <ul><li>the item will be skipped if STRING is undef</li>
- <li>if STRING is br a new line will be started</li>
- <li>if STRING is hr a horizontal line will be inserted</li>
- <li>if STRING is tfoot the table footer is started</li>
- <li>if STRING is of the form %ICON[%CMD] ICON will be used as the name of an icon instead of a text and CMD
- as the command to be executed if the icon is clicked. also see the commands attribute.</li></ul>
- if readings is given the perl expression will be reevaluated during longpoll updates.</li>
- <li>If the first regex is '@<index>' it gives the index of the following regex by which the readings
- are to be grouped. if capture groups are used they can be refferenced by #<number>. eg:<br><ul>
- <code><IP-Adress><Hostname><MAC><Vendor><br>
- nmap:@2,<#1>,(.*)_hostname,#1_macAddress,#1_macVendor</code></ul></li>
- <li>For internal values and attributes longpoll update is not possible. Refresh the page to update the values.</li>
- <li>the <{perl}> expression is limited to expressions without a space. it is best just to call a small sub
- in 99_myUtils.pm instead of having a compex expression in the define.</li>
- </ul><br>
- Examples:
- <ul>
- <code>
- define batteries readingsGroup .*:battery</code><br>
- <br>
- <code>define temperatures readingsGroup s300th.*:temperature</code><br>
- <code>define temperatures readingsGroup TYPE=CUL_WS:temperature</code><br>
- <br>
- <code>define culRSSI readingsGroup cul_RSSI=.*:+cul_RSSI</code><br>
- <br>
- <code>define heizung readingsGroup t1:temperature t2:temperature t3:temperature<br>
- attr heizung notime 1<br>
- attr heizung mapping {'t1.temperature' => 'Vorlauf', 't2.temperature' => 'R&uuml;cklauf', 't3.temperature' => 'Zirkulation'}<br>
- attr heizung style style="font-size:20px"<br>
- <br>
- define systemStatus readingsGroup sysstat<br>
- attr systemStatus notime 1<br>
- attr systemStatus nostate 1<br>
- attr systemStatus mapping {'load' => 'Systemauslastung', 'temperature' => 'Systemtemperatur in &deg;C'}<br>
- <br>
- define Verbrauch readingsGroup TYPE=PCA301:state,power,consumption<br>
- attr Verbrauch mapping %ALIAS<br>
- attr Verbrauch nameStyle style="font-weight:bold"<br>
- attr Verbrauch style style="font-size:20px"<br>
- attr Verbrauch valueFormat {power => "%.1f W", consumption => "%.2f kWh"}<br>
- attr Verbrauch valueIcon { state => '%devStateIcon' }<br>
- attr Verbrauch valueStyle {($READING eq "power" && $VALUE > 150)?'style="color:red"':'style="color:green"'}<br>
- <br>
- define rg_battery readingsGroup TYPE=LaCrosse:[Bb]attery<br>
- attr rg_battery alias Batteriestatus<br>
- attr rg_battery commands { "battery.low" => "set %DEVICE replaceBatteryForSec 60" }<br>
- attr rg_battery valueIcon {'battery.ok' => 'batterie', 'battery.low' => 'batterie@red'}<br>
- <br>
- define rgMediaPlayer readingsGroup myMediaPlayer:currentTitle,<>,totaltime,<br>,currentAlbum,<>,currentArtist,<br>,volume,<{if(ReadingsVal($DEVICE,"playStatus","")eq"paused"){"%rc_PLAY%set+$DEVICE+play"}else{"%rc_PAUSE%set+$DEVICE+pause"}}@playStatus>,playStatus<br>
- attr rgMediaPlayer commands { "playStatus.paused" => "set %DEVICE play", "playStatus.playing" => "set %DEVICE pause" }<br>
- attr rgMediaPlayer mapping <br>
- attr rgMediaPlayer notime 1<br>
- attr rgMediaPlayer valueFormat { "volume" => "Volume: %i" }<br>
- #attr rgMediaPlayer valueIcon { "playStatus.paused" => "rc_PLAY", "playStatus.playing" => "rc_PAUSE" }<br>
- </code><br>
- </ul>
- </ul><br>
- <a name="readingsGroup_Set"></a>
- <b>Set</b>
- <ul>
- <li>hide<br>
- will hide all visible instances of this readingsGroup</li>
- <li>show<br>
- will show all visible instances of this readingsGroup</li>
- <li>toggle<br>
- will toggle the hidden/shown state of all visible instances of this readingsGroup</li>
- <li>toggle2<br>
- will toggle the expanded/collapsed state of all visible instances of this readingsGroup</li>
- </ul><br>
- <a name="readingsGroup_Get"></a>
- <b>Get</b>
- <ul>
- </ul><br>
- <a name="readingsGroup_Attr"></a>
- <b>Attributes</b>
- <ul>
- <li>alwaysTrigger<br>
- 1 -> alwaysTrigger update events. even if not visible.<br>
- 2 -> trigger events for calculated values.</li><br>
- <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><br>
- <li>sortDevices<br>
- 1 -> sort the device lines alphabetically. use the first of sortby or alias or name that is defined for each device.</li>
- <li>noheading<br>
- If set to 1 the readings table will have no heading.</li><br>
- <li>nolinks<br>
- Disables the html links from the heading and the reading names.</li><br>
- <li>nostate<br>
- If set to 1 the state reading is excluded.</li><br>
- <li>nonames<br>
- If set to 1 the reading name / row title is not displayed.</li><br>
- <li>notime<br>
- If set to 1 the reading timestamp is not displayed.</li><br>
- <li>mapping<br>
- Can be a simple string or a perl expression enclosed in {} that returns a hash that maps reading names
- to the displayed name. The keys can be either the name of the reading or <device>.<reading> or
- <reading>.<value> or <device>.<reading>.<value>.
- %DEVICE, %ALIAS, %ROOM, %GROUP, %ROW and %READING are replaced by the device name, device alias, room attribute,
- group attribute and reading name respectively. You can also prefix these keywords with $ instead of %. Examples:<br>
- <code>attr temperatures mapping $DEVICE-$READING</code><br>
- <code>attr temperatures mapping {temperature => "%DEVICE Temperatur"}</code>
- </li><br>
- <li>separator<br>
- The separator to use between the device alias and the reading name if no mapping is given. Defaults to ':'
- a space can be enteread as <code>&nbsp;</code></li><br>
- <li>setList<br>
- Space separated list of commands, which will be returned upon "set name ?",
- so the FHEMWEB frontend can construct a dropdown and offer on/off switches.
- set commands not in this list will be rejected.</li><br>
- <li>setFn<br>
- perl expresion that will be executed for the commands from the setList.
- has access to $CMD and $ARGS.</li><br>
- <li>style<br>
- Specify an HTML style for the readings table, e.g.:<br>
- <code>attr temperatures style style="font-size:20px"</code></li><br>
- <li>cellStyle<br>
- Specify an HTML style for a cell of the readings table. regular rows and colums are counted starting with 1,
- the row headings are column number 0. perl code has access to $ROW and $COLUMN. keys for hash lookup can be
- r:#, c:# or r:#,c:# , e.g.:<br>
- <code>attr temperatures cellStyle { "c:0" => 'style="text-align:right"' }</code></li><br>
- <li>nameStyle<br>
- Specify an HTML style for the reading names, e.g.:<br>
- <code>attr temperatures nameStyle style="font-weight:bold"</code></li><br>
- <li>valueStyle<br>
- Specify an HTML style for the reading values, e.g.:<br>
- <code>attr temperatures valueStyle style="text-align:right"</code></li><br>
- <li>valueColumn<br>
- Specify the minimum column in which a reading should appear. <br>
- <code>attr temperatures valueColumn { temperature => 2 }</code></li><br>
- <li>valueColumns<br>
- Specify an HTML colspan for the reading values, e.g.:<br>
- <code>attr wzReceiverRG valueColumns { eventdescription => 'colspan="4"' }</code></li><br>
- <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 temperatures valueFormat %.1f °C</code><br>
- <code>attr temperatures valueFormat { temperature => "%.1f °C", humidity => "%i %" }</code><br>
- <code>attr temperatures valueFormat { ($READING eq 'temperature')?"%.1f °C":undef }</code></li><br>
- <li>valuePrefix<br>
- text to be prepended to the reading value</li><br>
- <li>valueSuffix<br>
- text to be appended after the reading value<br>
- <code>attr temperatures valueFormat { temperature => "%.1f", humidity => "%i" }</code><br>
- <code>attr temperatures valueSuffix { temperature => "°C", humidity => " %" }</code></li><br>
- <li>nameIcon<br>
- Specify the icon to be used instead of the reading name. Can be a simple string or a perl expression enclosed
- in {} that returns a hash that maps reading names to the icon name. e.g.:<br>
- <code>attr devices nameIcon $DEVICE</code></li><br>
- <li>valueIcon<br>
- Specify an icon to be used instead of the reading value. Can be a simple string or a perl expression enclosed
- in {} that returns a hash that maps reading value to the icon name. e.g.:<br>
- <code>attr devices valueIcon $VALUE</code><br>
- <code>attr devices valueIcon {state => '%VALUE'}</code><br>
- <code>attr devices valueIcon {state => '%devStateIcon'}</code><br>
- <code>attr rgMediaPlayer valueIcon { "playStatus.paused" => "rc_PLAY", "playStatus.playing" => "rc_PAUSE" }</code></li><br>
- <li>commands<br>
- Can be used in to different ways:
- <ul>
- <li>To make a reading or icon clickable by directly specifying the command that should be executed. eg.:<br>
- <code>attr rgMediaPlayer commands { "playStatus.paused" => "set %DEVICE play", "playStatus.playing" => "set %DEVICE pause" }</code></li>
- <li>Or if the mapped command is of the form <command>:[<modifier>] then the normal <a href="#FHEMWEB">FHEMWEB</a>
- webCmd widget for <modifier> will be used for this command. if <modifier> is omitted then the FHEMWEB lookup mechanism for <command> will be used. eg:<br>
- <code>attr rgMediaPlayer commands { volume => "volume:slider,0,1,100" }</code><br>
- <code>attr lights commands { pct => "pct:", dim => "dim:" }</code></li>
- <li>commands can be used for attribtues. eg:<br>
- <code>attr <rg> commands { disable => "disable:" }</code></li>
- </ul></li><br>
- <li>visibility<br>
- if set to hidden or hideable will display a small button to the left of the readingsGroup name to expand/hide the contents of the readingsGroup. if a readingsGroup is expanded then all others in the same group will be hidden.<br>
- <ul>
- hidden -> default state is hidden but can be expanded<br>
- hideable -> default state is visible but can be hidden<br><br>
- </ul>
- if set to collapsed or collapsible readingsGroup will recognise the specials <->,<+> and <+-> as the first elements of
- a line to add a + or - symbol to this line. clicking on the + or - symbol will toggle between expanded and collapsed state. if a readingsGroup is expanded then all others in the same group will be collapsed.
- <ul>
- - -> line will be visible in expanded state<br>
- + -> line will be visible in collapsed state<br>
- +- -> line will be visible in both states<br>
- <br>
- collapsed -> default state is collapsed but can be expanded<br>
- collapsible -> default state is visible but can be collapsed </li>
- </ul>
- <li>headerRows<br>
- </li>
- <li>sortColumn<br>
- > 0 -> automatically sort the table by this column after page loading
- 0 -> do not sort automatically but allow sorting of the table by clicking on a column header
- < 0 -> automatically sort the table in reverse by this column after page loading
- </li>
- <br><li><a href="#perlSyntaxCheck">perlSyntaxCheck</a></li>
- </ul><br>
- For the hash version of all mapping attributes it is possible to give a default value
- with <code>{ '' => <default> }</code>.<br><br>
- The style attributes can also contain a perl expression enclosed in {} that returns the style
- string to use. For nameStyle and valueStyle The perl code can use $DEVICE,$READING,$VALUE and $NUM, e.g.:<br>
- <ul>
- <code>attr batteries valueStyle {($VALUE ne "ok")?'style="color:red"':'style="color:green"'}</code><br>
- <code>attr temperatures valueStyle {($DEVICE =~ m/aussen/)?'style="color:green"':'style="color:red"'}</code>
- </ul><br>
- Note: Only valueStyle, valueFomat, valueIcon and <{...}@reading> are evaluated during longpoll updates
- and valueStyle has to return a non empty style for every possible value. All other perl expressions are
- evaluated only once during html creation and will not reflect value updates with longpoll.
- Refresh the page to update the dynamic style. For nameStyle the color attribut is not working at the moment,
- the font-... and background attributes do work.<br><br>
- Calculation: to be written...<br>
- eg: <code>define rg readingsGroup .*:temperature rg:$avg</code><br>
- please see a description <a href="http://www.fhemwiki.de/wiki/ReadingsGroup#Berechnungen">in the wiki</a>
- </ul>
- =end html
- =begin html_DE
- <a name="readingsGroup"></a>
- <h3>readingsGroup</h3>
- <ul>
- Zeigt eine Sammlung von Messwerten von einem oder mehreren Geräten an.
- <br><br>
- <a name="readingsGroup_Define"></a>
- <b>Define</b>
- <ul>
- <code>define <name> readingsGroup <device>[:regex] [<device-2>[:regex-2]] ... [<device-n>[:regex-n]]</code><br>
- <br>
- Anmerkungen:
- <ul>
- <li><device> kann die Form INTERNAL=VALUE haben, wobei INTERNAL der Name eines internen Wertes ist und VALUE ein Regex.</li>
- <li><device> kann die Form ATTRIBUTE&VALUE haben, wobei ATTRIBUTE der Name eines Attributs ist und VALUE ein Regex.</li>
- <li><device> kann die Form <STRING> oder <{perl}> haben, wobei STRING oder die von Perl zurückgegebene Zeichenfolge als Zeile in die Readings List eingefügt wird. Wird übersprungen, wenn STRING undef ist.</li>
- <li><device> kann ein devspec sein (siehe <a href="#devspec">devspec</a>) mit mindestens einem FILTER-Ausdruck sein.</li>
- <li>Wenn Regex eine Komma separarierte Liste ist, werden die Reading-Values in einer einzelnen Zeile angezeigt.</li>
- <li>Wenn Regex mit einem "+" beginnt, wird es mit den internen Werten (Internals) des Geräts anstelle der Readings verglichen.</li>
- <li>Wenn Regex mit einem '?' beginnt, wird es mit den Attributen des Geräts verglichen und nicht mit den Werten (Readings) verglichen.</li>
- <li>Wenn Regex mit einem '!' beginnt, wird die Anzeige des Wertes erzwungen, auch wenn kein Reading mit diesem Namen verfügbar ist.</li>
- <li>Wenn Regex mit einem '$' beginnt, ist die Berechnung mit Wert-Spalten und Zeilen möglich.</li>
- <li>Die folgenden <a href="#set">"set magic"</a> Präfixe und Suffixe können mit Regex verwendet werden:
- <ul>
- <li>Sie können anstelle von + und ? ein Präfix i :, r: oder a: verwenden. Analog zur devspec-Filterung.</li>
- <li>Der Suffix :d ruft die erste Nummer ab.</li>
- <li>Der Suffix :i ruft den ganzzahligen Teil der ersten Zahl ab.</li>
- <li>Der Suffix :r<n> ruft die erste Zahl ab und rundet sie auf <n> Nachkommastellen ab. Wenn <n> fehlt, wird es auf eine Dezimalstelle gerundet.</li>
- <li>Der Suffix :t gibt den Zeitstempel zurück (funktioniert nur mit Readings).</li>
- <li>Der Suffix :sec gibt die Anzahl der Sekunden seit dem das Reading gesetzt wurde zurück. Wahrscheinlich nicht nützlich mit readingsGroups.</li>
- </ul></li>
- <li>Regex kann von der Form <regex>@device sein, um Readings von einem anderen Gerät zu verwenden.<br>
- Wenn der Gerätename mit einem '!' beginnt, wird die Anzeige deaktiviert. Verwenden Sie in Verbindung mit ! den Reading-Name.</li>
- <li>Regex kann die Form <regex>@{perl} haben, um Readings von einem anderen Gerät zu verwenden.</li>
- <li>Regex kann von der Form <STRING> oder <{perl}[@readings]> sein, wobei STRING oder die von Perl zurückgegebene Zeichenfolge als Reading eingefügt wird, oder:
- <ul><li>das Element wird übersprungen, wenn STRING undef ist</li>
- <li>wenn STRING br ist, wird eine neue Zeile gestartet</li>
- <li>wenn STRING hr ist, wird eine horizontale Linie eingefügt</li>
- <li>wenn STRING tfoot ist, wird der Tabellenfuß gestartet</li>
- <li>wenn STRING die Form hat, %ICON[%CMD] ICON wird als Name eines Symbols anstelle von Text und CMD als der Befehl verwendet, der ausgeführt werden soll, wenn auf das Symbol geklickt wird. Siehe auch die Befehlsattribute.</li></ul>
- Wenn Readings aktualisiert werden, wird der Perl-Ausdruck bei Longpoll-Aktualisierungen erneut ausgewertet.</li>
- <li>Wenn der erste Regex '@<index>' ist, gibt es den Index der folgenden Regex an, mit dem die Messwerte gruppiert werden sollen. Wenn Erfassungsgruppen verwendet werden, können sie durch #<number> refferenziert werden. z.Bsp:<br><ul>
- <code><IP-Adress><Hostname><MAC><Vendor><br>
- nmap:@2,<#1>,(.*)_hostname,#1_macAddress,#1_macVendor</code></ul></li>
- <li>Für interne Werte (Internals) und Attribute ist longpoll update nicht möglich. Aktualisieren Sie die Seite, um die Werte zu aktualisieren.</li>
- <li>Der Ausdruck <{perl}> ist auf Ausdrücke ohne Leerzeichen beschränkt. Es ist am besten, eine kleine Sub in 99_myUtils.pm aufzurufen, anstatt einen complexen Ausdruck im Define zu haben.</li>
- </ul><br>
- Beispiele:
- <ul>
- <code>
- define batteries readingsGroup .*:battery</code><br>
- <br>
- <code>define temperatures readingsGroup s300th.*:temperature</code><br>
- <code>define temperatures readingsGroup TYPE=CUL_WS:temperature</code><br>
- <br>
- <code>define culRSSI readingsGroup cul_RSSI=.*:+cul_RSSI</code><br>
- <br>
- <code>define heizung readingsGroup t1:temperature t2:temperature t3:temperature<br>
- attr heizung notime 1<br>
- attr heizung mapping {'t1.temperature' => 'Vorlauf', 't2.temperature' => 'R&uuml;cklauf', 't3.temperature' => 'Zirkulation'}<br>
- attr heizung style style="font-size:20px"<br>
- <br>
- define systemStatus readingsGroup sysstat<br>
- attr systemStatus notime 1<br>
- attr systemStatus nostate 1<br>
- attr systemStatus mapping {'load' => 'Systemauslastung', 'temperature' => 'Systemtemperatur in &deg;C'}<br>
- <br>
- define Verbrauch readingsGroup TYPE=PCA301:state,power,consumption<br>
- attr Verbrauch mapping %ALIAS<br>
- attr Verbrauch nameStyle style="font-weight:bold"<br>
- attr Verbrauch style style="font-size:20px"<br>
- attr Verbrauch valueFormat {power => "%.1f W", consumption => "%.2f kWh"}<br>
- attr Verbrauch valueIcon { state => '%devStateIcon' }<br>
- attr Verbrauch valueStyle {($READING eq "power" && $VALUE > 150)?'style="color:red"':'style="color:green"'}<br>
- <br>
- define rg_battery readingsGroup TYPE=LaCrosse:[Bb]attery<br>
- attr rg_battery alias Batteriestatus<br>
- attr rg_battery commands { "battery.low" => "set %DEVICE replaceBatteryForSec 60" }<br>
- attr rg_battery valueIcon {'battery.ok' => 'batterie', 'battery.low' => 'batterie@red'}<br>
- <br>
- define rgMediaPlayer readingsGroup myMediaPlayer:currentTitle,<>,totaltime,<br>,currentAlbum,<>,currentArtist,<br>,volume,<{if(ReadingsVal($DEVICE,"playStatus","")eq"paused"){"%rc_PLAY%set+$DEVICE+play"}else{"%rc_PAUSE%set+$DEVICE+pause"}}@playStatus>,playStatus<br>
- attr rgMediaPlayer commands { "playStatus.paused" => "set %DEVICE play", "playStatus.playing" => "set %DEVICE pause" }<br>
- attr rgMediaPlayer mapping <br>
- attr rgMediaPlayer notime 1<br>
- attr rgMediaPlayer valueFormat { "volume" => "Volume: %i" }<br>
- #attr rgMediaPlayer valueIcon { "playStatus.paused" => "rc_PLAY", "playStatus.playing" => "rc_PAUSE" }<br>
- </code><br>
- </ul>
- </ul><br>
- <a name="readingsGroup_Set"></a>
- <b>Set</b>
- <ul>
- <li>hide<br>
- Alle sichtbaren Instanzen dieser ReadingsGroup werden ausgeblendet</li>
- <li>show<br>
- Zeigt alle sichtbaren Instanzen dieser ReadingsGroup an</li>
- <li>toggle<br>
- Schaltet den versteckten / angezeigten Zustand aller sichtbaren Instanzen dieser ReadingsGroup an.</li>
- <li>toggle2<br>
- schaltet den erweiterten / kollabierten Zustand aller sichtbaren Instanzen dieser ReadingsGroup an.</li>
- </ul><br>
- <a name="readingsGroup_Get"></a>
- <b>Get</b>
- <ul>
- </ul><br>
- <a name="readingsGroup_Attr"></a>
- <b>Attribute</b>
- <ul>
- <li>alwaysTrigger<br>
- 1 -> alwaysTrigger Ereignisse aktualisieren auch wenn nicht sichtbar.<br>
- 2 -> trigger Ereignisse für berechnete Werte.</li><br>
- <li>disable<br>
- 1 -> Deaktivieren der Benachrichtigung Verarbeitung und Longpoll-Updates. Hinweis: Dadurch wird auch die Umbenennung und Löschbehandlung deaktiviert.<br>
- 2 -> Deaktivieren der HTML-Tabellenerstellung<br>
- 3 -> Deaktivieren der HTML-Erstellung vollständig</li><br>
- <li>sortDevices<br>
- 1 -> Sortieren der Geräteliste alphabetisch. Verwenden Sie das erste von sortby oder alias oder name, das für jedes Gerät definiert ist.</li>
- <li>noheading<br>
- Wenn sie auf 1 gesetzt ist, hat die Readings-Tabelle keine Überschrift.</li><br>
- <li>nolinks<br>
- Deaktiviert die HTML-Links von der Überschrift und den Readings-Namen.</li><br>
- <li>nostate<br>
- Wenn der Wert 1 ist, wird der Status nicht berücksichtigt.</li><br>
- <li>nonames<br>
- Wenn der Wert auf 1 gesetzt ist, wird der Readings-Name / Zeilentitel nicht angezeigt.</li><br>
- <li>notime<br>
- Wenn der Wert auf 1 gesetzt, wird der Readings-Timestamp nicht angezeigt.</li><br>
- <li>mapping<br>
- Kann ein einfacher String oder ein in {} eingeschlossener Perl-Ausdruck sein, der einen Hash zurückgibt, der den Reading-Name dem angezeigten Namen zuordnet.
- Der Schlüssel kann entweder der Name des Readings oder <device>.<reading> oder <reading>.<value> oder <device>.<reading>.<value> sein.
- %DEVICE, %ALIAS, %ROOM, %GROUP, %ROW und %READING werden durch den Gerätenamen, Gerätealias, Raumattribut ersetzt. Sie können diesen Keywords auch ein Präfix voranstellen $ anstatt von %. Beispiele:<br>
- <code>attr temperatures mapping $DEVICE-$READING</code><br>
- <code>attr temperatures mapping {temperature => "%DEVICE Temperatur"}</code>
- </li><br>
- <li>separator<br>
- Das zu verwendende Trennzeichen zwischen dem Gerätealias und dem Reading-Namen, wenn keine Zuordnung angegeben ist, standardgemäß ':'
- Ein Leerzeichen wird so dargestellt <code>&nbsp;</code></li><br>
- <li>setList<br>
- Eine durch Leerzeichen getrennte Liste von Befehlen, die zurückgegeben werden "set name ?",
- Das FHEMWEB-Frontend kann also ein Dropdown-Menü erstellen und An / Aus-Schalter anbieten.
- Set-Befehle, die nicht in dieser Liste enthalten sind, werden zurückgewiesen.</li><br>
- <li>setFn<br>
- Perl-Ausdruck, der für die Befehle aus der setList ausgeführt wird. Es hat Zugriff auf $CMD und $ARGS.</li><br>
- <li>style<br>
- Geben Sie einen HTML-Stil für die Readings-Tabelle an, z.Bsp:<br>
- <code>attr temperatures style style="font-size:20px"</code></li><br>
- <li>cellStyle<br>
- Geben Sie einen HTML-Stil für eine Zelle der Readings-Tabelle an. Normale Zeilen und Spalten werden gezählt beginnend mit 1,
- Die Zeilenüberschriften beginnt mit der Spaltennummer 0. Perl-Code hat Zugriff auf $ROW und $COLUMN. Schlüssel für Hash-Lookup können sein:
- r:#, c:# oder r:#,c:# , z.Bsp:<br>
- <code>attr temperatures cellStyle { "c:0" => 'style="text-align:right"' }</code></li><br>
- <li>nameStyle<br>
- Geben Sie einen HTML-Stil für die Readings-Namen an, z.Bsp:<br>
- <code>attr temperatures nameStyle style="font-weight:bold"</code></li><br>
- <li>valueStyle<br>
- Geben Sie einen HTML-Stil für die Readings-Werte an, z.Bsp:<br>
- <code>attr temperatures valueStyle style="text-align:right"</code></li><br>
- <li>valueColumn<br>
- Geben Sie die Mindestspalte an, in der ein Messwert angezeigt werden soll. z.Bsp:<br>
- <code>attr temperatures valueColumn { temperature => 2 }</code></li><br>
- <li>valueColumns<br>
- Geben Sie einen HTML-Colspan für die Readings-Werte an, z.Bsp:<br>
- <code>attr wzReceiverRG valueColumns { eventdescription => 'colspan="4"' }</code></li><br>
- <li>valueFormat<br>
- Geben Sie eine Sprintf-Stilformat-Zeichenfolge an, die zum Anzeigen der Readings-Werte verwendet wird. Wenn die Formatzeichenfolge undef ist
- wird dieser Messwert übersprungen. Es kann als String angegeben werden, ein Perl-Ausdruck, der einen Hash- oder Perl-Ausdruck zurückgibt, der einen String zurückgibt, z.Bsp:<br>
- <code>attr temperatures valueFormat %.1f °C</code><br>
- <code>attr temperatures valueFormat { temperature => "%.1f °C", humidity => "%i %" }</code><br>
- <code>attr temperatures valueFormat { ($READING eq 'temperature')?"%.1f °C":undef }</code></li><br>
- <li>valuePrefix<br>
- Text, der dem Readings-Wert vorangestellt wird</li><br>
- <li>valueSuffix<br>
- Text, der nach dem Readings-Wert angehängt wird<br>
- <code>attr temperatures valueFormat { temperature => "%.1f", humidity => "%i" }</code><br>
- <code>attr temperatures valueSuffix { temperature => "°C", humidity => " %" }</code></li><br>
- <li>nameIcon<br>
- Geben Sie das Symbol an, das anstelle des Readings-Name verwendet werden soll. Es kann ein einfacher String oder ein in {} eingeschlossener Perl-Ausdruck sein, der einen Hash zurückgibt, der dem Readings-Name den Icon-Namen zuordnet. z.Bsp:<br>
- <code>attr devices nameIcon $DEVICE</code></li><br>
- <li>valueIcon<br>
- Geben Sie ein Symbol an, das anstelle des Readings-Wert verwendet werden soll. Es kann ein einfacher String oder ein in {} eingeschlossener Perl-Ausdruck sein, der einen Hash zurückgibt, der dem Readings-Wert dem Symbolnamen zuordnet. z.Bsp:<br>
- <code>attr devices valueIcon $VALUE</code><br>
- <code>attr devices valueIcon {state => '%VALUE'}</code><br>
- <code>attr devices valueIcon {state => '%devStateIcon'}</code><br>
- <code>attr rgMediaPlayer valueIcon { "playStatus.paused" => "rc_PLAY", "playStatus.playing" => "rc_PAUSE" }</code></li><br>
- <li>commands<br>
- Kann auf verschiedene Arten verwendet werden:
- <ul>
- <li>Um ein Reading oder ein Symbol anklickbar zu machen, indem Sie direkt den Befehl angeben, der ausgeführt werden soll. z.Bsp:<br>
- <code>attr rgMediaPlayer commands { "playStatus.paused" => "set %DEVICE play", "playStatus.playing" => "set %DEVICE pause" }</code></li>
- <li>Wenn der zugeordnete Befehl die Form <command>:[<modifier>] hat, wird das normale <a href="#FHEMWEB">FHEMWEB</a> webCmd-Widget für <Modifikator> für diesen commands verwendet. z.Bsp:<br>
- <code>attr rgMediaPlayer commands { volume => "volume:slider,0,1,100" }</code><br>
- <code>attr lights commands { pct => "pct:", dim => "dim:" }</code></li>
- <li>commands können für Attribute verwendet werden. z.Bsp:<br>
- <code>attr <rg> commands { disable => "disable:" }</code></li>
- </ul></li><br>
- <li>visibility<br>
- Wenn sie auf hidden oder hideable eingestellt ist, wird eine kleine Schaltfläche links neben dem Namen der Readings-Group angezeigt, um den Inhalt der Readings-Group zu erweitern / auszublenden. Wenn eine Readings-Group erweitert wird, werden alle anderen Gruppen derselben Gruppe ausgeblendet.<br>
- <ul>
- hidden -> Standardmäßig ist hidden aktiv, kann jedoch erweitert werden.<br>
- hideable -> Standardmäßig ist hideable aktiv, kann jedoch ausgeblendet werden.<br><br>
- </ul>
- Wenn diese Option auf "collapsed" oder "collapsible" eingestellt ist, erkennt readingsGroup die Specials <->,<+> und <+-> als die ersten Elemente von
- eine Linie, um dieser Linie ein + oder - Symbol hinzuzufügen. Durch Klicken auf das + oder - Symbol wird zwischen erweitertem und reduziertem Zustand umgeschaltet. Wenn eine Readings-Group erweitert wird, werden alle anderen Gruppen in der gleichen Gruppe ausgeblendet.
- <ul>
- - -> Die Linie wird im expandierten Zustand sichtbar sein.<br>
- + -> Die Linie wird im zusammengefalteten Zustand angezeigt.<br>
- +- -> Die Linie wird in beiden Zuständen sichtbar sein.<br>
- <br>
- collapsed -> Der Standardstatus ist reduziert, kann jedoch erweitert werden.<br>
- collapsible -> Der Standardstatus ist sichtbar, kann jedoch minimiert werden.<br><br></li>
- </ul>
- <li>headerRows<br><br>
- </li>
- <li>sortColumn<br>
- > 0 -> sortiert die Tabelle automatisch nach dem Laden der Seite nach dieser Spalte<br>
- 0 -> sortiert Sie nicht automatisch, sondern durch Klicken auf eine Spaltenüberschrift<br>
- < 0 -> sortiert die Tabelle automatisch nach dem Laden der Seite nach dieser Spalte
- </li>
- <br><li><a href="#perlSyntaxCheck">perlSyntaxCheck</a></li>
- </ul><br>
- Für die Hash-Version aller Zuordnungsattribute kann ein Standardwert angegeben werden mit <code>{ '' => <default> }</code>.<br><br>
- Die Stilattribute können auch einen in {} eingeschlossenen Perl-Ausdruck enthalten, der die zu verwendende Stilzeichenfolge zurückgibt. Für nameStyle und valueStyle, kann der Perl-Code $DEVICE,$READING,$VALUE und $NUM verwendet werden. z.Bsp:<br>
- <ul>
- <code>attr batteries valueStyle {($VALUE ne "ok")?'style="color:red"':'style="color:green"'}</code><br>
- <code>attr temperatures valueStyle {($DEVICE =~ m/aussen/)?'style="color:green"':'style="color:red"'}</code>
- </ul><br>
- Hinweis: Nur valueStyle, valueFomat, valueIcon und <{...}@reading> werden bei Longpoll-Updates ausgewertet und valueStyle muss für jeden möglichen Wert einen nicht leeren Stil zurückgeben. Alle anderen Perl-Ausdrücke werden nur einmal während der HTML-Erstellung ausgewertet und geben keine Wertupdates mit longpoll wieder.
- Aktualisieren Sie die Seite, um den dynamischen Stil zu aktualisieren. Für nameStyle funktioniert das Farbattribut momentan nicht, die font -... und background Attribute funktionieren.<br><br>
- Berechnung: Bitte sehen Sie sich dafür diese <a href="http://www.fhemwiki.de/wiki/ReadingsGroup#Berechnungen">Beschreibung</a> an in der Wiki.<br>
- z.Bsp: <code>define rg readingsGroup .*:temperature rg:$avg</code>
-
- </ul>
- =end html_DE
- =cut
|