From 1af4b93426e3c6fc13e091d9b7c9050e1e78d7ab Mon Sep 17 00:00:00 2001 From: rudolfkoenig <> Date: Mon, 19 Mar 2007 14:59:37 +0000 Subject: [PATCH] Major rewrite. See the HISTORY git-svn-id: https://svn.fhem.de/fhem/trunk@30 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 13 +- fhem/FHEM/00_FHZ.pm | 73 +-- fhem/FHEM/10_FS20.pm | 133 ++-- fhem/FHEM/20_FHT.pm | 404 ------------ fhem/FHEM/30_HMS.pm | 262 -------- fhem/FHEM/40_KS300.pm | 308 ---------- fhem/FHEM/50_WS300.pm | 149 ++--- fhem/FHEM/90_FileLog.pm | 97 --- fhem/HISTORY | 34 + fhem/contrib/91_DbLog.pm | 2 - fhem/contrib/99_ALARM.pm | 3 +- fhem/contrib/99_SUNRISE.pm | 2 - fhem/contrib/em1010.pl | 54 +- fhem/contrib/rolwzo_not_off.sh | 4 +- fhem/docs/commandref.html | 954 ++++++++++++++++------------- fhem/docs/faq.html | 2 +- fhem/docs/raw-codes | 3 + fhem/examples/01_fs20 | 12 +- fhem/examples/02_fs20 | 31 +- fhem/examples/03_fht | 14 +- fhem/examples/04_log | 12 +- fhem/examples/05_rm100 | 16 +- fhem/examples/06_at | 26 +- fhem/fhem.pl | 1056 ++++++++++++++------------------ 24 files changed, 1292 insertions(+), 2372 deletions(-) delete mode 100755 fhem/FHEM/20_FHT.pm delete mode 100755 fhem/FHEM/30_HMS.pm delete mode 100755 fhem/FHEM/40_KS300.pm delete mode 100755 fhem/FHEM/90_FileLog.pm diff --git a/fhem/CHANGED b/fhem/CHANGED index 02d8b6a3c..dcee17da4 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -305,5 +305,16 @@ - feature: attribute showtime in web-pgm2 (show time instead of state) - feature: defattr (default attribute for following defines) - feature: added em1010.pl to the contrib directory - - TODO: bugfix: more thorough serial line initialization - doc: added linux.html (multiple devices, udev-links) + - REORGANIZATION: + - at/notify "renamed" to "define at/notify" + - logfile/modpath/pidfile/port/verbose "renamed" to "attr global xxx" + - savefile renamed to "attr global statefile" + - save command added, it writes the configfile and the statefile + - delattr added + - list/xmllist format changed + - disable attribute for at/notify/filelog + See HISTORY for details and reasoning + - TODO: bugfix: more thorough serial line initialization + +- RENAME diff --git a/fhem/FHEM/00_FHZ.pm b/fhem/FHEM/00_FHZ.pm index be0d449f6..615dbb446 100755 --- a/fhem/FHEM/00_FHZ.pm +++ b/fhem/FHEM/00_FHZ.pm @@ -44,7 +44,6 @@ my %codes = ( "^8501..\$" => "fhtbuf", ); -my %readings; my $def; my %msghist; # Used when more than one FHZ is attached my $msgcount = 0; @@ -57,8 +56,6 @@ FHZ_Initialize($) my ($hash) = @_; - $hash->{Category}= "DEV"; - # Provider $hash->{ReadFn} = "FHZ_Read"; $hash->{WriteFn} = "FHZ_Write"; @@ -71,8 +68,8 @@ FHZ_Initialize($) $hash->{GetFn} = "FHZ_Get"; $hash->{SetFn} = "FHZ_Set"; $hash->{StateFn} = "FHZ_SetState"; - $hash->{ListFn} = "FHZ_List"; $hash->{ParseFn} = "FHZ_Parse"; + $hash->{AttrList}= "do_not_notify:1,0 dummy:1,0 filtertimeout repeater:1,0 showtime:1,0 model:fhz1000,fhz1300 loglevel:0,1,2,3,4,5,6"; } ##################################### @@ -81,16 +78,18 @@ FHZ_Set($@) { my ($hash, @a) = @_; - return "Need one to three parameter" if(@a > 4); - return "invalid parameter, use one of:\n " . join("\n ", sort keys %sets) + return "Need one to three parameter" if(@a < 2); + return "Unknown argument $a[1], choose one of " . join(" ", sort keys %sets) if(!defined($sets{$a[1]})); + return "Need one to three parameter" if(@a > 4); return "Wrong number of parameters for $a[1], need " . ($setnrparam{$a[1]}+2) if(@a != ($setnrparam{$a[1]} + 2)); my ($fn, $arg) = split(" ", $sets{$a[1]}); my $v = join(" ", @a); - Log GetLogLevel("FHZ"), "FHZ set $v"; + my $name = $hash->{NAME}; + Log GetLogLevel($name,2), "FHZ set $name $v"; if($a[1] eq "activefor") { @@ -98,7 +97,7 @@ FHZ_Set($@) return "device $a[2] unknown" if(!defined($dhash)); return "Cannot handle $dhash->{TYPE} devices" - if($devmods{FHZ}->{Clients} !~ m/:$dhash->{TYPE}:/); + if($modules{FHZ}->{Clients} !~ m/:$dhash->{TYPE}:/); $dhash->{IODev} = $hash; return undef; @@ -133,15 +132,14 @@ FHZ_Get($@) my ($hash, @a) = @_; return "\"get FHZ\" needs only one parameter" if(@a != 2); - if(!defined($gets{$a[1]})) { - return "Unknown set value $a[1], please specify one of: " . - join(" ", sort(keys %gets)); - } + return "Unknown argument $a[1], choose one of " . join(",", sort keys %gets) + if(!defined($gets{$a[1]})); my ($fn, $arg) = split(" ", $gets{$a[1]}); my $v = join(" ", @a); - Log GetLogLevel("FHZ"), "FHZ get $v"; + my $name = $hash->{NAME}; + Log GetLogLevel($name,2), "FHZ get $name $v"; FHZ_Write($hash, $fn, $arg) if(!IsDummy("FHZ")); @@ -157,26 +155,12 @@ FHZ_Get($@) } else { $v = substr($msg, 12); } - $readings{$a[1]}{VAL} = $v; - $readings{$a[1]}{TIM} = TimeNow(); + $hash->{READINGS}{$a[1]}{VAL} = $v; + $hash->{READINGS}{$a[1]}{TIME} = TimeNow(); return "$a[0] $a[1] => $v"; } -##################################### -sub -FHZ_List($) -{ - my ($hash) = @_; - - my $str = ""; - foreach my $m (sort keys %readings) { - $str .= sprintf("%-19s %-15s %s\n", - $readings{$m}{TIM},$m,$readings{$m}{VAL}); - } - return $str; -} - ##################################### sub FHZ_SetState($$$$) @@ -184,11 +168,6 @@ FHZ_SetState($$$$) my ($hash, $tim, $vt, $val) = @_; return "Undefined value $vt" if(!defined($gets{$vt})); - - if(!$readings{$vt} || $readings{$vt}{TIM} lt $tim) { - $readings{$vt}{TIM} = $tim; - $readings{$vt}{VAL} = $val; - } return undef; } @@ -216,7 +195,8 @@ DoInit($) sub FHZ_Define($$) { - my ($hash, @a) = @_; + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); $hash->{STATE} = "Initialized"; @@ -224,6 +204,8 @@ FHZ_Define($$) delete $hash->{FD}; my $dev = $a[2]; + $attr{$a[0]}{savefirst} = 1; + if($dev eq "none") { Log 1, "FHZ device is none, commands will be echoed only"; return undef; @@ -252,12 +234,14 @@ sub FHZ_Undef($$) { my ($hash, $arg) = @_; + my $name = $hash->{NAME}; + foreach my $d (keys %defs) { if(defined($defs{$d}) && defined($defs{$d}{IODev}) && $defs{$d}{IODev} == $hash) { - Log 4, "deleting port for $d"; + Log GetLogLevel($name,2), "deleting port for $d"; delete $defs{$d}{IODev}; } } @@ -275,6 +259,7 @@ FHZ_Parse($$) $msg = substr($msg, 12); # The first 12 bytes are not really interesting my $type = ""; + my $name = $hash->{NAME}; foreach my $c (keys %codes) { if($msg =~ m/$c/) { $type = $codes{$c}; @@ -283,7 +268,7 @@ FHZ_Parse($$) } if(!$type) { - Log 4, "FHZ unknown: $omsg"; + Log 4, "FHZ $name unknown: $omsg"; $def->{CHANGED}[0] = "$msg"; return $hash->{NAME}; } @@ -293,7 +278,7 @@ FHZ_Parse($$) $msg = substr($msg, 4, 2); } - Log 4, "FHZ $type: $msg)"; + Log 4, "FHZ $name $type: $msg)"; $def->{CHANGED}[0] = "$type: $msg"; return $hash->{NAME}; } @@ -355,7 +340,7 @@ FHZ_ReadAnswer($$) my $len = ord(substr($mfhzdata,1,1)) + 2; if($len>20) { - Log 1, "Oversized message (" . unpack('H*',$mfhzdata) . + Log 4, "Oversized message (" . unpack('H*',$mfhzdata) . "), dropping it ..."; return undef; } @@ -438,7 +423,7 @@ FHZ_Read($) my ($hash) = @_; my $buf = $hash->{PortObj}->input(); - my $iohash = $devmods{$hash->{TYPE}}; + my $iohash = $modules{$hash->{TYPE}}; my $name = $hash->{NAME}; ########### @@ -531,12 +516,12 @@ FHZ_Read($) my @found; - foreach my $m (sort { $devmods{$a}{ORDER} cmp $devmods{$b}{ORDER} } - keys %devmods) { + foreach my $m (sort { $modules{$a}{ORDER} cmp $modules{$b}{ORDER} } + keys %modules) { next if($iohash->{Clients} !~ m/:$m:/); - next if($dmsg !~ m/$devmods{$m}{Match}/i); + next if($dmsg !~ m/$modules{$m}{Match}/i); no strict "refs"; - @found = &{$devmods{$m}{ParseFn}}($hash,$dmsg); + @found = &{$modules{$m}{ParseFn}}($hash,$dmsg); use strict "refs"; last if(int(@found)); } diff --git a/fhem/FHEM/10_FS20.pm b/fhem/FHEM/10_FS20.pm index c84e40caa..0e7540b3d 100755 --- a/fhem/FHEM/10_FS20.pm +++ b/fhem/FHEM/10_FS20.pm @@ -45,8 +45,44 @@ my %readonly = ( use vars qw(%fs20_c2b); # Peter would like to access it from outside my %defptr; -my %readings; my %follow; +my $fs20_simple ="off off-for-timer on on-for-timer on-till reset timer toggle"; +my %models = ( + fs20hgs => 'sender', + fs20hgs => 'sender', + fs20pira => 'sender', + fs20piri => 'sender', + fs20s20 => 'sender', + fs20s4 => 'sender', + fs20s4a => 'sender', + fs20s4m => 'sender', + fs20s4u => 'sender', + fs20s4ub => 'sender', + fs20sd => 'sender', + fs20sn => 'sender', + fs20sr => 'sender', + fs20ss => 'sender', + fs20str => 'sender', + fs20tfk => 'sender', + fs20tfk => 'sender', + fs20tk => 'sender', + fs20uts => 'sender', + fs20ze => 'sender', + + fs20as1 => 'simple', + fs20as4 => 'simple', + fs20di => 'dimmer', + fs20du => 'dimmer', + fs20ms2 => 'simple', + fs20rst => 'simple', + fs20sa => 'simple', + fs20sig => 'simple', + fs20st => 'simple', + fs20sv => 'simple', + fs20sv => 'simple', + fs20usr => 'simple', +); + sub FS20_Initialize($) @@ -58,38 +94,14 @@ FS20_Initialize($) } $fs20_c2b{"on-till"} = 99; - $hash->{Category} = "DEV"; - $hash->{Match} = "^81..(04|0c)..0101a001"; $hash->{SetFn} = "FS20_Set"; - $hash->{GetFn} = "FS20_Get"; - $hash->{ListFn} = "FS20_List"; $hash->{StateFn} = "FS20_SetState"; $hash->{DefFn} = "FS20_Define"; $hash->{UndefFn} = "FS20_Undef"; $hash->{ParseFn} = "FS20_Parse"; -} + $hash->{AttrList} = "follow-on-for-timer:1,0 do_not_notify:1,0 dummy:1,0 showtime:1,0 model;fs20hgs,fs20hgs,fs20pira,fs20piri,fs20s20,fs20s4,fs20s4a,fs20s4m,fs20s4u,fs20s4ub,fs20sd,fs20sn,fs20sr,fs20ss,fs20str,fs20tfk,fs20tfk,fs20tk,fs20uts,fs20ze,fs20as1,fs20as4,fs20di,fs20du,fs20ms2,fs20rst,fs20sa,fs20sig,fs20st,fs20sv,fs20sv,fs20usr loglevel:0,1,2,3,4,5,6"; -################################### -sub -FS20_Get($@) -{ - my ($hash, @a) = @_; - return "No get function implemented"; -} - -################################### -sub -FS20_List($) -{ - my ($hash) = @_; - - my $n = $hash->{NAME}; - if(!defined($readings{$n})) { - return "No information about $n\n"; - } else { - return sprintf("%-19s %s\n", $readings{$n}{TIM}, $readings{$n}{VAL}); - } } ##################################### @@ -98,13 +110,8 @@ FS20_SetState($$$$) { my ($hash, $tim, $vt, $val) = @_; - return "Undefined value $vt" if(!defined($fs20_c2b{$vt})); - - my $name = $hash->{NAME}; - if(!$readings{$name} || $readings{$name}{TIM} lt $tim) { - $readings{$name}{TIM} = $tim; - $readings{$name}{VAL} = $vt; - } + $val = $1 if($val =~ m/^\(.*\) \d+$/); + return "Undefined value $val" if(!defined($fs20_c2b{$val})); return undef; } @@ -128,7 +135,8 @@ Do_On_Till($@) my @b = ($a[0], "on"); FS20_Set($hash, @b); - CommandAt(undef, "$hms_till set $a[0] off"); + CommandDefine(undef, $hash->{NAME} . "_till at $hms_till set $a[0] off"); + } @@ -145,8 +153,18 @@ FS20_Set($@) my $c = $fs20_c2b{$a[1]}; if(!defined($c)) { - return "Unknown set value $a[1], please specify one of:\n " . - join("\n ", sort(keys %fs20_c2b)); + + # Model specific set arguments + if(defined($attr{$a[0]}) && defined($attr{$a[0]}{"model"})) { + my $mt = $models{$attr{$a[0]}{"model"}}; + return "Unknown argument $a[1], choose one of " + if($mt && $mt eq "sender"); + return "Unknown argument $a[1], choose one of $fs20_simple" + if($mt && $mt eq "simple"); + } + return "Unknown argument $a[1], choose one of " . + join(" ", sort keys %fs20_c2b); + } return Do_On_Till($hash, @a) if($a[1] eq "on-till"); @@ -154,7 +172,7 @@ FS20_Set($@) return "Bad time spec" if($na == 3 && $a[2] !~ m/^\d*\.?\d+$/); my $v = join(" ", @a); - Log GetLogLevel($a[0]), "FS20 set $v"; + Log GetLogLevel($a[0],2), "FS20 set $v"; (undef, $v) = split(" ", $v, 2); # Not interested in the name... my $val; @@ -175,7 +193,7 @@ FS20_Set($@) if($val >= $a[2]) { if($val != $a[2]) { $ret = "FS20 Setting timeout to $val from $a[2]"; - Log GetLogLevel($a[0]), $ret; + Log GetLogLevel($a[0],2), $ret; } $c .= sprintf("%x%x", $i, $j); last LOOP; @@ -200,7 +218,7 @@ FS20_Set($@) my $to = sprintf("%02d:%02d:%02d", $val/3600, ($val%3600)/60, $val%60); $follow{$a[0]} = $to; Log 4, "Follow: +$to setstate $a[0] off"; - CommandAt(undef, "+$to setstate $a[0] off"); + CommandDefine(undef, $a[0] . "_timer at +$to setstate $a[0] off"); } ########################## @@ -208,23 +226,25 @@ FS20_Set($@) my $code = "$hash->{XMIT} $hash->{BTN}"; my $tn = TimeNow(); foreach my $n (keys %{ $defptr{$code} }) { - $defptr{$code}{$n}->{CHANGED}[0] = $v; - $defptr{$code}{$n}->{STATE} = $v; - $readings{$n}{TIM} = $tn; - $readings{$n}{VAL} = $v; - } - + my $lh = $defptr{$code}{$n}; + $lh->{CHANGED}[0] = $v; + $lh->{STATE} = $v; + $lh->{READINGS}{state}{TIME} = $tn; + $lh->{READINGS}{state}{VAL} = $v; + } return $ret; } ############################# sub -FS20_Define($@) +FS20_Define($$) { - my ($hash, @a) = @_; - my $u = - "wrong syntax: define FS20 housecode addr [fg addr] [lm addr] [gm FF]"; + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + + my $u = "wrong syntax: define FS20 housecode " . + "addr [fg addr] [lm addr] [gm FF]"; return $u if(int(@a) < 4); return "Define $a[0]: wrong housecode format: specify a 4 digit hex value" @@ -287,7 +307,6 @@ FS20_Parse($) my $btn = substr($msg, 20, 2); my $cde = substr($msg, 24, 2); - my $def = $defptr{"$dev $btn"}; my $dur = 0; my $cx = hex($cde); @@ -302,15 +321,17 @@ FS20_Parse($) my $v = $codes{$cde}; $v = "unknown:$cde" if(!defined($v)); $v .= " $dur" if($dur); - if($def) { + my $def = $defptr{"$dev $btn"}; + if($def) { my @list; foreach my $n (keys %{ $def }) { - $readings{$n}{TIM} = TimeNow(); - $readings{$n}{VAL} = $v; - $def->{$n}->{CHANGED}[0] = $v; - $def->{$n}->{STATE} = $v; - Log GetLogLevel($n), "FS20 $n $v"; + my $lh = $def->{$n}; + $lh->{CHANGED}[0] = $v; + $lh->{STATE} = $v; + $lh->{READINGS}{state}{TIME} = TimeNow(); + $lh->{READINGS}{state}{VAL} = $v; + Log GetLogLevel($n,2), "FS20 $n $v"; push(@list, $n); } return @list; diff --git a/fhem/FHEM/20_FHT.pm b/fhem/FHEM/20_FHT.pm deleted file mode 100755 index 5e0146b56..000000000 --- a/fhem/FHEM/20_FHT.pm +++ /dev/null @@ -1,404 +0,0 @@ -############################################## -package main; - -use strict; -use warnings; - -my %codes = ( - "0000.6" => "actuator", - "00002c" => "synctime", # Not verified - "0100.6" => "actuator1", # Not verified (1-8) - "0200.6" => "actuator2", - "0300.6" => "actuator3", - "0400.6" => "actuator4", - "0500.6" => "actuator5", - "0600.6" => "actuator6", - "0700.6" => "actuator7", - "0800.6" => "actuator8", - "140069" => "mon-from1", - "150069" => "mon-to1", - "160069" => "mon-from2", - "170069" => "mon-to2", - "180069" => "tue-from1", - "190069" => "tue-to1", - "1a0069" => "tue-from2", - "1b0069" => "tue-to2", - "1c0069" => "wed-from1", - "1d0069" => "wed-to1", - "1e0069" => "wed-from2", - "1f0069" => "wed-to2", - "200069" => "thu-from1", - "210069" => "thu-to1", - "220069" => "thu-from2", - "230069" => "thu-to2", - "240069" => "fri-from1", - "250069" => "fri-to1", - "260069" => "fri-from2", - "270069" => "fri-to2", - "280069" => "sat-from1", - "290069" => "sat-to1", - "2a0069" => "sat-from2", - "2b0069" => "sat-to2", - "2c0069" => "sun-from1", - "2d0069" => "sun-to1", - "2e0069" => "sun-from2", - "2f0069" => "sun-to2", - "3e0069" => "mode", - "3f0069" => "holiday1", # Not verified - "400069" => "holiday2", # Not verified - "410069" => "desired-temp", - "XX0069" => "measured-temp", # sum of next. two, never "really" sent - "420069" => "measured-low", - "430069" => "measured-high", - "440069" => "state", - "600069" => "year", - "610069" => "month", - "620069" => "day", - "630069" => "hour", - "640069" => "minute", - "650069" => "init", - "820069" => "day-temp", - "840069" => "night-temp", - "850069" => "unknown_85", - "8a0069" => "windowopen-temp", - - "0000aa" => "code_0000aa", - "0000ba" => "code_0000ba", - "430079" => "code_430079", - "440079" => "code_440079", - "4b0067" => "code_4b004b", - "4b0077" => "code_4b0077", - "7e0067" => "code_7e0067", -); - -my %cantset = ( - "actuator" => 1, - "actuator1" => 1, - "actuator2" => 1, - "actuator3" => 1, - "actuator4" => 1, - "actuator5" => 1, - "actuator6" => 1, - "actuator7" => 1, - "actuator8" => 1, - "synctime" => 1, - "measured-temp" => 1, - "measured-high" => 1, - "measured-low" => 1, - "state" => 1, - "init" => 1, - - "code_0000aa" => 1, - "code_0000ba" => 1, - "code_430079" => 1, - "code_440079" => 1, - "code_4b004b" => 1, - "code_4b0077" => 1, - "code_7e0067" => 1, -); - -my %nosetarg = ( - "help" => 1, - "refreshvalues" => 1, -); - -my %c2m = (0 => "auto", 1 => "manual", 2 => "holiday"); -my %m2c = ("auto" => 0, "manual" => 1, "holiday" => 2); - -my %readings; -my %defptr; -my %c2b; # command->button hash (reverse of codes) -my %c2bset; # Setteable values - - -##################################### -sub -FHT_Initialize($) -{ - my ($hash) = @_; - - foreach my $k (keys %codes) { - my $v = $codes{$k}; - $c2b{$v} = $k; - $c2bset{$v} = substr($k, 0, 2) if(!defined($cantset{$v})); - } - $c2bset{refreshvalues} = "65ff66ff"; - - $hash->{Category} = "DEV"; - -# 810c0426 0909a001 1111 1600 -# 810c04b3 0909a001 1111 44006900 -# 810b0402 83098301 1111 41301d -# 81090421 c409c401 1111 00 - -# 810c0d20 0909a001 3232 7e006724 (NYI) - - $hash->{Match} = "^81..(04|09|0d)..(0909a001|83098301|c409c401).."; - $hash->{SetFn} = "FHT_Set"; - $hash->{GetFn} = "FHT_Get"; - $hash->{StateFn} = "FHT_SetState"; - $hash->{ListFn} = "FHT_List"; - $hash->{DefFn} = "FHT_Define"; - $hash->{UndefFn} = "FHT_Undef"; - $hash->{ParseFn} = "FHT_Parse"; -} - - -##################################### -sub -FHT_Set($@) -{ - my ($hash, @a) = @_; - my $ret = undef; - - return "\"set $a[0]\" needs two parameters" - if(@a != 3 && !(@a == 2 && $nosetarg{$a[1]})); - return "invalid parameter, use one of:\n " . - join("\n ", sort {$c2bset{$a} cmp $c2bset{$b} } keys %c2bset) - if(!defined($c2bset{$a[1]})); - - - Log GetLogLevel($a[0]), "FHT set " . join(" ", @a); - - my $arg = "020183" . $hash->{CODE} . $c2bset{$a[1]}; - - if($a[1] eq "refreshvalues") { - - # This is special. Without the sleep the next FHT won't send its data - if(!IsDummy($a[0])) { - my $havefhz; - $havefhz = 1 if($hash->{IODev} && defined($hash->{IODev}->{FD})); - - IOWrite($hash, "04", $arg); - sleep(1) if($havefhz); - IOWrite($hash, "04", "c90185"); # Check the fht buffer - sleep(1) if($havefhz); - } - return $ret; - - } elsif($a[1] =~ m/-temp/) { - return "Invalid temperature, use NN.N" if($a[2] !~ m/^\d*\.?\d+$/); - my $a = int($a[2]*2); - $arg .= sprintf("%02x", $a); - $ret = "Rounded temperature to " . $a/2 if($a/2 != $a[2]); - - } elsif($a[1] =~ m/-from/ || $a[1] =~ m/-to/) { - return "Invalid timeformat, use HH:MM" if($a[2] !~ m/^([0-2]\d):([0-5]\d)/); - my $a = ($1*6) + ($2/10); - $arg .= sprintf("%02x", $a); - - my $nt = sprintf("%02d:%02d", $1, ($2/10)*10); - $ret = "Rounded time to $nt" if($nt ne $a[2]); - - } elsif($a[1] eq "mode") { - return "Invalid mode, use one of " . join(" ", sort keys %m2c) - if(!defined($m2c{$a[2]})); - $arg .= sprintf("%02x", $m2c{$a[2]}); - - } else { # Holiday1, Holiday2 - $arg .= sprintf("%02x", $a[2]); - - } - - IOWrite($hash, "04", $arg) if(!IsDummy($a[0])); - return $ret; -} - -##################################### -sub -FHT_Get($@) -{ - my ($hash,@a) = @_; - - return "NYI"; -} - -##################################### -sub -FHT_List($) -{ - my ($hash) = @_; - - my $n = $hash->{CODE}; - if(!defined($readings{$n})) { - return "No information about " . $hash->{NAME} . "\n"; - } else { - my $str = ""; - foreach my $m (sort { $c2b{$a} cmp $c2b{$b} } keys %{ $readings{$n} }) { - $str .= sprintf("%-19s %-15s %s\n", - $readings{$n}{$m}{TIM}, $m, $readings{$n}{$m}{VAL}); - } - return $str; - } -} - -##################################### -sub -FHT_SetState($$$$) -{ - my ($hash, $tim, $vt, $val) = @_; - - return "Undefined type $vt" if(!defined($c2b{$vt})); - - my $n = $hash->{CODE}; - - if(!$readings{$n}{$vt} || $readings{$n}{$vt}{TIM} lt $tim) { - $readings{$n}{$vt}{TIM} = $tim; - $readings{$n}{$vt}{VAL} = $val; - } - return undef; -} - - -##################################### -sub -FHT_Define($@) -{ - my ($hash, @a) = @_; - - return "wrong syntax: define FHT CODE" if(int(@a) != 3); - $a[2] = lc($a[2]); - return "Define $a[0]: wrong CODE format: specify a 4 digit hex value" - if($a[2] !~ m/^[a-f0-9][a-f0-9][a-f0-9][a-f0-9]$/i); - - - $hash->{CODE} = $a[2]; - $defptr{$a[2]} = $hash; - - AssignIoPort($hash); - - Log 1, "Asking the FHT device $a[0]/$a[2] to send its data"; - FHT_Set($hash, ($a[0], "refreshvalues")); - - return undef; -} - -##################################### -sub -FHT_Undef($$) -{ - my ($hash, $name) = @_; - delete($defptr{$hash->{CODE}}); - return undef; -} - -##################################### -sub -FHT_Parse($$) -{ - my ($hash,$msg) = @_; - - my $dev = substr($msg, 16, 4); - my $cde = substr($msg, 20, 6); - my $val = substr($msg, 26, 2) if(length($msg) > 26); - - if(!defined($defptr{$dev})) { - Log 3, "FHT Unknown device $dev, please define it"; - return "UNDEFINED FHT $dev"; - } - - - my $def = $defptr{$dev}; - - # Unknown, but don't want report it. Should come with c409c401 - if($cde eq "00") { - return ""; - } - - if(length($cde) < 6) { - Log 4, "FHT Unknown code from $def->{NAME} : $cde"; - $def->{CHANGED}[0] = "unknown code $cde"; - return $def->{NAME}; - } - - - if(!$val) { - # This is a confirmation message. We reformat it so that - # it looks like a real message, and let the rest parse it - Log 4, "FHT $def->{NAME} confirmation: $cde)"; - $val = substr($cde, 2, 2); - $cde = substr($cde, 0, 2) . "0069"; - } - - my $type; - foreach my $c (keys %codes) { - if($cde =~ m/$c/) { - $type = $codes{$c}; - last; - } - } - - $val = hex($val); - - if(!$type) { - Log 4, "FHT $def->{NAME} (Unknown: $cde => $val)"; - $def->{CHANGED}[0] = "unknown $cde: $val"; - return $def->{NAME}; - } - - - my $tn = TimeNow(); - - ########################### - # Reformat the values so they are readable - if($type eq "actuator") { - $val = sprintf("%02d%%", int(100*$val/255 + 0.5)); - - } elsif($cde ge "140069" && $cde le "2f0069") { # Time specs - Log 5, "FHT $def->{NAME} ($type: $val)"; - return "" if($val == 144); # Empty, forget it - my $hour = $val / 6; - my $min = ($val % 6) * 10; - $val = sprintf("%02d:%02d", $hour, $min); - - } elsif($type eq "mode") { - $val = $c2m{$val} if(defined($c2m{$val})); - - } elsif($type eq "measured-low") { - - $readings{$dev}{$type}{TIM} = $tn; - $readings{$dev}{$type}{VAL} = $val; - return ""; - - } elsif($type eq "measured-high") { - - $readings{$dev}{$type}{TIM} = $tn; - $readings{$dev}{$type}{VAL} = $val; - - if(defined($readings{$dev}{"measured-low"}{VAL})) { - - $val = $val*256 + $readings{$dev}{"measured-low"}{VAL}; - $val /= 10; - $val = sprintf("%.1f (Celsius)", $val); - $type = "measured-temp" - - } else { - return ""; - } - - } elsif($type =~ m/.*-temp/) { - $val = sprintf("%.1f (Celsius)", $val / 2) - - } elsif($type eq "state") { - - my $nval; - $nval = "Bat: " . (($val & 1) ? "empty" : "ok"); - $nval .= ", Window: " . (($val & 32) ? "open" : "closed"); - $nval .= ", Fault: " . (($val & 16) ? "yes" : "no"); - $val = $nval; - - } elsif($type =~ m/echo_/) { # Ignore these messages - return ""; - - } - - $readings{$dev}{$type}{TIM} = $tn; - $readings{$dev}{$type}{VAL} = $val; - - Log 4, "FHT $def->{NAME} ($type: $val)"; - $def->{CHANGED}[0] = "$type: $val"; - $def->{STATE} = "$type: $val" if($type eq "measured-temp"); - return $def->{NAME}; -} - -1; diff --git a/fhem/FHEM/30_HMS.pm b/fhem/FHEM/30_HMS.pm deleted file mode 100755 index 63c5b4a1d..000000000 --- a/fhem/FHEM/30_HMS.pm +++ /dev/null @@ -1,262 +0,0 @@ -############################################## -package main; - -use strict; -use warnings; - -my %codes = ( - "0" => "HMS100TF", - "1" => "HMS100T", - "2" => "HMS100WD", - "3" => "RM100-2", - "4" => "HMS100TFK", # Depending on the onboard jumper it is 4 or 5 - "5" => "HMS100TFK", - "6" => "HMS100MG", -); - -my %readings; -my %defptr; - - -##################################### -sub -HMS_Initialize($) -{ - my ($hash) = @_; - - $hash->{Category} = "DEV"; - -# 810e047e0510a001473a000000120233 HMS100TF -# 810e04b90511a0018e63000001100000 HMS100T -# 810e04e80212a001ec46000001000000 HMS100WD -# 810e04d70213a001b16d000003000000 RM100-2 -# 810e047f0214a001a81f000001000000 HMS100TFK -# 810e048f0295a0010155000001000000 HMS100TFK (jumper) -# 810e04330216a001b4c5000001000000 HMS100MG - - $hash->{Match} = "^810e04....(1|5|9)[0-6]a001"; - $hash->{SetFn} = "HMS_Set"; - $hash->{GetFn} = "HMS_Get"; - $hash->{StateFn} = "HMS_SetState"; - $hash->{ListFn} = "HMS_List"; - $hash->{DefFn} = "HMS_Define"; - $hash->{UndefFn} = "HMS_Undef"; - $hash->{ParseFn} = "HMS_Parse"; -} - -################################### -sub -HMS_Set($@) -{ - my ($hash, @a) = @_; - return "No set function implemented"; -} - -################################### -sub -HMS_Get($@) -{ - my ($hash,@a) = @_; - return "No get function implemented"; -} - -##################################### -sub -HMS_SetState($$$$) -{ - my ($hash, $tim, $vt, $val) = @_; - - my $n = $hash->{CODE}; - if(!$readings{$n}{$vt} || $readings{$n}{$vt}{TIM} lt $tim) { - $readings{$n}{$vt}{TIM} = $tim; - $readings{$n}{$vt}{VAL} = $val; - } - return undef; -} - -##################################### -sub -HMS_List($) -{ - my ($hash) = @_; - - my $n = $hash->{CODE}; - if(!defined($readings{$n})) { - return "No information about " . $hash->{NAME} . "\n"; - } else { - my $str = ""; - foreach my $m (keys %{ $readings{$n} }) { - $str .= sprintf("%-19s %-15s %s\n", - $readings{$n}{$m}{TIM}, $m, $readings{$n}{$m}{VAL}); - } - return $str; - } -} - -##################################### -sub -HMS_Define($@) -{ - my ($hash, @a) = @_; - - return "wrong syntax: define HMS CODE" if(int(@a) != 3); - $a[2] = lc($a[2]); - return "Define $a[0]: wrong CODE format: specify a 4 digit hex value" - if($a[2] !~ m/^[a-f0-9][a-f0-9][a-f0-9][a-f0-9]$/); - - - $hash->{CODE} = $a[2]; - $defptr{$a[2]} = $hash; - - return undef; -} - -##################################### -sub -HMS_Undef($$) -{ - my ($hash, $name) = @_; - delete($defptr{$hash->{CODE}}); - return undef; -} - - -##################################### -sub -HMS_Parse($$) -{ - my ($hash, $msg) = @_; - - my $dev = substr($msg, 16, 4); - my $cde = substr($msg, 11, 1); - my $val = substr($msg, 24, 8) if(length($msg) == 32); - - my $type = ""; - foreach my $c (keys %codes) { - if($cde =~ m/$c/) { - $type = $codes{$c}; - last; - } - } - - # As the HMS devices change their id on each battery change, we offer - # a wildcard too for each type: 100, - my $odev = $dev; - if(!defined($defptr{$dev})) { - Log 4, "HMS device $dev not defined, using the wildcard device 100$cde"; - $dev = "100$cde"; - } - - if(!defined($defptr{$dev})) { - Log 3, "Unknown HMS device $dev/$odev, please define it"; - $type = "HMS" if(!$type); - return "UNDEFINED $type $odev"; - } - - my $def = $defptr{$dev}; - - - my (@v, @txt, @sfx); - - if($type eq "HMS100TF") { - - @txt = ( "temperature", "humidity", "battery"); - @sfx = ( "(Celsius)", "(%)", ""); - - # Codierung - my $status = hex(substr($val, 0, 1)); - $v[0] = int(substr($val, 5, 1) . substr($val, 2, 2))/10; - $v[1] = int(substr($val, 6, 2) . substr($val, 4, 1))/10; - $v[2] = "ok"; - if ( $status & 2 ) { $v[2] = "empty"; } - if ( $status & 4 ) { $v[2] = "replaced"; } - if ( $status & 8 ) { $v[0] = -$v[0]; } - - $val = "T: $v[0] H: $v[1] Bat: $v[2]"; - - } elsif ($type eq "HMS100T") { - - @txt = ( "temperature", "battery"); - @sfx = ( "(Celsius)", ""); - - my $status = hex(substr($val, 0, 1)); - $v[0] = int(substr($val, 5, 1) . substr($val, 2, 2))/10; - $v[1] = "ok"; - if ( $status & 2 ) { $v[1] = "empty"; } - if ( $status & 4 ) { $v[1] = "replaced"; } - if ( $status & 8 ) { $v[0] = -$v[0]; } - - $val = "T: $v[0] Bat: $v[1]"; - - } elsif ($type eq "HMS100WD") { - - @txt = ( "water_detect", "battery"); - @sfx = ( "", ""); - - # Battery-low condition detect is not yet properly - # implemented. As soon as my WD's batteries get low - # I am willing to supply a patch ;-) SEP7-RIPE, 2006/05/13 - my $status = hex(substr($val, 1, 1)); - $v[1] = "ok"; - $v[0] = "off"; - if ( $status & 1 ) { $v[0] = "on"; } - $val = "Water Detect: $v[0]"; - - } elsif ($type eq "HMS100TFK") { # By Peter P. - - @txt = ( "switch_detect", "battery"); - @sfx = ( "", ""); - # Battery-low condition detect is not yet properly implemented. - my $status = hex(substr($val, 1, 1)); - $v[0] = ($status ? "on" : "off"); - $v[1] = "off"; - $val = "Switch Detect: $v[0]"; - - } elsif($type eq "RM100-2") { - - @txt = ( "smoke_detect", "battery"); - @sfx = ( "", ""); - - $v[0] = ( hex(substr($val, 1, 1)) != "0" ) ? "on" : "off"; - $v[1] = "unknown"; # Battery-low detect is _NOT_ implemented. - $val = "smoke_detect: $v[0]"; - - } elsif ($type eq "HMS100MG") { # By Peter Stark - - @txt = ( "gas_detect", "battery"); - @sfx = ( "", ""); - - # Battery-low condition detect is not yet properly - # implemented. - my $status = hex(substr($val, 1, 1)); - $v[0] = ($status != "0") ? "on" : "off"; - $v[1] = "off"; - if ($status & 1) { $v[0] = "on"; } - $val = "Gas Detect: $v[0]"; - - } else { - - Log 4, "HMS Device $dev (Unknown type: $type)"; - return ""; - - } - - my $now = TimeNow(); - Log 4, "HMS Device $dev ($type: $val)"; - - my $max = int(@txt); - for( my $i = 0; $i < $max; $i++) { - $readings{$dev}{$txt[$i]}{TIM} = $now; - my $v = "$v[$i] $sfx[$i]"; - $readings{$dev}{$txt[$i]}{VAL} = $v; - $def->{CHANGED}[$i] = "$txt[$i]: $v"; - } - $readings{$dev}{type}{TIM} = $now; - $readings{$dev}{type}{VAL} = $type; - - $def->{STATE} = $val; - $def->{CHANGED}[$max] = $val; - return $def->{NAME}; -} - -1; diff --git a/fhem/FHEM/40_KS300.pm b/fhem/FHEM/40_KS300.pm deleted file mode 100755 index aaeb12bf3..000000000 --- a/fhem/FHEM/40_KS300.pm +++ /dev/null @@ -1,308 +0,0 @@ -############################################## -package main; - -use strict; -use warnings; - -my %readings; -my %defptr; -my $negcount = 0; - -###################### -# Note: this is just an empty hull. - -##################################### -sub -KS300_Initialize($) -{ - my ($hash) = @_; - - # Message is like - # 810d04f94027a00171212730000008 - # 81 0d 04 f9 4027a00171 212730000008 - - $hash->{Category} = "DEV"; - - $hash->{Match} = "^810.04..402.a001"; - $hash->{SetFn} = "KS300_Set"; - $hash->{GetFn} = "KS300_Get"; - $hash->{StateFn} = "KS300_SetState"; - $hash->{ListFn} = "KS300_List"; - $hash->{DefFn} = "KS300_Define"; - $hash->{UndefFn} = "KS300_Undef"; - $hash->{ParseFn} = "KS300_Parse"; -} - -################################### -sub -KS300_Set($@) -{ - my ($hash, @a) = @_; - return "No set function implemented"; -} - -################################### -sub -KS300_Get($@) -{ - my ($hash,@a) = @_; - return "No get function implemented"; -} - -##################################### -sub -KS300_SetState($$$$) -{ - my ($hash, $tim, $vt, $val) = @_; - - my $n = $hash->{CODE}; - if(!$readings{$n}{$vt} || $readings{$n}{$vt}{TIM} lt $tim) { - $readings{$n}{$vt}{TIM} = $tim; - $readings{$n}{$vt}{VAL} = $val; - } - return undef; -} - -##################################### -sub -KS300_List($) -{ - my ($hash) = @_; - my $str = ""; - - my $n = $hash->{CODE}; - if(!defined($readings{$n})) { - $str .= "No information about " . $hash->{NAME} . "\n"; - } else { - foreach my $m (keys %{ $readings{$n} }) { - $str .= sprintf("%-19s %-15s %s\n", - $readings{$n}{$m}{TIM}, $m, $readings{$n}{$m}{VAL}); - } - } - return $str; - -} - -##################################### -sub -KS300_Define($@) -{ - my ($hash, @a) = @_; - - return "wrong syntax: define KS300 " . - "[ml/raincounter] [wind-factor]" if(int(@a) < 3 || int(@a) > 5); - $a[2] = lc($a[2]); - return "Define $a[0]: wrong CODE format: specify a 4 digit hex value" - if($a[2] !~ m/^[a-f0-9][a-f0-9][a-f0-9][a-f0-9]$/); - - $hash->{CODE} = $a[2]; - my $rainunit = ((int(@a) > 3) ? $a[3] : 255); - my $windunit = ((int(@a) > 4) ? $a[4] : 1.0); - $hash->{CODE} = $a[2]; - $hash->{RAINUNIT} = $rainunit; - $hash->{WINDUNIT} = $windunit; - $defptr{$a[2]} = $hash; - - return undef; -} - -##################################### -sub -KS300_Undef($$) -{ - my ($hash, $name) = @_; - delete($defptr{$hash->{CODE}}); - return undef; -} - - -##################################### -sub -KS300_Parse($) -{ - my ($hash,$msg) = @_; - - if($msg !~ m/^810d04..4027a001/) { - Log 4, "KS300 unknown message $msg"; - return ""; - } - - ############################### - # 1 2 - #0123456789012345 67890123456789 - # - #810d04f94027a001 71212730000008 - ############################### - my @a = split("", $msg); - - ########################## - # I've seldom (1 out of 700) seen messages of length 10 and 11 with correct - # CRC, they seem to contain partial data (e.g. temp/wind/hum but not rain) - # They are suppressed as of now. - if(hex($a[3]) != 13) { - Log 4, "Strange KS400 message received, wont decode ($msg)"; - return ""; - } - - if(int(keys %defptr)) { - - my @arr = keys(%defptr); # No code is known yet - my $dev = shift(@arr); - my $def = $defptr{$dev}; - my $haverain = 0; - - my @v; - my @txt = ( "rain_raw", "rain", "wind", "humidity", "temperature", - "israining", "unknown1", "unknown2", "unknown3"); - my @sfx = ( "(counter)", "(l/m2)", "(km/h)", "(%)", "(Celsius)", - "(yes/no)", "","",""); - - # The next instr wont work for empty hashes, so we init it now - $readings{$dev}{$txt[0]}{VAL} = 0 if(!$readings{$dev}); - my $r = $readings{$dev}; - - $v[0] = hex("$a[28]$a[27]$a[26]"); - - ############################# - # My KS300 sends a (quite huge) "negative" rain, when the rain begins, - # then the value is "normal" again. So we have to filter neg. rain out. - # But if the KS300 is sending this value more than once, then accept it, - # as the KS300 was probably reset - - if($r->{rain_raw}{VAL}) { - my ($rrv, undef) = split(" ", $r->{rain_raw}{VAL}); - $haverain = 1 if($v[0] != $rrv); - if($v[0] < $rrv) { - if($negcount++ < 3) { - Log 3, "KS300 negative rain, ignoring it"; - $v[0] = $rrv; - } else { - Log 1, "KS300 was probably reset, accepting new rain value"; - } - } else { - $negcount = 0; - } - } - - $v[1] = sprintf("%0.1f", $v[0] * $def->{RAINUNIT} / 1000); - $v[2] = sprintf("%0.1f", ("$a[25]$a[24].$a[23]"+0) * $def->{WINDUNIT}); - $v[3] = "$a[22]$a[21]" + 0; - $v[4] = "$a[20]$a[19].$a[18]" + 0; $v[4] = "-$v[4]" if($a[17] eq "7"); - $v[4] = sprintf("%0.1f", $v[4]); - - $v[5] = ((hex($a[17]) & 0x2) || $haverain) ? "yes" : "no"; - $v[6] = $a[29]; - $v[7] = $a[16]; - $v[8] = $a[17]; - - # Negative temp - $v[4] = -$v[4] if($v[8] & 8); - - my $tm = TimeNow(); - - Log 4, "KS300 $dev: $msg"; - - my $max = int(@v); - - - for(my $i = 0; $i < $max; $i++) { - $r->{$txt[$i]}{TIM} = $tm; - my $val = "$v[$i] $sfx[$i]"; - $r->{$txt[$i]}{VAL} = $val; - $def->{CHANGED}[$i] = "$txt[$i]: $val"; - } - - # For logging/summary - my $val = "T: $v[4] H: $v[3] W: $v[2] R: $v[1] IR: $v[5]"; - $def->{STATE} = $val; - $def->{CHANGED}[$max++] = $val; - - ################################### - # AVG computing - if(!$r->{cum_day}) { - - $r->{cum_day}{VAL} = "$tm T: 0 H: 0 W: 0 R: $v[1]"; - $r->{avg_day}{VAL} = "T: $v[4] H: $v[3] W: $v[2] R: $v[1]"; - - } else { - - my @cv = split(" ", $r->{cum_day}{VAL}); - - my @cd = split("[ :-]", $r->{cum_day}{TIM}); - my $csec = 3600*$cd[3] + 60*$cd[4] + $cd[5]; # Sec of last reading - - my @d = split("[ :-]", $tm); - my $sec = 3600*$d[3] + 60*$d[4] + $d[5]; # Sec now - - my @sd = split("[ :-]", "$cv[0] $cv[1]"); - my $ssec = 3600*$sd[3] + 60*$sd[4] + $sd[5]; # Sec at start of day - - my $difft = $sec - $csec; - $difft += 86400 if($d[2] != $cd[2]); # Sec since last reading - - my $t = $cv[3] + $difft * $v[4]; - my $h = $cv[5] + $difft * $v[3]; - my $w = $cv[7] + $difft * $v[2]; - my $e = $cv[9]; - - $r->{cum_day}{VAL} = "$cv[0] $cv[1] T: $t H: $h W: $w R: $e"; - - $difft = $sec - $ssec; - $difft += 86400 if($d[2] != $sd[2]); # Sec since last reading - - $t /= $difft; $h /= $difft; $w /= $difft; $e = $v[1] - $cv[9]; - $r->{avg_day}{VAL} = - sprintf("T: %.1f H: %d W: %.1f R: %.1f", $t, $h, $w, $e); - - if($d[2] != $sd[2]) { # Day changed, report it - - $def->{CHANGED}[$max++] = "avg_day $r->{avg_day}{VAL}"; - $r->{cum_day}{VAL} = "$tm T: 0 H: 0 W: 0 R: $v[1]"; - - if(!$r->{cum_month}) { # Check the month - - $r->{cum_month}{VAL} = "1 $r->{avg_day}{VAL}"; - $r->{avg_month}{VAL} = $r->{avg_day}{VAL}; - - } else { - - my @cmv = split(" ", $r->{cum_month}{VAL}); - $t += $cmv[2]; $w += $cmv[4]; $h += $cmv[6]; - - $cmv[0]++; - $r->{cum_month}{VAL} = - sprintf("%d T: %.1f H: %d W: %.1f R: %.1f", - $cmv[0], $t, $h, $w, $cmv[8]+$e); - $r->{avg_month}{VAL} = - sprintf("T: %.1f H: %d W: %.1f R: %.1f", - $t/$cmv[0], $h/$cmv[0], $w/$cmv[0], $cmv[8]+$e); - - if($d[1] != $sd[1]) { # Month changed, report it - - $def->{CHANGED}[$max++] = "avg_month $r->{avg_month}{VAL}"; - $r->{cum_month}{VAL} = "0 T: 0 H: 0 W: 0 R: 0"; - - } - - } - $r->{cum_month}{TIM} = $r->{avg_month}{TIM} = $tm; - - } - - } - $r->{cum_day}{TIM} = $r->{avg_day}{TIM} = $tm; - # AVG computing - ################################### - - return $def->{NAME}; - - } else { - - Log 4, "KS300 detected: $msg"; - - } - - return ""; -} - -1; diff --git a/fhem/FHEM/50_WS300.pm b/fhem/FHEM/50_WS300.pm index d16628766..cfd54bcfc 100644 --- a/fhem/FHEM/50_WS300.pm +++ b/fhem/FHEM/50_WS300.pm @@ -39,7 +39,6 @@ package main; use strict; use warnings; -my %readings; my %defptr; my $DeviceName=""; my $inbuf=""; @@ -64,8 +63,6 @@ WS300_Initialize($) { my ($hash) = @_; - $hash->{Category} = "DEV"; - # Provider $hash->{Clients} = ":WS300:"; $hash->{ReadFn} = "WS300_Read"; @@ -74,12 +71,11 @@ WS300_Initialize($) $hash->{Match} = "^WS300.*"; $hash->{SetFn} = "WS300_Set"; $hash->{GetFn} = "WS300_Get"; - $hash->{StateFn} = "WS300_SetState"; - $hash->{ListFn} = "WS300_List"; $hash->{DefFn} = "WS300_Define"; $hash->{UndefFn} = "WS300_Undef"; $hash->{ParseFn} = "WS300_Parse"; $hash->{ReadFn} = "WS300_Read"; + $hash->{AttrList} = "do_not_notify:0,1 showtime:0,1 model:ws300 loglevel:0,1,2,3,4,5,6"; } ################################### @@ -101,7 +97,7 @@ WS300_Set($@) ################################### sub WS300_Get(@) -{ +{ my ($hash, @a) = @_; if($hash->{NAME} eq "WS300Device") { @@ -114,64 +110,27 @@ WS300_Get(@) ##################################### sub -WS300_SetState($$$$) +WS300_Define($$) { - my ($hash, $tim, $vt, $val) = @_; - - return undef if(!defined($hash->{SENSOR})); - - my $n = $hash->{SENSOR}; - if(!$readings{$n}{$vt} || $readings{$n}{$vt}{TIM} lt $tim) { - $readings{$n}{$vt}{TIM} = $tim; - $readings{$n}{$vt}{VAL} = $val; - } - return undef; -} - -##################################### -sub -WS300_List($) -{ - my ($hash) = @_; - my $str = ""; - - return "No information about $hash->{NAME}" if(!defined($hash->{SENSOR})); - my $n = $hash->{SENSOR}; - if(!defined($readings{$n})) - { - $str .= "No information about " . $hash->{NAME} . "\n"; - } - else - { - foreach my $m (keys %{ $readings{$n} }) - { - $str .= sprintf("%-19s %-15s %s\n",$readings{$n}{$m}{TIM}, $m, $readings{$n}{$m}{VAL}); - } - } - return $str; - -} - -##################################### -sub -WS300_Define($@) -{ - my ($hash, @a) = @_; + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); if($a[0] eq "WS300Device") { + $defptr{10} = $hash; return "wrong syntax: define WS300Device WS300 " if(int(@a) < 3); $DeviceName = $a[2]; $hash->{STATE} = "Initializing"; $hash->{SENSOR} = 10; - $readings{10}{WS300Device}{VAL} = "Initializing"; - $readings{10}{WS300Device}{TIM} = TimeNow; + $hash->{READINGS}{WS300Device}{VAL} = "Initializing"; + $hash->{READINGS}{WS300Device}{TIME} = TimeNow; + my $po = new Device::SerialPort ($a[2]); if(!$po) { $hash->{STATE} = "error opening device"; - $readings{10}{WS300Device}{VAL} = "error opening device"; - $readings{10}{WS300Device}{TIM} = TimeNow; + $hash->{READINGS}{WS300Device}{VAL} = "error opening device"; + $hash->{READINGS}{WS300Device}{TIME} = TimeNow; Log 1,"Error opening WS300 Device $a[2]"; return "Can't open $a[2]: $!\n"; } @@ -188,15 +147,15 @@ WS300_Define($@) $hash->{PortObj} = $po; $hash->{DeviceName} = $a[2]; $hash->{STATE} = "opened"; - $readings{10}{WS300Device}{VAL} = "opened"; - $readings{10}{WS300Device}{TIM} = TimeNow; - CommandAt($hash,"+*00:00:05 get WS300Device data"); + $hash->{READINGS}{WS300Device}{VAL} = "opened"; + $hash->{READINGS}{WS300Device}{TIME} = TimeNow; + CommandDefine(undef,"WS300Device_timer at +*00:00:05 get WS300Device data"); Log 1,"WS300 Device $a[2] opened"; + $attr{$a[0]}{savefirst} = 1; return undef; } return "wrong syntax: define WS300 \n0-7=ASH2200\n8=KS300\n9=WS300" if(int(@a) < 3); return "no device: define WS300Device WS300 first" if($DeviceName eq ""); - $a[2] = lc($a[2]); return "Define $a[0]: wrong sensor number." if($a[2] !~ m/^[0-9]$/); $hash->{SENSOR} = $a[2]; $defptr{$a[2]} = $hash; @@ -294,9 +253,10 @@ WS300_Parse($) } else { - $readings{$s}{$txt[0]}{VAL} = 0 if(!$readings{$s}); - $ref = $readings{$s}; $def = $defptr{$s}; + $def->{READINGS}{$txt[0]}{VAL} = 0 if(!$def->{READINGS}); + $ref = $def->{READINGS}; + $t = hex($a[$p].$a[$p+1].$a[$p+2].$a[$p+3]); $t -= 65535 if( $t > 32767 ); $t /= 10.0; @@ -318,25 +278,25 @@ WS300_Parse($) $def->{CHANGED}[0] = $val; $def->{CHANGETIME}[0] = $tm; # temperatur - $ref->{$txt[0]}{TIM} = $tm; + $ref->{$txt[0]}{TIME} = $tm; $value = "$t $sfx[0]"; $ref->{$txt[0]}{VAL} = $value; $def->{CHANGED}[1] = "$txt[0]: $value"; $def->{CHANGETIME}[1] = $tm; # humidity - $ref->{$txt[1]}{TIM} = $tm; + $ref->{$txt[1]}{TIME} = $tm; $value = "$h $sfx[1]"; $ref->{$txt[1]}{VAL} = $value; $def->{CHANGED}[2] = "$txt[1]: $value"; $def->{CHANGETIME}[2] = $tm; # battery - $ref->{$txt[5]}{TIM} = $tm; + $ref->{$txt[5]}{TIME} = $tm; $value = "$b $sfx[5]"; $ref->{$txt[5]}{VAL} = $value; $def->{CHANGED}[3] = "$txt[5]: $value"; $def->{CHANGETIME}[3] = $tm; # lost receives - $ref->{$txt[6]}{TIM} = $tm; + $ref->{$txt[6]}{TIME} = $tm; $value = "$l $sfx[6]"; $ref->{$txt[6]}{VAL} = $value; $def->{CHANGED}[4] = "$txt[6]: $value"; @@ -353,49 +313,49 @@ WS300_Parse($) $def->{CHANGED}[0] = $val; $def->{CHANGETIME}[0] = $tm; # temperature - $ref->{$txt[0]}{TIM} = $tm; + $ref->{$txt[0]}{TIME} = $tm; $value = "$t $sfx[0]"; $ref->{$txt[0]}{VAL} = $value; $def->{CHANGED}[1] = "$txt[0]: $value"; $def->{CHANGETIME}[1] = $tm; # humidity - $ref->{$txt[1]}{TIM} = $tm; + $ref->{$txt[1]}{TIME} = $tm; $value = "$h $sfx[1]"; $ref->{$txt[1]}{VAL} = $value; $def->{CHANGED}[2] = "$txt[1]: $value"; $def->{CHANGETIME}[2] = $tm; # wind - $ref->{$txt[2]}{TIM} = $tm; + $ref->{$txt[2]}{TIME} = $tm; $value = "$wind $sfx[2]"; $ref->{$txt[2]}{VAL} = $value; $def->{CHANGED}[3] = "$txt[2]: $value"; $def->{CHANGETIME}[3] = $tm; #rain counter - $ref->{$txt[3]}{TIM} = $tm; + $ref->{$txt[3]}{TIME} = $tm; $value = "$rainc $sfx[3]"; $ref->{$txt[3]}{VAL} = $value; $def->{CHANGED}[4] = "$txt[3]: $value"; $def->{CHANGETIME}[4] = $tm; # is raining - $ref->{$txt[4]}{TIM} = $tm; + $ref->{$txt[4]}{TIME} = $tm; $value = "$ir $sfx[4]"; $ref->{$txt[4]}{VAL} = $value; $def->{CHANGED}[5] = "$txt[4]: $value"; $def->{CHANGETIME}[5] = $tm; # battery - $ref->{$txt[5]}{TIM} = $tm; + $ref->{$txt[5]}{TIME} = $tm; $value = "$b $sfx[5]"; $ref->{$txt[5]}{VAL} = $value; $def->{CHANGED}[6] = "$txt[5]: $value"; $def->{CHANGETIME}[6] = $tm; # lost receives - $ref->{$txt[6]}{TIM} = $tm; + $ref->{$txt[6]}{TIME} = $tm; $value = "$l $sfx[6]"; $ref->{$txt[6]}{VAL} = $value; $def->{CHANGED}[7] = "$txt[6]: $value"; $def->{CHANGETIME}[7] = $tm; # rain cumulative - $ref->{$txt[8]}{TIM} = $tm; + $ref->{$txt[8]}{TIME} = $tm; $value = "$rain $sfx[8]"; $ref->{$txt[8]}{VAL} = $value; $def->{CHANGED}[8] = "$txt[8]: $value"; @@ -417,17 +377,17 @@ WS300_Parse($) $rain_hour = sprintf("%.1f",$rain_hour); $rain_day = sprintf("%.1f",$rain_day); $rain_month = sprintf("%.1f",$rain_month); - $ref->{acthour}{TIM} = $tm; + $ref->{acthour}{TIME} = $tm; $ref->{acthour}{VAL} = "$acthour"; - $ref->{$txt[9]}{TIM} = $tm; + $ref->{$txt[9]}{TIME} = $tm; $ref->{$txt[9]}{VAL} = $rain_hour; $def->{CHANGED}[9] = "$txt[9]: $rain_hour $sfx[9]"; $def->{CHANGETIME}[9] = $tm; - $ref->{$txt[10]}{TIM} = $tm; + $ref->{$txt[10]}{TIME} = $tm; $ref->{$txt[10]}{VAL} = $rain_day; $def->{CHANGED}[10] = "$txt[10]: $rain_day $sfx[10]"; $def->{CHANGETIME}[10] = $tm; - $ref->{$txt[11]}{TIM} = $tm; + $ref->{$txt[11]}{TIME} = $tm; $ref->{$txt[11]}{VAL} = $rain_month; $def->{CHANGED}[11] = "$txt[11]: $rain_month $sfx[11]"; $def->{CHANGETIME}[11] = $tm; @@ -436,14 +396,14 @@ WS300_Parse($) if($actday != $lt[3]) { $actday = $lt[3]; - $ref->{actday}{TIM} = $tm; + $ref->{actday}{TIME} = $tm; $ref->{actday}{VAL} = "$actday"; $rain_day=0; } if($actmonth != $lt[4]+1) { $actmonth = $lt[4]+1; - $ref->{actmonth}{TIM} = $tm; + $ref->{actmonth}{TIME} = $tm; $ref->{actmonth}{VAL} = "$actmonth"; $rain_month=0; } @@ -457,17 +417,17 @@ WS300_Parse($) $rain_month = sprintf("%.1f",$rain_month); $oldrain = $rain; - $ref->{acthour}{TIM} = $tm; + $ref->{acthour}{TIME} = $tm; $ref->{acthour}{VAL} = "$acthour"; - $ref->{$txt[9]}{TIM} = $tm; + $ref->{$txt[9]}{TIME} = $tm; $ref->{$txt[9]}{VAL} = $rain_hour; $def->{CHANGED}[9] = "$txt[9]: $rain_hour $sfx[9]"; $def->{CHANGETIME}[9] = $tm; - $ref->{$txt[10]}{TIM} = $tm; + $ref->{$txt[10]}{TIME} = $tm; $ref->{$txt[10]}{VAL} = $rain_day; $def->{CHANGED}[10] = "$txt[10]: $rain_day $sfx[10]"; $def->{CHANGETIME}[10] = $tm; - $ref->{$txt[11]}{TIM} = $tm; + $ref->{$txt[11]}{TIME} = $tm; $ref->{$txt[11]}{VAL} = $rain_month; $def->{CHANGED}[11] = "$txt[11]: $rain_month $sfx[11]"; $def->{CHANGETIME}[11] = $tm; @@ -484,9 +444,10 @@ WS300_Parse($) } else { - $readings{9}{$txt[0]}{VAL} = 0 if(!$readings{9}); - $ref = $readings{9}; $def = $defptr{9}; + $def->{READINGS}{$txt[0]}{VAL} = 0 if(!$def->{READINGS}); + $ref = $def->{READINGS}; + $t = hex($a[62+$offs].$a[63+$offs].$a[64+$offs].$a[65+$offs]); $t -= 65535 if( $t > 32767 ); $t /= 10.0; @@ -497,25 +458,25 @@ WS300_Parse($) $def->{CHANGED}[0] = $val; $def->{CHANGETIME}[0] = $tm; # temperature - $ref->{$txt[0]}{TIM} = $tm; + $ref->{$txt[0]}{TIME} = $tm; $value = "$t $sfx[0]"; $ref->{$txt[0]}{VAL} = $value; $def->{CHANGED}[1] = "$txt[0]: $value"; $def->{CHANGETIME}[1] = $tm; # humidity - $ref->{$txt[1]}{TIM} = $tm; + $ref->{$txt[1]}{TIME} = $tm; $value = "$h $sfx[1]"; $ref->{$txt[1]}{VAL} = $value; $def->{CHANGED}[2] = "$txt[1]: $value"; $def->{CHANGETIME}[2] = $tm; # pressure - $ref->{$txt[7]}{TIM} = $tm; + $ref->{$txt[7]}{TIME} = $tm; $value = "$press $sfx[7]"; $ref->{$txt[7]}{VAL} = $value; $def->{CHANGED}[3] = "$txt[7]: $value"; $def->{CHANGETIME}[3] = $tm; # willi - $ref->{willi}{TIM} = $tm; + $ref->{willi}{TIME} = $tm; $value = "$willi"; $ref->{willi}{VAL} = $value; $def->{CHANGED}[4] = "willi: $value"; @@ -569,8 +530,8 @@ NEXTPOLL: Log 1, "USB device $devname disconnected, waiting to reappear"; $hash->{PortObj}->close(); $hash->{STATE} = "disconnected"; - $readings{10}{WS300Device}{VAL} = "disconnected"; - $readings{10}{WS300Device}{TIM} = TimeNow; + $hash->{READINGS}{WS300Device}{VAL} = "disconnected"; + $hash->{READINGS}{WS300Device}{TIME} = TimeNow; sleep(1); my $po = new Device::SerialPort($devname); if($po) @@ -588,8 +549,8 @@ NEXTPOLL: Log 1, "USB device $devname reappeared"; $hash->{PortObj} = $po; $hash->{STATE} = "opened"; - $readings{10}{WS300Device}{VAL} = "opened"; - $readings{10}{WS300Device}{TIM} = TimeNow; + $hash->{READINGS}{WS300Device}{VAL} = "opened"; + $hash->{READINGS}{WS300Device}{TIME} = TimeNow; $polling=0; return; } @@ -651,8 +612,8 @@ NEXTPOLL: if($errcount == 10) { $hash->{STATE} = "timeout"; - $readings{10}{WS300Device}{VAL} = "timeout"; - $readings{10}{WS300Device}{TIM} = TimeNow; + $hash->{READINGS}{WS300Device}{VAL} = "timeout"; + $hash->{READINGS}{WS300Device}{TIME} = TimeNow; $errcount++; } Log 1,"WS300: no data" if($rcount == 0); @@ -663,8 +624,8 @@ NEXTPOLL: if($hash->{STATE} ne "connected" && $errcount > 10) { $hash->{STATE} = "connected"; - $readings{10}{WS300Device}{VAL} = "connected"; - $readings{10}{WS300Device}{TIM} = TimeNow; + $hash->{READINGS}{WS300Device}{VAL} = "connected"; + $hash->{READINGS}{WS300Device}{TIME} = TimeNow; } $errcount = 0; $ic = ord(substr($inbuf,0,1)); diff --git a/fhem/FHEM/90_FileLog.pm b/fhem/FHEM/90_FileLog.pm deleted file mode 100755 index 6ddfec9a9..000000000 --- a/fhem/FHEM/90_FileLog.pm +++ /dev/null @@ -1,97 +0,0 @@ -############################################## -package main; - -use strict; -use warnings; -use IO::File; - -##################################### -sub -FileLog_Initialize($) -{ - my ($hash) = @_; - - $hash->{Category}= "LOG"; - - $hash->{DefFn} = "FileLog_Define"; - $hash->{UndefFn} = "FileLog_Undef"; - $hash->{LogFn} = "FileLog_Log"; -} - - -##################################### -sub -FileLog_Define($@) -{ - my ($hash, @a) = @_; - my $fh; - - return "wrong syntax: define FileLog filename regexp" if(int(@a) != 4); - - eval { "Hallo" =~ m/^$a[3]$/ }; - return "Bad regexp: $@" if($@); - - my @t = localtime; - my $f = ResolveDateWildcards($a[2], @t); - $fh = new IO::File ">>$f"; - return "Can't open $f" if(!defined($fh)); - - $hash->{FH} = $fh; - $hash->{REGEXP} = $a[3]; - $hash->{FILENAME} = $a[2]; - $hash->{CURRENT} = $f; - - return undef; -} - -##################################### -sub -FileLog_Undef($$) -{ - my ($hash, $name) = @_; - close($hash->{FH}); - return undef; -} - - -##################################### -sub -FileLog_Log($$) -{ - my ($log, $dev) = @_; - - my $n = $dev->{NAME}; - my $re = $log->{REGEXP}; - my $max = int(@{$dev->{CHANGED}}); - - for (my $i = 0; $i < $max; $i++) { - my $s = $dev->{CHANGED}[$i]; - $s = "" if(!defined($s)); - if($n =~ m/^$re$/ || "$n:$s" =~ m/^$re$/) { - my $t = TimeNow(); - $t = $dev->{CHANGETIME}[$i] if(defined($dev->{CHANGETIME}[$i])); - $t =~ s/ /_/; - - my $fh = $log->{FH}; - my @t = localtime; - my $cn = ResolveDateWildcards($log->{FILENAME}, @t); - - if($cn ne $log->{CURRENT}) { # New logfile - $fh->close(); - $fh = new IO::File ">>$cn"; - if(!defined($fh)) { - Log(0, "Can't open $cn"); - return; - } - $log->{CURRENT} = $cn; - $log->{FH} = $fh; - } - - print $fh "$t $n $s\n"; - $fh->flush; - $fh->sync; - } - } -} - -1; diff --git a/fhem/HISTORY b/fhem/HISTORY index 3b916096f..5e98b6d36 100644 --- a/fhem/HISTORY +++ b/fhem/HISTORY @@ -15,6 +15,40 @@ - Added doc/linux.html (multiple USDB devices, udev links) - Linked fhem.html and commandref.html to linux.html +- rudi, Sun Mar 4 11:18:10 MET 2007 + Reorganization. Goal: making attribute adding/deleting more uniform + ("at/notify" and other device differences), and making web-configuration + possible (i.e. saving the configfile, list of possible devices etc). + + Internal changes: + - %logmods,%devmods moved to %modules. Makes things more uniform + - %logs merged into %defs + - local state info (%readings) changed to global ($defs{$d}{READINGS}) + -> No need for the listfn function in each module + -> User written scripts can more easily analyze device states + + User visible changes: + - at/notify "renamed" to "define at/notify", both moved to external + modules. Now it is possible + - to have a further "at" or "notify" modules + (notify & filelog use the same interface) + - to have more than one notify for the same event + - to delete at commands without strange escapes. + The delete syntax changed (no more def/at/ntfy needed) + - at/notify can have attributes + Drawback: each at and notify must have a name, which is strange first. + - logfile/modpath/pidfile/port/verbose "renamed" to "attr global xxx" + Dumping and extending these attributes is easier, no special handling + required in the web-frontend. + - savefile renamed to "attr global statefile" + - configfile global attribute added. + - save command added, it writes the statefile and then the configfile. + - delattr added to delete single attributes + - list/xmllist format changed, they contain more information. + - "define/set/get/attr name ?" returns a list of possible arguments + in the same format. This data is contained in the xmllist. + - disable attribute for at/notify/filelog + - Martin Haas, Fri Feb 23 10:18 MET 2007 - ARM-Section (NSLU2) added to doc/linux.html diff --git a/fhem/contrib/91_DbLog.pm b/fhem/contrib/91_DbLog.pm index 2aaa4794a..9d4b32ac9 100755 --- a/fhem/contrib/91_DbLog.pm +++ b/fhem/contrib/91_DbLog.pm @@ -32,8 +32,6 @@ DbLog_Initialize($) { my ($hash) = @_; - $hash->{Category} = "none"; - # Lets connect here, so we see the error at startup DbConnect(); } diff --git a/fhem/contrib/99_ALARM.pm b/fhem/contrib/99_ALARM.pm index f7a310e5f..122e62562 100755 --- a/fhem/contrib/99_ALARM.pm +++ b/fhem/contrib/99_ALARM.pm @@ -31,8 +31,7 @@ use warnings; sub ALARM_Initialize($$) { - my ($hash, $init) = @_; - $hash->{Type} = "none"; + my ($hash) = @_; } diff --git a/fhem/contrib/99_SUNRISE.pm b/fhem/contrib/99_SUNRISE.pm index f26c22b06..1e490098b 100755 --- a/fhem/contrib/99_SUNRISE.pm +++ b/fhem/contrib/99_SUNRISE.pm @@ -30,8 +30,6 @@ sub SUNRISE_Initialize($) { my ($hash) = @_; - - $hash->{Category} = "none"; } diff --git a/fhem/contrib/em1010.pl b/fhem/contrib/em1010.pl index 389264e5c..43c2551c8 100755 --- a/fhem/contrib/em1010.pl +++ b/fhem/contrib/em1010.pl @@ -246,9 +246,10 @@ getDevStatus() printf(" Nr devs (off 05): %d\n", b($d,6)); printf(" puls/5min (off 13): %d\n", w($d,13)); printf(" Startblk (off 18): %d\n", b($d,18)+13); - printf(" Alarm, PA (off 45): %d W\n", w($d,45)); - printf(" PRICE, CF (off 47): %0.2f (EUR/KWH)\n", w($d,47)/10000); - printf(" R/KW, EC (off 49): %d\n", w($d,49)/10); + printf(" cur.power (off 33): %.3f kW\n", w($d,33) * 2 / 300); + printf(" Alarm PA (off 45): %d W\n", w($d,45)); + printf(" Price CF (off 47): %0.2f (EUR/KWH)\n", w($d,47)/10000); + printf(" R/KW EC (off 49): %d\n", w($d,49)/10); hexdump($d); } @@ -268,6 +269,8 @@ getDevPage() sub getDevData() { + my $smooth = 1; # Set this to 0 to get the "real" values + die "Usage: getDevData devicenumber (1-12)\n" if(@ARGV != 3); my $d = getData(sprintf("7a%02x",$ARGV[2]-1)); @@ -284,23 +287,39 @@ getDevData() my $step = b($d,6); my $start = b($d,18)+13; my $end = $start + int(($nrreadings-1)/64)*$step; - my $offset = ($nrreadings%64)*4+4; my $div = w($d,49)/10; - $div = 1 if($div == 0); - #printf("Total $nrreadings, $start - $end, Nr $step, Off: $offset\n"); + #printf("Total $nrreadings, $start - $end, Nr $step\n"); - my $now = time(); - for(my $p = $end; $p >= $start; $p -= $step) { + my $tm = time()-(($nrreadings-1)*300); + my $backlog = 0; + for(my $p = $start; $p <= $end; $p += $step) { #printf("Get page $p\n"); + $d = getData(sprintf("52%02x%02x00000801", $p%256, int($p/256))); + #hexdump($d); - $offset = 260 if($p != $end); - while($offset >= 8) { - printf("%s %0.3f kWh (%d)\n", - maketime($now), w($d,$offset)*12/$div, w($d,$offset+2)); - $offset -=4; - $now -= 300; + + my $max = (($p == $end) ? ($nrreadings%64)*4+4 : 260); + my $step = b($d, 6); + + for(my $off = 8; $off <= $max; $off += 4) { + $backlog++; + if($smooth && (w($d,$off+2) == 0xffff)) { # "smoothing" + next; + } else { + my $v = w($d,$off)*12/$div/$backlog; + my $f1 = b($d,$off+2); + my $f2 = b($d,$off+3); + my $f3 = w($d,$off+2); + + while($backlog--) { + printf("%s %0.3f kWh (%d %d %d)\n", maketime($tm), $v, + ($backlog?-1:$f1), ($backlog?-1:$f2), ($backlog?-1:$f3)); + $tm += 300; + } + $backlog = 0; + } } } } @@ -378,9 +397,12 @@ setTime() my @d = split("-", $ARGV[2]); my @t = split(":", $ARGV[3]); - my $d = getData(sprintf("73%02x%02x%02x00%02x%02x%02x", + my $s = sprintf("73%02x%02x%02x00%02x%02x%02x", $d[2],$d[1],$d[0]-2000+0xd0, - $t[0],$t[1],$2[2])); + $t[0],$t[1],$t[2]); + print("-> $s\n"); + + my $d = getData($s); if(b($d,0) == 6) { print("OK"); } else { diff --git a/fhem/contrib/rolwzo_not_off.sh b/fhem/contrib/rolwzo_not_off.sh index bc5b43354..c3076888f 100755 --- a/fhem/contrib/rolwzo_not_off.sh +++ b/fhem/contrib/rolwzo_not_off.sh @@ -5,11 +5,11 @@ # o'clock then the at-job for going down by sunset will be deleted # put something like the following into your fhz100.cfg: -# notifyon rolwzo /usr/local/bin/rolwzo_not_off.sh +# define rolzwo_not_off notify rolwzo /usr/local/bin/rolwzo_not_off.sh FHZ="/usr/local/bin/fhem.pl 7072" -order="delete at set rolwzo off" +order="delete rolwzo_off" DATESTRING=`date +"%H"` [[ $DATESTRING > 16 ]] && $FHZ "$order" diff --git a/fhem/docs/commandref.html b/fhem/docs/commandref.html index ab7d8274e..fa2c28d0a 100644 --- a/fhem/docs/commandref.html +++ b/fhem/docs/commandref.html @@ -20,11 +20,11 @@ You can use all of the following commands in in two ways:
A minimal configuration file would look like:
-      logfile /tmp/fhem.log
-      savefile /tmp/fhem.save  
-      verbose 3                   
-      port 7072                   
-      modpath .                   
+      attr global logfile /tmp/fhem.log
+      attr global statefile /tmp/fhem.save  
+      attr global verbose 3                   
+      attr global port 7072                   
+      attr global modpath /usr/local/lib
       define FHZ FHZ /dev/tts/USB0        
 
       define lamp FS20 8765 01
