SetExtensions.pm 5.1 KB

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