diff --git a/fhem/CHANGED b/fhem/CHANGED index a46830036..74b42402d 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,6 +1,7 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Do not insert empty lines here, update check depends on it. - SVN + - feature: readingsGroup: added icons and links/commands - feature: new module 98_Text2Speech.pm added (Tobias Faust) Google Translator Engine or ESpeak can be used - feature: YAMAHA_AVR: define separate on and off status intervals for diff --git a/fhem/FHEM/33_readingsGroup.pm b/fhem/FHEM/33_readingsGroup.pm index 03a3f3294..fc4b2b18a 100644 --- a/fhem/FHEM/33_readingsGroup.pm +++ b/fhem/FHEM/33_readingsGroup.pm @@ -35,7 +35,7 @@ sub readingsGroup_Initialize($) #$hash->{SetFn} = "readingsGroup_Set"; $hash->{GetFn} = "readingsGroup_Get"; $hash->{AttrFn} = "readingsGroup_Attr"; - $hash->{AttrList} = "disable:1,2,3 nameIcon valueIcon mapping separator style nameStyle valueColumns valueStyle valueFormat timestampStyle noheading:1 nolinks:1 notime:1 nostate:1 alwaysTrigger:1"; + $hash->{AttrList} = "disable:1,2,3 nameIcon valueIcon mapping separator style nameStyle valueColumns valueStyle valueFormat commands timestampStyle noheading:1 nolinks:1 notime:1 nostate:1 alwaysTrigger:1"; $hash->{FW_detailFn} = "readingsGroup_detailFn"; $hash->{FW_summaryFn} = "readingsGroup_detailFn"; @@ -165,6 +165,7 @@ lookup($$$$$$$$) $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}) ); #} elsif( $mapping =~ m/^{.*}$/) { # my $DEVICE = $name; # my $READING = $reading; @@ -203,6 +204,7 @@ lookup2($$$$) my $vf = ""; $vf = $lookup->{$reading} if( exists($lookup->{$reading}) ); $vf = $lookup->{$name.".".$reading} if( exists($lookup->{$name.".".$reading}) ); + $vf = $lookup->{$reading.".".$value} if( exists($lookup->{$reading.".".$value}) ); $lookup = $vf; } elsif($lookup =~ m/^{.*}$/) { my $DEVICE = $name; @@ -212,8 +214,44 @@ lookup2($$$$) $lookup = "" if( $@ ); } + $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 = "$txt"; + } else { + my $room = $FW_webArgs{room}; + $room = "&detail=$FW_webArgs{detail}"; + my $srf = $room ? "&room=$room" : ""; + $srf = $room if( $room && $room =~ m/^&/ ); + $txt = "$txt"; + } + if( !$devStateIcon ) { + $v = $txt; + } else { + $devStateIcon = $txt; + } + } + + return ($v, $devStateIcon); +} + sub readingsGroup_2html($) { @@ -284,6 +322,8 @@ readingsGroup_2html($) $valueIcon = eval $valueIcon if( $valueIcon =~ m/^{.*}$/ ); #$valueIcon = undef if( ref($valueIcon) ne 'HASH' ); + my $commands = AttrVal( $d, "commands", "" ); + $commands = eval $commands if( $commands =~ m/^{.*}$/ ); my $devices = $hash->{DEVICES}; @@ -320,6 +360,7 @@ readingsGroup_2html($) if( $regex && $regex =~ m/^<(.*)>$/ ) { my $txt = $1; my $readings; + $item++; if( $txt =~ m/^{(.*)}(@[\w|.*]+)?$/ ) { $txt = "{$1}"; $readings = $2; @@ -342,6 +383,14 @@ readingsGroup_2html($) $ret .= "
"; $first = 0; next; + } elsif( $txt && $txt =~ m/^%([^%]*)(%(.*))?/ ) { + my $icon = $1; + my $cmd = $3; + $txt = FW_makeImage( $icon, $icon, "icon" ); + + $cmd = lookup2($commands,$name,undef,$icon) if( !defined($cmd) ); + ($txt,undef) = readingsGroup_makeLink($txt,undef,$cmd); + } elsif( $first || $multi == 1 ) { $ret .= sprintf("", ($row&1)?"odd":"even"); $row++; @@ -357,10 +406,9 @@ readingsGroup_2html($) $ret .= "
$txt
"; } } - $item++; my $inform_id = ""; $inform_id = "informId=\"$d-$item.item\"" if( $readings ); - $ret .= "
$txt
"; + $ret .= "
$txt
"; $first = 0; next; } elsif( $regex && $regex =~ m/^\+(.*)/ ) { @@ -422,6 +470,7 @@ readingsGroup_2html($) } } + my $cmd; my $devStateIcon; if( $valueIcon ) { if( my $icon = lookup($valueIcon,$name,$a,$n,$v,$room,$group,"") ) { @@ -431,10 +480,15 @@ readingsGroup_2html($) $devStateIcon = $txt; } else { $devStateIcon = FW_makeImage( $icon, $v, "icon" ); + $cmd = lookup2($commands,$name,$n,$icon); + $cmd = lookup2($commands,$name,$n,$v) if( !$cmd ); } } } + $cmd = lookup2($commands,$name,$n,$v) if( !$devStateIcon ); + ($v,$devStateIcon) = readingsGroup_makeLink($v,$devStateIcon,$cmd); + if( $first || $multi == 1 ) { $ret .= sprintf("", ($row&1)?"odd":"even"); $row++; @@ -580,6 +634,10 @@ readingsGroup_Notify($$) my $valueIcon = AttrVal( $name, "valueIcon", ""); $valueIcon = eval $valueIcon if( $valueIcon =~ m/^{.*}$/ ); + my $commands = AttrVal( $name, "commands", "" ); + $commands = eval $commands if( $commands =~ m/^{.*}$/ ); + + foreach my $device (@{$devices}) { my $item = 0; my $h = $defs{@{$device}[0]}; @@ -600,6 +658,7 @@ readingsGroup_Notify($$) if( $regex && $regex =~ m/^<(.*)>$/ ) { my $txt = $1; my $readings; + $item++; if( $txt =~ m/^{(.*)}(@([\w|.*]+))?$/ ) { $txt = "{$1}"; $readings = $3; @@ -616,7 +675,14 @@ readingsGroup_Notify($$) } $txt = "" if( !defined($txt) ); - $item++; + if( $txt && $txt =~ m/^%([^%]*)(%(.*))?/ ) { + my $icon = $1; + my $cmd = $3; + + $txt = FW_makeImage( $icon, $icon, "icon" ); + ($txt,undef) = readingsGroup_makeLink($txt,undef,$cmd); + } + CommandTrigger( "", "$name $item.item: $txt" ); } @@ -640,6 +706,7 @@ readingsGroup_Notify($$) } } + my $cmd; my $devStateIcon; if( $valueIcon ) { my $n = $h->{NAME}; @@ -653,13 +720,22 @@ readingsGroup_Notify($$) $devStateIcon = $txt; } else { $devStateIcon = FW_makeImage( $icon, $value, "icon" ); + $cmd = lookup2($commands,$n,$reading,$icon); + $cmd = lookup2($commands,$n,$reading,$value) if( !$cmd ); } } - CommandTrigger( "", "$name $n.$reading: $devStateIcon" ) if( $devStateIcon ); - next if( $devStateIcon ); + if( $devStateIcon ) { + (undef,$devStateIcon) = readingsGroup_makeLink(undef,$devStateIcon,$cmd); + + CommandTrigger( "", "$name $n.$reading: $devStateIcon" ); + next; + } } + $cmd = lookup2($commands,$dev->{NAME},$reading,$value); + ($value,undef) = readingsGroup_makeLink($value,undef,$cmd); + $value = "
$value
" if( $value_style ); CommandTrigger( "", "$name $dev->{NAME}.$reading: $value" ); @@ -759,8 +835,12 @@ readingsGroup_Attr($$$)
  • If regex starts with a '+' it will be matched against the internal values of the device instead of the readings.
  • If regex starts with a '?' it will be matched against the attributes of the device instead of the readings.
  • regex can be of the form <STRING> or <{perl}[@readings]> where STRING or the string returned by perl is - inserted as the reading. skipped if STRING is undef. if STRING is br a new line will be started. if readings is - given the perl expression will be reevaluated during longpoll updates.
  • + inserted as a reading or: + + if readings is given the perl expression will be reevaluated during longpoll updates.
  • For internal values and attributes longpoll update is not possible. Refresh the page to update the values.
  • 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.
  • @@ -793,6 +873,19 @@ readingsGroup_Attr($$$) attr Verbrauch valueFormat {power => "%.1f W", consumption => "%.2f kWh"}
    attr Verbrauch valueIcon { state => '%devStateIcon' }
    attr Verbrauch valueStyle {($READING eq "power" && $VALUE > 150)?'style="color:red"':'style="color:green"'}
    +
    + define rg_battery readingsGroup TYPE=LaCrosse:[Bb]attery
    + attr rg_battery alias Batteriestatus
    + attr rg_battery commands { "battery.low" => "set %DEVICE replaceBatteryForSec 60" }
    + attr rg_battery valueFormat {($VALUE eq 'ok')?"batterie":"batterie\@red"}
    + attr rg_battery valueIcon %VALUE
    +
    + define rgMediaPlayer readingsGroup myMediaPlayer:currentTitle,<>,totaltime,
    ,currentAlbum,<>,currentArtist,
    ,volume,<{if(ReadingsVal($DEVICE,"playStatus","")eq"paused"){"%rc_PLAY%set+$DEVICE+play"}else{"%rc_PAUSE%set+$DEVICE+pause"}}@playStatus>,playStatus
    + attr rgMediaPlayer commands { "playStatus.paused" => "set %DEVICE play", "playStatus.playing" => "set %DEVICE pause" }
    + attr rgMediaPlayer mapping  
    + attr rgMediaPlayer notime 1
    + attr rgMediaPlayer valueFormat { "volume" => "Volume: %i" }
    + #attr rgMediaPlayer valueIcon { "playStatus.paused" => "rc_PLAY", "playStatus.playing" => "rc_PAUSE" }


    @@ -863,7 +956,11 @@ readingsGroup_Attr($$$) in {} that returns a hash that maps reading value to the icon name. e.g.:
    attr devices valueIcon $VALUE
    attr devices valueIcon {state => '%VALUE'}
    - attr devices valueIcon {state => '%devStateIcon'} + attr devices valueIcon {state => '%devStateIcon'} + attr rgMediaPlayer valueIcon { "playStatus.paused" => "rc_PLAY", "playStatus.playing" => "rc_PAUSE" } +
  • commands
    + Makes a reading or icon clickable and specifies the command that should be executed. eg.:
    + attr rgMediaPlayer commands { "playStatus.paused" => "set %DEVICE play", "playStatus.playing" => "set %DEVICE pause" }

  • The nameStyle and valueStyle attributes can also contain a perl expression enclosed in {} that returns the style