From 6cfa68ebd7b8cb3b69d4aa158f4b02eb81e77c2b Mon Sep 17 00:00:00 2001 From: OBones Date: Sun, 24 Mar 2024 17:17:44 +0100 Subject: [PATCH] feat: add the 15-minutely forecast --- .../internal/OpenMeteoBindingConstants.java | 4 + .../OpenMeteoForecastThingConfiguration.java | 3 + .../connection/OpenMeteoConnection.java | 3 +- .../connection/OpenMeteoHttpConnection.java | 39 +++++++- .../OpenMeteoForecastThingHandler.java | 98 ++++++++++++++++++- .../OpenMeteoForecastThingTypeProvider.java | 3 + src/main/resources/OH-INF/config/config.xml | 19 ++++ .../OH-INF/i18n/openmeteo.properties | 9 ++ src/main/resources/OH-INF/thing/channels.xml | 5 + 9 files changed, 180 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/obones/binding/openmeteo/internal/OpenMeteoBindingConstants.java b/src/main/java/com/obones/binding/openmeteo/internal/OpenMeteoBindingConstants.java index 1ea4780..4524708 100644 --- a/src/main/java/com/obones/binding/openmeteo/internal/OpenMeteoBindingConstants.java +++ b/src/main/java/com/obones/binding/openmeteo/internal/OpenMeteoBindingConstants.java @@ -49,6 +49,7 @@ public class OpenMeteoBindingConstants { private static final String CHANNEL_GROUP_TYPE_ID_DAILY_TIME_SERIES = "dailyTimeSeries"; private static final String CHANNEL_GROUP_TYPE_ID_DAILY = "daily"; private static final String CHANNEL_GROUP_TYPE_ID_CURRENT = "current"; + private static final String CHANNEL_GROUP_TYPE_ID_MINUTELY_15 = "minutely15"; // Discovered things id public static final String SYSTEM_LOCATION_THING_ID = "system"; @@ -71,6 +72,8 @@ public class OpenMeteoBindingConstants { CHANNEL_GROUP_TYPE_ID_DAILY); public static final ChannelGroupTypeUID CHANNEL_GROUP_TYPE_CURRENT = new ChannelGroupTypeUID(BINDING_ID, CHANNEL_GROUP_TYPE_ID_CURRENT); + public static final ChannelGroupTypeUID CHANNEL_GROUP_TYPE_MINUTELY_15 = new ChannelGroupTypeUID(BINDING_ID, + CHANNEL_GROUP_TYPE_ID_MINUTELY_15); // Definitions of different set of Things public static final Set SUPPORTED_THINGS_BRIDGE = new HashSet<>(Arrays.asList(THING_TYPE_BRIDGE)); @@ -93,6 +96,7 @@ public class OpenMeteoBindingConstants { public static final String CHANNEL_GROUP_DAILY_TOMORROW = "forecastTomorrow"; public static final String CHANNEL_GROUP_DAILY_PREFIX = "forecastDay"; public static final String CHANNEL_GROUP_CURRENT = "current"; + public static final String CHANNEL_GROUP_MINUTELY_15 = "minutely15"; // List of all forecast channel/property ids public static final String CHANNEL_FORECAST_TEMPERATURE = "temperature"; diff --git a/src/main/java/com/obones/binding/openmeteo/internal/config/OpenMeteoForecastThingConfiguration.java b/src/main/java/com/obones/binding/openmeteo/internal/config/OpenMeteoForecastThingConfiguration.java index a31d685..8311109 100644 --- a/src/main/java/com/obones/binding/openmeteo/internal/config/OpenMeteoForecastThingConfiguration.java +++ b/src/main/java/com/obones/binding/openmeteo/internal/config/OpenMeteoForecastThingConfiguration.java @@ -32,6 +32,9 @@ public class OpenMeteoForecastThingConfiguration { public boolean current = false; + public boolean minutely15 = false; + public int minutely15Steps = 48; + public boolean includeTemperature = true; public boolean includeHumidity = true; public boolean includeDewPoint = true; diff --git a/src/main/java/com/obones/binding/openmeteo/internal/connection/OpenMeteoConnection.java b/src/main/java/com/obones/binding/openmeteo/internal/connection/OpenMeteoConnection.java index 6521e11..c0aa582 100644 --- a/src/main/java/com/obones/binding/openmeteo/internal/connection/OpenMeteoConnection.java +++ b/src/main/java/com/obones/binding/openmeteo/internal/connection/OpenMeteoConnection.java @@ -63,5 +63,6 @@ enum ForecastValue { } WeatherApiResponse getForecast(PointType location, EnumSet forecastValues, - @Nullable Integer hourlyHours, @Nullable Integer dailyDays, boolean current); + @Nullable Integer hourlyHours, @Nullable Integer dailyDays, boolean current, + @Nullable Integer minutely15Steps); } diff --git a/src/main/java/com/obones/binding/openmeteo/internal/connection/OpenMeteoHttpConnection.java b/src/main/java/com/obones/binding/openmeteo/internal/connection/OpenMeteoHttpConnection.java index df0f06b..3e3801c 100644 --- a/src/main/java/com/obones/binding/openmeteo/internal/connection/OpenMeteoHttpConnection.java +++ b/src/main/java/com/obones/binding/openmeteo/internal/connection/OpenMeteoHttpConnection.java @@ -201,8 +201,38 @@ private void addCurrentFields(ForecastValue forecastValue, ArrayList fie } } + private void addMinutely15Fields(ForecastValue forecastValue, ArrayList fields) { + switch (forecastValue) { + case TEMPERATURE: + case HUMIDITY: + case DEW_POINT: + case APPARENT_TEMPERATURE: + case SHORTWAVE_RADIATION: + case DIRECT_RADIATION: + case DIRECT_NORMAL_IRRADIANCE: + case DIFFUSE_RADIATION: + case SUNSHINE_DURATION: + case PRECIPITATION: + case SNOW: + case RAIN: + case SHOWERS: + case SNOW_DEPTH: + case FREEZING_LEVEL_HEIGHT: + case CAPE: + case WIND_SPEED: + case WING_DIRECTION: + case GUST_SPEED: + case VISIBILITY: + case WEATHER_CODE: + fields.add(getForecastValueFieldName(forecastValue)); + default: // any other field is not supported in 15 minutely forecast + break; + } + } + public WeatherApiResponse getForecast(PointType location, EnumSet forecastValues, - @Nullable Integer hourlyHours, @Nullable Integer dailyDays, boolean current) { + @Nullable Integer hourlyHours, @Nullable Integer dailyDays, boolean current, + @Nullable Integer minutely15Steps) { if (hourlyHours == null && dailyDays == null) { logger.warn("No point in getting a forecast if no elements are required"); @@ -227,10 +257,12 @@ public WeatherApiResponse getForecast(PointType location, EnumSet ArrayList requiredHourlyFields = new ArrayList<>(); ArrayList requiredDailyFields = new ArrayList<>(); ArrayList requiredCurrentFields = new ArrayList<>(); + ArrayList requiredMinutely15Fields = new ArrayList<>(); for (ForecastValue forecastValue : forecastValues) { addHourlyFields(forecastValue, requiredHourlyFields); addDailyFields(forecastValue, requiredDailyFields); addCurrentFields(forecastValue, requiredCurrentFields); + addMinutely15Fields(forecastValue, requiredMinutely15Fields); } if (hourlyHours != null) { @@ -247,6 +279,11 @@ public WeatherApiResponse getForecast(PointType location, EnumSet builder.queryParam("current", String.join(",", requiredCurrentFields)); } + if (minutely15Steps != null) { + builder.queryParam("forecast_minutely_15", minutely15Steps); + builder.queryParam("minutely_15", String.join(",", requiredMinutely15Fields)); + } + String url = builder.build().toString(); logger.debug("Calling OpenMeteo on {}", url); diff --git a/src/main/java/com/obones/binding/openmeteo/internal/handler/OpenMeteoForecastThingHandler.java b/src/main/java/com/obones/binding/openmeteo/internal/handler/OpenMeteoForecastThingHandler.java index d6950dc..09598e9 100644 --- a/src/main/java/com/obones/binding/openmeteo/internal/handler/OpenMeteoForecastThingHandler.java +++ b/src/main/java/com/obones/binding/openmeteo/internal/handler/OpenMeteoForecastThingHandler.java @@ -235,6 +235,10 @@ protected void initializeChannels(OpenMeteoBridgeHandler bridgeHandler) { initializeCurrentGroupOptionalChannels(callback, builder, thingUID, config); } + if (config.minutely15) { + initializeMinutely15GroupOptionalChannels(callback, builder, thingUID, config); + } + updateThing(builder.build()); } @@ -528,6 +532,88 @@ protected ThingBuilder initializeCurrentGroupOptionalChannels(ThingHandlerCallba return builder; } + private void initializeMinutely15GroupOptionalChannels(ThingHandlerCallback callback, ThingBuilder builder, + ThingUID thingUID, OpenMeteoForecastThingConfiguration config) { + Object[] labelArguments = { localization.getText("channel-type.openmeteo.forecast.label-suffix.minutely15") }; + String channelGroupId = CHANNEL_GROUP_MINUTELY_15; + + initializeOptionalChannel(callback, builder, thingUID, channelGroupId, CHANNEL_FORECAST_TEMPERATURE, + SYSTEM_CHANNEL_TYPE_UID_OUTDOOR_TEMPERATURE, config.includeTemperature, // + "channel-type.openmeteo.forecast.temperature.label", + "channel-type.openmeteo.forecast.temperature.description", // + labelArguments, null); + + initializeOptionalChannel(callback, builder, thingUID, channelGroupId, CHANNEL_FORECAST_HUMIDITY, + SYSTEM_CHANNEL_TYPE_UID_ATMOSPHERIC_HUMIDITY, config.includeHumidity, // + "channel-type.openmeteo.forecast.humidity.label", + "channel-type.openmeteo.forecast.humidity.description", // + labelArguments, null); + + initializeOptionalChannel(callback, builder, thingUID, channelGroupId, CHANNEL_FORECAST_DEW_POINT, + CHANNEL_TYPE_UID_DEW_POINT, config.includeDewPoint, labelArguments); + + initializeOptionalChannel(callback, builder, thingUID, channelGroupId, CHANNEL_FORECAST_APPARENT_TEMPERATURE, + CHANNEL_TYPE_UID_APPARENT_TEMPERATURE, config.includeApparentTemperature, labelArguments); + + initializeOptionalChannel(callback, builder, thingUID, channelGroupId, CHANNEL_FORECAST_SHORTWAVE_RADIATION, + CHANNEL_TYPE_UID_SHORTWAVE_RADIATION, config.includeShortwaveRadiation, labelArguments); + + initializeOptionalChannel(callback, builder, thingUID, channelGroupId, CHANNEL_FORECAST_DIRECT_RADIATION, + CHANNEL_TYPE_UID_DIRECT_RADIATION, config.includeDirectRadiation, labelArguments); + + initializeOptionalChannel(callback, builder, thingUID, channelGroupId, + CHANNEL_FORECAST_DIRECT_NORMAL_IRRADIANCE, CHANNEL_TYPE_UID_DIRECT_NORMAL_IRRADIANCE, + config.includeDirectNormalIrradiance, labelArguments); + + initializeOptionalChannel(callback, builder, thingUID, channelGroupId, CHANNEL_FORECAST_DIFFUSE_RADIATION, + CHANNEL_TYPE_UID_DIFFUSE_RADIATION, config.includeDiffuseRadiation, labelArguments); + + initializeOptionalChannel(callback, builder, thingUID, channelGroupId, CHANNEL_FORECAST_SUNSHINE_DURATION, + CHANNEL_TYPE_UID_SUNSHINE_DURATION, config.includeSunshineDuration, labelArguments); + + initializeOptionalChannel(callback, builder, thingUID, channelGroupId, CHANNEL_FORECAST_PRECIPITATION, + CHANNEL_TYPE_UID_PRECIPITATION, config.includePrecipitation, labelArguments); + + initializeOptionalChannel(callback, builder, thingUID, channelGroupId, CHANNEL_FORECAST_SNOW, + CHANNEL_TYPE_UID_SNOW, config.includeSnow, labelArguments); + + initializeOptionalChannel(callback, builder, thingUID, channelGroupId, CHANNEL_FORECAST_RAIN, + CHANNEL_TYPE_UID_RAIN, config.includeRain, labelArguments); + + initializeOptionalChannel(callback, builder, thingUID, channelGroupId, CHANNEL_FORECAST_SHOWERS, + CHANNEL_TYPE_UID_SHOWERS, config.includeShowers, labelArguments); + + initializeOptionalChannel(callback, builder, thingUID, channelGroupId, CHANNEL_FORECAST_SNOW_DEPTH, + CHANNEL_TYPE_UID_SNOW_DEPTH, config.includeSnowDepth, labelArguments); + + initializeOptionalChannel(callback, builder, thingUID, channelGroupId, CHANNEL_FORECAST_FREEZING_LEVEL_HEIGHT, + CHANNEL_TYPE_UID_FREEZING_LEVEL_HEIGHT, config.includeFreezingLevelHeight, labelArguments); + + initializeOptionalChannel(callback, builder, thingUID, channelGroupId, CHANNEL_FORECAST_CAPE, + CHANNEL_TYPE_UID_CAPE, config.includeCape, labelArguments); + + initializeOptionalChannel(callback, builder, thingUID, channelGroupId, CHANNEL_FORECAST_WIND_SPEED, + SYSTEM_CHANNEL_TYPE_UID_WIND_SPEED, config.includeWindSpeed, // + "channel-type.openmeteo.forecast.wind-speed.label", + "channel-type.openmeteo.forecast.wind-speed.description", // + labelArguments, null); + + initializeOptionalChannel(callback, builder, thingUID, channelGroupId, CHANNEL_FORECAST_WIND_DIRECTION, + SYSTEM_CHANNEL_TYPE_UID_WIND_DIRECTION, config.includeWindDirection, // + "channel-type.openmeteo.forecast.wind-direction.label", + "channel-type.openmeteo.forecast.wind-direction.description", // + labelArguments, null); + + initializeOptionalChannel(callback, builder, thingUID, channelGroupId, CHANNEL_FORECAST_GUST_SPEED, + CHANNEL_TYPE_UID_GUST_SPEED, config.includeGustSpeed, labelArguments); + + initializeOptionalChannel(callback, builder, thingUID, channelGroupId, CHANNEL_FORECAST_VISIBILITY, + CHANNEL_TYPE_UID_VISIBILITY, config.includeVisibility, labelArguments); + + initializeOptionalChannel(callback, builder, thingUID, channelGroupId, CHANNEL_FORECAST_WEATHER_CODE, + CHANNEL_TYPE_UID_WEATHER_CODE, config.includeWeatherCode, labelArguments); + } + protected ThingBuilder initializeOptionalChannel(ThingHandlerCallback callback, ThingBuilder builder, ThingUID thingUID, String channelGroupId, String channelId, ChannelTypeUID channelTypeUID, boolean isActive, AutoUpdatePolicy autoUpdatePolicy, @Nullable String labelKey, @Nullable String descriptionKey, @@ -699,7 +785,8 @@ protected boolean requestData(OpenMeteoConnection connection) forecastData = connection.getForecast(location, getForecastValues(), (config.hourlyTimeSeries || config.hourlySplit) ? config.hourlyHours : null, // (config.dailyTimeSeries || config.dailySplit) ? config.dailyDays : null, // - config.current); + config.current, // + (config.minutely15) ? config.minutely15Steps : null); return true; } @@ -816,6 +903,8 @@ protected void updateChannel(ChannelUID channelUID) { case CHANNEL_GROUP_CURRENT: updateCurrentChannel(channelUID); break; + case CHANNEL_GROUP_MINUTELY_15: + updateMinutely15TImeSeries(channelUID); default: Matcher hourlyForecastMatcher = CHANNEL_GROUP_HOURLY_FORECAST_PREFIX_PATTERN.matcher(channelGroupId); if (hourlyForecastMatcher.find()) { @@ -874,6 +963,13 @@ private void updateDailyTimeSeries(ChannelUID channelUID) { } } + private void updateMinutely15TImeSeries(ChannelUID channelUID) { + var forecastData = this.forecastData; + if (forecastData != null) { + updateForecastTimeSeries(channelUID, forecastData.minutely15()); + } + } + private void updateForecastChannel(ChannelUID channelUID, @Nullable VariablesWithTime forecast, @Nullable Integer index) { StringBuilder channelId = new StringBuilder(channelUID.getIdWithoutGroup()); diff --git a/src/main/java/com/obones/binding/openmeteo/internal/provider/OpenMeteoForecastThingTypeProvider.java b/src/main/java/com/obones/binding/openmeteo/internal/provider/OpenMeteoForecastThingTypeProvider.java index 3429675..37b9479 100644 --- a/src/main/java/com/obones/binding/openmeteo/internal/provider/OpenMeteoForecastThingTypeProvider.java +++ b/src/main/java/com/obones/binding/openmeteo/internal/provider/OpenMeteoForecastThingTypeProvider.java @@ -155,6 +155,9 @@ private List getChannelGroupDefinitions() { // current situation result.add(new ChannelGroupDefinition(CHANNEL_GROUP_CURRENT, CHANNEL_GROUP_TYPE_CURRENT)); + // 15 minutely forecast + result.add(new ChannelGroupDefinition(CHANNEL_GROUP_MINUTELY_15, CHANNEL_GROUP_TYPE_MINUTELY_15)); + return result; } } diff --git a/src/main/resources/OH-INF/config/config.xml b/src/main/resources/OH-INF/config/config.xml index 309e616..37b9c66 100644 --- a/src/main/resources/OH-INF/config/config.xml +++ b/src/main/resources/OH-INF/config/config.xml @@ -100,6 +100,25 @@ false + + + + @text/config.openmeteo.thing.forecast.minutely15Parameters.description + + + + + @text/config.openmeteo.thing.forecast.minutely15Steps.description + true + 48 + + + + @text/config.openmeteo.thing.forecast.minutely15.description + true + false + + diff --git a/src/main/resources/OH-INF/i18n/openmeteo.properties b/src/main/resources/OH-INF/i18n/openmeteo.properties index 7066e8b..790cbeb 100644 --- a/src/main/resources/OH-INF/i18n/openmeteo.properties +++ b/src/main/resources/OH-INF/i18n/openmeteo.properties @@ -49,6 +49,12 @@ config.openmeteo.thing.forecast.currentParameters.label = Current weather parame config.openmeteo.thing.forecast.currentParameters.description = config.openmeteo.thing.forecast.current.label = Current weather config.openmeteo.thing.forecast.current.description = Current weather, based on 15-minutely weather model data. +config.openmeteo.thing.forecast.minutely15Parameters.label = 15-minutely forecast parameters +config.openmeteo.thing.forecast.minutely15Parameters.description = +config.openmeteo.thing.forecast.minutely15.label = 15-minutely forecast +config.openmeteo.thing.forecast.minutely15.description = Weather forecast in 15 minutes steps, for Central europe and North America. Interpolated from hourly in other regions. +config.openmeteo.thing.forecast.minutely15Steps.label = 15-minutely steps +config.openmeteo.thing.forecast.minutely15Steps.description = Number of 15 minutes step to retrieve the forecaast for. # config.openmeteo.thing.forecast.channelParameters.label = Channels config.openmeteo.thing.forecast.channelParameters.description = Select which channels should be available on this thing. @@ -197,6 +203,7 @@ channel-type.openmeteo.forecast.label-suffix.daily.today = Today channel-type.openmeteo.forecast.label-suffix.daily.tomorrow = Tomorrow channel-type.openmeteo.forecast.label-suffix.daily.split = Day %d channel-type.openmeteo.forecast.label-suffix.current = Current +channel-type.openmeteo.forecast.label-suffix.minutely15 = 15-Minutely # # Channel group types descriptions @@ -219,6 +226,8 @@ channel-group-type.openmeteo.forecast.daily.label.format = Day %d forecast channel-group-type.openmeteo.forecast.daily.description.format = Daily weather forecast for day %d channel-group-type.openmeteo.forecast.current.label = Current weather channel-group-type.openmeteo.forecast.current.description = Current weather conditions, based on 15-minutely weather model data. +channel-group-type.openmeteo.forecast.minutely15.label = 15-minutely forecast +channel-group-type.openmeteo.forecast.minutely15.description = Weather forecast in 15 minutes steps, for Central europe and North America. Interpolated from hourly in other regions. # # Runtime status descriptions diff --git a/src/main/resources/OH-INF/thing/channels.xml b/src/main/resources/OH-INF/thing/channels.xml index c4fdf8b..eda2932 100644 --- a/src/main/resources/OH-INF/thing/channels.xml +++ b/src/main/resources/OH-INF/thing/channels.xml @@ -353,4 +353,9 @@ @text/channel-group-type.openmeteo.forecast.current.description + + + + @text/channel-group-type.openmeteo.forecast.minutely15.description +