SetExtensions.pm 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. ##############################################
  2. # $Id: SetExtensions.pm 12935 2017-01-02 19:51:46Z rudolfkoenig $
  3. package main;
  4. use strict;
  5. use warnings;
  6. sub SetExtensions($$@);
  7. sub SetExtensionsFn($);
  8. sub
  9. SetExtensionsCancel($)
  10. {
  11. my ($hash) = @_;
  12. $hash = $defs{$hash} if( ref($hash) ne 'HASH' );
  13. return undef if( !$hash );
  14. return undef if( $hash->{InSetExtensions} );
  15. my $name = $hash->{NAME};
  16. if($hash->{TIMED_OnOff}) { # on-for-timer, blink
  17. my $cmd = $hash->{TIMED_OnOff}{CMD};
  18. RemoveInternalTimer("SE $name $cmd");
  19. delete $hash->{TIMED_OnOff};
  20. }
  21. for my $sfx ("_till", "_intervalFrom", "_intervalNext") {
  22. CommandDelete(undef, $name.$sfx) if($defs{$name.$sfx});
  23. }
  24. return undef;
  25. }
  26. sub
  27. SE_DoSet(@)
  28. {
  29. my $hash = $defs{$_[0]};
  30. $hash->{InSetExtensions} = 1;
  31. AnalyzeCommand($hash->{CL}, "set ".join(" ", @_)); # cmdalias (Forum #63896)
  32. delete $hash->{InSetExtensions};
  33. }
  34. sub
  35. SetExtensions($$@)
  36. {
  37. my ($hash, $list, $name, $cmd, @a) = @_;
  38. return "Unknown argument $cmd, choose one of " if(!$list);
  39. my %se_list = (
  40. "on-for-timer" => 1,
  41. "off-for-timer" => 1,
  42. "on-till" => 1,
  43. "off-till" => 1,
  44. "on-till-overnight" => 1,
  45. "off-till-overnight"=> 1,
  46. "blink" => 0,
  47. "intervals" => 0,
  48. "toggle" => 0
  49. );
  50. my $hasOn = ($list =~ m/(^| )on\b/);
  51. my $hasOff = ($list =~ m/(^| )off\b/);
  52. my $value = Value($name);
  53. my $em = AttrVal($name, "eventMap", undef);
  54. if($em) {
  55. if(!$hasOn || !$hasOff) {
  56. $hasOn = ($em =~ m/:on\b/) if(!$hasOn);
  57. $hasOff = ($em =~ m/:off\b/) if(!$hasOff);
  58. }
  59. # Following is fix for P#1: /B0:on/on-for-timer 300:5Min/
  60. # $cmd = ReplaceEventMap($name, $cmd, 1) if($cmd ne "?");
  61. # Has problem with P#2 (Forum #28855): /on-for-timer 300:5Min/on:Ein/
  62. # Workaround for P#1 /on-for-timer 300:5Min/on-for-timer:on-for-timer/B0:on/
  63. (undef,$value) = ReplaceEventMap($name, [$name, $value], 0) if($cmd ne "?");
  64. }
  65. if(!$hasOn || !$hasOff) { # No extension
  66. return "Unknown argument $cmd, choose one of $list";
  67. }
  68. if(!defined($se_list{$cmd})) {
  69. # Add only "new" commands
  70. my @mylist = grep { $list !~ m/\b$_\b/ } keys %se_list;
  71. return "Unknown argument $cmd, choose one of $list " .
  72. join(" ", @mylist);
  73. }
  74. if($se_list{$cmd} && $se_list{$cmd} != int(@a)) {
  75. return "$cmd requires $se_list{$cmd} parameter";
  76. }
  77. SetExtensionsCancel($hash);
  78. my $cmd1 = ($cmd =~ m/^on.*/ ? "on" : "off");
  79. my $cmd2 = ($cmd =~ m/^on.*/ ? "off" : "on");
  80. my $param = $a[0];
  81. if($cmd eq "on-for-timer" || $cmd eq "off-for-timer") {
  82. return "$cmd requires a number as argument" if($param !~ m/^\d*\.?\d*$/);
  83. if($param) {
  84. $hash->{TIMED_OnOff} = {
  85. START=>time(), START_FMT=>TimeNow(), DURATION=>$param,
  86. CMD=>$cmd, NEXTCMD=>$cmd2
  87. };
  88. SE_DoSet($name, $cmd1);
  89. InternalTimer(gettimeofday()+$param,"SetExtensionsFn","SE $name $cmd",0);
  90. }
  91. } elsif($cmd =~ m/^(on|off)-till/) {
  92. my ($err, $hr, $min, $sec, $fn) = GetTimeSpec($param);
  93. return "$cmd: $err" if($err);
  94. my $hms_till = sprintf("%02d:%02d:%02d", $hr, $min, $sec);
  95. if($cmd =~ m/-till$/) {
  96. my @lt = localtime;
  97. my $hms_now = sprintf("%02d:%02d:%02d", $lt[2], $lt[1], $lt[0]);
  98. if($hms_now ge $hms_till) {
  99. Log3 $hash, 4,
  100. "$cmd: won't switch as now ($hms_now) is later than $hms_till";
  101. return "";
  102. }
  103. }
  104. SE_DoSet($name, $cmd1);
  105. CommandDefine(undef, "${name}_till at $hms_till set $name $cmd2");
  106. } elsif($cmd eq "blink") {
  107. my $p2 = $a[1];
  108. return "$cmd requires 2 numbers as argument"
  109. if($param !~ m/^\d+$/ || $p2 !~ m/^\d*\.?\d*$/);
  110. if($param) {
  111. SE_DoSet($name, $a[2] ? "off" : "on");
  112. $param-- if($a[2]);
  113. if($param) {
  114. $hash->{TIMED_OnOff} = {
  115. START=>time(), START_FMT=>TimeNow(), DURATION=>$param,
  116. CMD=>$cmd, NEXTCMD=>"$cmd $param $p2 ".($a[2] ? "0" : "1")
  117. };
  118. InternalTimer(gettimeofday()+$p2, "SetExtensionsFn","SE $name $cmd",0);
  119. }
  120. }
  121. } elsif($cmd eq "intervals") {
  122. my $intSpec = shift(@a);
  123. if($intSpec) {
  124. my ($from, $till) = split("-", $intSpec);
  125. my ($err, $hr, $min, $sec, $fn) = GetTimeSpec($from);
  126. return "$cmd: $err" if($err);
  127. my @lt = localtime;
  128. my $hms_from = sprintf("%02d:%02d:%02d", $hr, $min, $sec);
  129. my $hms_now = sprintf("%02d:%02d:%02d", $lt[2], $lt[1], $lt[0]);
  130. if($hms_from le $hms_now) { # By slight delays at will schedule tomorrow.
  131. SetExtensions($hash, $list, $name, "on-till", $till);
  132. } else {
  133. CommandDefine(undef,
  134. "${name}_intervalFrom at $from set $name on-till $till");
  135. }
  136. if(@a) {
  137. my $rest = join(" ", @a);
  138. my ($from, $till) = split("-", shift @a);
  139. CommandDefine(undef,
  140. "${name}_intervalNext at $from set $name intervals $rest");
  141. }
  142. }
  143. } elsif($cmd eq "toggle") {
  144. $value = ($1==0 ? "off" : "on") if($value =~ m/dim (\d+)/); # Forum #49391
  145. SE_DoSet($name, $value =~ m/^on/ ? "off" : "on");
  146. }
  147. return undef;
  148. }
  149. sub
  150. SetExtensionsFn($)
  151. {
  152. my (undef, $name, $cmd) = split(" ", shift, 3);
  153. my $hash = $defs{$name};
  154. return if(!$hash || !$hash->{TIMED_OnOff});
  155. my $nextcmd = $hash->{TIMED_OnOff}{NEXTCMD};
  156. delete $hash->{TIMED_OnOff};
  157. SE_DoSet($name, split(" ",$nextcmd));
  158. }
  159. 1;