@@ -47,28 +47,28 @@ You can use all of the following commands in in two ways: -There are three types of commands: "fhz" commands (described in this document), +There are three types of commands: "fhem" commands (described in this document), shell commands (they must be enclosed in double quotes ") and perl expressions (enclosed in curly brackets {}). shell commands or perl expressions are needed for -complex at or notifyon arguments.

+complex at or notify arguments.

Shell commands will be executed in the -background, the perl program and the fhz commands will be executed in the main +background, the perl program and the fhem commands will be executed in the main "thread". In order to make perl expressions easier to write, some special functions and variables are available. See the section Perl special for a description. -To trigger "fhz" commands from a shell script, use the client form of +To trigger "fhem" commands from a shell script, use the client form of fhem.pl (described above).

-Multiple fhz commands are separated by semicolon (;). In order to use semicolon +Multiple fhem commands are separated by semicolon (;). In order to use semicolon in perl code or shell programs, they have to be escaped by the double semicolon (;;).

Commands can be either typed in plain, or read from a file (e.g. the configuration file at startup). The commands are either executed directly, or later if they are arguments to the at and notifyon fhz commands.

+href="#notify">notify fhem commands.

If commands are read from a file, then a line ending with \ will be concatenated with the next one, so long lines (e.g. perl oneliners) can be @@ -85,136 +85,174 @@ split in multiple lines

- -

at

-
    - at <timespec> <command>
    -
    - Start an arbitrary fhem.pl command at a later time. - <timespec> format: [+][*]HH:MM:SS
    - The optional + indicates that the specification is - relative(i.e. it will be added to the current time).
    - The optional * indicates that the command should be - executed repeatedly.
    - The optional {N} after the * indicates,that the command - should be repeated N-times only.
    -
    - - Examples: -
    -  # absolute ones:
    -  at 17:00:00 set lamp on                            # fhz command
    -  at 17:00:00 { Log 1, "Teetime" }                   # Perl command
    -  at 17:00:00 "/bin/echo "Teetime" > /dev/console"   # shell command
    -  at *17:00:00 set lamp on                           # repeat every day
    -
    -  # relative ones
    -  at +00:00:10 set lamp on                  # switch the lamp on in 10 seconds
    -  at +00:00:02 set lamp on-for-timer 1      # Blink once in 2 seconds
    -  at +*{3}00:00:02 set lamp on-for-timer 1   # Blink 3 times
    -
    -  # Blink 3 times if the piri sends a command
    -  notify piri:on.* at +*{3}00:00:02 set lamp on-for-timer 1
    -
    -  # Switch the lamp on from sunset to 11 PM 
    -  # Copy 99_SUNRISE.pm in the FHEM directory to have sunset_rel()
    -  { sunrise_coord("8.686", "50.112", "Europe/Berlin") }
    -  at +*{sunset_rel()} set lamp on
    -  at *23:00:00 set lamp off
    -
    -  # More elegant version, works for sunset > 23:00 too
    -  at +*{sunset_rel()} set lamp on-till 23:00
    -
    -  # Only do this on weekend
    -  at +*{sunset_rel()} { fhz("set lamp on-till 23:00") if($we) }
    -
    -  # Switch lamp1 and lamp2 on from 7:00 till 10 minutes after sunrise
    -  at *07:00 set lamp1,lamp2 on-till {sunrise_abs(+600)}
    -
    -  # Switch the lamp off 2 minutes after sunrise each day
    -  at +*{sunrise_rel(+120)} set lamp on
    -  
    - - Notes:
    -
      -
    • if no * is specified, then a command will be executed - only once, and then the at entry will be deleted.
    • -
    • if the current time is greater then the time specified, then the - command will be executed tomorrow. This is why the relative forms - of the sunset/sunrise functions should be used with the relative - (+) flag
    • -
    • the delete argument for at is the - complete line as it appears in list - (with spaces), but you can use regexps.
    • -
    • In order to use the sunrise_rel()/sunset_rel() functions, copy the - 99_SUNRISE.pm file from the contrib into the modules (FHEM) - directory, and put { sunrise_coord(long, lat, tz) } into your config - file, as in the above example. If you are not using sunrise_coord, then - the coordinates for Frankfurt am Main, Germany will be used. - You also have to install the Datetime::Event::Sunrise perl module. -
    • -
    • do not place at commands in the config file if you - specified a savefile, as all - at commands will be saved there too, and then defined - twice at startup. at is intended to be inserted by - hand or by cronjobs.
    • -
    • For more complex date handling you either have to call fhem from - cron or filter the date in a perl expression, see the last example and - the section Perl special. -
    • -

attr

    - attr <devname> <attrname> [<value>]
    + attr <name> <attrname> [<value>]
    or
    attr at <at-spec-regexp> <attribute>
    -
    Set a device,log or at attribute. There are some special attributes used - by the fhem.pl itself or the web-frontends, but you can define your own to - use them in other applications.

    +
    Set an attribute to something defined by define. + There are some special attributes used by the fhem.pl itself or the + web-frontends, but you can define your own to use them in other applications. + You have to define them first with userattr, see + below.
    + + Use "attr <name> ?" to get a list of possible attributes. +

    + + Following are attributes of the global device:
    - Recognized attributes:
      + + +
    • configfile
      + Contains the name of the configuration file. If save is called without argument, then the output will + be written to this file. +

    • + + +
    • logfile
      + Specify the logfile to write. You can use "-" for + stdout, in this case the server won't background itself.
      + The logfile name can also take wildcards for easier logfile rotation, + see the FileLog section. +

    • + + +
    • modpath
      + Specify the path to the modules directory FHEM. The path + should not contain the directory FHEM. Every file there with the + name pattern <number>_<name>.pm will be loaded in the order + of the number. +

    • + + +
    • pidfilename
      + Write the process id of the perl process to the specified file. The + server runs as a daemon, and some distributions would like to check by + the pid if we are still running. The file will be deleted upon + shutdown. +

    • + + +
    • pidfilename
      + Works for the global "device"
      +

    • + + +
    • port
      + Listen on the TCP/IP port <number> for incoming + connections. To offer at least a little bit of security, the server + will only listen for connections from the localhost per default. If + there is a second value "global" then the server will listen for + non-localhost connections too. +

    • + + +
    • statefile
      + Set the filename where the state and certain at + information will be saved before shutdown. If it is not specified, then + no information will be saved. +

    • + + +
    • userattr
      + A space separated list which contains the names of additional + attributes. Without specifying them you will not be able to set them + (in order to prevent typos). +

    • + + +
    • verbose
      + Set the verbosity level. Possible values: +
        +
      • 0 - it will only tell you when the server was started, or + stopped
      • +
      • 1 - it logs all error messages or unknown packets
      • +
      • 2 - it logs all signals received or sent in a "digested" + format,
      • +
      • 3 - it logs the signals for undefined devices,
      • +
      • 4 - it logs the TCP/IP connections and the called programs with + parameters,
      • +
      • 5 - is for debugging.
      • +
      + Recommended level is 3 for normal use. +
    • +
    + +
    + Attributes used by all devices: +
      +
    • room
      Filter/group devices. Recognized by web-pgm2 and web-pgm3. - Devices in the room hidden will not appear in the web output.
    • + Devices in the room hidden will not appear in the web output.
      + + +
    • showtime
      + Used in the webfrontend pgm2 to show the time of last activity + instead of the state in the summary view. Useful e.g. for FS20 PIRI + devices. +
    • +
    + +
    + Attributes used by selected devices: +
      + +
    • disable
      + Can be applied to at/notify/FileLog devices.
      + Disables the corresponding at/notify or FileLog device. Note: + If applied to an at, the command will not be executed, + but the next time will be computed.

    • + + +
    • do_not_notify
      + Can be applied to FHZ/FS20/FHT/HMS/KS300/WS300 devices.
      + Disable FileLog/notify/inform notification for a device. This affects + the received signal, the set and trigger commands.

    • + + +
    • dummy
      + Can be applied to FS20/FHT devices.
      + Set the device attribute dummy to define devices which should not + output any radio signals. Associated notifys will be executed if + the signal is received. Used e.g. to react to a code from a sender, but + it will not emit radio signal if triggered in the web frontend. + Implemented for FS20 and FHT devices.

    • + + +
    • filtertimeout
      + Can be applied to FHZ devices.
      + Ignore duplicate messages for this amount of time. The time is in + seconds, fractions are allowed. It affects installations with more then + one FHZ device or repeater, see the repeater + entry. +

    • + + +
    • follow-on-for-timer
      + Can be applied to FS20 devices.
      + the program automatically schedules a "setstate off" for the time + specified as argument to the on-for-timer command (for the specified + device only). +

    • + +
    • loglevel
      + Can be applied to FHZ/FS20/FHT/HMS/KS300/WS300 devices.
      Set the device loglevel to e.g. 6 if you do not wish messages from a given device to appear in the global logfile (FHZ/FS20/FHT). E.g. to set the FHT time, you should schedule "set FHZ time" every minute, but this in turn makes your logfile unreadable. These messages will not be - generated if the FHZ attribute loglevel is set to 6.
    • -
    • dummy
      - Set the device attribute dummy to define devices which should not - output any radio signals. Associated notifyons will be executed if - the signal is received. Used e.g. to react to a code from a sender, but - it will not emit radio signal if triggered in the web frontend. - Implemented for FS20 and FHT devices.
    • -
    • do_not_notify
      - Disable FileLog/notify/inform notification for a device. This affects - the received signal, the set and trigger commands.
    • -
    • skip_next
      - Used for at commands: skip the execution of the command the next time. -
    • -
    • follow-on-for-timer
      - the program automatically schedules a "setstate off" for the time - specified as argument to the on-for-timer command (for the specified - device only). -
    • -
    • repeater
      - Set the attribute "repeater" for an FHZ device to 1 to ignore events - received from a FS20 repeater. In fact we are not sure that they are - repeater messages, we just ignore messages which were sent out by our - device for the next 3 seconds (see the next attribute) -
    • -
    • filtertimeout
      - Ignore duplicate messages for this amount of time. The time is in - seconds, fractions are allowed. It affects installations with more then - one FHZ device or repeater, see the entry above. -
    • + generated if the FHZ attribute loglevel is set to 6.
      + +
    • model
      + Can be applied to FHZ/FS20/FHT/HMS/KS300/WS300 devices.
      The model attribute denotes the model type of the device. The attributes will (currently) not be used by the fhem.pl directly. It can be used by e.g. external programs or web interfaces to @@ -224,34 +262,84 @@ split in multiple lines

      documentation which comes which each device. This name is used without blanks in all lower-case letters. Valid characters should be a-z 0-9 and - (dash), - other characters should be ommited - (fht80b, fs20du, fs20led, fs20piri, fs20s20, fs20s4a, - fs20st fs20ts, hms100mg, rm100-2uni-s, etc.). -
    • -
    • showtime
      - Used in the webfrontend pgm2 to show the time of last activity - instead of the state in the summary view. Useful e.g. for FS20 PIRI - devices. -
    • + other characters should be ommited. Here is a list of "official" + devices:
      +
        +
      • FHT: fht80b
      • +
      • FS20: + fs20hgs + fs20hgs + fs20pira + fs20piri + fs20s20 + fs20s4 + fs20s4a + fs20s4m + fs20s4u + fs20s4ub + fs20sd + fs20sn + fs20sr + fs20ss + fs20str + fs20tfk + fs20tfk + fs20tk + fs20uts + fs20ze + + fs20as1 + fs20as4 + fs20di + fs20du + fs20ms2 + fs20rst + fs20sa + fs20sig + fs20st + fs20sv + fs20sv + fs20usr + +
      • + +
      • HMS: hms100-t hms100-tf hms100-wd hms100-mg hms100-tfk rm100-2
      • +
      • KS300: ks300
      • +
      • WS300: ws300pc
      • +
      • EM1010: em1010pc
      • +
          +
          + + +
        • repeater
          + Can be applied to FHZ devices.
          + Set the attribute "repeater" for an FHZ device to 1 to ignore events + received from a FS20 repeater. In fact we are not sure that they are + repeater messages, we just ignore messages which were sent out by our + device for the next 3 seconds (see the next attribute) +

        • + + +
        • skip_next
          + Can be applied to at devices.
          + Used for at commands: skip the execution of the command the next time. +

        • +
        Examples:
          + attr global verbose 3
          attr lamp room kitchen
          attr lamp dummy
          attr lamp loglevel 6
          - attr lamp model fs20du
          - del attr lamp
          - at *23:00:10 set lamp off
          - attr at lamp.off skip_next
          -

        Notes:
        -
          -
        • There is no way to delete a single attribute.
        +
      • See delattr to delete attributes.
      • +
        @@ -260,8 +348,8 @@ split in multiple lines

        defattr [<attrname> [<value>]]

        Set the default attribute. Each device defined from now on will receive - this attribute (see the at for details on attribute names - and values).
        + this attribute (see the attr entry for details on + attribute names and values).
        If no attrname is specified, then the default attribute list will be deleted.

        @@ -293,9 +381,11 @@ split in multiple lines

        Define a device. You need devices if you want to manipulate them (e.g. set on/off), and the logfile is also more readable if it contains e.g. "lamp off" instead of "Device 5673, Button 00, Code 00 (off)".
        + Use "define <name> ?" to get a list of possible types.
        - Type FHZ: + +

        Type FHZ

          define <name> FHZ <serial-device>

          @@ -319,7 +409,8 @@ split in multiple lines


        - Type FS20: + +

        Type FS20

          define <name> FS20 <housecode> <button> [fg <fgaddr>] [lm <lmaddr>] [gm FF] @@ -343,7 +434,8 @@ split in multiple lines


        - Type FHT: + +

        Type FHT

          define <name> FHT <housecode>

          @@ -361,7 +453,8 @@ split in multiple lines


        - Type HMS: + +

        Type HMS

          define <name> HMS <housecode>

          @@ -403,7 +496,8 @@ split in multiple lines


        - Type KS300: + +

        Type KS300

          define <name> KS300 <housecode> [ml/raincounter [wind-factor]]

          @@ -423,7 +517,8 @@ split in multiple lines


        - Type WS300: + +

        Type WS300

          define WS300Device WS300 <serial device>
          or
          @@ -449,7 +544,8 @@ split in multiple lines


          - Type FileLog: + +

          Type FileLog

            define <name> FileLog <filename> <regexp>

            @@ -480,37 +576,178 @@ split in multiple lines


        + + +

        Type at

        +
          + define <name> at <timespec> <command>
          +
          + Start an arbitrary fhem.pl command at a later time.
          + <timespec> format: [+][*{N}]<timedet>
          +
            + The optional + indicates that the specification is + relative(i.e. it will be added to the current time).
            + The optional * indicates that the command should be + executed repeatedly.
            + The optional {N} after the * indicates,that the command + should be repeated N-times only.
            + <timedet> is either HH:MM, HH:MM:SS or {perlfunc()}, where perlfunc + must return a HH:MM or HH:MM:SS date. +
          +
          + + Examples: +
          +    # absolute ones:
          +    define a1 at 17:00:00 set lamp on                            # fhem command
          +    define a2 at 17:00:00 { Log 1, "Teetime" }                   # Perl command
          +    define a3 at 17:00:00 "/bin/echo "Teetime" > /dev/console"   # shell command
          +    define a4 at *17:00:00 set lamp on                           # every day
          +
          +    # relative ones
          +    define a5 at +00:00:10 set lamp on                  # switch the lamp on in 10 seconds
          +    define a6 at +00:00:02 set lamp on-for-timer 1      # Blink once in 2 seconds
          +    define a7 at +*{3}00:00:02 set lamp on-for-timer 1  # Blink 3 times
          +
          +    # Blink 3 times if the piri sends a command
          +    define n1 notify piri:on.* define a8 at +*{3}00:00:02 set lamp on-for-timer 1
          +
          +    # Switch the lamp on from sunset to 11 PM 
          +    # Copy 99_SUNRISE.pm in the FHEM directory to have sunset_rel()
          +    { sunrise_coord("8.686", "50.112", "Europe/Berlin") }
          +    define a9 at +*{sunset_rel()} set lamp on
          +    define a10 at *23:00:00 set lamp off
          +
          +    # More elegant version, works for sunset > 23:00 too
          +    define a11 at +*{sunset_rel()} set lamp on-till 23:00
          +
          +    # Only do this on weekend
          +    define a12 at +*{sunset_rel()} { fhem("set lamp on-till 23:00") if($we) }
          +
          +    # Switch lamp1 and lamp2 on from 7:00 till 10 minutes after sunrise
          +    define a13 at *07:00 set lamp1,lamp2 on-till {sunrise_abs(+600)}
          +
          +    # Switch the lamp off 2 minutes after sunrise each day
          +    define a14 at +*{sunrise_rel(+120)} set lamp on
          +    
          + + Notes:
          +
            +
          • if no * is specified, then a command will be executed + only once, and then the at entry will be deleted. In + this case the command will be saved to the statefile (as it + considered volatile, i.e. entered by cronjob) and not to the + configfile (see the save command.) +
          • + +
          • if the current time is greater then the time specified, then the + command will be executed tomorrow. This is why the relative forms + of the sunset/sunrise functions should be used with the relative + (+) flag
          • + +
          • In order to use the sunrise_rel()/sunset_rel() functions, copy the + 99_SUNRISE.pm file from the contrib into the modules (FHEM) + directory, and put { sunrise_coord(long, lat, tz) } into your config + file, as in the above example. If you are not using sunrise_coord, then + the coordinates for Frankfurt am Main, Germany will be used. + You also have to install the Datetime::Event::Sunrise perl module. +
          • + +
          • For even more complex date handling you either have to call fhem from + cron or filter the date in a perl expression, see the last example and + the section Perl special. +
          • +
          +
          +
        + + +

        Type notify

        +
          + define <name> notify <pattern> <command> +

          + Execute a command when received an event for the definition <pattern>. If + <command> is enclosed in {}, then it is a perl expression, if it is + enclosed in "", then it is a shell command, else it is a "plain" fhem.pl + command (chain). See the trigger command for + testing it. + + Examples: +
            + notify btn3 set lamp %
            + notify btn3 { fhem "set lamp %" }
            + notify btn3 "/usr/local/bin/setlamp "%""
            + notify btn3 set lamp1 %;;set lamp2 %
            + notify wz:measured.* "/usr/local/bin/logfht @ "%""
            + notify .*H:.* {DbLog("@","%")}
            + notify UNDEFINED "send-me-mail.sh "%""
            +
          +
          + + Notes: +
            +
          • The character % will be replaced with the received event, + e.g. with on or off or measured-temp: 21.7 + (Celsius)
            It is advisable to put the % into double + quotes, else the shell may get a syntax error.
            + To use % or @ in the text itself, use the double mode (%% or @@)
          • + +
          • The character @ will be replaced with the device + name.
          • + +
          • <pattern> may also be a compound of + definition:event to filter for events.
          • + +
          • <pattern> must completely (!) + match either the device name, or the compound of the device name and the + event. The event is either the string you see in the list output in paranthesis after the device name, or the + string you see when you do a detailed list of the device.
          • + +
          • To use database logging, copy the file contrib/91_DbLog.p into your + modules directory, and change the $dbconn parameter in the file.
          • + +
          • Each undefined device (FS20, HMS, FHT) will be reported with the + device name "UNDEFINED". The % parameter will contain the type (FS20, + HMS100T, etc) and device number, separated by a space.
          • + +
          + +
        +
      + +

      delattr

      +
        + delattr <name> [<attrname>]
        +
        + Delete either a single attribute (see the attr command) + or all attributes for a device (if no <attrname> is defined).
        + + Examples: +
          + delattr lamp follow-on-for-timer
          + delattr lamp
          +
        +
        +
      +

      delete

        - delete {def|ntfy|at} <name>
        + delete <name>
        +
        + Delete something created with the define command.
        - Delete a definition, a - notifyon setting or an at command.
        - The <name> argument has to be the first column of - the list output (as in case of at least at - it is not obvious). -

        - Examples:
          - delete def lamp
          - delete at 22:15:00 set lamp off + delete lamp

        - - Notes: -
          -
        • The program first tries to find the exact <name>, if - it is not found, then it will try it as a regexp, so you can also specify - delete at .*lamp.* to delete the at command above.
        • - -
        • Do not use doubleqoutes ("e;) in <name>, only if - you used them in your definition. -
      @@ -519,8 +756,7 @@ split in multiple lines

      get <name> <type-specific>

      Ask a value directly from the device, and wait for an answer. In general, you - can get a list of possible commands by
      get <device> - help + can get a list of possible commands by
      get <device> ?
      Right now only the FHZ module supports this function. @@ -539,17 +775,15 @@ split in multiple lines

      • There is only one FHZ device (called FHZ), it is created automatically at startup.
      • -
      • The mentioned codes are needed for initializing the FHZ1000
      • +
      • The mentioned codes are needed for initializing the FHZ1X00
      • The answer for a command is also displayed by list FHZ
      • - fhtbuf should be incorporated later in the FHT - module:
        the FHZ1000PC has a message buffer for the FHT, as it + The FHZ1000PC has a message buffer for the FHT, as it only can send messages to it every 2 (or so) minutes. If the buffer is full, then newly issued ones will be dropped. fhtbuf returns the free memory in this buffer (in hex), my maximum is 2c (42 bytes).
      • -
@@ -581,7 +815,7 @@ split in multiple lines

    list [name]

    - Output a list of all definitions, all notifyon settings and all at + Output a list of all definitions, all notify settings and all at entries. This is one of the few commands which return a string in a normal case.

    @@ -590,185 +824,80 @@ split in multiple lines

    Type list for detailed info. - FHZ devices: - FHZ (Last msg: Initialized) + Internal: + global (Internal) - FS20 devices: - Btn3 (Last msg: off) - Roll1 (Last msg: on-for-timer 11) - Stehlampe (Last msg: off) + FHZ: + FHZ (fhtbuf: 23) - FHT devices: - fl (Last msg: state: Bat: ok, Window: closed) - wz (Last msg: actuator: 07%) + FS20: + Btn4 (on-old-for-timer) + Roll1 (on) + Stehlampe (off) - NotifyOn: - Btn3 /usr/local/bin/setroll % + FHT: + fl (measured-temp: 21.1 (Celsius)) - At: - +*{sunrise_rel(+10)} set Roll1 on-for-timer 10 (07:43:56) + KS300: + out1 (T: 2.9 H: 74 W: 2.2 R: 8.2 IR: no) + at: + at_rollup (Next: 07:00:00) + + notify: + ntfy_btn4 (active) + + FileLog: + avglog (active) - Logs: - wzlog FileLog /var/tmp/wz.log wz:.*(temp|actuator).*
    If specifying name, then a detailed status for name will be displayed, e.g.: -
      FHZ> list wz
    +  
      FHZ> list fl
    +
    +  Internals:
    +    CODE       5102
    +    DEF        5102
    +    NAME       fl
    +    NR         15
    +    STATE      measured-temp: 21.1 (Celsius)
    +    TYPE       FHT
    +    IODev      FHZ
    +  Attributes:
    +    room       Heizung
    +  Readings:
    +    2006-11-02 09:45:56   actuator        19%
    +    2006-10-28 15:10:58   code_0000aa     17
    +    2006-10-07 10:52:09   code_0000ba     0
    +    2006-11-02 10:40:08   code_4b004b     0
    +    2006-11-02 08:24:46   code_7e0067     0
    +    2006-11-02 04:13:35   day-temp        21.0 (Celsius)
    +    2006-11-02 06:03:37   desired-temp    21.0 (Celsius)
    +    2006-11-02 04:13:31   fri-from1       06:00
    +    2006-11-02 04:13:31   fri-to1         23:50
    +    2006-11-02 04:11:30   init            255
    +    2006-11-02 10:40:06   measured-high   0
    +    2006-11-02 10:40:06   measured-low    211
    +    2006-11-02 10:40:06   measured-temp   21.1 (Celsius)
    +    2006-11-02 04:13:39   mode            auto
    +    2006-11-02 04:13:25   mon-from1       06:00
    +    2006-11-02 04:13:25   mon-to1         23:00
    +    2006-11-02 04:13:35   night-temp      18.0 (Celsius)
    +    2006-11-02 04:13:32   sat-from1       08:00
    +    2006-11-02 04:13:33   sat-to1         23:50
    +    2006-11-02 10:40:07   state           Bat: ok, Window: closed
    +    2006-11-02 04:13:34   sun-from1       08:00
    +    2006-11-02 04:13:34   sun-to1         23:00
    +    2006-11-02 04:13:29   thu-from1       06:00
    +    2006-11-02 04:13:29   thu-to1         23:00
    +    2006-11-02 04:13:26   tue-from1       06:00
    +    2006-11-02 04:13:26   tue-to1         23:00
    +    2006-11-02 04:13:36   unknown_85      4
    +    2006-11-02 04:13:27   wed-from1       06:00
    +    2006-11-02 04:13:28   wed-to1         23:00
    +    2006-11-02 04:13:36   windowopen-temp 12.0 (Celsius)
     
    -  2006-01-03 18:28:27   actuator        07%
    -  2006-01-03 18:26:32   mon-from1       06:00
    -  2006-01-03 18:26:33   mon-to1         23:00
    -  2006-01-03 18:26:34   tue-from1       06:00
    -  2006-01-03 18:26:34   tue-to1         23:00
    -  2006-01-03 18:26:35   wed-from1       06:00
    -  2006-01-03 18:26:35   wed-to1         23:00
    -  2006-01-03 18:26:37   thu-from1       06:00
    -  2006-01-03 18:26:37   thu-to1         23:00
    -  2006-01-03 18:26:38   fri-from1       06:00
    -  2006-01-03 18:26:38   fri-to1         23:00
    -  2006-01-03 18:26:39   sat-from1       06:00
    -  2006-01-03 18:26:40   sat-to1         23:50
    -  2006-01-03 18:26:41   sun-from1       06:00
    -  2006-01-03 18:26:41   sun-to1         23:00
    -  2006-01-03 18:26:45   mode            manual
    -  2006-01-03 18:26:44   desired-temp    21.5 (Celsius)
    -  2006-01-03 18:26:45   measured-temp   22.0 (Celsius)
    -  2006-01-03 18:26:46   state           Bat: ok, Window: closed
    -  2006-01-03 18:24:37   init            255
    -  2006-01-03 18:26:42   day-temp        21.0 (Celsius)
    -  2006-01-03 18:26:42   night-temp      17.0 (Celsius)
    -  2006-01-03 18:26:43   unknown_85      4
    -  2006-01-03 18:26:43   windowopen-temp 12.0 (Celsius)
       
    - -
- - - - -

logfile

-
    - logfile -

    - Specify the logfile to write. Specify this as the first command in the - configfile as everything gets logged in the logfile. You can use "-" for - stdout, in this case the server won't background itself.
    - The logfile name can also take wildcards for easier logfile rotation, - see the FileLog section of the define command. -

    - - Examples: -
      - logfile /var/log/fhem
      - logfile /var/log/fhem-%Y-%m.log -
    -
- - - -

modpath

-
    - modpath <path> -

    - Specify the path to the modules directory FHEM. The path - should not contain the directory FHEM. Every module there will - be loaded. -

    - - Example: -
      - modpath /usr/local/lib -
    -
- - - -

notifyon

-
    - notifyon <name> <command> -

    - Execute a command when received an event for the definition <name>. As with normal - commands, if it is enclosed in {}, then it is a perl expression, if it is - enclosed in "", then it is a shell command, else it is a "plain" fhem.pl - command (chain). See the trigger command for testing - it. - - Examples: -
      - notifyon btn3 set lamp %
      - notifyon btn3 { fhz "set lamp %" }
      - notifyon btn3 "/usr/local/bin/setlamp "%""
      - notifyon btn3 set lamp1 %;;set lamp2 %
      - notifyon wz:measured.* "/usr/local/bin/logfht @ "%""
      - notifyon .*H:.* {DbLog("@","%")}
      - notifyon UNDEFINED "send-me-mail.sh "%""
      -
    -
    - - Notes: -
      -
    • The character % will be replaced with the received event, - e.g. with on or off or measured-temp: 21.7 - (Celsius)
      It is advisable to put the % into double - quotes, else the shell may get a syntax error.
      - To use % or @ in the text itself, use the double mode (%% or @@)
    • - -
    • The character @ will be replaced with the device - name.
    • - -
    • <name> may also be a compound of - definition:event to filter for events.
    • - -
    • <name> is in fact a regexp. It must completely (!) - match either the device name, or the compound of the device name and the - event. The event is either the string you see in the list output in paranthesis after the device name, or the - string you see when you do a detailed list of the device.
    • - -
    • To use database logging, copy the file contrib/91_DbLog.p into your - modules directory, and change the $dbconn parameter in the file.
    • - -
    • Each undefined device (FS20, HMS, FHT) will be reported with the device - name "UNDEFINED". The % parameter will contain the type (FS20, HMS100T, - etc) and device number, separated by a space.
    • - -
    - -
- - -

pidfile

-
    - pidfile <filename> -

    - Write the process id of the perl process to the specified file. We are - running as a daemon, and some distributions would like to check by the pid if - we are still runnning. -

    - Example: -
      - pidfile /var/run/fhem.pid -
    -
- - - - -

port

-
    - port <number> [<global>] -

    - Listen on the TCP/IP port <number> for incoming - connections. To offer at least a little bit of security, the server will only - listen for connections from the localhost per default. The optional global - parameters enables listening for non-localhost connections too. -

    - Example: -
      - port 7072 -
@@ -816,20 +945,14 @@ split in multiple lines

- - -

savefile

+ +

save

    - savefile <filename> + save [<configfile>]

    - Set the filename where the state and at information will - be saved before shutdown. If not setting it, then no information will be - saved. -

    - Example: -
      - savefile /var/tmp/fhem.save -
    + Save first the statefile, then the + configfile information. If a parameter is specified, + it will be used instead the global configfile attribute.
@@ -838,8 +961,8 @@ split in multiple lines

@@ -936,13 +1061,13 @@ split in multiple lines

  • The time argument ranges from 0.25sec to 4 hours and 16 minutes. - As the time is encoded in one byte (there are only 112 distinct - values), the resolution gets coarse with larger values. The program + As the time is encoded in one byte there are only 112 distinct + values, the resolution gets coarse with larger values. The program will report the used timeout if the specified one cannot be set exactly. The resolution is 0.25 sec from 0 to 4 sec, 0.5 sec from 4 to 8 sec, 1 sec from 8 to 16 sec and so on. If you need better - precision for large values, use the at command - which has a 1 sec resolution.
  • + precision for large values, use at which has a 1 + sec resolution.
  • If the attribute follow-on-for-timer is set for the device and the on-for-timer command is sent to the device with a time parameter, the program automatically schedules a "setstate off" for the @@ -1003,7 +1128,7 @@ split in multiple lines

    refreshvalues Notes:
      -
    • All *-temp valuetypes need a temperature +
    • All *-temp values need a temperature as argument, which will be rounded to 0.5 Celsius
    • mode is one of auto, manual or holiday
    • The *-from1/*-from2/*-to1/*-to2 valuetypes need a time @@ -1014,9 +1139,6 @@ split in multiple lines

      plea to the FHT device, so it may send its parameters. If you want to get these values regularly, then schedule:
      at +*01:00:00 set <name> refreshvalues
      - My two FHT devices send the actuator, measured-temp and state - regularly, and everything else if it changes, so I do not schedule - this normally.
    • The FHT is very economical (or lazy), it receives messages from the FHZ1000 every 2 minutes (or so). Don't be surprized if your command @@ -1026,7 +1148,7 @@ split in multiple lines

    -

    Type FHT:

    +

    Type WS300:

      set WS300Device <interval(min.)> <height(m)> <rainvalume(ml)>

      @@ -1041,30 +1163,21 @@ split in multiple lines

        setstate <name> <value>

        - Set the "Last msg" for the definition - <name> shown in the list command + Set the "STATE" for <name> as shown in paranthesis in the + list command to <value> without sending any signals to the device - itself. This command is also used in the savefile. -
        - Setting/changing arbitrary comments is possible, if the value begins with - comment:. To delete the comment, set it with an empty value, - see below. + itself. This command is also used in the statefile.

        Examples:
               setstate lamp on
          -    setstate lamp comment:location sleeping room
          -    setstate lamp comment:location # deletes the comment

        Note:
          -
        • You even may set the detailed state (i.e. what you get when you call - list <name;> by specifying "<time> - <attribute> <value>". Take a look at the - savefile - after devices reported values for an example.
        • +
        • The statefile uses another version of this command, don't be surprised. +
      @@ -1074,7 +1187,7 @@ split in multiple lines

        shutdown

        - Shut down the server (after saving the state information + Shut down the server (after saving the state information )

        Example: @@ -1089,7 +1202,7 @@ split in multiple lines

          trigger <dev> <state>

          - Trigger a notifyon command. + Trigger a notify definition.

          Example:
            @@ -1107,7 +1220,7 @@ split in multiple lines

            Example:
              sleep 0.5
              - notifyon btn3 set lamp toggle;;sleep 0.5;;set lamp toggle + notify btn3 set lamp toggle;;sleep 0.5;;set lamp toggle

            Note: As the server is not multithreaded, everything is blocked for @@ -1115,44 +1228,25 @@ split in multiple lines

          - -

          verbose

          -
            - verbose <level> -

            - Set the verbosity level. Possible values: -
              -
            • 0 - it will only tell you when the server was started, or stopped
            • -
            • 1 - it logs all error messages or unknown packets
            • -
            • 2 - it logs all signals received or sent in a "digested" format,
            • -
            • 3 - it logs the signals for undefined devices,
            • -
            • 4 - it logs the TCP/IP connections and the called programs with parameters,
            • -
            • 5 - is for debugging.
            • -
            - Recommended level is 3 for normal use. -

            - Example: -
              - verbose 3 -
            -

          xmllist

            xmllist

            - Returns an XML tree of all definitions, all notifyon settings and all at + Returns an XML tree of all definitions, all notify settings and all at entries. It is not intended for human consumption.

            Example:
              FHZ> xmllist
            -    <FHZINFO>
            -	<FHZ_DEVICES>
            -	    <FHZ name="FHZ" definition="FHZ FHZ" state="fhtbuf: 1c">
            -                <STATE name="fhtbuf" value="23" measured="2006-02-12 14:03:39"/>
            -		<STATE name="serial" value="136e21bc" measured="2006-03-26 08:47:36"/>
            -    [...]
            +  <FHZINFO>
            +          <internal_LIST>
            +                  <internal name="global" state="internal" sets="" attrs="room configfile logfile modpath pidfilename port statefile userattr verbose version">
            +                          <INT key="DEF" value="<no definition>"/>
            +                          <INT key="NR" value="0"/>
            +                          <INT key="STATE" value="internal"/>
            +      [...]
            +
               
          @@ -1160,12 +1254,12 @@ split in multiple lines

          Perl specials

            -
          • To use fhz commands from the perl expression, use the function "fhz", - which takes a string argument, this string will be evaluated as an fhz +
          • To use fhem commands from the perl expression, use the function "fhem", + which takes a string argument, this string will be evaluated as an fhem command chain.
            Example:
              - notifyon piri:on { fhz "set light on" } + define n1 notify piri:on { fhem "set light on" }
          • @@ -1177,22 +1271,22 @@ split in multiple lines

            $wday == 6), and 0 otherwise. Example:
              - notifyon piri:on { if($hour > 18 || $hour < 5) { fhz "set light on" } } + define n2 notify piri:on { if($hour > 18 || $hour < 5) { fhem "set light on" } }
          • Note: do not forget to escape the semicolon (;) with two semicolons - (;;), else your perl code will be interpreted as an fhz command and you + (;;), else your perl code will be interpreted as an fhem command and you most certainly get syntax errors.
          • The current value (the string you see in paranthesis in the output of the - list command) is available in the value hash, to access it, + list command) is available in the value hash; to access it use $value{<devicename>}
            - If you need the old value (and time) of the currentliy triggered device, + If you need the old value (and time) of the currently triggered device, then you can access it with $oldvalue{$dev}{TIME} and $oldvalue{$dev}{VAL}.
          • @@ -1219,8 +1313,6 @@ split in multiple lines

            isday returns 1 if the sun is visible, and 0 else. - -
          diff --git a/fhem/docs/faq.html b/fhem/docs/faq.html index 089ff3425..412024e2f 100644 --- a/fhem/docs/faq.html +++ b/fhem/docs/faq.html @@ -116,7 +116,7 @@ Please add day/month/weekday to it. I defined my FS20STR as an FHT device, but I do not get any data from it.
            The FS20STR is an FS20 device, even if it looks like an FHT80b. - You'll get "only" on-for-timer and off-fot-timer events sent. + You'll get "only" on-for-timer and off-for-timer events sent.
          How to convert the FHT8b code seen in its display to the hex code needed by fhem.pl? diff --git a/fhem/docs/raw-codes b/fhem/docs/raw-codes index 333a5d6e3..5fb12fadb 100644 --- a/fhem/docs/raw-codes +++ b/fhem/docs/raw-codes @@ -14,3 +14,6 @@ where is one of: 810e04d70213a001b16d000000000000 RM100-2 smoke off 81xx04xx0101a00180c2030011 FS20 dev: 1234 button: 03 on (11) + +To send it: +set FHZ raw 04 0101a00180c2030011 diff --git a/fhem/examples/01_fs20 b/fhem/examples/01_fs20 index 3f31715a1..77405eccf 100644 --- a/fhem/examples/01_fs20 +++ b/fhem/examples/01_fs20 @@ -7,12 +7,12 @@ # # Common part -logfile /tmp/fhem-%Y-%m.log -savefile /tmp/fhem.save # where to save the state of the devices -verbose 3 # "normal" verbosity (min 1, max 5) -port 7072 # our TCP/IP port (working from localhost only) -modpath . # where our FHEM directory is +attr global logfile /tmp/fhem-%Y-%m.log +attr global statefile /tmp/fhem.save # where to save the state of the devices +attr global verbose 3 # "normal" verbosity (min 1, max 5) +attr global port 7072 # our TCP/IP port (localhost only) +attr global modpath . # where our FHEM directory is + define FHZ FHZ /dev/tts/USB0 # the serial port of an FHZ 1000 PC - define lamp FS20 8765 01 # type FS20, transmitter code 8765, button 2 diff --git a/fhem/examples/02_fs20 b/fhem/examples/02_fs20 index d0a374aef..6ebf8576a 100644 --- a/fhem/examples/02_fs20 +++ b/fhem/examples/02_fs20 @@ -9,13 +9,13 @@ # Common part -logfile /tmp/fhem-%Y-%m.log -savefile /tmp/fhem.save # where to save the state of the devices -verbose 3 # "normal" verbosity -port 7072 # our TCP/IP port (working from localhost only) -modpath . # where our FHEM directory is -define FHZ FHZ /dev/tts/USB0 # the serial port of an FHZ 1000 PC +attr global logfile /tmp/fhem-%Y-%m.log +attr global statefile /tmp/fhem.save # where to save the state of the devices +attr global verbose 3 # "normal" verbosity +attr global port 7072 # our TCP/IP port (localhost only) +attr global modpath . # where our FHEM directory is +define FHZ FHZ /dev/tts/USB0 # the serial port of an FHZ 1000 PC define roll1 FS20 7777 02 # type FS20, transmitter code 7777, button 3 define roll2 FS20 7777 03 # type FS20, transmitter code 7777, button 4 @@ -31,16 +31,16 @@ notifyon btn3 set roll1 %;; set roll2 % notifyon btn3 set roll1,roll2 % # Method 2a: perl. -notifyon btn3 { fhz "set roll1 %;; set roll2 %" } +notifyon btn3 { fhem "set roll1,roll2 %" } # Method 2b: perl. open the rollades only to a certain amount if they are # closed. Else do the required command. notifyon btn3 {\ if("%" eq "on" && $value{roll1} eq "off") {\ - fhz "set roll1 on-for-timer 10";;\ - fhz "set roll2 on-for-timer 16";;\ + fhem "set roll1 on-for-timer 10";;\ + fhem "set roll2 on-for-timer 16";;\ } else { \ - fhz "set roll1,roll2 %"\ + fhem "set roll1,roll2 %"\ } \ } @@ -57,16 +57,15 @@ quit # Ignore the rest of this file # # Note that for greater time values the FS20 timer gets inaccurate, you # can use something like -# $fhz 7072 "set roll1 on; at +00:00:21 set roll1 on" +# $fhem 7072 "set roll1 on; at +00:00:21 set roll1 on" # instead. # -fhz=/usr/local/bin/fhem.pl +fhem=/usr/local/bin/fhem.pl if test $1 = "on"; then - $fhz 7072 "set roll1 on-for-timer 10 - $fhz 7072 "set roll2 on-for-timer 16 + $fhem 7072 "set roll1 on-for-timer 10 + $fhem 7072 "set roll2 on-for-timer 16 else - $fhz 7072 "set roll1,roll2 off" + $fhem 7072 "set roll1,roll2 off" fi - diff --git a/fhem/examples/03_fht b/fhem/examples/03_fht index 096bbe93f..a202a5e73 100644 --- a/fhem/examples/03_fht +++ b/fhem/examples/03_fht @@ -8,11 +8,11 @@ # After about 5-10 minutes, check if "list wz" returns something meaningful # -logfile /tmp/fhem-%Y-%m.log -savefile /tmp/fhem.save # where to save the state of the devices -verbose 3 # "normal" verbosity -port 7072 # our TCP/IP port (working from localhost only) -modpath . # where our FHEM directory is +attr global logfile /tmp/fhem-%Y-%m.log +attr global statefile /tmp/fhem.save # where to save the state of the devices +attr global verbose 3 # "normal" verbosity +attr global port 7072 # our TCP/IP port (localhost only) +attr global modpath . # where our FHEM directory is define FHZ FHZ /dev/tts/USB0 # the serial port of an FHZ 1000 PC define wz FHT 3232 # type FHT, transmitter code 3232 (default value) @@ -20,11 +20,11 @@ define wz FHT 3232 # type FHT, transmitter code 3232 (default value) ######################### # Some documentation suggests that the FHZ time should be set every minute. # I only set it once a day. -at *03:30:00 set FHZ time +define fhz_timer at *03:30:00 set FHZ time ######################### # If you wish to have up-to date information on certain strange parameters # then comment out the line below. My devices send a message when a value # changes, and send measured-temp, actuator and state messages regularly. # Be patient: the reply comes in 5-10 minutes. -at *04:00:00 set wz refreshvalues +define wz_refresh at *04:00:00 set wz refreshvalues diff --git a/fhem/examples/04_log b/fhem/examples/04_log index 0bcb39257..10d98ab9a 100644 --- a/fhem/examples/04_log +++ b/fhem/examples/04_log @@ -4,11 +4,11 @@ # See the file fht.gnuplot for displaying the logged data (or webfrontend/pgm2) # -logfile /tmp/fhem-%Y-%m.log -savefile /tmp/fhem.save # where to save the state of the devices -verbose 3 # "normal" verbosity -port 7072 # our TCP/IP port (working from localhost only) -modpath . # where our FHEM directory is +attr global logfile /tmp/fhem-%Y-%m.log +attr global statefile /tmp/fhem.save # where to save the state of the devices +attr global verbose 3 # "normal" verbosity +attr global port 7072 # our TCP/IP port (localhost only) +attr global modpath . # where our FHEM directory is define FHZ FHZ /dev/tts/USB0 # the serial port of an FHZ 1000 PC define wz FHT 3232 # type FHT, transmitter code 3232 (default value) @@ -27,7 +27,7 @@ define avglog FileLog /var/log/avg.log ks1:.*avg.* # Alternative log method. It does the same, but it is somewhat slower as it # starts the shellscript below. Don't forget the "", as some values contain # paranthesis, and your shell will probably bark. -notifyon wz:temp.* "/usr/local/bin/log.sh @ "@ %"" +define tmplog notifyon wz:temp.* "/usr/local/bin/log.sh @ "@ %"" ############################## # If using the frontends pgm2 or pgm3, then you can put the devices diff --git a/fhem/examples/05_rm100 b/fhem/examples/05_rm100 index 14a06e8a2..2d2ccc7d9 100644 --- a/fhem/examples/05_rm100 +++ b/fhem/examples/05_rm100 @@ -5,19 +5,21 @@ # # As the RM100-2 changes its code after the battery is changed (or the switch # on the device itself is changed), we map _all_ RM100-2 to the device id 1001 -# Check the commandref.html define, Type HMS section for details +# if there is no definition for it. Check the commandref.html define, Type HMS +# section for details + +attr global logfile /tmp/fhem-%Y-%m.log +attr global statefile /tmp/fhem.save # where to save the state of the devices +attr global verbose 3 # "normal" verbosity (min 1, max 5) +attr global port 7072 # our TCP/IP port (localhost only) +attr global modpath . # where our FHEM directory is -logfile /tmp/fhem-%Y-%m.log -savefile /tmp/fhem.save # where to save the state of the devices -verbose 3 # "normal" verbosity (min 1, max 5) -port 7072 # our TCP/IP port (working from localhost only) -modpath . # where our FHEM directory is define FHZ FHZ /dev/tts/USB0 # the serial port of an FHZ 1000 PC define rm100 HMS 1001 # type HMS define rm100log FileLog /var/log/wz-%Y-%U.log rm100:.* -notifyon rm100:smoke.*on "wall "FIRE: @ %"" +define smokealarm notify rm100:smoke.*on "wall "FIRE: @ %"" # Test the log/notify # fhem.pl 7072 'trigger rm100 smoke on' diff --git a/fhem/examples/06_at b/fhem/examples/06_at index 7cf2659f4..f34f8ada6 100644 --- a/fhem/examples/06_at +++ b/fhem/examples/06_at @@ -3,37 +3,37 @@ ################################## # absolute ones: -at 17:00:00 set lamp on # fhz command -at 17:00:00 { Log 1, "Teetime" } # Perl command -at 17:00:00 "/bin/echo "Teetime" > /dev/console" # shell command -at *17:00:00 set lamp on # repeat every day +define a1 at 17:00:00 set lamp on # fhem command +define a2 at 17:00:00 { Log 1, "Teetime" } # Perl command +define a3 at 17:00:00 "/bin/echo "Teetime" > /dev/console" # shell command +define a4 at *17:00:00 set lamp on # repeat every day ################################## # relative ones -at +00:00:10 set lamp on # switch the lamp on in 10 seconds -at +00:00:02 set lamp on-for-timer 1 # Blink once in 2 seconds -at +*{3}00:00:02 set lamp on-for-timer 1 # Blink 3 times +define a5 at +00:00:10 set lamp on # switch the lamp on in 10 seconds +define a6 at +00:00:02 set lamp on-for-timer 1 # Blink once in 2 seconds +define a7 at +*{3}00:00:02 set lamp on-for-timer 1 # Blink 3 times ################################## # Switch the lamp on from sunset to 11 PM each day # You have to install 99_SUNRISE.pm in the FHEM directory to have sunset() # We have to use the relative versions, as the next event is computed now { sunrise_coord("8.686", "50.112", "Europe/Berlin") } -at +*{sunset_rel()} set lamp on -at *23:00:00 set lamp off +define a8 at +*{sunset_rel()} set lamp on +define a9 at *23:00:00 set lamp off ################################## # A more elegant solution, which even works if sunset is after 23:00 -at +*{sunset_rel()} set lamp on-till 23:00 +define a10 at +*{sunset_rel()} set lamp on-till 23:00 ################################## # Only do this on weekend. For the preset perl variables see commandref.html -at +*{sunset_rel()} { fhz("set lamp on-till 23:00") if($we) } +define a11 at +*{sunset_rel()} { fhem("set lamp on-till 23:00") if($we) } ################################## # Switch lamp1 and lamp2 on from 7:00 till 10 minutes after sunrise -at *07:00 set lamp1,lamp2 on-till {sunrise_abs(+600)} +define a12 at *07:00 set lamp1,lamp2 on-till {sunrise_abs(+600)} ################################## # Blink 3 times if the piri sends a command -notify piri:on.* at +*{3}00:00:02 set lamp on-for-timer 1 +define n1 notify piri:on.* define a13 at +*{3}00:00:02 set lamp on-for-timer 1 diff --git a/fhem/fhem.pl b/fhem/fhem.pl index 64454655d..6990c9225 100755 --- a/fhem/fhem.pl +++ b/fhem/fhem.pl @@ -1,7 +1,5 @@ #!/usr/bin/perl -my $version = "=VERS= from =DATE="; - ################################################################ # # Copyright notice @@ -42,76 +40,87 @@ use Time::HiRes qw(gettimeofday); ################################################## # Forward declarations # -sub AnalyzeInput($); sub AnalyzeCommand($$); sub AnalyzeCommandChain($$); -sub IOWrite($@); +sub AnalyzeInput($); sub AssignIoPort($); -sub InternalTimer($$$); -sub fhz($); +sub CallFn(@); sub CommandChain($$); sub DoClose($); +sub GetLogLevel(@); sub HandleTimeout(); +sub IOWrite($@); +sub InternalTimer($$$); sub Log($$); sub OpenLogfile($); sub ResolveDateWildcards($@); +sub SemicolonEscape($); sub SignalHandling(); sub TimeNow(); -sub DoSavefile(); -sub SemicolonEscape($); +sub WriteStatefile(); sub XmlEscape($); +sub fhem($); -sub CommandAt($$); sub CommandAttr($$); sub CommandDefAttr($$); sub CommandDefine($$); +sub CommandDelAttr($$); sub CommandDelete($$); -sub CommandFhzDev($$); sub CommandGet($$); sub CommandHelp($$); sub CommandInclude($$); sub CommandInform($$); sub CommandList($$); -sub CommandLogfile($$); -sub CommandModpath($$); -sub CommandNotifyon($$); -sub CommandPidfile($$); -sub CommandPort($$); sub CommandRereadCfg($$); sub CommandQuit($$); -sub CommandSavefile($$); +sub CommandSave($$); sub CommandSet($$); sub CommandSetstate($$); sub CommandSleep($$); sub CommandShutdown($$); -sub CommandVerbose($$); sub CommandXmlList($$); sub CommandTrigger($$); ################################################## # Variables: # global, to be able to access them from modules + +#Special values in %modules (used if set): +# DefFn - define a "device" of this type +# UndefFn - clean up at delete +# ParseFn - Interpret a raw message +# ListFn - details for this "device" +# SetFn - set/activate this device +# GetFn - get some data from this device +# StateFn - set local info for this device, do not activate anything +# TimeFn - if the TRIGGERTIME of a device is reached, call this function +# NotifyFn - call this if some device changed its properties +# ReadFn - Reading from a filedescriptor (see FHZ/WS300) + +#Special values in %defs: +# TYPE - The name of the module it belongs to +# STATE - Oneliner describing its state +# NR - its "serial" number +# DEF - its definition +# READINGS- The readings. Each value has a "VAL" and a "TIME" component. +# FD - FileDescriptor. If set, it will be integrated into the global select +# IODev - attached to io device +# CHANGED - Currently changed attributes of this device. Used by NotifyFn +# VOLATILE- Set if the definition should be saved to the "statefile" + +use vars qw(%modules); # List of loaded modules (device/log/etc) use vars qw(%defs); # FHEM device/button definitions -use vars qw(%logs); # Log channels use vars qw(%attr); # Attributes + use vars qw(%value); # Current values, see commandref.html use vars qw(%oldvalue); # Old values, see commandref.html -use vars qw(%devmods); # List of loaded device modules - -my %ntfy; -my %at; +use vars qw($nextat); # used by the at module my $server; # Server socket -my $verbose = 0; -my $logfile; # logfile name, if its "-" then wont background my $currlogfile; # logfile, without wildcards -my $logopened; +my $logopened = 0; # logfile opened or using stdout my %client; # Client array -my %logmods; # List of loaded logger modules -my $savefile = ""; # Save ste info and at cmd's here -my $nextat; my $rcvdquit; # Used for quit handling in init files -my $configfile=$ARGV[0]; my $sig_term = 0; # if set to 1, terminate (saving the state) my $modpath_set; # Check if modpath was used, and report if not. my $global_cl; # To use from perl snippets @@ -120,22 +129,36 @@ my %defattr; # Default attributes my %intAt; # Internal at timer hash. my $intAtCnt=0; my $init_done = 0; -my $pidfilename; +my $AttrList = "room"; +$modules{Internal}{ORDER} = -1; +$modules{Internal}{AttrList} = "configfile logfile modpath " . + "pidfilename port statefile userattr verbose:1,2,3,4,5 version"; + +$defs{global}{NR} = $devcount++; +$defs{global}{TYPE} = "Internal"; +$defs{global}{STATE} = "Internal"; +$defs{global}{DEF} = ""; + +CommandAttr(undef, "global verbose 3"); +CommandAttr(undef, "global configfile $ARGV[0]"); +CommandAttr(undef, "global logfile -"); +CommandAttr(undef, "global version =VERS= from =DATE="); + my %cmds = ( "?" => { Fn=>"CommandHelp", Hlp=>",get this help" }, - "at" => { Fn=>"CommandAt", - Hlp=>" ,issue a command at a given time" }, - "attr" => { Fn=>"CommandAttr", - Hlp=>" ,set attributes for " }, + "attr" => { Fn=>"CommandAttr", + Hlp=>" [],set attributes for " }, "defattr" => { Fn=>"CommandDefAttr", Hlp=>" ,set attr for following definitions" }, "define" => { Fn=>"CommandDefine", - Hlp=>" ,define a code" }, + Hlp=>" ,define a device/at/notifyon entity" }, + "delattr" => { Fn=>"CommandDelAttr", + Hlp=>" [],delete attribute for " }, "delete" => { Fn=>"CommandDelete", - Hlp=>"{def|ntfy|at} name,delete the corresponding definition"}, + Hlp=>"name,delete the corresponding definition"}, "get" => { Fn=>"CommandGet", Hlp=>" ,request data from " }, "help" => { Fn=>"CommandHelp", @@ -146,24 +169,14 @@ my %cmds = ( Hlp=>"{on|off},echo all commands and events to this client" }, "list" => { Fn=>"CommandList", Hlp=>"[device],list definitions and status info" }, - "logfile" => { Fn=>"CommandLogfile", - Hlp=>"filename,use - for stdout" }, - "modpath" => { Fn=>"CommandModpath", - Hlp=>",the directory where the FHEM subdir is" }, - "notifyon"=> { Fn=>"CommandNotifyon", - Hlp=>" ,exec when recvd signal for " }, - "pidfile" => { Fn=>"CommandPidfile", - Hlp=>"filename,write the process id into the pidfile" }, - "port" => { Fn=>"CommandPort", - Hlp=>" [global],TCP/IP port for the server" }, "quit" => { Fn=>"CommandQuit", Hlp=>",end the client session" }, "reload" => { Fn=>"CommandReload", Hlp=>",reload the given module (e.g. 99_PRIV)" }, "rereadcfg" => { Fn=>"CommandRereadCfg", Hlp=>",reread the config file" }, - "savefile"=> { Fn=>"CommandSavefile", - Hlp=>",on shutdown save all states and at entries" }, + "save" => { Fn=>"CommandSave", + Hlp=>"[configfile],write the configfile and the statefile" }, "set" => { Fn=>"CommandSet", Hlp=>" ,transmit code for " }, "setstate"=> { Fn=>"CommandSetstate", @@ -174,8 +187,6 @@ my %cmds = ( Hlp=>",sleep for usecs" }, "trigger" => { Fn=>"CommandTrigger", Hlp=>" ,trigger notify command" }, - "verbose" => { Fn=>"CommandVerbose", - Hlp=>",verbosity level, 0-5" }, "xmllist" => { Fn=>"CommandXmlList", Hlp=>",list definitions and status info as xml" }, ); @@ -201,39 +212,47 @@ if(int(@ARGV) == 2) { $server = IO::Socket::INET->new(PeerAddr => $addr); die "Can't connect to $addr\n" if(!$server); syswrite($server, "$ARGV[1] ; quit\n"); - my $err = 0; while(sysread($server, $buf, 256) > 0) { print($buf); - $err = 1; } - exit($err); + exit(0); } +# End of client code +################################################### -my $ret = CommandInclude(undef, $configfile); +my $ret = CommandInclude(undef, $attr{global}{configfile}); die($ret) if($ret); -if($logfile ne "-") { +# Go to background if the logfile is a real file (not stdout) +if($attr{global}{logfile} ne "-") { defined(my $pid = fork) || die "Can't fork: $!"; exit(0) if $pid; } die("No modpath specified in the configfile.\n") if(!$modpath_set); +die("No port specified in the configfile.\n") if(!$server); -if($savefile && -r $savefile) { - $ret = CommandInclude(undef, $savefile); +if($attr{global}{statefile} && -r $attr{global}{statefile}) { + $ret = CommandInclude(undef, $attr{global}{statefile}); die($ret) if($ret); } SignalHandling(); - -Log 0, "Server started (version $version, pid $$)"; +Log 0, "Server started (version $attr{global}{version}, pid $$)"; ################################################ # Main loop $init_done = 1; -CommandPidfile(undef, $pidfilename) if($pidfilename); +my $pfn = $attr{global}{pidfilename}; +if($pfn) { + return "$pfn: $!" if(!open(PID, ">$pfn")); + print PID $$ . "\n"; + close(PID); +} + +# Main Loop while (1) { my ($rout, $rin) = ('', ''); @@ -258,9 +277,7 @@ while (1) { # Message from the hardware (FHZ1000/WS3000/etc) foreach my $p (keys %defs) { next if(!$defs{$p}{FD} || !vec($rout, $defs{$p}{FD}, 1)); - no strict "refs"; - &{$devmods{$defs{$p}{TYPE}}{ReadFn}}($defs{$p}); - use strict "refs"; + CallFn($p, "ReadFn", $defs{$p}); } if(vec($rout, $server->fileno(), 1)) { @@ -297,6 +314,9 @@ while (1) { } } +################################################ +#Functions ahead, no more "plain" code + ################################################ sub IsDummy($) @@ -309,13 +329,13 @@ IsDummy($) ################################################ sub -GetLogLevel($) +GetLogLevel(@) { - my $dev = shift; + my ($dev,$deflev) = @_; return $attr{$dev}{loglevel} if(defined($attr{$dev}) && defined($attr{$dev}{loglevel})); - return 2; + return defined($deflev) ? $deflev : 2; } @@ -325,16 +345,17 @@ Log($$) { my ($loglevel, $text) = @_; - return if($loglevel > $verbose); + return if($loglevel > $attr{global}{verbose}); my @t = localtime; - my $nfile = ResolveDateWildcards($logfile, @t); + my $nfile = ResolveDateWildcards($attr{global}{logfile}, @t); OpenLogfile($nfile) if($currlogfile && $currlogfile ne $nfile); + my $tim = sprintf("%04d.%02d.%02d %02d:%02d:%02d", $t[5]+1900,$t[4]+1,$t[3], $t[2],$t[1],$t[0]); # my ($seconds, $microseconds) = gettimeofday(); -# $tim = sprintf("%04d.%02d.%02d %02d:%02d:%02d.%03d", +# my $tim = sprintf("%04d.%02d.%02d %02d:%02d:%02d.%03d", # $t[5]+1900,$t[4]+1,$t[3], $t[2],$t[1],$t[0], $microseconds/1000); if($logopened) { @@ -371,7 +392,7 @@ IOWrite($@) } no strict "refs"; - &{$devmods{$iohash->{TYPE}}{WriteFn}}($iohash, @a); + &{$modules{$iohash->{TYPE}}{WriteFn}}($iohash, @a); use strict "refs"; } @@ -396,6 +417,7 @@ AnalyzeInput($) } ##################################### +# i.e. split a line by ; (escape ;;), and execute each sub AnalyzeCommandChain($$) { @@ -409,15 +431,6 @@ AnalyzeCommandChain($$) } } -##################################### -# Used from perl oneliners inside of scripts -sub -fhz($) -{ - my $param = shift; - return AnalyzeCommandChain($global_cl, $param); -} - ##################################### sub AnalyzeCommand($$) @@ -461,7 +474,6 @@ AnalyzeCommand($$) $cmd =~ s/^[ \t]*//; my ($fn, $param) = split("[ \t][ \t]*", $cmd, 2); - return if(!$fn); ############# @@ -484,10 +496,12 @@ AnalyzeCommand($$) } return; } + $param = "" if(!defined($param)); no strict "refs"; my $ret = &{$cmds{$fn}{Fn} }($cl, $param); use strict "refs"; + if($ret) { if($cl) { syswrite($client{$cl}{fd}, $ret . "\n"); @@ -517,6 +531,7 @@ CommandHelp($$) return $str; } +##################################### sub CommandInclude($$) { @@ -541,28 +556,6 @@ CommandInclude($$) return undef; } -##################################### -sub -CommandPort($$) -{ - my ($cl, $arg) = @_; - - my ($port, $global) = split(" ", $arg); - if($global && $global ne "global") { - return "Bad syntax, usage: port [global]"; - } - - close($server) if($server); - $server = IO::Socket::INET->new( - Proto => 'tcp', - LocalHost => ($global ? undef : "localhost"), - LocalPort => $port, - Listen => 10, - ReuseAddr => 1); - - die "Can't open server port at $port\n" if(!$server); - return undef; -} ##################################### sub @@ -570,7 +563,7 @@ OpenLogfile($) { my $param = shift; - close(LOG) if($logfile); + close(LOG); $logopened=0; $currlogfile = $param; if($currlogfile eq "-") { @@ -597,34 +590,6 @@ OpenLogfile($) return undef; } -##################################### -sub -CommandLogfile($$) -{ - my ($cl, $param) = @_; - - $logfile = $param; - - my @t = localtime; - my $ret = OpenLogfile(ResolveDateWildcards($param, @t)); - die($ret) if($ret); - return undef; -} - - - -##################################### -sub -CommandVerbose($$) -{ - my ($cl, $param) = @_; - if($param =~ m/^[0-5]$/) { - $verbose = $param; - return undef; - } else { - return "Valid value for verbose are 0,1,2,3,4,5"; - } -} ##################################### sub @@ -632,26 +597,21 @@ CommandRereadCfg($$) { my ($cl, $param) = @_; - return "RereadCfg: No parameters are accepted" if($param); - DoSavefile(); + WriteStatefile(); foreach my $d (keys %defs) { - no strict "refs"; - my $ret = &{$devmods{$defs{$d}{TYPE}}{UndefFn}}($defs{$d}, $d); - use strict "refs"; + my $ret = CallFn($d, "UndefFn", $defs{$d}, $d); return $ret if($ret); } %defs = (); - %logs = (); %attr = (); - %ntfy = (); - %at = (); my $ret; - $ret = CommandInclude($cl, $configfile); + $ret = CommandInclude($cl, $attr{global}{configfile}); return $ret if($ret); - $ret = CommandInclude($cl, $savefile) if($savefile); + $ret = CommandInclude($cl, $attr{global}{statefile}) + if($attr{global}{statefile} && -r $attr{global}{statefile}); return $ret; } @@ -673,69 +633,109 @@ CommandQuit($$) ##################################### sub -DoSavefile() +WriteStatefile() { - return if(!$savefile); - if(!open(SFH, ">$savefile")) { - Log 1, "Cannot open $savefile: $!"; - return; + return if(!$attr{global}{statefile}); + if(!open(SFH, ">$attr{global}{statefile}")) { + my $msg = "Cannot open $attr{global}{statefile}: $!"; + Log 1, $msg; + return $msg; } my $t = localtime; print SFH "#$t\n"; foreach my $d (sort keys %defs) { - my $t = $defs{$d}{TYPE}; + print SFH "define $d $defs{$d}{TYPE} $defs{$d}{DEF}\n" + if($defs{$d}{VOLATILE}); print SFH "setstate $d $defs{$d}{STATE}\n" - if($defs{$d}{STATE} && $defs{$d}{STATE} ne "unknown"); + if($defs{$d}{STATE} && $defs{$d}{STATE} ne "unknown"); ############# # Now the detailed list - no strict "refs"; - my $str = &{$devmods{$defs{$d}{TYPE}}{ListFn}}($defs{$d}); - use strict "refs"; - next if($str =~ m/^No information about/); - - foreach my $l (split("\n", $str)) { - print SFH "setstate $d $l\n" + my $r = $defs{$d}{READINGS}; + if($r) { + foreach my $c (sort keys %{$r}) { + print SFH "setstate $d $r->{$c}{TIME} $c $r->{$c}{VAL}\n"; + } } - - } - - foreach my $t (sort keys %at) { - # $t =~ s/_/ /g; # Why is this here? - print SFH "at $t\n"; } close(SFH); } +##################################### +sub +CommandSave($$) +{ + my ($cl, $param) = @_; + my $ret = WriteStatefile(); + + $param = $attr{global}{configfile} if(!$param); + return "No configfile attribute set and no argument specified" if(!$param); + if(!open(SFH, ">$param")) { + return "Cannot open $param: $!"; + } + + # Sort the devices by room + my (%rooms, %savefirst); + foreach my $d (sort keys %defs) { + next if($d eq "global"); + my $r = ($attr{$d} && $attr{$d}{room}) ? $attr{$d}{room} : "~"; + $rooms{$r}{$d} = 1; + $savefirst{$d} = $r if($attr{$d} && $attr{$d}{savefirst}); + } + + # First the global definitions + my $t = localtime; + print SFH "#$t\n\n"; + print SFH "attr global userattr $attr{global}{userattr}\n" + if($attr{global}{userattr}); + foreach my $a (sort keys %{$attr{global}}) { + next if($a eq "configfile" || $a eq "version" || $a eq "userattr"); + print SFH "attr global $a $attr{global}{$a}\n"; + } + print SFH "\n"; + + # then the "important" ones (FHZ, WS300Device) + foreach my $d (sort keys %savefirst) { + my $r = $savefirst{$d}; + delete $rooms{$r}{$d}; + delete $rooms{$r} if(int(%{$rooms{$r}}) == 0); + print SFH "define $d $defs{$d}{TYPE} $defs{$d}{DEF}\n"; + foreach my $a (sort keys %{$attr{$d}}) { + next if($a eq "savefirst"); + print SFH "attr $d $a $attr{$d}{$a}\n"; + } + } + + foreach my $r (sort keys %rooms) { + print SFH "\ndefattr" . ($r ne "~" ? " room $r" : "") . "\n"; + foreach my $d (sort keys %{$rooms{$r}} ) { + next if($defs{$d}{VOLATILE}); + print SFH "define $d $defs{$d}{TYPE} $defs{$d}{DEF}\n"; + foreach my $a (sort keys %{$attr{$d}}) { + next if($a eq "room"); + print SFH "attr $d $a $attr{$d}{$a}\n"; + } + } + } + + close(SFH); + return undef; +} + ##################################### sub CommandShutdown($$) { my ($cl, $param) = @_; Log 0, "Server shutdown"; - DoSavefile(); - unlink($pidfilename) if($pidfilename); + WriteStatefile(); + unlink($attr{global}{pidfilename}) if($attr{global}{pidfilename}); exit(0); } -##################################### -sub -CommandNotifyon($$) -{ - my ($cl, $param) = @_; - - my @a = split("[ \t]", $param, 2); - - # Checking for misleading regexps - eval { "Hallo" =~ m/^$a[0]$/ }; - return "Bad regexp: $@" if($@); - - $ntfy{$a[0]} = SemicolonEscape($a[1]); - return undef; -} ##################################### sub @@ -744,11 +744,8 @@ DoSet(@) my @a = @_; my $dev = $a[0]; - my $ret; - no strict "refs"; - $ret = &{$devmods{$defs{$dev}{TYPE}}{SetFn}}($defs{$dev}, @a); - use strict "refs"; - + return "No set implemented for $dev" if(!$modules{$defs{$dev}{TYPE}}{SetFn}); + my $ret = CallFn($dev, "SetFn", $defs{$dev}, @a); return $ret if($ret); shift @a; @@ -761,7 +758,9 @@ CommandSet($$) { my ($cl, $param) = @_; my @a = split("[ \t][ \t]*", $param); - return "Usage: set " if(int(@a) < 1); + return "Usage: set \n" . + " can be an enumeration (separated by comma)\n" . + " or a range (separated by -)" if(int(@a)<1); my $dev = $a[0]; my @rets; @@ -809,18 +808,13 @@ CommandGet($$) return "Usage: get " if(int(@a) < 1); my $dev = $a[0]; return "Please define $dev first ($param)" if(!defined($defs{$dev})); + return "No get implemented for $dev" if(!$modules{$defs{$dev}{TYPE}}{GetFn}); - ######################## - # Type specific set - my $ret; - no strict "refs"; - $ret = &{$devmods{$defs{$a[0]}{TYPE}}{GetFn}}($defs{$dev}, @a); - use strict "refs"; - - return $ret; + return CallFn($a[0], "GetFn", $defs{$dev}, @a); } ##################################### +# Parse a timespec: Either HH:MM:SS or HH:MM or { perfunc() } sub GetTimeSpec($) { @@ -840,7 +834,7 @@ GetTimeSpec($) ($hr, $min, $sec) = ($1, $2, 0); } else { $tspec = "" if(!$tspec); - return ("the at function must return a timespec HH:MM:SS and not $tspec.", + return ("the at function \"$fn\" must return a timespec and not $tspec.", undef, undef, undef, undef); } } else { @@ -850,90 +844,45 @@ GetTimeSpec($) return (undef, $hr, $min, $sec, $fn); } -##################################### -sub -CommandAt($$) -{ - my ($cl, $def) = @_; - my ($tm, $command) = split("[ \t]+", $def, 2); - - return "Usage: at " if(!$command); - return "Wrong timespec, use \"[+][*[{count}]]