95_Alarm.pm 67 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596
  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 17344 2018-09-14 14:05:23Z 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. use JSON; # imports encode_json, decode_json, to_json and from_json.
  36. #########################
  37. # Global variables
  38. my $alarmlinkname = "Alarms"; # link text
  39. my $alarmhiddenroom = "AlarmRoom"; # hidden room
  40. my $alarmpublicroom = "Alarm"; # public room
  41. my $alarmno = 8;
  42. my $alarmversion = "5.0";
  43. my %alarm_transtable_EN = (
  44. "ok" => "OK",
  45. "notok" => "Not OK",
  46. "cond" => "Condition",
  47. "start" => "Start",
  48. "end" => "End",
  49. "autocan" => "AutoCancel",
  50. "status" => "Status",
  51. "notstarted" => "Not started",
  52. "next" => "Next",
  53. "arm" => "Arm",
  54. "disarm" => "Disarm",
  55. "armbutton" => "Arming",
  56. "disarmbutton" => "Disarming",
  57. "cancelbutton" => "Canceling",
  58. "raise" => "Raise",
  59. "wait" => "Wait",
  60. "delay" => "Delay",
  61. "cancel" => "Cancel",
  62. "button" => "Button",
  63. "level" => "Level",
  64. "message" => "Message",
  65. "messagepart" => "Message Part",
  66. "notify" => "Notify",
  67. "notifyto" => "Notify to",
  68. "notifyby" => "Notify by",
  69. "setby" => "Set by",
  70. "regexp" => "RegExp",
  71. "time" => "Time",
  72. "description" => "Description",
  73. "settings" => "Settings",
  74. "sensors" => "Sensors",
  75. "actors" => "Actors",
  76. "action" => "Action",
  77. "setaction" => "Set Action",
  78. "unsetaction" => "Unset Action",
  79. "testaction" => "Test",
  80. "armaction" => "Arm Action",
  81. "disarmaction" => "Disarm Action",
  82. "waitaction" => "Wait Action",
  83. "cancelaction" => "Cancel Action",
  84. "canceled" => "canceled by:",
  85. "alarm" => "Alarm",
  86. "raised" => "raised by:",
  87. "alarms" => "Alarm System",
  88. "setparms" => "Set Parameters",
  89. #--
  90. "state" => "Security",
  91. "unlocked" => "Unlocked",
  92. "locked" => "Locked",
  93. "unsecured" => "Not Secured",
  94. "secured" => "Secured",
  95. "protected" => "Geschützt",
  96. "guarded" => "Guarded"
  97. );
  98. my %alarm_transtable_DE = (
  99. "ok" => "OK",
  100. "notok" => "Nicht OK",
  101. "cond" => "Bedingung",
  102. "start" => "Start",
  103. "end" => "Ende",
  104. "autocan" => "AutoWiderruf",
  105. "status" => "Status",
  106. "notstarted" => "Nicht gestartet",
  107. "next" => "Nächste",
  108. "arm" => "Schärfen",
  109. "disarm" => "Entschärfen",
  110. "armbutton" => "Schärfen",
  111. "disarmbutton" => "Entschärfen",
  112. "cancelbutton" => "Widerrufen",
  113. "raise" => "Auslösen",
  114. "wait" => "Warte",
  115. "delay" => "Verzögerung",
  116. "cancel" => "Widerruf",
  117. "button" => "Button",
  118. "level" => "Level",
  119. "message" => "Nachricht",
  120. "messagepart" => "Nachrichtenteil",
  121. "notify" => "Auslösung",
  122. "notifyto" => "Wirkt auf",
  123. "notifyby" => "Auslösung durch",
  124. "setby" => "Gesetzt durch",
  125. "regexp" => "RegExp",
  126. "time" => "Zeit",
  127. "description" => "Beschreibung",
  128. "settings" => "Einstellungen",
  129. "sensors" => "Sensoren",
  130. "actors" => "Aktoren",
  131. "action" => "Wirkung",
  132. "setaction" => "Aktion Setzen",
  133. "unsetaction" => "Aktion Rücksetzen",
  134. "testaction" => "Testen",
  135. "armaction" => "Scharf-Aktion",
  136. "disarmaction" => "Unscharf-Aktion",
  137. "waitaction" => "Warte-Aktion",
  138. "cancelaction" => "Widerruf-Aktion",
  139. "canceled" => "widerrufen durch:",
  140. "alarm" => "Alarm",
  141. "raised" => "ausgelöst durch:",
  142. "alarms" => "Alarmanlage",
  143. "setparms" => "Parameter setzen",
  144. #--
  145. "state" => "Sicherheit",
  146. "unlocked" => "Unverschlossen",
  147. "locked" => "Verschlossen",
  148. "unsecured" => "Nicht Gesichert",
  149. "secured" => "Gesichert",
  150. "protected" => "Geschützt",
  151. "guarded" => "Überwacht"
  152. );
  153. my $alarm_tt;
  154. #########################################################################################
  155. #
  156. # Alarm_Initialize
  157. #
  158. # Parameter hash = hash of device addressed
  159. #
  160. #########################################################################################
  161. sub Alarm_Initialize ($) {
  162. my ($hash) = @_;
  163. $hash->{DefFn} = "Alarm_Define";
  164. $hash->{SetFn} = "Alarm_Set";
  165. $hash->{GetFn} = "Alarm_Get";
  166. $hash->{UndefFn} = "Alarm_Undef";
  167. #$hash->{AttrFn} = "Alarm_Attr";
  168. my $attst = "lockstate:locked,unlocked testbutton:0,1 statedisplay:simple,color,table,none noicons iconmap disarmcolor ".
  169. "armwaitcolor armcolor alarmcolor armdelay armwait armact disarmact cancelact";
  170. for( my $level=0;$level<$alarmno;$level++ ){
  171. $attst .=" level".$level."cond level".$level."start level".$level."end level".$level."autocan level".$level."msg level".$level."onact level".$level."offact ";
  172. }
  173. $hash->{AttrList} = $attst;
  174. if( !defined($alarm_tt) ){
  175. #-- in any attribute redefinition readjust language
  176. my $lang = AttrVal("global","language","EN");
  177. if( $lang eq "DE"){
  178. $alarm_tt = \%alarm_transtable_DE;
  179. }else{
  180. $alarm_tt = \%alarm_transtable_EN;
  181. }
  182. }
  183. $alarmlinkname = $alarm_tt->{"alarms"};
  184. $data{FWEXT}{Alarmx}{LINK} = "?room=".$alarmhiddenroom;
  185. $data{FWEXT}{Alarmx}{NAME} = $alarmlinkname;
  186. $data{FWEXT}{"/Alarm_widget"}{FUNC} = "Alarm_widget";
  187. $data{FWEXT}{"/Alarm_widget"}{FORKABLE} = 0;
  188. return undef;
  189. }
  190. #########################################################################################
  191. #
  192. # Alarm_Define - Implements DefFn function
  193. #
  194. # Parameter hash = hash of device addressed, def = definition string
  195. #
  196. #########################################################################################
  197. sub Alarm_Define ($$) {
  198. my ($hash, $def) = @_;
  199. my $now = time();
  200. my $name = $hash->{NAME};
  201. $hash->{VERSION} = $alarmversion;
  202. #-- readjust language
  203. my $lang = AttrVal("global","language","EN");
  204. if( $lang eq "DE"){
  205. $alarm_tt = \%alarm_transtable_DE;
  206. }else{
  207. $alarm_tt = \%alarm_transtable_EN;
  208. }
  209. readingsSingleUpdate( $hash, "state", "Initialized", 1 );
  210. $alarmhiddenroom = defined($attr{$name}{"hiddenroom"}) ? $attr{$name}{"hiddenroom"} : $alarmhiddenroom;
  211. $alarmpublicroom = defined($attr{$name}{"publicroom"}) ? $attr{$name}{"publicroom"} : $alarmpublicroom;
  212. $data{FWEXT}{Alarmx}{LINK} = "?room=".$alarmhiddenroom;
  213. $data{FWEXT}{Alarmx}{NAME} = $alarmlinkname;
  214. $attr{$name}{"room"} = $alarmhiddenroom;
  215. #$data{FWEXT}{"/Alarm_widget"}{FUNC} = "Alarm_widget";
  216. #$data{FWEXT}{"/Alarm_widget"}{FORKABLE} = 0;
  217. my $date = Alarm_restore($hash,0);
  218. #-- data seems to be ok, restore
  219. if( defined($date) ){
  220. Alarm_restore($hash,1);
  221. Log3 $name,1,"[Alarm_Define] data hash restored from save file with date $date";
  222. #-- intialization
  223. }else{
  224. for( my $i=0;$i<$alarmno;$i++){
  225. $hash->{DATA}{"armstate"}{"level".$i} = "disarmed";
  226. }
  227. Alarm_save($hash);
  228. Log3 $name,1,"[Alarm_Define] data hash is initialized";
  229. }
  230. $modules{Alarm}{defptr}{$name} = $hash;
  231. RemoveInternalTimer($hash);
  232. InternalTimer ($now + 5, 'Alarm_CreateEntry', $hash, 0);
  233. return;
  234. }
  235. #########################################################################################
  236. #
  237. # Alarm_Undef - Implements Undef function
  238. #
  239. # Parameter hash = hash of device addressed, def = definition string
  240. #
  241. #########################################################################################
  242. sub Alarm_Undef ($$) {
  243. my ($hash,$arg) = @_;
  244. my $name = $hash->{NAME};
  245. RemoveInternalTimer($hash);
  246. delete $data{FWEXT}{Alarmx};
  247. if (defined $defs{$name."_weblink"}) {
  248. FW_fC("delete ".$name."_weblink");
  249. Log3 $hash, 3, "[".$name. " V".$alarmversion."]"." Weblink ".$name."_weblink deleted";
  250. }
  251. return undef;
  252. }
  253. #########################################################################################
  254. #
  255. # Alarm_Attr - Implements Attr function
  256. #
  257. # Parameter hash = hash of device addressed, ???
  258. #
  259. #########################################################################################
  260. sub Alarm_Attr($$$) {
  261. my ($cmd, $name, $attrName, $attrVal) = @_;
  262. my $hash = $defs{"$name"};
  263. #-- in any attribute redefinition readjust language
  264. my $lang = AttrVal("global","language","EN");
  265. if( $lang eq "DE"){
  266. $alarm_tt = \%alarm_transtable_DE;
  267. }else{
  268. $alarm_tt = \%alarm_transtable_EN;
  269. }
  270. return;
  271. }
  272. #########################################################################################
  273. #
  274. # Alarm_CreateEntry - Puts the Alarm entry into the FHEM menu
  275. #
  276. # Parameter hash = hash of device addressed
  277. #
  278. #########################################################################################
  279. sub Alarm_CreateEntry($) {
  280. my ($hash) = @_;
  281. my $name = $hash->{NAME};
  282. if (!defined $defs{$name."_weblink"}) {
  283. FW_fC("define ".$name."_weblink weblink htmlCode {Alarm_Html(\"".$name."\")}");
  284. Log3 $hash, 3, "[".$name. " V".$alarmversion."]"." Weblink ".$name."_weblink created";
  285. }
  286. FW_fC("attr ".$name."_weblink room ".$alarmhiddenroom);
  287. foreach my $dn (sort keys %defs) {
  288. if ($defs{$dn}{TYPE} eq "FHEMWEB" && $defs{$dn}{NAME} !~ /FHEMWEB:/) {
  289. my $hr = AttrVal($defs{$dn}{NAME}, "hiddenroom", "");
  290. if (index($hr,$alarmhiddenroom) == -1){
  291. if ($hr eq "") {
  292. FW_fC("attr ".$defs{$dn}{NAME}." hiddenroom ".$alarmhiddenroom);
  293. }else {
  294. FW_fC("attr ".$defs{$dn}{NAME}." hiddenroom ".$hr.",".$alarmhiddenroom);
  295. }
  296. Log3 $hash, 3, "[".$name. " V".$alarmversion."]"." Added hidden room '".$alarmhiddenroom."' to ".$defs{$dn}{NAME};
  297. }
  298. }
  299. }
  300. #-- recover state from stored readings
  301. readingsBeginUpdate($hash);
  302. for( my $level=0;$level<$alarmno;$level++ ){
  303. my $val = $hash->{DATA}{"armstate"}{"level".$level};
  304. readingsBulkUpdate( $hash, "level".$level, $val);
  305. }
  306. my $mga = Alarm_getstate($hash);
  307. readingsBulkUpdate( $hash, "state", $mga);
  308. readingsEndUpdate( $hash,1 );
  309. }
  310. #########################################################################################
  311. #
  312. # Alarm_Set - Implements the Set function
  313. #
  314. # Parameter hash = hash of device addressed
  315. #
  316. #########################################################################################
  317. sub Alarm_Set($@) {
  318. my ( $hash, $name, $cmd, @args ) = @_;
  319. if ( $cmd =~ /^(cancel|arm|disarm)(ed)?$/ ) {
  320. return "[Alarm] Invalid argument to set $cmd, must be numeric"
  321. if ( $args[0] !~ /\d+/ );
  322. return "[Alarm] Invalid argument to set $cmd, must be 0 < arg < $alarmno"
  323. if ( ($args[0] >= $alarmno)||($args[0]<0) );
  324. if( $cmd =~ /^cancel(ed)?$/ ){
  325. Alarm_Exec($name,$args[0],"web","button","cancel");
  326. }elsif ( $cmd =~ /^arm(ed)?$/ ) {
  327. Alarm_Arm($name,$args[0],"web","button","arm");
  328. }elsif ( $cmd =~ /^disarm(ed)?$/ ){
  329. Alarm_Arm($name,$args[0],"web","button","disarm");
  330. }else{
  331. return "[Alarm] Invalid argument set $cmd";
  332. }
  333. return;
  334. #-----------------------------------------------------------
  335. } elsif ( $cmd =~ /^lock(ed)?$/ ) {
  336. readingsSingleUpdate( $hash, "lockstate", "locked", 0 );
  337. return;
  338. #-----------------------------------------------------------
  339. } elsif ( $cmd =~ /^unlock(ed)?$/ ) {
  340. readingsSingleUpdate( $hash, "lockstate", "unlocked", 0 );
  341. return;
  342. #-----------------------------------------------------------
  343. } elsif ( $cmd =~ /^save/ ) {
  344. return Alarm_save($hash);
  345. #-----------------------------------------------------------
  346. } elsif ( $cmd =~ /^restore/ ) {
  347. return Alarm_restore($hash,1);
  348. } else {
  349. my $str = join(",",(0..($alarmno-1)));
  350. return "[Alarm] Unknown argument " . $cmd . ", choose one of canceled:$str armed:$str disarmed:$str locked:noArg unlocked:noArg save:noArg restore:noArg";
  351. }
  352. }
  353. #########################################################################################
  354. #
  355. # Alarm_Get - Implements the Get function
  356. #
  357. # Parameter hash = hash of device addressed
  358. #
  359. #########################################################################################
  360. sub Alarm_Get($@) {
  361. my ($hash, @a) = @_;
  362. my $res = "";
  363. my $arg = (defined($a[1]) ? $a[1] : "");
  364. if ($arg eq "version") {
  365. return "Alarm.version => $alarmversion";
  366. } else {
  367. return "Unknown argument $arg choose one of version:noArg";
  368. }
  369. }
  370. #########################################################################################
  371. #
  372. # Alarm_getsettings - Helper function to assemble the alarm settings for a device
  373. #
  374. # Parameter hash = hash of Alarm device
  375. # dev = name of device addressed
  376. #
  377. #########################################################################################
  378. sub Alarm_getsettings($$$){
  379. my ($hash,$dev,$type) = @_;
  380. my $chg = 0;
  381. my $avl = AttrVal($dev, "alarmSettings","|||0:00");
  382. $avl = Alarm_escape($avl,"beforesplit");
  383. my @aval = split('\|',$avl,4);
  384. $aval[1] = Alarm_escape($aval[1],"aftersplit");
  385. $aval[2] = Alarm_escape($aval[2],"aftersplit");
  386. if( $type eq "Actor"){
  387. #-- position 0:set by level, 1:set func, 2:unset func, 3:delay
  388. if( $aval[0] eq "" || $aval[1] eq "" ){
  389. Log3 $hash, 1, "[Alarm] Settings $avl incomplete for alarmActor $dev";
  390. }
  391. #-- check if empty unset action
  392. if( !defined($aval[3]) ){
  393. $aval[3] = $aval[2];
  394. $aval[2] = "";
  395. }
  396. #-- check delay time
  397. if( $aval[3] =~ /^\d+$/ ){
  398. if( $aval[3] > 3559 ){
  399. Log3 $hash, 1, "[Alarm] Delay time $aval[3] for alarmActor $dev to large as single number, maximum 3559 seconds";;
  400. $aval[3] = "59:59";
  401. }else{
  402. my $min = int($aval[3]/60);
  403. my $sec = $aval[3]%60;
  404. $aval[3] = sprintf("%02d:%02d",$min,$sec);
  405. }
  406. $chg = 1;
  407. }elsif( $aval[3] !~ /^(\d\d:)?\d?\d:\d\d/ ){
  408. Log3 $hash, 1, "[Alarm] Delay time $aval[3] ill defined for alarmActor $dev";
  409. $aval[3] = "0:00";
  410. $chg = 1;
  411. }
  412. if( $chg==1 ){
  413. CommandAttr(undef,$dev.' alarmSettings '.join('|',@aval));
  414. }
  415. }
  416. return @aval;
  417. }
  418. #########################################################################################
  419. #
  420. # Alarm_escape - Helper function to de-escape and to escape action parameters
  421. #
  422. # Parameter hash = hash of Alarm device
  423. # dev = name of device addressed
  424. #
  425. #########################################################################################
  426. sub Alarm_escape($$){
  427. my ($str,$type) = @_;
  428. if( $type eq "beforesplit"){
  429. $str =~ s/\\\|/%7C/g;
  430. }elsif( $type eq "aftersplit"){
  431. $str =~ s/\\//g;
  432. $str =~ s/%7C/\|/g;
  433. }elsif( $type eq "beforehtml"){
  434. $str =~ s/\\//g;
  435. $str =~ s/\|/\\\|/g;
  436. }
  437. return $str;
  438. }
  439. #########################################################################################
  440. #
  441. # Alarm_save
  442. #
  443. # Parameter hash = hash of the Alarm device
  444. #
  445. #########################################################################################
  446. sub Alarm_save($) {
  447. my ($hash) = @_;
  448. my $date = TimeNow();
  449. $hash->{DATA}{"savedate"} = $date;
  450. readingsSingleUpdate( $hash, "savedate", $date, 1 );
  451. my $jhash0 = toJSON($hash->{DATA});
  452. my $error = FileWrite("AlarmFILE",$jhash0);
  453. #Log 1,"[Alarm_save] error=$error";
  454. return;
  455. }
  456. #########################################################################################
  457. #
  458. # Alarm_restore
  459. #
  460. # Parameter hash = hash of the Alarm device
  461. #
  462. #########################################################################################
  463. sub Alarm_restore($$) {
  464. my ($hash,$doit) = @_;
  465. my $name = $hash->{NAME};
  466. my ($error,$jhash0) = FileRead("AlarmFILE");
  467. if( defined($error) && $error ne "" ){
  468. Log3 $name,1,"[Alarm_restore] read error=$error";
  469. return undef;
  470. }
  471. my $json = JSON->new->utf8;
  472. my $jhash1 = eval{ $json->decode( $jhash0 ) };
  473. my $date = $jhash1->{"savedate"};
  474. #-- just for the first time, reading an old savefile
  475. $date = localtime(time)
  476. if( !defined($date));
  477. readingsSingleUpdate( $hash, "savedate", $date, 0 );
  478. if( $doit==1 ){
  479. $hash->{DATA} = {%{$jhash1}};
  480. Log3 $name,5,"[Alarm_restore] Data hash restored from save file with date ".$date;
  481. return 1;
  482. }else{
  483. return $date;
  484. }
  485. }
  486. #########################################################################################
  487. #
  488. # Alarm_Test - Test an actor
  489. #
  490. # Parameter name = name of the Alarm definition
  491. # cmd =
  492. #
  493. #########################################################################################
  494. sub Alarm_Test($$){
  495. my ($name,$cmd) = @_;
  496. my $hash = $defs{$name};
  497. $cmd =~ s/\$NAME/Gerät/g;
  498. $cmd =~ s/\$EVENT/Event/g;
  499. $cmd =~ s/\$SHORT/Kurznachricht/g;
  500. #for( my $i=1;$i<= int(@evtpart);$i++){
  501. # $cmd =~ s/\$EVTPART$i/$evtpart[$i-1]/g;
  502. #}
  503. fhem($cmd);
  504. }
  505. #########################################################################################
  506. #
  507. # Alarm_Exec - Execute the Alarm
  508. #
  509. # Parameter name = name of the Alarm definition
  510. # level = Alarm level
  511. # dev = name of the device calling the alarm
  512. # evt = event calling the alarm
  513. # act = action - "on" or "off"
  514. #
  515. #########################################################################################
  516. sub Alarm_Exec($$$$$){
  517. my ($name,$level,$dev,$evt,$act) = @_;
  518. my $hash = $defs{$name};
  519. my $xec = $hash->{DATA}{"armstate"}{"level".$level};
  520. my $xac = $hash->{READINGS}{"level".$level}{VAL};
  521. my $msg = '';
  522. my $cmd;
  523. my $mga;
  524. my $dly;
  525. my @sta;
  526. #Log3 $hash,1,"[Alarm $level] Exec called with dev $dev evt $evt act $act]";
  527. return
  528. if ($dev eq 'global');
  529. return
  530. if (!defined($level));
  531. #-- raising the alarm
  532. if( $act eq "on" ){
  533. #-- only if this level is armed and not yet active
  534. if( ($xec eq "armed") && ($xac eq "armed") ){
  535. #-- check for condition
  536. my $cond = AttrVal($name, "level".$level."cond", 1);
  537. $cond =~ s/\$NAME/$dev/g;
  538. if( eval($cond) ne "1" ){
  539. Log3 $hash,1,"[Alarm $level] Cannot be executed due to condition {$cond} not equal to 1 for level".$level."cond";
  540. return;
  541. }
  542. #-- check for time
  543. my $start = AttrVal($name, "level".$level."start", "0:00");
  544. if( index($start, '{') != -1){
  545. $start = eval($start);
  546. }
  547. my @st = split(':',$start);
  548. if( (int(@st)>3) || (int(@st)<2) || ($st[0] > 23) || ($st[0] < 0) || ($st[1] > 59) || ($st[1] < 0) ){
  549. Log3 $hash,1,"[Alarm $level] Cannot be executed due to wrong time spec $start for level".$level."start";
  550. return;
  551. }
  552. my $end = AttrVal($name, "level".$level."end", "23:59");
  553. if( index($end, '{') != -1){
  554. $end = eval($end);
  555. }
  556. my @et = split(':',$end);
  557. if( (int(@et)>3) || (int(@et)<2) || ($et[0] > 23) || ($et[0] < 0) || ($et[1] > 59) || ($et[1] < 0) ){
  558. Log3 $hash,1,"[Alarm $level] Cannot be executed due to wrong time spec $end for level".$level."end";
  559. return;
  560. }
  561. my $dur = AttrVal($name, "level".$level."autocan", "0:00");
  562. if( index($dur, '{') != -1){
  563. $dur = eval($dur);
  564. }
  565. my @dt = split(':',$dur);
  566. if( (int(@dt)>3) || (int(@dt)<2) || ($dt[0] > 23) || ($dt[0] < 0) || ($dt[1] > 59) || ($dt[1] < 0) ){
  567. Log3 $hash,1,"[Alarm $level] Cannot be executed due to wrong time spec $dur for level".$level."autocan";
  568. return;
  569. }
  570. my $stp = $st[0]*60+$st[1];
  571. my $etp = $et[0]*60+$et[1];
  572. my $dtp = $dt[0]*60+$dt[1];
  573. my ($sec, $min, $hour, $day, $month, $year, $wday, $yday, $isdst) = localtime(time);
  574. my $ntp = $hour*60+$min;
  575. if( (($stp < $etp) && ($ntp <= $etp) && ($ntp >= $stp)) || (($stp > $etp) && (($ntp <= $etp) || ($ntp >= $stp))) ){
  576. #-- raised by sensor (attribute values have been controlled in CreateNotifiers)
  577. my $avl = AttrVal($dev, "alarmSettings","");
  578. $avl =~ s/\\\|/%7C/g;
  579. @sta = split('\|',$avl);
  580. for( my $i=0;$i<4;$i++ ){
  581. $sta[$i] =~ s/\\//g;
  582. $sta[$i] =~ s/%7C/|/g;
  583. }
  584. if( $sta[2] ){
  585. $mga = $sta[2]." ".AttrVal($name, "level".$level."msg", 0);
  586. #-- replace some parts
  587. my @evtpart = split(" ",$evt);
  588. $mga =~ s/\$NAME/$dev/g;
  589. $mga =~ s/\$EVENT/$evt/g;
  590. for( my $i=1;$i<= int(@evtpart);$i++){
  591. $mga =~ s/\$EVTPART$i/$evtpart[$i-1]/g;
  592. }
  593. #-- readings
  594. readingsSingleUpdate( $hash, "level".$level,$dev,1 );
  595. readingsSingleUpdate( $hash, "short", $mga, 1);
  596. $msg = Alarm_getstate($hash)." ".$mga;
  597. readingsSingleUpdate( $hash, "state", $msg, 1 );
  598. $msg = "[Alarm $level] raised from device $dev with event $evt";
  599. #-- set up timer for auto cancel
  600. if( $dur ne "0:00" ){
  601. my $cmd = "alarm".$level.".autocancel at +$dur {Alarm_Exec('$name',$level,'".$alarm_tt->{"autocan"}."','dummy','off')}";
  602. CommandDefine(undef,$cmd);
  603. CommandAttr (undef,'alarm'.$level.'.autocancel room '.$alarmpublicroom);
  604. CommandAttr (undef,'alarm'.$level.'.autocancel group alarmHelper');
  605. }
  606. #-- calling actors AFTER state update
  607. $cmd = AttrVal($name, "level".$level."onact", 0);
  608. $cmd =~ s/\$NAME/$dev/g;
  609. $cmd =~ s/\$EVENT/$evt/g;
  610. $cmd =~ s/\$SHORT/$mga/g;
  611. for( my $i=1;$i<= int(@evtpart);$i++){
  612. $cmd =~ s/\$EVTPART$i/$evtpart[$i-1]/g;
  613. }
  614. fhem($cmd);
  615. Log3 $hash,3,$msg;
  616. }else{
  617. $msg = "[Alarm $level] not raised, alarmSensor $dev has wrong settings";
  618. Log3 $hash,1,$msg;
  619. }
  620. }else{
  621. $msg = "[Alarm $level] not raised, not in time slot";
  622. Log3 $hash,5,$msg;
  623. }
  624. }else{
  625. $msg = "[Alarm $level] not raised, not armed or already active";
  626. Log3 $hash,5,$msg;
  627. }
  628. }elsif( ($act eq "off")||($act eq "cancel") ){
  629. #-- only if this level is active
  630. if( ($xac ne "armed")&&($xac ne "disarmed") ){
  631. #-- TODO: ohne intAt auskommen
  632. #-- deleting all running ats
  633. CommandDelete(undef,'alarm'.$level.'.autocancel');
  634. $dly = sprintf("alarm%1ddly",$level);
  635. foreach my $d ( devspec2array("NAME=alarm.dly.*")) {
  636. Log3 $hash,1,"[Alarm] Killing delayed action $d";
  637. CommandDelete(undef,$d);
  638. }
  639. #-- replace some parts
  640. my @evtpart = split(" ",$evt);
  641. #-- calling actors BEFORE state update
  642. $cmd = AttrVal($name, "level".$level."offact", 0);
  643. $cmd =~ s/\$NAME/$dev/g;
  644. $cmd =~ s/\$EVENT/$evt/g;
  645. $cmd =~ s/\$SHORT/$mga/g;
  646. for( my $i=1;$i<= int(@evtpart);$i++){
  647. $cmd =~ s/\$EVTPART$i/$evtpart[$i-1]/g;
  648. }
  649. fhem($cmd);
  650. $cmd = AttrVal($name, "cancelact", 0);
  651. fhem($cmd)
  652. if( $cmd );
  653. #-- readings - arm status does not change
  654. readingsSingleUpdate( $hash, "level".$level,"canceled",1);
  655. readingsSingleUpdate( $hash, "level".$level,"armed",1);
  656. readingsSingleUpdate( $hash, "short", "", 0);
  657. $mga = Alarm_getstate($hash)." ".$alarm_tt->{"canceled"}." ".$dev;
  658. readingsSingleUpdate( $hash, "state", $mga, 1 );
  659. $msg = "[Alarm $level] canceled from device $dev";
  660. Log3 $hash,3,$msg;
  661. }
  662. }else{
  663. Log3 $hash,3,"[Alarm $level] Exec called with act=$act";
  664. }
  665. #return $msg;
  666. }
  667. #########################################################################################
  668. #
  669. # Alarm_Arm - Arm the Alarm
  670. #
  671. # Parameter name = name of the Alarm definition
  672. # level = Alarm level
  673. # dev = name of the device calling the alarm
  674. # evt = Event of the device
  675. # act = action - "armed" or "disarmed"
  676. #
  677. #########################################################################################
  678. sub Alarm_Arm($$$$$){
  679. my ($name,$level,$dev,$evt,$act) = @_;
  680. my $hash = $defs{$name};
  681. my $xec = $hash->{DATA}{"armstate"}{"level".$level};
  682. my $xac = $hash->{READINGS}{"level".$level}{VAL};
  683. my $msg = '';
  684. my $mga;
  685. my $cmd;
  686. #-- arming the alarm
  687. if( ($act eq "arm") && ( $xac ne "armed") ){
  688. my $xdl = AttrVal($name, "armdelay", 0);
  689. my $cmdwait = AttrVal($name, "armwait", 0);
  690. #-- immediate arming
  691. if( ($xdl eq '')||($xdl eq '0:00')||($xdl eq '00:00')||($evt eq "delay") ){
  692. my $cmdact = AttrVal($name, "armact", 0);
  693. #-- update state display
  694. $hash->{DATA}{"armstate"}{"level".$level} = "armed";
  695. readingsSingleUpdate( $hash, "level".$level,"armed",1 );
  696. readingsSingleUpdate( $hash, "state", Alarm_getstate($hash)." ".$hash->{READINGS}{"short"}{VAL}, 1 );
  697. #-- save new state
  698. Alarm_save($hash);
  699. #--transform commands from fhem to perl level
  700. my @cmdactarr = split(/;/,$cmdact);
  701. my $cmdactf;
  702. if( int(@cmdactarr) == 1 ){
  703. fhem("$cmdact");
  704. }else{
  705. for(my $i=0;$i<int(@cmdactarr);$i++){
  706. fhem("$cmdactarr[$i]");
  707. }
  708. }
  709. $msg = "[Alarm $level] armed from alarmSensor $dev with event $evt";
  710. Log3 $hash,3,$msg;
  711. } elsif( $xdl =~ /([0-9])?:([0-5][0-9])?/ ){
  712. #-- update state display
  713. $hash->{DATA}{"armstate"}{"level".$level} = "armed";
  714. readingsSingleUpdate( $hash, "level".$level,"armwait",1 );
  715. readingsSingleUpdate( $hash, "state", Alarm_getstate($hash)." ".$hash->{READINGS}{"short"}{VAL}, 1 );
  716. #-- save new state
  717. Alarm_save($hash);
  718. #-- compose commands TODO
  719. $cmd = sprintf("defmod alarm%1d.arm.dly at +00:%02d:%02d {Alarm_Arm(\"%s\",%1d,\"%s\",\"delay\",\"arm\")}",
  720. $level,$1,$2,$name,$level,$dev);
  721. $msg = "[Alarm $level] will be armed from alarmSensor $dev with event $evt, delay $xdl";
  722. #-- define new delayed arm
  723. fhem($cmd);
  724. #-- execute armwait action
  725. fhem($cmdwait);
  726. Log3 $hash,1,$msg;
  727. }else{
  728. $msg = "[Alarm $level] cannot be armed due to wrong delay timespec";
  729. Log3 $hash,1,$msg;
  730. }
  731. #-- disarming implies canceling as well
  732. }elsif( ($act eq "disarm") && ($xec ne "disarmed")) {
  733. #-- delete stale delayed arm
  734. if( defined $defs{'alarm'.$level.'.arm.dly'}){
  735. fhem('delete alarm'.$level.'.arm.dly' )
  736. #-- really kill active alarm
  737. }else{
  738. Alarm_Exec($name,$level,"program","disarm","cancel");
  739. }
  740. $hash->{DATA}{"armstate"}{"level".$level} = "disarmed";
  741. #-- update state display
  742. readingsSingleUpdate( $hash, "level".$level,"disarmed",1 );
  743. readingsSingleUpdate( $hash, "state", Alarm_getstate($hash)." ".$hash->{READINGS}{"short"}{VAL}, 1 );
  744. #-- save new state
  745. Alarm_save($hash);
  746. #--
  747. $msg = "[Alarm $level] disarmed from alarmSensor $dev with event $evt";
  748. $cmd = AttrVal($name, "disarmact", 0);
  749. fhem("define alarm".$level.".disarm.T at +00:00:03 ".$cmd)
  750. if( $cmd );
  751. }
  752. return $msg;
  753. }
  754. #########################################################################################
  755. #
  756. # Alarm_CreateNotifiers - Create the notifiers
  757. #
  758. # Parameter name = name of the Alarm definition
  759. #
  760. #########################################################################################
  761. sub Alarm_CreateNotifiers($){
  762. my ($name) = @_;
  763. my $ret = "";
  764. my $res;
  765. my $hash = $defs{$name};
  766. #-- don't do anything if locked
  767. if( $hash->{READINGS}{"lockstate"}{VAL} ne "unlocked" ){
  768. Log3 $hash, 1, "[Alarm] State locked, cannot create new notifiers";
  769. return "State locked, cannot create new notifiers";
  770. }
  771. for( my $level=0;$level<$alarmno;$level++ ){
  772. #-- delete old defs in any case
  773. fhem('delete alarm'.$level.'.on.N' )
  774. if( defined $defs{'alarm'.$level.'.on.N'});
  775. fhem('delete alarm'.$level.'.off.N' )
  776. if( defined $defs{'alarm'.$level.'.off.N'});
  777. fhem('delete alarm'.$level.'.arm.N' )
  778. if( defined $defs{'alarm'.$level.'.arm.N'});
  779. fhem('delete alarm'.$level.'.disarm.N' )
  780. if( defined $defs{'alarm'.$level.'.disarm.N'});
  781. my $start = AttrVal($name, "level".$level."start", 0);
  782. my @st;
  783. if( index($start,'{')!=-1 ){
  784. Log3 $hash,1,"[Alarm $level] perl function $start detected for level".$level."start, currently the function gives ".eval($start);
  785. }else{
  786. @st = split(':',($start ne '') ? $start :'0:00');
  787. if( (int(@st)!=2) || ($st[0] > 23) || ($st[0] < 0) || ($st[1] > 59) || ($st[1] < 0) ){
  788. Log3 $hash,1,"[Alarm $level] Will not be executed due to wrong time spec $start for level".$level."start";
  789. next;
  790. }
  791. }
  792. my $end = AttrVal($name, "level".$level."end", 0);
  793. my @et;
  794. if( index($end,'{')!=-1 ){
  795. Log3 $hash,1,"[Alarm $level] perl function $end detected for level".$level."end, currently the function gives ".eval($end);
  796. }else{
  797. @et = split(':',($end ne '') ? $end :'23:59');
  798. if( (int(@et)!=2) || ($et[0] > 23) || ($et[0] < 0) || ($et[1] > 59) || ($et[1] < 0) ){
  799. Log3 $hash,1,"[Alarm $level] Will not be executed due to wrong time spec $end for level".$level."end";
  800. next;
  801. }
  802. }
  803. #-- now set up the command for cancel alarm, and contained in this loop all other notifiers as well
  804. my $cmd = '';
  805. foreach my $d (keys %defs ) {
  806. next if(IsIgnored($d));
  807. if( AttrVal($d, "alarmDevice","") eq "Sensor" ) {
  808. my $avl = AttrVal($d, "alarmSettings","");
  809. $avl =~ s/\\\|/%7C/g;
  810. my @aval = split('\|',$avl);
  811. if( int(@aval) != 4){
  812. Log3 $hash,1, "[Alarm $level] Settings $avl incomplete for alarmSensor $d";
  813. next;
  814. }
  815. for( my $i=0;$i<4;$i++ ){
  816. $aval[$i] =~ s/\\//g;
  817. $aval[$i] =~ s/%7C/\|/g;
  818. }
  819. #-- workaround: replace any space by \s
  820. $aval[1] =~ s/\s/\\s/g;
  821. if( (index($aval[0],"alarm".$level) != -1) && ($aval[1] ne "") && ($aval[3] eq "off")){
  822. $cmd .= '('.$aval[1].')|';
  823. Log3 $hash,5,"[Alarm $level] Adding sensor $d to cancel notifier";
  824. }
  825. }
  826. }
  827. if( $cmd eq '' ){
  828. Log3 $hash,1,"[Alarm $level] No \"Cancel\" device defined, level will be ignored";
  829. } else {
  830. $cmd = substr($cmd,0,length($cmd)-1);
  831. $cmd = 'alarm'.$level.'.off.N notify '.$cmd;
  832. $cmd .= ' {main::Alarm_Exec("'.$name.'",'.$level.',"$NAME","$EVENT","off")}';
  833. CommandDefine(undef,$cmd);
  834. CommandAttr (undef,'alarm'.$level.'.off.N room '.$alarmpublicroom);
  835. CommandAttr (undef,'alarm'.$level.'.off.N group alarmNotifier');
  836. Log3 $hash,3,"[Alarm $level] Created cancel notifier";
  837. #-- now set up the command for raising alarm - only if cancel exists
  838. $cmd = '';
  839. my $cmdarm = "";
  840. my $cmddisarm = "";
  841. foreach my $d (sort keys %defs ) {
  842. next if(IsIgnored($d));
  843. if( AttrVal($d, "alarmDevice","") eq "Sensor" ) {
  844. my $avl = AttrVal($d, "alarmSettings","");
  845. my @aval = split('\|',$avl);
  846. if( int(@aval) != 4){
  847. Log3 $hash, 1, "[Alarm $level] Settings $avl incomplete for alarmSensor $d";
  848. next;
  849. }
  850. if( index($aval[0],"alarm".$level) != -1){
  851. if( ($aval[1] ne "") && ($aval[3] eq "on") ){
  852. $cmd .= '('.$aval[1].')|';
  853. Log3 $hash,5,"[Alarm $level] Adding alarmSensor $d to raise notifier";
  854. }elsif( ($aval[1] ne "") && ($aval[3] eq "arm") ){
  855. $cmdarm .= '('.$aval[1].')|';
  856. Log3 $hash,5,"[Alarm $level] Adding alarmSensor $d to arm notifier";
  857. }elsif( ($aval[1] ne "") && ($aval[3] eq "disarm") ){
  858. $cmddisarm .= '('.$aval[1].')|';
  859. Log3 $hash,5,"[Alarm $level] Adding alarmSensor $d to disarm notifier";
  860. }
  861. }
  862. }
  863. }
  864. #-- raise notifier
  865. if( $cmd eq '' ){
  866. CommandAttr(undef,$name.' level'.$level.'onact 1');
  867. CommandAttr(undef,$name.' level'.$level.'offact 1');
  868. Log3 $hash,1,"[Alarm $level] No \"Raise\" device defined";
  869. } else {
  870. $cmd = substr($cmd,0,length($cmd)-1);
  871. $cmd = 'alarm'.$level.'.on.N notify '.$cmd;
  872. $cmd .= ' {main::Alarm_Exec("'.$name.'",'.$level.',"$NAME","$EVENT","on")}';
  873. CommandDefine(undef,$cmd);
  874. CommandAttr (undef,'alarm'.$level.'.on.N room '.$alarmpublicroom);
  875. CommandAttr (undef,'alarm'.$level.'.on.N group alarmNotifier');
  876. Log3 $hash,3,"[Alarm $level] Created raise notifier";
  877. #-- now set up the list of actors
  878. $cmd = '';
  879. my $cmd2 = '';
  880. my $nonum = 0;
  881. foreach my $d (sort keys %defs ) {
  882. next if(IsIgnored($d));
  883. if( AttrVal($d, "alarmDevice","") eq "Actor" ) {
  884. my @aval = Alarm_getsettings($hash,$d,"Actor");
  885. if( int(@aval) != 4){
  886. Log3 $hash, 3, "[Alarm $level] Settings incomplete for alarmActor $d";
  887. next;
  888. }
  889. if( index($aval[0],"alarm".$level) != -1 ){
  890. #-- activate without delay
  891. if(( $aval[3] eq "" )||($aval[3] =~ /(00:)?0?0:00/)){
  892. $cmd .= $aval[1].';';
  893. #-- activate with delay
  894. } else {
  895. $nonum++;
  896. my @tarr = split(':',$aval[3]);
  897. if( int(@tarr) == 2){
  898. $cmd .= sprintf('defmod alarm%1ddly%1d at +00:%02d:%02d %s;',$level,$nonum,$tarr[0],$tarr[1],$aval[1]);
  899. }elsif( int(@tarr) == 3){
  900. $cmd .= sprintf('defmod alarm%1ddly%1d at +%02d:%02d:%02d %s;',$level,$nonum,$tarr[0],$tarr[1],$tarr[2],$aval[1]);
  901. }else{
  902. Log3 $name,1,"[Alarm $level] Invalid delay specification for actor $d, skipped";
  903. $cmd .= $aval[1].';';
  904. }
  905. }
  906. $cmd2 .= $aval[2].';'
  907. if( $aval[2] ne '' );
  908. Log3 $hash,5,"[Alarm $level] Adding actor $d to action list";
  909. }
  910. }
  911. }
  912. if( $cmd ne '' ){
  913. CommandAttr(undef,$name.' level'.$level.'onact '.$cmd);
  914. CommandAttr(undef,$name.' level'.$level.'offact '.$cmd2);
  915. Log3 $hash,5,"[Alarm $level] Added on/off actors to $name";
  916. } else {
  917. CommandAttr(undef,$name.' level'.$level.'onact 1');
  918. CommandAttr(undef,$name.' level'.$level.'offact 1');
  919. Log3 $hash,5,"[Alarm $level] Adding on/off actors not possible";
  920. }
  921. #-- arm notifier - optional, but only in case the alarm may be raised
  922. if( $cmdarm ne '' ){
  923. $cmdarm = substr($cmdarm,0,length($cmdarm)-1);
  924. $cmdarm = 'alarm'.$level.'.arm.N notify '.$cmdarm;
  925. $cmdarm .= ' {main::Alarm_Arm("'.$name.'",'.$level.',"$NAME","$EVENT","arm")}';
  926. CommandDefine(undef,$cmdarm);
  927. CommandAttr (undef,'alarm'.$level.'.arm.N room '.$alarmpublicroom);
  928. CommandAttr (undef,'alarm'.$level.'.arm.N group alarmNotifier');
  929. Log3 $hash,3,"[Alarm $level] Created arm notifier";
  930. }
  931. #-- disarm notifier - optional, but only in case the alarm may be raised
  932. if( $cmddisarm ne '' ){
  933. $cmddisarm = substr($cmddisarm,0,length($cmddisarm)-1);
  934. $cmddisarm = 'alarm'.$level.'.disarm.N notify '.$cmddisarm;
  935. $cmddisarm .= ' {main::Alarm_Arm("'.$name.'",'.$level.',"$NAME","$EVENT","disarm")}';
  936. CommandDefine(undef,$cmddisarm);
  937. CommandAttr (undef,'alarm'.$level.'.disarm.N room '.$alarmpublicroom);
  938. CommandAttr (undef,'alarm'.$level.'.disarm.N group alarmNotifier');
  939. Log3 $hash,3,"[Alarm $level] Created disarm notifier";
  940. }
  941. }
  942. }
  943. }
  944. return "Created alarm notifiers";
  945. }
  946. #########################################################################################
  947. #
  948. # Alarm_getstate - Helper function to assemble a state display
  949. #
  950. # Parameter hash = hash of device addressed
  951. #
  952. #########################################################################################
  953. sub Alarm_getstate($) {
  954. my ($hash) = @_;
  955. my $name = $hash->{NAME};
  956. my $res = '';
  957. my $type = AttrVal($hash->{NAME},"statedisplay",0);
  958. my $val;
  959. #--------------------------
  960. if( $type eq "simple" ){
  961. for( my $level=0;$level<$alarmno;$level++ ){
  962. $val = $hash->{READINGS}{"level".$level}{VAL};
  963. if( $val eq "disarmed" ){
  964. $res .= '-';
  965. }elsif( $val eq "armwait" ){
  966. $res .= 'o';
  967. }elsif( $val eq "armed" ){
  968. $res .= 'O';
  969. }else{
  970. $res .= 'X';
  971. }
  972. }
  973. #--------------------------
  974. }else{
  975. my $dac = AttrVal($name,"disarmcolor","lightgray");
  976. my $ac = AttrVal($name,"armcolor","#53f3c7");
  977. my $awc = AttrVal($name,"armwaitcolor","#ffe658");
  978. my $alc = AttrVal($name,"alarmcolor","#fd5777");
  979. if( $type eq "color" ){
  980. $res = '<span style="color:'.$dac.'">';
  981. for( my $level=0;$level<$alarmno;$level++ ){
  982. $val = $hash->{READINGS}{"level".$level}{VAL};
  983. if( $val eq "disarmed" ){
  984. $res .= ' '.$level;
  985. }elsif( $val eq "armwait" ){
  986. $res .= ' <span style="width:1ex;font-weight:bold;color:'.$awc.'">'.$level.'</span>';
  987. }elsif( $val eq "armed" ){
  988. $res .= ' <span style="width:1ex;font-weight:bold;color:'.$ac.'">'.$level.'</span>';
  989. }else{
  990. $res .= ' <span style="width:1ex;font-weight:bold;color:'.$alc.'">'.$level.'</span>';
  991. }
  992. }
  993. $res.='</span>';
  994. #--------------------------
  995. }elsif( $type eq "table" ){
  996. $res = '<table><tr style="height:1ex">';
  997. for( my $level=0;$level<$alarmno;$level++ ){
  998. $val = $hash->{READINGS}{"level".$level}{VAL};
  999. if( $val eq "disarmed" ){
  1000. $res .= '<td style="width:1ex;background-color:'.$dac.'"/>';
  1001. }elsif( $val eq "armwait" ){
  1002. $res .= '<td style="width:1ex;background-color:'.$awc.'"/>';
  1003. }elsif( $val eq "armed" ){
  1004. $res .= '<td style="width:1ex;background-color:'.$ac.'"/>';
  1005. }else{
  1006. $res .= '<td style="width:1ex;background-color:'.$alc.'"/>';
  1007. }
  1008. }
  1009. $res.='</tr></table>';
  1010. }
  1011. }
  1012. return $res;
  1013. }
  1014. #########################################################################################
  1015. #
  1016. # Alarm_widget - returns animated SVG-code for the Alarm page
  1017. #
  1018. # Parameter name = name of the Alarm definition
  1019. #
  1020. #########################################################################################
  1021. sub Alarm_widget($){
  1022. my ($arg) = @_;
  1023. my $name = $FW_webArgs{name};
  1024. my $sizep = $FW_webArgs{size};
  1025. my $gstate = ( $FW_webArgs{gstate} ? $FW_webArgs{gstate} : "disarmed");
  1026. my $dstate = ( $FW_webArgs{dstate} ? $FW_webArgs{dstate} : "--------");
  1027. my $inline = 0;
  1028. #-- no webarg, check direct parameter. TODO: order
  1029. if( !defined($name) || $name eq "" ){
  1030. if( $arg =~ /^name=(\w*)&gstate=(.+)&dstate=(.*)&size=(\d+x\d+)/ ){
  1031. $name = $1;
  1032. $gstate = $2;
  1033. $dstate = $3;
  1034. $sizep = $4;
  1035. $inline = 1;
  1036. }
  1037. }
  1038. Log 5,"[Alarm_widget] name=$name gstate=$gstate dstate=$dstate sizep=$sizep";
  1039. $name =~ s/'//g;
  1040. my @size=split('x',($sizep ? $sizep : '60x80'));
  1041. my ($fillcolor,$fillcolor2);
  1042. my $dac = AttrVal($name,"disarmcolor","lightgray");
  1043. my $ac = AttrVal($name,"armcolor","#53f3c7");
  1044. my $awc = AttrVal($name,"armwaitcolor","#ffe658");
  1045. my $alc = AttrVal($name,"alarmcolor","#fd5777");
  1046. if($gstate eq "disarmed"){
  1047. $fillcolor = AttrVal($name,"disarmcolor",$dac);
  1048. $fillcolor2 = "white";
  1049. }elsif($gstate eq "armed"){
  1050. $fillcolor = AttrVal($name,"armcolor",$ac);
  1051. $fillcolor2 = "white";
  1052. }elsif($gstate eq "mixed"){
  1053. $fillcolor = AttrVal($name,"armwaitcolor",$awc);
  1054. $fillcolor2 = "white";
  1055. }else{
  1056. $fillcolor = AttrVal($name,"alarmcolor",$alc);
  1057. $fillcolor2 = $fillcolor;
  1058. }
  1059. my $hash = $defs{$name};
  1060. my $id = $defs{$name}{NR};
  1061. my $ret="";
  1062. $ret = "<svg viewBox=\"0 0 60 80\" preserveAspectRatio=\"xMidYMin slice\" width=\"100%\" style=\"padding-bottom: 92%; height: 1px; overflow: visible\">";
  1063. $ret .= "<g id=\"alarmicon\" transform=\"translate(20,5) scale(1.5,1.5)\">".
  1064. "<path class=\"alarmst_b\" id=\"alarmst_ib\" d=\"M 25 6 C 23.354545 6 22 7.3545455 22 9 L 22 10.365234 C 17.172775 11.551105 14.001117 15.612755 14.001953 21.0625 ".
  1065. "L 14.001953 28.863281 C 14.001953 31.035281 12.718469 33.494563 11.980469 34.726562 L 10.167969 37.445312 C 9.9629687 37.751312 9.9431875 ".
  1066. "38.147656 10.117188 38.472656 C 10.291188 38.797656 10.631 39 11 39 L 39 39 C 39.369 39 39.708813 38.797656 39.882812 38.472656 C 40.056813 ".
  1067. "38.147656 40.037031 37.752313 39.832031 37.445312 L 38.044922 34.767578 C 36.668922 32.473578 36 30.587 36 29 L 36 21.199219 C 36 15.68167 ".
  1068. "32.827303 11.569596 28 10.369141 L 28 9 C 28 7.3545455 26.645455 6 25 6 z M 25 8 C 25.554545 8 26 8.4454545 26 9 L 26 10.044922 C 25.671339 ".
  1069. "10.019952 25.339787 10 25 10 C 24.660213 10 24.328661 10.020256 24 10.044922 L 24 9 C 24 8.4454545 24.445455 8 25 8 z \" fill=\"$fillcolor\" stroke=\"black\" style=\"stroke-width:1.5\"/>";
  1070. $ret .= "<path d=\"M 20.423828 41 C 21.197828 42.763 22.955 44 25 44 C 27.045 44 28.802172 42.763 29.576172 41 L 20.423828 41 z\" fill=\"black\"/>";
  1071. $ret .= "<path id=\"alarmst_isb\" class=\"alarmst_sb\" d=\"M 3.4804688 9.4765625 ".
  1072. "C 1.2493231 13.103089 -2.9605947e-16 17.418182 0 22 C 0 26.581818 1.2493231 30.896911 3.4804688 34.523438 L 5.1855469 33.476562 C 3.1506926 ".
  1073. "30.169089 2 26.218182 2 22 C 2 17.781818 3.1506926 13.830911 5.1855469 10.523438 L 3.4804688 9.4765625 z M 46.519531 9.4765625 L 44.814453 ".
  1074. "10.523438 C 46.849307 13.83091 48 17.781818 48 22 C 48 26.218182 46.849307 30.169089 44.814453 33.476562 L 46.519531 34.523438 C 48.750677 ".
  1075. "30.896911 50 26.581818 50 22 C 50 17.418182 48.750677 13.103089 46.519531 9.4765625 z M 7.8164062 12.140625 C 5.9949036 15.081921 5 ".
  1076. "18.353594 5 22 C 5 25.672173 6.1278502 29.047117 7.8085938 31.847656 L 9.5253906 30.818359 C 8.0061341 28.286898 7 25.261827 7 22 C 7 ".
  1077. "18.712406 7.8710809 15.852063 9.5175781 13.193359 L 7.8164062 12.140625 z M 42.183594 12.140625 L 40.482422 13.193359 C 42.128919 15.852063 ".
  1078. "43 18.712406 43 22 C 43 25.261827 41.993866 28.286898 40.474609 30.818359 L 42.191406 31.847656 C 43.87215 29.047117 45 25.672173 45 22 C 45 ".
  1079. "18.353594 44.005097 15.081921 42.183594 12.140625 z\" fill=\"$fillcolor2\" />";
  1080. $ret .= "<g id=\"alarmstate\" transform=\"translate(0,30) scale(0.6,0.6)\">";
  1081. for( my $level=0;$level<$alarmno;$level++ ){
  1082. my $val = $hash->{READINGS}{"level".$level}{VAL};
  1083. my $col;
  1084. if($val eq "disarmed"){
  1085. $col = $dac;
  1086. }elsif($val eq "armed"){
  1087. $col = $ac;
  1088. }elsif($val eq "armwait"){
  1089. $col = $awc;
  1090. }else{
  1091. $col = $alc;
  1092. }
  1093. $ret .= "<rect class=\"arec\" width=\"10\" height=\"10\" x=\"".(5+10*$level)."\" y=\"35\" fill=\"".$col."\" stroke=\"black\"/>";
  1094. }
  1095. $ret .= "</g></g></svg>";
  1096. return $ret;
  1097. if( $inline ){
  1098. return $ret;
  1099. }else{
  1100. $FW_RETTYPE = "image/svg+xml";
  1101. $FW_RET="";
  1102. FW_pO $ret;
  1103. return ($FW_RETTYPE, $FW_RET);
  1104. }
  1105. }
  1106. #########################################################################################
  1107. #
  1108. # Alarm_Html - returns HTML code for the Alarm page
  1109. #
  1110. # Parameter name = name of the Alarm definition
  1111. #
  1112. #########################################################################################
  1113. sub Alarm_Html($){
  1114. my ($name) = @_;
  1115. my $ret = "";
  1116. my $hash = $defs{$name};
  1117. my $id = $defs{$name}{NR};
  1118. if( !defined($alarm_tt) ){
  1119. #-- readjust language
  1120. my $lang = AttrVal("global","language","EN");
  1121. if( $lang eq "DE"){
  1122. $alarm_tt = \%alarm_transtable_DE;
  1123. }else{
  1124. $alarm_tt = \%alarm_transtable_EN;
  1125. }
  1126. }
  1127. #-- update state display
  1128. readingsSingleUpdate( $hash, "state", Alarm_getstate($hash)." ".$hash->{READINGS}{"short"}{VAL}, 1 );
  1129. #--
  1130. my $lockstate = ($hash->{READINGS}{lockstate}{VAL}) ? $hash->{READINGS}{lockstate}{VAL} : "unlocked";
  1131. my $showhelper = ($lockstate eq "unlocked") ? 1 : 0;
  1132. #--
  1133. $ret .= "<script type=\"text/javascript\" src=\"$FW_ME/pgm2/alarm.js\"></script><script type=\"text/javascript\">\n";
  1134. $ret .= "var alarmno = ".$alarmno.";\n";
  1135. #-- colors
  1136. $ret .= "var disarmcolor = \"".AttrVal($name,"disarmcolor","lightgray")."\";\n";
  1137. $ret .= "var armwaitcolor = \"".AttrVal($name,"armwaitcolor","#ffe658")."\";\n";
  1138. $ret .= "var armcolor = \"".AttrVal($name,"armcolor","#53f3c7")."\";\n";
  1139. $ret .= "var alarmcolor = \"".AttrVal($name,"alarmcolor","#fd5777")."\";\n";
  1140. #-- icon map
  1141. my $iconmap = AttrVal($name,"iconmap","");
  1142. $ret .= "var iconmap = '".$iconmap."';\n";
  1143. #-- initial state of system
  1144. my ($s,$ad,$aa,$al,$at,$detailstate);
  1145. $ad = 1;
  1146. $aa = 1;
  1147. $al = "";
  1148. $at = "";
  1149. $detailstate = "";
  1150. $ret .= "var ast = ['";
  1151. for( my $i=0;$i<$alarmno;$i++){
  1152. $s = $hash->{READINGS}{"level".$i}{VAL};
  1153. if( index($iconmap,$i) > -1 ){
  1154. #-- simplify by using state ??
  1155. if( ($s eq "disarmed") || ($s eq "armwait") ){
  1156. $detailstate .= "-";
  1157. }elsif( $s eq "armed" ){
  1158. $detailstate .= "O";
  1159. }else{
  1160. $detailstate .= "X";
  1161. }
  1162. $ad = $ad & ( ($s eq "disarmed")||($s eq "armwait") );
  1163. $aa = $aa & ( $s eq "armed" );
  1164. if( $s ne "disarmed" && $s ne "armwait" && $s ne "armed" ){
  1165. $al .= $i.",";
  1166. $at .= $s.",";
  1167. }
  1168. }else{
  1169. $detailstate .= "-";
  1170. }
  1171. $ret .= $s."'";
  1172. $ret .= ",'"
  1173. if( $i != $alarmno-1 );
  1174. }
  1175. $ret .= "];\n";
  1176. $ret .="var aa = ".(($aa == 1) ? "true;\n" : "false;\n");
  1177. $ret .="var ad = ".(($ad == 1) ? "true;\n" : "false;\n");
  1178. $ret .="var al = '".$al."';\n";
  1179. $ret .="var at = '".$at."';\n";
  1180. #-- initial state of alarm icon
  1181. my $iconstate;
  1182. if( $al ne "" ){
  1183. $iconstate = $al;
  1184. $ret .= "var blinking = 1;\n";
  1185. $ret .= "var blinker = setInterval('blinkbell()', 250);\n";
  1186. }else{
  1187. if( $aa == 1 && $ad == 0 ){
  1188. $iconstate = "armed";
  1189. }elsif( $aa == 0 && $ad == 1 ){
  1190. $iconstate = "disarmed";
  1191. }else{
  1192. $iconstate = "mixed";
  1193. }
  1194. $ret .= "var blinking = 0;\n";
  1195. $ret .= "var blinker;\n";
  1196. }
  1197. $ret .= "</script>\n";
  1198. $ret .= "<table class=\"roomoverview\">\n";
  1199. #--- here we insert the icon
  1200. $ret .= "<tr><td style=\"width:60px;height:80px\">";
  1201. $ret .= "<div>".Alarm_widget("name=".$name."&gstate=".$iconstate."&dstate=".$detailstate."&size=60x80")."</div>"
  1202. if( AttrVal($name,"noicons",0)==0 );
  1203. $ret .= "</td>";
  1204. $ret .= "<td style=\"width:150px;vertical-align:center\"><input type=\"button\" value=\"".$alarm_tt->{"setparms"}."\" onclick=\"javascript:alarm_set('$name')\"/></td><td>".
  1205. "<div id=\"hid_levels\">";
  1206. for( my $k=0;$k<$alarmno;$k++ ){
  1207. $ret .= "<div informId=\"$name-level".$k."\" class=\"hid_lx\" style=\"display:none\">".$hash->{READINGS}{"level".$k}{VAL}."</div>";
  1208. }
  1209. $ret .= "</div></td></tr>\n";
  1210. #-- settings table
  1211. my $row=1;
  1212. $ret .= "<tr><td colspan=\"3\"><div class=\"devType\">".$alarm_tt->{"settings"}."</div></td></tr>";
  1213. $ret .= "<tr><td colspan=\"3\"><table class=\"block wide\" id=\"settingstable\">\n";
  1214. $ret .= "<tr class=\"odd\"><td class=\"col1\" colspan=\"4\"><table id=\"armtable\" border=\"0\">\n";
  1215. $ret .= "<tr class=\"odd\"><td class=\"col1\" style=\"text-align:right\">".$alarm_tt->{"armbutton"}."&nbsp;&#8608;</td>";
  1216. $ret .= "<td class=\"col2\" style=\"text-align:right\"> ".$alarm_tt->{"waitaction"}." ";
  1217. $ret .= sprintf("<input type=\"text\" id=\"armwait\" size=\"50\" maxlength=\"512\" value=\"%s\"/>",(AttrVal($name, "armwait","") eq "1")?"":AttrVal($name, "armwait",""));
  1218. $ret .= "</td><td class=\"col3\" rowspan=\"2\"> &#8628 ".$alarm_tt->{"delay"}."<br> &#8626;";
  1219. $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"));
  1220. $ret .= "</td></tr>\n";
  1221. $ret .= "<tr class=\"even\"><td class=\"col1\"></td><td class=\"col2\" style=\"text-align:right\">".$alarm_tt->{"armaction"}." ";
  1222. $ret .= sprintf("<input type=\"text\" id=\"armaction\" size=\"50\" maxlength=\"512\" value=\"%s\"/>",(AttrVal($name, "armact","") eq "1")?"":AttrVal($name, "armact",""));
  1223. $ret .= "</td></tr>\n";
  1224. $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"}." ";
  1225. $ret .= sprintf("<input type=\"text\" id=\"disarmaction\" size=\"50\" maxlength=\"512\" value=\"%s\"/>",(AttrVal($name, "disarmact","") eq "1")?"":AttrVal($name, "disarmact",""));
  1226. $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"}." ";
  1227. $ret .= sprintf("<input type=\"text\" id=\"cancelaction\" size=\"50\" maxlength=\"512\" value=\"%s\"/>",(AttrVal($name, "cancelact","") eq "1")?"":AttrVal($name, "cancelact",""));
  1228. $ret .= "</td><td></td></tr></table></td></tr>";
  1229. $ret .= "<tr class=\"odd\"><td colspan=\"3\"style=\"padding:20px 0 20px 0;\"><table><tr><td></td><td></td><td class=\"col2\" colspan=\"3\">".$alarm_tt->{"time"}." [hh:mm]<td/></tr>".
  1230. "<tr><td class=\"col1\" style=\"padding:0 10px 0 10px;\">".$alarm_tt->{"level"}."</td><td class=\"col2\" style=\"padding:0 10px 0 10px;\">".$alarm_tt->{"cond"}.
  1231. "</td><td class=\"col2\" style=\"padding:0 10px 0 10px;\">".$alarm_tt->{"start"}."</td><td class=\"col2\" style=\"padding:0 10px 0 10px;\">".$alarm_tt->{"end"}.
  1232. "</td><td class=\"col2\" style=\"padding:0 10px 0 15px;\">".$alarm_tt->{"autocan"}.
  1233. "</td><td class=\"col3\" style=\"padding:0 10px 0 10px;\">".$alarm_tt->{"messagepart"}." II</td>".
  1234. "<td class=\"col4\"style=\"padding:0 10px 0 10px;\">".$alarm_tt->{"arm"}."</td><td class=\"col4\">".$alarm_tt->{"cancel"}."</td></tr>\n";
  1235. #-- foreach level
  1236. for( my $k=0;$k<$alarmno;$k++ ){
  1237. $row++;
  1238. my $cval = AttrVal($name, "level".$k."cond", 1);
  1239. my $sval = AttrVal($name, "level".$k."start", "0:00");
  1240. $sval = ""
  1241. if( $sval eq "1");
  1242. my $eval = AttrVal($name, "level".$k."end", "23:59");
  1243. $eval = ""
  1244. if( $eval eq "1");
  1245. my $oval = AttrVal($name, "level".$k."autocan", "0:00");
  1246. $oval = ""
  1247. if( $oval eq "1");
  1248. my $mval = AttrVal($name, "level".$k."msg", "--");
  1249. $mval = ""
  1250. if( $mval eq "1");
  1251. my $xval = $hash->{DATA}{"armstate"}{"level".$k};
  1252. $ret .= sprintf("<tr class=\"%s\"><td class=\"col1\">".$alarm_tt->{"alarm"}." $k</td>\n", ($row&1)?"odd":"even");
  1253. $ret .= "<td class=\"col2\" style=\"padding:0 10px 0 10px;\"><input type=\"text\" id=\"l".$k."c\" size=\"15\" maxlength=\"512\" value=\"$cval\"/></td><td class=\"col2\">".
  1254. "<input type=\"text\" id=\"l".$k."s\" size=\"4\" maxlength=\"120\" value=\"$sval\"/></td><td class=\"col2\">".
  1255. "<input type=\"text\" id=\"l".$k."e\" size=\"4\" maxlength=\"120\" value=\"$eval\"/></td><td class=\"col2\">".
  1256. "<input type=\"text\" id=\"l".$k."o\" size=\"4\" maxlength=\"120\" value=\"$oval\"/></td>".
  1257. "<td class=\"col3\"><input type=\"text\" id=\"l".$k."m\" size=\"25\" maxlength=\"256\" value=\"$mval\"/></td>";
  1258. $ret .= sprintf("<td class=\"col4\" width=\"30px\" align=\"center\"><input type=\"checkbox\" id=\"l".$k."x\" %s onclick=\"javascript:alarm_arm('$name','$k')\"/>",($xval eq "armed")?"checked=\"checked\"":"").
  1259. "</td><td class=\"col4\"><input type=\"button\" value=\"".$alarm_tt->{"cancel"}."\" onclick=\"javascript:alarm_cancel('$name','$k')\"/></td></tr>\n";
  1260. }
  1261. $ret .= "</table></td></tr>";
  1262. #-- sensors table
  1263. $row=1;
  1264. $ret .= "<tr><td colspan=\"3\"><div class=\"devType\">".$alarm_tt->{"sensors"}."</div></td></tr>";
  1265. $ret .= "<tr><td colspan=\"3\"><table class=\"block wide\" id=\"sensorstable\">\n";
  1266. $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/>".
  1267. join("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;",(0..($alarmno-1)))."</td><td class=\"col3\">".
  1268. $alarm_tt->{"notifyby"}." ".$alarm_tt->{"regexp"}."</td><td class=\"col3\">".$alarm_tt->{"messagepart"}." I</td><td class=\"col4\">".$alarm_tt->{"action"}."</td></tr>\n";
  1269. foreach my $d (sort keys %defs ) {
  1270. next if(IsIgnored($d));
  1271. if( AttrVal($d, "alarmDevice","") eq "Sensor" ) {
  1272. my $avl = AttrVal($d, "alarmSettings","");
  1273. #-- no escaping necessary
  1274. my @aval = split('\|',$avl);
  1275. if( int(@aval) != 4){
  1276. Log3 $hash, 1, "[Alarm] Settings incomplete for alarmSensor $d";
  1277. }
  1278. $row++;
  1279. $ret .= sprintf("<tr class=\"%s\" informId=\"$d\" name=\"sensor\">", ($row&1)?"odd":"even");
  1280. $ret .= "<td width=\"120\" class=\"col1\"><a href=\"$FW_ME?detail=$d\">$d</a></td>\n";
  1281. $ret .= "<td id=\"$d\" class=\"col2\">\n";
  1282. for( my $k=0;$k<$alarmno;$k++ ){
  1283. $ret .= sprintf("<input type=\"checkbox\" name=\"alarm$k\" value=\"$k\" %s/>&nbsp;",(index($aval[0],"alarm".$k) != -1)?"checked=\"checked\"":"");
  1284. }
  1285. $ret .= "</td><td class=\"col3\"><input type=\"text\" name=\"alarmnotify\" size=\"30\" maxlength=\"512\" value=\"$aval[1]\"/>";
  1286. $ret .= "</td><td class=\"col3\"><input type=\"text\" name=\"alarmmsg\" size=\"30\" maxlength=\"512\" value=\"$aval[2]\"/></td>\n";
  1287. $ret .= sprintf("<td class=\"col4\"><select name=\"%sonoff\"><option value=\"on\" %s>".$alarm_tt->{"raise"}."</option><option value=\"off\" %s>".$alarm_tt->{"cancel"}."</option>",
  1288. $d,($aval[3] eq "on")?"selected=\"selected\"":"",($aval[3] eq "off")?"selected=\"selected\"":"");
  1289. $ret .= sprintf("<option value=\"arm\" %s>".$alarm_tt->{"arm"}."</option><option value=\"disarm\" %s>".$alarm_tt->{"disarm"}."</option><select></td></tr>\n",
  1290. ($aval[3] eq "arm")?"selected=\"seleced\"":"",($aval[3] eq "disarm")?"selected=\"selected\"":"");
  1291. }
  1292. }
  1293. $ret .= "</table></td></tr>";
  1294. #-- actors table
  1295. $row=1;
  1296. $ret .= "<tr><td colspan=\"3\"><div class=\"devType\">".$alarm_tt->{"actors"}."</div></td></tr>";
  1297. $ret .= "<tr><td colspan=\"3\"><table class=\"block wide\" id=\"actorstable\">\n";
  1298. $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))).
  1299. "</td><td class=\"col3\">".$alarm_tt->{"setaction"};
  1300. $ret .= "&nbsp; (".$alarm_tt->{"testaction"}.")"
  1301. if( AttrVal($name,"testbutton",0) == 1);
  1302. $ret .= "</td><td class=\"col3\">".$alarm_tt->{"unsetaction"};
  1303. $ret .= "&nbsp; (".$alarm_tt->{"testaction"}.")"
  1304. if( AttrVal($name,"testbutton",0) == 1);
  1305. $ret .= "</td><td class=\"col4\">".$alarm_tt->{"delay"}."<br/>[hh:]mm:ss</td></tr>\n";
  1306. foreach my $d (sort keys %defs ) {
  1307. next if(IsIgnored($d));
  1308. if( AttrVal($d, "alarmDevice","") eq "Actor" ) {
  1309. my @aval = Alarm_getsettings($hash,$d,"Actor");
  1310. #-- escaping before HTML publish
  1311. $aval[1] = Alarm_escape($aval[1],"beforehtml");
  1312. $aval[2] = Alarm_escape($aval[2],"beforehtml");
  1313. $aval[3] = Alarm_escape($aval[3],"beforehtml");
  1314. $row++;
  1315. $ret .= sprintf("<tr class=\"%s\" informId=\"$d\" name=\"actor\">", ($row&1)?"odd":"even");
  1316. $ret .= "<td width=\"120\" class=\"col1\"><a href=\"$FW_ME?detail=$d\">$d</a></td>\n";
  1317. $ret .= "<td id=\"$d\" class=\"col2\">\n";
  1318. for( my $k=0;$k<$alarmno;$k++ ){
  1319. $ret .= sprintf("<input type=\"checkbox\" name=\"alarm$k\"%s/>&nbsp;",(index($aval[0],"alarm".$k) != -1)?"checked=\"checked\"":"");
  1320. }
  1321. $ret .= "</td><td class=\"col3\"><input type=\"text\" name=\"alarmon\" size=\"30\" maxlength=\"512\" value=\"$aval[1]\"/>";
  1322. $ret .= "&nbsp;<input type=\"button\" value=\"T\" onclick=\"javascript:alarm_testaction('$name','$d','set')\"/>"
  1323. if( AttrVal($name,"testbutton",0) == 1);
  1324. $ret .= "</td><td class=\"col3\">";
  1325. $ret .= "<input type=\"text\" name=\"alarmoff\" size=\"30\" maxlength=\"512\" value=\"$aval[2]\"/>";
  1326. $ret .= "&nbsp;<input type=\"button\" value=\"T\" onclick=\"javascript:alarm_testaction('$name','$d','unset')\"/>"
  1327. if( AttrVal($name,"testbutton",0) == 1);
  1328. $ret .= "</td><td class=\"col4\"><input type=\"text\" name=\"delay\" size=\"5\" maxlength=\"8\" value=\"$aval[3]\"/></td></tr>\n";
  1329. }
  1330. }
  1331. $ret .= "</table></td></tr>\n";
  1332. $ret .= "</table>";
  1333. return $ret;
  1334. }
  1335. 1;
  1336. =pod
  1337. =item helper
  1338. =item summary to set up a house alarm system with 8 different alarm levels
  1339. =begin html
  1340. <a name="Alarm"></a>
  1341. <h3>Alarm</h3>
  1342. <ul>
  1343. <p> FHEM module to set up a house alarm system with 8 different alarm levels</p>
  1344. <a name="Alarmusage"></a>
  1345. <h4>Usage</h4>
  1346. See <a href="http://www.fhemwiki.de/wiki/Modul_Alarm">German Wiki page</a>
  1347. <a name="Alarmdefine"></a>
  1348. <br/>
  1349. <h4>Define</h4>
  1350. <p>
  1351. <code>define &lt;name&gt; Alarm</code>
  1352. <br />Defines the Alarm system. </p>
  1353. <a name="Alarmset"></a>
  1354. Notes: <ul>
  1355. <li>This module uses the global attribute <code>language</code> to determine its output data<br/>
  1356. (default: EN=english). For German output set <code>attr global language DE</code>.</li>
  1357. <li>This module needs the JSON package.</li>
  1358. </ul>
  1359. <h4>Set</h4>
  1360. <ul>
  1361. <li><a name="alarm_cancel">
  1362. <code>set &lt;name&gt; canceled &lt;level&gt;</code>
  1363. </a>
  1364. <br />cancels an alarm of level &lt;level&gt;, where &lt;level&gt; = 0..7 </li>
  1365. <li><a name="alarm_arm">
  1366. <code>set &lt;name&gt; armed &lt;level&gt;</code><br />
  1367. <code>set &lt;name&gt; disarmed &lt;level&gt;</code>
  1368. </a>
  1369. <br />sets the alarm of level &lt;level&gt; to armed (i.e., active) or disarmed
  1370. (i.e., inactive), where &lt;level&gt; = 0..7 </li>
  1371. <li><a name="alarm_lock">
  1372. <code>set &lt;name&gt; locked</code><br />
  1373. <code>set &lt;name&gt; unlocked</code>
  1374. </a>
  1375. <br />sets the lockstate of the alarm module to <i>locked</i> (i.e., alarm setups
  1376. may not be changed) resp. <i>unlocked</i> (i.e., alarm setups may be changed>)</li>
  1377. <li><a name="alarm_save">
  1378. <code>set &lt;name&gt; save|restore</code>
  1379. </a>
  1380. <br />Manually save/restore the arm states to/from the external file AlarmFILE (save done automatically at each state modification, restore at FHEM start)</li>
  1381. </ul>
  1382. <a name="Alarmget"></a>
  1383. <h4>Get</h4>
  1384. <ul>
  1385. <li><a name="alarm_version"></a>
  1386. <code>get &lt;name&gt; version</code>
  1387. <br />Display the version of the module</li>
  1388. </ul>
  1389. <a name="Alarmattr"></a>
  1390. <h4>Attributes</h4>
  1391. <ul>
  1392. <li><a name="alarm_hiddenroom"><code>attr &lt;name&gt; hiddenroom
  1393. &lt;string&gt;</code></a>
  1394. <br />Room name for hidden alarm room (containing only the Alarm device), default:
  1395. AlarmRoom</li>
  1396. <li><a name="alarm_publicroom"><code>attr &lt;name&gt; publicroom
  1397. &lt;string&gt;</code></a>
  1398. <br />Room name for public alarm room (containing sensor/actor devices), default:
  1399. Alarm</li>
  1400. <li><a name="alarm_lockstate"><code>attr &lt;name&gt; lockstate
  1401. locked|unlocked</code></a>
  1402. <br /><i>locked</i> means that alarm setups may not be changed, <i>unlocked</i>
  1403. means that alarm setups may be changed></li>
  1404. <li><a name="alarm_testbutton"><code>attr &lt;name&gt; testbutton 0|1</code></a>
  1405. <br /><i>1</i> means that a test button is displayed for every actor field</li>
  1406. <li><a name="alarm_statedisplay"><code>attr &lt;name&gt; statedisplay
  1407. simple,color,table,none</code></a>
  1408. <br />defines how the state of all eight alarm levels is shown. Example for the case
  1409. when alarm no. 0 is disarmed and only alarm no. 2 is raised: <ul>
  1410. <li> simple = OXOOOOO</li>
  1411. <li> color = <span style="color:lightgray"> 0 </span><span style="font-weight:bold;color:#53f3c7">1 <span style="font-weight:bold;color:#fd5777"
  1412. >2</span> 3 4 5 6 7</span></li>
  1413. <li> table = HTML mini table with colored fields for alarms</li>
  1414. <li> none = no state display</li>
  1415. </ul>
  1416. </li>
  1417. <li><a name="alarm_noicons"><code>attr &lt;name&gt; noicons
  1418. 0|1</code></a>
  1419. <br />when set to 1, animated icons are suppressed</li>
  1420. <li><a name="alarm_iconmap"><code>attr &lt;name&gt; iconmap <i>list</i></code></a>
  1421. <br /> comma separated list of alarm levels for which the main icon/widget is set to disarmed/mixed/armed. No default=icon static</li>
  1422. <li><a name="alarm_color"><code>attr &lt;name&gt; disarmcolor|armwaitcolor|armcolor|alarmcolor <i>color</i></code></a>
  1423. <br />four color specifications to signal the states disarmed (default <span style="color:lightgray">lightgray</span>),
  1424. armwait (default <span style="color:#ffe658">#ffe658</span>),
  1425. armed (default <span style="color:#53f3c7">#53f3c7</span>) and alarmed (default <span style="color:#fd5777">#fd5777</span>)</li>
  1426. <li><a name="alarm_armdelay"><code>attr &lt;name&gt; armdelay <i>mm:ss</i></code></a>
  1427. <br />time until the arming of an alarm becomes operative (0:00 - 9:59 allowed)</li>
  1428. <li><a name="alarm_armwait"><code>attr &lt;name&gt; armwait <i>action</i></code></a>
  1429. <br />FHEM action to be carried out immediately after the arm event</li>
  1430. <li><a name="alarm_armact"><code>attr &lt;name&gt; armact <i>action</i></code></a>
  1431. <br />FHEM action to be carried out at the arm event after the delay time </li>
  1432. <li><a name="alarm_disarmact"><code>attr &lt;name&gt; disarmact <i>action</i></code></a>
  1433. <br />FHEM action to be carried out on the disarming of an alarm</li>
  1434. <li><a name="alarm_cancelact"><code>attr &lt;name&gt; cancelact <i>action</i></code></a>
  1435. <br />FHEM action to be carried out on the canceling of an alarm</li>
  1436. <li><a name="alarm_internals"></a>For each of the 8 alarm levels, several attributes
  1437. hold the alarm setup. They should not be changed by hand, but through the web
  1438. interface to avoid confusion: <code>level&lt;level&gt;cond, level&lt;level&gt;start, level&lt;level&gt;end, level&lt;level&gt;autocan,
  1439. level&lt;level&gt;msg, level&lt;level&gt;onact,
  1440. level&lt;level&gt;offact</code></li>
  1441. <li>Standard attributes <a href="#alias">alias</a>, <a href="#comment">comment</a>, <a
  1442. href="#event-on-update-reading">event-on-update-reading</a>, <a
  1443. href="#event-on-change-reading">event-on-change-reading</a>, <a href="#room"
  1444. >room</a>, <a href="#eventMap">eventMap</a>, <a href="#loglevel">loglevel</a>,
  1445. <a href="#webCmd">webCmd</a></li>
  1446. </ul>
  1447. </ul>
  1448. =end html
  1449. =begin html_DE
  1450. <a name="Alarm"></a>
  1451. <h3>Alarm</h3>
  1452. <ul>
  1453. <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>
  1454. </ul>
  1455. =end html_DE
  1456. =cut