diff --git a/fhem/FHEM/70_WS3600.pm b/fhem/FHEM/70_WS3600.pm index d50769f0a..2777f0ef4 100644 --- a/fhem/FHEM/70_WS3600.pm +++ b/fhem/FHEM/70_WS3600.pm @@ -21,7 +21,7 @@ # GNU General Public License for more details. # # This copyright notice MUST APPEAR in all copies of the script! -############################################### +################################################################ package main; ########################### # 70_WS3600.pm @@ -31,119 +31,139 @@ package main; # Based in part on work for FHEM by other authors ... # $Id$ ########################### - +# 15.06.2013 Josch (Josch at abwesend dot de) some debugging +# 25.06.2013 Josch combined Date/Time-Records supported +# 12.07.2013 Josch documentation reworked +# 15.07.2013 Josch state handling improved (shows conn problems) +# 16.08.2013 Josch Logging improved: Level relative to verbosity level +# 27.08.2013 Josch Change to Log3, loglevel removed use strict; use warnings; #use Device::SerialPort; -my %sets = ( - "cmd" => "", - "freq" => "", -); - +# all records except Time- and Date/Time-Records my %TranslatedCodes = ( - "Date" => "Date", - "Time" => "Time", - "Ti" => "Temp-inside", - "Timin" => "Temp-inside-min", - "Timax" => "Temp-inside-max", - "TTimin" => "Temp-inside-min-Time", - "DTimin" => "Temp-inside-min-Date", - "TTimax" => "Temp-inside-max-Time", - "DTimax" => "Temp-inside-max-Date", - "To" => "Temp-outside", - "Tomin" => "Temp-outside-min", - "Tomax" => "Temp-outside-max", - "TTomin" => "Temp-outside-min-Time", - "DTomin" => "Temp-outside-min-Date", - "TTomax" => "Temp-outside-max-Time", - "DTomax" => "Temp-outside-max-Date", - "DP" => "Dew-Point", - "DPmin" => "Dew-Point-min", - "DPmax" => "Dew-Point-max", - "TDPmin" => "Dew-Point-min-Time", - "DDPmin" => "Dew-Point-min-Date", - "TDPmax" => "Dew-Point-min-Time", - "DDPmax" => "Dew-Point-min-Date", - "RHi" => "rel-Humidity-inside", - "RHimin" => "rel-Humidity-inside-min", - "RHimax" => "rel-Humidity-inside-max", - "TRHimin" => "rel-Humidity-inside-min-Time", - "DRHimin" => "rel-Humidity-inside-min-Date", - "TRHimax" => "rel-Humidity-inside-max-Time", - "DRHimax" => "rel-Humidity-inside-max-Date", - "RHo" => "rel-Humidity-outside", - "RHomin" => "rel-Humidity-outside-min", - "RHomax" => "rel-Humidity-outside-max", - "TRHomin" => "rel-Humidity-outside-min-Time", - "DRHomin" => "rel-Humidity-outside-min-Date", - "TRHomax" => "rel-Humidity-outside-max-Time", - "DRHomax" => "rel-Humidity-outside-max-Date", - "WS" => "Wind-Speed", + "Date" => "DTime", + "Ti" => "Temp-inside", + "Timin" => "Temp-inside-min", + "Timax" => "Temp-inside-max", + "DTimin" => "Temp-inside-min-DTime", + "DTimax" => "Temp-inside-max-DTime", + "To" => "Temp-outside", + "Tomin" => "Temp-outside-min", + "Tomax" => "Temp-outside-max", + "DTomin" => "Temp-outside-min-DTime", + "DTomax" => "Temp-outside-max-DTime", + "DP" => "Dew-Point", + "DPmin" => "Dew-Point-min", + "DPmax" => "Dew-Point-max", + "DDPmin" => "Dew-Point-min-DTime", + "DDPmax" => "Dew-Point-min-DTime", + "RHi" => "rel-Humidity-inside", + "RHimin" => "rel-Humidity-inside-min", + "RHimax" => "rel-Humidity-inside-max", + "DRHimin" => "rel-Humidity-inside-min-DTime", + "DRHimax" => "rel-Humidity-inside-max-DTime", + "RHo" => "rel-Humidity-outside", + "RHomin" => "rel-Humidity-outside-min", + "RHomax" => "rel-Humidity-outside-max", + "DRHomin" => "rel-Humidity-outside-min-DTime", + "DRHomax" => "rel-Humidity-outside-max-DTime", + "WS" => "Wind-Speed", "DIRtext" => "Wind-Direction-Text", - "DIR0" => "Wind-DIR0", - "DIR1" => "Wind-DIR1", - "DIR2" => "Wind-DIR2", - "DIR3" => "Wind-DIR3", - "DIR4" => "Wind-DIR4", - "DIR5" => "Wind-DIR5", - "WC" => "Wind-Chill", - "WCmin" => "Wind-Chill-min", - "WCmax" => "Wind-Chill-max", - "TWCmin" => "Wind-Chill-min-Time", - "DWCmin" => "Wind-Chill-min-Date", - "TWCmax" => "Wind-Chill-max-Time", - "DWCmax" => "Wind-Chill-max-Date", - "WSmin" => "Wind-Speed-min", - "WSmax" => "Wind-Speed-max", - "TWSmin" => "Wind-Speed-min-Time", - "DWSmin" => "Wind-Speed-min-Date", - "TWSmax" => "Wind-Speed-max-Time", - "DWSmax" => "Wind-Speed-max-Date", - "R1h" => "Rain-1h", - "R1hmax" => "Rain-1h-hmax", - "TR1hmax" => "Rain-1h-hmax-Time", - "DR1hmax" => "Rain-1h-hmax-Date", - "R24h" => "Rain-24h", + "DIR0" => "Wind-DIR0", + "DIR1" => "Wind-DIR1", + "DIR2" => "Wind-DIR2", + "DIR3" => "Wind-DIR3", + "DIR4" => "Wind-DIR4", + "DIR5" => "Wind-DIR5", + "WC" => "Wind-Chill", + "WCmin" => "Wind-Chill-min", + "WCmax" => "Wind-Chill-max", + "DWCmin" => "Wind-Chill-min-DTime", + "DWCmax" => "Wind-Chill-max-DTime", + "WSmin" => "Wind-Speed-min", + "WSmax" => "Wind-Speed-max", + "DWSmin" => "Wind-Speed-min-DTime", + "DWSmax" => "Wind-Speed-max-DTime", + "R1h" => "Rain-1h", + "R1hmax" => "Rain-1h-hmax", + "DR1hmax" => "Rain-1h-hmax-DTime", + "R24h" => "Rain-24h", "R24hmax" => "Rain-24-hmax", - "TR24hmax" => "Rain-24h-max-Time", - "DR24hmax" => "Rain-24h-max-Date", - "R1w" => "Rain-1w", - "R1wmax" => "Rain-1w-max", - "TR1wmax" => "Rain-1w-max-Time", - "DR1wmax" => "Rain-1w-max-Date", - "R1m" => "Rain-1M", - "R1mmax" => "Rain-1M-max", - "TR1mmax" => "Rain-1M-max-Time", - "DR1mmax" => "Rain-1M-max-Date", - "Rtot" => "Rain-total", - "TRtot" => "Rain-total-Time", - "DRtot" => "Rain-total-Date", - "RP" => "rel-Pressure", - "AP" => "abs-Pressure", - "RPmin" => "rel-Pressure-min", - "RPmax" => "rel-Pressure-max", - "TRPmin" => "rel-Pressure-min-Time", - "DRPmin" => "rel-Pressure-min-Date", - "TRPmax" => "rel-Pressure-max-Time", - "DRPmax" => "rel-Pressure-max-Date", - "Tendency" => "Tendency", - "Forecast" => "Forecast", + "DR24hmax"=> "Rain-24h-max-DTime", + "R1w" => "Rain-1w", + "R1wmax" => "Rain-1w-max", + "DR1wmax" => "Rain-1w-max-DTime", + "R1m" => "Rain-1M", + "R1mmax" => "Rain-1M-max", + "DR1mmax" => "Rain-1M-max-DTime", + "Rtot" => "Rain-total", + "DRtot" => "Rain-total-DTime", + "RP" => "rel-Pressure", + "AP" => "abs-Pressure", + "RPmin" => "rel-Pressure-min", + "RPmax" => "rel-Pressure-max", + "DRPmin" => "rel-Pressure-min-DTime", + "DRPmax" => "rel-Pressure-max-DTime", + "Tendency"=> "Tendency", + "Forecast"=> "Forecast", +#added for WS-0101 / WS-1080 + "WG" => "Wind-Gust", + "DIR" => "Wind-Dir", + "state" => "State", ); -my %WantedCodesForStatus = ( - "Ti" => "Ti:", - "To" => "T:", - "DP" => "DP:", - "RHi" => "Hi:", - "RHo" => "H:", - "WS" => "W:", - "DIRtext" => "Dir:", - "WC" => "WC:", - "R1h" => "R:", - "RP" => "P:", - "Tendency" => "Tendency:", - "Forecast" => "Forecast:", +# Date/Time-Records +my %TranslatedDateTimeCodes = ( + "DTime" => "DTime", + "DTTimin" => "Temp-inside-min-DTime", + "DTTimax" => "Temp-inside-max-DTime", + "DTTomin" => "Temp-outside-min-DTime", + "DTTomax" => "Temp-outside-max-DTime", + "DTDPmin" => "Dew-Point-min-DTime", + "DTDPmax" => "Dew-Point-min-DTime", + "DTRHimin" => "rel-Humidity-inside-min-DTime", + "DTRHimax" => "rel-Humidity-inside-max-DTime", + "DTRHomin" => "rel-Humidity-outside-min-DTime", + "DTRHomax" => "rel-Humidity-outside-max-DTime", + "DTWCmin" => "Wind-Chill-min-DTime", + "DTWCmax" => "Wind-Chill-max-DTime", + "DTWSmin" => "Wind-Speed-min-DTime", + "DTWSmax" => "Wind-Speed-max-DTime", + "DTR1hmax" => "Rain-1h-hmax-DTime", + "DTR24hmax"=> "Rain-24h-max-DTime", + "DTR1wmax" => "Rain-1w-max-DTime", + "DTR1mmax" => "Rain-1M-max-DTime", + "DTRtot" => "Rain-total-DTime", + "DTRPmin" => "rel-Pressure-min-DTime", + "DTRPmax" => "rel-Pressure-max-DTime", +); + +# Time-Records (will be appended to Date-Record) +my %TranslatedTimeCodes = ( + "Time" => "DTime", + "TTimin" => "Temp-inside-min-DTime", + "TTimax" => "Temp-inside-max-DTime", + "TTomin" => "Temp-outside-min-DTime", + "TTomax" => "Temp-outside-max-DTime", + "TDPmin" => "Dew-Point-min-DTime", + "TDPmax" => "Dew-Point-min-DTime", + "TRHimin" => "rel-Humidity-inside-min-DTime", + "TRHimax" => "rel-Humidity-inside-max-DTime", + "TRHomin" => "rel-Humidity-outside-min-DTime", + "TRHomax" => "rel-Humidity-outside-max-DTime", + "TWCmin" => "Wind-Chill-min-DTime", + "TWCmax" => "Wind-Chill-max-DTime", + "TWSmin" => "Wind-Speed-min-DTime", + "TWSmax" => "Wind-Speed-max-DTime", + "TR1hmax" => "Rain-1h-hmax-DTime", + "TR24hmax"=> "Rain-24h-max-DTime", + "TR1wmax" => "Rain-1w-max-DTime", + "TR1mmax" => "Rain-1M-max-DTime", + "TRtot" => "Rain-total-DTime", + "TRPmin" => "rel-Pressure-min-DTime", + "TRPmax" => "rel-Pressure-max-DTime", ); ##################################### @@ -154,8 +174,8 @@ WS3600_Initialize($) # Consumer $hash->{DefFn} = "WS3600_Define"; - $hash->{AttrList}= "model:WS3600,WS2300 loglevel:0,1,2,3,4,5,6"; - $hash->{ReadFn} = "WS3600_Read"; + $hash->{AttrList}= "model:WS3600,WS2300,WS1080"; +# $hash->{ReadFn} = "WS3600_Read"; $hash->{UndefFn} = "WS3600_Undef"; } @@ -164,55 +184,42 @@ sub WS3600_Define($$) { my ($hash, $def) = @_; - my @a = split("[ \t][ \t]*", $def); + my @a = split("\"", $def); + my $dev; + my $Timer = 60; # call every 64 seconds; normal wireless update interval + # is 128 sec, on wind >10 m/s 32 sec. 64 sec should ensure + # quite current data. - return "Define the /path/to/fetch3600 as a parameter" if(@a != 3); + if(@a==1) { + @a = split("[ \t][ \t]*", $def); #compatibility for old syntax + return "wrong syntax: define WS3600 \"]>\" []" if(@a!=3); + $dev = $a[2]; + } + else { + return "wrong syntax: define WS3600 \"]>\" []" if(@a < 2 || @a > 3); + $dev = $a[1]; + $Timer = $a[2] if((@a==3)&&($a[2]>=10)); + } - my $FH; - my $dev = sprintf("%s |", $a[2]); - Log 3, "WS3600 using \"$dev\" as parameter to open(); trying ..."; - open($FH, $dev); - if(!$FH) { - return "WS3600 Can't start $dev: $!"; - } - local $_; - while (<$FH>) { -# my ($reading, $val)=split(/ /, $_); - - if(/^(Date) (.*)/ || /^(Time) (.*)/ || /^(Ti) (.*)/ || /^(To) (.*)/) { - Log 3, "WS3600 initial read: $1 $2"; - } - } - close($FH); - Log 3, "WS3600 initial read done"; + my $name = $hash->{NAME}; + my $ret = `$dev`; #call external program + Log3 $name, 4, "WS3600(Dbg): $name ret=$ret"; + + return "WS3600(Err): Can't start $dev: $!" if(!defined($ret)); + +# Log3 $name, 3, "WS3600 $dev started"; $hash->{DeviceName} = $dev; - $hash->{Timer} = 64; # call every 64 seconds; normal wireless update interval - # is 128 sec, on wind >10 km/h 32 sec. 64 sec should ensure - # quite current data. + $hash->{Timer} = $Timer; -# my $tn = TimeNow(); -# $hash->{READINGS}{"freq"}{TIME} = $tn; -# $hash->{READINGS}{"freq"}{VAL} = $hash->{Timer}; -# $hash->{CHANGED}[0] = "freq: $hash->{Timer}"; + my $nt = gettimeofday() + $hash->{Timer}; + $nt -= $nt % $hash->{Timer}; # round + Log3 $name, 3, "WS3600(Msg): $name initialized, setting callback timer to " . FmtTime($nt) . "(+ $Timer s)"; - # InternalTimer blocks if init_done is not true -# my $oid = $init_done; -# $init_done = 1; -# WS3600_GetStatus($hash); -# $init_done = $oid; - - Log 3, "WS3600 setting callback timer"; - - my $oid = $init_done; - $init_done = 1; - InternalTimer(gettimeofday()+ $hash->{Timer}, "WS3600_GetStatus", $hash, 1); - $init_done = $oid; - - Log 3, "WS3600 initialized"; + RemoveInternalTimer($hash); + InternalTimer($nt, "WS3600_Read", $hash, 0); $hash->{STATE} = "initialized"; - $hash->{TMPSTATE} = ""; return undef; } @@ -221,225 +228,87 @@ sub WS3600_Undef($$) { my ($hash, $def) = @_; - my @a = split("[ \t][ \t]*", $def); my $name = $hash->{NAME}; - if(defined($hash->{FD})) { - close($hash->{FD}); - delete $hash->{FD}; - } - delete $selectlist{"$name.pipe"}; - + RemoveInternalTimer($hash); $hash->{STATE}='undefined'; - Log GetLogLevel($name,3), "$name shutdown complete"; + Log3 $name, 3, "WS3600(Msg): $name shutdown complete"; return undef; } - -##################################### -sub -WS3600_GetStatus($) -{ - my ($hash) = @_; - my $dnr = $hash->{DEVNR}; - my $name = $hash->{NAME}; - my $dev = $hash->{DeviceName}; - my $FH; - - # Call us in n seconds again. -# InternalTimer(gettimeofday()+ $hash->{Timer}, "WS3600_GetStatus", $hash, 1); - - Log GetLogLevel($name,4), "WS3600 contacting station"; - - open($FH, $dev); - if(!$FH) { - return "WS3600 Can't start $dev: $!"; - } - - $hash->{FD}=$FH; - $selectlist{"$name.pipe"} = $hash; - Log GetLogLevel($name,4), "WS3600 pipe opened"; -# $hash->{STATE} = "running"; - $hash->{pipeopentime} = time(); -# InternalTimer(gettimeofday() + 6, "WS3600_Read", $hash, 1); - return $hash->{STATE}; -} - ##################################### sub WS3600_Read($) { - my ($hash) = @_; - my $dnr = $hash->{DEVNR}; - my $name = $hash->{NAME}; - my $dev = $hash->{DeviceName}; - my $FH; - my $inputline; + my ($hash) = @_; + my $name = $hash->{NAME}; + my $dev = $hash->{DeviceName}; + my @lines; + my $tn = TimeNow(); + my $reading; + my $AnythingRead = 0; - Log GetLogLevel($name,4), "WS3600 Read entered"; + $hash->{LastRead} = $tn; + if(defined($defs{$name}{READINGS}{"State"})) { + $defs{$name}{READINGS}{"State"}{VAL} = 0xFF; + $defs{$name}{READINGS}{"State"}{TIME} = $tn; + } - if(!defined($hash->{FD})) { - Log GetLogLevel($name,3), "Oops, WS3600 FD undef'd"; - return undef; +# Log 4-GetLogLevel($name,0), "WS3600(Dbg): (4) Info"; +# Log 3-GetLogLevel($name,0), "WS3600(Msg): (3) Msg"; +# Log 2-GetLogLevel($name,0), "WS3600(Wng): (2) Warning"; +# Log 1-GetLogLevel($name,0), "WS3600(Err): (1) Error"; + +# Log3 $name, 4, "WS3600(Dbg): $name Read started using \"$dev\""; + Log3 $name, 3, "WS3600(Msg): $name Read started"; + @lines = `$dev`; # call external program + + foreach my $inputline ( @lines ) { + $inputline =~ s/\s+$//; + my ($rawreading, $val, $val2) = split(/ /, $inputline); + Log3 $name, 4, "WS3600(Dbg): $name read $inputline|$rawreading|$val|$val2"; + if(defined($TranslatedCodes{$rawreading})) { + $reading = $TranslatedCodes{$rawreading}; + $defs{$name}{READINGS}{$reading}{VAL} = $val; + $defs{$name}{READINGS}{$reading}{TIME} = $tn; + $AnythingRead = 1; } - if(!$hash->{FD}) { - Log GetLogLevel($name,3), "Oops, WS3600 FD empty"; - return undef; + # write Date/Time-Records + elsif(defined($TranslatedDateTimeCodes{$rawreading})) { + $reading = $TranslatedDateTimeCodes{$rawreading}; + $defs{$name}{READINGS}{$reading}{VAL} = $val . " " . $val2; + $defs{$name}{READINGS}{$reading}{TIME} = $tn; + $AnythingRead = 1; } - $FH = $hash->{FD}; - - Log GetLogLevel($name,4), "WS3600 reading started"; - - my @lines; - my $eof; - my $i=0; - my $tn = TimeNow(); - my $StateString=$hash->{TMPSTATE}; - my $HumidString=""; - my $TempsString=""; - my $OtherString=""; - my $reading; - my $readingforstatus; - - ($eof, @lines) = nonblockGetLines($FH); - - if(!defined($eof)) { - Log GetLogLevel($name,4), "WS3600 FIXME: eof undefined?!"; - $eof=0; + # append Time-Record to Date-Record (managed by same Name) + elsif(defined($TranslatedTimeCodes{$rawreading})) { + $reading = $TranslatedTimeCodes{$rawreading}; + $defs{$name}{READINGS}{$reading}{VAL} .= " " . $val; + $defs{$name}{READINGS}{$reading}{TIME} = $tn; + $AnythingRead = 1; } - Log GetLogLevel($name,4), "WS3600 reading ended with eof==$eof"; + } + if($AnythingRead) { + $hash->{STATE} = "T: " . $defs{$name}{READINGS}{"Temp-outside"}{VAL} + . " H: " . $defs{$name}{READINGS}{"rel-Humidity-outside"}{VAL} + . " W: " . $defs{$name}{READINGS}{"Wind-Speed"}{VAL} + . " R: " . $defs{$name}{READINGS}{"Rain-total"}{VAL} + . " Ti: " . $defs{$name}{READINGS}{"Temp-inside"}{VAL} + . " Hi: " . $defs{$name}{READINGS}{"rel-Humidity-inside"}{VAL}; - # FIXME! Current observed behaviour is "would block", then read of only EOF. - # Not sure if it's always that way; more correct would be checking - # for empty $inputline or undef'd $rawreading,$val. -wusel, 2010-01-04 - if($eof != 1) { - foreach my $inputline ( @lines ) { - $inputline =~ s/\s+$//; - my ($rawreading, $val)=split(/ /, $inputline); - Log GetLogLevel($name,5), "WS3600 read $inputline:$rawreading:$val"; - if(defined($TranslatedCodes{$rawreading})) { - -# delete $defs{$name}{READINGS}{" $rawreading"}; - - $reading=$TranslatedCodes{$rawreading}; - - $defs{$name}{READINGS}{$reading}{VAL} = $val; - $defs{$name}{READINGS}{$reading}{TIME} = $tn; -# -# -wusel, 2010-01-30: BIG CHANGE: only put into CHANGED[] what will be in -# STATE as well; this is done to reduce the burden on -# the notification framework (each one currently leads -# to a separate notify which will in turn lead a call -# of EVERY NotifyFn()) and to improve FHEMs overall -# performance. -# Every value is still be stored in READINGS though. -# -# $hash->{CHANGED}[$i++] = "$reading: $val"; - - if(defined($WantedCodesForStatus{$rawreading})) { - $readingforstatus=$WantedCodesForStatus{$rawreading}; - $StateString=sprintf("%s %s %s", $StateString, $readingforstatus, $val); - $hash->{CHANGED}[$i++] = "$reading: $val"; - } -# if($rawreading =~ m/^(Tendency|Forecast)/) { -# $hash->{CHANGED}[$i++] = "$reading: $val"; -# $StateString=sprintf("%s %s: %s", $StateString, $reading, $val); -# } -# if($rawreading =~ m/^(Ti$|To$|WC$)/) { -# $hash->{CHANGED}[$i++] = "$reading: $val"; -# $TempsString=sprintf("%s %s: %s °C", $TempsString, $reading, $val); -# } -# if($rawreading =~ m/^(RHi$|RHo$)/) { -# $hash->{CHANGED}[$i++] = "$reading: $val"; -# $HumidString=sprintf("%s %s: %s %%", $HumidString, $reading, $val); -# } -# if($rawreading =~ m/^(R1h$|R24h$)/) { -# $hash->{CHANGED}[$i++] = "$reading: $val"; -# $OtherString=sprintf("%s %s: %s mm", $OtherString, $reading, $val); -# } -# if($rawreading =~ m/^(RP$|AP$)/) { -# $hash->{CHANGED}[$i++] = "$reading: $val"; -# $OtherString=sprintf("%s %s: %s hPa", $OtherString, $reading, $val); -# } - } - } - $hash->{TMPSTATE} = $StateString; - } - - if($eof) { - close($FH); - delete $hash->{FD}; - delete $selectlist{"$name.pipe"}; - InternalTimer(gettimeofday()+ $hash->{Timer}, "WS3600_GetStatus", $hash, 1); - Log GetLogLevel($name,4), "WS3600 done reading pipe"; - } else { - Log GetLogLevel($name,4), "WS3600 (further) reading would block"; - } - -# $OtherString =~ s/^\s+//; -# $HumidString =~ s/^\s+//; -# $TempsString =~ s/^\s+//; -# $StateString =~ s/^\s+//; -# -# $defs{$name}{READINGS}{"Humidity"}{VAL} = $HumidString; -# $defs{$name}{READINGS}{"Humidity"}{TIME} = $tn; -# $hash->{CHANGED}[$i++] = $HumidString; -# $defs{$name}{READINGS}{"Temperatures"}{VAL} = $TempsString; -# $defs{$name}{READINGS}{"Temperatures"}{TIME} = $tn; -# $hash->{CHANGED}[$i++] = $TempsString; -# $defs{$name}{READINGS}{"Rain/Pressure"}{VAL} = $OtherString; -# $defs{$name}{READINGS}{"Rain/Pressure"}{TIME} = $tn; -# $hash->{CHANGED}[$i++] = $OtherString; -# $defs{$name}{READINGS}{"Forecast"}{VAL} = $StateString; -# $defs{$name}{READINGS}{"Forecast"}{TIME} = $tn; -# $hash->{CHANGED}[$i++] = $StateString; - -# -wusel, 2010-01-06: FIXME: does this logic with STATE work? -# -wusel, 2010-01-30: Removed setting STATE to "reading". - - if($eof) { -# $hash->{CHANGED}[$i++] = "Status: $StateString"; - $hash->{STATE} = $hash->{TMPSTATE}; - $hash->{TMPSTATE} = ""; - DoTrigger($name, undef); -# } else { -# $hash->{STATE} = "reading"; - } - - return $hash->{STATE}; -} - - -# From http://www.perlmonks.org/?node_id=713384 / http://davesource.com/Solutions/20080924.Perl-Non-blocking-Read-On-Pipes-Or-Files.html -# -# Used, hopefully, with permission ;) -# -# An non-blocking filehandle read that returns an array of lines read -# Returns: ($eof,@lines) -my %nonblockGetLines_last; -sub nonblockGetLines { - my ($fh,$timeout) = @_; - - $timeout = 0 unless defined $timeout; - my $rfd = ''; - $nonblockGetLines_last{$fh} = '' - unless defined $nonblockGetLines_last{$fh}; - - vec($rfd,fileno($fh),1) = 1; - return unless select($rfd, undef, undef, $timeout)>=0; - # I'm not sure the following is necessary? - return unless vec($rfd,fileno($fh),1); - my $buf = ''; - my $n = sysread($fh,$buf,1024*1024); - # If we're done, make sure to send the last unfinished line - return (1,$nonblockGetLines_last{$fh}) unless $n; - # Prepend the last unfinished line - $buf = $nonblockGetLines_last{$fh}.$buf; - # And save any newly unfinished lines - $nonblockGetLines_last{$fh} = - (substr($buf,-1) !~ /[\r\n]/ && $buf =~ s/([^\r\n]*)$//) - ? $1 : ''; - $buf ? (0,split(/\n/,$buf)) : (0); + $hash->{CHANGED}[0] = $hash->{STATE}; + DoTrigger($name, undef); + } + else { + $hash->{STATE} = "no data received"; + } + # Call us in n seconds again. + my $nt = gettimeofday() + $hash->{Timer}; + $nt -= $nt % $hash->{Timer}; # round + RemoveInternalTimer($hash); + InternalTimer($nt, "WS3600_Read", $hash, 0); + + return $hash->{STATE}; } 1; @@ -450,32 +319,55 @@ sub nonblockGetLines {

