]> git.basschouten.com Git - openhab-addons.git/commitdiff
[openweathermap] Add support for persisting OneCall API forecasts (#15963)
authorFlorian Hotze <florianh_dev@icloud.com>
Thu, 14 Dec 2023 19:21:54 +0000 (20:21 +0100)
committerGitHub <noreply@github.com>
Thu, 14 Dec 2023 19:21:54 +0000 (20:21 +0100)
Implement time series support introduced by https://github.com/openhab/openhab-core/pull/3597.

Signed-off-by: Florian Hotze <florianh_dev@icloud.com>
bundles/org.openhab.binding.openweathermap/README.md
bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/handler/OpenWeatherMapOneCallHandler.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.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
bundles/org.openhab.binding.openweathermap/src/main/resources/OH-INF/update/instructions.xml [new file with mode: 0644]

index 1543fbeb45d4459f64e61ba1510f145c58942f69..23eb14c0723b95ded2b82b1726c7b1ea6d64871a 100644 (file)
@@ -50,6 +50,7 @@ New Subscribers to the One Call API will require setting the API version to 3.0
 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.
 It requires coordinates of the location of your interest.
 You can add as many `onecall` things for different locations to your setup as you like to observe.
+It also supports persisting forecast data using time series support, please read [Persisting Time Series](#persisting-time-series).
 
 ### One Call API History Data
 
@@ -70,7 +71,7 @@ Once the system location will be changed, the background discovery updates the c
 | Parameter       | Description                                                                                                                                                                                                                                                                       |
 |-----------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
 | apikey          | API key to access the OpenWeatherMap API. **Mandatory**                                                                                                                                                                                                                           |
-| refreshInterval | Specifies the refresh interval (in minutes). Optional, the default value is 60, the minimum value is 1.                                                                                                                                                                          |
+| refreshInterval | Specifies the refresh interval (in minutes). Optional, the default value is 60, the minimum value is 1.                                                                                                                                                                           |
 | language        | Language to be used by the OpenWeatherMap API. Optional, valid values are: `ar`, `bg`, `ca`, `de`, `el`, `en`, `es`, `fa`, `fi`, `fr`, `gl`, `hr`, `hu`, `it`, `ja`, `kr`, `la`, `lt`, `mk`,  `nl`, `pl`, `pt`, `ro`, `ru`, `se`, `sk`, `sl`, `tr`, `ua`, `vi`, `zh_cn`, `zh_tw`. |
 
 ### Current Weather And Forecast
@@ -103,13 +104,16 @@ Once the parameter `forecastHours` will be changed, the available channel groups
 
 ### One Call API Weather and Forecast
 
-| Parameter      | Description                                                                                                                                                           |
-|----------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| location       | Location of weather in geographical coordinates (latitude/longitude/altitude). **Mandatory**                                                                          |
-| forecastMinutes| Number of minutes for minutely precipitation forecast. Optional, the default value is 0, so by default **no** minutely forecast data is fetched. (min="0", max="60"). |
-| forecastHours  | Number of hours for hourly forecast. Optional, the default value is 24 (min="0", max="48").                                                                           |
-| forecastDays   | Number of days for daily forecast (including todays forecast). Optional, the default value is 6 (min="0", max="8").                                                   |
-| numberOfAlerts | Number of alerts to be shown. Optional, the default value is 0 (min="0", max="5").                                                                                    |
+| Parameter       | Description                                                                                                                           |
+|-----------------|---------------------------------------------------------------------------------------------------------------------------------------|
+| location        | Location of weather in geographical coordinates (latitude/longitude/altitude). **Mandatory**                                          |
+| forecastMinutes | Number of minutes for minutely precipitation forecast as minutely channels. Optional, the default value is 0 (min="0", max="60").     |
+| forecastHours   | Number of hours for hourly forecast as hourly channels. Optional, the default value is 12 (min="0", max="48").                        |
+| forecastDays    | Number of days for daily forecast (including todays forecast) as daily channels. Optional, the default value is 6 (min="0", max="8"). |
+| numberOfAlerts  | Number of alerts to be shown. Optional, the default value is 0 (min="0", max="5").                                                    |
+
+Set `forecastMinutes`, `forecastHours` and `forecastDays` to `0` if you only want to use the channels with time series support.
+In a future release, this will become the default setting as usage of the time series channels instead is encouraged.
 
 ### One Call API History Data
 
@@ -128,7 +132,7 @@ Once the parameter `forecastHours` will be changed, the available channel groups
 | station          | name       | String    | Name of the weather station or the city.     |
 | station          | location   | Location  | Location of the weather station or the city. |
 
-These channels are not supported in the One Call API
+These channels are not supported in the One Call API.
 
 ### Current Weather
 
@@ -165,10 +169,12 @@ The "3h" value will be divided by three to always have an estimated value for on
 
 Where available, the One Call API provides a minutely precipitation forecast for the next 60 minutes.
 
-| Channel Group ID                                       | Channel ID           | Item Type            | Description                                                                |
-|--------------------------------------------------------|----------------------|----------------------|----------------------------------------------------------------------------|
-| forecastMinutes01 ... forecastMinutes60                | time-stamp           | DateTime             | Time of data forecasted.                                                   |
-| forecastMinutes01 ... forecastMinutes60                | precipitation        | Number:Length        | Expected precipitation volume.                                             |
+| Channel Group ID                                          | Channel ID    | Item Type     | Description                                                     |
+|-----------------------------------------------------------|---------------|---------------|-----------------------------------------------------------------|
+| forecastMinutes01 ... forecastMinutes60                   | time-stamp    | DateTime      | Time of data forecasted.                                        |
+| forecastMinutely, forecastMinutes01 ... forecastMinutes60 | precipitation | Number:Length | Expected precipitation volume.                                  |
+
+The `forecastMinutely` channel group provides [time series support](#persisting-time-series).
 
 ### 3 Hour Forecast
 
@@ -197,42 +203,49 @@ Where available, the One Call API provides a minutely precipitation forecast for
 ### One Call API Hourly Forecast
 
 The One Call API provides hourly forecasts for 48 hours.
-The Channel Group IDs for those are `forecastHours01` to `forecastHours48`.
+The Channel Group IDs for those are `forecastHours01` to `forecastHours48`, and `forecastHourly` for channels with time series support.
 See above for a description of the available channels.
+The `forecastHourly` channel group provides all channels as described above with [time series support](#persisting-time-series), except `time-stamp`.
+
+In a future release, the `forecastHours01` to `forecastHours48` channel groups won't be created anymore by default as usage of the time series channels instead is encouraged.
 
 ### Daily Forecast
 
-| 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.                               |
-| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | icon-id              | String               | Id of the icon representing the forecasted weather condition. **Advanced**        |
-| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | apparent-temperature | Number:Temperature   | Forecasted apparent temperature. Not available in the One Call API                |
-| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | min-temperature      | Number:Temperature   | Minimum forecasted temperature of a day.                                          |
-| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | max-temperature      | Number:Temperature   | Maximum forecasted temperature of a day.                                          |
-| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | pressure             | Number:Pressure      | Forecasted barometric pressure.                                                   |
-| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | humidity             | Number:Dimensionless | Forecasted atmospheric humidity.                                                  |
-| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | wind-speed           | Number:Speed         | Forecasted wind speed.                                                            |
-| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | wind-direction       | Number:Angle         | Forecasted wind direction.                                                        |
-| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | gust-speed           | Number:Speed         | Forecasted gust speed. **Advanced**                                               |
-| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | cloudiness           | Number:Dimensionless | Forecasted cloudiness.                                                            |
-| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | rain                 | Number:Length        | Expected rain volume of a day.                                                    |
-| 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                  |
-| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7  | night-temperature    | Number:Temperature   | Expected night-temperature. Only available in the One Call API                    |
-| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7  | apparent-morning     | Number:Temperature   | Expected apparent temperature in the morning. Only available in the One Call API  |
-| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7  | apparent-day         | Number:Temperature   | Expected apparent temperature in the day. Only available in the One Call API      |
-| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7  | apparent-evening     | Number:Temperature   | Expected apparent temperature in the evening. Only available in the One Call API  |
-| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7  | apparent-night       | Number:Temperature   | Expected apparent temperature in the night. Only available in the One Call API    |
+| Channel Group ID                                                                | Channel ID           | Item Type            | Description                                                                      |
+|---------------------------------------------------------------------------------|----------------------|----------------------|----------------------------------------------------------------------------------|
+| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16                | time-stamp           | DateTime             | Date of data forecasted.                                                         |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | sunrise              | DateTime             | Time of sunrise for the given day.                                               |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | sunset               | DateTime             | Time of sunset for the given day.                                                |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | condition            | String               | Forecast weather condition.                                                      |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | condition-id         | String               | Id of the forecasted weather condition. **Advanced**                             |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | icon                 | Image                | Icon representing the forecasted weather condition.                              |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | icon-id              | String               | Id of the icon representing the forecasted weather condition. **Advanced**       |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | apparent-temperature | Number:Temperature   | Forecasted apparent temperature. Not available in the One Call API               |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | min-temperature      | Number:Temperature   | Minimum forecasted temperature of a day.                                         |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | max-temperature      | Number:Temperature   | Maximum forecasted temperature of a day.                                         |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | pressure             | Number:Pressure      | Forecasted barometric pressure.                                                  |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | humidity             | Number:Dimensionless | Forecasted atmospheric humidity.                                                 |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | wind-speed           | Number:Speed         | Forecasted wind speed.                                                           |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | wind-direction       | Number:Angle         | Forecasted wind direction.                                                       |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | gust-speed           | Number:Speed         | Forecasted gust speed. **Advanced**                                              |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | cloudiness           | Number:Dimensionless | Forecasted cloudiness.                                                           |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | rain                 | Number:Length        | Expected rain volume of a day.                                                   |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | snow                 | Number:Length        | Expected snow volume of a day.                                                   |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7  | dew-point            | Number:Temperature   | Expected dew-point. Only available in the One Call API                           |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7  | uvindex              | Number               | Forecasted Midday UV Index. Only available in the One Call API                   |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7  | precip-probability   | Number:Dimensionless | Precipitation probability.                                                       |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7  | morning-temperature  | Number:Temperature   | Expected morning temperature. Only available in the One Call API                 |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7  | day-temperature      | Number:Temperature   | Expected day-temperature. Only available in the One Call API                     |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7  | evening-temperature  | Number:Temperature   | Expected evening-temperature. Only available in the One Call API                 |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7  | night-temperature    | Number:Temperature   | Expected night-temperature. Only available in the One Call API                   |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7  | apparent-morning     | Number:Temperature   | Expected apparent temperature in the morning. Only available in the One Call API |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7  | apparent-day         | Number:Temperature   | Expected apparent temperature in the day. Only available in the One Call API     |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7  | apparent-evening     | Number:Temperature   | Expected apparent temperature in the evening. Only available in the One Call API |
+| forecastDaily, forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7  | apparent-night       | Number:Temperature   | Expected apparent temperature in the night. Only available in the One Call API   |
+
+The `forecastDaily` channel group provides [time series support](#persisting-time-series).
+
+In a future release, the `forecastToday` to `forecastDay7` channel groups won't be created anymore by default as usage of the time series channels instead is encouraged.
 
 ### One Call API Weather Warnings
 
@@ -268,6 +281,21 @@ The `uvindex` channel is also available in the current data and the daily foreca
 | 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.                          |
 
+## Persisting Time Series
+
+The binding offers support for persisting forecast values for most channels of the [One Call API Weather and Forecast](#one-call-api-weather-and-forecast) Thing.
+The recommended persistence strategy is `forecast`, as it ensures a clean history without redundancy.
+
+### Configuration
+
+Make sure you have a persistence service installed and ready for use.
+
+To configure persisting forecast data, create and link Items to those channels with time series support (as usual).
+Next, enable persistence for these Items using the `forecast` persistence strategy.
+Finally, open the UI, search for one of the newly created Items, open the analyzer and select a future time range.
+
+To access forecast data stored in persistence from scripts and rules, use the [Persistence Extensions]({{base}}/configuration/persistence.html#persistence-extensions-in-scripts-and-rules).
+
 ## Full Example
 
 ### Things
index 6049aaf71ec532da19b6d2c43cfc88f40e828347..ef3ceb3c58f5e1cba673e80639538fb051e976aa 100644 (file)
@@ -16,7 +16,9 @@ import static org.openhab.binding.openweathermap.internal.OpenWeatherMapBindingC
 import static org.openhab.core.library.unit.MetricPrefix.*;
 import static org.openhab.core.library.unit.SIUnits.*;
 import static org.openhab.core.library.unit.Units.*;
+import static org.openhab.core.types.TimeSeries.Policy.REPLACE;
 
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.regex.Matcher;
@@ -30,6 +32,8 @@ import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapOneCallAPID
 import org.openhab.binding.openweathermap.internal.dto.forecast.daily.FeelsLikeTemp;
 import org.openhab.binding.openweathermap.internal.dto.forecast.daily.Temp;
 import org.openhab.binding.openweathermap.internal.dto.onecall.Alert;
+import org.openhab.binding.openweathermap.internal.dto.onecall.Daily;
+import org.openhab.binding.openweathermap.internal.dto.onecall.Hourly;
 import org.openhab.binding.openweathermap.internal.dto.onecall.Precipitation;
 import org.openhab.core.i18n.CommunicationException;
 import org.openhab.core.i18n.ConfigurationException;
@@ -42,6 +46,7 @@ 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.TimeSeries;
 import org.openhab.core.types.UnDefType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -54,6 +59,7 @@ import com.google.gson.JsonSyntaxException;
  *
  * @author Wolfgang Klimt - Initial contribution
  * @author Christoph Weitkamp - Added weather alerts
+ * @author Florian Hotze - Added support for persisting forecasts
  */
 @NonNullByDefault
 public class OpenWeatherMapOneCallHandler extends AbstractOpenWeatherMapHandler {
@@ -61,8 +67,11 @@ public class OpenWeatherMapOneCallHandler extends AbstractOpenWeatherMapHandler
     private final Logger logger = LoggerFactory.getLogger(OpenWeatherMapOneCallHandler.class);
 
     private static final String CHANNEL_GROUP_MINUTELY_FORECAST_PREFIX = "forecastMinutes";
+    private static final String CHANNEL_GROUP_MINUTELY_TIMESERIES_PREFIX = "forecastMinutely";
     private static final String CHANNEL_GROUP_HOURLY_FORECAST_PREFIX = "forecastHours";
+    private static final String CHANNEL_GROUP_HOURLY_TIMESERIES_PREFIX = "forecastHourly";
     private static final String CHANNEL_GROUP_DAILY_FORECAST_PREFIX = "forecastDay";
+    private static final String CHANNEL_GROUP_DAILY_TIMESERIES_PREFIX = "forecastDaily";
     private static final String CHANNEL_GROUP_ALERTS_PREFIX = "alerts";
     private static final Pattern CHANNEL_GROUP_HOURLY_FORECAST_PREFIX_PATTERN = Pattern
             .compile(CHANNEL_GROUP_HOURLY_FORECAST_PREFIX + "([0-9]*)");
@@ -75,9 +84,10 @@ public class OpenWeatherMapOneCallHandler extends AbstractOpenWeatherMapHandler
 
     private @Nullable OpenWeatherMapOneCallAPIData weatherData;
 
-    private int forecastMinutes = 60;
-    private int forecastHours = 24;
-    private int forecastDays = 8;
+    // forecastMinutes, -Hours and -Days determine the number of channel groups to create for each type
+    private int forecastMinutes = 0;
+    private int forecastHours = 12;
+    private int forecastDays = 6;
     private int numberOfAlerts = 0;
 
     public OpenWeatherMapOneCallHandler(Thing thing, final TimeZoneProvider timeZoneProvider) {
@@ -217,8 +227,8 @@ public class OpenWeatherMapOneCallHandler extends AbstractOpenWeatherMapHandler
             throws CommunicationException, ConfigurationException {
         logger.debug("Update weather and forecast data of thing '{}'.", getThing().getUID());
         try {
-            weatherData = connection.getOneCallAPIData(location, forecastMinutes == 0, forecastHours == 0,
-                    forecastDays == 0, numberOfAlerts == 0);
+            // Include minutely, hourly and daily data as this is needed for the time series channels
+            weatherData = connection.getOneCallAPIData(location, false, false, false, numberOfAlerts == 0);
             return true;
         } catch (JsonSyntaxException e) {
             logger.debug("JsonSyntaxException occurred during execution: {}", e.getMessage(), e);
@@ -240,6 +250,15 @@ public class OpenWeatherMapOneCallHandler extends AbstractOpenWeatherMapHandler
             case CHANNEL_GROUP_ONECALL_TOMORROW:
                 updateDailyForecastChannel(channelUID, 1);
                 break;
+            case CHANNEL_GROUP_MINUTELY_TIMESERIES_PREFIX:
+                updateMinutelyForecastTimeSeries(channelUID);
+                break;
+            case CHANNEL_GROUP_HOURLY_TIMESERIES_PREFIX:
+                updateHourlyForecastTimeSeries(channelUID);
+                break;
+            case CHANNEL_GROUP_DAILY_TIMESERIES_PREFIX:
+                updateDailyForecastTimeSeries(channelUID);
+                break;
             default:
                 int i;
                 Matcher hourlyForecastMatcher = CHANNEL_GROUP_HOURLY_FORECAST_PREFIX_PATTERN.matcher(channelGroupId);
@@ -412,8 +431,41 @@ public class OpenWeatherMapOneCallHandler extends AbstractOpenWeatherMapHandler
         }
     }
 
+    private void updateMinutelyForecastTimeSeries(ChannelUID channelUID) {
+        String channelId = channelUID.getIdWithoutGroup();
+        String channelGroupId = channelUID.getGroupId();
+        OpenWeatherMapOneCallAPIData localWeatherData = weatherData;
+        if (channelId.equals(CHANNEL_TIME_STAMP)) {
+            logger.debug("Channel `{}` of group '{}' is no supported time-series channel.", channelId, channelGroupId);
+            return;
+        }
+        if (localWeatherData != null && !localWeatherData.getMinutely().isEmpty()) {
+            List<org.openhab.binding.openweathermap.internal.dto.onecall.Minutely> forecastData = localWeatherData
+                    .getMinutely();
+            TimeSeries timeSeries = new TimeSeries(REPLACE);
+            forecastData.forEach((m) -> {
+                if (channelId.equals(CHANNEL_PRECIPITATION)) {
+                    State state = UnDefType.UNDEF;
+                    Instant timestamp = Instant.ofEpochSecond(m.getDt());
+                    double precipitation = m.getPrecipitation();
+                    state = getQuantityTypeState(precipitation, MILLI(METRE));
+                    timeSeries.add(timestamp, state);
+                } else {
+                    // This should not happen
+                    logger.warn("Unknown channel id {} in onecall minutely weather data", channelId);
+                    return;
+                }
+            });
+            logger.debug("Update channel '{}' of group '{}' with new time-series '{}'.", channelId, channelGroupId,
+                    timeSeries);
+            sendTimeSeries(channelUID, timeSeries);
+        } else {
+            logger.debug("No weather data available to update channel '{}' of group '{}'.", channelId, channelGroupId);
+        }
+    }
+
     /**
-     * Update the channel from the last OpenWeatherMap data retrieved.
+     * Update the hourly forecast channel from the last OpenWeatherMap data retrieved.
      *
      * @param channelUID the id identifying the channel to be updated
      * @param count the index of the hourly data referenced by the channel (hour 1 is count 0)
@@ -431,79 +483,7 @@ public class OpenWeatherMapOneCallHandler extends AbstractOpenWeatherMapHandler
         if (localWeatherData != null && localWeatherData.getHourly().size() > count) {
             org.openhab.binding.openweathermap.internal.dto.onecall.Hourly forecastData = localWeatherData.getHourly()
                     .get(count);
-            State state = UnDefType.UNDEF;
-            switch (channelId) {
-                case CHANNEL_TIME_STAMP:
-                    state = getDateTimeTypeState(forecastData.getDt());
-                    break;
-                case CHANNEL_CONDITION:
-                    if (!forecastData.getWeather().isEmpty()) {
-                        state = getStringTypeState(forecastData.getWeather().get(0).getDescription());
-                    }
-                    break;
-                case CHANNEL_CONDITION_ID:
-                    if (!forecastData.getWeather().isEmpty()) {
-                        state = getStringTypeState(Integer.toString(forecastData.getWeather().get(0).getId()));
-                    }
-                    break;
-                case CHANNEL_CONDITION_ICON:
-                    if (!forecastData.getWeather().isEmpty()) {
-                        state = getRawTypeState(
-                                OpenWeatherMapConnection.getWeatherIcon(forecastData.getWeather().get(0).getIcon()));
-                    }
-                    break;
-                case CHANNEL_CONDITION_ICON_ID:
-                    if (!forecastData.getWeather().isEmpty()) {
-                        state = getStringTypeState(forecastData.getWeather().get(0).getIcon());
-                    }
-                    break;
-                case CHANNEL_TEMPERATURE:
-                    state = getQuantityTypeState(forecastData.getTemp(), CELSIUS);
-                    break;
-                case CHANNEL_APPARENT_TEMPERATURE:
-                    state = getQuantityTypeState(forecastData.getFeelsLike(), CELSIUS);
-                    break;
-                case CHANNEL_PRESSURE:
-                    state = getQuantityTypeState(forecastData.getPressure(), HECTO(PASCAL));
-                    break;
-                case CHANNEL_HUMIDITY:
-                    state = getQuantityTypeState(forecastData.getHumidity(), PERCENT);
-                    break;
-                case CHANNEL_DEW_POINT:
-                    state = getQuantityTypeState(forecastData.getDewPoint(), CELSIUS);
-                    break;
-                case CHANNEL_WIND_SPEED:
-                    state = getQuantityTypeState(forecastData.getWindSpeed(), METRE_PER_SECOND);
-                    break;
-                case CHANNEL_WIND_DIRECTION:
-                    state = getQuantityTypeState(forecastData.getWindDeg(), DEGREE_ANGLE);
-                    break;
-                case CHANNEL_GUST_SPEED:
-                    state = getQuantityTypeState(forecastData.getWindGust(), METRE_PER_SECOND);
-                    break;
-                case CHANNEL_CLOUDINESS:
-                    state = getQuantityTypeState(forecastData.getClouds(), PERCENT);
-                    break;
-                case CHANNEL_VISIBILITY:
-                    State tempstate = new QuantityType<>(localWeatherData.getCurrent().getVisibility(), METRE)
-                            .toUnit(KILO(METRE));
-                    state = (tempstate == null ? state : tempstate);
-                case CHANNEL_PRECIP_PROBABILITY:
-                    state = getQuantityTypeState(forecastData.getPop() * 100.0, PERCENT);
-                    break;
-                case CHANNEL_RAIN:
-                    Precipitation rain = forecastData.getRain();
-                    state = getQuantityTypeState(rain == null ? 0 : rain.get1h(), MILLI(METRE));
-                    break;
-                case CHANNEL_SNOW:
-                    Precipitation snow = forecastData.getSnow();
-                    state = getQuantityTypeState(snow == null ? 0 : snow.get1h(), MILLI(METRE));
-                    break;
-                default:
-                    // This should not happen
-                    logger.warn("Unknown channel id {} in onecall hourly weather data", channelId);
-                    break;
-            }
+            State state = getHourlyForecastState(channelId, forecastData, localWeatherData);
             logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
             updateState(channelUID, state);
         } else {
@@ -512,7 +492,115 @@ public class OpenWeatherMapOneCallHandler extends AbstractOpenWeatherMapHandler
     }
 
     /**
-     * Update the channel from the last OpenWeatherMap data retrieved.
+     * Update the hourly forecast time series channel from the last OpenWeatherMap data retrieved.
+     * 
+     * @param channelUID the id identifying the channel to be updated
+     */
+    private void updateHourlyForecastTimeSeries(ChannelUID channelUID) {
+        String channelId = channelUID.getIdWithoutGroup();
+        String channelGroupId = channelUID.getGroupId();
+        if (channelId.equals(CHANNEL_TIME_STAMP)) {
+            logger.debug("Channel `{}` of group '{}' is no supported time-series channel.", channelId, channelGroupId);
+            return;
+        }
+        OpenWeatherMapOneCallAPIData localWeatherData = weatherData;
+        if (localWeatherData != null && !localWeatherData.getHourly().isEmpty()) {
+            List<org.openhab.binding.openweathermap.internal.dto.onecall.Hourly> forecastData = localWeatherData
+                    .getHourly();
+            TimeSeries timeSeries = new TimeSeries(REPLACE);
+            forecastData.forEach((h) -> {
+                Instant timestamp = Instant.ofEpochSecond(h.getDt());
+                State state = getHourlyForecastState(channelId, h, localWeatherData);
+                timeSeries.add(timestamp, state);
+            });
+            logger.debug("Update channel '{}' of group '{}' with new time-series '{}'.", channelId, channelGroupId,
+                    timeSeries);
+            sendTimeSeries(channelUID, timeSeries);
+        } else {
+            logger.debug("No weather data available to update channel '{}'.", channelId);
+        }
+    }
+
+    private State getHourlyForecastState(String channelId, Hourly forecastData,
+            OpenWeatherMapOneCallAPIData localWeatherData) {
+        State state = UnDefType.UNDEF;
+        switch (channelId) {
+            case CHANNEL_TIME_STAMP:
+                state = getDateTimeTypeState(forecastData.getDt());
+                break;
+            case CHANNEL_CONDITION:
+                if (!forecastData.getWeather().isEmpty()) {
+                    state = getStringTypeState(forecastData.getWeather().get(0).getDescription());
+                }
+                break;
+            case CHANNEL_CONDITION_ID:
+                if (!forecastData.getWeather().isEmpty()) {
+                    state = getStringTypeState(Integer.toString(forecastData.getWeather().get(0).getId()));
+                }
+                break;
+            case CHANNEL_CONDITION_ICON:
+                if (!forecastData.getWeather().isEmpty()) {
+                    state = getRawTypeState(
+                            OpenWeatherMapConnection.getWeatherIcon(forecastData.getWeather().get(0).getIcon()));
+                }
+                break;
+            case CHANNEL_CONDITION_ICON_ID:
+                if (!forecastData.getWeather().isEmpty()) {
+                    state = getStringTypeState(forecastData.getWeather().get(0).getIcon());
+                }
+                break;
+            case CHANNEL_TEMPERATURE:
+                state = getQuantityTypeState(forecastData.getTemp(), CELSIUS);
+                break;
+            case CHANNEL_APPARENT_TEMPERATURE:
+                state = getQuantityTypeState(forecastData.getFeelsLike(), CELSIUS);
+                break;
+            case CHANNEL_PRESSURE:
+                state = getQuantityTypeState(forecastData.getPressure(), HECTO(PASCAL));
+                break;
+            case CHANNEL_HUMIDITY:
+                state = getQuantityTypeState(forecastData.getHumidity(), PERCENT);
+                break;
+            case CHANNEL_DEW_POINT:
+                state = getQuantityTypeState(forecastData.getDewPoint(), CELSIUS);
+                break;
+            case CHANNEL_WIND_SPEED:
+                state = getQuantityTypeState(forecastData.getWindSpeed(), METRE_PER_SECOND);
+                break;
+            case CHANNEL_WIND_DIRECTION:
+                state = getQuantityTypeState(forecastData.getWindDeg(), DEGREE_ANGLE);
+                break;
+            case CHANNEL_GUST_SPEED:
+                state = getQuantityTypeState(forecastData.getWindGust(), METRE_PER_SECOND);
+                break;
+            case CHANNEL_CLOUDINESS:
+                state = getQuantityTypeState(forecastData.getClouds(), PERCENT);
+                break;
+            case CHANNEL_VISIBILITY:
+                State tempstate = new QuantityType<>(localWeatherData.getCurrent().getVisibility(), METRE)
+                        .toUnit(KILO(METRE));
+                state = (tempstate == null ? state : tempstate);
+            case CHANNEL_PRECIP_PROBABILITY:
+                state = getQuantityTypeState(forecastData.getPop() * 100.0, PERCENT);
+                break;
+            case CHANNEL_RAIN:
+                Precipitation rain = forecastData.getRain();
+                state = getQuantityTypeState(rain == null ? 0 : rain.get1h(), MILLI(METRE));
+                break;
+            case CHANNEL_SNOW:
+                Precipitation snow = forecastData.getSnow();
+                state = getQuantityTypeState(snow == null ? 0 : snow.get1h(), MILLI(METRE));
+                break;
+            default:
+                // This should not happen
+                logger.warn("Unknown channel id {} in OneCall hourly weather data", channelId);
+                break;
+        }
+        return state;
+    }
+
+    /**
+     * Update the daily forecast channel from the last OpenWeatherMap data retrieved.
      *
      * @param channelUID the id identifying the channel to be updated
      * @param count the index of the daily data referenced by the channel (today is count 0)
@@ -530,143 +618,7 @@ public class OpenWeatherMapOneCallHandler extends AbstractOpenWeatherMapHandler
         if (localWeatherData != null && localWeatherData.getDaily().size() > count) {
             org.openhab.binding.openweathermap.internal.dto.onecall.Daily forecastData = localWeatherData.getDaily()
                     .get(count);
-            State state = UnDefType.UNDEF;
-            Temp temp;
-            FeelsLikeTemp feelsLike;
-            switch (channelId) {
-                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());
-                    }
-                    break;
-                case CHANNEL_CONDITION_ID:
-                    if (!forecastData.getWeather().isEmpty()) {
-                        state = getStringTypeState(Integer.toString(forecastData.getWeather().get(0).getId()));
-                    }
-                    break;
-                case CHANNEL_CONDITION_ICON:
-                    if (!forecastData.getWeather().isEmpty()) {
-                        state = getRawTypeState(
-                                OpenWeatherMapConnection.getWeatherIcon(forecastData.getWeather().get(0).getIcon()));
-                    }
-                    break;
-                case CHANNEL_CONDITION_ICON_ID:
-                    if (!forecastData.getWeather().isEmpty()) {
-                        state = getStringTypeState(forecastData.getWeather().get(0).getIcon());
-                    }
-                    break;
-                case CHANNEL_MIN_TEMPERATURE:
-                    temp = forecastData.getTemp();
-                    if (temp != null) {
-                        state = getQuantityTypeState(temp.getMin(), CELSIUS);
-                    }
-                    break;
-                case CHANNEL_MAX_TEMPERATURE:
-                    temp = forecastData.getTemp();
-                    if (temp != null) {
-                        state = getQuantityTypeState(temp.getMax(), CELSIUS);
-                    }
-                    break;
-                case CHANNEL_MORNING_TEMPERATURE:
-                    temp = forecastData.getTemp();
-                    if (temp != null) {
-                        state = getQuantityTypeState(temp.getMorn(), CELSIUS);
-                    }
-                    break;
-                case CHANNEL_DAY_TEMPERATURE:
-                    temp = forecastData.getTemp();
-                    if (temp != null) {
-                        state = getQuantityTypeState(temp.getDay(), CELSIUS);
-                    }
-                    break;
-                case CHANNEL_EVENING_TEMPERATURE:
-                    temp = forecastData.getTemp();
-                    if (temp != null) {
-                        state = getQuantityTypeState(temp.getEve(), CELSIUS);
-                    }
-                    break;
-                case CHANNEL_NIGHT_TEMPERATURE:
-                    temp = forecastData.getTemp();
-                    if (temp != null) {
-                        state = getQuantityTypeState(temp.getNight(), CELSIUS);
-                    }
-                    break;
-
-                case CHANNEL_APPARENT_DAY:
-                    feelsLike = forecastData.getFeelsLike();
-                    if (feelsLike != null) {
-                        state = getQuantityTypeState(feelsLike.getDay(), CELSIUS);
-                    }
-                    break;
-                case CHANNEL_APPARENT_MORNING:
-                    feelsLike = forecastData.getFeelsLike();
-                    if (feelsLike != null) {
-                        state = getQuantityTypeState(feelsLike.getMorn(), CELSIUS);
-                    }
-                    break;
-                case CHANNEL_APPARENT_EVENING:
-                    feelsLike = forecastData.getFeelsLike();
-                    if (feelsLike != null) {
-                        state = getQuantityTypeState(feelsLike.getEve(), CELSIUS);
-                    }
-                    break;
-                case CHANNEL_APPARENT_NIGHT:
-                    feelsLike = forecastData.getFeelsLike();
-                    if (feelsLike != null) {
-                        state = getQuantityTypeState(feelsLike.getNight(), CELSIUS);
-                    }
-                    break;
-                case CHANNEL_PRESSURE:
-                    state = getQuantityTypeState(forecastData.getPressure(), HECTO(PASCAL));
-                    break;
-                case CHANNEL_HUMIDITY:
-                    state = getQuantityTypeState(forecastData.getHumidity(), PERCENT);
-                    break;
-                case CHANNEL_WIND_SPEED:
-                    state = getQuantityTypeState(forecastData.getWindSpeed(), METRE_PER_SECOND);
-                    break;
-                case CHANNEL_WIND_DIRECTION:
-                    state = getQuantityTypeState(forecastData.getWindDeg(), DEGREE_ANGLE);
-                    break;
-                case CHANNEL_GUST_SPEED:
-                    state = getQuantityTypeState(forecastData.getWindGust(), METRE_PER_SECOND);
-                    break;
-                case CHANNEL_CLOUDINESS:
-                    state = getQuantityTypeState(forecastData.getClouds(), PERCENT);
-                    break;
-                case CHANNEL_DEW_POINT:
-                    state = getQuantityTypeState(forecastData.getDewPoint(), CELSIUS);
-                    break;
-                case CHANNEL_UVINDEX:
-                    state = getDecimalTypeState(forecastData.getUvi());
-                    break;
-                case CHANNEL_VISIBILITY:
-                    State tempstate = new QuantityType<>(localWeatherData.getCurrent().getVisibility(), METRE)
-                            .toUnit(KILO(METRE));
-                    state = (tempstate == null ? state : tempstate);
-                case CHANNEL_PRECIP_PROBABILITY:
-                    state = getQuantityTypeState(forecastData.getPop() * 100.0, PERCENT);
-                    break;
-                case CHANNEL_RAIN:
-                    state = getQuantityTypeState(forecastData.getRain(), MILLI(METRE));
-                    break;
-                case CHANNEL_SNOW:
-                    state = getQuantityTypeState(forecastData.getSnow(), MILLI(METRE));
-                    break;
-                default:
-                    // This should not happen
-                    logger.warn("Unknown channel id {} in onecall daily weather data", channelId);
-                    break;
-            }
+            State state = getDailyForecastState(channelId, forecastData, localWeatherData);
             logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
             updateState(channelUID, state);
         } else {
@@ -674,6 +626,173 @@ public class OpenWeatherMapOneCallHandler extends AbstractOpenWeatherMapHandler
         }
     }
 
+    private void updateDailyForecastTimeSeries(ChannelUID channelUID) {
+        String channelId = channelUID.getIdWithoutGroup();
+        String channelGroupId = channelUID.getGroupId();
+        if (channelId.equals(CHANNEL_TIME_STAMP)) {
+            logger.debug("Channel `{}` of group '{}' is no supported time-series channel.", channelId, channelGroupId);
+            return;
+        }
+        OpenWeatherMapOneCallAPIData localWeatherData = weatherData;
+        if (localWeatherData != null && !localWeatherData.getDaily().isEmpty()) {
+            List<org.openhab.binding.openweathermap.internal.dto.onecall.Daily> forecastData = localWeatherData
+                    .getDaily();
+            TimeSeries timeSeries = new TimeSeries(REPLACE);
+            forecastData.forEach((d) -> {
+                Instant timestamp = Instant.ofEpochSecond(d.getDt());
+                State state = getDailyForecastState(channelId, d, localWeatherData);
+                timeSeries.add(timestamp, state);
+            });
+            logger.debug("Update channel '{}' of group '{}' with new time-series '{}'.", channelId, channelGroupId,
+                    timeSeries);
+            sendTimeSeries(channelUID, timeSeries);
+        } else {
+            logger.debug("No weather data available to update channel '{}'.", channelId);
+        }
+    }
+
+    private State getDailyForecastState(String channelId, Daily forecastData,
+            OpenWeatherMapOneCallAPIData localWeatherData) {
+        State state = UnDefType.UNDEF;
+        FeelsLikeTemp feelsLike;
+        Temp temp;
+        switch (channelId) {
+            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());
+                }
+                break;
+            case CHANNEL_CONDITION_ID:
+                if (!forecastData.getWeather().isEmpty()) {
+                    state = getStringTypeState(Integer.toString(forecastData.getWeather().get(0).getId()));
+                }
+                break;
+            case CHANNEL_CONDITION_ICON:
+                if (!forecastData.getWeather().isEmpty()) {
+                    state = getRawTypeState(
+                            OpenWeatherMapConnection.getWeatherIcon(forecastData.getWeather().get(0).getIcon()));
+                }
+                break;
+            case CHANNEL_CONDITION_ICON_ID:
+                if (!forecastData.getWeather().isEmpty()) {
+                    state = getStringTypeState(forecastData.getWeather().get(0).getIcon());
+                }
+                break;
+            case CHANNEL_MIN_TEMPERATURE:
+                temp = forecastData.getTemp();
+                if (temp != null) {
+                    state = getQuantityTypeState(temp.getMin(), CELSIUS);
+                }
+                break;
+            case CHANNEL_MAX_TEMPERATURE:
+                temp = forecastData.getTemp();
+                if (temp != null) {
+                    state = getQuantityTypeState(temp.getMax(), CELSIUS);
+                }
+                break;
+            case CHANNEL_MORNING_TEMPERATURE:
+                temp = forecastData.getTemp();
+                if (temp != null) {
+                    state = getQuantityTypeState(temp.getMorn(), CELSIUS);
+                }
+                break;
+            case CHANNEL_DAY_TEMPERATURE:
+                temp = forecastData.getTemp();
+                if (temp != null) {
+                    state = getQuantityTypeState(temp.getDay(), CELSIUS);
+                }
+                break;
+            case CHANNEL_EVENING_TEMPERATURE:
+                temp = forecastData.getTemp();
+                if (temp != null) {
+                    state = getQuantityTypeState(temp.getEve(), CELSIUS);
+                }
+                break;
+            case CHANNEL_NIGHT_TEMPERATURE:
+                temp = forecastData.getTemp();
+                if (temp != null) {
+                    state = getQuantityTypeState(temp.getNight(), CELSIUS);
+                }
+                break;
+
+            case CHANNEL_APPARENT_DAY:
+                feelsLike = forecastData.getFeelsLike();
+                if (feelsLike != null) {
+                    state = getQuantityTypeState(feelsLike.getDay(), CELSIUS);
+                }
+                break;
+            case CHANNEL_APPARENT_MORNING:
+                feelsLike = forecastData.getFeelsLike();
+                if (feelsLike != null) {
+                    state = getQuantityTypeState(feelsLike.getMorn(), CELSIUS);
+                }
+                break;
+            case CHANNEL_APPARENT_EVENING:
+                feelsLike = forecastData.getFeelsLike();
+                if (feelsLike != null) {
+                    state = getQuantityTypeState(feelsLike.getEve(), CELSIUS);
+                }
+                break;
+            case CHANNEL_APPARENT_NIGHT:
+                feelsLike = forecastData.getFeelsLike();
+                if (feelsLike != null) {
+                    state = getQuantityTypeState(feelsLike.getNight(), CELSIUS);
+                }
+                break;
+            case CHANNEL_PRESSURE:
+                state = getQuantityTypeState(forecastData.getPressure(), HECTO(PASCAL));
+                break;
+            case CHANNEL_HUMIDITY:
+                state = getQuantityTypeState(forecastData.getHumidity(), PERCENT);
+                break;
+            case CHANNEL_WIND_SPEED:
+                state = getQuantityTypeState(forecastData.getWindSpeed(), METRE_PER_SECOND);
+                break;
+            case CHANNEL_WIND_DIRECTION:
+                state = getQuantityTypeState(forecastData.getWindDeg(), DEGREE_ANGLE);
+                break;
+            case CHANNEL_GUST_SPEED:
+                state = getQuantityTypeState(forecastData.getWindGust(), METRE_PER_SECOND);
+                break;
+            case CHANNEL_CLOUDINESS:
+                state = getQuantityTypeState(forecastData.getClouds(), PERCENT);
+                break;
+            case CHANNEL_DEW_POINT:
+                state = getQuantityTypeState(forecastData.getDewPoint(), CELSIUS);
+                break;
+            case CHANNEL_UVINDEX:
+                state = getDecimalTypeState(forecastData.getUvi());
+                break;
+            case CHANNEL_VISIBILITY:
+                State tempstate = new QuantityType<>(localWeatherData.getCurrent().getVisibility(), METRE)
+                        .toUnit(KILO(METRE));
+                state = (tempstate == null ? state : tempstate);
+            case CHANNEL_PRECIP_PROBABILITY:
+                state = getQuantityTypeState(forecastData.getPop() * 100.0, PERCENT);
+                break;
+            case CHANNEL_RAIN:
+                state = getQuantityTypeState(forecastData.getRain(), MILLI(METRE));
+                break;
+            case CHANNEL_SNOW:
+                state = getQuantityTypeState(forecastData.getSnow(), MILLI(METRE));
+                break;
+            default:
+                // This should not happen
+                logger.warn("Unknown channel id {} in OneCall daily weather data", channelId);
+                break;
+        }
+        return state;
+    }
+
     /**
      * Update the channel from the last OpenWeaterhMap data retrieved.
      *
index 728a2c7a96ce6f8cf1e866d91877100e03af1283..58c40dc36e1caf9f21fdc6a36bf3e744d98184b3 100644 (file)
                        <description>Location of weather in geographical coordinates (latitude/longitude/altitude).</description>
                </parameter>
                <parameter name="forecastDays" type="integer" min="0" max="8" step="1">
-                       <label>Number of Days</label>
-                       <description>Number of days for daily forecast, including the current day.</description>
+                       <label>Number of Daily Forecast Channels</label>
+                       <description>Number of days for daily forecast, including the current day and determining how many daily channels are
+                               created.</description>
                        <default>6</default>
                </parameter>
                <parameter name="forecastHours" type="integer" min="0" max="48" step="1">
-                       <label>Number of Hours</label>
-                       <description>Number of hours for hourly forecast.</description>
+                       <label>Number of Hourly Forecast Channels</label>
+                       <description>Number of hours for hourly forecast, determining how many hourly channels are created.</description>
                        <default>12</default>
                </parameter>
                <parameter name="forecastMinutes" type="integer" min="0" max="60" step="1">
-                       <label>Number of Minutes</label>
-                       <description>Number of minutes for minutely precipitation forecast.</description>
+                       <label>Number of Minutely Forecast Channels</label>
+                       <description>Number of minutes for minutely precipitation forecast, determining how many minutely channels are
+                               created.</description>
                        <default>0</default>
                </parameter>
                <parameter name="numberOfAlerts" type="integer" min="0" max="5" step="1">
index 8bfd0c26132f31cc05254b6a183dec7d26a29e64..8bc59e69b3a798dce16a010ec7546766b955001c 100644 (file)
@@ -74,10 +74,10 @@ thing-type.openweathermap.weather-api.description = Provides access to the OpenW
 
 # thing types config
 
-bridge-type.config.openweathermap.weather-api.apikey.label = API Key
-bridge-type.config.openweathermap.weather-api.apikey.description = API key to access the OpenWeatherMap API.
 bridge-type.config.openweathermap.weather-api.apiVersion.label = One Call API Version
 bridge-type.config.openweathermap.weather-api.apiVersion.description = One Call API version (defaults to 2.5, version 3.0 is available, but needs different subscription).
+bridge-type.config.openweathermap.weather-api.apikey.label = API Key
+bridge-type.config.openweathermap.weather-api.apikey.description = API key to access the OpenWeatherMap API.
 bridge-type.config.openweathermap.weather-api.language.label = Language
 bridge-type.config.openweathermap.weather-api.language.description = Language to be used by the OpenWeatherMap API.
 bridge-type.config.openweathermap.weather-api.language.option.af = Afrikaans
@@ -136,12 +136,12 @@ thing-type.config.openweathermap.onecall-history.historyDay.label = History Day
 thing-type.config.openweathermap.onecall-history.historyDay.description = Relative number of days in the past for historical data.
 thing-type.config.openweathermap.onecall-history.location.label = Location of Weather
 thing-type.config.openweathermap.onecall-history.location.description = Location of weather in geographical coordinates (latitude/longitude/altitude).
-thing-type.config.openweathermap.onecall.forecastDays.label = Number of Days
-thing-type.config.openweathermap.onecall.forecastDays.description = Number of days for daily forecast, including the current day.
-thing-type.config.openweathermap.onecall.forecastHours.label = Number of Hours
-thing-type.config.openweathermap.onecall.forecastHours.description = Number of hours for hourly forecast.
-thing-type.config.openweathermap.onecall.forecastMinutes.label = Number of Minutes
-thing-type.config.openweathermap.onecall.forecastMinutes.description = Number of minutes for minutely precipitation forecast.
+thing-type.config.openweathermap.onecall.forecastDays.label = Number of Daily Forecast Channels
+thing-type.config.openweathermap.onecall.forecastDays.description = Number of days for daily forecast, including the current day and determining how many daily channels are created.
+thing-type.config.openweathermap.onecall.forecastHours.label = Number of Hourly Forecast Channels
+thing-type.config.openweathermap.onecall.forecastHours.description = Number of hours for hourly forecast, determining how many hourly channels are created.
+thing-type.config.openweathermap.onecall.forecastMinutes.label = Number of Minutely Forecast Channels
+thing-type.config.openweathermap.onecall.forecastMinutes.description = Number of minutes for minutely precipitation forecast, determining how many minutely channels are created.
 thing-type.config.openweathermap.onecall.location.label = Location of Weather
 thing-type.config.openweathermap.onecall.location.description = Location of weather in geographical coordinates (latitude/longitude/altitude).
 thing-type.config.openweathermap.onecall.numberOfAlerts.label = Number of Alerts
@@ -173,14 +173,20 @@ channel-group-type.openweathermap.oneCallCurrent.label = One Call API Current We
 channel-group-type.openweathermap.oneCallCurrent.description = Current weather data from the One Call API.
 channel-group-type.openweathermap.oneCallDaily.label = One Call API Daily Forecast
 channel-group-type.openweathermap.oneCallDaily.description = Daily weather forecast delivered by the One Call API.
+channel-group-type.openweathermap.oneCallDailyTimeSeries.label = One Call API Daily Forecast
+channel-group-type.openweathermap.oneCallDailyTimeSeries.description = Daily weather forecast delivered by the One Call API.
 channel-group-type.openweathermap.oneCallHistory.label = One Call API Historical Weather
 channel-group-type.openweathermap.oneCallHistory.description = Historical weather data from the One Call API at this point in time the given day.
 channel-group-type.openweathermap.oneCallHistoryHours.label = One Call API Hourly Historical Weather Data
 channel-group-type.openweathermap.oneCallHistoryHours.description = Historical weather data from the One Call API per hour.
 channel-group-type.openweathermap.oneCallHourly.label = One Call API Hourly Forecast
 channel-group-type.openweathermap.oneCallHourly.description = Hourly weather forecast delivered by the One Call API.
+channel-group-type.openweathermap.oneCallHourlyTimeSeries.label = One Call API Hourly Forecast
+channel-group-type.openweathermap.oneCallHourlyTimeSeries.description = Hourly weather forecast delivered by the One Call API.
 channel-group-type.openweathermap.oneCallMinutely.label = One Call API Minutely Forecast
 channel-group-type.openweathermap.oneCallMinutely.description = Minutely precipitation delivered by the One Call API.
+channel-group-type.openweathermap.oneCallMinutelyTimeSeries.label = One Call API Minutely Forecast
+channel-group-type.openweathermap.oneCallMinutelyTimeSeries.description = Minutely precipitation delivered by the One Call API.
 channel-group-type.openweathermap.station.label = Weather Station
 channel-group-type.openweathermap.station.description = This is a weather station.
 channel-group-type.openweathermap.station.channel.location.description = Location of the weather station or the city.
index fc11c45e22dbc21abc46f5ce94ce11ba1b5991c6..8b353c1524550a83e3212e85bcee542d6cd56cf3 100644 (file)
                </channels>
        </channel-group-type>
 
+       <channel-group-type id="oneCallMinutelyTimeSeries">
+               <label>One Call API Minutely Forecast</label>
+               <description>Minutely precipitation delivered by the One Call API.</description>
+               <channels>
+                       <channel id="precipitation" typeId="precipitation"/>
+               </channels>
+       </channel-group-type>
+
        <channel-group-type id="oneCallMinutely">
                <label>One Call API Minutely Forecast</label>
                <description>Minutely precipitation delivered by the One Call API.</description>
                </channels>
        </channel-group-type>
 
+       <channel-group-type id="oneCallHourlyTimeSeries">
+               <label>One Call API Hourly Forecast</label>
+               <description>Hourly weather forecast delivered by the One Call API.</description>
+               <channels>
+                       <channel id="condition" typeId="condition"/>
+                       <channel id="condition-id" typeId="condition-id"/>
+                       <channel id="icon" typeId="condition-icon"/>
+                       <channel id="icon-id" typeId="condition-icon-id"/>
+                       <channel id="temperature" typeId="system.outdoor-temperature"/>
+                       <channel id="apparent-temperature" typeId="apparent-temperature"/>
+                       <channel id="pressure" typeId="system.barometric-pressure"/>
+                       <channel id="humidity" typeId="system.atmospheric-humidity"/>
+                       <channel id="dew-point" typeId="dew-point"/>
+                       <channel id="wind-speed" typeId="system.wind-speed"/>
+                       <channel id="wind-direction" typeId="system.wind-direction"/>
+                       <channel id="gust-speed" typeId="gust-speed"/>
+                       <channel id="cloudiness" typeId="cloudiness"/>
+                       <channel id="precip-probability" typeId="precip-probability"/>
+                       <channel id="rain" typeId="rain"/>
+                       <channel id="snow" typeId="snow"/>
+                       <channel id="visibility" typeId="visibility"/>
+               </channels>
+       </channel-group-type>
+
        <channel-group-type id="oneCallHourly">
                <label>One Call API Hourly Forecast</label>
                <description>Hourly weather forecast delivered by the One Call API.</description>
                </channels>
        </channel-group-type>
 
+       <channel-group-type id="oneCallDailyTimeSeries">
+               <label>One Call API Daily Forecast</label>
+               <description>Daily weather forecast delivered by the One Call API.</description>
+               <channels>
+                       <channel id="sunrise" typeId="sunrise"/>
+                       <channel id="sunset" typeId="sunset"/>
+                       <channel id="condition" typeId="condition"/>
+                       <channel id="condition-id" typeId="condition-id"/>
+                       <channel id="icon" typeId="condition-icon"/>
+                       <channel id="icon-id" typeId="condition-icon-id"/>
+                       <channel id="morning-temperature" typeId="morning-temperature"/>
+                       <channel id="day-temperature" typeId="day-temperature"/>
+                       <channel id="evening-temperature" typeId="evening-temperature"/>
+                       <channel id="night-temperature" typeId="night-temperature"/>
+                       <channel id="min-temperature" typeId="forecasted-min-outdoor-temperature"/>
+                       <channel id="max-temperature" typeId="forecasted-max-outdoor-temperature"/>
+                       <channel id="apparent-morning" typeId="apparent-morning"/>
+                       <channel id="apparent-day" typeId="apparent-day"/>
+                       <channel id="apparent-evening" typeId="apparent-evening"/>
+                       <channel id="apparent-night" typeId="apparent-night"/>
+                       <channel id="pressure" typeId="system.barometric-pressure"/>
+                       <channel id="humidity" typeId="system.atmospheric-humidity"/>
+                       <channel id="dew-point" typeId="dew-point"/>
+                       <channel id="wind-speed" typeId="system.wind-speed"/>
+                       <channel id="wind-direction" typeId="system.wind-direction"/>
+                       <channel id="gust-speed" typeId="gust-speed"/>
+                       <channel id="cloudiness" typeId="cloudiness"/>
+                       <channel id="uvindex" typeId="forecasted-uvindex"/>
+                       <channel id="precip-probability" typeId="precip-probability"/>
+                       <channel id="rain" typeId="rain"/>
+                       <channel id="snow" typeId="snow"/>
+               </channels>
+       </channel-group-type>
+
        <channel-group-type id="oneCallDaily">
                <label>One Call API Daily Forecast</label>
                <description>Daily weather forecast delivered by the One Call API.</description>
index 2fab59a36fc61a9c3054c5e18cf6db5b451faba6..bc9a2aad61b89f06770bda5e0f272ccff47bd965 100644 (file)
 
                <channel-groups>
                        <channel-group id="current" typeId="oneCallCurrent"/>
+
+                       <channel-group id="forecastMinutely" typeId="oneCallMinutelyTimeSeries"/>
+
                        <channel-group id="forecastMinutes01" typeId="oneCallMinutely"/>
                        <channel-group id="forecastMinutes02" typeId="oneCallMinutely"/>
                        <channel-group id="forecastMinutes03" typeId="oneCallMinutely"/>
                        <channel-group id="forecastMinutes59" typeId="oneCallMinutely"/>
                        <channel-group id="forecastMinutes60" typeId="oneCallMinutely"/>
 
+                       <channel-group id="forecastHourly" typeId="oneCallHourlyTimeSeries"/>
+
                        <channel-group id="forecastHours01" typeId="oneCallHourly"/>
                        <channel-group id="forecastHours02" typeId="oneCallHourly"/>
                        <channel-group id="forecastHours03" typeId="oneCallHourly"/>
                        <channel-group id="forecastHours23" typeId="oneCallHourly"/>
                        <channel-group id="forecastHours24" typeId="oneCallHourly"/>
 
+                       <channel-group id="forecastDaily" typeId="oneCallDailyTimeSeries"/>
+
                        <channel-group id="forecastToday" typeId="oneCallDaily">
                                <label>One Call API Todays Forecast</label>
                                <description>This is the weather forecast for today from the one call API.</description>
                        </channel-group>
                </channel-groups>
 
+               <properties>
+                       <property name="thingTypeVersion">1</property>
+               </properties>
                <representation-property>location</representation-property>
 
                <config-description-ref uri="thing-type:openweathermap:onecall"/>
diff --git a/bundles/org.openhab.binding.openweathermap/src/main/resources/OH-INF/update/instructions.xml b/bundles/org.openhab.binding.openweathermap/src/main/resources/OH-INF/update/instructions.xml
new file mode 100644 (file)
index 0000000..238adb6
--- /dev/null
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<update:update-descriptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:update="https://openhab.org/schemas/update-description/v1.0.0"
+       xsi:schemaLocation="https://openhab.org/schemas/update-description/v1.0.0 https://openhab.org/schemas/update-description-1.0.0.xsd">
+
+       <thing-type uid="openweathermap:onecall">
+               <instruction-set targetVersion="1">
+                       <add-channel id="precipitation" groupIds="forecastMinutely">
+                               <type>openweathermap:precipitation</type>
+                       </add-channel>
+
+                       <add-channel id="condition" groupIds="forecastHourly">
+                               <type>openweathermap:condition</type>
+                       </add-channel>
+                       <add-channel id="condition-id" groupIds="forecastHourly">
+                               <type>openweathermap:condition-id</type>
+                       </add-channel>
+                       <add-channel id="icon" groupIds="forecastHourly">
+                               <type>openweathermap:condition-icon</type>
+                       </add-channel>
+                       <add-channel id="icon-id" groupIds="forecastHourly">
+                               <type>openweathermap:condition-icon-id</type>
+                       </add-channel>
+                       <add-channel id="temperature" groupIds="forecastHourly">
+                               <type>system:outdoor-temperature</type>
+                       </add-channel>
+                       <add-channel id="apperent-temperature" groupIds="forecastHourly">
+                               <type>openweathermap:apparent-temperature</type>
+                       </add-channel>
+                       <add-channel id="pressure" groupIds="forecastHourly">
+                               <type>system:barometric-pressure</type>
+                       </add-channel>
+                       <add-channel id="humidity" groupIds="forecastHourly">
+                               <type>system:atmospheric-humidity</type>
+                       </add-channel>
+                       <add-channel id="dew-point" groupIds="forecastHourly">
+                               <type>openweathermap:dew-point</type>
+                       </add-channel>
+                       <add-channel id="wind-speed" groupIds="forecastHourly">
+                               <type>system:wind-speed</type>
+                       </add-channel>
+                       <add-channel id="wind-direction" groupIds="forecastHourly">
+                               <type>system:wind-direction</type>
+                       </add-channel>
+                       <add-channel id="gust-speed" groupIds="forecastHourly">
+                               <type>openweathermap:gust-speed</type>
+                       </add-channel>
+                       <add-channel id="cloudiness" groupIds="forecastHourly">
+                               <type>openweathermap:cloudiness</type>
+                       </add-channel>
+                       <add-channel id="precip-probability" groupIds="forecastHourly">
+                               <type>openweathermap:precip-probability</type>
+                       </add-channel>
+                       <add-channel id="rain" groupIds="forecastHourly">
+                               <type>openweathermap:rain</type>
+                       </add-channel>
+                       <add-channel id="snow" groupIds="forecastHourly">
+                               <type>openweathermap:snow</type>
+                       </add-channel>
+                       <add-channel id="visbility" groupIds="forecastHourly">
+                               <type>openweathermap:visibility</type>
+                       </add-channel>
+
+                       <add-channel id="sunrise" groupIds="forecastDaily">
+                               <type>openweathermap:sunrise</type>
+                       </add-channel>
+                       <add-channel id="sunset" groupIds="forecastDaily">
+                               <type>openweathermap:sunset</type>
+                       </add-channel>
+                       <add-channel id="condition" groupIds="forecastDaily">
+                               <type>openweathermap:condition</type>
+                       </add-channel>
+                       <add-channel id="condition-id" groupIds="forecastDaily">
+                               <type>openweathermap:condition-id</type>
+                       </add-channel>
+                       <add-channel id="icon" groupIds="forecastDaily">
+                               <type>openweathermap:condition-icon</type>
+                       </add-channel>
+                       <add-channel id="icon-id" groupIds="forecastDaily">
+                               <type>openweathermap:condition-icon-id</type>
+                       </add-channel>
+                       <add-channel id="morning-temperature" groupIds="forecastDaily">
+                               <type>openweathermap:morning-temperature</type>
+                       </add-channel>
+                       <add-channel id="day-temperature" groupIds="forecastDaily">
+                               <type>openweathermap:day-temperature</type>
+                       </add-channel>
+                       <add-channel id="evening-temperature" groupIds="forecastDaily">
+                               <type>openweathermap:evening-temperature</type>
+                       </add-channel>
+                       <add-channel id="night-temperature" groupIds="forecastDaily">
+                               <type>openweathermap:night-temperature</type>
+                       </add-channel>
+                       <add-channel id="min-temperature" groupIds="forecastDaily">
+                               <type>openweathermap:forecasted-min-outdoor-temperature</type>
+                       </add-channel>
+                       <add-channel id="max-temperature" groupIds="forecastDaily">
+                               <type>openweathermap:forecasted-max-outdoor-temperature</type>
+                       </add-channel>
+                       <add-channel id="apparent-morning" groupIds="forecastDaily">
+                               <type>openweathermap:apparent-morning</type>
+                       </add-channel>
+                       <add-channel id="apparent-day" groupIds="forecastDaily">
+                               <type>openweathermap:apparent-day</type>
+                       </add-channel>
+                       <add-channel id="apparent-evening" groupIds="forecastDaily">
+                               <type>openweathermap:apparent-evening</type>
+                       </add-channel>
+                       <add-channel id="apparent-night" groupIds="forecastDaily">
+                               <type>openweathermap:apparent-night</type>
+                       </add-channel>
+                       <add-channel id="pressure" groupIds="forecastDaily">
+                               <type>system:barometric-pressure</type>
+                       </add-channel>
+                       <add-channel id="humidity" groupIds="forecastDaily">
+                               <type>system:atmospheric-humidity</type>
+                       </add-channel>
+                       <add-channel id="dew-point" groupIds="forecastDaily">
+                               <type>openweathermap:dew-point</type>
+                       </add-channel>
+                       <add-channel id="wind-speed" groupIds="forecastDaily">
+                               <type>system:wind-speed</type>
+                       </add-channel>
+                       <add-channel id="wind-direction" groupIds="forecastDaily">
+                               <type>system:wind-direction</type>
+                       </add-channel>
+                       <add-channel id="gust-speed" groupIds="forecastDaily">
+                               <type>openweathermap:gust-speed</type>
+                       </add-channel>
+                       <add-channel id="cloudiness" groupIds="forecastDaily">
+                               <type>openweathermap:cloudiness</type>
+                       </add-channel>
+                       <add-channel id="uvindex" groupIds="forecastDaily">
+                               <type>openweathermap:forecasted-uvindex</type>
+                       </add-channel>
+                       <add-channel id="precip-probability" groupIds="forecastDaily">
+                               <type>openweathermap:precip-probability</type>
+                       </add-channel>
+                       <add-channel id="rain" groupIds="forecastDaily">
+                               <type>openweathermap:rain</type>
+                       </add-channel>
+                       <add-channel id="snow" groupIds="forecastDaily">
+                               <type>openweathermap:snow</type>
+                       </add-channel>
+               </instruction-set>
+       </thing-type>
+
+</update:update-descriptions>