SetExtensions.pm 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. ##############################################
  2. # $Id: SetExtensions.pm 17100 2018-08-07 07:40:20Z 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 $eventMap = AttrVal($name, "eventMap", undef);
  53. if((!$hasOn || !$hasOff) && $eventMap) {
  54. if(!$hasOn) {
  55. my (undef,$value) = ReplaceEventMap($name, [$name, "on"], 0);
  56. $hasOn = ($value ne "on");
  57. }
  58. if(!$hasOff) {
  59. my (undef,$value) = ReplaceEventMap($name, [$name, "off"], 0);
  60. $hasOff = ($value ne "off");
  61. }
  62. }
  63. if(!$hasOn || !$hasOff) { # No extension
  64. return "Unknown argument $cmd, choose one of $list";
  65. }
  66. if(!defined($se_list{$cmd})) {
  67. # Add only "new" commands
  68. my @mylist = grep { $list !~ m/\b$_\b/ } keys %se_list;
  69. return "Unknown argument $cmd, choose one of $list " .
  70. join(" ", @mylist);
  71. }
  72. if($se_list{$cmd} && $se_list{$cmd} != int(@a)) {
  73. return "$cmd requires $se_list{$cmd} parameter";
  74. }
  75. SetExtensionsCancel($hash);
  76. my $cmd1 = ($cmd =~ m/^on.*/ ? "on" : "off");
  77. my $cmd2 = ($cmd =~ m/^on.*/ ? "off" : "on");
  78. my $param = $a[0];
  79. if($cmd eq "on-for-timer" || $cmd eq "off-for-timer") {
  80. return "$cmd requires a number as argument" if($param !~ m/^\d*\.?\d*$/);
  81. if($param) {
  82. $hash->{TIMED_OnOff} = {
  83. START=>time(), START_FMT=>TimeNow(), DURATION=>$param,
  84. CMD=>$cmd, NEXTCMD=>$cmd2
  85. };
  86. SE_DoSet($name, $cmd1);
  87. InternalTimer(gettimeofday()+$param,"SetExtensionsFn","SE $name $cmd",0);
  88. }
  89. } elsif($cmd =~ m/^(on|off)-till/) {
  90. my ($err, $hr, $min, $sec, $fn) = GetTimeSpec($param);
  91. return "$cmd: $err" if($err);
  92. my $hms_till = sprintf("%02d:%02d:%02d", $hr, $min, $sec);
  93. if($cmd =~ m/-till$/) {
  94. my @lt = localtime;
  95. my $hms_now = sprintf("%02d:%02d:%02d", $lt[2], $lt[1], $lt[0]);
  96. if($hms_now ge $hms_till) {
  97. Log3 $hash, 4,
  98. "$name $cmd: won't switch as now ($hms_now) is later than $hms_till";
  99. return "";
  100. }
  101. if($hms_till ge "24") { # sunrise, #89985
  102. Log3 $hash, 4, "$name $cmd: won't switch as $hms_till is tomorrow";
  103. return "";
  104. }
  105. }
  106. SE_DoSet($name, $cmd1);
  107. CommandDefine(undef, "${name}_till at $hms_till set $name $cmd2");
  108. } elsif($cmd eq "blink") {
  109. my $p2 = $a[1];
  110. return "$cmd requires 2 numbers as argument"
  111. if($param !~ m/^\d+$/ || $p2 !~ m/^\d*\.?\d*$/);
  112. if($param) {
  113. SE_DoSet($name, $a[2] ? "off" : "on");
  114. $param-- if($a[2]);
  115. if($param) {
  116. $hash->{TIMED_OnOff} = {
  117. START=>time(), START_FMT=>TimeNow(), DURATION=>$param,
  118. CMD=>$cmd, NEXTCMD=>"$cmd $param $p2 ".($a[2] ? "0" : "1")
  119. };
  120. InternalTimer(gettimeofday()+$p2, "SetExtensionsFn","SE $name $cmd",0);
  121. }
  122. }
  123. } elsif($cmd eq "intervals") {
  124. my $intSpec = shift(@a);
  125. if($intSpec) {
  126. my ($from, $till) = split("-", $intSpec);
  127. my ($err, $hr, $min, $sec, $fn) = GetTimeSpec($from);
  128. return "$cmd: $err" if($err);
  129. my @lt = localtime;
  130. my $hms_from = sprintf("%02d:%02d:%02d", $hr, $min, $sec);
  131. my $hms_now = sprintf("%02d:%02d:%02d", $lt[2], $lt[1], $lt[0]);
  132. if($hms_from le $hms_now) { # By slight delays at will schedule tomorrow.
  133. SetExtensions($hash, $list, $name, "on-till", $till);
  134. } else {
  135. CommandDefine(undef,
  136. "${name}_intervalFrom at $from set $name on-till $till");
  137. }
  138. if(@a) {
  139. my $rest = join(" ", @a);
  140. my ($from, $till) = split("-", shift @a);
  141. CommandDefine(undef,
  142. "${name}_intervalNext at $from set $name intervals $rest");
  143. }
  144. }
  145. } elsif($cmd eq "toggle") {
  146. my $value = Value($name);
  147. (undef,$value) = ReplaceEventMap($name, [$name, $value], 0) if($eventMap);
  148. $value = ($1==0 ? "off" : "on") if($value =~ m/dim (\d+)/); # Forum #49391
  149. SE_DoSet($name, $value =~ m/^on/ ? "off" : "on");
  150. }
  151. return undef;
  152. }
  153. sub
  154. SetExtensionsFn($)
  155. {
  156. my (undef, $name, $cmd) = split(" ", shift, 3);
  157. my $hash = $defs{$name};
  158. return if(!$hash || !$hash->{TIMED_OnOff});
  159. my $nextcmd = $hash->{TIMED_OnOff}{NEXTCMD};
  160. delete $hash->{TIMED_OnOff};
  161. SE_DoSet($name, split(" ",$nextcmd));
  162. }
  163. 1;