]> git.basschouten.com Git - openhab-addons.git/commitdiff
[fmiweather] Add support for edited Scandinavia forecast (#17555)
authorJacob Laursen <jacob-github@vindvejr.dk>
Sat, 19 Oct 2024 20:37:22 +0000 (22:37 +0200)
committerGitHub <noreply@github.com>
Sat, 19 Oct 2024 20:37:22 +0000 (22:37 +0200)
* Add support for edited Scandinavia forecast

Resolves #17548

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>
bundles/org.openhab.binding.fmiweather/README.md
bundles/org.openhab.binding.fmiweather/src/main/java/org/openhab/binding/fmiweather/internal/AbstractWeatherHandler.java
bundles/org.openhab.binding.fmiweather/src/main/java/org/openhab/binding/fmiweather/internal/ForecastWeatherHandler.java
bundles/org.openhab.binding.fmiweather/src/main/java/org/openhab/binding/fmiweather/internal/client/Client.java
bundles/org.openhab.binding.fmiweather/src/main/java/org/openhab/binding/fmiweather/internal/client/ForecastRequest.java
bundles/org.openhab.binding.fmiweather/src/main/java/org/openhab/binding/fmiweather/internal/config/ForecastConfiguration.java [new file with mode: 0644]
bundles/org.openhab.binding.fmiweather/src/main/resources/OH-INF/i18n/fmiweather.properties
bundles/org.openhab.binding.fmiweather/src/main/resources/OH-INF/thing/thing-types.xml
bundles/org.openhab.binding.fmiweather/src/test/java/org/openhab/binding/fmiweather/FMIRequestTest.java

index be0724a878459691ca974e2b9469503a072d3366..94aed6ca50f45af059643269d28b928720321d41 100644 (file)
@@ -2,8 +2,12 @@
 
 This binding integrates to [the Finnish Meteorological Institute (FMI) Open Data API](https://en.ilmatieteenlaitos.fi/open-data).
 
-Binding provides access to weather observations from FMI weather stations and [HARMONIE weather forecast model](https://en.ilmatieteenlaitos.fi/weather-forecast-models) forecasts.
+The binding provides access to weather observations from FMI weather stations and FMI weather forecasts.
 Forecast covers "northern Europe" (Finland, Baltics, Scandinavia, some parts of surrounding countries), see [coverage map in the documentation](https://en.ilmatieteenlaitos.fi/weather-forecast-models).
+The binding supports two different forecast queries:
+
+- [HARMONIE weather forecast model](https://en.ilmatieteenlaitos.fi/weather-forecast-models), which is one of the weather models that meteorologists use in their work.
+- An edited query providing the official FMI forecast, which is often more accurate since it's edited by meteorologists who combine several different weather models and their experience to produce the official forecast.
 
 ![example of things](doc/images/fmi-example-things.png)
 
@@ -36,9 +40,10 @@ The binding automatically discovers weather stations and forecasts for nearby pl
 
 ### `forecast` Thing Configuration
 
-| Parameter  | Type | Required | Description                                                                                          | Example                           |
-| ---------- | ---- | -------- | ---------------------------------------------------------------------------------------------------- | --------------------------------- |
+| Parameter  | Type | Required | Description                                                                                          | Example                               |
+| ---------- | ---- | -------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------- |
 | `location` | text | ✓        | Latitude longitude location for the forecast. The parameter is given in format `LATITUDE,LONGITUDE`. | `"60.192059, 24.945831"` for Helsinki |
+| `query`    | text |          | Stored query for official FMI forecast, either `harmonie` or `edited`.                               |                                       |
 
 ## Channels
 
index 0aaf824e39972c88160367fc602be640095b4821..57426a9cdb7bc0bdcc6ce15d290b66fd57aebe7c 100644 (file)
@@ -138,9 +138,6 @@ public abstract class AbstractWeatherHandler extends BaseThingHandler {
         if (retry < RETRIES) {
             try {
                 response = client.query(getRequest(), TIMEOUT_MILLIS);
-            } catch (FMIUnexpectedResponseException e) {
-                handleError(e, retry);
-                return;
             } catch (FMIResponseException e) {
                 handleError(e, retry);
                 return;
index 03e78ef778b06e56744fb2cb8d48a5d856beb6a6..019d8ed3d9ac13e6520b6110c8527b2c4705a2ae 100644 (file)
@@ -37,6 +37,7 @@ import org.openhab.binding.fmiweather.internal.client.LatLon;
 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.config.ForecastConfiguration;
 import org.openhab.core.thing.Channel;
 import org.openhab.core.thing.ChannelUID;
 import org.openhab.core.thing.Thing;
@@ -82,6 +83,7 @@ public class ForecastWeatherHandler extends AbstractWeatherHandler {
     }
 
     private @NonNullByDefault({}) LatLon location;
+    private String query = "";
 
     public ForecastWeatherHandler(Thing thing) {
         super(thing);
@@ -91,27 +93,23 @@ public class ForecastWeatherHandler extends AbstractWeatherHandler {
 
     @Override
     public void initialize() {
-        try {
-            Object location = getConfig().get(BindingConstants.LOCATION);
-            if (location == null) {
-                logger.debug("Location not set for thing {} -- aborting initialization.", getThing().getUID());
-                updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
-                        String.format("location parameter not set"));
-                return;
-            }
-            String latlon = location.toString();
-            String[] split = latlon.split(",");
-            if (split.length != 2) {
-                throw new NumberFormatException(String.format(
-                        "Expecting location parameter to have latitude and longitude separated by comma (LATITUDE,LONGITUDE). Found %d values instead.",
-                        split.length));
-            }
-            this.location = new LatLon(new BigDecimal(split[0].trim()), new BigDecimal(split[1].trim()));
-            super.initialize();
-        } catch (NumberFormatException e) {
+        ForecastConfiguration config = getConfigAs(ForecastConfiguration.class);
+        String location = config.location;
+        if (location.isBlank()) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "location parameter not set");
+            return;
+        }
+        String[] split = location.split(",");
+        if (split.length != 2) {
             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, String.format(
-                    "location parameter should be in format LATITUDE,LONGITUDE. Error details: %s", e.getMessage()));
+                    "location parameter should have latitude and longitude separated by comma (LATITUDE,LONGITUDE). Found %d values instead",
+                    split.length));
+            return;
         }
+        this.location = new LatLon(new BigDecimal(split[0].trim()), new BigDecimal(split[1].trim()));
+        query = config.query;
+
+        super.initialize();
     }
 
     @Override
@@ -123,7 +121,7 @@ public class ForecastWeatherHandler extends AbstractWeatherHandler {
     @Override
     protected Request getRequest() {
         long now = Instant.now().getEpochSecond();
-        return new ForecastRequest(location, 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),
                 QUERY_RESOLUTION_MINUTES);
     }
index 4b9f4b7715cddefc79fd58fc4774248daf419cfb..6cd22851e05507ab23e71b899db4ef696023c265 100644 (file)
@@ -133,10 +133,12 @@ public class Client {
             throws FMIExceptionReportException, FMIUnexpectedResponseException, FMIIOException {
         try {
             String url = request.toUrl();
+            logger.trace("GET request for {}", url);
             String responseText = HttpUtil.executeUrl("GET", url, timeoutMillis);
             if (responseText == null) {
                 throw new FMIIOException(String.format("HTTP error with %s", request.toUrl()));
             }
+            logger.trace("Response content: '{}'", responseText);
             FMIResponse response = parseMultiPointCoverageXml(responseText);
             logger.debug("Request {} translated to url {}. Response: {}", request, url, response);
             return response;
index 359a757b050d0173ccc82a64af687fc4649d0af0..cd494034d4b049a99427740530a3050f565cfd7f 100644 (file)
@@ -13,6 +13,7 @@
 package org.openhab.binding.fmiweather.internal.client;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.fmiweather.internal.config.ForecastConfiguration;
 
 /**
  * Request for weather forecasts
@@ -23,7 +24,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 @NonNullByDefault
 public class ForecastRequest extends Request {
 
-    public static final String STORED_QUERY_ID = "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";
 
     // For description of variables: http://opendata.fmi.fi/meta?observableProperty=forecast
     public static final String PARAM_TEMPERATURE = "Temperature";
@@ -39,7 +41,12 @@ public class ForecastRequest extends Request {
             PARAM_WIND_SPEED, PARAM_WIND_GUST, PARAM_PRESSURE, PARAM_PRECIPITATION_1H, PARAM_TOTAL_CLOUD_COVER,
             PARAM_WEATHER_SYMBOL };
 
-    public ForecastRequest(QueryParameter location, long startEpoch, long endEpoch, long timestepMinutes) {
-        super(STORED_QUERY_ID, location, startEpoch, endEpoch, timestepMinutes, PARAMETERS);
+    public ForecastRequest(QueryParameter location, String query, long startEpoch, long endEpoch,
+            long timestepMinutes) {
+        super(switch (query) {
+            case ForecastConfiguration.QUERY_HARMONIE -> STORED_QUERY_ID_HARMONIE;
+            case ForecastConfiguration.QUERY_EDITED -> STORED_QUERY_ID_EDITED;
+            default -> throw new IllegalArgumentException("Invalid query parameter '%s'".formatted(query));
+        }, location, startEpoch, endEpoch, timestepMinutes, PARAMETERS);
     }
 }
diff --git a/bundles/org.openhab.binding.fmiweather/src/main/java/org/openhab/binding/fmiweather/internal/config/ForecastConfiguration.java b/bundles/org.openhab.binding.fmiweather/src/main/java/org/openhab/binding/fmiweather/internal/config/ForecastConfiguration.java
new file mode 100644 (file)
index 0000000..5ee66c8
--- /dev/null
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2010-2024 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.fmiweather.internal.config;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link ForecastConfiguration} class contains fields mapping Thing configuration parameters.
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class ForecastConfiguration {
+    public static final String QUERY_HARMONIE = "harmonie";
+    public static final String QUERY_EDITED = "edited";
+
+    public String location = "";
+    public String query = QUERY_HARMONIE;
+}
index f231e99d1a8655af4d17f665fb6a5b0842116783..b666012577538ef17c32c9e7c9eaf3fbc6121cac 100644 (file)
@@ -118,6 +118,10 @@ thing-type.fmiweather.observation.description = Finnish Meteorological Institute
 
 thing-type.config.fmiweather.forecast.location.label = Location
 thing-type.config.fmiweather.forecast.location.description = Location of weather in geographical coordinates (latitude,longitude).
+thing-type.config.fmiweather.forecast.query.label = Stored Query
+thing-type.config.fmiweather.forecast.query.description = Stored query for official FMI forecast
+thing-type.config.fmiweather.forecast.query.option.harmonie = Harmonie Surface Point Weather Forecast
+thing-type.config.fmiweather.forecast.query.option.edited = Forecast for Scandinavia (edited by a forecaster)
 thing-type.config.fmiweather.observation.fmisid.label = FMISID of the Weather Station
 thing-type.config.fmiweather.observation.fmisid.description = Station ID (FMISID) of the weather observation station <br /> <br />See https://en.ilmatieteenlaitos.fi/observation-stations for a list of observation stations. Select 'Weather' station for widest set of observations.
 
index 7ce3e14f82b11ba5ea36fa07f2090a622a8a300d..468afc12d545ec716397c4495ae16d607646c29d 100644 (file)
                                <label>Location</label>
                                <description>Location of weather in geographical coordinates (latitude,longitude).</description>
                        </parameter>
+                       <parameter name="query" type="text" required="false">
+                               <label>Stored Query</label>
+                               <description>Stored query for official FMI forecast</description>
+                               <default>harmonie</default>
+                               <options>
+                                       <option value="harmonie">Harmonie Surface Point Weather Forecast</option>
+                                       <option value="edited">Forecast for Scandinavia (edited by a forecaster)</option>
+                               </options>
+                       </parameter>
                </config-description>
 
        </thing-type>
index 4bbd9a13a0878a6922994750d7f59330342de720..c4d6f3fc94748881078fb8e6b78af44ca1d6791c 100644 (file)
@@ -49,9 +49,9 @@ public class FMIRequestTest {
     }
 
     @Test
-    public void testForecastRequestToUrl() {
-        ForecastRequest request = new ForecastRequest(new LatLon(new BigDecimal("9"), new BigDecimal("8")), 1552215664L,
-                1552215665L, 61);
+    public void testForecastRequestToUrlHarmonie() {
+        ForecastRequest request = new ForecastRequest(new LatLon(new BigDecimal("9"), new BigDecimal("8")), "harmonie",
+                1552215664L, 1552215665L, 61);
         assertThat(request.toUrl(),
                 is("""
                         https://opendata.fmi.fi/wfs?service=WFS&version=2.0.0&request=getFeature&storedquery_id=fmi::forecast::harmonie::surface::point::multipointcoverage\
@@ -60,6 +60,18 @@ public class FMIRequestTest {
                         """));
     }
 
+    @Test
+    public void testForecastRequestToUrlEdited() {
+        ForecastRequest request = new ForecastRequest(new LatLon(new BigDecimal("9"), new BigDecimal("8")), "edited",
+                1552215664L, 1552215665L, 61);
+        assertThat(request.toUrl(),
+                is("""
+                        https://opendata.fmi.fi/wfs?service=WFS&version=2.0.0&request=getFeature&storedquery_id=fmi::forecast::edited::weather::scandinavia::point::multipointcoverage\
+                        &starttime=2019-03-10T11:01:04Z&endtime=2019-03-10T11:01:05Z&timestep=61&latlon=9,8\
+                        &parameters=Temperature,Humidity,WindDirection,WindSpeedMS,WindGust,Pressure,Precipitation1h,TotalCloudCover,WeatherSymbol3\
+                        """));
+    }
+
     @Test
     public void testCustomLocation() {
         QueryParameter location = new QueryParameter() {