added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
38
bundles/org.openhab.binding.dwdunwetter/.classpath
Normal file
38
bundles/org.openhab.binding.dwdunwetter/.classpath
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
23
bundles/org.openhab.binding.dwdunwetter/.project
Normal file
23
bundles/org.openhab.binding.dwdunwetter/.project
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.openhab.binding.dwdunwetter</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
13
bundles/org.openhab.binding.dwdunwetter/NOTICE
Normal file
13
bundles/org.openhab.binding.dwdunwetter/NOTICE
Normal file
@@ -0,0 +1,13 @@
|
||||
This content is produced and maintained by the openHAB project.
|
||||
|
||||
* Project home: https://www.openhab.org
|
||||
|
||||
== Declared Project Licenses
|
||||
|
||||
This program and the accompanying materials are made available under the terms
|
||||
of the Eclipse Public License 2.0 which is available at
|
||||
https://www.eclipse.org/legal/epl-2.0/.
|
||||
|
||||
== Source Code
|
||||
|
||||
https://github.com/openhab/openhab-addons
|
||||
116
bundles/org.openhab.binding.dwdunwetter/README.md
Normal file
116
bundles/org.openhab.binding.dwdunwetter/README.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# DwdUnwetter Binding
|
||||
|
||||
Binding to retrieve the Weather Warnings of the "Deutscher Wetterdienstes" from the [DWD Geoserver](https://maps.dwd.de/geoserver/web/).
|
||||
The DWD provides weather warnings for Germany.
|
||||
Regions outside of Germany are not supported.
|
||||
|
||||
|
||||
## Supported Things
|
||||
|
||||
This binding supports one thing, the Weather Warning.
|
||||
Each Thing provides one or more warnings for a city.
|
||||
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
| Property | Default | Required | Description |
|
||||
|--------------|---------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| cellId | - | Yes | ID of the area to retrieve weather warnings. See [this list](https://www.dwd.de/DE/leistungen/opendata/help/warnungen/cap_warncellids_csv.csv) of valid IDs. Using the percent sign (%) as a wildcard, it is possible to query multiple cells. For example, the value `8111%` retrieves all cell IDs that start with `8111`. |
|
||||
| refresh | 30 | No | Time between API requests in minutes. Minimum 15 minutes. |
|
||||
| warningCount | 1 | No | Number of warnings to provide. |
|
||||
|
||||
The binding will deliver no warnings if the number of retrieved warnings for one Thing is too big.
|
||||
This can only happen if you select too many cell IDs (more than about 300) with the percent wildcard.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
dwdunwetter:dwdwarnings:cologne "Warnings Cologne" [ cellId="105315000", refresh=15, warningCount=1 ]
|
||||
```
|
||||
|
||||
|
||||
## Channels
|
||||
|
||||
The are multiple channels for every weather warning.
|
||||
The channels are numbered, with channel names ending in 1 for the first warning, 2 for the second warning, and so on.
|
||||
The warnings will be sorted first by `Severity` and then by the `Valid From` date.
|
||||
This ensures that the channels for the first warning will always be the highest Severity.
|
||||
If the API returns more warnings than the configured number in the Thing, the warnings with the lowest Severity will be dropped.
|
||||
|
||||
| Channel | Type | Description |
|
||||
|--------------|-----------------|----------------------------------------------------------------------------------|
|
||||
| warningN | Switch | ON if a warning is present |
|
||||
| UpdatedN | Trigger Channel | Triggers NEW when a warning is sent the first time |
|
||||
| severityN | String | Severity of the warning. Possible values are Minor, Moderate, Severe, and Extreme|
|
||||
| headlineN | String | Headline of the warning (e.g. "Amtliche Warnung vor FROST") |
|
||||
| descriptionN | String | Textual description of the warning |
|
||||
| eventN | String | Type of the warning (e.g. FROST) |
|
||||
| effectiveN | DateTime | Issued Date and Time |
|
||||
| onsetN | DateTime | Start Date and Time for which the warning is valid |
|
||||
| expiresN | DateTime | End Date and Time for which the warning is valid |
|
||||
| altitudeN | Number:Length | Lower Height above sea level for which the warning is valid |
|
||||
| ceilingN | Number:Length | Upper Height above sea level for which the warning is valid |
|
||||
| urgencyN | String | Urgency of the warning. Possible values are Future and Immediate |
|
||||
| instructionN | String | Additional instructions and safety information |
|
||||
|
||||
All channels are readonly.
|
||||
|
||||
The main purpose of the channel `warningN` is to be used for controlling visibility in sitemaps.
|
||||
The channel can also be used in rules to check if there is a warning present.
|
||||
It should not be used for rules that need to fire if a new warning shows up.
|
||||
If a warning is replaced by another warning, that channel stays at ON, and there will be no state change.
|
||||
For rules that need to fire if a new warning occurs, there is the trigger channel `updatedN`.
|
||||
That trigger channel fires an event whenever a warning is sent for the first time.
|
||||
It also triggers if a warning is replaced by another.
|
||||
|
||||
More explanations about the specific values of the channels can be found in the documentation of the DWD at: [CAP DWD Profile 1.2](https://www.dwd.de/DE/leistungen/opendata/help/warnungen/cap_dwd_profile_de_pdf.pdf?__blob=publicationFile&v=7)
|
||||
|
||||
|
||||
## Full Example
|
||||
|
||||
demo.things:
|
||||
|
||||
```
|
||||
dwdunwetter:dwdwarnings:cologne "Warnings Cologne" [ cellId="105315000", refresh=15, warningCount=1 ]
|
||||
```
|
||||
|
||||
demo.items:
|
||||
|
||||
```
|
||||
Switch WarningCologne "Weather warning" { channel="dwdunwetter:dwdwarnings:cologne:warning1" }
|
||||
String WarningCologneServerity "Severity[%s]" { channel="dwdunwetter:dwdwarnings:cologne:severity1" }
|
||||
String WarningCologneBeschreibung "[%s]" { channel="dwdunwetter:dwdwarnings:cologne:description1" }
|
||||
String WarningCologneAusgabedatum "Issued at [%s]" { channel="dwdunwetter:dwdwarnings:cologne:effective1" }
|
||||
String WarningCologneGueltigAb "Valid from [%s]" { channel="dwdunwetter:dwdwarnings:cologne:onset1" }
|
||||
String WarningCologneGueltigBis "Valid to [%s]" { channel="dwdunwetter:dwdwarnings:cologne:expires1" }
|
||||
String WarningCologneTyp "Type [%s]" { channel="dwdunwetter:dwdwarnings:cologne:event1" }
|
||||
String WarningCologneTitel "[%s]" { channel="dwdunwetter:dwdwarnings:cologne:headline1" }
|
||||
String WarningCologneHoeheAb "Height from [%d m]" { channel="dwdunwetter:dwdwarnings:cologne:altitude1" }
|
||||
String WarningCologneHoeheBis "Height to [%d m]" { channel="dwdunwetter:dwdwarnings:cologne:ceiling1" }
|
||||
String WarningCologneUrgency "[%s]" { channel="dwdunwetter:dwdwarnings:cologne:urgency1" }
|
||||
String WarningCologneInstruction "Additional information: [%s]" { channel="dwdunwetter:dwdwarnings:cologne:instruction1" }
|
||||
```
|
||||
|
||||
demo.sitemap:
|
||||
|
||||
```
|
||||
sitemap demo label="Main Menu"
|
||||
{
|
||||
Frame {
|
||||
Text item=WarningCologneTitel visibility=[WarningCologne==ON]
|
||||
Text item=WarningCologneBeschreibung visibility=[WarningCologne==ON]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
demo.rules
|
||||
|
||||
```
|
||||
rule "New Warnung"
|
||||
when
|
||||
Channel 'dwdunwetter:dwdwarnings:cologne:updated1' triggered NEW
|
||||
then
|
||||
// New Warning send a push notification to everyone
|
||||
end
|
||||
|
||||
```
|
||||
105
bundles/org.openhab.binding.dwdunwetter/README_de.md
Normal file
105
bundles/org.openhab.binding.dwdunwetter/README_de.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# DwdUnwetter Binding
|
||||
|
||||
Binding zur Abfrage von aktuellen Unwetterwarnungen des Deutschen Wetterdienstes via [DWD Geoserver](https://maps.dwd.de/geoserver/web/)
|
||||
|
||||
## Unterstütztes Thing
|
||||
|
||||
Das Binding unterstützt genau ein Thing - Unwetterwarnungen.
|
||||
Ein Thing stellt dabei eine oder mehrere Warnungen für eine Gemeinde bereit.
|
||||
|
||||
|
||||
## Thing Konfiguration
|
||||
|
||||
| Property | Default | Required | Description |
|
||||
|--------------|---------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| cellId | - | Yes | ID der abzufragenden Zelle. Siehe [cap_warncellids_csv.csv](https://www.dwd.de/DE/leistungen/opendata/help/warnungen/cap_warncellids_csv.csv "cap_warncellids_csv.csv") Es kann auch mittels % eine Gesamtmenge abgefragt werden, z.B. 8111% alle Gemeinden die mit 8111 anfangen |
|
||||
| refresh | 30 | No | Abfrageintervall in Minuten. Minimum 15 Minuten. |
|
||||
| warningCount | 1 | No | Anzahl der Warnungen, die als Channels bereitgestellt werden sollen |
|
||||
Wählt man die Cell-ID mittels des %-Operators zu groß, so kann es passieren, das gar keine Warnungen kommen.
|
||||
Das ist immer Fall, wenn die zurückgelieferte XML-Datei des DWD zu groß ist, dass sie nicht intern gebuffered werden kann.
|
||||
Dies ist bei ca. 300+ Warnungen der Fall.
|
||||
|
||||
|
||||
## Channels
|
||||
|
||||
Für jede bereitgestellte Warnung werden mehrere Channels bereitgestellt.
|
||||
Die Channels sind jeweils durchnummeriert, Channels die mit 1 enden sind für die erste Warnung, Channels die mit 2 enden für die zweite Warnung usw.
|
||||
Die vom DWD gelieferten Warnungen werden dabei nach Severity (Warnstufe) sortiert und innerhalb der Warnstufe nach Beginndatum.
|
||||
Dadurch ist sichergestellt, dass in den Channels für die erste Warnung (...1) immer die Warnung mit der höchsten Warnstufe steht.
|
||||
Werden mehr Warnungen vom DWD geliefert, als an Channels konfiguriert ist, werden dadurch die Warnungen mit der niedrigsten Warnstufe verworfen.
|
||||
|
||||
| Channel | Type | Beschreibung |
|
||||
|--------------|-----------------|--------------------------------------------------------------------------------------------------------|
|
||||
| warningN | Switch | Schalter, der auf ON steht, wenn eine Warnung vorliegt, OFF sonst. |
|
||||
| UpdatedN | Trigger Channel | Sendet das Event "NEW", wenn diese Warnung das erste mal gesendet wird. |
|
||||
| severityN | String | Warnstufe, von niedrig nach hoch: Minor, Moderate, Severe, Extreme. |
|
||||
| headlineN | String | Überschrift der Warnung, z.B. Amtliche WARNUNG vor STURMBÖEN |
|
||||
| descriptionN | String | Klartext Beschreibung der Warnung. |
|
||||
| eventN | String | Art der Warnung, z.B. STURMBÖEN |
|
||||
| effectiveN | DateTime | Zeitpunkt, an dem die Warnung ausgegeben wurde. |
|
||||
| onsetN | DateTime | Zeitpunkt, von dem an die Warnung gilt. |
|
||||
| effectiveN | DateTime | Zeitpunkt, bis zu dem die Warnung gilt. |
|
||||
| altitudeN | Number:Length | Höhe über dem Meerespiegel, ab dem die Warnung gilt. |
|
||||
| ceilingN | Number:Length | Höhe über dem Meerespiegel, bis zu dem die Warnung gilt. |
|
||||
| urgencyN | String | Zeitrahmen der Meldung, Mögliche Werte sind Future (Vorabinformation) und Immediate (Konkrete Warnung) |
|
||||
| instructionN | String | Zusatztext zur Warnung (Instruktionen und Sicherheitshinweise) |
|
||||
|
||||
Sämtliche Channels sind ReadOnly!
|
||||
|
||||
Der Channel _warningN_ dient hauptsächlich dazu, um z.B. in Sitemaps dynamisch Warnungen ein- oder auszublenden, bzw. um in Regeln zu prüfen, ob überhaupt eine Warnung vorliegt.
|
||||
Er ist nicht geeignet um auf das Erscheinen einer Warnung zu prüfen.
|
||||
Denn wenn eine Warnung durch eine neue Warnung ersetzt wird, bleibt der Zustand ON, es gibt keinen Zustandswechsel.
|
||||
Um auf das erscheinen einer Warnung zu prüfen, sollte der Trigger-Channel _updatedN_ genutzt werden.
|
||||
Der feuert immer dann, wenn eine Warnung das erste mal gesendet wird.
|
||||
Das heißt, der feuert auch dann, wenn eine Warnung durch eine neue Warnung ersetzt wird.
|
||||
|
||||
Weitere Erläuterungen der Bedeutungen finden sich in der Dokumentation des DWDs unter [CAP DWD Profile 1.2](https://www.dwd.de/DE/leistungen/opendata/help/warnungen/cap_dwd_profile_de_pdf.pdf?__blob=publicationFile&v=7)
|
||||
|
||||
## Vollständiges Beispiel
|
||||
|
||||
demo.things:
|
||||
|
||||
```
|
||||
dwdunwetter:dwdwarnings:koeln "Warnungen Köln" [ cellId="105315000", refresh=15, warningCount=1 ]
|
||||
```
|
||||
|
||||
demo.items:
|
||||
|
||||
```
|
||||
Switch WarnungKoeln "Warnung vorhanden" { channel="dwdunwetter:dwdwarnings:koeln:warning1" }
|
||||
String WarnungKoelnServerity "Warnstufe [%s]" { channel="dwdunwetter:dwdwarnings:koeln:severity1" }
|
||||
String WarnungKoelnBeschreibung "[%s]" { channel="dwdunwetter:dwdwarnings:koeln:description1" }
|
||||
String WarnungKoelnAusgabedatum "Ausgeben am [%s]" { channel="dwdunwetter:dwdwarnings:koeln:effective1" }
|
||||
String WarnungKoelnGueltigAb "Warnung gültig ab [%s]" { channel="dwdunwetter:dwdwarnings:koeln:onset1" }
|
||||
String WarnungKoelnGueltigBis "Warnung gültig bis [%s]" { channel="dwdunwetter:dwdwarnings:koeln:expires1" }
|
||||
String WarnungKoelnTyp "Warnungstyp [%s]" { channel="dwdunwetter:dwdwarnings:koeln:event1" }
|
||||
String WarnungKoelnTitel "[%s]" { channel="dwdunwetter:dwdwarnings:koeln:headline1" }
|
||||
String WarnungKoelnHoeheAb "Höhe ab [%d m]" { channel="dwdunwetter:dwdwarnings:koeln:altitude1" }
|
||||
String WarnungKoelnHoeheBis "Höhe bis [%d m]" { channel="dwdunwetter:dwdwarnings:koeln:ceiling1" }
|
||||
String WarningCologneUrgency "[%s]" { channel="dwdunwetter:dwdwarnings:cologne:urgency1" }
|
||||
String WarningCologneInstruction "Zusatzinformationen: [%s]" { channel="dwdunwetter:dwdwarnings:cologne:instruction1" }
|
||||
```
|
||||
|
||||
demo.sitemap:
|
||||
|
||||
```
|
||||
sitemap demo label="Main Menu"
|
||||
{
|
||||
Frame {
|
||||
Text item=WarnungKoelnTitel visibility=[WarnungKoeln==ON]
|
||||
Text item=WarnungKoelnBeschreibung visibility=[WarnungKoeln==ON]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
demo.rules
|
||||
|
||||
```
|
||||
rule "Neue Warnung"
|
||||
when
|
||||
Channel 'dwdunwetter:dwdwarnings:koeln:updated1' triggered NEW
|
||||
then
|
||||
// Neue Warnung, Informiere alle Bewohner
|
||||
end
|
||||
|
||||
```
|
||||
17
bundles/org.openhab.binding.dwdunwetter/pom.xml
Normal file
17
bundles/org.openhab.binding.dwdunwetter/pom.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.binding.dwdunwetter</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: DwdUnwetter Binding</name>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.dwdunwetter-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-binding-dwdunwetter" description="DwdUnwetter Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.dwdunwetter/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.dwdunwetter.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link DwdUnwetterBindingConstants} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Martin Koehler - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DwdUnwetterBindingConstants {
|
||||
|
||||
public static final String BINDING_ID = "dwdunwetter";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_WARNINGS = new ThingTypeUID(BINDING_ID, "dwdwarnings");
|
||||
|
||||
// Channels
|
||||
public static final String CHANNEL_LAST_UPDATED = "lastUpdated";
|
||||
|
||||
// Channels per Warning
|
||||
public static final String CHANNEL_WARNING = "warning";
|
||||
public static final String CHANNEL_UPDATED = "updated";
|
||||
public static final String CHANNEL_SEVERITY = "severity";
|
||||
public static final String CHANNEL_DESCRIPTION = "description";
|
||||
public static final String CHANNEL_EFFECTIVE = "effective";
|
||||
public static final String CHANNEL_ONSET = "onset";
|
||||
public static final String CHANNEL_EXPIRES = "expires";
|
||||
public static final String CHANNEL_EVENT = "event";
|
||||
public static final String CHANNEL_HEADLINE = "headline";
|
||||
public static final String CHANNEL_ALTITUDE = "altitude";
|
||||
public static final String CHANNEL_CEILING = "ceiling";
|
||||
public static final String CHANNEL_INSTRUCTION = "instruction";
|
||||
public static final String CHANNEL_URGENCY = "urgency";
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.dwdunwetter.internal.config;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link DwdUnwetterConfiguration} class contains fields mapping thing configuration parameters.
|
||||
*
|
||||
* @author Martin Koehler - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DwdUnwetterConfiguration {
|
||||
public int refresh;
|
||||
public int warningCount;
|
||||
public String cellId = StringUtils.EMPTY;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.dwdunwetter.internal.data;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Cache of Warnings to update the {@link DwdUnwetterBindingConstants#CHANNEL_UPDATED} if a new warning is sent to a
|
||||
* channel.
|
||||
*
|
||||
* @author Martin Koehler - Initial contribution
|
||||
*/
|
||||
public class DwdWarningCache {
|
||||
|
||||
// Remove Entries 30 Minutes after they expired
|
||||
private static final long WAIT_TIME_IN_MINUTES = 30;
|
||||
|
||||
private final Map<String, Instant> idExpiresMap;
|
||||
|
||||
public DwdWarningCache() {
|
||||
idExpiresMap = new HashMap<>();
|
||||
}
|
||||
|
||||
private boolean isExpired(Entry<String, Instant> entry) {
|
||||
Instant expireTime = entry.getValue().plus(WAIT_TIME_IN_MINUTES, ChronoUnit.MINUTES);
|
||||
return Instant.now().isAfter(expireTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Warning
|
||||
*
|
||||
* @param data The warning data
|
||||
* @return <code>true</code> if it is a new warning, <code>false</code> if the warning is not new.
|
||||
*/
|
||||
public boolean addEntry(DwdWarningData data) {
|
||||
return idExpiresMap.put(data.getId(), data.getExpires()) == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the expired Entries
|
||||
*/
|
||||
public void deleteOldEntries() {
|
||||
List<String> oldEntries = idExpiresMap.entrySet().stream().filter(this::isExpired).map(Entry::getKey)
|
||||
.collect(Collectors.toList());
|
||||
oldEntries.forEach(idExpiresMap::remove);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.dwdunwetter.internal.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
/**
|
||||
* Data for one warning.
|
||||
*
|
||||
* @author Martin Koehler - Initial contribution
|
||||
*/
|
||||
public class DwdWarningData {
|
||||
|
||||
private String id;
|
||||
|
||||
private Severity severity;
|
||||
private String description;
|
||||
private Instant effective;
|
||||
private Instant expires;
|
||||
private Instant onset;
|
||||
private String event;
|
||||
private String status;
|
||||
private String msgType;
|
||||
private String headline;
|
||||
private BigDecimal altitude;
|
||||
private BigDecimal ceiling;
|
||||
private String instruction;
|
||||
private Urgency urgency;
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setSeverity(Severity severity) {
|
||||
this.severity = severity;
|
||||
}
|
||||
|
||||
public Severity getSeverity() {
|
||||
return severity == null ? Severity.UNKNOWN : severity;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setEffective(Instant effective) {
|
||||
this.effective = effective;
|
||||
}
|
||||
|
||||
public Instant getEffective() {
|
||||
return effective;
|
||||
}
|
||||
|
||||
public void setExpires(Instant expires) {
|
||||
this.expires = expires;
|
||||
}
|
||||
|
||||
public Instant getExpires() {
|
||||
return expires;
|
||||
}
|
||||
|
||||
public void setEvent(String event) {
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
public String getEvent() {
|
||||
return event;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public boolean isTest() {
|
||||
return StringUtils.equalsIgnoreCase(status, "Test");
|
||||
}
|
||||
|
||||
public void setMsgType(String msgType) {
|
||||
this.msgType = msgType;
|
||||
}
|
||||
|
||||
public boolean isCancel() {
|
||||
return StringUtils.equalsIgnoreCase(msgType, "Cancel");
|
||||
}
|
||||
|
||||
public void setHeadline(String headline) {
|
||||
this.headline = headline;
|
||||
}
|
||||
|
||||
public String getHeadline() {
|
||||
return headline;
|
||||
}
|
||||
|
||||
public Instant getOnset() {
|
||||
return onset;
|
||||
}
|
||||
|
||||
public void setOnset(Instant onset) {
|
||||
this.onset = onset;
|
||||
}
|
||||
|
||||
public void setAltitude(BigDecimal altitude) {
|
||||
this.altitude = altitude;
|
||||
}
|
||||
|
||||
public BigDecimal getAltitude() {
|
||||
return altitude;
|
||||
}
|
||||
|
||||
public void setCeiling(BigDecimal ceiling) {
|
||||
this.ceiling = ceiling;
|
||||
}
|
||||
|
||||
public BigDecimal getCeiling() {
|
||||
return ceiling;
|
||||
}
|
||||
|
||||
public void setInstruction(String instruction) {
|
||||
this.instruction = instruction;
|
||||
}
|
||||
|
||||
public String getInstruction() {
|
||||
return instruction;
|
||||
}
|
||||
|
||||
public void setUrgency(Urgency urgency) {
|
||||
this.urgency = urgency;
|
||||
}
|
||||
|
||||
public Urgency getUrgency() {
|
||||
return urgency;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((id == null) ? 0 : id.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
DwdWarningData other = (DwdWarningData) obj;
|
||||
if (id == null) {
|
||||
if (other.id != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!id.equals(other.id)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.dwdunwetter.internal.data;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.openhab.core.io.net.http.HttpUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Provides the access to the API Endpoint
|
||||
*
|
||||
* @author Martin Koehler - Initial contribution
|
||||
*/
|
||||
public class DwdWarningDataAccess {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(DwdWarningDataAccess.class);
|
||||
|
||||
// URL of the Service
|
||||
private static final String DWD_URL = "https://maps.dwd.de/geoserver/dwd/ows?service=WFS&version=2.0.0&request=GetFeature&typeName=dwd:Warnungen_Gemeinden";
|
||||
|
||||
/**
|
||||
* Returns the raw Data from the Endpoint.
|
||||
* In case of errors or empty cellId value, returns an {@link StringUtils#EMPTY Empty String}.
|
||||
*
|
||||
* @param cellId The warnCell-Id for which the warnings should be returned
|
||||
* @return The raw data received or an empty string.
|
||||
*/
|
||||
public String getDataFromEndpoint(String cellId) {
|
||||
try {
|
||||
if (StringUtils.isBlank(cellId)) {
|
||||
logger.warn("No cellId provided");
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append(DWD_URL);
|
||||
stringBuilder.append("&CQL_FILTER=");
|
||||
stringBuilder
|
||||
.append(URLEncoder.encode("WARNCELLID LIKE '" + cellId + "'", StandardCharsets.UTF_8.toString()));
|
||||
logger.debug("Refreshing Data for cell {}", cellId);
|
||||
String rawData = HttpUtil.executeUrl("GET", stringBuilder.toString(), 5000);
|
||||
logger.trace("Raw request: {}", stringBuilder);
|
||||
logger.trace("Raw response: {}", rawData);
|
||||
|
||||
return rawData;
|
||||
} catch (IOException e) {
|
||||
logger.warn("Communication error occurred while getting data: {}", e.getMessage());
|
||||
logger.debug("Communication error trace", e);
|
||||
}
|
||||
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.dwdunwetter.internal.data;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeFormatterBuilder;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.stream.XMLEventReader;
|
||||
import javax.xml.stream.XMLInputFactory;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamReader;
|
||||
import javax.xml.stream.events.XMLEvent;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.openhab.core.cache.ExpiringCache;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.unit.ImperialUnits;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Contains the Data for all retrieved warnings for one thing.
|
||||
*
|
||||
* @author Martin Koehler - Initial contribution
|
||||
*/
|
||||
public class DwdWarningsData {
|
||||
|
||||
private static final int MIN_REFRESH_WAIT_MINUTES = 5;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(DwdWarningsData.class);
|
||||
|
||||
private List<DwdWarningData> cityData = new LinkedList<>();
|
||||
|
||||
private DwdWarningCache cache = new DwdWarningCache();
|
||||
|
||||
private ExpiringCache<String> dataAccessCached;
|
||||
|
||||
private DateTimeFormatter formatter = new DateTimeFormatterBuilder()
|
||||
// date/time
|
||||
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
|
||||
// offset (hh:mm - "+00:00" when it's zero)
|
||||
.optionalStart().appendOffset("+HH:MM", "+00:00").optionalEnd()
|
||||
// offset (hhmm - "+0000" when it's zero)
|
||||
.optionalStart().appendOffset("+HHMM", "+0000").optionalEnd()
|
||||
// offset (hh - "Z" when it's zero)
|
||||
.optionalStart().appendOffset("+HH", "Z").optionalEnd()
|
||||
// create formatter
|
||||
.toFormatter();
|
||||
|
||||
public DwdWarningsData(String cellId) {
|
||||
DwdWarningDataAccess dataAccess = new DwdWarningDataAccess();
|
||||
this.dataAccessCached = new ExpiringCache<>(Duration.ofMinutes(MIN_REFRESH_WAIT_MINUTES),
|
||||
() -> dataAccess.getDataFromEndpoint(cellId));
|
||||
}
|
||||
|
||||
private String getValue(XMLEventReader eventReader) throws XMLStreamException {
|
||||
XMLEvent event = eventReader.nextEvent();
|
||||
return event.asCharacters().getData();
|
||||
}
|
||||
|
||||
private BigDecimal getBigDecimalValue(XMLEventReader eventReader) throws XMLStreamException {
|
||||
XMLEvent event = eventReader.nextEvent();
|
||||
try {
|
||||
return new BigDecimal(event.asCharacters().getData());
|
||||
} catch (NumberFormatException e) {
|
||||
logger.debug("Exception while parsing a BigDecimal", e);
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
private Instant getTimestampValue(XMLEventReader eventReader) throws XMLStreamException {
|
||||
XMLEvent event = eventReader.nextEvent();
|
||||
String dateTimeString = event.asCharacters().getData();
|
||||
try {
|
||||
OffsetDateTime dateTime = OffsetDateTime.parse(dateTimeString, formatter);
|
||||
return dateTime.toInstant();
|
||||
} catch (DateTimeParseException e) {
|
||||
logger.debug("Exception while parsing a DateTime", e);
|
||||
return Instant.MIN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the Warnings Data
|
||||
*/
|
||||
public boolean refresh() {
|
||||
String rawData = dataAccessCached.getValue();
|
||||
if (StringUtils.isEmpty(rawData)) {
|
||||
logger.debug("No Data from Endpoint");
|
||||
return false;
|
||||
}
|
||||
|
||||
cityData.clear();
|
||||
|
||||
try {
|
||||
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
|
||||
XMLStreamReader reader = inputFactory.createXMLStreamReader(new StringReader(rawData));
|
||||
XMLEventReader eventReader = inputFactory.createXMLEventReader(reader);
|
||||
DwdWarningData gemeindeData = new DwdWarningData();
|
||||
boolean insideGemeinde = false;
|
||||
while (eventReader.hasNext()) {
|
||||
XMLEvent event = eventReader.nextEvent();
|
||||
if (!insideGemeinde && event.isStartElement()) {
|
||||
DwdXmlTag xmlTag = DwdXmlTag.getDwdXmlTag(event.asStartElement().getName().getLocalPart());
|
||||
switch (xmlTag) {
|
||||
case WARNUNGEN_GEMEINDEN:
|
||||
gemeindeData = new DwdWarningData();
|
||||
insideGemeinde = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (insideGemeinde && event.isStartElement()) {
|
||||
DwdXmlTag xmlTag = DwdXmlTag.getDwdXmlTag(event.asStartElement().getName().getLocalPart());
|
||||
switch (xmlTag) {
|
||||
case SEVERITY:
|
||||
gemeindeData.setSeverity(Severity.getSeverity(getValue(eventReader)));
|
||||
break;
|
||||
case DESCRIPTION:
|
||||
gemeindeData.setDescription(getValue(eventReader));
|
||||
break;
|
||||
case EFFECTIVE:
|
||||
gemeindeData.setEffective(getTimestampValue(eventReader));
|
||||
break;
|
||||
case EXPIRES:
|
||||
gemeindeData.setExpires(getTimestampValue(eventReader));
|
||||
break;
|
||||
case EVENT:
|
||||
gemeindeData.setEvent(getValue(eventReader));
|
||||
break;
|
||||
case STATUS:
|
||||
gemeindeData.setStatus(getValue(eventReader));
|
||||
break;
|
||||
case MSGTYPE:
|
||||
gemeindeData.setMsgType(getValue(eventReader));
|
||||
break;
|
||||
case HEADLINE:
|
||||
gemeindeData.setHeadline(getValue(eventReader));
|
||||
break;
|
||||
case ONSET:
|
||||
gemeindeData.setOnset(getTimestampValue(eventReader));
|
||||
break;
|
||||
case ALTITUDE:
|
||||
gemeindeData.setAltitude(getBigDecimalValue(eventReader));
|
||||
break;
|
||||
case CEILING:
|
||||
gemeindeData.setCeiling(getBigDecimalValue(eventReader));
|
||||
break;
|
||||
case IDENTIFIER:
|
||||
gemeindeData.setId(getValue(eventReader));
|
||||
break;
|
||||
case INSTRUCTION:
|
||||
gemeindeData.setInstruction(getValue(eventReader));
|
||||
break;
|
||||
case URGENCY:
|
||||
gemeindeData.setUrgency(Urgency.getUrgency(getValue(eventReader)));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (insideGemeinde && event.isEndElement()) {
|
||||
DwdXmlTag xmlTag = DwdXmlTag.getDwdXmlTag(event.asEndElement().getName().getLocalPart());
|
||||
switch (xmlTag) {
|
||||
case WARNUNGEN_GEMEINDEN:
|
||||
if (!gemeindeData.isTest() && !gemeindeData.isCancel()) {
|
||||
cityData.add(gemeindeData);
|
||||
}
|
||||
insideGemeinde = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (XMLStreamException e) {
|
||||
logger.warn("Exception occurred while parsing the XML response: {}", e.getMessage());
|
||||
logger.debug("Exception trace", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
Collections.sort(cityData, new SeverityComparator());
|
||||
return true;
|
||||
}
|
||||
|
||||
private DwdWarningData getGemeindeData(int number) {
|
||||
return cityData.size() <= number ? null : cityData.get(number);
|
||||
}
|
||||
|
||||
public State getWarning(int number) {
|
||||
DwdWarningData data = getGemeindeData(number);
|
||||
return data == null ? OnOffType.OFF : OnOffType.ON;
|
||||
}
|
||||
|
||||
public State getSeverity(int number) {
|
||||
DwdWarningData data = getGemeindeData(number);
|
||||
return data == null ? UnDefType.NULL : StringType.valueOf(data.getSeverity().getText());
|
||||
}
|
||||
|
||||
public State getDescription(int number) {
|
||||
DwdWarningData data = getGemeindeData(number);
|
||||
return data == null ? UnDefType.NULL : StringType.valueOf(data.getDescription());
|
||||
}
|
||||
|
||||
public State getEffective(int number) {
|
||||
DwdWarningData data = getGemeindeData(number);
|
||||
if (data == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
ZonedDateTime zoned = ZonedDateTime.ofInstant(data.getEffective(), ZoneId.systemDefault());
|
||||
return new DateTimeType(zoned);
|
||||
}
|
||||
|
||||
public State getExpires(int number) {
|
||||
DwdWarningData data = getGemeindeData(number);
|
||||
if (data == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
ZonedDateTime zoned = ZonedDateTime.ofInstant(data.getExpires(), ZoneId.systemDefault());
|
||||
return new DateTimeType(zoned);
|
||||
}
|
||||
|
||||
public State getOnset(int number) {
|
||||
DwdWarningData data = getGemeindeData(number);
|
||||
if (data == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
ZonedDateTime zoned = ZonedDateTime.ofInstant(data.getOnset(), ZoneId.systemDefault());
|
||||
return new DateTimeType(zoned);
|
||||
}
|
||||
|
||||
public State getEvent(int number) {
|
||||
DwdWarningData data = getGemeindeData(number);
|
||||
return data == null ? UnDefType.NULL : StringType.valueOf(data.getEvent());
|
||||
}
|
||||
|
||||
public State getHeadline(int number) {
|
||||
DwdWarningData data = getGemeindeData(number);
|
||||
return data == null ? UnDefType.NULL : StringType.valueOf(data.getHeadline());
|
||||
}
|
||||
|
||||
public State getAltitude(int number) {
|
||||
DwdWarningData data = getGemeindeData(number);
|
||||
if (data == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
return new QuantityType<>(data.getAltitude(), ImperialUnits.FOOT);
|
||||
}
|
||||
|
||||
public State getCeiling(int number) {
|
||||
DwdWarningData data = getGemeindeData(number);
|
||||
if (data == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
return new QuantityType<>(data.getCeiling(), ImperialUnits.FOOT);
|
||||
}
|
||||
|
||||
public State getInstruction(int number) {
|
||||
DwdWarningData data = getGemeindeData(number);
|
||||
return data == null ? UnDefType.NULL : StringType.valueOf(data.getInstruction());
|
||||
}
|
||||
|
||||
public State getUrgency(int number) {
|
||||
DwdWarningData data = getGemeindeData(number);
|
||||
return data == null ? UnDefType.NULL : StringType.valueOf(data.getUrgency().getText());
|
||||
}
|
||||
|
||||
public boolean isNew(int number) {
|
||||
DwdWarningData data = getGemeindeData(number);
|
||||
if (data == null) {
|
||||
return false;
|
||||
}
|
||||
return cache.addEntry(data);
|
||||
}
|
||||
|
||||
public void updateCache() {
|
||||
cache.deleteOldEntries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Only for Tests
|
||||
*/
|
||||
protected void setDataAccess(DwdWarningDataAccess dataAccess) {
|
||||
dataAccessCached = new ExpiringCache<>(Duration.ofMinutes(MIN_REFRESH_WAIT_MINUTES),
|
||||
() -> dataAccess.getDataFromEndpoint(""));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.dwdunwetter.internal.data;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
/**
|
||||
* The XML Tags to extract the relevant parts from the API response.
|
||||
* The names map directly to the XML Tags of the API Response.
|
||||
*
|
||||
* @author Martin Koehler - Initial contribution
|
||||
*/
|
||||
public enum DwdXmlTag {
|
||||
|
||||
UNKNOWN(""),
|
||||
SEVERITY("SEVERITY"),
|
||||
DESCRIPTION("DESCRIPTION"),
|
||||
EFFECTIVE("EFFECTIVE"),
|
||||
EXPIRES("EXPIRES"),
|
||||
ONSET("ONSET"),
|
||||
EVENT("EVENT"),
|
||||
STATUS("STATUS"),
|
||||
MSGTYPE("MSGTYPE"),
|
||||
HEADLINE("HEADLINE"),
|
||||
ALTITUDE("ALTITUDE"),
|
||||
CEILING("CEILING"),
|
||||
INSTRUCTION("INSTRUCTION"),
|
||||
URGENCY("URGENCY"),
|
||||
IDENTIFIER("IDENTIFIER"),
|
||||
WARNUNGEN_GEMEINDEN("Warnungen_Gemeinden");
|
||||
|
||||
private String tag;
|
||||
|
||||
private DwdXmlTag(String tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
String getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
public static DwdXmlTag getDwdXmlTag(String tag) {
|
||||
return Arrays.asList(DwdXmlTag.values()).stream().filter(t -> StringUtils.equals(t.getTag(), tag)).findFirst()
|
||||
.orElse(UNKNOWN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.dwdunwetter.internal.data;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
/**
|
||||
* Severity enum to make the severity comparable
|
||||
*
|
||||
* @author Martin Koehler - Initial contribution
|
||||
*/
|
||||
public enum Severity {
|
||||
|
||||
EXTREME(1, "Extreme"),
|
||||
SEVERE(2, "Severe"),
|
||||
MODERATE(3, "Moderate"),
|
||||
MINOR(4, "Minor"),
|
||||
UNKNOWN(5, "Unknown");
|
||||
|
||||
private int order;
|
||||
private String text;
|
||||
|
||||
private Severity(int order, String text) {
|
||||
this.order = order;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public static Severity getSeverity(String input) {
|
||||
return Arrays.asList(Severity.values()).stream()
|
||||
.filter(sev -> StringUtils.equalsIgnoreCase(input, sev.getText())).findAny().orElse(UNKNOWN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.dwdunwetter.internal.data;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import org.apache.commons.lang.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Comperator to sort a Warning first by Severity, second by the onSet date.
|
||||
*
|
||||
* @author Martin Koehler - Initial contribution
|
||||
*/
|
||||
public class SeverityComparator implements Comparator<DwdWarningData> {
|
||||
|
||||
@Override
|
||||
public int compare(DwdWarningData o1, DwdWarningData o2) {
|
||||
Comparator.comparingInt(d -> ((DwdWarningData) d).getSeverity().getOrder());
|
||||
Comparator.comparing(DwdWarningData::getOnset);
|
||||
|
||||
int result = Integer.compare(o1.getSeverity().getOrder(), o2.getSeverity().getOrder());
|
||||
if (result == 0) {
|
||||
result = ObjectUtils.compare(o1.getOnset(), o2.getOnset());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.dwdunwetter.internal.data;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
/**
|
||||
* Enum for the urgency of the warning.
|
||||
*
|
||||
* @author Martin Koehler - Initial contribution
|
||||
*/
|
||||
public enum Urgency {
|
||||
|
||||
IMMEDIATE("Immediate"),
|
||||
FUTURE("Future"),
|
||||
UNKNOWN("Unknown");
|
||||
|
||||
private final String text;
|
||||
|
||||
private Urgency(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public static Urgency getUrgency(String input) {
|
||||
return Arrays.asList(Urgency.values()).stream()
|
||||
.filter(urg -> StringUtils.equalsIgnoreCase(input, urg.getText())).findAny().orElse(UNKNOWN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.dwdunwetter.internal.factory;
|
||||
|
||||
import static org.openhab.binding.dwdunwetter.internal.DwdUnwetterBindingConstants.THING_TYPE_WARNINGS;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.dwdunwetter.internal.handler.DwdUnwetterHandler;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
|
||||
/**
|
||||
* The {@link DwdUnwetterHandlerFactory} is responsible for creating things and thing handlers.
|
||||
*
|
||||
* @author Martin Koehler - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(configurationPid = "binding.dwdunwetter", service = ThingHandlerFactory.class)
|
||||
public class DwdUnwetterHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_WARNINGS);
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (THING_TYPE_WARNINGS.equals(thingTypeUID)) {
|
||||
return new DwdUnwetterHandler(thing);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.dwdunwetter.internal.handler;
|
||||
|
||||
import static org.openhab.binding.dwdunwetter.internal.DwdUnwetterBindingConstants.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.dwdunwetter.internal.config.DwdUnwetterConfiguration;
|
||||
import org.openhab.binding.dwdunwetter.internal.data.DwdWarningsData;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.thing.binding.builder.ChannelBuilder;
|
||||
import org.openhab.core.thing.type.ChannelKind;
|
||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link DwdUnwetterHandler} is responsible for handling commands, which are sent to one of the channels.
|
||||
*
|
||||
* @author Martin Koehler - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DwdUnwetterHandler extends BaseThingHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(DwdUnwetterHandler.class);
|
||||
|
||||
private @Nullable ScheduledFuture<?> refreshJob;
|
||||
private int warningCount;
|
||||
private @Nullable DwdWarningsData data;
|
||||
|
||||
private boolean inRefresh;
|
||||
private boolean initializing;
|
||||
|
||||
public DwdUnwetterHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (command instanceof RefreshType) {
|
||||
scheduler.submit(this::refresh);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the Warning Data.
|
||||
*
|
||||
* The Switch Channel is switched to ON only after all other Channels are updated.
|
||||
* The Switch Channel is switched to OFF before all other Channels are updated.
|
||||
*/
|
||||
private void refresh() {
|
||||
if (inRefresh) {
|
||||
logger.trace("Already refreshing. Ignoring refresh request.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (initializing) {
|
||||
logger.trace("Still initializing. Ignoring refresh request.");
|
||||
return;
|
||||
}
|
||||
|
||||
ThingStatus status = getThing().getStatus();
|
||||
if (status != ThingStatus.ONLINE && status != ThingStatus.UNKNOWN) {
|
||||
logger.debug("Unable to refresh. Thing status is {}", status);
|
||||
return;
|
||||
}
|
||||
|
||||
final DwdWarningsData warningsData = data;
|
||||
if (warningsData == null) {
|
||||
logger.debug("Unable to refresh. No data to use.");
|
||||
return;
|
||||
}
|
||||
|
||||
inRefresh = true;
|
||||
|
||||
boolean refreshSucceeded = warningsData.refresh();
|
||||
if (!refreshSucceeded) {
|
||||
logger.debug("Failed to retrieve new data from the server.");
|
||||
inRefresh = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (status == ThingStatus.UNKNOWN) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
|
||||
updateState(getChannelUuid(CHANNEL_LAST_UPDATED), new DateTimeType());
|
||||
|
||||
for (int i = 0; i < warningCount; i++) {
|
||||
State warning = warningsData.getWarning(i);
|
||||
if (warning == OnOffType.OFF) {
|
||||
updateState(getChannelUuid(CHANNEL_WARNING, i), warning);
|
||||
}
|
||||
updateState(getChannelUuid(CHANNEL_SEVERITY, i), warningsData.getSeverity(i));
|
||||
updateState(getChannelUuid(CHANNEL_DESCRIPTION, i), warningsData.getDescription(i));
|
||||
updateState(getChannelUuid(CHANNEL_EFFECTIVE, i), warningsData.getEffective(i));
|
||||
updateState(getChannelUuid(CHANNEL_EXPIRES, i), warningsData.getExpires(i));
|
||||
updateState(getChannelUuid(CHANNEL_ONSET, i), warningsData.getOnset(i));
|
||||
updateState(getChannelUuid(CHANNEL_EVENT, i), warningsData.getEvent(i));
|
||||
updateState(getChannelUuid(CHANNEL_HEADLINE, i), warningsData.getHeadline(i));
|
||||
updateState(getChannelUuid(CHANNEL_ALTITUDE, i), warningsData.getAltitude(i));
|
||||
updateState(getChannelUuid(CHANNEL_CEILING, i), warningsData.getCeiling(i));
|
||||
updateState(getChannelUuid(CHANNEL_INSTRUCTION, i), warningsData.getInstruction(i));
|
||||
updateState(getChannelUuid(CHANNEL_URGENCY, i), warningsData.getUrgency(i));
|
||||
if (warning == OnOffType.ON) {
|
||||
updateState(getChannelUuid(CHANNEL_WARNING, i), warning);
|
||||
}
|
||||
if (warningsData.isNew(i)) {
|
||||
triggerChannel(getChannelUuid(CHANNEL_UPDATED, i), "NEW");
|
||||
}
|
||||
}
|
||||
|
||||
warningsData.updateCache();
|
||||
inRefresh = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Start initializing!");
|
||||
initializing = true;
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
|
||||
DwdUnwetterConfiguration config = getConfigAs(DwdUnwetterConfiguration.class);
|
||||
warningCount = config.warningCount;
|
||||
|
||||
data = new DwdWarningsData(config.cellId);
|
||||
|
||||
updateThing(editThing().withChannels(createChannels()).build());
|
||||
|
||||
refreshJob = scheduler.scheduleWithFixedDelay(this::refresh, 0, config.refresh, TimeUnit.MINUTES);
|
||||
initializing = false;
|
||||
logger.debug("Finished initializing!");
|
||||
}
|
||||
|
||||
private ChannelUID getChannelUuid(String typeId, int warningNumber) {
|
||||
return new ChannelUID(getThing().getUID(), typeId + (warningNumber + 1));
|
||||
}
|
||||
|
||||
private ChannelUID getChannelUuid(String typeId) {
|
||||
return new ChannelUID(getThing().getUID(), typeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a trigger Channel.
|
||||
*/
|
||||
private Channel createTriggerChannel(String typeId, String label, int warningNumber) {
|
||||
ChannelUID channelUID = getChannelUuid(typeId, warningNumber);
|
||||
return ChannelBuilder.create(channelUID, "String") //
|
||||
.withType(new ChannelTypeUID(BINDING_ID, typeId)) //
|
||||
.withLabel(label + " (" + (warningNumber + 1) + ")")//
|
||||
.withKind(ChannelKind.TRIGGER) //
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a normal, state based, channel associated with a warning.
|
||||
*/
|
||||
private Channel createChannel(String typeId, String itemType, String label, int warningNumber) {
|
||||
ChannelUID channelUID = getChannelUuid(typeId, warningNumber);
|
||||
return ChannelBuilder.create(channelUID, itemType) //
|
||||
.withType(new ChannelTypeUID(BINDING_ID, typeId)) //
|
||||
.withLabel(label + " (" + (warningNumber + 1) + ")")//
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a normal, state based, channel not associated with a warning.
|
||||
*/
|
||||
private Channel createChannel(String typeId, String itemType, String label) {
|
||||
ChannelUID channelUID = getChannelUuid(typeId);
|
||||
return ChannelBuilder.create(channelUID, itemType) //
|
||||
.withType(new ChannelTypeUID(BINDING_ID, typeId)) //
|
||||
.withLabel(label)//
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the ChannelsT for each warning.
|
||||
*
|
||||
* @return The List of Channels
|
||||
*/
|
||||
private List<Channel> createChannels() {
|
||||
List<Channel> channels = new ArrayList<>(warningCount * 11 + 1);
|
||||
channels.add(createChannel(CHANNEL_LAST_UPDATED, "DateTime", "Last Updated"));
|
||||
for (int i = 0; i < warningCount; i++) {
|
||||
channels.add(createChannel(CHANNEL_WARNING, "Switch", "Warning", i));
|
||||
channels.add(createTriggerChannel(CHANNEL_UPDATED, "Updated", i));
|
||||
channels.add(createChannel(CHANNEL_SEVERITY, "String", "Severity", i));
|
||||
channels.add(createChannel(CHANNEL_DESCRIPTION, "String", "Description", i));
|
||||
channels.add(createChannel(CHANNEL_EFFECTIVE, "DateTime", "Issued", i));
|
||||
channels.add(createChannel(CHANNEL_ONSET, "DateTime", "Valid From", i));
|
||||
channels.add(createChannel(CHANNEL_EXPIRES, "DateTime", "Valid To", i));
|
||||
channels.add(createChannel(CHANNEL_EVENT, "String", "Type", i));
|
||||
channels.add(createChannel(CHANNEL_HEADLINE, "String", "Headline", i));
|
||||
channels.add(createChannel(CHANNEL_ALTITUDE, "Number:Length", "Height (from)", i));
|
||||
channels.add(createChannel(CHANNEL_CEILING, "Number:Length", "Height (to)", i));
|
||||
channels.add(createChannel(CHANNEL_INSTRUCTION, "String", "Instruction", i));
|
||||
channels.add(createChannel(CHANNEL_URGENCY, "String", "Urgency", i));
|
||||
}
|
||||
return channels;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
final ScheduledFuture<?> job = refreshJob;
|
||||
|
||||
if (job != null) {
|
||||
job.cancel(true);
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="dwdunwetter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
|
||||
|
||||
<name>DWD Unwetter Binding</name>
|
||||
<description>This is the binding for DWD Unwetter.</description>
|
||||
<author>Martin Koehler</author>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,48 @@
|
||||
# binding
|
||||
binding.dwdunwetter.name = DWD Unwetter Binding
|
||||
binding.dwdunwetter.description = Das DWD Unwetter Binding ermöglicht es über die API des DWD aktuelle Unwetterwarnungen abzurufen
|
||||
|
||||
# thing types
|
||||
thing-type.dwdunwetter.dwdwarnings.label = DWD Unwetter Warnungen
|
||||
thing-type.dwdunwetter.dwdwarnings.description = DWD Unwetterwarnungen für ein Gebiet
|
||||
|
||||
thing-type.config.dwdunwetter.dwdwarnings.cellId.label=Cell-ID
|
||||
thing-type.config.dwdunwetter.dwdwarnings.cellId.description=ID der abzufragenden Zelle.\
|
||||
Siehe https://www.dwd.de/DE/leistungen/opendata/help/warnungen/cap_warncellids_csv.csv.\
|
||||
Es kann auch mittels % eine Gesamtmenge abgefragt werden, z.B. 8111% alle Gemeinden die mit 8111 anfangen.
|
||||
thing-type.config.dwdunwetter.dwdwarnings.refresh.label=Refresh in Minuten
|
||||
thing-type.config.dwdunwetter.dwdwarnings.refresh.description=Abfrageintervall in Minuten. Minimal 15 Minuten.
|
||||
thing-type.config.dwdunwetter.dwdwarnings.warningCount.label=Anzahl bereitgestellter Warnungen
|
||||
thing-type.config.dwdunwetter.dwdwarnings.warningCount.description=Die Anzahl der Warnungen, die als Channels bereitgestellt werden sollen.\
|
||||
Die Warnungen werden dabei nach Severity und dann nach Beginn sortiert. Die erste Warnung ist also die Warnung mit der höchsten Warnstufe
|
||||
|
||||
# channel types
|
||||
channel-type.dwdunwetter.warning.label = Warnung
|
||||
channel-type.dwdunwetter.warning.description = Steht auf ON, wenn eine Warning vorliegt, OFF wenn keine vorliegt.\
|
||||
Es ist garantiert, dass wenn der Channel von OFF auf ON umspringt, die anderen Channels bereits gefüllt sind, mit Ausnahme des Trigger Channels.\
|
||||
Es ist garantiert, dass wenn der Channel von ON auf OFF umspringt, die anderen Channels erst danach auf NULL gesetzt werden.
|
||||
channel-type.dwdunwetter.updated.label = Aktualisiert
|
||||
channel-type.dwdunwetter.updated.description = Triggered, wenn eine Warnung das erste mal gesendet wird.\
|
||||
Dies passiert als letztes, nachdem alle anderen Channels bereits gesetzt sind.
|
||||
channel-type.dwdunwetter.severity.label = Schwere-Grad
|
||||
channel-type.dwdunwetter.severity.description = Schwere-Grad der Warnung. Mögliche Werte sind Minor, Moderate, Severe und Extreme.
|
||||
channel-type.dwdunwetter.description.label = Beschreibung
|
||||
channel-type.dwdunwetter.description.description = Klartext Beschreibung der Warnung
|
||||
channel-type.dwdunwetter.effective.label = Ausgegeben
|
||||
channel-type.dwdunwetter.effective.description = Datum und Uhrzeit, wann die Warnung ausgegeben wurde
|
||||
channel-type.dwdunwetter.onset.label = Gültig ab
|
||||
channel-type.dwdunwetter.onset.description = Datum und Uhrzeit, ab dem die Warnung gültig ist
|
||||
channel-type.dwdunwetter.expires.label = Gültig bis
|
||||
channel-type.dwdunwetter.expires.description = Datum und Uhrzeit, bis zu dem die Warnung gültig ist
|
||||
channel-type.dwdunwetter.headline.label = Titel
|
||||
channel-type.dwdunwetter.headline.description = Titel der Warnung z.B. "Amtliche Warnung vor FROST"
|
||||
channel-type.dwdunwetter.event.label = Art
|
||||
channel-type.dwdunwetter.event.description = Art der Warnung, z.B. FROST
|
||||
channel-type.dwdunwetter.altitude.label = Höhe von
|
||||
channel-type.dwdunwetter.altitude.description = Höhe über dem Meeresspiegel, ab dem die Warnung gilt
|
||||
channel-type.dwdunwetter.ceiling.label = Höhe bis
|
||||
channel-type.dwdunwetter.ceiling.description = Höhe über dem Meeresspiegel, bis zu dem die Warnung gilt
|
||||
channel-type.dwdunwetter.urgency.label=Zeitrahmen
|
||||
channel-type.dwdunwetter.urgency.description=Zeitrahmen der Meldung - Vorabinformation oder Warnung
|
||||
channel-type.dwdunwetter.instruction.label=Zusatztext
|
||||
channel-type.dwdunwetter.instruction.description=Zusatztext zur Warnung (Instruktionen und Sicherheitshinweise)
|
||||
@@ -0,0 +1,135 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="dwdunwetter"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<thing-type id="dwdwarnings">
|
||||
<label>Weather Warnings</label>
|
||||
<description>Weather Warnings for an area</description>
|
||||
<config-description>
|
||||
<parameter name="cellId" type="text" required="true">
|
||||
<label>Cell-ID</label>
|
||||
<description><![CDATA[ID of the area to retrieve weather warnings.
|
||||
For a list of valid IDs look at https://www.dwd.de/DE/leistungen/opendata/help/warnungen/cap_warncellids_csv.csv.
|
||||
With the % sign at the end it is possible to query multiple cells at once. For example with 8111% are cells retrieved that starts with 8111.]]></description>
|
||||
</parameter>
|
||||
<parameter name="refresh" type="integer" unit="min" min="15">
|
||||
<default>30</default>
|
||||
<label>Refresh in Minutes</label>
|
||||
<description>Time between to API requests in minutes. Minimum 15 minutes.</description>
|
||||
</parameter>
|
||||
<parameter name="warningCount" type="integer" min="1" max="15">
|
||||
<default>1</default>
|
||||
<label>Number of Provided Warnings</label>
|
||||
<description><![CDATA[Number of warnings to provide.
|
||||
For each warning there will multiple channels.
|
||||
The warnings are sorted by severity first and begin second so the first warning is always the one with the highest severity.]]></description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<channel-type id="lastUpdated">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Last Updated</label>
|
||||
<description>Timestamp of the last update from the endpoint</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="warning">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Warning</label>
|
||||
<description><![CDATA[ON if a warning is present, OFF else.
|
||||
While be switched to ON only after all other channels - except the trigger channel - are updated.
|
||||
Will be switched to OFF before all other channels are updated to NULL]]></description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="updated">
|
||||
<kind>trigger</kind>
|
||||
<label>Updated</label>
|
||||
<description><![CDATA[Triggers NEW if a warning is send the first time.
|
||||
This happens after all other channels are populated]]></description>
|
||||
<event>
|
||||
<options>
|
||||
<option value="NEW">New</option>
|
||||
</options>
|
||||
</event>
|
||||
</channel-type>
|
||||
<channel-type id="severity">
|
||||
<item-type>String</item-type>
|
||||
<label>Severity</label>
|
||||
<description>Severity of the warning. Possible values are Minor, Moderate, Severe and Extreme.</description>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="Minor">Minor</option>
|
||||
<option value="Moderate">Moderate</option>
|
||||
<option value="Severe">Severe</option>
|
||||
<option value="Extreme">Extreme</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="description">
|
||||
<item-type>String</item-type>
|
||||
<label>Description</label>
|
||||
<description>Textual description of the warning.</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="effective">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Issued</label>
|
||||
<description>Issued Date and Time</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="onset">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Valid From</label>
|
||||
<description>Start Date and Time for which the warning is valid</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="expires">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Valid To</label>
|
||||
<description>End Date and Time for which the warning is valid</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="headline">
|
||||
<item-type>String</item-type>
|
||||
<label>Headline</label>
|
||||
<description>Headline of the warning like "Amtliche Warnung vor FROST"</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="event">
|
||||
<item-type>String</item-type>
|
||||
<label>Type</label>
|
||||
<description>Type of the warning, e.g. FROST</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="altitude">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Height (from)</label>
|
||||
<description>Lower Height above sea level for which the warning is valid</description>
|
||||
<state readOnly="true" pattern="%d m"/>
|
||||
</channel-type>
|
||||
<channel-type id="ceiling">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Height (to)</label>
|
||||
<description>Upper Height above sea level for which the warning is valid</description>
|
||||
<state readOnly="true" pattern="%d m"/>
|
||||
</channel-type>
|
||||
<channel-type id="instruction">
|
||||
<item-type>String</item-type>
|
||||
<label>Instruction</label>
|
||||
<description>Instructions and safety information</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="urgency">
|
||||
<item-type>String</item-type>
|
||||
<label>Urgency</label>
|
||||
<description>Urgency of the warning. Possible values are Immediate and Future.</description>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="Immediate">Immediate</option>
|
||||
<option value="Future">Future</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.dwdunwetter;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.mockito.MockitoAnnotations.initMocks;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.openhab.binding.dwdunwetter.internal.DwdUnwetterBindingConstants;
|
||||
import org.openhab.binding.dwdunwetter.internal.handler.DwdUnwetterHandler;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.test.java.JavaTest;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusInfo;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerCallback;
|
||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
/**
|
||||
* Test cases for {@link DwdUnwetterHandler}. The tests provide mocks for supporting entities using Mockito.
|
||||
*
|
||||
* @author Martin Koehler - Initial contribution
|
||||
*/
|
||||
public class DwdUnwetterHandlerTest extends JavaTest {
|
||||
|
||||
private ThingHandler handler;
|
||||
|
||||
@Mock
|
||||
private ThingHandlerCallback callback;
|
||||
|
||||
@Mock
|
||||
private Thing thing;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
initMocks(this);
|
||||
handler = new DwdUnwetterHandler(thing);
|
||||
handler.setCallback(callback);
|
||||
// mock getConfiguration to prevent NPEs
|
||||
when(thing.getUID()).thenReturn(new ThingUID(DwdUnwetterBindingConstants.BINDING_ID, "test"));
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.put("refresh", Integer.valueOf("1"));
|
||||
configuration.put("warningCount", Integer.valueOf("1"));
|
||||
when(thing.getConfiguration()).thenReturn(configuration);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitializeShouldCallTheCallback() {
|
||||
// we expect the handler#initialize method to call the callback during execution and
|
||||
// pass it the thing and a ThingStatusInfo object containing the ThingStatus of the thing.
|
||||
handler.initialize();
|
||||
|
||||
// the argument captor will capture the argument of type ThingStatusInfo given to the
|
||||
// callback#statusUpdated method.
|
||||
ArgumentCaptor<ThingStatusInfo> statusInfoCaptor = ArgumentCaptor.forClass(ThingStatusInfo.class);
|
||||
|
||||
// verify the interaction with the callback and capture the ThingStatusInfo argument:
|
||||
waitForAssert(() -> {
|
||||
verify(callback, times(1)).statusUpdated(eq(thing), statusInfoCaptor.capture());
|
||||
});
|
||||
|
||||
// assert that the (temporary) UNKNOWN status was to the mocked thing first:
|
||||
assertThat(statusInfoCaptor.getAllValues().get(0).getStatus(), is(ThingStatus.UNKNOWN));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the labels of the channels are equal to the ChannelType Definition
|
||||
*/
|
||||
@Test
|
||||
public void testLabels() throws Exception {
|
||||
handler.initialize();
|
||||
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
InputStream stream = getClass().getResourceAsStream("/OH-INF/thing/thing-types.xml");
|
||||
Document document = builder.parse(stream);
|
||||
NodeList nodeList = document.getElementsByTagName("channel-type");
|
||||
|
||||
thing = handler.getThing();
|
||||
List<Channel> channels = thing.getChannels();
|
||||
for (Channel channel : channels) {
|
||||
String label = getLabel(nodeList, channel.getChannelTypeUID());
|
||||
assertThat(channel.getLabel(), CoreMatchers.startsWith(label));
|
||||
}
|
||||
}
|
||||
|
||||
private String getLabel(NodeList nodeList, ChannelTypeUID uuid) {
|
||||
for (int i = 0; i < nodeList.getLength(); i++) {
|
||||
Node node = nodeList.item(i);
|
||||
Node nodeId = node.getAttributes().getNamedItem("id");
|
||||
if (nodeId == null) {
|
||||
continue;
|
||||
}
|
||||
if (StringUtils.equals(nodeId.getTextContent(), uuid.getId())) {
|
||||
return getLabel(node.getChildNodes());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getLabel(NodeList nodeList) {
|
||||
for (int i = 0; i < nodeList.getLength(); i++) {
|
||||
Node node = nodeList.item(i);
|
||||
if (node.getNodeName().equals("label")) {
|
||||
return node.getTextContent();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.dwdunwetter.internal.data;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Tests for {@link DwdWarningCache}
|
||||
*
|
||||
* @author Martin Koehler - Initial contribution
|
||||
*/
|
||||
public class DwdWarningCacheTest {
|
||||
|
||||
private DwdWarningCache cache;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
cache = new DwdWarningCache();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddEntry() {
|
||||
DwdWarningData data = createData("ID", 0);
|
||||
|
||||
assertThat(cache.addEntry(data), is(true));
|
||||
assertThat(cache.addEntry(data), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteOldEntries() {
|
||||
DwdWarningData data = createData("ID", 0);
|
||||
cache.addEntry(data);
|
||||
|
||||
cache.deleteOldEntries();
|
||||
assertThat(cache.addEntry(data), is(false));
|
||||
|
||||
data = createData("ID", 60 * 60);
|
||||
assertThat(cache.addEntry(data), is(false));
|
||||
cache.deleteOldEntries();
|
||||
assertThat(cache.addEntry(data), is(true));
|
||||
}
|
||||
|
||||
private DwdWarningData createData(String id, long secondsBeforeNow) {
|
||||
DwdWarningData data = new DwdWarningData();
|
||||
data.setId(id);
|
||||
data.setExpires(Instant.now().minusSeconds(secondsBeforeNow));
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.dwdunwetter.internal.data;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.ZoneId;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
|
||||
/**
|
||||
* Tests for {@link DwdWarningsData}
|
||||
*
|
||||
* Uses the warnings.xml from the resources directory instead of accessing the api endpoint.
|
||||
*
|
||||
* The XML has 2 Entries:
|
||||
* <ol>
|
||||
* <li>Amtliche WARNUNG vor WINDBÖEN, MINOR</li>
|
||||
* <li>Amtliche WARNUNG vor STURMBÖEN, MODERATE</li>
|
||||
* </ol>
|
||||
*
|
||||
* @author Martin Koehler - Initial contribution
|
||||
*/
|
||||
public class DwdWarningsDataTest {
|
||||
|
||||
private TestDataProvider testDataProvider;
|
||||
private DwdWarningsData warningsData;
|
||||
|
||||
@Before
|
||||
public void setUp() throws IOException {
|
||||
this.testDataProvider = new TestDataProvider();
|
||||
loadXmlFromFile();
|
||||
warningsData = new DwdWarningsData("");
|
||||
warningsData.setDataAccess(testDataProvider);
|
||||
warningsData.refresh();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetHeadline() {
|
||||
assertThat(warningsData.getHeadline(0), is("Amtliche WARNUNG vor STURMBÖEN"));
|
||||
assertThat(warningsData.getHeadline(1), is("Amtliche WARNUNG vor WINDBÖEN"));
|
||||
assertThat(warningsData.getHeadline(2), is(UnDefType.NULL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSeverity() {
|
||||
assertThat(warningsData.getSeverity(0), is("Moderate"));
|
||||
assertThat(warningsData.getSeverity(1), is("Minor"));
|
||||
assertThat(warningsData.getSeverity(2), is(UnDefType.NULL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetEvent() {
|
||||
assertThat(warningsData.getEvent(0), is("STURMBÖEN"));
|
||||
assertThat(warningsData.getEvent(1), is("WINDBÖEN"));
|
||||
assertThat(warningsData.getEvent(2), is(UnDefType.NULL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDescription() {
|
||||
assertThat(warningsData.getDescription(0), is(
|
||||
"Es treten Sturmböen mit Geschwindigkeiten zwischen 60 km/h (17m/s, 33kn, Bft 7) und 80 km/h (22m/s, 44kn, Bft 9) anfangs aus südwestlicher, später aus westlicher Richtung auf. In Schauernähe sowie in exponierten Lagen muss mit schweren Sturmböen um 90 km/h (25m/s, 48kn, Bft 10) gerechnet werden."));
|
||||
assertThat(warningsData.getDescription(1), is(
|
||||
"Es treten Windböen mit Geschwindigkeiten bis 60 km/h (17m/s, 33kn, Bft 7) aus westlicher Richtung auf. In Schauernähe sowie in exponierten Lagen muss mit Sturmböen bis 80 km/h (22m/s, 44kn, Bft 9) gerechnet werden."));
|
||||
assertThat(warningsData.getDescription(2), is(UnDefType.NULL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAltitude() {
|
||||
assertThat(warningsData.getAltitude(0).format("%.0f ft"), is("0 ft"));
|
||||
assertThat(warningsData.getAltitude(1).format("%.0f ft"), is("0 ft"));
|
||||
assertThat(warningsData.getAltitude(2), is(UnDefType.NULL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCeiling() {
|
||||
assertThat(warningsData.getCeiling(0).format("%.0f ft"), is("9843 ft"));
|
||||
assertThat(warningsData.getCeiling(1).format("%.0f ft"), is("9843 ft"));
|
||||
assertThat(warningsData.getCeiling(2), is(UnDefType.NULL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetExpires() {
|
||||
// Conversion is needed as getExpires returns the Date with the System Default Zone
|
||||
assertThat(((DateTimeType) warningsData.getExpires(0)).getZonedDateTime().withZoneSameInstant(ZoneId.of("UTC"))
|
||||
.toString(), is("2018-12-22T18:00Z[UTC]"));
|
||||
assertThat(((DateTimeType) warningsData.getExpires(1)).getZonedDateTime().withZoneSameInstant(ZoneId.of("UTC"))
|
||||
.toString(), is("2018-12-23T01:00Z[UTC]"));
|
||||
assertThat(warningsData.getExpires(2), is(UnDefType.NULL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOnset() {
|
||||
// Conversion is needed as getOnset returns the Date with the System Default Zone
|
||||
assertThat(((DateTimeType) warningsData.getOnset(0)).getZonedDateTime().withZoneSameInstant(ZoneId.of("UTC"))
|
||||
.toString(), is("2018-12-21T10:00Z[UTC]"));
|
||||
assertThat(((DateTimeType) warningsData.getOnset(1)).getZonedDateTime().withZoneSameInstant(ZoneId.of("UTC"))
|
||||
.toString(), is("2018-12-22T18:00Z[UTC]"));
|
||||
assertThat(warningsData.getOnset(2), is(UnDefType.NULL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetEffective() {
|
||||
// Conversion is needed as getEffective returns the Date with the System Default Zone
|
||||
assertThat(((DateTimeType) warningsData.getEffective(0)).getZonedDateTime()
|
||||
.withZoneSameInstant(ZoneId.of("UTC")).toString(), is("2018-12-22T03:02Z[UTC]"));
|
||||
assertThat(((DateTimeType) warningsData.getEffective(1)).getZonedDateTime()
|
||||
.withZoneSameInstant(ZoneId.of("UTC")).toString(), is("2018-12-22T10:15Z[UTC]"));
|
||||
assertThat(warningsData.getEffective(2), is(UnDefType.NULL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetWarning() {
|
||||
assertThat(warningsData.getWarning(0), is(OnOffType.ON));
|
||||
assertThat(warningsData.getWarning(1), is(OnOffType.ON));
|
||||
assertThat(warningsData.getWarning(2), is(OnOffType.OFF));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testisNew() {
|
||||
assertThat(warningsData.isNew(0), is(true));
|
||||
assertThat(warningsData.isNew(1), is(true));
|
||||
assertThat(warningsData.isNew(2), is(false));
|
||||
// No longer new
|
||||
assertThat(warningsData.isNew(0), is(false));
|
||||
assertThat(warningsData.isNew(1), is(false));
|
||||
assertThat(warningsData.isNew(2), is(false));
|
||||
}
|
||||
|
||||
private void loadXmlFromFile() throws IOException {
|
||||
InputStream stream = getClass().getResourceAsStream("warnings.xml");
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8));
|
||||
String line = null;
|
||||
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
while ((line = reader.readLine()) != null) {
|
||||
stringWriter.write(line);
|
||||
}
|
||||
reader.close();
|
||||
testDataProvider.rawData = stringWriter.toString();
|
||||
}
|
||||
|
||||
private class TestDataProvider extends DwdWarningDataAccess {
|
||||
|
||||
private String rawData = "";
|
||||
|
||||
@Override
|
||||
public String getDataFromEndpoint(String cellId) {
|
||||
return rawData;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<wfs:FeatureCollection xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:dwd="http://www.dwd.de" xmlns:wfs="http://www.opengis.net/wfs/2.0" xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" numberMatched="2" numberReturned="2" timeStamp="2018-12-22T10:34:44.134Z" xsi:schemaLocation="http://www.dwd.de https://maps.dwd.de:443/geoserver/dwd/wfs?service=WFS&version=2.0.0&request=DescribeFeatureType&typeName=dwd%3AWarnungen_Gemeinden http://www.opengis.net/wfs/2.0 https://maps.dwd.de:443/geoserver/schemas/wfs/2.0/wfs.xsd http://www.opengis.net/gml/3.2 https://maps.dwd.de:443/geoserver/schemas/gml/3.2.1/gml.xsd">
|
||||
<wfs:boundedBy>
|
||||
<gml:Envelope>
|
||||
<gml:lowerCorner>47.7798 12.874</gml:lowerCorner>
|
||||
<gml:upperCorner>47.8513 12.9787</gml:upperCorner>
|
||||
</gml:Envelope>
|
||||
</wfs:boundedBy>
|
||||
<wfs:member>
|
||||
<dwd:Warnungen_Gemeinden gml:id="Warnungen_Gemeinden.809172111.2.49.0.1.276.0.DWD.PVW.1545473700000.f6482b21-04d4-4811-b379-652602cb9703.DEU">
|
||||
<gml:boundedBy>
|
||||
<gml:Envelope srsName="urn:ogc:def:crs:EPSG::4326" srsDimension="2">
|
||||
<gml:lowerCorner>47.7798 12.874</gml:lowerCorner>
|
||||
<gml:upperCorner>47.8513 12.9787</gml:upperCorner>
|
||||
</gml:Envelope>
|
||||
</gml:boundedBy>
|
||||
<dwd:AREADESC>Ainring</dwd:AREADESC>
|
||||
<dwd:NAME>Gemeinde Ainring</dwd:NAME>
|
||||
<dwd:WARNCELLID>809172111</dwd:WARNCELLID>
|
||||
<dwd:IDENTIFIER>2.49.0.1.276.0.DWD.PVW.1545473700000.f6482b21-04d4-4811-b379-652602cb9703.DEU</dwd:IDENTIFIER>
|
||||
<dwd:SENDER>CAP@dwd.de</dwd:SENDER>
|
||||
<dwd:SENT>2018-12-22T10:15:00Z</dwd:SENT>
|
||||
<dwd:STATUS>Actual</dwd:STATUS>
|
||||
<dwd:MSGTYPE>Alert</dwd:MSGTYPE>
|
||||
<dwd:SOURCE>PVW</dwd:SOURCE>
|
||||
<dwd:SCOPE>Public</dwd:SCOPE>
|
||||
<dwd:CODE>id:2.49.0.1.276.0.DWD.PVW.1545473700000.f6482b21-04d4-4811-b379-652602cb9703</dwd:CODE>
|
||||
<dwd:LANGUAGE>de-DE</dwd:LANGUAGE>
|
||||
<dwd:CATEGORY>Met</dwd:CATEGORY>
|
||||
<dwd:EVENT>WINDBÖEN</dwd:EVENT>
|
||||
<dwd:RESPONSETYPE>None</dwd:RESPONSETYPE>
|
||||
<dwd:URGENCY>Immediate</dwd:URGENCY>
|
||||
<dwd:SEVERITY>Minor</dwd:SEVERITY>
|
||||
<dwd:CERTAINTY>Likely</dwd:CERTAINTY>
|
||||
<dwd:EC_PROFILE>2.1</dwd:EC_PROFILE>
|
||||
<dwd:EC_LICENSE>Geobasisdaten: Copyright Bundesamt für Kartographie und Geodäsie, Frankfurt am Main, 2013</dwd:EC_LICENSE>
|
||||
<dwd:EC_II>51</dwd:EC_II>
|
||||
<dwd:EC_GROUP>WIND</dwd:EC_GROUP>
|
||||
<dwd:EC_AREA_COLOR>255 255 0</dwd:EC_AREA_COLOR>
|
||||
<dwd:EFFECTIVE>2018-12-22T10:15:00Z</dwd:EFFECTIVE>
|
||||
<dwd:ONSET>2018-12-22T18:00:00Z</dwd:ONSET>
|
||||
<dwd:EXPIRES>2018-12-23T01:00:00Z</dwd:EXPIRES>
|
||||
<dwd:SENDERNAME>DWD / Nationales Warnzentrum Offenbach</dwd:SENDERNAME>
|
||||
<dwd:HEADLINE>Amtliche WARNUNG vor WINDBÖEN</dwd:HEADLINE>
|
||||
<dwd:DESCRIPTION>Es treten Windböen mit Geschwindigkeiten bis 60 km/h (17m/s, 33kn, Bft 7) aus westlicher Richtung auf. In Schauernähe sowie in exponierten Lagen muss mit Sturmböen bis 80 km/h (22m/s, 44kn, Bft 9) gerechnet werden.</dwd:DESCRIPTION>
|
||||
<dwd:WEB>http://www.wettergefahren.de</dwd:WEB>
|
||||
<dwd:CONTACT>Deutscher Wetterdienst</dwd:CONTACT>
|
||||
<dwd:PARAMETERNAME>Windrichtung;Böen;Exponierte Böen</dwd:PARAMETERNAME>
|
||||
<dwd:PARAMATERVALUE>W;<60 [km/h];<80 [km/h]</dwd:PARAMATERVALUE>
|
||||
<dwd:ALTITUDE>0</dwd:ALTITUDE>
|
||||
<dwd:CEILING>9842.5197</dwd:CEILING>
|
||||
<dwd:THE_GEOM>
|
||||
<gml:MultiSurface srsName="urn:ogc:def:crs:EPSG::4326" srsDimension="2" gml:id="Warnungen_Gemeinden.809172111.2.49.0.1.276.0.DWD.PVW.1545473700000.f6482b21-04d4-4811-b379-652602cb9703.DEU.THE_GEOM">
|
||||
<gml:surfaceMember>
|
||||
<gml:Polygon gml:id="Warnungen_Gemeinden.809172111.2.49.0.1.276.0.DWD.PVW.1545473700000.f6482b21-04d4-4811-b379-652602cb9703.DEU.THE_GEOM.1">
|
||||
<gml:exterior>
|
||||
<gml:LinearRing>
|
||||
<gml:posList>47.8513 12.9113 47.8327 12.8749 47.8241 12.874 47.8066 12.9062 47.8039 12.9186 47.7798 12.9396 47.8191 12.9787 47.8262 12.9556 47.8441 12.9405 47.8513 12.9113</gml:posList>
|
||||
</gml:LinearRing>
|
||||
</gml:exterior>
|
||||
</gml:Polygon>
|
||||
</gml:surfaceMember>
|
||||
</gml:MultiSurface>
|
||||
</dwd:THE_GEOM>
|
||||
</dwd:Warnungen_Gemeinden>
|
||||
</wfs:member>
|
||||
<wfs:member>
|
||||
<dwd:Warnungen_Gemeinden gml:id="Warnungen_Gemeinden.809172111.2.49.0.1.276.0.DWD.PVW.1545447720000.90650875-4c27-4ccd-8a11-ecfbdff8e3cf.DEU">
|
||||
<gml:boundedBy>
|
||||
<gml:Envelope srsName="urn:ogc:def:crs:EPSG::4326" srsDimension="2">
|
||||
<gml:lowerCorner>47.7798 12.874</gml:lowerCorner>
|
||||
<gml:upperCorner>47.8513 12.9787</gml:upperCorner>
|
||||
</gml:Envelope>
|
||||
</gml:boundedBy>
|
||||
<dwd:AREADESC>Ainring</dwd:AREADESC>
|
||||
<dwd:NAME>Gemeinde Ainring</dwd:NAME>
|
||||
<dwd:WARNCELLID>809172111</dwd:WARNCELLID>
|
||||
<dwd:IDENTIFIER>2.49.0.1.276.0.DWD.PVW.1545447720000.90650875-4c27-4ccd-8a11-ecfbdff8e3cf.DEU</dwd:IDENTIFIER>
|
||||
<dwd:SENDER>CAP@dwd.de</dwd:SENDER>
|
||||
<dwd:SENT>2018-12-22T03:02:00Z</dwd:SENT>
|
||||
<dwd:STATUS>Actual</dwd:STATUS>
|
||||
<dwd:MSGTYPE>Update</dwd:MSGTYPE>
|
||||
<dwd:SOURCE>PVW</dwd:SOURCE>
|
||||
<dwd:SCOPE>Public</dwd:SCOPE>
|
||||
<dwd:CODE>id:2.49.0.1.276.0.DWD.PVW.1545447720000.90650875-4c27-4ccd-8a11-ecfbdff8e3cf</dwd:CODE>
|
||||
<dwd:LANGUAGE>de-DE</dwd:LANGUAGE>
|
||||
<dwd:CATEGORY>Met</dwd:CATEGORY>
|
||||
<dwd:EVENT>STURMBÖEN</dwd:EVENT>
|
||||
<dwd:RESPONSETYPE>Prepare</dwd:RESPONSETYPE>
|
||||
<dwd:URGENCY>Immediate</dwd:URGENCY>
|
||||
<dwd:SEVERITY>Moderate</dwd:SEVERITY>
|
||||
<dwd:CERTAINTY>Likely</dwd:CERTAINTY>
|
||||
<dwd:EC_PROFILE>2.1</dwd:EC_PROFILE>
|
||||
<dwd:EC_LICENSE>Geobasisdaten: Copyright Bundesamt für Kartographie und Geodäsie, Frankfurt am Main, 2013</dwd:EC_LICENSE>
|
||||
<dwd:EC_II>52</dwd:EC_II>
|
||||
<dwd:EC_GROUP>WIND</dwd:EC_GROUP>
|
||||
<dwd:EC_AREA_COLOR>255 153 0</dwd:EC_AREA_COLOR>
|
||||
<dwd:EFFECTIVE>2018-12-22T03:02:00Z</dwd:EFFECTIVE>
|
||||
<dwd:ONSET>2018-12-21T10:00:00Z</dwd:ONSET>
|
||||
<dwd:EXPIRES>2018-12-22T18:00:00Z</dwd:EXPIRES>
|
||||
<dwd:SENDERNAME>DWD / Nationales Warnzentrum Offenbach</dwd:SENDERNAME>
|
||||
<dwd:HEADLINE>Amtliche WARNUNG vor STURMBÖEN</dwd:HEADLINE>
|
||||
<dwd:DESCRIPTION>Es treten Sturmböen mit Geschwindigkeiten zwischen 60 km/h (17m/s, 33kn, Bft 7) und 80 km/h (22m/s, 44kn, Bft 9) anfangs aus südwestlicher, später aus westlicher Richtung auf. In Schauernähe sowie in exponierten Lagen muss mit schweren Sturmböen um 90 km/h (25m/s, 48kn, Bft 10) gerechnet werden.</dwd:DESCRIPTION>
|
||||
<dwd:INSTRUCTION>ACHTUNG! Hinweis auf mögliche Gefahren: Es können zum Beispiel einzelne Äste herabstürzen. Achten Sie besonders auf herabfallende Gegenstände.</dwd:INSTRUCTION>
|
||||
<dwd:WEB>http://www.wettergefahren.de</dwd:WEB>
|
||||
<dwd:CONTACT>Deutscher Wetterdienst</dwd:CONTACT>
|
||||
<dwd:PARAMETERNAME>Böen;Exponierte Böen;Windrichtung;Windrichtung</dwd:PARAMETERNAME>
|
||||
<dwd:PARAMATERVALUE>60 bis 80 [km/h];~90 [km/h];SW;W</dwd:PARAMATERVALUE>
|
||||
<dwd:ALTITUDE>0</dwd:ALTITUDE>
|
||||
<dwd:CEILING>9842.5197</dwd:CEILING>
|
||||
<dwd:THE_GEOM>
|
||||
<gml:MultiSurface srsName="urn:ogc:def:crs:EPSG::4326" srsDimension="2" gml:id="Warnungen_Gemeinden.809172111.2.49.0.1.276.0.DWD.PVW.1545447720000.90650875-4c27-4ccd-8a11-ecfbdff8e3cf.DEU.THE_GEOM">
|
||||
<gml:surfaceMember>
|
||||
<gml:Polygon gml:id="Warnungen_Gemeinden.809172111.2.49.0.1.276.0.DWD.PVW.1545447720000.90650875-4c27-4ccd-8a11-ecfbdff8e3cf.DEU.THE_GEOM.1">
|
||||
<gml:exterior>
|
||||
<gml:LinearRing>
|
||||
<gml:posList>47.8513 12.9113 47.8327 12.8749 47.8241 12.874 47.8066 12.9062 47.8039 12.9186 47.7798 12.9396 47.8191 12.9787 47.8262 12.9556 47.8441 12.9405 47.8513 12.9113</gml:posList>
|
||||
</gml:LinearRing>
|
||||
</gml:exterior>
|
||||
</gml:Polygon>
|
||||
</gml:surfaceMember>
|
||||
</gml:MultiSurface>
|
||||
</dwd:THE_GEOM>
|
||||
</dwd:Warnungen_Gemeinden>
|
||||
</wfs:member>
|
||||
</wfs:FeatureCollection>
|
||||
Reference in New Issue
Block a user