From b84d5626c02408e38dc2e9ce6a8da5d9655cd2be Mon Sep 17 00:00:00 2001 From: jensb <> Date: Fri, 1 Mar 2019 18:52:26 +0000 Subject: [PATCH] 55_DWD_OpenData.pm: new readings alertExcludeEvents, SunAz, SunEl and SunUp, support forecast resolution value 1 hour (see Forum #83097) git-svn-id: https://svn.fhem.de/fhem/trunk@18767 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 3 + fhem/FHEM/55_DWD_OpenData.pm | 303 ++++++++++++++++++++++++++++++----- 2 files changed, 262 insertions(+), 44 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index ab2112398..c72a83571 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,8 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Do not insert empty lines here, update check depends on it. + - feature: 55_DWD_OpenData: + new readings alertExcludeEvents, SunAz, SunEl and SunUp + support forecast resolution value 1 hour - bugfix: 12_HProtocolGateway: fix checksum - bugfix: 73_AutoShuttersControl: fix typo in Event RegEx for Twilight Event - bugfix: 73_AutoShuttersControl: fix little winrec bug then state opened diff --git a/fhem/FHEM/55_DWD_OpenData.pm b/fhem/FHEM/55_DWD_OpenData.pm index 8834a3dcf..a68876f77 100644 --- a/fhem/FHEM/55_DWD_OpenData.pm +++ b/fhem/FHEM/55_DWD_OpenData.pm @@ -11,11 +11,24 @@ DWD Open Data Server. =head1 LICENSE AND COPYRIGHT -Copyright (C) 2018 Jens B. + Copyright (C) 2018 Jens B. -Copyright (C) 2018 JoWiemann (use of HttpUtils instead of LWP::Simple) + All rights reserved -All rights reserved +Use of HttpUtils instead of LWP::Simple: + + Copyright (C) 2018 JoWiemann (FHEM forum post) + see https://forum.fhem.de/index.php/topic,83097.msg761015.html#msg761015 + +Sun position: + + Copyright (C) 2013 Dietmar Ortmann + Copyright (C) 2012 Sebastian Stuecker + see FHEM/59_Twilight.pm + Copyright (C) 2011 aglutz (Symcon forum post) + see http://www.ip-symcon.de/forum/threads/14925-Sonnenstand-berechnen-(Azimut-amp-Elevation) + Copyright (C) 2011 DocZoid (Twilight.tcl) + see http://www.wikimatic.de/wiki/TCLScript:twilight This script is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -46,7 +59,9 @@ use warnings; use Encode; use File::Temp qw(tempfile); use IO::Uncompress::Unzip qw(unzip $UnzipError); +use Math::Trig ':pi'; use POSIX; +use Scalar::Util qw(looks_like_number); use Storable qw(freeze thaw); use Time::HiRes qw(gettimeofday usleep); use Time::Local; @@ -63,7 +78,7 @@ use constant UPDATE_COMMUNEUNIONS => -2; use constant UPDATE_ALL => -3; require Exporter; -our $VERSION = 1.011.001; +our $VERSION = 1.013.000; our @ISA = qw(Exporter); our @EXPORT = qw(GetForecast GetAlerts UpdateAlerts UPDATE_DISTRICTS UPDATE_COMMUNEUNIONS UPDATE_ALL); our @EXPORT_OK = qw(IsCommuneUnionWarncellId); @@ -71,15 +86,15 @@ our @EXPORT_OK = qw(IsCommuneUnionWarncellId); my %forecastPropertyAliases = ( 'TX' => 'Tx', 'TN' => 'Tn', 'TG' => 'Tg', 'TM' => 'Tm' ); my %forecastPropertyPeriods = ( - 'DD' => 1, 'DRR1' => 1, 'E_DD' => 1, 'E_FF' => 1, 'E_PPP' => 1, 'E_Td' => 1, 'E_TTT' => 1, 'FF' => 1, 'FX1' => 1, 'FX3' => 1, 'FX625' => 1, 'FX640' => 1, 'FX655' => 1, 'FXh' => 1, 'FXh25' => 1, 'FXh40' => 1, 'FXh55' => 1, 'N' => 1, 'N05' => 1, 'Neff' => 1, 'Nh' => 1, 'Nl' => 1, 'Nlm' => 1, 'Nm' => 1, 'PPPP' => 1, 'R101' => 1, 'R102' => 1, 'R103' => 1, 'R105' => 1, 'R107' => 1, 'R110' => 1, 'R120' => 1, 'R130' => 1, 'R150' => 1, 'R600' => 1, 'R602' => 1, 'R610' => 1, 'R650' => 1, 'RR1c' => 1, 'RR1o1' => 1, 'RR1u1' => 1, 'RR1w1' => 1, 'RR3c' => 1, 'RR6c' => 1, 'RRL1c' => 1, 'RRS1c' => 1, 'RRS3c' => 1, 'RRad1' => 1, 'Rad1h' => 1, 'RRhc' => 1, 'Rh00' => 1, 'Rh02' => 1, 'Rh10' => 1, 'Rh50' => 1, 'SunD1' => 1, 'SunD3' => 1, 'T5cm' => 1, 'Td' => 1, 'TTT' => 1, 'VV' => 1, 'VV10' => 1, 'W1W2' => 1, 'WPc11' => 1, 'WPc31' => 1, 'WPc61' => 1, 'WPcd1' => 1, 'WPch1' => 1, 'ww' => 1, 'ww3' => 1, 'wwC' => 1, 'wwC6' => 1, 'wwCh' => 1, 'wwD' => 1, 'wwD6' => 1, 'wwDh' => 1, 'wwF' => 1, 'wwF6' => 1, 'wwFh' => 1, 'wwL' => 1, 'wwL6' => 1, 'wwLh' => 1, 'wwM' => 1, 'wwM6' => 1, 'wwMd' => 1, 'wwMh' => 1, 'wwP' => 1, 'wwP6' => 1, 'wwPd' => 1, 'wwPh' => 1, 'wwS' => 1, 'wwS6' => 1, 'wwSh' => 1, 'wwT' => 1, 'wwT6' => 1, 'wwTd' => 1, 'wwTh' => 1, 'wwZ' => 1, 'wwZ6' => 1, 'wwZh' => 1, + 'DD' => 1, 'DRR1' => 1, 'E_DD' => 1, 'E_FF' => 1, 'E_PPP' => 1, 'E_Td' => 1, 'E_TTT' => 1, 'FF' => 1, 'FX1' => 1, 'FX3' => 1, 'FX625' => 1, 'FX640' => 1, 'FX655' => 1, 'FXh' => 1, 'FXh25' => 1, 'FXh40' => 1, 'FXh55' => 1, 'N' => 1, 'N05' => 1, 'Neff' => 1, 'Nh' => 1, 'Nl' => 1, 'Nlm' => 1, 'Nm' => 1, 'PPPP' => 1, 'R101' => 1, 'R102' => 1, 'R103' => 1, 'R105' => 1, 'R107' => 1, 'R110' => 1, 'R120' => 1, 'R130' => 1, 'R150' => 1, 'R600' => 1, 'R602' => 1, 'R610' => 1, 'R650' => 1, 'RR1c' => 1, 'RR1o1' => 1, 'RR1u1' => 1, 'RR1w1' => 1, 'RR3c' => 1, 'RR6c' => 1, 'RRL1c' => 1, 'RRS1c' => 1, 'RRS3c' => 1, 'RRad1' => 1, 'Rad1h' => 1, 'RRhc' => 1, 'Rh00' => 1, 'Rh02' => 1, 'Rh10' => 1, 'Rh50' => 1, 'SunAz' => 1, 'SunD1' => 1, 'SunD3' => 1, 'SunEl' => 1, 'SunUp' => 1, 'T5cm' => 1, 'Td' => 1, 'TTT' => 1, 'VV' => 1, 'VV10' => 1, 'W1W2' => 1, 'WPc11' => 1, 'WPc31' => 1, 'WPc61' => 1, 'WPcd1' => 1, 'WPch1' => 1, 'ww' => 1, 'ww3' => 1, 'wwC' => 1, 'wwC6' => 1, 'wwCh' => 1, 'wwD' => 1, 'wwD6' => 1, 'wwDh' => 1, 'wwF' => 1, 'wwF6' => 1, 'wwFh' => 1, 'wwL' => 1, 'wwL6' => 1, 'wwLh' => 1, 'wwM' => 1, 'wwM6' => 1, 'wwMd' => 1, 'wwMh' => 1, 'wwP' => 1, 'wwP6' => 1, 'wwPd' => 1, 'wwPh' => 1, 'wwS' => 1, 'wwS6' => 1, 'wwSh' => 1, 'wwT' => 1, 'wwT6' => 1, 'wwTd' => 1, 'wwTh' => 1, 'wwZ' => 1, 'wwZ6' => 1, 'wwZh' => 1, 'PEvap' => 24, 'PSd00' => 24, 'PSd30' => 24, 'PSd60' => 24, 'RRdc' => 24, 'RSunD' => 24, 'Rd00' => 24, 'Rd02' => 24, 'Rd10' => 24, 'Rd50' => 24, 'SunD' => 24, 'Tg' => 24, 'Tm' => 24, 'Tn' => 24, 'Tx' => 24 ); my %forecastDefaultProperties = ( - 'Tg' => 1, 'Tn' => 1, 'Tx' => 1, 'DD' => 1, 'FX1' => 1, 'Neff' => 1, 'RR6c' => 1, 'RRhc' => 1, 'Rh00' => 1, 'TTT' => 1, 'ww' => 1 + 'Tg' => 1, 'Tn' => 1, 'Tx' => 1, 'DD' => 1, 'FX1' => 1, 'Neff' => 1, 'RR6c' => 1, 'R600' => 1, 'RRhc' => 1, 'Rh00' => 1, 'TTT' => 1, 'ww' => 1, 'SunUp' => 1 ); -# 1 = temperature in K, 2 = integer value, 3 = wind speed in m/s, 4 = pressure in Pa +# conversion of DWD value to: 1 = temperature in K, 2 = integer value, 3 = wind speed in m/s, 4 = pressure in Pa my %forecastPropertyTypes = ( 'Tx' => 1, 'Tn' => 1, 'Tg' => 1, 'Tm'=> 1, 'Td' => 1, 'T5cm' => 1, 'TTT' => 1, 'DD' => 2, 'Neff' => 2, 'Nh' => 2, 'Nl' => 2, 'Nlm' => 2, 'Nm' => 2, 'Rh00' => 2, 'ww' => 2, 'ww3' => 2, 'WPc11' => 2, 'WPc31' => 2, 'WPc61' => 2, 'WPch1' => 2, 'WPcd1' => 2, @@ -334,7 +349,7 @@ sub Attr(@) { given($attribute) { when("disable") { # enable/disable polling - if ($main::init_done) { + if ($::init_done) { if ($value) { ::RemoveInternalTimer($hash); ::readingsSingleUpdate($hash, 'state', 'disabled', 1); @@ -344,9 +359,25 @@ sub Attr(@) { } } } + when("forecastResolution") { + if (defined($value) && looks_like_number($value) && $value > 0) { + my $oldForecastResolution = ::AttrVal($name, 'forecastResolution', 6); + if ($::init_done && defined($oldForecastResolution) && $oldForecastResolution != $value) { + ::CommandDeleteReading(undef, "$name ^fc.*"); + } + } else { + return "invalid value for forecastResolution (possible values are 1, 3 and 6)"; + } + } + when("forecastStation") { + my $oldForecastStation = ::AttrVal($name, 'forecastStation', undef); + if ($::init_done && defined($oldForecastStation) && $oldForecastStation ne $value) { + ::CommandDeleteReading(undef, "$name ^fc.*"); + } + } when("forecastWW2Text") { - if (!$value) { - ::CommandDeleteReading(undef, "$name fc.*wwd"); + if ($::init_done && !$value) { + ::CommandDeleteReading(undef, "$name ^fc.*wwd\$"); } } when("timezone") { @@ -365,8 +396,17 @@ sub Attr(@) { ::readingsSingleUpdate($hash, 'state', 'defined', 1); ::InternalTimer(gettimeofday() + 3, 'DWD_OpenData::Timer', $hash, 0); } + when("forecastResolution") { + my $oldForecastResolution = ::AttrVal($name, 'forecastResolution', 6); + if ($oldForecastResolution != 6) { + ::CommandDeleteReading(undef, "$name ^fc.*"); + } + } + when("forecastStation") { + ::CommandDeleteReading(undef, "$name ^fc.*"); + } when("forecastWW2Text") { - ::CommandDeleteReading(undef, "$name fc.*wwd"); + ::CommandDeleteReading(undef, "$name ^fc.*wwd\$"); } when("timezone") { $hash->{'.TZ'} = $hash->{FHEM_TZ}; @@ -765,6 +805,110 @@ sub IsCommuneUnionWarncellId($) { || $warncellId == UPDATE_COMMUNEUNIONS || $warncellId == UPDATE_ALL? 1 : 0; } +=head2 SunPosition(;$$$$) + +Calculate the azimuth and elevation of the sun for the given time and location. + +Background: see https://en.wikipedia.org/wiki/Position_of_the_Sun + +=over + +=item * param time: epoch time [s], optional, default: now + +=item * param longitude: geographic longitude [deg], optional, default: global longitude or Frankfurt, Germany + +=item * param latitude: geographic latitude [deg], optional, default: global latitude or Frankfurt, Germany + +=item * return array of azimuth and elevation [deg] + +=back + +=cut + +sub SunPosition(;$$$$) { + my ($time, $longitude, $latitude) = @_; + + if (!defined($time)) { + $time = time(); + } + + if (!defined($longitude) || !defined($latitude)) { + # undefined: use Frankfurt, Germany + $longitude = ::AttrVal("global", "longitude", "8.686"); + $latitude = ::AttrVal("global", "latitude", "50.112"); + } + + # Convert epoch time into its date/time parts + my ($seconds, $minutes, $hours, $day, $month, $year, $wday, $yday, $isdst) = gmtime($time); + $month++; + $year += 100; + + # Convert day time into decimal hours + my $timeAsHours = $hours + $minutes/60.0 + $seconds/3600.0; + + # Calculate difference in days between the current day and + # noon 1 January 2000 Universal Time + my $yearsAfter2000 = $year; + my $aux1 = (14 - ($month))/12; + my $aux2 = $month + 12*$aux1 - 3; + my $aux3 = (153 * $aux2 + 2)/5; + my $aux4 = 365 * ($yearsAfter2000 - $aux1); + my $aux5 = ($yearsAfter2000 - $aux1)/4; + my $elapsedDays = ($day + $aux3 + $aux4 + $aux5 + 59) - 0.5 + $timeAsHours/24.0; + + # Calculate ecliptic coordinates (ecliptic longitude and obliquity of the + # ecliptic in radians but without limiting the angle to 2*pi + # (i.e., the result may be greater than 2*pi) + my $omega = 2.1429 - 0.0010394594 * $elapsedDays; + my $meanLongitude = 4.8950630 + 0.017202791698 * $elapsedDays; # [rad] + my $meanAnomaly = 6.2400600 + 0.0172019699 * $elapsedDays; + my $eclipticLongitude = $meanLongitude + 0.03341607 * sin($meanAnomaly) + 0.00034894 * sin(2*$meanAnomaly ) - 0.0001134 - 0.0000203 * sin($omega); + my $eclipticObliquity = 0.4090928 - 6.2140e-9 * $elapsedDays +0.0000396 * cos($omega); + + # Calculate celestial coordinates (right ascension and declination) in radians + # but without limiting the angle to 2*pi (i.e., the result may be + # greater than 2*pi) + my $sinEclipticLongitude = sin($eclipticLongitude); + my $y1 = cos($eclipticObliquity)*$sinEclipticLongitude; + my $x1 = cos($eclipticLongitude); + my $rightAscension = atan2($y1, $x1); + if ($rightAscension < 0.0) { + $rightAscension = $rightAscension + pi2; + } + my $declination = asin(sin($eclipticObliquity)*$sinEclipticLongitude); + + # Calculate local coordinates (azimuth [deg] and zenith angle [rad]) + my $rad = (pi/180); + my $greenwichMeanSiderealTime = 6.6974243242 + 0.0657098283*$elapsedDays + $timeAsHours; + my $localMeanSiderealTime = ($greenwichMeanSiderealTime*15 + $longitude)*$rad; + my $hourAngle = $localMeanSiderealTime - $rightAscension; + my $cosHourAngle = cos($hourAngle); + my $latitudeRadians = $latitude*$rad; + my $cosLatitude = cos($latitudeRadians); + my $sinLatitude = sin($latitudeRadians); + my $zenithAngle = (acos($cosLatitude*$cosHourAngle*cos($declination) + sin($declination)*$sinLatitude)); + my $y = -sin($hourAngle); + my $x = tan($declination )*$cosLatitude - $sinLatitude*$cosHourAngle; + my $azimuth = atan2($y, $x); + if ($azimuth < 0.0) { + $azimuth = $azimuth + pi2; + } + $azimuth = $azimuth/$rad; + $azimuth = sprintf("%0.1f", $azimuth); # round(1) + + # Parallax correction of zenith angle [deg] + my $meanEarthRadius = 6371.01; # [km] + my $astronomicalUnit = 149597890; # [km] + my $parallax = ($meanEarthRadius/$astronomicalUnit)*sin($zenithAngle); + $zenithAngle = ($zenithAngle + $parallax)/$rad; + + # Elevation [deg] + my $elevation = 90 - $zenithAngle; + $elevation = sprintf("%0.1f", $elevation); # round(1) + + return ($azimuth, $elevation); +} + =head2 RotateForecast($$;$) =over @@ -798,7 +942,7 @@ sub RotateForecast($$;$) my $stationChanged = ::ReadingsVal($name, 'fc_station', '') ne $station; if ($stationChanged) { # different station, delete all existing readings - ::CommandDeleteReading(undef, "$name fc.*"); + ::CommandDeleteReading(undef, "$name ^fc.*"); $daysAvailable = 0; } elsif (defined($oldToday)) { # same station, shift existing readings @@ -843,12 +987,12 @@ sub RotateForecast($$;$) } # delete existing readings of all days that have not been written for (my $d=($daysAvailable - $daysForward); $d<$daysAvailable; $d++) { - ::CommandDeleteReading(undef, "$name fc".$d."_.*"); + ::CommandDeleteReading(undef, "$name ^fc".$d."_.*"); } $daysAvailable -= $daysForward; } else { - # nothing to shift, delete existing readings - ::CommandDeleteReading(undef, "$name fc.*"); + # nothing remains after shifting, delete existing day readings + ::CommandDeleteReading(undef, "$name ^fc\\d+.*"); $daysAvailable = 0; } } @@ -994,6 +1138,7 @@ sub ProcessForecast($$$) my %forecast; my $relativeDay = 0; + my @coordinates; eval { if (defined($httpError) && length($httpError) > 0) { die "error retrieving URL '$url': $httpError"; @@ -1085,6 +1230,7 @@ sub ProcessForecast($$$) # extract time data my %timeProperties; + my ($longitude, $latitude); my $placemarkNodeList = $dom->getElementsByLocalName('Placemark'); if ($placemarkNodeList->size()) { my $placemarkNode = $placemarkNodeList->get_node(1); @@ -1104,7 +1250,7 @@ sub ProcessForecast($$$) my $textContent = $extendedDataChildNode->nonBlankChildNodes()->get_node(1)->textContent(); $textContent =~ s/^\s+|\s+$//g; # trim outside $textContent =~ s/\s+/ /g; # trim inside - my @values = split(' ',$textContent); + my @values = split(' ', $textContent); $timeProperties{$elementName} = \@values; } } @@ -1112,9 +1258,33 @@ sub ProcessForecast($$$) } elsif ($placemarkChildNode->nodeName() eq 'kml:Point') { my $coordinates = $placemarkChildNode->nonBlankChildNodes()->get_node(1)->textContent(); $header{coordinates} = $coordinates; + ($longitude, $latitude) = split(',', $coordinates); } } } + + # calculate sun position properties for each timestamp + if (defined($longitude) && defined($latitude)) { + my @azimuths; + my @elevations; + my @sunups; + foreach my $timestamp (@timestamps) { + my ($azimuth, $elevation) = SunPosition($timestamp, $longitude, $latitude); + push(@azimuths, $azimuth); # [deg] + push(@elevations, $elevation); # [deg] + push(@sunups, $elevation >= -12? 1 : 0); # nautical twilight + } + if (defined($selectedProperties{SunAz})) { + $timeProperties{SunAz} = \@azimuths; + } + if (defined($selectedProperties{SunEl})) { + $timeProperties{SunEl} = \@elevations; + } + if (defined($selectedProperties{SunUp})) { + $timeProperties{SunUp} = \@sunups; + } + } + $forecast{timeProperties} = \%timeProperties; } $forecast{header} = \%header; @@ -1316,7 +1486,7 @@ sub UpdateForecast($$) my $forecastTime = $timestamps->[$i]; my ($fcSec, $fcMin, $fcHour, $fcMday, $fcMon, $fcYear, $fcWday, $fcYday, $fcIsdst) = Localtime($hash, $forecastTime); my $forecastDate = Timelocal($hash, 0, 0, 0, $fcMday, $fcMon, $fcYear); - $relativeDay = sprintf("%.0f", ($forecastDate - $today)/(24*60*60)); # Perl equivalent for round() + $relativeDay = sprintf("%.0f", ($forecastDate - $today)/(24*60*60)); # round() if ($relativeDay > $forecastDays) { # max. number of days processed, done last; @@ -1352,7 +1522,7 @@ sub UpdateForecast($$) if ($forecastPropertyType == 1) { $value -= 273.15; # K -> °C if (length($value) > 6) { - $value = sprintf('%0.2f', $value); # round to compensate floating point granularity + $value = sprintf('%0.2f', $value); # round(2) to compensate floating point granularity } } elsif ($forecastPropertyType == 2) { @@ -1388,7 +1558,7 @@ sub UpdateForecast($$) if ($relativeDay >= 0 && $daysAvailable > $relativeDay + 1) { ::Log3 $name, 5, "$name: deleting days with index " . ($relativeDay + 1) . " to " . ($daysAvailable - 1); for (my $d=($relativeDay + 1); $d<$daysAvailable; $d++) { - ::CommandDeleteReading(undef, "$name fc".$d."_.*"); + ::CommandDeleteReading(undef, "$name ^fc".$d."_.*"); } } @@ -1860,7 +2030,7 @@ sub UpdateAlerts($$) my $name = $hash->{NAME}; # delete existing alert readings - ::CommandDeleteReading(undef, "$name a_.*"); + ::CommandDeleteReading(undef, "$name ^(?!a_count|a_state|a_time)a_.*"); ::readingsBeginUpdate($hash); @@ -1894,6 +2064,14 @@ sub UpdateAlerts($$) ::readingsBulkUpdate($hash, 'a_state', 'updated'); } + # prepare processing + my $alertExcludeEvents = ::AttrVal($name, 'alertExcludeEvents', undef); + my @excludeEventsList = split(',', $alertExcludeEvents) if (defined($alertExcludeEvents)); + foreach my $excludeEvent (@excludeEventsList) { + $excludeEvent =~ s/^\s+|\s+$//g; # trim + } + my %excludeEvents = map { $_ => 1 } @excludeEventsList; + # order alerts by onset my $alerts = $alertsData[$communeUnion]; my @identifiers = sort { $alerts->{$a}->{onset} <=> $alerts->{$b}->{onset} } keys(%{$alerts}); @@ -1902,8 +2080,8 @@ sub UpdateAlerts($$) # find alert for selected warncell my $areaIndex = 0; foreach my $wcId (@{$alert->{warncellid}}) { - if ($wcId == $warncellId) { - # alert found, create readings + if ($wcId == $warncellId && !(lc($alert->{severity}) eq 'minor' && defined($excludeEvents{$alert->{eventCode}}))) { + # alert found that is not on the exclude list, create readings my $prefix = 'a_'.$index.'_'; ::readingsBulkUpdate($hash, $prefix.'category', $alert->{category}); ::readingsBulkUpdate($hash, $prefix.'event', $alert->{eventCode}); @@ -1974,8 +2152,8 @@ sub DWD_OpenData_Initialize($) { $hash->{GetFn} = 'DWD_OpenData::Get'; $hash->{AttrList} = 'disable:0,1 ' - .'forecastStation forecastDays forecastProperties forecastResolution:3,6 forecastWW2Text:0,1 ' - .'alertArea alertLanguage:DE,EN ' + .'forecastStation forecastDays forecastProperties forecastResolution:1,3,6 forecastWW2Text:0,1 ' + .'alertArea alertLanguage:DE,EN alertExcludeEvents ' .'timezone ' .$readingFnAttributes; } @@ -1988,6 +2166,22 @@ sub DWD_OpenData_Initialize($) { # # CHANGES # +# 23.02.2019 (version 1.13.0) jensb +# feature: new sun position readings SunAz, SunEl and SunUp +# +# 10.02.2019 (version 1.12.3) jensb +# feature: do not delete readings a_count, a_state, a_time when updating alerts +# +# 28.12.2018 (version 1.12.2) jensb +# bugfix: modified regexp to delete forecast readings on attribute change +# +# 23.12.2018 (version 1.12.1) jensb +# feature: new attribute alertExcludeEvents +# feature: delete forecast readings if attribute forecastResolution or forecastStation are changed +# +# 20.12.2018 (version 1.12.0) jensb +# feature: enable 1h forecast resolution +# # 02.12.2018 (version 1.11.0) jensb # feature: async processing of forecast enhanced (HttpUtils_NonblockingGet replaced by BlockingCall) to further unload FHEM process # feature: staggered update of forecast and alert to spread load @@ -2073,7 +2267,7 @@ sub DWD_OpenData_Initialize($) {
forecastResolution
attributeforecastProperties
and reduce the event processing overhead by setting the attribute event-on-update-reading
to a small list of important reading (e.g. to state,fc_time,a_time
). a_<index>_<property>
forecastProperties
to the ones you actually use, b) the attribute forecastResolution
to the highest value suitable for your purposes and c) the attribute forecastDays
to the lowest number suitable for your purposes. To further reduce the event processing overhead you can set the attribute event-on-update-reading
to a small list of important reading that really need events (e.g. state,fc_state,a_state
). For almost the same reason be selective when creating a log device. If you use wildcards for all readings without filtering either at the source device with readingFnAttributes or at the destination device with a regexp you will get significant extra file IO when the readings are updated and quite a lot of data.