mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-02-04 19:34:05 +01:00
Fix discovery exception (#17669)
Fixes #17668 Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk> Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
parent
9f5349bae8
commit
b4e9bd5456
@ -29,10 +29,11 @@ import javax.measure.Unit;
|
|||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
import org.openhab.binding.fmiweather.internal.client.Client;
|
import org.openhab.binding.fmiweather.internal.client.Client;
|
||||||
import org.openhab.binding.fmiweather.internal.client.Data;
|
import org.openhab.binding.fmiweather.internal.client.Data;
|
||||||
|
import org.openhab.binding.fmiweather.internal.client.FMIRequest;
|
||||||
import org.openhab.binding.fmiweather.internal.client.FMIResponse;
|
import org.openhab.binding.fmiweather.internal.client.FMIResponse;
|
||||||
import org.openhab.binding.fmiweather.internal.client.Request;
|
|
||||||
import org.openhab.binding.fmiweather.internal.client.exception.FMIResponseException;
|
import org.openhab.binding.fmiweather.internal.client.exception.FMIResponseException;
|
||||||
import org.openhab.binding.fmiweather.internal.client.exception.FMIUnexpectedResponseException;
|
import org.openhab.binding.fmiweather.internal.client.exception.FMIUnexpectedResponseException;
|
||||||
import org.openhab.core.library.types.DateTimeType;
|
import org.openhab.core.library.types.DateTimeType;
|
||||||
@ -68,6 +69,7 @@ public abstract class AbstractWeatherHandler extends BaseThingHandler {
|
|||||||
|
|
||||||
protected static final int TIMEOUT_MILLIS = 10_000;
|
protected static final int TIMEOUT_MILLIS = 10_000;
|
||||||
private final Logger logger = LoggerFactory.getLogger(AbstractWeatherHandler.class);
|
private final Logger logger = LoggerFactory.getLogger(AbstractWeatherHandler.class);
|
||||||
|
private final HttpClient httpClient;
|
||||||
|
|
||||||
protected volatile @NonNullByDefault({}) Client client;
|
protected volatile @NonNullByDefault({}) Client client;
|
||||||
protected final AtomicReference<@Nullable ScheduledFuture<?>> futureRef = new AtomicReference<>();
|
protected final AtomicReference<@Nullable ScheduledFuture<?>> futureRef = new AtomicReference<>();
|
||||||
@ -77,8 +79,9 @@ public abstract class AbstractWeatherHandler extends BaseThingHandler {
|
|||||||
private volatile long lastRefreshMillis = 0;
|
private volatile long lastRefreshMillis = 0;
|
||||||
private final AtomicReference<@Nullable ScheduledFuture<?>> updateChannelsFutureRef = new AtomicReference<>();
|
private final AtomicReference<@Nullable ScheduledFuture<?>> updateChannelsFutureRef = new AtomicReference<>();
|
||||||
|
|
||||||
public AbstractWeatherHandler(Thing thing) {
|
public AbstractWeatherHandler(final Thing thing, final HttpClient httpClient) {
|
||||||
super(thing);
|
super(thing);
|
||||||
|
this.httpClient = httpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -111,7 +114,7 @@ public abstract class AbstractWeatherHandler extends BaseThingHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
client = new Client();
|
client = new Client(httpClient);
|
||||||
updateStatus(ThingStatus.UNKNOWN);
|
updateStatus(ThingStatus.UNKNOWN);
|
||||||
rescheduleUpdate(0, false);
|
rescheduleUpdate(0, false);
|
||||||
}
|
}
|
||||||
@ -136,7 +139,7 @@ public abstract class AbstractWeatherHandler extends BaseThingHandler {
|
|||||||
|
|
||||||
protected abstract void updateChannels();
|
protected abstract void updateChannels();
|
||||||
|
|
||||||
protected abstract Request getRequest();
|
protected abstract FMIRequest getRequest();
|
||||||
|
|
||||||
protected void update(int retry) {
|
protected void update(int retry) {
|
||||||
if (retry < RETRIES) {
|
if (retry < RETRIES) {
|
||||||
@ -145,6 +148,9 @@ public abstract class AbstractWeatherHandler extends BaseThingHandler {
|
|||||||
} catch (FMIResponseException e) {
|
} catch (FMIResponseException e) {
|
||||||
handleError(e, retry);
|
handleError(e, retry);
|
||||||
return;
|
return;
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.trace("Query failed. Retries exhausted, not trying again until next poll.");
|
logger.trace("Query failed. Retries exhausted, not trying again until next poll.");
|
||||||
|
@ -30,12 +30,13 @@ import javax.measure.Unit;
|
|||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
import org.openhab.binding.fmiweather.internal.client.Data;
|
import org.openhab.binding.fmiweather.internal.client.Data;
|
||||||
|
import org.openhab.binding.fmiweather.internal.client.FMIRequest;
|
||||||
import org.openhab.binding.fmiweather.internal.client.FMIResponse;
|
import org.openhab.binding.fmiweather.internal.client.FMIResponse;
|
||||||
import org.openhab.binding.fmiweather.internal.client.ForecastRequest;
|
import org.openhab.binding.fmiweather.internal.client.ForecastRequest;
|
||||||
import org.openhab.binding.fmiweather.internal.client.LatLon;
|
import org.openhab.binding.fmiweather.internal.client.LatLon;
|
||||||
import org.openhab.binding.fmiweather.internal.client.Location;
|
import org.openhab.binding.fmiweather.internal.client.Location;
|
||||||
import org.openhab.binding.fmiweather.internal.client.Request;
|
|
||||||
import org.openhab.binding.fmiweather.internal.client.exception.FMIUnexpectedResponseException;
|
import org.openhab.binding.fmiweather.internal.client.exception.FMIUnexpectedResponseException;
|
||||||
import org.openhab.binding.fmiweather.internal.config.ForecastConfiguration;
|
import org.openhab.binding.fmiweather.internal.config.ForecastConfiguration;
|
||||||
import org.openhab.core.thing.Channel;
|
import org.openhab.core.thing.Channel;
|
||||||
@ -85,8 +86,8 @@ public class ForecastWeatherHandler extends AbstractWeatherHandler {
|
|||||||
private @NonNullByDefault({}) LatLon location;
|
private @NonNullByDefault({}) LatLon location;
|
||||||
private String query = "";
|
private String query = "";
|
||||||
|
|
||||||
public ForecastWeatherHandler(Thing thing) {
|
public ForecastWeatherHandler(final Thing thing, final HttpClient httpClient) {
|
||||||
super(thing);
|
super(thing, httpClient);
|
||||||
// Override poll interval to slower value
|
// Override poll interval to slower value
|
||||||
pollIntervalSeconds = (int) TimeUnit.MINUTES.toSeconds(QUERY_RESOLUTION_MINUTES);
|
pollIntervalSeconds = (int) TimeUnit.MINUTES.toSeconds(QUERY_RESOLUTION_MINUTES);
|
||||||
}
|
}
|
||||||
@ -119,7 +120,7 @@ public class ForecastWeatherHandler extends AbstractWeatherHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Request getRequest() {
|
protected FMIRequest getRequest() {
|
||||||
long now = Instant.now().getEpochSecond();
|
long now = Instant.now().getEpochSecond();
|
||||||
return new ForecastRequest(location, query, floorToEvenMinutes(now, QUERY_RESOLUTION_MINUTES),
|
return new ForecastRequest(location, query, floorToEvenMinutes(now, QUERY_RESOLUTION_MINUTES),
|
||||||
ceilToEvenMinutes(now + TimeUnit.HOURS.toSeconds(FORECAST_HORIZON_HOURS), QUERY_RESOLUTION_MINUTES),
|
ceilToEvenMinutes(now + TimeUnit.HOURS.toSeconds(FORECAST_HORIZON_HOURS), QUERY_RESOLUTION_MINUTES),
|
||||||
|
@ -18,12 +18,17 @@ import java.util.Set;
|
|||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
|
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||||
import org.openhab.core.thing.Thing;
|
import org.openhab.core.thing.Thing;
|
||||||
import org.openhab.core.thing.ThingTypeUID;
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||||
import org.openhab.core.thing.binding.ThingHandler;
|
import org.openhab.core.thing.binding.ThingHandler;
|
||||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||||
|
import org.osgi.service.component.ComponentContext;
|
||||||
|
import org.osgi.service.component.annotations.Activate;
|
||||||
import org.osgi.service.component.annotations.Component;
|
import org.osgi.service.component.annotations.Component;
|
||||||
|
import org.osgi.service.component.annotations.Reference;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@link HandlerFactory} is responsible for creating things and thing
|
* The {@link HandlerFactory} is responsible for creating things and thing
|
||||||
@ -38,6 +43,14 @@ public class HandlerFactory extends BaseThingHandlerFactory {
|
|||||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_OBSERVATION,
|
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_OBSERVATION,
|
||||||
THING_TYPE_FORECAST);
|
THING_TYPE_FORECAST);
|
||||||
|
|
||||||
|
private final HttpClient httpClient;
|
||||||
|
|
||||||
|
@Activate
|
||||||
|
public HandlerFactory(final @Reference HttpClientFactory httpClientFactory, ComponentContext componentContext) {
|
||||||
|
super.activate(componentContext);
|
||||||
|
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||||
@ -48,9 +61,9 @@ public class HandlerFactory extends BaseThingHandlerFactory {
|
|||||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||||
|
|
||||||
if (THING_TYPE_OBSERVATION.equals(thingTypeUID)) {
|
if (THING_TYPE_OBSERVATION.equals(thingTypeUID)) {
|
||||||
return new ObservationWeatherHandler(thing);
|
return new ObservationWeatherHandler(thing, httpClient);
|
||||||
} else if (THING_TYPE_FORECAST.equals(thingTypeUID)) {
|
} else if (THING_TYPE_FORECAST.equals(thingTypeUID)) {
|
||||||
return new ForecastWeatherHandler(thing);
|
return new ForecastWeatherHandler(thing, httpClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -32,12 +32,13 @@ import javax.measure.quantity.Length;
|
|||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
import org.openhab.binding.fmiweather.internal.client.Data;
|
import org.openhab.binding.fmiweather.internal.client.Data;
|
||||||
|
import org.openhab.binding.fmiweather.internal.client.FMIRequest;
|
||||||
import org.openhab.binding.fmiweather.internal.client.FMIResponse;
|
import org.openhab.binding.fmiweather.internal.client.FMIResponse;
|
||||||
import org.openhab.binding.fmiweather.internal.client.FMISID;
|
import org.openhab.binding.fmiweather.internal.client.FMISID;
|
||||||
import org.openhab.binding.fmiweather.internal.client.Location;
|
import org.openhab.binding.fmiweather.internal.client.Location;
|
||||||
import org.openhab.binding.fmiweather.internal.client.ObservationRequest;
|
import org.openhab.binding.fmiweather.internal.client.ObservationRequest;
|
||||||
import org.openhab.binding.fmiweather.internal.client.Request;
|
|
||||||
import org.openhab.binding.fmiweather.internal.client.exception.FMIUnexpectedResponseException;
|
import org.openhab.binding.fmiweather.internal.client.exception.FMIUnexpectedResponseException;
|
||||||
import org.openhab.core.library.unit.MetricPrefix;
|
import org.openhab.core.library.unit.MetricPrefix;
|
||||||
import org.openhab.core.thing.Channel;
|
import org.openhab.core.thing.Channel;
|
||||||
@ -107,8 +108,8 @@ public class ObservationWeatherHandler extends AbstractWeatherHandler {
|
|||||||
|
|
||||||
private @NonNullByDefault({}) String fmisid;
|
private @NonNullByDefault({}) String fmisid;
|
||||||
|
|
||||||
public ObservationWeatherHandler(Thing thing) {
|
public ObservationWeatherHandler(final Thing thing, final HttpClient httpClient) {
|
||||||
super(thing);
|
super(thing, httpClient);
|
||||||
pollIntervalSeconds = POLL_INTERVAL_SECONDS;
|
pollIntervalSeconds = POLL_INTERVAL_SECONDS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +125,7 @@ public class ObservationWeatherHandler extends AbstractWeatherHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Request getRequest() {
|
protected FMIRequest getRequest() {
|
||||||
long now = Instant.now().getEpochSecond();
|
long now = Instant.now().getEpochSecond();
|
||||||
return new ObservationRequest(new FMISID(fmisid),
|
return new ObservationRequest(new FMISID(fmisid),
|
||||||
floorToEvenMinutes(now - OBSERVATION_LOOK_BACK_SECONDS, STEP_MINUTES),
|
floorToEvenMinutes(now - OBSERVATION_LOOK_BACK_SECONDS, STEP_MINUTES),
|
||||||
|
@ -19,6 +19,9 @@ import java.util.HashSet;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
import javax.xml.namespace.NamespaceContext;
|
import javax.xml.namespace.NamespaceContext;
|
||||||
@ -33,11 +36,15 @@ import javax.xml.xpath.XPathFactory;
|
|||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
|
import org.eclipse.jetty.client.api.Request;
|
||||||
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
import org.openhab.binding.fmiweather.internal.client.FMIResponse.Builder;
|
import org.openhab.binding.fmiweather.internal.client.FMIResponse.Builder;
|
||||||
import org.openhab.binding.fmiweather.internal.client.exception.FMIExceptionReportException;
|
import org.openhab.binding.fmiweather.internal.client.exception.FMIExceptionReportException;
|
||||||
import org.openhab.binding.fmiweather.internal.client.exception.FMIIOException;
|
import org.openhab.binding.fmiweather.internal.client.exception.FMIIOException;
|
||||||
import org.openhab.binding.fmiweather.internal.client.exception.FMIUnexpectedResponseException;
|
import org.openhab.binding.fmiweather.internal.client.exception.FMIUnexpectedResponseException;
|
||||||
import org.openhab.core.io.net.http.HttpUtil;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
@ -79,6 +86,7 @@ public class Client {
|
|||||||
"ef", "http://inspire.ec.europa.eu/schemas/ef/4.0");
|
"ef", "http://inspire.ec.europa.eu/schemas/ef/4.0");
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(Client.class);
|
private final Logger logger = LoggerFactory.getLogger(Client.class);
|
||||||
|
private final HttpClient httpClient;
|
||||||
|
|
||||||
private static final NamespaceContext NAMESPACE_CONTEXT = new NamespaceContext() {
|
private static final NamespaceContext NAMESPACE_CONTEXT = new NamespaceContext() {
|
||||||
@Override
|
@Override
|
||||||
@ -101,7 +109,9 @@ public class Client {
|
|||||||
private DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
|
private DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||||
private DocumentBuilder documentBuilder;
|
private DocumentBuilder documentBuilder;
|
||||||
|
|
||||||
public Client() {
|
public Client(final HttpClient httpClient) {
|
||||||
|
this.httpClient = httpClient;
|
||||||
|
|
||||||
documentBuilderFactory.setNamespaceAware(true);
|
documentBuilderFactory.setNamespaceAware(true);
|
||||||
try {
|
try {
|
||||||
// see https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
|
// see https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
|
||||||
@ -126,20 +136,30 @@ public class Client {
|
|||||||
* @throws FMIUnexpectedResponseException on all unexpected content errors
|
* @throws FMIUnexpectedResponseException on all unexpected content errors
|
||||||
* @throws FMIExceptionReportException on explicit error responses from the server
|
* @throws FMIExceptionReportException on explicit error responses from the server
|
||||||
*/
|
*/
|
||||||
public FMIResponse query(Request request, int timeoutMillis)
|
public FMIResponse query(FMIRequest fmiRequest, int timeoutMillis)
|
||||||
throws FMIExceptionReportException, FMIUnexpectedResponseException, FMIIOException {
|
throws FMIExceptionReportException, FMIUnexpectedResponseException, FMIIOException, InterruptedException {
|
||||||
try {
|
try {
|
||||||
String url = request.toUrl();
|
String url = fmiRequest.toUrl();
|
||||||
logger.trace("GET request for {}", url);
|
logger.trace("GET request for {}", url);
|
||||||
String responseText = HttpUtil.executeUrl("GET", url, timeoutMillis);
|
Request request = httpClient.newRequest(url).timeout(timeoutMillis, TimeUnit.MILLISECONDS)
|
||||||
if (responseText == null) {
|
.method(HttpMethod.GET);
|
||||||
throw new FMIIOException(String.format("HTTP error with %s", request.toUrl()));
|
|
||||||
|
ContentResponse response = request.send();
|
||||||
|
|
||||||
|
int status = response.getStatus();
|
||||||
|
if (!HttpStatus.isSuccess(status)) {
|
||||||
|
throw new FMIIOException("The request failed with HTTP error " + status);
|
||||||
}
|
}
|
||||||
logger.trace("Response content: '{}'", responseText);
|
|
||||||
FMIResponse response = parseMultiPointCoverageXml(responseText);
|
String responseContent = response.getContentAsString();
|
||||||
logger.debug("Request {} translated to url {}. Response: {}", request, url, response);
|
if (responseContent == null) {
|
||||||
return response;
|
throw new FMIIOException(String.format("HTTP error with %s", url));
|
||||||
} catch (IOException e) {
|
}
|
||||||
|
logger.trace("Response content: '{}'", responseContent);
|
||||||
|
FMIResponse fmiResponse = parseMultiPointCoverageXml(responseContent);
|
||||||
|
logger.debug("Request {} translated to url {}. Response: {}", fmiRequest, url, response);
|
||||||
|
return fmiResponse;
|
||||||
|
} catch (IOException | TimeoutException | ExecutionException e) {
|
||||||
throw new FMIIOException(e);
|
throw new FMIIOException(e);
|
||||||
} catch (SAXException | XPathExpressionException e) {
|
} catch (SAXException | XPathExpressionException e) {
|
||||||
throw new FMIUnexpectedResponseException(e);
|
throw new FMIUnexpectedResponseException(e);
|
||||||
@ -156,14 +176,24 @@ public class Client {
|
|||||||
* @throws FMIExceptionReportException on explicit error responses from the server
|
* @throws FMIExceptionReportException on explicit error responses from the server
|
||||||
*/
|
*/
|
||||||
public Set<Location> queryWeatherStations(int timeoutMillis)
|
public Set<Location> queryWeatherStations(int timeoutMillis)
|
||||||
throws FMIIOException, FMIUnexpectedResponseException, FMIExceptionReportException {
|
throws FMIIOException, FMIUnexpectedResponseException, FMIExceptionReportException, InterruptedException {
|
||||||
try {
|
try {
|
||||||
String response = HttpUtil.executeUrl("GET", WEATHER_STATIONS_URL, timeoutMillis);
|
Request request = httpClient.newRequest(WEATHER_STATIONS_URL).timeout(timeoutMillis, TimeUnit.MILLISECONDS)
|
||||||
if (response == null) {
|
.method(HttpMethod.GET);
|
||||||
|
|
||||||
|
ContentResponse response = request.send();
|
||||||
|
|
||||||
|
int status = response.getStatus();
|
||||||
|
if (!HttpStatus.isSuccess(status)) {
|
||||||
|
throw new FMIIOException("The request failed with HTTP error " + status);
|
||||||
|
}
|
||||||
|
|
||||||
|
String responseContent = response.getContentAsString();
|
||||||
|
if (responseContent == null) {
|
||||||
throw new FMIIOException(String.format("HTTP error with %s", WEATHER_STATIONS_URL));
|
throw new FMIIOException(String.format("HTTP error with %s", WEATHER_STATIONS_URL));
|
||||||
}
|
}
|
||||||
return parseStations(response);
|
return parseStations(responseContent);
|
||||||
} catch (IOException e) {
|
} catch (IOException | TimeoutException | ExecutionException e) {
|
||||||
throw new FMIIOException(e);
|
throw new FMIIOException(e);
|
||||||
} catch (XPathExpressionException | SAXException e) {
|
} catch (XPathExpressionException | SAXException e) {
|
||||||
throw new FMIUnexpectedResponseException(e);
|
throw new FMIUnexpectedResponseException(e);
|
||||||
|
@ -26,7 +26,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class Request {
|
public class FMIRequest {
|
||||||
|
|
||||||
public static final String FMI_WFS_URL = "https://opendata.fmi.fi/wfs";
|
public static final String FMI_WFS_URL = "https://opendata.fmi.fi/wfs";
|
||||||
|
|
||||||
@ -40,13 +40,13 @@ public class Request {
|
|||||||
private static final ZoneId UTC = ZoneId.of("Z");
|
private static final ZoneId UTC = ZoneId.of("Z");
|
||||||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
||||||
|
|
||||||
public Request(String storedQueryId, QueryParameter location, long startEpoch, long endEpoch,
|
public FMIRequest(String storedQueryId, QueryParameter location, long startEpoch, long endEpoch,
|
||||||
long timestepMinutes) {
|
long timestepMinutes) {
|
||||||
this(storedQueryId, location, startEpoch, endEpoch, timestepMinutes, new String[0]);
|
this(storedQueryId, location, startEpoch, endEpoch, timestepMinutes, new String[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Request(String storedQueryId, QueryParameter location, long startEpoch, long endEpoch, long timestepMinutes,
|
public FMIRequest(String storedQueryId, QueryParameter location, long startEpoch, long endEpoch,
|
||||||
String[] parameters) {
|
long timestepMinutes, String[] parameters) {
|
||||||
this.storedQueryId = storedQueryId;
|
this.storedQueryId = storedQueryId;
|
||||||
this.location = location;
|
this.location = location;
|
||||||
this.startEpoch = startEpoch;
|
this.startEpoch = startEpoch;
|
@ -22,7 +22,7 @@ import org.openhab.binding.fmiweather.internal.config.ForecastConfiguration;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class ForecastRequest extends Request {
|
public class ForecastRequest extends FMIRequest {
|
||||||
|
|
||||||
public static final String STORED_QUERY_ID_HARMONIE = "fmi::forecast::harmonie::surface::point::multipointcoverage";
|
public static final String STORED_QUERY_ID_HARMONIE = "fmi::forecast::harmonie::surface::point::multipointcoverage";
|
||||||
public static final String STORED_QUERY_ID_EDITED = "fmi::forecast::edited::weather::scandinavia::point::multipointcoverage";
|
public static final String STORED_QUERY_ID_EDITED = "fmi::forecast::edited::weather::scandinavia::point::multipointcoverage";
|
||||||
|
@ -21,7 +21,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class ObservationRequest extends Request {
|
public class ObservationRequest extends FMIRequest {
|
||||||
|
|
||||||
public static final String STORED_QUERY_ID = "fmi::observations::weather::multipointcoverage";
|
public static final String STORED_QUERY_ID = "fmi::observations::weather::multipointcoverage";
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ import java.util.stream.Stream;
|
|||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
import org.openhab.binding.fmiweather.internal.BindingConstants;
|
import org.openhab.binding.fmiweather.internal.BindingConstants;
|
||||||
import org.openhab.binding.fmiweather.internal.client.Client;
|
import org.openhab.binding.fmiweather.internal.client.Client;
|
||||||
import org.openhab.binding.fmiweather.internal.client.Location;
|
import org.openhab.binding.fmiweather.internal.client.Location;
|
||||||
@ -39,10 +40,12 @@ import org.openhab.core.config.discovery.DiscoveryResult;
|
|||||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||||
import org.openhab.core.config.discovery.DiscoveryService;
|
import org.openhab.core.config.discovery.DiscoveryService;
|
||||||
import org.openhab.core.i18n.LocationProvider;
|
import org.openhab.core.i18n.LocationProvider;
|
||||||
|
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||||
import org.openhab.core.library.types.DecimalType;
|
import org.openhab.core.library.types.DecimalType;
|
||||||
import org.openhab.core.library.types.PointType;
|
import org.openhab.core.library.types.PointType;
|
||||||
import org.openhab.core.thing.ThingTypeUID;
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
import org.openhab.core.thing.ThingUID;
|
import org.openhab.core.thing.ThingUID;
|
||||||
|
import org.osgi.service.component.annotations.Activate;
|
||||||
import org.osgi.service.component.annotations.Component;
|
import org.osgi.service.component.annotations.Component;
|
||||||
import org.osgi.service.component.annotations.Reference;
|
import org.osgi.service.component.annotations.Reference;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -65,35 +68,45 @@ public class FMIWeatherDiscoveryService extends AbstractDiscoveryService {
|
|||||||
private static final int LOCATION_CHANGED_CHECK_INTERVAL_SECONDS = 60;
|
private static final int LOCATION_CHANGED_CHECK_INTERVAL_SECONDS = 60;
|
||||||
private static final int FIND_STATION_METERS = 80_000;
|
private static final int FIND_STATION_METERS = 80_000;
|
||||||
|
|
||||||
|
private final LocationProvider locationProvider;
|
||||||
|
private final HttpClient httpClient;
|
||||||
|
private final ExpiringCache<Set<Location>> stationsCache;
|
||||||
|
|
||||||
private @Nullable ScheduledFuture<?> discoveryJob;
|
private @Nullable ScheduledFuture<?> discoveryJob;
|
||||||
private @Nullable PointType previousLocation;
|
private @Nullable PointType previousLocation;
|
||||||
private @NonNullByDefault({}) LocationProvider locationProvider;
|
|
||||||
|
|
||||||
private ExpiringCache<Set<Location>> stationsCache = new ExpiringCache<>(STATIONS_CACHE_MILLIS, () -> {
|
|
||||||
try {
|
|
||||||
return new Client().queryWeatherStations(STATIONS_TIMEOUT_MILLIS);
|
|
||||||
} catch (FMIUnexpectedResponseException e) {
|
|
||||||
logger.warn("Unexpected error with the response, potentially API format has changed. Printing out details",
|
|
||||||
e);
|
|
||||||
} catch (FMIResponseException e) {
|
|
||||||
logger.warn("Error when querying stations, {}: {}", e.getClass().getSimpleName(), e.getMessage());
|
|
||||||
}
|
|
||||||
// Return empty set on errors
|
|
||||||
return Collections.emptySet();
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@link FMIWeatherDiscoveryService} with immediately enabled background discovery.
|
* Creates a {@link FMIWeatherDiscoveryService} with immediately enabled background discovery.
|
||||||
*/
|
*/
|
||||||
public FMIWeatherDiscoveryService() {
|
@Activate
|
||||||
|
public FMIWeatherDiscoveryService(final @Reference LocationProvider locationProvider,
|
||||||
|
final @Reference HttpClientFactory httpClientFactory) {
|
||||||
super(SUPPORTED_THING_TYPES, DISCOVER_TIMEOUT_SECONDS, true);
|
super(SUPPORTED_THING_TYPES, DISCOVER_TIMEOUT_SECONDS, true);
|
||||||
|
this.locationProvider = locationProvider;
|
||||||
|
httpClient = httpClientFactory.getCommonHttpClient();
|
||||||
|
|
||||||
|
stationsCache = new ExpiringCache<>(STATIONS_CACHE_MILLIS, () -> {
|
||||||
|
try {
|
||||||
|
return new Client(httpClient).queryWeatherStations(STATIONS_TIMEOUT_MILLIS);
|
||||||
|
} catch (FMIUnexpectedResponseException e) {
|
||||||
|
logger.warn(
|
||||||
|
"Unexpected error with the response, potentially API format has changed. Printing out details",
|
||||||
|
e);
|
||||||
|
} catch (FMIResponseException e) {
|
||||||
|
logger.warn("Error when querying stations, {}: {}", e.getClass().getSimpleName(), e.getMessage());
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
logger.debug("Interrupted");
|
||||||
|
}
|
||||||
|
// Return empty set on errors
|
||||||
|
return Collections.emptySet();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void startScan() {
|
protected void startScan() {
|
||||||
PointType location = null;
|
PointType location = null;
|
||||||
logger.debug("Starting FMI Weather discovery scan");
|
logger.debug("Starting FMI Weather discovery scan");
|
||||||
LocationProvider locationProvider = getLocationProvider();
|
|
||||||
location = locationProvider.getLocation();
|
location = locationProvider.getLocation();
|
||||||
if (location == null) {
|
if (location == null) {
|
||||||
logger.debug("LocationProvider.getLocation() is not set -> Will discover all stations");
|
logger.debug("LocationProvider.getLocation() is not set -> Will discover all stations");
|
||||||
@ -208,17 +221,4 @@ public class FMIWeatherDiscoveryService extends AbstractDiscoveryService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Reference
|
|
||||||
protected void setLocationProvider(LocationProvider locationProvider) {
|
|
||||||
this.locationProvider = locationProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void unsetLocationProvider(LocationProvider provider) {
|
|
||||||
this.locationProvider = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected LocationProvider getLocationProvider() {
|
|
||||||
return locationProvider;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.openhab.binding.fmiweather.internal;
|
package org.openhab.binding.fmiweather.internal;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
@ -22,6 +24,7 @@ import javax.xml.xpath.XPathExpressionException;
|
|||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
import org.hamcrest.Description;
|
import org.hamcrest.Description;
|
||||||
import org.hamcrest.Matcher;
|
import org.hamcrest.Matcher;
|
||||||
import org.hamcrest.TypeSafeMatcher;
|
import org.hamcrest.TypeSafeMatcher;
|
||||||
@ -122,6 +125,10 @@ public class AbstractFMIResponseParsingTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected class ClientExposed extends Client {
|
protected class ClientExposed extends Client {
|
||||||
|
public ClientExposed() {
|
||||||
|
super(mock(HttpClient.class));
|
||||||
|
}
|
||||||
|
|
||||||
public FMIResponse parseMultiPointCoverageXml(String response) throws FMIUnexpectedResponseException,
|
public FMIResponse parseMultiPointCoverageXml(String response) throws FMIUnexpectedResponseException,
|
||||||
FMIExceptionReportException, SAXException, IOException, XPathExpressionException {
|
FMIExceptionReportException, SAXException, IOException, XPathExpressionException {
|
||||||
return super.parseMultiPointCoverageXml(response);
|
return super.parseMultiPointCoverageXml(response);
|
||||||
|
Loading…
Reference in New Issue
Block a user