WS3600

    + Defines a weather station, which is queried by means of an external + program. That program is executed by FHEM and is expected to deliver the + data at stdout in the format of a WS3600 series weather station (details + see below).

    - - - Define + Define
      - define <name> WS3600 </path/to/fetch3600> -

      - - Define a WS3600 series weather station (Europe Supplies, technotrade, etc; refer to - Wetterstationen.info - (german) for details on this model); the station is queried by means of an external program, - fetch3600. It talks to the attached weather station (several WS do supply an RS323 interface - but seem to use some kind of "morse code" on the RTS, CTS wires instead of using propper - serial communication (RX, TX); it's no use to recode that crap into FHEM when there is a - stable package of tools to talk to the station available: open3600) - and delivers the current readings line by line as reading-value-pairs. These are read in - and translated into more readable names for FHEM by the module WS3600.pm.

      - As the WS3600 is rather similar to the WS2300 - and open3600 basically is a modified offspring of open2300, by exchanging the /path/to/fetch3600 with /path/to/fetch2300 this module - should be able to handle the WS2300 was well.

      - Currently, it is expected that the WS is attached to the local computer and fetch3600 is run - locally. Basically the executable called needs to supply on stdout an output similar to what - fetch3600 returns; how to implement a "networked setup" is left as an excercise to the reader. + define <name> WS3600 "<wsreaderprog> + [<options>]" [<interval>]

      - For the records, this is an output of fetch3600:
      -Date 14-Nov-2009
      +    
        +
        +
        <wsreaderprog>
        +
        full path to the executable which queries the weatherstation + (for WS3600 series fetch3600 should be used)
        +
        <options>
        +
        options for <wsreaderprog>, if necessary
        +
        <interval>
        +
        this optional parameter is the time between subsequent calls to + <wsreaderprog>. It defaults to 60s.
        +
        +
      +
      + Supported Stations are:
      +
        +
      • WS3600 series weather station (Europe Supplies, technotrade, etc; + refer to Wetterstationen.info + (german) for details on this model) with fetch3600 from the + toolchain open3600). + Fetch3600 delivers the current readings line by line as + reading-value-pairs. These are read periodically and translated into + more readable names for FHEM by the module WS3600.pm.
      • +
      • WS2300 + with toolchain open2300, + because it is rather similar to the WS3600.
      • +
      • WS1080 + (and other stations which come with the EasyWeather windows + application) with fowsr + (version 2.0 or above)
      • +
      +
      + Currently, it is expected that the WS is attached to the local computer + and <wsreaderprog> is run locally. Basically the executable called + needs to supply on stdout an output similar to what fetch3600 returns; + how to implement a "networked setup" is left as an excercise to the + reader.
      + For the records, this is an output of fetch3600:
      +
      +
      Date 14-Nov-2009
       Time 10:50:22
       Ti 22.8
       Timin 20.8
      @@ -562,51 +454,230 @@ TRPmax 09:19
       DRPmax 11-09-2009
       Tendency Falling
       Forecast Cloudy
      - - There is no expectation on the readings received from the fetch3600 binary; so, in - essence, if you have a similar setup (unsupported, attached weather station and a - means to get it's reading into an output similar to above's), you should be able - to use WS3600.pm with a custom written script to interface FHEM with your station - as well. WS3600.pm only recognizes the above readings (and translates these - into, e. g., Temp-inside for Ti for use within FHEM), other - lines are silently dropped on the floor.

      - - fetch3600 is available as binary for the Windows OS as well, but I haven't tested operation - under that OS, use it at your own risk and you mileage may vary ... -
      Note: Currently this device does not support a "set" function nor anything to "get". The - later would be possible to implement if neccessary, though. -

      - - Implementation of WS3600.pm tries to be nice, that is it reads from the pipe only - non-blocking (== if there is data), so it should be safe even to use it via ssh or - a netcat-pipe over the Internet, but this, as well, has not been tested yet. -

      - - Attributes: -
        -
      • model: WS3600 or WS2300 (not used for anything, yet)
      • -
      +
      + There is no expectation on the readings received from the fetch3600 + binary; so, in essence, if you have a similar setup (unsupported, + attached weather station and a means to get it's reading into an output + similar to above's), you should be able to use WS3600.pm with + a custom written script to interface FHEM with your station as well. + WS3600.pm only recognizes the above readings (and translates + these into, e. g., Temp-inside for Ti for + use within FHEM), other lines are silently dropped on the floor. Note: + To step down the number of readings date and time records will now be + merged to one reading containing date and time. This now also allows + records with merged date / time values delivered from + <wsreaderprog> - detected by prefix DT (e.g. Date + + Time --> DTime, DRPmin + + TRPmin --> DTRPmin and so on).
      + fetch3600 is available as binary for the Windows OS as well, but + operation under that OS isn't tested yet.

      - Example: + Examples:
        - define my3600 W36000 /usr/local/bin/fetch360
        + define myWS3600 W3600 /usr/local/bin/fetch360
        + define myWS1080 W3600 "/usr/local/bin/fowsr -c" 300

    - - - Set
      N/A

    - - - Get
      N/A

    - - - Attributes + Set
      -
    • model (WS3600, WS2300)
    • + N/A +
    +
    + Get +
      + N/A +
    +
    + Attributes +
      +
    • model     WS3600, WS2300, + WS1080 (not used for anything, yet)

