diff --git a/fhem/FHEM/95_Alarm.pm b/fhem/FHEM/95_Alarm.pm index bf4cbc05f..f203f03be 100644 --- a/fhem/FHEM/95_Alarm.pm +++ b/fhem/FHEM/95_Alarm.pm @@ -43,7 +43,7 @@ my $alarmlinkname = "Alarms"; # link text my $alarmhiddenroom = "AlarmRoom"; # hidden room my $alarmpublicroom = "Alarm"; # public room my $alarmno = 8; -my $alarmversion = "3.12"; +my $alarmversion = "4.0"; my %alarm_transtable_EN = ( "ok" => "OK", @@ -441,12 +441,16 @@ sub Alarm_getsettings($$$){ my ($hash,$dev,$type) = @_; my $chg = 0; - my @aval = split('\|',AttrVal($dev, "alarmSettings","|||0:00"),4); - + my $avl = AttrVal($dev, "alarmSettings","|||0:00"); + $avl = Alarm_escape($avl,"beforesplit"); + my @aval = split('\|',$avl,4); + $aval[1] = Alarm_escape($aval[1],"aftersplit"); + $aval[2] = Alarm_escape($aval[2],"aftersplit"); + if( $type eq "Actor"){ - #-- position 0:set by, 1:set func, 2:unset func, 3:delay + #-- position 0:set by level, 1:set func, 2:unset func, 3:delay if( $aval[0] eq "" || $aval[1] eq "" ){ - Log3 $hash, 1, "[Alarm] Settings incomplete for alarmActor $dev"; + Log3 $hash, 1, "[Alarm] Settings $avl incomplete for alarmActor $dev"; } #-- check delay time if( $aval[3] =~ /^\d+$/ ){ @@ -459,7 +463,7 @@ sub Alarm_getsettings($$$){ $aval[3] = sprintf("%02d:%02d",$min,$sec); } $chg = 1; - }elsif( $aval[3] !~ /^(\d\d:)?\d\d:\d\d/ ){ + }elsif( $aval[3] !~ /^(\d\d:)?\d?\d:\d\d/ ){ Log3 $hash, 1, "[Alarm] Delay time $aval[3] ill defined for alarmActor $dev"; $aval[3] = "0:00"; $chg = 1; @@ -472,6 +476,30 @@ sub Alarm_getsettings($$$){ return @aval; } +######################################################################################### +# +# Alarm_escape - Helper function to de-escape and to escape action parameters +# +# Parameter hash = hash of Alarm device +# dev = name of device addressed +# +######################################################################################### + +sub Alarm_escape($$){ + my ($str,$type) = @_; + + if( $type eq "beforesplit"){ + $str =~ s/\\\|/%7C/g; + }elsif( $type eq "aftersplit"){ + $str =~ s/\\//g; + $str =~ s/%7C/\|/g; + }elsif( $type eq "beforehtml"){ + $str =~ s/\\//g; + $str =~ s/\|/\\\|/g; + } + return $str; +} + ######################################################################################### # # Alarm_save @@ -482,7 +510,8 @@ sub Alarm_getsettings($$$){ sub Alarm_save($) { my ($hash) = @_; - $hash->{DATA}{"savedate"} = sprintf("%s",localtime(time)); + my $date = localtime(time); + $hash->{DATA}{"savedate"} = $date; readingsSingleUpdate( $hash, "savedate", $hash->{DATA}{"savedate"}, 1 ); my $json = JSON->new->utf8; my $jhash0 = eval{ $json->encode( $hash->{DATA} ) }; @@ -610,7 +639,13 @@ sub Alarm_Exec($$$$$){ if( (($stp < $etp) && ($ntp <= $etp) && ($ntp >= $stp)) || (($stp > $etp) && (($ntp <= $etp) || ($ntp >= $stp))) ){ #-- raised by sensor (attribute values have been controlled in CreateNotifiers) - @sta = split('\|', AttrVal($dev, "alarmSettings", "")); + my $avl = AttrVal($dev, "alarmSettings",""); + $avl =~ s/\\\|/%7C/g; + @sta = split('\|',$avl); + for( my $i=0;$i<4;$i++ ){ + $sta[$i] =~ s/\\//g; + $sta[$i] =~ s/%7C/|/g; + } if( $sta[2] ){ $mga = $sta[2]." ".AttrVal($name, "level".$level."msg", 0); #-- replace some parts @@ -651,14 +686,12 @@ sub Alarm_Exec($$$$$){ }elsif( ($act eq "off")||($act eq "cancel") ){ #-- only if this level is active if( ($xac ne "armed")&&($xac ne "disarmed") ){ + #-- TODO: ohne intAt auskommen #-- deleting all running ats $dly = sprintf("alarm%1ddly",$level); - foreach my $d (sort keys %intAt ) { - next if( $intAt{$d}{FN} ne "at_Exec" ); - $mga = $intAt{$d}{ARG}{NAME}; - next if( $mga !~ /$dly\d/); - #Log3 $hash,1,"[Alarm] Killing delayed action $name"; - CommandDelete(undef,"$mga"); + foreach my $d ( devspec2array("NAME=alarm.dly.*")) { + Log3 $hash,1,"[Alarm] Killing delayed action $d"; + CommandDelete(undef,$d); } #-- replace some parts my @evtpart = split(" ",$evt); @@ -812,7 +845,7 @@ sub Alarm_CreateNotifiers($){ } #-- temporary code: transferm from attributes to hash - Alarm_transform($hash); + #Alarm_transform($hash); for( my $level=0;$level<$alarmno;$level++ ){ @@ -854,15 +887,25 @@ sub Alarm_CreateNotifiers($){ my $cmd = ''; foreach my $d (keys %defs ) { next if(IsIgnored($d)); - if( AttrVal($d, "alarmDevice","") eq "Sensor" ) { - my @aval = split('\|',AttrVal($d, "alarmSettings","")); + if( AttrVal($d, "alarmDevice","") eq "Sensor" ) { + my $avl = AttrVal($d, "alarmSettings",""); + $avl =~ s/\\\|/%7C/g; + my @aval = split('\|',$avl); if( int(@aval) != 4){ - # Log3 $hash, 1, "[Alarm $level] Settings incomplete for sensor $d"; - next; + Log3 $hash,1, "[Alarm $level] Settings $avl incomplete for alarmSensor $d"; + next; } - if( (index($aval[0],"alarm".$level) != -1) && ($aval[3] eq "off") ){ + for( my $i=0;$i<4;$i++ ){ + $aval[$i] =~ s/\\//g; + $aval[$i] =~ s/%7C/\|/g; + } + + #-- workaround: replace any space by \s + $aval[1] =~ s/\s/\\s/g; + + if( (index($aval[0],"alarm".$level) != -1) && ($aval[1] ne "") && ($aval[3] eq "off")){ $cmd .= '('.$aval[1].')|'; - #Log3 $hash,1,"[Alarm $level] Adding sensor $d to cancel notifier"; + Log3 $hash,5,"[Alarm $level] Adding sensor $d to cancel notifier"; } } } @@ -875,7 +918,7 @@ sub Alarm_CreateNotifiers($){ CommandDefine(undef,$cmd); CommandAttr (undef,'alarm'.$level.'.off.N room '.$alarmpublicroom); CommandAttr (undef,'alarm'.$level.'.off.N group alarmNotifier'); - Log3 $hash,5,"[Alarm $level] Created cancel notifier"; + Log3 $hash,3,"[Alarm $level] Created cancel notifier"; #-- now set up the command for raising alarm - only if cancel exists $cmd = ''; @@ -884,27 +927,30 @@ sub Alarm_CreateNotifiers($){ foreach my $d (sort keys %defs ) { next if(IsIgnored($d)); if( AttrVal($d, "alarmDevice","") eq "Sensor" ) { - my @aval = split('\|',AttrVal($d, "alarmSettings","")); - if( int(@aval) != 4){ - Log3 $hash, 5, "[Alarm $level] Settings incomplete for alarmSensor $d"; - next; - } - if( index($aval[0],"alarm".$level) != -1){ - if( $aval[3] eq "on" ){ - $cmd .= '('.$aval[1].')|'; - Log3 $hash,5,"[Alarm $level] Adding alarmSensor $d to raise notifier"; - }elsif( $aval[3] eq "arm" ){ - $cmdarm .= '('.$aval[1].')|'; - Log3 $hash,5,"[Alarm $level] Adding alarmSensor $d to arm notifier"; - }elsif( $aval[3] eq "disarm" ){ - $cmddisarm .= '('.$aval[1].')|'; - Log3 $hash,5,"[Alarm $level] Adding alarmSensor $d to disarm notifier"; - } - } - } + my $avl = AttrVal($d, "alarmSettings",""); + my @aval = split('\|',$avl); + if( int(@aval) != 4){ + Log3 $hash, 1, "[Alarm $level] Settings $avl incomplete for alarmSensor $d"; + next; + } + if( index($aval[0],"alarm".$level) != -1){ + if( ($aval[1] ne "") && ($aval[3] eq "on") ){ + $cmd .= '('.$aval[1].')|'; + Log3 $hash,5,"[Alarm $level] Adding alarmSensor $d to raise notifier"; + }elsif( ($aval[1] ne "") && ($aval[3] eq "arm") ){ + $cmdarm .= '('.$aval[1].')|'; + Log3 $hash,5,"[Alarm $level] Adding alarmSensor $d to arm notifier"; + }elsif( ($aval[1] ne "") && ($aval[3] eq "disarm") ){ + $cmddisarm .= '('.$aval[1].')|'; + Log3 $hash,5,"[Alarm $level] Adding alarmSensor $d to disarm notifier"; + } + } + } } #-- raise notifier if( $cmd eq '' ){ + CommandAttr(undef,$name.' level'.$level.'onact 1'); + CommandAttr(undef,$name.' level'.$level.'offact 1'); Log3 $hash,1,"[Alarm $level] No \"Raise\" device defined"; } else { $cmd = substr($cmd,0,length($cmd)-1); @@ -913,7 +959,7 @@ sub Alarm_CreateNotifiers($){ CommandDefine(undef,$cmd); CommandAttr (undef,'alarm'.$level.'.on.N room '.$alarmpublicroom); CommandAttr (undef,'alarm'.$level.'.on.N group alarmNotifier'); - Log3 $hash,5,"[Alarm $level] Created raise notifier"; + Log3 $hash,3,"[Alarm $level] Created raise notifier"; #-- now set up the list of actors $cmd = ''; @@ -924,12 +970,12 @@ sub Alarm_CreateNotifiers($){ if( AttrVal($d, "alarmDevice","") eq "Actor" ) { my @aval = Alarm_getsettings($hash,$d,"Actor"); if( int(@aval) != 4){ - Log3 $hash, 5, "[Alarm $level] Settings incomplete for alarmActor $d"; + Log3 $hash, 3, "[Alarm $level] Settings incomplete for alarmActor $d"; next; } if( index($aval[0],"alarm".$level) != -1 ){ #-- activate without delay - if(( $aval[3] eq "" )||($aval[3] eq "00:00")){ + if(( $aval[3] eq "" )||($aval[3] =~ /(00:)?0?0:00/)){ $cmd .= $aval[1].';'; #-- activate with delay } else { @@ -955,6 +1001,8 @@ sub Alarm_CreateNotifiers($){ CommandAttr(undef,$name.' level'.$level.'offact '.$cmd2); Log3 $hash,5,"[Alarm $level] Added on/off actors to $name"; } else { + CommandAttr(undef,$name.' level'.$level.'onact 1'); + CommandAttr(undef,$name.' level'.$level.'offact 1'); Log3 $hash,5,"[Alarm $level] Adding on/off actors not possible"; } #-- arm notifier - optional, but only in case the alarm may be raised @@ -1084,7 +1132,7 @@ sub Alarm_widget($){ } } - Log 1,"[Alarm_widget] name=$name gstate=$gstate dstate=$dstate sizep=$sizep"; + Log 5,"[Alarm_widget] name=$name gstate=$gstate dstate=$dstate sizep=$sizep"; $name =~ s/'//g; my @size=split('x',($sizep ? $sizep : '60x80')); @@ -1308,7 +1356,6 @@ sub Alarm_Html($) $mval = "" if( $mval eq "1"); - my $xval = AttrVal($name, "level".$k."xec", 0); my $xval = $hash->{DATA}{"armstate"}{"level".$k}; $ret .= sprintf("".$alarm_tt->{"alarm"}." $k\n", ($row&1)?"odd":"even"); $ret .= "   ". @@ -1329,10 +1376,13 @@ sub Alarm_Html($) foreach my $d (sort keys %defs ) { next if(IsIgnored($d)); if( AttrVal($d, "alarmDevice","") eq "Sensor" ) { - my @aval = split('\|',AttrVal($d, "alarmSettings","")); + my $avl = AttrVal($d, "alarmSettings",""); + #-- no escaping necessary + my @aval = split('\|',$avl); if( int(@aval) != 4){ Log3 $hash, 1, "[Alarm] Settings incomplete for alarmSensor $d"; } + $row++; $ret .= sprintf("", ($row&1)?"odd":"even"); $ret .= "$d\n"; @@ -1366,6 +1416,10 @@ sub Alarm_Html($) next if(IsIgnored($d)); if( AttrVal($d, "alarmDevice","") eq "Actor" ) { my @aval = Alarm_getsettings($hash,$d,"Actor"); + #-- escaping before HTML publish + $aval[1] = Alarm_escape($aval[1],"beforehtml"); + $aval[2] = Alarm_escape($aval[2],"beforehtml"); + $aval[3] = Alarm_escape($aval[3],"beforehtml"); $row++; $ret .= sprintf("", ($row&1)?"odd":"even"); $ret .= "$d\n"; diff --git a/fhem/www/pgm2/alarm.js b/fhem/www/pgm2/alarm.js index 4de207149..35cf225c1 100644 --- a/fhem/www/pgm2/alarm.js +++ b/fhem/www/pgm2/alarm.js @@ -1,15 +1,34 @@ //######################################################################################## // alarm.js -// Version 3.1 +// Version 4.0 // See 95_Alarm for licensing //######################################################################################## //# Prof. Dr. Peter A. Henning +//------------------------------------------------------------------------------------------------------ +// Determine csrfToken +//------------------------------------------------------------------------------------------------------ + +var req = new XMLHttpRequest(); +req.open('GET', document.location, false); +req.send(null); +var csrfToken = req.getResponseHeader('X-FHEM-csrfToken'); + +//------------------------------------------------------------------------------------------------------ +// encode Parameters for URL +//------------------------------------------------------------------------------------------------------ + function encodeParm(oldval) { var newval; - newval = oldval.replace(/\+/g, '%2B'); + newval = oldval.replace(/"/g, '%27'); newval = newval.replace(/#/g, '%23'); - newval = newval.replace(/"/g, '%27'); + newval = newval.replace(/\+/g, '%2B'); + newval = newval.replace(/&/g, '%26'); + newval = newval.replace(/'/g, '%27'); + newval = newval.replace(/=/g, '%3D'); + newval = newval.replace(/\?/g, '%3F'); + newval = newval.replace(/\|/g, '%7C'); + newval = newval.replace(/\s/g, '%20'); return newval; } @@ -84,20 +103,20 @@ $("body").on('DOMSubtreeModified', "#hid_levels", function () { for (i = 0; i < alarmno; i++) { var s = w.getElementsByClassName("hid_lx")[i].innerHTML; if (ast[i] != s) { - switch(s){ + switch (s) { case "disarmed": - col = disarmcolor; - break; + col = disarmcolor; + break; case "armwait": - col = armwaitcolor; - break; + col = armwaitcolor; + break; case "armed": - col = armcolor; - break; + col = armcolor; + break; default: - col = alarmcolor + col = alarmcolor } - t[i].setAttribute("fill",col); + t[i].setAttribute("fill", col); ast[i] = s; ifnd = i; sfnd = s; @@ -115,7 +134,7 @@ $("body").on('DOMSubtreeModified', "#hid_levels", function () { aln = aln + i + ","; atn = atn + s + ","; } else { - adn = adn && ((s == "disarmed")||(s == "armwait")); + adn = adn && ((s == "disarmed") ||(s == "armwait")); aan = aan && (s == "armed"); } } @@ -157,7 +176,7 @@ function alarm_setAttribute(name, attr, val) { location = location.substr(0, location.length -1); } var url = document.location.protocol + "//" + document.location.host + location; - FW_cmd(url + '?XHR=1&cmd.' + name + '=attr ' + name + ' ' + encodeParm(attr) + ' ' + encodeParm(val)); + FW_cmd(url + '?XHR=1&fwcsrf=' + csrfToken + '&cmd.' + name + '=attr%20' + name + '%20' + encodeParm(attr) + '%20' + encodeParm(val)); } function alarm_cancel(name, level) { @@ -170,7 +189,7 @@ function alarm_cancel(name, level) { } var url = document.location.protocol + "//" + document.location.host + location; - FW_cmd(url + '?XHR=1&cmd.' + name + '={Alarm_Exec("' + name + '",' + level + ',"web","button","off")}'); + FW_cmd(url + '?XHR=1&fwcsrf=' + csrfToken + '&cmd.' + name + '={Alarm_Exec("' + name + '",' + level + ',"web","button","off")}'); } function alarm_arm(name, level) { @@ -188,7 +207,7 @@ function alarm_arm(name, level) { } var url = document.location.protocol + "//" + document.location.host + location; - FW_cmd(url + '?XHR=1&cmd.' + name + '={Alarm_Arm("' + name + '",' + level + ',"web","button","' + command + '")}'); + FW_cmd(url + '?XHR=1&fwcsrf=' + csrfToken + '&cmd.' + name + '={Alarm_Arm("' + name + '",' + level + ',"web","button","' + command + '")}'); } function alarm_testaction(name, dev, type) { @@ -211,7 +230,7 @@ function alarm_testaction(name, dev, type) { } var url = document.location.protocol + "//" + document.location.host + location; - FW_cmd(url + '?XHR=1&cmd.' + name + '={Alarm_Test("' + name + '","' + cmds + '")}'); + FW_cmd(url + '?XHR=1&fwcsrf=' + csrfToken + '&cmd.' + name + '={Alarm_Test("' + name + '","' + cmds + '")}'); } @@ -226,29 +245,26 @@ function alarm_set(name) { var url = document.location.protocol + "//" + document.location.host + location; // saving arm data - FW_cmd(url + '?XHR=1&cmd.' + name + '=attr ' + name + ' armdelay ' + document.getElementById('armdelay').value); - FW_cmd(url + '?XHR=1&cmd.' + name + '=attr ' + name + ' armwait ' + encodeParm(document.getElementById('armwait').value)); - FW_cmd(url + '?XHR=1&cmd.' + name + '=attr ' + name + ' armact ' + encodeParm(document.getElementById('armaction').value)); - FW_cmd(url + '?XHR=1&cmd.' + name + '=attr ' + name + ' disarmact ' + encodeParm(document.getElementById('disarmaction').value)); - FW_cmd(url + '?XHR=1&cmd.' + name + '=attr ' + name + ' cancelact ' + encodeParm(document.getElementById('cancelaction').value)); + FW_cmd(url + '?XHR=1&fwcsrf=' + csrfToken + '&cmd.' + name + '=attr%20' + name + '%20armdelay%20' + document.getElementById('armdelay').value); + FW_cmd(url + '?XHR=1&fwcsrf=' + csrfToken + '&cmd.' + name + '=attr%20' + name + '%20armwait%20' + encodeParm(document.getElementById('armwait').value)); + FW_cmd(url + '?XHR=1&fwcsrf=' + csrfToken + '&cmd.' + name + '=attr%20 ' + name + '%20armact%20' + encodeParm(document.getElementById('armaction').value)); + FW_cmd(url + '?XHR=1&fwcsrf=' + csrfToken + '&cmd.' + name + '=attr%20' + name + '%20disarmact%20' + encodeParm(document.getElementById('disarmaction').value)); + FW_cmd(url + '?XHR=1&fwcsrf=' + csrfToken + '&cmd.' + name + '=attr%20' + name + '%20cancelact%20' + encodeParm(document.getElementById('cancelaction').value)); // saving start and end times for (var i = 0; i < alarmno; i++) { - FW_cmd(url + '?XHR=1&cmd.' + name + '=attr ' + name + ' level' + i + 'start ' + document.getElementById('l' + i + 's').value); - FW_cmd(url + '?XHR=1&cmd.' + name + '=attr ' + name + ' level' + i + 'end ' + document.getElementById('l' + i + 'e').value); - FW_cmd(url + '?XHR=1&cmd.' + name + '=attr ' + name + ' level' + i + 'msg ' + document.getElementById('l' + i + 'm').value); + FW_cmd(url + '?XHR=1&fwcsrf=' + csrfToken + '&cmd.' + name + '=attr%20' + name + '%20level' + i + 'start%20' + document.getElementById('l' + i + 's').value); + FW_cmd(url + '?XHR=1&fwcsrf=' + csrfToken + '&cmd.' + name + '=attr%20' + name + '%20level' + i + 'end%20' + document.getElementById('l' + i + 'e').value); + FW_cmd(url + '?XHR=1&fwcsrf=' + csrfToken + '&cmd.' + name + '=attr%20' + name + '%20level' + i + 'msg%20' + document.getElementById('l' + i + 'm').value); if (document.getElementById('l' + i + 'x').checked == true) { val = "armed"; } else { val = "disarmed"; } - FW_cmd(url + '?XHR=1&cmd.' + name + '=attr ' + name + ' level' + i + 'xec ' + val); + FW_cmd(url + '?XHR=1&fwcsrf=' + csrfToken + '&cmd.' + name + '=attr%20' + name + '%20level' + i + 'xec%20' + val); } - //for (var k in ah.items) { - // ah.setItem(k,document.getElementById(k).value); - //} // acquiring data for each sensor var sarr = document.getElementsByName('sensor'); @@ -264,12 +280,11 @@ function alarm_set(name) { val += "alarm" + i + ","; } } - val += "|" + sarr[k].children[2].children[0].value; - val += "|" + sarr[k].children[3].children[0].value; + val += "|" + encodeParm(sarr[k].children[2].children[0].value); + val += "|" + encodeParm(sarr[k].children[3].children[0].value); val += "|" + sarr[k].children[4].children[0].options[sarr[k].children[4].children[0].selectedIndex].value; - FW_cmd(url + '?XHR=1&cmd.' + nam + '=attr ' + nam + ' alarmSettings ' + encodeParm(val)); + FW_cmd(url + '?XHR=1&fwcsrf=' + csrfToken + '&cmd.' + nam + '=attr%20' + nam + '%20alarmSettings%20' + val); } - // acquiring data for each actor var aarr = document.getElementsByName('actor'); for (var k = 0; @@ -285,12 +300,12 @@ function alarm_set(name) { val += "alarm" + i + ","; } } - val += "|" + aarr[k].children[2].children[0].value; - val += "|" + aarr[k].children[3].children[0].value; - val += "|" + aarr[k].children[4].children[0].value; - FW_cmd(url + '?XHR=1&cmd.' + nam + '=attr ' + nam + ' alarmSettings ' + encodeParm(val)); + val += "|" + encodeParm(aarr[k].children[2].children[0].value); + val += "|" + encodeParm(aarr[k].children[3].children[0].value); + val += "|" + encodeParm(aarr[k].children[4].children[0].value); + FW_cmd(url + '?XHR=1&fwcsrf=' + csrfToken + '&cmd.' + nam + '=attr%20' + nam + '%20alarmSettings%20' + val); } // creating notifiers - FW_cmd(url + '?XHR=1&cmd.' + name + ' ={main::Alarm_CreateNotifiers("' + name + '")}'); + FW_cmd(url + '?XHR=1&fwcsrf=' + csrfToken + '&cmd.' + name + ' ={main::Alarm_CreateNotifiers("' + name + '")}'); } \ No newline at end of file