2 * Copyright (c) 2010-2020 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.dwdunwetter.internal.data;
15 import java.io.StringReader;
16 import java.math.BigDecimal;
17 import java.time.Duration;
18 import java.time.Instant;
19 import java.time.OffsetDateTime;
20 import java.time.ZoneId;
21 import java.time.ZonedDateTime;
22 import java.time.format.DateTimeFormatter;
23 import java.time.format.DateTimeFormatterBuilder;
24 import java.time.format.DateTimeParseException;
25 import java.util.Collections;
26 import java.util.LinkedList;
27 import java.util.List;
29 import javax.xml.stream.XMLEventReader;
30 import javax.xml.stream.XMLInputFactory;
31 import javax.xml.stream.XMLStreamException;
32 import javax.xml.stream.XMLStreamReader;
33 import javax.xml.stream.events.XMLEvent;
35 import org.apache.commons.lang.StringUtils;
36 import org.openhab.core.cache.ExpiringCache;
37 import org.openhab.core.library.types.DateTimeType;
38 import org.openhab.core.library.types.OnOffType;
39 import org.openhab.core.library.types.QuantityType;
40 import org.openhab.core.library.types.StringType;
41 import org.openhab.core.library.unit.ImperialUnits;
42 import org.openhab.core.types.State;
43 import org.openhab.core.types.UnDefType;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
48 * Contains the Data for all retrieved warnings for one thing.
50 * @author Martin Koehler - Initial contribution
52 public class DwdWarningsData {
54 private static final int MIN_REFRESH_WAIT_MINUTES = 5;
56 private final Logger logger = LoggerFactory.getLogger(DwdWarningsData.class);
58 private List<DwdWarningData> cityData = new LinkedList<>();
60 private DwdWarningCache cache = new DwdWarningCache();
62 private ExpiringCache<String> dataAccessCached;
64 private DateTimeFormatter formatter = new DateTimeFormatterBuilder()
66 .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
67 // offset (hh:mm - "+00:00" when it's zero)
68 .optionalStart().appendOffset("+HH:MM", "+00:00").optionalEnd()
69 // offset (hhmm - "+0000" when it's zero)
70 .optionalStart().appendOffset("+HHMM", "+0000").optionalEnd()
71 // offset (hh - "Z" when it's zero)
72 .optionalStart().appendOffset("+HH", "Z").optionalEnd()
76 public DwdWarningsData(String cellId) {
77 DwdWarningDataAccess dataAccess = new DwdWarningDataAccess();
78 this.dataAccessCached = new ExpiringCache<>(Duration.ofMinutes(MIN_REFRESH_WAIT_MINUTES),
79 () -> dataAccess.getDataFromEndpoint(cellId));
82 private String getValue(XMLEventReader eventReader) throws XMLStreamException {
83 XMLEvent event = eventReader.nextEvent();
84 return event.asCharacters().getData();
87 private BigDecimal getBigDecimalValue(XMLEventReader eventReader) throws XMLStreamException {
88 XMLEvent event = eventReader.nextEvent();
90 return new BigDecimal(event.asCharacters().getData());
91 } catch (NumberFormatException e) {
92 logger.debug("Exception while parsing a BigDecimal", e);
93 return BigDecimal.ZERO;
97 private Instant getTimestampValue(XMLEventReader eventReader) throws XMLStreamException {
98 XMLEvent event = eventReader.nextEvent();
99 String dateTimeString = event.asCharacters().getData();
101 OffsetDateTime dateTime = OffsetDateTime.parse(dateTimeString, formatter);
102 return dateTime.toInstant();
103 } catch (DateTimeParseException e) {
104 logger.debug("Exception while parsing a DateTime", e);
110 * Refreshes the Warnings Data
112 public boolean refresh() {
113 String rawData = dataAccessCached.getValue();
114 if (StringUtils.isEmpty(rawData)) {
115 logger.debug("No Data from Endpoint");
122 XMLInputFactory inputFactory = XMLInputFactory.newInstance();
123 XMLStreamReader reader = inputFactory.createXMLStreamReader(new StringReader(rawData));
124 XMLEventReader eventReader = inputFactory.createXMLEventReader(reader);
125 DwdWarningData gemeindeData = new DwdWarningData();
126 boolean insideGemeinde = false;
127 while (eventReader.hasNext()) {
128 XMLEvent event = eventReader.nextEvent();
129 if (!insideGemeinde && event.isStartElement()) {
130 DwdXmlTag xmlTag = DwdXmlTag.getDwdXmlTag(event.asStartElement().getName().getLocalPart());
132 case WARNUNGEN_GEMEINDEN:
133 gemeindeData = new DwdWarningData();
134 insideGemeinde = true;
139 } else if (insideGemeinde && event.isStartElement()) {
140 DwdXmlTag xmlTag = DwdXmlTag.getDwdXmlTag(event.asStartElement().getName().getLocalPart());
143 gemeindeData.setSeverity(Severity.getSeverity(getValue(eventReader)));
146 gemeindeData.setDescription(getValue(eventReader));
149 gemeindeData.setEffective(getTimestampValue(eventReader));
152 gemeindeData.setExpires(getTimestampValue(eventReader));
155 gemeindeData.setEvent(getValue(eventReader));
158 gemeindeData.setStatus(getValue(eventReader));
161 gemeindeData.setMsgType(getValue(eventReader));
164 gemeindeData.setHeadline(getValue(eventReader));
167 gemeindeData.setOnset(getTimestampValue(eventReader));
170 gemeindeData.setAltitude(getBigDecimalValue(eventReader));
173 gemeindeData.setCeiling(getBigDecimalValue(eventReader));
176 gemeindeData.setId(getValue(eventReader));
179 gemeindeData.setInstruction(getValue(eventReader));
182 gemeindeData.setUrgency(Urgency.getUrgency(getValue(eventReader)));
187 } else if (insideGemeinde && event.isEndElement()) {
188 DwdXmlTag xmlTag = DwdXmlTag.getDwdXmlTag(event.asEndElement().getName().getLocalPart());
190 case WARNUNGEN_GEMEINDEN:
191 if (!gemeindeData.isTest() && !gemeindeData.isCancel()) {
192 cityData.add(gemeindeData);
194 insideGemeinde = false;
201 } catch (XMLStreamException e) {
202 logger.warn("Exception occurred while parsing the XML response: {}", e.getMessage());
203 logger.debug("Exception trace", e);
207 Collections.sort(cityData, new SeverityComparator());
211 private DwdWarningData getGemeindeData(int number) {
212 return cityData.size() <= number ? null : cityData.get(number);
215 public State getWarning(int number) {
216 DwdWarningData data = getGemeindeData(number);
217 return data == null ? OnOffType.OFF : OnOffType.ON;
220 public State getSeverity(int number) {
221 DwdWarningData data = getGemeindeData(number);
222 return data == null ? UnDefType.NULL : StringType.valueOf(data.getSeverity().getText());
225 public State getDescription(int number) {
226 DwdWarningData data = getGemeindeData(number);
227 return data == null ? UnDefType.NULL : StringType.valueOf(data.getDescription());
230 public State getEffective(int number) {
231 DwdWarningData data = getGemeindeData(number);
233 return UnDefType.NULL;
235 ZonedDateTime zoned = ZonedDateTime.ofInstant(data.getEffective(), ZoneId.systemDefault());
236 return new DateTimeType(zoned);
239 public State getExpires(int number) {
240 DwdWarningData data = getGemeindeData(number);
242 return UnDefType.NULL;
244 ZonedDateTime zoned = ZonedDateTime.ofInstant(data.getExpires(), ZoneId.systemDefault());
245 return new DateTimeType(zoned);
248 public State getOnset(int number) {
249 DwdWarningData data = getGemeindeData(number);
251 return UnDefType.NULL;
253 ZonedDateTime zoned = ZonedDateTime.ofInstant(data.getOnset(), ZoneId.systemDefault());
254 return new DateTimeType(zoned);
257 public State getEvent(int number) {
258 DwdWarningData data = getGemeindeData(number);
259 return data == null ? UnDefType.NULL : StringType.valueOf(data.getEvent());
262 public State getHeadline(int number) {
263 DwdWarningData data = getGemeindeData(number);
264 return data == null ? UnDefType.NULL : StringType.valueOf(data.getHeadline());
267 public State getAltitude(int number) {
268 DwdWarningData data = getGemeindeData(number);
270 return UnDefType.NULL;
272 return new QuantityType<>(data.getAltitude(), ImperialUnits.FOOT);
275 public State getCeiling(int number) {
276 DwdWarningData data = getGemeindeData(number);
278 return UnDefType.NULL;
280 return new QuantityType<>(data.getCeiling(), ImperialUnits.FOOT);
283 public State getInstruction(int number) {
284 DwdWarningData data = getGemeindeData(number);
285 return data == null ? UnDefType.NULL : StringType.valueOf(data.getInstruction());
288 public State getUrgency(int number) {
289 DwdWarningData data = getGemeindeData(number);
290 return data == null ? UnDefType.NULL : StringType.valueOf(data.getUrgency().getText());
293 public boolean isNew(int number) {
294 DwdWarningData data = getGemeindeData(number);
298 return cache.addEntry(data);
301 public void updateCache() {
302 cache.deleteOldEntries();
308 protected void setDataAccess(DwdWarningDataAccess dataAccess) {
309 dataAccessCached = new ExpiringCache<>(Duration.ofMinutes(MIN_REFRESH_WAIT_MINUTES),
310 () -> dataAccess.getDataFromEndpoint(""));