added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.buienradar-${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-buienradar" description="Buienradar Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.buienradar/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,57 @@
/**
* 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.buienradar.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link BuienradarBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Edwin de Jong - Initial contribution
*/
@NonNullByDefault
public class BuienradarBindingConstants {
private static final String BINDING_ID = "buienradar";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_RAIN_FORECAST = new ThingTypeUID(BINDING_ID, "rain_forecast");
public static final String ACTUAL_DATETIME = "actual_datetime";
public static final String FORECAST_0 = "forecast_0";
public static final String FORECAST_5 = "forecast_5";
public static final String FORECAST_10 = "forecast_10";
public static final String FORECAST_15 = "forecast_15";
public static final String FORECAST_20 = "forecast_20";
public static final String FORECAST_25 = "forecast_25";
public static final String FORECAST_30 = "forecast_30";
public static final String FORECAST_35 = "forecast_35";
public static final String FORECAST_40 = "forecast_40";
public static final String FORECAST_45 = "forecast_45";
public static final String FORECAST_50 = "forecast_50";
public static final String FORECAST_55 = "forecast_55";
public static final String FORECAST_60 = "forecast_60";
public static final String FORECAST_65 = "forecast_65";
public static final String FORECAST_70 = "forecast_70";
public static final String FORECAST_75 = "forecast_75";
public static final String FORECAST_80 = "forecast_80";
public static final String FORECAST_85 = "forecast_85";
public static final String FORECAST_90 = "forecast_90";
public static final String FORECAST_95 = "forecast_95";
public static final String FORECAST_100 = "forecast_100";
public static final String FORECAST_105 = "forecast_105";
public static final String FORECAST_110 = "forecast_110";
public static final String FORECAST_115 = "forecast_115";
}

View File

@@ -0,0 +1,42 @@
/**
* 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.buienradar.internal;
/**
* The {@link BuienradarConfiguration} class contains fields mapping thing configuration parameters.
*
* @author Edwin de Jong - Initial contribution
*/
public class BuienradarConfiguration {
/**
* Location of the forecast from buienradar
*/
public String location;
/**
* Refresh interval for retrieving results from buienradar.
*/
public Integer refreshIntervalMinutes;
/**
* Number of retries to try to retrieve buienradar results.
*/
public Integer retries;
/**
* Exponential backoff base value for retries in seconds. For example, when this is 2 seconds, will retry at 2, 4,
* 8, 16, 32, 64 seconds.
*/
public Integer exponentialBackoffRetryBaseInSeconds;
}

View File

@@ -0,0 +1,180 @@
/**
* 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.buienradar.internal;
import static java.util.concurrent.TimeUnit.MINUTES;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.lang.StringUtils;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.buienradar.internal.buienradarapi.BuienradarPredictionAPI;
import org.openhab.binding.buienradar.internal.buienradarapi.Prediction;
import org.openhab.binding.buienradar.internal.buienradarapi.PredictionAPI;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.PointType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.SmartHomeUnits;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link BuienradarHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Edwin de Jong - Initial contribution
*/
@NonNullByDefault
public class BuienradarHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(BuienradarHandler.class);
private final PredictionAPI client = new BuienradarPredictionAPI();
private @NonNullByDefault({}) ScheduledFuture<?> listenableFuture;
/**
* Prevents race-condition access to listenableFuture.
*/
private final Lock listenableFutureLock = new ReentrantLock();
private @NonNullByDefault({}) PointType location;
private @NonNullByDefault({}) BuienradarConfiguration config;
public BuienradarHandler(Thing thing) {
super(thing);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
}
@SuppressWarnings("null")
@Override
public void initialize() {
this.config = getConfigAs(BuienradarConfiguration.class);
boolean configValid = true;
if (StringUtils.trimToNull(config.location) == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.conf-error-missing-location");
configValid = false;
}
try {
location = new PointType(config.location);
} catch (IllegalArgumentException e) {
location = null;
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.conf-error-parsing-location");
configValid = false;
}
if (configValid) {
updateStatus(ThingStatus.UNKNOWN);
}
try {
listenableFutureLock.lock();
if (listenableFuture == null || listenableFuture.isCancelled()) {
listenableFuture = scheduler.scheduleWithFixedDelay(() -> refresh(), 0L, config.refreshIntervalMinutes,
MINUTES);
}
} finally {
listenableFutureLock.unlock();
}
}
private void refresh() {
refresh(config.retries, ZonedDateTime.now().plusMinutes(config.refreshIntervalMinutes),
config.exponentialBackoffRetryBaseInSeconds);
}
private void refresh(int tries, ZonedDateTime nextRefresh, int retryInSeconds) {
if (nextRefresh.isBefore(ZonedDateTime.now())) {
// The next refresh is already running, stop retries.
return;
}
if (tries <= 0) {
// We are out of tries, stop retrying.
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
return;
}
try {
@SuppressWarnings("null")
final Optional<List<Prediction>> predictionsOpt = client.getPredictions(location);
if (!predictionsOpt.isPresent()) {
// Did not get a result, retry the retrieval.
logger.warn("Did not get a result from buienradar. Retrying. {} tries remaining, waiting {} seconds.",
tries, retryInSeconds);
scheduler.schedule(() -> refresh(tries - 1, nextRefresh, retryInSeconds * 2), retryInSeconds,
TimeUnit.SECONDS);
return;
}
final List<Prediction> predictions = predictionsOpt.get();
if (!predictions.isEmpty()) {
final ZonedDateTime actual = predictions.get(0).getActualDateTime();
updateState(BuienradarBindingConstants.ACTUAL_DATETIME, new DateTimeType(actual));
}
for (final Prediction prediction : predictions) {
final BigDecimal intensity = prediction.getIntensity();
final long minutesFromNow = prediction.getActualDateTime().until(prediction.getDateTimeOfPrediction(),
ChronoUnit.MINUTES);
logger.debug("Forecast for {} at {} made at {} is {}", minutesFromNow,
prediction.getDateTimeOfPrediction(), prediction.getActualDateTime(), intensity);
if (minutesFromNow >= 0 && minutesFromNow <= 115) {
final String label = String.format(Locale.ENGLISH, "forecast_%d", minutesFromNow);
updateState(label, new QuantityType<>(intensity, SmartHomeUnits.MILLIMETRE_PER_HOUR));
}
}
updateStatus(ThingStatus.ONLINE);
} catch (IOException e) {
logger.warn("Cannot retrieve predictions", e);
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.COMMUNICATION_ERROR,
String.format("Could not reach buienradar: %s", e.getMessage()));
}
}
@SuppressWarnings("null")
@Override
public void dispose() {
try {
listenableFutureLock.lock();
if (listenableFuture != null && !listenableFuture.isCancelled()) {
listenableFuture.cancel(true);
listenableFuture = null;
}
} finally {
listenableFutureLock.unlock();
}
}
}

