diff --git a/fhem/CHANGED b/fhem/CHANGED index c71a991a2..170b97769 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,6 +1,8 @@ # Add changes at the top of the list. - SVN + - feature: new module 31_LightScene to save and restore the state of a + group of lights and other actors - feature: VIERA module added (by teevau) - change: FHEMWEB: the first webCmd argument is no longer used by the state-icon, this can be implemented by the new devStateIcon @@ -12,11 +14,11 @@ - feature: added example Setup SQL and configuration for SQLite - change: modified MySQL Setup SQL to use 512 characters in EVENT column - feature: added new Javascript Frontend based on ExtJS (by Johannes) - - feature: new Modules 30_HUEBridge and 31_HUEDevice for phillips hue and + - feature: new modules 30_HUEBridge and 31_HUEDevice for phillips hue and smartlink devices (by justme1968) - change: SYSSTAT: allow remote monitoring by ssh - change: SYSSTAT: allow less frequent updates for diskusage - - feature: new Module 32_SYSSTAT to monitor system load and disk usage + - feature: new module 32_SYSSTAT to monitor system load and disk usage on linux FHEM hosts (by justme1968) - feature: new Module 73_PRESENCE to make automatic presence detection of mobile phones or other mobile devices (like tablets) via ping or diff --git a/fhem/FHEM/31_LightScene.pm b/fhem/FHEM/31_LightScene.pm new file mode 100644 index 000000000..5c480b64d --- /dev/null +++ b/fhem/FHEM/31_LightScene.pm @@ -0,0 +1,307 @@ + +package main; + +use strict; +use warnings; +use POSIX; +use JSON::XS; + +sub LightScene_Initialize($) +{ + my ($hash) = @_; + + $hash->{DefFn} = "LightScene_Define"; + $hash->{NotifyFn} = "LightScene_Notify"; + $hash->{UndefFn} = "LightScene_Undefine"; + $hash->{SetFn} = "LightScene_Set"; + $hash->{GetFn} = "LightScene_Get"; +} + +sub LightScene_Define($$) +{ + my ($hash, $def) = @_; + + my @args = split("[ \t]+", $def); + + return "Usage: define LightScene +" if(@args < 3); + + my $name = shift(@args); + my $tyoe = shift(@args); + + my %list; + foreach my $a (@args) { + foreach my $d (devspec2array($a)) { + $list{$d} = 1; + } + } + $hash->{CONTENT} = \%list; + + my %scenes; + $hash->{SCENES} = \%scenes; + + LightScene_Load($hash); + + $hash->{STATE} = 'Initialized'; + + return undef; +} + +sub LightScene_Undefine($$) +{ + my ($hash,$arg) = @_; + + LightScene_Save(); + + return undef; +} + +sub +LightScene_Notify($$) +{ + my ($hash,$dev) = @_; + my $name = $hash->{NAME}; + my $type = $hash->{TYPE}; + + return if($dev->{NAME} ne "global"); + + if( grep(m/^INITIALIZED$/, @{$dev->{CHANGED}}) ) { + } elsif( grep(m/^SAVE$/, @{$dev->{CHANGED}}) ) { + LightScene_Save(); + } + + return undef; +} + +sub +myStatefileName() +{ + my $statefile = $attr{global}{statefile}; + $statefile = substr $statefile,0,rindex($statefile,'/')+1; + return $statefile ."LightScenes.save"; +} +my $LightScene_LastSaveTime=""; +sub +LightScene_Save() +{ + my $time_now = TimeNow(); + return if( $time_now eq $LightScene_LastSaveTime); + $LightScene_LastSaveTime = $time_now ; + + return "No statefile specified" if(!$attr{global}{statefile}); + my $statefile = myStatefileName(); + + my $hash; + for my $d (keys %defs) { + next if($defs{$d}{TYPE} ne "LightScene"); + next if( !defined($defs{$d}{SCENES}) ); + + $hash->{$d} = $defs{$d}{SCENES} if( keys(%{$defs{$d}{SCENES}}) ); + } + + if(open(FH, ">$statefile")) { + my $t = localtime; + print FH "#$t\n"; + + print FH encode_json($hash) if( defined($hash) ); + + close(FH); + } else { + + my $msg = "LightScene_Save: Cannot open $statefile: $!"; + Log 1, $msg; + } + + return undef; +} +sub +LightScene_Load($) +{ + my ($hash) = @_; + + return "No statefile specified" if(!$attr{global}{statefile}); + my $statefile = myStatefileName(); + + if(open(FH, "<$statefile")) { + my $json; + while (my $line = ) { + chomp $line; + next if($line =~ m/^#.*$/); + $json .= $line; + } + + close(FH); + + return if( !defined($json) ); + + my $decoded = decode_json( $json ); + + if( defined($decoded->{$hash->{NAME}}) ) { + $hash->{SCENES} = $decoded->{$hash->{NAME}}; + } + } else { + my $msg = "LightScene_Load: Cannot open $statefile: $!"; + Log 1, $msg; + } + return undef; +} + + +sub +LightScene_Set($@) +{ + my ($hash, $name, $cmd, $value, @a) = @_; + my $ret = ""; + + if( !defined($cmd) ){ return "$name: set needs at least one parameter" }; + + if( $cmd eq "?" ){ return "Unknown argument ?, choose one of save scene:".join(",", sort keys %{$hash->{SCENES}}) }; + + if( $cmd eq "save" && !defined( $value ) ) { return "Usage: set save " }; + if( $cmd eq "scene" && !defined( $value ) ) { return "Usage: set scene " }; + if( $cmd eq "remove" && !defined( $value ) ) { return "Usage: set remove " }; + + if( $cmd eq "remove" ) { + delete( $hash->{SCENES}{$value} ); + return undef; + }; + + + $hash->{INSET} = 1; + + foreach my $d (sort keys %{ $hash->{CONTENT} }) { + next if(!$defs{$d}); + if($defs{$d}{INSET}) { + Log 1, "ERROR: endless loop detected for $d in " . $hash->{NAME}; + next; + } + + if( $cmd eq "save" ) { + my $status = ""; + if( $defs{$d}{TYPE} eq 'CUL_HM' ) { + my $subtype = AttrVal($d,"subType",""); + if( $subtype eq "switch" ) { + $status = Value($d); + } elsif( $subtype eq "dimmer" ) { + $status = Value($d); + } + } elsif( $defs{$d}{TYPE} eq 'FS20' ) { + $status = Value($d); + } elsif( $defs{$d}{TYPE} eq 'HUEDevice' ) { + my $subtype = AttrVal($d,"subType",""); + if( $subtype eq "switch" || Value($d) eq "off" ) { + $status = Value($d); + } elsif( $subtype eq "dimmer" ) { + $status = "bri ". ReadingsVal($d,'bri',"0"); + } elsif( $subtype eq "colordimmer" ) { + $status = "bri ". ReadingsVal($d,'bri',"0") ." : xy ". ReadingsVal($d,'xy',""); + } + } elsif( $defs{$d}{TYPE} eq 'IT' ) { + my $subtype = AttrVal($d,"model",""); + if( $subtype eq "itswitch" ) { + $status = Value($d); + } elsif( $subtype eq "itdimmer" ) { + $status = Value($d); + } + } elsif( $defs{$d}{TYPE} eq 'TRX_LIGHT' ) { + $status = Value($d); + } else { + $status = Value($d); + } + + $hash->{SCENES}{$value}{$d} = $status; + $ret .= $d .": ". $status ."\n"; + + } elsif ( $cmd eq "scene" ) { + $hash->{STATE} = $value; + $ret .= " ". CommandSet(undef,"$d $hash->{SCENES}{$value}{$d}"); + } else { + $ret = "Unknown argument $cmd, choose one of save scene"; + } + } + + delete($hash->{INSET}); + Log GetLogLevel($hash->{NAME},5), "SET: $ret" if($ret); + + return $ret; + + return undef; +} + +sub +LightScene_Get($@) +{ + my ($hash, @a) = @_; + + my $name = $a[0]; + return "$name: get needs at least one parameter" if(@a < 2); + + my $cmd= $a[1]; + if( $cmd eq "scene" && @a < 3 ) { return "Usage: get scene " }; + + my $ret = ""; + if( $cmd eq "scenes" ) { + foreach my $scene (sort keys %{ $hash->{SCENES} }) { + $ret .= $scene ."\n"; + } + return $ret; + } elsif( $cmd eq "scene" ) { + my $ret = ""; + my $scene = $a[2]; + if( defined($hash->{SCENES}{$scene}) ) { + foreach my $device (sort keys %{ $hash->{SCENES}{$scene} }) { + $ret .= $device .": ". $hash->{SCENES}{$scene}{$device} ."\n"; + } + } else { + $ret = "no scene <$scene> defined"; + } + return $ret; + } + + return "Unknown argument $cmd, choose one of scenes scene"; +} + +1; + +=pod +=begin html + + +

LightScene

+
    + Allows to store the state of a group of lights and recall it later. Multiple states for one group can be stored. + +

    + + Define +
      + define <name> LightScene [<dev1>] [<dev2>] [<dev3>] ...
      +
      + + Examples: +
        + define light_group LightScene Lampe1 Lampe2 Dimmer1
        +
      +

    + + + Set +
      +
    • save <scene_name>
      + save current state for alle devices in this LightScene to <scene_name>
    • +
    • scene <scene_name>
      + shows scene <scene_name> - all devices are switched to the previously saved state
    • +
    • remove <scene_name>
      + remove <scene_name> from list of saved scenes
    • +

    + + + Get +
      +
    • scenes
    • +
    • scene <scene_name>
    • +

    + +

+ +=end html +=cut