diff --git a/fhem/FHEM/95_Alarm.pm b/fhem/FHEM/95_Alarm.pm index 8d0b8b715..b0b399116 100644 --- a/fhem/FHEM/95_Alarm.pm +++ b/fhem/FHEM/95_Alarm.pm @@ -35,13 +35,15 @@ use vars qw(%defs); # FHEM device/button definitions use vars qw(%intAt); # FHEM at definitions use vars qw($FW_ME); +use JSON; # imports encode_json, decode_json, to_json and from_json. + ######################### # Global variables my $alarmlinkname = "Alarms"; # link text my $alarmhiddenroom = "AlarmRoom"; # hidden room my $alarmpublicroom = "Alarm"; # public room my $alarmno = 8; -my $alarmversion = "3.0"; +my $alarmversion = "3.1"; my %alarm_transtable_EN = ( "ok" => "OK", @@ -82,8 +84,11 @@ my %alarm_transtable_EN = ( "disarmaction" => "Disarm Action", "waitaction" => "Wait Action", "cancelaction" => "Cancel Action", + "canceled" => "canceled by:", "alarm" => "Alarm", + "raised" => "raised by:", "alarms" => "Alarm System", + "setparms" => "Set Parameters", #-- "state" => "Security", "unlocked" => "Unlocked", @@ -133,8 +138,11 @@ my %alarm_transtable_EN = ( "disarmaction" => "Unscharf-Aktion", "waitaction" => "Warte-Aktion", "cancelaction" => "Widerruf-Aktion", + "canceled" => "widerrufen durch:", "alarm" => "Alarm", + "raised" => "ausgelöst durch:", "alarms" => "Alarmanlage", + "setparms" => "Parameter setzen", #-- "state" => "Sicherheit", "unlocked" => "Unverschlossen", @@ -163,9 +171,10 @@ sub Alarm_Initialize ($) { $hash->{GetFn} = "Alarm_Get"; $hash->{UndefFn} = "Alarm_Undef"; #$hash->{AttrFn} = "Alarm_Attr"; - my $attst = "lockstate:locked,unlocked testbutton:0,1 statedisplay:simple,color,table,none armdelay armwait armact disarmact cancelact"; + my $attst = "lockstate:locked,unlocked testbutton:0,1 statedisplay:simple,color,table,none noicons iconmap disarmcolor ". + "armwaitcolor armcolor alarmcolor armdelay armwait armact disarmact cancelact"; for( my $level=0;$level<$alarmno;$level++ ){ - $attst .=" level".$level."start level".$level."end level".$level."msg level".$level."xec:0,1 level".$level."onact level".$level."offact "; + $attst .=" level".$level."start level".$level."end level".$level."msg level".$level."xec level".$level."onact level".$level."offact "; } $hash->{AttrList} = $attst; @@ -181,7 +190,10 @@ sub Alarm_Initialize ($) { $alarmlinkname = $alarm_tt->{"alarms"}; $data{FWEXT}{Alarmx}{LINK} = "?room=".$alarmhiddenroom; - $data{FWEXT}{Alarmx}{NAME} = $alarmlinkname; + $data{FWEXT}{Alarmx}{NAME} = $alarmlinkname; + + $data{FWEXT}{"/Alarm_widget"}{FUNC} = "Alarm_widget"; + $data{FWEXT}{"/Alarm_widget"}{FORKABLE} = 0; return undef; } @@ -215,6 +227,23 @@ sub Alarm_Define ($$) { $data{FWEXT}{Alarmx}{LINK} = "?room=".$alarmhiddenroom; $data{FWEXT}{Alarmx}{NAME} = $alarmlinkname; $attr{$name}{"room"} = $alarmhiddenroom; + + #$data{FWEXT}{"/Alarm_widget"}{FUNC} = "Alarm_widget"; + #$data{FWEXT}{"/Alarm_widget"}{FORKABLE} = 0; + + my $date = Alarm_restore($hash,0); + #-- data seems to be ok, restore + if( defined($date) ){ + Alarm_restore($hash,1); + Log3 $name,1,"[Alarm_Define] data hash restored from save file with date $date"; + #-- intialization + }else{ + for( my $i=0;$i<$alarmno;$i++){ + $hash->{DATA}{"armstate"}{"level".$i} = "disarmed"; + } + Alarm_save($hash); + Log3 $name,1,"[Alarm_Define] data hash is initialized"; + } $modules{Alarm}{defptr}{$name} = $hash; @@ -224,6 +253,24 @@ sub Alarm_Define ($$) { return; } +sub Alarm_transform($){ + my ($hash) = @_; + + Log 1,"[Alarm] transforming old data format into new one"; + + my $md = 0; + for( my $i=0;$i<$alarmno;$i++){ + if( defined(AttrVal($hash->{NAME},"level".$i."xec",undef)) ){ + $md = 1; + $hash->{DATA}{"armstate"}{"level".$i} = AttrVal($hash->{NAME},"level".$i."xec",""); + fhem("deleteattr ".$hash->{NAME}." level".$i."xec"); + } + } + Alarm_save($hash) + if( $md==1 ); +} + + ######################################################################################### # # Alarm_Undef - Implements Undef function @@ -302,19 +349,14 @@ sub Alarm_CreateEntry($) { } #-- recover state from stored readings + readingsBeginUpdate($hash); for( my $level=0;$level<$alarmno;$level++ ){ - my $val = ReadingsVal($name,"level".$level,""); - if( $val eq "disarmed" ){# - CommandAttr (undef,$name.' level'.$level.'xec disarmed'); - }elsif( $val eq "armed" ){ - CommandAttr (undef,$name.' level'.$level.'xec armed'); - }else{ - Log3 $hash,1,"[Alarm $level] has undefined save data $val, disarming"; - CommandAttr (undef,$name.' level'.$level.'xec disarmed'); - } + my $val = $hash->{DATA}{"armstate"}{"level".$level}; + readingsBulkUpdate( $hash, "level".$level, $val); } - my $mga = Alarm_getstate($hash)." Keine Störung"; - readingsSingleUpdate( $hash, "state", $mga, 1 ); + my $mga = Alarm_getstate($hash); + readingsBulkUpdate( $hash, "state", $mga); + readingsEndUpdate( $hash,1 ); } @@ -344,21 +386,31 @@ sub Alarm_Set($@) { return "[Alarm] Invalid argument set $cmd"; } return; + #----------------------------------------------------------- } elsif ( $cmd =~ /^lock(ed)?$/ ) { readingsSingleUpdate( $hash, "lockstate", "locked", 0 ); return; + #----------------------------------------------------------- } elsif ( $cmd =~ /^unlock(ed)?$/ ) { readingsSingleUpdate( $hash, "lockstate", "unlocked", 0 ); return; + #----------------------------------------------------------- + } elsif ( $cmd =~ /^save/ ) { + return Alarm_save($hash); + + #----------------------------------------------------------- + } elsif ( $cmd =~ /^restore/ ) { + return Alarm_restore($hash,1); + } else { my $str = join(",",(0..($alarmno-1))); - return "[Alarm] Unknown argument " . $cmd . ", choose one of canceled:$str armed:$str disarmed:$str locked:noArg unlocked:noArg"; + return "[Alarm] Unknown argument " . $cmd . ", choose one of canceled:$str armed:$str disarmed:$str locked:noArg unlocked:noArg save:noArg restore:noArg"; } } ######################################################################################### # -# Alarm_Set - Implements the Get function +# Alarm_Get - Implements the Get function # # Parameter hash = hash of device addressed # @@ -372,68 +424,10 @@ sub Alarm_Get($@) { if ($arg eq "version") { return "Alarm.version => $alarmversion"; } else { - return "Unknown argument $arg choose one of version"; + return "Unknown argument $arg choose one of version:noArg"; } } -######################################################################################### -# -# Alarm_getstate - Helper function to assemble a state display -# -# Parameter hash = hash of device addressed -# -######################################################################################### - -sub Alarm_getstate($) { - my ($hash) = @_; - my $res = ''; - my $type = AttrVal($hash->{NAME},"statedisplay",0); - my $val; - #-------------------------- - if( $type eq "simple" ){ - for( my $level=0;$level<$alarmno;$level++ ){ - $val = $hash->{READINGS}{"level".$level}{VAL}; - if( $val eq "disarmed" ){ - $res .= '-'; - }elsif( $val eq "armed" ){ - $res .= 'O'; - }else{ - $res .= 'X'; - } - } - #-------------------------- - }elsif( $type eq "color" ){ - $res = ''; - for( my $level=0;$level<$alarmno;$level++ ){ - $val = $hash->{READINGS}{"level".$level}{VAL}; - if( $val eq "disarmed" ){ - $res .= ' '.$level; - }elsif( $val eq "armed" ){ - $res .= ' '.$level.''; - }else{ - $res .= ' '.$level.''; - } - } - $res.=''; - #-------------------------- - }elsif( $type eq "table" ){ - $res = ''; - for( my $level=0;$level<$alarmno;$level++ ){ - $val = $hash->{READINGS}{"level".$level}{VAL}; - if( $val eq "disarmed" ){ - $res .= '
'; - }elsif( $val eq "armed" ){ - $res .= ''; - }else{ - $res .= ''; - } - } - $res.='
'; - #-------------------------- - } - return $res; -} - ######################################################################################### # # Alarm_getsettings - Helper function to assemble the alarm settings for a device @@ -462,8 +456,11 @@ sub Alarm_getsettings($$$){ $aval[3] = ""; $chg = 1; } - #-- - if( int(@aval) != 4){ + #-- unset func may be missing + $aval[2] = "" + if(!defined($aval[2])); + #-- position 0:set by, 1:set func, 2:unset func, 3:delay + if( int(@aval) != 4 || !defined($aval[0]) || !defined($aval[1]) ){ Log3 $hash, 1, "[Alarm] Settings incomplete for alarmActor $dev"; } } @@ -474,6 +471,57 @@ sub Alarm_getsettings($$$){ return @aval; } +######################################################################################### +# +# Alarm_save +# +# Parameter hash = hash of the Alarm device +# +######################################################################################### + +sub Alarm_save($) { + my ($hash) = @_; + $hash->{DATA}{"savedate"} = localtime(time); + readingsSingleUpdate( $hash, "savedate", $hash->{DATA}{"savedate"}, 1 ); + my $json = JSON->new->utf8; + my $jhash0 = eval{ $json->encode( $hash->{DATA} ) }; + my $error = FileWrite("AlarmFILE",$jhash0); + #Log 1,"[Alarm_save] error=$error"; + return; +} + +######################################################################################### +# +# Alarm_restore +# +# Parameter hash = hash of the Alarm device +# +######################################################################################### + +sub Alarm_restore($$) { + my ($hash,$doit) = @_; + my $name = $hash->{NAME}; + my ($error,$jhash0) = FileRead("AlarmFILE"); + if( defined($error) && $error ne "" ){ + Log3 $name,1,"[Alarm_restore] read error=$error"; + return undef; + } + my $json = JSON->new->utf8; + my $jhash1 = eval{ $json->decode( $jhash0 ) }; + my $date = $jhash1->{"savedate"}; + #-- just for the first time, reading an old savefile + $date = localtime(time) + if( !defined($date)); + readingsSingleUpdate( $hash, "savedate", $date, 0 ); + if( $doit==1 ){ + $hash->{DATA} = {%{$jhash1}}; + Log3 $name,5,"[Alarm_restore] Data hash restored from save file with date ".$date; + return 1; + }else{ + return $date; + } +} + ######################################################################################### # # Alarm_Test - Test an actor @@ -514,8 +562,8 @@ sub Alarm_Exec($$$$$){ my ($name,$level,$dev,$evt,$act) = @_; my $hash = $defs{$name}; - my $xec = AttrVal($name, "level".$level."xec", 0); - my $xac = $hash->{READINGS}{'level'.$level}{VAL}; + my $xec = $hash->{DATA}{"armstate"}{"level".$level}; + my $xac = $hash->{READINGS}{"level".$level}{VAL}; my $msg = ''; my $cmd; my $mga; @@ -629,7 +677,7 @@ sub Alarm_Exec($$$$$){ readingsSingleUpdate( $hash, "level".$level,"canceled",1); readingsSingleUpdate( $hash, "level".$level,"armed",1); readingsSingleUpdate( $hash, "short", "", 0); - $mga = Alarm_getstate($hash)." ".$mga; + $mga = Alarm_getstate($hash)." ".$alarm_tt->{"canceled"}." ".$dev; readingsSingleUpdate( $hash, "state", $mga, 1 ); $msg = "[Alarm $level] canceled from device $dev"; Log3 $hash,3,$msg; @@ -647,6 +695,7 @@ sub Alarm_Exec($$$$$){ # Parameter name = name of the Alarm definition # level = Alarm level # dev = name of the device calling the alarm +# evt = Event of the device # act = action - "armed" or "disarmed" # ######################################################################################### @@ -655,20 +704,26 @@ sub Alarm_Arm($$$$$){ my ($name,$level,$dev,$evt,$act) = @_; my $hash = $defs{$name}; - my $xac = $hash->{READINGS}{"level"}{VAL}; - my $xec = AttrVal($name, 'level'.$level.'xec', 0); + my $xec = $hash->{DATA}{"armstate"}{"level".$level}; + my $xac = $hash->{READINGS}{"level".$level}{VAL}; my $msg = ''; my $mga; my $cmd; #-- arming the alarm - if( ($act eq "arm") && ( $xec ne "armed") ){ + if( ($act eq "arm") && ( $xac ne "armed") ){ my $xdl = AttrVal($name, "armdelay", 0); my $cmdwait = AttrVal($name, "armwait", 0); my $cmdact = AttrVal($name, "armact", 0); - if( ($xdl eq '')|($xdl eq '0:00')|($xdl eq '00:00') ){ - CommandAttr(undef,$name.' level'.$level.'xec armed'); + + #-- immediate arming + if( ($xdl eq '')||($xdl eq '0:00')||($xdl eq '00:00')||($evt eq "delay") ){ + #-- update state display + $hash->{DATA}{"armstate"}{"level".$level} = "armed"; readingsSingleUpdate( $hash, "level".$level,"armed",1 ); + readingsSingleUpdate( $hash, "state", Alarm_getstate($hash)." ".$hash->{READINGS}{"short"}{VAL}, 1 ); + #-- save new state + Alarm_save($hash); #--transform commands from fhem to perl level my @cmdactarr = split(/;/,$cmdact); my $cmdactf; @@ -681,8 +736,14 @@ sub Alarm_Arm($$$$$){ } $msg = "[Alarm $level] armed from alarmSensor $dev with event $evt"; Log3 $hash,3,$msg; + } elsif( $xdl =~ /([0-9])?:([0-5][0-9])?/ ){ - CommandAttr(undef,$name.' level'.$level.'xec armwait'); + #-- update state display + $hash->{DATA}{"armstate"}{"level".$level} = "armed"; + readingsSingleUpdate( $hash, "level".$level,"armwait",1 ); + readingsSingleUpdate( $hash, "state", Alarm_getstate($hash)." ".$hash->{READINGS}{"short"}{VAL}, 1 ); + #-- save new state + Alarm_save($hash); #--transform commands from fhem to perl level my @cmdactarr = split(/;/,$cmdact); my $cmdactf; @@ -694,30 +755,31 @@ sub Alarm_Arm($$$$$){ $cmdactf.="fhem(\"".$cmdactarr[$i]."\");;"; } } - #-- compose commands - $cmd = sprintf("define alarm%1d.arm.dly at +00:%02d:%02d {fhem(\"setreading %s level%1d armed\");;fhem(\"attr %s level%1dxec armed\");;%s}", - $level,$1,$2,$name,$level,$name,$level,$cmdactf); + #-- compose commands TODO + $cmd = sprintf("defmod alarm%1d.arm.dly at +00:%02d:%02d {Alarm_Arm(\"%s\",%1d,\"%s\",\"delay\",\"arm\");;%s}", + $level,$1,$2,$name,$level,$dev,$cmdactf); $msg = "[Alarm $level] will be armed from alarmSensor $dev with event $evt, delay $xdl"; - #-- delete old delayed arm - fhem('delete alarm'.$level.'.arm.dly' ) - if( defined $defs{'alarm'.$level.'.arm.dly'}); #-- define new delayed arm fhem($cmd); #-- execute armwait action fhem($cmdwait); - Log3 $hash,3,$msg; + Log3 $hash,1,$msg; }else{ $msg = "[Alarm $level] cannot be armed due to wrong delay timespec"; Log3 $hash,1,$msg; } #-- disarming implies canceling as well }elsif( ($act eq "disarm") && ($xec ne "disarmed")) { - #-- delete old delayed arm + #-- delete stale delayed arm fhem('delete alarm'.$level.'.arm.dly' ) if( defined $defs{'alarm'.$level.'.arm.dly'}); - CommandAttr (undef,$name.' level'.$level.'xec disarmed'); + $hash->{DATA}{"armstate"}{"level".$level} = "disarmed"; Alarm_Exec($name,$level,"program","disarm","cancel"); + #-- update state display readingsSingleUpdate( $hash, "level".$level,"disarmed",1 ); + readingsSingleUpdate( $hash, "state", Alarm_getstate($hash)." ".$hash->{READINGS}{"short"}{VAL}, 1 ); + #-- save new state + Alarm_save($hash); #-- $msg = "[Alarm $level] disarmed from alarmSensor $dev with event $evt"; $cmd = AttrVal($name, "disarmact", 0); @@ -748,6 +810,9 @@ sub Alarm_CreateNotifiers($){ return "State locked, cannot create new notifiers"; } + #-- temporary code: transferm from attributes to hash + Alarm_transform($hash); + for( my $level=0;$level<$alarmno;$level++ ){ #-- delete old defs in any case @@ -870,9 +935,9 @@ sub Alarm_CreateNotifiers($){ $nonum++; my @tarr = split(':',$aval[3]); if( int(@tarr) == 2){ - $cmd .= sprintf('define alarm%1ddly%1d at +00:%02d:%02d %s;',$level,$nonum,$tarr[0],$tarr[1],$aval[1]); + $cmd .= sprintf('defmod alarm%1ddly%1d at +00:%02d:%02d %s;',$level,$nonum,$tarr[0],$tarr[1],$aval[1]); }elsif( int(@tarr) == 3){ - $cmd .= sprintf('define alarm%1ddly%1d at +%02d:%02d:%02d %s;',$level,$nonum,$tarr[0],$tarr[1],$tarr[2],$aval[1]); + $cmd .= sprintf('defmod alarm%1ddly%1d at +%02d:%02d:%02d %s;',$level,$nonum,$tarr[0],$tarr[1],$tarr[2],$aval[1]); }else{ Log3 $name,1,"[Alarm $level] Invalid delay specification for actor $d, skipped"; $cmd .= $aval[1].';'; @@ -917,6 +982,183 @@ sub Alarm_CreateNotifiers($){ return "Created alarm notifiers"; } +######################################################################################### +# +# Alarm_getstate - Helper function to assemble a state display +# +# Parameter hash = hash of device addressed +# +######################################################################################### + +sub Alarm_getstate($) { + my ($hash) = @_; + + my $name = $hash->{NAME}; + my $res = ''; + my $type = AttrVal($hash->{NAME},"statedisplay",0); + my $val; + #-------------------------- + if( $type eq "simple" ){ + for( my $level=0;$level<$alarmno;$level++ ){ + $val = $hash->{READINGS}{"level".$level}{VAL}; + if( $val eq "disarmed" ){ + $res .= '-'; + }elsif( $val eq "armwait" ){ + $res .= 'o'; + }elsif( $val eq "armed" ){ + $res .= 'O'; + }else{ + $res .= 'X'; + } + } + #-------------------------- + }else{ + + my $dac = AttrVal($name,"disarmcolor","lightgray"); + my $ac = AttrVal($name,"armcolor","#53f3c7"); + my $awc = AttrVal($name,"armwaitcolor","#ffe658"); + my $alc = AttrVal($name,"alarmcolor","#fd5777"); + + if( $type eq "color" ){ + $res = ''; + for( my $level=0;$level<$alarmno;$level++ ){ + $val = $hash->{READINGS}{"level".$level}{VAL}; + if( $val eq "disarmed" ){ + $res .= ' '.$level; + }elsif( $val eq "armwait" ){ + $res .= ' '.$level.''; + }elsif( $val eq "armed" ){ + $res .= ' '.$level.''; + }else{ + $res .= ' '.$level.''; + } + } + $res.=''; + #-------------------------- + }elsif( $type eq "table" ){ + $res = ''; + for( my $level=0;$level<$alarmno;$level++ ){ + $val = $hash->{READINGS}{"level".$level}{VAL}; + if( $val eq "disarmed" ){ + $res .= '
'; + }elsif( $val eq "armwait" ){ + $res .= ''; + }elsif( $val eq "armed" ){ + $res .= ''; + }else{ + $res .= ''; + } + } + $res.='
'; + } + } + return $res; +} + +######################################################################################### +# +# Alarm_widget - returns animated SVG-code for the Alarm page +# +# Parameter name = name of the Alarm definition +# +######################################################################################### + +sub Alarm_widget($){ + my ($arg) = @_; + + my $name = $FW_webArgs{name}; + my $sizep = $FW_webArgs{size}; + my $gstate = ( $FW_webArgs{gstate} ? $FW_webArgs{gstate} : "disarmed"); + my $dstate = ( $FW_webArgs{dstate} ? $FW_webArgs{dstate} : "--------"); + my $inline = 0; + + #-- no webarg, check direct parameter. TODO: order + if( !defined($name) || $name eq "" ){ + if( $arg =~ /^name=(\w*)&gstate=(.+)&dstate=(.+)&size=(\d+x\d+)/ ){ + $name = $1; + $gstate = $2; + $dstate = $3; + $sizep = $4; + $inline = 1; + } + } + + Log 1,"[Alarm_widget] name=$name gstate=$gstate dstate=$dstate sizep=$sizep"; + + $name =~ s/'//g; + my @size=split('x',($sizep ? $sizep : '60x80')); + + my ($fillcolor,$fillcolor2); + my $dac = AttrVal($name,"disarmcolor","lightgray"); + my $ac = AttrVal($name,"armcolor","#53f3c7"); + my $awc = AttrVal($name,"armwaitcolor","#ffe658"); + my $alc = AttrVal($name,"alarmcolor","#fd5777"); + + if($gstate eq "disarmed"){ + $fillcolor = AttrVal($name,"disarmcolor",$dac); + $fillcolor2 = "white"; + }elsif($gstate eq "armed"){ + $fillcolor = AttrVal($name,"armcolor",$ac); + $fillcolor2 = "white"; + }elsif($gstate eq "mixed"){ + $fillcolor = AttrVal($name,"armwaitcolor",$awc); + $fillcolor2 = "white"; + }else{ + $fillcolor = AttrVal($name,"alarmcolor",$alc); + $fillcolor2 = $fillcolor; + } + + my $hash = $defs{$name}; + my $id = $defs{$name}{NR}; + my $ret=""; + + $ret = ""; + $ret .= "". + ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + for( my $level=0;$level<$alarmno;$level++ ){ + my $val = $hash->{READINGS}{"level".$level}{VAL}; + my $col; + if($val eq "disarmed"){ + $col = $dac; + }elsif($val eq "armed"){ + $col = $ac; + }elsif($val eq "armwait"){ + $col = $awc; + }else{ + $col = $alc; + } + $ret .= ""; + } + $ret .= ""; + + return $ret; + + if( $inline ){ + return $ret; + }else{ + $FW_RETTYPE = "image/svg+xml"; + $FW_RET=""; + FW_pO $ret; + return ($FW_RETTYPE, $FW_RET); + } +} + ######################################################################################### # # Alarm_Html - returns HTML code for the Alarm page @@ -944,7 +1186,7 @@ sub Alarm_Html($) } } - #-- + #-- update state display readingsSingleUpdate( $hash, "state", Alarm_getstate($hash)." ".$hash->{READINGS}{"short"}{VAL}, 1 ); #-- @@ -954,30 +1196,92 @@ sub Alarm_Html($) #-- $ret .= "\n"; $ret .= "\n"; - $ret .= "\n"; + #--- here we insert the icon + $ret .= ""; + $ret .= "\n"; #-- settings table my $row=1; - $ret .= ""; - $ret .= ""; #-- sensors table $row=1; - $ret .= ""; - $ret .= ""; #-- actors table $row=1; - $ret .= ""; - $ret .= "\n"; $ret .= "
"; + $ret .= "
".Alarm_widget("name=".$name."&gstate=".$iconstate."&dstate=".$detailstate."&size=60x80")."
" + if( AttrVal($name,"noicons",0)==0 ); + $ret .= "
{"setparms"}."\" onclick=\"javascript:alarm_set('$name')\"/>". + "
"; + for( my $k=0;$k<$alarmno;$k++ ){ + $ret .= "
".$hash->{READINGS}{"level".$k}{VAL}."
"; + } + $ret .= "
".$alarm_tt->{"settings"}."
\n"; + $ret .= ""; + $ret .= ""; + $ret .= "
".$alarm_tt->{"settings"}."
\n"; $ret .= ""; $ret .= "\n", ($row&1)?"odd":"even"); $ret .= "". @@ -1011,12 +1316,12 @@ sub Alarm_Html($) $ret .= sprintf("\n"; } - $ret .= "
\n"; - $ret .= ""; + $ret .= ""; $ret .= "\n"; $ret .= "\n"; $ret .="
".$alarm_tt->{"armbutton"}." ↠
".$alarm_tt->{"armbutton"}." ↠ ".$alarm_tt->{"waitaction"}." "; $ret .= sprintf("",(AttrVal($name, "armwait","") eq "1")?"":AttrVal($name, "armwait","")); - $ret .= " ↴ ".$alarm_tt->{"delay"}."
↲"; + $ret .= "
↴ ".$alarm_tt->{"delay"}."
↲"; $ret .= sprintf("",(AttrVal($name, "armdelay","0:00") eq "1")?"":AttrVal($name, "armdelay","0:00")); $ret .= "
".$alarm_tt->{"armaction"}." "; @@ -985,7 +1289,7 @@ sub Alarm_Html($) $ret .= "
".$alarm_tt->{"disarmbutton"}."↠".$alarm_tt->{"disarmaction"}." "; $ret .= sprintf("",(AttrVal($name, "disarmact","") eq "1")?"":AttrVal($name, "disarmact","")); - $ret .= "
".$alarm_tt->{"cancelbutton"}." ↠ ".$alarm_tt->{"cancelaction"}." "; + $ret .= "
".$alarm_tt->{"cancelbutton"}." ↠ ".$alarm_tt->{"cancelaction"}." "; $ret .= sprintf("",(AttrVal($name, "cancelact","") eq "1")?"":AttrVal($name, "cancelact","")); $ret .= "
".$alarm_tt->{"level"}."".$alarm_tt->{"time"}." [hh:mm]
". @@ -1004,6 +1308,7 @@ sub Alarm_Html($) 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   ". "",($xval eq "armed")?"checked=\"checked\"":""). "{"cancel"}."\" onclick=\"javascript:alarm_cancel('$name','$k')\"/>
".$alarm_tt->{"sensors"}."
\n"; + $ret .= ""; + $ret .= ""; + $ret .= "
".$alarm_tt->{"sensors"}."
\n"; $ret .= "\n"; @@ -1042,12 +1347,12 @@ sub Alarm_Html($) ($aval[3] eq "arm")?"selected=\"seleced\"":"",($aval[3] eq "disarm")?"selected=\"selected\"":""); } } - $ret .= "
".$alarm_tt->{"notifyto"}." ".$alarm_tt->{"alarm"}." ".$alarm_tt->{"level"}."
". join("     ",(0..($alarmno-1)))."
". $alarm_tt->{"notifyby"}." ".$alarm_tt->{"regexp"}."".$alarm_tt->{"messagepart"}." I".$alarm_tt->{"action"}."
".$alarm_tt->{"actors"}."
\n"; + $ret .= ""; + $ret .= "\n"; + $ret .= "
".$alarm_tt->{"actors"}."
\n"; $ret .= "\n"; } } - $ret .= "
".$alarm_tt->{"setby"}." ".$alarm_tt->{"alarm"}." ".$alarm_tt->{"level"}."
".join("     ",(0..($alarmno-1))). "
".$alarm_tt->{"setaction"}; $ret .= "  (".$alarm_tt->{"testaction"}.")" @@ -1077,7 +1382,7 @@ sub Alarm_Html($) $ret .= "
"; @@ -1093,6 +1398,7 @@ sub Alarm_Html($)

