91_sequence.pm 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. ##############################################
  2. # $Id: 91_sequence.pm 11984 2016-08-19 12:47:50Z rudolfkoenig $
  3. package main;
  4. use strict;
  5. use warnings;
  6. use Time::HiRes qw(gettimeofday);
  7. #####################################
  8. sub
  9. sequence_Initialize($)
  10. {
  11. my ($hash) = @_;
  12. $hash->{DefFn} = "sequence_Define";
  13. $hash->{UndefFn} = "sequence_Undef";
  14. $hash->{NotifyFn} = "sequence_Notify";
  15. $hash->{AttrList} = "disable:0,1 triggerPartial:1,0 showtime:1,0 reportEvents:1,0";
  16. }
  17. #####################################
  18. # define sq1 sequence reg1 [timeout reg2]
  19. sub
  20. sequence_Define($$)
  21. {
  22. my ($hash, $def) = @_;
  23. my @def = split("[ \t]+", $def);
  24. my $name = shift(@def);
  25. my $type = shift(@def);
  26. return "Usage: define <name> sequence <re1> <timeout1> <re2> ".
  27. "[<timeout2> <re3> ...]"
  28. if(int(@def) % 2 == 0 || int(@def) < 3);
  29. # "Syntax" checking
  30. for(my $i = 0; $i < int(@def); $i += 2) {
  31. my $re = $def[$i];
  32. my $to = $def[$i+1];
  33. eval { "Hallo" =~ m/^$re$/ };
  34. return "Bad regexp 1: $@" if($@);
  35. return "Bad timeout spec $to"
  36. if(defined($to) && $to !~ m/^\d*.?\d$/);
  37. }
  38. $hash->{RE} = $def[0];
  39. $hash->{IDX} = 0;
  40. $hash->{MAX} = int(@def);
  41. $hash->{STATE} = "active";
  42. return undef;
  43. }
  44. #####################################
  45. sub
  46. sequence_Notify($$)
  47. {
  48. my ($hash, $dev) = @_;
  49. my $ln = $hash->{NAME};
  50. return "" if($attr{$ln} && $attr{$ln}{disable});
  51. my $n = $dev->{NAME};
  52. my $re = $hash->{RE};
  53. my $max = int(@{$dev->{CHANGED}});
  54. for (my $i = 0; $i < $max; $i++) {
  55. my $s = $dev->{CHANGED}[$i];
  56. $s = "" if(!defined($s));
  57. next if($n !~ m/^$re$/ && "$n:$s" !~ m/^$re$/);
  58. RemoveInternalTimer($ln);
  59. my $idx = $hash->{IDX} + 2;
  60. Log3 $ln, 5, "sequence $ln matched $idx";
  61. my @d = split("[ \t]+", $hash->{DEF});
  62. $hash->{EVENTS} = "" if(!$hash->{EVENTS});
  63. $hash->{EVENTS} .= " $n:$s";
  64. if($idx > $hash->{MAX}) { # Last element reached
  65. my $tt = "trigger";
  66. $tt .= $hash->{EVENTS} if(AttrVal($ln, "reportEvents", undef));
  67. delete($hash->{EVENTS});
  68. Log3 $ln, 5, "sequence $ln $tt";
  69. setReadingsVal($hash, "state", "active", TimeNow());
  70. DoTrigger($ln, $tt);
  71. $idx = 0;
  72. } else {
  73. $hash->{RE} = $d[$idx];
  74. my $nt = gettimeofday() + $d[$idx-1];
  75. InternalTimer($nt, "sequence_Trigger", $ln, 0);
  76. }
  77. $hash->{IDX} = $idx;
  78. $hash->{RE} = $d[$idx];
  79. last;
  80. }
  81. return "";
  82. }
  83. sub
  84. sequence_Trigger($)
  85. {
  86. my ($ln) = @_;
  87. my $hash = $defs{$ln};
  88. my @d = split("[ \t]+", $hash->{DEF});
  89. $hash->{RE} = $d[0];
  90. my $idx = $hash->{IDX}/2;
  91. $hash->{IDX} = 0;
  92. my $tt = "partial_$idx";
  93. Log3 $ln, 5, "sequence $ln timeout on $idx ($tt)";
  94. $tt .= $hash->{EVENTS} if(AttrVal($ln, "reportEvents", undef));
  95. delete($hash->{EVENTS});
  96. DoTrigger($ln, $tt) if(AttrVal($ln, "triggerPartial", undef));
  97. }
  98. sub
  99. sequence_Undef($$)
  100. {
  101. my ($hash, $name) = @_;
  102. RemoveInternalTimer($name);
  103. return undef;
  104. }
  105. 1;
  106. =pod
  107. =item helper
  108. =item summary generate an event upon reception of a defined sequence of events
  109. =item summary_DE generiert Event nach Empfang einer definierten Event-Sequenz
  110. =begin html
  111. <a name="sequence"></a>
  112. <h3>sequence</h3>
  113. <ul>
  114. <br>
  115. <a name="sequencedefine"></a>
  116. <b>Define</b>
  117. <ul>
  118. <code>define &lt;name&gt; sequence &lt;re1&gt; &lt;timeout1&gt;
  119. &lt;re2&gt; [&lt;timeout2&gt; &lt;re3&gt; ...]</code>
  120. <br><br>
  121. A sequence is used to allow to trigger events for a certain combination of
  122. button presses on a remote. E.g. to switch on a lamp when pressing the
  123. Btn1:on, then Btn2:off and at last Btn1:on one after the other you could
  124. define the following:<br>
  125. <br>
  126. <ul>
  127. <code>
  128. define lampseq sequence Btn1:on 0.5 Btn2:off 0.5 Btn1:on<br>
  129. define lampon notify lampseq:trigger set lamp on
  130. </code>
  131. </ul>
  132. </ul>
  133. <br>
  134. <a name="sequenceset"></a>
  135. <b>Set</b> <ul>N/A</ul><br>
  136. <a name="sequenceget"></a>
  137. <b>Get</b> <ul>N/A</ul><br>
  138. <a name="sequenceattr"></a>
  139. <b>Attributes</b>
  140. <ul>
  141. <li><a href="#disable">disable</a></li>
  142. <li><a href="#showtime">showtime</a></li>
  143. <li><a href="#triggerPartial">triggerPartial</a><br>
  144. if set (to 1), and not all the events of a sequence are received, then a
  145. partial_X event is generated by the sequence. Example:<br><code><ul>
  146. fhem> define seq sequence d1:on 1 d1:on 1 d1:on<br>
  147. fhem> attr seq triggerPartial<br>
  148. fhem> set d1 on;; sleep 0.5;; set d1 on<br>
  149. </ul></code>
  150. generates the event seq partial_2. This can be used to assign different
  151. tasks for a single button, depending on the number of times it is
  152. pressed.
  153. </li><br>
  154. <li><a href="#reportEvents">reportEvents</a><br>
  155. if set (to 1), report the events (space separated) after the
  156. "trigger" or "partial_X" keyword. This way one can create more general
  157. sequences, and create different notifies to react:<br>
  158. <ul><code>
  159. define seq sequence remote:btn.* remote:btn.*<br>
  160. attr seq reportEvents<br>
  161. define n_b1b2 notify seq:trigger.remote:btn1.remote:btn2 set lamp1 on<br>
  162. define n_b2b1 notify seq:trigger.remote:btn2.remote:btn1 set lamp1 off<br>
  163. </code></ul>
  164. </li>
  165. </ul>
  166. <br>
  167. </ul>
  168. =end html
  169. =begin html_DE
  170. <a name="sequence"></a>
  171. <h3>sequence</h3>
  172. <ul>
  173. <br>
  174. <a name="sequencedefine"></a>
  175. <b>Define</b>
  176. <ul>
  177. <code>define &lt;name&gt; sequence &lt;re1&gt; &lt;timeout1&gt;
  178. &lt;re2&gt; [&lt;timeout2&gt; &lt;re3&gt; ...]</code>
  179. <br><br>
  180. Ein sequence kann verwendet werden, um ein neues Event zu generieren, wenn
  181. eine bestimmte Folge von anderen Events in einem festgelegten Zeitraum
  182. eingetroffen ist. Z.Bsp. um eine Lampe dann einzuschalten, falls Btn1:on,
  183. dann Btn2:off und zum Schluss Btn3:on innerhalb einer Sekunde gedr&uuml;ckt
  184. wurde, definiert man folgendes:<br>
  185. <ul>
  186. <code>
  187. define lampseq sequence Btn1:on 0.5 Btn2:off 0.5 Btn1:on<br>
  188. define lampon notify lampseq:trigger set lamp on
  189. </code>
  190. </ul>
  191. </ul>
  192. <br>
  193. <a name="sequenceset"></a>
  194. <b>Set</b> <ul>N/A</ul><br>
  195. <a name="sequenceget"></a>
  196. <b>Get</b> <ul>N/A</ul><br>
  197. <a name="sequenceattr"></a>
  198. <b>Attributes</b>
  199. <ul>
  200. <li><a href="#disable">disable</a></li>
  201. <li><a href="#triggerPartial">triggerPartial</a><br>
  202. Falls gesetzt (auf 1), und nicht alle erwarteten Events eingetroffen
  203. sind, dann wird ein partial_X Event generiert, wobei X durch Anzahl der
  204. eingetroffenen Events ersetzt wird. Beispiel:<br><code><ul>
  205. fhem> define seq sequence d1:on 1 d1:on 1 d1:on<br>
  206. fhem> attr seq triggerPartial<br>
  207. fhem> set d1 on;; sleep 0.5;; set d1 on<br>
  208. </ul></code>
  209. erzeugt das Event "seq partial_2". Dies kann verwendet werden, um z.Bsp.
  210. einer Taste unterschiedliche Aufgaben zuzuweisen, jenachdem wie oft sie
  211. gedr&uuml;ckt wurde.
  212. </li>
  213. </ul>
  214. <br>
  215. <a name="sequenceattr"></a>
  216. <b>Attributes</b>
  217. <ul>
  218. <li><a href="#disable">disable</a></li>
  219. <li><a href="#showtime">showtime</a></li>
  220. <li><a href="#triggerPartial">triggerPartial</a><br>
  221. Falls gesetzt (auf 1), und nicht alle erwarteten Events eingetroffen
  222. sind, dann wird ein partial_X Event generiert, wobei X durch Anzahl der
  223. eingetroffenen Events ersetzt wird. Beispiel:<br><code><ul>
  224. fhem> define seq sequence d1:on 1 d1:on 1 d1:on<br>
  225. fhem> attr seq triggerPartial<br>
  226. fhem> set d1 on;; sleep 0.5;; set d1 on<br>
  227. </ul></code>
  228. erzeugt das Event "seq partial_2". Dies kann verwendet werden, um z.Bsp.
  229. einer Taste unterschiedliche Aufgaben zuzuweisen, jenachdem wie oft sie
  230. gedr&uuml;ckt wurde.
  231. </li><br>
  232. <li><a href="#reportEvents">reportEvents</a><br>
  233. Falls gesetzt (auf 1), meldet trigger die empfangenen Events (Leerzeichen
  234. getrennt) nach dem "trigger" oder "partial_X" Schl&uuml;sselwort.
  235. Das kann verwendet werden, um generische sequence Instanzen zu definieren:
  236. <br>
  237. <ul><code>
  238. define seq sequence remote:btn.* remote:btn.*<br>
  239. attr seq reportEvents<br>
  240. define n_b1b2 notify seq:trigger.remote:btn1.remote:btn2 set lamp1 on<br>
  241. define n_b2b1 notify seq:trigger.remote:btn2.remote:btn1 set lamp1 off<br>
  242. </code></ul>
  243. </li>
  244. </ul>
  245. <br>
  246. </ul>
  247. =end html_DE
  248. =cut