]> git.basschouten.com Git - openhab-addons.git/commitdiff
[openweathermap] Added support for Air Pollution API (#10343)
authorChristoph Weitkamp <github@christophweitkamp.de>
Sat, 27 Mar 2021 19:04:02 +0000 (20:04 +0100)
committerGitHub <noreply@github.com>
Sat, 27 Mar 2021 19:04:02 +0000 (20:04 +0100)
Signed-off-by: Christoph Weitkamp <github@christophweitkamp.de>
19 files changed:
bundles/org.openhab.binding.openweathermap/README.md
bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/OpenWeatherMapBindingConstants.java
bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/config/OpenWeatherMapAirPollutionConfiguration.java [new file with mode: 0644]
bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/connection/OpenWeatherMapConnection.java
bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/discovery/OpenWeatherMapDiscoveryService.java
bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/dto/OpenWeatherMapJsonAirPollutionData.java [new file with mode: 0644]
bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/dto/airpollution/Components.java [new file with mode: 0644]
bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/dto/airpollution/List.java [new file with mode: 0644]
bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/dto/airpollution/Main.java [new file with mode: 0644]
bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/dto/forecast/daily/List.java
bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/factory/OpenWeatherMapHandlerFactory.java
bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/handler/AbstractOpenWeatherMapHandler.java
bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/handler/OpenWeatherMapAPIHandler.java
bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/handler/OpenWeatherMapAirPollutionHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/handler/OpenWeatherMapWeatherAndForecastHandler.java
bundles/org.openhab.binding.openweathermap/src/main/resources/OH-INF/config/config.xml
bundles/org.openhab.binding.openweathermap/src/main/resources/OH-INF/i18n/openweathermap_de.properties
bundles/org.openhab.binding.openweathermap/src/main/resources/OH-INF/thing/channel-types.xml
bundles/org.openhab.binding.openweathermap/src/main/resources/OH-INF/thing/thing-types.xml

index 69510989c5e8dc6b7385844f599a585b551f7426..d63422b117ae6d7d9ac5fa9b1ce374c21d7ee96a 100644 (file)
@@ -10,7 +10,7 @@ This binding integrates the [OpenWeatherMap weather API](https://openweathermap.
 
 ## Supported Things
 
-There are five supported things.
+There are six supported things.
 
 ### OpenWeatherMap Account
 
@@ -38,6 +38,13 @@ The third thing `uvindex` supports the [current UV Index](https://openweathermap
 It requires coordinates of the location of your interest.
 You can add as much `uvindex` things for different locations to your setup as you like to observe.
 
+### Current And Forecasted Air Pollution
+
+Another thing is the `air-pollution` which provides the [current air pollution](https://openweathermap.org/api/air-pollution) and [forecasted air pollution](https://openweathermap.org/api/air-pollution#forecast) for a specific location.
+It requires coordinates of the location of your interest.
+Air pollution forecast is available for 5 days with hourly granularity.
+You can add as much `air-pollution` things for different locations to your setup as you like to observe.
+
 ### One Call API Weather and Forecast 
 
 The thing `onecall` supports the [current and forecast weather data](https://openweathermap.org/api/one-call-api#how) for a specific location using the One Call API.
@@ -78,12 +85,21 @@ Once the parameters `forecastHours` or `forecastDays` will be changed, the avail
 
 ### Current UV Index And Forecast
 
+| Parameter      | Description                                                                                                                      |
+|----------------|----------------------------------------------------------------------------------------------------------------------------------|
+| location       | Location of weather in geographical coordinates (latitude/longitude/altitude). **Mandatory**                                     |
+| forecastDays   | Number of days for UV Index forecast (including todays forecast). Optional, the default value is 6 (min="1", max="8", step="1"). |
+
+Once the parameter `forecastDays` will be changed, the available channel groups on the thing will be created or removed accordingly.
+
+### Current Air Pollution And Forecast
+
 | Parameter      | Description                                                                                                                    |
 |----------------|--------------------------------------------------------------------------------------------------------------------------------|
 | location       | Location of weather in geographical coordinates (latitude/longitude/altitude). **Mandatory**                                   |
-| forecastDays   | Number of days for UV Index forecast (including todays forecast). Optional, the default value is 6 (min="1", max="8", step="1"). |
+| forecastHours  | Number of hours for air pollution forecast. Optional, the default value is 0 (min="0", max="120", step="1").                   |
 
-Once the parameter `forecastDays` will be changed, the available channel groups on the thing will be created or removed accordingly.
+Once the parameter `forecastHours` will be changed, the available channel groups on the thing will be created or removed accordingly.
 
 ### One Call API Weather and Forecast
 
@@ -188,6 +204,8 @@ See above for a description of the available channels.
 | Channel Group ID                                                 | Channel ID           | Item Type            | Description                                                                       |
 |------------------------------------------------------------------|----------------------|----------------------|-----------------------------------------------------------------------------------|
 | forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | time-stamp           | DateTime             | Date of data forecasted.                                                          |
+| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | sunrise              | DateTime             | Time of sunrise for the given day.                                                |
+| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | sunset               | DateTime             | Time of sunset for the given day.                                                 |
 | forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | condition            | String               | Forecast weather condition.                                                       |
 | forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | condition-id         | String               | Id of the forecasted weather condition. **Advanced**                              |
 | forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | icon                 | Image                | Icon representing the forecasted weather condition.                               |
@@ -205,6 +223,7 @@ See above for a description of the available channels.
 | forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | snow                 | Number:Length        | Expected snow volume of a day.                                                    |
 | forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7  | dew-point            | Number:Temperature   | Expected dew-point. Only available in the One Call API                            |
 | forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7  | uvindex              | Number               | Forecasted Midday UV Index. Only available in the One Call API                    |
+| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7  | precip-probability   | Number:Dimensionless | Precipitation probability.                                                        |
 | forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7  | morning-temperature  | Number:Temperature   | Expected morning temperature. Only available in the One Call API                  |
 | forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7  | day-temperature      | Number:Temperature   | Expected day-temperature. Only available in the One Call API                      |
 | forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7  | evening-temperature  | Number:Temperature   | Expected evening-temperature. Only available in the One Call API                  |
@@ -223,6 +242,21 @@ See above for a description of the available channels.
 
 The `uvindex` channel is also available in the current data and the daily forecast of the One Call API.
 
+### Air Pollution
+
+| Channel Group ID                                                | Channel ID             | Item Type      | Description                                                              |
+|-----------------------------------------------------------------|------------------------|----------------|--------------------------------------------------------------------------|
+| current, forecastHours01, forecastHours02, ... forecastHours120 | time-stamp             | DateTime       | Date of data observation / forecast.                                     |
+| current, forecastHours01, forecastHours02, ... forecastHours120 | airQualityIndex        | Number         | Current or forecasted air quality index.                                 |
+| current, forecastHours01, forecastHours02, ... forecastHours120 | particulateMatter2dot5 | Number:Density | Current or forecasted density of particles less than 2.5 µm in diameter. |
+| current, forecastHours01, forecastHours02, ... forecastHours120 | particulateMatter10    | Number:Density | Current or forecasted density of particles less than 10 µm in diameter.  |
+| current, forecastHours01, forecastHours02, ... forecastHours120 | carbonMonoxide         | Number:Density | Current or forecasted concentration of carbon monoxide.                  |
+| current, forecastHours01, forecastHours02, ... forecastHours120 | nitrogenMonoxide       | Number:Density | Current or forecasted concentration of nitrogen monoxide.                |
+| current, forecastHours01, forecastHours02, ... forecastHours120 | nitrogenDioxide        | Number:Density | Current or forecasted concentration of nitrogen dioxide.                 |
+| current, forecastHours01, forecastHours02, ... forecastHours120 | ozone                  | Number:Density | Current or forecasted concentration of ozone.                            |
+| current, forecastHours01, forecastHours02, ... forecastHours120 | sulphurDioxide         | Number:Density | Current or forecasted concentration of sulphur dioxide.                  |
+| current, forecastHours01, forecastHours02, ... forecastHours120 | ammonia                | Number:Density | Current or forecasted concentration of ammonia.                          |
+
 ## Full Example
 
 ### Things
index ea3c6afad960bc2a3f59a00ce75a8f408fe3b4ae..3a72a1908b64950dc102743d05815f1b533b6e4d 100644 (file)
@@ -36,6 +36,7 @@ public class OpenWeatherMapBindingConstants {
     public static final ThingTypeUID THING_TYPE_WEATHER_AND_FORECAST = new ThingTypeUID(BINDING_ID,
             "weather-and-forecast");
     public static final ThingTypeUID THING_TYPE_UVINDEX = new ThingTypeUID(BINDING_ID, "uvindex");
+    public static final ThingTypeUID THING_TYPE_AIR_POLLUTION = new ThingTypeUID(BINDING_ID, "air-pollution");
     // One Call API forecast
     public static final ThingTypeUID THING_TYPE_ONECALL_WEATHER_AND_FORECAST = new ThingTypeUID(BINDING_ID, "onecall");
     // One Call API historical data
@@ -54,6 +55,8 @@ public class OpenWeatherMapBindingConstants {
             "dailyForecast");
     public static final ChannelGroupTypeUID CHANNEL_GROUP_TYPE_UVINDEX_FORECAST = new ChannelGroupTypeUID(BINDING_ID,
             "uvindexForecast");
+    public static final ChannelGroupTypeUID CHANNEL_GROUP_TYPE_AIR_POLLUTION_FORECAST = new ChannelGroupTypeUID(
+            BINDING_ID, "airPollutionForecast");
     public static final ChannelGroupTypeUID CHANNEL_GROUP_TYPE_ONECALL_MINUTELY_FORECAST = new ChannelGroupTypeUID(
             BINDING_ID, "oneCallMinutely");
     public static final ChannelGroupTypeUID CHANNEL_GROUP_TYPE_ONECALL_HOURLY_FORECAST = new ChannelGroupTypeUID(
@@ -69,6 +72,7 @@ public class OpenWeatherMapBindingConstants {
     public static final String CHANNEL_GROUP_FORECAST_TODAY = "forecastToday";
     public static final String CHANNEL_GROUP_FORECAST_TOMORROW = "forecastTomorrow";
     public static final String CHANNEL_GROUP_CURRENT_UVINDEX = "current";
+    public static final String CHANNEL_GROUP_CURRENT_AIR_POLLUTION = "current";
     public static final String CHANNEL_GROUP_ONECALL_CURRENT = "current";
     public static final String CHANNEL_GROUP_ONECALL_HISTORY = "history";
     public static final String CHANNEL_GROUP_ONECALL_TODAY = "forecastToday";
@@ -109,6 +113,15 @@ public class OpenWeatherMapBindingConstants {
     public static final String CHANNEL_SNOW = "snow";
     public static final String CHANNEL_VISIBILITY = "visibility";
     public static final String CHANNEL_UVINDEX = "uvindex";
+    public static final String CHANNEL_AIR_QUALITY_INDEX = "airQualityIndex";
+    public static final String CHANNEL_PARTICULATE_MATTER_2_5 = "particulateMatter2dot5";
+    public static final String CHANNEL_PARTICULATE_MATTER_10 = "particulateMatter10";
+    public static final String CHANNEL_CARBON_MONOXIDE = "carbonMonoxide";
+    public static final String CHANNEL_NITROGEN_MONOXIDE = "nitrogenMonoxide";
+    public static final String CHANNEL_NITROGEN_DIOXIDE = "nitrogenDioxide";
+    public static final String CHANNEL_OZONE = "ozone";
+    public static final String CHANNEL_SULPHUR_DIOXIDE = "sulphurDioxide";
+    public static final String CHANNEL_AMMONIA = "ammonia";
     public static final String CHANNEL_PRECIPITATION = "precipitation";
 
     // List of all configuration
diff --git a/bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/config/OpenWeatherMapAirPollutionConfiguration.java b/bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/config/OpenWeatherMapAirPollutionConfiguration.java
new file mode 100644 (file)
index 0000000..7068b1c
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2010-2021 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.openweathermap.internal.config;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapAirPollutionHandler;
+
+/**
+ * The {@link OpenWeatherMapAirPollutionConfiguration} is the class used to match the
+ * {@link OpenWeatherMapAirPollutionHandler}s configuration.
+ *
+ * @author Christoph Weitkamp - Initial contribution
+ */
+@NonNullByDefault
+public class OpenWeatherMapAirPollutionConfiguration extends OpenWeatherMapLocationConfiguration {
+    public int forecastHours;
+}
index a0bb9bcc871c2d6635fb6ca0811607cbb7458e9b..db25ed9fac398e9988bffd73f74faa97e1818168 100644 (file)
@@ -35,6 +35,7 @@ import org.eclipse.jetty.client.HttpClient;
 import org.eclipse.jetty.client.HttpResponseException;
 import org.eclipse.jetty.client.api.ContentResponse;
 import org.openhab.binding.openweathermap.internal.config.OpenWeatherMapAPIConfiguration;
+import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapJsonAirPollutionData;
 import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapJsonDailyForecastData;
 import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapJsonHourlyForecastData;
 import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapJsonUVIndexData;
@@ -88,6 +89,9 @@ public class OpenWeatherMapConnection {
     // UV Index (see https://openweathermap.org/api/uvi)
     private static final String UVINDEX_URL = "https://api.openweathermap.org/data/2.5/uvi";
     private static final String UVINDEX_FORECAST_URL = "https://api.openweathermap.org/data/2.5/uvi/forecast";
+    // Air Pollution (see https://openweathermap.org/api/air-pollution)
+    private static final String AIR_POLLUTION_URL = "https://api.openweathermap.org/data/2.5/air_pollution";
+    private static final String AIR_POLLUTION_FORECAST_URL = "https://api.openweathermap.org/data/2.5/air_pollution/forecast";
     // Weather icons (see https://openweathermap.org/weather-conditions)
     private static final String ICON_URL = "https://openweathermap.org/img/w/%s.png";
     // One Call API (see https://openweathermap.org/api/one-call-api )
@@ -176,7 +180,7 @@ public class OpenWeatherMapConnection {
     }
 
     /**
-     * Requests the UV Index data for the given location (see https://api.openweathermap.org/data/2.5/uvi).
+     * Requests the UV Index data for the given location (see https://openweathermap.org/api/uvi).
      *
      * @param location location represented as {@link PointType}
      * @return the UV Index data
@@ -193,7 +197,7 @@ public class OpenWeatherMapConnection {
     }
 
     /**
-     * Requests the UV Index forecast data for the given location (see https://api.openweathermap.org/data/2.5/uvi).
+     * Requests the UV Index forecast data for the given location (see https://openweathermap.org/api/uvi).
      *
      * @param location location represented as {@link PointType}
      * @return the UV Index forecast data
@@ -216,6 +220,42 @@ public class OpenWeatherMapConnection {
                 OpenWeatherMapJsonUVIndexData[].class));
     }
 
+    /**
+     * Requests the Air Pollution data for the given location (see https://openweathermap.org/api/air-pollution).
+     *
+     * @param location location represented as {@link PointType}
+     * @return the Air Pollution data
+     * @throws JsonSyntaxException
+     * @throws OpenWeatherMapCommunicationException
+     * @throws OpenWeatherMapConfigurationException
+     */
+    public synchronized @Nullable OpenWeatherMapJsonAirPollutionData getAirPollutionData(@Nullable PointType location)
+            throws JsonSyntaxException, OpenWeatherMapCommunicationException, OpenWeatherMapConfigurationException {
+        return gson.fromJson(
+                getResponseFromCache(
+                        buildURL(AIR_POLLUTION_URL, getRequestParams(handler.getOpenWeatherMapAPIConfig(), location))),
+                OpenWeatherMapJsonAirPollutionData.class);
+    }
+
+    /**
+     * Requests the Air Pollution forecast data for the given location (see
+     * https://openweathermap.org/api/air-pollution).
+     *
+     * @param location location represented as {@link PointType}
+     * @return the Air Pollution forecast data
+     * @throws JsonSyntaxException
+     * @throws OpenWeatherMapCommunicationException
+     * @throws OpenWeatherMapConfigurationException
+     */
+    public synchronized @Nullable OpenWeatherMapJsonAirPollutionData getAirPollutionForecastData(
+            @Nullable PointType location)
+            throws JsonSyntaxException, OpenWeatherMapCommunicationException, OpenWeatherMapConfigurationException {
+        return gson.fromJson(
+                getResponseFromCache(buildURL(AIR_POLLUTION_FORECAST_URL,
+                        getRequestParams(handler.getOpenWeatherMapAPIConfig(), location))),
+                OpenWeatherMapJsonAirPollutionData.class);
+    }
+
     /**
      * Downloads the icon for the given icon id (see https://openweathermap.org/weather-conditions).
      *
@@ -317,7 +357,7 @@ public class OpenWeatherMapConnection {
         }
 
         Map<String, String> params = new HashMap<>();
-        // API key (see http://openweathermap.org/appid)
+        // API key (see https://openweathermap.org/appid)
         String apikey = config.apikey;
         if (apikey == null || (apikey = apikey.trim()).isEmpty()) {
             throw new OpenWeatherMapConfigurationException("@text/offline.conf-error-missing-apikey");
index 04cef9f1a648a91cfb926557844c730dd7310c7c..70293fe1e22120fb57b02ba1767f7124b31aaab4 100644 (file)
@@ -129,6 +129,7 @@ public class OpenWeatherMapDiscoveryService extends AbstractDiscoveryService {
         String locationString = location.toFullString();
         ThingUID bridgeUID = bridgeHandler.getThing().getUID();
         createWeatherAndForecastResult(locationString, bridgeUID);
+        createAirPollutionResult(locationString, bridgeUID);
         createOneCallResult(locationString, bridgeUID);
         createOneCallHistoryResult(locationString, bridgeUID);
     }
@@ -139,6 +140,12 @@ public class OpenWeatherMapDiscoveryService extends AbstractDiscoveryService {
                 .withRepresentationProperty(CONFIG_LOCATION).withBridge(bridgeUID).build());
     }
 
+    private void createAirPollutionResult(String location, ThingUID bridgeUID) {
+        thingDiscovered(DiscoveryResultBuilder.create(new ThingUID(THING_TYPE_AIR_POLLUTION, bridgeUID, LOCAL))
+                .withLabel("Local Air Pollution").withProperty(CONFIG_LOCATION, location)
+                .withRepresentationProperty(CONFIG_LOCATION).withBridge(bridgeUID).build());
+    }
+
     private void createOneCallResult(String location, ThingUID bridgeUID) {
         thingDiscovered(
                 DiscoveryResultBuilder.create(new ThingUID(THING_TYPE_ONECALL_WEATHER_AND_FORECAST, bridgeUID, LOCAL))
diff --git a/bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/dto/OpenWeatherMapJsonAirPollutionData.java b/bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/dto/OpenWeatherMapJsonAirPollutionData.java
new file mode 100644 (file)
index 0000000..f7230e4
--- /dev/null
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2010-2021 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.openweathermap.internal.dto;
+
+import org.openhab.binding.openweathermap.internal.dto.base.Coord;
+
+/**
+ * The {@link OpenWeatherMapJsonAirPollutionData} is the Java class used to map the JSON response to an OpenWeatherMap
+ * request.
+ *
+ * @author Christoph Weitkamp - Initial contribution
+ */
+public class OpenWeatherMapJsonAirPollutionData {
+    public Coord coord;
+    public java.util.List<org.openhab.binding.openweathermap.internal.dto.airpollution.List> list;
+}
diff --git a/bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/dto/airpollution/Components.java b/bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/dto/airpollution/Components.java
new file mode 100644 (file)
index 0000000..3c20083
--- /dev/null
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2010-2021 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.openweathermap.internal.dto.airpollution;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Generated Plain Old Java Objects class for {@link Components} from JSON.
+ *
+ * @author Christoph Weitkamp - Initial contribution
+ */
+public class Components {
+    @SerializedName("co")
+    public double carbonMonoxide;
+    @SerializedName("no")
+    public double nitrogenMonoxide;
+    @SerializedName("no2")
+    public double nitrogenDioxide;
+    @SerializedName("o2")
+    public double ozone;
+    @SerializedName("so2")
+    public double sulphurDioxide;
+    @SerializedName("pm2_5")
+    public double particulateMatter2dot5;
+    @SerializedName("pm10")
+    public double particulateMatter10;
+    @SerializedName("nh3")
+    public double ammonia;
+}
diff --git a/bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/dto/airpollution/List.java b/bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/dto/airpollution/List.java
new file mode 100644 (file)
index 0000000..f0e2393
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2010-2021 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.openweathermap.internal.dto.airpollution;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Generated Plain Old Java Objects class for {@link List} from JSON.
+ *
+ * @author Christoph Weitkamp - Initial contribution
+ */
+public class List {
+    public int dt;
+    @SerializedName("main")
+    public Main airQualityIndex;
+    @SerializedName("components")
+    public Components measurements;
+}
diff --git a/bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/dto/airpollution/Main.java b/bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/dto/airpollution/Main.java
new file mode 100644 (file)
index 0000000..d25c965
--- /dev/null
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2010-2021 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.openweathermap.internal.dto.airpollution;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Generated Plain Old Java Objects class for {@link Main} from JSON.
+ *
+ * @author Christoph Weitkamp - Initial contribution
+ */
+public class Main {
+    @SerializedName("aqi")
+    public int index;
+}
index a6dbe74eed30594f68777251bbf7e2c14d653b92..63f18b5b37570fd58a3363f5795f2c2adf4ec77c 100644 (file)
@@ -24,6 +24,8 @@ import com.google.gson.annotations.SerializedName;
  */
 public class List {
     private Integer dt;
+    private @Nullable Integer sunrise;
+    private @Nullable Integer sunset;
     private Temp temp;
     @SerializedName("feels_like")
     private @Nullable FeelsLikeTemp feelsLikeTemp;
@@ -36,6 +38,7 @@ public class List {
     private Integer clouds;
     private @Nullable Double rain;
     private @Nullable Double snow;
+    private @Nullable Double pop;
 
     public Integer getDt() {
         return dt;
@@ -45,6 +48,22 @@ public class List {
         this.dt = dt;
     }
 
+    public @Nullable Integer getSunrise() {
+        return sunrise;
+    }
+
+    public void setSunrise(Integer sunrise) {
+        this.sunrise = sunrise;
+    }
+
+    public @Nullable Integer getSunset() {
+        return sunset;
+    }
+
+    public void setSunset(Integer sunset) {
+        this.sunset = sunset;
+    }
+
     public Temp getTemp() {
         return temp;
     }
@@ -132,4 +151,12 @@ public class List {
     public void setSnow(Double snow) {
         this.snow = snow;
     }
+
+    public @Nullable Double getPop() {
+        return pop;
+    }
+
+    public void setPop(Double pop) {
+        this.pop = pop;
+    }
 }
index df137ce069c7a5df03f449f79ed4709b31e610d4..a2aa8fac6e20bddfb543285101d21dc3ac0e1925 100644 (file)
@@ -16,7 +16,6 @@ import static org.openhab.binding.openweathermap.internal.OpenWeatherMapBindingC
 
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.Hashtable;
 import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -26,7 +25,13 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.jetty.client.HttpClient;
 import org.openhab.binding.openweathermap.internal.discovery.OpenWeatherMapDiscoveryService;
-import org.openhab.binding.openweathermap.internal.handler.*;
+import org.openhab.binding.openweathermap.internal.handler.AbstractOpenWeatherMapHandler;
+import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapAPIHandler;
+import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapAirPollutionHandler;
+import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapOneCallHandler;
+import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapOneCallHistoryHandler;
+import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapUVIndexHandler;
+import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapWeatherAndForecastHandler;
 import org.openhab.core.config.discovery.DiscoveryService;
 import org.openhab.core.i18n.LocaleProvider;
 import org.openhab.core.i18n.LocationProvider;
@@ -90,13 +95,15 @@ public class OpenWeatherMapHandlerFactory extends BaseThingHandlerFactory {
             // register discovery service
             OpenWeatherMapDiscoveryService discoveryService = new OpenWeatherMapDiscoveryService(handler,
                     locationProvider, localeProvider, i18nProvider);
-            discoveryServiceRegs.put(handler.getThing().getUID(), bundleContext
-                    .registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>()));
+            discoveryServiceRegs.put(handler.getThing().getUID(),
+                    bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, null));
             return handler;
         } else if (THING_TYPE_WEATHER_AND_FORECAST.equals(thingTypeUID)) {
             return new OpenWeatherMapWeatherAndForecastHandler(thing, timeZoneProvider);
         } else if (THING_TYPE_UVINDEX.equals(thingTypeUID)) {
             return new OpenWeatherMapUVIndexHandler(thing, timeZoneProvider);
+        } else if (THING_TYPE_AIR_POLLUTION.equals(thingTypeUID)) {
+            return new OpenWeatherMapAirPollutionHandler(thing, timeZoneProvider);
         } else if (THING_TYPE_ONECALL_WEATHER_AND_FORECAST.equals(thingTypeUID)) {
             return new OpenWeatherMapOneCallHandler(thing, timeZoneProvider);
         } else if (THING_TYPE_ONECALL_HISTORY.equals(thingTypeUID)) {
index 26b67388ea079e4ab7fab6e2129ec2a54301d42b..6f3e00b938715d5dea1e0843379fa1b4ae9475d8 100644 (file)
@@ -17,11 +17,8 @@ import static org.openhab.binding.openweathermap.internal.OpenWeatherMapBindingC
 import java.time.Instant;
 import java.time.ZonedDateTime;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 import javax.measure.Unit;
 
@@ -69,9 +66,9 @@ public abstract class AbstractOpenWeatherMapHandler extends BaseThingHandler {
 
     private final Logger logger = LoggerFactory.getLogger(AbstractOpenWeatherMapHandler.class);
 
-    public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections
-            .unmodifiableSet(Stream.of(THING_TYPE_WEATHER_AND_FORECAST, THING_TYPE_UVINDEX,
-                    THING_TYPE_ONECALL_WEATHER_AND_FORECAST, THING_TYPE_ONECALL_HISTORY).collect(Collectors.toSet()));
+    public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_WEATHER_AND_FORECAST,
+            THING_TYPE_UVINDEX, THING_TYPE_AIR_POLLUTION, THING_TYPE_ONECALL_WEATHER_AND_FORECAST,
+            THING_TYPE_ONECALL_HISTORY);
 
     private final TimeZoneProvider timeZoneProvider;
 
index 357b9055af364904a014ee8e650ac766b5024ce1..654d940cfd0d6566a4eed2bd311eaab4c5854717 100644 (file)
@@ -14,7 +14,6 @@ package org.openhab.binding.openweathermap.internal.handler;
 
 import static org.openhab.binding.openweathermap.internal.OpenWeatherMapBindingConstants.*;
 
-import java.util.Collections;
 import java.util.Set;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
@@ -34,6 +33,7 @@ import org.openhab.core.thing.ThingStatusDetail;
 import org.openhab.core.thing.ThingTypeUID;
 import org.openhab.core.thing.binding.BaseBridgeHandler;
 import org.openhab.core.thing.binding.ThingHandler;
+import org.openhab.core.thing.util.ThingHandlerHelper;
 import org.openhab.core.types.Command;
 import org.openhab.core.types.RefreshType;
 import org.slf4j.Logger;
@@ -49,7 +49,7 @@ public class OpenWeatherMapAPIHandler extends BaseBridgeHandler {
 
     private final Logger logger = LoggerFactory.getLogger(OpenWeatherMapAPIHandler.class);
 
-    public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_WEATHER_API);
+    public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_WEATHER_API);
 
     private static final long INITIAL_DELAY_IN_SECONDS = 15;
 
@@ -74,7 +74,7 @@ public class OpenWeatherMapAPIHandler extends BaseBridgeHandler {
         config = getConfigAs(OpenWeatherMapAPIConfiguration.class);
 
         boolean configValid = true;
-        if (config.apikey == null || config.apikey.trim().isEmpty()) {
+        if (config.apikey.isBlank()) {
             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
                     "@text/offline.conf-error-missing-apikey");
             configValid = false;
@@ -172,7 +172,7 @@ public class OpenWeatherMapAPIHandler extends BaseBridgeHandler {
     }
 
     private ThingStatus updateThing(@Nullable AbstractOpenWeatherMapHandler handler, Thing thing) {
-        if (handler != null && connection != null) {
+        if (handler != null && ThingHandlerHelper.isHandlerInitialized(handler) && connection != null) {
             handler.updateData(connection);
             return thing.getStatus();
         } else {
diff --git a/bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/handler/OpenWeatherMapAirPollutionHandler.java b/bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/handler/OpenWeatherMapAirPollutionHandler.java
new file mode 100644 (file)
index 0000000..b937c3a
--- /dev/null
@@ -0,0 +1,262 @@
+/**
+ * Copyright (c) 2010-2021 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.openweathermap.internal.handler;
+
+import static org.openhab.binding.openweathermap.internal.OpenWeatherMapBindingConstants.*;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.openweathermap.internal.config.OpenWeatherMapAirPollutionConfiguration;
+import org.openhab.binding.openweathermap.internal.connection.OpenWeatherMapCommunicationException;
+import org.openhab.binding.openweathermap.internal.connection.OpenWeatherMapConfigurationException;
+import org.openhab.binding.openweathermap.internal.connection.OpenWeatherMapConnection;
+import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapJsonAirPollutionData;
+import org.openhab.core.i18n.TimeZoneProvider;
+import org.openhab.core.library.types.DecimalType;
+import org.openhab.core.library.unit.Units;
+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.ThingStatusDetail;
+import org.openhab.core.thing.binding.builder.ThingBuilder;
+import org.openhab.core.types.State;
+import org.openhab.core.types.UnDefType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.JsonSyntaxException;
+
+/**
+ * The {@link OpenWeatherMapAirPollutionHandler} is responsible for handling commands, which are sent to one of the
+ * channels.
+ *
+ * @author Christoph Weitkamp - Initial contribution
+ */
+@NonNullByDefault
+public class OpenWeatherMapAirPollutionHandler extends AbstractOpenWeatherMapHandler {
+
+    private final Logger logger = LoggerFactory.getLogger(OpenWeatherMapAirPollutionHandler.class);
+
+    private static final String CHANNEL_GROUP_HOURLY_FORECAST_PREFIX = "forecastHours";
+    private static final Pattern CHANNEL_GROUP_HOURLY_FORECAST_PREFIX_PATTERN = Pattern
+            .compile(CHANNEL_GROUP_HOURLY_FORECAST_PREFIX + "([0-9]*)");
+
+    // keeps track of the parsed count
+    private int forecastHours = 0;
+
+    private @Nullable OpenWeatherMapJsonAirPollutionData airPollutionData;
+    private @Nullable OpenWeatherMapJsonAirPollutionData airPollutionForecastData;
+
+    public OpenWeatherMapAirPollutionHandler(Thing thing, final TimeZoneProvider timeZoneProvider) {
+        super(thing, timeZoneProvider);
+    }
+
+    @Override
+    public void initialize() {
+        super.initialize();
+        logger.debug("Initialize OpenWeatherMapAirPollutionHandler handler '{}'.", getThing().getUID());
+        OpenWeatherMapAirPollutionConfiguration config = getConfigAs(OpenWeatherMapAirPollutionConfiguration.class);
+
+        boolean configValid = true;
+        int newForecastHours = config.forecastHours;
+        if (newForecastHours < 0 || newForecastHours > 120) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+                    "@text/offline.conf-error-not-supported-air-pollution-number-of-hours");
+            configValid = false;
+        }
+
+        if (configValid) {
+            logger.debug("Rebuilding thing '{}'.", getThing().getUID());
+            List<Channel> toBeAddedChannels = new ArrayList<>();
+            List<Channel> toBeRemovedChannels = new ArrayList<>();
+            if (forecastHours != newForecastHours) {
+                logger.debug("Rebuilding air pollution channel groups.");
+                if (forecastHours > newForecastHours) {
+                    for (int i = newForecastHours + 1; i <= forecastHours; i++) {
+                        toBeRemovedChannels.addAll(removeChannelsOfGroup(
+                                CHANNEL_GROUP_HOURLY_FORECAST_PREFIX + ((i < 10) ? "0" : "") + Integer.toString(i)));
+                    }
+                } else {
+                    for (int i = forecastHours + 1; i <= newForecastHours; i++) {
+                        toBeAddedChannels.addAll(createChannelsForGroup(
+                                CHANNEL_GROUP_HOURLY_FORECAST_PREFIX + ((i < 10) ? "0" : "") + Integer.toString(i),
+                                CHANNEL_GROUP_TYPE_AIR_POLLUTION_FORECAST));
+                    }
+                }
+                forecastHours = newForecastHours;
+            }
+            ThingBuilder builder = editThing().withoutChannels(toBeRemovedChannels);
+            for (Channel channel : toBeAddedChannels) {
+                builder.withChannel(channel);
+            }
+            updateThing(builder.build());
+        }
+    }
+
+    @Override
+    protected boolean requestData(OpenWeatherMapConnection connection)
+            throws OpenWeatherMapCommunicationException, OpenWeatherMapConfigurationException {
+        logger.debug("Update air pollution data of thing '{}'.", getThing().getUID());
+        try {
+            airPollutionData = connection.getAirPollutionData(location);
+            if (forecastHours > 0) {
+                airPollutionForecastData = connection.getAirPollutionForecastData(location);
+            }
+            return true;
+        } catch (JsonSyntaxException e) {
+            logger.debug("JsonSyntaxException occurred during execution: {}", e.getLocalizedMessage(), e);
+            return false;
+        }
+    }
+
+    @Override
+    protected void updateChannel(ChannelUID channelUID) {
+        switch (channelUID.getGroupId()) {
+            case CHANNEL_GROUP_CURRENT_AIR_POLLUTION:
+                updateCurrentAirPollutionChannel(channelUID);
+                break;
+            default:
+                Matcher m = CHANNEL_GROUP_HOURLY_FORECAST_PREFIX_PATTERN.matcher(channelUID.getGroupId());
+                int i;
+                if (m.find() && (i = Integer.parseInt(m.group(1))) > 0 && i <= 120) {
+                    updateHourlyForecastChannel(channelUID, i);
+                }
+                break;
+        }
+    }
+
+    /**
+     * Update the channel from the last OpenWeatherMap data retrieved.
+     *
+     * @param channelUID the id identifying the channel to be updated
+     */
+    private void updateCurrentAirPollutionChannel(ChannelUID channelUID) {
+        String channelId = channelUID.getIdWithoutGroup();
+        String channelGroupId = channelUID.getGroupId();
+        OpenWeatherMapJsonAirPollutionData localAirPollutionData = airPollutionData;
+        if (localAirPollutionData != null && !localAirPollutionData.list.isEmpty()) {
+            org.openhab.binding.openweathermap.internal.dto.airpollution.List currentData = localAirPollutionData.list
+                    .get(0);
+            State state = UnDefType.UNDEF;
+            switch (channelId) {
+                case CHANNEL_TIME_STAMP:
+                    state = getDateTimeTypeState(currentData.dt);
+                    break;
+                case CHANNEL_AIR_QUALITY_INDEX:
+                    state = new DecimalType(currentData.airQualityIndex.index);
+                    break;
+                case CHANNEL_PARTICULATE_MATTER_2_5:
+                    state = getQuantityTypeState(currentData.measurements.particulateMatter2dot5,
+                            Units.MICROGRAM_PER_CUBICMETRE);
+                    break;
+                case CHANNEL_PARTICULATE_MATTER_10:
+                    state = getQuantityTypeState(currentData.measurements.particulateMatter10,
+                            Units.MICROGRAM_PER_CUBICMETRE);
+                    break;
+                case CHANNEL_CARBON_MONOXIDE:
+                    state = getQuantityTypeState(currentData.measurements.carbonMonoxide,
+                            Units.MICROGRAM_PER_CUBICMETRE);
+                    break;
+                case CHANNEL_NITROGEN_MONOXIDE:
+                    state = getQuantityTypeState(currentData.measurements.nitrogenMonoxide,
+                            Units.MICROGRAM_PER_CUBICMETRE);
+                    break;
+                case CHANNEL_NITROGEN_DIOXIDE:
+                    state = getQuantityTypeState(currentData.measurements.nitrogenDioxide,
+                            Units.MICROGRAM_PER_CUBICMETRE);
+                    break;
+                case CHANNEL_OZONE:
+                    state = getQuantityTypeState(currentData.measurements.ozone, Units.MICROGRAM_PER_CUBICMETRE);
+                    break;
+                case CHANNEL_SULPHUR_DIOXIDE:
+                    state = getQuantityTypeState(currentData.measurements.sulphurDioxide,
+                            Units.MICROGRAM_PER_CUBICMETRE);
+                    break;
+                case CHANNEL_AMMONIA:
+                    state = getQuantityTypeState(currentData.measurements.ammonia, Units.MICROGRAM_PER_CUBICMETRE);
+                    break;
+            }
+            logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
+            updateState(channelUID, state);
+        } else {
+            logger.debug("No air pollution data available to update channel '{}' of group '{}'.", channelId,
+                    channelGroupId);
+        }
+    }
+
+    /**
+     * Update the channel from the last OpenWeatherMap data retrieved.
+     *
+     * @param channelUID the id identifying the channel to be updated
+     * @param count
+     */
+    private void updateHourlyForecastChannel(ChannelUID channelUID, int count) {
+        String channelId = channelUID.getIdWithoutGroup();
+        String channelGroupId = channelUID.getGroupId();
+        OpenWeatherMapJsonAirPollutionData localAirPollutionForecastData = airPollutionForecastData;
+        if (localAirPollutionForecastData != null && localAirPollutionForecastData.list.size() >= count) {
+            org.openhab.binding.openweathermap.internal.dto.airpollution.List forecastData = localAirPollutionForecastData.list
+                    .get(count - 1);
+            State state = UnDefType.UNDEF;
+            switch (channelId) {
+                case CHANNEL_TIME_STAMP:
+                    state = getDateTimeTypeState(forecastData.dt);
+                    break;
+                case CHANNEL_AIR_QUALITY_INDEX:
+                    state = new DecimalType(forecastData.airQualityIndex.index);
+                    break;
+                case CHANNEL_PARTICULATE_MATTER_2_5:
+                    state = getQuantityTypeState(forecastData.measurements.particulateMatter2dot5,
+                            Units.MICROGRAM_PER_CUBICMETRE);
+                    break;
+                case CHANNEL_PARTICULATE_MATTER_10:
+                    state = getQuantityTypeState(forecastData.measurements.particulateMatter10,
+                            Units.MICROGRAM_PER_CUBICMETRE);
+                    break;
+                case CHANNEL_CARBON_MONOXIDE:
+                    state = getQuantityTypeState(forecastData.measurements.carbonMonoxide,
+                            Units.MICROGRAM_PER_CUBICMETRE);
+                    break;
+                case CHANNEL_NITROGEN_MONOXIDE:
+                    state = getQuantityTypeState(forecastData.measurements.nitrogenMonoxide,
+                            Units.MICROGRAM_PER_CUBICMETRE);
+                    break;
+                case CHANNEL_NITROGEN_DIOXIDE:
+                    state = getQuantityTypeState(forecastData.measurements.nitrogenDioxide,
+                            Units.MICROGRAM_PER_CUBICMETRE);
+                    break;
+                case CHANNEL_OZONE:
+                    state = getQuantityTypeState(forecastData.measurements.ozone, Units.MICROGRAM_PER_CUBICMETRE);
+                    break;
+                case CHANNEL_SULPHUR_DIOXIDE:
+                    state = getQuantityTypeState(forecastData.measurements.sulphurDioxide,
+                            Units.MICROGRAM_PER_CUBICMETRE);
+                    break;
+                case CHANNEL_AMMONIA:
+                    state = getQuantityTypeState(forecastData.measurements.ammonia, Units.MICROGRAM_PER_CUBICMETRE);
+                    break;
+            }
+            logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
+            updateState(channelUID, state);
+        } else {
+            logger.debug("No air pollution data available to update channel '{}' of group '{}'.", channelId,
+                    channelGroupId);
+        }
+    }
+}
index b45e838672f98d88f99d232262d167173b953e9a..f0faccec36ccf5212aecbcdc7084b402ef4bdab1 100644 (file)
@@ -428,6 +428,12 @@ public class OpenWeatherMapWeatherAndForecastHandler extends AbstractOpenWeather
                 case CHANNEL_TIME_STAMP:
                     state = getDateTimeTypeState(forecastData.getDt());
                     break;
+                case CHANNEL_SUNRISE:
+                    state = getDateTimeTypeState(forecastData.getSunrise());
+                    break;
+                case CHANNEL_SUNSET:
+                    state = getDateTimeTypeState(forecastData.getSunset());
+                    break;
                 case CHANNEL_CONDITION:
                     if (!forecastData.getWeather().isEmpty()) {
                         state = getStringTypeState(forecastData.getWeather().get(0).getDescription());
@@ -487,6 +493,10 @@ public class OpenWeatherMapWeatherAndForecastHandler extends AbstractOpenWeather
                     Double snow = forecastData.getSnow();
                     state = getQuantityTypeState(snow == null ? 0 : snow, MILLI(METRE));
                     break;
+                case CHANNEL_PRECIP_PROBABILITY:
+                    Double probability = forecastData.getPop();
+                    state = getQuantityTypeState(probability == null ? 0 : probability * 100.0, PERCENT);
+                    break;
             }
             logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
             updateState(channelUID, state);
index 5bdcaeb40308bb36dd5b8e0efff0a40a395d6691..002c9c0fa67fb1f1ec7b1e24672938c903860553 100644 (file)
                </parameter>
        </config-description>
 
+       <config-description uri="thing-type:openweathermap:air-pollution">
+               <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="forecastHours" type="integer" min="0" max="120" step="1">
+                       <label>Number of Hours</label>
+                       <description>Number of hours for air pollution forecast.</description>
+                       <default>0</default>
+               </parameter>
+       </config-description>
+
        <config-description uri="thing-type:openweathermap:onecall">
                <parameter name="location" type="text" required="true">
                        <context>location</context>
index 524d7457cc7c399c789155bb841244b75ca7e6e3..327534734d8619b0d8b775e9a581e2cd510a57b2 100644 (file)
@@ -69,6 +69,9 @@ thing-type.openweathermap.weather-and-forecast.description = Erm
 thing-type.openweathermap.uvindex.label = UV-Index
 thing-type.openweathermap.uvindex.description = Ermöglicht die Anzeige des aktuellen UV-Index.
 
+thing-type.openweathermap.air-pollution.label = Luftqualität
+thing-type.openweathermap.air-pollution.description = Ermöglicht die Anzeige der aktuellen Luftqualität.
+
 # thing types config
 thing-type.config.openweathermap.weather-and-forecast.location.label = Ort der Wetterdaten
 thing-type.config.openweathermap.weather-and-forecast.location.description = Ort der Wetterdaten in geographischen Koordinaten (Breitengrad/Längengrad/Höhe).
@@ -85,6 +88,12 @@ thing-type.config.openweathermap.uvindex.location.description = Ort der Wetterda
 thing-type.config.openweathermap.uvindex.forecastDays.label = Tage
 thing-type.config.openweathermap.uvindex.forecastDays.description = Anzahl der Tage für die UV-Index Vorhersage.
 
+thing-type.config.openweathermap.air-pollution.location.label = Ort der Wetterdaten
+thing-type.config.openweathermap.air-pollution.location.description = Ort der Wetterdaten in geographischen Koordinaten (Breitengrad/Längengrad/Höhe).
+
+thing-type.config.openweathermap.air-pollution.forecastHours.label = Stunden
+thing-type.config.openweathermap.air-pollution.forecastHours.description = Anzahl der Stunden für die Vorhersage der Luftqualität.
+
 # channel group types
 channel-group-type.openweathermap.station.label = Wetterstation
 channel-group-type.openweathermap.station.description = Fasst Daten über die Wetterstation oder den Ort zusammen.
@@ -104,6 +113,12 @@ channel-group-type.openweathermap.uvindex.description = Fasst aktuelle UV-Index
 channel-group-type.openweathermap.uvindexForecast.label = UV-Index Vorhersage
 channel-group-type.openweathermap.uvindexForecast.description = Fasst Daten der UV-Index Vorhersage zusammen.
 
+channel-group-type.openweathermap.airPollution.label = Aktuelle Luftqualität
+channel-group-type.openweathermap.airPollution.description = Fasst Daten über die aktuelle Luftqualität zusammen.
+
+channel-group-type.openweathermap.airPollutionForecast.label = Vorhersage der Luftqualität
+channel-group-type.openweathermap.airPollutionForecast.description = Fasst Daten über die vorhergesagte Luftqualität zusammen.
+
 # channel groups
 thing-type.openweathermap.weather-and-forecast.group.forecastHours03.label = Wettervorhersage für 3 Stunden
 thing-type.openweathermap.weather-and-forecast.group.forecastHours03.description = Fasst Daten der Wettervorhersage in den nächsten drei Stunden zusammen.
@@ -184,6 +199,14 @@ channel-type.openweathermap.daily-forecast-time-stamp.label = Vorhersage Datum
 channel-type.openweathermap.daily-forecast-time-stamp.description = Zeigt das Datum der Vorhersage an.
 channel-type.openweathermap.daily-forecast-time-stamp.state.pattern = %1$td.%1$tm.%1$tY
 
+channel-type.openweathermap.sunrise.label = Sonnenaufgang
+channel-type.openweathermap.sunrise.description = Zeigt den Zeitpunkt des Sonnenaufgangs an.
+channel-type.openweathermap.sunrise.state.pattern = %1$td.%1$tm.%1$tY %1$tH:%1$tM:%1$tS
+
+channel-type.openweathermap.sunset.label = Sonnenuntergang
+channel-type.openweathermap.sunset.description = Zeigt den Zeitpunkt des Sonnenuntergangs an.
+channel-type.openweathermap.sunset.state.pattern = %1$td.%1$tm.%1$tY %1$tH:%1$tM:%1$tS
+
 channel-type.openweathermap.condition.label = Wetterlage
 channel-type.openweathermap.condition.description = Zeigt die aktuelle Wetterlage an.
 
@@ -253,12 +276,79 @@ channel-type.openweathermap.snow.description = Zeigt den kumulierten Schnee der
 channel-type.openweathermap.forecasted-snow.label = Vorhergesagter Schnee
 channel-type.openweathermap.forecasted-snow.description = Zeigt die vorhergesagte Schneemenge an.
 
+channel-type.openweathermap.precip-probability.label = Vorhergesagte Niederschlagswahrscheinlichkeit
+channel-type.openweathermap.precip-probability.description = Zeigt die vorhergesagte Niederschlagswahrscheinlichkeit an.
+
 channel-type.openweathermap.uvindex.label = UV-Index
 channel-type.openweathermap.uvindex.description = Zeigt den aktuellen UV-Index an.
 
 channel-type.openweathermap.forecasted-uvindex.label = Vorhergesagter UV-Index
 channel-type.openweathermap.forecasted-uvindex.description = Zeigt den vorhergesagten UV-Index an.
 
+channel-type.openweathermap.air-quality-index.label = Luftqualitätsindex
+channel-type.openweathermap.air-quality-index.description = Zeigt den aktuellen Luftqualitätsindex an.
+channel-type.openweathermap.air-quality-index.state.option.1 = Gut
+channel-type.openweathermap.air-quality-index.state.option.2 = Befriedigend
+channel-type.openweathermap.air-quality-index.state.option.3 = Moderat
+channel-type.openweathermap.air-quality-index.state.option.4 = Ungesund
+channel-type.openweathermap.air-quality-index.state.option.5 = Äußerst Ungesund
+
+channel-type.openweathermap.forecasted-air-quality-index.label = Vorhergesagter Luftqualitätsindex
+channel-type.openweathermap.forecasted-air-quality-index.description = Zeigt den vorhergesagten Luftqualitätsindex an.
+channel-type.openweathermap.forecasted-air-quality-index.state.option.1 = Gut
+channel-type.openweathermap.forecasted-air-quality-index.state.option.2 = Befriedigend
+channel-type.openweathermap.forecasted-air-quality-index.state.option.3 = Moderat
+channel-type.openweathermap.forecasted-air-quality-index.state.option.4 = Ungesund
+channel-type.openweathermap.forecasted-air-quality-index.state.option.5 = Äußerst Ungesund
+
+channel-type.openweathermap.particulate-matter-2dot5.label = Feinstaub - PM2.5
+channel-type.openweathermap.particulate-matter-2dot5.description = Aktuelle Dichte von Teilchen mit Partikelgröße unter 2,5 µm.
+
+channel-type.openweathermap.forecasted-particulate-matter-2dot5.label = Vorhergesagter Feinstaub - PM2.5
+channel-type.openweathermap.forecasted-particulate-matter-2dot5.description = Vorhergesagte Dichte von Teilchen mit Partikelgröße unter 2,5 µm.
+
+channel-type.openweathermap.particulate-matter-10.label = Feinstaub - PM10
+channel-type.openweathermap.particulate-matter-10.description = Aktuelle Dichte von Teilchen mit Partikelgröße unter 10 µm.
+
+channel-type.openweathermap.forecasted-particulate-matter-10.label = Vorhergesagter Feinstaub - PM10
+channel-type.openweathermap.forecasted-particulate-matter-10.description = Vorhergesagte Dichte von Teilchen mit Partikelgröße unter 10 µm.
+
+channel-type.openweathermap.carbon-monoxide.label = Kohlenmonoxid
+channel-type.openweathermap.carbon-monoxide.description = Aktuelle Konzentration an Kohlenmonoxid.
+
+channel-type.openweathermap.forecasted-carbon-monoxide.label = Vorhergesagter Kohlenmonoxid
+channel-type.openweathermap.forecasted-carbon-monoxide.description = Vorhergesagte Konzentration an Kohlenmonoxid.
+
+channel-type.openweathermap.nitrogen-monoxide.label = Stickstoffmonoxid
+channel-type.openweathermap.nitrogen-monoxide.description = Aktuelle Konzentration an Stickstoffmonoxid.
+
+channel-type.openweathermap.forecasted-nitrogen-monoxide.label = Vorhergesagter Stickstoffmonoxid
+channel-type.openweathermap.forecasted-nitrogen-monoxide.description = Vorhergesagte Konzentration an Stickstoffmonoxid.
+
+channel-type.openweathermap.nitrogen-dioxide.label = Stickstoffdioxid
+channel-type.openweathermap.nitrogen-dioxide.description = Aktuelle Konzentration an Stickstoffdioxid.
+
+channel-type.openweathermap.forecasted-nitrogen-dioxide.label = Vorhergesagter Stickstoffdioxid
+channel-type.openweathermap.forecasted-nitrogen-dioxide.description = Vorhergesagte Konzentration an Stickstoffdioxid.
+
+channel-type.openweathermap.ozone.label = Ozon
+channel-type.openweathermap.ozone.description = Aktuelle Konzentration an Ozon.
+
+channel-type.openweathermap.forecasted-ozone.label = Vorhergesagter Ozon
+channel-type.openweathermap.forecasted-ozone.description = Vorhergesagte Konzentration an Ozon.
+
+channel-type.openweathermap.sulphur-dioxide.label = Schwefeldioxid
+channel-type.openweathermap.sulphur-dioxide.description = Aktuelle Konzentration an Schwefeldioxid.
+
+channel-type.openweathermap.forecasted-sulphur-dioxide.label = Vorhergesagter Schwefeldioxid
+channel-type.openweathermap.forecasted-sulphur-dioxide.description = Vorhergesagte Konzentration an Schwefeldioxid.
+
+channel-type.openweathermap.ammonia.label = Ammoniak
+channel-type.openweathermap.ammonia.description = Aktuelle Konzentration an Ammoniak.
+
+channel-type.openweathermap.forecasted-ammonia.label = Vorhergesagter Ammoniak
+channel-type.openweathermap.forecasted-ammonia.description = Vorhergesagte Konzentration an Ammoniak.
+
 # thing status
 offline.conf-error-missing-apikey = Der Parameter 'API Schlüssel' muss konfiguriert werden.
 offline.conf-error-invalid-apikey = Ungültiger 'API Schlüssel'. Mehr Infos unter https://openweathermap.org/faq#error401.
@@ -270,6 +360,7 @@ offline.conf-error-parsing-location = Der Parameter 'Ort' kann nicht in Latitude
 offline.conf-error-not-supported-number-of-hours = Der Parameter 'forecastHours' muss zwischen 0 und 120 liegen - Schrittweite: 3.
 offline.conf-error-not-supported-number-of-days = Der Parameter 'forecastDays' muss zwischen 0 und 16 liegen.
 offline.conf-error-not-supported-uvindex-number-of-days = Der Parameter 'forecastDays' muss zwischen 1 und 8 liegen.
+offline.conf-error-not-supported-air-pollution-number-of-hours = Der Parameter 'forecastHours' muss zwischen 0 und 120 liegen - Schrittweite: 1.
 
 offline.conf-error-not-supported-onecall-number-of-minutes = Der Parameter 'forecastMinutes' muss zwischen 0 and 60 liegen.
 offline.conf-error-not-supported-onecall-number-of-hours = Der Parameter 'forecastHours' muss zwischen 0 und 48 liegen.
@@ -278,3 +369,4 @@ offline.conf-error-not-supported-onecall-number-of-days = Der Parameter 'forecas
 # discovery result
 discovery.openweathermap.weather-and-forecast.api.local.label = Lokales Wetter und Wettervorhersage
 discovery.openweathermap.uvindex.api.local.label = Lokaler UV-Index
+discovery.openweathermap.air-pollution.api.local.label = Lokale Luftqualität
index 9bbf626368efe2d99285cea575c52cdf616acf8d..9a7a4d39a6b83e97462078e3ddc0146151a3ad93 100644 (file)
@@ -84,6 +84,9 @@
                        <channel id="cloudiness" typeId="forecasted-cloudiness"/>
                        <channel id="rain" typeId="forecasted-rain"/>
                        <channel id="snow" typeId="forecasted-snow"/>
+                       <channel id="precip-probability" typeId="precip-probability"/>
+                       <channel id="sunrise" typeId="sunrise"/>
+                       <channel id="sunset" typeId="sunset"/>
                </channels>
        </channel-group-type>
 
                </channels>
        </channel-group-type>
 
+       <channel-group-type id="airPollution">
+               <label>Current Air Pollution</label>
+               <description>This is the current air pollution.</description>
+               <channels>
+                       <channel id="time-stamp" typeId="time-stamp"/>
+                       <channel id="airQualityIndex" typeId="air-quality-index"/>
+                       <channel id="particulateMatter2dot5" typeId="particulate-matter-2dot5"/>
+                       <channel id="particulateMatter10" typeId="particulate-matter-10"/>
+                       <channel id="carbonMonoxide" typeId="carbon-monoxide"/>
+                       <channel id="nitrogenMonoxide" typeId="nitrogen-monoxide"/>
+                       <channel id="nitrogenDioxide" typeId="nitrogen-dioxide"/>
+                       <channel id="ozone" typeId="ozone"/>
+                       <channel id="sulphurDioxide" typeId="sulphur-dioxide"/>
+                       <channel id="ammonia" typeId="ammonia"/>
+               </channels>
+       </channel-group-type>
+
+       <channel-group-type id="airPollutionForecast">
+               <label>Forecasted Air Pollution</label>
+               <description>This is the forecasted air pollution.</description>
+               <channels>
+                       <channel id="time-stamp" typeId="hourly-forecast-time-stamp"/>
+                       <channel id="airQualityIndex" typeId="forecasted-air-quality-index"/>
+                       <channel id="particulateMatter2dot5" typeId="forecasted-particulate-matter-2dot5"/>
+                       <channel id="particulateMatter10" typeId="forecasted-particulate-matter-10"/>
+                       <channel id="carbonMonoxide" typeId="forecasted-carbon-monoxide"/>
+                       <channel id="nitrogenMonoxide" typeId="forecasted-nitrogen-monoxide"/>
+                       <channel id="nitrogenDioxide" typeId="forecasted-nitrogen-dioxide"/>
+                       <channel id="ozone" typeId="forecasted-ozone"/>
+                       <channel id="sulphurDioxide" typeId="forecasted-sulphur-dioxide"/>
+                       <channel id="ammonia" typeId="forecasted-ammonia"/>
+               </channels>
+       </channel-group-type>
+
        <channel-group-type id="oneCallCurrent">
                <label>One Call API Current Weather</label>
                <description>Current weather data from the One Call API.</description>
                <state readOnly="true" pattern="%.1f"/>
        </channel-type>
 
+       <channel-type id="air-quality-index">
+               <item-type>Number</item-type>
+               <label>Air Quality Index</label>
+               <description>Current air quality index.</description>
+               <state readOnly="true" pattern="%.0f">
+                       <options>
+                               <option value="1">Good</option>
+                               <option value="2">Fair</option>
+                               <option value="3">Moderate</option>
+                               <option value="4">Poor</option>
+                               <option value="5">Very Poor</option>
+                       </options>
+               </state>
+       </channel-type>
+
+       <channel-type id="forecasted-air-quality-index">
+               <item-type>Number</item-type>
+               <label>Forecasted Air Quality Index</label>
+               <description>Forecasted air quality index.</description>
+               <state readOnly="true" pattern="%.0f">
+                       <options>
+                               <option value="1">Good</option>
+                               <option value="2">Fair</option>
+                               <option value="3">Moderate</option>
+                               <option value="4">Poor</option>
+                               <option value="5">Very Poor</option>
+                       </options>
+               </state>
+       </channel-type>
+
+       <channel-type id="particulate-matter-2dot5">
+               <item-type>Number:Density</item-type>
+               <label>Particulate Matter - PM2.5</label>
+               <description>Current density of particles less than 2.5 µm in diameter.</description>
+               <state readOnly="true" pattern="%.2f %unit%"/>
+       </channel-type>
+
+       <channel-type id="forecasted-particulate-matter-2dot5">
+               <item-type>Number:Density</item-type>
+               <label>Forecasted Particulate Matter - PM2.5</label>
+               <description>Forecasted density of particles less than 2.5 µm in diameter.</description>
+               <state readOnly="true" pattern="%.2f %unit%"/>
+       </channel-type>
+
+       <channel-type id="forecasted-particulate-matter-10">
+               <item-type>Number:Density</item-type>
+               <label>Particulate Matter - PM10</label>
+               <description>Current density of particles less than 10 µm in diameter.</description>
+               <state readOnly="true" pattern="%.2f %unit%"/>
+       </channel-type>
+
+       <channel-type id="particulate-matter-10">
+               <item-type>Number:Density</item-type>
+               <label>Forecasted Particulate Matter - PM10</label>
+               <description>Forecasted density of particles less than 10 µm in diameter.</description>
+               <state readOnly="true" pattern="%.2f %unit%"/>
+       </channel-type>
+
+       <channel-type id="carbon-monoxide">
+               <item-type>Number:Density</item-type>
+               <label>Carbon Monoxide</label>
+               <description>Current concentration of carbon monoxide.</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+
+       <channel-type id="forecasted-carbon-monoxide">
+               <item-type>Number:Density</item-type>
+               <label>Forecasted Carbon Monoxide</label>
+               <description>Forecasted concentration of carbon monoxide.</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+
+       <channel-type id="nitrogen-monoxide">
+               <item-type>Number:Density</item-type>
+               <label>Nitrogen Monoxide</label>
+               <description>Current concentration of nitrogen monoxide.</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+
+       <channel-type id="forecasted-nitrogen-monoxide">
+               <item-type>Number:Density</item-type>
+               <label>Forecasted Nitrogen Monoxide</label>
+               <description>Forecasted concentration of nitrogen monoxide.</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+
+       <channel-type id="nitrogen-dioxide">
+               <item-type>Number:Density</item-type>
+               <label>Nitrogen Dioxide</label>
+               <description>Current concentration of nitrogen dioxide.</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+
+       <channel-type id="forecasted-nitrogen-dioxide">
+               <item-type>Number:Density</item-type>
+               <label>Forecasted Nitrogen Dioxide</label>
+               <description>Forecasted concentration of nitrogen dioxide.</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+
+       <channel-type id="ozone">
+               <item-type>Number:Density</item-type>
+               <label>Ozone</label>
+               <description>Current concentration of ozone.</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+
+       <channel-type id="forecasted-ozone">
+               <item-type>Number:Density</item-type>
+               <label>Forecasted Ozone</label>
+               <description>Forecasted concentration of ozone.</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+
+       <channel-type id="sulphur-dioxide">
+               <item-type>Number:Density</item-type>
+               <label>Sulphur Dioxide</label>
+               <description>Current concentration of sulphur dioxide.</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+
+       <channel-type id="forecasted-sulphur-dioxide">
+               <item-type>Number:Density</item-type>
+               <label>Forecasted Sulphur Dioxide</label>
+               <description>Forecasted concentration of sulphur dioxide.</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+
+       <channel-type id="ammonia">
+               <item-type>Number:Density</item-type>
+               <label>Ammonia</label>
+               <description>Current concentration of ammonia.</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+
+       <channel-type id="forecasted-ammonia">
+               <item-type>Number:Density</item-type>
+               <label>Forecasted Ammonia</label>
+               <description>Forecasted concentration of ammonia.</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+
        <channel-type id="visibility">
                <item-type>Number:Length</item-type>
                <label>Visibility</label>
 
        <channel-type id="precip-probability">
                <item-type>Number:Dimensionless</item-type>
-               <label>Probability</label>
+               <label>Precipitation Probability</label>
                <description>Forecasted precipitation probability.</description>
+               <category>Rain</category>
                <state readOnly="true" pattern="%.1f %unit%"/>
        </channel-type>
-
 </thing:thing-descriptions>
index 848132a6865a162e1144a90918c6e70cf5dfedda..2155ea55ba4082b8d551a5e228dcc808fbb12311 100644 (file)
                <config-description-ref uri="thing-type:openweathermap:uvindex"/>
        </thing-type>
 
+       <thing-type id="air-pollution">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="weather-api"/>
+               </supported-bridge-type-refs>
+
+               <label>Air Pollution</label>
+               <description>Provides air pollution data from the OpenWeatherMap API.</description>
+
+               <channel-groups>
+                       <channel-group id="current" typeId="airPollution"/>
+               </channel-groups>
+
+               <representation-property>location</representation-property>
+
+               <config-description-ref uri="thing-type:openweathermap:air-pollution"/>
+       </thing-type>
+
        <thing-type id="onecall">
                <supported-bridge-type-refs>
                        <bridge-type-ref id="weather-api"/>