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