Skip to content

Commit

Permalink
feat: add the 15-minutely forecast
Browse files Browse the repository at this point in the history
  • Loading branch information
obones committed Mar 24, 2024
1 parent 8ca97c9 commit 6cfa68e
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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<ThingTypeUID> SUPPORTED_THINGS_BRIDGE = new HashSet<>(Arrays.asList(THING_TYPE_BRIDGE));
Expand All @@ -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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,6 @@ enum ForecastValue {
}

WeatherApiResponse getForecast(PointType location, EnumSet<ForecastValue> forecastValues,
@Nullable Integer hourlyHours, @Nullable Integer dailyDays, boolean current);
@Nullable Integer hourlyHours, @Nullable Integer dailyDays, boolean current,
@Nullable Integer minutely15Steps);
}
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,38 @@ private void addCurrentFields(ForecastValue forecastValue, ArrayList<String> fie
}
}

private void addMinutely15Fields(ForecastValue forecastValue, ArrayList<String> 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<ForecastValue> 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");
Expand All @@ -227,10 +257,12 @@ public WeatherApiResponse getForecast(PointType location, EnumSet<ForecastValue>
ArrayList<String> requiredHourlyFields = new ArrayList<>();
ArrayList<String> requiredDailyFields = new ArrayList<>();
ArrayList<String> requiredCurrentFields = new ArrayList<>();
ArrayList<String> requiredMinutely15Fields = new ArrayList<>();
for (ForecastValue forecastValue : forecastValues) {
addHourlyFields(forecastValue, requiredHourlyFields);
addDailyFields(forecastValue, requiredDailyFields);
addCurrentFields(forecastValue, requiredCurrentFields);
addMinutely15Fields(forecastValue, requiredMinutely15Fields);
}

if (hourlyHours != null) {
Expand All @@ -247,6 +279,11 @@ public WeatherApiResponse getForecast(PointType location, EnumSet<ForecastValue>
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@ private List<ChannelGroupDefinition> 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;
}
}
19 changes: 19 additions & 0 deletions src/main/resources/OH-INF/config/config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,25 @@
<default>false</default>
</parameter>

<!-- 15-minutely parameters -->
<parameter-group name="minutely15Parameters">
<label>@text/config.openmeteo.thing.forecast.minutely15Parameters.label</label>
<description>@text/config.openmeteo.thing.forecast.minutely15Parameters.description</description>
</parameter-group>

<parameter name="minutely15Steps" type="integer" groupName="minutely15Parameters" min="1" max="288" step="1">
<label>@text/config.openmeteo.thing.forecast.minutely15Steps.label</label>
<description>@text/config.openmeteo.thing.forecast.minutely15Steps.description</description>
<advanced>true</advanced>
<default>48</default>
</parameter>
<parameter name="minutely15" type="boolean" groupName="minutely15Parameters">
<label>@text/config.openmeteo.thing.forecast.minutely15.label</label>
<description>@text/config.openmeteo.thing.forecast.minutely15.description</description>
<advanced>true</advanced>
<default>false</default>
</parameter>

<!-- Channel parameters -->
<parameter-group name="channelParameters">
<label>@text/config.openmeteo.thing.forecast.channelParameters.label</label>
Expand Down
9 changes: 9 additions & 0 deletions src/main/resources/OH-INF/i18n/openmeteo.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
5 changes: 5 additions & 0 deletions src/main/resources/OH-INF/thing/channels.xml
Original file line number Diff line number Diff line change
Expand Up @@ -353,4 +353,9 @@
<label>@text/channel-group-type.openmeteo.forecast.current.label</label>
<description>@text/channel-group-type.openmeteo.forecast.current.description</description>
</channel-group-type>

<channel-group-type id="minutely15">
<label>@text/channel-group-type.openmeteo.forecast.minutely15.label</label>
<description>@text/channel-group-type.openmeteo.forecast.minutely15.description</description>
</channel-group-type>
</thing:thing-descriptions>

0 comments on commit 6cfa68e

Please sign in to comment.