=end html +=begin html_DE + + +

WS3600

+
    + Definiert eine Wetterstation, die über ein externes Programm ausgelesen + wird. Dieses Programm wird zyklisch durch FHEM aufgerufen. Es muss die + Daten im gleichen Format wie fetch3600 (Details siehe unten) auf der + Standardausgabe liefern.
    +
    + Define +
      + define <name> WS3600 "<wsreaderprog> + [<options>]" [<interval>]
      +
      +
        +
        +
        <wsreaderprog>
        +
        kompletter Pfad zum Ausleseprogramm (für Wetterstationen Typ + WS3600 fetch3600 verwenden)
        +
        <options>
        +
        Kommandozeilenparameter für <wsreaderprog>, falls + erforderlich
        +
        <interval>
        +
        optionaler Parameter für das Aufrufintervall [s]. Defaultwert + ist 60s.
        +
        +
      +
      +   Unterstützte Stationen sind:
      +
        +
      • WS3600 Serie (Europe Supplies, technotrade, usw.; s.a. Wetterstationen.info + (deutsch) für Details) in Verbindung mit fetch3600 aus dem Paket open3600). + Fetch3600 liefert die aktuellen Werte zeilenweise als + Name-Wert-Paare. Diese werden durch FHEM zyklisch eingelesen, mit + besser lesbaren Bezeichnungen versehen und als Readings zur + Verfügung gestellt.
      • +
      • WS2300 + Serie in Verbindung mit dem Paket open2300 + (ähnlich zu open3600).
      • +
      • WS1080 + (und andere Stationen, die mit der Windows-Software "Easy Weather" + ausgeliefert werden) in Verbindung mit fowsr + (ab Version 2.0)
      • +
      +
      + Es wird vorausgesetzt, dass die Wetterstation am lokalen Computer + angeschlossen ist und <wsreaderprog> deshalb lokal läuft. + <wsreaderprog> muss grundsätzlich eine zu fetch3600 vergleichbare + Ausgabe auf der Standardausgabe liefern.
      + Als Beispiel für das erwartete Format hier die Ausgabe von fetch3600:
      +
      +
      Date 14-Nov-2009
      +Time 10:50:22
      +Ti 22.8
      +Timin 20.8
      +Timax 27.9
      +TTimin 10:27
      +DTimin 15-10-2009
      +TTimax 23:31
      +DTimax 20-08-2009
      +To 14.2
      +Tomin -0.4
      +Tomax 35.6
      +TTomin 07:03
      +DTomin 15-10-2009
      +TTomax 16:52
      +DTomax 20-08-2009
      +DP 9.2
      +DPmin -2.2
      +DPmax 20.3
      +TDPmin 07:03
      +DDPmin 15-10-2009
      +TDPmax 11:58
      +DDPmax 20-08-2009
      +RHi 48
      +RHimin 32
      +RHimax 57
      +TRHimin 17:03
      +DRHimin 21-10-2009
      +TRHimax 22:24
      +DRHimax 07-10-2009
      +RHo 72
      +RHomin 27
      +RHomax 96
      +TRHomin 16:41
      +DRHomin 20-08-2009
      +TRHomax 06:28
      +DRHomax 02-11-2009
      +WS 0.0
      +DIRtext WSW
      +DIR0 247.5
      +DIR1 247.5
      +DIR2 247.5
      +DIR3 247.5
      +DIR4 247.5
      +DIR5 247.5
      +WC 14.2
      +WCmin -0.4
      +WCmax 35.6
      +TWCmin 07:03
      +DWCmin 15-10-2009
      +TWCmax 16:52
      +DWCmax 20-08-2009
      +WSmin 0.0
      +WSmax 25.6
      +TWSmin 10:44
      +DWSmin 14-11-2009
      +TWSmax 19:08
      +DWSmax 24-09-2009
      +R1h 0.00
      +R1hmax 24.34
      +TR1hmax 22:34
      +DR1hmax 07-10-2009
      +R24h 0.00
      +R24hmax 55.42
      +TR24hmax 07:11
      +DR24hmax 08-10-2009
      +R1w 29.00
      +R1wmax 95.83
      +TR1wmax 00:00
      +DR1wmax 12-10-2009
      +R1m 117.58
      +R1mmax 117.58
      +TR1mmax 00:00
      +DR1mmax 01-11-2009
      +Rtot 3028.70
      +TRtot 03:29
      +DRtot 18-09-2005
      +RP 992.200
      +AP 995.900
      +RPmin 970.300
      +RPmax 1020.000
      +TRPmin 05:25
      +DRPmin 04-11-2009
      +TRPmax 09:19
      +DRPmax 11-09-2009
      +Tendency Falling
      +Forecast Cloudy
      +
      + Welche der vorgenannten Wertepaare durch <wsreaderprog>  + geliefert werden, ist egal. Jedes bekannte wird übersetzt (z.B. Ti + nach Temp-inside) und als Reading angezeigt, alle + unbekannten werden kommentarlos verworfen. Mittels geeignetem Programm + oder Script sollte sich also jede beliebige Wetterstation anschließen + lassen.
      + Anmerkung: Um die Anzahl Readings zu reduzieren, werden jetzt Date- und + Time-Wertepaare zusammengefasst. Es ist jetzt auch zulässig, dass + <wsreaderprog> schon kombinierte Wertepaare liefert. Diese sind + mit dem Prefix DT zu kennzeichnen, also z.B. Date + + Time --> DTime, DRPmin + + TRPmin --> DTRPmin usw.).
      + Fetch3600 ist auch unter Windows verfügbar, ob das Zusammenspiel mit + FHEM dort auch funktioniert, wurde noch nicht getestet.
      +
      + Beispiele: +
        + define myWS3600 W3600 /usr/local/bin/fetch360
        + define myWS1080 W3600 "/usr/local/bin/fowsr -c" 300
        +
      +
      +
    + Set +
      + N/A +
    +
    + Get +
      + N/A +
    +
    + Attributes +
      +
    • model     WS3600, WS2300, + WS1080 (z.Zt (noch) ohne Wirkung)
    • +
    +
    +
+ +=end html_DE =cut diff --git a/fhem/MAINTAINER.txt b/fhem/MAINTAINER.txt index 6fa20b9d0..042da6285 100644 --- a/fhem/MAINTAINER.txt +++ b/fhem/MAINTAINER.txt @@ -124,7 +124,7 @@ FHEM/70_STV.pm bentele http://forum.fhem.de Sonstiges FHEM/70_TellStick.pm real-wusel http://forum.fhem.de Sonstiges FHEM/70_USBWX.pm wherzig http://forum.fhem.de Sonstiges FHEM/70_VIERA.pm teevau http://forum.fhem.de Sonstiges -FHEM/70_WS3600.pm painseeker http://forum.fhem.de Sonstiges +FHEM/70_WS3600.pm Josch http://forum.fhem.de Sonstiges FHEM/71_LISTENLIVE.pm betateilchen http://forum.fhem.de Multimedia FHEM/71_YAMAHA_AVR.pm markusbloch http://forum.fhem.de Multimedia FHEM/72_FB_CALLMONITOR.pm markusbloch http://forum.fhem.de Unterstützende Dienste