mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-03-09 20:57:11 +00:00
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
This commit is contained in:
parent
8fb01308c2
commit
b84d5626c0
@ -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
|
||||
|
@ -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($) {
|
||||
<ul>
|
||||
The Deutsche Wetterdienst (DWD) provides public weather related data via its <a href="https://www.dwd.de/DE/leistungen/opendata/opendata.html">Open Data Server</a>. Any usage of the service and the data provided by the DWD is subject to the usage conditions on the Open Data Server webpage. An overview of the available content can be found at <a href="https://www.dwd.de/DE/leistungen/opendata/help/inhalt_allgemein/opendata_content_de_en_xls.xls">OpenData_weather_content.xls</a>. <br><br>
|
||||
|
||||
This modules provides two elements of the available data:
|
||||
This module provides two elements of the available data:
|
||||
<ul> <br>
|
||||
<li>weather forecasts:
|
||||
<a href="https://opendata.dwd.de/weather/local_forecasts/mos/MOSMIX_L/single_stations/">Total lists of local forecasts of WMO, national and interpolated stations, all variables, 3, 9, 15, 21 UTC</a>. More than 70 properties are available for worldwide POIs and the German DWD network. This data typically spans 10 days and is updated by the DWD every 6 hours.<br><br>
|
||||
@ -2155,18 +2349,21 @@ sub DWD_OpenData_Initialize($) {
|
||||
<ul> <br>
|
||||
<li>forecastStation <station code>, default: none<br>
|
||||
Setting forecastStation enables automatic updates every hour.
|
||||
The station code is either a 5 digit WMO station code or an alphanumeric DWD station code from the <a href="https://www.dwd.de/DE/leistungen/met_verfahren_mosmix/mosmix_stationskatalog.pdf">MOSMIX station catalogue</a>.
|
||||
The station code is either a 5 digit WMO station code or an alphanumeric DWD station code from the <a href="https://www.dwd.de/DE/leistungen/met_verfahren_mosmix/mosmix_stationskatalog.pdf">MOSMIX station catalogue</a>.<br>
|
||||
Note: When value is changed all existing forecast readings will be deleted.
|
||||
</li><br>
|
||||
<li>forecastDays <n>, default: 6<br>
|
||||
Limits number of forecast days. Setting 0 will still provide forecast data for today. The maximum value is 9 (for today and 9 future days).
|
||||
</li><br>
|
||||
<li>forecastResolution {3|6}, default: 6 h<br>
|
||||
Time resolution (number of hours between 2 samples).
|
||||
<li>forecastResolution {1|3|6}, default: 6 h<br>
|
||||
Time resolution (number of hours between 2 samples).<br>
|
||||
Note: When value is changed all existing forecast readings will be deleted.
|
||||
</li><br>
|
||||
<li>forecastProperties [<p1>[,<p2>]...] , default: Tx, Tn, Tg, TTT, DD, FX1, Neff, RR6c, RRhc, Rh00, ww<br>
|
||||
A list of the properties available can be found <a href="https://opendata.dwd.de/weather/lib/MetElementDefinition.xml">here</a>.
|
||||
If you remove a property from the list existing readings must be deleted manually in continuous mode.<br>
|
||||
Note: Not all properties are available for all stations and for all hours.
|
||||
A list of the properties available can be found <a href="https://opendata.dwd.de/weather/lib/MetElementDefinition.xml">here</a>.<br>
|
||||
Notes:<br>
|
||||
- Not all properties are available for all stations and for all hours.<br>
|
||||
- If you remove a property from the list then already existing readings must be deleted manually in continuous mode.<br>
|
||||
</li><br>
|
||||
<li>forecastWW2Text {0|1}, default: 0<br>
|
||||
Create additional wwd readings containing the weather code as a descriptive text in German language.
|
||||
@ -2176,11 +2373,15 @@ sub DWD_OpenData_Initialize($) {
|
||||
<b>alert</b> related:
|
||||
<ul> <br>
|
||||
<li>alertArea <warncell id>, default: none<br>
|
||||
Setting alertArea enables automatic updates of the alerts cache every 15 minutes.
|
||||
Setting alertArea enables automatic updates of the alerts cache every 15 minutes.<br>
|
||||
A warncell id is a 9 digit numeric value from the <a href="https://www.dwd.de/DE/leistungen/opendata/help/warnungen/cap_warncellids_csv.csv">Warncell-IDs for CAP alerts catalogue</a>. Supported ids start with 8 (communeunion), 1 and 9 (district) or 5 (coast). To verify that alerts are provided for the warncell id you selected you should consult another source, wait for an alert situation and compare.
|
||||
</li>
|
||||
<li>alertLanguage [DE|EN], default: DE<br>
|
||||
Language of descriptive alert properties.</a>.
|
||||
Language of descriptive alert properties.
|
||||
</li>
|
||||
<li>alertExcludeEvents <event code>, default: none<br>
|
||||
Comma separated list of numeric events codes for which no alerts should be created.<br>
|
||||
Only minor alerts may be suppressed. Use at your own risk!
|
||||
</li>
|
||||
</ul> <br><br>
|
||||
|
||||
@ -2197,18 +2398,18 @@ sub DWD_OpenData_Initialize($) {
|
||||
<ul>
|
||||
<li>day - relative day (0 .. 9) based on the timezone attribute where 0 is today</li><br>
|
||||
|
||||
<li>sample - relative time (0 .. 3 or 7) equivalent to multiples of 6 or 3 hours UTC depending on the forecastHours attribute</li><br>
|
||||
<li>sample - relative time (0 .. 3, 7 or 23) equivalent to multiples of 6, 3 or 1 hours UTC depending on the <code>forecastResolution</code> attribute</li><br>
|
||||
|
||||
<li>day properties (typically for 06:00 station time, see raw data of station for time relation)
|
||||
<li>day properties (typically for 06:00 station time, see raw data of station for actual time relation)
|
||||
<ul>
|
||||
<li>date - date based on the timezone attribute</li>
|
||||
<li>weekday - abbreviated weekday based on the timezone attribute in the language of your FHEM system</li>
|
||||
<li>Tn [°C] - minimum temperature of previous 24 hours</li>
|
||||
<li>Tx [°C] - maximum temperature of previous 24 hours (typically for 18:00 station time)</li>
|
||||
<li>Tn [°C] - minimum temperature of previous 12 hours</li>
|
||||
<li>Tx [°C] - maximum temperature of previous 12 hours (typically at 18:00 station time)</li>
|
||||
<li>Tm [°C] - average temperature of previous 24 hours</li>
|
||||
<li>Tg [°C] - minimum temperature 5 cm above ground of previous 24 hours</li>
|
||||
<li>Tg [°C] - minimum temperature 5 cm above ground of previous 12 hours</li>
|
||||
<li>PEvap [kg/m2] - evapotranspiration of previous 24 hours</li>
|
||||
<li>SunD [s] - total sunshine duration of previous 24 hours</li>
|
||||
<li>SunD [s] - total sunshine duration of previous day</li>
|
||||
</ul>
|
||||
</li><br>
|
||||
|
||||
@ -2220,6 +2421,10 @@ sub DWD_OpenData_Initialize($) {
|
||||
<li>DD [°] - average wind direction 10 m above ground</li>
|
||||
<li>FF [km/h] - average wind speed 10 m above ground</li>
|
||||
<li>FX1 [km/h] - maximum wind speed in the last hour</li>
|
||||
<li>SunD1 [s] - sunshine duration in the last hour</li>
|
||||
<li>SunD3 [s] - sunshine duration in the last 3 hours</li>
|
||||
<li>RR1c [kg/m2] - precipitation amount in the last hour</li>
|
||||
<li>RR3c [kg/m2] - precipitation amount in the last 3 hours</li>
|
||||
<li>RR6c [kg/m2] - precipitation amount in the last 6 hours</li>
|
||||
<li>R600 [%] - probability of rain in the last 6 hours</li>
|
||||
<li>RRhc [kg/m2] - precipitation amount in the last 12 hours</li>
|
||||
@ -2232,10 +2437,18 @@ sub DWD_OpenData_Initialize($) {
|
||||
<li>Neff [%] - effective cloud cover</li>
|
||||
<li>Nl [%] - lower level cloud cover below 2000 m</li>
|
||||
<li>Nm [%] - medium level cloud cover below 7000 m</li>
|
||||
<li>Nh [%] - high level cloud cover obove 7000 m</li>
|
||||
<li>Nh [%] - high level cloud cover above 7000 m</li>
|
||||
<li>PPPP [hPa] - pressure equivalent at sea level</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li>extra hour properties (not provided by the DWD but calculated by the FHEM module)
|
||||
<ul>
|
||||
<li>SunAz [°] - sun azimuth</li>
|
||||
<li>SunEl [°] - sun elevation</li>
|
||||
<li>SunUp - sun up (0/1) based on nautical twilight (-12 °)</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul> <br>
|
||||
|
||||
Additionally there are global forecast readings:
|
||||
@ -2244,14 +2457,12 @@ sub DWD_OpenData_Initialize($) {
|
||||
<li>fc_state - state of the last forecast update, possible values are 'updated' and 'error: ...'</li>
|
||||
<li>fc_station - forecast station code (WMO or DWD)</li>
|
||||
<li>fc_description - station description</li>
|
||||
<li>fc_coordinates - world coordinat and height of station</li>
|
||||
<li>fc_coordinates - world coordinate and height of station</li>
|
||||
<li>fc_time - time the forecast was issued based on the timezone attribute</li>
|
||||
<li>fc_copyright - legal information, must be displayed with forecast data, see DWD usage conditions</li>
|
||||
</ul>
|
||||
</ul> <br>
|
||||
|
||||
Note that depending on your device configuration each forecast consists of quite a lot of readings and each reading update will cause a FHEM event that needs to be processed. Depending on your hardware and your FHEM configuration this will take several hundred milliseconds. If you need to improve overall performance you can limit the number of readings created by setting the attribute <code>forecastProperties</code> and reduce the event processing overhead by setting the attribute <code>event-on-update-reading</code> to a small list of important reading (e.g. <code>to state,fc_time,a_time</code>). <br><br>
|
||||
|
||||
The <b>alert</b> readings are ordered by onset and are build like this: <br><br>
|
||||
|
||||
<code>a_<index>_<property></code> <br><br>
|
||||
@ -2298,7 +2509,11 @@ sub DWD_OpenData_Initialize($) {
|
||||
|
||||
Note that all alert readings are completely replaced and reindexed with each update! <br><br>
|
||||
|
||||
Further information regarding the alert properties can be found in the documentation of the <a href="https://www.dwd.de/DE/leistungen/opendata/help/warnungen/cap_dwd_profile_de_pdf.pdf">CAP DWS Profile</a>. <br>
|
||||
Further information regarding the alert properties can be found in the documentation of the <a href="https://www.dwd.de/DE/leistungen/opendata/help/warnungen/cap_dwd_profile_de_pdf.pdf">CAP DWS Profile</a>. <br><br>
|
||||
|
||||
<b>Performance</b> <br><br>
|
||||
|
||||
Note that depending on your device configuration each forecast consists of quite a lot of readings and each reading update will cause a FHEM event that needs to be processed. Depending on your hardware and your FHEM configuration this will take several hundred milliseconds. If you need to improve overall performance you can limit the number of readings created by setting a) the attribute <code>forecastProperties</code> to the ones you actually use, b) the attribute <code>forecastResolution</code> to the highest value suitable for your purposes and c) the attribute <code>forecastDays</code> to the lowest number suitable for your purposes. To further reduce the event processing overhead you can set the attribute <code>event-on-update-reading</code> to a small list of important reading that really need events (e.g. <code>state,fc_state,a_state</code>). 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 <a href="#readingFnAttributes">readingFnAttributes</a> 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. <br>
|
||||
|
||||
</ul> <br>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user