diff --git a/bundles/org.openhab.binding.fmiweather/README.md b/bundles/org.openhab.binding.fmiweather/README.md
index 0b604981a4b..be0724a8784 100644
--- a/bundles/org.openhab.binding.fmiweather/README.md
+++ b/bundles/org.openhab.binding.fmiweather/README.md
@@ -28,13 +28,13 @@ The binding automatically discovers weather stations and forecasts for nearby pl
## Thing Configuration
-### `observation` thing configuration
+### `observation` Thing Configuration
| Parameter | Type | Required | Description | Example |
| --------- | ---- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| `fmisid` | text | ✓ | FMI Station ID. You can FMISID of see all weathers stations at [FMI web site](https://en.ilmatieteenlaitos.fi/observation-stations?p_p_id=stationlistingportlet_WAR_fmiwwwweatherportlets&p_p_lifecycle=0&p_p_state=normal&p_p_mode=view&p_p_col_id=column-4&p_p_col_count=1&_stationlistingportlet_WAR_fmiwwwweatherportlets_stationGroup=WEATHER#station-listing) | `"852678"` for Espoo Nuuksio station |
-### `forecast` thing configuration
+### `forecast` Thing Configuration
| Parameter | Type | Required | Description | Example |
| ---------- | ---- | -------- | ---------------------------------------------------------------------------------------------------- | --------------------------------- |
@@ -44,7 +44,7 @@ The binding automatically discovers weather stations and forecasts for nearby pl
Observation and forecast things provide slightly different details on weather.
-### `observation` thing channels
+### `observation` Thing Channels
Observation channels are grouped in single group, `current`.
@@ -67,11 +67,12 @@ You can check the exact observation time by using the `time` channel.
To refer to certain channel, use the normal convention `THING_ID:GROUP_ID#CHANNEL_ID`, e.g. `fmiweather:observation:station_874863_Espoo_Tapiola:current#temperature`.
-### `forecast` thing channels
+### `forecast` Thing Channels
Forecast has multiple channel groups, one for each forecasted time. The groups are named as follows:
-- `forecastNow`: Forecasted weather for the current time
+- `forecast`: Forecasted weather (with time series support)
+- `forecastNow`: Forecasted weather for the current time (deprecated, please use `forecast` instead)
- `forecastHours01`: Forecasted weather for 1 hours from now
- `forecastHours02`: Forecasted weather for 2 hours from now
- etc.
diff --git a/bundles/org.openhab.binding.fmiweather/src/main/java/org/openhab/binding/fmiweather/internal/AbstractWeatherHandler.java b/bundles/org.openhab.binding.fmiweather/src/main/java/org/openhab/binding/fmiweather/internal/AbstractWeatherHandler.java
index eb20e2cfc37..0aaf824e399 100644
--- a/bundles/org.openhab.binding.fmiweather/src/main/java/org/openhab/binding/fmiweather/internal/AbstractWeatherHandler.java
+++ b/bundles/org.openhab.binding.fmiweather/src/main/java/org/openhab/binding/fmiweather/internal/AbstractWeatherHandler.java
@@ -44,6 +44,7 @@ import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
+import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -210,13 +211,24 @@ public abstract class AbstractWeatherHandler extends BaseThingHandler {
*/
protected void updateStateIfLinked(ChannelUID channelUID, @Nullable BigDecimal value, @Nullable Unit> unit) {
if (isLinked(channelUID)) {
- if (value == null) {
- updateState(channelUID, UnDefType.UNDEF);
- } else if (unit == null) {
- updateState(channelUID, new DecimalType(value));
- } else {
- updateState(channelUID, new QuantityType<>(value, unit));
- }
+ updateState(channelUID, getState(value, unit));
+ }
+ }
+
+ /**
+ * Return QuantityType or DecimalType channel state
+ *
+ * @param value value to update
+ * @param unit unit associated with the value
+ * @return UNDEF state when value is null, otherwise QuantityType or DecimalType
+ */
+ protected State getState(@Nullable BigDecimal value, @Nullable Unit> unit) {
+ if (value == null) {
+ return UnDefType.UNDEF;
+ } else if (unit == null) {
+ return new DecimalType(value);
+ } else {
+ return new QuantityType<>(value, unit);
}
}
diff --git a/bundles/org.openhab.binding.fmiweather/src/main/java/org/openhab/binding/fmiweather/internal/BindingConstants.java b/bundles/org.openhab.binding.fmiweather/src/main/java/org/openhab/binding/fmiweather/internal/BindingConstants.java
index 4791d09a701..624089e7bb7 100644
--- a/bundles/org.openhab.binding.fmiweather/src/main/java/org/openhab/binding/fmiweather/internal/BindingConstants.java
+++ b/bundles/org.openhab.binding.fmiweather/src/main/java/org/openhab/binding/fmiweather/internal/BindingConstants.java
@@ -35,6 +35,9 @@ public class BindingConstants {
public static final ThingTypeUID THING_TYPE_FORECAST = new ThingTypeUID(BINDING_ID, "forecast");
public static final ThingUID UID_LOCAL_FORECAST = new ThingUID(BINDING_ID, "forecast", "local");
+ // List of all static Channel Group IDs
+ public static final String CHANNEL_GROUP_FORECAST = "forecast";
+
// List of all Channel ids
public static final String CHANNEL_TIME = "time";
public static final String CHANNEL_TEMPERATURE = "temperature";
diff --git a/bundles/org.openhab.binding.fmiweather/src/main/java/org/openhab/binding/fmiweather/internal/ForecastWeatherHandler.java b/bundles/org.openhab.binding.fmiweather/src/main/java/org/openhab/binding/fmiweather/internal/ForecastWeatherHandler.java
index 3aee7ede982..03e78ef778b 100644
--- a/bundles/org.openhab.binding.fmiweather/src/main/java/org/openhab/binding/fmiweather/internal/ForecastWeatherHandler.java
+++ b/bundles/org.openhab.binding.fmiweather/src/main/java/org/openhab/binding/fmiweather/internal/ForecastWeatherHandler.java
@@ -16,6 +16,7 @@ import static org.openhab.binding.fmiweather.internal.BindingConstants.*;
import static org.openhab.binding.fmiweather.internal.client.ForecastRequest.*;
import static org.openhab.core.library.unit.SIUnits.CELSIUS;
import static org.openhab.core.library.unit.Units.*;
+import static org.openhab.core.types.TimeSeries.Policy.REPLACE;
import java.math.BigDecimal;
import java.time.Instant;
@@ -41,6 +42,7 @@ 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.types.TimeSeries;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -140,29 +142,8 @@ public class ForecastWeatherHandler extends AbstractWeatherHandler {
properties.put(PROP_LATITUDE, location.latitude.toPlainString());
properties.put(PROP_LONGITUDE, location.longitude.toPlainString());
updateProperties(properties);
- for (Channel channel : getThing().getChannels()) {
- ChannelUID channelUID = channel.getUID();
- int hours = getHours(channelUID);
- int timeIndex = getTimeIndex(hours);
- if (channelUID.getIdWithoutGroup().equals(CHANNEL_TIME)) {
- // All parameters and locations should share the same timestamps. We use temperature to figure out
- // timestamp for the group of channels
- String field = ForecastRequest.PARAM_TEMPERATURE;
- Data data = unwrap(response.getData(location, field),
- "Field %s not present for location %s in response. Bug?", field, location);
- updateEpochSecondStateIfLinked(channelUID, data.timestampsEpochSecs[timeIndex]);
- } else {
- String field = getDataField(channelUID);
- Unit> unit = getUnit(channelUID);
- if (field == null) {
- logger.error("Channel {} not handled. Bug?", channelUID.getId());
- continue;
- }
- Data data = unwrap(response.getData(location, field),
- "Field %s not present for location %s in response. Bug?", field, location);
- updateStateIfLinked(channelUID, data.values[timeIndex], unit);
- }
- }
+ updateHourlyChannels(response, location);
+ updateTimeSeriesChannels(response, location);
updateStatus(ThingStatus.ONLINE);
} catch (FMIUnexpectedResponseException e) {
// Unexpected (possibly bug) issue with response
@@ -172,6 +153,71 @@ public class ForecastWeatherHandler extends AbstractWeatherHandler {
}
}
+ private void updateHourlyChannels(FMIResponse response, Location location) throws FMIUnexpectedResponseException {
+ for (Channel channel : getThing().getChannels()) {
+ ChannelUID channelUID = channel.getUID();
+ if (CHANNEL_GROUP_FORECAST.equals(channelUID.getGroupId())) {
+ // Skip time series group
+ continue;
+ }
+ int hours = getHours(channelUID);
+ int timeIndex = getTimeIndex(hours);
+ if (channelUID.getIdWithoutGroup().equals(CHANNEL_TIME)) {
+ // All parameters and locations should share the same timestamps. We use temperature to figure out
+ // timestamp for the group of channels
+ final String field = ForecastRequest.PARAM_TEMPERATURE;
+ Data data = unwrap(response.getData(location, field),
+ "Field %s not present for location %s in response. Bug?", field, location);
+ updateEpochSecondStateIfLinked(channelUID, data.timestampsEpochSecs[timeIndex]);
+ } else {
+ String field = getDataField(channelUID);
+ Unit> unit = getUnit(channelUID);
+ if (field == null) {
+ logger.error("Channel {} not handled. Bug?", channelUID.getId());
+ continue;
+ }
+ Data data = unwrap(response.getData(location, field),
+ "Field %s not present for location %s in response. Bug?", field, location);
+ updateStateIfLinked(channelUID, data.values[timeIndex], unit);
+ }
+ }
+ }
+
+ private void updateTimeSeriesChannels(FMIResponse response, Location location)
+ throws FMIUnexpectedResponseException {
+ for (Channel channel : getThing().getChannelsOfGroup(CHANNEL_GROUP_FORECAST)) {
+ ChannelUID channelUID = channel.getUID();
+ if (CHANNEL_TIME.equals(channelUID.getIdWithoutGroup())) {
+ // All parameters and locations should share the same timestamps. We use temperature to figure out
+ // timestamp for the group of channels
+ final String field = ForecastRequest.PARAM_TEMPERATURE;
+ Data data = unwrap(response.getData(location, field),
+ "Field %s not present for location %s in response. Bug?", field, location);
+ updateEpochSecondStateIfLinked(channelUID, data.timestampsEpochSecs[0]);
+ continue;
+ }
+ String field = getDataField(channelUID);
+ Unit> unit = getUnit(channelUID);
+ if (field == null) {
+ logger.error("Channel {} not handled. Bug?", channelUID.getId());
+ continue;
+ }
+ Data data = unwrap(response.getData(location, field),
+ "Field %s not present for location %s in response. Bug?", field, location);
+ if (data.values.length != data.timestampsEpochSecs.length) {
+ logger.warn("Number of values ({}) doesn't match number of timestamps ({})", data.values.length,
+ data.timestampsEpochSecs.length);
+ continue;
+ }
+ updateStateIfLinked(channelUID, data.values[0], unit);
+ TimeSeries timeSeries = new TimeSeries(REPLACE);
+ for (int i = 0; i < data.values.length; i++) {
+ timeSeries.add(Instant.ofEpochSecond(data.timestampsEpochSecs[i]), getState(data.values[i], unit));
+ }
+ sendTimeSeries(channelUID, timeSeries);
+ }
+ }
+
private static int getHours(ChannelUID uid) {
String groupId = uid.getGroupId();
if (groupId == null) {
diff --git a/bundles/org.openhab.binding.fmiweather/src/main/resources/OH-INF/i18n/fmiweather.properties b/bundles/org.openhab.binding.fmiweather/src/main/resources/OH-INF/i18n/fmiweather.properties
index bcc28a52cf5..f231e99d1a8 100644
--- a/bundles/org.openhab.binding.fmiweather/src/main/resources/OH-INF/i18n/fmiweather.properties
+++ b/bundles/org.openhab.binding.fmiweather/src/main/resources/OH-INF/i18n/fmiweather.properties
@@ -7,6 +7,8 @@ addon.fmiweather.description = This is the binding for Finnish Meteorological In
thing-type.fmiweather.forecast.label = FMI Weather Forecast
thing-type.fmiweather.forecast.description = Finnish Meteorological Institute (FMI) weather forecast
+thing-type.fmiweather.forecast.group.forecast.label = Forecast
+thing-type.fmiweather.forecast.group.forecast.description = This is the weather forecast
thing-type.fmiweather.forecast.group.forecastHours01.label = 1 Hours Forecast
thing-type.fmiweather.forecast.group.forecastHours01.description = This is the weather forecast in 1 hours.
thing-type.fmiweather.forecast.group.forecastHours02.label = 2 Hours Forecast
diff --git a/bundles/org.openhab.binding.fmiweather/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.fmiweather/src/main/resources/OH-INF/thing/thing-types.xml
index 5f7c64966f9..7ce3e14f82b 100644
--- a/bundles/org.openhab.binding.fmiweather/src/main/resources/OH-INF/thing/thing-types.xml
+++ b/bundles/org.openhab.binding.fmiweather/src/main/resources/OH-INF/thing/thing-types.xml
@@ -28,6 +28,10 @@
Finnish Meteorological Institute (FMI) weather forecast
+
+
+ This is the weather forecast
+ This is the weather forecast for the current time
@@ -234,6 +238,10 @@
+
+ 1
+
+
diff --git a/bundles/org.openhab.binding.fmiweather/src/main/resources/OH-INF/update/instructions.xml b/bundles/org.openhab.binding.fmiweather/src/main/resources/OH-INF/update/instructions.xml
new file mode 100644
index 00000000000..6c0e1a58f11
--- /dev/null
+++ b/bundles/org.openhab.binding.fmiweather/src/main/resources/OH-INF/update/instructions.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+ fmiweather:forecast-time-channel
+
+
+ fmiweather:temperature-channel
+
+
+ fmiweather:humidity-channel
+
+
+ fmiweather:wind-direction-channel
+
+
+ fmiweather:wind-speed-channel
+
+
+ fmiweather:wind-gust-channel
+
+
+ fmiweather:pressure-channel
+
+
+ fmiweather:precipitation-intensity-channel
+
+
+ fmiweather:total-cloud-cover-channel
+
+
+ fmiweather:weather-id-channel
+
+
+
+
+
+