View File

@@ -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.buienradar.internal;
import static org.openhab.binding.buienradar.internal.BuienradarBindingConstants.THING_TYPE_RAIN_FORECAST;
import java.util.Collections;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
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 BuienradarHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Edwin de Jong - Initial contribution
*/
@NonNullByDefault
@Component(configurationPid = "binding.buienradar", service = ThingHandlerFactory.class)
public class BuienradarHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_RAIN_FORECAST);
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
final ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (THING_TYPE_RAIN_FORECAST.equals(thingTypeUID)) {
return new BuienradarHandler(thing);
}
return null;
}
}

View File

@@ -0,0 +1,47 @@
/**
* 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.buienradar.internal.buienradarapi;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* An exception thrown when a result from Buienradar could not be correctly parsed.
*
* @author Edwin de Jong - Initial contribution
*/
@NonNullByDefault
public class BuienradarParseException extends Exception {
private static final long serialVersionUID = 1L;
public BuienradarParseException() {
super();
}
public BuienradarParseException(String message) {
super(message);
}
public BuienradarParseException(Throwable cause) {
super(cause);
}
public BuienradarParseException(String message, Throwable cause) {
super(message, cause);
}
public BuienradarParseException(String message, Throwable cause, boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View File

@@ -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.buienradar.internal.buienradarapi;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.io.net.http.HttpUtil;
import org.openhab.core.library.types.PointType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link BuienradarPredictionAPI} class implements the methods for retrieving results from the buienradar.nl
* service.
*
* @author Edwin de Jong - Initial contribution
*/
@NonNullByDefault
public class BuienradarPredictionAPI implements PredictionAPI {
private static final ZoneId ZONE_AMSTERDAM = ZoneId.of("Europe/Amsterdam");
private static final String BASE_ADDRESS = "https://gpsgadget.buienradar.nl/data/raintext";
private static final int TIMEOUT_MS = 3000;
private final Logger logger = LoggerFactory.getLogger(BuienradarPredictionAPI.class);
/**
* Parses a raw intensity string, such as <code>000</code>, into the right intensity in mm / hour.
*
* @param intensityStr The raw intensity string, being 3 characters long.
* @return The real intensity in mm / hour
* @throws BuienradarParseException when the intensity string could not be parsed.
*/
public static BigDecimal parseIntensity(String intensityStr) throws BuienradarParseException {
try {
// Intensity in mm / hour = 10^((value-109)/32)
double unrounded = Math.pow(10.0, (Integer.parseInt(intensityStr) - 109) / 32.0);
return BigDecimal.valueOf(unrounded).setScale(2, RoundingMode.HALF_EVEN);
} catch (NumberFormatException e) {
throw new BuienradarParseException("Could not parse intensity part of API", e);
}
}
/**
* Parses a time string, such as <code>10:30</code> into a ZonedDateTime, using the reference ZonedDateTime to find
* the closest
* match.
*
* @param timeStr The time string to parse.
* @param now The reference time to use.
* @return A ZonedDateTime of the indicated time.
* @throws BuienradarParseException When the time string cannot be correctly parsed.
*/
public static ZonedDateTime parseDateTime(String timeStr, ZonedDateTime now) throws BuienradarParseException {
final String[] timeElements = timeStr.split(":");
if (timeElements.length != 2) {
throw new BuienradarParseException("Expecting exactly two time elements");
}
final int hour = Integer.parseInt(timeElements[0]);
final int minute = Integer.parseInt(timeElements[1]);
final LocalTime time = LocalTime.of(hour, minute);
final LocalDate localDateInAmsterdam = now.withZoneSameInstant(ZONE_AMSTERDAM).toLocalDate();
final ZonedDateTime tryDateTime = time.atDate(localDateInAmsterdam).atZone(ZONE_AMSTERDAM);
if (tryDateTime.plusMinutes(20).isBefore(now)) {
// Check me: could this go wrong at DTS days?
return time.atDate(localDateInAmsterdam.plusDays(1)).atZone(ZONE_AMSTERDAM);
} else {
return tryDateTime;
}
}
/**
* Parses a line returned from the buienradar API service. An example line could be <code>100|23:00</code>.
*
* @param line The line to parse, such as <code>100|23:00</code>
* @param now The reference time to determine which instant to match to.
* @param actual The date time of the 'actual' prediction if known. If None is given, it is assumed this is the
* first row of the results.
* @return A Prediction interface, which contains the tuple with the intensity and the time.
* @throws BuienradarParseException Thrown when the line could not be correctly parsed.
*/
public static Prediction parseLine(String line, ZonedDateTime now, Optional<ZonedDateTime> actual)
throws BuienradarParseException {
final String[] lineElements = line.trim().split("\\|");
if (lineElements.length != 2) {
throw new BuienradarParseException(
String.format("Expected two line elements, but found %s", lineElements.length));
}
final BigDecimal intensityOut = parseIntensity(lineElements[0]);
final ZonedDateTime dateTime = parseDateTime(lineElements[1], now);
return new Prediction() {
@Override
public final BigDecimal getIntensity() {
return intensityOut;
}
@Override
public ZonedDateTime getDateTimeOfPrediction() {
return dateTime;
}
@Override
public ZonedDateTime getActualDateTime() {
return actual.orElseGet(this::getDateTimeOfPrediction);
}
};
}
@Override
public Optional<List<Prediction>> getPredictions(PointType location) throws IOException {
final String address = String.format(Locale.ENGLISH, BASE_ADDRESS + "?lat=%.2f&lon=%.2f",
location.getLatitude().doubleValue(), location.getLongitude().doubleValue());
final String result;
try {
result = HttpUtil.executeUrl("GET", address, TIMEOUT_MS);
} catch (IOException e) {
logger.warn("IO Exception when trying to retrieve Buienradar results", e);
return Optional.empty();
}
if (result.trim().isEmpty()) {
logger.warn("Buienradar API at URI {} return empty result", address);
return Optional.empty();
}
final List<Prediction> predictions = new ArrayList<>(24);
final List<String> errors = new LinkedList<>();
logger.debug("Returned result from buienradar: {}", result);
final String[] lines = result.split("\n");
Optional<ZonedDateTime> actual = Optional.empty();
for (String line : lines) {
try {
final Prediction prediction = parseLine(line, ZonedDateTime.now(), actual);
actual = Optional.of(prediction.getActualDateTime());
predictions.add(prediction);
} catch (BuienradarParseException e) {
errors.add(e.getMessage());
}
}
if (!errors.isEmpty()) {
logger.warn("Could not parse all results: {}", errors.stream().collect(Collectors.joining(", ")));
}
return Optional.of(predictions);
}
}

View File

@@ -0,0 +1,41 @@
/**
* 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.buienradar.internal.buienradarapi;
import java.math.BigDecimal;
import java.time.ZonedDateTime;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link Prediction} interface contains a prediction of rain at a specific time.
*
* @author Edwin de Jong - Initial contribution
*/
@NonNullByDefault
public interface Prediction {
/**
* Intensity of rain in mm/hour
*/
BigDecimal getIntensity();
/**
* Date-time of prediction.
*/
ZonedDateTime getDateTimeOfPrediction();
/**
* Date-time of when the prediction was made.
*/
ZonedDateTime getActualDateTime();
}

View File

@@ -0,0 +1,31 @@
/**
* 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.buienradar.internal.buienradarapi;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.types.PointType;
/**
* The {@link PredictionAPI} interface.
*
* @author Edwin de Jong - Initial contribution
*/
@NonNullByDefault
public interface PredictionAPI {
Optional<List<Prediction>> getPredictions(PointType location) throws IOException;
}

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="buienradar" 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>Buienradar Binding</name>
<description>The Buienradar Binding periodically (5 minutes) retrieves results from the Buienradar API. For personal
use only.</description>
<author>Edwin de Jong</author>
</binding:binding>

View File

@@ -0,0 +1,248 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="buienradar"
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="rain_forecast">
<label>Rain Forecast</label>
<description>Does a simple rain forecast at the specified longitude/latitude of
two hours, in 5 minute increments using
the buienradar.nl webservice.
</description>
<channels>
<channel id="actual_datetime" typeId="actual_datetime"/>
<channel id="forecast_0" typeId="forecast_0"/>
<channel id="forecast_5" typeId="forecast_5"/>
<channel id="forecast_10" typeId="forecast_10"/>
<channel id="forecast_15" typeId="forecast_15"/>
<channel id="forecast_20" typeId="forecast_20"/>
<channel id="forecast_25" typeId="forecast_25"/>
<channel id="forecast_30" typeId="forecast_30"/>
<channel id="forecast_35" typeId="forecast_35"/>
<channel id="forecast_40" typeId="forecast_40"/>
<channel id="forecast_45" typeId="forecast_45"/>
<channel id="forecast_50" typeId="forecast_50"/>
<channel id="forecast_55" typeId="forecast_55"/>
<channel id="forecast_60" typeId="forecast_60"/>
<channel id="forecast_65" typeId="forecast_65"/>
<channel id="forecast_70" typeId="forecast_70"/>
<channel id="forecast_75" typeId="forecast_75"/>
<channel id="forecast_80" typeId="forecast_80"/>
<channel id="forecast_85" typeId="forecast_85"/>
<channel id="forecast_90" typeId="forecast_90"/>
<channel id="forecast_95" typeId="forecast_95"/>
<channel id="forecast_100" typeId="forecast_100"/>
<channel id="forecast_105" typeId="forecast_105"/>
<channel id="forecast_110" typeId="forecast_110"/>
<channel id="forecast_115" typeId="forecast_115"/>
</channels>
<config-description>
<parameter name="location" type="text" required="true">
<context>location</context>
<label>Location of Weather</label>
<description>Location of weather in geographical coordinates (latitude/longitude/altitude).</description>
</parameter>
<parameter name="refreshIntervalMinutes" type="integer" required="true" min="5" unit="min">
<label>Refresh Interval</label>
<description>Refresh interval in minutes</description>
<default>5</default>
</parameter>
<parameter name="retries" type="integer" required="true" min="1">
<label>Retries</label>
<description>Number of retries to try to retrieve buienradar results</description>
<default>4</default>
</parameter>
<parameter name="exponentialBackoffRetryBaseInSeconds" type="integer" required="true" min="1" unit="s">
<label>Exponential Backoff Base for Retries</label>
<description>Exponential back-off base value for retries in seconds. For example, when this is 2 seconds, will retry
at 2, 4, 8, 16, 32, 64 seconds.</description>
<default>5</default>
</parameter>
</config-description>
</thing-type>
<channel-type id="actual_datetime">
<item-type>DateTime</item-type>
<label>Actual Date/Time</label>
<description>The actual date and time when the prediction was made."</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="forecast_0">
<item-type>Number:Speed</item-type>
<label>Current Rainfall</label>
<description>Current rainfall</description>
<state readOnly="true" pattern="%.10f %unit%"/>
</channel-type>
<channel-type id="forecast_5">
<item-type>Number:Speed</item-type>
<label>Rainfall in 5 min</label>
<description>Rainfall in 5 minutes</description>
<state readOnly="true" pattern="%.10f %unit%"/>
</channel-type>
<channel-type id="forecast_10">
<item-type>Number:Speed</item-type>
<label>Rainfall in 10 min</label>
<description>Rainfall in 10 minutes</description>
<state readOnly="true" pattern="%.10f %unit%"/>
</channel-type>
<channel-type id="forecast_15">
<item-type>Number:Speed</item-type>
<label>Rainfall in 15 min</label>
<description>Rainfall in 15 minutes</description>
<state readOnly="true" pattern="%.10f %unit%"/>
</channel-type>
<channel-type id="forecast_20">
<item-type>Number:Speed</item-type>
<label>Rainfall in 20 min</label>
<description>Rainfall in 20 minutes</description>
<state readOnly="true" pattern="%.10f %unit%"/>
</channel-type>
<channel-type id="forecast_25">
<item-type>Number:Speed</item-type>
<label>Rainfall in 25 min</label>
<description>Rainfall in 25 minutes</description>
<state readOnly="true" pattern="%.10f %unit%"/>
</channel-type>
<channel-type id="forecast_30">
<item-type>Number:Speed</item-type>
<label>Rainfall in 30 min</label>
<description>Rainfall in 30 minutes</description>
<state readOnly="true" pattern="%.10f %unit%"/>
</channel-type>
<channel-type id="forecast_35">
<item-type>Number:Speed</item-type>
<label>Rainfall in 35 min</label>
<description>Rainfall in 35 minutes</description>
<state readOnly="true" pattern="%.10f %unit%"/>
</channel-type>
<channel-type id="forecast_40">
<item-type>Number:Speed</item-type>
<label>Rainfall in 40 min</label>
<description>Rainfall in 40 minutes</description>
<state readOnly="true" pattern="%.10f %unit%"/>
</channel-type>
<channel-type id="forecast_45">
<item-type>Number:Speed</item-type>
<label>Rainfall in 45 min</label>
<description>Rainfall in 45 minutes</description>
<state readOnly="true" pattern="%.10f %unit%"/>
</channel-type>
<channel-type id="forecast_50">
<item-type>Number:Speed</item-type>
<label>Rainfall in 50 min</label>
<description>Rainfall in 50 minutes</description>
<state readOnly="true" pattern="%.10f %unit%"/>
</channel-type>
<channel-type id="forecast_55">
<item-type>Number:Speed</item-type>
<label>Rainfall in 55 min</label>
<description>Rainfall in 55 minutes</description>
<state readOnly="true" pattern="%.10f %unit%"/>
</channel-type>
<channel-type id="forecast_60">
<item-type>Number:Speed</item-type>
<label>Rainfall in 60 min</label>
<description>Rainfall in 60 minutes</description>
<state readOnly="true" pattern="%.10f %unit%"/>
</channel-type>
<channel-type id="forecast_65">
<item-type>Number:Speed</item-type>
<label>Rainfall in 65 min</label>
<description>Rainfall in 65 minutes</description>
<state readOnly="true" pattern="%.10f %unit%"/>
</channel-type>
<channel-type id="forecast_70">
<item-type>Number:Speed</item-type>
<label>Rainfall in 70 min</label>
<description>Rainfall in 70 minutes</description>
<state readOnly="true" pattern="%.10f %unit%"/>
</channel-type>
<channel-type id="forecast_75">
<item-type>Number:Speed</item-type>
<label>Rainfall in 75 min</label>
<description>Rainfall in 75 minutes</description>
<state readOnly="true" pattern="%.10f %unit%"/>
</channel-type>
<channel-type id="forecast_80">
<item-type>Number:Speed</item-type>
<label>Rainfall in 80 min</label>
<description>Rainfall in 80 minutes</description>
<state readOnly="true" pattern="%.10f %unit%"/>
</channel-type>
<channel-type id="forecast_85">
<item-type>Number:Speed</item-type>
<label>Rainfall in 85 min</label>
<description>Rainfall in 85 minutes</description>
<state readOnly="true" pattern="%.10f %unit%"/>
</channel-type>
<channel-type id="forecast_90">
<item-type>Number:Speed</item-type>
<label>Rainfall in 90 min</label>
<description>Rainfall in 90 minutes</description>
<state readOnly="true" pattern="%.10f %unit%"/>
</channel-type>
<channel-type id="forecast_95">
<item-type>Number:Speed</item-type>
<label>Rainfall in 95 min</label>
<description>Rainfall in 95 minutes</description>
<state readOnly="true" pattern="%.10f %unit%"/>
</channel-type>
<channel-type id="forecast_100">
<item-type>Number:Speed</item-type>
<label>Rainfall in 100 min</label>
<description>Rainfall in 100 minutes</description>
<state readOnly="true" pattern="%.10f %unit%"/>
</channel-type>
<channel-type id="forecast_105">
<item-type>Number:Speed</item-type>
<label>Rainfall in 105 min</label>
<description>Rainfall in 105 minutes</description>
<state readOnly="true" pattern="%.10f %unit%"/>
</channel-type>
<channel-type id="forecast_110">
<item-type>Number:Speed</item-type>
<label>Rainfall in 110 min</label>
<description>Rainfall in 110 minutes</description>
<state readOnly="true" pattern="%.10f %unit%"/>
</channel-type>
<channel-type id="forecast_115">
<item-type>Number:Speed</item-type>
<label>Rainfall in 115 min</label>
<description>Rainfall in 115 minutes</description>
<state readOnly="true" pattern="%.10f %unit%"/>
</channel-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,88 @@
/**
* 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.buienradar;
import static org.junit.Assert.assertEquals;
import java.math.BigDecimal;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Optional;
import org.junit.Test;
import org.openhab.binding.buienradar.internal.buienradarapi.BuienradarParseException;
import org.openhab.binding.buienradar.internal.buienradarapi.BuienradarPredictionAPI;
import org.openhab.binding.buienradar.internal.buienradarapi.Prediction;
/**
* Tests {@link BuienradarPredictionAPI}.
*
* @author Edwin de Jong - Initial contribution
*/
public class BuienradarPredictionAPITest {
private static final ZonedDateTime NOW = ZonedDateTime.of(2019, 3, 10, 20, 37, 0, 0, ZoneId.of("Europe/Amsterdam"));
private static final ZonedDateTime ACTUAL = ZonedDateTime.of(2019, 3, 10, 20, 35, 0, 0,
ZoneId.of("Europe/Amsterdam"));
private static final ZonedDateTime NOW_LONDON = ZonedDateTime.of(2019, 3, 10, 20, 37, 0, 0,
ZoneId.of("Europe/London"));
@Test
public void testParseIntensity000() throws BuienradarParseException {
assertEquals(BigDecimal.valueOf(0, 2), BuienradarPredictionAPI.parseIntensity("000"));
}
@Test
public void testParseIntensity050() throws BuienradarParseException {
assertEquals(BigDecimal.valueOf(1, 2), BuienradarPredictionAPI.parseIntensity("050"));
}
@Test
public void testParseIntensity500() throws BuienradarParseException {
assertEquals(BigDecimal.valueOf(165481709994318L, 2), BuienradarPredictionAPI.parseIntensity("500"));
}
@Test
public void testParseIntensity101() throws BuienradarParseException {
assertEquals(BigDecimal.valueOf(56, 2), BuienradarPredictionAPI.parseIntensity("101"));
}
@Test
public void testParseDateTime() throws BuienradarParseException {
final ZonedDateTime parsed = BuienradarPredictionAPI.parseDateTime("20:45", NOW);
assertEquals(ZonedDateTime.of(2019, 3, 10, 20, 45, 0, 0, ZoneId.of("Europe/Amsterdam")), parsed);
}
@Test
public void testParseDateTimeLondon() throws BuienradarParseException {
// 20:37 in London is *before* 20:45 in Amsterdam, therefore, it should be parsed as a timestamp happening
// tomorrow.
final ZonedDateTime parsed = BuienradarPredictionAPI.parseDateTime("20:45", NOW_LONDON);
assertEquals(ZonedDateTime.of(2019, 3, 11, 20, 45, 0, 0, ZoneId.of("Europe/Amsterdam")), parsed);
}
@Test
public void testParseDateTimeTomorrow() throws BuienradarParseException {
final ZonedDateTime parsed = BuienradarPredictionAPI.parseDateTime("19:40", NOW);
assertEquals(ZonedDateTime.of(2019, 3, 11, 19, 40, 0, 0, ZoneId.of("Europe/Amsterdam")), parsed);
}
@Test
public void testParseLine() throws BuienradarParseException {
final Prediction parsed = BuienradarPredictionAPI.parseLine("000|19:35", NOW, Optional.of(ACTUAL));
assertEquals(ZonedDateTime.of(2019, 3, 11, 19, 35, 0, 0, ZoneId.of("Europe/Amsterdam")),
parsed.getDateTimeOfPrediction());
assertEquals(BigDecimal.valueOf(0, 2), parsed.getIntensity());
}
}