Alarm

+ + =end html =begin html_DE

Alarm

+ =end html_DE =cut diff --git a/fhem/FHEM/95_YAAHM.pm b/fhem/FHEM/95_YAAHM.pm index 353650494..0bbdfe601 100644 --- a/fhem/FHEM/95_YAAHM.pm +++ b/fhem/FHEM/95_YAAHM.pm @@ -6,8 +6,6 @@ # # Prof. Dr. Peter A. Henning # -# TODO: Löschen readings, wenn timer gelöscht -# # $Id$ # ######################################################################################## @@ -50,7 +48,7 @@ my $yaahmname; my $yaahmlinkname = "Profile"; # link text my $yaahmhiddenroom = "ProfileRoom"; # hidden room my $yaahmpublicroom = "Unsorted"; # public room -my $yaahmversion = "1.30"; +my $yaahmversion = "1.31"; my $firstcall = 1; my %yaahm_transtable_EN = ( @@ -328,14 +326,11 @@ my @profmode = ("party","absence","donotdisturb"); my @profday = ("vacation","holiday"); #-- color schemes -my $csnum=2; my @csmode; my @csmode1 = ("#53f3c7","#8bfa56","#ff9458","#fd5777"); -my @csmode2 = ("#35ffc7","#77ff35","#ff7e35","#ff355e"); my @csstate; my @csstate1 = ("#53f3c7","#ff9458","#f554e2","#fd5777"); -my @csstate2 = ("#35ffc7","#ff7e35","#ff35e2","#ff355e"); #-- temporary fix for update purpose sub YAAHM_restore($$){}; @@ -358,7 +353,8 @@ sub YAAHM_Initialize ($) { $hash->{UndefFn} = "YAAHM_Undef"; $hash->{AttrFn} = "YAAHM_Attr"; my $attst = "linkname publicroom hiddenroom lockstate:locked,unlocked simulation:0,1 ". - "timeHelper modeHelper modeAuto:0,1 stateDevices:textField-long stateInterval noicons:0,1 colorscheme:1,2 stateWarning stateHelper stateAuto:0,1 ". + "modecolor0 modecolor1 modecolor2 modecolor3 statecolor0 statecolor1 statecolor2 statecolor3 ". + "timeHelper modeHelper modeAuto:0,1 stateDevices:textField-long stateInterval noicons:0,1 stateWarning stateHelper stateAuto:0,1 ". "holidayDevices:textField-long vacationDevices:textField-long specialDevices:textField-long"; $hash->{AttrList} = $attst; @@ -537,6 +533,32 @@ sub YAAHM_Attr($$$) { } } #--------------------------------------- + }elsif ( ($cmd eq "set") && ($attrName =~ /modecolor(\d)/) ) { + my $ci = $1; + if( $ci >= 0 && $ci <= 3 ){ + $csmode[$ci] = $attrVal; + } + + #--------------------------------------- + }elsif ( ($cmd eq "del") && ($attrName =~ /modecolor(\d)/) ) { + my $ci = $1; + if( $ci >= 0 && $ci <= 3 ){ + $csmode[$ci] = $csmode1[$ci]; + } + #--------------------------------------- + }elsif ( ($cmd eq "set") && ($attrName =~ /statecolor(\d)/) ) { + my $ci = $1; + if( $ci >= 0 && $ci <= 3 ){ + $csstate[$ci] = $attrVal; + } + + #--------------------------------------- + }elsif ( ($cmd eq "del") && ($attrName =~ /statecolor(\d)/) ) { + my $ci = $1; + if( $ci >= 0 && $ci <= 3 ){ + $csstate[$ci] = $csstate1[$ci]; + } + #--------------------------------------- }elsif ( ($cmd eq "set") && ($attrName eq "linkname") ) { $yaahmlinkname = $attrVal; @@ -579,17 +601,7 @@ sub YAAHM_Attr($$$) { } } } - #--------------------------------------- - }elsif ( ($cmd eq "set") && ($attrName eq "colorscheme") ) { - Log 1,"==================> colorscheme"; - if( $attrVal == 2 ){ - @csmode = @csmode2; - @csstate = @csstate2; - }else{ - @csmode = @csmode2; - @csstate = @csstate2; - } - + #--------------------------------------- }elsif ( ($cmd eq "delete") && ($attrName eq "stateDevices") ) { fhem("deletereading $name sdev_housestate"); @@ -713,6 +725,10 @@ sub YAAHM_Set($@) { $cmd = "next_".$if; } return YAAHM_nextWeeklyTime($name,$cmd,$args[1],$exec); + + #----------------------------------------------------------- + }elsif ( $cmd =~ /^checkstate.*/ ) { + YAAHM_InternalTimer("check",time()+ $args[0], "YAAHM_checkstate", $hash, 0); #----------------------------------------------------------- }elsif ( $cmd =~ /^time.*/ ) { @@ -749,6 +765,7 @@ sub YAAHM_Set($@) { $firstcall = 1; YAAHM_updater($hash); YAAHM_InternalTimer("check",time()+ 5, "YAAHM_checkstate", $hash, 0); + #----------------------------------------------------------- } elsif ( $cmd eq "createWeekly" ){ return "[YAAHM] missing name for new weekly profile" @@ -781,7 +798,7 @@ sub YAAHM_Set($@) { #-- find index $imax = int(@{$hash->{DATA}{"WT"}}); $if= undef; - for( my $j=0;$j<$imax;$j++){ + for( my $j=2;$j<$imax;$j++){ if($hash->{DATA}{"WT"}[$j]{"name"} eq $args[0]){ $if = $j; last; @@ -792,6 +809,25 @@ sub YAAHM_Set($@) { splice(@{$hash->{DATA}{"WT"}},$if,1); #-- delete timer fhem("delete ".$name.".wtimer_".$if.".IF"); + #-- delete readings + for( my $j=$if;$j<$imax-1;$j++){ + $hash->{READINGS}{"ring_".$j}{VAL} = $hash->{READINGS}{"ring_".($j+1)}{VAL}; + $hash->{READINGS}{"ring_".$j."_1"}{VAL} = $hash->{READINGS}{"ring_".($j+1)."_1"}{VAL}; + $hash->{READINGS}{"next_".$j}{VAL} = $hash->{READINGS}{"next_".($j+1)}{VAL}; + $hash->{READINGS}{"today_".$j}{VAL} = $hash->{READINGS}{"today_".($j+1)}{VAL}; + $hash->{READINGS}{"today_".$j."_e"}{VAL} = $hash->{READINGS}{"today_".($j+1)."_e"}{VAL}; + $hash->{READINGS}{"tomorrow_".$j}{VAL} = $hash->{READINGS}{"tomorrow_".($j+1)}{VAL}; + $hash->{READINGS}{"tomorrow_".$j."_e"}{VAL} = $hash->{READINGS}{"tomorrow_".($j+1)."_e"}{VAL}; + $hash->{READINGS}{"tr_wake_".$j}{VAL} = $hash->{READINGS}{"tr_wake".($j+1)}{VAL}; + } + fhem("deletereading ".$name." ring_".($imax-1)); + fhem("deletereading ".$name." ring_".($imax-1)."_1"); + fhem("deletereading ".$name." next_".($imax-1)); + fhem("deletereading ".$name." today_".($imax-1)); + fhem("deletereading ".$name." today_".($imax-1)."_e"); + fhem("deletereading ".$name." tomorrow_".($imax-1)); + fhem("deletereading ".$name." tomorrow_".($imax-1)."_e"); + fhem("deletereading ".$name." tr_wake_".($imax-1)); #-- save everything YAAHM_save($hash); fhem("save"); @@ -802,7 +838,7 @@ sub YAAHM_Set($@) { my $str = ""; return "[YAAHM] Unknown argument " . $cmd . ", choose one of". " manualnext time:".join(',',@times)." mode:".join(',',@modes). - " state:".join(',',@states)." locked:noArg unlocked:noArg save:noArg restore:noArg initialize:noArg createWeekly deleteWeekly"; + " state:".join(',',@states)." locked:noArg unlocked:noArg save:noArg checkstate:0,5,10 restore:noArg initialize:noArg createWeekly deleteWeekly"; } } @@ -1424,19 +1460,6 @@ sub YAAHM_checkstate($) { return undef } -######################################################################################### -# -# YAAHM_SM - State machine -# -# Parameter hash = hash of device addressed -# -######################################################################################### - -sub YAAHM_SM($) { - my ($hash) = @_; - -} - ######################################################################################### # # YAAHM_informer - Tell FHEMWEB to inform this page @@ -1721,6 +1744,9 @@ sub YAAHM_setWeeklyTime($) { #-- today's waketime my $tga = $sg0; $tga =~ s/://; + #-- tomorrow's waketime + my $tgm = $sg1; + $tgm =~ s/://; #-- "next" input my $nga = (defined $ng)?$ng:""; $nga =~ s/://; @@ -1729,6 +1755,20 @@ sub YAAHM_setWeeklyTime($) { if( $nga eq "" ){ $ring_0 = $sg0; $ring_1 = $sg1; + $ng = ""; + $hash->{DATA}{"WT"}[$i]{ "next" }=""; + #-- "next" is the same as today and today not over + }elsif( ($nga eq $tga) && ($tga > $lga)){ + $ring_0 = $sg0; + $ring_1 = $sg1; + $ng = ""; + $hash->{DATA}{"WT"}[$i]{ "next" }=""; + #-- "next" is the same as tomorrow and today over + }elsif( ($nga eq $tgm) && ($tga < $lga)){ + $ring_0 = $sg0; + $ring_1 = $sg1; + $ng = ""; + $hash->{DATA}{"WT"}[$i]{ "next" }=""; #-- "next" is off }elsif( $nga eq "off" ){ #-- today's waketime not over => we mean today @@ -1750,10 +1790,11 @@ sub YAAHM_setWeeklyTime($) { }else{ #-- "next" after current time => we mean today if( $nga > $lga ){ - #-- only restore standard setting + #-- only restore standard setting (do we come here at all ?) if( $ng eq $sg0 ){ $sg0mod = $sg0; $ring_0 = $sg0; + $ng = ""; $hash->{DATA}{"WT"}[$i]{ "next" } = ""; }else{ $sg0mod = "$ng (man)"; @@ -1762,10 +1803,11 @@ sub YAAHM_setWeeklyTime($) { $ring_1 = $sg1; #-- "next" before current time => we mean tomorrow }else{ - #-- only restore standard setting + #-- only restore standard setting (do we come here at all ?) if( $ng eq $sg1 ){ $sg0mod = $sg1; $ring_1 = $sg1; + $ng = ""; $hash->{DATA}{"WT"}[$i]{ "next" } = ""; }else{ $sg1mod = "$ng (man)"; @@ -2980,7 +3022,7 @@ sub YAAHM_timewidget($){ FW_pO ''; #-- evening to sunset sector - $dir = ( $ss < $ev ) ? 1 : 0; + $dir = ( $ss < $ev ) ? 0 : 1; FW_pO ''; #-- midnight line @@ -3070,6 +3112,9 @@ sub YAAHM_toptable($){ $ret .= "var csmode = [\"".$csmode[0]."\",\"".$csmode[1]."\",\"".$csmode[2]."\",\"".$csmode[3]."\"];"; $ret .= "var csstate = [\"".$csstate[0]."\",\"".$csstate[1]."\",\"".$csstate[2]."\",\"".$csstate[3]."\"];"; + $ret .= "var blinking = 0;\n"; + $ret .= "var hscolor = \"".$csstate[0]."\";\n"; + $ret .= "var dailyno = ".$dailyno.";\n"; $ret .= "var dailykeys = [\"".join("\",\"",(sort YAAHM_dsort keys %dailytable))."\"];\n"; @@ -3080,9 +3125,14 @@ sub YAAHM_toptable($){ $ret .= "," if( $i!=0 ); $ret .= "\"".$hash->{DATA}{"WT"}[$i]{"name"}."\""; - } + } $ret .= "];\n"; + #-- watcher for next hidden divisions + for( my $i=2;$i<$weeklyno;$i++){ + $ret .= "$(\"body\").on('DOMSubtreeModified', \"#wt".$i."_o\",function () {nval = document.getElementById(\"wt".$i."_o\").innerHTML;document.getElementById(\"wt".$i."_n\").value = nval;})\n"; + } $ret .= "\n"; + $ret .= "
".ReadingsVal($name,"housestate",undef)."
". "
".ReadingsVal($name,"housemode",undef)."
"; $ret .= "\n"; @@ -3108,7 +3158,7 @@ sub YAAHM_toptable($){ $ret .= "". "". ""; + ""; for( my $i=0; $i<$cols; $i++){ if( $i < int(@states)){ $ret .= ""; + $ret .= "\n"; for (my $i=0;$i<$weeklyno;$i++){ if($i<$weeklyno-1){ $styl= "border-bottom:1px solid gray;border-top:1px solid gray"; @@ -3134,7 +3184,7 @@ sub YAAHM_toptable($){ $wupn = $hash->{DATA}{"WT"}[$i]{"name"}; $nval = ( defined($hash->{DATA}{"WT"}[$i]{"next"}) ) ? $hash->{DATA}{"WT"}[$i]{"next"} : ""; $ret .= sprintf("",$i,$i); + "
$nval
\n",$i,$i,$i); } $ret .= "\n"; $ret .= "
".YAAHM_statewidget($hash)."".$yaahm_tt->{"state"}."
".ReadingsVal($name,"tr_housestate",undef). - "
".ReadingsVal($name,"sym_housestate",undef)."
".ReadingsVal($name,"sym_housestate",undef)."
{$states[$i]}. @@ -3124,7 +3174,7 @@ sub YAAHM_toptable($){ #-- repeat manual next for every weekly table my $nval = ""; my $wupn; - $ret .= "
".$yaahm_tt->{"manual"}."
".$yaahm_tt->{"manual"}."$wupn
". - "

"; @@ -3546,6 +3596,7 @@ sub YAAHM_Longtable($){ Notes:

Set

Get

@@ -3661,9 +3712,14 @@ sub YAAHM_Longtable($){
  • attr <name> noicons 0|1
    when set to 1, animated icons are suppressed
  • -
  • attr <name> colorscheme - 1|2 -
    color scheme for the icons
  • +
  • attr <name> modecolor[0|1|2|3] color +
    four color specifications to signal the modes normal (default #53f3c7), + party (default #8bfa56), absence (default #ff9458), + donotodisturb (default #fd5777),
  • +
  • attr <name> statecolor[0|1|2|3] color +
    four color specifications to signal the states unsecured (default #53f3c7), + secured (default #ff9458), + protected (default #f554e2) and guarded (default #fd5777)
  • attr <name> timeHelper <name of perl program>
    name of a perl function that is called at each time step of the daily profile and for the two default weekly profiles
  • attr <name> modeHelper <name of perl program> diff --git a/fhem/www/pgm2/alarm.js b/fhem/www/pgm2/alarm.js index cd71bf6fa..4de207149 100644 --- a/fhem/www/pgm2/alarm.js +++ b/fhem/www/pgm2/alarm.js @@ -1,10 +1,10 @@ //######################################################################################## // alarm.js -// Version 2.81 +// Version 3.1 // See 95_Alarm for licensing //######################################################################################## //# Prof. Dr. Peter A. Henning - + function encodeParm(oldval) { var newval; newval = oldval.replace(/\+/g, '%2B'); @@ -13,134 +13,284 @@ function encodeParm(oldval) { return newval; } -//var ah = new HashTable('l0s','','l0e',''); +//------------------------------------------------------------------------------------------------------ +// Animated Icon +//------------------------------------------------------------------------------------------------------ + +var bellfill; + +function blinkbell() { + var w = document.getElementById("alarmicon"); + if (w) { + if (bellfill == alarmcolor) { + bellfill = "white"; + w.getElementsByClassName("alarmst_b")[0].setAttribute("fill", "white"); + w.getElementsByClassName("alarmst_sb")[0].setAttribute("fill", "white"); + } else { + bellfill = alarmcolor; + w.getElementsByClassName("alarmst_b")[0].setAttribute("fill", alarmcolor); + w.getElementsByClassName("alarmst_sb")[0].setAttribute("fill", alarmcolor); + } + } +} + +function updateIcon(name, alarmst) { + var w = document.getElementById(name); + if (w) { + switch (alarmst) { + case "disarmed": + w.getElementsByClassName("alarmst_b")[0].setAttribute("fill", "white"); + w.getElementsByClassName("alarmst_sb")[0].setAttribute("fill", "white"); + if (blinking == 1) { + clearInterval(blinker); + blinking = 0; + } + break; + + case "mixed": + w.getElementsByClassName("alarmst_b")[0].setAttribute("fill", armwaitcolor); + w.getElementsByClassName("alarmst_sb")[0].setAttribute("fill", "white"); + if (blinking == 1) { + clearInterval(blinker); + blinking = 0; + } + break; + + case "armed": + w.getElementsByClassName("alarmst_b")[0].setAttribute("fill", armcolor); + w.getElementsByClassName("alarmst_sb")[0].setAttribute("fill", "white"); + if (blinking == 1) { + clearInterval(blinker); + blinking = 0; + } + break; + + default: + if (blinking == 0) { + blinker = setInterval('blinkbell()', 250); + blinking = 1; + } + } + } +} + +$("body").on('DOMSubtreeModified', "#hid_levels", function () { + var w = document.getElementById("hid_levels"); + var v = document.getElementById("alarmicon"); + var t = v.getElementsByClassName("arec"); + var ifnd; + var sfnd; + var col; + for (i = 0; i < alarmno; i++) { + var s = w.getElementsByClassName("hid_lx")[i].innerHTML; + if (ast[i] != s) { + switch(s){ + case "disarmed": + col = disarmcolor; + break; + case "armwait": + col = armwaitcolor; + break; + case "armed": + col = armcolor; + break; + default: + col = alarmcolor + } + t[i].setAttribute("fill",col); + ast[i] = s; + ifnd = i; + sfnd = s; + } + } + if (ifnd && (iconmap.includes(ifnd))) { + var aan = true; + var adn = true; + var aln = ""; + var atn = ""; + for (i = 0; i < alarmno; i++) { + if (iconmap.includes(i)) { + var s = ast[i]; + if (s != "disarmed" && s != "armwait" && s != "armed") { + aln = aln + i + ","; + atn = atn + s + ","; + } else { + adn = adn && ((s == "disarmed")||(s == "armwait")); + aan = aan && (s == "armed"); + } + } + } + + if (adn != ad || aan != aa || aln != al) { + aa = aan; + ad = adn; + al = aln; + at = atn; + + var iconstate; + if (al != "") { + iconstate = al; + } else { + if (aa && (! ad)) { + iconstate = "armed"; + } else { + if ((! aa) && ad) { + iconstate = "disarmed"; + } else { + iconstate = "mixed"; + } + } + } + updateIcon('alarmicon', iconstate); + } + } +}); //------------------------------------------------------------------------------------------------------ // Write the Attribute Value //------------------------------------------------------------------------------------------------------ -function alarm_setAttribute(name, attr, val) {//set Alarm Attribute - var location = document.location.pathname; - if (location.substr(location.length-1,1) == '/') {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)); + +function alarm_setAttribute(name, attr, val) { + //set Alarm Attribute + var location = document.location.pathname; + if (location.substr(location.length -1, 1) == '/') { + 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)); } -function alarm_cancel(name,level){ +function alarm_cancel(name, level) { var val; var nam; - + var location = document.location.pathname; - if (location.substr(location.length-1,1) == '/') {location = location.substr(0,location.length-1);} - var url = document.location.protocol+"//"+document.location.host+location; + if (location.substr(location.length -1, 1) == '/') { + location = location.substr(0, location.length -1); + } + 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&cmd.'+name+'={Alarm_Exec("'+name+'",'+level+',"web","button","off")}'); - } - -function alarm_arm(name,level){ +function alarm_arm(name, level) { var val; var nam; - var command = document.getElementById('l'+level+'x').checked; - if (command == true){ - command="arm"; - }else{ - command="disarm"; + var command = document.getElementById('l' + level + 'x').checked; + if (command == true) { + command = "arm"; + } else { + command = "disarm"; } var location = document.location.pathname; - if (location.substr(location.length-1,1) == '/') {location = location.substr(0,location.length-1);} - var url = document.location.protocol+"//"+document.location.host+location; + if (location.substr(location.length -1, 1) == '/') { + location = location.substr(0, location.length -1); + } + 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&cmd.'+name+'={Alarm_Arm("'+name+'",'+level+',"web","button","'+command+'")}'); - } - -function alarm_testaction(name,dev,type){ +function alarm_testaction(name, dev, type) { var cmd; var nam; - if(type == 'set'){ - cmd = document.getElementById(dev).parentElement.children[2].children[0].value; - }else{ - cmd = document.getElementById(dev).parentElement.children[3].children[0].value; + if (type == 'set') { + cmd = document.getElementById(dev).parentElement.children[2].children[0].value; + } else { + cmd = document.getElementById(dev).parentElement.children[3].children[0].value; } var cmds; cmds = cmd.replace(/\\/g, '\\'); cmds = cmds.replace(/\'/g, '\"'); cmds = cmds.replace(/\$/g, '\\$'); - alert( cmds ); - - var location = document.location.pathname; - if (location.substr(location.length-1,1) == '/') {location = location.substr(0,location.length-1);} - var url = document.location.protocol+"//"+document.location.host+location; - - FW_cmd(url+'?XHR=1&cmd.'+name+'={Alarm_Test("'+name+'","' + cmds + '")}'); - } + alert(cmds); + var location = document.location.pathname; + if (location.substr(location.length -1, 1) == '/') { + location = location.substr(0, location.length -1); + } + var url = document.location.protocol + "//" + document.location.host + location; + + FW_cmd(url + '?XHR=1&cmd.' + name + '={Alarm_Test("' + name + '","' + cmds + '")}'); +} -function alarm_set(name){ + +function alarm_set(name) { var val; var nam; - + var location = document.location.pathname; - if (location.substr(location.length-1,1) == '/') {location = location.substr(0,location.length-1);} - var url = document.location.protocol+"//"+document.location.host+location; + if (location.substr(location.length -1, 1) == '/') { + location = location.substr(0, location.length -1); + } + 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&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)); // 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); - if (document.getElementById('l'+i+'x').checked == true ){ + 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); + if (document.getElementById('l' + i + 'x').checked == true) { val = "armed"; - }else{ + } else { val = "disarmed"; } - FW_cmd(url+'?XHR=1&cmd.'+name+'=attr '+name+' level'+i+'xec ' + val); - } + FW_cmd(url + '?XHR=1&cmd.' + name + '=attr ' + name + ' level' + i + 'xec ' + val); + } //for (var k in ah.items) { // ah.setItem(k,document.getElementById(k).value); //} // acquiring data for each sensor - var sarr = document.getElementsByName('sensor'); - for (var k = 0; k < sarr.length; k++){ - nam = sarr[k].getAttribute('informId'); - val = ""; - for (var i = 0; i < alarmno; i++){ - if (sarr[k].children[1].children[i].checked == true ){ - val += "alarm"+i+","; + var sarr = document.getElementsByName('sensor'); + for (var k = 0; + k < sarr.length; + k++) { + nam = sarr[k].getAttribute('informId'); + val = ""; + for (var i = 0; + i < alarmno; + i++) { + if (sarr[k].children[1].children[i].checked == true) { + val += "alarm" + i + ","; } } - val += "|"+sarr[k].children[2].children[0].value; - val += "|"+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)); + val += "|" + sarr[k].children[2].children[0].value; + val += "|" + 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)); } // acquiring data for each actor - var aarr = document.getElementsByName('actor'); - for (var k = 0; k < aarr.length; k++){ - nam = aarr[k].getAttribute('informId'); - val = ""; - for (var i = 0; i < alarmno; i++){ + var aarr = document.getElementsByName('actor'); + for (var k = 0; + k < aarr.length; + k++) { + nam = aarr[k].getAttribute('informId'); + val = ""; + for (var i = 0; + i < alarmno; + i++) { //alert(" Checking "+k+" "+i) - if (aarr[k].children[1].children[i].checked == true ){ - val += "alarm"+i+","; + if (aarr[k].children[1].children[i].checked == true) { + 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 += "|" + 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)); } // creating notifiers - FW_cmd(url+'?XHR=1&cmd.' + name + ' ={main::Alarm_CreateNotifiers("' + name + '")}'); - -} - - + FW_cmd(url + '?XHR=1&cmd.' + name + ' ={main::Alarm_CreateNotifiers("' + name + '")}'); +} \ No newline at end of file diff --git a/fhem/www/pgm2/yaahm.js b/fhem/www/pgm2/yaahm.js index 58682e45a..1c5b31462 100644 --- a/fhem/www/pgm2/yaahm.js +++ b/fhem/www/pgm2/yaahm.js @@ -98,10 +98,62 @@ function yaahm_setnext(name, i) { FW_cmd(url + '?XHR=1&cmd.' + name + '={main::YAAHM_nextWeeklyTime("' + name + '","next_' + i + '","' + nval + '")}'); } +//------------------------------------------------------------------------------------------------------ +// Write field value for next - first two here, the others dynamically +//------------------------------------------------------------------------------------------------------ + +$("body").on('DOMSubtreeModified', "#wt0_o", + function () { + nval = document.getElementById("wt0_o").innerHTML; + document.getElementById("wt0_n").value = nval; + }) + +$("body").on('DOMSubtreeModified', "#wt1_o", + function () { + nval = document.getElementById("wt1_o").innerHTML; + document.getElementById("wt1_n").value = nval; + }) + //------------------------------------------------------------------------------------------------------ // Animate housestate icon //------------------------------------------------------------------------------------------------------ +var blinker; +var hsfill; +var hscolor; + +function blinkhs() { + var w = document.getElementById("wid_hs"); + if (w) { + if (hsfill == hscolor) { + hsfill = "white"; + w.getElementsByClassName("hs_is")[0].setAttribute("fill", "white"); + } else { + hsfill = hscolor; + w.getElementsByClassName("hs_is")[0].setAttribute("fill", hscolor); + } + } +} + +$("body").on('DOMSubtreeModified', "#sym_hs", +function () { + var w = document.getElementById("wid_hs"); + if (w) { + var symnew = document.getElementById("sym_hs").innerHTML; + if (blinking == 1 && symnew.includes("green")) { + clearInterval(blinker); + blinking = 0; + w.getElementsByClassName("hs_is")[0].setAttribute("fill", hscolor); + } else { + if (blinking == 0 && ! symnew.includes("green")) { + hscolor = w.getElementsByClassName("hs_is")[0].getAttribute("fill"); + blinker = setInterval('blinkhs()', 1000); + blinking = 1; + } + } + } +}) + $("body").on('DOMSubtreeModified', "#hid_hs", function () { var hsnew = document.getElementById("hid_hs").innerHTML; @@ -111,6 +163,7 @@ function () { if (w) { switch (hsnew) { case "unsecured": + hscolor = csstate[0]; w.getElementsByClassName("hs_is")[0].setAttribute("fill", csstate[0]); w.getElementsByClassName("hs_smb")[0].setAttribute("visibility", "hidden"); w.getElementsByClassName("hs_unlocked")[0].setAttribute("visibility", "visible"); @@ -118,6 +171,7 @@ function () { w.getElementsByClassName("hs_eye")[0].setAttribute("visibility", "hidden"); break; case "secured": + hscolor = csstate[1]; w.getElementsByClassName("hs_is")[0].setAttribute("fill", csstate[1]); w.getElementsByClassName("hs_smb")[0].setAttribute("visibility", "hidden"); w.getElementsByClassName("hs_unlocked")[0].setAttribute("visibility", "hidden"); @@ -125,6 +179,7 @@ function () { w.getElementsByClassName("hs_eye")[0].setAttribute("visibility", "hidden"); break; case "protected": + hscolor = csstate[2]; w.getElementsByClassName("hs_is")[0].setAttribute("fill", csstate[2]); w.getElementsByClassName("hs_smb")[0].setAttribute("visibility", "visible"); w.getElementsByClassName("hs_unlocked")[0].setAttribute("visibility", "hidden"); @@ -132,6 +187,7 @@ function () { w.getElementsByClassName("hs_eye")[0].setAttribute("visibility", "hidden"); break; case "guarded": + hscolor = csstate[3]; w.getElementsByClassName("hs_is")[0].setAttribute("fill", csstate[3]); w.getElementsByClassName("hs_smb")[0].setAttribute("visibility", "visible"); w.getElementsByClassName("hs_unlocked")[0].setAttribute("visibility", "hidden");