mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-01-31 12:49:34 +00:00
55_DWD_OpenData: revert to 1.16.3
git-svn-id: https://svn.fhem.de/fhem/trunk@28557 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
3cd6785fdc
commit
964414bb9b
@ -20,10 +20,6 @@ Use of HttpUtils instead of LWP::Simple:
|
|||||||
Copyright (C) 2018 JoWiemann
|
Copyright (C) 2018 JoWiemann
|
||||||
see https://forum.fhem.de/index.php/topic,83097.msg761015.html#msg761015
|
see https://forum.fhem.de/index.php/topic,83097.msg761015.html#msg761015
|
||||||
|
|
||||||
MOSMIX S forecast data support:
|
|
||||||
|
|
||||||
Copyright (C) 2024 DS_Starter + Jens B.
|
|
||||||
|
|
||||||
Sun position:
|
Sun position:
|
||||||
|
|
||||||
Copyright (c) Plataforma Solar de Almerýa, Spain
|
Copyright (c) Plataforma Solar de Almerýa, Spain
|
||||||
@ -621,7 +617,7 @@ use constant UPDATE_COMMUNEUNIONS => -2;
|
|||||||
use constant UPDATE_ALL => -3;
|
use constant UPDATE_ALL => -3;
|
||||||
|
|
||||||
require Exporter;
|
require Exporter;
|
||||||
our $VERSION = '1.017000';
|
our $VERSION = '1.016003';
|
||||||
our @ISA = qw(Exporter);
|
our @ISA = qw(Exporter);
|
||||||
our @EXPORT = qw(GetForecast GetAlerts UpdateAlerts UPDATE_DISTRICTS UPDATE_COMMUNEUNIONS UPDATE_ALL);
|
our @EXPORT = qw(GetForecast GetAlerts UpdateAlerts UPDATE_DISTRICTS UPDATE_COMMUNEUNIONS UPDATE_ALL);
|
||||||
our @EXPORT_OK = qw(IsCommuneUnionWarncellId);
|
our @EXPORT_OK = qw(IsCommuneUnionWarncellId);
|
||||||
@ -633,13 +629,9 @@ my %forecastPropertyPeriods = (
|
|||||||
'PEvap' => 24, 'PSd00' => 24, 'PSd30' => 24, 'PSd60' => 24, 'RRdc' => 24, 'RSunD' => 24, 'Rd00' => 24, 'Rd02' => 24, 'Rd10' => 24, 'Rd50' => 24, 'SunD' => 24, 'SunRise' => 24, 'SunSet' => 24, 'Tg' => 24, 'Tm' => 24, 'Tn' => 24, 'Tx' => 24
|
'PEvap' => 24, 'PSd00' => 24, 'PSd30' => 24, 'PSd60' => 24, 'RRdc' => 24, 'RSunD' => 24, 'Rd00' => 24, 'Rd02' => 24, 'Rd10' => 24, 'Rd50' => 24, 'SunD' => 24, 'SunRise' => 24, 'SunSet' => 24, 'Tg' => 24, 'Tm' => 24, 'Tn' => 24, 'Tx' => 24
|
||||||
);
|
);
|
||||||
|
|
||||||
my %forecastDefaultPropertiesS = (
|
my %forecastDefaultProperties = (
|
||||||
'Tn' => 1, 'Tx' => 1, 'DD' => 1, 'FX1' => 1, 'Neff' => 1, 'RR1c' => 1, 'R602' => 1, 'RR3c' => 1, 'Rh00' => 1, 'TTT' => 1, 'ww' => 1, 'SunUp' => 1
|
|
||||||
);
|
|
||||||
|
|
||||||
my %forecastDefaultPropertiesL = (
|
|
||||||
'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
|
'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
|
||||||
);
|
);
|
||||||
|
|
||||||
# conversion of DWD value to: 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 = (
|
my %forecastPropertyTypes = (
|
||||||
@ -802,13 +794,7 @@ sub Define {
|
|||||||
$hash->{'.TZ'} = ::AttrVal($hash, 'timezone', $hash->{FHEM_TZ});
|
$hash->{'.TZ'} = ::AttrVal($hash, 'timezone', $hash->{FHEM_TZ});
|
||||||
|
|
||||||
::readingsSingleUpdate($hash, 'state', ::IsDisabled($name)? 'disabled' : 'defined', 1);
|
::readingsSingleUpdate($hash, 'state', ::IsDisabled($name)? 'disabled' : 'defined', 1);
|
||||||
|
::InternalTimer(gettimeofday() + 3, 'DWD_OpenData::Timer', $hash, 0);
|
||||||
# @TODO randomize start of next update check to distribute load cause by mulitple module instances
|
|
||||||
my $nextUpdate = gettimeofday() + int(rand(480));
|
|
||||||
::readingsSingleUpdate($hash, 'nextUpdate', ::FmtTime($nextUpdate), 1);
|
|
||||||
::InternalTimer($nextUpdate, 'DWD_OpenData::Timer', $hash);
|
|
||||||
|
|
||||||
$hash->{'.firstRun'} = 1;
|
|
||||||
|
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
@ -927,24 +913,12 @@ sub Attr {
|
|||||||
return "invalid value for forecastResolution (possible values are 1, 3 and 6)";
|
return "invalid value for forecastResolution (possible values are 1, 3 and 6)";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
when ("downloadTimeout") {
|
|
||||||
unless ($value =~ /^[0-9]+$/x) {
|
|
||||||
return qq{invalid value for downloadTimeout. Use only figures 0-9!};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
when ("forecastStation") {
|
when ("forecastStation") {
|
||||||
my $oldForecastStation = ::AttrVal($name, 'forecastStation', undef);
|
my $oldForecastStation = ::AttrVal($name, 'forecastStation', undef);
|
||||||
if ($::init_done && defined($oldForecastStation) && $oldForecastStation ne $value) {
|
if ($::init_done && defined($oldForecastStation) && $oldForecastStation ne $value) {
|
||||||
::CommandDeleteReading(undef, "$name ^fc.*");
|
::CommandDeleteReading(undef, "$name ^fc.*");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# @TODO check attribute name
|
|
||||||
when ("forecastDataPrecision") {
|
|
||||||
my $oldForecastProcess = ::AttrVal($name, 'forecastDataPrecision', 'low');
|
|
||||||
if ($::init_done && $oldForecastProcess ne $value) {
|
|
||||||
::CommandDeleteReading(undef, "$name ^fc.*");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
when ("forecastWW2Text") {
|
when ("forecastWW2Text") {
|
||||||
if ($::init_done && !$value) {
|
if ($::init_done && !$value) {
|
||||||
::CommandDeleteReading(undef, "$name ^fc.*wwd\$");
|
::CommandDeleteReading(undef, "$name ^fc.*wwd\$");
|
||||||
@ -975,10 +949,6 @@ sub Attr {
|
|||||||
when ("forecastStation") {
|
when ("forecastStation") {
|
||||||
::CommandDeleteReading(undef, "$name ^fc.*");
|
::CommandDeleteReading(undef, "$name ^fc.*");
|
||||||
}
|
}
|
||||||
# @TODO check attribute name
|
|
||||||
when ("forecastDataPrecision") {
|
|
||||||
::CommandDeleteReading(undef, "$name ^fc.*");
|
|
||||||
}
|
|
||||||
when ("forecastWW2Text") {
|
when ("forecastWW2Text") {
|
||||||
::CommandDeleteReading(undef, "$name ^fc.*wwd\$");
|
::CommandDeleteReading(undef, "$name ^fc.*wwd\$");
|
||||||
}
|
}
|
||||||
@ -1108,14 +1078,8 @@ sub Timer {
|
|||||||
my ($tSec, $tMin, $tHour, $tMday, $tMon, $tYear, $tWday, $tYday, $tIsdst) = gmtime($time);
|
my ($tSec, $tMin, $tHour, $tMday, $tMon, $tYear, $tWday, $tYday, $tIsdst) = gmtime($time);
|
||||||
my $actQuarter = int($tMin/15);
|
my $actQuarter = int($tMin/15);
|
||||||
|
|
||||||
# cancel periodic timer
|
if ($actQuarter == 0 && !(defined($hash->{".fetchAlerts"}) && $hash->{".fetchAlerts"})) {
|
||||||
::RemoveInternalTimer($hash);
|
# preset: try to fetch alerts immediately
|
||||||
|
|
||||||
my $firstRun = delete $hash->{'.firstRun'} // 0;
|
|
||||||
my $forecastQuarter = ::AttrVal($name, 'forecastDataPrecision', 'low') eq 'low' ? 0 : 2;
|
|
||||||
my $fetchAlerts = defined($hash->{".fetchAlerts"}) && $hash->{".fetchAlerts"};
|
|
||||||
if ($firstRun || ($actQuarter == $forecastQuarter && !$fetchAlerts)) {
|
|
||||||
# preset: try to fetch alerts after forecast
|
|
||||||
$hash->{".fetchAlerts"} = 1;
|
$hash->{".fetchAlerts"} = 1;
|
||||||
my $forecastStation = ::AttrVal($name, 'forecastStation', undef);
|
my $forecastStation = ::AttrVal($name, 'forecastStation', undef);
|
||||||
if (defined($forecastStation)) {
|
if (defined($forecastStation)) {
|
||||||
@ -1131,8 +1095,7 @@ sub Timer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$fetchAlerts = defined($hash->{".fetchAlerts"}) && $hash->{".fetchAlerts"};
|
if ($actQuarter > 0 || (defined($hash->{".fetchAlerts"}) && $hash->{".fetchAlerts"})) {
|
||||||
if ($actQuarter > 0 || $fetchAlerts) {
|
|
||||||
my $warncellId = ::AttrVal($name, 'alertArea', undef);
|
my $warncellId = ::AttrVal($name, 'alertArea', undef);
|
||||||
if (defined($warncellId)) {
|
if (defined($warncellId)) {
|
||||||
# skip update if already in progress
|
# skip update if already in progress
|
||||||
@ -1148,10 +1111,10 @@ sub Timer {
|
|||||||
$hash->{".fetchAlerts"} = $actQuarter < 3;
|
$hash->{".fetchAlerts"} = $actQuarter < 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
# reschedule next run to 5 .. 600 seconds past next quarter
|
# reschedule next run for 5 seconds past next quarter
|
||||||
my $nextUpdate = timegm(0, $actQuarter*15, $tHour, $tMday, $tMon, $tYear) + 905 + int(rand(595));
|
::RemoveInternalTimer($hash);
|
||||||
::readingsSingleUpdate($hash, 'nextUpdate', ::FmtTime($nextUpdate), 1);
|
my $nextTime = timegm(0, $actQuarter*15, $tHour, $tMday, $tMon, $tYear) + 905;
|
||||||
::InternalTimer($nextUpdate, 'DWD_OpenData::Timer', $hash);
|
::InternalTimer($nextTime, 'DWD_OpenData::Timer', $hash);
|
||||||
|
|
||||||
::Log3 $name, 5, "$name: Timer END";
|
::Log3 $name, 5, "$name: Timer END";
|
||||||
}
|
}
|
||||||
@ -1617,8 +1580,7 @@ sub GetForecast {
|
|||||||
# kill old blocking call
|
# kill old blocking call
|
||||||
::BlockingKill($hash->{".forecastBlockingCall"});
|
::BlockingKill($hash->{".forecastBlockingCall"});
|
||||||
}
|
}
|
||||||
my $timeout = ::AttrVal($name, 'downloadTimeout', 60);
|
$hash->{".forecastBlockingCall"} = ::BlockingCall("DWD_OpenData::GetForecastStart", $hash, "DWD_OpenData::GetForecastFinish", 30, "DWD_OpenData::GetForecastAbort", $hash);
|
||||||
$hash->{".forecastBlockingCall"} = ::BlockingCall("DWD_OpenData::GetForecastStart", $hash, "DWD_OpenData::GetForecastFinish", $timeout, "DWD_OpenData::GetForecastAbort", $hash);
|
|
||||||
|
|
||||||
$hash->{forecastUpdating} = time();
|
$hash->{forecastUpdating} = time();
|
||||||
|
|
||||||
@ -1659,21 +1621,13 @@ sub GetForecastStart {
|
|||||||
usleep(100);
|
usleep(100);
|
||||||
|
|
||||||
# get forecast for station from DWD server
|
# get forecast for station from DWD server
|
||||||
my $url;
|
my $url = 'https://opendata.dwd.de/weather/local_forecasts/mos/MOSMIX_L/single_stations/' . $station . '/kml/MOSMIX_L_LATEST_' . $station . '.kmz ';
|
||||||
my $dataPrecision = ::AttrVal($name, 'forecastDataPrecision', 'low') eq 'high' ? 'S' : 'L';
|
|
||||||
if ($dataPrecision eq 'S') {
|
|
||||||
$url = "https://opendata.dwd.de/weather/local_forecasts/mos/MOSMIX_S/all_stations/kml/MOSMIX_S_LATEST_240.kmz";
|
|
||||||
} else {
|
|
||||||
$url = 'https://opendata.dwd.de/weather/local_forecasts/mos/MOSMIX_L/single_stations/' . $station . '/kml/MOSMIX_L_LATEST_' . $station . '.kmz ';
|
|
||||||
}
|
|
||||||
|
|
||||||
my $param = {
|
my $param = {
|
||||||
url => $url,
|
url => $url,
|
||||||
method => "GET",
|
method => "GET",
|
||||||
timeout => 10,
|
timeout => 10,
|
||||||
hash => $hash,
|
hash => $hash,
|
||||||
station => $station,
|
station => $station
|
||||||
dataPrecision => $dataPrecision
|
|
||||||
};
|
};
|
||||||
::Log3 $name, 5, "$name: GetForecastStart START (PID $$): $url";
|
::Log3 $name, 5, "$name: GetForecastStart START (PID $$): $url";
|
||||||
my ($httpError, $fileContent) = ::HttpUtils_BlockingGet($param);
|
my ($httpError, $fileContent) = ::HttpUtils_BlockingGet($param);
|
||||||
@ -1686,50 +1640,6 @@ sub GetForecastStart {
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
=head2 getStationPos($$$)
|
|
||||||
|
|
||||||
=over
|
|
||||||
|
|
||||||
=item * param name: name of DWD_OpenData device
|
|
||||||
|
|
||||||
=item * param station: name of station to search for
|
|
||||||
|
|
||||||
=item * param placemarkNodeList: XML node to search
|
|
||||||
|
|
||||||
=item * index in list (1 ..) or 0 if not found
|
|
||||||
|
|
||||||
=back
|
|
||||||
|
|
||||||
find XML node of station
|
|
||||||
|
|
||||||
=cut
|
|
||||||
|
|
||||||
sub getStationPos {
|
|
||||||
my $name = shift;
|
|
||||||
my $station = shift;
|
|
||||||
my $placemarkNodeList = shift;
|
|
||||||
|
|
||||||
my $pos = 0;
|
|
||||||
my $listSize = $placemarkNodeList->size();
|
|
||||||
for my $n (1..$listSize) {
|
|
||||||
my $pn = $placemarkNodeList->get_node($n);
|
|
||||||
for my $placemarkChildNode ($pn->nonBlankChildNodes()) {
|
|
||||||
if ($placemarkChildNode->nodeName() eq 'kml:name') {
|
|
||||||
my $stname = $placemarkChildNode->textContent();
|
|
||||||
if ($stname eq $station) {
|
|
||||||
$pos = $n;
|
|
||||||
last;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($pos > 0) {
|
|
||||||
last;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
=head2 ProcessForecast($$$)
|
=head2 ProcessForecast($$$)
|
||||||
|
|
||||||
=over
|
=over
|
||||||
@ -1753,12 +1663,11 @@ ATTENTION: This method is executed in a different process than FHEM.
|
|||||||
|
|
||||||
sub ProcessForecast {
|
sub ProcessForecast {
|
||||||
my ($param, $httpError, $fileContent) = @_;
|
my ($param, $httpError, $fileContent) = @_;
|
||||||
my $hash = $param->{hash};
|
my $hash = $param->{hash};
|
||||||
my $name = $hash->{NAME};
|
my $name = $hash->{NAME};
|
||||||
my $url = $param->{url};
|
my $url = $param->{url};
|
||||||
my $code = $param->{code};
|
my $code = $param->{code};
|
||||||
my $station = $param->{station};
|
my $station = $param->{station};
|
||||||
my $dataPrecision = $param->{dataPrecision};
|
|
||||||
|
|
||||||
::Log3 $name, 5, "$name: ProcessForecast START";
|
::Log3 $name, 5, "$name: ProcessForecast START";
|
||||||
|
|
||||||
@ -1784,15 +1693,11 @@ sub ProcessForecast {
|
|||||||
my %selectedProperties;
|
my %selectedProperties;
|
||||||
if (!@properties) {
|
if (!@properties) {
|
||||||
# no selection: use defaults
|
# no selection: use defaults
|
||||||
if ($dataPrecision eq 'S') {
|
%selectedProperties = %forecastDefaultProperties;
|
||||||
%selectedProperties = %forecastDefaultPropertiesS;
|
|
||||||
} else {
|
|
||||||
%selectedProperties = %forecastDefaultPropertiesL;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
# use selected properties
|
# use selected properties
|
||||||
for my $property (@properties) { # use selected properties
|
foreach my $property (@properties) {
|
||||||
$property =~ s/^\s+|\s+$//g; # trim
|
$property =~ s/^\s+|\s+$//g; # trim
|
||||||
$selectedProperties{$property} = 1;
|
$selectedProperties{$property} = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1806,8 +1711,8 @@ sub ProcessForecast {
|
|||||||
$header{station} = $station;
|
$header{station} = $station;
|
||||||
|
|
||||||
# parse XML strings (files from zip)
|
# parse XML strings (files from zip)
|
||||||
for my $xmlString (@xmlStrings) {
|
foreach my $xmlString (@xmlStrings) {
|
||||||
if (substr(${$xmlString}, 0, 2) eq 'PK') { # empty string, skip
|
if (substr(${$xmlString}, 0, 2) eq 'PK') {
|
||||||
# empty string, skip
|
# empty string, skip
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
@ -1828,7 +1733,7 @@ sub ProcessForecast {
|
|||||||
my $productDefinitionNodeList = $dom->getElementsByLocalName('ProductDefinition');
|
my $productDefinitionNodeList = $dom->getElementsByLocalName('ProductDefinition');
|
||||||
if ($productDefinitionNodeList->size()) {
|
if ($productDefinitionNodeList->size()) {
|
||||||
my $productDefinitionNode = $productDefinitionNodeList->get_node(1);
|
my $productDefinitionNode = $productDefinitionNodeList->get_node(1);
|
||||||
for my $productDefinitionChildNode ($productDefinitionNode->nonBlankChildNodes()) {
|
foreach my $productDefinitionChildNode ($productDefinitionNode->nonBlankChildNodes()) {
|
||||||
if ($productDefinitionChildNode->nodeName() eq 'dwd:Issuer') {
|
if ($productDefinitionChildNode->nodeName() eq 'dwd:Issuer') {
|
||||||
$issuer = $productDefinitionChildNode->textContent();
|
$issuer = $productDefinitionChildNode->textContent();
|
||||||
$header{copyright} = "Datenbasis: $issuer";
|
$header{copyright} = "Datenbasis: $issuer";
|
||||||
@ -1836,14 +1741,14 @@ sub ProcessForecast {
|
|||||||
my $issueTime = $productDefinitionChildNode->textContent();
|
my $issueTime = $productDefinitionChildNode->textContent();
|
||||||
$header{time} = FormatDateTimeLocal($hash, ParseKMLTime($issueTime));
|
$header{time} = FormatDateTimeLocal($hash, ParseKMLTime($issueTime));
|
||||||
} elsif ($productDefinitionChildNode->nodeName() eq 'dwd:ForecastTimeSteps') {
|
} elsif ($productDefinitionChildNode->nodeName() eq 'dwd:ForecastTimeSteps') {
|
||||||
for my $forecastTimeStepsChildNode ($productDefinitionChildNode->nonBlankChildNodes()) {
|
foreach my $forecastTimeStepsChildNode ($productDefinitionChildNode->nonBlankChildNodes()) {
|
||||||
if ($forecastTimeStepsChildNode->nodeName() eq 'dwd:TimeStep') {
|
if ($forecastTimeStepsChildNode->nodeName() eq 'dwd:TimeStep') {
|
||||||
my $forecastTimeSteps = $forecastTimeStepsChildNode->textContent();
|
my $forecastTimeSteps = $forecastTimeStepsChildNode->textContent();
|
||||||
push(@timestamps, ParseKMLTime($forecastTimeSteps));
|
push(@timestamps, ParseKMLTime($forecastTimeSteps));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} elsif ($productDefinitionChildNode->nodeName() eq 'dwd:FormatCfg') {
|
} elsif ($productDefinitionChildNode->nodeName() eq 'dwd:FormatCfg') {
|
||||||
for my $formatCfgChildNode ($productDefinitionChildNode->nonBlankChildNodes()) {
|
foreach my $formatCfgChildNode ($productDefinitionChildNode->nonBlankChildNodes()) {
|
||||||
if ($formatCfgChildNode->nodeName() eq 'dwd:DefaultUndefSign') {
|
if ($formatCfgChildNode->nodeName() eq 'dwd:DefaultUndefSign') {
|
||||||
$defaultUndefSign = $formatCfgChildNode->textContent();
|
$defaultUndefSign = $formatCfgChildNode->textContent();
|
||||||
}
|
}
|
||||||
@ -1863,22 +1768,13 @@ sub ProcessForecast {
|
|||||||
my ($longitude, $latitude, $altitude);
|
my ($longitude, $latitude, $altitude);
|
||||||
my $placemarkNodeList = $dom->getElementsByLocalName('Placemark');
|
my $placemarkNodeList = $dom->getElementsByLocalName('Placemark');
|
||||||
if ($placemarkNodeList->size()) {
|
if ($placemarkNodeList->size()) {
|
||||||
my $placemarkNodePos;
|
my $placemarkNode = $placemarkNodeList->get_node(1);
|
||||||
if ($dataPrecision eq 'S') {
|
foreach my $placemarkChildNode ($placemarkNode->nonBlankChildNodes()) {
|
||||||
$placemarkNodePos = getStationPos ($name, $station, $placemarkNodeList);
|
|
||||||
if ($placemarkNodePos < 1) {
|
|
||||||
die "station '" . $station . "' not found in XML data";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$placemarkNodePos = 1;
|
|
||||||
}
|
|
||||||
my $placemarkNode = $placemarkNodeList->get_node($placemarkNodePos);
|
|
||||||
for my $placemarkChildNode ($placemarkNode->nonBlankChildNodes()) {
|
|
||||||
if ($placemarkChildNode->nodeName() eq 'kml:description') {
|
if ($placemarkChildNode->nodeName() eq 'kml:description') {
|
||||||
my $description = $placemarkChildNode->textContent();
|
my $description = $placemarkChildNode->textContent();
|
||||||
$header{description} = encode('UTF-8', $description);
|
$header{description} = encode('UTF-8', $description);
|
||||||
} elsif ($placemarkChildNode->nodeName() eq 'kml:ExtendedData') {
|
} elsif ($placemarkChildNode->nodeName() eq 'kml:ExtendedData') {
|
||||||
for my $extendedDataChildNode ($placemarkChildNode->nonBlankChildNodes()) {
|
foreach my $extendedDataChildNode ($placemarkChildNode->nonBlankChildNodes()) {
|
||||||
if ($extendedDataChildNode->nodeName() eq 'dwd:Forecast') {
|
if ($extendedDataChildNode->nodeName() eq 'dwd:Forecast') {
|
||||||
my $elementName = $extendedDataChildNode->getAttribute('dwd:elementName');
|
my $elementName = $extendedDataChildNode->getAttribute('dwd:elementName');
|
||||||
# convert some elements names for backward compatibility
|
# convert some elements names for backward compatibility
|
||||||
@ -1887,8 +1783,8 @@ sub ProcessForecast {
|
|||||||
my $selectedProperty = $selectedProperties{$elementName};
|
my $selectedProperty = $selectedProperties{$elementName};
|
||||||
if (defined($selectedProperty)) {
|
if (defined($selectedProperty)) {
|
||||||
my $textContent = $extendedDataChildNode->nonBlankChildNodes()->get_node(1)->textContent();
|
my $textContent = $extendedDataChildNode->nonBlankChildNodes()->get_node(1)->textContent();
|
||||||
$textContent =~ s/^\s+|\s+$//g; # trim outside
|
$textContent =~ s/^\s+|\s+$//g; # trim outside
|
||||||
$textContent =~ s/\s+/ /g; # trim inside
|
$textContent =~ s/\s+/ /g; # trim inside
|
||||||
my @values = split(' ', $textContent);
|
my @values = split(' ', $textContent);
|
||||||
$timeProperties{$elementName} = \@values;
|
$timeProperties{$elementName} = \@values;
|
||||||
}
|
}
|
||||||
@ -1911,7 +1807,7 @@ sub ProcessForecast {
|
|||||||
my @sunsets;
|
my @sunsets;
|
||||||
my $lastDate = '';
|
my $lastDate = '';
|
||||||
my $sunElevationCorrection = AstroSun::ElevationCorrection($altitude);
|
my $sunElevationCorrection = AstroSun::ElevationCorrection($altitude);
|
||||||
for my $timestamp (@timestamps) {
|
foreach my $timestamp (@timestamps) {
|
||||||
my ($azimuth, $elevation) = AstroSun::AzimuthElevation($timestamp, $longitude, $latitude);
|
my ($azimuth, $elevation) = AstroSun::AzimuthElevation($timestamp, $longitude, $latitude);
|
||||||
push(@azimuths, $azimuth); # [deg]
|
push(@azimuths, $azimuth); # [deg]
|
||||||
push(@elevations, $elevation); # [deg]
|
push(@elevations, $elevation); # [deg]
|
||||||
@ -2055,8 +1951,7 @@ sub GetForecastFinish {
|
|||||||
if (defined($hash->{".fetchAlerts"}) && !$hash->{".fetchAlerts"}) {
|
if (defined($hash->{".fetchAlerts"}) && !$hash->{".fetchAlerts"}) {
|
||||||
# get forecast was initiated by timer, reschedule to fetch alerts
|
# get forecast was initiated by timer, reschedule to fetch alerts
|
||||||
$hash->{".fetchAlerts"} = 1;
|
$hash->{".fetchAlerts"} = 1;
|
||||||
# @TODO needs to be reactivated?
|
::InternalTimer(gettimeofday() + 1, 'DWD_OpenData::Timer', $hash);
|
||||||
#::InternalTimer(gettimeofday() + 1, 'DWD_OpenData::Timer', $hash);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
::Log3 $name, 5, "$name: GetForecastFinish END";
|
::Log3 $name, 5, "$name: GetForecastFinish END";
|
||||||
@ -2098,8 +1993,7 @@ sub GetForecastAbort {
|
|||||||
if (defined($hash->{".fetchAlerts"}) && !$hash->{".fetchAlerts"}) {
|
if (defined($hash->{".fetchAlerts"}) && !$hash->{".fetchAlerts"}) {
|
||||||
# get forecast was initiated by timer, reschedule to fetch alerts
|
# get forecast was initiated by timer, reschedule to fetch alerts
|
||||||
$hash->{".fetchAlerts"} = 1;
|
$hash->{".fetchAlerts"} = 1;
|
||||||
# @TODO needs to be reactivated?
|
::InternalTimer(gettimeofday() + 1, 'DWD_OpenData::Timer', $hash);
|
||||||
#::InternalTimer(gettimeofday() + 1, 'DWD_OpenData::Timer', $hash);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2285,8 +2179,7 @@ sub GetAlerts {
|
|||||||
# kill old blocking call
|
# kill old blocking call
|
||||||
::BlockingKill($hash->{".alertsBlockingCall".$communeUnion});
|
::BlockingKill($hash->{".alertsBlockingCall".$communeUnion});
|
||||||
}
|
}
|
||||||
my $timeout = ::AttrVal($name, 'downloadTimeout', 60);
|
$hash->{".alertsBlockingCall".$communeUnion} = ::BlockingCall("DWD_OpenData::GetAlertsStart", $hash, "DWD_OpenData::GetAlertsFinish", 60, "DWD_OpenData::GetAlertsAbort", $hash);
|
||||||
$hash->{".alertsBlockingCall".$communeUnion} = ::BlockingCall("DWD_OpenData::GetAlertsStart", $hash, "DWD_OpenData::GetAlertsFinish", $timeout, "DWD_OpenData::GetAlertsAbort", $hash);
|
|
||||||
|
|
||||||
$alertsUpdating[$communeUnion] = time();
|
$alertsUpdating[$communeUnion] = time();
|
||||||
|
|
||||||
@ -2822,10 +2715,9 @@ sub DWD_OpenData_Initialize {
|
|||||||
$hash->{GetFn} = 'DWD_OpenData::Get';
|
$hash->{GetFn} = 'DWD_OpenData::Get';
|
||||||
|
|
||||||
$hash->{AttrList} = 'disable:0,1 '
|
$hash->{AttrList} = 'disable:0,1 '
|
||||||
.'forecastStation forecastDays forecastProperties forecastResolution:1,3,6 forecastWW2Text:0,1 forecastPruning:0,1 forecastDataPrecision:low,high '
|
.'forecastStation forecastDays forecastProperties forecastResolution:1,3,6 forecastWW2Text:0,1 forecastPruning:0,1 '
|
||||||
.'alertArea alertLanguage:DE,EN alertExcludeEvents '
|
.'alertArea alertLanguage:DE,EN alertExcludeEvents '
|
||||||
.'timezone '
|
.'timezone '
|
||||||
.'downloadTimeout '
|
|
||||||
.$readingFnAttributes;
|
.$readingFnAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2837,9 +2729,6 @@ sub DWD_OpenData_Initialize {
|
|||||||
#
|
#
|
||||||
# CHANGES
|
# CHANGES
|
||||||
#
|
#
|
||||||
# 25.02.2024 (version 1.17.0) DS_Starter + jensb
|
|
||||||
# feature: support MOSMIX S
|
|
||||||
#
|
|
||||||
# 16.02.2021 (version 1.16.3) jensb
|
# 16.02.2021 (version 1.16.3) jensb
|
||||||
# bugfix: fix version for experimental::smartmatch
|
# bugfix: fix version for experimental::smartmatch
|
||||||
#
|
#
|
||||||
@ -3051,9 +2940,6 @@ sub DWD_OpenData_Initialize {
|
|||||||
<li>disable {0|1}, default: 0<br>
|
<li>disable {0|1}, default: 0<br>
|
||||||
Disable fetching data.
|
Disable fetching data.
|
||||||
</li><br>
|
</li><br>
|
||||||
<li>downloadTimeout {Integer}, default: 60 s<br>
|
|
||||||
Timeout for downloading data (alerts, forecast) from DWD server.
|
|
||||||
</li><br>
|
|
||||||
<li>timezone <tz>, default: OS dependent<br>
|
<li>timezone <tz>, default: OS dependent<br>
|
||||||
<a href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones">IANA TZ string</a> for date and time readings (e.g. "Europe/Berlin"), can be used to assume the perspective of a station that is in a different timezone or if your OS timezone settings do not match your local timezone. Alternatively you may use <code>tzselect</code> on the Linux command line to find a valid timezone string.
|
<a href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones">IANA TZ string</a> for date and time readings (e.g. "Europe/Berlin"), can be used to assume the perspective of a station that is in a different timezone or if your OS timezone settings do not match your local timezone. Alternatively you may use <code>tzselect</code> on the Linux command line to find a valid timezone string.
|
||||||
</li><br>
|
</li><br>
|
||||||
@ -3073,17 +2959,6 @@ sub DWD_OpenData_Initialize {
|
|||||||
Time resolution (number of hours between 2 samples).<br>
|
Time resolution (number of hours between 2 samples).<br>
|
||||||
Note: When value is changed all existing forecast readings will be deleted.
|
Note: When value is changed all existing forecast readings will be deleted.
|
||||||
</li><br>
|
</li><br>
|
||||||
<li>forecastDataPrecision {low|high}, default: low<br>
|
|
||||||
Selection of the DWD forecast method used. <br>
|
|
||||||
The DWD distinguishes between MOSMIX_L and MOSMIX_S stations, which differ in terms of update frequency and data volume. <br>
|
|
||||||
See the
|
|
||||||
<a href="https://www.dwd.de/DE/leistungen/met_verfahren_mosmix/mosmix_verfahrenbeschreibung_gesamt.pdf;jsessionid=425620479D0FC3EA4EEFD89B0A5D1C8F.live31092?__blob=publicationFile&v=2">Description of the processes</a>
|
|
||||||
and differences of data elements between MOSMIX_L and MOSMIX_S stations in this
|
|
||||||
<a href='https://www.dwd.de/DE/leistungen/opendata/help/schluessel_datenformate/kml/mosmix_elemente_xls.html' target='_blank'>Overview</a>.<br>
|
|
||||||
- low: MOSMIX_L is used <br>
|
|
||||||
- high: MOSMIX_S is used <br>
|
|
||||||
Note: The "high" method requires powerful hardware in terms of RAM and CPU. At least 4 GB RAM is strongly recommended!
|
|
||||||
</li><br>
|
|
||||||
<li>forecastProperties [<p1>[,<p2>]...], default: Tx, Tn, Tg, TTT, DD, FX1, Neff, RR6c, RRhc, Rh00, ww<br>
|
<li>forecastProperties [<p1>[,<p2>]...], default: Tx, Tn, Tg, TTT, DD, FX1, Neff, RR6c, RRhc, Rh00, ww<br>
|
||||||
See the <a href="https://opendata.dwd.de/weather/lib/MetElementDefinition.xml">DWD forecast property defintions</a> for more details.<br>
|
See the <a href="https://opendata.dwd.de/weather/lib/MetElementDefinition.xml">DWD forecast property defintions</a> for more details.<br>
|
||||||
Notes:<br>
|
Notes:<br>
|
||||||
|
Loading…
Reference in New Issue
Block a user