From 66240ae303f7a901c7231aaf834e54dcdd161d9e Mon Sep 17 00:00:00 2001 From: jowiemann <> Date: Tue, 28 Nov 2023 15:53:38 +0000 Subject: [PATCH] 98_CDCOpenData.pm: Version 01.11 git-svn-id: https://svn.fhem.de/fhem/trunk@28221 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/98_CDCOpenData.pm | 478 +++++++++++++++++++++++++++++------- 1 file changed, 387 insertions(+), 91 deletions(-) diff --git a/fhem/FHEM/98_CDCOpenData.pm b/fhem/FHEM/98_CDCOpenData.pm index 0b8aad0f7..3730ab0e1 100644 --- a/fhem/FHEM/98_CDCOpenData.pm +++ b/fhem/FHEM/98_CDCOpenData.pm @@ -7,6 +7,8 @@ # # (c) 2023 basic works for getting and decoding data from DWD by F. Ahlers https://forum.fhem.de/index.php?action=profile;u=3346 # (c) herrmannj (https://forum.fhem.de/index.php?action=profile;u=769) Original work for cron functions taken from 98_JsonMod.pm +# (c) Jamo (https://forum.fhem.de/index.php?msg=1292677) basierend auf den html bars der älteren Module '59_RainTMC.pm' und '59_Buienradar.pm', +# und adaptiert für CDCOpenData rain_radar # # The module extracts data for daily rainfall from binary files supplied # by DWD's (German weather service) open data server. The data are based on rain radar data which @@ -52,10 +54,12 @@ use warnings; use Blocking; use HttpUtils; -my $ModulVersion = "01.10f"; +my $ModulVersion = "01.11"; my $missingModul = ""; sub CDCOpenData_Log($$$); +sub CDCOpenData_DebugLog($$$$;$); +sub CDCOpenData_dbgLogInit($@); sub CDCOpenData_Initialize($); sub CDCOpenData_Readout_Add_Reading ($$$$@); sub CDCOpenData_Readout_Process($$); @@ -74,12 +78,12 @@ my @cmdBuffer=(); my $cmdBufferTimeout=0; my %LOG_Text = ( - 0 => "SERVER: ", - 1 => "EMERGENCY: ", - 2 => "ERROR: ", - 3 => "EVENT: ", - 4 => "INFO: ", - 5 => "DEBUG: " + 0 => "SERVER:", + 1 => "ERROR:", + 2 => "SIGNIFICANT:", + 3 => "BASIC:", + 4 => "EXPANDED:", + 5 => "DEBUG:" ); ####################################################################### @@ -104,11 +108,149 @@ sub CDCOpenData_Log($$$) $sub ||= 'no-subroutine-specified'; $text = $LOG_Text{$loglevel} . $text; + $text = "[$instName | $sub.$xline] - " . $text; - Log3 $hash, $loglevel, "[$instName | $sub.$xline] - " . $text; + if ( $instHash->{helper}{logDebug} ) { + CDCOpenData_DebugLog $instHash, $instHash->{helper}{debugLog} . "-%Y-%m.dlog", $loglevel, $text; + } else { + Log3 $hash, $loglevel, $text; + } } # End CDCOpenData_Log +####################################################################### +sub CDCOpenData_DebugLog($$$$;$) { + + my ($hash, $filename, $loglevel, $text, $timestamp) = @_; + my $name = $hash->{'NAME'}; + my $tim; + + $loglevel .= ":" if ($loglevel); + $loglevel ||= ""; + + my $dirdef = AttrVal('global', 'logdir', $attr{global}{modpath}.'/log/'); + + my ($seconds, $microseconds) = gettimeofday(); + my @t = localtime($seconds); + my $nfile = $dirdef . ResolveDateWildcards($filename, @t); + my $fh; + + unless ($timestamp) { + + $tim = sprintf("%04d.%02d.%02d %02d:%02d:%02d", $t[5] * 1900, $t[4] + 1, $t[3], $t[2], $t[1], $t[0]); + + if ($attr{global}{mseclog}) { + $tim .= sprintf(".%03d", $microseconds / 1000); + } + + $tim .= " "; + open($fh, '>>', $nfile); + + } elsif ( $timestamp eq "no") { + + $tim = ""; + open($fh, '>', $nfile); + + } else { + + $tim = $timestamp . " "; + open($fh, '>>', $nfile); + } + + print $fh "$tim$loglevel$text\n"; + close $fh; + + return undef; + +} # end CDCOpenData__DebugLog + +####################################################################### +sub CDCOpenData_dbgLogInit($@) { + + my ($hash, $cmd, $aName, $aVal) = @_; + my $name = $hash->{NAME}; + + if ($cmd eq "init" ) { + $hash->{DEBUGLOG} = "OFF"; + $hash->{helper}{debugLog} = $name . "_debugLog"; + $hash->{helper}{logDebug} = AttrVal($name, "verbose", 0) == 5; + } + + return if $aVal && $aVal == -1; + + my $dirdef = AttrVal('global', 'logdir', $attr{global}{modpath}.'/log/'); + my $dbgLogFile = $dirdef . $hash->{helper}{debugLog} . '-%Y-%m.dlog'; + + if ($cmd eq "set" || $cmd eq "init") { + + if($aVal == 5) { + my $dMod = 'defmod ' . $hash->{helper}{debugLog} . ' FileLog ' . $dbgLogFile . ' FakeLog readonly'; + + fhem($dMod, 1); + + if (my $dRoom = AttrVal($name, "room", undef)) { + $dMod = 'attr -silent ' . $hash->{helper}{debugLog} . ' room ' . $dRoom; + fhem($dMod, 1); + } + + if (my $dGroup = AttrVal($name, "group", undef)) { + $dMod = 'attr -silent ' . $hash->{helper}{debugLog} . ' group ' . $dGroup; + fhem($dMod, 1); + } + + CDCOpenData_Log $name, 3, "redirection debugLog: $dbgLogFile started"; + + $hash->{helper}{logDebug} = 1; + + CDCOpenData_Log $name, 3, "redirection debugLog: $dbgLogFile started"; + + my ($seconds, $microseconds) = gettimeofday(); + my @t = localtime($seconds); + my $nfile = ResolveDateWildcards($hash->{helper}{debugLog} . '-%Y-%m.dlog', @t); + + $hash->{DEBUGLOG} = '' + . 'DEBUG Log kann hier eingesehen werden' + . ''; + + } elsif($aVal < 5 && $hash->{helper}{logDebug}) { + fhem("delete " . $hash->{helper}{debugLog}, 1); + + CDCOpenData_Log $name, 3, "redirection debugLog: $hash->{helper}{debugLog} deleted"; + + $hash->{helper}{logDebug} = 0; + $hash->{DEBUGLOG} = "OFF"; + + CDCOpenData_Log $name, 3, "redirection debugLog: $dbgLogFile stopped"; + +# unless (unlink glob($dirdef . $hash->{helper}{debugLog} . '*.dlog')) { +# return "Temporary debug file: " . $dirdef . $hash->{helper}{debugLog} . "*.dlog could not be removed: $!"; +# } + } + } + + if ($cmd eq "del" ) { + fhem("delete " . $hash->{helper}{debugLog}, 1) if $hash->{helper}{logDebug}; + + CDCOpenData_Log $name, 3, "redirection debugLog: $hash->{helper}{debugLog} deleted"; + + $hash->{helper}{logDebug} = 0; + $hash->{DEBUGLOG} = "OFF"; + + CDCOpenData_Log $name, 3, "redirection debugLog: $dbgLogFile stopped"; + + unless (unlink glob($dirdef . $hash->{helper}{debugLog} . '*.dlog')) { + CDCOpenData_Log $name, 3, "Temporary debug file: " . $dirdef . $hash->{helper}{debugLog} . "*.dlog could not be removed: $!"; + } + + } + +} # end CDCOpenData_dbgLogInit + + ####################################################################### sub CDCOpenData_Initialize($) { @@ -134,7 +276,7 @@ sub CDCOpenData_Initialize($) ."enableDWDdata:multiple-strict,rainByDay,rainSinceMidnight,rainRadarbyLocation " ."clearRadarFileLog " ."RainRadarFileLog " -# ."ownRadarFileLog " + ."ownRadarFileLog:0,1 " .$readingFnAttributes; } # end CDCOpenData_Initialize @@ -166,7 +308,11 @@ sub CDCOpenData_Define($$) $hash->{LOCATION} = $location; } -# stop if certain perl moduls are missing + # initialize DEGUB Log function + CDCOpenData_dbgLogInit($hash, "init", "verbose", AttrVal($name, "verbose", -1)); + # end initialize DEGUB Log function + + # stop if certain perl moduls are missing my $msg; if ( $missingModul ) { $msg = "ERROR: Cannot define a CDCOpenData device. Perl modul $missingModul is missing."; @@ -175,14 +321,15 @@ sub CDCOpenData_Define($$) return $msg; } - $hash->{NAME} = $name; - $hash->{VERSION} = $ModulVersion; + $hash->{NAME} = $name; + $hash->{VERSION} = $ModulVersion; $hash->{STATE} = "Initializing"; $hash->{INTERVAL} = 300; $hash->{TIMEOUT} = 55; $hash->{TMPDIR} = "temp_radolan_data_" . $name; $hash->{DWDHOST} = "opendata.dwd.de"; + $hash->{fhem}{UPDATE} = 0; $hash->{helper}{TimerReadout} = $name . ".Readout"; @@ -190,6 +337,7 @@ sub CDCOpenData_Define($$) $hash->{helper}{baseTMPDIR} = "temp_radolan_data_" . $name; $hash->{helper}{FhemLog3Std} = AttrVal($name, "FhemLog3Std", 0); $hash->{helper}{CronTime} = AttrVal($name, "cronTime", 0) ? 1 : 0; + $hash->{helper}{rainLog} = $name . "_rainLog"; eval {File::Path::make_path($hash->{helper}{baseTMPDIR}) }; @@ -312,6 +460,22 @@ sub CDCOpenData_Attr($@) my $hash = $defs{$name}; + if ($aName eq "verbose") { + CDCOpenData_dbgLogInit($hash, $cmd, $aName, $aVal) if !$hash->{helper}{FhemLog3Std}; + } + + if($aName eq "FhemLog3Std") { + if ($cmd eq "set") { + return "FhemLog3Std: $aVal. Valid is 0 or 1." if $aVal !~ /[0-1]/; + return "special debug log ist activated." if $hash->{helper}{logDebug}; + $hash->{helper}{FhemLog3Std} = $aVal; + CDCOpenData_dbgLogInit($hash, "set", "verbose", 5) if AttrVal($name, "verbose", 0) == 5 && $aVal == 0; + } else { + $hash->{helper}{FhemLog3Std} = 0; + CDCOpenData_dbgLogInit($hash, "set", "verbose", 5) if AttrVal($name, "verbose", 0) == 5; + } + } + if ($cmd eq "set") { if ($aName eq "INTERVAL") { @@ -330,26 +494,16 @@ sub CDCOpenData_Attr($@) } } - if ($aName eq "numberOfDays") { - return "number of days: $aVal for which data is fetched in the past. Default is 5 days." if $aVal !~ /[1-9]|1[0]/; - fhem( "deletereading $name .*_day_rain:.*", 1 ); - } - - if($aName eq "FhemLog3Std") { - $hash->{helper}{FhemLog3Std} = $aVal; - } - if($aName eq "clearRadarFileLog" && $init_done) { return "no FileLog device: $aVal defined." unless defined $defs{$aVal}; } } - if ($cmd eq "del") { - if($aName eq "FhemLog3Std") { - $hash->{helper}{FhemLog3Std} = 0; - } - - if ($aName eq "numberOfDays") { + if ($aName eq "numberOfDays") { + if ($cmd eq "set") { + return "number of days: $aVal for which data is fetched in the past. Default is 5 days." if $aVal !~ /[1-9]|1[0]/; + fhem( "deletereading $name .*_day_rain:.*", 1 ); + } else { fhem( "deletereading $name .*_day_rain:.*", 1 ); } } @@ -385,7 +539,7 @@ sub CDCOpenData_Attr($@) $hash->{helper}{CronTime} = 0; } } - + if ($aName eq "tmpRadolanData") { return undef if $aVal eq $hash->{TMPDIR}; @@ -483,15 +637,28 @@ sub CDCOpenData_Attr($@) } if ($aName eq "ownRadarFileLog") { + my $dirdef = AttrVal('global', 'logdir', $attr{global}{modpath}.'/log/'); + my $rLogFile = $dirdef . $hash->{helper}{rainLog} . '.log'; + if ($cmd eq "set") { + return "ownRadarFileLog: $aVal. Valid is 0 or 1." if $aVal !~ /[0-1]/; - } + my $dMod = 'defmod ' . $hash->{helper}{rainLog} . ' FileLog ' . $rLogFile . ' FakeLog readonly'; - if ($cmd eq "del") { - if (my $dLog = AttrVal($name, $aName, undef)) { - return "FileLog Device: $dLog not defined." unless defined $defs{$dLog}; - fhem('delete ' . $dLog, 1); + fhem($dMod, 1); + + if (my $dRoom = AttrVal($name, "room", undef)) { + $dMod = 'attr -silent ' . $hash->{helper}{rainLog} . ' room ' . $dRoom; + fhem($dMod, 1); } + + if (my $dGroup = AttrVal($name, "group", undef)) { + $dMod = 'attr -silent ' . $hash->{helper}{rainLog} . ' group ' . $dGroup; + fhem($dMod, 1); + } + } + if ($cmd eq "del") { + fhem("delete " . $hash->{helper}{rainLog}); } } # end ownRadarFileLog @@ -632,7 +799,8 @@ sub CDCOpenData_Set($$@) my ($hash, $name, $cmd, @val) = @_; my $resultStr = ""; - my $list = " update:noArg"; + my $list = " update:noArg" + . " htmlBarAsStateFormat:on,off"; if ( lc $cmd eq 'update' ) { CDCOpenData_Log $hash, 3, "set $name $cmd " . join(" ", @val); @@ -642,6 +810,17 @@ sub CDCOpenData_Set($$@) return undef; } + elsif ( lc $cmd eq 'htmlbarasstateformat' ) { + return "wrong parameter" if int @val != 1 || $val[0] !~ /on|off/; + if ($val[0] eq "on") { + fhem("attr $name stateFormat {CDCOpenData_radar2html('" . $name . "','Home_rain_radar',0)}"); + } else { + fhem("deleteattr -silent $name stateFormat"); + $hash->{STATE} = ReadingsVal($name, "state", ""); + } + return 'please save the change in the stateFormat attribute by clicking on “Save config”'; + } + return "Unknown argument $cmd or wrong parameter(s), choose one of $list"; } # end CDCOpenData_Set @@ -1065,7 +1244,7 @@ sub CDCOpenData_get_RegenRadar_atLocations($$$$) { if ($rain_forecast == 2500) { $rain_forecast = -1 ; - CDCOpenData_Log $name, 3, "Regen Radar: " . $rName . ": error in value"; + CDCOpenData_Log $name, 4, "Regen Radar: " . $rName . ": error in value"; } else { $rain_forecast *= 0.01; } @@ -1256,9 +1435,9 @@ sub CDCOpenData_Readout_Run_Rain_Since_Midnight ($@) { # use this handle to unzip the remote file 'on-the-fly': if (my $status = gunzip $retr_fh => $tmpDir . $localname, AutoClose => 1) { - Log3 $name, 4, "Rain_Since_Midnight - Loaded new local file $tmpDir$localname: $status"; + CDCOpenData_Log $name, 4, "Rain_Since_Midnight - Loaded new local file $tmpDir$localname: $status"; } else { - Log3 $name, 3, "Rain_Since_Midnight - $GunzipError"; + CDCOpenData_Log $name, 3, "Rain_Since_Midnight - $GunzipError"; } } @@ -1272,7 +1451,7 @@ sub CDCOpenData_Readout_Run_Rain_Since_Midnight ($@) { # It is reset to -1 in order to keep the y-axis scale small when plotting. if ($upMenge == 2500) { - CDCOpenData_Log $name, 3, "Rain_Since_Midnight: error in value"; + CDCOpenData_Log $name, 4, "Rain_Since_Midnight: error in value"; } else { $regenmenge += 0.1 * $upMenge; } @@ -1421,9 +1600,9 @@ sub CDCOpenData_Readout_Run_getRain($@) # use this handle to unzip the remote file 'on-the-fly': if (my $status = gunzip $retr_fh => $tmpDir . $localname, AutoClose => 1) { - Log3 $name, 4, "getRain - Loaded new local file $tmpDir$localname: $status"; + CDCOpenData_Log $name, 4, "getRain - Loaded new local file $tmpDir$localname: $status"; } else { - Log3 $name, 3, "getRain - $GunzipError"; + CDCOpenData_Log $name, 3, "getRain - $GunzipError"; } # close ftp session: @@ -1479,7 +1658,7 @@ sub CDCOpenData_Readout_Run_getRain($@) # It is reset to -1 in order to keep the y-axis scale small when plotting. if ($regenmenge == 2500) { $regenmenge = -1 ; - CDCOpenData_Log $name, 3, "day rain: " . $tmpDir . $localname . ": error in rain value"; + CDCOpenData_Log $name, 4, "day rain: " . $tmpDir . $localname . ": error in rain value"; } else { $regenmenge *= 0.1; } @@ -1557,6 +1736,11 @@ sub CDCOpenData_Readout_Process($$) my $counter = 0; my $offset = 0; my $dayRainCnt = 0; + my $ownRadarFLog = AttrVal($name, "ownRadarFileLog", 0); + my $dirdef = AttrVal('global', 'logdir', $attr{global}{modpath}.'/log/'); + my $rLogFile = $dirdef . $hash->{helper}{rainLog} . '.log'; + + my $textRadarLog = ""; readingsBeginUpdate($hash); @@ -1575,6 +1759,7 @@ sub CDCOpenData_Readout_Process($$) if (exists $hash->{READINGS}{$_}{VAL}) { delete $hash->{READINGS}{$_}; CDCOpenData_Log $hash, 4, "delete old readings: $_"; +# unlink $rLogFile if $ownRadarFLog; } } } @@ -1638,6 +1823,13 @@ sub CDCOpenData_Readout_Process($$) $newName .= "/" . substr("00" . $counter, -2); $newName =~ s/://; readingsBulkUpdate($hash, $newName, $rValue, undef, $TS); + + my $text = $newName; + $text =~ s/\/\d+//; + $TS =~ s/ /_/; + + $textRadarLog .= $TS . " " . $name . " " . $text. ": " . $rValue . "\n"; + $offset += 5; } @@ -1660,6 +1852,8 @@ sub CDCOpenData_Readout_Process($$) } } + CDCOpenData_DebugLog($hash, $hash->{helper}{rainLog} . '.log', undef, $textRadarLog, "no") if $ownRadarFLog; + my $msg = keys( %values ) . " values captured in " . $values{readoutTime} . " s"; readingsBulkUpdate( $hash, "retStat_lastReadout", $msg ); readingsBulkUpdate( $hash, "state", $msg); @@ -2048,54 +2242,13 @@ sub CDCOpenData_DoTimer { } # end CDCOpenData_DoTimer -# ############################################ -sub CDCOpenData_RainRadar_Log($$$$;$) { - - my ($hash, $filename, $loglevel, $text, $timestamp) = @_; - my $name = $hash->{'NAME'}; - my $tim; - - $loglevel .= ":" if ($loglevel); - $loglevel ||= ""; - -# return if ( $loglevel > AttrVal($name, "verbose", AttrVal("global", "verbose", 3)) ); - - my $dirdef = AttrVal('global', 'logdir', $attr{global}{modpath}.'/log/'); - - my ($seconds, $microseconds) = gettimeofday(); - my @t = localtime($seconds); - my $nfile = $dirdef . ResolveDateWildcards($filename, @t); - - unless ($timestamp) { - -# CDCOpenData_Log $hash, 3, "rainDataLog: " . $nfile; - - $tim = sprintf("%04d.%02d.%02d %02d:%02d:%02d", $t[5] * 1900, $t[4] + 1, $t[3], $t[2], $t[1], $t[0]); - - if ($attr{global}{mseclog}) { - $tim .= sprintf(".%03d", $microseconds / 1000); - } - } else { - $tim = $timestamp; - } - - open(my $fh, '>>', $nfile); - print $fh "$tim $loglevel$text\n"; - close $fh; - - return undef; - -} # end CDCOpenData__RainRadar_Log - - -############################### # Unterverzeichnisse erstellen # Parameter # Directory ... z.B. ../test/test1/test2 # Rechte im oktalen Format # http://www.hidemail.de/blog/mkdir-perl.shtml -####################################### +############################################ sub CDCOpenData_mk_subdirs{ my $dir = shift; my $rights = shift; @@ -2120,6 +2273,93 @@ sub CDCOpenData_mk_subdirs{ } # end CDCOpenData_mk_subdirs +############################################ +# This is for htmlCode {radar2html('CDC','loc0_rain_radar')} Niederschlagsvorhersage (CDCOpenData) +# to create a HTML bar table with raincolors for the radarvalues +sub CDCOpenData_radar2html { + + my $name = shift // 'CDC'; # return "Error, sub color2html: we need name as parameter!"; + my $reading = shift // 'loc0_rain_radar'; # return "Error, sub color2html: we need reading as parameter!"; + my $headline = shift // 1; + + my $as_htmlBarhead = '
set <name> update
set <name> htmlBarAsStateFormat <on|off>
defmod <barName> weblink htmlCode {CDCOpenData_radar2html('<nameCDCDevice>', '<readingName_rain_radar>')}
get <name> rainbyLatLongDate [latitude,longitude] [date]
attr <name> verbose <0 .. 5>
attr <name> clearRadarFileLog <name of FileLog device>
attr <name> RainRadarFileLog <name of FileLog device>
attr <name> ownRadarFileLog <0 | 1>
attr <name> cronTime <* * * * *>
attr <name> enableDWDdata <rainByDay, rainSinceMidnight, rainRadarbyLocation>
set <name> update
set <name> htmlBarAsStateFormat <on|off>
defmod <barName> weblink htmlCode {CDCOpenData_radar2html('<nameCDCDevice>', '<readingName_rain_radar>')}
get <name> rainbyLatLongDate [latitude,longitude] [date]
attr <name> verbose <0 .. 5>
attr <name> clearRadarFileLog <name of FileLog device>
attr <name> RainRadarFileLog <name of FileLog device>
attr <name> ownRadarFileLog <0 | 1>
attr <name> cronTime <* * * * *>
attr <name> enableDWDdata <rainByDay, rainSinceMidnight, rainRadarbyLocation>