95_Alarm.pm 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192
  1. ########################################################################################
  2. #
  3. # Alarm.pm
  4. #
  5. # FHEM module to set up a house alarm system with 8 different alarm levels
  6. #
  7. # Prof. Dr. Peter A. Henning
  8. #
  9. # $Id: 95_Alarm.pm 15339 2017-10-29 08:14:07Z phenning $
  10. #
  11. ########################################################################################
  12. #
  13. # This programm is free software; you can redistribute it and/or modify
  14. # it under the terms of the GNU General Public License as published by
  15. # the Free Software Foundation; either version 2 of the License, or
  16. # (at your option) any later version.
  17. #
  18. # The GNU General Public License can be found at
  19. # http://www.gnu.org/copyleft/gpl.html.
  20. # A copy is found in the textfile GPL.txt and important notices to the license
  21. # from the author is found in LICENSE.txt distributed with these scripts.
  22. #
  23. # This script is distributed in the hope that it will be useful,
  24. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  25. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  26. # GNU General Public License for more details.
  27. #
  28. ########################################################################################
  29. package main;
  30. use strict;
  31. use warnings;
  32. use vars qw(%defs); # FHEM device/button definitions
  33. use vars qw(%intAt); # FHEM at definitions
  34. use vars qw($FW_ME);
  35. #########################
  36. # Global variables
  37. my $alarmlinkname = "Alarms"; # link text
  38. my $alarmhiddenroom = "AlarmRoom"; # hidden room
  39. my $alarmpublicroom = "Alarm"; # public room
  40. my $alarmno = 8;
  41. my $alarmversion = "3.0";
  42. my %alarm_transtable_EN = (
  43. "ok" => "OK",
  44. "notok" => "Not OK",
  45. "start" => "Start",
  46. "end" => "End",
  47. "status" => "Status",
  48. "notstarted" => "Not started",
  49. "next" => "Next",
  50. "arm" => "Arm",
  51. "disarm" => "Disarm",
  52. "armbutton" => "Arming",
  53. "disarmbutton" => "Disarming",
  54. "cancelbutton" => "Canceling",
  55. "raise" => "Raise",
  56. "wait" => "Wait",
  57. "delay" => "Delay",
  58. "cancel" => "Cancel",
  59. "button" => "Button",
  60. "level" => "Level",
  61. "message" => "Message",
  62. "messagepart" => "Message Part",
  63. "notify" => "Notify",
  64. "notifyto" => "Notify to",
  65. "notifyby" => "Notify by",
  66. "setby" => "Set by",
  67. "regexp" => "RegExp",
  68. "time" => "Time",
  69. "description" => "Description",
  70. "settings" => "Settings",
  71. "sensors" => "Sensors",
  72. "actors" => "Actors",
  73. "action" => "Action",
  74. "setaction" => "Set Action",
  75. "unsetaction" => "Unset Action",
  76. "testaction" => "Test",
  77. "armaction" => "Arm Action",
  78. "disarmaction" => "Disarm Action",
  79. "waitaction" => "Wait Action",
  80. "cancelaction" => "Cancel Action",
  81. "alarm" => "Alarm",
  82. "alarms" => "Alarm System",
  83. #--
  84. "state" => "Security",
  85. "unlocked" => "Unlocked",
  86. "locked" => "Locked",
  87. "unsecured" => "Not Secured",
  88. "secured" => "Secured",
  89. "protected" => "Geschützt",
  90. "guarded" => "Guarded"
  91. );
  92. my %alarm_transtable_DE = (
  93. "ok" => "OK",
  94. "notok" => "Nicht OK",
  95. "start" => "Start",
  96. "end" => "Ende",
  97. "status" => "Status",
  98. "notstarted" => "Nicht gestartet",
  99. "next" => "Nächste",
  100. "arm" => "Schärfen",
  101. "disarm" => "Entschärfen",
  102. "armbutton" => "Schärfen",
  103. "disarmbutton" => "Entschärfen",
  104. "cancelbutton" => "Widerrufen",
  105. "raise" => "Auslösen",
  106. "wait" => "Warte",
  107. "delay" => "Verzögerung",
  108. "cancel" => "Widerruf",
  109. "button" => "Button",
  110. "level" => "Level",
  111. "message" => "Nachricht",
  112. "messagepart" => "Nachrichtenteil",
  113. "notify" => "Auslösung",
  114. "notifyto" => "Wirkt auf",
  115. "notifyby" => "Auslösung durch",
  116. "setby" => "Gesetzt durch",
  117. "regexp" => "RegExp",
  118. "time" => "Zeit",
  119. "description" => "Beschreibung",
  120. "settings" => "Einstellungen",
  121. "sensors" => "Sensoren",
  122. "actors" => "Aktoren",
  123. "action" => "Wirkung",
  124. "setaction" => "Aktion Setzen",
  125. "unsetaction" => "Aktion Rücksetzen",
  126. "testaction" => "Testen",
  127. "armaction" => "Scharf-Aktion",
  128. "disarmaction" => "Unscharf-Aktion",
  129. "waitaction" => "Warte-Aktion",
  130. "cancelaction" => "Widerruf-Aktion",
  131. "alarm" => "Alarm",
  132. "alarms" => "Alarmanlage",
  133. #--
  134. "state" => "Sicherheit",
  135. "unlocked" => "Unverschlossen",
  136. "locked" => "Verschlossen",
  137. "unsecured" => "Nicht Gesichert",
  138. "secured" => "Gesichert",
  139. "protected" => "Geschützt",
  140. "guarded" => "Überwacht"
  141. );
  142. my $alarm_tt;
  143. #########################################################################################
  144. #
  145. # Alarm_Initialize
  146. #
  147. # Parameter hash = hash of device addressed
  148. #
  149. #########################################################################################
  150. sub Alarm_Initialize ($) {
  151. my ($hash) = @_;
  152. $hash->{DefFn} = "Alarm_Define";
  153. $hash->{SetFn} = "Alarm_Set";
  154. $hash->{GetFn} = "Alarm_Get";
  155. $hash->{UndefFn} = "Alarm_Undef";
  156. #$hash->{AttrFn} = "Alarm_Attr";
  157. my $attst = "lockstate:locked,unlocked testbutton:0,1 statedisplay:simple,color,table,none armdelay armwait armact disarmact cancelact";
  158. for( my $level=0;$level<$alarmno;$level++ ){
  159. $attst .=" level".$level."start level".$level."end level".$level."msg level".$level."xec:0,1 level".$level."onact level".$level."offact ";
  160. }
  161. $hash->{AttrList} = $attst;
  162. if( !defined($alarm_tt) ){
  163. #-- in any attribute redefinition readjust language
  164. my $lang = AttrVal("global","language","EN");
  165. if( $lang eq "DE"){
  166. $alarm_tt = \%alarm_transtable_DE;
  167. }else{
  168. $alarm_tt = \%alarm_transtable_EN;
  169. }
  170. }
  171. $alarmlinkname = $alarm_tt->{"alarms"};
  172. $data{FWEXT}{Alarmx}{LINK} = "?room=".$alarmhiddenroom;
  173. $data{FWEXT}{Alarmx}{NAME} = $alarmlinkname;
  174. return undef;
  175. }
  176. #########################################################################################
  177. #
  178. # Alarm_Define - Implements DefFn function
  179. #
  180. # Parameter hash = hash of device addressed, def = definition string
  181. #
  182. #########################################################################################
  183. sub Alarm_Define ($$) {
  184. my ($hash, $def) = @_;
  185. my $now = time();
  186. my $name = $hash->{NAME};
  187. $hash->{VERSION} = $alarmversion;
  188. #-- readjust language
  189. my $lang = AttrVal("global","language","EN");
  190. if( $lang eq "DE"){
  191. $alarm_tt = \%alarm_transtable_DE;
  192. }else{
  193. $alarm_tt = \%alarm_transtable_EN;
  194. }
  195. readingsSingleUpdate( $hash, "state", "Initialized", 1 );
  196. $alarmhiddenroom = defined($attr{$name}{"hiddenroom"}) ? $attr{$name}{"hiddenroom"} : $alarmhiddenroom;
  197. $alarmpublicroom = defined($attr{$name}{"publicroom"}) ? $attr{$name}{"publicroom"} : $alarmpublicroom;
  198. $data{FWEXT}{Alarmx}{LINK} = "?room=".$alarmhiddenroom;
  199. $data{FWEXT}{Alarmx}{NAME} = $alarmlinkname;
  200. $attr{$name}{"room"} = $alarmhiddenroom;
  201. $modules{Alarm}{defptr}{$name} = $hash;
  202. RemoveInternalTimer($hash);
  203. InternalTimer ($now + 5, 'Alarm_CreateEntry', $hash, 0);
  204. return;
  205. }
  206. #########################################################################################
  207. #
  208. # Alarm_Undef - Implements Undef function
  209. #
  210. # Parameter hash = hash of device addressed, def = definition string
  211. #
  212. #########################################################################################
  213. sub Alarm_Undef ($$) {
  214. my ($hash,$arg) = @_;
  215. my $name = $hash->{NAME};
  216. RemoveInternalTimer($hash);
  217. delete $data{FWEXT}{Alarmx};
  218. if (defined $defs{$name."_weblink"}) {
  219. FW_fC("delete ".$name."_weblink");
  220. Log3 $hash, 3, "[".$name. " V".$alarmversion."]"." Weblink ".$name."_weblink deleted";
  221. }
  222. return undef;
  223. }
  224. #########################################################################################
  225. #
  226. # Alarm_Attr - Implements Attr function
  227. #
  228. # Parameter hash = hash of device addressed, ???
  229. #
  230. #########################################################################################
  231. sub Alarm_Attr($$$) {
  232. my ($cmd, $name, $attrName, $attrVal) = @_;
  233. my $hash = $defs{"$name"};
  234. #-- in any attribute redefinition readjust language
  235. my $lang = AttrVal("global","language","EN");
  236. if( $lang eq "DE"){
  237. $alarm_tt = \%alarm_transtable_DE;
  238. }else{
  239. $alarm_tt = \%alarm_transtable_EN;
  240. }
  241. return;
  242. }
  243. #########################################################################################
  244. #
  245. # Alarm_CreateEntry - Puts the Alarm entry into the FHEM menu
  246. #
  247. # Parameter hash = hash of device addressed
  248. #
  249. #########################################################################################
  250. sub Alarm_CreateEntry($) {
  251. my ($hash) = @_;
  252. my $name = $hash->{NAME};
  253. if (!defined $defs{$name."_weblink"}) {
  254. FW_fC("define ".$name."_weblink weblink htmlCode {Alarm_Html(\"".$name."\")}");
  255. Log3 $hash, 3, "[".$name. " V".$alarmversion."]"." Weblink ".$name."_weblink created";
  256. }
  257. FW_fC("attr ".$name."_weblink room ".$alarmhiddenroom);
  258. foreach my $dn (sort keys %defs) {
  259. if ($defs{$dn}{TYPE} eq "FHEMWEB" && $defs{$dn}{NAME} !~ /FHEMWEB:/) {
  260. my $hr = AttrVal($defs{$dn}{NAME}, "hiddenroom", "");
  261. if (index($hr,$alarmhiddenroom) == -1){
  262. if ($hr eq "") {
  263. FW_fC("attr ".$defs{$dn}{NAME}." hiddenroom ".$alarmhiddenroom);
  264. }else {
  265. FW_fC("attr ".$defs{$dn}{NAME}." hiddenroom ".$hr.",".$alarmhiddenroom);
  266. }
  267. Log3 $hash, 3, "[".$name. " V".$alarmversion."]"." Added hidden room '".$alarmhiddenroom."' to ".$defs{$dn}{NAME};
  268. }
  269. }
  270. }
  271. #-- recover state from stored readings
  272. for( my $level=0;$level<$alarmno;$level++ ){
  273. my $val = ReadingsVal($name,"level".$level,"");
  274. if( $val eq "disarmed" ){#
  275. CommandAttr (undef,$name.' level'.$level.'xec disarmed');
  276. }elsif( $val eq "armed" ){
  277. CommandAttr (undef,$name.' level'.$level.'xec armed');
  278. }else{
  279. Log3 $hash,1,"[Alarm $level] has undefined save data $val, disarming";
  280. CommandAttr (undef,$name.' level'.$level.'xec disarmed');
  281. }
  282. }
  283. my $mga = Alarm_getstate($hash)." Keine Störung";
  284. readingsSingleUpdate( $hash, "state", $mga, 1 );
  285. }
  286. #########################################################################################
  287. #
  288. # Alarm_Set - Implements the Set function
  289. #
  290. # Parameter hash = hash of device addressed
  291. #
  292. #########################################################################################
  293. sub Alarm_Set($@) {
  294. my ( $hash, $name, $cmd, @args ) = @_;
  295. if ( $cmd =~ /^(cancel|arm|disarm)(ed)?$/ ) {
  296. return "[Alarm] Invalid argument to set $cmd, must be numeric"
  297. if ( $args[0] !~ /\d+/ );
  298. return "[Alarm] Invalid argument to set $cmd, must be 0 < arg < $alarmno"
  299. if ( ($args[0] >= $alarmno)||($args[0]<0) );
  300. if( $cmd =~ /^cancel(ed)?$/ ){
  301. Alarm_Exec($name,$args[0],"web","button","cancel");
  302. }elsif ( $cmd =~ /^arm(ed)?$/ ) {
  303. Alarm_Arm($name,$args[0],"web","button","arm");
  304. }elsif ( $cmd =~ /^disarm(ed)?$/ ){
  305. Alarm_Arm($name,$args[0],"web","button","disarm");
  306. }else{
  307. return "[Alarm] Invalid argument set $cmd";
  308. }
  309. return;
  310. } elsif ( $cmd =~ /^lock(ed)?$/ ) {
  311. readingsSingleUpdate( $hash, "lockstate", "locked", 0 );
  312. return;
  313. } elsif ( $cmd =~ /^unlock(ed)?$/ ) {
  314. readingsSingleUpdate( $hash, "lockstate", "unlocked", 0 );
  315. return;
  316. } else {
  317. my $str = join(",",(0..($alarmno-1)));
  318. return "[Alarm] Unknown argument " . $cmd . ", choose one of canceled:$str armed:$str disarmed:$str locked:noArg unlocked:noArg";
  319. }
  320. }
  321. #########################################################################################
  322. #
  323. # Alarm_Set - Implements the Get function
  324. #
  325. # Parameter hash = hash of device addressed
  326. #
  327. #########################################################################################
  328. sub Alarm_Get($@) {
  329. my ($hash, @a) = @_;
  330. my $res = "";
  331. my $arg = (defined($a[1]) ? $a[1] : "");
  332. if ($arg eq "version") {
  333. return "Alarm.version => $alarmversion";
  334. } else {
  335. return "Unknown argument $arg choose one of version";
  336. }
  337. }
  338. #########################################################################################
  339. #
  340. # Alarm_getstate - Helper function to assemble a state display
  341. #
  342. # Parameter hash = hash of device addressed
  343. #
  344. #########################################################################################
  345. sub Alarm_getstate($) {
  346. my ($hash) = @_;
  347. my $res = '';
  348. my $type = AttrVal($hash->{NAME},"statedisplay",0);
  349. my $val;
  350. #--------------------------
  351. if( $type eq "simple" ){
  352. for( my $level=0;$level<$alarmno;$level++ ){
  353. $val = $hash->{READINGS}{"level".$level}{VAL};
  354. if( $val eq "disarmed" ){
  355. $res .= '-';
  356. }elsif( $val eq "armed" ){
  357. $res .= 'O';
  358. }else{
  359. $res .= 'X';
  360. }
  361. }
  362. #--------------------------
  363. }elsif( $type eq "color" ){
  364. $res = '<span style="color:lightgray">';
  365. for( my $level=0;$level<$alarmno;$level++ ){
  366. $val = $hash->{READINGS}{"level".$level}{VAL};
  367. if( $val eq "disarmed" ){
  368. $res .= ' '.$level;
  369. }elsif( $val eq "armed" ){
  370. $res .= ' <span style="width:1ex;font-weight:bold;color:green">'.$level.'</span>';
  371. }else{
  372. $res .= ' <span style="width:1ex;font-weight:bold;color:red">'.$level.'</span>';
  373. }
  374. }
  375. $res.='</span>';
  376. #--------------------------
  377. }elsif( $type eq "table" ){
  378. $res = '<table><tr style="height:1ex">';
  379. for( my $level=0;$level<$alarmno;$level++ ){
  380. $val = $hash->{READINGS}{"level".$level}{VAL};
  381. if( $val eq "disarmed" ){
  382. $res .= '<td style="width:1ex;background-color:lightgray"/>';
  383. }elsif( $val eq "armed" ){
  384. $res .= '<td style="width:1ex;background-color:green"/>';
  385. }else{
  386. $res .= '<td style="width:1ex;background-color:red"/>';
  387. }
  388. }
  389. $res.='</tr></table>';
  390. #--------------------------
  391. }
  392. return $res;
  393. }
  394. #########################################################################################
  395. #
  396. # Alarm_getsettings - Helper function to assemble the alarm settings for a device
  397. #
  398. # Parameter hash = hash of Alarm device
  399. # dev = name of device addressed
  400. #
  401. #########################################################################################
  402. sub Alarm_getsettings($$$){
  403. my ($hash,$dev,$type) = @_;
  404. my $chg = 0;
  405. my @aval = split('\|',AttrVal($dev, "alarmSettings","|||0:00"));
  406. if( $type eq "Actor"){
  407. #-- check delay time
  408. if( !defined($aval[3]) || $aval[3] eq "" ){
  409. $aval[3] = "";
  410. $chg = 1;
  411. }elsif( $aval[3] =~ /^\d+$/ ){
  412. $aval[3] = sprintf("00:%02d",$aval[3]);
  413. $chg = 1;
  414. }elsif( $aval[3] !~ /^(\d\d:)?\d\d:\d\d/ ){
  415. Log3 $hash, 1, "[Alarm] Delay time $aval[3] ill defined for alarmActor $dev";
  416. $aval[3] = "";
  417. $chg = 1;
  418. }
  419. #--
  420. if( int(@aval) != 4){
  421. Log3 $hash, 1, "[Alarm] Settings incomplete for alarmActor $dev";
  422. }
  423. }
  424. if( $chg==1 ){
  425. CommandAttr(undef,$dev.'alarmSettings '.join('|',@aval));
  426. }
  427. return @aval;
  428. }
  429. #########################################################################################
  430. #
  431. # Alarm_Test - Test an actor
  432. #
  433. # Parameter name = name of the Alarm definition
  434. # cmd =
  435. #
  436. #########################################################################################
  437. sub Alarm_Test($$){
  438. my ($name,$cmd) = @_;
  439. my $hash = $defs{$name};
  440. $cmd =~ s/\$NAME/Gerät/g;
  441. $cmd =~ s/\$EVENT/Event/g;
  442. $cmd =~ s/\$SHORT/Kurznachricht/g;
  443. #for( my $i=1;$i<= int(@evtpart);$i++){
  444. # $cmd =~ s/\$EVTPART$i/$evtpart[$i-1]/g;
  445. #}
  446. fhem($cmd);
  447. }
  448. #########################################################################################
  449. #
  450. # Alarm_Exec - Execute the Alarm
  451. #
  452. # Parameter name = name of the Alarm definition
  453. # level = Alarm level
  454. # dev = name of the device calling the alarm
  455. # evt = event calling the alarm
  456. # act = action - "on" or "off"
  457. #
  458. #########################################################################################
  459. sub Alarm_Exec($$$$$){
  460. my ($name,$level,$dev,$evt,$act) = @_;
  461. my $hash = $defs{$name};
  462. my $xec = AttrVal($name, "level".$level."xec", 0);
  463. my $xac = $hash->{READINGS}{'level'.$level}{VAL};
  464. my $msg = '';
  465. my $cmd;
  466. my $mga;
  467. my $dly;
  468. my @sta;
  469. #Log3 $hash,1,"[Alarm $level] Exec called with dev $dev evt $evt act $act]";
  470. return
  471. if ($dev eq 'global');
  472. return
  473. if (!defined($level));
  474. #-- raising the alarm
  475. if( $act eq "on" ){
  476. #-- only if this level is armed and not yet active
  477. if( ($xec eq "armed") && ($xac eq "armed") ){
  478. #-- check for time
  479. my $start = AttrVal($name, "level".$level."start", 0);
  480. if( index($start, '{') != -1){
  481. $start = eval($start);
  482. }
  483. my @st = split(':',$start);
  484. if( (int(@st)>3) || (int(@st)<2) || ($st[0] > 23) || ($st[0] < 0) || ($st[1] > 59) || ($st[1] < 0) ){
  485. Log3 $hash,1,"[Alarm $level] Cannot be executed due to wrong time spec $start for level".$level."start";
  486. return;
  487. }
  488. my $end = AttrVal($name, "level".$level."end", 0);
  489. if( index($end, '{') != -1){
  490. $end = eval($end);
  491. }
  492. my @et = split(':',$end);
  493. if( (int(@et)>3) || (int(@et)<2) || ($et[0] > 23) || ($et[0] < 0) || ($et[1] > 59) || ($et[1] < 0) ){
  494. Log3 $hash,1,"[Alarm $level] Cannot be executed due to wrong time spec $end for level".$level."end";
  495. return;
  496. }
  497. my $stp = $st[0]*60+$st[1];
  498. my $etp = $et[0]*60+$et[1];
  499. my ($sec, $min, $hour, $day, $month, $year, $wday, $yday, $isdst) = localtime(time);
  500. my $ntp = $hour*60+$min;
  501. if( (($stp < $etp) && ($ntp <= $etp) && ($ntp >= $stp)) || (($stp > $etp) && (($ntp <= $etp) || ($ntp >= $stp))) ){
  502. #-- raised by sensor (attribute values have been controlled in CreateNotifiers)
  503. @sta = split('\|', AttrVal($dev, "alarmSettings", ""));
  504. if( $sta[2] ){
  505. $mga = $sta[2]." ".AttrVal($name, "level".$level."msg", 0);
  506. #-- replace some parts
  507. my @evtpart = split(" ",$evt);
  508. $mga =~ s/\$NAME/$dev/g;
  509. $mga =~ s/\$EVENT/$evt/g;
  510. for( my $i=1;$i<= int(@evtpart);$i++){
  511. $mga =~ s/\$EVTPART$i/$evtpart[$i-1]/g;
  512. }
  513. #-- readings
  514. readingsSingleUpdate( $hash, "level".$level,$dev,1 );
  515. readingsSingleUpdate( $hash, "short", $mga, 1);
  516. $msg = Alarm_getstate($hash)." ".$mga;
  517. readingsSingleUpdate( $hash, "state", $msg, 1 );
  518. $msg = "[Alarm $level] raised from device $dev with event $evt";
  519. #-- calling actors AFTER state update
  520. $cmd = AttrVal($name, "level".$level."onact", 0);
  521. $cmd =~ s/\$NAME/$dev/g;
  522. $cmd =~ s/\$EVENT/$evt/g;
  523. $cmd =~ s/\$SHORT/$mga/g;
  524. for( my $i=1;$i<= int(@evtpart);$i++){
  525. $cmd =~ s/\$EVTPART$i/$evtpart[$i-1]/g;
  526. }
  527. fhem($cmd);
  528. Log3 $hash,3,$msg;
  529. }else{
  530. $msg = "[Alarm $level] not raised, alarmSensor $dev has wrong settings";
  531. Log3 $hash,1,$msg;
  532. }
  533. }else{
  534. $msg = "[Alarm $level] not raised, not in time slot";
  535. Log3 $hash,5,$msg;
  536. }
  537. }else{
  538. $msg = "[Alarm $level] not raised, not armed or already active";
  539. Log3 $hash,5,$msg;
  540. }
  541. }elsif( ($act eq "off")||($act eq "cancel") ){
  542. #-- only if this level is active
  543. if( ($xac ne "armed")&&($xac ne "disarmed") ){
  544. #-- deleting all running ats
  545. $dly = sprintf("alarm%1ddly",$level);
  546. foreach my $d (sort keys %intAt ) {
  547. next if( $intAt{$d}{FN} ne "at_Exec" );
  548. $mga = $intAt{$d}{ARG}{NAME};
  549. next if( $mga !~ /$dly\d/);
  550. #Log3 $hash,1,"[Alarm] Killing delayed action $name";
  551. CommandDelete(undef,"$mga");
  552. }
  553. #-- replace some parts
  554. my @evtpart = split(" ",$evt);
  555. #-- calling actors BEFORE state update
  556. $cmd = AttrVal($name, "level".$level."offact", 0);
  557. $cmd =~ s/\$NAME/$dev/g;
  558. $cmd =~ s/\$EVENT/$evt/g;
  559. $cmd =~ s/\$SHORT/$mga/g;
  560. for( my $i=1;$i<= int(@evtpart);$i++){
  561. $cmd =~ s/\$EVTPART$i/$evtpart[$i-1]/g;
  562. }
  563. fhem($cmd);
  564. $cmd = AttrVal($name, "cancelact", 0);
  565. fhem($cmd)
  566. if( $cmd );
  567. #-- readings - arm status does not change
  568. readingsSingleUpdate( $hash, "level".$level,"canceled",1);
  569. readingsSingleUpdate( $hash, "level".$level,"armed",1);
  570. readingsSingleUpdate( $hash, "short", "", 0);
  571. $mga = Alarm_getstate($hash)." ".$mga;
  572. readingsSingleUpdate( $hash, "state", $mga, 1 );
  573. $msg = "[Alarm $level] canceled from device $dev";
  574. Log3 $hash,3,$msg;
  575. }
  576. }else{
  577. Log3 $hash,3,"[Alarm $level] Exec called with act=$act";
  578. }
  579. #return $msg;
  580. }
  581. #########################################################################################
  582. #
  583. # Alarm_Arm - Arm the Alarm
  584. #
  585. # Parameter name = name of the Alarm definition
  586. # level = Alarm level
  587. # dev = name of the device calling the alarm
  588. # act = action - "armed" or "disarmed"
  589. #
  590. #########################################################################################
  591. sub Alarm_Arm($$$$$){
  592. my ($name,$level,$dev,$evt,$act) = @_;
  593. my $hash = $defs{$name};
  594. my $xac = $hash->{READINGS}{"level"}{VAL};
  595. my $xec = AttrVal($name, 'level'.$level.'xec', 0);
  596. my $msg = '';
  597. my $mga;
  598. my $cmd;
  599. #-- arming the alarm
  600. if( ($act eq "arm") && ( $xec ne "armed") ){
  601. my $xdl = AttrVal($name, "armdelay", 0);
  602. my $cmdwait = AttrVal($name, "armwait", 0);
  603. my $cmdact = AttrVal($name, "armact", 0);
  604. if( ($xdl eq '')|($xdl eq '0:00')|($xdl eq '00:00') ){
  605. CommandAttr(undef,$name.' level'.$level.'xec armed');
  606. readingsSingleUpdate( $hash, "level".$level,"armed",1 );
  607. #--transform commands from fhem to perl level
  608. my @cmdactarr = split(/;/,$cmdact);
  609. my $cmdactf;
  610. if( int(@cmdactarr) == 1 ){
  611. fhem("$cmdact");
  612. }else{
  613. for(my $i=0;$i<int(@cmdactarr);$i++){
  614. fhem("$cmdactarr[$i]");
  615. }
  616. }
  617. $msg = "[Alarm $level] armed from alarmSensor $dev with event $evt";
  618. Log3 $hash,3,$msg;
  619. } elsif( $xdl =~ /([0-9])?:([0-5][0-9])?/ ){
  620. CommandAttr(undef,$name.' level'.$level.'xec armwait');
  621. #--transform commands from fhem to perl level
  622. my @cmdactarr = split(/;/,$cmdact);
  623. my $cmdactf;
  624. if( int(@cmdactarr) == 1 ){
  625. $cmdactf = "fhem(\"".$cmdact."\");;";
  626. }else{
  627. $cmdactf = '';
  628. for(my $i=0;$i<int(@cmdactarr);$i++){
  629. $cmdactf.="fhem(\"".$cmdactarr[$i]."\");;";
  630. }
  631. }
  632. #-- compose commands
  633. $cmd = sprintf("define alarm%1d.arm.dly at +00:%02d:%02d {fhem(\"setreading %s level%1d armed\");;fhem(\"attr %s level%1dxec armed\");;%s}",
  634. $level,$1,$2,$name,$level,$name,$level,$cmdactf);
  635. $msg = "[Alarm $level] will be armed from alarmSensor $dev with event $evt, delay $xdl";
  636. #-- delete old delayed arm
  637. fhem('delete alarm'.$level.'.arm.dly' )
  638. if( defined $defs{'alarm'.$level.'.arm.dly'});
  639. #-- define new delayed arm
  640. fhem($cmd);
  641. #-- execute armwait action
  642. fhem($cmdwait);
  643. Log3 $hash,3,$msg;
  644. }else{
  645. $msg = "[Alarm $level] cannot be armed due to wrong delay timespec";
  646. Log3 $hash,1,$msg;
  647. }
  648. #-- disarming implies canceling as well
  649. }elsif( ($act eq "disarm") && ($xec ne "disarmed")) {
  650. #-- delete old delayed arm
  651. fhem('delete alarm'.$level.'.arm.dly' )
  652. if( defined $defs{'alarm'.$level.'.arm.dly'});
  653. CommandAttr (undef,$name.' level'.$level.'xec disarmed');
  654. Alarm_Exec($name,$level,"program","disarm","cancel");
  655. readingsSingleUpdate( $hash, "level".$level,"disarmed",1 );
  656. #--
  657. $msg = "[Alarm $level] disarmed from alarmSensor $dev with event $evt";
  658. $cmd = AttrVal($name, "disarmact", 0);
  659. fhem("define alarm".$level.".disarm.T at +00:00:03 ".$cmd)
  660. if( $cmd );
  661. }
  662. return $msg;
  663. }
  664. #########################################################################################
  665. #
  666. # Alarm_CreateNotifiers - Create the notifiers
  667. #
  668. # Parameter name = name of the Alarm definition
  669. #
  670. #########################################################################################
  671. sub Alarm_CreateNotifiers($){
  672. my ($name) = @_;
  673. my $ret = "";
  674. my $res;
  675. my $hash = $defs{$name};
  676. #-- don't do anything if locked
  677. if( $hash->{READINGS}{"lockstate"}{VAL} ne "unlocked" ){
  678. Log3 $hash, 1, "[Alarm] State locked, cannot create new notifiers";
  679. return "State locked, cannot create new notifiers";
  680. }
  681. for( my $level=0;$level<$alarmno;$level++ ){
  682. #-- delete old defs in any case
  683. fhem('delete alarm'.$level.'.on.N' )
  684. if( defined $defs{'alarm'.$level.'.on.N'});
  685. fhem('delete alarm'.$level.'.off.N' )
  686. if( defined $defs{'alarm'.$level.'.off.N'});
  687. fhem('delete alarm'.$level.'.arm.N' )
  688. if( defined $defs{'alarm'.$level.'.arm.N'});
  689. fhem('delete alarm'.$level.'.disarm.N' )
  690. if( defined $defs{'alarm'.$level.'.disarm.N'});
  691. my $start = AttrVal($name, "level".$level."start", 0);
  692. my @st;
  693. if( index($start,'{')!=-1 ){
  694. Log3 $hash,1,"[Alarm $level] perl function $start detected for level".$level."start, currently the function gives ".eval($start);
  695. }else{
  696. @st = split(':',($start ne '') ? $start :'0:00');
  697. if( (int(@st)!=2) || ($st[0] > 23) || ($st[0] < 0) || ($st[1] > 59) || ($st[1] < 0) ){
  698. Log3 $hash,1,"[Alarm $level] Will not be executed due to wrong time spec $start for level".$level."start";
  699. next;
  700. }
  701. }
  702. my $end = AttrVal($name, "level".$level."end", 0);
  703. my @et;
  704. if( index($end,'{')!=-1 ){
  705. Log3 $hash,1,"[Alarm $level] perl function $end detected for level".$level."end, currently the function gives ".eval($end);
  706. }else{
  707. @et = split(':',($end ne '') ? $end :'23:59');
  708. if( (int(@et)!=2) || ($et[0] > 23) || ($et[0] < 0) || ($et[1] > 59) || ($et[1] < 0) ){
  709. Log3 $hash,1,"[Alarm $level] Will not be executed due to wrong time spec $end for level".$level."end";
  710. next;
  711. }
  712. }
  713. #-- now set up the command for cancel alarm, and contained in this loop all other notifiers as well
  714. my $cmd = '';
  715. foreach my $d (keys %defs ) {
  716. next if(IsIgnored($d));
  717. if( AttrVal($d, "alarmDevice","") eq "Sensor" ) {
  718. my @aval = split('\|',AttrVal($d, "alarmSettings",""));
  719. if( int(@aval) != 4){
  720. # Log3 $hash, 1, "[Alarm $level] Settings incomplete for sensor $d";
  721. next;
  722. }
  723. if( (index($aval[0],"alarm".$level) != -1) && ($aval[3] eq "off") ){
  724. $cmd .= '('.$aval[1].')|';
  725. #Log3 $hash,1,"[Alarm $level] Adding sensor $d to cancel notifier";
  726. }
  727. }
  728. }
  729. if( $cmd eq '' ){
  730. Log3 $hash,1,"[Alarm $level] No \"Cancel\" device defined, level will be ignored";
  731. } else {
  732. $cmd = substr($cmd,0,length($cmd)-1);
  733. $cmd = 'alarm'.$level.'.off.N notify '.$cmd;
  734. $cmd .= ' {main::Alarm_Exec("'.$name.'",'.$level.',"$NAME","$EVENT","off")}';
  735. CommandDefine(undef,$cmd);
  736. CommandAttr (undef,'alarm'.$level.'.off.N room '.$alarmpublicroom);
  737. CommandAttr (undef,'alarm'.$level.'.off.N group alarmNotifier');
  738. Log3 $hash,5,"[Alarm $level] Created cancel notifier";
  739. #-- now set up the command for raising alarm - only if cancel exists
  740. $cmd = '';
  741. my $cmdarm = "";
  742. my $cmddisarm = "";
  743. foreach my $d (sort keys %defs ) {
  744. next if(IsIgnored($d));
  745. if( AttrVal($d, "alarmDevice","") eq "Sensor" ) {
  746. my @aval = split('\|',AttrVal($d, "alarmSettings",""));
  747. if( int(@aval) != 4){
  748. Log3 $hash, 5, "[Alarm $level] Settings incomplete for alarmSensor $d";
  749. next;
  750. }
  751. if( index($aval[0],"alarm".$level) != -1){
  752. if( $aval[3] eq "on" ){
  753. $cmd .= '('.$aval[1].')|';
  754. Log3 $hash,5,"[Alarm $level] Adding alarmSensor $d to raise notifier";
  755. }elsif( $aval[3] eq "arm" ){
  756. $cmdarm .= '('.$aval[1].')|';
  757. Log3 $hash,5,"[Alarm $level] Adding alarmSensor $d to arm notifier";
  758. }elsif( $aval[3] eq "disarm" ){
  759. $cmddisarm .= '('.$aval[1].')|';
  760. Log3 $hash,5,"[Alarm $level] Adding alarmSensor $d to disarm notifier";
  761. }
  762. }
  763. }
  764. }
  765. #-- raise notifier
  766. if( $cmd eq '' ){
  767. Log3 $hash,1,"[Alarm $level] No \"Raise\" device defined";
  768. } else {
  769. $cmd = substr($cmd,0,length($cmd)-1);
  770. $cmd = 'alarm'.$level.'.on.N notify '.$cmd;
  771. $cmd .= ' {main::Alarm_Exec("'.$name.'",'.$level.',"$NAME","$EVENT","on")}';
  772. CommandDefine(undef,$cmd);
  773. CommandAttr (undef,'alarm'.$level.'.on.N room '.$alarmpublicroom);
  774. CommandAttr (undef,'alarm'.$level.'.on.N group alarmNotifier');
  775. Log3 $hash,5,"[Alarm $level] Created raise notifier";
  776. #-- now set up the list of actors
  777. $cmd = '';
  778. my $cmd2 = '';
  779. my $nonum = 0;
  780. foreach my $d (sort keys %defs ) {
  781. next if(IsIgnored($d));
  782. if( AttrVal($d, "alarmDevice","") eq "Actor" ) {
  783. my @aval = Alarm_getsettings($hash,$d,"Actor");
  784. if( int(@aval) != 4){
  785. Log3 $hash, 5, "[Alarm $level] Settings incomplete for alarmActor $d";
  786. next;
  787. }
  788. if( index($aval[0],"alarm".$level) != -1 ){
  789. #-- activate without delay
  790. if(( $aval[3] eq "" )||($aval[3] eq "00:00")){
  791. $cmd .= $aval[1].';';
  792. #-- activate with delay
  793. } else {
  794. $nonum++;
  795. my @tarr = split(':',$aval[3]);
  796. if( int(@tarr) == 2){
  797. $cmd .= sprintf('define alarm%1ddly%1d at +00:%02d:%02d %s;',$level,$nonum,$tarr[0],$tarr[1],$aval[1]);
  798. }elsif( int(@tarr) == 3){
  799. $cmd .= sprintf('define alarm%1ddly%1d at +%02d:%02d:%02d %s;',$level,$nonum,$tarr[0],$tarr[1],$tarr[2],$aval[1]);
  800. }else{
  801. Log3 $name,1,"[Alarm $level] Invalid delay specification for actor $d, skipped";
  802. $cmd .= $aval[1].';';
  803. }
  804. }
  805. $cmd2 .= $aval[2].';'
  806. if( $aval[2] ne '' );
  807. Log3 $hash,5,"[Alarm $level] Adding actor $d to action list";
  808. }
  809. }
  810. }
  811. if( $cmd ne '' ){
  812. CommandAttr(undef,$name.' level'.$level.'onact '.$cmd);
  813. CommandAttr(undef,$name.' level'.$level.'offact '.$cmd2);
  814. Log3 $hash,5,"[Alarm $level] Added on/off actors to $name";
  815. } else {
  816. Log3 $hash,5,"[Alarm $level] Adding on/off actors not possible";
  817. }
  818. #-- arm notifier - optional, but only in case the alarm may be raised
  819. if( $cmdarm ne '' ){
  820. $cmdarm = substr($cmdarm,0,length($cmdarm)-1);
  821. $cmdarm = 'alarm'.$level.'.arm.N notify '.$cmdarm;
  822. $cmdarm .= ' {main::Alarm_Arm("'.$name.'",'.$level.',"$NAME","$EVENT","arm")}';
  823. CommandDefine(undef,$cmdarm);
  824. CommandAttr (undef,'alarm'.$level.'.arm.N room '.$alarmpublicroom);
  825. CommandAttr (undef,'alarm'.$level.'.arm.N group alarmNotifier');
  826. Log3 $hash,3,"[Alarm $level] Created arm notifier";
  827. }
  828. #-- disarm notifier - optional, but only in case the alarm may be raised
  829. if( $cmddisarm ne '' ){
  830. $cmddisarm = substr($cmddisarm,0,length($cmddisarm)-1);
  831. $cmddisarm = 'alarm'.$level.'.disarm.N notify '.$cmddisarm;
  832. $cmddisarm .= ' {main::Alarm_Arm("'.$name.'",'.$level.',"$NAME","$EVENT","disarm")}';
  833. CommandDefine(undef,$cmddisarm);
  834. CommandAttr (undef,'alarm'.$level.'.disarm.N room '.$alarmpublicroom);
  835. CommandAttr (undef,'alarm'.$level.'.disarm.N group alarmNotifier');
  836. Log3 $hash,3,"[Alarm $level] Created disarm notifier";
  837. }
  838. }
  839. }
  840. }
  841. return "Created alarm notifiers";
  842. }
  843. #########################################################################################
  844. #
  845. # Alarm_Html - returns HTML code for the Alarm page
  846. #
  847. # Parameter name = name of the Alarm definition
  848. #
  849. #########################################################################################
  850. sub Alarm_Html($)
  851. {
  852. my ($name) = @_;
  853. my $ret = "";
  854. my $hash = $defs{$name};
  855. my $id = $defs{$name}{NR};
  856. if( !defined($alarm_tt) ){
  857. #-- readjust language
  858. my $lang = AttrVal("global","language","EN");
  859. if( $lang eq "DE"){
  860. $alarm_tt = \%alarm_transtable_DE;
  861. }else{
  862. $alarm_tt = \%alarm_transtable_EN;
  863. }
  864. }
  865. #--
  866. readingsSingleUpdate( $hash, "state", Alarm_getstate($hash)." ".$hash->{READINGS}{"short"}{VAL}, 1 );
  867. #--
  868. my $lockstate = ($hash->{READINGS}{lockstate}{VAL}) ? $hash->{READINGS}{lockstate}{VAL} : "unlocked";
  869. my $showhelper = ($lockstate eq "unlocked") ? 1 : 0;
  870. #--
  871. $ret .= "<script type=\"text/javascript\" src=\"$FW_ME/pgm2/alarm.js\"></script><script type=\"text/javascript\">\n";
  872. $ret .= "var alarmno = ".$alarmno.";\n";
  873. #for( my $k=0;$k<$alarmno;$k++ ){
  874. # $ret .= "ah.setItem('l".$k."s','".AttrVal($name, "level".$k."start", 0)."');\n"
  875. # if( defined AttrVal($name, "level".$k."start", 0));
  876. # $ret .= "ah.setItem('l".$k."e','".AttrVal($name, "level".$k."end", 0)."');\n"
  877. # if( defined AttrVal($name, "level".$k."end", 0));
  878. # $ret .= "ah.setItem('l".$k."m','".AttrVal($name, "level".$k."msg", 0)."');\n"
  879. # if( defined AttrVal($name, "level".$k."msg", 0));
  880. # $ret .= "ah.setItem('l".$k."x','".AttrVal($name, "level".$k."xec", 0)."');\n"
  881. # if( defined AttrVal($name, "level".$k."xec", 0));
  882. #}
  883. $ret .= "</script>\n";
  884. $ret .= "<table class=\"roomoverview\">\n";
  885. $ret .= "<tr><td><input type=\"button\" value=\"Set Alarms\" onclick=\"javascript:alarm_set('$name')\"/></td></tr>\n";
  886. #-- settings table
  887. my $row=1;
  888. $ret .= "<tr><td><div class=\"devType\">".$alarm_tt->{"settings"}."</div></td></tr>";
  889. $ret .= "<tr><td><table class=\"block wide\" id=\"settingstable\">\n";
  890. $ret .= "<tr class=\"odd\"><td class=\"col1\" colspan=\"4\"><table id=\"armtable\" border=\"0\">\n";
  891. $ret .= "<tr class=\"odd\"><td class=\"col1\" style=\"text-align:right\">".$alarm_tt->{"armbutton"}."&nbsp;&#8608</td>";
  892. $ret .= "<td class=\"col2\" style=\"text-align:right\"> ".$alarm_tt->{"waitaction"}." ";
  893. $ret .= sprintf("<input type=\"text\" id=\"armwait\" size=\"50\" maxlength=\"512\" value=\"%s\"/>",(AttrVal($name, "armwait","") eq "1")?"":AttrVal($name, "armwait",""));
  894. $ret .= "</td><td class=\"col3\" rowspan=\"2\"> &#8628 ".$alarm_tt->{"delay"}."<br> &#8626";
  895. $ret .= sprintf("<input type=\"text\" id=\"armdelay\" size=\"4\" maxlength=\"5\" value=\"%s\"/>",(AttrVal($name, "armdelay","0:00") eq "1")?"":AttrVal($name, "armdelay","0:00"));
  896. $ret .= "</td></tr>\n";
  897. $ret .= "<tr class=\"even\"><td class=\"col1\"></td><td class=\"col2\" style=\"text-align:right\">".$alarm_tt->{"armaction"}." ";
  898. $ret .= sprintf("<input type=\"text\" id=\"armaction\" size=\"50\" maxlength=\"512\" value=\"%s\"/>",(AttrVal($name, "armact","") eq "1")?"":AttrVal($name, "armact",""));
  899. $ret .= "</td></tr>\n";
  900. $ret .="<tr class=\"odd\"><td class=\"col1\" style=\"text-align:right\">".$alarm_tt->{"disarmbutton"}."&#8608</td><td class=\"col2\" style=\"text-align:right\">".$alarm_tt->{"disarmaction"}." ";
  901. $ret .= sprintf("<input type=\"text\" id=\"disarmaction\" size=\"50\" maxlength=\"512\" value=\"%s\"/>",(AttrVal($name, "disarmact","") eq "1")?"":AttrVal($name, "disarmact",""));
  902. $ret .= "</td><td></td></tr><tr class=\"odd\"><td class=\"col1\" style=\"text-align:right\">".$alarm_tt->{"cancelbutton"}."&nbsp;&#8608</td><td class=\"col2\" style=\"text-align:right\"> ".$alarm_tt->{"cancelaction"}." ";
  903. $ret .= sprintf("<input type=\"text\" id=\"cancelaction\" size=\"50\" maxlength=\"512\" value=\"%s\"/>",(AttrVal($name, "cancelact","") eq "1")?"":AttrVal($name, "cancelact",""));
  904. $ret .= "</td><td></td></tr></table></td></tr>";
  905. $ret .= "<tr class=\"odd\"><td class=\"col1\">".$alarm_tt->{"level"}."</td><td class=\"col2\">".$alarm_tt->{"time"}." [hh:mm]<br/>".
  906. $alarm_tt->{"start"}."&nbsp;&nbsp;&nbsp;&nbsp;".$alarm_tt->{"end"}."&nbsp;</td><td class=\"col3\">".$alarm_tt->{"messagepart"}." II</td>".
  907. "<td class=\"col4\">".$alarm_tt->{"arm"}."/".$alarm_tt->{"cancel"}."</td></tr>\n";
  908. for( my $k=0;$k<$alarmno;$k++ ){
  909. $row++;
  910. my $sval = AttrVal($name, "level".$k."start", 0);
  911. $sval = ""
  912. if( $sval eq "1");
  913. my $eval = AttrVal($name, "level".$k."end", 0);
  914. $eval = ""
  915. if( $eval eq "1");
  916. my $mval = AttrVal($name, "level".$k."msg", 0);
  917. $mval = ""
  918. if( $mval eq "1");
  919. my $xval = AttrVal($name, "level".$k."xec", 0);
  920. $ret .= sprintf("<tr class=\"%s\"><td class=\"col1\">".$alarm_tt->{"alarm"}." $k</td>\n", ($row&1)?"odd":"even");
  921. $ret .= "<td class=\"col2\"><input type=\"text\" id=\"l".$k."s\" size=\"4\" maxlength=\"120\" value=\"$sval\"/>&nbsp;&nbsp;&nbsp;".
  922. "<input type=\"text\" id=\"l".$k."e\" size=\"4\" maxlength=\"120\" value=\"$eval\"/></td>".
  923. "<td class=\"col3\"><input type=\"text\" id=\"l".$k."m\" size=\"25\" maxlength=\"256\" value=\"$mval\"/></td>";
  924. $ret .= sprintf("<td class=\"col4\"><input type=\"checkbox\" id=\"l".$k."x\" %s onclick=\"javascript:alarm_arm('$name','$k')\"/>",($xval eq "armed")?"checked=\"checked\"":"").
  925. "<input type=\"button\" value=\"".$alarm_tt->{"cancel"}."\" onclick=\"javascript:alarm_cancel('$name','$k')\"/></td></tr>\n";
  926. }
  927. $ret .= "</table></td></tr></tr>";
  928. #-- sensors table
  929. $row=1;
  930. $ret .= "<tr><td><div class=\"devType\">".$alarm_tt->{"sensors"}."</div></td></tr>";
  931. $ret .= "<tr><td><table class=\"block wide\" id=\"sensorstable\">\n";
  932. $ret .= "<tr class=\"odd\" style=\"min-width:100px\"><td/><td class=\"col2\" style=\"min-width:200px\">".$alarm_tt->{"notifyto"}." ".$alarm_tt->{"alarm"}." ".$alarm_tt->{"level"}."<br/>".
  933. join("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;",(0..($alarmno-1)))."</td><td class=\"col3\">".
  934. $alarm_tt->{"notifyby"}." ".$alarm_tt->{"regexp"}."</td><td class=\"col3\">".$alarm_tt->{"messagepart"}." I</td><td class=\"col4\">".$alarm_tt->{"action"}."</td></tr>\n";
  935. foreach my $d (sort keys %defs ) {
  936. next if(IsIgnored($d));
  937. if( AttrVal($d, "alarmDevice","") eq "Sensor" ) {
  938. my @aval = split('\|',AttrVal($d, "alarmSettings",""));
  939. if( int(@aval) != 4){
  940. Log3 $hash, 1, "[Alarm] Settings incomplete for alarmSensor $d";
  941. }
  942. $row++;
  943. $ret .= sprintf("<tr class=\"%s\" informId=\"$d\" name=\"sensor\">", ($row&1)?"odd":"even");
  944. $ret .= "<td width=\"120\" class=\"col1\"><a href=\"$FW_ME?detail=$d\">$d</a></td>\n";
  945. $ret .= "<td id=\"$d\" class=\"col2\">\n";
  946. for( my $k=0;$k<$alarmno;$k++ ){
  947. $ret .= sprintf("<input type=\"checkbox\" name=\"alarm$k\" value=\"$k\" %s/>&nbsp;",(index($aval[0],"alarm".$k) != -1)?"checked=\"checked\"":"");
  948. }
  949. $ret .= "</td><td class=\"col3\"><input type=\"text\" name=\"alarmnotify\" size=\"30\" maxlength=\"512\" value=\"$aval[1]\"/>";
  950. $ret .= "</td><td class=\"col3\"><input type=\"text\" name=\"alarmmsg\" size=\"30\" maxlength=\"512\" value=\"$aval[2]\"/></td>\n";
  951. $ret .= sprintf("<td class=\"col4\"><select name=\"%sonoff\"><option value=\"on\" %s>".$alarm_tt->{"raise"}."</option><option value=\"off\" %s>".$alarm_tt->{"cancel"}."</option>",
  952. $d,($aval[3] eq "on")?"selected=\"selected\"":"",($aval[3] eq "off")?"selected=\"selected\"":"");
  953. $ret .= sprintf("<option value=\"arm\" %s>".$alarm_tt->{"arm"}."</option><option value=\"disarm\" %s>".$alarm_tt->{"disarm"}."</option><select></td></tr>\n",
  954. ($aval[3] eq "arm")?"selected=\"seleced\"":"",($aval[3] eq "disarm")?"selected=\"selected\"":"");
  955. }
  956. }
  957. $ret .= "</table></td></tr></tr>";
  958. #-- actors table
  959. $row=1;
  960. $ret .= "<tr><td><div class=\"devType\">".$alarm_tt->{"actors"}."</div></td></tr>";
  961. $ret .= "<tr><td><table class=\"block wide\" id=\"actorstable\">\n";
  962. $ret .= "<tr class=\"odd\" style=\"min-width:100px\"><td/><td class=\"col2\" style=\"min-width:200px\">".$alarm_tt->{"setby"}." ".$alarm_tt->{"alarm"}." ".$alarm_tt->{"level"}."<br/>".join("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;",(0..($alarmno-1))).
  963. "</td><td class=\"col3\">".$alarm_tt->{"setaction"};
  964. $ret .= "&nbsp; (".$alarm_tt->{"testaction"}.")"
  965. if( AttrVal($name,"testbutton",0) == 1);
  966. $ret .= "</td><td class=\"col3\">".$alarm_tt->{"unsetaction"};
  967. $ret .= "&nbsp; (".$alarm_tt->{"testaction"}.")"
  968. if( AttrVal($name,"testbutton",0) == 1);
  969. $ret .= "</td><td class=\"col4\">".$alarm_tt->{"delay"}."<br/>[hh:]mm:ss</td></tr>\n";
  970. foreach my $d (sort keys %defs ) {
  971. next if(IsIgnored($d));
  972. if( AttrVal($d, "alarmDevice","") eq "Actor" ) {
  973. my @aval = Alarm_getsettings($hash,$d,"Actor");
  974. $row++;
  975. $ret .= sprintf("<tr class=\"%s\" informId=\"$d\" name=\"actor\">", ($row&1)?"odd":"even");
  976. $ret .= "<td width=\"120\" class=\"col1\"><a href=\"$FW_ME?detail=$d\">$d</a></td>\n";
  977. $ret .= "<td id=\"$d\" class=\"col2\">\n";
  978. for( my $k=0;$k<$alarmno;$k++ ){
  979. $ret .= sprintf("<input type=\"checkbox\" name=\"alarm$k\"%s/>&nbsp;",(index($aval[0],"alarm".$k) != -1)?"checked=\"checked\"":"");
  980. }
  981. $ret .= "</td><td class=\"col3\"><input type=\"text\" name=\"alarmon\" size=\"30\" maxlength=\"512\" value=\"$aval[1]\"/>";
  982. $ret .= "&nbsp;<input type=\"button\" value=\"T\" onclick=\"javascript:alarm_testaction('$name','$d','set')\"/>"
  983. if( AttrVal($name,"testbutton",0) == 1);
  984. $ret .= "</td><td class=\"col3\">";
  985. $ret .= "<input type=\"text\" name=\"alarmoff\" size=\"30\" maxlength=\"512\" value=\"$aval[2]\"/>";
  986. $ret .= "&nbsp;<input type=\"button\" value=\"T\" onclick=\"javascript:alarm_testaction('$name','$d','unset')\"/>"
  987. if( AttrVal($name,"testbutton",0) == 1);
  988. $ret .= "</td><td class=\"col4\"><input type=\"text\" name=\"delay\" size=\"5\" maxlength=\"8\" value=\"$aval[3]\"/></td></tr>\n";
  989. }
  990. }
  991. $ret .= "</table></td></tr></tr>\n";
  992. $ret .= "</table>";
  993. return $ret;
  994. }
  995. 1;
  996. =pod
  997. =item helper
  998. =item summary to set up a house alarm system with 8 different alarm levels
  999. =begin html
  1000. <a name="Alarm"></a>
  1001. <h3>Alarm</h3>
  1002. <p> FHEM module to set up a house alarm system with 8 different alarm levels</p>
  1003. <a name="Alarmusage"></a>
  1004. <h4>Usage</h4>
  1005. See <a href="http://www.fhemwiki.de/wiki/Modul_Alarm">German Wiki page</a>
  1006. <a name="Alarmdefine"></a>
  1007. <br/>
  1008. <h4>Define</h4>
  1009. <p>
  1010. <code>define &lt;name&gt; Alarm</code>
  1011. <br />Defines the Alarm system. </p>
  1012. <a name="Alarmset"></a>
  1013. <h4>Set</h4>
  1014. <ul>
  1015. <li><a name="alarm_cancel">
  1016. <code>set &lt;name&gt; canceled &lt;level&gt;</code>
  1017. </a>
  1018. <br />cancels an alarm of level &lt;level&gt;, where &lt;level&gt; = 0..7 </li>
  1019. <li><a name="alarm_arm">
  1020. <code>set &lt;name&gt; armed &lt;level&gt;</code><br />
  1021. <code>set &lt;name&gt; disarmed &lt;level&gt;</code>
  1022. </a>
  1023. <br />sets the alarm of level &lt;level&gt; to armed (i.e., active) or disarmed
  1024. (i.e., inactive), where &lt;level&gt; = 0..7 </li>
  1025. <li><a name="alarm_lock">
  1026. <code>set &lt;name&gt; locked</code><br />
  1027. <code>set &lt;name&gt; unlocked</code>
  1028. </a>
  1029. <br />sets the lockstate of the alarm module to <i>locked</i> (i.e., alarm setups
  1030. may not be changed) resp. <i>unlocked</i> (i.e., alarm setups may be changed>)</li>
  1031. </ul>
  1032. <a name="Alarmget"></a>
  1033. <h4>Get</h4>
  1034. <ul>
  1035. <li><a name="alarm_version"></a>
  1036. <code>get &lt;name&gt; version</code>
  1037. <br />Display the version of the module</li>
  1038. </ul>
  1039. <a name="Alarmattr"></a>
  1040. <h4>Attributes</h4>
  1041. <ul>
  1042. <li><a name="alarm_hiddenroom"><code>attr &lt;name&gt; hiddenroom
  1043. &lt;string&gt;</code></a>
  1044. <br />Room name for hidden alarm room (containing only the Alarm device), default:
  1045. AlarmRoom</li>
  1046. <li><a name="alarm_publicroom"><code>attr &lt;name&gt; publicroom
  1047. &lt;string&gt;</code></a>
  1048. <br />Room name for public alarm room (containing sensor/actor devices), default:
  1049. Alarm</li>
  1050. <li><a name="alarm_lockstate"><code>attr &lt;name&gt; lockstate
  1051. locked|unlocked</code></a>
  1052. <br /><i>locked</i> means that alarm setups may not be changed, <i>unlocked</i>
  1053. means that alarm setups may be changed></li>
  1054. <li><a name="alarm_testbutton"><code>attr &lt;name&gt; testbutton 0|1</code></a>
  1055. <br /><i>1</i> means that a test button is displayed for every actor field</li>
  1056. <li><a name="alarm_statedisplay"><code>attr &lt;name&gt; statedisplay
  1057. simple,color,table,none</code></a>
  1058. <br />defines how the state of all eight alarm levels is shown. Example for the case
  1059. when alarm no. 0 is disarmed and only alarm no. 2 is raised: <ul>
  1060. <li> simple = -OXOOOOO</li>
  1061. <li> color = <span style="color:lightgray"> 0 </span><span style="font-weight:bold;color:green">1 <span style="font-weight:bold;color:red"
  1062. >2</span> 3 4 5 6 7</span></li>
  1063. <li> table = HTML mini table with lightgray, green and red fields for alarms
  1064. </li>
  1065. <li> none = no state display</li>
  1066. </ul>
  1067. </li>
  1068. <li><a name="alarm_armdelay"><code>attr &lt;name&gt; armdelay <i>mm:ss</i></code></a>
  1069. <br />time until the arming of an alarm becomes operative (0:00 - 9:59 allowed)</li>
  1070. <li><a name="alarm_armwait"><code>attr &lt;name&gt; armwait <i>action</i></code></a>
  1071. <br />FHEM action to be carried out immediately after the arm event</li>
  1072. <li><a name="alarm_armact"><code>attr &lt;name&gt; armact <i>action</i></code></a>
  1073. <br />FHEM action to be carried out at the arme event after the delay time </li>
  1074. <li><a name="alarm_disarmact"><code>attr &lt;name&gt; disarmact <i>action</i></code></a>
  1075. <br />FHEM action to be carried out on the disarming of an alarm</li>
  1076. <li><a name="alarm_cancelact"><code>attr &lt;name&gt; cancelact <i>action</i></code></a>
  1077. <br />FHEM action to be carried out on the canceling of an alarm</li>
  1078. <li><a name="alarm_internals"></a>For each of the 8 alarm levels, several attributes
  1079. hold the alarm setup. They should not be changed by hand, but through the web
  1080. interface to avoid confusion: <code>level&lt;level&gt;start, level&lt;level&gt;end,
  1081. level&lt;level&gt;msg, level&lt;level&gt;xec, level&lt;level&gt;onact,
  1082. level&lt;level&gt;offact</code></li>
  1083. <li>Standard attributes <a href="#alias">alias</a>, <a href="#comment">comment</a>, <a
  1084. href="#event-on-update-reading">event-on-update-reading</a>, <a
  1085. href="#event-on-change-reading">event-on-change-reading</a>, <a href="#room"
  1086. >room</a>, <a href="#eventMap">eventMap</a>, <a href="#loglevel">loglevel</a>,
  1087. <a href="#webCmd">webCmd</a></li>
  1088. </ul>
  1089. =end html
  1090. =begin html_DE
  1091. <a name="Alarm"></a>
  1092. <h3>Alarm</h3>
  1093. <a href="https://wiki.fhem.de/wiki/Modul_Alarm">Deutsche Dokumentation im Wiki</a> vorhanden, die englische Version gibt es hier: <a href="/fhem/docs/commandref.html#Alarm">Alarm</a>
  1094. =end html_DE
  1095. =cut