diff --git a/fhem/CHANGED b/fhem/CHANGED index c9a0e93df..5a10d61cf 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,7 @@ # Add changes at the top of the list. Keep it in ASCII - SVN + - feature: LightScene: add html overview of all configured scenes in + detail view. allow usage of overview in a weblink. - feature: FLOORPLAN: enhanced detail-screen for floorplans in fhemweb, arrange-mode has delete-button, new command "get config" diff --git a/fhem/FHEM/31_LightScene.pm b/fhem/FHEM/31_LightScene.pm index 0bf4c819f..0725934ab 100644 --- a/fhem/FHEM/31_LightScene.pm +++ b/fhem/FHEM/31_LightScene.pm @@ -6,6 +6,8 @@ use warnings; use POSIX; use JSON; +use vars qw(%FW_webArgs); # all arguments specified in the GET + sub LightScene_Initialize($) { my ($hash) = @_; @@ -15,6 +17,8 @@ sub LightScene_Initialize($) $hash->{UndefFn} = "LightScene_Undefine"; $hash->{SetFn} = "LightScene_Set"; $hash->{GetFn} = "LightScene_Get"; + + $hash->{FW_detailFn} = "LightScene_detailFn"; } sub LightScene_Define($$) @@ -26,7 +30,7 @@ sub LightScene_Define($$) return "Usage: define LightScene +" if(@args < 3); my $name = shift(@args); - my $tyoe = shift(@args); + my $type = shift(@args); my %list; foreach my $a (@args) { @@ -55,6 +59,102 @@ sub LightScene_Undefine($$) return undef; } +sub +LightScene_2html($) +{ + my($hash) = @_; + $hash = $defs{$hash} if( ref($hash) ne 'HASH' ); + + return undef if( !$hash ); + + my $name = $hash->{NAME}; + my $room = $FW_webArgs{room}; + + my $show_heading = 1; + + my $row = 1; + my $ret = ""; + $ret .= ""; + $ret .= "" if( $show_heading ); + $ret .= ""; + $ret .= "
"; + + $ret .= sprintf("", ($row&1)?"odd":"even"); + $row++; + $ret .= ""; + foreach my $d (sort keys %{ $hash->{CONTENT} }) { + $ret .= ""; + } + + if( defined($FW_webArgs{detail}) ) { + $room = "&detail=$FW_webArgs{detail}"; + + $ret .= sprintf("", ($row&1)?"odd":"even"); + $row++; + $ret .= ""; + foreach my $d (sort keys %{ $hash->{CONTENT} }) { + my %extPage = (); + my ($allSets, $cmdlist, $txt) = FW_devState($d, $room, \%extPage); + $ret .= ""; + } + } + + foreach my $scene (sort keys %{ $hash->{SCENES} }) { + $ret .= sprintf("", ($row&1)?"odd":"even"); + $row++; + + my $srf = $room ? "&room=$room" : ""; + $srf = $room if( $room && $room =~ m/^&/ ); + my $link = "cmd=set $name scene $scene"; + my $txt = $scene; + if( AttrVal($FW_wname, "longpoll", 1)) { + $txt = "$txt"; + } else { + $txt = "$txt"; + } + $ret .= ""; + + foreach my $d (sort keys %{ $hash->{CONTENT} }) { + if( !defined($hash->{SCENES}{$scene}{$d} ) ) { + $ret .= ""; + next; + } + + my $icon; + my $state = $hash->{SCENES}{$scene}{$d}; + $icon = $state->{icon} if( ref($state) eq 'HASH' ); + $state = $state->{state} if( ref($state) eq 'HASH' ); + + my ($isHtml); + $isHtml = 0; + + if( !$icon ) { + my ($link); + ($icon, $link, $isHtml) = FW_dev2image($d, $state); + } + $icon = FW_iconName($state) if( !$icon ); + + if( $icon ) { + $ret .= ""; + } else { + $ret .= ""; + } + } + } + + $ret .= "
$txt
$txt
". ($isHtml ? $icon : FW_makeImage($icon, $state)) ."
". $state ."
"; + $ret .= "
"; + + return $ret; +} +sub +LightScene_detailFn() +{ + my ($FW_wname, $d, $room, $pageHash) = @_; # pageHash is set for summaryFn. + + return LightScene_2html($d); +} + sub LightScene_Notify($$) { @@ -69,6 +169,43 @@ LightScene_Notify($$) LightScene_Save(); } + my $max = int(@{$dev->{CHANGED}}); + for (my $i = 0; $i < $max; $i++) { + my $s = $dev->{CHANGED}[$i]; + $s = "" if(!defined($s)); + + if($s =~ m/^RENAMED ([^ ]*) ([^ ]*)$/) { + my ($old, $new) = ($1, $2); + if( defined($hash->{CONTENT}{$old}) ) { + + $hash->{DEF} =~ s/(\s*)$old(\s*)/$1$new$2/; + + foreach my $scene (keys %{ $hash->{SCENES} }) { + $hash->{SCENES}{$scene}{$new} = $hash->{SCENES}{$scene}{$old} if( defined($hash->{SCENES}{$scene}{$old}) ); + delete( $hash->{SCENES}{$scene}{$old} ); + } + + delete( $hash->{CONTENT}{$old} ); + $hash->{CONTENT}{$new} = 1; + } + } elsif($s =~ m/^DELETED ([^ ]*)$/) { + my ($name) = ($1); + + if( defined($hash->{CONTENT}{$name}) ) { + + $hash->{DEF} =~ s/(\s*)$name(\s*)/ /; + $hash->{DEF} =~ s/^ //; + $hash->{DEF} =~ s/ $//; + + foreach my $scene (keys %{ $hash->{SCENES} }) { + delete( $hash->{SCENES}{$scene}{$name} ); + } + + delete( $hash->{CONTENT}{$name} ); + } + } + } + return undef; } @@ -85,7 +222,7 @@ LightScene_Save() { my $time_now = TimeNow(); return if( $time_now eq $LightScene_LastSaveTime); - $LightScene_LastSaveTime = $time_now ; + $LightScene_LastSaveTime = $time_now; return "No statefile specified" if(!$attr{global}{statefile}); my $statefile = myStatefileName(); @@ -154,7 +291,7 @@ LightScene_Set($@) if( !defined($cmd) ){ return "$name: set needs at least one parameter" }; - if( $cmd eq "?" ){ return "Unknown argument ?, choose one of remove save set scene:".join(",", sort keys %{$hash->{SCENES}}) }; + if( $cmd eq "?" ){ return "Unknown argument ?, choose one of remove:".join(",", sort keys %{$hash->{SCENES}}) ." save set scene:".join(",", sort keys %{$hash->{SCENES}})}; if( $cmd eq "save" && !defined( $scene ) ) { return "Usage: set $name save " }; if( $cmd eq "scene" && !defined( $scene ) ) { return "Usage: set $name scene " }; @@ -167,13 +304,11 @@ LightScene_Set($@) my ($d, @args) = @a; if( !defined( $scene ) || !defined( $d ) || !@args ) { return "Usage: set $name set " }; + return "no stored scene >$scene<" if( !defined($hash->{SCENES}{$scene} ) ); + return "device >$d< is not a member of scene >$scene<" if( !defined($hash->{CONTENT}{$d} ) ); - if( defined($hash->{SCENES}{$scene}) - && defined($hash->{SCENES}{$scene}{$d}) ) - { - $hash->{SCENES}{$scene}{$d} = join(" ", @args); - return undef; - } + $hash->{SCENES}{$scene}{$d} = join(" ", @args); + return undef; } @@ -187,54 +322,74 @@ LightScene_Set($@) } if( $cmd eq "save" ) { - my $status = ""; - if( $defs{$d}{TYPE} eq 'CUL_HM' ) { + my $state = ""; + my $type = $defs{$d}->{TYPE}; + $type = "" if( !defined($type) ); + + if( $type eq 'CUL_HM' ) { my $subtype = AttrVal($d,"subType",""); if( $subtype eq "switch" ) { - $status = Value($d); + $state = Value($d); } elsif( $subtype eq "dimmer" ) { - $status = Value($d); + $state = Value($d); } else { - $status = Value($d); + $state = Value($d); } - } elsif( $defs{$d}{TYPE} eq 'FS20' ) { - $status = Value($d); - } elsif( $defs{$d}{TYPE} eq 'HUEDevice' ) { + } elsif( $type eq 'FS20' ) { + $state = Value($d); + } elsif( $type eq 'SWAP_0000002200000003' ) { + $state = Value($d); + $state = "rgb ". $state if( $state ne "off" ); + } elsif( $type eq 'HUEDevice' ) { my $subtype = AttrVal($d,"subType",""); if( $subtype eq "switch" || Value($d) eq "off" ) { - $status = Value($d); + $state = Value($d); } elsif( $subtype eq "dimmer" ) { - $status = "bri ". ReadingsVal($d,'bri',"0"); + $state = "bri ". ReadingsVal($d,'bri',"0"); } elsif( $subtype eq "colordimmer" ) { if( ReadingsVal($d,"colormode","") eq "ct" ) { ReadingsVal($d,"ct","") =~ m/(\d+) .*/; - $status = "bri ". ReadingsVal($d,'bri',"0") ." : ct ". $1; + $state = "bri ". ReadingsVal($d,'bri',"0") ." : ct ". $1; } else { - $status = "bri ". ReadingsVal($d,'bri',"0") ." : xy ". ReadingsVal($d,'xy',""); + $state = "bri ". ReadingsVal($d,'bri',"0") ." : xy ". ReadingsVal($d,'xy',""); } } - } elsif( $defs{$d}{TYPE} eq 'IT' ) { + } elsif( $type eq 'IT' ) { my $subtype = AttrVal($d,"model",""); if( $subtype eq "itswitch" ) { - $status = Value($d); + $state = Value($d); } elsif( $subtype eq "itdimmer" ) { - $status = Value($d); + $state = Value($d); } else { - $status = Value($d); + $state = Value($d); } - } elsif( $defs{$d}{TYPE} eq 'TRX_LIGHT' ) { - $status = Value($d); + } elsif( $type eq 'TRX_LIGHT' ) { + $state = Value($d); } else { - $status = Value($d); + $state = Value($d); } - $hash->{SCENES}{$scene}{$d} = $status; - $ret .= $d .": ". $status ."\n"; + if( $type eq "SWAP_0000002200000003" || $type eq "HUEDevice" ) { + my %desc; + $desc{state} = $state; + my ($icon, $link, $isHtml) = FW_dev2image($d); + $desc{icon} = $icon; + $hash->{SCENES}{$scene}{$d} = \%desc; + } else { + $hash->{SCENES}{$scene}{$d} = $state; + } + + $ret .= $d .": ". $state ."\n" if( defined($FW_webArgs{room}) && $FW_webArgs{room} eq "all" ); #only if telnet } elsif ( $cmd eq "scene" ) { $hash->{STATE} = $scene; + next if( !defined($hash->{SCENES}{$scene}{$d})); + + my $state = $hash->{SCENES}{$scene}{$d}; + $state = $state->{state} if( ref($state) eq 'HASH' ); + $ret .= " " if( $ret ); - $ret .= CommandSet(undef,"$d $hash->{SCENES}{$scene}{$d}"); + $ret .= CommandSet(undef,"$d $state"); } else { $ret = "Unknown argument $cmd, choose one of save scene"; } @@ -260,7 +415,9 @@ LightScene_Get($@) if( $cmd eq "scene" && @a < 3 ) { return "Usage: get scene " }; my $ret = ""; - if( $cmd eq "scenes" ) { + if( $cmd eq "html" ) { + return LightScene_2html($hash); + } elsif( $cmd eq "scenes" ) { foreach my $scene (sort keys %{ $hash->{SCENES} }) { $ret .= $scene ."\n"; } @@ -270,7 +427,12 @@ LightScene_Get($@) my $scene = $a[2]; if( defined($hash->{SCENES}{$scene}) ) { foreach my $d (sort keys %{ $hash->{SCENES}{$scene} }) { - $ret .= $d .": ". $hash->{SCENES}{$scene}{$d} ."\n"; + next if( !defined($hash->{SCENES}{$scene}{$d})); + + my $state = $hash->{SCENES}{$scene}{$d}; + $state = $state->{state} if( ref($state) eq 'HASH' ); + + $ret .= $d .": ". $state ."\n"; } } else { $ret = "no scene <$scene> defined"; @@ -278,7 +440,7 @@ LightScene_Get($@) return $ret; } - return "Unknown argument $cmd, choose one of scenes scene"; + return "Unknown argument $cmd, choose one of html scenes scene"; } 1; @@ -303,9 +465,20 @@ LightScene_Get($@)
    define light_group LightScene Lampe1 Lampe2 Dimmer1
    define kino_group LightScene LampeDecke LampeFernseher Fernseher Verstaerker
    + define Wohnzimmer LightScene Leinwand Beamer TV Leselampe Deckenlampe

+ The device detail view will show an html overview of the current state of all included devices and all + configured scenes with the device states for each. The column heading with the device names is clickable + to go to detail view of this device. The first row that displays the current device state is clickable + and should react like a click on the device icon in a room overview would. this can be used to interactively + configure a new scene and save it with the command menu of the detail view. The first column of the table with + the scene names ic clickable to activate the scene.

+ + A weblink with a scene overview that can be included in any room or a floorplan can be created with: +
    define wlScene weblink htmlCode {LightScene_2html("Scene")}
+ Set
    @@ -326,7 +499,7 @@ LightScene_Get($@)
  • scene <scene_name>

-
+ =end html =cut