95_Alarm.pm 65 KB

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